summaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2023-09-01 09:23:34 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2023-09-01 09:23:34 -0700
commit51e7accbe8ab51476fbe55fbb5616c12fb3a0beb (patch)
tree70e83070a63b607c7bb3a7d4b2757b3ad47fb427 /drivers/usb
parente2c874f999f079e3ec9b8071e92c87d57aded3b6 (diff)
parent895ed7eb263d7ce2d2592fdd3e211464a556084a (diff)
downloadlinux-51e7accbe8ab51476fbe55fbb5616c12fb3a0beb.tar.gz
linux-51e7accbe8ab51476fbe55fbb5616c12fb3a0beb.tar.bz2
linux-51e7accbe8ab51476fbe55fbb5616c12fb3a0beb.zip
Merge tag 'usb-6.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
Pull USB / Thunderbolt / PHY driver updates from Greg KH: "Here is the big set of USB, Thunderbolt, and PHY driver updates for 6.6-rc1. Included in here are: - PHY driver additions and cleanups - Thunderbolt minor additions and fixes - USB MIDI 2 gadget support added - dwc3 driver updates and additions - Removal of some old USB wireless code that was missed when that codebase was originally removed a few years ago, cleaning up some core USB code paths - USB core potential use-after-free fixes that syzbot from different people/groups keeps tripping over - typec updates and additions - gadget fixes and cleanups - loads of smaller USB core and driver cleanups all over the place Full details are in the shortlog. All of these have been in linux-next for a while with no reported problems" * tag 'usb-6.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (154 commits) platform/chrome: cros_ec_typec: Configure Retimer cable type tcpm: Avoid soft reset when partner does not support get_status usb: typec: tcpm: reset counter when enter into unattached state after try role usb: typec: tcpm: set initial svdm version based on pd revision USB: serial: option: add FOXCONN T99W368/T99W373 product USB: serial: option: add Quectel EM05G variant (0x030e) usb: dwc2: add pci_device_id driver_data parse support usb: gadget: remove max support speed info in bind operation usb: gadget: composite: cleanup function config_ep_by_speed_and_alt() usb: gadget: config: remove max speed check in usb_assign_descriptors() usb: gadget: unconditionally allocate hs/ss descriptor in bind operation usb: gadget: f_uvc: change endpoint allocation in uvc_function_bind() usb: gadget: add a inline function gether_bitrate() usb: gadget: use working speed to calcaulate network bitrate and qlen dt-bindings: usb: samsung,exynos-dwc3: Add Exynos850 support usb: dwc3: exynos: Add support for Exynos850 variant usb: gadget: udc-xilinx: fix incorrect type in assignment warning usb: gadget: udc-xilinx: fix cast from restricted __le16 warning usb: gadget: udc-xilinx: fix restricted __le16 degrades to integer warning USB: dwc2: hande irq on dead controller correctly ...
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/cdns3/cdns3-gadget.c1
-rw-r--r--drivers/usb/cdns3/cdns3-plat.c4
-rw-r--r--drivers/usb/cdns3/cdns3-starfive.c6
-rw-r--r--drivers/usb/cdns3/cdns3-ti.c1
-rw-r--r--drivers/usb/cdns3/cdnsp-pci.c3
-rw-r--r--drivers/usb/cdns3/core.c16
-rw-r--r--drivers/usb/cdns3/core.h7
-rw-r--r--drivers/usb/cdns3/drd.c4
-rw-r--r--drivers/usb/chipidea/ci.h19
-rw-r--r--drivers/usb/chipidea/ci_hdrc_imx.c18
-rw-r--r--drivers/usb/chipidea/ci_hdrc_imx.h1
-rw-r--r--drivers/usb/chipidea/ci_hdrc_tegra.c3
-rw-r--r--drivers/usb/chipidea/core.c5
-rw-r--r--drivers/usb/chipidea/host.c1
-rw-r--r--drivers/usb/chipidea/udc.c12
-rw-r--r--drivers/usb/chipidea/usbmisc_imx.c18
-rw-r--r--drivers/usb/class/cdc-acm.c25
-rw-r--r--drivers/usb/common/common.c1
-rw-r--r--drivers/usb/core/config.c3
-rw-r--r--drivers/usb/core/devices.c1
-rw-r--r--drivers/usb/core/file.c68
-rw-r--r--drivers/usb/core/hcd.c50
-rw-r--r--drivers/usb/core/hub.c505
-rw-r--r--drivers/usb/core/ledtrig-usbport.c13
-rw-r--r--drivers/usb/core/message.c30
-rw-r--r--drivers/usb/core/of.c1
-rw-r--r--drivers/usb/core/sysfs.c3
-rw-r--r--drivers/usb/core/urb.c27
-rw-r--r--drivers/usb/core/usb.c20
-rw-r--r--drivers/usb/core/usb.h5
-rw-r--r--drivers/usb/dwc2/core.h1
-rw-r--r--drivers/usb/dwc2/gadget.c1
-rw-r--r--drivers/usb/dwc2/hcd_intr.c4
-rw-r--r--drivers/usb/dwc2/params.c39
-rw-r--r--drivers/usb/dwc2/pci.c14
-rw-r--r--drivers/usb/dwc2/platform.c2
-rw-r--r--drivers/usb/dwc3/Kconfig10
-rw-r--r--drivers/usb/dwc3/Makefile1
-rw-r--r--drivers/usb/dwc3/dwc3-am62.c96
-rw-r--r--drivers/usb/dwc3/dwc3-exynos.c9
-rw-r--r--drivers/usb/dwc3/dwc3-imx8mp.c2
-rw-r--r--drivers/usb/dwc3/dwc3-keystone.c3
-rw-r--r--drivers/usb/dwc3/dwc3-meson-g12a.c6
-rw-r--r--drivers/usb/dwc3/dwc3-octeon.c543
-rw-r--r--drivers/usb/dwc3/dwc3-of-simple.c1
-rw-r--r--drivers/usb/gadget/Kconfig18
-rw-r--r--drivers/usb/gadget/composite.c34
-rw-r--r--drivers/usb/gadget/config.c8
-rw-r--r--drivers/usb/gadget/function/Makefile2
-rw-r--r--drivers/usb/gadget/function/f_acm.c4
-rw-r--r--drivers/usb/gadget/function/f_ecm.c19
-rw-r--r--drivers/usb/gadget/function/f_eem.c4
-rw-r--r--drivers/usb/gadget/function/f_loopback.c4
-rw-r--r--drivers/usb/gadget/function/f_mass_storage.c2
-rw-r--r--drivers/usb/gadget/function/f_midi.c56
-rw-r--r--drivers/usb/gadget/function/f_midi2.c2871
-rw-r--r--drivers/usb/gadget/function/f_ncm.c23
-rw-r--r--drivers/usb/gadget/function/f_obex.c3
-rw-r--r--drivers/usb/gadget/function/f_rndis.c19
-rw-r--r--drivers/usb/gadget/function/f_serial.c4
-rw-r--r--drivers/usb/gadget/function/f_sourcesink.c4
-rw-r--r--drivers/usb/gadget/function/f_subset.c4
-rw-r--r--drivers/usb/gadget/function/f_uvc.c36
-rw-r--r--drivers/usb/gadget/function/u_ether.c5
-rw-r--r--drivers/usb/gadget/function/u_ether.h13
-rw-r--r--drivers/usb/gadget/function/u_midi2.h81
-rw-r--r--drivers/usb/gadget/function/u_phonet.h1
-rw-r--r--drivers/usb/gadget/function/u_serial.h4
-rw-r--r--drivers/usb/gadget/function/uvc.h2
-rw-r--r--drivers/usb/gadget/udc/aspeed-vhub/core.c3
-rw-r--r--drivers/usb/gadget/udc/aspeed_udc.c4
-rw-r--r--drivers/usb/gadget/udc/atmel_usba_udc.c6
-rw-r--r--drivers/usb/gadget/udc/core.c1
-rw-r--r--drivers/usb/gadget/udc/fsl_qe_udc.c2
-rw-r--r--drivers/usb/gadget/udc/fsl_udc_core.c3
-rw-r--r--drivers/usb/gadget/udc/gr_udc.c13
-rw-r--r--drivers/usb/gadget/udc/max3420_udc.c4
-rw-r--r--drivers/usb/gadget/udc/mv_u3d_core.c4
-rw-r--r--drivers/usb/gadget/udc/mv_udc_core.c2
-rw-r--r--drivers/usb/gadget/udc/pxa27x_udc.c2
-rw-r--r--drivers/usb/gadget/udc/renesas_usb3.c2
-rw-r--r--drivers/usb/gadget/udc/renesas_usbf.c6
-rw-r--r--drivers/usb/gadget/udc/snps_udc_plat.c7
-rw-r--r--drivers/usb/gadget/udc/tegra-xudc.c1
-rw-r--r--drivers/usb/gadget/udc/udc-xilinx.c35
-rw-r--r--drivers/usb/host/ehci-atmel.c7
-rw-r--r--drivers/usb/host/ehci-brcm.c4
-rw-r--r--drivers/usb/host/ehci-exynos.c3
-rw-r--r--drivers/usb/host/ehci-fsl.c5
-rw-r--r--drivers/usb/host/ehci-hcd.c8
-rw-r--r--drivers/usb/host/ehci-hub.c10
-rw-r--r--drivers/usb/host/ehci-mv.c3
-rw-r--r--drivers/usb/host/ehci-npcm7xx.c5
-rw-r--r--drivers/usb/host/ehci-omap.c3
-rw-r--r--drivers/usb/host/ehci-orion.c9
-rw-r--r--drivers/usb/host/ehci-platform.c3
-rw-r--r--drivers/usb/host/ehci-sched.c3
-rw-r--r--drivers/usb/host/ehci-sh.c7
-rw-r--r--drivers/usb/host/ehci-spear.c3
-rw-r--r--drivers/usb/host/ehci-st.c12
-rw-r--r--drivers/usb/host/ehci.h10
-rw-r--r--drivers/usb/host/fhci-hcd.c3
-rw-r--r--drivers/usb/host/fsl-mph-dr-of.c3
-rw-r--r--drivers/usb/host/isp1362-hcd.c3
-rw-r--r--drivers/usb/host/ohci-at91.c9
-rw-r--r--drivers/usb/host/ohci-da8xx.c4
-rw-r--r--drivers/usb/host/ohci-exynos.c3
-rw-r--r--drivers/usb/host/ohci-nxp.c3
-rw-r--r--drivers/usb/host/ohci-platform.c3
-rw-r--r--drivers/usb/host/ohci-ppc-of.c3
-rw-r--r--drivers/usb/host/ohci-pxa27x.c3
-rw-r--r--drivers/usb/host/ohci-sm501.c3
-rw-r--r--drivers/usb/host/ohci-spear.c3
-rw-r--r--drivers/usb/host/ohci-st.c14
-rw-r--r--drivers/usb/host/oxu210hp-hcd.c3
-rw-r--r--drivers/usb/host/uhci-platform.c3
-rw-r--r--drivers/usb/host/xhci-mem.c3
-rw-r--r--drivers/usb/host/xhci-plat.c1
-rw-r--r--drivers/usb/host/xhci-rcar.c1
-rw-r--r--drivers/usb/host/xhci-tegra.c28
-rw-r--r--drivers/usb/host/xhci.c11
-rw-r--r--drivers/usb/misc/cypress_cy7c63.c2
-rw-r--r--drivers/usb/misc/cytherm.c12
-rw-r--r--drivers/usb/misc/onboard_usb_hub.c41
-rw-r--r--drivers/usb/misc/onboard_usb_hub.h15
-rw-r--r--drivers/usb/misc/usb251xb.c2
-rw-r--r--drivers/usb/misc/usb_u132.h97
-rw-r--r--drivers/usb/misc/usbsevseg.c2
-rw-r--r--drivers/usb/mtu3/mtu3.h1
-rw-r--r--drivers/usb/mtu3/mtu3_host.c1
-rw-r--r--drivers/usb/musb/cppi_dma.h3
-rw-r--r--drivers/usb/musb/jz4740.c2
-rw-r--r--drivers/usb/musb/mediatek.c1
-rw-r--r--drivers/usb/musb/mpfs.c1
-rw-r--r--drivers/usb/musb/musb_core.c4
-rw-r--r--drivers/usb/musb/musb_dma.h4
-rw-r--r--drivers/usb/musb/musb_dsps.c2
-rw-r--r--drivers/usb/musb/musb_gadget.c2
-rw-r--r--drivers/usb/musb/sunxi.c1
-rw-r--r--drivers/usb/musb/tusb6010.c17
-rw-r--r--drivers/usb/phy/phy-mxs-usb.c16
-rw-r--r--drivers/usb/phy/phy-tegra-usb.c2
-rw-r--r--drivers/usb/renesas_usbhs/common.c2
-rw-r--r--drivers/usb/renesas_usbhs/rza.c2
-rw-r--r--drivers/usb/renesas_usbhs/rza2.c1
-rw-r--r--drivers/usb/serial/option.c7
-rw-r--r--drivers/usb/serial/xr_serial.c89
-rw-r--r--drivers/usb/typec/altmodes/displayport.c5
-rw-r--r--drivers/usb/typec/bus.c12
-rw-r--r--drivers/usb/typec/mux/intel_pmc_mux.c53
-rw-r--r--drivers/usb/typec/mux/nb7vpq904m.c2
-rw-r--r--drivers/usb/typec/tcpm/Kconfig1
-rw-r--r--drivers/usb/typec/tcpm/fusb302.c2
-rw-r--r--drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c39
-rw-r--r--drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c2
-rw-r--r--drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c6
-rw-r--r--drivers/usb/typec/tcpm/tcpci.c4
-rw-r--r--drivers/usb/typec/tcpm/tcpci_mt6370.c2
-rw-r--r--drivers/usb/typec/tcpm/tcpm.c177
-rw-r--r--drivers/usb/typec/ucsi/Kconfig1
-rw-r--r--drivers/usb/typec/ucsi/Makefile2
-rw-r--r--drivers/usb/typec/ucsi/debugfs.c99
-rw-r--r--drivers/usb/typec/ucsi/ucsi.c15
-rw-r--r--drivers/usb/typec/ucsi/ucsi.h24
-rw-r--r--drivers/usb/typec/ucsi/ucsi_glink.c1
-rw-r--r--drivers/usb/usbip/vudc_dev.c5
166 files changed, 4779 insertions, 1123 deletions
diff --git a/drivers/usb/cdns3/cdns3-gadget.c b/drivers/usb/cdns3/cdns3-gadget.c
index aa0111b365bb..11a5b3437c32 100644
--- a/drivers/usb/cdns3/cdns3-gadget.c
+++ b/drivers/usb/cdns3/cdns3-gadget.c
@@ -61,6 +61,7 @@
#include <linux/module.h>
#include <linux/dmapool.h>
#include <linux/iopoll.h>
+#include <linux/property.h>
#include "core.h"
#include "gadget-export.h"
diff --git a/drivers/usb/cdns3/cdns3-plat.c b/drivers/usb/cdns3/cdns3-plat.c
index 884e2301237f..2c1aca84f226 100644
--- a/drivers/usb/cdns3/cdns3-plat.c
+++ b/drivers/usb/cdns3/cdns3-plat.c
@@ -15,6 +15,7 @@
#include <linux/module.h>
#include <linux/irq.h>
#include <linux/kernel.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
@@ -255,9 +256,10 @@ static int cdns3_controller_resume(struct device *dev, pm_message_t msg)
cdns3_set_platform_suspend(cdns->dev, false, false);
spin_lock_irqsave(&cdns->lock, flags);
- cdns_resume(cdns, !PMSG_IS_AUTO(msg));
+ cdns_resume(cdns);
cdns->in_lpm = false;
spin_unlock_irqrestore(&cdns->lock, flags);
+ cdns_set_active(cdns, !PMSG_IS_AUTO(msg));
if (cdns->wakeup_pending) {
cdns->wakeup_pending = false;
enable_irq(cdns->wakeup_irq);
diff --git a/drivers/usb/cdns3/cdns3-starfive.c b/drivers/usb/cdns3/cdns3-starfive.c
index fc1f003b145d..a7265b86e427 100644
--- a/drivers/usb/cdns3/cdns3-starfive.c
+++ b/drivers/usb/cdns3/cdns3-starfive.c
@@ -166,7 +166,7 @@ static int cdns_starfive_remove_core(struct device *dev, void *c)
return 0;
}
-static int cdns_starfive_remove(struct platform_device *pdev)
+static void cdns_starfive_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct cdns_starfive *data = dev_get_drvdata(dev);
@@ -178,8 +178,6 @@ static int cdns_starfive_remove(struct platform_device *pdev)
pm_runtime_put_noidle(dev);
cdns_clk_rst_deinit(data);
platform_set_drvdata(pdev, NULL);
-
- return 0;
}
#ifdef CONFIG_PM
@@ -232,7 +230,7 @@ MODULE_DEVICE_TABLE(of, cdns_starfive_of_match);
static struct platform_driver cdns_starfive_driver = {
.probe = cdns_starfive_probe,
- .remove = cdns_starfive_remove,
+ .remove_new = cdns_starfive_remove,
.driver = {
.name = "cdns3-starfive",
.of_match_table = cdns_starfive_of_match,
diff --git a/drivers/usb/cdns3/cdns3-ti.c b/drivers/usb/cdns3/cdns3-ti.c
index 81b9132e3aaa..5945c4b1e11f 100644
--- a/drivers/usb/cdns3/cdns3-ti.c
+++ b/drivers/usb/cdns3/cdns3-ti.c
@@ -15,6 +15,7 @@
#include <linux/io.h>
#include <linux/of_platform.h>
#include <linux/pm_runtime.h>
+#include <linux/property.h>
/* USB Wrapper register offsets */
#define USBSS_PID 0x0
diff --git a/drivers/usb/cdns3/cdnsp-pci.c b/drivers/usb/cdns3/cdnsp-pci.c
index 7b151f5af3cc..0725668ffea4 100644
--- a/drivers/usb/cdns3/cdnsp-pci.c
+++ b/drivers/usb/cdns3/cdnsp-pci.c
@@ -208,8 +208,9 @@ static int __maybe_unused cdnsp_pci_resume(struct device *dev)
int ret;
spin_lock_irqsave(&cdns->lock, flags);
- ret = cdns_resume(cdns, 1);
+ ret = cdns_resume(cdns);
spin_unlock_irqrestore(&cdns->lock, flags);
+ cdns_set_active(cdns, 1);
return ret;
}
diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c
index dbcdf3b24b47..33548771a0d3 100644
--- a/drivers/usb/cdns3/core.c
+++ b/drivers/usb/cdns3/core.c
@@ -14,6 +14,7 @@
#include <linux/dma-mapping.h>
#include <linux/module.h>
#include <linux/kernel.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/io.h>
@@ -522,9 +523,8 @@ int cdns_suspend(struct cdns *cdns)
}
EXPORT_SYMBOL_GPL(cdns_suspend);
-int cdns_resume(struct cdns *cdns, u8 set_active)
+int cdns_resume(struct cdns *cdns)
{
- struct device *dev = cdns->dev;
enum usb_role real_role;
bool role_changed = false;
int ret = 0;
@@ -556,15 +556,23 @@ int cdns_resume(struct cdns *cdns, u8 set_active)
if (cdns->roles[cdns->role]->resume)
cdns->roles[cdns->role]->resume(cdns, cdns_power_is_lost(cdns));
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cdns_resume);
+
+void cdns_set_active(struct cdns *cdns, u8 set_active)
+{
+ struct device *dev = cdns->dev;
+
if (set_active) {
pm_runtime_disable(dev);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
}
- return 0;
+ return;
}
-EXPORT_SYMBOL_GPL(cdns_resume);
+EXPORT_SYMBOL_GPL(cdns_set_active);
#endif /* CONFIG_PM_SLEEP */
MODULE_AUTHOR("Peter Chen <peter.chen@nxp.com>");
diff --git a/drivers/usb/cdns3/core.h b/drivers/usb/cdns3/core.h
index 2d332a788871..4a4dbc2c1561 100644
--- a/drivers/usb/cdns3/core.h
+++ b/drivers/usb/cdns3/core.h
@@ -125,10 +125,13 @@ int cdns_init(struct cdns *cdns);
int cdns_remove(struct cdns *cdns);
#ifdef CONFIG_PM_SLEEP
-int cdns_resume(struct cdns *cdns, u8 set_active);
+int cdns_resume(struct cdns *cdns);
int cdns_suspend(struct cdns *cdns);
+void cdns_set_active(struct cdns *cdns, u8 set_active);
#else /* CONFIG_PM_SLEEP */
-static inline int cdns_resume(struct cdns *cdns, u8 set_active)
+static inline int cdns_resume(struct cdns *cdns)
+{ return 0; }
+static inline int cdns_set_active(struct cdns *cdns, u8 set_active)
{ return 0; }
static inline int cdns_suspend(struct cdns *cdns)
{ return 0; }
diff --git a/drivers/usb/cdns3/drd.c b/drivers/usb/cdns3/drd.c
index d00ff98dffab..04b6d12f2b9a 100644
--- a/drivers/usb/cdns3/drd.c
+++ b/drivers/usb/cdns3/drd.c
@@ -196,6 +196,7 @@ int cdns_drd_host_on(struct cdns *cdns)
if (ret)
dev_err(cdns->dev, "timeout waiting for xhci_ready\n");
+ phy_set_mode(cdns->usb2_phy, PHY_MODE_USB_HOST);
phy_set_mode(cdns->usb3_phy, PHY_MODE_USB_HOST);
return ret;
}
@@ -216,6 +217,7 @@ void cdns_drd_host_off(struct cdns *cdns)
readl_poll_timeout_atomic(&cdns->otg_regs->state, val,
!(val & OTGSTATE_HOST_STATE_MASK),
1, 2000000);
+ phy_set_mode(cdns->usb2_phy, PHY_MODE_INVALID);
phy_set_mode(cdns->usb3_phy, PHY_MODE_INVALID);
}
@@ -248,6 +250,7 @@ int cdns_drd_gadget_on(struct cdns *cdns)
return ret;
}
+ phy_set_mode(cdns->usb2_phy, PHY_MODE_USB_DEVICE);
phy_set_mode(cdns->usb3_phy, PHY_MODE_USB_DEVICE);
return 0;
}
@@ -273,6 +276,7 @@ void cdns_drd_gadget_off(struct cdns *cdns)
readl_poll_timeout_atomic(&cdns->otg_regs->state, val,
!(val & OTGSTATE_DEV_STATE_MASK),
1, 2000000);
+ phy_set_mode(cdns->usb2_phy, PHY_MODE_INVALID);
phy_set_mode(cdns->usb3_phy, PHY_MODE_INVALID);
}
EXPORT_SYMBOL_GPL(cdns_drd_gadget_off);
diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h
index f210b7489fd5..d9bb3d3f026e 100644
--- a/drivers/usb/chipidea/ci.h
+++ b/drivers/usb/chipidea/ci.h
@@ -257,6 +257,7 @@ struct ci_hdrc {
bool id_event;
bool b_sess_valid_event;
bool imx28_write_fix;
+ bool has_portsc_pec_bug;
bool supports_runtime_pm;
bool in_lpm;
bool wakeup_int;
@@ -281,8 +282,19 @@ static inline int ci_role_start(struct ci_hdrc *ci, enum ci_role role)
return -ENXIO;
ret = ci->roles[role]->start(ci);
- if (!ret)
- ci->role = role;
+ if (ret)
+ return ret;
+
+ ci->role = role;
+
+ if (ci->usb_phy) {
+ if (role == CI_ROLE_HOST)
+ usb_phy_set_event(ci->usb_phy, USB_EVENT_ID);
+ else
+ /* in device mode but vbus is invalid*/
+ usb_phy_set_event(ci->usb_phy, USB_EVENT_NONE);
+ }
+
return ret;
}
@@ -296,6 +308,9 @@ static inline void ci_role_stop(struct ci_hdrc *ci)
ci->role = CI_ROLE_END;
ci->roles[role]->stop(ci);
+
+ if (ci->usb_phy)
+ usb_phy_set_event(ci->usb_phy, USB_EVENT_NONE);
}
static inline enum usb_role ci_role_to_usb_role(struct ci_hdrc *ci)
diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c
index 336ef6dd8e7d..e28bb2f2612d 100644
--- a/drivers/usb/chipidea/ci_hdrc_imx.c
+++ b/drivers/usb/chipidea/ci_hdrc_imx.c
@@ -6,6 +6,7 @@
*/
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
@@ -67,11 +68,13 @@ static const struct ci_hdrc_imx_platform_flag imx7d_usb_data = {
static const struct ci_hdrc_imx_platform_flag imx7ulp_usb_data = {
.flags = CI_HDRC_SUPPORTS_RUNTIME_PM |
+ CI_HDRC_HAS_PORTSC_PEC_MISSED |
CI_HDRC_PMQOS,
};
static const struct ci_hdrc_imx_platform_flag imx8ulp_usb_data = {
- .flags = CI_HDRC_SUPPORTS_RUNTIME_PM,
+ .flags = CI_HDRC_SUPPORTS_RUNTIME_PM |
+ CI_HDRC_HAS_PORTSC_PEC_MISSED,
};
static const struct of_device_id ci_hdrc_imx_dt_ids[] = {
@@ -175,10 +178,15 @@ static struct imx_usbmisc_data *usbmisc_get_init_data(struct device *dev)
if (of_usb_get_phy_mode(np) == USBPHY_INTERFACE_MODE_ULPI)
data->ulpi = 1;
- of_property_read_u32(np, "samsung,picophy-pre-emp-curr-control",
- &data->emp_curr_control);
- of_property_read_u32(np, "samsung,picophy-dc-vol-level-adjust",
- &data->dc_vol_level_adjust);
+ if (of_property_read_u32(np, "samsung,picophy-pre-emp-curr-control",
+ &data->emp_curr_control))
+ data->emp_curr_control = -1;
+ if (of_property_read_u32(np, "samsung,picophy-dc-vol-level-adjust",
+ &data->dc_vol_level_adjust))
+ data->dc_vol_level_adjust = -1;
+ if (of_property_read_u32(np, "fsl,picophy-rise-fall-time-adjust",
+ &data->rise_fall_time_adjust))
+ data->rise_fall_time_adjust = -1;
return data;
}
diff --git a/drivers/usb/chipidea/ci_hdrc_imx.h b/drivers/usb/chipidea/ci_hdrc_imx.h
index 7135b9a5d913..88b8da79d518 100644
--- a/drivers/usb/chipidea/ci_hdrc_imx.h
+++ b/drivers/usb/chipidea/ci_hdrc_imx.h
@@ -28,6 +28,7 @@ struct imx_usbmisc_data {
enum usb_dr_mode available_role; /* runtime usb dr mode */
int emp_curr_control;
int dc_vol_level_adjust;
+ int rise_fall_time_adjust;
};
int imx_usbmisc_init(struct imx_usbmisc_data *data);
diff --git a/drivers/usb/chipidea/ci_hdrc_tegra.c b/drivers/usb/chipidea/ci_hdrc_tegra.c
index ca36d11a69ea..8e78bf643e25 100644
--- a/drivers/usb/chipidea/ci_hdrc_tegra.c
+++ b/drivers/usb/chipidea/ci_hdrc_tegra.c
@@ -6,7 +6,8 @@
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c
index 51994d655b82..7ac39a281b8c 100644
--- a/drivers/usb/chipidea/core.c
+++ b/drivers/usb/chipidea/core.c
@@ -1028,8 +1028,7 @@ static int ci_hdrc_probe(struct platform_device *pdev)
return -ENODEV;
}
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(dev, res);
+ base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(base))
return PTR_ERR(base);
@@ -1045,6 +1044,8 @@ static int ci_hdrc_probe(struct platform_device *pdev)
CI_HDRC_IMX28_WRITE_FIX);
ci->supports_runtime_pm = !!(ci->platdata->flags &
CI_HDRC_SUPPORTS_RUNTIME_PM);
+ ci->has_portsc_pec_bug = !!(ci->platdata->flags &
+ CI_HDRC_HAS_PORTSC_PEC_MISSED);
platform_set_drvdata(pdev, ci);
ret = hw_device_init(ci, base);
diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c
index ebe7400243b1..08af26b762a2 100644
--- a/drivers/usb/chipidea/host.c
+++ b/drivers/usb/chipidea/host.c
@@ -151,6 +151,7 @@ static int host_start(struct ci_hdrc *ci)
ehci->has_hostpc = ci->hw_bank.lpm;
ehci->has_tdi_phy_lpm = ci->hw_bank.lpm;
ehci->imx28_write_fix = ci->imx28_write_fix;
+ ehci->has_ci_pec_bug = ci->has_portsc_pec_bug;
priv = (struct ehci_ci_priv *)ehci->priv;
priv->reg_vbus = NULL;
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
index 54c09245ad05..0b7bd3c643c3 100644
--- a/drivers/usb/chipidea/udc.c
+++ b/drivers/usb/chipidea/udc.c
@@ -1463,7 +1463,7 @@ static int ep_disable(struct usb_ep *ep)
*/
static struct usb_request *ep_alloc_request(struct usb_ep *ep, gfp_t gfp_flags)
{
- struct ci_hw_req *hwreq = NULL;
+ struct ci_hw_req *hwreq;
if (ep == NULL)
return NULL;
@@ -1718,6 +1718,13 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active)
ret = ci->platdata->notify_event(ci,
CI_HDRC_CONTROLLER_VBUS_EVENT);
+ if (ci->usb_phy) {
+ if (is_active)
+ usb_phy_set_event(ci->usb_phy, USB_EVENT_VBUS);
+ else
+ usb_phy_set_event(ci->usb_phy, USB_EVENT_NONE);
+ }
+
if (ci->driver)
ci_hdrc_gadget_connect(_gadget, is_active);
@@ -2034,6 +2041,9 @@ static irqreturn_t udc_irq(struct ci_hdrc *ci)
if (USBi_PCI & intr) {
ci->gadget.speed = hw_port_is_high_speed(ci) ?
USB_SPEED_HIGH : USB_SPEED_FULL;
+ if (ci->usb_phy)
+ usb_phy_set_event(ci->usb_phy,
+ USB_EVENT_ENUMERATED);
if (ci->suspended) {
if (ci->driver->resume) {
spin_unlock(&ci->lock);
diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c
index 9ee9621e2ccc..173c78afd502 100644
--- a/drivers/usb/chipidea/usbmisc_imx.c
+++ b/drivers/usb/chipidea/usbmisc_imx.c
@@ -4,10 +4,11 @@
*/
#include <linux/module.h>
-#include <linux/of_platform.h>
+#include <linux/of.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/delay.h>
+#include <linux/platform_device.h>
#include <linux/usb/otg.h>
#include "ci_hdrc_imx.h"
@@ -130,6 +131,8 @@
#define MX7D_USB_OTG_PHY_CFG1 0x30
#define TXPREEMPAMPTUNE0_BIT 28
#define TXPREEMPAMPTUNE0_MASK (3 << 28)
+#define TXRISETUNE0_BIT 24
+#define TXRISETUNE0_MASK (3 << 24)
#define TXVREFTUNE0_BIT 20
#define TXVREFTUNE0_MASK (0xf << 20)
@@ -659,18 +662,27 @@ static int usbmisc_imx7d_init(struct imx_usbmisc_data *data)
usbmisc->base + MX7D_USBNC_USB_CTRL2);
/* PHY tuning for signal quality */
reg = readl(usbmisc->base + MX7D_USB_OTG_PHY_CFG1);
- if (data->emp_curr_control && data->emp_curr_control <=
+ if (data->emp_curr_control >= 0 &&
+ data->emp_curr_control <=
(TXPREEMPAMPTUNE0_MASK >> TXPREEMPAMPTUNE0_BIT)) {
reg &= ~TXPREEMPAMPTUNE0_MASK;
reg |= (data->emp_curr_control << TXPREEMPAMPTUNE0_BIT);
}
- if (data->dc_vol_level_adjust && data->dc_vol_level_adjust <=
+ if (data->dc_vol_level_adjust >= 0 &&
+ data->dc_vol_level_adjust <=
(TXVREFTUNE0_MASK >> TXVREFTUNE0_BIT)) {
reg &= ~TXVREFTUNE0_MASK;
reg |= (data->dc_vol_level_adjust << TXVREFTUNE0_BIT);
}
+ if (data->rise_fall_time_adjust >= 0 &&
+ data->rise_fall_time_adjust <=
+ (TXRISETUNE0_MASK >> TXRISETUNE0_BIT)) {
+ reg &= ~TXRISETUNE0_MASK;
+ reg |= (data->rise_fall_time_adjust << TXRISETUNE0_BIT);
+ }
+
writel(reg, usbmisc->base + MX7D_USB_OTG_PHY_CFG1);
}
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index 11da5fb284d0..a1933a86aee0 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -28,6 +28,7 @@
#include <linux/serial.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
+#include <linux/tty_ldisc.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/uaccess.h>
@@ -318,6 +319,16 @@ static void acm_process_notification(struct acm *acm, unsigned char *buf)
}
difference = acm->ctrlin ^ newctrl;
+
+ if ((difference & USB_CDC_SERIAL_STATE_DCD) && acm->port.tty) {
+ struct tty_ldisc *ld = tty_ldisc_ref(acm->port.tty);
+ if (ld) {
+ if (ld->ops->dcd_change)
+ ld->ops->dcd_change(acm->port.tty, newctrl & USB_CDC_SERIAL_STATE_DCD);
+ tty_ldisc_deref(ld);
+ }
+ }
+
spin_lock_irqsave(&acm->read_lock, flags);
acm->ctrlin = newctrl;
acm->oldcount = acm->iocount;
@@ -853,6 +864,19 @@ static unsigned int acm_tty_write_room(struct tty_struct *tty)
return acm_wb_is_avail(acm) ? acm->writesize : 0;
}
+static void acm_tty_flush_buffer(struct tty_struct *tty)
+{
+ struct acm *acm = tty->driver_data;
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&acm->write_lock, flags);
+ for (i = 0; i < ACM_NW; i++)
+ if (acm->wb[i].use)
+ usb_unlink_urb(acm->wb[i].urb);
+ spin_unlock_irqrestore(&acm->write_lock, flags);
+}
+
static unsigned int acm_tty_chars_in_buffer(struct tty_struct *tty)
{
struct acm *acm = tty->driver_data;
@@ -2016,6 +2040,7 @@ static const struct tty_operations acm_ops = {
.hangup = acm_tty_hangup,
.write = acm_tty_write,
.write_room = acm_tty_write_room,
+ .flush_buffer = acm_tty_flush_buffer,
.ioctl = acm_tty_ioctl,
.throttle = acm_tty_throttle,
.unthrottle = acm_tty_unthrottle,
diff --git a/drivers/usb/common/common.c b/drivers/usb/common/common.c
index c9bdeb4ddcb5..b84efae26e15 100644
--- a/drivers/usb/common/common.c
+++ b/drivers/usb/common/common.c
@@ -11,6 +11,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/platform_device.h>
#include <linux/usb/ch9.h>
#include <linux/usb/of.h>
#include <linux/usb/otg.h>
diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c
index 725b8dbcfe5f..b19e38d5fd10 100644
--- a/drivers/usb/core/config.c
+++ b/drivers/usb/core/config.c
@@ -1051,9 +1051,6 @@ int usb_get_bos_descriptor(struct usb_device *dev)
}
switch (cap_type) {
- case USB_CAP_TYPE_WIRELESS_USB:
- /* Wireless USB cap descriptor is handled by wusb */
- break;
case USB_CAP_TYPE_EXT:
dev->bos->ext_cap =
(struct usb_ext_cap_descriptor *)buffer;
diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c
index 2c14a9636056..a247da73f34d 100644
--- a/drivers/usb/core/devices.c
+++ b/drivers/usb/core/devices.c
@@ -424,7 +424,6 @@ static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes,
case USB_SPEED_UNKNOWN: /* usb 1.1 root hub code */
case USB_SPEED_FULL:
speed = "12"; break;
- case USB_SPEED_WIRELESS: /* Wireless has no real fixed speed */
case USB_SPEED_HIGH:
speed = "480"; break;
case USB_SPEED_SUPER:
diff --git a/drivers/usb/core/file.c b/drivers/usb/core/file.c
index c4ed3310e069..a88ced93b5e7 100644
--- a/drivers/usb/core/file.c
+++ b/drivers/usb/core/file.c
@@ -29,7 +29,6 @@
#define MAX_USB_MINORS 256
static const struct file_operations *usb_minors[MAX_USB_MINORS];
static DECLARE_RWSEM(minor_rwsem);
-static DEFINE_MUTEX(init_usb_class_mutex);
static int usb_open(struct inode *inode, struct file *file)
{
@@ -57,11 +56,6 @@ static const struct file_operations usb_fops = {
.llseek = noop_llseek,
};
-static struct usb_class {
- struct kref kref;
- struct class *class;
-} *usb_class;
-
static char *usb_devnode(const struct device *dev, umode_t *mode)
{
struct usb_class_driver *drv;
@@ -72,50 +66,10 @@ static char *usb_devnode(const struct device *dev, umode_t *mode)
return drv->devnode(dev, mode);
}
-static int init_usb_class(void)
-{
- int result = 0;
-
- if (usb_class != NULL) {
- kref_get(&usb_class->kref);
- goto exit;
- }
-
- usb_class = kmalloc(sizeof(*usb_class), GFP_KERNEL);
- if (!usb_class) {
- result = -ENOMEM;
- goto exit;
- }
-
- kref_init(&usb_class->kref);
- usb_class->class = class_create("usbmisc");
- if (IS_ERR(usb_class->class)) {
- result = PTR_ERR(usb_class->class);
- printk(KERN_ERR "class_create failed for usb devices\n");
- kfree(usb_class);
- usb_class = NULL;
- goto exit;
- }
- usb_class->class->devnode = usb_devnode;
-
-exit:
- return result;
-}
-
-static void release_usb_class(struct kref *kref)
-{
- /* Ok, we cheat as we know we only have one usb_class */
- class_destroy(usb_class->class);
- kfree(usb_class);
- usb_class = NULL;
-}
-
-static void destroy_usb_class(void)
-{
- mutex_lock(&init_usb_class_mutex);
- kref_put(&usb_class->kref, release_usb_class);
- mutex_unlock(&init_usb_class_mutex);
-}
+const struct class usbmisc_class = {
+ .name = "usbmisc",
+ .devnode = usb_devnode,
+};
int usb_major_init(void)
{
@@ -156,7 +110,7 @@ void usb_major_cleanup(void)
int usb_register_dev(struct usb_interface *intf,
struct usb_class_driver *class_driver)
{
- int retval;
+ int retval = 0;
int minor_base = class_driver->minor_base;
int minor;
char name[20];
@@ -175,13 +129,6 @@ int usb_register_dev(struct usb_interface *intf,
if (intf->minor >= 0)
return -EADDRINUSE;
- mutex_lock(&init_usb_class_mutex);
- retval = init_usb_class();
- mutex_unlock(&init_usb_class_mutex);
-
- if (retval)
- return retval;
-
dev_dbg(&intf->dev, "looking for a minor, starting at %d\n", minor_base);
down_write(&minor_rwsem);
@@ -200,7 +147,7 @@ int usb_register_dev(struct usb_interface *intf,
/* create a usb class device for this usb interface */
snprintf(name, sizeof(name), class_driver->name, minor - minor_base);
- intf->usb_dev = device_create(usb_class->class, &intf->dev,
+ intf->usb_dev = device_create(&usbmisc_class, &intf->dev,
MKDEV(USB_MAJOR, minor), class_driver,
"%s", kbasename(name));
if (IS_ERR(intf->usb_dev)) {
@@ -234,7 +181,7 @@ void usb_deregister_dev(struct usb_interface *intf,
return;
dev_dbg(&intf->dev, "removing %d minor\n", intf->minor);
- device_destroy(usb_class->class, MKDEV(USB_MAJOR, intf->minor));
+ device_destroy(&usbmisc_class, MKDEV(USB_MAJOR, intf->minor));
down_write(&minor_rwsem);
usb_minors[intf->minor] = NULL;
@@ -242,6 +189,5 @@ void usb_deregister_dev(struct usb_interface *intf,
intf->usb_dev = NULL;
intf->minor = -1;
- destroy_usb_class();
}
EXPORT_SYMBOL_GPL(usb_deregister_dev);
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 8300baedafd2..12b6dfeaf658 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -156,27 +156,6 @@ static const u8 usb3_rh_dev_descriptor[18] = {
0x01 /* __u8 bNumConfigurations; */
};
-/* usb 2.5 (wireless USB 1.0) root hub device descriptor */
-static const u8 usb25_rh_dev_descriptor[18] = {
- 0x12, /* __u8 bLength; */
- USB_DT_DEVICE, /* __u8 bDescriptorType; Device */
- 0x50, 0x02, /* __le16 bcdUSB; v2.5 */
-
- 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */
- 0x00, /* __u8 bDeviceSubClass; */
- 0x00, /* __u8 bDeviceProtocol; [ usb 2.0 no TT ] */
- 0xFF, /* __u8 bMaxPacketSize0; always 0xFF (WUSB Spec 7.4.1). */
-
- 0x6b, 0x1d, /* __le16 idVendor; Linux Foundation 0x1d6b */
- 0x02, 0x00, /* __le16 idProduct; device 0x0002 */
- KERNEL_VER, KERNEL_REL, /* __le16 bcdDevice */
-
- 0x03, /* __u8 iManufacturer; */
- 0x02, /* __u8 iProduct; */
- 0x01, /* __u8 iSerialNumber; */
- 0x01 /* __u8 bNumConfigurations; */
-};
-
/* usb 2.0 root hub device descriptor */
static const u8 usb2_rh_dev_descriptor[18] = {
0x12, /* __u8 bLength; */
@@ -368,7 +347,7 @@ static const u8 ss_rh_config_descriptor[] = {
};
/* authorized_default behaviour:
- * -1 is authorized for all devices except wireless (old behaviour)
+ * -1 is authorized for all devices (leftover from wireless USB)
* 0 is unauthorized for all devices
* 1 is authorized for all devices
* 2 is authorized for internal devices
@@ -383,7 +362,7 @@ module_param(authorized_default, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(authorized_default,
"Default USB device authorization: 0 is not authorized, 1 is "
"authorized, 2 is authorized for internal devices, -1 is "
- "authorized except for wireless USB (default, old behaviour)");
+ "authorized (default, same as 1)");
/*-------------------------------------------------------------------------*/
/**
@@ -578,9 +557,6 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
case HCD_USB3:
bufp = usb3_rh_dev_descriptor;
break;
- case HCD_USB25:
- bufp = usb25_rh_dev_descriptor;
- break;
case HCD_USB2:
bufp = usb2_rh_dev_descriptor;
break;
@@ -602,7 +578,6 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
bufp = ss_rh_config_descriptor;
len = sizeof ss_rh_config_descriptor;
break;
- case HCD_USB25:
case HCD_USB2:
bufp = hs_rh_config_descriptor;
len = sizeof hs_rh_config_descriptor;
@@ -983,6 +958,7 @@ static int register_root_hub(struct usb_hcd *hcd)
{
struct device *parent_dev = hcd->self.controller;
struct usb_device *usb_dev = hcd->self.root_hub;
+ struct usb_device_descriptor *descr;
const int devnum = 1;
int retval;
@@ -994,13 +970,16 @@ static int register_root_hub(struct usb_hcd *hcd)
mutex_lock(&usb_bus_idr_lock);
usb_dev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
- retval = usb_get_device_descriptor(usb_dev, USB_DT_DEVICE_SIZE);
- if (retval != sizeof usb_dev->descriptor) {
+ descr = usb_get_device_descriptor(usb_dev);
+ if (IS_ERR(descr)) {
+ retval = PTR_ERR(descr);
mutex_unlock(&usb_bus_idr_lock);
dev_dbg (parent_dev, "can't read %s device descriptor %d\n",
dev_name(&usb_dev->dev), retval);
- return (retval < 0) ? retval : -EMSGSIZE;
+ return retval;
}
+ usb_dev->descriptor = *descr;
+ kfree(descr);
if (le16_to_cpu(usb_dev->descriptor.bcdUSB) >= 0x0201) {
retval = usb_get_bos_descriptor(usb_dev);
@@ -2844,18 +2823,14 @@ int usb_add_hcd(struct usb_hcd *hcd,
hcd->dev_policy = USB_DEVICE_AUTHORIZE_NONE;
break;
- case USB_AUTHORIZE_ALL:
- hcd->dev_policy = USB_DEVICE_AUTHORIZE_ALL;
- break;
-
case USB_AUTHORIZE_INTERNAL:
hcd->dev_policy = USB_DEVICE_AUTHORIZE_INTERNAL;
break;
+ case USB_AUTHORIZE_ALL:
case USB_AUTHORIZE_WIRED:
default:
- hcd->dev_policy = hcd->wireless ?
- USB_DEVICE_AUTHORIZE_NONE : USB_DEVICE_AUTHORIZE_ALL;
+ hcd->dev_policy = USB_DEVICE_AUTHORIZE_ALL;
break;
}
@@ -2899,9 +2874,6 @@ int usb_add_hcd(struct usb_hcd *hcd,
case HCD_USB2:
rhdev->speed = USB_SPEED_HIGH;
break;
- case HCD_USB25:
- rhdev->speed = USB_SPEED_WIRELESS;
- break;
case HCD_USB3:
rhdev->speed = USB_SPEED_SUPER;
break;
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index a739403a9e45..3c54b218301c 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -614,6 +614,29 @@ static int hub_ext_port_status(struct usb_hub *hub, int port1, int type,
ret = 0;
}
mutex_unlock(&hub->status_mutex);
+
+ /*
+ * There is no need to lock status_mutex here, because status_mutex
+ * protects hub->status, and the phy driver only checks the port
+ * status without changing the status.
+ */
+ if (!ret) {
+ struct usb_device *hdev = hub->hdev;
+
+ /*
+ * Only roothub will be notified of port state changes,
+ * since the USB PHY only cares about changes at the next
+ * level.
+ */
+ if (is_root_hub(hdev)) {
+ struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
+
+ if (hcd->usb_phy)
+ usb_phy_notify_port_status(hcd->usb_phy,
+ port1 - 1, *status, *change);
+ }
+ }
+
return ret;
}
@@ -2117,22 +2140,6 @@ EXPORT_SYMBOL_GPL(usb_set_device_state);
* USB-3.0 buses the address is assigned by the controller hardware
* and it usually is not the same as the device number.
*
- * WUSB devices are simple: they have no hubs behind, so the mapping
- * device <-> virtual port number becomes 1:1. Why? to simplify the
- * life of the device connection logic in
- * drivers/usb/wusbcore/devconnect.c. When we do the initial secret
- * handshake we need to assign a temporary address in the unauthorized
- * space. For simplicity we use the first virtual port number found to
- * be free [drivers/usb/wusbcore/devconnect.c:wusbhc_devconnect_ack()]
- * and that becomes it's address [X < 128] or its unauthorized address
- * [X | 0x80].
- *
- * We add 1 as an offset to the one-based USB-stack port number
- * (zero-based wusb virtual port index) for two reasons: (a) dev addr
- * 0 is reserved by USB for default address; (b) Linux's USB stack
- * uses always #1 for the root hub of the controller. So USB stack's
- * port #1, which is wusb virtual-port #0 has address #2.
- *
* Devices connected under xHCI are not as simple. The host controller
* supports virtualization, so the hardware assigns device addresses and
* the HCD must setup data structures before issuing a set address
@@ -2145,19 +2152,13 @@ static void choose_devnum(struct usb_device *udev)
/* be safe when more hub events are proceed in parallel */
mutex_lock(&bus->devnum_next_mutex);
- if (udev->wusb) {
- devnum = udev->portnum + 1;
- BUG_ON(test_bit(devnum, bus->devmap.devicemap));
- } else {
- /* Try to allocate the next devnum beginning at
- * bus->devnum_next. */
- devnum = find_next_zero_bit(bus->devmap.devicemap, 128,
- bus->devnum_next);
- if (devnum >= 128)
- devnum = find_next_zero_bit(bus->devmap.devicemap,
- 128, 1);
- bus->devnum_next = (devnum >= 127 ? 1 : devnum + 1);
- }
+
+ /* Try to allocate the next devnum beginning at bus->devnum_next. */
+ devnum = find_next_zero_bit(bus->devmap.devicemap, 128,
+ bus->devnum_next);
+ if (devnum >= 128)
+ devnum = find_next_zero_bit(bus->devmap.devicemap, 128, 1);
+ bus->devnum_next = (devnum >= 127 ? 1 : devnum + 1);
if (devnum < 128) {
set_bit(devnum, bus->devmap.devicemap);
udev->devnum = devnum;
@@ -2175,9 +2176,7 @@ static void release_devnum(struct usb_device *udev)
static void update_devnum(struct usb_device *udev, int devnum)
{
- /* The address for a WUSB device is managed by wusbcore. */
- if (!udev->wusb)
- udev->devnum = devnum;
+ udev->devnum = devnum;
if (!udev->devaddr)
udev->devaddr = (u8)devnum;
}
@@ -2670,15 +2669,6 @@ int usb_authorize_device(struct usb_device *usb_dev)
goto error_autoresume;
}
- if (usb_dev->wusb) {
- result = usb_get_device_descriptor(usb_dev, sizeof(usb_dev->descriptor));
- if (result < 0) {
- dev_err(&usb_dev->dev, "can't re-read device descriptor for "
- "authorization: %d\n", result);
- goto error_device_descriptor;
- }
- }
-
usb_dev->authorized = 1;
/* Choose and set the configuration. This registers the interfaces
* with the driver core and lets interface drivers bind to them.
@@ -2695,7 +2685,6 @@ int usb_authorize_device(struct usb_device *usb_dev)
}
dev_info(&usb_dev->dev, "authorized to connect\n");
-error_device_descriptor:
usb_autosuspend_device(usb_dev);
error_autoresume:
out_authorized:
@@ -2778,17 +2767,6 @@ out:
return USB_SSP_GEN_UNKNOWN;
}
-/* Returns 1 if @hub is a WUSB root hub, 0 otherwise */
-static unsigned hub_is_wusb(struct usb_hub *hub)
-{
- struct usb_hcd *hcd;
- if (hub->hdev->parent != NULL) /* not a root hub? */
- return 0;
- hcd = bus_to_hcd(hub->hdev->bus);
- return hcd->wireless;
-}
-
-
#ifdef CONFIG_USB_FEW_INIT_RETRIES
#define PORT_RESET_TRIES 2
#define SET_ADDRESS_TRIES 1
@@ -2941,9 +2919,7 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
udev->tx_lanes = 1;
udev->ssp_rate = USB_SSP_GEN_UNKNOWN;
}
- if (hub_is_wusb(hub))
- udev->speed = USB_SPEED_WIRELESS;
- else if (udev->ssp_rate != USB_SSP_GEN_UNKNOWN)
+ if (udev->ssp_rate != USB_SSP_GEN_UNKNOWN)
udev->speed = USB_SPEED_SUPER_PLUS;
else if (hub_is_superspeed(hub->hdev))
udev->speed = USB_SPEED_SUPER;
@@ -4718,6 +4694,67 @@ static int hub_enable_device(struct usb_device *udev)
return hcd->driver->enable_device(hcd, udev);
}
+/*
+ * Get the bMaxPacketSize0 value during initialization by reading the
+ * device's device descriptor. Since we don't already know this value,
+ * the transfer is unsafe and it ignores I/O errors, only testing for
+ * reasonable received values.
+ *
+ * For "old scheme" initialization, size will be 8 so we read just the
+ * start of the device descriptor, which should work okay regardless of
+ * the actual bMaxPacketSize0 value. For "new scheme" initialization,
+ * size will be 64 (and buf will point to a sufficiently large buffer),
+ * which might not be kosher according to the USB spec but it's what
+ * Windows does and what many devices expect.
+ *
+ * Returns: bMaxPacketSize0 or a negative error code.
+ */
+static int get_bMaxPacketSize0(struct usb_device *udev,
+ struct usb_device_descriptor *buf, int size, bool first_time)
+{
+ int i, rc;
+
+ /*
+ * Retry on all errors; some devices are flakey.
+ * 255 is for WUSB devices, we actually need to use
+ * 512 (WUSB1.0[4.8.1]).
+ */
+ for (i = 0; i < GET_MAXPACKET0_TRIES; ++i) {
+ /* Start with invalid values in case the transfer fails */
+ buf->bDescriptorType = buf->bMaxPacketSize0 = 0;
+ rc = usb_control_msg(udev, usb_rcvaddr0pipe(),
+ USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
+ USB_DT_DEVICE << 8, 0,
+ buf, size,
+ initial_descriptor_timeout);
+ switch (buf->bMaxPacketSize0) {
+ case 8: case 16: case 32: case 64: case 9:
+ if (buf->bDescriptorType == USB_DT_DEVICE) {
+ rc = buf->bMaxPacketSize0;
+ break;
+ }
+ fallthrough;
+ default:
+ if (rc >= 0)
+ rc = -EPROTO;
+ break;
+ }
+
+ /*
+ * Some devices time out if they are powered on
+ * when already connected. They need a second
+ * reset, so return early. But only on the first
+ * attempt, lest we get into a time-out/reset loop.
+ */
+ if (rc > 0 || (rc == -ETIMEDOUT && first_time &&
+ udev->speed > USB_SPEED_FULL))
+ break;
+ }
+ return rc;
+}
+
+#define GET_DESCRIPTOR_BUFSIZE 64
+
/* Reset device, (re)assign address, get device descriptor.
* Device connection must be stable, no more debouncing needed.
* Returns device in USB_STATE_ADDRESS, except on error.
@@ -4727,10 +4764,17 @@ static int hub_enable_device(struct usb_device *udev)
* the port lock. For a newly detected device that is not accessible
* through any global pointers, it's not necessary to lock the device,
* but it is still necessary to lock the port.
+ *
+ * For a newly detected device, @dev_descr must be NULL. The device
+ * descriptor retrieved from the device will then be stored in
+ * @udev->descriptor. For an already existing device, @dev_descr
+ * must be non-NULL. The device descriptor will be stored there,
+ * not in @udev->descriptor, because descriptors for registered
+ * devices are meant to be immutable.
*/
static int
hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
- int retry_counter)
+ int retry_counter, struct usb_device_descriptor *dev_descr)
{
struct usb_device *hdev = hub->hdev;
struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
@@ -4742,6 +4786,13 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
int devnum = udev->devnum;
const char *driver_name;
bool do_new_scheme;
+ const bool initial = !dev_descr;
+ int maxp0;
+ struct usb_device_descriptor *buf, *descr;
+
+ buf = kmalloc(GET_DESCRIPTOR_BUFSIZE, GFP_NOIO);
+ if (!buf)
+ return -ENOMEM;
/* root hub ports have a slightly longer reset period
* (from USB 2.0 spec, section 7.1.7.5)
@@ -4774,38 +4825,34 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
}
oldspeed = udev->speed;
- /* USB 2.0 section 5.5.3 talks about ep0 maxpacket ...
- * it's fixed size except for full speed devices.
- * For Wireless USB devices, ep0 max packet is always 512 (tho
- * reported as 0xff in the device descriptor). WUSB1.0[4.8.1].
- */
- switch (udev->speed) {
- case USB_SPEED_SUPER_PLUS:
- case USB_SPEED_SUPER:
- case USB_SPEED_WIRELESS: /* fixed at 512 */
- udev->ep0.desc.wMaxPacketSize = cpu_to_le16(512);
- break;
- case USB_SPEED_HIGH: /* fixed at 64 */
- udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
- break;
- case USB_SPEED_FULL: /* 8, 16, 32, or 64 */
- /* to determine the ep0 maxpacket size, try to read
- * the device descriptor to get bMaxPacketSize0 and
- * then correct our initial guess.
+ if (initial) {
+ /* USB 2.0 section 5.5.3 talks about ep0 maxpacket ...
+ * it's fixed size except for full speed devices.
*/
- udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
- break;
- case USB_SPEED_LOW: /* fixed at 8 */
- udev->ep0.desc.wMaxPacketSize = cpu_to_le16(8);
- break;
- default:
- goto fail;
+ switch (udev->speed) {
+ case USB_SPEED_SUPER_PLUS:
+ case USB_SPEED_SUPER:
+ udev->ep0.desc.wMaxPacketSize = cpu_to_le16(512);
+ break;
+ case USB_SPEED_HIGH: /* fixed at 64 */
+ udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
+ break;
+ case USB_SPEED_FULL: /* 8, 16, 32, or 64 */
+ /* to determine the ep0 maxpacket size, try to read
+ * the device descriptor to get bMaxPacketSize0 and
+ * then correct our initial guess.
+ */
+ udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
+ break;
+ case USB_SPEED_LOW: /* fixed at 8 */
+ udev->ep0.desc.wMaxPacketSize = cpu_to_le16(8);
+ break;
+ default:
+ goto fail;
+ }
}
- if (udev->speed == USB_SPEED_WIRELESS)
- speed = "variable speed Wireless";
- else
- speed = usb_speed_string(udev->speed);
+ speed = usb_speed_string(udev->speed);
/*
* The controller driver may be NULL if the controller device
@@ -4822,22 +4869,24 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
if (udev->speed < USB_SPEED_SUPER)
dev_info(&udev->dev,
"%s %s USB device number %d using %s\n",
- (udev->config) ? "reset" : "new", speed,
+ (initial ? "new" : "reset"), speed,
devnum, driver_name);
- /* Set up TT records, if needed */
- if (hdev->tt) {
- udev->tt = hdev->tt;
- udev->ttport = hdev->ttport;
- } else if (udev->speed != USB_SPEED_HIGH
- && hdev->speed == USB_SPEED_HIGH) {
- if (!hub->tt.hub) {
- dev_err(&udev->dev, "parent hub has no TT\n");
- retval = -EINVAL;
- goto fail;
+ if (initial) {
+ /* Set up TT records, if needed */
+ if (hdev->tt) {
+ udev->tt = hdev->tt;
+ udev->ttport = hdev->ttport;
+ } else if (udev->speed != USB_SPEED_HIGH
+ && hdev->speed == USB_SPEED_HIGH) {
+ if (!hub->tt.hub) {
+ dev_err(&udev->dev, "parent hub has no TT\n");
+ retval = -EINVAL;
+ goto fail;
+ }
+ udev->tt = &hub->tt;
+ udev->ttport = port1;
}
- udev->tt = &hub->tt;
- udev->ttport = port1;
}
/* Why interleave GET_DESCRIPTOR and SET_ADDRESS this way?
@@ -4861,9 +4910,6 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
}
if (do_new_scheme) {
- struct usb_device_descriptor *buf;
- int r = 0;
-
retval = hub_enable_device(udev);
if (retval < 0) {
dev_err(&udev->dev,
@@ -4872,52 +4918,14 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
goto fail;
}
-#define GET_DESCRIPTOR_BUFSIZE 64
- buf = kmalloc(GET_DESCRIPTOR_BUFSIZE, GFP_NOIO);
- if (!buf) {
- retval = -ENOMEM;
- continue;
- }
-
- /* Retry on all errors; some devices are flakey.
- * 255 is for WUSB devices, we actually need to use
- * 512 (WUSB1.0[4.8.1]).
- */
- for (operations = 0; operations < GET_MAXPACKET0_TRIES;
- ++operations) {
- buf->bMaxPacketSize0 = 0;
- r = usb_control_msg(udev, usb_rcvaddr0pipe(),
- USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
- USB_DT_DEVICE << 8, 0,
- buf, GET_DESCRIPTOR_BUFSIZE,
- initial_descriptor_timeout);
- switch (buf->bMaxPacketSize0) {
- case 8: case 16: case 32: case 64: case 255:
- if (buf->bDescriptorType ==
- USB_DT_DEVICE) {
- r = 0;
- break;
- }
- fallthrough;
- default:
- if (r == 0)
- r = -EPROTO;
- break;
- }
- /*
- * Some devices time out if they are powered on
- * when already connected. They need a second
- * reset. But only on the first attempt,
- * lest we get into a time out/reset loop
- */
- if (r == 0 || (r == -ETIMEDOUT &&
- retries == 0 &&
- udev->speed > USB_SPEED_FULL))
- break;
+ maxp0 = get_bMaxPacketSize0(udev, buf,
+ GET_DESCRIPTOR_BUFSIZE, retries == 0);
+ if (maxp0 > 0 && !initial &&
+ maxp0 != udev->descriptor.bMaxPacketSize0) {
+ dev_err(&udev->dev, "device reset changed ep0 maxpacket size!\n");
+ retval = -ENODEV;
+ goto fail;
}
- udev->descriptor.bMaxPacketSize0 =
- buf->bMaxPacketSize0;
- kfree(buf);
retval = hub_port_reset(hub, port1, udev, delay, false);
if (retval < 0) /* error or disconnect */
@@ -4928,71 +4936,68 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
retval = -ENODEV;
goto fail;
}
- if (r) {
- if (r != -ENODEV)
+ if (maxp0 < 0) {
+ if (maxp0 != -ENODEV)
dev_err(&udev->dev, "device descriptor read/64, error %d\n",
- r);
- retval = -EMSGSIZE;
+ maxp0);
+ retval = maxp0;
continue;
}
-#undef GET_DESCRIPTOR_BUFSIZE
+ }
+
+ for (operations = 0; operations < SET_ADDRESS_TRIES; ++operations) {
+ retval = hub_set_address(udev, devnum);
+ if (retval >= 0)
+ break;
+ msleep(200);
+ }
+ if (retval < 0) {
+ if (retval != -ENODEV)
+ dev_err(&udev->dev, "device not accepting address %d, error %d\n",
+ devnum, retval);
+ goto fail;
+ }
+ if (udev->speed >= USB_SPEED_SUPER) {
+ devnum = udev->devnum;
+ dev_info(&udev->dev,
+ "%s SuperSpeed%s%s USB device number %d using %s\n",
+ (udev->config) ? "reset" : "new",
+ (udev->speed == USB_SPEED_SUPER_PLUS) ?
+ " Plus" : "",
+ (udev->ssp_rate == USB_SSP_GEN_2x2) ?
+ " Gen 2x2" :
+ (udev->ssp_rate == USB_SSP_GEN_2x1) ?
+ " Gen 2x1" :
+ (udev->ssp_rate == USB_SSP_GEN_1x2) ?
+ " Gen 1x2" : "",
+ devnum, driver_name);
}
/*
- * If device is WUSB, we already assigned an
- * unauthorized address in the Connect Ack sequence;
- * authorization will assign the final address.
+ * cope with hardware quirkiness:
+ * - let SET_ADDRESS settle, some device hardware wants it
+ * - read ep0 maxpacket even for high and low speed,
*/
- if (udev->wusb == 0) {
- for (operations = 0; operations < SET_ADDRESS_TRIES; ++operations) {
- retval = hub_set_address(udev, devnum);
- if (retval >= 0)
- break;
- msleep(200);
- }
- if (retval < 0) {
- if (retval != -ENODEV)
- dev_err(&udev->dev, "device not accepting address %d, error %d\n",
- devnum, retval);
- goto fail;
- }
- if (udev->speed >= USB_SPEED_SUPER) {
- devnum = udev->devnum;
- dev_info(&udev->dev,
- "%s SuperSpeed%s%s USB device number %d using %s\n",
- (udev->config) ? "reset" : "new",
- (udev->speed == USB_SPEED_SUPER_PLUS) ?
- " Plus" : "",
- (udev->ssp_rate == USB_SSP_GEN_2x2) ?
- " Gen 2x2" :
- (udev->ssp_rate == USB_SSP_GEN_2x1) ?
- " Gen 2x1" :
- (udev->ssp_rate == USB_SSP_GEN_1x2) ?
- " Gen 1x2" : "",
- devnum, driver_name);
- }
+ msleep(10);
- /* cope with hardware quirkiness:
- * - let SET_ADDRESS settle, some device hardware wants it
- * - read ep0 maxpacket even for high and low speed,
- */
- msleep(10);
- if (do_new_scheme)
- break;
- }
+ if (do_new_scheme)
+ break;
- retval = usb_get_device_descriptor(udev, 8);
- if (retval < 8) {
+ maxp0 = get_bMaxPacketSize0(udev, buf, 8, retries == 0);
+ if (maxp0 < 0) {
+ retval = maxp0;
if (retval != -ENODEV)
dev_err(&udev->dev,
"device descriptor read/8, error %d\n",
retval);
- if (retval >= 0)
- retval = -EMSGSIZE;
} else {
u32 delay;
- retval = 0;
+ if (!initial && maxp0 != udev->descriptor.bMaxPacketSize0) {
+ dev_err(&udev->dev, "device reset changed ep0 maxpacket size!\n");
+ retval = -ENODEV;
+ goto fail;
+ }
delay = udev->parent->hub_delay;
udev->hub_delay = min_t(u32, delay,
@@ -5011,54 +5016,67 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
goto fail;
/*
- * Some superspeed devices have finished the link training process
- * and attached to a superspeed hub port, but the device descriptor
- * got from those devices show they aren't superspeed devices. Warm
- * reset the port attached by the devices can fix them.
+ * Check the ep0 maxpacket guess and correct it if necessary.
+ * maxp0 is the value stored in the device descriptor;
+ * i is the value it encodes (logarithmic for SuperSpeed or greater).
*/
- if ((udev->speed >= USB_SPEED_SUPER) &&
- (le16_to_cpu(udev->descriptor.bcdUSB) < 0x0300)) {
- dev_err(&udev->dev, "got a wrong device descriptor, "
- "warm reset device\n");
- hub_port_reset(hub, port1, udev,
- HUB_BH_RESET_TIME, true);
- retval = -EINVAL;
- goto fail;
- }
-
- if (udev->descriptor.bMaxPacketSize0 == 0xff ||
- udev->speed >= USB_SPEED_SUPER)
- i = 512;
- else
- i = udev->descriptor.bMaxPacketSize0;
- if (usb_endpoint_maxp(&udev->ep0.desc) != i) {
- if (udev->speed == USB_SPEED_LOW ||
- !(i == 8 || i == 16 || i == 32 || i == 64)) {
- dev_err(&udev->dev, "Invalid ep0 maxpacket: %d\n", i);
- retval = -EMSGSIZE;
- goto fail;
- }
+ i = maxp0;
+ if (udev->speed >= USB_SPEED_SUPER) {
+ if (maxp0 <= 16)
+ i = 1 << maxp0;
+ else
+ i = 0; /* Invalid */
+ }
+ if (usb_endpoint_maxp(&udev->ep0.desc) == i) {
+ ; /* Initial ep0 maxpacket guess is right */
+ } else if ((udev->speed == USB_SPEED_FULL ||
+ udev->speed == USB_SPEED_HIGH) &&
+ (i == 8 || i == 16 || i == 32 || i == 64)) {
+ /* Initial guess is wrong; use the descriptor's value */
if (udev->speed == USB_SPEED_FULL)
dev_dbg(&udev->dev, "ep0 maxpacket = %d\n", i);
else
dev_warn(&udev->dev, "Using ep0 maxpacket: %d\n", i);
udev->ep0.desc.wMaxPacketSize = cpu_to_le16(i);
usb_ep0_reinit(udev);
+ } else {
+ /* Initial guess is wrong and descriptor's value is invalid */
+ dev_err(&udev->dev, "Invalid ep0 maxpacket: %d\n", maxp0);
+ retval = -EMSGSIZE;
+ goto fail;
}
- retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);
- if (retval < (signed)sizeof(udev->descriptor)) {
+ descr = usb_get_device_descriptor(udev);
+ if (IS_ERR(descr)) {
+ retval = PTR_ERR(descr);
if (retval != -ENODEV)
dev_err(&udev->dev, "device descriptor read/all, error %d\n",
retval);
- if (retval >= 0)
- retval = -ENOMSG;
+ goto fail;
+ }
+ if (initial)
+ udev->descriptor = *descr;
+ else
+ *dev_descr = *descr;
+ kfree(descr);
+
+ /*
+ * Some superspeed devices have finished the link training process
+ * and attached to a superspeed hub port, but the device descriptor
+ * got from those devices show they aren't superspeed devices. Warm
+ * reset the port attached by the devices can fix them.
+ */
+ if ((udev->speed >= USB_SPEED_SUPER) &&
+ (le16_to_cpu(udev->descriptor.bcdUSB) < 0x0300)) {
+ dev_err(&udev->dev, "got a wrong device descriptor, warm reset device\n");
+ hub_port_reset(hub, port1, udev, HUB_BH_RESET_TIME, true);
+ retval = -EINVAL;
goto fail;
}
usb_detect_quirks(udev);
- if (udev->wusb == 0 && le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0201) {
+ if (le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0201) {
retval = usb_get_bos_descriptor(udev);
if (!retval) {
udev->lpm_capable = usb_device_supports_lpm(udev);
@@ -5078,6 +5096,7 @@ fail:
hub_port_disable(hub, port1, 0);
update_devnum(udev, devnum); /* for disconnect processing */
}
+ kfree(buf);
return retval;
}
@@ -5158,7 +5177,7 @@ hub_power_remaining(struct usb_hub *hub)
static int descriptors_changed(struct usb_device *udev,
- struct usb_device_descriptor *old_device_descriptor,
+ struct usb_device_descriptor *new_device_descriptor,
struct usb_host_bos *old_bos)
{
int changed = 0;
@@ -5169,8 +5188,8 @@ static int descriptors_changed(struct usb_device *udev,
int length;
char *buf;
- if (memcmp(&udev->descriptor, old_device_descriptor,
- sizeof(*old_device_descriptor)) != 0)
+ if (memcmp(&udev->descriptor, new_device_descriptor,
+ sizeof(*new_device_descriptor)) != 0)
return 1;
if ((old_bos && !udev->bos) || (!old_bos && udev->bos))
@@ -5333,7 +5352,6 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
usb_set_device_state(udev, USB_STATE_POWERED);
udev->bus_mA = hub->mA_per_port;
udev->level = hdev->level + 1;
- udev->wusb = hub_is_wusb(hub);
/* Devices connected to SuperSpeed hubs are USB 3.0 or later */
if (hub_is_superspeed(hub->hdev))
@@ -5348,7 +5366,7 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
}
/* reset (non-USB 3.0 devices) and get descriptor */
- status = hub_port_init(hub, udev, port1, i);
+ status = hub_port_init(hub, udev, port1, i, NULL);
if (status < 0)
goto loop;
@@ -5495,9 +5513,8 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
{
struct usb_port *port_dev = hub->ports[port1 - 1];
struct usb_device *udev = port_dev->child;
- struct usb_device_descriptor descriptor;
+ struct usb_device_descriptor *descr;
int status = -ENODEV;
- int retval;
dev_dbg(&port_dev->dev, "status %04x, change %04x, %s\n", portstatus,
portchange, portspeed(hub, portstatus));
@@ -5524,23 +5541,20 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
* changed device descriptors before resuscitating the
* device.
*/
- descriptor = udev->descriptor;
- retval = usb_get_device_descriptor(udev,
- sizeof(udev->descriptor));
- if (retval < 0) {
+ descr = usb_get_device_descriptor(udev);
+ if (IS_ERR(descr)) {
dev_dbg(&udev->dev,
- "can't read device descriptor %d\n",
- retval);
+ "can't read device descriptor %ld\n",
+ PTR_ERR(descr));
} else {
- if (descriptors_changed(udev, &descriptor,
+ if (descriptors_changed(udev, descr,
udev->bos)) {
dev_dbg(&udev->dev,
"device descriptor has changed\n");
- /* for disconnect() calls */
- udev->descriptor = descriptor;
} else {
status = 0; /* Nothing to do */
}
+ kfree(descr);
}
#ifdef CONFIG_PM
} else if (udev->state == USB_STATE_SUSPENDED &&
@@ -5982,7 +5996,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
struct usb_device *parent_hdev = udev->parent;
struct usb_hub *parent_hub;
struct usb_hcd *hcd = bus_to_hcd(udev->bus);
- struct usb_device_descriptor descriptor = udev->descriptor;
+ struct usb_device_descriptor descriptor;
struct usb_host_bos *bos;
int i, j, ret = 0;
int port1 = udev->portnum;
@@ -6018,7 +6032,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
/* ep0 maxpacket size may change; let the HCD know about it.
* Other endpoints will be handled by re-enumeration. */
usb_ep0_reinit(udev);
- ret = hub_port_init(parent_hub, udev, port1, i);
+ ret = hub_port_init(parent_hub, udev, port1, i, &descriptor);
if (ret >= 0 || ret == -ENOTCONN || ret == -ENODEV)
break;
}
@@ -6030,7 +6044,6 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
/* Device might have changed firmware (DFU or similar) */
if (descriptors_changed(udev, &descriptor, bos)) {
dev_info(&udev->dev, "device firmware changed\n");
- udev->descriptor = descriptor; /* for disconnect() calls */
goto re_enumerate;
}
diff --git a/drivers/usb/core/ledtrig-usbport.c b/drivers/usb/core/ledtrig-usbport.c
index ba371a24ff78..85c999f71ad7 100644
--- a/drivers/usb/core/ledtrig-usbport.c
+++ b/drivers/usb/core/ledtrig-usbport.c
@@ -350,18 +350,7 @@ static struct led_trigger usbport_led_trigger = {
.deactivate = usbport_trig_deactivate,
};
-static int __init usbport_trig_init(void)
-{
- return led_trigger_register(&usbport_led_trigger);
-}
-
-static void __exit usbport_trig_exit(void)
-{
- led_trigger_unregister(&usbport_led_trigger);
-}
-
-module_init(usbport_trig_init);
-module_exit(usbport_trig_exit);
+module_led_trigger(usbport_led_trigger);
MODULE_AUTHOR("Rafał Miłecki <rafal@milecki.pl>");
MODULE_DESCRIPTION("USB port trigger");
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index b5811620f1de..077dfe48d01c 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -9,6 +9,7 @@
#include <linux/pci.h> /* for scatterlist macros */
#include <linux/usb.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/timer.h>
@@ -1040,40 +1041,35 @@ char *usb_cache_string(struct usb_device *udev, int index)
EXPORT_SYMBOL_GPL(usb_cache_string);
/*
- * usb_get_device_descriptor - (re)reads the device descriptor (usbcore)
- * @dev: the device whose device descriptor is being updated
- * @size: how much of the descriptor to read
+ * usb_get_device_descriptor - read the device descriptor
+ * @udev: the device whose device descriptor should be read
*
* Context: task context, might sleep.
*
- * Updates the copy of the device descriptor stored in the device structure,
- * which dedicates space for this purpose.
- *
* Not exported, only for use by the core. If drivers really want to read
* the device descriptor directly, they can call usb_get_descriptor() with
* type = USB_DT_DEVICE and index = 0.
*
- * This call is synchronous, and may not be used in an interrupt context.
- *
- * Return: The number of bytes received on success, or else the status code
- * returned by the underlying usb_control_msg() call.
+ * Returns: a pointer to a dynamically allocated usb_device_descriptor
+ * structure (which the caller must deallocate), or an ERR_PTR value.
*/
-int usb_get_device_descriptor(struct usb_device *dev, unsigned int size)
+struct usb_device_descriptor *usb_get_device_descriptor(struct usb_device *udev)
{
struct usb_device_descriptor *desc;
int ret;
- if (size > sizeof(*desc))
- return -EINVAL;
desc = kmalloc(sizeof(*desc), GFP_NOIO);
if (!desc)
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
+
+ ret = usb_get_descriptor(udev, USB_DT_DEVICE, 0, desc, sizeof(*desc));
+ if (ret == sizeof(*desc))
+ return desc;
- ret = usb_get_descriptor(dev, USB_DT_DEVICE, 0, desc, size);
if (ret >= 0)
- memcpy(&dev->descriptor, desc, size);
+ ret = -EMSGSIZE;
kfree(desc);
- return ret;
+ return ERR_PTR(ret);
}
/*
diff --git a/drivers/usb/core/of.c b/drivers/usb/core/of.c
index 617e92569b2c..db4ccf9ce3d9 100644
--- a/drivers/usb/core/of.c
+++ b/drivers/usb/core/of.c
@@ -8,7 +8,6 @@
*/
#include <linux/of.h>
-#include <linux/of_platform.h>
#include <linux/usb/of.h>
/**
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c
index 323dc02becbe..5d21718afb05 100644
--- a/drivers/usb/core/sysfs.c
+++ b/drivers/usb/core/sysfs.c
@@ -161,9 +161,6 @@ static ssize_t speed_show(struct device *dev, struct device_attribute *attr,
case USB_SPEED_HIGH:
speed = "480";
break;
- case USB_SPEED_WIRELESS:
- speed = "480";
- break;
case USB_SPEED_SUPER:
speed = "5000";
break;
diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c
index 9f3c54032556..7576920e2d5a 100644
--- a/drivers/usb/core/urb.c
+++ b/drivers/usb/core/urb.c
@@ -480,8 +480,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
urb->iso_frame_desc[n].status = -EXDEV;
urb->iso_frame_desc[n].actual_length = 0;
}
- } else if (urb->num_sgs && !urb->dev->bus->no_sg_constraint &&
- dev->speed != USB_SPEED_WIRELESS) {
+ } else if (urb->num_sgs && !urb->dev->bus->no_sg_constraint) {
struct scatterlist *sg;
int i;
@@ -540,17 +539,9 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
case USB_ENDPOINT_XFER_ISOC:
case USB_ENDPOINT_XFER_INT:
/* too small? */
- switch (dev->speed) {
- case USB_SPEED_WIRELESS:
- if ((urb->interval < 6)
- && (xfertype == USB_ENDPOINT_XFER_INT))
- return -EINVAL;
- fallthrough;
- default:
- if (urb->interval <= 0)
- return -EINVAL;
- break;
- }
+ if (urb->interval <= 0)
+ return -EINVAL;
+
/* too big? */
switch (dev->speed) {
case USB_SPEED_SUPER_PLUS:
@@ -560,10 +551,6 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
return -EINVAL;
max = 1 << 15;
break;
- case USB_SPEED_WIRELESS:
- if (urb->interval > 16)
- return -EINVAL;
- break;
case USB_SPEED_HIGH: /* units are microframes */
/* NOTE usb handles 2^15 */
if (urb->interval > (1024 * 8))
@@ -587,10 +574,8 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
default:
return -EINVAL;
}
- if (dev->speed != USB_SPEED_WIRELESS) {
- /* Round down to a power of 2, no more than max */
- urb->interval = min(max, 1 << ilog2(urb->interval));
- }
+ /* Round down to a power of 2, no more than max */
+ urb->interval = min(max, 1 << ilog2(urb->interval));
}
return usb_hcd_submit_urb(urb, mem_flags);
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 901ec732321c..2a938cf47ccd 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -25,6 +25,7 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
+#include <linux/of.h>
#include <linux/string.h>
#include <linux/bitops.h>
#include <linux/slab.h>
@@ -601,14 +602,6 @@ struct device_type usb_device_type = {
#endif
};
-
-/* Returns 1 if @usb_bus is WUSB, 0 otherwise */
-static unsigned usb_bus_is_wusb(struct usb_bus *bus)
-{
- struct usb_hcd *hcd = bus_to_hcd(bus);
- return hcd->wireless;
-}
-
static bool usb_dev_authorized(struct usb_device *dev, struct usb_hcd *hcd)
{
struct usb_hub *hub;
@@ -652,7 +645,6 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
{
struct usb_device *dev;
struct usb_hcd *usb_hcd = bus_to_hcd(bus);
- unsigned root_hub = 0;
unsigned raw_port = port1;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
@@ -702,7 +694,6 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
dev->dev.parent = bus->controller;
device_set_of_node_from_dev(&dev->dev, bus->sysdev);
dev_set_name(&dev->dev, "usb%d", bus->busnum);
- root_hub = 1;
} else {
/* match any labeling on the hubs; it's one-based */
if (parent->devpath[0] == '0') {
@@ -748,9 +739,6 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
#endif
dev->authorized = usb_dev_authorized(dev, usb_hcd);
- if (!root_hub)
- dev->wusb = usb_bus_is_wusb(bus) ? 1 : 0;
-
return dev;
}
EXPORT_SYMBOL_GPL(usb_alloc_dev);
@@ -1101,6 +1089,9 @@ static int __init usb_init(void)
retval = usb_major_init();
if (retval)
goto major_init_failed;
+ retval = class_register(&usbmisc_class);
+ if (retval)
+ goto class_register_failed;
retval = usb_register(&usbfs_driver);
if (retval)
goto driver_register_failed;
@@ -1120,6 +1111,8 @@ hub_init_failed:
usb_devio_init_failed:
usb_deregister(&usbfs_driver);
driver_register_failed:
+ class_unregister(&usbmisc_class);
+class_register_failed:
usb_major_cleanup();
major_init_failed:
bus_unregister_notifier(&usb_bus_type, &usb_bus_nb);
@@ -1147,6 +1140,7 @@ static void __exit usb_exit(void)
usb_deregister(&usbfs_driver);
usb_devio_cleanup();
usb_hub_cleanup();
+ class_unregister(&usbmisc_class);
bus_unregister_notifier(&usb_bus_type, &usb_bus_nb);
bus_unregister(&usb_bus_type);
usb_acpi_unregister();
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index ffe3f6818e9c..60363153fc3f 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -43,8 +43,8 @@ extern bool usb_endpoint_is_ignored(struct usb_device *udev,
struct usb_endpoint_descriptor *epd);
extern int usb_remove_device(struct usb_device *udev);
-extern int usb_get_device_descriptor(struct usb_device *dev,
- unsigned int size);
+extern struct usb_device_descriptor *usb_get_device_descriptor(
+ struct usb_device *udev);
extern int usb_set_isoch_delay(struct usb_device *dev);
extern int usb_get_bos_descriptor(struct usb_device *dev);
extern void usb_release_bos_descriptor(struct usb_device *dev);
@@ -141,6 +141,7 @@ static inline int usb_disable_usb2_hardware_lpm(struct usb_device *udev)
#endif
+extern const struct class usbmisc_class;
extern const struct bus_type usb_bus_type;
extern struct mutex usb_port_peer_mutex;
extern struct device_type usb_device_type;
diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h
index 0bb4c0c845bf..c92a1da46a01 100644
--- a/drivers/usb/dwc2/core.h
+++ b/drivers/usb/dwc2/core.h
@@ -1330,6 +1330,7 @@ irqreturn_t dwc2_handle_common_intr(int irq, void *dev);
/* The device ID match table */
extern const struct of_device_id dwc2_of_match_table[];
extern const struct acpi_device_id dwc2_acpi_match[];
+extern const struct pci_device_id dwc2_pci_ids[];
int dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg);
int dwc2_lowlevel_hw_disable(struct dwc2_hsotg *hsotg);
diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c
index 8b15742d9e8a..b517a7216de2 100644
--- a/drivers/usb/dwc2/gadget.c
+++ b/drivers/usb/dwc2/gadget.c
@@ -22,7 +22,6 @@
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/slab.h>
-#include <linux/of_platform.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c
index c9740caa5974..0144ca8350c3 100644
--- a/drivers/usb/dwc2/hcd_intr.c
+++ b/drivers/usb/dwc2/hcd_intr.c
@@ -2203,11 +2203,13 @@ static void dwc2_hc_intr(struct dwc2_hsotg *hsotg)
irqreturn_t dwc2_handle_hcd_intr(struct dwc2_hsotg *hsotg)
{
u32 gintsts, dbg_gintsts;
- irqreturn_t retval = IRQ_NONE;
+ irqreturn_t retval = IRQ_HANDLED;
if (!dwc2_is_controller_alive(hsotg)) {
dev_warn(hsotg->dev, "Controller is dead\n");
return retval;
+ } else {
+ retval = IRQ_NONE;
}
spin_lock(&hsotg->lock);
diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c
index 4c7c3dd15f9b..93f52e371cdd 100644
--- a/drivers/usb/dwc2/params.c
+++ b/drivers/usb/dwc2/params.c
@@ -7,9 +7,14 @@
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/usb/of.h>
+#include <linux/pci_ids.h>
+#include <linux/pci.h>
#include "core.h"
+#define PCI_PRODUCT_ID_HAPS_HSOTG 0xabc0
+#define PCI_DEVICE_ID_LOONGSON_DWC2 0x7a04
+
static void dwc2_set_bcm_params(struct dwc2_hsotg *hsotg)
{
struct dwc2_core_params *p = &hsotg->params;
@@ -55,6 +60,14 @@ static void dwc2_set_jz4775_params(struct dwc2_hsotg *hsotg)
!device_property_read_bool(hsotg->dev, "disable-over-current");
}
+static void dwc2_set_loongson_params(struct dwc2_hsotg *hsotg)
+{
+ struct dwc2_core_params *p = &hsotg->params;
+
+ p->phy_utmi_width = 8;
+ p->power_down = DWC2_POWER_DOWN_PARAM_PARTIAL;
+}
+
static void dwc2_set_x1600_params(struct dwc2_hsotg *hsotg)
{
struct dwc2_core_params *p = &hsotg->params;
@@ -302,6 +315,23 @@ const struct acpi_device_id dwc2_acpi_match[] = {
};
MODULE_DEVICE_TABLE(acpi, dwc2_acpi_match);
+const struct pci_device_id dwc2_pci_ids[] = {
+ {
+ PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS, PCI_PRODUCT_ID_HAPS_HSOTG),
+ },
+ {
+ PCI_DEVICE(PCI_VENDOR_ID_STMICRO,
+ PCI_DEVICE_ID_STMICRO_USB_OTG),
+ },
+ {
+ PCI_DEVICE(PCI_VENDOR_ID_LOONGSON, PCI_DEVICE_ID_LOONGSON_DWC2),
+ .driver_data = (unsigned long)dwc2_set_loongson_params,
+ },
+ { /* end: all zeroes */ }
+};
+MODULE_DEVICE_TABLE(pci, dwc2_pci_ids);
+EXPORT_SYMBOL_GPL(dwc2_pci_ids);
+
static void dwc2_set_param_otg_cap(struct dwc2_hsotg *hsotg)
{
switch (hsotg->hw_params.op_mode) {
@@ -948,13 +978,20 @@ int dwc2_init_params(struct dwc2_hsotg *hsotg)
if (match && match->data) {
set_params = match->data;
set_params(hsotg);
- } else {
+ } else if (!match) {
const struct acpi_device_id *amatch;
+ const struct pci_device_id *pmatch = NULL;
amatch = acpi_match_device(dwc2_acpi_match, hsotg->dev);
if (amatch && amatch->driver_data) {
set_params = (set_params_cb)amatch->driver_data;
set_params(hsotg);
+ } else if (!amatch)
+ pmatch = pci_match_id(dwc2_pci_ids, to_pci_dev(hsotg->dev->parent));
+
+ if (pmatch && pmatch->driver_data) {
+ set_params = (set_params_cb)pmatch->driver_data;
+ set_params(hsotg);
}
}
diff --git a/drivers/usb/dwc2/pci.c b/drivers/usb/dwc2/pci.c
index b7306ed8be4c..f3a1e4232a31 100644
--- a/drivers/usb/dwc2/pci.c
+++ b/drivers/usb/dwc2/pci.c
@@ -24,7 +24,7 @@
#include <linux/platform_device.h>
#include <linux/usb/usb_phy_generic.h>
-#define PCI_PRODUCT_ID_HAPS_HSOTG 0xabc0
+#include "core.h"
static const char dwc2_driver_name[] = "dwc2-pci";
@@ -122,18 +122,6 @@ err:
return ret;
}
-static const struct pci_device_id dwc2_pci_ids[] = {
- {
- PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS, PCI_PRODUCT_ID_HAPS_HSOTG),
- },
- {
- PCI_DEVICE(PCI_VENDOR_ID_STMICRO,
- PCI_DEVICE_ID_STMICRO_USB_OTG),
- },
- { /* end: all zeroes */ }
-};
-MODULE_DEVICE_TABLE(pci, dwc2_pci_ids);
-
static struct pci_driver dwc2_pci_driver = {
.name = dwc2_driver_name,
.id_table = dwc2_pci_ids,
diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c
index 0a806f80217e..b1d48019e944 100644
--- a/drivers/usb/dwc2/platform.c
+++ b/drivers/usb/dwc2/platform.c
@@ -11,7 +11,7 @@
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/phy/phy.h>
diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig
index be954a9abbe0..98efcbb76c88 100644
--- a/drivers/usb/dwc3/Kconfig
+++ b/drivers/usb/dwc3/Kconfig
@@ -168,4 +168,14 @@ config USB_DWC3_AM62
The Designware Core USB3 IP is programmed to operate in
in USB 2.0 mode only.
Say 'Y' or 'M' here if you have one such device
+
+config USB_DWC3_OCTEON
+ tristate "Cavium Octeon Platforms"
+ depends on CAVIUM_OCTEON_SOC || COMPILE_TEST
+ default USB_DWC3
+ help
+ Support Cavium Octeon platforms with DesignWare Core USB3 IP.
+ Only the host mode is currently supported.
+ Say 'Y' or 'M' here if you have one such device.
+
endif
diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile
index 9f66bd82b639..fe1493d4bbe5 100644
--- a/drivers/usb/dwc3/Makefile
+++ b/drivers/usb/dwc3/Makefile
@@ -54,3 +54,4 @@ obj-$(CONFIG_USB_DWC3_ST) += dwc3-st.o
obj-$(CONFIG_USB_DWC3_QCOM) += dwc3-qcom.o
obj-$(CONFIG_USB_DWC3_IMX8MP) += dwc3-imx8mp.o
obj-$(CONFIG_USB_DWC3_XILINX) += dwc3-xilinx.o
+obj-$(CONFIG_USB_DWC3_OCTEON) += dwc3-octeon.o
diff --git a/drivers/usb/dwc3/dwc3-am62.c b/drivers/usb/dwc3/dwc3-am62.c
index 1755f2f848c5..90a587bc29b7 100644
--- a/drivers/usb/dwc3/dwc3-am62.c
+++ b/drivers/usb/dwc3/dwc3-am62.c
@@ -102,7 +102,7 @@
#define DWC3_AM62_AUTOSUSPEND_DELAY 100
-struct dwc3_data {
+struct dwc3_am62 {
struct device *dev;
void __iomem *usbss;
struct clk *usb2_refclk;
@@ -129,19 +129,19 @@ static const int dwc3_ti_rate_table[] = { /* in KHZ */
52000,
};
-static inline u32 dwc3_ti_readl(struct dwc3_data *data, u32 offset)
+static inline u32 dwc3_ti_readl(struct dwc3_am62 *am62, u32 offset)
{
- return readl((data->usbss) + offset);
+ return readl((am62->usbss) + offset);
}
-static inline void dwc3_ti_writel(struct dwc3_data *data, u32 offset, u32 value)
+static inline void dwc3_ti_writel(struct dwc3_am62 *am62, u32 offset, u32 value)
{
- writel(value, (data->usbss) + offset);
+ writel(value, (am62->usbss) + offset);
}
-static int phy_syscon_pll_refclk(struct dwc3_data *data)
+static int phy_syscon_pll_refclk(struct dwc3_am62 *am62)
{
- struct device *dev = data->dev;
+ struct device *dev = am62->dev;
struct device_node *node = dev->of_node;
struct of_phandle_args args;
struct regmap *syscon;
@@ -153,16 +153,16 @@ static int phy_syscon_pll_refclk(struct dwc3_data *data)
return PTR_ERR(syscon);
}
- data->syscon = syscon;
+ am62->syscon = syscon;
ret = of_parse_phandle_with_fixed_args(node, "ti,syscon-phy-pll-refclk", 1,
0, &args);
if (ret)
return ret;
- data->offset = args.args[0];
+ am62->offset = args.args[0];
- ret = regmap_update_bits(data->syscon, data->offset, PHY_PLL_REFCLK_MASK, data->rate_code);
+ ret = regmap_update_bits(am62->syscon, am62->offset, PHY_PLL_REFCLK_MASK, am62->rate_code);
if (ret) {
dev_err(dev, "failed to set phy pll reference clock rate\n");
return ret;
@@ -175,32 +175,32 @@ static int dwc3_ti_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *node = pdev->dev.of_node;
- struct dwc3_data *data;
+ struct dwc3_am62 *am62;
int i, ret;
unsigned long rate;
u32 reg;
- data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
- if (!data)
+ am62 = devm_kzalloc(dev, sizeof(*am62), GFP_KERNEL);
+ if (!am62)
return -ENOMEM;
- data->dev = dev;
- platform_set_drvdata(pdev, data);
+ am62->dev = dev;
+ platform_set_drvdata(pdev, am62);
- data->usbss = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(data->usbss)) {
+ am62->usbss = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(am62->usbss)) {
dev_err(dev, "can't map IOMEM resource\n");
- return PTR_ERR(data->usbss);
+ return PTR_ERR(am62->usbss);
}
- data->usb2_refclk = devm_clk_get(dev, "ref");
- if (IS_ERR(data->usb2_refclk)) {
+ am62->usb2_refclk = devm_clk_get(dev, "ref");
+ if (IS_ERR(am62->usb2_refclk)) {
dev_err(dev, "can't get usb2_refclk\n");
- return PTR_ERR(data->usb2_refclk);
+ return PTR_ERR(am62->usb2_refclk);
}
/* Calculate the rate code */
- rate = clk_get_rate(data->usb2_refclk);
+ rate = clk_get_rate(am62->usb2_refclk);
rate /= 1000; // To KHz
for (i = 0; i < ARRAY_SIZE(dwc3_ti_rate_table); i++) {
if (dwc3_ti_rate_table[i] == rate)
@@ -212,20 +212,20 @@ static int dwc3_ti_probe(struct platform_device *pdev)
return -EINVAL;
}
- data->rate_code = i;
+ am62->rate_code = i;
/* Read the syscon property and set the rate code */
- ret = phy_syscon_pll_refclk(data);
+ ret = phy_syscon_pll_refclk(am62);
if (ret)
return ret;
/* VBUS divider select */
- data->vbus_divider = device_property_read_bool(dev, "ti,vbus-divider");
- reg = dwc3_ti_readl(data, USBSS_PHY_CONFIG);
- if (data->vbus_divider)
+ am62->vbus_divider = device_property_read_bool(dev, "ti,vbus-divider");
+ reg = dwc3_ti_readl(am62, USBSS_PHY_CONFIG);
+ if (am62->vbus_divider)
reg |= 1 << USBSS_PHY_VBUS_SEL_SHIFT;
- dwc3_ti_writel(data, USBSS_PHY_CONFIG, reg);
+ dwc3_ti_writel(am62, USBSS_PHY_CONFIG, reg);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
@@ -233,7 +233,7 @@ static int dwc3_ti_probe(struct platform_device *pdev)
* Don't ignore its dependencies with its children
*/
pm_suspend_ignore_children(dev, false);
- clk_prepare_enable(data->usb2_refclk);
+ clk_prepare_enable(am62->usb2_refclk);
pm_runtime_get_noresume(dev);
ret = of_platform_populate(node, NULL, NULL, dev);
@@ -243,9 +243,9 @@ static int dwc3_ti_probe(struct platform_device *pdev)
}
/* Set mode valid bit to indicate role is valid */
- reg = dwc3_ti_readl(data, USBSS_MODE_CONTROL);
+ reg = dwc3_ti_readl(am62, USBSS_MODE_CONTROL);
reg |= USBSS_MODE_VALID;
- dwc3_ti_writel(data, USBSS_MODE_CONTROL, reg);
+ dwc3_ti_writel(am62, USBSS_MODE_CONTROL, reg);
/* Device has capability to wakeup system from sleep */
device_set_wakeup_capable(dev, true);
@@ -261,7 +261,7 @@ static int dwc3_ti_probe(struct platform_device *pdev)
return 0;
err_pm_disable:
- clk_disable_unprepare(data->usb2_refclk);
+ clk_disable_unprepare(am62->usb2_refclk);
pm_runtime_disable(dev);
pm_runtime_set_suspended(dev);
return ret;
@@ -278,36 +278,34 @@ static int dwc3_ti_remove_core(struct device *dev, void *c)
static void dwc3_ti_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct dwc3_data *data = platform_get_drvdata(pdev);
+ struct dwc3_am62 *am62 = platform_get_drvdata(pdev);
u32 reg;
device_for_each_child(dev, NULL, dwc3_ti_remove_core);
/* Clear mode valid bit */
- reg = dwc3_ti_readl(data, USBSS_MODE_CONTROL);
+ reg = dwc3_ti_readl(am62, USBSS_MODE_CONTROL);
reg &= ~USBSS_MODE_VALID;
- dwc3_ti_writel(data, USBSS_MODE_CONTROL, reg);
+ dwc3_ti_writel(am62, USBSS_MODE_CONTROL, reg);
pm_runtime_put_sync(dev);
- clk_disable_unprepare(data->usb2_refclk);
+ clk_disable_unprepare(am62->usb2_refclk);
pm_runtime_disable(dev);
pm_runtime_set_suspended(dev);
-
- platform_set_drvdata(pdev, NULL);
}
#ifdef CONFIG_PM
static int dwc3_ti_suspend_common(struct device *dev)
{
- struct dwc3_data *data = dev_get_drvdata(dev);
+ struct dwc3_am62 *am62 = dev_get_drvdata(dev);
u32 reg, current_prtcap_dir;
if (device_may_wakeup(dev)) {
- reg = dwc3_ti_readl(data, USBSS_CORE_STAT);
+ reg = dwc3_ti_readl(am62, USBSS_CORE_STAT);
current_prtcap_dir = (reg & USBSS_CORE_OPERATIONAL_MODE_MASK)
>> USBSS_CORE_OPERATIONAL_MODE_SHIFT;
/* Set wakeup config enable bits */
- reg = dwc3_ti_readl(data, USBSS_WAKEUP_CONFIG);
+ reg = dwc3_ti_readl(am62, USBSS_WAKEUP_CONFIG);
if (current_prtcap_dir == DWC3_GCTL_PRTCAP_HOST) {
reg = USBSS_WAKEUP_CFG_LINESTATE_EN | USBSS_WAKEUP_CFG_OVERCURRENT_EN;
} else {
@@ -317,30 +315,30 @@ static int dwc3_ti_suspend_common(struct device *dev)
* and in U2/L3 state else it causes spurious wake-up.
*/
}
- dwc3_ti_writel(data, USBSS_WAKEUP_CONFIG, reg);
+ dwc3_ti_writel(am62, USBSS_WAKEUP_CONFIG, reg);
/* clear wakeup status so we know what caused the wake up */
- dwc3_ti_writel(data, USBSS_WAKEUP_STAT, USBSS_WAKEUP_STAT_CLR);
+ dwc3_ti_writel(am62, USBSS_WAKEUP_STAT, USBSS_WAKEUP_STAT_CLR);
}
- clk_disable_unprepare(data->usb2_refclk);
+ clk_disable_unprepare(am62->usb2_refclk);
return 0;
}
static int dwc3_ti_resume_common(struct device *dev)
{
- struct dwc3_data *data = dev_get_drvdata(dev);
+ struct dwc3_am62 *am62 = dev_get_drvdata(dev);
u32 reg;
- clk_prepare_enable(data->usb2_refclk);
+ clk_prepare_enable(am62->usb2_refclk);
if (device_may_wakeup(dev)) {
/* Clear wakeup config enable bits */
- dwc3_ti_writel(data, USBSS_WAKEUP_CONFIG, USBSS_WAKEUP_CFG_NONE);
+ dwc3_ti_writel(am62, USBSS_WAKEUP_CONFIG, USBSS_WAKEUP_CFG_NONE);
}
- reg = dwc3_ti_readl(data, USBSS_WAKEUP_STAT);
- data->wakeup_stat = reg;
+ reg = dwc3_ti_readl(am62, USBSS_WAKEUP_STAT);
+ am62->wakeup_stat = reg;
return 0;
}
diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c
index f882dd647340..5d365ca51771 100644
--- a/drivers/usb/dwc3/dwc3-exynos.c
+++ b/drivers/usb/dwc3/dwc3-exynos.c
@@ -163,6 +163,12 @@ static const struct dwc3_exynos_driverdata exynos7_drvdata = {
.suspend_clk_idx = 1,
};
+static const struct dwc3_exynos_driverdata exynos850_drvdata = {
+ .clk_names = { "bus_early", "ref" },
+ .num_clks = 2,
+ .suspend_clk_idx = -1,
+};
+
static const struct of_device_id exynos_dwc3_match[] = {
{
.compatible = "samsung,exynos5250-dwusb3",
@@ -174,6 +180,9 @@ static const struct of_device_id exynos_dwc3_match[] = {
.compatible = "samsung,exynos7-dwusb3",
.data = &exynos7_drvdata,
}, {
+ .compatible = "samsung,exynos850-dwusb3",
+ .data = &exynos850_drvdata,
+ }, {
}
};
MODULE_DEVICE_TABLE(of, exynos_dwc3_match);
diff --git a/drivers/usb/dwc3/dwc3-imx8mp.c b/drivers/usb/dwc3/dwc3-imx8mp.c
index 8b9a3bb587bf..a1e15f2fffdb 100644
--- a/drivers/usb/dwc3/dwc3-imx8mp.c
+++ b/drivers/usb/dwc3/dwc3-imx8mp.c
@@ -10,6 +10,7 @@
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
@@ -279,7 +280,6 @@ static void dwc3_imx8mp_remove(struct platform_device *pdev)
pm_runtime_disable(dev);
pm_runtime_put_noidle(dev);
- platform_set_drvdata(pdev, NULL);
}
static int __maybe_unused dwc3_imx8mp_suspend(struct dwc3_imx8mp *dwc3_imx,
diff --git a/drivers/usb/dwc3/dwc3-keystone.c b/drivers/usb/dwc3/dwc3-keystone.c
index 0a09aedc2573..8899348b6276 100644
--- a/drivers/usb/dwc3/dwc3-keystone.c
+++ b/drivers/usb/dwc3/dwc3-keystone.c
@@ -13,6 +13,7 @@
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/io.h>
+#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/phy/phy.h>
#include <linux/pm_runtime.h>
@@ -196,8 +197,6 @@ static void kdwc3_remove(struct platform_device *pdev)
phy_power_off(kdwc->usb3_phy);
phy_exit(kdwc->usb3_phy);
phy_pm_runtime_put_sync(kdwc->usb3_phy);
-
- platform_set_drvdata(pdev, NULL);
}
static const struct of_device_id kdwc3_of_match[] = {
diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c
index e99c7489dba0..2c07c038b584 100644
--- a/drivers/usb/dwc3/dwc3-meson-g12a.c
+++ b/drivers/usb/dwc3/dwc3-meson-g12a.c
@@ -926,6 +926,12 @@ static int __maybe_unused dwc3_meson_g12a_resume(struct device *dev)
return ret;
}
+ if (priv->drvdata->usb_post_init) {
+ ret = priv->drvdata->usb_post_init(priv);
+ if (ret)
+ return ret;
+ }
+
return 0;
}
diff --git a/drivers/usb/dwc3/dwc3-octeon.c b/drivers/usb/dwc3/dwc3-octeon.c
new file mode 100644
index 000000000000..ff01f2c17452
--- /dev/null
+++ b/drivers/usb/dwc3/dwc3-octeon.c
@@ -0,0 +1,543 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * DWC3 glue for Cavium Octeon III SOCs.
+ *
+ * Copyright (C) 2010-2017 Cavium Networks
+ * Copyright (C) 2023 RACOM s.r.o.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_platform.h>
+
+/*
+ * USB Control Register
+ */
+#define USBDRD_UCTL_CTL 0x00
+/* BIST fast-clear mode select. A BIST run with this bit set
+ * clears all entries in USBH RAMs to 0x0.
+ */
+# define USBDRD_UCTL_CTL_CLEAR_BIST BIT_ULL(63)
+/* 1 = Start BIST and cleared by hardware */
+# define USBDRD_UCTL_CTL_START_BIST BIT_ULL(62)
+/* Reference clock select for SuperSpeed and HighSpeed PLLs:
+ * 0x0 = Both PLLs use DLMC_REF_CLK0 for reference clock
+ * 0x1 = Both PLLs use DLMC_REF_CLK1 for reference clock
+ * 0x2 = SuperSpeed PLL uses DLMC_REF_CLK0 for reference clock &
+ * HighSpeed PLL uses PLL_REF_CLK for reference clck
+ * 0x3 = SuperSpeed PLL uses DLMC_REF_CLK1 for reference clock &
+ * HighSpeed PLL uses PLL_REF_CLK for reference clck
+ */
+# define USBDRD_UCTL_CTL_REF_CLK_SEL GENMASK_ULL(61, 60)
+/* 1 = Spread-spectrum clock enable, 0 = SS clock disable */
+# define USBDRD_UCTL_CTL_SSC_EN BIT_ULL(59)
+/* Spread-spectrum clock modulation range:
+ * 0x0 = -4980 ppm downspread
+ * 0x1 = -4492 ppm downspread
+ * 0x2 = -4003 ppm downspread
+ * 0x3 - 0x7 = Reserved
+ */
+# define USBDRD_UCTL_CTL_SSC_RANGE GENMASK_ULL(58, 56)
+/* Enable non-standard oscillator frequencies:
+ * [55:53] = modules -1
+ * [52:47] = 2's complement push amount, 0 = Feature disabled
+ */
+# define USBDRD_UCTL_CTL_SSC_REF_CLK_SEL GENMASK_ULL(55, 47)
+/* Reference clock multiplier for non-standard frequencies:
+ * 0x19 = 100MHz on DLMC_REF_CLK* if REF_CLK_SEL = 0x0 or 0x1
+ * 0x28 = 125MHz on DLMC_REF_CLK* if REF_CLK_SEL = 0x0 or 0x1
+ * 0x32 = 50MHz on DLMC_REF_CLK* if REF_CLK_SEL = 0x0 or 0x1
+ * Other Values = Reserved
+ */
+# define USBDRD_UCTL_CTL_MPLL_MULTIPLIER GENMASK_ULL(46, 40)
+/* Enable reference clock to prescaler for SuperSpeed functionality.
+ * Should always be set to "1"
+ */
+# define USBDRD_UCTL_CTL_REF_SSP_EN BIT_ULL(39)
+/* Divide the reference clock by 2 before entering the
+ * REF_CLK_FSEL divider:
+ * If REF_CLK_SEL = 0x0 or 0x1, then only 0x0 is legal
+ * If REF_CLK_SEL = 0x2 or 0x3, then:
+ * 0x1 = DLMC_REF_CLK* is 125MHz
+ * 0x0 = DLMC_REF_CLK* is another supported frequency
+ */
+# define USBDRD_UCTL_CTL_REF_CLK_DIV2 BIT_ULL(38)
+/* Select reference clock freqnuency for both PLL blocks:
+ * 0x27 = REF_CLK_SEL is 0x0 or 0x1
+ * 0x07 = REF_CLK_SEL is 0x2 or 0x3
+ */
+# define USBDRD_UCTL_CTL_REF_CLK_FSEL GENMASK_ULL(37, 32)
+/* Controller clock enable. */
+# define USBDRD_UCTL_CTL_H_CLK_EN BIT_ULL(30)
+/* Select bypass input to controller clock divider:
+ * 0x0 = Use divided coprocessor clock from H_CLKDIV
+ * 0x1 = Use clock from GPIO pins
+ */
+# define USBDRD_UCTL_CTL_H_CLK_BYP_SEL BIT_ULL(29)
+/* Reset controller clock divider. */
+# define USBDRD_UCTL_CTL_H_CLKDIV_RST BIT_ULL(28)
+/* Clock divider select:
+ * 0x0 = divide by 1
+ * 0x1 = divide by 2
+ * 0x2 = divide by 4
+ * 0x3 = divide by 6
+ * 0x4 = divide by 8
+ * 0x5 = divide by 16
+ * 0x6 = divide by 24
+ * 0x7 = divide by 32
+ */
+# define USBDRD_UCTL_CTL_H_CLKDIV_SEL GENMASK_ULL(26, 24)
+/* USB3 port permanently attached: 0x0 = No, 0x1 = Yes */
+# define USBDRD_UCTL_CTL_USB3_PORT_PERM_ATTACH BIT_ULL(21)
+/* USB2 port permanently attached: 0x0 = No, 0x1 = Yes */
+# define USBDRD_UCTL_CTL_USB2_PORT_PERM_ATTACH BIT_ULL(20)
+/* Disable SuperSpeed PHY: 0x0 = No, 0x1 = Yes */
+# define USBDRD_UCTL_CTL_USB3_PORT_DISABLE BIT_ULL(18)
+/* Disable HighSpeed PHY: 0x0 = No, 0x1 = Yes */
+# define USBDRD_UCTL_CTL_USB2_PORT_DISABLE BIT_ULL(16)
+/* Enable PHY SuperSpeed block power: 0x0 = No, 0x1 = Yes */
+# define USBDRD_UCTL_CTL_SS_POWER_EN BIT_ULL(14)
+/* Enable PHY HighSpeed block power: 0x0 = No, 0x1 = Yes */
+# define USBDRD_UCTL_CTL_HS_POWER_EN BIT_ULL(12)
+/* Enable USB UCTL interface clock: 0xx = No, 0x1 = Yes */
+# define USBDRD_UCTL_CTL_CSCLK_EN BIT_ULL(4)
+/* Controller mode: 0x0 = Host, 0x1 = Device */
+# define USBDRD_UCTL_CTL_DRD_MODE BIT_ULL(3)
+/* PHY reset */
+# define USBDRD_UCTL_CTL_UPHY_RST BIT_ULL(2)
+/* Software reset UAHC */
+# define USBDRD_UCTL_CTL_UAHC_RST BIT_ULL(1)
+/* Software resets UCTL */
+# define USBDRD_UCTL_CTL_UCTL_RST BIT_ULL(0)
+
+#define USBDRD_UCTL_BIST_STATUS 0x08
+#define USBDRD_UCTL_SPARE0 0x10
+#define USBDRD_UCTL_INTSTAT 0x30
+#define USBDRD_UCTL_PORT_CFG_HS(port) (0x40 + (0x20 * port))
+#define USBDRD_UCTL_PORT_CFG_SS(port) (0x48 + (0x20 * port))
+#define USBDRD_UCTL_PORT_CR_DBG_CFG(port) (0x50 + (0x20 * port))
+#define USBDRD_UCTL_PORT_CR_DBG_STATUS(port) (0x58 + (0x20 * port))
+
+/*
+ * UCTL Configuration Register
+ */
+#define USBDRD_UCTL_HOST_CFG 0xe0
+/* Indicates minimum value of all received BELT values */
+# define USBDRD_UCTL_HOST_CFG_HOST_CURRENT_BELT GENMASK_ULL(59, 48)
+/* HS jitter adjustment */
+# define USBDRD_UCTL_HOST_CFG_FLA GENMASK_ULL(37, 32)
+/* Bus-master enable: 0x0 = Disabled (stall DMAs), 0x1 = enabled */
+# define USBDRD_UCTL_HOST_CFG_BME BIT_ULL(28)
+/* Overcurrent protection enable: 0x0 = unavailable, 0x1 = available */
+# define USBDRD_UCTL_HOST_OCI_EN BIT_ULL(27)
+/* Overcurrent sene selection:
+ * 0x0 = Overcurrent indication from off-chip is active-low
+ * 0x1 = Overcurrent indication from off-chip is active-high
+ */
+# define USBDRD_UCTL_HOST_OCI_ACTIVE_HIGH_EN BIT_ULL(26)
+/* Port power control enable: 0x0 = unavailable, 0x1 = available */
+# define USBDRD_UCTL_HOST_PPC_EN BIT_ULL(25)
+/* Port power control sense selection:
+ * 0x0 = Port power to off-chip is active-low
+ * 0x1 = Port power to off-chip is active-high
+ */
+# define USBDRD_UCTL_HOST_PPC_ACTIVE_HIGH_EN BIT_ULL(24)
+
+/*
+ * UCTL Shim Features Register
+ */
+#define USBDRD_UCTL_SHIM_CFG 0xe8
+/* Out-of-bound UAHC register access: 0 = read, 1 = write */
+# define USBDRD_UCTL_SHIM_CFG_XS_NCB_OOB_WRN BIT_ULL(63)
+/* SRCID error log for out-of-bound UAHC register access:
+ * [59:58] = chipID
+ * [57] = Request source: 0 = core, 1 = NCB-device
+ * [56:51] = Core/NCB-device number, [56] always 0 for NCB devices
+ * [50:48] = SubID
+ */
+# define USBDRD_UCTL_SHIM_CFG_XS_NCB_OOB_OSRC GENMASK_ULL(59, 48)
+/* Error log for bad UAHC DMA access: 0 = Read log, 1 = Write log */
+# define USBDRD_UCTL_SHIM_CFG_XM_BAD_DMA_WRN BIT_ULL(47)
+/* Encoded error type for bad UAHC DMA */
+# define USBDRD_UCTL_SHIM_CFG_XM_BAD_DMA_TYPE GENMASK_ULL(43, 40)
+/* Select the IOI read command used by DMA accesses */
+# define USBDRD_UCTL_SHIM_CFG_DMA_READ_CMD BIT_ULL(12)
+/* Select endian format for DMA accesses to the L2C:
+ * 0x0 = Little endian
+ * 0x1 = Big endian
+ * 0x2 = Reserved
+ * 0x3 = Reserved
+ */
+# define USBDRD_UCTL_SHIM_CFG_DMA_ENDIAN_MODE GENMASK_ULL(9, 8)
+/* Select endian format for IOI CSR access to UAHC:
+ * 0x0 = Little endian
+ * 0x1 = Big endian
+ * 0x2 = Reserved
+ * 0x3 = Reserved
+ */
+# define USBDRD_UCTL_SHIM_CFG_CSR_ENDIAN_MODE GENMASK_ULL(1, 0)
+
+#define USBDRD_UCTL_ECC 0xf0
+#define USBDRD_UCTL_SPARE1 0xf8
+
+struct dwc3_octeon {
+ struct device *dev;
+ void __iomem *base;
+};
+
+#define DWC3_GPIO_POWER_NONE (-1)
+
+#ifdef CONFIG_CAVIUM_OCTEON_SOC
+#include <asm/octeon/octeon.h>
+static inline uint64_t dwc3_octeon_readq(void __iomem *addr)
+{
+ return cvmx_readq_csr(addr);
+}
+
+static inline void dwc3_octeon_writeq(void __iomem *base, uint64_t val)
+{
+ cvmx_writeq_csr(base, val);
+}
+
+static void dwc3_octeon_config_gpio(int index, int gpio)
+{
+ union cvmx_gpio_bit_cfgx gpio_bit;
+
+ if ((OCTEON_IS_MODEL(OCTEON_CN73XX) ||
+ OCTEON_IS_MODEL(OCTEON_CNF75XX))
+ && gpio <= 31) {
+ gpio_bit.u64 = cvmx_read_csr(CVMX_GPIO_BIT_CFGX(gpio));
+ gpio_bit.s.tx_oe = 1;
+ gpio_bit.s.output_sel = (index == 0 ? 0x14 : 0x15);
+ cvmx_write_csr(CVMX_GPIO_BIT_CFGX(gpio), gpio_bit.u64);
+ } else if (gpio <= 15) {
+ gpio_bit.u64 = cvmx_read_csr(CVMX_GPIO_BIT_CFGX(gpio));
+ gpio_bit.s.tx_oe = 1;
+ gpio_bit.s.output_sel = (index == 0 ? 0x14 : 0x19);
+ cvmx_write_csr(CVMX_GPIO_BIT_CFGX(gpio), gpio_bit.u64);
+ } else {
+ gpio_bit.u64 = cvmx_read_csr(CVMX_GPIO_XBIT_CFGX(gpio));
+ gpio_bit.s.tx_oe = 1;
+ gpio_bit.s.output_sel = (index == 0 ? 0x14 : 0x19);
+ cvmx_write_csr(CVMX_GPIO_XBIT_CFGX(gpio), gpio_bit.u64);
+ }
+}
+#else
+static inline uint64_t dwc3_octeon_readq(void __iomem *addr)
+{
+ return 0;
+}
+
+static inline void dwc3_octeon_writeq(void __iomem *base, uint64_t val) { }
+
+static inline void dwc3_octeon_config_gpio(int index, int gpio) { }
+
+static uint64_t octeon_get_io_clock_rate(void)
+{
+ return 150000000;
+}
+#endif
+
+static int dwc3_octeon_get_divider(void)
+{
+ static const uint8_t clk_div[] = { 1, 2, 4, 6, 8, 16, 24, 32 };
+ int div = 0;
+
+ while (div < ARRAY_SIZE(clk_div)) {
+ uint64_t rate = octeon_get_io_clock_rate() / clk_div[div];
+ if (rate <= 300000000 && rate >= 150000000)
+ return div;
+ div++;
+ }
+
+ return -EINVAL;
+}
+
+static int dwc3_octeon_setup(struct dwc3_octeon *octeon,
+ int ref_clk_sel, int ref_clk_fsel, int mpll_mul,
+ int power_gpio, int power_active_low)
+{
+ u64 val;
+ int div;
+ struct device *dev = octeon->dev;
+ void __iomem *uctl_ctl_reg = octeon->base + USBDRD_UCTL_CTL;
+ void __iomem *uctl_host_cfg_reg = octeon->base + USBDRD_UCTL_HOST_CFG;
+
+ /*
+ * Step 1: Wait for all voltages to be stable...that surely
+ * happened before starting the kernel. SKIP
+ */
+
+ /* Step 2: Select GPIO for overcurrent indication, if desired. SKIP */
+
+ /* Step 3: Assert all resets. */
+ val = dwc3_octeon_readq(uctl_ctl_reg);
+ val |= USBDRD_UCTL_CTL_UPHY_RST |
+ USBDRD_UCTL_CTL_UAHC_RST |
+ USBDRD_UCTL_CTL_UCTL_RST;
+ dwc3_octeon_writeq(uctl_ctl_reg, val);
+
+ /* Step 4a: Reset the clock dividers. */
+ val = dwc3_octeon_readq(uctl_ctl_reg);
+ val |= USBDRD_UCTL_CTL_H_CLKDIV_RST;
+ dwc3_octeon_writeq(uctl_ctl_reg, val);
+
+ /* Step 4b: Select controller clock frequency. */
+ div = dwc3_octeon_get_divider();
+ if (div < 0) {
+ dev_err(dev, "clock divider invalid\n");
+ return div;
+ }
+ val = dwc3_octeon_readq(uctl_ctl_reg);
+ val &= ~USBDRD_UCTL_CTL_H_CLKDIV_SEL;
+ val |= FIELD_PREP(USBDRD_UCTL_CTL_H_CLKDIV_SEL, div);
+ val |= USBDRD_UCTL_CTL_H_CLK_EN;
+ dwc3_octeon_writeq(uctl_ctl_reg, val);
+ val = dwc3_octeon_readq(uctl_ctl_reg);
+ if ((div != FIELD_GET(USBDRD_UCTL_CTL_H_CLKDIV_SEL, val)) ||
+ (!(FIELD_GET(USBDRD_UCTL_CTL_H_CLK_EN, val)))) {
+ dev_err(dev, "clock init failure (UCTL_CTL=%016llx)\n", val);
+ return -EINVAL;
+ }
+
+ /* Step 4c: Deassert the controller clock divider reset. */
+ val &= ~USBDRD_UCTL_CTL_H_CLKDIV_RST;
+ dwc3_octeon_writeq(uctl_ctl_reg, val);
+
+ /* Step 5a: Reference clock configuration. */
+ val = dwc3_octeon_readq(uctl_ctl_reg);
+ val &= ~USBDRD_UCTL_CTL_REF_CLK_DIV2;
+ val &= ~USBDRD_UCTL_CTL_REF_CLK_SEL;
+ val |= FIELD_PREP(USBDRD_UCTL_CTL_REF_CLK_SEL, ref_clk_sel);
+
+ val &= ~USBDRD_UCTL_CTL_REF_CLK_FSEL;
+ val |= FIELD_PREP(USBDRD_UCTL_CTL_REF_CLK_FSEL, ref_clk_fsel);
+
+ val &= ~USBDRD_UCTL_CTL_MPLL_MULTIPLIER;
+ val |= FIELD_PREP(USBDRD_UCTL_CTL_MPLL_MULTIPLIER, mpll_mul);
+
+ /* Step 5b: Configure and enable spread-spectrum for SuperSpeed. */
+ val |= USBDRD_UCTL_CTL_SSC_EN;
+
+ /* Step 5c: Enable SuperSpeed. */
+ val |= USBDRD_UCTL_CTL_REF_SSP_EN;
+
+ /* Step 5d: Configure PHYs. SKIP */
+
+ /* Step 6a & 6b: Power up PHYs. */
+ val |= USBDRD_UCTL_CTL_HS_POWER_EN;
+ val |= USBDRD_UCTL_CTL_SS_POWER_EN;
+ dwc3_octeon_writeq(uctl_ctl_reg, val);
+
+ /* Step 7: Wait 10 controller-clock cycles to take effect. */
+ udelay(10);
+
+ /* Step 8a: Deassert UCTL reset signal. */
+ val = dwc3_octeon_readq(uctl_ctl_reg);
+ val &= ~USBDRD_UCTL_CTL_UCTL_RST;
+ dwc3_octeon_writeq(uctl_ctl_reg, val);
+
+ /* Step 8b: Wait 10 controller-clock cycles. */
+ udelay(10);
+
+ /* Step 8c: Setup power control. */
+ val = dwc3_octeon_readq(uctl_host_cfg_reg);
+ val |= USBDRD_UCTL_HOST_PPC_EN;
+ if (power_gpio == DWC3_GPIO_POWER_NONE) {
+ val &= ~USBDRD_UCTL_HOST_PPC_EN;
+ } else {
+ val |= USBDRD_UCTL_HOST_PPC_EN;
+ dwc3_octeon_config_gpio(((__force uintptr_t)octeon->base >> 24) & 1,
+ power_gpio);
+ dev_dbg(dev, "power control is using gpio%d\n", power_gpio);
+ }
+ if (power_active_low)
+ val &= ~USBDRD_UCTL_HOST_PPC_ACTIVE_HIGH_EN;
+ else
+ val |= USBDRD_UCTL_HOST_PPC_ACTIVE_HIGH_EN;
+ dwc3_octeon_writeq(uctl_host_cfg_reg, val);
+
+ /* Step 8d: Deassert UAHC reset signal. */
+ val = dwc3_octeon_readq(uctl_ctl_reg);
+ val &= ~USBDRD_UCTL_CTL_UAHC_RST;
+ dwc3_octeon_writeq(uctl_ctl_reg, val);
+
+ /* Step 8e: Wait 10 controller-clock cycles. */
+ udelay(10);
+
+ /* Step 9: Enable conditional coprocessor clock of UCTL. */
+ val = dwc3_octeon_readq(uctl_ctl_reg);
+ val |= USBDRD_UCTL_CTL_CSCLK_EN;
+ dwc3_octeon_writeq(uctl_ctl_reg, val);
+
+ /*Step 10: Set for host mode only. */
+ val = dwc3_octeon_readq(uctl_ctl_reg);
+ val &= ~USBDRD_UCTL_CTL_DRD_MODE;
+ dwc3_octeon_writeq(uctl_ctl_reg, val);
+
+ return 0;
+}
+
+static void dwc3_octeon_set_endian_mode(struct dwc3_octeon *octeon)
+{
+ u64 val;
+ void __iomem *uctl_shim_cfg_reg = octeon->base + USBDRD_UCTL_SHIM_CFG;
+
+ val = dwc3_octeon_readq(uctl_shim_cfg_reg);
+ val &= ~USBDRD_UCTL_SHIM_CFG_DMA_ENDIAN_MODE;
+ val &= ~USBDRD_UCTL_SHIM_CFG_CSR_ENDIAN_MODE;
+#ifdef __BIG_ENDIAN
+ val |= FIELD_PREP(USBDRD_UCTL_SHIM_CFG_DMA_ENDIAN_MODE, 1);
+ val |= FIELD_PREP(USBDRD_UCTL_SHIM_CFG_CSR_ENDIAN_MODE, 1);
+#endif
+ dwc3_octeon_writeq(uctl_shim_cfg_reg, val);
+}
+
+static void dwc3_octeon_phy_reset(struct dwc3_octeon *octeon)
+{
+ u64 val;
+ void __iomem *uctl_ctl_reg = octeon->base + USBDRD_UCTL_CTL;
+
+ val = dwc3_octeon_readq(uctl_ctl_reg);
+ val &= ~USBDRD_UCTL_CTL_UPHY_RST;
+ dwc3_octeon_writeq(uctl_ctl_reg, val);
+}
+
+static int dwc3_octeon_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *node = dev->of_node;
+ struct dwc3_octeon *octeon;
+ const char *hs_clock_type, *ss_clock_type;
+ int ref_clk_sel, ref_clk_fsel, mpll_mul;
+ int power_active_low, power_gpio;
+ int err, len;
+ u32 clock_rate;
+
+ if (of_property_read_u32(node, "refclk-frequency", &clock_rate)) {
+ dev_err(dev, "No UCTL \"refclk-frequency\"\n");
+ return -EINVAL;
+ }
+ if (of_property_read_string(node, "refclk-type-ss", &ss_clock_type)) {
+ dev_err(dev, "No UCTL \"refclk-type-ss\"\n");
+ return -EINVAL;
+ }
+ if (of_property_read_string(node, "refclk-type-hs", &hs_clock_type)) {
+ dev_err(dev, "No UCTL \"refclk-type-hs\"\n");
+ return -EINVAL;
+ }
+
+ ref_clk_sel = 2;
+ if (strcmp("dlmc_ref_clk0", ss_clock_type) == 0) {
+ if (strcmp(hs_clock_type, "dlmc_ref_clk0") == 0)
+ ref_clk_sel = 0;
+ else if (strcmp(hs_clock_type, "pll_ref_clk"))
+ dev_warn(dev, "Invalid HS clock type %s, using pll_ref_clk instead\n",
+ hs_clock_type);
+ } else if (strcmp(ss_clock_type, "dlmc_ref_clk1") == 0) {
+ if (strcmp(hs_clock_type, "dlmc_ref_clk1") == 0) {
+ ref_clk_sel = 1;
+ } else {
+ ref_clk_sel = 3;
+ if (strcmp(hs_clock_type, "pll_ref_clk"))
+ dev_warn(dev, "Invalid HS clock type %s, using pll_ref_clk instead\n",
+ hs_clock_type);
+ }
+ } else {
+ dev_warn(dev, "Invalid SS clock type %s, using dlmc_ref_clk0 instead\n",
+ ss_clock_type);
+ }
+
+ ref_clk_fsel = 0x07;
+ switch (clock_rate) {
+ default:
+ dev_warn(dev, "Invalid ref_clk %u, using 100000000 instead\n",
+ clock_rate);
+ fallthrough;
+ case 100000000:
+ mpll_mul = 0x19;
+ if (ref_clk_sel < 2)
+ ref_clk_fsel = 0x27;
+ break;
+ case 50000000:
+ mpll_mul = 0x32;
+ break;
+ case 125000000:
+ mpll_mul = 0x28;
+ break;
+ }
+
+ power_gpio = DWC3_GPIO_POWER_NONE;
+ power_active_low = 0;
+ if (of_find_property(node, "power", &len)) {
+ u32 gpio_pwr[3];
+
+ switch (len) {
+ case 8:
+ of_property_read_u32_array(node, "power", gpio_pwr, 2);
+ break;
+ case 12:
+ of_property_read_u32_array(node, "power", gpio_pwr, 3);
+ power_active_low = gpio_pwr[2] & 0x01;
+ break;
+ default:
+ dev_err(dev, "invalid power configuration\n");
+ return -EINVAL;
+ }
+ power_gpio = gpio_pwr[1];
+ }
+
+ octeon = devm_kzalloc(dev, sizeof(*octeon), GFP_KERNEL);
+ if (!octeon)
+ return -ENOMEM;
+
+ octeon->dev = dev;
+ octeon->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(octeon->base))
+ return PTR_ERR(octeon->base);
+
+ err = dwc3_octeon_setup(octeon, ref_clk_sel, ref_clk_fsel, mpll_mul,
+ power_gpio, power_active_low);
+ if (err)
+ return err;
+
+ dwc3_octeon_set_endian_mode(octeon);
+ dwc3_octeon_phy_reset(octeon);
+
+ platform_set_drvdata(pdev, octeon);
+
+ return of_platform_populate(node, NULL, NULL, dev);
+}
+
+static void dwc3_octeon_remove(struct platform_device *pdev)
+{
+ struct dwc3_octeon *octeon = platform_get_drvdata(pdev);
+
+ of_platform_depopulate(octeon->dev);
+}
+
+static const struct of_device_id dwc3_octeon_of_match[] = {
+ { .compatible = "cavium,octeon-7130-usb-uctl" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, dwc3_octeon_of_match);
+
+static struct platform_driver dwc3_octeon_driver = {
+ .probe = dwc3_octeon_probe,
+ .remove_new = dwc3_octeon_remove,
+ .driver = {
+ .name = "dwc3-octeon",
+ .of_match_table = dwc3_octeon_of_match,
+ },
+};
+module_platform_driver(dwc3_octeon_driver);
+
+MODULE_ALIAS("platform:dwc3-octeon");
+MODULE_AUTHOR("Ladislav Michl <ladis@linux-mips.org>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("DesignWare USB3 OCTEON III Glue Layer");
diff --git a/drivers/usb/dwc3/dwc3-of-simple.c b/drivers/usb/dwc3/dwc3-of-simple.c
index 7e6ad8fe61a5..d1539fc9eabd 100644
--- a/drivers/usb/dwc3/dwc3-of-simple.c
+++ b/drivers/usb/dwc3/dwc3-of-simple.c
@@ -170,7 +170,6 @@ static const struct dev_pm_ops dwc3_of_simple_dev_pm_ops = {
static const struct of_device_id of_dwc3_simple_match[] = {
{ .compatible = "rockchip,rk3399-dwc3" },
- { .compatible = "cavium,octeon-7130-usb-uctl" },
{ .compatible = "sprd,sc9860-dwc3" },
{ .compatible = "allwinner,sun50i-h6-dwc3" },
{ .compatible = "hisilicon,hi3670-dwc3" },
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 336db8f92afa..b3592bcb0f96 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -208,6 +208,9 @@ config USB_F_UVC
config USB_F_MIDI
tristate
+config USB_F_MIDI2
+ tristate
+
config USB_F_HID
tristate
@@ -436,6 +439,21 @@ config USB_CONFIGFS_F_MIDI
connections can then be made on the gadget system, using
ALSA's aconnect utility etc.
+config USB_CONFIGFS_F_MIDI2
+ bool "MIDI 2.0 function"
+ depends on USB_CONFIGFS
+ depends on SND
+ select USB_LIBCOMPOSITE
+ select SND_UMP
+ select SND_UMP_LEGACY_RAWMIDI
+ select USB_F_MIDI2
+ help
+ The MIDI 2.0 function driver provides the generic emulated
+ USB MIDI 2.0 interface, looped back to ALSA UMP rawmidi
+ device on the gadget host. It supports UMP 1.1 spec and
+ responds UMP Stream messages for UMP Endpoint and Function
+ Block information / configuration.
+
config USB_CONFIGFS_F_HID
bool "HID function"
depends on USB_CONFIGFS
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index dd9b90481b4c..0ace45b66a31 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -170,33 +170,27 @@ int config_ep_by_speed_and_alt(struct usb_gadget *g,
/* select desired speed */
switch (g->speed) {
case USB_SPEED_SUPER_PLUS:
- if (gadget_is_superspeed_plus(g)) {
- if (f->ssp_descriptors) {
- speed_desc = f->ssp_descriptors;
- want_comp_desc = 1;
- break;
- }
- incomplete_desc = true;
+ if (f->ssp_descriptors) {
+ speed_desc = f->ssp_descriptors;
+ want_comp_desc = 1;
+ break;
}
+ incomplete_desc = true;
fallthrough;
case USB_SPEED_SUPER:
- if (gadget_is_superspeed(g)) {
- if (f->ss_descriptors) {
- speed_desc = f->ss_descriptors;
- want_comp_desc = 1;
- break;
- }
- incomplete_desc = true;
+ if (f->ss_descriptors) {
+ speed_desc = f->ss_descriptors;
+ want_comp_desc = 1;
+ break;
}
+ incomplete_desc = true;
fallthrough;
case USB_SPEED_HIGH:
- if (gadget_is_dualspeed(g)) {
- if (f->hs_descriptors) {
- speed_desc = f->hs_descriptors;
- break;
- }
- incomplete_desc = true;
+ if (f->hs_descriptors) {
+ speed_desc = f->hs_descriptors;
+ break;
}
+ incomplete_desc = true;
fallthrough;
default:
speed_desc = f->fs_descriptors;
diff --git a/drivers/usb/gadget/config.c b/drivers/usb/gadget/config.c
index 05507606b2b4..b1f625245713 100644
--- a/drivers/usb/gadget/config.c
+++ b/drivers/usb/gadget/config.c
@@ -162,8 +162,6 @@ int usb_assign_descriptors(struct usb_function *f,
struct usb_descriptor_header **ss,
struct usb_descriptor_header **ssp)
{
- struct usb_gadget *g = f->config->cdev->gadget;
-
/* super-speed-plus descriptor falls back to super-speed one,
* if such a descriptor was provided, thus avoiding a NULL
* pointer dereference if a 5gbps capable gadget is used with
@@ -177,17 +175,17 @@ int usb_assign_descriptors(struct usb_function *f,
if (!f->fs_descriptors)
goto err;
}
- if (hs && gadget_is_dualspeed(g)) {
+ if (hs) {
f->hs_descriptors = usb_copy_descriptors(hs);
if (!f->hs_descriptors)
goto err;
}
- if (ss && gadget_is_superspeed(g)) {
+ if (ss) {
f->ss_descriptors = usb_copy_descriptors(ss);
if (!f->ss_descriptors)
goto err;
}
- if (ssp && gadget_is_superspeed_plus(g)) {
+ if (ssp) {
f->ssp_descriptors = usb_copy_descriptors(ssp);
if (!f->ssp_descriptors)
goto err;
diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile
index 5d3a6cf02218..87917a7d4a9b 100644
--- a/drivers/usb/gadget/function/Makefile
+++ b/drivers/usb/gadget/function/Makefile
@@ -44,6 +44,8 @@ usb_f_uvc-y := f_uvc.o uvc_queue.o uvc_v4l2.o uvc_video.o uvc_configfs.o
obj-$(CONFIG_USB_F_UVC) += usb_f_uvc.o
usb_f_midi-y := f_midi.o
obj-$(CONFIG_USB_F_MIDI) += usb_f_midi.o
+usb_f_midi2-y := f_midi2.o
+obj-$(CONFIG_USB_F_MIDI2) += usb_f_midi2.o
usb_f_hid-y := f_hid.o
obj-$(CONFIG_USB_F_HID) += usb_f_hid.o
usb_f_printer-y := f_printer.o
diff --git a/drivers/usb/gadget/function/f_acm.c b/drivers/usb/gadget/function/f_acm.c
index cb523f118f04..f616059c5e1e 100644
--- a/drivers/usb/gadget/function/f_acm.c
+++ b/drivers/usb/gadget/function/f_acm.c
@@ -691,10 +691,8 @@ acm_bind(struct usb_configuration *c, struct usb_function *f)
goto fail;
dev_dbg(&cdev->gadget->dev,
- "acm ttyGS%d: %s speed IN/%s OUT/%s NOTIFY/%s\n",
+ "acm ttyGS%d: IN/%s OUT/%s NOTIFY/%s\n",
acm->port_num,
- gadget_is_superspeed(c->cdev->gadget) ? "super" :
- gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
acm->port.in->name, acm->port.out->name,
acm->notify->name);
return 0;
diff --git a/drivers/usb/gadget/function/f_ecm.c b/drivers/usb/gadget/function/f_ecm.c
index c6e63ad77a40..f55f60639e42 100644
--- a/drivers/usb/gadget/function/f_ecm.c
+++ b/drivers/usb/gadget/function/f_ecm.c
@@ -65,17 +65,6 @@ static inline struct f_ecm *func_to_ecm(struct usb_function *f)
return container_of(f, struct f_ecm, port.func);
}
-/* peak (theoretical) bulk transfer rate in bits-per-second */
-static inline unsigned ecm_bitrate(struct usb_gadget *g)
-{
- if (gadget_is_superspeed(g) && g->speed == USB_SPEED_SUPER)
- return 13 * 1024 * 8 * 1000 * 8;
- else if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
- return 13 * 512 * 8 * 1000 * 8;
- else
- return 19 * 64 * 1 * 1000 * 8;
-}
-
/*-------------------------------------------------------------------------*/
/*
@@ -411,10 +400,10 @@ static void ecm_do_notify(struct f_ecm *ecm)
/* SPEED_CHANGE data is up/down speeds in bits/sec */
data = req->buf + sizeof *event;
- data[0] = cpu_to_le32(ecm_bitrate(cdev->gadget));
+ data[0] = cpu_to_le32(gether_bitrate(cdev->gadget));
data[1] = data[0];
- DBG(cdev, "notify speed %d\n", ecm_bitrate(cdev->gadget));
+ DBG(cdev, "notify speed %d\n", gether_bitrate(cdev->gadget));
ecm->notify_state = ECM_NOTIFY_NONE;
break;
}
@@ -799,9 +788,7 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f)
ecm->port.open = ecm_open;
ecm->port.close = ecm_close;
- DBG(cdev, "CDC Ethernet: %s speed IN/%s OUT/%s NOTIFY/%s\n",
- gadget_is_superspeed(c->cdev->gadget) ? "super" :
- gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
+ DBG(cdev, "CDC Ethernet: IN/%s OUT/%s NOTIFY/%s\n",
ecm->port.in_ep->name, ecm->port.out_ep->name,
ecm->notify->name);
return 0;
diff --git a/drivers/usb/gadget/function/f_eem.c b/drivers/usb/gadget/function/f_eem.c
index 5d38f29bda72..3b445bd88498 100644
--- a/drivers/usb/gadget/function/f_eem.c
+++ b/drivers/usb/gadget/function/f_eem.c
@@ -311,9 +311,7 @@ static int eem_bind(struct usb_configuration *c, struct usb_function *f)
if (status)
goto fail;
- DBG(cdev, "CDC Ethernet (EEM): %s speed IN/%s OUT/%s\n",
- gadget_is_superspeed(c->cdev->gadget) ? "super" :
- gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
+ DBG(cdev, "CDC Ethernet (EEM): IN/%s OUT/%s\n",
eem->port.in_ep->name, eem->port.out_ep->name);
return 0;
diff --git a/drivers/usb/gadget/function/f_loopback.c b/drivers/usb/gadget/function/f_loopback.c
index ae41f556eb75..17ac6ace0cff 100644
--- a/drivers/usb/gadget/function/f_loopback.c
+++ b/drivers/usb/gadget/function/f_loopback.c
@@ -211,9 +211,7 @@ autoconf_fail:
if (ret)
return ret;
- DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n",
- (gadget_is_superspeed(c->cdev->gadget) ? "super" :
- (gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full")),
+ DBG(cdev, "%s: IN/%s, OUT/%s\n",
f->name, loop->in_ep->name, loop->out_ep->name);
return 0;
}
diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c
index da07e45ae6df..722a3ab2b337 100644
--- a/drivers/usb/gadget/function/f_mass_storage.c
+++ b/drivers/usb/gadget/function/f_mass_storage.c
@@ -927,7 +927,7 @@ static void invalidate_sub(struct fsg_lun *curlun)
{
struct file *filp = curlun->filp;
struct inode *inode = file_inode(filp);
- unsigned long rc;
+ unsigned long __maybe_unused rc;
rc = invalidate_mapping_pages(inode->i_mapping, 0, -1);
VLDBG(curlun, "invalidate_mapping_pages -> %ld\n", rc);
diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c
index fddf539008a9..2d02f25f9597 100644
--- a/drivers/usb/gadget/function/f_midi.c
+++ b/drivers/usb/gadget/function/f_midi.c
@@ -1023,40 +1023,30 @@ static int f_midi_bind(struct usb_configuration *c, struct usb_function *f)
if (!f->fs_descriptors)
goto fail_f_midi;
- if (gadget_is_dualspeed(c->cdev->gadget)) {
- bulk_in_desc.wMaxPacketSize = cpu_to_le16(512);
- bulk_out_desc.wMaxPacketSize = cpu_to_le16(512);
- f->hs_descriptors = usb_copy_descriptors(midi_function);
- if (!f->hs_descriptors)
- goto fail_f_midi;
- }
+ bulk_in_desc.wMaxPacketSize = cpu_to_le16(512);
+ bulk_out_desc.wMaxPacketSize = cpu_to_le16(512);
+ f->hs_descriptors = usb_copy_descriptors(midi_function);
+ if (!f->hs_descriptors)
+ goto fail_f_midi;
- if (gadget_is_superspeed(c->cdev->gadget)) {
- bulk_in_desc.wMaxPacketSize = cpu_to_le16(1024);
- bulk_out_desc.wMaxPacketSize = cpu_to_le16(1024);
- i = endpoint_descriptor_index;
- midi_function[i++] = (struct usb_descriptor_header *)
- &bulk_out_desc;
- midi_function[i++] = (struct usb_descriptor_header *)
- &bulk_out_ss_comp_desc;
- midi_function[i++] = (struct usb_descriptor_header *)
- &ms_out_desc;
- midi_function[i++] = (struct usb_descriptor_header *)
- &bulk_in_desc;
- midi_function[i++] = (struct usb_descriptor_header *)
- &bulk_in_ss_comp_desc;
- midi_function[i++] = (struct usb_descriptor_header *)
- &ms_in_desc;
- f->ss_descriptors = usb_copy_descriptors(midi_function);
- if (!f->ss_descriptors)
- goto fail_f_midi;
-
- if (gadget_is_superspeed_plus(c->cdev->gadget)) {
- f->ssp_descriptors = usb_copy_descriptors(midi_function);
- if (!f->ssp_descriptors)
- goto fail_f_midi;
- }
- }
+ bulk_in_desc.wMaxPacketSize = cpu_to_le16(1024);
+ bulk_out_desc.wMaxPacketSize = cpu_to_le16(1024);
+ i = endpoint_descriptor_index;
+ midi_function[i++] = (struct usb_descriptor_header *)
+ &bulk_out_desc;
+ midi_function[i++] = (struct usb_descriptor_header *)
+ &bulk_out_ss_comp_desc;
+ midi_function[i++] = (struct usb_descriptor_header *)
+ &ms_out_desc;
+ midi_function[i++] = (struct usb_descriptor_header *)
+ &bulk_in_desc;
+ midi_function[i++] = (struct usb_descriptor_header *)
+ &bulk_in_ss_comp_desc;
+ midi_function[i++] = (struct usb_descriptor_header *)
+ &ms_in_desc;
+ f->ss_descriptors = usb_copy_descriptors(midi_function);
+ if (!f->ss_descriptors)
+ goto fail_f_midi;
kfree(midi_function);
diff --git a/drivers/usb/gadget/function/f_midi2.c b/drivers/usb/gadget/function/f_midi2.c
new file mode 100644
index 000000000000..ec8cd7c7bbfc
--- /dev/null
+++ b/drivers/usb/gadget/function/f_midi2.c
@@ -0,0 +1,2871 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * f_midi2.c -- USB MIDI 2.0 class function driver
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/ump.h>
+#include <sound/ump_msg.h>
+#include <sound/ump_convert.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/audio.h>
+#include <linux/usb/midi-v2.h>
+
+#include "u_f.h"
+#include "u_midi2.h"
+
+struct f_midi2;
+struct f_midi2_ep;
+struct f_midi2_usb_ep;
+
+/* Context for each USB request */
+struct f_midi2_req_ctx {
+ struct f_midi2_usb_ep *usb_ep; /* belonging USB EP */
+ unsigned int index; /* array index: 0-31 */
+ struct usb_request *req; /* assigned request */
+};
+
+/* Resources for a USB Endpoint */
+struct f_midi2_usb_ep {
+ struct f_midi2 *card; /* belonging card */
+ struct f_midi2_ep *ep; /* belonging UMP EP (optional) */
+ struct usb_ep *usb_ep; /* assigned USB EP */
+ void (*complete)(struct usb_ep *usb_ep, struct usb_request *req);
+ unsigned long free_reqs; /* bitmap for unused requests */
+ unsigned int num_reqs; /* number of allocated requests */
+ struct f_midi2_req_ctx *reqs; /* request context array */
+};
+
+/* Resources for UMP Function Block (and USB Group Terminal Block) */
+struct f_midi2_block {
+ struct f_midi2_block_info info; /* FB info, copied from configfs */
+ struct snd_ump_block *fb; /* assigned FB */
+ unsigned int gtb_id; /* assigned GTB id */
+ unsigned int string_id; /* assigned string id */
+};
+
+/* Temporary buffer for altset 0 MIDI 1.0 handling */
+struct f_midi2_midi1_port {
+ unsigned int pending; /* pending bytes on the input buffer */
+ u8 buf[32]; /* raw MIDI 1.0 byte input */
+ u8 state; /* running status */
+ u8 data[2]; /* rendered USB MIDI 1.0 packet data */
+};
+
+/* MIDI 1.0 message states */
+enum {
+ STATE_INITIAL = 0, /* pseudo state */
+ STATE_1PARAM,
+ STATE_2PARAM_1,
+ STATE_2PARAM_2,
+ STATE_SYSEX_0,
+ STATE_SYSEX_1,
+ STATE_SYSEX_2,
+ STATE_REAL_TIME,
+ STATE_FINISHED, /* pseudo state */
+};
+
+/* Resources for UMP Endpoint */
+struct f_midi2_ep {
+ struct snd_ump_endpoint *ump; /* assigned UMP EP */
+ struct f_midi2 *card; /* belonging MIDI 2.0 device */
+
+ struct f_midi2_ep_info info; /* UMP EP info, copied from configfs */
+ unsigned int num_blks; /* number of FBs */
+ struct f_midi2_block blks[SNDRV_UMP_MAX_BLOCKS]; /* UMP FBs */
+
+ struct f_midi2_usb_ep ep_in; /* USB MIDI EP-in */
+ struct f_midi2_usb_ep ep_out; /* USB MIDI EP-out */
+
+ u8 in_group_to_cable[SNDRV_UMP_MAX_GROUPS]; /* map to cable; 1-based! */
+};
+
+/* indices for USB strings */
+enum {
+ STR_IFACE = 0,
+ STR_GTB1 = 1,
+};
+
+/* 1-based GTB id to string id */
+#define gtb_to_str_id(id) (STR_GTB1 + (id) - 1)
+
+/* mapping from MIDI 1.0 cable to UMP group */
+struct midi1_cable_mapping {
+ struct f_midi2_ep *ep;
+ unsigned char block;
+ unsigned char group;
+};
+
+/* operation mode */
+enum {
+ MIDI_OP_MODE_UNSET, /* no altset set yet */
+ MIDI_OP_MODE_MIDI1, /* MIDI 1.0 (altset 0) is used */
+ MIDI_OP_MODE_MIDI2, /* MIDI 2.0 (altset 1) is used */
+};
+
+/* Resources for MIDI 2.0 Device */
+struct f_midi2 {
+ struct usb_function func;
+ struct usb_gadget *gadget;
+ struct snd_card *card;
+
+ /* MIDI 1.0 in/out USB EPs */
+ struct f_midi2_usb_ep midi1_ep_in;
+ struct f_midi2_usb_ep midi1_ep_out;
+
+ /* number of MIDI 1.0 I/O cables */
+ unsigned int num_midi1_in;
+ unsigned int num_midi1_out;
+
+ /* conversion for MIDI 1.0 EP-in */
+ struct f_midi2_midi1_port midi1_port[MAX_CABLES];
+ /* conversion for MIDI 1.0 EP-out */
+ struct ump_cvt_to_ump midi1_ump_cvt;
+ /* mapping between cables and UMP groups */
+ struct midi1_cable_mapping in_cable_mapping[MAX_CABLES];
+ struct midi1_cable_mapping out_cable_mapping[MAX_CABLES];
+
+ int midi_if; /* USB MIDI interface number */
+ int operation_mode; /* current operation mode */
+
+ spinlock_t queue_lock;
+
+ struct f_midi2_card_info info; /* card info, copied from configfs */
+
+ unsigned int num_eps;
+ struct f_midi2_ep midi2_eps[MAX_UMP_EPS];
+
+ unsigned int total_blocks; /* total number of blocks of all EPs */
+ struct usb_string *string_defs;
+ struct usb_string *strings;
+};
+
+#define func_to_midi2(f) container_of(f, struct f_midi2, func)
+
+/* get EP name string */
+static const char *ump_ep_name(const struct f_midi2_ep *ep)
+{
+ return ep->info.ep_name ? ep->info.ep_name : "MIDI 2.0 Gadget";
+}
+
+/* get EP product ID string */
+static const char *ump_product_id(const struct f_midi2_ep *ep)
+{
+ return ep->info.product_id ? ep->info.product_id : "Unique Product ID";
+}
+
+/* get FB name string */
+static const char *ump_fb_name(const struct f_midi2_block_info *info)
+{
+ return info->name ? info->name : "MIDI 2.0 Gadget I/O";
+}
+
+/*
+ * USB Descriptor Definitions
+ */
+/* GTB header descriptor */
+static struct usb_ms20_gr_trm_block_header_descriptor gtb_header_desc = {
+ .bLength = sizeof(gtb_header_desc),
+ .bDescriptorType = USB_DT_CS_GR_TRM_BLOCK,
+ .bDescriptorSubtype = USB_MS_GR_TRM_BLOCK_HEADER,
+ .wTotalLength = __cpu_to_le16(0x12), // to be filled
+};
+
+/* GTB descriptor template: most items are replaced dynamically */
+static struct usb_ms20_gr_trm_block_descriptor gtb_desc = {
+ .bLength = sizeof(gtb_desc),
+ .bDescriptorType = USB_DT_CS_GR_TRM_BLOCK,
+ .bDescriptorSubtype = USB_MS_GR_TRM_BLOCK,
+ .bGrpTrmBlkID = 0x01,
+ .bGrpTrmBlkType = USB_MS_GR_TRM_BLOCK_TYPE_BIDIRECTIONAL,
+ .nGroupTrm = 0x00,
+ .nNumGroupTrm = 1,
+ .iBlockItem = 0,
+ .bMIDIProtocol = USB_MS_MIDI_PROTO_1_0_64,
+ .wMaxInputBandwidth = 0,
+ .wMaxOutputBandwidth = 0,
+};
+
+DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(1);
+DECLARE_USB_MS_ENDPOINT_DESCRIPTOR(16);
+DECLARE_UAC_AC_HEADER_DESCRIPTOR(1);
+DECLARE_USB_MS20_ENDPOINT_DESCRIPTOR(32);
+
+#define EP_MAX_PACKET_INT 8
+
+/* Audio Control Interface */
+static struct usb_interface_descriptor midi2_audio_if_desc = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0, // to be filled
+ .bNumEndpoints = 0,
+ .bInterfaceClass = USB_CLASS_AUDIO,
+ .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL,
+ .bInterfaceProtocol = 0,
+ .iInterface = 0,
+};
+
+static struct uac1_ac_header_descriptor_1 midi2_audio_class_desc = {
+ .bLength = 0x09,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = 0x01,
+ .bcdADC = __cpu_to_le16(0x0100),
+ .wTotalLength = __cpu_to_le16(0x0009),
+ .bInCollection = 0x01,
+ .baInterfaceNr = { 0x01 }, // to be filled
+};
+
+/* MIDI 1.0 Streaming Interface (altset 0) */
+static struct usb_interface_descriptor midi2_midi1_if_desc = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0, // to be filled
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 2, // to be filled
+ .bInterfaceClass = USB_CLASS_AUDIO,
+ .bInterfaceSubClass = USB_SUBCLASS_MIDISTREAMING,
+ .bInterfaceProtocol = 0,
+ .iInterface = 0, // to be filled
+};
+
+static struct usb_ms_header_descriptor midi2_midi1_class_desc = {
+ .bLength = 0x07,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = USB_MS_HEADER,
+ .bcdMSC = __cpu_to_le16(0x0100),
+ .wTotalLength = __cpu_to_le16(0x41), // to be calculated
+};
+
+/* MIDI 1.0 EP OUT */
+static struct usb_endpoint_descriptor midi2_midi1_ep_out_desc = {
+ .bLength = USB_DT_ENDPOINT_AUDIO_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT | 0, // set up dynamically
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_ss_ep_comp_descriptor midi2_midi1_ep_out_ss_comp_desc = {
+ .bLength = sizeof(midi2_midi1_ep_out_ss_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+};
+
+static struct usb_ms_endpoint_descriptor_16 midi2_midi1_ep_out_class_desc = {
+ .bLength = 0x05, // to be filled
+ .bDescriptorType = USB_DT_CS_ENDPOINT,
+ .bDescriptorSubtype = USB_MS_GENERAL,
+ .bNumEmbMIDIJack = 1,
+ .baAssocJackID = { 0x01 },
+};
+
+/* MIDI 1.0 EP IN */
+static struct usb_endpoint_descriptor midi2_midi1_ep_in_desc = {
+ .bLength = USB_DT_ENDPOINT_AUDIO_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN | 0, // set up dynamically
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_ss_ep_comp_descriptor midi2_midi1_ep_in_ss_comp_desc = {
+ .bLength = sizeof(midi2_midi1_ep_in_ss_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+};
+
+static struct usb_ms_endpoint_descriptor_16 midi2_midi1_ep_in_class_desc = {
+ .bLength = 0x05, // to be filled
+ .bDescriptorType = USB_DT_CS_ENDPOINT,
+ .bDescriptorSubtype = USB_MS_GENERAL,
+ .bNumEmbMIDIJack = 1,
+ .baAssocJackID = { 0x03 },
+};
+
+/* MIDI 2.0 Streaming Interface (altset 1) */
+static struct usb_interface_descriptor midi2_midi2_if_desc = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0, // to be filled
+ .bAlternateSetting = 1,
+ .bNumEndpoints = 2, // to be filled
+ .bInterfaceClass = USB_CLASS_AUDIO,
+ .bInterfaceSubClass = USB_SUBCLASS_MIDISTREAMING,
+ .bInterfaceProtocol = 0,
+ .iInterface = 0, // to be filled
+};
+
+static struct usb_ms_header_descriptor midi2_midi2_class_desc = {
+ .bLength = 0x07,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = USB_MS_HEADER,
+ .bcdMSC = __cpu_to_le16(0x0200),
+ .wTotalLength = __cpu_to_le16(0x07),
+};
+
+/* MIDI 2.0 EP OUT */
+static struct usb_endpoint_descriptor midi2_midi2_ep_out_desc[MAX_UMP_EPS];
+
+static struct usb_ss_ep_comp_descriptor midi2_midi2_ep_out_ss_comp_desc = {
+ .bLength = sizeof(midi2_midi1_ep_out_ss_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+};
+
+static struct usb_ms20_endpoint_descriptor_32 midi2_midi2_ep_out_class_desc[MAX_UMP_EPS];
+
+/* MIDI 2.0 EP IN */
+static struct usb_endpoint_descriptor midi2_midi2_ep_in_desc[MAX_UMP_EPS];
+
+static struct usb_ss_ep_comp_descriptor midi2_midi2_ep_in_ss_comp_desc = {
+ .bLength = sizeof(midi2_midi2_ep_in_ss_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+};
+
+static struct usb_ms20_endpoint_descriptor_32 midi2_midi2_ep_in_class_desc[MAX_UMP_EPS];
+
+/* Arrays of descriptors to be created */
+static void *midi2_audio_descs[] = {
+ &midi2_audio_if_desc,
+ &midi2_audio_class_desc,
+ NULL
+};
+
+static void *midi2_midi1_descs[] = {
+ &midi2_midi1_if_desc,
+ &midi2_midi1_class_desc,
+ NULL
+};
+
+static void *midi2_midi1_ep_out_descs[] = {
+ &midi2_midi1_ep_out_desc,
+ &midi2_midi1_ep_out_class_desc,
+ NULL
+};
+
+static void *midi2_midi1_ep_in_descs[] = {
+ &midi2_midi1_ep_in_desc,
+ &midi2_midi1_ep_in_class_desc,
+ NULL
+};
+
+static void *midi2_midi1_ep_out_ss_descs[] = {
+ &midi2_midi1_ep_out_desc,
+ &midi2_midi1_ep_out_ss_comp_desc,
+ &midi2_midi1_ep_out_class_desc,
+ NULL
+};
+
+static void *midi2_midi1_ep_in_ss_descs[] = {
+ &midi2_midi1_ep_in_desc,
+ &midi2_midi1_ep_in_ss_comp_desc,
+ &midi2_midi1_ep_in_class_desc,
+ NULL
+};
+
+static void *midi2_midi2_descs[] = {
+ &midi2_midi2_if_desc,
+ &midi2_midi2_class_desc,
+ NULL
+};
+
+/*
+ * USB request handling
+ */
+
+/* get an empty request for the given EP */
+static struct usb_request *get_empty_request(struct f_midi2_usb_ep *usb_ep)
+{
+ struct usb_request *req = NULL;
+ unsigned long flags;
+ int index;
+
+ spin_lock_irqsave(&usb_ep->card->queue_lock, flags);
+ if (!usb_ep->free_reqs)
+ goto unlock;
+ index = find_first_bit(&usb_ep->free_reqs, usb_ep->num_reqs);
+ if (index >= usb_ep->num_reqs)
+ goto unlock;
+ req = usb_ep->reqs[index].req;
+ if (!req)
+ goto unlock;
+ clear_bit(index, &usb_ep->free_reqs);
+ req->length = 0;
+ unlock:
+ spin_unlock_irqrestore(&usb_ep->card->queue_lock, flags);
+ return req;
+}
+
+/* put the empty request back */
+static void put_empty_request(struct usb_request *req)
+{
+ struct f_midi2_req_ctx *ctx = req->context;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ctx->usb_ep->card->queue_lock, flags);
+ set_bit(ctx->index, &ctx->usb_ep->free_reqs);
+ spin_unlock_irqrestore(&ctx->usb_ep->card->queue_lock, flags);
+}
+
+/*
+ * UMP v1.1 Stream message handling
+ */
+
+/* queue a request to UMP EP; request is either queued or freed after this */
+static int queue_request_ep_raw(struct usb_request *req)
+{
+ struct f_midi2_req_ctx *ctx = req->context;
+ int err;
+
+ req->complete = ctx->usb_ep->complete;
+ err = usb_ep_queue(ctx->usb_ep->usb_ep, req, GFP_ATOMIC);
+ if (err) {
+ put_empty_request(req);
+ return err;
+ }
+ return 0;
+}
+
+/* queue a request with endianness conversion */
+static int queue_request_ep_in(struct usb_request *req)
+{
+ /* UMP packets have to be converted to little-endian */
+ cpu_to_le32_array((u32 *)req->buf, req->length >> 2);
+ return queue_request_ep_raw(req);
+}
+
+/* reply a UMP packet via EP-in */
+static int reply_ep_in(struct f_midi2_ep *ep, const void *buf, int len)
+{
+ struct f_midi2_usb_ep *usb_ep = &ep->ep_in;
+ struct usb_request *req;
+
+ req = get_empty_request(usb_ep);
+ if (!req)
+ return -ENOSPC;
+
+ req->length = len;
+ memcpy(req->buf, buf, len);
+ return queue_request_ep_in(req);
+}
+
+/* reply a UMP stream EP info */
+static void reply_ump_stream_ep_info(struct f_midi2_ep *ep)
+{
+ struct snd_ump_stream_msg_ep_info rep = {
+ .type = UMP_MSG_TYPE_STREAM,
+ .status = UMP_STREAM_MSG_STATUS_EP_INFO,
+ .ump_version_major = 0x01,
+ .ump_version_minor = 0x01,
+ .num_function_blocks = ep->num_blks,
+ .static_function_block = !!ep->card->info.static_block,
+ .protocol = (UMP_STREAM_MSG_EP_INFO_CAP_MIDI1 |
+ UMP_STREAM_MSG_EP_INFO_CAP_MIDI2) >> 8,
+ };
+
+ reply_ep_in(ep, &rep, sizeof(rep));
+}
+
+/* reply a UMP EP device info */
+static void reply_ump_stream_ep_device(struct f_midi2_ep *ep)
+{
+ struct snd_ump_stream_msg_devince_info rep = {
+ .type = UMP_MSG_TYPE_STREAM,
+ .status = UMP_STREAM_MSG_STATUS_DEVICE_INFO,
+ .manufacture_id = ep->info.manufacturer,
+ .family_lsb = ep->info.family & 0xff,
+ .family_msb = (ep->info.family >> 8) & 0xff,
+ .model_lsb = ep->info.model & 0xff,
+ .model_msb = (ep->info.model >> 8) & 0xff,
+ .sw_revision = ep->info.sw_revision,
+ };
+
+ reply_ep_in(ep, &rep, sizeof(rep));
+}
+
+#define UMP_STREAM_PKT_BYTES 16 /* UMP stream packet size = 16 bytes*/
+#define UMP_STREAM_EP_STR_OFF 2 /* offset of name string for EP info */
+#define UMP_STREAM_FB_STR_OFF 3 /* offset of name string for FB info */
+
+/* Helper to replay a string */
+static void reply_ump_stream_string(struct f_midi2_ep *ep, const u8 *name,
+ unsigned int type, unsigned int extra,
+ unsigned int start_ofs)
+{
+ struct f_midi2_usb_ep *usb_ep = &ep->ep_in;
+ struct f_midi2 *midi2 = ep->card;
+ struct usb_request *req;
+ unsigned int pos;
+ u32 *buf;
+
+ if (!*name)
+ return;
+ req = get_empty_request(usb_ep);
+ if (!req)
+ return;
+
+ buf = (u32 *)req->buf;
+ pos = start_ofs;
+ for (;;) {
+ if (pos == start_ofs) {
+ memset(buf, 0, UMP_STREAM_PKT_BYTES);
+ buf[0] = ump_stream_compose(type, 0) | extra;
+ }
+ buf[pos / 4] |= *name++ << ((3 - (pos % 4)) * 8);
+ if (!*name) {
+ if (req->length)
+ buf[0] |= UMP_STREAM_MSG_FORMAT_END << 26;
+ req->length += UMP_STREAM_PKT_BYTES;
+ break;
+ }
+ if (++pos == UMP_STREAM_PKT_BYTES) {
+ if (!req->length)
+ buf[0] |= UMP_STREAM_MSG_FORMAT_START << 26;
+ else
+ buf[0] |= UMP_STREAM_MSG_FORMAT_CONTINUE << 26;
+ req->length += UMP_STREAM_PKT_BYTES;
+ if (midi2->info.req_buf_size - req->length < UMP_STREAM_PKT_BYTES)
+ break;
+ buf += 4;
+ pos = start_ofs;
+ }
+ }
+
+ if (req->length)
+ queue_request_ep_in(req);
+ else
+ put_empty_request(req);
+}
+
+/* Reply a UMP EP name string */
+static void reply_ump_stream_ep_name(struct f_midi2_ep *ep)
+{
+ reply_ump_stream_string(ep, ump_ep_name(ep),
+ UMP_STREAM_MSG_STATUS_EP_NAME, 0,
+ UMP_STREAM_EP_STR_OFF);
+}
+
+/* Reply a UMP EP product ID string */
+static void reply_ump_stream_ep_pid(struct f_midi2_ep *ep)
+{
+ reply_ump_stream_string(ep, ump_product_id(ep),
+ UMP_STREAM_MSG_STATUS_PRODUCT_ID, 0,
+ UMP_STREAM_EP_STR_OFF);
+}
+
+/* Reply a UMP EP stream config */
+static void reply_ump_stream_ep_config(struct f_midi2_ep *ep)
+{
+ struct snd_ump_stream_msg_stream_cfg rep = {
+ .type = UMP_MSG_TYPE_STREAM,
+ .status = UMP_STREAM_MSG_STATUS_STREAM_CFG,
+ };
+
+ if ((ep->info.protocol & SNDRV_UMP_EP_INFO_PROTO_MIDI_MASK) ==
+ SNDRV_UMP_EP_INFO_PROTO_MIDI2)
+ rep.protocol = UMP_STREAM_MSG_EP_INFO_CAP_MIDI2 >> 8;
+ else
+ rep.protocol = UMP_STREAM_MSG_EP_INFO_CAP_MIDI1 >> 8;
+
+ reply_ep_in(ep, &rep, sizeof(rep));
+}
+
+/* Reply a UMP FB info */
+static void reply_ump_stream_fb_info(struct f_midi2_ep *ep, int blk)
+{
+ struct f_midi2_block_info *b = &ep->blks[blk].info;
+ struct snd_ump_stream_msg_fb_info rep = {
+ .type = UMP_MSG_TYPE_STREAM,
+ .status = UMP_STREAM_MSG_STATUS_FB_INFO,
+ .active = !!b->active,
+ .function_block_id = blk,
+ .ui_hint = b->ui_hint,
+ .midi_10 = b->is_midi1,
+ .direction = b->direction,
+ .first_group = b->first_group,
+ .num_groups = b->num_groups,
+ .midi_ci_version = b->midi_ci_version,
+ .sysex8_streams = b->sysex8_streams,
+ };
+
+ reply_ep_in(ep, &rep, sizeof(rep));
+}
+
+/* Reply a FB name string */
+static void reply_ump_stream_fb_name(struct f_midi2_ep *ep, unsigned int blk)
+{
+ reply_ump_stream_string(ep, ump_fb_name(&ep->blks[blk].info),
+ UMP_STREAM_MSG_STATUS_FB_NAME, blk << 8,
+ UMP_STREAM_FB_STR_OFF);
+}
+
+/* Process a UMP Stream message */
+static void process_ump_stream_msg(struct f_midi2_ep *ep, const u32 *data)
+{
+ struct f_midi2 *midi2 = ep->card;
+ unsigned int format, status, blk;
+
+ format = ump_stream_message_format(*data);
+ status = ump_stream_message_status(*data);
+ switch (status) {
+ case UMP_STREAM_MSG_STATUS_EP_DISCOVERY:
+ if (format)
+ return; // invalid
+ if (data[1] & UMP_STREAM_MSG_REQUEST_EP_INFO)
+ reply_ump_stream_ep_info(ep);
+ if (data[1] & UMP_STREAM_MSG_REQUEST_DEVICE_INFO)
+ reply_ump_stream_ep_device(ep);
+ if (data[1] & UMP_STREAM_MSG_REQUEST_EP_NAME)
+ reply_ump_stream_ep_name(ep);
+ if (data[1] & UMP_STREAM_MSG_REQUEST_PRODUCT_ID)
+ reply_ump_stream_ep_pid(ep);
+ if (data[1] & UMP_STREAM_MSG_REQUEST_STREAM_CFG)
+ reply_ump_stream_ep_config(ep);
+ return;
+ case UMP_STREAM_MSG_STATUS_STREAM_CFG_REQUEST:
+ if (*data & UMP_STREAM_MSG_EP_INFO_CAP_MIDI2) {
+ ep->info.protocol = SNDRV_UMP_EP_INFO_PROTO_MIDI2;
+ DBG(midi2, "Switching Protocol to MIDI2\n");
+ } else {
+ ep->info.protocol = SNDRV_UMP_EP_INFO_PROTO_MIDI1;
+ DBG(midi2, "Switching Protocol to MIDI1\n");
+ }
+ snd_ump_switch_protocol(ep->ump, ep->info.protocol);
+ reply_ump_stream_ep_config(ep);
+ return;
+ case UMP_STREAM_MSG_STATUS_FB_DISCOVERY:
+ if (format)
+ return; // invalid
+ blk = (*data >> 8) & 0xff;
+ if (blk >= ep->num_blks)
+ return;
+ if (*data & UMP_STREAM_MSG_REQUEST_FB_INFO)
+ reply_ump_stream_fb_info(ep, blk);
+ if (*data & UMP_STREAM_MSG_REQUEST_FB_NAME)
+ reply_ump_stream_fb_name(ep, blk);
+ return;
+ }
+}
+
+/* Process UMP messages included in a USB request */
+static void process_ump(struct f_midi2_ep *ep, const struct usb_request *req)
+{
+ const u32 *data = (u32 *)req->buf;
+ int len = req->actual >> 2;
+ const u32 *in_buf = ep->ump->input_buf;
+
+ for (; len > 0; len--, data++) {
+ if (snd_ump_receive_ump_val(ep->ump, *data) <= 0)
+ continue;
+ if (ump_message_type(*in_buf) == UMP_MSG_TYPE_STREAM)
+ process_ump_stream_msg(ep, in_buf);
+ }
+}
+
+/*
+ * MIDI 2.0 UMP USB request handling
+ */
+
+/* complete handler for UMP EP-out requests */
+static void f_midi2_ep_out_complete(struct usb_ep *usb_ep,
+ struct usb_request *req)
+{
+ struct f_midi2_req_ctx *ctx = req->context;
+ struct f_midi2_ep *ep = ctx->usb_ep->ep;
+ struct f_midi2 *midi2 = ep->card;
+ int status = req->status;
+
+ if (status) {
+ DBG(midi2, "%s complete error %d: %d/%d\n",
+ usb_ep->name, status, req->actual, req->length);
+ goto error;
+ }
+
+ /* convert to UMP packet in native endianness */
+ le32_to_cpu_array((u32 *)req->buf, req->actual >> 2);
+
+ if (midi2->info.process_ump)
+ process_ump(ep, req);
+
+ snd_ump_receive(ep->ump, req->buf, req->actual & ~3);
+
+ if (midi2->operation_mode != MIDI_OP_MODE_MIDI2)
+ goto error;
+
+ if (queue_request_ep_raw(req))
+ goto error;
+ return;
+
+ error:
+ put_empty_request(req);
+}
+
+/* Transmit UMP packets received from user-space to the gadget */
+static void process_ump_transmit(struct f_midi2_ep *ep)
+{
+ struct f_midi2_usb_ep *usb_ep = &ep->ep_in;
+ struct f_midi2 *midi2 = ep->card;
+ struct usb_request *req;
+ int len;
+
+ if (!usb_ep->usb_ep->enabled)
+ return;
+
+ for (;;) {
+ req = get_empty_request(usb_ep);
+ if (!req)
+ break;
+ len = snd_ump_transmit(ep->ump, (u32 *)req->buf,
+ midi2->info.req_buf_size);
+ if (len <= 0) {
+ put_empty_request(req);
+ break;
+ }
+
+ req->length = len;
+ if (queue_request_ep_in(req) < 0)
+ break;
+ }
+}
+
+/* Complete handler for UMP EP-in requests */
+static void f_midi2_ep_in_complete(struct usb_ep *usb_ep,
+ struct usb_request *req)
+{
+ struct f_midi2_req_ctx *ctx = req->context;
+ struct f_midi2_ep *ep = ctx->usb_ep->ep;
+ struct f_midi2 *midi2 = ep->card;
+ int status = req->status;
+
+ put_empty_request(req);
+
+ if (status) {
+ DBG(midi2, "%s complete error %d: %d/%d\n",
+ usb_ep->name, status, req->actual, req->length);
+ return;
+ }
+
+ process_ump_transmit(ep);
+}
+
+/*
+ * MIDI1 (altset 0) USB request handling
+ */
+
+/* process one MIDI byte -- copied from f_midi.c
+ *
+ * fill the packet or request if needed
+ * returns true if the request became empty (queued)
+ */
+static bool process_midi1_byte(struct f_midi2 *midi2, u8 cable, u8 b,
+ struct usb_request **req_p)
+{
+ struct f_midi2_midi1_port *port = &midi2->midi1_port[cable];
+ u8 p[4] = { cable << 4, 0, 0, 0 };
+ int next_state = STATE_INITIAL;
+ struct usb_request *req = *req_p;
+
+ switch (b) {
+ case 0xf8 ... 0xff:
+ /* System Real-Time Messages */
+ p[0] |= 0x0f;
+ p[1] = b;
+ next_state = port->state;
+ port->state = STATE_REAL_TIME;
+ break;
+
+ case 0xf7:
+ /* End of SysEx */
+ switch (port->state) {
+ case STATE_SYSEX_0:
+ p[0] |= 0x05;
+ p[1] = 0xf7;
+ next_state = STATE_FINISHED;
+ break;
+ case STATE_SYSEX_1:
+ p[0] |= 0x06;
+ p[1] = port->data[0];
+ p[2] = 0xf7;
+ next_state = STATE_FINISHED;
+ break;
+ case STATE_SYSEX_2:
+ p[0] |= 0x07;
+ p[1] = port->data[0];
+ p[2] = port->data[1];
+ p[3] = 0xf7;
+ next_state = STATE_FINISHED;
+ break;
+ default:
+ /* Ignore byte */
+ next_state = port->state;
+ port->state = STATE_INITIAL;
+ }
+ break;
+
+ case 0xf0 ... 0xf6:
+ /* System Common Messages */
+ port->data[0] = port->data[1] = 0;
+ port->state = STATE_INITIAL;
+ switch (b) {
+ case 0xf0:
+ port->data[0] = b;
+ port->data[1] = 0;
+ next_state = STATE_SYSEX_1;
+ break;
+ case 0xf1:
+ case 0xf3:
+ port->data[0] = b;
+ next_state = STATE_1PARAM;
+ break;
+ case 0xf2:
+ port->data[0] = b;
+ next_state = STATE_2PARAM_1;
+ break;
+ case 0xf4:
+ case 0xf5:
+ next_state = STATE_INITIAL;
+ break;
+ case 0xf6:
+ p[0] |= 0x05;
+ p[1] = 0xf6;
+ next_state = STATE_FINISHED;
+ break;
+ }
+ break;
+
+ case 0x80 ... 0xef:
+ /*
+ * Channel Voice Messages, Channel Mode Messages
+ * and Control Change Messages.
+ */
+ port->data[0] = b;
+ port->data[1] = 0;
+ port->state = STATE_INITIAL;
+ if (b >= 0xc0 && b <= 0xdf)
+ next_state = STATE_1PARAM;
+ else
+ next_state = STATE_2PARAM_1;
+ break;
+
+ case 0x00 ... 0x7f:
+ /* Message parameters */
+ switch (port->state) {
+ case STATE_1PARAM:
+ if (port->data[0] < 0xf0)
+ p[0] |= port->data[0] >> 4;
+ else
+ p[0] |= 0x02;
+
+ p[1] = port->data[0];
+ p[2] = b;
+ /* This is to allow Running State Messages */
+ next_state = STATE_1PARAM;
+ break;
+ case STATE_2PARAM_1:
+ port->data[1] = b;
+ next_state = STATE_2PARAM_2;
+ break;
+ case STATE_2PARAM_2:
+ if (port->data[0] < 0xf0)
+ p[0] |= port->data[0] >> 4;
+ else
+ p[0] |= 0x03;
+
+ p[1] = port->data[0];
+ p[2] = port->data[1];
+ p[3] = b;
+ /* This is to allow Running State Messages */
+ next_state = STATE_2PARAM_1;
+ break;
+ case STATE_SYSEX_0:
+ port->data[0] = b;
+ next_state = STATE_SYSEX_1;
+ break;
+ case STATE_SYSEX_1:
+ port->data[1] = b;
+ next_state = STATE_SYSEX_2;
+ break;
+ case STATE_SYSEX_2:
+ p[0] |= 0x04;
+ p[1] = port->data[0];
+ p[2] = port->data[1];
+ p[3] = b;
+ next_state = STATE_SYSEX_0;
+ break;
+ }
+ break;
+ }
+
+ /* States where we have to write into the USB request */
+ if (next_state == STATE_FINISHED ||
+ port->state == STATE_SYSEX_2 ||
+ port->state == STATE_1PARAM ||
+ port->state == STATE_2PARAM_2 ||
+ port->state == STATE_REAL_TIME) {
+ memcpy(req->buf + req->length, p, sizeof(p));
+ req->length += sizeof(p);
+
+ if (next_state == STATE_FINISHED) {
+ next_state = STATE_INITIAL;
+ port->data[0] = port->data[1] = 0;
+ }
+
+ if (midi2->info.req_buf_size - req->length <= 4) {
+ queue_request_ep_raw(req);
+ *req_p = NULL;
+ return true;
+ }
+ }
+
+ port->state = next_state;
+ return false;
+}
+
+/* process all pending MIDI bytes in the internal buffer;
+ * returns true if the request gets empty
+ * returns false if all have been processed
+ */
+static bool process_midi1_pending_buf(struct f_midi2 *midi2,
+ struct usb_request **req_p)
+{
+ unsigned int cable, c;
+
+ for (cable = 0; cable < midi2->num_midi1_in; cable++) {
+ struct f_midi2_midi1_port *port = &midi2->midi1_port[cable];
+
+ if (!port->pending)
+ continue;
+ for (c = 0; c < port->pending; c++) {
+ if (process_midi1_byte(midi2, cable, port->buf[c],
+ req_p)) {
+ port->pending -= c;
+ if (port->pending)
+ memmove(port->buf, port->buf + c,
+ port->pending);
+ return true;
+ }
+ }
+ port->pending = 0;
+ }
+
+ return false;
+}
+
+/* fill the MIDI bytes onto the temporary buffer
+ */
+static void fill_midi1_pending_buf(struct f_midi2 *midi2, u8 cable, u8 *buf,
+ unsigned int size)
+{
+ struct f_midi2_midi1_port *port = &midi2->midi1_port[cable];
+
+ if (port->pending + size > sizeof(port->buf))
+ return;
+ memcpy(port->buf + port->pending, buf, size);
+ port->pending += size;
+}
+
+/* try to process data given from the associated UMP stream */
+static void process_midi1_transmit(struct f_midi2 *midi2)
+{
+ struct f_midi2_usb_ep *usb_ep = &midi2->midi1_ep_in;
+ struct f_midi2_ep *ep = &midi2->midi2_eps[0];
+ struct usb_request *req = NULL;
+ /* 12 is the largest outcome (4 MIDI1 cmds) for a single UMP packet */
+ unsigned char outbuf[12];
+ unsigned char group, cable;
+ int len, size;
+ u32 ump;
+
+ if (!usb_ep->usb_ep || !usb_ep->usb_ep->enabled)
+ return;
+
+ for (;;) {
+ if (!req) {
+ req = get_empty_request(usb_ep);
+ if (!req)
+ break;
+ }
+
+ if (process_midi1_pending_buf(midi2, &req))
+ continue;
+
+ len = snd_ump_transmit(ep->ump, &ump, 4);
+ if (len <= 0)
+ break;
+ if (snd_ump_receive_ump_val(ep->ump, ump) <= 0)
+ continue;
+ size = snd_ump_convert_from_ump(ep->ump->input_buf, outbuf,
+ &group);
+ if (size <= 0)
+ continue;
+ cable = ep->in_group_to_cable[group];
+ if (!cable)
+ continue;
+ cable--; /* to 0-base */
+ fill_midi1_pending_buf(midi2, cable, outbuf, size);
+ }
+
+ if (req) {
+ if (req->length)
+ queue_request_ep_raw(req);
+ else
+ put_empty_request(req);
+ }
+}
+
+/* complete handler for MIDI1 EP-in requests */
+static void f_midi2_midi1_ep_in_complete(struct usb_ep *usb_ep,
+ struct usb_request *req)
+{
+ struct f_midi2_req_ctx *ctx = req->context;
+ struct f_midi2 *midi2 = ctx->usb_ep->card;
+ int status = req->status;
+
+ put_empty_request(req);
+
+ if (status) {
+ DBG(midi2, "%s complete error %d: %d/%d\n",
+ usb_ep->name, status, req->actual, req->length);
+ return;
+ }
+
+ process_midi1_transmit(midi2);
+}
+
+/* complete handler for MIDI1 EP-out requests */
+static void f_midi2_midi1_ep_out_complete(struct usb_ep *usb_ep,
+ struct usb_request *req)
+{
+ struct f_midi2_req_ctx *ctx = req->context;
+ struct f_midi2 *midi2 = ctx->usb_ep->card;
+ struct f_midi2_ep *ep;
+ struct ump_cvt_to_ump *cvt = &midi2->midi1_ump_cvt;
+ static const u8 midi1_packet_bytes[16] = {
+ 0, 0, 2, 3, 3, 1, 2, 3, 3, 3, 3, 3, 2, 2, 3, 1
+ };
+ unsigned int group, cable, bytes, c, len;
+ int status = req->status;
+ const u8 *buf = req->buf;
+
+ if (status) {
+ DBG(midi2, "%s complete error %d: %d/%d\n",
+ usb_ep->name, status, req->actual, req->length);
+ goto error;
+ }
+
+ len = req->actual >> 2;
+ for (; len; len--, buf += 4) {
+ cable = *buf >> 4;
+ ep = midi2->out_cable_mapping[cable].ep;
+ if (!ep)
+ continue;
+ group = midi2->out_cable_mapping[cable].group;
+ bytes = midi1_packet_bytes[*buf & 0x0f];
+ for (c = 0; c < bytes; c++) {
+ snd_ump_convert_to_ump(cvt, group, ep->info.protocol,
+ buf[c + 1]);
+ if (cvt->ump_bytes) {
+ snd_ump_receive(ep->ump, cvt->ump,
+ cvt->ump_bytes);
+ cvt->ump_bytes = 0;
+ }
+ }
+ }
+
+ if (midi2->operation_mode != MIDI_OP_MODE_MIDI1)
+ goto error;
+
+ if (queue_request_ep_raw(req))
+ goto error;
+ return;
+
+ error:
+ put_empty_request(req);
+}
+
+/*
+ * Common EP handling helpers
+ */
+
+/* Start MIDI EP */
+static int f_midi2_start_ep(struct f_midi2_usb_ep *usb_ep,
+ struct usb_function *fn)
+{
+ int err;
+
+ if (!usb_ep->usb_ep)
+ return 0;
+
+ usb_ep_disable(usb_ep->usb_ep);
+ err = config_ep_by_speed(usb_ep->card->gadget, fn, usb_ep->usb_ep);
+ if (err)
+ return err;
+ return usb_ep_enable(usb_ep->usb_ep);
+}
+
+/* Drop pending requests */
+static void f_midi2_drop_reqs(struct f_midi2_usb_ep *usb_ep)
+{
+ int i;
+
+ if (!usb_ep->usb_ep || !usb_ep->num_reqs)
+ return;
+
+ for (i = 0; i < usb_ep->num_reqs; i++) {
+ if (!test_bit(i, &usb_ep->free_reqs) && usb_ep->reqs[i].req) {
+ usb_ep_dequeue(usb_ep->usb_ep, usb_ep->reqs[i].req);
+ set_bit(i, &usb_ep->free_reqs);
+ }
+ }
+}
+
+/* Allocate requests for the given EP */
+static int f_midi2_alloc_ep_reqs(struct f_midi2_usb_ep *usb_ep)
+{
+ struct f_midi2 *midi2 = usb_ep->card;
+ int i;
+
+ if (!usb_ep->usb_ep)
+ return 0;
+ if (!usb_ep->reqs)
+ return -EINVAL;
+
+ for (i = 0; i < midi2->info.num_reqs; i++) {
+ if (usb_ep->reqs[i].req)
+ continue;
+ usb_ep->reqs[i].req = alloc_ep_req(usb_ep->usb_ep,
+ midi2->info.req_buf_size);
+ if (!usb_ep->reqs[i].req)
+ return -ENOMEM;
+ usb_ep->reqs[i].req->context = &usb_ep->reqs[i];
+ }
+ return 0;
+}
+
+/* Free allocated requests */
+static void f_midi2_free_ep_reqs(struct f_midi2_usb_ep *usb_ep)
+{
+ struct f_midi2 *midi2 = usb_ep->card;
+ int i;
+
+ for (i = 0; i < midi2->info.num_reqs; i++) {
+ if (!usb_ep->reqs[i].req)
+ continue;
+ free_ep_req(usb_ep->usb_ep, usb_ep->reqs[i].req);
+ usb_ep->reqs[i].req = NULL;
+ }
+}
+
+/* Initialize EP */
+static int f_midi2_init_ep(struct f_midi2 *midi2, struct f_midi2_ep *ep,
+ struct f_midi2_usb_ep *usb_ep,
+ void *desc,
+ void (*complete)(struct usb_ep *usb_ep,
+ struct usb_request *req))
+{
+ int i;
+
+ usb_ep->card = midi2;
+ usb_ep->ep = ep;
+ usb_ep->usb_ep = usb_ep_autoconfig(midi2->gadget, desc);
+ if (!usb_ep->usb_ep)
+ return -ENODEV;
+ usb_ep->complete = complete;
+
+ usb_ep->reqs = kcalloc(midi2->info.num_reqs, sizeof(*usb_ep->reqs),
+ GFP_KERNEL);
+ if (!usb_ep->reqs)
+ return -ENOMEM;
+ for (i = 0; i < midi2->info.num_reqs; i++) {
+ usb_ep->reqs[i].index = i;
+ usb_ep->reqs[i].usb_ep = usb_ep;
+ set_bit(i, &usb_ep->free_reqs);
+ usb_ep->num_reqs++;
+ }
+
+ return 0;
+}
+
+/* Free EP */
+static void f_midi2_free_ep(struct f_midi2_usb_ep *usb_ep)
+{
+ f_midi2_drop_reqs(usb_ep);
+
+ f_midi2_free_ep_reqs(usb_ep);
+
+ kfree(usb_ep->reqs);
+ usb_ep->num_reqs = 0;
+ usb_ep->free_reqs = 0;
+ usb_ep->reqs = NULL;
+}
+
+/* Queue requests for EP-out at start */
+static void f_midi2_queue_out_reqs(struct f_midi2_usb_ep *usb_ep)
+{
+ int i, err;
+
+ if (!usb_ep->usb_ep)
+ return;
+
+ for (i = 0; i < usb_ep->num_reqs; i++) {
+ if (!test_bit(i, &usb_ep->free_reqs) || !usb_ep->reqs[i].req)
+ continue;
+ usb_ep->reqs[i].req->complete = usb_ep->complete;
+ err = usb_ep_queue(usb_ep->usb_ep, usb_ep->reqs[i].req,
+ GFP_ATOMIC);
+ if (!err)
+ clear_bit(i, &usb_ep->free_reqs);
+ }
+}
+
+/*
+ * Gadget Function callbacks
+ */
+
+/* stop both IN and OUT EPs */
+static void f_midi2_stop_eps(struct f_midi2_usb_ep *ep_in,
+ struct f_midi2_usb_ep *ep_out)
+{
+ f_midi2_drop_reqs(ep_in);
+ f_midi2_drop_reqs(ep_out);
+ f_midi2_free_ep_reqs(ep_in);
+ f_midi2_free_ep_reqs(ep_out);
+}
+
+/* start/queue both IN and OUT EPs */
+static int f_midi2_start_eps(struct f_midi2_usb_ep *ep_in,
+ struct f_midi2_usb_ep *ep_out,
+ struct usb_function *fn)
+{
+ int err;
+
+ err = f_midi2_start_ep(ep_in, fn);
+ if (err)
+ return err;
+ err = f_midi2_start_ep(ep_out, fn);
+ if (err)
+ return err;
+
+ err = f_midi2_alloc_ep_reqs(ep_in);
+ if (err)
+ return err;
+ err = f_midi2_alloc_ep_reqs(ep_out);
+ if (err)
+ return err;
+
+ f_midi2_queue_out_reqs(ep_out);
+ return 0;
+}
+
+/* gadget function set_alt callback */
+static int f_midi2_set_alt(struct usb_function *fn, unsigned int intf,
+ unsigned int alt)
+{
+ struct f_midi2 *midi2 = func_to_midi2(fn);
+ struct f_midi2_ep *ep;
+ int i, op_mode, err;
+
+ if (intf != midi2->midi_if || alt > 1)
+ return 0;
+
+ if (alt == 0)
+ op_mode = MIDI_OP_MODE_MIDI1;
+ else if (alt == 1)
+ op_mode = MIDI_OP_MODE_MIDI2;
+ else
+ op_mode = MIDI_OP_MODE_UNSET;
+
+ if (midi2->operation_mode == op_mode)
+ return 0;
+
+ midi2->operation_mode = op_mode;
+
+ if (op_mode != MIDI_OP_MODE_MIDI1)
+ f_midi2_stop_eps(&midi2->midi1_ep_in, &midi2->midi1_ep_out);
+
+ if (op_mode != MIDI_OP_MODE_MIDI2) {
+ for (i = 0; i < midi2->num_eps; i++) {
+ ep = &midi2->midi2_eps[i];
+ f_midi2_stop_eps(&ep->ep_in, &ep->ep_out);
+ }
+ }
+
+ if (op_mode == MIDI_OP_MODE_MIDI1)
+ return f_midi2_start_eps(&midi2->midi1_ep_in,
+ &midi2->midi1_ep_out, fn);
+
+ if (op_mode == MIDI_OP_MODE_MIDI2) {
+ for (i = 0; i < midi2->num_eps; i++) {
+ ep = &midi2->midi2_eps[i];
+
+ err = f_midi2_start_eps(&ep->ep_in, &ep->ep_out, fn);
+ if (err)
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+/* gadget function get_alt callback */
+static int f_midi2_get_alt(struct usb_function *fn, unsigned int intf)
+{
+ struct f_midi2 *midi2 = func_to_midi2(fn);
+
+ if (intf == midi2->midi_if &&
+ midi2->operation_mode == MIDI_OP_MODE_MIDI2)
+ return 1;
+ return 0;
+}
+
+/* convert UMP direction to USB MIDI 2.0 direction */
+static unsigned int ump_to_usb_dir(unsigned int ump_dir)
+{
+ switch (ump_dir) {
+ case SNDRV_UMP_DIR_INPUT:
+ return USB_MS_GR_TRM_BLOCK_TYPE_INPUT_ONLY;
+ case SNDRV_UMP_DIR_OUTPUT:
+ return USB_MS_GR_TRM_BLOCK_TYPE_OUTPUT_ONLY;
+ default:
+ return USB_MS_GR_TRM_BLOCK_TYPE_BIDIRECTIONAL;
+ }
+}
+
+/* assign GTB descriptors (for the given request) */
+static void assign_block_descriptors(struct f_midi2 *midi2,
+ struct usb_request *req,
+ int max_len)
+{
+ struct usb_ms20_gr_trm_block_header_descriptor header;
+ struct usb_ms20_gr_trm_block_descriptor *desc;
+ struct f_midi2_block_info *b;
+ struct f_midi2_ep *ep;
+ int i, blk, len;
+ char *data;
+
+ len = sizeof(gtb_header_desc) + sizeof(gtb_desc) * midi2->total_blocks;
+ if (WARN_ON(len > midi2->info.req_buf_size))
+ return;
+
+ header = gtb_header_desc;
+ header.wTotalLength = cpu_to_le16(len);
+ if (max_len < len) {
+ len = min_t(int, len, sizeof(header));
+ memcpy(req->buf, &header, len);
+ req->length = len;
+ req->zero = len < max_len;
+ return;
+ }
+
+ memcpy(req->buf, &header, sizeof(header));
+ data = req->buf + sizeof(header);
+ for (i = 0; i < midi2->num_eps; i++) {
+ ep = &midi2->midi2_eps[i];
+ for (blk = 0; blk < ep->num_blks; blk++) {
+ b = &ep->blks[blk].info;
+ desc = (struct usb_ms20_gr_trm_block_descriptor *)data;
+
+ *desc = gtb_desc;
+ desc->bGrpTrmBlkID = ep->blks[blk].gtb_id;
+ desc->bGrpTrmBlkType = ump_to_usb_dir(b->direction);
+ desc->nGroupTrm = b->first_group;
+ desc->nNumGroupTrm = b->num_groups;
+ desc->iBlockItem = ep->blks[blk].string_id;
+
+ if (ep->info.protocol & SNDRV_UMP_EP_INFO_PROTO_MIDI2)
+ desc->bMIDIProtocol = USB_MS_MIDI_PROTO_2_0;
+ else
+ desc->bMIDIProtocol = USB_MS_MIDI_PROTO_1_0_128;
+
+ if (b->is_midi1 == 2) {
+ desc->wMaxInputBandwidth = cpu_to_le16(1);
+ desc->wMaxOutputBandwidth = cpu_to_le16(1);
+ }
+
+ data += sizeof(*desc);
+ }
+ }
+
+ req->length = len;
+ req->zero = len < max_len;
+}
+
+/* gadget function setup callback: handle GTB requests */
+static int f_midi2_setup(struct usb_function *fn,
+ const struct usb_ctrlrequest *ctrl)
+{
+ struct f_midi2 *midi2 = func_to_midi2(fn);
+ struct usb_composite_dev *cdev = fn->config->cdev;
+ struct usb_request *req = cdev->req;
+ u16 value, length;
+
+ if ((ctrl->bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD ||
+ ctrl->bRequest != USB_REQ_GET_DESCRIPTOR)
+ return -EOPNOTSUPP;
+
+ value = le16_to_cpu(ctrl->wValue);
+ length = le16_to_cpu(ctrl->wLength);
+
+ if ((value >> 8) != USB_DT_CS_GR_TRM_BLOCK)
+ return -EOPNOTSUPP;
+
+ /* handle only altset 1 */
+ if ((value & 0xff) != 1)
+ return -EOPNOTSUPP;
+
+ assign_block_descriptors(midi2, req, length);
+ return usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+}
+
+/* gadget function disable callback */
+static void f_midi2_disable(struct usb_function *fn)
+{
+ struct f_midi2 *midi2 = func_to_midi2(fn);
+
+ midi2->operation_mode = MIDI_OP_MODE_UNSET;
+}
+
+/*
+ * ALSA UMP ops: most of them are NOPs, only trigger for write is needed
+ */
+static int f_midi2_ump_open(struct snd_ump_endpoint *ump, int dir)
+{
+ return 0;
+}
+
+static void f_midi2_ump_close(struct snd_ump_endpoint *ump, int dir)
+{
+}
+
+static void f_midi2_ump_trigger(struct snd_ump_endpoint *ump, int dir, int up)
+{
+ struct f_midi2_ep *ep = ump->private_data;
+ struct f_midi2 *midi2 = ep->card;
+
+ if (up && dir == SNDRV_RAWMIDI_STREAM_OUTPUT) {
+ switch (midi2->operation_mode) {
+ case MIDI_OP_MODE_MIDI1:
+ process_midi1_transmit(midi2);
+ break;
+ case MIDI_OP_MODE_MIDI2:
+ process_ump_transmit(ep);
+ break;
+ }
+ }
+}
+
+static void f_midi2_ump_drain(struct snd_ump_endpoint *ump, int dir)
+{
+}
+
+static const struct snd_ump_ops f_midi2_ump_ops = {
+ .open = f_midi2_ump_open,
+ .close = f_midi2_ump_close,
+ .trigger = f_midi2_ump_trigger,
+ .drain = f_midi2_ump_drain,
+};
+
+/*
+ * "Operation Mode" control element
+ */
+static int f_midi2_operation_mode_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = MIDI_OP_MODE_UNSET;
+ uinfo->value.integer.max = MIDI_OP_MODE_MIDI2;
+ return 0;
+}
+
+static int f_midi2_operation_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct f_midi2 *midi2 = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = midi2->operation_mode;
+ return 0;
+}
+
+static const struct snd_kcontrol_new operation_mode_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_RAWMIDI,
+ .name = "Operation Mode",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = f_midi2_operation_mode_info,
+ .get = f_midi2_operation_mode_get,
+};
+
+/*
+ * ALSA UMP instance creation / deletion
+ */
+static void f_midi2_free_card(struct f_midi2 *midi2)
+{
+ if (midi2->card) {
+ snd_card_free_when_closed(midi2->card);
+ midi2->card = NULL;
+ }
+}
+
+/* use a reverse direction for the gadget host */
+static int reverse_dir(int dir)
+{
+ if (!dir || dir == SNDRV_UMP_DIR_BIDIRECTION)
+ return dir;
+ return (dir == SNDRV_UMP_DIR_OUTPUT) ?
+ SNDRV_UMP_DIR_INPUT : SNDRV_UMP_DIR_OUTPUT;
+}
+
+static int f_midi2_create_card(struct f_midi2 *midi2)
+{
+ struct snd_card *card;
+ struct snd_ump_endpoint *ump;
+ struct f_midi2_ep *ep;
+ int i, id, blk, err;
+ __be32 sw;
+
+ err = snd_card_new(&midi2->gadget->dev, -1, NULL, THIS_MODULE, 0,
+ &card);
+ if (err < 0)
+ return err;
+ midi2->card = card;
+
+ strcpy(card->driver, "f_midi2");
+ strcpy(card->shortname, "MIDI 2.0 Gadget");
+ strcpy(card->longname, "MIDI 2.0 Gadget");
+
+ id = 0;
+ for (i = 0; i < midi2->num_eps; i++) {
+ ep = &midi2->midi2_eps[i];
+ err = snd_ump_endpoint_new(card, "MIDI 2.0 Gadget", id,
+ 1, 1, &ump);
+ if (err < 0)
+ goto error;
+ id++;
+
+ ep->ump = ump;
+ ump->no_process_stream = true;
+ ump->private_data = ep;
+ ump->ops = &f_midi2_ump_ops;
+ if (midi2->info.static_block)
+ ump->info.flags |= SNDRV_UMP_EP_INFO_STATIC_BLOCKS;
+ ump->info.protocol_caps = (ep->info.protocol_caps & 3) << 8;
+ ump->info.protocol = (ep->info.protocol & 3) << 8;
+ ump->info.version = 0x0101;
+ ump->info.family_id = ep->info.family;
+ ump->info.model_id = ep->info.model;
+ ump->info.manufacturer_id = ep->info.manufacturer & 0xffffff;
+ sw = cpu_to_be32(ep->info.sw_revision);
+ memcpy(ump->info.sw_revision, &sw, 4);
+
+ strscpy(ump->info.name, ump_ep_name(ep),
+ sizeof(ump->info.name));
+ strscpy(ump->info.product_id, ump_product_id(ep),
+ sizeof(ump->info.product_id));
+ strscpy(ump->core.name, ump->info.name, sizeof(ump->core.name));
+
+ for (blk = 0; blk < ep->num_blks; blk++) {
+ const struct f_midi2_block_info *b = &ep->blks[blk].info;
+ struct snd_ump_block *fb;
+
+ err = snd_ump_block_new(ump, blk,
+ reverse_dir(b->direction),
+ b->first_group, b->num_groups,
+ &ep->blks[blk].fb);
+ if (err < 0)
+ goto error;
+ fb = ep->blks[blk].fb;
+ fb->info.active = !!b->active;
+ fb->info.midi_ci_version = b->midi_ci_version;
+ fb->info.ui_hint = reverse_dir(b->ui_hint);
+ fb->info.sysex8_streams = b->sysex8_streams;
+ fb->info.flags |= b->is_midi1;
+ strscpy(fb->info.name, ump_fb_name(b),
+ sizeof(fb->info.name));
+ }
+ }
+
+ for (i = 0; i < midi2->num_eps; i++) {
+ err = snd_ump_attach_legacy_rawmidi(midi2->midi2_eps[i].ump,
+ "Legacy MIDI", id);
+ if (err < 0)
+ goto error;
+ id++;
+ }
+
+ err = snd_ctl_add(card, snd_ctl_new1(&operation_mode_ctl, midi2));
+ if (err < 0)
+ goto error;
+
+ err = snd_card_register(card);
+ if (err < 0)
+ goto error;
+
+ return 0;
+
+ error:
+ f_midi2_free_card(midi2);
+ return err;
+}
+
+/*
+ * Creation of USB descriptors
+ */
+struct f_midi2_usb_config {
+ struct usb_descriptor_header **list;
+ unsigned int size;
+ unsigned int alloc;
+
+ /* MIDI 1.0 jacks */
+ unsigned char jack_in, jack_out, jack_id;
+ struct usb_midi_in_jack_descriptor jack_ins[MAX_CABLES];
+ struct usb_midi_out_jack_descriptor_1 jack_outs[MAX_CABLES];
+};
+
+static int append_config(struct f_midi2_usb_config *config, void *d)
+{
+ unsigned int size;
+ void *buf;
+
+ if (config->size + 2 >= config->alloc) {
+ size = config->size + 16;
+ buf = krealloc(config->list, size * sizeof(void *), GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+ config->list = buf;
+ config->alloc = size;
+ }
+
+ config->list[config->size] = d;
+ config->size++;
+ config->list[config->size] = NULL;
+ return 0;
+}
+
+static int append_configs(struct f_midi2_usb_config *config, void **d)
+{
+ int err;
+
+ for (; *d; d++) {
+ err = append_config(config, *d);
+ if (err)
+ return err;
+ }
+ return 0;
+}
+
+static int append_midi1_in_jack(struct f_midi2 *midi2,
+ struct f_midi2_usb_config *config,
+ struct midi1_cable_mapping *map,
+ unsigned int type)
+{
+ struct usb_midi_in_jack_descriptor *jack =
+ &config->jack_ins[config->jack_in++];
+ int id = ++config->jack_id;
+ int err;
+
+ jack->bLength = 0x06;
+ jack->bDescriptorType = USB_DT_CS_INTERFACE;
+ jack->bDescriptorSubtype = USB_MS_MIDI_IN_JACK;
+ jack->bJackType = type;
+ jack->bJackID = id;
+ /* use the corresponding block name as jack name */
+ if (map->ep)
+ jack->iJack = map->ep->blks[map->block].string_id;
+
+ err = append_config(config, jack);
+ if (err < 0)
+ return err;
+ return id;
+}
+
+static int append_midi1_out_jack(struct f_midi2 *midi2,
+ struct f_midi2_usb_config *config,
+ struct midi1_cable_mapping *map,
+ unsigned int type, unsigned int source)
+{
+ struct usb_midi_out_jack_descriptor_1 *jack =
+ &config->jack_outs[config->jack_out++];
+ int id = ++config->jack_id;
+ int err;
+
+ jack->bLength = 0x09;
+ jack->bDescriptorType = USB_DT_CS_INTERFACE;
+ jack->bDescriptorSubtype = USB_MS_MIDI_OUT_JACK;
+ jack->bJackType = type;
+ jack->bJackID = id;
+ jack->bNrInputPins = 1;
+ jack->pins[0].baSourceID = source;
+ jack->pins[0].baSourcePin = 0x01;
+ /* use the corresponding block name as jack name */
+ if (map->ep)
+ jack->iJack = map->ep->blks[map->block].string_id;
+
+ err = append_config(config, jack);
+ if (err < 0)
+ return err;
+ return id;
+}
+
+static int f_midi2_create_usb_configs(struct f_midi2 *midi2,
+ struct f_midi2_usb_config *config,
+ int speed)
+{
+ void **midi1_in_eps, **midi1_out_eps;
+ int i, jack, total;
+ int err;
+
+ switch (speed) {
+ default:
+ case USB_SPEED_HIGH:
+ midi2_midi1_ep_out_desc.wMaxPacketSize = cpu_to_le16(512);
+ midi2_midi1_ep_in_desc.wMaxPacketSize = cpu_to_le16(512);
+ for (i = 0; i < midi2->num_eps; i++)
+ midi2_midi2_ep_out_desc[i].wMaxPacketSize =
+ cpu_to_le16(512);
+ fallthrough;
+ case USB_SPEED_FULL:
+ midi1_in_eps = midi2_midi1_ep_in_descs;
+ midi1_out_eps = midi2_midi1_ep_out_descs;
+ break;
+ case USB_SPEED_SUPER:
+ midi2_midi1_ep_out_desc.wMaxPacketSize = cpu_to_le16(1024);
+ midi2_midi1_ep_in_desc.wMaxPacketSize = cpu_to_le16(1024);
+ for (i = 0; i < midi2->num_eps; i++)
+ midi2_midi2_ep_out_desc[i].wMaxPacketSize =
+ cpu_to_le16(1024);
+ midi1_in_eps = midi2_midi1_ep_in_ss_descs;
+ midi1_out_eps = midi2_midi1_ep_out_ss_descs;
+ break;
+ }
+
+ err = append_configs(config, midi2_audio_descs);
+ if (err < 0)
+ return err;
+
+ if (midi2->num_midi1_in && midi2->num_midi1_out)
+ midi2_midi1_if_desc.bNumEndpoints = 2;
+ else
+ midi2_midi1_if_desc.bNumEndpoints = 1;
+
+ err = append_configs(config, midi2_midi1_descs);
+ if (err < 0)
+ return err;
+
+ total = USB_DT_MS_HEADER_SIZE;
+ if (midi2->num_midi1_out) {
+ midi2_midi1_ep_out_class_desc.bLength =
+ USB_DT_MS_ENDPOINT_SIZE(midi2->num_midi1_out);
+ total += midi2_midi1_ep_out_class_desc.bLength;
+ midi2_midi1_ep_out_class_desc.bNumEmbMIDIJack =
+ midi2->num_midi1_out;
+ total += midi2->num_midi1_out *
+ (USB_DT_MIDI_IN_SIZE + USB_DT_MIDI_OUT_SIZE(1));
+ for (i = 0; i < midi2->num_midi1_out; i++) {
+ jack = append_midi1_in_jack(midi2, config,
+ &midi2->in_cable_mapping[i],
+ USB_MS_EMBEDDED);
+ if (jack < 0)
+ return jack;
+ midi2_midi1_ep_out_class_desc.baAssocJackID[i] = jack;
+ jack = append_midi1_out_jack(midi2, config,
+ &midi2->in_cable_mapping[i],
+ USB_MS_EXTERNAL, jack);
+ if (jack < 0)
+ return jack;
+ }
+ }
+
+ if (midi2->num_midi1_in) {
+ midi2_midi1_ep_in_class_desc.bLength =
+ USB_DT_MS_ENDPOINT_SIZE(midi2->num_midi1_in);
+ total += midi2_midi1_ep_in_class_desc.bLength;
+ midi2_midi1_ep_in_class_desc.bNumEmbMIDIJack =
+ midi2->num_midi1_in;
+ total += midi2->num_midi1_in *
+ (USB_DT_MIDI_IN_SIZE + USB_DT_MIDI_OUT_SIZE(1));
+ for (i = 0; i < midi2->num_midi1_in; i++) {
+ jack = append_midi1_in_jack(midi2, config,
+ &midi2->out_cable_mapping[i],
+ USB_MS_EXTERNAL);
+ if (jack < 0)
+ return jack;
+ jack = append_midi1_out_jack(midi2, config,
+ &midi2->out_cable_mapping[i],
+ USB_MS_EMBEDDED, jack);
+ if (jack < 0)
+ return jack;
+ midi2_midi1_ep_in_class_desc.baAssocJackID[i] = jack;
+ }
+ }
+
+ midi2_midi1_class_desc.wTotalLength = cpu_to_le16(total);
+
+ if (midi2->num_midi1_out) {
+ err = append_configs(config, midi1_out_eps);
+ if (err < 0)
+ return err;
+ }
+ if (midi2->num_midi1_in) {
+ err = append_configs(config, midi1_in_eps);
+ if (err < 0)
+ return err;
+ }
+
+ err = append_configs(config, midi2_midi2_descs);
+ if (err < 0)
+ return err;
+
+ for (i = 0; i < midi2->num_eps; i++) {
+ err = append_config(config, &midi2_midi2_ep_out_desc[i]);
+ if (err < 0)
+ return err;
+ if (speed == USB_SPEED_SUPER || speed == USB_SPEED_SUPER_PLUS) {
+ err = append_config(config, &midi2_midi2_ep_out_ss_comp_desc);
+ if (err < 0)
+ return err;
+ }
+ err = append_config(config, &midi2_midi2_ep_out_class_desc[i]);
+ if (err < 0)
+ return err;
+ err = append_config(config, &midi2_midi2_ep_in_desc[i]);
+ if (err < 0)
+ return err;
+ if (speed == USB_SPEED_SUPER || speed == USB_SPEED_SUPER_PLUS) {
+ err = append_config(config, &midi2_midi2_ep_in_ss_comp_desc);
+ if (err < 0)
+ return err;
+ }
+ err = append_config(config, &midi2_midi2_ep_in_class_desc[i]);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static void f_midi2_free_usb_configs(struct f_midi2_usb_config *config)
+{
+ kfree(config->list);
+ memset(config, 0, sizeof(*config));
+}
+
+/* as we use the static descriptors for simplicity, serialize bind call */
+static DEFINE_MUTEX(f_midi2_desc_mutex);
+
+/* fill MIDI2 EP class-specific descriptor */
+static void fill_midi2_class_desc(struct f_midi2_ep *ep,
+ struct usb_ms20_endpoint_descriptor_32 *cdesc)
+{
+ int blk;
+
+ cdesc->bLength = USB_DT_MS20_ENDPOINT_SIZE(ep->num_blks);
+ cdesc->bDescriptorType = USB_DT_CS_ENDPOINT;
+ cdesc->bDescriptorSubtype = USB_MS_GENERAL_2_0;
+ cdesc->bNumGrpTrmBlock = ep->num_blks;
+ for (blk = 0; blk < ep->num_blks; blk++)
+ cdesc->baAssoGrpTrmBlkID[blk] = ep->blks[blk].gtb_id;
+}
+
+/* initialize MIDI2 EP-in */
+static int f_midi2_init_midi2_ep_in(struct f_midi2 *midi2, int index)
+{
+ struct f_midi2_ep *ep = &midi2->midi2_eps[index];
+ struct usb_endpoint_descriptor *desc = &midi2_midi2_ep_in_desc[index];
+
+ desc->bLength = USB_DT_ENDPOINT_SIZE;
+ desc->bDescriptorType = USB_DT_ENDPOINT;
+ desc->bEndpointAddress = USB_DIR_IN;
+ desc->bmAttributes = USB_ENDPOINT_XFER_INT;
+ desc->wMaxPacketSize = cpu_to_le16(EP_MAX_PACKET_INT);
+ desc->bInterval = 1;
+
+ fill_midi2_class_desc(ep, &midi2_midi2_ep_in_class_desc[index]);
+
+ return f_midi2_init_ep(midi2, ep, &ep->ep_in, desc,
+ f_midi2_ep_in_complete);
+}
+
+/* initialize MIDI2 EP-out */
+static int f_midi2_init_midi2_ep_out(struct f_midi2 *midi2, int index)
+{
+ struct f_midi2_ep *ep = &midi2->midi2_eps[index];
+ struct usb_endpoint_descriptor *desc = &midi2_midi2_ep_out_desc[index];
+
+ desc->bLength = USB_DT_ENDPOINT_SIZE;
+ desc->bDescriptorType = USB_DT_ENDPOINT;
+ desc->bEndpointAddress = USB_DIR_OUT;
+ desc->bmAttributes = USB_ENDPOINT_XFER_BULK;
+
+ fill_midi2_class_desc(ep, &midi2_midi2_ep_out_class_desc[index]);
+
+ return f_midi2_init_ep(midi2, ep, &ep->ep_out, desc,
+ f_midi2_ep_out_complete);
+}
+
+/* gadget function bind callback */
+static int f_midi2_bind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct usb_composite_dev *cdev = c->cdev;
+ struct f_midi2 *midi2 = func_to_midi2(f);
+ struct f_midi2_ep *ep;
+ struct f_midi2_usb_config config = {};
+ struct usb_gadget_strings string_fn = {
+ .language = 0x0409, /* en-us */
+ .strings = midi2->string_defs,
+ };
+ struct usb_gadget_strings *strings[] = {
+ &string_fn,
+ NULL,
+ };
+ int i, blk, status;
+
+ midi2->gadget = cdev->gadget;
+ midi2->operation_mode = MIDI_OP_MODE_UNSET;
+
+ status = f_midi2_create_card(midi2);
+ if (status < 0)
+ goto fail_register;
+
+ /* maybe allocate device-global string ID */
+ midi2->strings = usb_gstrings_attach(c->cdev, strings,
+ midi2->total_blocks + 1);
+ if (IS_ERR(midi2->strings)) {
+ status = PTR_ERR(midi2->strings);
+ goto fail_string;
+ }
+
+ mutex_lock(&f_midi2_desc_mutex);
+ midi2_midi1_if_desc.iInterface = midi2->strings[STR_IFACE].id;
+ midi2_midi2_if_desc.iInterface = midi2->strings[STR_IFACE].id;
+ for (i = 0; i < midi2->num_eps; i++) {
+ ep = &midi2->midi2_eps[i];
+ for (blk = 0; blk < ep->num_blks; blk++)
+ ep->blks[blk].string_id =
+ midi2->strings[gtb_to_str_id(ep->blks[blk].gtb_id)].id;
+ }
+
+ midi2_midi2_if_desc.bNumEndpoints = midi2->num_eps * 2;
+
+ /* audio interface */
+ status = usb_interface_id(c, f);
+ if (status < 0)
+ goto fail;
+ midi2_audio_if_desc.bInterfaceNumber = status;
+
+ /* MIDI streaming */
+ status = usb_interface_id(c, f);
+ if (status < 0)
+ goto fail;
+ midi2->midi_if = status;
+ midi2_midi1_if_desc.bInterfaceNumber = status;
+ midi2_midi2_if_desc.bInterfaceNumber = status;
+ midi2_audio_class_desc.baInterfaceNr[0] = status;
+
+ /* allocate instance-specific endpoints */
+ if (midi2->midi2_eps[0].blks[0].info.direction != SNDRV_UMP_DIR_OUTPUT) {
+ status = f_midi2_init_ep(midi2, NULL, &midi2->midi1_ep_in,
+ &midi2_midi1_ep_in_desc,
+ f_midi2_midi1_ep_in_complete);
+ if (status)
+ goto fail;
+ }
+
+ if (midi2->midi2_eps[0].blks[0].info.direction != SNDRV_UMP_DIR_INPUT) {
+ status = f_midi2_init_ep(midi2, NULL, &midi2->midi1_ep_out,
+ &midi2_midi1_ep_out_desc,
+ f_midi2_midi1_ep_out_complete);
+ if (status)
+ goto fail;
+ }
+
+ for (i = 0; i < midi2->num_eps; i++) {
+ status = f_midi2_init_midi2_ep_in(midi2, i);
+ if (status)
+ goto fail;
+ status = f_midi2_init_midi2_ep_out(midi2, i);
+ if (status)
+ goto fail;
+ }
+
+ status = f_midi2_create_usb_configs(midi2, &config, USB_SPEED_FULL);
+ if (status < 0)
+ goto fail;
+ f->fs_descriptors = usb_copy_descriptors(config.list);
+ if (!f->fs_descriptors) {
+ status = -ENOMEM;
+ goto fail;
+ }
+ f_midi2_free_usb_configs(&config);
+
+ status = f_midi2_create_usb_configs(midi2, &config, USB_SPEED_HIGH);
+ if (status < 0)
+ goto fail;
+ f->hs_descriptors = usb_copy_descriptors(config.list);
+ if (!f->hs_descriptors) {
+ status = -ENOMEM;
+ goto fail;
+ }
+ f_midi2_free_usb_configs(&config);
+
+ status = f_midi2_create_usb_configs(midi2, &config, USB_SPEED_SUPER);
+ if (status < 0)
+ goto fail;
+ f->ss_descriptors = usb_copy_descriptors(config.list);
+ if (!f->ss_descriptors) {
+ status = -ENOMEM;
+ goto fail;
+ }
+ f_midi2_free_usb_configs(&config);
+
+ mutex_unlock(&f_midi2_desc_mutex);
+ return 0;
+
+fail:
+ f_midi2_free_usb_configs(&config);
+ mutex_unlock(&f_midi2_desc_mutex);
+ usb_free_all_descriptors(f);
+fail_string:
+ f_midi2_free_card(midi2);
+fail_register:
+ ERROR(midi2, "%s: can't bind, err %d\n", f->name, status);
+ return status;
+}
+
+/* gadget function unbind callback */
+static void f_midi2_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct f_midi2 *midi2 = func_to_midi2(f);
+ int i;
+
+ f_midi2_free_card(midi2);
+
+ f_midi2_free_ep(&midi2->midi1_ep_in);
+ f_midi2_free_ep(&midi2->midi1_ep_out);
+ for (i = 0; i < midi2->num_eps; i++) {
+ f_midi2_free_ep(&midi2->midi2_eps[i].ep_in);
+ f_midi2_free_ep(&midi2->midi2_eps[i].ep_out);
+ }
+
+ usb_free_all_descriptors(f);
+}
+
+/*
+ * ConfigFS interface
+ */
+
+/* type conversion helpers */
+static inline struct f_midi2_opts *to_f_midi2_opts(struct config_item *item)
+{
+ return container_of(to_config_group(item), struct f_midi2_opts,
+ func_inst.group);
+}
+
+static inline struct f_midi2_ep_opts *
+to_f_midi2_ep_opts(struct config_item *item)
+{
+ return container_of(to_config_group(item), struct f_midi2_ep_opts,
+ group);
+}
+
+static inline struct f_midi2_block_opts *
+to_f_midi2_block_opts(struct config_item *item)
+{
+ return container_of(to_config_group(item), struct f_midi2_block_opts,
+ group);
+}
+
+/* trim the string to be usable for EP and FB name strings */
+static void make_name_string(char *s)
+{
+ char *p;
+
+ p = strchr(s, '\n');
+ if (p)
+ *p = 0;
+
+ p = s + strlen(s);
+ for (; p > s && isspace(*p); p--)
+ *p = 0;
+}
+
+/* configfs helpers: generic show/store for unisnged int */
+static ssize_t f_midi2_opts_uint_show(struct f_midi2_opts *opts,
+ u32 val, const char *format, char *page)
+{
+ int result;
+
+ mutex_lock(&opts->lock);
+ result = sprintf(page, format, val);
+ mutex_unlock(&opts->lock);
+ return result;
+}
+
+static ssize_t f_midi2_opts_uint_store(struct f_midi2_opts *opts,
+ u32 *valp, u32 minval, u32 maxval,
+ const char *page, size_t len)
+{
+ int ret;
+ u32 val;
+
+ mutex_lock(&opts->lock);
+ if (opts->refcnt) {
+ ret = -EBUSY;
+ goto end;
+ }
+
+ ret = kstrtou32(page, 0, &val);
+ if (ret)
+ goto end;
+ if (val < minval || val > maxval) {
+ ret = -EINVAL;
+ goto end;
+ }
+
+ *valp = val;
+ ret = len;
+
+end:
+ mutex_unlock(&opts->lock);
+ return ret;
+}
+
+/* generic store for bool */
+static ssize_t f_midi2_opts_bool_store(struct f_midi2_opts *opts,
+ bool *valp, const char *page, size_t len)
+{
+ int ret;
+ bool val;
+
+ mutex_lock(&opts->lock);
+ if (opts->refcnt) {
+ ret = -EBUSY;
+ goto end;
+ }
+
+ ret = kstrtobool(page, &val);
+ if (ret)
+ goto end;
+ *valp = val;
+ ret = len;
+
+end:
+ mutex_unlock(&opts->lock);
+ return ret;
+}
+
+/* generic show/store for string */
+static ssize_t f_midi2_opts_str_show(struct f_midi2_opts *opts,
+ const char *str, char *page)
+{
+ int result = 0;
+
+ mutex_lock(&opts->lock);
+ if (str)
+ result = scnprintf(page, PAGE_SIZE, "%s\n", str);
+ mutex_unlock(&opts->lock);
+ return result;
+}
+
+static ssize_t f_midi2_opts_str_store(struct f_midi2_opts *opts,
+ const char **strp, size_t maxlen,
+ const char *page, size_t len)
+{
+ char *c;
+ int ret;
+
+ mutex_lock(&opts->lock);
+ if (opts->refcnt) {
+ ret = -EBUSY;
+ goto end;
+ }
+
+ c = kstrndup(page, min(len, maxlen), GFP_KERNEL);
+ if (!c) {
+ ret = -ENOMEM;
+ goto end;
+ }
+
+ kfree(*strp);
+ make_name_string(c);
+ *strp = c;
+ ret = len;
+
+end:
+ mutex_unlock(&opts->lock);
+ return ret;
+}
+
+/*
+ * Definitions for UMP Block config
+ */
+
+/* define an uint option for block */
+#define F_MIDI2_BLOCK_OPT(name, format, minval, maxval) \
+static ssize_t f_midi2_block_opts_##name##_show(struct config_item *item,\
+ char *page) \
+{ \
+ struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item); \
+ return f_midi2_opts_uint_show(opts->ep->opts, opts->info.name, \
+ format "\n", page); \
+} \
+ \
+static ssize_t f_midi2_block_opts_##name##_store(struct config_item *item,\
+ const char *page, size_t len) \
+{ \
+ struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item); \
+ return f_midi2_opts_uint_store(opts->ep->opts, &opts->info.name,\
+ minval, maxval, page, len); \
+} \
+ \
+CONFIGFS_ATTR(f_midi2_block_opts_, name)
+
+/* define a boolean option for block */
+#define F_MIDI2_BLOCK_BOOL_OPT(name) \
+static ssize_t f_midi2_block_opts_##name##_show(struct config_item *item,\
+ char *page) \
+{ \
+ struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item); \
+ return f_midi2_opts_uint_show(opts->ep->opts, opts->info.name, \
+ "%u\n", page); \
+} \
+ \
+static ssize_t f_midi2_block_opts_##name##_store(struct config_item *item,\
+ const char *page, size_t len) \
+{ \
+ struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item); \
+ return f_midi2_opts_bool_store(opts->ep->opts, &opts->info.name,\
+ page, len); \
+} \
+ \
+CONFIGFS_ATTR(f_midi2_block_opts_, name)
+
+F_MIDI2_BLOCK_OPT(direction, "0x%x", 1, 3);
+F_MIDI2_BLOCK_OPT(first_group, "0x%x", 0, 15);
+F_MIDI2_BLOCK_OPT(num_groups, "0x%x", 1, 16);
+F_MIDI2_BLOCK_OPT(midi1_first_group, "0x%x", 0, 15);
+F_MIDI2_BLOCK_OPT(midi1_num_groups, "0x%x", 0, 16);
+F_MIDI2_BLOCK_OPT(ui_hint, "0x%x", 0, 3);
+F_MIDI2_BLOCK_OPT(midi_ci_version, "%u", 0, 1);
+F_MIDI2_BLOCK_OPT(sysex8_streams, "%u", 0, 255);
+F_MIDI2_BLOCK_OPT(is_midi1, "%u", 0, 2);
+F_MIDI2_BLOCK_BOOL_OPT(active);
+
+static ssize_t f_midi2_block_opts_name_show(struct config_item *item,
+ char *page)
+{
+ struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item);
+
+ return f_midi2_opts_str_show(opts->ep->opts, opts->info.name, page);
+}
+
+static ssize_t f_midi2_block_opts_name_store(struct config_item *item,
+ const char *page, size_t len)
+{
+ struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item);
+
+ return f_midi2_opts_str_store(opts->ep->opts, &opts->info.name, 128,
+ page, len);
+}
+
+CONFIGFS_ATTR(f_midi2_block_opts_, name);
+
+static struct configfs_attribute *f_midi2_block_attrs[] = {
+ &f_midi2_block_opts_attr_direction,
+ &f_midi2_block_opts_attr_first_group,
+ &f_midi2_block_opts_attr_num_groups,
+ &f_midi2_block_opts_attr_midi1_first_group,
+ &f_midi2_block_opts_attr_midi1_num_groups,
+ &f_midi2_block_opts_attr_ui_hint,
+ &f_midi2_block_opts_attr_midi_ci_version,
+ &f_midi2_block_opts_attr_sysex8_streams,
+ &f_midi2_block_opts_attr_is_midi1,
+ &f_midi2_block_opts_attr_active,
+ &f_midi2_block_opts_attr_name,
+ NULL,
+};
+
+static void f_midi2_block_opts_release(struct config_item *item)
+{
+ struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item);
+
+ kfree(opts->info.name);
+ kfree(opts);
+}
+
+static struct configfs_item_operations f_midi2_block_item_ops = {
+ .release = f_midi2_block_opts_release,
+};
+
+static const struct config_item_type f_midi2_block_type = {
+ .ct_item_ops = &f_midi2_block_item_ops,
+ .ct_attrs = f_midi2_block_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+/* create a f_midi2_block_opts instance for the given block number */
+static int f_midi2_block_opts_create(struct f_midi2_ep_opts *ep_opts,
+ unsigned int blk,
+ struct f_midi2_block_opts **block_p)
+{
+ struct f_midi2_block_opts *block_opts;
+ int ret = 0;
+
+ mutex_lock(&ep_opts->opts->lock);
+ if (ep_opts->opts->refcnt || ep_opts->blks[blk]) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ block_opts = kzalloc(sizeof(*block_opts), GFP_KERNEL);
+ if (!block_opts) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ block_opts->ep = ep_opts;
+ block_opts->id = blk;
+
+ /* set up the default values */
+ block_opts->info.direction = SNDRV_UMP_DIR_BIDIRECTION;
+ block_opts->info.first_group = 0;
+ block_opts->info.num_groups = 1;
+ block_opts->info.ui_hint = SNDRV_UMP_BLOCK_UI_HINT_BOTH;
+ block_opts->info.active = 1;
+
+ ep_opts->blks[blk] = block_opts;
+ *block_p = block_opts;
+
+ out:
+ mutex_unlock(&ep_opts->opts->lock);
+ return ret;
+}
+
+/* make_group callback for a block */
+static struct config_group *
+f_midi2_opts_block_make(struct config_group *group, const char *name)
+{
+ struct f_midi2_ep_opts *ep_opts;
+ struct f_midi2_block_opts *block_opts;
+ unsigned int blk;
+ int ret;
+
+ if (strncmp(name, "block.", 6))
+ return ERR_PTR(-EINVAL);
+ ret = kstrtouint(name + 6, 10, &blk);
+ if (ret)
+ return ERR_PTR(ret);
+
+ ep_opts = to_f_midi2_ep_opts(&group->cg_item);
+
+ if (blk >= SNDRV_UMP_MAX_BLOCKS)
+ return ERR_PTR(-EINVAL);
+ if (ep_opts->blks[blk])
+ return ERR_PTR(-EBUSY);
+ ret = f_midi2_block_opts_create(ep_opts, blk, &block_opts);
+ if (ret)
+ return ERR_PTR(ret);
+
+ config_group_init_type_name(&block_opts->group, name,
+ &f_midi2_block_type);
+ return &block_opts->group;
+}
+
+/* drop_item callback for a block */
+static void
+f_midi2_opts_block_drop(struct config_group *group, struct config_item *item)
+{
+ struct f_midi2_block_opts *block_opts = to_f_midi2_block_opts(item);
+
+ mutex_lock(&block_opts->ep->opts->lock);
+ block_opts->ep->blks[block_opts->id] = NULL;
+ mutex_unlock(&block_opts->ep->opts->lock);
+ config_item_put(item);
+}
+
+/*
+ * Definitions for UMP Endpoint config
+ */
+
+/* define an uint option for EP */
+#define F_MIDI2_EP_OPT(name, format, minval, maxval) \
+static ssize_t f_midi2_ep_opts_##name##_show(struct config_item *item, \
+ char *page) \
+{ \
+ struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item); \
+ return f_midi2_opts_uint_show(opts->opts, opts->info.name, \
+ format "\n", page); \
+} \
+ \
+static ssize_t f_midi2_ep_opts_##name##_store(struct config_item *item, \
+ const char *page, size_t len)\
+{ \
+ struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item); \
+ return f_midi2_opts_uint_store(opts->opts, &opts->info.name, \
+ minval, maxval, page, len); \
+} \
+ \
+CONFIGFS_ATTR(f_midi2_ep_opts_, name)
+
+/* define a string option for EP */
+#define F_MIDI2_EP_STR_OPT(name, maxlen) \
+static ssize_t f_midi2_ep_opts_##name##_show(struct config_item *item, \
+ char *page) \
+{ \
+ struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item); \
+ return f_midi2_opts_str_show(opts->opts, opts->info.name, page);\
+} \
+ \
+static ssize_t f_midi2_ep_opts_##name##_store(struct config_item *item, \
+ const char *page, size_t len) \
+{ \
+ struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item); \
+ return f_midi2_opts_str_store(opts->opts, &opts->info.name, maxlen,\
+ page, len); \
+} \
+ \
+CONFIGFS_ATTR(f_midi2_ep_opts_, name)
+
+F_MIDI2_EP_OPT(protocol, "0x%x", 1, 2);
+F_MIDI2_EP_OPT(protocol_caps, "0x%x", 1, 3);
+F_MIDI2_EP_OPT(manufacturer, "0x%x", 0, 0xffffff);
+F_MIDI2_EP_OPT(family, "0x%x", 0, 0xffff);
+F_MIDI2_EP_OPT(model, "0x%x", 0, 0xffff);
+F_MIDI2_EP_OPT(sw_revision, "0x%x", 0, 0xffffffff);
+F_MIDI2_EP_STR_OPT(ep_name, 128);
+F_MIDI2_EP_STR_OPT(product_id, 128);
+
+static struct configfs_attribute *f_midi2_ep_attrs[] = {
+ &f_midi2_ep_opts_attr_protocol,
+ &f_midi2_ep_opts_attr_protocol_caps,
+ &f_midi2_ep_opts_attr_ep_name,
+ &f_midi2_ep_opts_attr_product_id,
+ &f_midi2_ep_opts_attr_manufacturer,
+ &f_midi2_ep_opts_attr_family,
+ &f_midi2_ep_opts_attr_model,
+ &f_midi2_ep_opts_attr_sw_revision,
+ NULL,
+};
+
+static void f_midi2_ep_opts_release(struct config_item *item)
+{
+ struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item);
+
+ kfree(opts->info.ep_name);
+ kfree(opts->info.product_id);
+ kfree(opts);
+}
+
+static struct configfs_item_operations f_midi2_ep_item_ops = {
+ .release = f_midi2_ep_opts_release,
+};
+
+static struct configfs_group_operations f_midi2_ep_group_ops = {
+ .make_group = f_midi2_opts_block_make,
+ .drop_item = f_midi2_opts_block_drop,
+};
+
+static const struct config_item_type f_midi2_ep_type = {
+ .ct_item_ops = &f_midi2_ep_item_ops,
+ .ct_group_ops = &f_midi2_ep_group_ops,
+ .ct_attrs = f_midi2_ep_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+/* create a f_midi2_ep_opts instance */
+static int f_midi2_ep_opts_create(struct f_midi2_opts *opts,
+ unsigned int index,
+ struct f_midi2_ep_opts **ep_p)
+{
+ struct f_midi2_ep_opts *ep_opts;
+
+ ep_opts = kzalloc(sizeof(*ep_opts), GFP_KERNEL);
+ if (!ep_opts)
+ return -ENOMEM;
+
+ ep_opts->opts = opts;
+ ep_opts->index = index;
+
+ /* set up the default values */
+ ep_opts->info.protocol = 2;
+ ep_opts->info.protocol_caps = 3;
+
+ opts->eps[index] = ep_opts;
+ *ep_p = ep_opts;
+ return 0;
+}
+
+/* make_group callback for an EP */
+static struct config_group *
+f_midi2_opts_ep_make(struct config_group *group, const char *name)
+{
+ struct f_midi2_opts *opts;
+ struct f_midi2_ep_opts *ep_opts;
+ unsigned int index;
+ int ret;
+
+ if (strncmp(name, "ep.", 3))
+ return ERR_PTR(-EINVAL);
+ ret = kstrtouint(name + 3, 10, &index);
+ if (ret)
+ return ERR_PTR(ret);
+
+ opts = to_f_midi2_opts(&group->cg_item);
+ if (index >= MAX_UMP_EPS)
+ return ERR_PTR(-EINVAL);
+ if (opts->eps[index])
+ return ERR_PTR(-EBUSY);
+ ret = f_midi2_ep_opts_create(opts, index, &ep_opts);
+ if (ret)
+ return ERR_PTR(ret);
+
+ config_group_init_type_name(&ep_opts->group, name, &f_midi2_ep_type);
+ return &ep_opts->group;
+}
+
+/* drop_item callback for an EP */
+static void
+f_midi2_opts_ep_drop(struct config_group *group, struct config_item *item)
+{
+ struct f_midi2_ep_opts *ep_opts = to_f_midi2_ep_opts(item);
+
+ mutex_lock(&ep_opts->opts->lock);
+ ep_opts->opts->eps[ep_opts->index] = NULL;
+ mutex_unlock(&ep_opts->opts->lock);
+ config_item_put(item);
+}
+
+/*
+ * Definitions for card config
+ */
+
+/* define a bool option for card */
+#define F_MIDI2_BOOL_OPT(name) \
+static ssize_t f_midi2_opts_##name##_show(struct config_item *item, \
+ char *page) \
+{ \
+ struct f_midi2_opts *opts = to_f_midi2_opts(item); \
+ return f_midi2_opts_uint_show(opts, opts->info.name, \
+ "%u\n", page); \
+} \
+ \
+static ssize_t f_midi2_opts_##name##_store(struct config_item *item, \
+ const char *page, size_t len) \
+{ \
+ struct f_midi2_opts *opts = to_f_midi2_opts(item); \
+ return f_midi2_opts_bool_store(opts, &opts->info.name, \
+ page, len); \
+} \
+ \
+CONFIGFS_ATTR(f_midi2_opts_, name)
+
+F_MIDI2_BOOL_OPT(process_ump);
+F_MIDI2_BOOL_OPT(static_block);
+
+static ssize_t f_midi2_opts_iface_name_show(struct config_item *item,
+ char *page)
+{
+ struct f_midi2_opts *opts = to_f_midi2_opts(item);
+
+ return f_midi2_opts_str_show(opts, opts->info.iface_name, page);
+}
+
+static ssize_t f_midi2_opts_iface_name_store(struct config_item *item,
+ const char *page, size_t len)
+{
+ struct f_midi2_opts *opts = to_f_midi2_opts(item);
+
+ return f_midi2_opts_str_store(opts, &opts->info.iface_name, 128,
+ page, len);
+}
+
+CONFIGFS_ATTR(f_midi2_opts_, iface_name);
+
+static struct configfs_attribute *f_midi2_attrs[] = {
+ &f_midi2_opts_attr_process_ump,
+ &f_midi2_opts_attr_static_block,
+ &f_midi2_opts_attr_iface_name,
+ NULL
+};
+
+static void f_midi2_opts_release(struct config_item *item)
+{
+ struct f_midi2_opts *opts = to_f_midi2_opts(item);
+
+ usb_put_function_instance(&opts->func_inst);
+}
+
+static struct configfs_item_operations f_midi2_item_ops = {
+ .release = f_midi2_opts_release,
+};
+
+static struct configfs_group_operations f_midi2_group_ops = {
+ .make_group = f_midi2_opts_ep_make,
+ .drop_item = f_midi2_opts_ep_drop,
+};
+
+static const struct config_item_type f_midi2_func_type = {
+ .ct_item_ops = &f_midi2_item_ops,
+ .ct_group_ops = &f_midi2_group_ops,
+ .ct_attrs = f_midi2_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+static void f_midi2_free_inst(struct usb_function_instance *f)
+{
+ struct f_midi2_opts *opts;
+
+ opts = container_of(f, struct f_midi2_opts, func_inst);
+
+ kfree(opts->info.iface_name);
+ kfree(opts);
+}
+
+/* gadget alloc_inst */
+static struct usb_function_instance *f_midi2_alloc_inst(void)
+{
+ struct f_midi2_opts *opts;
+ struct f_midi2_ep_opts *ep_opts;
+ struct f_midi2_block_opts *block_opts;
+ int ret;
+
+ opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+ if (!opts)
+ return ERR_PTR(-ENOMEM);
+
+ mutex_init(&opts->lock);
+ opts->func_inst.free_func_inst = f_midi2_free_inst;
+ opts->info.process_ump = true;
+ opts->info.static_block = true;
+ opts->info.num_reqs = 32;
+ opts->info.req_buf_size = 512;
+
+ /* create the default ep */
+ ret = f_midi2_ep_opts_create(opts, 0, &ep_opts);
+ if (ret) {
+ kfree(opts);
+ return ERR_PTR(ret);
+ }
+
+ /* create the default block */
+ ret = f_midi2_block_opts_create(ep_opts, 0, &block_opts);
+ if (ret) {
+ kfree(ep_opts);
+ kfree(opts);
+ return ERR_PTR(ret);
+ }
+
+ /* set up the default MIDI1 (that is mandatory) */
+ block_opts->info.midi1_num_groups = 1;
+
+ config_group_init_type_name(&opts->func_inst.group, "",
+ &f_midi2_func_type);
+
+ config_group_init_type_name(&ep_opts->group, "ep.0",
+ &f_midi2_ep_type);
+ configfs_add_default_group(&ep_opts->group, &opts->func_inst.group);
+
+ config_group_init_type_name(&block_opts->group, "block.0",
+ &f_midi2_block_type);
+ configfs_add_default_group(&block_opts->group, &ep_opts->group);
+
+ return &opts->func_inst;
+}
+
+static void do_f_midi2_free(struct f_midi2 *midi2, struct f_midi2_opts *opts)
+{
+ mutex_lock(&opts->lock);
+ --opts->refcnt;
+ mutex_unlock(&opts->lock);
+ kfree(midi2->string_defs);
+ kfree(midi2);
+}
+
+static void f_midi2_free(struct usb_function *f)
+{
+ do_f_midi2_free(func_to_midi2(f),
+ container_of(f->fi, struct f_midi2_opts, func_inst));
+}
+
+/* verify the parameters set up via configfs;
+ * return the number of EPs or a negative error
+ */
+static int verify_parameters(struct f_midi2_opts *opts)
+{
+ int i, j, num_eps, num_blks;
+ struct f_midi2_ep_info *ep;
+ struct f_midi2_block_info *bp;
+
+ for (num_eps = 0; num_eps < MAX_UMP_EPS && opts->eps[num_eps];
+ num_eps++)
+ ;
+ if (!num_eps) {
+ pr_err("f_midi2: No EP is defined\n");
+ return -EINVAL;
+ }
+
+ num_blks = 0;
+ for (i = 0; i < num_eps; i++) {
+ ep = &opts->eps[i]->info;
+ if (!(ep->protocol_caps & ep->protocol)) {
+ pr_err("f_midi2: Invalid protocol 0x%x (caps 0x%x) for EP %d\n",
+ ep->protocol, ep->protocol_caps, i);
+ return -EINVAL;
+ }
+
+ for (j = 0; j < SNDRV_UMP_MAX_BLOCKS && opts->eps[i]->blks[j];
+ j++, num_blks++) {
+ bp = &opts->eps[i]->blks[j]->info;
+ if (bp->first_group + bp->num_groups > SNDRV_UMP_MAX_GROUPS) {
+ pr_err("f_midi2: Invalid group definitions for block %d:%d\n",
+ i, j);
+ return -EINVAL;
+ }
+
+ if (bp->midi1_num_groups) {
+ if (bp->midi1_first_group < bp->first_group ||
+ bp->midi1_first_group + bp->midi1_num_groups >
+ bp->first_group + bp->num_groups) {
+ pr_err("f_midi2: Invalid MIDI1 group definitions for block %d:%d\n",
+ i, j);
+ return -EINVAL;
+ }
+ }
+ }
+ }
+ if (!num_blks) {
+ pr_err("f_midi2: No block is defined\n");
+ return -EINVAL;
+ }
+
+ return num_eps;
+}
+
+/* fill mapping between MIDI 1.0 cable and UMP EP/group */
+static void fill_midi1_cable_mapping(struct f_midi2 *midi2,
+ struct f_midi2_ep *ep,
+ int blk)
+{
+ const struct f_midi2_block_info *binfo = &ep->blks[blk].info;
+ struct midi1_cable_mapping *map;
+ int i, group;
+
+ if (!binfo->midi1_num_groups)
+ return;
+ if (binfo->direction != SNDRV_UMP_DIR_OUTPUT) {
+ group = binfo->midi1_first_group;
+ map = midi2->in_cable_mapping + midi2->num_midi1_in;
+ for (i = 0; i < binfo->midi1_num_groups; i++, group++, map++) {
+ if (midi2->num_midi1_in >= MAX_CABLES)
+ break;
+ map->ep = ep;
+ map->block = blk;
+ map->group = group;
+ midi2->num_midi1_in++;
+ /* store 1-based cable number */
+ ep->in_group_to_cable[group] = midi2->num_midi1_in;
+ }
+ }
+
+ if (binfo->direction != SNDRV_UMP_DIR_INPUT) {
+ group = binfo->midi1_first_group;
+ map = midi2->out_cable_mapping + midi2->num_midi1_out;
+ for (i = 0; i < binfo->midi1_num_groups; i++, group++, map++) {
+ if (midi2->num_midi1_out >= MAX_CABLES)
+ break;
+ map->ep = ep;
+ map->block = blk;
+ map->group = group;
+ midi2->num_midi1_out++;
+ }
+ }
+}
+
+/* gadget alloc callback */
+static struct usb_function *f_midi2_alloc(struct usb_function_instance *fi)
+{
+ struct f_midi2 *midi2;
+ struct f_midi2_opts *opts;
+ struct f_midi2_ep *ep;
+ struct f_midi2_block *bp;
+ int i, num_eps, blk;
+
+ midi2 = kzalloc(sizeof(*midi2), GFP_KERNEL);
+ if (!midi2)
+ return ERR_PTR(-ENOMEM);
+
+ opts = container_of(fi, struct f_midi2_opts, func_inst);
+ mutex_lock(&opts->lock);
+ num_eps = verify_parameters(opts);
+ if (num_eps < 0) {
+ mutex_unlock(&opts->lock);
+ kfree(midi2);
+ return ERR_PTR(num_eps);
+ }
+ ++opts->refcnt;
+ mutex_unlock(&opts->lock);
+
+ spin_lock_init(&midi2->queue_lock);
+
+ midi2->func.name = "midi2_func";
+ midi2->func.bind = f_midi2_bind;
+ midi2->func.unbind = f_midi2_unbind;
+ midi2->func.get_alt = f_midi2_get_alt;
+ midi2->func.set_alt = f_midi2_set_alt;
+ midi2->func.setup = f_midi2_setup;
+ midi2->func.disable = f_midi2_disable;
+ midi2->func.free_func = f_midi2_free;
+
+ midi2->info = opts->info;
+ midi2->num_eps = num_eps;
+
+ for (i = 0; i < num_eps; i++) {
+ ep = &midi2->midi2_eps[i];
+ ep->info = opts->eps[i]->info;
+ ep->card = midi2;
+ for (blk = 0; blk < SNDRV_UMP_MAX_BLOCKS &&
+ opts->eps[i]->blks[blk]; blk++) {
+ bp = &ep->blks[blk];
+ ep->num_blks++;
+ bp->info = opts->eps[i]->blks[blk]->info;
+ bp->gtb_id = ++midi2->total_blocks;
+ }
+ }
+
+ midi2->string_defs = kcalloc(midi2->total_blocks + 1,
+ sizeof(*midi2->string_defs), GFP_KERNEL);
+ if (!midi2->string_defs) {
+ do_f_midi2_free(midi2, opts);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ if (opts->info.iface_name && *opts->info.iface_name)
+ midi2->string_defs[STR_IFACE].s = opts->info.iface_name;
+ else
+ midi2->string_defs[STR_IFACE].s = ump_ep_name(&midi2->midi2_eps[0]);
+
+ for (i = 0; i < midi2->num_eps; i++) {
+ ep = &midi2->midi2_eps[i];
+ for (blk = 0; blk < ep->num_blks; blk++) {
+ bp = &ep->blks[blk];
+ midi2->string_defs[gtb_to_str_id(bp->gtb_id)].s =
+ ump_fb_name(&bp->info);
+
+ fill_midi1_cable_mapping(midi2, ep, blk);
+ }
+ }
+
+ if (!midi2->num_midi1_in && !midi2->num_midi1_out) {
+ pr_err("f_midi2: MIDI1 definition is missing\n");
+ do_f_midi2_free(midi2, opts);
+ return ERR_PTR(-EINVAL);
+ }
+
+ return &midi2->func;
+}
+
+DECLARE_USB_FUNCTION_INIT(midi2, f_midi2_alloc_inst, f_midi2_alloc);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c
index 424bb3b666db..feccf4c8cc4f 100644
--- a/drivers/usb/gadget/function/f_ncm.c
+++ b/drivers/usb/gadget/function/f_ncm.c
@@ -80,21 +80,6 @@ static inline struct f_ncm *func_to_ncm(struct usb_function *f)
return container_of(f, struct f_ncm, port.func);
}
-/* peak (theoretical) bulk transfer rate in bits-per-second */
-static inline unsigned ncm_bitrate(struct usb_gadget *g)
-{
- if (!g)
- return 0;
- else if (gadget_is_superspeed(g) && g->speed >= USB_SPEED_SUPER_PLUS)
- return 4250000000U;
- else if (gadget_is_superspeed(g) && g->speed == USB_SPEED_SUPER)
- return 3750000000U;
- else if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
- return 13 * 512 * 8 * 1000 * 8;
- else
- return 19 * 64 * 1 * 1000 * 8;
-}
-
/*-------------------------------------------------------------------------*/
/*
@@ -576,10 +561,10 @@ static void ncm_do_notify(struct f_ncm *ncm)
/* SPEED_CHANGE data is up/down speeds in bits/sec */
data = req->buf + sizeof *event;
- data[0] = cpu_to_le32(ncm_bitrate(cdev->gadget));
+ data[0] = cpu_to_le32(gether_bitrate(cdev->gadget));
data[1] = data[0];
- DBG(cdev, "notify speed %u\n", ncm_bitrate(cdev->gadget));
+ DBG(cdev, "notify speed %u\n", gether_bitrate(cdev->gadget));
ncm->notify_state = NCM_NOTIFY_CONNECT;
break;
}
@@ -1544,9 +1529,7 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
hrtimer_init(&ncm->task_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
ncm->task_timer.function = ncm_tx_timeout;
- DBG(cdev, "CDC Network: %s speed IN/%s OUT/%s NOTIFY/%s\n",
- gadget_is_superspeed(c->cdev->gadget) ? "super" :
- gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
+ DBG(cdev, "CDC Network: IN/%s OUT/%s NOTIFY/%s\n",
ncm->port.in_ep->name, ncm->port.out_ep->name,
ncm->notify->name);
return 0;
diff --git a/drivers/usb/gadget/function/f_obex.c b/drivers/usb/gadget/function/f_obex.c
index ab26d84ed95e..dcb093210305 100644
--- a/drivers/usb/gadget/function/f_obex.c
+++ b/drivers/usb/gadget/function/f_obex.c
@@ -365,9 +365,8 @@ static int obex_bind(struct usb_configuration *c, struct usb_function *f)
if (status)
goto fail;
- dev_dbg(&cdev->gadget->dev, "obex ttyGS%d: %s speed IN/%s OUT/%s\n",
+ dev_dbg(&cdev->gadget->dev, "obex ttyGS%d: IN/%s OUT/%s\n",
obex->port_num,
- gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
obex->port.in->name, obex->port.out->name);
return 0;
diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c
index ee95e8f5f9d4..b47f99d17ee9 100644
--- a/drivers/usb/gadget/function/f_rndis.c
+++ b/drivers/usb/gadget/function/f_rndis.c
@@ -84,19 +84,6 @@ static inline struct f_rndis *func_to_rndis(struct usb_function *f)
return container_of(f, struct f_rndis, port.func);
}
-/* peak (theoretical) bulk transfer rate in bits-per-second */
-static unsigned int bitrate(struct usb_gadget *g)
-{
- if (gadget_is_superspeed(g) && g->speed >= USB_SPEED_SUPER_PLUS)
- return 4250000000U;
- if (gadget_is_superspeed(g) && g->speed == USB_SPEED_SUPER)
- return 3750000000U;
- else if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
- return 13 * 512 * 8 * 1000 * 8;
- else
- return 19 * 64 * 1 * 1000 * 8;
-}
-
/*-------------------------------------------------------------------------*/
/*
@@ -640,7 +627,7 @@ static void rndis_open(struct gether *geth)
DBG(cdev, "%s\n", __func__);
rndis_set_param_medium(rndis->params, RNDIS_MEDIUM_802_3,
- bitrate(cdev->gadget) / 100);
+ gether_bitrate(cdev->gadget) / 100);
rndis_signal_connect(rndis->params);
}
@@ -811,9 +798,7 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
* until we're activated via set_alt().
*/
- DBG(cdev, "RNDIS: %s speed IN/%s OUT/%s NOTIFY/%s\n",
- gadget_is_superspeed(c->cdev->gadget) ? "super" :
- gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
+ DBG(cdev, "RNDIS: IN/%s OUT/%s NOTIFY/%s\n",
rndis->port.in_ep->name, rndis->port.out_ep->name,
rndis->notify->name);
return 0;
diff --git a/drivers/usb/gadget/function/f_serial.c b/drivers/usb/gadget/function/f_serial.c
index a9480b9e312e..65c50092aea2 100644
--- a/drivers/usb/gadget/function/f_serial.c
+++ b/drivers/usb/gadget/function/f_serial.c
@@ -236,10 +236,8 @@ static int gser_bind(struct usb_configuration *c, struct usb_function *f)
gser_ss_function, gser_ss_function);
if (status)
goto fail;
- dev_dbg(&cdev->gadget->dev, "generic ttyGS%d: %s speed IN/%s OUT/%s\n",
+ dev_dbg(&cdev->gadget->dev, "generic ttyGS%d: IN/%s OUT/%s\n",
gser->port_num,
- gadget_is_superspeed(c->cdev->gadget) ? "super" :
- gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
gser->port.in->name, gser->port.out->name);
return 0;
diff --git a/drivers/usb/gadget/function/f_sourcesink.c b/drivers/usb/gadget/function/f_sourcesink.c
index 6803cd60cc6d..2edbd9b510d6 100644
--- a/drivers/usb/gadget/function/f_sourcesink.c
+++ b/drivers/usb/gadget/function/f_sourcesink.c
@@ -436,9 +436,7 @@ no_iso:
if (ret)
return ret;
- DBG(cdev, "%s speed %s: IN/%s, OUT/%s, ISO-IN/%s, ISO-OUT/%s\n",
- (gadget_is_superspeed(c->cdev->gadget) ? "super" :
- (gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full")),
+ DBG(cdev, "%s: IN/%s, OUT/%s, ISO-IN/%s, ISO-OUT/%s\n",
f->name, ss->in_ep->name, ss->out_ep->name,
ss->iso_in_ep ? ss->iso_in_ep->name : "<none>",
ss->iso_out_ep ? ss->iso_out_ep->name : "<none>");
diff --git a/drivers/usb/gadget/function/f_subset.c b/drivers/usb/gadget/function/f_subset.c
index 51c1cae162d9..8ae9689ef2a0 100644
--- a/drivers/usb/gadget/function/f_subset.c
+++ b/drivers/usb/gadget/function/f_subset.c
@@ -367,9 +367,7 @@ geth_bind(struct usb_configuration *c, struct usb_function *f)
* until we're activated via set_alt().
*/
- DBG(cdev, "CDC Subset: %s speed IN/%s OUT/%s\n",
- gadget_is_superspeed(c->cdev->gadget) ? "super" :
- gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
+ DBG(cdev, "CDC Subset: IN/%s OUT/%s\n",
geth->port.in_ep->name, geth->port.out_ep->name);
return 0;
diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c
index 5e919fb65833..faa398109431 100644
--- a/drivers/usb/gadget/function/f_uvc.c
+++ b/drivers/usb/gadget/function/f_uvc.c
@@ -719,21 +719,13 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
}
uvc->enable_interrupt_ep = opts->enable_interrupt_ep;
- if (gadget_is_superspeed(c->cdev->gadget))
- ep = usb_ep_autoconfig_ss(cdev->gadget, &uvc_ss_streaming_ep,
- &uvc_ss_streaming_comp);
- else if (gadget_is_dualspeed(cdev->gadget))
- ep = usb_ep_autoconfig(cdev->gadget, &uvc_hs_streaming_ep);
- else
- ep = usb_ep_autoconfig(cdev->gadget, &uvc_fs_streaming_ep);
-
+ ep = usb_ep_autoconfig(cdev->gadget, &uvc_fs_streaming_ep);
if (!ep) {
uvcg_info(f, "Unable to allocate streaming EP\n");
goto error;
}
uvc->video.ep = ep;
- uvc_fs_streaming_ep.bEndpointAddress = uvc->video.ep->address;
uvc_hs_streaming_ep.bEndpointAddress = uvc->video.ep->address;
uvc_ss_streaming_ep.bEndpointAddress = uvc->video.ep->address;
@@ -788,21 +780,19 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
f->fs_descriptors = NULL;
goto error;
}
- if (gadget_is_dualspeed(cdev->gadget)) {
- f->hs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_HIGH);
- if (IS_ERR(f->hs_descriptors)) {
- ret = PTR_ERR(f->hs_descriptors);
- f->hs_descriptors = NULL;
- goto error;
- }
+
+ f->hs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_HIGH);
+ if (IS_ERR(f->hs_descriptors)) {
+ ret = PTR_ERR(f->hs_descriptors);
+ f->hs_descriptors = NULL;
+ goto error;
}
- if (gadget_is_superspeed(c->cdev->gadget)) {
- f->ss_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_SUPER);
- if (IS_ERR(f->ss_descriptors)) {
- ret = PTR_ERR(f->ss_descriptors);
- f->ss_descriptors = NULL;
- goto error;
- }
+
+ f->ss_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_SUPER);
+ if (IS_ERR(f->ss_descriptors)) {
+ ret = PTR_ERR(f->ss_descriptors);
+ f->ss_descriptors = NULL;
+ goto error;
}
/* Preallocate control endpoint request. */
diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c
index a366abb45623..4bb0553da658 100644
--- a/drivers/usb/gadget/function/u_ether.c
+++ b/drivers/usb/gadget/function/u_ether.c
@@ -93,11 +93,10 @@ struct eth_dev {
#define DEFAULT_QLEN 2 /* double buffering by default */
-/* for dual-speed hardware, use deeper queues at high/super speed */
+/* use deeper queues at high/super speed */
static inline int qlen(struct usb_gadget *gadget, unsigned qmult)
{
- if (gadget_is_dualspeed(gadget) && (gadget->speed == USB_SPEED_HIGH ||
- gadget->speed >= USB_SPEED_SUPER))
+ if (gadget->speed == USB_SPEED_HIGH || gadget->speed >= USB_SPEED_SUPER)
return qmult * DEFAULT_QLEN;
else
return DEFAULT_QLEN;
diff --git a/drivers/usb/gadget/function/u_ether.h b/drivers/usb/gadget/function/u_ether.h
index 851ee10d6e63..34be220cef77 100644
--- a/drivers/usb/gadget/function/u_ether.h
+++ b/drivers/usb/gadget/function/u_ether.h
@@ -279,4 +279,17 @@ static inline bool can_support_ecm(struct usb_gadget *gadget)
return true;
}
+/* peak (theoretical) bulk transfer rate in bits-per-second */
+static inline unsigned int gether_bitrate(struct usb_gadget *g)
+{
+ if (g->speed >= USB_SPEED_SUPER_PLUS)
+ return 4250000000U;
+ if (g->speed == USB_SPEED_SUPER)
+ return 3750000000U;
+ else if (g->speed == USB_SPEED_HIGH)
+ return 13 * 512 * 8 * 1000 * 8;
+ else
+ return 19 * 64 * 1 * 1000 * 8;
+}
+
#endif /* __U_ETHER_H */
diff --git a/drivers/usb/gadget/function/u_midi2.h b/drivers/usb/gadget/function/u_midi2.h
new file mode 100644
index 000000000000..4e7adb41dfb7
--- /dev/null
+++ b/drivers/usb/gadget/function/u_midi2.h
@@ -0,0 +1,81 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Utility definitions for MIDI 2.0 function
+ */
+
+#ifndef U_MIDI2_H
+#define U_MIDI2_H
+
+#include <linux/usb/composite.h>
+#include <sound/asound.h>
+
+struct f_midi2_opts;
+struct f_midi2_ep_opts;
+struct f_midi2_block_opts;
+
+/* UMP Function Block info */
+struct f_midi2_block_info {
+ unsigned int direction; /* FB direction: 1-3 */
+ unsigned int first_group; /* first UMP group: 0-15 */
+ unsigned int num_groups; /* number of UMP groups: 1-16 */
+ unsigned int midi1_first_group; /* first UMP group for MIDI 1.0 */
+ unsigned int midi1_num_groups; /* number of UMP groups for MIDI 1.0 */
+ unsigned int ui_hint; /* UI-hint: 0-3 */
+ unsigned int midi_ci_version; /* MIDI-CI version: 0-255 */
+ unsigned int sysex8_streams; /* number of sysex8 streams: 0-255 */
+ unsigned int is_midi1; /* MIDI 1.0 port: 0-2 */
+ bool active; /* FB active flag: bool */
+ const char *name; /* FB name */
+};
+
+/* UMP Endpoint info */
+struct f_midi2_ep_info {
+ unsigned int protocol_caps; /* protocol capabilities: 1-3 */
+ unsigned int protocol; /* default protocol: 1-2 */
+ unsigned int manufacturer; /* manufacturer id: 0-0xffffff */
+ unsigned int family; /* device family id: 0-0xffff */
+ unsigned int model; /* device model id: 0x-0xffff */
+ unsigned int sw_revision; /* software revision: 32bit */
+
+ const char *ep_name; /* Endpoint name */
+ const char *product_id; /* Product ID */
+};
+
+struct f_midi2_card_info {
+ bool process_ump; /* process UMP stream: bool */
+ bool static_block; /* static FBs: bool */
+ unsigned int req_buf_size; /* request buffer size */
+ unsigned int num_reqs; /* number of requests */
+ const char *iface_name; /* interface name */
+};
+
+struct f_midi2_block_opts {
+ struct config_group group;
+ unsigned int id;
+ struct f_midi2_block_info info;
+ struct f_midi2_ep_opts *ep;
+};
+
+struct f_midi2_ep_opts {
+ struct config_group group;
+ unsigned int index;
+ struct f_midi2_ep_info info;
+ struct f_midi2_block_opts *blks[SNDRV_UMP_MAX_BLOCKS];
+ struct f_midi2_opts *opts;
+};
+
+#define MAX_UMP_EPS 4
+#define MAX_CABLES 16
+
+struct f_midi2_opts {
+ struct usb_function_instance func_inst;
+ struct mutex lock;
+ int refcnt;
+
+ struct f_midi2_card_info info;
+
+ unsigned int num_eps;
+ struct f_midi2_ep_opts *eps[MAX_UMP_EPS];
+};
+
+#endif /* U_MIDI2_H */
diff --git a/drivers/usb/gadget/function/u_phonet.h b/drivers/usb/gadget/function/u_phonet.h
index c53233b37192..ff62ca22c40d 100644
--- a/drivers/usb/gadget/function/u_phonet.h
+++ b/drivers/usb/gadget/function/u_phonet.h
@@ -20,7 +20,6 @@ struct f_phonet_opts {
struct net_device *gphonet_setup_default(void);
void gphonet_set_gadget(struct net_device *net, struct usb_gadget *g);
int gphonet_register_netdev(struct net_device *net);
-int phonet_bind_config(struct usb_configuration *c, struct net_device *dev);
void gphonet_cleanup(struct net_device *dev);
#endif /* __U_PHONET_H */
diff --git a/drivers/usb/gadget/function/u_serial.h b/drivers/usb/gadget/function/u_serial.h
index 102a7323a1fd..901d99310bc4 100644
--- a/drivers/usb/gadget/function/u_serial.h
+++ b/drivers/usb/gadget/function/u_serial.h
@@ -71,8 +71,4 @@ void gserial_disconnect(struct gserial *);
void gserial_suspend(struct gserial *p);
void gserial_resume(struct gserial *p);
-/* functions are bound to configurations by a config or gadget driver */
-int gser_bind_config(struct usb_configuration *c, u8 port_num);
-int obex_bind_config(struct usb_configuration *c, u8 port_num);
-
#endif /* __U_SERIAL_H */
diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h
index 100475b1363e..6751de8b63ad 100644
--- a/drivers/usb/gadget/function/uvc.h
+++ b/drivers/usb/gadget/function/uvc.h
@@ -178,8 +178,6 @@ struct uvc_file_handle {
*/
extern void uvc_function_setup_continue(struct uvc_device *uvc);
-extern void uvc_endpoint_stream(struct uvc_device *dev);
-
extern void uvc_function_connect(struct uvc_device *uvc);
extern void uvc_function_disconnect(struct uvc_device *uvc);
diff --git a/drivers/usb/gadget/udc/aspeed-vhub/core.c b/drivers/usb/gadget/udc/aspeed-vhub/core.c
index 16f2db8c4a2b..f60a019bb173 100644
--- a/drivers/usb/gadget/udc/aspeed-vhub/core.c
+++ b/drivers/usb/gadget/udc/aspeed-vhub/core.c
@@ -328,8 +328,7 @@ static int ast_vhub_probe(struct platform_device *pdev)
vhub->port_irq_mask = GENMASK(VHUB_IRQ_DEV1_BIT + vhub->max_ports - 1,
VHUB_IRQ_DEV1_BIT);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- vhub->regs = devm_ioremap_resource(&pdev->dev, res);
+ vhub->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(vhub->regs)) {
dev_err(&pdev->dev, "Failed to map resources\n");
return PTR_ERR(vhub->regs);
diff --git a/drivers/usb/gadget/udc/aspeed_udc.c b/drivers/usb/gadget/udc/aspeed_udc.c
index 01968e2167f9..2ef89a442f50 100644
--- a/drivers/usb/gadget/udc/aspeed_udc.c
+++ b/drivers/usb/gadget/udc/aspeed_udc.c
@@ -1468,7 +1468,6 @@ static int ast_udc_probe(struct platform_device *pdev)
enum usb_device_speed max_speed;
struct device *dev = &pdev->dev;
struct ast_udc_dev *udc;
- struct resource *res;
int rc;
udc = devm_kzalloc(&pdev->dev, sizeof(struct ast_udc_dev), GFP_KERNEL);
@@ -1484,8 +1483,7 @@ static int ast_udc_probe(struct platform_device *pdev)
udc->gadget.name = "aspeed-udc";
udc->gadget.dev.init_name = "gadget";
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- udc->reg = devm_ioremap_resource(&pdev->dev, res);
+ udc->reg = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(udc->reg)) {
dev_err(&pdev->dev, "Failed to map resources\n");
return PTR_ERR(udc->reg);
diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c
index 6c0ed3fa5eb1..02b1bef5e22e 100644
--- a/drivers/usb/gadget/udc/atmel_usba_udc.c
+++ b/drivers/usb/gadget/udc/atmel_usba_udc.c
@@ -2285,15 +2285,13 @@ static int usba_udc_probe(struct platform_device *pdev)
udc->gadget = usba_gadget_template;
INIT_LIST_HEAD(&udc->gadget.ep_list);
- res = platform_get_resource(pdev, IORESOURCE_MEM, CTRL_IOMEM_ID);
- udc->regs = devm_ioremap_resource(&pdev->dev, res);
+ udc->regs = devm_platform_get_and_ioremap_resource(pdev, CTRL_IOMEM_ID, &res);
if (IS_ERR(udc->regs))
return PTR_ERR(udc->regs);
dev_info(&pdev->dev, "MMIO registers at %pR mapped at %p\n",
res, udc->regs);
- res = platform_get_resource(pdev, IORESOURCE_MEM, FIFO_IOMEM_ID);
- udc->fifo = devm_ioremap_resource(&pdev->dev, res);
+ udc->fifo = devm_platform_get_and_ioremap_resource(pdev, FIFO_IOMEM_ID, &res);
if (IS_ERR(udc->fifo))
return PTR_ERR(udc->fifo);
dev_info(&pdev->dev, "FIFO at %pR mapped at %p\n", res, udc->fifo);
diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c
index 7d49d8a0b00c..7166d1117742 100644
--- a/drivers/usb/gadget/udc/core.c
+++ b/drivers/usb/gadget/udc/core.c
@@ -40,6 +40,7 @@ static const struct bus_type gadget_bus_type;
* @allow_connect: Indicates whether UDC is allowed to be pulled up.
* Set/cleared by gadget_(un)bind_driver() after gadget driver is bound or
* unbound.
+ * @vbus_work: work routine to handle VBUS status change notifications.
* @connect_lock: protects udc->started, gadget->connect,
* gadget->allow_connect and gadget->deactivate. The routines
* usb_gadget_connect_locked(), usb_gadget_disconnect_locked(),
diff --git a/drivers/usb/gadget/udc/fsl_qe_udc.c b/drivers/usb/gadget/udc/fsl_qe_udc.c
index 9c5dc1c1a68e..4aae86b47edf 100644
--- a/drivers/usb/gadget/udc/fsl_qe_udc.c
+++ b/drivers/usb/gadget/udc/fsl_qe_udc.c
@@ -1959,6 +1959,8 @@ static void ch9getstatus(struct qe_udc *udc, u8 request_type, u16 value,
} else if ((request_type & USB_RECIP_MASK) == USB_RECIP_ENDPOINT) {
/* Get endpoint status */
int pipe = index & USB_ENDPOINT_NUMBER_MASK;
+ if (pipe >= USB_MAX_ENDPOINTS)
+ goto stall;
struct qe_ep *target_ep = &udc->eps[pipe];
u16 usep;
diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c
index a67873a074b7..ee5705d336e3 100644
--- a/drivers/usb/gadget/udc/fsl_udc_core.c
+++ b/drivers/usb/gadget/udc/fsl_udc_core.c
@@ -36,7 +36,6 @@
#include <linux/platform_device.h>
#include <linux/fsl_devices.h>
#include <linux/dmapool.h>
-#include <linux/of_device.h>
#include <asm/byteorder.h>
#include <asm/io.h>
@@ -672,7 +671,7 @@ static int fsl_ep_disable(struct usb_ep *_ep)
static struct usb_request *
fsl_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
{
- struct fsl_req *req = NULL;
+ struct fsl_req *req;
req = kzalloc(sizeof *req, gfp_flags);
if (!req)
diff --git a/drivers/usb/gadget/udc/gr_udc.c b/drivers/usb/gadget/udc/gr_udc.c
index 09762559912d..c6dfa7cccc11 100644
--- a/drivers/usb/gadget/udc/gr_udc.c
+++ b/drivers/usb/gadget/udc/gr_udc.c
@@ -23,6 +23,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/errno.h>
@@ -36,9 +37,7 @@
#include <linux/dmapool.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
-#include <linux/of_platform.h>
-#include <linux/of_irq.h>
-#include <linux/of_address.h>
+#include <linux/of.h>
#include <asm/byteorder.h>
@@ -2137,15 +2136,15 @@ static int gr_probe(struct platform_device *pdev)
return PTR_ERR(regs);
dev->irq = platform_get_irq(pdev, 0);
- if (dev->irq <= 0)
- return -ENODEV;
+ if (dev->irq < 0)
+ return dev->irq;
/* Some core configurations has separate irqs for IN and OUT events */
dev->irqi = platform_get_irq(pdev, 1);
if (dev->irqi > 0) {
dev->irqo = platform_get_irq(pdev, 2);
- if (dev->irqo <= 0)
- return -ENODEV;
+ if (dev->irqo < 0)
+ return dev->irqo;
} else {
dev->irqi = 0;
}
diff --git a/drivers/usb/gadget/udc/max3420_udc.c b/drivers/usb/gadget/udc/max3420_udc.c
index 12c519f32bf7..2d57786d3db7 100644
--- a/drivers/usb/gadget/udc/max3420_udc.c
+++ b/drivers/usb/gadget/udc/max3420_udc.c
@@ -19,9 +19,7 @@
#include <linux/io.h>
#include <linux/module.h>
#include <linux/bitfield.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
-#include <linux/of_platform.h>
+#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/prefetch.h>
#include <linux/usb/ch9.h>
diff --git a/drivers/usb/gadget/udc/mv_u3d_core.c b/drivers/usb/gadget/udc/mv_u3d_core.c
index 3473048a85f5..2a421f0ff931 100644
--- a/drivers/usb/gadget/udc/mv_u3d_core.c
+++ b/drivers/usb/gadget/udc/mv_u3d_core.c
@@ -665,7 +665,7 @@ static int mv_u3d_ep_disable(struct usb_ep *_ep)
static struct usb_request *
mv_u3d_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
{
- struct mv_u3d_req *req = NULL;
+ struct mv_u3d_req *req;
req = kzalloc(sizeof *req, gfp_flags);
if (!req)
@@ -1779,7 +1779,7 @@ static void mv_u3d_remove(struct platform_device *dev)
static int mv_u3d_probe(struct platform_device *dev)
{
- struct mv_u3d *u3d = NULL;
+ struct mv_u3d *u3d;
struct mv_usb_platform_data *pdata = dev_get_platdata(&dev->dev);
int retval = 0;
struct resource *r;
diff --git a/drivers/usb/gadget/udc/mv_udc_core.c b/drivers/usb/gadget/udc/mv_udc_core.c
index 79db74e2040b..d888dcda2bc8 100644
--- a/drivers/usb/gadget/udc/mv_udc_core.c
+++ b/drivers/usb/gadget/udc/mv_udc_core.c
@@ -595,7 +595,7 @@ static int mv_ep_disable(struct usb_ep *_ep)
static struct usb_request *
mv_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
{
- struct mv_req *req = NULL;
+ struct mv_req *req;
req = kzalloc(sizeof *req, gfp_flags);
if (!req)
diff --git a/drivers/usb/gadget/udc/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c
index c4e1d957f913..61424cfd2e1c 100644
--- a/drivers/usb/gadget/udc/pxa27x_udc.c
+++ b/drivers/usb/gadget/udc/pxa27x_udc.c
@@ -23,7 +23,7 @@
#include <linux/prefetch.h>
#include <linux/byteorder/generic.h>
#include <linux/platform_data/pxa2xx_udc.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/usb.h>
diff --git a/drivers/usb/gadget/udc/renesas_usb3.c b/drivers/usb/gadget/udc/renesas_usb3.c
index 59bb25de2015..3b01734ce1b7 100644
--- a/drivers/usb/gadget/udc/renesas_usb3.c
+++ b/drivers/usb/gadget/udc/renesas_usb3.c
@@ -14,7 +14,7 @@
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
diff --git a/drivers/usb/gadget/udc/renesas_usbf.c b/drivers/usb/gadget/udc/renesas_usbf.c
index 6cd0af83e91e..657f265ac7cc 100644
--- a/drivers/usb/gadget/udc/renesas_usbf.c
+++ b/drivers/usb/gadget/udc/renesas_usbf.c
@@ -12,10 +12,9 @@
#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/kfifo.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
-#include <linux/of_address.h>
-#include <linux/of_irq.h>
-#include <linux/of_platform.h>
+#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/types.h>
#include <linux/usb/composite.h>
@@ -3379,7 +3378,6 @@ MODULE_DEVICE_TABLE(of, usbf_match);
static struct platform_driver udc_driver = {
.driver = {
.name = "usbf_renesas",
- .owner = THIS_MODULE,
.of_match_table = usbf_match,
},
.probe = usbf_probe,
diff --git a/drivers/usb/gadget/udc/snps_udc_plat.c b/drivers/usb/gadget/udc/snps_udc_plat.c
index 0ed685db149d..547af2ed9e5e 100644
--- a/drivers/usb/gadget/udc/snps_udc_plat.c
+++ b/drivers/usb/gadget/udc/snps_udc_plat.c
@@ -112,8 +112,7 @@ static int udc_plat_probe(struct platform_device *pdev)
spin_lock_init(&udc->lock);
udc->dev = dev;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- udc->virt_addr = devm_ioremap_resource(dev, res);
+ udc->virt_addr = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(udc->virt_addr))
return PTR_ERR(udc->virt_addr);
@@ -301,7 +300,6 @@ static const struct dev_pm_ops udc_plat_pm_ops = {
};
#endif
-#if defined(CONFIG_OF)
static const struct of_device_id of_udc_match[] = {
{ .compatible = "brcm,ns2-udc", },
{ .compatible = "brcm,cygnus-udc", },
@@ -309,14 +307,13 @@ static const struct of_device_id of_udc_match[] = {
{ }
};
MODULE_DEVICE_TABLE(of, of_udc_match);
-#endif
static struct platform_driver udc_plat_driver = {
.probe = udc_plat_probe,
.remove_new = udc_plat_remove,
.driver = {
.name = "snps-udc-plat",
- .of_match_table = of_match_ptr(of_udc_match),
+ .of_match_table = of_udc_match,
#ifdef CONFIG_PM_SLEEP
.pm = &udc_plat_pm_ops,
#endif
diff --git a/drivers/usb/gadget/udc/tegra-xudc.c b/drivers/usb/gadget/udc/tegra-xudc.c
index df6028f7b273..cb85168fd00c 100644
--- a/drivers/usb/gadget/udc/tegra-xudc.c
+++ b/drivers/usb/gadget/udc/tegra-xudc.c
@@ -16,7 +16,6 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/phy/phy.h>
#include <linux/phy/tegra/xusb.h>
#include <linux/pm_domain.h>
diff --git a/drivers/usb/gadget/udc/udc-xilinx.c b/drivers/usb/gadget/udc/udc-xilinx.c
index a4a7b90a97e7..56b8286a8009 100644
--- a/drivers/usb/gadget/udc/udc-xilinx.c
+++ b/drivers/usb/gadget/udc/udc-xilinx.c
@@ -18,10 +18,8 @@
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
-#include <linux/of_platform.h>
-#include <linux/of_irq.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
#include <linux/prefetch.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
@@ -1617,13 +1615,13 @@ static void xudc_getstatus(struct xusb_udc *udc)
case USB_RECIP_INTERFACE:
break;
case USB_RECIP_ENDPOINT:
- epnum = udc->setup.wIndex & USB_ENDPOINT_NUMBER_MASK;
+ epnum = le16_to_cpu(udc->setup.wIndex) & USB_ENDPOINT_NUMBER_MASK;
if (epnum >= XUSB_MAX_ENDPOINTS)
goto stall;
target_ep = &udc->ep[epnum];
epcfgreg = udc->read_fn(udc->addr + target_ep->offset);
halt = epcfgreg & XUSB_EP_CFG_STALL_MASK;
- if (udc->setup.wIndex & USB_DIR_IN) {
+ if (le16_to_cpu(udc->setup.wIndex) & USB_DIR_IN) {
if (!target_ep->is_in)
goto stall;
} else {
@@ -1638,7 +1636,7 @@ static void xudc_getstatus(struct xusb_udc *udc)
}
req->usb_req.length = 2;
- *(u16 *)req->usb_req.buf = cpu_to_le16(status);
+ *(__le16 *)req->usb_req.buf = cpu_to_le16(status);
ret = __xudc_ep0_queue(ep0, req);
if (ret == 0)
return;
@@ -1666,7 +1664,7 @@ static void xudc_set_clear_feature(struct xusb_udc *udc)
switch (udc->setup.bRequestType) {
case USB_RECIP_DEVICE:
- switch (udc->setup.wValue) {
+ switch (le16_to_cpu(udc->setup.wValue)) {
case USB_DEVICE_TEST_MODE:
/*
* The Test Mode will be executed
@@ -1686,13 +1684,15 @@ static void xudc_set_clear_feature(struct xusb_udc *udc)
break;
case USB_RECIP_ENDPOINT:
if (!udc->setup.wValue) {
- endpoint = udc->setup.wIndex & USB_ENDPOINT_NUMBER_MASK;
+ endpoint = le16_to_cpu(udc->setup.wIndex) &
+ USB_ENDPOINT_NUMBER_MASK;
if (endpoint >= XUSB_MAX_ENDPOINTS) {
xudc_ep0_stall(udc);
return;
}
target_ep = &udc->ep[endpoint];
- outinbit = udc->setup.wIndex & USB_ENDPOINT_DIR_MASK;
+ outinbit = le16_to_cpu(udc->setup.wIndex) &
+ USB_ENDPOINT_DIR_MASK;
outinbit = outinbit >> 7;
/* Make sure direction matches.*/
@@ -1755,9 +1755,9 @@ static void xudc_handle_setup(struct xusb_udc *udc)
memcpy(&setup, ep0rambase, 8);
udc->setup = setup;
- udc->setup.wValue = cpu_to_le16(setup.wValue);
- udc->setup.wIndex = cpu_to_le16(setup.wIndex);
- udc->setup.wLength = cpu_to_le16(setup.wLength);
+ udc->setup.wValue = cpu_to_le16((u16 __force)setup.wValue);
+ udc->setup.wIndex = cpu_to_le16((u16 __force)setup.wIndex);
+ udc->setup.wLength = cpu_to_le16((u16 __force)setup.wLength);
/* Clear previous requests */
xudc_nuke(ep0, -ECONNRESET);
@@ -1869,7 +1869,7 @@ static void xudc_ep0_in(struct xusb_udc *udc)
u16 count = 0;
u16 length;
u8 *ep0rambase;
- u8 test_mode = udc->setup.wIndex >> 8;
+ u8 test_mode = le16_to_cpu(udc->setup.wIndex) >> 8;
req = list_first_entry(&ep0->queue, struct xusb_req, queue);
bytes_to_tx = req->usb_req.length - req->usb_req.actual;
@@ -1880,12 +1880,12 @@ static void xudc_ep0_in(struct xusb_udc *udc)
case USB_REQ_SET_ADDRESS:
/* Set the address of the device.*/
udc->write_fn(udc->addr, XUSB_ADDRESS_OFFSET,
- udc->setup.wValue);
+ le16_to_cpu(udc->setup.wValue));
break;
case USB_REQ_SET_FEATURE:
if (udc->setup.bRequestType ==
USB_RECIP_DEVICE) {
- if (udc->setup.wValue ==
+ if (le16_to_cpu(udc->setup.wValue) ==
USB_DEVICE_TEST_MODE)
udc->write_fn(udc->addr,
XUSB_TESTMODE_OFFSET,
@@ -2080,8 +2080,7 @@ static int xudc_probe(struct platform_device *pdev)
udc->req->usb_req.buf = buff;
/* Map the registers */
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- udc->addr = devm_ioremap_resource(&pdev->dev, res);
+ udc->addr = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(udc->addr))
return PTR_ERR(udc->addr);
diff --git a/drivers/usb/host/ehci-atmel.c b/drivers/usb/host/ehci-atmel.c
index 61808c51e702..6a6e1c510b28 100644
--- a/drivers/usb/host/ehci-atmel.c
+++ b/drivers/usb/host/ehci-atmel.c
@@ -102,8 +102,8 @@ static int ehci_atmel_drv_probe(struct platform_device *pdev)
pr_debug("Initializing Atmel-SoC USB Host Controller\n");
irq = platform_get_irq(pdev, 0);
- if (irq <= 0) {
- retval = -ENODEV;
+ if (irq < 0) {
+ retval = irq;
goto fail_create_hcd;
}
@@ -122,8 +122,7 @@ static int ehci_atmel_drv_probe(struct platform_device *pdev)
}
atmel_ehci = hcd_to_atmel_ehci_priv(hcd);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+ hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(hcd->regs)) {
retval = PTR_ERR(hcd->regs);
goto fail_request_resource;
diff --git a/drivers/usb/host/ehci-brcm.c b/drivers/usb/host/ehci-brcm.c
index 0362a082abb4..77e42c739c58 100644
--- a/drivers/usb/host/ehci-brcm.c
+++ b/drivers/usb/host/ehci-brcm.c
@@ -140,8 +140,8 @@ static int ehci_brcm_probe(struct platform_device *pdev)
return err;
irq = platform_get_irq(pdev, 0);
- if (irq <= 0)
- return irq ? irq : -EINVAL;
+ if (irq < 0)
+ return irq;
/* Hook the hub control routine to work around a bug */
ehci_brcm_hc_driver.hub_control = ehci_brcm_hub_control;
diff --git a/drivers/usb/host/ehci-exynos.c b/drivers/usb/host/ehci-exynos.c
index 20f8c0ec6810..f644b131cc0b 100644
--- a/drivers/usb/host/ehci-exynos.c
+++ b/drivers/usb/host/ehci-exynos.c
@@ -173,8 +173,7 @@ static int exynos_ehci_probe(struct platform_device *pdev)
if (err)
goto fail_clk;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+ hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(hcd->regs)) {
err = PTR_ERR(hcd->regs);
goto fail_io;
diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c
index 81d60a695510..5b1ce394a417 100644
--- a/drivers/usb/host/ehci-fsl.c
+++ b/drivers/usb/host/ehci-fsl.c
@@ -22,7 +22,7 @@
#include <linux/usb/otg.h>
#include <linux/platform_device.h>
#include <linux/fsl_devices.h>
-#include <linux/of_platform.h>
+#include <linux/of.h>
#include <linux/io.h>
#include "ehci.h"
@@ -87,8 +87,7 @@ static int fsl_ehci_drv_probe(struct platform_device *pdev)
goto err1;
}
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+ hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(hcd->regs)) {
retval = PTR_ERR(hcd->regs);
goto err2;
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index a1930db0da1c..802bfafb1012 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -755,10 +755,14 @@ restart:
/* normal [4.15.1.2] or error [4.15.1.1] completion */
if (likely ((status & (STS_INT|STS_ERR)) != 0)) {
- if (likely ((status & STS_ERR) == 0))
+ if (likely ((status & STS_ERR) == 0)) {
INCR(ehci->stats.normal);
- else
+ } else {
+ /* Force to check port status */
+ if (ehci->has_ci_pec_bug)
+ status |= STS_PCD;
INCR(ehci->stats.error);
+ }
bh = 1;
}
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index efe30e3be22f..1aee392e8492 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -674,7 +674,8 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
if ((temp & mask) != 0 || test_bit(i, &ehci->port_c_suspend)
|| (ehci->reset_done[i] && time_after_eq(
- jiffies, ehci->reset_done[i]))) {
+ jiffies, ehci->reset_done[i]))
+ || ehci_has_ci_pec_bug(ehci, temp)) {
if (i < 7)
buf [0] |= 1 << (i + 1);
else
@@ -875,6 +876,13 @@ int ehci_hub_control(
if (temp & PORT_PEC)
status |= USB_PORT_STAT_C_ENABLE << 16;
+ if (ehci_has_ci_pec_bug(ehci, temp)) {
+ status |= USB_PORT_STAT_C_ENABLE << 16;
+ ehci_info(ehci,
+ "PE is cleared by HW port:%d PORTSC:%08x\n",
+ wIndex + 1, temp);
+ }
+
if ((temp & PORT_OCC) && (!ignore_oc && !ehci->spurious_oc)){
status |= USB_PORT_STAT_C_OVERCURRENT << 16;
diff --git a/drivers/usb/host/ehci-mv.c b/drivers/usb/host/ehci-mv.c
index 9320cf0e5bc7..2f1fc7eb8b72 100644
--- a/drivers/usb/host/ehci-mv.c
+++ b/drivers/usb/host/ehci-mv.c
@@ -142,8 +142,7 @@ static int mv_ehci_probe(struct platform_device *pdev)
goto err_put_hcd;
}
- r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- ehci_mv->base = devm_ioremap_resource(&pdev->dev, r);
+ ehci_mv->base = devm_platform_get_and_ioremap_resource(pdev, 0, &r);
if (IS_ERR(ehci_mv->base)) {
retval = PTR_ERR(ehci_mv->base);
goto err_put_hcd;
diff --git a/drivers/usb/host/ehci-npcm7xx.c b/drivers/usb/host/ehci-npcm7xx.c
index ad79ce63afcf..3d3317a1a0b3 100644
--- a/drivers/usb/host/ehci-npcm7xx.c
+++ b/drivers/usb/host/ehci-npcm7xx.c
@@ -53,7 +53,7 @@ static int npcm7xx_ehci_hcd_drv_probe(struct platform_device *pdev)
int irq;
int retval;
- dev_dbg(&pdev->dev, "initializing npcm7xx ehci USB Controller\n");
+ dev_dbg(&pdev->dev, "initializing npcm7xx ehci USB Controller\n");
if (usb_disabled())
return -ENODEV;
@@ -79,8 +79,7 @@ static int npcm7xx_ehci_hcd_drv_probe(struct platform_device *pdev)
goto fail;
}
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+ hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(hcd->regs)) {
retval = PTR_ERR(hcd->regs);
goto err_put_hcd;
diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c
index cb6509a735ac..b24f371a46f3 100644
--- a/drivers/usb/host/ehci-omap.c
+++ b/drivers/usb/host/ehci-omap.c
@@ -113,8 +113,7 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev)
if (irq < 0)
return irq;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- regs = devm_ioremap_resource(dev, res);
+ regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(regs))
return PTR_ERR(regs);
diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c
index 2cfb27dc943a..6c47ab0a491d 100644
--- a/drivers/usb/host/ehci-orion.c
+++ b/drivers/usb/host/ehci-orion.c
@@ -13,8 +13,6 @@
#include <linux/platform_data/usb-ehci-orion.h>
#include <linux/of.h>
#include <linux/phy/phy.h>
-#include <linux/of_device.h>
-#include <linux/of_irq.h>
#include <linux/usb.h>
#include <linux/usb/hcd.h>
#include <linux/io.h>
@@ -220,8 +218,8 @@ static int ehci_orion_drv_probe(struct platform_device *pdev)
pr_debug("Initializing Orion-SoC USB Host Controller\n");
irq = platform_get_irq(pdev, 0);
- if (irq <= 0) {
- err = -ENODEV;
+ if (irq < 0) {
+ err = irq;
goto err;
}
@@ -234,8 +232,7 @@ static int ehci_orion_drv_probe(struct platform_device *pdev)
if (err)
goto err;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- regs = devm_ioremap_resource(&pdev->dev, res);
+ regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(regs)) {
err = PTR_ERR(regs);
goto err;
diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c
index 83bf56c9424f..98b073185e1c 100644
--- a/drivers/usb/host/ehci-platform.c
+++ b/drivers/usb/host/ehci-platform.c
@@ -359,8 +359,7 @@ static int ehci_platform_probe(struct platform_device *dev)
goto err_reset;
}
- res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0);
- hcd->regs = devm_ioremap_resource(&dev->dev, res_mem);
+ hcd->regs = devm_platform_get_and_ioremap_resource(dev, 0, &res_mem);
if (IS_ERR(hcd->regs)) {
err = PTR_ERR(hcd->regs);
goto err_power;
diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c
index bd542b6fc46b..7e834587e7de 100644
--- a/drivers/usb/host/ehci-sched.c
+++ b/drivers/usb/host/ehci-sched.c
@@ -490,13 +490,14 @@ static int tt_no_collision(
static void enable_periodic(struct ehci_hcd *ehci)
{
if (ehci->periodic_count++)
- return;
+ goto out;
/* Stop waiting to turn off the periodic schedule */
ehci->enabled_hrtimer_events &= ~BIT(EHCI_HRTIMER_DISABLE_PERIODIC);
/* Don't start the schedule until PSS is 0 */
ehci_poll_PSS(ehci);
+out:
turn_on_io_watchdog(ehci);
}
diff --git a/drivers/usb/host/ehci-sh.c b/drivers/usb/host/ehci-sh.c
index 0520e762801d..d31d9506e41a 100644
--- a/drivers/usb/host/ehci-sh.c
+++ b/drivers/usb/host/ehci-sh.c
@@ -82,8 +82,8 @@ static int ehci_hcd_sh_probe(struct platform_device *pdev)
return -ENODEV;
irq = platform_get_irq(pdev, 0);
- if (irq <= 0) {
- ret = -ENODEV;
+ if (irq < 0) {
+ ret = irq;
goto fail_create_hcd;
}
@@ -95,8 +95,7 @@ static int ehci_hcd_sh_probe(struct platform_device *pdev)
goto fail_create_hcd;
}
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+ hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(hcd->regs)) {
ret = PTR_ERR(hcd->regs);
goto fail_request_resource;
diff --git a/drivers/usb/host/ehci-spear.c b/drivers/usb/host/ehci-spear.c
index 1407703649be..d0e94e4c9fe2 100644
--- a/drivers/usb/host/ehci-spear.c
+++ b/drivers/usb/host/ehci-spear.c
@@ -91,8 +91,7 @@ static int spear_ehci_hcd_drv_probe(struct platform_device *pdev)
goto fail;
}
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+ hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(hcd->regs)) {
retval = PTR_ERR(hcd->regs);
goto err_put_hcd;
diff --git a/drivers/usb/host/ehci-st.c b/drivers/usb/host/ehci-st.c
index ee0976b815b4..2dbb0d86daaa 100644
--- a/drivers/usb/host/ehci-st.c
+++ b/drivers/usb/host/ehci-st.c
@@ -158,11 +158,6 @@ static int st_ehci_platform_probe(struct platform_device *dev)
irq = platform_get_irq(dev, 0);
if (irq < 0)
return irq;
- res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0);
- if (!res_mem) {
- dev_err(&dev->dev, "no memory resource provided");
- return -ENXIO;
- }
hcd = usb_create_hcd(&ehci_platform_hc_driver, &dev->dev,
dev_name(&dev->dev));
@@ -222,14 +217,13 @@ static int st_ehci_platform_probe(struct platform_device *dev)
goto err_put_clks;
}
- hcd->rsrc_start = res_mem->start;
- hcd->rsrc_len = resource_size(res_mem);
-
- hcd->regs = devm_ioremap_resource(&dev->dev, res_mem);
+ hcd->regs = devm_platform_get_and_ioremap_resource(dev, 0, &res_mem);
if (IS_ERR(hcd->regs)) {
err = PTR_ERR(hcd->regs);
goto err_put_clks;
}
+ hcd->rsrc_start = res_mem->start;
+ hcd->rsrc_len = resource_size(res_mem);
err = usb_add_hcd(hcd, irq, IRQF_SHARED);
if (err)
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index c5c7f8782549..1441e3400796 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -207,6 +207,7 @@ struct ehci_hcd { /* one per controller */
unsigned has_fsl_port_bug:1; /* FreeScale */
unsigned has_fsl_hs_errata:1; /* Freescale HS quirk */
unsigned has_fsl_susp_errata:1; /* NXP SUSP quirk */
+ unsigned has_ci_pec_bug:1; /* ChipIdea PEC bug */
unsigned big_endian_mmio:1;
unsigned big_endian_desc:1;
unsigned big_endian_capbase:1;
@@ -708,6 +709,15 @@ ehci_port_speed(struct ehci_hcd *ehci, unsigned int portsc)
#define ehci_has_fsl_susp_errata(e) ((e)->has_fsl_susp_errata)
/*
+ * Some Freescale/NXP processors using ChipIdea IP have a bug in which
+ * disabling the port (PE is cleared) does not cause PEC to be asserted
+ * when frame babble is detected.
+ */
+#define ehci_has_ci_pec_bug(e, portsc) \
+ ((e)->has_ci_pec_bug && ((e)->command & CMD_PSE) \
+ && !(portsc & PORT_PEC) && !(portsc & PORT_PE))
+
+/*
* While most USB host controllers implement their registers in
* little-endian format, a minority (celleb companion chip) implement
* them in big endian format.
diff --git a/drivers/usb/host/fhci-hcd.c b/drivers/usb/host/fhci-hcd.c
index 66a045e01dad..9a1b5224f239 100644
--- a/drivers/usb/host/fhci-hcd.c
+++ b/drivers/usb/host/fhci-hcd.c
@@ -22,9 +22,10 @@
#include <linux/io.h>
#include <linux/usb.h>
#include <linux/usb/hcd.h>
+#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
-#include <linux/of_platform.h>
+#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/gpio/consumer.h>
#include <soc/fsl/qe/qe.h>
diff --git a/drivers/usb/host/fsl-mph-dr-of.c b/drivers/usb/host/fsl-mph-dr-of.c
index a9877f2569f4..8508d37a2aff 100644
--- a/drivers/usb/host/fsl-mph-dr-of.c
+++ b/drivers/usb/host/fsl-mph-dr-of.c
@@ -10,7 +10,8 @@
#include <linux/fsl_devices.h>
#include <linux/err.h>
#include <linux/io.h>
-#include <linux/of_platform.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/clk.h>
#include <linux/module.h>
#include <linux/dma-mapping.h>
diff --git a/drivers/usb/host/isp1362-hcd.c b/drivers/usb/host/isp1362-hcd.c
index 606f0a64f3b7..a52c3d858f3e 100644
--- a/drivers/usb/host/isp1362-hcd.c
+++ b/drivers/usb/host/isp1362-hcd.c
@@ -2651,8 +2651,7 @@ static int isp1362_probe(struct platform_device *pdev)
if (IS_ERR(addr_reg))
return PTR_ERR(addr_reg);
- data = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- data_reg = devm_ioremap_resource(&pdev->dev, data);
+ data_reg = devm_platform_get_and_ioremap_resource(pdev, 0, &data);
if (IS_ERR(data_reg))
return PTR_ERR(data_reg);
diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c
index b805c4b52ac3..f691cd98a574 100644
--- a/drivers/usb/host/ohci-at91.c
+++ b/drivers/usb/host/ohci-at91.c
@@ -17,13 +17,13 @@
#include <linux/clk.h>
#include <linux/dma-mapping.h>
#include <linux/gpio/consumer.h>
-#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/platform_data/atmel.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mfd/syscon.h>
+#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/usb.h>
#include <linux/usb/hcd.h>
@@ -190,18 +190,15 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver,
int irq;
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_dbg(dev, "hcd probe: missing irq resource\n");
+ if (irq < 0)
return irq;
- }
hcd = usb_create_hcd(driver, dev, "at91");
if (!hcd)
return -ENOMEM;
ohci_at91 = hcd_to_ohci_at91_priv(hcd);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- hcd->regs = devm_ioremap_resource(dev, res);
+ hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(hcd->regs)) {
retval = PTR_ERR(hcd->regs);
goto err;
diff --git a/drivers/usb/host/ohci-da8xx.c b/drivers/usb/host/ohci-da8xx.c
index e4191a868944..d9adae53466b 100644
--- a/drivers/usb/host/ohci-da8xx.c
+++ b/drivers/usb/host/ohci-da8xx.c
@@ -15,6 +15,7 @@
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/phy/phy.h>
#include <linux/platform_data/usb-davinci.h>
@@ -435,8 +436,7 @@ static int ohci_da8xx_probe(struct platform_device *pdev)
goto err;
}
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- hcd->regs = devm_ioremap_resource(dev, mem);
+ hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &mem);
if (IS_ERR(hcd->regs)) {
error = PTR_ERR(hcd->regs);
goto err;
diff --git a/drivers/usb/host/ohci-exynos.c b/drivers/usb/host/ohci-exynos.c
index ab31c459b32d..20e26a474591 100644
--- a/drivers/usb/host/ohci-exynos.c
+++ b/drivers/usb/host/ohci-exynos.c
@@ -149,8 +149,7 @@ static int exynos_ohci_probe(struct platform_device *pdev)
if (err)
goto fail_clk;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+ hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(hcd->regs)) {
err = PTR_ERR(hcd->regs);
goto fail_io;
diff --git a/drivers/usb/host/ohci-nxp.c b/drivers/usb/host/ohci-nxp.c
index c04b2af5c766..8264c454f6bd 100644
--- a/drivers/usb/host/ohci-nxp.c
+++ b/drivers/usb/host/ohci-nxp.c
@@ -202,8 +202,7 @@ static int ohci_hcd_nxp_probe(struct platform_device *pdev)
goto fail_hcd;
}
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+ hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(hcd->regs)) {
ret = PTR_ERR(hcd->regs);
goto fail_resource;
diff --git a/drivers/usb/host/ohci-platform.c b/drivers/usb/host/ohci-platform.c
index 45a2c981319e..4a75507325dd 100644
--- a/drivers/usb/host/ohci-platform.c
+++ b/drivers/usb/host/ohci-platform.c
@@ -200,8 +200,7 @@ static int ohci_platform_probe(struct platform_device *dev)
goto err_reset;
}
- res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0);
- hcd->regs = devm_ioremap_resource(&dev->dev, res_mem);
+ hcd->regs = devm_platform_get_and_ioremap_resource(dev, 0, &res_mem);
if (IS_ERR(hcd->regs)) {
err = PTR_ERR(hcd->regs);
goto err_power;
diff --git a/drivers/usb/host/ohci-ppc-of.c b/drivers/usb/host/ohci-ppc-of.c
index 35a7ad7e2569..f64bfe5f4d4d 100644
--- a/drivers/usb/host/ohci-ppc-of.c
+++ b/drivers/usb/host/ohci-ppc-of.c
@@ -15,9 +15,10 @@
*/
#include <linux/signal.h>
+#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
-#include <linux/of_platform.h>
+#include <linux/platform_device.h>
static int
ohci_ppc_of_start(struct usb_hcd *hcd)
diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c
index 7410442f720f..357d9aee38a3 100644
--- a/drivers/usb/host/ohci-pxa27x.c
+++ b/drivers/usb/host/ohci-pxa27x.c
@@ -435,8 +435,7 @@ static int ohci_hcd_pxa27x_probe(struct platform_device *pdev)
if (!hcd)
return -ENOMEM;
- r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- hcd->regs = devm_ioremap_resource(&pdev->dev, r);
+ hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &r);
if (IS_ERR(hcd->regs)) {
retval = PTR_ERR(hcd->regs);
goto err;
diff --git a/drivers/usb/host/ohci-sm501.c b/drivers/usb/host/ohci-sm501.c
index 0468eeb4fcfd..4b39e9d6f33a 100644
--- a/drivers/usb/host/ohci-sm501.c
+++ b/drivers/usb/host/ohci-sm501.c
@@ -195,8 +195,7 @@ static void ohci_hcd_sm501_drv_remove(struct platform_device *pdev)
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- if (mem)
- release_mem_region(mem->start, resource_size(mem));
+ release_mem_region(mem->start, resource_size(mem));
/* mask interrupts and disable power */
diff --git a/drivers/usb/host/ohci-spear.c b/drivers/usb/host/ohci-spear.c
index f4b2656407dd..993f347c5c28 100644
--- a/drivers/usb/host/ohci-spear.c
+++ b/drivers/usb/host/ohci-spear.c
@@ -68,8 +68,7 @@ static int spear_ohci_hcd_drv_probe(struct platform_device *pdev)
goto fail;
}
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+ hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(hcd->regs)) {
retval = PTR_ERR(hcd->regs);
goto err_put_hcd;
diff --git a/drivers/usb/host/ohci-st.c b/drivers/usb/host/ohci-st.c
index 884e447a8098..214342013f7e 100644
--- a/drivers/usb/host/ohci-st.c
+++ b/drivers/usb/host/ohci-st.c
@@ -139,12 +139,6 @@ static int st_ohci_platform_probe(struct platform_device *dev)
if (irq < 0)
return irq;
- res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0);
- if (!res_mem) {
- dev_err(&dev->dev, "no memory resource provided");
- return -ENXIO;
- }
-
hcd = usb_create_hcd(&ohci_platform_hc_driver, &dev->dev,
dev_name(&dev->dev));
if (!hcd)
@@ -199,14 +193,14 @@ static int st_ohci_platform_probe(struct platform_device *dev)
goto err_power;
}
- hcd->rsrc_start = res_mem->start;
- hcd->rsrc_len = resource_size(res_mem);
-
- hcd->regs = devm_ioremap_resource(&dev->dev, res_mem);
+ hcd->regs = devm_platform_get_and_ioremap_resource(dev, 0, &res_mem);
if (IS_ERR(hcd->regs)) {
err = PTR_ERR(hcd->regs);
goto err_power;
}
+ hcd->rsrc_start = res_mem->start;
+ hcd->rsrc_len = resource_size(res_mem);
+
err = usb_add_hcd(hcd, irq, IRQF_SHARED);
if (err)
goto err_power;
diff --git a/drivers/usb/host/oxu210hp-hcd.c b/drivers/usb/host/oxu210hp-hcd.c
index 50c1ccabb0f5..d467472f9d3c 100644
--- a/drivers/usb/host/oxu210hp-hcd.c
+++ b/drivers/usb/host/oxu210hp-hcd.c
@@ -4230,8 +4230,7 @@ static int oxu_drv_probe(struct platform_device *pdev)
return irq;
dev_dbg(&pdev->dev, "IRQ resource %d\n", irq);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(&pdev->dev, res);
+ base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(base)) {
ret = PTR_ERR(base);
goto error;
diff --git a/drivers/usb/host/uhci-platform.c b/drivers/usb/host/uhci-platform.c
index 71ca532fc086..3dec5dd3a0d5 100644
--- a/drivers/usb/host/uhci-platform.c
+++ b/drivers/usb/host/uhci-platform.c
@@ -91,8 +91,7 @@ static int uhci_hcd_platform_probe(struct platform_device *pdev)
uhci = hcd_to_uhci(hcd);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+ hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(hcd->regs)) {
ret = PTR_ERR(hcd->regs);
goto err_rmr;
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index 19a402123de0..8714ab5bf04d 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -1108,9 +1108,6 @@ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *ud
slot_ctx->dev_info |= cpu_to_le32(SLOT_SPEED_LS);
max_packets = MAX_PACKET(8);
break;
- case USB_SPEED_WIRELESS:
- xhci_dbg(xhci, "FIXME xHCI doesn't support wireless speeds\n");
- return -EINVAL;
default:
/* Speed was set earlier, this shouldn't happen. */
return -EINVAL;
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index b26ea7cb4357..28218c8f1837 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -13,7 +13,6 @@
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/usb/phy.h>
#include <linux/slab.h>
diff --git a/drivers/usb/host/xhci-rcar.c b/drivers/usb/host/xhci-rcar.c
index bf5261fed32c..ab9c5969e462 100644
--- a/drivers/usb/host/xhci-rcar.c
+++ b/drivers/usb/host/xhci-rcar.c
@@ -10,7 +10,6 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/usb/phy.h>
#include "xhci.h"
diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c
index 4693d83351c6..6246d5ad1468 100644
--- a/drivers/usb/host/xhci-tegra.c
+++ b/drivers/usb/host/xhci-tegra.c
@@ -14,7 +14,7 @@
#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/phy/phy.h>
#include <linux/phy/tegra/xusb.h>
@@ -1912,6 +1912,15 @@ put_padctl:
return err;
}
+static void tegra_xusb_disable(struct tegra_xusb *tegra)
+{
+ tegra_xusb_powergate_partitions(tegra);
+ tegra_xusb_powerdomain_remove(tegra->dev, tegra);
+ tegra_xusb_phy_disable(tegra);
+ tegra_xusb_clk_disable(tegra);
+ regulator_bulk_disable(tegra->soc->num_supplies, tegra->supplies);
+}
+
static void tegra_xusb_remove(struct platform_device *pdev)
{
struct tegra_xusb *tegra = platform_get_drvdata(pdev);
@@ -1934,14 +1943,18 @@ static void tegra_xusb_remove(struct platform_device *pdev)
pm_runtime_put(&pdev->dev);
- tegra_xusb_powergate_partitions(tegra);
+ tegra_xusb_disable(tegra);
+ tegra_xusb_padctl_put(tegra->padctl);
+}
- tegra_xusb_powerdomain_remove(&pdev->dev, tegra);
+static void tegra_xusb_shutdown(struct platform_device *pdev)
+{
+ struct tegra_xusb *tegra = platform_get_drvdata(pdev);
- tegra_xusb_phy_disable(tegra);
- tegra_xusb_clk_disable(tegra);
- regulator_bulk_disable(tegra->soc->num_supplies, tegra->supplies);
- tegra_xusb_padctl_put(tegra->padctl);
+ pm_runtime_get_sync(&pdev->dev);
+ disable_irq(tegra->xhci_irq);
+ xhci_shutdown(tegra->hcd);
+ tegra_xusb_disable(tegra);
}
static bool xhci_hub_ports_suspended(struct xhci_hub *hub)
@@ -2652,6 +2665,7 @@ MODULE_DEVICE_TABLE(of, tegra_xusb_of_match);
static struct platform_driver tegra_xusb_driver = {
.probe = tegra_xusb_probe,
.remove_new = tegra_xusb_remove,
+ .shutdown = tegra_xusb_shutdown,
.driver = {
.name = "tegra-xusb",
.pm = &tegra_xusb_pm_ops,
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index fae994f679d4..e1b1b64a0723 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -2194,7 +2194,6 @@ static unsigned int xhci_get_block_size(struct usb_device *udev)
case USB_SPEED_SUPER_PLUS:
return SS_BLOCK;
case USB_SPEED_UNKNOWN:
- case USB_SPEED_WIRELESS:
default:
/* Should never happen */
return 1;
@@ -2555,10 +2554,7 @@ static void xhci_drop_ep_from_interval_table(struct xhci_hcd *xhci,
case USB_SPEED_HIGH:
interval_bw->overhead[HS_OVERHEAD_TYPE] -= 1;
break;
- case USB_SPEED_SUPER:
- case USB_SPEED_SUPER_PLUS:
- case USB_SPEED_UNKNOWN:
- case USB_SPEED_WIRELESS:
+ default:
/* Should never happen because only LS/FS/HS endpoints will get
* added to the endpoint list.
*/
@@ -2615,10 +2611,7 @@ static void xhci_add_ep_to_interval_table(struct xhci_hcd *xhci,
case USB_SPEED_HIGH:
interval_bw->overhead[HS_OVERHEAD_TYPE] += 1;
break;
- case USB_SPEED_SUPER:
- case USB_SPEED_SUPER_PLUS:
- case USB_SPEED_UNKNOWN:
- case USB_SPEED_WIRELESS:
+ default:
/* Should never happen because only LS/FS/HS endpoints will get
* added to the endpoint list.
*/
diff --git a/drivers/usb/misc/cypress_cy7c63.c b/drivers/usb/misc/cypress_cy7c63.c
index 14faec51d7a5..cecd7693b741 100644
--- a/drivers/usb/misc/cypress_cy7c63.c
+++ b/drivers/usb/misc/cypress_cy7c63.c
@@ -203,7 +203,7 @@ ATTRIBUTE_GROUPS(cypress);
static int cypress_probe(struct usb_interface *interface,
const struct usb_device_id *id)
{
- struct cypress *dev = NULL;
+ struct cypress *dev;
int retval = -ENOMEM;
/* allocate memory for our device state and initialize it */
diff --git a/drivers/usb/misc/cytherm.c b/drivers/usb/misc/cytherm.c
index 3e3802aaefa3..875016dd073c 100644
--- a/drivers/usb/misc/cytherm.c
+++ b/drivers/usb/misc/cytherm.c
@@ -304,20 +304,20 @@ static int cytherm_probe(struct usb_interface *interface,
const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(interface);
- struct usb_cytherm *dev = NULL;
+ struct usb_cytherm *dev;
int retval = -ENOMEM;
- dev = kzalloc (sizeof(struct usb_cytherm), GFP_KERNEL);
+ dev = kzalloc(sizeof(struct usb_cytherm), GFP_KERNEL);
if (!dev)
goto error_mem;
dev->udev = usb_get_dev(udev);
- usb_set_intfdata (interface, dev);
+ usb_set_intfdata(interface, dev);
dev->brightness = 0xFF;
- dev_info (&interface->dev,
+ dev_info(&interface->dev,
"Cypress thermometer device now attached\n");
return 0;
@@ -329,10 +329,10 @@ static void cytherm_disconnect(struct usb_interface *interface)
{
struct usb_cytherm *dev;
- dev = usb_get_intfdata (interface);
+ dev = usb_get_intfdata(interface);
/* first remove the files, then NULL the pointer */
- usb_set_intfdata (interface, NULL);
+ usb_set_intfdata(interface, NULL);
usb_put_dev(dev->udev);
diff --git a/drivers/usb/misc/onboard_usb_hub.c b/drivers/usb/misc/onboard_usb_hub.c
index 83f14ca1d38e..3da1a4659c5f 100644
--- a/drivers/usb/misc/onboard_usb_hub.c
+++ b/drivers/usb/misc/onboard_usb_hub.c
@@ -27,6 +27,17 @@
#include "onboard_usb_hub.h"
+/*
+ * Use generic names, as the actual names might differ between hubs. If a new
+ * hub requires more than the currently supported supplies, add a new one here.
+ */
+static const char * const supply_names[] = {
+ "vdd",
+ "vdd2",
+};
+
+#define MAX_SUPPLIES ARRAY_SIZE(supply_names)
+
static void onboard_hub_attach_usb_driver(struct work_struct *work);
static struct usb_device_driver onboard_hub_usbdev_driver;
@@ -40,7 +51,7 @@ struct usbdev_node {
};
struct onboard_hub {
- struct regulator *vdd;
+ struct regulator_bulk_data supplies[MAX_SUPPLIES];
struct device *dev;
const struct onboard_hub_pdata *pdata;
struct gpio_desc *reset_gpio;
@@ -55,9 +66,9 @@ static int onboard_hub_power_on(struct onboard_hub *hub)
{
int err;
- err = regulator_enable(hub->vdd);
+ err = regulator_bulk_enable(hub->pdata->num_supplies, hub->supplies);
if (err) {
- dev_err(hub->dev, "failed to enable regulator: %d\n", err);
+ dev_err(hub->dev, "failed to enable supplies: %d\n", err);
return err;
}
@@ -75,9 +86,9 @@ static int onboard_hub_power_off(struct onboard_hub *hub)
gpiod_set_value_cansleep(hub->reset_gpio, 1);
- err = regulator_disable(hub->vdd);
+ err = regulator_bulk_disable(hub->pdata->num_supplies, hub->supplies);
if (err) {
- dev_err(hub->dev, "failed to disable regulator: %d\n", err);
+ dev_err(hub->dev, "failed to disable supplies: %d\n", err);
return err;
}
@@ -232,6 +243,7 @@ static int onboard_hub_probe(struct platform_device *pdev)
const struct of_device_id *of_id;
struct device *dev = &pdev->dev;
struct onboard_hub *hub;
+ unsigned int i;
int err;
hub = devm_kzalloc(dev, sizeof(*hub), GFP_KERNEL);
@@ -246,9 +258,18 @@ static int onboard_hub_probe(struct platform_device *pdev)
if (!hub->pdata)
return -EINVAL;
- hub->vdd = devm_regulator_get(dev, "vdd");
- if (IS_ERR(hub->vdd))
- return PTR_ERR(hub->vdd);
+ if (hub->pdata->num_supplies > MAX_SUPPLIES)
+ return dev_err_probe(dev, -EINVAL, "max %zu supplies supported!\n",
+ MAX_SUPPLIES);
+
+ for (i = 0; i < hub->pdata->num_supplies; i++)
+ hub->supplies[i].supply = supply_names[i];
+
+ err = devm_regulator_bulk_get(dev, hub->pdata->num_supplies, hub->supplies);
+ if (err) {
+ dev_err(dev, "Failed to get regulator supplies: %d\n", err);
+ return err;
+ }
hub->reset_gpio = devm_gpiod_get_optional(dev, "reset",
GPIOD_OUT_HIGH);
@@ -329,6 +350,7 @@ static struct platform_driver onboard_hub_driver = {
/************************** USB driver **************************/
+#define VENDOR_ID_CYPRESS 0x04b4
#define VENDOR_ID_GENESYS 0x05e3
#define VENDOR_ID_MICROCHIP 0x0424
#define VENDOR_ID_REALTEK 0x0bda
@@ -407,8 +429,11 @@ static void onboard_hub_usbdev_disconnect(struct usb_device *udev)
}
static const struct usb_device_id onboard_hub_id_table[] = {
+ { USB_DEVICE(VENDOR_ID_CYPRESS, 0x6504) }, /* CYUSB33{0,1,2}x/CYUSB230x 3.0 */
+ { USB_DEVICE(VENDOR_ID_CYPRESS, 0x6506) }, /* CYUSB33{0,1,2}x/CYUSB230x 2.0 */
{ USB_DEVICE(VENDOR_ID_GENESYS, 0x0608) }, /* Genesys Logic GL850G USB 2.0 */
{ USB_DEVICE(VENDOR_ID_GENESYS, 0x0610) }, /* Genesys Logic GL852G USB 2.0 */
+ { USB_DEVICE(VENDOR_ID_GENESYS, 0x0620) }, /* Genesys Logic GL3523 USB 3.1 */
{ USB_DEVICE(VENDOR_ID_MICROCHIP, 0x2514) }, /* USB2514B USB 2.0 */
{ USB_DEVICE(VENDOR_ID_MICROCHIP, 0x2517) }, /* USB2517 USB 2.0 */
{ USB_DEVICE(VENDOR_ID_REALTEK, 0x0411) }, /* RTS5411 USB 3.1 */
diff --git a/drivers/usb/misc/onboard_usb_hub.h b/drivers/usb/misc/onboard_usb_hub.h
index aca5f50eb0da..4026ba64c592 100644
--- a/drivers/usb/misc/onboard_usb_hub.h
+++ b/drivers/usb/misc/onboard_usb_hub.h
@@ -8,30 +8,42 @@
struct onboard_hub_pdata {
unsigned long reset_us; /* reset pulse width in us */
+ unsigned int num_supplies; /* number of supplies */
};
static const struct onboard_hub_pdata microchip_usb424_data = {
.reset_us = 1,
+ .num_supplies = 1,
};
static const struct onboard_hub_pdata realtek_rts5411_data = {
.reset_us = 0,
+ .num_supplies = 1,
};
static const struct onboard_hub_pdata ti_tusb8041_data = {
.reset_us = 3000,
+ .num_supplies = 1,
+};
+
+static const struct onboard_hub_pdata cypress_hx3_data = {
+ .reset_us = 10000,
+ .num_supplies = 2,
};
static const struct onboard_hub_pdata genesys_gl850g_data = {
.reset_us = 3,
+ .num_supplies = 1,
};
static const struct onboard_hub_pdata genesys_gl852g_data = {
.reset_us = 50,
+ .num_supplies = 1,
};
static const struct onboard_hub_pdata vialab_vl817_data = {
.reset_us = 10,
+ .num_supplies = 1,
};
static const struct of_device_id onboard_hub_match[] = {
@@ -39,8 +51,11 @@ static const struct of_device_id onboard_hub_match[] = {
{ .compatible = "usb424,2517", .data = &microchip_usb424_data, },
{ .compatible = "usb451,8140", .data = &ti_tusb8041_data, },
{ .compatible = "usb451,8142", .data = &ti_tusb8041_data, },
+ { .compatible = "usb4b4,6504", .data = &cypress_hx3_data, },
+ { .compatible = "usb4b4,6506", .data = &cypress_hx3_data, },
{ .compatible = "usb5e3,608", .data = &genesys_gl850g_data, },
{ .compatible = "usb5e3,610", .data = &genesys_gl852g_data, },
+ { .compatible = "usb5e3,620", .data = &genesys_gl852g_data, },
{ .compatible = "usbbda,411", .data = &realtek_rts5411_data, },
{ .compatible = "usbbda,5411", .data = &realtek_rts5411_data, },
{ .compatible = "usbbda,414", .data = &realtek_rts5411_data, },
diff --git a/drivers/usb/misc/usb251xb.c b/drivers/usb/misc/usb251xb.c
index e4edb486b69e..7da404f55a6d 100644
--- a/drivers/usb/misc/usb251xb.c
+++ b/drivers/usb/misc/usb251xb.c
@@ -16,7 +16,7 @@
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/nls.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
diff --git a/drivers/usb/misc/usb_u132.h b/drivers/usb/misc/usb_u132.h
deleted file mode 100644
index 1584efbbd704..000000000000
--- a/drivers/usb/misc/usb_u132.h
+++ /dev/null
@@ -1,97 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
-* Common Header File for the Elan Digital Systems U132 adapter
-* this file should be included by both the "ftdi-u132" and
-* the "u132-hcd" modules.
-*
-* Copyright(C) 2006 Elan Digital Systems Limited
-*(http://www.elandigitalsystems.com)
-*
-* Author and Maintainer - Tony Olech - Elan Digital Systems
-*(tony.olech@elandigitalsystems.com)
-*
-* The driver was written by Tony Olech(tony.olech@elandigitalsystems.com)
-* based on various USB client drivers in the 2.6.15 linux kernel
-* with constant reference to the 3rd Edition of Linux Device Drivers
-* published by O'Reilly
-*
-* The U132 adapter is a USB to CardBus adapter specifically designed
-* for PC cards that contain an OHCI host controller. Typical PC cards
-* are the Orange Mobile 3G Option GlobeTrotter Fusion card.
-*
-* The U132 adapter will *NOT *work with PC cards that do not contain
-* an OHCI controller. A simple way to test whether a PC card has an
-* OHCI controller as an interface is to insert the PC card directly
-* into a laptop(or desktop) with a CardBus slot and if "lspci" shows
-* a new USB controller and "lsusb -v" shows a new OHCI Host Controller
-* then there is a good chance that the U132 adapter will support the
-* PC card.(you also need the specific client driver for the PC card)
-*
-* Please inform the Author and Maintainer about any PC cards that
-* contain OHCI Host Controller and work when directly connected to
-* an embedded CardBus slot but do not work when they are connected
-* via an ELAN U132 adapter.
-*
-* The driver consists of two modules, the "ftdi-u132" module is
-* a USB client driver that interfaces to the FTDI chip within
-* the U132 adapter manufactured by Elan Digital Systems, and the
-* "u132-hcd" module is a USB host controller driver that talks
-* to the OHCI controller within CardBus card that are inserted
-* in the U132 adapter.
-*
-* The "ftdi-u132" module should be loaded automatically by the
-* hot plug system when the U132 adapter is plugged in. The module
-* initialises the adapter which mostly consists of synchronising
-* the FTDI chip, before continuously polling the adapter to detect
-* PC card insertions. As soon as a PC card containing a recognised
-* OHCI controller is seen the "ftdi-u132" module explicitly requests
-* the kernel to load the "u132-hcd" module.
-*
-* The "ftdi-u132" module provides the interface to the inserted
-* PC card and the "u132-hcd" module uses the API to send and receive
-* data. The API features call-backs, so that part of the "u132-hcd"
-* module code will run in the context of one of the kernel threads
-* of the "ftdi-u132" module.
-*
-*/
-int ftdi_elan_switch_on_diagnostics(int number);
-void ftdi_elan_gone_away(struct platform_device *pdev);
-void start_usb_lock_device_tracing(void);
-struct u132_platform_data {
- u16 vendor;
- u16 device;
- u8 potpg;
- void (*port_power) (struct device *dev, int is_on);
- void (*reset) (struct device *dev);
-};
-int usb_ftdi_elan_edset_single(struct platform_device *pdev, u8 ed_number,
- void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
- void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
- int toggle_bits, int error_count, int condition_code, int repeat_number,
- int halted, int skipped, int actual, int non_null));
-int usb_ftdi_elan_edset_output(struct platform_device *pdev, u8 ed_number,
- void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
- void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
- int toggle_bits, int error_count, int condition_code, int repeat_number,
- int halted, int skipped, int actual, int non_null));
-int usb_ftdi_elan_edset_empty(struct platform_device *pdev, u8 ed_number,
- void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
- void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
- int toggle_bits, int error_count, int condition_code, int repeat_number,
- int halted, int skipped, int actual, int non_null));
-int usb_ftdi_elan_edset_input(struct platform_device *pdev, u8 ed_number,
- void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
- void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
- int toggle_bits, int error_count, int condition_code, int repeat_number,
- int halted, int skipped, int actual, int non_null));
-int usb_ftdi_elan_edset_setup(struct platform_device *pdev, u8 ed_number,
- void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
- void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
- int toggle_bits, int error_count, int condition_code, int repeat_number,
- int halted, int skipped, int actual, int non_null));
-int usb_ftdi_elan_edset_flush(struct platform_device *pdev, u8 ed_number,
- void *endp);
-int usb_ftdi_elan_read_pcimem(struct platform_device *pdev, int mem_offset,
- u8 width, u32 *data);
-int usb_ftdi_elan_write_pcimem(struct platform_device *pdev, int mem_offset,
- u8 width, u32 data);
diff --git a/drivers/usb/misc/usbsevseg.c b/drivers/usb/misc/usbsevseg.c
index c3114d9bd128..546deff754ba 100644
--- a/drivers/usb/misc/usbsevseg.c
+++ b/drivers/usb/misc/usbsevseg.c
@@ -305,7 +305,7 @@ static int sevseg_probe(struct usb_interface *interface,
const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(interface);
- struct usb_sevsegdev *mydev = NULL;
+ struct usb_sevsegdev *mydev;
int rc = -ENOMEM;
mydev = kzalloc(sizeof(struct usb_sevsegdev), GFP_KERNEL);
diff --git a/drivers/usb/mtu3/mtu3.h b/drivers/usb/mtu3/mtu3.h
index b4a7662dded5..c11840b9a6f1 100644
--- a/drivers/usb/mtu3/mtu3.h
+++ b/drivers/usb/mtu3/mtu3.h
@@ -16,6 +16,7 @@
#include <linux/extcon.h>
#include <linux/interrupt.h>
#include <linux/list.h>
+#include <linux/of.h>
#include <linux/phy/phy.h>
#include <linux/regulator/consumer.h>
#include <linux/usb.h>
diff --git a/drivers/usb/mtu3/mtu3_host.c b/drivers/usb/mtu3/mtu3_host.c
index 177d2caf887c..9f2be22af844 100644
--- a/drivers/usb/mtu3/mtu3_host.c
+++ b/drivers/usb/mtu3/mtu3_host.c
@@ -11,6 +11,7 @@
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/mfd/syscon.h>
+#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/regmap.h>
diff --git a/drivers/usb/musb/cppi_dma.h b/drivers/usb/musb/cppi_dma.h
index 16dd1ed44bb5..3606be897cb2 100644
--- a/drivers/usb/musb/cppi_dma.h
+++ b/drivers/usb/musb/cppi_dma.h
@@ -121,9 +121,6 @@ struct cppi {
struct list_head tx_complete;
};
-/* CPPI IRQ handler */
-extern irqreturn_t cppi_interrupt(int, void *);
-
struct cppi41_dma_channel {
struct dma_channel channel;
struct cppi41_dma_controller *controller;
diff --git a/drivers/usb/musb/jz4740.c b/drivers/usb/musb/jz4740.c
index 5aabdd7e2511..b38df9226278 100644
--- a/drivers/usb/musb/jz4740.c
+++ b/drivers/usb/musb/jz4740.c
@@ -10,7 +10,7 @@
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/usb/role.h>
diff --git a/drivers/usb/musb/mediatek.c b/drivers/usb/musb/mediatek.c
index 598ee5c0bf34..0a35aab3ab81 100644
--- a/drivers/usb/musb/mediatek.c
+++ b/drivers/usb/musb/mediatek.c
@@ -10,6 +10,7 @@
#include <linux/clk.h>
#include <linux/dma-mapping.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/usb/role.h>
diff --git a/drivers/usb/musb/mpfs.c b/drivers/usb/musb/mpfs.c
index 24b98716f7fc..f0f56df38835 100644
--- a/drivers/usb/musb/mpfs.c
+++ b/drivers/usb/musb/mpfs.c
@@ -13,6 +13,7 @@
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/usb/usb_phy_generic.h>
#include "musb_core.h"
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
index ecbd3784bec3..b24adb5b399f 100644
--- a/drivers/usb/musb/musb_core.c
+++ b/drivers/usb/musb/musb_core.c
@@ -2610,8 +2610,8 @@ static int musb_probe(struct platform_device *pdev)
int irq = platform_get_irq_byname(pdev, "mc");
void __iomem *base;
- if (irq <= 0)
- return -ENODEV;
+ if (irq < 0)
+ return irq;
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
diff --git a/drivers/usb/musb/musb_dma.h b/drivers/usb/musb/musb_dma.h
index e2445ca3356d..0cd7fc468de8 100644
--- a/drivers/usb/musb/musb_dma.h
+++ b/drivers/usb/musb/musb_dma.h
@@ -199,10 +199,6 @@ tusb_dma_controller_create(struct musb *musb, void __iomem *base);
extern void tusb_dma_controller_destroy(struct dma_controller *c);
extern struct dma_controller *
-cppi_dma_controller_create(struct musb *musb, void __iomem *base);
-extern void cppi_dma_controller_destroy(struct dma_controller *c);
-
-extern struct dma_controller *
cppi41_dma_controller_create(struct musb *musb, void __iomem *base);
extern void cppi41_dma_controller_destroy(struct dma_controller *c);
diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c
index 9119b1d51370..98b42dc04dee 100644
--- a/drivers/usb/musb/musb_dsps.c
+++ b/drivers/usb/musb/musb_dsps.c
@@ -26,9 +26,7 @@
#include <linux/sizes.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/of_address.h>
-#include <linux/of_irq.h>
#include <linux/usb/of.h>
#include <linux/debugfs.h>
diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c
index 31c44325e828..051c6da7cf6d 100644
--- a/drivers/usb/musb/musb_gadget.c
+++ b/drivers/usb/musb/musb_gadget.c
@@ -1130,7 +1130,7 @@ static int musb_gadget_disable(struct usb_ep *ep)
struct usb_request *musb_alloc_request(struct usb_ep *ep, gfp_t gfp_flags)
{
struct musb_ep *musb_ep = to_musb_ep(ep);
- struct musb_request *request = NULL;
+ struct musb_request *request;
request = kzalloc(sizeof *request, gfp_flags);
if (!request)
diff --git a/drivers/usb/musb/sunxi.c b/drivers/usb/musb/sunxi.c
index c5c6c4e09300..d54283fd026b 100644
--- a/drivers/usb/musb/sunxi.c
+++ b/drivers/usb/musb/sunxi.c
@@ -15,7 +15,6 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/phy/phy-sun4i-usb.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c
index cbc707fe570f..461587629bf2 100644
--- a/drivers/usb/musb/tusb6010.c
+++ b/drivers/usb/musb/tusb6010.c
@@ -21,6 +21,7 @@
#include <linux/usb.h>
#include <linux/irq.h>
#include <linux/io.h>
+#include <linux/iopoll.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
@@ -1029,7 +1030,7 @@ static int tusb_musb_start(struct musb *musb)
void __iomem *tbase = musb->ctrl_base;
unsigned long flags;
u32 reg;
- int i;
+ int ret;
/*
* Enable or disable power to TUSB6010. When enabling, turn on 3.3 V and
@@ -1037,17 +1038,13 @@ static int tusb_musb_start(struct musb *musb)
* provide then PGOOD signal to TUSB6010 which will release it from reset.
*/
gpiod_set_value(glue->enable, 1);
- msleep(1);
/* Wait for 100ms until TUSB6010 pulls INT pin down */
- i = 100;
- while (i && gpiod_get_value(glue->intpin)) {
- msleep(1);
- i--;
- }
- if (!i) {
- pr_err("tusb: Powerup respones failed\n");
- return -ENODEV;
+ ret = read_poll_timeout(gpiod_get_value, reg, !reg, 5000, 100000, true,
+ glue->intpin);
+ if (ret) {
+ pr_err("tusb: Powerup response failed\n");
+ return ret;
}
spin_lock_irqsave(&musb->lock, flags);
diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c
index e1a2b2ea098b..acd46b72899e 100644
--- a/drivers/usb/phy/phy-mxs-usb.c
+++ b/drivers/usb/phy/phy-mxs-usb.c
@@ -14,7 +14,7 @@
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/io.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/mfd/syscon.h>
#include <linux/iopoll.h>
@@ -388,19 +388,14 @@ static void __mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool disconnect)
static bool mxs_phy_is_otg_host(struct mxs_phy *mxs_phy)
{
- void __iomem *base = mxs_phy->phy.io_priv;
- u32 phyctrl = readl(base + HW_USBPHY_CTRL);
-
- if (IS_ENABLED(CONFIG_USB_OTG) &&
- !(phyctrl & BM_USBPHY_CTRL_OTG_ID_VALUE))
- return true;
-
- return false;
+ return IS_ENABLED(CONFIG_USB_OTG) &&
+ mxs_phy->phy.last_event == USB_EVENT_ID;
}
static void mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool on)
{
bool vbus_is_on = false;
+ enum usb_phy_events last_event = mxs_phy->phy.last_event;
/* If the SoCs don't need to disconnect line without vbus, quit */
if (!(mxs_phy->data->flags & MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS))
@@ -412,7 +407,8 @@ static void mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool on)
vbus_is_on = mxs_phy_get_vbus_status(mxs_phy);
- if (on && !vbus_is_on && !mxs_phy_is_otg_host(mxs_phy))
+ if (on && ((!vbus_is_on && !mxs_phy_is_otg_host(mxs_phy))
+ || (last_event == USB_EVENT_VBUS)))
__mxs_phy_disconnect_line(mxs_phy, true);
else
__mxs_phy_disconnect_line(mxs_phy, false);
diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c
index 8b2ff3a8882d..4ea47e6f835b 100644
--- a/drivers/usb/phy/phy-tegra-usb.c
+++ b/drivers/usb/phy/phy-tegra-usb.c
@@ -16,7 +16,7 @@
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/of_device.h>
+#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/resource.h>
#include <linux/slab.h>
diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c
index 111b7ee152c4..dd1c17542439 100644
--- a/drivers/usb/renesas_usbhs/common.c
+++ b/drivers/usb/renesas_usbhs/common.c
@@ -11,7 +11,7 @@
#include <linux/gpio/consumer.h>
#include <linux/io.h>
#include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
#include <linux/slab.h>
diff --git a/drivers/usb/renesas_usbhs/rza.c b/drivers/usb/renesas_usbhs/rza.c
index 2d77edefb4b3..97b5217c5a90 100644
--- a/drivers/usb/renesas_usbhs/rza.c
+++ b/drivers/usb/renesas_usbhs/rza.c
@@ -8,7 +8,7 @@
#include <linux/delay.h>
#include <linux/io.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include "common.h"
#include "rza.h"
diff --git a/drivers/usb/renesas_usbhs/rza2.c b/drivers/usb/renesas_usbhs/rza2.c
index 3eed3334a17f..f079817250bb 100644
--- a/drivers/usb/renesas_usbhs/rza2.c
+++ b/drivers/usb/renesas_usbhs/rza2.c
@@ -8,7 +8,6 @@
#include <linux/delay.h>
#include <linux/io.h>
-#include <linux/of_device.h>
#include <linux/phy/phy.h>
#include "common.h"
#include "rza.h"
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index 8ac98e60fff5..7994a4549a6c 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -259,6 +259,7 @@ static void option_instat_callback(struct urb *urb);
#define QUECTEL_PRODUCT_EM05G 0x030a
#define QUECTEL_PRODUCT_EM060K 0x030b
#define QUECTEL_PRODUCT_EM05G_CS 0x030c
+#define QUECTEL_PRODUCT_EM05GV2 0x030e
#define QUECTEL_PRODUCT_EM05CN_SG 0x0310
#define QUECTEL_PRODUCT_EM05G_SG 0x0311
#define QUECTEL_PRODUCT_EM05CN 0x0312
@@ -1188,6 +1189,8 @@ static const struct usb_device_id option_ids[] = {
.driver_info = RSVD(6) | ZLP },
{ USB_DEVICE_INTERFACE_CLASS(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM05G, 0xff),
.driver_info = RSVD(6) | ZLP },
+ { USB_DEVICE_INTERFACE_CLASS(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM05GV2, 0xff),
+ .driver_info = RSVD(4) | ZLP },
{ USB_DEVICE_INTERFACE_CLASS(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM05G_CS, 0xff),
.driver_info = RSVD(6) | ZLP },
{ USB_DEVICE_INTERFACE_CLASS(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM05G_GR, 0xff),
@@ -2232,6 +2235,10 @@ static const struct usb_device_id option_ids[] = {
.driver_info = RSVD(0) | RSVD(1) | RSVD(6) },
{ USB_DEVICE_INTERFACE_CLASS(0x0489, 0xe0db, 0xff), /* Foxconn T99W265 MBIM */
.driver_info = RSVD(3) },
+ { USB_DEVICE_INTERFACE_CLASS(0x0489, 0xe0ee, 0xff), /* Foxconn T99W368 MBIM */
+ .driver_info = RSVD(3) },
+ { USB_DEVICE_INTERFACE_CLASS(0x0489, 0xe0f0, 0xff), /* Foxconn T99W373 MBIM */
+ .driver_info = RSVD(3) },
{ USB_DEVICE(0x1508, 0x1001), /* Fibocom NL668 (IOT version) */
.driver_info = RSVD(4) | RSVD(5) | RSVD(6) },
{ USB_DEVICE(0x1782, 0x4d10) }, /* Fibocom L610 (AT mode) */
diff --git a/drivers/usb/serial/xr_serial.c b/drivers/usb/serial/xr_serial.c
index 4ec7c5892b84..1d9a12628f81 100644
--- a/drivers/usb/serial/xr_serial.c
+++ b/drivers/usb/serial/xr_serial.c
@@ -93,6 +93,7 @@ struct xr_txrx_clk_mask {
#define XR_GPIO_MODE_SEL_DTR_DSR 0x2
#define XR_GPIO_MODE_SEL_RS485 0x3
#define XR_GPIO_MODE_SEL_RS485_ADDR 0x4
+#define XR_GPIO_MODE_RS485_TX_H 0x8
#define XR_GPIO_MODE_TX_TOGGLE 0x100
#define XR_GPIO_MODE_RX_TOGGLE 0x200
@@ -237,6 +238,7 @@ static const struct xr_type xr_types[] = {
struct xr_data {
const struct xr_type *type;
u8 channel; /* zero-based index or interface number */
+ struct serial_rs485 rs485;
};
static int xr_set_reg(struct usb_serial_port *port, u8 channel, u16 reg, u16 val)
@@ -629,6 +631,7 @@ static void xr_set_flow_mode(struct tty_struct *tty,
struct xr_data *data = usb_get_serial_port_data(port);
const struct xr_type *type = data->type;
u16 flow, gpio_mode;
+ bool rs485_enabled;
int ret;
ret = xr_get_reg_uart(port, type->gpio_mode, &gpio_mode);
@@ -645,7 +648,17 @@ static void xr_set_flow_mode(struct tty_struct *tty,
/* Set GPIO mode for controlling the pins manually by default. */
gpio_mode &= ~XR_GPIO_MODE_SEL_MASK;
- if (C_CRTSCTS(tty) && C_BAUD(tty) != B0) {
+ rs485_enabled = !!(data->rs485.flags & SER_RS485_ENABLED);
+ if (rs485_enabled) {
+ dev_dbg(&port->dev, "Enabling RS-485\n");
+ gpio_mode |= XR_GPIO_MODE_SEL_RS485;
+ if (data->rs485.flags & SER_RS485_RTS_ON_SEND)
+ gpio_mode &= ~XR_GPIO_MODE_RS485_TX_H;
+ else
+ gpio_mode |= XR_GPIO_MODE_RS485_TX_H;
+ }
+
+ if (C_CRTSCTS(tty) && C_BAUD(tty) != B0 && !rs485_enabled) {
dev_dbg(&port->dev, "Enabling hardware flow ctrl\n");
gpio_mode |= XR_GPIO_MODE_SEL_RTS_CTS;
flow = XR_UART_FLOW_MODE_HW;
@@ -809,6 +822,79 @@ static void xr_cdc_set_line_coding(struct tty_struct *tty,
kfree(lc);
}
+static void xr_sanitize_serial_rs485(struct serial_rs485 *rs485)
+{
+ if (!(rs485->flags & SER_RS485_ENABLED)) {
+ memset(rs485, 0, sizeof(*rs485));
+ return;
+ }
+
+ /* RTS always toggles after TX */
+ if (rs485->flags & SER_RS485_RTS_ON_SEND)
+ rs485->flags &= ~SER_RS485_RTS_AFTER_SEND;
+ else
+ rs485->flags |= SER_RS485_RTS_AFTER_SEND;
+
+ /* Only the flags are implemented at the moment */
+ rs485->flags &= SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND |
+ SER_RS485_RTS_AFTER_SEND;
+ rs485->delay_rts_before_send = 0;
+ rs485->delay_rts_after_send = 0;
+ memset(rs485->padding, 0, sizeof(rs485->padding));
+}
+
+static int xr_get_rs485_config(struct tty_struct *tty,
+ struct serial_rs485 __user *argp)
+{
+ struct usb_serial_port *port = tty->driver_data;
+ struct xr_data *data = usb_get_serial_port_data(port);
+
+ down_read(&tty->termios_rwsem);
+ if (copy_to_user(argp, &data->rs485, sizeof(data->rs485))) {
+ up_read(&tty->termios_rwsem);
+ return -EFAULT;
+ }
+ up_read(&tty->termios_rwsem);
+
+ return 0;
+}
+
+static int xr_set_rs485_config(struct tty_struct *tty,
+ struct serial_rs485 __user *argp)
+{
+ struct usb_serial_port *port = tty->driver_data;
+ struct xr_data *data = usb_get_serial_port_data(port);
+ struct serial_rs485 rs485;
+
+ if (copy_from_user(&rs485, argp, sizeof(rs485)))
+ return -EFAULT;
+ xr_sanitize_serial_rs485(&rs485);
+
+ down_write(&tty->termios_rwsem);
+ data->rs485 = rs485;
+ xr_set_flow_mode(tty, port, NULL);
+ up_write(&tty->termios_rwsem);
+
+ if (copy_to_user(argp, &rs485, sizeof(rs485)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int xr_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+
+ switch (cmd) {
+ case TIOCGRS485:
+ return xr_get_rs485_config(tty, argp);
+ case TIOCSRS485:
+ return xr_set_rs485_config(tty, argp);
+ }
+
+ return -ENOIOCTLCMD;
+}
+
static void xr_set_termios(struct tty_struct *tty,
struct usb_serial_port *port,
const struct ktermios *old_termios)
@@ -1010,6 +1096,7 @@ static struct usb_serial_driver xr_device = {
.set_termios = xr_set_termios,
.tiocmget = xr_tiocmget,
.tiocmset = xr_tiocmset,
+ .ioctl = xr_ioctl,
.dtr_rts = xr_dtr_rts
};
diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c
index cdf8261e22db..426c88a516e5 100644
--- a/drivers/usb/typec/altmodes/displayport.c
+++ b/drivers/usb/typec/altmodes/displayport.c
@@ -594,7 +594,10 @@ int dp_altmode_probe(struct typec_altmode *alt)
alt->ops = &dp_altmode_ops;
fwnode = dev_fwnode(alt->dev.parent->parent); /* typec_port fwnode */
- dp->connector_fwnode = fwnode_find_reference(fwnode, "displayport", 0);
+ if (fwnode_property_present(fwnode, "displayport"))
+ dp->connector_fwnode = fwnode_find_reference(fwnode, "displayport", 0);
+ else
+ dp->connector_fwnode = fwnode_handle_get(fwnode); /* embedded DP */
if (IS_ERR(dp->connector_fwnode))
dp->connector_fwnode = NULL;
diff --git a/drivers/usb/typec/bus.c b/drivers/usb/typec/bus.c
index fe5b9a2e61f5..e95ec7e382bb 100644
--- a/drivers/usb/typec/bus.c
+++ b/drivers/usb/typec/bus.c
@@ -183,12 +183,20 @@ EXPORT_SYMBOL_GPL(typec_altmode_exit);
*
* Notifies the partner of @adev about Attention command.
*/
-void typec_altmode_attention(struct typec_altmode *adev, u32 vdo)
+int typec_altmode_attention(struct typec_altmode *adev, u32 vdo)
{
- struct typec_altmode *pdev = &to_altmode(adev)->partner->adev;
+ struct altmode *partner = to_altmode(adev)->partner;
+ struct typec_altmode *pdev;
+
+ if (!partner)
+ return -ENODEV;
+
+ pdev = &partner->adev;
if (pdev->ops && pdev->ops->attention)
pdev->ops->attention(pdev, vdo);
+
+ return 0;
}
EXPORT_SYMBOL_GPL(typec_altmode_attention);
diff --git a/drivers/usb/typec/mux/intel_pmc_mux.c b/drivers/usb/typec/mux/intel_pmc_mux.c
index 5e8edf3881c0..60ed1f809130 100644
--- a/drivers/usb/typec/mux/intel_pmc_mux.c
+++ b/drivers/usb/typec/mux/intel_pmc_mux.c
@@ -59,7 +59,7 @@ enum {
};
/* Common Mode Data bits */
-#define PMC_USB_ALTMODE_ACTIVE_CABLE BIT(2)
+#define PMC_USB_ALTMODE_RETIMER_CABLE BIT(2)
#define PMC_USB_ALTMODE_ORI_SHIFT 1
#define PMC_USB_ALTMODE_UFP_SHIFT 3
@@ -71,6 +71,7 @@ enum {
#define PMC_USB_ALTMODE_TBT_TYPE BIT(17)
#define PMC_USB_ALTMODE_CABLE_TYPE BIT(18)
#define PMC_USB_ALTMODE_ACTIVE_LINK BIT(20)
+#define PMC_USB_ALTMODE_ACTIVE_CABLE BIT(22)
#define PMC_USB_ALTMODE_FORCE_LSR BIT(23)
#define PMC_USB_ALTMODE_CABLE_SPD(_s_) (((_s_) & GENMASK(2, 0)) << 25)
#define PMC_USB_ALTMODE_CABLE_USB31 1
@@ -117,6 +118,16 @@ enum {
IOM_PORT_STATUS_DHPD_HPD_STATUS_SHIFT) & \
IOM_PORT_STATUS_DHPD_HPD_STATUS_ASSERT)
+/* IOM port status register */
+#define IOM_PORT_STATUS_REGS(_offset_, _size_) ((_offset_) | (_size_))
+#define IOM_PORT_STATUS_REGS_SZ_MASK BIT(0)
+#define IOM_PORT_STATUS_REGS_SZ_4 0
+#define IOM_PORT_STATUS_REGS_SZ_8 1
+#define IOM_PORT_STATUS_REGS_OFFSET(_d_) \
+ ((_d_) & ~IOM_PORT_STATUS_REGS_SZ_MASK)
+#define IOM_PORT_STATUS_REGS_SIZE(_d_) \
+ (4 << ((_d_) & IOM_PORT_STATUS_REGS_SZ_MASK))
+
struct pmc_usb;
struct pmc_usb_port {
@@ -145,6 +156,7 @@ struct pmc_usb {
struct acpi_device *iom_adev;
void __iomem *iom_base;
u32 iom_port_status_offset;
+ u8 iom_port_status_size;
struct dentry *dentry;
};
@@ -160,7 +172,7 @@ static void update_port_status(struct pmc_usb_port *port)
port->iom_status = readl(port->pmc->iom_base +
port->pmc->iom_port_status_offset +
- port_num * sizeof(u32));
+ port_num * port->pmc->iom_port_status_size);
}
static int sbu_orientation(struct pmc_usb_port *port)
@@ -319,8 +331,18 @@ pmc_usb_mux_tbt(struct pmc_usb_port *port, struct typec_mux_state *state)
if (data->cable_mode & TBT_CABLE_LINK_TRAINING)
req.mode_data |= PMC_USB_ALTMODE_ACTIVE_LINK;
- if (data->enter_vdo & TBT_ENTER_MODE_ACTIVE_CABLE)
- req.mode_data |= PMC_USB_ALTMODE_ACTIVE_CABLE;
+ if (acpi_dev_hid_uid_match(port->pmc->iom_adev, "INTC1072", NULL) ||
+ acpi_dev_hid_uid_match(port->pmc->iom_adev, "INTC1079", NULL)) {
+ if ((data->enter_vdo & TBT_ENTER_MODE_ACTIVE_CABLE) ||
+ (data->cable_mode & TBT_CABLE_RETIMER))
+ req.mode_data |= PMC_USB_ALTMODE_RETIMER_CABLE;
+ } else {
+ if (data->enter_vdo & TBT_ENTER_MODE_ACTIVE_CABLE)
+ req.mode_data |= PMC_USB_ALTMODE_ACTIVE_CABLE;
+
+ if (data->cable_mode & TBT_CABLE_RETIMER)
+ req.mode_data |= PMC_USB_ALTMODE_RETIMER_CABLE;
+ }
req.mode_data |= PMC_USB_ALTMODE_CABLE_SPD(cable_speed);
@@ -359,8 +381,17 @@ pmc_usb_mux_usb4(struct pmc_usb_port *port, struct typec_mux_state *state)
case EUDO_CABLE_TYPE_OPTICAL:
req.mode_data |= PMC_USB_ALTMODE_CABLE_TYPE;
fallthrough;
+ case EUDO_CABLE_TYPE_RE_TIMER:
+ if (!acpi_dev_hid_uid_match(port->pmc->iom_adev, "INTC1072", NULL) ||
+ !acpi_dev_hid_uid_match(port->pmc->iom_adev, "INTC1079", NULL))
+ req.mode_data |= PMC_USB_ALTMODE_RETIMER_CABLE;
+ fallthrough;
default:
- req.mode_data |= PMC_USB_ALTMODE_ACTIVE_CABLE;
+ if (acpi_dev_hid_uid_match(port->pmc->iom_adev, "INTC1072", NULL) ||
+ acpi_dev_hid_uid_match(port->pmc->iom_adev, "INTC1079", NULL))
+ req.mode_data |= PMC_USB_ALTMODE_RETIMER_CABLE;
+ else
+ req.mode_data |= PMC_USB_ALTMODE_ACTIVE_CABLE;
/* Configure data rate to rounded in the case of Active TBT3
* and USB4 cables.
@@ -589,13 +620,16 @@ err_unregister_switch:
/* IOM ACPI IDs and IOM_PORT_STATUS_OFFSET */
static const struct acpi_device_id iom_acpi_ids[] = {
/* TigerLake */
- { "INTC1072", 0x560, },
+ { "INTC1072", IOM_PORT_STATUS_REGS(0x560, IOM_PORT_STATUS_REGS_SZ_4) },
/* AlderLake */
- { "INTC1079", 0x160, },
+ { "INTC1079", IOM_PORT_STATUS_REGS(0x160, IOM_PORT_STATUS_REGS_SZ_4) },
/* Meteor Lake */
- { "INTC107A", 0x160, },
+ { "INTC107A", IOM_PORT_STATUS_REGS(0x160, IOM_PORT_STATUS_REGS_SZ_4) },
+
+ /* Lunar Lake */
+ { "INTC10EA", IOM_PORT_STATUS_REGS(0x150, IOM_PORT_STATUS_REGS_SZ_8) },
{}
};
@@ -615,7 +649,8 @@ static int pmc_usb_probe_iom(struct pmc_usb *pmc)
if (!adev)
return -ENODEV;
- pmc->iom_port_status_offset = (u32)dev_id->driver_data;
+ pmc->iom_port_status_offset = IOM_PORT_STATUS_REGS_OFFSET(dev_id->driver_data);
+ pmc->iom_port_status_size = IOM_PORT_STATUS_REGS_SIZE(dev_id->driver_data);
INIT_LIST_HEAD(&resource_list);
ret = acpi_dev_get_memory_resources(adev, &resource_list);
diff --git a/drivers/usb/typec/mux/nb7vpq904m.c b/drivers/usb/typec/mux/nb7vpq904m.c
index 4d1122d95013..cda206cf0c38 100644
--- a/drivers/usb/typec/mux/nb7vpq904m.c
+++ b/drivers/usb/typec/mux/nb7vpq904m.c
@@ -528,7 +528,7 @@ static struct i2c_driver nb7vpq904m_driver = {
.name = "nb7vpq904m",
.of_match_table = nb7vpq904m_of_table,
},
- .probe_new = nb7vpq904m_probe,
+ .probe = nb7vpq904m_probe,
.remove = nb7vpq904m_remove,
.id_table = nb7vpq904m_table,
};
diff --git a/drivers/usb/typec/tcpm/Kconfig b/drivers/usb/typec/tcpm/Kconfig
index 5d393f520fc2..0b2993fef564 100644
--- a/drivers/usb/typec/tcpm/Kconfig
+++ b/drivers/usb/typec/tcpm/Kconfig
@@ -79,6 +79,7 @@ config TYPEC_WCOVE
config TYPEC_QCOM_PMIC
tristate "Qualcomm PMIC USB Type-C Port Controller Manager driver"
depends on ARCH_QCOM || COMPILE_TEST
+ depends on DRM || DRM=n
help
A Type-C port and Power Delivery driver which aggregates two
discrete pieces of silicon in the PM8150b PMIC block: the
diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c
index 7fc1ffa14f76..bc21006e979c 100644
--- a/drivers/usb/typec/tcpm/fusb302.c
+++ b/drivers/usb/typec/tcpm/fusb302.c
@@ -15,7 +15,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/pinctrl/consumer.h>
#include <linux/proc_fs.h>
#include <linux/regulator/consumer.h>
diff --git a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c
index 9b467a346114..581199d37b49 100644
--- a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c
+++ b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c
@@ -8,7 +8,7 @@
#include <linux/kernel.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/of_graph.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
@@ -17,6 +17,9 @@
#include <linux/usb/role.h>
#include <linux/usb/tcpm.h>
#include <linux/usb/typec_mux.h>
+
+#include <drm/drm_bridge.h>
+
#include "qcom_pmic_typec_pdphy.h"
#include "qcom_pmic_typec_port.h"
@@ -33,6 +36,7 @@ struct pmic_typec {
struct pmic_typec_port *pmic_typec_port;
bool vbus_enabled;
struct mutex lock; /* VBUS state serialization */
+ struct drm_bridge bridge;
};
#define tcpc_to_tcpm(_tcpc_) container_of(_tcpc_, struct pmic_typec, tcpc)
@@ -146,6 +150,35 @@ static int qcom_pmic_typec_init(struct tcpc_dev *tcpc)
return 0;
}
+#if IS_ENABLED(CONFIG_DRM)
+static int qcom_pmic_typec_attach(struct drm_bridge *bridge,
+ enum drm_bridge_attach_flags flags)
+{
+ return flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR ? 0 : -EINVAL;
+}
+
+static const struct drm_bridge_funcs qcom_pmic_typec_bridge_funcs = {
+ .attach = qcom_pmic_typec_attach,
+};
+
+static int qcom_pmic_typec_init_drm(struct pmic_typec *tcpm)
+{
+ tcpm->bridge.funcs = &qcom_pmic_typec_bridge_funcs;
+#ifdef CONFIG_OF
+ tcpm->bridge.of_node = of_get_child_by_name(tcpm->dev->of_node, "connector");
+#endif
+ tcpm->bridge.ops = DRM_BRIDGE_OP_HPD;
+ tcpm->bridge.type = DRM_MODE_CONNECTOR_DisplayPort;
+
+ return devm_drm_bridge_add(tcpm->dev, &tcpm->bridge);
+}
+#else
+static int qcom_pmic_typec_init_drm(struct pmic_typec *tcpm)
+{
+ return 0;
+}
+#endif
+
static int qcom_pmic_typec_probe(struct platform_device *pdev)
{
struct pmic_typec *tcpm;
@@ -208,6 +241,10 @@ static int qcom_pmic_typec_probe(struct platform_device *pdev)
mutex_init(&tcpm->lock);
platform_set_drvdata(pdev, tcpm);
+ ret = qcom_pmic_typec_init_drm(tcpm);
+ if (ret)
+ return ret;
+
tcpm->tcpc.fwnode = device_get_named_child_node(tcpm->dev, "connector");
if (!tcpm->tcpc.fwnode)
return -EINVAL;
diff --git a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c
index 4e1b846627d2..bb0b8479d80f 100644
--- a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c
+++ b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c
@@ -8,8 +8,6 @@
#include <linux/kernel.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
-#include <linux/of_device.h>
-#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
diff --git a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c
index 94285f64b67d..a8f3f4d3a450 100644
--- a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c
+++ b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c
@@ -9,7 +9,6 @@
#include <linux/kernel.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
-#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
@@ -214,6 +213,11 @@ int qcom_pmic_typec_port_get_cc(struct pmic_typec_port *pmic_typec_port,
if (ret)
goto done;
switch (val & DETECTED_SRC_TYPE_MASK) {
+ case AUDIO_ACCESS_RA_RA:
+ val = TYPEC_CC_RA;
+ *cc1 = TYPEC_CC_RA;
+ *cc2 = TYPEC_CC_RA;
+ break;
case SRC_RD_OPEN:
val = TYPEC_CC_RD;
break;
diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c
index fc708c289a73..0ee3e6e29bb1 100644
--- a/drivers/usb/typec/tcpm/tcpci.c
+++ b/drivers/usb/typec/tcpm/tcpci.c
@@ -602,6 +602,10 @@ static int tcpci_init(struct tcpc_dev *tcpc)
if (time_after(jiffies, timeout))
return -ETIMEDOUT;
+ ret = tcpci_write16(tcpci, TCPC_FAULT_STATUS, TCPC_FAULT_STATUS_ALL_REG_RST_TO_DEFAULT);
+ if (ret < 0)
+ return ret;
+
/* Handle vendor init */
if (tcpci->data->init) {
ret = tcpci->data->init(tcpci, tcpci->data);
diff --git a/drivers/usb/typec/tcpm/tcpci_mt6370.c b/drivers/usb/typec/tcpm/tcpci_mt6370.c
index 2a079464b398..9cda1005ef01 100644
--- a/drivers/usb/typec/tcpm/tcpci_mt6370.c
+++ b/drivers/usb/typec/tcpm/tcpci_mt6370.c
@@ -147,7 +147,7 @@ static int mt6370_tcpc_probe(struct platform_device *pdev)
irq = platform_get_irq(pdev, 0);
if (irq < 0)
- return dev_err_probe(dev, irq, "Failed to get irq\n");
+ return irq;
/* Assign TCPCI feature and ops */
priv->tcpci_data.auto_discharge_disconnect = 1;
diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
index cc1d83926497..d962f67c95ae 100644
--- a/drivers/usb/typec/tcpm/tcpm.c
+++ b/drivers/usb/typec/tcpm/tcpm.c
@@ -1877,7 +1877,8 @@ static void tcpm_handle_vdm_request(struct tcpm_port *port,
}
break;
case ADEV_ATTENTION:
- typec_altmode_attention(adev, p[1]);
+ if (typec_altmode_attention(adev, p[1]))
+ tcpm_log(port, "typec_altmode_attention no port partner altmode");
break;
}
}
@@ -2753,6 +2754,13 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port,
port->sink_cap_done = true;
tcpm_set_state(port, ready_state(port), 0);
break;
+ /*
+ * Some port partners do not support GET_STATUS, avoid soft reset the link to
+ * prevent redundant power re-negotiation
+ */
+ case GET_STATUS_SEND:
+ tcpm_set_state(port, ready_state(port), 0);
+ break;
case SRC_READY:
case SNK_READY:
if (port->vdm_state > VDM_STATE_READY) {
@@ -3253,23 +3261,12 @@ static int tcpm_pd_select_pdo(struct tcpm_port *port, int *sink_pdo,
return ret;
}
-#define min_pps_apdo_current(x, y) \
- min(pdo_pps_apdo_max_current(x), pdo_pps_apdo_max_current(y))
-
static unsigned int tcpm_pd_select_pps_apdo(struct tcpm_port *port)
{
- unsigned int i, j, max_mw = 0, max_mv = 0;
- unsigned int min_src_mv, max_src_mv, src_ma, src_mw;
- unsigned int min_snk_mv, max_snk_mv;
- unsigned int max_op_mv;
- u32 pdo, src, snk;
- unsigned int src_pdo = 0, snk_pdo = 0;
+ unsigned int i, src_ma, max_temp_mw = 0, max_op_ma, op_mw;
+ unsigned int src_pdo = 0;
+ u32 pdo, src;
- /*
- * Select the source PPS APDO providing the most power while staying
- * within the board's limits. We skip the first PDO as this is always
- * 5V 3A.
- */
for (i = 1; i < port->nr_source_caps; ++i) {
pdo = port->source_caps[i];
@@ -3280,54 +3277,17 @@ static unsigned int tcpm_pd_select_pps_apdo(struct tcpm_port *port)
continue;
}
- min_src_mv = pdo_pps_apdo_min_voltage(pdo);
- max_src_mv = pdo_pps_apdo_max_voltage(pdo);
- src_ma = pdo_pps_apdo_max_current(pdo);
- src_mw = (src_ma * max_src_mv) / 1000;
-
- /*
- * Now search through the sink PDOs to find a matching
- * PPS APDO. Again skip the first sink PDO as this will
- * always be 5V 3A.
- */
- for (j = 1; j < port->nr_snk_pdo; j++) {
- pdo = port->snk_pdo[j];
-
- switch (pdo_type(pdo)) {
- case PDO_TYPE_APDO:
- if (pdo_apdo_type(pdo) != APDO_TYPE_PPS) {
- tcpm_log(port,
- "Not PPS APDO (sink), ignoring");
- continue;
- }
-
- min_snk_mv =
- pdo_pps_apdo_min_voltage(pdo);
- max_snk_mv =
- pdo_pps_apdo_max_voltage(pdo);
- break;
- default:
- tcpm_log(port,
- "Not APDO type (sink), ignoring");
- continue;
- }
+ if (port->pps_data.req_out_volt > pdo_pps_apdo_max_voltage(pdo) ||
+ port->pps_data.req_out_volt < pdo_pps_apdo_min_voltage(pdo))
+ continue;
- if (min_src_mv <= max_snk_mv &&
- max_src_mv >= min_snk_mv) {
- max_op_mv = min(max_src_mv, max_snk_mv);
- src_mw = (max_op_mv * src_ma) / 1000;
- /* Prefer higher voltages if available */
- if ((src_mw == max_mw &&
- max_op_mv > max_mv) ||
- src_mw > max_mw) {
- src_pdo = i;
- snk_pdo = j;
- max_mw = src_mw;
- max_mv = max_op_mv;
- }
- }
+ src_ma = pdo_pps_apdo_max_current(pdo);
+ max_op_ma = min(src_ma, port->pps_data.req_op_curr);
+ op_mw = max_op_ma * port->pps_data.req_out_volt / 1000;
+ if (op_mw > max_temp_mw) {
+ src_pdo = i;
+ max_temp_mw = op_mw;
}
-
break;
default:
tcpm_log(port, "Not APDO type (source), ignoring");
@@ -3337,16 +3297,10 @@ static unsigned int tcpm_pd_select_pps_apdo(struct tcpm_port *port)
if (src_pdo) {
src = port->source_caps[src_pdo];
- snk = port->snk_pdo[snk_pdo];
-
- port->pps_data.req_min_volt = max(pdo_pps_apdo_min_voltage(src),
- pdo_pps_apdo_min_voltage(snk));
- port->pps_data.req_max_volt = min(pdo_pps_apdo_max_voltage(src),
- pdo_pps_apdo_max_voltage(snk));
- port->pps_data.req_max_curr = min_pps_apdo_current(src, snk);
- port->pps_data.req_out_volt = min(port->pps_data.req_max_volt,
- max(port->pps_data.req_min_volt,
- port->pps_data.req_out_volt));
+
+ port->pps_data.req_min_volt = pdo_pps_apdo_min_voltage(src);
+ port->pps_data.req_max_volt = pdo_pps_apdo_max_voltage(src);
+ port->pps_data.req_max_curr = pdo_pps_apdo_max_current(src);
port->pps_data.req_op_curr = min(port->pps_data.req_max_curr,
port->pps_data.req_op_curr);
}
@@ -3464,32 +3418,16 @@ static int tcpm_pd_send_request(struct tcpm_port *port)
static int tcpm_pd_build_pps_request(struct tcpm_port *port, u32 *rdo)
{
unsigned int out_mv, op_ma, op_mw, max_mv, max_ma, flags;
- enum pd_pdo_type type;
unsigned int src_pdo_index;
- u32 pdo;
src_pdo_index = tcpm_pd_select_pps_apdo(port);
if (!src_pdo_index)
return -EOPNOTSUPP;
- pdo = port->source_caps[src_pdo_index];
- type = pdo_type(pdo);
-
- switch (type) {
- case PDO_TYPE_APDO:
- if (pdo_apdo_type(pdo) != APDO_TYPE_PPS) {
- tcpm_log(port, "Invalid APDO selected!");
- return -EINVAL;
- }
- max_mv = port->pps_data.req_max_volt;
- max_ma = port->pps_data.req_max_curr;
- out_mv = port->pps_data.req_out_volt;
- op_ma = port->pps_data.req_op_curr;
- break;
- default:
- tcpm_log(port, "Invalid PDO selected!");
- return -EINVAL;
- }
+ max_mv = port->pps_data.req_max_volt;
+ max_ma = port->pps_data.req_max_curr;
+ out_mv = port->pps_data.req_out_volt;
+ op_ma = port->pps_data.req_op_curr;
flags = RDO_USB_COMM | RDO_NO_SUSPEND;
@@ -3789,6 +3727,9 @@ static void tcpm_detach(struct tcpm_port *port)
if (tcpm_port_is_disconnected(port))
port->hard_reset_count = 0;
+ port->try_src_count = 0;
+ port->try_snk_count = 0;
+
if (!port->attached)
return;
@@ -3928,6 +3869,29 @@ static enum typec_cc_status tcpm_pwr_opmode_to_rp(enum typec_pwr_opmode opmode)
}
}
+static void tcpm_set_initial_svdm_version(struct tcpm_port *port)
+{
+ switch (port->negotiated_rev) {
+ case PD_REV30:
+ break;
+ /*
+ * 6.4.4.2.3 Structured VDM Version
+ * 2.0 states "At this time, there is only one version (1.0) defined.
+ * This field Shall be set to zero to indicate Version 1.0."
+ * 3.0 states "This field Shall be set to 01b to indicate Version 2.0."
+ * To ensure that we follow the Power Delivery revision we are currently
+ * operating on, downgrade the SVDM version to the highest one supported
+ * by the Power Delivery revision.
+ */
+ case PD_REV20:
+ typec_partner_set_svdm_version(port->partner, SVDM_VER_1_0);
+ break;
+ default:
+ typec_partner_set_svdm_version(port->partner, SVDM_VER_1_0);
+ break;
+ }
+}
+
static void run_state_machine(struct tcpm_port *port)
{
int ret;
@@ -4165,10 +4129,12 @@ static void run_state_machine(struct tcpm_port *port)
* For now, this driver only supports SOP for DISCOVER_IDENTITY, thus using
* port->explicit_contract to decide whether to send the command.
*/
- if (port->explicit_contract)
+ if (port->explicit_contract) {
+ tcpm_set_initial_svdm_version(port);
mod_send_discover_delayed_work(port, 0);
- else
+ } else {
port->send_discover = false;
+ }
/*
* 6.3.5
@@ -4301,7 +4267,9 @@ static void run_state_machine(struct tcpm_port *port)
if (port->slow_charger_loop && (current_lim > PD_P_SNK_STDBY_MW / 5))
current_lim = PD_P_SNK_STDBY_MW / 5;
tcpm_set_current_limit(port, current_lim, 5000);
- tcpm_set_charge(port, true);
+ /* Not sink vbus if operational current is 0mA */
+ tcpm_set_charge(port, !!pdo_max_current(port->snk_pdo[0]));
+
if (!port->pd_supported)
tcpm_set_state(port, SNK_READY, 0);
else
@@ -4455,10 +4423,12 @@ static void run_state_machine(struct tcpm_port *port)
* For now, this driver only supports SOP for DISCOVER_IDENTITY, thus using
* port->explicit_contract.
*/
- if (port->explicit_contract)
+ if (port->explicit_contract) {
+ tcpm_set_initial_svdm_version(port);
mod_send_discover_delayed_work(port, 0);
- else
+ } else {
port->send_discover = false;
+ }
power_supply_changed(port->psy);
break;
@@ -4582,7 +4552,8 @@ static void run_state_machine(struct tcpm_port *port)
tcpm_set_current_limit(port,
tcpm_get_current_limit(port),
5000);
- tcpm_set_charge(port, true);
+ /* Not sink vbus if operational current is 0mA */
+ tcpm_set_charge(port, !!pdo_max_current(port->snk_pdo[0]));
}
if (port->ams == HARD_RESET)
tcpm_ams_finish(port);
@@ -5889,12 +5860,6 @@ static int tcpm_pps_set_out_volt(struct tcpm_port *port, u16 req_out_volt)
goto port_unlock;
}
- if (req_out_volt < port->pps_data.min_volt ||
- req_out_volt > port->pps_data.max_volt) {
- ret = -EINVAL;
- goto port_unlock;
- }
-
target_mw = (port->current_limit * req_out_volt) / 1000;
if (target_mw < port->operating_snk_mw) {
ret = -EINVAL;
@@ -6447,11 +6412,7 @@ static int tcpm_psy_set_prop(struct power_supply *psy,
ret = tcpm_psy_set_online(port, val);
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
- if (val->intval < port->pps_data.min_volt * 1000 ||
- val->intval > port->pps_data.max_volt * 1000)
- ret = -EINVAL;
- else
- ret = tcpm_pps_set_out_volt(port, val->intval / 1000);
+ ret = tcpm_pps_set_out_volt(port, val->intval / 1000);
break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
if (val->intval > port->pps_data.max_curr * 1000)
diff --git a/drivers/usb/typec/ucsi/Kconfig b/drivers/usb/typec/ucsi/Kconfig
index b3bb0191987e..bdcb1764cfae 100644
--- a/drivers/usb/typec/ucsi/Kconfig
+++ b/drivers/usb/typec/ucsi/Kconfig
@@ -4,6 +4,7 @@ config TYPEC_UCSI
tristate "USB Type-C Connector System Software Interface driver"
depends on !CPU_BIG_ENDIAN
depends on USB_ROLE_SWITCH || !USB_ROLE_SWITCH
+ select USB_COMMON if DEBUG_FS
help
USB Type-C Connector System Software Interface (UCSI) is a
specification for an interface that allows the operating system to
diff --git a/drivers/usb/typec/ucsi/Makefile b/drivers/usb/typec/ucsi/Makefile
index 77f09e136956..b4679f94696b 100644
--- a/drivers/usb/typec/ucsi/Makefile
+++ b/drivers/usb/typec/ucsi/Makefile
@@ -5,6 +5,8 @@ obj-$(CONFIG_TYPEC_UCSI) += typec_ucsi.o
typec_ucsi-y := ucsi.o
+typec_ucsi-$(CONFIG_DEBUG_FS) += debugfs.o
+
typec_ucsi-$(CONFIG_TRACING) += trace.o
ifneq ($(CONFIG_POWER_SUPPLY),)
diff --git a/drivers/usb/typec/ucsi/debugfs.c b/drivers/usb/typec/ucsi/debugfs.c
new file mode 100644
index 000000000000..0c7bf88d4a7f
--- /dev/null
+++ b/drivers/usb/typec/ucsi/debugfs.c
@@ -0,0 +1,99 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * UCSI debugfs interface
+ *
+ * Copyright (C) 2023 Intel Corporation
+ *
+ * Authors: Rajaram Regupathy <rajaram.regupathy@intel.com>
+ * Gopal Saranya <saranya.gopal@intel.com>
+ */
+#include <linux/debugfs.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/usb.h>
+
+#include <asm/errno.h>
+
+#include "ucsi.h"
+
+static struct dentry *ucsi_debugfs_root;
+
+static int ucsi_cmd(void *data, u64 val)
+{
+ struct ucsi *ucsi = data;
+ int ret;
+
+ memset(&ucsi->debugfs->response, 0, sizeof(ucsi->debugfs->response));
+ ucsi->debugfs->status = 0;
+
+ switch (UCSI_COMMAND(val)) {
+ case UCSI_SET_UOM:
+ case UCSI_SET_UOR:
+ case UCSI_SET_PDR:
+ case UCSI_CONNECTOR_RESET:
+ ret = ucsi_send_command(ucsi, val, NULL, 0);
+ break;
+ case UCSI_GET_CAPABILITY:
+ case UCSI_GET_CONNECTOR_CAPABILITY:
+ case UCSI_GET_ALTERNATE_MODES:
+ case UCSI_GET_CURRENT_CAM:
+ case UCSI_GET_PDOS:
+ case UCSI_GET_CABLE_PROPERTY:
+ case UCSI_GET_CONNECTOR_STATUS:
+ ret = ucsi_send_command(ucsi, val,
+ &ucsi->debugfs->response,
+ sizeof(ucsi->debugfs->response));
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ }
+
+ if (ret < 0) {
+ ucsi->debugfs->status = ret;
+ return ret;
+ }
+
+ return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(ucsi_cmd_fops, NULL, ucsi_cmd, "0x%llx\n");
+
+static int ucsi_resp_show(struct seq_file *s, void *not_used)
+{
+ struct ucsi *ucsi = s->private;
+
+ if (ucsi->debugfs->status)
+ return ucsi->debugfs->status;
+
+ seq_printf(s, "0x%016llx%016llx\n", ucsi->debugfs->response.high,
+ ucsi->debugfs->response.low);
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(ucsi_resp);
+
+void ucsi_debugfs_register(struct ucsi *ucsi)
+{
+ ucsi->debugfs = kzalloc(sizeof(*ucsi->debugfs), GFP_KERNEL);
+ if (!ucsi->debugfs)
+ return;
+
+ ucsi->debugfs->dentry = debugfs_create_dir(dev_name(ucsi->dev), ucsi_debugfs_root);
+ debugfs_create_file("command", 0200, ucsi->debugfs->dentry, ucsi, &ucsi_cmd_fops);
+ debugfs_create_file("response", 0400, ucsi->debugfs->dentry, ucsi, &ucsi_resp_fops);
+}
+
+void ucsi_debugfs_unregister(struct ucsi *ucsi)
+{
+ debugfs_remove_recursive(ucsi->debugfs->dentry);
+ kfree(ucsi->debugfs);
+}
+
+void ucsi_debugfs_init(void)
+{
+ ucsi_debugfs_root = debugfs_create_dir("ucsi", usb_debug_root);
+}
+
+void ucsi_debugfs_exit(void)
+{
+ debugfs_remove(ucsi_debugfs_root);
+}
diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c
index f6901319639d..c6dfe3dff346 100644
--- a/drivers/usb/typec/ucsi/ucsi.c
+++ b/drivers/usb/typec/ucsi/ucsi.c
@@ -1530,6 +1530,7 @@ EXPORT_SYMBOL_GPL(ucsi_create);
*/
void ucsi_destroy(struct ucsi *ucsi)
{
+ ucsi_debugfs_unregister(ucsi);
kfree(ucsi);
}
EXPORT_SYMBOL_GPL(ucsi_destroy);
@@ -1552,6 +1553,7 @@ int ucsi_register(struct ucsi *ucsi)
queue_delayed_work(system_long_wq, &ucsi->work, 0);
+ ucsi_debugfs_register(ucsi);
return 0;
}
EXPORT_SYMBOL_GPL(ucsi_register);
@@ -1611,6 +1613,19 @@ void ucsi_unregister(struct ucsi *ucsi)
}
EXPORT_SYMBOL_GPL(ucsi_unregister);
+static int __init ucsi_module_init(void)
+{
+ ucsi_debugfs_init();
+ return 0;
+}
+module_init(ucsi_module_init);
+
+static void __exit ucsi_module_exit(void)
+{
+ ucsi_debugfs_exit();
+}
+module_exit(ucsi_module_exit);
+
MODULE_AUTHOR("Heikki Krogerus <heikki.krogerus@linux.intel.com>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("USB Type-C Connector System Software Interface driver");
diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h
index c09af859f573..474315a72c77 100644
--- a/drivers/usb/typec/ucsi/ucsi.h
+++ b/drivers/usb/typec/ucsi/ucsi.h
@@ -15,6 +15,7 @@
struct ucsi;
struct ucsi_altmode;
+struct dentry;
/* UCSI offsets (Bytes) */
#define UCSI_VERSION 0
@@ -277,6 +278,16 @@ struct ucsi_connector_status {
/* -------------------------------------------------------------------------- */
+struct ucsi_debugfs_entry {
+ u64 command;
+ struct ucsi_data {
+ u64 low;
+ u64 high;
+ } response;
+ u32 status;
+ struct dentry *dentry;
+};
+
struct ucsi {
u16 version;
struct device *dev;
@@ -286,6 +297,7 @@ struct ucsi {
struct ucsi_capability cap;
struct ucsi_connector *connector;
+ struct ucsi_debugfs_entry *debugfs;
struct work_struct resume_work;
struct delayed_work work;
@@ -388,6 +400,18 @@ static inline void
ucsi_displayport_remove_partner(struct typec_altmode *adev) { }
#endif /* CONFIG_TYPEC_DP_ALTMODE */
+#ifdef CONFIG_DEBUG_FS
+void ucsi_debugfs_init(void);
+void ucsi_debugfs_exit(void);
+void ucsi_debugfs_register(struct ucsi *ucsi);
+void ucsi_debugfs_unregister(struct ucsi *ucsi);
+#else
+static inline void ucsi_debugfs_init(void) { }
+static inline void ucsi_debugfs_exit(void) { }
+static inline void ucsi_debugfs_register(struct ucsi *ucsi) { }
+static inline void ucsi_debugfs_unregister(struct ucsi *ucsi) { }
+#endif /* CONFIG_DEBUG_FS */
+
/*
* NVIDIA VirtualLink (svid 0x955) has two altmode. VirtualLink
* DP mode with vdo=0x1 and NVIDIA test mode with vdo=0x3
diff --git a/drivers/usb/typec/ucsi/ucsi_glink.c b/drivers/usb/typec/ucsi/ucsi_glink.c
index 1fe9cb5b6bd9..bb1854b3311d 100644
--- a/drivers/usb/typec/ucsi/ucsi_glink.c
+++ b/drivers/usb/typec/ucsi/ucsi_glink.c
@@ -5,7 +5,6 @@
*/
#include <linux/auxiliary_bus.h>
#include <linux/module.h>
-#include <linux/of_device.h>
#include <linux/mutex.h>
#include <linux/property.h>
#include <linux/soc/qcom/pdr.h>
diff --git a/drivers/usb/usbip/vudc_dev.c b/drivers/usb/usbip/vudc_dev.c
index 2bc428f2e261..44b04c54c086 100644
--- a/drivers/usb/usbip/vudc_dev.c
+++ b/drivers/usb/usbip/vudc_dev.c
@@ -489,11 +489,11 @@ static void vudc_device_unusable(struct usbip_device *ud)
struct vudc_device *alloc_vudc_device(int devid)
{
- struct vudc_device *udc_dev = NULL;
+ struct vudc_device *udc_dev;
udc_dev = kzalloc(sizeof(*udc_dev), GFP_KERNEL);
if (!udc_dev)
- goto out;
+ return NULL;
INIT_LIST_HEAD(&udc_dev->dev_entry);
@@ -503,7 +503,6 @@ struct vudc_device *alloc_vudc_device(int devid)
udc_dev = NULL;
}
-out:
return udc_dev;
}