summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/ABI/testing/configfs-usb-gadget-uac18
-rw-r--r--Documentation/ABI/testing/configfs-usb-gadget-uac211
-rw-r--r--Documentation/devicetree/bindings/usb/fsl,ls1028a.yaml52
-rw-r--r--Documentation/devicetree/bindings/usb/msm-hsusb.txt110
-rw-r--r--Documentation/devicetree/bindings/usb/qcom,dwc3.yaml20
-rw-r--r--Documentation/devicetree/bindings/usb/ti,j721e-usb.yaml3
-rw-r--r--Documentation/usb/functionfs-desc.rst39
-rw-r--r--Documentation/usb/functionfs.rst2
-rw-r--r--Documentation/usb/gadget-testing.rst19
-rw-r--r--Documentation/usb/index.rst1
-rw-r--r--drivers/usb/chipidea/ci_hdrc_imx.c2
-rw-r--r--drivers/usb/common/common.c7
-rw-r--r--drivers/usb/dwc2/debugfs.c1
-rw-r--r--drivers/usb/dwc2/params.c2
-rw-r--r--drivers/usb/dwc3/dwc3-imx8mp.c22
-rw-r--r--drivers/usb/dwc3/dwc3-octeon.c19
-rw-r--r--drivers/usb/gadget/configfs.c10
-rw-r--r--drivers/usb/gadget/function/f_fs.c12
-rw-r--r--drivers/usb/gadget/function/f_hid.c273
-rw-r--r--drivers/usb/gadget/function/f_uac1.c63
-rw-r--r--drivers/usb/gadget/function/f_uac2.c80
-rw-r--r--drivers/usb/gadget/function/u_audio.c10
-rw-r--r--drivers/usb/gadget/function/u_serial.c22
-rw-r--r--drivers/usb/gadget/function/u_uac1.h12
-rw-r--r--drivers/usb/gadget/function/u_uac2.h15
-rw-r--r--drivers/usb/gadget/function/uvc_v4l2.c12
-rw-r--r--drivers/usb/gadget/udc/bdc/bdc_core.c1
-rw-r--r--drivers/usb/gadget/udc/lpc32xx_udc.c67
-rw-r--r--drivers/usb/host/Kconfig2
-rw-r--r--drivers/usb/host/ehci-brcm.c1
-rw-r--r--drivers/usb/host/ehci-exynos.c9
-rw-r--r--drivers/usb/host/ohci-exynos.c9
-rw-r--r--drivers/usb/host/ohci-ppc-of.c4
-rw-r--r--drivers/usb/host/xhci-pci-renesas.c48
-rw-r--r--drivers/usb/host/xhci-pci.c57
-rw-r--r--drivers/usb/host/xhci-pci.h19
-rw-r--r--drivers/usb/host/xhci.h2
-rw-r--r--drivers/usb/misc/brcmstb-usb-pinmap.c1
-rw-r--r--drivers/usb/misc/qcom_eud.c2
-rw-r--r--drivers/usb/musb/mpfs.c160
-rw-r--r--drivers/usb/phy/phy-gpio-vbus-usb.c1
-rw-r--r--drivers/usb/phy/phy-mxs-usb.c132
-rw-r--r--drivers/usb/roles/class.c9
-rw-r--r--drivers/usb/typec/anx7411.c6
-rw-r--r--drivers/usb/typec/tcpm/maxim_contaminant.c53
-rw-r--r--drivers/usb/typec/tcpm/tcpci.c104
-rw-r--r--drivers/usb/typec/tcpm/tcpci_maxim.h34
-rw-r--r--drivers/usb/typec/tcpm/tcpci_maxim_core.c82
-rw-r--r--drivers/usb/typec/tcpm/tcpci_rt1711h.c27
-rw-r--r--drivers/usb/typec/tipd/core.c3
-rw-r--r--drivers/usb/typec/ucsi/ucsi.c142
-rw-r--r--drivers/usb/typec/ucsi/ucsi.h46
-rw-r--r--drivers/usb/typec/ucsi/ucsi_glink.c2
-rw-r--r--drivers/usb/usbip/vhci_hcd.c36
-rw-r--r--drivers/usb/usbip/vhci_sysfs.c3
-rw-r--r--include/linux/usb/composite.h2
-rw-r--r--include/linux/usb/gadget_configfs.h7
-rw-r--r--include/linux/usb/tcpci.h31
-rw-r--r--include/uapi/linux/usb/ch9.h8
-rw-r--r--include/uapi/linux/usb/functionfs.h97
-rw-r--r--include/uapi/linux/usb/g_hid.h40
-rw-r--r--include/uapi/linux/usb/gadgetfs.h2
62 files changed, 1388 insertions, 688 deletions
diff --git a/Documentation/ABI/testing/configfs-usb-gadget-uac1 b/Documentation/ABI/testing/configfs-usb-gadget-uac1
index c4ba92f004c3..64188a85592b 100644
--- a/Documentation/ABI/testing/configfs-usb-gadget-uac1
+++ b/Documentation/ABI/testing/configfs-usb-gadget-uac1
@@ -30,4 +30,12 @@ Description:
req_number the number of pre-allocated requests
for both capture and playback
function_name name of the interface
+ p_it_name playback input terminal name
+ p_it_ch_name playback channels name
+ p_ot_name playback output terminal name
+ p_fu_vol_name playback mute/volume functional unit name
+ c_it_name capture input terminal name
+ c_it_ch_name capture channels name
+ c_ot_name capture output terminal name
+ c_fu_vol_name capture mute/volume functional unit name
===================== =======================================
diff --git a/Documentation/ABI/testing/configfs-usb-gadget-uac2 b/Documentation/ABI/testing/configfs-usb-gadget-uac2
index a2bf4fd82a5b..133e995c3e92 100644
--- a/Documentation/ABI/testing/configfs-usb-gadget-uac2
+++ b/Documentation/ABI/testing/configfs-usb-gadget-uac2
@@ -35,6 +35,17 @@ Description:
req_number the number of pre-allocated requests
for both capture and playback
function_name name of the interface
+ if_ctrl_name topology control name
+ clksrc_in_name input clock name
+ clksrc_out_name output clock name
+ p_it_name playback input terminal name
+ p_it_ch_name playback input first channel name
+ p_ot_name playback output terminal name
+ p_fu_vol_name playback mute/volume function unit name
+ c_it_name capture input terminal name
+ c_it_ch_name capture input first channel name
+ c_ot_name capture output terminal name
+ c_fu_vol_name capture mute/volume functional unit name
c_terminal_type code of the capture terminal type
p_terminal_type code of the playback terminal type
===================== =======================================
diff --git a/Documentation/devicetree/bindings/usb/fsl,ls1028a.yaml b/Documentation/devicetree/bindings/usb/fsl,ls1028a.yaml
new file mode 100644
index 000000000000..a44bdf391887
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/fsl,ls1028a.yaml
@@ -0,0 +1,52 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/usb/fsl,ls1028a.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Freescale layerscape SuperSpeed DWC3 USB SoC controller
+
+maintainers:
+ - Frank Li <Frank.Li@nxp.com>
+
+select:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - fsl,ls1028a-dwc3
+ required:
+ - compatible
+
+properties:
+ compatible:
+ items:
+ - enum:
+ - fsl,ls1028a-dwc3
+ - const: snps,dwc3
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+unevaluatedProperties: false
+
+required:
+ - compatible
+ - reg
+ - interrupts
+
+allOf:
+ - $ref: snps,dwc3.yaml#
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+ usb@fe800000 {
+ compatible = "fsl,ls1028a-dwc3", "snps,dwc3";
+ reg = <0xfe800000 0x100000>;
+ interrupts = <GIC_SPI 105 IRQ_TYPE_LEVEL_HIGH>;
+ };
diff --git a/Documentation/devicetree/bindings/usb/msm-hsusb.txt b/Documentation/devicetree/bindings/usb/msm-hsusb.txt
deleted file mode 100644
index afc30e98b123..000000000000
--- a/Documentation/devicetree/bindings/usb/msm-hsusb.txt
+++ /dev/null
@@ -1,110 +0,0 @@
-MSM SoC HSUSB controllers
-
-EHCI
-
-Required properties:
-- compatible: Should contain "qcom,ehci-host"
-- regs: offset and length of the register set in the memory map
-- usb-phy: phandle for the PHY device
-
-Example EHCI controller device node:
-
- ehci: ehci@f9a55000 {
- compatible = "qcom,ehci-host";
- reg = <0xf9a55000 0x400>;
- usb-phy = <&usb_otg>;
- };
-
-USB PHY with optional OTG:
-
-Required properties:
-- compatible: Should contain:
- "qcom,usb-otg-ci" for chipsets with ChipIdea 45nm PHY
- "qcom,usb-otg-snps" for chipsets with Synopsys 28nm PHY
-
-- regs: Offset and length of the register set in the memory map
-- interrupts: interrupt-specifier for the OTG interrupt.
-
-- clocks: A list of phandle + clock-specifier pairs for the
- clocks listed in clock-names
-- clock-names: Should contain the following:
- "phy" USB PHY reference clock
- "core" Protocol engine clock
- "iface" Interface bus clock
- "alt_core" Protocol engine clock for targets with asynchronous
- reset methodology. (optional)
-
-- vdccx-supply: phandle to the regulator for the vdd supply for
- digital circuit operation.
-- v1p8-supply: phandle to the regulator for the 1.8V supply
-- v3p3-supply: phandle to the regulator for the 3.3V supply
-
-- resets: A list of phandle + reset-specifier pairs for the
- resets listed in reset-names
-- reset-names: Should contain the following:
- "phy" USB PHY controller reset
- "link" USB LINK controller reset
-
-- qcom,otg-control: OTG control (VBUS and ID notifications) can be one of
- 1 - PHY control
- 2 - PMIC control
-
-Optional properties:
-- dr_mode: One of "host", "peripheral" or "otg". Defaults to "otg"
-
-- switch-gpio: A phandle + gpio-specifier pair. Some boards are using Dual
- SPDT USB Switch, witch is controlled by GPIO to de/multiplex
- D+/D- USB lines between connectors.
-
-- qcom,phy-init-sequence: PHY configuration sequence values. This is related to Device
- Mode Eye Diagram test. Start address at which these values will be
- written is ULPI_EXT_VENDOR_SPECIFIC. Value of -1 is reserved as
- "do not overwrite default value at this address".
- For example: qcom,phy-init-sequence = < -1 0x63 >;
- Will update only value at address ULPI_EXT_VENDOR_SPECIFIC + 1.
-
-- qcom,phy-num: Select number of pyco-phy to use, can be one of
- 0 - PHY one, default
- 1 - Second PHY
- Some platforms may have configuration to allow USB
- controller work with any of the two HSPHYs present.
-
-- qcom,vdd-levels: This property must be a list of three integer values
- (no, min, max) where each value represents either a voltage
- in microvolts or a value corresponding to voltage corner.
-
-- qcom,manual-pullup: If present, vbus is not routed to USB controller/phy
- and controller driver therefore enables pull-up explicitly
- before starting controller using usbcmd run/stop bit.
-
-- extcon: phandles to external connector devices. First phandle
- should point to external connector, which provide "USB"
- cable events, the second should point to external connector
- device, which provide "USB-HOST" cable events. If one of
- the external connector devices is not required empty <0>
- phandle should be specified.
-
-Example HSUSB OTG controller device node:
-
- usb@f9a55000 {
- compatible = "qcom,usb-otg-snps";
- reg = <0xf9a55000 0x400>;
- interrupts = <0 134 0>;
- dr_mode = "peripheral";
-
- clocks = <&gcc GCC_XO_CLK>, <&gcc GCC_USB_HS_SYSTEM_CLK>,
- <&gcc GCC_USB_HS_AHB_CLK>;
-
- clock-names = "phy", "core", "iface";
-
- vddcx-supply = <&pm8841_s2_corner>;
- v1p8-supply = <&pm8941_l6>;
- v3p3-supply = <&pm8941_l24>;
-
- resets = <&gcc GCC_USB2A_PHY_BCR>, <&gcc GCC_USB_HS_BCR>;
- reset-names = "phy", "link";
-
- qcom,otg-control = <1>;
- qcom,phy-init-sequence = < -1 0x63 >;
- qcom,vdd-levels = <1 5 7>;
- };
diff --git a/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml b/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml
index efde47a5b145..18758efb8d29 100644
--- a/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml
+++ b/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml
@@ -52,6 +52,7 @@ properties:
- qcom,sm8550-dwc3
- qcom,sm8650-dwc3
- qcom,x1e80100-dwc3
+ - qcom,x1e80100-dwc3-mp
- const: qcom,dwc3
reg:
@@ -164,6 +165,7 @@ allOf:
contains:
enum:
- qcom,ipq4019-dwc3
+ - qcom,ipq5332-dwc3
then:
properties:
clocks:
@@ -267,7 +269,6 @@ allOf:
contains:
enum:
- qcom,ipq5018-dwc3
- - qcom,ipq5332-dwc3
- qcom,msm8994-dwc3
- qcom,qcs404-dwc3
then:
@@ -289,6 +290,7 @@ allOf:
- qcom,sc8280xp-dwc3
- qcom,sc8280xp-dwc3-mp
- qcom,x1e80100-dwc3
+ - qcom,x1e80100-dwc3-mp
then:
properties:
clocks:
@@ -428,6 +430,21 @@ allOf:
contains:
enum:
- qcom,ipq5332-dwc3
+ then:
+ properties:
+ interrupts:
+ maxItems: 3
+ interrupt-names:
+ items:
+ - const: pwr_event
+ - const: dp_hs_phy_irq
+ - const: dm_hs_phy_irq
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
- qcom,x1e80100-dwc3
then:
properties:
@@ -486,6 +503,7 @@ allOf:
contains:
enum:
- qcom,sc8180x-dwc3-mp
+ - qcom,x1e80100-dwc3-mp
then:
properties:
interrupts:
diff --git a/Documentation/devicetree/bindings/usb/ti,j721e-usb.yaml b/Documentation/devicetree/bindings/usb/ti,j721e-usb.yaml
index 95ff9791baea..653a89586f4e 100644
--- a/Documentation/devicetree/bindings/usb/ti,j721e-usb.yaml
+++ b/Documentation/devicetree/bindings/usb/ti,j721e-usb.yaml
@@ -13,10 +13,9 @@ properties:
compatible:
oneOf:
- const: ti,j721e-usb
- - const: ti,am64-usb
- items:
- - const: ti,j721e-usb
- const: ti,am64-usb
+ - const: ti,j721e-usb
reg:
maxItems: 1
diff --git a/Documentation/usb/functionfs-desc.rst b/Documentation/usb/functionfs-desc.rst
new file mode 100644
index 000000000000..39649774da54
--- /dev/null
+++ b/Documentation/usb/functionfs-desc.rst
@@ -0,0 +1,39 @@
+======================
+FunctionFS Descriptors
+======================
+
+Some of the descriptors that can be written to the FFS gadget are
+described below. Device and configuration descriptors are handled
+by the composite gadget and are not written by the user to the
+FFS gadget.
+
+Descriptors are written to the "ep0" file in the FFS gadget
+following the descriptor header.
+
+.. kernel-doc:: include/uapi/linux/usb/functionfs.h
+ :doc: descriptors
+
+Interface Descriptors
+---------------------
+
+Standard USB interface descriptors may be written. The class/subclass of the
+most recent interface descriptor determines what type of class-specific
+descriptors are accepted.
+
+Class-Specific Descriptors
+--------------------------
+
+Class-specific descriptors are accepted only for the class/subclass of the
+most recent interface descriptor. The following are some of the
+class-specific descriptors that are supported.
+
+DFU Functional Descriptor
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+When the interface class is USB_CLASS_APP_SPEC and the interface subclass
+is USB_SUBCLASS_DFU, a DFU functional descriptor can be provided.
+The DFU functional descriptor is a described in the USB specification for
+Device Firmware Upgrade (DFU), version 1.1 as of this writing.
+
+.. kernel-doc:: include/uapi/linux/usb/functionfs.h
+ :doc: usb_dfu_functional_descriptor
diff --git a/Documentation/usb/functionfs.rst b/Documentation/usb/functionfs.rst
index d05a775bc45b..f7487b0d8057 100644
--- a/Documentation/usb/functionfs.rst
+++ b/Documentation/usb/functionfs.rst
@@ -25,6 +25,8 @@ interface numbers starting from zero). The FunctionFS changes
them as needed also handling situation when numbers differ in
different configurations.
+For more information about FunctionFS descriptors see :doc:`functionfs-desc`
+
When descriptors and strings are written "ep#" files appear
(one for each declared endpoint) which handle communication on
a single endpoint. Again, FunctionFS takes care of the real
diff --git a/Documentation/usb/gadget-testing.rst b/Documentation/usb/gadget-testing.rst
index b086c7ab72f0..bf555c2270f5 100644
--- a/Documentation/usb/gadget-testing.rst
+++ b/Documentation/usb/gadget-testing.rst
@@ -765,6 +765,17 @@ The uac2 function provides these attributes in its function directory:
req_number the number of pre-allocated request for both capture
and playback
function_name name of the interface
+ if_ctrl_name topology control name
+ clksrc_in_name input clock name
+ clksrc_out_name output clock name
+ p_it_name playback input terminal name
+ p_it_ch_name playback input first channel name
+ p_ot_name playback output terminal name
+ p_fu_vol_name playback function unit name
+ c_it_name capture input terminal name
+ c_it_ch_name capture input first channel name
+ c_ot_name capture output terminal name
+ c_fu_vol_name capture functional unit name
c_terminal_type code of the capture terminal type
p_terminal_type code of the playback terminal type
================ ====================================================
@@ -957,6 +968,14 @@ The uac1 function provides these attributes in its function directory:
req_number the number of pre-allocated requests for both capture
and playback
function_name name of the interface
+ p_it_name playback input terminal name
+ p_it_ch_name playback channels name
+ p_ot_name playback output terminal name
+ p_fu_vol_name playback mute/volume functional unit name
+ c_it_name capture input terminal name
+ c_it_ch_name capture channels name
+ c_ot_name capture output terminal name
+ c_fu_vol_name capture mute/volume functional unit name
================ ====================================================
The attributes have sane default values.
diff --git a/Documentation/usb/index.rst b/Documentation/usb/index.rst
index 27955dad95e1..826492c813ac 100644
--- a/Documentation/usb/index.rst
+++ b/Documentation/usb/index.rst
@@ -11,6 +11,7 @@ USB support
dwc3
ehci
functionfs
+ functionfs-desc
gadget_configfs
gadget_hid
gadget_multi
diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c
index bdc04ce919f7..c64ab0e07ea0 100644
--- a/drivers/usb/chipidea/ci_hdrc_imx.c
+++ b/drivers/usb/chipidea/ci_hdrc_imx.c
@@ -128,7 +128,7 @@ static struct imx_usbmisc_data *usbmisc_get_init_data(struct device *dev)
* In case the fsl,usbmisc property is not present this device doesn't
* need usbmisc. Return NULL (which is no error here)
*/
- if (!of_get_property(np, "fsl,usbmisc", NULL))
+ if (!of_property_present(np, "fsl,usbmisc"))
return NULL;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
diff --git a/drivers/usb/common/common.c b/drivers/usb/common/common.c
index 59b55d6cf490..84ec00b7966c 100644
--- a/drivers/usb/common/common.c
+++ b/drivers/usb/common/common.c
@@ -276,14 +276,13 @@ EXPORT_SYMBOL_GPL(usb_decode_interval);
*/
enum usb_dr_mode of_usb_get_dr_mode_by_phy(struct device_node *np, int arg0)
{
- struct device_node *controller = NULL;
+ struct device_node *controller;
struct of_phandle_args args;
const char *dr_mode;
int index;
int err;
- do {
- controller = of_find_node_with_property(controller, "phys");
+ for_each_node_with_property(controller, "phys") {
if (!of_device_is_available(controller))
continue;
index = 0;
@@ -306,7 +305,7 @@ enum usb_dr_mode of_usb_get_dr_mode_by_phy(struct device_node *np, int arg0)
goto finish;
index++;
} while (args.np);
- } while (controller);
+ }
finish:
err = of_property_read_string(controller, "dr_mode", &dr_mode);
diff --git a/drivers/usb/dwc2/debugfs.c b/drivers/usb/dwc2/debugfs.c
index 7c82ab590401..3116ac72747f 100644
--- a/drivers/usb/dwc2/debugfs.c
+++ b/drivers/usb/dwc2/debugfs.c
@@ -702,6 +702,7 @@ static int params_show(struct seq_file *seq, void *v)
print_param(seq, p, uframe_sched);
print_param(seq, p, external_id_pin_ctl);
print_param(seq, p, power_down);
+ print_param(seq, p, no_clock_gating);
print_param(seq, p, lpm);
print_param(seq, p, lpm_clock_gating);
print_param(seq, p, besl);
diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c
index a937eadbc9b3..68226defdc60 100644
--- a/drivers/usb/dwc2/params.c
+++ b/drivers/usb/dwc2/params.c
@@ -23,6 +23,7 @@ static void dwc2_set_bcm_params(struct dwc2_hsotg *hsotg)
p->max_transfer_size = 65535;
p->max_packet_count = 511;
p->ahbcfg = 0x10;
+ p->no_clock_gating = true;
}
static void dwc2_set_his_params(struct dwc2_hsotg *hsotg)
@@ -352,6 +353,7 @@ const struct of_device_id dwc2_of_match_table[] = {
MODULE_DEVICE_TABLE(of, dwc2_of_match_table);
const struct acpi_device_id dwc2_acpi_match[] = {
+ /* This ID refers to the same USB IP as of_device_id brcm,bcm2835-usb */
{ "BCM2848", (kernel_ulong_t)dwc2_set_bcm_params },
{ },
};
diff --git a/drivers/usb/dwc3/dwc3-imx8mp.c b/drivers/usb/dwc3/dwc3-imx8mp.c
index 8ee448068503..392fa1232788 100644
--- a/drivers/usb/dwc3/dwc3-imx8mp.c
+++ b/drivers/usb/dwc3/dwc3-imx8mp.c
@@ -282,8 +282,7 @@ static void dwc3_imx8mp_remove(struct platform_device *pdev)
pm_runtime_put_noidle(dev);
}
-static int __maybe_unused dwc3_imx8mp_suspend(struct dwc3_imx8mp *dwc3_imx,
- pm_message_t msg)
+static int dwc3_imx8mp_suspend(struct dwc3_imx8mp *dwc3_imx, pm_message_t msg)
{
if (dwc3_imx->pm_suspended)
return 0;
@@ -297,8 +296,7 @@ static int __maybe_unused dwc3_imx8mp_suspend(struct dwc3_imx8mp *dwc3_imx,
return 0;
}
-static int __maybe_unused dwc3_imx8mp_resume(struct dwc3_imx8mp *dwc3_imx,
- pm_message_t msg)
+static int dwc3_imx8mp_resume(struct dwc3_imx8mp *dwc3_imx, pm_message_t msg)
{
struct dwc3 *dwc = platform_get_drvdata(dwc3_imx->dwc3);
int ret = 0;
@@ -331,7 +329,7 @@ static int __maybe_unused dwc3_imx8mp_resume(struct dwc3_imx8mp *dwc3_imx,
return ret;
}
-static int __maybe_unused dwc3_imx8mp_pm_suspend(struct device *dev)
+static int dwc3_imx8mp_pm_suspend(struct device *dev)
{
struct dwc3_imx8mp *dwc3_imx = dev_get_drvdata(dev);
int ret;
@@ -349,7 +347,7 @@ static int __maybe_unused dwc3_imx8mp_pm_suspend(struct device *dev)
return ret;
}
-static int __maybe_unused dwc3_imx8mp_pm_resume(struct device *dev)
+static int dwc3_imx8mp_pm_resume(struct device *dev)
{
struct dwc3_imx8mp *dwc3_imx = dev_get_drvdata(dev);
int ret;
@@ -379,7 +377,7 @@ static int __maybe_unused dwc3_imx8mp_pm_resume(struct device *dev)
return ret;
}
-static int __maybe_unused dwc3_imx8mp_runtime_suspend(struct device *dev)
+static int dwc3_imx8mp_runtime_suspend(struct device *dev)
{
struct dwc3_imx8mp *dwc3_imx = dev_get_drvdata(dev);
@@ -388,7 +386,7 @@ static int __maybe_unused dwc3_imx8mp_runtime_suspend(struct device *dev)
return dwc3_imx8mp_suspend(dwc3_imx, PMSG_AUTO_SUSPEND);
}
-static int __maybe_unused dwc3_imx8mp_runtime_resume(struct device *dev)
+static int dwc3_imx8mp_runtime_resume(struct device *dev)
{
struct dwc3_imx8mp *dwc3_imx = dev_get_drvdata(dev);
@@ -398,9 +396,9 @@ static int __maybe_unused dwc3_imx8mp_runtime_resume(struct device *dev)
}
static const struct dev_pm_ops dwc3_imx8mp_dev_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(dwc3_imx8mp_pm_suspend, dwc3_imx8mp_pm_resume)
- SET_RUNTIME_PM_OPS(dwc3_imx8mp_runtime_suspend,
- dwc3_imx8mp_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(dwc3_imx8mp_pm_suspend, dwc3_imx8mp_pm_resume)
+ RUNTIME_PM_OPS(dwc3_imx8mp_runtime_suspend, dwc3_imx8mp_runtime_resume,
+ NULL)
};
static const struct of_device_id dwc3_imx8mp_of_match[] = {
@@ -414,7 +412,7 @@ static struct platform_driver dwc3_imx8mp_driver = {
.remove_new = dwc3_imx8mp_remove,
.driver = {
.name = "imx8mp-dwc3",
- .pm = &dwc3_imx8mp_dev_pm_ops,
+ .pm = pm_ptr(&dwc3_imx8mp_dev_pm_ops),
.of_match_table = dwc3_imx8mp_of_match,
},
};
diff --git a/drivers/usb/dwc3/dwc3-octeon.c b/drivers/usb/dwc3/dwc3-octeon.c
index 6010135e1acc..1a3b205367fd 100644
--- a/drivers/usb/dwc3/dwc3-octeon.c
+++ b/drivers/usb/dwc3/dwc3-octeon.c
@@ -419,7 +419,7 @@ static int dwc3_octeon_probe(struct platform_device *pdev)
int ref_clk_sel, ref_clk_fsel, mpll_mul;
int power_active_low, power_gpio;
int err, len;
- u32 clock_rate;
+ u32 clock_rate, gpio_pwr[3];
if (of_property_read_u32(node, "refclk-frequency", &clock_rate)) {
dev_err(dev, "No UCTL \"refclk-frequency\"\n");
@@ -476,21 +476,10 @@ static int dwc3_octeon_probe(struct platform_device *pdev)
power_gpio = DWC3_GPIO_POWER_NONE;
power_active_low = 0;
- if (of_find_property(node, "power", &len)) {
- u32 gpio_pwr[3];
-
- switch (len) {
- case 8:
- of_property_read_u32_array(node, "power", gpio_pwr, 2);
- break;
- case 12:
- of_property_read_u32_array(node, "power", gpio_pwr, 3);
+ len = of_property_read_variable_u32_array(node, "power", gpio_pwr, 2, 3);
+ if (len > 0) {
+ if (len == 3)
power_active_low = gpio_pwr[2] & 0x01;
- break;
- default:
- dev_err(dev, "invalid power configuration\n");
- return -EINVAL;
- }
power_gpio = gpio_pwr[1];
}
diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c
index 0e7c1e947c0a..5cba3e8d626c 100644
--- a/drivers/usb/gadget/configfs.c
+++ b/drivers/usb/gadget/configfs.c
@@ -12,7 +12,7 @@
#include "u_f.h"
#include "u_os_desc.h"
-int check_user_usb_string(const char *name,
+static int check_user_usb_string(const char *name,
struct usb_gadget_strings *stringtab_dev)
{
u16 num;
@@ -902,7 +902,7 @@ static struct configfs_group_operations gadget_language_langid_group_ops = {
.drop_item = gadget_language_string_drop,
};
-static struct config_item_type gadget_language_type = {
+static const struct config_item_type gadget_language_type = {
.ct_item_ops = &gadget_language_langid_item_ops,
.ct_group_ops = &gadget_language_langid_group_ops,
.ct_attrs = gadget_language_langid_attrs,
@@ -961,7 +961,7 @@ static struct configfs_group_operations gadget_language_group_ops = {
.drop_item = &gadget_language_drop,
};
-static struct config_item_type gadget_language_strings_type = {
+static const struct config_item_type gadget_language_strings_type = {
.ct_group_ops = &gadget_language_group_ops,
.ct_owner = THIS_MODULE,
};
@@ -1106,7 +1106,7 @@ static struct configfs_attribute *webusb_attrs[] = {
NULL,
};
-static struct config_item_type webusb_type = {
+static const struct config_item_type webusb_type = {
.ct_attrs = webusb_attrs,
.ct_owner = THIS_MODULE,
};
@@ -1263,7 +1263,7 @@ static struct configfs_item_operations os_desc_ops = {
.drop_link = os_desc_unlink,
};
-static struct config_item_type os_desc_type = {
+static const struct config_item_type os_desc_type = {
.ct_item_ops = &os_desc_ops,
.ct_attrs = os_desc_attrs,
.ct_owner = THIS_MODULE,
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index e0ceaa721949..e35177fc6c55 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -2478,7 +2478,7 @@ typedef int (*ffs_os_desc_callback)(enum ffs_os_desc_type entity,
static int __must_check ffs_do_single_desc(char *data, unsigned len,
ffs_entity_callback entity,
- void *priv, int *current_class)
+ void *priv, int *current_class, int *current_subclass)
{
struct usb_descriptor_header *_ds = (void *)data;
u8 length;
@@ -2535,6 +2535,7 @@ static int __must_check ffs_do_single_desc(char *data, unsigned len,
if (ds->iInterface)
__entity(STRING, ds->iInterface);
*current_class = ds->bInterfaceClass;
+ *current_subclass = ds->bInterfaceSubClass;
}
break;
@@ -2559,6 +2560,12 @@ static int __must_check ffs_do_single_desc(char *data, unsigned len,
if (length != sizeof(struct ccid_descriptor))
goto inv_length;
break;
+ } else if (*current_class == USB_CLASS_APP_SPEC &&
+ *current_subclass == USB_SUBCLASS_DFU) {
+ pr_vdebug("dfu functional descriptor\n");
+ if (length != sizeof(struct usb_dfu_functional_descriptor))
+ goto inv_length;
+ break;
} else {
pr_vdebug("unknown descriptor: %d for class %d\n",
_ds->bDescriptorType, *current_class);
@@ -2621,6 +2628,7 @@ static int __must_check ffs_do_descs(unsigned count, char *data, unsigned len,
const unsigned _len = len;
unsigned long num = 0;
int current_class = -1;
+ int current_subclass = -1;
for (;;) {
int ret;
@@ -2640,7 +2648,7 @@ static int __must_check ffs_do_descs(unsigned count, char *data, unsigned len,
return _len - len;
ret = ffs_do_single_desc(data, len, entity, priv,
- &current_class);
+ &current_class, &current_subclass);
if (ret < 0) {
pr_debug("%s returns %d\n", __func__, ret);
return ret;
diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c
index 93dae017ae45..82c7dff6de5c 100644
--- a/drivers/usb/gadget/function/f_hid.c
+++ b/drivers/usb/gadget/function/f_hid.c
@@ -15,13 +15,21 @@
#include <linux/uaccess.h>
#include <linux/wait.h>
#include <linux/sched.h>
+#include <linux/workqueue.h>
#include <linux/usb/g_hid.h>
+#include <uapi/linux/usb/g_hid.h>
#include "u_f.h"
#include "u_hid.h"
#define HIDG_MINORS 4
+/*
+ * Most operating systems seem to allow for 5000ms timeout, we will allow
+ * userspace half that time to respond before we return an empty report.
+ */
+#define GET_REPORT_TIMEOUT_MS 2500
+
static int major, minors;
static const struct class hidg_class = {
@@ -31,6 +39,11 @@ static const struct class hidg_class = {
static DEFINE_IDA(hidg_ida);
static DEFINE_MUTEX(hidg_ida_lock); /* protects access to hidg_ida */
+struct report_entry {
+ struct usb_hidg_report report_data;
+ struct list_head node;
+};
+
/*-------------------------------------------------------------------------*/
/* HID gadget struct */
@@ -75,6 +88,19 @@ struct f_hidg {
wait_queue_head_t write_queue;
struct usb_request *req;
+ /* get report */
+ struct usb_request *get_req;
+ struct usb_hidg_report get_report;
+ bool get_report_returned;
+ int get_report_req_report_id;
+ int get_report_req_report_length;
+ spinlock_t get_report_spinlock;
+ wait_queue_head_t get_queue; /* Waiting for userspace response */
+ wait_queue_head_t get_id_queue; /* Get ID came in */
+ struct work_struct work;
+ struct workqueue_struct *workqueue;
+ struct list_head report_list;
+
struct device dev;
struct cdev cdev;
struct usb_function func;
@@ -524,6 +550,174 @@ release_write_pending:
return status;
}
+static struct report_entry *f_hidg_search_for_report(struct f_hidg *hidg, u8 report_id)
+{
+ struct list_head *ptr;
+ struct report_entry *entry;
+
+ list_for_each(ptr, &hidg->report_list) {
+ entry = list_entry(ptr, struct report_entry, node);
+ if (entry->report_data.report_id == report_id)
+ return entry;
+ }
+
+ return NULL;
+}
+
+static void get_report_workqueue_handler(struct work_struct *work)
+{
+ struct f_hidg *hidg = container_of(work, struct f_hidg, work);
+ struct usb_composite_dev *cdev = hidg->func.config->cdev;
+ struct usb_request *req;
+ struct report_entry *ptr;
+ unsigned long flags;
+
+ int status = 0;
+
+ spin_lock_irqsave(&hidg->get_report_spinlock, flags);
+ req = hidg->get_req;
+ if (!req) {
+ spin_unlock_irqrestore(&hidg->get_report_spinlock, flags);
+ return;
+ }
+
+ req->zero = 0;
+ req->length = min_t(unsigned int, min_t(unsigned int, hidg->get_report_req_report_length,
+ hidg->report_length),
+ MAX_REPORT_LENGTH);
+
+ /* Check if there is a response available for immediate response */
+ ptr = f_hidg_search_for_report(hidg, hidg->get_report_req_report_id);
+ if (ptr && !ptr->report_data.userspace_req) {
+ /* Report exists in list and it is to be used for immediate response */
+ req->buf = ptr->report_data.data;
+ status = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+ hidg->get_report_returned = true;
+ spin_unlock_irqrestore(&hidg->get_report_spinlock, flags);
+ } else {
+ /*
+ * Report does not exist in list or should not be immediately sent
+ * i.e. give userspace time to respond
+ */
+ hidg->get_report_returned = false;
+ spin_unlock_irqrestore(&hidg->get_report_spinlock, flags);
+ wake_up(&hidg->get_id_queue);
+#define GET_REPORT_COND (!hidg->get_report_returned)
+ /* Wait until userspace has responded or timeout */
+ status = wait_event_interruptible_timeout(hidg->get_queue, !GET_REPORT_COND,
+ msecs_to_jiffies(GET_REPORT_TIMEOUT_MS));
+ spin_lock_irqsave(&hidg->get_report_spinlock, flags);
+ req = hidg->get_req;
+ if (!req) {
+ spin_unlock_irqrestore(&hidg->get_report_spinlock, flags);
+ return;
+ }
+ if (status == 0 && !hidg->get_report_returned) {
+ /* GET_REPORT request was not serviced by userspace within timeout period */
+ VDBG(cdev, "get_report : userspace timeout.\n");
+ hidg->get_report_returned = true;
+ }
+
+ /* Search again for report ID in list and respond to GET_REPORT request */
+ ptr = f_hidg_search_for_report(hidg, hidg->get_report_req_report_id);
+ if (ptr) {
+ /*
+ * Either get an updated response just serviced by userspace
+ * or send the latest response in the list
+ */
+ req->buf = ptr->report_data.data;
+ } else {
+ /* If there are no prevoiusly sent reports send empty report */
+ req->buf = hidg->get_report.data;
+ memset(req->buf, 0x0, req->length);
+ }
+
+ status = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+ spin_unlock_irqrestore(&hidg->get_report_spinlock, flags);
+ }
+
+ if (status < 0)
+ VDBG(cdev, "usb_ep_queue error on ep0 responding to GET_REPORT\n");
+}
+
+static int f_hidg_get_report_id(struct file *file, __u8 __user *buffer)
+{
+ struct f_hidg *hidg = file->private_data;
+ int ret = 0;
+
+ ret = put_user(hidg->get_report_req_report_id, buffer);
+
+ return ret;
+}
+
+static int f_hidg_get_report(struct file *file, struct usb_hidg_report __user *buffer)
+{
+ struct f_hidg *hidg = file->private_data;
+ struct usb_composite_dev *cdev = hidg->func.config->cdev;
+ unsigned long flags;
+ struct report_entry *entry;
+ struct report_entry *ptr;
+ __u8 report_id;
+
+ entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry)
+ return -ENOMEM;
+
+ if (copy_from_user(&entry->report_data, buffer,
+ sizeof(struct usb_hidg_report))) {
+ ERROR(cdev, "copy_from_user error\n");
+ kfree(entry);
+ return -EINVAL;
+ }
+
+ report_id = entry->report_data.report_id;
+
+ spin_lock_irqsave(&hidg->get_report_spinlock, flags);
+ ptr = f_hidg_search_for_report(hidg, report_id);
+
+ if (ptr) {
+ /* Report already exists in list - update it */
+ if (copy_from_user(&ptr->report_data, buffer,
+ sizeof(struct usb_hidg_report))) {
+ spin_unlock_irqrestore(&hidg->get_report_spinlock, flags);
+ ERROR(cdev, "copy_from_user error\n");
+ kfree(entry);
+ return -EINVAL;
+ }
+ kfree(entry);
+ } else {
+ /* Report does not exist in list - add it */
+ list_add_tail(&entry->node, &hidg->report_list);
+ }
+
+ /* If there is no response pending then do nothing further */
+ if (hidg->get_report_returned) {
+ spin_unlock_irqrestore(&hidg->get_report_spinlock, flags);
+ return 0;
+ }
+
+ /* If this userspace response serves the current pending report */
+ if (hidg->get_report_req_report_id == report_id) {
+ hidg->get_report_returned = true;
+ wake_up(&hidg->get_queue);
+ }
+
+ spin_unlock_irqrestore(&hidg->get_report_spinlock, flags);
+ return 0;
+}
+
+static long f_hidg_ioctl(struct file *file, unsigned int code, unsigned long arg)
+{
+ switch (code) {
+ case GADGET_HID_READ_GET_REPORT_ID:
+ return f_hidg_get_report_id(file, (__u8 __user *)arg);
+ case GADGET_HID_WRITE_GET_REPORT:
+ return f_hidg_get_report(file, (struct usb_hidg_report __user *)arg);
+ default:
+ return -ENOTTY;
+ }
+}
+
static __poll_t f_hidg_poll(struct file *file, poll_table *wait)
{
struct f_hidg *hidg = file->private_data;
@@ -531,6 +725,8 @@ static __poll_t f_hidg_poll(struct file *file, poll_table *wait)
poll_wait(file, &hidg->read_queue, wait);
poll_wait(file, &hidg->write_queue, wait);
+ poll_wait(file, &hidg->get_queue, wait);
+ poll_wait(file, &hidg->get_id_queue, wait);
if (WRITE_COND)
ret |= EPOLLOUT | EPOLLWRNORM;
@@ -543,12 +739,16 @@ static __poll_t f_hidg_poll(struct file *file, poll_table *wait)
ret |= EPOLLIN | EPOLLRDNORM;
}
+ if (GET_REPORT_COND)
+ ret |= EPOLLPRI;
+
return ret;
}
#undef WRITE_COND
#undef READ_COND_SSREPORT
#undef READ_COND_INTOUT
+#undef GET_REPORT_COND
static int f_hidg_release(struct inode *inode, struct file *fd)
{
@@ -641,6 +841,10 @@ static void hidg_ssreport_complete(struct usb_ep *ep, struct usb_request *req)
wake_up(&hidg->read_queue);
}
+static void hidg_get_report_complete(struct usb_ep *ep, struct usb_request *req)
+{
+}
+
static int hidg_setup(struct usb_function *f,
const struct usb_ctrlrequest *ctrl)
{
@@ -649,6 +853,7 @@ static int hidg_setup(struct usb_function *f,
struct usb_request *req = cdev->req;
int status = 0;
__u16 value, length;
+ unsigned long flags;
value = __le16_to_cpu(ctrl->wValue);
length = __le16_to_cpu(ctrl->wLength);
@@ -660,14 +865,20 @@ static int hidg_setup(struct usb_function *f,
switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
| HID_REQ_GET_REPORT):
- VDBG(cdev, "get_report\n");
+ VDBG(cdev, "get_report | wLength=%d\n", ctrl->wLength);
- /* send an empty report */
- length = min_t(unsigned, length, hidg->report_length);
- memset(req->buf, 0x0, length);
+ /*
+ * Update GET_REPORT ID so that an ioctl can be used to determine what
+ * GET_REPORT the request was actually for.
+ */
+ spin_lock_irqsave(&hidg->get_report_spinlock, flags);
+ hidg->get_report_req_report_id = value & 0xff;
+ hidg->get_report_req_report_length = length;
+ spin_unlock_irqrestore(&hidg->get_report_spinlock, flags);
- goto respond;
- break;
+ queue_work(hidg->workqueue, &hidg->work);
+
+ return status;
case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
| HID_REQ_GET_PROTOCOL):
@@ -793,6 +1004,14 @@ static void hidg_disable(struct usb_function *f)
spin_unlock_irqrestore(&hidg->read_spinlock, flags);
}
+ spin_lock_irqsave(&hidg->get_report_spinlock, flags);
+ if (!hidg->get_report_returned) {
+ usb_ep_free_request(f->config->cdev->gadget->ep0, hidg->get_req);
+ hidg->get_req = NULL;
+ hidg->get_report_returned = true;
+ }
+ spin_unlock_irqrestore(&hidg->get_report_spinlock, flags);
+
spin_lock_irqsave(&hidg->write_spinlock, flags);
if (!hidg->write_pending) {
free_ep_req(hidg->in_ep, hidg->req);
@@ -902,6 +1121,14 @@ fail:
return status;
}
+#ifdef CONFIG_COMPAT
+static long f_hidg_compat_ioctl(struct file *file, unsigned int code,
+ unsigned long value)
+{
+ return f_hidg_ioctl(file, code, value);
+}
+#endif
+
static const struct file_operations f_hidg_fops = {
.owner = THIS_MODULE,
.open = f_hidg_open,
@@ -909,6 +1136,10 @@ static const struct file_operations f_hidg_fops = {
.write = f_hidg_write,
.read = f_hidg_read,
.poll = f_hidg_poll,
+ .unlocked_ioctl = f_hidg_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = f_hidg_compat_ioctl,
+#endif
.llseek = noop_llseek,
};
@@ -919,6 +1150,15 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f)
struct usb_string *us;
int status;
+ hidg->get_req = usb_ep_alloc_request(c->cdev->gadget->ep0, GFP_ATOMIC);
+ if (!hidg->get_req)
+ return -ENOMEM;
+
+ hidg->get_req->zero = 0;
+ hidg->get_req->complete = hidg_get_report_complete;
+ hidg->get_req->context = hidg;
+ hidg->get_report_returned = true;
+
/* maybe allocate device-global string IDs, and patch descriptors */
us = usb_gstrings_attach(c->cdev, ct_func_strings,
ARRAY_SIZE(ct_func_string_defs));
@@ -1004,9 +1244,24 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f)
hidg->write_pending = 1;
hidg->req = NULL;
spin_lock_init(&hidg->read_spinlock);
+ spin_lock_init(&hidg->get_report_spinlock);
init_waitqueue_head(&hidg->write_queue);
init_waitqueue_head(&hidg->read_queue);
+ init_waitqueue_head(&hidg->get_queue);
+ init_waitqueue_head(&hidg->get_id_queue);
INIT_LIST_HEAD(&hidg->completed_out_req);
+ INIT_LIST_HEAD(&hidg->report_list);
+
+ INIT_WORK(&hidg->work, get_report_workqueue_handler);
+ hidg->workqueue = alloc_workqueue("report_work",
+ WQ_FREEZABLE |
+ WQ_MEM_RECLAIM,
+ 1);
+
+ if (!hidg->workqueue) {
+ status = -ENOMEM;
+ goto fail;
+ }
/* create char device */
cdev_init(&hidg->cdev, &f_hidg_fops);
@@ -1016,12 +1271,16 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f)
return 0;
fail_free_descs:
+ destroy_workqueue(hidg->workqueue);
usb_free_all_descriptors(f);
fail:
ERROR(f->config->cdev, "hidg_bind FAILED\n");
if (hidg->req != NULL)
free_ep_req(hidg->in_ep, hidg->req);
+ usb_ep_free_request(c->cdev->gadget->ep0, hidg->get_req);
+ hidg->get_req = NULL;
+
return status;
}
@@ -1256,7 +1515,7 @@ static void hidg_unbind(struct usb_configuration *c, struct usb_function *f)
struct f_hidg *hidg = func_to_hidg(f);
cdev_device_del(&hidg->cdev, &hidg->dev);
-
+ destroy_workqueue(hidg->workqueue);
usb_free_all_descriptors(f);
}
diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c
index 2b9fb4daa806..c87e74afc881 100644
--- a/drivers/usb/gadget/function/f_uac1.c
+++ b/drivers/usb/gadget/function/f_uac1.c
@@ -377,24 +377,10 @@ enum {
STR_AS_OUT_IF_ALT1,
STR_AS_IN_IF_ALT0,
STR_AS_IN_IF_ALT1,
+ NUM_STR_DESCRIPTORS,
};
-static struct usb_string strings_uac1[] = {
- /* [STR_AC_IF].s = DYNAMIC, */
- [STR_USB_OUT_IT].s = "Playback Input terminal",
- [STR_USB_OUT_IT_CH_NAMES].s = "Playback Channels",
- [STR_IO_OUT_OT].s = "Playback Output terminal",
- [STR_IO_IN_IT].s = "Capture Input terminal",
- [STR_IO_IN_IT_CH_NAMES].s = "Capture Channels",
- [STR_USB_IN_OT].s = "Capture Output terminal",
- [STR_FU_IN].s = "Capture Volume",
- [STR_FU_OUT].s = "Playback Volume",
- [STR_AS_OUT_IF_ALT0].s = "Playback Inactive",
- [STR_AS_OUT_IF_ALT1].s = "Playback Active",
- [STR_AS_IN_IF_ALT0].s = "Capture Inactive",
- [STR_AS_IN_IF_ALT1].s = "Capture Active",
- { },
-};
+static struct usb_string strings_uac1[NUM_STR_DESCRIPTORS + 1] = {};
static struct usb_gadget_strings str_uac1 = {
.language = 0x0409, /* en-us */
@@ -1265,6 +1251,20 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
strings_uac1[STR_AC_IF].s = audio_opts->function_name;
+ strings_uac1[STR_USB_OUT_IT].s = audio_opts->c_it_name;
+ strings_uac1[STR_USB_OUT_IT_CH_NAMES].s = audio_opts->c_it_ch_name;
+ strings_uac1[STR_IO_OUT_OT].s = audio_opts->c_ot_name;
+ strings_uac1[STR_FU_OUT].s = audio_opts->c_fu_vol_name;
+ strings_uac1[STR_AS_OUT_IF_ALT0].s = "Playback Inactive";
+ strings_uac1[STR_AS_OUT_IF_ALT1].s = "Playback Active";
+
+ strings_uac1[STR_IO_IN_IT].s = audio_opts->p_it_name;
+ strings_uac1[STR_IO_IN_IT_CH_NAMES].s = audio_opts->p_it_ch_name;
+ strings_uac1[STR_USB_IN_OT].s = audio_opts->p_ot_name;
+ strings_uac1[STR_FU_IN].s = audio_opts->p_fu_vol_name;
+ strings_uac1[STR_AS_IN_IF_ALT0].s = "Capture Inactive";
+ strings_uac1[STR_AS_IN_IF_ALT1].s = "Capture Active";
+
us = usb_gstrings_attach(cdev, uac1_strings, ARRAY_SIZE(strings_uac1));
if (IS_ERR(us))
return PTR_ERR(us);
@@ -1681,8 +1681,19 @@ UAC1_ATTRIBUTE(bool, c_volume_present);
UAC1_ATTRIBUTE(s16, c_volume_min);
UAC1_ATTRIBUTE(s16, c_volume_max);
UAC1_ATTRIBUTE(s16, c_volume_res);
+
UAC1_ATTRIBUTE_STRING(function_name);
+UAC1_ATTRIBUTE_STRING(p_it_name);
+UAC1_ATTRIBUTE_STRING(p_it_ch_name);
+UAC1_ATTRIBUTE_STRING(p_ot_name);
+UAC1_ATTRIBUTE_STRING(p_fu_vol_name);
+
+UAC1_ATTRIBUTE_STRING(c_it_name);
+UAC1_ATTRIBUTE_STRING(c_it_ch_name);
+UAC1_ATTRIBUTE_STRING(c_ot_name);
+UAC1_ATTRIBUTE_STRING(c_fu_vol_name);
+
static struct configfs_attribute *f_uac1_attrs[] = {
&f_uac1_opts_attr_c_chmask,
&f_uac1_opts_attr_c_srate,
@@ -1706,6 +1717,16 @@ static struct configfs_attribute *f_uac1_attrs[] = {
&f_uac1_opts_attr_function_name,
+ &f_uac1_opts_attr_p_it_name,
+ &f_uac1_opts_attr_p_it_ch_name,
+ &f_uac1_opts_attr_p_ot_name,
+ &f_uac1_opts_attr_p_fu_vol_name,
+
+ &f_uac1_opts_attr_c_it_name,
+ &f_uac1_opts_attr_c_it_ch_name,
+ &f_uac1_opts_attr_c_ot_name,
+ &f_uac1_opts_attr_c_fu_vol_name,
+
NULL,
};
@@ -1760,6 +1781,16 @@ static struct usb_function_instance *f_audio_alloc_inst(void)
scnprintf(opts->function_name, sizeof(opts->function_name), "AC Interface");
+ scnprintf(opts->p_it_name, sizeof(opts->p_it_name), "Capture Input terminal");
+ scnprintf(opts->p_it_ch_name, sizeof(opts->p_it_ch_name), "Capture Channels");
+ scnprintf(opts->p_ot_name, sizeof(opts->p_ot_name), "Capture Output terminal");
+ scnprintf(opts->p_fu_vol_name, sizeof(opts->p_fu_vol_name), "Capture Volume");
+
+ scnprintf(opts->c_it_name, sizeof(opts->c_it_name), "Playback Input terminal");
+ scnprintf(opts->c_it_ch_name, sizeof(opts->c_it_ch_name), "Playback Channels");
+ scnprintf(opts->c_ot_name, sizeof(opts->c_ot_name), "Playback Output terminal");
+ scnprintf(opts->c_fu_vol_name, sizeof(opts->c_fu_vol_name), "Playback Volume");
+
return &opts->func_inst;
}
diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
index 2d6d3286ffde..1cdda44455b3 100644
--- a/drivers/usb/gadget/function/f_uac2.c
+++ b/drivers/usb/gadget/function/f_uac2.c
@@ -95,7 +95,9 @@ enum {
STR_CLKSRC_IN,
STR_CLKSRC_OUT,
STR_USB_IT,
+ STR_USB_IT_CH,
STR_IO_IT,
+ STR_IO_IT_CH,
STR_USB_OT,
STR_IO_OT,
STR_FU_IN,
@@ -104,25 +106,10 @@ enum {
STR_AS_OUT_ALT1,
STR_AS_IN_ALT0,
STR_AS_IN_ALT1,
+ NUM_STR_DESCRIPTORS,
};
-static struct usb_string strings_fn[] = {
- /* [STR_ASSOC].s = DYNAMIC, */
- [STR_IF_CTRL].s = "Topology Control",
- [STR_CLKSRC_IN].s = "Input Clock",
- [STR_CLKSRC_OUT].s = "Output Clock",
- [STR_USB_IT].s = "USBH Out",
- [STR_IO_IT].s = "USBD Out",
- [STR_USB_OT].s = "USBH In",
- [STR_IO_OT].s = "USBD In",
- [STR_FU_IN].s = "Capture Volume",
- [STR_FU_OUT].s = "Playback Volume",
- [STR_AS_OUT_ALT0].s = "Playback Inactive",
- [STR_AS_OUT_ALT1].s = "Playback Active",
- [STR_AS_IN_ALT0].s = "Capture Inactive",
- [STR_AS_IN_ALT1].s = "Capture Active",
- { },
-};
+static struct usb_string strings_fn[NUM_STR_DESCRIPTORS + 1] = {};
static const char *const speed_names[] = {
[USB_SPEED_UNKNOWN] = "UNKNOWN",
@@ -1049,6 +1036,23 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
return ret;
strings_fn[STR_ASSOC].s = uac2_opts->function_name;
+ strings_fn[STR_IF_CTRL].s = uac2_opts->if_ctrl_name;
+ strings_fn[STR_CLKSRC_IN].s = uac2_opts->clksrc_in_name;
+ strings_fn[STR_CLKSRC_OUT].s = uac2_opts->clksrc_out_name;
+
+ strings_fn[STR_USB_IT].s = uac2_opts->c_it_name;
+ strings_fn[STR_USB_IT_CH].s = uac2_opts->c_it_ch_name;
+ strings_fn[STR_IO_OT].s = uac2_opts->c_ot_name;
+ strings_fn[STR_FU_OUT].s = uac2_opts->c_fu_vol_name;
+ strings_fn[STR_AS_OUT_ALT0].s = "Playback Inactive";
+ strings_fn[STR_AS_OUT_ALT1].s = "Playback Active";
+
+ strings_fn[STR_IO_IT].s = uac2_opts->p_it_name;
+ strings_fn[STR_IO_IT_CH].s = uac2_opts->p_it_ch_name;
+ strings_fn[STR_USB_OT].s = uac2_opts->p_ot_name;
+ strings_fn[STR_FU_IN].s = uac2_opts->p_fu_vol_name;
+ strings_fn[STR_AS_IN_ALT0].s = "Capture Inactive";
+ strings_fn[STR_AS_IN_ALT1].s = "Capture Active";
us = usb_gstrings_attach(cdev, fn_strings, ARRAY_SIZE(strings_fn));
if (IS_ERR(us))
@@ -1072,7 +1076,9 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
in_clk_src_desc.iClockSource = us[STR_CLKSRC_IN].id;
out_clk_src_desc.iClockSource = us[STR_CLKSRC_OUT].id;
usb_out_it_desc.iTerminal = us[STR_USB_IT].id;
+ usb_out_it_desc.iChannelNames = us[STR_USB_IT_CH].id;
io_in_it_desc.iTerminal = us[STR_IO_IT].id;
+ io_in_it_desc.iChannelNames = us[STR_IO_IT_CH].id;
usb_in_ot_desc.iTerminal = us[STR_USB_OT].id;
io_out_ot_desc.iTerminal = us[STR_IO_OT].id;
std_as_out_if0_desc.iInterface = us[STR_AS_OUT_ALT0].id;
@@ -2100,10 +2106,24 @@ UAC2_ATTRIBUTE(s16, c_volume_max);
UAC2_ATTRIBUTE(s16, c_volume_res);
UAC2_ATTRIBUTE(u32, fb_max);
UAC2_ATTRIBUTE_STRING(function_name);
+UAC2_ATTRIBUTE_STRING(if_ctrl_name);
+UAC2_ATTRIBUTE_STRING(clksrc_in_name);
+UAC2_ATTRIBUTE_STRING(clksrc_out_name);
+
+UAC2_ATTRIBUTE_STRING(p_it_name);
+UAC2_ATTRIBUTE_STRING(p_it_ch_name);
+UAC2_ATTRIBUTE_STRING(p_ot_name);
+UAC2_ATTRIBUTE_STRING(p_fu_vol_name);
+
+UAC2_ATTRIBUTE_STRING(c_it_name);
+UAC2_ATTRIBUTE_STRING(c_it_ch_name);
+UAC2_ATTRIBUTE_STRING(c_ot_name);
+UAC2_ATTRIBUTE_STRING(c_fu_vol_name);
UAC2_ATTRIBUTE(s16, p_terminal_type);
UAC2_ATTRIBUTE(s16, c_terminal_type);
+
static struct configfs_attribute *f_uac2_attrs[] = {
&f_uac2_opts_attr_p_chmask,
&f_uac2_opts_attr_p_srate,
@@ -2130,6 +2150,19 @@ static struct configfs_attribute *f_uac2_attrs[] = {
&f_uac2_opts_attr_c_volume_res,
&f_uac2_opts_attr_function_name,
+ &f_uac2_opts_attr_if_ctrl_name,
+ &f_uac2_opts_attr_clksrc_in_name,
+ &f_uac2_opts_attr_clksrc_out_name,
+
+ &f_uac2_opts_attr_p_it_name,
+ &f_uac2_opts_attr_p_it_ch_name,
+ &f_uac2_opts_attr_p_ot_name,
+ &f_uac2_opts_attr_p_fu_vol_name,
+
+ &f_uac2_opts_attr_c_it_name,
+ &f_uac2_opts_attr_c_it_ch_name,
+ &f_uac2_opts_attr_c_ot_name,
+ &f_uac2_opts_attr_c_fu_vol_name,
&f_uac2_opts_attr_p_terminal_type,
&f_uac2_opts_attr_c_terminal_type,
@@ -2191,6 +2224,19 @@ static struct usb_function_instance *afunc_alloc_inst(void)
opts->fb_max = FBACK_FAST_MAX;
scnprintf(opts->function_name, sizeof(opts->function_name), "Source/Sink");
+ scnprintf(opts->if_ctrl_name, sizeof(opts->if_ctrl_name), "Topology Control");
+ scnprintf(opts->clksrc_in_name, sizeof(opts->clksrc_in_name), "Input Clock");
+ scnprintf(opts->clksrc_out_name, sizeof(opts->clksrc_out_name), "Output Clock");
+
+ scnprintf(opts->p_it_name, sizeof(opts->p_it_name), "USBD Out");
+ scnprintf(opts->p_it_ch_name, sizeof(opts->p_it_ch_name), "Capture Channels");
+ scnprintf(opts->p_ot_name, sizeof(opts->p_ot_name), "USBH In");
+ scnprintf(opts->p_fu_vol_name, sizeof(opts->p_fu_vol_name), "Capture Volume");
+
+ scnprintf(opts->c_it_name, sizeof(opts->c_it_name), "USBH Out");
+ scnprintf(opts->c_it_ch_name, sizeof(opts->c_it_ch_name), "Playback Channels");
+ scnprintf(opts->c_ot_name, sizeof(opts->c_ot_name), "USBD In");
+ scnprintf(opts->c_fu_vol_name, sizeof(opts->c_fu_vol_name), "Playback Volume");
opts->p_terminal_type = UAC2_DEF_P_TERM_TYPE;
opts->c_terminal_type = UAC2_DEF_C_TERM_TYPE;
diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c
index 24299576972f..ca8dbec65f73 100644
--- a/drivers/usb/gadget/function/u_audio.c
+++ b/drivers/usb/gadget/function/u_audio.c
@@ -1140,35 +1140,35 @@ static int u_audio_rate_get(struct snd_kcontrol *kcontrol,
}
static struct snd_kcontrol_new u_audio_controls[] = {
- [UAC_FBACK_CTRL] {
+ [UAC_FBACK_CTRL] = {
.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,
},
- [UAC_P_PITCH_CTRL] {
+ [UAC_P_PITCH_CTRL] = {
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "Playback Pitch 1000000",
.info = u_audio_pitch_info,
.get = u_audio_pitch_get,
.put = u_audio_pitch_put,
},
- [UAC_MUTE_CTRL] {
+ [UAC_MUTE_CTRL] = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "", /* will be filled later */
.info = u_audio_mute_info,
.get = u_audio_mute_get,
.put = u_audio_mute_put,
},
- [UAC_VOLUME_CTRL] {
+ [UAC_VOLUME_CTRL] = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "", /* will be filled later */
.info = u_audio_volume_info,
.get = u_audio_volume_get,
.put = u_audio_volume_put,
},
- [UAC_RATE_CTRL] {
+ [UAC_RATE_CTRL] = {
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "", /* will be filled later */
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c
index b394105e55d6..0a8c05b2746b 100644
--- a/drivers/usb/gadget/function/u_serial.c
+++ b/drivers/usb/gadget/function/u_serial.c
@@ -28,6 +28,7 @@
#include <linux/kthread.h>
#include <linux/workqueue.h>
#include <linux/kfifo.h>
+#include <linux/serial.h>
#include "u_serial.h"
@@ -126,6 +127,7 @@ struct gs_port {
wait_queue_head_t close_wait;
bool suspended; /* port suspended */
bool start_delayed; /* delay start when suspended */
+ struct async_icount icount;
/* REVISIT this state ... */
struct usb_cdc_line_coding port_line_coding; /* 8-N-1 etc */
@@ -257,6 +259,7 @@ __acquires(&port->port_lock)
break;
}
do_tty_wake = true;
+ port->icount.tx += len;
req->length = len;
list_del(&req->list);
@@ -408,6 +411,7 @@ static void gs_rx_push(struct work_struct *work)
size -= n;
}
+ port->icount.rx += size;
count = tty_insert_flip_string(&port->port, packet,
size);
if (count)
@@ -851,6 +855,23 @@ static int gs_break_ctl(struct tty_struct *tty, int duration)
return status;
}
+static int gs_get_icount(struct tty_struct *tty,
+ struct serial_icounter_struct *icount)
+{
+ struct gs_port *port = tty->driver_data;
+ struct async_icount cnow;
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ cnow = port->icount;
+ spin_unlock_irqrestore(&port->port_lock, flags);
+
+ icount->rx = cnow.rx;
+ icount->tx = cnow.tx;
+
+ return 0;
+}
+
static const struct tty_operations gs_tty_ops = {
.open = gs_open,
.close = gs_close,
@@ -861,6 +882,7 @@ static const struct tty_operations gs_tty_ops = {
.chars_in_buffer = gs_chars_in_buffer,
.unthrottle = gs_unthrottle,
.break_ctl = gs_break_ctl,
+ .get_icount = gs_get_icount,
};
/*-------------------------------------------------------------------------*/
diff --git a/drivers/usb/gadget/function/u_uac1.h b/drivers/usb/gadget/function/u_uac1.h
index f7a616760e31..feb6eb76462f 100644
--- a/drivers/usb/gadget/function/u_uac1.h
+++ b/drivers/usb/gadget/function/u_uac1.h
@@ -52,7 +52,17 @@ struct f_uac1_opts {
int req_number;
unsigned bound:1;
- char function_name[32];
+ char function_name[USB_MAX_STRING_LEN];
+
+ char p_it_name[USB_MAX_STRING_LEN];
+ char p_it_ch_name[USB_MAX_STRING_LEN];
+ char p_ot_name[USB_MAX_STRING_LEN];
+ char p_fu_vol_name[USB_MAX_STRING_LEN];
+
+ char c_it_name[USB_MAX_STRING_LEN];
+ char c_it_ch_name[USB_MAX_STRING_LEN];
+ char c_ot_name[USB_MAX_STRING_LEN];
+ char c_fu_vol_name[USB_MAX_STRING_LEN];
struct mutex lock;
int refcnt;
diff --git a/drivers/usb/gadget/function/u_uac2.h b/drivers/usb/gadget/function/u_uac2.h
index 5e81bdd6c5fb..0df808289ded 100644
--- a/drivers/usb/gadget/function/u_uac2.h
+++ b/drivers/usb/gadget/function/u_uac2.h
@@ -68,7 +68,20 @@ struct f_uac2_opts {
int fb_max;
bool bound;
- char function_name[32];
+ char function_name[USB_MAX_STRING_LEN];
+ char if_ctrl_name[USB_MAX_STRING_LEN];
+ char clksrc_in_name[USB_MAX_STRING_LEN];
+ char clksrc_out_name[USB_MAX_STRING_LEN];
+
+ char p_it_name[USB_MAX_STRING_LEN];
+ char p_it_ch_name[USB_MAX_STRING_LEN];
+ char p_ot_name[USB_MAX_STRING_LEN];
+ char p_fu_vol_name[USB_MAX_STRING_LEN];
+
+ char c_it_name[USB_MAX_STRING_LEN];
+ char c_it_ch_name[USB_MAX_STRING_LEN];
+ char c_ot_name[USB_MAX_STRING_LEN];
+ char c_fu_vol_name[USB_MAX_STRING_LEN];
s16 p_terminal_type;
s16 c_terminal_type;
diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c
index a024aecb76dc..de1736f834e6 100644
--- a/drivers/usb/gadget/function/uvc_v4l2.c
+++ b/drivers/usb/gadget/function/uvc_v4l2.c
@@ -121,6 +121,9 @@ static struct uvcg_format *find_format_by_pix(struct uvc_device *uvc,
list_for_each_entry(format, &uvc->header->formats, entry) {
const struct uvc_format_desc *fmtdesc = to_uvc_format(format->fmt);
+ if (IS_ERR(fmtdesc))
+ continue;
+
if (fmtdesc->fcc == pixelformat) {
uformat = format->fmt;
break;
@@ -240,6 +243,7 @@ uvc_v4l2_try_format(struct file *file, void *fh, struct v4l2_format *fmt)
struct uvc_video *video = &uvc->video;
struct uvcg_format *uformat;
struct uvcg_frame *uframe;
+ const struct uvc_format_desc *fmtdesc;
u8 *fcc;
if (fmt->type != video->queue.queue.type)
@@ -277,7 +281,10 @@ uvc_v4l2_try_format(struct file *file, void *fh, struct v4l2_format *fmt)
fmt->fmt.pix.height = uframe->frame.w_height;
fmt->fmt.pix.bytesperline = uvc_v4l2_get_bytesperline(uformat, uframe);
fmt->fmt.pix.sizeimage = uvc_get_frame_size(uformat, uframe);
- fmt->fmt.pix.pixelformat = to_uvc_format(uformat)->fcc;
+ fmtdesc = to_uvc_format(uformat);
+ if (IS_ERR(fmtdesc))
+ return PTR_ERR(fmtdesc);
+ fmt->fmt.pix.pixelformat = fmtdesc->fcc;
}
fmt->fmt.pix.field = V4L2_FIELD_NONE;
fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
@@ -389,6 +396,9 @@ uvc_v4l2_enum_format(struct file *file, void *fh, struct v4l2_fmtdesc *f)
return -EINVAL;
fmtdesc = to_uvc_format(uformat);
+ if (IS_ERR(fmtdesc))
+ return PTR_ERR(fmtdesc);
+
f->pixelformat = fmtdesc->fcc;
return 0;
diff --git a/drivers/usb/gadget/udc/bdc/bdc_core.c b/drivers/usb/gadget/udc/bdc/bdc_core.c
index 35a652807fca..5149e2b7f050 100644
--- a/drivers/usb/gadget/udc/bdc/bdc_core.c
+++ b/drivers/usb/gadget/udc/bdc/bdc_core.c
@@ -639,6 +639,7 @@ static const struct of_device_id bdc_of_match[] = {
{ .compatible = "brcm,bdc" },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, bdc_of_match);
static struct platform_driver bdc_driver = {
.driver = {
diff --git a/drivers/usb/gadget/udc/lpc32xx_udc.c b/drivers/usb/gadget/udc/lpc32xx_udc.c
index d5f29f8fe481..3bfd889ed56a 100644
--- a/drivers/usb/gadget/udc/lpc32xx_udc.c
+++ b/drivers/usb/gadget/udc/lpc32xx_udc.c
@@ -1487,31 +1487,29 @@ static int udc_ep0_out_req(struct lpc32xx_udc *udc)
req = list_entry(ep0->queue.next, struct lpc32xx_request,
queue);
- if (req) {
- if (req->req.length == 0) {
- /* Just dequeue request */
- done(ep0, req, 0);
- udc->ep0state = WAIT_FOR_SETUP;
- return 1;
- }
+ if (req->req.length == 0) {
+ /* Just dequeue request */
+ done(ep0, req, 0);
+ udc->ep0state = WAIT_FOR_SETUP;
+ return 1;
+ }
- /* Get data from FIFO */
- bufferspace = req->req.length - req->req.actual;
- if (bufferspace > ep0->ep.maxpacket)
- bufferspace = ep0->ep.maxpacket;
+ /* Get data from FIFO */
+ bufferspace = req->req.length - req->req.actual;
+ if (bufferspace > ep0->ep.maxpacket)
+ bufferspace = ep0->ep.maxpacket;
- /* Copy data to buffer */
- prefetchw(req->req.buf + req->req.actual);
- tr = udc_read_hwep(udc, EP_OUT, req->req.buf + req->req.actual,
- bufferspace);
- req->req.actual += bufferspace;
+ /* Copy data to buffer */
+ prefetchw(req->req.buf + req->req.actual);
+ tr = udc_read_hwep(udc, EP_OUT, req->req.buf + req->req.actual,
+ bufferspace);
+ req->req.actual += bufferspace;
- if (tr < ep0->ep.maxpacket) {
- /* This is the last packet */
- done(ep0, req, 0);
- udc->ep0state = WAIT_FOR_SETUP;
- return 1;
- }
+ if (tr < ep0->ep.maxpacket) {
+ /* This is the last packet */
+ done(ep0, req, 0);
+ udc->ep0state = WAIT_FOR_SETUP;
+ return 1;
}
return 0;
@@ -1962,18 +1960,17 @@ static void udc_handle_eps(struct lpc32xx_udc *udc, struct lpc32xx_ep *ep)
/* If there isn't a request waiting, something went wrong */
req = list_entry(ep->queue.next, struct lpc32xx_request, queue);
- if (req) {
- done(ep, req, 0);
- /* Start another request if ready */
- if (!list_empty(&ep->queue)) {
- if (ep->is_in)
- udc_ep_in_req_dma(udc, ep);
- else
- udc_ep_out_req_dma(udc, ep);
- } else
- ep->req_pending = 0;
- }
+ done(ep, req, 0);
+
+ /* Start another request if ready */
+ if (!list_empty(&ep->queue)) {
+ if (ep->is_in)
+ udc_ep_in_req_dma(udc, ep);
+ else
+ udc_ep_out_req_dma(udc, ep);
+ } else
+ ep->req_pending = 0;
}
@@ -1989,10 +1986,6 @@ static void udc_handle_dma_ep(struct lpc32xx_udc *udc, struct lpc32xx_ep *ep)
#endif
req = list_entry(ep->queue.next, struct lpc32xx_request, queue);
- if (!req) {
- ep_err(ep, "DMA interrupt on no req!\n");
- return;
- }
dd = req->dd_desc_ptr;
/* DMA descriptor should always be retired for this call */
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 4448d0ab06f0..d011d6c753ed 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -40,11 +40,11 @@ config USB_XHCI_DBGCAP
config USB_XHCI_PCI
tristate
depends on USB_PCI
- depends on USB_XHCI_PCI_RENESAS || !USB_XHCI_PCI_RENESAS
default y
config USB_XHCI_PCI_RENESAS
tristate "Support for additional Renesas xHCI controller with firmware"
+ depends on USB_XHCI_PCI
help
Say 'Y' to enable the support for the Renesas xHCI controller with
firmware. Make sure you have the firmware for the device and
diff --git a/drivers/usb/host/ehci-brcm.c b/drivers/usb/host/ehci-brcm.c
index 77e42c739c58..68cad0620f1a 100644
--- a/drivers/usb/host/ehci-brcm.c
+++ b/drivers/usb/host/ehci-brcm.c
@@ -246,6 +246,7 @@ static const struct of_device_id brcm_ehci_of_match[] = {
{ .compatible = "brcm,bcm7445-ehci", },
{}
};
+MODULE_DEVICE_TABLE(of, brcm_ehci_of_match);
static struct platform_driver ehci_brcm_driver = {
.probe = ehci_brcm_probe,
diff --git a/drivers/usb/host/ehci-exynos.c b/drivers/usb/host/ehci-exynos.c
index f40bc2a7a124..e3a961d3f5fc 100644
--- a/drivers/usb/host/ehci-exynos.c
+++ b/drivers/usb/host/ehci-exynos.c
@@ -48,7 +48,6 @@ struct exynos_ehci_hcd {
static int exynos_ehci_get_phy(struct device *dev,
struct exynos_ehci_hcd *exynos_ehci)
{
- struct device_node *child;
struct phy *phy;
int phy_number, num_phys;
int ret;
@@ -66,26 +65,22 @@ static int exynos_ehci_get_phy(struct device *dev,
return 0;
/* Get PHYs using legacy bindings */
- for_each_available_child_of_node(dev->of_node, child) {
+ for_each_available_child_of_node_scoped(dev->of_node, child) {
ret = of_property_read_u32(child, "reg", &phy_number);
if (ret) {
dev_err(dev, "Failed to parse device tree\n");
- of_node_put(child);
return ret;
}
if (phy_number >= PHY_NUMBER) {
dev_err(dev, "Invalid number of PHYs\n");
- of_node_put(child);
return -EINVAL;
}
phy = devm_of_phy_optional_get(dev, child, NULL);
exynos_ehci->phy[phy_number] = phy;
- if (IS_ERR(phy)) {
- of_node_put(child);
+ if (IS_ERR(phy))
return PTR_ERR(phy);
- }
}
exynos_ehci->legacy_phy = true;
diff --git a/drivers/usb/host/ohci-exynos.c b/drivers/usb/host/ohci-exynos.c
index bfa2eba4e3a7..1379e03644b2 100644
--- a/drivers/usb/host/ohci-exynos.c
+++ b/drivers/usb/host/ohci-exynos.c
@@ -37,7 +37,6 @@ struct exynos_ohci_hcd {
static int exynos_ohci_get_phy(struct device *dev,
struct exynos_ohci_hcd *exynos_ohci)
{
- struct device_node *child;
struct phy *phy;
int phy_number, num_phys;
int ret;
@@ -55,26 +54,22 @@ static int exynos_ohci_get_phy(struct device *dev,
return 0;
/* Get PHYs using legacy bindings */
- for_each_available_child_of_node(dev->of_node, child) {
+ for_each_available_child_of_node_scoped(dev->of_node, child) {
ret = of_property_read_u32(child, "reg", &phy_number);
if (ret) {
dev_err(dev, "Failed to parse device tree\n");
- of_node_put(child);
return ret;
}
if (phy_number >= PHY_NUMBER) {
dev_err(dev, "Invalid number of PHYs\n");
- of_node_put(child);
return -EINVAL;
}
phy = devm_of_phy_optional_get(dev, child, NULL);
exynos_ohci->phy[phy_number] = phy;
- if (IS_ERR(phy)) {
- of_node_put(child);
+ if (IS_ERR(phy))
return PTR_ERR(phy);
- }
}
exynos_ohci->legacy_phy = true;
diff --git a/drivers/usb/host/ohci-ppc-of.c b/drivers/usb/host/ohci-ppc-of.c
index f64bfe5f4d4d..a6be061f8653 100644
--- a/drivers/usb/host/ohci-ppc-of.c
+++ b/drivers/usb/host/ohci-ppc-of.c
@@ -204,10 +204,6 @@ static const struct of_device_id ohci_hcd_ppc_of_match[] = {
#ifdef CONFIG_USB_OHCI_HCD_PPC_OF_LE
{
.name = "usb",
- .compatible = "ohci-littledian",
- },
- {
- .name = "usb",
.compatible = "ohci-le",
},
#endif
diff --git a/drivers/usb/host/xhci-pci-renesas.c b/drivers/usb/host/xhci-pci-renesas.c
index 247cc7c2ce70..30cc5a1380a5 100644
--- a/drivers/usb/host/xhci-pci-renesas.c
+++ b/drivers/usb/host/xhci-pci-renesas.c
@@ -50,6 +50,8 @@
#define RENESAS_RETRY 10000
#define RENESAS_DELAY 10
+#define RENESAS_FW_NAME "renesas_usb_fw.mem"
+
static int renesas_fw_download_image(struct pci_dev *dev,
const u32 *fw, size_t step, bool rom)
{
@@ -573,12 +575,10 @@ exit:
return err;
}
-int renesas_xhci_check_request_fw(struct pci_dev *pdev,
- const struct pci_device_id *id)
+static int renesas_xhci_check_request_fw(struct pci_dev *pdev,
+ const struct pci_device_id *id)
{
- struct xhci_driver_data *driver_data =
- (struct xhci_driver_data *)id->driver_data;
- const char *fw_name = driver_data->firmware;
+ const char fw_name[] = RENESAS_FW_NAME;
const struct firmware *fw;
bool has_rom;
int err;
@@ -625,7 +625,41 @@ exit:
release_firmware(fw);
return err;
}
-EXPORT_SYMBOL_GPL(renesas_xhci_check_request_fw);
-MODULE_DESCRIPTION("Support for Renesas xHCI controller with firmware");
+static int
+xhci_pci_renesas_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+ int retval;
+
+ retval = renesas_xhci_check_request_fw(dev, id);
+ if (retval)
+ return retval;
+
+ return xhci_pci_common_probe(dev, id);
+}
+
+static const struct pci_device_id pci_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, 0x0014) },
+ { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, 0x0015) },
+ { /* end: all zeroes */ }
+};
+MODULE_DEVICE_TABLE(pci, pci_ids);
+
+static struct pci_driver xhci_renesas_pci_driver = {
+ .name = "xhci-pci-renesas",
+ .id_table = pci_ids,
+
+ .probe = xhci_pci_renesas_probe,
+ .remove = xhci_pci_remove,
+
+ .shutdown = usb_hcd_pci_shutdown,
+ .driver = {
+ .pm = pm_ptr(&usb_hcd_pci_pm_ops),
+ },
+};
+module_pci_driver(xhci_renesas_pci_driver);
+
+MODULE_DESCRIPTION("Renesas xHCI PCI Host Controller Driver");
+MODULE_FIRMWARE(RENESAS_FW_NAME);
+MODULE_IMPORT_NS(xhci);
MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index dc1e345ab67e..b5705ed01d83 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -235,15 +235,6 @@ static int xhci_pci_reinit(struct xhci_hcd *xhci, struct pci_dev *pdev)
static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
{
struct pci_dev *pdev = to_pci_dev(dev);
- struct xhci_driver_data *driver_data;
- const struct pci_device_id *id;
-
- id = pci_match_id(to_pci_driver(pdev->dev.driver)->id_table, pdev);
-
- if (id && id->driver_data) {
- driver_data = (struct xhci_driver_data *)id->driver_data;
- xhci->quirks |= driver_data->quirks;
- }
/* Look for vendor-specific quirks */
if (pdev->vendor == PCI_VENDOR_ID_FRESCO_LOGIC &&
@@ -572,21 +563,13 @@ static int xhci_pci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hd
* We need to register our own PCI probe function (instead of the USB core's
* function) in order to create a second roothub under xHCI.
*/
-static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
+int xhci_pci_common_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
int retval;
struct xhci_hcd *xhci;
struct usb_hcd *hcd;
- struct xhci_driver_data *driver_data;
struct reset_control *reset;
- driver_data = (struct xhci_driver_data *)id->driver_data;
- if (driver_data && driver_data->quirks & XHCI_RENESAS_FW_QUIRK) {
- retval = renesas_xhci_check_request_fw(dev, id);
- if (retval)
- return retval;
- }
-
reset = devm_reset_control_get_optional_exclusive(&dev->dev, NULL);
if (IS_ERR(reset))
return PTR_ERR(reset);
@@ -651,8 +634,24 @@ put_runtime_pm:
pm_runtime_put_noidle(&dev->dev);
return retval;
}
+EXPORT_SYMBOL_NS_GPL(xhci_pci_common_probe, xhci);
+
+static const struct pci_device_id pci_ids_reject[] = {
+ /* handled by xhci-pci-renesas */
+ { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, 0x0014) },
+ { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, 0x0015) },
+ { /* end: all zeroes */ }
+};
+
+static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+ if (pci_match_id(pci_ids_reject, dev))
+ return -ENODEV;
+
+ return xhci_pci_common_probe(dev, id);
+}
-static void xhci_pci_remove(struct pci_dev *dev)
+void xhci_pci_remove(struct pci_dev *dev)
{
struct xhci_hcd *xhci;
@@ -675,6 +674,7 @@ static void xhci_pci_remove(struct pci_dev *dev)
usb_hcd_pci_remove(dev);
}
+EXPORT_SYMBOL_NS_GPL(xhci_pci_remove, xhci);
/*
* In some Intel xHCI controllers, in order to get D3 working,
@@ -882,19 +882,8 @@ static void xhci_pci_shutdown(struct usb_hcd *hcd)
/*-------------------------------------------------------------------------*/
-static const struct xhci_driver_data reneses_data = {
- .quirks = XHCI_RENESAS_FW_QUIRK,
- .firmware = "renesas_usb_fw.mem",
-};
-
/* PCI driver selection metadata; PCI hotplugging uses this */
static const struct pci_device_id pci_ids[] = {
- { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, 0x0014),
- .driver_data = (unsigned long)&reneses_data,
- },
- { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, 0x0015),
- .driver_data = (unsigned long)&reneses_data,
- },
/* handle any USB 3.0 xHCI controller */
{ PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_XHCI, ~0),
},
@@ -902,14 +891,6 @@ static const struct pci_device_id pci_ids[] = {
};
MODULE_DEVICE_TABLE(pci, pci_ids);
-/*
- * Without CONFIG_USB_XHCI_PCI_RENESAS renesas_xhci_check_request_fw() won't
- * load firmware, so don't encumber the xhci-pci driver with it.
- */
-#if IS_ENABLED(CONFIG_USB_XHCI_PCI_RENESAS)
-MODULE_FIRMWARE("renesas_usb_fw.mem");
-#endif
-
/* pci driver glue; this is a "new style" PCI driver module */
static struct pci_driver xhci_pci_driver = {
.name = hcd_name,
diff --git a/drivers/usb/host/xhci-pci.h b/drivers/usb/host/xhci-pci.h
index cb9a8f331a44..e87c7d9d76b8 100644
--- a/drivers/usb/host/xhci-pci.h
+++ b/drivers/usb/host/xhci-pci.h
@@ -4,22 +4,7 @@
#ifndef XHCI_PCI_H
#define XHCI_PCI_H
-#if IS_ENABLED(CONFIG_USB_XHCI_PCI_RENESAS)
-int renesas_xhci_check_request_fw(struct pci_dev *dev,
- const struct pci_device_id *id);
-
-#else
-static int renesas_xhci_check_request_fw(struct pci_dev *dev,
- const struct pci_device_id *id)
-{
- return 0;
-}
-
-#endif
-
-struct xhci_driver_data {
- u64 quirks;
- const char *firmware;
-};
+int xhci_pci_common_probe(struct pci_dev *dev, const struct pci_device_id *id);
+void xhci_pci_remove(struct pci_dev *dev);
#endif
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index ebd0afd59a60..df2c81db21a1 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1616,7 +1616,7 @@ struct xhci_hcd {
#define XHCI_DEFAULT_PM_RUNTIME_ALLOW BIT_ULL(33)
#define XHCI_RESET_PLL_ON_DISCONNECT BIT_ULL(34)
#define XHCI_SNPS_BROKEN_SUSPEND BIT_ULL(35)
-#define XHCI_RENESAS_FW_QUIRK BIT_ULL(36)
+/* Reserved. It was XHCI_RENESAS_FW_QUIRK */
#define XHCI_SKIP_PHY_INIT BIT_ULL(37)
#define XHCI_DISABLE_SPARSE BIT_ULL(38)
#define XHCI_SG_TRB_CACHE_SIZE_QUIRK BIT_ULL(39)
diff --git a/drivers/usb/misc/brcmstb-usb-pinmap.c b/drivers/usb/misc/brcmstb-usb-pinmap.c
index 2b2019c19cde..1ce885e4184c 100644
--- a/drivers/usb/misc/brcmstb-usb-pinmap.c
+++ b/drivers/usb/misc/brcmstb-usb-pinmap.c
@@ -335,6 +335,7 @@ static const struct of_device_id brcmstb_usb_pinmap_of_match[] = {
{ .compatible = "brcm,usb-pinmap" },
{ },
};
+MODULE_DEVICE_TABLE(of, brcmstb_usb_pinmap_of_match);
static struct platform_driver brcmstb_usb_pinmap_driver = {
.driver = {
diff --git a/drivers/usb/misc/qcom_eud.c b/drivers/usb/misc/qcom_eud.c
index 26e9b8749d8a..19906301a4eb 100644
--- a/drivers/usb/misc/qcom_eud.c
+++ b/drivers/usb/misc/qcom_eud.c
@@ -232,7 +232,7 @@ static void eud_remove(struct platform_device *pdev)
}
static const struct of_device_id eud_dt_match[] = {
- { .compatible = "qcom,sc7280-eud" },
+ { .compatible = "qcom,eud" },
{ }
};
MODULE_DEVICE_TABLE(of, eud_dt_match);
diff --git a/drivers/usb/musb/mpfs.c b/drivers/usb/musb/mpfs.c
index 29c7e5cdb230..00e13214aa76 100644
--- a/drivers/usb/musb/mpfs.c
+++ b/drivers/usb/musb/mpfs.c
@@ -49,30 +49,6 @@ static const struct musb_hdrc_config mpfs_musb_hdrc_config = {
.ram_bits = MPFS_MUSB_RAM_BITS,
};
-static irqreturn_t mpfs_musb_interrupt(int irq, void *__hci)
-{
- unsigned long flags;
- irqreturn_t ret = IRQ_NONE;
- struct musb *musb = __hci;
-
- spin_lock_irqsave(&musb->lock, flags);
-
- musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB);
- musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX);
- musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX);
-
- if (musb->int_usb || musb->int_tx || musb->int_rx) {
- musb_writeb(musb->mregs, MUSB_INTRUSB, musb->int_usb);
- musb_writew(musb->mregs, MUSB_INTRTX, musb->int_tx);
- musb_writew(musb->mregs, MUSB_INTRRX, musb->int_rx);
- ret = musb_interrupt(musb);
- }
-
- spin_unlock_irqrestore(&musb->lock, flags);
-
- return ret;
-}
-
static void mpfs_musb_set_vbus(struct musb *musb, int is_on)
{
u8 devctl;
@@ -111,6 +87,129 @@ static void mpfs_musb_set_vbus(struct musb *musb, int is_on)
musb_readb(musb->mregs, MUSB_DEVCTL));
}
+#define POLL_SECONDS 2
+
+static void otg_timer(struct timer_list *t)
+{
+ struct musb *musb = from_timer(musb, t, dev_timer);
+ void __iomem *mregs = musb->mregs;
+ u8 devctl;
+ unsigned long flags;
+
+ /*
+ * We poll because PolarFire SoC won't expose several OTG-critical
+ * status change events (from the transceiver) otherwise.
+ */
+ devctl = musb_readb(mregs, MUSB_DEVCTL);
+ dev_dbg(musb->controller, "Poll devctl %02x (%s)\n", devctl,
+ usb_otg_state_string(musb->xceiv->otg->state));
+
+ spin_lock_irqsave(&musb->lock, flags);
+ switch (musb->xceiv->otg->state) {
+ case OTG_STATE_A_WAIT_BCON:
+ devctl &= ~MUSB_DEVCTL_SESSION;
+ musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
+
+ devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
+ if (devctl & MUSB_DEVCTL_BDEVICE) {
+ musb->xceiv->otg->state = OTG_STATE_B_IDLE;
+ MUSB_DEV_MODE(musb);
+ mod_timer(&musb->dev_timer, jiffies + POLL_SECONDS * HZ);
+ } else {
+ musb->xceiv->otg->state = OTG_STATE_A_IDLE;
+ MUSB_HST_MODE(musb);
+ }
+ break;
+ case OTG_STATE_A_WAIT_VFALL:
+ if (devctl & MUSB_DEVCTL_VBUS) {
+ mod_timer(&musb->dev_timer, jiffies + POLL_SECONDS * HZ);
+ break;
+ }
+ musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE;
+ break;
+ case OTG_STATE_B_IDLE:
+ /*
+ * There's no ID-changed IRQ, so we have no good way to tell
+ * when to switch to the A-Default state machine (by setting
+ * the DEVCTL.Session bit).
+ *
+ * Workaround: whenever we're in B_IDLE, try setting the
+ * session flag every few seconds. If it works, ID was
+ * grounded and we're now in the A-Default state machine.
+ *
+ * NOTE: setting the session flag is _supposed_ to trigger
+ * SRP but clearly it doesn't.
+ */
+ musb_writeb(mregs, MUSB_DEVCTL, devctl | MUSB_DEVCTL_SESSION);
+ devctl = musb_readb(mregs, MUSB_DEVCTL);
+ if (devctl & MUSB_DEVCTL_BDEVICE)
+ mod_timer(&musb->dev_timer, jiffies + POLL_SECONDS * HZ);
+ else
+ musb->xceiv->otg->state = OTG_STATE_A_IDLE;
+ break;
+ default:
+ break;
+ }
+ spin_unlock_irqrestore(&musb->lock, flags);
+}
+
+static void __maybe_unused mpfs_musb_try_idle(struct musb *musb, unsigned long timeout)
+{
+ static unsigned long last_timer;
+
+ if (timeout == 0)
+ timeout = jiffies + msecs_to_jiffies(3);
+
+ /* Never idle if active, or when VBUS timeout is not set as host */
+ if (musb->is_active || (musb->a_wait_bcon == 0 &&
+ musb->xceiv->otg->state == OTG_STATE_A_WAIT_BCON)) {
+ dev_dbg(musb->controller, "%s active, deleting timer\n",
+ usb_otg_state_string(musb->xceiv->otg->state));
+ del_timer(&musb->dev_timer);
+ last_timer = jiffies;
+ return;
+ }
+
+ if (time_after(last_timer, timeout) && timer_pending(&musb->dev_timer)) {
+ dev_dbg(musb->controller, "Longer idle timer already pending, ignoring...\n");
+ return;
+ }
+ last_timer = timeout;
+
+ dev_dbg(musb->controller, "%s inactive, starting idle timer for %u ms\n",
+ usb_otg_state_string(musb->xceiv->otg->state),
+ jiffies_to_msecs(timeout - jiffies));
+ mod_timer(&musb->dev_timer, timeout);
+}
+
+static irqreturn_t mpfs_musb_interrupt(int irq, void *__hci)
+{
+ unsigned long flags;
+ irqreturn_t ret = IRQ_NONE;
+ struct musb *musb = __hci;
+
+ spin_lock_irqsave(&musb->lock, flags);
+
+ musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB);
+ musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX);
+ musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX);
+
+ if (musb->int_usb || musb->int_tx || musb->int_rx) {
+ musb_writeb(musb->mregs, MUSB_INTRUSB, musb->int_usb);
+ musb_writew(musb->mregs, MUSB_INTRTX, musb->int_tx);
+ musb_writew(musb->mregs, MUSB_INTRRX, musb->int_rx);
+ ret = musb_interrupt(musb);
+ }
+
+ /* Poll for ID change */
+ if (musb->xceiv->otg->state == OTG_STATE_B_IDLE)
+ mod_timer(&musb->dev_timer, jiffies + POLL_SECONDS * HZ);
+
+ spin_unlock_irqrestore(&musb->lock, flags);
+
+ return ret;
+}
+
static int mpfs_musb_init(struct musb *musb)
{
struct device *dev = musb->controller;
@@ -121,6 +220,8 @@ static int mpfs_musb_init(struct musb *musb)
return PTR_ERR(musb->xceiv);
}
+ timer_setup(&musb->dev_timer, otg_timer, 0);
+
musb->dyn_fifo = true;
musb->isr = mpfs_musb_interrupt;
@@ -129,14 +230,25 @@ static int mpfs_musb_init(struct musb *musb)
return 0;
}
+static int mpfs_musb_exit(struct musb *musb)
+{
+ del_timer_sync(&musb->dev_timer);
+
+ return 0;
+}
+
static const struct musb_platform_ops mpfs_ops = {
.quirks = MUSB_DMA_INVENTRA,
.init = mpfs_musb_init,
+ .exit = mpfs_musb_exit,
.fifo_mode = 2,
#ifdef CONFIG_USB_INVENTRA_DMA
.dma_init = musbhs_dma_controller_create,
.dma_exit = musbhs_dma_controller_destroy,
#endif
+#ifndef CONFIG_USB_MUSB_HOST
+ .try_idle = mpfs_musb_try_idle,
+#endif
.set_vbus = mpfs_musb_set_vbus
};
diff --git a/drivers/usb/phy/phy-gpio-vbus-usb.c b/drivers/usb/phy/phy-gpio-vbus-usb.c
index 817c242a76ca..5428b2b67de1 100644
--- a/drivers/usb/phy/phy-gpio-vbus-usb.c
+++ b/drivers/usb/phy/phy-gpio-vbus-usb.c
@@ -374,6 +374,7 @@ static const struct of_device_id gpio_vbus_of_match[] = {
},
{},
};
+MODULE_DEVICE_TABLE(of, gpio_vbus_of_match);
static struct platform_driver gpio_vbus_driver = {
.driver = {
diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c
index 920a32cd094d..cc4156c1b148 100644
--- a/drivers/usb/phy/phy-mxs-usb.c
+++ b/drivers/usb/phy/phy-mxs-usb.c
@@ -18,6 +18,7 @@
#include <linux/regmap.h>
#include <linux/mfd/syscon.h>
#include <linux/iopoll.h>
+#include <linux/regulator/consumer.h>
#define DRIVER_NAME "mxs_phy"
@@ -70,6 +71,9 @@
#define BM_USBPHY_PLL_EN_USB_CLKS BIT(6)
/* Anatop Registers */
+#define ANADIG_REG_1P1_SET 0x114
+#define ANADIG_REG_1P1_CLR 0x118
+
#define ANADIG_ANA_MISC0 0x150
#define ANADIG_ANA_MISC0_SET 0x154
#define ANADIG_ANA_MISC0_CLR 0x158
@@ -117,6 +121,14 @@
#define BM_ANADIG_USB2_MISC_RX_VPIN_FS BIT(29)
#define BM_ANADIG_USB2_MISC_RX_VMIN_FS BIT(28)
+/* System Integration Module (SIM) Registers */
+#define SIM_GPR1 0x30
+
+#define USB_PHY_VLLS_WAKEUP_EN BIT(0)
+
+#define BM_ANADIG_REG_1P1_ENABLE_WEAK_LINREG BIT(18)
+#define BM_ANADIG_REG_1P1_TRACK_VDD_SOC_CAP BIT(19)
+
#define to_mxs_phy(p) container_of((p), struct mxs_phy, phy)
/* Do disconnection between PHY and controller without vbus */
@@ -149,6 +161,15 @@
#define MXS_PHY_TX_D_CAL_MIN 79
#define MXS_PHY_TX_D_CAL_MAX 119
+/*
+ * At imx6q/6sl/6sx, the PHY2's clock is controlled by hardware directly,
+ * eg, according to PHY's suspend status. In these PHYs, we only need to
+ * open the clock at the initialization and close it at its shutdown routine.
+ * These PHYs can send resume signal without software interfere if not
+ * gate clock.
+ */
+#define MXS_PHY_HARDWARE_CONTROL_PHY2_CLK BIT(4)
+
struct mxs_phy_data {
unsigned int flags;
};
@@ -160,12 +181,14 @@ static const struct mxs_phy_data imx23_phy_data = {
static const struct mxs_phy_data imx6q_phy_data = {
.flags = MXS_PHY_SENDING_SOF_TOO_FAST |
MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS |
- MXS_PHY_NEED_IP_FIX,
+ MXS_PHY_NEED_IP_FIX |
+ MXS_PHY_HARDWARE_CONTROL_PHY2_CLK,
};
static const struct mxs_phy_data imx6sl_phy_data = {
.flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS |
- MXS_PHY_NEED_IP_FIX,
+ MXS_PHY_NEED_IP_FIX |
+ MXS_PHY_HARDWARE_CONTROL_PHY2_CLK,
};
static const struct mxs_phy_data vf610_phy_data = {
@@ -174,11 +197,13 @@ static const struct mxs_phy_data vf610_phy_data = {
};
static const struct mxs_phy_data imx6sx_phy_data = {
- .flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS,
+ .flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS |
+ MXS_PHY_HARDWARE_CONTROL_PHY2_CLK,
};
static const struct mxs_phy_data imx6ul_phy_data = {
- .flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS,
+ .flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS |
+ MXS_PHY_HARDWARE_CONTROL_PHY2_CLK,
};
static const struct mxs_phy_data imx7ulp_phy_data = {
@@ -201,9 +226,11 @@ struct mxs_phy {
struct clk *clk;
const struct mxs_phy_data *data;
struct regmap *regmap_anatop;
+ struct regmap *regmap_sim;
int port_id;
u32 tx_reg_set;
u32 tx_reg_mask;
+ struct regulator *phy_3p0;
};
static inline bool is_imx6q_phy(struct mxs_phy *mxs_phy)
@@ -221,6 +248,11 @@ static inline bool is_imx7ulp_phy(struct mxs_phy *mxs_phy)
return mxs_phy->data == &imx7ulp_phy_data;
}
+static inline bool is_imx6ul_phy(struct mxs_phy *mxs_phy)
+{
+ return mxs_phy->data == &imx6ul_phy_data;
+}
+
/*
* PHY needs some 32K cycles to switch from 32K clock to
* bus (such as AHB/AXI, etc) clock.
@@ -288,6 +320,16 @@ static int mxs_phy_hw_init(struct mxs_phy *mxs_phy)
if (ret)
goto disable_pll;
+ if (mxs_phy->phy_3p0) {
+ ret = regulator_enable(mxs_phy->phy_3p0);
+ if (ret) {
+ dev_err(mxs_phy->phy.dev,
+ "Failed to enable 3p0 regulator, ret=%d\n",
+ ret);
+ return ret;
+ }
+ }
+
/* Power up the PHY */
writel(0, base + HW_USBPHY_PWD);
@@ -448,6 +490,9 @@ static void mxs_phy_shutdown(struct usb_phy *phy)
if (is_imx7ulp_phy(mxs_phy))
mxs_phy_pll_enable(phy->io_priv, false);
+ if (mxs_phy->phy_3p0)
+ regulator_disable(mxs_phy->phy_3p0);
+
clk_disable_unprepare(mxs_phy->clk);
}
@@ -503,12 +548,19 @@ static int mxs_phy_suspend(struct usb_phy *x, int suspend)
}
writel(BM_USBPHY_CTRL_CLKGATE,
x->io_priv + HW_USBPHY_CTRL_SET);
- clk_disable_unprepare(mxs_phy->clk);
+ if (!(mxs_phy->port_id == 1 &&
+ (mxs_phy->data->flags &
+ MXS_PHY_HARDWARE_CONTROL_PHY2_CLK)))
+ clk_disable_unprepare(mxs_phy->clk);
} else {
mxs_phy_clock_switch_delay();
- ret = clk_prepare_enable(mxs_phy->clk);
- if (ret)
- return ret;
+ if (!(mxs_phy->port_id == 1 &&
+ (mxs_phy->data->flags &
+ MXS_PHY_HARDWARE_CONTROL_PHY2_CLK))) {
+ ret = clk_prepare_enable(mxs_phy->clk);
+ if (ret)
+ return ret;
+ }
writel(BM_USBPHY_CTRL_CLKGATE,
x->io_priv + HW_USBPHY_CTRL_CLR);
writel(0, x->io_priv + HW_USBPHY_PWD);
@@ -738,6 +790,17 @@ static int mxs_phy_probe(struct platform_device *pdev)
}
}
+ /* Currently, only imx7ulp has SIM module */
+ if (of_get_property(np, "nxp,sim", NULL)) {
+ mxs_phy->regmap_sim = syscon_regmap_lookup_by_phandle
+ (np, "nxp,sim");
+ if (IS_ERR(mxs_phy->regmap_sim)) {
+ dev_dbg(&pdev->dev,
+ "failed to find regmap for sim\n");
+ return PTR_ERR(mxs_phy->regmap_sim);
+ }
+ }
+
/* Precompute which bits of the TX register are to be updated, if any */
if (!of_property_read_u32(np, "fsl,tx-cal-45-dn-ohms", &val) &&
val >= MXS_PHY_TX_CAL45_MIN && val <= MXS_PHY_TX_CAL45_MAX) {
@@ -789,6 +852,17 @@ static int mxs_phy_probe(struct platform_device *pdev)
mxs_phy->clk = clk;
mxs_phy->data = of_device_get_match_data(&pdev->dev);
+ mxs_phy->phy_3p0 = devm_regulator_get(&pdev->dev, "phy-3p0");
+ if (PTR_ERR(mxs_phy->phy_3p0) == -ENODEV)
+ /* not exist */
+ mxs_phy->phy_3p0 = NULL;
+ else if (IS_ERR(mxs_phy->phy_3p0))
+ return dev_err_probe(&pdev->dev, PTR_ERR(mxs_phy->phy_3p0),
+ "Getting regulator error\n");
+
+ if (mxs_phy->phy_3p0)
+ regulator_set_voltage(mxs_phy->phy_3p0, 3200000, 3200000);
+
platform_set_drvdata(pdev, mxs_phy);
device_set_wakeup_capable(&pdev->dev, true);
@@ -804,28 +878,58 @@ static void mxs_phy_remove(struct platform_device *pdev)
}
#ifdef CONFIG_PM_SLEEP
+static void mxs_phy_wakeup_enable(struct mxs_phy *mxs_phy, bool on)
+{
+ u32 mask = USB_PHY_VLLS_WAKEUP_EN;
+
+ /* If the SoCs don't have SIM, quit */
+ if (!mxs_phy->regmap_sim)
+ return;
+
+ if (on) {
+ regmap_update_bits(mxs_phy->regmap_sim, SIM_GPR1, mask, mask);
+ udelay(500);
+ } else {
+ regmap_update_bits(mxs_phy->regmap_sim, SIM_GPR1, mask, 0);
+ }
+}
+
static void mxs_phy_enable_ldo_in_suspend(struct mxs_phy *mxs_phy, bool on)
{
- unsigned int reg = on ? ANADIG_ANA_MISC0_SET : ANADIG_ANA_MISC0_CLR;
+ unsigned int reg;
+ u32 value;
/* If the SoCs don't have anatop, quit */
if (!mxs_phy->regmap_anatop)
return;
- if (is_imx6q_phy(mxs_phy))
+ if (is_imx6q_phy(mxs_phy)) {
+ reg = on ? ANADIG_ANA_MISC0_SET : ANADIG_ANA_MISC0_CLR;
regmap_write(mxs_phy->regmap_anatop, reg,
BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG);
- else if (is_imx6sl_phy(mxs_phy))
+ } else if (is_imx6sl_phy(mxs_phy)) {
+ reg = on ? ANADIG_ANA_MISC0_SET : ANADIG_ANA_MISC0_CLR;
regmap_write(mxs_phy->regmap_anatop,
reg, BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG_SL);
+ } else if (is_imx6ul_phy(mxs_phy)) {
+ reg = on ? ANADIG_REG_1P1_SET : ANADIG_REG_1P1_CLR;
+ value = BM_ANADIG_REG_1P1_ENABLE_WEAK_LINREG |
+ BM_ANADIG_REG_1P1_TRACK_VDD_SOC_CAP;
+ if (mxs_phy_get_vbus_status(mxs_phy) && on)
+ regmap_write(mxs_phy->regmap_anatop, reg, value);
+ else if (!on)
+ regmap_write(mxs_phy->regmap_anatop, reg, value);
+ }
}
static int mxs_phy_system_suspend(struct device *dev)
{
struct mxs_phy *mxs_phy = dev_get_drvdata(dev);
- if (device_may_wakeup(dev))
+ if (device_may_wakeup(dev)) {
mxs_phy_enable_ldo_in_suspend(mxs_phy, true);
+ mxs_phy_wakeup_enable(mxs_phy, true);
+ }
return 0;
}
@@ -834,8 +938,10 @@ static int mxs_phy_system_resume(struct device *dev)
{
struct mxs_phy *mxs_phy = dev_get_drvdata(dev);
- if (device_may_wakeup(dev))
+ if (device_may_wakeup(dev)) {
mxs_phy_enable_ldo_in_suspend(mxs_phy, false);
+ mxs_phy_wakeup_enable(mxs_phy, false);
+ }
return 0;
}
diff --git a/drivers/usb/roles/class.c b/drivers/usb/roles/class.c
index d7aa913ceb8a..7aca1ef7f44c 100644
--- a/drivers/usb/roles/class.c
+++ b/drivers/usb/roles/class.c
@@ -11,6 +11,7 @@
#include <linux/usb/role.h>
#include <linux/property.h>
#include <linux/device.h>
+#include <linux/lockdep.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/slab.h>
@@ -33,6 +34,8 @@ struct usb_role_switch {
usb_role_switch_set_t set;
usb_role_switch_get_t get;
bool allow_userspace_control;
+
+ struct lock_class_key key;
};
#define to_role_switch(d) container_of(d, struct usb_role_switch, dev)
@@ -396,6 +399,9 @@ usb_role_switch_register(struct device *parent,
sw->registered = true;
+ lockdep_register_key(&sw->key);
+ lockdep_set_class(&sw->lock, &sw->key);
+
/* TODO: Symlinks for the host port and the device controller. */
return sw;
@@ -412,6 +418,9 @@ void usb_role_switch_unregister(struct usb_role_switch *sw)
{
if (IS_ERR_OR_NULL(sw))
return;
+
+ lockdep_unregister_key(&sw->key);
+
sw->registered = false;
if (dev_fwnode(&sw->dev))
component_del(&sw->dev, &connector_ops);
diff --git a/drivers/usb/typec/anx7411.c b/drivers/usb/typec/anx7411.c
index 5a5bf3532ad7..06d88e9caeaf 100644
--- a/drivers/usb/typec/anx7411.c
+++ b/drivers/usb/typec/anx7411.c
@@ -6,6 +6,7 @@
* Copyright(c) 2022, Analogix Semiconductor. All rights reserved.
*
*/
+#include <linux/bitfield.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
@@ -884,8 +885,8 @@ static void anx7411_chip_standby(struct anx7411_data *ctx)
OCM_RESET);
ret |= anx7411_reg_write(ctx->tcpc_client, ANALOG_CTRL_10, 0x80);
/* Set TCPC to RD and DRP enable */
- cc1 = TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC1_SHIFT;
- cc2 = TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC2_SHIFT;
+ cc1 = FIELD_PREP(TCPC_ROLE_CTRL_CC1, TCPC_ROLE_CTRL_CC_RD);
+ cc2 = FIELD_PREP(TCPC_ROLE_CTRL_CC2, TCPC_ROLE_CTRL_CC_RD);
ret |= anx7411_reg_write(ctx->tcpc_client, TCPC_ROLE_CTRL,
TCPC_ROLE_CTRL_DRP | cc1 | cc2);
@@ -1576,6 +1577,7 @@ static const struct of_device_id anx_match_table[] = {
{.compatible = "analogix,anx7411",},
{},
};
+MODULE_DEVICE_TABLE(of, anx_match_table);
static struct i2c_driver anx7411_driver = {
.driver = {
diff --git a/drivers/usb/typec/tcpm/maxim_contaminant.c b/drivers/usb/typec/tcpm/maxim_contaminant.c
index f8504a90da26..22163d8f9eb0 100644
--- a/drivers/usb/typec/tcpm/maxim_contaminant.c
+++ b/drivers/usb/typec/tcpm/maxim_contaminant.c
@@ -5,6 +5,7 @@
* USB-C module to reduce wakeups due to contaminants.
*/
+#include <linux/bitfield.h>
#include <linux/device.h>
#include <linux/irqreturn.h>
#include <linux/module.h>
@@ -45,14 +46,9 @@ enum fladc_select {
#define READ1_SLEEP_MS 10
#define READ2_SLEEP_MS 5
-#define STATUS_CHECK(reg, mask, val) (((reg) & (mask)) == (val))
-
#define IS_CC_OPEN(cc_status) \
- (STATUS_CHECK((cc_status), TCPC_CC_STATUS_CC1_MASK << TCPC_CC_STATUS_CC1_SHIFT, \
- TCPC_CC_STATE_SRC_OPEN) && STATUS_CHECK((cc_status), \
- TCPC_CC_STATUS_CC2_MASK << \
- TCPC_CC_STATUS_CC2_SHIFT, \
- TCPC_CC_STATE_SRC_OPEN))
+ (FIELD_GET(TCPC_CC_STATUS_CC1, cc_status) == TCPC_CC_STATE_SRC_OPEN \
+ && FIELD_GET(TCPC_CC_STATUS_CC2, cc_status) == TCPC_CC_STATE_SRC_OPEN)
static int max_contaminant_adc_to_mv(struct max_tcpci_chip *chip, enum fladc_select channel,
bool ua_src, u8 fladc)
@@ -80,8 +76,8 @@ static int max_contaminant_read_adc_mv(struct max_tcpci_chip *chip, enum fladc_s
int ret;
/* Channel & scale select */
- ret = regmap_update_bits(regmap, TCPC_VENDOR_ADC_CTRL1, ADCINSEL_MASK,
- channel << ADC_CHANNEL_OFFSET);
+ ret = regmap_update_bits(regmap, TCPC_VENDOR_ADC_CTRL1, ADCINSEL,
+ FIELD_PREP(ADCINSEL, channel));
if (ret < 0)
return ret;
@@ -100,7 +96,8 @@ static int max_contaminant_read_adc_mv(struct max_tcpci_chip *chip, enum fladc_s
if (ret < 0)
return ret;
- ret = regmap_update_bits(regmap, TCPC_VENDOR_ADC_CTRL1, ADCINSEL_MASK, 0);
+ ret = regmap_update_bits(regmap, TCPC_VENDOR_ADC_CTRL1, ADCINSEL,
+ FIELD_PREP(ADCINSEL, 0));
if (ret < 0)
return ret;
@@ -120,13 +117,14 @@ static int max_contaminant_read_resistance_kohm(struct max_tcpci_chip *chip,
if (channel == CC1_SCALE1 || channel == CC2_SCALE1 || channel == CC1_SCALE2 ||
channel == CC2_SCALE2) {
/* Enable 1uA current source */
- ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, CCLPMODESEL_MASK,
- ULTRA_LOW_POWER_MODE);
+ ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, CCLPMODESEL,
+ FIELD_PREP(CCLPMODESEL, ULTRA_LOW_POWER_MODE));
if (ret < 0)
return ret;
/* Enable 1uA current source */
- ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, CCRPCTRL_MASK, UA_1_SRC);
+ ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, CCRPCTRL,
+ FIELD_PREP(CCRPCTRL, UA_1_SRC));
if (ret < 0)
return ret;
@@ -180,7 +178,8 @@ static int max_contaminant_read_comparators(struct max_tcpci_chip *chip, u8 *ven
int ret;
/* Enable 80uA source */
- ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, CCRPCTRL_MASK, UA_80_SRC);
+ ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, CCRPCTRL,
+ FIELD_PREP(CCRPCTRL, UA_80_SRC));
if (ret < 0)
return ret;
@@ -213,7 +212,8 @@ static int max_contaminant_read_comparators(struct max_tcpci_chip *chip, u8 *ven
if (ret < 0)
return ret;
- ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, CCRPCTRL_MASK, 0);
+ ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, CCRPCTRL,
+ FIELD_PREP(CCRPCTRL, 0));
if (ret < 0)
return ret;
@@ -284,10 +284,11 @@ static int max_contaminant_enable_dry_detection(struct max_tcpci_chip *chip)
u8 temp;
int ret;
- ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL3, CCWTRDEB_MASK | CCWTRSEL_MASK
- | WTRCYCLE_MASK, CCWTRDEB_1MS << CCWTRDEB_SHIFT |
- CCWTRSEL_1V << CCWTRSEL_SHIFT | WTRCYCLE_4_8_S <<
- WTRCYCLE_SHIFT);
+ ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL3,
+ CCWTRDEB | CCWTRSEL | WTRCYCLE,
+ FIELD_PREP(CCWTRDEB, CCWTRDEB_1MS)
+ | FIELD_PREP(CCWTRSEL, CCWTRSEL_1V)
+ | FIELD_PREP(WTRCYCLE, WTRCYCLE_4_8_S));
if (ret < 0)
return ret;
@@ -302,8 +303,9 @@ static int max_contaminant_enable_dry_detection(struct max_tcpci_chip *chip)
if (ret < 0)
return ret;
- ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, CCLPMODESEL_MASK,
- ULTRA_LOW_POWER_MODE);
+ ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, CCLPMODESEL,
+ FIELD_PREP(CCLPMODESEL,
+ ULTRA_LOW_POWER_MODE));
if (ret < 0)
return ret;
ret = max_tcpci_read8(chip, TCPC_VENDOR_CC_CTRL2, &temp);
@@ -322,11 +324,14 @@ static int max_contaminant_enable_dry_detection(struct max_tcpci_chip *chip)
return 0;
}
-bool max_contaminant_is_contaminant(struct max_tcpci_chip *chip, bool disconnect_while_debounce)
+bool max_contaminant_is_contaminant(struct max_tcpci_chip *chip, bool disconnect_while_debounce,
+ bool *cc_handled)
{
u8 cc_status, pwr_cntl;
int ret;
+ *cc_handled = true;
+
ret = max_tcpci_read8(chip, TCPC_CC_STATUS, &cc_status);
if (ret < 0)
return false;
@@ -368,9 +373,8 @@ bool max_contaminant_is_contaminant(struct max_tcpci_chip *chip, bool disconnect
return true;
}
}
- return false;
} else if (chip->contaminant_state == DETECTED) {
- if (STATUS_CHECK(cc_status, TCPC_CC_STATUS_TOGGLING, 0)) {
+ if (!(cc_status & TCPC_CC_STATUS_TOGGLING)) {
chip->contaminant_state = max_contaminant_detect_contaminant(chip);
if (chip->contaminant_state == DETECTED) {
max_contaminant_enable_dry_detection(chip);
@@ -379,6 +383,7 @@ bool max_contaminant_is_contaminant(struct max_tcpci_chip *chip, bool disconnect
}
}
+ *cc_handled = false;
return false;
}
diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c
index 3e3dcb983dde..a2651a2a7f2e 100644
--- a/drivers/usb/typec/tcpm/tcpci.c
+++ b/drivers/usb/typec/tcpm/tcpci.c
@@ -5,6 +5,7 @@
* USB Type-C Port Controller Interface.
*/
+#include <linux/bitfield.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -103,45 +104,45 @@ static int tcpci_set_cc(struct tcpc_dev *tcpc, enum typec_cc_status cc)
switch (cc) {
case TYPEC_CC_RA:
- reg = (TCPC_ROLE_CTRL_CC_RA << TCPC_ROLE_CTRL_CC1_SHIFT) |
- (TCPC_ROLE_CTRL_CC_RA << TCPC_ROLE_CTRL_CC2_SHIFT);
+ reg = (FIELD_PREP(TCPC_ROLE_CTRL_CC1, TCPC_ROLE_CTRL_CC_RA)
+ | FIELD_PREP(TCPC_ROLE_CTRL_CC2, TCPC_ROLE_CTRL_CC_RA));
break;
case TYPEC_CC_RD:
- reg = (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC1_SHIFT) |
- (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC2_SHIFT);
+ reg = (FIELD_PREP(TCPC_ROLE_CTRL_CC1, TCPC_ROLE_CTRL_CC_RD)
+ | FIELD_PREP(TCPC_ROLE_CTRL_CC2, TCPC_ROLE_CTRL_CC_RD));
break;
case TYPEC_CC_RP_DEF:
- reg = (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT) |
- (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT) |
- (TCPC_ROLE_CTRL_RP_VAL_DEF <<
- TCPC_ROLE_CTRL_RP_VAL_SHIFT);
+ reg = (FIELD_PREP(TCPC_ROLE_CTRL_CC1, TCPC_ROLE_CTRL_CC_RP)
+ | FIELD_PREP(TCPC_ROLE_CTRL_CC2, TCPC_ROLE_CTRL_CC_RP)
+ | FIELD_PREP(TCPC_ROLE_CTRL_RP_VAL,
+ TCPC_ROLE_CTRL_RP_VAL_DEF));
break;
case TYPEC_CC_RP_1_5:
- reg = (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT) |
- (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT) |
- (TCPC_ROLE_CTRL_RP_VAL_1_5 <<
- TCPC_ROLE_CTRL_RP_VAL_SHIFT);
+ reg = (FIELD_PREP(TCPC_ROLE_CTRL_CC1, TCPC_ROLE_CTRL_CC_RP)
+ | FIELD_PREP(TCPC_ROLE_CTRL_CC2, TCPC_ROLE_CTRL_CC_RP)
+ | FIELD_PREP(TCPC_ROLE_CTRL_RP_VAL,
+ TCPC_ROLE_CTRL_RP_VAL_1_5));
break;
case TYPEC_CC_RP_3_0:
- reg = (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT) |
- (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT) |
- (TCPC_ROLE_CTRL_RP_VAL_3_0 <<
- TCPC_ROLE_CTRL_RP_VAL_SHIFT);
+ reg = (FIELD_PREP(TCPC_ROLE_CTRL_CC1, TCPC_ROLE_CTRL_CC_RP)
+ | FIELD_PREP(TCPC_ROLE_CTRL_CC2, TCPC_ROLE_CTRL_CC_RP)
+ | FIELD_PREP(TCPC_ROLE_CTRL_RP_VAL,
+ TCPC_ROLE_CTRL_RP_VAL_3_0));
break;
case TYPEC_CC_OPEN:
default:
- reg = (TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC1_SHIFT) |
- (TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC2_SHIFT);
+ reg = (FIELD_PREP(TCPC_ROLE_CTRL_CC1, TCPC_ROLE_CTRL_CC_OPEN)
+ | FIELD_PREP(TCPC_ROLE_CTRL_CC2, TCPC_ROLE_CTRL_CC_OPEN));
break;
}
if (vconn_pres) {
if (polarity == TYPEC_POLARITY_CC2) {
- reg &= ~(TCPC_ROLE_CTRL_CC1_MASK << TCPC_ROLE_CTRL_CC1_SHIFT);
- reg |= (TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC1_SHIFT);
+ reg &= ~TCPC_ROLE_CTRL_CC1;
+ reg |= FIELD_PREP(TCPC_ROLE_CTRL_CC1, TCPC_ROLE_CTRL_CC_OPEN);
} else {
- reg &= ~(TCPC_ROLE_CTRL_CC2_MASK << TCPC_ROLE_CTRL_CC2_SHIFT);
- reg |= (TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC2_SHIFT);
+ reg &= ~TCPC_ROLE_CTRL_CC2;
+ reg |= FIELD_PREP(TCPC_ROLE_CTRL_CC2, TCPC_ROLE_CTRL_CC_OPEN);
}
}
@@ -167,15 +168,11 @@ static int tcpci_apply_rc(struct tcpc_dev *tcpc, enum typec_cc_status cc,
* 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))
+ if (FIELD_GET(TCPC_ROLE_CTRL_CC2, reg) != FIELD_GET(TCPC_ROLE_CTRL_CC1, reg))
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_CC2 : TCPC_ROLE_CTRL_CC1,
TCPC_ROLE_CTRL_CC_OPEN);
}
@@ -200,25 +197,25 @@ static int tcpci_start_toggling(struct tcpc_dev *tcpc,
switch (cc) {
default:
case TYPEC_CC_RP_DEF:
- reg |= (TCPC_ROLE_CTRL_RP_VAL_DEF <<
- TCPC_ROLE_CTRL_RP_VAL_SHIFT);
+ reg |= FIELD_PREP(TCPC_ROLE_CTRL_RP_VAL,
+ TCPC_ROLE_CTRL_RP_VAL_DEF);
break;
case TYPEC_CC_RP_1_5:
- reg |= (TCPC_ROLE_CTRL_RP_VAL_1_5 <<
- TCPC_ROLE_CTRL_RP_VAL_SHIFT);
+ reg |= FIELD_PREP(TCPC_ROLE_CTRL_RP_VAL,
+ TCPC_ROLE_CTRL_RP_VAL_1_5);
break;
case TYPEC_CC_RP_3_0:
- reg |= (TCPC_ROLE_CTRL_RP_VAL_3_0 <<
- TCPC_ROLE_CTRL_RP_VAL_SHIFT);
+ reg |= FIELD_PREP(TCPC_ROLE_CTRL_RP_VAL,
+ TCPC_ROLE_CTRL_RP_VAL_3_0);
break;
}
if (cc == TYPEC_CC_RD)
- reg |= (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC1_SHIFT) |
- (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC2_SHIFT);
+ reg |= (FIELD_PREP(TCPC_ROLE_CTRL_CC1, TCPC_ROLE_CTRL_CC_RD)
+ | FIELD_PREP(TCPC_ROLE_CTRL_CC2, TCPC_ROLE_CTRL_CC_RD));
else
- reg |= (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT) |
- (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT);
+ reg |= (FIELD_PREP(TCPC_ROLE_CTRL_CC1, TCPC_ROLE_CTRL_CC_RP)
+ | FIELD_PREP(TCPC_ROLE_CTRL_CC2, TCPC_ROLE_CTRL_CC_RP));
ret = regmap_write(tcpci->regmap, TCPC_ROLE_CTRL, reg);
if (ret < 0)
return ret;
@@ -241,12 +238,10 @@ static int tcpci_get_cc(struct tcpc_dev *tcpc,
if (ret < 0)
return ret;
- *cc1 = tcpci_to_typec_cc((reg >> TCPC_CC_STATUS_CC1_SHIFT) &
- TCPC_CC_STATUS_CC1_MASK,
+ *cc1 = tcpci_to_typec_cc(FIELD_GET(TCPC_CC_STATUS_CC1, reg),
reg & TCPC_CC_STATUS_TERM ||
tcpc_presenting_rd(role_control, CC1));
- *cc2 = tcpci_to_typec_cc((reg >> TCPC_CC_STATUS_CC2_SHIFT) &
- TCPC_CC_STATUS_CC2_MASK,
+ *cc2 = tcpci_to_typec_cc(FIELD_GET(TCPC_CC_STATUS_CC2, reg),
reg & TCPC_CC_STATUS_TERM ||
tcpc_presenting_rd(role_control, CC2));
@@ -282,28 +277,28 @@ static int tcpci_set_polarity(struct tcpc_dev *tcpc,
reg = reg & ~TCPC_ROLE_CTRL_DRP;
if (polarity == TYPEC_POLARITY_CC2) {
- reg &= ~(TCPC_ROLE_CTRL_CC2_MASK << TCPC_ROLE_CTRL_CC2_SHIFT);
+ reg &= ~TCPC_ROLE_CTRL_CC2;
/* Local port is source */
if (cc2 == TYPEC_CC_RD)
/* Role control would have the Rp setting when DRP was enabled */
- reg |= TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT;
+ reg |= FIELD_PREP(TCPC_ROLE_CTRL_CC2, TCPC_ROLE_CTRL_CC_RP);
else
- reg |= TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC2_SHIFT;
+ reg |= FIELD_PREP(TCPC_ROLE_CTRL_CC2, TCPC_ROLE_CTRL_CC_RD);
} else {
- reg &= ~(TCPC_ROLE_CTRL_CC1_MASK << TCPC_ROLE_CTRL_CC1_SHIFT);
+ reg &= ~TCPC_ROLE_CTRL_CC1;
/* Local port is source */
if (cc1 == TYPEC_CC_RD)
/* Role control would have the Rp setting when DRP was enabled */
- reg |= TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT;
+ reg |= FIELD_PREP(TCPC_ROLE_CTRL_CC1, TCPC_ROLE_CTRL_CC_RP);
else
- reg |= TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC1_SHIFT;
+ reg |= FIELD_PREP(TCPC_ROLE_CTRL_CC1, TCPC_ROLE_CTRL_CC_RD);
}
}
if (polarity == TYPEC_POLARITY_CC2)
- reg |= TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC1_SHIFT;
+ reg |= FIELD_PREP(TCPC_ROLE_CTRL_CC1, TCPC_ROLE_CTRL_CC_OPEN);
else
- reg |= TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC2_SHIFT;
+ reg |= FIELD_PREP(TCPC_ROLE_CTRL_CC2, TCPC_ROLE_CTRL_CC_OPEN);
ret = regmap_write(tcpci->regmap, TCPC_ROLE_CTRL, reg);
if (ret < 0)
return ret;
@@ -461,7 +456,7 @@ static int tcpci_set_roles(struct tcpc_dev *tcpc, bool attached,
unsigned int reg;
int ret;
- reg = PD_REV20 << TCPC_MSG_HDR_INFO_REV_SHIFT;
+ reg = FIELD_PREP(TCPC_MSG_HDR_INFO_REV, PD_REV20);
if (role == TYPEC_SOURCE)
reg |= TCPC_MSG_HDR_INFO_PWR_ROLE;
if (data == TYPEC_HOST)
@@ -612,8 +607,11 @@ static int tcpci_pd_transmit(struct tcpc_dev *tcpc, enum tcpm_transmit_type type
}
/* nRetryCount is 3 in PD2.0 spec where 2 in PD3.0 spec */
- reg = ((negotiated_rev > PD_REV20 ? PD_RETRY_COUNT_3_0_OR_HIGHER : PD_RETRY_COUNT_DEFAULT)
- << TCPC_TRANSMIT_RETRY_SHIFT) | (type << TCPC_TRANSMIT_TYPE_SHIFT);
+ reg = FIELD_PREP(TCPC_TRANSMIT_RETRY,
+ (negotiated_rev > PD_REV20
+ ? PD_RETRY_COUNT_3_0_OR_HIGHER
+ : PD_RETRY_COUNT_DEFAULT));
+ reg |= FIELD_PREP(TCPC_TRANSMIT_TYPE, type);
ret = regmap_write(tcpci->regmap, TCPC_TRANSMIT, reg);
if (ret < 0)
return ret;
diff --git a/drivers/usb/typec/tcpm/tcpci_maxim.h b/drivers/usb/typec/tcpm/tcpci_maxim.h
index 78ff3b73ee7e..76270d5c2838 100644
--- a/drivers/usb/typec/tcpm/tcpci_maxim.h
+++ b/drivers/usb/typec/tcpm/tcpci_maxim.h
@@ -20,28 +20,24 @@
#define SBUOVPDIS BIT(7)
#define CCOVPDIS BIT(6)
#define SBURPCTRL BIT(5)
-#define CCLPMODESEL_MASK GENMASK(4, 3)
-#define ULTRA_LOW_POWER_MODE BIT(3)
-#define CCRPCTRL_MASK GENMASK(2, 0)
+#define CCLPMODESEL GENMASK(4, 3)
+#define ULTRA_LOW_POWER_MODE 1
+#define CCRPCTRL GENMASK(2, 0)
#define UA_1_SRC 1
#define UA_80_SRC 3
#define TCPC_VENDOR_CC_CTRL3 0x8e
-#define CCWTRDEB_MASK GENMASK(7, 6)
-#define CCWTRDEB_SHIFT 6
+#define CCWTRDEB GENMASK(7, 6)
#define CCWTRDEB_1MS 1
-#define CCWTRSEL_MASK GENMASK(5, 3)
-#define CCWTRSEL_SHIFT 3
+#define CCWTRSEL GENMASK(5, 3)
#define CCWTRSEL_1V 0x4
#define CCLADDERDIS BIT(2)
-#define WTRCYCLE_MASK BIT(0)
-#define WTRCYCLE_SHIFT 0
+#define WTRCYCLE GENMASK(0, 0)
#define WTRCYCLE_2_4_S 0
#define WTRCYCLE_4_8_S 1
#define TCPC_VENDOR_ADC_CTRL1 0x91
-#define ADCINSEL_MASK GENMASK(7, 5)
-#define ADC_CHANNEL_OFFSET 5
+#define ADCINSEL GENMASK(7, 5)
#define ADCEN BIT(0)
enum contamiant_state {
@@ -85,6 +81,20 @@ static inline int max_tcpci_write8(struct max_tcpci_chip *chip, unsigned int reg
return regmap_raw_write(chip->data.regmap, reg, &val, sizeof(u8));
}
-bool max_contaminant_is_contaminant(struct max_tcpci_chip *chip, bool disconnect_while_debounce);
+/**
+ * max_contaminant_is_contaminant - Test if CC was toggled due to contaminant
+ *
+ * @chip: Handle to a struct max_tcpci_chip
+ * @disconnect_while_debounce: Whether the disconnect was detected when CC
+ * pins were debouncing
+ * @cc_handled: Returns whether or not update to CC status was handled here
+ *
+ * Determine if a contaminant was detected.
+ *
+ * Returns: true if a contaminant was detected, false otherwise. cc_handled
+ * is updated to reflect whether or not further CC handling is required.
+ */
+bool max_contaminant_is_contaminant(struct max_tcpci_chip *chip, bool disconnect_while_debounce,
+ bool *cc_handled);
#endif // TCPCI_MAXIM_H_
diff --git a/drivers/usb/typec/tcpm/tcpci_maxim_core.c b/drivers/usb/typec/tcpm/tcpci_maxim_core.c
index 760e2f92b958..fd1b80593367 100644
--- a/drivers/usb/typec/tcpm/tcpci_maxim_core.c
+++ b/drivers/usb/typec/tcpm/tcpci_maxim_core.c
@@ -97,11 +97,13 @@ static void max_tcpci_init_regs(struct max_tcpci_chip *chip)
if (ret < 0)
return;
- alert_mask = TCPC_ALERT_TX_SUCCESS | TCPC_ALERT_TX_DISCARDED | TCPC_ALERT_TX_FAILED |
- TCPC_ALERT_RX_HARD_RST | TCPC_ALERT_RX_STATUS | TCPC_ALERT_CC_STATUS |
- TCPC_ALERT_VBUS_DISCNCT | TCPC_ALERT_RX_BUF_OVF | TCPC_ALERT_POWER_STATUS |
- /* Enable Extended alert for detecting Fast Role Swap Signal */
- TCPC_ALERT_EXTND | TCPC_ALERT_EXTENDED_STATUS | TCPC_ALERT_FAULT;
+ alert_mask = (TCPC_ALERT_TX_SUCCESS | TCPC_ALERT_TX_DISCARDED |
+ TCPC_ALERT_TX_FAILED | TCPC_ALERT_RX_HARD_RST |
+ TCPC_ALERT_RX_STATUS | TCPC_ALERT_POWER_STATUS |
+ TCPC_ALERT_CC_STATUS |
+ TCPC_ALERT_EXTND | TCPC_ALERT_EXTENDED_STATUS |
+ TCPC_ALERT_VBUS_DISCNCT | TCPC_ALERT_RX_BUF_OVF |
+ TCPC_ALERT_FAULT);
ret = max_tcpci_write16(chip, TCPC_ALERT_MASK, alert_mask);
if (ret < 0) {
@@ -191,9 +193,8 @@ static void process_rx(struct max_tcpci_chip *chip, u16 status)
* Read complete, clear RX status alert bit.
* Clear overflow as well if set.
*/
- ret = max_tcpci_write16(chip, TCPC_ALERT, status & TCPC_ALERT_RX_BUF_OVF ?
- TCPC_ALERT_RX_STATUS | TCPC_ALERT_RX_BUF_OVF :
- TCPC_ALERT_RX_STATUS);
+ ret = max_tcpci_write16(chip, TCPC_ALERT,
+ TCPC_ALERT_RX_STATUS | (status & TCPC_ALERT_RX_BUF_OVF));
if (ret < 0)
return;
@@ -295,9 +296,8 @@ static irqreturn_t _max_tcpci_irq(struct max_tcpci_chip *chip, u16 status)
* be cleared until we have successfully retrieved message.
*/
if (status & ~TCPC_ALERT_RX_STATUS) {
- mask = status & TCPC_ALERT_RX_BUF_OVF ?
- status & ~(TCPC_ALERT_RX_STATUS | TCPC_ALERT_RX_BUF_OVF) :
- status & ~TCPC_ALERT_RX_STATUS;
+ mask = status & ~(TCPC_ALERT_RX_STATUS
+ | (status & TCPC_ALERT_RX_BUF_OVF));
ret = max_tcpci_write16(chip, TCPC_ALERT, mask);
if (ret < 0) {
dev_err(chip->dev, "ALERT clear failed\n");
@@ -357,12 +357,14 @@ static irqreturn_t _max_tcpci_irq(struct max_tcpci_chip *chip, u16 status)
tcpm_vbus_change(chip->port);
if (status & TCPC_ALERT_CC_STATUS) {
+ bool cc_handled = false;
+
if (chip->contaminant_state == DETECTED || tcpm_port_is_toggling(chip->port)) {
- if (!max_contaminant_is_contaminant(chip, false))
+ if (!max_contaminant_is_contaminant(chip, false, &cc_handled))
tcpm_port_clean(chip->port);
- } else {
- tcpm_cc_change(chip->port);
}
+ if (!cc_handled)
+ tcpm_cc_change(chip->port);
}
if (status & TCPC_ALERT_POWER_STATUS)
@@ -397,7 +399,7 @@ static irqreturn_t max_tcpci_irq(int irq, void *dev_id)
}
while (status) {
irq_return = _max_tcpci_irq(chip, status);
- /* Do not return if the ALERT is already set. */
+ /* Do not return if a (new) ALERT is set (again). */
ret = max_tcpci_read16(chip, TCPC_ALERT, &status);
if (ret < 0)
break;
@@ -455,8 +457,9 @@ static int tcpci_init(struct tcpci *tcpci, struct tcpci_data *data)
static void max_tcpci_check_contaminant(struct tcpci *tcpci, struct tcpci_data *tdata)
{
struct max_tcpci_chip *chip = tdata_to_max_tcpci(tdata);
+ bool cc_handled;
- if (!max_contaminant_is_contaminant(chip, true))
+ if (!max_contaminant_is_contaminant(chip, true, &cc_handled))
tcpm_port_clean(chip->port);
}
@@ -472,6 +475,11 @@ static bool max_tcpci_attempt_vconn_swap_discovery(struct tcpci *tcpci, struct t
return true;
}
+static void max_tcpci_unregister_tcpci_port(void *tcpci)
+{
+ tcpci_unregister_port(tcpci);
+}
+
static int max_tcpci_probe(struct i2c_client *client)
{
int ret;
@@ -484,17 +492,17 @@ static int max_tcpci_probe(struct i2c_client *client)
chip->client = client;
chip->data.regmap = devm_regmap_init_i2c(client, &max_tcpci_regmap_config);
- if (IS_ERR(chip->data.regmap)) {
- dev_err(&client->dev, "Regmap init failed\n");
- return PTR_ERR(chip->data.regmap);
- }
+ if (IS_ERR(chip->data.regmap))
+ return dev_err_probe(&client->dev, PTR_ERR(chip->data.regmap),
+ "Regmap init failed\n");
chip->dev = &client->dev;
i2c_set_clientdata(client, chip);
ret = max_tcpci_read8(chip, TCPC_POWER_STATUS, &power_status);
if (ret < 0)
- return ret;
+ return dev_err_probe(&client->dev, ret,
+ "Failed to read TCPC_POWER_STATUS\n");
/* Chip level tcpci callbacks */
chip->data.set_vbus = max_tcpci_set_vbus;
@@ -511,30 +519,25 @@ static int max_tcpci_probe(struct i2c_client *client)
max_tcpci_init_regs(chip);
chip->tcpci = tcpci_register_port(chip->dev, &chip->data);
- if (IS_ERR(chip->tcpci)) {
- dev_err(&client->dev, "TCPCI port registration failed\n");
- return PTR_ERR(chip->tcpci);
- }
+ if (IS_ERR(chip->tcpci))
+ return dev_err_probe(&client->dev, PTR_ERR(chip->tcpci),
+ "TCPCI port registration failed\n");
+
+ ret = devm_add_action_or_reset(&client->dev,
+ max_tcpci_unregister_tcpci_port,
+ chip->tcpci);
+ if (ret)
+ return ret;
+
chip->port = tcpci_get_tcpm_port(chip->tcpci);
+
ret = max_tcpci_init_alert(chip, client);
if (ret < 0)
- goto unreg_port;
+ return dev_err_probe(&client->dev, ret,
+ "IRQ initialization failed\n");
device_init_wakeup(chip->dev, true);
return 0;
-
-unreg_port:
- tcpci_unregister_port(chip->tcpci);
-
- return ret;
-}
-
-static void max_tcpci_remove(struct i2c_client *client)
-{
- struct max_tcpci_chip *chip = i2c_get_clientdata(client);
-
- if (!IS_ERR_OR_NULL(chip->tcpci))
- tcpci_unregister_port(chip->tcpci);
}
static const struct i2c_device_id max_tcpci_id[] = {
@@ -557,7 +560,6 @@ static struct i2c_driver max_tcpci_i2c_driver = {
.of_match_table = of_match_ptr(max_tcpci_of_match),
},
.probe = max_tcpci_probe,
- .remove = max_tcpci_remove,
.id_table = max_tcpci_id,
};
module_i2c_driver(max_tcpci_i2c_driver);
diff --git a/drivers/usb/typec/tcpm/tcpci_rt1711h.c b/drivers/usb/typec/tcpm/tcpci_rt1711h.c
index 67422d45eb54..64f6dd0dc660 100644
--- a/drivers/usb/typec/tcpm/tcpci_rt1711h.c
+++ b/drivers/usb/typec/tcpm/tcpci_rt1711h.c
@@ -5,6 +5,7 @@
* Richtek RT1711H Type-C Chip Driver
*/
+#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/kernel.h>
#include <linux/mod_devicetable.h>
@@ -195,12 +196,10 @@ static inline int rt1711h_init_cc_params(struct rt1711h_chip *chip, u8 status)
if (ret < 0)
return ret;
- cc1 = tcpci_to_typec_cc((status >> TCPC_CC_STATUS_CC1_SHIFT) &
- TCPC_CC_STATUS_CC1_MASK,
+ cc1 = tcpci_to_typec_cc(FIELD_GET(TCPC_CC_STATUS_CC1, status),
status & TCPC_CC_STATUS_TERM ||
tcpc_presenting_rd(role, CC1));
- cc2 = tcpci_to_typec_cc((status >> TCPC_CC_STATUS_CC2_SHIFT) &
- TCPC_CC_STATUS_CC2_MASK,
+ cc2 = tcpci_to_typec_cc(FIELD_GET(TCPC_CC_STATUS_CC2, status),
status & TCPC_CC_STATUS_TERM ||
tcpc_presenting_rd(role, CC2));
@@ -233,25 +232,25 @@ static int rt1711h_start_drp_toggling(struct tcpci *tcpci,
switch (cc) {
default:
case TYPEC_CC_RP_DEF:
- reg |= (TCPC_ROLE_CTRL_RP_VAL_DEF <<
- TCPC_ROLE_CTRL_RP_VAL_SHIFT);
+ reg |= FIELD_PREP(TCPC_ROLE_CTRL_RP_VAL,
+ TCPC_ROLE_CTRL_RP_VAL_DEF);
break;
case TYPEC_CC_RP_1_5:
- reg |= (TCPC_ROLE_CTRL_RP_VAL_1_5 <<
- TCPC_ROLE_CTRL_RP_VAL_SHIFT);
+ reg |= FIELD_PREP(TCPC_ROLE_CTRL_RP_VAL,
+ TCPC_ROLE_CTRL_RP_VAL_1_5);
break;
case TYPEC_CC_RP_3_0:
- reg |= (TCPC_ROLE_CTRL_RP_VAL_3_0 <<
- TCPC_ROLE_CTRL_RP_VAL_SHIFT);
+ reg |= FIELD_PREP(TCPC_ROLE_CTRL_RP_VAL,
+ TCPC_ROLE_CTRL_RP_VAL_3_0);
break;
}
if (cc == TYPEC_CC_RD)
- reg |= (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC1_SHIFT) |
- (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC2_SHIFT);
+ reg |= (FIELD_PREP(TCPC_ROLE_CTRL_CC1, TCPC_ROLE_CTRL_CC_RD)
+ | FIELD_PREP(TCPC_ROLE_CTRL_CC2, TCPC_ROLE_CTRL_CC_RD));
else
- reg |= (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT) |
- (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT);
+ reg |= (FIELD_PREP(TCPC_ROLE_CTRL_CC1, TCPC_ROLE_CTRL_CC_RP)
+ | FIELD_PREP(TCPC_ROLE_CTRL_CC2, TCPC_ROLE_CTRL_CC_RP));
ret = rt1711h_write8(chip, TCPC_ROLE_CTRL, reg);
if (ret < 0)
diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c
index dd51a25480bf..256b0c054e9a 100644
--- a/drivers/usb/typec/tipd/core.c
+++ b/drivers/usb/typec/tipd/core.c
@@ -1465,8 +1465,9 @@ static void tps6598x_remove(struct i2c_client *client)
if (!client->irq)
cancel_delayed_work_sync(&tps->wq_poll);
+ else
+ devm_free_irq(tps->dev, client->irq, tps);
- devm_free_irq(tps->dev, client->irq, tps);
tps6598x_disconnect(tps, 0);
typec_unregister_port(tps->port);
usb_role_switch_put(tps->role_sw);
diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c
index 4039851551c1..f0b5867048e2 100644
--- a/drivers/usb/typec/ucsi/ucsi.c
+++ b/drivers/usb/typec/ucsi/ucsi.c
@@ -99,12 +99,8 @@ static int ucsi_run_command(struct ucsi *ucsi, u64 command, u32 *cci,
*cci = 0;
- /*
- * Below UCSI 2.0, MESSAGE_IN was limited to 16 bytes. Truncate the
- * reads here.
- */
- if (ucsi->version <= UCSI_VERSION_1_2)
- size = clamp(size, 0, 16);
+ if (size > UCSI_MAX_DATA_LENGTH(ucsi))
+ return -EINVAL;
ret = ucsi->ops->sync_control(ucsi, command);
if (ret)
@@ -115,7 +111,7 @@ static int ucsi_run_command(struct ucsi *ucsi, u64 command, u32 *cci,
return ret;
if (*cci & UCSI_CCI_BUSY)
- return -EBUSY;
+ return ucsi_run_command(ucsi, UCSI_CANCEL, cci, NULL, 0, false) ?: -EBUSY;
if (!(*cci & UCSI_CCI_COMMAND_COMPLETE))
return -EIO;
@@ -148,21 +144,10 @@ static int ucsi_read_error(struct ucsi *ucsi, u8 connector_num)
int ret;
command = UCSI_GET_ERROR_STATUS | UCSI_CONNECTOR_NUMBER(connector_num);
- ret = ucsi_run_command(ucsi, command, &cci,
- &error, sizeof(error), false);
-
- if (cci & UCSI_CCI_BUSY) {
- ret = ucsi_run_command(ucsi, UCSI_CANCEL, &cci, NULL, 0, false);
-
- return ret ? ret : -EBUSY;
- }
-
+ ret = ucsi_run_command(ucsi, command, &cci, &error, sizeof(error), false);
if (ret < 0)
return ret;
- if (cci & UCSI_CCI_ERROR)
- return -EIO;
-
switch (error) {
case UCSI_ERROR_INCOMPATIBLE_PARTNER:
return -EOPNOTSUPP;
@@ -238,9 +223,8 @@ static int ucsi_send_command_common(struct ucsi *ucsi, u64 cmd,
mutex_lock(&ucsi->ppm_lock);
ret = ucsi_run_command(ucsi, cmd, &cci, data, size, conn_ack);
- if (cci & UCSI_CCI_BUSY)
- ret = ucsi_run_command(ucsi, UCSI_CANCEL, &cci, NULL, 0, false) ?: -EBUSY;
- else if (cci & UCSI_CCI_ERROR)
+
+ if (cci & UCSI_CCI_ERROR)
ret = ucsi_read_error(ucsi, connector_num);
mutex_unlock(&ucsi->ppm_lock);
@@ -752,104 +736,66 @@ static struct usb_power_delivery_capabilities *ucsi_get_pd_caps(struct ucsi_conn
&pd_caps);
}
-static int ucsi_read_identity(struct ucsi_connector *con, u8 recipient,
- u8 offset, u8 bytes, void *resp)
+static int ucsi_get_pd_message(struct ucsi_connector *con, u8 recipient,
+ size_t bytes, void *data, u8 type)
{
- struct ucsi *ucsi = con->ucsi;
+ size_t len = min(bytes, UCSI_MAX_DATA_LENGTH(con->ucsi));
u64 command;
+ u8 offset;
int ret;
- command = UCSI_COMMAND(UCSI_GET_PD_MESSAGE) |
- UCSI_CONNECTOR_NUMBER(con->num);
- command |= UCSI_GET_PD_MESSAGE_RECIPIENT(recipient);
- command |= UCSI_GET_PD_MESSAGE_OFFSET(offset);
- command |= UCSI_GET_PD_MESSAGE_BYTES(bytes);
- command |= UCSI_GET_PD_MESSAGE_TYPE(UCSI_GET_PD_MESSAGE_TYPE_IDENTITY);
-
- ret = ucsi_send_command(ucsi, command, resp, bytes);
- if (ret < 0)
- dev_err(ucsi->dev, "UCSI_GET_PD_MESSAGE failed (%d)\n", ret);
-
- return ret;
-}
-
-static int ucsi_get_identity(struct ucsi_connector *con, u8 recipient,
- struct usb_pd_identity *id)
-{
- struct ucsi *ucsi = con->ucsi;
- struct ucsi_pd_message_disc_id resp = {};
- int ret;
-
- if (ucsi->version < UCSI_VERSION_2_0) {
- /*
- * Before UCSI v2.0, MESSAGE_IN is 16 bytes which cannot fit
- * the 28 byte identity response including the VDM header.
- * First request the VDM header, ID Header VDO, Cert Stat VDO
- * and Product VDO.
- */
- ret = ucsi_read_identity(con, recipient, 0, 0x10, &resp);
- if (ret < 0)
- return ret;
-
+ for (offset = 0; offset < bytes; offset += len) {
+ len = min(len, bytes - offset);
- /* Then request Product Type VDO1 through Product Type VDO3. */
- ret = ucsi_read_identity(con, recipient, 0x10, 0xc,
- &resp.vdo[0]);
- if (ret < 0)
- return ret;
+ command = UCSI_COMMAND(UCSI_GET_PD_MESSAGE) | UCSI_CONNECTOR_NUMBER(con->num);
+ command |= UCSI_GET_PD_MESSAGE_RECIPIENT(recipient);
+ command |= UCSI_GET_PD_MESSAGE_OFFSET(offset);
+ command |= UCSI_GET_PD_MESSAGE_BYTES(len);
+ command |= UCSI_GET_PD_MESSAGE_TYPE(type);
- } else {
- /*
- * In UCSI v2.0 and after, MESSAGE_IN is large enough to request
- * the large enough to request the full Discover Identity
- * response at once.
- */
- ret = ucsi_read_identity(con, recipient, 0x0, 0x1c, &resp);
+ ret = ucsi_send_command(con->ucsi, command, data + offset, len);
if (ret < 0)
return ret;
}
- id->id_header = resp.id_header;
- id->cert_stat = resp.cert_stat;
- id->product = resp.product;
- id->vdo[0] = resp.vdo[0];
- id->vdo[1] = resp.vdo[1];
- id->vdo[2] = resp.vdo[2];
return 0;
}
static int ucsi_get_partner_identity(struct ucsi_connector *con)
{
+ u32 vdo[7] = {};
int ret;
- ret = ucsi_get_identity(con, UCSI_RECIPIENT_SOP,
- &con->partner_identity);
+ ret = ucsi_get_pd_message(con, UCSI_RECIPIENT_SOP, sizeof(vdo), vdo,
+ UCSI_GET_PD_MESSAGE_TYPE_IDENTITY);
if (ret < 0)
return ret;
+ /* VDM Header is not part of struct usb_pd_identity, so dropping it. */
+ con->partner_identity = *(struct usb_pd_identity *)&vdo[1];
+
ret = typec_partner_set_identity(con->partner);
- if (ret < 0) {
- dev_err(con->ucsi->dev, "Failed to set partner identity (%d)\n",
- ret);
- }
+ if (ret < 0)
+ dev_err(con->ucsi->dev, "Failed to set partner identity (%d)\n", ret);
return ret;
}
static int ucsi_get_cable_identity(struct ucsi_connector *con)
{
+ u32 vdo[7] = {};
int ret;
- ret = ucsi_get_identity(con, UCSI_RECIPIENT_SOP_P,
- &con->cable_identity);
+ ret = ucsi_get_pd_message(con, UCSI_RECIPIENT_SOP_P, sizeof(vdo), vdo,
+ UCSI_GET_PD_MESSAGE_TYPE_IDENTITY);
if (ret < 0)
return ret;
+ con->cable_identity = *(struct usb_pd_identity *)&vdo[1];
+
ret = typec_cable_set_identity(con->cable);
- if (ret < 0) {
- dev_err(con->ucsi->dev, "Failed to set cable identity (%d)\n",
- ret);
- }
+ if (ret < 0)
+ dev_err(con->ucsi->dev, "Failed to set cable identity (%d)\n", ret);
return ret;
}
@@ -983,7 +929,8 @@ static int ucsi_register_cable(struct ucsi_connector *con)
break;
}
- desc.identity = &con->cable_identity;
+ if (con->ucsi->cap.features & UCSI_CAP_GET_PD_MESSAGE)
+ desc.identity = &con->cable_identity;
desc.active = !!(UCSI_CABLE_PROP_FLAG_ACTIVE_CABLE &
con->cable_prop.flags);
desc.pd_revision = UCSI_CABLE_PROP_FLAG_PD_MAJOR_REV_AS_BCD(
@@ -1062,7 +1009,8 @@ static int ucsi_register_partner(struct ucsi_connector *con)
if (pwr_opmode == UCSI_CONSTAT_PWR_OPMODE_PD)
ucsi_register_device_pdos(con);
- desc.identity = &con->partner_identity;
+ if (con->ucsi->cap.features & UCSI_CAP_GET_PD_MESSAGE)
+ desc.identity = &con->partner_identity;
desc.usb_pd = pwr_opmode == UCSI_CONSTAT_PWR_OPMODE_PD;
desc.pd_revision = UCSI_CONCAP_FLAG_PARTNER_PD_MAJOR_REV_AS_BCD(con->cap.flags);
@@ -1340,12 +1288,26 @@ EXPORT_SYMBOL_GPL(ucsi_connector_change);
/* -------------------------------------------------------------------------- */
+/*
+ * Hard Reset bit field was defined with value 1 in UCSI spec version 1.0.
+ * Starting with spec version 1.1, Hard Reset bit field was removed from the
+ * CONNECTOR_RESET command, until spec 2.0 reintroduced it with value 0, so, in effect,
+ * the value to pass in to the command for a Hard Reset is different depending
+ * on the supported UCSI version by the LPM.
+ *
+ * For performing a Data Reset on LPMs supporting version 2.0 and greater,
+ * this function needs to be called with the second argument set to 0.
+ */
static int ucsi_reset_connector(struct ucsi_connector *con, bool hard)
{
u64 command;
command = UCSI_CONNECTOR_RESET | UCSI_CONNECTOR_NUMBER(con->num);
- command |= hard ? UCSI_CONNECTOR_RESET_HARD : 0;
+
+ if (con->ucsi->version < UCSI_VERSION_1_1)
+ command |= hard ? UCSI_CONNECTOR_RESET_HARD_VER_1_0 : 0;
+ else if (con->ucsi->version >= UCSI_VERSION_2_0)
+ command |= hard ? 0 : UCSI_CONNECTOR_RESET_DATA_VER_2_0;
return ucsi_send_command(con->ucsi, command, NULL, 0);
}
diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h
index 57129f3c0814..63cc7f982663 100644
--- a/drivers/usb/typec/ucsi/ucsi.h
+++ b/drivers/usb/typec/ucsi/ucsi.h
@@ -29,6 +29,7 @@ struct dentry;
#define UCSIv2_MESSAGE_OUT 272
/* UCSI versions */
+#define UCSI_VERSION_1_1 0x0110
#define UCSI_VERSION_1_2 0x0120
#define UCSI_VERSION_2_0 0x0200
#define UCSI_VERSION_2_1 0x0210
@@ -122,7 +123,9 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num);
#define UCSI_DEFAULT_GET_CONNECTOR_NUMBER(_cmd_) (((_cmd_) >> 16) & GENMASK(6, 0))
/* CONNECTOR_RESET command bits */
-#define UCSI_CONNECTOR_RESET_HARD BIT(23) /* Deprecated in v1.1 */
+#define UCSI_CONNECTOR_RESET_HARD_VER_1_0 BIT(23) /* Deprecated in v1.1 */
+#define UCSI_CONNECTOR_RESET_DATA_VER_2_0 BIT(23) /* Redefined in v2.0 */
+
/* ACK_CC_CI bits */
#define UCSI_ACK_CONNECTOR_CHANGE BIT(16)
@@ -344,47 +347,12 @@ struct ucsi_connector_status {
#define UCSI_CONSTAT_PARTNER_TYPE_AUDIO 6
u32 request_data_obj;
- u8 pwr_status[3];
-#define UCSI_CONSTAT_BC_STATUS(_p_) ((_p_[0]) & GENMASK(1, 0))
+ u8 pwr_status;
+#define UCSI_CONSTAT_BC_STATUS(_p_) ((_p_) & GENMASK(1, 0))
#define UCSI_CONSTAT_BC_NOT_CHARGING 0
#define UCSI_CONSTAT_BC_NOMINAL_CHARGING 1
#define UCSI_CONSTAT_BC_SLOW_CHARGING 2
#define UCSI_CONSTAT_BC_TRICKLE_CHARGING 3
-#define UCSI_CONSTAT_PROVIDER_CAP_LIMIT(_p_) (((_p_[0]) & GENMASK(5, 2)) >> 2)
-#define UCSI_CONSTAT_CAP_PWR_LOWERED 0
-#define UCSI_CONSTAT_CAP_PWR_BUDGET_LIMIT 1
-#define UCSI_CONSTAT_PROVIDER_PD_VERSION_OPER_MODE(_p_) \
- ((get_unaligned_le32(_p_) & GENMASK(21, 6)) >> 6)
-#define UCSI_CONSTAT_ORIENTATION(_p_) (((_p_[2]) & GENMASK(6, 6)) >> 6)
-#define UCSI_CONSTAT_ORIENTATION_DIRECT 0
-#define UCSI_CONSTAT_ORIENTATION_FLIPPED 1
-#define UCSI_CONSTAT_SINK_PATH_STATUS(_p_) (((_p_[2]) & GENMASK(7, 7)) >> 7)
-#define UCSI_CONSTAT_SINK_PATH_DISABLED 0
-#define UCSI_CONSTAT_SINK_PATH_ENABLED 1
- u8 pwr_readings[9];
-#define UCSI_CONSTAT_REV_CURR_PROT_STATUS(_p_) ((_p_[0]) & 0x1)
-#define UCSI_CONSTAT_PWR_READING_VALID(_p_) (((_p_[0]) & GENMASK(1, 1)) >> 1)
-#define UCSI_CONSTAT_CURRENT_SCALE(_p_) (((_p_[0]) & GENMASK(4, 2)) >> 2)
-#define UCSI_CONSTAT_PEAK_CURRENT(_p_) \
- ((get_unaligned_le32(_p_) & GENMASK(20, 5)) >> 5)
-#define UCSI_CONSTAT_AVG_CURRENT(_p_) \
- ((get_unaligned_le32(&(_p_)[2]) & GENMASK(20, 5)) >> 5)
-#define UCSI_CONSTAT_VOLTAGE_SCALE(_p_) \
- ((get_unaligned_le16(&(_p_)[4]) & GENMASK(8, 5)) >> 5)
-#define UCSI_CONSTAT_VOLTAGE_READING(_p_) \
- ((get_unaligned_le32(&(_p_)[5]) & GENMASK(16, 1)) >> 1)
-} __packed;
-
-/*
- * Data structure filled by PPM in response to GET_PD_MESSAGE command with the
- * Response Message Type set to Discover Identity Response.
- */
-struct ucsi_pd_message_disc_id {
- u32 vdm_header;
- u32 id_header;
- u32 cert_stat;
- u32 product;
- u32 vdo[3];
} __packed;
/* -------------------------------------------------------------------------- */
@@ -435,6 +403,8 @@ struct ucsi {
#define UCSI_DELAY_DEVICE_PDOS BIT(1) /* Reading PDOs fails until the parter is in PD mode */
};
+#define UCSI_MAX_DATA_LENGTH(u) (((u)->version < UCSI_VERSION_2_0) ? 0x10 : 0xff)
+
#define UCSI_MAX_SVID 5
#define UCSI_MAX_ALTMODES (UCSI_MAX_SVID * 6)
diff --git a/drivers/usb/typec/ucsi/ucsi_glink.c b/drivers/usb/typec/ucsi/ucsi_glink.c
index 6aace19d595b..03c0fa8edc8d 100644
--- a/drivers/usb/typec/ucsi/ucsi_glink.c
+++ b/drivers/usb/typec/ucsi/ucsi_glink.c
@@ -278,7 +278,7 @@ static void pmic_glink_ucsi_callback(const void *data, size_t len, void *priv)
case UC_UCSI_USBC_NOTIFY_IND:
schedule_work(&ucsi->notify_work);
break;
- };
+ }
}
static void pmic_glink_ucsi_pdr_notify(void *priv, int state)
diff --git a/drivers/usb/usbip/vhci_hcd.c b/drivers/usb/usbip/vhci_hcd.c
index 302a89aeb258..8dac1edc74d4 100644
--- a/drivers/usb/usbip/vhci_hcd.c
+++ b/drivers/usb/usbip/vhci_hcd.c
@@ -372,7 +372,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
}
switch (wValue) {
case USB_PORT_FEAT_SUSPEND:
- if (hcd->speed == HCD_USB3) {
+ if (hcd->speed >= HCD_USB3) {
pr_err(" ClearPortFeature: USB_PORT_FEAT_SUSPEND req not "
"supported for USB 3.0 roothub\n");
goto error;
@@ -388,7 +388,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
case USB_PORT_FEAT_POWER:
usbip_dbg_vhci_rh(
" ClearPortFeature: USB_PORT_FEAT_POWER\n");
- if (hcd->speed == HCD_USB3)
+ if (hcd->speed >= HCD_USB3)
vhci_hcd->port_status[rhport] &= ~USB_SS_PORT_STAT_POWER;
else
vhci_hcd->port_status[rhport] &= ~USB_PORT_STAT_POWER;
@@ -404,19 +404,19 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
break;
case GetHubDescriptor:
usbip_dbg_vhci_rh(" GetHubDescriptor\n");
- if (hcd->speed == HCD_USB3 &&
+ if (hcd->speed >= HCD_USB3 &&
(wLength < USB_DT_SS_HUB_SIZE ||
wValue != (USB_DT_SS_HUB << 8))) {
pr_err("Wrong hub descriptor type for USB 3.0 roothub.\n");
goto error;
}
- if (hcd->speed == HCD_USB3)
+ if (hcd->speed >= HCD_USB3)
ss_hub_descriptor((struct usb_hub_descriptor *) buf);
else
hub_descriptor((struct usb_hub_descriptor *) buf);
break;
case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
- if (hcd->speed != HCD_USB3)
+ if (hcd->speed < HCD_USB3)
goto error;
if ((wValue >> 8) != USB_DT_BOS)
@@ -503,7 +503,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
case USB_PORT_FEAT_LINK_STATE:
usbip_dbg_vhci_rh(
" SetPortFeature: USB_PORT_FEAT_LINK_STATE\n");
- if (hcd->speed != HCD_USB3) {
+ if (hcd->speed < HCD_USB3) {
pr_err("USB_PORT_FEAT_LINK_STATE req not "
"supported for USB 2.0 roothub\n");
goto error;
@@ -521,7 +521,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
usbip_dbg_vhci_rh(
" SetPortFeature: USB_PORT_FEAT_U2_TIMEOUT\n");
/* TODO: add suspend/resume support! */
- if (hcd->speed != HCD_USB3) {
+ if (hcd->speed < HCD_USB3) {
pr_err("USB_PORT_FEAT_U1/2_TIMEOUT req not "
"supported for USB 2.0 roothub\n");
goto error;
@@ -531,7 +531,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
usbip_dbg_vhci_rh(
" SetPortFeature: USB_PORT_FEAT_SUSPEND\n");
/* Applicable only for USB2.0 hub */
- if (hcd->speed == HCD_USB3) {
+ if (hcd->speed >= HCD_USB3) {
pr_err("USB_PORT_FEAT_SUSPEND req not "
"supported for USB 3.0 roothub\n");
goto error;
@@ -551,7 +551,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
pr_err("invalid port number %d\n", wIndex);
goto error;
}
- if (hcd->speed == HCD_USB3)
+ if (hcd->speed >= HCD_USB3)
vhci_hcd->port_status[rhport] |= USB_SS_PORT_STAT_POWER;
else
vhci_hcd->port_status[rhport] |= USB_PORT_STAT_POWER;
@@ -564,7 +564,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
goto error;
}
/* Applicable only for USB3.0 hub */
- if (hcd->speed != HCD_USB3) {
+ if (hcd->speed < HCD_USB3) {
pr_err("USB_PORT_FEAT_BH_PORT_RESET req not "
"supported for USB 2.0 roothub\n");
goto error;
@@ -578,7 +578,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
goto error;
}
/* if it's already enabled, disable */
- if (hcd->speed == HCD_USB3) {
+ if (hcd->speed >= HCD_USB3) {
vhci_hcd->port_status[rhport] = 0;
vhci_hcd->port_status[rhport] =
(USB_SS_PORT_STAT_POWER |
@@ -602,7 +602,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
}
if (wValue >= 32)
goto error;
- if (hcd->speed == HCD_USB3) {
+ if (hcd->speed >= HCD_USB3) {
if ((vhci_hcd->port_status[rhport] &
USB_SS_PORT_STAT_POWER) != 0) {
vhci_hcd->port_status[rhport] |= (1 << wValue);
@@ -616,7 +616,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
break;
case GetPortErrorCount:
usbip_dbg_vhci_rh(" GetPortErrorCount\n");
- if (hcd->speed != HCD_USB3) {
+ if (hcd->speed < HCD_USB3) {
pr_err("GetPortErrorCount req not "
"supported for USB 2.0 roothub\n");
goto error;
@@ -626,7 +626,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
break;
case SetHubDepth:
usbip_dbg_vhci_rh(" SetHubDepth\n");
- if (hcd->speed != HCD_USB3) {
+ if (hcd->speed < HCD_USB3) {
pr_err("SetHubDepth req not supported for "
"USB 2.0 roothub\n");
goto error;
@@ -646,7 +646,7 @@ error:
if (!invalid_rhport) {
dump_port_status_diff(prev_port_status[rhport],
vhci_hcd->port_status[rhport],
- hcd->speed == HCD_USB3);
+ hcd->speed >= HCD_USB3);
}
}
usbip_dbg_vhci_rh(" bye\n");
@@ -1157,8 +1157,8 @@ static int vhci_setup(struct usb_hcd *hcd)
} else {
vhci->vhci_hcd_ss = hcd_to_vhci_hcd(hcd);
vhci->vhci_hcd_ss->vhci = vhci;
- hcd->speed = HCD_USB3;
- hcd->self.root_hub->speed = USB_SPEED_SUPER;
+ hcd->speed = HCD_USB31;
+ hcd->self.root_hub->speed = USB_SPEED_SUPER_PLUS;
}
/*
@@ -1319,7 +1319,7 @@ static const struct hc_driver vhci_hc_driver = {
.product_desc = driver_desc,
.hcd_priv_size = sizeof(struct vhci_hcd),
- .flags = HCD_USB3 | HCD_SHARED,
+ .flags = HCD_USB31 | HCD_SHARED,
.reset = vhci_setup,
.start = vhci_start,
diff --git a/drivers/usb/usbip/vhci_sysfs.c b/drivers/usb/usbip/vhci_sysfs.c
index e2847cd3e6e3..d5865460e82d 100644
--- a/drivers/usb/usbip/vhci_sysfs.c
+++ b/drivers/usb/usbip/vhci_sysfs.c
@@ -283,6 +283,7 @@ static int valid_args(__u32 *pdev_nr, __u32 *rhport,
case USB_SPEED_HIGH:
case USB_SPEED_WIRELESS:
case USB_SPEED_SUPER:
+ case USB_SPEED_SUPER_PLUS:
break;
default:
pr_err("Failed attach request for unsupported USB speed: %s\n",
@@ -349,7 +350,7 @@ static ssize_t attach_store(struct device *dev, struct device_attribute *attr,
vhci_hcd = hcd_to_vhci_hcd(hcd);
vhci = vhci_hcd->vhci;
- if (speed == USB_SPEED_SUPER)
+ if (speed >= USB_SPEED_SUPER)
vdev = &vhci->vhci_hcd_ss->vdev[rhport];
else
vdev = &vhci->vhci_hcd_hs->vdev[rhport];
diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h
index af3cd2aae4bc..6e38fb9d2117 100644
--- a/include/linux/usb/composite.h
+++ b/include/linux/usb/composite.h
@@ -256,7 +256,7 @@ int config_ep_by_speed(struct usb_gadget *g, struct usb_function *f,
struct usb_ep *_ep);
int usb_func_wakeup(struct usb_function *func);
-#define MAX_CONFIG_INTERFACES 16 /* arbitrary; max 255 */
+#define MAX_CONFIG_INTERFACES 32
/**
* struct usb_configuration - represents one gadget configuration
diff --git a/include/linux/usb/gadget_configfs.h b/include/linux/usb/gadget_configfs.h
index d61aebd68128..6b5d6838f865 100644
--- a/include/linux/usb/gadget_configfs.h
+++ b/include/linux/usb/gadget_configfs.h
@@ -4,9 +4,6 @@
#include <linux/configfs.h>
-int check_user_usb_string(const char *name,
- struct usb_gadget_strings *stringtab_dev);
-
#define GS_STRINGS_W(__struct, __name) \
static ssize_t __struct##_##__name##_store(struct config_item *item, \
const char *page, size_t len) \
@@ -37,7 +34,7 @@ static struct configfs_item_operations struct_in##_langid_item_ops = { \
.release = struct_in##_attr_release, \
}; \
\
-static struct config_item_type struct_in##_langid_type = { \
+static const struct config_item_type struct_in##_langid_type = { \
.ct_item_ops = &struct_in##_langid_item_ops, \
.ct_attrs = struct_in##_langid_attrs, \
.ct_owner = THIS_MODULE, \
@@ -94,7 +91,7 @@ static struct configfs_group_operations struct_in##_strings_ops = { \
.drop_item = &struct_in##_strings_drop, \
}; \
\
-static struct config_item_type struct_in##_strings_type = { \
+static const struct config_item_type struct_in##_strings_type = { \
.ct_group_ops = &struct_in##_strings_ops, \
.ct_owner = THIS_MODULE, \
}
diff --git a/include/linux/usb/tcpci.h b/include/linux/usb/tcpci.h
index 0ab39b6ea205..f7f5cfbdef12 100644
--- a/include/linux/usb/tcpci.h
+++ b/include/linux/usb/tcpci.h
@@ -63,15 +63,12 @@
#define TCPC_ROLE_CTRL 0x1a
#define TCPC_ROLE_CTRL_DRP BIT(6)
-#define TCPC_ROLE_CTRL_RP_VAL_SHIFT 4
-#define TCPC_ROLE_CTRL_RP_VAL_MASK 0x3
+#define TCPC_ROLE_CTRL_RP_VAL GENMASK(5, 4)
#define TCPC_ROLE_CTRL_RP_VAL_DEF 0x0
#define TCPC_ROLE_CTRL_RP_VAL_1_5 0x1
#define TCPC_ROLE_CTRL_RP_VAL_3_0 0x2
-#define TCPC_ROLE_CTRL_CC2_SHIFT 2
-#define TCPC_ROLE_CTRL_CC2_MASK 0x3
-#define TCPC_ROLE_CTRL_CC1_SHIFT 0
-#define TCPC_ROLE_CTRL_CC1_MASK 0x3
+#define TCPC_ROLE_CTRL_CC2 GENMASK(3, 2)
+#define TCPC_ROLE_CTRL_CC1 GENMASK(1, 0)
#define TCPC_ROLE_CTRL_CC_RA 0x0
#define TCPC_ROLE_CTRL_CC_RP 0x1
#define TCPC_ROLE_CTRL_CC_RD 0x2
@@ -92,11 +89,9 @@
#define TCPC_CC_STATUS_TERM BIT(4)
#define TCPC_CC_STATUS_TERM_RP 0
#define TCPC_CC_STATUS_TERM_RD 1
+#define TCPC_CC_STATUS_CC2 GENMASK(3, 2)
+#define TCPC_CC_STATUS_CC1 GENMASK(1, 0)
#define TCPC_CC_STATE_SRC_OPEN 0
-#define TCPC_CC_STATUS_CC2_SHIFT 2
-#define TCPC_CC_STATUS_CC2_MASK 0x3
-#define TCPC_CC_STATUS_CC1_SHIFT 0
-#define TCPC_CC_STATUS_CC1_MASK 0x3
#define TCPC_POWER_STATUS 0x1e
#define TCPC_POWER_STATUS_DBG_ACC_CON BIT(7)
@@ -134,9 +129,8 @@
#define TCPC_MSG_HDR_INFO 0x2e
#define TCPC_MSG_HDR_INFO_DATA_ROLE BIT(3)
+#define TCPC_MSG_HDR_INFO_REV GENMASK(2, 1)
#define TCPC_MSG_HDR_INFO_PWR_ROLE BIT(0)
-#define TCPC_MSG_HDR_INFO_REV_SHIFT 1
-#define TCPC_MSG_HDR_INFO_REV_MASK 0x3
#define TCPC_RX_DETECT 0x2f
#define TCPC_RX_DETECT_HARD_RESET BIT(5)
@@ -154,10 +148,8 @@
#define TCPC_RX_DATA 0x34 /* through 0x4f */
#define TCPC_TRANSMIT 0x50
-#define TCPC_TRANSMIT_RETRY_SHIFT 4
-#define TCPC_TRANSMIT_RETRY_MASK 0x3
-#define TCPC_TRANSMIT_TYPE_SHIFT 0
-#define TCPC_TRANSMIT_TYPE_MASK 0x7
+#define TCPC_TRANSMIT_RETRY GENMASK(5, 4)
+#define TCPC_TRANSMIT_TYPE GENMASK(2, 0)
#define TCPC_TX_BYTE_CNT 0x51
#define TCPC_TX_HDR 0x52
@@ -178,8 +170,7 @@
#define tcpc_presenting_rd(reg, cc) \
(!(TCPC_ROLE_CTRL_DRP & (reg)) && \
- (((reg) & (TCPC_ROLE_CTRL_## cc ##_MASK << TCPC_ROLE_CTRL_## cc ##_SHIFT)) == \
- (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_## cc ##_SHIFT)))
+ FIELD_GET(TCPC_ROLE_CTRL_## cc, reg) == TCPC_ROLE_CTRL_CC_RD)
struct tcpci;
@@ -190,7 +181,7 @@ struct tcpci;
* Optional; Callback to perform chip specific operations when FRS
* is sourcing vbus.
* @auto_discharge_disconnect:
- * Optional; Enables TCPC to autonously discharge vbus on disconnect.
+ * Optional; Enables TCPC to autonomously discharge vbus on disconnect.
* @vbus_vsafe0v:
* optional; Set when TCPC can detect whether vbus is at VSAFE0V.
* @set_partner_usb_comm_capable:
@@ -256,7 +247,7 @@ static inline enum typec_cc_status tcpci_to_typec_cc(unsigned int cc, bool sink)
if (sink)
return TYPEC_CC_RP_3_0;
fallthrough;
- case 0x0:
+ case TCPC_CC_STATE_SRC_OPEN:
default:
return TYPEC_CC_OPEN;
}
diff --git a/include/uapi/linux/usb/ch9.h b/include/uapi/linux/usb/ch9.h
index 44d73ba8788d..91f0f7e214a5 100644
--- a/include/uapi/linux/usb/ch9.h
+++ b/include/uapi/linux/usb/ch9.h
@@ -254,6 +254,9 @@ struct usb_ctrlrequest {
#define USB_DT_DEVICE_CAPABILITY 0x10
#define USB_DT_WIRELESS_ENDPOINT_COMP 0x11
#define USB_DT_WIRE_ADAPTER 0x21
+/* From USB Device Firmware Upgrade Specification, Revision 1.1 */
+#define USB_DT_DFU_FUNCTIONAL 0x21
+/* these are from the Wireless USB spec */
#define USB_DT_RPIPE 0x22
#define USB_DT_CS_RADIO_CONTROL 0x23
/* From the T10 UAS specification */
@@ -329,9 +332,10 @@ struct usb_device_descriptor {
#define USB_CLASS_USB_TYPE_C_BRIDGE 0x12
#define USB_CLASS_MISC 0xef
#define USB_CLASS_APP_SPEC 0xfe
-#define USB_CLASS_VENDOR_SPEC 0xff
+#define USB_SUBCLASS_DFU 0x01
-#define USB_SUBCLASS_VENDOR_SPEC 0xff
+#define USB_CLASS_VENDOR_SPEC 0xff
+#define USB_SUBCLASS_VENDOR_SPEC 0xff
/*-------------------------------------------------------------------------*/
diff --git a/include/uapi/linux/usb/functionfs.h b/include/uapi/linux/usb/functionfs.h
index 9f88de9c3d66..2ebdba111a8f 100644
--- a/include/uapi/linux/usb/functionfs.h
+++ b/include/uapi/linux/usb/functionfs.h
@@ -3,6 +3,7 @@
#define _UAPI__LINUX_FUNCTIONFS_H__
+#include <linux/const.h>
#include <linux/types.h>
#include <linux/ioctl.h>
@@ -37,6 +38,31 @@ struct usb_endpoint_descriptor_no_audio {
__u8 bInterval;
} __attribute__((packed));
+/**
+ * struct usb_dfu_functional_descriptor - DFU Functional descriptor
+ * @bLength: Size of the descriptor (bytes)
+ * @bDescriptorType: USB_DT_DFU_FUNCTIONAL
+ * @bmAttributes: DFU attributes
+ * @wDetachTimeOut: Maximum time to wait after DFU_DETACH (ms, le16)
+ * @wTransferSize: Maximum number of bytes per control-write (le16)
+ * @bcdDFUVersion: DFU Spec version (BCD, le16)
+ */
+struct usb_dfu_functional_descriptor {
+ __u8 bLength;
+ __u8 bDescriptorType;
+ __u8 bmAttributes;
+ __le16 wDetachTimeOut;
+ __le16 wTransferSize;
+ __le16 bcdDFUVersion;
+} __attribute__ ((packed));
+
+/* from DFU functional descriptor bmAttributes */
+#define DFU_FUNC_ATT_CAN_DOWNLOAD _BITUL(0)
+#define DFU_FUNC_ATT_CAN_UPLOAD _BITUL(1)
+#define DFU_FUNC_ATT_MANIFEST_TOLERANT _BITUL(2)
+#define DFU_FUNC_ATT_WILL_DETACH _BITUL(3)
+
+
struct usb_functionfs_descs_head_v2 {
__le32 magic;
__le32 length;
@@ -104,23 +130,38 @@ struct usb_ffs_dmabuf_transfer_req {
#ifndef __KERNEL__
-/*
+/**
+ * DOC: descriptors
+ *
* Descriptors format:
*
+ * +-----+-----------+--------------+--------------------------------------+
* | off | name | type | description |
- * |-----+-----------+--------------+--------------------------------------|
+ * +-----+-----------+--------------+--------------------------------------+
* | 0 | magic | LE32 | FUNCTIONFS_DESCRIPTORS_MAGIC_V2 |
+ * +-----+-----------+--------------+--------------------------------------+
* | 4 | length | LE32 | length of the whole data chunk |
+ * +-----+-----------+--------------+--------------------------------------+
* | 8 | flags | LE32 | combination of functionfs_flags |
+ * +-----+-----------+--------------+--------------------------------------+
* | | eventfd | LE32 | eventfd file descriptor |
+ * +-----+-----------+--------------+--------------------------------------+
* | | fs_count | LE32 | number of full-speed descriptors |
+ * +-----+-----------+--------------+--------------------------------------+
* | | hs_count | LE32 | number of high-speed descriptors |
+ * +-----+-----------+--------------+--------------------------------------+
* | | ss_count | LE32 | number of super-speed descriptors |
+ * +-----+-----------+--------------+--------------------------------------+
* | | os_count | LE32 | number of MS OS descriptors |
+ * +-----+-----------+--------------+--------------------------------------+
* | | fs_descrs | Descriptor[] | list of full-speed descriptors |
+ * +-----+-----------+--------------+--------------------------------------+
* | | hs_descrs | Descriptor[] | list of high-speed descriptors |
+ * +-----+-----------+--------------+--------------------------------------+
* | | ss_descrs | Descriptor[] | list of super-speed descriptors |
+ * +-----+-----------+--------------+--------------------------------------+
* | | os_descrs | OSDesc[] | list of MS OS descriptors |
+ * +-----+-----------+--------------+--------------------------------------+
*
* Depending on which flags are set, various fields may be missing in the
* structure. Any flags that are not recognised cause the whole block to be
@@ -128,71 +169,111 @@ struct usb_ffs_dmabuf_transfer_req {
*
* Legacy descriptors format (deprecated as of 3.14):
*
+ * +-----+-----------+--------------+--------------------------------------+
* | off | name | type | description |
- * |-----+-----------+--------------+--------------------------------------|
+ * +-----+-----------+--------------+--------------------------------------+
* | 0 | magic | LE32 | FUNCTIONFS_DESCRIPTORS_MAGIC |
+ * +-----+-----------+--------------+--------------------------------------+
* | 4 | length | LE32 | length of the whole data chunk |
+ * +-----+-----------+--------------+--------------------------------------+
* | 8 | fs_count | LE32 | number of full-speed descriptors |
+ * +-----+-----------+--------------+--------------------------------------+
* | 12 | hs_count | LE32 | number of high-speed descriptors |
+ * +-----+-----------+--------------+--------------------------------------+
* | 16 | fs_descrs | Descriptor[] | list of full-speed descriptors |
+ * +-----+-----------+--------------+--------------------------------------+
* | | hs_descrs | Descriptor[] | list of high-speed descriptors |
+ * +-----+-----------+--------------+--------------------------------------+
*
* All numbers must be in little endian order.
*
* Descriptor[] is an array of valid USB descriptors which have the following
* format:
*
+ * +-----+-----------------+------+--------------------------+
* | off | name | type | description |
- * |-----+-----------------+------+--------------------------|
+ * +-----+-----------------+------+--------------------------+
* | 0 | bLength | U8 | length of the descriptor |
+ * +-----+-----------------+------+--------------------------+
* | 1 | bDescriptorType | U8 | descriptor type |
+ * +-----+-----------------+------+--------------------------+
* | 2 | payload | | descriptor's payload |
+ * +-----+-----------------+------+--------------------------+
*
* OSDesc[] is an array of valid MS OS Feature Descriptors which have one of
* the following formats:
*
+ * +-----+-----------------+------+--------------------------+
* | off | name | type | description |
- * |-----+-----------------+------+--------------------------|
+ * +-----+-----------------+------+--------------------------+
* | 0 | inteface | U8 | related interface number |
+ * +-----+-----------------+------+--------------------------+
* | 1 | dwLength | U32 | length of the descriptor |
+ * +-----+-----------------+------+--------------------------+
* | 5 | bcdVersion | U16 | currently supported: 1 |
+ * +-----+-----------------+------+--------------------------+
* | 7 | wIndex | U16 | currently supported: 4 |
+ * +-----+-----------------+------+--------------------------+
* | 9 | bCount | U8 | number of ext. compat. |
+ * +-----+-----------------+------+--------------------------+
* | 10 | Reserved | U8 | 0 |
+ * +-----+-----------------+------+--------------------------+
* | 11 | ExtCompat[] | | list of ext. compat. d. |
+ * +-----+-----------------+------+--------------------------+
*
+ * +-----+-----------------+------+--------------------------+
* | off | name | type | description |
- * |-----+-----------------+------+--------------------------|
+ * +-----+-----------------+------+--------------------------+
* | 0 | inteface | U8 | related interface number |
+ * +-----+-----------------+------+--------------------------+
* | 1 | dwLength | U32 | length of the descriptor |
+ * +-----+-----------------+------+--------------------------+
* | 5 | bcdVersion | U16 | currently supported: 1 |
+ * +-----+-----------------+------+--------------------------+
* | 7 | wIndex | U16 | currently supported: 5 |
+ * +-----+-----------------+------+--------------------------+
* | 9 | wCount | U16 | number of ext. compat. |
+ * +-----+-----------------+------+--------------------------+
* | 11 | ExtProp[] | | list of ext. prop. d. |
+ * +-----+-----------------+------+--------------------------+
*
* ExtCompat[] is an array of valid Extended Compatiblity descriptors
* which have the following format:
*
+ * +-----+-----------------------+------+-------------------------------------+
* | off | name | type | description |
- * |-----+-----------------------+------+-------------------------------------|
+ * +-----+-----------------------+------+-------------------------------------+
* | 0 | bFirstInterfaceNumber | U8 | index of the interface or of the 1st|
+ * +-----+-----------------------+------+-------------------------------------+
* | | | | interface in an IAD group |
+ * +-----+-----------------------+------+-------------------------------------+
* | 1 | Reserved | U8 | 1 |
+ * +-----+-----------------------+------+-------------------------------------+
* | 2 | CompatibleID | U8[8]| compatible ID string |
+ * +-----+-----------------------+------+-------------------------------------+
* | 10 | SubCompatibleID | U8[8]| subcompatible ID string |
+ * +-----+-----------------------+------+-------------------------------------+
* | 18 | Reserved | U8[6]| 0 |
+ * +-----+-----------------------+------+-------------------------------------+
*
* ExtProp[] is an array of valid Extended Properties descriptors
* which have the following format:
*
+ * +-----+-----------------------+------+-------------------------------------+
* | off | name | type | description |
- * |-----+-----------------------+------+-------------------------------------|
+ * +-----+-----------------------+------+-------------------------------------+
* | 0 | dwSize | U32 | length of the descriptor |
+ * +-----+-----------------------+------+-------------------------------------+
* | 4 | dwPropertyDataType | U32 | 1..7 |
+ * +-----+-----------------------+------+-------------------------------------+
* | 8 | wPropertyNameLength | U16 | bPropertyName length (NL) |
+ * +-----+-----------------------+------+-------------------------------------+
* | 10 | bPropertyName |U8[NL]| name of this property |
+ * +-----+-----------------------+------+-------------------------------------+
* |10+NL| dwPropertyDataLength | U32 | bPropertyData length (DL) |
+ * +-----+-----------------------+------+-------------------------------------+
* |14+NL| bProperty |U8[DL]| payload of this property |
+ * +-----+-----------------------+------+-------------------------------------+
*/
struct usb_functionfs_strings_head {
diff --git a/include/uapi/linux/usb/g_hid.h b/include/uapi/linux/usb/g_hid.h
new file mode 100644
index 000000000000..b965092db476
--- /dev/null
+++ b/include/uapi/linux/usb/g_hid.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
+
+#ifndef __UAPI_LINUX_USB_G_HID_H
+#define __UAPI_LINUX_USB_G_HID_H
+
+#include <linux/types.h>
+
+/* Maximum HID report length for High-Speed USB (i.e. USB 2.0) */
+#define MAX_REPORT_LENGTH 64
+
+/**
+ * struct usb_hidg_report - response to GET_REPORT
+ * @report_id: report ID that this is a response for
+ * @userspace_req:
+ * !0 this report is used for any pending GET_REPORT request
+ * but wait on userspace to issue a new report on future requests
+ * 0 this report is to be used for any future GET_REPORT requests
+ * @length: length of the report response
+ * @data: report response
+ * @padding: padding for 32/64 bit compatibility
+ *
+ * Structure used by GADGET_HID_WRITE_GET_REPORT ioctl on /dev/hidg*.
+ */
+struct usb_hidg_report {
+ __u8 report_id;
+ __u8 userspace_req;
+ __u16 length;
+ __u8 data[MAX_REPORT_LENGTH];
+ __u8 padding[4];
+};
+
+/* The 'g' code is used by gadgetfs and hid gadget ioctl requests.
+ * Don't add any colliding codes to either driver, and keep
+ * them in unique ranges.
+ */
+
+#define GADGET_HID_READ_GET_REPORT_ID _IOR('g', 0x41, __u8)
+#define GADGET_HID_WRITE_GET_REPORT _IOW('g', 0x42, struct usb_hidg_report)
+
+#endif /* __UAPI_LINUX_USB_G_HID_H */
diff --git a/include/uapi/linux/usb/gadgetfs.h b/include/uapi/linux/usb/gadgetfs.h
index 835473910a49..9754822b2a40 100644
--- a/include/uapi/linux/usb/gadgetfs.h
+++ b/include/uapi/linux/usb/gadgetfs.h
@@ -62,7 +62,7 @@ struct usb_gadgetfs_event {
};
-/* The 'g' code is also used by printer gadget ioctl requests.
+/* The 'g' code is also used by printer and hid gadget ioctl requests.
* Don't add any colliding codes to either driver, and keep
* them in unique ranges (size 0x20 for now).
*/