summaryrefslogtreecommitdiff
path: root/drivers/usb
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2023-07-03 13:23:10 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2023-07-03 13:23:10 -0700
commit56cbceab928d7ac3702de172ff8dcc1da2a6aaeb (patch)
treed225b447d5421b652dae4b7d3501bc82900a8ecf /drivers/usb
parent868a9fd9480785952336e5f119e1f75877c423a8 (diff)
parent18af4b5c97915a6daef9de28a30ae1d3786bc2ac (diff)
Merge tag 'usb-6.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
Pull USB / Thunderbolt driver updates from Greg KH: "Here is the big set of USB and Thunderbolt driver updates for 6.5-rc1. Included in here are: - Lots of USB4/Thunderbolt additions and updates for new hardware types and fixes as people are starting to get access to the hardware in the wild - new gadget controller driver, cdns2, added - new typec drivers added - xhci driver updates - typec driver updates - usbip driver fixes - usb-serial driver updates and fixes - lots of smaller USB driver updates All of these have been in linux-next for a while with no reported problems" * tag 'usb-6.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (265 commits) usb: host: xhci-plat: Set XHCI_STATE_REMOVING before resuming XHCI HC usb: host: xhci: Do not re-initialize the XHCI HC if being removed usb: typec: nb7vpq904m: fix CONFIG_DRM dependency usbip: usbip_host: Replace strlcpy with strscpy usb: dwc3: gadget: Propagate core init errors to UDC during pullup USB: serial: option: add LARA-R6 01B PIDs usb: ulpi: Make container_of() no-op in to_ulpi_dev() usb: gadget: legacy: fix error return code in gfs_bind usb: typec: fsa4480: add support for Audio Accessory Mode usb: typec: fsa4480: rework mux & switch setup to handle more states usb: typec: ucsi: call typec_set_mode on non-altmode partner change USB: gadget: f_hid: make hidg_class a static const structure USB: gadget: f_printer: make usb_gadget_class a static const structure USB: mon: make mon_bin_class a static const structure USB: gadget: udc: core: make udc_class a static const structure USB: roles: make role_class a static const structure dt-bindings: usb: dwc3: Add interrupt-names property support for wakeup interrupt dt-bindings: usb: Add StarFive JH7110 USB controller dt-bindings: usb: dwc3: Add IPQ9574 compatible usb: cdns2: Fix spelling mistake in a trace message "Wakupe" -> "Wakeup" ...
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/c67x00/c67x00-drv.c6
-rw-r--r--drivers/usb/cdns3/Kconfig11
-rw-r--r--drivers/usb/cdns3/Makefile1
-rw-r--r--drivers/usb/cdns3/cdns3-gadget.c15
-rw-r--r--drivers/usb/cdns3/cdns3-imx.c30
-rw-r--r--drivers/usb/cdns3/cdns3-plat.c5
-rw-r--r--drivers/usb/cdns3/cdns3-starfive.c246
-rw-r--r--drivers/usb/cdns3/cdns3-ti.c6
-rw-r--r--drivers/usb/chipidea/ci_hdrc_imx.c11
-rw-r--r--drivers/usb/chipidea/ci_hdrc_msm.c6
-rw-r--r--drivers/usb/chipidea/ci_hdrc_tegra.c6
-rw-r--r--drivers/usb/chipidea/ci_hdrc_usb2.c6
-rw-r--r--drivers/usb/chipidea/core.c6
-rw-r--r--drivers/usb/chipidea/usbmisc_imx.c38
-rw-r--r--drivers/usb/common/usb-conn-gpio.c6
-rw-r--r--drivers/usb/core/devio.c18
-rw-r--r--drivers/usb/core/hcd-pci.c24
-rw-r--r--drivers/usb/core/hub.c15
-rw-r--r--drivers/usb/core/hub.h4
-rw-r--r--drivers/usb/core/port.c32
-rw-r--r--drivers/usb/dwc2/params.c21
-rw-r--r--drivers/usb/dwc2/platform.c22
-rw-r--r--drivers/usb/dwc3/core.c17
-rw-r--r--drivers/usb/dwc3/core.h2
-rw-r--r--drivers/usb/dwc3/dwc3-am62.c5
-rw-r--r--drivers/usb/dwc3/dwc3-exynos.c6
-rw-r--r--drivers/usb/dwc3/dwc3-imx8mp.c6
-rw-r--r--drivers/usb/dwc3/dwc3-keystone.c6
-rw-r--r--drivers/usb/dwc3/dwc3-meson-g12a.c27
-rw-r--r--drivers/usb/dwc3/dwc3-of-simple.c6
-rw-r--r--drivers/usb/dwc3/dwc3-omap.c6
-rw-r--r--drivers/usb/dwc3/dwc3-qcom.c43
-rw-r--r--drivers/usb/dwc3/dwc3-st.c6
-rw-r--r--drivers/usb/dwc3/dwc3-xilinx.c6
-rw-r--r--drivers/usb/dwc3/ep0.c3
-rw-r--r--drivers/usb/dwc3/gadget.c13
-rw-r--r--drivers/usb/fotg210/fotg210-core.c6
-rw-r--r--drivers/usb/gadget/function/f_hid.c21
-rw-r--r--drivers/usb/gadget/function/f_mass_storage.c8
-rw-r--r--drivers/usb/gadget/function/f_printer.c29
-rw-r--r--drivers/usb/gadget/function/u_serial.c22
-rw-r--r--drivers/usb/gadget/function/uvc_video.c50
-rw-r--r--drivers/usb/gadget/legacy/g_ffs.c4
-rw-r--r--drivers/usb/gadget/legacy/hid.c6
-rw-r--r--drivers/usb/gadget/udc/Kconfig2
-rw-r--r--drivers/usb/gadget/udc/Makefile1
-rw-r--r--drivers/usb/gadget/udc/aspeed-vhub/core.c8
-rw-r--r--drivers/usb/gadget/udc/atmel_usba_udc.c6
-rw-r--r--drivers/usb/gadget/udc/bcm63xx_udc.c6
-rw-r--r--drivers/usb/gadget/udc/bdc/bdc_core.c5
-rw-r--r--drivers/usb/gadget/udc/cdns2/Kconfig11
-rw-r--r--drivers/usb/gadget/udc/cdns2/Makefile7
-rw-r--r--drivers/usb/gadget/udc/cdns2/cdns2-debug.h203
-rw-r--r--drivers/usb/gadget/udc/cdns2/cdns2-ep0.c659
-rw-r--r--drivers/usb/gadget/udc/cdns2/cdns2-gadget.c2474
-rw-r--r--drivers/usb/gadget/udc/cdns2/cdns2-gadget.h707
-rw-r--r--drivers/usb/gadget/udc/cdns2/cdns2-pci.c138
-rw-r--r--drivers/usb/gadget/udc/cdns2/cdns2-trace.c11
-rw-r--r--drivers/usb/gadget/udc/cdns2/cdns2-trace.h605
-rw-r--r--drivers/usb/gadget/udc/core.c24
-rw-r--r--drivers/usb/gadget/udc/dummy_hcd.c11
-rw-r--r--drivers/usb/gadget/udc/fsl_qe_udc.c6
-rw-r--r--drivers/usb/gadget/udc/fusb300_udc.c6
-rw-r--r--drivers/usb/gadget/udc/m66592-udc.c5
-rw-r--r--drivers/usb/gadget/udc/mv_u3d_core.c6
-rw-r--r--drivers/usb/gadget/udc/mv_udc_core.c6
-rw-r--r--drivers/usb/gadget/udc/net2272.c6
-rw-r--r--drivers/usb/gadget/udc/omap_udc.c6
-rw-r--r--drivers/usb/gadget/udc/pxa27x_udc.c6
-rw-r--r--drivers/usb/gadget/udc/r8a66597-udc.c6
-rw-r--r--drivers/usb/gadget/udc/renesas_usb3.c6
-rw-r--r--drivers/usb/gadget/udc/renesas_usbf.c6
-rw-r--r--drivers/usb/gadget/udc/rzv2m_usb3drd.c6
-rw-r--r--drivers/usb/gadget/udc/snps_udc_plat.c8
-rw-r--r--drivers/usb/gadget/udc/tegra-xudc.c6
-rw-r--r--drivers/usb/gadget/udc/udc-xilinx.c8
-rw-r--r--drivers/usb/host/Kconfig4
-rw-r--r--drivers/usb/host/ehci-atmel.c6
-rw-r--r--drivers/usb/host/ehci-brcm.c5
-rw-r--r--drivers/usb/host/ehci-exynos.c6
-rw-r--r--drivers/usb/host/ehci-fsl.c6
-rw-r--r--drivers/usb/host/ehci-grlib.c6
-rw-r--r--drivers/usb/host/ehci-mv.c6
-rw-r--r--drivers/usb/host/ehci-npcm7xx.c6
-rw-r--r--drivers/usb/host/ehci-omap.c6
-rw-r--r--drivers/usb/host/ehci-orion.c6
-rw-r--r--drivers/usb/host/ehci-pci.c3
-rw-r--r--drivers/usb/host/ehci-platform.c6
-rw-r--r--drivers/usb/host/ehci-ppc-of.c6
-rw-r--r--drivers/usb/host/ehci-sh.c6
-rw-r--r--drivers/usb/host/ehci-spear.c6
-rw-r--r--drivers/usb/host/ehci-st.c6
-rw-r--r--drivers/usb/host/ehci-xilinx-of.c6
-rw-r--r--drivers/usb/host/fhci-hcd.c9
-rw-r--r--drivers/usb/host/fsl-mph-dr-of.c5
-rw-r--r--drivers/usb/host/isp116x-hcd.c7
-rw-r--r--drivers/usb/host/isp1362-hcd.c6
-rw-r--r--drivers/usb/host/octeon-hcd.c6
-rw-r--r--drivers/usb/host/ohci-at91.c5
-rw-r--r--drivers/usb/host/ohci-da8xx.c6
-rw-r--r--drivers/usb/host/ohci-exynos.c6
-rw-r--r--drivers/usb/host/ohci-nxp.c6
-rw-r--r--drivers/usb/host/ohci-omap.c5
-rw-r--r--drivers/usb/host/ohci-pci.c8
-rw-r--r--drivers/usb/host/ohci-platform.c8
-rw-r--r--drivers/usb/host/ohci-ppc-of.c6
-rw-r--r--drivers/usb/host/ohci-pxa27x.c5
-rw-r--r--drivers/usb/host/ohci-s3c2410.c5
-rw-r--r--drivers/usb/host/ohci-sm501.c6
-rw-r--r--drivers/usb/host/ohci-spear.c5
-rw-r--r--drivers/usb/host/ohci-st.c6
-rw-r--r--drivers/usb/host/oxu210hp-hcd.c6
-rw-r--r--drivers/usb/host/r8a66597-hcd.c5
-rw-r--r--drivers/usb/host/sl811-hcd.c5
-rw-r--r--drivers/usb/host/uhci-grlib.c6
-rw-r--r--drivers/usb/host/uhci-hcd.c2
-rw-r--r--drivers/usb/host/uhci-hcd.h24
-rw-r--r--drivers/usb/host/uhci-pci.c7
-rw-r--r--drivers/usb/host/uhci-platform.c6
-rw-r--r--drivers/usb/host/xhci-histb.c20
-rw-r--r--drivers/usb/host/xhci-mem.c132
-rw-r--r--drivers/usb/host/xhci-mtk.c12
-rw-r--r--drivers/usb/host/xhci-pci.c24
-rw-r--r--drivers/usb/host/xhci-plat.c23
-rw-r--r--drivers/usb/host/xhci-plat.h2
-rw-r--r--drivers/usb/host/xhci-rcar.c6
-rw-r--r--drivers/usb/host/xhci-ring.c137
-rw-r--r--drivers/usb/host/xhci-tegra.c12
-rw-r--r--drivers/usb/host/xhci-trace.h9
-rw-r--r--drivers/usb/host/xhci.c56
-rw-r--r--drivers/usb/host/xhci.h9
-rw-r--r--drivers/usb/isp1760/isp1760-if.c6
-rw-r--r--drivers/usb/misc/onboard_usb_hub.c6
-rw-r--r--drivers/usb/misc/qcom_eud.c10
-rw-r--r--drivers/usb/misc/usb251xb.c11
-rw-r--r--drivers/usb/misc/usb3503.c8
-rw-r--r--drivers/usb/misc/usb4604.c2
-rw-r--r--drivers/usb/mon/mon_bin.c19
-rw-r--r--drivers/usb/phy/phy-isp1301.c2
-rw-r--r--drivers/usb/phy/phy-tahvo.c2
-rw-r--r--drivers/usb/renesas_usbhs/common.c6
-rw-r--r--drivers/usb/roles/class.c17
-rw-r--r--drivers/usb/roles/intel-xhci-usb-role-switch.c6
-rw-r--r--drivers/usb/serial/ark3116.c7
-rw-r--r--drivers/usb/serial/belkin_sa.c12
-rw-r--r--drivers/usb/serial/ch341.c37
-rw-r--r--drivers/usb/serial/cp210x.c14
-rw-r--r--drivers/usb/serial/digi_acceleport.c7
-rw-r--r--drivers/usb/serial/f81232.c4
-rw-r--r--drivers/usb/serial/f81534.c4
-rw-r--r--drivers/usb/serial/ftdi_sio.c10
-rw-r--r--drivers/usb/serial/io_edgeport.c6
-rw-r--r--drivers/usb/serial/io_ti.c9
-rw-r--r--drivers/usb/serial/keyspan.c5
-rw-r--r--drivers/usb/serial/keyspan_pda.c8
-rw-r--r--drivers/usb/serial/mct_u232.c6
-rw-r--r--drivers/usb/serial/mos7720.c9
-rw-r--r--drivers/usb/serial/mos7840.c7
-rw-r--r--drivers/usb/serial/mxuport.c6
-rw-r--r--drivers/usb/serial/option.c4
-rw-r--r--drivers/usb/serial/pl2303.c14
-rw-r--r--drivers/usb/serial/quatech2.c8
-rw-r--r--drivers/usb/serial/ti_usb_3410_5052.c10
-rw-r--r--drivers/usb/serial/upd78f0730.c7
-rw-r--r--drivers/usb/serial/usb-serial.c4
-rw-r--r--drivers/usb/serial/usb_debug.c13
-rw-r--r--drivers/usb/serial/whiteheat.c7
-rw-r--r--drivers/usb/serial/xr_serial.c4
-rw-r--r--drivers/usb/typec/Kconfig13
-rw-r--r--drivers/usb/typec/Makefile1
-rw-r--r--drivers/usb/typec/anx7411.c2
-rw-r--r--drivers/usb/typec/class.c4
-rw-r--r--drivers/usb/typec/hd3ss3220.c4
-rw-r--r--drivers/usb/typec/mux.c61
-rw-r--r--drivers/usb/typec/mux/Kconfig10
-rw-r--r--drivers/usb/typec/mux/Makefile1
-rw-r--r--drivers/usb/typec/mux/fsa4480.c128
-rw-r--r--drivers/usb/typec/mux/gpio-sbu-mux.c11
-rw-r--r--drivers/usb/typec/mux/intel_pmc_mux.c56
-rw-r--r--drivers/usb/typec/mux/nb7vpq904m.c529
-rw-r--r--drivers/usb/typec/mux/pi3usb30532.c2
-rw-r--r--drivers/usb/typec/qcom-pmic-typec.c261
-rw-r--r--drivers/usb/typec/rt1719.c2
-rw-r--r--drivers/usb/typec/stusb160x.c2
-rw-r--r--drivers/usb/typec/tcpm/Kconfig11
-rw-r--r--drivers/usb/typec/tcpm/Makefile1
-rw-r--r--drivers/usb/typec/tcpm/fusb302.c2
-rw-r--r--drivers/usb/typec/tcpm/qcom/Makefile6
-rw-r--r--drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c344
-rw-r--r--drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c528
-rw-r--r--drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.h119
-rw-r--r--drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c556
-rw-r--r--drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.h195
-rw-r--r--drivers/usb/typec/tcpm/tcpci.c2
-rw-r--r--drivers/usb/typec/tcpm/tcpci_maxim_core.c2
-rw-r--r--drivers/usb/typec/tcpm/tcpci_mt6360.c5
-rw-r--r--drivers/usb/typec/tcpm/tcpci_mt6370.c6
-rw-r--r--drivers/usb/typec/tcpm/tcpci_rt1711h.c2
-rw-r--r--drivers/usb/typec/tcpm/tcpm.c27
-rw-r--r--drivers/usb/typec/tcpm/wcove.c6
-rw-r--r--drivers/usb/typec/tipd/core.c2
-rw-r--r--drivers/usb/typec/ucsi/ucsi.c17
-rw-r--r--drivers/usb/typec/ucsi/ucsi_acpi.c6
-rw-r--r--drivers/usb/typec/ucsi/ucsi_ccg.c2
-rw-r--r--drivers/usb/typec/ucsi/ucsi_glink.c2
-rw-r--r--drivers/usb/typec/ucsi/ucsi_stm32g0.c2
-rw-r--r--drivers/usb/typec/wusb3801.c2
-rw-r--r--drivers/usb/usbip/stub_main.c4
-rw-r--r--drivers/usb/usbip/vhci_hcd.c6
209 files changed, 8539 insertions, 1233 deletions
diff --git a/drivers/usb/c67x00/c67x00-drv.c b/drivers/usb/c67x00/c67x00-drv.c
index 6db5cb1b2dbb..bb9d5d7ffefc 100644
--- a/drivers/usb/c67x00/c67x00-drv.c
+++ b/drivers/usb/c67x00/c67x00-drv.c
@@ -177,7 +177,7 @@ static int c67x00_drv_probe(struct platform_device *pdev)
return ret;
}
-static int c67x00_drv_remove(struct platform_device *pdev)
+static void c67x00_drv_remove(struct platform_device *pdev)
{
struct c67x00_device *c67x00 = platform_get_drvdata(pdev);
struct resource *res;
@@ -197,13 +197,11 @@ static int c67x00_drv_remove(struct platform_device *pdev)
release_mem_region(res->start, resource_size(res));
kfree(c67x00);
-
- return 0;
}
static struct platform_driver c67x00_driver = {
.probe = c67x00_drv_probe,
- .remove = c67x00_drv_remove,
+ .remove_new = c67x00_drv_remove,
.driver = {
.name = "c67x00",
},
diff --git a/drivers/usb/cdns3/Kconfig b/drivers/usb/cdns3/Kconfig
index b98ca0a1352a..0a514b591527 100644
--- a/drivers/usb/cdns3/Kconfig
+++ b/drivers/usb/cdns3/Kconfig
@@ -78,6 +78,17 @@ config USB_CDNS3_IMX
For example, imx8qm and imx8qxp.
+config USB_CDNS3_STARFIVE
+ tristate "Cadence USB3 support on StarFive SoC platforms"
+ depends on ARCH_STARFIVE || COMPILE_TEST
+ help
+ Say 'Y' or 'M' here if you are building for StarFive SoCs
+ platforms that contain Cadence USB3 controller core.
+
+ e.g. JH7110.
+
+ If you choose to build this driver as module it will
+ be dynamically linked and module will be called cdns3-starfive.ko
endif
if USB_CDNS_SUPPORT
diff --git a/drivers/usb/cdns3/Makefile b/drivers/usb/cdns3/Makefile
index 61edb2f89276..48dfae75b5aa 100644
--- a/drivers/usb/cdns3/Makefile
+++ b/drivers/usb/cdns3/Makefile
@@ -24,6 +24,7 @@ endif
obj-$(CONFIG_USB_CDNS3_PCI_WRAP) += cdns3-pci-wrap.o
obj-$(CONFIG_USB_CDNS3_TI) += cdns3-ti.o
obj-$(CONFIG_USB_CDNS3_IMX) += cdns3-imx.o
+obj-$(CONFIG_USB_CDNS3_STARFIVE) += cdns3-starfive.o
cdnsp-udc-pci-y := cdnsp-pci.o
diff --git a/drivers/usb/cdns3/cdns3-gadget.c b/drivers/usb/cdns3/cdns3-gadget.c
index 1dcadef933e3..ea19253fd2d0 100644
--- a/drivers/usb/cdns3/cdns3-gadget.c
+++ b/drivers/usb/cdns3/cdns3-gadget.c
@@ -800,7 +800,8 @@ void cdns3_gadget_giveback(struct cdns3_endpoint *priv_ep,
if (request->status == -EINPROGRESS)
request->status = status;
- usb_gadget_unmap_request_by_dev(priv_dev->sysdev, request,
+ if (likely(!(priv_req->flags & REQUEST_UNALIGNED)))
+ usb_gadget_unmap_request_by_dev(priv_dev->sysdev, request,
priv_ep->dir);
if ((priv_req->flags & REQUEST_UNALIGNED) &&
@@ -808,10 +809,10 @@ void cdns3_gadget_giveback(struct cdns3_endpoint *priv_ep,
/* Make DMA buffer CPU accessible */
dma_sync_single_for_cpu(priv_dev->sysdev,
priv_req->aligned_buf->dma,
- priv_req->aligned_buf->size,
+ request->actual,
priv_req->aligned_buf->dir);
memcpy(request->buf, priv_req->aligned_buf->buf,
- request->length);
+ request->actual);
}
priv_req->flags &= ~(REQUEST_PENDING | REQUEST_UNALIGNED);
@@ -2543,10 +2544,12 @@ static int __cdns3_gadget_ep_queue(struct usb_ep *ep,
if (ret < 0)
return ret;
- ret = usb_gadget_map_request_by_dev(priv_dev->sysdev, request,
+ if (likely(!(priv_req->flags & REQUEST_UNALIGNED))) {
+ ret = usb_gadget_map_request_by_dev(priv_dev->sysdev, request,
usb_endpoint_dir_in(ep->desc));
- if (ret)
- return ret;
+ if (ret)
+ return ret;
+ }
list_add_tail(&request->list, &priv_ep->deferred_req_list);
diff --git a/drivers/usb/cdns3/cdns3-imx.c b/drivers/usb/cdns3/cdns3-imx.c
index 59860d1753fd..281de47e2a3b 100644
--- a/drivers/usb/cdns3/cdns3-imx.c
+++ b/drivers/usb/cdns3/cdns3-imx.c
@@ -105,11 +105,11 @@ static inline void cdns_imx_writel(struct cdns_imx *data, u32 offset, u32 value)
}
static const struct clk_bulk_data imx_cdns3_core_clks[] = {
- { .id = "usb3_lpm_clk" },
- { .id = "usb3_bus_clk" },
- { .id = "usb3_aclk" },
- { .id = "usb3_ipg_clk" },
- { .id = "usb3_core_pclk" },
+ { .id = "lpm" },
+ { .id = "bus" },
+ { .id = "aclk" },
+ { .id = "ipg" },
+ { .id = "core" },
};
static int cdns_imx_noncore_init(struct cdns_imx *data)
@@ -218,7 +218,7 @@ err:
return ret;
}
-static int cdns_imx_remove(struct platform_device *pdev)
+static void cdns_imx_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct cdns_imx *data = dev_get_drvdata(dev);
@@ -229,8 +229,6 @@ static int cdns_imx_remove(struct platform_device *pdev)
pm_runtime_disable(dev);
pm_runtime_put_noidle(dev);
platform_set_drvdata(pdev, NULL);
-
- return 0;
}
#ifdef CONFIG_PM
@@ -375,14 +373,22 @@ static inline bool cdns_imx_is_power_lost(struct cdns_imx *data)
return false;
}
+static int __maybe_unused cdns_imx_system_suspend(struct device *dev)
+{
+ pm_runtime_put_sync(dev);
+ return 0;
+}
+
static int __maybe_unused cdns_imx_system_resume(struct device *dev)
{
struct cdns_imx *data = dev_get_drvdata(dev);
int ret;
- ret = cdns_imx_resume(dev);
- if (ret)
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0) {
+ dev_err(dev, "Could not get runtime PM.\n");
return ret;
+ }
if (cdns_imx_is_power_lost(data)) {
dev_dbg(dev, "resume from power lost\n");
@@ -405,7 +411,7 @@ static int cdns_imx_platform_suspend(struct device *dev,
static const struct dev_pm_ops cdns_imx_pm_ops = {
SET_RUNTIME_PM_OPS(cdns_imx_suspend, cdns_imx_resume, NULL)
- SET_SYSTEM_SLEEP_PM_OPS(cdns_imx_suspend, cdns_imx_system_resume)
+ SET_SYSTEM_SLEEP_PM_OPS(cdns_imx_system_suspend, cdns_imx_system_resume)
};
static const struct of_device_id cdns_imx_of_match[] = {
@@ -416,7 +422,7 @@ MODULE_DEVICE_TABLE(of, cdns_imx_of_match);
static struct platform_driver cdns_imx_driver = {
.probe = cdns_imx_probe,
- .remove = cdns_imx_remove,
+ .remove_new = cdns_imx_remove,
.driver = {
.name = "cdns3-imx",
.of_match_table = cdns_imx_of_match,
diff --git a/drivers/usb/cdns3/cdns3-plat.c b/drivers/usb/cdns3/cdns3-plat.c
index 2bc5d094548b..884e2301237f 100644
--- a/drivers/usb/cdns3/cdns3-plat.c
+++ b/drivers/usb/cdns3/cdns3-plat.c
@@ -175,7 +175,7 @@ err_phy3_init:
*
* Returns 0 on success otherwise negative errno
*/
-static int cdns3_plat_remove(struct platform_device *pdev)
+static void cdns3_plat_remove(struct platform_device *pdev)
{
struct cdns *cdns = platform_get_drvdata(pdev);
struct device *dev = cdns->dev;
@@ -187,7 +187,6 @@ static int cdns3_plat_remove(struct platform_device *pdev)
set_phy_power_off(cdns);
phy_exit(cdns->usb2_phy);
phy_exit(cdns->usb3_phy);
- return 0;
}
#ifdef CONFIG_PM
@@ -320,7 +319,7 @@ MODULE_DEVICE_TABLE(of, of_cdns3_match);
static struct platform_driver cdns3_driver = {
.probe = cdns3_plat_probe,
- .remove = cdns3_plat_remove,
+ .remove_new = cdns3_plat_remove,
.driver = {
.name = "cdns-usb3",
.of_match_table = of_match_ptr(of_cdns3_match),
diff --git a/drivers/usb/cdns3/cdns3-starfive.c b/drivers/usb/cdns3/cdns3-starfive.c
new file mode 100644
index 000000000000..fc1f003b145d
--- /dev/null
+++ b/drivers/usb/cdns3/cdns3-starfive.c
@@ -0,0 +1,246 @@
+// SPDX-License-Identifier: GPL-2.0
+/**
+ * cdns3-starfive.c - StarFive specific Glue layer for Cadence USB Controller
+ *
+ * Copyright (C) 2023 StarFive Technology Co., Ltd.
+ *
+ * Author: Minda Chen <minda.chen@starfivetech.com>
+ */
+
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/mfd/syscon.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/of_platform.h>
+#include <linux/reset.h>
+#include <linux/regmap.h>
+#include <linux/usb/otg.h>
+#include "core.h"
+
+#define USB_STRAP_HOST BIT(17)
+#define USB_STRAP_DEVICE BIT(18)
+#define USB_STRAP_MASK GENMASK(18, 16)
+
+#define USB_SUSPENDM_HOST BIT(19)
+#define USB_SUSPENDM_MASK BIT(19)
+
+#define USB_MISC_CFG_MASK GENMASK(23, 20)
+#define USB_SUSPENDM_BYPS BIT(20)
+#define USB_PLL_EN BIT(22)
+#define USB_REFCLK_MODE BIT(23)
+
+struct cdns_starfive {
+ struct device *dev;
+ struct regmap *stg_syscon;
+ struct reset_control *resets;
+ struct clk_bulk_data *clks;
+ int num_clks;
+ u32 stg_usb_mode;
+};
+
+static void cdns_mode_init(struct platform_device *pdev,
+ struct cdns_starfive *data)
+{
+ enum usb_dr_mode mode;
+
+ regmap_update_bits(data->stg_syscon, data->stg_usb_mode,
+ USB_MISC_CFG_MASK,
+ USB_SUSPENDM_BYPS | USB_PLL_EN | USB_REFCLK_MODE);
+
+ /* dr mode setting */
+ mode = usb_get_dr_mode(&pdev->dev);
+
+ switch (mode) {
+ case USB_DR_MODE_HOST:
+ regmap_update_bits(data->stg_syscon,
+ data->stg_usb_mode,
+ USB_STRAP_MASK,
+ USB_STRAP_HOST);
+ regmap_update_bits(data->stg_syscon,
+ data->stg_usb_mode,
+ USB_SUSPENDM_MASK,
+ USB_SUSPENDM_HOST);
+ break;
+
+ case USB_DR_MODE_PERIPHERAL:
+ regmap_update_bits(data->stg_syscon, data->stg_usb_mode,
+ USB_STRAP_MASK, USB_STRAP_DEVICE);
+ regmap_update_bits(data->stg_syscon, data->stg_usb_mode,
+ USB_SUSPENDM_MASK, 0);
+ break;
+ default:
+ break;
+ }
+}
+
+static int cdns_clk_rst_init(struct cdns_starfive *data)
+{
+ int ret;
+
+ ret = clk_bulk_prepare_enable(data->num_clks, data->clks);
+ if (ret)
+ return dev_err_probe(data->dev, ret,
+ "failed to enable clocks\n");
+
+ ret = reset_control_deassert(data->resets);
+ if (ret) {
+ dev_err(data->dev, "failed to reset clocks\n");
+ goto err_clk_init;
+ }
+
+ return ret;
+
+err_clk_init:
+ clk_bulk_disable_unprepare(data->num_clks, data->clks);
+ return ret;
+}
+
+static void cdns_clk_rst_deinit(struct cdns_starfive *data)
+{
+ reset_control_assert(data->resets);
+ clk_bulk_disable_unprepare(data->num_clks, data->clks);
+}
+
+static int cdns_starfive_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct cdns_starfive *data;
+ unsigned int args;
+ int ret;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->dev = dev;
+
+ data->stg_syscon =
+ syscon_regmap_lookup_by_phandle_args(pdev->dev.of_node,
+ "starfive,stg-syscon", 1, &args);
+
+ if (IS_ERR(data->stg_syscon))
+ return dev_err_probe(dev, PTR_ERR(data->stg_syscon),
+ "Failed to parse starfive,stg-syscon\n");
+
+ data->stg_usb_mode = args;
+
+ data->num_clks = devm_clk_bulk_get_all(data->dev, &data->clks);
+ if (data->num_clks < 0)
+ return dev_err_probe(data->dev, -ENODEV,
+ "Failed to get clocks\n");
+
+ data->resets = devm_reset_control_array_get_exclusive(data->dev);
+ if (IS_ERR(data->resets))
+ return dev_err_probe(data->dev, PTR_ERR(data->resets),
+ "Failed to get resets");
+
+ cdns_mode_init(pdev, data);
+ ret = cdns_clk_rst_init(data);
+ if (ret)
+ return ret;
+
+ ret = of_platform_populate(dev->of_node, NULL, NULL, dev);
+ if (ret) {
+ dev_err(dev, "Failed to create children\n");
+ cdns_clk_rst_deinit(data);
+ return ret;
+ }
+
+ device_set_wakeup_capable(dev, true);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ platform_set_drvdata(pdev, data);
+
+ return 0;
+}
+
+static int cdns_starfive_remove_core(struct device *dev, void *c)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+
+ platform_device_unregister(pdev);
+
+ return 0;
+}
+
+static int cdns_starfive_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct cdns_starfive *data = dev_get_drvdata(dev);
+
+ pm_runtime_get_sync(dev);
+ device_for_each_child(dev, NULL, cdns_starfive_remove_core);
+
+ pm_runtime_disable(dev);
+ pm_runtime_put_noidle(dev);
+ cdns_clk_rst_deinit(data);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int cdns_starfive_runtime_resume(struct device *dev)
+{
+ struct cdns_starfive *data = dev_get_drvdata(dev);
+
+ return clk_bulk_prepare_enable(data->num_clks, data->clks);
+}
+
+static int cdns_starfive_runtime_suspend(struct device *dev)
+{
+ struct cdns_starfive *data = dev_get_drvdata(dev);
+
+ clk_bulk_disable_unprepare(data->num_clks, data->clks);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int cdns_starfive_resume(struct device *dev)
+{
+ struct cdns_starfive *data = dev_get_drvdata(dev);
+
+ return cdns_clk_rst_init(data);
+}
+
+static int cdns_starfive_suspend(struct device *dev)
+{
+ struct cdns_starfive *data = dev_get_drvdata(dev);
+
+ cdns_clk_rst_deinit(data);
+
+ return 0;
+}
+#endif
+#endif
+
+static const struct dev_pm_ops cdns_starfive_pm_ops = {
+ SET_RUNTIME_PM_OPS(cdns_starfive_runtime_suspend,
+ cdns_starfive_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(cdns_starfive_suspend, cdns_starfive_resume)
+};
+
+static const struct of_device_id cdns_starfive_of_match[] = {
+ { .compatible = "starfive,jh7110-usb", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, cdns_starfive_of_match);
+
+static struct platform_driver cdns_starfive_driver = {
+ .probe = cdns_starfive_probe,
+ .remove = cdns_starfive_remove,
+ .driver = {
+ .name = "cdns3-starfive",
+ .of_match_table = cdns_starfive_of_match,
+ .pm = &cdns_starfive_pm_ops,
+ },
+};
+module_platform_driver(cdns_starfive_driver);
+
+MODULE_ALIAS("platform:cdns3-starfive");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Cadence USB3 StarFive Glue Layer");
diff --git a/drivers/usb/cdns3/cdns3-ti.c b/drivers/usb/cdns3/cdns3-ti.c
index 07c318770362..81b9132e3aaa 100644
--- a/drivers/usb/cdns3/cdns3-ti.c
+++ b/drivers/usb/cdns3/cdns3-ti.c
@@ -199,7 +199,7 @@ static int cdns_ti_remove_core(struct device *dev, void *c)
return 0;
}
-static int cdns_ti_remove(struct platform_device *pdev)
+static void cdns_ti_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -208,8 +208,6 @@ static int cdns_ti_remove(struct platform_device *pdev)
pm_runtime_disable(dev);
platform_set_drvdata(pdev, NULL);
-
- return 0;
}
static const struct of_device_id cdns_ti_of_match[] = {
@@ -221,7 +219,7 @@ MODULE_DEVICE_TABLE(of, cdns_ti_of_match);
static struct platform_driver cdns_ti_driver = {
.probe = cdns_ti_probe,
- .remove = cdns_ti_remove,
+ .remove_new = cdns_ti_remove,
.driver = {
.name = "cdns3-ti",
.of_match_table = cdns_ti_of_match,
diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c
index 2855ac303001..336ef6dd8e7d 100644
--- a/drivers/usb/chipidea/ci_hdrc_imx.c
+++ b/drivers/usb/chipidea/ci_hdrc_imx.c
@@ -70,6 +70,10 @@ static const struct ci_hdrc_imx_platform_flag imx7ulp_usb_data = {
CI_HDRC_PMQOS,
};
+static const struct ci_hdrc_imx_platform_flag imx8ulp_usb_data = {
+ .flags = CI_HDRC_SUPPORTS_RUNTIME_PM,
+};
+
static const struct of_device_id ci_hdrc_imx_dt_ids[] = {
{ .compatible = "fsl,imx23-usb", .data = &imx23_usb_data},
{ .compatible = "fsl,imx28-usb", .data = &imx28_usb_data},
@@ -80,6 +84,7 @@ static const struct of_device_id ci_hdrc_imx_dt_ids[] = {
{ .compatible = "fsl,imx6ul-usb", .data = &imx6ul_usb_data},
{ .compatible = "fsl,imx7d-usb", .data = &imx7d_usb_data},
{ .compatible = "fsl,imx7ulp-usb", .data = &imx7ulp_usb_data},
+ { .compatible = "fsl,imx8ulp-usb", .data = &imx8ulp_usb_data},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, ci_hdrc_imx_dt_ids);
@@ -502,7 +507,7 @@ disable_hsic_regulator:
return ret;
}
-static int ci_hdrc_imx_remove(struct platform_device *pdev)
+static void ci_hdrc_imx_remove(struct platform_device *pdev)
{
struct ci_hdrc_imx_data *data = platform_get_drvdata(pdev);
@@ -522,8 +527,6 @@ static int ci_hdrc_imx_remove(struct platform_device *pdev)
if (data->hsic_pad_regulator)
regulator_disable(data->hsic_pad_regulator);
}
-
- return 0;
}
static void ci_hdrc_imx_shutdown(struct platform_device *pdev)
@@ -650,7 +653,7 @@ static const struct dev_pm_ops ci_hdrc_imx_pm_ops = {
};
static struct platform_driver ci_hdrc_imx_driver = {
.probe = ci_hdrc_imx_probe,
- .remove = ci_hdrc_imx_remove,
+ .remove_new = ci_hdrc_imx_remove,
.shutdown = ci_hdrc_imx_shutdown,
.driver = {
.name = "imx_usb",
diff --git a/drivers/usb/chipidea/ci_hdrc_msm.c b/drivers/usb/chipidea/ci_hdrc_msm.c
index 46105457e1ca..7b5b47ce8a02 100644
--- a/drivers/usb/chipidea/ci_hdrc_msm.c
+++ b/drivers/usb/chipidea/ci_hdrc_msm.c
@@ -274,7 +274,7 @@ err_iface:
return ret;
}
-static int ci_hdrc_msm_remove(struct platform_device *pdev)
+static void ci_hdrc_msm_remove(struct platform_device *pdev)
{
struct ci_hdrc_msm *ci = platform_get_drvdata(pdev);
@@ -282,8 +282,6 @@ static int ci_hdrc_msm_remove(struct platform_device *pdev)
ci_hdrc_remove_device(ci->ci);
clk_disable_unprepare(ci->iface_clk);
clk_disable_unprepare(ci->core_clk);
-
- return 0;
}
static const struct of_device_id msm_ci_dt_match[] = {
@@ -294,7 +292,7 @@ MODULE_DEVICE_TABLE(of, msm_ci_dt_match);
static struct platform_driver ci_hdrc_msm_driver = {
.probe = ci_hdrc_msm_probe,
- .remove = ci_hdrc_msm_remove,
+ .remove_new = ci_hdrc_msm_remove,
.driver = {
.name = "msm_hsusb",
.of_match_table = msm_ci_dt_match,
diff --git a/drivers/usb/chipidea/ci_hdrc_tegra.c b/drivers/usb/chipidea/ci_hdrc_tegra.c
index a72a9474afea..ca36d11a69ea 100644
--- a/drivers/usb/chipidea/ci_hdrc_tegra.c
+++ b/drivers/usb/chipidea/ci_hdrc_tegra.c
@@ -362,7 +362,7 @@ fail_power_off:
return err;
}
-static int tegra_usb_remove(struct platform_device *pdev)
+static void tegra_usb_remove(struct platform_device *pdev)
{
struct tegra_usb *usb = platform_get_drvdata(pdev);
@@ -371,8 +371,6 @@ static int tegra_usb_remove(struct platform_device *pdev)
pm_runtime_put_sync_suspend(&pdev->dev);
pm_runtime_force_suspend(&pdev->dev);
-
- return 0;
}
static int __maybe_unused tegra_usb_runtime_resume(struct device *dev)
@@ -410,7 +408,7 @@ static struct platform_driver tegra_usb_driver = {
.pm = &tegra_usb_pm,
},
.probe = tegra_usb_probe,
- .remove = tegra_usb_remove,
+ .remove_new = tegra_usb_remove,
};
module_platform_driver(tegra_usb_driver);
diff --git a/drivers/usb/chipidea/ci_hdrc_usb2.c b/drivers/usb/chipidea/ci_hdrc_usb2.c
index dc86b12060b5..1321ee67f3b8 100644
--- a/drivers/usb/chipidea/ci_hdrc_usb2.c
+++ b/drivers/usb/chipidea/ci_hdrc_usb2.c
@@ -106,20 +106,18 @@ clk_err:
return ret;
}
-static int ci_hdrc_usb2_remove(struct platform_device *pdev)
+static void ci_hdrc_usb2_remove(struct platform_device *pdev)
{
struct ci_hdrc_usb2_priv *priv = platform_get_drvdata(pdev);
pm_runtime_disable(&pdev->dev);
ci_hdrc_remove_device(priv->ci_pdev);
clk_disable_unprepare(priv->clk);
-
- return 0;
}
static struct platform_driver ci_hdrc_usb2_driver = {
.probe = ci_hdrc_usb2_probe,
- .remove = ci_hdrc_usb2_remove,
+ .remove_new = ci_hdrc_usb2_remove,
.driver = {
.name = "chipidea-usb2",
.of_match_table = of_match_ptr(ci_hdrc_usb2_of_match),
diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c
index 798cb077867a..51994d655b82 100644
--- a/drivers/usb/chipidea/core.c
+++ b/drivers/usb/chipidea/core.c
@@ -1227,7 +1227,7 @@ ulpi_exit:
return ret;
}
-static int ci_hdrc_remove(struct platform_device *pdev)
+static void ci_hdrc_remove(struct platform_device *pdev)
{
struct ci_hdrc *ci = platform_get_drvdata(pdev);
@@ -1245,8 +1245,6 @@ static int ci_hdrc_remove(struct platform_device *pdev)
ci_hdrc_enter_lpm(ci, true);
ci_usb_phy_exit(ci);
ci_ulpi_exit(ci);
-
- return 0;
}
#ifdef CONFIG_PM
@@ -1485,7 +1483,7 @@ static const struct dev_pm_ops ci_pm_ops = {
static struct platform_driver ci_hdrc_driver = {
.probe = ci_hdrc_probe,
- .remove = ci_hdrc_remove,
+ .remove_new = ci_hdrc_remove,
.driver = {
.name = "ci_hdrc",
.pm = &ci_pm_ops,
diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c
index c57c1a71a513..9ee9621e2ccc 100644
--- a/drivers/usb/chipidea/usbmisc_imx.c
+++ b/drivers/usb/chipidea/usbmisc_imx.c
@@ -113,7 +113,6 @@
#define MX7D_USBNC_USB_CTRL2_DP_DM_MASK (BIT(12) | BIT(13) | \
BIT(14) | BIT(15))
-#define MX7D_USB_OTG_PHY_CFG1 0x30
#define MX7D_USB_OTG_PHY_CFG2_CHRG_CHRGSEL BIT(0)
#define MX7D_USB_OTG_PHY_CFG2_CHRG_VDATDETENB0 BIT(1)
#define MX7D_USB_OTG_PHY_CFG2_CHRG_VDATSRCENB0 BIT(2)
@@ -135,7 +134,7 @@
#define TXVREFTUNE0_MASK (0xf << 20)
#define MX6_USB_OTG_WAKEUP_BITS (MX6_BM_WAKEUP_ENABLE | MX6_BM_VBUS_WAKEUP | \
- MX6_BM_ID_WAKEUP)
+ MX6_BM_ID_WAKEUP | MX6SX_BM_DPDM_WAKEUP_EN)
struct usbmisc_ops {
/* It's called once when probe a usb device */
@@ -152,6 +151,7 @@ struct usbmisc_ops {
int (*charger_detection)(struct imx_usbmisc_data *data);
/* It's called when system resume from usb power lost */
int (*power_lost_check)(struct imx_usbmisc_data *data);
+ void (*vbus_comparator_on)(struct imx_usbmisc_data *data, bool on);
};
struct imx_usbmisc {
@@ -875,6 +875,33 @@ static int imx7d_charger_detection(struct imx_usbmisc_data *data)
return ret;
}
+static void usbmisc_imx7d_vbus_comparator_on(struct imx_usbmisc_data *data,
+ bool on)
+{
+ unsigned long flags;
+ struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
+ u32 val;
+
+ if (data->hsic)
+ return;
+
+ spin_lock_irqsave(&usbmisc->lock, flags);
+ /*
+ * Disable VBUS valid comparator when in suspend mode,
+ * when OTG is disabled and DRVVBUS0 is asserted case
+ * the Bandgap circuitry and VBUS Valid comparator are
+ * still powered, even in Suspend or Sleep mode.
+ */
+ val = readl(usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
+ if (on)
+ val |= MX7D_USB_OTG_PHY_CFG2_DRVVBUS0;
+ else
+ val &= ~MX7D_USB_OTG_PHY_CFG2_DRVVBUS0;
+
+ writel(val, usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
+ spin_unlock_irqrestore(&usbmisc->lock, flags);
+}
+
static int usbmisc_imx7ulp_init(struct imx_usbmisc_data *data)
{
struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
@@ -1018,6 +1045,7 @@ static const struct usbmisc_ops imx7d_usbmisc_ops = {
.set_wakeup = usbmisc_imx7d_set_wakeup,
.charger_detection = imx7d_charger_detection,
.power_lost_check = usbmisc_imx7d_power_lost_check,
+ .vbus_comparator_on = usbmisc_imx7d_vbus_comparator_on,
};
static const struct usbmisc_ops imx7ulp_usbmisc_ops = {
@@ -1132,6 +1160,9 @@ int imx_usbmisc_suspend(struct imx_usbmisc_data *data, bool wakeup)
usbmisc = dev_get_drvdata(data->dev);
+ if (usbmisc->ops->vbus_comparator_on)
+ usbmisc->ops->vbus_comparator_on(data, false);
+
if (wakeup && usbmisc->ops->set_wakeup)
ret = usbmisc->ops->set_wakeup(data, true);
if (ret) {
@@ -1185,6 +1216,9 @@ int imx_usbmisc_resume(struct imx_usbmisc_data *data, bool wakeup)
goto hsic_set_clk_fail;
}
+ if (usbmisc->ops->vbus_comparator_on)
+ usbmisc->ops->vbus_comparator_on(data, true);
+
return 0;
hsic_set_clk_fail:
diff --git a/drivers/usb/common/usb-conn-gpio.c b/drivers/usb/common/usb-conn-gpio.c
index e20874caba36..766005d20bae 100644
--- a/drivers/usb/common/usb-conn-gpio.c
+++ b/drivers/usb/common/usb-conn-gpio.c
@@ -267,7 +267,7 @@ put_role_sw:
return ret;
}
-static int usb_conn_remove(struct platform_device *pdev)
+static void usb_conn_remove(struct platform_device *pdev)
{
struct usb_conn_info *info = platform_get_drvdata(pdev);
@@ -277,8 +277,6 @@ static int usb_conn_remove(struct platform_device *pdev)
regulator_disable(info->vbus);
usb_role_switch_put(info->role_sw);
-
- return 0;
}
static int __maybe_unused usb_conn_suspend(struct device *dev)
@@ -338,7 +336,7 @@ MODULE_DEVICE_TABLE(of, usb_conn_dt_match);
static struct platform_driver usb_conn_driver = {
.probe = usb_conn_probe,
- .remove = usb_conn_remove,
+ .remove_new = usb_conn_remove,
.driver = {
.name = "usb-conn-gpio",
.pm = &usb_conn_pm_ops,
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index fcf68818e999..1a16a8bdea60 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -746,6 +746,7 @@ static int driver_resume(struct usb_interface *intf)
return 0;
}
+#ifdef CONFIG_PM
/* The following routines apply to the entire device, not interfaces */
void usbfs_notify_suspend(struct usb_device *udev)
{
@@ -764,6 +765,7 @@ void usbfs_notify_resume(struct usb_device *udev)
}
mutex_unlock(&usbfs_mutex);
}
+#endif
struct usb_driver usbfs_driver = {
.name = "usbfs",
@@ -2640,21 +2642,21 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd,
snoop(&dev->dev, "%s: CONTROL\n", __func__);
ret = proc_control(ps, p);
if (ret >= 0)
- inode->i_mtime = current_time(inode);
+ inode->i_mtime = inode->i_ctime = current_time(inode);
break;
case USBDEVFS_BULK:
snoop(&dev->dev, "%s: BULK\n", __func__);
ret = proc_bulk(ps, p);
if (ret >= 0)
- inode->i_mtime = current_time(inode);
+ inode->i_mtime = inode->i_ctime = current_time(inode);
break;
case USBDEVFS_RESETEP:
snoop(&dev->dev, "%s: RESETEP\n", __func__);
ret = proc_resetep(ps, p);
if (ret >= 0)
- inode->i_mtime = current_time(inode);
+ inode->i_mtime = inode->i_ctime = current_time(inode);
break;
case USBDEVFS_RESET:
@@ -2666,7 +2668,7 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd,
snoop(&dev->dev, "%s: CLEAR_HALT\n", __func__);
ret = proc_clearhalt(ps, p);
if (ret >= 0)
- inode->i_mtime = current_time(inode);
+ inode->i_mtime = inode->i_ctime = current_time(inode);
break;
case USBDEVFS_GETDRIVER:
@@ -2693,7 +2695,7 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd,
snoop(&dev->dev, "%s: SUBMITURB\n", __func__);
ret = proc_submiturb(ps, p);
if (ret >= 0)
- inode->i_mtime = current_time(inode);
+ inode->i_mtime = inode->i_ctime = current_time(inode);
break;
#ifdef CONFIG_COMPAT
@@ -2701,14 +2703,14 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd,
snoop(&dev->dev, "%s: CONTROL32\n", __func__);
ret = proc_control_compat(ps, p);
if (ret >= 0)
- inode->i_mtime = current_time(inode);
+ inode->i_mtime = inode->i_ctime = current_time(inode);
break;
case USBDEVFS_BULK32:
snoop(&dev->dev, "%s: BULK32\n", __func__);
ret = proc_bulk_compat(ps, p);
if (ret >= 0)
- inode->i_mtime = current_time(inode);
+ inode->i_mtime = inode->i_ctime = current_time(inode);
break;
case USBDEVFS_DISCSIGNAL32:
@@ -2720,7 +2722,7 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd,
snoop(&dev->dev, "%s: SUBMITURB32\n", __func__);
ret = proc_submiturb_compat(ps, p);
if (ret >= 0)
- inode->i_mtime = current_time(inode);
+ inode->i_mtime = inode->i_ctime = current_time(inode);
break;
case USBDEVFS_IOCTL32:
diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c
index ab2f3737764e..990280688b25 100644
--- a/drivers/usb/core/hcd-pci.c
+++ b/drivers/usb/core/hcd-pci.c
@@ -415,12 +415,15 @@ static int check_root_hub_suspended(struct device *dev)
return 0;
}
-static int suspend_common(struct device *dev, bool do_wakeup)
+static int suspend_common(struct device *dev, pm_message_t msg)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
struct usb_hcd *hcd = pci_get_drvdata(pci_dev);
+ bool do_wakeup;
int retval;
+ do_wakeup = PMSG_IS_AUTO(msg) ? true : device_may_wakeup(dev);
+
/* Root hub suspend should have stopped all downstream traffic,
* and all bus master traffic. And done so for both the interface
* and the stub usb_device (which we check here). But maybe it
@@ -447,7 +450,7 @@ static int suspend_common(struct device *dev, bool do_wakeup)
(retval == 0 && do_wakeup && hcd->shared_hcd &&
HCD_WAKEUP_PENDING(hcd->shared_hcd))) {
if (hcd->driver->pci_resume)
- hcd->driver->pci_resume(hcd, false);
+ hcd->driver->pci_resume(hcd, msg);
retval = -EBUSY;
}
if (retval)
@@ -470,7 +473,7 @@ static int suspend_common(struct device *dev, bool do_wakeup)
return retval;
}
-static int resume_common(struct device *dev, int event)
+static int resume_common(struct device *dev, pm_message_t msg)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
struct usb_hcd *hcd = pci_get_drvdata(pci_dev);
@@ -498,12 +501,11 @@ static int resume_common(struct device *dev, int event)
* No locking is needed because PCI controller drivers do not
* get unbound during system resume.
*/
- if (pci_dev->class == CL_EHCI && event != PM_EVENT_AUTO_RESUME)
+ if (pci_dev->class == CL_EHCI && msg.event != PM_EVENT_AUTO_RESUME)
for_each_companion(pci_dev, hcd,
ehci_wait_for_companions);
- retval = hcd->driver->pci_resume(hcd,
- event == PM_EVENT_RESTORE);
+ retval = hcd->driver->pci_resume(hcd, msg);
if (retval) {
dev_err(dev, "PCI post-resume error %d!\n", retval);
usb_hc_died(hcd);
@@ -516,7 +518,7 @@ static int resume_common(struct device *dev, int event)
static int hcd_pci_suspend(struct device *dev)
{
- return suspend_common(dev, device_may_wakeup(dev));
+ return suspend_common(dev, PMSG_SUSPEND);
}
static int hcd_pci_suspend_noirq(struct device *dev)
@@ -577,12 +579,12 @@ static int hcd_pci_resume_noirq(struct device *dev)
static int hcd_pci_resume(struct device *dev)
{
- return resume_common(dev, PM_EVENT_RESUME);
+ return resume_common(dev, PMSG_RESUME);
}
static int hcd_pci_restore(struct device *dev)
{
- return resume_common(dev, PM_EVENT_RESTORE);
+ return resume_common(dev, PMSG_RESTORE);
}
#else
@@ -600,7 +602,7 @@ static int hcd_pci_runtime_suspend(struct device *dev)
{
int retval;
- retval = suspend_common(dev, true);
+ retval = suspend_common(dev, PMSG_AUTO_SUSPEND);
if (retval == 0)
powermac_set_asic(to_pci_dev(dev), 0);
dev_dbg(dev, "hcd_pci_runtime_suspend: %d\n", retval);
@@ -612,7 +614,7 @@ static int hcd_pci_runtime_resume(struct device *dev)
int retval;
powermac_set_asic(to_pci_dev(dev), 1);
- retval = resume_common(dev, PM_EVENT_AUTO_RESUME);
+ retval = resume_common(dev, PMSG_AUTO_RESUME);
dev_dbg(dev, "hcd_pci_runtime_resume: %d\n", retval);
return retval;
}
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 97a0f8faea6e..a739403a9e45 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -2018,6 +2018,19 @@ bool usb_device_is_owned(struct usb_device *udev)
return !!hub->ports[udev->portnum - 1]->port_owner;
}
+static void update_port_device_state(struct usb_device *udev)
+{
+ struct usb_hub *hub;
+ struct usb_port *port_dev;
+
+ if (udev->parent) {
+ hub = usb_hub_to_struct_hub(udev->parent);
+ port_dev = hub->ports[udev->portnum - 1];
+ WRITE_ONCE(port_dev->state, udev->state);
+ sysfs_notify_dirent(port_dev->state_kn);
+ }
+}
+
static void recursively_mark_NOTATTACHED(struct usb_device *udev)
{
struct usb_hub *hub = usb_hub_to_struct_hub(udev);
@@ -2030,6 +2043,7 @@ static void recursively_mark_NOTATTACHED(struct usb_device *udev)
if (udev->state == USB_STATE_SUSPENDED)
udev->active_duration -= jiffies;
udev->state = USB_STATE_NOTATTACHED;
+ update_port_device_state(udev);
}
/**
@@ -2086,6 +2100,7 @@ void usb_set_device_state(struct usb_device *udev,
udev->state != USB_STATE_SUSPENDED)
udev->active_duration += jiffies;
udev->state = new_state;
+ update_port_device_state(udev);
} else
recursively_mark_NOTATTACHED(udev);
spin_unlock_irqrestore(&device_state_lock, flags);
diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h
index e23833562e4f..37897afd1b64 100644
--- a/drivers/usb/core/hub.h
+++ b/drivers/usb/core/hub.h
@@ -84,6 +84,8 @@ struct usb_hub {
* @peer: related usb2 and usb3 ports (share the same connector)
* @req: default pm qos request for hubs without port power control
* @connect_type: port's connect type
+ * @state: device state of the usb device attached to the port
+ * @state_kn: kernfs_node of the sysfs attribute that accesses @state
* @location: opaque representation of platform connector location
* @status_lock: synchronize port_event() vs usb_port_{suspend|resume}
* @portnum: port index num based one
@@ -100,6 +102,8 @@ struct usb_port {
struct usb_port *peer;
struct dev_pm_qos_request *req;
enum usb_port_connect_type connect_type;
+ enum usb_device_state state;
+ struct kernfs_node *state_kn;
usb_port_location_t location;
struct mutex status_lock;
u32 over_current_count;
diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c
index 06a8f1f84f6f..77be0dc28da9 100644
--- a/drivers/usb/core/port.c
+++ b/drivers/usb/core/port.c
@@ -160,6 +160,16 @@ static ssize_t connect_type_show(struct device *dev,
}
static DEVICE_ATTR_RO(connect_type);
+static ssize_t state_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct usb_port *port_dev = to_usb_port(dev);
+ enum usb_device_state state = READ_ONCE(port_dev->state);
+
+ return sysfs_emit(buf, "%s\n", usb_state_string(state));
+}
+static DEVICE_ATTR_RO(state);
+
static ssize_t over_current_count_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -259,6 +269,7 @@ static DEVICE_ATTR_RW(usb3_lpm_permit);
static struct attribute *port_dev_attrs[] = {
&dev_attr_connect_type.attr,
+ &dev_attr_state.attr,
&dev_attr_location.attr,
&dev_attr_quirks.attr,
&dev_attr_over_current_count.attr,
@@ -705,19 +716,24 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1)
return retval;
}
+ port_dev->state_kn = sysfs_get_dirent(port_dev->dev.kobj.sd, "state");
+ if (!port_dev->state_kn) {
+ dev_err(&port_dev->dev, "failed to sysfs_get_dirent 'state'\n");
+ retval = -ENODEV;
+ goto err_unregister;
+ }
+
/* Set default policy of port-poweroff disabled. */
retval = dev_pm_qos_add_request(&port_dev->dev, port_dev->req,
DEV_PM_QOS_FLAGS, PM_QOS_FLAG_NO_POWER_OFF);
if (retval < 0) {
- device_unregister(&port_dev->dev);
- return retval;
+ goto err_put_kn;
}
retval = component_add(&port_dev->dev, &connector_ops);
if (retval) {
dev_warn(&port_dev->dev, "failed to add component\n");
- device_unregister(&port_dev->dev);
- return retval;
+ goto err_put_kn;
}
find_and_link_peer(hub, port1);
@@ -754,6 +770,13 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1)
port_dev->req = NULL;
}
return 0;
+
+err_put_kn:
+ sysfs_put(port_dev->state_kn);
+err_unregister:
+ device_unregister(&port_dev->dev);
+
+ return retval;
}
void usb_hub_remove_port_device(struct usb_hub *hub, int port1)
@@ -765,5 +788,6 @@ void usb_hub_remove_port_device(struct usb_hub *hub, int port1)
if (peer)
unlink_peers(port_dev, peer);
component_del(&port_dev->dev, &connector_ops);
+ sysfs_put(port_dev->state_kn);
device_unregister(&port_dev->dev);
}
diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c
index 21d16533bd2f..4c7c3dd15f9b 100644
--- a/drivers/usb/dwc2/params.c
+++ b/drivers/usb/dwc2/params.c
@@ -161,6 +161,25 @@ static void dwc2_set_amlogic_g12a_params(struct dwc2_hsotg *hsotg)
p->hird_threshold_en = false;
}
+static void dwc2_set_amlogic_a1_params(struct dwc2_hsotg *hsotg)
+{
+ struct dwc2_core_params *p = &hsotg->params;
+
+ p->otg_caps.hnp_support = false;
+ p->otg_caps.srp_support = false;
+ p->speed = DWC2_SPEED_PARAM_HIGH;
+ p->host_rx_fifo_size = 192;
+ p->host_nperio_tx_fifo_size = 128;
+ p->host_perio_tx_fifo_size = 128;
+ p->phy_type = DWC2_PHY_TYPE_PARAM_UTMI;
+ p->phy_utmi_width = 8;
+ p->ahbcfg = GAHBCFG_HBSTLEN_INCR8 << GAHBCFG_HBSTLEN_SHIFT;
+ p->lpm = false;
+ p->lpm_clock_gating = false;
+ p->besl = false;
+ p->hird_threshold_en = false;
+}
+
static void dwc2_set_amcc_params(struct dwc2_hsotg *hsotg)
{
struct dwc2_core_params *p = &hsotg->params;
@@ -258,6 +277,8 @@ const struct of_device_id dwc2_of_match_table[] = {
.data = dwc2_set_amlogic_params },
{ .compatible = "amlogic,meson-g12a-usb",
.data = dwc2_set_amlogic_g12a_params },
+ { .compatible = "amlogic,meson-a1-usb",
+ .data = dwc2_set_amlogic_a1_params },
{ .compatible = "amcc,dwc-otg", .data = dwc2_set_amcc_params },
{ .compatible = "apm,apm82181-dwc-otg", .data = dwc2_set_amcc_params },
{ .compatible = "st,stm32f4x9-fsotg",
diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c
index 5aee284018c0..0a806f80217e 100644
--- a/drivers/usb/dwc2/platform.c
+++ b/drivers/usb/dwc2/platform.c
@@ -203,6 +203,11 @@ int dwc2_lowlevel_hw_disable(struct dwc2_hsotg *hsotg)
return ret;
}
+static void dwc2_reset_control_assert(void *data)
+{
+ reset_control_assert(data);
+}
+
static int dwc2_lowlevel_hw_init(struct dwc2_hsotg *hsotg)
{
int i, ret;
@@ -213,6 +218,10 @@ static int dwc2_lowlevel_hw_init(struct dwc2_hsotg *hsotg)
"error getting reset control\n");
reset_control_deassert(hsotg->reset);
+ ret = devm_add_action_or_reset(hsotg->dev, dwc2_reset_control_assert,
+ hsotg->reset);
+ if (ret)
+ return ret;
hsotg->reset_ecc = devm_reset_control_get_optional(hsotg->dev, "dwc2-ecc");
if (IS_ERR(hsotg->reset_ecc))
@@ -220,6 +229,10 @@ static int dwc2_lowlevel_hw_init(struct dwc2_hsotg *hsotg)
"error getting reset control for ecc\n");
reset_control_deassert(hsotg->reset_ecc);
+ ret = devm_add_action_or_reset(hsotg->dev, dwc2_reset_control_assert,
+ hsotg->reset_ecc);
+ if (ret)
+ return ret;
/*
* Attempt to find a generic PHY, then look for an old style
@@ -288,7 +301,7 @@ static int dwc2_lowlevel_hw_init(struct dwc2_hsotg *hsotg)
* stops device processing. Any resources used on behalf of this device are
* freed.
*/
-static int dwc2_driver_remove(struct platform_device *dev)
+static void dwc2_driver_remove(struct platform_device *dev)
{
struct dwc2_hsotg *hsotg = platform_get_drvdata(dev);
struct dwc2_gregs_backup *gr;
@@ -338,11 +351,6 @@ static int dwc2_driver_remove(struct platform_device *dev)
if (hsotg->ll_hw_enabled)
dwc2_lowlevel_hw_disable(hsotg);
-
- reset_control_assert(hsotg->reset);
- reset_control_assert(hsotg->reset_ecc);
-
- return 0;
}
/**
@@ -746,7 +754,7 @@ static struct platform_driver dwc2_platform_driver = {
.pm = &dwc2_dev_pm_ops,
},
.probe = dwc2_driver_probe,
- .remove = dwc2_driver_remove,
+ .remove_new = dwc2_driver_remove,
.shutdown = dwc2_driver_shutdown,
};
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index d68958e151a7..f6689b731718 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -1800,6 +1800,17 @@ static int dwc3_probe(struct platform_device *pdev)
dwc_res = *res;
dwc_res.start += DWC3_GLOBALS_REGS_START;
+ if (dev->of_node) {
+ struct device_node *parent = of_get_parent(dev->of_node);
+
+ if (of_device_is_compatible(parent, "realtek,rtd-dwc3")) {
+ dwc_res.start -= DWC3_GLOBALS_REGS_START;
+ dwc_res.start += DWC3_RTK_RTD_GLOBALS_REGS_START;
+ }
+
+ of_node_put(parent);
+ }
+
regs = devm_ioremap_resource(dev, &dwc_res);
if (IS_ERR(regs))
return PTR_ERR(regs);
@@ -1913,7 +1924,7 @@ err_put_psy:
return ret;
}
-static int dwc3_remove(struct platform_device *pdev)
+static void dwc3_remove(struct platform_device *pdev)
{
struct dwc3 *dwc = platform_get_drvdata(pdev);
@@ -1940,8 +1951,6 @@ static int dwc3_remove(struct platform_device *pdev)
if (dwc->usb_psy)
power_supply_put(dwc->usb_psy);
-
- return 0;
}
#ifdef CONFIG_PM
@@ -2252,7 +2261,7 @@ MODULE_DEVICE_TABLE(acpi, dwc3_acpi_match);
static struct platform_driver dwc3_driver = {
.probe = dwc3_probe,
- .remove = dwc3_remove,
+ .remove_new = dwc3_remove,
.driver = {
.name = "dwc3",
.of_match_table = of_match_ptr(of_dwc3_match),
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 1f043c31a096..8b1295e4dcdd 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -84,6 +84,8 @@
#define DWC3_OTG_REGS_START 0xcc00
#define DWC3_OTG_REGS_END 0xccff
+#define DWC3_RTK_RTD_GLOBALS_REGS_START 0x8100
+
/* Global Registers */
#define DWC3_GSBUSCFG0 0xc100
#define DWC3_GSBUSCFG1 0xc104
diff --git a/drivers/usb/dwc3/dwc3-am62.c b/drivers/usb/dwc3/dwc3-am62.c
index cda9458c809b..1755f2f848c5 100644
--- a/drivers/usb/dwc3/dwc3-am62.c
+++ b/drivers/usb/dwc3/dwc3-am62.c
@@ -275,7 +275,7 @@ static int dwc3_ti_remove_core(struct device *dev, void *c)
return 0;
}
-static int dwc3_ti_remove(struct platform_device *pdev)
+static void dwc3_ti_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct dwc3_data *data = platform_get_drvdata(pdev);
@@ -294,7 +294,6 @@ static int dwc3_ti_remove(struct platform_device *pdev)
pm_runtime_set_suspended(dev);
platform_set_drvdata(pdev, NULL);
- return 0;
}
#ifdef CONFIG_PM
@@ -362,7 +361,7 @@ MODULE_DEVICE_TABLE(of, dwc3_ti_of_match);
static struct platform_driver dwc3_ti_driver = {
.probe = dwc3_ti_probe,
- .remove = dwc3_ti_remove,
+ .remove_new = dwc3_ti_remove,
.driver = {
.name = "dwc3-am62",
.pm = DEV_PM_OPS,
diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c
index 4be6a873bd07..f882dd647340 100644
--- a/drivers/usb/dwc3/dwc3-exynos.c
+++ b/drivers/usb/dwc3/dwc3-exynos.c
@@ -128,7 +128,7 @@ vdd33_err:
return ret;
}
-static int dwc3_exynos_remove(struct platform_device *pdev)
+static void dwc3_exynos_remove(struct platform_device *pdev)
{
struct dwc3_exynos *exynos = platform_get_drvdata(pdev);
int i;
@@ -143,8 +143,6 @@ static int dwc3_exynos_remove(struct platform_device *pdev)
regulator_disable(exynos->vdd33);
regulator_disable(exynos->vdd10);
-
- return 0;
}
static const struct dwc3_exynos_driverdata exynos5250_drvdata = {
@@ -234,7 +232,7 @@ static const struct dev_pm_ops dwc3_exynos_dev_pm_ops = {
static struct platform_driver dwc3_exynos_driver = {
.probe = dwc3_exynos_probe,
- .remove = dwc3_exynos_remove,
+ .remove_new = dwc3_exynos_remove,
.driver = {
.name = "exynos-dwc3",
.of_match_table = exynos_dwc3_match,
diff --git a/drivers/usb/dwc3/dwc3-imx8mp.c b/drivers/usb/dwc3/dwc3-imx8mp.c
index 174f07614318..8b9a3bb587bf 100644
--- a/drivers/usb/dwc3/dwc3-imx8mp.c
+++ b/drivers/usb/dwc3/dwc3-imx8mp.c
@@ -266,7 +266,7 @@ disable_hsio_clk:
return err;
}
-static int dwc3_imx8mp_remove(struct platform_device *pdev)
+static void dwc3_imx8mp_remove(struct platform_device *pdev)
{
struct dwc3_imx8mp *dwc3_imx = platform_get_drvdata(pdev);
struct device *dev = &pdev->dev;
@@ -280,8 +280,6 @@ static int dwc3_imx8mp_remove(struct platform_device *pdev)
pm_runtime_disable(dev);
pm_runtime_put_noidle(dev);
platform_set_drvdata(pdev, NULL);
-
- return 0;
}
static int __maybe_unused dwc3_imx8mp_suspend(struct dwc3_imx8mp *dwc3_imx,
@@ -411,7 +409,7 @@ MODULE_DEVICE_TABLE(of, dwc3_imx8mp_of_match);
static struct platform_driver dwc3_imx8mp_driver = {
.probe = dwc3_imx8mp_probe,
- .remove = dwc3_imx8mp_remove,
+ .remove_new = dwc3_imx8mp_remove,
.driver = {
.name = "imx8mp-dwc3",
.pm = &dwc3_imx8mp_dev_pm_ops,
diff --git a/drivers/usb/dwc3/dwc3-keystone.c b/drivers/usb/dwc3/dwc3-keystone.c
index 1317959294e6..0a09aedc2573 100644
--- a/drivers/usb/dwc3/dwc3-keystone.c
+++ b/drivers/usb/dwc3/dwc3-keystone.c
@@ -181,7 +181,7 @@ static int kdwc3_remove_core(struct device *dev, void *c)
return 0;
}
-static int kdwc3_remove(struct platform_device *pdev)
+static void kdwc3_remove(struct platform_device *pdev)
{
struct dwc3_keystone *kdwc = platform_get_drvdata(pdev);
struct device_node *node = pdev->dev.of_node;
@@ -198,8 +198,6 @@ static int kdwc3_remove(struct platform_device *pdev)
phy_pm_runtime_put_sync(kdwc->usb3_phy);
platform_set_drvdata(pdev, NULL);
-
- return 0;
}
static const struct of_device_id kdwc3_of_match[] = {
@@ -211,7 +209,7 @@ MODULE_DEVICE_TABLE(of, kdwc3_of_match);
static struct platform_driver kdwc3_driver = {
.probe = kdwc3_probe,
- .remove = kdwc3_remove,
+ .remove_new = kdwc3_remove,
.driver = {
.name = "keystone-dwc3",
.of_match_table = kdwc3_of_match,
diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c
index b282ad0e69c6..e99c7489dba0 100644
--- a/drivers/usb/dwc3/dwc3-meson-g12a.c
+++ b/drivers/usb/dwc3/dwc3-meson-g12a.c
@@ -140,7 +140,6 @@ static const char * const meson_a1_phy_names[] = {
struct dwc3_meson_g12a;
struct dwc3_meson_g12a_drvdata {
- bool otg_switch_supported;
bool otg_phy_host_port_disable;
struct clk_bulk_data *clks;
int num_clks;
@@ -189,7 +188,6 @@ static int dwc3_meson_gxl_usb_post_init(struct dwc3_meson_g12a *priv);
*/
static const struct dwc3_meson_g12a_drvdata gxl_drvdata = {
- .otg_switch_supported = true,
.otg_phy_host_port_disable = true,
.clks = meson_gxl_clocks,
.num_clks = ARRAY_SIZE(meson_g12a_clocks),
@@ -203,7 +201,6 @@ static const struct dwc3_meson_g12a_drvdata gxl_drvdata = {
};
static const struct dwc3_meson_g12a_drvdata gxm_drvdata = {
- .otg_switch_supported = true,
.otg_phy_host_port_disable = true,
.clks = meson_gxl_clocks,
.num_clks = ARRAY_SIZE(meson_g12a_clocks),
@@ -217,7 +214,6 @@ static const struct dwc3_meson_g12a_drvdata gxm_drvdata = {
};
static const struct dwc3_meson_g12a_drvdata axg_drvdata = {
- .otg_switch_supported = true,
.clks = meson_gxl_clocks,
.num_clks = ARRAY_SIZE(meson_gxl_clocks),
.phy_names = meson_a1_phy_names,
@@ -230,7 +226,6 @@ static const struct dwc3_meson_g12a_drvdata axg_drvdata = {
};
static const struct dwc3_meson_g12a_drvdata g12a_drvdata = {
- .otg_switch_supported = true,
.clks = meson_g12a_clocks,
.num_clks = ARRAY_SIZE(meson_g12a_clocks),
.phy_names = meson_g12a_phy_names,
@@ -242,7 +237,6 @@ static const struct dwc3_meson_g12a_drvdata g12a_drvdata = {
};
static const struct dwc3_meson_g12a_drvdata a1_drvdata = {
- .otg_switch_supported = false,
.clks = meson_a1_clocks,
.num_clks = ARRAY_SIZE(meson_a1_clocks),
.phy_names = meson_a1_phy_names,
@@ -307,7 +301,7 @@ static int dwc3_meson_g12a_usb2_init_phy(struct dwc3_meson_g12a *priv, int i,
U2P_R0_POWER_ON_RESET,
U2P_R0_POWER_ON_RESET);
- if (priv->drvdata->otg_switch_supported && i == USB2_OTG_PHY) {
+ if (i == USB2_OTG_PHY) {
regmap_update_bits(priv->u2p_regmap[i], U2P_R0,
U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS,
U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS);
@@ -490,7 +484,7 @@ static int dwc3_meson_g12a_otg_mode_set(struct dwc3_meson_g12a *priv,
{
int ret;
- if (!priv->drvdata->otg_switch_supported || !priv->phys[USB2_OTG_PHY])
+ if (!priv->phys[USB2_OTG_PHY])
return -EINVAL;
if (mode == PHY_MODE_USB_HOST)
@@ -589,9 +583,6 @@ static int dwc3_meson_g12a_otg_init(struct platform_device *pdev,
int ret, irq;
struct device *dev = &pdev->dev;
- if (!priv->drvdata->otg_switch_supported)
- return 0;
-
if (priv->otg_mode == USB_DR_MODE_OTG) {
/* Ack irq before registering */
regmap_update_bits(priv->usb_glue_regmap, USB_R5,
@@ -805,7 +796,7 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev)
ret = dwc3_meson_g12a_otg_init(pdev, priv);
if (ret)
- goto err_phys_power;
+ goto err_plat_depopulate;
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
@@ -813,6 +804,9 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev)
return 0;
+err_plat_depopulate:
+ of_platform_depopulate(dev);
+
err_phys_power:
for (i = 0 ; i < PHY_COUNT ; ++i)
phy_power_off(priv->phys[i]);
@@ -835,14 +829,13 @@ err_disable_clks:
return ret;
}
-static int dwc3_meson_g12a_remove(struct platform_device *pdev)
+static void dwc3_meson_g12a_remove(struct platform_device *pdev)
{
struct dwc3_meson_g12a *priv = platform_get_drvdata(pdev);
struct device *dev = &pdev->dev;
int i;
- if (priv->drvdata->otg_switch_supported)
- usb_role_switch_unregister(priv->role_switch);
+ usb_role_switch_unregister(priv->role_switch);
of_platform_depopulate(dev);
@@ -859,8 +852,6 @@ static int dwc3_meson_g12a_remove(struct platform_device *pdev)
clk_bulk_disable_unprepare(priv->drvdata->num_clks,
priv->drvdata->clks);
-
- return 0;
}
static int __maybe_unused dwc3_meson_g12a_runtime_suspend(struct device *dev)
@@ -971,7 +962,7 @@ MODULE_DEVICE_TABLE(of, dwc3_meson_g12a_match);
static struct platform_driver dwc3_meson_g12a_driver = {
.probe = dwc3_meson_g12a_probe,
- .remove = dwc3_meson_g12a_remove,
+ .remove_new = dwc3_meson_g12a_remove,
.driver = {
.name = "dwc3-meson-g12a",
.of_match_table = dwc3_meson_g12a_match,
diff --git a/drivers/usb/dwc3/dwc3-of-simple.c b/drivers/usb/dwc3/dwc3-of-simple.c
index 71fd620c5161..7e6ad8fe61a5 100644
--- a/drivers/usb/dwc3/dwc3-of-simple.c
+++ b/drivers/usb/dwc3/dwc3-of-simple.c
@@ -112,13 +112,11 @@ static void __dwc3_of_simple_teardown(struct dwc3_of_simple *simple)
pm_runtime_set_suspended(simple->dev);
}
-static int dwc3_of_simple_remove(struct platform_device *pdev)
+static void dwc3_of_simple_remove(struct platform_device *pdev)
{
struct dwc3_of_simple *simple = platform_get_drvdata(pdev);
__dwc3_of_simple_teardown(simple);
-
- return 0;
}
static void dwc3_of_simple_shutdown(struct platform_device *pdev)
@@ -183,7 +181,7 @@ MODULE_DEVICE_TABLE(of, of_dwc3_simple_match);
static struct platform_driver dwc3_of_simple_driver = {
.probe = dwc3_of_simple_probe,
- .remove = dwc3_of_simple_remove,
+ .remove_new = dwc3_of_simple_remove,
.shutdown = dwc3_of_simple_shutdown,
.driver = {
.name = "dwc3-of-simple",
diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c
index efaf0db595f4..d5c77db4daa9 100644
--- a/drivers/usb/dwc3/dwc3-omap.c
+++ b/drivers/usb/dwc3/dwc3-omap.c
@@ -534,7 +534,7 @@ err1:
return ret;
}
-static int dwc3_omap_remove(struct platform_device *pdev)
+static void dwc3_omap_remove(struct platform_device *pdev)
{
struct dwc3_omap *omap = platform_get_drvdata(pdev);
@@ -543,8 +543,6 @@ static int dwc3_omap_remove(struct platform_device *pdev)
of_platform_depopulate(omap->dev);
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
-
- return 0;
}
static const struct of_device_id of_dwc3_match[] = {
@@ -611,7 +609,7 @@ static const struct dev_pm_ops dwc3_omap_dev_pm_ops = {
static struct platform_driver dwc3_omap_driver = {
.probe = dwc3_omap_probe,
- .remove = dwc3_omap_remove,
+ .remove_new = dwc3_omap_remove,
.driver = {
.name = "omap-dwc3",
.of_match_table = of_dwc3_match,
diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c
index 79b22abf9727..3de43df6bbe8 100644
--- a/drivers/usb/dwc3/dwc3-qcom.c
+++ b/drivers/usb/dwc3/dwc3-qcom.c
@@ -167,7 +167,8 @@ static int dwc3_qcom_register_extcon(struct dwc3_qcom *qcom)
qcom->edev = extcon_get_edev_by_phandle(dev, 0);
if (IS_ERR(qcom->edev))
- return PTR_ERR(qcom->edev);
+ return dev_err_probe(dev, PTR_ERR(qcom->edev),
+ "Failed to get extcon\n");
qcom->vbus_nb.notifier_call = dwc3_qcom_vbus_notifier;
@@ -252,16 +253,14 @@ static int dwc3_qcom_interconnect_init(struct dwc3_qcom *qcom)
qcom->icc_path_ddr = of_icc_get(dev, "usb-ddr");
if (IS_ERR(qcom->icc_path_ddr)) {
- dev_err(dev, "failed to get usb-ddr path: %ld\n",
- PTR_ERR(qcom->icc_path_ddr));
- return PTR_ERR(qcom->icc_path_ddr);
+ return dev_err_probe(dev, PTR_ERR(qcom->icc_path_ddr),
+ "failed to get usb-ddr path\n");
}
qcom->icc_path_apps = of_icc_get(dev, "apps-usb");
if (IS_ERR(qcom->icc_path_apps)) {
- dev_err(dev, "failed to get apps-usb path: %ld\n",
- PTR_ERR(qcom->icc_path_apps));
- ret = PTR_ERR(qcom->icc_path_apps);
+ ret = dev_err_probe(dev, PTR_ERR(qcom->icc_path_apps),
+ "failed to get apps-usb path\n");
goto put_path_ddr;
}
@@ -800,6 +799,7 @@ static int dwc3_qcom_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct dwc3_qcom *qcom;
struct resource *res, *parent_res = NULL;
+ struct resource local_res;
int ret, i;
bool ignore_pipe_clk;
bool wakeup_source;
@@ -821,9 +821,8 @@ static int dwc3_qcom_probe(struct platform_device *pdev)
qcom->resets = devm_reset_control_array_get_optional_exclusive(dev);
if (IS_ERR(qcom->resets)) {
- ret = PTR_ERR(qcom->resets);
- dev_err(&pdev->dev, "failed to get resets, err=%d\n", ret);
- return ret;
+ return dev_err_probe(&pdev->dev, PTR_ERR(qcom->resets),
+ "failed to get resets\n");
}
ret = reset_control_assert(qcom->resets);
@@ -842,7 +841,7 @@ static int dwc3_qcom_probe(struct platform_device *pdev)
ret = dwc3_qcom_clk_init(qcom, of_clk_get_parent_count(np));
if (ret) {
- dev_err(dev, "failed to get clocks\n");
+ dev_err_probe(dev, ret, "failed to get clocks\n");
goto reset_assert;
}
@@ -851,9 +850,8 @@ static int dwc3_qcom_probe(struct platform_device *pdev)
if (np) {
parent_res = res;
} else {
- parent_res = kmemdup(res, sizeof(struct resource), GFP_KERNEL);
- if (!parent_res)
- return -ENOMEM;
+ memcpy(&local_res, res, sizeof(struct resource));
+ parent_res = &local_res;
parent_res->start = res->start +
qcom->acpi_pdata->qscratch_base_offset;
@@ -865,9 +863,10 @@ static int dwc3_qcom_probe(struct platform_device *pdev)
if (IS_ERR_OR_NULL(qcom->urs_usb)) {
dev_err(dev, "failed to create URS USB platdev\n");
if (!qcom->urs_usb)
- return -ENODEV;
+ ret = -ENODEV;
else
- return PTR_ERR(qcom->urs_usb);
+ ret = PTR_ERR(qcom->urs_usb);
+ goto clk_disable;
}
}
}
@@ -947,14 +946,18 @@ reset_assert:
return ret;
}
-static int dwc3_qcom_remove(struct platform_device *pdev)
+static void dwc3_qcom_remove(struct platform_device *pdev)
{
struct dwc3_qcom *qcom = platform_get_drvdata(pdev);
+ struct device_node *np = pdev->dev.of_node;
struct device *dev = &pdev->dev;
int i;
device_remove_software_node(&qcom->dwc3->dev);
- of_platform_depopulate(dev);
+ if (np)
+ of_platform_depopulate(&pdev->dev);
+ else
+ platform_device_put(pdev);
for (i = qcom->num_clocks - 1; i >= 0; i--) {
clk_disable_unprepare(qcom->clks[i]);
@@ -967,8 +970,6 @@ static int dwc3_qcom_remove(struct platform_device *pdev)
pm_runtime_allow(dev);
pm_runtime_disable(dev);
-
- return 0;
}
static int __maybe_unused dwc3_qcom_pm_suspend(struct device *dev)
@@ -1061,7 +1062,7 @@ MODULE_DEVICE_TABLE(acpi, dwc3_qcom_acpi_match);
static struct platform_driver dwc3_qcom_driver = {
.probe = dwc3_qcom_probe,
- .remove = dwc3_qcom_remove,
+ .remove_new = dwc3_qcom_remove,
.driver = {
.name = "dwc3-qcom",
.pm = &dwc3_qcom_dev_pm_ops,
diff --git a/drivers/usb/dwc3/dwc3-st.c b/drivers/usb/dwc3/dwc3-st.c
index fea5290de83f..211360eee95a 100644
--- a/drivers/usb/dwc3/dwc3-st.c
+++ b/drivers/usb/dwc3/dwc3-st.c
@@ -305,7 +305,7 @@ undo_platform_dev_alloc:
return ret;
}
-static int st_dwc3_remove(struct platform_device *pdev)
+static void st_dwc3_remove(struct platform_device *pdev)
{
struct st_dwc3 *dwc3_data = platform_get_drvdata(pdev);
@@ -313,8 +313,6 @@ static int st_dwc3_remove(struct platform_device *pdev)
reset_control_assert(dwc3_data->rstc_pwrdn);
reset_control_assert(dwc3_data->rstc_rst);
-
- return 0;
}
#ifdef CONFIG_PM_SLEEP
@@ -364,7 +362,7 @@ MODULE_DEVICE_TABLE(of, st_dwc3_match);
static struct platform_driver st_dwc3_driver = {
.probe = st_dwc3_probe,
- .remove = st_dwc3_remove,
+ .remove_new = st_dwc3_remove,
.driver = {
.name = "usb-st-dwc3",
.of_match_table = st_dwc3_match,
diff --git a/drivers/usb/dwc3/dwc3-xilinx.c b/drivers/usb/dwc3/dwc3-xilinx.c
index 2c36f97652ca..19307d24f3a0 100644
--- a/drivers/usb/dwc3/dwc3-xilinx.c
+++ b/drivers/usb/dwc3/dwc3-xilinx.c
@@ -305,7 +305,7 @@ err_clk_put:
return ret;
}
-static int dwc3_xlnx_remove(struct platform_device *pdev)
+static void dwc3_xlnx_remove(struct platform_device *pdev)
{
struct dwc3_xlnx *priv_data = platform_get_drvdata(pdev);
struct device *dev = &pdev->dev;
@@ -318,8 +318,6 @@ static int dwc3_xlnx_remove(struct platform_device *pdev)
pm_runtime_disable(dev);
pm_runtime_put_noidle(dev);
pm_runtime_set_suspended(dev);
-
- return 0;
}
static int __maybe_unused dwc3_xlnx_runtime_suspend(struct device *dev)
@@ -388,7 +386,7 @@ static const struct dev_pm_ops dwc3_xlnx_dev_pm_ops = {
static struct platform_driver dwc3_xlnx_driver = {
.probe = dwc3_xlnx_probe,
- .remove = dwc3_xlnx_remove,
+ .remove_new = dwc3_xlnx_remove,
.driver = {
.name = "dwc3-xilinx",
.of_match_table = dwc3_xlnx_of_match,
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 953b752a5052..b94243237293 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -1207,5 +1207,8 @@ void dwc3_ep0_interrupt(struct dwc3 *dwc,
dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
}
break;
+ default:
+ dev_err(dwc->dev, "unknown endpoint event %d\n", event->endpoint_event);
+ break;
}
}
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index b78599dd705c..5fd067151fbf 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -2703,13 +2703,17 @@ static int dwc3_gadget_soft_disconnect(struct dwc3 *dwc)
static int dwc3_gadget_soft_connect(struct dwc3 *dwc)
{
+ int ret;
+
/*
* In the Synopsys DWC_usb31 1.90a programming guide section
* 4.1.9, it specifies that for a reconnect after a
* device-initiated disconnect requires a core soft reset
* (DCTL.CSftRst) before enabling the run/stop bit.
*/
- dwc3_core_soft_reset(dwc);
+ ret = dwc3_core_soft_reset(dwc);
+ if (ret)
+ return ret;
dwc3_event_buffers_setup(dwc);
__dwc3_gadget_start(dwc);
@@ -2744,7 +2748,9 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
ret = pm_runtime_get_sync(dwc->dev);
if (!ret || ret < 0) {
pm_runtime_put(dwc->dev);
- return 0;
+ if (ret < 0)
+ pm_runtime_set_suspended(dwc->dev);
+ return ret;
}
if (dwc->pullups_connected == is_on) {
@@ -3809,6 +3815,9 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
break;
case DWC3_DEPEVT_RXTXFIFOEVT:
break;
+ default:
+ dev_err(dwc->dev, "unknown endpoint event %d\n", event->endpoint_event);
+ break;
}
}
diff --git a/drivers/usb/fotg210/fotg210-core.c b/drivers/usb/fotg210/fotg210-core.c
index cb75464ab290..958fc40eae86 100644
--- a/drivers/usb/fotg210/fotg210-core.c
+++ b/drivers/usb/fotg210/fotg210-core.c
@@ -165,7 +165,7 @@ static int fotg210_probe(struct platform_device *pdev)
return ret;
}
-static int fotg210_remove(struct platform_device *pdev)
+static void fotg210_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
enum usb_dr_mode mode;
@@ -176,8 +176,6 @@ static int fotg210_remove(struct platform_device *pdev)
fotg210_udc_remove(pdev);
else
fotg210_hcd_remove(pdev);
-
- return 0;
}
#ifdef CONFIG_OF
@@ -196,7 +194,7 @@ static struct platform_driver fotg210_driver = {
.of_match_table = of_match_ptr(fotg210_of_match),
},
.probe = fotg210_probe,
- .remove = fotg210_remove,
+ .remove_new = fotg210_remove,
};
static int __init fotg210_init(void)
diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c
index 9f6b10134121..ea85e2c701a1 100644
--- a/drivers/usb/gadget/function/f_hid.c
+++ b/drivers/usb/gadget/function/f_hid.c
@@ -23,7 +23,11 @@
#define HIDG_MINORS 4
static int major, minors;
-static struct class *hidg_class;
+
+static const struct class hidg_class = {
+ .name = "hidg",
+};
+
static DEFINE_IDA(hidg_ida);
static DEFINE_MUTEX(hidg_ida_lock); /* protects access to hidg_ida */
@@ -1272,7 +1276,7 @@ static struct usb_function *hidg_alloc(struct usb_function_instance *fi)
device_initialize(&hidg->dev);
hidg->dev.release = hidg_release;
- hidg->dev.class = hidg_class;
+ hidg->dev.class = &hidg_class;
hidg->dev.devt = MKDEV(major, opts->minor);
ret = dev_set_name(&hidg->dev, "hidg%d", opts->minor);
if (ret)
@@ -1325,17 +1329,13 @@ int ghid_setup(struct usb_gadget *g, int count)
int status;
dev_t dev;
- hidg_class = class_create("hidg");
- if (IS_ERR(hidg_class)) {
- status = PTR_ERR(hidg_class);
- hidg_class = NULL;
+ status = class_register(&hidg_class);
+ if (status)
return status;
- }
status = alloc_chrdev_region(&dev, 0, count, "hidg");
if (status) {
- class_destroy(hidg_class);
- hidg_class = NULL;
+ class_unregister(&hidg_class);
return status;
}
@@ -1352,6 +1352,5 @@ void ghid_cleanup(void)
major = minors = 0;
}
- class_destroy(hidg_class);
- hidg_class = NULL;
+ class_unregister(&hidg_class);
}
diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c
index 3a30feb47073..da07e45ae6df 100644
--- a/drivers/usb/gadget/function/f_mass_storage.c
+++ b/drivers/usb/gadget/function/f_mass_storage.c
@@ -2857,7 +2857,7 @@ int fsg_common_create_lun(struct fsg_common *common, struct fsg_lun_config *cfg,
const char **name_pfx)
{
struct fsg_lun *lun;
- char *pathbuf, *p;
+ char *pathbuf = NULL, *p = "(no medium)";
int rc = -ENOMEM;
if (id >= ARRAY_SIZE(common->luns))
@@ -2907,12 +2907,9 @@ int fsg_common_create_lun(struct fsg_common *common, struct fsg_lun_config *cfg,
rc = fsg_lun_open(lun, cfg->filename);
if (rc)
goto error_lun;
- }
- pathbuf = kmalloc(PATH_MAX, GFP_KERNEL);
- p = "(no medium)";
- if (fsg_lun_is_open(lun)) {
p = "(error)";
+ pathbuf = kmalloc(PATH_MAX, GFP_KERNEL);
if (pathbuf) {
p = file_path(lun->filp, pathbuf, PATH_MAX);
if (IS_ERR(p))
@@ -2931,7 +2928,6 @@ int fsg_common_create_lun(struct fsg_common *common, struct fsg_lun_config *cfg,
error_lun:
if (device_is_registered(&lun->dev))
device_unregister(&lun->dev);
- fsg_lun_close(lun);
common->luns[id] = NULL;
error_sysfs:
kfree(lun);
diff --git a/drivers/usb/gadget/function/f_printer.c b/drivers/usb/gadget/function/f_printer.c
index 28db3e336e7d..076dd4c1be96 100644
--- a/drivers/usb/gadget/function/f_printer.c
+++ b/drivers/usb/gadget/function/f_printer.c
@@ -54,7 +54,10 @@
#define DEFAULT_Q_LEN 10 /* same as legacy g_printer gadget */
static int major, minors;
-static struct class *usb_gadget_class;
+static const struct class usb_gadget_class = {
+ .name = "usb_printer_gadget",
+};
+
static DEFINE_IDA(printer_ida);
static DEFINE_MUTEX(printer_ida_lock); /* protects access do printer_ida */
@@ -1120,7 +1123,7 @@ autoconf_fail:
/* Setup the sysfs files for the printer gadget. */
devt = MKDEV(major, dev->minor);
- pdev = device_create(usb_gadget_class, NULL, devt,
+ pdev = device_create(&usb_gadget_class, NULL, devt,
NULL, "g_printer%d", dev->minor);
if (IS_ERR(pdev)) {
ERROR(dev, "Failed to create device: g_printer\n");
@@ -1143,7 +1146,7 @@ autoconf_fail:
return 0;
fail_cdev_add:
- device_destroy(usb_gadget_class, devt);
+ device_destroy(&usb_gadget_class, devt);
fail_rx_reqs:
while (!list_empty(&dev->rx_reqs)) {
@@ -1211,8 +1214,8 @@ static ssize_t f_printer_opts_pnp_string_show(struct config_item *item,
if (!opts->pnp_string)
goto unlock;
- result = strlcpy(page, opts->pnp_string, PAGE_SIZE);
- if (result >= PAGE_SIZE) {
+ result = strscpy(page, opts->pnp_string, PAGE_SIZE);
+ if (result < 1) {
result = PAGE_SIZE;
} else if (page[result - 1] != '\n' && result + 1 < PAGE_SIZE) {
page[result++] = '\n';
@@ -1410,7 +1413,7 @@ static void printer_func_unbind(struct usb_configuration *c,
dev = func_to_printer(f);
- device_destroy(usb_gadget_class, MKDEV(major, dev->minor));
+ device_destroy(&usb_gadget_class, MKDEV(major, dev->minor));
/* Remove Character Device */
cdev_del(&dev->printer_cdev);
@@ -1512,19 +1515,14 @@ static int gprinter_setup(int count)
int status;
dev_t devt;
- usb_gadget_class = class_create("usb_printer_gadget");
- if (IS_ERR(usb_gadget_class)) {
- status = PTR_ERR(usb_gadget_class);
- usb_gadget_class = NULL;
- pr_err("unable to create usb_gadget class %d\n", status);
+ status = class_register(&usb_gadget_class);
+ if (status)
return status;
- }
status = alloc_chrdev_region(&devt, 0, count, "USB printer gadget");
if (status) {
pr_err("alloc_chrdev_region %d\n", status);
- class_destroy(usb_gadget_class);
- usb_gadget_class = NULL;
+ class_unregister(&usb_gadget_class);
return status;
}
@@ -1540,6 +1538,5 @@ static void gprinter_cleanup(void)
unregister_chrdev_region(MKDEV(major, 0), minors);
major = minors = 0;
}
- class_destroy(usb_gadget_class);
- usb_gadget_class = NULL;
+ class_unregister(&usb_gadget_class);
}
diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c
index a0ca47fbff0f..1115396b46a0 100644
--- a/drivers/usb/gadget/function/u_serial.c
+++ b/drivers/usb/gadget/function/u_serial.c
@@ -539,16 +539,20 @@ static int gs_alloc_requests(struct usb_ep *ep, struct list_head *head,
static int gs_start_io(struct gs_port *port)
{
struct list_head *head = &port->read_pool;
- struct usb_ep *ep = port->port_usb->out;
+ struct usb_ep *ep;
int status;
unsigned started;
+ if (!port->port_usb || !port->port.tty)
+ return -EIO;
+
/* Allocate RX and TX I/O buffers. We can't easily do this much
* earlier (with GFP_KERNEL) because the requests are coupled to
* endpoints, as are the packet sizes we'll be using. Different
* configurations may use different endpoints with a given port;
* and high speed vs full speed changes packet sizes too.
*/
+ ep = port->port_usb->out;
status = gs_alloc_requests(ep, head, gs_read_complete,
&port->read_allocated);
if (status)
@@ -916,8 +920,11 @@ static void __gs_console_push(struct gs_console *cons)
}
req->length = size;
+
+ spin_unlock_irq(&cons->lock);
if (usb_ep_queue(ep, req, GFP_ATOMIC))
req->length = 0;
+ spin_lock_irq(&cons->lock);
}
static void gs_console_work(struct work_struct *work)
@@ -1420,10 +1427,19 @@ EXPORT_SYMBOL_GPL(gserial_disconnect);
void gserial_suspend(struct gserial *gser)
{
- struct gs_port *port = gser->ioport;
+ struct gs_port *port;
unsigned long flags;
- spin_lock_irqsave(&port->port_lock, flags);
+ spin_lock_irqsave(&serial_port_lock, flags);
+ port = gser->ioport;
+
+ if (!port) {
+ spin_unlock_irqrestore(&serial_port_lock, flags);
+ return;
+ }
+
+ spin_lock(&port->port_lock);
+ spin_unlock(&serial_port_lock);
port->suspended = true;
spin_unlock_irqrestore(&port->port_lock, flags);
}
diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c
index dd1c6b2ca7c6..91af3b1ef0d4 100644
--- a/drivers/usb/gadget/function/uvc_video.c
+++ b/drivers/usb/gadget/function/uvc_video.c
@@ -382,9 +382,12 @@ static void uvcg_video_pump(struct work_struct *work)
{
struct uvc_video *video = container_of(work, struct uvc_video, pump);
struct uvc_video_queue *queue = &video->queue;
+ /* video->max_payload_size is only set when using bulk transfer */
+ bool is_bulk = video->max_payload_size;
struct usb_request *req = NULL;
struct uvc_buffer *buf;
unsigned long flags;
+ bool buf_done;
int ret;
while (video->ep->enabled) {
@@ -408,20 +411,47 @@ static void uvcg_video_pump(struct work_struct *work)
*/
spin_lock_irqsave(&queue->irqlock, flags);
buf = uvcg_queue_head(queue);
- if (buf == NULL) {
+
+ if (buf != NULL) {
+ video->encode(req, video, buf);
+ buf_done = buf->state == UVC_BUF_STATE_DONE;
+ } else if (!(queue->flags & UVC_QUEUE_DISCONNECTED) && !is_bulk) {
+ /*
+ * No video buffer available; the queue is still connected and
+ * we're transferring over ISOC. Queue a 0 length request to
+ * prevent missed ISOC transfers.
+ */
+ req->length = 0;
+ buf_done = false;
+ } else {
+ /*
+ * Either the queue has been disconnected or no video buffer
+ * available for bulk transfer. Either way, stop processing
+ * further.
+ */
spin_unlock_irqrestore(&queue->irqlock, flags);
break;
}
- video->encode(req, video, buf);
-
/*
- * With usb3 we have more requests. This will decrease the
- * interrupt load to a quarter but also catches the corner
- * cases, which needs to be handled.
+ * With USB3 handling more requests at a higher speed, we can't
+ * afford to generate an interrupt for every request. Decide to
+ * interrupt:
+ *
+ * - When no more requests are available in the free queue, as
+ * this may be our last chance to refill the endpoint's
+ * request queue.
+ *
+ * - When this is request is the last request for the video
+ * buffer, as we want to start sending the next video buffer
+ * ASAP in case it doesn't get started already in the next
+ * iteration of this loop.
+ *
+ * - Four times over the length of the requests queue (as
+ * indicated by video->uvc_num_requests), as a trade-off
+ * between latency and interrupt load.
*/
- if (list_empty(&video->req_free) ||
- buf->state == UVC_BUF_STATE_DONE ||
+ if (list_empty(&video->req_free) || buf_done ||
!(video->req_int_count %
DIV_ROUND_UP(video->uvc_num_requests, 4))) {
video->req_int_count = 0;
@@ -441,8 +471,7 @@ static void uvcg_video_pump(struct work_struct *work)
/* Endpoint now owns the request */
req = NULL;
- if (buf->state != UVC_BUF_STATE_DONE)
- video->req_int_count++;
+ video->req_int_count++;
}
if (!req)
@@ -527,4 +556,3 @@ int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc)
V4L2_BUF_TYPE_VIDEO_OUTPUT, &video->mutex);
return 0;
}
-
diff --git a/drivers/usb/gadget/legacy/g_ffs.c b/drivers/usb/gadget/legacy/g_ffs.c
index 097854683e5b..a9544fea8723 100644
--- a/drivers/usb/gadget/legacy/g_ffs.c
+++ b/drivers/usb/gadget/legacy/g_ffs.c
@@ -389,8 +389,10 @@ static int gfs_bind(struct usb_composite_dev *cdev)
struct usb_descriptor_header *usb_desc;
usb_desc = usb_otg_descriptor_alloc(cdev->gadget);
- if (!usb_desc)
+ if (!usb_desc) {
+ ret = -ENOMEM;
goto error_rndis;
+ }
usb_otg_descriptor_init(cdev->gadget, usb_desc);
gfs_otg_desc[0] = usb_desc;
gfs_otg_desc[1] = NULL;
diff --git a/drivers/usb/gadget/legacy/hid.c b/drivers/usb/gadget/legacy/hid.c
index 133daf88162e..4ca67b2f8f24 100644
--- a/drivers/usb/gadget/legacy/hid.c
+++ b/drivers/usb/gadget/legacy/hid.c
@@ -237,7 +237,7 @@ static int hidg_plat_driver_probe(struct platform_device *pdev)
return 0;
}
-static int hidg_plat_driver_remove(struct platform_device *pdev)
+static void hidg_plat_driver_remove(struct platform_device *pdev)
{
struct hidg_func_node *e, *n;
@@ -245,8 +245,6 @@ static int hidg_plat_driver_remove(struct platform_device *pdev)
list_del(&e->node);
kfree(e);
}
-
- return 0;
}
@@ -263,7 +261,7 @@ static struct usb_composite_driver hidg_driver = {
};
static struct platform_driver hidg_plat_driver = {
- .remove = hidg_plat_driver_remove,
+ .remove_new = hidg_plat_driver_remove,
.driver = {
.name = "hidg",
},
diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig
index 83cae6bb12eb..aae1787320d4 100644
--- a/drivers/usb/gadget/udc/Kconfig
+++ b/drivers/usb/gadget/udc/Kconfig
@@ -463,6 +463,8 @@ config USB_ASPEED_UDC
source "drivers/usb/gadget/udc/aspeed-vhub/Kconfig"
+source "drivers/usb/gadget/udc/cdns2/Kconfig"
+
#
# LAST -- dummy/emulated controller
#
diff --git a/drivers/usb/gadget/udc/Makefile b/drivers/usb/gadget/udc/Makefile
index ee569f63c74a..b52f93e9c61d 100644
--- a/drivers/usb/gadget/udc/Makefile
+++ b/drivers/usb/gadget/udc/Makefile
@@ -42,3 +42,4 @@ obj-$(CONFIG_USB_ASPEED_VHUB) += aspeed-vhub/
obj-$(CONFIG_USB_ASPEED_UDC) += aspeed_udc.o
obj-$(CONFIG_USB_BDC_UDC) += bdc/
obj-$(CONFIG_USB_MAX3420_UDC) += max3420_udc.o
+obj-$(CONFIG_USB_CDNS2_UDC) += cdns2/
diff --git a/drivers/usb/gadget/udc/aspeed-vhub/core.c b/drivers/usb/gadget/udc/aspeed-vhub/core.c
index 86398a04a012..16f2db8c4a2b 100644
--- a/drivers/usb/gadget/udc/aspeed-vhub/core.c
+++ b/drivers/usb/gadget/udc/aspeed-vhub/core.c
@@ -253,14 +253,14 @@ void ast_vhub_init_hw(struct ast_vhub *vhub)
vhub->regs + AST_VHUB_IER);
}
-static int ast_vhub_remove(struct platform_device *pdev)
+static void ast_vhub_remove(struct platform_device *pdev)
{
struct ast_vhub *vhub = platform_get_drvdata(pdev);
unsigned long flags;
int i;
if (!vhub || !vhub->regs)
- return 0;
+ return;
/* Remove devices */
for (i = 0; i < vhub->max_ports; i++)
@@ -289,8 +289,6 @@ static int ast_vhub_remove(struct platform_device *pdev)
vhub->ep0_bufs,
vhub->ep0_bufs_dma);
vhub->ep0_bufs = NULL;
-
- return 0;
}
static int ast_vhub_probe(struct platform_device *pdev)
@@ -431,7 +429,7 @@ MODULE_DEVICE_TABLE(of, ast_vhub_dt_ids);
static struct platform_driver ast_vhub_driver = {
.probe = ast_vhub_probe,
- .remove = ast_vhub_remove,
+ .remove_new = ast_vhub_remove,
.driver = {
.name = KBUILD_MODNAME,
.of_match_table = ast_vhub_dt_ids,
diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c
index 53ca38c4b3ec..6c0ed3fa5eb1 100644
--- a/drivers/usb/gadget/udc/atmel_usba_udc.c
+++ b/drivers/usb/gadget/udc/atmel_usba_udc.c
@@ -2369,7 +2369,7 @@ static int usba_udc_probe(struct platform_device *pdev)
return 0;
}
-static int usba_udc_remove(struct platform_device *pdev)
+static void usba_udc_remove(struct platform_device *pdev)
{
struct usba_udc *udc;
int i;
@@ -2382,8 +2382,6 @@ static int usba_udc_remove(struct platform_device *pdev)
for (i = 1; i < udc->num_ep; i++)
usba_ep_cleanup_debugfs(&udc->usba_ep[i]);
usba_cleanup_debugfs(udc);
-
- return 0;
}
#ifdef CONFIG_PM_SLEEP
@@ -2450,7 +2448,7 @@ static SIMPLE_DEV_PM_OPS(usba_udc_pm_ops, usba_udc_suspend, usba_udc_resume);
static struct platform_driver udc_driver = {
.probe = usba_udc_probe,
- .remove = usba_udc_remove,
+ .remove_new = usba_udc_remove,
.driver = {
.name = "atmel_usba_udc",
.pm = &usba_udc_pm_ops,
diff --git a/drivers/usb/gadget/udc/bcm63xx_udc.c b/drivers/usb/gadget/udc/bcm63xx_udc.c
index a3055dd4acfb..da7011d906e0 100644
--- a/drivers/usb/gadget/udc/bcm63xx_udc.c
+++ b/drivers/usb/gadget/udc/bcm63xx_udc.c
@@ -2354,7 +2354,7 @@ report_request_failure:
* bcm63xx_udc_remove - Remove the device from the system.
* @pdev: Platform device struct from the bcm63xx BSP code.
*/
-static int bcm63xx_udc_remove(struct platform_device *pdev)
+static void bcm63xx_udc_remove(struct platform_device *pdev)
{
struct bcm63xx_udc *udc = platform_get_drvdata(pdev);
@@ -2363,13 +2363,11 @@ static int bcm63xx_udc_remove(struct platform_device *pdev)
BUG_ON(udc->driver);
bcm63xx_uninit_udc_hw(udc);
-
- return 0;
}
static struct platform_driver bcm63xx_udc_driver = {
.probe = bcm63xx_udc_probe,
- .remove = bcm63xx_udc_remove,
+ .remove_new = bcm63xx_udc_remove,
.driver = {
.name = DRV_MODULE_NAME,
},
diff --git a/drivers/usb/gadget/udc/bdc/bdc_core.c b/drivers/usb/gadget/udc/bdc/bdc_core.c
index 9849e0c86e23..35a652807fca 100644
--- a/drivers/usb/gadget/udc/bdc/bdc_core.c
+++ b/drivers/usb/gadget/udc/bdc/bdc_core.c
@@ -583,7 +583,7 @@ disable_clk:
return ret;
}
-static int bdc_remove(struct platform_device *pdev)
+static void bdc_remove(struct platform_device *pdev)
{
struct bdc *bdc;
@@ -593,7 +593,6 @@ static int bdc_remove(struct platform_device *pdev)
bdc_hw_exit(bdc);
bdc_phy_exit(bdc);
clk_disable_unprepare(bdc->clk);
- return 0;
}
#ifdef CONFIG_PM_SLEEP
@@ -648,7 +647,7 @@ static struct platform_driver bdc_driver = {
.of_match_table = bdc_of_match,
},
.probe = bdc_probe,
- .remove = bdc_remove,
+ .remove_new = bdc_remove,
};
module_platform_driver(bdc_driver);
diff --git a/drivers/usb/gadget/udc/cdns2/Kconfig b/drivers/usb/gadget/udc/cdns2/Kconfig
new file mode 100644
index 000000000000..c07d353903ea
--- /dev/null
+++ b/drivers/usb/gadget/udc/cdns2/Kconfig
@@ -0,0 +1,11 @@
+config USB_CDNS2_UDC
+ tristate "Cadence USBHS Device Controller"
+ depends on USB_PCI && ACPI && HAS_DMA
+ help
+ Cadence USBHS Device controller is a PCI based USB peripheral
+ controller which supports both full and high speed USB 2.0
+ data transfers.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "cdns2-udc-pci.ko" and to
+ force all gadget drivers to also be dynamically linked.
diff --git a/drivers/usb/gadget/udc/cdns2/Makefile b/drivers/usb/gadget/udc/cdns2/Makefile
new file mode 100644
index 000000000000..a1ffbbe2e768
--- /dev/null
+++ b/drivers/usb/gadget/udc/cdns2/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0
+# define_trace.h needs to know how to find our header
+CFLAGS_cdns2-trace.o := -I$(src)
+
+obj-$(CONFIG_USB_CDNS2_UDC) += cdns2-udc-pci.o
+cdns2-udc-pci-$(CONFIG_USB_CDNS2_UDC) += cdns2-pci.o cdns2-gadget.o cdns2-ep0.o
+cdns2-udc-pci-$(CONFIG_TRACING) += cdns2-trace.o
diff --git a/drivers/usb/gadget/udc/cdns2/cdns2-debug.h b/drivers/usb/gadget/udc/cdns2/cdns2-debug.h
new file mode 100644
index 000000000000..be9ae0d28a40
--- /dev/null
+++ b/drivers/usb/gadget/udc/cdns2/cdns2-debug.h
@@ -0,0 +1,203 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Cadence USBHS-DEV Driver.
+ * Debug header file.
+ *
+ * Copyright (C) 2023 Cadence.
+ *
+ * Author: Pawel Laszczak <pawell@cadence.com>
+ */
+
+#ifndef __LINUX_CDNS2_DEBUG
+#define __LINUX_CDNS2_DEBUG
+
+static inline const char *cdns2_decode_usb_irq(char *str, size_t size,
+ u8 usb_irq, u8 ext_irq)
+{
+ int ret;
+
+ ret = snprintf(str, size, "usbirq: 0x%02x - ", usb_irq);
+
+ if (usb_irq & USBIRQ_SOF)
+ ret += snprintf(str + ret, size - ret, "SOF ");
+ if (usb_irq & USBIRQ_SUTOK)
+ ret += snprintf(str + ret, size - ret, "SUTOK ");
+ if (usb_irq & USBIRQ_SUDAV)
+ ret += snprintf(str + ret, size - ret, "SETUP ");
+ if (usb_irq & USBIRQ_SUSPEND)
+ ret += snprintf(str + ret, size - ret, "Suspend ");
+ if (usb_irq & USBIRQ_URESET)
+ ret += snprintf(str + ret, size - ret, "Reset ");
+ if (usb_irq & USBIRQ_HSPEED)
+ ret += snprintf(str + ret, size - ret, "HS ");
+ if (usb_irq & USBIRQ_LPM)
+ ret += snprintf(str + ret, size - ret, "LPM ");
+
+ ret += snprintf(str + ret, size - ret, ", EXT: 0x%02x - ", ext_irq);
+
+ if (ext_irq & EXTIRQ_WAKEUP)
+ ret += snprintf(str + ret, size - ret, "Wakeup ");
+ if (ext_irq & EXTIRQ_VBUSFAULT_FALL)
+ ret += snprintf(str + ret, size - ret, "VBUS_FALL ");
+ if (ext_irq & EXTIRQ_VBUSFAULT_RISE)
+ ret += snprintf(str + ret, size - ret, "VBUS_RISE ");
+
+ if (ret >= size)
+ pr_info("CDNS2: buffer overflowed.\n");
+
+ return str;
+}
+
+static inline const char *cdns2_decode_dma_irq(char *str, size_t size,
+ u32 ep_ists, u32 ep_sts,
+ const char *ep_name)
+{
+ int ret;
+
+ ret = snprintf(str, size, "ISTS: %08x, %s: %08x ",
+ ep_ists, ep_name, ep_sts);
+
+ if (ep_sts & DMA_EP_STS_IOC)
+ ret += snprintf(str + ret, size - ret, "IOC ");
+ if (ep_sts & DMA_EP_STS_ISP)
+ ret += snprintf(str + ret, size - ret, "ISP ");
+ if (ep_sts & DMA_EP_STS_DESCMIS)
+ ret += snprintf(str + ret, size - ret, "DESCMIS ");
+ if (ep_sts & DMA_EP_STS_TRBERR)
+ ret += snprintf(str + ret, size - ret, "TRBERR ");
+ if (ep_sts & DMA_EP_STS_OUTSMM)
+ ret += snprintf(str + ret, size - ret, "OUTSMM ");
+ if (ep_sts & DMA_EP_STS_ISOERR)
+ ret += snprintf(str + ret, size - ret, "ISOERR ");
+ if (ep_sts & DMA_EP_STS_DBUSY)
+ ret += snprintf(str + ret, size - ret, "DBUSY ");
+ if (DMA_EP_STS_CCS(ep_sts))
+ ret += snprintf(str + ret, size - ret, "CCS ");
+
+ if (ret >= size)
+ pr_info("CDNS2: buffer overflowed.\n");
+
+ return str;
+}
+
+static inline const char *cdns2_decode_epx_irq(char *str, size_t size,
+ char *ep_name, u32 ep_ists,
+ u32 ep_sts)
+{
+ return cdns2_decode_dma_irq(str, size, ep_ists, ep_sts, ep_name);
+}
+
+static inline const char *cdns2_decode_ep0_irq(char *str, size_t size,
+ u32 ep_ists, u32 ep_sts,
+ int dir)
+{
+ return cdns2_decode_dma_irq(str, size, ep_ists, ep_sts,
+ dir ? "ep0IN" : "ep0OUT");
+}
+
+static inline const char *cdns2_raw_ring(struct cdns2_endpoint *pep,
+ struct cdns2_trb *trbs,
+ char *str, size_t size)
+{
+ struct cdns2_ring *ring = &pep->ring;
+ struct cdns2_trb *trb;
+ dma_addr_t dma;
+ int ret;
+ int i;
+
+ ret = snprintf(str, size, "\n\t\tTR for %s:", pep->name);
+
+ trb = &trbs[ring->dequeue];
+ dma = cdns2_trb_virt_to_dma(pep, trb);
+ ret += snprintf(str + ret, size - ret,
+ "\n\t\tRing deq index: %d, trb: V=%p, P=0x%pad\n",
+ ring->dequeue, trb, &dma);
+
+ trb = &trbs[ring->enqueue];
+ dma = cdns2_trb_virt_to_dma(pep, trb);
+ ret += snprintf(str + ret, size - ret,
+ "\t\tRing enq index: %d, trb: V=%p, P=0x%pad\n",
+ ring->enqueue, trb, &dma);
+
+ ret += snprintf(str + ret, size - ret,
+ "\t\tfree trbs: %d, CCS=%d, PCS=%d\n",
+ ring->free_trbs, ring->ccs, ring->pcs);
+
+ if (TRBS_PER_SEGMENT > 40) {
+ ret += snprintf(str + ret, size - ret,
+ "\t\tTransfer ring %d too big\n", TRBS_PER_SEGMENT);
+ return str;
+ }
+
+ dma = ring->dma;
+ for (i = 0; i < TRBS_PER_SEGMENT; ++i) {
+ trb = &trbs[i];
+ ret += snprintf(str + ret, size - ret,
+ "\t\t@%pad %08x %08x %08x\n", &dma,
+ le32_to_cpu(trb->buffer),
+ le32_to_cpu(trb->length),
+ le32_to_cpu(trb->control));
+ dma += sizeof(*trb);
+ }
+
+ if (ret >= size)
+ pr_info("CDNS2: buffer overflowed.\n");
+
+ return str;
+}
+
+static inline const char *cdns2_trb_type_string(u8 type)
+{
+ switch (type) {
+ case TRB_NORMAL:
+ return "Normal";
+ case TRB_LINK:
+ return "Link";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+static inline const char *cdns2_decode_trb(char *str, size_t size, u32 flags,
+ u32 length, u32 buffer)
+{
+ int type = TRB_FIELD_TO_TYPE(flags);
+ int ret;
+
+ switch (type) {
+ case TRB_LINK:
+ ret = snprintf(str, size,
+ "LINK %08x type '%s' flags %c:%c:%c%c:%c",
+ buffer, cdns2_trb_type_string(type),
+ flags & TRB_CYCLE ? 'C' : 'c',
+ flags & TRB_TOGGLE ? 'T' : 't',
+ flags & TRB_CHAIN ? 'C' : 'c',
+ flags & TRB_CHAIN ? 'H' : 'h',
+ flags & TRB_IOC ? 'I' : 'i');
+ break;
+ case TRB_NORMAL:
+ ret = snprintf(str, size,
+ "type: '%s', Buffer: %08x, length: %ld, burst len: %ld, "
+ "flags %c:%c:%c%c:%c",
+ cdns2_trb_type_string(type),
+ buffer, TRB_LEN(length),
+ TRB_FIELD_TO_BURST(length),
+ flags & TRB_CYCLE ? 'C' : 'c',
+ flags & TRB_ISP ? 'I' : 'i',
+ flags & TRB_CHAIN ? 'C' : 'c',
+ flags & TRB_CHAIN ? 'H' : 'h',
+ flags & TRB_IOC ? 'I' : 'i');
+ break;
+ default:
+ ret = snprintf(str, size, "type '%s' -> raw %08x %08x %08x",
+ cdns2_trb_type_string(type),
+ buffer, length, flags);
+ }
+
+ if (ret >= size)
+ pr_info("CDNS2: buffer overflowed.\n");
+
+ return str;
+}
+
+#endif /*__LINUX_CDNS2_DEBUG*/
diff --git a/drivers/usb/gadget/udc/cdns2/cdns2-ep0.c b/drivers/usb/gadget/udc/cdns2/cdns2-ep0.c
new file mode 100644
index 000000000000..fa12a5d46f2e
--- /dev/null
+++ b/drivers/usb/gadget/udc/cdns2/cdns2-ep0.c
@@ -0,0 +1,659 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Cadence USBHS-DEV driver.
+ *
+ * Copyright (C) 2023 Cadence Design Systems.
+ *
+ * Authors: Pawel Laszczak <pawell@cadence.com>
+ */
+
+#include <linux/usb/composite.h>
+#include <asm/unaligned.h>
+
+#include "cdns2-gadget.h"
+#include "cdns2-trace.h"
+
+static struct usb_endpoint_descriptor cdns2_gadget_ep0_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bmAttributes = USB_ENDPOINT_XFER_CONTROL,
+ .wMaxPacketSize = cpu_to_le16(64)
+};
+
+static int cdns2_w_index_to_ep_index(u16 wIndex)
+{
+ if (!(wIndex & USB_ENDPOINT_NUMBER_MASK))
+ return 0;
+
+ return ((wIndex & USB_ENDPOINT_NUMBER_MASK) * 2) +
+ (wIndex & USB_ENDPOINT_DIR_MASK ? 1 : 0) - 1;
+}
+
+static bool cdns2_check_new_setup(struct cdns2_device *pdev)
+{
+ u8 reg;
+
+ reg = readb(&pdev->ep0_regs->cs);
+
+ return !!(reg & EP0CS_CHGSET);
+}
+
+static void cdns2_ep0_enqueue(struct cdns2_device *pdev, dma_addr_t dma_addr,
+ unsigned int length, int zlp)
+{
+ struct cdns2_adma_regs __iomem *regs = pdev->adma_regs;
+ struct cdns2_endpoint *pep = &pdev->eps[0];
+ struct cdns2_ring *ring = &pep->ring;
+
+ ring->trbs[0].buffer = cpu_to_le32(TRB_BUFFER(dma_addr));
+ ring->trbs[0].length = cpu_to_le32(TRB_LEN(length));
+
+ if (zlp) {
+ ring->trbs[0].control = cpu_to_le32(TRB_CYCLE |
+ TRB_TYPE(TRB_NORMAL));
+ ring->trbs[1].buffer = cpu_to_le32(TRB_BUFFER(dma_addr));
+ ring->trbs[1].length = cpu_to_le32(TRB_LEN(0));
+ ring->trbs[1].control = cpu_to_le32(TRB_CYCLE | TRB_IOC |
+ TRB_TYPE(TRB_NORMAL));
+ } else {
+ ring->trbs[0].control = cpu_to_le32(TRB_CYCLE | TRB_IOC |
+ TRB_TYPE(TRB_NORMAL));
+ ring->trbs[1].control = 0;
+ }
+
+ trace_cdns2_queue_trb(pep, ring->trbs);
+
+ if (!pep->dir)
+ writel(0, &pdev->ep0_regs->rxbc);
+
+ cdns2_select_ep(pdev, pep->dir);
+
+ writel(DMA_EP_STS_TRBERR, &regs->ep_sts);
+ writel(pep->ring.dma, &regs->ep_traddr);
+
+ trace_cdns2_doorbell_ep0(pep, readl(&regs->ep_traddr));
+
+ writel(DMA_EP_CMD_DRDY, &regs->ep_cmd);
+}
+
+static int cdns2_ep0_delegate_req(struct cdns2_device *pdev)
+{
+ int ret;
+
+ spin_unlock(&pdev->lock);
+ ret = pdev->gadget_driver->setup(&pdev->gadget, &pdev->setup);
+ spin_lock(&pdev->lock);
+
+ return ret;
+}
+
+static void cdns2_ep0_stall(struct cdns2_device *pdev)
+{
+ struct cdns2_endpoint *pep = &pdev->eps[0];
+ struct cdns2_request *preq;
+
+ preq = cdns2_next_preq(&pep->pending_list);
+ set_reg_bit_8(&pdev->ep0_regs->cs, EP0CS_DSTALL);
+
+ if (pdev->ep0_stage == CDNS2_DATA_STAGE && preq)
+ cdns2_gadget_giveback(pep, preq, -ECONNRESET);
+ else if (preq)
+ list_del_init(&preq->list);
+
+ pdev->ep0_stage = CDNS2_SETUP_STAGE;
+ pep->ep_state |= EP_STALLED;
+}
+
+static void cdns2_status_stage(struct cdns2_device *pdev)
+{
+ struct cdns2_endpoint *pep = &pdev->eps[0];
+ struct cdns2_request *preq;
+
+ preq = cdns2_next_preq(&pep->pending_list);
+ if (preq)
+ list_del_init(&preq->list);
+
+ pdev->ep0_stage = CDNS2_SETUP_STAGE;
+ writeb(EP0CS_HSNAK, &pdev->ep0_regs->cs);
+}
+
+static int cdns2_req_ep0_set_configuration(struct cdns2_device *pdev,
+ struct usb_ctrlrequest *ctrl_req)
+{
+ enum usb_device_state state = pdev->gadget.state;
+ u32 config = le16_to_cpu(ctrl_req->wValue);
+ int ret;
+
+ if (state < USB_STATE_ADDRESS) {
+ dev_err(pdev->dev, "Set Configuration - bad device state\n");
+ return -EINVAL;
+ }
+
+ ret = cdns2_ep0_delegate_req(pdev);
+ if (ret)
+ return ret;
+
+ trace_cdns2_device_state(config ? "configured" : "addressed");
+
+ if (!config)
+ usb_gadget_set_state(&pdev->gadget, USB_STATE_ADDRESS);
+
+ return 0;
+}
+
+static int cdns2_req_ep0_set_address(struct cdns2_device *pdev, u32 addr)
+{
+ enum usb_device_state device_state = pdev->gadget.state;
+ u8 reg;
+
+ if (addr > USB_DEVICE_MAX_ADDRESS) {
+ dev_err(pdev->dev,
+ "Device address (%d) cannot be greater than %d\n",
+ addr, USB_DEVICE_MAX_ADDRESS);
+ return -EINVAL;
+ }
+
+ if (device_state == USB_STATE_CONFIGURED) {
+ dev_err(pdev->dev,
+ "can't set_address from configured state\n");
+ return -EINVAL;
+ }
+
+ reg = readb(&pdev->usb_regs->fnaddr);
+ pdev->dev_address = reg;
+
+ usb_gadget_set_state(&pdev->gadget,
+ (addr ? USB_STATE_ADDRESS : USB_STATE_DEFAULT));
+
+ trace_cdns2_device_state(addr ? "addressed" : "default");
+
+ return 0;
+}
+
+static int cdns2_req_ep0_handle_status(struct cdns2_device *pdev,
+ struct usb_ctrlrequest *ctrl)
+{
+ struct cdns2_endpoint *pep;
+ __le16 *response_pkt;
+ u16 status = 0;
+ int ep_sts;
+ u32 recip;
+
+ recip = ctrl->bRequestType & USB_RECIP_MASK;
+
+ switch (recip) {
+ case USB_RECIP_DEVICE:
+ status = pdev->gadget.is_selfpowered;
+ status |= pdev->may_wakeup << USB_DEVICE_REMOTE_WAKEUP;
+ break;
+ case USB_RECIP_INTERFACE:
+ return cdns2_ep0_delegate_req(pdev);
+ case USB_RECIP_ENDPOINT:
+ ep_sts = cdns2_w_index_to_ep_index(le16_to_cpu(ctrl->wIndex));
+ pep = &pdev->eps[ep_sts];
+
+ if (pep->ep_state & EP_STALLED)
+ status = BIT(USB_ENDPOINT_HALT);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ put_unaligned_le16(status, (__le16 *)pdev->ep0_preq.request.buf);
+
+ cdns2_ep0_enqueue(pdev, pdev->ep0_preq.request.dma,
+ sizeof(*response_pkt), 0);
+
+ return 0;
+}
+
+static int cdns2_ep0_handle_feature_device(struct cdns2_device *pdev,
+ struct usb_ctrlrequest *ctrl,
+ int set)
+{
+ enum usb_device_state state;
+ enum usb_device_speed speed;
+ int ret = 0;
+ u32 wValue;
+ u16 tmode;
+
+ wValue = le16_to_cpu(ctrl->wValue);
+ state = pdev->gadget.state;
+ speed = pdev->gadget.speed;
+
+ switch (wValue) {
+ case USB_DEVICE_REMOTE_WAKEUP:
+ pdev->may_wakeup = !!set;
+ break;
+ case USB_DEVICE_TEST_MODE:
+ if (state != USB_STATE_CONFIGURED || speed > USB_SPEED_HIGH)
+ return -EINVAL;
+
+ tmode = le16_to_cpu(ctrl->wIndex);
+
+ if (!set || (tmode & 0xff) != 0)
+ return -EINVAL;
+
+ tmode >>= 8;
+ switch (tmode) {
+ case USB_TEST_J:
+ case USB_TEST_K:
+ case USB_TEST_SE0_NAK:
+ case USB_TEST_PACKET:
+ /*
+ * The USBHS controller automatically handles the
+ * Set_Feature(testmode) request. Standard test modes
+ * that use values of test mode selector from
+ * 01h to 04h (Test_J, Test_K, Test_SE0_NAK,
+ * Test_Packet) are supported by the
+ * controller(HS - ack, FS - stall).
+ */
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int cdns2_ep0_handle_feature_intf(struct cdns2_device *pdev,
+ struct usb_ctrlrequest *ctrl,
+ int set)
+{
+ int ret = 0;
+ u32 wValue;
+
+ wValue = le16_to_cpu(ctrl->wValue);
+
+ switch (wValue) {
+ case USB_INTRF_FUNC_SUSPEND:
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int cdns2_ep0_handle_feature_endpoint(struct cdns2_device *pdev,
+ struct usb_ctrlrequest *ctrl,
+ int set)
+{
+ struct cdns2_endpoint *pep;
+ u8 wValue;
+
+ wValue = le16_to_cpu(ctrl->wValue);
+ pep = &pdev->eps[cdns2_w_index_to_ep_index(le16_to_cpu(ctrl->wIndex))];
+
+ if (wValue != USB_ENDPOINT_HALT)
+ return -EINVAL;
+
+ if (!(le16_to_cpu(ctrl->wIndex) & ~USB_DIR_IN))
+ return 0;
+
+ switch (wValue) {
+ case USB_ENDPOINT_HALT:
+ if (set || !(pep->ep_state & EP_WEDGE))
+ return cdns2_halt_endpoint(pdev, pep, set);
+ break;
+ default:
+ dev_warn(pdev->dev, "WARN Incorrect wValue %04x\n", wValue);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cdns2_req_ep0_handle_feature(struct cdns2_device *pdev,
+ struct usb_ctrlrequest *ctrl,
+ int set)
+{
+ switch (ctrl->bRequestType & USB_RECIP_MASK) {
+ case USB_RECIP_DEVICE:
+ return cdns2_ep0_handle_feature_device(pdev, ctrl, set);
+ case USB_RECIP_INTERFACE:
+ return cdns2_ep0_handle_feature_intf(pdev, ctrl, set);
+ case USB_RECIP_ENDPOINT:
+ return cdns2_ep0_handle_feature_endpoint(pdev, ctrl, set);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int cdns2_ep0_std_request(struct cdns2_device *pdev)
+{
+ struct usb_ctrlrequest *ctrl = &pdev->setup;
+ int ret;
+
+ switch (ctrl->bRequest) {
+ case USB_REQ_SET_ADDRESS:
+ ret = cdns2_req_ep0_set_address(pdev,
+ le16_to_cpu(ctrl->wValue));
+ break;
+ case USB_REQ_SET_CONFIGURATION:
+ ret = cdns2_req_ep0_set_configuration(pdev, ctrl);
+ break;
+ case USB_REQ_GET_STATUS:
+ ret = cdns2_req_ep0_handle_status(pdev, ctrl);
+ break;
+ case USB_REQ_CLEAR_FEATURE:
+ ret = cdns2_req_ep0_handle_feature(pdev, ctrl, 0);
+ break;
+ case USB_REQ_SET_FEATURE:
+ ret = cdns2_req_ep0_handle_feature(pdev, ctrl, 1);
+ break;
+ default:
+ ret = cdns2_ep0_delegate_req(pdev);
+ break;
+ }
+
+ return ret;
+}
+
+static void __pending_setup_status_handler(struct cdns2_device *pdev)
+{
+ struct usb_request *request = pdev->pending_status_request;
+
+ if (pdev->status_completion_no_call && request && request->complete) {
+ request->complete(&pdev->eps[0].endpoint, request);
+ pdev->status_completion_no_call = 0;
+ }
+}
+
+void cdns2_pending_setup_status_handler(struct work_struct *work)
+{
+ struct cdns2_device *pdev = container_of(work, struct cdns2_device,
+ pending_status_wq);
+ unsigned long flags;
+
+ spin_lock_irqsave(&pdev->lock, flags);
+ __pending_setup_status_handler(pdev);
+ spin_unlock_irqrestore(&pdev->lock, flags);
+}
+
+void cdns2_handle_setup_packet(struct cdns2_device *pdev)
+{
+ struct usb_ctrlrequest *ctrl = &pdev->setup;
+ struct cdns2_endpoint *pep = &pdev->eps[0];
+ struct cdns2_request *preq;
+ int ret = 0;
+ u16 len;
+ u8 reg;
+ int i;
+
+ writeb(EP0CS_CHGSET, &pdev->ep0_regs->cs);
+
+ for (i = 0; i < 8; i++)
+ ((u8 *)&pdev->setup)[i] = readb(&pdev->ep0_regs->setupdat[i]);
+
+ /*
+ * If SETUP packet was modified while reading just simple ignore it.
+ * The new one will be handled latter.
+ */
+ if (cdns2_check_new_setup(pdev)) {
+ trace_cdns2_ep0_setup("overridden");
+ return;
+ }
+
+ trace_cdns2_ctrl_req(ctrl);
+
+ if (!pdev->gadget_driver)
+ goto out;
+
+ if (pdev->gadget.state == USB_STATE_NOTATTACHED) {
+ dev_err(pdev->dev, "ERR: Setup detected in unattached state\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ pep = &pdev->eps[0];
+
+ /* Halt for Ep0 is cleared automatically when SETUP packet arrives. */
+ pep->ep_state &= ~EP_STALLED;
+
+ if (!list_empty(&pep->pending_list)) {
+ preq = cdns2_next_preq(&pep->pending_list);
+ cdns2_gadget_giveback(pep, preq, -ECONNRESET);
+ }
+
+ len = le16_to_cpu(ctrl->wLength);
+ if (len)
+ pdev->ep0_stage = CDNS2_DATA_STAGE;
+ else
+ pdev->ep0_stage = CDNS2_STATUS_STAGE;
+
+ pep->dir = ctrl->bRequestType & USB_DIR_IN;
+
+ /*
+ * SET_ADDRESS request is acknowledged automatically by controller and
+ * in the worse case driver may not notice this request. To check
+ * whether this request has been processed driver can use
+ * fnaddr register.
+ */
+ reg = readb(&pdev->usb_regs->fnaddr);
+ if (pdev->setup.bRequest != USB_REQ_SET_ADDRESS &&
+ pdev->dev_address != reg)
+ cdns2_req_ep0_set_address(pdev, reg);
+
+ if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)
+ ret = cdns2_ep0_std_request(pdev);
+ else
+ ret = cdns2_ep0_delegate_req(pdev);
+
+ if (ret == USB_GADGET_DELAYED_STATUS) {
+ trace_cdns2_ep0_status_stage("delayed");
+ return;
+ }
+
+out:
+ if (ret < 0)
+ cdns2_ep0_stall(pdev);
+ else if (pdev->ep0_stage == CDNS2_STATUS_STAGE)
+ cdns2_status_stage(pdev);
+}
+
+static void cdns2_transfer_completed(struct cdns2_device *pdev)
+{
+ struct cdns2_endpoint *pep = &pdev->eps[0];
+
+ if (!list_empty(&pep->pending_list)) {
+ struct cdns2_request *preq;
+
+ trace_cdns2_complete_trb(pep, pep->ring.trbs);
+ preq = cdns2_next_preq(&pep->pending_list);
+
+ preq->request.actual =
+ TRB_LEN(le32_to_cpu(pep->ring.trbs->length));
+ cdns2_gadget_giveback(pep, preq, 0);
+ }
+
+ cdns2_status_stage(pdev);
+}
+
+void cdns2_handle_ep0_interrupt(struct cdns2_device *pdev, int dir)
+{
+ u32 ep_sts_reg;
+
+ cdns2_select_ep(pdev, dir);
+
+ trace_cdns2_ep0_irq(pdev);
+
+ ep_sts_reg = readl(&pdev->adma_regs->ep_sts);
+ writel(ep_sts_reg, &pdev->adma_regs->ep_sts);
+
+ __pending_setup_status_handler(pdev);
+
+ if ((ep_sts_reg & DMA_EP_STS_IOC) || (ep_sts_reg & DMA_EP_STS_ISP)) {
+ pdev->eps[0].dir = dir;
+ cdns2_transfer_completed(pdev);
+ }
+}
+
+/*
+ * Function shouldn't be called by gadget driver,
+ * endpoint 0 is allways active.
+ */
+static int cdns2_gadget_ep0_enable(struct usb_ep *ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ return -EINVAL;
+}
+
+/*
+ * Function shouldn't be called by gadget driver,
+ * endpoint 0 is allways active.
+ */
+static int cdns2_gadget_ep0_disable(struct usb_ep *ep)
+{
+ return -EINVAL;
+}
+
+static int cdns2_gadget_ep0_set_halt(struct usb_ep *ep, int value)
+{
+ struct cdns2_endpoint *pep = ep_to_cdns2_ep(ep);
+ struct cdns2_device *pdev = pep->pdev;
+ unsigned long flags;
+
+ if (!value)
+ return 0;
+
+ spin_lock_irqsave(&pdev->lock, flags);
+ cdns2_ep0_stall(pdev);
+ spin_unlock_irqrestore(&pdev->lock, flags);
+
+ return 0;
+}
+
+static int cdns2_gadget_ep0_set_wedge(struct usb_ep *ep)
+{
+ return cdns2_gadget_ep0_set_halt(ep, 1);
+}
+
+static int cdns2_gadget_ep0_queue(struct usb_ep *ep,
+ struct usb_request *request,
+ gfp_t gfp_flags)
+{
+ struct cdns2_endpoint *pep = ep_to_cdns2_ep(ep);
+ struct cdns2_device *pdev = pep->pdev;
+ struct cdns2_request *preq;
+ unsigned long flags;
+ u8 zlp = 0;
+ int ret;
+
+ spin_lock_irqsave(&pdev->lock, flags);
+
+ preq = to_cdns2_request(request);
+
+ trace_cdns2_request_enqueue(preq);
+
+ /* Cancel the request if controller receive new SETUP packet. */
+ if (cdns2_check_new_setup(pdev)) {
+ trace_cdns2_ep0_setup("overridden");
+ spin_unlock_irqrestore(&pdev->lock, flags);
+ return -ECONNRESET;
+ }
+
+ /* Send STATUS stage. Should be called only for SET_CONFIGURATION. */
+ if (pdev->ep0_stage == CDNS2_STATUS_STAGE) {
+ cdns2_status_stage(pdev);
+
+ request->actual = 0;
+ pdev->status_completion_no_call = true;
+ pdev->pending_status_request = request;
+ usb_gadget_set_state(&pdev->gadget, USB_STATE_CONFIGURED);
+ spin_unlock_irqrestore(&pdev->lock, flags);
+
+ /*
+ * Since there is no completion interrupt for status stage,
+ * it needs to call ->completion in software after
+ * cdns2_gadget_ep0_queue is back.
+ */
+ queue_work(system_freezable_wq, &pdev->pending_status_wq);
+ return 0;
+ }
+
+ if (!list_empty(&pep->pending_list)) {
+ trace_cdns2_ep0_setup("pending");
+ dev_err(pdev->dev,
+ "can't handle multiple requests for ep0\n");
+ spin_unlock_irqrestore(&pdev->lock, flags);
+ return -EBUSY;
+ }
+
+ ret = usb_gadget_map_request_by_dev(pdev->dev, request, pep->dir);
+ if (ret) {
+ spin_unlock_irqrestore(&pdev->lock, flags);
+ dev_err(pdev->dev, "failed to map request\n");
+ return -EINVAL;
+ }
+
+ request->status = -EINPROGRESS;
+ list_add_tail(&preq->list, &pep->pending_list);
+
+ if (request->zero && request->length &&
+ (request->length % ep->maxpacket == 0))
+ zlp = 1;
+
+ cdns2_ep0_enqueue(pdev, request->dma, request->length, zlp);
+
+ spin_unlock_irqrestore(&pdev->lock, flags);
+
+ return 0;
+}
+
+static const struct usb_ep_ops cdns2_gadget_ep0_ops = {
+ .enable = cdns2_gadget_ep0_enable,
+ .disable = cdns2_gadget_ep0_disable,
+ .alloc_request = cdns2_gadget_ep_alloc_request,
+ .free_request = cdns2_gadget_ep_free_request,
+ .queue = cdns2_gadget_ep0_queue,
+ .dequeue = cdns2_gadget_ep_dequeue,
+ .set_halt = cdns2_gadget_ep0_set_halt,
+ .set_wedge = cdns2_gadget_ep0_set_wedge,
+};
+
+void cdns2_ep0_config(struct cdns2_device *pdev)
+{
+ struct cdns2_endpoint *pep;
+
+ pep = &pdev->eps[0];
+
+ if (!list_empty(&pep->pending_list)) {
+ struct cdns2_request *preq;
+
+ preq = cdns2_next_preq(&pep->pending_list);
+ list_del_init(&preq->list);
+ }
+
+ writeb(EP0_FIFO_AUTO, &pdev->ep0_regs->fifo);
+ cdns2_select_ep(pdev, USB_DIR_OUT);
+ writel(DMA_EP_CFG_ENABLE, &pdev->adma_regs->ep_cfg);
+
+ writeb(EP0_FIFO_IO_TX | EP0_FIFO_AUTO, &pdev->ep0_regs->fifo);
+ cdns2_select_ep(pdev, USB_DIR_IN);
+ writel(DMA_EP_CFG_ENABLE, &pdev->adma_regs->ep_cfg);
+
+ writeb(pdev->gadget.ep0->maxpacket, &pdev->ep0_regs->maxpack);
+ writel(DMA_EP_IEN_EP_OUT0 | DMA_EP_IEN_EP_IN0,
+ &pdev->adma_regs->ep_ien);
+}
+
+void cdns2_init_ep0(struct cdns2_device *pdev,
+ struct cdns2_endpoint *pep)
+{
+ u16 maxpacket = le16_to_cpu(cdns2_gadget_ep0_desc.wMaxPacketSize);
+
+ usb_ep_set_maxpacket_limit(&pep->endpoint, maxpacket);
+
+ pep->endpoint.ops = &cdns2_gadget_ep0_ops;
+ pep->endpoint.desc = &cdns2_gadget_ep0_desc;
+ pep->endpoint.caps.type_control = true;
+ pep->endpoint.caps.dir_in = true;
+ pep->endpoint.caps.dir_out = true;
+
+ pdev->gadget.ep0 = &pep->endpoint;
+}
diff --git a/drivers/usb/gadget/udc/cdns2/cdns2-gadget.c b/drivers/usb/gadget/udc/cdns2/cdns2-gadget.c
new file mode 100644
index 000000000000..0eed0e03842c
--- /dev/null
+++ b/drivers/usb/gadget/udc/cdns2/cdns2-gadget.c
@@ -0,0 +1,2474 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Cadence USBHS-DEV Driver - gadget side.
+ *
+ * Copyright (C) 2023 Cadence Design Systems.
+ *
+ * Authors: Pawel Laszczak <pawell@cadence.com>
+ */
+
+/*
+ * Work around 1:
+ * At some situations, the controller may get stale data address in TRB
+ * at below sequences:
+ * 1. Controller read TRB includes data address
+ * 2. Software updates TRBs includes data address and Cycle bit
+ * 3. Controller read TRB which includes Cycle bit
+ * 4. DMA run with stale data address
+ *
+ * To fix this problem, driver needs to make the first TRB in TD as invalid.
+ * After preparing all TRBs driver needs to check the position of DMA and
+ * if the DMA point to the first just added TRB and doorbell is 1,
+ * then driver must defer making this TRB as valid. This TRB will be make
+ * as valid during adding next TRB only if DMA is stopped or at TRBERR
+ * interrupt.
+ *
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/pm_runtime.h>
+#include <linux/interrupt.h>
+#include <linux/property.h>
+#include <linux/dmapool.h>
+#include <linux/iopoll.h>
+
+#include "cdns2-gadget.h"
+#include "cdns2-trace.h"
+
+/**
+ * set_reg_bit_32 - set bit in given 32 bits register.
+ * @ptr: register address.
+ * @mask: bits to set.
+ */
+static void set_reg_bit_32(void __iomem *ptr, u32 mask)
+{
+ mask = readl(ptr) | mask;
+ writel(mask, ptr);
+}
+
+/*
+ * clear_reg_bit_32 - clear bit in given 32 bits register.
+ * @ptr: register address.
+ * @mask: bits to clear.
+ */
+static void clear_reg_bit_32(void __iomem *ptr, u32 mask)
+{
+ mask = readl(ptr) & ~mask;
+ writel(mask, ptr);
+}
+
+/* Clear bit in given 8 bits register. */
+static void clear_reg_bit_8(void __iomem *ptr, u8 mask)
+{
+ mask = readb(ptr) & ~mask;
+ writeb(mask, ptr);
+}
+
+/* Set bit in given 16 bits register. */
+void set_reg_bit_8(void __iomem *ptr, u8 mask)
+{
+ mask = readb(ptr) | mask;
+ writeb(mask, ptr);
+}
+
+static int cdns2_get_dma_pos(struct cdns2_device *pdev,
+ struct cdns2_endpoint *pep)
+{
+ int dma_index;
+
+ dma_index = readl(&pdev->adma_regs->ep_traddr) - pep->ring.dma;
+
+ return dma_index / TRB_SIZE;
+}
+
+/* Get next private request from list. */
+struct cdns2_request *cdns2_next_preq(struct list_head *list)
+{
+ return list_first_entry_or_null(list, struct cdns2_request, list);
+}
+
+void cdns2_select_ep(struct cdns2_device *pdev, u32 ep)
+{
+ if (pdev->selected_ep == ep)
+ return;
+
+ pdev->selected_ep = ep;
+ writel(ep, &pdev->adma_regs->ep_sel);
+}
+
+dma_addr_t cdns2_trb_virt_to_dma(struct cdns2_endpoint *pep,
+ struct cdns2_trb *trb)
+{
+ u32 offset = (char *)trb - (char *)pep->ring.trbs;
+
+ return pep->ring.dma + offset;
+}
+
+static void cdns2_free_tr_segment(struct cdns2_endpoint *pep)
+{
+ struct cdns2_device *pdev = pep->pdev;
+ struct cdns2_ring *ring = &pep->ring;
+
+ if (pep->ring.trbs) {
+ dma_pool_free(pdev->eps_dma_pool, ring->trbs, ring->dma);
+ memset(ring, 0, sizeof(*ring));
+ }
+}
+
+/* Allocates Transfer Ring segment. */
+static int cdns2_alloc_tr_segment(struct cdns2_endpoint *pep)
+{
+ struct cdns2_device *pdev = pep->pdev;
+ struct cdns2_trb *link_trb;
+ struct cdns2_ring *ring;
+
+ ring = &pep->ring;
+
+ if (!ring->trbs) {
+ ring->trbs = dma_pool_alloc(pdev->eps_dma_pool,
+ GFP_DMA32 | GFP_ATOMIC,
+ &ring->dma);
+ if (!ring->trbs)
+ return -ENOMEM;
+ }
+
+ memset(ring->trbs, 0, TR_SEG_SIZE);
+
+ if (!pep->num)
+ return 0;
+
+ /* Initialize the last TRB as Link TRB */
+ link_trb = (ring->trbs + (TRBS_PER_SEGMENT - 1));
+ link_trb->buffer = cpu_to_le32(TRB_BUFFER(ring->dma));
+ link_trb->control = cpu_to_le32(TRB_CYCLE | TRB_TYPE(TRB_LINK) |
+ TRB_TOGGLE);
+
+ return 0;
+}
+
+/*
+ * Stalls and flushes selected endpoint.
+ * Endpoint must be selected before invoking this function.
+ */
+static void cdns2_ep_stall_flush(struct cdns2_endpoint *pep)
+{
+ struct cdns2_device *pdev = pep->pdev;
+ int val;
+
+ trace_cdns2_ep_halt(pep, 1, 1);
+
+ writel(DMA_EP_CMD_DFLUSH, &pdev->adma_regs->ep_cmd);
+
+ /* Wait for DFLUSH cleared. */
+ readl_poll_timeout_atomic(&pdev->adma_regs->ep_cmd, val,
+ !(val & DMA_EP_CMD_DFLUSH), 1, 1000);
+ pep->ep_state |= EP_STALLED;
+ pep->ep_state &= ~EP_STALL_PENDING;
+}
+
+/*
+ * Increment a trb index.
+ *
+ * The index should never point to the last link TRB in TR. After incrementing,
+ * if it point to the link TRB, wrap around to the beginning and revert
+ * cycle state bit. The link TRB is always at the last TRB entry.
+ */
+static void cdns2_ep_inc_trb(int *index, u8 *cs, int trb_in_seg)
+{
+ (*index)++;
+ if (*index == (trb_in_seg - 1)) {
+ *index = 0;
+ *cs ^= 1;
+ }
+}
+
+static void cdns2_ep_inc_enq(struct cdns2_ring *ring)
+{
+ ring->free_trbs--;
+ cdns2_ep_inc_trb(&ring->enqueue, &ring->pcs, TRBS_PER_SEGMENT);
+}
+
+static void cdns2_ep_inc_deq(struct cdns2_ring *ring)
+{
+ ring->free_trbs++;
+ cdns2_ep_inc_trb(&ring->dequeue, &ring->ccs, TRBS_PER_SEGMENT);
+}
+
+/*
+ * Enable/disable LPM.
+ *
+ * If bit USBCS_LPMNYET is not set and device receive Extended Token packet,
+ * then controller answer with ACK handshake.
+ * If bit USBCS_LPMNYET is set and device receive Extended Token packet,
+ * then controller answer with NYET handshake.
+ */
+static void cdns2_enable_l1(struct cdns2_device *pdev, int enable)
+{
+ if (enable) {
+ clear_reg_bit_8(&pdev->usb_regs->usbcs, USBCS_LPMNYET);
+ writeb(LPMCLOCK_SLEEP_ENTRY, &pdev->usb_regs->lpmclock);
+ } else {
+ set_reg_bit_8(&pdev->usb_regs->usbcs, USBCS_LPMNYET);
+ }
+}
+
+static enum usb_device_speed cdns2_get_speed(struct cdns2_device *pdev)
+{
+ u8 speed = readb(&pdev->usb_regs->speedctrl);
+
+ if (speed & SPEEDCTRL_HS)
+ return USB_SPEED_HIGH;
+ else if (speed & SPEEDCTRL_FS)
+ return USB_SPEED_FULL;
+
+ return USB_SPEED_UNKNOWN;
+}
+
+static struct cdns2_trb *cdns2_next_trb(struct cdns2_endpoint *pep,
+ struct cdns2_trb *trb)
+{
+ if (trb == (pep->ring.trbs + (TRBS_PER_SEGMENT - 1)))
+ return pep->ring.trbs;
+ else
+ return ++trb;
+}
+
+void cdns2_gadget_giveback(struct cdns2_endpoint *pep,
+ struct cdns2_request *preq,
+ int status)
+{
+ struct usb_request *request = &preq->request;
+ struct cdns2_device *pdev = pep->pdev;
+
+ list_del_init(&preq->list);
+
+ if (request->status == -EINPROGRESS)
+ request->status = status;
+
+ usb_gadget_unmap_request_by_dev(pdev->dev, request, pep->dir);
+
+ /* All TRBs have finished, clear the counter. */
+ preq->finished_trb = 0;
+
+ trace_cdns2_request_giveback(preq);
+
+ if (request->complete) {
+ spin_unlock(&pdev->lock);
+ usb_gadget_giveback_request(&pep->endpoint, request);
+ spin_lock(&pdev->lock);
+ }
+
+ if (request->buf == pdev->zlp_buf)
+ cdns2_gadget_ep_free_request(&pep->endpoint, request);
+}
+
+static void cdns2_wa1_restore_cycle_bit(struct cdns2_endpoint *pep)
+{
+ /* Work around for stale data address in TRB. */
+ if (pep->wa1_set) {
+ trace_cdns2_wa1(pep, "restore cycle bit");
+
+ pep->wa1_set = 0;
+ pep->wa1_trb_index = 0xFFFF;
+ if (pep->wa1_cycle_bit)
+ pep->wa1_trb->control |= cpu_to_le32(0x1);
+ else
+ pep->wa1_trb->control &= cpu_to_le32(~0x1);
+ }
+}
+
+static int cdns2_wa1_update_guard(struct cdns2_endpoint *pep,
+ struct cdns2_trb *trb)
+{
+ struct cdns2_device *pdev = pep->pdev;
+
+ if (!pep->wa1_set) {
+ u32 doorbell;
+
+ doorbell = !!(readl(&pdev->adma_regs->ep_cmd) & DMA_EP_CMD_DRDY);
+
+ if (doorbell) {
+ pep->wa1_cycle_bit = pep->ring.pcs ? TRB_CYCLE : 0;
+ pep->wa1_set = 1;
+ pep->wa1_trb = trb;
+ pep->wa1_trb_index = pep->ring.enqueue;
+ trace_cdns2_wa1(pep, "set guard");
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static void cdns2_wa1_tray_restore_cycle_bit(struct cdns2_device *pdev,
+ struct cdns2_endpoint *pep)
+{
+ int dma_index;
+ u32 doorbell;
+
+ doorbell = !!(readl(&pdev->adma_regs->ep_cmd) & DMA_EP_CMD_DRDY);
+ dma_index = cdns2_get_dma_pos(pdev, pep);
+
+ if (!doorbell || dma_index != pep->wa1_trb_index)
+ cdns2_wa1_restore_cycle_bit(pep);
+}
+
+static int cdns2_prepare_ring(struct cdns2_device *pdev,
+ struct cdns2_endpoint *pep,
+ int num_trbs)
+{
+ struct cdns2_trb *link_trb = NULL;
+ int doorbell, dma_index;
+ struct cdns2_ring *ring;
+ u32 ch_bit = 0;
+
+ ring = &pep->ring;
+
+ if (num_trbs > ring->free_trbs) {
+ pep->ep_state |= EP_RING_FULL;
+ trace_cdns2_no_room_on_ring("Ring full\n");
+ return -ENOBUFS;
+ }
+
+ if ((ring->enqueue + num_trbs) >= (TRBS_PER_SEGMENT - 1)) {
+ doorbell = !!(readl(&pdev->adma_regs->ep_cmd) & DMA_EP_CMD_DRDY);
+ dma_index = cdns2_get_dma_pos(pdev, pep);
+
+ /* Driver can't update LINK TRB if it is current processed. */
+ if (doorbell && dma_index == TRBS_PER_SEGMENT - 1) {
+ pep->ep_state |= EP_DEFERRED_DRDY;
+ return -ENOBUFS;
+ }
+
+ /* Update C bt in Link TRB before starting DMA. */
+ link_trb = ring->trbs + (TRBS_PER_SEGMENT - 1);
+
+ /*
+ * For TRs size equal 2 enabling TRB_CHAIN for epXin causes
+ * that DMA stuck at the LINK TRB.
+ * On the other hand, removing TRB_CHAIN for longer TRs for
+ * epXout cause that DMA stuck after handling LINK TRB.
+ * To eliminate this strange behavioral driver set TRB_CHAIN
+ * bit only for TR size > 2.
+ */
+ if (pep->type == USB_ENDPOINT_XFER_ISOC || TRBS_PER_SEGMENT > 2)
+ ch_bit = TRB_CHAIN;
+
+ link_trb->control = cpu_to_le32(((ring->pcs) ? TRB_CYCLE : 0) |
+ TRB_TYPE(TRB_LINK) | TRB_TOGGLE | ch_bit);
+ }
+
+ return 0;
+}
+
+static void cdns2_dbg_request_trbs(struct cdns2_endpoint *pep,
+ struct cdns2_request *preq)
+{
+ struct cdns2_trb *link_trb = pep->ring.trbs + (TRBS_PER_SEGMENT - 1);
+ struct cdns2_trb *trb = preq->trb;
+ int num_trbs = preq->num_of_trb;
+ int i = 0;
+
+ while (i < num_trbs) {
+ trace_cdns2_queue_trb(pep, trb + i);
+ if (trb + i == link_trb) {
+ trb = pep->ring.trbs;
+ num_trbs = num_trbs - i;
+ i = 0;
+ } else {
+ i++;
+ }
+ }
+}
+
+static unsigned int cdns2_count_trbs(struct cdns2_endpoint *pep,
+ u64 addr, u64 len)
+{
+ unsigned int num_trbs = 1;
+
+ if (pep->type == USB_ENDPOINT_XFER_ISOC) {
+ /*
+ * To speed up DMA performance address should not exceed 4KB.
+ * for high bandwidth transfer and driver will split
+ * such buffer into two TRBs.
+ */
+ num_trbs = DIV_ROUND_UP(len +
+ (addr & (TRB_MAX_ISO_BUFF_SIZE - 1)),
+ TRB_MAX_ISO_BUFF_SIZE);
+
+ if (pep->interval > 1)
+ num_trbs = pep->dir ? num_trbs * pep->interval : 1;
+ } else if (pep->dir) {
+ /*
+ * One extra link trb for IN direction.
+ * Sometimes DMA doesn't want advance to next TD and transfer
+ * hangs. This extra Link TRB force DMA to advance to next TD.
+ */
+ num_trbs++;
+ }
+
+ return num_trbs;
+}
+
+static unsigned int cdns2_count_sg_trbs(struct cdns2_endpoint *pep,
+ struct usb_request *req)
+{
+ unsigned int i, len, full_len, num_trbs = 0;
+ struct scatterlist *sg;
+ int trb_len = 0;
+
+ full_len = req->length;
+
+ for_each_sg(req->sg, sg, req->num_sgs, i) {
+ len = sg_dma_len(sg);
+ num_trbs += cdns2_count_trbs(pep, sg_dma_address(sg), len);
+ len = min(len, full_len);
+
+ /*
+ * For HS ISO transfer TRBs should not exceed max packet size.
+ * When DMA is working, and data exceed max packet size then
+ * some data will be read in single mode instead burst mode.
+ * This behavior will drastically reduce the copying speed.
+ * To avoid this we need one or two extra TRBs.
+ * This issue occurs for UVC class with sg_supported = 1
+ * because buffers addresses are not aligned to 1024.
+ */
+ if (pep->type == USB_ENDPOINT_XFER_ISOC) {
+ u8 temp;
+
+ trb_len += len;
+ temp = trb_len >> 10;
+
+ if (temp) {
+ if (trb_len % 1024)
+ num_trbs = num_trbs + temp;
+ else
+ num_trbs = num_trbs + temp - 1;
+
+ trb_len = trb_len - (temp << 10);
+ }
+ }
+
+ full_len -= len;
+ if (full_len == 0)
+ break;
+ }
+
+ return num_trbs;
+}
+
+/*
+ * Function prepares the array with optimized AXI burst value for different
+ * transfer lengths. Controller handles the final data which are less
+ * then AXI burst size as single byte transactions.
+ * e.g.:
+ * Let's assume that driver prepares trb with trb->length 700 and burst size
+ * will be set to 128. In this case the controller will handle a first 512 as
+ * single AXI transaction but the next 188 bytes will be handled
+ * as 47 separate AXI transaction.
+ * The better solution is to use the burst size equal 16 and then we will
+ * have only 25 AXI transaction (10 * 64 + 15 *4).
+ */
+static void cdsn2_isoc_burst_opt(struct cdns2_device *pdev)
+{
+ int axi_burst_option[] = {1, 2, 4, 8, 16, 32, 64, 128};
+ int best_burst;
+ int array_size;
+ int opt_burst;
+ int trb_size;
+ int i, j;
+
+ array_size = ARRAY_SIZE(axi_burst_option);
+
+ for (i = 0; i <= MAX_ISO_SIZE; i++) {
+ trb_size = i / 4;
+ best_burst = trb_size ? trb_size : 1;
+
+ for (j = 0; j < array_size; j++) {
+ opt_burst = trb_size / axi_burst_option[j];
+ opt_burst += trb_size % axi_burst_option[j];
+
+ if (opt_burst < best_burst) {
+ best_burst = opt_burst;
+ pdev->burst_opt[i] = axi_burst_option[j];
+ }
+ }
+ }
+}
+
+static void cdns2_ep_tx_isoc(struct cdns2_endpoint *pep,
+ struct cdns2_request *preq,
+ int num_trbs)
+{
+ struct scatterlist *sg = NULL;
+ u32 remaining_packet_size = 0;
+ struct cdns2_trb *trb;
+ bool first_trb = true;
+ dma_addr_t trb_dma;
+ u32 trb_buff_len;
+ u32 block_length;
+ int td_idx = 0;
+ int split_size;
+ u32 full_len;
+ int enqd_len;
+ int sent_len;
+ int sg_iter;
+ u32 control;
+ int num_tds;
+ u32 length;
+
+ /*
+ * For OUT direction 1 TD per interval is enough
+ * because TRBs are not dumped by controller.
+ */
+ num_tds = pep->dir ? pep->interval : 1;
+ split_size = preq->request.num_sgs ? 1024 : 3072;
+
+ for (td_idx = 0; td_idx < num_tds; td_idx++) {
+ if (preq->request.num_sgs) {
+ sg = preq->request.sg;
+ trb_dma = sg_dma_address(sg);
+ block_length = sg_dma_len(sg);
+ } else {
+ trb_dma = preq->request.dma;
+ block_length = preq->request.length;
+ }
+
+ full_len = preq->request.length;
+ sg_iter = preq->request.num_sgs ? preq->request.num_sgs : 1;
+ remaining_packet_size = split_size;
+
+ for (enqd_len = 0; enqd_len < full_len;
+ enqd_len += trb_buff_len) {
+ if (remaining_packet_size == 0)
+ remaining_packet_size = split_size;
+
+ /*
+ * Calculate TRB length.- buffer can't across 4KB
+ * and max packet size.
+ */
+ trb_buff_len = TRB_BUFF_LEN_UP_TO_BOUNDARY(trb_dma);
+ trb_buff_len = min(trb_buff_len, remaining_packet_size);
+ trb_buff_len = min(trb_buff_len, block_length);
+
+ if (trb_buff_len > full_len - enqd_len)
+ trb_buff_len = full_len - enqd_len;
+
+ control = TRB_TYPE(TRB_NORMAL);
+
+ /*
+ * For IN direction driver has to set the IOC for
+ * last TRB in last TD.
+ * For OUT direction driver must set IOC and ISP
+ * only for last TRB in each TDs.
+ */
+ if (enqd_len + trb_buff_len >= full_len || !pep->dir)
+ control |= TRB_IOC | TRB_ISP;
+
+ /*
+ * Don't give the first TRB to the hardware (by toggling
+ * the cycle bit) until we've finished creating all the
+ * other TRBs.
+ */
+ if (first_trb) {
+ first_trb = false;
+ if (pep->ring.pcs == 0)
+ control |= TRB_CYCLE;
+ } else {
+ control |= pep->ring.pcs;
+ }
+
+ if (enqd_len + trb_buff_len < full_len)
+ control |= TRB_CHAIN;
+
+ length = TRB_LEN(trb_buff_len) |
+ TRB_BURST(pep->pdev->burst_opt[trb_buff_len]);
+
+ trb = pep->ring.trbs + pep->ring.enqueue;
+ trb->buffer = cpu_to_le32(TRB_BUFFER(trb_dma));
+ trb->length = cpu_to_le32(length);
+ trb->control = cpu_to_le32(control);
+
+ trb_dma += trb_buff_len;
+ sent_len = trb_buff_len;
+
+ if (sg && sent_len >= block_length) {
+ /* New sg entry */
+ --sg_iter;
+ sent_len -= block_length;
+ if (sg_iter != 0) {
+ sg = sg_next(sg);
+ trb_dma = sg_dma_address(sg);
+ block_length = sg_dma_len(sg);
+ }
+ }
+
+ remaining_packet_size -= trb_buff_len;
+ block_length -= sent_len;
+ preq->end_trb = pep->ring.enqueue;
+
+ cdns2_ep_inc_enq(&pep->ring);
+ }
+ }
+}
+
+static void cdns2_ep_tx_bulk(struct cdns2_endpoint *pep,
+ struct cdns2_request *preq,
+ int trbs_per_td)
+{
+ struct scatterlist *sg = NULL;
+ struct cdns2_ring *ring;
+ struct cdns2_trb *trb;
+ dma_addr_t trb_dma;
+ int sg_iter = 0;
+ u32 control;
+ u32 length;
+
+ if (preq->request.num_sgs) {
+ sg = preq->request.sg;
+ trb_dma = sg_dma_address(sg);
+ length = sg_dma_len(sg);
+ } else {
+ trb_dma = preq->request.dma;
+ length = preq->request.length;
+ }
+
+ ring = &pep->ring;
+
+ for (sg_iter = 0; sg_iter < trbs_per_td; sg_iter++) {
+ control = TRB_TYPE(TRB_NORMAL) | ring->pcs | TRB_ISP;
+ trb = pep->ring.trbs + ring->enqueue;
+
+ if (pep->dir && sg_iter == trbs_per_td - 1) {
+ preq->end_trb = ring->enqueue;
+ control = ring->pcs | TRB_TYPE(TRB_LINK) | TRB_CHAIN
+ | TRB_IOC;
+ cdns2_ep_inc_enq(&pep->ring);
+
+ if (ring->enqueue == 0)
+ control |= TRB_TOGGLE;
+
+ /* Point to next bad TRB. */
+ trb->buffer = cpu_to_le32(pep->ring.dma +
+ (ring->enqueue * TRB_SIZE));
+ trb->length = 0;
+ trb->control = cpu_to_le32(control);
+ break;
+ }
+
+ /*
+ * Don't give the first TRB to the hardware (by toggling
+ * the cycle bit) until we've finished creating all the
+ * other TRBs.
+ */
+ if (sg_iter == 0)
+ control = control ^ TRB_CYCLE;
+
+ /* For last TRB in TD. */
+ if (sg_iter == (trbs_per_td - (pep->dir ? 2 : 1)))
+ control |= TRB_IOC;
+ else
+ control |= TRB_CHAIN;
+
+ trb->buffer = cpu_to_le32(trb_dma);
+ trb->length = cpu_to_le32(TRB_BURST(pep->trb_burst_size) |
+ TRB_LEN(length));
+ trb->control = cpu_to_le32(control);
+
+ if (sg && sg_iter < (trbs_per_td - 1)) {
+ sg = sg_next(sg);
+ trb_dma = sg_dma_address(sg);
+ length = sg_dma_len(sg);
+ }
+
+ preq->end_trb = ring->enqueue;
+ cdns2_ep_inc_enq(&pep->ring);
+ }
+}
+
+static void cdns2_set_drdy(struct cdns2_device *pdev,
+ struct cdns2_endpoint *pep)
+{
+ trace_cdns2_ring(pep);
+
+ /*
+ * Memory barrier - Cycle Bit must be set before doorbell.
+ */
+ dma_wmb();
+
+ /* Clearing TRBERR and DESCMIS before setting DRDY. */
+ writel(DMA_EP_STS_TRBERR | DMA_EP_STS_DESCMIS,
+ &pdev->adma_regs->ep_sts);
+ writel(DMA_EP_CMD_DRDY, &pdev->adma_regs->ep_cmd);
+
+ if (readl(&pdev->adma_regs->ep_sts) & DMA_EP_STS_TRBERR) {
+ writel(DMA_EP_STS_TRBERR, &pdev->adma_regs->ep_sts);
+ writel(DMA_EP_CMD_DRDY, &pdev->adma_regs->ep_cmd);
+ }
+
+ trace_cdns2_doorbell_epx(pep, readl(&pdev->adma_regs->ep_traddr));
+}
+
+static int cdns2_prepare_first_isoc_transfer(struct cdns2_device *pdev,
+ struct cdns2_endpoint *pep)
+{
+ struct cdns2_trb *trb;
+ u32 buffer;
+ u8 hw_ccs;
+
+ if ((readl(&pdev->adma_regs->ep_cmd) & DMA_EP_CMD_DRDY))
+ return -EBUSY;
+
+ if (!pep->dir) {
+ set_reg_bit_32(&pdev->adma_regs->ep_cfg, DMA_EP_CFG_ENABLE);
+ writel(pep->ring.dma + pep->ring.dequeue,
+ &pdev->adma_regs->ep_traddr);
+ return 0;
+ }
+
+ /*
+ * The first packet after doorbell can be corrupted so,
+ * driver prepares 0 length packet as first packet.
+ */
+ buffer = pep->ring.dma + pep->ring.dequeue * TRB_SIZE;
+ hw_ccs = !!DMA_EP_STS_CCS(readl(&pdev->adma_regs->ep_sts));
+
+ trb = &pep->ring.trbs[TRBS_PER_SEGMENT];
+ trb->length = 0;
+ trb->buffer = cpu_to_le32(TRB_BUFFER(buffer));
+ trb->control = cpu_to_le32((hw_ccs ? TRB_CYCLE : 0) | TRB_TYPE(TRB_NORMAL));
+
+ /*
+ * LINK TRB is used to force updating cycle bit in controller and
+ * move to correct place in transfer ring.
+ */
+ trb++;
+ trb->length = 0;
+ trb->buffer = cpu_to_le32(TRB_BUFFER(buffer));
+ trb->control = cpu_to_le32((hw_ccs ? TRB_CYCLE : 0) |
+ TRB_TYPE(TRB_LINK) | TRB_CHAIN);
+
+ if (hw_ccs != pep->ring.ccs)
+ trb->control |= cpu_to_le32(TRB_TOGGLE);
+
+ set_reg_bit_32(&pdev->adma_regs->ep_cfg, DMA_EP_CFG_ENABLE);
+ writel(pep->ring.dma + (TRBS_PER_SEGMENT * TRB_SIZE),
+ &pdev->adma_regs->ep_traddr);
+
+ return 0;
+}
+
+/* Prepare and start transfer on no-default endpoint. */
+static int cdns2_ep_run_transfer(struct cdns2_endpoint *pep,
+ struct cdns2_request *preq)
+{
+ struct cdns2_device *pdev = pep->pdev;
+ struct cdns2_ring *ring;
+ u32 togle_pcs = 1;
+ int num_trbs;
+ int ret;
+
+ cdns2_select_ep(pdev, pep->endpoint.address);
+
+ if (preq->request.sg)
+ num_trbs = cdns2_count_sg_trbs(pep, &preq->request);
+ else
+ num_trbs = cdns2_count_trbs(pep, preq->request.dma,
+ preq->request.length);
+
+ ret = cdns2_prepare_ring(pdev, pep, num_trbs);
+ if (ret)
+ return ret;
+
+ ring = &pep->ring;
+ preq->start_trb = ring->enqueue;
+ preq->trb = ring->trbs + ring->enqueue;
+
+ if (usb_endpoint_xfer_isoc(pep->endpoint.desc)) {
+ cdns2_ep_tx_isoc(pep, preq, num_trbs);
+ } else {
+ togle_pcs = cdns2_wa1_update_guard(pep, ring->trbs + ring->enqueue);
+ cdns2_ep_tx_bulk(pep, preq, num_trbs);
+ }
+
+ preq->num_of_trb = num_trbs;
+
+ /*
+ * Memory barrier - cycle bit must be set as the last operation.
+ */
+ dma_wmb();
+
+ /* Give the TD to the consumer. */
+ if (togle_pcs)
+ preq->trb->control = preq->trb->control ^ cpu_to_le32(1);
+
+ cdns2_wa1_tray_restore_cycle_bit(pdev, pep);
+ cdns2_dbg_request_trbs(pep, preq);
+
+ if (!pep->wa1_set && !(pep->ep_state & EP_STALLED) && !pep->skip) {
+ if (pep->type == USB_ENDPOINT_XFER_ISOC) {
+ ret = cdns2_prepare_first_isoc_transfer(pdev, pep);
+ if (ret)
+ return 0;
+ }
+
+ cdns2_set_drdy(pdev, pep);
+ }
+
+ return 0;
+}
+
+/* Prepare and start transfer for all not started requests. */
+static int cdns2_start_all_request(struct cdns2_device *pdev,
+ struct cdns2_endpoint *pep)
+{
+ struct cdns2_request *preq;
+ int ret;
+
+ while (!list_empty(&pep->deferred_list)) {
+ preq = cdns2_next_preq(&pep->deferred_list);
+
+ ret = cdns2_ep_run_transfer(pep, preq);
+ if (ret)
+ return ret;
+
+ list_move_tail(&preq->list, &pep->pending_list);
+ }
+
+ pep->ep_state &= ~EP_RING_FULL;
+
+ return 0;
+}
+
+/*
+ * Check whether trb has been handled by DMA.
+ *
+ * Endpoint must be selected before invoking this function.
+ *
+ * Returns false if request has not been handled by DMA, else returns true.
+ *
+ * SR - start ring
+ * ER - end ring
+ * DQ = ring->dequeue - dequeue position
+ * EQ = ring->enqueue - enqueue position
+ * ST = preq->start_trb - index of first TRB in transfer ring
+ * ET = preq->end_trb - index of last TRB in transfer ring
+ * CI = current_index - index of processed TRB by DMA.
+ *
+ * As first step, we check if the TRB between the ST and ET.
+ * Then, we check if cycle bit for index pep->dequeue
+ * is correct.
+ *
+ * some rules:
+ * 1. ring->dequeue never equals to current_index.
+ * 2 ring->enqueue never exceed ring->dequeue
+ * 3. exception: ring->enqueue == ring->dequeue
+ * and ring->free_trbs is zero.
+ * This case indicate that TR is full.
+ *
+ * At below two cases, the request have been handled.
+ * Case 1 - ring->dequeue < current_index
+ * SR ... EQ ... DQ ... CI ... ER
+ * SR ... DQ ... CI ... EQ ... ER
+ *
+ * Case 2 - ring->dequeue > current_index
+ * This situation takes place when CI go through the LINK TRB at the end of
+ * transfer ring.
+ * SR ... CI ... EQ ... DQ ... ER
+ */
+static bool cdns2_trb_handled(struct cdns2_endpoint *pep,
+ struct cdns2_request *preq)
+{
+ struct cdns2_device *pdev = pep->pdev;
+ struct cdns2_ring *ring;
+ struct cdns2_trb *trb;
+ int current_index = 0;
+ int handled = 0;
+ int doorbell;
+
+ ring = &pep->ring;
+ current_index = cdns2_get_dma_pos(pdev, pep);
+ doorbell = !!(readl(&pdev->adma_regs->ep_cmd) & DMA_EP_CMD_DRDY);
+
+ /*
+ * Only ISO transfer can use 2 entries outside the standard
+ * Transfer Ring. First of them is used as zero length packet and the
+ * second as LINK TRB.
+ */
+ if (current_index >= TRBS_PER_SEGMENT)
+ goto finish;
+
+ /* Current trb doesn't belong to this request. */
+ if (preq->start_trb < preq->end_trb) {
+ if (ring->dequeue > preq->end_trb)
+ goto finish;
+
+ if (ring->dequeue < preq->start_trb)
+ goto finish;
+ }
+
+ if (preq->start_trb > preq->end_trb && ring->dequeue > preq->end_trb &&
+ ring->dequeue < preq->start_trb)
+ goto finish;
+
+ if (preq->start_trb == preq->end_trb && ring->dequeue != preq->end_trb)
+ goto finish;
+
+ trb = &ring->trbs[ring->dequeue];
+
+ if ((le32_to_cpu(trb->control) & TRB_CYCLE) != ring->ccs)
+ goto finish;
+
+ if (doorbell == 1 && current_index == ring->dequeue)
+ goto finish;
+
+ /* The corner case for TRBS_PER_SEGMENT equal 2). */
+ if (TRBS_PER_SEGMENT == 2 && pep->type != USB_ENDPOINT_XFER_ISOC) {
+ handled = 1;
+ goto finish;
+ }
+
+ if (ring->enqueue == ring->dequeue &&
+ ring->free_trbs == 0) {
+ handled = 1;
+ } else if (ring->dequeue < current_index) {
+ if ((current_index == (TRBS_PER_SEGMENT - 1)) &&
+ !ring->dequeue)
+ goto finish;
+
+ handled = 1;
+ } else if (ring->dequeue > current_index) {
+ handled = 1;
+ }
+
+finish:
+ trace_cdns2_request_handled(preq, current_index, handled);
+
+ return handled;
+}
+
+static void cdns2_skip_isoc_td(struct cdns2_device *pdev,
+ struct cdns2_endpoint *pep,
+ struct cdns2_request *preq)
+{
+ struct cdns2_trb *trb;
+ int i;
+
+ trb = pep->ring.trbs + pep->ring.dequeue;
+
+ for (i = preq->finished_trb ; i < preq->num_of_trb; i++) {
+ preq->finished_trb++;
+ trace_cdns2_complete_trb(pep, trb);
+ cdns2_ep_inc_deq(&pep->ring);
+ trb = cdns2_next_trb(pep, trb);
+ }
+
+ cdns2_gadget_giveback(pep, preq, 0);
+ cdns2_prepare_first_isoc_transfer(pdev, pep);
+ pep->skip = false;
+ cdns2_set_drdy(pdev, pep);
+}
+
+static void cdns2_transfer_completed(struct cdns2_device *pdev,
+ struct cdns2_endpoint *pep)
+{
+ struct cdns2_request *preq = NULL;
+ bool request_handled = false;
+ struct cdns2_trb *trb;
+
+ while (!list_empty(&pep->pending_list)) {
+ preq = cdns2_next_preq(&pep->pending_list);
+ trb = pep->ring.trbs + pep->ring.dequeue;
+
+ /*
+ * The TRB was changed as link TRB, and the request
+ * was handled at ep_dequeue.
+ */
+ while (TRB_FIELD_TO_TYPE(le32_to_cpu(trb->control)) == TRB_LINK &&
+ le32_to_cpu(trb->length)) {
+ trace_cdns2_complete_trb(pep, trb);
+ cdns2_ep_inc_deq(&pep->ring);
+ trb = pep->ring.trbs + pep->ring.dequeue;
+ }
+
+ /*
+ * Re-select endpoint. It could be changed by other CPU
+ * during handling usb_gadget_giveback_request.
+ */
+ cdns2_select_ep(pdev, pep->endpoint.address);
+
+ while (cdns2_trb_handled(pep, preq)) {
+ preq->finished_trb++;
+
+ if (preq->finished_trb >= preq->num_of_trb)
+ request_handled = true;
+
+ trb = pep->ring.trbs + pep->ring.dequeue;
+ trace_cdns2_complete_trb(pep, trb);
+
+ if (pep->dir && pep->type == USB_ENDPOINT_XFER_ISOC)
+ /*
+ * For ISOC IN controller doens't update the
+ * trb->length.
+ */
+ preq->request.actual = preq->request.length;
+ else
+ preq->request.actual +=
+ TRB_LEN(le32_to_cpu(trb->length));
+
+ cdns2_ep_inc_deq(&pep->ring);
+ }
+
+ if (request_handled) {
+ cdns2_gadget_giveback(pep, preq, 0);
+ request_handled = false;
+ } else {
+ goto prepare_next_td;
+ }
+
+ if (pep->type != USB_ENDPOINT_XFER_ISOC &&
+ TRBS_PER_SEGMENT == 2)
+ break;
+ }
+
+prepare_next_td:
+ if (pep->skip && preq)
+ cdns2_skip_isoc_td(pdev, pep, preq);
+
+ if (!(pep->ep_state & EP_STALLED) &&
+ !(pep->ep_state & EP_STALL_PENDING))
+ cdns2_start_all_request(pdev, pep);
+}
+
+static void cdns2_wakeup(struct cdns2_device *pdev)
+{
+ if (!pdev->may_wakeup)
+ return;
+
+ /* Start driving resume signaling to indicate remote wakeup. */
+ set_reg_bit_8(&pdev->usb_regs->usbcs, USBCS_SIGRSUME);
+}
+
+static void cdns2_rearm_transfer(struct cdns2_endpoint *pep, u8 rearm)
+{
+ struct cdns2_device *pdev = pep->pdev;
+
+ cdns2_wa1_restore_cycle_bit(pep);
+
+ if (rearm) {
+ trace_cdns2_ring(pep);
+
+ /* Cycle Bit must be updated before arming DMA. */
+ dma_wmb();
+
+ writel(DMA_EP_CMD_DRDY, &pdev->adma_regs->ep_cmd);
+
+ cdns2_wakeup(pdev);
+
+ trace_cdns2_doorbell_epx(pep,
+ readl(&pdev->adma_regs->ep_traddr));
+ }
+}
+
+static void cdns2_handle_epx_interrupt(struct cdns2_endpoint *pep)
+{
+ struct cdns2_device *pdev = pep->pdev;
+ u8 isoerror = 0;
+ u32 ep_sts_reg;
+ u32 val;
+
+ cdns2_select_ep(pdev, pep->endpoint.address);
+
+ trace_cdns2_epx_irq(pdev, pep);
+
+ ep_sts_reg = readl(&pdev->adma_regs->ep_sts);
+ writel(ep_sts_reg, &pdev->adma_regs->ep_sts);
+
+ if (pep->type == USB_ENDPOINT_XFER_ISOC) {
+ u8 mult;
+ u8 cs;
+
+ mult = USB_EP_MAXP_MULT(pep->endpoint.desc->wMaxPacketSize);
+ cs = pep->dir ? readb(&pdev->epx_regs->ep[pep->num - 1].txcs) :
+ readb(&pdev->epx_regs->ep[pep->num - 1].rxcs);
+ if (mult > 0)
+ isoerror = EPX_CS_ERR(cs);
+ }
+
+ /*
+ * Sometimes ISO Error for mult=1 or mult=2 is not propagated on time
+ * from USB module to DMA module. To protect against this driver
+ * checks also the txcs/rxcs registers.
+ */
+ if ((ep_sts_reg & DMA_EP_STS_ISOERR) || isoerror) {
+ clear_reg_bit_32(&pdev->adma_regs->ep_cfg, DMA_EP_CFG_ENABLE);
+
+ /* Wait for DBUSY cleared. */
+ readl_poll_timeout_atomic(&pdev->adma_regs->ep_sts, val,
+ !(val & DMA_EP_STS_DBUSY), 1, 125);
+
+ writel(DMA_EP_CMD_DFLUSH, &pep->pdev->adma_regs->ep_cmd);
+
+ /* Wait for DFLUSH cleared. */
+ readl_poll_timeout_atomic(&pep->pdev->adma_regs->ep_cmd, val,
+ !(val & DMA_EP_CMD_DFLUSH), 1, 10);
+
+ pep->skip = true;
+ }
+
+ if (ep_sts_reg & DMA_EP_STS_TRBERR || pep->skip) {
+ if (pep->ep_state & EP_STALL_PENDING &&
+ !(ep_sts_reg & DMA_EP_STS_DESCMIS))
+ cdns2_ep_stall_flush(pep);
+
+ /*
+ * For isochronous transfer driver completes request on
+ * IOC or on TRBERR. IOC appears only when device receive
+ * OUT data packet. If host disable stream or lost some packet
+ * then the only way to finish all queued transfer is to do it
+ * on TRBERR event.
+ */
+ if (pep->type == USB_ENDPOINT_XFER_ISOC && !pep->wa1_set) {
+ if (!pep->dir)
+ clear_reg_bit_32(&pdev->adma_regs->ep_cfg,
+ DMA_EP_CFG_ENABLE);
+
+ cdns2_transfer_completed(pdev, pep);
+ if (pep->ep_state & EP_DEFERRED_DRDY) {
+ pep->ep_state &= ~EP_DEFERRED_DRDY;
+ cdns2_set_drdy(pdev, pep);
+ }
+
+ return;
+ }
+
+ cdns2_transfer_completed(pdev, pep);
+
+ if (!(pep->ep_state & EP_STALLED) &&
+ !(pep->ep_state & EP_STALL_PENDING)) {
+ if (pep->ep_state & EP_DEFERRED_DRDY) {
+ pep->ep_state &= ~EP_DEFERRED_DRDY;
+ cdns2_start_all_request(pdev, pep);
+ } else {
+ cdns2_rearm_transfer(pep, pep->wa1_set);
+ }
+ }
+
+ return;
+ }
+
+ if ((ep_sts_reg & DMA_EP_STS_IOC) || (ep_sts_reg & DMA_EP_STS_ISP))
+ cdns2_transfer_completed(pdev, pep);
+}
+
+static void cdns2_disconnect_gadget(struct cdns2_device *pdev)
+{
+ if (pdev->gadget_driver && pdev->gadget_driver->disconnect)
+ pdev->gadget_driver->disconnect(&pdev->gadget);
+}
+
+static irqreturn_t cdns2_usb_irq_handler(int irq, void *data)
+{
+ struct cdns2_device *pdev = data;
+ unsigned long reg_ep_ists;
+ u8 reg_usb_irq_m;
+ u8 reg_ext_irq_m;
+ u8 reg_usb_irq;
+ u8 reg_ext_irq;
+
+ if (pdev->in_lpm)
+ return IRQ_NONE;
+
+ reg_usb_irq_m = readb(&pdev->interrupt_regs->usbien);
+ reg_ext_irq_m = readb(&pdev->interrupt_regs->extien);
+
+ /* Mask all sources of interrupt. */
+ writeb(0, &pdev->interrupt_regs->usbien);
+ writeb(0, &pdev->interrupt_regs->extien);
+ writel(0, &pdev->adma_regs->ep_ien);
+
+ /* Clear interrupt sources. */
+ writel(0, &pdev->adma_regs->ep_sts);
+ writeb(0, &pdev->interrupt_regs->usbirq);
+ writeb(0, &pdev->interrupt_regs->extirq);
+
+ reg_ep_ists = readl(&pdev->adma_regs->ep_ists);
+ reg_usb_irq = readb(&pdev->interrupt_regs->usbirq);
+ reg_ext_irq = readb(&pdev->interrupt_regs->extirq);
+
+ if (reg_ep_ists || (reg_usb_irq & reg_usb_irq_m) ||
+ (reg_ext_irq & reg_ext_irq_m))
+ return IRQ_WAKE_THREAD;
+
+ writeb(USB_IEN_INIT, &pdev->interrupt_regs->usbien);
+ writeb(EXTIRQ_WAKEUP, &pdev->interrupt_regs->extien);
+ writel(~0, &pdev->adma_regs->ep_ien);
+
+ return IRQ_NONE;
+}
+
+static irqreturn_t cdns2_thread_usb_irq_handler(struct cdns2_device *pdev)
+{
+ u8 usb_irq, ext_irq;
+ int speed;
+ int i;
+
+ ext_irq = readb(&pdev->interrupt_regs->extirq) & EXTIRQ_WAKEUP;
+ writeb(ext_irq, &pdev->interrupt_regs->extirq);
+
+ usb_irq = readb(&pdev->interrupt_regs->usbirq) & USB_IEN_INIT;
+ writeb(usb_irq, &pdev->interrupt_regs->usbirq);
+
+ if (!ext_irq && !usb_irq)
+ return IRQ_NONE;
+
+ trace_cdns2_usb_irq(usb_irq, ext_irq);
+
+ if (ext_irq & EXTIRQ_WAKEUP) {
+ if (pdev->gadget_driver && pdev->gadget_driver->resume) {
+ spin_unlock(&pdev->lock);
+ pdev->gadget_driver->resume(&pdev->gadget);
+ spin_lock(&pdev->lock);
+ }
+ }
+
+ if (usb_irq & USBIRQ_LPM) {
+ u8 reg = readb(&pdev->usb_regs->lpmctrl);
+
+ /* LPM1 enter */
+ if (!(reg & LPMCTRLLH_LPMNYET))
+ writeb(0, &pdev->usb_regs->sleep_clkgate);
+ }
+
+ if (usb_irq & USBIRQ_SUSPEND) {
+ if (pdev->gadget_driver && pdev->gadget_driver->suspend) {
+ spin_unlock(&pdev->lock);
+ pdev->gadget_driver->suspend(&pdev->gadget);
+ spin_lock(&pdev->lock);
+ }
+ }
+
+ if (usb_irq & USBIRQ_URESET) {
+ if (pdev->gadget_driver) {
+ pdev->dev_address = 0;
+
+ spin_unlock(&pdev->lock);
+ usb_gadget_udc_reset(&pdev->gadget,
+ pdev->gadget_driver);
+ spin_lock(&pdev->lock);
+
+ /*
+ * The USBIRQ_URESET is reported at the beginning of
+ * reset signal. 100ms is enough time to finish reset
+ * process. For high-speed reset procedure is completed
+ * when controller detect HS mode.
+ */
+ for (i = 0; i < 100; i++) {
+ mdelay(1);
+ speed = cdns2_get_speed(pdev);
+ if (speed == USB_SPEED_HIGH)
+ break;
+ }
+
+ pdev->gadget.speed = speed;
+ cdns2_enable_l1(pdev, 0);
+ cdns2_ep0_config(pdev);
+ pdev->may_wakeup = 0;
+ }
+ }
+
+ if (usb_irq & USBIRQ_SUDAV) {
+ pdev->ep0_stage = CDNS2_SETUP_STAGE;
+ cdns2_handle_setup_packet(pdev);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/* Deferred USB interrupt handler. */
+static irqreturn_t cdns2_thread_irq_handler(int irq, void *data)
+{
+ struct cdns2_device *pdev = data;
+ unsigned long dma_ep_ists;
+ unsigned long flags;
+ unsigned int bit;
+
+ local_bh_disable();
+ spin_lock_irqsave(&pdev->lock, flags);
+
+ cdns2_thread_usb_irq_handler(pdev);
+
+ dma_ep_ists = readl(&pdev->adma_regs->ep_ists);
+ if (!dma_ep_ists)
+ goto unlock;
+
+ trace_cdns2_dma_ep_ists(dma_ep_ists);
+
+ /* Handle default endpoint OUT. */
+ if (dma_ep_ists & DMA_EP_ISTS_EP_OUT0)
+ cdns2_handle_ep0_interrupt(pdev, USB_DIR_OUT);
+
+ /* Handle default endpoint IN. */
+ if (dma_ep_ists & DMA_EP_ISTS_EP_IN0)
+ cdns2_handle_ep0_interrupt(pdev, USB_DIR_IN);
+
+ dma_ep_ists &= ~(DMA_EP_ISTS_EP_OUT0 | DMA_EP_ISTS_EP_IN0);
+
+ for_each_set_bit(bit, &dma_ep_ists, sizeof(u32) * BITS_PER_BYTE) {
+ u8 ep_idx = bit > 16 ? (bit - 16) * 2 : (bit * 2) - 1;
+
+ /*
+ * Endpoints in pdev->eps[] are held in order:
+ * ep0, ep1out, ep1in, ep2out, ep2in... ep15out, ep15in.
+ * but in dma_ep_ists in order:
+ * ep0 ep1out ep2out ... ep15out ep0in ep1in .. ep15in
+ */
+ cdns2_handle_epx_interrupt(&pdev->eps[ep_idx]);
+ }
+
+unlock:
+ writel(~0, &pdev->adma_regs->ep_ien);
+ writeb(USB_IEN_INIT, &pdev->interrupt_regs->usbien);
+ writeb(EXTIRQ_WAKEUP, &pdev->interrupt_regs->extien);
+
+ spin_unlock_irqrestore(&pdev->lock, flags);
+ local_bh_enable();
+
+ return IRQ_HANDLED;
+}
+
+/* Calculates and assigns onchip memory for endpoints. */
+static void cdns2_eps_onchip_buffer_init(struct cdns2_device *pdev)
+{
+ struct cdns2_endpoint *pep;
+ int min_buf_tx = 0;
+ int min_buf_rx = 0;
+ u16 tx_offset = 0;
+ u16 rx_offset = 0;
+ int free;
+ int i;
+
+ for (i = 0; i < CDNS2_ENDPOINTS_NUM; i++) {
+ pep = &pdev->eps[i];
+
+ if (!(pep->ep_state & EP_CLAIMED))
+ continue;
+
+ if (pep->dir)
+ min_buf_tx += pep->buffering;
+ else
+ min_buf_rx += pep->buffering;
+ }
+
+ for (i = 0; i < CDNS2_ENDPOINTS_NUM; i++) {
+ pep = &pdev->eps[i];
+
+ if (!(pep->ep_state & EP_CLAIMED))
+ continue;
+
+ if (pep->dir) {
+ free = pdev->onchip_tx_buf - min_buf_tx;
+
+ if (free + pep->buffering >= 4)
+ free = 4;
+ else
+ free = free + pep->buffering;
+
+ min_buf_tx = min_buf_tx - pep->buffering + free;
+
+ pep->buffering = free;
+
+ writel(tx_offset,
+ &pdev->epx_regs->txstaddr[pep->num - 1]);
+ pdev->epx_regs->txstaddr[pep->num - 1] = tx_offset;
+
+ dev_dbg(pdev->dev, "%s onchip address %04x, buffering: %d\n",
+ pep->name, tx_offset, pep->buffering);
+
+ tx_offset += pep->buffering * 1024;
+ } else {
+ free = pdev->onchip_rx_buf - min_buf_rx;
+
+ if (free + pep->buffering >= 4)
+ free = 4;
+ else
+ free = free + pep->buffering;
+
+ min_buf_rx = min_buf_rx - pep->buffering + free;
+
+ pep->buffering = free;
+ writel(rx_offset,
+ &pdev->epx_regs->rxstaddr[pep->num - 1]);
+
+ dev_dbg(pdev->dev, "%s onchip address %04x, buffering: %d\n",
+ pep->name, rx_offset, pep->buffering);
+
+ rx_offset += pep->buffering * 1024;
+ }
+ }
+}
+
+/* Configure hardware endpoint. */
+static int cdns2_ep_config(struct cdns2_endpoint *pep, bool enable)
+{
+ bool is_iso_ep = (pep->type == USB_ENDPOINT_XFER_ISOC);
+ struct cdns2_device *pdev = pep->pdev;
+ u32 max_packet_size;
+ u8 dir = 0;
+ u8 ep_cfg;
+ u8 mult;
+ u32 val;
+ int ret;
+
+ switch (pep->type) {
+ case USB_ENDPOINT_XFER_INT:
+ ep_cfg = EPX_CON_TYPE_INT;
+ break;
+ case USB_ENDPOINT_XFER_BULK:
+ ep_cfg = EPX_CON_TYPE_BULK;
+ break;
+ default:
+ mult = USB_EP_MAXP_MULT(pep->endpoint.desc->wMaxPacketSize);
+ ep_cfg = mult << EPX_CON_ISOD_SHIFT;
+ ep_cfg |= EPX_CON_TYPE_ISOC;
+
+ if (pep->dir) {
+ set_reg_bit_8(&pdev->epx_regs->isoautoarm, BIT(pep->num));
+ set_reg_bit_8(&pdev->epx_regs->isoautodump, BIT(pep->num));
+ set_reg_bit_8(&pdev->epx_regs->isodctrl, BIT(pep->num));
+ }
+ }
+
+ switch (pdev->gadget.speed) {
+ case USB_SPEED_FULL:
+ max_packet_size = is_iso_ep ? 1023 : 64;
+ break;
+ case USB_SPEED_HIGH:
+ max_packet_size = is_iso_ep ? 1024 : 512;
+ break;
+ default:
+ /* All other speed are not supported. */
+ return -EINVAL;
+ }
+
+ ep_cfg |= (EPX_CON_VAL | (pep->buffering - 1));
+
+ if (pep->dir) {
+ dir = FIFOCTRL_IO_TX;
+ writew(max_packet_size, &pdev->epx_regs->txmaxpack[pep->num - 1]);
+ writeb(ep_cfg, &pdev->epx_regs->ep[pep->num - 1].txcon);
+ } else {
+ writew(max_packet_size, &pdev->epx_regs->rxmaxpack[pep->num - 1]);
+ writeb(ep_cfg, &pdev->epx_regs->ep[pep->num - 1].rxcon);
+ }
+
+ writeb(pep->num | dir | FIFOCTRL_FIFOAUTO,
+ &pdev->usb_regs->fifoctrl);
+ writeb(pep->num | dir, &pdev->epx_regs->endprst);
+ writeb(pep->num | ENDPRST_FIFORST | ENDPRST_TOGRST | dir,
+ &pdev->epx_regs->endprst);
+
+ if (max_packet_size == 1024)
+ pep->trb_burst_size = 128;
+ else if (max_packet_size >= 512)
+ pep->trb_burst_size = 64;
+ else
+ pep->trb_burst_size = 16;
+
+ cdns2_select_ep(pdev, pep->num | pep->dir);
+ writel(DMA_EP_CMD_EPRST | DMA_EP_CMD_DFLUSH, &pdev->adma_regs->ep_cmd);
+
+ ret = readl_poll_timeout_atomic(&pdev->adma_regs->ep_cmd, val,
+ !(val & (DMA_EP_CMD_DFLUSH |
+ DMA_EP_CMD_EPRST)),
+ 1, 1000);
+
+ if (ret)
+ return ret;
+
+ writel(DMA_EP_STS_TRBERR | DMA_EP_STS_ISOERR, &pdev->adma_regs->ep_sts_en);
+
+ if (enable)
+ writel(DMA_EP_CFG_ENABLE, &pdev->adma_regs->ep_cfg);
+
+ trace_cdns2_epx_hw_cfg(pdev, pep);
+
+ dev_dbg(pdev->dev, "Configure %s: with MPS: %08x, ep con: %02x\n",
+ pep->name, max_packet_size, ep_cfg);
+
+ return 0;
+}
+
+struct usb_request *cdns2_gadget_ep_alloc_request(struct usb_ep *ep,
+ gfp_t gfp_flags)
+{
+ struct cdns2_endpoint *pep = ep_to_cdns2_ep(ep);
+ struct cdns2_request *preq;
+
+ preq = kzalloc(sizeof(*preq), gfp_flags);
+ if (!preq)
+ return NULL;
+
+ preq->pep = pep;
+
+ trace_cdns2_alloc_request(preq);
+
+ return &preq->request;
+}
+
+void cdns2_gadget_ep_free_request(struct usb_ep *ep,
+ struct usb_request *request)
+{
+ struct cdns2_request *preq = to_cdns2_request(request);
+
+ trace_cdns2_free_request(preq);
+ kfree(preq);
+}
+
+static int cdns2_gadget_ep_enable(struct usb_ep *ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ u32 reg = DMA_EP_STS_EN_TRBERREN;
+ struct cdns2_endpoint *pep;
+ struct cdns2_device *pdev;
+ unsigned long flags;
+ int enable = 1;
+ int ret = 0;
+
+ if (!ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT ||
+ !desc->wMaxPacketSize) {
+ return -EINVAL;
+ }
+
+ pep = ep_to_cdns2_ep(ep);
+ pdev = pep->pdev;
+
+ if (dev_WARN_ONCE(pdev->dev, pep->ep_state & EP_ENABLED,
+ "%s is already enabled\n", pep->name))
+ return 0;
+
+ spin_lock_irqsave(&pdev->lock, flags);
+
+ pep->type = usb_endpoint_type(desc);
+ pep->interval = desc->bInterval ? BIT(desc->bInterval - 1) : 0;
+
+ if (pdev->gadget.speed == USB_SPEED_FULL)
+ if (pep->type == USB_ENDPOINT_XFER_INT)
+ pep->interval = desc->bInterval;
+
+ if (pep->interval > ISO_MAX_INTERVAL &&
+ pep->type == USB_ENDPOINT_XFER_ISOC) {
+ dev_err(pdev->dev, "ISO period is limited to %d (current: %d)\n",
+ ISO_MAX_INTERVAL, pep->interval);
+
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ /*
+ * During ISO OUT traffic DMA reads Transfer Ring for the EP which has
+ * never got doorbell.
+ * This issue was detected only on simulation, but to avoid this issue
+ * driver add protection against it. To fix it driver enable ISO OUT
+ * endpoint before setting DRBL. This special treatment of ISO OUT
+ * endpoints are recommended by controller specification.
+ */
+ if (pep->type == USB_ENDPOINT_XFER_ISOC && !pep->dir)
+ enable = 0;
+
+ ret = cdns2_alloc_tr_segment(pep);
+ if (ret)
+ goto exit;
+
+ ret = cdns2_ep_config(pep, enable);
+ if (ret) {
+ cdns2_free_tr_segment(pep);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ trace_cdns2_gadget_ep_enable(pep);
+
+ pep->ep_state &= ~(EP_STALLED | EP_STALL_PENDING);
+ pep->ep_state |= EP_ENABLED;
+ pep->wa1_set = 0;
+ pep->ring.enqueue = 0;
+ pep->ring.dequeue = 0;
+ reg = readl(&pdev->adma_regs->ep_sts);
+ pep->ring.pcs = !!DMA_EP_STS_CCS(reg);
+ pep->ring.ccs = !!DMA_EP_STS_CCS(reg);
+
+ writel(pep->ring.dma, &pdev->adma_regs->ep_traddr);
+
+ /* one TRB is reserved for link TRB used in DMULT mode*/
+ pep->ring.free_trbs = TRBS_PER_SEGMENT - 1;
+
+exit:
+ spin_unlock_irqrestore(&pdev->lock, flags);
+
+ return ret;
+}
+
+static int cdns2_gadget_ep_disable(struct usb_ep *ep)
+{
+ struct cdns2_endpoint *pep;
+ struct cdns2_request *preq;
+ struct cdns2_device *pdev;
+ unsigned long flags;
+ int val;
+
+ if (!ep)
+ return -EINVAL;
+
+ pep = ep_to_cdns2_ep(ep);
+ pdev = pep->pdev;
+
+ if (dev_WARN_ONCE(pdev->dev, !(pep->ep_state & EP_ENABLED),
+ "%s is already disabled\n", pep->name))
+ return 0;
+
+ spin_lock_irqsave(&pdev->lock, flags);
+
+ trace_cdns2_gadget_ep_disable(pep);
+
+ cdns2_select_ep(pdev, ep->desc->bEndpointAddress);
+
+ clear_reg_bit_32(&pdev->adma_regs->ep_cfg, DMA_EP_CFG_ENABLE);
+
+ /*
+ * Driver needs some time before resetting endpoint.
+ * It need waits for clearing DBUSY bit or for timeout expired.
+ * 10us is enough time for controller to stop transfer.
+ */
+ readl_poll_timeout_atomic(&pdev->adma_regs->ep_sts, val,
+ !(val & DMA_EP_STS_DBUSY), 1, 10);
+ writel(DMA_EP_CMD_EPRST, &pdev->adma_regs->ep_cmd);
+
+ readl_poll_timeout_atomic(&pdev->adma_regs->ep_cmd, val,
+ !(val & (DMA_EP_CMD_DFLUSH | DMA_EP_CMD_EPRST)),
+ 1, 1000);
+
+ while (!list_empty(&pep->pending_list)) {
+ preq = cdns2_next_preq(&pep->pending_list);
+ cdns2_gadget_giveback(pep, preq, -ESHUTDOWN);
+ }
+
+ while (!list_empty(&pep->deferred_list)) {
+ preq = cdns2_next_preq(&pep->deferred_list);
+ cdns2_gadget_giveback(pep, preq, -ESHUTDOWN);
+ }
+
+ ep->desc = NULL;
+ pep->ep_state &= ~EP_ENABLED;
+
+ spin_unlock_irqrestore(&pdev->lock, flags);
+
+ return 0;
+}
+
+static int cdns2_ep_enqueue(struct cdns2_endpoint *pep,
+ struct cdns2_request *preq,
+ gfp_t gfp_flags)
+{
+ struct cdns2_device *pdev = pep->pdev;
+ struct usb_request *request;
+ int ret;
+
+ request = &preq->request;
+ request->actual = 0;
+ request->status = -EINPROGRESS;
+
+ ret = usb_gadget_map_request_by_dev(pdev->dev, request, pep->dir);
+ if (ret) {
+ trace_cdns2_request_enqueue_error(preq);
+ return ret;
+ }
+
+ list_add_tail(&preq->list, &pep->deferred_list);
+ trace_cdns2_request_enqueue(preq);
+
+ if (!(pep->ep_state & EP_STALLED) && !(pep->ep_state & EP_STALL_PENDING))
+ cdns2_start_all_request(pdev, pep);
+
+ return 0;
+}
+
+static int cdns2_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
+ gfp_t gfp_flags)
+{
+ struct usb_request *zlp_request;
+ struct cdns2_request *preq;
+ struct cdns2_endpoint *pep;
+ struct cdns2_device *pdev;
+ unsigned long flags;
+ int ret;
+
+ if (!request || !ep)
+ return -EINVAL;
+
+ pep = ep_to_cdns2_ep(ep);
+ pdev = pep->pdev;
+
+ if (!(pep->ep_state & EP_ENABLED)) {
+ dev_err(pdev->dev, "%s: can't queue to disabled endpoint\n",
+ pep->name);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&pdev->lock, flags);
+
+ preq = to_cdns2_request(request);
+ ret = cdns2_ep_enqueue(pep, preq, gfp_flags);
+
+ if (ret == 0 && request->zero && request->length &&
+ (request->length % ep->maxpacket == 0)) {
+ struct cdns2_request *preq;
+
+ zlp_request = cdns2_gadget_ep_alloc_request(ep, GFP_ATOMIC);
+ zlp_request->buf = pdev->zlp_buf;
+ zlp_request->length = 0;
+
+ preq = to_cdns2_request(zlp_request);
+ ret = cdns2_ep_enqueue(pep, preq, gfp_flags);
+ }
+
+ spin_unlock_irqrestore(&pdev->lock, flags);
+ return ret;
+}
+
+int cdns2_gadget_ep_dequeue(struct usb_ep *ep,
+ struct usb_request *request)
+{
+ struct cdns2_request *preq, *preq_temp, *cur_preq;
+ struct cdns2_endpoint *pep;
+ struct cdns2_trb *link_trb;
+ u8 req_on_hw_ring = 0;
+ unsigned long flags;
+ u32 buffer;
+ int val, i;
+
+ if (!ep || !request || !ep->desc)
+ return -EINVAL;
+
+ pep = ep_to_cdns2_ep(ep);
+ if (!pep->endpoint.desc) {
+ dev_err(pep->pdev->dev, "%s: can't dequeue to disabled endpoint\n",
+ pep->name);
+ return -ESHUTDOWN;
+ }
+
+ /* Requests has been dequeued during disabling endpoint. */
+ if (!(pep->ep_state & EP_ENABLED))
+ return 0;
+
+ spin_lock_irqsave(&pep->pdev->lock, flags);
+
+ cur_preq = to_cdns2_request(request);
+ trace_cdns2_request_dequeue(cur_preq);
+
+ list_for_each_entry_safe(preq, preq_temp, &pep->pending_list, list) {
+ if (cur_preq == preq) {
+ req_on_hw_ring = 1;
+ goto found;
+ }
+ }
+
+ list_for_each_entry_safe(preq, preq_temp, &pep->deferred_list, list) {
+ if (cur_preq == preq)
+ goto found;
+ }
+
+ goto not_found;
+
+found:
+ link_trb = preq->trb;
+
+ /* Update ring only if removed request is on pending_req_list list. */
+ if (req_on_hw_ring && link_trb) {
+ /* Stop DMA */
+ writel(DMA_EP_CMD_DFLUSH, &pep->pdev->adma_regs->ep_cmd);
+
+ /* Wait for DFLUSH cleared. */
+ readl_poll_timeout_atomic(&pep->pdev->adma_regs->ep_cmd, val,
+ !(val & DMA_EP_CMD_DFLUSH), 1, 1000);
+
+ buffer = cpu_to_le32(TRB_BUFFER(pep->ring.dma +
+ ((preq->end_trb + 1) * TRB_SIZE)));
+
+ for (i = 0; i < preq->num_of_trb; i++) {
+ link_trb->buffer = buffer;
+ link_trb->control = cpu_to_le32((le32_to_cpu(link_trb->control)
+ & TRB_CYCLE) | TRB_CHAIN |
+ TRB_TYPE(TRB_LINK));
+
+ trace_cdns2_queue_trb(pep, link_trb);
+ link_trb = cdns2_next_trb(pep, link_trb);
+ }
+
+ if (pep->wa1_trb == preq->trb)
+ cdns2_wa1_restore_cycle_bit(pep);
+ }
+
+ cdns2_gadget_giveback(pep, cur_preq, -ECONNRESET);
+
+ preq = cdns2_next_preq(&pep->pending_list);
+ if (preq)
+ cdns2_rearm_transfer(pep, 1);
+
+not_found:
+ spin_unlock_irqrestore(&pep->pdev->lock, flags);
+ return 0;
+}
+
+int cdns2_halt_endpoint(struct cdns2_device *pdev,
+ struct cdns2_endpoint *pep,
+ int value)
+{
+ u8 __iomem *conf;
+ int dir = 0;
+
+ if (!(pep->ep_state & EP_ENABLED))
+ return -EPERM;
+
+ if (pep->dir) {
+ dir = ENDPRST_IO_TX;
+ conf = &pdev->epx_regs->ep[pep->num - 1].txcon;
+ } else {
+ conf = &pdev->epx_regs->ep[pep->num - 1].rxcon;
+ }
+
+ if (!value) {
+ struct cdns2_trb *trb = NULL;
+ struct cdns2_request *preq;
+ struct cdns2_trb trb_tmp;
+
+ preq = cdns2_next_preq(&pep->pending_list);
+ if (preq) {
+ trb = preq->trb;
+ if (trb) {
+ trb_tmp = *trb;
+ trb->control = trb->control ^ cpu_to_le32(TRB_CYCLE);
+ }
+ }
+
+ trace_cdns2_ep_halt(pep, 0, 0);
+
+ /* Resets Sequence Number */
+ writeb(dir | pep->num, &pdev->epx_regs->endprst);
+ writeb(dir | ENDPRST_TOGRST | pep->num,
+ &pdev->epx_regs->endprst);
+
+ clear_reg_bit_8(conf, EPX_CON_STALL);
+
+ pep->ep_state &= ~(EP_STALLED | EP_STALL_PENDING);
+
+ if (preq) {
+ if (trb)
+ *trb = trb_tmp;
+
+ cdns2_rearm_transfer(pep, 1);
+ }
+
+ cdns2_start_all_request(pdev, pep);
+ } else {
+ trace_cdns2_ep_halt(pep, 1, 0);
+ set_reg_bit_8(conf, EPX_CON_STALL);
+ writeb(dir | pep->num, &pdev->epx_regs->endprst);
+ writeb(dir | ENDPRST_FIFORST | pep->num,
+ &pdev->epx_regs->endprst);
+ pep->ep_state |= EP_STALLED;
+ }
+
+ return 0;
+}
+
+/* Sets/clears stall on selected endpoint. */
+static int cdns2_gadget_ep_set_halt(struct usb_ep *ep, int value)
+{
+ struct cdns2_endpoint *pep = ep_to_cdns2_ep(ep);
+ struct cdns2_device *pdev = pep->pdev;
+ struct cdns2_request *preq;
+ unsigned long flags = 0;
+ int ret;
+
+ spin_lock_irqsave(&pdev->lock, flags);
+
+ preq = cdns2_next_preq(&pep->pending_list);
+ if (value && preq) {
+ trace_cdns2_ep_busy_try_halt_again(pep);
+ ret = -EAGAIN;
+ goto done;
+ }
+
+ if (!value)
+ pep->ep_state &= ~EP_WEDGE;
+
+ ret = cdns2_halt_endpoint(pdev, pep, value);
+
+done:
+ spin_unlock_irqrestore(&pdev->lock, flags);
+ return ret;
+}
+
+static int cdns2_gadget_ep_set_wedge(struct usb_ep *ep)
+{
+ struct cdns2_endpoint *pep = ep_to_cdns2_ep(ep);
+
+ cdns2_gadget_ep_set_halt(ep, 1);
+ pep->ep_state |= EP_WEDGE;
+
+ return 0;
+}
+
+static struct
+cdns2_endpoint *cdns2_find_available_ep(struct cdns2_device *pdev,
+ struct usb_endpoint_descriptor *desc)
+{
+ struct cdns2_endpoint *pep;
+ struct usb_ep *ep;
+ int ep_correct;
+
+ list_for_each_entry(ep, &pdev->gadget.ep_list, ep_list) {
+ unsigned long num;
+ int ret;
+ /* ep name pattern likes epXin or epXout. */
+ char c[2] = {ep->name[2], '\0'};
+
+ ret = kstrtoul(c, 10, &num);
+ if (ret)
+ return ERR_PTR(ret);
+ pep = ep_to_cdns2_ep(ep);
+
+ if (pep->num != num)
+ continue;
+
+ ep_correct = (pep->endpoint.caps.dir_in &&
+ usb_endpoint_dir_in(desc)) ||
+ (pep->endpoint.caps.dir_out &&
+ usb_endpoint_dir_out(desc));
+
+ if (ep_correct && !(pep->ep_state & EP_CLAIMED))
+ return pep;
+ }
+
+ return ERR_PTR(-ENOENT);
+}
+
+/*
+ * Function used to recognize which endpoints will be used to optimize
+ * on-chip memory usage.
+ */
+static struct
+usb_ep *cdns2_gadget_match_ep(struct usb_gadget *gadget,
+ struct usb_endpoint_descriptor *desc,
+ struct usb_ss_ep_comp_descriptor *comp_desc)
+{
+ struct cdns2_device *pdev = gadget_to_cdns2_device(gadget);
+ struct cdns2_endpoint *pep;
+ unsigned long flags;
+
+ pep = cdns2_find_available_ep(pdev, desc);
+ if (IS_ERR(pep)) {
+ dev_err(pdev->dev, "no available ep\n");
+ return NULL;
+ }
+
+ spin_lock_irqsave(&pdev->lock, flags);
+
+ if (usb_endpoint_type(desc) == USB_ENDPOINT_XFER_ISOC)
+ pep->buffering = 4;
+ else
+ pep->buffering = 1;
+
+ pep->ep_state |= EP_CLAIMED;
+ spin_unlock_irqrestore(&pdev->lock, flags);
+
+ return &pep->endpoint;
+}
+
+static const struct usb_ep_ops cdns2_gadget_ep_ops = {
+ .enable = cdns2_gadget_ep_enable,
+ .disable = cdns2_gadget_ep_disable,
+ .alloc_request = cdns2_gadget_ep_alloc_request,
+ .free_request = cdns2_gadget_ep_free_request,
+ .queue = cdns2_gadget_ep_queue,
+ .dequeue = cdns2_gadget_ep_dequeue,
+ .set_halt = cdns2_gadget_ep_set_halt,
+ .set_wedge = cdns2_gadget_ep_set_wedge,
+};
+
+static int cdns2_gadget_get_frame(struct usb_gadget *gadget)
+{
+ struct cdns2_device *pdev = gadget_to_cdns2_device(gadget);
+
+ return readw(&pdev->usb_regs->frmnr);
+}
+
+static int cdns2_gadget_wakeup(struct usb_gadget *gadget)
+{
+ struct cdns2_device *pdev = gadget_to_cdns2_device(gadget);
+ unsigned long flags;
+
+ spin_lock_irqsave(&pdev->lock, flags);
+ cdns2_wakeup(pdev);
+ spin_unlock_irqrestore(&pdev->lock, flags);
+
+ return 0;
+}
+
+static int cdns2_gadget_set_selfpowered(struct usb_gadget *gadget,
+ int is_selfpowered)
+{
+ struct cdns2_device *pdev = gadget_to_cdns2_device(gadget);
+ unsigned long flags;
+
+ spin_lock_irqsave(&pdev->lock, flags);
+ pdev->is_selfpowered = !!is_selfpowered;
+ spin_unlock_irqrestore(&pdev->lock, flags);
+ return 0;
+}
+
+/* Disable interrupts and begin the controller halting process. */
+static void cdns2_quiesce(struct cdns2_device *pdev)
+{
+ set_reg_bit_8(&pdev->usb_regs->usbcs, USBCS_DISCON);
+
+ /* Disable interrupt. */
+ writeb(0, &pdev->interrupt_regs->extien),
+ writeb(0, &pdev->interrupt_regs->usbien),
+ writew(0, &pdev->adma_regs->ep_ien);
+
+ /* Clear interrupt line. */
+ writeb(0x0, &pdev->interrupt_regs->usbirq);
+}
+
+static void cdns2_gadget_config(struct cdns2_device *pdev)
+{
+ cdns2_ep0_config(pdev);
+
+ /* Enable DMA interrupts for all endpoints. */
+ writel(~0x0, &pdev->adma_regs->ep_ien);
+ cdns2_enable_l1(pdev, 0);
+ writeb(USB_IEN_INIT, &pdev->interrupt_regs->usbien);
+ writeb(EXTIRQ_WAKEUP, &pdev->interrupt_regs->extien);
+ writel(DMA_CONF_DMULT, &pdev->adma_regs->conf);
+}
+
+static int cdns2_gadget_pullup(struct usb_gadget *gadget, int is_on)
+{
+ struct cdns2_device *pdev = gadget_to_cdns2_device(gadget);
+ unsigned long flags;
+
+ trace_cdns2_pullup(is_on);
+
+ /*
+ * Disable events handling while controller is being
+ * enabled/disabled.
+ */
+ disable_irq(pdev->irq);
+ spin_lock_irqsave(&pdev->lock, flags);
+
+ if (is_on) {
+ cdns2_gadget_config(pdev);
+ clear_reg_bit_8(&pdev->usb_regs->usbcs, USBCS_DISCON);
+ } else {
+ cdns2_quiesce(pdev);
+ }
+
+ spin_unlock_irqrestore(&pdev->lock, flags);
+ enable_irq(pdev->irq);
+
+ return 0;
+}
+
+static int cdns2_gadget_udc_start(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
+{
+ struct cdns2_device *pdev = gadget_to_cdns2_device(gadget);
+ enum usb_device_speed max_speed = driver->max_speed;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pdev->lock, flags);
+ pdev->gadget_driver = driver;
+
+ /* Limit speed if necessary. */
+ max_speed = min(driver->max_speed, gadget->max_speed);
+
+ switch (max_speed) {
+ case USB_SPEED_FULL:
+ writeb(SPEEDCTRL_HSDISABLE, &pdev->usb_regs->speedctrl);
+ break;
+ case USB_SPEED_HIGH:
+ writeb(0, &pdev->usb_regs->speedctrl);
+ break;
+ default:
+ dev_err(pdev->dev, "invalid maximum_speed parameter %d\n",
+ max_speed);
+ fallthrough;
+ case USB_SPEED_UNKNOWN:
+ /* Default to highspeed. */
+ max_speed = USB_SPEED_HIGH;
+ break;
+ }
+
+ /* Reset all USB endpoints. */
+ writeb(ENDPRST_IO_TX, &pdev->usb_regs->endprst);
+ writeb(ENDPRST_FIFORST | ENDPRST_TOGRST | ENDPRST_IO_TX,
+ &pdev->usb_regs->endprst);
+ writeb(ENDPRST_FIFORST | ENDPRST_TOGRST, &pdev->usb_regs->endprst);
+
+ cdns2_eps_onchip_buffer_init(pdev);
+
+ cdns2_gadget_config(pdev);
+ spin_unlock_irqrestore(&pdev->lock, flags);
+
+ return 0;
+}
+
+static int cdns2_gadget_udc_stop(struct usb_gadget *gadget)
+{
+ struct cdns2_device *pdev = gadget_to_cdns2_device(gadget);
+ struct cdns2_endpoint *pep;
+ u32 bEndpointAddress;
+ struct usb_ep *ep;
+ int val;
+
+ pdev->gadget_driver = NULL;
+ pdev->gadget.speed = USB_SPEED_UNKNOWN;
+
+ list_for_each_entry(ep, &pdev->gadget.ep_list, ep_list) {
+ pep = ep_to_cdns2_ep(ep);
+ bEndpointAddress = pep->num | pep->dir;
+ cdns2_select_ep(pdev, bEndpointAddress);
+ writel(DMA_EP_CMD_EPRST, &pdev->adma_regs->ep_cmd);
+ readl_poll_timeout_atomic(&pdev->adma_regs->ep_cmd, val,
+ !(val & DMA_EP_CMD_EPRST), 1, 100);
+ }
+
+ cdns2_quiesce(pdev);
+
+ writeb(ENDPRST_IO_TX, &pdev->usb_regs->endprst);
+ writeb(ENDPRST_FIFORST | ENDPRST_TOGRST | ENDPRST_IO_TX,
+ &pdev->epx_regs->endprst);
+ writeb(ENDPRST_FIFORST | ENDPRST_TOGRST, &pdev->epx_regs->endprst);
+
+ return 0;
+}
+
+static const struct usb_gadget_ops cdns2_gadget_ops = {
+ .get_frame = cdns2_gadget_get_frame,
+ .wakeup = cdns2_gadget_wakeup,
+ .set_selfpowered = cdns2_gadget_set_selfpowered,
+ .pullup = cdns2_gadget_pullup,
+ .udc_start = cdns2_gadget_udc_start,
+ .udc_stop = cdns2_gadget_udc_stop,
+ .match_ep = cdns2_gadget_match_ep,
+};
+
+static void cdns2_free_all_eps(struct cdns2_device *pdev)
+{
+ int i;
+
+ for (i = 0; i < CDNS2_ENDPOINTS_NUM; i++)
+ cdns2_free_tr_segment(&pdev->eps[i]);
+}
+
+/* Initializes software endpoints of gadget. */
+static int cdns2_init_eps(struct cdns2_device *pdev)
+{
+ struct cdns2_endpoint *pep;
+ int i;
+
+ for (i = 0; i < CDNS2_ENDPOINTS_NUM; i++) {
+ bool direction = !(i & 1); /* Start from OUT endpoint. */
+ u8 epnum = ((i + 1) >> 1);
+
+ /*
+ * Endpoints are being held in pdev->eps[] in form:
+ * ep0, ep1out, ep1in ... ep15out, ep15in.
+ */
+ if (!CDNS2_IF_EP_EXIST(pdev, epnum, direction))
+ continue;
+
+ pep = &pdev->eps[i];
+ pep->pdev = pdev;
+ pep->num = epnum;
+ /* 0 for OUT, 1 for IN. */
+ pep->dir = direction ? USB_DIR_IN : USB_DIR_OUT;
+ pep->idx = i;
+
+ /* Ep0in and ep0out are represented by pdev->eps[0]. */
+ if (!epnum) {
+ int ret;
+
+ snprintf(pep->name, sizeof(pep->name), "ep%d%s",
+ epnum, "BiDir");
+
+ cdns2_init_ep0(pdev, pep);
+
+ ret = cdns2_alloc_tr_segment(pep);
+ if (ret) {
+ dev_err(pdev->dev, "Failed to init ep0\n");
+ return ret;
+ }
+ } else {
+ snprintf(pep->name, sizeof(pep->name), "ep%d%s",
+ epnum, !!direction ? "in" : "out");
+ pep->endpoint.name = pep->name;
+
+ usb_ep_set_maxpacket_limit(&pep->endpoint, 1024);
+ pep->endpoint.ops = &cdns2_gadget_ep_ops;
+ list_add_tail(&pep->endpoint.ep_list, &pdev->gadget.ep_list);
+
+ pep->endpoint.caps.dir_in = direction;
+ pep->endpoint.caps.dir_out = !direction;
+
+ pep->endpoint.caps.type_iso = 1;
+ pep->endpoint.caps.type_bulk = 1;
+ pep->endpoint.caps.type_int = 1;
+ }
+
+ pep->endpoint.name = pep->name;
+ pep->ep_state = 0;
+
+ dev_dbg(pdev->dev, "Init %s, SupType: CTRL: %s, INT: %s, "
+ "BULK: %s, ISOC %s, SupDir IN: %s, OUT: %s\n",
+ pep->name,
+ (pep->endpoint.caps.type_control) ? "yes" : "no",
+ (pep->endpoint.caps.type_int) ? "yes" : "no",
+ (pep->endpoint.caps.type_bulk) ? "yes" : "no",
+ (pep->endpoint.caps.type_iso) ? "yes" : "no",
+ (pep->endpoint.caps.dir_in) ? "yes" : "no",
+ (pep->endpoint.caps.dir_out) ? "yes" : "no");
+
+ INIT_LIST_HEAD(&pep->pending_list);
+ INIT_LIST_HEAD(&pep->deferred_list);
+ }
+
+ return 0;
+}
+
+static int cdns2_gadget_start(struct cdns2_device *pdev)
+{
+ u32 max_speed;
+ void *buf;
+ int val;
+ int ret;
+
+ pdev->usb_regs = pdev->regs;
+ pdev->ep0_regs = pdev->regs;
+ pdev->epx_regs = pdev->regs;
+ pdev->interrupt_regs = pdev->regs;
+ pdev->adma_regs = pdev->regs + CDNS2_ADMA_REGS_OFFSET;
+
+ /* Reset controller. */
+ set_reg_bit_8(&pdev->usb_regs->cpuctrl, CPUCTRL_SW_RST);
+
+ ret = readl_poll_timeout_atomic(&pdev->usb_regs->cpuctrl, val,
+ !(val & CPUCTRL_SW_RST), 1, 10000);
+ if (ret) {
+ dev_err(pdev->dev, "Error: reset controller timeout\n");
+ return -EINVAL;
+ }
+
+ usb_initialize_gadget(pdev->dev, &pdev->gadget, NULL);
+
+ device_property_read_u16(pdev->dev, "cdns,on-chip-tx-buff-size",
+ &pdev->onchip_tx_buf);
+ device_property_read_u16(pdev->dev, "cdns,on-chip-rx-buff-size",
+ &pdev->onchip_rx_buf);
+ device_property_read_u32(pdev->dev, "cdns,avail-endpoints",
+ &pdev->eps_supported);
+
+ /*
+ * Driver assumes that each USBHS controller has at least
+ * one IN and one OUT non control endpoint.
+ */
+ if (!pdev->onchip_tx_buf && !pdev->onchip_rx_buf) {
+ ret = -EINVAL;
+ dev_err(pdev->dev, "Invalid on-chip memory configuration\n");
+ goto put_gadget;
+ }
+
+ if (!(pdev->eps_supported & ~0x00010001)) {
+ ret = -EINVAL;
+ dev_err(pdev->dev, "No hardware endpoints available\n");
+ goto put_gadget;
+ }
+
+ max_speed = usb_get_maximum_speed(pdev->dev);
+
+ switch (max_speed) {
+ case USB_SPEED_FULL:
+ case USB_SPEED_HIGH:
+ break;
+ default:
+ dev_err(pdev->dev, "invalid maximum_speed parameter %d\n",
+ max_speed);
+ fallthrough;
+ case USB_SPEED_UNKNOWN:
+ max_speed = USB_SPEED_HIGH;
+ break;
+ }
+
+ pdev->gadget.max_speed = max_speed;
+ pdev->gadget.speed = USB_SPEED_UNKNOWN;
+ pdev->gadget.ops = &cdns2_gadget_ops;
+ pdev->gadget.name = "usbhs-gadget";
+ pdev->gadget.quirk_avoids_skb_reserve = 1;
+ pdev->gadget.irq = pdev->irq;
+
+ spin_lock_init(&pdev->lock);
+ INIT_WORK(&pdev->pending_status_wq, cdns2_pending_setup_status_handler);
+
+ /* Initialize endpoint container. */
+ INIT_LIST_HEAD(&pdev->gadget.ep_list);
+ pdev->eps_dma_pool = dma_pool_create("cdns2_eps_dma_pool", pdev->dev,
+ TR_SEG_SIZE, 8, 0);
+ if (!pdev->eps_dma_pool) {
+ dev_err(pdev->dev, "Failed to create TRB dma pool\n");
+ ret = -ENOMEM;
+ goto put_gadget;
+ }
+
+ ret = cdns2_init_eps(pdev);
+ if (ret) {
+ dev_err(pdev->dev, "Failed to create endpoints\n");
+ goto destroy_dma_pool;
+ }
+
+ pdev->gadget.sg_supported = 1;
+
+ pdev->zlp_buf = kzalloc(CDNS2_EP_ZLP_BUF_SIZE, GFP_KERNEL);
+ if (!pdev->zlp_buf) {
+ ret = -ENOMEM;
+ goto destroy_dma_pool;
+ }
+
+ /* Allocate memory for setup packet buffer. */
+ buf = dma_alloc_coherent(pdev->dev, 8, &pdev->ep0_preq.request.dma,
+ GFP_DMA);
+ pdev->ep0_preq.request.buf = buf;
+
+ if (!pdev->ep0_preq.request.buf) {
+ ret = -ENOMEM;
+ goto free_zlp_buf;
+ }
+
+ /* Add USB gadget device. */
+ ret = usb_add_gadget(&pdev->gadget);
+ if (ret < 0) {
+ dev_err(pdev->dev, "Failed to add gadget\n");
+ goto free_ep0_buf;
+ }
+
+ return 0;
+
+free_ep0_buf:
+ dma_free_coherent(pdev->dev, 8, pdev->ep0_preq.request.buf,
+ pdev->ep0_preq.request.dma);
+free_zlp_buf:
+ kfree(pdev->zlp_buf);
+destroy_dma_pool:
+ dma_pool_destroy(pdev->eps_dma_pool);
+put_gadget:
+ usb_put_gadget(&pdev->gadget);
+
+ return ret;
+}
+
+int cdns2_gadget_suspend(struct cdns2_device *pdev)
+{
+ unsigned long flags;
+
+ cdns2_disconnect_gadget(pdev);
+
+ spin_lock_irqsave(&pdev->lock, flags);
+ pdev->gadget.speed = USB_SPEED_UNKNOWN;
+
+ trace_cdns2_device_state("notattached");
+ usb_gadget_set_state(&pdev->gadget, USB_STATE_NOTATTACHED);
+ cdns2_enable_l1(pdev, 0);
+
+ /* Disable interrupt for device. */
+ writeb(0, &pdev->interrupt_regs->usbien);
+ writel(0, &pdev->adma_regs->ep_ien);
+ spin_unlock_irqrestore(&pdev->lock, flags);
+
+ return 0;
+}
+
+int cdns2_gadget_resume(struct cdns2_device *pdev, bool hibernated)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&pdev->lock, flags);
+
+ if (!pdev->gadget_driver) {
+ spin_unlock_irqrestore(&pdev->lock, flags);
+ return 0;
+ }
+
+ cdns2_gadget_config(pdev);
+
+ if (hibernated)
+ clear_reg_bit_8(&pdev->usb_regs->usbcs, USBCS_DISCON);
+
+ spin_unlock_irqrestore(&pdev->lock, flags);
+
+ return 0;
+}
+
+void cdns2_gadget_remove(struct cdns2_device *pdev)
+{
+ pm_runtime_mark_last_busy(pdev->dev);
+ pm_runtime_put_autosuspend(pdev->dev);
+
+ usb_del_gadget(&pdev->gadget);
+ cdns2_free_all_eps(pdev);
+
+ dma_pool_destroy(pdev->eps_dma_pool);
+ kfree(pdev->zlp_buf);
+ usb_put_gadget(&pdev->gadget);
+}
+
+int cdns2_gadget_init(struct cdns2_device *pdev)
+{
+ int ret;
+
+ /* Ensure 32-bit DMA Mask. */
+ ret = dma_set_mask_and_coherent(pdev->dev, DMA_BIT_MASK(32));
+ if (ret) {
+ dev_err(pdev->dev, "Failed to set dma mask: %d\n", ret);
+ return ret;
+ }
+
+ pm_runtime_get_sync(pdev->dev);
+
+ cdsn2_isoc_burst_opt(pdev);
+
+ ret = cdns2_gadget_start(pdev);
+ if (ret) {
+ pm_runtime_put_sync(pdev->dev);
+ return ret;
+ }
+
+ /*
+ * Because interrupt line can be shared with other components in
+ * driver it can't use IRQF_ONESHOT flag here.
+ */
+ ret = devm_request_threaded_irq(pdev->dev, pdev->irq,
+ cdns2_usb_irq_handler,
+ cdns2_thread_irq_handler,
+ IRQF_SHARED,
+ dev_name(pdev->dev),
+ pdev);
+ if (ret)
+ goto err0;
+
+ return 0;
+
+err0:
+ cdns2_gadget_remove(pdev);
+
+ return ret;
+}
diff --git a/drivers/usb/gadget/udc/cdns2/cdns2-gadget.h b/drivers/usb/gadget/udc/cdns2/cdns2-gadget.h
new file mode 100644
index 000000000000..71e2f62d653a
--- /dev/null
+++ b/drivers/usb/gadget/udc/cdns2/cdns2-gadget.h
@@ -0,0 +1,707 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * USBHS-DEV device controller driver header file
+ *
+ * Copyright (C) 2023 Cadence.
+ *
+ * Author: Pawel Laszczak <pawell@cadence.com>
+ */
+
+#ifndef __LINUX_CDNS2_GADGET
+#define __LINUX_CDNS2_GADGET
+
+#include <linux/usb/gadget.h>
+#include <linux/dma-direction.h>
+
+/*
+ * USBHS register interface.
+ * This corresponds to the USBHS Device Controller Interface.
+ */
+
+/**
+ * struct cdns2_ep0_regs - endpoint 0 related registers.
+ * @rxbc: receive (OUT) 0 endpoint byte count register.
+ * @txbc: transmit (IN) 0 endpoint byte count register.
+ * @cs: 0 endpoint control and status register.
+ * @reserved1: reserved.
+ * @fifo: 0 endpoint fifo register.
+ * @reserved2: reserved.
+ * @setupdat: SETUP data register.
+ * @reserved4: reserved.
+ * @maxpack: 0 endpoint max packet size.
+ */
+struct cdns2_ep0_regs {
+ __u8 rxbc;
+ __u8 txbc;
+ __u8 cs;
+ __u8 reserved1[4];
+ __u8 fifo;
+ __le32 reserved2[94];
+ __u8 setupdat[8];
+ __u8 reserved4[88];
+ __u8 maxpack;
+} __packed __aligned(4);
+
+/* EP0CS - bitmasks. */
+/* Endpoint 0 stall bit for status stage. */
+#define EP0CS_STALL BIT(0)
+/* HSNAK bit. */
+#define EP0CS_HSNAK BIT(1)
+/* IN 0 endpoint busy bit. */
+#define EP0CS_TXBSY_MSK BIT(2)
+/* OUT 0 endpoint busy bit. */
+#define EP0CS_RXBSY_MSK BIT(3)
+/* Send STALL in the data stage phase. */
+#define EP0CS_DSTALL BIT(4)
+/* SETUP buffer content was changed. */
+#define EP0CS_CHGSET BIT(7)
+
+/* EP0FIFO - bitmasks. */
+/* Direction. */
+#define EP0_FIFO_IO_TX BIT(4)
+/* FIFO auto bit. */
+#define EP0_FIFO_AUTO BIT(5)
+/* FIFO commit bit. */
+#define EP0_FIFO_COMMIT BIT(6)
+/* FIFO access bit. */
+#define EP0_FIFO_ACCES BIT(7)
+
+/**
+ * struct cdns2_epx_base - base endpoint registers.
+ * @rxbc: OUT endpoint byte count register.
+ * @rxcon: OUT endpoint control register.
+ * @rxcs: OUT endpoint control and status register.
+ * @txbc: IN endpoint byte count register.
+ * @txcon: IN endpoint control register.
+ * @txcs: IN endpoint control and status register.
+ */
+struct cdns2_epx_base {
+ __le16 rxbc;
+ __u8 rxcon;
+ __u8 rxcs;
+ __le16 txbc;
+ __u8 txcon;
+ __u8 txcs;
+} __packed __aligned(4);
+
+/* rxcon/txcon - endpoint control register bitmasks. */
+/* Endpoint buffering: 0 - single buffering ... 3 - quad buffering. */
+#define EPX_CON_BUF GENMASK(1, 0)
+/* Endpoint type. */
+#define EPX_CON_TYPE GENMASK(3, 2)
+/* Endpoint type: isochronous. */
+#define EPX_CON_TYPE_ISOC 0x4
+/* Endpoint type: bulk. */
+#define EPX_CON_TYPE_BULK 0x8
+/* Endpoint type: interrupt. */
+#define EPX_CON_TYPE_INT 0xC
+/* Number of packets per microframe. */
+#define EPX_CON_ISOD GENMASK(5, 4)
+#define EPX_CON_ISOD_SHIFT 0x4
+/* Endpoint stall bit. */
+#define EPX_CON_STALL BIT(6)
+/* Endpoint enable bit.*/
+#define EPX_CON_VAL BIT(7)
+
+/* rxcs/txcs - endpoint control and status bitmasks. */
+/* Data sequence error for the ISO endpoint. */
+#define EPX_CS_ERR(p) ((p) & BIT(0))
+
+/**
+ * struct cdns2_epx_regs - endpoint 1..15 related registers.
+ * @reserved: reserved.
+ * @ep: none control endpoints array.
+ * @reserved2: reserved.
+ * @endprst: endpoint reset register.
+ * @reserved3: reserved.
+ * @isoautoarm: ISO auto-arm register.
+ * @reserved4: reserved.
+ * @isodctrl: ISO control register.
+ * @reserved5: reserved.
+ * @isoautodump: ISO auto dump enable register.
+ * @reserved6: reserved.
+ * @rxmaxpack: receive (OUT) Max packet size register.
+ * @reserved7: reserved.
+ * @rxstaddr: receive (OUT) start address endpoint buffer register.
+ * @reserved8: reserved.
+ * @txstaddr: transmit (IN) start address endpoint buffer register.
+ * @reserved9: reserved.
+ * @txmaxpack: transmit (IN) Max packet size register.
+ */
+struct cdns2_epx_regs {
+ __le32 reserved[2];
+ struct cdns2_epx_base ep[15];
+ __u8 reserved2[290];
+ __u8 endprst;
+ __u8 reserved3[41];
+ __le16 isoautoarm;
+ __u8 reserved4[10];
+ __le16 isodctrl;
+ __le16 reserved5;
+ __le16 isoautodump;
+ __le32 reserved6;
+ __le16 rxmaxpack[15];
+ __le32 reserved7[65];
+ __le32 rxstaddr[15];
+ __u8 reserved8[4];
+ __le32 txstaddr[15];
+ __u8 reserved9[98];
+ __le16 txmaxpack[15];
+} __packed __aligned(4);
+
+/* ENDPRST - bitmasks. */
+/* Endpoint number. */
+#define ENDPRST_EP GENMASK(3, 0)
+/* IN direction bit. */
+#define ENDPRST_IO_TX BIT(4)
+/* Toggle reset bit. */
+#define ENDPRST_TOGRST BIT(5)
+/* FIFO reset bit. */
+#define ENDPRST_FIFORST BIT(6)
+/* Toggle status and reset bit. */
+#define ENDPRST_TOGSETQ BIT(7)
+
+/**
+ * struct cdns2_interrupt_regs - USB interrupt related registers.
+ * @reserved: reserved.
+ * @usbirq: USB interrupt request register.
+ * @extirq: external interrupt request register.
+ * @rxpngirq: external interrupt request register.
+ * @reserved1: reserved.
+ * @usbien: USB interrupt enable register.
+ * @extien: external interrupt enable register.
+ * @reserved2: reserved.
+ * @usbivect: USB interrupt vector register.
+ */
+struct cdns2_interrupt_regs {
+ __u8 reserved[396];
+ __u8 usbirq;
+ __u8 extirq;
+ __le16 rxpngirq;
+ __le16 reserved1[4];
+ __u8 usbien;
+ __u8 extien;
+ __le16 reserved2[3];
+ __u8 usbivect;
+} __packed __aligned(4);
+
+/* EXTIRQ and EXTIEN - bitmasks. */
+/* VBUS fault fall interrupt. */
+#define EXTIRQ_VBUSFAULT_FALL BIT(0)
+/* VBUS fault fall interrupt. */
+#define EXTIRQ_VBUSFAULT_RISE BIT(1)
+/* Wake up interrupt bit. */
+#define EXTIRQ_WAKEUP BIT(7)
+
+/* USBIEN and USBIRQ - bitmasks. */
+/* SETUP data valid interrupt bit.*/
+#define USBIRQ_SUDAV BIT(0)
+/* Start-of-frame interrupt bit. */
+#define USBIRQ_SOF BIT(1)
+/* SETUP token interrupt bit. */
+#define USBIRQ_SUTOK BIT(2)
+/* USB suspend interrupt bit. */
+#define USBIRQ_SUSPEND BIT(3)
+/* USB reset interrupt bit. */
+#define USBIRQ_URESET BIT(4)
+/* USB high-speed mode interrupt bit. */
+#define USBIRQ_HSPEED BIT(5)
+/* Link Power Management interrupt bit. */
+#define USBIRQ_LPM BIT(7)
+
+#define USB_IEN_INIT (USBIRQ_SUDAV | USBIRQ_SUSPEND | USBIRQ_URESET \
+ | USBIRQ_HSPEED | USBIRQ_LPM)
+/**
+ * struct cdns2_usb_regs - USB controller registers.
+ * @reserved: reserved.
+ * @lpmctrl: LPM control register.
+ * @lpmclock: LPM clock register.
+ * @reserved2: reserved.
+ * @endprst: endpoint reset register.
+ * @usbcs: USB control and status register.
+ * @frmnr: USB frame counter register.
+ * @fnaddr: function Address register.
+ * @clkgate: clock gate register.
+ * @fifoctrl: FIFO control register.
+ * @speedctrl: speed Control register.
+ * @sleep_clkgate: sleep Clock Gate register.
+ * @reserved3: reserved.
+ * @cpuctrl: microprocessor control register.
+ */
+struct cdns2_usb_regs {
+ __u8 reserved[4];
+ __u16 lpmctrl;
+ __u8 lpmclock;
+ __u8 reserved2[411];
+ __u8 endprst;
+ __u8 usbcs;
+ __le16 frmnr;
+ __u8 fnaddr;
+ __u8 clkgate;
+ __u8 fifoctrl;
+ __u8 speedctrl;
+ __u8 sleep_clkgate;
+ __u8 reserved3[533];
+ __u8 cpuctrl;
+} __packed __aligned(4);
+
+/* LPMCTRL - bitmasks. */
+/* BESL (Best Effort Service Latency). */
+#define LPMCTRLLL_HIRD GENMASK(7, 4)
+/* Last received Remote Wakeup field from LPM Extended Token packet. */
+#define LPMCTRLLH_BREMOTEWAKEUP BIT(8)
+/* Reflects value of the lpmnyet bit located in the usbcs[1] register. */
+#define LPMCTRLLH_LPMNYET BIT(16)
+
+/* LPMCLOCK - bitmasks. */
+/*
+ * If bit is 1 the controller automatically turns off clock
+ * (utmisleepm goes to low), else the microprocessor should use
+ * sleep clock gate register to turn off clock.
+ */
+#define LPMCLOCK_SLEEP_ENTRY BIT(7)
+
+/* USBCS - bitmasks. */
+/* Send NYET handshake for the LPM transaction. */
+#define USBCS_LPMNYET BIT(2)
+/* Remote wake-up bit. */
+#define USBCS_SIGRSUME BIT(5)
+/* Software disconnect bit. */
+#define USBCS_DISCON BIT(6)
+/* Indicates that a wakeup pin resumed the controller. */
+#define USBCS_WAKESRC BIT(7)
+
+/* FIFOCTRL - bitmasks. */
+/* Endpoint number. */
+#define FIFOCTRL_EP GENMASK(3, 0)
+/* Direction bit. */
+#define FIFOCTRL_IO_TX BIT(4)
+/* FIFO auto bit. */
+#define FIFOCTRL_FIFOAUTO BIT(5)
+/* FIFO commit bit. */
+#define FIFOCTRL_FIFOCMIT BIT(6)
+/* FIFO access bit. */
+#define FIFOCTRL_FIFOACC BIT(7)
+
+/* SPEEDCTRL - bitmasks. */
+/* Device works in Full Speed. */
+#define SPEEDCTRL_FS BIT(1)
+/* Device works in High Speed. */
+#define SPEEDCTRL_HS BIT(2)
+/* Force FS mode. */
+#define SPEEDCTRL_HSDISABLE BIT(7)
+
+/* CPUCTRL- bitmasks. */
+/* Controller reset bit. */
+#define CPUCTRL_SW_RST BIT(1)
+
+/**
+ * struct cdns2_adma_regs - ADMA controller registers.
+ * @conf: DMA global configuration register.
+ * @sts: DMA global Status register.
+ * @reserved1: reserved.
+ * @ep_sel: DMA endpoint select register.
+ * @ep_traddr: DMA endpoint transfer ring address register.
+ * @ep_cfg: DMA endpoint configuration register.
+ * @ep_cmd: DMA endpoint command register.
+ * @ep_sts: DMA endpoint status register.
+ * @reserved2: reserved.
+ * @ep_sts_en: DMA endpoint status enable register.
+ * @drbl: DMA doorbell register.
+ * @ep_ien: DMA endpoint interrupt enable register.
+ * @ep_ists: DMA endpoint interrupt status register.
+ * @axim_ctrl: AXI Master Control register.
+ * @axim_id: AXI Master ID register.
+ * @reserved3: reserved.
+ * @axim_cap: AXI Master Wrapper Extended Capability.
+ * @reserved4: reserved.
+ * @axim_ctrl0: AXI Master Wrapper Extended Capability Control Register 0.
+ * @axim_ctrl1: AXI Master Wrapper Extended Capability Control Register 1.
+ */
+struct cdns2_adma_regs {
+ __le32 conf;
+ __le32 sts;
+ __le32 reserved1[5];
+ __le32 ep_sel;
+ __le32 ep_traddr;
+ __le32 ep_cfg;
+ __le32 ep_cmd;
+ __le32 ep_sts;
+ __le32 reserved2;
+ __le32 ep_sts_en;
+ __le32 drbl;
+ __le32 ep_ien;
+ __le32 ep_ists;
+ __le32 axim_ctrl;
+ __le32 axim_id;
+ __le32 reserved3;
+ __le32 axim_cap;
+ __le32 reserved4;
+ __le32 axim_ctrl0;
+ __le32 axim_ctrl1;
+};
+
+#define CDNS2_ADMA_REGS_OFFSET 0x400
+
+/* DMA_CONF - bitmasks. */
+/* Reset USB device configuration. */
+#define DMA_CONF_CFGRST BIT(0)
+/* Singular DMA transfer mode.*/
+#define DMA_CONF_DSING BIT(8)
+/* Multiple DMA transfers mode.*/
+#define DMA_CONF_DMULT BIT(9)
+
+/* DMA_EP_CFG - bitmasks. */
+/* Endpoint enable. */
+#define DMA_EP_CFG_ENABLE BIT(0)
+
+/* DMA_EP_CMD - bitmasks. */
+/* Endpoint reset. */
+#define DMA_EP_CMD_EPRST BIT(0)
+/* Transfer descriptor ready. */
+#define DMA_EP_CMD_DRDY BIT(6)
+/* Data flush. */
+#define DMA_EP_CMD_DFLUSH BIT(7)
+
+/* DMA_EP_STS - bitmasks. */
+/* Interrupt On Complete. */
+#define DMA_EP_STS_IOC BIT(2)
+/* Interrupt on Short Packet. */
+#define DMA_EP_STS_ISP BIT(3)
+/* Transfer descriptor missing. */
+#define DMA_EP_STS_DESCMIS BIT(4)
+/* TRB error. */
+#define DMA_EP_STS_TRBERR BIT(7)
+/* DMA busy bit. */
+#define DMA_EP_STS_DBUSY BIT(9)
+/* Current Cycle Status. */
+#define DMA_EP_STS_CCS(p) ((p) & BIT(11))
+/* OUT size mismatch. */
+#define DMA_EP_STS_OUTSMM BIT(14)
+/* ISO transmission error. */
+#define DMA_EP_STS_ISOERR BIT(15)
+
+/* DMA_EP_STS_EN - bitmasks. */
+/* OUT transfer missing descriptor enable. */
+#define DMA_EP_STS_EN_DESCMISEN BIT(4)
+/* TRB enable. */
+#define DMA_EP_STS_EN_TRBERREN BIT(7)
+/* OUT size mismatch enable. */
+#define DMA_EP_STS_EN_OUTSMMEN BIT(14)
+/* ISO transmission error enable. */
+#define DMA_EP_STS_EN_ISOERREN BIT(15)
+
+/* DMA_EP_IEN - bitmasks. */
+#define DMA_EP_IEN(index) (1 << (index))
+#define DMA_EP_IEN_EP_OUT0 BIT(0)
+#define DMA_EP_IEN_EP_IN0 BIT(16)
+
+/* DMA_EP_ISTS - bitmasks. */
+#define DMA_EP_ISTS(index) (1 << (index))
+#define DMA_EP_ISTS_EP_OUT0 BIT(0)
+#define DMA_EP_ISTS_EP_IN0 BIT(16)
+
+#define gadget_to_cdns2_device(g) (container_of(g, struct cdns2_device, gadget))
+#define ep_to_cdns2_ep(ep) (container_of(ep, struct cdns2_endpoint, endpoint))
+
+/*-------------------------------------------------------------------------*/
+#define TRBS_PER_SEGMENT 600
+#define ISO_MAX_INTERVAL 8
+#define MAX_TRB_LENGTH BIT(16)
+#define MAX_ISO_SIZE 3076
+/*
+ * To improve performance the TRB buffer pointers can't cross
+ * 4KB boundaries.
+ */
+#define TRB_MAX_ISO_BUFF_SHIFT 12
+#define TRB_MAX_ISO_BUFF_SIZE BIT(TRB_MAX_ISO_BUFF_SHIFT)
+/* How much data is left before the 4KB boundary? */
+#define TRB_BUFF_LEN_UP_TO_BOUNDARY(addr) (TRB_MAX_ISO_BUFF_SIZE - \
+ ((addr) & (TRB_MAX_ISO_BUFF_SIZE - 1)))
+
+#if TRBS_PER_SEGMENT < 2
+#error "Incorrect TRBS_PER_SEGMENT. Minimal Transfer Ring size is 2."
+#endif
+
+/**
+ * struct cdns2_trb - represent Transfer Descriptor block.
+ * @buffer: pointer to buffer data.
+ * @length: length of data.
+ * @control: control flags.
+ *
+ * This structure describes transfer block handled by DMA module.
+ */
+struct cdns2_trb {
+ __le32 buffer;
+ __le32 length;
+ __le32 control;
+};
+
+#define TRB_SIZE (sizeof(struct cdns2_trb))
+/*
+ * These two extra TRBs are reserved for isochronous transfer
+ * to inject 0 length packet and extra LINK TRB to synchronize the ISO transfer.
+ */
+#define TRB_ISO_RESERVED 2
+#define TR_SEG_SIZE (TRB_SIZE * (TRBS_PER_SEGMENT + TRB_ISO_RESERVED))
+
+/* TRB bit mask. */
+#define TRB_TYPE_BITMASK GENMASK(15, 10)
+#define TRB_TYPE(p) ((p) << 10)
+#define TRB_FIELD_TO_TYPE(p) (((p) & TRB_TYPE_BITMASK) >> 10)
+
+/* TRB type IDs. */
+/* Used for Bulk, Interrupt, ISOC, and control data stage. */
+#define TRB_NORMAL 1
+/* TRB for linking ring segments. */
+#define TRB_LINK 6
+
+/* Cycle bit - indicates TRB ownership by driver or hw. */
+#define TRB_CYCLE BIT(0)
+/*
+ * When set to '1', the device will toggle its interpretation of the Cycle bit.
+ */
+#define TRB_TOGGLE BIT(1)
+/* Interrupt on short packet. */
+#define TRB_ISP BIT(2)
+/* Chain bit associate this TRB with next one TRB. */
+#define TRB_CHAIN BIT(4)
+/* Interrupt on completion. */
+#define TRB_IOC BIT(5)
+
+/* Transfer_len bitmasks. */
+#define TRB_LEN(p) ((p) & GENMASK(16, 0))
+#define TRB_BURST(p) (((p) << 24) & GENMASK(31, 24))
+#define TRB_FIELD_TO_BURST(p) (((p) & GENMASK(31, 24)) >> 24)
+
+/* Data buffer pointer bitmasks. */
+#define TRB_BUFFER(p) ((p) & GENMASK(31, 0))
+
+/*-------------------------------------------------------------------------*/
+/* Driver numeric constants. */
+
+/* Maximum address that can be assigned to device. */
+#define USB_DEVICE_MAX_ADDRESS 127
+
+/* One control and 15 IN and 15 OUT endpoints. */
+#define CDNS2_ENDPOINTS_NUM 31
+
+#define CDNS2_EP_ZLP_BUF_SIZE 512
+
+/*-------------------------------------------------------------------------*/
+/* Used structures. */
+
+struct cdns2_device;
+
+/**
+ * struct cdns2_ring - transfer ring representation.
+ * @trbs: pointer to transfer ring.
+ * @dma: dma address of transfer ring.
+ * @free_trbs: number of free TRBs in transfer ring.
+ * @pcs: producer cycle state.
+ * @ccs: consumer cycle state.
+ * @enqueue: enqueue index in transfer ring.
+ * @dequeue: dequeue index in transfer ring.
+ */
+struct cdns2_ring {
+ struct cdns2_trb *trbs;
+ dma_addr_t dma;
+ int free_trbs;
+ u8 pcs;
+ u8 ccs;
+ int enqueue;
+ int dequeue;
+};
+
+/**
+ * struct cdns2_endpoint - extended device side representation of USB endpoint.
+ * @endpoint: usb endpoint.
+ * @pending_list: list of requests queuing on transfer ring.
+ * @deferred_list: list of requests waiting for queuing on transfer ring.
+ * @pdev: device associated with endpoint.
+ * @name: a human readable name e.g. ep1out.
+ * @ring: transfer ring associated with endpoint.
+ * @ep_state: state of endpoint.
+ * @idx: index of endpoint in pdev->eps table.
+ * @dir: endpoint direction.
+ * @num: endpoint number (1 - 15).
+ * @type: set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK.
+ * @interval: interval between packets used for ISOC and Interrupt endpoint.
+ * @buffering: on-chip buffers assigned to endpoint.
+ * @trb_burst_size: number of burst used in TRB.
+ * @skip: Sometimes the controller cannot process isochronous endpoint ring
+ * quickly enough and it will miss some isoc tds on the ring and
+ * generate ISO transmition error.
+ * Driver sets skip flag when receive a ISO transmition error and
+ * process the missed TDs on the endpoint ring.
+ * @wa1_set: use WA1.
+ * @wa1_trb: TRB assigned to WA1.
+ * @wa1_trb_index: TRB index for WA1.
+ * @wa1_cycle_bit: correct cycle bit for WA1.
+ */
+struct cdns2_endpoint {
+ struct usb_ep endpoint;
+ struct list_head pending_list;
+ struct list_head deferred_list;
+
+ struct cdns2_device *pdev;
+ char name[20];
+
+ struct cdns2_ring ring;
+
+#define EP_ENABLED BIT(0)
+#define EP_STALLED BIT(1)
+#define EP_STALL_PENDING BIT(2)
+#define EP_WEDGE BIT(3)
+#define EP_CLAIMED BIT(4)
+#define EP_RING_FULL BIT(5)
+#define EP_DEFERRED_DRDY BIT(6)
+
+ u32 ep_state;
+
+ u8 idx;
+ u8 dir;
+ u8 num;
+ u8 type;
+ int interval;
+ u8 buffering;
+ u8 trb_burst_size;
+ bool skip;
+
+ unsigned int wa1_set:1;
+ struct cdns2_trb *wa1_trb;
+ unsigned int wa1_trb_index;
+ unsigned int wa1_cycle_bit:1;
+};
+
+/**
+ * struct cdns2_request - extended device side representation of usb_request
+ * object.
+ * @request: generic usb_request object describing single I/O request.
+ * @pep: extended representation of usb_ep object.
+ * @trb: the first TRB association with this request.
+ * @start_trb: number of the first TRB in transfer ring.
+ * @end_trb: number of the last TRB in transfer ring.
+ * @list: used for queuing request in lists.
+ * @finished_trb: number of trb has already finished per request.
+ * @num_of_trb: how many trbs are associated with request.
+ */
+struct cdns2_request {
+ struct usb_request request;
+ struct cdns2_endpoint *pep;
+ struct cdns2_trb *trb;
+ int start_trb;
+ int end_trb;
+ struct list_head list;
+ int finished_trb;
+ int num_of_trb;
+};
+
+#define to_cdns2_request(r) (container_of(r, struct cdns2_request, request))
+
+/* Stages used during enumeration process.*/
+#define CDNS2_SETUP_STAGE 0x0
+#define CDNS2_DATA_STAGE 0x1
+#define CDNS2_STATUS_STAGE 0x2
+
+/**
+ * struct cdns2_device - represent USB device.
+ * @dev: pointer to device structure associated whit this controller.
+ * @gadget: device side representation of the peripheral controller.
+ * @gadget_driver: pointer to the gadget driver.
+ * @lock: for synchronizing.
+ * @irq: interrupt line number.
+ * @regs: base address for registers
+ * @usb_regs: base address for common USB registers.
+ * @ep0_regs: base address for endpoint 0 related registers.
+ * @epx_regs: base address for all none control endpoint registers.
+ * @interrupt_regs: base address for interrupt handling related registers.
+ * @adma_regs: base address for ADMA registers.
+ * @eps_dma_pool: endpoint Transfer Ring pool.
+ * @setup: used while processing usb control requests.
+ * @ep0_preq: private request used while handling EP0.
+ * @ep0_stage: ep0 stage during enumeration process.
+ * @zlp_buf: zlp buffer.
+ * @dev_address: device address assigned by host.
+ * @eps: array of objects describing endpoints.
+ * @selected_ep: actually selected endpoint. It's used only to improve
+ * performance by limiting access to dma_ep_sel register.
+ * @is_selfpowered: device is self powered.
+ * @may_wakeup: allows device to remote wakeup the host.
+ * @status_completion_no_call: indicate that driver is waiting for status
+ * stage completion. It's used in deferred SET_CONFIGURATION request.
+ * @in_lpm: indicate the controller is in low power mode.
+ * @pending_status_wq: workqueue handling status stage for deferred requests.
+ * @pending_status_request: request for which status stage was deferred.
+ * @eps_supported: endpoints supported by controller in form:
+ * bit: 0 - ep0, 1 - epOut1, 2 - epIn1, 3 - epOut2 ...
+ * @burst_opt: array with the best burst size value for different TRB size.
+ * @onchip_tx_buf: size of transmit on-chip buffer in KB.
+ * @onchip_rx_buf: size of receive on-chip buffer in KB.
+ */
+struct cdns2_device {
+ struct device *dev;
+ struct usb_gadget gadget;
+ struct usb_gadget_driver *gadget_driver;
+
+ /* generic spin-lock for drivers */
+ spinlock_t lock;
+ int irq;
+ void __iomem *regs;
+ struct cdns2_usb_regs __iomem *usb_regs;
+ struct cdns2_ep0_regs __iomem *ep0_regs;
+ struct cdns2_epx_regs __iomem *epx_regs;
+ struct cdns2_interrupt_regs __iomem *interrupt_regs;
+ struct cdns2_adma_regs __iomem *adma_regs;
+ struct dma_pool *eps_dma_pool;
+ struct usb_ctrlrequest setup;
+ struct cdns2_request ep0_preq;
+ u8 ep0_stage;
+ void *zlp_buf;
+ u8 dev_address;
+ struct cdns2_endpoint eps[CDNS2_ENDPOINTS_NUM];
+ u32 selected_ep;
+ bool is_selfpowered;
+ bool may_wakeup;
+ bool status_completion_no_call;
+ bool in_lpm;
+ struct work_struct pending_status_wq;
+ struct usb_request *pending_status_request;
+ u32 eps_supported;
+ u8 burst_opt[MAX_ISO_SIZE + 1];
+
+ /*in KB */
+ u16 onchip_tx_buf;
+ u16 onchip_rx_buf;
+};
+
+#define CDNS2_IF_EP_EXIST(pdev, ep_num, dir) \
+ ((pdev)->eps_supported & \
+ (BIT(ep_num) << ((dir) ? 0 : 16)))
+
+dma_addr_t cdns2_trb_virt_to_dma(struct cdns2_endpoint *pep,
+ struct cdns2_trb *trb);
+void cdns2_pending_setup_status_handler(struct work_struct *work);
+void cdns2_select_ep(struct cdns2_device *pdev, u32 ep);
+struct cdns2_request *cdns2_next_preq(struct list_head *list);
+struct usb_request *cdns2_gadget_ep_alloc_request(struct usb_ep *ep,
+ gfp_t gfp_flags);
+void cdns2_gadget_ep_free_request(struct usb_ep *ep,
+ struct usb_request *request);
+int cdns2_gadget_ep_dequeue(struct usb_ep *ep, struct usb_request *request);
+void cdns2_gadget_giveback(struct cdns2_endpoint *pep,
+ struct cdns2_request *priv_req,
+ int status);
+void cdns2_init_ep0(struct cdns2_device *pdev, struct cdns2_endpoint *pep);
+void cdns2_ep0_config(struct cdns2_device *pdev);
+void cdns2_handle_ep0_interrupt(struct cdns2_device *pdev, int dir);
+void cdns2_handle_setup_packet(struct cdns2_device *pdev);
+int cdns2_gadget_resume(struct cdns2_device *pdev, bool hibernated);
+int cdns2_gadget_suspend(struct cdns2_device *pdev);
+void cdns2_gadget_remove(struct cdns2_device *pdev);
+int cdns2_gadget_init(struct cdns2_device *pdev);
+void set_reg_bit_8(void __iomem *ptr, u8 mask);
+int cdns2_halt_endpoint(struct cdns2_device *pdev, struct cdns2_endpoint *pep,
+ int value);
+
+#endif /* __LINUX_CDNS2_GADGET */
diff --git a/drivers/usb/gadget/udc/cdns2/cdns2-pci.c b/drivers/usb/gadget/udc/cdns2/cdns2-pci.c
new file mode 100644
index 000000000000..1691541c9413
--- /dev/null
+++ b/drivers/usb/gadget/udc/cdns2/cdns2-pci.c
@@ -0,0 +1,138 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Cadence USBHS-DEV controller - PCI Glue driver.
+ *
+ * Copyright (C) 2023 Cadence.
+ *
+ * Author: Pawel Laszczak <pawell@cadence.com>
+ *
+ */
+
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+
+#include "cdns2-gadget.h"
+
+#define PCI_DRIVER_NAME "cdns-pci-usbhs"
+#define CDNS_VENDOR_ID 0x17cd
+#define CDNS_DEVICE_ID 0x0120
+#define PCI_BAR_DEV 0
+#define PCI_DEV_FN_DEVICE 0
+
+static int cdns2_pci_probe(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ resource_size_t rsrc_start, rsrc_len;
+ struct device *dev = &pdev->dev;
+ struct cdns2_device *priv_dev;
+ struct resource *res;
+ int ret;
+
+ /* For GADGET PCI (devfn) function number is 0. */
+ if (!id || pdev->devfn != PCI_DEV_FN_DEVICE ||
+ pdev->class != PCI_CLASS_SERIAL_USB_DEVICE)
+ return -EINVAL;
+
+ ret = pcim_enable_device(pdev);
+ if (ret) {
+ dev_err(&pdev->dev, "Enabling PCI device has failed %d\n", ret);
+ return ret;
+ }
+
+ pci_set_master(pdev);
+
+ priv_dev = devm_kzalloc(&pdev->dev, sizeof(*priv_dev), GFP_KERNEL);
+ if (!priv_dev)
+ return -ENOMEM;
+
+ dev_dbg(dev, "Initialize resources\n");
+ rsrc_start = pci_resource_start(pdev, PCI_BAR_DEV);
+ rsrc_len = pci_resource_len(pdev, PCI_BAR_DEV);
+
+ res = devm_request_mem_region(dev, rsrc_start, rsrc_len, "dev");
+ if (!res) {
+ dev_dbg(dev, "controller already in use\n");
+ return -EBUSY;
+ }
+
+ priv_dev->regs = devm_ioremap(dev, rsrc_start, rsrc_len);
+ if (!priv_dev->regs) {
+ dev_dbg(dev, "error mapping memory\n");
+ return -EFAULT;
+ }
+
+ priv_dev->irq = pdev->irq;
+ dev_dbg(dev, "USBSS-DEV physical base addr: %pa\n",
+ &rsrc_start);
+
+ priv_dev->dev = dev;
+
+ priv_dev->eps_supported = 0x000f000f;
+ priv_dev->onchip_tx_buf = 16;
+ priv_dev->onchip_rx_buf = 16;
+
+ ret = cdns2_gadget_init(priv_dev);
+ if (ret)
+ return ret;
+
+ pci_set_drvdata(pdev, priv_dev);
+
+ device_wakeup_enable(&pdev->dev);
+ if (pci_dev_run_wake(pdev))
+ pm_runtime_put_noidle(&pdev->dev);
+
+ return 0;
+}
+
+static void cdns2_pci_remove(struct pci_dev *pdev)
+{
+ struct cdns2_device *priv_dev = pci_get_drvdata(pdev);
+
+ if (pci_dev_run_wake(pdev))
+ pm_runtime_get_noresume(&pdev->dev);
+
+ cdns2_gadget_remove(priv_dev);
+}
+
+static int cdns2_pci_suspend(struct device *dev)
+{
+ struct cdns2_device *priv_dev = dev_get_drvdata(dev);
+
+ return cdns2_gadget_suspend(priv_dev);
+}
+
+static int cdns2_pci_resume(struct device *dev)
+{
+ struct cdns2_device *priv_dev = dev_get_drvdata(dev);
+
+ return cdns2_gadget_resume(priv_dev, 1);
+}
+
+static const struct dev_pm_ops cdns2_pci_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(cdns2_pci_suspend, cdns2_pci_resume)
+};
+
+static const struct pci_device_id cdns2_pci_ids[] = {
+ { PCI_VENDOR_ID_CDNS, CDNS_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,
+ PCI_CLASS_SERIAL_USB_DEVICE, PCI_ANY_ID },
+ { 0, }
+};
+
+static struct pci_driver cdns2_pci_driver = {
+ .name = "cdns2-pci",
+ .id_table = &cdns2_pci_ids[0],
+ .probe = cdns2_pci_probe,
+ .remove = cdns2_pci_remove,
+ .driver = {
+ .pm = pm_ptr(&cdns2_pci_pm_ops),
+ }
+};
+
+module_pci_driver(cdns2_pci_driver);
+MODULE_DEVICE_TABLE(pci, cdns2_pci_ids);
+
+MODULE_ALIAS("pci:cdns2");
+MODULE_AUTHOR("Pawel Laszczak <pawell@cadence.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Cadence CDNS2 PCI driver");
diff --git a/drivers/usb/gadget/udc/cdns2/cdns2-trace.c b/drivers/usb/gadget/udc/cdns2/cdns2-trace.c
new file mode 100644
index 000000000000..de6b8cc3d071
--- /dev/null
+++ b/drivers/usb/gadget/udc/cdns2/cdns2-trace.c
@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * USBHS device controller driver Trace Support
+ *
+ * Copyright (C) 2023 Cadence.
+ *
+ * Author: Pawel Laszczak <pawell@cadence.com>
+ */
+
+#define CREATE_TRACE_POINTS
+#include "cdns2-trace.h"
diff --git a/drivers/usb/gadget/udc/cdns2/cdns2-trace.h b/drivers/usb/gadget/udc/cdns2/cdns2-trace.h
new file mode 100644
index 000000000000..61f241634ea5
--- /dev/null
+++ b/drivers/usb/gadget/udc/cdns2/cdns2-trace.h
@@ -0,0 +1,605 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * USBHS-DEV device controller driver.
+ * Trace support header file.
+ *
+ * Copyright (C) 2023 Cadence.
+ *
+ * Author: Pawel Laszczak <pawell@cadence.com>
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM cdns2-dev
+
+/*
+ * The TRACE_SYSTEM_VAR defaults to TRACE_SYSTEM, but must be a
+ * legitimate C variable. It is not exported to user space.
+ */
+#undef TRACE_SYSTEM_VAR
+#define TRACE_SYSTEM_VAR cdns2_dev
+
+#if !defined(__LINUX_CDNS2_TRACE) || defined(TRACE_HEADER_MULTI_READ)
+#define __LINUX_CDNS2_TRACE
+
+#include <linux/types.h>
+#include <linux/tracepoint.h>
+#include <asm/byteorder.h>
+#include <linux/usb/ch9.h>
+#include "cdns2-gadget.h"
+#include "cdns2-debug.h"
+
+#define CDNS2_MSG_MAX 500
+
+DECLARE_EVENT_CLASS(cdns2_log_enable_disable,
+ TP_PROTO(int set),
+ TP_ARGS(set),
+ TP_STRUCT__entry(
+ __field(int, set)
+ ),
+ TP_fast_assign(
+ __entry->set = set;
+ ),
+ TP_printk("%s", __entry->set ? "enabled" : "disabled")
+);
+
+DEFINE_EVENT(cdns2_log_enable_disable, cdns2_pullup,
+ TP_PROTO(int set),
+ TP_ARGS(set)
+);
+
+DEFINE_EVENT(cdns2_log_enable_disable, cdns2_lpm,
+ TP_PROTO(int set),
+ TP_ARGS(set)
+);
+
+DEFINE_EVENT(cdns2_log_enable_disable, cdns2_may_wakeup,
+ TP_PROTO(int set),
+ TP_ARGS(set)
+);
+
+DECLARE_EVENT_CLASS(cdns2_log_simple,
+ TP_PROTO(char *msg),
+ TP_ARGS(msg),
+ TP_STRUCT__entry(
+ __string(text, msg)
+ ),
+ TP_fast_assign(
+ __assign_str(text, msg);
+ ),
+ TP_printk("%s", __get_str(text))
+);
+
+DEFINE_EVENT(cdns2_log_simple, cdns2_no_room_on_ring,
+ TP_PROTO(char *msg),
+ TP_ARGS(msg)
+);
+
+DEFINE_EVENT(cdns2_log_simple, cdns2_ep0_status_stage,
+ TP_PROTO(char *msg),
+ TP_ARGS(msg)
+);
+
+DEFINE_EVENT(cdns2_log_simple, cdns2_ep0_set_config,
+ TP_PROTO(char *msg),
+ TP_ARGS(msg)
+);
+
+DEFINE_EVENT(cdns2_log_simple, cdns2_ep0_setup,
+ TP_PROTO(char *msg),
+ TP_ARGS(msg)
+);
+
+DEFINE_EVENT(cdns2_log_simple, cdns2_device_state,
+ TP_PROTO(char *msg),
+ TP_ARGS(msg)
+);
+
+TRACE_EVENT(cdns2_ep_halt,
+ TP_PROTO(struct cdns2_endpoint *ep_priv, u8 halt, u8 flush),
+ TP_ARGS(ep_priv, halt, flush),
+ TP_STRUCT__entry(
+ __string(name, ep_priv->name)
+ __field(u8, halt)
+ __field(u8, flush)
+ ),
+ TP_fast_assign(
+ __assign_str(name, ep_priv->name);
+ __entry->halt = halt;
+ __entry->flush = flush;
+ ),
+ TP_printk("Halt %s for %s: %s", __entry->flush ? " and flush" : "",
+ __get_str(name), __entry->halt ? "set" : "cleared")
+);
+
+TRACE_EVENT(cdns2_wa1,
+ TP_PROTO(struct cdns2_endpoint *ep_priv, char *msg),
+ TP_ARGS(ep_priv, msg),
+ TP_STRUCT__entry(
+ __string(ep_name, ep_priv->name)
+ __string(msg, msg)
+ ),
+ TP_fast_assign(
+ __assign_str(ep_name, ep_priv->name);
+ __assign_str(msg, msg);
+ ),
+ TP_printk("WA1: %s %s", __get_str(ep_name), __get_str(msg))
+);
+
+DECLARE_EVENT_CLASS(cdns2_log_doorbell,
+ TP_PROTO(struct cdns2_endpoint *pep, u32 ep_trbaddr),
+ TP_ARGS(pep, ep_trbaddr),
+ TP_STRUCT__entry(
+ __string(name, pep->num ? pep->name :
+ (pep->dir ? "ep0in" : "ep0out"))
+ __field(u32, ep_trbaddr)
+ ),
+ TP_fast_assign(
+ __assign_str(name, pep->name);
+ __entry->ep_trbaddr = ep_trbaddr;
+ ),
+ TP_printk("%s, ep_trbaddr %08x", __get_str(name),
+ __entry->ep_trbaddr)
+);
+
+DEFINE_EVENT(cdns2_log_doorbell, cdns2_doorbell_ep0,
+ TP_PROTO(struct cdns2_endpoint *pep, u32 ep_trbaddr),
+ TP_ARGS(pep, ep_trbaddr)
+);
+
+DEFINE_EVENT(cdns2_log_doorbell, cdns2_doorbell_epx,
+ TP_PROTO(struct cdns2_endpoint *pep, u32 ep_trbaddr),
+ TP_ARGS(pep, ep_trbaddr)
+);
+
+DECLARE_EVENT_CLASS(cdns2_log_usb_irq,
+ TP_PROTO(u32 usb_irq, u32 ext_irq),
+ TP_ARGS(usb_irq, ext_irq),
+ TP_STRUCT__entry(
+ __field(u32, usb_irq)
+ __field(u32, ext_irq)
+ ),
+ TP_fast_assign(
+ __entry->usb_irq = usb_irq;
+ __entry->ext_irq = ext_irq;
+ ),
+ TP_printk("%s", cdns2_decode_usb_irq(__get_buf(CDNS2_MSG_MAX),
+ CDNS2_MSG_MAX,
+ __entry->usb_irq,
+ __entry->ext_irq))
+);
+
+DEFINE_EVENT(cdns2_log_usb_irq, cdns2_usb_irq,
+ TP_PROTO(u32 usb_irq, u32 ext_irq),
+ TP_ARGS(usb_irq, ext_irq)
+);
+
+TRACE_EVENT(cdns2_dma_ep_ists,
+ TP_PROTO(u32 dma_ep_ists),
+ TP_ARGS(dma_ep_ists),
+ TP_STRUCT__entry(
+ __field(u32, dma_ep_ists)
+ ),
+ TP_fast_assign(
+ __entry->dma_ep_ists = dma_ep_ists;
+ ),
+ TP_printk("OUT: 0x%04x, IN: 0x%04x", (u16)__entry->dma_ep_ists,
+ __entry->dma_ep_ists >> 16)
+);
+
+DECLARE_EVENT_CLASS(cdns2_log_epx_irq,
+ TP_PROTO(struct cdns2_device *pdev, struct cdns2_endpoint *pep),
+ TP_ARGS(pdev, pep),
+ TP_STRUCT__entry(
+ __string(ep_name, pep->name)
+ __field(u32, ep_sts)
+ __field(u32, ep_ists)
+ __field(u32, ep_traddr)
+ ),
+ TP_fast_assign(
+ __assign_str(ep_name, pep->name);
+ __entry->ep_sts = readl(&pdev->adma_regs->ep_sts);
+ __entry->ep_ists = readl(&pdev->adma_regs->ep_ists);
+ __entry->ep_traddr = readl(&pdev->adma_regs->ep_traddr);
+ ),
+ TP_printk("%s, ep_traddr: %08x",
+ cdns2_decode_epx_irq(__get_buf(CDNS2_MSG_MAX), CDNS2_MSG_MAX,
+ __get_str(ep_name),
+ __entry->ep_ists, __entry->ep_sts),
+ __entry->ep_traddr)
+);
+
+DEFINE_EVENT(cdns2_log_epx_irq, cdns2_epx_irq,
+ TP_PROTO(struct cdns2_device *pdev, struct cdns2_endpoint *pep),
+ TP_ARGS(pdev, pep)
+);
+
+DECLARE_EVENT_CLASS(cdns2_log_ep0_irq,
+ TP_PROTO(struct cdns2_device *pdev),
+ TP_ARGS(pdev),
+ TP_STRUCT__entry(
+ __field(int, ep_dir)
+ __field(u32, ep_ists)
+ __field(u32, ep_sts)
+ ),
+ TP_fast_assign(
+ __entry->ep_dir = pdev->selected_ep;
+ __entry->ep_ists = readl(&pdev->adma_regs->ep_ists);
+ __entry->ep_sts = readl(&pdev->adma_regs->ep_sts);
+ ),
+ TP_printk("%s", cdns2_decode_ep0_irq(__get_buf(CDNS2_MSG_MAX),
+ CDNS2_MSG_MAX,
+ __entry->ep_ists, __entry->ep_sts,
+ __entry->ep_dir))
+);
+
+DEFINE_EVENT(cdns2_log_ep0_irq, cdns2_ep0_irq,
+ TP_PROTO(struct cdns2_device *pdev),
+ TP_ARGS(pdev)
+);
+
+DECLARE_EVENT_CLASS(cdns2_log_ctrl,
+ TP_PROTO(struct usb_ctrlrequest *ctrl),
+ TP_ARGS(ctrl),
+ TP_STRUCT__entry(
+ __field(u8, bRequestType)
+ __field(u8, bRequest)
+ __field(u16, wValue)
+ __field(u16, wIndex)
+ __field(u16, wLength)
+ ),
+ TP_fast_assign(
+ __entry->bRequestType = ctrl->bRequestType;
+ __entry->bRequest = ctrl->bRequest;
+ __entry->wValue = le16_to_cpu(ctrl->wValue);
+ __entry->wIndex = le16_to_cpu(ctrl->wIndex);
+ __entry->wLength = le16_to_cpu(ctrl->wLength);
+ ),
+ TP_printk("%s", usb_decode_ctrl(__get_buf(CDNS2_MSG_MAX), CDNS2_MSG_MAX,
+ __entry->bRequestType,
+ __entry->bRequest, __entry->wValue,
+ __entry->wIndex, __entry->wLength)
+ )
+);
+
+DEFINE_EVENT(cdns2_log_ctrl, cdns2_ctrl_req,
+ TP_PROTO(struct usb_ctrlrequest *ctrl),
+ TP_ARGS(ctrl)
+);
+
+DECLARE_EVENT_CLASS(cdns2_log_request,
+ TP_PROTO(struct cdns2_request *preq),
+ TP_ARGS(preq),
+ TP_STRUCT__entry(
+ __string(name, preq->pep->name)
+ __field(struct usb_request *, request)
+ __field(struct cdns2_request *, preq)
+ __field(void *, buf)
+ __field(unsigned int, actual)
+ __field(unsigned int, length)
+ __field(int, status)
+ __field(dma_addr_t, dma)
+ __field(int, zero)
+ __field(int, short_not_ok)
+ __field(int, no_interrupt)
+ __field(struct scatterlist*, sg)
+ __field(unsigned int, num_sgs)
+ __field(unsigned int, num_mapped_sgs)
+ __field(int, start_trb)
+ __field(int, end_trb)
+ ),
+ TP_fast_assign(
+ __assign_str(name, preq->pep->name);
+ __entry->request = &preq->request;
+ __entry->preq = preq;
+ __entry->buf = preq->request.buf;
+ __entry->actual = preq->request.actual;
+ __entry->length = preq->request.length;
+ __entry->status = preq->request.status;
+ __entry->dma = preq->request.dma;
+ __entry->zero = preq->request.zero;
+ __entry->short_not_ok = preq->request.short_not_ok;
+ __entry->no_interrupt = preq->request.no_interrupt;
+ __entry->sg = preq->request.sg;
+ __entry->num_sgs = preq->request.num_sgs;
+ __entry->num_mapped_sgs = preq->request.num_mapped_sgs;
+ __entry->start_trb = preq->start_trb;
+ __entry->end_trb = preq->end_trb;
+ ),
+ TP_printk("%s: req: %p, preq: %p, req buf: %p, length: %u/%u, status: %d,"
+ "buf dma: (%pad), %s%s%s, sg: %p, num_sgs: %d, num_m_sgs: %d,"
+ "trb: [start: %d, end: %d]",
+ __get_str(name), __entry->request, __entry->preq,
+ __entry->buf, __entry->actual, __entry->length,
+ __entry->status, &__entry->dma,
+ __entry->zero ? "Z" : "z",
+ __entry->short_not_ok ? "S" : "s",
+ __entry->no_interrupt ? "I" : "i",
+ __entry->sg, __entry->num_sgs, __entry->num_mapped_sgs,
+ __entry->start_trb,
+ __entry->end_trb
+ )
+);
+
+DEFINE_EVENT(cdns2_log_request, cdns2_request_enqueue,
+ TP_PROTO(struct cdns2_request *preq),
+ TP_ARGS(preq)
+);
+
+DEFINE_EVENT(cdns2_log_request, cdns2_request_enqueue_error,
+ TP_PROTO(struct cdns2_request *preq),
+ TP_ARGS(preq)
+);
+
+DEFINE_EVENT(cdns2_log_request, cdns2_alloc_request,
+ TP_PROTO(struct cdns2_request *preq),
+ TP_ARGS(preq)
+);
+
+DEFINE_EVENT(cdns2_log_request, cdns2_free_request,
+ TP_PROTO(struct cdns2_request *preq),
+ TP_ARGS(preq)
+);
+
+DEFINE_EVENT(cdns2_log_request, cdns2_ep_queue,
+ TP_PROTO(struct cdns2_request *preq),
+ TP_ARGS(preq)
+);
+
+DEFINE_EVENT(cdns2_log_request, cdns2_request_dequeue,
+ TP_PROTO(struct cdns2_request *preq),
+ TP_ARGS(preq)
+);
+
+DEFINE_EVENT(cdns2_log_request, cdns2_request_giveback,
+ TP_PROTO(struct cdns2_request *preq),
+ TP_ARGS(preq)
+);
+
+TRACE_EVENT(cdns2_ep0_enqueue,
+ TP_PROTO(struct cdns2_device *dev_priv, struct usb_request *request),
+ TP_ARGS(dev_priv, request),
+ TP_STRUCT__entry(
+ __field(int, dir)
+ __field(int, length)
+ ),
+ TP_fast_assign(
+ __entry->dir = dev_priv->eps[0].dir;
+ __entry->length = request->length;
+ ),
+ TP_printk("Queue to ep0%s length: %u", __entry->dir ? "in" : "out",
+ __entry->length)
+);
+
+DECLARE_EVENT_CLASS(cdns2_log_map_request,
+ TP_PROTO(struct cdns2_request *priv_req),
+ TP_ARGS(priv_req),
+ TP_STRUCT__entry(
+ __string(name, priv_req->pep->name)
+ __field(struct usb_request *, req)
+ __field(void *, buf)
+ __field(dma_addr_t, dma)
+ ),
+ TP_fast_assign(
+ __assign_str(name, priv_req->pep->name);
+ __entry->req = &priv_req->request;
+ __entry->buf = priv_req->request.buf;
+ __entry->dma = priv_req->request.dma;
+ ),
+ TP_printk("%s: req: %p, req buf %p, dma %p",
+ __get_str(name), __entry->req, __entry->buf, &__entry->dma
+ )
+);
+
+DEFINE_EVENT(cdns2_log_map_request, cdns2_map_request,
+ TP_PROTO(struct cdns2_request *req),
+ TP_ARGS(req)
+);
+DEFINE_EVENT(cdns2_log_map_request, cdns2_mapped_request,
+ TP_PROTO(struct cdns2_request *req),
+ TP_ARGS(req)
+);
+
+DECLARE_EVENT_CLASS(cdns2_log_trb,
+ TP_PROTO(struct cdns2_endpoint *pep, struct cdns2_trb *trb),
+ TP_ARGS(pep, trb),
+ TP_STRUCT__entry(
+ __string(name, pep->name)
+ __field(struct cdns2_trb *, trb)
+ __field(u32, buffer)
+ __field(u32, length)
+ __field(u32, control)
+ __field(u32, type)
+ ),
+ TP_fast_assign(
+ __assign_str(name, pep->name);
+ __entry->trb = trb;
+ __entry->buffer = le32_to_cpu(trb->buffer);
+ __entry->length = le32_to_cpu(trb->length);
+ __entry->control = le32_to_cpu(trb->control);
+ __entry->type = usb_endpoint_type(pep->endpoint.desc);
+ ),
+ TP_printk("%s: trb V: %p, dma buf: P: 0x%08x, %s",
+ __get_str(name), __entry->trb, __entry->buffer,
+ cdns2_decode_trb(__get_buf(CDNS2_MSG_MAX), CDNS2_MSG_MAX,
+ __entry->control, __entry->length,
+ __entry->buffer))
+);
+
+DEFINE_EVENT(cdns2_log_trb, cdns2_queue_trb,
+ TP_PROTO(struct cdns2_endpoint *pep, struct cdns2_trb *trb),
+ TP_ARGS(pep, trb)
+);
+
+DEFINE_EVENT(cdns2_log_trb, cdns2_complete_trb,
+ TP_PROTO(struct cdns2_endpoint *pep, struct cdns2_trb *trb),
+ TP_ARGS(pep, trb)
+);
+
+DECLARE_EVENT_CLASS(cdns2_log_ring,
+ TP_PROTO(struct cdns2_endpoint *pep),
+ TP_ARGS(pep),
+ TP_STRUCT__entry(
+ __dynamic_array(u8, tr_seg, TR_SEG_SIZE)
+ __dynamic_array(u8, pep, sizeof(struct cdns2_endpoint))
+ __dynamic_array(char, buffer,
+ (TRBS_PER_SEGMENT * 65) + CDNS2_MSG_MAX)
+ ),
+ TP_fast_assign(
+ memcpy(__get_dynamic_array(pep), pep,
+ sizeof(struct cdns2_endpoint));
+ memcpy(__get_dynamic_array(tr_seg), pep->ring.trbs,
+ TR_SEG_SIZE);
+ ),
+
+ TP_printk("%s",
+ cdns2_raw_ring((struct cdns2_endpoint *)__get_str(pep),
+ (struct cdns2_trb *)__get_str(tr_seg),
+ __get_str(buffer),
+ (TRBS_PER_SEGMENT * 65) + CDNS2_MSG_MAX))
+);
+
+DEFINE_EVENT(cdns2_log_ring, cdns2_ring,
+ TP_PROTO(struct cdns2_endpoint *pep),
+ TP_ARGS(pep)
+);
+
+DECLARE_EVENT_CLASS(cdns2_log_ep,
+ TP_PROTO(struct cdns2_endpoint *pep),
+ TP_ARGS(pep),
+ TP_STRUCT__entry(
+ __string(name, pep->name)
+ __field(unsigned int, maxpacket)
+ __field(unsigned int, maxpacket_limit)
+ __field(unsigned int, flags)
+ __field(unsigned int, dir)
+ __field(u8, enqueue)
+ __field(u8, dequeue)
+ ),
+ TP_fast_assign(
+ __assign_str(name, pep->name);
+ __entry->maxpacket = pep->endpoint.maxpacket;
+ __entry->maxpacket_limit = pep->endpoint.maxpacket_limit;
+ __entry->flags = pep->ep_state;
+ __entry->dir = pep->dir;
+ __entry->enqueue = pep->ring.enqueue;
+ __entry->dequeue = pep->ring.dequeue;
+ ),
+ TP_printk("%s: mps: %d/%d, enq idx: %d, deq idx: %d, "
+ "flags: %s%s%s%s, dir: %s",
+ __get_str(name), __entry->maxpacket,
+ __entry->maxpacket_limit, __entry->enqueue,
+ __entry->dequeue,
+ __entry->flags & EP_ENABLED ? "EN | " : "",
+ __entry->flags & EP_STALLED ? "STALLED | " : "",
+ __entry->flags & EP_WEDGE ? "WEDGE | " : "",
+ __entry->flags & EP_RING_FULL ? "RING FULL |" : "",
+ __entry->dir ? "IN" : "OUT"
+ )
+);
+
+DEFINE_EVENT(cdns2_log_ep, cdns2_gadget_ep_enable,
+ TP_PROTO(struct cdns2_endpoint *pep),
+ TP_ARGS(pep)
+);
+
+DEFINE_EVENT(cdns2_log_ep, cdns2_gadget_ep_disable,
+ TP_PROTO(struct cdns2_endpoint *pep),
+ TP_ARGS(pep)
+);
+
+DEFINE_EVENT(cdns2_log_ep, cdns2_iso_out_ep_disable,
+ TP_PROTO(struct cdns2_endpoint *pep),
+ TP_ARGS(pep)
+);
+
+DEFINE_EVENT(cdns2_log_ep, cdns2_ep_busy_try_halt_again,
+ TP_PROTO(struct cdns2_endpoint *pep),
+ TP_ARGS(pep)
+);
+
+DECLARE_EVENT_CLASS(cdns2_log_request_handled,
+ TP_PROTO(struct cdns2_request *priv_req, int current_index,
+ int handled),
+ TP_ARGS(priv_req, current_index, handled),
+ TP_STRUCT__entry(
+ __field(struct cdns2_request *, priv_req)
+ __field(unsigned int, dma_position)
+ __field(unsigned int, handled)
+ __field(unsigned int, dequeue_idx)
+ __field(unsigned int, enqueue_idx)
+ __field(unsigned int, start_trb)
+ __field(unsigned int, end_trb)
+ ),
+ TP_fast_assign(
+ __entry->priv_req = priv_req;
+ __entry->dma_position = current_index;
+ __entry->handled = handled;
+ __entry->dequeue_idx = priv_req->pep->ring.dequeue;
+ __entry->enqueue_idx = priv_req->pep->ring.enqueue;
+ __entry->start_trb = priv_req->start_trb;
+ __entry->end_trb = priv_req->end_trb;
+ ),
+ TP_printk("Req: %p %s, DMA pos: %d, ep deq: %d, ep enq: %d,"
+ " start trb: %d, end trb: %d",
+ __entry->priv_req,
+ __entry->handled ? "handled" : "not handled",
+ __entry->dma_position, __entry->dequeue_idx,
+ __entry->enqueue_idx, __entry->start_trb,
+ __entry->end_trb
+ )
+);
+
+DEFINE_EVENT(cdns2_log_request_handled, cdns2_request_handled,
+ TP_PROTO(struct cdns2_request *priv_req, int current_index,
+ int handled),
+ TP_ARGS(priv_req, current_index, handled)
+);
+
+DECLARE_EVENT_CLASS(cdns2_log_epx_reg_config,
+ TP_PROTO(struct cdns2_device *pdev, struct cdns2_endpoint *pep),
+ TP_ARGS(pdev, pep),
+ TP_STRUCT__entry(
+ __string(ep_name, pep->name)
+ __field(u8, burst_size)
+ __field(__le16, maxpack_reg)
+ __field(__u8, con_reg)
+ __field(u32, ep_sel_reg)
+ __field(u32, ep_sts_en_reg)
+ __field(u32, ep_cfg_reg)
+ ),
+ TP_fast_assign(
+ __assign_str(ep_name, pep->name);
+ __entry->burst_size = pep->trb_burst_size;
+ __entry->maxpack_reg = pep->dir ? readw(&pdev->epx_regs->txmaxpack[pep->num - 1]) :
+ readw(&pdev->epx_regs->rxmaxpack[pep->num - 1]);
+ __entry->con_reg = pep->dir ? readb(&pdev->epx_regs->ep[pep->num - 1].txcon) :
+ readb(&pdev->epx_regs->ep[pep->num - 1].rxcon);
+ __entry->ep_sel_reg = readl(&pdev->adma_regs->ep_sel);
+ __entry->ep_sts_en_reg = readl(&pdev->adma_regs->ep_sts_en);
+ __entry->ep_cfg_reg = readl(&pdev->adma_regs->ep_cfg);
+ ),
+
+ TP_printk("%s, maxpack: %d, con: %02x, dma_ep_sel: %08x, dma_ep_sts_en: %08x"
+ " dma_ep_cfg %08x",
+ __get_str(ep_name), __entry->maxpack_reg, __entry->con_reg,
+ __entry->ep_sel_reg, __entry->ep_sts_en_reg,
+ __entry->ep_cfg_reg
+ )
+);
+
+DEFINE_EVENT(cdns2_log_epx_reg_config, cdns2_epx_hw_cfg,
+ TP_PROTO(struct cdns2_device *pdev, struct cdns2_endpoint *pep),
+ TP_ARGS(pdev, pep)
+);
+
+#endif /* __LINUX_CDNS2_TRACE */
+
+/* This part must be outside header guard. */
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE cdns2-trace
+
+#include <trace/define_trace.h>
diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c
index 83fd1de14784..59188ea065e0 100644
--- a/drivers/usb/gadget/udc/core.c
+++ b/drivers/usb/gadget/udc/core.c
@@ -61,7 +61,7 @@ struct usb_udc {
struct mutex connect_lock;
};
-static struct class *udc_class;
+static const struct class udc_class;
static LIST_HEAD(udc_list);
/* Protects udc_list, udc->driver, driver->is_bound, and related calls */
@@ -1378,7 +1378,7 @@ int usb_add_gadget(struct usb_gadget *gadget)
device_initialize(&udc->dev);
udc->dev.release = usb_udc_release;
- udc->dev.class = udc_class;
+ udc->dev.class = &udc_class;
udc->dev.groups = usb_udc_attr_groups;
udc->dev.parent = gadget->dev.parent;
ret = dev_set_name(&udc->dev, "%s",
@@ -1856,6 +1856,11 @@ static int usb_udc_uevent(const struct device *dev, struct kobj_uevent_env *env)
return 0;
}
+static const struct class udc_class = {
+ .name = "udc",
+ .dev_uevent = usb_udc_uevent,
+};
+
static const struct bus_type gadget_bus_type = {
.name = "gadget",
.probe = gadget_bind_driver,
@@ -1867,18 +1872,13 @@ static int __init usb_udc_init(void)
{
int rc;
- udc_class = class_create("udc");
- if (IS_ERR(udc_class)) {
- pr_err("failed to create udc class --> %ld\n",
- PTR_ERR(udc_class));
- return PTR_ERR(udc_class);
- }
-
- udc_class->dev_uevent = usb_udc_uevent;
+ rc = class_register(&udc_class);
+ if (rc)
+ return rc;
rc = bus_register(&gadget_bus_type);
if (rc)
- class_destroy(udc_class);
+ class_unregister(&udc_class);
return rc;
}
subsys_initcall(usb_udc_init);
@@ -1886,7 +1886,7 @@ subsys_initcall(usb_udc_init);
static void __exit usb_udc_exit(void)
{
bus_unregister(&gadget_bus_type);
- class_destroy(udc_class);
+ class_unregister(&udc_class);
}
module_exit(usb_udc_exit);
diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c
index 899ac9f9c279..0953e1b5c030 100644
--- a/drivers/usb/gadget/udc/dummy_hcd.c
+++ b/drivers/usb/gadget/udc/dummy_hcd.c
@@ -1108,13 +1108,12 @@ err_udc:
return rc;
}
-static int dummy_udc_remove(struct platform_device *pdev)
+static void dummy_udc_remove(struct platform_device *pdev)
{
struct dummy *dum = platform_get_drvdata(pdev);
device_remove_file(&dum->gadget.dev, &dev_attr_function);
usb_del_gadget_udc(&dum->gadget);
- return 0;
}
static void dummy_udc_pm(struct dummy *dum, struct dummy_hcd *dum_hcd,
@@ -1150,7 +1149,7 @@ static int dummy_udc_resume(struct platform_device *pdev)
static struct platform_driver dummy_udc_driver = {
.probe = dummy_udc_probe,
- .remove = dummy_udc_remove,
+ .remove_new = dummy_udc_remove,
.suspend = dummy_udc_suspend,
.resume = dummy_udc_resume,
.driver = {
@@ -2701,7 +2700,7 @@ put_usb2_hcd:
return retval;
}
-static int dummy_hcd_remove(struct platform_device *pdev)
+static void dummy_hcd_remove(struct platform_device *pdev)
{
struct dummy *dum;
@@ -2717,8 +2716,6 @@ static int dummy_hcd_remove(struct platform_device *pdev)
dum->hs_hcd = NULL;
dum->ss_hcd = NULL;
-
- return 0;
}
static int dummy_hcd_suspend(struct platform_device *pdev, pm_message_t state)
@@ -2753,7 +2750,7 @@ static int dummy_hcd_resume(struct platform_device *pdev)
static struct platform_driver dummy_hcd_driver = {
.probe = dummy_hcd_probe,
- .remove = dummy_hcd_remove,
+ .remove_new = dummy_hcd_remove,
.suspend = dummy_hcd_suspend,
.resume = dummy_hcd_resume,
.driver = {
diff --git a/drivers/usb/gadget/udc/fsl_qe_udc.c b/drivers/usb/gadget/udc/fsl_qe_udc.c
index 3b1cc8fa30c8..9c5dc1c1a68e 100644
--- a/drivers/usb/gadget/udc/fsl_qe_udc.c
+++ b/drivers/usb/gadget/udc/fsl_qe_udc.c
@@ -2628,7 +2628,7 @@ static int qe_udc_resume(struct platform_device *dev)
}
#endif
-static int qe_udc_remove(struct platform_device *ofdev)
+static void qe_udc_remove(struct platform_device *ofdev)
{
struct qe_udc *udc = platform_get_drvdata(ofdev);
struct qe_ep *ep;
@@ -2679,8 +2679,6 @@ static int qe_udc_remove(struct platform_device *ofdev)
/* wait for release() of gadget.dev to free udc */
wait_for_completion(&done);
-
- return 0;
}
/*-------------------------------------------------------------------------*/
@@ -2708,7 +2706,7 @@ static struct platform_driver udc_driver = {
.of_match_table = qe_udc_match,
},
.probe = qe_udc_probe,
- .remove = qe_udc_remove,
+ .remove_new = qe_udc_remove,
#ifdef CONFIG_PM
.suspend = qe_udc_suspend,
.resume = qe_udc_resume,
diff --git a/drivers/usb/gadget/udc/fusb300_udc.c b/drivers/usb/gadget/udc/fusb300_udc.c
index 08ba9c8c1e67..bd03d475f927 100644
--- a/drivers/usb/gadget/udc/fusb300_udc.c
+++ b/drivers/usb/gadget/udc/fusb300_udc.c
@@ -1338,7 +1338,7 @@ static const struct usb_gadget_ops fusb300_gadget_ops = {
.udc_stop = fusb300_udc_stop,
};
-static int fusb300_remove(struct platform_device *pdev)
+static void fusb300_remove(struct platform_device *pdev)
{
struct fusb300 *fusb300 = platform_get_drvdata(pdev);
int i;
@@ -1352,8 +1352,6 @@ static int fusb300_remove(struct platform_device *pdev)
for (i = 0; i < FUSB300_MAX_NUM_EP; i++)
kfree(fusb300->ep[i]);
kfree(fusb300);
-
- return 0;
}
static int fusb300_probe(struct platform_device *pdev)
@@ -1508,7 +1506,7 @@ clean_up:
}
static struct platform_driver fusb300_driver = {
- .remove = fusb300_remove,
+ .remove_new = fusb300_remove,
.driver = {
.name = udc_name,
},
diff --git a/drivers/usb/gadget/udc/m66592-udc.c b/drivers/usb/gadget/udc/m66592-udc.c
index 06e21cee431b..e05f45a4b56b 100644
--- a/drivers/usb/gadget/udc/m66592-udc.c
+++ b/drivers/usb/gadget/udc/m66592-udc.c
@@ -1512,7 +1512,7 @@ static const struct usb_gadget_ops m66592_gadget_ops = {
.pullup = m66592_pullup,
};
-static int m66592_remove(struct platform_device *pdev)
+static void m66592_remove(struct platform_device *pdev)
{
struct m66592 *m66592 = platform_get_drvdata(pdev);
@@ -1527,7 +1527,6 @@ static int m66592_remove(struct platform_device *pdev)
clk_put(m66592->clk);
}
kfree(m66592);
- return 0;
}
static void nop_completion(struct usb_ep *ep, struct usb_request *r)
@@ -1688,7 +1687,7 @@ clean_up:
/*-------------------------------------------------------------------------*/
static struct platform_driver m66592_driver = {
- .remove = m66592_remove,
+ .remove_new = m66592_remove,
.driver = {
.name = udc_name,
},
diff --git a/drivers/usb/gadget/udc/mv_u3d_core.c b/drivers/usb/gadget/udc/mv_u3d_core.c
index 411b6179782c..3473048a85f5 100644
--- a/drivers/usb/gadget/udc/mv_u3d_core.c
+++ b/drivers/usb/gadget/udc/mv_u3d_core.c
@@ -1746,7 +1746,7 @@ static irqreturn_t mv_u3d_irq(int irq, void *dev)
return IRQ_HANDLED;
}
-static int mv_u3d_remove(struct platform_device *dev)
+static void mv_u3d_remove(struct platform_device *dev)
{
struct mv_u3d *u3d = platform_get_drvdata(dev);
@@ -1775,8 +1775,6 @@ static int mv_u3d_remove(struct platform_device *dev)
clk_put(u3d->clk);
kfree(u3d);
-
- return 0;
}
static int mv_u3d_probe(struct platform_device *dev)
@@ -2049,7 +2047,7 @@ static void mv_u3d_shutdown(struct platform_device *dev)
static struct platform_driver mv_u3d_driver = {
.probe = mv_u3d_probe,
- .remove = mv_u3d_remove,
+ .remove_new = mv_u3d_remove,
.shutdown = mv_u3d_shutdown,
.driver = {
.name = "mv-u3d",
diff --git a/drivers/usb/gadget/udc/mv_udc_core.c b/drivers/usb/gadget/udc/mv_udc_core.c
index 08474c08d874..79db74e2040b 100644
--- a/drivers/usb/gadget/udc/mv_udc_core.c
+++ b/drivers/usb/gadget/udc/mv_udc_core.c
@@ -2077,7 +2077,7 @@ static void gadget_release(struct device *_dev)
complete(udc->done);
}
-static int mv_udc_remove(struct platform_device *pdev)
+static void mv_udc_remove(struct platform_device *pdev)
{
struct mv_udc *udc;
@@ -2099,8 +2099,6 @@ static int mv_udc_remove(struct platform_device *pdev)
/* free dev, wait for the release() finished */
wait_for_completion(udc->done);
-
- return 0;
}
static int mv_udc_probe(struct platform_device *pdev)
@@ -2411,7 +2409,7 @@ static void mv_udc_shutdown(struct platform_device *pdev)
static struct platform_driver udc_driver = {
.probe = mv_udc_probe,
- .remove = mv_udc_remove,
+ .remove_new = mv_udc_remove,
.shutdown = mv_udc_shutdown,
.driver = {
.name = "mv-udc",
diff --git a/drivers/usb/gadget/udc/net2272.c b/drivers/usb/gadget/udc/net2272.c
index 538c1b9a2883..12e76bb62c20 100644
--- a/drivers/usb/gadget/udc/net2272.c
+++ b/drivers/usb/gadget/udc/net2272.c
@@ -2670,7 +2670,7 @@ net2272_plat_probe(struct platform_device *pdev)
return ret;
}
-static int
+static void
net2272_plat_remove(struct platform_device *pdev)
{
struct net2272 *dev = platform_get_drvdata(pdev);
@@ -2681,13 +2681,11 @@ net2272_plat_remove(struct platform_device *pdev)
resource_size(&pdev->resource[0]));
usb_put_gadget(&dev->gadget);
-
- return 0;
}
static struct platform_driver net2272_plat_driver = {
.probe = net2272_plat_probe,
- .remove = net2272_plat_remove,
+ .remove_new = net2272_plat_remove,
.driver = {
.name = driver_name,
},
diff --git a/drivers/usb/gadget/udc/omap_udc.c b/drivers/usb/gadget/udc/omap_udc.c
index 2d87c7cd5f7e..10c5d7f726a1 100644
--- a/drivers/usb/gadget/udc/omap_udc.c
+++ b/drivers/usb/gadget/udc/omap_udc.c
@@ -2927,7 +2927,7 @@ cleanup0:
return status;
}
-static int omap_udc_remove(struct platform_device *pdev)
+static void omap_udc_remove(struct platform_device *pdev)
{
DECLARE_COMPLETION_ONSTACK(done);
@@ -2939,8 +2939,6 @@ static int omap_udc_remove(struct platform_device *pdev)
release_mem_region(pdev->resource[0].start,
resource_size(&pdev->resource[0]));
-
- return 0;
}
/* suspend/resume/wakeup from sysfs (echo > power/state) or when the
@@ -2985,7 +2983,7 @@ static int omap_udc_resume(struct platform_device *dev)
static struct platform_driver udc_driver = {
.probe = omap_udc_probe,
- .remove = omap_udc_remove,
+ .remove_new = omap_udc_remove,
.suspend = omap_udc_suspend,
.resume = omap_udc_resume,
.driver = {
diff --git a/drivers/usb/gadget/udc/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c
index fdf9cd4506b0..c4e1d957f913 100644
--- a/drivers/usb/gadget/udc/pxa27x_udc.c
+++ b/drivers/usb/gadget/udc/pxa27x_udc.c
@@ -2445,7 +2445,7 @@ err:
* pxa_udc_remove - removes the udc device driver
* @_dev: platform device
*/
-static int pxa_udc_remove(struct platform_device *_dev)
+static void pxa_udc_remove(struct platform_device *_dev)
{
struct pxa_udc *udc = platform_get_drvdata(_dev);
@@ -2460,8 +2460,6 @@ static int pxa_udc_remove(struct platform_device *_dev)
udc->transceiver = NULL;
the_controller = NULL;
clk_unprepare(udc->clk);
-
- return 0;
}
static void pxa_udc_shutdown(struct platform_device *_dev)
@@ -2541,7 +2539,7 @@ static struct platform_driver udc_driver = {
.of_match_table = of_match_ptr(udc_pxa_dt_ids),
},
.probe = pxa_udc_probe,
- .remove = pxa_udc_remove,
+ .remove_new = pxa_udc_remove,
.shutdown = pxa_udc_shutdown,
#ifdef CONFIG_PM
.suspend = pxa_udc_suspend,
diff --git a/drivers/usb/gadget/udc/r8a66597-udc.c b/drivers/usb/gadget/udc/r8a66597-udc.c
index 38e4d6b505a0..51b665f15c8e 100644
--- a/drivers/usb/gadget/udc/r8a66597-udc.c
+++ b/drivers/usb/gadget/udc/r8a66597-udc.c
@@ -1805,7 +1805,7 @@ static const struct usb_gadget_ops r8a66597_gadget_ops = {
.set_selfpowered = r8a66597_set_selfpowered,
};
-static int r8a66597_remove(struct platform_device *pdev)
+static void r8a66597_remove(struct platform_device *pdev)
{
struct r8a66597 *r8a66597 = platform_get_drvdata(pdev);
@@ -1816,8 +1816,6 @@ static int r8a66597_remove(struct platform_device *pdev)
if (r8a66597->pdata->on_chip) {
clk_disable_unprepare(r8a66597->clk);
}
-
- return 0;
}
static void nop_completion(struct usb_ep *ep, struct usb_request *r)
@@ -1966,7 +1964,7 @@ clean_up2:
/*-------------------------------------------------------------------------*/
static struct platform_driver r8a66597_driver = {
- .remove = r8a66597_remove,
+ .remove_new = r8a66597_remove,
.driver = {
.name = udc_name,
},
diff --git a/drivers/usb/gadget/udc/renesas_usb3.c b/drivers/usb/gadget/udc/renesas_usb3.c
index eb008e873361..59bb25de2015 100644
--- a/drivers/usb/gadget/udc/renesas_usb3.c
+++ b/drivers/usb/gadget/udc/renesas_usb3.c
@@ -2653,7 +2653,7 @@ static void renesas_usb3_debugfs_init(struct renesas_usb3 *usb3,
}
/*------- platform_driver ------------------------------------------------*/
-static int renesas_usb3_remove(struct platform_device *pdev)
+static void renesas_usb3_remove(struct platform_device *pdev)
{
struct renesas_usb3 *usb3 = platform_get_drvdata(pdev);
@@ -2669,8 +2669,6 @@ static int renesas_usb3_remove(struct platform_device *pdev)
__renesas_usb3_ep_free_request(usb3->ep0_req);
pm_runtime_disable(&pdev->dev);
-
- return 0;
}
static int renesas_usb3_init_ep(struct renesas_usb3 *usb3, struct device *dev,
@@ -3015,7 +3013,7 @@ static SIMPLE_DEV_PM_OPS(renesas_usb3_pm_ops, renesas_usb3_suspend,
static struct platform_driver renesas_usb3_driver = {
.probe = renesas_usb3_probe,
- .remove = renesas_usb3_remove,
+ .remove_new = renesas_usb3_remove,
.driver = {
.name = udc_name,
.pm = &renesas_usb3_pm_ops,
diff --git a/drivers/usb/gadget/udc/renesas_usbf.c b/drivers/usb/gadget/udc/renesas_usbf.c
index 84ac9fe4ce7f..6cd0af83e91e 100644
--- a/drivers/usb/gadget/udc/renesas_usbf.c
+++ b/drivers/usb/gadget/udc/renesas_usbf.c
@@ -3361,15 +3361,13 @@ static int usbf_probe(struct platform_device *pdev)
return 0;
}
-static int usbf_remove(struct platform_device *pdev)
+static void usbf_remove(struct platform_device *pdev)
{
struct usbf_udc *udc = platform_get_drvdata(pdev);
usb_del_gadget_udc(&udc->gadget);
pm_runtime_put(&pdev->dev);
-
- return 0;
}
static const struct of_device_id usbf_match[] = {
@@ -3385,7 +3383,7 @@ static struct platform_driver udc_driver = {
.of_match_table = usbf_match,
},
.probe = usbf_probe,
- .remove = usbf_remove,
+ .remove_new = usbf_remove,
};
module_platform_driver(udc_driver);
diff --git a/drivers/usb/gadget/udc/rzv2m_usb3drd.c b/drivers/usb/gadget/udc/rzv2m_usb3drd.c
index 589c7252e014..36f4ff00d22f 100644
--- a/drivers/usb/gadget/udc/rzv2m_usb3drd.c
+++ b/drivers/usb/gadget/udc/rzv2m_usb3drd.c
@@ -58,7 +58,7 @@ void rzv2m_usb3drd_reset(struct device *dev, bool host)
}
EXPORT_SYMBOL_GPL(rzv2m_usb3drd_reset);
-static int rzv2m_usb3drd_remove(struct platform_device *pdev)
+static void rzv2m_usb3drd_remove(struct platform_device *pdev)
{
struct rzv2m_usb3drd *usb3 = platform_get_drvdata(pdev);
@@ -66,8 +66,6 @@ static int rzv2m_usb3drd_remove(struct platform_device *pdev)
pm_runtime_put(usb3->dev);
pm_runtime_disable(&pdev->dev);
reset_control_assert(usb3->drd_rstc);
-
- return 0;
}
static int rzv2m_usb3drd_probe(struct platform_device *pdev)
@@ -129,7 +127,7 @@ static struct platform_driver rzv2m_usb3drd_driver = {
.of_match_table = rzv2m_usb3drd_of_match,
},
.probe = rzv2m_usb3drd_probe,
- .remove = rzv2m_usb3drd_remove,
+ .remove_new = rzv2m_usb3drd_remove,
};
module_platform_driver(rzv2m_usb3drd_driver);
diff --git a/drivers/usb/gadget/udc/snps_udc_plat.c b/drivers/usb/gadget/udc/snps_udc_plat.c
index 0d3e705655b9..0ed685db149d 100644
--- a/drivers/usb/gadget/udc/snps_udc_plat.c
+++ b/drivers/usb/gadget/udc/snps_udc_plat.c
@@ -225,7 +225,7 @@ exit_phy:
return ret;
}
-static int udc_plat_remove(struct platform_device *pdev)
+static void udc_plat_remove(struct platform_device *pdev)
{
struct udc *dev;
@@ -234,7 +234,7 @@ static int udc_plat_remove(struct platform_device *pdev)
usb_del_gadget_udc(&dev->gadget);
/* gadget driver must not be registered */
if (WARN_ON(dev->driver))
- return 0;
+ return;
/* dma pool cleanup */
free_dma_pools(dev);
@@ -248,8 +248,6 @@ static int udc_plat_remove(struct platform_device *pdev)
extcon_unregister_notifier(dev->edev, EXTCON_USB, &dev->nb);
dev_info(&pdev->dev, "Synopsys UDC platform driver removed\n");
-
- return 0;
}
#ifdef CONFIG_PM_SLEEP
@@ -315,7 +313,7 @@ MODULE_DEVICE_TABLE(of, of_udc_match);
static struct platform_driver udc_plat_driver = {
.probe = udc_plat_probe,
- .remove = udc_plat_remove,
+ .remove_new = udc_plat_remove,
.driver = {
.name = "snps-udc-plat",
.of_match_table = of_match_ptr(of_udc_match),
diff --git a/drivers/usb/gadget/udc/tegra-xudc.c b/drivers/usb/gadget/udc/tegra-xudc.c
index 34e9c1df54c7..83eaa65ddde3 100644
--- a/drivers/usb/gadget/udc/tegra-xudc.c
+++ b/drivers/usb/gadget/udc/tegra-xudc.c
@@ -3906,7 +3906,7 @@ put_padctl:
return err;
}
-static int tegra_xudc_remove(struct platform_device *pdev)
+static void tegra_xudc_remove(struct platform_device *pdev)
{
struct tegra_xudc *xudc = platform_get_drvdata(pdev);
unsigned int i;
@@ -3936,8 +3936,6 @@ static int tegra_xudc_remove(struct platform_device *pdev)
pm_runtime_put(xudc->dev);
tegra_xusb_padctl_put(xudc->padctl);
-
- return 0;
}
static int __maybe_unused tegra_xudc_powergate(struct tegra_xudc *xudc)
@@ -4063,7 +4061,7 @@ static const struct dev_pm_ops tegra_xudc_pm_ops = {
static struct platform_driver tegra_xudc_driver = {
.probe = tegra_xudc_probe,
- .remove = tegra_xudc_remove,
+ .remove_new = tegra_xudc_remove,
.driver = {
.name = "tegra-xudc",
.pm = &tegra_xudc_pm_ops,
diff --git a/drivers/usb/gadget/udc/udc-xilinx.c b/drivers/usb/gadget/udc/udc-xilinx.c
index 4827e3cd3834..a4a7b90a97e7 100644
--- a/drivers/usb/gadget/udc/udc-xilinx.c
+++ b/drivers/usb/gadget/udc/udc-xilinx.c
@@ -192,7 +192,7 @@ struct xusb_udc {
bool dma_enabled;
struct clk *clk;
- unsigned int (*read_fn)(void __iomem *);
+ unsigned int (*read_fn)(void __iomem *reg);
void (*write_fn)(void __iomem *, u32, u32);
};
@@ -2178,14 +2178,12 @@ fail:
*
* Return: 0 always
*/
-static int xudc_remove(struct platform_device *pdev)
+static void xudc_remove(struct platform_device *pdev)
{
struct xusb_udc *udc = platform_get_drvdata(pdev);
usb_del_gadget_udc(&udc->gadget);
clk_disable_unprepare(udc->clk);
-
- return 0;
}
#ifdef CONFIG_PM_SLEEP
@@ -2257,7 +2255,7 @@ static struct platform_driver xudc_driver = {
.pm = &xudc_pm_ops,
},
.probe = xudc_probe,
- .remove = xudc_remove,
+ .remove_new = xudc_remove,
};
module_platform_driver(xudc_driver);
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index c170672f847e..4448d0ab06f0 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -376,7 +376,7 @@ config USB_ISP116X_HCD
config USB_ISP1362_HCD
tristate "ISP1362 HCD support"
- depends on HAS_IOMEM
+ depends on HAS_IOPORT
depends on COMPILE_TEST # nothing uses this
help
Supports the Philips ISP1362 chip as a host controller
@@ -578,7 +578,7 @@ endif # USB_OHCI_HCD
config USB_UHCI_HCD
tristate "UHCI HCD (most Intel and VIA) support"
- depends on USB_PCI || USB_UHCI_SUPPORT_NON_PCI_HC
+ depends on (USB_PCI && HAS_IOPORT) || USB_UHCI_SUPPORT_NON_PCI_HC
help
The Universal Host Controller Interface is a standard by Intel for
accessing the USB hardware in the PC (which is also called the USB
diff --git a/drivers/usb/host/ehci-atmel.c b/drivers/usb/host/ehci-atmel.c
index 8b775e7bab06..61808c51e702 100644
--- a/drivers/usb/host/ehci-atmel.c
+++ b/drivers/usb/host/ehci-atmel.c
@@ -173,7 +173,7 @@ fail_create_hcd:
return retval;
}
-static int ehci_atmel_drv_remove(struct platform_device *pdev)
+static void ehci_atmel_drv_remove(struct platform_device *pdev)
{
struct usb_hcd *hcd = platform_get_drvdata(pdev);
@@ -181,8 +181,6 @@ static int ehci_atmel_drv_remove(struct platform_device *pdev)
usb_put_hcd(hcd);
atmel_stop_ehci(pdev);
-
- return 0;
}
static int __maybe_unused ehci_atmel_drv_suspend(struct device *dev)
@@ -223,7 +221,7 @@ static SIMPLE_DEV_PM_OPS(ehci_atmel_pm_ops, ehci_atmel_drv_suspend,
static struct platform_driver ehci_atmel_driver = {
.probe = ehci_atmel_drv_probe,
- .remove = ehci_atmel_drv_remove,
+ .remove_new = ehci_atmel_drv_remove,
.shutdown = usb_hcd_platform_shutdown,
.driver = {
.name = "atmel-ehci",
diff --git a/drivers/usb/host/ehci-brcm.c b/drivers/usb/host/ehci-brcm.c
index 6a0f64c9e5e8..0362a082abb4 100644
--- a/drivers/usb/host/ehci-brcm.c
+++ b/drivers/usb/host/ehci-brcm.c
@@ -188,7 +188,7 @@ err_hcd:
return err;
}
-static int ehci_brcm_remove(struct platform_device *dev)
+static void ehci_brcm_remove(struct platform_device *dev)
{
struct usb_hcd *hcd = platform_get_drvdata(dev);
struct brcm_priv *priv = hcd_to_ehci_priv(hcd);
@@ -196,7 +196,6 @@ static int ehci_brcm_remove(struct platform_device *dev)
usb_remove_hcd(hcd);
clk_disable_unprepare(priv->clk);
usb_put_hcd(hcd);
- return 0;
}
static int __maybe_unused ehci_brcm_suspend(struct device *dev)
@@ -250,7 +249,7 @@ static const struct of_device_id brcm_ehci_of_match[] = {
static struct platform_driver ehci_brcm_driver = {
.probe = ehci_brcm_probe,
- .remove = ehci_brcm_remove,
+ .remove_new = ehci_brcm_remove,
.shutdown = usb_hcd_platform_shutdown,
.driver = {
.name = "ehci-brcm",
diff --git a/drivers/usb/host/ehci-exynos.c b/drivers/usb/host/ehci-exynos.c
index 47c9f06c3d84..20f8c0ec6810 100644
--- a/drivers/usb/host/ehci-exynos.c
+++ b/drivers/usb/host/ehci-exynos.c
@@ -230,7 +230,7 @@ fail_clk:
return err;
}
-static int exynos_ehci_remove(struct platform_device *pdev)
+static void exynos_ehci_remove(struct platform_device *pdev)
{
struct usb_hcd *hcd = platform_get_drvdata(pdev);
struct exynos_ehci_hcd *exynos_ehci = to_exynos_ehci(hcd);
@@ -244,8 +244,6 @@ static int exynos_ehci_remove(struct platform_device *pdev)
clk_disable_unprepare(exynos_ehci->clk);
usb_put_hcd(hcd);
-
- return 0;
}
#ifdef CONFIG_PM
@@ -311,7 +309,7 @@ MODULE_DEVICE_TABLE(of, exynos_ehci_match);
static struct platform_driver exynos_ehci_driver = {
.probe = exynos_ehci_probe,
- .remove = exynos_ehci_remove,
+ .remove_new = exynos_ehci_remove,
.shutdown = usb_hcd_platform_shutdown,
.driver = {
.name = "exynos-ehci",
diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c
index d74fa5ba845b..81d60a695510 100644
--- a/drivers/usb/host/ehci-fsl.c
+++ b/drivers/usb/host/ehci-fsl.c
@@ -684,7 +684,7 @@ static const struct ehci_driver_overrides ehci_fsl_overrides __initconst = {
*
* Reverses the effect of usb_hcd_fsl_probe().
*/
-static int fsl_ehci_drv_remove(struct platform_device *pdev)
+static void fsl_ehci_drv_remove(struct platform_device *pdev)
{
struct fsl_usb2_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct usb_hcd *hcd = platform_get_drvdata(pdev);
@@ -703,13 +703,11 @@ static int fsl_ehci_drv_remove(struct platform_device *pdev)
if (pdata->exit)
pdata->exit(pdev);
usb_put_hcd(hcd);
-
- return 0;
}
static struct platform_driver ehci_fsl_driver = {
.probe = fsl_ehci_drv_probe,
- .remove = fsl_ehci_drv_remove,
+ .remove_new = fsl_ehci_drv_remove,
.shutdown = usb_hcd_platform_shutdown,
.driver = {
.name = DRV_NAME,
diff --git a/drivers/usb/host/ehci-grlib.c b/drivers/usb/host/ehci-grlib.c
index 0717f2ccf49d..14150e4d3382 100644
--- a/drivers/usb/host/ehci-grlib.c
+++ b/drivers/usb/host/ehci-grlib.c
@@ -140,7 +140,7 @@ err_irq:
}
-static int ehci_hcd_grlib_remove(struct platform_device *op)
+static void ehci_hcd_grlib_remove(struct platform_device *op)
{
struct usb_hcd *hcd = platform_get_drvdata(op);
@@ -151,8 +151,6 @@ static int ehci_hcd_grlib_remove(struct platform_device *op)
irq_dispose_mapping(hcd->irq);
usb_put_hcd(hcd);
-
- return 0;
}
@@ -170,7 +168,7 @@ MODULE_DEVICE_TABLE(of, ehci_hcd_grlib_of_match);
static struct platform_driver ehci_grlib_driver = {
.probe = ehci_hcd_grlib_probe,
- .remove = ehci_hcd_grlib_remove,
+ .remove_new = ehci_hcd_grlib_remove,
.shutdown = usb_hcd_platform_shutdown,
.driver = {
.name = "grlib-ehci",
diff --git a/drivers/usb/host/ehci-mv.c b/drivers/usb/host/ehci-mv.c
index fa46d217dd10..9320cf0e5bc7 100644
--- a/drivers/usb/host/ehci-mv.c
+++ b/drivers/usb/host/ehci-mv.c
@@ -235,7 +235,7 @@ err_put_hcd:
return retval;
}
-static int mv_ehci_remove(struct platform_device *pdev)
+static void mv_ehci_remove(struct platform_device *pdev)
{
struct usb_hcd *hcd = platform_get_drvdata(pdev);
struct ehci_hcd_mv *ehci_mv = hcd_to_ehci_hcd_mv(hcd);
@@ -254,8 +254,6 @@ static int mv_ehci_remove(struct platform_device *pdev)
}
usb_put_hcd(hcd);
-
- return 0;
}
static const struct platform_device_id ehci_id_table[] = {
@@ -282,7 +280,7 @@ static const struct of_device_id ehci_mv_dt_ids[] = {
static struct platform_driver ehci_mv_driver = {
.probe = mv_ehci_probe,
- .remove = mv_ehci_remove,
+ .remove_new = mv_ehci_remove,
.shutdown = mv_ehci_shutdown,
.driver = {
.name = "mv-ehci",
diff --git a/drivers/usb/host/ehci-npcm7xx.c b/drivers/usb/host/ehci-npcm7xx.c
index 63af1a827fcb..ad79ce63afcf 100644
--- a/drivers/usb/host/ehci-npcm7xx.c
+++ b/drivers/usb/host/ehci-npcm7xx.c
@@ -106,15 +106,13 @@ fail:
return retval;
}
-static int npcm7xx_ehci_hcd_drv_remove(struct platform_device *pdev)
+static void npcm7xx_ehci_hcd_drv_remove(struct platform_device *pdev)
{
struct usb_hcd *hcd = platform_get_drvdata(pdev);
usb_remove_hcd(hcd);
usb_put_hcd(hcd);
-
- return 0;
}
static const struct of_device_id npcm7xx_ehci_id_table[] = {
@@ -125,7 +123,7 @@ MODULE_DEVICE_TABLE(of, npcm7xx_ehci_id_table);
static struct platform_driver npcm7xx_ehci_hcd_driver = {
.probe = npcm7xx_ehci_hcd_drv_probe,
- .remove = npcm7xx_ehci_hcd_drv_remove,
+ .remove_new = npcm7xx_ehci_hcd_drv_remove,
.shutdown = usb_hcd_platform_shutdown,
.driver = {
.name = "npcm7xx-ehci",
diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c
index 7dd984722a7f..cb6509a735ac 100644
--- a/drivers/usb/host/ehci-omap.c
+++ b/drivers/usb/host/ehci-omap.c
@@ -237,7 +237,7 @@ err_phy:
* the HCD's stop() method. It is always called from a thread
* context, normally "rmmod", "apmd", or something similar.
*/
-static int ehci_hcd_omap_remove(struct platform_device *pdev)
+static void ehci_hcd_omap_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct usb_hcd *hcd = dev_get_drvdata(dev);
@@ -254,8 +254,6 @@ static int ehci_hcd_omap_remove(struct platform_device *pdev)
usb_put_hcd(hcd);
pm_runtime_put_sync(dev);
pm_runtime_disable(dev);
-
- return 0;
}
static const struct of_device_id omap_ehci_dt_ids[] = {
@@ -267,7 +265,7 @@ MODULE_DEVICE_TABLE(of, omap_ehci_dt_ids);
static struct platform_driver ehci_hcd_omap_driver = {
.probe = ehci_hcd_omap_probe,
- .remove = ehci_hcd_omap_remove,
+ .remove_new = ehci_hcd_omap_remove,
.shutdown = usb_hcd_platform_shutdown,
/*.suspend = ehci_hcd_omap_suspend, */
/*.resume = ehci_hcd_omap_resume, */
diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c
index a3454a3ea4e0..2cfb27dc943a 100644
--- a/drivers/usb/host/ehci-orion.c
+++ b/drivers/usb/host/ehci-orion.c
@@ -321,7 +321,7 @@ err:
return err;
}
-static int ehci_orion_drv_remove(struct platform_device *pdev)
+static void ehci_orion_drv_remove(struct platform_device *pdev)
{
struct usb_hcd *hcd = platform_get_drvdata(pdev);
struct orion_ehci_hcd *priv = hcd_to_orion_priv(hcd);
@@ -332,8 +332,6 @@ static int ehci_orion_drv_remove(struct platform_device *pdev)
clk_disable_unprepare(priv->clk);
usb_put_hcd(hcd);
-
- return 0;
}
static const struct of_device_id ehci_orion_dt_ids[] = {
@@ -345,7 +343,7 @@ MODULE_DEVICE_TABLE(of, ehci_orion_dt_ids);
static struct platform_driver ehci_orion_driver = {
.probe = ehci_orion_drv_probe,
- .remove = ehci_orion_drv_remove,
+ .remove_new = ehci_orion_drv_remove,
.shutdown = usb_hcd_platform_shutdown,
.driver = {
.name = "orion-ehci",
diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c
index 4b148fe5e43b..889dc4426271 100644
--- a/drivers/usb/host/ehci-pci.c
+++ b/drivers/usb/host/ehci-pci.c
@@ -354,10 +354,11 @@ done:
* Also they depend on separate root hub suspend/resume.
*/
-static int ehci_pci_resume(struct usb_hcd *hcd, bool hibernated)
+static int ehci_pci_resume(struct usb_hcd *hcd, pm_message_t msg)
{
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
+ bool hibernated = (msg.event == PM_EVENT_RESTORE);
if (ehci_resume(hcd, hibernated) != 0)
(void) ehci_pci_reinit(ehci, pdev);
diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c
index fe497c876d76..83bf56c9424f 100644
--- a/drivers/usb/host/ehci-platform.c
+++ b/drivers/usb/host/ehci-platform.c
@@ -400,7 +400,7 @@ err_put_clks:
return err;
}
-static int ehci_platform_remove(struct platform_device *dev)
+static void ehci_platform_remove(struct platform_device *dev)
{
struct usb_hcd *hcd = platform_get_drvdata(dev);
struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev);
@@ -424,8 +424,6 @@ static int ehci_platform_remove(struct platform_device *dev)
if (pdata == &ehci_platform_defaults)
dev->dev.platform_data = NULL;
-
- return 0;
}
static int __maybe_unused ehci_platform_suspend(struct device *dev)
@@ -511,7 +509,7 @@ static SIMPLE_DEV_PM_OPS(ehci_platform_pm_ops, ehci_platform_suspend,
static struct platform_driver ehci_platform_driver = {
.id_table = ehci_platform_table,
.probe = ehci_platform_probe,
- .remove = ehci_platform_remove,
+ .remove_new = ehci_platform_remove,
.shutdown = usb_hcd_platform_shutdown,
.driver = {
.name = "ehci-platform",
diff --git a/drivers/usb/host/ehci-ppc-of.c b/drivers/usb/host/ehci-ppc-of.c
index b3aa464e9d2c..7fd83e806ae4 100644
--- a/drivers/usb/host/ehci-ppc-of.c
+++ b/drivers/usb/host/ehci-ppc-of.c
@@ -184,7 +184,7 @@ err_irq:
}
-static int ehci_hcd_ppc_of_remove(struct platform_device *op)
+static void ehci_hcd_ppc_of_remove(struct platform_device *op)
{
struct usb_hcd *hcd = platform_get_drvdata(op);
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
@@ -216,8 +216,6 @@ static int ehci_hcd_ppc_of_remove(struct platform_device *op)
}
}
usb_put_hcd(hcd);
-
- return 0;
}
@@ -232,7 +230,7 @@ MODULE_DEVICE_TABLE(of, ehci_hcd_ppc_of_match);
static struct platform_driver ehci_hcd_ppc_of_driver = {
.probe = ehci_hcd_ppc_of_probe,
- .remove = ehci_hcd_ppc_of_remove,
+ .remove_new = ehci_hcd_ppc_of_remove,
.shutdown = usb_hcd_platform_shutdown,
.driver = {
.name = "ppc-of-ehci",
diff --git a/drivers/usb/host/ehci-sh.c b/drivers/usb/host/ehci-sh.c
index c25c51d26f26..0520e762801d 100644
--- a/drivers/usb/host/ehci-sh.c
+++ b/drivers/usb/host/ehci-sh.c
@@ -147,7 +147,7 @@ fail_create_hcd:
return ret;
}
-static int ehci_hcd_sh_remove(struct platform_device *pdev)
+static void ehci_hcd_sh_remove(struct platform_device *pdev)
{
struct ehci_sh_priv *priv = platform_get_drvdata(pdev);
struct usb_hcd *hcd = priv->hcd;
@@ -157,8 +157,6 @@ static int ehci_hcd_sh_remove(struct platform_device *pdev)
clk_disable(priv->fclk);
clk_disable(priv->iclk);
-
- return 0;
}
static void ehci_hcd_sh_shutdown(struct platform_device *pdev)
@@ -172,7 +170,7 @@ static void ehci_hcd_sh_shutdown(struct platform_device *pdev)
static struct platform_driver ehci_hcd_sh_driver = {
.probe = ehci_hcd_sh_probe,
- .remove = ehci_hcd_sh_remove,
+ .remove_new = ehci_hcd_sh_remove,
.shutdown = ehci_hcd_sh_shutdown,
.driver = {
.name = "sh_ehci",
diff --git a/drivers/usb/host/ehci-spear.c b/drivers/usb/host/ehci-spear.c
index c4ddd1022f60..1407703649be 100644
--- a/drivers/usb/host/ehci-spear.c
+++ b/drivers/usb/host/ehci-spear.c
@@ -124,7 +124,7 @@ fail:
return retval ;
}
-static int spear_ehci_hcd_drv_remove(struct platform_device *pdev)
+static void spear_ehci_hcd_drv_remove(struct platform_device *pdev)
{
struct usb_hcd *hcd = platform_get_drvdata(pdev);
struct spear_ehci *sehci = to_spear_ehci(hcd);
@@ -134,8 +134,6 @@ static int spear_ehci_hcd_drv_remove(struct platform_device *pdev)
if (sehci->clk)
clk_disable_unprepare(sehci->clk);
usb_put_hcd(hcd);
-
- return 0;
}
static const struct of_device_id spear_ehci_id_table[] = {
@@ -146,7 +144,7 @@ MODULE_DEVICE_TABLE(of, spear_ehci_id_table);
static struct platform_driver spear_ehci_hcd_driver = {
.probe = spear_ehci_hcd_drv_probe,
- .remove = spear_ehci_hcd_drv_remove,
+ .remove_new = spear_ehci_hcd_drv_remove,
.shutdown = usb_hcd_platform_shutdown,
.driver = {
.name = "spear-ehci",
diff --git a/drivers/usb/host/ehci-st.c b/drivers/usb/host/ehci-st.c
index f731dc98c533..ee0976b815b4 100644
--- a/drivers/usb/host/ehci-st.c
+++ b/drivers/usb/host/ehci-st.c
@@ -252,7 +252,7 @@ err_put_hcd:
return err;
}
-static int st_ehci_platform_remove(struct platform_device *dev)
+static void st_ehci_platform_remove(struct platform_device *dev)
{
struct usb_hcd *hcd = platform_get_drvdata(dev);
struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev);
@@ -271,8 +271,6 @@ static int st_ehci_platform_remove(struct platform_device *dev)
if (pdata == &ehci_platform_defaults)
dev->dev.platform_data = NULL;
-
- return 0;
}
#ifdef CONFIG_PM_SLEEP
@@ -328,7 +326,7 @@ MODULE_DEVICE_TABLE(of, st_ehci_ids);
static struct platform_driver ehci_platform_driver = {
.probe = st_ehci_platform_probe,
- .remove = st_ehci_platform_remove,
+ .remove_new = st_ehci_platform_remove,
.shutdown = usb_hcd_platform_shutdown,
.driver = {
.name = "st-ehci",
diff --git a/drivers/usb/host/ehci-xilinx-of.c b/drivers/usb/host/ehci-xilinx-of.c
index 3d7893747835..a2112c28f631 100644
--- a/drivers/usb/host/ehci-xilinx-of.c
+++ b/drivers/usb/host/ehci-xilinx-of.c
@@ -201,7 +201,7 @@ err_irq:
*
* Return: Always return 0
*/
-static int ehci_hcd_xilinx_of_remove(struct platform_device *op)
+static void ehci_hcd_xilinx_of_remove(struct platform_device *op)
{
struct usb_hcd *hcd = platform_get_drvdata(op);
@@ -210,8 +210,6 @@ static int ehci_hcd_xilinx_of_remove(struct platform_device *op)
usb_remove_hcd(hcd);
usb_put_hcd(hcd);
-
- return 0;
}
static const struct of_device_id ehci_hcd_xilinx_of_match[] = {
@@ -222,7 +220,7 @@ MODULE_DEVICE_TABLE(of, ehci_hcd_xilinx_of_match);
static struct platform_driver ehci_hcd_xilinx_of_driver = {
.probe = ehci_hcd_xilinx_of_probe,
- .remove = ehci_hcd_xilinx_of_remove,
+ .remove_new = ehci_hcd_xilinx_of_remove,
.shutdown = usb_hcd_platform_shutdown,
.driver = {
.name = "xilinx-of-ehci",
diff --git a/drivers/usb/host/fhci-hcd.c b/drivers/usb/host/fhci-hcd.c
index 92794ffc25c8..66a045e01dad 100644
--- a/drivers/usb/host/fhci-hcd.c
+++ b/drivers/usb/host/fhci-hcd.c
@@ -757,7 +757,7 @@ err_regs:
return ret;
}
-static int fhci_remove(struct device *dev)
+static void fhci_remove(struct device *dev)
{
struct usb_hcd *hcd = dev_get_drvdata(dev);
struct fhci_hcd *fhci = hcd_to_fhci(hcd);
@@ -771,12 +771,11 @@ static int fhci_remove(struct device *dev)
qe_pin_free(fhci->pins[j]);
fhci_dfs_destroy(fhci);
usb_put_hcd(hcd);
- return 0;
}
-static int of_fhci_remove(struct platform_device *ofdev)
+static void of_fhci_remove(struct platform_device *ofdev)
{
- return fhci_remove(&ofdev->dev);
+ fhci_remove(&ofdev->dev);
}
static const struct of_device_id of_fhci_match[] = {
@@ -791,7 +790,7 @@ static struct platform_driver of_fhci_driver = {
.of_match_table = of_fhci_match,
},
.probe = of_fhci_probe,
- .remove = of_fhci_remove,
+ .remove_new = of_fhci_remove,
};
module_platform_driver(of_fhci_driver);
diff --git a/drivers/usb/host/fsl-mph-dr-of.c b/drivers/usb/host/fsl-mph-dr-of.c
index 9db909d12354..a9877f2569f4 100644
--- a/drivers/usb/host/fsl-mph-dr-of.c
+++ b/drivers/usb/host/fsl-mph-dr-of.c
@@ -265,10 +265,9 @@ static int __unregister_subdev(struct device *dev, void *d)
return 0;
}
-static int fsl_usb2_mph_dr_of_remove(struct platform_device *ofdev)
+static void fsl_usb2_mph_dr_of_remove(struct platform_device *ofdev)
{
device_for_each_child(&ofdev->dev, NULL, __unregister_subdev);
- return 0;
}
#ifdef CONFIG_PPC_MPC512x
@@ -362,7 +361,7 @@ static struct platform_driver fsl_usb2_mph_dr_driver = {
.of_match_table = fsl_usb2_mph_dr_of_match,
},
.probe = fsl_usb2_mph_dr_of_probe,
- .remove = fsl_usb2_mph_dr_of_remove,
+ .remove_new = fsl_usb2_mph_dr_of_remove,
};
module_platform_driver(fsl_usb2_mph_dr_driver);
diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c
index 49ae01487af4..a82d8926e922 100644
--- a/drivers/usb/host/isp116x-hcd.c
+++ b/drivers/usb/host/isp116x-hcd.c
@@ -1526,14 +1526,14 @@ static const struct hc_driver isp116x_hc_driver = {
/*----------------------------------------------------------------*/
-static int isp116x_remove(struct platform_device *pdev)
+static void isp116x_remove(struct platform_device *pdev)
{
struct usb_hcd *hcd = platform_get_drvdata(pdev);
struct isp116x *isp116x;
struct resource *res;
if (!hcd)
- return 0;
+ return;
isp116x = hcd_to_isp116x(hcd);
remove_debug_file(isp116x);
usb_remove_hcd(hcd);
@@ -1548,7 +1548,6 @@ static int isp116x_remove(struct platform_device *pdev)
release_mem_region(res->start, 2);
usb_put_hcd(hcd);
- return 0;
}
static int isp116x_probe(struct platform_device *pdev)
@@ -1685,7 +1684,7 @@ MODULE_ALIAS("platform:isp116x-hcd");
static struct platform_driver isp116x_driver = {
.probe = isp116x_probe,
- .remove = isp116x_remove,
+ .remove_new = isp116x_remove,
.suspend = isp116x_suspend,
.resume = isp116x_resume,
.driver = {
diff --git a/drivers/usb/host/isp1362-hcd.c b/drivers/usb/host/isp1362-hcd.c
index b0da143ef4be..606f0a64f3b7 100644
--- a/drivers/usb/host/isp1362-hcd.c
+++ b/drivers/usb/host/isp1362-hcd.c
@@ -2606,7 +2606,7 @@ static const struct hc_driver isp1362_hc_driver = {
/*-------------------------------------------------------------------------*/
-static int isp1362_remove(struct platform_device *pdev)
+static void isp1362_remove(struct platform_device *pdev)
{
struct usb_hcd *hcd = platform_get_drvdata(pdev);
struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
@@ -2617,8 +2617,6 @@ static int isp1362_remove(struct platform_device *pdev)
DBG(0, "%s: put_hcd\n", __func__);
usb_put_hcd(hcd);
DBG(0, "%s: Done\n", __func__);
-
- return 0;
}
static int isp1362_probe(struct platform_device *pdev)
@@ -2760,7 +2758,7 @@ static int isp1362_resume(struct platform_device *pdev)
static struct platform_driver isp1362_driver = {
.probe = isp1362_probe,
- .remove = isp1362_remove,
+ .remove_new = isp1362_remove,
.suspend = isp1362_suspend,
.resume = isp1362_resume,
diff --git a/drivers/usb/host/octeon-hcd.c b/drivers/usb/host/octeon-hcd.c
index a1cd81d4a114..19d5777f5db2 100644
--- a/drivers/usb/host/octeon-hcd.c
+++ b/drivers/usb/host/octeon-hcd.c
@@ -3680,7 +3680,7 @@ static int octeon_usb_probe(struct platform_device *pdev)
return 0;
}
-static int octeon_usb_remove(struct platform_device *pdev)
+static void octeon_usb_remove(struct platform_device *pdev)
{
int status;
struct device *dev = &pdev->dev;
@@ -3696,8 +3696,6 @@ static int octeon_usb_remove(struct platform_device *pdev)
dev_dbg(dev, "USB shutdown failed with %d\n", status);
usb_put_hcd(hcd);
-
- return 0;
}
static const struct of_device_id octeon_usb_match[] = {
@@ -3714,7 +3712,7 @@ static struct platform_driver octeon_usb_driver = {
.of_match_table = octeon_usb_match,
},
.probe = octeon_usb_probe,
- .remove = octeon_usb_remove,
+ .remove_new = octeon_usb_remove,
};
static int __init octeon_usb_driver_init(void)
diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c
index 533537ef3c21..b9ce8d80f20b 100644
--- a/drivers/usb/host/ohci-at91.c
+++ b/drivers/usb/host/ohci-at91.c
@@ -599,7 +599,7 @@ static int ohci_hcd_at91_drv_probe(struct platform_device *pdev)
return usb_hcd_at91_probe(&ohci_at91_hc_driver, pdev);
}
-static int ohci_hcd_at91_drv_remove(struct platform_device *pdev)
+static void ohci_hcd_at91_drv_remove(struct platform_device *pdev)
{
struct at91_usbh_data *pdata = dev_get_platdata(&pdev->dev);
int i;
@@ -611,7 +611,6 @@ static int ohci_hcd_at91_drv_remove(struct platform_device *pdev)
device_init_wakeup(&pdev->dev, 0);
usb_hcd_at91_remove(platform_get_drvdata(pdev), pdev);
- return 0;
}
static int __maybe_unused
@@ -683,7 +682,7 @@ static SIMPLE_DEV_PM_OPS(ohci_hcd_at91_pm_ops, ohci_hcd_at91_drv_suspend,
static struct platform_driver ohci_hcd_at91_driver = {
.probe = ohci_hcd_at91_drv_probe,
- .remove = ohci_hcd_at91_drv_remove,
+ .remove_new = ohci_hcd_at91_drv_remove,
.shutdown = usb_hcd_platform_shutdown,
.driver = {
.name = "at91_ohci",
diff --git a/drivers/usb/host/ohci-da8xx.c b/drivers/usb/host/ohci-da8xx.c
index d4818e8d652b..e4191a868944 100644
--- a/drivers/usb/host/ohci-da8xx.c
+++ b/drivers/usb/host/ohci-da8xx.c
@@ -469,14 +469,12 @@ err:
return error;
}
-static int ohci_da8xx_remove(struct platform_device *pdev)
+static void ohci_da8xx_remove(struct platform_device *pdev)
{
struct usb_hcd *hcd = platform_get_drvdata(pdev);
usb_remove_hcd(hcd);
usb_put_hcd(hcd);
-
- return 0;
}
#ifdef CONFIG_PM
@@ -533,7 +531,7 @@ static const struct ohci_driver_overrides da8xx_overrides __initconst = {
*/
static struct platform_driver ohci_hcd_da8xx_driver = {
.probe = ohci_da8xx_probe,
- .remove = ohci_da8xx_remove,
+ .remove_new = ohci_da8xx_remove,
.shutdown = usb_hcd_platform_shutdown,
#ifdef CONFIG_PM
.suspend = ohci_da8xx_suspend,
diff --git a/drivers/usb/host/ohci-exynos.c b/drivers/usb/host/ohci-exynos.c
index 8af17c1ee5cc..ab31c459b32d 100644
--- a/drivers/usb/host/ohci-exynos.c
+++ b/drivers/usb/host/ohci-exynos.c
@@ -198,7 +198,7 @@ fail_clk:
return err;
}
-static int exynos_ohci_remove(struct platform_device *pdev)
+static void exynos_ohci_remove(struct platform_device *pdev)
{
struct usb_hcd *hcd = platform_get_drvdata(pdev);
struct exynos_ohci_hcd *exynos_ohci = to_exynos_ohci(hcd);
@@ -212,8 +212,6 @@ static int exynos_ohci_remove(struct platform_device *pdev)
clk_disable_unprepare(exynos_ohci->clk);
usb_put_hcd(hcd);
-
- return 0;
}
static void exynos_ohci_shutdown(struct platform_device *pdev)
@@ -285,7 +283,7 @@ MODULE_DEVICE_TABLE(of, exynos_ohci_match);
static struct platform_driver exynos_ohci_driver = {
.probe = exynos_ohci_probe,
- .remove = exynos_ohci_remove,
+ .remove_new = exynos_ohci_remove,
.shutdown = exynos_ohci_shutdown,
.driver = {
.name = "exynos-ohci",
diff --git a/drivers/usb/host/ohci-nxp.c b/drivers/usb/host/ohci-nxp.c
index 5b32e683e367..c04b2af5c766 100644
--- a/drivers/usb/host/ohci-nxp.c
+++ b/drivers/usb/host/ohci-nxp.c
@@ -237,7 +237,7 @@ fail_disable:
return ret;
}
-static int ohci_hcd_nxp_remove(struct platform_device *pdev)
+static void ohci_hcd_nxp_remove(struct platform_device *pdev)
{
struct usb_hcd *hcd = platform_get_drvdata(pdev);
@@ -246,8 +246,6 @@ static int ohci_hcd_nxp_remove(struct platform_device *pdev)
usb_put_hcd(hcd);
clk_disable_unprepare(usb_host_clk);
isp1301_i2c_client = NULL;
-
- return 0;
}
/* work with hotplug and coldplug */
@@ -267,7 +265,7 @@ static struct platform_driver ohci_hcd_nxp_driver = {
.of_match_table = of_match_ptr(ohci_hcd_nxp_match),
},
.probe = ohci_hcd_nxp_probe,
- .remove = ohci_hcd_nxp_remove,
+ .remove_new = ohci_hcd_nxp_remove,
};
static int __init ohci_nxp_init(void)
diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c
index c82121602511..21a6f6c55e07 100644
--- a/drivers/usb/host/ohci-omap.c
+++ b/drivers/usb/host/ohci-omap.c
@@ -321,7 +321,7 @@ err_put_hcd:
* the HCD's stop() method. It is always called from a thread
* context, normally "rmmod", "apmd", or something similar.
*/
-static int ohci_hcd_omap_remove(struct platform_device *pdev)
+static void ohci_hcd_omap_remove(struct platform_device *pdev)
{
struct usb_hcd *hcd = platform_get_drvdata(pdev);
struct ohci_omap_priv *priv = hcd_to_ohci_omap_priv(hcd);
@@ -340,7 +340,6 @@ static int ohci_hcd_omap_remove(struct platform_device *pdev)
clk_unprepare(priv->usb_host_ck);
clk_put(priv->usb_host_ck);
usb_put_hcd(hcd);
- return 0;
}
/*-------------------------------------------------------------------------*/
@@ -391,7 +390,7 @@ static int ohci_omap_resume(struct platform_device *dev)
*/
static struct platform_driver ohci_hcd_omap_driver = {
.probe = ohci_hcd_omap_probe,
- .remove = ohci_hcd_omap_remove,
+ .remove_new = ohci_hcd_omap_remove,
.shutdown = usb_hcd_platform_shutdown,
#ifdef CONFIG_PM
.suspend = ohci_omap_suspend,
diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c
index d7b4f40f9ff4..900ea0d368e0 100644
--- a/drivers/usb/host/ohci-pci.c
+++ b/drivers/usb/host/ohci-pci.c
@@ -301,6 +301,12 @@ static struct pci_driver ohci_pci_driver = {
#endif
};
+#ifdef CONFIG_PM
+static int ohci_pci_resume(struct usb_hcd *hcd, pm_message_t msg)
+{
+ return ohci_resume(hcd, msg.event == PM_EVENT_RESTORE);
+}
+#endif
static int __init ohci_pci_init(void)
{
if (usb_disabled())
@@ -311,7 +317,7 @@ static int __init ohci_pci_init(void)
#ifdef CONFIG_PM
/* Entries for the PCI suspend/resume callbacks are special */
ohci_pci_hc_driver.pci_suspend = ohci_suspend;
- ohci_pci_hc_driver.pci_resume = ohci_resume;
+ ohci_pci_hc_driver.pci_resume = ohci_pci_resume;
#endif
return pci_register_driver(&ohci_pci_driver);
diff --git a/drivers/usb/host/ohci-platform.c b/drivers/usb/host/ohci-platform.c
index a84305091c43..45a2c981319e 100644
--- a/drivers/usb/host/ohci-platform.c
+++ b/drivers/usb/host/ohci-platform.c
@@ -33,7 +33,7 @@
#include "ohci.h"
#define DRIVER_DESC "OHCI generic platform driver"
-#define OHCI_MAX_CLKS 3
+#define OHCI_MAX_CLKS 4
#define hcd_to_ohci_priv(h) ((struct ohci_platform_priv *)hcd_to_ohci(h)->priv)
struct ohci_platform_priv {
@@ -239,7 +239,7 @@ err_put_clks:
return err;
}
-static int ohci_platform_remove(struct platform_device *dev)
+static void ohci_platform_remove(struct platform_device *dev)
{
struct usb_hcd *hcd = platform_get_drvdata(dev);
struct usb_ohci_pdata *pdata = dev_get_platdata(&dev->dev);
@@ -264,8 +264,6 @@ static int ohci_platform_remove(struct platform_device *dev)
if (pdata == &ohci_platform_defaults)
dev->dev.platform_data = NULL;
-
- return 0;
}
#ifdef CONFIG_PM_SLEEP
@@ -347,7 +345,7 @@ static const struct dev_pm_ops ohci_platform_pm_ops = {
static struct platform_driver ohci_platform_driver = {
.id_table = ohci_platform_table,
.probe = ohci_platform_probe,
- .remove = ohci_platform_remove,
+ .remove_new = ohci_platform_remove,
.shutdown = usb_hcd_platform_shutdown,
.driver = {
.name = "ohci-platform",
diff --git a/drivers/usb/host/ohci-ppc-of.c b/drivers/usb/host/ohci-ppc-of.c
index f2f6c832ec98..35a7ad7e2569 100644
--- a/drivers/usb/host/ohci-ppc-of.c
+++ b/drivers/usb/host/ohci-ppc-of.c
@@ -176,7 +176,7 @@ err_rmr:
return rv;
}
-static int ohci_hcd_ppc_of_remove(struct platform_device *op)
+static void ohci_hcd_ppc_of_remove(struct platform_device *op)
{
struct usb_hcd *hcd = platform_get_drvdata(op);
@@ -187,8 +187,6 @@ static int ohci_hcd_ppc_of_remove(struct platform_device *op)
irq_dispose_mapping(hcd->irq);
usb_put_hcd(hcd);
-
- return 0;
}
static const struct of_device_id ohci_hcd_ppc_of_match[] = {
@@ -224,7 +222,7 @@ MODULE_DEVICE_TABLE(of, ohci_hcd_ppc_of_match);
static struct platform_driver ohci_hcd_ppc_of_driver = {
.probe = ohci_hcd_ppc_of_probe,
- .remove = ohci_hcd_ppc_of_remove,
+ .remove_new = ohci_hcd_ppc_of_remove,
.shutdown = usb_hcd_platform_shutdown,
.driver = {
.name = "ppc-of-ohci",
diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c
index dcac2938789a..7410442f720f 100644
--- a/drivers/usb/host/ohci-pxa27x.c
+++ b/drivers/usb/host/ohci-pxa27x.c
@@ -501,7 +501,7 @@ static int ohci_hcd_pxa27x_probe(struct platform_device *pdev)
* the HCD's stop() method. It is always called from a thread
* context, normally "rmmod", "apmd", or something similar.
*/
-static int ohci_hcd_pxa27x_remove(struct platform_device *pdev)
+static void ohci_hcd_pxa27x_remove(struct platform_device *pdev)
{
struct usb_hcd *hcd = platform_get_drvdata(pdev);
struct pxa27x_ohci *pxa_ohci = to_pxa27x_ohci(hcd);
@@ -514,7 +514,6 @@ static int ohci_hcd_pxa27x_remove(struct platform_device *pdev)
pxa27x_ohci_set_vbus_power(pxa_ohci, i, false);
usb_put_hcd(hcd);
- return 0;
}
/*-------------------------------------------------------------------------*/
@@ -572,7 +571,7 @@ static const struct dev_pm_ops ohci_hcd_pxa27x_pm_ops = {
static struct platform_driver ohci_hcd_pxa27x_driver = {
.probe = ohci_hcd_pxa27x_probe,
- .remove = ohci_hcd_pxa27x_remove,
+ .remove_new = ohci_hcd_pxa27x_remove,
.shutdown = usb_hcd_platform_shutdown,
.driver = {
.name = "pxa27x-ohci",
diff --git a/drivers/usb/host/ohci-s3c2410.c b/drivers/usb/host/ohci-s3c2410.c
index 85a0a9ae0095..c5c9b4cbcb9a 100644
--- a/drivers/usb/host/ohci-s3c2410.c
+++ b/drivers/usb/host/ohci-s3c2410.c
@@ -329,7 +329,7 @@ static void s3c2410_hcd_oc(struct s3c2410_hcd_info *info, int port_oc)
* the HCD's stop() method. It is always called from a thread
* context, normally "rmmod", "apmd", or something similar.
*/
-static int
+static void
ohci_hcd_s3c2410_remove(struct platform_device *dev)
{
struct usb_hcd *hcd = platform_get_drvdata(dev);
@@ -337,7 +337,6 @@ ohci_hcd_s3c2410_remove(struct platform_device *dev)
usb_remove_hcd(hcd);
s3c2410_stop_hc(dev);
usb_put_hcd(hcd);
- return 0;
}
/*
@@ -458,7 +457,7 @@ MODULE_DEVICE_TABLE(of, ohci_hcd_s3c2410_dt_ids);
static struct platform_driver ohci_hcd_s3c2410_driver = {
.probe = ohci_hcd_s3c2410_probe,
- .remove = ohci_hcd_s3c2410_remove,
+ .remove_new = ohci_hcd_s3c2410_remove,
.shutdown = usb_hcd_platform_shutdown,
.driver = {
.name = "s3c2410-ohci",
diff --git a/drivers/usb/host/ohci-sm501.c b/drivers/usb/host/ohci-sm501.c
index f5de586454e3..0468eeb4fcfd 100644
--- a/drivers/usb/host/ohci-sm501.c
+++ b/drivers/usb/host/ohci-sm501.c
@@ -185,7 +185,7 @@ err0:
return retval;
}
-static int ohci_hcd_sm501_drv_remove(struct platform_device *pdev)
+static void ohci_hcd_sm501_drv_remove(struct platform_device *pdev)
{
struct usb_hcd *hcd = platform_get_drvdata(pdev);
struct resource *mem;
@@ -202,8 +202,6 @@ static int ohci_hcd_sm501_drv_remove(struct platform_device *pdev)
sm501_modify_reg(pdev->dev.parent, SM501_IRQ_MASK, 0, 1 << 6);
sm501_unit_power(pdev->dev.parent, SM501_GATE_USB_HOST, 0);
-
- return 0;
}
/*-------------------------------------------------------------------------*/
@@ -255,7 +253,7 @@ static int ohci_sm501_resume(struct platform_device *pdev)
*/
static struct platform_driver ohci_hcd_sm501_driver = {
.probe = ohci_hcd_sm501_drv_probe,
- .remove = ohci_hcd_sm501_drv_remove,
+ .remove_new = ohci_hcd_sm501_drv_remove,
.shutdown = usb_hcd_platform_shutdown,
.suspend = ohci_sm501_suspend,
.resume = ohci_sm501_resume,
diff --git a/drivers/usb/host/ohci-spear.c b/drivers/usb/host/ohci-spear.c
index 196951a27f3f..f4b2656407dd 100644
--- a/drivers/usb/host/ohci-spear.c
+++ b/drivers/usb/host/ohci-spear.c
@@ -98,7 +98,7 @@ fail:
return retval;
}
-static int spear_ohci_hcd_drv_remove(struct platform_device *pdev)
+static void spear_ohci_hcd_drv_remove(struct platform_device *pdev)
{
struct usb_hcd *hcd = platform_get_drvdata(pdev);
struct spear_ohci *sohci_p = to_spear_ohci(hcd);
@@ -108,7 +108,6 @@ static int spear_ohci_hcd_drv_remove(struct platform_device *pdev)
clk_disable_unprepare(sohci_p->clk);
usb_put_hcd(hcd);
- return 0;
}
#if defined(CONFIG_PM)
@@ -159,7 +158,7 @@ MODULE_DEVICE_TABLE(of, spear_ohci_id_table);
/* Driver definition to register with the platform bus */
static struct platform_driver spear_ohci_hcd_driver = {
.probe = spear_ohci_hcd_drv_probe,
- .remove = spear_ohci_hcd_drv_remove,
+ .remove_new = spear_ohci_hcd_drv_remove,
#ifdef CONFIG_PM
.suspend = spear_ohci_hcd_drv_suspend,
.resume = spear_ohci_hcd_drv_resume,
diff --git a/drivers/usb/host/ohci-st.c b/drivers/usb/host/ohci-st.c
index 82eef3c62e11..884e447a8098 100644
--- a/drivers/usb/host/ohci-st.c
+++ b/drivers/usb/host/ohci-st.c
@@ -233,7 +233,7 @@ err_put_hcd:
return err;
}
-static int st_ohci_platform_remove(struct platform_device *dev)
+static void st_ohci_platform_remove(struct platform_device *dev)
{
struct usb_hcd *hcd = platform_get_drvdata(dev);
struct usb_ohci_pdata *pdata = dev_get_platdata(&dev->dev);
@@ -253,8 +253,6 @@ static int st_ohci_platform_remove(struct platform_device *dev)
if (pdata == &ohci_platform_defaults)
dev->dev.platform_data = NULL;
-
- return 0;
}
#ifdef CONFIG_PM_SLEEP
@@ -306,7 +304,7 @@ MODULE_DEVICE_TABLE(of, st_ohci_platform_ids);
static struct platform_driver ohci_platform_driver = {
.probe = st_ohci_platform_probe,
- .remove = st_ohci_platform_remove,
+ .remove_new = st_ohci_platform_remove,
.shutdown = usb_hcd_platform_shutdown,
.driver = {
.name = "st-ohci",
diff --git a/drivers/usb/host/oxu210hp-hcd.c b/drivers/usb/host/oxu210hp-hcd.c
index f998d3f1a78a..50c1ccabb0f5 100644
--- a/drivers/usb/host/oxu210hp-hcd.c
+++ b/drivers/usb/host/oxu210hp-hcd.c
@@ -4278,14 +4278,12 @@ static void oxu_remove(struct platform_device *pdev, struct usb_hcd *hcd)
usb_put_hcd(hcd);
}
-static int oxu_drv_remove(struct platform_device *pdev)
+static void oxu_drv_remove(struct platform_device *pdev)
{
struct oxu_info *info = platform_get_drvdata(pdev);
oxu_remove(pdev, info->hcd[0]);
oxu_remove(pdev, info->hcd[1]);
-
- return 0;
}
static void oxu_drv_shutdown(struct platform_device *pdev)
@@ -4317,7 +4315,7 @@ static int oxu_drv_resume(struct device *dev)
static struct platform_driver oxu_driver = {
.probe = oxu_drv_probe,
- .remove = oxu_drv_remove,
+ .remove_new = oxu_drv_remove,
.shutdown = oxu_drv_shutdown,
.suspend = oxu_drv_suspend,
.resume = oxu_drv_resume,
diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c
index abb88dd40d4e..9f4bf8c5f8a5 100644
--- a/drivers/usb/host/r8a66597-hcd.c
+++ b/drivers/usb/host/r8a66597-hcd.c
@@ -2379,7 +2379,7 @@ static const struct dev_pm_ops r8a66597_dev_pm_ops = {
#define R8A66597_DEV_PM_OPS NULL
#endif
-static int r8a66597_remove(struct platform_device *pdev)
+static void r8a66597_remove(struct platform_device *pdev)
{
struct r8a66597 *r8a66597 = platform_get_drvdata(pdev);
struct usb_hcd *hcd = r8a66597_to_hcd(r8a66597);
@@ -2390,7 +2390,6 @@ static int r8a66597_remove(struct platform_device *pdev)
if (r8a66597->pdata->on_chip)
clk_put(r8a66597->clk);
usb_put_hcd(hcd);
- return 0;
}
static int r8a66597_probe(struct platform_device *pdev)
@@ -2511,7 +2510,7 @@ clean_up:
static struct platform_driver r8a66597_driver = {
.probe = r8a66597_probe,
- .remove = r8a66597_remove,
+ .remove_new = r8a66597_remove,
.driver = {
.name = hcd_name,
.pm = R8A66597_DEV_PM_OPS,
diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c
index b8b90eec9107..0956495bba57 100644
--- a/drivers/usb/host/sl811-hcd.c
+++ b/drivers/usb/host/sl811-hcd.c
@@ -1579,7 +1579,7 @@ static const struct hc_driver sl811h_hc_driver = {
/*-------------------------------------------------------------------------*/
-static int
+static void
sl811h_remove(struct platform_device *dev)
{
struct usb_hcd *hcd = platform_get_drvdata(dev);
@@ -1599,7 +1599,6 @@ sl811h_remove(struct platform_device *dev)
iounmap(sl811->addr_reg);
usb_put_hcd(hcd);
- return 0;
}
static int
@@ -1783,7 +1782,7 @@ sl811h_resume(struct platform_device *dev)
/* this driver is exported so sl811_cs can depend on it */
struct platform_driver sl811h_driver = {
.probe = sl811h_probe,
- .remove = sl811h_remove,
+ .remove_new = sl811h_remove,
.suspend = sl811h_suspend,
.resume = sl811h_resume,
diff --git a/drivers/usb/host/uhci-grlib.c b/drivers/usb/host/uhci-grlib.c
index 907d5f01edfd..ac3fc5970315 100644
--- a/drivers/usb/host/uhci-grlib.c
+++ b/drivers/usb/host/uhci-grlib.c
@@ -147,7 +147,7 @@ err_usb:
return rv;
}
-static int uhci_hcd_grlib_remove(struct platform_device *op)
+static void uhci_hcd_grlib_remove(struct platform_device *op)
{
struct usb_hcd *hcd = platform_get_drvdata(op);
@@ -157,8 +157,6 @@ static int uhci_hcd_grlib_remove(struct platform_device *op)
irq_dispose_mapping(hcd->irq);
usb_put_hcd(hcd);
-
- return 0;
}
/* Make sure the controller is quiescent and that we're not using it
@@ -185,7 +183,7 @@ MODULE_DEVICE_TABLE(of, uhci_hcd_grlib_of_match);
static struct platform_driver uhci_grlib_driver = {
.probe = uhci_hcd_grlib_probe,
- .remove = uhci_hcd_grlib_remove,
+ .remove_new = uhci_hcd_grlib_remove,
.shutdown = uhci_hcd_grlib_shutdown,
.driver = {
.name = "grlib-uhci",
diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c
index 7cdc2fa7c28f..fd2408b553cf 100644
--- a/drivers/usb/host/uhci-hcd.c
+++ b/drivers/usb/host/uhci-hcd.c
@@ -841,7 +841,7 @@ static int uhci_count_ports(struct usb_hcd *hcd)
static const char hcd_name[] = "uhci_hcd";
-#ifdef CONFIG_USB_PCI
+#if defined(CONFIG_USB_PCI) && defined(CONFIG_HAS_IOPORT)
#include "uhci-pci.c"
#define PCI_DRIVER uhci_pci_driver
#endif
diff --git a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h
index 0688c3e5bfe2..13ee2a6144b2 100644
--- a/drivers/usb/host/uhci-hcd.h
+++ b/drivers/usb/host/uhci-hcd.h
@@ -505,6 +505,14 @@ static inline bool uhci_is_aspeed(const struct uhci_hcd *uhci)
* we use memory mapped registers.
*/
+#ifdef CONFIG_HAS_IOPORT
+#define UHCI_IN(x) x
+#define UHCI_OUT(x) x
+#else
+#define UHCI_IN(x) 0
+#define UHCI_OUT(x) do { } while (0)
+#endif
+
#ifndef CONFIG_USB_UHCI_SUPPORT_NON_PCI_HC
/* Support PCI only */
static inline u32 uhci_readl(const struct uhci_hcd *uhci, int reg)
@@ -539,7 +547,7 @@ static inline void uhci_writeb(const struct uhci_hcd *uhci, u8 val, int reg)
#else
/* Support non-PCI host controllers */
-#ifdef CONFIG_USB_PCI
+#if defined(CONFIG_USB_PCI) && defined(HAS_IOPORT)
/* Support PCI and non-PCI host controllers */
#define uhci_has_pci_registers(u) ((u)->io_addr != 0)
#else
@@ -587,7 +595,7 @@ static inline int uhci_aspeed_reg(unsigned int reg)
static inline u32 uhci_readl(const struct uhci_hcd *uhci, int reg)
{
if (uhci_has_pci_registers(uhci))
- return inl(uhci->io_addr + reg);
+ return UHCI_IN(inl(uhci->io_addr + reg));
else if (uhci_is_aspeed(uhci))
return readl(uhci->regs + uhci_aspeed_reg(reg));
#ifdef CONFIG_USB_UHCI_BIG_ENDIAN_MMIO
@@ -601,7 +609,7 @@ static inline u32 uhci_readl(const struct uhci_hcd *uhci, int reg)
static inline void uhci_writel(const struct uhci_hcd *uhci, u32 val, int reg)
{
if (uhci_has_pci_registers(uhci))
- outl(val, uhci->io_addr + reg);
+ UHCI_OUT(outl(val, uhci->io_addr + reg));
else if (uhci_is_aspeed(uhci))
writel(val, uhci->regs + uhci_aspeed_reg(reg));
#ifdef CONFIG_USB_UHCI_BIG_ENDIAN_MMIO
@@ -615,7 +623,7 @@ static inline void uhci_writel(const struct uhci_hcd *uhci, u32 val, int reg)
static inline u16 uhci_readw(const struct uhci_hcd *uhci, int reg)
{
if (uhci_has_pci_registers(uhci))
- return inw(uhci->io_addr + reg);
+ return UHCI_IN(inw(uhci->io_addr + reg));
else if (uhci_is_aspeed(uhci))
return readl(uhci->regs + uhci_aspeed_reg(reg));
#ifdef CONFIG_USB_UHCI_BIG_ENDIAN_MMIO
@@ -629,7 +637,7 @@ static inline u16 uhci_readw(const struct uhci_hcd *uhci, int reg)
static inline void uhci_writew(const struct uhci_hcd *uhci, u16 val, int reg)
{
if (uhci_has_pci_registers(uhci))
- outw(val, uhci->io_addr + reg);
+ UHCI_OUT(outw(val, uhci->io_addr + reg));
else if (uhci_is_aspeed(uhci))
writel(val, uhci->regs + uhci_aspeed_reg(reg));
#ifdef CONFIG_USB_UHCI_BIG_ENDIAN_MMIO
@@ -643,7 +651,7 @@ static inline void uhci_writew(const struct uhci_hcd *uhci, u16 val, int reg)
static inline u8 uhci_readb(const struct uhci_hcd *uhci, int reg)
{
if (uhci_has_pci_registers(uhci))
- return inb(uhci->io_addr + reg);
+ return UHCI_IN(inb(uhci->io_addr + reg));
else if (uhci_is_aspeed(uhci))
return readl(uhci->regs + uhci_aspeed_reg(reg));
#ifdef CONFIG_USB_UHCI_BIG_ENDIAN_MMIO
@@ -657,7 +665,7 @@ static inline u8 uhci_readb(const struct uhci_hcd *uhci, int reg)
static inline void uhci_writeb(const struct uhci_hcd *uhci, u8 val, int reg)
{
if (uhci_has_pci_registers(uhci))
- outb(val, uhci->io_addr + reg);
+ UHCI_OUT(outb(val, uhci->io_addr + reg));
else if (uhci_is_aspeed(uhci))
writel(val, uhci->regs + uhci_aspeed_reg(reg));
#ifdef CONFIG_USB_UHCI_BIG_ENDIAN_MMIO
@@ -668,6 +676,8 @@ static inline void uhci_writeb(const struct uhci_hcd *uhci, u8 val, int reg)
writeb(val, uhci->regs + reg);
}
#endif /* CONFIG_USB_UHCI_SUPPORT_NON_PCI_HC */
+#undef UHCI_IN
+#undef UHCI_OUT
/*
* The GRLIB GRUSBHC controller can use big endian format for its descriptors.
diff --git a/drivers/usb/host/uhci-pci.c b/drivers/usb/host/uhci-pci.c
index 7bd2fddde770..5edf6a08cf82 100644
--- a/drivers/usb/host/uhci-pci.c
+++ b/drivers/usb/host/uhci-pci.c
@@ -169,7 +169,7 @@ static void uhci_shutdown(struct pci_dev *pdev)
#ifdef CONFIG_PM
-static int uhci_pci_resume(struct usb_hcd *hcd, bool hibernated);
+static int uhci_pci_resume(struct usb_hcd *hcd, pm_message_t state);
static int uhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup)
{
@@ -204,14 +204,15 @@ done_okay:
/* Check for race with a wakeup request */
if (do_wakeup && HCD_WAKEUP_PENDING(hcd)) {
- uhci_pci_resume(hcd, false);
+ uhci_pci_resume(hcd, PMSG_SUSPEND);
rc = -EBUSY;
}
return rc;
}
-static int uhci_pci_resume(struct usb_hcd *hcd, bool hibernated)
+static int uhci_pci_resume(struct usb_hcd *hcd, pm_message_t msg)
{
+ bool hibernated = (msg.event == PM_EVENT_RESTORE);
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
dev_dbg(uhci_dev(uhci), "%s\n", __func__);
diff --git a/drivers/usb/host/uhci-platform.c b/drivers/usb/host/uhci-platform.c
index b2049b47a08d..71ca532fc086 100644
--- a/drivers/usb/host/uhci-platform.c
+++ b/drivers/usb/host/uhci-platform.c
@@ -152,7 +152,7 @@ err_rmr:
return ret;
}
-static int uhci_hcd_platform_remove(struct platform_device *pdev)
+static void uhci_hcd_platform_remove(struct platform_device *pdev)
{
struct usb_hcd *hcd = platform_get_drvdata(pdev);
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
@@ -160,8 +160,6 @@ static int uhci_hcd_platform_remove(struct platform_device *pdev)
clk_disable_unprepare(uhci->clk);
usb_remove_hcd(hcd);
usb_put_hcd(hcd);
-
- return 0;
}
/* Make sure the controller is quiescent and that we're not using it
@@ -187,7 +185,7 @@ MODULE_DEVICE_TABLE(of, platform_uhci_ids);
static struct platform_driver uhci_platform_driver = {
.probe = uhci_hcd_platform_probe,
- .remove = uhci_hcd_platform_remove,
+ .remove_new = uhci_hcd_platform_remove,
.shutdown = uhci_hcd_platform_shutdown,
.driver = {
.name = "platform-uhci",
diff --git a/drivers/usb/host/xhci-histb.c b/drivers/usb/host/xhci-histb.c
index 08369857686e..f9a4a4b0eb57 100644
--- a/drivers/usb/host/xhci-histb.c
+++ b/drivers/usb/host/xhci-histb.c
@@ -164,16 +164,6 @@ static void xhci_histb_host_disable(struct xhci_hcd_histb *histb)
clk_disable_unprepare(histb->bus_clk);
}
-static void xhci_histb_quirks(struct device *dev, struct xhci_hcd *xhci)
-{
- /*
- * As of now platform drivers don't provide MSI support so we ensure
- * here that the generic code does not try to make a pci_dev from our
- * dev struct in order to setup MSI
- */
- xhci->quirks |= XHCI_PLAT;
-}
-
/* called during probe() after chip reset completes */
static int xhci_histb_setup(struct usb_hcd *hcd)
{
@@ -186,7 +176,7 @@ static int xhci_histb_setup(struct usb_hcd *hcd)
return ret;
}
- return xhci_gen_setup(hcd, xhci_histb_quirks);
+ return xhci_gen_setup(hcd, NULL);
}
static const struct xhci_driver_overrides xhci_histb_overrides __initconst = {
@@ -319,7 +309,7 @@ disable_pm:
return ret;
}
-static int xhci_histb_remove(struct platform_device *dev)
+static void xhci_histb_remove(struct platform_device *dev)
{
struct xhci_hcd_histb *histb = platform_get_drvdata(dev);
struct usb_hcd *hcd = histb->hcd;
@@ -339,8 +329,6 @@ static int xhci_histb_remove(struct platform_device *dev)
usb_put_hcd(hcd);
pm_runtime_put_sync(&dev->dev);
pm_runtime_disable(&dev->dev);
-
- return 0;
}
static int __maybe_unused xhci_histb_suspend(struct device *dev)
@@ -367,7 +355,7 @@ static int __maybe_unused xhci_histb_resume(struct device *dev)
if (!device_may_wakeup(dev))
xhci_histb_host_enable(histb);
- return xhci_resume(xhci, 0);
+ return xhci_resume(xhci, PMSG_RESUME);
}
static const struct dev_pm_ops xhci_histb_pm_ops = {
@@ -385,7 +373,7 @@ MODULE_DEVICE_TABLE(of, histb_xhci_of_match);
static struct platform_driver histb_xhci_driver = {
.probe = xhci_histb_probe,
- .remove = xhci_histb_remove,
+ .remove_new = xhci_histb_remove,
.driver = {
.name = "xhci-histb",
.pm = DEV_PM_OPS,
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index 7e106bd804ca..19a402123de0 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -143,7 +143,6 @@ static void xhci_link_rings(struct xhci_hcd *xhci, struct xhci_ring *ring,
xhci_link_segments(ring->enq_seg, first, ring->type, chain_links);
xhci_link_segments(last, next, ring->type, chain_links);
ring->num_segs += num_segs;
- ring->num_trbs_free += (TRBS_PER_SEGMENT - 1) * num_segs;
if (ring->type != TYPE_EVENT && ring->enq_seg == ring->last_seg) {
ring->last_seg->trbs[TRBS_PER_SEGMENT-1].link.control
@@ -422,22 +421,14 @@ void xhci_free_endpoint_ring(struct xhci_hcd *xhci,
* Allocate a new ring which has same segment numbers and link the two rings.
*/
int xhci_ring_expansion(struct xhci_hcd *xhci, struct xhci_ring *ring,
- unsigned int num_trbs, gfp_t flags)
+ unsigned int num_new_segs, gfp_t flags)
{
struct xhci_segment *first;
struct xhci_segment *last;
- unsigned int num_segs;
- unsigned int num_segs_needed;
int ret;
- num_segs_needed = (num_trbs + (TRBS_PER_SEGMENT - 1) - 1) /
- (TRBS_PER_SEGMENT - 1);
-
- /* Allocate number of segments we needed, or double the ring size */
- num_segs = max(ring->num_segs, num_segs_needed);
-
ret = xhci_alloc_segments_for_ring(xhci, &first, &last,
- num_segs, ring->cycle_state, ring->type,
+ num_new_segs, ring->cycle_state, ring->type,
ring->bounce_buf_len, flags);
if (ret)
return -ENOMEM;
@@ -457,7 +448,7 @@ int xhci_ring_expansion(struct xhci_hcd *xhci, struct xhci_ring *ring,
return ret;
}
- xhci_link_rings(xhci, ring, first, last, num_segs);
+ xhci_link_rings(xhci, ring, first, last, num_new_segs);
trace_xhci_ring_expansion(ring);
xhci_dbg_trace(xhci, trace_xhci_dbg_ring_expansion,
"ring expansion succeed, now has %d segments",
@@ -1831,13 +1822,15 @@ xhci_free_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir)
* low or high 32 bits of ERSTBA immediately causes the controller to
* dereference the partially cleared 64 bit address, causing IOMMU error.
*/
- tmp = readl(&ir->ir_set->erst_size);
- tmp &= ERST_SIZE_MASK;
- writel(tmp, &ir->ir_set->erst_size);
-
- tmp64 = xhci_read_64(xhci, &ir->ir_set->erst_dequeue);
- tmp64 &= (u64) ERST_PTR_MASK;
- xhci_write_64(xhci, tmp64, &ir->ir_set->erst_dequeue);
+ if (ir->ir_set) {
+ tmp = readl(&ir->ir_set->erst_size);
+ tmp &= ERST_SIZE_MASK;
+ writel(tmp, &ir->ir_set->erst_size);
+
+ tmp64 = xhci_read_64(xhci, &ir->ir_set->erst_dequeue);
+ tmp64 &= (u64) ERST_PTR_MASK;
+ xhci_write_64(xhci, tmp64, &ir->ir_set->erst_dequeue);
+ }
/* free interrrupter event ring */
if (ir->event_ring)
@@ -1968,7 +1961,7 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports,
{
u32 temp, port_offset, port_count;
int i;
- u8 major_revision, minor_revision;
+ u8 major_revision, minor_revision, tmp_minor_revision;
struct xhci_hub *rhub;
struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
struct xhci_port_cap *port_cap;
@@ -1988,6 +1981,15 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports,
*/
if (minor_revision > 0x00 && minor_revision < 0x10)
minor_revision <<= 4;
+ /*
+ * Some zhaoxin's xHCI controller that follow usb3.1 spec
+ * but only support Gen1.
+ */
+ if (xhci->quirks & XHCI_ZHAOXIN_HOST) {
+ tmp_minor_revision = minor_revision;
+ minor_revision = 0;
+ }
+
} else if (major_revision <= 0x02) {
rhub = &xhci->usb2_rhub;
} else {
@@ -1996,10 +1998,6 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports,
/* Ignoring port protocol we can't understand. FIXME */
return;
}
- rhub->maj_rev = XHCI_EXT_PORT_MAJOR(temp);
-
- if (rhub->min_rev < minor_revision)
- rhub->min_rev = minor_revision;
/* Port offset and count in the third dword, see section 7.2 */
temp = readl(addr + 2);
@@ -2017,8 +2015,6 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports,
if (xhci->num_port_caps > max_caps)
return;
- port_cap->maj_rev = major_revision;
- port_cap->min_rev = minor_revision;
port_cap->psi_count = XHCI_EXT_PORT_PSIC(temp);
if (port_cap->psi_count) {
@@ -2039,6 +2035,11 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports,
XHCI_EXT_PORT_PSIV(port_cap->psi[i - 1])))
port_cap->psi_uid_count++;
+ if (xhci->quirks & XHCI_ZHAOXIN_HOST &&
+ major_revision == 0x03 &&
+ XHCI_EXT_PORT_PSIV(port_cap->psi[i]) >= 5)
+ minor_revision = tmp_minor_revision;
+
xhci_dbg(xhci, "PSIV:%d PSIE:%d PLT:%d PFD:%d LP:%d PSIM:%d\n",
XHCI_EXT_PORT_PSIV(port_cap->psi[i]),
XHCI_EXT_PORT_PSIE(port_cap->psi[i]),
@@ -2048,6 +2049,15 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports,
XHCI_EXT_PORT_PSIM(port_cap->psi[i]));
}
}
+
+ rhub->maj_rev = major_revision;
+
+ if (rhub->min_rev < minor_revision)
+ rhub->min_rev = minor_revision;
+
+ port_cap->maj_rev = major_revision;
+ port_cap->min_rev = minor_revision;
+
/* cache usb2 port capabilities */
if (major_revision < 0x03 && xhci->num_ext_caps < max_caps)
xhci->ext_caps[xhci->num_ext_caps++] = temp;
@@ -2227,43 +2237,50 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags)
}
static struct xhci_interrupter *
-xhci_alloc_interrupter(struct xhci_hcd *xhci, unsigned int intr_num, gfp_t flags)
+xhci_alloc_interrupter(struct xhci_hcd *xhci, gfp_t flags)
{
struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
struct xhci_interrupter *ir;
- u64 erst_base;
- u32 erst_size;
int ret;
- if (intr_num > xhci->max_interrupters) {
- xhci_warn(xhci, "Can't allocate interrupter %d, max interrupters %d\n",
- intr_num, xhci->max_interrupters);
- return NULL;
- }
-
- if (xhci->interrupter) {
- xhci_warn(xhci, "Can't allocate already set up interrupter %d\n", intr_num);
- return NULL;
- }
-
ir = kzalloc_node(sizeof(*ir), flags, dev_to_node(dev));
if (!ir)
return NULL;
- ir->ir_set = &xhci->run_regs->ir_set[intr_num];
ir->event_ring = xhci_ring_alloc(xhci, ERST_NUM_SEGS, 1, TYPE_EVENT,
0, flags);
if (!ir->event_ring) {
- xhci_warn(xhci, "Failed to allocate interrupter %d event ring\n", intr_num);
- goto fail_ir;
+ xhci_warn(xhci, "Failed to allocate interrupter event ring\n");
+ kfree(ir);
+ return NULL;
}
ret = xhci_alloc_erst(xhci, ir->event_ring, &ir->erst, flags);
if (ret) {
- xhci_warn(xhci, "Failed to allocate interrupter %d erst\n", intr_num);
- goto fail_ev;
+ xhci_warn(xhci, "Failed to allocate interrupter erst\n");
+ xhci_ring_free(xhci, ir->event_ring);
+ kfree(ir);
+ return NULL;
+ }
+ return ir;
+}
+
+static int
+xhci_add_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir,
+ unsigned int intr_num)
+{
+ u64 erst_base;
+ u32 erst_size;
+
+ if (intr_num > xhci->max_interrupters) {
+ xhci_warn(xhci, "Can't add interrupter %d, max interrupters %d\n",
+ intr_num, xhci->max_interrupters);
+ return -EINVAL;
}
+
+ ir->ir_set = &xhci->run_regs->ir_set[intr_num];
+
/* set ERST count with the number of entries in the segment table */
erst_size = readl(&ir->ir_set->erst_size);
erst_size &= ERST_SIZE_MASK;
@@ -2278,14 +2295,7 @@ xhci_alloc_interrupter(struct xhci_hcd *xhci, unsigned int intr_num, gfp_t flags
/* Set the event ring dequeue address of this interrupter */
xhci_set_hc_event_deq(xhci, ir);
- return ir;
-
-fail_ev:
- xhci_ring_free(xhci, ir->event_ring);
-fail_ir:
- kfree(ir);
-
- return NULL;
+ return 0;
}
int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
@@ -2352,8 +2362,12 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
* and our use of dma addresses in the trb_address_map radix tree needs
* TRB_SEGMENT_SIZE alignment, so we pick the greater alignment need.
*/
- xhci->segment_pool = dma_pool_create("xHCI ring segments", dev,
- TRB_SEGMENT_SIZE, TRB_SEGMENT_SIZE, xhci->page_size);
+ if (xhci->quirks & XHCI_ZHAOXIN_TRB_FETCH)
+ xhci->segment_pool = dma_pool_create("xHCI ring segments", dev,
+ TRB_SEGMENT_SIZE * 2, TRB_SEGMENT_SIZE * 2, xhci->page_size * 2);
+ else
+ xhci->segment_pool = dma_pool_create("xHCI ring segments", dev,
+ TRB_SEGMENT_SIZE, TRB_SEGMENT_SIZE, xhci->page_size);
/* See Table 46 and Note on Figure 55 */
xhci->device_pool = dma_pool_create("xHCI input/output contexts", dev,
@@ -2407,15 +2421,17 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
"// Doorbell array is located at offset 0x%x from cap regs base addr",
val);
xhci->dba = (void __iomem *) xhci->cap_regs + val;
- /* Set ir_set to interrupt register set 0 */
- /* allocate and set up primary interrupter with an event ring. */
+ /* Allocate and set up primary interrupter 0 with an event ring. */
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
"Allocating primary event ring");
- xhci->interrupter = xhci_alloc_interrupter(xhci, 0, flags);
+ xhci->interrupter = xhci_alloc_interrupter(xhci, flags);
if (!xhci->interrupter)
goto fail;
+ if (xhci_add_interrupter(xhci, xhci->interrupter, 0))
+ goto fail;
+
xhci->isoc_bei_interval = AVOID_BEI_INTERVAL_MAX;
/*
diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c
index 90cf40d6d0c3..51d9d4d4f6a5 100644
--- a/drivers/usb/host/xhci-mtk.c
+++ b/drivers/usb/host/xhci-mtk.c
@@ -418,12 +418,6 @@ static void xhci_mtk_quirks(struct device *dev, struct xhci_hcd *xhci)
struct usb_hcd *hcd = xhci_to_hcd(xhci);
struct xhci_hcd_mtk *mtk = hcd_to_mtk(hcd);
- /*
- * As of now platform drivers don't provide MSI support so we ensure
- * here that the generic code does not try to make a pci_dev from our
- * dev struct in order to setup MSI
- */
- xhci->quirks |= XHCI_PLAT;
xhci->quirks |= XHCI_MTK_HOST;
/*
* MTK host controller gives a spurious successful event after a
@@ -673,7 +667,7 @@ disable_pm:
return ret;
}
-static int xhci_mtk_remove(struct platform_device *pdev)
+static void xhci_mtk_remove(struct platform_device *pdev)
{
struct xhci_hcd_mtk *mtk = platform_get_drvdata(pdev);
struct usb_hcd *hcd = mtk->hcd;
@@ -703,8 +697,6 @@ static int xhci_mtk_remove(struct platform_device *pdev)
pm_runtime_disable(dev);
pm_runtime_put_noidle(dev);
pm_runtime_set_suspended(dev);
-
- return 0;
}
static int __maybe_unused xhci_mtk_suspend(struct device *dev)
@@ -824,7 +816,7 @@ MODULE_DEVICE_TABLE(of, mtk_xhci_of_match);
static struct platform_driver mtk_xhci_driver = {
.probe = xhci_mtk_probe,
- .remove = xhci_mtk_remove,
+ .remove_new = xhci_mtk_remove,
.driver = {
.name = "xhci-mtk",
.pm = DEV_PM_OPS,
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index 79b3691f373f..c6742bae41c0 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -108,9 +108,6 @@ static void xhci_cleanup_msix(struct xhci_hcd *xhci)
struct usb_hcd *hcd = xhci_to_hcd(xhci);
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
- if (xhci->quirks & XHCI_PLAT)
- return;
-
/* return if using legacy interrupt */
if (hcd->irq > 0)
return;
@@ -208,10 +205,6 @@ static int xhci_try_enable_msi(struct usb_hcd *hcd)
struct pci_dev *pdev;
int ret;
- /* The xhci platform device has set up IRQs through usb_add_hcd. */
- if (xhci->quirks & XHCI_PLAT)
- return 0;
-
pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
/*
* Some Fresco Logic host controllers advertise MSI, but fail to
@@ -528,6 +521,19 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
pdev->device == PCI_DEVICE_ID_AMD_PROMONTORYA_4))
xhci->quirks |= XHCI_NO_SOFT_RETRY;
+ if (pdev->vendor == PCI_VENDOR_ID_ZHAOXIN) {
+ xhci->quirks |= XHCI_ZHAOXIN_HOST;
+ xhci->quirks |= XHCI_LPM_SUPPORT;
+
+ if (pdev->device == 0x9202) {
+ xhci->quirks |= XHCI_RESET_ON_RESUME;
+ xhci->quirks |= XHCI_ZHAOXIN_TRB_FETCH;
+ }
+
+ if (pdev->device == 0x9203)
+ xhci->quirks |= XHCI_ZHAOXIN_TRB_FETCH;
+ }
+
/* xHC spec requires PCI devices to support D3hot and D3cold */
if (xhci->hci_version >= 0x120)
xhci->quirks |= XHCI_DEFAULT_PM_RUNTIME_ALLOW;
@@ -832,7 +838,7 @@ static int xhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup)
return ret;
}
-static int xhci_pci_resume(struct usb_hcd *hcd, bool hibernated)
+static int xhci_pci_resume(struct usb_hcd *hcd, pm_message_t msg)
{
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
@@ -867,7 +873,7 @@ static int xhci_pci_resume(struct usb_hcd *hcd, bool hibernated)
if (xhci->quirks & XHCI_PME_STUCK_QUIRK)
xhci_pme_quirk(hcd);
- retval = xhci_resume(xhci, hibernated);
+ retval = xhci_resume(xhci, msg);
return retval;
}
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index b0c8e8efc43b..b26ea7cb4357 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -78,12 +78,7 @@ static void xhci_plat_quirks(struct device *dev, struct xhci_hcd *xhci)
{
struct xhci_plat_priv *priv = xhci_to_priv(xhci);
- /*
- * As of now platform drivers don't provide MSI support so we ensure
- * here that the generic code does not try to make a pci_dev from our
- * dev struct in order to setup MSI
- */
- xhci->quirks |= XHCI_PLAT | priv->quirks;
+ xhci->quirks |= priv->quirks;
}
/* called during probe() after chip reset completes */
@@ -294,10 +289,6 @@ int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, const s
xhci->shared_hcd->usb_phy = devm_usb_get_phy_by_phandle(sysdev,
"usb-phy", 1);
if (IS_ERR(xhci->shared_hcd->usb_phy)) {
- if (PTR_ERR(xhci->shared_hcd->usb_phy) != -ENODEV)
- dev_err(sysdev, "%s get usb3phy fail (ret=%d)\n",
- __func__,
- (int)PTR_ERR(xhci->shared_hcd->usb_phy));
xhci->shared_hcd->usb_phy = NULL;
} else {
ret = usb_phy_init(xhci->shared_hcd->usb_phy);
@@ -399,7 +390,7 @@ static int xhci_generic_plat_probe(struct platform_device *pdev)
return xhci_plat_probe(pdev, sysdev, priv_match);
}
-int xhci_plat_remove(struct platform_device *dev)
+void xhci_plat_remove(struct platform_device *dev)
{
struct usb_hcd *hcd = platform_get_drvdata(dev);
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
@@ -407,8 +398,8 @@ int xhci_plat_remove(struct platform_device *dev)
struct clk *reg_clk = xhci->reg_clk;
struct usb_hcd *shared_hcd = xhci->shared_hcd;
- pm_runtime_get_sync(&dev->dev);
xhci->xhc_state |= XHCI_STATE_REMOVING;
+ pm_runtime_get_sync(&dev->dev);
if (shared_hcd) {
usb_remove_hcd(shared_hcd);
@@ -430,8 +421,6 @@ int xhci_plat_remove(struct platform_device *dev)
pm_runtime_disable(&dev->dev);
pm_runtime_put_noidle(&dev->dev);
pm_runtime_set_suspended(&dev->dev);
-
- return 0;
}
EXPORT_SYMBOL_GPL(xhci_plat_remove);
@@ -478,7 +467,7 @@ static int __maybe_unused xhci_plat_resume(struct device *dev)
if (ret)
return ret;
- ret = xhci_resume(xhci, 0);
+ ret = xhci_resume(xhci, PMSG_RESUME);
if (ret)
return ret;
@@ -507,7 +496,7 @@ static int __maybe_unused xhci_plat_runtime_resume(struct device *dev)
struct usb_hcd *hcd = dev_get_drvdata(dev);
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
- return xhci_resume(xhci, 0);
+ return xhci_resume(xhci, PMSG_AUTO_RESUME);
}
const struct dev_pm_ops xhci_plat_pm_ops = {
@@ -530,7 +519,7 @@ MODULE_DEVICE_TABLE(acpi, usb_xhci_acpi_match);
static struct platform_driver usb_generic_xhci_driver = {
.probe = xhci_generic_plat_probe,
- .remove = xhci_plat_remove,
+ .remove_new = xhci_plat_remove,
.shutdown = usb_hcd_platform_shutdown,
.driver = {
.name = "xhci-hcd",
diff --git a/drivers/usb/host/xhci-plat.h b/drivers/usb/host/xhci-plat.h
index 83b5b5aa9f8e..2d15386f2c50 100644
--- a/drivers/usb/host/xhci-plat.h
+++ b/drivers/usb/host/xhci-plat.h
@@ -25,7 +25,7 @@ struct xhci_plat_priv {
int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev,
const struct xhci_plat_priv *priv_match);
-int xhci_plat_remove(struct platform_device *dev);
+void xhci_plat_remove(struct platform_device *dev);
extern const struct dev_pm_ops xhci_plat_pm_ops;
#endif /* _XHCI_PLAT_H */
diff --git a/drivers/usb/host/xhci-rcar.c b/drivers/usb/host/xhci-rcar.c
index ad966b797b89..bf5261fed32c 100644
--- a/drivers/usb/host/xhci-rcar.c
+++ b/drivers/usb/host/xhci-rcar.c
@@ -276,10 +276,10 @@ static int xhci_renesas_probe(struct platform_device *pdev)
}
static struct platform_driver usb_xhci_renesas_driver = {
- .probe = xhci_renesas_probe,
- .remove = xhci_plat_remove,
+ .probe = xhci_renesas_probe,
+ .remove_new = xhci_plat_remove,
.shutdown = usb_hcd_platform_shutdown,
- .driver = {
+ .driver = {
.name = "xhci-renesas-hcd",
.pm = &xhci_plat_pm_ops,
.of_match_table = usb_xhci_of_match,
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 2bc82b3a2f98..646ff125def5 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -174,12 +174,10 @@ void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring)
/* All other rings have link trbs */
if (!trb_is_link(ring->dequeue)) {
- if (last_trb_on_seg(ring->deq_seg, ring->dequeue)) {
+ if (last_trb_on_seg(ring->deq_seg, ring->dequeue))
xhci_warn(xhci, "Missing link TRB at end of segment\n");
- } else {
+ else
ring->dequeue++;
- ring->num_trbs_free++;
- }
}
while (trb_is_link(ring->dequeue)) {
@@ -221,9 +219,6 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring,
unsigned int link_trb_count = 0;
chain = le32_to_cpu(ring->enqueue->generic.field[3]) & TRB_CHAIN;
- /* If this is not event ring, there is one less usable TRB */
- if (!trb_is_link(ring->enqueue))
- ring->num_trbs_free--;
if (last_trb_on_seg(ring->enq_seg, ring->enqueue)) {
xhci_err(xhci, "Tried to move enqueue past ring segment\n");
@@ -276,45 +271,84 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring,
trace_xhci_inc_enq(ring);
}
-static int xhci_num_trbs_to(struct xhci_segment *start_seg, union xhci_trb *start,
- struct xhci_segment *end_seg, union xhci_trb *end,
- unsigned int num_segs)
+/*
+ * Return number of free normal TRBs from enqueue to dequeue pointer on ring.
+ * Not counting an assumed link TRB at end of each TRBS_PER_SEGMENT sized segment.
+ * Only for transfer and command rings where driver is the producer, not for
+ * event rings.
+ */
+static unsigned int xhci_num_trbs_free(struct xhci_hcd *xhci, struct xhci_ring *ring)
{
+ struct xhci_segment *enq_seg = ring->enq_seg;
+ union xhci_trb *enq = ring->enqueue;
union xhci_trb *last_on_seg;
- int num = 0;
+ unsigned int free = 0;
int i = 0;
+ /* Ring might be empty even if enq != deq if enq is left on a link trb */
+ if (trb_is_link(enq)) {
+ enq_seg = enq_seg->next;
+ enq = enq_seg->trbs;
+ }
+
+ /* Empty ring, common case, don't walk the segments */
+ if (enq == ring->dequeue)
+ return ring->num_segs * (TRBS_PER_SEGMENT - 1);
+
do {
- if (start_seg == end_seg && end >= start)
- return num + (end - start);
- last_on_seg = &start_seg->trbs[TRBS_PER_SEGMENT - 1];
- num += last_on_seg - start;
- start_seg = start_seg->next;
- start = start_seg->trbs;
- } while (i++ <= num_segs);
-
- return -EINVAL;
+ if (ring->deq_seg == enq_seg && ring->dequeue >= enq)
+ return free + (ring->dequeue - enq);
+ last_on_seg = &enq_seg->trbs[TRBS_PER_SEGMENT - 1];
+ free += last_on_seg - enq;
+ enq_seg = enq_seg->next;
+ enq = enq_seg->trbs;
+ } while (i++ <= ring->num_segs);
+
+ return free;
}
/*
* Check to see if there's room to enqueue num_trbs on the ring and make sure
* enqueue pointer will not advance into dequeue segment. See rules above.
+ * return number of new segments needed to ensure this.
*/
-static inline int room_on_ring(struct xhci_hcd *xhci, struct xhci_ring *ring,
- unsigned int num_trbs)
+
+static unsigned int xhci_ring_expansion_needed(struct xhci_hcd *xhci, struct xhci_ring *ring,
+ unsigned int num_trbs)
{
- int num_trbs_in_deq_seg;
+ struct xhci_segment *seg;
+ int trbs_past_seg;
+ int enq_used;
+ int new_segs;
+
+ enq_used = ring->enqueue - ring->enq_seg->trbs;
+
+ /* how many trbs will be queued past the enqueue segment? */
+ trbs_past_seg = enq_used + num_trbs - (TRBS_PER_SEGMENT - 1);
- if (ring->num_trbs_free < num_trbs)
+ if (trbs_past_seg <= 0)
return 0;
- if (ring->type != TYPE_COMMAND && ring->type != TYPE_EVENT) {
- num_trbs_in_deq_seg = ring->dequeue - ring->deq_seg->trbs;
- if (ring->num_trbs_free < num_trbs + num_trbs_in_deq_seg)
- return 0;
+ /* Empty ring special case, enqueue stuck on link trb while dequeue advanced */
+ if (trb_is_link(ring->enqueue) && ring->enq_seg->next->trbs == ring->dequeue)
+ return 0;
+
+ new_segs = 1 + (trbs_past_seg / (TRBS_PER_SEGMENT - 1));
+ seg = ring->enq_seg;
+
+ while (new_segs > 0) {
+ seg = seg->next;
+ if (seg == ring->deq_seg) {
+ xhci_dbg(xhci, "Ring expansion by %d segments needed\n",
+ new_segs);
+ xhci_dbg(xhci, "Adding %d trbs moves enq %d trbs into deq seg\n",
+ num_trbs, trbs_past_seg % TRBS_PER_SEGMENT);
+ return new_segs;
+ }
+ new_segs--;
}
- return 1;
+ return 0;
}
/* Ring the host controller doorbell after placing a command on the ring */
@@ -1268,10 +1302,7 @@ static void update_ring_for_set_deq_completion(struct xhci_hcd *xhci,
unsigned int ep_index)
{
union xhci_trb *dequeue_temp;
- int num_trbs_free_temp;
- bool revert = false;
- num_trbs_free_temp = ep_ring->num_trbs_free;
dequeue_temp = ep_ring->dequeue;
/* If we get two back-to-back stalls, and the first stalled transfer
@@ -1287,7 +1318,6 @@ static void update_ring_for_set_deq_completion(struct xhci_hcd *xhci,
while (ep_ring->dequeue != dev->eps[ep_index].queued_deq_ptr) {
/* We have more usable TRBs */
- ep_ring->num_trbs_free++;
ep_ring->dequeue++;
if (trb_is_link(ep_ring->dequeue)) {
if (ep_ring->dequeue ==
@@ -1297,15 +1327,10 @@ static void update_ring_for_set_deq_completion(struct xhci_hcd *xhci,
ep_ring->dequeue = ep_ring->deq_seg->trbs;
}
if (ep_ring->dequeue == dequeue_temp) {
- revert = true;
+ xhci_dbg(xhci, "Unable to find new dequeue pointer\n");
break;
}
}
-
- if (revert) {
- xhci_dbg(xhci, "Unable to find new dequeue pointer\n");
- ep_ring->num_trbs_free = num_trbs_free_temp;
- }
}
/*
@@ -2160,7 +2185,6 @@ static int finish_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
u32 trb_comp_code)
{
struct xhci_ep_ctx *ep_ctx;
- int trbs_freed;
ep_ctx = xhci_get_ep_ctx(xhci, ep->vdev->out_ctx, ep->ep_index);
@@ -2230,13 +2254,6 @@ static int finish_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
}
/* Update ring dequeue pointer */
- trbs_freed = xhci_num_trbs_to(ep_ring->deq_seg, ep_ring->dequeue,
- td->last_trb_seg, td->last_trb,
- ep_ring->num_segs);
- if (trbs_freed < 0)
- xhci_dbg(xhci, "Failed to count freed trbs at TD finish\n");
- else
- ep_ring->num_trbs_free += trbs_freed;
ep_ring->dequeue = td->last_trb;
ep_ring->deq_seg = td->last_trb_seg;
inc_deq(xhci, ep_ring);
@@ -2460,7 +2477,6 @@ static int skip_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
/* Update ring dequeue pointer */
ep->ring->dequeue = td->last_trb;
ep->ring->deq_seg = td->last_trb_seg;
- ep->ring->num_trbs_free += td->num_trbs - 1;
inc_deq(xhci, ep->ring);
return xhci_td_cleanup(xhci, td, ep->ring, status);
@@ -3165,13 +3181,13 @@ static void queue_trb(struct xhci_hcd *xhci, struct xhci_ring *ring,
/*
* Does various checks on the endpoint ring, and makes it ready to queue num_trbs.
- * FIXME allocate segments if the ring is full.
+ * expand ring if it start to be full.
*/
static int prepare_ring(struct xhci_hcd *xhci, struct xhci_ring *ep_ring,
u32 ep_state, unsigned int num_trbs, gfp_t mem_flags)
{
- unsigned int num_trbs_needed;
unsigned int link_trb_count = 0;
+ unsigned int new_segs = 0;
/* Make sure the endpoint has been added to xHC schedule */
switch (ep_state) {
@@ -3202,20 +3218,17 @@ static int prepare_ring(struct xhci_hcd *xhci, struct xhci_ring *ep_ring,
return -EINVAL;
}
- while (1) {
- if (room_on_ring(xhci, ep_ring, num_trbs))
- break;
-
- if (ep_ring == xhci->cmd_ring) {
- xhci_err(xhci, "Do not support expand command ring\n");
- return -ENOMEM;
- }
+ if (ep_ring != xhci->cmd_ring) {
+ new_segs = xhci_ring_expansion_needed(xhci, ep_ring, num_trbs);
+ } else if (xhci_num_trbs_free(xhci, ep_ring) <= num_trbs) {
+ xhci_err(xhci, "Do not support expand command ring\n");
+ return -ENOMEM;
+ }
+ if (new_segs) {
xhci_dbg_trace(xhci, trace_xhci_dbg_ring_expansion,
"ERROR no room on ep ring, try ring expansion");
- num_trbs_needed = num_trbs - ep_ring->num_trbs_free;
- if (xhci_ring_expansion(xhci, ep_ring, num_trbs_needed,
- mem_flags)) {
+ if (xhci_ring_expansion(xhci, ep_ring, new_segs, mem_flags)) {
xhci_err(xhci, "Ring expansion failed\n");
return -ENOMEM;
}
@@ -4185,7 +4198,6 @@ cleanup:
ep_ring->enqueue = urb_priv->td[0].first_trb;
ep_ring->enq_seg = urb_priv->td[0].start_seg;
ep_ring->cycle_state = start_cycle;
- ep_ring->num_trbs_free = ep_ring->num_trbs_free_temp;
usb_hcd_unlink_urb_from_ep(bus_to_hcd(urb->dev->bus), urb);
return ret;
}
@@ -4267,7 +4279,6 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags,
}
skip_start_over:
- ep_ring->num_trbs_free_temp = ep_ring->num_trbs_free;
return xhci_queue_isoc_tx(xhci, mem_flags, urb, slot_id, ep_index);
}
diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c
index c75d93244143..6ca8a37e53e1 100644
--- a/drivers/usb/host/xhci-tegra.c
+++ b/drivers/usb/host/xhci-tegra.c
@@ -1828,6 +1828,9 @@ static int tegra_xusb_probe(struct platform_device *pdev)
goto remove_usb2;
}
+ if (HCC_MAX_PSA(xhci->hcc_params) >= 4)
+ xhci->shared_hcd->can_do_streams = 1;
+
err = usb_add_hcd(xhci->shared_hcd, tegra->xhci_irq, IRQF_SHARED);
if (err < 0) {
dev_err(&pdev->dev, "failed to add shared HCD: %d\n", err);
@@ -1909,7 +1912,7 @@ put_padctl:
return err;
}
-static int tegra_xusb_remove(struct platform_device *pdev)
+static void tegra_xusb_remove(struct platform_device *pdev)
{
struct tegra_xusb *tegra = platform_get_drvdata(pdev);
struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd);
@@ -1939,8 +1942,6 @@ static int tegra_xusb_remove(struct platform_device *pdev)
tegra_xusb_clk_disable(tegra);
regulator_bulk_disable(tegra->soc->num_supplies, tegra->supplies);
tegra_xusb_padctl_put(tegra->padctl);
-
- return 0;
}
static bool xhci_hub_ports_suspended(struct xhci_hub *hub)
@@ -2272,7 +2273,7 @@ static int tegra_xusb_exit_elpg(struct tegra_xusb *tegra, bool runtime)
if (wakeup)
tegra_xhci_disable_phy_sleepwalk(tegra);
- err = xhci_resume(xhci, 0);
+ err = xhci_resume(xhci, runtime ? PMSG_AUTO_RESUME : PMSG_RESUME);
if (err < 0) {
dev_err(tegra->dev, "failed to resume XHCI: %d\n", err);
goto disable_phy;
@@ -2650,7 +2651,7 @@ MODULE_DEVICE_TABLE(of, tegra_xusb_of_match);
static struct platform_driver tegra_xusb_driver = {
.probe = tegra_xusb_probe,
- .remove = tegra_xusb_remove,
+ .remove_new = tegra_xusb_remove,
.driver = {
.name = "tegra-xusb",
.pm = &tegra_xusb_pm_ops,
@@ -2662,7 +2663,6 @@ static void tegra_xhci_quirks(struct device *dev, struct xhci_hcd *xhci)
{
struct tegra_xusb *tegra = dev_get_drvdata(dev);
- xhci->quirks |= XHCI_PLAT;
if (tegra && tegra->soc->lpm_support)
xhci->quirks |= XHCI_LPM_SUPPORT;
}
diff --git a/drivers/usb/host/xhci-trace.h b/drivers/usb/host/xhci-trace.h
index 4286dba5b157..d6b32f2ad90e 100644
--- a/drivers/usb/host/xhci-trace.h
+++ b/drivers/usb/host/xhci-trace.h
@@ -80,20 +80,16 @@ DECLARE_EVENT_CLASS(xhci_log_ctx,
__field(dma_addr_t, ctx_dma)
__field(u8 *, ctx_va)
__field(unsigned, ctx_ep_num)
- __field(int, slot_id)
__dynamic_array(u32, ctx_data,
((HCC_64BYTE_CONTEXT(xhci->hcc_params) + 1) * 8) *
((ctx->type == XHCI_CTX_TYPE_INPUT) + ep_num + 1))
),
TP_fast_assign(
- struct usb_device *udev;
- udev = to_usb_device(xhci_to_hcd(xhci)->self.controller);
__entry->ctx_64 = HCC_64BYTE_CONTEXT(xhci->hcc_params);
__entry->ctx_type = ctx->type;
__entry->ctx_dma = ctx->dma;
__entry->ctx_va = ctx->bytes;
- __entry->slot_id = udev->slot_id;
__entry->ctx_ep_num = ep_num;
memcpy(__get_dynamic_array(ctx_data), ctx->bytes,
((HCC_64BYTE_CONTEXT(xhci->hcc_params) + 1) * 32) *
@@ -462,7 +458,6 @@ DECLARE_EVENT_CLASS(xhci_log_ring,
__field(unsigned int, num_segs)
__field(unsigned int, stream_id)
__field(unsigned int, cycle_state)
- __field(unsigned int, num_trbs_free)
__field(unsigned int, bounce_buf_len)
),
TP_fast_assign(
@@ -473,18 +468,16 @@ DECLARE_EVENT_CLASS(xhci_log_ring,
__entry->enq_seg = ring->enq_seg->dma;
__entry->deq_seg = ring->deq_seg->dma;
__entry->cycle_state = ring->cycle_state;
- __entry->num_trbs_free = ring->num_trbs_free;
__entry->bounce_buf_len = ring->bounce_buf_len;
__entry->enq = xhci_trb_virt_to_dma(ring->enq_seg, ring->enqueue);
__entry->deq = xhci_trb_virt_to_dma(ring->deq_seg, ring->dequeue);
),
- TP_printk("%s %p: enq %pad(%pad) deq %pad(%pad) segs %d stream %d free_trbs %d bounce %d cycle %d",
+ TP_printk("%s %p: enq %pad(%pad) deq %pad(%pad) segs %d stream %d bounce %d cycle %d",
xhci_ring_type_string(__entry->type), __entry->ring,
&__entry->enq, &__entry->enq_seg,
&__entry->deq, &__entry->deq_seg,
__entry->num_segs,
__entry->stream_id,
- __entry->num_trbs_free,
__entry->bounce_buf_len,
__entry->cycle_state
)
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 78790dc13c5f..fae994f679d4 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -833,7 +833,7 @@ static bool xhci_pending_portevent(struct xhci_hcd *xhci)
ports = xhci->usb3_rhub.ports;
while (port_index--) {
portsc = readl(ports[port_index]->addr);
- if (portsc & PORT_CHANGE_MASK ||
+ if (portsc & (PORT_CHANGE_MASK | PORT_CAS) ||
(portsc & PORT_PLS_MASK) == XDEV_RESUME)
return true;
}
@@ -960,8 +960,9 @@ EXPORT_SYMBOL_GPL(xhci_suspend);
* This is called when the machine transition from S3/S4 mode.
*
*/
-int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
+int xhci_resume(struct xhci_hcd *xhci, pm_message_t msg)
{
+ bool hibernated = (msg.event == PM_EVENT_RESTORE);
u32 command, temp = 0;
struct usb_hcd *hcd = xhci_to_hcd(xhci);
int retval = 0;
@@ -1027,7 +1028,8 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
temp = readl(&xhci->op_regs->status);
/* re-initialize the HC on Restore Error, or Host Controller Error */
- if (temp & (STS_SRE | STS_HCE)) {
+ if ((temp & (STS_SRE | STS_HCE)) &&
+ !(xhci->xhc_state & XHCI_STATE_REMOVING)) {
reinit_xhc = true;
if (!xhci->broken_suspend)
xhci_warn(xhci, "xHC error in resume, USBSTS 0x%x, Reinit\n", temp);
@@ -1116,7 +1118,7 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
* the first wake signalling failed, give it that chance.
*/
pending_portevent = xhci_pending_portevent(xhci);
- if (!pending_portevent) {
+ if (!pending_portevent && msg.event == PM_EVENT_AUTO_RESUME) {
msleep(120);
pending_portevent = xhci_pending_portevent(xhci);
}
@@ -4604,7 +4606,7 @@ static u16 xhci_calculate_u1_timeout(struct xhci_hcd *xhci,
}
}
- if (xhci->quirks & XHCI_INTEL_HOST)
+ if (xhci->quirks & (XHCI_INTEL_HOST | XHCI_ZHAOXIN_HOST))
timeout_ns = xhci_calculate_intel_u1_timeout(udev, desc);
else
timeout_ns = udev->u1_params.sel;
@@ -4668,7 +4670,7 @@ static u16 xhci_calculate_u2_timeout(struct xhci_hcd *xhci,
}
}
- if (xhci->quirks & XHCI_INTEL_HOST)
+ if (xhci->quirks & (XHCI_INTEL_HOST | XHCI_ZHAOXIN_HOST))
timeout_ns = xhci_calculate_intel_u2_timeout(udev, desc);
else
timeout_ns = udev->u2_params.sel;
@@ -4740,37 +4742,30 @@ static int xhci_update_timeout_for_interface(struct xhci_hcd *xhci,
return 0;
}
-static int xhci_check_intel_tier_policy(struct usb_device *udev,
+static int xhci_check_tier_policy(struct xhci_hcd *xhci,
+ struct usb_device *udev,
enum usb3_link_state state)
{
- struct usb_device *parent;
- unsigned int num_hubs;
+ struct usb_device *parent = udev->parent;
+ int tier = 1; /* roothub is tier1 */
- /* Don't enable U1 if the device is on a 2nd tier hub or lower. */
- for (parent = udev->parent, num_hubs = 0; parent->parent;
- parent = parent->parent)
- num_hubs++;
+ while (parent) {
+ parent = parent->parent;
+ tier++;
+ }
- if (num_hubs < 2)
- return 0;
+ if (xhci->quirks & XHCI_INTEL_HOST && tier > 3)
+ goto fail;
+ if (xhci->quirks & XHCI_ZHAOXIN_HOST && tier > 2)
+ goto fail;
- dev_dbg(&udev->dev, "Disabling U1/U2 link state for device"
- " below second-tier hub.\n");
- dev_dbg(&udev->dev, "Plug device into first-tier hub "
- "to decrease power consumption.\n");
+ return 0;
+fail:
+ dev_dbg(&udev->dev, "Tier policy prevents U1/U2 LPM states for devices at tier %d\n",
+ tier);
return -E2BIG;
}
-static int xhci_check_tier_policy(struct xhci_hcd *xhci,
- struct usb_device *udev,
- enum usb3_link_state state)
-{
- if (xhci->quirks & XHCI_INTEL_HOST)
- return xhci_check_intel_tier_policy(udev, state);
- else
- return 0;
-}
-
/* Returns the U1 or U2 timeout that should be enabled.
* If the tier check or timeout setting functions return with a non-zero exit
* code, that means the timeout value has been finalized and we shouldn't look
@@ -5180,7 +5175,8 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
xhci->quirks |= quirks;
- get_quirks(dev, xhci);
+ if (get_quirks)
+ get_quirks(dev, xhci);
/* In xhci controllers which follow xhci 1.0 spec gives a spurious
* success event after a short transfer. This quirk will ignore such
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 6b690ec91ff3..7e282b4522c0 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1633,8 +1633,7 @@ struct xhci_ring {
u32 cycle_state;
unsigned int stream_id;
unsigned int num_segs;
- unsigned int num_trbs_free;
- unsigned int num_trbs_free_temp;
+ unsigned int num_trbs_free; /* used only by xhci DbC */
unsigned int bounce_buf_len;
enum xhci_ring_type type;
bool last_td_was_short;
@@ -1874,7 +1873,7 @@ struct xhci_hcd {
#define XHCI_SPURIOUS_REBOOT BIT_ULL(13)
#define XHCI_COMP_MODE_QUIRK BIT_ULL(14)
#define XHCI_AVOID_BEI BIT_ULL(15)
-#define XHCI_PLAT BIT_ULL(16)
+#define XHCI_PLAT BIT_ULL(16) /* Deprecated */
#define XHCI_SLOW_SUSPEND BIT_ULL(17)
#define XHCI_SPURIOUS_WAKEUP BIT_ULL(18)
/* For controllers with a broken beyond repair streams implementation */
@@ -1905,6 +1904,8 @@ struct xhci_hcd {
#define XHCI_EP_CTX_BROKEN_DCS BIT_ULL(42)
#define XHCI_SUSPEND_RESUME_CLKS BIT_ULL(43)
#define XHCI_RESET_TO_DEFAULT BIT_ULL(44)
+#define XHCI_ZHAOXIN_TRB_FETCH BIT_ULL(45)
+#define XHCI_ZHAOXIN_HOST BIT_ULL(46)
unsigned int num_active_eps;
unsigned int limit_active_eps;
@@ -2140,7 +2141,7 @@ int xhci_disable_slot(struct xhci_hcd *xhci, u32 slot_id);
int xhci_ext_cap_init(struct xhci_hcd *xhci);
int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup);
-int xhci_resume(struct xhci_hcd *xhci, bool hibernated);
+int xhci_resume(struct xhci_hcd *xhci, pm_message_t msg);
irqreturn_t xhci_irq(struct usb_hcd *hcd);
irqreturn_t xhci_msi_irq(int irq, void *hcd);
diff --git a/drivers/usb/isp1760/isp1760-if.c b/drivers/usb/isp1760/isp1760-if.c
index 65ba5aca2a4f..fe1e3985419a 100644
--- a/drivers/usb/isp1760/isp1760-if.c
+++ b/drivers/usb/isp1760/isp1760-if.c
@@ -246,11 +246,9 @@ static int isp1760_plat_probe(struct platform_device *pdev)
return 0;
}
-static int isp1760_plat_remove(struct platform_device *pdev)
+static void isp1760_plat_remove(struct platform_device *pdev)
{
isp1760_unregister(&pdev->dev);
-
- return 0;
}
#ifdef CONFIG_OF
@@ -265,7 +263,7 @@ MODULE_DEVICE_TABLE(of, isp1760_of_match);
static struct platform_driver isp1760_plat_driver = {
.probe = isp1760_plat_probe,
- .remove = isp1760_plat_remove,
+ .remove_new = isp1760_plat_remove,
.driver = {
.name = "isp1760",
.of_match_table = of_match_ptr(isp1760_of_match),
diff --git a/drivers/usb/misc/onboard_usb_hub.c b/drivers/usb/misc/onboard_usb_hub.c
index 12fc6eb67c3b..83f14ca1d38e 100644
--- a/drivers/usb/misc/onboard_usb_hub.c
+++ b/drivers/usb/misc/onboard_usb_hub.c
@@ -278,7 +278,7 @@ static int onboard_hub_probe(struct platform_device *pdev)
return 0;
}
-static int onboard_hub_remove(struct platform_device *pdev)
+static void onboard_hub_remove(struct platform_device *pdev)
{
struct onboard_hub *hub = dev_get_drvdata(&pdev->dev);
struct usbdev_node *node;
@@ -306,7 +306,7 @@ static int onboard_hub_remove(struct platform_device *pdev)
mutex_unlock(&hub->lock);
- return onboard_hub_power_off(hub);
+ onboard_hub_power_off(hub);
}
MODULE_DEVICE_TABLE(of, onboard_hub_match);
@@ -317,7 +317,7 @@ static const struct dev_pm_ops __maybe_unused onboard_hub_pm_ops = {
static struct platform_driver onboard_hub_driver = {
.probe = onboard_hub_probe,
- .remove = onboard_hub_remove,
+ .remove_new = onboard_hub_remove,
.driver = {
.name = "onboard-usb-hub",
diff --git a/drivers/usb/misc/qcom_eud.c b/drivers/usb/misc/qcom_eud.c
index b7f13df00764..7f371ea1248c 100644
--- a/drivers/usb/misc/qcom_eud.c
+++ b/drivers/usb/misc/qcom_eud.c
@@ -22,10 +22,10 @@
#define EUD_REG_VBUS_INT_CLR 0x0080
#define EUD_REG_CSR_EUD_EN 0x1014
#define EUD_REG_SW_ATTACH_DET 0x1018
-#define EUD_REG_EUD_EN2 0x0000
+#define EUD_REG_EUD_EN2 0x0000
#define EUD_ENABLE BIT(0)
-#define EUD_INT_PET_EUD BIT(0)
+#define EUD_INT_PET_EUD BIT(0)
#define EUD_INT_VBUS BIT(2)
#define EUD_INT_SAFE_MODE BIT(4)
#define EUD_INT_ALL (EUD_INT_VBUS | EUD_INT_SAFE_MODE)
@@ -217,7 +217,7 @@ static int eud_probe(struct platform_device *pdev)
return 0;
}
-static int eud_remove(struct platform_device *pdev)
+static void eud_remove(struct platform_device *pdev)
{
struct eud_chip *chip = platform_get_drvdata(pdev);
@@ -226,8 +226,6 @@ static int eud_remove(struct platform_device *pdev)
device_init_wakeup(&pdev->dev, false);
disable_irq_wake(chip->irq);
-
- return 0;
}
static const struct of_device_id eud_dt_match[] = {
@@ -238,7 +236,7 @@ MODULE_DEVICE_TABLE(of, eud_dt_match);
static struct platform_driver eud_driver = {
.probe = eud_probe,
- .remove = eud_remove,
+ .remove_new = eud_remove,
.driver = {
.name = "qcom_eud",
.dev_groups = eud_groups,
diff --git a/drivers/usb/misc/usb251xb.c b/drivers/usb/misc/usb251xb.c
index ce1da80d3365..e4edb486b69e 100644
--- a/drivers/usb/misc/usb251xb.c
+++ b/drivers/usb/misc/usb251xb.c
@@ -416,14 +416,13 @@ static int usb251xb_get_ofdata(struct usb251xb *hub,
return dev_err_probe(dev, PTR_ERR(hub->gpio_reset),
"unable to request GPIO reset pin\n");
- if (of_property_read_u16_array(np, "vendor-id", &hub->vendor_id, 1))
+ if (of_property_read_u16(np, "vendor-id", &hub->vendor_id))
hub->vendor_id = USB251XB_DEF_VENDOR_ID;
- if (of_property_read_u16_array(np, "product-id",
- &hub->product_id, 1))
+ if (of_property_read_u16(np, "product-id", &hub->product_id))
hub->product_id = data->product_id;
- if (of_property_read_u16_array(np, "device-id", &hub->device_id, 1))
+ if (of_property_read_u16(np, "device-id", &hub->device_id))
hub->device_id = USB251XB_DEF_DEVICE_ID;
hub->conf_data1 = USB251XB_DEF_CONFIG_DATA_1;
@@ -532,7 +531,7 @@ static int usb251xb_get_ofdata(struct usb251xb *hub,
if (!of_property_read_u32(np, "power-on-time-ms", &property_u32))
hub->power_on_time = min_t(u8, property_u32 / 2, 255);
- if (of_property_read_u16_array(np, "language-id", &hub->lang_id, 1))
+ if (of_property_read_u16(np, "language-id", &hub->lang_id))
hub->lang_id = USB251XB_DEF_LANGUAGE_ID;
if (of_property_read_u8(np, "boost-up", &hub->boost_up))
@@ -746,7 +745,7 @@ static struct i2c_driver usb251xb_i2c_driver = {
.of_match_table = usb251xb_of_match,
.pm = &usb251xb_pm_ops,
},
- .probe_new = usb251xb_i2c_probe,
+ .probe = usb251xb_i2c_probe,
.id_table = usb251xb_id,
};
diff --git a/drivers/usb/misc/usb3503.c b/drivers/usb/misc/usb3503.c
index c6cfd1edaf76..72765077932c 100644
--- a/drivers/usb/misc/usb3503.c
+++ b/drivers/usb/misc/usb3503.c
@@ -335,14 +335,12 @@ static int usb3503_platform_probe(struct platform_device *pdev)
return usb3503_probe(hub);
}
-static int usb3503_platform_remove(struct platform_device *pdev)
+static void usb3503_platform_remove(struct platform_device *pdev)
{
struct usb3503 *hub;
hub = platform_get_drvdata(pdev);
clk_disable_unprepare(hub->clk);
-
- return 0;
}
static int __maybe_unused usb3503_suspend(struct usb3503 *hub)
@@ -413,7 +411,7 @@ static struct i2c_driver usb3503_i2c_driver = {
.pm = pm_ptr(&usb3503_i2c_pm_ops),
.of_match_table = of_match_ptr(usb3503_of_match),
},
- .probe_new = usb3503_i2c_probe,
+ .probe = usb3503_i2c_probe,
.remove = usb3503_i2c_remove,
.id_table = usb3503_id,
};
@@ -425,7 +423,7 @@ static struct platform_driver usb3503_platform_driver = {
.pm = pm_ptr(&usb3503_platform_pm_ops),
},
.probe = usb3503_platform_probe,
- .remove = usb3503_platform_remove,
+ .remove_new = usb3503_platform_remove,
};
static int __init usb3503_init(void)
diff --git a/drivers/usb/misc/usb4604.c b/drivers/usb/misc/usb4604.c
index 6b5e77231efa..065e269ba4e3 100644
--- a/drivers/usb/misc/usb4604.c
+++ b/drivers/usb/misc/usb4604.c
@@ -154,7 +154,7 @@ static struct i2c_driver usb4604_i2c_driver = {
.pm = pm_ptr(&usb4604_i2c_pm_ops),
.of_match_table = of_match_ptr(usb4604_of_match),
},
- .probe_new = usb4604_i2c_probe,
+ .probe = usb4604_i2c_probe,
.id_table = usb4604_id,
};
module_i2c_driver(usb4604_i2c_driver);
diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c
index 952c56789258..9ca9305243fe 100644
--- a/drivers/usb/mon/mon_bin.c
+++ b/drivers/usb/mon/mon_bin.c
@@ -213,7 +213,10 @@ static unsigned char xfer_to_pipe[4] = {
PIPE_CONTROL, PIPE_ISOCHRONOUS, PIPE_BULK, PIPE_INTERRUPT
};
-static struct class *mon_bin_class;
+static const struct class mon_bin_class = {
+ .name = "usbmon",
+};
+
static dev_t mon_bin_dev0;
static struct cdev mon_bin_cdev;
@@ -1360,7 +1363,7 @@ int mon_bin_add(struct mon_bus *mbus, const struct usb_bus *ubus)
if (minor >= MON_BIN_MAX_MINOR)
return 0;
- dev = device_create(mon_bin_class, ubus ? ubus->controller : NULL,
+ dev = device_create(&mon_bin_class, ubus ? ubus->controller : NULL,
MKDEV(MAJOR(mon_bin_dev0), minor), NULL,
"usbmon%d", minor);
if (IS_ERR(dev))
@@ -1372,18 +1375,16 @@ int mon_bin_add(struct mon_bus *mbus, const struct usb_bus *ubus)
void mon_bin_del(struct mon_bus *mbus)
{
- device_destroy(mon_bin_class, mbus->classdev->devt);
+ device_destroy(&mon_bin_class, mbus->classdev->devt);
}
int __init mon_bin_init(void)
{
int rc;
- mon_bin_class = class_create("usbmon");
- if (IS_ERR(mon_bin_class)) {
- rc = PTR_ERR(mon_bin_class);
+ rc = class_register(&mon_bin_class);
+ if (rc)
goto err_class;
- }
rc = alloc_chrdev_region(&mon_bin_dev0, 0, MON_BIN_MAX_MINOR, "usbmon");
if (rc < 0)
@@ -1401,7 +1402,7 @@ int __init mon_bin_init(void)
err_add:
unregister_chrdev_region(mon_bin_dev0, MON_BIN_MAX_MINOR);
err_dev:
- class_destroy(mon_bin_class);
+ class_unregister(&mon_bin_class);
err_class:
return rc;
}
@@ -1410,5 +1411,5 @@ void mon_bin_exit(void)
{
cdev_del(&mon_bin_cdev);
unregister_chrdev_region(mon_bin_dev0, MON_BIN_MAX_MINOR);
- class_destroy(mon_bin_class);
+ class_unregister(&mon_bin_class);
}
diff --git a/drivers/usb/phy/phy-isp1301.c b/drivers/usb/phy/phy-isp1301.c
index f4ee14d98585..993d7525a102 100644
--- a/drivers/usb/phy/phy-isp1301.c
+++ b/drivers/usb/phy/phy-isp1301.c
@@ -132,7 +132,7 @@ static struct i2c_driver isp1301_driver = {
.name = DRV_NAME,
.of_match_table = isp1301_of_match,
},
- .probe_new = isp1301_probe,
+ .probe = isp1301_probe,
.remove = isp1301_remove,
.id_table = isp1301_id,
};
diff --git a/drivers/usb/phy/phy-tahvo.c b/drivers/usb/phy/phy-tahvo.c
index 47562d49dfc1..5cac31c6029b 100644
--- a/drivers/usb/phy/phy-tahvo.c
+++ b/drivers/usb/phy/phy-tahvo.c
@@ -391,7 +391,7 @@ static int tahvo_usb_probe(struct platform_device *pdev)
tu->irq = ret = platform_get_irq(pdev, 0);
if (ret < 0)
- return ret;
+ goto err_remove_phy;
ret = request_threaded_irq(tu->irq, NULL, tahvo_usb_vbus_interrupt,
IRQF_ONESHOT,
"tahvo-vbus", tu);
diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c
index fa34efabcccf..111b7ee152c4 100644
--- a/drivers/usb/renesas_usbhs/common.c
+++ b/drivers/usb/renesas_usbhs/common.c
@@ -762,7 +762,7 @@ probe_end_pipe_exit:
return ret;
}
-static int usbhs_remove(struct platform_device *pdev)
+static void usbhs_remove(struct platform_device *pdev)
{
struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev);
@@ -780,8 +780,6 @@ static int usbhs_remove(struct platform_device *pdev)
usbhs_mod_remove(priv);
usbhs_fifo_remove(priv);
usbhs_pipe_remove(priv);
-
- return 0;
}
static __maybe_unused int usbhsc_suspend(struct device *dev)
@@ -826,7 +824,7 @@ static struct platform_driver renesas_usbhs_driver = {
.of_match_table = usbhs_of_match,
},
.probe = usbhs_probe,
- .remove = usbhs_remove,
+ .remove_new = usbhs_remove,
};
module_platform_driver(renesas_usbhs_driver);
diff --git a/drivers/usb/roles/class.c b/drivers/usb/roles/class.c
index 0395bd5dbd3e..ae41578bd014 100644
--- a/drivers/usb/roles/class.c
+++ b/drivers/usb/roles/class.c
@@ -14,7 +14,9 @@
#include <linux/mutex.h>
#include <linux/slab.h>
-static struct class *role_class;
+static const struct class role_class = {
+ .name = "usb_role",
+};
struct usb_role_switch {
struct device dev;
@@ -95,7 +97,7 @@ static void *usb_role_switch_match(const struct fwnode_handle *fwnode, const cha
if (id && !fwnode_property_present(fwnode, id))
return NULL;
- dev = class_find_device_by_fwnode(role_class, fwnode);
+ dev = class_find_device_by_fwnode(&role_class, fwnode);
return dev ? to_role_switch(dev) : ERR_PTR(-EPROBE_DEFER);
}
@@ -111,7 +113,7 @@ usb_role_switch_is_parent(struct fwnode_handle *fwnode)
return NULL;
}
- dev = class_find_device_by_fwnode(role_class, parent);
+ dev = class_find_device_by_fwnode(&role_class, parent);
fwnode_handle_put(parent);
return dev ? to_role_switch(dev) : ERR_PTR(-EPROBE_DEFER);
}
@@ -191,7 +193,7 @@ usb_role_switch_find_by_fwnode(const struct fwnode_handle *fwnode)
if (!fwnode)
return NULL;
- dev = class_find_device_by_fwnode(role_class, fwnode);
+ dev = class_find_device_by_fwnode(&role_class, fwnode);
if (dev)
WARN_ON(!try_module_get(dev->parent->driver->owner));
@@ -338,7 +340,7 @@ usb_role_switch_register(struct device *parent,
sw->dev.parent = parent;
sw->dev.fwnode = desc->fwnode;
- sw->dev.class = role_class;
+ sw->dev.class = &role_class;
sw->dev.type = &usb_role_dev_type;
dev_set_drvdata(&sw->dev, desc->driver_data);
dev_set_name(&sw->dev, "%s-role-switch",
@@ -392,14 +394,13 @@ EXPORT_SYMBOL_GPL(usb_role_switch_get_drvdata);
static int __init usb_roles_init(void)
{
- role_class = class_create("usb_role");
- return PTR_ERR_OR_ZERO(role_class);
+ return class_register(&role_class);
}
subsys_initcall(usb_roles_init);
static void __exit usb_roles_exit(void)
{
- class_destroy(role_class);
+ class_unregister(&role_class);
}
module_exit(usb_roles_exit);
diff --git a/drivers/usb/roles/intel-xhci-usb-role-switch.c b/drivers/usb/roles/intel-xhci-usb-role-switch.c
index 5c96e929acea..e5c6c413a075 100644
--- a/drivers/usb/roles/intel-xhci-usb-role-switch.c
+++ b/drivers/usb/roles/intel-xhci-usb-role-switch.c
@@ -195,7 +195,7 @@ static int intel_xhci_usb_probe(struct platform_device *pdev)
return 0;
}
-static int intel_xhci_usb_remove(struct platform_device *pdev)
+static void intel_xhci_usb_remove(struct platform_device *pdev)
{
struct intel_xhci_usb_data *data = platform_get_drvdata(pdev);
@@ -203,8 +203,6 @@ static int intel_xhci_usb_remove(struct platform_device *pdev)
usb_role_switch_unregister(data->role_sw);
fwnode_handle_put(software_node_fwnode(&intel_xhci_usb_node));
-
- return 0;
}
static const struct platform_device_id intel_xhci_usb_table[] = {
@@ -219,7 +217,7 @@ static struct platform_driver intel_xhci_usb_driver = {
},
.id_table = intel_xhci_usb_table,
.probe = intel_xhci_usb_probe,
- .remove = intel_xhci_usb_remove,
+ .remove_new = intel_xhci_usb_remove,
};
module_platform_driver(intel_xhci_usb_driver);
diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c
index 9452291f1703..67a07cc007f0 100644
--- a/drivers/usb/serial/ark3116.c
+++ b/drivers/usb/serial/ark3116.c
@@ -433,10 +433,11 @@ static int ark3116_tiocmset(struct tty_struct *tty,
return 0;
}
-static void ark3116_break_ctl(struct tty_struct *tty, int break_state)
+static int ark3116_break_ctl(struct tty_struct *tty, int break_state)
{
struct usb_serial_port *port = tty->driver_data;
struct ark3116_private *priv = usb_get_serial_port_data(port);
+ int ret;
/* LCR is also used for other things: protect access */
mutex_lock(&priv->hw_lock);
@@ -446,9 +447,11 @@ static void ark3116_break_ctl(struct tty_struct *tty, int break_state)
else
priv->lcr &= ~UART_LCR_SBC;
- ark3116_write_reg(port->serial, UART_LCR, priv->lcr);
+ ret = ark3116_write_reg(port->serial, UART_LCR, priv->lcr);
mutex_unlock(&priv->hw_lock);
+
+ return ret;
}
static void ark3116_update_msr(struct usb_serial_port *port, __u8 msr)
diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c
index 9331a562dac0..cf47ee4ae5d3 100644
--- a/drivers/usb/serial/belkin_sa.c
+++ b/drivers/usb/serial/belkin_sa.c
@@ -46,7 +46,7 @@ static void belkin_sa_process_read_urb(struct urb *urb);
static void belkin_sa_set_termios(struct tty_struct *tty,
struct usb_serial_port *port,
const struct ktermios *old_termios);
-static void belkin_sa_break_ctl(struct tty_struct *tty, int break_state);
+static int belkin_sa_break_ctl(struct tty_struct *tty, int break_state);
static int belkin_sa_tiocmget(struct tty_struct *tty);
static int belkin_sa_tiocmset(struct tty_struct *tty,
unsigned int set, unsigned int clear);
@@ -399,13 +399,19 @@ static void belkin_sa_set_termios(struct tty_struct *tty,
spin_unlock_irqrestore(&priv->lock, flags);
}
-static void belkin_sa_break_ctl(struct tty_struct *tty, int break_state)
+static int belkin_sa_break_ctl(struct tty_struct *tty, int break_state)
{
struct usb_serial_port *port = tty->driver_data;
struct usb_serial *serial = port->serial;
+ int ret;
- if (BSA_USB_CMD(BELKIN_SA_SET_BREAK_REQUEST, break_state ? 1 : 0) < 0)
+ ret = BSA_USB_CMD(BELKIN_SA_SET_BREAK_REQUEST, break_state ? 1 : 0);
+ if (ret < 0) {
dev_err(&port->dev, "Set break_ctl %d\n", break_state);
+ return ret;
+ }
+
+ return 0;
}
static int belkin_sa_tiocmget(struct tty_struct *tty)
diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c
index 6e1b87e67304..612bea504d7a 100644
--- a/drivers/usb/serial/ch341.c
+++ b/drivers/usb/serial/ch341.c
@@ -562,12 +562,12 @@ static void ch341_set_termios(struct tty_struct *tty,
* TCSBRKP. Due to how the simulation is implemented the duration can't be
* controlled. The duration is always about (1s / 46bd * 9bit) = 196ms.
*/
-static void ch341_simulate_break(struct tty_struct *tty, int break_state)
+static int ch341_simulate_break(struct tty_struct *tty, int break_state)
{
struct usb_serial_port *port = tty->driver_data;
struct ch341_private *priv = usb_get_serial_port_data(port);
unsigned long now, delay;
- int r;
+ int r, r2;
if (break_state != 0) {
dev_dbg(&port->dev, "enter break state requested\n");
@@ -599,7 +599,7 @@ static void ch341_simulate_break(struct tty_struct *tty, int break_state)
*/
priv->break_end = jiffies + (11 * HZ / CH341_MIN_BPS);
- return;
+ return 0;
}
dev_dbg(&port->dev, "leave break state requested\n");
@@ -615,17 +615,22 @@ static void ch341_simulate_break(struct tty_struct *tty, int break_state)
schedule_timeout_interruptible(delay);
}
+ r = 0;
restore:
/* Restore original baud rate */
- r = ch341_set_baudrate_lcr(port->serial->dev, priv, priv->baud_rate,
- priv->lcr);
- if (r < 0)
+ r2 = ch341_set_baudrate_lcr(port->serial->dev, priv, priv->baud_rate,
+ priv->lcr);
+ if (r2 < 0) {
dev_err(&port->dev,
"restoring original baud rate of %u failed: %d\n",
- priv->baud_rate, r);
+ priv->baud_rate, r2);
+ return r2;
+ }
+
+ return r;
}
-static void ch341_break_ctl(struct tty_struct *tty, int break_state)
+static int ch341_break_ctl(struct tty_struct *tty, int break_state)
{
const uint16_t ch341_break_reg =
((uint16_t) CH341_REG_LCR << 8) | CH341_REG_BREAK;
@@ -635,17 +640,17 @@ static void ch341_break_ctl(struct tty_struct *tty, int break_state)
uint16_t reg_contents;
uint8_t break_reg[2];
- if (priv->quirks & CH341_QUIRK_SIMULATE_BREAK) {
- ch341_simulate_break(tty, break_state);
- return;
- }
+ if (priv->quirks & CH341_QUIRK_SIMULATE_BREAK)
+ return ch341_simulate_break(tty, break_state);
r = ch341_control_in(port->serial->dev, CH341_REQ_READ_REG,
ch341_break_reg, 0, break_reg, 2);
if (r) {
dev_err(&port->dev, "%s - USB control read error (%d)\n",
__func__, r);
- return;
+ if (r > 0)
+ r = -EIO;
+ return r;
}
dev_dbg(&port->dev, "%s - initial ch341 break register contents - reg1: %x, reg2: %x\n",
__func__, break_reg[0], break_reg[1]);
@@ -663,9 +668,13 @@ static void ch341_break_ctl(struct tty_struct *tty, int break_state)
reg_contents = get_unaligned_le16(break_reg);
r = ch341_control_out(port->serial->dev, CH341_REQ_WRITE_REG,
ch341_break_reg, reg_contents);
- if (r < 0)
+ if (r < 0) {
dev_err(&port->dev, "%s - USB control write error (%d)\n",
__func__, r);
+ return r;
+ }
+
+ return 0;
}
static int ch341_tiocmset(struct tty_struct *tty,
diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c
index cdea1bff3b70..1e61fe043171 100644
--- a/drivers/usb/serial/cp210x.c
+++ b/drivers/usb/serial/cp210x.c
@@ -39,7 +39,7 @@ static int cp210x_tiocmget(struct tty_struct *);
static int cp210x_tiocmset(struct tty_struct *, unsigned int, unsigned int);
static int cp210x_tiocmset_port(struct usb_serial_port *port,
unsigned int, unsigned int);
-static void cp210x_break_ctl(struct tty_struct *, int);
+static int cp210x_break_ctl(struct tty_struct *, int);
static int cp210x_attach(struct usb_serial *);
static void cp210x_disconnect(struct usb_serial *);
static void cp210x_release(struct usb_serial *);
@@ -1434,18 +1434,26 @@ static int cp210x_tiocmget(struct tty_struct *tty)
return result;
}
-static void cp210x_break_ctl(struct tty_struct *tty, int break_state)
+static int cp210x_break_ctl(struct tty_struct *tty, int break_state)
{
struct usb_serial_port *port = tty->driver_data;
+ struct cp210x_serial_private *priv = usb_get_serial_data(port->serial);
u16 state;
+ if (priv->partnum == CP210X_PARTNUM_CP2105) {
+ if (cp210x_interface_num(port->serial) == 1)
+ return -ENOTTY;
+ }
+
if (break_state == 0)
state = BREAK_OFF;
else
state = BREAK_ON;
+
dev_dbg(&port->dev, "%s - turning break %s\n", __func__,
state == BREAK_OFF ? "off" : "on");
- cp210x_write_u16_reg(port, CP210X_SET_BREAK, state);
+
+ return cp210x_write_u16_reg(port, CP210X_SET_BREAK, state);
}
#ifdef CONFIG_GPIOLIB
diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c
index 45d688e9b93f..d1dea3850576 100644
--- a/drivers/usb/serial/digi_acceleport.c
+++ b/drivers/usb/serial/digi_acceleport.c
@@ -217,7 +217,7 @@ static void digi_rx_unthrottle(struct tty_struct *tty);
static void digi_set_termios(struct tty_struct *tty,
struct usb_serial_port *port,
const struct ktermios *old_termios);
-static void digi_break_ctl(struct tty_struct *tty, int break_state);
+static int digi_break_ctl(struct tty_struct *tty, int break_state);
static int digi_tiocmget(struct tty_struct *tty);
static int digi_tiocmset(struct tty_struct *tty, unsigned int set,
unsigned int clear);
@@ -839,7 +839,7 @@ static void digi_set_termios(struct tty_struct *tty,
}
-static void digi_break_ctl(struct tty_struct *tty, int break_state)
+static int digi_break_ctl(struct tty_struct *tty, int break_state)
{
struct usb_serial_port *port = tty->driver_data;
unsigned char buf[4];
@@ -848,7 +848,8 @@ static void digi_break_ctl(struct tty_struct *tty, int break_state)
buf[1] = 2; /* length */
buf[2] = break_state ? 1 : 0;
buf[3] = 0; /* pad */
- digi_write_inb_command(port, buf, 4, 0);
+
+ return digi_write_inb_command(port, buf, 4, 0);
}
diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c
index 891fb1fe69df..5f7a46bcace6 100644
--- a/drivers/usb/serial/f81232.c
+++ b/drivers/usb/serial/f81232.c
@@ -448,7 +448,7 @@ static void f81534a_process_read_urb(struct urb *urb)
tty_flip_buffer_push(&port->port);
}
-static void f81232_break_ctl(struct tty_struct *tty, int break_state)
+static int f81232_break_ctl(struct tty_struct *tty, int break_state)
{
struct usb_serial_port *port = tty->driver_data;
struct f81232_private *priv = usb_get_serial_port_data(port);
@@ -467,6 +467,8 @@ static void f81232_break_ctl(struct tty_struct *tty, int break_state)
dev_err(&port->dev, "set break failed: %d\n", status);
mutex_unlock(&priv->lock);
+
+ return status;
}
static int f81232_find_clk(speed_t baudrate)
diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c
index 4083ae961be4..ef126cd3d94f 100644
--- a/drivers/usb/serial/f81534.c
+++ b/drivers/usb/serial/f81534.c
@@ -656,7 +656,7 @@ out_unlock:
return status;
}
-static void f81534_break_ctl(struct tty_struct *tty, int break_state)
+static int f81534_break_ctl(struct tty_struct *tty, int break_state)
{
struct usb_serial_port *port = tty->driver_data;
struct f81534_port_private *port_priv = usb_get_serial_port_data(port);
@@ -675,6 +675,8 @@ static void f81534_break_ctl(struct tty_struct *tty, int break_state)
dev_err(&port->dev, "set break failed: %d\n", status);
mutex_unlock(&port_priv->lcr_mutex);
+
+ return status;
}
static int f81534_update_mctrl(struct usb_serial_port *port, unsigned int set,
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index 05e28a5ce42b..1bf23611be12 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -2550,11 +2550,12 @@ static void ftdi_process_read_urb(struct urb *urb)
tty_flip_buffer_push(&port->port);
}
-static void ftdi_break_ctl(struct tty_struct *tty, int break_state)
+static int ftdi_break_ctl(struct tty_struct *tty, int break_state)
{
struct usb_serial_port *port = tty->driver_data;
struct ftdi_private *priv = usb_get_serial_port_data(port);
u16 value;
+ int ret;
/* break_state = -1 to turn on break, and 0 to turn off break */
/* see drivers/char/tty_io.c to see it used */
@@ -2565,19 +2566,22 @@ static void ftdi_break_ctl(struct tty_struct *tty, int break_state)
else
value = priv->last_set_data_value;
- if (usb_control_msg(port->serial->dev,
+ ret = usb_control_msg(port->serial->dev,
usb_sndctrlpipe(port->serial->dev, 0),
FTDI_SIO_SET_DATA_REQUEST,
FTDI_SIO_SET_DATA_REQUEST_TYPE,
value, priv->channel,
- NULL, 0, WDR_TIMEOUT) < 0) {
+ NULL, 0, WDR_TIMEOUT);
+ if (ret < 0) {
dev_err(&port->dev, "%s FAILED to enable/disable break state (state was %d)\n",
__func__, break_state);
+ return ret;
}
dev_dbg(&port->dev, "%s break state is %d - urb is %d\n", __func__,
break_state, value);
+ return 0;
}
static bool ftdi_tx_empty(struct usb_serial_port *port)
diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c
index 3a4c0febf335..abe4bbb0ac65 100644
--- a/drivers/usb/serial/io_edgeport.c
+++ b/drivers/usb/serial/io_edgeport.c
@@ -1560,12 +1560,12 @@ static int edge_ioctl(struct tty_struct *tty,
* SerialBreak
* this function sends a break to the port
*****************************************************************************/
-static void edge_break(struct tty_struct *tty, int break_state)
+static int edge_break(struct tty_struct *tty, int break_state)
{
struct usb_serial_port *port = tty->driver_data;
struct edgeport_port *edge_port = usb_get_serial_port_data(port);
struct edgeport_serial *edge_serial = usb_get_serial_data(port->serial);
- int status;
+ int status = 0;
if (!edge_serial->is_epic ||
edge_serial->epic_descriptor.Supports.IOSPChase) {
@@ -1597,6 +1597,8 @@ static void edge_break(struct tty_struct *tty, int break_state)
dev_dbg(&port->dev, "%s - error sending break set/clear command.\n",
__func__);
}
+
+ return status;
}
diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c
index bc3c24ea42c1..7a3a6e539456 100644
--- a/drivers/usb/serial/io_ti.c
+++ b/drivers/usb/serial/io_ti.c
@@ -2421,7 +2421,7 @@ static int edge_tiocmget(struct tty_struct *tty)
return result;
}
-static void edge_break(struct tty_struct *tty, int break_state)
+static int edge_break(struct tty_struct *tty, int break_state)
{
struct usb_serial_port *port = tty->driver_data;
struct edgeport_port *edge_port = usb_get_serial_port_data(port);
@@ -2430,10 +2430,15 @@ static void edge_break(struct tty_struct *tty, int break_state)
if (break_state == -1)
bv = 1; /* On */
+
status = ti_do_config(edge_port, UMPC_SET_CLR_BREAK, bv);
- if (status)
+ if (status) {
dev_dbg(&port->dev, "%s - error %d sending break set/clear command.\n",
__func__, status);
+ return status;
+ }
+
+ return 0;
}
static void edge_heartbeat_schedule(struct edgeport_serial *edge_serial)
diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c
index 2966e0c4941e..93b17e0e05a3 100644
--- a/drivers/usb/serial/keyspan.c
+++ b/drivers/usb/serial/keyspan.c
@@ -599,7 +599,7 @@ struct keyspan_port_private {
#include "keyspan_usa67msg.h"
-static void keyspan_break_ctl(struct tty_struct *tty, int break_state)
+static int keyspan_break_ctl(struct tty_struct *tty, int break_state)
{
struct usb_serial_port *port = tty->driver_data;
struct keyspan_port_private *p_priv;
@@ -611,7 +611,10 @@ static void keyspan_break_ctl(struct tty_struct *tty, int break_state)
else
p_priv->break_on = 0;
+ /* FIXME: return errors */
keyspan_send_setup(port, 0);
+
+ return 0;
}
diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c
index 6fd15cd9e1eb..0eef358b314a 100644
--- a/drivers/usb/serial/keyspan_pda.c
+++ b/drivers/usb/serial/keyspan_pda.c
@@ -299,7 +299,7 @@ static speed_t keyspan_pda_setbaud(struct usb_serial *serial, speed_t baud)
return baud;
}
-static void keyspan_pda_break_ctl(struct tty_struct *tty, int break_state)
+static int keyspan_pda_break_ctl(struct tty_struct *tty, int break_state)
{
struct usb_serial_port *port = tty->driver_data;
struct usb_serial *serial = port->serial;
@@ -315,9 +315,13 @@ static void keyspan_pda_break_ctl(struct tty_struct *tty, int break_state)
4, /* set break */
USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
value, 0, NULL, 0, 2000);
- if (result < 0)
+ if (result < 0) {
dev_dbg(&port->dev, "%s - error %d from usb_control_msg\n",
__func__, result);
+ return result;
+ }
+
+ return 0;
}
static void keyspan_pda_set_termios(struct tty_struct *tty,
diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c
index d3852feb81a4..6570c8817a80 100644
--- a/drivers/usb/serial/mct_u232.c
+++ b/drivers/usb/serial/mct_u232.c
@@ -47,7 +47,7 @@ static void mct_u232_read_int_callback(struct urb *urb);
static void mct_u232_set_termios(struct tty_struct *tty,
struct usb_serial_port *port,
const struct ktermios *old_termios);
-static void mct_u232_break_ctl(struct tty_struct *tty, int break_state);
+static int mct_u232_break_ctl(struct tty_struct *tty, int break_state);
static int mct_u232_tiocmget(struct tty_struct *tty);
static int mct_u232_tiocmset(struct tty_struct *tty,
unsigned int set, unsigned int clear);
@@ -677,7 +677,7 @@ static void mct_u232_set_termios(struct tty_struct *tty,
spin_unlock_irqrestore(&priv->lock, flags);
} /* mct_u232_set_termios */
-static void mct_u232_break_ctl(struct tty_struct *tty, int break_state)
+static int mct_u232_break_ctl(struct tty_struct *tty, int break_state)
{
struct usb_serial_port *port = tty->driver_data;
struct mct_u232_private *priv = usb_get_serial_port_data(port);
@@ -691,7 +691,7 @@ static void mct_u232_break_ctl(struct tty_struct *tty, int break_state)
lcr |= MCT_U232_SET_BREAK;
spin_unlock_irqrestore(&priv->lock, flags);
- mct_u232_set_line_ctrl(port, lcr);
+ return mct_u232_set_line_ctrl(port, lcr);
} /* mct_u232_break_ctl */
diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c
index 1d1f85fabc28..23544074eb1c 100644
--- a/drivers/usb/serial/mos7720.c
+++ b/drivers/usb/serial/mos7720.c
@@ -996,7 +996,7 @@ static void mos7720_close(struct usb_serial_port *port)
mos7720_port->open = 0;
}
-static void mos7720_break(struct tty_struct *tty, int break_state)
+static int mos7720_break(struct tty_struct *tty, int break_state)
{
struct usb_serial_port *port = tty->driver_data;
unsigned char data;
@@ -1007,7 +1007,7 @@ static void mos7720_break(struct tty_struct *tty, int break_state)
mos7720_port = usb_get_serial_port_data(port);
if (mos7720_port == NULL)
- return;
+ return -ENODEV;
if (break_state == -1)
data = mos7720_port->shadowLCR | UART_LCR_SBC;
@@ -1015,8 +1015,9 @@ static void mos7720_break(struct tty_struct *tty, int break_state)
data = mos7720_port->shadowLCR & ~UART_LCR_SBC;
mos7720_port->shadowLCR = data;
- write_mos_reg(serial, port->port_number, MOS7720_LCR,
- mos7720_port->shadowLCR);
+
+ return write_mos_reg(serial, port->port_number, MOS7720_LCR,
+ mos7720_port->shadowLCR);
}
/*
diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c
index 73370eaae0ab..8b0308d84270 100644
--- a/drivers/usb/serial/mos7840.c
+++ b/drivers/usb/serial/mos7840.c
@@ -787,7 +787,7 @@ static void mos7840_close(struct usb_serial_port *port)
* mos7840_break
* this function sends a break to the port
*****************************************************************************/
-static void mos7840_break(struct tty_struct *tty, int break_state)
+static int mos7840_break(struct tty_struct *tty, int break_state)
{
struct usb_serial_port *port = tty->driver_data;
struct moschip_port *mos7840_port = usb_get_serial_port_data(port);
@@ -801,8 +801,9 @@ static void mos7840_break(struct tty_struct *tty, int break_state)
/* FIXME: no locking on shadowLCR anywhere in driver */
mos7840_port->shadowLCR = data;
dev_dbg(&port->dev, "%s mos7840_port->shadowLCR is %x\n", __func__, mos7840_port->shadowLCR);
- mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER,
- mos7840_port->shadowLCR);
+
+ return mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER,
+ mos7840_port->shadowLCR);
}
/*****************************************************************************
diff --git a/drivers/usb/serial/mxuport.c b/drivers/usb/serial/mxuport.c
index faa0eedfe245..1f7bb3e4fcf2 100644
--- a/drivers/usb/serial/mxuport.c
+++ b/drivers/usb/serial/mxuport.c
@@ -1230,7 +1230,7 @@ static void mxuport_close(struct usb_serial_port *port)
}
/* Send a break to the port. */
-static void mxuport_break_ctl(struct tty_struct *tty, int break_state)
+static int mxuport_break_ctl(struct tty_struct *tty, int break_state)
{
struct usb_serial_port *port = tty->driver_data;
struct usb_serial *serial = port->serial;
@@ -1244,8 +1244,8 @@ static void mxuport_break_ctl(struct tty_struct *tty, int break_state)
dev_dbg(&port->dev, "%s - clearing break\n", __func__);
}
- mxuport_send_ctrl_urb(serial, RQ_VENDOR_SET_BREAK,
- enable, port->port_number);
+ return mxuport_send_ctrl_urb(serial, RQ_VENDOR_SET_BREAK,
+ enable, port->port_number);
}
static int mxuport_resume(struct usb_serial *serial)
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index fd42e3a0bd18..288a96a74266 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -1151,6 +1151,10 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE(QUALCOMM_VENDOR_ID, 0x90fa),
.driver_info = RSVD(3) },
/* u-blox products */
+ { USB_DEVICE(UBLOX_VENDOR_ID, 0x1311) }, /* u-blox LARA-R6 01B */
+ { USB_DEVICE(UBLOX_VENDOR_ID, 0x1312), /* u-blox LARA-R6 01B (RMNET) */
+ .driver_info = RSVD(4) },
+ { USB_DEVICE_INTERFACE_CLASS(UBLOX_VENDOR_ID, 0x1313, 0xff) }, /* u-blox LARA-R6 01B (ECM) */
{ USB_DEVICE(UBLOX_VENDOR_ID, 0x1341) }, /* u-blox LARA-L6 */
{ USB_DEVICE(UBLOX_VENDOR_ID, 0x1342), /* u-blox LARA-L6 (RMNET) */
.driver_info = RSVD(4) },
diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c
index 8949c1891164..d93f5d584557 100644
--- a/drivers/usb/serial/pl2303.c
+++ b/drivers/usb/serial/pl2303.c
@@ -173,7 +173,7 @@ MODULE_DEVICE_TABLE(usb, id_table);
#define PL2303_HXN_FLOWCTRL_RTS_CTS 0x18
#define PL2303_HXN_FLOWCTRL_XON_XOFF 0x0c
-static void pl2303_set_break(struct usb_serial_port *port, bool enable);
+static int pl2303_set_break(struct usb_serial_port *port, bool enable);
enum pl2303_type {
TYPE_H,
@@ -1060,7 +1060,7 @@ static int pl2303_carrier_raised(struct usb_serial_port *port)
return 0;
}
-static void pl2303_set_break(struct usb_serial_port *port, bool enable)
+static int pl2303_set_break(struct usb_serial_port *port, bool enable)
{
struct usb_serial *serial = port->serial;
u16 state;
@@ -1077,15 +1077,19 @@ static void pl2303_set_break(struct usb_serial_port *port, bool enable)
result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
BREAK_REQUEST, BREAK_REQUEST_TYPE, state,
0, NULL, 0, 100);
- if (result)
+ if (result) {
dev_err(&port->dev, "error sending break = %d\n", result);
+ return result;
+ }
+
+ return 0;
}
-static void pl2303_break_ctl(struct tty_struct *tty, int state)
+static int pl2303_break_ctl(struct tty_struct *tty, int state)
{
struct usb_serial_port *port = tty->driver_data;
- pl2303_set_break(port, state);
+ return pl2303_set_break(port, state);
}
static void pl2303_update_line_status(struct usb_serial_port *port,
diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c
index fee581409bf6..821f25e52ec2 100644
--- a/drivers/usb/serial/quatech2.c
+++ b/drivers/usb/serial/quatech2.c
@@ -741,7 +741,7 @@ static int qt2_tiocmset(struct tty_struct *tty,
return update_mctrl(port_priv, set, clear);
}
-static void qt2_break_ctl(struct tty_struct *tty, int break_state)
+static int qt2_break_ctl(struct tty_struct *tty, int break_state)
{
struct usb_serial_port *port = tty->driver_data;
struct qt2_port_private *port_priv;
@@ -754,10 +754,14 @@ static void qt2_break_ctl(struct tty_struct *tty, int break_state)
status = qt2_control_msg(port->serial->dev, QT2_BREAK_CONTROL,
val, port_priv->device_port);
- if (status < 0)
+ if (status < 0) {
dev_warn(&port->dev,
"%s - failed to send control message: %i\n", __func__,
status);
+ return status;
+ }
+
+ return 0;
}
diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c
index b99f78224846..0fba25abf671 100644
--- a/drivers/usb/serial/ti_usb_3410_5052.c
+++ b/drivers/usb/serial/ti_usb_3410_5052.c
@@ -319,7 +319,7 @@ static void ti_set_termios(struct tty_struct *tty,
static int ti_tiocmget(struct tty_struct *tty);
static int ti_tiocmset(struct tty_struct *tty,
unsigned int set, unsigned int clear);
-static void ti_break(struct tty_struct *tty, int break_state);
+static int ti_break(struct tty_struct *tty, int break_state);
static void ti_interrupt_callback(struct urb *urb);
static void ti_bulk_in_callback(struct urb *urb);
static void ti_bulk_out_callback(struct urb *urb);
@@ -1071,7 +1071,7 @@ static int ti_tiocmset(struct tty_struct *tty,
}
-static void ti_break(struct tty_struct *tty, int break_state)
+static int ti_break(struct tty_struct *tty, int break_state)
{
struct usb_serial_port *port = tty->driver_data;
struct ti_port *tport = usb_get_serial_port_data(port);
@@ -1083,8 +1083,12 @@ static void ti_break(struct tty_struct *tty, int break_state)
tport->tp_uart_base_addr + TI_UART_OFFSET_LCR,
TI_LCR_BREAK, break_state == -1 ? TI_LCR_BREAK : 0);
- if (status)
+ if (status) {
dev_dbg(&port->dev, "%s - error setting break, %d\n", __func__, status);
+ return status;
+ }
+
+ return 0;
}
static int ti_get_port_from_code(unsigned char code)
diff --git a/drivers/usb/serial/upd78f0730.c b/drivers/usb/serial/upd78f0730.c
index c47439bd90fa..46952182e04f 100644
--- a/drivers/usb/serial/upd78f0730.c
+++ b/drivers/usb/serial/upd78f0730.c
@@ -238,12 +238,13 @@ static int upd78f0730_tiocmset(struct tty_struct *tty,
return res;
}
-static void upd78f0730_break_ctl(struct tty_struct *tty, int break_state)
+static int upd78f0730_break_ctl(struct tty_struct *tty, int break_state)
{
struct upd78f0730_port_private *private;
struct usb_serial_port *port = tty->driver_data;
struct upd78f0730_set_dtr_rts request;
struct device *dev = &port->dev;
+ int res;
private = usb_get_serial_port_data(port);
@@ -258,8 +259,10 @@ static void upd78f0730_break_ctl(struct tty_struct *tty, int break_state)
request.opcode = UPD78F0730_CMD_SET_DTR_RTS;
request.params = private->line_signals;
- upd78f0730_send_ctl(port, &request, sizeof(request));
+ res = upd78f0730_send_ctl(port, &request, sizeof(request));
mutex_unlock(&private->lock);
+
+ return res;
}
static void upd78f0730_dtr_rts(struct usb_serial_port *port, int on)
diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c
index f8404073558b..7b4805c1004d 100644
--- a/drivers/usb/serial/usb-serial.c
+++ b/drivers/usb/serial/usb-serial.c
@@ -539,9 +539,9 @@ static int serial_break(struct tty_struct *tty, int break_state)
dev_dbg(&port->dev, "%s\n", __func__);
if (port->serial->type->break_ctl)
- port->serial->type->break_ctl(tty, break_state);
+ return port->serial->type->break_ctl(tty, break_state);
- return 0;
+ return -ENOTTY;
}
static int serial_proc_show(struct seq_file *m, void *v)
diff --git a/drivers/usb/serial/usb_debug.c b/drivers/usb/serial/usb_debug.c
index aaf4813e4971..6934970f180d 100644
--- a/drivers/usb/serial/usb_debug.c
+++ b/drivers/usb/serial/usb_debug.c
@@ -47,12 +47,19 @@ MODULE_DEVICE_TABLE(usb, id_table_combined);
/* This HW really does not support a serial break, so one will be
* emulated when ever the break state is set to true.
*/
-static void usb_debug_break_ctl(struct tty_struct *tty, int break_state)
+static int usb_debug_break_ctl(struct tty_struct *tty, int break_state)
{
struct usb_serial_port *port = tty->driver_data;
+ int ret;
+
if (!break_state)
- return;
- usb_serial_generic_write(tty, port, USB_DEBUG_BRK, USB_DEBUG_BRK_SIZE);
+ return 0;
+
+ ret = usb_serial_generic_write(tty, port, USB_DEBUG_BRK, USB_DEBUG_BRK_SIZE);
+ if (ret < 0)
+ return ret;
+
+ return 0;
}
static void usb_debug_process_read_urb(struct urb *urb)
diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c
index 7f82d40753ee..ca48e90a8e81 100644
--- a/drivers/usb/serial/whiteheat.c
+++ b/drivers/usb/serial/whiteheat.c
@@ -87,7 +87,7 @@ static void whiteheat_set_termios(struct tty_struct *tty,
static int whiteheat_tiocmget(struct tty_struct *tty);
static int whiteheat_tiocmset(struct tty_struct *tty,
unsigned int set, unsigned int clear);
-static void whiteheat_break_ctl(struct tty_struct *tty, int break_state);
+static int whiteheat_break_ctl(struct tty_struct *tty, int break_state);
static struct usb_serial_driver whiteheat_fake_device = {
.driver = {
@@ -449,10 +449,11 @@ static void whiteheat_set_termios(struct tty_struct *tty,
firm_setup_port(tty);
}
-static void whiteheat_break_ctl(struct tty_struct *tty, int break_state)
+static int whiteheat_break_ctl(struct tty_struct *tty, int break_state)
{
struct usb_serial_port *port = tty->driver_data;
- firm_set_break(port, break_state);
+
+ return firm_set_break(port, break_state);
}
diff --git a/drivers/usb/serial/xr_serial.c b/drivers/usb/serial/xr_serial.c
index fdb0aae546c3..4ec7c5892b84 100644
--- a/drivers/usb/serial/xr_serial.c
+++ b/drivers/usb/serial/xr_serial.c
@@ -503,7 +503,7 @@ static void xr_dtr_rts(struct usb_serial_port *port, int on)
xr_tiocmset_port(port, 0, TIOCM_DTR | TIOCM_RTS);
}
-static void xr_break_ctl(struct tty_struct *tty, int break_state)
+static int xr_break_ctl(struct tty_struct *tty, int break_state)
{
struct usb_serial_port *port = tty->driver_data;
struct xr_data *data = usb_get_serial_port_data(port);
@@ -517,7 +517,7 @@ static void xr_break_ctl(struct tty_struct *tty, int break_state)
dev_dbg(&port->dev, "Turning break %s\n", state == 0 ? "off" : "on");
- xr_set_reg_uart(port, type->tx_break, state);
+ return xr_set_reg_uart(port, type->tx_break, state);
}
/* Tx and Rx clock mask values obtained from section 3.3.4 of datasheet */
diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig
index 831e7049977d..2f80c2792dbd 100644
--- a/drivers/usb/typec/Kconfig
+++ b/drivers/usb/typec/Kconfig
@@ -100,19 +100,6 @@ config TYPEC_STUSB160X
If you choose to build this driver as a dynamically linked module, the
module will be called stusb160x.ko.
-config TYPEC_QCOM_PMIC
- tristate "Qualcomm PMIC USB Type-C driver"
- depends on ARCH_QCOM || COMPILE_TEST
- depends on USB_ROLE_SWITCH || !USB_ROLE_SWITCH
- help
- Driver for supporting role switch over the Qualcomm PMIC. This will
- handle the USB Type-C role and orientation detection reported by the
- QCOM PMIC if the PMIC has the capability to handle USB Type-C
- detection.
-
- It will also enable the VBUS output to connected devices when a
- DFP connection is made.
-
config TYPEC_WUSB3801
tristate "Willsemi WUSB3801 Type-C port controller driver"
depends on I2C
diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile
index 4a83dad51a6c..7a368fea61bc 100644
--- a/drivers/usb/typec/Makefile
+++ b/drivers/usb/typec/Makefile
@@ -8,7 +8,6 @@ obj-$(CONFIG_TYPEC_UCSI) += ucsi/
obj-$(CONFIG_TYPEC_TPS6598X) += tipd/
obj-$(CONFIG_TYPEC_ANX7411) += anx7411.o
obj-$(CONFIG_TYPEC_HD3SS3220) += hd3ss3220.o
-obj-$(CONFIG_TYPEC_QCOM_PMIC) += qcom-pmic-typec.o
obj-$(CONFIG_TYPEC_STUSB160X) += stusb160x.o
obj-$(CONFIG_TYPEC_RT1719) += rt1719.o
obj-$(CONFIG_TYPEC_WUSB3801) += wusb3801.o
diff --git a/drivers/usb/typec/anx7411.c b/drivers/usb/typec/anx7411.c
index 3d5edce270a4..221604f933a4 100644
--- a/drivers/usb/typec/anx7411.c
+++ b/drivers/usb/typec/anx7411.c
@@ -1584,7 +1584,7 @@ static struct i2c_driver anx7411_driver = {
.of_match_table = anx_match_table,
.pm = &anx7411_pm_ops,
},
- .probe_new = anx7411_i2c_probe,
+ .probe = anx7411_i2c_probe,
.remove = anx7411_i2c_remove,
.id_table = anx7411_id,
diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c
index 349cc2030c90..faa184ae3dac 100644
--- a/drivers/usb/typec/class.c
+++ b/drivers/usb/typec/class.c
@@ -2110,7 +2110,7 @@ typec_port_register_altmode(struct typec_port *port,
struct typec_mux *mux;
struct typec_retimer *retimer;
- mux = typec_mux_get(&port->dev, desc);
+ mux = typec_mux_get(&port->dev);
if (IS_ERR(mux))
return ERR_CAST(mux);
@@ -2274,7 +2274,7 @@ struct typec_port *typec_register_port(struct device *parent,
return ERR_PTR(ret);
}
- port->mux = typec_mux_get(&port->dev, NULL);
+ port->mux = typec_mux_get(&port->dev);
if (IS_ERR(port->mux)) {
ret = PTR_ERR(port->mux);
put_device(&port->dev);
diff --git a/drivers/usb/typec/hd3ss3220.c b/drivers/usb/typec/hd3ss3220.c
index 8bbeb9b1e439..fb1242e82ffd 100644
--- a/drivers/usb/typec/hd3ss3220.c
+++ b/drivers/usb/typec/hd3ss3220.c
@@ -292,8 +292,8 @@ static struct i2c_driver hd3ss3220_driver = {
.name = "hd3ss3220",
.of_match_table = dev_ids,
},
- .probe_new = hd3ss3220_probe,
- .remove = hd3ss3220_remove,
+ .probe = hd3ss3220_probe,
+ .remove = hd3ss3220_remove,
};
module_i2c_driver(hd3ss3220_driver);
diff --git a/drivers/usb/typec/mux.c b/drivers/usb/typec/mux.c
index d9eaf9a0b0bf..80dd91938d96 100644
--- a/drivers/usb/typec/mux.c
+++ b/drivers/usb/typec/mux.c
@@ -265,60 +265,19 @@ static int mux_fwnode_match(struct device *dev, const void *fwnode)
static void *typec_mux_match(const struct fwnode_handle *fwnode,
const char *id, void *data)
{
- const struct typec_altmode_desc *desc = data;
struct device *dev;
- bool match;
- int nval;
- u16 *val;
- int ret;
- int i;
/*
- * Check has the identifier already been "consumed". If it
- * has, no need to do any extra connection identification.
+ * Device graph (OF graph) does not give any means to identify the
+ * device type or the device class of the remote port parent that @fwnode
+ * represents, so in order to identify the type or the class of @fwnode
+ * an additional device property is needed. With typec muxes the
+ * property is named "mode-switch" (@id). The value of the device
+ * property is ignored.
*/
- match = !id;
- if (match)
- goto find_mux;
-
- if (!desc) {
- /*
- * Accessory Mode muxes & muxes which explicitly specify
- * the required identifier can avoid SVID matching.
- */
- match = fwnode_property_present(fwnode, "accessory") ||
- fwnode_property_present(fwnode, id);
- if (match)
- goto find_mux;
- return NULL;
- }
-
- /* Alternate Mode muxes */
- nval = fwnode_property_count_u16(fwnode, "svid");
- if (nval <= 0)
+ if (id && !fwnode_property_present(fwnode, id))
return NULL;
- val = kcalloc(nval, sizeof(*val), GFP_KERNEL);
- if (!val)
- return ERR_PTR(-ENOMEM);
-
- ret = fwnode_property_read_u16_array(fwnode, "svid", val, nval);
- if (ret < 0) {
- kfree(val);
- return ERR_PTR(ret);
- }
-
- for (i = 0; i < nval; i++) {
- match = val[i] == desc->svid;
- if (match) {
- kfree(val);
- goto find_mux;
- }
- }
- kfree(val);
- return NULL;
-
-find_mux:
dev = class_find_device(&typec_mux_class, NULL, fwnode,
mux_fwnode_match);
@@ -328,15 +287,13 @@ find_mux:
/**
* fwnode_typec_mux_get - Find USB Type-C Multiplexer
* @fwnode: The caller device node
- * @desc: Alt Mode description
*
* Finds a mux linked to the caller. This function is primarily meant for the
* Type-C drivers. Returns a reference to the mux on success, NULL if no
* matching connection was found, or ERR_PTR(-EPROBE_DEFER) when a connection
* was found but the mux has not been enumerated yet.
*/
-struct typec_mux *fwnode_typec_mux_get(struct fwnode_handle *fwnode,
- const struct typec_altmode_desc *desc)
+struct typec_mux *fwnode_typec_mux_get(struct fwnode_handle *fwnode)
{
struct typec_mux_dev *mux_devs[TYPEC_MUX_MAX_DEVS];
struct typec_mux *mux;
@@ -349,7 +306,7 @@ struct typec_mux *fwnode_typec_mux_get(struct fwnode_handle *fwnode,
return ERR_PTR(-ENOMEM);
count = fwnode_connection_find_matches(fwnode, "mode-switch",
- (void *)desc, typec_mux_match,
+ NULL, typec_mux_match,
(void **)mux_devs,
ARRAY_SIZE(mux_devs));
if (count <= 0) {
diff --git a/drivers/usb/typec/mux/Kconfig b/drivers/usb/typec/mux/Kconfig
index c46fa4f9d3df..784b9d8107e9 100644
--- a/drivers/usb/typec/mux/Kconfig
+++ b/drivers/usb/typec/mux/Kconfig
@@ -35,4 +35,14 @@ config TYPEC_MUX_INTEL_PMC
control the USB role switch and also the multiplexer/demultiplexer
switches used with USB Type-C Alternate Modes.
+config TYPEC_MUX_NB7VPQ904M
+ tristate "On Semiconductor NB7VPQ904M Type-C redriver driver"
+ depends on I2C
+ depends on DRM || DRM=n
+ select DRM_PANEL_BRIDGE if DRM
+ select REGMAP_I2C
+ help
+ Say Y or M if your system has a On Semiconductor NB7VPQ904M Type-C
+ redriver chip found on some devices with a Type-C port.
+
endmenu
diff --git a/drivers/usb/typec/mux/Makefile b/drivers/usb/typec/mux/Makefile
index dda67e19b58b..76196096ef41 100644
--- a/drivers/usb/typec/mux/Makefile
+++ b/drivers/usb/typec/mux/Makefile
@@ -4,3 +4,4 @@ obj-$(CONFIG_TYPEC_MUX_FSA4480) += fsa4480.o
obj-$(CONFIG_TYPEC_MUX_GPIO_SBU) += gpio-sbu-mux.o
obj-$(CONFIG_TYPEC_MUX_PI3USB30532) += pi3usb30532.o
obj-$(CONFIG_TYPEC_MUX_INTEL_PMC) += intel_pmc_mux.o
+obj-$(CONFIG_TYPEC_MUX_NB7VPQ904M) += nb7vpq904m.o
diff --git a/drivers/usb/typec/mux/fsa4480.c b/drivers/usb/typec/mux/fsa4480.c
index d6495e533e58..e0ee1f621abb 100644
--- a/drivers/usb/typec/mux/fsa4480.c
+++ b/drivers/usb/typec/mux/fsa4480.c
@@ -25,15 +25,24 @@
#define FSA4480_DELAY_L_MIC 0x0e
#define FSA4480_DELAY_L_SENSE 0x0f
#define FSA4480_DELAY_L_AGND 0x10
+#define FSA4480_FUNCTION_ENABLE 0x12
#define FSA4480_RESET 0x1e
#define FSA4480_MAX_REGISTER 0x1f
#define FSA4480_ENABLE_DEVICE BIT(7)
#define FSA4480_ENABLE_SBU GENMASK(6, 5)
#define FSA4480_ENABLE_USB GENMASK(4, 3)
+#define FSA4480_ENABLE_SENSE BIT(2)
+#define FSA4480_ENABLE_MIC BIT(1)
+#define FSA4480_ENABLE_AGND BIT(0)
#define FSA4480_SEL_SBU_REVERSE GENMASK(6, 5)
#define FSA4480_SEL_USB GENMASK(4, 3)
+#define FSA4480_SEL_SENSE BIT(2)
+#define FSA4480_SEL_MIC BIT(1)
+#define FSA4480_SEL_AGND BIT(0)
+
+#define FSA4480_ENABLE_AUTO_JACK_DETECT BIT(0)
struct fsa4480 {
struct i2c_client *client;
@@ -46,8 +55,11 @@ struct fsa4480 {
struct regmap *regmap;
+ enum typec_orientation orientation;
+ unsigned long mode;
+ unsigned int svid;
+
u8 cur_enable;
- u8 cur_select;
};
static const struct regmap_config fsa4480_regmap_config = {
@@ -58,19 +70,45 @@ static const struct regmap_config fsa4480_regmap_config = {
.disable_locking = true,
};
-static int fsa4480_switch_set(struct typec_switch_dev *sw,
- enum typec_orientation orientation)
+static int fsa4480_set(struct fsa4480 *fsa)
{
- struct fsa4480 *fsa = typec_switch_get_drvdata(sw);
- u8 new_sel;
-
- mutex_lock(&fsa->lock);
- new_sel = FSA4480_SEL_USB;
- if (orientation == TYPEC_ORIENTATION_REVERSE)
- new_sel |= FSA4480_SEL_SBU_REVERSE;
-
- if (new_sel == fsa->cur_select)
- goto out_unlock;
+ bool reverse = (fsa->orientation == TYPEC_ORIENTATION_REVERSE);
+ u8 enable = FSA4480_ENABLE_DEVICE;
+ u8 sel = 0;
+
+ /* USB Mode */
+ if (fsa->mode < TYPEC_STATE_MODAL ||
+ (!fsa->svid && (fsa->mode == TYPEC_MODE_USB2 ||
+ fsa->mode == TYPEC_MODE_USB3))) {
+ enable |= FSA4480_ENABLE_USB;
+ sel = FSA4480_SEL_USB;
+ } else if (fsa->svid) {
+ switch (fsa->mode) {
+ /* DP Only */
+ case TYPEC_DP_STATE_C:
+ case TYPEC_DP_STATE_E:
+ enable |= FSA4480_ENABLE_SBU;
+ if (reverse)
+ sel = FSA4480_SEL_SBU_REVERSE;
+ break;
+
+ /* DP + USB */
+ case TYPEC_DP_STATE_D:
+ case TYPEC_DP_STATE_F:
+ enable |= FSA4480_ENABLE_USB | FSA4480_ENABLE_SBU;
+ sel = FSA4480_SEL_USB;
+ if (reverse)
+ sel |= FSA4480_SEL_SBU_REVERSE;
+ break;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+ } else if (fsa->mode == TYPEC_MODE_AUDIO) {
+ /* Audio Accessory Mode, setup to auto Jack Detection */
+ enable |= FSA4480_ENABLE_USB | FSA4480_ENABLE_AGND;
+ } else
+ return -EOPNOTSUPP;
if (fsa->cur_enable & FSA4480_ENABLE_SBU) {
/* Disable SBU output while re-configuring the switch */
@@ -81,48 +119,64 @@ static int fsa4480_switch_set(struct typec_switch_dev *sw,
usleep_range(35, 1000);
}
- regmap_write(fsa->regmap, FSA4480_SWITCH_SELECT, new_sel);
- fsa->cur_select = new_sel;
+ regmap_write(fsa->regmap, FSA4480_SWITCH_SELECT, sel);
+ regmap_write(fsa->regmap, FSA4480_SWITCH_ENABLE, enable);
- if (fsa->cur_enable & FSA4480_ENABLE_SBU) {
- regmap_write(fsa->regmap, FSA4480_SWITCH_ENABLE, fsa->cur_enable);
+ /* Start AUDIO JACK DETECTION to setup MIC, AGND & Sense muxes */
+ if (enable & FSA4480_ENABLE_AGND)
+ regmap_write(fsa->regmap, FSA4480_FUNCTION_ENABLE,
+ FSA4480_ENABLE_AUTO_JACK_DETECT);
+ if (enable & FSA4480_ENABLE_SBU) {
/* 15us to allow the SBU switch to turn on again */
usleep_range(15, 1000);
}
-out_unlock:
- mutex_unlock(&fsa->lock);
+ fsa->cur_enable = enable;
return 0;
}
+static int fsa4480_switch_set(struct typec_switch_dev *sw,
+ enum typec_orientation orientation)
+{
+ struct fsa4480 *fsa = typec_switch_get_drvdata(sw);
+ int ret = 0;
+
+ mutex_lock(&fsa->lock);
+
+ if (fsa->orientation != orientation) {
+ fsa->orientation = orientation;
+
+ ret = fsa4480_set(fsa);
+ }
+
+ mutex_unlock(&fsa->lock);
+
+ return ret;
+}
+
static int fsa4480_mux_set(struct typec_mux_dev *mux, struct typec_mux_state *state)
{
struct fsa4480 *fsa = typec_mux_get_drvdata(mux);
- u8 new_enable;
+ int ret = 0;
mutex_lock(&fsa->lock);
- new_enable = FSA4480_ENABLE_DEVICE | FSA4480_ENABLE_USB;
- if (state->mode >= TYPEC_DP_STATE_A)
- new_enable |= FSA4480_ENABLE_SBU;
+ if (fsa->mode != state->mode) {
+ fsa->mode = state->mode;
- if (new_enable == fsa->cur_enable)
- goto out_unlock;
+ if (state->alt)
+ fsa->svid = state->alt->svid;
+ else
+ fsa->svid = 0; // No SVID
- regmap_write(fsa->regmap, FSA4480_SWITCH_ENABLE, new_enable);
- fsa->cur_enable = new_enable;
-
- if (new_enable & FSA4480_ENABLE_SBU) {
- /* 15us to allow the SBU switch to turn off */
- usleep_range(15, 1000);
+ ret = fsa4480_set(fsa);
}
-out_unlock:
mutex_unlock(&fsa->lock);
- return 0;
+ return ret;
}
static int fsa4480_probe(struct i2c_client *client)
@@ -143,8 +197,10 @@ static int fsa4480_probe(struct i2c_client *client)
if (IS_ERR(fsa->regmap))
return dev_err_probe(dev, PTR_ERR(fsa->regmap), "failed to initialize regmap\n");
+ /* Safe mode */
fsa->cur_enable = FSA4480_ENABLE_DEVICE | FSA4480_ENABLE_USB;
- fsa->cur_select = FSA4480_SEL_USB;
+ fsa->mode = TYPEC_STATE_SAFE;
+ fsa->orientation = TYPEC_ORIENTATION_NONE;
/* set default settings */
regmap_write(fsa->regmap, FSA4480_SLOW_L, 0x00);
@@ -156,7 +212,7 @@ static int fsa4480_probe(struct i2c_client *client)
regmap_write(fsa->regmap, FSA4480_DELAY_L_MIC, 0x00);
regmap_write(fsa->regmap, FSA4480_DELAY_L_SENSE, 0x00);
regmap_write(fsa->regmap, FSA4480_DELAY_L_AGND, 0x09);
- regmap_write(fsa->regmap, FSA4480_SWITCH_SELECT, fsa->cur_select);
+ regmap_write(fsa->regmap, FSA4480_SWITCH_SELECT, FSA4480_SEL_USB);
regmap_write(fsa->regmap, FSA4480_SWITCH_ENABLE, fsa->cur_enable);
sw_desc.drvdata = fsa;
@@ -206,7 +262,7 @@ static struct i2c_driver fsa4480_driver = {
.name = "fsa4480",
.of_match_table = fsa4480_of_table,
},
- .probe_new = fsa4480_probe,
+ .probe = fsa4480_probe,
.remove = fsa4480_remove,
.id_table = fsa4480_table,
};
diff --git a/drivers/usb/typec/mux/gpio-sbu-mux.c b/drivers/usb/typec/mux/gpio-sbu-mux.c
index f62516dafe8f..ad60fd4e8431 100644
--- a/drivers/usb/typec/mux/gpio-sbu-mux.c
+++ b/drivers/usb/typec/mux/gpio-sbu-mux.c
@@ -3,14 +3,11 @@
* Copyright (C) 2022 Linaro Ltd.
*/
-#include <linux/bits.h>
-#include <linux/i2c.h>
-#include <linux/kernel.h>
+#include <linux/device.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
-#include <linux/regmap.h>
#include <linux/usb/typec_dp.h>
#include <linux/usb/typec_mux.h>
@@ -140,7 +137,7 @@ static int gpio_sbu_mux_probe(struct platform_device *pdev)
return 0;
}
-static int gpio_sbu_mux_remove(struct platform_device *pdev)
+static void gpio_sbu_mux_remove(struct platform_device *pdev)
{
struct gpio_sbu_mux *sbu_mux = platform_get_drvdata(pdev);
@@ -148,8 +145,6 @@ static int gpio_sbu_mux_remove(struct platform_device *pdev)
typec_mux_unregister(sbu_mux->mux);
typec_switch_unregister(sbu_mux->sw);
-
- return 0;
}
static const struct of_device_id gpio_sbu_mux_match[] = {
@@ -160,7 +155,7 @@ MODULE_DEVICE_TABLE(of, gpio_sbu_mux_match);
static struct platform_driver gpio_sbu_mux_driver = {
.probe = gpio_sbu_mux_probe,
- .remove = gpio_sbu_mux_remove,
+ .remove_new = gpio_sbu_mux_remove,
.driver = {
.name = "gpio_sbu_mux",
.of_match_table = gpio_sbu_mux_match,
diff --git a/drivers/usb/typec/mux/intel_pmc_mux.c b/drivers/usb/typec/mux/intel_pmc_mux.c
index 34e4188a40ff..5e8edf3881c0 100644
--- a/drivers/usb/typec/mux/intel_pmc_mux.c
+++ b/drivers/usb/typec/mux/intel_pmc_mux.c
@@ -15,6 +15,8 @@
#include <linux/usb/typec_mux.h>
#include <linux/usb/typec_dp.h>
#include <linux/usb/typec_tbt.h>
+#include <linux/debugfs.h>
+#include <linux/usb.h>
#include <asm/intel_scu_ipc.h>
@@ -143,8 +145,12 @@ struct pmc_usb {
struct acpi_device *iom_adev;
void __iomem *iom_base;
u32 iom_port_status_offset;
+
+ struct dentry *dentry;
};
+static struct dentry *pmc_mux_debugfs_root;
+
static void update_port_status(struct pmc_usb_port *port)
{
u8 port_num;
@@ -639,6 +645,29 @@ static int pmc_usb_probe_iom(struct pmc_usb *pmc)
return 0;
}
+static int port_iom_status_show(struct seq_file *s, void *unused)
+{
+ struct pmc_usb_port *port = s->private;
+
+ update_port_status(port);
+ seq_printf(s, "0x%08x\n", port->iom_status);
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(port_iom_status);
+
+static void pmc_mux_port_debugfs_init(struct pmc_usb_port *port)
+{
+ struct dentry *debugfs_dir;
+ char name[6];
+
+ snprintf(name, sizeof(name), "port%d", port->usb3_port - 1);
+
+ debugfs_dir = debugfs_create_dir(name, port->pmc->dentry);
+ debugfs_create_file("iom_status", 0400, debugfs_dir, port,
+ &port_iom_status_fops);
+}
+
static int pmc_usb_probe(struct platform_device *pdev)
{
struct fwnode_handle *fwnode = NULL;
@@ -674,6 +703,8 @@ static int pmc_usb_probe(struct platform_device *pdev)
if (ret)
return ret;
+ pmc->dentry = debugfs_create_dir(dev_name(pmc->dev), pmc_mux_debugfs_root);
+
/*
* For every physical USB connector (USB2 and USB3 combo) there is a
* child ACPI device node under the PMC mux ACPI device object.
@@ -688,6 +719,8 @@ static int pmc_usb_probe(struct platform_device *pdev)
fwnode_handle_put(fwnode);
goto err_remove_ports;
}
+
+ pmc_mux_port_debugfs_init(&pmc->port[i]);
}
platform_set_drvdata(pdev, pmc);
@@ -703,10 +736,12 @@ err_remove_ports:
acpi_dev_put(pmc->iom_adev);
+ debugfs_remove(pmc->dentry);
+
return ret;
}
-static int pmc_usb_remove(struct platform_device *pdev)
+static void pmc_usb_remove(struct platform_device *pdev)
{
struct pmc_usb *pmc = platform_get_drvdata(pdev);
int i;
@@ -719,7 +754,7 @@ static int pmc_usb_remove(struct platform_device *pdev)
acpi_dev_put(pmc->iom_adev);
- return 0;
+ debugfs_remove(pmc->dentry);
}
static const struct acpi_device_id pmc_usb_acpi_ids[] = {
@@ -734,10 +769,23 @@ static struct platform_driver pmc_usb_driver = {
.acpi_match_table = ACPI_PTR(pmc_usb_acpi_ids),
},
.probe = pmc_usb_probe,
- .remove = pmc_usb_remove,
+ .remove_new = pmc_usb_remove,
};
-module_platform_driver(pmc_usb_driver);
+static int __init pmc_usb_init(void)
+{
+ pmc_mux_debugfs_root = debugfs_create_dir("intel_pmc_mux", usb_debug_root);
+
+ return platform_driver_register(&pmc_usb_driver);
+}
+module_init(pmc_usb_init);
+
+static void __exit pmc_usb_exit(void)
+{
+ platform_driver_unregister(&pmc_usb_driver);
+ debugfs_remove(pmc_mux_debugfs_root);
+}
+module_exit(pmc_usb_exit);
MODULE_AUTHOR("Heikki Krogerus <heikki.krogerus@linux.intel.com>");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/typec/mux/nb7vpq904m.c b/drivers/usb/typec/mux/nb7vpq904m.c
new file mode 100644
index 000000000000..80e580d50129
--- /dev/null
+++ b/drivers/usb/typec/mux/nb7vpq904m.c
@@ -0,0 +1,529 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * OnSemi NB7VPQ904M Type-C driver
+ *
+ * Copyright (C) 2023 Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
+ */
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/bitfield.h>
+#include <linux/of_graph.h>
+#include <drm/drm_bridge.h>
+#include <linux/usb/typec_dp.h>
+#include <linux/usb/typec_mux.h>
+#include <linux/usb/typec_retimer.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+
+#define NB7_CHNA 0
+#define NB7_CHNB 1
+#define NB7_CHNC 2
+#define NB7_CHND 3
+#define NB7_IS_CHAN_AD(channel) (channel == NB7_CHNA || channel == NB7_CHND)
+
+#define GEN_DEV_SET_REG 0x00
+
+#define GEN_DEV_SET_CHIP_EN BIT(0)
+#define GEN_DEV_SET_CHNA_EN BIT(4)
+#define GEN_DEV_SET_CHNB_EN BIT(5)
+#define GEN_DEV_SET_CHNC_EN BIT(6)
+#define GEN_DEV_SET_CHND_EN BIT(7)
+
+#define GEN_DEV_SET_OP_MODE_MASK GENMASK(3, 1)
+
+#define GEN_DEV_SET_OP_MODE_DP_CC2 0
+#define GEN_DEV_SET_OP_MODE_DP_CC1 1
+#define GEN_DEV_SET_OP_MODE_DP_4LANE 2
+#define GEN_DEV_SET_OP_MODE_USB 5
+
+#define EQ_SETTING_REG_BASE 0x01
+#define EQ_SETTING_REG(n) (EQ_SETTING_REG_BASE + (n) * 2)
+#define EQ_SETTING_MASK GENMASK(3, 1)
+
+#define OUTPUT_COMPRESSION_AND_POL_REG_BASE 0x02
+#define OUTPUT_COMPRESSION_AND_POL_REG(n) (OUTPUT_COMPRESSION_AND_POL_REG_BASE + (n) * 2)
+#define OUTPUT_COMPRESSION_MASK GENMASK(2, 1)
+
+#define FLAT_GAIN_REG_BASE 0x18
+#define FLAT_GAIN_REG(n) (FLAT_GAIN_REG_BASE + (n) * 2)
+#define FLAT_GAIN_MASK GENMASK(1, 0)
+
+#define LOSS_MATCH_REG_BASE 0x19
+#define LOSS_MATCH_REG(n) (LOSS_MATCH_REG_BASE + (n) * 2)
+#define LOSS_MATCH_MASK GENMASK(1, 0)
+
+#define AUX_CC_REG 0x09
+
+#define CHIP_VERSION_REG 0x17
+
+struct nb7vpq904m {
+ struct i2c_client *client;
+ struct gpio_desc *enable_gpio;
+ struct regulator *vcc_supply;
+ struct regmap *regmap;
+ struct typec_switch_dev *sw;
+ struct typec_retimer *retimer;
+
+ bool swap_data_lanes;
+ struct typec_switch *typec_switch;
+
+ struct drm_bridge bridge;
+
+ struct mutex lock; /* protect non-concurrent retimer & switch */
+
+ enum typec_orientation orientation;
+ unsigned long mode;
+ unsigned int svid;
+};
+
+static void nb7vpq904m_set_channel(struct nb7vpq904m *nb7, unsigned int channel, bool dp)
+{
+ u8 eq, out_comp, flat_gain, loss_match;
+
+ if (dp) {
+ eq = NB7_IS_CHAN_AD(channel) ? 0x6 : 0x4;
+ out_comp = 0x3;
+ flat_gain = NB7_IS_CHAN_AD(channel) ? 0x2 : 0x1;
+ loss_match = 0x3;
+ } else {
+ eq = 0x4;
+ out_comp = 0x3;
+ flat_gain = NB7_IS_CHAN_AD(channel) ? 0x3 : 0x1;
+ loss_match = NB7_IS_CHAN_AD(channel) ? 0x1 : 0x3;
+ }
+
+ regmap_update_bits(nb7->regmap, EQ_SETTING_REG(channel),
+ EQ_SETTING_MASK, FIELD_PREP(EQ_SETTING_MASK, eq));
+ regmap_update_bits(nb7->regmap, OUTPUT_COMPRESSION_AND_POL_REG(channel),
+ OUTPUT_COMPRESSION_MASK, FIELD_PREP(OUTPUT_COMPRESSION_MASK, out_comp));
+ regmap_update_bits(nb7->regmap, FLAT_GAIN_REG(channel),
+ FLAT_GAIN_MASK, FIELD_PREP(FLAT_GAIN_MASK, flat_gain));
+ regmap_update_bits(nb7->regmap, LOSS_MATCH_REG(channel),
+ LOSS_MATCH_MASK, FIELD_PREP(LOSS_MATCH_MASK, loss_match));
+}
+
+static int nb7vpq904m_set(struct nb7vpq904m *nb7)
+{
+ bool reverse = (nb7->orientation == TYPEC_ORIENTATION_REVERSE);
+
+ switch (nb7->mode) {
+ case TYPEC_STATE_SAFE:
+ regmap_write(nb7->regmap, GEN_DEV_SET_REG,
+ GEN_DEV_SET_CHIP_EN |
+ GEN_DEV_SET_CHNA_EN |
+ GEN_DEV_SET_CHNB_EN |
+ GEN_DEV_SET_CHNC_EN |
+ GEN_DEV_SET_CHND_EN |
+ FIELD_PREP(GEN_DEV_SET_OP_MODE_MASK,
+ GEN_DEV_SET_OP_MODE_USB));
+ nb7vpq904m_set_channel(nb7, NB7_CHNA, false);
+ nb7vpq904m_set_channel(nb7, NB7_CHNB, false);
+ nb7vpq904m_set_channel(nb7, NB7_CHNC, false);
+ nb7vpq904m_set_channel(nb7, NB7_CHND, false);
+ regmap_write(nb7->regmap, AUX_CC_REG, 0x2);
+
+ return 0;
+
+ case TYPEC_STATE_USB:
+ /*
+ * Normal Orientation (CC1)
+ * A -> USB RX
+ * B -> USB TX
+ * C -> X
+ * D -> X
+ * Flipped Orientation (CC2)
+ * A -> X
+ * B -> X
+ * C -> USB TX
+ * D -> USB RX
+ *
+ * Reversed if data lanes are swapped
+ */
+ if (reverse ^ nb7->swap_data_lanes) {
+ regmap_write(nb7->regmap, GEN_DEV_SET_REG,
+ GEN_DEV_SET_CHIP_EN |
+ GEN_DEV_SET_CHNA_EN |
+ GEN_DEV_SET_CHNB_EN |
+ FIELD_PREP(GEN_DEV_SET_OP_MODE_MASK,
+ GEN_DEV_SET_OP_MODE_USB));
+ nb7vpq904m_set_channel(nb7, NB7_CHNA, false);
+ nb7vpq904m_set_channel(nb7, NB7_CHNB, false);
+ } else {
+ regmap_write(nb7->regmap, GEN_DEV_SET_REG,
+ GEN_DEV_SET_CHIP_EN |
+ GEN_DEV_SET_CHNC_EN |
+ GEN_DEV_SET_CHND_EN |
+ FIELD_PREP(GEN_DEV_SET_OP_MODE_MASK,
+ GEN_DEV_SET_OP_MODE_USB));
+ nb7vpq904m_set_channel(nb7, NB7_CHNC, false);
+ nb7vpq904m_set_channel(nb7, NB7_CHND, false);
+ }
+ regmap_write(nb7->regmap, AUX_CC_REG, 0x2);
+
+ return 0;
+
+ default:
+ if (nb7->svid != USB_TYPEC_DP_SID)
+ return -EINVAL;
+
+ break;
+ }
+
+ /* DP Altmode Setup */
+
+ regmap_write(nb7->regmap, AUX_CC_REG, reverse ? 0x1 : 0x0);
+
+ switch (nb7->mode) {
+ case TYPEC_DP_STATE_C:
+ case TYPEC_DP_STATE_E:
+ /*
+ * Normal Orientation (CC1)
+ * A -> DP3
+ * B -> DP2
+ * C -> DP1
+ * D -> DP0
+ * Flipped Orientation (CC2)
+ * A -> DP0
+ * B -> DP1
+ * C -> DP2
+ * D -> DP3
+ */
+ regmap_write(nb7->regmap, GEN_DEV_SET_REG,
+ GEN_DEV_SET_CHIP_EN |
+ GEN_DEV_SET_CHNA_EN |
+ GEN_DEV_SET_CHNB_EN |
+ GEN_DEV_SET_CHNC_EN |
+ GEN_DEV_SET_CHND_EN |
+ FIELD_PREP(GEN_DEV_SET_OP_MODE_MASK,
+ GEN_DEV_SET_OP_MODE_DP_4LANE));
+ nb7vpq904m_set_channel(nb7, NB7_CHNA, true);
+ nb7vpq904m_set_channel(nb7, NB7_CHNB, true);
+ nb7vpq904m_set_channel(nb7, NB7_CHNC, true);
+ nb7vpq904m_set_channel(nb7, NB7_CHND, true);
+ break;
+
+ case TYPEC_DP_STATE_D:
+ case TYPEC_DP_STATE_F:
+ regmap_write(nb7->regmap, GEN_DEV_SET_REG,
+ GEN_DEV_SET_CHIP_EN |
+ GEN_DEV_SET_CHNA_EN |
+ GEN_DEV_SET_CHNB_EN |
+ GEN_DEV_SET_CHNC_EN |
+ GEN_DEV_SET_CHND_EN |
+ FIELD_PREP(GEN_DEV_SET_OP_MODE_MASK,
+ reverse ^ nb7->swap_data_lanes ?
+ GEN_DEV_SET_OP_MODE_DP_CC2
+ : GEN_DEV_SET_OP_MODE_DP_CC1));
+
+ /*
+ * Normal Orientation (CC1)
+ * A -> USB RX
+ * B -> USB TX
+ * C -> DP1
+ * D -> DP0
+ * Flipped Orientation (CC2)
+ * A -> DP0
+ * B -> DP1
+ * C -> USB TX
+ * D -> USB RX
+ *
+ * Reversed if data lanes are swapped
+ */
+ if (nb7->swap_data_lanes) {
+ nb7vpq904m_set_channel(nb7, NB7_CHNA, !reverse);
+ nb7vpq904m_set_channel(nb7, NB7_CHNB, !reverse);
+ nb7vpq904m_set_channel(nb7, NB7_CHNC, reverse);
+ nb7vpq904m_set_channel(nb7, NB7_CHND, reverse);
+ } else {
+ nb7vpq904m_set_channel(nb7, NB7_CHNA, reverse);
+ nb7vpq904m_set_channel(nb7, NB7_CHNB, reverse);
+ nb7vpq904m_set_channel(nb7, NB7_CHNC, !reverse);
+ nb7vpq904m_set_channel(nb7, NB7_CHND, !reverse);
+ }
+ break;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int nb7vpq904m_sw_set(struct typec_switch_dev *sw, enum typec_orientation orientation)
+{
+ struct nb7vpq904m *nb7 = typec_switch_get_drvdata(sw);
+ int ret;
+
+ ret = typec_switch_set(nb7->typec_switch, orientation);
+ if (ret)
+ return ret;
+
+ mutex_lock(&nb7->lock);
+
+ if (nb7->orientation != orientation) {
+ nb7->orientation = orientation;
+
+ ret = nb7vpq904m_set(nb7);
+ }
+
+ mutex_unlock(&nb7->lock);
+
+ return ret;
+}
+
+static int nb7vpq904m_retimer_set(struct typec_retimer *retimer, struct typec_retimer_state *state)
+{
+ struct nb7vpq904m *nb7 = typec_retimer_get_drvdata(retimer);
+ int ret = 0;
+
+ mutex_lock(&nb7->lock);
+
+ if (nb7->mode != state->mode) {
+ nb7->mode = state->mode;
+
+ if (state->alt)
+ nb7->svid = state->alt->svid;
+ else
+ nb7->svid = 0; // No SVID
+
+ ret = nb7vpq904m_set(nb7);
+ }
+
+ mutex_unlock(&nb7->lock);
+
+ return ret;
+}
+
+#if IS_ENABLED(CONFIG_OF) && IS_ENABLED(CONFIG_DRM_PANEL_BRIDGE)
+static int nb7vpq904m_bridge_attach(struct drm_bridge *bridge,
+ enum drm_bridge_attach_flags flags)
+{
+ struct nb7vpq904m *nb7 = container_of(bridge, struct nb7vpq904m, bridge);
+ struct drm_bridge *next_bridge;
+
+ if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR))
+ return -EINVAL;
+
+ next_bridge = devm_drm_of_get_bridge(&nb7->client->dev, nb7->client->dev.of_node, 0, 0);
+ if (IS_ERR(next_bridge)) {
+ dev_err(&nb7->client->dev, "failed to acquire drm_bridge: %pe\n", next_bridge);
+ return PTR_ERR(next_bridge);
+ }
+
+ return drm_bridge_attach(bridge->encoder, next_bridge, bridge,
+ DRM_BRIDGE_ATTACH_NO_CONNECTOR);
+}
+
+static const struct drm_bridge_funcs nb7vpq904m_bridge_funcs = {
+ .attach = nb7vpq904m_bridge_attach,
+};
+
+static int nb7vpq904m_register_bridge(struct nb7vpq904m *nb7)
+{
+ nb7->bridge.funcs = &nb7vpq904m_bridge_funcs;
+ nb7->bridge.of_node = nb7->client->dev.of_node;
+
+ return devm_drm_bridge_add(&nb7->client->dev, &nb7->bridge);
+}
+#else
+static int nb7vpq904m_register_bridge(struct nb7vpq904m *nb7)
+{
+ return 0;
+}
+#endif
+
+static const struct regmap_config nb7_regmap = {
+ .max_register = 0x1f,
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+enum {
+ NORMAL_LANE_MAPPING,
+ INVERT_LANE_MAPPING,
+};
+
+#define DATA_LANES_COUNT 4
+
+static const int supported_data_lane_mapping[][DATA_LANES_COUNT] = {
+ [NORMAL_LANE_MAPPING] = { 0, 1, 2, 3 },
+ [INVERT_LANE_MAPPING] = { 3, 2, 1, 0 },
+};
+
+static int nb7vpq904m_parse_data_lanes_mapping(struct nb7vpq904m *nb7)
+{
+ struct device_node *ep;
+ u32 data_lanes[4];
+ int ret, i, j;
+
+ ep = of_graph_get_endpoint_by_regs(nb7->client->dev.of_node, 1, 0);
+
+ if (ep) {
+ ret = of_property_count_u32_elems(ep, "data-lanes");
+ if (ret == -EINVAL)
+ /* Property isn't here, consider default mapping */
+ goto out_done;
+ if (ret < 0)
+ goto out_error;
+
+ if (ret != DATA_LANES_COUNT) {
+ dev_err(&nb7->client->dev, "expected 4 data lanes\n");
+ ret = -EINVAL;
+ goto out_error;
+ }
+
+ ret = of_property_read_u32_array(ep, "data-lanes", data_lanes, DATA_LANES_COUNT);
+ if (ret)
+ goto out_error;
+
+ for (i = 0; i < ARRAY_SIZE(supported_data_lane_mapping); i++) {
+ for (j = 0; j < DATA_LANES_COUNT; j++) {
+ if (data_lanes[j] != supported_data_lane_mapping[i][j])
+ break;
+ }
+
+ if (j == DATA_LANES_COUNT)
+ break;
+ }
+
+ switch (i) {
+ case NORMAL_LANE_MAPPING:
+ break;
+ case INVERT_LANE_MAPPING:
+ nb7->swap_data_lanes = true;
+ dev_info(&nb7->client->dev, "using inverted data lanes mapping\n");
+ break;
+ default:
+ dev_err(&nb7->client->dev, "invalid data lanes mapping\n");
+ ret = -EINVAL;
+ goto out_error;
+ }
+ }
+
+out_done:
+ ret = 0;
+
+out_error:
+ of_node_put(ep);
+
+ return ret;
+}
+
+static int nb7vpq904m_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct typec_switch_desc sw_desc = { };
+ struct typec_retimer_desc retimer_desc = { };
+ struct nb7vpq904m *nb7;
+ int ret;
+
+ nb7 = devm_kzalloc(dev, sizeof(*nb7), GFP_KERNEL);
+ if (!nb7)
+ return -ENOMEM;
+
+ nb7->client = client;
+
+ nb7->regmap = devm_regmap_init_i2c(client, &nb7_regmap);
+ if (IS_ERR(nb7->regmap)) {
+ dev_err(&client->dev, "Failed to allocate register map\n");
+ return PTR_ERR(nb7->regmap);
+ }
+
+ nb7->mode = TYPEC_STATE_SAFE;
+ nb7->orientation = TYPEC_ORIENTATION_NONE;
+
+ mutex_init(&nb7->lock);
+
+ nb7->enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW);
+ if (IS_ERR(nb7->enable_gpio))
+ return dev_err_probe(dev, PTR_ERR(nb7->enable_gpio),
+ "unable to acquire enable gpio\n");
+
+ nb7->vcc_supply = devm_regulator_get_optional(dev, "vcc");
+ if (IS_ERR(nb7->vcc_supply))
+ return PTR_ERR(nb7->vcc_supply);
+
+ nb7->typec_switch = fwnode_typec_switch_get(dev->fwnode);
+ if (IS_ERR(nb7->typec_switch))
+ return dev_err_probe(dev, PTR_ERR(nb7->typec_switch),
+ "failed to acquire orientation-switch\n");
+
+ ret = nb7vpq904m_parse_data_lanes_mapping(nb7);
+ if (ret)
+ return ret;
+
+ ret = regulator_enable(nb7->vcc_supply);
+ if (ret)
+ dev_warn(dev, "Failed to enable vcc: %d\n", ret);
+
+ gpiod_set_value(nb7->enable_gpio, 1);
+
+ ret = nb7vpq904m_register_bridge(nb7);
+ if (ret)
+ return ret;
+
+ sw_desc.drvdata = nb7;
+ sw_desc.fwnode = dev->fwnode;
+ sw_desc.set = nb7vpq904m_sw_set;
+
+ nb7->sw = typec_switch_register(dev, &sw_desc);
+ if (IS_ERR(nb7->sw))
+ return dev_err_probe(dev, PTR_ERR(nb7->sw),
+ "Error registering typec switch\n");
+
+ retimer_desc.drvdata = nb7;
+ retimer_desc.fwnode = dev->fwnode;
+ retimer_desc.set = nb7vpq904m_retimer_set;
+
+ nb7->retimer = typec_retimer_register(dev, &retimer_desc);
+ if (IS_ERR(nb7->retimer)) {
+ typec_switch_unregister(nb7->sw);
+ return dev_err_probe(dev, PTR_ERR(nb7->retimer),
+ "Error registering typec retimer\n");
+ }
+
+ return 0;
+}
+
+static void nb7vpq904m_remove(struct i2c_client *client)
+{
+ struct nb7vpq904m *nb7 = i2c_get_clientdata(client);
+
+ typec_retimer_unregister(nb7->retimer);
+ typec_switch_unregister(nb7->sw);
+
+ gpiod_set_value(nb7->enable_gpio, 0);
+
+ regulator_disable(nb7->vcc_supply);
+}
+
+static const struct i2c_device_id nb7vpq904m_table[] = {
+ { "nb7vpq904m" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, nb7vpq904m_table);
+
+static const struct of_device_id nb7vpq904m_of_table[] = {
+ { .compatible = "onnn,nb7vpq904m" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, nb7vpq904m_of_table);
+
+static struct i2c_driver nb7vpq904m_driver = {
+ .driver = {
+ .name = "nb7vpq904m",
+ .of_match_table = nb7vpq904m_of_table,
+ },
+ .probe_new = nb7vpq904m_probe,
+ .remove = nb7vpq904m_remove,
+ .id_table = nb7vpq904m_table,
+};
+
+module_i2c_driver(nb7vpq904m_driver);
+
+MODULE_AUTHOR("Dmitry Baryshkov <dmitry.baryshkov@linaro.org>");
+MODULE_DESCRIPTION("OnSemi NB7VPQ904M Type-C driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/typec/mux/pi3usb30532.c b/drivers/usb/typec/mux/pi3usb30532.c
index 1cd388b55c30..8eeec135dcdb 100644
--- a/drivers/usb/typec/mux/pi3usb30532.c
+++ b/drivers/usb/typec/mux/pi3usb30532.c
@@ -178,7 +178,7 @@ static struct i2c_driver pi3usb30532_driver = {
.driver = {
.name = "pi3usb30532",
},
- .probe_new = pi3usb30532_probe,
+ .probe = pi3usb30532_probe,
.remove = pi3usb30532_remove,
.id_table = pi3usb30532_table,
};
diff --git a/drivers/usb/typec/qcom-pmic-typec.c b/drivers/usb/typec/qcom-pmic-typec.c
deleted file mode 100644
index 432ea62f1bab..000000000000
--- a/drivers/usb/typec/qcom-pmic-typec.c
+++ /dev/null
@@ -1,261 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Copyright (c) 2020, The Linux Foundation. All rights reserved.
- */
-
-#include <linux/err.h>
-#include <linux/interrupt.h>
-#include <linux/kernel.h>
-#include <linux/mod_devicetable.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/regmap.h>
-#include <linux/regulator/consumer.h>
-#include <linux/slab.h>
-#include <linux/usb/role.h>
-#include <linux/usb/typec_mux.h>
-
-#define TYPEC_MISC_STATUS 0xb
-#define CC_ATTACHED BIT(0)
-#define CC_ORIENTATION BIT(1)
-#define SNK_SRC_MODE BIT(6)
-#define TYPEC_MODE_CFG 0x44
-#define TYPEC_DISABLE_CMD BIT(0)
-#define EN_SNK_ONLY BIT(1)
-#define EN_SRC_ONLY BIT(2)
-#define TYPEC_VCONN_CONTROL 0x46
-#define VCONN_EN_SRC BIT(0)
-#define VCONN_EN_VAL BIT(1)
-#define TYPEC_EXIT_STATE_CFG 0x50
-#define SEL_SRC_UPPER_REF BIT(2)
-#define TYPEC_INTR_EN_CFG_1 0x5e
-#define TYPEC_INTR_EN_CFG_1_MASK GENMASK(7, 0)
-
-struct qcom_pmic_typec {
- struct device *dev;
- struct regmap *regmap;
- u32 base;
-
- struct typec_port *port;
- struct usb_role_switch *role_sw;
-
- struct regulator *vbus_reg;
- bool vbus_enabled;
-};
-
-static void qcom_pmic_typec_enable_vbus_regulator(struct qcom_pmic_typec
- *qcom_usb, bool enable)
-{
- int ret;
-
- if (enable == qcom_usb->vbus_enabled)
- return;
-
- if (enable) {
- ret = regulator_enable(qcom_usb->vbus_reg);
- if (ret)
- return;
- } else {
- ret = regulator_disable(qcom_usb->vbus_reg);
- if (ret)
- return;
- }
- qcom_usb->vbus_enabled = enable;
-}
-
-static void qcom_pmic_typec_check_connection(struct qcom_pmic_typec *qcom_usb)
-{
- enum typec_orientation orientation;
- enum usb_role role;
- unsigned int stat;
- bool enable_vbus;
-
- regmap_read(qcom_usb->regmap, qcom_usb->base + TYPEC_MISC_STATUS,
- &stat);
-
- if (stat & CC_ATTACHED) {
- orientation = (stat & CC_ORIENTATION) ?
- TYPEC_ORIENTATION_REVERSE :
- TYPEC_ORIENTATION_NORMAL;
- typec_set_orientation(qcom_usb->port, orientation);
-
- role = (stat & SNK_SRC_MODE) ? USB_ROLE_HOST : USB_ROLE_DEVICE;
- if (role == USB_ROLE_HOST)
- enable_vbus = true;
- else
- enable_vbus = false;
- } else {
- role = USB_ROLE_NONE;
- enable_vbus = false;
- }
-
- qcom_pmic_typec_enable_vbus_regulator(qcom_usb, enable_vbus);
- usb_role_switch_set_role(qcom_usb->role_sw, role);
-}
-
-static irqreturn_t qcom_pmic_typec_interrupt(int irq, void *_qcom_usb)
-{
- struct qcom_pmic_typec *qcom_usb = _qcom_usb;
-
- qcom_pmic_typec_check_connection(qcom_usb);
- return IRQ_HANDLED;
-}
-
-static void qcom_pmic_typec_typec_hw_init(struct qcom_pmic_typec *qcom_usb,
- enum typec_port_type type)
-{
- u8 mode = 0;
-
- regmap_update_bits(qcom_usb->regmap,
- qcom_usb->base + TYPEC_INTR_EN_CFG_1,
- TYPEC_INTR_EN_CFG_1_MASK, 0);
-
- if (type == TYPEC_PORT_SRC)
- mode = EN_SRC_ONLY;
- else if (type == TYPEC_PORT_SNK)
- mode = EN_SNK_ONLY;
-
- regmap_update_bits(qcom_usb->regmap, qcom_usb->base + TYPEC_MODE_CFG,
- EN_SNK_ONLY | EN_SRC_ONLY, mode);
-
- regmap_update_bits(qcom_usb->regmap,
- qcom_usb->base + TYPEC_VCONN_CONTROL,
- VCONN_EN_SRC | VCONN_EN_VAL, VCONN_EN_SRC);
- regmap_update_bits(qcom_usb->regmap,
- qcom_usb->base + TYPEC_EXIT_STATE_CFG,
- SEL_SRC_UPPER_REF, SEL_SRC_UPPER_REF);
-}
-
-static int qcom_pmic_typec_probe(struct platform_device *pdev)
-{
- struct qcom_pmic_typec *qcom_usb;
- struct device *dev = &pdev->dev;
- struct fwnode_handle *fwnode;
- struct typec_capability cap;
- const char *buf;
- int ret, irq, role;
- u32 reg;
-
- ret = device_property_read_u32(dev, "reg", &reg);
- if (ret < 0) {
- dev_err(dev, "missing base address\n");
- return ret;
- }
-
- qcom_usb = devm_kzalloc(dev, sizeof(*qcom_usb), GFP_KERNEL);
- if (!qcom_usb)
- return -ENOMEM;
-
- qcom_usb->dev = dev;
- qcom_usb->base = reg;
-
- qcom_usb->regmap = dev_get_regmap(dev->parent, NULL);
- if (!qcom_usb->regmap) {
- dev_err(dev, "Failed to get regmap\n");
- return -EINVAL;
- }
-
- qcom_usb->vbus_reg = devm_regulator_get(qcom_usb->dev, "usb_vbus");
- if (IS_ERR(qcom_usb->vbus_reg))
- return PTR_ERR(qcom_usb->vbus_reg);
-
- fwnode = device_get_named_child_node(dev, "connector");
- if (!fwnode)
- return -EINVAL;
-
- ret = fwnode_property_read_string(fwnode, "power-role", &buf);
- if (!ret) {
- role = typec_find_port_power_role(buf);
- if (role < 0)
- role = TYPEC_PORT_SNK;
- } else {
- role = TYPEC_PORT_SNK;
- }
- cap.type = role;
-
- ret = fwnode_property_read_string(fwnode, "data-role", &buf);
- if (!ret) {
- role = typec_find_port_data_role(buf);
- if (role < 0)
- role = TYPEC_PORT_UFP;
- } else {
- role = TYPEC_PORT_UFP;
- }
- cap.data = role;
-
- cap.prefer_role = TYPEC_NO_PREFERRED_ROLE;
- cap.fwnode = fwnode;
- qcom_usb->port = typec_register_port(dev, &cap);
- if (IS_ERR(qcom_usb->port)) {
- ret = PTR_ERR(qcom_usb->port);
- dev_err(dev, "Failed to register type c port %d\n", ret);
- goto err_put_node;
- }
- fwnode_handle_put(fwnode);
-
- qcom_usb->role_sw = fwnode_usb_role_switch_get(dev_fwnode(qcom_usb->dev));
- if (IS_ERR(qcom_usb->role_sw)) {
- ret = dev_err_probe(dev, PTR_ERR(qcom_usb->role_sw),
- "failed to get role switch\n");
- goto err_typec_port;
- }
-
- irq = platform_get_irq(pdev, 0);
- if (irq < 0)
- goto err_usb_role_sw;
-
- ret = devm_request_threaded_irq(qcom_usb->dev, irq, NULL,
- qcom_pmic_typec_interrupt, IRQF_ONESHOT,
- "qcom-pmic-typec", qcom_usb);
- if (ret) {
- dev_err(&pdev->dev, "Could not request IRQ\n");
- goto err_usb_role_sw;
- }
-
- platform_set_drvdata(pdev, qcom_usb);
- qcom_pmic_typec_typec_hw_init(qcom_usb, cap.type);
- qcom_pmic_typec_check_connection(qcom_usb);
-
- return 0;
-
-err_usb_role_sw:
- usb_role_switch_put(qcom_usb->role_sw);
-err_typec_port:
- typec_unregister_port(qcom_usb->port);
-err_put_node:
- fwnode_handle_put(fwnode);
-
- return ret;
-}
-
-static int qcom_pmic_typec_remove(struct platform_device *pdev)
-{
- struct qcom_pmic_typec *qcom_usb = platform_get_drvdata(pdev);
-
- usb_role_switch_set_role(qcom_usb->role_sw, USB_ROLE_NONE);
- qcom_pmic_typec_enable_vbus_regulator(qcom_usb, 0);
-
- typec_unregister_port(qcom_usb->port);
- usb_role_switch_put(qcom_usb->role_sw);
-
- return 0;
-}
-
-static const struct of_device_id qcom_pmic_typec_table[] = {
- { .compatible = "qcom,pm8150b-usb-typec" },
- { }
-};
-MODULE_DEVICE_TABLE(of, qcom_pmic_typec_table);
-
-static struct platform_driver qcom_pmic_typec = {
- .driver = {
- .name = "qcom,pmic-typec",
- .of_match_table = qcom_pmic_typec_table,
- },
- .probe = qcom_pmic_typec_probe,
- .remove = qcom_pmic_typec_remove,
-};
-module_platform_driver(qcom_pmic_typec);
-
-MODULE_DESCRIPTION("QCOM PMIC USB type C driver");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/typec/rt1719.c b/drivers/usb/typec/rt1719.c
index ea8b700b0ceb..be02d420920e 100644
--- a/drivers/usb/typec/rt1719.c
+++ b/drivers/usb/typec/rt1719.c
@@ -949,7 +949,7 @@ static struct i2c_driver rt1719_driver = {
.name = "rt1719",
.of_match_table = rt1719_device_table,
},
- .probe_new = rt1719_probe,
+ .probe = rt1719_probe,
.remove = rt1719_remove,
};
module_i2c_driver(rt1719_driver);
diff --git a/drivers/usb/typec/stusb160x.c b/drivers/usb/typec/stusb160x.c
index 494b371151e0..3ab118df1bd4 100644
--- a/drivers/usb/typec/stusb160x.c
+++ b/drivers/usb/typec/stusb160x.c
@@ -870,7 +870,7 @@ static struct i2c_driver stusb160x_driver = {
.pm = &stusb160x_pm_ops,
.of_match_table = stusb160x_of_match,
},
- .probe_new = stusb160x_probe,
+ .probe = stusb160x_probe,
.remove = stusb160x_remove,
};
module_i2c_driver(stusb160x_driver);
diff --git a/drivers/usb/typec/tcpm/Kconfig b/drivers/usb/typec/tcpm/Kconfig
index e6b88ca4a4b9..5d393f520fc2 100644
--- a/drivers/usb/typec/tcpm/Kconfig
+++ b/drivers/usb/typec/tcpm/Kconfig
@@ -76,4 +76,15 @@ config TYPEC_WCOVE
To compile this driver as module, choose M here: the module will be
called typec_wcove.ko
+config TYPEC_QCOM_PMIC
+ tristate "Qualcomm PMIC USB Type-C Port Controller Manager driver"
+ depends on ARCH_QCOM || COMPILE_TEST
+ help
+ A Type-C port and Power Delivery driver which aggregates two
+ discrete pieces of silicon in the PM8150b PMIC block: the
+ Type-C port controller and the Power Delivery PHY.
+
+ This driver enables Type-C role switching, orientation, Alternate
+ mode and Power Delivery support both for VBUS and VCONN.
+
endif # TYPEC_TCPM
diff --git a/drivers/usb/typec/tcpm/Makefile b/drivers/usb/typec/tcpm/Makefile
index 08e57bb499cb..7a8cad0c0bdb 100644
--- a/drivers/usb/typec/tcpm/Makefile
+++ b/drivers/usb/typec/tcpm/Makefile
@@ -9,3 +9,4 @@ obj-$(CONFIG_TYPEC_MT6360) += tcpci_mt6360.o
obj-$(CONFIG_TYPEC_TCPCI_MT6370) += tcpci_mt6370.o
obj-$(CONFIG_TYPEC_TCPCI_MAXIM) += tcpci_maxim.o
tcpci_maxim-y += tcpci_maxim_core.o maxim_contaminant.o
+obj-$(CONFIG_TYPEC_QCOM_PMIC) += qcom/
diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c
index 62ba53357612..7fc1ffa14f76 100644
--- a/drivers/usb/typec/tcpm/fusb302.c
+++ b/drivers/usb/typec/tcpm/fusb302.c
@@ -1836,7 +1836,7 @@ static struct i2c_driver fusb302_driver = {
.pm = &fusb302_pm_ops,
.of_match_table = of_match_ptr(fusb302_dt_match),
},
- .probe_new = fusb302_probe,
+ .probe = fusb302_probe,
.remove = fusb302_remove,
.id_table = fusb302_i2c_device_id,
};
diff --git a/drivers/usb/typec/tcpm/qcom/Makefile b/drivers/usb/typec/tcpm/qcom/Makefile
new file mode 100644
index 000000000000..dc1e8832e197
--- /dev/null
+++ b/drivers/usb/typec/tcpm/qcom/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+obj-$(CONFIG_TYPEC_QCOM_PMIC) += qcom_pmic_tcpm.o
+qcom_pmic_tcpm-y += qcom_pmic_typec.o \
+ qcom_pmic_typec_port.o \
+ qcom_pmic_typec_pdphy.o
diff --git a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c
new file mode 100644
index 000000000000..a905160dd860
--- /dev/null
+++ b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c
@@ -0,0 +1,344 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2023, Linaro Ltd. All rights reserved.
+ */
+
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/usb/role.h>
+#include <linux/usb/tcpm.h>
+#include <linux/usb/typec_mux.h>
+#include "qcom_pmic_typec_pdphy.h"
+#include "qcom_pmic_typec_port.h"
+
+struct pmic_typec_resources {
+ struct pmic_typec_pdphy_resources *pdphy_res;
+ struct pmic_typec_port_resources *port_res;
+};
+
+struct pmic_typec {
+ struct device *dev;
+ struct tcpm_port *tcpm_port;
+ struct tcpc_dev tcpc;
+ struct pmic_typec_pdphy *pmic_typec_pdphy;
+ struct pmic_typec_port *pmic_typec_port;
+ bool vbus_enabled;
+ struct mutex lock; /* VBUS state serialization */
+};
+
+#define tcpc_to_tcpm(_tcpc_) container_of(_tcpc_, struct pmic_typec, tcpc)
+
+static int qcom_pmic_typec_get_vbus(struct tcpc_dev *tcpc)
+{
+ struct pmic_typec *tcpm = tcpc_to_tcpm(tcpc);
+ int ret;
+
+ mutex_lock(&tcpm->lock);
+ ret = tcpm->vbus_enabled || qcom_pmic_typec_port_get_vbus(tcpm->pmic_typec_port);
+ mutex_unlock(&tcpm->lock);
+
+ return ret;
+}
+
+static int qcom_pmic_typec_set_vbus(struct tcpc_dev *tcpc, bool on, bool sink)
+{
+ struct pmic_typec *tcpm = tcpc_to_tcpm(tcpc);
+ int ret = 0;
+
+ mutex_lock(&tcpm->lock);
+ if (tcpm->vbus_enabled == on)
+ goto done;
+
+ ret = qcom_pmic_typec_port_set_vbus(tcpm->pmic_typec_port, on);
+ if (ret)
+ goto done;
+
+ tcpm->vbus_enabled = on;
+ tcpm_vbus_change(tcpm->tcpm_port);
+
+done:
+ dev_dbg(tcpm->dev, "set_vbus set: %d result %d\n", on, ret);
+ mutex_unlock(&tcpm->lock);
+
+ return ret;
+}
+
+static int qcom_pmic_typec_set_vconn(struct tcpc_dev *tcpc, bool on)
+{
+ struct pmic_typec *tcpm = tcpc_to_tcpm(tcpc);
+
+ return qcom_pmic_typec_port_set_vconn(tcpm->pmic_typec_port, on);
+}
+
+static int qcom_pmic_typec_get_cc(struct tcpc_dev *tcpc,
+ enum typec_cc_status *cc1,
+ enum typec_cc_status *cc2)
+{
+ struct pmic_typec *tcpm = tcpc_to_tcpm(tcpc);
+
+ return qcom_pmic_typec_port_get_cc(tcpm->pmic_typec_port, cc1, cc2);
+}
+
+static int qcom_pmic_typec_set_cc(struct tcpc_dev *tcpc,
+ enum typec_cc_status cc)
+{
+ struct pmic_typec *tcpm = tcpc_to_tcpm(tcpc);
+
+ return qcom_pmic_typec_port_set_cc(tcpm->pmic_typec_port, cc);
+}
+
+static int qcom_pmic_typec_set_polarity(struct tcpc_dev *tcpc,
+ enum typec_cc_polarity pol)
+{
+ /* Polarity is set separately by phy-qcom-qmp.c */
+ return 0;
+}
+
+static int qcom_pmic_typec_start_toggling(struct tcpc_dev *tcpc,
+ enum typec_port_type port_type,
+ enum typec_cc_status cc)
+{
+ struct pmic_typec *tcpm = tcpc_to_tcpm(tcpc);
+
+ return qcom_pmic_typec_port_start_toggling(tcpm->pmic_typec_port,
+ port_type, cc);
+}
+
+static int qcom_pmic_typec_set_roles(struct tcpc_dev *tcpc, bool attached,
+ enum typec_role power_role,
+ enum typec_data_role data_role)
+{
+ struct pmic_typec *tcpm = tcpc_to_tcpm(tcpc);
+
+ return qcom_pmic_typec_pdphy_set_roles(tcpm->pmic_typec_pdphy,
+ data_role, power_role);
+}
+
+static int qcom_pmic_typec_set_pd_rx(struct tcpc_dev *tcpc, bool on)
+{
+ struct pmic_typec *tcpm = tcpc_to_tcpm(tcpc);
+
+ return qcom_pmic_typec_pdphy_set_pd_rx(tcpm->pmic_typec_pdphy, on);
+}
+
+static int qcom_pmic_typec_pd_transmit(struct tcpc_dev *tcpc,
+ enum tcpm_transmit_type type,
+ const struct pd_message *msg,
+ unsigned int negotiated_rev)
+{
+ struct pmic_typec *tcpm = tcpc_to_tcpm(tcpc);
+
+ return qcom_pmic_typec_pdphy_pd_transmit(tcpm->pmic_typec_pdphy, type,
+ msg, negotiated_rev);
+}
+
+static int qcom_pmic_typec_init(struct tcpc_dev *tcpc)
+{
+ return 0;
+}
+
+static int qcom_pmic_typec_probe(struct platform_device *pdev)
+{
+ struct pmic_typec *tcpm;
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ const struct pmic_typec_resources *res;
+ struct regmap *regmap;
+ u32 base[2];
+ int ret;
+
+ res = of_device_get_match_data(dev);
+ if (!res)
+ return -ENODEV;
+
+ tcpm = devm_kzalloc(dev, sizeof(*tcpm), GFP_KERNEL);
+ if (!tcpm)
+ return -ENOMEM;
+
+ tcpm->dev = dev;
+ tcpm->tcpc.init = qcom_pmic_typec_init;
+ tcpm->tcpc.get_vbus = qcom_pmic_typec_get_vbus;
+ tcpm->tcpc.set_vbus = qcom_pmic_typec_set_vbus;
+ tcpm->tcpc.set_cc = qcom_pmic_typec_set_cc;
+ tcpm->tcpc.get_cc = qcom_pmic_typec_get_cc;
+ tcpm->tcpc.set_polarity = qcom_pmic_typec_set_polarity;
+ tcpm->tcpc.set_vconn = qcom_pmic_typec_set_vconn;
+ tcpm->tcpc.start_toggling = qcom_pmic_typec_start_toggling;
+ tcpm->tcpc.set_pd_rx = qcom_pmic_typec_set_pd_rx;
+ tcpm->tcpc.set_roles = qcom_pmic_typec_set_roles;
+ tcpm->tcpc.pd_transmit = qcom_pmic_typec_pd_transmit;
+
+ regmap = dev_get_regmap(dev->parent, NULL);
+ if (!regmap) {
+ dev_err(dev, "Failed to get regmap\n");
+ return -ENODEV;
+ }
+
+ ret = of_property_read_u32_array(np, "reg", base, 2);
+ if (ret)
+ return ret;
+
+ tcpm->pmic_typec_port = qcom_pmic_typec_port_alloc(dev);
+ if (IS_ERR(tcpm->pmic_typec_port))
+ return PTR_ERR(tcpm->pmic_typec_port);
+
+ tcpm->pmic_typec_pdphy = qcom_pmic_typec_pdphy_alloc(dev);
+ if (IS_ERR(tcpm->pmic_typec_pdphy))
+ return PTR_ERR(tcpm->pmic_typec_pdphy);
+
+ ret = qcom_pmic_typec_port_probe(pdev, tcpm->pmic_typec_port,
+ res->port_res, regmap, base[0]);
+ if (ret)
+ return ret;
+
+ ret = qcom_pmic_typec_pdphy_probe(pdev, tcpm->pmic_typec_pdphy,
+ res->pdphy_res, regmap, base[1]);
+ if (ret)
+ return ret;
+
+ mutex_init(&tcpm->lock);
+ platform_set_drvdata(pdev, tcpm);
+
+ tcpm->tcpc.fwnode = device_get_named_child_node(tcpm->dev, "connector");
+ if (IS_ERR(tcpm->tcpc.fwnode))
+ return PTR_ERR(tcpm->tcpc.fwnode);
+
+ tcpm->tcpm_port = tcpm_register_port(tcpm->dev, &tcpm->tcpc);
+ if (IS_ERR(tcpm->tcpm_port)) {
+ ret = PTR_ERR(tcpm->tcpm_port);
+ goto fwnode_remove;
+ }
+
+ ret = qcom_pmic_typec_port_start(tcpm->pmic_typec_port,
+ tcpm->tcpm_port);
+ if (ret)
+ goto fwnode_remove;
+
+ ret = qcom_pmic_typec_pdphy_start(tcpm->pmic_typec_pdphy,
+ tcpm->tcpm_port);
+ if (ret)
+ goto fwnode_remove;
+
+ return 0;
+
+fwnode_remove:
+ fwnode_remove_software_node(tcpm->tcpc.fwnode);
+
+ return ret;
+}
+
+static void qcom_pmic_typec_remove(struct platform_device *pdev)
+{
+ struct pmic_typec *tcpm = platform_get_drvdata(pdev);
+
+ qcom_pmic_typec_pdphy_stop(tcpm->pmic_typec_pdphy);
+ qcom_pmic_typec_port_stop(tcpm->pmic_typec_port);
+ tcpm_unregister_port(tcpm->tcpm_port);
+ fwnode_remove_software_node(tcpm->tcpc.fwnode);
+}
+
+static struct pmic_typec_pdphy_resources pm8150b_pdphy_res = {
+ .irq_params = {
+ {
+ .virq = PMIC_PDPHY_SIG_TX_IRQ,
+ .irq_name = "sig-tx",
+ },
+ {
+ .virq = PMIC_PDPHY_SIG_RX_IRQ,
+ .irq_name = "sig-rx",
+ },
+ {
+ .virq = PMIC_PDPHY_MSG_TX_IRQ,
+ .irq_name = "msg-tx",
+ },
+ {
+ .virq = PMIC_PDPHY_MSG_RX_IRQ,
+ .irq_name = "msg-rx",
+ },
+ {
+ .virq = PMIC_PDPHY_MSG_TX_FAIL_IRQ,
+ .irq_name = "msg-tx-failed",
+ },
+ {
+ .virq = PMIC_PDPHY_MSG_TX_DISCARD_IRQ,
+ .irq_name = "msg-tx-discarded",
+ },
+ {
+ .virq = PMIC_PDPHY_MSG_RX_DISCARD_IRQ,
+ .irq_name = "msg-rx-discarded",
+ },
+ },
+ .nr_irqs = 7,
+};
+
+static struct pmic_typec_port_resources pm8150b_port_res = {
+ .irq_params = {
+ {
+ .irq_name = "vpd-detect",
+ .virq = PMIC_TYPEC_VPD_IRQ,
+ },
+
+ {
+ .irq_name = "cc-state-change",
+ .virq = PMIC_TYPEC_CC_STATE_IRQ,
+ },
+ {
+ .irq_name = "vconn-oc",
+ .virq = PMIC_TYPEC_VCONN_OC_IRQ,
+ },
+
+ {
+ .irq_name = "vbus-change",
+ .virq = PMIC_TYPEC_VBUS_IRQ,
+ },
+
+ {
+ .irq_name = "attach-detach",
+ .virq = PMIC_TYPEC_ATTACH_DETACH_IRQ,
+ },
+ {
+ .irq_name = "legacy-cable-detect",
+ .virq = PMIC_TYPEC_LEGACY_CABLE_IRQ,
+ },
+
+ {
+ .irq_name = "try-snk-src-detect",
+ .virq = PMIC_TYPEC_TRY_SNK_SRC_IRQ,
+ },
+ },
+ .nr_irqs = 7,
+};
+
+static struct pmic_typec_resources pm8150b_typec_res = {
+ .pdphy_res = &pm8150b_pdphy_res,
+ .port_res = &pm8150b_port_res,
+};
+
+static const struct of_device_id qcom_pmic_typec_table[] = {
+ { .compatible = "qcom,pm8150b-typec", .data = &pm8150b_typec_res },
+ { }
+};
+MODULE_DEVICE_TABLE(of, qcom_pmic_typec_table);
+
+static struct platform_driver qcom_pmic_typec_driver = {
+ .driver = {
+ .name = "qcom,pmic-typec",
+ .of_match_table = qcom_pmic_typec_table,
+ },
+ .probe = qcom_pmic_typec_probe,
+ .remove_new = qcom_pmic_typec_remove,
+};
+
+module_platform_driver(qcom_pmic_typec_driver);
+
+MODULE_DESCRIPTION("QCOM PMIC USB Type-C Port Manager Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c
new file mode 100644
index 000000000000..4e1b846627d2
--- /dev/null
+++ b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c
@@ -0,0 +1,528 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2023, Linaro Ltd. All rights reserved.
+ */
+
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#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>
+#include <linux/slab.h>
+#include <linux/usb/pd.h>
+#include <linux/usb/tcpm.h>
+#include "qcom_pmic_typec_pdphy.h"
+
+struct pmic_typec_pdphy_irq_data {
+ int virq;
+ int irq;
+ struct pmic_typec_pdphy *pmic_typec_pdphy;
+};
+
+struct pmic_typec_pdphy {
+ struct device *dev;
+ struct tcpm_port *tcpm_port;
+ struct regmap *regmap;
+ u32 base;
+
+ unsigned int nr_irqs;
+ struct pmic_typec_pdphy_irq_data *irq_data;
+
+ struct work_struct reset_work;
+ struct work_struct receive_work;
+ struct regulator *vdd_pdphy;
+ spinlock_t lock; /* Register atomicity */
+};
+
+static void qcom_pmic_typec_pdphy_reset_on(struct pmic_typec_pdphy *pmic_typec_pdphy)
+{
+ struct device *dev = pmic_typec_pdphy->dev;
+ int ret;
+
+ /* Terminate TX */
+ ret = regmap_write(pmic_typec_pdphy->regmap,
+ pmic_typec_pdphy->base + USB_PDPHY_TX_CONTROL_REG, 0);
+ if (ret)
+ goto err;
+
+ ret = regmap_write(pmic_typec_pdphy->regmap,
+ pmic_typec_pdphy->base + USB_PDPHY_FRAME_FILTER_REG, 0);
+ if (ret)
+ goto err;
+
+ return;
+err:
+ dev_err(dev, "pd_reset_on error\n");
+}
+
+static void qcom_pmic_typec_pdphy_reset_off(struct pmic_typec_pdphy *pmic_typec_pdphy)
+{
+ struct device *dev = pmic_typec_pdphy->dev;
+ int ret;
+
+ ret = regmap_write(pmic_typec_pdphy->regmap,
+ pmic_typec_pdphy->base + USB_PDPHY_FRAME_FILTER_REG,
+ FRAME_FILTER_EN_SOP | FRAME_FILTER_EN_HARD_RESET);
+ if (ret)
+ dev_err(dev, "pd_reset_off error\n");
+}
+
+static void qcom_pmic_typec_pdphy_sig_reset_work(struct work_struct *work)
+{
+ struct pmic_typec_pdphy *pmic_typec_pdphy = container_of(work, struct pmic_typec_pdphy,
+ reset_work);
+ unsigned long flags;
+
+ spin_lock_irqsave(&pmic_typec_pdphy->lock, flags);
+
+ qcom_pmic_typec_pdphy_reset_on(pmic_typec_pdphy);
+ qcom_pmic_typec_pdphy_reset_off(pmic_typec_pdphy);
+
+ spin_unlock_irqrestore(&pmic_typec_pdphy->lock, flags);
+
+ tcpm_pd_hard_reset(pmic_typec_pdphy->tcpm_port);
+}
+
+static int
+qcom_pmic_typec_pdphy_clear_tx_control_reg(struct pmic_typec_pdphy *pmic_typec_pdphy)
+{
+ struct device *dev = pmic_typec_pdphy->dev;
+ unsigned int val;
+ int ret;
+
+ /* Clear TX control register */
+ ret = regmap_write(pmic_typec_pdphy->regmap,
+ pmic_typec_pdphy->base + USB_PDPHY_TX_CONTROL_REG, 0);
+ if (ret)
+ goto done;
+
+ /* Perform readback to ensure sufficient delay for command to latch */
+ ret = regmap_read(pmic_typec_pdphy->regmap,
+ pmic_typec_pdphy->base + USB_PDPHY_TX_CONTROL_REG, &val);
+
+done:
+ if (ret)
+ dev_err(dev, "pd_clear_tx_control_reg: clear tx flag\n");
+
+ return ret;
+}
+
+static int
+qcom_pmic_typec_pdphy_pd_transmit_signal(struct pmic_typec_pdphy *pmic_typec_pdphy,
+ enum tcpm_transmit_type type,
+ unsigned int negotiated_rev)
+{
+ struct device *dev = pmic_typec_pdphy->dev;
+ unsigned int val;
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&pmic_typec_pdphy->lock, flags);
+
+ /* Clear TX control register */
+ ret = qcom_pmic_typec_pdphy_clear_tx_control_reg(pmic_typec_pdphy);
+ if (ret)
+ goto done;
+
+ val = TX_CONTROL_SEND_SIGNAL;
+ if (negotiated_rev == PD_REV30)
+ val |= TX_CONTROL_RETRY_COUNT(2);
+ else
+ val |= TX_CONTROL_RETRY_COUNT(3);
+
+ if (type == TCPC_TX_CABLE_RESET || type == TCPC_TX_HARD_RESET)
+ val |= TX_CONTROL_FRAME_TYPE(1);
+
+ ret = regmap_write(pmic_typec_pdphy->regmap,
+ pmic_typec_pdphy->base + USB_PDPHY_TX_CONTROL_REG, val);
+
+done:
+ spin_unlock_irqrestore(&pmic_typec_pdphy->lock, flags);
+
+ dev_vdbg(dev, "pd_transmit_signal: type %d negotiate_rev %d send %d\n",
+ type, negotiated_rev, ret);
+
+ return ret;
+}
+
+static int
+qcom_pmic_typec_pdphy_pd_transmit_payload(struct pmic_typec_pdphy *pmic_typec_pdphy,
+ enum tcpm_transmit_type type,
+ const struct pd_message *msg,
+ unsigned int negotiated_rev)
+{
+ struct device *dev = pmic_typec_pdphy->dev;
+ unsigned int val, hdr_len, txbuf_len, txsize_len;
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&pmic_typec_pdphy->lock, flags);
+
+ ret = regmap_read(pmic_typec_pdphy->regmap,
+ pmic_typec_pdphy->base + USB_PDPHY_RX_ACKNOWLEDGE_REG,
+ &val);
+ if (ret)
+ goto done;
+
+ if (val) {
+ dev_err(dev, "pd_transmit_payload: RX message pending\n");
+ ret = -EBUSY;
+ goto done;
+ }
+
+ /* Clear TX control register */
+ ret = qcom_pmic_typec_pdphy_clear_tx_control_reg(pmic_typec_pdphy);
+ if (ret)
+ goto done;
+
+ hdr_len = sizeof(msg->header);
+ txbuf_len = pd_header_cnt_le(msg->header) * 4;
+ txsize_len = hdr_len + txbuf_len - 1;
+
+ /* Write message header sizeof(u16) to USB_PDPHY_TX_BUFFER_HDR_REG */
+ ret = regmap_bulk_write(pmic_typec_pdphy->regmap,
+ pmic_typec_pdphy->base + USB_PDPHY_TX_BUFFER_HDR_REG,
+ &msg->header, hdr_len);
+ if (ret)
+ goto done;
+
+ /* Write payload to USB_PDPHY_TX_BUFFER_DATA_REG for txbuf_len */
+ if (txbuf_len) {
+ ret = regmap_bulk_write(pmic_typec_pdphy->regmap,
+ pmic_typec_pdphy->base + USB_PDPHY_TX_BUFFER_DATA_REG,
+ &msg->payload, txbuf_len);
+ if (ret)
+ goto done;
+ }
+
+ /* Write total length ((header + data) - 1) to USB_PDPHY_TX_SIZE_REG */
+ ret = regmap_write(pmic_typec_pdphy->regmap,
+ pmic_typec_pdphy->base + USB_PDPHY_TX_SIZE_REG,
+ txsize_len);
+ if (ret)
+ goto done;
+
+ /* Clear TX control register */
+ ret = qcom_pmic_typec_pdphy_clear_tx_control_reg(pmic_typec_pdphy);
+ if (ret)
+ goto done;
+
+ /* Initiate transmit with retry count as indicated by PD revision */
+ val = TX_CONTROL_FRAME_TYPE(type) | TX_CONTROL_SEND_MSG;
+ if (pd_header_rev(msg->header) == PD_REV30)
+ val |= TX_CONTROL_RETRY_COUNT(2);
+ else
+ val |= TX_CONTROL_RETRY_COUNT(3);
+
+ ret = regmap_write(pmic_typec_pdphy->regmap,
+ pmic_typec_pdphy->base + USB_PDPHY_TX_CONTROL_REG, val);
+
+done:
+ spin_unlock_irqrestore(&pmic_typec_pdphy->lock, flags);
+
+ if (ret) {
+ dev_err(dev, "pd_transmit_payload: hdr %*ph data %*ph ret %d\n",
+ hdr_len, &msg->header, txbuf_len, &msg->payload, ret);
+ }
+
+ return ret;
+}
+
+int qcom_pmic_typec_pdphy_pd_transmit(struct pmic_typec_pdphy *pmic_typec_pdphy,
+ enum tcpm_transmit_type type,
+ const struct pd_message *msg,
+ unsigned int negotiated_rev)
+{
+ struct device *dev = pmic_typec_pdphy->dev;
+ int ret;
+
+ if (msg) {
+ ret = qcom_pmic_typec_pdphy_pd_transmit_payload(pmic_typec_pdphy,
+ type, msg,
+ negotiated_rev);
+ } else {
+ ret = qcom_pmic_typec_pdphy_pd_transmit_signal(pmic_typec_pdphy,
+ type,
+ negotiated_rev);
+ }
+
+ if (ret)
+ dev_dbg(dev, "pd_transmit: type %x result %d\n", type, ret);
+
+ return ret;
+}
+
+static void qcom_pmic_typec_pdphy_pd_receive(struct pmic_typec_pdphy *pmic_typec_pdphy)
+{
+ struct device *dev = pmic_typec_pdphy->dev;
+ struct pd_message msg;
+ unsigned int size, rx_status;
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&pmic_typec_pdphy->lock, flags);
+
+ ret = regmap_read(pmic_typec_pdphy->regmap,
+ pmic_typec_pdphy->base + USB_PDPHY_RX_SIZE_REG, &size);
+ if (ret)
+ goto done;
+
+ /* Hardware requires +1 of the real read value to be passed */
+ if (size < 1 || size > sizeof(msg.payload) + 1) {
+ dev_dbg(dev, "pd_receive: invalid size %d\n", size);
+ goto done;
+ }
+
+ size += 1;
+ ret = regmap_read(pmic_typec_pdphy->regmap,
+ pmic_typec_pdphy->base + USB_PDPHY_RX_STATUS_REG,
+ &rx_status);
+
+ if (ret)
+ goto done;
+
+ ret = regmap_bulk_read(pmic_typec_pdphy->regmap,
+ pmic_typec_pdphy->base + USB_PDPHY_RX_BUFFER_REG,
+ (u8 *)&msg, size);
+ if (ret)
+ goto done;
+
+ /* Return ownership of RX buffer to hardware */
+ ret = regmap_write(pmic_typec_pdphy->regmap,
+ pmic_typec_pdphy->base + USB_PDPHY_RX_ACKNOWLEDGE_REG, 0);
+
+done:
+ spin_unlock_irqrestore(&pmic_typec_pdphy->lock, flags);
+
+ if (!ret) {
+ dev_vdbg(dev, "pd_receive: handing %d bytes to tcpm\n", size);
+ tcpm_pd_receive(pmic_typec_pdphy->tcpm_port, &msg);
+ }
+}
+
+static irqreturn_t qcom_pmic_typec_pdphy_isr(int irq, void *dev_id)
+{
+ struct pmic_typec_pdphy_irq_data *irq_data = dev_id;
+ struct pmic_typec_pdphy *pmic_typec_pdphy = irq_data->pmic_typec_pdphy;
+ struct device *dev = pmic_typec_pdphy->dev;
+
+ switch (irq_data->virq) {
+ case PMIC_PDPHY_SIG_TX_IRQ:
+ dev_err(dev, "isr: tx_sig\n");
+ break;
+ case PMIC_PDPHY_SIG_RX_IRQ:
+ schedule_work(&pmic_typec_pdphy->reset_work);
+ break;
+ case PMIC_PDPHY_MSG_TX_IRQ:
+ tcpm_pd_transmit_complete(pmic_typec_pdphy->tcpm_port,
+ TCPC_TX_SUCCESS);
+ break;
+ case PMIC_PDPHY_MSG_RX_IRQ:
+ qcom_pmic_typec_pdphy_pd_receive(pmic_typec_pdphy);
+ break;
+ case PMIC_PDPHY_MSG_TX_FAIL_IRQ:
+ tcpm_pd_transmit_complete(pmic_typec_pdphy->tcpm_port,
+ TCPC_TX_FAILED);
+ break;
+ case PMIC_PDPHY_MSG_TX_DISCARD_IRQ:
+ tcpm_pd_transmit_complete(pmic_typec_pdphy->tcpm_port,
+ TCPC_TX_DISCARDED);
+ break;
+ }
+
+ return IRQ_HANDLED;
+}
+
+int qcom_pmic_typec_pdphy_set_pd_rx(struct pmic_typec_pdphy *pmic_typec_pdphy, bool on)
+{
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&pmic_typec_pdphy->lock, flags);
+
+ ret = regmap_write(pmic_typec_pdphy->regmap,
+ pmic_typec_pdphy->base + USB_PDPHY_RX_ACKNOWLEDGE_REG, !on);
+
+ spin_unlock_irqrestore(&pmic_typec_pdphy->lock, flags);
+
+ dev_dbg(pmic_typec_pdphy->dev, "set_pd_rx: %s\n", on ? "on" : "off");
+
+ return ret;
+}
+
+int qcom_pmic_typec_pdphy_set_roles(struct pmic_typec_pdphy *pmic_typec_pdphy,
+ bool data_role_host, bool power_role_src)
+{
+ struct device *dev = pmic_typec_pdphy->dev;
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&pmic_typec_pdphy->lock, flags);
+
+ ret = regmap_update_bits(pmic_typec_pdphy->regmap,
+ pmic_typec_pdphy->base + USB_PDPHY_MSG_CONFIG_REG,
+ MSG_CONFIG_PORT_DATA_ROLE |
+ MSG_CONFIG_PORT_POWER_ROLE,
+ data_role_host << 3 | power_role_src << 2);
+
+ spin_unlock_irqrestore(&pmic_typec_pdphy->lock, flags);
+
+ dev_dbg(dev, "pdphy_set_roles: data_role_host=%d power_role_src=%d\n",
+ data_role_host, power_role_src);
+
+ return ret;
+}
+
+static int qcom_pmic_typec_pdphy_enable(struct pmic_typec_pdphy *pmic_typec_pdphy)
+{
+ struct device *dev = pmic_typec_pdphy->dev;
+ int ret;
+
+ ret = regulator_enable(pmic_typec_pdphy->vdd_pdphy);
+ if (ret)
+ return ret;
+
+ /* PD 2.0, DR=TYPEC_DEVICE, PR=TYPEC_SINK */
+ ret = regmap_update_bits(pmic_typec_pdphy->regmap,
+ pmic_typec_pdphy->base + USB_PDPHY_MSG_CONFIG_REG,
+ MSG_CONFIG_SPEC_REV_MASK, PD_REV20);
+ if (ret)
+ goto done;
+
+ ret = regmap_write(pmic_typec_pdphy->regmap,
+ pmic_typec_pdphy->base + USB_PDPHY_EN_CONTROL_REG, 0);
+ if (ret)
+ goto done;
+
+ ret = regmap_write(pmic_typec_pdphy->regmap,
+ pmic_typec_pdphy->base + USB_PDPHY_EN_CONTROL_REG,
+ CONTROL_ENABLE);
+ if (ret)
+ goto done;
+
+ qcom_pmic_typec_pdphy_reset_off(pmic_typec_pdphy);
+done:
+ if (ret) {
+ regulator_disable(pmic_typec_pdphy->vdd_pdphy);
+ dev_err(dev, "pdphy_enable fail %d\n", ret);
+ }
+
+ return ret;
+}
+
+static int qcom_pmic_typec_pdphy_disable(struct pmic_typec_pdphy *pmic_typec_pdphy)
+{
+ int ret;
+
+ qcom_pmic_typec_pdphy_reset_on(pmic_typec_pdphy);
+
+ ret = regmap_write(pmic_typec_pdphy->regmap,
+ pmic_typec_pdphy->base + USB_PDPHY_EN_CONTROL_REG, 0);
+
+ regulator_disable(pmic_typec_pdphy->vdd_pdphy);
+
+ return ret;
+}
+
+static int pmic_typec_pdphy_reset(struct pmic_typec_pdphy *pmic_typec_pdphy)
+{
+ int ret;
+
+ ret = qcom_pmic_typec_pdphy_disable(pmic_typec_pdphy);
+ if (ret)
+ goto done;
+
+ usleep_range(400, 500);
+ ret = qcom_pmic_typec_pdphy_enable(pmic_typec_pdphy);
+done:
+ return ret;
+}
+
+int qcom_pmic_typec_pdphy_start(struct pmic_typec_pdphy *pmic_typec_pdphy,
+ struct tcpm_port *tcpm_port)
+{
+ int i;
+ int ret;
+
+ pmic_typec_pdphy->tcpm_port = tcpm_port;
+
+ ret = pmic_typec_pdphy_reset(pmic_typec_pdphy);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < pmic_typec_pdphy->nr_irqs; i++)
+ enable_irq(pmic_typec_pdphy->irq_data[i].irq);
+
+ return 0;
+}
+
+void qcom_pmic_typec_pdphy_stop(struct pmic_typec_pdphy *pmic_typec_pdphy)
+{
+ int i;
+
+ for (i = 0; i < pmic_typec_pdphy->nr_irqs; i++)
+ disable_irq(pmic_typec_pdphy->irq_data[i].irq);
+
+ qcom_pmic_typec_pdphy_reset_on(pmic_typec_pdphy);
+}
+
+struct pmic_typec_pdphy *qcom_pmic_typec_pdphy_alloc(struct device *dev)
+{
+ return devm_kzalloc(dev, sizeof(struct pmic_typec_pdphy), GFP_KERNEL);
+}
+
+int qcom_pmic_typec_pdphy_probe(struct platform_device *pdev,
+ struct pmic_typec_pdphy *pmic_typec_pdphy,
+ struct pmic_typec_pdphy_resources *res,
+ struct regmap *regmap,
+ u32 base)
+{
+ struct device *dev = &pdev->dev;
+ struct pmic_typec_pdphy_irq_data *irq_data;
+ int i, ret, irq;
+
+ if (!res->nr_irqs || res->nr_irqs > PMIC_PDPHY_MAX_IRQS)
+ return -EINVAL;
+
+ irq_data = devm_kzalloc(dev, sizeof(*irq_data) * res->nr_irqs,
+ GFP_KERNEL);
+ if (!irq_data)
+ return -ENOMEM;
+
+ pmic_typec_pdphy->vdd_pdphy = devm_regulator_get(dev, "vdd-pdphy");
+ if (IS_ERR(pmic_typec_pdphy->vdd_pdphy))
+ return PTR_ERR(pmic_typec_pdphy->vdd_pdphy);
+
+ pmic_typec_pdphy->dev = dev;
+ pmic_typec_pdphy->base = base;
+ pmic_typec_pdphy->regmap = regmap;
+ pmic_typec_pdphy->nr_irqs = res->nr_irqs;
+ pmic_typec_pdphy->irq_data = irq_data;
+ spin_lock_init(&pmic_typec_pdphy->lock);
+ INIT_WORK(&pmic_typec_pdphy->reset_work, qcom_pmic_typec_pdphy_sig_reset_work);
+
+ for (i = 0; i < res->nr_irqs; i++, irq_data++) {
+ irq = platform_get_irq_byname(pdev, res->irq_params[i].irq_name);
+ if (irq < 0)
+ return irq;
+
+ irq_data->pmic_typec_pdphy = pmic_typec_pdphy;
+ irq_data->irq = irq;
+ irq_data->virq = res->irq_params[i].virq;
+
+ ret = devm_request_threaded_irq(dev, irq, NULL,
+ qcom_pmic_typec_pdphy_isr,
+ IRQF_ONESHOT | IRQF_NO_AUTOEN,
+ res->irq_params[i].irq_name,
+ irq_data);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.h b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.h
new file mode 100644
index 000000000000..e67954e31b14
--- /dev/null
+++ b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.h
@@ -0,0 +1,119 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2023, Linaro Ltd. All rights reserved.
+ */
+#ifndef __QCOM_PMIC_PDPHY_H__
+#define __QCOM_PMIC_PDPHY_H__
+
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/usb/tcpm.h>
+
+#define USB_PDPHY_MAX_DATA_OBJ_LEN 28
+#define USB_PDPHY_MSG_HDR_LEN 2
+
+/* PD PHY register offsets and bit fields */
+#define USB_PDPHY_MSG_CONFIG_REG 0x40
+#define MSG_CONFIG_PORT_DATA_ROLE BIT(3)
+#define MSG_CONFIG_PORT_POWER_ROLE BIT(2)
+#define MSG_CONFIG_SPEC_REV_MASK (BIT(1) | BIT(0))
+
+#define USB_PDPHY_EN_CONTROL_REG 0x46
+#define CONTROL_ENABLE BIT(0)
+
+#define USB_PDPHY_RX_STATUS_REG 0x4A
+#define RX_FRAME_TYPE (BIT(0) | BIT(1) | BIT(2))
+
+#define USB_PDPHY_FRAME_FILTER_REG 0x4C
+#define FRAME_FILTER_EN_HARD_RESET BIT(5)
+#define FRAME_FILTER_EN_SOP BIT(0)
+
+#define USB_PDPHY_TX_SIZE_REG 0x42
+#define TX_SIZE_MASK 0xF
+
+#define USB_PDPHY_TX_CONTROL_REG 0x44
+#define TX_CONTROL_RETRY_COUNT(n) (((n) & 0x3) << 5)
+#define TX_CONTROL_FRAME_TYPE(n) (((n) & 0x7) << 2)
+#define TX_CONTROL_FRAME_TYPE_CABLE_RESET (0x1 << 2)
+#define TX_CONTROL_SEND_SIGNAL BIT(1)
+#define TX_CONTROL_SEND_MSG BIT(0)
+
+#define USB_PDPHY_RX_SIZE_REG 0x48
+
+#define USB_PDPHY_RX_ACKNOWLEDGE_REG 0x4B
+#define RX_BUFFER_TOKEN BIT(0)
+
+#define USB_PDPHY_BIST_MODE_REG 0x4E
+#define BIST_MODE_MASK 0xF
+#define BIST_ENABLE BIT(7)
+#define PD_MSG_BIST 0x3
+#define PD_BIST_TEST_DATA_MODE 0x8
+
+#define USB_PDPHY_TX_BUFFER_HDR_REG 0x60
+#define USB_PDPHY_TX_BUFFER_DATA_REG 0x62
+
+#define USB_PDPHY_RX_BUFFER_REG 0x80
+
+/* VDD regulator */
+#define VDD_PDPHY_VOL_MIN 2800000 /* uV */
+#define VDD_PDPHY_VOL_MAX 3300000 /* uV */
+#define VDD_PDPHY_HPM_LOAD 3000 /* uA */
+
+/* Message Spec Rev field */
+#define PD_MSG_HDR_REV(hdr) (((hdr) >> 6) & 3)
+
+/* timers */
+#define RECEIVER_RESPONSE_TIME 15 /* tReceiverResponse */
+#define HARD_RESET_COMPLETE_TIME 5 /* tHardResetComplete */
+
+/* Interrupt numbers */
+#define PMIC_PDPHY_SIG_TX_IRQ 0x0
+#define PMIC_PDPHY_SIG_RX_IRQ 0x1
+#define PMIC_PDPHY_MSG_TX_IRQ 0x2
+#define PMIC_PDPHY_MSG_RX_IRQ 0x3
+#define PMIC_PDPHY_MSG_TX_FAIL_IRQ 0x4
+#define PMIC_PDPHY_MSG_TX_DISCARD_IRQ 0x5
+#define PMIC_PDPHY_MSG_RX_DISCARD_IRQ 0x6
+#define PMIC_PDPHY_FR_SWAP_IRQ 0x7
+
+/* Resources */
+#define PMIC_PDPHY_MAX_IRQS 0x08
+
+struct pmic_typec_pdphy_irq_params {
+ int virq;
+ char *irq_name;
+};
+
+struct pmic_typec_pdphy_resources {
+ unsigned int nr_irqs;
+ struct pmic_typec_pdphy_irq_params irq_params[PMIC_PDPHY_MAX_IRQS];
+};
+
+/* API */
+struct pmic_typec_pdphy;
+
+struct pmic_typec_pdphy *qcom_pmic_typec_pdphy_alloc(struct device *dev);
+
+int qcom_pmic_typec_pdphy_probe(struct platform_device *pdev,
+ struct pmic_typec_pdphy *pmic_typec_pdphy,
+ struct pmic_typec_pdphy_resources *res,
+ struct regmap *regmap,
+ u32 base);
+
+int qcom_pmic_typec_pdphy_start(struct pmic_typec_pdphy *pmic_typec_pdphy,
+ struct tcpm_port *tcpm_port);
+
+void qcom_pmic_typec_pdphy_stop(struct pmic_typec_pdphy *pmic_typec_pdphy);
+
+int qcom_pmic_typec_pdphy_set_roles(struct pmic_typec_pdphy *pmic_typec_pdphy,
+ bool power_role_src, bool data_role_host);
+
+int qcom_pmic_typec_pdphy_set_pd_rx(struct pmic_typec_pdphy *pmic_typec_pdphy, bool on);
+
+int qcom_pmic_typec_pdphy_pd_transmit(struct pmic_typec_pdphy *pmic_typec_pdphy,
+ enum tcpm_transmit_type type,
+ const struct pd_message *msg,
+ unsigned int negotiated_rev);
+
+#endif /* __QCOM_PMIC_TYPEC_PDPHY_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
new file mode 100644
index 000000000000..94285f64b67d
--- /dev/null
+++ b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c
@@ -0,0 +1,556 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2023, Linaro Ltd. All rights reserved.
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#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>
+#include <linux/slab.h>
+#include <linux/usb/tcpm.h>
+#include <linux/usb/typec_mux.h>
+#include <linux/workqueue.h>
+#include "qcom_pmic_typec_port.h"
+
+struct pmic_typec_port_irq_data {
+ int virq;
+ int irq;
+ struct pmic_typec_port *pmic_typec_port;
+};
+
+struct pmic_typec_port {
+ struct device *dev;
+ struct tcpm_port *tcpm_port;
+ struct regmap *regmap;
+ u32 base;
+ unsigned int nr_irqs;
+ struct pmic_typec_port_irq_data *irq_data;
+
+ struct regulator *vdd_vbus;
+
+ int cc;
+ bool debouncing_cc;
+ struct delayed_work cc_debounce_dwork;
+
+ spinlock_t lock; /* Register atomicity */
+};
+
+static const char * const typec_cc_status_name[] = {
+ [TYPEC_CC_OPEN] = "Open",
+ [TYPEC_CC_RA] = "Ra",
+ [TYPEC_CC_RD] = "Rd",
+ [TYPEC_CC_RP_DEF] = "Rp-def",
+ [TYPEC_CC_RP_1_5] = "Rp-1.5",
+ [TYPEC_CC_RP_3_0] = "Rp-3.0",
+};
+
+static const char *rp_unknown = "unknown";
+
+static const char *cc_to_name(enum typec_cc_status cc)
+{
+ if (cc > TYPEC_CC_RP_3_0)
+ return rp_unknown;
+
+ return typec_cc_status_name[cc];
+}
+
+static const char * const rp_sel_name[] = {
+ [TYPEC_SRC_RP_SEL_80UA] = "Rp-def-80uA",
+ [TYPEC_SRC_RP_SEL_180UA] = "Rp-1.5-180uA",
+ [TYPEC_SRC_RP_SEL_330UA] = "Rp-3.0-330uA",
+};
+
+static const char *rp_sel_to_name(int rp_sel)
+{
+ if (rp_sel > TYPEC_SRC_RP_SEL_330UA)
+ return rp_unknown;
+
+ return rp_sel_name[rp_sel];
+}
+
+#define misc_to_cc(msic) !!(misc & CC_ORIENTATION) ? "cc1" : "cc2"
+#define misc_to_vconn(msic) !!(misc & CC_ORIENTATION) ? "cc2" : "cc1"
+
+static void qcom_pmic_typec_port_cc_debounce(struct work_struct *work)
+{
+ struct pmic_typec_port *pmic_typec_port =
+ container_of(work, struct pmic_typec_port, cc_debounce_dwork.work);
+ unsigned long flags;
+
+ spin_lock_irqsave(&pmic_typec_port->lock, flags);
+ pmic_typec_port->debouncing_cc = false;
+ spin_unlock_irqrestore(&pmic_typec_port->lock, flags);
+
+ dev_dbg(pmic_typec_port->dev, "Debounce cc complete\n");
+}
+
+static irqreturn_t pmic_typec_port_isr(int irq, void *dev_id)
+{
+ struct pmic_typec_port_irq_data *irq_data = dev_id;
+ struct pmic_typec_port *pmic_typec_port = irq_data->pmic_typec_port;
+ u32 misc_stat;
+ bool vbus_change = false;
+ bool cc_change = false;
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&pmic_typec_port->lock, flags);
+
+ ret = regmap_read(pmic_typec_port->regmap,
+ pmic_typec_port->base + TYPEC_MISC_STATUS_REG,
+ &misc_stat);
+ if (ret)
+ goto done;
+
+ switch (irq_data->virq) {
+ case PMIC_TYPEC_VBUS_IRQ:
+ vbus_change = true;
+ break;
+ case PMIC_TYPEC_CC_STATE_IRQ:
+ case PMIC_TYPEC_ATTACH_DETACH_IRQ:
+ if (!pmic_typec_port->debouncing_cc)
+ cc_change = true;
+ break;
+ }
+
+done:
+ spin_unlock_irqrestore(&pmic_typec_port->lock, flags);
+
+ if (vbus_change)
+ tcpm_vbus_change(pmic_typec_port->tcpm_port);
+
+ if (cc_change)
+ tcpm_cc_change(pmic_typec_port->tcpm_port);
+
+ return IRQ_HANDLED;
+}
+
+int qcom_pmic_typec_port_get_vbus(struct pmic_typec_port *pmic_typec_port)
+{
+ struct device *dev = pmic_typec_port->dev;
+ unsigned int misc;
+ int ret;
+
+ ret = regmap_read(pmic_typec_port->regmap,
+ pmic_typec_port->base + TYPEC_MISC_STATUS_REG,
+ &misc);
+ if (ret)
+ misc = 0;
+
+ dev_dbg(dev, "get_vbus: 0x%08x detect %d\n", misc, !!(misc & TYPEC_VBUS_DETECT));
+
+ return !!(misc & TYPEC_VBUS_DETECT);
+}
+
+int qcom_pmic_typec_port_set_vbus(struct pmic_typec_port *pmic_typec_port, bool on)
+{
+ u32 sm_stat;
+ u32 val;
+ int ret;
+
+ if (on) {
+ ret = regulator_enable(pmic_typec_port->vdd_vbus);
+ if (ret)
+ return ret;
+
+ val = TYPEC_SM_VBUS_VSAFE5V;
+ } else {
+ ret = regulator_disable(pmic_typec_port->vdd_vbus);
+ if (ret)
+ return ret;
+
+ val = TYPEC_SM_VBUS_VSAFE0V;
+ }
+
+ /* Poll waiting for transition to required vSafe5V or vSafe0V */
+ ret = regmap_read_poll_timeout(pmic_typec_port->regmap,
+ pmic_typec_port->base + TYPEC_SM_STATUS_REG,
+ sm_stat, sm_stat & val,
+ 100, 250000);
+ if (ret)
+ dev_warn(pmic_typec_port->dev, "vbus vsafe%dv fail\n", on ? 5 : 0);
+
+ return 0;
+}
+
+int qcom_pmic_typec_port_get_cc(struct pmic_typec_port *pmic_typec_port,
+ enum typec_cc_status *cc1,
+ enum typec_cc_status *cc2)
+{
+ struct device *dev = pmic_typec_port->dev;
+ unsigned int misc, val;
+ bool attached;
+ int ret = 0;
+
+ ret = regmap_read(pmic_typec_port->regmap,
+ pmic_typec_port->base + TYPEC_MISC_STATUS_REG, &misc);
+ if (ret)
+ goto done;
+
+ attached = !!(misc & CC_ATTACHED);
+
+ if (pmic_typec_port->debouncing_cc) {
+ ret = -EBUSY;
+ goto done;
+ }
+
+ *cc1 = TYPEC_CC_OPEN;
+ *cc2 = TYPEC_CC_OPEN;
+
+ if (!attached)
+ goto done;
+
+ if (misc & SNK_SRC_MODE) {
+ ret = regmap_read(pmic_typec_port->regmap,
+ pmic_typec_port->base + TYPEC_SRC_STATUS_REG,
+ &val);
+ if (ret)
+ goto done;
+ switch (val & DETECTED_SRC_TYPE_MASK) {
+ case SRC_RD_OPEN:
+ val = TYPEC_CC_RD;
+ break;
+ case SRC_RD_RA_VCONN:
+ val = TYPEC_CC_RD;
+ *cc1 = TYPEC_CC_RA;
+ *cc2 = TYPEC_CC_RA;
+ break;
+ default:
+ dev_warn(dev, "unexpected src status %.2x\n", val);
+ val = TYPEC_CC_RD;
+ break;
+ }
+ } else {
+ ret = regmap_read(pmic_typec_port->regmap,
+ pmic_typec_port->base + TYPEC_SNK_STATUS_REG,
+ &val);
+ if (ret)
+ goto done;
+ switch (val & DETECTED_SNK_TYPE_MASK) {
+ case SNK_RP_STD:
+ val = TYPEC_CC_RP_DEF;
+ break;
+ case SNK_RP_1P5:
+ val = TYPEC_CC_RP_1_5;
+ break;
+ case SNK_RP_3P0:
+ val = TYPEC_CC_RP_3_0;
+ break;
+ default:
+ dev_warn(dev, "unexpected snk status %.2x\n", val);
+ val = TYPEC_CC_RP_DEF;
+ break;
+ }
+ val = TYPEC_CC_RP_DEF;
+ }
+
+ if (misc & CC_ORIENTATION)
+ *cc2 = val;
+ else
+ *cc1 = val;
+
+done:
+ dev_dbg(dev, "get_cc: misc 0x%08x cc1 0x%08x %s cc2 0x%08x %s attached %d cc=%s\n",
+ misc, *cc1, cc_to_name(*cc1), *cc2, cc_to_name(*cc2), attached,
+ misc_to_cc(misc));
+
+ return ret;
+}
+
+static void qcom_pmic_set_cc_debounce(struct pmic_typec_port *pmic_typec_port)
+{
+ pmic_typec_port->debouncing_cc = true;
+ schedule_delayed_work(&pmic_typec_port->cc_debounce_dwork,
+ msecs_to_jiffies(2));
+}
+
+int qcom_pmic_typec_port_set_cc(struct pmic_typec_port *pmic_typec_port,
+ enum typec_cc_status cc)
+{
+ struct device *dev = pmic_typec_port->dev;
+ unsigned int mode, currsrc;
+ unsigned int misc;
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&pmic_typec_port->lock, flags);
+
+ ret = regmap_read(pmic_typec_port->regmap,
+ pmic_typec_port->base + TYPEC_MISC_STATUS_REG,
+ &misc);
+ if (ret)
+ goto done;
+
+ mode = EN_SRC_ONLY;
+
+ switch (cc) {
+ case TYPEC_CC_OPEN:
+ currsrc = TYPEC_SRC_RP_SEL_80UA;
+ break;
+ case TYPEC_CC_RP_DEF:
+ currsrc = TYPEC_SRC_RP_SEL_80UA;
+ break;
+ case TYPEC_CC_RP_1_5:
+ currsrc = TYPEC_SRC_RP_SEL_180UA;
+ break;
+ case TYPEC_CC_RP_3_0:
+ currsrc = TYPEC_SRC_RP_SEL_330UA;
+ break;
+ case TYPEC_CC_RD:
+ currsrc = TYPEC_SRC_RP_SEL_80UA;
+ mode = EN_SNK_ONLY;
+ break;
+ default:
+ dev_warn(dev, "unexpected set_cc %d\n", cc);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (mode == EN_SRC_ONLY) {
+ ret = regmap_write(pmic_typec_port->regmap,
+ pmic_typec_port->base + TYPEC_CURRSRC_CFG_REG,
+ currsrc);
+ if (ret)
+ goto done;
+ }
+
+ pmic_typec_port->cc = cc;
+ qcom_pmic_set_cc_debounce(pmic_typec_port);
+ ret = 0;
+
+done:
+ spin_unlock_irqrestore(&pmic_typec_port->lock, flags);
+
+ dev_dbg(dev, "set_cc: currsrc=%x %s mode %s debounce %d attached %d cc=%s\n",
+ currsrc, rp_sel_to_name(currsrc),
+ mode == EN_SRC_ONLY ? "EN_SRC_ONLY" : "EN_SNK_ONLY",
+ pmic_typec_port->debouncing_cc, !!(misc & CC_ATTACHED),
+ misc_to_cc(misc));
+
+ return ret;
+}
+
+int qcom_pmic_typec_port_set_vconn(struct pmic_typec_port *pmic_typec_port, bool on)
+{
+ struct device *dev = pmic_typec_port->dev;
+ unsigned int orientation, misc, mask, value;
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&pmic_typec_port->lock, flags);
+
+ ret = regmap_read(pmic_typec_port->regmap,
+ pmic_typec_port->base + TYPEC_MISC_STATUS_REG, &misc);
+ if (ret)
+ goto done;
+
+ /* Set VCONN on the inversion of the active CC channel */
+ orientation = (misc & CC_ORIENTATION) ? 0 : VCONN_EN_ORIENTATION;
+ if (on) {
+ mask = VCONN_EN_ORIENTATION | VCONN_EN_VALUE;
+ value = orientation | VCONN_EN_VALUE | VCONN_EN_SRC;
+ } else {
+ mask = VCONN_EN_VALUE;
+ value = 0;
+ }
+
+ ret = regmap_update_bits(pmic_typec_port->regmap,
+ pmic_typec_port->base + TYPEC_VCONN_CONTROL_REG,
+ mask, value);
+done:
+ spin_unlock_irqrestore(&pmic_typec_port->lock, flags);
+
+ dev_dbg(dev, "set_vconn: orientation %d control 0x%08x state %s cc %s vconn %s\n",
+ orientation, value, on ? "on" : "off", misc_to_vconn(misc), misc_to_cc(misc));
+
+ return ret;
+}
+
+int qcom_pmic_typec_port_start_toggling(struct pmic_typec_port *pmic_typec_port,
+ enum typec_port_type port_type,
+ enum typec_cc_status cc)
+{
+ struct device *dev = pmic_typec_port->dev;
+ unsigned int misc;
+ u8 mode = 0;
+ unsigned long flags;
+ int ret;
+
+ switch (port_type) {
+ case TYPEC_PORT_SRC:
+ mode = EN_SRC_ONLY;
+ break;
+ case TYPEC_PORT_SNK:
+ mode = EN_SNK_ONLY;
+ break;
+ case TYPEC_PORT_DRP:
+ mode = EN_TRY_SNK;
+ break;
+ }
+
+ spin_lock_irqsave(&pmic_typec_port->lock, flags);
+
+ ret = regmap_read(pmic_typec_port->regmap,
+ pmic_typec_port->base + TYPEC_MISC_STATUS_REG, &misc);
+ if (ret)
+ goto done;
+
+ dev_dbg(dev, "start_toggling: misc 0x%08x attached %d port_type %d current cc %d new %d\n",
+ misc, !!(misc & CC_ATTACHED), port_type, pmic_typec_port->cc, cc);
+
+ qcom_pmic_set_cc_debounce(pmic_typec_port);
+
+ /* force it to toggle at least once */
+ ret = regmap_write(pmic_typec_port->regmap,
+ pmic_typec_port->base + TYPEC_MODE_CFG_REG,
+ TYPEC_DISABLE_CMD);
+ if (ret)
+ goto done;
+
+ ret = regmap_write(pmic_typec_port->regmap,
+ pmic_typec_port->base + TYPEC_MODE_CFG_REG,
+ mode);
+done:
+ spin_unlock_irqrestore(&pmic_typec_port->lock, flags);
+
+ return ret;
+}
+
+#define TYPEC_INTR_EN_CFG_1_MASK \
+ (TYPEC_LEGACY_CABLE_INT_EN | \
+ TYPEC_NONCOMPLIANT_LEGACY_CABLE_INT_EN | \
+ TYPEC_TRYSOURCE_DETECT_INT_EN | \
+ TYPEC_TRYSINK_DETECT_INT_EN | \
+ TYPEC_CCOUT_DETACH_INT_EN | \
+ TYPEC_CCOUT_ATTACH_INT_EN | \
+ TYPEC_VBUS_DEASSERT_INT_EN | \
+ TYPEC_VBUS_ASSERT_INT_EN)
+
+#define TYPEC_INTR_EN_CFG_2_MASK \
+ (TYPEC_STATE_MACHINE_CHANGE_INT_EN | TYPEC_VBUS_ERROR_INT_EN | \
+ TYPEC_DEBOUNCE_DONE_INT_EN)
+
+int qcom_pmic_typec_port_start(struct pmic_typec_port *pmic_typec_port,
+ struct tcpm_port *tcpm_port)
+{
+ int i;
+ int mask;
+ int ret;
+
+ /* Configure interrupt sources */
+ ret = regmap_write(pmic_typec_port->regmap,
+ pmic_typec_port->base + TYPEC_INTERRUPT_EN_CFG_1_REG,
+ TYPEC_INTR_EN_CFG_1_MASK);
+ if (ret)
+ goto done;
+
+ ret = regmap_write(pmic_typec_port->regmap,
+ pmic_typec_port->base + TYPEC_INTERRUPT_EN_CFG_2_REG,
+ TYPEC_INTR_EN_CFG_2_MASK);
+ if (ret)
+ goto done;
+
+ /* start in TRY_SNK mode */
+ ret = regmap_write(pmic_typec_port->regmap,
+ pmic_typec_port->base + TYPEC_MODE_CFG_REG, EN_TRY_SNK);
+ if (ret)
+ goto done;
+
+ /* Configure VCONN for software control */
+ ret = regmap_update_bits(pmic_typec_port->regmap,
+ pmic_typec_port->base + TYPEC_VCONN_CONTROL_REG,
+ VCONN_EN_SRC | VCONN_EN_VALUE, VCONN_EN_SRC);
+ if (ret)
+ goto done;
+
+ /* Set CC threshold to 1.6 Volts | tPDdebounce = 10-20ms */
+ mask = SEL_SRC_UPPER_REF | USE_TPD_FOR_EXITING_ATTACHSRC;
+ ret = regmap_update_bits(pmic_typec_port->regmap,
+ pmic_typec_port->base + TYPEC_EXIT_STATE_CFG_REG,
+ mask, mask);
+ if (ret)
+ goto done;
+
+ pmic_typec_port->tcpm_port = tcpm_port;
+
+ for (i = 0; i < pmic_typec_port->nr_irqs; i++)
+ enable_irq(pmic_typec_port->irq_data[i].irq);
+
+done:
+ return ret;
+}
+
+void qcom_pmic_typec_port_stop(struct pmic_typec_port *pmic_typec_port)
+{
+ int i;
+
+ for (i = 0; i < pmic_typec_port->nr_irqs; i++)
+ disable_irq(pmic_typec_port->irq_data[i].irq);
+}
+
+struct pmic_typec_port *qcom_pmic_typec_port_alloc(struct device *dev)
+{
+ return devm_kzalloc(dev, sizeof(struct pmic_typec_port), GFP_KERNEL);
+}
+
+int qcom_pmic_typec_port_probe(struct platform_device *pdev,
+ struct pmic_typec_port *pmic_typec_port,
+ struct pmic_typec_port_resources *res,
+ struct regmap *regmap,
+ u32 base)
+{
+ struct device *dev = &pdev->dev;
+ struct pmic_typec_port_irq_data *irq_data;
+ int i, ret, irq;
+
+ if (!res->nr_irqs || res->nr_irqs > PMIC_TYPEC_MAX_IRQS)
+ return -EINVAL;
+
+ irq_data = devm_kzalloc(dev, sizeof(*irq_data) * res->nr_irqs,
+ GFP_KERNEL);
+ if (!irq_data)
+ return -ENOMEM;
+
+ pmic_typec_port->vdd_vbus = devm_regulator_get(dev, "vdd-vbus");
+ if (IS_ERR(pmic_typec_port->vdd_vbus))
+ return PTR_ERR(pmic_typec_port->vdd_vbus);
+
+ pmic_typec_port->dev = dev;
+ pmic_typec_port->base = base;
+ pmic_typec_port->regmap = regmap;
+ pmic_typec_port->nr_irqs = res->nr_irqs;
+ pmic_typec_port->irq_data = irq_data;
+ spin_lock_init(&pmic_typec_port->lock);
+ INIT_DELAYED_WORK(&pmic_typec_port->cc_debounce_dwork,
+ qcom_pmic_typec_port_cc_debounce);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ for (i = 0; i < res->nr_irqs; i++, irq_data++) {
+ irq = platform_get_irq_byname(pdev,
+ res->irq_params[i].irq_name);
+ if (irq < 0)
+ return irq;
+
+ irq_data->pmic_typec_port = pmic_typec_port;
+ irq_data->irq = irq;
+ irq_data->virq = res->irq_params[i].virq;
+ ret = devm_request_threaded_irq(dev, irq, NULL, pmic_typec_port_isr,
+ IRQF_ONESHOT | IRQF_NO_AUTOEN,
+ res->irq_params[i].irq_name,
+ irq_data);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.h b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.h
new file mode 100644
index 000000000000..d4d358c680b6
--- /dev/null
+++ b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.h
@@ -0,0 +1,195 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2023, Linaro Ltd. All rights reserved.
+ */
+#ifndef __QCOM_PMIC_TYPEC_H__
+#define __QCOM_PMIC_TYPEC_H__
+
+#include <linux/platform_device.h>
+#include <linux/usb/tcpm.h>
+
+#define TYPEC_SNK_STATUS_REG 0x06
+#define DETECTED_SNK_TYPE_MASK GENMASK(6, 0)
+#define SNK_DAM_MASK GENMASK(6, 4)
+#define SNK_DAM_500MA BIT(6)
+#define SNK_DAM_1500MA BIT(5)
+#define SNK_DAM_3000MA BIT(4)
+#define SNK_RP_STD BIT(3)
+#define SNK_RP_1P5 BIT(2)
+#define SNK_RP_3P0 BIT(1)
+#define SNK_RP_SHORT BIT(0)
+
+#define TYPEC_SRC_STATUS_REG 0x08
+#define DETECTED_SRC_TYPE_MASK GENMASK(4, 0)
+#define SRC_HIGH_BATT BIT(5)
+#define SRC_DEBUG_ACCESS BIT(4)
+#define SRC_RD_OPEN BIT(3)
+#define SRC_RD_RA_VCONN BIT(2)
+#define SRC_RA_OPEN BIT(1)
+#define AUDIO_ACCESS_RA_RA BIT(0)
+
+#define TYPEC_STATE_MACHINE_STATUS_REG 0x09
+#define TYPEC_ATTACH_DETACH_STATE BIT(5)
+
+#define TYPEC_SM_STATUS_REG 0x0A
+#define TYPEC_SM_VBUS_VSAFE5V BIT(5)
+#define TYPEC_SM_VBUS_VSAFE0V BIT(6)
+#define TYPEC_SM_USBIN_LT_LV BIT(7)
+
+#define TYPEC_MISC_STATUS_REG 0x0B
+#define TYPEC_WATER_DETECTION_STATUS BIT(7)
+#define SNK_SRC_MODE BIT(6)
+#define TYPEC_VBUS_DETECT BIT(5)
+#define TYPEC_VBUS_ERROR_STATUS BIT(4)
+#define TYPEC_DEBOUNCE_DONE BIT(3)
+#define CC_ORIENTATION BIT(1)
+#define CC_ATTACHED BIT(0)
+
+#define LEGACY_CABLE_STATUS_REG 0x0D
+#define TYPEC_LEGACY_CABLE_STATUS BIT(1)
+#define TYPEC_NONCOMP_LEGACY_CABLE_STATUS BIT(0)
+
+#define TYPEC_U_USB_STATUS_REG 0x0F
+#define U_USB_GROUND_NOVBUS BIT(6)
+#define U_USB_GROUND BIT(4)
+#define U_USB_FMB1 BIT(3)
+#define U_USB_FLOAT1 BIT(2)
+#define U_USB_FMB2 BIT(1)
+#define U_USB_FLOAT2 BIT(0)
+
+#define TYPEC_MODE_CFG_REG 0x44
+#define TYPEC_TRY_MODE_MASK GENMASK(4, 3)
+#define EN_TRY_SNK BIT(4)
+#define EN_TRY_SRC BIT(3)
+#define TYPEC_POWER_ROLE_CMD_MASK GENMASK(2, 0)
+#define EN_SRC_ONLY BIT(2)
+#define EN_SNK_ONLY BIT(1)
+#define TYPEC_DISABLE_CMD BIT(0)
+
+#define TYPEC_VCONN_CONTROL_REG 0x46
+#define VCONN_EN_ORIENTATION BIT(2)
+#define VCONN_EN_VALUE BIT(1)
+#define VCONN_EN_SRC BIT(0)
+
+#define TYPEC_CCOUT_CONTROL_REG 0x48
+#define TYPEC_CCOUT_BUFFER_EN BIT(2)
+#define TYPEC_CCOUT_VALUE BIT(1)
+#define TYPEC_CCOUT_SRC BIT(0)
+
+#define DEBUG_ACCESS_SRC_CFG_REG 0x4C
+#define EN_UNORIENTED_DEBUG_ACCESS_SRC BIT(0)
+
+#define TYPE_C_CRUDE_SENSOR_CFG_REG 0x4e
+#define EN_SRC_CRUDE_SENSOR BIT(1)
+#define EN_SNK_CRUDE_SENSOR BIT(0)
+
+#define TYPEC_EXIT_STATE_CFG_REG 0x50
+#define BYPASS_VSAFE0V_DURING_ROLE_SWAP BIT(3)
+#define SEL_SRC_UPPER_REF BIT(2)
+#define USE_TPD_FOR_EXITING_ATTACHSRC BIT(1)
+#define EXIT_SNK_BASED_ON_CC BIT(0)
+
+#define TYPEC_CURRSRC_CFG_REG 0x52
+#define TYPEC_SRC_RP_SEL_330UA BIT(1)
+#define TYPEC_SRC_RP_SEL_180UA BIT(0)
+#define TYPEC_SRC_RP_SEL_80UA 0
+#define TYPEC_SRC_RP_SEL_MASK GENMASK(1, 0)
+
+#define TYPEC_INTERRUPT_EN_CFG_1_REG 0x5E
+#define TYPEC_LEGACY_CABLE_INT_EN BIT(7)
+#define TYPEC_NONCOMPLIANT_LEGACY_CABLE_INT_EN BIT(6)
+#define TYPEC_TRYSOURCE_DETECT_INT_EN BIT(5)
+#define TYPEC_TRYSINK_DETECT_INT_EN BIT(4)
+#define TYPEC_CCOUT_DETACH_INT_EN BIT(3)
+#define TYPEC_CCOUT_ATTACH_INT_EN BIT(2)
+#define TYPEC_VBUS_DEASSERT_INT_EN BIT(1)
+#define TYPEC_VBUS_ASSERT_INT_EN BIT(0)
+
+#define TYPEC_INTERRUPT_EN_CFG_2_REG 0x60
+#define TYPEC_SRC_BATT_HPWR_INT_EN BIT(6)
+#define MICRO_USB_STATE_CHANGE_INT_EN BIT(5)
+#define TYPEC_STATE_MACHINE_CHANGE_INT_EN BIT(4)
+#define TYPEC_DEBUG_ACCESS_DETECT_INT_EN BIT(3)
+#define TYPEC_WATER_DETECTION_INT_EN BIT(2)
+#define TYPEC_VBUS_ERROR_INT_EN BIT(1)
+#define TYPEC_DEBOUNCE_DONE_INT_EN BIT(0)
+
+#define TYPEC_DEBOUNCE_OPTION_REG 0x62
+#define REDUCE_TCCDEBOUNCE_TO_2MS BIT(2)
+
+#define TYPE_C_SBU_CFG_REG 0x6A
+#define SEL_SBU1_ISRC_VAL 0x04
+#define SEL_SBU2_ISRC_VAL 0x01
+
+#define TYPEC_U_USB_CFG_REG 0x70
+#define EN_MICRO_USB_FACTORY_MODE BIT(1)
+#define EN_MICRO_USB_MODE BIT(0)
+
+#define TYPEC_PMI632_U_USB_WATER_PROTECTION_CFG_REG 0x72
+
+#define TYPEC_U_USB_WATER_PROTECTION_CFG_REG 0x73
+#define EN_MICRO_USB_WATER_PROTECTION BIT(4)
+#define MICRO_USB_DETECTION_ON_TIME_CFG_MASK GENMASK(3, 2)
+#define MICRO_USB_DETECTION_PERIOD_CFG_MASK GENMASK(1, 0)
+
+#define TYPEC_PMI632_MICRO_USB_MODE_REG 0x73
+#define MICRO_USB_MODE_ONLY BIT(0)
+
+/* Interrupt numbers */
+#define PMIC_TYPEC_OR_RID_IRQ 0x0
+#define PMIC_TYPEC_VPD_IRQ 0x1
+#define PMIC_TYPEC_CC_STATE_IRQ 0x2
+#define PMIC_TYPEC_VCONN_OC_IRQ 0x3
+#define PMIC_TYPEC_VBUS_IRQ 0x4
+#define PMIC_TYPEC_ATTACH_DETACH_IRQ 0x5
+#define PMIC_TYPEC_LEGACY_CABLE_IRQ 0x6
+#define PMIC_TYPEC_TRY_SNK_SRC_IRQ 0x7
+
+/* Resources */
+#define PMIC_TYPEC_MAX_IRQS 0x08
+
+struct pmic_typec_port_irq_params {
+ int virq;
+ char *irq_name;
+};
+
+struct pmic_typec_port_resources {
+ unsigned int nr_irqs;
+ struct pmic_typec_port_irq_params irq_params[PMIC_TYPEC_MAX_IRQS];
+};
+
+/* API */
+struct pmic_typec;
+
+struct pmic_typec_port *qcom_pmic_typec_port_alloc(struct device *dev);
+
+int qcom_pmic_typec_port_probe(struct platform_device *pdev,
+ struct pmic_typec_port *pmic_typec_port,
+ struct pmic_typec_port_resources *res,
+ struct regmap *regmap,
+ u32 base);
+
+int qcom_pmic_typec_port_start(struct pmic_typec_port *pmic_typec_port,
+ struct tcpm_port *tcpm_port);
+
+void qcom_pmic_typec_port_stop(struct pmic_typec_port *pmic_typec_port);
+
+int qcom_pmic_typec_port_get_cc(struct pmic_typec_port *pmic_typec_port,
+ enum typec_cc_status *cc1,
+ enum typec_cc_status *cc2);
+
+int qcom_pmic_typec_port_set_cc(struct pmic_typec_port *pmic_typec_port,
+ enum typec_cc_status cc);
+
+int qcom_pmic_typec_port_get_vbus(struct pmic_typec_port *pmic_typec_port);
+
+int qcom_pmic_typec_port_set_vconn(struct pmic_typec_port *pmic_typec_port, bool on);
+
+int qcom_pmic_typec_port_start_toggling(struct pmic_typec_port *pmic_typec_port,
+ enum typec_port_type port_type,
+ enum typec_cc_status cc);
+
+int qcom_pmic_typec_port_set_vbus(struct pmic_typec_port *pmic_typec_port, bool on);
+
+#endif /* __QCOM_PMIC_TYPE_C_PORT_H__ */
diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c
index 8da23240afbe..fc708c289a73 100644
--- a/drivers/usb/typec/tcpm/tcpci.c
+++ b/drivers/usb/typec/tcpm/tcpci.c
@@ -895,7 +895,7 @@ static struct i2c_driver tcpci_i2c_driver = {
.name = "tcpci",
.of_match_table = of_match_ptr(tcpci_of_match),
},
- .probe_new = tcpci_probe,
+ .probe = tcpci_probe,
.remove = tcpci_remove,
.id_table = tcpci_id,
};
diff --git a/drivers/usb/typec/tcpm/tcpci_maxim_core.c b/drivers/usb/typec/tcpm/tcpci_maxim_core.c
index f32cda2a5e3a..9454b12a073c 100644
--- a/drivers/usb/typec/tcpm/tcpci_maxim_core.c
+++ b/drivers/usb/typec/tcpm/tcpci_maxim_core.c
@@ -508,7 +508,7 @@ static struct i2c_driver max_tcpci_i2c_driver = {
.name = "maxtcpc",
.of_match_table = of_match_ptr(max_tcpci_of_match),
},
- .probe_new = max_tcpci_probe,
+ .probe = max_tcpci_probe,
.remove = max_tcpci_remove,
.id_table = max_tcpci_id,
};
diff --git a/drivers/usb/typec/tcpm/tcpci_mt6360.c b/drivers/usb/typec/tcpm/tcpci_mt6360.c
index 6fa8fd5c8041..02b7fd302265 100644
--- a/drivers/usb/typec/tcpm/tcpci_mt6360.c
+++ b/drivers/usb/typec/tcpm/tcpci_mt6360.c
@@ -178,13 +178,12 @@ static int mt6360_tcpc_probe(struct platform_device *pdev)
return 0;
}
-static int mt6360_tcpc_remove(struct platform_device *pdev)
+static void mt6360_tcpc_remove(struct platform_device *pdev)
{
struct mt6360_tcpc_info *mti = platform_get_drvdata(pdev);
disable_irq(mti->irq);
tcpci_unregister_port(mti->tcpci);
- return 0;
}
static int __maybe_unused mt6360_tcpc_suspend(struct device *dev)
@@ -222,7 +221,7 @@ static struct platform_driver mt6360_tcpc_driver = {
.of_match_table = mt6360_tcpc_of_id,
},
.probe = mt6360_tcpc_probe,
- .remove = mt6360_tcpc_remove,
+ .remove_new = mt6360_tcpc_remove,
};
module_platform_driver(mt6360_tcpc_driver);
diff --git a/drivers/usb/typec/tcpm/tcpci_mt6370.c b/drivers/usb/typec/tcpm/tcpci_mt6370.c
index c5bb201a5163..2a079464b398 100644
--- a/drivers/usb/typec/tcpm/tcpci_mt6370.c
+++ b/drivers/usb/typec/tcpm/tcpci_mt6370.c
@@ -178,12 +178,10 @@ static int mt6370_tcpc_probe(struct platform_device *pdev)
return 0;
}
-static int mt6370_tcpc_remove(struct platform_device *pdev)
+static void mt6370_tcpc_remove(struct platform_device *pdev)
{
dev_pm_clear_wake_irq(&pdev->dev);
device_init_wakeup(&pdev->dev, false);
-
- return 0;
}
static const struct of_device_id mt6370_tcpc_devid_table[] = {
@@ -198,7 +196,7 @@ static struct platform_driver mt6370_tcpc_driver = {
.of_match_table = mt6370_tcpc_devid_table,
},
.probe = mt6370_tcpc_probe,
- .remove = mt6370_tcpc_remove,
+ .remove_new = mt6370_tcpc_remove,
};
module_platform_driver(mt6370_tcpc_driver);
diff --git a/drivers/usb/typec/tcpm/tcpci_rt1711h.c b/drivers/usb/typec/tcpm/tcpci_rt1711h.c
index a0e9e3fe8564..17ebc5fb684f 100644
--- a/drivers/usb/typec/tcpm/tcpci_rt1711h.c
+++ b/drivers/usb/typec/tcpm/tcpci_rt1711h.c
@@ -412,7 +412,7 @@ static struct i2c_driver rt1711h_i2c_driver = {
.name = "rt1711h",
.of_match_table = of_match_ptr(rt1711h_of_match),
},
- .probe_new = rt1711h_probe,
+ .probe = rt1711h_probe,
.remove = rt1711h_remove,
.id_table = rt1711h_id,
};
diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
index 3c6b0c8e2d3a..829d75ebab42 100644
--- a/drivers/usb/typec/tcpm/tcpm.c
+++ b/drivers/usb/typec/tcpm/tcpm.c
@@ -4885,7 +4885,8 @@ static void run_state_machine(struct tcpm_port *port)
break;
case PORT_RESET:
tcpm_reset_port(port);
- tcpm_set_cc(port, TYPEC_CC_OPEN);
+ tcpm_set_cc(port, tcpm_default_state(port) == SNK_UNATTACHED ?
+ TYPEC_CC_RD : tcpm_rp_cc(port));
tcpm_set_state(port, PORT_RESET_WAIT_OFF,
PD_T_ERROR_RECOVERY);
break;
@@ -6339,6 +6340,27 @@ static int tcpm_psy_get_current_now(struct tcpm_port *port,
return 0;
}
+static int tcpm_psy_get_input_power_limit(struct tcpm_port *port,
+ union power_supply_propval *val)
+{
+ unsigned int src_mv, src_ma, max_src_uw = 0;
+ unsigned int i, tmp;
+
+ for (i = 0; i < port->nr_source_caps; i++) {
+ u32 pdo = port->source_caps[i];
+
+ if (pdo_type(pdo) == PDO_TYPE_FIXED) {
+ src_mv = pdo_fixed_voltage(pdo);
+ src_ma = pdo_max_current(pdo);
+ tmp = src_mv * src_ma;
+ max_src_uw = tmp > max_src_uw ? tmp : max_src_uw;
+ }
+ }
+
+ val->intval = max_src_uw;
+ return 0;
+}
+
static int tcpm_psy_get_prop(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
@@ -6368,6 +6390,9 @@ static int tcpm_psy_get_prop(struct power_supply *psy,
case POWER_SUPPLY_PROP_CURRENT_NOW:
ret = tcpm_psy_get_current_now(port, val);
break;
+ case POWER_SUPPLY_PROP_INPUT_POWER_LIMIT:
+ tcpm_psy_get_input_power_limit(port, val);
+ break;
default:
ret = -EINVAL;
break;
diff --git a/drivers/usb/typec/tcpm/wcove.c b/drivers/usb/typec/tcpm/wcove.c
index 20917d85d6f4..87d4abde0ea2 100644
--- a/drivers/usb/typec/tcpm/wcove.c
+++ b/drivers/usb/typec/tcpm/wcove.c
@@ -671,7 +671,7 @@ static int wcove_typec_probe(struct platform_device *pdev)
return 0;
}
-static int wcove_typec_remove(struct platform_device *pdev)
+static void wcove_typec_remove(struct platform_device *pdev)
{
struct wcove_typec *wcove = platform_get_drvdata(pdev);
unsigned int val;
@@ -684,8 +684,6 @@ static int wcove_typec_remove(struct platform_device *pdev)
tcpm_unregister_port(wcove->tcpm);
fwnode_remove_software_node(wcove->tcpc.fwnode);
-
- return 0;
}
static struct platform_driver wcove_typec_driver = {
@@ -693,7 +691,7 @@ static struct platform_driver wcove_typec_driver = {
.name = "bxt_wcove_usbc",
},
.probe = wcove_typec_probe,
- .remove = wcove_typec_remove,
+ .remove_new = wcove_typec_remove,
};
module_platform_driver(wcove_typec_driver);
diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c
index 603dbd44deba..37b56ce75f39 100644
--- a/drivers/usb/typec/tipd/core.c
+++ b/drivers/usb/typec/tipd/core.c
@@ -950,7 +950,7 @@ static struct i2c_driver tps6598x_i2c_driver = {
.pm = &tps6598x_pm_ops,
.of_match_table = tps6598x_of_match,
},
- .probe_new = tps6598x_probe,
+ .probe = tps6598x_probe,
.remove = tps6598x_remove,
.id_table = tps6598x_id,
};
diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c
index b664ecbb798b..9b6d6b14431f 100644
--- a/drivers/usb/typec/ucsi/ucsi.c
+++ b/drivers/usb/typec/ucsi/ucsi.c
@@ -812,6 +812,23 @@ static void ucsi_partner_change(struct ucsi_connector *con)
break;
}
+ if (con->status.flags & UCSI_CONSTAT_CONNECTED) {
+ switch (UCSI_CONSTAT_PARTNER_TYPE(con->status.flags)) {
+ case UCSI_CONSTAT_PARTNER_TYPE_DEBUG:
+ typec_set_mode(con->port, TYPEC_MODE_DEBUG);
+ break;
+ case UCSI_CONSTAT_PARTNER_TYPE_AUDIO:
+ typec_set_mode(con->port, TYPEC_MODE_AUDIO);
+ break;
+ default:
+ if (UCSI_CONSTAT_PARTNER_FLAGS(con->status.flags) ==
+ UCSI_CONSTAT_PARTNER_FLAG_USB)
+ typec_set_mode(con->port, TYPEC_STATE_USB);
+ }
+ } else {
+ typec_set_mode(con->port, TYPEC_STATE_SAFE);
+ }
+
/* Only notify USB controller if partner supports USB data */
if (!(UCSI_CONSTAT_PARTNER_FLAGS(con->status.flags) & UCSI_CONSTAT_PARTNER_FLAG_USB))
u_role = USB_ROLE_NONE;
diff --git a/drivers/usb/typec/ucsi/ucsi_acpi.c b/drivers/usb/typec/ucsi/ucsi_acpi.c
index 217355f1f9b9..6bbf490ac401 100644
--- a/drivers/usb/typec/ucsi/ucsi_acpi.c
+++ b/drivers/usb/typec/ucsi/ucsi_acpi.c
@@ -212,7 +212,7 @@ static int ucsi_acpi_probe(struct platform_device *pdev)
return 0;
}
-static int ucsi_acpi_remove(struct platform_device *pdev)
+static void ucsi_acpi_remove(struct platform_device *pdev)
{
struct ucsi_acpi *ua = platform_get_drvdata(pdev);
@@ -221,8 +221,6 @@ static int ucsi_acpi_remove(struct platform_device *pdev)
acpi_remove_notify_handler(ACPI_HANDLE(&pdev->dev), ACPI_DEVICE_NOTIFY,
ucsi_acpi_notify);
-
- return 0;
}
static int ucsi_acpi_resume(struct device *dev)
@@ -247,7 +245,7 @@ static struct platform_driver ucsi_acpi_platform_driver = {
.acpi_match_table = ACPI_PTR(ucsi_acpi_match),
},
.probe = ucsi_acpi_probe,
- .remove = ucsi_acpi_remove,
+ .remove_new = ucsi_acpi_remove,
};
module_platform_driver(ucsi_acpi_platform_driver);
diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c
index e0ed465bd518..607061a37eca 100644
--- a/drivers/usb/typec/ucsi/ucsi_ccg.c
+++ b/drivers/usb/typec/ucsi/ucsi_ccg.c
@@ -1495,7 +1495,7 @@ static struct i2c_driver ucsi_ccg_driver = {
.acpi_match_table = amd_i2c_ucsi_match,
.of_match_table = ucsi_ccg_of_match_table,
},
- .probe_new = ucsi_ccg_probe,
+ .probe = ucsi_ccg_probe,
.remove = ucsi_ccg_remove,
.id_table = ucsi_ccg_device_id,
};
diff --git a/drivers/usb/typec/ucsi/ucsi_glink.c b/drivers/usb/typec/ucsi/ucsi_glink.c
index b454a5159896..1fe9cb5b6bd9 100644
--- a/drivers/usb/typec/ucsi/ucsi_glink.c
+++ b/drivers/usb/typec/ucsi/ucsi_glink.c
@@ -245,7 +245,7 @@ static void pmic_glink_ucsi_callback(const void *data, size_t len, void *priv)
struct pmic_glink_ucsi *ucsi = priv;
const struct pmic_glink_hdr *hdr = data;
- switch (hdr->opcode) {
+ switch (le32_to_cpu(hdr->opcode)) {
case UC_UCSI_READ_BUF_REQ:
pmic_glink_ucsi_read_ack(ucsi, data, len);
break;
diff --git a/drivers/usb/typec/ucsi/ucsi_stm32g0.c b/drivers/usb/typec/ucsi/ucsi_stm32g0.c
index 93fead0096b7..93d7806681cf 100644
--- a/drivers/usb/typec/ucsi/ucsi_stm32g0.c
+++ b/drivers/usb/typec/ucsi/ucsi_stm32g0.c
@@ -763,7 +763,7 @@ static struct i2c_driver ucsi_stm32g0_i2c_driver = {
.of_match_table = of_match_ptr(ucsi_stm32g0_typec_of_match),
.pm = pm_sleep_ptr(&ucsi_stm32g0_pm_ops),
},
- .probe_new = ucsi_stm32g0_probe,
+ .probe = ucsi_stm32g0_probe,
.remove = ucsi_stm32g0_remove,
.id_table = ucsi_stm32g0_typec_i2c_devid
};
diff --git a/drivers/usb/typec/wusb3801.c b/drivers/usb/typec/wusb3801.c
index a43a18d4b02e..6062875fb04a 100644
--- a/drivers/usb/typec/wusb3801.c
+++ b/drivers/usb/typec/wusb3801.c
@@ -420,7 +420,7 @@ static const struct of_device_id wusb3801_of_match[] = {
MODULE_DEVICE_TABLE(of, wusb3801_of_match);
static struct i2c_driver wusb3801_driver = {
- .probe_new = wusb3801_probe,
+ .probe = wusb3801_probe,
.remove = wusb3801_remove,
.driver = {
.name = "wusb3801",
diff --git a/drivers/usb/usbip/stub_main.c b/drivers/usb/usbip/stub_main.c
index e8c3131a8543..0a6624d37929 100644
--- a/drivers/usb/usbip/stub_main.c
+++ b/drivers/usb/usbip/stub_main.c
@@ -167,15 +167,13 @@ static ssize_t match_busid_show(struct device_driver *drv, char *buf)
static ssize_t match_busid_store(struct device_driver *dev, const char *buf,
size_t count)
{
- int len;
char busid[BUSID_SIZE];
if (count < 5)
return -EINVAL;
/* busid needs to include \0 termination */
- len = strlcpy(busid, buf + 4, BUSID_SIZE);
- if (sizeof(busid) <= len)
+ if (strscpy(busid, buf + 4, BUSID_SIZE) < 0)
return -EINVAL;
if (!strncmp(buf, "add ", 4)) {
diff --git a/drivers/usb/usbip/vhci_hcd.c b/drivers/usb/usbip/vhci_hcd.c
index 233265550fc6..37d1fc34e8a5 100644
--- a/drivers/usb/usbip/vhci_hcd.c
+++ b/drivers/usb/usbip/vhci_hcd.c
@@ -1393,7 +1393,7 @@ put_usb2_hcd:
return ret;
}
-static int vhci_hcd_remove(struct platform_device *pdev)
+static void vhci_hcd_remove(struct platform_device *pdev)
{
struct vhci *vhci = *((void **)dev_get_platdata(&pdev->dev));
@@ -1410,8 +1410,6 @@ static int vhci_hcd_remove(struct platform_device *pdev)
vhci->vhci_hcd_hs = NULL;
vhci->vhci_hcd_ss = NULL;
-
- return 0;
}
#ifdef CONFIG_PM
@@ -1485,7 +1483,7 @@ static int vhci_hcd_resume(struct platform_device *pdev)
static struct platform_driver vhci_driver = {
.probe = vhci_hcd_probe,
- .remove = vhci_hcd_remove,
+ .remove_new = vhci_hcd_remove,
.suspend = vhci_hcd_suspend,
.resume = vhci_hcd_resume,
.driver = {