summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/connector/usb-connector.txt14
-rw-r--r--Documentation/devicetree/bindings/usb/cdns-usb3.txt45
-rw-r--r--Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt1
-rw-r--r--Documentation/devicetree/bindings/usb/exynos-usb.txt41
-rw-r--r--Documentation/devicetree/bindings/usb/fcs,fusb302.txt7
-rw-r--r--Documentation/devicetree/bindings/usb/generic.txt4
-rw-r--r--Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.txt5
-rw-r--r--Documentation/devicetree/bindings/usb/mediatek,mtu3.txt12
-rw-r--r--Documentation/devicetree/bindings/usb/renesas,usb3-peri.txt (renamed from Documentation/devicetree/bindings/usb/renesas,usb3.txt)0
-rw-r--r--Documentation/devicetree/bindings/usb/usb-conn-gpio.txt30
-rw-r--r--Documentation/devicetree/bindings/usb/usbmisc-imx.txt1
-rw-r--r--MAINTAINERS19
-rw-r--r--arch/arm/boot/dts/exynos4.dtsi28
-rw-r--r--arch/arm/boot/dts/exynos4210-universal_c210.dts8
-rw-r--r--arch/arm/boot/dts/exynos4412-itop-elite.dts9
-rw-r--r--arch/arm/boot/dts/exynos4412-odroidu3.dts8
-rw-r--r--arch/arm/boot/dts/exynos4412-odroidx.dts5
-rw-r--r--arch/arm/boot/dts/exynos4412-origen.dts9
-rw-r--r--arch/arm/boot/dts/exynos5250.dtsi16
-rw-r--r--arch/arm/boot/dts/exynos54xx.dtsi18
-rw-r--r--arch/m68k/kernel/dma.c9
-rw-r--r--arch/powerpc/kernel/setup-common.c6
-rw-r--r--arch/sh/boards/mach-ap325rxa/setup.c1
-rw-r--r--arch/sh/boards/mach-ecovec24/setup.c2
-rw-r--r--arch/sh/boards/mach-kfr2r09/setup.c1
-rw-r--r--arch/sh/boards/mach-migor/setup.c1
-rw-r--r--arch/sh/boards/mach-se/7724/setup.c2
-rw-r--r--drivers/Kconfig2
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/base/devcon.c43
-rw-r--r--drivers/base/platform.c37
-rw-r--r--drivers/mfd/Kconfig2
-rw-r--r--drivers/staging/Kconfig3
-rw-r--r--drivers/staging/Makefile2
-rw-r--r--drivers/staging/octeon-usb/octeon-hcd.c2
-rw-r--r--drivers/staging/uwb/Kconfig (renamed from drivers/uwb/Kconfig)0
-rw-r--r--drivers/staging/uwb/Makefile (renamed from drivers/uwb/Makefile)0
-rw-r--r--drivers/staging/uwb/TODO8
-rw-r--r--drivers/staging/uwb/address.c (renamed from drivers/uwb/address.c)0
-rw-r--r--drivers/staging/uwb/allocator.c (renamed from drivers/uwb/allocator.c)2
-rw-r--r--drivers/staging/uwb/beacon.c (renamed from drivers/uwb/beacon.c)0
-rw-r--r--drivers/staging/uwb/driver.c (renamed from drivers/uwb/driver.c)0
-rw-r--r--drivers/staging/uwb/drp-avail.c (renamed from drivers/uwb/drp-avail.c)0
-rw-r--r--drivers/staging/uwb/drp-ie.c (renamed from drivers/uwb/drp-ie.c)2
-rw-r--r--drivers/staging/uwb/drp.c (renamed from drivers/uwb/drp.c)0
-rw-r--r--drivers/staging/uwb/est.c (renamed from drivers/uwb/est.c)0
-rw-r--r--drivers/staging/uwb/hwa-rc.c (renamed from drivers/uwb/hwa-rc.c)6
-rw-r--r--drivers/staging/uwb/i1480/Makefile (renamed from drivers/uwb/i1480/Makefile)0
-rw-r--r--drivers/staging/uwb/i1480/dfu/Makefile (renamed from drivers/uwb/i1480/dfu/Makefile)0
-rw-r--r--drivers/staging/uwb/i1480/dfu/dfu.c (renamed from drivers/uwb/i1480/dfu/dfu.c)2
-rw-r--r--drivers/staging/uwb/i1480/dfu/i1480-dfu.h (renamed from drivers/uwb/i1480/dfu/i1480-dfu.h)2
-rw-r--r--drivers/staging/uwb/i1480/dfu/mac.c (renamed from drivers/uwb/i1480/dfu/mac.c)2
-rw-r--r--drivers/staging/uwb/i1480/dfu/phy.c (renamed from drivers/uwb/i1480/dfu/phy.c)2
-rw-r--r--drivers/staging/uwb/i1480/dfu/usb.c (renamed from drivers/uwb/i1480/dfu/usb.c)6
-rw-r--r--drivers/staging/uwb/i1480/i1480-est.c (renamed from drivers/uwb/i1480/i1480-est.c)2
-rw-r--r--drivers/staging/uwb/ie-rcv.c (renamed from drivers/uwb/ie-rcv.c)0
-rw-r--r--drivers/staging/uwb/ie.c (renamed from drivers/uwb/ie.c)0
-rw-r--r--drivers/staging/uwb/include/debug-cmd.h (renamed from include/linux/uwb/debug-cmd.h)0
-rw-r--r--drivers/staging/uwb/include/spec.h (renamed from include/linux/uwb/spec.h)0
-rw-r--r--drivers/staging/uwb/include/umc.h (renamed from include/linux/uwb/umc.h)0
-rw-r--r--drivers/staging/uwb/include/whci.h (renamed from include/linux/uwb/whci.h)0
-rw-r--r--drivers/staging/uwb/lc-dev.c (renamed from drivers/uwb/lc-dev.c)0
-rw-r--r--drivers/staging/uwb/lc-rc.c (renamed from drivers/uwb/lc-rc.c)0
-rw-r--r--drivers/staging/uwb/neh.c (renamed from drivers/uwb/neh.c)0
-rw-r--r--drivers/staging/uwb/pal.c (renamed from drivers/uwb/pal.c)2
-rw-r--r--drivers/staging/uwb/radio.c (renamed from drivers/uwb/radio.c)2
-rw-r--r--drivers/staging/uwb/reset.c (renamed from drivers/uwb/reset.c)0
-rw-r--r--drivers/staging/uwb/rsv.c (renamed from drivers/uwb/rsv.c)2
-rw-r--r--drivers/staging/uwb/scan.c (renamed from drivers/uwb/scan.c)0
-rw-r--r--drivers/staging/uwb/umc-bus.c (renamed from drivers/uwb/umc-bus.c)2
-rw-r--r--drivers/staging/uwb/umc-dev.c (renamed from drivers/uwb/umc-dev.c)2
-rw-r--r--drivers/staging/uwb/umc-drv.c (renamed from drivers/uwb/umc-drv.c)2
-rw-r--r--drivers/staging/uwb/uwb-debug.c (renamed from drivers/uwb/uwb-debug.c)3
-rw-r--r--drivers/staging/uwb/uwb-internal.h (renamed from drivers/uwb/uwb-internal.h)2
-rw-r--r--drivers/staging/uwb/uwb.h (renamed from include/linux/uwb.h)2
-rw-r--r--drivers/staging/uwb/uwbd.c (renamed from drivers/uwb/uwbd.c)0
-rw-r--r--drivers/staging/uwb/whc-rc.c (renamed from drivers/uwb/whc-rc.c)6
-rw-r--r--drivers/staging/uwb/whci.c (renamed from drivers/uwb/whci.c)4
-rw-r--r--drivers/staging/wusbcore/Documentation/wusb-cbaf (renamed from Documentation/usb/wusb-cbaf)0
-rw-r--r--drivers/staging/wusbcore/Documentation/wusb-design-overview.rst (renamed from Documentation/usb/wusb-design-overview.rst)0
-rw-r--r--drivers/staging/wusbcore/Kconfig (renamed from drivers/usb/wusbcore/Kconfig)3
-rw-r--r--drivers/staging/wusbcore/Makefile (renamed from drivers/usb/wusbcore/Makefile)2
-rw-r--r--drivers/staging/wusbcore/TODO8
-rw-r--r--drivers/staging/wusbcore/cbaf.c (renamed from drivers/usb/wusbcore/cbaf.c)6
-rw-r--r--drivers/staging/wusbcore/crypto.c (renamed from drivers/usb/wusbcore/crypto.c)4
-rw-r--r--drivers/staging/wusbcore/dev-sysfs.c (renamed from drivers/usb/wusbcore/dev-sysfs.c)0
-rw-r--r--drivers/staging/wusbcore/devconnect.c (renamed from drivers/usb/wusbcore/devconnect.c)2
-rw-r--r--drivers/staging/wusbcore/host/Kconfig28
-rw-r--r--drivers/staging/wusbcore/host/Makefile3
-rw-r--r--drivers/staging/wusbcore/host/hwa-hc.c (renamed from drivers/usb/host/hwa-hc.c)4
-rw-r--r--drivers/staging/wusbcore/host/whci/Makefile (renamed from drivers/usb/host/whci/Makefile)0
-rw-r--r--drivers/staging/wusbcore/host/whci/asl.c (renamed from drivers/usb/host/whci/asl.c)4
-rw-r--r--drivers/staging/wusbcore/host/whci/debug.c (renamed from drivers/usb/host/whci/debug.c)2
-rw-r--r--drivers/staging/wusbcore/host/whci/hcd.c (renamed from drivers/usb/host/whci/hcd.c)4
-rw-r--r--drivers/staging/wusbcore/host/whci/hw.c (renamed from drivers/usb/host/whci/hw.c)4
-rw-r--r--drivers/staging/wusbcore/host/whci/init.c (renamed from drivers/usb/host/whci/init.c)4
-rw-r--r--drivers/staging/wusbcore/host/whci/int.c (renamed from drivers/usb/host/whci/int.c)4
-rw-r--r--drivers/staging/wusbcore/host/whci/pzl.c (renamed from drivers/usb/host/whci/pzl.c)4
-rw-r--r--drivers/staging/wusbcore/host/whci/qset.c (renamed from drivers/usb/host/whci/qset.c)4
-rw-r--r--drivers/staging/wusbcore/host/whci/whcd.h (renamed from drivers/usb/host/whci/whcd.h)4
-rw-r--r--drivers/staging/wusbcore/host/whci/whci-hc.h (renamed from drivers/usb/host/whci/whci-hc.h)0
-rw-r--r--drivers/staging/wusbcore/host/whci/wusb.c (renamed from drivers/usb/host/whci/wusb.c)4
-rw-r--r--drivers/staging/wusbcore/include/association.h (renamed from include/linux/usb/association.h)0
-rw-r--r--drivers/staging/wusbcore/include/wusb-wa.h (renamed from include/linux/usb/wusb-wa.h)0
-rw-r--r--drivers/staging/wusbcore/include/wusb.h (renamed from include/linux/usb/wusb.h)2
-rw-r--r--drivers/staging/wusbcore/mmc.c (renamed from drivers/usb/wusbcore/mmc.c)2
-rw-r--r--drivers/staging/wusbcore/pal.c (renamed from drivers/usb/wusbcore/pal.c)0
-rw-r--r--drivers/staging/wusbcore/reservation.c (renamed from drivers/usb/wusbcore/reservation.c)2
-rw-r--r--drivers/staging/wusbcore/rh.c (renamed from drivers/usb/wusbcore/rh.c)0
-rw-r--r--drivers/staging/wusbcore/security.c (renamed from drivers/usb/wusbcore/security.c)0
-rw-r--r--drivers/staging/wusbcore/wa-hc.c (renamed from drivers/usb/wusbcore/wa-hc.c)0
-rw-r--r--drivers/staging/wusbcore/wa-hc.h (renamed from drivers/usb/wusbcore/wa-hc.h)6
-rw-r--r--drivers/staging/wusbcore/wa-nep.c (renamed from drivers/usb/wusbcore/wa-nep.c)0
-rw-r--r--drivers/staging/wusbcore/wa-rpipe.c (renamed from drivers/usb/wusbcore/wa-rpipe.c)0
-rw-r--r--drivers/staging/wusbcore/wa-xfer.c (renamed from drivers/usb/wusbcore/wa-xfer.c)0
-rw-r--r--drivers/staging/wusbcore/wusbhc.c (renamed from drivers/usb/wusbcore/wusbhc.c)0
-rw-r--r--drivers/staging/wusbcore/wusbhc.h (renamed from drivers/usb/wusbcore/wusbhc.h)4
-rw-r--r--drivers/usb/Kconfig39
-rw-r--r--drivers/usb/Makefile4
-rw-r--r--drivers/usb/atm/cxacru.c58
-rw-r--r--drivers/usb/atm/ueagle-atm.c16
-rw-r--r--drivers/usb/cdns3/Kconfig46
-rw-r--r--drivers/usb/cdns3/Makefile16
-rw-r--r--drivers/usb/cdns3/cdns3-pci-wrap.c203
-rw-r--r--drivers/usb/cdns3/core.c651
-rw-r--r--drivers/usb/cdns3/core.h98
-rw-r--r--drivers/usb/cdns3/debug.h161
-rw-r--r--drivers/usb/cdns3/drd.c381
-rw-r--r--drivers/usb/cdns3/drd.h167
-rw-r--r--drivers/usb/cdns3/ep0.c886
-rw-r--r--drivers/usb/cdns3/gadget-export.h28
-rw-r--r--drivers/usb/cdns3/gadget.c2744
-rw-r--r--drivers/usb/cdns3/gadget.h1338
-rw-r--r--drivers/usb/cdns3/host-export.h28
-rw-r--r--drivers/usb/cdns3/host.c74
-rw-r--r--drivers/usb/cdns3/trace.c11
-rw-r--r--drivers/usb/cdns3/trace.h493
-rw-r--r--drivers/usb/chipidea/Kconfig1
-rw-r--r--drivers/usb/chipidea/ci.h12
-rw-r--r--drivers/usb/chipidea/ci_hdrc_msm.c9
-rw-r--r--drivers/usb/chipidea/core.c97
-rw-r--r--drivers/usb/chipidea/otg.c8
-rw-r--r--drivers/usb/chipidea/udc.c3
-rw-r--r--drivers/usb/class/usblp.c13
-rw-r--r--drivers/usb/class/usbtmc.c13
-rw-r--r--drivers/usb/common/Kconfig51
-rw-r--r--drivers/usb/common/Makefile2
-rw-r--r--drivers/usb/common/debug.c268
-rw-r--r--drivers/usb/common/usb-conn-gpio.c284
-rw-r--r--drivers/usb/core/config.c12
-rw-r--r--drivers/usb/core/devio.c108
-rw-r--r--drivers/usb/core/driver.c2
-rw-r--r--drivers/usb/core/generic.c5
-rw-r--r--drivers/usb/core/hcd-pci.c3
-rw-r--r--drivers/usb/core/hcd.c11
-rw-r--r--drivers/usb/core/phy.c21
-rw-r--r--drivers/usb/core/phy.h1
-rw-r--r--drivers/usb/core/port.c9
-rw-r--r--drivers/usb/core/usb.c222
-rw-r--r--drivers/usb/core/usb.h3
-rw-r--r--drivers/usb/dwc2/gadget.c9
-rw-r--r--drivers/usb/dwc2/hcd.c6
-rw-r--r--drivers/usb/dwc2/params.c5
-rw-r--r--drivers/usb/dwc2/platform.c4
-rw-r--r--drivers/usb/dwc3/core.c61
-rw-r--r--drivers/usb/dwc3/core.h2
-rw-r--r--drivers/usb/dwc3/debug.h252
-rw-r--r--drivers/usb/dwc3/dwc3-keystone.c5
-rw-r--r--drivers/usb/dwc3/dwc3-meson-g12a.c18
-rw-r--r--drivers/usb/dwc3/dwc3-omap.c15
-rw-r--r--drivers/usb/dwc3/dwc3-st.c10
-rw-r--r--drivers/usb/dwc3/gadget.c24
-rw-r--r--drivers/usb/dwc3/host.c22
-rw-r--r--drivers/usb/dwc3/trace.h2
-rw-r--r--drivers/usb/gadget/composite.c38
-rw-r--r--drivers/usb/gadget/udc/aspeed-vhub/core.c7
-rw-r--r--drivers/usb/gadget/udc/aspeed-vhub/dev.c80
-rw-r--r--drivers/usb/gadget/udc/aspeed-vhub/ep0.c59
-rw-r--r--drivers/usb/gadget/udc/aspeed-vhub/epn.c2
-rw-r--r--drivers/usb/gadget/udc/aspeed-vhub/hub.c15
-rw-r--r--drivers/usb/gadget/udc/aspeed-vhub/vhub.h3
-rw-r--r--drivers/usb/gadget/udc/atmel_usba_udc.c1
-rw-r--r--drivers/usb/gadget/udc/bcm63xx_udc.c8
-rw-r--r--drivers/usb/gadget/udc/bdc/bdc_core.c4
-rw-r--r--drivers/usb/gadget/udc/core.c2
-rw-r--r--drivers/usb/gadget/udc/gr_udc.c8
-rw-r--r--drivers/usb/gadget/udc/lpc32xx_udc.c14
-rw-r--r--drivers/usb/gadget/udc/net2280.c38
-rw-r--r--drivers/usb/gadget/udc/net2280.h3
-rw-r--r--drivers/usb/gadget/udc/pch_udc.c3
-rw-r--r--drivers/usb/gadget/udc/renesas_usb3.c4
-rw-r--r--drivers/usb/gadget/udc/s3c-hsudc.c4
-rw-r--r--drivers/usb/gadget/udc/s3c2410_udc.c2
-rw-r--r--drivers/usb/gadget/udc/udc-xilinx.c4
-rw-r--r--drivers/usb/host/Kconfig32
-rw-r--r--drivers/usb/host/Makefile4
-rw-r--r--drivers/usb/host/ehci-atmel.c3
-rw-r--r--drivers/usb/host/ehci-exynos.c23
-rw-r--r--drivers/usb/host/ehci-grlib.c2
-rw-r--r--drivers/usb/host/ehci-hcd.c2
-rw-r--r--drivers/usb/host/ehci-omap.c4
-rw-r--r--drivers/usb/host/ehci-orion.c3
-rw-r--r--drivers/usb/host/ehci-platform.c4
-rw-r--r--drivers/usb/host/ehci-pmcmsp.c2
-rw-r--r--drivers/usb/host/ehci-ppc-of.c2
-rw-r--r--drivers/usb/host/ehci-ps3.c2
-rw-r--r--drivers/usb/host/ehci-sh.c5
-rw-r--r--drivers/usb/host/ehci-st.c4
-rw-r--r--drivers/usb/host/ehci-w90x900.c130
-rw-r--r--drivers/usb/host/ehci-xilinx-of.c2
-rw-r--r--drivers/usb/host/fhci-hcd.c2
-rw-r--r--drivers/usb/host/fotg210-hcd.c2
-rw-r--r--drivers/usb/host/imx21-hcd.c6
-rw-r--r--drivers/usb/host/isp116x-hcd.c6
-rw-r--r--drivers/usb/host/isp1362-hcd.c5
-rw-r--r--drivers/usb/host/max3421-hcd.c17
-rw-r--r--drivers/usb/host/ohci-exynos.c23
-rw-r--r--drivers/usb/host/ohci-hcd.c2
-rw-r--r--drivers/usb/host/ohci-platform.c4
-rw-r--r--drivers/usb/host/ohci-ppc-of.c2
-rw-r--r--drivers/usb/host/ohci-ps3.c2
-rw-r--r--drivers/usb/host/ohci-pxa27x.c2
-rw-r--r--drivers/usb/host/ohci-sa1111.c2
-rw-r--r--drivers/usb/host/ohci-st.c4
-rw-r--r--drivers/usb/host/ohci-tmio.c3
-rw-r--r--drivers/usb/host/oxu210hp-hcd.c446
-rw-r--r--drivers/usb/host/oxu210hp.h448
-rw-r--r--drivers/usb/host/r8a66597-hcd.c6
-rw-r--r--drivers/usb/host/sl811-hcd.c6
-rw-r--r--drivers/usb/host/u132-hcd.c2
-rw-r--r--drivers/usb/host/uhci-grlib.c2
-rw-r--r--drivers/usb/host/uhci-pci.c2
-rw-r--r--drivers/usb/host/uhci-platform.c2
-rw-r--r--drivers/usb/host/xhci-dbgcap.c1
-rw-r--r--drivers/usb/host/xhci-dbgtty.c4
-rw-r--r--drivers/usb/host/xhci-ext-caps.c18
-rw-r--r--drivers/usb/host/xhci-hub.c2
-rw-r--r--drivers/usb/host/xhci-mem.c1
-rw-r--r--drivers/usb/host/xhci-mtk.c13
-rw-r--r--drivers/usb/host/xhci-mtk.h1
-rw-r--r--drivers/usb/host/xhci-plat.c15
-rw-r--r--drivers/usb/host/xhci-plat.h2
-rw-r--r--drivers/usb/host/xhci-rcar.c28
-rw-r--r--drivers/usb/host/xhci-rcar.h21
-rw-r--r--drivers/usb/host/xhci.c5
-rw-r--r--drivers/usb/host/xhci.h3
-rw-r--r--drivers/usb/isp1760/isp1760-core.c3
-rw-r--r--drivers/usb/isp1760/isp1760-if.c1
-rw-r--r--drivers/usb/misc/cypress_cy7c63.c29
-rw-r--r--drivers/usb/misc/cytherm.c64
-rw-r--r--drivers/usb/misc/lvstest.c19
-rw-r--r--drivers/usb/misc/rio500.c109
-rw-r--r--drivers/usb/misc/trancevibrator.c15
-rw-r--r--drivers/usb/misc/usbsevseg.c17
-rw-r--r--drivers/usb/mtu3/Kconfig1
-rw-r--r--drivers/usb/mtu3/mtu3.h5
-rw-r--r--drivers/usb/mtu3/mtu3_core.c4
-rw-r--r--drivers/usb/mtu3/mtu3_debugfs.c4
-rw-r--r--drivers/usb/mtu3/mtu3_dr.c48
-rw-r--r--drivers/usb/mtu3/mtu3_dr.h6
-rw-r--r--drivers/usb/mtu3/mtu3_plat.c3
-rw-r--r--drivers/usb/musb/musb_core.c24
-rw-r--r--drivers/usb/musb/musb_host.c2
-rw-r--r--drivers/usb/phy/phy-ab8500-usb.c18
-rw-r--r--drivers/usb/phy/phy-fsl-usb.c14
-rw-r--r--drivers/usb/phy/phy-mv-usb.c17
-rw-r--r--drivers/usb/phy/phy-tahvo.c18
-rw-r--r--drivers/usb/phy/phy-twl6030-usb.c12
-rw-r--r--drivers/usb/renesas_usbhs/mod_host.c2
-rw-r--r--drivers/usb/roles/class.c41
-rw-r--r--drivers/usb/roles/intel-xhci-usb-role-switch.c27
-rw-r--r--drivers/usb/serial/ftdi_sio.c43
-rw-r--r--drivers/usb/storage/debug.h2
-rw-r--r--drivers/usb/storage/isd200.c2
-rw-r--r--drivers/usb/storage/scsiglue.c8
-rw-r--r--drivers/usb/typec/Kconfig1
-rw-r--r--drivers/usb/typec/mux.c2
-rw-r--r--drivers/usb/typec/tcpm/fusb302.c101
-rw-r--r--drivers/usb/typec/tcpm/tcpm.c23
-rw-r--r--drivers/usb/typec/tcpm/wcove.c4
-rw-r--r--drivers/usb/typec/ucsi/ucsi_ccg.c13
-rw-r--r--drivers/usb/usb-skeleton.c8
-rw-r--r--drivers/usb/usbip/stub.h7
-rw-r--r--drivers/usb/usbip/stub_dev.c50
-rw-r--r--drivers/usb/usbip/stub_main.c57
-rw-r--r--drivers/usb/usbip/stub_rx.c204
-rw-r--r--drivers/usb/usbip/stub_tx.c99
-rw-r--r--drivers/usb/usbip/usbip_common.c59
-rw-r--r--drivers/usb/usbip/vhci_hcd.c12
-rw-r--r--drivers/usb/usbip/vhci_rx.c3
-rw-r--r--drivers/usb/usbip/vhci_tx.c66
-rw-r--r--drivers/usb/usbip/vudc.h2
-rw-r--r--drivers/usb/usbip/vudc_dev.c9
-rw-r--r--drivers/usb/usbip/vudc_main.c1
-rw-r--r--drivers/usb/usbip/vudc_sysfs.c7
-rw-r--r--include/linux/device.h10
-rw-r--r--include/linux/dma-mapping.h5
-rw-r--r--include/linux/oxu210hp.h8
-rw-r--r--include/linux/platform_data/dwc3-omap.h43
-rw-r--r--include/linux/platform_device.h2
-rw-r--r--include/linux/usb.h7
-rw-r--r--include/linux/usb/ch9.h27
-rw-r--r--include/linux/usb/gadget.h3
-rw-r--r--include/linux/usb/hcd.h11
-rw-r--r--include/linux/usb/role.h37
-rw-r--r--include/linux/usb/usb338x.h35
-rw-r--r--include/uapi/linux/usb/ch9.h2
-rw-r--r--include/uapi/linux/usbdevice_fs.h4
-rw-r--r--tools/usb/usbip/libsrc/usbip_common.c6
-rw-r--r--tools/usb/usbip/libsrc/usbip_device_driver.c6
310 files changed, 10289 insertions, 2442 deletions
diff --git a/Documentation/devicetree/bindings/connector/usb-connector.txt b/Documentation/devicetree/bindings/connector/usb-connector.txt
index cef556d4e5ee..d357987181ee 100644
--- a/Documentation/devicetree/bindings/connector/usb-connector.txt
+++ b/Documentation/devicetree/bindings/connector/usb-connector.txt
@@ -17,6 +17,20 @@ Optional properties:
- self-powered: Set this property if the usb device that has its own power
source.
+Optional properties for usb-b-connector:
+- id-gpios: an input gpio for USB ID pin.
+- vbus-gpios: an input gpio for USB VBUS pin, used to detect presence of
+ VBUS 5V.
+ see gpio/gpio.txt.
+- vbus-supply: a phandle to the regulator for USB VBUS if needed when host
+ mode or dual role mode is supported.
+ Particularly, if use an output GPIO to control a VBUS regulator, should
+ model it as a regulator.
+ see regulator/fixed-regulator.yaml
+- pinctrl-names : a pinctrl state named "default" is optional
+- pinctrl-0 : pin control group
+ see pinctrl/pinctrl-bindings.txt
+
Optional properties for usb-c-connector:
- power-role: should be one of "source", "sink" or "dual"(DRP) if typec
connector has power support.
diff --git a/Documentation/devicetree/bindings/usb/cdns-usb3.txt b/Documentation/devicetree/bindings/usb/cdns-usb3.txt
new file mode 100644
index 000000000000..b7dc606d37b5
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/cdns-usb3.txt
@@ -0,0 +1,45 @@
+Binding for the Cadence USBSS-DRD controller
+
+Required properties:
+ - reg: Physical base address and size of the controller's register areas.
+ Controller has 3 different regions:
+ - HOST registers area
+ - DEVICE registers area
+ - OTG/DRD registers area
+ - reg-names - register memory area names:
+ "xhci" - for HOST registers space
+ "dev" - for DEVICE registers space
+ "otg" - for OTG/DRD registers space
+ - compatible: Should contain: "cdns,usb3"
+ - interrupts: Interrupts used by cdns3 controller:
+ "host" - interrupt used by XHCI driver.
+ "peripheral" - interrupt used by device driver
+ "otg" - interrupt used by DRD/OTG part of driver
+
+Optional properties:
+ - maximum-speed : valid arguments are "super-speed", "high-speed" and
+ "full-speed"; refer to usb/generic.txt
+ - dr_mode: Should be one of "host", "peripheral" or "otg".
+ - phys: reference to the USB PHY
+ - phy-names: from the *Generic PHY* bindings;
+ Supported names are:
+ - cdns3,usb2-phy
+ - cdns3,usb3-phy
+
+ - cdns,on-chip-buff-size : size of memory intended as internal memory for endpoints
+ buffers expressed in KB
+
+Example:
+ usb@f3000000 {
+ compatible = "cdns,usb3";
+ interrupts = <GIC_USB_IRQ 7 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_USB_IRQ 7 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_USB_IRQ 8 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "host", "peripheral", "otg";
+ reg = <0xf3000000 0x10000>, /* memory area for HOST registers */
+ <0xf3010000 0x10000>, /* memory area for DEVICE registers */
+ <0xf3020000 0x10000>; /* memory area for OTG/DRD registers */
+ reg-names = "xhci", "dev", "otg";
+ phys = <&usb2_phy>, <&usb3_phy>;
+ phy-names = "cdns3,usb2-phy", "cnds3,usb3-phy";
+ };
diff --git a/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt b/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt
index a254386a91ad..cfc9f40ab641 100644
--- a/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt
+++ b/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt
@@ -10,6 +10,7 @@ Required properties:
"fsl,imx6sx-usb"
"fsl,imx6ul-usb"
"fsl,imx7d-usb"
+ "fsl,imx7ulp-usb"
"lsi,zevio-usb"
"qcom,ci-hdrc"
"chipidea,usb2"
diff --git a/Documentation/devicetree/bindings/usb/exynos-usb.txt b/Documentation/devicetree/bindings/usb/exynos-usb.txt
index b7111f43fa59..66c394f9e11f 100644
--- a/Documentation/devicetree/bindings/usb/exynos-usb.txt
+++ b/Documentation/devicetree/bindings/usb/exynos-usb.txt
@@ -12,13 +12,11 @@ Required properties:
- interrupts: interrupt number to the cpu.
- clocks: from common clock binding: handle to usb clock.
- clock-names: from common clock binding: Shall be "usbhost".
- - port: if in the SoC there are EHCI phys, they should be listed here.
- One phy per port. Each port should have following entries:
- - reg: port number on EHCI controller, e.g
- On Exynos5250, port 0 is USB2.0 otg phy
- port 1 is HSIC phy0
- port 2 is HSIC phy1
- - phys: from the *Generic PHY* bindings; specifying phy used by port.
+ - phys: from the *Generic PHY* bindings; array specifying phy(s) used
+ by the root port.
+ - phy-names: from the *Generic PHY* bindings; array of the names for
+ each phy for the root ports, must be a subset of the following:
+ "host", "hsic0", "hsic1".
Optional properties:
- samsung,vbus-gpio: if present, specifies the GPIO that
@@ -35,12 +33,8 @@ Example:
clocks = <&clock 285>;
clock-names = "usbhost";
- #address-cells = <1>;
- #size-cells = <0>;
- port@0 {
- reg = <0>;
- phys = <&usb2phy 1>;
- };
+ phys = <&usb2phy 1>;
+ phy-names = "host";
};
OHCI
@@ -52,13 +46,11 @@ Required properties:
- interrupts: interrupt number to the cpu.
- clocks: from common clock binding: handle to usb clock.
- clock-names: from common clock binding: Shall be "usbhost".
- - port: if in the SoC there are OHCI phys, they should be listed here.
- One phy per port. Each port should have following entries:
- - reg: port number on OHCI controller, e.g
- On Exynos5250, port 0 is USB2.0 otg phy
- port 1 is HSIC phy0
- port 2 is HSIC phy1
- - phys: from the *Generic PHY* bindings, specifying phy used by port.
+ - phys: from the *Generic PHY* bindings; array specifying phy(s) used
+ by the root port.
+ - phy-names: from the *Generic PHY* bindings; array of the names for
+ each phy for the root ports, must be a subset of the following:
+ "host", "hsic0", "hsic1".
Example:
usb@12120000 {
@@ -69,13 +61,8 @@ Example:
clocks = <&clock 285>;
clock-names = "usbhost";
- #address-cells = <1>;
- #size-cells = <0>;
- port@0 {
- reg = <0>;
- phys = <&usb2phy 1>;
- };
-
+ phys = <&usb2phy 1>;
+ phy-names = "host";
};
DWC3
diff --git a/Documentation/devicetree/bindings/usb/fcs,fusb302.txt b/Documentation/devicetree/bindings/usb/fcs,fusb302.txt
index a5d011d2efc8..ba2e32d500c0 100644
--- a/Documentation/devicetree/bindings/usb/fcs,fusb302.txt
+++ b/Documentation/devicetree/bindings/usb/fcs,fusb302.txt
@@ -11,13 +11,6 @@ Required sub-node:
Documentation/devicetree/bindings/connector/usb-connector.txt
-Deprecated properties :
-- fcs,max-sink-microvolt : Maximum sink voltage accepted by port controller
-- fcs,max-sink-microamp : Maximum sink current accepted by port controller
-- fcs,max-sink-microwatt : Maximum sink power accepted by port controller
-- fcs,operating-sink-microwatt : Minimum amount of power accepted from a sink
- when negotiating
-
Example:
diff --git a/Documentation/devicetree/bindings/usb/generic.txt b/Documentation/devicetree/bindings/usb/generic.txt
index 0a74ab8dfdc2..cf5a1ad456e6 100644
--- a/Documentation/devicetree/bindings/usb/generic.txt
+++ b/Documentation/devicetree/bindings/usb/generic.txt
@@ -30,6 +30,10 @@ Optional properties:
optional for OTG device.
- adp-disable: tells OTG controllers we want to disable OTG ADP, ADP is
optional for OTG device.
+ - usb-role-switch: boolean, indicates that the device is capable of assigning
+ the USB data role (USB host or USB device) for a given
+ USB connector, such as Type-C, Type-B(micro).
+ see connector/usb-connector.txt.
This is an attribute to a USB controller such as:
diff --git a/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.txt b/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.txt
index 266c2d917a28..f3e4acecabe8 100644
--- a/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.txt
+++ b/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.txt
@@ -30,7 +30,8 @@ Required properties:
the following ones are optional:
"ref_ck": reference clock used by low power mode etc,
"mcu_ck": mcu_bus clock for register access,
- "dma_ck": dma_bus clock for data transfer by DMA
+ "dma_ck": dma_bus clock for data transfer by DMA,
+ "xhci_ck": controller clock
- phys : see usb-hcd.txt in the current directory
@@ -100,7 +101,7 @@ Required properties:
- clocks : a list of phandle + clock-specifier pairs, one for each
entry in clock-names
- clock-names : must contain "sys_ck", and the following ones are optional:
- "ref_ck", "mcu_ck" and "dma_ck"
+ "ref_ck", "mcu_ck" and "dma_ck", "xhci_ck"
Optional properties:
- vbus-supply : reference to the VBUS regulator;
diff --git a/Documentation/devicetree/bindings/usb/mediatek,mtu3.txt b/Documentation/devicetree/bindings/usb/mediatek,mtu3.txt
index 3382b5cb471d..b9af7f5ee91d 100644
--- a/Documentation/devicetree/bindings/usb/mediatek,mtu3.txt
+++ b/Documentation/devicetree/bindings/usb/mediatek,mtu3.txt
@@ -16,7 +16,7 @@ Required properties:
entry in clock-names
- clock-names : must contain "sys_ck" for clock of controller,
the following clocks are optional:
- "ref_ck", "mcu_ck" and "dam_ck";
+ "ref_ck", "mcu_ck" and "dma_ck";
- phys : see usb-hcd.txt in the current directory
- dr_mode : should be one of "host", "peripheral" or "otg",
refer to usb/generic.txt
@@ -28,8 +28,13 @@ Optional properties:
parent's address space
- extcon : external connector for vbus and idpin changes detection, needed
when supports dual-role mode.
+ it's considered valid for compatibility reasons, not allowed for
+ new bindings, and use "usb-role-switch" property instead.
- vbus-supply : reference to the VBUS regulator, needed when supports
dual-role mode.
+ it's considered valid for compatibility reasons, not allowed for
+ new bindings, and put into a usb-connector node.
+ see connector/usb-connector.txt.
- pinctrl-names : a pinctrl state named "default" is optional, and need be
defined if auto drd switch is enabled, that means the property dr_mode
is set as "otg", and meanwhile the property "mediatek,enable-manual-drd"
@@ -39,6 +44,8 @@ Optional properties:
- maximum-speed : valid arguments are "super-speed", "high-speed" and
"full-speed"; refer to usb/generic.txt
+ - usb-role-switch : use USB Role Switch to support dual-role switch, but
+ not extcon; see usb/generic.txt.
- enable-manual-drd : supports manual dual-role switch via debugfs; usually
used when receptacle is TYPE-A and also wants to support dual-role
mode.
@@ -61,6 +68,9 @@ The xhci should be added as subnode to mtu3 as shown in the following example
if host mode is enabled. The DT binding details of xhci can be found in:
Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.txt
+The port would be added as subnode if use "usb-role-switch" property.
+ see graph.txt
+
Example:
ssusb: usb@11271000 {
compatible = "mediatek,mt8173-mtu3";
diff --git a/Documentation/devicetree/bindings/usb/renesas,usb3.txt b/Documentation/devicetree/bindings/usb/renesas,usb3-peri.txt
index 35039e720515..35039e720515 100644
--- a/Documentation/devicetree/bindings/usb/renesas,usb3.txt
+++ b/Documentation/devicetree/bindings/usb/renesas,usb3-peri.txt
diff --git a/Documentation/devicetree/bindings/usb/usb-conn-gpio.txt b/Documentation/devicetree/bindings/usb/usb-conn-gpio.txt
new file mode 100644
index 000000000000..3d05ae56cb0d
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/usb-conn-gpio.txt
@@ -0,0 +1,30 @@
+USB GPIO Based Connection Detection
+
+This is typically used to switch dual role mode from the USB ID pin connected
+to an input GPIO, and also used to enable/disable device mode from the USB
+Vbus pin connected to an input GPIO.
+
+Required properties:
+- compatible : should include "gpio-usb-b-connector" and "usb-b-connector".
+- id-gpios, vbus-gpios : input gpios, either one of them must be present,
+ and both can be present as well.
+ see connector/usb-connector.txt
+
+Optional properties:
+- vbus-supply : can be present if needed when supports dual role mode.
+ see connector/usb-connector.txt
+
+- Sub-nodes:
+ - port : can be present.
+ see graph.txt
+
+Example:
+
+&mtu3 {
+ connector {
+ compatible = "gpio-usb-b-connector", "usb-b-connector";
+ type = "micro";
+ id-gpios = <&pio 12 GPIO_ACTIVE_HIGH>;
+ vbus-supply = <&usb_p0_vbus>;
+ };
+};
diff --git a/Documentation/devicetree/bindings/usb/usbmisc-imx.txt b/Documentation/devicetree/bindings/usb/usbmisc-imx.txt
index a85a631ec434..b353b9816487 100644
--- a/Documentation/devicetree/bindings/usb/usbmisc-imx.txt
+++ b/Documentation/devicetree/bindings/usb/usbmisc-imx.txt
@@ -7,6 +7,7 @@ Required properties:
"fsl,vf610-usbmisc" for Vybrid vf610
"fsl,imx6sx-usbmisc" for imx6sx
"fsl,imx7d-usbmisc" for imx7d
+ "fsl,imx7ulp-usbmisc" for imx7ulp
- reg: Should contain registers location and length
Examples:
diff --git a/MAINTAINERS b/MAINTAINERS
index 2ecc1de1910d..0e01ab35df04 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3797,14 +3797,9 @@ F: scripts/sign-file.c
F: scripts/extract-cert.c
CERTIFIED WIRELESS USB (WUSB) SUBSYSTEM:
-L: linux-usb@vger.kernel.org
-S: Orphan
-F: Documentation/usb/wusb-design-overview.rst
-F: Documentation/usb/wusb-cbaf
-F: drivers/usb/host/hwa-hc.c
-F: drivers/usb/host/whci/
-F: drivers/usb/wusbcore/
-F: include/linux/usb/wusb*
+L: devel@driverdev.osuosl.org
+S: Obsolete
+F: drivers/staging/wusbcore/
CFAG12864B LCD DRIVER
M: Miguel Ojeda Sandonis <miguel.ojeda.sandonis@gmail.com>
@@ -16500,11 +16495,9 @@ F: drivers/usb/common/ulpi.c
F: include/linux/ulpi/
ULTRA-WIDEBAND (UWB) SUBSYSTEM:
-L: linux-usb@vger.kernel.org
-S: Orphan
-F: drivers/uwb/
-F: include/linux/uwb.h
-F: include/linux/uwb/
+L: devel@driverdev.osuosl.org
+S: Obsolete
+F: drivers/staging/uwb/
UNICODE SUBSYSTEM:
M: Gabriel Krisman Bertazi <krisman@collabora.com>
diff --git a/arch/arm/boot/dts/exynos4.dtsi b/arch/arm/boot/dts/exynos4.dtsi
index 1264cc431ff6..433f109d97ca 100644
--- a/arch/arm/boot/dts/exynos4.dtsi
+++ b/arch/arm/boot/dts/exynos4.dtsi
@@ -380,23 +380,8 @@
clocks = <&clock CLK_USB_HOST>;
clock-names = "usbhost";
status = "disabled";
- #address-cells = <1>;
- #size-cells = <0>;
- port@0 {
- reg = <0>;
- phys = <&exynos_usbphy 1>;
- status = "disabled";
- };
- port@1 {
- reg = <1>;
- phys = <&exynos_usbphy 2>;
- status = "disabled";
- };
- port@2 {
- reg = <2>;
- phys = <&exynos_usbphy 3>;
- status = "disabled";
- };
+ phys = <&exynos_usbphy 1>, <&exynos_usbphy 2>, <&exynos_usbphy 3>;
+ phy-names = "host", "hsic0", "hsic1";
};
ohci: ohci@12590000 {
@@ -406,13 +391,8 @@
clocks = <&clock CLK_USB_HOST>;
clock-names = "usbhost";
status = "disabled";
- #address-cells = <1>;
- #size-cells = <0>;
- port@0 {
- reg = <0>;
- phys = <&exynos_usbphy 1>;
- status = "disabled";
- };
+ phys = <&exynos_usbphy 1>;
+ phy-names = "host";
};
gpu: gpu@13000000 {
diff --git a/arch/arm/boot/dts/exynos4210-universal_c210.dts b/arch/arm/boot/dts/exynos4210-universal_c210.dts
index 82a8b5449978..09d3d54d09ff 100644
--- a/arch/arm/boot/dts/exynos4210-universal_c210.dts
+++ b/arch/arm/boot/dts/exynos4210-universal_c210.dts
@@ -204,9 +204,8 @@
&ehci {
status = "okay";
- port@0 {
- status = "okay";
- };
+ phys = <&exynos_usbphy 1>;
+ phy-names = "host";
};
&exynos_usbphy {
@@ -520,9 +519,6 @@
&ohci {
status = "okay";
- port@0 {
- status = "okay";
- };
};
&pinctrl_1 {
diff --git a/arch/arm/boot/dts/exynos4412-itop-elite.dts b/arch/arm/boot/dts/exynos4412-itop-elite.dts
index 0dedeba89b5f..f6d0a5f5d339 100644
--- a/arch/arm/boot/dts/exynos4412-itop-elite.dts
+++ b/arch/arm/boot/dts/exynos4412-itop-elite.dts
@@ -146,13 +146,8 @@
/* In order to reset USB ethernet */
samsung,vbus-gpio = <&gpc0 1 GPIO_ACTIVE_HIGH>;
- port@0 {
- status = "okay";
- };
-
- port@2 {
- status = "okay";
- };
+ phys = <&exynos_usbphy 1>, <&exynos_usbphy 3>;
+ phy-names = "host", "hsic1";
};
&exynos_usbphy {
diff --git a/arch/arm/boot/dts/exynos4412-odroidu3.dts b/arch/arm/boot/dts/exynos4412-odroidu3.dts
index 96d99887bceb..8ff243ba4542 100644
--- a/arch/arm/boot/dts/exynos4412-odroidu3.dts
+++ b/arch/arm/boot/dts/exynos4412-odroidu3.dts
@@ -105,12 +105,8 @@
};
&ehci {
- port@1 {
- status = "okay";
- };
- port@2 {
- status = "okay";
- };
+ phys = <&exynos_usbphy 2>, <&exynos_usbphy 3>;
+ phy-names = "hsic0", "hsic1";
};
&sound {
diff --git a/arch/arm/boot/dts/exynos4412-odroidx.dts b/arch/arm/boot/dts/exynos4412-odroidx.dts
index a2251581f6b6..3ea2a0101e80 100644
--- a/arch/arm/boot/dts/exynos4412-odroidx.dts
+++ b/arch/arm/boot/dts/exynos4412-odroidx.dts
@@ -72,9 +72,8 @@
};
&ehci {
- port@1 {
- status = "okay";
- };
+ phys = <&exynos_usbphy 2>;
+ phy-names = "hsic0";
};
&mshc_0 {
diff --git a/arch/arm/boot/dts/exynos4412-origen.dts b/arch/arm/boot/dts/exynos4412-origen.dts
index 698de4345d16..ecd14b283a6b 100644
--- a/arch/arm/boot/dts/exynos4412-origen.dts
+++ b/arch/arm/boot/dts/exynos4412-origen.dts
@@ -88,13 +88,8 @@
&ehci {
samsung,vbus-gpio = <&gpx3 5 1>;
status = "okay";
-
- port@1 {
- status = "okay";
- };
- port@2 {
- status = "okay";
- };
+ phys = <&exynos_usbphy 2>, <&exynos_usbphy 3>;
+ phy-names = "hsic0", "hsic1";
};
&fimd {
diff --git a/arch/arm/boot/dts/exynos5250.dtsi b/arch/arm/boot/dts/exynos5250.dtsi
index 81729cf3ad2d..fc966c10cf49 100644
--- a/arch/arm/boot/dts/exynos5250.dtsi
+++ b/arch/arm/boot/dts/exynos5250.dtsi
@@ -665,12 +665,8 @@
clocks = <&clock CLK_USB2>;
clock-names = "usbhost";
- #address-cells = <1>;
- #size-cells = <0>;
- port@0 {
- reg = <0>;
- phys = <&usb2_phy_gen 1>;
- };
+ phys = <&usb2_phy_gen 1>;
+ phy-names = "host";
};
ohci: usb@12120000 {
@@ -680,12 +676,8 @@
clocks = <&clock CLK_USB2>;
clock-names = "usbhost";
- #address-cells = <1>;
- #size-cells = <0>;
- port@0 {
- reg = <0>;
- phys = <&usb2_phy_gen 1>;
- };
+ phys = <&usb2_phy_gen 1>;
+ phy-names = "host";
};
usb2_phy_gen: phy@12130000 {
diff --git a/arch/arm/boot/dts/exynos54xx.dtsi b/arch/arm/boot/dts/exynos54xx.dtsi
index 0b27bebf9528..9c3b63b7cac6 100644
--- a/arch/arm/boot/dts/exynos54xx.dtsi
+++ b/arch/arm/boot/dts/exynos54xx.dtsi
@@ -189,26 +189,16 @@
compatible = "samsung,exynos4210-ehci";
reg = <0x12110000 0x100>;
interrupts = <GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>;
-
- #address-cells = <1>;
- #size-cells = <0>;
- port@0 {
- reg = <0>;
- phys = <&usb2_phy 1>;
- };
+ phys = <&usb2_phy 1>;
+ phy-names = "host";
};
usbhost1: usb@12120000 {
compatible = "samsung,exynos4210-ohci";
reg = <0x12120000 0x100>;
interrupts = <GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>;
-
- #address-cells = <1>;
- #size-cells = <0>;
- port@0 {
- reg = <0>;
- phys = <&usb2_phy 1>;
- };
+ phys = <&usb2_phy 1>;
+ phy-names = "host";
};
usb2_phy: phy@12130000 {
diff --git a/arch/m68k/kernel/dma.c b/arch/m68k/kernel/dma.c
index 30cd59caf037..447849d1d645 100644
--- a/arch/m68k/kernel/dma.c
+++ b/arch/m68k/kernel/dma.c
@@ -79,12 +79,3 @@ void arch_sync_dma_for_device(struct device *dev, phys_addr_t handle,
break;
}
}
-
-void arch_setup_pdev_archdata(struct platform_device *pdev)
-{
- if (pdev->dev.coherent_dma_mask == DMA_MASK_NONE &&
- pdev->dev.dma_mask == NULL) {
- pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
- pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;
- }
-}
diff --git a/arch/powerpc/kernel/setup-common.c b/arch/powerpc/kernel/setup-common.c
index 1f8db666468d..5e6543aba1b3 100644
--- a/arch/powerpc/kernel/setup-common.c
+++ b/arch/powerpc/kernel/setup-common.c
@@ -778,12 +778,6 @@ void ppc_printk_progress(char *s, unsigned short hex)
pr_info("%s\n", s);
}
-void arch_setup_pdev_archdata(struct platform_device *pdev)
-{
- pdev->archdata.dma_mask = DMA_BIT_MASK(32);
- pdev->dev.dma_mask = &pdev->archdata.dma_mask;
-}
-
static __init void print_system_info(void)
{
pr_info("-----------------------------------------------------\n");
diff --git a/arch/sh/boards/mach-ap325rxa/setup.c b/arch/sh/boards/mach-ap325rxa/setup.c
index 8301a4378f50..665cad452798 100644
--- a/arch/sh/boards/mach-ap325rxa/setup.c
+++ b/arch/sh/boards/mach-ap325rxa/setup.c
@@ -527,7 +527,6 @@ static int __init ap325rxa_devices_setup(void)
/* Initialize CEU platform device separately to map memory first */
device_initialize(&ap325rxa_ceu_device.dev);
- arch_setup_pdev_archdata(&ap325rxa_ceu_device);
dma_declare_coherent_memory(&ap325rxa_ceu_device.dev,
ceu_dma_membase, ceu_dma_membase,
ceu_dma_membase + CEU_BUFFER_MEMORY_SIZE - 1);
diff --git a/arch/sh/boards/mach-ecovec24/setup.c b/arch/sh/boards/mach-ecovec24/setup.c
index f402aa741bf3..acaa97459531 100644
--- a/arch/sh/boards/mach-ecovec24/setup.c
+++ b/arch/sh/boards/mach-ecovec24/setup.c
@@ -1440,7 +1440,6 @@ static int __init arch_setup(void)
/* Initialize CEU platform devices separately to map memory first */
device_initialize(&ecovec_ceu_devices[0]->dev);
- arch_setup_pdev_archdata(ecovec_ceu_devices[0]);
dma_declare_coherent_memory(&ecovec_ceu_devices[0]->dev,
ceu0_dma_membase, ceu0_dma_membase,
ceu0_dma_membase +
@@ -1448,7 +1447,6 @@ static int __init arch_setup(void)
platform_device_add(ecovec_ceu_devices[0]);
device_initialize(&ecovec_ceu_devices[1]->dev);
- arch_setup_pdev_archdata(ecovec_ceu_devices[1]);
dma_declare_coherent_memory(&ecovec_ceu_devices[1]->dev,
ceu1_dma_membase, ceu1_dma_membase,
ceu1_dma_membase +
diff --git a/arch/sh/boards/mach-kfr2r09/setup.c b/arch/sh/boards/mach-kfr2r09/setup.c
index 1cf9a47ac90e..96538ba3aa32 100644
--- a/arch/sh/boards/mach-kfr2r09/setup.c
+++ b/arch/sh/boards/mach-kfr2r09/setup.c
@@ -601,7 +601,6 @@ static int __init kfr2r09_devices_setup(void)
/* Initialize CEU platform device separately to map memory first */
device_initialize(&kfr2r09_ceu_device.dev);
- arch_setup_pdev_archdata(&kfr2r09_ceu_device);
dma_declare_coherent_memory(&kfr2r09_ceu_device.dev,
ceu_dma_membase, ceu_dma_membase,
ceu_dma_membase + CEU_BUFFER_MEMORY_SIZE - 1);
diff --git a/arch/sh/boards/mach-migor/setup.c b/arch/sh/boards/mach-migor/setup.c
index 90702740f207..9ed369dad62d 100644
--- a/arch/sh/boards/mach-migor/setup.c
+++ b/arch/sh/boards/mach-migor/setup.c
@@ -602,7 +602,6 @@ static int __init migor_devices_setup(void)
/* Initialize CEU platform device separately to map memory first */
device_initialize(&migor_ceu_device.dev);
- arch_setup_pdev_archdata(&migor_ceu_device);
dma_declare_coherent_memory(&migor_ceu_device.dev,
ceu_dma_membase, ceu_dma_membase,
ceu_dma_membase + CEU_BUFFER_MEMORY_SIZE - 1);
diff --git a/arch/sh/boards/mach-se/7724/setup.c b/arch/sh/boards/mach-se/7724/setup.c
index 3674064816c7..32f5dd944889 100644
--- a/arch/sh/boards/mach-se/7724/setup.c
+++ b/arch/sh/boards/mach-se/7724/setup.c
@@ -937,7 +937,6 @@ static int __init devices_setup(void)
/* Initialize CEU platform devices separately to map memory first */
device_initialize(&ms7724se_ceu_devices[0]->dev);
- arch_setup_pdev_archdata(ms7724se_ceu_devices[0]);
dma_declare_coherent_memory(&ms7724se_ceu_devices[0]->dev,
ceu0_dma_membase, ceu0_dma_membase,
ceu0_dma_membase +
@@ -945,7 +944,6 @@ static int __init devices_setup(void)
platform_device_add(ms7724se_ceu_devices[0]);
device_initialize(&ms7724se_ceu_devices[1]->dev);
- arch_setup_pdev_archdata(ms7724se_ceu_devices[1]);
dma_declare_coherent_memory(&ms7724se_ceu_devices[1]->dev,
ceu1_dma_membase, ceu1_dma_membase,
ceu1_dma_membase +
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 477d63d0364d..13f09bf64a0f 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -106,8 +106,6 @@ source "drivers/hid/Kconfig"
source "drivers/usb/Kconfig"
-source "drivers/uwb/Kconfig"
-
source "drivers/mmc/Kconfig"
source "drivers/memstick/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index 05be8c798c51..ad66dc2325d7 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -100,7 +100,6 @@ obj-$(CONFIG_ZORRO) += zorro/
obj-$(CONFIG_ATA_OVER_ETH) += block/aoe/
obj-$(CONFIG_PARIDE) += block/paride/
obj-$(CONFIG_TC) += tc/
-obj-$(CONFIG_UWB) += uwb/
obj-$(CONFIG_USB_PHY) += usb/
obj-$(CONFIG_USB) += usb/
obj-$(CONFIG_USB_SUPPORT) += usb/
diff --git a/drivers/base/devcon.c b/drivers/base/devcon.c
index 1d488dc5dd0c..14e2178e09f8 100644
--- a/drivers/base/devcon.c
+++ b/drivers/base/devcon.c
@@ -12,9 +12,6 @@
static DEFINE_MUTEX(devcon_lock);
static LIST_HEAD(devcon_list);
-typedef void *(*devcon_match_fn_t)(struct device_connection *con, int ep,
- void *data);
-
static void *
fwnode_graph_devcon_match(struct fwnode_handle *fwnode, const char *con_id,
void *data, devcon_match_fn_t match)
@@ -61,6 +58,34 @@ fwnode_devcon_match(struct fwnode_handle *fwnode, const char *con_id,
}
/**
+ * fwnode_connection_find_match - Find connection from a device node
+ * @fwnode: Device node with the connection
+ * @con_id: Identifier for the connection
+ * @data: Data for the match function
+ * @match: Function to check and convert the connection description
+ *
+ * Find a connection with unique identifier @con_id between @fwnode and another
+ * device node. @match will be used to convert the connection description to
+ * data the caller is expecting to be returned.
+ */
+void *fwnode_connection_find_match(struct fwnode_handle *fwnode,
+ const char *con_id, void *data,
+ devcon_match_fn_t match)
+{
+ void *ret;
+
+ if (!fwnode || !match)
+ return NULL;
+
+ ret = fwnode_graph_devcon_match(fwnode, con_id, data, match);
+ if (ret)
+ return ret;
+
+ return fwnode_devcon_match(fwnode, con_id, data, match);
+}
+EXPORT_SYMBOL_GPL(fwnode_connection_find_match);
+
+/**
* device_connection_find_match - Find physical connection to a device
* @dev: Device with the connection
* @con_id: Identifier for the connection
@@ -83,15 +108,9 @@ void *device_connection_find_match(struct device *dev, const char *con_id,
if (!match)
return NULL;
- if (fwnode) {
- ret = fwnode_graph_devcon_match(fwnode, con_id, data, match);
- if (ret)
- return ret;
-
- ret = fwnode_devcon_match(fwnode, con_id, data, match);
- if (ret)
- return ret;
- }
+ ret = fwnode_connection_find_match(fwnode, con_id, data, match);
+ if (ret)
+ return ret;
mutex_lock(&devcon_lock);
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index 11c6e56ccc22..b6c6c7d97d5b 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -40,25 +40,6 @@ struct device platform_bus = {
EXPORT_SYMBOL_GPL(platform_bus);
/**
- * arch_setup_pdev_archdata - Allow manipulation of archdata before its used
- * @pdev: platform device
- *
- * This is called before platform_device_add() such that any pdev_archdata may
- * be setup before the platform_notifier is called. So if a user needs to
- * manipulate any relevant information in the pdev_archdata they can do:
- *
- * platform_device_alloc()
- * ... manipulate ...
- * platform_device_add()
- *
- * And if they don't care they can just call platform_device_register() and
- * everything will just work out.
- */
-void __weak arch_setup_pdev_archdata(struct platform_device *pdev)
-{
-}
-
-/**
* platform_get_resource - get a resource for a device
* @dev: platform device
* @type: resource type
@@ -313,6 +294,20 @@ struct platform_object {
char name[];
};
+/*
+ * Set up default DMA mask for platform devices if the they weren't
+ * previously set by the architecture / DT.
+ */
+static void setup_pdev_dma_masks(struct platform_device *pdev)
+{
+ if (!pdev->dev.coherent_dma_mask)
+ pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+ if (!pdev->dma_mask)
+ pdev->dma_mask = DMA_BIT_MASK(32);
+ if (!pdev->dev.dma_mask)
+ pdev->dev.dma_mask = &pdev->dma_mask;
+};
+
/**
* platform_device_put - destroy a platform device
* @pdev: platform device to free
@@ -359,7 +354,7 @@ struct platform_device *platform_device_alloc(const char *name, int id)
pa->pdev.id = id;
device_initialize(&pa->pdev.dev);
pa->pdev.dev.release = platform_device_release;
- arch_setup_pdev_archdata(&pa->pdev);
+ setup_pdev_dma_masks(&pa->pdev);
}
return pa ? &pa->pdev : NULL;
@@ -561,7 +556,7 @@ EXPORT_SYMBOL_GPL(platform_device_del);
int platform_device_register(struct platform_device *pdev)
{
device_initialize(&pdev->dev);
- arch_setup_pdev_archdata(pdev);
+ setup_pdev_dma_masks(pdev);
return platform_device_add(pdev);
}
EXPORT_SYMBOL_GPL(platform_device_register);
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index f129f9678940..c8cbde59bbf6 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1105,7 +1105,6 @@ config MFD_SI476X_CORE
config MFD_SM501
tristate "Silicon Motion SM501"
depends on HAS_DMA
- select DMA_DECLARE_COHERENT
---help---
This is the core driver for the Silicon Motion SM501 multimedia
companion chip. This device is a multifunction device which may
@@ -1714,7 +1713,6 @@ config MFD_TC6393XB
select GPIOLIB
select MFD_CORE
select MFD_TMIO
- select DMA_DECLARE_COHERENT
help
Support for Toshiba Mobile IO Controller TC6393XB
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index 7c96a01eef6c..cf419d9c942d 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -120,4 +120,7 @@ source "drivers/staging/kpc2000/Kconfig"
source "drivers/staging/isdn/Kconfig"
+source "drivers/staging/wusbcore/Kconfig"
+source "drivers/staging/uwb/Kconfig"
+
endif # STAGING
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index fcaac9693b83..38179bc842a8 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -50,3 +50,5 @@ obj-$(CONFIG_EROFS_FS) += erofs/
obj-$(CONFIG_FIELDBUS_DEV) += fieldbus/
obj-$(CONFIG_KPC2000) += kpc2000/
obj-$(CONFIG_ISDN_CAPI) += isdn/
+obj-$(CONFIG_UWB) += uwb/
+obj-$(CONFIG_USB_WUSB) += wusbcore/
diff --git a/drivers/staging/octeon-usb/octeon-hcd.c b/drivers/staging/octeon-usb/octeon-hcd.c
index cd2b777073c4..a5321cc692c5 100644
--- a/drivers/staging/octeon-usb/octeon-hcd.c
+++ b/drivers/staging/octeon-usb/octeon-hcd.c
@@ -3512,7 +3512,7 @@ static const struct hc_driver octeon_hc_driver = {
.product_desc = "Octeon Host Controller",
.hcd_priv_size = sizeof(struct octeon_hcd),
.irq = octeon_usb_irq,
- .flags = HCD_MEMORY | HCD_USB2,
+ .flags = HCD_MEMORY | HCD_DMA | HCD_USB2,
.start = octeon_usb_start,
.stop = octeon_usb_stop,
.urb_enqueue = octeon_usb_urb_enqueue,
diff --git a/drivers/uwb/Kconfig b/drivers/staging/uwb/Kconfig
index 259e053e1e09..259e053e1e09 100644
--- a/drivers/uwb/Kconfig
+++ b/drivers/staging/uwb/Kconfig
diff --git a/drivers/uwb/Makefile b/drivers/staging/uwb/Makefile
index 32f4de7afbd6..32f4de7afbd6 100644
--- a/drivers/uwb/Makefile
+++ b/drivers/staging/uwb/Makefile
diff --git a/drivers/staging/uwb/TODO b/drivers/staging/uwb/TODO
new file mode 100644
index 000000000000..abae57000534
--- /dev/null
+++ b/drivers/staging/uwb/TODO
@@ -0,0 +1,8 @@
+TODO: Remove in late 2019 unless there are users
+
+There seems to not be any real wireless USB devices anywhere in the wild
+anymore. It turned out to be a failed technology :(
+
+This will be removed from the tree if no one objects.
+
+Greg Kroah-Hartman <gregkh@linuxfoundation.org>
diff --git a/drivers/uwb/address.c b/drivers/staging/uwb/address.c
index 857d5cd56a95..857d5cd56a95 100644
--- a/drivers/uwb/address.c
+++ b/drivers/staging/uwb/address.c
diff --git a/drivers/uwb/allocator.c b/drivers/staging/uwb/allocator.c
index 2e1590124d5f..1f429fba20b7 100644
--- a/drivers/uwb/allocator.c
+++ b/drivers/staging/uwb/allocator.c
@@ -6,7 +6,7 @@
*/
#include <linux/kernel.h>
#include <linux/slab.h>
-#include <linux/uwb.h>
+#include "uwb.h"
#include "uwb-internal.h"
diff --git a/drivers/uwb/beacon.c b/drivers/staging/uwb/beacon.c
index c483c19c5ef8..c483c19c5ef8 100644
--- a/drivers/uwb/beacon.c
+++ b/drivers/staging/uwb/beacon.c
diff --git a/drivers/uwb/driver.c b/drivers/staging/uwb/driver.c
index 5755c2e49ffc..5755c2e49ffc 100644
--- a/drivers/uwb/driver.c
+++ b/drivers/staging/uwb/driver.c
diff --git a/drivers/uwb/drp-avail.c b/drivers/staging/uwb/drp-avail.c
index 02392ab82a7d..02392ab82a7d 100644
--- a/drivers/uwb/drp-avail.c
+++ b/drivers/staging/uwb/drp-avail.c
diff --git a/drivers/uwb/drp-ie.c b/drivers/staging/uwb/drp-ie.c
index 4b545b41161c..b2a862cf76de 100644
--- a/drivers/uwb/drp-ie.c
+++ b/drivers/staging/uwb/drp-ie.c
@@ -8,8 +8,8 @@
#include <linux/kernel.h>
#include <linux/random.h>
#include <linux/slab.h>
-#include <linux/uwb.h>
+#include "uwb.h"
#include "uwb-internal.h"
diff --git a/drivers/uwb/drp.c b/drivers/staging/uwb/drp.c
index 869987bede7b..869987bede7b 100644
--- a/drivers/uwb/drp.c
+++ b/drivers/staging/uwb/drp.c
diff --git a/drivers/uwb/est.c b/drivers/staging/uwb/est.c
index d4141ffdd775..d4141ffdd775 100644
--- a/drivers/uwb/est.c
+++ b/drivers/staging/uwb/est.c
diff --git a/drivers/uwb/hwa-rc.c b/drivers/staging/uwb/hwa-rc.c
index cd03b7f827c1..b6effad749d7 100644
--- a/drivers/uwb/hwa-rc.c
+++ b/drivers/staging/uwb/hwa-rc.c
@@ -38,9 +38,9 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/usb.h>
-#include <linux/usb/wusb.h>
-#include <linux/usb/wusb-wa.h>
-#include <linux/uwb.h>
+#include "../wusbcore/include/wusb.h"
+#include "../wusbcore/include/wusb-wa.h"
+#include "uwb.h"
#include "uwb-internal.h"
diff --git a/drivers/uwb/i1480/Makefile b/drivers/staging/uwb/i1480/Makefile
index d26fb9b845ae..d26fb9b845ae 100644
--- a/drivers/uwb/i1480/Makefile
+++ b/drivers/staging/uwb/i1480/Makefile
diff --git a/drivers/uwb/i1480/dfu/Makefile b/drivers/staging/uwb/i1480/dfu/Makefile
index 4739fdac5922..4739fdac5922 100644
--- a/drivers/uwb/i1480/dfu/Makefile
+++ b/drivers/staging/uwb/i1480/dfu/Makefile
diff --git a/drivers/uwb/i1480/dfu/dfu.c b/drivers/staging/uwb/i1480/dfu/dfu.c
index ec1af858ead9..9d51ce8faad1 100644
--- a/drivers/uwb/i1480/dfu/dfu.c
+++ b/drivers/staging/uwb/i1480/dfu/dfu.c
@@ -17,9 +17,9 @@
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/device.h>
-#include <linux/uwb.h>
#include <linux/random.h>
#include <linux/export.h>
+#include "../../uwb.h"
/*
* i1480_rceb_check - Check RCEB for expected field values
diff --git a/drivers/uwb/i1480/dfu/i1480-dfu.h b/drivers/staging/uwb/i1480/dfu/i1480-dfu.h
index 9dd567d174b3..b21d058ecc23 100644
--- a/drivers/uwb/i1480/dfu/i1480-dfu.h
+++ b/drivers/staging/uwb/i1480/dfu/i1480-dfu.h
@@ -50,9 +50,9 @@
#ifndef __i1480_DFU_H__
#define __i1480_DFU_H__
-#include <linux/uwb/spec.h>
#include <linux/types.h>
#include <linux/completion.h>
+#include "../../include/spec.h"
#define i1480_FW_UPLOAD_MODE_MASK (cpu_to_le32(0x00000018))
diff --git a/drivers/uwb/i1480/dfu/mac.c b/drivers/staging/uwb/i1480/dfu/mac.c
index ddc224f01a7f..6e4d6c9cecf5 100644
--- a/drivers/uwb/i1480/dfu/mac.c
+++ b/drivers/staging/uwb/i1480/dfu/mac.c
@@ -15,7 +15,7 @@
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/slab.h>
-#include <linux/uwb.h>
+#include "../../uwb.h"
#include "i1480-dfu.h"
/*
diff --git a/drivers/uwb/i1480/dfu/phy.c b/drivers/staging/uwb/i1480/dfu/phy.c
index 50da4527c113..13512c7dda0b 100644
--- a/drivers/uwb/i1480/dfu/phy.c
+++ b/drivers/staging/uwb/i1480/dfu/phy.c
@@ -15,7 +15,7 @@
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/firmware.h>
-#include <linux/usb/wusb.h>
+#include "../../../wusbcore/include/wusb.h"
#include "i1480-dfu.h"
diff --git a/drivers/uwb/i1480/dfu/usb.c b/drivers/staging/uwb/i1480/dfu/usb.c
index 6129a8f4b5f2..d41086bdd783 100644
--- a/drivers/uwb/i1480/dfu/usb.c
+++ b/drivers/staging/uwb/i1480/dfu/usb.c
@@ -25,9 +25,9 @@
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/delay.h>
-#include <linux/uwb.h>
-#include <linux/usb/wusb.h>
-#include <linux/usb/wusb-wa.h>
+#include "../../uwb.h"
+#include "../../../wusbcore/include/wusb.h"
+#include "../../../wusbcore/include/wusb-wa.h"
#include "i1480-dfu.h"
struct i1480_usb {
diff --git a/drivers/uwb/i1480/i1480-est.c b/drivers/staging/uwb/i1480/i1480-est.c
index 1346c409d10e..106e0a44b138 100644
--- a/drivers/uwb/i1480/i1480-est.c
+++ b/drivers/staging/uwb/i1480/i1480-est.c
@@ -12,7 +12,7 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/usb.h>
-#include <linux/uwb.h>
+#include "../uwb.h"
#include "dfu/i1480-dfu.h"
diff --git a/drivers/uwb/ie-rcv.c b/drivers/staging/uwb/ie-rcv.c
index 51a4706e0dd3..51a4706e0dd3 100644
--- a/drivers/uwb/ie-rcv.c
+++ b/drivers/staging/uwb/ie-rcv.c
diff --git a/drivers/uwb/ie.c b/drivers/staging/uwb/ie.c
index b11678dd0380..b11678dd0380 100644
--- a/drivers/uwb/ie.c
+++ b/drivers/staging/uwb/ie.c
diff --git a/include/linux/uwb/debug-cmd.h b/drivers/staging/uwb/include/debug-cmd.h
index f97db6c3bcc0..f97db6c3bcc0 100644
--- a/include/linux/uwb/debug-cmd.h
+++ b/drivers/staging/uwb/include/debug-cmd.h
diff --git a/include/linux/uwb/spec.h b/drivers/staging/uwb/include/spec.h
index 5f75caf7b4ba..5f75caf7b4ba 100644
--- a/include/linux/uwb/spec.h
+++ b/drivers/staging/uwb/include/spec.h
diff --git a/include/linux/uwb/umc.h b/drivers/staging/uwb/include/umc.h
index ddbceb39ad15..ddbceb39ad15 100644
--- a/include/linux/uwb/umc.h
+++ b/drivers/staging/uwb/include/umc.h
diff --git a/include/linux/uwb/whci.h b/drivers/staging/uwb/include/whci.h
index 1a5c2cc2b008..1a5c2cc2b008 100644
--- a/include/linux/uwb/whci.h
+++ b/drivers/staging/uwb/include/whci.h
diff --git a/drivers/uwb/lc-dev.c b/drivers/staging/uwb/lc-dev.c
index 3e5c07fd6b10..3e5c07fd6b10 100644
--- a/drivers/uwb/lc-dev.c
+++ b/drivers/staging/uwb/lc-dev.c
diff --git a/drivers/uwb/lc-rc.c b/drivers/staging/uwb/lc-rc.c
index ee31b221cdc2..ee31b221cdc2 100644
--- a/drivers/uwb/lc-rc.c
+++ b/drivers/staging/uwb/lc-rc.c
diff --git a/drivers/uwb/neh.c b/drivers/staging/uwb/neh.c
index 1695584be412..1695584be412 100644
--- a/drivers/uwb/neh.c
+++ b/drivers/staging/uwb/neh.c
diff --git a/drivers/uwb/pal.c b/drivers/staging/uwb/pal.c
index 765fd426dbd1..a541e646a603 100644
--- a/drivers/uwb/pal.c
+++ b/drivers/staging/uwb/pal.c
@@ -6,9 +6,9 @@
*/
#include <linux/kernel.h>
#include <linux/debugfs.h>
-#include <linux/uwb.h>
#include <linux/export.h>
+#include "uwb.h"
#include "uwb-internal.h"
/**
diff --git a/drivers/uwb/radio.c b/drivers/staging/uwb/radio.c
index 240dd755927e..6afb75ce1b5f 100644
--- a/drivers/uwb/radio.c
+++ b/drivers/staging/uwb/radio.c
@@ -5,9 +5,9 @@
* Copyright (C) 2008 Cambridge Silicon Radio Ltd.
*/
#include <linux/kernel.h>
-#include <linux/uwb.h>
#include <linux/export.h>
+#include "uwb.h"
#include "uwb-internal.h"
diff --git a/drivers/uwb/reset.c b/drivers/staging/uwb/reset.c
index 8fc7c14d903e..8fc7c14d903e 100644
--- a/drivers/uwb/reset.c
+++ b/drivers/staging/uwb/reset.c
diff --git a/drivers/uwb/rsv.c b/drivers/staging/uwb/rsv.c
index ec924deb0a32..f45a04ff7275 100644
--- a/drivers/uwb/rsv.c
+++ b/drivers/staging/uwb/rsv.c
@@ -5,11 +5,11 @@
* Copyright (C) 2008 Cambridge Silicon Radio Ltd.
*/
#include <linux/kernel.h>
-#include <linux/uwb.h>
#include <linux/slab.h>
#include <linux/random.h>
#include <linux/export.h>
+#include "uwb.h"
#include "uwb-internal.h"
static void uwb_rsv_timer(struct timer_list *t);
diff --git a/drivers/uwb/scan.c b/drivers/staging/uwb/scan.c
index ffc3f452302d..ffc3f452302d 100644
--- a/drivers/uwb/scan.c
+++ b/drivers/staging/uwb/scan.c
diff --git a/drivers/uwb/umc-bus.c b/drivers/staging/uwb/umc-bus.c
index 0fdc38078eee..8b931f66a720 100644
--- a/drivers/uwb/umc-bus.c
+++ b/drivers/staging/uwb/umc-bus.c
@@ -8,8 +8,8 @@
#include <linux/sysfs.h>
#include <linux/workqueue.h>
#include <linux/module.h>
-#include <linux/uwb/umc.h>
#include <linux/pci.h>
+#include "include/umc.h"
static int umc_bus_pre_reset_helper(struct device *dev, void *data)
{
diff --git a/drivers/uwb/umc-dev.c b/drivers/staging/uwb/umc-dev.c
index c845ca414bb2..0c71caae00be 100644
--- a/drivers/uwb/umc-dev.c
+++ b/drivers/staging/uwb/umc-dev.c
@@ -7,7 +7,7 @@
#include <linux/kernel.h>
#include <linux/export.h>
#include <linux/slab.h>
-#include <linux/uwb/umc.h>
+#include "include/umc.h"
static void umc_device_release(struct device *dev)
{
diff --git a/drivers/uwb/umc-drv.c b/drivers/staging/uwb/umc-drv.c
index b141d520efbf..ed3bd220e8c2 100644
--- a/drivers/uwb/umc-drv.c
+++ b/drivers/staging/uwb/umc-drv.c
@@ -6,7 +6,7 @@
*/
#include <linux/kernel.h>
#include <linux/export.h>
-#include <linux/uwb/umc.h>
+#include "include/umc.h"
int __umc_driver_register(struct umc_driver *umc_drv, struct module *module,
const char *mod_name)
diff --git a/drivers/uwb/uwb-debug.c b/drivers/staging/uwb/uwb-debug.c
index 5457b6d42387..dd14df219ef8 100644
--- a/drivers/uwb/uwb-debug.c
+++ b/drivers/staging/uwb/uwb-debug.c
@@ -19,8 +19,7 @@
#include <linux/uaccess.h>
#include <linux/seq_file.h>
-#include <linux/uwb/debug-cmd.h>
-
+#include "include/debug-cmd.h"
#include "uwb-internal.h"
/*
diff --git a/drivers/uwb/uwb-internal.h b/drivers/staging/uwb/uwb-internal.h
index 00de0a5333d2..4c2fdac7f610 100644
--- a/drivers/uwb/uwb-internal.h
+++ b/drivers/staging/uwb/uwb-internal.h
@@ -17,8 +17,8 @@
#include <linux/kernel.h>
#include <linux/device.h>
-#include <linux/uwb.h>
#include <linux/mutex.h>
+#include "uwb.h"
struct uwb_beca_e;
diff --git a/include/linux/uwb.h b/drivers/staging/uwb/uwb.h
index 6918a61e1ac1..6a59706ba3a0 100644
--- a/include/linux/uwb.h
+++ b/drivers/staging/uwb/uwb.h
@@ -18,8 +18,8 @@
#include <linux/timer.h>
#include <linux/wait.h>
#include <linux/workqueue.h>
-#include <linux/uwb/spec.h>
#include <asm/page.h>
+#include "include/spec.h"
struct uwb_dev;
struct uwb_beca_e;
diff --git a/drivers/uwb/uwbd.c b/drivers/staging/uwb/uwbd.c
index dc5c743d4f5f..dc5c743d4f5f 100644
--- a/drivers/uwb/uwbd.c
+++ b/drivers/staging/uwb/uwbd.c
diff --git a/drivers/uwb/whc-rc.c b/drivers/staging/uwb/whc-rc.c
index 22397f70dee2..34020ed351ab 100644
--- a/drivers/uwb/whc-rc.c
+++ b/drivers/staging/uwb/whc-rc.c
@@ -33,9 +33,9 @@
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
-#include <linux/uwb.h>
-#include <linux/uwb/whci.h>
-#include <linux/uwb/umc.h>
+#include "uwb.h"
+#include "include/whci.h"
+#include "include/umc.h"
#include "uwb-internal.h"
diff --git a/drivers/uwb/whci.c b/drivers/staging/uwb/whci.c
index be8a8b8e857b..a8832f64d708 100644
--- a/drivers/uwb/whci.c
+++ b/drivers/staging/uwb/whci.c
@@ -10,8 +10,8 @@
#include <linux/pci.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
-#include <linux/uwb/whci.h>
-#include <linux/uwb/umc.h>
+#include "include/whci.h"
+#include "include/umc.h"
struct whci_card {
struct pci_dev *pci;
diff --git a/Documentation/usb/wusb-cbaf b/drivers/staging/wusbcore/Documentation/wusb-cbaf
index 8b3d43efce90..8b3d43efce90 100644
--- a/Documentation/usb/wusb-cbaf
+++ b/drivers/staging/wusbcore/Documentation/wusb-cbaf
diff --git a/Documentation/usb/wusb-design-overview.rst b/drivers/staging/wusbcore/Documentation/wusb-design-overview.rst
index dc5e21609bb5..dc5e21609bb5 100644
--- a/Documentation/usb/wusb-design-overview.rst
+++ b/drivers/staging/wusbcore/Documentation/wusb-design-overview.rst
diff --git a/drivers/usb/wusbcore/Kconfig b/drivers/staging/wusbcore/Kconfig
index abc0f361021f..a559d023b508 100644
--- a/drivers/usb/wusbcore/Kconfig
+++ b/drivers/staging/wusbcore/Kconfig
@@ -4,7 +4,7 @@
#
config USB_WUSB
tristate "Enable Wireless USB extensions"
- depends on UWB
+ depends on UWB && USB
select CRYPTO
select CRYPTO_AES
select CRYPTO_CCM
@@ -36,3 +36,4 @@ config USB_WUSB_CBAF_DEBUG
to the system log. Select this if you are having a problem with
CBA support and want to see more of what is going on.
+source "drivers/staging/wusbcore/host/Kconfig"
diff --git a/drivers/usb/wusbcore/Makefile b/drivers/staging/wusbcore/Makefile
index d604ccdd916f..b47b874268ac 100644
--- a/drivers/usb/wusbcore/Makefile
+++ b/drivers/staging/wusbcore/Makefile
@@ -24,3 +24,5 @@ wusb-wa-y := \
wa-nep.o \
wa-rpipe.o \
wa-xfer.o
+
+obj-y += host/
diff --git a/drivers/staging/wusbcore/TODO b/drivers/staging/wusbcore/TODO
new file mode 100644
index 000000000000..abae57000534
--- /dev/null
+++ b/drivers/staging/wusbcore/TODO
@@ -0,0 +1,8 @@
+TODO: Remove in late 2019 unless there are users
+
+There seems to not be any real wireless USB devices anywhere in the wild
+anymore. It turned out to be a failed technology :(
+
+This will be removed from the tree if no one objects.
+
+Greg Kroah-Hartman <gregkh@linuxfoundation.org>
diff --git a/drivers/usb/wusbcore/cbaf.c b/drivers/staging/wusbcore/cbaf.c
index af77064c7456..57062eaf7558 100644
--- a/drivers/usb/wusbcore/cbaf.c
+++ b/drivers/staging/wusbcore/cbaf.c
@@ -80,9 +80,9 @@
#include <linux/random.h>
#include <linux/slab.h>
#include <linux/mutex.h>
-#include <linux/uwb.h>
-#include <linux/usb/wusb.h>
-#include <linux/usb/association.h>
+#include "../uwb/uwb.h"
+#include "include/wusb.h"
+#include "include/association.h"
#define CBA_NAME_LEN 0x40 /* [WUSB-AM] table 4-7 */
diff --git a/drivers/usb/wusbcore/crypto.c b/drivers/staging/wusbcore/crypto.c
index 9ee66483ee54..d7d55ed19a98 100644
--- a/drivers/usb/wusbcore/crypto.c
+++ b/drivers/staging/wusbcore/crypto.c
@@ -38,10 +38,10 @@
#include <linux/crypto.h>
#include <linux/module.h>
#include <linux/err.h>
-#include <linux/uwb.h>
#include <linux/slab.h>
-#include <linux/usb/wusb.h>
#include <linux/scatterlist.h>
+#include "../uwb/uwb.h"
+#include "include/wusb.h"
static int debug_crypto_verify;
diff --git a/drivers/usb/wusbcore/dev-sysfs.c b/drivers/staging/wusbcore/dev-sysfs.c
index 67b0a4c412b2..67b0a4c412b2 100644
--- a/drivers/usb/wusbcore/dev-sysfs.c
+++ b/drivers/staging/wusbcore/dev-sysfs.c
diff --git a/drivers/usb/wusbcore/devconnect.c b/drivers/staging/wusbcore/devconnect.c
index a93837d57d53..1170f8baf608 100644
--- a/drivers/usb/wusbcore/devconnect.c
+++ b/drivers/staging/wusbcore/devconnect.c
@@ -49,7 +49,7 @@
* for processing a DN_Alive pong from a device.
*
* wusb_handle_dn_disconnect()Called by notif.c:wusb_handle_dn() to
- * process a disconenct request from a
+ * process a disconnect request from a
* device.
*
* __wusb_dev_disable() Called by rh.c:wusbhc_rh_clear_port_feat() when
diff --git a/drivers/staging/wusbcore/host/Kconfig b/drivers/staging/wusbcore/host/Kconfig
new file mode 100644
index 000000000000..9a73f9360a08
--- /dev/null
+++ b/drivers/staging/wusbcore/host/Kconfig
@@ -0,0 +1,28 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config USB_WHCI_HCD
+ tristate "Wireless USB Host Controller Interface (WHCI) driver"
+ depends on USB_PCI && USB && UWB
+ select USB_WUSB
+ select UWB_WHCI
+ help
+ A driver for PCI-based Wireless USB Host Controllers that are
+ compliant with the WHCI specification.
+
+ To compile this driver a module, choose M here: the module
+ will be called "whci-hcd".
+
+config USB_HWA_HCD
+ tristate "Host Wire Adapter (HWA) driver"
+ depends on USB && UWB
+ select USB_WUSB
+ select UWB_HWA
+ help
+ This driver enables you to connect Wireless USB devices to
+ your system using a Host Wire Adaptor USB dongle. This is an
+ UWB Radio Controller and WUSB Host Controller connected to
+ your machine via USB (specified in WUSB1.0).
+
+ To compile this driver a module, choose M here: the module
+ will be called "hwa-hc".
+
diff --git a/drivers/staging/wusbcore/host/Makefile b/drivers/staging/wusbcore/host/Makefile
new file mode 100644
index 000000000000..d65ee8a73e21
--- /dev/null
+++ b/drivers/staging/wusbcore/host/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_USB_WHCI_HCD) += whci/
+obj-$(CONFIG_USB_HWA_HCD) += hwa-hc.o
diff --git a/drivers/usb/host/hwa-hc.c b/drivers/staging/wusbcore/host/hwa-hc.c
index 6968b9f2b76b..8d959e91fe27 100644
--- a/drivers/usb/host/hwa-hc.c
+++ b/drivers/staging/wusbcore/host/hwa-hc.c
@@ -45,8 +45,8 @@
#include <linux/workqueue.h>
#include <linux/wait.h>
#include <linux/completion.h>
-#include "../wusbcore/wa-hc.h"
-#include "../wusbcore/wusbhc.h"
+#include "../wa-hc.h"
+#include "../wusbhc.h"
struct hwahc {
struct wusbhc wusbhc; /* has to be 1st */
diff --git a/drivers/usb/host/whci/Makefile b/drivers/staging/wusbcore/host/whci/Makefile
index 859d20079df6..859d20079df6 100644
--- a/drivers/usb/host/whci/Makefile
+++ b/drivers/staging/wusbcore/host/whci/Makefile
diff --git a/drivers/usb/host/whci/asl.c b/drivers/staging/wusbcore/host/whci/asl.c
index 276fb34c8efd..a2b9a50cfb80 100644
--- a/drivers/usb/host/whci/asl.c
+++ b/drivers/staging/wusbcore/host/whci/asl.c
@@ -7,10 +7,10 @@
#include <linux/kernel.h>
#include <linux/gfp.h>
#include <linux/dma-mapping.h>
-#include <linux/uwb/umc.h>
#include <linux/usb.h>
-#include "../../wusbcore/wusbhc.h"
+#include "../../../uwb/include/umc.h"
+#include "../../wusbhc.h"
#include "whcd.h"
diff --git a/drivers/usb/host/whci/debug.c b/drivers/staging/wusbcore/host/whci/debug.c
index 8ddfe3f1f693..443da6719147 100644
--- a/drivers/usb/host/whci/debug.c
+++ b/drivers/staging/wusbcore/host/whci/debug.c
@@ -10,7 +10,7 @@
#include <linux/seq_file.h>
#include <linux/export.h>
-#include "../../wusbcore/wusbhc.h"
+#include "../../wusbhc.h"
#include "whcd.h"
diff --git a/drivers/usb/host/whci/hcd.c b/drivers/staging/wusbcore/host/whci/hcd.c
index 8af9dcfea127..bee1ff2d35be 100644
--- a/drivers/usb/host/whci/hcd.c
+++ b/drivers/staging/wusbcore/host/whci/hcd.c
@@ -7,9 +7,9 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
-#include <linux/uwb/umc.h>
-#include "../../wusbcore/wusbhc.h"
+#include "../../../uwb/include/umc.h"
+#include "../../wusbhc.h"
#include "whcd.h"
diff --git a/drivers/usb/host/whci/hw.c b/drivers/staging/wusbcore/host/whci/hw.c
index 22b3b7f7419d..e4e8914abf42 100644
--- a/drivers/usb/host/whci/hw.c
+++ b/drivers/staging/wusbcore/host/whci/hw.c
@@ -6,9 +6,9 @@
*/
#include <linux/kernel.h>
#include <linux/dma-mapping.h>
-#include <linux/uwb/umc.h>
-#include "../../wusbcore/wusbhc.h"
+#include "../../../uwb/include/umc.h"
+#include "../../wusbhc.h"
#include "whcd.h"
diff --git a/drivers/usb/host/whci/init.c b/drivers/staging/wusbcore/host/whci/init.c
index 82416973f773..55fd458a8f30 100644
--- a/drivers/usb/host/whci/init.c
+++ b/drivers/staging/wusbcore/host/whci/init.c
@@ -7,9 +7,9 @@
#include <linux/kernel.h>
#include <linux/gfp.h>
#include <linux/dma-mapping.h>
-#include <linux/uwb/umc.h>
-#include "../../wusbcore/wusbhc.h"
+#include "../../../uwb/include/umc.h"
+#include "../../wusbhc.h"
#include "whcd.h"
diff --git a/drivers/usb/host/whci/int.c b/drivers/staging/wusbcore/host/whci/int.c
index 7e4ad1b8f3e3..bdbe35e9366f 100644
--- a/drivers/usb/host/whci/int.c
+++ b/drivers/staging/wusbcore/host/whci/int.c
@@ -5,9 +5,9 @@
* Copyright (C) 2007 Cambridge Silicon Radio Ltd.
*/
#include <linux/kernel.h>
-#include <linux/uwb/umc.h>
-#include "../../wusbcore/wusbhc.h"
+#include "../../../uwb/include/umc.h"
+#include "../../wusbhc.h"
#include "whcd.h"
diff --git a/drivers/usb/host/whci/pzl.c b/drivers/staging/wusbcore/host/whci/pzl.c
index ef52aeb02fde..6dfc075f5798 100644
--- a/drivers/usb/host/whci/pzl.c
+++ b/drivers/staging/wusbcore/host/whci/pzl.c
@@ -7,10 +7,10 @@
#include <linux/kernel.h>
#include <linux/gfp.h>
#include <linux/dma-mapping.h>
-#include <linux/uwb/umc.h>
#include <linux/usb.h>
-#include "../../wusbcore/wusbhc.h"
+#include "../../../uwb/include/umc.h"
+#include "../../wusbhc.h"
#include "whcd.h"
diff --git a/drivers/usb/host/whci/qset.c b/drivers/staging/wusbcore/host/whci/qset.c
index 925166a207aa..66459b77dc77 100644
--- a/drivers/usb/host/whci/qset.c
+++ b/drivers/staging/wusbcore/host/whci/qset.c
@@ -7,10 +7,10 @@
#include <linux/kernel.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
-#include <linux/uwb/umc.h>
#include <linux/usb.h>
-#include "../../wusbcore/wusbhc.h"
+#include "../../../uwb/include/umc.h"
+#include "../../wusbhc.h"
#include "whcd.h"
diff --git a/drivers/usb/host/whci/whcd.h b/drivers/staging/wusbcore/host/whci/whcd.h
index 139476997e7c..a442a2589e83 100644
--- a/drivers/usb/host/whci/whcd.h
+++ b/drivers/staging/wusbcore/host/whci/whcd.h
@@ -7,10 +7,10 @@
#ifndef __WHCD_H
#define __WHCD_H
-#include <linux/uwb/whci.h>
-#include <linux/uwb/umc.h>
#include <linux/workqueue.h>
+#include "../../../uwb/include/whci.h"
+#include "../../../uwb/include/umc.h"
#include "whci-hc.h"
/* Generic command timeout. */
diff --git a/drivers/usb/host/whci/whci-hc.h b/drivers/staging/wusbcore/host/whci/whci-hc.h
index 5a86a57a80cc..5a86a57a80cc 100644
--- a/drivers/usb/host/whci/whci-hc.h
+++ b/drivers/staging/wusbcore/host/whci/whci-hc.h
diff --git a/drivers/usb/host/whci/wusb.c b/drivers/staging/wusbcore/host/whci/wusb.c
index 8a4d805ff63a..6d0068ab35e4 100644
--- a/drivers/usb/host/whci/wusb.c
+++ b/drivers/staging/wusbcore/host/whci/wusb.c
@@ -5,9 +5,9 @@
* Copyright (C) 2007 Cambridge Silicon Radio Ltd.
*/
#include <linux/kernel.h>
-#include <linux/uwb/umc.h>
-#include "../../wusbcore/wusbhc.h"
+#include "../../../uwb/include/umc.h"
+#include "../../wusbhc.h"
#include "whcd.h"
diff --git a/include/linux/usb/association.h b/drivers/staging/wusbcore/include/association.h
index d7f3cb9b9db5..d7f3cb9b9db5 100644
--- a/include/linux/usb/association.h
+++ b/drivers/staging/wusbcore/include/association.h
diff --git a/include/linux/usb/wusb-wa.h b/drivers/staging/wusbcore/include/wusb-wa.h
index 64a840b5106e..64a840b5106e 100644
--- a/include/linux/usb/wusb-wa.h
+++ b/drivers/staging/wusbcore/include/wusb-wa.h
diff --git a/include/linux/usb/wusb.h b/drivers/staging/wusbcore/include/wusb.h
index 65adee629106..09771d1da7bc 100644
--- a/include/linux/usb/wusb.h
+++ b/drivers/staging/wusbcore/include/wusb.h
@@ -33,9 +33,9 @@
#include <linux/types.h>
#include <linux/kernel.h>
-#include <linux/uwb/spec.h>
#include <linux/usb/ch9.h>
#include <linux/param.h>
+#include "../../uwb/include/spec.h"
/**
* WUSB Information Element header
diff --git a/drivers/usb/wusbcore/mmc.c b/drivers/staging/wusbcore/mmc.c
index acce0d551eb2..881e1f20d718 100644
--- a/drivers/usb/wusbcore/mmc.c
+++ b/drivers/staging/wusbcore/mmc.c
@@ -22,9 +22,9 @@
* FIXME:
* - add timers that autoremove intervalled IEs?
*/
-#include <linux/usb/wusb.h>
#include <linux/slab.h>
#include <linux/export.h>
+#include "include/wusb.h"
#include "wusbhc.h"
/* Initialize the MMCIEs handling mechanism */
diff --git a/drivers/usb/wusbcore/pal.c b/drivers/staging/wusbcore/pal.c
index 30f569131471..30f569131471 100644
--- a/drivers/usb/wusbcore/pal.c
+++ b/drivers/staging/wusbcore/pal.c
diff --git a/drivers/usb/wusbcore/reservation.c b/drivers/staging/wusbcore/reservation.c
index 6dcfc6825f55..b921faac698b 100644
--- a/drivers/usb/wusbcore/reservation.c
+++ b/drivers/staging/wusbcore/reservation.c
@@ -5,8 +5,8 @@
* Copyright (C) 2007 Cambridge Silicon Radio Ltd.
*/
#include <linux/kernel.h>
-#include <linux/uwb.h>
+#include "../uwb/uwb.h"
#include "wusbhc.h"
/*
diff --git a/drivers/usb/wusbcore/rh.c b/drivers/staging/wusbcore/rh.c
index 20c08cd9dcbf..20c08cd9dcbf 100644
--- a/drivers/usb/wusbcore/rh.c
+++ b/drivers/staging/wusbcore/rh.c
diff --git a/drivers/usb/wusbcore/security.c b/drivers/staging/wusbcore/security.c
index 14ac8c98ac9e..14ac8c98ac9e 100644
--- a/drivers/usb/wusbcore/security.c
+++ b/drivers/staging/wusbcore/security.c
diff --git a/drivers/usb/wusbcore/wa-hc.c b/drivers/staging/wusbcore/wa-hc.c
index 6827075fb8a1..6827075fb8a1 100644
--- a/drivers/usb/wusbcore/wa-hc.c
+++ b/drivers/staging/wusbcore/wa-hc.c
diff --git a/drivers/usb/wusbcore/wa-hc.h b/drivers/staging/wusbcore/wa-hc.h
index ec90fff21deb..5a38465724c2 100644
--- a/drivers/usb/wusbcore/wa-hc.h
+++ b/drivers/staging/wusbcore/wa-hc.h
@@ -70,9 +70,9 @@
#include <linux/usb.h>
#include <linux/mutex.h>
#include <linux/spinlock.h>
-#include <linux/uwb.h>
-#include <linux/usb/wusb.h>
-#include <linux/usb/wusb-wa.h>
+#include "../uwb/uwb.h"
+#include "include/wusb.h"
+#include "include/wusb-wa.h"
struct wusbhc;
struct wahc;
diff --git a/drivers/usb/wusbcore/wa-nep.c b/drivers/staging/wusbcore/wa-nep.c
index 5f0656db5482..5f0656db5482 100644
--- a/drivers/usb/wusbcore/wa-nep.c
+++ b/drivers/staging/wusbcore/wa-nep.c
diff --git a/drivers/usb/wusbcore/wa-rpipe.c b/drivers/staging/wusbcore/wa-rpipe.c
index a5734cbcd5ad..a5734cbcd5ad 100644
--- a/drivers/usb/wusbcore/wa-rpipe.c
+++ b/drivers/staging/wusbcore/wa-rpipe.c
diff --git a/drivers/usb/wusbcore/wa-xfer.c b/drivers/staging/wusbcore/wa-xfer.c
index abf88cea37bb..abf88cea37bb 100644
--- a/drivers/usb/wusbcore/wa-xfer.c
+++ b/drivers/staging/wusbcore/wa-xfer.c
diff --git a/drivers/usb/wusbcore/wusbhc.c b/drivers/staging/wusbcore/wusbhc.c
index d0b404d258e8..d0b404d258e8 100644
--- a/drivers/usb/wusbcore/wusbhc.c
+++ b/drivers/staging/wusbcore/wusbhc.c
diff --git a/drivers/usb/wusbcore/wusbhc.h b/drivers/staging/wusbcore/wusbhc.h
index 7681d796ca5b..716244a2ec44 100644
--- a/drivers/usb/wusbcore/wusbhc.h
+++ b/drivers/staging/wusbcore/wusbhc.h
@@ -45,8 +45,8 @@
#include <linux/kref.h>
#include <linux/workqueue.h>
#include <linux/usb/hcd.h>
-#include <linux/uwb.h>
-#include <linux/usb/wusb.h>
+#include "../uwb/uwb.h"
+#include "include/wusb.h"
/*
* Time from a WUSB channel stop request to the last transmitted MMC.
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 6e59d370ef81..275568abc670 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -36,8 +36,7 @@ menuconfig USB_SUPPORT
if USB_SUPPORT
-config USB_COMMON
- tristate
+source "drivers/usb/common/Kconfig"
config USB_ARCH_HAS_HCD
def_bool y
@@ -98,8 +97,6 @@ source "drivers/usb/core/Kconfig"
source "drivers/usb/mon/Kconfig"
-source "drivers/usb/wusbcore/Kconfig"
-
source "drivers/usb/host/Kconfig"
source "drivers/usb/renesas_usbhs/Kconfig"
@@ -114,6 +111,8 @@ source "drivers/usb/usbip/Kconfig"
endif
+source "drivers/usb/cdns3/Kconfig"
+
source "drivers/usb/mtu3/Kconfig"
source "drivers/usb/musb/Kconfig"
@@ -175,36 +174,4 @@ source "drivers/usb/typec/Kconfig"
source "drivers/usb/roles/Kconfig"
-config USB_LED_TRIG
- bool "USB LED Triggers"
- depends on LEDS_CLASS && LEDS_TRIGGERS
- select USB_COMMON
- help
- This option adds LED triggers for USB host and/or gadget activity.
-
- Say Y here if you are working on a system with led-class supported
- LEDs and you want to use them as activity indicators for USB host or
- gadget.
-
-config USB_ULPI_BUS
- tristate "USB ULPI PHY interface support"
- select USB_COMMON
- help
- UTMI+ Low Pin Interface (ULPI) is specification for a commonly used
- USB 2.0 PHY interface. The ULPI specification defines a standard set
- of registers that can be used to detect the vendor and product which
- allows ULPI to be handled as a bus. This module is the driver for that
- bus.
-
- The ULPI interfaces (the buses) are registered by the drivers for USB
- controllers which support ULPI register access and have ULPI PHY
- attached to them. The ULPI PHY drivers themselves are normal PHY
- drivers.
-
- ULPI PHYs provide often functions such as ADP sensing/probing (OTG
- protocol) and USB charger detection.
-
- To compile this driver as a module, choose M here: the module will
- be called ulpi.
-
endif # USB_SUPPORT
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index ecc2de1ffaae..1c1c1d659394 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -13,6 +13,8 @@ obj-$(CONFIG_USB_DWC3) += dwc3/
obj-$(CONFIG_USB_DWC2) += dwc2/
obj-$(CONFIG_USB_ISP1760) += isp1760/
+obj-$(CONFIG_USB_CDNS3) += cdns3/
+
obj-$(CONFIG_USB_MON) += mon/
obj-$(CONFIG_USB_MTU3) += mtu3/
@@ -35,8 +37,6 @@ obj-$(CONFIG_USB_MAX3421_HCD) += host/
obj-$(CONFIG_USB_C67X00_HCD) += c67x00/
-obj-$(CONFIG_USB_WUSB) += wusbcore/
-
obj-$(CONFIG_USB_ACM) += class/
obj-$(CONFIG_USB_PRINTER) += class/
obj-$(CONFIG_USB_WDM) += class/
diff --git a/drivers/usb/atm/cxacru.c b/drivers/usb/atm/cxacru.c
index e57a2be8754a..5d41f85a7445 100644
--- a/drivers/usb/atm/cxacru.c
+++ b/drivers/usb/atm/cxacru.c
@@ -539,6 +539,37 @@ CXACRU_SET_##_action( adsl_config);
CXACRU_ALL_FILES(INIT);
+static struct attribute *cxacru_attrs[] = {
+ &dev_attr_adsl_config.attr,
+ &dev_attr_adsl_state.attr,
+ &dev_attr_adsl_controller_version.attr,
+ &dev_attr_adsl_headend_environment.attr,
+ &dev_attr_adsl_headend.attr,
+ &dev_attr_modulation.attr,
+ &dev_attr_line_startable.attr,
+ &dev_attr_downstream_hec_errors.attr,
+ &dev_attr_upstream_hec_errors.attr,
+ &dev_attr_downstream_fec_errors.attr,
+ &dev_attr_upstream_fec_errors.attr,
+ &dev_attr_downstream_crc_errors.attr,
+ &dev_attr_upstream_crc_errors.attr,
+ &dev_attr_startup_attempts.attr,
+ &dev_attr_downstream_bits_per_frame.attr,
+ &dev_attr_upstream_bits_per_frame.attr,
+ &dev_attr_transmitter_power.attr,
+ &dev_attr_downstream_attenuation.attr,
+ &dev_attr_upstream_attenuation.attr,
+ &dev_attr_downstream_snr_margin.attr,
+ &dev_attr_upstream_snr_margin.attr,
+ &dev_attr_mac_address.attr,
+ &dev_attr_line_status.attr,
+ &dev_attr_link_status.attr,
+ &dev_attr_upstream_rate.attr,
+ &dev_attr_downstream_rate.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(cxacru);
+
/* the following three functions are stolen from drivers/usb/core/message.c */
static void cxacru_blocking_completion(struct urb *urb)
{
@@ -736,17 +767,6 @@ static int cxacru_card_status(struct cxacru_data *instance)
return 0;
}
-static void cxacru_remove_device_files(struct usbatm_data *usbatm_instance,
- struct atm_dev *atm_dev)
-{
- struct usb_interface *intf = usbatm_instance->usb_intf;
-
- #define CXACRU_DEVICE_REMOVE_FILE(_name) \
- device_remove_file(&intf->dev, &dev_attr_##_name);
- CXACRU_ALL_FILES(REMOVE);
- #undef CXACRU_DEVICE_REMOVE_FILE
-}
-
static int cxacru_atm_start(struct usbatm_data *usbatm_instance,
struct atm_dev *atm_dev)
{
@@ -765,13 +785,6 @@ static int cxacru_atm_start(struct usbatm_data *usbatm_instance,
return ret;
}
- #define CXACRU_DEVICE_CREATE_FILE(_name) \
- ret = device_create_file(&intf->dev, &dev_attr_##_name); \
- if (unlikely(ret)) \
- goto fail_sysfs;
- CXACRU_ALL_FILES(CREATE);
- #undef CXACRU_DEVICE_CREATE_FILE
-
/* start ADSL */
mutex_lock(&instance->adsl_state_serialize);
ret = cxacru_cm(instance, CM_REQUEST_CHIP_ADSL_LINE_START, NULL, 0, NULL, 0);
@@ -804,11 +817,6 @@ static int cxacru_atm_start(struct usbatm_data *usbatm_instance,
if (start_polling)
cxacru_poll_status(&instance->poll_work.work);
return 0;
-
-fail_sysfs:
- usb_err(usbatm_instance, "cxacru_atm_start: device_create_file failed (%d)\n", ret);
- cxacru_remove_device_files(usbatm_instance, atm_dev);
- return ret;
}
static void cxacru_poll_status(struct work_struct *work)
@@ -1332,7 +1340,6 @@ static struct usbatm_driver cxacru_driver = {
.heavy_init = cxacru_heavy_init,
.unbind = cxacru_unbind,
.atm_start = cxacru_atm_start,
- .atm_stop = cxacru_remove_device_files,
.bulk_in = CXACRU_EP_DATA,
.bulk_out = CXACRU_EP_DATA,
.rx_padding = 3,
@@ -1364,7 +1371,8 @@ static struct usb_driver cxacru_usb_driver = {
.name = cxacru_driver_name,
.probe = cxacru_usb_probe,
.disconnect = usbatm_usb_disconnect,
- .id_table = cxacru_usb_ids
+ .id_table = cxacru_usb_ids,
+ .dev_groups = cxacru_groups,
};
module_usb_driver(cxacru_usb_driver);
diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c
index 8faa51b1a520..8b0ea8c70d73 100644
--- a/drivers/usb/atm/ueagle-atm.c
+++ b/drivers/usb/atm/ueagle-atm.c
@@ -2458,7 +2458,7 @@ static int claim_interface(struct usb_device *usb_dev,
return ret;
}
-static struct attribute *attrs[] = {
+static struct attribute *uea_attrs[] = {
&dev_attr_stat_status.attr,
&dev_attr_stat_mflags.attr,
&dev_attr_stat_human_status.attr,
@@ -2479,9 +2479,7 @@ static struct attribute *attrs[] = {
&dev_attr_stat_firmid.attr,
NULL,
};
-static const struct attribute_group attr_grp = {
- .attrs = attrs,
-};
+ATTRIBUTE_GROUPS(uea);
static int uea_bind(struct usbatm_data *usbatm, struct usb_interface *intf,
const struct usb_device_id *id)
@@ -2550,18 +2548,12 @@ static int uea_bind(struct usbatm_data *usbatm, struct usb_interface *intf,
}
}
- ret = sysfs_create_group(&intf->dev.kobj, &attr_grp);
- if (ret < 0)
- goto error;
-
ret = uea_boot(sc);
if (ret < 0)
- goto error_rm_grp;
+ goto error;
return 0;
-error_rm_grp:
- sysfs_remove_group(&intf->dev.kobj, &attr_grp);
error:
kfree(sc);
return ret;
@@ -2571,7 +2563,6 @@ static void uea_unbind(struct usbatm_data *usbatm, struct usb_interface *intf)
{
struct uea_softc *sc = usbatm->driver_data;
- sysfs_remove_group(&intf->dev.kobj, &attr_grp);
uea_stop(sc);
kfree(sc);
}
@@ -2721,6 +2712,7 @@ static struct usb_driver uea_driver = {
.id_table = uea_ids,
.probe = uea_probe,
.disconnect = uea_disconnect,
+ .dev_groups = uea_groups,
};
MODULE_DEVICE_TABLE(usb, uea_ids);
diff --git a/drivers/usb/cdns3/Kconfig b/drivers/usb/cdns3/Kconfig
new file mode 100644
index 000000000000..d0331613a355
--- /dev/null
+++ b/drivers/usb/cdns3/Kconfig
@@ -0,0 +1,46 @@
+config USB_CDNS3
+ tristate "Cadence USB3 Dual-Role Controller"
+ depends on USB_SUPPORT && (USB || USB_GADGET) && HAS_DMA
+ select USB_XHCI_PLATFORM if USB_XHCI_HCD
+ select USB_ROLE_SWITCH
+ help
+ Say Y here if your system has a Cadence USB3 dual-role controller.
+ It supports: dual-role switch, Host-only, and Peripheral-only.
+
+ If you choose to build this driver is a dynamically linked
+ as module, the module will be called cdns3.ko.
+
+if USB_CDNS3
+
+config USB_CDNS3_GADGET
+ bool "Cadence USB3 device controller"
+ depends on USB_GADGET=y || USB_GADGET=USB_CDNS3
+ help
+ Say Y here to enable device controller functionality of the
+ Cadence USBSS-DEV driver.
+
+ This controller supports FF, HS and SS mode. It doesn't support
+ LS and SSP mode.
+
+config USB_CDNS3_HOST
+ bool "Cadence USB3 host controller"
+ depends on USB=y || USB=USB_CDNS3
+ help
+ Say Y here to enable host controller functionality of the
+ Cadence driver.
+
+ Host controller is compliant with XHCI so it will use
+ standard XHCI driver.
+
+config USB_CDNS3_PCI_WRAP
+ tristate "Cadence USB3 support on PCIe-based platforms"
+ depends on USB_PCI && ACPI
+ default USB_CDNS3
+ help
+ If you're using the USBSS Core IP with a PCIe, please say
+ 'Y' or 'M' here.
+
+ If you choose to build this driver as module it will
+ be dynamically linked and module will be called cdns3-pci.ko
+
+endif
diff --git a/drivers/usb/cdns3/Makefile b/drivers/usb/cdns3/Makefile
new file mode 100644
index 000000000000..a703547350bb
--- /dev/null
+++ b/drivers/usb/cdns3/Makefile
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0
+# define_trace.h needs to know how to find our header
+CFLAGS_trace.o := -I$(src)
+
+cdns3-y := core.o drd.o
+
+obj-$(CONFIG_USB_CDNS3) += cdns3.o
+cdns3-$(CONFIG_USB_CDNS3_GADGET) += gadget.o ep0.o
+
+ifneq ($(CONFIG_USB_CDNS3_GADGET),)
+cdns3-$(CONFIG_TRACING) += trace.o
+endif
+
+cdns3-$(CONFIG_USB_CDNS3_HOST) += host.o
+
+obj-$(CONFIG_USB_CDNS3_PCI_WRAP) += cdns3-pci-wrap.o
diff --git a/drivers/usb/cdns3/cdns3-pci-wrap.c b/drivers/usb/cdns3/cdns3-pci-wrap.c
new file mode 100644
index 000000000000..c41ddb61b857
--- /dev/null
+++ b/drivers/usb/cdns3/cdns3-pci-wrap.c
@@ -0,0 +1,203 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Cadence USBSS PCI Glue driver
+ *
+ * Copyright (C) 2018-2019 Cadence.
+ *
+ * Author: Pawel Laszczak <pawell@cadence.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+
+struct cdns3_wrap {
+ struct platform_device *plat_dev;
+ struct resource dev_res[6];
+ int devfn;
+};
+
+#define RES_IRQ_HOST_ID 0
+#define RES_IRQ_PERIPHERAL_ID 1
+#define RES_IRQ_OTG_ID 2
+#define RES_HOST_ID 3
+#define RES_DEV_ID 4
+#define RES_DRD_ID 5
+
+#define PCI_BAR_HOST 0
+#define PCI_BAR_DEV 2
+#define PCI_BAR_OTG 0
+
+#define PCI_DEV_FN_HOST_DEVICE 0
+#define PCI_DEV_FN_OTG 1
+
+#define PCI_DRIVER_NAME "cdns3-pci-usbss"
+#define PLAT_DRIVER_NAME "cdns-usb3"
+
+#define CDNS_VENDOR_ID 0x17cd
+#define CDNS_DEVICE_ID 0x0100
+
+static struct pci_dev *cdns3_get_second_fun(struct pci_dev *pdev)
+{
+ struct pci_dev *func;
+
+ /*
+ * Gets the second function.
+ * It's little tricky, but this platform has two function.
+ * The fist keeps resources for Host/Device while the second
+ * keeps resources for DRD/OTG.
+ */
+ func = pci_get_device(pdev->vendor, pdev->device, NULL);
+ if (unlikely(!func))
+ return NULL;
+
+ if (func->devfn == pdev->devfn) {
+ func = pci_get_device(pdev->vendor, pdev->device, func);
+ if (unlikely(!func))
+ return NULL;
+ }
+
+ return func;
+}
+
+static int cdns3_pci_probe(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ struct platform_device_info plat_info;
+ struct cdns3_wrap *wrap;
+ struct resource *res;
+ struct pci_dev *func;
+ int err;
+
+ /*
+ * for GADGET/HOST PCI (devfn) function number is 0,
+ * for OTG PCI (devfn) function number is 1
+ */
+ if (!id || (pdev->devfn != PCI_DEV_FN_HOST_DEVICE &&
+ pdev->devfn != PCI_DEV_FN_OTG))
+ return -EINVAL;
+
+ func = cdns3_get_second_fun(pdev);
+ if (unlikely(!func))
+ return -EINVAL;
+
+ err = pcim_enable_device(pdev);
+ if (err) {
+ dev_err(&pdev->dev, "Enabling PCI device has failed %d\n", err);
+ return err;
+ }
+
+ pci_set_master(pdev);
+
+ if (pci_is_enabled(func)) {
+ wrap = pci_get_drvdata(func);
+ } else {
+ wrap = kzalloc(sizeof(*wrap), GFP_KERNEL);
+ if (!wrap) {
+ pci_disable_device(pdev);
+ return -ENOMEM;
+ }
+ }
+
+ res = wrap->dev_res;
+
+ if (pdev->devfn == PCI_DEV_FN_HOST_DEVICE) {
+ /* function 0: host(BAR_0) + device(BAR_1).*/
+ dev_dbg(&pdev->dev, "Initialize Device resources\n");
+ res[RES_DEV_ID].start = pci_resource_start(pdev, PCI_BAR_DEV);
+ res[RES_DEV_ID].end = pci_resource_end(pdev, PCI_BAR_DEV);
+ res[RES_DEV_ID].name = "dev";
+ res[RES_DEV_ID].flags = IORESOURCE_MEM;
+ dev_dbg(&pdev->dev, "USBSS-DEV physical base addr: %pa\n",
+ &res[RES_DEV_ID].start);
+
+ res[RES_HOST_ID].start = pci_resource_start(pdev, PCI_BAR_HOST);
+ res[RES_HOST_ID].end = pci_resource_end(pdev, PCI_BAR_HOST);
+ res[RES_HOST_ID].name = "xhci";
+ res[RES_HOST_ID].flags = IORESOURCE_MEM;
+ dev_dbg(&pdev->dev, "USBSS-XHCI physical base addr: %pa\n",
+ &res[RES_HOST_ID].start);
+
+ /* Interrupt for XHCI */
+ wrap->dev_res[RES_IRQ_HOST_ID].start = pdev->irq;
+ wrap->dev_res[RES_IRQ_HOST_ID].name = "host";
+ wrap->dev_res[RES_IRQ_HOST_ID].flags = IORESOURCE_IRQ;
+
+ /* Interrupt device. It's the same as for HOST. */
+ wrap->dev_res[RES_IRQ_PERIPHERAL_ID].start = pdev->irq;
+ wrap->dev_res[RES_IRQ_PERIPHERAL_ID].name = "peripheral";
+ wrap->dev_res[RES_IRQ_PERIPHERAL_ID].flags = IORESOURCE_IRQ;
+ } else {
+ res[RES_DRD_ID].start = pci_resource_start(pdev, PCI_BAR_OTG);
+ res[RES_DRD_ID].end = pci_resource_end(pdev, PCI_BAR_OTG);
+ res[RES_DRD_ID].name = "otg";
+ res[RES_DRD_ID].flags = IORESOURCE_MEM;
+ dev_dbg(&pdev->dev, "USBSS-DRD physical base addr: %pa\n",
+ &res[RES_DRD_ID].start);
+
+ /* Interrupt for OTG/DRD. */
+ wrap->dev_res[RES_IRQ_OTG_ID].start = pdev->irq;
+ wrap->dev_res[RES_IRQ_OTG_ID].name = "otg";
+ wrap->dev_res[RES_IRQ_OTG_ID].flags = IORESOURCE_IRQ;
+ }
+
+ if (pci_is_enabled(func)) {
+ /* set up platform device info */
+ memset(&plat_info, 0, sizeof(plat_info));
+ plat_info.parent = &pdev->dev;
+ plat_info.fwnode = pdev->dev.fwnode;
+ plat_info.name = PLAT_DRIVER_NAME;
+ plat_info.id = pdev->devfn;
+ wrap->devfn = pdev->devfn;
+ plat_info.res = wrap->dev_res;
+ plat_info.num_res = ARRAY_SIZE(wrap->dev_res);
+ plat_info.dma_mask = pdev->dma_mask;
+ /* register platform device */
+ wrap->plat_dev = platform_device_register_full(&plat_info);
+ if (IS_ERR(wrap->plat_dev)) {
+ pci_disable_device(pdev);
+ kfree(wrap);
+ return PTR_ERR(wrap->plat_dev);
+ }
+ }
+
+ pci_set_drvdata(pdev, wrap);
+ return err;
+}
+
+static void cdns3_pci_remove(struct pci_dev *pdev)
+{
+ struct cdns3_wrap *wrap;
+ struct pci_dev *func;
+
+ func = cdns3_get_second_fun(pdev);
+
+ wrap = (struct cdns3_wrap *)pci_get_drvdata(pdev);
+ if (wrap->devfn == pdev->devfn)
+ platform_device_unregister(wrap->plat_dev);
+
+ if (!pci_is_enabled(func))
+ kfree(wrap);
+}
+
+static const struct pci_device_id cdns3_pci_ids[] = {
+ { PCI_DEVICE(CDNS_VENDOR_ID, CDNS_DEVICE_ID), },
+ { 0, }
+};
+
+static struct pci_driver cdns3_pci_driver = {
+ .name = PCI_DRIVER_NAME,
+ .id_table = cdns3_pci_ids,
+ .probe = cdns3_pci_probe,
+ .remove = cdns3_pci_remove,
+};
+
+module_pci_driver(cdns3_pci_driver);
+MODULE_DEVICE_TABLE(pci, cdns3_pci_ids);
+
+MODULE_AUTHOR("Pawel Laszczak <pawell@cadence.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Cadence USBSS PCI wrapperr");
diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c
new file mode 100644
index 000000000000..06f1e105be4e
--- /dev/null
+++ b/drivers/usb/cdns3/core.c
@@ -0,0 +1,651 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Cadence USBSS DRD Driver.
+ *
+ * Copyright (C) 2018-2019 Cadence.
+ * Copyright (C) 2017-2018 NXP
+ * Copyright (C) 2019 Texas Instruments
+ *
+ * Author: Peter Chen <peter.chen@nxp.com>
+ * Pawel Laszczak <pawell@cadence.com>
+ * Roger Quadros <rogerq@ti.com>
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/pm_runtime.h>
+
+#include "gadget.h"
+#include "core.h"
+#include "host-export.h"
+#include "gadget-export.h"
+#include "drd.h"
+
+static int cdns3_idle_init(struct cdns3 *cdns);
+
+static inline
+struct cdns3_role_driver *cdns3_get_current_role_driver(struct cdns3 *cdns)
+{
+ WARN_ON(!cdns->roles[cdns->role]);
+ return cdns->roles[cdns->role];
+}
+
+static int cdns3_role_start(struct cdns3 *cdns, enum usb_role role)
+{
+ int ret;
+
+ if (WARN_ON(role > USB_ROLE_DEVICE))
+ return 0;
+
+ mutex_lock(&cdns->mutex);
+ cdns->role = role;
+ mutex_unlock(&cdns->mutex);
+
+ if (!cdns->roles[role])
+ return -ENXIO;
+
+ if (cdns->roles[role]->state == CDNS3_ROLE_STATE_ACTIVE)
+ return 0;
+
+ mutex_lock(&cdns->mutex);
+ ret = cdns->roles[role]->start(cdns);
+ if (!ret)
+ cdns->roles[role]->state = CDNS3_ROLE_STATE_ACTIVE;
+ mutex_unlock(&cdns->mutex);
+
+ return ret;
+}
+
+static void cdns3_role_stop(struct cdns3 *cdns)
+{
+ enum usb_role role = cdns->role;
+
+ if (WARN_ON(role > USB_ROLE_DEVICE))
+ return;
+
+ if (cdns->roles[role]->state == CDNS3_ROLE_STATE_INACTIVE)
+ return;
+
+ mutex_lock(&cdns->mutex);
+ cdns->roles[role]->stop(cdns);
+ cdns->roles[role]->state = CDNS3_ROLE_STATE_INACTIVE;
+ mutex_unlock(&cdns->mutex);
+}
+
+static void cdns3_exit_roles(struct cdns3 *cdns)
+{
+ cdns3_role_stop(cdns);
+ cdns3_drd_exit(cdns);
+}
+
+static enum usb_role cdsn3_hw_role_state_machine(struct cdns3 *cdns);
+
+/**
+ * cdns3_core_init_role - initialize role of operation
+ * @cdns: Pointer to cdns3 structure
+ *
+ * Returns 0 on success otherwise negative errno
+ */
+static int cdns3_core_init_role(struct cdns3 *cdns)
+{
+ struct device *dev = cdns->dev;
+ enum usb_dr_mode best_dr_mode;
+ enum usb_dr_mode dr_mode;
+ int ret = 0;
+
+ dr_mode = usb_get_dr_mode(dev);
+ cdns->role = USB_ROLE_NONE;
+
+ /*
+ * If driver can't read mode by means of usb_get_dr_mode function then
+ * chooses mode according with Kernel configuration. This setting
+ * can be restricted later depending on strap pin configuration.
+ */
+ if (dr_mode == USB_DR_MODE_UNKNOWN) {
+ if (IS_ENABLED(CONFIG_USB_CDNS3_HOST) &&
+ IS_ENABLED(CONFIG_USB_CDNS3_GADGET))
+ dr_mode = USB_DR_MODE_OTG;
+ else if (IS_ENABLED(CONFIG_USB_CDNS3_HOST))
+ dr_mode = USB_DR_MODE_HOST;
+ else if (IS_ENABLED(CONFIG_USB_CDNS3_GADGET))
+ dr_mode = USB_DR_MODE_PERIPHERAL;
+ }
+
+ /*
+ * At this point cdns->dr_mode contains strap configuration.
+ * Driver try update this setting considering kernel configuration
+ */
+ best_dr_mode = cdns->dr_mode;
+
+ ret = cdns3_idle_init(cdns);
+ if (ret)
+ return ret;
+
+ if (dr_mode == USB_DR_MODE_OTG) {
+ best_dr_mode = cdns->dr_mode;
+ } else if (cdns->dr_mode == USB_DR_MODE_OTG) {
+ best_dr_mode = dr_mode;
+ } else if (cdns->dr_mode != dr_mode) {
+ dev_err(dev, "Incorrect DRD configuration\n");
+ return -EINVAL;
+ }
+
+ dr_mode = best_dr_mode;
+
+ if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_HOST) {
+ ret = cdns3_host_init(cdns);
+ if (ret) {
+ dev_err(dev, "Host initialization failed with %d\n",
+ ret);
+ goto err;
+ }
+ }
+
+ if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_PERIPHERAL) {
+ ret = cdns3_gadget_init(cdns);
+ if (ret) {
+ dev_err(dev, "Device initialization failed with %d\n",
+ ret);
+ goto err;
+ }
+ }
+
+ cdns->dr_mode = dr_mode;
+
+ ret = cdns3_drd_update_mode(cdns);
+ if (ret)
+ goto err;
+
+ if (cdns->dr_mode != USB_DR_MODE_OTG) {
+ ret = cdns3_hw_role_switch(cdns);
+ if (ret)
+ goto err;
+ }
+
+ return ret;
+err:
+ cdns3_exit_roles(cdns);
+ return ret;
+}
+
+/**
+ * cdsn3_hw_role_state_machine - role switch state machine based on hw events.
+ * @cdns: Pointer to controller structure.
+ *
+ * Returns next role to be entered based on hw events.
+ */
+static enum usb_role cdsn3_hw_role_state_machine(struct cdns3 *cdns)
+{
+ enum usb_role role;
+ int id, vbus;
+
+ if (cdns->dr_mode != USB_DR_MODE_OTG)
+ goto not_otg;
+
+ id = cdns3_get_id(cdns);
+ vbus = cdns3_get_vbus(cdns);
+
+ /*
+ * Role change state machine
+ * Inputs: ID, VBUS
+ * Previous state: cdns->role
+ * Next state: role
+ */
+ role = cdns->role;
+
+ switch (role) {
+ case USB_ROLE_NONE:
+ /*
+ * Driver treats USB_ROLE_NONE synonymous to IDLE state from
+ * controller specification.
+ */
+ if (!id)
+ role = USB_ROLE_HOST;
+ else if (vbus)
+ role = USB_ROLE_DEVICE;
+ break;
+ case USB_ROLE_HOST: /* from HOST, we can only change to NONE */
+ if (id)
+ role = USB_ROLE_NONE;
+ break;
+ case USB_ROLE_DEVICE: /* from GADGET, we can only change to NONE*/
+ if (!vbus)
+ role = USB_ROLE_NONE;
+ break;
+ }
+
+ dev_dbg(cdns->dev, "role %d -> %d\n", cdns->role, role);
+
+ return role;
+
+not_otg:
+ if (cdns3_is_host(cdns))
+ role = USB_ROLE_HOST;
+ if (cdns3_is_device(cdns))
+ role = USB_ROLE_DEVICE;
+
+ return role;
+}
+
+static int cdns3_idle_role_start(struct cdns3 *cdns)
+{
+ return 0;
+}
+
+static void cdns3_idle_role_stop(struct cdns3 *cdns)
+{
+ /* Program Lane swap and bring PHY out of RESET */
+ phy_reset(cdns->usb3_phy);
+}
+
+static int cdns3_idle_init(struct cdns3 *cdns)
+{
+ struct cdns3_role_driver *rdrv;
+
+ rdrv = devm_kzalloc(cdns->dev, sizeof(*rdrv), GFP_KERNEL);
+ if (!rdrv)
+ return -ENOMEM;
+
+ rdrv->start = cdns3_idle_role_start;
+ rdrv->stop = cdns3_idle_role_stop;
+ rdrv->state = CDNS3_ROLE_STATE_INACTIVE;
+ rdrv->suspend = NULL;
+ rdrv->resume = NULL;
+ rdrv->name = "idle";
+
+ cdns->roles[USB_ROLE_NONE] = rdrv;
+
+ return 0;
+}
+
+/**
+ * cdns3_hw_role_switch - switch roles based on HW state
+ * @cdns3: controller
+ */
+int cdns3_hw_role_switch(struct cdns3 *cdns)
+{
+ enum usb_role real_role, current_role;
+ int ret = 0;
+
+ /* Do nothing if role based on syfs. */
+ if (cdns->role_override)
+ return 0;
+
+ pm_runtime_get_sync(cdns->dev);
+
+ current_role = cdns->role;
+ real_role = cdsn3_hw_role_state_machine(cdns);
+
+ /* Do nothing if nothing changed */
+ if (current_role == real_role)
+ goto exit;
+
+ cdns3_role_stop(cdns);
+
+ dev_dbg(cdns->dev, "Switching role %d -> %d", current_role, real_role);
+
+ ret = cdns3_role_start(cdns, real_role);
+ if (ret) {
+ /* Back to current role */
+ dev_err(cdns->dev, "set %d has failed, back to %d\n",
+ real_role, current_role);
+ ret = cdns3_role_start(cdns, current_role);
+ if (ret)
+ dev_err(cdns->dev, "back to %d failed too\n",
+ current_role);
+ }
+exit:
+ pm_runtime_put_sync(cdns->dev);
+ return ret;
+}
+
+/**
+ * cdsn3_role_get - get current role of controller.
+ *
+ * @dev: Pointer to device structure
+ *
+ * Returns role
+ */
+static enum usb_role cdns3_role_get(struct device *dev)
+{
+ struct cdns3 *cdns = dev_get_drvdata(dev);
+
+ return cdns->role;
+}
+
+/**
+ * cdns3_role_set - set current role of controller.
+ *
+ * @dev: pointer to device object
+ * @role - the previous role
+ * Handles below events:
+ * - Role switch for dual-role devices
+ * - USB_ROLE_GADGET <--> USB_ROLE_NONE for peripheral-only devices
+ */
+static int cdns3_role_set(struct device *dev, enum usb_role role)
+{
+ struct cdns3 *cdns = dev_get_drvdata(dev);
+ int ret = 0;
+
+ pm_runtime_get_sync(cdns->dev);
+
+ /*
+ * FIXME: switch role framework should be extended to meet
+ * requirements. Driver assumes that role can be controlled
+ * by SW or HW. Temporary workaround is to use USB_ROLE_NONE to
+ * switch from SW to HW control.
+ *
+ * For dr_mode == USB_DR_MODE_OTG:
+ * if user sets USB_ROLE_HOST or USB_ROLE_DEVICE then driver
+ * sets role_override flag and forces that role.
+ * if user sets USB_ROLE_NONE, driver clears role_override and lets
+ * HW state machine take over.
+ *
+ * For dr_mode != USB_DR_MODE_OTG:
+ * Assumptions:
+ * 1. Restricted user control between NONE and dr_mode.
+ * 2. Driver doesn't need to rely on role_override flag.
+ * 3. Driver needs to ensure that HW state machine is never called
+ * if dr_mode != USB_DR_MODE_OTG.
+ */
+ if (role == USB_ROLE_NONE)
+ cdns->role_override = 0;
+ else
+ cdns->role_override = 1;
+
+ /*
+ * HW state might have changed so driver need to trigger
+ * HW state machine if dr_mode == USB_DR_MODE_OTG.
+ */
+ if (!cdns->role_override && cdns->dr_mode == USB_DR_MODE_OTG) {
+ cdns3_hw_role_switch(cdns);
+ goto pm_put;
+ }
+
+ if (cdns->role == role)
+ goto pm_put;
+
+ if (cdns->dr_mode == USB_DR_MODE_HOST) {
+ switch (role) {
+ case USB_ROLE_NONE:
+ case USB_ROLE_HOST:
+ break;
+ default:
+ ret = -EPERM;
+ goto pm_put;
+ }
+ }
+
+ if (cdns->dr_mode == USB_DR_MODE_PERIPHERAL) {
+ switch (role) {
+ case USB_ROLE_NONE:
+ case USB_ROLE_DEVICE:
+ break;
+ default:
+ ret = -EPERM;
+ goto pm_put;
+ }
+ }
+
+ cdns3_role_stop(cdns);
+ ret = cdns3_role_start(cdns, role);
+ if (ret) {
+ dev_err(cdns->dev, "set role %d has failed\n", role);
+ ret = -EPERM;
+ }
+
+pm_put:
+ pm_runtime_put_sync(cdns->dev);
+ return ret;
+}
+
+static const struct usb_role_switch_desc cdns3_switch_desc = {
+ .set = cdns3_role_set,
+ .get = cdns3_role_get,
+ .allow_userspace_control = true,
+};
+
+/**
+ * cdns3_probe - probe for cdns3 core device
+ * @pdev: Pointer to cdns3 core platform device
+ *
+ * Returns 0 on success otherwise negative errno
+ */
+static int cdns3_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ struct cdns3 *cdns;
+ void __iomem *regs;
+ int ret;
+
+ ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
+ if (ret) {
+ dev_err(dev, "error setting dma mask: %d\n", ret);
+ return -ENODEV;
+ }
+
+ cdns = devm_kzalloc(dev, sizeof(*cdns), GFP_KERNEL);
+ if (!cdns)
+ return -ENOMEM;
+
+ cdns->dev = dev;
+
+ platform_set_drvdata(pdev, cdns);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "host");
+ if (!res) {
+ dev_err(dev, "missing host IRQ\n");
+ return -ENODEV;
+ }
+
+ cdns->xhci_res[0] = *res;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "xhci");
+ if (!res) {
+ dev_err(dev, "couldn't get xhci resource\n");
+ return -ENXIO;
+ }
+
+ cdns->xhci_res[1] = *res;
+
+ cdns->dev_irq = platform_get_irq_byname(pdev, "peripheral");
+ if (cdns->dev_irq == -EPROBE_DEFER)
+ return cdns->dev_irq;
+
+ if (cdns->dev_irq < 0)
+ dev_err(dev, "couldn't get peripheral irq\n");
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dev");
+ regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+ cdns->dev_regs = regs;
+
+ cdns->otg_irq = platform_get_irq_byname(pdev, "otg");
+ if (cdns->otg_irq == -EPROBE_DEFER)
+ return cdns->otg_irq;
+
+ if (cdns->otg_irq < 0) {
+ dev_err(dev, "couldn't get otg irq\n");
+ return cdns->otg_irq;
+ }
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "otg");
+ if (!res) {
+ dev_err(dev, "couldn't get otg resource\n");
+ return -ENXIO;
+ }
+
+ cdns->otg_res = *res;
+
+ mutex_init(&cdns->mutex);
+
+ cdns->usb2_phy = devm_phy_optional_get(dev, "cdns3,usb2-phy");
+ if (IS_ERR(cdns->usb2_phy))
+ return PTR_ERR(cdns->usb2_phy);
+
+ ret = phy_init(cdns->usb2_phy);
+ if (ret)
+ return ret;
+
+ cdns->usb3_phy = devm_phy_optional_get(dev, "cdns3,usb3-phy");
+ if (IS_ERR(cdns->usb3_phy))
+ return PTR_ERR(cdns->usb3_phy);
+
+ ret = phy_init(cdns->usb3_phy);
+ if (ret)
+ goto err1;
+
+ ret = phy_power_on(cdns->usb2_phy);
+ if (ret)
+ goto err2;
+
+ ret = phy_power_on(cdns->usb3_phy);
+ if (ret)
+ goto err3;
+
+ cdns->role_sw = usb_role_switch_register(dev, &cdns3_switch_desc);
+ if (IS_ERR(cdns->role_sw)) {
+ ret = PTR_ERR(cdns->role_sw);
+ dev_warn(dev, "Unable to register Role Switch\n");
+ goto err4;
+ }
+
+ ret = cdns3_drd_init(cdns);
+ if (ret)
+ goto err5;
+
+ ret = cdns3_core_init_role(cdns);
+ if (ret)
+ goto err5;
+
+ device_set_wakeup_capable(dev, true);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
+ /*
+ * The controller needs less time between bus and controller suspend,
+ * and we also needs a small delay to avoid frequently entering low
+ * power mode.
+ */
+ pm_runtime_set_autosuspend_delay(dev, 20);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_use_autosuspend(dev);
+ dev_dbg(dev, "Cadence USB3 core: probe succeed\n");
+
+ return 0;
+err5:
+ cdns3_drd_exit(cdns);
+ usb_role_switch_unregister(cdns->role_sw);
+err4:
+ phy_power_off(cdns->usb3_phy);
+
+err3:
+ phy_power_off(cdns->usb2_phy);
+err2:
+ phy_exit(cdns->usb3_phy);
+err1:
+ phy_exit(cdns->usb2_phy);
+
+ return ret;
+}
+
+/**
+ * cdns3_remove - unbind drd driver and clean up
+ * @pdev: Pointer to Linux platform device
+ *
+ * Returns 0 on success otherwise negative errno
+ */
+static int cdns3_remove(struct platform_device *pdev)
+{
+ struct cdns3 *cdns = platform_get_drvdata(pdev);
+
+ pm_runtime_get_sync(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_put_noidle(&pdev->dev);
+ cdns3_exit_roles(cdns);
+ usb_role_switch_unregister(cdns->role_sw);
+ phy_power_off(cdns->usb2_phy);
+ phy_power_off(cdns->usb3_phy);
+ phy_exit(cdns->usb2_phy);
+ phy_exit(cdns->usb3_phy);
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+
+static int cdns3_suspend(struct device *dev)
+{
+ struct cdns3 *cdns = dev_get_drvdata(dev);
+ unsigned long flags;
+
+ if (cdns->role == USB_ROLE_HOST)
+ return 0;
+
+ if (pm_runtime_status_suspended(dev))
+ pm_runtime_resume(dev);
+
+ if (cdns->roles[cdns->role]->suspend) {
+ spin_lock_irqsave(&cdns->gadget_dev->lock, flags);
+ cdns->roles[cdns->role]->suspend(cdns, false);
+ spin_unlock_irqrestore(&cdns->gadget_dev->lock, flags);
+ }
+
+ return 0;
+}
+
+static int cdns3_resume(struct device *dev)
+{
+ struct cdns3 *cdns = dev_get_drvdata(dev);
+ unsigned long flags;
+
+ if (cdns->role == USB_ROLE_HOST)
+ return 0;
+
+ if (cdns->roles[cdns->role]->resume) {
+ spin_lock_irqsave(&cdns->gadget_dev->lock, flags);
+ cdns->roles[cdns->role]->resume(cdns, false);
+ spin_unlock_irqrestore(&cdns->gadget_dev->lock, flags);
+ }
+
+ pm_runtime_disable(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops cdns3_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(cdns3_suspend, cdns3_resume)
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id of_cdns3_match[] = {
+ { .compatible = "cdns,usb3" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, of_cdns3_match);
+#endif
+
+static struct platform_driver cdns3_driver = {
+ .probe = cdns3_probe,
+ .remove = cdns3_remove,
+ .driver = {
+ .name = "cdns-usb3",
+ .of_match_table = of_match_ptr(of_cdns3_match),
+ .pm = &cdns3_pm_ops,
+ },
+};
+
+module_platform_driver(cdns3_driver);
+
+MODULE_ALIAS("platform:cdns3");
+MODULE_AUTHOR("Pawel Laszczak <pawell@cadence.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Cadence USB3 DRD Controller Driver");
diff --git a/drivers/usb/cdns3/core.h b/drivers/usb/cdns3/core.h
new file mode 100644
index 000000000000..969eb94de204
--- /dev/null
+++ b/drivers/usb/cdns3/core.h
@@ -0,0 +1,98 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Cadence USBSS DRD Header File.
+ *
+ * Copyright (C) 2017-2018 NXP
+ * Copyright (C) 2018-2019 Cadence.
+ *
+ * Authors: Peter Chen <peter.chen@nxp.com>
+ * Pawel Laszczak <pawell@cadence.com>
+ */
+#include <linux/usb/otg.h>
+#include <linux/usb/role.h>
+
+#ifndef __LINUX_CDNS3_CORE_H
+#define __LINUX_CDNS3_CORE_H
+
+struct cdns3;
+
+/**
+ * struct cdns3_role_driver - host/gadget role driver
+ * @start: start this role
+ * @stop: stop this role
+ * @suspend: suspend callback for this role
+ * @resume: resume callback for this role
+ * @irq: irq handler for this role
+ * @name: role name string (host/gadget)
+ * @state: current state
+ */
+struct cdns3_role_driver {
+ int (*start)(struct cdns3 *cdns);
+ void (*stop)(struct cdns3 *cdns);
+ int (*suspend)(struct cdns3 *cdns, bool do_wakeup);
+ int (*resume)(struct cdns3 *cdns, bool hibernated);
+ const char *name;
+#define CDNS3_ROLE_STATE_INACTIVE 0
+#define CDNS3_ROLE_STATE_ACTIVE 1
+ int state;
+};
+
+#define CDNS3_XHCI_RESOURCES_NUM 2
+/**
+ * struct cdns3 - Representation of Cadence USB3 DRD controller.
+ * @dev: pointer to Cadence device struct
+ * @xhci_regs: pointer to base of xhci registers
+ * @xhci_res: the resource for xhci
+ * @dev_regs: pointer to base of dev registers
+ * @otg_res: the resource for otg
+ * @otg_v0_regs: pointer to base of v0 otg registers
+ * @otg_v1_regs: pointer to base of v1 otg registers
+ * @otg_regs: pointer to base of otg registers
+ * @otg_irq: irq number for otg controller
+ * @dev_irq: irq number for device controller
+ * @roles: array of supported roles for this controller
+ * @role: current role
+ * @host_dev: the child host device pointer for cdns3 core
+ * @gadget_dev: the child gadget device pointer for cdns3 core
+ * @usb2_phy: pointer to USB2 PHY
+ * @usb3_phy: pointer to USB3 PHY
+ * @mutex: the mutex for concurrent code at driver
+ * @dr_mode: supported mode of operation it can be only Host, only Device
+ * or OTG mode that allow to switch between Device and Host mode.
+ * This field based on firmware setting, kernel configuration
+ * and hardware configuration.
+ * @role_sw: pointer to role switch object.
+ * @role_override: set 1 if role rely on SW.
+ */
+struct cdns3 {
+ struct device *dev;
+ void __iomem *xhci_regs;
+ struct resource xhci_res[CDNS3_XHCI_RESOURCES_NUM];
+ struct cdns3_usb_regs __iomem *dev_regs;
+
+ struct resource otg_res;
+ struct cdns3_otg_legacy_regs *otg_v0_regs;
+ struct cdns3_otg_regs *otg_v1_regs;
+ struct cdns3_otg_common_regs *otg_regs;
+#define CDNS3_CONTROLLER_V0 0
+#define CDNS3_CONTROLLER_V1 1
+ u32 version;
+
+ int otg_irq;
+ int dev_irq;
+ struct cdns3_role_driver *roles[USB_ROLE_DEVICE + 1];
+ enum usb_role role;
+ struct platform_device *host_dev;
+ struct cdns3_device *gadget_dev;
+ struct phy *usb2_phy;
+ struct phy *usb3_phy;
+ /* mutext used in workqueue*/
+ struct mutex mutex;
+ enum usb_dr_mode dr_mode;
+ struct usb_role_switch *role_sw;
+ int role_override;
+};
+
+int cdns3_hw_role_switch(struct cdns3 *cdns);
+
+#endif /* __LINUX_CDNS3_CORE_H */
diff --git a/drivers/usb/cdns3/debug.h b/drivers/usb/cdns3/debug.h
new file mode 100644
index 000000000000..2c9afbfe988b
--- /dev/null
+++ b/drivers/usb/cdns3/debug.h
@@ -0,0 +1,161 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Cadence USBSS DRD Driver.
+ * Debug header file.
+ *
+ * Copyright (C) 2018-2019 Cadence.
+ *
+ * Author: Pawel Laszczak <pawell@cadence.com>
+ */
+#ifndef __LINUX_CDNS3_DEBUG
+#define __LINUX_CDNS3_DEBUG
+
+#include "core.h"
+
+static inline char *cdns3_decode_usb_irq(char *str,
+ enum usb_device_speed speed,
+ u32 usb_ists)
+{
+ int ret;
+
+ ret = sprintf(str, "IRQ %08x = ", usb_ists);
+
+ if (usb_ists & (USB_ISTS_CON2I | USB_ISTS_CONI)) {
+ ret += sprintf(str + ret, "Connection %s\n",
+ usb_speed_string(speed));
+ }
+ if (usb_ists & USB_ISTS_DIS2I || usb_ists & USB_ISTS_DISI)
+ ret += sprintf(str + ret, "Disconnection ");
+ if (usb_ists & USB_ISTS_L2ENTI)
+ ret += sprintf(str + ret, "suspended ");
+ if (usb_ists & USB_ISTS_L1ENTI)
+ ret += sprintf(str + ret, "L1 enter ");
+ if (usb_ists & USB_ISTS_L1EXTI)
+ ret += sprintf(str + ret, "L1 exit ");
+ if (usb_ists & USB_ISTS_L2ENTI)
+ ret += sprintf(str + ret, "L2 enter ");
+ if (usb_ists & USB_ISTS_L2EXTI)
+ ret += sprintf(str + ret, "L2 exit ");
+ if (usb_ists & USB_ISTS_U3EXTI)
+ ret += sprintf(str + ret, "U3 exit ");
+ if (usb_ists & USB_ISTS_UWRESI)
+ ret += sprintf(str + ret, "Warm Reset ");
+ if (usb_ists & USB_ISTS_UHRESI)
+ ret += sprintf(str + ret, "Hot Reset ");
+ if (usb_ists & USB_ISTS_U2RESI)
+ ret += sprintf(str + ret, "Reset");
+
+ return str;
+}
+
+static inline char *cdns3_decode_ep_irq(char *str,
+ u32 ep_sts,
+ const char *ep_name)
+{
+ int ret;
+
+ ret = sprintf(str, "IRQ for %s: %08x ", ep_name, ep_sts);
+
+ if (ep_sts & EP_STS_SETUP)
+ ret += sprintf(str + ret, "SETUP ");
+ if (ep_sts & EP_STS_IOC)
+ ret += sprintf(str + ret, "IOC ");
+ if (ep_sts & EP_STS_ISP)
+ ret += sprintf(str + ret, "ISP ");
+ if (ep_sts & EP_STS_DESCMIS)
+ ret += sprintf(str + ret, "DESCMIS ");
+ if (ep_sts & EP_STS_STREAMR)
+ ret += sprintf(str + ret, "STREAMR ");
+ if (ep_sts & EP_STS_MD_EXIT)
+ ret += sprintf(str + ret, "MD_EXIT ");
+ if (ep_sts & EP_STS_TRBERR)
+ ret += sprintf(str + ret, "TRBERR ");
+ if (ep_sts & EP_STS_NRDY)
+ ret += sprintf(str + ret, "NRDY ");
+ if (ep_sts & EP_STS_PRIME)
+ ret += sprintf(str + ret, "PRIME ");
+ if (ep_sts & EP_STS_SIDERR)
+ ret += sprintf(str + ret, "SIDERRT ");
+ if (ep_sts & EP_STS_OUTSMM)
+ ret += sprintf(str + ret, "OUTSMM ");
+ if (ep_sts & EP_STS_ISOERR)
+ ret += sprintf(str + ret, "ISOERR ");
+ if (ep_sts & EP_STS_IOT)
+ ret += sprintf(str + ret, "IOT ");
+
+ return str;
+}
+
+static inline char *cdns3_decode_epx_irq(char *str,
+ char *ep_name,
+ u32 ep_sts)
+{
+ return cdns3_decode_ep_irq(str, ep_sts, ep_name);
+}
+
+static inline char *cdns3_decode_ep0_irq(char *str,
+ int dir,
+ u32 ep_sts)
+{
+ return cdns3_decode_ep_irq(str, ep_sts,
+ dir ? "ep0IN" : "ep0OUT");
+}
+
+/**
+ * Debug a transfer ring.
+ *
+ * Prints out all TRBs in the endpoint ring, even those after the Link TRB.
+ *.
+ */
+static inline char *cdns3_dbg_ring(struct cdns3_endpoint *priv_ep,
+ struct cdns3_trb *ring, char *str)
+{
+ dma_addr_t addr = priv_ep->trb_pool_dma;
+ struct cdns3_trb *trb;
+ int trb_per_sector;
+ int ret = 0;
+ int i;
+
+ trb_per_sector = GET_TRBS_PER_SEGMENT(priv_ep->type);
+
+ trb = &priv_ep->trb_pool[priv_ep->dequeue];
+ ret += sprintf(str + ret, "\n\t\tRing contents for %s:", priv_ep->name);
+
+ ret += sprintf(str + ret,
+ "\n\t\tRing deq index: %d, trb: %p (virt), 0x%llx (dma)\n",
+ priv_ep->dequeue, trb,
+ (unsigned long long)cdns3_trb_virt_to_dma(priv_ep, trb));
+
+ trb = &priv_ep->trb_pool[priv_ep->enqueue];
+ ret += sprintf(str + ret,
+ "\t\tRing enq index: %d, trb: %p (virt), 0x%llx (dma)\n",
+ priv_ep->enqueue, trb,
+ (unsigned long long)cdns3_trb_virt_to_dma(priv_ep, trb));
+
+ ret += sprintf(str + ret,
+ "\t\tfree trbs: %d, CCS=%d, PCS=%d\n",
+ priv_ep->free_trbs, priv_ep->ccs, priv_ep->pcs);
+
+ if (trb_per_sector > TRBS_PER_SEGMENT)
+ trb_per_sector = TRBS_PER_SEGMENT;
+
+ if (trb_per_sector > TRBS_PER_SEGMENT) {
+ sprintf(str + ret, "\t\tTo big transfer ring %d\n",
+ trb_per_sector);
+ return str;
+ }
+
+ for (i = 0; i < trb_per_sector; ++i) {
+ trb = &ring[i];
+ ret += sprintf(str + ret,
+ "\t\t@%pad %08x %08x %08x\n", &addr,
+ le32_to_cpu(trb->buffer),
+ le32_to_cpu(trb->length),
+ le32_to_cpu(trb->control));
+ addr += sizeof(*trb);
+ }
+
+ return str;
+}
+
+#endif /*__LINUX_CDNS3_DEBUG*/
diff --git a/drivers/usb/cdns3/drd.c b/drivers/usb/cdns3/drd.c
new file mode 100644
index 000000000000..16ad485f0b69
--- /dev/null
+++ b/drivers/usb/cdns3/drd.c
@@ -0,0 +1,381 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Cadence USBSS DRD Driver.
+ *
+ * Copyright (C) 2018-2019 Cadence.
+ * Copyright (C) 2019 Texas Instruments
+ *
+ * Author: Pawel Laszczak <pawell@cadence.com>
+ * Roger Quadros <rogerq@ti.com>
+ *
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/iopoll.h>
+#include <linux/usb/otg.h>
+
+#include "gadget.h"
+#include "drd.h"
+#include "core.h"
+
+/**
+ * cdns3_set_mode - change mode of OTG Core
+ * @cdns: pointer to context structure
+ * @mode: selected mode from cdns_role
+ *
+ * Returns 0 on success otherwise negative errno
+ */
+int cdns3_set_mode(struct cdns3 *cdns, enum usb_dr_mode mode)
+{
+ int ret = 0;
+ u32 reg;
+
+ switch (mode) {
+ case USB_DR_MODE_PERIPHERAL:
+ break;
+ case USB_DR_MODE_HOST:
+ break;
+ case USB_DR_MODE_OTG:
+ dev_dbg(cdns->dev, "Set controller to OTG mode\n");
+ if (cdns->version == CDNS3_CONTROLLER_V1) {
+ reg = readl(&cdns->otg_v1_regs->override);
+ reg |= OVERRIDE_IDPULLUP;
+ writel(reg, &cdns->otg_v1_regs->override);
+ } else {
+ reg = readl(&cdns->otg_v0_regs->ctrl1);
+ reg |= OVERRIDE_IDPULLUP_V0;
+ writel(reg, &cdns->otg_v0_regs->ctrl1);
+ }
+
+ /*
+ * Hardware specification says: "ID_VALUE must be valid within
+ * 50ms after idpullup is set to '1" so driver must wait
+ * 50ms before reading this pin.
+ */
+ usleep_range(50000, 60000);
+ break;
+ default:
+ dev_err(cdns->dev, "Unsupported mode of operation %d\n", mode);
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+int cdns3_get_id(struct cdns3 *cdns)
+{
+ int id;
+
+ id = readl(&cdns->otg_regs->sts) & OTGSTS_ID_VALUE;
+ dev_dbg(cdns->dev, "OTG ID: %d", id);
+
+ return id;
+}
+
+int cdns3_get_vbus(struct cdns3 *cdns)
+{
+ int vbus;
+
+ vbus = !!(readl(&cdns->otg_regs->sts) & OTGSTS_VBUS_VALID);
+ dev_dbg(cdns->dev, "OTG VBUS: %d", vbus);
+
+ return vbus;
+}
+
+int cdns3_is_host(struct cdns3 *cdns)
+{
+ if (cdns->dr_mode == USB_DR_MODE_HOST)
+ return 1;
+ else if (!cdns3_get_id(cdns))
+ return 1;
+
+ return 0;
+}
+
+int cdns3_is_device(struct cdns3 *cdns)
+{
+ if (cdns->dr_mode == USB_DR_MODE_PERIPHERAL)
+ return 1;
+ else if (cdns->dr_mode == USB_DR_MODE_OTG)
+ if (cdns3_get_id(cdns))
+ return 1;
+
+ return 0;
+}
+
+/**
+ * cdns3_otg_disable_irq - Disable all OTG interrupts
+ * @cdns: Pointer to controller context structure
+ */
+static void cdns3_otg_disable_irq(struct cdns3 *cdns)
+{
+ writel(0, &cdns->otg_regs->ien);
+}
+
+/**
+ * cdns3_otg_enable_irq - enable id and sess_valid interrupts
+ * @cdns: Pointer to controller context structure
+ */
+static void cdns3_otg_enable_irq(struct cdns3 *cdns)
+{
+ writel(OTGIEN_ID_CHANGE_INT | OTGIEN_VBUSVALID_RISE_INT |
+ OTGIEN_VBUSVALID_FALL_INT, &cdns->otg_regs->ien);
+}
+
+/**
+ * cdns3_drd_switch_host - start/stop host
+ * @cdns: Pointer to controller context structure
+ * @on: 1 for start, 0 for stop
+ *
+ * Returns 0 on success otherwise negative errno
+ */
+int cdns3_drd_switch_host(struct cdns3 *cdns, int on)
+{
+ int ret, val;
+ u32 reg = OTGCMD_OTG_DIS;
+
+ /* switch OTG core */
+ if (on) {
+ writel(OTGCMD_HOST_BUS_REQ | reg, &cdns->otg_regs->cmd);
+
+ dev_dbg(cdns->dev, "Waiting till Host mode is turned on\n");
+ ret = readl_poll_timeout_atomic(&cdns->otg_regs->sts, val,
+ val & OTGSTS_XHCI_READY,
+ 1, 100000);
+ if (ret) {
+ dev_err(cdns->dev, "timeout waiting for xhci_ready\n");
+ return ret;
+ }
+ } else {
+ writel(OTGCMD_HOST_BUS_DROP | OTGCMD_DEV_BUS_DROP |
+ OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF,
+ &cdns->otg_regs->cmd);
+ /* Waiting till H_IDLE state.*/
+ readl_poll_timeout_atomic(&cdns->otg_regs->state, val,
+ !(val & OTGSTATE_HOST_STATE_MASK),
+ 1, 2000000);
+ }
+
+ return 0;
+}
+
+/**
+ * cdns3_drd_switch_gadget - start/stop gadget
+ * @cdns: Pointer to controller context structure
+ * @on: 1 for start, 0 for stop
+ *
+ * Returns 0 on success otherwise negative errno
+ */
+int cdns3_drd_switch_gadget(struct cdns3 *cdns, int on)
+{
+ int ret, val;
+ u32 reg = OTGCMD_OTG_DIS;
+
+ /* switch OTG core */
+ if (on) {
+ writel(OTGCMD_DEV_BUS_REQ | reg, &cdns->otg_regs->cmd);
+
+ dev_dbg(cdns->dev, "Waiting till Device mode is turned on\n");
+
+ ret = readl_poll_timeout_atomic(&cdns->otg_regs->sts, val,
+ val & OTGSTS_DEV_READY,
+ 1, 100000);
+ if (ret) {
+ dev_err(cdns->dev, "timeout waiting for dev_ready\n");
+ return ret;
+ }
+ } else {
+ /*
+ * driver should wait at least 10us after disabling Device
+ * before turning-off Device (DEV_BUS_DROP)
+ */
+ usleep_range(20, 30);
+ writel(OTGCMD_HOST_BUS_DROP | OTGCMD_DEV_BUS_DROP |
+ OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF,
+ &cdns->otg_regs->cmd);
+ /* Waiting till DEV_IDLE state.*/
+ readl_poll_timeout_atomic(&cdns->otg_regs->state, val,
+ !(val & OTGSTATE_DEV_STATE_MASK),
+ 1, 2000000);
+ }
+
+ return 0;
+}
+
+/**
+ * cdns3_init_otg_mode - initialize drd controller
+ * @cdns: Pointer to controller context structure
+ *
+ * Returns 0 on success otherwise negative errno
+ */
+static int cdns3_init_otg_mode(struct cdns3 *cdns)
+{
+ int ret = 0;
+
+ cdns3_otg_disable_irq(cdns);
+ /* clear all interrupts */
+ writel(~0, &cdns->otg_regs->ivect);
+
+ ret = cdns3_set_mode(cdns, USB_DR_MODE_OTG);
+ if (ret)
+ return ret;
+
+ cdns3_otg_enable_irq(cdns);
+ return ret;
+}
+
+/**
+ * cdns3_drd_update_mode - initialize mode of operation
+ * @cdns: Pointer to controller context structure
+ *
+ * Returns 0 on success otherwise negative errno
+ */
+int cdns3_drd_update_mode(struct cdns3 *cdns)
+{
+ int ret = 0;
+
+ switch (cdns->dr_mode) {
+ case USB_DR_MODE_PERIPHERAL:
+ ret = cdns3_set_mode(cdns, USB_DR_MODE_PERIPHERAL);
+ break;
+ case USB_DR_MODE_HOST:
+ ret = cdns3_set_mode(cdns, USB_DR_MODE_HOST);
+ break;
+ case USB_DR_MODE_OTG:
+ ret = cdns3_init_otg_mode(cdns);
+ break;
+ default:
+ dev_err(cdns->dev, "Unsupported mode of operation %d\n",
+ cdns->dr_mode);
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static irqreturn_t cdns3_drd_thread_irq(int irq, void *data)
+{
+ struct cdns3 *cdns = data;
+
+ cdns3_hw_role_switch(cdns);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * cdns3_drd_irq - interrupt handler for OTG events
+ *
+ * @irq: irq number for cdns3 core device
+ * @data: structure of cdns3
+ *
+ * Returns IRQ_HANDLED or IRQ_NONE
+ */
+static irqreturn_t cdns3_drd_irq(int irq, void *data)
+{
+ irqreturn_t ret = IRQ_NONE;
+ struct cdns3 *cdns = data;
+ u32 reg;
+
+ if (cdns->dr_mode != USB_DR_MODE_OTG)
+ return ret;
+
+ reg = readl(&cdns->otg_regs->ivect);
+
+ if (!reg)
+ return ret;
+
+ if (reg & OTGIEN_ID_CHANGE_INT) {
+ dev_dbg(cdns->dev, "OTG IRQ: new ID: %d\n",
+ cdns3_get_id(cdns));
+
+ ret = IRQ_WAKE_THREAD;
+ }
+
+ if (reg & (OTGIEN_VBUSVALID_RISE_INT | OTGIEN_VBUSVALID_FALL_INT)) {
+ dev_dbg(cdns->dev, "OTG IRQ: new VBUS: %d\n",
+ cdns3_get_vbus(cdns));
+
+ ret = IRQ_WAKE_THREAD;
+ }
+
+ writel(~0, &cdns->otg_regs->ivect);
+ return ret;
+}
+
+int cdns3_drd_init(struct cdns3 *cdns)
+{
+ void __iomem *regs;
+ int ret = 0;
+ u32 state;
+
+ regs = devm_ioremap_resource(cdns->dev, &cdns->otg_res);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ /* Detection of DRD version. Controller has been released
+ * in two versions. Both are similar, but they have same changes
+ * in register maps.
+ * The first register in old version is command register and it's read
+ * only, so driver should read 0 from it. On the other hand, in v1
+ * the first register contains device ID number which is not set to 0.
+ * Driver uses this fact to detect the proper version of
+ * controller.
+ */
+ cdns->otg_v0_regs = regs;
+ if (!readl(&cdns->otg_v0_regs->cmd)) {
+ cdns->version = CDNS3_CONTROLLER_V0;
+ cdns->otg_v1_regs = NULL;
+ cdns->otg_regs = regs;
+ writel(1, &cdns->otg_v0_regs->simulate);
+ dev_info(cdns->dev, "DRD version v0 (%08x)\n",
+ readl(&cdns->otg_v0_regs->version));
+ } else {
+ cdns->otg_v0_regs = NULL;
+ cdns->otg_v1_regs = regs;
+ cdns->otg_regs = (void *)&cdns->otg_v1_regs->cmd;
+ cdns->version = CDNS3_CONTROLLER_V1;
+ writel(1, &cdns->otg_v1_regs->simulate);
+ dev_info(cdns->dev, "DRD version v1 (ID: %08x, rev: %08x)\n",
+ readl(&cdns->otg_v1_regs->did),
+ readl(&cdns->otg_v1_regs->rid));
+ }
+
+ state = OTGSTS_STRAP(readl(&cdns->otg_regs->sts));
+
+ /* Update dr_mode according to STRAP configuration. */
+ cdns->dr_mode = USB_DR_MODE_OTG;
+ if (state == OTGSTS_STRAP_HOST) {
+ dev_dbg(cdns->dev, "Controller strapped to HOST\n");
+ cdns->dr_mode = USB_DR_MODE_HOST;
+ } else if (state == OTGSTS_STRAP_GADGET) {
+ dev_dbg(cdns->dev, "Controller strapped to PERIPHERAL\n");
+ cdns->dr_mode = USB_DR_MODE_PERIPHERAL;
+ }
+
+ ret = devm_request_threaded_irq(cdns->dev, cdns->otg_irq,
+ cdns3_drd_irq,
+ cdns3_drd_thread_irq,
+ IRQF_SHARED,
+ dev_name(cdns->dev), cdns);
+
+ if (ret) {
+ dev_err(cdns->dev, "couldn't get otg_irq\n");
+ return ret;
+ }
+
+ state = readl(&cdns->otg_regs->sts);
+ if (OTGSTS_OTG_NRDY(state) != 0) {
+ dev_err(cdns->dev, "Cadence USB3 OTG device not ready\n");
+ return -ENODEV;
+ }
+
+ return ret;
+}
+
+int cdns3_drd_exit(struct cdns3 *cdns)
+{
+ cdns3_otg_disable_irq(cdns);
+ return 0;
+}
diff --git a/drivers/usb/cdns3/drd.h b/drivers/usb/cdns3/drd.h
new file mode 100644
index 000000000000..04e01c4d2377
--- /dev/null
+++ b/drivers/usb/cdns3/drd.h
@@ -0,0 +1,167 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Cadence USB3 DRD header file.
+ *
+ * Copyright (C) 2018-2019 Cadence.
+ *
+ * Author: Pawel Laszczak <pawell@cadence.com>
+ */
+#ifndef __LINUX_CDNS3_DRD
+#define __LINUX_CDNS3_DRD
+
+#include <linux/usb/otg.h>
+#include <linux/phy/phy.h>
+#include "core.h"
+
+/* DRD register interface for version v1. */
+struct cdns3_otg_regs {
+ __le32 did;
+ __le32 rid;
+ __le32 capabilities;
+ __le32 reserved1;
+ __le32 cmd;
+ __le32 sts;
+ __le32 state;
+ __le32 reserved2;
+ __le32 ien;
+ __le32 ivect;
+ __le32 refclk;
+ __le32 tmr;
+ __le32 reserved3[4];
+ __le32 simulate;
+ __le32 override;
+ __le32 susp_ctrl;
+ __le32 reserved4;
+ __le32 anasts;
+ __le32 adp_ramp_time;
+ __le32 ctrl1;
+ __le32 ctrl2;
+};
+
+/* DRD register interface for version v0. */
+struct cdns3_otg_legacy_regs {
+ __le32 cmd;
+ __le32 sts;
+ __le32 state;
+ __le32 refclk;
+ __le32 ien;
+ __le32 ivect;
+ __le32 reserved1[3];
+ __le32 tmr;
+ __le32 reserved2[2];
+ __le32 version;
+ __le32 capabilities;
+ __le32 reserved3[2];
+ __le32 simulate;
+ __le32 reserved4[5];
+ __le32 ctrl1;
+};
+
+/*
+ * Common registers interface for both version of DRD.
+ */
+struct cdns3_otg_common_regs {
+ __le32 cmd;
+ __le32 sts;
+ __le32 state;
+ __le32 different1;
+ __le32 ien;
+ __le32 ivect;
+};
+
+/* CDNS_RID - bitmasks */
+#define CDNS_RID(p) ((p) & GENMASK(15, 0))
+
+/* CDNS_VID - bitmasks */
+#define CDNS_DID(p) ((p) & GENMASK(31, 0))
+
+/* OTGCMD - bitmasks */
+/* "Request the bus for Device mode. */
+#define OTGCMD_DEV_BUS_REQ BIT(0)
+/* Request the bus for Host mode */
+#define OTGCMD_HOST_BUS_REQ BIT(1)
+/* Enable OTG mode. */
+#define OTGCMD_OTG_EN BIT(2)
+/* Disable OTG mode */
+#define OTGCMD_OTG_DIS BIT(3)
+/*"Configure OTG as A-Device. */
+#define OTGCMD_A_DEV_EN BIT(4)
+/*"Configure OTG as A-Device. */
+#define OTGCMD_A_DEV_DIS BIT(5)
+/* Drop the bus for Device mod e. */
+#define OTGCMD_DEV_BUS_DROP BIT(8)
+/* Drop the bus for Host mode*/
+#define OTGCMD_HOST_BUS_DROP BIT(9)
+/* Power Down USBSS-DEV. */
+#define OTGCMD_DEV_POWER_OFF BIT(11)
+/* Power Down CDNSXHCI. */
+#define OTGCMD_HOST_POWER_OFF BIT(12)
+
+/* OTGIEN - bitmasks */
+/* ID change interrupt enable */
+#define OTGIEN_ID_CHANGE_INT BIT(0)
+/* Vbusvalid fall detected interrupt enable.*/
+#define OTGIEN_VBUSVALID_RISE_INT BIT(4)
+/* Vbusvalid fall detected interrupt enable */
+#define OTGIEN_VBUSVALID_FALL_INT BIT(5)
+
+/* OTGSTS - bitmasks */
+/*
+ * Current value of the ID pin. It is only valid when idpullup in
+ * OTGCTRL1_TYPE register is set to '1'.
+ */
+#define OTGSTS_ID_VALUE BIT(0)
+/* Current value of the vbus_valid */
+#define OTGSTS_VBUS_VALID BIT(1)
+/* Current value of the b_sess_vld */
+#define OTGSTS_SESSION_VALID BIT(2)
+/*Device mode is active*/
+#define OTGSTS_DEV_ACTIVE BIT(3)
+/* Host mode is active. */
+#define OTGSTS_HOST_ACTIVE BIT(4)
+/* OTG Controller not ready. */
+#define OTGSTS_OTG_NRDY_MASK BIT(11)
+#define OTGSTS_OTG_NRDY(p) ((p) & OTGSTS_OTG_NRDY_MASK)
+/*
+ * Value of the strap pins.
+ * 000 - no default configuration
+ * 010 - Controller initiall configured as Host
+ * 100 - Controller initially configured as Device
+ */
+#define OTGSTS_STRAP(p) (((p) & GENMASK(14, 12)) >> 12)
+#define OTGSTS_STRAP_NO_DEFAULT_CFG 0x00
+#define OTGSTS_STRAP_HOST_OTG 0x01
+#define OTGSTS_STRAP_HOST 0x02
+#define OTGSTS_STRAP_GADGET 0x04
+/* Host mode is turned on. */
+#define OTGSTS_XHCI_READY BIT(26)
+/* "Device mode is turned on .*/
+#define OTGSTS_DEV_READY BIT(27)
+
+/* OTGSTATE- bitmasks */
+#define OTGSTATE_DEV_STATE_MASK GENMASK(2, 0)
+#define OTGSTATE_HOST_STATE_MASK GENMASK(5, 3)
+#define OTGSTATE_HOST_STATE_IDLE 0x0
+#define OTGSTATE_HOST_STATE_VBUS_FALL 0x7
+#define OTGSTATE_HOST_STATE(p) (((p) & OTGSTATE_HOST_STATE_MASK) >> 3)
+
+/* OTGREFCLK - bitmasks */
+#define OTGREFCLK_STB_CLK_SWITCH_EN BIT(31)
+
+/* OVERRIDE - bitmasks */
+#define OVERRIDE_IDPULLUP BIT(0)
+/* Only for CDNS3_CONTROLLER_V0 version */
+#define OVERRIDE_IDPULLUP_V0 BIT(24)
+
+int cdns3_is_host(struct cdns3 *cdns);
+int cdns3_is_device(struct cdns3 *cdns);
+int cdns3_get_id(struct cdns3 *cdns);
+int cdns3_get_vbus(struct cdns3 *cdns);
+int cdns3_drd_init(struct cdns3 *cdns);
+int cdns3_drd_exit(struct cdns3 *cdns);
+int cdns3_drd_update_mode(struct cdns3 *cdns);
+int cdns3_drd_switch_gadget(struct cdns3 *cdns, int on);
+int cdns3_drd_switch_host(struct cdns3 *cdns, int on);
+int cdns3_set_mode(struct cdns3 *cdns, enum usb_dr_mode mode);
+
+#endif /* __LINUX_CDNS3_DRD */
diff --git a/drivers/usb/cdns3/ep0.c b/drivers/usb/cdns3/ep0.c
new file mode 100644
index 000000000000..44f652e8b5a2
--- /dev/null
+++ b/drivers/usb/cdns3/ep0.c
@@ -0,0 +1,886 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Cadence USBSS DRD Driver - gadget side.
+ *
+ * Copyright (C) 2018 Cadence Design Systems.
+ * Copyright (C) 2017-2018 NXP
+ *
+ * Authors: Pawel Jez <pjez@cadence.com>,
+ * Pawel Laszczak <pawell@cadence.com>
+ * Peter Chen <peter.chen@nxp.com>
+ */
+
+#include <linux/usb/composite.h>
+#include <linux/iopoll.h>
+
+#include "gadget.h"
+#include "trace.h"
+
+static struct usb_endpoint_descriptor cdns3_gadget_ep0_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bmAttributes = USB_ENDPOINT_XFER_CONTROL,
+};
+
+/**
+ * cdns3_ep0_run_transfer - Do transfer on default endpoint hardware
+ * @priv_dev: extended gadget object
+ * @dma_addr: physical address where data is/will be stored
+ * @length: data length
+ * @erdy: set it to 1 when ERDY packet should be sent -
+ * exit from flow control state
+ */
+static void cdns3_ep0_run_transfer(struct cdns3_device *priv_dev,
+ dma_addr_t dma_addr,
+ unsigned int length, int erdy, int zlp)
+{
+ struct cdns3_usb_regs __iomem *regs = priv_dev->regs;
+ struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
+
+ priv_ep->trb_pool[0].buffer = TRB_BUFFER(dma_addr);
+ priv_ep->trb_pool[0].length = TRB_LEN(length);
+
+ if (zlp) {
+ priv_ep->trb_pool[0].control = TRB_CYCLE | TRB_TYPE(TRB_NORMAL);
+ priv_ep->trb_pool[1].buffer = TRB_BUFFER(dma_addr);
+ priv_ep->trb_pool[1].length = TRB_LEN(0);
+ priv_ep->trb_pool[1].control = TRB_CYCLE | TRB_IOC |
+ TRB_TYPE(TRB_NORMAL);
+ } else {
+ priv_ep->trb_pool[0].control = TRB_CYCLE | TRB_IOC |
+ TRB_TYPE(TRB_NORMAL);
+ priv_ep->trb_pool[1].control = 0;
+ }
+
+ trace_cdns3_prepare_trb(priv_ep, priv_ep->trb_pool);
+
+ cdns3_select_ep(priv_dev, priv_dev->ep0_data_dir);
+
+ writel(EP_STS_TRBERR, &regs->ep_sts);
+ writel(EP_TRADDR_TRADDR(priv_ep->trb_pool_dma), &regs->ep_traddr);
+ trace_cdns3_doorbell_ep0(priv_dev->ep0_data_dir ? "ep0in" : "ep0out",
+ readl(&regs->ep_traddr));
+
+ /* TRB should be prepared before starting transfer. */
+ writel(EP_CMD_DRDY, &regs->ep_cmd);
+
+ /* Resume controller before arming transfer. */
+ __cdns3_gadget_wakeup(priv_dev);
+
+ if (erdy)
+ writel(EP_CMD_ERDY, &priv_dev->regs->ep_cmd);
+}
+
+/**
+ * cdns3_ep0_delegate_req - Returns status of handling setup packet
+ * Setup is handled by gadget driver
+ * @priv_dev: extended gadget object
+ * @ctrl_req: pointer to received setup packet
+ *
+ * Returns zero on success or negative value on failure
+ */
+static int cdns3_ep0_delegate_req(struct cdns3_device *priv_dev,
+ struct usb_ctrlrequest *ctrl_req)
+{
+ int ret;
+
+ spin_unlock(&priv_dev->lock);
+ priv_dev->setup_pending = 1;
+ ret = priv_dev->gadget_driver->setup(&priv_dev->gadget, ctrl_req);
+ priv_dev->setup_pending = 0;
+ spin_lock(&priv_dev->lock);
+ return ret;
+}
+
+static void cdns3_prepare_setup_packet(struct cdns3_device *priv_dev)
+{
+ priv_dev->ep0_data_dir = 0;
+ priv_dev->ep0_stage = CDNS3_SETUP_STAGE;
+ cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma,
+ sizeof(struct usb_ctrlrequest), 0, 0);
+}
+
+static void cdns3_ep0_complete_setup(struct cdns3_device *priv_dev,
+ u8 send_stall, u8 send_erdy)
+{
+ struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
+ struct usb_request *request;
+
+ request = cdns3_next_request(&priv_ep->pending_req_list);
+ if (request)
+ list_del_init(&request->list);
+
+ if (send_stall) {
+ trace_cdns3_halt(priv_ep, send_stall, 0);
+ /* set_stall on ep0 */
+ cdns3_select_ep(priv_dev, 0x00);
+ writel(EP_CMD_SSTALL, &priv_dev->regs->ep_cmd);
+ } else {
+ cdns3_prepare_setup_packet(priv_dev);
+ }
+
+ priv_dev->ep0_stage = CDNS3_SETUP_STAGE;
+ writel((send_erdy ? EP_CMD_ERDY : 0) | EP_CMD_REQ_CMPL,
+ &priv_dev->regs->ep_cmd);
+
+ cdns3_allow_enable_l1(priv_dev, 1);
+}
+
+/**
+ * cdns3_req_ep0_set_configuration - Handling of SET_CONFIG standard USB request
+ * @priv_dev: extended gadget object
+ * @ctrl_req: pointer to received setup packet
+ *
+ * Returns 0 if success, USB_GADGET_DELAYED_STATUS on deferred status stage,
+ * error code on error
+ */
+static int cdns3_req_ep0_set_configuration(struct cdns3_device *priv_dev,
+ struct usb_ctrlrequest *ctrl_req)
+{
+ enum usb_device_state device_state = priv_dev->gadget.state;
+ struct cdns3_endpoint *priv_ep;
+ u32 config = le16_to_cpu(ctrl_req->wValue);
+ int result = 0;
+ int i;
+
+ switch (device_state) {
+ case USB_STATE_ADDRESS:
+ /* Configure non-control EPs */
+ for (i = 0; i < CDNS3_ENDPOINTS_MAX_COUNT; i++) {
+ priv_ep = priv_dev->eps[i];
+ if (!priv_ep)
+ continue;
+
+ if (priv_ep->flags & EP_CLAIMED)
+ cdns3_ep_config(priv_ep);
+ }
+
+ result = cdns3_ep0_delegate_req(priv_dev, ctrl_req);
+
+ if (result)
+ return result;
+
+ if (config) {
+ cdns3_set_hw_configuration(priv_dev);
+ } else {
+ cdns3_hw_reset_eps_config(priv_dev);
+ usb_gadget_set_state(&priv_dev->gadget,
+ USB_STATE_ADDRESS);
+ }
+ break;
+ case USB_STATE_CONFIGURED:
+ result = cdns3_ep0_delegate_req(priv_dev, ctrl_req);
+
+ if (!config && !result) {
+ cdns3_hw_reset_eps_config(priv_dev);
+ usb_gadget_set_state(&priv_dev->gadget,
+ USB_STATE_ADDRESS);
+ }
+ break;
+ default:
+ result = -EINVAL;
+ }
+
+ return result;
+}
+
+/**
+ * cdns3_req_ep0_set_address - Handling of SET_ADDRESS standard USB request
+ * @priv_dev: extended gadget object
+ * @ctrl_req: pointer to received setup packet
+ *
+ * Returns 0 if success, error code on error
+ */
+static int cdns3_req_ep0_set_address(struct cdns3_device *priv_dev,
+ struct usb_ctrlrequest *ctrl_req)
+{
+ enum usb_device_state device_state = priv_dev->gadget.state;
+ u32 reg;
+ u32 addr;
+
+ addr = le16_to_cpu(ctrl_req->wValue);
+
+ if (addr > USB_DEVICE_MAX_ADDRESS) {
+ dev_err(priv_dev->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(priv_dev->dev,
+ "can't set_address from configured state\n");
+ return -EINVAL;
+ }
+
+ reg = readl(&priv_dev->regs->usb_cmd);
+
+ writel(reg | USB_CMD_FADDR(addr) | USB_CMD_SET_ADDR,
+ &priv_dev->regs->usb_cmd);
+
+ usb_gadget_set_state(&priv_dev->gadget,
+ (addr ? USB_STATE_ADDRESS : USB_STATE_DEFAULT));
+
+ return 0;
+}
+
+/**
+ * cdns3_req_ep0_get_status - Handling of GET_STATUS standard USB request
+ * @priv_dev: extended gadget object
+ * @ctrl_req: pointer to received setup packet
+ *
+ * Returns 0 if success, error code on error
+ */
+static int cdns3_req_ep0_get_status(struct cdns3_device *priv_dev,
+ struct usb_ctrlrequest *ctrl)
+{
+ __le16 *response_pkt;
+ u16 usb_status = 0;
+ u32 recip;
+
+ recip = ctrl->bRequestType & USB_RECIP_MASK;
+
+ switch (recip) {
+ case USB_RECIP_DEVICE:
+ /* self powered */
+ if (priv_dev->is_selfpowered)
+ usb_status = BIT(USB_DEVICE_SELF_POWERED);
+
+ if (priv_dev->wake_up_flag)
+ usb_status |= BIT(USB_DEVICE_REMOTE_WAKEUP);
+
+ if (priv_dev->gadget.speed != USB_SPEED_SUPER)
+ break;
+
+ if (priv_dev->u1_allowed)
+ usb_status |= BIT(USB_DEV_STAT_U1_ENABLED);
+
+ if (priv_dev->u2_allowed)
+ usb_status |= BIT(USB_DEV_STAT_U2_ENABLED);
+
+ break;
+ case USB_RECIP_INTERFACE:
+ return cdns3_ep0_delegate_req(priv_dev, ctrl);
+ case USB_RECIP_ENDPOINT:
+ /* check if endpoint is stalled */
+ cdns3_select_ep(priv_dev, ctrl->wIndex);
+ if (EP_STS_STALL(readl(&priv_dev->regs->ep_sts)))
+ usb_status = BIT(USB_ENDPOINT_HALT);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ response_pkt = (__le16 *)priv_dev->setup_buf;
+ *response_pkt = cpu_to_le16(usb_status);
+
+ cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma,
+ sizeof(*response_pkt), 1, 0);
+ return 0;
+}
+
+static int cdns3_ep0_feature_handle_device(struct cdns3_device *priv_dev,
+ 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 = priv_dev->gadget.state;
+ speed = priv_dev->gadget.speed;
+
+ switch (wValue) {
+ case USB_DEVICE_REMOTE_WAKEUP:
+ priv_dev->wake_up_flag = !!set;
+ break;
+ case USB_DEVICE_U1_ENABLE:
+ if (state != USB_STATE_CONFIGURED || speed != USB_SPEED_SUPER)
+ return -EINVAL;
+
+ priv_dev->u1_allowed = !!set;
+ break;
+ case USB_DEVICE_U2_ENABLE:
+ if (state != USB_STATE_CONFIGURED || speed != USB_SPEED_SUPER)
+ return -EINVAL;
+
+ priv_dev->u2_allowed = !!set;
+ break;
+ case USB_DEVICE_LTM_ENABLE:
+ ret = -EINVAL;
+ 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;
+
+ switch (tmode >> 8) {
+ case TEST_J:
+ case TEST_K:
+ case TEST_SE0_NAK:
+ case TEST_PACKET:
+ cdns3_ep0_complete_setup(priv_dev, 0, 1);
+ /**
+ * Little delay to give the controller some time
+ * for sending status stage.
+ * This time should be less then 3ms.
+ */
+ usleep_range(1000, 2000);
+ cdns3_set_register_bit(&priv_dev->regs->usb_cmd,
+ USB_CMD_STMODE |
+ USB_STS_TMODE_SEL(tmode - 1));
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int cdns3_ep0_feature_handle_intf(struct cdns3_device *priv_dev,
+ struct usb_ctrlrequest *ctrl,
+ int set)
+{
+ u32 wValue;
+ int ret = 0;
+
+ wValue = le16_to_cpu(ctrl->wValue);
+
+ switch (wValue) {
+ case USB_INTRF_FUNC_SUSPEND:
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int cdns3_ep0_feature_handle_endpoint(struct cdns3_device *priv_dev,
+ struct usb_ctrlrequest *ctrl,
+ int set)
+{
+ struct cdns3_endpoint *priv_ep;
+ int ret = 0;
+ u8 index;
+
+ if (le16_to_cpu(ctrl->wValue) != USB_ENDPOINT_HALT)
+ return -EINVAL;
+
+ if (!(ctrl->wIndex & ~USB_DIR_IN))
+ return 0;
+
+ index = cdns3_ep_addr_to_index(ctrl->wIndex);
+ priv_ep = priv_dev->eps[index];
+
+ cdns3_select_ep(priv_dev, ctrl->wIndex);
+
+ if (set)
+ __cdns3_gadget_ep_set_halt(priv_ep);
+ else if (!(priv_ep->flags & EP_WEDGE))
+ ret = __cdns3_gadget_ep_clear_halt(priv_ep);
+
+ cdns3_select_ep(priv_dev, 0x00);
+
+ return ret;
+}
+
+/**
+ * cdns3_req_ep0_handle_feature -
+ * Handling of GET/SET_FEATURE standard USB request
+ *
+ * @priv_dev: extended gadget object
+ * @ctrl_req: pointer to received setup packet
+ * @set: must be set to 1 for SET_FEATURE request
+ *
+ * Returns 0 if success, error code on error
+ */
+static int cdns3_req_ep0_handle_feature(struct cdns3_device *priv_dev,
+ struct usb_ctrlrequest *ctrl,
+ int set)
+{
+ int ret = 0;
+ u32 recip;
+
+ recip = ctrl->bRequestType & USB_RECIP_MASK;
+
+ switch (recip) {
+ case USB_RECIP_DEVICE:
+ ret = cdns3_ep0_feature_handle_device(priv_dev, ctrl, set);
+ break;
+ case USB_RECIP_INTERFACE:
+ ret = cdns3_ep0_feature_handle_intf(priv_dev, ctrl, set);
+ break;
+ case USB_RECIP_ENDPOINT:
+ ret = cdns3_ep0_feature_handle_endpoint(priv_dev, ctrl, set);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+/**
+ * cdns3_req_ep0_set_sel - Handling of SET_SEL standard USB request
+ * @priv_dev: extended gadget object
+ * @ctrl_req: pointer to received setup packet
+ *
+ * Returns 0 if success, error code on error
+ */
+static int cdns3_req_ep0_set_sel(struct cdns3_device *priv_dev,
+ struct usb_ctrlrequest *ctrl_req)
+{
+ if (priv_dev->gadget.state < USB_STATE_ADDRESS)
+ return -EINVAL;
+
+ if (ctrl_req->wLength != 6) {
+ dev_err(priv_dev->dev, "Set SEL should be 6 bytes, got %d\n",
+ ctrl_req->wLength);
+ return -EINVAL;
+ }
+
+ cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma, 6, 1, 0);
+ return 0;
+}
+
+/**
+ * cdns3_req_ep0_set_isoch_delay -
+ * Handling of GET_ISOCH_DELAY standard USB request
+ * @priv_dev: extended gadget object
+ * @ctrl_req: pointer to received setup packet
+ *
+ * Returns 0 if success, error code on error
+ */
+static int cdns3_req_ep0_set_isoch_delay(struct cdns3_device *priv_dev,
+ struct usb_ctrlrequest *ctrl_req)
+{
+ if (ctrl_req->wIndex || ctrl_req->wLength)
+ return -EINVAL;
+
+ priv_dev->isoch_delay = ctrl_req->wValue;
+
+ return 0;
+}
+
+/**
+ * cdns3_ep0_standard_request - Handling standard USB requests
+ * @priv_dev: extended gadget object
+ * @ctrl_req: pointer to received setup packet
+ *
+ * Returns 0 if success, error code on error
+ */
+static int cdns3_ep0_standard_request(struct cdns3_device *priv_dev,
+ struct usb_ctrlrequest *ctrl_req)
+{
+ int ret;
+
+ switch (ctrl_req->bRequest) {
+ case USB_REQ_SET_ADDRESS:
+ ret = cdns3_req_ep0_set_address(priv_dev, ctrl_req);
+ break;
+ case USB_REQ_SET_CONFIGURATION:
+ ret = cdns3_req_ep0_set_configuration(priv_dev, ctrl_req);
+ break;
+ case USB_REQ_GET_STATUS:
+ ret = cdns3_req_ep0_get_status(priv_dev, ctrl_req);
+ break;
+ case USB_REQ_CLEAR_FEATURE:
+ ret = cdns3_req_ep0_handle_feature(priv_dev, ctrl_req, 0);
+ break;
+ case USB_REQ_SET_FEATURE:
+ ret = cdns3_req_ep0_handle_feature(priv_dev, ctrl_req, 1);
+ break;
+ case USB_REQ_SET_SEL:
+ ret = cdns3_req_ep0_set_sel(priv_dev, ctrl_req);
+ break;
+ case USB_REQ_SET_ISOCH_DELAY:
+ ret = cdns3_req_ep0_set_isoch_delay(priv_dev, ctrl_req);
+ break;
+ default:
+ ret = cdns3_ep0_delegate_req(priv_dev, ctrl_req);
+ break;
+ }
+
+ return ret;
+}
+
+static void __pending_setup_status_handler(struct cdns3_device *priv_dev)
+{
+ struct usb_request *request = priv_dev->pending_status_request;
+
+ if (priv_dev->status_completion_no_call && request &&
+ request->complete) {
+ request->complete(&priv_dev->eps[0]->endpoint, request);
+ priv_dev->status_completion_no_call = 0;
+ }
+}
+
+void cdns3_pending_setup_status_handler(struct work_struct *work)
+{
+ struct cdns3_device *priv_dev = container_of(work, struct cdns3_device,
+ pending_status_wq);
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv_dev->lock, flags);
+ __pending_setup_status_handler(priv_dev);
+ spin_unlock_irqrestore(&priv_dev->lock, flags);
+}
+
+/**
+ * cdns3_ep0_setup_phase - Handling setup USB requests
+ * @priv_dev: extended gadget object
+ */
+static void cdns3_ep0_setup_phase(struct cdns3_device *priv_dev)
+{
+ struct usb_ctrlrequest *ctrl = priv_dev->setup_buf;
+ struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
+ int result;
+
+ priv_dev->ep0_data_dir = ctrl->bRequestType & USB_DIR_IN;
+
+ trace_cdns3_ctrl_req(ctrl);
+
+ if (!list_empty(&priv_ep->pending_req_list)) {
+ struct usb_request *request;
+
+ request = cdns3_next_request(&priv_ep->pending_req_list);
+ priv_ep->dir = priv_dev->ep0_data_dir;
+ cdns3_gadget_giveback(priv_ep, to_cdns3_request(request),
+ -ECONNRESET);
+ }
+
+ if (le16_to_cpu(ctrl->wLength))
+ priv_dev->ep0_stage = CDNS3_DATA_STAGE;
+ else
+ priv_dev->ep0_stage = CDNS3_STATUS_STAGE;
+
+ if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)
+ result = cdns3_ep0_standard_request(priv_dev, ctrl);
+ else
+ result = cdns3_ep0_delegate_req(priv_dev, ctrl);
+
+ if (result == USB_GADGET_DELAYED_STATUS)
+ return;
+
+ if (result < 0)
+ cdns3_ep0_complete_setup(priv_dev, 1, 1);
+ else if (priv_dev->ep0_stage == CDNS3_STATUS_STAGE)
+ cdns3_ep0_complete_setup(priv_dev, 0, 1);
+}
+
+static void cdns3_transfer_completed(struct cdns3_device *priv_dev)
+{
+ struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
+
+ if (!list_empty(&priv_ep->pending_req_list)) {
+ struct usb_request *request;
+
+ trace_cdns3_complete_trb(priv_ep, priv_ep->trb_pool);
+ request = cdns3_next_request(&priv_ep->pending_req_list);
+
+ request->actual =
+ TRB_LEN(le32_to_cpu(priv_ep->trb_pool->length));
+
+ priv_ep->dir = priv_dev->ep0_data_dir;
+ cdns3_gadget_giveback(priv_ep, to_cdns3_request(request), 0);
+ }
+
+ cdns3_ep0_complete_setup(priv_dev, 0, 0);
+}
+
+/**
+ * cdns3_check_new_setup - Check if controller receive new SETUP packet.
+ * @priv_dev: extended gadget object
+ *
+ * The SETUP packet can be kept in on-chip memory or in system memory.
+ */
+static bool cdns3_check_new_setup(struct cdns3_device *priv_dev)
+{
+ u32 ep_sts_reg;
+
+ cdns3_select_ep(priv_dev, 0 | USB_DIR_OUT);
+ ep_sts_reg = readl(&priv_dev->regs->ep_sts);
+
+ return !!(ep_sts_reg & (EP_STS_SETUP | EP_STS_STPWAIT));
+}
+
+/**
+ * cdns3_check_ep0_interrupt_proceed - Processes interrupt related to endpoint 0
+ * @priv_dev: extended gadget object
+ * @dir: USB_DIR_IN for IN direction, USB_DIR_OUT for OUT direction
+ */
+void cdns3_check_ep0_interrupt_proceed(struct cdns3_device *priv_dev, int dir)
+{
+ u32 ep_sts_reg;
+
+ cdns3_select_ep(priv_dev, dir);
+
+ ep_sts_reg = readl(&priv_dev->regs->ep_sts);
+ writel(ep_sts_reg, &priv_dev->regs->ep_sts);
+
+ trace_cdns3_ep0_irq(priv_dev, ep_sts_reg);
+
+ __pending_setup_status_handler(priv_dev);
+
+ if (ep_sts_reg & EP_STS_SETUP)
+ priv_dev->wait_for_setup = 1;
+
+ if (priv_dev->wait_for_setup && ep_sts_reg & EP_STS_IOC) {
+ priv_dev->wait_for_setup = 0;
+ cdns3_allow_enable_l1(priv_dev, 0);
+ cdns3_ep0_setup_phase(priv_dev);
+ } else if ((ep_sts_reg & EP_STS_IOC) || (ep_sts_reg & EP_STS_ISP)) {
+ priv_dev->ep0_data_dir = dir;
+ cdns3_transfer_completed(priv_dev);
+ }
+
+ if (ep_sts_reg & EP_STS_DESCMIS) {
+ if (dir == 0 && !priv_dev->setup_pending)
+ cdns3_prepare_setup_packet(priv_dev);
+ }
+}
+
+/**
+ * cdns3_gadget_ep0_enable
+ * Function shouldn't be called by gadget driver,
+ * endpoint 0 is allways active
+ */
+static int cdns3_gadget_ep0_enable(struct usb_ep *ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ return -EINVAL;
+}
+
+/**
+ * cdns3_gadget_ep0_disable
+ * Function shouldn't be called by gadget driver,
+ * endpoint 0 is allways active
+ */
+static int cdns3_gadget_ep0_disable(struct usb_ep *ep)
+{
+ return -EINVAL;
+}
+
+/**
+ * cdns3_gadget_ep0_set_halt
+ * @ep: pointer to endpoint zero object
+ * @value: 1 for set stall, 0 for clear stall
+ *
+ * Returns 0
+ */
+static int cdns3_gadget_ep0_set_halt(struct usb_ep *ep, int value)
+{
+ /* TODO */
+ return 0;
+}
+
+/**
+ * cdns3_gadget_ep0_queue Transfer data on endpoint zero
+ * @ep: pointer to endpoint zero object
+ * @request: pointer to request object
+ * @gfp_flags: gfp flags
+ *
+ * Returns 0 on success, error code elsewhere
+ */
+static int cdns3_gadget_ep0_queue(struct usb_ep *ep,
+ struct usb_request *request,
+ gfp_t gfp_flags)
+{
+ struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
+ struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+ unsigned long flags;
+ int erdy_sent = 0;
+ int ret = 0;
+ u8 zlp = 0;
+
+ trace_cdns3_ep0_queue(priv_dev, request);
+
+ /* cancel the request if controller receive new SETUP packet. */
+ if (cdns3_check_new_setup(priv_dev))
+ return -ECONNRESET;
+
+ /* send STATUS stage. Should be called only for SET_CONFIGURATION */
+ if (priv_dev->ep0_stage == CDNS3_STATUS_STAGE) {
+ spin_lock_irqsave(&priv_dev->lock, flags);
+ cdns3_select_ep(priv_dev, 0x00);
+
+ erdy_sent = !priv_dev->hw_configured_flag;
+ cdns3_set_hw_configuration(priv_dev);
+
+ if (!erdy_sent)
+ cdns3_ep0_complete_setup(priv_dev, 0, 1);
+
+ cdns3_allow_enable_l1(priv_dev, 1);
+
+ request->actual = 0;
+ priv_dev->status_completion_no_call = true;
+ priv_dev->pending_status_request = request;
+ spin_unlock_irqrestore(&priv_dev->lock, flags);
+
+ /*
+ * Since there is no completion interrupt for status stage,
+ * it needs to call ->completion in software after
+ * ep0_queue is back.
+ */
+ queue_work(system_freezable_wq, &priv_dev->pending_status_wq);
+ return 0;
+ }
+
+ spin_lock_irqsave(&priv_dev->lock, flags);
+ if (!list_empty(&priv_ep->pending_req_list)) {
+ dev_err(priv_dev->dev,
+ "can't handle multiple requests for ep0\n");
+ spin_unlock_irqrestore(&priv_dev->lock, flags);
+ return -EBUSY;
+ }
+
+ ret = usb_gadget_map_request_by_dev(priv_dev->sysdev, request,
+ priv_dev->ep0_data_dir);
+ if (ret) {
+ spin_unlock_irqrestore(&priv_dev->lock, flags);
+ dev_err(priv_dev->dev, "failed to map request\n");
+ return -EINVAL;
+ }
+
+ request->status = -EINPROGRESS;
+ list_add_tail(&request->list, &priv_ep->pending_req_list);
+
+ if (request->zero && request->length &&
+ (request->length % ep->maxpacket == 0))
+ zlp = 1;
+
+ cdns3_ep0_run_transfer(priv_dev, request->dma, request->length, 1, zlp);
+
+ spin_unlock_irqrestore(&priv_dev->lock, flags);
+
+ return ret;
+}
+
+/**
+ * cdns3_gadget_ep_set_wedge Set wedge on selected endpoint
+ * @ep: endpoint object
+ *
+ * Returns 0
+ */
+int cdns3_gadget_ep_set_wedge(struct usb_ep *ep)
+{
+ struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
+ struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+
+ dev_dbg(priv_dev->dev, "Wedge for %s\n", ep->name);
+ cdns3_gadget_ep_set_halt(ep, 1);
+ priv_ep->flags |= EP_WEDGE;
+
+ return 0;
+}
+
+const struct usb_ep_ops cdns3_gadget_ep0_ops = {
+ .enable = cdns3_gadget_ep0_enable,
+ .disable = cdns3_gadget_ep0_disable,
+ .alloc_request = cdns3_gadget_ep_alloc_request,
+ .free_request = cdns3_gadget_ep_free_request,
+ .queue = cdns3_gadget_ep0_queue,
+ .dequeue = cdns3_gadget_ep_dequeue,
+ .set_halt = cdns3_gadget_ep0_set_halt,
+ .set_wedge = cdns3_gadget_ep_set_wedge,
+};
+
+/**
+ * cdns3_ep0_config - Configures default endpoint
+ * @priv_dev: extended gadget object
+ *
+ * Functions sets parameters: maximal packet size and enables interrupts
+ */
+void cdns3_ep0_config(struct cdns3_device *priv_dev)
+{
+ struct cdns3_usb_regs __iomem *regs;
+ struct cdns3_endpoint *priv_ep;
+ u32 max_packet_size = 64;
+
+ regs = priv_dev->regs;
+
+ if (priv_dev->gadget.speed == USB_SPEED_SUPER)
+ max_packet_size = 512;
+
+ priv_ep = priv_dev->eps[0];
+
+ if (!list_empty(&priv_ep->pending_req_list)) {
+ struct usb_request *request;
+
+ request = cdns3_next_request(&priv_ep->pending_req_list);
+ list_del_init(&request->list);
+ }
+
+ priv_dev->u1_allowed = 0;
+ priv_dev->u2_allowed = 0;
+
+ priv_dev->gadget.ep0->maxpacket = max_packet_size;
+ cdns3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(max_packet_size);
+
+ /* init ep out */
+ cdns3_select_ep(priv_dev, USB_DIR_OUT);
+
+ if (priv_dev->dev_ver >= DEV_VER_V3) {
+ cdns3_set_register_bit(&priv_dev->regs->dtrans,
+ BIT(0) | BIT(16));
+ cdns3_set_register_bit(&priv_dev->regs->tdl_from_trb,
+ BIT(0) | BIT(16));
+ }
+
+ writel(EP_CFG_ENABLE | EP_CFG_MAXPKTSIZE(max_packet_size),
+ &regs->ep_cfg);
+
+ writel(EP_STS_EN_SETUPEN | EP_STS_EN_DESCMISEN | EP_STS_EN_TRBERREN,
+ &regs->ep_sts_en);
+
+ /* init ep in */
+ cdns3_select_ep(priv_dev, USB_DIR_IN);
+
+ writel(EP_CFG_ENABLE | EP_CFG_MAXPKTSIZE(max_packet_size),
+ &regs->ep_cfg);
+
+ writel(EP_STS_EN_SETUPEN | EP_STS_EN_TRBERREN, &regs->ep_sts_en);
+
+ cdns3_set_register_bit(&regs->usb_conf, USB_CONF_U1DS | USB_CONF_U2DS);
+}
+
+/**
+ * cdns3_init_ep0 Initializes software endpoint 0 of gadget
+ * @priv_dev: extended gadget object
+ * @ep_priv: extended endpoint object
+ *
+ * Returns 0 on success else error code.
+ */
+int cdns3_init_ep0(struct cdns3_device *priv_dev,
+ struct cdns3_endpoint *priv_ep)
+{
+ sprintf(priv_ep->name, "ep0");
+
+ /* fill linux fields */
+ priv_ep->endpoint.ops = &cdns3_gadget_ep0_ops;
+ priv_ep->endpoint.maxburst = 1;
+ usb_ep_set_maxpacket_limit(&priv_ep->endpoint,
+ CDNS3_EP0_MAX_PACKET_LIMIT);
+ priv_ep->endpoint.address = 0;
+ priv_ep->endpoint.caps.type_control = 1;
+ priv_ep->endpoint.caps.dir_in = 1;
+ priv_ep->endpoint.caps.dir_out = 1;
+ priv_ep->endpoint.name = priv_ep->name;
+ priv_ep->endpoint.desc = &cdns3_gadget_ep0_desc;
+ priv_dev->gadget.ep0 = &priv_ep->endpoint;
+ priv_ep->type = USB_ENDPOINT_XFER_CONTROL;
+
+ return cdns3_allocate_trb_pool(priv_ep);
+}
diff --git a/drivers/usb/cdns3/gadget-export.h b/drivers/usb/cdns3/gadget-export.h
new file mode 100644
index 000000000000..577469eee961
--- /dev/null
+++ b/drivers/usb/cdns3/gadget-export.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Cadence USBSS DRD Driver - Gadget Export APIs.
+ *
+ * Copyright (C) 2017 NXP
+ * Copyright (C) 2017-2018 NXP
+ *
+ * Authors: Peter Chen <peter.chen@nxp.com>
+ */
+#ifndef __LINUX_CDNS3_GADGET_EXPORT
+#define __LINUX_CDNS3_GADGET_EXPORT
+
+#ifdef CONFIG_USB_CDNS3_GADGET
+
+int cdns3_gadget_init(struct cdns3 *cdns);
+void cdns3_gadget_exit(struct cdns3 *cdns);
+#else
+
+static inline int cdns3_gadget_init(struct cdns3 *cdns)
+{
+ return -ENXIO;
+}
+
+static inline void cdns3_gadget_exit(struct cdns3 *cdns) { }
+
+#endif
+
+#endif /* __LINUX_CDNS3_GADGET_EXPORT */
diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c
new file mode 100644
index 000000000000..228cdc4ab886
--- /dev/null
+++ b/drivers/usb/cdns3/gadget.c
@@ -0,0 +1,2744 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Cadence USBSS DRD Driver - gadget side.
+ *
+ * Copyright (C) 2018-2019 Cadence Design Systems.
+ * Copyright (C) 2017-2018 NXP
+ *
+ * Authors: Pawel Jez <pjez@cadence.com>,
+ * Pawel Laszczak <pawell@cadence.com>
+ * Peter Chen <peter.chen@nxp.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.
+ *
+ * Issue has been fixed in DEV_VER_V3 version of controller.
+ *
+ * Work around 2:
+ * Controller for OUT endpoints has shared on-chip buffers for all incoming
+ * packets, including ep0out. It's FIFO buffer, so packets must be handle by DMA
+ * in correct order. If the first packet in the buffer will not be handled,
+ * then the following packets directed for other endpoints and functions
+ * will be blocked.
+ * Additionally the packets directed to one endpoint can block entire on-chip
+ * buffers. In this case transfer to other endpoints also will blocked.
+ *
+ * To resolve this issue after raising the descriptor missing interrupt
+ * driver prepares internal usb_request object and use it to arm DMA transfer.
+ *
+ * The problematic situation was observed in case when endpoint has been enabled
+ * but no usb_request were queued. Driver try detects such endpoints and will
+ * use this workaround only for these endpoint.
+ *
+ * Driver use limited number of buffer. This number can be set by macro
+ * CDNS3_WA2_NUM_BUFFERS.
+ *
+ * Such blocking situation was observed on ACM gadget. For this function
+ * host send OUT data packet but ACM function is not prepared for this packet.
+ * It's cause that buffer placed in on chip memory block transfer to other
+ * endpoints.
+ *
+ * Issue has been fixed in DEV_VER_V2 version of controller.
+ *
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/usb/gadget.h>
+#include <linux/module.h>
+#include <linux/iopoll.h>
+
+#include "core.h"
+#include "gadget-export.h"
+#include "gadget.h"
+#include "trace.h"
+#include "drd.h"
+
+static int __cdns3_gadget_ep_queue(struct usb_ep *ep,
+ struct usb_request *request,
+ gfp_t gfp_flags);
+
+/**
+ * cdns3_set_register_bit - set bit in given register.
+ * @ptr: address of device controller register to be read and changed
+ * @mask: bits requested to set
+ */
+void cdns3_set_register_bit(void __iomem *ptr, u32 mask)
+{
+ mask = readl(ptr) | mask;
+ writel(mask, ptr);
+}
+
+/**
+ * cdns3_ep_addr_to_index - Macro converts endpoint address to
+ * index of endpoint object in cdns3_device.eps[] container
+ * @ep_addr: endpoint address for which endpoint object is required
+ *
+ */
+u8 cdns3_ep_addr_to_index(u8 ep_addr)
+{
+ return (((ep_addr & 0x7F)) + ((ep_addr & USB_DIR_IN) ? 16 : 0));
+}
+
+static int cdns3_get_dma_pos(struct cdns3_device *priv_dev,
+ struct cdns3_endpoint *priv_ep)
+{
+ int dma_index;
+
+ dma_index = readl(&priv_dev->regs->ep_traddr) - priv_ep->trb_pool_dma;
+
+ return dma_index / TRB_SIZE;
+}
+
+/**
+ * cdns3_next_request - returns next request from list
+ * @list: list containing requests
+ *
+ * Returns request or NULL if no requests in list
+ */
+struct usb_request *cdns3_next_request(struct list_head *list)
+{
+ return list_first_entry_or_null(list, struct usb_request, list);
+}
+
+/**
+ * cdns3_next_align_buf - returns next buffer from list
+ * @list: list containing buffers
+ *
+ * Returns buffer or NULL if no buffers in list
+ */
+struct cdns3_aligned_buf *cdns3_next_align_buf(struct list_head *list)
+{
+ return list_first_entry_or_null(list, struct cdns3_aligned_buf, list);
+}
+
+/**
+ * cdns3_next_priv_request - returns next request from list
+ * @list: list containing requests
+ *
+ * Returns request or NULL if no requests in list
+ */
+struct cdns3_request *cdns3_next_priv_request(struct list_head *list)
+{
+ return list_first_entry_or_null(list, struct cdns3_request, list);
+}
+
+/**
+ * select_ep - selects endpoint
+ * @priv_dev: extended gadget object
+ * @ep: endpoint address
+ */
+void cdns3_select_ep(struct cdns3_device *priv_dev, u32 ep)
+{
+ if (priv_dev->selected_ep == ep)
+ return;
+
+ priv_dev->selected_ep = ep;
+ writel(ep, &priv_dev->regs->ep_sel);
+}
+
+dma_addr_t cdns3_trb_virt_to_dma(struct cdns3_endpoint *priv_ep,
+ struct cdns3_trb *trb)
+{
+ u32 offset = (char *)trb - (char *)priv_ep->trb_pool;
+
+ return priv_ep->trb_pool_dma + offset;
+}
+
+int cdns3_ring_size(struct cdns3_endpoint *priv_ep)
+{
+ switch (priv_ep->type) {
+ case USB_ENDPOINT_XFER_ISOC:
+ return TRB_ISO_RING_SIZE;
+ case USB_ENDPOINT_XFER_CONTROL:
+ return TRB_CTRL_RING_SIZE;
+ default:
+ return TRB_RING_SIZE;
+ }
+}
+
+/**
+ * cdns3_allocate_trb_pool - Allocates TRB's pool for selected endpoint
+ * @priv_ep: endpoint object
+ *
+ * Function will return 0 on success or -ENOMEM on allocation error
+ */
+int cdns3_allocate_trb_pool(struct cdns3_endpoint *priv_ep)
+{
+ struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+ int ring_size = cdns3_ring_size(priv_ep);
+ struct cdns3_trb *link_trb;
+
+ if (!priv_ep->trb_pool) {
+ priv_ep->trb_pool = dma_alloc_coherent(priv_dev->sysdev,
+ ring_size,
+ &priv_ep->trb_pool_dma,
+ GFP_DMA32 | GFP_ATOMIC);
+ if (!priv_ep->trb_pool)
+ return -ENOMEM;
+ } else {
+ memset(priv_ep->trb_pool, 0, ring_size);
+ }
+
+ if (!priv_ep->num)
+ return 0;
+
+ priv_ep->num_trbs = ring_size / TRB_SIZE;
+ /* Initialize the last TRB as Link TRB. */
+ link_trb = (priv_ep->trb_pool + (priv_ep->num_trbs - 1));
+ link_trb->buffer = TRB_BUFFER(priv_ep->trb_pool_dma);
+ link_trb->control = TRB_CYCLE | TRB_TYPE(TRB_LINK) | TRB_TOGGLE;
+
+ return 0;
+}
+
+static void cdns3_free_trb_pool(struct cdns3_endpoint *priv_ep)
+{
+ struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+
+ if (priv_ep->trb_pool) {
+ dma_free_coherent(priv_dev->sysdev,
+ cdns3_ring_size(priv_ep),
+ priv_ep->trb_pool, priv_ep->trb_pool_dma);
+ priv_ep->trb_pool = NULL;
+ }
+}
+
+/**
+ * cdns3_ep_stall_flush - Stalls and flushes selected endpoint
+ * @priv_ep: endpoint object
+ *
+ * Endpoint must be selected before call to this function
+ */
+static void cdns3_ep_stall_flush(struct cdns3_endpoint *priv_ep)
+{
+ struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+ int val;
+
+ trace_cdns3_halt(priv_ep, 1, 1);
+
+ writel(EP_CMD_DFLUSH | EP_CMD_ERDY | EP_CMD_SSTALL,
+ &priv_dev->regs->ep_cmd);
+
+ /* wait for DFLUSH cleared */
+ readl_poll_timeout_atomic(&priv_dev->regs->ep_cmd, val,
+ !(val & EP_CMD_DFLUSH), 1, 1000);
+ priv_ep->flags |= EP_STALLED;
+ priv_ep->flags &= ~EP_STALL_PENDING;
+}
+
+/**
+ * cdns3_hw_reset_eps_config - reset endpoints configuration kept by controller.
+ * @priv_dev: extended gadget object
+ */
+void cdns3_hw_reset_eps_config(struct cdns3_device *priv_dev)
+{
+ writel(USB_CONF_CFGRST, &priv_dev->regs->usb_conf);
+
+ cdns3_allow_enable_l1(priv_dev, 0);
+ priv_dev->hw_configured_flag = 0;
+ priv_dev->onchip_used_size = 0;
+ priv_dev->out_mem_is_allocated = 0;
+ priv_dev->wait_for_setup = 0;
+}
+
+/**
+ * cdns3_ep_inc_trb - increment a trb index.
+ * @index: Pointer to the TRB index to increment.
+ * @cs: Cycle state
+ * @trb_in_seg: number of TRBs in segment
+ *
+ * The index should never point to the link TRB. After incrementing,
+ * if it is 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 cdns3_ep_inc_trb(int *index, u8 *cs, int trb_in_seg)
+{
+ (*index)++;
+ if (*index == (trb_in_seg - 1)) {
+ *index = 0;
+ *cs ^= 1;
+ }
+}
+
+/**
+ * cdns3_ep_inc_enq - increment endpoint's enqueue pointer
+ * @priv_ep: The endpoint whose enqueue pointer we're incrementing
+ */
+static void cdns3_ep_inc_enq(struct cdns3_endpoint *priv_ep)
+{
+ priv_ep->free_trbs--;
+ cdns3_ep_inc_trb(&priv_ep->enqueue, &priv_ep->pcs, priv_ep->num_trbs);
+}
+
+/**
+ * cdns3_ep_inc_deq - increment endpoint's dequeue pointer
+ * @priv_ep: The endpoint whose dequeue pointer we're incrementing
+ */
+static void cdns3_ep_inc_deq(struct cdns3_endpoint *priv_ep)
+{
+ priv_ep->free_trbs++;
+ cdns3_ep_inc_trb(&priv_ep->dequeue, &priv_ep->ccs, priv_ep->num_trbs);
+}
+
+void cdns3_move_deq_to_next_trb(struct cdns3_request *priv_req)
+{
+ struct cdns3_endpoint *priv_ep = priv_req->priv_ep;
+ int current_trb = priv_req->start_trb;
+
+ while (current_trb != priv_req->end_trb) {
+ cdns3_ep_inc_deq(priv_ep);
+ current_trb = priv_ep->dequeue;
+ }
+
+ cdns3_ep_inc_deq(priv_ep);
+}
+
+/**
+ * cdns3_allow_enable_l1 - enable/disable permits to transition to L1.
+ * @priv_dev: Extended gadget object
+ * @enable: Enable/disable permit to transition to L1.
+ *
+ * If bit USB_CONF_L1EN is set and device receive Extended Token packet,
+ * then controller answer with ACK handshake.
+ * If bit USB_CONF_L1DS is set and device receive Extended Token packet,
+ * then controller answer with NYET handshake.
+ */
+void cdns3_allow_enable_l1(struct cdns3_device *priv_dev, int enable)
+{
+ if (enable)
+ writel(USB_CONF_L1EN, &priv_dev->regs->usb_conf);
+ else
+ writel(USB_CONF_L1DS, &priv_dev->regs->usb_conf);
+}
+
+enum usb_device_speed cdns3_get_speed(struct cdns3_device *priv_dev)
+{
+ u32 reg;
+
+ reg = readl(&priv_dev->regs->usb_sts);
+
+ if (DEV_SUPERSPEED(reg))
+ return USB_SPEED_SUPER;
+ else if (DEV_HIGHSPEED(reg))
+ return USB_SPEED_HIGH;
+ else if (DEV_FULLSPEED(reg))
+ return USB_SPEED_FULL;
+ else if (DEV_LOWSPEED(reg))
+ return USB_SPEED_LOW;
+ return USB_SPEED_UNKNOWN;
+}
+
+/**
+ * cdns3_start_all_request - add to ring all request not started
+ * @priv_dev: Extended gadget object
+ * @priv_ep: The endpoint for whom request will be started.
+ *
+ * Returns return ENOMEM if transfer ring i not enough TRBs to start
+ * all requests.
+ */
+static int cdns3_start_all_request(struct cdns3_device *priv_dev,
+ struct cdns3_endpoint *priv_ep)
+{
+ struct usb_request *request;
+ int ret = 0;
+
+ while (!list_empty(&priv_ep->deferred_req_list)) {
+ request = cdns3_next_request(&priv_ep->deferred_req_list);
+
+ ret = cdns3_ep_run_transfer(priv_ep, request);
+ if (ret)
+ return ret;
+
+ list_del(&request->list);
+ list_add_tail(&request->list,
+ &priv_ep->pending_req_list);
+ }
+
+ priv_ep->flags &= ~EP_RING_FULL;
+ return ret;
+}
+
+/*
+ * WA2: Set flag for all not ISOC OUT endpoints. If this flag is set
+ * driver try to detect whether endpoint need additional internal
+ * buffer for unblocking on-chip FIFO buffer. This flag will be cleared
+ * if before first DESCMISS interrupt the DMA will be armed.
+ */
+#define cdns3_wa2_enable_detection(priv_dev, ep_priv, reg) do { \
+ if (!priv_ep->dir && priv_ep->type != USB_ENDPOINT_XFER_ISOC) { \
+ priv_ep->flags |= EP_QUIRK_EXTRA_BUF_DET; \
+ (reg) |= EP_STS_EN_DESCMISEN; \
+ } } while (0)
+
+/**
+ * cdns3_wa2_descmiss_copy_data copy data from internal requests to
+ * request queued by class driver.
+ * @priv_ep: extended endpoint object
+ * @request: request object
+ */
+static void cdns3_wa2_descmiss_copy_data(struct cdns3_endpoint *priv_ep,
+ struct usb_request *request)
+{
+ struct usb_request *descmiss_req;
+ struct cdns3_request *descmiss_priv_req;
+
+ while (!list_empty(&priv_ep->wa2_descmiss_req_list)) {
+ int chunk_end;
+ int length;
+
+ descmiss_priv_req =
+ cdns3_next_priv_request(&priv_ep->wa2_descmiss_req_list);
+ descmiss_req = &descmiss_priv_req->request;
+
+ /* driver can't touch pending request */
+ if (descmiss_priv_req->flags & REQUEST_PENDING)
+ break;
+
+ chunk_end = descmiss_priv_req->flags & REQUEST_INTERNAL_CH;
+ length = request->actual + descmiss_req->actual;
+
+ request->status = descmiss_req->status;
+
+ if (length <= request->length) {
+ memcpy(&((u8 *)request->buf)[request->actual],
+ descmiss_req->buf,
+ descmiss_req->actual);
+ request->actual = length;
+ } else {
+ /* It should never occures */
+ request->status = -ENOMEM;
+ }
+
+ list_del_init(&descmiss_priv_req->list);
+
+ kfree(descmiss_req->buf);
+ cdns3_gadget_ep_free_request(&priv_ep->endpoint, descmiss_req);
+ --priv_ep->wa2_counter;
+
+ if (!chunk_end)
+ break;
+ }
+}
+
+struct usb_request *cdns3_wa2_gadget_giveback(struct cdns3_device *priv_dev,
+ struct cdns3_endpoint *priv_ep,
+ struct cdns3_request *priv_req)
+{
+ if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN &&
+ priv_req->flags & REQUEST_INTERNAL) {
+ struct usb_request *req;
+
+ req = cdns3_next_request(&priv_ep->deferred_req_list);
+
+ priv_ep->descmis_req = NULL;
+
+ if (!req)
+ return NULL;
+
+ cdns3_wa2_descmiss_copy_data(priv_ep, req);
+ if (!(priv_ep->flags & EP_QUIRK_END_TRANSFER) &&
+ req->length != req->actual) {
+ /* wait for next part of transfer */
+ return NULL;
+ }
+
+ if (req->status == -EINPROGRESS)
+ req->status = 0;
+
+ list_del_init(&req->list);
+ cdns3_start_all_request(priv_dev, priv_ep);
+ return req;
+ }
+
+ return &priv_req->request;
+}
+
+int cdns3_wa2_gadget_ep_queue(struct cdns3_device *priv_dev,
+ struct cdns3_endpoint *priv_ep,
+ struct cdns3_request *priv_req)
+{
+ int deferred = 0;
+
+ /*
+ * If transfer was queued before DESCMISS appear than we
+ * can disable handling of DESCMISS interrupt. Driver assumes that it
+ * can disable special treatment for this endpoint.
+ */
+ if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_DET) {
+ u32 reg;
+
+ cdns3_select_ep(priv_dev, priv_ep->num | priv_ep->dir);
+ priv_ep->flags &= ~EP_QUIRK_EXTRA_BUF_DET;
+ reg = readl(&priv_dev->regs->ep_sts_en);
+ reg &= ~EP_STS_EN_DESCMISEN;
+ trace_cdns3_wa2(priv_ep, "workaround disabled\n");
+ writel(reg, &priv_dev->regs->ep_sts_en);
+ }
+
+ if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN) {
+ u8 pending_empty = list_empty(&priv_ep->pending_req_list);
+ u8 descmiss_empty = list_empty(&priv_ep->wa2_descmiss_req_list);
+
+ /*
+ * DESCMISS transfer has been finished, so data will be
+ * directly copied from internal allocated usb_request
+ * objects.
+ */
+ if (pending_empty && !descmiss_empty &&
+ !(priv_req->flags & REQUEST_INTERNAL)) {
+ cdns3_wa2_descmiss_copy_data(priv_ep,
+ &priv_req->request);
+
+ trace_cdns3_wa2(priv_ep, "get internal stored data");
+
+ list_add_tail(&priv_req->request.list,
+ &priv_ep->pending_req_list);
+ cdns3_gadget_giveback(priv_ep, priv_req,
+ priv_req->request.status);
+
+ /*
+ * Intentionally driver returns positive value as
+ * correct value. It informs that transfer has
+ * been finished.
+ */
+ return EINPROGRESS;
+ }
+
+ /*
+ * Driver will wait for completion DESCMISS transfer,
+ * before starts new, not DESCMISS transfer.
+ */
+ if (!pending_empty && !descmiss_empty) {
+ trace_cdns3_wa2(priv_ep, "wait for pending transfer\n");
+ deferred = 1;
+ }
+
+ if (priv_req->flags & REQUEST_INTERNAL)
+ list_add_tail(&priv_req->list,
+ &priv_ep->wa2_descmiss_req_list);
+ }
+
+ return deferred;
+}
+
+static void cdns3_wa2_remove_old_request(struct cdns3_endpoint *priv_ep)
+{
+ struct cdns3_request *priv_req;
+
+ while (!list_empty(&priv_ep->wa2_descmiss_req_list)) {
+ u8 chain;
+
+ priv_req = cdns3_next_priv_request(&priv_ep->wa2_descmiss_req_list);
+ chain = !!(priv_req->flags & REQUEST_INTERNAL_CH);
+
+ trace_cdns3_wa2(priv_ep, "removes eldest request");
+
+ kfree(priv_req->request.buf);
+ cdns3_gadget_ep_free_request(&priv_ep->endpoint,
+ &priv_req->request);
+ list_del_init(&priv_req->list);
+ --priv_ep->wa2_counter;
+
+ if (!chain)
+ break;
+ }
+}
+
+/**
+ * cdns3_wa2_descmissing_packet - handles descriptor missing event.
+ * @priv_dev: extended gadget object
+ *
+ * This function is used only for WA2. For more information see Work around 2
+ * description.
+ */
+static void cdns3_wa2_descmissing_packet(struct cdns3_endpoint *priv_ep)
+{
+ struct cdns3_request *priv_req;
+ struct usb_request *request;
+
+ if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_DET) {
+ priv_ep->flags &= ~EP_QUIRK_EXTRA_BUF_DET;
+ priv_ep->flags |= EP_QUIRK_EXTRA_BUF_EN;
+ }
+
+ trace_cdns3_wa2(priv_ep, "Description Missing detected\n");
+
+ if (priv_ep->wa2_counter >= CDNS3_WA2_NUM_BUFFERS)
+ cdns3_wa2_remove_old_request(priv_ep);
+
+ request = cdns3_gadget_ep_alloc_request(&priv_ep->endpoint,
+ GFP_ATOMIC);
+ if (!request)
+ goto err;
+
+ priv_req = to_cdns3_request(request);
+ priv_req->flags |= REQUEST_INTERNAL;
+
+ /* if this field is still assigned it indicate that transfer related
+ * with this request has not been finished yet. Driver in this
+ * case simply allocate next request and assign flag REQUEST_INTERNAL_CH
+ * flag to previous one. It will indicate that current request is
+ * part of the previous one.
+ */
+ if (priv_ep->descmis_req)
+ priv_ep->descmis_req->flags |= REQUEST_INTERNAL_CH;
+
+ priv_req->request.buf = kzalloc(CDNS3_DESCMIS_BUF_SIZE,
+ GFP_ATOMIC);
+ priv_ep->wa2_counter++;
+
+ if (!priv_req->request.buf) {
+ cdns3_gadget_ep_free_request(&priv_ep->endpoint, request);
+ goto err;
+ }
+
+ priv_req->request.length = CDNS3_DESCMIS_BUF_SIZE;
+ priv_ep->descmis_req = priv_req;
+
+ __cdns3_gadget_ep_queue(&priv_ep->endpoint,
+ &priv_ep->descmis_req->request,
+ GFP_ATOMIC);
+
+ return;
+
+err:
+ dev_err(priv_ep->cdns3_dev->dev,
+ "Failed: No sufficient memory for DESCMIS\n");
+}
+
+/**
+ * cdns3_gadget_giveback - call struct usb_request's ->complete callback
+ * @priv_ep: The endpoint to whom the request belongs to
+ * @priv_req: The request we're giving back
+ * @status: completion code for the request
+ *
+ * Must be called with controller's lock held and interrupts disabled. This
+ * function will unmap @req and call its ->complete() callback to notify upper
+ * layers that it has completed.
+ */
+void cdns3_gadget_giveback(struct cdns3_endpoint *priv_ep,
+ struct cdns3_request *priv_req,
+ int status)
+{
+ struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+ struct usb_request *request = &priv_req->request;
+
+ list_del_init(&request->list);
+
+ if (request->status == -EINPROGRESS)
+ request->status = status;
+
+ usb_gadget_unmap_request_by_dev(priv_dev->sysdev, request,
+ priv_ep->dir);
+
+ if ((priv_req->flags & REQUEST_UNALIGNED) &&
+ priv_ep->dir == USB_DIR_OUT && !request->status)
+ memcpy(request->buf, priv_req->aligned_buf->buf,
+ request->length);
+
+ priv_req->flags &= ~(REQUEST_PENDING | REQUEST_UNALIGNED);
+ trace_cdns3_gadget_giveback(priv_req);
+
+ if (priv_dev->dev_ver < DEV_VER_V2) {
+ request = cdns3_wa2_gadget_giveback(priv_dev, priv_ep,
+ priv_req);
+ if (!request)
+ return;
+ }
+
+ if (request->complete) {
+ spin_unlock(&priv_dev->lock);
+ usb_gadget_giveback_request(&priv_ep->endpoint,
+ request);
+ spin_lock(&priv_dev->lock);
+ }
+
+ if (request->buf == priv_dev->zlp_buf)
+ cdns3_gadget_ep_free_request(&priv_ep->endpoint, request);
+}
+
+void cdns3_wa1_restore_cycle_bit(struct cdns3_endpoint *priv_ep)
+{
+ /* Work around for stale data address in TRB*/
+ if (priv_ep->wa1_set) {
+ trace_cdns3_wa1(priv_ep, "restore cycle bit");
+
+ priv_ep->wa1_set = 0;
+ priv_ep->wa1_trb_index = 0xFFFF;
+ if (priv_ep->wa1_cycle_bit) {
+ priv_ep->wa1_trb->control =
+ priv_ep->wa1_trb->control | 0x1;
+ } else {
+ priv_ep->wa1_trb->control =
+ priv_ep->wa1_trb->control & ~0x1;
+ }
+ }
+}
+
+static void cdns3_free_aligned_request_buf(struct work_struct *work)
+{
+ struct cdns3_device *priv_dev = container_of(work, struct cdns3_device,
+ aligned_buf_wq);
+ struct cdns3_aligned_buf *buf, *tmp;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv_dev->lock, flags);
+
+ list_for_each_entry_safe(buf, tmp, &priv_dev->aligned_buf_list, list) {
+ if (!buf->in_use) {
+ list_del(&buf->list);
+
+ /*
+ * Re-enable interrupts to free DMA capable memory.
+ * Driver can't free this memory with disabled
+ * interrupts.
+ */
+ spin_unlock_irqrestore(&priv_dev->lock, flags);
+ dma_free_coherent(priv_dev->sysdev, buf->size,
+ buf->buf, buf->dma);
+ kfree(buf);
+ spin_lock_irqsave(&priv_dev->lock, flags);
+ }
+ }
+
+ spin_unlock_irqrestore(&priv_dev->lock, flags);
+}
+
+static int cdns3_prepare_aligned_request_buf(struct cdns3_request *priv_req)
+{
+ struct cdns3_endpoint *priv_ep = priv_req->priv_ep;
+ struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+ struct cdns3_aligned_buf *buf;
+
+ /* check if buffer is aligned to 8. */
+ if (!((uintptr_t)priv_req->request.buf & 0x7))
+ return 0;
+
+ buf = priv_req->aligned_buf;
+
+ if (!buf || priv_req->request.length > buf->size) {
+ buf = kzalloc(sizeof(*buf), GFP_ATOMIC);
+ if (!buf)
+ return -ENOMEM;
+
+ buf->size = priv_req->request.length;
+
+ buf->buf = dma_alloc_coherent(priv_dev->sysdev,
+ buf->size,
+ &buf->dma,
+ GFP_ATOMIC);
+ if (!buf->buf) {
+ kfree(buf);
+ return -ENOMEM;
+ }
+
+ if (priv_req->aligned_buf) {
+ trace_cdns3_free_aligned_request(priv_req);
+ priv_req->aligned_buf->in_use = 0;
+ queue_work(system_freezable_wq,
+ &priv_dev->aligned_buf_wq);
+ }
+
+ buf->in_use = 1;
+ priv_req->aligned_buf = buf;
+
+ list_add_tail(&buf->list,
+ &priv_dev->aligned_buf_list);
+ }
+
+ if (priv_ep->dir == USB_DIR_IN) {
+ memcpy(buf->buf, priv_req->request.buf,
+ priv_req->request.length);
+ }
+
+ priv_req->flags |= REQUEST_UNALIGNED;
+ trace_cdns3_prepare_aligned_request(priv_req);
+
+ return 0;
+}
+
+static int cdns3_wa1_update_guard(struct cdns3_endpoint *priv_ep,
+ struct cdns3_trb *trb)
+{
+ struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+
+ if (!priv_ep->wa1_set) {
+ u32 doorbell;
+
+ doorbell = !!(readl(&priv_dev->regs->ep_cmd) & EP_CMD_DRDY);
+
+ if (doorbell) {
+ priv_ep->wa1_cycle_bit = priv_ep->pcs ? TRB_CYCLE : 0;
+ priv_ep->wa1_set = 1;
+ priv_ep->wa1_trb = trb;
+ priv_ep->wa1_trb_index = priv_ep->enqueue;
+ trace_cdns3_wa1(priv_ep, "set guard");
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static void cdns3_wa1_tray_restore_cycle_bit(struct cdns3_device *priv_dev,
+ struct cdns3_endpoint *priv_ep)
+{
+ int dma_index;
+ u32 doorbell;
+
+ doorbell = !!(readl(&priv_dev->regs->ep_cmd) & EP_CMD_DRDY);
+ dma_index = cdns3_get_dma_pos(priv_dev, priv_ep);
+
+ if (!doorbell || dma_index != priv_ep->wa1_trb_index)
+ cdns3_wa1_restore_cycle_bit(priv_ep);
+}
+
+/**
+ * cdns3_ep_run_transfer - start transfer on no-default endpoint hardware
+ * @priv_ep: endpoint object
+ *
+ * Returns zero on success or negative value on failure
+ */
+int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
+ struct usb_request *request)
+{
+ struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+ struct cdns3_request *priv_req;
+ struct cdns3_trb *trb;
+ dma_addr_t trb_dma;
+ u32 togle_pcs = 1;
+ int sg_iter = 0;
+ int num_trb;
+ int address;
+ u32 control;
+ int pcs;
+
+ if (priv_ep->type == USB_ENDPOINT_XFER_ISOC)
+ num_trb = priv_ep->interval;
+ else
+ num_trb = request->num_sgs ? request->num_sgs : 1;
+
+ if (num_trb > priv_ep->free_trbs) {
+ priv_ep->flags |= EP_RING_FULL;
+ return -ENOBUFS;
+ }
+
+ priv_req = to_cdns3_request(request);
+ address = priv_ep->endpoint.desc->bEndpointAddress;
+
+ priv_ep->flags |= EP_PENDING_REQUEST;
+
+ /* must allocate buffer aligned to 8 */
+ if (priv_req->flags & REQUEST_UNALIGNED)
+ trb_dma = priv_req->aligned_buf->dma;
+ else
+ trb_dma = request->dma;
+
+ trb = priv_ep->trb_pool + priv_ep->enqueue;
+ priv_req->start_trb = priv_ep->enqueue;
+ priv_req->trb = trb;
+
+ cdns3_select_ep(priv_ep->cdns3_dev, address);
+
+ /* prepare ring */
+ if ((priv_ep->enqueue + num_trb) >= (priv_ep->num_trbs - 1)) {
+ struct cdns3_trb *link_trb;
+ int doorbell, dma_index;
+ u32 ch_bit = 0;
+
+ doorbell = !!(readl(&priv_dev->regs->ep_cmd) & EP_CMD_DRDY);
+ dma_index = cdns3_get_dma_pos(priv_dev, priv_ep);
+
+ /* Driver can't update LINK TRB if it is current processed. */
+ if (doorbell && dma_index == priv_ep->num_trbs - 1) {
+ priv_ep->flags |= EP_DEFERRED_DRDY;
+ return -ENOBUFS;
+ }
+
+ /*updating C bt in Link TRB before starting DMA*/
+ link_trb = priv_ep->trb_pool + (priv_ep->num_trbs - 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 (priv_ep->type == USB_ENDPOINT_XFER_ISOC ||
+ TRBS_PER_SEGMENT > 2)
+ ch_bit = TRB_CHAIN;
+
+ link_trb->control = ((priv_ep->pcs) ? TRB_CYCLE : 0) |
+ TRB_TYPE(TRB_LINK) | TRB_TOGGLE | ch_bit;
+ }
+
+ if (priv_dev->dev_ver <= DEV_VER_V2)
+ togle_pcs = cdns3_wa1_update_guard(priv_ep, trb);
+
+ /* set incorrect Cycle Bit for first trb*/
+ control = priv_ep->pcs ? 0 : TRB_CYCLE;
+
+ do {
+ u32 length;
+ u16 td_size = 0;
+
+ /* fill TRB */
+ control |= TRB_TYPE(TRB_NORMAL);
+ trb->buffer = TRB_BUFFER(request->num_sgs == 0
+ ? trb_dma : request->sg[sg_iter].dma_address);
+
+ if (likely(!request->num_sgs))
+ length = request->length;
+ else
+ length = request->sg[sg_iter].length;
+
+ if (likely(priv_dev->dev_ver >= DEV_VER_V2))
+ td_size = DIV_ROUND_UP(length,
+ priv_ep->endpoint.maxpacket);
+
+ trb->length = TRB_BURST_LEN(priv_ep->trb_burst_size) |
+ TRB_LEN(length);
+ if (priv_dev->gadget.speed == USB_SPEED_SUPER)
+ trb->length |= TRB_TDL_SS_SIZE(td_size);
+ else
+ control |= TRB_TDL_HS_SIZE(td_size);
+
+ pcs = priv_ep->pcs ? TRB_CYCLE : 0;
+
+ /*
+ * first trb should be prepared as last to avoid processing
+ * transfer to early
+ */
+ if (sg_iter != 0)
+ control |= pcs;
+
+ if (priv_ep->type == USB_ENDPOINT_XFER_ISOC && !priv_ep->dir) {
+ control |= TRB_IOC | TRB_ISP;
+ } else {
+ /* for last element in TD or in SG list */
+ if (sg_iter == (num_trb - 1) && sg_iter != 0)
+ control |= pcs | TRB_IOC | TRB_ISP;
+ }
+
+ if (sg_iter)
+ trb->control = control;
+ else
+ priv_req->trb->control = control;
+
+ control = 0;
+ ++sg_iter;
+ priv_req->end_trb = priv_ep->enqueue;
+ cdns3_ep_inc_enq(priv_ep);
+ trb = priv_ep->trb_pool + priv_ep->enqueue;
+ } while (sg_iter < num_trb);
+
+ trb = priv_req->trb;
+
+ priv_req->flags |= REQUEST_PENDING;
+
+ if (sg_iter == 1)
+ trb->control |= TRB_IOC | TRB_ISP;
+
+ /*
+ * Memory barrier - cycle bit must be set before other filds in trb.
+ */
+ wmb();
+
+ /* give the TD to the consumer*/
+ if (togle_pcs)
+ trb->control = trb->control ^ 1;
+
+ if (priv_dev->dev_ver <= DEV_VER_V2)
+ cdns3_wa1_tray_restore_cycle_bit(priv_dev, priv_ep);
+
+ trace_cdns3_prepare_trb(priv_ep, priv_req->trb);
+
+ /*
+ * Memory barrier - Cycle Bit must be set before trb->length and
+ * trb->buffer fields.
+ */
+ wmb();
+
+ /*
+ * For DMULT mode we can set address to transfer ring only once after
+ * enabling endpoint.
+ */
+ if (priv_ep->flags & EP_UPDATE_EP_TRBADDR) {
+ /*
+ * Until SW is not ready to handle the OUT transfer the ISO OUT
+ * Endpoint should be disabled (EP_CFG.ENABLE = 0).
+ * EP_CFG_ENABLE must be set before updating ep_traddr.
+ */
+ if (priv_ep->type == USB_ENDPOINT_XFER_ISOC && !priv_ep->dir &&
+ !(priv_ep->flags & EP_QUIRK_ISO_OUT_EN)) {
+ priv_ep->flags |= EP_QUIRK_ISO_OUT_EN;
+ cdns3_set_register_bit(&priv_dev->regs->ep_cfg,
+ EP_CFG_ENABLE);
+ }
+
+ writel(EP_TRADDR_TRADDR(priv_ep->trb_pool_dma +
+ priv_req->start_trb * TRB_SIZE),
+ &priv_dev->regs->ep_traddr);
+
+ priv_ep->flags &= ~EP_UPDATE_EP_TRBADDR;
+ }
+
+ if (!priv_ep->wa1_set && !(priv_ep->flags & EP_STALLED)) {
+ trace_cdns3_ring(priv_ep);
+ /*clearing TRBERR and EP_STS_DESCMIS before seting DRDY*/
+ writel(EP_STS_TRBERR | EP_STS_DESCMIS, &priv_dev->regs->ep_sts);
+ writel(EP_CMD_DRDY, &priv_dev->regs->ep_cmd);
+ trace_cdns3_doorbell_epx(priv_ep->name,
+ readl(&priv_dev->regs->ep_traddr));
+ }
+
+ /* WORKAROUND for transition to L0 */
+ __cdns3_gadget_wakeup(priv_dev);
+
+ return 0;
+}
+
+void cdns3_set_hw_configuration(struct cdns3_device *priv_dev)
+{
+ struct cdns3_endpoint *priv_ep;
+ struct usb_ep *ep;
+ int val;
+
+ if (priv_dev->hw_configured_flag)
+ return;
+
+ writel(USB_CONF_CFGSET, &priv_dev->regs->usb_conf);
+ writel(EP_CMD_ERDY | EP_CMD_REQ_CMPL, &priv_dev->regs->ep_cmd);
+
+ cdns3_set_register_bit(&priv_dev->regs->usb_conf,
+ USB_CONF_U1EN | USB_CONF_U2EN);
+
+ /* wait until configuration set */
+ readl_poll_timeout_atomic(&priv_dev->regs->usb_sts, val,
+ val & USB_STS_CFGSTS_MASK, 1, 100);
+
+ priv_dev->hw_configured_flag = 1;
+
+ list_for_each_entry(ep, &priv_dev->gadget.ep_list, ep_list) {
+ if (ep->enabled) {
+ priv_ep = ep_to_cdns3_ep(ep);
+ cdns3_start_all_request(priv_dev, priv_ep);
+ }
+ }
+}
+
+/**
+ * cdns3_request_handled - check whether request has been handled by DMA
+ *
+ * @priv_ep: extended endpoint object.
+ * @priv_req: request object for checking
+ *
+ * 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 = priv_ep->dequeue - dequeue position
+ * EQ = priv_ep->enqueue - enqueue position
+ * ST = priv_req->start_trb - index of first TRB in transfer ring
+ * ET = priv_req->end_trb - index of last TRB in transfer ring
+ * CI = current_index - index of processed TRB by DMA.
+ *
+ * As first step, function checks if cycle bit for priv_req->start_trb is
+ * correct.
+ *
+ * some rules:
+ * 1. priv_ep->dequeue never exceed current_index.
+ * 2 priv_ep->enqueue never exceed priv_ep->dequeue
+ * 3. exception: priv_ep->enqueue == priv_ep->dequeue
+ * and priv_ep->free_trbs is zero.
+ * This case indicate that TR is full.
+ *
+ * Then We can split recognition into two parts:
+ * Case 1 - priv_ep->dequeue < current_index
+ * SR ... EQ ... DQ ... CI ... ER
+ * SR ... DQ ... CI ... EQ ... ER
+ *
+ * Request has been handled by DMA if ST and ET is between DQ and CI.
+ *
+ * Case 2 - priv_ep->dequeue > current_index
+ * This situation take place when CI go through the LINK TRB at the end of
+ * transfer ring.
+ * SR ... CI ... EQ ... DQ ... ER
+ *
+ * Request has been handled by DMA if ET is less then CI or
+ * ET is greater or equal DQ.
+ */
+static bool cdns3_request_handled(struct cdns3_endpoint *priv_ep,
+ struct cdns3_request *priv_req)
+{
+ struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+ struct cdns3_trb *trb = priv_req->trb;
+ int current_index = 0;
+ int handled = 0;
+ int doorbell;
+
+ current_index = cdns3_get_dma_pos(priv_dev, priv_ep);
+ doorbell = !!(readl(&priv_dev->regs->ep_cmd) & EP_CMD_DRDY);
+
+ trb = &priv_ep->trb_pool[priv_req->start_trb];
+
+ if ((trb->control & TRB_CYCLE) != priv_ep->ccs)
+ goto finish;
+
+ if (doorbell == 1 && current_index == priv_ep->dequeue)
+ goto finish;
+
+ /* The corner case for TRBS_PER_SEGMENT equal 2). */
+ if (TRBS_PER_SEGMENT == 2 && priv_ep->type != USB_ENDPOINT_XFER_ISOC) {
+ handled = 1;
+ goto finish;
+ }
+
+ if (priv_ep->enqueue == priv_ep->dequeue &&
+ priv_ep->free_trbs == 0) {
+ handled = 1;
+ } else if (priv_ep->dequeue < current_index) {
+ if ((current_index == (priv_ep->num_trbs - 1)) &&
+ !priv_ep->dequeue)
+ goto finish;
+
+ if (priv_req->end_trb >= priv_ep->dequeue &&
+ priv_req->end_trb < current_index)
+ handled = 1;
+ } else if (priv_ep->dequeue > current_index) {
+ if (priv_req->end_trb < current_index ||
+ priv_req->end_trb >= priv_ep->dequeue)
+ handled = 1;
+ }
+
+finish:
+ trace_cdns3_request_handled(priv_req, current_index, handled);
+
+ return handled;
+}
+
+static void cdns3_transfer_completed(struct cdns3_device *priv_dev,
+ struct cdns3_endpoint *priv_ep)
+{
+ struct cdns3_request *priv_req;
+ struct usb_request *request;
+ struct cdns3_trb *trb;
+
+ while (!list_empty(&priv_ep->pending_req_list)) {
+ request = cdns3_next_request(&priv_ep->pending_req_list);
+ priv_req = to_cdns3_request(request);
+
+ /* Re-select endpoint. It could be changed by other CPU during
+ * handling usb_gadget_giveback_request.
+ */
+ cdns3_select_ep(priv_dev, priv_ep->endpoint.address);
+
+ if (!cdns3_request_handled(priv_ep, priv_req))
+ goto prepare_next_td;
+
+ trb = priv_ep->trb_pool + priv_ep->dequeue;
+ trace_cdns3_complete_trb(priv_ep, trb);
+
+ if (trb != priv_req->trb)
+ dev_warn(priv_dev->dev,
+ "request_trb=0x%p, queue_trb=0x%p\n",
+ priv_req->trb, trb);
+
+ request->actual = TRB_LEN(le32_to_cpu(trb->length));
+ cdns3_move_deq_to_next_trb(priv_req);
+ cdns3_gadget_giveback(priv_ep, priv_req, 0);
+
+ if (priv_ep->type != USB_ENDPOINT_XFER_ISOC &&
+ TRBS_PER_SEGMENT == 2)
+ break;
+ }
+ priv_ep->flags &= ~EP_PENDING_REQUEST;
+
+prepare_next_td:
+ if (!(priv_ep->flags & EP_STALLED) &&
+ !(priv_ep->flags & EP_STALL_PENDING))
+ cdns3_start_all_request(priv_dev, priv_ep);
+}
+
+void cdns3_rearm_transfer(struct cdns3_endpoint *priv_ep, u8 rearm)
+{
+ struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+
+ cdns3_wa1_restore_cycle_bit(priv_ep);
+
+ if (rearm) {
+ trace_cdns3_ring(priv_ep);
+
+ /* Cycle Bit must be updated before arming DMA. */
+ wmb();
+ writel(EP_CMD_DRDY, &priv_dev->regs->ep_cmd);
+
+ __cdns3_gadget_wakeup(priv_dev);
+
+ trace_cdns3_doorbell_epx(priv_ep->name,
+ readl(&priv_dev->regs->ep_traddr));
+ }
+}
+
+/**
+ * cdns3_check_ep_interrupt_proceed - Processes interrupt related to endpoint
+ * @priv_ep: endpoint object
+ *
+ * Returns 0
+ */
+static int cdns3_check_ep_interrupt_proceed(struct cdns3_endpoint *priv_ep)
+{
+ struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+ u32 ep_sts_reg;
+
+ cdns3_select_ep(priv_dev, priv_ep->endpoint.address);
+
+ trace_cdns3_epx_irq(priv_dev, priv_ep);
+
+ ep_sts_reg = readl(&priv_dev->regs->ep_sts);
+ writel(ep_sts_reg, &priv_dev->regs->ep_sts);
+
+ if (ep_sts_reg & EP_STS_TRBERR) {
+ if (priv_ep->flags & EP_STALL_PENDING &&
+ !(ep_sts_reg & EP_STS_DESCMIS &&
+ priv_dev->dev_ver < DEV_VER_V2)) {
+ cdns3_ep_stall_flush(priv_ep);
+ }
+
+ /*
+ * 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 (priv_ep->type == USB_ENDPOINT_XFER_ISOC &&
+ !priv_ep->wa1_set) {
+ if (!priv_ep->dir) {
+ u32 ep_cfg = readl(&priv_dev->regs->ep_cfg);
+
+ ep_cfg &= ~EP_CFG_ENABLE;
+ writel(ep_cfg, &priv_dev->regs->ep_cfg);
+ priv_ep->flags &= ~EP_QUIRK_ISO_OUT_EN;
+ }
+ cdns3_transfer_completed(priv_dev, priv_ep);
+ } else if (!(priv_ep->flags & EP_STALLED) &&
+ !(priv_ep->flags & EP_STALL_PENDING)) {
+ if (priv_ep->flags & EP_DEFERRED_DRDY) {
+ priv_ep->flags &= ~EP_DEFERRED_DRDY;
+ cdns3_start_all_request(priv_dev, priv_ep);
+ } else {
+ cdns3_rearm_transfer(priv_ep,
+ priv_ep->wa1_set);
+ }
+ }
+ }
+
+ if ((ep_sts_reg & EP_STS_IOC) || (ep_sts_reg & EP_STS_ISP)) {
+ if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN) {
+ if (ep_sts_reg & EP_STS_ISP)
+ priv_ep->flags |= EP_QUIRK_END_TRANSFER;
+ else
+ priv_ep->flags &= ~EP_QUIRK_END_TRANSFER;
+ }
+
+ cdns3_transfer_completed(priv_dev, priv_ep);
+ }
+
+ /*
+ * WA2: this condition should only be meet when
+ * priv_ep->flags & EP_QUIRK_EXTRA_BUF_DET or
+ * priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN.
+ * In other cases this interrupt will be disabled/
+ */
+ if (ep_sts_reg & EP_STS_DESCMIS && priv_dev->dev_ver < DEV_VER_V2 &&
+ !(priv_ep->flags & EP_STALLED))
+ cdns3_wa2_descmissing_packet(priv_ep);
+
+ return 0;
+}
+
+static void cdns3_disconnect_gadget(struct cdns3_device *priv_dev)
+{
+ if (priv_dev->gadget_driver && priv_dev->gadget_driver->disconnect) {
+ spin_unlock(&priv_dev->lock);
+ priv_dev->gadget_driver->disconnect(&priv_dev->gadget);
+ spin_lock(&priv_dev->lock);
+ }
+}
+
+/**
+ * cdns3_check_usb_interrupt_proceed - Processes interrupt related to device
+ * @priv_dev: extended gadget object
+ * @usb_ists: bitmap representation of device's reported interrupts
+ * (usb_ists register value)
+ */
+static void cdns3_check_usb_interrupt_proceed(struct cdns3_device *priv_dev,
+ u32 usb_ists)
+{
+ int speed = 0;
+
+ trace_cdns3_usb_irq(priv_dev, usb_ists);
+ if (usb_ists & USB_ISTS_L1ENTI) {
+ /*
+ * WORKAROUND: CDNS3 controller has issue with hardware resuming
+ * from L1. To fix it, if any DMA transfer is pending driver
+ * must starts driving resume signal immediately.
+ */
+ if (readl(&priv_dev->regs->drbl))
+ __cdns3_gadget_wakeup(priv_dev);
+ }
+
+ /* Connection detected */
+ if (usb_ists & (USB_ISTS_CON2I | USB_ISTS_CONI)) {
+ speed = cdns3_get_speed(priv_dev);
+ priv_dev->gadget.speed = speed;
+ usb_gadget_set_state(&priv_dev->gadget, USB_STATE_POWERED);
+ cdns3_ep0_config(priv_dev);
+ }
+
+ /* Disconnection detected */
+ if (usb_ists & (USB_ISTS_DIS2I | USB_ISTS_DISI)) {
+ cdns3_disconnect_gadget(priv_dev);
+ priv_dev->gadget.speed = USB_SPEED_UNKNOWN;
+ usb_gadget_set_state(&priv_dev->gadget, USB_STATE_NOTATTACHED);
+ cdns3_hw_reset_eps_config(priv_dev);
+ }
+
+ if (usb_ists & (USB_ISTS_L2ENTI | USB_ISTS_U3ENTI)) {
+ if (priv_dev->gadget_driver &&
+ priv_dev->gadget_driver->suspend) {
+ spin_unlock(&priv_dev->lock);
+ priv_dev->gadget_driver->suspend(&priv_dev->gadget);
+ spin_lock(&priv_dev->lock);
+ }
+ }
+
+ if (usb_ists & (USB_ISTS_L2EXTI | USB_ISTS_U3EXTI)) {
+ if (priv_dev->gadget_driver &&
+ priv_dev->gadget_driver->resume) {
+ spin_unlock(&priv_dev->lock);
+ priv_dev->gadget_driver->resume(&priv_dev->gadget);
+ spin_lock(&priv_dev->lock);
+ }
+ }
+
+ /* reset*/
+ if (usb_ists & (USB_ISTS_UWRESI | USB_ISTS_UHRESI | USB_ISTS_U2RESI)) {
+ if (priv_dev->gadget_driver) {
+ spin_unlock(&priv_dev->lock);
+ usb_gadget_udc_reset(&priv_dev->gadget,
+ priv_dev->gadget_driver);
+ spin_lock(&priv_dev->lock);
+
+ /*read again to check the actual speed*/
+ speed = cdns3_get_speed(priv_dev);
+ priv_dev->gadget.speed = speed;
+ cdns3_hw_reset_eps_config(priv_dev);
+ cdns3_ep0_config(priv_dev);
+ }
+ }
+}
+
+/**
+ * cdns3_device_irq_handler- interrupt handler for device part of controller
+ *
+ * @irq: irq number for cdns3 core device
+ * @data: structure of cdns3
+ *
+ * Returns IRQ_HANDLED or IRQ_NONE
+ */
+static irqreturn_t cdns3_device_irq_handler(int irq, void *data)
+{
+ struct cdns3_device *priv_dev;
+ struct cdns3 *cdns = data;
+ irqreturn_t ret = IRQ_NONE;
+ u32 reg;
+
+ priv_dev = cdns->gadget_dev;
+
+ /* check USB device interrupt */
+ reg = readl(&priv_dev->regs->usb_ists);
+ if (reg) {
+ /* After masking interrupts the new interrupts won't be
+ * reported in usb_ists/ep_ists. In order to not lose some
+ * of them driver disables only detected interrupts.
+ * They will be enabled ASAP after clearing source of
+ * interrupt. This an unusual behavior only applies to
+ * usb_ists register.
+ */
+ reg = ~reg & readl(&priv_dev->regs->usb_ien);
+ /* mask deferred interrupt. */
+ writel(reg, &priv_dev->regs->usb_ien);
+ ret = IRQ_WAKE_THREAD;
+ }
+
+ /* check endpoint interrupt */
+ reg = readl(&priv_dev->regs->ep_ists);
+ if (reg) {
+ writel(0, &priv_dev->regs->ep_ien);
+ ret = IRQ_WAKE_THREAD;
+ }
+
+ return ret;
+}
+
+/**
+ * cdns3_device_thread_irq_handler- interrupt handler for device part
+ * of controller
+ *
+ * @irq: irq number for cdns3 core device
+ * @data: structure of cdns3
+ *
+ * Returns IRQ_HANDLED or IRQ_NONE
+ */
+static irqreturn_t cdns3_device_thread_irq_handler(int irq, void *data)
+{
+ struct cdns3_device *priv_dev;
+ struct cdns3 *cdns = data;
+ irqreturn_t ret = IRQ_NONE;
+ unsigned long flags;
+ int bit;
+ u32 reg;
+
+ priv_dev = cdns->gadget_dev;
+ spin_lock_irqsave(&priv_dev->lock, flags);
+
+ reg = readl(&priv_dev->regs->usb_ists);
+ if (reg) {
+ writel(reg, &priv_dev->regs->usb_ists);
+ writel(USB_IEN_INIT, &priv_dev->regs->usb_ien);
+ cdns3_check_usb_interrupt_proceed(priv_dev, reg);
+ ret = IRQ_HANDLED;
+ }
+
+ reg = readl(&priv_dev->regs->ep_ists);
+
+ /* handle default endpoint OUT */
+ if (reg & EP_ISTS_EP_OUT0) {
+ cdns3_check_ep0_interrupt_proceed(priv_dev, USB_DIR_OUT);
+ ret = IRQ_HANDLED;
+ }
+
+ /* handle default endpoint IN */
+ if (reg & EP_ISTS_EP_IN0) {
+ cdns3_check_ep0_interrupt_proceed(priv_dev, USB_DIR_IN);
+ ret = IRQ_HANDLED;
+ }
+
+ /* check if interrupt from non default endpoint, if no exit */
+ reg &= ~(EP_ISTS_EP_OUT0 | EP_ISTS_EP_IN0);
+ if (!reg)
+ goto irqend;
+
+ for_each_set_bit(bit, (unsigned long *)&reg,
+ sizeof(u32) * BITS_PER_BYTE) {
+ cdns3_check_ep_interrupt_proceed(priv_dev->eps[bit]);
+ ret = IRQ_HANDLED;
+ }
+
+irqend:
+ writel(~0, &priv_dev->regs->ep_ien);
+ spin_unlock_irqrestore(&priv_dev->lock, flags);
+
+ return ret;
+}
+
+/**
+ * cdns3_ep_onchip_buffer_reserve - Try to reserve onchip buf for EP
+ *
+ * The real reservation will occur during write to EP_CFG register,
+ * this function is used to check if the 'size' reservation is allowed.
+ *
+ * @priv_dev: extended gadget object
+ * @size: the size (KB) for EP would like to allocate
+ * @is_in: endpoint direction
+ *
+ * Return 0 if the required size can met or negative value on failure
+ */
+static int cdns3_ep_onchip_buffer_reserve(struct cdns3_device *priv_dev,
+ int size, int is_in)
+{
+ int remained;
+
+ /* 2KB are reserved for EP0*/
+ remained = priv_dev->onchip_buffers - priv_dev->onchip_used_size - 2;
+
+ if (is_in) {
+ if (remained < size)
+ return -EPERM;
+
+ priv_dev->onchip_used_size += size;
+ } else {
+ int required;
+
+ /**
+ * ALL OUT EPs are shared the same chunk onchip memory, so
+ * driver checks if it already has assigned enough buffers
+ */
+ if (priv_dev->out_mem_is_allocated >= size)
+ return 0;
+
+ required = size - priv_dev->out_mem_is_allocated;
+
+ if (required > remained)
+ return -EPERM;
+
+ priv_dev->out_mem_is_allocated += required;
+ priv_dev->onchip_used_size += required;
+ }
+
+ return 0;
+}
+
+void cdns3_configure_dmult(struct cdns3_device *priv_dev,
+ struct cdns3_endpoint *priv_ep)
+{
+ struct cdns3_usb_regs __iomem *regs = priv_dev->regs;
+
+ /* For dev_ver > DEV_VER_V2 DMULT is configured per endpoint */
+ if (priv_dev->dev_ver <= DEV_VER_V2)
+ writel(USB_CONF_DMULT, &regs->usb_conf);
+
+ if (priv_dev->dev_ver == DEV_VER_V2)
+ writel(USB_CONF2_EN_TDL_TRB, &regs->usb_conf2);
+
+ if (priv_dev->dev_ver >= DEV_VER_V3 && priv_ep) {
+ u32 mask;
+
+ if (priv_ep->dir)
+ mask = BIT(priv_ep->num + 16);
+ else
+ mask = BIT(priv_ep->num);
+
+ if (priv_ep->type != USB_ENDPOINT_XFER_ISOC) {
+ cdns3_set_register_bit(&regs->tdl_from_trb, mask);
+ cdns3_set_register_bit(&regs->tdl_beh, mask);
+ cdns3_set_register_bit(&regs->tdl_beh2, mask);
+ cdns3_set_register_bit(&regs->dma_adv_td, mask);
+ }
+
+ if (priv_ep->type == USB_ENDPOINT_XFER_ISOC && !priv_ep->dir)
+ cdns3_set_register_bit(&regs->tdl_from_trb, mask);
+
+ cdns3_set_register_bit(&regs->dtrans, mask);
+ }
+}
+
+/**
+ * cdns3_ep_config Configure hardware endpoint
+ * @priv_ep: extended endpoint object
+ */
+void cdns3_ep_config(struct cdns3_endpoint *priv_ep)
+{
+ bool is_iso_ep = (priv_ep->type == USB_ENDPOINT_XFER_ISOC);
+ struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+ u32 bEndpointAddress = priv_ep->num | priv_ep->dir;
+ u32 max_packet_size = 0;
+ u8 maxburst = 0;
+ u32 ep_cfg = 0;
+ u8 buffering;
+ u8 mult = 0;
+ int ret;
+
+ buffering = CDNS3_EP_BUF_SIZE - 1;
+
+ cdns3_configure_dmult(priv_dev, priv_ep);
+
+ switch (priv_ep->type) {
+ case USB_ENDPOINT_XFER_INT:
+ ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_INT);
+
+ if ((priv_dev->dev_ver == DEV_VER_V2 && !priv_ep->dir) ||
+ priv_dev->dev_ver > DEV_VER_V2)
+ ep_cfg |= EP_CFG_TDL_CHK;
+ break;
+ case USB_ENDPOINT_XFER_BULK:
+ ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_BULK);
+
+ if ((priv_dev->dev_ver == DEV_VER_V2 && !priv_ep->dir) ||
+ priv_dev->dev_ver > DEV_VER_V2)
+ ep_cfg |= EP_CFG_TDL_CHK;
+ break;
+ default:
+ ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_ISOC);
+ mult = CDNS3_EP_ISO_HS_MULT - 1;
+ buffering = mult + 1;
+ }
+
+ switch (priv_dev->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;
+ case USB_SPEED_SUPER:
+ /* It's limitation that driver assumes in driver. */
+ mult = 0;
+ max_packet_size = 1024;
+ if (priv_ep->type == USB_ENDPOINT_XFER_ISOC) {
+ maxburst = CDNS3_EP_ISO_SS_BURST - 1;
+ buffering = (mult + 1) *
+ (maxburst + 1);
+
+ if (priv_ep->interval > 1)
+ buffering++;
+ } else {
+ maxburst = CDNS3_EP_BUF_SIZE - 1;
+ }
+ break;
+ default:
+ /* all other speed are not supported */
+ return;
+ }
+
+ if (max_packet_size == 1024)
+ priv_ep->trb_burst_size = 128;
+ else if (max_packet_size >= 512)
+ priv_ep->trb_burst_size = 64;
+ else
+ priv_ep->trb_burst_size = 16;
+
+ ret = cdns3_ep_onchip_buffer_reserve(priv_dev, buffering + 1,
+ !!priv_ep->dir);
+ if (ret) {
+ dev_err(priv_dev->dev, "onchip mem is full, ep is invalid\n");
+ return;
+ }
+
+ ep_cfg |= EP_CFG_MAXPKTSIZE(max_packet_size) |
+ EP_CFG_MULT(mult) |
+ EP_CFG_BUFFERING(buffering) |
+ EP_CFG_MAXBURST(maxburst);
+
+ cdns3_select_ep(priv_dev, bEndpointAddress);
+ writel(ep_cfg, &priv_dev->regs->ep_cfg);
+
+ dev_dbg(priv_dev->dev, "Configure %s: with val %08x\n",
+ priv_ep->name, ep_cfg);
+}
+
+/* Find correct direction for HW endpoint according to description */
+static int cdns3_ep_dir_is_correct(struct usb_endpoint_descriptor *desc,
+ struct cdns3_endpoint *priv_ep)
+{
+ return (priv_ep->endpoint.caps.dir_in && usb_endpoint_dir_in(desc)) ||
+ (priv_ep->endpoint.caps.dir_out && usb_endpoint_dir_out(desc));
+}
+
+static struct
+cdns3_endpoint *cdns3_find_available_ep(struct cdns3_device *priv_dev,
+ struct usb_endpoint_descriptor *desc)
+{
+ struct usb_ep *ep;
+ struct cdns3_endpoint *priv_ep;
+
+ list_for_each_entry(ep, &priv_dev->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);
+
+ priv_ep = ep_to_cdns3_ep(ep);
+ if (cdns3_ep_dir_is_correct(desc, priv_ep)) {
+ if (!(priv_ep->flags & EP_CLAIMED)) {
+ priv_ep->num = num;
+ return priv_ep;
+ }
+ }
+ }
+
+ return ERR_PTR(-ENOENT);
+}
+
+/*
+ * Cadence IP has one limitation that all endpoints must be configured
+ * (Type & MaxPacketSize) before setting configuration through hardware
+ * register, it means we can't change endpoints configuration after
+ * set_configuration.
+ *
+ * This function set EP_CLAIMED flag which is added when the gadget driver
+ * uses usb_ep_autoconfig to configure specific endpoint;
+ * When the udc driver receives set_configurion request,
+ * it goes through all claimed endpoints, and configure all endpoints
+ * accordingly.
+ *
+ * At usb_ep_ops.enable/disable, we only enable and disable endpoint through
+ * ep_cfg register which can be changed after set_configuration, and do
+ * some software operation accordingly.
+ */
+static struct
+usb_ep *cdns3_gadget_match_ep(struct usb_gadget *gadget,
+ struct usb_endpoint_descriptor *desc,
+ struct usb_ss_ep_comp_descriptor *comp_desc)
+{
+ struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
+ struct cdns3_endpoint *priv_ep;
+ unsigned long flags;
+
+ priv_ep = cdns3_find_available_ep(priv_dev, desc);
+ if (IS_ERR(priv_ep)) {
+ dev_err(priv_dev->dev, "no available ep\n");
+ return NULL;
+ }
+
+ dev_dbg(priv_dev->dev, "match endpoint: %s\n", priv_ep->name);
+
+ spin_lock_irqsave(&priv_dev->lock, flags);
+ priv_ep->endpoint.desc = desc;
+ priv_ep->dir = usb_endpoint_dir_in(desc) ? USB_DIR_IN : USB_DIR_OUT;
+ priv_ep->type = usb_endpoint_type(desc);
+ priv_ep->flags |= EP_CLAIMED;
+ priv_ep->interval = desc->bInterval ? BIT(desc->bInterval - 1) : 0;
+
+ spin_unlock_irqrestore(&priv_dev->lock, flags);
+ return &priv_ep->endpoint;
+}
+
+/**
+ * cdns3_gadget_ep_alloc_request Allocates request
+ * @ep: endpoint object associated with request
+ * @gfp_flags: gfp flags
+ *
+ * Returns allocated request address, NULL on allocation error
+ */
+struct usb_request *cdns3_gadget_ep_alloc_request(struct usb_ep *ep,
+ gfp_t gfp_flags)
+{
+ struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
+ struct cdns3_request *priv_req;
+
+ priv_req = kzalloc(sizeof(*priv_req), gfp_flags);
+ if (!priv_req)
+ return NULL;
+
+ priv_req->priv_ep = priv_ep;
+
+ trace_cdns3_alloc_request(priv_req);
+ return &priv_req->request;
+}
+
+/**
+ * cdns3_gadget_ep_free_request Free memory occupied by request
+ * @ep: endpoint object associated with request
+ * @request: request to free memory
+ */
+void cdns3_gadget_ep_free_request(struct usb_ep *ep,
+ struct usb_request *request)
+{
+ struct cdns3_request *priv_req = to_cdns3_request(request);
+
+ if (priv_req->aligned_buf)
+ priv_req->aligned_buf->in_use = 0;
+
+ trace_cdns3_free_request(priv_req);
+ kfree(priv_req);
+}
+
+/**
+ * cdns3_gadget_ep_enable Enable endpoint
+ * @ep: endpoint object
+ * @desc: endpoint descriptor
+ *
+ * Returns 0 on success, error code elsewhere
+ */
+static int cdns3_gadget_ep_enable(struct usb_ep *ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ struct cdns3_endpoint *priv_ep;
+ struct cdns3_device *priv_dev;
+ u32 reg = EP_STS_EN_TRBERREN;
+ u32 bEndpointAddress;
+ unsigned long flags;
+ int enable = 1;
+ int ret;
+ int val;
+
+ priv_ep = ep_to_cdns3_ep(ep);
+ priv_dev = priv_ep->cdns3_dev;
+
+ if (!ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT) {
+ dev_dbg(priv_dev->dev, "usbss: invalid parameters\n");
+ return -EINVAL;
+ }
+
+ if (!desc->wMaxPacketSize) {
+ dev_err(priv_dev->dev, "usbss: missing wMaxPacketSize\n");
+ return -EINVAL;
+ }
+
+ if (dev_WARN_ONCE(priv_dev->dev, priv_ep->flags & EP_ENABLED,
+ "%s is already enabled\n", priv_ep->name))
+ return 0;
+
+ spin_lock_irqsave(&priv_dev->lock, flags);
+
+ priv_ep->endpoint.desc = desc;
+ priv_ep->type = usb_endpoint_type(desc);
+ priv_ep->interval = desc->bInterval ? BIT(desc->bInterval - 1) : 0;
+
+ if (priv_ep->interval > ISO_MAX_INTERVAL &&
+ priv_ep->type == USB_ENDPOINT_XFER_ISOC) {
+ dev_err(priv_dev->dev, "Driver is limited to %d period\n",
+ ISO_MAX_INTERVAL);
+
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ ret = cdns3_allocate_trb_pool(priv_ep);
+
+ if (ret)
+ goto exit;
+
+ bEndpointAddress = priv_ep->num | priv_ep->dir;
+ cdns3_select_ep(priv_dev, bEndpointAddress);
+
+ trace_cdns3_gadget_ep_enable(priv_ep);
+
+ writel(EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
+
+ ret = readl_poll_timeout_atomic(&priv_dev->regs->ep_cmd, val,
+ !(val & (EP_CMD_CSTALL | EP_CMD_EPRST)),
+ 1, 1000);
+
+ if (unlikely(ret)) {
+ cdns3_free_trb_pool(priv_ep);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ /* enable interrupt for selected endpoint */
+ cdns3_set_register_bit(&priv_dev->regs->ep_ien,
+ BIT(cdns3_ep_addr_to_index(bEndpointAddress)));
+
+ if (priv_dev->dev_ver < DEV_VER_V2)
+ cdns3_wa2_enable_detection(priv_dev, priv_ep, reg);
+
+ writel(reg, &priv_dev->regs->ep_sts_en);
+
+ /*
+ * For some versions of controller at some point 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 (priv_ep->type == USB_ENDPOINT_XFER_ISOC && !priv_ep->dir)
+ enable = 0;
+
+ if (enable)
+ cdns3_set_register_bit(&priv_dev->regs->ep_cfg, EP_CFG_ENABLE);
+
+ ep->desc = desc;
+ priv_ep->flags &= ~(EP_PENDING_REQUEST | EP_STALLED | EP_STALL_PENDING |
+ EP_QUIRK_ISO_OUT_EN | EP_QUIRK_EXTRA_BUF_EN);
+ priv_ep->flags |= EP_ENABLED | EP_UPDATE_EP_TRBADDR;
+ priv_ep->wa1_set = 0;
+ priv_ep->enqueue = 0;
+ priv_ep->dequeue = 0;
+ reg = readl(&priv_dev->regs->ep_sts);
+ priv_ep->pcs = !!EP_STS_CCS(reg);
+ priv_ep->ccs = !!EP_STS_CCS(reg);
+ /* one TRB is reserved for link TRB used in DMULT mode*/
+ priv_ep->free_trbs = priv_ep->num_trbs - 1;
+exit:
+ spin_unlock_irqrestore(&priv_dev->lock, flags);
+
+ return ret;
+}
+
+/**
+ * cdns3_gadget_ep_disable Disable endpoint
+ * @ep: endpoint object
+ *
+ * Returns 0 on success, error code elsewhere
+ */
+static int cdns3_gadget_ep_disable(struct usb_ep *ep)
+{
+ struct cdns3_endpoint *priv_ep;
+ struct cdns3_request *priv_req;
+ struct cdns3_device *priv_dev;
+ struct usb_request *request;
+ unsigned long flags;
+ int ret = 0;
+ u32 ep_cfg;
+ int val;
+
+ if (!ep) {
+ pr_err("usbss: invalid parameters\n");
+ return -EINVAL;
+ }
+
+ priv_ep = ep_to_cdns3_ep(ep);
+ priv_dev = priv_ep->cdns3_dev;
+
+ if (dev_WARN_ONCE(priv_dev->dev, !(priv_ep->flags & EP_ENABLED),
+ "%s is already disabled\n", priv_ep->name))
+ return 0;
+
+ spin_lock_irqsave(&priv_dev->lock, flags);
+
+ trace_cdns3_gadget_ep_disable(priv_ep);
+
+ cdns3_select_ep(priv_dev, ep->desc->bEndpointAddress);
+
+ ep_cfg = readl(&priv_dev->regs->ep_cfg);
+ ep_cfg &= ~EP_CFG_ENABLE;
+ writel(ep_cfg, &priv_dev->regs->ep_cfg);
+
+ /**
+ * 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(&priv_dev->regs->ep_sts, val,
+ !(val & EP_STS_DBUSY), 1, 10);
+ writel(EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
+
+ readl_poll_timeout_atomic(&priv_dev->regs->ep_cmd, val,
+ !(val & (EP_CMD_CSTALL | EP_CMD_EPRST)),
+ 1, 1000);
+ if (unlikely(ret))
+ dev_err(priv_dev->dev, "Timeout: %s resetting failed.\n",
+ priv_ep->name);
+
+ while (!list_empty(&priv_ep->pending_req_list)) {
+ request = cdns3_next_request(&priv_ep->pending_req_list);
+
+ cdns3_gadget_giveback(priv_ep, to_cdns3_request(request),
+ -ESHUTDOWN);
+ }
+
+ while (!list_empty(&priv_ep->wa2_descmiss_req_list)) {
+ priv_req = cdns3_next_priv_request(&priv_ep->wa2_descmiss_req_list);
+
+ kfree(priv_req->request.buf);
+ cdns3_gadget_ep_free_request(&priv_ep->endpoint,
+ &priv_req->request);
+ list_del_init(&priv_req->list);
+ --priv_ep->wa2_counter;
+ }
+
+ while (!list_empty(&priv_ep->deferred_req_list)) {
+ request = cdns3_next_request(&priv_ep->deferred_req_list);
+
+ cdns3_gadget_giveback(priv_ep, to_cdns3_request(request),
+ -ESHUTDOWN);
+ }
+
+ priv_ep->descmis_req = NULL;
+
+ ep->desc = NULL;
+ priv_ep->flags &= ~EP_ENABLED;
+
+ spin_unlock_irqrestore(&priv_dev->lock, flags);
+
+ return ret;
+}
+
+/**
+ * cdns3_gadget_ep_queue Transfer data on endpoint
+ * @ep: endpoint object
+ * @request: request object
+ * @gfp_flags: gfp flags
+ *
+ * Returns 0 on success, error code elsewhere
+ */
+static int __cdns3_gadget_ep_queue(struct usb_ep *ep,
+ struct usb_request *request,
+ gfp_t gfp_flags)
+{
+ struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
+ struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+ struct cdns3_request *priv_req;
+ int ret = 0;
+
+ request->actual = 0;
+ request->status = -EINPROGRESS;
+ priv_req = to_cdns3_request(request);
+ trace_cdns3_ep_queue(priv_req);
+
+ if (priv_dev->dev_ver < DEV_VER_V2) {
+ ret = cdns3_wa2_gadget_ep_queue(priv_dev, priv_ep,
+ priv_req);
+
+ if (ret == EINPROGRESS)
+ return 0;
+ }
+
+ ret = cdns3_prepare_aligned_request_buf(priv_req);
+ if (ret < 0)
+ return ret;
+
+ ret = usb_gadget_map_request_by_dev(priv_dev->sysdev, request,
+ usb_endpoint_dir_in(ep->desc));
+ if (ret)
+ return ret;
+
+ list_add_tail(&request->list, &priv_ep->deferred_req_list);
+
+ /*
+ * If hardware endpoint configuration has not been set yet then
+ * just queue request in deferred list. Transfer will be started in
+ * cdns3_set_hw_configuration.
+ */
+ if (priv_dev->hw_configured_flag && !(priv_ep->flags & EP_STALLED) &&
+ !(priv_ep->flags & EP_STALL_PENDING))
+ cdns3_start_all_request(priv_dev, priv_ep);
+
+ return 0;
+}
+
+static int cdns3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
+ gfp_t gfp_flags)
+{
+ struct usb_request *zlp_request;
+ struct cdns3_endpoint *priv_ep;
+ struct cdns3_device *priv_dev;
+ unsigned long flags;
+ int ret;
+
+ if (!request || !ep)
+ return -EINVAL;
+
+ priv_ep = ep_to_cdns3_ep(ep);
+ priv_dev = priv_ep->cdns3_dev;
+
+ spin_lock_irqsave(&priv_dev->lock, flags);
+
+ ret = __cdns3_gadget_ep_queue(ep, request, gfp_flags);
+
+ if (ret == 0 && request->zero && request->length &&
+ (request->length % ep->maxpacket == 0)) {
+ struct cdns3_request *priv_req;
+
+ zlp_request = cdns3_gadget_ep_alloc_request(ep, GFP_ATOMIC);
+ zlp_request->buf = priv_dev->zlp_buf;
+ zlp_request->length = 0;
+
+ priv_req = to_cdns3_request(zlp_request);
+ priv_req->flags |= REQUEST_ZLP;
+
+ dev_dbg(priv_dev->dev, "Queuing ZLP for endpoint: %s\n",
+ priv_ep->name);
+ ret = __cdns3_gadget_ep_queue(ep, zlp_request, gfp_flags);
+ }
+
+ spin_unlock_irqrestore(&priv_dev->lock, flags);
+ return ret;
+}
+
+/**
+ * cdns3_gadget_ep_dequeue Remove request from transfer queue
+ * @ep: endpoint object associated with request
+ * @request: request object
+ *
+ * Returns 0 on success, error code elsewhere
+ */
+int cdns3_gadget_ep_dequeue(struct usb_ep *ep,
+ struct usb_request *request)
+{
+ struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
+ struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+ struct usb_request *req, *req_temp;
+ struct cdns3_request *priv_req;
+ struct cdns3_trb *link_trb;
+ unsigned long flags;
+ int ret = 0;
+
+ if (!ep || !request || !ep->desc)
+ return -EINVAL;
+
+ spin_lock_irqsave(&priv_dev->lock, flags);
+
+ priv_req = to_cdns3_request(request);
+
+ trace_cdns3_ep_dequeue(priv_req);
+
+ cdns3_select_ep(priv_dev, ep->desc->bEndpointAddress);
+
+ list_for_each_entry_safe(req, req_temp, &priv_ep->pending_req_list,
+ list) {
+ if (request == req)
+ goto found;
+ }
+
+ list_for_each_entry_safe(req, req_temp, &priv_ep->deferred_req_list,
+ list) {
+ if (request == req)
+ goto found;
+ }
+
+ goto not_found;
+
+found:
+
+ if (priv_ep->wa1_trb == priv_req->trb)
+ cdns3_wa1_restore_cycle_bit(priv_ep);
+
+ link_trb = priv_req->trb;
+ cdns3_move_deq_to_next_trb(priv_req);
+ cdns3_gadget_giveback(priv_ep, priv_req, -ECONNRESET);
+
+ /* Update ring */
+ request = cdns3_next_request(&priv_ep->deferred_req_list);
+ if (request) {
+ priv_req = to_cdns3_request(request);
+
+ link_trb->buffer = TRB_BUFFER(priv_ep->trb_pool_dma +
+ (priv_req->start_trb * TRB_SIZE));
+ link_trb->control = (link_trb->control & TRB_CYCLE) |
+ TRB_TYPE(TRB_LINK) | TRB_CHAIN | TRB_TOGGLE;
+ } else {
+ priv_ep->flags |= EP_UPDATE_EP_TRBADDR;
+ }
+
+not_found:
+ spin_unlock_irqrestore(&priv_dev->lock, flags);
+ return ret;
+}
+
+/**
+ * __cdns3_gadget_ep_set_halt Sets stall on selected endpoint
+ * Should be called after acquiring spin_lock and selecting ep
+ * @ep: endpoint object to set stall on.
+ */
+void __cdns3_gadget_ep_set_halt(struct cdns3_endpoint *priv_ep)
+{
+ struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+
+ trace_cdns3_halt(priv_ep, 1, 0);
+
+ if (!(priv_ep->flags & EP_STALLED)) {
+ u32 ep_sts_reg = readl(&priv_dev->regs->ep_sts);
+
+ if (!(ep_sts_reg & EP_STS_DBUSY))
+ cdns3_ep_stall_flush(priv_ep);
+ else
+ priv_ep->flags |= EP_STALL_PENDING;
+ }
+}
+
+/**
+ * __cdns3_gadget_ep_clear_halt Clears stall on selected endpoint
+ * Should be called after acquiring spin_lock and selecting ep
+ * @ep: endpoint object to clear stall on
+ */
+int __cdns3_gadget_ep_clear_halt(struct cdns3_endpoint *priv_ep)
+{
+ struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+ struct usb_request *request;
+ int ret;
+ int val;
+
+ trace_cdns3_halt(priv_ep, 0, 0);
+
+ writel(EP_CMD_CSTALL | EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
+
+ /* wait for EPRST cleared */
+ ret = readl_poll_timeout_atomic(&priv_dev->regs->ep_cmd, val,
+ !(val & EP_CMD_EPRST), 1, 100);
+ if (ret)
+ return -EINVAL;
+
+ priv_ep->flags &= ~(EP_STALLED | EP_STALL_PENDING);
+
+ request = cdns3_next_request(&priv_ep->pending_req_list);
+
+ if (request)
+ cdns3_rearm_transfer(priv_ep, 1);
+
+ cdns3_start_all_request(priv_dev, priv_ep);
+ return ret;
+}
+
+/**
+ * cdns3_gadget_ep_set_halt Sets/clears stall on selected endpoint
+ * @ep: endpoint object to set/clear stall on
+ * @value: 1 for set stall, 0 for clear stall
+ *
+ * Returns 0 on success, error code elsewhere
+ */
+int cdns3_gadget_ep_set_halt(struct usb_ep *ep, int value)
+{
+ struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
+ struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+ unsigned long flags;
+ int ret = 0;
+
+ if (!(priv_ep->flags & EP_ENABLED))
+ return -EPERM;
+
+ spin_lock_irqsave(&priv_dev->lock, flags);
+
+ cdns3_select_ep(priv_dev, ep->desc->bEndpointAddress);
+
+ if (!value) {
+ priv_ep->flags &= ~EP_WEDGE;
+ ret = __cdns3_gadget_ep_clear_halt(priv_ep);
+ } else {
+ __cdns3_gadget_ep_set_halt(priv_ep);
+ }
+
+ spin_unlock_irqrestore(&priv_dev->lock, flags);
+
+ return ret;
+}
+
+extern const struct usb_ep_ops cdns3_gadget_ep0_ops;
+
+static const struct usb_ep_ops cdns3_gadget_ep_ops = {
+ .enable = cdns3_gadget_ep_enable,
+ .disable = cdns3_gadget_ep_disable,
+ .alloc_request = cdns3_gadget_ep_alloc_request,
+ .free_request = cdns3_gadget_ep_free_request,
+ .queue = cdns3_gadget_ep_queue,
+ .dequeue = cdns3_gadget_ep_dequeue,
+ .set_halt = cdns3_gadget_ep_set_halt,
+ .set_wedge = cdns3_gadget_ep_set_wedge,
+};
+
+/**
+ * cdns3_gadget_get_frame Returns number of actual ITP frame
+ * @gadget: gadget object
+ *
+ * Returns number of actual ITP frame
+ */
+static int cdns3_gadget_get_frame(struct usb_gadget *gadget)
+{
+ struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
+
+ return readl(&priv_dev->regs->usb_itpn);
+}
+
+int __cdns3_gadget_wakeup(struct cdns3_device *priv_dev)
+{
+ enum usb_device_speed speed;
+
+ speed = cdns3_get_speed(priv_dev);
+
+ if (speed >= USB_SPEED_SUPER)
+ return 0;
+
+ /* Start driving resume signaling to indicate remote wakeup. */
+ writel(USB_CONF_LGO_L0, &priv_dev->regs->usb_conf);
+
+ return 0;
+}
+
+static int cdns3_gadget_wakeup(struct usb_gadget *gadget)
+{
+ struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&priv_dev->lock, flags);
+ ret = __cdns3_gadget_wakeup(priv_dev);
+ spin_unlock_irqrestore(&priv_dev->lock, flags);
+ return ret;
+}
+
+static int cdns3_gadget_set_selfpowered(struct usb_gadget *gadget,
+ int is_selfpowered)
+{
+ struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv_dev->lock, flags);
+ priv_dev->is_selfpowered = !!is_selfpowered;
+ spin_unlock_irqrestore(&priv_dev->lock, flags);
+ return 0;
+}
+
+static int cdns3_gadget_pullup(struct usb_gadget *gadget, int is_on)
+{
+ struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
+
+ if (is_on)
+ writel(USB_CONF_DEVEN, &priv_dev->regs->usb_conf);
+ else
+ writel(USB_CONF_DEVDS, &priv_dev->regs->usb_conf);
+
+ return 0;
+}
+
+static void cdns3_gadget_config(struct cdns3_device *priv_dev)
+{
+ struct cdns3_usb_regs __iomem *regs = priv_dev->regs;
+ u32 reg;
+
+ cdns3_ep0_config(priv_dev);
+
+ /* enable interrupts for endpoint 0 (in and out) */
+ writel(EP_IEN_EP_OUT0 | EP_IEN_EP_IN0, &regs->ep_ien);
+
+ /*
+ * Driver needs to modify LFPS minimal U1 Exit time for DEV_VER_TI_V1
+ * revision of controller.
+ */
+ if (priv_dev->dev_ver == DEV_VER_TI_V1) {
+ reg = readl(&regs->dbg_link1);
+
+ reg &= ~DBG_LINK1_LFPS_MIN_GEN_U1_EXIT_MASK;
+ reg |= DBG_LINK1_LFPS_MIN_GEN_U1_EXIT(0x55) |
+ DBG_LINK1_LFPS_MIN_GEN_U1_EXIT_SET;
+ writel(reg, &regs->dbg_link1);
+ }
+
+ /*
+ * By default some platforms has set protected access to memory.
+ * This cause problem with cache, so driver restore non-secure
+ * access to memory.
+ */
+ reg = readl(&regs->dma_axi_ctrl);
+ reg |= DMA_AXI_CTRL_MARPROT(DMA_AXI_CTRL_NON_SECURE) |
+ DMA_AXI_CTRL_MAWPROT(DMA_AXI_CTRL_NON_SECURE);
+ writel(reg, &regs->dma_axi_ctrl);
+
+ /* enable generic interrupt*/
+ writel(USB_IEN_INIT, &regs->usb_ien);
+ writel(USB_CONF_CLK2OFFDS | USB_CONF_L1DS, &regs->usb_conf);
+
+ cdns3_configure_dmult(priv_dev, NULL);
+
+ cdns3_gadget_pullup(&priv_dev->gadget, 1);
+}
+
+/**
+ * cdns3_gadget_udc_start Gadget start
+ * @gadget: gadget object
+ * @driver: driver which operates on this gadget
+ *
+ * Returns 0 on success, error code elsewhere
+ */
+static int cdns3_gadget_udc_start(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
+{
+ struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv_dev->lock, flags);
+ priv_dev->gadget_driver = driver;
+ cdns3_gadget_config(priv_dev);
+ spin_unlock_irqrestore(&priv_dev->lock, flags);
+ return 0;
+}
+
+/**
+ * cdns3_gadget_udc_stop Stops gadget
+ * @gadget: gadget object
+ *
+ * Returns 0
+ */
+static int cdns3_gadget_udc_stop(struct usb_gadget *gadget)
+{
+ struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
+ struct cdns3_endpoint *priv_ep;
+ u32 bEndpointAddress;
+ struct usb_ep *ep;
+ int ret = 0;
+ int val;
+
+ priv_dev->gadget_driver = NULL;
+
+ priv_dev->onchip_used_size = 0;
+ priv_dev->out_mem_is_allocated = 0;
+ priv_dev->gadget.speed = USB_SPEED_UNKNOWN;
+
+ list_for_each_entry(ep, &priv_dev->gadget.ep_list, ep_list) {
+ priv_ep = ep_to_cdns3_ep(ep);
+ bEndpointAddress = priv_ep->num | priv_ep->dir;
+ cdns3_select_ep(priv_dev, bEndpointAddress);
+ writel(EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
+ readl_poll_timeout_atomic(&priv_dev->regs->ep_cmd, val,
+ !(val & EP_CMD_EPRST), 1, 100);
+ }
+
+ /* disable interrupt for device */
+ writel(0, &priv_dev->regs->usb_ien);
+ writel(USB_CONF_DEVDS, &priv_dev->regs->usb_conf);
+
+ return ret;
+}
+
+static const struct usb_gadget_ops cdns3_gadget_ops = {
+ .get_frame = cdns3_gadget_get_frame,
+ .wakeup = cdns3_gadget_wakeup,
+ .set_selfpowered = cdns3_gadget_set_selfpowered,
+ .pullup = cdns3_gadget_pullup,
+ .udc_start = cdns3_gadget_udc_start,
+ .udc_stop = cdns3_gadget_udc_stop,
+ .match_ep = cdns3_gadget_match_ep,
+};
+
+static void cdns3_free_all_eps(struct cdns3_device *priv_dev)
+{
+ int i;
+
+ /* ep0 OUT point to ep0 IN. */
+ priv_dev->eps[16] = NULL;
+
+ for (i = 0; i < CDNS3_ENDPOINTS_MAX_COUNT; i++)
+ if (priv_dev->eps[i]) {
+ cdns3_free_trb_pool(priv_dev->eps[i]);
+ devm_kfree(priv_dev->dev, priv_dev->eps[i]);
+ }
+}
+
+/**
+ * cdns3_init_eps Initializes software endpoints of gadget
+ * @cdns3: extended gadget object
+ *
+ * Returns 0 on success, error code elsewhere
+ */
+static int cdns3_init_eps(struct cdns3_device *priv_dev)
+{
+ u32 ep_enabled_reg, iso_ep_reg;
+ struct cdns3_endpoint *priv_ep;
+ int ep_dir, ep_number;
+ u32 ep_mask;
+ int ret = 0;
+ int i;
+
+ /* Read it from USB_CAP3 to USB_CAP5 */
+ ep_enabled_reg = readl(&priv_dev->regs->usb_cap3);
+ iso_ep_reg = readl(&priv_dev->regs->usb_cap4);
+
+ dev_dbg(priv_dev->dev, "Initializing non-zero endpoints\n");
+
+ for (i = 0; i < CDNS3_ENDPOINTS_MAX_COUNT; i++) {
+ ep_dir = i >> 4; /* i div 16 */
+ ep_number = i & 0xF; /* i % 16 */
+ ep_mask = BIT(i);
+
+ if (!(ep_enabled_reg & ep_mask))
+ continue;
+
+ if (ep_dir && !ep_number) {
+ priv_dev->eps[i] = priv_dev->eps[0];
+ continue;
+ }
+
+ priv_ep = devm_kzalloc(priv_dev->dev, sizeof(*priv_ep),
+ GFP_KERNEL);
+ if (!priv_ep)
+ goto err;
+
+ /* set parent of endpoint object */
+ priv_ep->cdns3_dev = priv_dev;
+ priv_dev->eps[i] = priv_ep;
+ priv_ep->num = ep_number;
+ priv_ep->dir = ep_dir ? USB_DIR_IN : USB_DIR_OUT;
+
+ if (!ep_number) {
+ ret = cdns3_init_ep0(priv_dev, priv_ep);
+ if (ret) {
+ dev_err(priv_dev->dev, "Failed to init ep0\n");
+ goto err;
+ }
+ } else {
+ snprintf(priv_ep->name, sizeof(priv_ep->name), "ep%d%s",
+ ep_number, !!ep_dir ? "in" : "out");
+ priv_ep->endpoint.name = priv_ep->name;
+
+ usb_ep_set_maxpacket_limit(&priv_ep->endpoint,
+ CDNS3_EP_MAX_PACKET_LIMIT);
+ priv_ep->endpoint.max_streams = CDNS3_EP_MAX_STREAMS;
+ priv_ep->endpoint.ops = &cdns3_gadget_ep_ops;
+ if (ep_dir)
+ priv_ep->endpoint.caps.dir_in = 1;
+ else
+ priv_ep->endpoint.caps.dir_out = 1;
+
+ if (iso_ep_reg & ep_mask)
+ priv_ep->endpoint.caps.type_iso = 1;
+
+ priv_ep->endpoint.caps.type_bulk = 1;
+ priv_ep->endpoint.caps.type_int = 1;
+
+ list_add_tail(&priv_ep->endpoint.ep_list,
+ &priv_dev->gadget.ep_list);
+ }
+
+ priv_ep->flags = 0;
+
+ dev_info(priv_dev->dev, "Initialized %s support: %s %s\n",
+ priv_ep->name,
+ priv_ep->endpoint.caps.type_bulk ? "BULK, INT" : "",
+ priv_ep->endpoint.caps.type_iso ? "ISO" : "");
+
+ INIT_LIST_HEAD(&priv_ep->pending_req_list);
+ INIT_LIST_HEAD(&priv_ep->deferred_req_list);
+ INIT_LIST_HEAD(&priv_ep->wa2_descmiss_req_list);
+ }
+
+ return 0;
+err:
+ cdns3_free_all_eps(priv_dev);
+ return -ENOMEM;
+}
+
+void cdns3_gadget_exit(struct cdns3 *cdns)
+{
+ struct cdns3_device *priv_dev;
+
+ priv_dev = cdns->gadget_dev;
+
+ devm_free_irq(cdns->dev, cdns->dev_irq, cdns);
+
+ pm_runtime_mark_last_busy(cdns->dev);
+ pm_runtime_put_autosuspend(cdns->dev);
+
+ usb_del_gadget_udc(&priv_dev->gadget);
+
+ cdns3_free_all_eps(priv_dev);
+
+ while (!list_empty(&priv_dev->aligned_buf_list)) {
+ struct cdns3_aligned_buf *buf;
+
+ buf = cdns3_next_align_buf(&priv_dev->aligned_buf_list);
+ dma_free_coherent(priv_dev->sysdev, buf->size,
+ buf->buf,
+ buf->dma);
+
+ list_del(&buf->list);
+ kfree(buf);
+ }
+
+ dma_free_coherent(priv_dev->sysdev, 8, priv_dev->setup_buf,
+ priv_dev->setup_dma);
+
+ kfree(priv_dev->zlp_buf);
+ kfree(priv_dev);
+ cdns->gadget_dev = NULL;
+ cdns3_drd_switch_gadget(cdns, 0);
+}
+
+static int cdns3_gadget_start(struct cdns3 *cdns)
+{
+ struct cdns3_device *priv_dev;
+ u32 max_speed;
+ int ret;
+
+ priv_dev = kzalloc(sizeof(*priv_dev), GFP_KERNEL);
+ if (!priv_dev)
+ return -ENOMEM;
+
+ cdns->gadget_dev = priv_dev;
+ priv_dev->sysdev = cdns->dev;
+ priv_dev->dev = cdns->dev;
+ priv_dev->regs = cdns->dev_regs;
+
+ device_property_read_u16(priv_dev->dev, "cdns,on-chip-buff-size",
+ &priv_dev->onchip_buffers);
+
+ if (priv_dev->onchip_buffers <= 0) {
+ u32 reg = readl(&priv_dev->regs->usb_cap2);
+
+ priv_dev->onchip_buffers = USB_CAP2_ACTUAL_MEM_SIZE(reg);
+ }
+
+ if (!priv_dev->onchip_buffers)
+ priv_dev->onchip_buffers = 256;
+
+ max_speed = usb_get_maximum_speed(cdns->dev);
+
+ /* Check the maximum_speed parameter */
+ switch (max_speed) {
+ case USB_SPEED_FULL:
+ writel(USB_CONF_SFORCE_FS, &priv_dev->regs->usb_conf);
+ break;
+ case USB_SPEED_HIGH:
+ writel(USB_CONF_USB3DIS, &priv_dev->regs->usb_conf);
+ break;
+ case USB_SPEED_SUPER:
+ break;
+ default:
+ dev_err(cdns->dev, "invalid maximum_speed parameter %d\n",
+ max_speed);
+ /* fall through */
+ case USB_SPEED_UNKNOWN:
+ /* default to superspeed */
+ max_speed = USB_SPEED_SUPER;
+ break;
+ }
+
+ /* fill gadget fields */
+ priv_dev->gadget.max_speed = max_speed;
+ priv_dev->gadget.speed = USB_SPEED_UNKNOWN;
+ priv_dev->gadget.ops = &cdns3_gadget_ops;
+ priv_dev->gadget.name = "usb-ss-gadget";
+ priv_dev->gadget.sg_supported = 1;
+ priv_dev->gadget.quirk_avoids_skb_reserve = 1;
+
+ spin_lock_init(&priv_dev->lock);
+ INIT_WORK(&priv_dev->pending_status_wq,
+ cdns3_pending_setup_status_handler);
+
+ INIT_WORK(&priv_dev->aligned_buf_wq,
+ cdns3_free_aligned_request_buf);
+
+ /* initialize endpoint container */
+ INIT_LIST_HEAD(&priv_dev->gadget.ep_list);
+ INIT_LIST_HEAD(&priv_dev->aligned_buf_list);
+
+ ret = cdns3_init_eps(priv_dev);
+ if (ret) {
+ dev_err(priv_dev->dev, "Failed to create endpoints\n");
+ goto err1;
+ }
+
+ /* allocate memory for setup packet buffer */
+ priv_dev->setup_buf = dma_alloc_coherent(priv_dev->sysdev, 8,
+ &priv_dev->setup_dma, GFP_DMA);
+ if (!priv_dev->setup_buf) {
+ ret = -ENOMEM;
+ goto err2;
+ }
+
+ priv_dev->dev_ver = readl(&priv_dev->regs->usb_cap6);
+
+ dev_dbg(priv_dev->dev, "Device Controller version: %08x\n",
+ readl(&priv_dev->regs->usb_cap6));
+ dev_dbg(priv_dev->dev, "USB Capabilities:: %08x\n",
+ readl(&priv_dev->regs->usb_cap1));
+ dev_dbg(priv_dev->dev, "On-Chip memory configuration: %08x\n",
+ readl(&priv_dev->regs->usb_cap2));
+
+ priv_dev->dev_ver = GET_DEV_BASE_VERSION(priv_dev->dev_ver);
+
+ priv_dev->zlp_buf = kzalloc(CDNS3_EP_ZLP_BUF_SIZE, GFP_KERNEL);
+ if (!priv_dev->zlp_buf) {
+ ret = -ENOMEM;
+ goto err3;
+ }
+
+ /* add USB gadget device */
+ ret = usb_add_gadget_udc(priv_dev->dev, &priv_dev->gadget);
+ if (ret < 0) {
+ dev_err(priv_dev->dev,
+ "Failed to register USB device controller\n");
+ goto err4;
+ }
+
+ return 0;
+err4:
+ kfree(priv_dev->zlp_buf);
+err3:
+ dma_free_coherent(priv_dev->sysdev, 8, priv_dev->setup_buf,
+ priv_dev->setup_dma);
+err2:
+ cdns3_free_all_eps(priv_dev);
+err1:
+ cdns->gadget_dev = NULL;
+ return ret;
+}
+
+static int __cdns3_gadget_init(struct cdns3 *cdns)
+{
+ int ret = 0;
+
+ cdns3_drd_switch_gadget(cdns, 1);
+ pm_runtime_get_sync(cdns->dev);
+
+ ret = cdns3_gadget_start(cdns);
+ if (ret)
+ 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(cdns->dev, cdns->dev_irq,
+ cdns3_device_irq_handler,
+ cdns3_device_thread_irq_handler,
+ IRQF_SHARED, dev_name(cdns->dev), cdns);
+
+ if (ret)
+ goto err0;
+
+ return 0;
+err0:
+ cdns3_gadget_exit(cdns);
+ return ret;
+}
+
+static int cdns3_gadget_suspend(struct cdns3 *cdns, bool do_wakeup)
+{
+ struct cdns3_device *priv_dev = cdns->gadget_dev;
+
+ cdns3_disconnect_gadget(priv_dev);
+
+ priv_dev->gadget.speed = USB_SPEED_UNKNOWN;
+ usb_gadget_set_state(&priv_dev->gadget, USB_STATE_NOTATTACHED);
+ cdns3_hw_reset_eps_config(priv_dev);
+
+ /* disable interrupt for device */
+ writel(0, &priv_dev->regs->usb_ien);
+
+ cdns3_gadget_pullup(&priv_dev->gadget, 0);
+
+ return 0;
+}
+
+static int cdns3_gadget_resume(struct cdns3 *cdns, bool hibernated)
+{
+ struct cdns3_device *priv_dev = cdns->gadget_dev;
+
+ if (!priv_dev->gadget_driver)
+ return 0;
+
+ cdns3_gadget_config(priv_dev);
+
+ return 0;
+}
+
+/**
+ * cdns3_gadget_init - initialize device structure
+ *
+ * cdns: cdns3 instance
+ *
+ * This function initializes the gadget.
+ */
+int cdns3_gadget_init(struct cdns3 *cdns)
+{
+ struct cdns3_role_driver *rdrv;
+
+ rdrv = devm_kzalloc(cdns->dev, sizeof(*rdrv), GFP_KERNEL);
+ if (!rdrv)
+ return -ENOMEM;
+
+ rdrv->start = __cdns3_gadget_init;
+ rdrv->stop = cdns3_gadget_exit;
+ rdrv->suspend = cdns3_gadget_suspend;
+ rdrv->resume = cdns3_gadget_resume;
+ rdrv->state = CDNS3_ROLE_STATE_INACTIVE;
+ rdrv->name = "gadget";
+ cdns->roles[USB_ROLE_DEVICE] = rdrv;
+
+ return 0;
+}
diff --git a/drivers/usb/cdns3/gadget.h b/drivers/usb/cdns3/gadget.h
new file mode 100644
index 000000000000..bc4024041ef2
--- /dev/null
+++ b/drivers/usb/cdns3/gadget.h
@@ -0,0 +1,1338 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * USBSS device controller driver header file
+ *
+ * Copyright (C) 2018-2019 Cadence.
+ * Copyright (C) 2017-2018 NXP
+ *
+ * Author: Pawel Laszczak <pawell@cadence.com>
+ * Pawel Jez <pjez@cadence.com>
+ * Peter Chen <peter.chen@nxp.com>
+ */
+#ifndef __LINUX_CDNS3_GADGET
+#define __LINUX_CDNS3_GADGET
+#include <linux/usb/gadget.h>
+
+/*
+ * USBSS-DEV register interface.
+ * This corresponds to the USBSS Device Controller Interface
+ */
+
+/**
+ * struct cdns3_usb_regs - device controller registers.
+ * @usb_conf: Global Configuration.
+ * @usb_sts: Global Status.
+ * @usb_cmd: Global Command.
+ * @usb_itpn: ITP/SOF number.
+ * @usb_lpm: Global Command.
+ * @usb_ien: USB Interrupt Enable.
+ * @usb_ists: USB Interrupt Status.
+ * @ep_sel: Endpoint Select.
+ * @ep_traddr: Endpoint Transfer Ring Address.
+ * @ep_cfg: Endpoint Configuration.
+ * @ep_cmd: Endpoint Command.
+ * @ep_sts: Endpoint Status.
+ * @ep_sts_sid: Endpoint Status.
+ * @ep_sts_en: Endpoint Status Enable.
+ * @drbl: Doorbell.
+ * @ep_ien: EP Interrupt Enable.
+ * @ep_ists: EP Interrupt Status.
+ * @usb_pwr: Global Power Configuration.
+ * @usb_conf2: Global Configuration 2.
+ * @usb_cap1: Capability 1.
+ * @usb_cap2: Capability 2.
+ * @usb_cap3: Capability 3.
+ * @usb_cap4: Capability 4.
+ * @usb_cap5: Capability 5.
+ * @usb_cap6: Capability 6.
+ * @usb_cpkt1: Custom Packet 1.
+ * @usb_cpkt2: Custom Packet 2.
+ * @usb_cpkt3: Custom Packet 3.
+ * @ep_dma_ext_addr: Upper address for DMA operations.
+ * @buf_addr: Address for On-chip Buffer operations.
+ * @buf_data: Data for On-chip Buffer operations.
+ * @buf_ctrl: On-chip Buffer Access Control.
+ * @dtrans: DMA Transfer Mode.
+ * @tdl_from_trb: Source of TD Configuration.
+ * @tdl_beh: TDL Behavior Configuration.
+ * @ep_tdl: Endpoint TDL.
+ * @tdl_beh2: TDL Behavior 2 Configuration.
+ * @dma_adv_td: DMA Advance TD Configuration.
+ * @reserved1: Reserved.
+ * @cfg_regs: Configuration.
+ * @reserved2: Reserved.
+ * @dma_axi_ctrl: AXI Control.
+ * @dma_axi_id: AXI ID register.
+ * @dma_axi_cap: AXI Capability.
+ * @dma_axi_ctrl0: AXI Control 0.
+ * @dma_axi_ctrl1: AXI Control 1.
+ */
+struct cdns3_usb_regs {
+ __le32 usb_conf;
+ __le32 usb_sts;
+ __le32 usb_cmd;
+ __le32 usb_itpn;
+ __le32 usb_lpm;
+ __le32 usb_ien;
+ __le32 usb_ists;
+ __le32 ep_sel;
+ __le32 ep_traddr;
+ __le32 ep_cfg;
+ __le32 ep_cmd;
+ __le32 ep_sts;
+ __le32 ep_sts_sid;
+ __le32 ep_sts_en;
+ __le32 drbl;
+ __le32 ep_ien;
+ __le32 ep_ists;
+ __le32 usb_pwr;
+ __le32 usb_conf2;
+ __le32 usb_cap1;
+ __le32 usb_cap2;
+ __le32 usb_cap3;
+ __le32 usb_cap4;
+ __le32 usb_cap5;
+ __le32 usb_cap6;
+ __le32 usb_cpkt1;
+ __le32 usb_cpkt2;
+ __le32 usb_cpkt3;
+ __le32 ep_dma_ext_addr;
+ __le32 buf_addr;
+ __le32 buf_data;
+ __le32 buf_ctrl;
+ __le32 dtrans;
+ __le32 tdl_from_trb;
+ __le32 tdl_beh;
+ __le32 ep_tdl;
+ __le32 tdl_beh2;
+ __le32 dma_adv_td;
+ __le32 reserved1[26];
+ __le32 cfg_reg1;
+ __le32 dbg_link1;
+ __le32 dbg_link2;
+ __le32 cfg_regs[74];
+ __le32 reserved2[51];
+ __le32 dma_axi_ctrl;
+ __le32 dma_axi_id;
+ __le32 dma_axi_cap;
+ __le32 dma_axi_ctrl0;
+ __le32 dma_axi_ctrl1;
+};
+
+/* USB_CONF - bitmasks */
+/* Reset USB device configuration. */
+#define USB_CONF_CFGRST BIT(0)
+/* Set Configuration. */
+#define USB_CONF_CFGSET BIT(1)
+/* Disconnect USB device in SuperSpeed. */
+#define USB_CONF_USB3DIS BIT(3)
+/* Disconnect USB device in HS/FS */
+#define USB_CONF_USB2DIS BIT(4)
+/* Little Endian access - default */
+#define USB_CONF_LENDIAN BIT(5)
+/*
+ * Big Endian access. Driver assume that byte order for
+ * SFRs access always is as Little Endian so this bit
+ * is not used.
+ */
+#define USB_CONF_BENDIAN BIT(6)
+/* Device software reset. */
+#define USB_CONF_SWRST BIT(7)
+/* Singular DMA transfer mode. Only for VER < DEV_VER_V3*/
+#define USB_CONF_DSING BIT(8)
+/* Multiple DMA transfers mode. Only for VER < DEV_VER_V3 */
+#define USB_CONF_DMULT BIT(9)
+/* DMA clock turn-off enable. */
+#define USB_CONF_DMAOFFEN BIT(10)
+/* DMA clock turn-off disable. */
+#define USB_CONF_DMAOFFDS BIT(11)
+/* Clear Force Full Speed. */
+#define USB_CONF_CFORCE_FS BIT(12)
+/* Set Force Full Speed. */
+#define USB_CONF_SFORCE_FS BIT(13)
+/* Device enable. */
+#define USB_CONF_DEVEN BIT(14)
+/* Device disable. */
+#define USB_CONF_DEVDS BIT(15)
+/* L1 LPM state entry enable (used in HS/FS mode). */
+#define USB_CONF_L1EN BIT(16)
+/* L1 LPM state entry disable (used in HS/FS mode). */
+#define USB_CONF_L1DS BIT(17)
+/* USB 2.0 clock gate disable. */
+#define USB_CONF_CLK2OFFEN BIT(18)
+/* USB 2.0 clock gate enable. */
+#define USB_CONF_CLK2OFFDS BIT(19)
+/* L0 LPM state entry request (used in HS/FS mode). */
+#define USB_CONF_LGO_L0 BIT(20)
+/* USB 3.0 clock gate disable. */
+#define USB_CONF_CLK3OFFEN BIT(21)
+/* USB 3.0 clock gate enable. */
+#define USB_CONF_CLK3OFFDS BIT(22)
+/* Bit 23 is reserved*/
+/* U1 state entry enable (used in SS mode). */
+#define USB_CONF_U1EN BIT(24)
+/* U1 state entry disable (used in SS mode). */
+#define USB_CONF_U1DS BIT(25)
+/* U2 state entry enable (used in SS mode). */
+#define USB_CONF_U2EN BIT(26)
+/* U2 state entry disable (used in SS mode). */
+#define USB_CONF_U2DS BIT(27)
+/* U0 state entry request (used in SS mode). */
+#define USB_CONF_LGO_U0 BIT(28)
+/* U1 state entry request (used in SS mode). */
+#define USB_CONF_LGO_U1 BIT(29)
+/* U2 state entry request (used in SS mode). */
+#define USB_CONF_LGO_U2 BIT(30)
+/* SS.Inactive state entry request (used in SS mode) */
+#define USB_CONF_LGO_SSINACT BIT(31)
+
+/* USB_STS - bitmasks */
+/*
+ * Configuration status.
+ * 1 - device is in the configured state.
+ * 0 - device is not configured.
+ */
+#define USB_STS_CFGSTS_MASK BIT(0)
+#define USB_STS_CFGSTS(p) ((p) & USB_STS_CFGSTS_MASK)
+/*
+ * On-chip memory overflow.
+ * 0 - On-chip memory status OK.
+ * 1 - On-chip memory overflow.
+ */
+#define USB_STS_OV_MASK BIT(1)
+#define USB_STS_OV(p) ((p) & USB_STS_OV_MASK)
+/*
+ * SuperSpeed connection status.
+ * 0 - USB in SuperSpeed mode disconnected.
+ * 1 - USB in SuperSpeed mode connected.
+ */
+#define USB_STS_USB3CONS_MASK BIT(2)
+#define USB_STS_USB3CONS(p) ((p) & USB_STS_USB3CONS_MASK)
+/*
+ * DMA transfer configuration status.
+ * 0 - single request.
+ * 1 - multiple TRB chain
+ * Supported only for controller version < DEV_VER_V3
+ */
+#define USB_STS_DTRANS_MASK BIT(3)
+#define USB_STS_DTRANS(p) ((p) & USB_STS_DTRANS_MASK)
+/*
+ * Device speed.
+ * 0 - Undefined (value after reset).
+ * 1 - Low speed
+ * 2 - Full speed
+ * 3 - High speed
+ * 4 - Super speed
+ */
+#define USB_STS_USBSPEED_MASK GENMASK(6, 4)
+#define USB_STS_USBSPEED(p) (((p) & USB_STS_USBSPEED_MASK) >> 4)
+#define USB_STS_LS (0x1 << 4)
+#define USB_STS_FS (0x2 << 4)
+#define USB_STS_HS (0x3 << 4)
+#define USB_STS_SS (0x4 << 4)
+#define DEV_UNDEFSPEED(p) (((p) & USB_STS_USBSPEED_MASK) == (0x0 << 4))
+#define DEV_LOWSPEED(p) (((p) & USB_STS_USBSPEED_MASK) == USB_STS_LS)
+#define DEV_FULLSPEED(p) (((p) & USB_STS_USBSPEED_MASK) == USB_STS_FS)
+#define DEV_HIGHSPEED(p) (((p) & USB_STS_USBSPEED_MASK) == USB_STS_HS)
+#define DEV_SUPERSPEED(p) (((p) & USB_STS_USBSPEED_MASK) == USB_STS_SS)
+/*
+ * Endianness for SFR access.
+ * 0 - Little Endian order (default after hardware reset).
+ * 1 - Big Endian order
+ */
+#define USB_STS_ENDIAN_MASK BIT(7)
+#define USB_STS_ENDIAN(p) ((p) & USB_STS_ENDIAN_MASK)
+/*
+ * HS/FS clock turn-off status.
+ * 0 - hsfs clock is always on.
+ * 1 - hsfs clock turn-off in L2 (HS/FS mode) is enabled
+ * (default after hardware reset).
+ */
+#define USB_STS_CLK2OFF_MASK BIT(8)
+#define USB_STS_CLK2OFF(p) ((p) & USB_STS_CLK2OFF_MASK)
+/*
+ * PCLK clock turn-off status.
+ * 0 - pclk clock is always on.
+ * 1 - pclk clock turn-off in U3 (SS mode) is enabled
+ * (default after hardware reset).
+ */
+#define USB_STS_CLK3OFF_MASK BIT(9)
+#define USB_STS_CLK3OFF(p) ((p) & USB_STS_CLK3OFF_MASK)
+/*
+ * Controller in reset state.
+ * 0 - Internal reset is active.
+ * 1 - Internal reset is not active and controller is fully operational.
+ */
+#define USB_STS_IN_RST_MASK BIT(10)
+#define USB_STS_IN_RST(p) ((p) & USB_STS_IN_RST_MASK)
+/*
+ * Status of the "TDL calculation basing on TRB" feature.
+ * 0 - disabled
+ * 1 - enabled
+ * Supported only for DEV_VER_V2 controller version.
+ */
+#define USB_STS_TDL_TRB_ENABLED BIT(11)
+/*
+ * Device enable Status.
+ * 0 - USB device is disabled (VBUS input is disconnected from internal logic).
+ * 1 - USB device is enabled (VBUS input is connected to the internal logic).
+ */
+#define USB_STS_DEVS_MASK BIT(14)
+#define USB_STS_DEVS(p) ((p) & USB_STS_DEVS_MASK)
+/*
+ * Address status.
+ * 0 - USB device is default state.
+ * 1 - USB device is at least in address state.
+ */
+#define USB_STS_ADDRESSED_MASK BIT(15)
+#define USB_STS_ADDRESSED(p) ((p) & USB_STS_ADDRESSED_MASK)
+/*
+ * L1 LPM state enable status (used in HS/FS mode).
+ * 0 - Entering to L1 LPM state disabled.
+ * 1 - Entering to L1 LPM state enabled.
+ */
+#define USB_STS_L1ENS_MASK BIT(16)
+#define USB_STS_L1ENS(p) ((p) & USB_STS_L1ENS_MASK)
+/*
+ * Internal VBUS connection status (used both in HS/FS and SS mode).
+ * 0 - internal VBUS is not detected.
+ * 1 - internal VBUS is detected.
+ */
+#define USB_STS_VBUSS_MASK BIT(17)
+#define USB_STS_VBUSS(p) ((p) & USB_STS_VBUSS_MASK)
+/*
+ * HS/FS LPM state (used in FS/HS mode).
+ * 0 - L0 State
+ * 1 - L1 State
+ * 2 - L2 State
+ * 3 - L3 State
+ */
+#define USB_STS_LPMST_MASK GENMASK(19, 18)
+#define DEV_L0_STATE(p) (((p) & USB_STS_LPMST_MASK) == (0x0 << 18))
+#define DEV_L1_STATE(p) (((p) & USB_STS_LPMST_MASK) == (0x1 << 18))
+#define DEV_L2_STATE(p) (((p) & USB_STS_LPMST_MASK) == (0x2 << 18))
+#define DEV_L3_STATE(p) (((p) & USB_STS_LPMST_MASK) == (0x3 << 18))
+/*
+ * Disable HS status (used in FS/HS mode).
+ * 0 - the disconnect bit for HS/FS mode is set .
+ * 1 - the disconnect bit for HS/FS mode is not set.
+ */
+#define USB_STS_USB2CONS_MASK BIT(20)
+#define USB_STS_USB2CONS(p) ((p) & USB_STS_USB2CONS_MASK)
+/*
+ * HS/FS mode connection status (used in FS/HS mode).
+ * 0 - High Speed operations in USB2.0 (FS/HS) mode not disabled.
+ * 1 - High Speed operations in USB2.0 (FS/HS).
+ */
+#define USB_STS_DISABLE_HS_MASK BIT(21)
+#define USB_STS_DISABLE_HS(p) ((p) & USB_STS_DISABLE_HS_MASK)
+/*
+ * U1 state enable status (used in SS mode).
+ * 0 - Entering to U1 state disabled.
+ * 1 - Entering to U1 state enabled.
+ */
+#define USB_STS_U1ENS_MASK BIT(24)
+#define USB_STS_U1ENS(p) ((p) & USB_STS_U1ENS_MASK)
+/*
+ * U2 state enable status (used in SS mode).
+ * 0 - Entering to U2 state disabled.
+ * 1 - Entering to U2 state enabled.
+ */
+#define USB_STS_U2ENS_MASK BIT(25)
+#define USB_STS_U2ENS(p) ((p) & USB_STS_U2ENS_MASK)
+/*
+ * SuperSpeed Link LTSSM state. This field reflects USBSS-DEV current
+ * SuperSpeed link state
+ */
+#define USB_STS_LST_MASK GENMASK(29, 26)
+#define DEV_LST_U0 (((p) & USB_STS_LST_MASK) == (0x0 << 26))
+#define DEV_LST_U1 (((p) & USB_STS_LST_MASK) == (0x1 << 26))
+#define DEV_LST_U2 (((p) & USB_STS_LST_MASK) == (0x2 << 26))
+#define DEV_LST_U3 (((p) & USB_STS_LST_MASK) == (0x3 << 26))
+#define DEV_LST_DISABLED (((p) & USB_STS_LST_MASK) == (0x4 << 26))
+#define DEV_LST_RXDETECT (((p) & USB_STS_LST_MASK) == (0x5 << 26))
+#define DEV_LST_INACTIVE (((p) & USB_STS_LST_MASK) == (0x6 << 26))
+#define DEV_LST_POLLING (((p) & USB_STS_LST_MASK) == (0x7 << 26))
+#define DEV_LST_RECOVERY (((p) & USB_STS_LST_MASK) == (0x8 << 26))
+#define DEV_LST_HOT_RESET (((p) & USB_STS_LST_MASK) == (0x9 << 26))
+#define DEV_LST_COMP_MODE (((p) & USB_STS_LST_MASK) == (0xa << 26))
+#define DEV_LST_LB_STATE (((p) & USB_STS_LST_MASK) == (0xb << 26))
+/*
+ * DMA clock turn-off status.
+ * 0 - DMA clock is always on (default after hardware reset).
+ * 1 - DMA clock turn-off in U1, U2 and U3 (SS mode) is enabled.
+ */
+#define USB_STS_DMAOFF_MASK BIT(30)
+#define USB_STS_DMAOFF(p) ((p) & USB_STS_DMAOFF_MASK)
+/*
+ * SFR Endian status.
+ * 0 - Little Endian order (default after hardware reset).
+ * 1 - Big Endian order.
+ */
+#define USB_STS_ENDIAN2_MASK BIT(31)
+#define USB_STS_ENDIAN2(p) ((p) & USB_STS_ENDIAN2_MASK)
+
+/* USB_CMD - bitmasks */
+/* Set Function Address */
+#define USB_CMD_SET_ADDR BIT(0)
+/*
+ * Function Address This field is saved to the device only when the field
+ * SET_ADDR is set '1 ' during write to USB_CMD register.
+ * Software is responsible for entering the address of the device during
+ * SET_ADDRESS request service. This field should be set immediately after
+ * the SETUP packet is decoded, and prior to confirmation of the status phase
+ */
+#define USB_CMD_FADDR_MASK GENMASK(7, 1)
+#define USB_CMD_FADDR(p) (((p) << 1) & USB_CMD_FADDR_MASK)
+/* Send Function Wake Device Notification TP (used only in SS mode). */
+#define USB_CMD_SDNFW BIT(8)
+/* Set Test Mode (used only in HS/FS mode). */
+#define USB_CMD_STMODE BIT(9)
+/* Test mode selector (used only in HS/FS mode) */
+#define USB_STS_TMODE_SEL_MASK GENMASK(11, 10)
+#define USB_STS_TMODE_SEL(p) (((p) << 10) & USB_STS_TMODE_SEL_MASK)
+/*
+ * Send Latency Tolerance Message Device Notification TP (used only
+ * in SS mode).
+ */
+#define USB_CMD_SDNLTM BIT(12)
+/* Send Custom Transaction Packet (used only in SS mode) */
+#define USB_CMD_SPKT BIT(13)
+/*Device Notification 'Function Wake' - Interface value (only in SS mode. */
+#define USB_CMD_DNFW_INT_MASK GENMASK(23, 16)
+#define USB_STS_DNFW_INT(p) (((p) << 16) & USB_CMD_DNFW_INT_MASK)
+/*
+ * Device Notification 'Latency Tolerance Message' -373 BELT value [7:0]
+ * (used only in SS mode).
+ */
+#define USB_CMD_DNLTM_BELT_MASK GENMASK(27, 16)
+#define USB_STS_DNLTM_BELT(p) (((p) << 16) & USB_CMD_DNLTM_BELT_MASK)
+
+/* USB_ITPN - bitmasks */
+/*
+ * ITP(SS) / SOF (HS/FS) number
+ * In SS mode this field represent number of last ITP received from host.
+ * In HS/FS mode this field represent number of last SOF received from host.
+ */
+#define USB_ITPN_MASK GENMASK(13, 0)
+#define USB_ITPN(p) ((p) & USB_ITPN_MASK)
+
+/* USB_LPM - bitmasks */
+/* Host Initiated Resume Duration. */
+#define USB_LPM_HIRD_MASK GENMASK(3, 0)
+#define USB_LPM_HIRD(p) ((p) & USB_LPM_HIRD_MASK)
+/* Remote Wakeup Enable (bRemoteWake). */
+#define USB_LPM_BRW BIT(4)
+
+/* USB_IEN - bitmasks */
+/* SS connection interrupt enable */
+#define USB_IEN_CONIEN BIT(0)
+/* SS disconnection interrupt enable. */
+#define USB_IEN_DISIEN BIT(1)
+/* USB SS warm reset interrupt enable. */
+#define USB_IEN_UWRESIEN BIT(2)
+/* USB SS hot reset interrupt enable */
+#define USB_IEN_UHRESIEN BIT(3)
+/* SS link U3 state enter interrupt enable (suspend).*/
+#define USB_IEN_U3ENTIEN BIT(4)
+/* SS link U3 state exit interrupt enable (wakeup). */
+#define USB_IEN_U3EXTIEN BIT(5)
+/* SS link U2 state enter interrupt enable.*/
+#define USB_IEN_U2ENTIEN BIT(6)
+/* SS link U2 state exit interrupt enable.*/
+#define USB_IEN_U2EXTIEN BIT(7)
+/* SS link U1 state enter interrupt enable.*/
+#define USB_IEN_U1ENTIEN BIT(8)
+/* SS link U1 state exit interrupt enable.*/
+#define USB_IEN_U1EXTIEN BIT(9)
+/* ITP/SOF packet detected interrupt enable.*/
+#define USB_IEN_ITPIEN BIT(10)
+/* Wakeup interrupt enable.*/
+#define USB_IEN_WAKEIEN BIT(11)
+/* Send Custom Packet interrupt enable.*/
+#define USB_IEN_SPKTIEN BIT(12)
+/* HS/FS mode connection interrupt enable.*/
+#define USB_IEN_CON2IEN BIT(16)
+/* HS/FS mode disconnection interrupt enable.*/
+#define USB_IEN_DIS2IEN BIT(17)
+/* USB reset (HS/FS mode) interrupt enable.*/
+#define USB_IEN_U2RESIEN BIT(18)
+/* LPM L2 state enter interrupt enable.*/
+#define USB_IEN_L2ENTIEN BIT(20)
+/* LPM L2 state exit interrupt enable.*/
+#define USB_IEN_L2EXTIEN BIT(21)
+/* LPM L1 state enter interrupt enable.*/
+#define USB_IEN_L1ENTIEN BIT(24)
+/* LPM L1 state exit interrupt enable.*/
+#define USB_IEN_L1EXTIEN BIT(25)
+/* Configuration reset interrupt enable.*/
+#define USB_IEN_CFGRESIEN BIT(26)
+/* Start of the USB SS warm reset interrupt enable.*/
+#define USB_IEN_UWRESSIEN BIT(28)
+/* End of the USB SS warm reset interrupt enable.*/
+#define USB_IEN_UWRESEIEN BIT(29)
+
+#define USB_IEN_INIT (USB_IEN_U2RESIEN | USB_ISTS_DIS2I | USB_IEN_CON2IEN \
+ | USB_IEN_UHRESIEN | USB_IEN_UWRESIEN | USB_IEN_DISIEN \
+ | USB_IEN_CONIEN | USB_IEN_U3EXTIEN | USB_IEN_L2ENTIEN \
+ | USB_IEN_L2EXTIEN | USB_IEN_L1ENTIEN | USB_IEN_U3ENTIEN)
+
+/* USB_ISTS - bitmasks */
+/* SS Connection detected. */
+#define USB_ISTS_CONI BIT(0)
+/* SS Disconnection detected. */
+#define USB_ISTS_DISI BIT(1)
+/* UUSB warm reset detectede. */
+#define USB_ISTS_UWRESI BIT(2)
+/* USB hot reset detected. */
+#define USB_ISTS_UHRESI BIT(3)
+/* U3 link state enter detected (suspend).*/
+#define USB_ISTS_U3ENTI BIT(4)
+/* U3 link state exit detected (wakeup). */
+#define USB_ISTS_U3EXTI BIT(5)
+/* U2 link state enter detected.*/
+#define USB_ISTS_U2ENTI BIT(6)
+/* U2 link state exit detected.*/
+#define USB_ISTS_U2EXTI BIT(7)
+/* U1 link state enter detected.*/
+#define USB_ISTS_U1ENTI BIT(8)
+/* U1 link state exit detected.*/
+#define USB_ISTS_U1EXTI BIT(9)
+/* ITP/SOF packet detected.*/
+#define USB_ISTS_ITPI BIT(10)
+/* Wakeup detected.*/
+#define USB_ISTS_WAKEI BIT(11)
+/* Send Custom Packet detected.*/
+#define USB_ISTS_SPKTI BIT(12)
+/* HS/FS mode connection detected.*/
+#define USB_ISTS_CON2I BIT(16)
+/* HS/FS mode disconnection detected.*/
+#define USB_ISTS_DIS2I BIT(17)
+/* USB reset (HS/FS mode) detected.*/
+#define USB_ISTS_U2RESI BIT(18)
+/* LPM L2 state enter detected.*/
+#define USB_ISTS_L2ENTI BIT(20)
+/* LPM L2 state exit detected.*/
+#define USB_ISTS_L2EXTI BIT(21)
+/* LPM L1 state enter detected.*/
+#define USB_ISTS_L1ENTI BIT(24)
+/* LPM L1 state exit detected.*/
+#define USB_ISTS_L1EXTI BIT(25)
+/* USB configuration reset detected.*/
+#define USB_ISTS_CFGRESI BIT(26)
+/* Start of the USB warm reset detected.*/
+#define USB_ISTS_UWRESSI BIT(28)
+/* End of the USB warm reset detected.*/
+#define USB_ISTS_UWRESEI BIT(29)
+
+/* USB_SEL - bitmasks */
+#define EP_SEL_EPNO_MASK GENMASK(3, 0)
+/* Endpoint number. */
+#define EP_SEL_EPNO(p) ((p) & EP_SEL_EPNO_MASK)
+/* Endpoint direction bit - 0 - OUT, 1 - IN. */
+#define EP_SEL_DIR BIT(7)
+
+#define select_ep_in(nr) (EP_SEL_EPNO(p) | EP_SEL_DIR)
+#define select_ep_out (EP_SEL_EPNO(p))
+
+/* EP_TRADDR - bitmasks */
+/* Transfer Ring address. */
+#define EP_TRADDR_TRADDR(p) ((p))
+
+/* EP_CFG - bitmasks */
+/* Endpoint enable */
+#define EP_CFG_ENABLE BIT(0)
+/*
+ * Endpoint type.
+ * 1 - isochronous
+ * 2 - bulk
+ * 3 - interrupt
+ */
+#define EP_CFG_EPTYPE_MASK GENMASK(2, 1)
+#define EP_CFG_EPTYPE(p) (((p) << 1) & EP_CFG_EPTYPE_MASK)
+/* Stream support enable (only in SS mode). */
+#define EP_CFG_STREAM_EN BIT(3)
+/* TDL check (only in SS mode for BULK EP). */
+#define EP_CFG_TDL_CHK BIT(4)
+/* SID check (only in SS mode for BULK OUT EP). */
+#define EP_CFG_SID_CHK BIT(5)
+/* DMA transfer endianness. */
+#define EP_CFG_EPENDIAN BIT(7)
+/* Max burst size (used only in SS mode). */
+#define EP_CFG_MAXBURST_MASK GENMASK(11, 8)
+#define EP_CFG_MAXBURST(p) (((p) << 8) & EP_CFG_MAXBURST_MASK)
+/* ISO max burst. */
+#define EP_CFG_MULT_MASK GENMASK(15, 14)
+#define EP_CFG_MULT(p) (((p) << 14) & EP_CFG_MULT_MASK)
+/* ISO max burst. */
+#define EP_CFG_MAXPKTSIZE_MASK GENMASK(26, 16)
+#define EP_CFG_MAXPKTSIZE(p) (((p) << 16) & EP_CFG_MAXPKTSIZE_MASK)
+/* Max number of buffered packets. */
+#define EP_CFG_BUFFERING_MASK GENMASK(31, 27)
+#define EP_CFG_BUFFERING(p) (((p) << 27) & EP_CFG_BUFFERING_MASK)
+
+/* EP_CMD - bitmasks */
+/* Endpoint reset. */
+#define EP_CMD_EPRST BIT(0)
+/* Endpoint STALL set. */
+#define EP_CMD_SSTALL BIT(1)
+/* Endpoint STALL clear. */
+#define EP_CMD_CSTALL BIT(2)
+/* Send ERDY TP. */
+#define EP_CMD_ERDY BIT(3)
+/* Request complete. */
+#define EP_CMD_REQ_CMPL BIT(5)
+/* Transfer descriptor ready. */
+#define EP_CMD_DRDY BIT(6)
+/* Data flush. */
+#define EP_CMD_DFLUSH BIT(7)
+/*
+ * Transfer Descriptor Length write (used only for Bulk Stream capable
+ * endpoints in SS mode).
+ * Bit Removed from DEV_VER_V3 controller version.
+ */
+#define EP_CMD_STDL BIT(8)
+/*
+ * Transfer Descriptor Length (used only in SS mode for bulk endpoints).
+ * Bits Removed from DEV_VER_V3 controller version.
+ */
+#define EP_CMD_TDL_MASK GENMASK(15, 9)
+#define EP_CMD_TDL_SET(p) (((p) << 9) & EP_CMD_TDL_MASK)
+#define EP_CMD_TDL_GET(p) (((p) & EP_CMD_TDL_MASK) >> 9)
+
+/* ERDY Stream ID value (used in SS mode). */
+#define EP_CMD_ERDY_SID_MASK GENMASK(31, 16)
+#define EP_CMD_ERDY_SID(p) (((p) << 16) & EP_CMD_ERDY_SID_MASK)
+
+/* EP_STS - bitmasks */
+/* Setup transfer complete. */
+#define EP_STS_SETUP BIT(0)
+/* Endpoint STALL status. */
+#define EP_STS_STALL(p) ((p) & BIT(1))
+/* Interrupt On Complete. */
+#define EP_STS_IOC BIT(2)
+/* Interrupt on Short Packet. */
+#define EP_STS_ISP BIT(3)
+/* Transfer descriptor missing. */
+#define EP_STS_DESCMIS BIT(4)
+/* Stream Rejected (used only in SS mode) */
+#define EP_STS_STREAMR BIT(5)
+/* EXIT from MOVE DATA State (used only for stream transfers in SS mode). */
+#define EP_STS_MD_EXIT BIT(6)
+/* TRB error. */
+#define EP_STS_TRBERR BIT(7)
+/* Not ready (used only in SS mode). */
+#define EP_STS_NRDY BIT(8)
+/* DMA busy bit. */
+#define EP_STS_DBUSY BIT(9)
+/* Endpoint Buffer Empty */
+#define EP_STS_BUFFEMPTY(p) ((p) & BIT(10))
+/* Current Cycle Status */
+#define EP_STS_CCS(p) ((p) & BIT(11))
+/* Prime (used only in SS mode. */
+#define EP_STS_PRIME BIT(12)
+/* Stream error (used only in SS mode). */
+#define EP_STS_SIDERR BIT(13)
+/* OUT size mismatch. */
+#define EP_STS_OUTSMM BIT(14)
+/* ISO transmission error. */
+#define EP_STS_ISOERR BIT(15)
+/* Host Packet Pending (only for SS mode). */
+#define EP_STS_HOSTPP(p) ((p) & BIT(16))
+/* Stream Protocol State Machine State (only for Bulk stream endpoints). */
+#define EP_STS_SPSMST_MASK GENMASK(18, 17)
+#define EP_STS_SPSMST_DISABLED(p) (((p) & EP_STS_SPSMST_MASK) >> 17)
+#define EP_STS_SPSMST_IDLE(p) (((p) & EP_STS_SPSMST_MASK) >> 17)
+#define EP_STS_SPSMST_START_STREAM(p) (((p) & EP_STS_SPSMST_MASK) >> 17)
+#define EP_STS_SPSMST_MOVE_DATA(p) (((p) & EP_STS_SPSMST_MASK) >> 17)
+/* Interrupt On Transfer complete. */
+#define EP_STS_IOT BIT(19)
+/* OUT queue endpoint number. */
+#define EP_STS_OUTQ_NO_MASK GENMASK(27, 24)
+#define EP_STS_OUTQ_NO(p) (((p) & EP_STS_OUTQ_NO_MASK) >> 24)
+/* OUT queue valid flag. */
+#define EP_STS_OUTQ_VAL_MASK BIT(28)
+#define EP_STS_OUTQ_VAL(p) ((p) & EP_STS_OUTQ_VAL_MASK)
+/* SETUP WAIT. */
+#define EP_STS_STPWAIT BIT(31)
+
+/* EP_STS_SID - bitmasks */
+/* Stream ID (used only in SS mode). */
+#define EP_STS_SID_MASK GENMASK(15, 0)
+#define EP_STS_SID(p) ((p) & EP_STS_SID_MASK)
+
+/* EP_STS_EN - bitmasks */
+/* SETUP interrupt enable. */
+#define EP_STS_EN_SETUPEN BIT(0)
+/* OUT transfer missing descriptor enable. */
+#define EP_STS_EN_DESCMISEN BIT(4)
+/* Stream Rejected enable. */
+#define EP_STS_EN_STREAMREN BIT(5)
+/* Move Data Exit enable.*/
+#define EP_STS_EN_MD_EXITEN BIT(6)
+/* TRB enable. */
+#define EP_STS_EN_TRBERREN BIT(7)
+/* NRDY enable. */
+#define EP_STS_EN_NRDYEN BIT(8)
+/* Prime enable. */
+#define EP_STS_EN_PRIMEEEN BIT(12)
+/* Stream error enable. */
+#define EP_STS_EN_SIDERREN BIT(13)
+/* OUT size mismatch enable. */
+#define EP_STS_EN_OUTSMMEN BIT(14)
+/* ISO transmission error enable. */
+#define EP_STS_EN_ISOERREN BIT(15)
+/* Interrupt on Transmission complete enable. */
+#define EP_STS_EN_IOTEN BIT(19)
+/* Setup Wait interrupt enable. */
+#define EP_STS_EN_STPWAITEN BIT(31)
+
+/* DRBL- bitmasks */
+#define DB_VALUE_BY_INDEX(index) (1 << (index))
+#define DB_VALUE_EP0_OUT BIT(0)
+#define DB_VALUE_EP0_IN BIT(16)
+
+/* EP_IEN - bitmasks */
+#define EP_IEN(index) (1 << (index))
+#define EP_IEN_EP_OUT0 BIT(0)
+#define EP_IEN_EP_IN0 BIT(16)
+
+/* EP_ISTS - bitmasks */
+#define EP_ISTS(index) (1 << (index))
+#define EP_ISTS_EP_OUT0 BIT(0)
+#define EP_ISTS_EP_IN0 BIT(16)
+
+/* USB_PWR- bitmasks */
+/*Power Shut Off capability enable*/
+#define PUSB_PWR_PSO_EN BIT(0)
+/*Power Shut Off capability disable*/
+#define PUSB_PWR_PSO_DS BIT(1)
+/*
+ * Enables turning-off Reference Clock.
+ * This bit is optional and implemented only when support for OTG is
+ * implemented (indicated by OTG_READY bit set to '1').
+ */
+#define PUSB_PWR_STB_CLK_SWITCH_EN BIT(8)
+/*
+ * Status bit indicating that operation required by STB_CLK_SWITCH_EN write
+ * is completed
+ */
+#define PUSB_PWR_STB_CLK_SWITCH_DONE BIT(9)
+/* This bit informs if Fast Registers Access is enabled. */
+#define PUSB_PWR_FST_REG_ACCESS_STAT BIT(30)
+/* Fast Registers Access Enable. */
+#define PUSB_PWR_FST_REG_ACCESS BIT(31)
+
+/* USB_CONF2- bitmasks */
+/*
+ * Writing 1 disables TDL calculation basing on TRB feature in controller
+ * for DMULT mode.
+ * Bit supported only for DEV_VER_V2 version.
+ */
+#define USB_CONF2_DIS_TDL_TRB BIT(1)
+/*
+ * Writing 1 enables TDL calculation basing on TRB feature in controller
+ * for DMULT mode.
+ * Bit supported only for DEV_VER_V2 version.
+ */
+#define USB_CONF2_EN_TDL_TRB BIT(2)
+
+/* USB_CAP1- bitmasks */
+/*
+ * SFR Interface type
+ * These field reflects type of SFR interface implemented:
+ * 0x0 - OCP
+ * 0x1 - AHB,
+ * 0x2 - PLB
+ * 0x3 - AXI
+ * 0x4-0xF - reserved
+ */
+#define USB_CAP1_SFR_TYPE_MASK GENMASK(3, 0)
+#define DEV_SFR_TYPE_OCP(p) (((p) & USB_CAP1_SFR_TYPE_MASK) == 0x0)
+#define DEV_SFR_TYPE_AHB(p) (((p) & USB_CAP1_SFR_TYPE_MASK) == 0x1)
+#define DEV_SFR_TYPE_PLB(p) (((p) & USB_CAP1_SFR_TYPE_MASK) == 0x2)
+#define DEV_SFR_TYPE_AXI(p) (((p) & USB_CAP1_SFR_TYPE_MASK) == 0x3)
+/*
+ * SFR Interface width
+ * These field reflects width of SFR interface implemented:
+ * 0x0 - 8 bit interface,
+ * 0x1 - 16 bit interface,
+ * 0x2 - 32 bit interface
+ * 0x3 - 64 bit interface
+ * 0x4-0xF - reserved
+ */
+#define USB_CAP1_SFR_WIDTH_MASK GENMASK(7, 4)
+#define DEV_SFR_WIDTH_8(p) (((p) & USB_CAP1_SFR_WIDTH_MASK) == (0x0 << 4))
+#define DEV_SFR_WIDTH_16(p) (((p) & USB_CAP1_SFR_WIDTH_MASK) == (0x1 << 4))
+#define DEV_SFR_WIDTH_32(p) (((p) & USB_CAP1_SFR_WIDTH_MASK) == (0x2 << 4))
+#define DEV_SFR_WIDTH_64(p) (((p) & USB_CAP1_SFR_WIDTH_MASK) == (0x3 << 4))
+/*
+ * DMA Interface type
+ * These field reflects type of DMA interface implemented:
+ * 0x0 - OCP
+ * 0x1 - AHB,
+ * 0x2 - PLB
+ * 0x3 - AXI
+ * 0x4-0xF - reserved
+ */
+#define USB_CAP1_DMA_TYPE_MASK GENMASK(11, 8)
+#define DEV_DMA_TYPE_OCP(p) (((p) & USB_CAP1_DMA_TYPE_MASK) == (0x0 << 8))
+#define DEV_DMA_TYPE_AHB(p) (((p) & USB_CAP1_DMA_TYPE_MASK) == (0x1 << 8))
+#define DEV_DMA_TYPE_PLB(p) (((p) & USB_CAP1_DMA_TYPE_MASK) == (0x2 << 8))
+#define DEV_DMA_TYPE_AXI(p) (((p) & USB_CAP1_DMA_TYPE_MASK) == (0x3 << 8))
+/*
+ * DMA Interface width
+ * These field reflects width of DMA interface implemented:
+ * 0x0 - reserved,
+ * 0x1 - reserved,
+ * 0x2 - 32 bit interface
+ * 0x3 - 64 bit interface
+ * 0x4-0xF - reserved
+ */
+#define USB_CAP1_DMA_WIDTH_MASK GENMASK(15, 12)
+#define DEV_DMA_WIDTH_32(p) (((p) & USB_CAP1_DMA_WIDTH_MASK) == (0x2 << 12))
+#define DEV_DMA_WIDTH_64(p) (((p) & USB_CAP1_DMA_WIDTH_MASK) == (0x3 << 12))
+/*
+ * USB3 PHY Interface type
+ * These field reflects type of USB3 PHY interface implemented:
+ * 0x0 - USB PIPE,
+ * 0x1 - RMMI,
+ * 0x2-0xF - reserved
+ */
+#define USB_CAP1_U3PHY_TYPE_MASK GENMASK(19, 16)
+#define DEV_U3PHY_PIPE(p) (((p) & USB_CAP1_U3PHY_TYPE_MASK) == (0x0 << 16))
+#define DEV_U3PHY_RMMI(p) (((p) & USB_CAP1_U3PHY_TYPE_MASK) == (0x1 << 16))
+/*
+ * USB3 PHY Interface width
+ * These field reflects width of USB3 PHY interface implemented:
+ * 0x0 - 8 bit PIPE interface,
+ * 0x1 - 16 bit PIPE interface,
+ * 0x2 - 32 bit PIPE interface,
+ * 0x3 - 64 bit PIPE interface
+ * 0x4-0xF - reserved
+ * Note: When SSIC interface is implemented this field shows the width of
+ * internal PIPE interface. The RMMI interface is always 20bit wide.
+ */
+#define USB_CAP1_U3PHY_WIDTH_MASK GENMASK(23, 20)
+#define DEV_U3PHY_WIDTH_8(p) \
+ (((p) & USB_CAP1_U3PHY_WIDTH_MASK) == (0x0 << 20))
+#define DEV_U3PHY_WIDTH_16(p) \
+ (((p) & USB_CAP1_U3PHY_WIDTH_MASK) == (0x1 << 16))
+#define DEV_U3PHY_WIDTH_32(p) \
+ (((p) & USB_CAP1_U3PHY_WIDTH_MASK) == (0x2 << 20))
+#define DEV_U3PHY_WIDTH_64(p) \
+ (((p) & USB_CAP1_U3PHY_WIDTH_MASK) == (0x3 << 16))
+
+/*
+ * USB2 PHY Interface enable
+ * These field informs if USB2 PHY interface is implemented:
+ * 0x0 - interface NOT implemented,
+ * 0x1 - interface implemented
+ */
+#define USB_CAP1_U2PHY_EN(p) ((p) & BIT(24))
+/*
+ * USB2 PHY Interface type
+ * These field reflects type of USB2 PHY interface implemented:
+ * 0x0 - UTMI,
+ * 0x1 - ULPI
+ */
+#define DEV_U2PHY_ULPI(p) ((p) & BIT(25))
+/*
+ * USB2 PHY Interface width
+ * These field reflects width of USB2 PHY interface implemented:
+ * 0x0 - 8 bit interface,
+ * 0x1 - 16 bit interface,
+ * Note: The ULPI interface is always 8bit wide.
+ */
+#define DEV_U2PHY_WIDTH_16(p) ((p) & BIT(26))
+/*
+ * OTG Ready
+ * 0x0 - pure device mode
+ * 0x1 - some features and ports for CDNS USB OTG controller are implemented.
+ */
+#define USB_CAP1_OTG_READY(p) ((p) & BIT(27))
+
+/*
+ * When set, indicates that controller supports automatic internal TDL
+ * calculation basing on the size provided in TRB (TRB[22:17]) for DMULT mode
+ * Supported only for DEV_VER_V2 controller version.
+ */
+#define USB_CAP1_TDL_FROM_TRB(p) ((p) & BIT(28))
+
+/* USB_CAP2- bitmasks */
+/*
+ * The actual size of the connected On-chip RAM memory in kB:
+ * - 0 means 256 kB (max supported mem size)
+ * - value other than 0 reflects the mem size in kB
+ */
+#define USB_CAP2_ACTUAL_MEM_SIZE(p) ((p) & GENMASK(7, 0))
+/*
+ * Max supported mem size
+ * These field reflects width of on-chip RAM address bus width,
+ * which determines max supported mem size:
+ * 0x0-0x7 - reserved,
+ * 0x8 - support for 4kB mem,
+ * 0x9 - support for 8kB mem,
+ * 0xA - support for 16kB mem,
+ * 0xB - support for 32kB mem,
+ * 0xC - support for 64kB mem,
+ * 0xD - support for 128kB mem,
+ * 0xE - support for 256kB mem,
+ * 0xF - reserved
+ */
+#define USB_CAP2_MAX_MEM_SIZE(p) ((p) & GENMASK(11, 8))
+
+/* USB_CAP3- bitmasks */
+#define EP_IS_IMPLEMENTED(reg, index) ((reg) & (1 << (index)))
+
+/* USB_CAP4- bitmasks */
+#define EP_SUPPORT_ISO(reg, index) ((reg) & (1 << (index)))
+
+/* USB_CAP5- bitmasks */
+#define EP_SUPPORT_STREAM(reg, index) ((reg) & (1 << (index)))
+
+/* USB_CAP6- bitmasks */
+/* The USBSS-DEV Controller Internal build number. */
+#define GET_DEV_BASE_VERSION(p) ((p) & GENMASK(23, 0))
+/* The USBSS-DEV Controller version number. */
+#define GET_DEV_CUSTOM_VERSION(p) ((p) & GENMASK(31, 24))
+
+#define DEV_VER_NXP_V1 0x00024502
+#define DEV_VER_TI_V1 0x00024509
+#define DEV_VER_V2 0x0002450C
+#define DEV_VER_V3 0x0002450d
+
+/* DBG_LINK1- bitmasks */
+/*
+ * LFPS_MIN_DET_U1_EXIT value This parameter configures the minimum
+ * time required for decoding the received LFPS as an LFPS.U1_Exit.
+ */
+#define DBG_LINK1_LFPS_MIN_DET_U1_EXIT(p) ((p) & GENMASK(7, 0))
+/*
+ * LFPS_MIN_GEN_U1_EXIT value This parameter configures the minimum time for
+ * phytxelecidle deassertion when LFPS.U1_Exit
+ */
+#define DBG_LINK1_LFPS_MIN_GEN_U1_EXIT_MASK GENMASK(15, 8)
+#define DBG_LINK1_LFPS_MIN_GEN_U1_EXIT(p) (((p) << 8) & GENMASK(15, 8))
+/*
+ * RXDET_BREAK_DIS value This parameter configures terminating the Far-end
+ * Receiver termination detection sequence:
+ * 0: it is possible that USBSS_DEV will terminate Farend receiver
+ * termination detection sequence
+ * 1: USBSS_DEV will not terminate Far-end receiver termination
+ * detection sequence
+ */
+#define DBG_LINK1_RXDET_BREAK_DIS BIT(16)
+/* LFPS_GEN_PING value This parameter configures the LFPS.Ping generation */
+#define DBG_LINK1_LFPS_GEN_PING(p) (((p) << 17) & GENMASK(21, 17))
+/*
+ * Set the LFPS_MIN_DET_U1_EXIT value Writing '1' to this bit writes the
+ * LFPS_MIN_DET_U1_EXIT field value to the device. This bit is automatically
+ * cleared. Writing '0' has no effect
+ */
+#define DBG_LINK1_LFPS_MIN_DET_U1_EXIT_SET BIT(24)
+/*
+ * Set the LFPS_MIN_GEN_U1_EXIT value. Writing '1' to this bit writes the
+ * LFPS_MIN_GEN_U1_EXIT field value to the device. This bit is automatically
+ * cleared. Writing '0' has no effect
+ */
+#define DBG_LINK1_LFPS_MIN_GEN_U1_EXIT_SET BIT(25)
+/*
+ * Set the RXDET_BREAK_DIS value Writing '1' to this bit writes
+ * the RXDET_BREAK_DIS field value to the device. This bit is automatically
+ * cleared. Writing '0' has no effect
+ */
+#define DBG_LINK1_RXDET_BREAK_DIS_SET BIT(26)
+/*
+ * Set the LFPS_GEN_PING_SET value Writing '1' to this bit writes
+ * the LFPS_GEN_PING field value to the device. This bit is automatically
+ * cleared. Writing '0' has no effect."
+ */
+#define DBG_LINK1_LFPS_GEN_PING_SET BIT(27)
+
+/* DMA_AXI_CTRL- bitmasks */
+/* The mawprot pin configuration. */
+#define DMA_AXI_CTRL_MARPROT(p) ((p) & GENMASK(2, 0))
+/* The marprot pin configuration. */
+#define DMA_AXI_CTRL_MAWPROT(p) (((p) & GENMASK(2, 0)) << 16)
+#define DMA_AXI_CTRL_NON_SECURE 0x02
+
+#define gadget_to_cdns3_device(g) (container_of(g, struct cdns3_device, gadget))
+
+#define ep_to_cdns3_ep(ep) (container_of(ep, struct cdns3_endpoint, endpoint))
+
+/*-------------------------------------------------------------------------*/
+/*
+ * USBSS-DEV DMA interface.
+ */
+#define TRBS_PER_SEGMENT 40
+
+#define ISO_MAX_INTERVAL 10
+
+#if TRBS_PER_SEGMENT < 2
+#error "Incorrect TRBS_PER_SEGMENT. Minimal Transfer Ring size is 2."
+#endif
+
+/*
+ *Only for ISOC endpoints - maximum number of TRBs is calculated as
+ * pow(2, bInterval-1) * number of usb requests. It is limitation made by
+ * driver to save memory. Controller must prepare TRB for each ITP even
+ * if bInterval > 1. It's the reason why driver needs so many TRBs for
+ * isochronous endpoints.
+ */
+#define TRBS_PER_ISOC_SEGMENT (ISO_MAX_INTERVAL * 8)
+
+#define GET_TRBS_PER_SEGMENT(ep_type) ((ep_type) == USB_ENDPOINT_XFER_ISOC ? \
+ TRBS_PER_ISOC_SEGMENT : TRBS_PER_SEGMENT)
+/**
+ * struct cdns3_trb - represent Transfer Descriptor block.
+ * @buffer: pointer to buffer data
+ * @length: length of data
+ * @control: control flags.
+ *
+ * This structure describes transfer block serviced by DMA module.
+ */
+struct cdns3_trb {
+ __le32 buffer;
+ __le32 length;
+ __le32 control;
+};
+
+#define TRB_SIZE (sizeof(struct cdns3_trb))
+#define TRB_RING_SIZE (TRB_SIZE * TRBS_PER_SEGMENT)
+#define TRB_ISO_RING_SIZE (TRB_SIZE * TRBS_PER_ISOC_SEGMENT)
+#define TRB_CTRL_RING_SIZE (TRB_SIZE * 2)
+
+/* 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 */
+/* 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)
+
+/*
+ * Short Packet (SP). OUT EPs at DMULT=1 only. Indicates if the TRB was
+ * processed while USB short packet was received. No more buffers defined by
+ * the TD will be used. DMA will automatically advance to next TD.
+ * - Shall be set to 0 by Software when putting TRB on the Transfer Ring
+ * - Shall be set to 1 by Controller when Short Packet condition for this TRB
+ * is detected independent if ISP is set or not.
+ */
+#define TRB_SP BIT(1)
+
+/* Interrupt on short packet*/
+#define TRB_ISP BIT(2)
+/*Setting this bit enables FIFO DMA operation mode*/
+#define TRB_FIFO_MODE BIT(3)
+/* Set PCIe no snoop attribute */
+#define TRB_CHAIN BIT(4)
+/* Interrupt on completion */
+#define TRB_IOC BIT(5)
+
+/* stream ID bitmasks. */
+#define TRB_STREAM_ID_BITMASK GENMASK(31, 16)
+#define TRB_STREAM_ID(p) ((p) << 16)
+#define TRB_FIELD_TO_STREAMID(p) (((p) & TRB_STREAM_ID_BITMASK) >> 16)
+
+/* Size of TD expressed in USB packets for HS/FS mode. */
+#define TRB_TDL_HS_SIZE(p) (((p) << 16) & GENMASK(31, 16))
+#define TRB_TDL_HS_SIZE_GET(p) (((p) & GENMASK(31, 16)) >> 16)
+
+/* transfer_len bitmasks. */
+#define TRB_LEN(p) ((p) & GENMASK(16, 0))
+
+/* Size of TD expressed in USB packets for SS mode. */
+#define TRB_TDL_SS_SIZE(p) (((p) << 17) & GENMASK(23, 17))
+#define TRB_TDL_SS_SIZE_GET(p) (((p) & GENMASK(23, 17)) >> 17)
+
+/* transfer_len bitmasks - bits 31:24 */
+#define TRB_BURST_LEN(p) (((p) << 24) & GENMASK(31, 24))
+#define TRB_BURST_LEN_GET(p) (((p) & GENMASK(31, 24)) >> 24)
+
+/* Data buffer pointer bitmasks*/
+#define TRB_BUFFER(p) ((p) & GENMASK(31, 0))
+
+/*-------------------------------------------------------------------------*/
+/* Driver numeric constants */
+
+/* Such declaration should be added to ch9.h */
+#define USB_DEVICE_MAX_ADDRESS 127
+
+/* Endpoint init values */
+#define CDNS3_EP_MAX_PACKET_LIMIT 1024
+#define CDNS3_EP_MAX_STREAMS 15
+#define CDNS3_EP0_MAX_PACKET_LIMIT 512
+
+/* All endpoints including EP0 */
+#define CDNS3_ENDPOINTS_MAX_COUNT 32
+#define CDNS3_EP_ZLP_BUF_SIZE 1024
+
+#define CDNS3_EP_BUF_SIZE 2 /* KB */
+#define CDNS3_EP_ISO_HS_MULT 3
+#define CDNS3_EP_ISO_SS_BURST 3
+#define CDNS3_MAX_NUM_DESCMISS_BUF 32
+#define CDNS3_DESCMIS_BUF_SIZE 2048 /* Bytes */
+#define CDNS3_WA2_NUM_BUFFERS 128
+/*-------------------------------------------------------------------------*/
+/* Used structs */
+
+struct cdns3_device;
+
+/**
+ * struct cdns3_endpoint - extended device side representation of USB endpoint.
+ * @endpoint: usb endpoint
+ * @pending_req_list: list of requests queuing on transfer ring.
+ * @deferred_req_list: list of requests waiting for queuing on transfer ring.
+ * @wa2_descmiss_req_list: list of requests internally allocated by driver.
+ * @trb_pool: transfer ring - array of transaction buffers
+ * @trb_pool_dma: dma address of transfer ring
+ * @cdns3_dev: device associated with this endpoint
+ * @name: a human readable name e.g. ep1out
+ * @flags: specify the current state of endpoint
+ * @descmis_req: internal transfer object used for getting data from on-chip
+ * buffer. It can happen only if function driver doesn't send usb_request
+ * object on time.
+ * @dir: endpoint direction
+ * @num: endpoint number (1 - 15)
+ * @type: set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK
+ * @interval: interval between packets used for ISOC endpoint.
+ * @free_trbs: number of free TRBs in transfer ring
+ * @num_trbs: number of all TRBs in transfer ring
+ * @pcs: producer cycle state
+ * @ccs: consumer cycle state
+ * @enqueue: enqueue index in transfer ring
+ * @dequeue: dequeue index in transfer ring
+ * @trb_burst_size: number of burst used in trb.
+ */
+struct cdns3_endpoint {
+ struct usb_ep endpoint;
+ struct list_head pending_req_list;
+ struct list_head deferred_req_list;
+ struct list_head wa2_descmiss_req_list;
+ int wa2_counter;
+
+ struct cdns3_trb *trb_pool;
+ dma_addr_t trb_pool_dma;
+
+ struct cdns3_device *cdns3_dev;
+ char name[20];
+
+#define EP_ENABLED BIT(0)
+#define EP_STALLED BIT(1)
+#define EP_STALL_PENDING BIT(2)
+#define EP_WEDGE BIT(3)
+#define EP_TRANSFER_STARTED BIT(4)
+#define EP_UPDATE_EP_TRBADDR BIT(5)
+#define EP_PENDING_REQUEST BIT(6)
+#define EP_RING_FULL BIT(7)
+#define EP_CLAIMED BIT(8)
+#define EP_DEFERRED_DRDY BIT(9)
+#define EP_QUIRK_ISO_OUT_EN BIT(10)
+#define EP_QUIRK_END_TRANSFER BIT(11)
+#define EP_QUIRK_EXTRA_BUF_DET BIT(12)
+#define EP_QUIRK_EXTRA_BUF_EN BIT(13)
+ u32 flags;
+
+ struct cdns3_request *descmis_req;
+
+ u8 dir;
+ u8 num;
+ u8 type;
+ int interval;
+
+ int free_trbs;
+ int num_trbs;
+ u8 pcs;
+ u8 ccs;
+ int enqueue;
+ int dequeue;
+ u8 trb_burst_size;
+
+ unsigned int wa1_set:1;
+ struct cdns3_trb *wa1_trb;
+ unsigned int wa1_trb_index;
+ unsigned int wa1_cycle_bit:1;
+};
+
+/**
+ * struct cdns3_aligned_buf - represent aligned buffer used for DMA transfer
+ * @buf: aligned to 8 bytes data buffer. Buffer address used in
+ * TRB shall be aligned to 8.
+ * @dma: dma address
+ * @size: size of buffer
+ * @in_use: inform if this buffer is associated with usb_request
+ * @list: used to adding instance of this object to list
+ */
+struct cdns3_aligned_buf {
+ void *buf;
+ dma_addr_t dma;
+ u32 size;
+ int in_use:1;
+ struct list_head list;
+};
+
+/**
+ * struct cdns3_request - extended device side representation of usb_request
+ * object .
+ * @request: generic usb_request object describing single I/O request.
+ * @priv_ep: 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
+ * @aligned_buf: object holds information about aligned buffer associated whit
+ * this endpoint
+ * @flags: flag specifying special usage of request
+ * @list: used by internally allocated request to add to wa2_descmiss_req_list.
+ */
+struct cdns3_request {
+ struct usb_request request;
+ struct cdns3_endpoint *priv_ep;
+ struct cdns3_trb *trb;
+ int start_trb;
+ int end_trb;
+ struct cdns3_aligned_buf *aligned_buf;
+#define REQUEST_PENDING BIT(0)
+#define REQUEST_INTERNAL BIT(1)
+#define REQUEST_INTERNAL_CH BIT(2)
+#define REQUEST_ZLP BIT(3)
+#define REQUEST_UNALIGNED BIT(4)
+ u32 flags;
+ struct list_head list;
+};
+
+#define to_cdns3_request(r) (container_of(r, struct cdns3_request, request))
+
+/*Stages used during enumeration process.*/
+#define CDNS3_SETUP_STAGE 0x0
+#define CDNS3_DATA_STAGE 0x1
+#define CDNS3_STATUS_STAGE 0x2
+
+/**
+ * struct cdns3_device - represent USB device.
+ * @dev: pointer to device structure associated whit this controller
+ * @sysdev: pointer to the DMA capable device
+ * @gadget: device side representation of the peripheral controller
+ * @gadget_driver: pointer to the gadget driver
+ * @dev_ver: device controller version.
+ * @lock: for synchronizing
+ * @regs: base address for device side registers
+ * @setup_buf: used while processing usb control requests
+ * @setup_dma: dma address for setup_buf
+ * @zlp_buf - zlp buffer
+ * @ep0_stage: ep0 stage during enumeration process.
+ * @ep0_data_dir: direction for control transfer
+ * @eps: array of pointers to all endpoints with exclusion ep0
+ * @aligned_buf_list: list of aligned buffers internally allocated by driver
+ * @aligned_buf_wq: workqueue freeing no longer used aligned buf.
+ * @selected_ep: actually selected endpoint. It's used only to improve
+ * performance.
+ * @isoch_delay: value from Set Isoch Delay request. Only valid on SS/SSP.
+ * @u1_allowed: allow device transition to u1 state
+ * @u2_allowed: allow device transition to u2 state
+ * @is_selfpowered: device is self powered
+ * @setup_pending: setup packet is processing by gadget driver
+ * @hw_configured_flag: hardware endpoint configuration was set.
+ * @wake_up_flag: allow device to remote up the host
+ * @status_completion_no_call: indicate that driver is waiting for status s
+ * stage completion. It's used in deferred SET_CONFIGURATION request.
+ * @onchip_buffers: number of available on-chip buffers.
+ * @onchip_used_size: actual size of on-chip memory assigned to endpoints.
+ * @pending_status_wq: workqueue handling status stage for deferred requests.
+ * @pending_status_request: request for which status stage was deferred
+ */
+struct cdns3_device {
+ struct device *dev;
+ struct device *sysdev;
+
+ struct usb_gadget gadget;
+ struct usb_gadget_driver *gadget_driver;
+
+#define CDNS_REVISION_V0 0x00024501
+#define CDNS_REVISION_V1 0x00024509
+ u32 dev_ver;
+
+ /* generic spin-lock for drivers */
+ spinlock_t lock;
+
+ struct cdns3_usb_regs __iomem *regs;
+
+ struct usb_ctrlrequest *setup_buf;
+ dma_addr_t setup_dma;
+ void *zlp_buf;
+
+ u8 ep0_stage;
+ int ep0_data_dir;
+
+ struct cdns3_endpoint *eps[CDNS3_ENDPOINTS_MAX_COUNT];
+
+ struct list_head aligned_buf_list;
+ struct work_struct aligned_buf_wq;
+
+ u32 selected_ep;
+ u16 isoch_delay;
+
+ unsigned wait_for_setup:1;
+ unsigned u1_allowed:1;
+ unsigned u2_allowed:1;
+ unsigned is_selfpowered:1;
+ unsigned setup_pending:1;
+ int hw_configured_flag:1;
+ int wake_up_flag:1;
+ unsigned status_completion_no_call:1;
+ int out_mem_is_allocated;
+
+ struct work_struct pending_status_wq;
+ struct usb_request *pending_status_request;
+
+ /*in KB */
+ u16 onchip_buffers;
+ u16 onchip_used_size;
+};
+
+void cdns3_set_register_bit(void __iomem *ptr, u32 mask);
+dma_addr_t cdns3_trb_virt_to_dma(struct cdns3_endpoint *priv_ep,
+ struct cdns3_trb *trb);
+enum usb_device_speed cdns3_get_speed(struct cdns3_device *priv_dev);
+void cdns3_pending_setup_status_handler(struct work_struct *work);
+void cdns3_hw_reset_eps_config(struct cdns3_device *priv_dev);
+void cdns3_set_hw_configuration(struct cdns3_device *priv_dev);
+void cdns3_select_ep(struct cdns3_device *priv_dev, u32 ep);
+void cdns3_allow_enable_l1(struct cdns3_device *priv_dev, int enable);
+struct usb_request *cdns3_next_request(struct list_head *list);
+int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
+ struct usb_request *request);
+void cdns3_rearm_transfer(struct cdns3_endpoint *priv_ep, u8 rearm);
+int cdns3_allocate_trb_pool(struct cdns3_endpoint *priv_ep);
+u8 cdns3_ep_addr_to_index(u8 ep_addr);
+int cdns3_gadget_ep_set_wedge(struct usb_ep *ep);
+int cdns3_gadget_ep_set_halt(struct usb_ep *ep, int value);
+void __cdns3_gadget_ep_set_halt(struct cdns3_endpoint *priv_ep);
+int __cdns3_gadget_ep_clear_halt(struct cdns3_endpoint *priv_ep);
+struct usb_request *cdns3_gadget_ep_alloc_request(struct usb_ep *ep,
+ gfp_t gfp_flags);
+void cdns3_gadget_ep_free_request(struct usb_ep *ep,
+ struct usb_request *request);
+int cdns3_gadget_ep_dequeue(struct usb_ep *ep, struct usb_request *request);
+void cdns3_gadget_giveback(struct cdns3_endpoint *priv_ep,
+ struct cdns3_request *priv_req,
+ int status);
+
+int cdns3_init_ep0(struct cdns3_device *priv_dev,
+ struct cdns3_endpoint *priv_ep);
+void cdns3_ep0_config(struct cdns3_device *priv_dev);
+void cdns3_ep_config(struct cdns3_endpoint *priv_ep);
+void cdns3_check_ep0_interrupt_proceed(struct cdns3_device *priv_dev, int dir);
+int __cdns3_gadget_wakeup(struct cdns3_device *priv_dev);
+
+#endif /* __LINUX_CDNS3_GADGET */
diff --git a/drivers/usb/cdns3/host-export.h b/drivers/usb/cdns3/host-export.h
new file mode 100644
index 000000000000..b498a170b7e8
--- /dev/null
+++ b/drivers/usb/cdns3/host-export.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Cadence USBSS DRD Driver - Host Export APIs
+ *
+ * Copyright (C) 2017-2018 NXP
+ *
+ * Authors: Peter Chen <peter.chen@nxp.com>
+ */
+#ifndef __LINUX_CDNS3_HOST_EXPORT
+#define __LINUX_CDNS3_HOST_EXPORT
+
+#ifdef CONFIG_USB_CDNS3_HOST
+
+int cdns3_host_init(struct cdns3 *cdns);
+void cdns3_host_exit(struct cdns3 *cdns);
+
+#else
+
+static inline int cdns3_host_init(struct cdns3 *cdns)
+{
+ return -ENXIO;
+}
+
+static inline void cdns3_host_exit(struct cdns3 *cdns) { }
+
+#endif /* CONFIG_USB_CDNS3_HOST */
+
+#endif /* __LINUX_CDNS3_HOST_EXPORT */
diff --git a/drivers/usb/cdns3/host.c b/drivers/usb/cdns3/host.c
new file mode 100644
index 000000000000..2733a8f71fcd
--- /dev/null
+++ b/drivers/usb/cdns3/host.c
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Cadence USBSS DRD Driver - host side
+ *
+ * Copyright (C) 2018-2019 Cadence Design Systems.
+ * Copyright (C) 2017-2018 NXP
+ *
+ * Authors: Peter Chen <peter.chen@nxp.com>
+ * Pawel Laszczak <pawell@cadence.com>
+ */
+
+#include <linux/platform_device.h>
+#include "core.h"
+#include "drd.h"
+
+static int __cdns3_host_init(struct cdns3 *cdns)
+{
+ struct platform_device *xhci;
+ int ret;
+
+ cdns3_drd_switch_host(cdns, 1);
+
+ xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO);
+ if (!xhci) {
+ dev_err(cdns->dev, "couldn't allocate xHCI device\n");
+ return -ENOMEM;
+ }
+
+ xhci->dev.parent = cdns->dev;
+ cdns->host_dev = xhci;
+
+ ret = platform_device_add_resources(xhci, cdns->xhci_res,
+ CDNS3_XHCI_RESOURCES_NUM);
+ if (ret) {
+ dev_err(cdns->dev, "couldn't add resources to xHCI device\n");
+ goto err1;
+ }
+
+ ret = platform_device_add(xhci);
+ if (ret) {
+ dev_err(cdns->dev, "failed to register xHCI device\n");
+ goto err1;
+ }
+
+ return 0;
+err1:
+ platform_device_put(xhci);
+ return ret;
+}
+
+static void cdns3_host_exit(struct cdns3 *cdns)
+{
+ platform_device_unregister(cdns->host_dev);
+ cdns->host_dev = NULL;
+ cdns3_drd_switch_host(cdns, 0);
+}
+
+int cdns3_host_init(struct cdns3 *cdns)
+{
+ struct cdns3_role_driver *rdrv;
+
+ rdrv = devm_kzalloc(cdns->dev, sizeof(*rdrv), GFP_KERNEL);
+ if (!rdrv)
+ return -ENOMEM;
+
+ rdrv->start = __cdns3_host_init;
+ rdrv->stop = cdns3_host_exit;
+ rdrv->state = CDNS3_ROLE_STATE_INACTIVE;
+ rdrv->name = "host";
+
+ cdns->roles[USB_ROLE_HOST] = rdrv;
+
+ return 0;
+}
diff --git a/drivers/usb/cdns3/trace.c b/drivers/usb/cdns3/trace.c
new file mode 100644
index 000000000000..459fa72d9c74
--- /dev/null
+++ b/drivers/usb/cdns3/trace.c
@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * USBSS device controller driver Trace Support
+ *
+ * Copyright (C) 2018-2019 Cadence.
+ *
+ * Author: Pawel Laszczak <pawell@cadence.com>
+ */
+
+#define CREATE_TRACE_POINTS
+#include "trace.h"
diff --git a/drivers/usb/cdns3/trace.h b/drivers/usb/cdns3/trace.h
new file mode 100644
index 000000000000..e92348c9b4d7
--- /dev/null
+++ b/drivers/usb/cdns3/trace.h
@@ -0,0 +1,493 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * USBSS device controller driver.
+ * Trace support header file.
+ *
+ * Copyright (C) 2018-2019 Cadence.
+ *
+ * Author: Pawel Laszczak <pawell@cadence.com>
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM cdns3
+
+#if !defined(__LINUX_CDNS3_TRACE) || defined(TRACE_HEADER_MULTI_READ)
+#define __LINUX_CDNS3_TRACE
+
+#include <linux/types.h>
+#include <linux/tracepoint.h>
+#include <asm/byteorder.h>
+#include <linux/usb/ch9.h>
+#include "core.h"
+#include "gadget.h"
+#include "debug.h"
+
+#define CDNS3_MSG_MAX 500
+
+TRACE_EVENT(cdns3_halt,
+ TP_PROTO(struct cdns3_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(cdns3_wa1,
+ TP_PROTO(struct cdns3_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))
+);
+
+TRACE_EVENT(cdns3_wa2,
+ TP_PROTO(struct cdns3_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("WA2: %s %s", __get_str(ep_name), __get_str(msg))
+);
+
+DECLARE_EVENT_CLASS(cdns3_log_doorbell,
+ TP_PROTO(const char *ep_name, u32 ep_trbaddr),
+ TP_ARGS(ep_name, ep_trbaddr),
+ TP_STRUCT__entry(
+ __string(name, ep_name)
+ __field(u32, ep_trbaddr)
+ ),
+ TP_fast_assign(
+ __assign_str(name, ep_name);
+ __entry->ep_trbaddr = ep_trbaddr;
+ ),
+ TP_printk("%s, ep_trbaddr %08x", __get_str(name),
+ __entry->ep_trbaddr)
+);
+
+DEFINE_EVENT(cdns3_log_doorbell, cdns3_doorbell_ep0,
+ TP_PROTO(const char *ep_name, u32 ep_trbaddr),
+ TP_ARGS(ep_name, ep_trbaddr)
+);
+
+DEFINE_EVENT(cdns3_log_doorbell, cdns3_doorbell_epx,
+ TP_PROTO(const char *ep_name, u32 ep_trbaddr),
+ TP_ARGS(ep_name, ep_trbaddr)
+);
+
+DECLARE_EVENT_CLASS(cdns3_log_usb_irq,
+ TP_PROTO(struct cdns3_device *priv_dev, u32 usb_ists),
+ TP_ARGS(priv_dev, usb_ists),
+ TP_STRUCT__entry(
+ __field(enum usb_device_speed, speed)
+ __field(u32, usb_ists)
+ __dynamic_array(char, str, CDNS3_MSG_MAX)
+ ),
+ TP_fast_assign(
+ __entry->speed = cdns3_get_speed(priv_dev);
+ __entry->usb_ists = usb_ists;
+ ),
+ TP_printk("%s", cdns3_decode_usb_irq(__get_str(str), __entry->speed,
+ __entry->usb_ists))
+);
+
+DEFINE_EVENT(cdns3_log_usb_irq, cdns3_usb_irq,
+ TP_PROTO(struct cdns3_device *priv_dev, u32 usb_ists),
+ TP_ARGS(priv_dev, usb_ists)
+);
+
+DECLARE_EVENT_CLASS(cdns3_log_epx_irq,
+ TP_PROTO(struct cdns3_device *priv_dev, struct cdns3_endpoint *priv_ep),
+ TP_ARGS(priv_dev, priv_ep),
+ TP_STRUCT__entry(
+ __string(ep_name, priv_ep->name)
+ __field(u32, ep_sts)
+ __field(u32, ep_traddr)
+ __dynamic_array(char, str, CDNS3_MSG_MAX)
+ ),
+ TP_fast_assign(
+ __assign_str(ep_name, priv_ep->name);
+ __entry->ep_sts = readl(&priv_dev->regs->ep_sts);
+ __entry->ep_traddr = readl(&priv_dev->regs->ep_traddr);
+ ),
+ TP_printk("%s, ep_traddr: %08x",
+ cdns3_decode_epx_irq(__get_str(str),
+ __get_str(ep_name),
+ __entry->ep_sts),
+ __entry->ep_traddr)
+);
+
+DEFINE_EVENT(cdns3_log_epx_irq, cdns3_epx_irq,
+ TP_PROTO(struct cdns3_device *priv_dev, struct cdns3_endpoint *priv_ep),
+ TP_ARGS(priv_dev, priv_ep)
+);
+
+DECLARE_EVENT_CLASS(cdns3_log_ep0_irq,
+ TP_PROTO(struct cdns3_device *priv_dev, u32 ep_sts),
+ TP_ARGS(priv_dev, ep_sts),
+ TP_STRUCT__entry(
+ __field(int, ep_dir)
+ __field(u32, ep_sts)
+ __dynamic_array(char, str, CDNS3_MSG_MAX)
+ ),
+ TP_fast_assign(
+ __entry->ep_dir = priv_dev->ep0_data_dir;
+ __entry->ep_sts = ep_sts;
+ ),
+ TP_printk("%s", cdns3_decode_ep0_irq(__get_str(str),
+ __entry->ep_dir,
+ __entry->ep_sts))
+);
+
+DEFINE_EVENT(cdns3_log_ep0_irq, cdns3_ep0_irq,
+ TP_PROTO(struct cdns3_device *priv_dev, u32 ep_sts),
+ TP_ARGS(priv_dev, ep_sts)
+);
+
+DECLARE_EVENT_CLASS(cdns3_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)
+ __dynamic_array(char, str, CDNS3_MSG_MAX)
+ ),
+ 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_str(str), CDNS3_MSG_MAX,
+ __entry->bRequestType,
+ __entry->bRequest, __entry->wValue,
+ __entry->wIndex, __entry->wLength)
+ )
+);
+
+DEFINE_EVENT(cdns3_log_ctrl, cdns3_ctrl_req,
+ TP_PROTO(struct usb_ctrlrequest *ctrl),
+ TP_ARGS(ctrl)
+);
+
+DECLARE_EVENT_CLASS(cdns3_log_request,
+ TP_PROTO(struct cdns3_request *req),
+ TP_ARGS(req),
+ TP_STRUCT__entry(
+ __string(name, req->priv_ep->name)
+ __field(struct cdns3_request *, req)
+ __field(void *, buf)
+ __field(unsigned int, actual)
+ __field(unsigned int, length)
+ __field(int, status)
+ __field(int, zero)
+ __field(int, short_not_ok)
+ __field(int, no_interrupt)
+ __field(int, start_trb)
+ __field(int, end_trb)
+ __field(struct cdns3_trb *, start_trb_addr)
+ __field(int, flags)
+ ),
+ TP_fast_assign(
+ __assign_str(name, req->priv_ep->name);
+ __entry->req = req;
+ __entry->buf = req->request.buf;
+ __entry->actual = req->request.actual;
+ __entry->length = req->request.length;
+ __entry->status = req->request.status;
+ __entry->zero = req->request.zero;
+ __entry->short_not_ok = req->request.short_not_ok;
+ __entry->no_interrupt = req->request.no_interrupt;
+ __entry->start_trb = req->start_trb;
+ __entry->end_trb = req->end_trb;
+ __entry->start_trb_addr = req->trb;
+ __entry->flags = req->flags;
+ ),
+ TP_printk("%s: req: %p, req buff %p, length: %u/%u %s%s%s, status: %d,"
+ " trb: [start:%d, end:%d: virt addr %pa], flags:%x ",
+ __get_str(name), __entry->req, __entry->buf, __entry->actual,
+ __entry->length,
+ __entry->zero ? "Z" : "z",
+ __entry->short_not_ok ? "S" : "s",
+ __entry->no_interrupt ? "I" : "i",
+ __entry->status,
+ __entry->start_trb,
+ __entry->end_trb,
+ __entry->start_trb_addr,
+ __entry->flags
+ )
+);
+
+DEFINE_EVENT(cdns3_log_request, cdns3_alloc_request,
+ TP_PROTO(struct cdns3_request *req),
+ TP_ARGS(req)
+);
+
+DEFINE_EVENT(cdns3_log_request, cdns3_free_request,
+ TP_PROTO(struct cdns3_request *req),
+ TP_ARGS(req)
+);
+
+DEFINE_EVENT(cdns3_log_request, cdns3_ep_queue,
+ TP_PROTO(struct cdns3_request *req),
+ TP_ARGS(req)
+);
+
+DEFINE_EVENT(cdns3_log_request, cdns3_ep_dequeue,
+ TP_PROTO(struct cdns3_request *req),
+ TP_ARGS(req)
+);
+
+DEFINE_EVENT(cdns3_log_request, cdns3_gadget_giveback,
+ TP_PROTO(struct cdns3_request *req),
+ TP_ARGS(req)
+);
+
+TRACE_EVENT(cdns3_ep0_queue,
+ TP_PROTO(struct cdns3_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->ep0_data_dir;
+ __entry->length = request->length;
+ ),
+ TP_printk("Queue to ep0%s length: %u", __entry->dir ? "in" : "out",
+ __entry->length)
+);
+
+DECLARE_EVENT_CLASS(cdns3_log_aligned_request,
+ TP_PROTO(struct cdns3_request *priv_req),
+ TP_ARGS(priv_req),
+ TP_STRUCT__entry(
+ __string(name, priv_req->priv_ep->name)
+ __field(struct usb_request *, req)
+ __field(void *, buf)
+ __field(dma_addr_t, dma)
+ __field(void *, aligned_buf)
+ __field(dma_addr_t, aligned_dma)
+ __field(u32, aligned_buf_size)
+ ),
+ TP_fast_assign(
+ __assign_str(name, priv_req->priv_ep->name);
+ __entry->req = &priv_req->request;
+ __entry->buf = priv_req->request.buf;
+ __entry->dma = priv_req->request.dma;
+ __entry->aligned_buf = priv_req->aligned_buf->buf;
+ __entry->aligned_dma = priv_req->aligned_buf->dma;
+ __entry->aligned_buf_size = priv_req->aligned_buf->size;
+ ),
+ TP_printk("%s: req: %p, req buf %p, dma %pad a_buf %p a_dma %pad, size %d",
+ __get_str(name), __entry->req, __entry->buf, &__entry->dma,
+ __entry->aligned_buf, &__entry->aligned_dma,
+ __entry->aligned_buf_size
+ )
+);
+
+DEFINE_EVENT(cdns3_log_aligned_request, cdns3_free_aligned_request,
+ TP_PROTO(struct cdns3_request *req),
+ TP_ARGS(req)
+);
+
+DEFINE_EVENT(cdns3_log_aligned_request, cdns3_prepare_aligned_request,
+ TP_PROTO(struct cdns3_request *req),
+ TP_ARGS(req)
+);
+
+DECLARE_EVENT_CLASS(cdns3_log_trb,
+ TP_PROTO(struct cdns3_endpoint *priv_ep, struct cdns3_trb *trb),
+ TP_ARGS(priv_ep, trb),
+ TP_STRUCT__entry(
+ __string(name, priv_ep->name)
+ __field(struct cdns3_trb *, trb)
+ __field(u32, buffer)
+ __field(u32, length)
+ __field(u32, control)
+ __field(u32, type)
+ ),
+ TP_fast_assign(
+ __assign_str(name, priv_ep->name);
+ __entry->trb = trb;
+ __entry->buffer = trb->buffer;
+ __entry->length = trb->length;
+ __entry->control = trb->control;
+ __entry->type = usb_endpoint_type(priv_ep->endpoint.desc);
+ ),
+ TP_printk("%s: trb %p, dma buf: 0x%08x, size: %ld, burst: %d ctrl: 0x%08x (%s%s%s%s%s%s%s)",
+ __get_str(name), __entry->trb, __entry->buffer,
+ TRB_LEN(__entry->length),
+ (u8)TRB_BURST_LEN_GET(__entry->length),
+ __entry->control,
+ __entry->control & TRB_CYCLE ? "C=1, " : "C=0, ",
+ __entry->control & TRB_TOGGLE ? "T=1, " : "T=0, ",
+ __entry->control & TRB_ISP ? "ISP, " : "",
+ __entry->control & TRB_FIFO_MODE ? "FIFO, " : "",
+ __entry->control & TRB_CHAIN ? "CHAIN, " : "",
+ __entry->control & TRB_IOC ? "IOC, " : "",
+ TRB_FIELD_TO_TYPE(__entry->control) == TRB_NORMAL ? "Normal" : "LINK"
+ )
+);
+
+DEFINE_EVENT(cdns3_log_trb, cdns3_prepare_trb,
+ TP_PROTO(struct cdns3_endpoint *priv_ep, struct cdns3_trb *trb),
+ TP_ARGS(priv_ep, trb)
+);
+
+DEFINE_EVENT(cdns3_log_trb, cdns3_complete_trb,
+ TP_PROTO(struct cdns3_endpoint *priv_ep, struct cdns3_trb *trb),
+ TP_ARGS(priv_ep, trb)
+);
+
+DECLARE_EVENT_CLASS(cdns3_log_ring,
+ TP_PROTO(struct cdns3_endpoint *priv_ep),
+ TP_ARGS(priv_ep),
+ TP_STRUCT__entry(
+ __dynamic_array(u8, ring, TRB_RING_SIZE)
+ __dynamic_array(u8, priv_ep, sizeof(struct cdns3_endpoint))
+ __dynamic_array(char, buffer,
+ (TRBS_PER_SEGMENT * 65) + CDNS3_MSG_MAX)
+ ),
+ TP_fast_assign(
+ memcpy(__get_dynamic_array(priv_ep), priv_ep,
+ sizeof(struct cdns3_endpoint));
+ memcpy(__get_dynamic_array(ring), priv_ep->trb_pool,
+ TRB_RING_SIZE);
+ ),
+
+ TP_printk("%s",
+ cdns3_dbg_ring((struct cdns3_endpoint *)__get_str(priv_ep),
+ (struct cdns3_trb *)__get_str(ring),
+ __get_str(buffer)))
+);
+
+DEFINE_EVENT(cdns3_log_ring, cdns3_ring,
+ TP_PROTO(struct cdns3_endpoint *priv_ep),
+ TP_ARGS(priv_ep)
+);
+
+DECLARE_EVENT_CLASS(cdns3_log_ep,
+ TP_PROTO(struct cdns3_endpoint *priv_ep),
+ TP_ARGS(priv_ep),
+ TP_STRUCT__entry(
+ __string(name, priv_ep->name)
+ __field(unsigned int, maxpacket)
+ __field(unsigned int, maxpacket_limit)
+ __field(unsigned int, max_streams)
+ __field(unsigned int, maxburst)
+ __field(unsigned int, flags)
+ __field(unsigned int, dir)
+ __field(u8, enqueue)
+ __field(u8, dequeue)
+ ),
+ TP_fast_assign(
+ __assign_str(name, priv_ep->name);
+ __entry->maxpacket = priv_ep->endpoint.maxpacket;
+ __entry->maxpacket_limit = priv_ep->endpoint.maxpacket_limit;
+ __entry->max_streams = priv_ep->endpoint.max_streams;
+ __entry->maxburst = priv_ep->endpoint.maxburst;
+ __entry->flags = priv_ep->flags;
+ __entry->dir = priv_ep->dir;
+ __entry->enqueue = priv_ep->enqueue;
+ __entry->dequeue = priv_ep->dequeue;
+ ),
+ TP_printk("%s: mps: %d/%d. streams: %d, burst: %d, enq idx: %d, "
+ "deq idx: %d, flags %s%s%s%s%s%s%s%s, dir: %s",
+ __get_str(name), __entry->maxpacket,
+ __entry->maxpacket_limit, __entry->max_streams,
+ __entry->maxburst, __entry->enqueue,
+ __entry->dequeue,
+ __entry->flags & EP_ENABLED ? "EN | " : "",
+ __entry->flags & EP_STALLED ? "STALLED | " : "",
+ __entry->flags & EP_WEDGE ? "WEDGE | " : "",
+ __entry->flags & EP_TRANSFER_STARTED ? "STARTED | " : "",
+ __entry->flags & EP_UPDATE_EP_TRBADDR ? "UPD TRB | " : "",
+ __entry->flags & EP_PENDING_REQUEST ? "REQ PEN | " : "",
+ __entry->flags & EP_RING_FULL ? "RING FULL |" : "",
+ __entry->flags & EP_CLAIMED ? "CLAIMED " : "",
+ __entry->dir ? "IN" : "OUT"
+ )
+);
+
+DEFINE_EVENT(cdns3_log_ep, cdns3_gadget_ep_enable,
+ TP_PROTO(struct cdns3_endpoint *priv_ep),
+ TP_ARGS(priv_ep)
+);
+
+DEFINE_EVENT(cdns3_log_ep, cdns3_gadget_ep_disable,
+ TP_PROTO(struct cdns3_endpoint *priv_ep),
+ TP_ARGS(priv_ep)
+);
+
+DECLARE_EVENT_CLASS(cdns3_log_request_handled,
+ TP_PROTO(struct cdns3_request *priv_req, int current_index,
+ int handled),
+ TP_ARGS(priv_req, current_index, handled),
+ TP_STRUCT__entry(
+ __field(struct cdns3_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->priv_ep->dequeue;
+ __entry->enqueue_idx = priv_req->priv_ep->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(cdns3_log_request_handled, cdns3_request_handled,
+ TP_PROTO(struct cdns3_request *priv_req, int current_index,
+ int handled),
+ TP_ARGS(priv_req, current_index, handled)
+);
+#endif /* __LINUX_CDNS3_TRACE */
+
+/* this part must be outside header guard */
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE trace
+
+#include <trace/define_trace.h>
diff --git a/drivers/usb/chipidea/Kconfig b/drivers/usb/chipidea/Kconfig
index eb37ebfcb123..ae850b3fddf2 100644
--- a/drivers/usb/chipidea/Kconfig
+++ b/drivers/usb/chipidea/Kconfig
@@ -6,6 +6,7 @@ config USB_CHIPIDEA
select EXTCON
select RESET_CONTROLLER
select USB_ULPI_BUS
+ select USB_ROLE_SWITCH
help
Say Y here if your system has a dual role high speed USB
controller based on ChipIdea silicon IP. It supports:
diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h
index 6a2cc5cd0281..6911aef500e9 100644
--- a/drivers/usb/chipidea/ci.h
+++ b/drivers/usb/chipidea/ci.h
@@ -16,6 +16,7 @@
#include <linux/usb/gadget.h>
#include <linux/usb/otg-fsm.h>
#include <linux/usb/otg.h>
+#include <linux/usb/role.h>
#include <linux/ulpi/interface.h>
/******************************************************************************
@@ -217,6 +218,7 @@ struct ci_hdrc {
ktime_t hr_timeouts[NUM_OTG_FSM_TIMERS];
unsigned enabled_otg_timer_bits;
enum otg_fsm_timer next_otg_timer;
+ struct usb_role_switch *role_switch;
struct work_struct work;
struct workqueue_struct *wq;
@@ -290,6 +292,16 @@ static inline void ci_role_stop(struct ci_hdrc *ci)
ci->roles[role]->stop(ci);
}
+static inline enum usb_role ci_role_to_usb_role(struct ci_hdrc *ci)
+{
+ if (ci->role == CI_ROLE_HOST)
+ return USB_ROLE_HOST;
+ else if (ci->role == CI_ROLE_GADGET && ci->vbus_active)
+ return USB_ROLE_DEVICE;
+ else
+ return USB_ROLE_NONE;
+}
+
/**
* hw_read_id_reg: reads from a identification register
* @ci: the controller
diff --git a/drivers/usb/chipidea/ci_hdrc_msm.c b/drivers/usb/chipidea/ci_hdrc_msm.c
index bb4645a8ca46..af648ba6544d 100644
--- a/drivers/usb/chipidea/ci_hdrc_msm.c
+++ b/drivers/usb/chipidea/ci_hdrc_msm.c
@@ -216,13 +216,13 @@ static int ci_hdrc_msm_probe(struct platform_device *pdev)
ci->rcdev.ops = &ci_hdrc_msm_reset_ops;
ci->rcdev.of_node = pdev->dev.of_node;
ci->rcdev.nr_resets = 2;
- ret = reset_controller_register(&ci->rcdev);
+ ret = devm_reset_controller_register(&pdev->dev, &ci->rcdev);
if (ret)
return ret;
ret = clk_prepare_enable(ci->fs_clk);
if (ret)
- goto err_fs;
+ return ret;
reset_control_assert(reset);
usleep_range(10000, 12000);
@@ -232,7 +232,7 @@ static int ci_hdrc_msm_probe(struct platform_device *pdev)
ret = clk_prepare_enable(ci->core_clk);
if (ret)
- goto err_fs;
+ return ret;
ret = clk_prepare_enable(ci->iface_clk);
if (ret)
@@ -271,8 +271,6 @@ err_mux:
clk_disable_unprepare(ci->iface_clk);
err_iface:
clk_disable_unprepare(ci->core_clk);
-err_fs:
- reset_controller_unregister(&ci->rcdev);
return ret;
}
@@ -284,7 +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);
- reset_controller_unregister(&ci->rcdev);
return 0;
}
diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c
index 26062d610c20..98ee575ee500 100644
--- a/drivers/usb/chipidea/core.c
+++ b/drivers/usb/chipidea/core.c
@@ -600,6 +600,71 @@ static int ci_cable_notifier(struct notifier_block *nb, unsigned long event,
return NOTIFY_DONE;
}
+static enum usb_role ci_usb_role_switch_get(struct device *dev)
+{
+ struct ci_hdrc *ci = dev_get_drvdata(dev);
+ enum usb_role role;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ci->lock, flags);
+ role = ci_role_to_usb_role(ci);
+ spin_unlock_irqrestore(&ci->lock, flags);
+
+ return role;
+}
+
+static int ci_usb_role_switch_set(struct device *dev, enum usb_role role)
+{
+ struct ci_hdrc *ci = dev_get_drvdata(dev);
+ struct ci_hdrc_cable *cable = NULL;
+ enum usb_role current_role = ci_role_to_usb_role(ci);
+ unsigned long flags;
+
+ if (current_role == role)
+ return 0;
+
+ pm_runtime_get_sync(ci->dev);
+ /* Stop current role */
+ spin_lock_irqsave(&ci->lock, flags);
+ if (current_role == USB_ROLE_DEVICE)
+ cable = &ci->platdata->vbus_extcon;
+ else if (current_role == USB_ROLE_HOST)
+ cable = &ci->platdata->id_extcon;
+
+ if (cable) {
+ cable->changed = true;
+ cable->connected = false;
+ ci_irq(ci->irq, ci);
+ spin_unlock_irqrestore(&ci->lock, flags);
+ if (ci->wq && role != USB_ROLE_NONE)
+ flush_workqueue(ci->wq);
+ spin_lock_irqsave(&ci->lock, flags);
+ }
+
+ cable = NULL;
+
+ /* Start target role */
+ if (role == USB_ROLE_DEVICE)
+ cable = &ci->platdata->vbus_extcon;
+ else if (role == USB_ROLE_HOST)
+ cable = &ci->platdata->id_extcon;
+
+ if (cable) {
+ cable->changed = true;
+ cable->connected = true;
+ ci_irq(ci->irq, ci);
+ }
+ spin_unlock_irqrestore(&ci->lock, flags);
+ pm_runtime_put_sync(ci->dev);
+
+ return 0;
+}
+
+static struct usb_role_switch_desc ci_role_switch = {
+ .set = ci_usb_role_switch_set,
+ .get = ci_usb_role_switch_get,
+};
+
static int ci_get_platdata(struct device *dev,
struct ci_hdrc_platform_data *platdata)
{
@@ -726,6 +791,9 @@ static int ci_get_platdata(struct device *dev,
cable->connected = false;
}
+ if (device_property_read_bool(dev, "usb-role-switch"))
+ ci_role_switch.fwnode = dev->fwnode;
+
platdata->pctl = devm_pinctrl_get(dev);
if (!IS_ERR(platdata->pctl)) {
struct pinctrl_state *p;
@@ -903,10 +971,7 @@ static struct attribute *ci_attrs[] = {
&dev_attr_role.attr,
NULL,
};
-
-static const struct attribute_group ci_attr_group = {
- .attrs = ci_attrs,
-};
+ATTRIBUTE_GROUPS(ci);
static int ci_hdrc_probe(struct platform_device *pdev)
{
@@ -1008,7 +1073,6 @@ static int ci_hdrc_probe(struct platform_device *pdev)
ci->irq = platform_get_irq(pdev, 0);
if (ci->irq < 0) {
- dev_err(dev, "missing IRQ\n");
ret = ci->irq;
goto deinit_phy;
}
@@ -1051,6 +1115,15 @@ static int ci_hdrc_probe(struct platform_device *pdev)
}
}
+ if (ci_role_switch.fwnode) {
+ ci->role_switch = usb_role_switch_register(dev,
+ &ci_role_switch);
+ if (IS_ERR(ci->role_switch)) {
+ ret = PTR_ERR(ci->role_switch);
+ goto deinit_otg;
+ }
+ }
+
if (ci->roles[CI_ROLE_HOST] && ci->roles[CI_ROLE_GADGET]) {
if (ci->is_otg) {
ci->role = ci_otg_role(ci);
@@ -1106,15 +1179,12 @@ static int ci_hdrc_probe(struct platform_device *pdev)
device_set_wakeup_capable(&pdev->dev, true);
dbg_create_files(ci);
- ret = sysfs_create_group(&dev->kobj, &ci_attr_group);
- if (ret)
- goto remove_debug;
-
return 0;
-remove_debug:
- dbg_remove_files(ci);
stop:
+ if (ci->role_switch)
+ usb_role_switch_unregister(ci->role_switch);
+deinit_otg:
if (ci->is_otg && ci->roles[CI_ROLE_GADGET])
ci_hdrc_otg_destroy(ci);
deinit_gadget:
@@ -1133,6 +1203,9 @@ static int ci_hdrc_remove(struct platform_device *pdev)
{
struct ci_hdrc *ci = platform_get_drvdata(pdev);
+ if (ci->role_switch)
+ usb_role_switch_unregister(ci->role_switch);
+
if (ci->supports_runtime_pm) {
pm_runtime_get_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
@@ -1140,7 +1213,6 @@ static int ci_hdrc_remove(struct platform_device *pdev)
}
dbg_remove_files(ci);
- sysfs_remove_group(&ci->dev->kobj, &ci_attr_group);
ci_role_destroy(ci);
ci_hdrc_enter_lpm(ci, true);
ci_usb_phy_exit(ci);
@@ -1319,6 +1391,7 @@ static struct platform_driver ci_hdrc_driver = {
.driver = {
.name = "ci_hdrc",
.pm = &ci_pm_ops,
+ .dev_groups = ci_groups,
},
};
diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c
index f25d4827fd49..fbfb02e05c97 100644
--- a/drivers/usb/chipidea/otg.c
+++ b/drivers/usb/chipidea/otg.c
@@ -35,7 +35,7 @@ u32 hw_read_otgsc(struct ci_hdrc *ci, u32 mask)
* detection overwrite OTGSC register value
*/
cable = &ci->platdata->vbus_extcon;
- if (!IS_ERR(cable->edev)) {
+ if (!IS_ERR(cable->edev) || ci->role_switch) {
if (cable->changed)
val |= OTGSC_BSVIS;
else
@@ -53,7 +53,7 @@ u32 hw_read_otgsc(struct ci_hdrc *ci, u32 mask)
}
cable = &ci->platdata->id_extcon;
- if (!IS_ERR(cable->edev)) {
+ if (!IS_ERR(cable->edev) || ci->role_switch) {
if (cable->changed)
val |= OTGSC_IDIS;
else
@@ -83,7 +83,7 @@ void hw_write_otgsc(struct ci_hdrc *ci, u32 mask, u32 data)
struct ci_hdrc_cable *cable;
cable = &ci->platdata->vbus_extcon;
- if (!IS_ERR(cable->edev)) {
+ if (!IS_ERR(cable->edev) || ci->role_switch) {
if (data & mask & OTGSC_BSVIS)
cable->changed = false;
@@ -97,7 +97,7 @@ void hw_write_otgsc(struct ci_hdrc *ci, u32 mask, u32 data)
}
cable = &ci->platdata->id_extcon;
- if (!IS_ERR(cable->edev)) {
+ if (!IS_ERR(cable->edev) || ci->role_switch) {
if (data & mask & OTGSC_IDIS)
cable->changed = false;
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
index 67ad40b0a05b..8f18e7b6cadf 100644
--- a/drivers/usb/chipidea/udc.c
+++ b/drivers/usb/chipidea/udc.c
@@ -1762,12 +1762,11 @@ static int ci_udc_start(struct usb_gadget *gadget,
struct usb_gadget_driver *driver)
{
struct ci_hdrc *ci = container_of(gadget, struct ci_hdrc, gadget);
- int retval = -ENOMEM;
+ int retval;
if (driver->disconnect == NULL)
return -EINVAL;
-
ci->ep0out->ep.desc = &ctrl_endpt_out_desc;
retval = usb_ep_enable(&ci->ep0out->ep);
if (retval)
diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c
index 407a7a6198a2..7fea4999d352 100644
--- a/drivers/usb/class/usblp.c
+++ b/drivers/usb/class/usblp.c
@@ -1082,6 +1082,12 @@ static ssize_t ieee1284_id_show(struct device *dev, struct device_attribute *att
static DEVICE_ATTR_RO(ieee1284_id);
+static struct attribute *usblp_attrs[] = {
+ &dev_attr_ieee1284_id.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(usblp);
+
static int usblp_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
@@ -1156,9 +1162,6 @@ static int usblp_probe(struct usb_interface *intf,
/* Retrieve and store the device ID string. */
usblp_cache_device_id_string(usblp);
- retval = device_create_file(&intf->dev, &dev_attr_ieee1284_id);
- if (retval)
- goto abort_intfdata;
#ifdef DEBUG
usblp_check_status(usblp, 0);
@@ -1189,7 +1192,6 @@ static int usblp_probe(struct usb_interface *intf,
abort_intfdata:
usb_set_intfdata(intf, NULL);
- device_remove_file(&intf->dev, &dev_attr_ieee1284_id);
abort:
kfree(usblp->readbuf);
kfree(usblp->statusbuf);
@@ -1360,8 +1362,6 @@ static void usblp_disconnect(struct usb_interface *intf)
BUG();
}
- device_remove_file(&intf->dev, &dev_attr_ieee1284_id);
-
mutex_lock(&usblp_mutex);
mutex_lock(&usblp->mut);
usblp->present = 0;
@@ -1421,6 +1421,7 @@ static struct usb_driver usblp_driver = {
.suspend = usblp_suspend,
.resume = usblp_resume,
.id_table = usblp_ids,
+ .dev_groups = usblp_groups,
.supports_autosuspend = 1,
};
diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c
index 36858ddd8d9b..dcd7066ffba2 100644
--- a/drivers/usb/class/usbtmc.c
+++ b/drivers/usb/class/usbtmc.c
@@ -1836,17 +1836,14 @@ capability_attribute(device_capabilities);
capability_attribute(usb488_interface_capabilities);
capability_attribute(usb488_device_capabilities);
-static struct attribute *capability_attrs[] = {
+static struct attribute *usbtmc_attrs[] = {
&dev_attr_interface_capabilities.attr,
&dev_attr_device_capabilities.attr,
&dev_attr_usb488_interface_capabilities.attr,
&dev_attr_usb488_device_capabilities.attr,
NULL,
};
-
-static const struct attribute_group capability_attr_grp = {
- .attrs = capability_attrs,
-};
+ATTRIBUTE_GROUPS(usbtmc);
static int usbtmc_ioctl_indicator_pulse(struct usbtmc_device_data *data)
{
@@ -2386,9 +2383,6 @@ static int usbtmc_probe(struct usb_interface *intf,
retcode = get_capabilities(data);
if (retcode)
dev_err(&intf->dev, "can't read capabilities\n");
- else
- retcode = sysfs_create_group(&intf->dev.kobj,
- &capability_attr_grp);
if (data->iin_ep_present) {
/* allocate int urb */
@@ -2435,7 +2429,6 @@ static int usbtmc_probe(struct usb_interface *intf,
return 0;
error_register:
- sysfs_remove_group(&intf->dev.kobj, &capability_attr_grp);
usbtmc_free_int(data);
err_put:
kref_put(&data->kref, usbtmc_delete);
@@ -2448,7 +2441,6 @@ static void usbtmc_disconnect(struct usb_interface *intf)
struct list_head *elem;
usb_deregister_dev(intf, &usbtmc_class);
- sysfs_remove_group(&intf->dev.kobj, &capability_attr_grp);
mutex_lock(&data->io_mutex);
data->zombie = 1;
wake_up_interruptible_all(&data->waitq);
@@ -2554,6 +2546,7 @@ static struct usb_driver usbtmc_driver = {
.resume = usbtmc_resume,
.pre_reset = usbtmc_pre_reset,
.post_reset = usbtmc_post_reset,
+ .dev_groups = usbtmc_groups,
};
module_usb_driver(usbtmc_driver);
diff --git a/drivers/usb/common/Kconfig b/drivers/usb/common/Kconfig
new file mode 100644
index 000000000000..d611477aae41
--- /dev/null
+++ b/drivers/usb/common/Kconfig
@@ -0,0 +1,51 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config USB_COMMON
+ tristate
+
+
+config USB_LED_TRIG
+ bool "USB LED Triggers"
+ depends on LEDS_CLASS && LEDS_TRIGGERS
+ select USB_COMMON
+ help
+ This option adds LED triggers for USB host and/or gadget activity.
+
+ Say Y here if you are working on a system with led-class supported
+ LEDs and you want to use them as activity indicators for USB host or
+ gadget.
+
+config USB_ULPI_BUS
+ tristate "USB ULPI PHY interface support"
+ select USB_COMMON
+ help
+ UTMI+ Low Pin Interface (ULPI) is specification for a commonly used
+ USB 2.0 PHY interface. The ULPI specification defines a standard set
+ of registers that can be used to detect the vendor and product which
+ allows ULPI to be handled as a bus. This module is the driver for that
+ bus.
+
+ The ULPI interfaces (the buses) are registered by the drivers for USB
+ controllers which support ULPI register access and have ULPI PHY
+ attached to them. The ULPI PHY drivers themselves are normal PHY
+ drivers.
+
+ ULPI PHYs provide often functions such as ADP sensing/probing (OTG
+ protocol) and USB charger detection.
+
+ To compile this driver as a module, choose M here: the module will
+ be called ulpi.
+
+config USB_CONN_GPIO
+ tristate "USB GPIO Based Connection Detection Driver"
+ depends on GPIOLIB
+ select USB_ROLE_SWITCH
+ help
+ The driver supports USB role switch between host and device via GPIO
+ based USB cable detection, used typically if an input GPIO is used
+ to detect USB ID pin, and another input GPIO may be also used to detect
+ Vbus pin at the same time, it also can be used to enable/disable
+ device if an input GPIO is only used to detect Vbus pin.
+
+ To compile the driver as a module, choose M here: the module will
+ be called usb-conn-gpio.ko
diff --git a/drivers/usb/common/Makefile b/drivers/usb/common/Makefile
index 0a7c45e85481..8ac4d21ef5c8 100644
--- a/drivers/usb/common/Makefile
+++ b/drivers/usb/common/Makefile
@@ -5,7 +5,9 @@
obj-$(CONFIG_USB_COMMON) += usb-common.o
usb-common-y += common.o
+usb-common-$(CONFIG_TRACING) += debug.o
usb-common-$(CONFIG_USB_LED_TRIG) += led.o
+obj-$(CONFIG_USB_CONN_GPIO) += usb-conn-gpio.o
obj-$(CONFIG_USB_OTG_FSM) += usb-otg-fsm.o
obj-$(CONFIG_USB_ULPI_BUS) += ulpi.o
diff --git a/drivers/usb/common/debug.c b/drivers/usb/common/debug.c
new file mode 100644
index 000000000000..92a986aeaa5d
--- /dev/null
+++ b/drivers/usb/common/debug.c
@@ -0,0 +1,268 @@
+// SPDX-License-Identifier: GPL-2.0
+/**
+ * Common USB debugging functions
+ *
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Authors: Felipe Balbi <balbi@ti.com>,
+ * Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+ */
+
+#include <linux/usb/ch9.h>
+
+static void usb_decode_get_status(__u8 bRequestType, __u16 wIndex,
+ __u16 wLength, char *str, size_t size)
+{
+ switch (bRequestType & USB_RECIP_MASK) {
+ case USB_RECIP_DEVICE:
+ snprintf(str, size, "Get Device Status(Length = %d)", wLength);
+ break;
+ case USB_RECIP_INTERFACE:
+ snprintf(str, size,
+ "Get Interface Status(Intf = %d, Length = %d)",
+ wIndex, wLength);
+ break;
+ case USB_RECIP_ENDPOINT:
+ snprintf(str, size, "Get Endpoint Status(ep%d%s)",
+ wIndex & ~USB_DIR_IN,
+ wIndex & USB_DIR_IN ? "in" : "out");
+ break;
+ }
+}
+
+static const char *usb_decode_device_feature(u16 wValue)
+{
+ switch (wValue) {
+ case USB_DEVICE_SELF_POWERED:
+ return "Self Powered";
+ case USB_DEVICE_REMOTE_WAKEUP:
+ return "Remote Wakeup";
+ case USB_DEVICE_TEST_MODE:
+ return "Test Mode";
+ case USB_DEVICE_U1_ENABLE:
+ return "U1 Enable";
+ case USB_DEVICE_U2_ENABLE:
+ return "U2 Enable";
+ case USB_DEVICE_LTM_ENABLE:
+ return "LTM Enable";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+static const char *usb_decode_test_mode(u16 wIndex)
+{
+ switch (wIndex) {
+ case TEST_J:
+ return ": TEST_J";
+ case TEST_K:
+ return ": TEST_K";
+ case TEST_SE0_NAK:
+ return ": TEST_SE0_NAK";
+ case TEST_PACKET:
+ return ": TEST_PACKET";
+ case TEST_FORCE_EN:
+ return ": TEST_FORCE_EN";
+ default:
+ return ": UNKNOWN";
+ }
+}
+
+static void usb_decode_set_clear_feature(__u8 bRequestType,
+ __u8 bRequest, __u16 wValue,
+ __u16 wIndex, char *str, size_t size)
+{
+ switch (bRequestType & USB_RECIP_MASK) {
+ case USB_RECIP_DEVICE:
+ snprintf(str, size, "%s Device Feature(%s%s)",
+ bRequest == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
+ usb_decode_device_feature(wValue),
+ wValue == USB_DEVICE_TEST_MODE ?
+ usb_decode_test_mode(wIndex) : "");
+ break;
+ case USB_RECIP_INTERFACE:
+ snprintf(str, size, "%s Interface Feature(%s)",
+ bRequest == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
+ wValue == USB_INTRF_FUNC_SUSPEND ?
+ "Function Suspend" : "UNKNOWN");
+ break;
+ case USB_RECIP_ENDPOINT:
+ snprintf(str, size, "%s Endpoint Feature(%s ep%d%s)",
+ bRequest == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
+ wValue == USB_ENDPOINT_HALT ? "Halt" : "UNKNOWN",
+ wIndex & ~USB_DIR_IN,
+ wIndex & USB_DIR_IN ? "in" : "out");
+ break;
+ }
+}
+
+static void usb_decode_set_address(__u16 wValue, char *str, size_t size)
+{
+ snprintf(str, size, "Set Address(Addr = %02x)", wValue);
+}
+
+static void usb_decode_get_set_descriptor(__u8 bRequestType, __u8 bRequest,
+ __u16 wValue, __u16 wIndex,
+ __u16 wLength, char *str, size_t size)
+{
+ char *s;
+
+ switch (wValue >> 8) {
+ case USB_DT_DEVICE:
+ s = "Device";
+ break;
+ case USB_DT_CONFIG:
+ s = "Configuration";
+ break;
+ case USB_DT_STRING:
+ s = "String";
+ break;
+ case USB_DT_INTERFACE:
+ s = "Interface";
+ break;
+ case USB_DT_ENDPOINT:
+ s = "Endpoint";
+ break;
+ case USB_DT_DEVICE_QUALIFIER:
+ s = "Device Qualifier";
+ break;
+ case USB_DT_OTHER_SPEED_CONFIG:
+ s = "Other Speed Config";
+ break;
+ case USB_DT_INTERFACE_POWER:
+ s = "Interface Power";
+ break;
+ case USB_DT_OTG:
+ s = "OTG";
+ break;
+ case USB_DT_DEBUG:
+ s = "Debug";
+ break;
+ case USB_DT_INTERFACE_ASSOCIATION:
+ s = "Interface Association";
+ break;
+ case USB_DT_BOS:
+ s = "BOS";
+ break;
+ case USB_DT_DEVICE_CAPABILITY:
+ s = "Device Capability";
+ break;
+ case USB_DT_PIPE_USAGE:
+ s = "Pipe Usage";
+ break;
+ case USB_DT_SS_ENDPOINT_COMP:
+ s = "SS Endpoint Companion";
+ break;
+ case USB_DT_SSP_ISOC_ENDPOINT_COMP:
+ s = "SSP Isochronous Endpoint Companion";
+ break;
+ default:
+ s = "UNKNOWN";
+ break;
+ }
+
+ snprintf(str, size, "%s %s Descriptor(Index = %d, Length = %d)",
+ bRequest == USB_REQ_GET_DESCRIPTOR ? "Get" : "Set",
+ s, wValue & 0xff, wLength);
+}
+
+static void usb_decode_get_configuration(__u16 wLength, char *str, size_t size)
+{
+ snprintf(str, size, "Get Configuration(Length = %d)", wLength);
+}
+
+static void usb_decode_set_configuration(__u8 wValue, char *str, size_t size)
+{
+ snprintf(str, size, "Set Configuration(Config = %d)", wValue);
+}
+
+static void usb_decode_get_intf(__u16 wIndex, __u16 wLength, char *str,
+ size_t size)
+{
+ snprintf(str, size, "Get Interface(Intf = %d, Length = %d)",
+ wIndex, wLength);
+}
+
+static void usb_decode_set_intf(__u8 wValue, __u16 wIndex, char *str,
+ size_t size)
+{
+ snprintf(str, size, "Set Interface(Intf = %d, Alt.Setting = %d)",
+ wIndex, wValue);
+}
+
+static void usb_decode_synch_frame(__u16 wIndex, __u16 wLength,
+ char *str, size_t size)
+{
+ snprintf(str, size, "Synch Frame(Endpoint = %d, Length = %d)",
+ wIndex, wLength);
+}
+
+static void usb_decode_set_sel(__u16 wLength, char *str, size_t size)
+{
+ snprintf(str, size, "Set SEL(Length = %d)", wLength);
+}
+
+static void usb_decode_set_isoch_delay(__u8 wValue, char *str, size_t size)
+{
+ snprintf(str, size, "Set Isochronous Delay(Delay = %d ns)", wValue);
+}
+
+/**
+ * usb_decode_ctrl - returns a string representation of ctrl request
+ */
+const char *usb_decode_ctrl(char *str, size_t size, __u8 bRequestType,
+ __u8 bRequest, __u16 wValue, __u16 wIndex,
+ __u16 wLength)
+{
+ switch (bRequest) {
+ case USB_REQ_GET_STATUS:
+ usb_decode_get_status(bRequestType, wIndex, wLength, str, size);
+ break;
+ case USB_REQ_CLEAR_FEATURE:
+ case USB_REQ_SET_FEATURE:
+ usb_decode_set_clear_feature(bRequestType, bRequest, wValue,
+ wIndex, str, size);
+ break;
+ case USB_REQ_SET_ADDRESS:
+ usb_decode_set_address(wValue, str, size);
+ break;
+ case USB_REQ_GET_DESCRIPTOR:
+ case USB_REQ_SET_DESCRIPTOR:
+ usb_decode_get_set_descriptor(bRequestType, bRequest, wValue,
+ wIndex, wLength, str, size);
+ break;
+ case USB_REQ_GET_CONFIGURATION:
+ usb_decode_get_configuration(wLength, str, size);
+ break;
+ case USB_REQ_SET_CONFIGURATION:
+ usb_decode_set_configuration(wValue, str, size);
+ break;
+ case USB_REQ_GET_INTERFACE:
+ usb_decode_get_intf(wIndex, wLength, str, size);
+ break;
+ case USB_REQ_SET_INTERFACE:
+ usb_decode_set_intf(wValue, wIndex, str, size);
+ break;
+ case USB_REQ_SYNCH_FRAME:
+ usb_decode_synch_frame(wIndex, wLength, str, size);
+ break;
+ case USB_REQ_SET_SEL:
+ usb_decode_set_sel(wLength, str, size);
+ break;
+ case USB_REQ_SET_ISOCH_DELAY:
+ usb_decode_set_isoch_delay(wValue, str, size);
+ break;
+ default:
+ snprintf(str, size, "%02x %02x %02x %02x %02x %02x %02x %02x",
+ bRequestType, bRequest,
+ (u8)(cpu_to_le16(wValue) & 0xff),
+ (u8)(cpu_to_le16(wValue) >> 8),
+ (u8)(cpu_to_le16(wIndex) & 0xff),
+ (u8)(cpu_to_le16(wIndex) >> 8),
+ (u8)(cpu_to_le16(wLength) & 0xff),
+ (u8)(cpu_to_le16(wLength) >> 8));
+ }
+
+ return str;
+}
+EXPORT_SYMBOL_GPL(usb_decode_ctrl);
diff --git a/drivers/usb/common/usb-conn-gpio.c b/drivers/usb/common/usb-conn-gpio.c
new file mode 100644
index 000000000000..87338f9eb5be
--- /dev/null
+++ b/drivers/usb/common/usb-conn-gpio.c
@@ -0,0 +1,284 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * USB GPIO Based Connection Detection Driver
+ *
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
+ *
+ * Some code borrowed from drivers/extcon/extcon-usb-gpio.c
+ */
+
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/usb/role.h>
+
+#define USB_GPIO_DEB_MS 20 /* ms */
+#define USB_GPIO_DEB_US ((USB_GPIO_DEB_MS) * 1000) /* us */
+
+#define USB_CONN_IRQF \
+ (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT)
+
+struct usb_conn_info {
+ struct device *dev;
+ struct usb_role_switch *role_sw;
+ enum usb_role last_role;
+ struct regulator *vbus;
+ struct delayed_work dw_det;
+ unsigned long debounce_jiffies;
+
+ struct gpio_desc *id_gpiod;
+ struct gpio_desc *vbus_gpiod;
+ int id_irq;
+ int vbus_irq;
+};
+
+/**
+ * "DEVICE" = VBUS and "HOST" = !ID, so we have:
+ * Both "DEVICE" and "HOST" can't be set as active at the same time
+ * so if "HOST" is active (i.e. ID is 0) we keep "DEVICE" inactive
+ * even if VBUS is on.
+ *
+ * Role | ID | VBUS
+ * ------------------------------------
+ * [1] DEVICE | H | H
+ * [2] NONE | H | L
+ * [3] HOST | L | H
+ * [4] HOST | L | L
+ *
+ * In case we have only one of these signals:
+ * - VBUS only - we want to distinguish between [1] and [2], so ID is always 1
+ * - ID only - we want to distinguish between [1] and [4], so VBUS = ID
+ */
+static void usb_conn_detect_cable(struct work_struct *work)
+{
+ struct usb_conn_info *info;
+ enum usb_role role;
+ int id, vbus, ret;
+
+ info = container_of(to_delayed_work(work),
+ struct usb_conn_info, dw_det);
+
+ /* check ID and VBUS */
+ id = info->id_gpiod ?
+ gpiod_get_value_cansleep(info->id_gpiod) : 1;
+ vbus = info->vbus_gpiod ?
+ gpiod_get_value_cansleep(info->vbus_gpiod) : id;
+
+ if (!id)
+ role = USB_ROLE_HOST;
+ else if (vbus)
+ role = USB_ROLE_DEVICE;
+ else
+ role = USB_ROLE_NONE;
+
+ dev_dbg(info->dev, "role %d/%d, gpios: id %d, vbus %d\n",
+ info->last_role, role, id, vbus);
+
+ if (info->last_role == role) {
+ dev_warn(info->dev, "repeated role: %d\n", role);
+ return;
+ }
+
+ if (info->last_role == USB_ROLE_HOST)
+ regulator_disable(info->vbus);
+
+ ret = usb_role_switch_set_role(info->role_sw, role);
+ if (ret)
+ dev_err(info->dev, "failed to set role: %d\n", ret);
+
+ if (role == USB_ROLE_HOST) {
+ ret = regulator_enable(info->vbus);
+ if (ret)
+ dev_err(info->dev, "enable vbus regulator failed\n");
+ }
+
+ info->last_role = role;
+
+ dev_dbg(info->dev, "vbus regulator is %s\n",
+ regulator_is_enabled(info->vbus) ? "enabled" : "disabled");
+}
+
+static void usb_conn_queue_dwork(struct usb_conn_info *info,
+ unsigned long delay)
+{
+ queue_delayed_work(system_power_efficient_wq, &info->dw_det, delay);
+}
+
+static irqreturn_t usb_conn_isr(int irq, void *dev_id)
+{
+ struct usb_conn_info *info = dev_id;
+
+ usb_conn_queue_dwork(info, info->debounce_jiffies);
+
+ return IRQ_HANDLED;
+}
+
+static int usb_conn_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct usb_conn_info *info;
+ int ret = 0;
+
+ info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->dev = dev;
+ info->id_gpiod = devm_gpiod_get_optional(dev, "id", GPIOD_IN);
+ if (IS_ERR(info->id_gpiod))
+ return PTR_ERR(info->id_gpiod);
+
+ info->vbus_gpiod = devm_gpiod_get_optional(dev, "vbus", GPIOD_IN);
+ if (IS_ERR(info->vbus_gpiod))
+ return PTR_ERR(info->vbus_gpiod);
+
+ if (!info->id_gpiod && !info->vbus_gpiod) {
+ dev_err(dev, "failed to get gpios\n");
+ return -ENODEV;
+ }
+
+ if (info->id_gpiod)
+ ret = gpiod_set_debounce(info->id_gpiod, USB_GPIO_DEB_US);
+ if (!ret && info->vbus_gpiod)
+ ret = gpiod_set_debounce(info->vbus_gpiod, USB_GPIO_DEB_US);
+ if (ret < 0)
+ info->debounce_jiffies = msecs_to_jiffies(USB_GPIO_DEB_MS);
+
+ INIT_DELAYED_WORK(&info->dw_det, usb_conn_detect_cable);
+
+ info->vbus = devm_regulator_get(dev, "vbus");
+ if (IS_ERR(info->vbus)) {
+ dev_err(dev, "failed to get vbus\n");
+ return PTR_ERR(info->vbus);
+ }
+
+ info->role_sw = usb_role_switch_get(dev);
+ if (IS_ERR(info->role_sw)) {
+ if (PTR_ERR(info->role_sw) != -EPROBE_DEFER)
+ dev_err(dev, "failed to get role switch\n");
+
+ return PTR_ERR(info->role_sw);
+ }
+
+ if (info->id_gpiod) {
+ info->id_irq = gpiod_to_irq(info->id_gpiod);
+ if (info->id_irq < 0) {
+ dev_err(dev, "failed to get ID IRQ\n");
+ ret = info->id_irq;
+ goto put_role_sw;
+ }
+
+ ret = devm_request_threaded_irq(dev, info->id_irq, NULL,
+ usb_conn_isr, USB_CONN_IRQF,
+ pdev->name, info);
+ if (ret < 0) {
+ dev_err(dev, "failed to request ID IRQ\n");
+ goto put_role_sw;
+ }
+ }
+
+ if (info->vbus_gpiod) {
+ info->vbus_irq = gpiod_to_irq(info->vbus_gpiod);
+ if (info->vbus_irq < 0) {
+ dev_err(dev, "failed to get VBUS IRQ\n");
+ ret = info->vbus_irq;
+ goto put_role_sw;
+ }
+
+ ret = devm_request_threaded_irq(dev, info->vbus_irq, NULL,
+ usb_conn_isr, USB_CONN_IRQF,
+ pdev->name, info);
+ if (ret < 0) {
+ dev_err(dev, "failed to request VBUS IRQ\n");
+ goto put_role_sw;
+ }
+ }
+
+ platform_set_drvdata(pdev, info);
+
+ /* Perform initial detection */
+ usb_conn_queue_dwork(info, 0);
+
+ return 0;
+
+put_role_sw:
+ usb_role_switch_put(info->role_sw);
+ return ret;
+}
+
+static int usb_conn_remove(struct platform_device *pdev)
+{
+ struct usb_conn_info *info = platform_get_drvdata(pdev);
+
+ cancel_delayed_work_sync(&info->dw_det);
+
+ if (info->last_role == USB_ROLE_HOST)
+ regulator_disable(info->vbus);
+
+ usb_role_switch_put(info->role_sw);
+
+ return 0;
+}
+
+static int __maybe_unused usb_conn_suspend(struct device *dev)
+{
+ struct usb_conn_info *info = dev_get_drvdata(dev);
+
+ if (info->id_gpiod)
+ disable_irq(info->id_irq);
+ if (info->vbus_gpiod)
+ disable_irq(info->vbus_irq);
+
+ pinctrl_pm_select_sleep_state(dev);
+
+ return 0;
+}
+
+static int __maybe_unused usb_conn_resume(struct device *dev)
+{
+ struct usb_conn_info *info = dev_get_drvdata(dev);
+
+ pinctrl_pm_select_default_state(dev);
+
+ if (info->id_gpiod)
+ enable_irq(info->id_irq);
+ if (info->vbus_gpiod)
+ enable_irq(info->vbus_irq);
+
+ usb_conn_queue_dwork(info, 0);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(usb_conn_pm_ops,
+ usb_conn_suspend, usb_conn_resume);
+
+static const struct of_device_id usb_conn_dt_match[] = {
+ { .compatible = "gpio-usb-b-connector", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, usb_conn_dt_match);
+
+static struct platform_driver usb_conn_driver = {
+ .probe = usb_conn_probe,
+ .remove = usb_conn_remove,
+ .driver = {
+ .name = "usb-conn-gpio",
+ .pm = &usb_conn_pm_ops,
+ .of_match_table = usb_conn_dt_match,
+ },
+};
+
+module_platform_driver(usb_conn_driver);
+
+MODULE_AUTHOR("Chunfeng Yun <chunfeng.yun@mediatek.com>");
+MODULE_DESCRIPTION("USB GPIO based connection detection driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c
index 9d6cb709ca7b..151a74a54386 100644
--- a/drivers/usb/core/config.c
+++ b/drivers/usb/core/config.c
@@ -921,7 +921,7 @@ int usb_get_bos_descriptor(struct usb_device *dev)
struct usb_bos_descriptor *bos;
struct usb_dev_cap_header *cap;
struct usb_ssp_cap_descriptor *ssp_cap;
- unsigned char *buffer;
+ unsigned char *buffer, *buffer0;
int length, total_len, num, i, ssac;
__u8 cap_type;
int ret;
@@ -966,10 +966,12 @@ int usb_get_bos_descriptor(struct usb_device *dev)
ret = -ENOMSG;
goto err;
}
+
+ buffer0 = buffer;
total_len -= length;
+ buffer += length;
for (i = 0; i < num; i++) {
- buffer += length;
cap = (struct usb_dev_cap_header *)buffer;
if (total_len < sizeof(*cap) || total_len < cap->bLength) {
@@ -983,8 +985,6 @@ int usb_get_bos_descriptor(struct usb_device *dev)
break;
}
- total_len -= length;
-
if (cap->bDescriptorType != USB_DT_DEVICE_CAPABILITY) {
dev_warn(ddev, "descriptor type invalid, skip\n");
continue;
@@ -1019,7 +1019,11 @@ int usb_get_bos_descriptor(struct usb_device *dev)
default:
break;
}
+
+ total_len -= length;
+ buffer += length;
}
+ dev->bos->desc->wTotalLength = cpu_to_le16(buffer - buffer0);
return 0;
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index e3696601e43b..3f899552f6e3 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -44,10 +44,19 @@
#include "usb.h"
+#ifdef CONFIG_PM
+#define MAYBE_CAP_SUSPEND USBDEVFS_CAP_SUSPEND
+#else
+#define MAYBE_CAP_SUSPEND 0
+#endif
+
#define USB_MAXBUS 64
#define USB_DEVICE_MAX (USB_MAXBUS * 128)
#define USB_SG_SIZE 16384 /* split-size for large txs */
+/* Mutual exclusion for ps->list in resume vs. release and remove */
+static DEFINE_MUTEX(usbfs_mutex);
+
struct usb_dev_state {
struct list_head list; /* state list */
struct usb_device *dev;
@@ -57,14 +66,17 @@ struct usb_dev_state {
struct list_head async_completed;
struct list_head memory_list;
wait_queue_head_t wait; /* wake up if a request completed */
+ wait_queue_head_t wait_for_resume; /* wake up upon runtime resume */
unsigned int discsignr;
struct pid *disc_pid;
const struct cred *cred;
sigval_t disccontext;
unsigned long ifclaimed;
u32 disabled_bulk_eps;
- bool privileges_dropped;
unsigned long interface_allowed_mask;
+ int not_yet_resumed;
+ bool suspend_allowed;
+ bool privileges_dropped;
};
struct usb_memory {
@@ -694,9 +706,7 @@ static void driver_disconnect(struct usb_interface *intf)
destroy_async_on_interface(ps, ifnum);
}
-/* The following routines are merely placeholders. There is no way
- * to inform a user task about suspend or resumes.
- */
+/* We don't care about suspend/resume of claimed interfaces */
static int driver_suspend(struct usb_interface *intf, pm_message_t msg)
{
return 0;
@@ -707,12 +717,32 @@ static int driver_resume(struct usb_interface *intf)
return 0;
}
+/* The following routines apply to the entire device, not interfaces */
+void usbfs_notify_suspend(struct usb_device *udev)
+{
+ /* We don't need to handle this */
+}
+
+void usbfs_notify_resume(struct usb_device *udev)
+{
+ struct usb_dev_state *ps;
+
+ /* Protect against simultaneous remove or release */
+ mutex_lock(&usbfs_mutex);
+ list_for_each_entry(ps, &udev->filelist, list) {
+ WRITE_ONCE(ps->not_yet_resumed, 0);
+ wake_up_all(&ps->wait_for_resume);
+ }
+ mutex_unlock(&usbfs_mutex);
+}
+
struct usb_driver usbfs_driver = {
.name = "usbfs",
.probe = driver_probe,
.disconnect = driver_disconnect,
.suspend = driver_suspend,
.resume = driver_resume,
+ .supports_autosuspend = 1,
};
static int claimintf(struct usb_dev_state *ps, unsigned int ifnum)
@@ -991,9 +1021,12 @@ static int usbdev_open(struct inode *inode, struct file *file)
INIT_LIST_HEAD(&ps->async_completed);
INIT_LIST_HEAD(&ps->memory_list);
init_waitqueue_head(&ps->wait);
+ init_waitqueue_head(&ps->wait_for_resume);
ps->disc_pid = get_pid(task_pid(current));
ps->cred = get_current_cred();
smp_wmb();
+
+ /* Can't race with resume; the device is already active */
list_add_tail(&ps->list, &dev->filelist);
file->private_data = ps;
usb_unlock_device(dev);
@@ -1019,7 +1052,10 @@ static int usbdev_release(struct inode *inode, struct file *file)
usb_lock_device(dev);
usb_hub_release_all_ports(dev, ps);
+ /* Protect against simultaneous resume */
+ mutex_lock(&usbfs_mutex);
list_del_init(&ps->list);
+ mutex_unlock(&usbfs_mutex);
for (ifnum = 0; ps->ifclaimed && ifnum < 8*sizeof(ps->ifclaimed);
ifnum++) {
@@ -1027,7 +1063,8 @@ static int usbdev_release(struct inode *inode, struct file *file)
releaseintf(ps, ifnum);
}
destroy_all_async(ps);
- usb_autosuspend_device(dev);
+ if (!ps->suspend_allowed)
+ usb_autosuspend_device(dev);
usb_unlock_device(dev);
usb_put_dev(dev);
put_pid(ps->disc_pid);
@@ -1621,7 +1658,8 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb
if (as->usbm)
num_sgs = 0;
- u += sizeof(struct async) + sizeof(struct urb) + uurb->buffer_length +
+ u += sizeof(struct async) + sizeof(struct urb) +
+ (as->usbm ? 0 : uurb->buffer_length) +
num_sgs * sizeof(struct scatterlist);
ret = usbfs_increase_memory_usage(u);
if (ret)
@@ -2273,7 +2311,8 @@ static int proc_get_capabilities(struct usb_dev_state *ps, void __user *arg)
caps = USBDEVFS_CAP_ZERO_PACKET | USBDEVFS_CAP_NO_PACKET_SIZE_LIM |
USBDEVFS_CAP_REAP_AFTER_DISCONNECT | USBDEVFS_CAP_MMAP |
- USBDEVFS_CAP_DROP_PRIVILEGES | USBDEVFS_CAP_CONNINFO_EX;
+ USBDEVFS_CAP_DROP_PRIVILEGES |
+ USBDEVFS_CAP_CONNINFO_EX | MAYBE_CAP_SUSPEND;
if (!ps->dev->bus->no_stop_on_short)
caps |= USBDEVFS_CAP_BULK_CONTINUATION;
if (ps->dev->bus->sg_tablesize)
@@ -2376,6 +2415,47 @@ static int proc_drop_privileges(struct usb_dev_state *ps, void __user *arg)
return 0;
}
+static int proc_forbid_suspend(struct usb_dev_state *ps)
+{
+ int ret = 0;
+
+ if (ps->suspend_allowed) {
+ ret = usb_autoresume_device(ps->dev);
+ if (ret == 0)
+ ps->suspend_allowed = false;
+ else if (ret != -ENODEV)
+ ret = -EIO;
+ }
+ return ret;
+}
+
+static int proc_allow_suspend(struct usb_dev_state *ps)
+{
+ if (!connected(ps))
+ return -ENODEV;
+
+ WRITE_ONCE(ps->not_yet_resumed, 1);
+ if (!ps->suspend_allowed) {
+ usb_autosuspend_device(ps->dev);
+ ps->suspend_allowed = true;
+ }
+ return 0;
+}
+
+static int proc_wait_for_resume(struct usb_dev_state *ps)
+{
+ int ret;
+
+ usb_unlock_device(ps->dev);
+ ret = wait_event_interruptible(ps->wait_for_resume,
+ READ_ONCE(ps->not_yet_resumed) == 0);
+ usb_lock_device(ps->dev);
+
+ if (ret != 0)
+ return -EINTR;
+ return proc_forbid_suspend(ps);
+}
+
/*
* NOTE: All requests here that have interface numbers as parameters
* are assuming that somehow the configuration has been prevented from
@@ -2570,6 +2650,15 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd,
case USBDEVFS_GET_SPEED:
ret = ps->dev->speed;
break;
+ case USBDEVFS_FORBID_SUSPEND:
+ ret = proc_forbid_suspend(ps);
+ break;
+ case USBDEVFS_ALLOW_SUSPEND:
+ ret = proc_allow_suspend(ps);
+ break;
+ case USBDEVFS_WAIT_FOR_RESUME:
+ ret = proc_wait_for_resume(ps);
+ break;
}
/* Handle variable-length commands */
@@ -2643,15 +2732,20 @@ static void usbdev_remove(struct usb_device *udev)
{
struct usb_dev_state *ps;
+ /* Protect against simultaneous resume */
+ mutex_lock(&usbfs_mutex);
while (!list_empty(&udev->filelist)) {
ps = list_entry(udev->filelist.next, struct usb_dev_state, list);
destroy_all_async(ps);
wake_up_all(&ps->wait);
+ WRITE_ONCE(ps->not_yet_resumed, 0);
+ wake_up_all(&ps->wait_for_resume);
list_del_init(&ps->list);
if (ps->discsignr)
kill_pid_usb_asyncio(ps->discsignr, EPIPE, ps->disccontext,
ps->disc_pid, ps->cred);
}
+ mutex_unlock(&usbfs_mutex);
}
static int usbdev_notify(struct notifier_block *self,
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index ebcadaad89d1..2b27d232d7a7 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -892,6 +892,7 @@ int usb_register_device_driver(struct usb_device_driver *new_udriver,
new_udriver->drvwrap.driver.probe = usb_probe_device;
new_udriver->drvwrap.driver.remove = usb_unbind_device;
new_udriver->drvwrap.driver.owner = owner;
+ new_udriver->drvwrap.driver.dev_groups = new_udriver->dev_groups;
retval = driver_register(&new_udriver->drvwrap.driver);
@@ -954,6 +955,7 @@ int usb_register_driver(struct usb_driver *new_driver, struct module *owner,
new_driver->drvwrap.driver.remove = usb_unbind_interface;
new_driver->drvwrap.driver.owner = owner;
new_driver->drvwrap.driver.mod_name = mod_name;
+ new_driver->drvwrap.driver.dev_groups = new_driver->dev_groups;
spin_lock_init(&new_driver->dynids.lock);
INIT_LIST_HEAD(&new_driver->dynids.list);
diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c
index 1ac9c1e5f773..38f8b3e31762 100644
--- a/drivers/usb/core/generic.c
+++ b/drivers/usb/core/generic.c
@@ -257,6 +257,8 @@ static int generic_suspend(struct usb_device *udev, pm_message_t msg)
else
rc = usb_port_suspend(udev, msg);
+ if (rc == 0)
+ usbfs_notify_suspend(udev);
return rc;
}
@@ -273,6 +275,9 @@ static int generic_resume(struct usb_device *udev, pm_message_t msg)
rc = hcd_bus_resume(udev, msg);
else
rc = usb_port_resume(udev, msg);
+
+ if (rc == 0)
+ usbfs_notify_resume(udev);
return rc;
}
diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c
index 7537681355f6..9e26b0143a59 100644
--- a/drivers/usb/core/hcd-pci.c
+++ b/drivers/usb/core/hcd-pci.c
@@ -393,8 +393,7 @@ static inline void powermac_set_asic(struct pci_dev *pci_dev, int enable)
static int check_root_hub_suspended(struct device *dev)
{
- struct pci_dev *pci_dev = to_pci_dev(dev);
- struct usb_hcd *hcd = pci_get_drvdata(pci_dev);
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
if (HCD_RH_RUNNING(hcd)) {
dev_warn(dev, "Root hub is not suspended\n");
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 8592c0344fe8..f225eaa98ff8 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -1249,9 +1249,6 @@ EXPORT_SYMBOL_GPL(usb_hcd_unlink_urb_from_ep);
* To support host controllers with limited dma capabilities we provide dma
* bounce buffers. This feature can be enabled by initializing
* hcd->localmem_pool using usb_hcd_setup_local_mem().
- * For this to work properly the host controller code must first use the
- * function dma_declare_coherent_memory() to point out which memory area
- * that should be used for dma allocations.
*
* The initialized hcd->localmem_pool then tells the usb code to allocate all
* data for dma using the genalloc API.
@@ -2191,6 +2188,9 @@ int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg)
hcd->state = HC_STATE_RESUMING;
status = hcd->driver->bus_resume(hcd);
clear_bit(HCD_FLAG_WAKEUP_PENDING, &hcd->flags);
+ if (status == 0)
+ status = usb_phy_roothub_calibrate(hcd->phy_roothub);
+
if (status == 0) {
struct usb_device *udev;
int port1;
@@ -2454,7 +2454,6 @@ struct usb_hcd *__usb_create_hcd(const struct hc_driver *driver,
hcd->self.controller = dev;
hcd->self.sysdev = sysdev;
hcd->self.bus_name = bus_name;
- hcd->self.uses_dma = (sysdev->dma_mask != NULL);
timer_setup(&hcd->rh_timer, rh_timer_func, 0);
#ifdef CONFIG_PM
@@ -2764,6 +2763,10 @@ int usb_add_hcd(struct usb_hcd *hcd,
}
hcd->rh_pollable = 1;
+ retval = usb_phy_roothub_calibrate(hcd->phy_roothub);
+ if (retval)
+ goto err_hcd_driver_setup;
+
/* NOTE: root hub and controller capabilities may not be the same */
if (device_can_wakeup(hcd->self.controller)
&& device_can_wakeup(&hcd->self.root_hub->dev))
diff --git a/drivers/usb/core/phy.c b/drivers/usb/core/phy.c
index 7580493b867a..fb1588e7c282 100644
--- a/drivers/usb/core/phy.c
+++ b/drivers/usb/core/phy.c
@@ -151,6 +151,27 @@ err_out:
}
EXPORT_SYMBOL_GPL(usb_phy_roothub_set_mode);
+int usb_phy_roothub_calibrate(struct usb_phy_roothub *phy_roothub)
+{
+ struct usb_phy_roothub *roothub_entry;
+ struct list_head *head;
+ int err;
+
+ if (!phy_roothub)
+ return 0;
+
+ head = &phy_roothub->list;
+
+ list_for_each_entry(roothub_entry, head, list) {
+ err = phy_calibrate(roothub_entry->phy);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_phy_roothub_calibrate);
+
int usb_phy_roothub_power_on(struct usb_phy_roothub *phy_roothub)
{
struct usb_phy_roothub *roothub_entry;
diff --git a/drivers/usb/core/phy.h b/drivers/usb/core/phy.h
index dad564e2d2d4..20a267cd986b 100644
--- a/drivers/usb/core/phy.h
+++ b/drivers/usb/core/phy.h
@@ -18,6 +18,7 @@ int usb_phy_roothub_exit(struct usb_phy_roothub *phy_roothub);
int usb_phy_roothub_set_mode(struct usb_phy_roothub *phy_roothub,
enum phy_mode mode);
+int usb_phy_roothub_calibrate(struct usb_phy_roothub *phy_roothub);
int usb_phy_roothub_power_on(struct usb_phy_roothub *phy_roothub);
void usb_phy_roothub_power_off(struct usb_phy_roothub *phy_roothub);
diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c
index 1a06a4b5fbb1..bbbb35fa639f 100644
--- a/drivers/usb/core/port.c
+++ b/drivers/usb/core/port.c
@@ -285,6 +285,14 @@ static int usb_port_runtime_suspend(struct device *dev)
}
#endif
+static void usb_port_shutdown(struct device *dev)
+{
+ struct usb_port *port_dev = to_usb_port(dev);
+
+ if (port_dev->child)
+ usb_disable_usb2_hardware_lpm(port_dev->child);
+}
+
static const struct dev_pm_ops usb_port_pm_ops = {
#ifdef CONFIG_PM
.runtime_suspend = usb_port_runtime_suspend,
@@ -301,6 +309,7 @@ struct device_type usb_port_device_type = {
static struct device_driver usb_port_driver = {
.name = "usb",
.owner = THIS_MODULE,
+ .shutdown = usb_port_shutdown,
};
static int link_peers(struct usb_port *left, struct usb_port *right)
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 0ab8738047da..f16c26dc079d 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -933,228 +933,6 @@ void usb_free_coherent(struct usb_device *dev, size_t size, void *addr,
}
EXPORT_SYMBOL_GPL(usb_free_coherent);
-/**
- * usb_buffer_map - create DMA mapping(s) for an urb
- * @urb: urb whose transfer_buffer/setup_packet will be mapped
- *
- * URB_NO_TRANSFER_DMA_MAP is added to urb->transfer_flags if the operation
- * succeeds. If the device is connected to this system through a non-DMA
- * controller, this operation always succeeds.
- *
- * This call would normally be used for an urb which is reused, perhaps
- * as the target of a large periodic transfer, with usb_buffer_dmasync()
- * calls to synchronize memory and dma state.
- *
- * Reverse the effect of this call with usb_buffer_unmap().
- *
- * Return: Either %NULL (indicating no buffer could be mapped), or @urb.
- *
- */
-#if 0
-struct urb *usb_buffer_map(struct urb *urb)
-{
- struct usb_bus *bus;
- struct device *controller;
-
- if (!urb
- || !urb->dev
- || !(bus = urb->dev->bus)
- || !(controller = bus->sysdev))
- return NULL;
-
- if (controller->dma_mask) {
- urb->transfer_dma = dma_map_single(controller,
- urb->transfer_buffer, urb->transfer_buffer_length,
- usb_pipein(urb->pipe)
- ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
- /* FIXME generic api broken like pci, can't report errors */
- /* if (urb->transfer_dma == DMA_ADDR_INVALID) return 0; */
- } else
- urb->transfer_dma = ~0;
- urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
- return urb;
-}
-EXPORT_SYMBOL_GPL(usb_buffer_map);
-#endif /* 0 */
-
-/* XXX DISABLED, no users currently. If you wish to re-enable this
- * XXX please determine whether the sync is to transfer ownership of
- * XXX the buffer from device to cpu or vice verse, and thusly use the
- * XXX appropriate _for_{cpu,device}() method. -DaveM
- */
-#if 0
-
-/**
- * usb_buffer_dmasync - synchronize DMA and CPU view of buffer(s)
- * @urb: urb whose transfer_buffer/setup_packet will be synchronized
- */
-void usb_buffer_dmasync(struct urb *urb)
-{
- struct usb_bus *bus;
- struct device *controller;
-
- if (!urb
- || !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)
- || !urb->dev
- || !(bus = urb->dev->bus)
- || !(controller = bus->sysdev))
- return;
-
- if (controller->dma_mask) {
- dma_sync_single_for_cpu(controller,
- urb->transfer_dma, urb->transfer_buffer_length,
- usb_pipein(urb->pipe)
- ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
- if (usb_pipecontrol(urb->pipe))
- dma_sync_single_for_cpu(controller,
- urb->setup_dma,
- sizeof(struct usb_ctrlrequest),
- DMA_TO_DEVICE);
- }
-}
-EXPORT_SYMBOL_GPL(usb_buffer_dmasync);
-#endif
-
-/**
- * usb_buffer_unmap - free DMA mapping(s) for an urb
- * @urb: urb whose transfer_buffer will be unmapped
- *
- * Reverses the effect of usb_buffer_map().
- */
-#if 0
-void usb_buffer_unmap(struct urb *urb)
-{
- struct usb_bus *bus;
- struct device *controller;
-
- if (!urb
- || !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)
- || !urb->dev
- || !(bus = urb->dev->bus)
- || !(controller = bus->sysdev))
- return;
-
- if (controller->dma_mask) {
- dma_unmap_single(controller,
- urb->transfer_dma, urb->transfer_buffer_length,
- usb_pipein(urb->pipe)
- ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
- }
- urb->transfer_flags &= ~URB_NO_TRANSFER_DMA_MAP;
-}
-EXPORT_SYMBOL_GPL(usb_buffer_unmap);
-#endif /* 0 */
-
-#if 0
-/**
- * usb_buffer_map_sg - create scatterlist DMA mapping(s) for an endpoint
- * @dev: device to which the scatterlist will be mapped
- * @is_in: mapping transfer direction
- * @sg: the scatterlist to map
- * @nents: the number of entries in the scatterlist
- *
- * Return: Either < 0 (indicating no buffers could be mapped), or the
- * number of DMA mapping array entries in the scatterlist.
- *
- * Note:
- * The caller is responsible for placing the resulting DMA addresses from
- * the scatterlist into URB transfer buffer pointers, and for setting the
- * URB_NO_TRANSFER_DMA_MAP transfer flag in each of those URBs.
- *
- * Top I/O rates come from queuing URBs, instead of waiting for each one
- * to complete before starting the next I/O. This is particularly easy
- * to do with scatterlists. Just allocate and submit one URB for each DMA
- * mapping entry returned, stopping on the first error or when all succeed.
- * Better yet, use the usb_sg_*() calls, which do that (and more) for you.
- *
- * This call would normally be used when translating scatterlist requests,
- * rather than usb_buffer_map(), since on some hardware (with IOMMUs) it
- * may be able to coalesce mappings for improved I/O efficiency.
- *
- * Reverse the effect of this call with usb_buffer_unmap_sg().
- */
-int usb_buffer_map_sg(const struct usb_device *dev, int is_in,
- struct scatterlist *sg, int nents)
-{
- struct usb_bus *bus;
- struct device *controller;
-
- if (!dev
- || !(bus = dev->bus)
- || !(controller = bus->sysdev)
- || !controller->dma_mask)
- return -EINVAL;
-
- /* FIXME generic api broken like pci, can't report errors */
- return dma_map_sg(controller, sg, nents,
- is_in ? DMA_FROM_DEVICE : DMA_TO_DEVICE) ? : -ENOMEM;
-}
-EXPORT_SYMBOL_GPL(usb_buffer_map_sg);
-#endif
-
-/* XXX DISABLED, no users currently. If you wish to re-enable this
- * XXX please determine whether the sync is to transfer ownership of
- * XXX the buffer from device to cpu or vice verse, and thusly use the
- * XXX appropriate _for_{cpu,device}() method. -DaveM
- */
-#if 0
-
-/**
- * usb_buffer_dmasync_sg - synchronize DMA and CPU view of scatterlist buffer(s)
- * @dev: device to which the scatterlist will be mapped
- * @is_in: mapping transfer direction
- * @sg: the scatterlist to synchronize
- * @n_hw_ents: the positive return value from usb_buffer_map_sg
- *
- * Use this when you are re-using a scatterlist's data buffers for
- * another USB request.
- */
-void usb_buffer_dmasync_sg(const struct usb_device *dev, int is_in,
- struct scatterlist *sg, int n_hw_ents)
-{
- struct usb_bus *bus;
- struct device *controller;
-
- if (!dev
- || !(bus = dev->bus)
- || !(controller = bus->sysdev)
- || !controller->dma_mask)
- return;
-
- dma_sync_sg_for_cpu(controller, sg, n_hw_ents,
- is_in ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
-}
-EXPORT_SYMBOL_GPL(usb_buffer_dmasync_sg);
-#endif
-
-#if 0
-/**
- * usb_buffer_unmap_sg - free DMA mapping(s) for a scatterlist
- * @dev: device to which the scatterlist will be mapped
- * @is_in: mapping transfer direction
- * @sg: the scatterlist to unmap
- * @n_hw_ents: the positive return value from usb_buffer_map_sg
- *
- * Reverses the effect of usb_buffer_map_sg().
- */
-void usb_buffer_unmap_sg(const struct usb_device *dev, int is_in,
- struct scatterlist *sg, int n_hw_ents)
-{
- struct usb_bus *bus;
- struct device *controller;
-
- if (!dev
- || !(bus = dev->bus)
- || !(controller = bus->sysdev)
- || !controller->dma_mask)
- return;
-
- dma_unmap_sg(controller, sg, n_hw_ents,
- is_in ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
-}
-EXPORT_SYMBOL_GPL(usb_buffer_unmap_sg);
-#endif
-
/*
* Notifications of device and interface registration
*/
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index 0c9fde5ad052..cf4783cf661a 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -95,6 +95,9 @@ extern int usb_runtime_idle(struct device *dev);
extern int usb_enable_usb2_hardware_lpm(struct usb_device *udev);
extern int usb_disable_usb2_hardware_lpm(struct usb_device *udev);
+extern void usbfs_notify_suspend(struct usb_device *udev);
+extern void usbfs_notify_resume(struct usb_device *udev);
+
#else
static inline int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c
index bff48a8a1984..6be10e496e10 100644
--- a/drivers/usb/dwc2/gadget.c
+++ b/drivers/usb/dwc2/gadget.c
@@ -3224,14 +3224,15 @@ static void kill_all_requests(struct dwc2_hsotg *hsotg,
struct dwc2_hsotg_ep *ep,
int result)
{
- struct dwc2_hsotg_req *req, *treq;
unsigned int size;
ep->req = NULL;
- list_for_each_entry_safe(req, treq, &ep->queue, queue)
- dwc2_hsotg_complete_request(hsotg, ep, req,
- result);
+ while (!list_empty(&ep->queue)) {
+ struct dwc2_hsotg_req *req = get_ep_head(ep);
+
+ dwc2_hsotg_complete_request(hsotg, ep, req, result);
+ }
if (!hsotg->dedicated_fifos)
return;
diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index 111787a137ee..81afe553aa66 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -5062,13 +5062,13 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg)
dwc2_hc_driver.reset_device = dwc2_reset_device;
}
+ if (hsotg->params.host_dma)
+ dwc2_hc_driver.flags |= HCD_DMA;
+
hcd = usb_create_hcd(&dwc2_hc_driver, hsotg->dev, dev_name(hsotg->dev));
if (!hcd)
goto error1;
- if (!hsotg->params.host_dma)
- hcd->self.uses_dma = 0;
-
hcd->has_tt = 1;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c
index 55f841a54015..31e090ac9f1e 100644
--- a/drivers/usb/dwc2/params.c
+++ b/drivers/usb/dwc2/params.c
@@ -404,10 +404,7 @@ static void dwc2_get_device_properties(struct dwc2_hsotg *hsotg)
device_property_read_u32(hsotg->dev, "g-np-tx-fifo-size",
&p->g_np_tx_fifo_size);
- num = device_property_read_u32_array(hsotg->dev,
- "g-tx-fifo-size",
- NULL, 0);
-
+ num = device_property_count_u32(hsotg->dev, "g-tx-fifo-size");
if (num > 0) {
num = min(num, 15);
memset(p->g_tx_fifo_size, 0,
diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c
index 80fd3c6dcd1c..3c6ce09a6db5 100644
--- a/drivers/usb/dwc2/platform.c
+++ b/drivers/usb/dwc2/platform.c
@@ -407,10 +407,8 @@ static int dwc2_driver_probe(struct platform_device *dev)
spin_lock_init(&hsotg->lock);
hsotg->irq = platform_get_irq(dev, 0);
- if (hsotg->irq < 0) {
- dev_err(&dev->dev, "missing IRQ resource\n");
+ if (hsotg->irq < 0)
return hsotg->irq;
- }
dev_dbg(hsotg->dev, "registering common handler for irq%d\n",
hsotg->irq);
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index c9bb93a2c81e..999ce5e84d3c 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -168,7 +168,6 @@ static void __dwc3_set_mode(struct work_struct *work)
otg_set_vbus(dwc->usb2_phy->otg, true);
phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_HOST);
phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_HOST);
- phy_calibrate(dwc->usb2_generic_phy);
}
break;
case DWC3_GCTL_PRTCAP_DEVICE:
@@ -252,12 +251,25 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc)
reg |= DWC3_DCTL_CSFTRST;
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+ /*
+ * For DWC_usb31 controller 1.90a and later, the DCTL.CSFRST bit
+ * is cleared only after all the clocks are synchronized. This can
+ * take a little more than 50ms. Set the polling rate at 20ms
+ * for 10 times instead.
+ */
+ if (dwc3_is_usb31(dwc) && dwc->revision >= DWC3_USB31_REVISION_190A)
+ retries = 10;
+
do {
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
if (!(reg & DWC3_DCTL_CSFTRST))
goto done;
- udelay(1);
+ if (dwc3_is_usb31(dwc) &&
+ dwc->revision >= DWC3_USB31_REVISION_190A)
+ msleep(20);
+ else
+ udelay(1);
} while (--retries);
phy_exit(dwc->usb3_generic_phy);
@@ -267,11 +279,11 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc)
done:
/*
- * For DWC_usb31 controller, once DWC3_DCTL_CSFTRST bit is cleared,
- * we must wait at least 50ms before accessing the PHY domain
- * (synchronization delay). DWC_usb31 programming guide section 1.3.2.
+ * For DWC_usb31 controller 1.80a and prior, once DCTL.CSFRST bit
+ * is cleared, we must wait at least 50ms before accessing the PHY
+ * domain (synchronization delay).
*/
- if (dwc3_is_usb31(dwc))
+ if (dwc3_is_usb31(dwc) && dwc->revision <= DWC3_USB31_REVISION_180A)
msleep(50);
return 0;
@@ -686,8 +698,7 @@ static void dwc3_core_exit(struct dwc3 *dwc)
usb_phy_set_suspend(dwc->usb3_phy, 1);
phy_power_off(dwc->usb2_generic_phy);
phy_power_off(dwc->usb3_generic_phy);
- clk_bulk_disable(dwc->num_clks, dwc->clks);
- clk_bulk_unprepare(dwc->num_clks, dwc->clks);
+ clk_bulk_disable_unprepare(dwc->num_clks, dwc->clks);
reset_control_assert(dwc->reset);
}
@@ -813,8 +824,7 @@ static void dwc3_set_incr_burst_type(struct dwc3 *dwc)
* result = 1, means INCRx burst mode supported.
* result > 1, means undefined length burst mode supported.
*/
- ntype = device_property_read_u32_array(dev,
- "snps,incr-burst-type-adjustment", NULL, 0);
+ ntype = device_property_count_u32(dev, "snps,incr-burst-type-adjustment");
if (ntype <= 0)
return;
@@ -1166,7 +1176,6 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
dev_err(dev, "failed to initialize host\n");
return ret;
}
- phy_calibrate(dwc->usb2_generic_phy);
break;
case USB_DR_MODE_OTG:
INIT_WORK(&dwc->drd_work, __dwc3_set_mode);
@@ -1310,8 +1319,7 @@ static void dwc3_get_properties(struct dwc3 *dwc)
dwc->lpm_nyet_threshold = lpm_nyet_threshold;
dwc->tx_de_emphasis = tx_de_emphasis;
- dwc->hird_threshold = hird_threshold
- | (dwc->is_utmi_l1_suspend << 4);
+ dwc->hird_threshold = hird_threshold;
dwc->rx_thr_num_pkt_prd = rx_thr_num_pkt_prd;
dwc->rx_max_burst_prd = rx_max_burst_prd;
@@ -1436,7 +1444,7 @@ static int dwc3_probe(struct platform_device *pdev)
if (dev->of_node) {
dwc->num_clks = ARRAY_SIZE(dwc3_core_clks);
- ret = clk_bulk_get(dev, dwc->num_clks, dwc->clks);
+ ret = devm_clk_bulk_get(dev, dwc->num_clks, dwc->clks);
if (ret == -EPROBE_DEFER)
return ret;
/*
@@ -1449,16 +1457,12 @@ static int dwc3_probe(struct platform_device *pdev)
ret = reset_control_deassert(dwc->reset);
if (ret)
- goto put_clks;
+ return ret;
- ret = clk_bulk_prepare(dwc->num_clks, dwc->clks);
+ ret = clk_bulk_prepare_enable(dwc->num_clks, dwc->clks);
if (ret)
goto assert_reset;
- ret = clk_bulk_enable(dwc->num_clks, dwc->clks);
- if (ret)
- goto unprepare_clks;
-
if (!dwc3_core_is_valid(dwc)) {
dev_err(dwc->dev, "this is not a DesignWare USB3 DRD Core\n");
ret = -ENODEV;
@@ -1531,13 +1535,9 @@ err1:
pm_runtime_disable(&pdev->dev);
disable_clks:
- clk_bulk_disable(dwc->num_clks, dwc->clks);
-unprepare_clks:
- clk_bulk_unprepare(dwc->num_clks, dwc->clks);
+ clk_bulk_disable_unprepare(dwc->num_clks, dwc->clks);
assert_reset:
reset_control_assert(dwc->reset);
-put_clks:
- clk_bulk_put(dwc->num_clks, dwc->clks);
return ret;
}
@@ -1560,7 +1560,6 @@ static int dwc3_remove(struct platform_device *pdev)
dwc3_free_event_buffers(dwc);
dwc3_free_scratch_buffers(dwc);
- clk_bulk_put(dwc->num_clks, dwc->clks);
return 0;
}
@@ -1574,14 +1573,10 @@ static int dwc3_core_init_for_resume(struct dwc3 *dwc)
if (ret)
return ret;
- ret = clk_bulk_prepare(dwc->num_clks, dwc->clks);
+ ret = clk_bulk_prepare_enable(dwc->num_clks, dwc->clks);
if (ret)
goto assert_reset;
- ret = clk_bulk_enable(dwc->num_clks, dwc->clks);
- if (ret)
- goto unprepare_clks;
-
ret = dwc3_core_init(dwc);
if (ret)
goto disable_clks;
@@ -1589,9 +1584,7 @@ static int dwc3_core_init_for_resume(struct dwc3 *dwc)
return 0;
disable_clks:
- clk_bulk_disable(dwc->num_clks, dwc->clks);
-unprepare_clks:
- clk_bulk_unprepare(dwc->num_clks, dwc->clks);
+ clk_bulk_disable_unprepare(dwc->num_clks, dwc->clks);
assert_reset:
reset_control_assert(dwc->reset);
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 3dd783b889cb..1c8b349379af 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -1137,6 +1137,8 @@ struct dwc3 {
#define DWC3_USB31_REVISION_120A (0x3132302a | DWC3_REVISION_IS_DWC31)
#define DWC3_USB31_REVISION_160A (0x3136302a | DWC3_REVISION_IS_DWC31)
#define DWC3_USB31_REVISION_170A (0x3137302a | DWC3_REVISION_IS_DWC31)
+#define DWC3_USB31_REVISION_180A (0x3138302a | DWC3_REVISION_IS_DWC31)
+#define DWC3_USB31_REVISION_190A (0x3139302a | DWC3_REVISION_IS_DWC31)
u32 version_type;
diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h
index 068259fdfb0c..9baabed87d61 100644
--- a/drivers/usb/dwc3/debug.h
+++ b/drivers/usb/dwc3/debug.h
@@ -246,258 +246,6 @@ static inline const char *dwc3_gadget_event_string(char *str, size_t size,
return str;
}
-static inline void dwc3_decode_get_status(__u8 t, __u16 i, __u16 l, char *str,
- size_t size)
-{
- switch (t & USB_RECIP_MASK) {
- case USB_RECIP_DEVICE:
- snprintf(str, size, "Get Device Status(Length = %d)", l);
- break;
- case USB_RECIP_INTERFACE:
- snprintf(str, size, "Get Interface Status(Intf = %d, Length = %d)",
- i, l);
- break;
- case USB_RECIP_ENDPOINT:
- snprintf(str, size, "Get Endpoint Status(ep%d%s)",
- i & ~USB_DIR_IN,
- i & USB_DIR_IN ? "in" : "out");
- break;
- }
-}
-
-static inline void dwc3_decode_set_clear_feature(__u8 t, __u8 b, __u16 v,
- __u16 i, char *str, size_t size)
-{
- switch (t & USB_RECIP_MASK) {
- case USB_RECIP_DEVICE:
- snprintf(str, size, "%s Device Feature(%s%s)",
- b == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
- ({char *s;
- switch (v) {
- case USB_DEVICE_SELF_POWERED:
- s = "Self Powered";
- break;
- case USB_DEVICE_REMOTE_WAKEUP:
- s = "Remote Wakeup";
- break;
- case USB_DEVICE_TEST_MODE:
- s = "Test Mode";
- break;
- case USB_DEVICE_U1_ENABLE:
- s = "U1 Enable";
- break;
- case USB_DEVICE_U2_ENABLE:
- s = "U2 Enable";
- break;
- case USB_DEVICE_LTM_ENABLE:
- s = "LTM Enable";
- break;
- default:
- s = "UNKNOWN";
- } s; }),
- v == USB_DEVICE_TEST_MODE ?
- ({ char *s;
- switch (i) {
- case TEST_J:
- s = ": TEST_J";
- break;
- case TEST_K:
- s = ": TEST_K";
- break;
- case TEST_SE0_NAK:
- s = ": TEST_SE0_NAK";
- break;
- case TEST_PACKET:
- s = ": TEST_PACKET";
- break;
- case TEST_FORCE_EN:
- s = ": TEST_FORCE_EN";
- break;
- default:
- s = ": UNKNOWN";
- } s; }) : "");
- break;
- case USB_RECIP_INTERFACE:
- snprintf(str, size, "%s Interface Feature(%s)",
- b == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
- v == USB_INTRF_FUNC_SUSPEND ?
- "Function Suspend" : "UNKNOWN");
- break;
- case USB_RECIP_ENDPOINT:
- snprintf(str, size, "%s Endpoint Feature(%s ep%d%s)",
- b == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
- v == USB_ENDPOINT_HALT ? "Halt" : "UNKNOWN",
- i & ~USB_DIR_IN,
- i & USB_DIR_IN ? "in" : "out");
- break;
- }
-}
-
-static inline void dwc3_decode_set_address(__u16 v, char *str, size_t size)
-{
- snprintf(str, size, "Set Address(Addr = %02x)", v);
-}
-
-static inline void dwc3_decode_get_set_descriptor(__u8 t, __u8 b, __u16 v,
- __u16 i, __u16 l, char *str, size_t size)
-{
- snprintf(str, size, "%s %s Descriptor(Index = %d, Length = %d)",
- b == USB_REQ_GET_DESCRIPTOR ? "Get" : "Set",
- ({ char *s;
- switch (v >> 8) {
- case USB_DT_DEVICE:
- s = "Device";
- break;
- case USB_DT_CONFIG:
- s = "Configuration";
- break;
- case USB_DT_STRING:
- s = "String";
- break;
- case USB_DT_INTERFACE:
- s = "Interface";
- break;
- case USB_DT_ENDPOINT:
- s = "Endpoint";
- break;
- case USB_DT_DEVICE_QUALIFIER:
- s = "Device Qualifier";
- break;
- case USB_DT_OTHER_SPEED_CONFIG:
- s = "Other Speed Config";
- break;
- case USB_DT_INTERFACE_POWER:
- s = "Interface Power";
- break;
- case USB_DT_OTG:
- s = "OTG";
- break;
- case USB_DT_DEBUG:
- s = "Debug";
- break;
- case USB_DT_INTERFACE_ASSOCIATION:
- s = "Interface Association";
- break;
- case USB_DT_BOS:
- s = "BOS";
- break;
- case USB_DT_DEVICE_CAPABILITY:
- s = "Device Capability";
- break;
- case USB_DT_PIPE_USAGE:
- s = "Pipe Usage";
- break;
- case USB_DT_SS_ENDPOINT_COMP:
- s = "SS Endpoint Companion";
- break;
- case USB_DT_SSP_ISOC_ENDPOINT_COMP:
- s = "SSP Isochronous Endpoint Companion";
- break;
- default:
- s = "UNKNOWN";
- break;
- } s; }), v & 0xff, l);
-}
-
-
-static inline void dwc3_decode_get_configuration(__u16 l, char *str,
- size_t size)
-{
- snprintf(str, size, "Get Configuration(Length = %d)", l);
-}
-
-static inline void dwc3_decode_set_configuration(__u8 v, char *str, size_t size)
-{
- snprintf(str, size, "Set Configuration(Config = %d)", v);
-}
-
-static inline void dwc3_decode_get_intf(__u16 i, __u16 l, char *str,
- size_t size)
-{
- snprintf(str, size, "Get Interface(Intf = %d, Length = %d)", i, l);
-}
-
-static inline void dwc3_decode_set_intf(__u8 v, __u16 i, char *str, size_t size)
-{
- snprintf(str, size, "Set Interface(Intf = %d, Alt.Setting = %d)", i, v);
-}
-
-static inline void dwc3_decode_synch_frame(__u16 i, __u16 l, char *str,
- size_t size)
-{
- snprintf(str, size, "Synch Frame(Endpoint = %d, Length = %d)", i, l);
-}
-
-static inline void dwc3_decode_set_sel(__u16 l, char *str, size_t size)
-{
- snprintf(str, size, "Set SEL(Length = %d)", l);
-}
-
-static inline void dwc3_decode_set_isoch_delay(__u8 v, char *str, size_t size)
-{
- snprintf(str, size, "Set Isochronous Delay(Delay = %d ns)", v);
-}
-
-/**
- * dwc3_decode_ctrl - returns a string represetion of ctrl request
- */
-static inline const char *dwc3_decode_ctrl(char *str, size_t size,
- __u8 bRequestType, __u8 bRequest, __u16 wValue, __u16 wIndex,
- __u16 wLength)
-{
- switch (bRequest) {
- case USB_REQ_GET_STATUS:
- dwc3_decode_get_status(bRequestType, wIndex, wLength, str,
- size);
- break;
- case USB_REQ_CLEAR_FEATURE:
- case USB_REQ_SET_FEATURE:
- dwc3_decode_set_clear_feature(bRequestType, bRequest, wValue,
- wIndex, str, size);
- break;
- case USB_REQ_SET_ADDRESS:
- dwc3_decode_set_address(wValue, str, size);
- break;
- case USB_REQ_GET_DESCRIPTOR:
- case USB_REQ_SET_DESCRIPTOR:
- dwc3_decode_get_set_descriptor(bRequestType, bRequest, wValue,
- wIndex, wLength, str, size);
- break;
- case USB_REQ_GET_CONFIGURATION:
- dwc3_decode_get_configuration(wLength, str, size);
- break;
- case USB_REQ_SET_CONFIGURATION:
- dwc3_decode_set_configuration(wValue, str, size);
- break;
- case USB_REQ_GET_INTERFACE:
- dwc3_decode_get_intf(wIndex, wLength, str, size);
- break;
- case USB_REQ_SET_INTERFACE:
- dwc3_decode_set_intf(wValue, wIndex, str, size);
- break;
- case USB_REQ_SYNCH_FRAME:
- dwc3_decode_synch_frame(wIndex, wLength, str, size);
- break;
- case USB_REQ_SET_SEL:
- dwc3_decode_set_sel(wLength, str, size);
- break;
- case USB_REQ_SET_ISOCH_DELAY:
- dwc3_decode_set_isoch_delay(wValue, str, size);
- break;
- default:
- snprintf(str, size, "%02x %02x %02x %02x %02x %02x %02x %02x",
- bRequestType, bRequest,
- cpu_to_le16(wValue) & 0xff,
- cpu_to_le16(wValue) >> 8,
- cpu_to_le16(wIndex) & 0xff,
- cpu_to_le16(wIndex) >> 8,
- cpu_to_le16(wLength) & 0xff,
- cpu_to_le16(wLength) >> 8);
- }
-
- return str;
-}
-
/**
* dwc3_ep_event_string - returns event name
* @event: then event code
diff --git a/drivers/usb/dwc3/dwc3-keystone.c b/drivers/usb/dwc3/dwc3-keystone.c
index cbee5fb9b9fb..1e14a6f4884b 100644
--- a/drivers/usb/dwc3/dwc3-keystone.c
+++ b/drivers/usb/dwc3/dwc3-keystone.c
@@ -81,7 +81,6 @@ static int kdwc3_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct device_node *node = pdev->dev.of_node;
struct dwc3_keystone *kdwc;
- struct resource *res;
int error, irq;
kdwc = devm_kzalloc(dev, sizeof(*kdwc), GFP_KERNEL);
@@ -92,8 +91,7 @@ static int kdwc3_probe(struct platform_device *pdev)
kdwc->dev = dev;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- kdwc->usbss = devm_ioremap_resource(dev, res);
+ kdwc->usbss = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(kdwc->usbss))
return PTR_ERR(kdwc->usbss);
@@ -112,7 +110,6 @@ static int kdwc3_probe(struct platform_device *pdev)
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
- dev_err(&pdev->dev, "missing irq\n");
error = irq;
goto err_irq;
}
diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c
index bca7e92a10e9..8a3ec1a951fe 100644
--- a/drivers/usb/dwc3/dwc3-meson-g12a.c
+++ b/drivers/usb/dwc3/dwc3-meson-g12a.c
@@ -386,7 +386,6 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
void __iomem *base;
- struct resource *res;
enum phy_mode otg_id;
int ret, i, irq;
@@ -394,8 +393,7 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev)
if (!priv)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(dev, res);
+ base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
@@ -564,7 +562,13 @@ static int __maybe_unused dwc3_meson_g12a_runtime_resume(struct device *dev)
static int __maybe_unused dwc3_meson_g12a_suspend(struct device *dev)
{
struct dwc3_meson_g12a *priv = dev_get_drvdata(dev);
- int i;
+ int i, ret;
+
+ if (priv->vbus && priv->otg_phy_mode == PHY_MODE_USB_HOST) {
+ ret = regulator_disable(priv->vbus);
+ if (ret)
+ return ret;
+ }
for (i = 0 ; i < PHY_COUNT ; ++i) {
phy_power_off(priv->phys[i]);
@@ -599,6 +603,12 @@ static int __maybe_unused dwc3_meson_g12a_resume(struct device *dev)
return ret;
}
+ if (priv->vbus && priv->otg_phy_mode == PHY_MODE_USB_HOST) {
+ ret = regulator_enable(priv->vbus);
+ if (ret)
+ return ret;
+ }
+
return 0;
}
diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c
index ed8b86517675..8c3de2d258bf 100644
--- a/drivers/usb/dwc3/dwc3-omap.c
+++ b/drivers/usb/dwc3/dwc3-omap.c
@@ -14,7 +14,6 @@
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
-#include <linux/platform_data/dwc3-omap.h>
#include <linux/pm_runtime.h>
#include <linux/dma-mapping.h>
#include <linux/ioport.h>
@@ -106,6 +105,12 @@
#define USBOTGSS_UTMI_OTG_CTRL_SESSVALID BIT(2)
#define USBOTGSS_UTMI_OTG_CTRL_VBUSVALID BIT(1)
+enum dwc3_omap_utmi_mode {
+ DWC3_OMAP_UTMI_MODE_UNKNOWN = 0,
+ DWC3_OMAP_UTMI_MODE_HW,
+ DWC3_OMAP_UTMI_MODE_SW,
+};
+
struct dwc3_omap {
struct device *dev;
@@ -446,7 +451,6 @@ static int dwc3_omap_probe(struct platform_device *pdev)
struct device_node *node = pdev->dev.of_node;
struct dwc3_omap *omap;
- struct resource *res;
struct device *dev = &pdev->dev;
struct regulator *vbus_reg = NULL;
@@ -469,13 +473,10 @@ static int dwc3_omap_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, omap);
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(dev, "missing IRQ resource: %d\n", irq);
+ if (irq < 0)
return irq;
- }
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(dev, res);
+ base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
diff --git a/drivers/usb/dwc3/dwc3-st.c b/drivers/usb/dwc3/dwc3-st.c
index 16081383c401..c682420f25ca 100644
--- a/drivers/usb/dwc3/dwc3-st.c
+++ b/drivers/usb/dwc3/dwc3-st.c
@@ -255,24 +255,26 @@ static int st_dwc3_probe(struct platform_device *pdev)
if (!child) {
dev_err(&pdev->dev, "failed to find dwc3 core node\n");
ret = -ENODEV;
- goto undo_softreset;
+ goto err_node_put;
}
/* Allocate and initialize the core */
ret = of_platform_populate(node, NULL, NULL, dev);
if (ret) {
dev_err(dev, "failed to add dwc3 core\n");
- goto undo_softreset;
+ goto err_node_put;
}
child_pdev = of_find_device_by_node(child);
if (!child_pdev) {
dev_err(dev, "failed to find dwc3 core device\n");
ret = -ENODEV;
- goto undo_softreset;
+ goto err_node_put;
}
dwc3_data->dr_mode = usb_get_dr_mode(&child_pdev->dev);
+ of_node_put(child);
+ of_dev_put(child_pdev);
/*
* Configure the USB port as device or host according to the static
@@ -292,6 +294,8 @@ static int st_dwc3_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, dwc3_data);
return 0;
+err_node_put:
+ of_node_put(child);
undo_softreset:
reset_control_assert(dwc3_data->rstc_rst);
undo_powerdown:
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 173f5329d3d9..8adb59f8e4f1 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -2078,6 +2078,26 @@ static void dwc3_gadget_config_params(struct usb_gadget *g,
{
struct dwc3 *dwc = gadget_to_dwc(g);
+ params->besl_baseline = USB_DEFAULT_BESL_UNSPECIFIED;
+ params->besl_deep = USB_DEFAULT_BESL_UNSPECIFIED;
+
+ /* Recommended BESL */
+ if (!dwc->dis_enblslpm_quirk) {
+ /*
+ * If the recommended BESL baseline is 0 or if the BESL deep is
+ * less than 2, Microsoft's Windows 10 host usb stack will issue
+ * a usb reset immediately after it receives the extended BOS
+ * descriptor and the enumeration will fail. To maintain
+ * compatibility with the Windows' usb stack, let's set the
+ * recommended BESL baseline to 1 and clamp the BESL deep to be
+ * within 2 to 15.
+ */
+ params->besl_baseline = 1;
+ if (dwc->is_utmi_l1_suspend)
+ params->besl_deep =
+ clamp_t(u8, dwc->hird_threshold, 2, 15);
+ }
+
/* U1 Device exit Latency */
if (dwc->dis_u1_entry_quirk)
params->bU1devExitLat = 0;
@@ -2868,7 +2888,8 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
reg &= ~(DWC3_DCTL_HIRD_THRES_MASK | DWC3_DCTL_L1_HIBER_EN);
- reg |= DWC3_DCTL_HIRD_THRES(dwc->hird_threshold);
+ reg |= DWC3_DCTL_HIRD_THRES(dwc->hird_threshold |
+ (dwc->is_utmi_l1_suspend << 4));
/*
* When dwc3 revisions >= 2.40a, LPM Erratum is enabled and
@@ -3318,7 +3339,6 @@ int dwc3_gadget_init(struct dwc3 *dwc)
dwc->gadget.speed = USB_SPEED_UNKNOWN;
dwc->gadget.sg_supported = true;
dwc->gadget.name = "dwc3-gadget";
- dwc->gadget.is_otg = dwc->dr_mode == USB_DR_MODE_OTG;
dwc->gadget.lpm_capable = true;
/*
diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c
index f55947294f7c..8deea8c91e03 100644
--- a/drivers/usb/dwc3/host.c
+++ b/drivers/usb/dwc3/host.c
@@ -85,7 +85,7 @@ int dwc3_host_init(struct dwc3 *dwc)
DWC3_XHCI_RESOURCES_NUM);
if (ret) {
dev_err(dwc->dev, "couldn't add resources to xHCI device\n");
- goto err1;
+ goto err;
}
memset(props, 0, sizeof(struct property_entry) * ARRAY_SIZE(props));
@@ -112,37 +112,23 @@ int dwc3_host_init(struct dwc3 *dwc)
ret = platform_device_add_properties(xhci, props);
if (ret) {
dev_err(dwc->dev, "failed to add properties to xHCI\n");
- goto err1;
+ goto err;
}
}
- phy_create_lookup(dwc->usb2_generic_phy, "usb2-phy",
- dev_name(dwc->dev));
- phy_create_lookup(dwc->usb3_generic_phy, "usb3-phy",
- dev_name(dwc->dev));
-
ret = platform_device_add(xhci);
if (ret) {
dev_err(dwc->dev, "failed to register xHCI device\n");
- goto err2;
+ goto err;
}
return 0;
-err2:
- phy_remove_lookup(dwc->usb2_generic_phy, "usb2-phy",
- dev_name(dwc->dev));
- phy_remove_lookup(dwc->usb3_generic_phy, "usb3-phy",
- dev_name(dwc->dev));
-err1:
+err:
platform_device_put(xhci);
return ret;
}
void dwc3_host_exit(struct dwc3 *dwc)
{
- phy_remove_lookup(dwc->usb2_generic_phy, "usb2-phy",
- dev_name(dwc->dev));
- phy_remove_lookup(dwc->usb3_generic_phy, "usb3-phy",
- dev_name(dwc->dev));
platform_device_unregister(dwc->xhci);
}
diff --git a/drivers/usb/dwc3/trace.h b/drivers/usb/dwc3/trace.h
index 818a63da1a44..9edff17111f7 100644
--- a/drivers/usb/dwc3/trace.h
+++ b/drivers/usb/dwc3/trace.h
@@ -86,7 +86,7 @@ DECLARE_EVENT_CLASS(dwc3_log_ctrl,
__entry->wIndex = le16_to_cpu(ctrl->wIndex);
__entry->wLength = le16_to_cpu(ctrl->wLength);
),
- TP_printk("%s", dwc3_decode_ctrl(__get_str(str), DWC3_MSG_MAX,
+ TP_printk("%s", usb_decode_ctrl(__get_str(str), DWC3_MSG_MAX,
__entry->bRequestType,
__entry->bRequest, __entry->wValue,
__entry->wIndex, __entry->wLength)
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 76883ff4f5bb..d516e8d6cd7f 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -612,6 +612,7 @@ static int bos_desc(struct usb_composite_dev *cdev)
struct usb_ext_cap_descriptor *usb_ext;
struct usb_dcd_config_params dcd_config_params;
struct usb_bos_descriptor *bos = cdev->req->buf;
+ unsigned int besl = 0;
bos->bLength = USB_DT_BOS_SIZE;
bos->bDescriptorType = USB_DT_BOS;
@@ -619,6 +620,29 @@ static int bos_desc(struct usb_composite_dev *cdev)
bos->wTotalLength = cpu_to_le16(USB_DT_BOS_SIZE);
bos->bNumDeviceCaps = 0;
+ /* Get Controller configuration */
+ if (cdev->gadget->ops->get_config_params) {
+ cdev->gadget->ops->get_config_params(cdev->gadget,
+ &dcd_config_params);
+ } else {
+ dcd_config_params.besl_baseline =
+ USB_DEFAULT_BESL_UNSPECIFIED;
+ dcd_config_params.besl_deep =
+ USB_DEFAULT_BESL_UNSPECIFIED;
+ dcd_config_params.bU1devExitLat =
+ USB_DEFAULT_U1_DEV_EXIT_LAT;
+ dcd_config_params.bU2DevExitLat =
+ cpu_to_le16(USB_DEFAULT_U2_DEV_EXIT_LAT);
+ }
+
+ if (dcd_config_params.besl_baseline != USB_DEFAULT_BESL_UNSPECIFIED)
+ besl = USB_BESL_BASELINE_VALID |
+ USB_SET_BESL_BASELINE(dcd_config_params.besl_baseline);
+
+ if (dcd_config_params.besl_deep != USB_DEFAULT_BESL_UNSPECIFIED)
+ besl |= USB_BESL_DEEP_VALID |
+ USB_SET_BESL_DEEP(dcd_config_params.besl_deep);
+
/*
* A SuperSpeed device shall include the USB2.0 extension descriptor
* and shall support LPM when operating in USB2.0 HS mode.
@@ -629,7 +653,8 @@ static int bos_desc(struct usb_composite_dev *cdev)
usb_ext->bLength = USB_DT_USB_EXT_CAP_SIZE;
usb_ext->bDescriptorType = USB_DT_DEVICE_CAPABILITY;
usb_ext->bDevCapabilityType = USB_CAP_TYPE_EXT;
- usb_ext->bmAttributes = cpu_to_le32(USB_LPM_SUPPORT | USB_BESL_SUPPORT);
+ usb_ext->bmAttributes = cpu_to_le32(USB_LPM_SUPPORT |
+ USB_BESL_SUPPORT | besl);
/*
* The Superspeed USB Capability descriptor shall be implemented by all
@@ -650,17 +675,6 @@ static int bos_desc(struct usb_composite_dev *cdev)
USB_HIGH_SPEED_OPERATION |
USB_5GBPS_OPERATION);
ss_cap->bFunctionalitySupport = USB_LOW_SPEED_OPERATION;
-
- /* Get Controller configuration */
- if (cdev->gadget->ops->get_config_params) {
- cdev->gadget->ops->get_config_params(cdev->gadget,
- &dcd_config_params);
- } else {
- dcd_config_params.bU1devExitLat =
- USB_DEFAULT_U1_DEV_EXIT_LAT;
- dcd_config_params.bU2DevExitLat =
- cpu_to_le16(USB_DEFAULT_U2_DEV_EXIT_LAT);
- }
ss_cap->bU1devExitLat = dcd_config_params.bU1devExitLat;
ss_cap->bU2DevExitLat = dcd_config_params.bU2DevExitLat;
}
diff --git a/drivers/usb/gadget/udc/aspeed-vhub/core.c b/drivers/usb/gadget/udc/aspeed-vhub/core.c
index db3628be38c0..90b134d5dca9 100644
--- a/drivers/usb/gadget/udc/aspeed-vhub/core.c
+++ b/drivers/usb/gadget/udc/aspeed-vhub/core.c
@@ -65,14 +65,16 @@ void ast_vhub_done(struct ast_vhub_ep *ep, struct ast_vhub_req *req,
void ast_vhub_nuke(struct ast_vhub_ep *ep, int status)
{
struct ast_vhub_req *req;
-
- EPDBG(ep, "Nuking\n");
+ int count = 0;
/* Beware, lock will be dropped & req-acquired by done() */
while (!list_empty(&ep->queue)) {
req = list_first_entry(&ep->queue, struct ast_vhub_req, queue);
ast_vhub_done(ep, req, status);
+ count++;
}
+ if (count)
+ EPDBG(ep, "Nuked %d request(s)\n", count);
}
struct usb_request *ast_vhub_alloc_request(struct usb_ep *u_ep,
@@ -348,7 +350,6 @@ static int ast_vhub_probe(struct platform_device *pdev)
/* Find interrupt and install handler */
vhub->irq = platform_get_irq(pdev, 0);
if (vhub->irq < 0) {
- dev_err(&pdev->dev, "Failed to get interrupt\n");
rc = vhub->irq;
goto err;
}
diff --git a/drivers/usb/gadget/udc/aspeed-vhub/dev.c b/drivers/usb/gadget/udc/aspeed-vhub/dev.c
index 6b1b16b17d7d..4008e7a51188 100644
--- a/drivers/usb/gadget/udc/aspeed-vhub/dev.c
+++ b/drivers/usb/gadget/udc/aspeed-vhub/dev.c
@@ -50,11 +50,14 @@ void ast_vhub_dev_irq(struct ast_vhub_dev *d)
static void ast_vhub_dev_enable(struct ast_vhub_dev *d)
{
- u32 reg, hmsk;
+ u32 reg, hmsk, i;
if (d->enabled)
return;
+ /* Cleanup EP0 state */
+ ast_vhub_reset_ep0(d);
+
/* Enable device and its EP0 interrupts */
reg = VHUB_DEV_EN_ENABLE_PORT |
VHUB_DEV_EN_EP0_IN_ACK_IRQEN |
@@ -73,6 +76,19 @@ static void ast_vhub_dev_enable(struct ast_vhub_dev *d)
/* Set EP0 DMA buffer address */
writel(d->ep0.buf_dma, d->regs + AST_VHUB_DEV_EP0_DATA);
+ /* Clear stall on all EPs */
+ for (i = 0; i < AST_VHUB_NUM_GEN_EPs; i++) {
+ struct ast_vhub_ep *ep = d->epns[i];
+
+ if (ep && (ep->epn.stalled || ep->epn.wedged)) {
+ ep->epn.stalled = false;
+ ep->epn.wedged = false;
+ ast_vhub_update_epn_stall(ep);
+ }
+ }
+
+ /* Additional cleanups */
+ d->wakeup_en = false;
d->enabled = true;
}
@@ -93,7 +109,6 @@ static void ast_vhub_dev_disable(struct ast_vhub_dev *d)
writel(0, d->regs + AST_VHUB_DEV_EN_CTRL);
d->gadget.speed = USB_SPEED_UNKNOWN;
d->enabled = false;
- d->suspended = false;
}
static int ast_vhub_dev_feature(struct ast_vhub_dev *d,
@@ -201,14 +216,19 @@ int ast_vhub_std_dev_request(struct ast_vhub_ep *ep,
u16 wValue, wIndex;
/* No driver, we shouldn't be enabled ... */
- if (!d->driver || !d->enabled || d->suspended) {
+ if (!d->driver || !d->enabled) {
EPDBG(ep,
- "Device is wrong state driver=%p enabled=%d"
- " suspended=%d\n",
- d->driver, d->enabled, d->suspended);
+ "Device is wrong state driver=%p enabled=%d\n",
+ d->driver, d->enabled);
return std_req_stall;
}
+ /*
+ * Note: we used to reject/stall requests while suspended,
+ * we don't do that anymore as we seem to have cases of
+ * mass storage getting very upset.
+ */
+
/* First packet, grab speed */
if (d->gadget.speed == USB_SPEED_UNKNOWN) {
d->gadget.speed = ep->vhub->speed;
@@ -449,8 +469,7 @@ static const struct usb_gadget_ops ast_vhub_udc_ops = {
void ast_vhub_dev_suspend(struct ast_vhub_dev *d)
{
- d->suspended = true;
- if (d->driver) {
+ if (d->driver && d->driver->suspend) {
spin_unlock(&d->vhub->lock);
d->driver->suspend(&d->gadget);
spin_lock(&d->vhub->lock);
@@ -459,8 +478,7 @@ void ast_vhub_dev_suspend(struct ast_vhub_dev *d)
void ast_vhub_dev_resume(struct ast_vhub_dev *d)
{
- d->suspended = false;
- if (d->driver) {
+ if (d->driver && d->driver->resume) {
spin_unlock(&d->vhub->lock);
d->driver->resume(&d->gadget);
spin_lock(&d->vhub->lock);
@@ -469,46 +487,28 @@ void ast_vhub_dev_resume(struct ast_vhub_dev *d)
void ast_vhub_dev_reset(struct ast_vhub_dev *d)
{
- /*
- * If speed is not set, we enable the port. If it is,
- * send reset to the gadget and reset "speed".
- *
- * Speed is an indication that we have got the first
- * setup packet to the device.
- */
- if (d->gadget.speed == USB_SPEED_UNKNOWN && !d->enabled) {
- DDBG(d, "Reset at unknown speed of disabled device, enabling...\n");
- ast_vhub_dev_enable(d);
- d->suspended = false;
+ /* No driver, just disable the device and return */
+ if (!d->driver) {
+ ast_vhub_dev_disable(d);
+ return;
}
- if (d->gadget.speed != USB_SPEED_UNKNOWN && d->driver) {
- unsigned int i;
- DDBG(d, "Reset at known speed of bound device, resetting...\n");
+ /* If the port isn't enabled, just enable it */
+ if (!d->enabled) {
+ DDBG(d, "Reset of disabled device, enabling...\n");
+ ast_vhub_dev_enable(d);
+ } else {
+ DDBG(d, "Reset of enabled device, resetting...\n");
spin_unlock(&d->vhub->lock);
- d->driver->reset(&d->gadget);
+ usb_gadget_udc_reset(&d->gadget, d->driver);
spin_lock(&d->vhub->lock);
/*
- * Disable/re-enable HW, this will clear the address
+ * Disable and maybe re-enable HW, this will clear the address
* and speed setting.
*/
ast_vhub_dev_disable(d);
ast_vhub_dev_enable(d);
-
- /* Clear stall on all EPs */
- for (i = 0; i < AST_VHUB_NUM_GEN_EPs; i++) {
- struct ast_vhub_ep *ep = d->epns[i];
-
- if (ep && ep->epn.stalled) {
- ep->epn.stalled = false;
- ast_vhub_update_epn_stall(ep);
- }
- }
-
- /* Additional cleanups */
- d->wakeup_en = false;
- d->suspended = false;
}
}
diff --git a/drivers/usb/gadget/udc/aspeed-vhub/ep0.c b/drivers/usb/gadget/udc/aspeed-vhub/ep0.c
index e2927fb083cf..022b777b85f8 100644
--- a/drivers/usb/gadget/udc/aspeed-vhub/ep0.c
+++ b/drivers/usb/gadget/udc/aspeed-vhub/ep0.c
@@ -105,18 +105,20 @@ void ast_vhub_ep0_handle_setup(struct ast_vhub_ep *ep)
(crq.bRequestType & USB_DIR_IN) ? "in" : "out",
ep->ep0.state);
- /* Check our state, cancel pending requests if needed */
- if (ep->ep0.state != ep0_state_token) {
+ /*
+ * Check our state, cancel pending requests if needed
+ *
+ * Note: Under some circumstances, we can get a new setup
+ * packet while waiting for the stall ack, just accept it.
+ *
+ * In any case, a SETUP packet in wrong state should have
+ * reset the HW state machine, so let's just log, nuke
+ * requests, move on.
+ */
+ if (ep->ep0.state != ep0_state_token &&
+ ep->ep0.state != ep0_state_stall) {
EPDBG(ep, "wrong state\n");
ast_vhub_nuke(ep, -EIO);
-
- /*
- * Accept the packet regardless, this seems to happen
- * when stalling a SETUP packet that has an OUT data
- * phase.
- */
- ast_vhub_nuke(ep, 0);
- goto stall;
}
/* Calculate next state for EP0 */
@@ -165,7 +167,7 @@ void ast_vhub_ep0_handle_setup(struct ast_vhub_ep *ep)
stall:
EPDBG(ep, "stalling\n");
writel(VHUB_EP0_CTRL_STALL, ep->ep0.ctlstat);
- ep->ep0.state = ep0_state_status;
+ ep->ep0.state = ep0_state_stall;
ep->ep0.dir_in = false;
return;
@@ -299,8 +301,8 @@ void ast_vhub_ep0_handle_ack(struct ast_vhub_ep *ep, bool in_ack)
if ((ep->ep0.dir_in && (stat & VHUB_EP0_TX_BUFF_RDY)) ||
(!ep->ep0.dir_in && (stat & VHUB_EP0_RX_BUFF_RDY)) ||
(ep->ep0.dir_in != in_ack)) {
+ /* In that case, ignore interrupt */
dev_warn(dev, "irq state mismatch");
- stall = true;
break;
}
/*
@@ -335,12 +337,22 @@ void ast_vhub_ep0_handle_ack(struct ast_vhub_ep *ep, bool in_ack)
dev_warn(dev, "status direction mismatch\n");
stall = true;
}
+ break;
+ case ep0_state_stall:
+ /*
+ * There shouldn't be any request left, but nuke just in case
+ * otherwise the stale request will block subsequent ones
+ */
+ ast_vhub_nuke(ep, -EIO);
+ break;
}
- /* Reset to token state */
- ep->ep0.state = ep0_state_token;
- if (stall)
+ /* Reset to token state or stall */
+ if (stall) {
writel(VHUB_EP0_CTRL_STALL, ep->ep0.ctlstat);
+ ep->ep0.state = ep0_state_stall;
+ } else
+ ep->ep0.state = ep0_state_token;
}
static int ast_vhub_ep0_queue(struct usb_ep* u_ep, struct usb_request *u_req,
@@ -367,7 +379,7 @@ static int ast_vhub_ep0_queue(struct usb_ep* u_ep, struct usb_request *u_req,
return -EINVAL;
/* Disabled device */
- if (ep->dev && (!ep->dev->enabled || ep->dev->suspended))
+ if (ep->dev && !ep->dev->enabled)
return -ESHUTDOWN;
/* Data, no buffer and not internal ? */
@@ -390,8 +402,12 @@ static int ast_vhub_ep0_queue(struct usb_ep* u_ep, struct usb_request *u_req,
spin_lock_irqsave(&vhub->lock, flags);
/* EP0 can only support a single request at a time */
- if (!list_empty(&ep->queue) || ep->ep0.state == ep0_state_token) {
+ if (!list_empty(&ep->queue) ||
+ ep->ep0.state == ep0_state_token ||
+ ep->ep0.state == ep0_state_stall) {
dev_warn(dev, "EP0: Request in wrong state\n");
+ EPVDBG(ep, "EP0: list_empty=%d state=%d\n",
+ list_empty(&ep->queue), ep->ep0.state);
spin_unlock_irqrestore(&vhub->lock, flags);
return -EBUSY;
}
@@ -459,6 +475,15 @@ static const struct usb_ep_ops ast_vhub_ep0_ops = {
.free_request = ast_vhub_free_request,
};
+void ast_vhub_reset_ep0(struct ast_vhub_dev *dev)
+{
+ struct ast_vhub_ep *ep = &dev->ep0;
+
+ ast_vhub_nuke(ep, -EIO);
+ ep->ep0.state = ep0_state_token;
+}
+
+
void ast_vhub_init_ep0(struct ast_vhub *vhub, struct ast_vhub_ep *ep,
struct ast_vhub_dev *dev)
{
diff --git a/drivers/usb/gadget/udc/aspeed-vhub/epn.c b/drivers/usb/gadget/udc/aspeed-vhub/epn.c
index 35941dc125f9..7475c74aa5c5 100644
--- a/drivers/usb/gadget/udc/aspeed-vhub/epn.c
+++ b/drivers/usb/gadget/udc/aspeed-vhub/epn.c
@@ -352,7 +352,7 @@ static int ast_vhub_epn_queue(struct usb_ep* u_ep, struct usb_request *u_req,
/* Endpoint enabled ? */
if (!ep->epn.enabled || !u_ep->desc || !ep->dev || !ep->d_idx ||
- !ep->dev->enabled || ep->dev->suspended) {
+ !ep->dev->enabled) {
EPDBG(ep, "Enqueuing request on wrong or disabled EP\n");
return -ESHUTDOWN;
}
diff --git a/drivers/usb/gadget/udc/aspeed-vhub/hub.c b/drivers/usb/gadget/udc/aspeed-vhub/hub.c
index 7c040f56100e..19b3517e04c0 100644
--- a/drivers/usb/gadget/udc/aspeed-vhub/hub.c
+++ b/drivers/usb/gadget/udc/aspeed-vhub/hub.c
@@ -449,8 +449,15 @@ static void ast_vhub_change_port_stat(struct ast_vhub *vhub,
USB_PORT_STAT_C_OVERCURRENT |
USB_PORT_STAT_C_RESET |
USB_PORT_STAT_C_L1;
- p->change |= chg;
+ /*
+ * We only set USB_PORT_STAT_C_ENABLE if we are disabling
+ * the port as per USB spec, otherwise MacOS gets upset
+ */
+ if (p->status & USB_PORT_STAT_ENABLE)
+ chg &= ~USB_PORT_STAT_C_ENABLE;
+
+ p->change = chg;
ast_vhub_update_hub_ep1(vhub, port);
}
}
@@ -723,6 +730,12 @@ enum std_req_rc ast_vhub_class_hub_request(struct ast_vhub_ep *ep,
case ClearPortFeature:
EPDBG(ep, "ClearPortFeature(%d,%d)\n", wIndex & 0xf, wValue);
return ast_vhub_clr_port_feature(ep, wIndex & 0xf, wValue);
+ case ClearTTBuffer:
+ case ResetTT:
+ case StopTT:
+ return std_req_complete;
+ case GetTTState:
+ return ast_vhub_simple_reply(ep, 0, 0, 0, 0);
default:
EPDBG(ep, "Unknown class request\n");
}
diff --git a/drivers/usb/gadget/udc/aspeed-vhub/vhub.h b/drivers/usb/gadget/udc/aspeed-vhub/vhub.h
index 4ed03d33a5a9..761919e220d3 100644
--- a/drivers/usb/gadget/udc/aspeed-vhub/vhub.h
+++ b/drivers/usb/gadget/udc/aspeed-vhub/vhub.h
@@ -257,6 +257,7 @@ enum ep0_state {
ep0_state_token,
ep0_state_data,
ep0_state_status,
+ ep0_state_stall,
};
/*
@@ -353,7 +354,6 @@ struct ast_vhub_dev {
struct usb_gadget_driver *driver;
bool registered : 1;
bool wakeup_en : 1;
- bool suspended : 1;
bool enabled : 1;
/* Endpoint structures */
@@ -507,6 +507,7 @@ void ast_vhub_init_hw(struct ast_vhub *vhub);
/* ep0.c */
void ast_vhub_ep0_handle_ack(struct ast_vhub_ep *ep, bool in_ack);
void ast_vhub_ep0_handle_setup(struct ast_vhub_ep *ep);
+void ast_vhub_reset_ep0(struct ast_vhub_dev *dev);
void ast_vhub_init_ep0(struct ast_vhub *vhub, struct ast_vhub_ep *ep,
struct ast_vhub_dev *dev);
int ast_vhub_reply(struct ast_vhub_ep *ep, char *ptr, int len);
diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c
index 503d275bc4c4..86ffc8307864 100644
--- a/drivers/usb/gadget/udc/atmel_usba_udc.c
+++ b/drivers/usb/gadget/udc/atmel_usba_udc.c
@@ -327,6 +327,7 @@ static int usba_config_fifo_table(struct usba_udc *udc)
switch (fifo_mode) {
default:
fifo_mode = 0;
+ /* fall through */
case 0:
udc->fifo_cfg = NULL;
n = 0;
diff --git a/drivers/usb/gadget/udc/bcm63xx_udc.c b/drivers/usb/gadget/udc/bcm63xx_udc.c
index c1fcc77403ea..97b16463f3ef 100644
--- a/drivers/usb/gadget/udc/bcm63xx_udc.c
+++ b/drivers/usb/gadget/udc/bcm63xx_udc.c
@@ -2328,10 +2328,8 @@ static int bcm63xx_udc_probe(struct platform_device *pdev)
/* IRQ resource #0: control interrupt (VBUS, speed, etc.) */
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(dev, "missing IRQ resource #0\n");
+ if (irq < 0)
goto out_uninit;
- }
if (devm_request_irq(dev, irq, &bcm63xx_udc_ctrl_isr, 0,
dev_name(dev), udc) < 0)
goto report_request_failure;
@@ -2339,10 +2337,8 @@ static int bcm63xx_udc_probe(struct platform_device *pdev)
/* IRQ resources #1-6: data interrupts for IUDMA channels 0-5 */
for (i = 0; i < BCM63XX_NUM_IUDMA; i++) {
irq = platform_get_irq(pdev, i + 1);
- if (irq < 0) {
- dev_err(dev, "missing IRQ resource #%d\n", i + 1);
+ if (irq < 0)
goto out_uninit;
- }
if (devm_request_irq(dev, irq, &bcm63xx_udc_data_isr, 0,
dev_name(dev), &udc->iudma[i]) < 0)
goto report_request_failure;
diff --git a/drivers/usb/gadget/udc/bdc/bdc_core.c b/drivers/usb/gadget/udc/bdc/bdc_core.c
index ccbd1d34eb2a..cc4a16e253ac 100644
--- a/drivers/usb/gadget/udc/bdc/bdc_core.c
+++ b/drivers/usb/gadget/udc/bdc/bdc_core.c
@@ -515,10 +515,8 @@ static int bdc_probe(struct platform_device *pdev)
return -ENOMEM;
}
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(dev, "platform_get_irq failed:%d\n", irq);
+ if (irq < 0)
return irq;
- }
spin_lock_init(&bdc->lock);
platform_set_drvdata(pdev, bdc);
bdc->irq = irq;
diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c
index 7cf34beb50df..92af8dc98c3d 100644
--- a/drivers/usb/gadget/udc/core.c
+++ b/drivers/usb/gadget/udc/core.c
@@ -1143,7 +1143,7 @@ static int check_pending_gadget_drivers(struct usb_udc *udc)
dev_name(&udc->dev)) == 0) {
ret = udc_bind_to_driver(udc, driver);
if (ret != -EPROBE_DEFER)
- list_del(&driver->pending);
+ list_del_init(&driver->pending);
break;
}
diff --git a/drivers/usb/gadget/udc/gr_udc.c b/drivers/usb/gadget/udc/gr_udc.c
index 729e60e49564..7a0e9a58c2d8 100644
--- a/drivers/usb/gadget/udc/gr_udc.c
+++ b/drivers/usb/gadget/udc/gr_udc.c
@@ -2134,19 +2134,15 @@ static int gr_probe(struct platform_device *pdev)
return PTR_ERR(regs);
dev->irq = platform_get_irq(pdev, 0);
- if (dev->irq <= 0) {
- dev_err(dev->dev, "No irq found\n");
+ if (dev->irq <= 0)
return -ENODEV;
- }
/* Some core configurations has separate irqs for IN and OUT events */
dev->irqi = platform_get_irq(pdev, 1);
if (dev->irqi > 0) {
dev->irqo = platform_get_irq(pdev, 2);
- if (dev->irqo <= 0) {
- dev_err(dev->dev, "Found irqi but not irqo\n");
+ if (dev->irqo <= 0)
return -ENODEV;
- }
} else {
dev->irqi = 0;
}
diff --git a/drivers/usb/gadget/udc/lpc32xx_udc.c b/drivers/usb/gadget/udc/lpc32xx_udc.c
index c65aed3e84c7..b3e073fb88c6 100644
--- a/drivers/usb/gadget/udc/lpc32xx_udc.c
+++ b/drivers/usb/gadget/udc/lpc32xx_udc.c
@@ -741,7 +741,6 @@ static inline void udc_protocol_cmd_data_w(struct lpc32xx_udc *udc, u32 cmd,
* response data */
static u32 udc_protocol_cmd_r(struct lpc32xx_udc *udc, u32 cmd)
{
- u32 tmp;
int to = 1000;
/* Write a command and read data from the protocol engine */
@@ -751,7 +750,6 @@ static u32 udc_protocol_cmd_r(struct lpc32xx_udc *udc, u32 cmd)
/* Write command code */
udc_protocol_cmd_w(udc, cmd);
- tmp = readl(USBD_DEVINTST(udc->udp_baseaddr));
while ((!(readl(USBD_DEVINTST(udc->udp_baseaddr)) & USBD_CDFULL))
&& (to > 0))
to--;
@@ -1991,7 +1989,7 @@ void udc_handle_eps(struct lpc32xx_udc *udc, struct lpc32xx_ep *ep)
/* DMA end of transfer completion */
static void udc_handle_dma_ep(struct lpc32xx_udc *udc, struct lpc32xx_ep *ep)
{
- u32 status, epstatus;
+ u32 status;
struct lpc32xx_request *req;
struct lpc32xx_usbd_dd_gad *dd;
@@ -2085,7 +2083,7 @@ static void udc_handle_dma_ep(struct lpc32xx_udc *udc, struct lpc32xx_ep *ep)
if (udc_clearep_getsts(udc, ep->hwep_num) & EP_SEL_F) {
udc_clearep_getsts(udc, ep->hwep_num);
uda_enable_hwepint(udc, ep->hwep_num);
- epstatus = udc_clearep_getsts(udc, ep->hwep_num);
+ udc_clearep_getsts(udc, ep->hwep_num);
/* Let the EP interrupt handle the ZLP */
return;
@@ -2197,7 +2195,7 @@ static void udc_handle_ep0_setup(struct lpc32xx_udc *udc)
struct lpc32xx_ep *ep, *ep0 = &udc->ep[0];
struct usb_ctrlrequest ctrlpkt;
int i, bytes;
- u16 wIndex, wValue, wLength, reqtype, req, tmp;
+ u16 wIndex, wValue, reqtype, req, tmp;
/* Nuke previous transfers */
nuke(ep0, -EPROTO);
@@ -2213,7 +2211,6 @@ static void udc_handle_ep0_setup(struct lpc32xx_udc *udc)
/* Native endianness */
wIndex = le16_to_cpu(ctrlpkt.wIndex);
wValue = le16_to_cpu(ctrlpkt.wValue);
- wLength = le16_to_cpu(ctrlpkt.wLength);
reqtype = le16_to_cpu(ctrlpkt.bRequestType);
/* Set direction of EP0 */
@@ -3060,11 +3057,8 @@ static int lpc32xx_udc_probe(struct platform_device *pdev)
/* Get IRQs */
for (i = 0; i < 4; i++) {
udc->udp_irq[i] = platform_get_irq(pdev, i);
- if (udc->udp_irq[i] < 0) {
- dev_err(udc->dev,
- "irq resource %d not available!\n", i);
+ if (udc->udp_irq[i] < 0)
return udc->udp_irq[i];
- }
}
udc->udp_baseaddr = devm_ioremap_resource(dev, res);
diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c
index b6bbe2e448ba..51efee21915f 100644
--- a/drivers/usb/gadget/udc/net2280.c
+++ b/drivers/usb/gadget/udc/net2280.c
@@ -2244,30 +2244,40 @@ static void usb_reinit_338x(struct net2280 *dev)
}
/* Hardware Defect and Workaround */
- val = readl(&dev->ll_lfps_regs->ll_lfps_5);
+ val = readl(&dev->llregs->ll_lfps_5);
val &= ~(0xf << TIMER_LFPS_6US);
val |= 0x5 << TIMER_LFPS_6US;
- writel(val, &dev->ll_lfps_regs->ll_lfps_5);
+ writel(val, &dev->llregs->ll_lfps_5);
- val = readl(&dev->ll_lfps_regs->ll_lfps_6);
+ val = readl(&dev->llregs->ll_lfps_6);
val &= ~(0xffff << TIMER_LFPS_80US);
val |= 0x0100 << TIMER_LFPS_80US;
- writel(val, &dev->ll_lfps_regs->ll_lfps_6);
+ writel(val, &dev->llregs->ll_lfps_6);
/*
* AA_AB Errata. Issue 4. Workaround for SuperSpeed USB
* Hot Reset Exit Handshake may Fail in Specific Case using
* Default Register Settings. Workaround for Enumeration test.
*/
- val = readl(&dev->ll_tsn_regs->ll_tsn_counters_2);
+ val = readl(&dev->llregs->ll_tsn_counters_2);
val &= ~(0x1f << HOT_TX_NORESET_TS2);
val |= 0x10 << HOT_TX_NORESET_TS2;
- writel(val, &dev->ll_tsn_regs->ll_tsn_counters_2);
+ writel(val, &dev->llregs->ll_tsn_counters_2);
- val = readl(&dev->ll_tsn_regs->ll_tsn_counters_3);
+ val = readl(&dev->llregs->ll_tsn_counters_3);
val &= ~(0x1f << HOT_RX_RESET_TS2);
val |= 0x3 << HOT_RX_RESET_TS2;
- writel(val, &dev->ll_tsn_regs->ll_tsn_counters_3);
+ writel(val, &dev->llregs->ll_tsn_counters_3);
+
+ /*
+ * AB errata. Errata 11. Workaround for Default Duration of LFPS
+ * Handshake Signaling for Device-Initiated U1 Exit is too short.
+ * Without this, various enumeration failures observed with
+ * modern superspeed hosts.
+ */
+ val = readl(&dev->llregs->ll_lfps_timers_2);
+ writel((val & 0xffff0000) | LFPS_TIMERS_2_WORKAROUND_VALUE,
+ &dev->llregs->ll_lfps_timers_2);
/*
* Set Recovery Idle to Recover bit:
@@ -2276,10 +2286,10 @@ static void usb_reinit_338x(struct net2280 *dev)
* - It is safe to set for all connection speeds; all chip revisions.
* - R-M-W to leave other bits undisturbed.
* - Reference PLX TT-7372
- */
- val = readl(&dev->ll_chicken_reg->ll_tsn_chicken_bit);
+ */
+ val = readl(&dev->llregs->ll_tsn_chicken_bit);
val |= BIT(RECOVERY_IDLE_TO_RECOVER_FMW);
- writel(val, &dev->ll_chicken_reg->ll_tsn_chicken_bit);
+ writel(val, &dev->llregs->ll_tsn_chicken_bit);
INIT_LIST_HEAD(&dev->gadget.ep0->ep_list);
@@ -3669,12 +3679,6 @@ static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id)
(base + 0x00b4);
dev->llregs = (struct usb338x_ll_regs __iomem *)
(base + 0x0700);
- dev->ll_lfps_regs = (struct usb338x_ll_lfps_regs __iomem *)
- (base + 0x0748);
- dev->ll_tsn_regs = (struct usb338x_ll_tsn_regs __iomem *)
- (base + 0x077c);
- dev->ll_chicken_reg = (struct usb338x_ll_chi_regs __iomem *)
- (base + 0x079c);
dev->plregs = (struct usb338x_pl_regs __iomem *)
(base + 0x0800);
usbstat = readl(&dev->usb->usbstat);
diff --git a/drivers/usb/gadget/udc/net2280.h b/drivers/usb/gadget/udc/net2280.h
index b65a797544d7..85d3ca1698ba 100644
--- a/drivers/usb/gadget/udc/net2280.h
+++ b/drivers/usb/gadget/udc/net2280.h
@@ -178,9 +178,6 @@ struct net2280 {
struct net2280_dep_regs __iomem *dep;
struct net2280_ep_regs __iomem *epregs;
struct usb338x_ll_regs __iomem *llregs;
- struct usb338x_ll_lfps_regs __iomem *ll_lfps_regs;
- struct usb338x_ll_tsn_regs __iomem *ll_tsn_regs;
- struct usb338x_ll_chi_regs __iomem *ll_chicken_reg;
struct usb338x_pl_regs __iomem *plregs;
struct dma_pool *requests;
diff --git a/drivers/usb/gadget/udc/pch_udc.c b/drivers/usb/gadget/udc/pch_udc.c
index cded51f36fc1..265dab2bbfac 100644
--- a/drivers/usb/gadget/udc/pch_udc.c
+++ b/drivers/usb/gadget/udc/pch_udc.c
@@ -3046,8 +3046,7 @@ static void pch_udc_remove(struct pci_dev *pdev)
#ifdef CONFIG_PM_SLEEP
static int pch_udc_suspend(struct device *d)
{
- struct pci_dev *pdev = to_pci_dev(d);
- struct pch_udc_dev *dev = pci_get_drvdata(pdev);
+ struct pch_udc_dev *dev = dev_get_drvdata(d);
pch_udc_disable_interrupts(dev, UDC_DEVINT_MSK);
pch_udc_disable_ep_interrupts(dev, UDC_EPINT_MSK_DISABLE_ALL);
diff --git a/drivers/usb/gadget/udc/renesas_usb3.c b/drivers/usb/gadget/udc/renesas_usb3.c
index 1f4c3fbd1df8..e098f16c01cb 100644
--- a/drivers/usb/gadget/udc/renesas_usb3.c
+++ b/drivers/usb/gadget/udc/renesas_usb3.c
@@ -2744,10 +2744,8 @@ static int renesas_usb3_probe(struct platform_device *pdev)
priv = of_device_get_match_data(&pdev->dev);
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "Failed to get IRQ: %d\n", irq);
+ if (irq < 0)
return irq;
- }
usb3 = devm_kzalloc(&pdev->dev, sizeof(*usb3), GFP_KERNEL);
if (!usb3)
diff --git a/drivers/usb/gadget/udc/s3c-hsudc.c b/drivers/usb/gadget/udc/s3c-hsudc.c
index 31c7c5587cf9..858993c73442 100644
--- a/drivers/usb/gadget/udc/s3c-hsudc.c
+++ b/drivers/usb/gadget/udc/s3c-hsudc.c
@@ -1311,10 +1311,8 @@ static int s3c_hsudc_probe(struct platform_device *pdev)
s3c_hsudc_setup_ep(hsudc);
ret = platform_get_irq(pdev, 0);
- if (ret < 0) {
- dev_err(dev, "unable to obtain IRQ number\n");
+ if (ret < 0)
goto err_res;
- }
hsudc->irq = ret;
ret = devm_request_irq(&pdev->dev, hsudc->irq, s3c_hsudc_irq, 0,
diff --git a/drivers/usb/gadget/udc/s3c2410_udc.c b/drivers/usb/gadget/udc/s3c2410_udc.c
index af3e63316ace..f82208fbc249 100644
--- a/drivers/usb/gadget/udc/s3c2410_udc.c
+++ b/drivers/usb/gadget/udc/s3c2410_udc.c
@@ -312,6 +312,7 @@ static int s3c2410_udc_write_fifo(struct s3c2410_ep *ep,
switch (idx) {
default:
idx = 0;
+ /* fall through */
case 0:
fifo_reg = S3C2410_UDC_EP0_FIFO_REG;
break;
@@ -416,6 +417,7 @@ static int s3c2410_udc_read_fifo(struct s3c2410_ep *ep,
switch (idx) {
default:
idx = 0;
+ /* fall through */
case 0:
fifo_reg = S3C2410_UDC_EP0_FIFO_REG;
break;
diff --git a/drivers/usb/gadget/udc/udc-xilinx.c b/drivers/usb/gadget/udc/udc-xilinx.c
index b1f4104d1283..29d8e5f8bb58 100644
--- a/drivers/usb/gadget/udc/udc-xilinx.c
+++ b/drivers/usb/gadget/udc/udc-xilinx.c
@@ -2074,10 +2074,8 @@ static int xudc_probe(struct platform_device *pdev)
return PTR_ERR(udc->addr);
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "unable to get irq\n");
+ if (irq < 0)
return irq;
- }
ret = devm_request_irq(&pdev->dev, irq, xudc_irq, 0,
dev_name(&pdev->dev), udc);
if (ret < 0) {
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 73d233d3bf4d..79b2e79dddd0 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -287,12 +287,6 @@ config USB_EHCI_MV
Dova, Armada 370 and Armada XP. See "Support for Marvell EBU
on-chip EHCI USB controller" for those.
-config USB_W90X900_EHCI
- tristate "W90X900(W90P910) EHCI support"
- depends on ARCH_W90X900
- ---help---
- Enables support for the W90X900 USB controller
-
config USB_CNS3XXX_EHCI
bool "Cavium CNS3XXX EHCI Module (DEPRECATED)"
depends on ARCH_CNS3XXX
@@ -718,32 +712,6 @@ config USB_RENESAS_USBHS_HCD
To compile this driver as a module, choose M here: the
module will be called renesas-usbhs.
-config USB_WHCI_HCD
- tristate "Wireless USB Host Controller Interface (WHCI) driver"
- depends on USB_PCI && USB && UWB
- select USB_WUSB
- select UWB_WHCI
- help
- A driver for PCI-based Wireless USB Host Controllers that are
- compliant with the WHCI specification.
-
- To compile this driver a module, choose M here: the module
- will be called "whci-hcd".
-
-config USB_HWA_HCD
- tristate "Host Wire Adapter (HWA) driver"
- depends on USB && UWB
- select USB_WUSB
- select UWB_HWA
- help
- This driver enables you to connect Wireless USB devices to
- your system using a Host Wire Adaptor USB dongle. This is an
- UWB Radio Controller and WUSB Host Controller connected to
- your machine via USB (specified in WUSB1.0).
-
- To compile this driver a module, choose M here: the module
- will be called "hwa-hc".
-
config USB_IMX21_HCD
tristate "i.MX21 HCD support"
depends on ARM && ARCH_MXC
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index 84514f71ae44..b191361257cc 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -35,8 +35,6 @@ ifneq ($(CONFIG_DEBUG_FS),)
xhci-hcd-y += xhci-debugfs.o
endif
-obj-$(CONFIG_USB_WHCI_HCD) += whci/
-
obj-$(CONFIG_USB_PCI) += pci-quirks.o
obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o
@@ -51,7 +49,6 @@ obj-$(CONFIG_USB_EHCI_HCD_STI) += ehci-st.o
obj-$(CONFIG_USB_EHCI_EXYNOS) += ehci-exynos.o
obj-$(CONFIG_USB_EHCI_HCD_AT91) += ehci-atmel.o
obj-$(CONFIG_USB_EHCI_TEGRA) += ehci-tegra.o
-obj-$(CONFIG_USB_W90X900_EHCI) += ehci-w90x900.o
obj-$(CONFIG_USB_OXU210HP_HCD) += oxu210hp-hcd.o
obj-$(CONFIG_USB_ISP116X_HCD) += isp116x-hcd.o
@@ -82,7 +79,6 @@ obj-$(CONFIG_USB_SL811_HCD) += sl811-hcd.o
obj-$(CONFIG_USB_SL811_CS) += sl811_cs.o
obj-$(CONFIG_USB_U132_HCD) += u132-hcd.o
obj-$(CONFIG_USB_R8A66597_HCD) += r8a66597-hcd.o
-obj-$(CONFIG_USB_HWA_HCD) += hwa-hc.o
obj-$(CONFIG_USB_IMX21_HCD) += imx21-hcd.o
obj-$(CONFIG_USB_FSL_USB2) += fsl-mph-dr-of.o
obj-$(CONFIG_USB_EHCI_FSL) += fsl-mph-dr-of.o
diff --git a/drivers/usb/host/ehci-atmel.c b/drivers/usb/host/ehci-atmel.c
index 3ba140ceaf52..e893467d659c 100644
--- a/drivers/usb/host/ehci-atmel.c
+++ b/drivers/usb/host/ehci-atmel.c
@@ -100,9 +100,6 @@ static int ehci_atmel_drv_probe(struct platform_device *pdev)
irq = platform_get_irq(pdev, 0);
if (irq <= 0) {
- dev_err(&pdev->dev,
- "Found HC with no IRQ. Check %s setup!\n",
- dev_name(&pdev->dev));
retval = -ENODEV;
goto fail_create_hcd;
}
diff --git a/drivers/usb/host/ehci-exynos.c b/drivers/usb/host/ehci-exynos.c
index 3a29a1a8519c..01debfd03d4a 100644
--- a/drivers/usb/host/ehci-exynos.c
+++ b/drivers/usb/host/ehci-exynos.c
@@ -41,6 +41,7 @@ struct exynos_ehci_hcd {
struct clk *clk;
struct device_node *of_node;
struct phy *phy[PHY_NUMBER];
+ bool legacy_phy;
};
#define to_exynos_ehci(hcd) (struct exynos_ehci_hcd *)(hcd_to_ehci(hcd)->priv)
@@ -50,10 +51,22 @@ static int exynos_ehci_get_phy(struct device *dev,
{
struct device_node *child;
struct phy *phy;
- int phy_number;
+ int phy_number, num_phys;
int ret;
/* Get PHYs for the controller */
+ num_phys = of_count_phandle_with_args(dev->of_node, "phys",
+ "#phy-cells");
+ for (phy_number = 0; phy_number < num_phys; phy_number++) {
+ phy = devm_of_phy_get_by_index(dev, dev->of_node, phy_number);
+ if (IS_ERR(phy))
+ return PTR_ERR(phy);
+ exynos_ehci->phy[phy_number] = phy;
+ }
+ if (num_phys > 0)
+ return 0;
+
+ /* Get PHYs using legacy bindings */
for_each_available_child_of_node(dev->of_node, child) {
ret = of_property_read_u32(child, "reg", &phy_number);
if (ret) {
@@ -84,6 +97,7 @@ static int exynos_ehci_get_phy(struct device *dev,
}
}
+ exynos_ehci->legacy_phy = true;
return 0;
}
@@ -205,11 +219,12 @@ static int exynos_ehci_probe(struct platform_device *pdev)
ehci->caps = hcd->regs;
/*
- * Workaround: reset of_node pointer to avoid conflict between Exynos
- * EHCI port subnodes and generic USB device bindings
+ * Workaround: reset of_node pointer to avoid conflict between legacy
+ * Exynos EHCI port subnodes and generic USB device bindings
*/
exynos_ehci->of_node = pdev->dev.of_node;
- pdev->dev.of_node = NULL;
+ if (exynos_ehci->legacy_phy)
+ pdev->dev.of_node = NULL;
/* DMA burst Enable */
writel(EHCI_INSNREG00_ENABLE_DMA_BURST, EHCI_INSNREG00(hcd->regs));
diff --git a/drivers/usb/host/ehci-grlib.c b/drivers/usb/host/ehci-grlib.c
index 656b8c08efc8..a2c3b4ec8a8b 100644
--- a/drivers/usb/host/ehci-grlib.c
+++ b/drivers/usb/host/ehci-grlib.c
@@ -30,7 +30,7 @@ static const struct hc_driver ehci_grlib_hc_driver = {
* generic hardware linkage
*/
.irq = ehci_irq,
- .flags = HCD_MEMORY | HCD_USB2 | HCD_BH,
+ .flags = HCD_MEMORY | HCD_DMA | HCD_USB2 | HCD_BH,
/*
* basic lifecycle operations
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 9da7e22848c9..cf2b7ae93b7e 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -1193,7 +1193,7 @@ static const struct hc_driver ehci_hc_driver = {
* generic hardware linkage
*/
.irq = ehci_irq,
- .flags = HCD_MEMORY | HCD_USB2 | HCD_BH,
+ .flags = HCD_MEMORY | HCD_DMA | HCD_USB2 | HCD_BH,
/*
* basic lifecycle operations
diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c
index 7d20296cbe9f..fc125b3d06e7 100644
--- a/drivers/usb/host/ehci-omap.c
+++ b/drivers/usb/host/ehci-omap.c
@@ -115,10 +115,8 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev)
}
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(dev, "EHCI irq failed: %d\n", irq);
+ if (irq < 0)
return irq;
- }
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
regs = devm_ioremap_resource(dev, res);
diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c
index 790acf3633e8..a319b1df3011 100644
--- a/drivers/usb/host/ehci-orion.c
+++ b/drivers/usb/host/ehci-orion.c
@@ -223,9 +223,6 @@ static int ehci_orion_drv_probe(struct platform_device *pdev)
irq = platform_get_irq(pdev, 0);
if (irq <= 0) {
- dev_err(&pdev->dev,
- "Found HC with no IRQ. Check %s setup!\n",
- dev_name(&pdev->dev));
err = -ENODEV;
goto err;
}
diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c
index 4c306fb6b069..769749ca5961 100644
--- a/drivers/usb/host/ehci-platform.c
+++ b/drivers/usb/host/ehci-platform.c
@@ -145,10 +145,8 @@ static int ehci_platform_probe(struct platform_device *dev)
}
irq = platform_get_irq(dev, 0);
- if (irq < 0) {
- dev_err(&dev->dev, "no irq provided");
+ if (irq < 0)
return irq;
- }
hcd = usb_create_hcd(&ehci_platform_hc_driver, &dev->dev,
dev_name(&dev->dev));
diff --git a/drivers/usb/host/ehci-pmcmsp.c b/drivers/usb/host/ehci-pmcmsp.c
index 46e160370d6e..a2b610dbedfc 100644
--- a/drivers/usb/host/ehci-pmcmsp.c
+++ b/drivers/usb/host/ehci-pmcmsp.c
@@ -250,7 +250,7 @@ static const struct hc_driver ehci_msp_hc_driver = {
* generic hardware linkage
*/
.irq = ehci_irq,
- .flags = HCD_MEMORY | HCD_USB2 | HCD_BH,
+ .flags = HCD_MEMORY | HCD_DMA | HCD_USB2 | HCD_BH,
/*
* basic lifecycle operations
diff --git a/drivers/usb/host/ehci-ppc-of.c b/drivers/usb/host/ehci-ppc-of.c
index 576f7d79ad4e..6bbaee74f7e7 100644
--- a/drivers/usb/host/ehci-ppc-of.c
+++ b/drivers/usb/host/ehci-ppc-of.c
@@ -31,7 +31,7 @@ static const struct hc_driver ehci_ppc_of_hc_driver = {
* generic hardware linkage
*/
.irq = ehci_irq,
- .flags = HCD_MEMORY | HCD_USB2 | HCD_BH,
+ .flags = HCD_MEMORY | HCD_DMA | HCD_USB2 | HCD_BH,
/*
* basic lifecycle operations
diff --git a/drivers/usb/host/ehci-ps3.c b/drivers/usb/host/ehci-ps3.c
index 454d8c624a3f..fb52133c3557 100644
--- a/drivers/usb/host/ehci-ps3.c
+++ b/drivers/usb/host/ehci-ps3.c
@@ -59,7 +59,7 @@ static const struct hc_driver ps3_ehci_hc_driver = {
.product_desc = "PS3 EHCI Host Controller",
.hcd_priv_size = sizeof(struct ehci_hcd),
.irq = ehci_irq,
- .flags = HCD_MEMORY | HCD_USB2 | HCD_BH,
+ .flags = HCD_MEMORY | HCD_DMA | HCD_USB2 | HCD_BH,
.reset = ps3_ehci_hc_reset,
.start = ehci_run,
.stop = ehci_stop,
diff --git a/drivers/usb/host/ehci-sh.c b/drivers/usb/host/ehci-sh.c
index a9ee767952c1..2afde14dc425 100644
--- a/drivers/usb/host/ehci-sh.c
+++ b/drivers/usb/host/ehci-sh.c
@@ -33,7 +33,7 @@ static const struct hc_driver ehci_sh_hc_driver = {
* generic hardware linkage
*/
.irq = ehci_irq,
- .flags = HCD_USB2 | HCD_MEMORY | HCD_BH,
+ .flags = HCD_USB2 | HCD_DMA | HCD_MEMORY | HCD_BH,
/*
* basic lifecycle operations
@@ -85,9 +85,6 @@ static int ehci_hcd_sh_probe(struct platform_device *pdev)
irq = platform_get_irq(pdev, 0);
if (irq <= 0) {
- dev_err(&pdev->dev,
- "Found HC with no IRQ. Check %s setup!\n",
- dev_name(&pdev->dev));
ret = -ENODEV;
goto fail_create_hcd;
}
diff --git a/drivers/usb/host/ehci-st.c b/drivers/usb/host/ehci-st.c
index ccb4e611001d..f74433aac948 100644
--- a/drivers/usb/host/ehci-st.c
+++ b/drivers/usb/host/ehci-st.c
@@ -158,10 +158,8 @@ static int st_ehci_platform_probe(struct platform_device *dev)
return -ENODEV;
irq = platform_get_irq(dev, 0);
- if (irq < 0) {
- dev_err(&dev->dev, "no irq provided");
+ if (irq < 0)
return irq;
- }
res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0);
if (!res_mem) {
dev_err(&dev->dev, "no memory resource provided");
diff --git a/drivers/usb/host/ehci-w90x900.c b/drivers/usb/host/ehci-w90x900.c
deleted file mode 100644
index 6d77ace1697b..000000000000
--- a/drivers/usb/host/ehci-w90x900.c
+++ /dev/null
@@ -1,130 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * linux/driver/usb/host/ehci-w90x900.c
- *
- * Copyright (c) 2008 Nuvoton technology corporation.
- *
- * Wan ZongShun <mcuos.com@gmail.com>
- */
-
-#include <linux/dma-mapping.h>
-#include <linux/io.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/platform_device.h>
-#include <linux/usb.h>
-#include <linux/usb/hcd.h>
-
-#include "ehci.h"
-
-/* enable phy0 and phy1 for w90p910 */
-#define ENPHY (0x01<<8)
-#define PHY0_CTR (0xA4)
-#define PHY1_CTR (0xA8)
-
-#define DRIVER_DESC "EHCI w90x900 driver"
-
-static const char hcd_name[] = "ehci-w90x900 ";
-
-static struct hc_driver __read_mostly ehci_w90x900_hc_driver;
-
-static int ehci_w90x900_probe(struct platform_device *pdev)
-{
- struct usb_hcd *hcd;
- struct ehci_hcd *ehci;
- struct resource *res;
- int retval = 0, irq;
- unsigned long val;
-
- hcd = usb_create_hcd(&ehci_w90x900_hc_driver,
- &pdev->dev, "w90x900 EHCI");
- if (!hcd) {
- retval = -ENOMEM;
- goto err1;
- }
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- hcd->regs = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(hcd->regs)) {
- retval = PTR_ERR(hcd->regs);
- goto err2;
- }
- hcd->rsrc_start = res->start;
- hcd->rsrc_len = resource_size(res);
-
- ehci = hcd_to_ehci(hcd);
- ehci->caps = hcd->regs;
- ehci->regs = hcd->regs +
- HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase));
-
- /* enable PHY 0,1,the regs only apply to w90p910
- * 0xA4,0xA8 were offsets of PHY0 and PHY1 controller of
- * w90p910 IC relative to ehci->regs.
- */
- val = __raw_readl(ehci->regs+PHY0_CTR);
- val |= ENPHY;
- __raw_writel(val, ehci->regs+PHY0_CTR);
-
- val = __raw_readl(ehci->regs+PHY1_CTR);
- val |= ENPHY;
- __raw_writel(val, ehci->regs+PHY1_CTR);
-
- irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- retval = irq;
- goto err2;
- }
-
- retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
- if (retval != 0)
- goto err2;
-
- device_wakeup_enable(hcd->self.controller);
- return retval;
-err2:
- usb_put_hcd(hcd);
-err1:
- return retval;
-}
-
-static int ehci_w90x900_remove(struct platform_device *pdev)
-{
- struct usb_hcd *hcd = platform_get_drvdata(pdev);
-
- usb_remove_hcd(hcd);
- usb_put_hcd(hcd);
-
- return 0;
-}
-
-static struct platform_driver ehci_hcd_w90x900_driver = {
- .probe = ehci_w90x900_probe,
- .remove = ehci_w90x900_remove,
- .driver = {
- .name = "w90x900-ehci",
- },
-};
-
-static int __init ehci_w90X900_init(void)
-{
- if (usb_disabled())
- return -ENODEV;
-
- pr_info("%s: " DRIVER_DESC "\n", hcd_name);
-
- ehci_init_driver(&ehci_w90x900_hc_driver, NULL);
- return platform_driver_register(&ehci_hcd_w90x900_driver);
-}
-module_init(ehci_w90X900_init);
-
-static void __exit ehci_w90X900_cleanup(void)
-{
- platform_driver_unregister(&ehci_hcd_w90x900_driver);
-}
-module_exit(ehci_w90X900_cleanup);
-
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
-MODULE_ALIAS("platform:w90p910-ehci");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/host/ehci-xilinx-of.c b/drivers/usb/host/ehci-xilinx-of.c
index d2a27578e440..67a6ee8cb5d8 100644
--- a/drivers/usb/host/ehci-xilinx-of.c
+++ b/drivers/usb/host/ehci-xilinx-of.c
@@ -66,7 +66,7 @@ static const struct hc_driver ehci_xilinx_of_hc_driver = {
* generic hardware linkage
*/
.irq = ehci_irq,
- .flags = HCD_MEMORY | HCD_USB2 | HCD_BH,
+ .flags = HCD_MEMORY | HCD_DMA | HCD_USB2 | HCD_BH,
/*
* basic lifecycle operations
diff --git a/drivers/usb/host/fhci-hcd.c b/drivers/usb/host/fhci-hcd.c
index 48fe9e6c2465..04733876c9c6 100644
--- a/drivers/usb/host/fhci-hcd.c
+++ b/drivers/usb/host/fhci-hcd.c
@@ -538,7 +538,7 @@ static const struct hc_driver fhci_driver = {
/* generic hardware linkage */
.irq = fhci_irq,
- .flags = HCD_USB11 | HCD_MEMORY,
+ .flags = HCD_DMA | HCD_USB11 | HCD_MEMORY,
/* basic lifecycle operation */
.start = fhci_start,
diff --git a/drivers/usb/host/fotg210-hcd.c b/drivers/usb/host/fotg210-hcd.c
index 0dbfa5c10703..9e0c98d6bdb0 100644
--- a/drivers/usb/host/fotg210-hcd.c
+++ b/drivers/usb/host/fotg210-hcd.c
@@ -5508,7 +5508,7 @@ static const struct hc_driver fotg210_fotg210_hc_driver = {
* generic hardware linkage
*/
.irq = fotg210_irq,
- .flags = HCD_MEMORY | HCD_USB2,
+ .flags = HCD_MEMORY | HCD_DMA | HCD_USB2,
/*
* basic lifecycle operations
diff --git a/drivers/usb/host/imx21-hcd.c b/drivers/usb/host/imx21-hcd.c
index 6e3dad19d369..5835f9966204 100644
--- a/drivers/usb/host/imx21-hcd.c
+++ b/drivers/usb/host/imx21-hcd.c
@@ -1771,7 +1771,7 @@ static const struct hc_driver imx21_hc_driver = {
.product_desc = "IMX21 USB Host Controller",
.hcd_priv_size = sizeof(struct imx21),
- .flags = HCD_USB11,
+ .flags = HCD_DMA | HCD_USB11,
.irq = imx21_irq,
.reset = imx21_hc_reset,
@@ -1836,10 +1836,8 @@ static int imx21_probe(struct platform_device *pdev)
if (!res)
return -ENODEV;
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "Failed to get IRQ: %d\n", irq);
+ if (irq < 0)
return irq;
- }
hcd = usb_create_hcd(&imx21_hc_driver,
&pdev->dev, dev_name(&pdev->dev));
diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c
index 74da136d322a..a87c0b26279e 100644
--- a/drivers/usb/host/isp116x-hcd.c
+++ b/drivers/usb/host/isp116x-hcd.c
@@ -1581,12 +1581,6 @@ static int isp116x_probe(struct platform_device *pdev)
irq = ires->start;
irqflags = ires->flags & IRQF_TRIGGER_MASK;
- if (pdev->dev.dma_mask) {
- DBG("DMA not supported\n");
- ret = -EINVAL;
- goto err1;
- }
-
if (!request_mem_region(addr->start, 2, hcd_name)) {
ret = -EBUSY;
goto err1;
diff --git a/drivers/usb/host/isp1362-hcd.c b/drivers/usb/host/isp1362-hcd.c
index 28bf8bfb091e..96f8daa11f25 100644
--- a/drivers/usb/host/isp1362-hcd.c
+++ b/drivers/usb/host/isp1362-hcd.c
@@ -2645,11 +2645,6 @@ static int isp1362_probe(struct platform_device *pdev)
if (pdev->num_resources < 3)
return -ENODEV;
- if (pdev->dev.dma_mask) {
- DBG(1, "won't do DMA");
- return -ENODEV;
- }
-
irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!irq_res)
return -ENODEV;
diff --git a/drivers/usb/host/max3421-hcd.c b/drivers/usb/host/max3421-hcd.c
index afa321ab55fc..8819f502b6a6 100644
--- a/drivers/usb/host/max3421-hcd.c
+++ b/drivers/usb/host/max3421-hcd.c
@@ -1800,21 +1800,6 @@ max3421_bus_resume(struct usb_hcd *hcd)
return -1;
}
-/*
- * The SPI driver already takes care of DMA-mapping/unmapping, so no
- * reason to do it twice.
- */
-static int
-max3421_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags)
-{
- return 0;
-}
-
-static void
-max3421_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb)
-{
-}
-
static const struct hc_driver max3421_hcd_desc = {
.description = "max3421",
.product_desc = DRIVER_DESC,
@@ -1826,8 +1811,6 @@ static const struct hc_driver max3421_hcd_desc = {
.get_frame_number = max3421_get_frame_number,
.urb_enqueue = max3421_urb_enqueue,
.urb_dequeue = max3421_urb_dequeue,
- .map_urb_for_dma = max3421_map_urb_for_dma,
- .unmap_urb_for_dma = max3421_unmap_urb_for_dma,
.endpoint_disable = max3421_endpoint_disable,
.hub_status_data = max3421_hub_status_data,
.hub_control = max3421_hub_control,
diff --git a/drivers/usb/host/ohci-exynos.c b/drivers/usb/host/ohci-exynos.c
index 905c6317e0c3..d5ce98e205c7 100644
--- a/drivers/usb/host/ohci-exynos.c
+++ b/drivers/usb/host/ohci-exynos.c
@@ -32,6 +32,7 @@ struct exynos_ohci_hcd {
struct clk *clk;
struct device_node *of_node;
struct phy *phy[PHY_NUMBER];
+ bool legacy_phy;
};
static int exynos_ohci_get_phy(struct device *dev,
@@ -39,10 +40,22 @@ static int exynos_ohci_get_phy(struct device *dev,
{
struct device_node *child;
struct phy *phy;
- int phy_number;
+ int phy_number, num_phys;
int ret;
/* Get PHYs for the controller */
+ num_phys = of_count_phandle_with_args(dev->of_node, "phys",
+ "#phy-cells");
+ for (phy_number = 0; phy_number < num_phys; phy_number++) {
+ phy = devm_of_phy_get_by_index(dev, dev->of_node, phy_number);
+ if (IS_ERR(phy))
+ return PTR_ERR(phy);
+ exynos_ohci->phy[phy_number] = phy;
+ }
+ if (num_phys > 0)
+ return 0;
+
+ /* Get PHYs using legacy bindings */
for_each_available_child_of_node(dev->of_node, child) {
ret = of_property_read_u32(child, "reg", &phy_number);
if (ret) {
@@ -73,6 +86,7 @@ static int exynos_ohci_get_phy(struct device *dev,
}
}
+ exynos_ohci->legacy_phy = true;
return 0;
}
@@ -172,11 +186,12 @@ static int exynos_ohci_probe(struct platform_device *pdev)
}
/*
- * Workaround: reset of_node pointer to avoid conflict between Exynos
- * OHCI port subnodes and generic USB device bindings
+ * Workaround: reset of_node pointer to avoid conflict between legacy
+ * Exynos OHCI port subnodes and generic USB device bindings
*/
exynos_ohci->of_node = pdev->dev.of_node;
- pdev->dev.of_node = NULL;
+ if (exynos_ohci->legacy_phy)
+ pdev->dev.of_node = NULL;
err = usb_add_hcd(hcd, irq, IRQF_SHARED);
if (err) {
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index 1fe3deec35cf..4de91653a2c7 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -1187,7 +1187,7 @@ static const struct hc_driver ohci_hc_driver = {
* generic hardware linkage
*/
.irq = ohci_irq,
- .flags = HCD_MEMORY | HCD_USB11,
+ .flags = HCD_MEMORY | HCD_DMA | HCD_USB11,
/*
* basic lifecycle operations
diff --git a/drivers/usb/host/ohci-platform.c b/drivers/usb/host/ohci-platform.c
index 65a1c3fdc88c..7addfc2cbadc 100644
--- a/drivers/usb/host/ohci-platform.c
+++ b/drivers/usb/host/ohci-platform.c
@@ -111,10 +111,8 @@ static int ohci_platform_probe(struct platform_device *dev)
return err;
irq = platform_get_irq(dev, 0);
- if (irq < 0) {
- dev_err(&dev->dev, "no irq provided");
+ if (irq < 0)
return irq;
- }
hcd = usb_create_hcd(&ohci_platform_hc_driver, &dev->dev,
dev_name(&dev->dev));
diff --git a/drivers/usb/host/ohci-ppc-of.c b/drivers/usb/host/ohci-ppc-of.c
index 76a9b40b08f1..45f7cceb6df3 100644
--- a/drivers/usb/host/ohci-ppc-of.c
+++ b/drivers/usb/host/ohci-ppc-of.c
@@ -50,7 +50,7 @@ static const struct hc_driver ohci_ppc_of_hc_driver = {
* generic hardware linkage
*/
.irq = ohci_irq,
- .flags = HCD_USB11 | HCD_MEMORY,
+ .flags = HCD_USB11 | HCD_DMA | HCD_MEMORY,
/*
* basic lifecycle operations
diff --git a/drivers/usb/host/ohci-ps3.c b/drivers/usb/host/ohci-ps3.c
index 395f9d3bc849..f77cd6af0ccf 100644
--- a/drivers/usb/host/ohci-ps3.c
+++ b/drivers/usb/host/ohci-ps3.c
@@ -46,7 +46,7 @@ static const struct hc_driver ps3_ohci_hc_driver = {
.product_desc = "PS3 OHCI Host Controller",
.hcd_priv_size = sizeof(struct ohci_hcd),
.irq = ohci_irq,
- .flags = HCD_MEMORY | HCD_USB11,
+ .flags = HCD_MEMORY | HCD_DMA | HCD_USB11,
.reset = ps3_ohci_hc_reset,
.start = ps3_ohci_hc_start,
.stop = ohci_stop,
diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c
index 3e2474959735..7679fb583e41 100644
--- a/drivers/usb/host/ohci-pxa27x.c
+++ b/drivers/usb/host/ohci-pxa27x.c
@@ -148,7 +148,7 @@ static int pxa27x_ohci_select_pmm(struct pxa27x_ohci *pxa_ohci, int mode)
uhcrhda |= RH_A_NPS;
break;
case PMM_GLOBAL_MODE:
- uhcrhda &= ~(RH_A_NPS & RH_A_PSM);
+ uhcrhda &= ~(RH_A_NPS | RH_A_PSM);
break;
case PMM_PERPORT_MODE:
uhcrhda &= ~(RH_A_NPS);
diff --git a/drivers/usb/host/ohci-sa1111.c b/drivers/usb/host/ohci-sa1111.c
index ebec9a7699e3..8e19a5eb5b62 100644
--- a/drivers/usb/host/ohci-sa1111.c
+++ b/drivers/usb/host/ohci-sa1111.c
@@ -84,7 +84,7 @@ static const struct hc_driver ohci_sa1111_hc_driver = {
* generic hardware linkage
*/
.irq = ohci_irq,
- .flags = HCD_USB11 | HCD_MEMORY,
+ .flags = HCD_USB11 | HCD_DMA | HCD_MEMORY,
/*
* basic lifecycle operations
diff --git a/drivers/usb/host/ohci-st.c b/drivers/usb/host/ohci-st.c
index 638a92bd2cdc..ac796ccd93ef 100644
--- a/drivers/usb/host/ohci-st.c
+++ b/drivers/usb/host/ohci-st.c
@@ -138,10 +138,8 @@ static int st_ohci_platform_probe(struct platform_device *dev)
return -ENODEV;
irq = platform_get_irq(dev, 0);
- if (irq < 0) {
- dev_err(&dev->dev, "no irq provided");
+ if (irq < 0)
return irq;
- }
res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0);
if (!res_mem) {
diff --git a/drivers/usb/host/ohci-tmio.c b/drivers/usb/host/ohci-tmio.c
index d5a293a707b6..fb6f5e9ae5c6 100644
--- a/drivers/usb/host/ohci-tmio.c
+++ b/drivers/usb/host/ohci-tmio.c
@@ -97,10 +97,13 @@ static void tmio_stop_hc(struct platform_device *dev)
switch (ohci->num_ports) {
default:
dev_err(&dev->dev, "Unsupported amount of ports: %d\n", ohci->num_ports);
+ /* fall through */
case 3:
pm |= CCR_PM_USBPW3;
+ /* fall through */
case 2:
pm |= CCR_PM_USBPW2;
+ /* fall through */
case 1:
pm |= CCR_PM_USBPW1;
}
diff --git a/drivers/usb/host/oxu210hp-hcd.c b/drivers/usb/host/oxu210hp-hcd.c
index 47c5515a9ce4..e67242e437ed 100644
--- a/drivers/usb/host/oxu210hp-hcd.c
+++ b/drivers/usb/host/oxu210hp-hcd.c
@@ -31,10 +31,449 @@
#include <linux/irq.h>
#include <linux/platform_device.h>
-#include "oxu210hp.h"
-
#define DRIVER_VERSION "0.0.50"
+#define OXU_DEVICEID 0x00
+ #define OXU_REV_MASK 0xffff0000
+ #define OXU_REV_SHIFT 16
+ #define OXU_REV_2100 0x2100
+ #define OXU_BO_SHIFT 8
+ #define OXU_BO_MASK (0x3 << OXU_BO_SHIFT)
+ #define OXU_MAJ_REV_SHIFT 4
+ #define OXU_MAJ_REV_MASK (0xf << OXU_MAJ_REV_SHIFT)
+ #define OXU_MIN_REV_SHIFT 0
+ #define OXU_MIN_REV_MASK (0xf << OXU_MIN_REV_SHIFT)
+#define OXU_HOSTIFCONFIG 0x04
+#define OXU_SOFTRESET 0x08
+ #define OXU_SRESET (1 << 0)
+
+#define OXU_PIOBURSTREADCTRL 0x0C
+
+#define OXU_CHIPIRQSTATUS 0x10
+#define OXU_CHIPIRQEN_SET 0x14
+#define OXU_CHIPIRQEN_CLR 0x18
+ #define OXU_USBSPHLPWUI 0x00000080
+ #define OXU_USBOTGLPWUI 0x00000040
+ #define OXU_USBSPHI 0x00000002
+ #define OXU_USBOTGI 0x00000001
+
+#define OXU_CLKCTRL_SET 0x1C
+ #define OXU_SYSCLKEN 0x00000008
+ #define OXU_USBSPHCLKEN 0x00000002
+ #define OXU_USBOTGCLKEN 0x00000001
+
+#define OXU_ASO 0x68
+ #define OXU_SPHPOEN 0x00000100
+ #define OXU_OVRCCURPUPDEN 0x00000800
+ #define OXU_ASO_OP (1 << 10)
+ #define OXU_COMPARATOR 0x000004000
+
+#define OXU_USBMODE 0x1A8
+ #define OXU_VBPS 0x00000020
+ #define OXU_ES_LITTLE 0x00000000
+ #define OXU_CM_HOST_ONLY 0x00000003
+
+/*
+ * Proper EHCI structs & defines
+ */
+
+/* Magic numbers that can affect system performance */
+#define EHCI_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */
+#define EHCI_TUNE_RL_HS 4 /* nak throttle; see 4.9 */
+#define EHCI_TUNE_RL_TT 0
+#define EHCI_TUNE_MULT_HS 1 /* 1-3 transactions/uframe; 4.10.3 */
+#define EHCI_TUNE_MULT_TT 1
+#define EHCI_TUNE_FLS 2 /* (small) 256 frame schedule */
+
+struct oxu_hcd;
+
+/* EHCI register interface, corresponds to EHCI Revision 0.95 specification */
+
+/* Section 2.2 Host Controller Capability Registers */
+struct ehci_caps {
+ /* these fields are specified as 8 and 16 bit registers,
+ * but some hosts can't perform 8 or 16 bit PCI accesses.
+ */
+ u32 hc_capbase;
+#define HC_LENGTH(p) (((p)>>00)&0x00ff) /* bits 7:0 */
+#define HC_VERSION(p) (((p)>>16)&0xffff) /* bits 31:16 */
+ u32 hcs_params; /* HCSPARAMS - offset 0x4 */
+#define HCS_DEBUG_PORT(p) (((p)>>20)&0xf) /* bits 23:20, debug port? */
+#define HCS_INDICATOR(p) ((p)&(1 << 16)) /* true: has port indicators */
+#define HCS_N_CC(p) (((p)>>12)&0xf) /* bits 15:12, #companion HCs */
+#define HCS_N_PCC(p) (((p)>>8)&0xf) /* bits 11:8, ports per CC */
+#define HCS_PORTROUTED(p) ((p)&(1 << 7)) /* true: port routing */
+#define HCS_PPC(p) ((p)&(1 << 4)) /* true: port power control */
+#define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */
+
+ u32 hcc_params; /* HCCPARAMS - offset 0x8 */
+#define HCC_EXT_CAPS(p) (((p)>>8)&0xff) /* for pci extended caps */
+#define HCC_ISOC_CACHE(p) ((p)&(1 << 7)) /* true: can cache isoc frame */
+#define HCC_ISOC_THRES(p) (((p)>>4)&0x7) /* bits 6:4, uframes cached */
+#define HCC_CANPARK(p) ((p)&(1 << 2)) /* true: can park on async qh */
+#define HCC_PGM_FRAMELISTLEN(p) ((p)&(1 << 1)) /* true: periodic_size changes*/
+#define HCC_64BIT_ADDR(p) ((p)&(1)) /* true: can use 64-bit addr */
+ u8 portroute[8]; /* nibbles for routing - offset 0xC */
+} __packed;
+
+
+/* Section 2.3 Host Controller Operational Registers */
+struct ehci_regs {
+ /* USBCMD: offset 0x00 */
+ u32 command;
+/* 23:16 is r/w intr rate, in microframes; default "8" == 1/msec */
+#define CMD_PARK (1<<11) /* enable "park" on async qh */
+#define CMD_PARK_CNT(c) (((c)>>8)&3) /* how many transfers to park for */
+#define CMD_LRESET (1<<7) /* partial reset (no ports, etc) */
+#define CMD_IAAD (1<<6) /* "doorbell" interrupt async advance */
+#define CMD_ASE (1<<5) /* async schedule enable */
+#define CMD_PSE (1<<4) /* periodic schedule enable */
+/* 3:2 is periodic frame list size */
+#define CMD_RESET (1<<1) /* reset HC not bus */
+#define CMD_RUN (1<<0) /* start/stop HC */
+
+ /* USBSTS: offset 0x04 */
+ u32 status;
+#define STS_ASS (1<<15) /* Async Schedule Status */
+#define STS_PSS (1<<14) /* Periodic Schedule Status */
+#define STS_RECL (1<<13) /* Reclamation */
+#define STS_HALT (1<<12) /* Not running (any reason) */
+/* some bits reserved */
+ /* these STS_* flags are also intr_enable bits (USBINTR) */
+#define STS_IAA (1<<5) /* Interrupted on async advance */
+#define STS_FATAL (1<<4) /* such as some PCI access errors */
+#define STS_FLR (1<<3) /* frame list rolled over */
+#define STS_PCD (1<<2) /* port change detect */
+#define STS_ERR (1<<1) /* "error" completion (overflow, ...) */
+#define STS_INT (1<<0) /* "normal" completion (short, ...) */
+
+#define INTR_MASK (STS_IAA | STS_FATAL | STS_PCD | STS_ERR | STS_INT)
+
+ /* USBINTR: offset 0x08 */
+ u32 intr_enable;
+
+ /* FRINDEX: offset 0x0C */
+ u32 frame_index; /* current microframe number */
+ /* CTRLDSSEGMENT: offset 0x10 */
+ u32 segment; /* address bits 63:32 if needed */
+ /* PERIODICLISTBASE: offset 0x14 */
+ u32 frame_list; /* points to periodic list */
+ /* ASYNCLISTADDR: offset 0x18 */
+ u32 async_next; /* address of next async queue head */
+
+ u32 reserved[9];
+
+ /* CONFIGFLAG: offset 0x40 */
+ u32 configured_flag;
+#define FLAG_CF (1<<0) /* true: we'll support "high speed" */
+
+ /* PORTSC: offset 0x44 */
+ u32 port_status[0]; /* up to N_PORTS */
+/* 31:23 reserved */
+#define PORT_WKOC_E (1<<22) /* wake on overcurrent (enable) */
+#define PORT_WKDISC_E (1<<21) /* wake on disconnect (enable) */
+#define PORT_WKCONN_E (1<<20) /* wake on connect (enable) */
+/* 19:16 for port testing */
+#define PORT_LED_OFF (0<<14)
+#define PORT_LED_AMBER (1<<14)
+#define PORT_LED_GREEN (2<<14)
+#define PORT_LED_MASK (3<<14)
+#define PORT_OWNER (1<<13) /* true: companion hc owns this port */
+#define PORT_POWER (1<<12) /* true: has power (see PPC) */
+#define PORT_USB11(x) (((x)&(3<<10)) == (1<<10)) /* USB 1.1 device */
+/* 11:10 for detecting lowspeed devices (reset vs release ownership) */
+/* 9 reserved */
+#define PORT_RESET (1<<8) /* reset port */
+#define PORT_SUSPEND (1<<7) /* suspend port */
+#define PORT_RESUME (1<<6) /* resume it */
+#define PORT_OCC (1<<5) /* over current change */
+#define PORT_OC (1<<4) /* over current active */
+#define PORT_PEC (1<<3) /* port enable change */
+#define PORT_PE (1<<2) /* port enable */
+#define PORT_CSC (1<<1) /* connect status change */
+#define PORT_CONNECT (1<<0) /* device connected */
+#define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC)
+} __packed;
+
+/* Appendix C, Debug port ... intended for use with special "debug devices"
+ * that can help if there's no serial console. (nonstandard enumeration.)
+ */
+struct ehci_dbg_port {
+ u32 control;
+#define DBGP_OWNER (1<<30)
+#define DBGP_ENABLED (1<<28)
+#define DBGP_DONE (1<<16)
+#define DBGP_INUSE (1<<10)
+#define DBGP_ERRCODE(x) (((x)>>7)&0x07)
+# define DBGP_ERR_BAD 1
+# define DBGP_ERR_SIGNAL 2
+#define DBGP_ERROR (1<<6)
+#define DBGP_GO (1<<5)
+#define DBGP_OUT (1<<4)
+#define DBGP_LEN(x) (((x)>>0)&0x0f)
+ u32 pids;
+#define DBGP_PID_GET(x) (((x)>>16)&0xff)
+#define DBGP_PID_SET(data, tok) (((data)<<8)|(tok))
+ u32 data03;
+ u32 data47;
+ u32 address;
+#define DBGP_EPADDR(dev, ep) (((dev)<<8)|(ep))
+} __packed;
+
+#define QTD_NEXT(dma) cpu_to_le32((u32)dma)
+
+/*
+ * EHCI Specification 0.95 Section 3.5
+ * QTD: describe data transfer components (buffer, direction, ...)
+ * See Fig 3-6 "Queue Element Transfer Descriptor Block Diagram".
+ *
+ * These are associated only with "QH" (Queue Head) structures,
+ * used with control, bulk, and interrupt transfers.
+ */
+struct ehci_qtd {
+ /* first part defined by EHCI spec */
+ __le32 hw_next; /* see EHCI 3.5.1 */
+ __le32 hw_alt_next; /* see EHCI 3.5.2 */
+ __le32 hw_token; /* see EHCI 3.5.3 */
+#define QTD_TOGGLE (1 << 31) /* data toggle */
+#define QTD_LENGTH(tok) (((tok)>>16) & 0x7fff)
+#define QTD_IOC (1 << 15) /* interrupt on complete */
+#define QTD_CERR(tok) (((tok)>>10) & 0x3)
+#define QTD_PID(tok) (((tok)>>8) & 0x3)
+#define QTD_STS_ACTIVE (1 << 7) /* HC may execute this */
+#define QTD_STS_HALT (1 << 6) /* halted on error */
+#define QTD_STS_DBE (1 << 5) /* data buffer error (in HC) */
+#define QTD_STS_BABBLE (1 << 4) /* device was babbling (qtd halted) */
+#define QTD_STS_XACT (1 << 3) /* device gave illegal response */
+#define QTD_STS_MMF (1 << 2) /* incomplete split transaction */
+#define QTD_STS_STS (1 << 1) /* split transaction state */
+#define QTD_STS_PING (1 << 0) /* issue PING? */
+ __le32 hw_buf[5]; /* see EHCI 3.5.4 */
+ __le32 hw_buf_hi[5]; /* Appendix B */
+
+ /* the rest is HCD-private */
+ dma_addr_t qtd_dma; /* qtd address */
+ struct list_head qtd_list; /* sw qtd list */
+ struct urb *urb; /* qtd's urb */
+ size_t length; /* length of buffer */
+
+ u32 qtd_buffer_len;
+ void *buffer;
+ dma_addr_t buffer_dma;
+ void *transfer_buffer;
+ void *transfer_dma;
+} __aligned(32);
+
+/* mask NakCnt+T in qh->hw_alt_next */
+#define QTD_MASK cpu_to_le32 (~0x1f)
+
+#define IS_SHORT_READ(token) (QTD_LENGTH(token) != 0 && QTD_PID(token) == 1)
+
+/* Type tag from {qh, itd, sitd, fstn}->hw_next */
+#define Q_NEXT_TYPE(dma) ((dma) & cpu_to_le32 (3 << 1))
+
+/* values for that type tag */
+#define Q_TYPE_QH cpu_to_le32 (1 << 1)
+
+/* next async queue entry, or pointer to interrupt/periodic QH */
+#define QH_NEXT(dma) (cpu_to_le32(((u32)dma)&~0x01f)|Q_TYPE_QH)
+
+/* for periodic/async schedules and qtd lists, mark end of list */
+#define EHCI_LIST_END cpu_to_le32(1) /* "null pointer" to hw */
+
+/*
+ * Entries in periodic shadow table are pointers to one of four kinds
+ * of data structure. That's dictated by the hardware; a type tag is
+ * encoded in the low bits of the hardware's periodic schedule. Use
+ * Q_NEXT_TYPE to get the tag.
+ *
+ * For entries in the async schedule, the type tag always says "qh".
+ */
+union ehci_shadow {
+ struct ehci_qh *qh; /* Q_TYPE_QH */
+ __le32 *hw_next; /* (all types) */
+ void *ptr;
+};
+
+/*
+ * EHCI Specification 0.95 Section 3.6
+ * QH: describes control/bulk/interrupt endpoints
+ * See Fig 3-7 "Queue Head Structure Layout".
+ *
+ * These appear in both the async and (for interrupt) periodic schedules.
+ */
+
+struct ehci_qh {
+ /* first part defined by EHCI spec */
+ __le32 hw_next; /* see EHCI 3.6.1 */
+ __le32 hw_info1; /* see EHCI 3.6.2 */
+#define QH_HEAD 0x00008000
+ __le32 hw_info2; /* see EHCI 3.6.2 */
+#define QH_SMASK 0x000000ff
+#define QH_CMASK 0x0000ff00
+#define QH_HUBADDR 0x007f0000
+#define QH_HUBPORT 0x3f800000
+#define QH_MULT 0xc0000000
+ __le32 hw_current; /* qtd list - see EHCI 3.6.4 */
+
+ /* qtd overlay (hardware parts of a struct ehci_qtd) */
+ __le32 hw_qtd_next;
+ __le32 hw_alt_next;
+ __le32 hw_token;
+ __le32 hw_buf[5];
+ __le32 hw_buf_hi[5];
+
+ /* the rest is HCD-private */
+ dma_addr_t qh_dma; /* address of qh */
+ union ehci_shadow qh_next; /* ptr to qh; or periodic */
+ struct list_head qtd_list; /* sw qtd list */
+ struct ehci_qtd *dummy;
+ struct ehci_qh *reclaim; /* next to reclaim */
+
+ struct oxu_hcd *oxu;
+ struct kref kref;
+ unsigned int stamp;
+
+ u8 qh_state;
+#define QH_STATE_LINKED 1 /* HC sees this */
+#define QH_STATE_UNLINK 2 /* HC may still see this */
+#define QH_STATE_IDLE 3 /* HC doesn't see this */
+#define QH_STATE_UNLINK_WAIT 4 /* LINKED and on reclaim q */
+#define QH_STATE_COMPLETING 5 /* don't touch token.HALT */
+
+ /* periodic schedule info */
+ u8 usecs; /* intr bandwidth */
+ u8 gap_uf; /* uframes split/csplit gap */
+ u8 c_usecs; /* ... split completion bw */
+ u16 tt_usecs; /* tt downstream bandwidth */
+ unsigned short period; /* polling interval */
+ unsigned short start; /* where polling starts */
+#define NO_FRAME ((unsigned short)~0) /* pick new start */
+ struct usb_device *dev; /* access to TT */
+} __aligned(32);
+
+/*
+ * Proper OXU210HP structs
+ */
+
+#define OXU_OTG_CORE_OFFSET 0x00400
+#define OXU_OTG_CAP_OFFSET (OXU_OTG_CORE_OFFSET + 0x100)
+#define OXU_SPH_CORE_OFFSET 0x00800
+#define OXU_SPH_CAP_OFFSET (OXU_SPH_CORE_OFFSET + 0x100)
+
+#define OXU_OTG_MEM 0xE000
+#define OXU_SPH_MEM 0x16000
+
+/* Only how many elements & element structure are specifies here. */
+/* 2 host controllers are enabled - total size <= 28 kbytes */
+#define DEFAULT_I_TDPS 1024
+#define QHEAD_NUM 16
+#define QTD_NUM 32
+#define SITD_NUM 8
+#define MURB_NUM 8
+
+#define BUFFER_NUM 8
+#define BUFFER_SIZE 512
+
+struct oxu_info {
+ struct usb_hcd *hcd[2];
+};
+
+struct oxu_buf {
+ u8 buffer[BUFFER_SIZE];
+} __aligned(BUFFER_SIZE);
+
+struct oxu_onchip_mem {
+ struct oxu_buf db_pool[BUFFER_NUM];
+
+ u32 frame_list[DEFAULT_I_TDPS];
+ struct ehci_qh qh_pool[QHEAD_NUM];
+ struct ehci_qtd qtd_pool[QTD_NUM];
+} __aligned(4 << 10);
+
+#define EHCI_MAX_ROOT_PORTS 15 /* see HCS_N_PORTS */
+
+struct oxu_murb {
+ struct urb urb;
+ struct urb *main;
+ u8 last;
+};
+
+struct oxu_hcd { /* one per controller */
+ unsigned int is_otg:1;
+
+ u8 qh_used[QHEAD_NUM];
+ u8 qtd_used[QTD_NUM];
+ u8 db_used[BUFFER_NUM];
+ u8 murb_used[MURB_NUM];
+
+ struct oxu_onchip_mem __iomem *mem;
+ spinlock_t mem_lock;
+
+ struct timer_list urb_timer;
+
+ struct ehci_caps __iomem *caps;
+ struct ehci_regs __iomem *regs;
+
+ u32 hcs_params; /* cached register copy */
+ spinlock_t lock;
+
+ /* async schedule support */
+ struct ehci_qh *async;
+ struct ehci_qh *reclaim;
+ unsigned int reclaim_ready:1;
+ unsigned int scanning:1;
+
+ /* periodic schedule support */
+ unsigned int periodic_size;
+ __le32 *periodic; /* hw periodic table */
+ dma_addr_t periodic_dma;
+ unsigned int i_thresh; /* uframes HC might cache */
+
+ union ehci_shadow *pshadow; /* mirror hw periodic table */
+ int next_uframe; /* scan periodic, start here */
+ unsigned int periodic_sched; /* periodic activity count */
+
+ /* per root hub port */
+ unsigned long reset_done[EHCI_MAX_ROOT_PORTS];
+ /* bit vectors (one bit per port) */
+ unsigned long bus_suspended; /* which ports were
+ * already suspended at the
+ * start of a bus suspend
+ */
+ unsigned long companion_ports;/* which ports are dedicated
+ * to the companion controller
+ */
+
+ struct timer_list watchdog;
+ unsigned long actions;
+ unsigned int stamp;
+ unsigned long next_statechange;
+ u32 command;
+
+ /* SILICON QUIRKS */
+ struct list_head urb_list; /* this is the head to urb
+ * queue that didn't get enough
+ * resources
+ */
+ struct oxu_murb *murb_pool; /* murb per split big urb */
+ unsigned int urb_len;
+
+ u8 sbrn; /* packed release number */
+};
+
+#define EHCI_IAA_JIFFIES (HZ/100) /* arbitrary; ~10 msec */
+#define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */
+#define EHCI_ASYNC_JIFFIES (HZ/20) /* async idle timeout */
+#define EHCI_SHRINK_JIFFIES (HZ/200) /* async qh unlink delay */
+
+enum ehci_timer_action {
+ TIMER_IO_WATCHDOG,
+ TIMER_IAA_WATCHDOG,
+ TIMER_ASYNC_SHRINK,
+ TIMER_ASYNC_OFF,
+};
+
/*
* Main defines
*/
@@ -2649,9 +3088,6 @@ static int oxu_reset(struct usb_hcd *hcd)
INIT_LIST_HEAD(&oxu->urb_list);
oxu->urb_len = 0;
- /* FIMXE */
- hcd->self.controller->dma_mask = NULL;
-
if (oxu->is_otg) {
oxu->caps = hcd->regs + OXU_OTG_CAP_OFFSET;
oxu->regs = hcd->regs + OXU_OTG_CAP_OFFSET + \
diff --git a/drivers/usb/host/oxu210hp.h b/drivers/usb/host/oxu210hp.h
deleted file mode 100644
index 437044147862..000000000000
--- a/drivers/usb/host/oxu210hp.h
+++ /dev/null
@@ -1,448 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * Host interface registers
- */
-
-#define OXU_DEVICEID 0x00
- #define OXU_REV_MASK 0xffff0000
- #define OXU_REV_SHIFT 16
- #define OXU_REV_2100 0x2100
- #define OXU_BO_SHIFT 8
- #define OXU_BO_MASK (0x3 << OXU_BO_SHIFT)
- #define OXU_MAJ_REV_SHIFT 4
- #define OXU_MAJ_REV_MASK (0xf << OXU_MAJ_REV_SHIFT)
- #define OXU_MIN_REV_SHIFT 0
- #define OXU_MIN_REV_MASK (0xf << OXU_MIN_REV_SHIFT)
-#define OXU_HOSTIFCONFIG 0x04
-#define OXU_SOFTRESET 0x08
- #define OXU_SRESET (1 << 0)
-
-#define OXU_PIOBURSTREADCTRL 0x0C
-
-#define OXU_CHIPIRQSTATUS 0x10
-#define OXU_CHIPIRQEN_SET 0x14
-#define OXU_CHIPIRQEN_CLR 0x18
- #define OXU_USBSPHLPWUI 0x00000080
- #define OXU_USBOTGLPWUI 0x00000040
- #define OXU_USBSPHI 0x00000002
- #define OXU_USBOTGI 0x00000001
-
-#define OXU_CLKCTRL_SET 0x1C
- #define OXU_SYSCLKEN 0x00000008
- #define OXU_USBSPHCLKEN 0x00000002
- #define OXU_USBOTGCLKEN 0x00000001
-
-#define OXU_ASO 0x68
- #define OXU_SPHPOEN 0x00000100
- #define OXU_OVRCCURPUPDEN 0x00000800
- #define OXU_ASO_OP (1 << 10)
- #define OXU_COMPARATOR 0x000004000
-
-#define OXU_USBMODE 0x1A8
- #define OXU_VBPS 0x00000020
- #define OXU_ES_LITTLE 0x00000000
- #define OXU_CM_HOST_ONLY 0x00000003
-
-/*
- * Proper EHCI structs & defines
- */
-
-/* Magic numbers that can affect system performance */
-#define EHCI_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */
-#define EHCI_TUNE_RL_HS 4 /* nak throttle; see 4.9 */
-#define EHCI_TUNE_RL_TT 0
-#define EHCI_TUNE_MULT_HS 1 /* 1-3 transactions/uframe; 4.10.3 */
-#define EHCI_TUNE_MULT_TT 1
-#define EHCI_TUNE_FLS 2 /* (small) 256 frame schedule */
-
-struct oxu_hcd;
-
-/* EHCI register interface, corresponds to EHCI Revision 0.95 specification */
-
-/* Section 2.2 Host Controller Capability Registers */
-struct ehci_caps {
- /* these fields are specified as 8 and 16 bit registers,
- * but some hosts can't perform 8 or 16 bit PCI accesses.
- */
- u32 hc_capbase;
-#define HC_LENGTH(p) (((p)>>00)&0x00ff) /* bits 7:0 */
-#define HC_VERSION(p) (((p)>>16)&0xffff) /* bits 31:16 */
- u32 hcs_params; /* HCSPARAMS - offset 0x4 */
-#define HCS_DEBUG_PORT(p) (((p)>>20)&0xf) /* bits 23:20, debug port? */
-#define HCS_INDICATOR(p) ((p)&(1 << 16)) /* true: has port indicators */
-#define HCS_N_CC(p) (((p)>>12)&0xf) /* bits 15:12, #companion HCs */
-#define HCS_N_PCC(p) (((p)>>8)&0xf) /* bits 11:8, ports per CC */
-#define HCS_PORTROUTED(p) ((p)&(1 << 7)) /* true: port routing */
-#define HCS_PPC(p) ((p)&(1 << 4)) /* true: port power control */
-#define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */
-
- u32 hcc_params; /* HCCPARAMS - offset 0x8 */
-#define HCC_EXT_CAPS(p) (((p)>>8)&0xff) /* for pci extended caps */
-#define HCC_ISOC_CACHE(p) ((p)&(1 << 7)) /* true: can cache isoc frame */
-#define HCC_ISOC_THRES(p) (((p)>>4)&0x7) /* bits 6:4, uframes cached */
-#define HCC_CANPARK(p) ((p)&(1 << 2)) /* true: can park on async qh */
-#define HCC_PGM_FRAMELISTLEN(p) ((p)&(1 << 1)) /* true: periodic_size changes*/
-#define HCC_64BIT_ADDR(p) ((p)&(1)) /* true: can use 64-bit addr */
- u8 portroute[8]; /* nibbles for routing - offset 0xC */
-} __attribute__ ((packed));
-
-
-/* Section 2.3 Host Controller Operational Registers */
-struct ehci_regs {
- /* USBCMD: offset 0x00 */
- u32 command;
-/* 23:16 is r/w intr rate, in microframes; default "8" == 1/msec */
-#define CMD_PARK (1<<11) /* enable "park" on async qh */
-#define CMD_PARK_CNT(c) (((c)>>8)&3) /* how many transfers to park for */
-#define CMD_LRESET (1<<7) /* partial reset (no ports, etc) */
-#define CMD_IAAD (1<<6) /* "doorbell" interrupt async advance */
-#define CMD_ASE (1<<5) /* async schedule enable */
-#define CMD_PSE (1<<4) /* periodic schedule enable */
-/* 3:2 is periodic frame list size */
-#define CMD_RESET (1<<1) /* reset HC not bus */
-#define CMD_RUN (1<<0) /* start/stop HC */
-
- /* USBSTS: offset 0x04 */
- u32 status;
-#define STS_ASS (1<<15) /* Async Schedule Status */
-#define STS_PSS (1<<14) /* Periodic Schedule Status */
-#define STS_RECL (1<<13) /* Reclamation */
-#define STS_HALT (1<<12) /* Not running (any reason) */
-/* some bits reserved */
- /* these STS_* flags are also intr_enable bits (USBINTR) */
-#define STS_IAA (1<<5) /* Interrupted on async advance */
-#define STS_FATAL (1<<4) /* such as some PCI access errors */
-#define STS_FLR (1<<3) /* frame list rolled over */
-#define STS_PCD (1<<2) /* port change detect */
-#define STS_ERR (1<<1) /* "error" completion (overflow, ...) */
-#define STS_INT (1<<0) /* "normal" completion (short, ...) */
-
-#define INTR_MASK (STS_IAA | STS_FATAL | STS_PCD | STS_ERR | STS_INT)
-
- /* USBINTR: offset 0x08 */
- u32 intr_enable;
-
- /* FRINDEX: offset 0x0C */
- u32 frame_index; /* current microframe number */
- /* CTRLDSSEGMENT: offset 0x10 */
- u32 segment; /* address bits 63:32 if needed */
- /* PERIODICLISTBASE: offset 0x14 */
- u32 frame_list; /* points to periodic list */
- /* ASYNCLISTADDR: offset 0x18 */
- u32 async_next; /* address of next async queue head */
-
- u32 reserved[9];
-
- /* CONFIGFLAG: offset 0x40 */
- u32 configured_flag;
-#define FLAG_CF (1<<0) /* true: we'll support "high speed" */
-
- /* PORTSC: offset 0x44 */
- u32 port_status[0]; /* up to N_PORTS */
-/* 31:23 reserved */
-#define PORT_WKOC_E (1<<22) /* wake on overcurrent (enable) */
-#define PORT_WKDISC_E (1<<21) /* wake on disconnect (enable) */
-#define PORT_WKCONN_E (1<<20) /* wake on connect (enable) */
-/* 19:16 for port testing */
-#define PORT_LED_OFF (0<<14)
-#define PORT_LED_AMBER (1<<14)
-#define PORT_LED_GREEN (2<<14)
-#define PORT_LED_MASK (3<<14)
-#define PORT_OWNER (1<<13) /* true: companion hc owns this port */
-#define PORT_POWER (1<<12) /* true: has power (see PPC) */
-#define PORT_USB11(x) (((x)&(3<<10)) == (1<<10)) /* USB 1.1 device */
-/* 11:10 for detecting lowspeed devices (reset vs release ownership) */
-/* 9 reserved */
-#define PORT_RESET (1<<8) /* reset port */
-#define PORT_SUSPEND (1<<7) /* suspend port */
-#define PORT_RESUME (1<<6) /* resume it */
-#define PORT_OCC (1<<5) /* over current change */
-#define PORT_OC (1<<4) /* over current active */
-#define PORT_PEC (1<<3) /* port enable change */
-#define PORT_PE (1<<2) /* port enable */
-#define PORT_CSC (1<<1) /* connect status change */
-#define PORT_CONNECT (1<<0) /* device connected */
-#define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC)
-} __attribute__ ((packed));
-
-/* Appendix C, Debug port ... intended for use with special "debug devices"
- * that can help if there's no serial console. (nonstandard enumeration.)
- */
-struct ehci_dbg_port {
- u32 control;
-#define DBGP_OWNER (1<<30)
-#define DBGP_ENABLED (1<<28)
-#define DBGP_DONE (1<<16)
-#define DBGP_INUSE (1<<10)
-#define DBGP_ERRCODE(x) (((x)>>7)&0x07)
-# define DBGP_ERR_BAD 1
-# define DBGP_ERR_SIGNAL 2
-#define DBGP_ERROR (1<<6)
-#define DBGP_GO (1<<5)
-#define DBGP_OUT (1<<4)
-#define DBGP_LEN(x) (((x)>>0)&0x0f)
- u32 pids;
-#define DBGP_PID_GET(x) (((x)>>16)&0xff)
-#define DBGP_PID_SET(data, tok) (((data)<<8)|(tok))
- u32 data03;
- u32 data47;
- u32 address;
-#define DBGP_EPADDR(dev, ep) (((dev)<<8)|(ep))
-} __attribute__ ((packed));
-
-
-#define QTD_NEXT(dma) cpu_to_le32((u32)dma)
-
-/*
- * EHCI Specification 0.95 Section 3.5
- * QTD: describe data transfer components (buffer, direction, ...)
- * See Fig 3-6 "Queue Element Transfer Descriptor Block Diagram".
- *
- * These are associated only with "QH" (Queue Head) structures,
- * used with control, bulk, and interrupt transfers.
- */
-struct ehci_qtd {
- /* first part defined by EHCI spec */
- __le32 hw_next; /* see EHCI 3.5.1 */
- __le32 hw_alt_next; /* see EHCI 3.5.2 */
- __le32 hw_token; /* see EHCI 3.5.3 */
-#define QTD_TOGGLE (1 << 31) /* data toggle */
-#define QTD_LENGTH(tok) (((tok)>>16) & 0x7fff)
-#define QTD_IOC (1 << 15) /* interrupt on complete */
-#define QTD_CERR(tok) (((tok)>>10) & 0x3)
-#define QTD_PID(tok) (((tok)>>8) & 0x3)
-#define QTD_STS_ACTIVE (1 << 7) /* HC may execute this */
-#define QTD_STS_HALT (1 << 6) /* halted on error */
-#define QTD_STS_DBE (1 << 5) /* data buffer error (in HC) */
-#define QTD_STS_BABBLE (1 << 4) /* device was babbling (qtd halted) */
-#define QTD_STS_XACT (1 << 3) /* device gave illegal response */
-#define QTD_STS_MMF (1 << 2) /* incomplete split transaction */
-#define QTD_STS_STS (1 << 1) /* split transaction state */
-#define QTD_STS_PING (1 << 0) /* issue PING? */
- __le32 hw_buf[5]; /* see EHCI 3.5.4 */
- __le32 hw_buf_hi[5]; /* Appendix B */
-
- /* the rest is HCD-private */
- dma_addr_t qtd_dma; /* qtd address */
- struct list_head qtd_list; /* sw qtd list */
- struct urb *urb; /* qtd's urb */
- size_t length; /* length of buffer */
-
- u32 qtd_buffer_len;
- void *buffer;
- dma_addr_t buffer_dma;
- void *transfer_buffer;
- void *transfer_dma;
-} __attribute__ ((aligned(32)));
-
-/* mask NakCnt+T in qh->hw_alt_next */
-#define QTD_MASK cpu_to_le32 (~0x1f)
-
-#define IS_SHORT_READ(token) (QTD_LENGTH(token) != 0 && QTD_PID(token) == 1)
-
-/* Type tag from {qh, itd, sitd, fstn}->hw_next */
-#define Q_NEXT_TYPE(dma) ((dma) & cpu_to_le32 (3 << 1))
-
-/* values for that type tag */
-#define Q_TYPE_QH cpu_to_le32 (1 << 1)
-
-/* next async queue entry, or pointer to interrupt/periodic QH */
-#define QH_NEXT(dma) (cpu_to_le32(((u32)dma)&~0x01f)|Q_TYPE_QH)
-
-/* for periodic/async schedules and qtd lists, mark end of list */
-#define EHCI_LIST_END cpu_to_le32(1) /* "null pointer" to hw */
-
-/*
- * Entries in periodic shadow table are pointers to one of four kinds
- * of data structure. That's dictated by the hardware; a type tag is
- * encoded in the low bits of the hardware's periodic schedule. Use
- * Q_NEXT_TYPE to get the tag.
- *
- * For entries in the async schedule, the type tag always says "qh".
- */
-union ehci_shadow {
- struct ehci_qh *qh; /* Q_TYPE_QH */
- __le32 *hw_next; /* (all types) */
- void *ptr;
-};
-
-/*
- * EHCI Specification 0.95 Section 3.6
- * QH: describes control/bulk/interrupt endpoints
- * See Fig 3-7 "Queue Head Structure Layout".
- *
- * These appear in both the async and (for interrupt) periodic schedules.
- */
-
-struct ehci_qh {
- /* first part defined by EHCI spec */
- __le32 hw_next; /* see EHCI 3.6.1 */
- __le32 hw_info1; /* see EHCI 3.6.2 */
-#define QH_HEAD 0x00008000
- __le32 hw_info2; /* see EHCI 3.6.2 */
-#define QH_SMASK 0x000000ff
-#define QH_CMASK 0x0000ff00
-#define QH_HUBADDR 0x007f0000
-#define QH_HUBPORT 0x3f800000
-#define QH_MULT 0xc0000000
- __le32 hw_current; /* qtd list - see EHCI 3.6.4 */
-
- /* qtd overlay (hardware parts of a struct ehci_qtd) */
- __le32 hw_qtd_next;
- __le32 hw_alt_next;
- __le32 hw_token;
- __le32 hw_buf[5];
- __le32 hw_buf_hi[5];
-
- /* the rest is HCD-private */
- dma_addr_t qh_dma; /* address of qh */
- union ehci_shadow qh_next; /* ptr to qh; or periodic */
- struct list_head qtd_list; /* sw qtd list */
- struct ehci_qtd *dummy;
- struct ehci_qh *reclaim; /* next to reclaim */
-
- struct oxu_hcd *oxu;
- struct kref kref;
- unsigned stamp;
-
- u8 qh_state;
-#define QH_STATE_LINKED 1 /* HC sees this */
-#define QH_STATE_UNLINK 2 /* HC may still see this */
-#define QH_STATE_IDLE 3 /* HC doesn't see this */
-#define QH_STATE_UNLINK_WAIT 4 /* LINKED and on reclaim q */
-#define QH_STATE_COMPLETING 5 /* don't touch token.HALT */
-
- /* periodic schedule info */
- u8 usecs; /* intr bandwidth */
- u8 gap_uf; /* uframes split/csplit gap */
- u8 c_usecs; /* ... split completion bw */
- u16 tt_usecs; /* tt downstream bandwidth */
- unsigned short period; /* polling interval */
- unsigned short start; /* where polling starts */
-#define NO_FRAME ((unsigned short)~0) /* pick new start */
- struct usb_device *dev; /* access to TT */
-} __attribute__ ((aligned(32)));
-
-/*
- * Proper OXU210HP structs
- */
-
-#define OXU_OTG_CORE_OFFSET 0x00400
-#define OXU_OTG_CAP_OFFSET (OXU_OTG_CORE_OFFSET + 0x100)
-#define OXU_SPH_CORE_OFFSET 0x00800
-#define OXU_SPH_CAP_OFFSET (OXU_SPH_CORE_OFFSET + 0x100)
-
-#define OXU_OTG_MEM 0xE000
-#define OXU_SPH_MEM 0x16000
-
-/* Only how many elements & element structure are specifies here. */
-/* 2 host controllers are enabled - total size <= 28 kbytes */
-#define DEFAULT_I_TDPS 1024
-#define QHEAD_NUM 16
-#define QTD_NUM 32
-#define SITD_NUM 8
-#define MURB_NUM 8
-
-#define BUFFER_NUM 8
-#define BUFFER_SIZE 512
-
-struct oxu_info {
- struct usb_hcd *hcd[2];
-};
-
-struct oxu_buf {
- u8 buffer[BUFFER_SIZE];
-} __attribute__ ((aligned(BUFFER_SIZE)));
-
-struct oxu_onchip_mem {
- struct oxu_buf db_pool[BUFFER_NUM];
-
- u32 frame_list[DEFAULT_I_TDPS];
- struct ehci_qh qh_pool[QHEAD_NUM];
- struct ehci_qtd qtd_pool[QTD_NUM];
-} __attribute__ ((aligned(4 << 10)));
-
-#define EHCI_MAX_ROOT_PORTS 15 /* see HCS_N_PORTS */
-
-struct oxu_murb {
- struct urb urb;
- struct urb *main;
- u8 last;
-};
-
-struct oxu_hcd { /* one per controller */
- unsigned int is_otg:1;
-
- u8 qh_used[QHEAD_NUM];
- u8 qtd_used[QTD_NUM];
- u8 db_used[BUFFER_NUM];
- u8 murb_used[MURB_NUM];
-
- struct oxu_onchip_mem __iomem *mem;
- spinlock_t mem_lock;
-
- struct timer_list urb_timer;
-
- struct ehci_caps __iomem *caps;
- struct ehci_regs __iomem *regs;
-
- __u32 hcs_params; /* cached register copy */
- spinlock_t lock;
-
- /* async schedule support */
- struct ehci_qh *async;
- struct ehci_qh *reclaim;
- unsigned reclaim_ready:1;
- unsigned scanning:1;
-
- /* periodic schedule support */
- unsigned periodic_size;
- __le32 *periodic; /* hw periodic table */
- dma_addr_t periodic_dma;
- unsigned i_thresh; /* uframes HC might cache */
-
- union ehci_shadow *pshadow; /* mirror hw periodic table */
- int next_uframe; /* scan periodic, start here */
- unsigned periodic_sched; /* periodic activity count */
-
- /* per root hub port */
- unsigned long reset_done[EHCI_MAX_ROOT_PORTS];
- /* bit vectors (one bit per port) */
- unsigned long bus_suspended; /* which ports were
- * already suspended at the
- * start of a bus suspend
- */
- unsigned long companion_ports;/* which ports are dedicated
- * to the companion controller
- */
-
- struct timer_list watchdog;
- unsigned long actions;
- unsigned stamp;
- unsigned long next_statechange;
- u32 command;
-
- /* SILICON QUIRKS */
- struct list_head urb_list; /* this is the head to urb
- * queue that didn't get enough
- * resources
- */
- struct oxu_murb *murb_pool; /* murb per split big urb */
- unsigned urb_len;
-
- u8 sbrn; /* packed release number */
-};
-
-#define EHCI_IAA_JIFFIES (HZ/100) /* arbitrary; ~10 msec */
-#define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */
-#define EHCI_ASYNC_JIFFIES (HZ/20) /* async idle timeout */
-#define EHCI_SHRINK_JIFFIES (HZ/200) /* async qh unlink delay */
-
-enum ehci_timer_action {
- TIMER_IO_WATCHDOG,
- TIMER_IAA_WATCHDOG,
- TIMER_ASYNC_SHRINK,
- TIMER_ASYNC_OFF,
-};
-
-#include <linux/oxu210hp.h>
diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c
index 42668aeca57c..0c03ac6b0213 100644
--- a/drivers/usb/host/r8a66597-hcd.c
+++ b/drivers/usb/host/r8a66597-hcd.c
@@ -2411,12 +2411,6 @@ static int r8a66597_probe(struct platform_device *pdev)
if (usb_disabled())
return -ENODEV;
- if (pdev->dev.dma_mask) {
- ret = -EINVAL;
- dev_err(&pdev->dev, "dma not supported\n");
- goto clean_up;
- }
-
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
ret = -ENODEV;
diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c
index 5b061e599948..72a34a1eb618 100644
--- a/drivers/usb/host/sl811-hcd.c
+++ b/drivers/usb/host/sl811-hcd.c
@@ -1632,12 +1632,6 @@ sl811h_probe(struct platform_device *dev)
irq = ires->start;
irqflags = ires->flags & IRQF_TRIGGER_MASK;
- /* refuse to confuse usbcore */
- if (dev->dev.dma_mask) {
- dev_dbg(&dev->dev, "no we won't dma\n");
- return -EINVAL;
- }
-
/* the chip may be wired for either kind of addressing */
addr = platform_get_resource(dev, IORESOURCE_MEM, 0);
data = platform_get_resource(dev, IORESOURCE_MEM, 1);
diff --git a/drivers/usb/host/u132-hcd.c b/drivers/usb/host/u132-hcd.c
index 400c40bc43a6..4efee34f154f 100644
--- a/drivers/usb/host/u132-hcd.c
+++ b/drivers/usb/host/u132-hcd.c
@@ -3077,8 +3077,6 @@ static int u132_probe(struct platform_device *pdev)
retval = ftdi_read_pcimem(pdev, roothub.a, &rh_a);
if (retval)
return retval;
- if (pdev->dev.dma_mask)
- return -EINVAL;
hcd = usb_create_hcd(&u132_hc_driver, &pdev->dev, dev_name(&pdev->dev));
if (!hcd) {
diff --git a/drivers/usb/host/uhci-grlib.c b/drivers/usb/host/uhci-grlib.c
index 2103b1ed0f8f..0a201a73b196 100644
--- a/drivers/usb/host/uhci-grlib.c
+++ b/drivers/usb/host/uhci-grlib.c
@@ -63,7 +63,7 @@ static const struct hc_driver uhci_grlib_hc_driver = {
/* Generic hardware linkage */
.irq = uhci_irq,
- .flags = HCD_MEMORY | HCD_USB11,
+ .flags = HCD_MEMORY | HCD_DMA | HCD_USB11,
/* Basic lifecycle operations */
.reset = uhci_grlib_init,
diff --git a/drivers/usb/host/uhci-pci.c b/drivers/usb/host/uhci-pci.c
index 0dd944277c99..0fa3d72bae26 100644
--- a/drivers/usb/host/uhci-pci.c
+++ b/drivers/usb/host/uhci-pci.c
@@ -261,7 +261,7 @@ static const struct hc_driver uhci_driver = {
/* Generic hardware linkage */
.irq = uhci_irq,
- .flags = HCD_USB11,
+ .flags = HCD_DMA | HCD_USB11,
/* Basic lifecycle operations */
.reset = uhci_pci_init,
diff --git a/drivers/usb/host/uhci-platform.c b/drivers/usb/host/uhci-platform.c
index 89700e26fb29..70dbd95c3f06 100644
--- a/drivers/usb/host/uhci-platform.c
+++ b/drivers/usb/host/uhci-platform.c
@@ -41,7 +41,7 @@ static const struct hc_driver uhci_platform_hc_driver = {
/* Generic hardware linkage */
.irq = uhci_irq,
- .flags = HCD_MEMORY | HCD_USB11,
+ .flags = HCD_MEMORY | HCD_DMA | HCD_USB11,
/* Basic lifecycle operations */
.reset = uhci_platform_init,
diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c
index 52e32644a4b2..93e2cca5262d 100644
--- a/drivers/usb/host/xhci-dbgcap.c
+++ b/drivers/usb/host/xhci-dbgcap.c
@@ -22,7 +22,6 @@ dbc_dma_alloc_coherent(struct xhci_hcd *xhci, size_t size,
vaddr = dma_alloc_coherent(xhci_to_hcd(xhci)->self.sysdev,
size, dma_handle, flags);
- memset(vaddr, 0, size);
return vaddr;
}
diff --git a/drivers/usb/host/xhci-dbgtty.c b/drivers/usb/host/xhci-dbgtty.c
index aff79ff5aba4..be726c791323 100644
--- a/drivers/usb/host/xhci-dbgtty.c
+++ b/drivers/usb/host/xhci-dbgtty.c
@@ -139,14 +139,14 @@ xhci_dbc_alloc_requests(struct dbc_ep *dep, struct list_head *head,
struct dbc_request *req;
for (i = 0; i < DBC_QUEUE_SIZE; i++) {
- req = dbc_alloc_request(dep, GFP_ATOMIC);
+ req = dbc_alloc_request(dep, GFP_KERNEL);
if (!req)
break;
req->length = DBC_MAX_PACKET;
req->buf = kmalloc(req->length, GFP_KERNEL);
if (!req->buf) {
- xhci_dbc_free_req(dep, req);
+ dbc_free_request(dep, req);
break;
}
diff --git a/drivers/usb/host/xhci-ext-caps.c b/drivers/usb/host/xhci-ext-caps.c
index 399113f9fc5c..f498160df969 100644
--- a/drivers/usb/host/xhci-ext-caps.c
+++ b/drivers/usb/host/xhci-ext-caps.c
@@ -6,11 +6,20 @@
*/
#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/pci.h>
#include "xhci.h"
#define USB_SW_DRV_NAME "intel_xhci_usb_sw"
#define USB_SW_RESOURCE_SIZE 0x400
+#define PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI 0x22b5
+
+static const struct property_entry role_switch_props[] = {
+ PROPERTY_ENTRY_BOOL("sw_switch_disable"),
+ {},
+};
+
static void xhci_intel_unregister_pdev(void *arg)
{
platform_device_unregister(arg);
@@ -21,6 +30,7 @@ static int xhci_create_intel_xhci_sw_pdev(struct xhci_hcd *xhci, u32 cap_offset)
struct usb_hcd *hcd = xhci_to_hcd(xhci);
struct device *dev = hcd->self.controller;
struct platform_device *pdev;
+ struct pci_dev *pci = to_pci_dev(dev);
struct resource res = { 0, };
int ret;
@@ -43,6 +53,14 @@ static int xhci_create_intel_xhci_sw_pdev(struct xhci_hcd *xhci, u32 cap_offset)
return ret;
}
+ if (pci->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI) {
+ ret = platform_device_add_properties(pdev, role_switch_props);
+ if (ret) {
+ dev_err(dev, "failed to register device properties\n");
+ return ret;
+ }
+ }
+
pdev->dev.parent = dev;
ret = platform_device_add(pdev);
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index 3abe70ff1b1e..b7d23c438756 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -1149,7 +1149,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
}
port_li = readl(ports[wIndex]->addr + PORTLI);
status = xhci_get_ext_port_status(temp, port_li);
- put_unaligned_le32(cpu_to_le32(status), &buf[4]);
+ put_unaligned_le32(status, &buf[4]);
}
break;
case SetPortFeature:
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index cf5e17962179..e16eda6e2b8b 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -2399,7 +2399,6 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
flags);
if (!xhci->dcbaa)
goto fail;
- memset(xhci->dcbaa, 0, sizeof *(xhci->dcbaa));
xhci->dcbaa->dma = dma;
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
"// Device context base array address = 0x%llx (DMA), %p (virt)",
diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c
index 026fe18972d3..b18a6baef204 100644
--- a/drivers/usb/host/xhci-mtk.c
+++ b/drivers/usb/host/xhci-mtk.c
@@ -216,6 +216,10 @@ static int xhci_mtk_clks_get(struct xhci_hcd_mtk *mtk)
return PTR_ERR(mtk->sys_clk);
}
+ mtk->xhci_clk = devm_clk_get_optional(dev, "xhci_ck");
+ if (IS_ERR(mtk->xhci_clk))
+ return PTR_ERR(mtk->xhci_clk);
+
mtk->ref_clk = devm_clk_get_optional(dev, "ref_ck");
if (IS_ERR(mtk->ref_clk))
return PTR_ERR(mtk->ref_clk);
@@ -244,6 +248,12 @@ static int xhci_mtk_clks_enable(struct xhci_hcd_mtk *mtk)
goto sys_clk_err;
}
+ ret = clk_prepare_enable(mtk->xhci_clk);
+ if (ret) {
+ dev_err(mtk->dev, "failed to enable xhci_clk\n");
+ goto xhci_clk_err;
+ }
+
ret = clk_prepare_enable(mtk->mcu_clk);
if (ret) {
dev_err(mtk->dev, "failed to enable mcu_clk\n");
@@ -261,6 +271,8 @@ static int xhci_mtk_clks_enable(struct xhci_hcd_mtk *mtk)
dma_clk_err:
clk_disable_unprepare(mtk->mcu_clk);
mcu_clk_err:
+ clk_disable_unprepare(mtk->xhci_clk);
+xhci_clk_err:
clk_disable_unprepare(mtk->sys_clk);
sys_clk_err:
clk_disable_unprepare(mtk->ref_clk);
@@ -272,6 +284,7 @@ static void xhci_mtk_clks_disable(struct xhci_hcd_mtk *mtk)
{
clk_disable_unprepare(mtk->dma_clk);
clk_disable_unprepare(mtk->mcu_clk);
+ clk_disable_unprepare(mtk->xhci_clk);
clk_disable_unprepare(mtk->sys_clk);
clk_disable_unprepare(mtk->ref_clk);
}
diff --git a/drivers/usb/host/xhci-mtk.h b/drivers/usb/host/xhci-mtk.h
index 8be8c5f7ff62..5ac458b7d2e0 100644
--- a/drivers/usb/host/xhci-mtk.h
+++ b/drivers/usb/host/xhci-mtk.h
@@ -139,6 +139,7 @@ struct xhci_hcd_mtk {
struct regulator *vusb33;
struct regulator *vbus;
struct clk *sys_clk; /* sys and mac clock */
+ struct clk *xhci_clk;
struct clk *ref_clk;
struct clk *mcu_clk;
struct clk *dma_clk;
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index 998241f5fce3..d90cd5ec09cf 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -66,12 +66,14 @@ static int xhci_priv_resume_quirk(struct usb_hcd *hcd)
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;
+ xhci->quirks |= XHCI_PLAT | priv->quirks;
}
/* called during probe() after chip reset completes */
@@ -103,17 +105,11 @@ static const struct xhci_plat_priv xhci_plat_marvell_armada3700 = {
};
static const struct xhci_plat_priv xhci_plat_renesas_rcar_gen2 = {
- .firmware_name = XHCI_RCAR_FIRMWARE_NAME_V1,
- .init_quirk = xhci_rcar_init_quirk,
- .plat_start = xhci_rcar_start,
- .resume_quirk = xhci_rcar_resume_quirk,
+ SET_XHCI_PLAT_PRIV_FOR_RCAR(XHCI_RCAR_FIRMWARE_NAME_V1)
};
static const struct xhci_plat_priv xhci_plat_renesas_rcar_gen3 = {
- .firmware_name = XHCI_RCAR_FIRMWARE_NAME_V3,
- .init_quirk = xhci_rcar_init_quirk,
- .plat_start = xhci_rcar_start,
- .resume_quirk = xhci_rcar_resume_quirk,
+ SET_XHCI_PLAT_PRIV_FOR_RCAR(XHCI_RCAR_FIRMWARE_NAME_V3)
};
static const struct of_device_id usb_xhci_of_match[] = {
@@ -307,7 +303,6 @@ static int xhci_plat_probe(struct platform_device *pdev)
ret = usb_phy_init(hcd->usb_phy);
if (ret)
goto put_usb3_hcd;
- hcd->skip_phy_initialization = 1;
}
hcd->tpl_support = of_usb_host_tpl_support(sysdev->of_node);
diff --git a/drivers/usb/host/xhci-plat.h b/drivers/usb/host/xhci-plat.h
index ae29f22ff5bd..5681723fc9cd 100644
--- a/drivers/usb/host/xhci-plat.h
+++ b/drivers/usb/host/xhci-plat.h
@@ -12,10 +12,12 @@
struct xhci_plat_priv {
const char *firmware_name;
+ unsigned long long quirks;
void (*plat_start)(struct usb_hcd *);
int (*init_quirk)(struct usb_hcd *);
int (*resume_quirk)(struct usb_hcd *);
};
#define hcd_to_xhci_priv(h) ((struct xhci_plat_priv *)hcd_to_xhci(h)->priv)
+#define xhci_to_priv(x) ((struct xhci_plat_priv *)(x)->priv)
#endif /* _XHCI_PLAT_H */
diff --git a/drivers/usb/host/xhci-rcar.c b/drivers/usb/host/xhci-rcar.c
index 2b0ccd150209..c1025d321a41 100644
--- a/drivers/usb/host/xhci-rcar.c
+++ b/drivers/usb/host/xhci-rcar.c
@@ -107,15 +107,6 @@ static int xhci_rcar_is_gen2(struct device *dev)
of_device_is_compatible(node, "renesas,rcar-gen2-xhci");
}
-static int xhci_rcar_is_gen3(struct device *dev)
-{
- struct device_node *node = dev->of_node;
-
- return of_device_is_compatible(node, "renesas,xhci-r8a7795") ||
- of_device_is_compatible(node, "renesas,xhci-r8a7796") ||
- of_device_is_compatible(node, "renesas,rcar-gen3-xhci");
-}
-
void xhci_rcar_start(struct usb_hcd *hcd)
{
u32 temp;
@@ -226,32 +217,13 @@ static bool xhci_rcar_wait_for_pll_active(struct usb_hcd *hcd)
/* This function needs to initialize a "phy" of usb before */
int xhci_rcar_init_quirk(struct usb_hcd *hcd)
{
- struct xhci_hcd *xhci = hcd_to_xhci(hcd);
-
/* If hcd->regs is NULL, we don't just call the following function */
if (!hcd->regs)
return 0;
- /*
- * On R-Car Gen2 and Gen3, the AC64 bit (bit 0) of HCCPARAMS1 is set
- * to 1. However, these SoCs don't support 64-bit address memory
- * pointers. So, this driver clears the AC64 bit of xhci->hcc_params
- * to call dma_set_coherent_mask(dev, DMA_BIT_MASK(32)) in
- * xhci_gen_setup().
- *
- * And, since the firmware/internal CPU control the USBSTS.STS_HALT
- * and the process speed is down when the roothub port enters U3,
- * long delay for the handshake of STS_HALT is neeed in xhci_suspend().
- */
- if (xhci_rcar_is_gen2(hcd->self.controller) ||
- xhci_rcar_is_gen3(hcd->self.controller)) {
- xhci->quirks |= XHCI_NO_64BIT_SUPPORT | XHCI_SLOW_SUSPEND;
- }
-
if (!xhci_rcar_wait_for_pll_active(hcd))
return -ETIMEDOUT;
- xhci->quirks |= XHCI_TRUST_TX_LENGTH;
return xhci_rcar_download_firmware(hcd);
}
diff --git a/drivers/usb/host/xhci-rcar.h b/drivers/usb/host/xhci-rcar.h
index 804b6ab4246f..012744a63a49 100644
--- a/drivers/usb/host/xhci-rcar.h
+++ b/drivers/usb/host/xhci-rcar.h
@@ -31,4 +31,25 @@ static inline int xhci_rcar_resume_quirk(struct usb_hcd *hcd)
return 0;
}
#endif
+
+/*
+ * On R-Car Gen2 and Gen3, the AC64 bit (bit 0) of HCCPARAMS1 is set
+ * to 1. However, these SoCs don't support 64-bit address memory
+ * pointers. So, this driver clears the AC64 bit of xhci->hcc_params
+ * to call dma_set_coherent_mask(dev, DMA_BIT_MASK(32)) in
+ * xhci_gen_setup() by using the XHCI_NO_64BIT_SUPPORT quirk.
+ *
+ * And, since the firmware/internal CPU control the USBSTS.STS_HALT
+ * and the process speed is down when the roothub port enters U3,
+ * long delay for the handshake of STS_HALT is neeed in xhci_suspend()
+ * by using the XHCI_SLOW_SUSPEND quirk.
+ */
+#define SET_XHCI_PLAT_PRIV_FOR_RCAR(firmware) \
+ .firmware_name = firmware, \
+ .quirks = XHCI_NO_64BIT_SUPPORT | XHCI_TRUST_TX_LENGTH | \
+ XHCI_SLOW_SUSPEND, \
+ .init_quirk = xhci_rcar_init_quirk, \
+ .plat_start = xhci_rcar_start, \
+ .resume_quirk = xhci_rcar_resume_quirk,
+
#endif /* _XHCI_RCAR_H */
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 03d1e552769b..500865975687 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -3814,7 +3814,6 @@ static void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev)
virt_dev->eps[i].ep_state &= ~EP_STOP_CMD_PENDING;
del_timer_sync(&virt_dev->eps[i].stop_cmd_timer);
}
- xhci_debugfs_remove_slot(xhci, udev->slot_id);
virt_dev->udev = NULL;
ret = xhci_disable_slot(xhci, udev->slot_id);
if (ret)
@@ -3832,6 +3831,8 @@ int xhci_disable_slot(struct xhci_hcd *xhci, u32 slot_id)
if (!command)
return -ENOMEM;
+ xhci_debugfs_remove_slot(xhci, slot_id);
+
spin_lock_irqsave(&xhci->lock, flags);
/* Don't disable the slot if the host controller is dead. */
state = readl(&xhci->op_regs->status);
@@ -5217,7 +5218,7 @@ static const struct hc_driver xhci_hc_driver = {
* generic hardware linkage
*/
.irq = xhci_irq,
- .flags = HCD_MEMORY | HCD_USB3 | HCD_SHARED,
+ .flags = HCD_MEMORY | HCD_DMA | HCD_USB3 | HCD_SHARED,
/*
* basic lifecycle operations
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index f5c41448d067..f9f88626a57a 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -2337,12 +2337,13 @@ static inline const char *xhci_decode_trb(u32 field0, u32 field1, u32 field2,
break;
case TRB_RESET_EP:
sprintf(str,
- "%s: ctx %08x%08x slot %d ep %d flags %c",
+ "%s: ctx %08x%08x slot %d ep %d flags %c:%c",
xhci_trb_type_string(type),
field1, field0,
TRB_TO_SLOT_ID(field3),
/* Macro decrements 1, maybe it shouldn't?!? */
TRB_TO_EP_INDEX(field3) + 1,
+ field3 & TRB_TSP ? 'T' : 't',
field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_STOP_RING:
diff --git a/drivers/usb/isp1760/isp1760-core.c b/drivers/usb/isp1760/isp1760-core.c
index 55b94fd10331..fdeb4cf97cc5 100644
--- a/drivers/usb/isp1760/isp1760-core.c
+++ b/drivers/usb/isp1760/isp1760-core.c
@@ -120,9 +120,6 @@ int isp1760_register(struct resource *mem, int irq, unsigned long irqflags,
(!IS_ENABLED(CONFIG_USB_ISP1761_UDC) || udc_disabled))
return -ENODEV;
- /* prevent usb-core allocating DMA pages */
- dev->dma_mask = NULL;
-
isp = devm_kzalloc(dev, sizeof(*isp), GFP_KERNEL);
if (!isp)
return -ENOMEM;
diff --git a/drivers/usb/isp1760/isp1760-if.c b/drivers/usb/isp1760/isp1760-if.c
index 241a00d75027..07cc82ff327c 100644
--- a/drivers/usb/isp1760/isp1760-if.c
+++ b/drivers/usb/isp1760/isp1760-if.c
@@ -139,7 +139,6 @@ static int isp1761_pci_probe(struct pci_dev *dev,
pci_set_master(dev);
- dev->dev.dma_mask = NULL;
ret = isp1760_register(&dev->resource[3], dev->irq, 0, &dev->dev,
devflags);
if (ret < 0)
diff --git a/drivers/usb/misc/cypress_cy7c63.c b/drivers/usb/misc/cypress_cy7c63.c
index 9d780b77314b..14faec51d7a5 100644
--- a/drivers/usb/misc/cypress_cy7c63.c
+++ b/drivers/usb/misc/cypress_cy7c63.c
@@ -183,6 +183,7 @@ static ssize_t port0_show(struct device *dev,
{
return read_port(dev, attr, buf, 0, CYPRESS_READ_PORT_ID0);
}
+static DEVICE_ATTR_RW(port0);
/* attribute callback handler (read) */
static ssize_t port1_show(struct device *dev,
@@ -190,11 +191,14 @@ static ssize_t port1_show(struct device *dev,
{
return read_port(dev, attr, buf, 1, CYPRESS_READ_PORT_ID1);
}
-
-static DEVICE_ATTR_RW(port0);
-
static DEVICE_ATTR_RW(port1);
+static struct attribute *cypress_attrs[] = {
+ &dev_attr_port0.attr,
+ &dev_attr_port1.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(cypress);
static int cypress_probe(struct usb_interface *interface,
const struct usb_device_id *id)
@@ -212,26 +216,11 @@ static int cypress_probe(struct usb_interface *interface,
/* save our data pointer in this interface device */
usb_set_intfdata(interface, dev);
- /* create device attribute files */
- retval = device_create_file(&interface->dev, &dev_attr_port0);
- if (retval)
- goto error;
- retval = device_create_file(&interface->dev, &dev_attr_port1);
- if (retval)
- goto error;
-
/* let the user know that the device is now attached */
dev_info(&interface->dev,
"Cypress CY7C63xxx device now attached\n");
return 0;
-error:
- device_remove_file(&interface->dev, &dev_attr_port0);
- device_remove_file(&interface->dev, &dev_attr_port1);
- usb_set_intfdata(interface, NULL);
- usb_put_dev(dev->udev);
- kfree(dev);
-
error_mem:
return retval;
}
@@ -242,9 +231,6 @@ static void cypress_disconnect(struct usb_interface *interface)
dev = usb_get_intfdata(interface);
- /* remove device attribute files */
- device_remove_file(&interface->dev, &dev_attr_port0);
- device_remove_file(&interface->dev, &dev_attr_port1);
/* the intfdata can be set to NULL only after the
* device files have been removed */
usb_set_intfdata(interface, NULL);
@@ -262,6 +248,7 @@ static struct usb_driver cypress_driver = {
.probe = cypress_probe,
.disconnect = cypress_disconnect,
.id_table = cypress_table,
+ .dev_groups = cypress_groups,
};
module_usb_driver(cypress_driver);
diff --git a/drivers/usb/misc/cytherm.c b/drivers/usb/misc/cytherm.c
index 8b15ab5e1450..3e3802aaefa3 100644
--- a/drivers/usb/misc/cytherm.c
+++ b/drivers/usb/misc/cytherm.c
@@ -36,20 +36,6 @@ struct usb_cytherm {
};
-/* local function prototypes */
-static int cytherm_probe(struct usb_interface *interface,
- const struct usb_device_id *id);
-static void cytherm_disconnect(struct usb_interface *interface);
-
-
-/* usb specific object needed to register this driver with the usb subsystem */
-static struct usb_driver cytherm_driver = {
- .name = "cytherm",
- .probe = cytherm_probe,
- .disconnect = cytherm_disconnect,
- .id_table = id_table,
-};
-
/* Vendor requests */
/* They all operate on one byte at a time */
#define PING 0x00
@@ -304,6 +290,15 @@ static ssize_t port1_store(struct device *dev, struct device_attribute *attr, co
}
static DEVICE_ATTR_RW(port1);
+static struct attribute *cytherm_attrs[] = {
+ &dev_attr_brightness.attr,
+ &dev_attr_temp.attr,
+ &dev_attr_button.attr,
+ &dev_attr_port0.attr,
+ &dev_attr_port1.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(cytherm);
static int cytherm_probe(struct usb_interface *interface,
const struct usb_device_id *id)
@@ -322,34 +317,10 @@ static int cytherm_probe(struct usb_interface *interface,
dev->brightness = 0xFF;
- retval = device_create_file(&interface->dev, &dev_attr_brightness);
- if (retval)
- goto error;
- retval = device_create_file(&interface->dev, &dev_attr_temp);
- if (retval)
- goto error;
- retval = device_create_file(&interface->dev, &dev_attr_button);
- if (retval)
- goto error;
- retval = device_create_file(&interface->dev, &dev_attr_port0);
- if (retval)
- goto error;
- retval = device_create_file(&interface->dev, &dev_attr_port1);
- if (retval)
- goto error;
-
dev_info (&interface->dev,
"Cypress thermometer device now attached\n");
return 0;
-error:
- device_remove_file(&interface->dev, &dev_attr_brightness);
- device_remove_file(&interface->dev, &dev_attr_temp);
- device_remove_file(&interface->dev, &dev_attr_button);
- device_remove_file(&interface->dev, &dev_attr_port0);
- device_remove_file(&interface->dev, &dev_attr_port1);
- usb_set_intfdata (interface, NULL);
- usb_put_dev(dev->udev);
- kfree(dev);
+
error_mem:
return retval;
}
@@ -360,12 +331,6 @@ static void cytherm_disconnect(struct usb_interface *interface)
dev = usb_get_intfdata (interface);
- device_remove_file(&interface->dev, &dev_attr_brightness);
- device_remove_file(&interface->dev, &dev_attr_temp);
- device_remove_file(&interface->dev, &dev_attr_button);
- device_remove_file(&interface->dev, &dev_attr_port0);
- device_remove_file(&interface->dev, &dev_attr_port1);
-
/* first remove the files, then NULL the pointer */
usb_set_intfdata (interface, NULL);
@@ -376,6 +341,15 @@ static void cytherm_disconnect(struct usb_interface *interface)
dev_info(&interface->dev, "Cypress thermometer now disconnected\n");
}
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver cytherm_driver = {
+ .name = "cytherm",
+ .probe = cytherm_probe,
+ .disconnect = cytherm_disconnect,
+ .id_table = id_table,
+ .dev_groups = cytherm_groups,
+};
+
module_usb_driver(cytherm_driver);
MODULE_AUTHOR(DRIVER_AUTHOR);
diff --git a/drivers/usb/misc/lvstest.c b/drivers/usb/misc/lvstest.c
index e5c03c6d16e9..407fe7570f3b 100644
--- a/drivers/usb/misc/lvstest.c
+++ b/drivers/usb/misc/lvstest.c
@@ -310,7 +310,7 @@ static ssize_t enable_compliance_store(struct device *dev,
}
static DEVICE_ATTR_WO(enable_compliance);
-static struct attribute *lvs_attributes[] = {
+static struct attribute *lvs_attrs[] = {
&dev_attr_get_dev_desc.attr,
&dev_attr_u1_timeout.attr,
&dev_attr_u2_timeout.attr,
@@ -321,10 +321,7 @@ static struct attribute *lvs_attributes[] = {
&dev_attr_enable_compliance.attr,
NULL
};
-
-static const struct attribute_group lvs_attr_group = {
- .attrs = lvs_attributes,
-};
+ATTRIBUTE_GROUPS(lvs);
static void lvs_rh_work(struct work_struct *work)
{
@@ -439,12 +436,6 @@ static int lvs_rh_probe(struct usb_interface *intf,
INIT_WORK(&lvs->rh_work, lvs_rh_work);
- ret = sysfs_create_group(&intf->dev.kobj, &lvs_attr_group);
- if (ret < 0) {
- dev_err(&intf->dev, "Failed to create sysfs node %d\n", ret);
- goto free_urb;
- }
-
pipe = usb_rcvintpipe(hdev, endpoint->bEndpointAddress);
maxp = usb_maxpacket(hdev, pipe, usb_pipeout(pipe));
usb_fill_int_urb(lvs->urb, hdev, pipe, &lvs->buffer[0], maxp,
@@ -453,13 +444,11 @@ static int lvs_rh_probe(struct usb_interface *intf,
ret = usb_submit_urb(lvs->urb, GFP_KERNEL);
if (ret < 0) {
dev_err(&intf->dev, "couldn't submit lvs urb %d\n", ret);
- goto sysfs_remove;
+ goto free_urb;
}
return ret;
-sysfs_remove:
- sysfs_remove_group(&intf->dev.kobj, &lvs_attr_group);
free_urb:
usb_free_urb(lvs->urb);
return ret;
@@ -469,7 +458,6 @@ static void lvs_rh_disconnect(struct usb_interface *intf)
{
struct lvs_rh *lvs = usb_get_intfdata(intf);
- sysfs_remove_group(&intf->dev.kobj, &lvs_attr_group);
usb_poison_urb(lvs->urb); /* used in scheduled work */
flush_work(&lvs->rh_work);
usb_free_urb(lvs->urb);
@@ -479,6 +467,7 @@ static struct usb_driver lvs_driver = {
.name = "lvs",
.probe = lvs_rh_probe,
.disconnect = lvs_rh_disconnect,
+ .dev_groups = lvs_groups,
};
module_usb_driver(lvs_driver);
diff --git a/drivers/usb/misc/rio500.c b/drivers/usb/misc/rio500.c
index a32d61a79ab8..30cae5e1954d 100644
--- a/drivers/usb/misc/rio500.c
+++ b/drivers/usb/misc/rio500.c
@@ -51,7 +51,6 @@ struct rio_usb_data {
char *obuf, *ibuf; /* transfer buffers */
char bulk_in_ep, bulk_out_ep; /* Endpoint assignments */
wait_queue_head_t wait_q; /* for timeouts */
- struct mutex lock; /* general race avoidance */
};
static DEFINE_MUTEX(rio500_mutex);
@@ -63,10 +62,8 @@ static int open_rio(struct inode *inode, struct file *file)
/* against disconnect() */
mutex_lock(&rio500_mutex);
- mutex_lock(&(rio->lock));
if (rio->isopen || !rio->present) {
- mutex_unlock(&(rio->lock));
mutex_unlock(&rio500_mutex);
return -EBUSY;
}
@@ -74,7 +71,6 @@ static int open_rio(struct inode *inode, struct file *file)
init_waitqueue_head(&rio->wait_q);
- mutex_unlock(&(rio->lock));
dev_info(&rio->rio_dev->dev, "Rio opened.\n");
mutex_unlock(&rio500_mutex);
@@ -88,7 +84,6 @@ static int close_rio(struct inode *inode, struct file *file)
/* against disconnect() */
mutex_lock(&rio500_mutex);
- mutex_lock(&(rio->lock));
rio->isopen = 0;
if (!rio->present) {
@@ -100,7 +95,6 @@ static int close_rio(struct inode *inode, struct file *file)
} else {
dev_info(&rio->rio_dev->dev, "Rio closed.\n");
}
- mutex_unlock(&(rio->lock));
mutex_unlock(&rio500_mutex);
return 0;
}
@@ -115,7 +109,7 @@ static long ioctl_rio(struct file *file, unsigned int cmd, unsigned long arg)
int retries;
int retval=0;
- mutex_lock(&(rio->lock));
+ mutex_lock(&rio500_mutex);
/* Sanity check to make sure rio is connected, powered, etc */
if (rio->present == 0 || rio->rio_dev == NULL) {
retval = -ENODEV;
@@ -259,7 +253,7 @@ static long ioctl_rio(struct file *file, unsigned int cmd, unsigned long arg)
err_out:
- mutex_unlock(&(rio->lock));
+ mutex_unlock(&rio500_mutex);
return retval;
}
@@ -279,12 +273,12 @@ write_rio(struct file *file, const char __user *buffer,
int errn = 0;
int intr;
- intr = mutex_lock_interruptible(&(rio->lock));
+ intr = mutex_lock_interruptible(&rio500_mutex);
if (intr)
return -EINTR;
/* Sanity check to make sure rio is connected, powered, etc */
if (rio->present == 0 || rio->rio_dev == NULL) {
- mutex_unlock(&(rio->lock));
+ mutex_unlock(&rio500_mutex);
return -ENODEV;
}
@@ -307,7 +301,7 @@ write_rio(struct file *file, const char __user *buffer,
goto error;
}
if (signal_pending(current)) {
- mutex_unlock(&(rio->lock));
+ mutex_unlock(&rio500_mutex);
return bytes_written ? bytes_written : -EINTR;
}
@@ -345,12 +339,12 @@ write_rio(struct file *file, const char __user *buffer,
buffer += copy_size;
} while (count > 0);
- mutex_unlock(&(rio->lock));
+ mutex_unlock(&rio500_mutex);
return bytes_written ? bytes_written : -EIO;
error:
- mutex_unlock(&(rio->lock));
+ mutex_unlock(&rio500_mutex);
return errn;
}
@@ -367,12 +361,12 @@ read_rio(struct file *file, char __user *buffer, size_t count, loff_t * ppos)
char *ibuf;
int intr;
- intr = mutex_lock_interruptible(&(rio->lock));
+ intr = mutex_lock_interruptible(&rio500_mutex);
if (intr)
return -EINTR;
/* Sanity check to make sure rio is connected, powered, etc */
if (rio->present == 0 || rio->rio_dev == NULL) {
- mutex_unlock(&(rio->lock));
+ mutex_unlock(&rio500_mutex);
return -ENODEV;
}
@@ -383,11 +377,11 @@ read_rio(struct file *file, char __user *buffer, size_t count, loff_t * ppos)
while (count > 0) {
if (signal_pending(current)) {
- mutex_unlock(&(rio->lock));
+ mutex_unlock(&rio500_mutex);
return read_count ? read_count : -EINTR;
}
if (!rio->rio_dev) {
- mutex_unlock(&(rio->lock));
+ mutex_unlock(&rio500_mutex);
return -ENODEV;
}
this_read = (count >= IBUF_SIZE) ? IBUF_SIZE : count;
@@ -405,7 +399,7 @@ read_rio(struct file *file, char __user *buffer, size_t count, loff_t * ppos)
count = this_read = partial;
} else if (result == -ETIMEDOUT || result == 15) { /* FIXME: 15 ??? */
if (!maxretry--) {
- mutex_unlock(&(rio->lock));
+ mutex_unlock(&rio500_mutex);
dev_err(&rio->rio_dev->dev,
"read_rio: maxretry timeout\n");
return -ETIME;
@@ -415,19 +409,19 @@ read_rio(struct file *file, char __user *buffer, size_t count, loff_t * ppos)
finish_wait(&rio->wait_q, &wait);
continue;
} else if (result != -EREMOTEIO) {
- mutex_unlock(&(rio->lock));
+ mutex_unlock(&rio500_mutex);
dev_err(&rio->rio_dev->dev,
"Read Whoops - result:%d partial:%u this_read:%u\n",
result, partial, this_read);
return -EIO;
} else {
- mutex_unlock(&(rio->lock));
+ mutex_unlock(&rio500_mutex);
return (0);
}
if (this_read) {
if (copy_to_user(buffer, ibuf, this_read)) {
- mutex_unlock(&(rio->lock));
+ mutex_unlock(&rio500_mutex);
return -EFAULT;
}
count -= this_read;
@@ -435,7 +429,7 @@ read_rio(struct file *file, char __user *buffer, size_t count, loff_t * ppos)
buffer += this_read;
}
}
- mutex_unlock(&(rio->lock));
+ mutex_unlock(&rio500_mutex);
return read_count;
}
@@ -460,53 +454,55 @@ static int probe_rio(struct usb_interface *intf,
{
struct usb_device *dev = interface_to_usbdev(intf);
struct rio_usb_data *rio = &rio_instance;
- int retval = 0;
+ int retval = -ENOMEM;
+ char *ibuf, *obuf;
- mutex_lock(&rio500_mutex);
if (rio->present) {
dev_info(&intf->dev, "Second USB Rio at address %d refused\n", dev->devnum);
- retval = -EBUSY;
- goto bail_out;
- } else {
- dev_info(&intf->dev, "USB Rio found at address %d\n", dev->devnum);
+ return -EBUSY;
}
+ dev_info(&intf->dev, "USB Rio found at address %d\n", dev->devnum);
- retval = usb_register_dev(intf, &usb_rio_class);
- if (retval) {
- dev_err(&dev->dev,
- "Not able to get a minor for this device.\n");
- retval = -ENOMEM;
- goto bail_out;
- }
-
- rio->rio_dev = dev;
-
- if (!(rio->obuf = kmalloc(OBUF_SIZE, GFP_KERNEL))) {
+ obuf = kmalloc(OBUF_SIZE, GFP_KERNEL);
+ if (!obuf) {
dev_err(&dev->dev,
"probe_rio: Not enough memory for the output buffer\n");
- usb_deregister_dev(intf, &usb_rio_class);
- retval = -ENOMEM;
- goto bail_out;
+ goto err_obuf;
}
- dev_dbg(&intf->dev, "obuf address:%p\n", rio->obuf);
+ dev_dbg(&intf->dev, "obuf address: %p\n", obuf);
- if (!(rio->ibuf = kmalloc(IBUF_SIZE, GFP_KERNEL))) {
+ ibuf = kmalloc(IBUF_SIZE, GFP_KERNEL);
+ if (!ibuf) {
dev_err(&dev->dev,
"probe_rio: Not enough memory for the input buffer\n");
- usb_deregister_dev(intf, &usb_rio_class);
- kfree(rio->obuf);
- retval = -ENOMEM;
- goto bail_out;
+ goto err_ibuf;
}
- dev_dbg(&intf->dev, "ibuf address:%p\n", rio->ibuf);
-
- mutex_init(&(rio->lock));
+ dev_dbg(&intf->dev, "ibuf address: %p\n", ibuf);
- usb_set_intfdata (intf, rio);
+ mutex_lock(&rio500_mutex);
+ rio->rio_dev = dev;
+ rio->ibuf = ibuf;
+ rio->obuf = obuf;
rio->present = 1;
-bail_out:
mutex_unlock(&rio500_mutex);
+ retval = usb_register_dev(intf, &usb_rio_class);
+ if (retval) {
+ dev_err(&dev->dev,
+ "Not able to get a minor for this device.\n");
+ goto err_register;
+ }
+
+ usb_set_intfdata(intf, rio);
+ return retval;
+
+ err_register:
+ mutex_lock(&rio500_mutex);
+ rio->present = 0;
+ mutex_unlock(&rio500_mutex);
+ err_ibuf:
+ kfree(obuf);
+ err_obuf:
return retval;
}
@@ -515,16 +511,14 @@ static void disconnect_rio(struct usb_interface *intf)
struct rio_usb_data *rio = usb_get_intfdata (intf);
usb_set_intfdata (intf, NULL);
- mutex_lock(&rio500_mutex);
if (rio) {
usb_deregister_dev(intf, &usb_rio_class);
- mutex_lock(&(rio->lock));
+ mutex_lock(&rio500_mutex);
if (rio->isopen) {
rio->isopen = 0;
/* better let it finish - the release will do whats needed */
rio->rio_dev = NULL;
- mutex_unlock(&(rio->lock));
mutex_unlock(&rio500_mutex);
return;
}
@@ -534,9 +528,8 @@ static void disconnect_rio(struct usb_interface *intf)
dev_info(&intf->dev, "USB Rio disconnected.\n");
rio->present = 0;
- mutex_unlock(&(rio->lock));
+ mutex_unlock(&rio500_mutex);
}
- mutex_unlock(&rio500_mutex);
}
static const struct usb_device_id rio_table[] = {
diff --git a/drivers/usb/misc/trancevibrator.c b/drivers/usb/misc/trancevibrator.c
index ac357ce2d1a6..a3dfc77578ea 100644
--- a/drivers/usb/misc/trancevibrator.c
+++ b/drivers/usb/misc/trancevibrator.c
@@ -71,9 +71,14 @@ static ssize_t speed_store(struct device *dev, struct device_attribute *attr,
}
return count;
}
-
static DEVICE_ATTR_RW(speed);
+static struct attribute *tv_attrs[] = {
+ &dev_attr_speed.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(tv);
+
static int tv_probe(struct usb_interface *interface,
const struct usb_device_id *id)
{
@@ -89,15 +94,9 @@ static int tv_probe(struct usb_interface *interface,
dev->udev = usb_get_dev(udev);
usb_set_intfdata(interface, dev);
- retval = device_create_file(&interface->dev, &dev_attr_speed);
- if (retval)
- goto error_create_file;
return 0;
-error_create_file:
- usb_put_dev(udev);
- usb_set_intfdata(interface, NULL);
error:
kfree(dev);
return retval;
@@ -108,7 +107,6 @@ static void tv_disconnect(struct usb_interface *interface)
struct trancevibrator *dev;
dev = usb_get_intfdata (interface);
- device_remove_file(&interface->dev, &dev_attr_speed);
usb_set_intfdata(interface, NULL);
usb_put_dev(dev->udev);
kfree(dev);
@@ -120,6 +118,7 @@ static struct usb_driver tv_driver = {
.probe = tv_probe,
.disconnect = tv_disconnect,
.id_table = id_table,
+ .dev_groups = tv_groups,
};
module_usb_driver(tv_driver);
diff --git a/drivers/usb/misc/usbsevseg.c b/drivers/usb/misc/usbsevseg.c
index 1923d5b6d9c9..551074f5b7ad 100644
--- a/drivers/usb/misc/usbsevseg.c
+++ b/drivers/usb/misc/usbsevseg.c
@@ -316,7 +316,7 @@ MYDEV_ATTR_SIMPLE_UNSIGNED(powered, update_display_powered);
MYDEV_ATTR_SIMPLE_UNSIGNED(mode_msb, update_display_mode);
MYDEV_ATTR_SIMPLE_UNSIGNED(mode_lsb, update_display_mode);
-static struct attribute *dev_attrs[] = {
+static struct attribute *sevseg_attrs[] = {
&dev_attr_powered.attr,
&dev_attr_text.attr,
&dev_attr_textmode.attr,
@@ -325,10 +325,7 @@ static struct attribute *dev_attrs[] = {
&dev_attr_mode_lsb.attr,
NULL
};
-
-static const struct attribute_group dev_attr_grp = {
- .attrs = dev_attrs,
-};
+ATTRIBUTE_GROUPS(sevseg);
static int sevseg_probe(struct usb_interface *interface,
const struct usb_device_id *id)
@@ -354,17 +351,9 @@ static int sevseg_probe(struct usb_interface *interface,
mydev->mode_msb = 0x06; /* 6 characters */
mydev->mode_lsb = 0x3f; /* scanmode for 6 chars */
- rc = sysfs_create_group(&interface->dev.kobj, &dev_attr_grp);
- if (rc)
- goto error;
-
dev_info(&interface->dev, "USB 7 Segment device now attached\n");
return 0;
-error:
- usb_set_intfdata(interface, NULL);
- usb_put_dev(mydev->udev);
- kfree(mydev);
error_mem:
return rc;
}
@@ -374,7 +363,6 @@ static void sevseg_disconnect(struct usb_interface *interface)
struct usb_sevsegdev *mydev;
mydev = usb_get_intfdata(interface);
- sysfs_remove_group(&interface->dev.kobj, &dev_attr_grp);
usb_set_intfdata(interface, NULL);
usb_put_dev(mydev->udev);
kfree(mydev);
@@ -423,6 +411,7 @@ static struct usb_driver sevseg_driver = {
.resume = sevseg_resume,
.reset_resume = sevseg_reset_resume,
.id_table = id_table,
+ .dev_groups = sevseg_groups,
.supports_autosuspend = 1,
};
diff --git a/drivers/usb/mtu3/Kconfig b/drivers/usb/mtu3/Kconfig
index 928c2cd6fc00..bf98fd36341d 100644
--- a/drivers/usb/mtu3/Kconfig
+++ b/drivers/usb/mtu3/Kconfig
@@ -44,6 +44,7 @@ config USB_MTU3_DUAL_ROLE
bool "Dual Role mode"
depends on ((USB=y || USB=USB_MTU3) && (USB_GADGET=y || USB_GADGET=USB_MTU3))
depends on (EXTCON=y || EXTCON=USB_MTU3)
+ select USB_ROLE_SWITCH
help
This is the default mode of working of MTU3 controller where
both host and gadget features are enabled.
diff --git a/drivers/usb/mtu3/mtu3.h b/drivers/usb/mtu3/mtu3.h
index 76ecf12fdf62..6087be236a35 100644
--- a/drivers/usb/mtu3/mtu3.h
+++ b/drivers/usb/mtu3/mtu3.h
@@ -199,6 +199,9 @@ struct mtu3_gpd_ring {
* @id_nb : notifier for iddig(idpin) detection
* @id_work : work of iddig detection notifier
* @id_event : event of iddig detecion notifier
+* @role_sw : use USB Role Switch to support dual-role switch, can't use
+* extcon at the same time, and extcon is deprecated.
+* @role_sw_used : true when the USB Role Switch is used.
* @is_u3_drd: whether port0 supports usb3.0 dual-role device or not
* @manual_drd_enabled: it's true when supports dual-role device by debugfs
* to switch host/device modes depending on user input.
@@ -212,6 +215,8 @@ struct otg_switch_mtk {
struct notifier_block id_nb;
struct work_struct id_work;
unsigned long id_event;
+ struct usb_role_switch *role_sw;
+ bool role_sw_used;
bool is_u3_drd;
bool manual_drd_enabled;
};
diff --git a/drivers/usb/mtu3/mtu3_core.c b/drivers/usb/mtu3/mtu3_core.c
index f8bd1d57e795..c3d5c1206eec 100644
--- a/drivers/usb/mtu3/mtu3_core.c
+++ b/drivers/usb/mtu3/mtu3_core.c
@@ -835,10 +835,8 @@ int ssusb_gadget_init(struct ssusb_mtk *ssusb)
return -ENOMEM;
mtu->irq = platform_get_irq(pdev, 0);
- if (mtu->irq < 0) {
- dev_err(dev, "fail to get irq number\n");
+ if (mtu->irq < 0)
return mtu->irq;
- }
dev_info(dev, "irq %d\n", mtu->irq);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mac");
diff --git a/drivers/usb/mtu3/mtu3_debugfs.c b/drivers/usb/mtu3/mtu3_debugfs.c
index 62c57ddc554e..c96e5dab0a48 100644
--- a/drivers/usb/mtu3/mtu3_debugfs.c
+++ b/drivers/usb/mtu3/mtu3_debugfs.c
@@ -453,9 +453,9 @@ static ssize_t ssusb_mode_write(struct file *file, const char __user *ubuf,
return -EFAULT;
if (!strncmp(buf, "host", 4) && !ssusb->is_host) {
- ssusb_mode_manual_switch(ssusb, 1);
+ ssusb_mode_switch(ssusb, 1);
} else if (!strncmp(buf, "device", 6) && ssusb->is_host) {
- ssusb_mode_manual_switch(ssusb, 0);
+ ssusb_mode_switch(ssusb, 0);
} else {
dev_err(ssusb->dev, "wrong or duplicated setting\n");
return -EINVAL;
diff --git a/drivers/usb/mtu3/mtu3_dr.c b/drivers/usb/mtu3/mtu3_dr.c
index 5fcb71af875a..08e18448e8b8 100644
--- a/drivers/usb/mtu3/mtu3_dr.c
+++ b/drivers/usb/mtu3/mtu3_dr.c
@@ -7,6 +7,8 @@
* Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
*/
+#include <linux/usb/role.h>
+
#include "mtu3.h"
#include "mtu3_dr.h"
#include "mtu3_debug.h"
@@ -280,7 +282,7 @@ static int ssusb_extcon_register(struct otg_switch_mtk *otg_sx)
* This is useful in special cases, such as uses TYPE-A receptacle but also
* wants to support dual-role mode.
*/
-void ssusb_mode_manual_switch(struct ssusb_mtk *ssusb, int to_host)
+void ssusb_mode_switch(struct ssusb_mtk *ssusb, int to_host)
{
struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
@@ -318,6 +320,47 @@ void ssusb_set_force_mode(struct ssusb_mtk *ssusb,
mtu3_writel(ssusb->ippc_base, SSUSB_U2_CTRL(0), value);
}
+static int ssusb_role_sw_set(struct device *dev, enum usb_role role)
+{
+ struct ssusb_mtk *ssusb = dev_get_drvdata(dev);
+ bool to_host = false;
+
+ if (role == USB_ROLE_HOST)
+ to_host = true;
+
+ if (to_host ^ ssusb->is_host)
+ ssusb_mode_switch(ssusb, to_host);
+
+ return 0;
+}
+
+static enum usb_role ssusb_role_sw_get(struct device *dev)
+{
+ struct ssusb_mtk *ssusb = dev_get_drvdata(dev);
+ enum usb_role role;
+
+ role = ssusb->is_host ? USB_ROLE_HOST : USB_ROLE_DEVICE;
+
+ return role;
+}
+
+static int ssusb_role_sw_register(struct otg_switch_mtk *otg_sx)
+{
+ struct usb_role_switch_desc role_sx_desc = { 0 };
+ struct ssusb_mtk *ssusb =
+ container_of(otg_sx, struct ssusb_mtk, otg_switch);
+
+ if (!otg_sx->role_sw_used)
+ return 0;
+
+ role_sx_desc.set = ssusb_role_sw_set;
+ role_sx_desc.get = ssusb_role_sw_get;
+ role_sx_desc.fwnode = dev_fwnode(ssusb->dev);
+ otg_sx->role_sw = usb_role_switch_register(ssusb->dev, &role_sx_desc);
+
+ return PTR_ERR_OR_ZERO(otg_sx->role_sw);
+}
+
int ssusb_otg_switch_init(struct ssusb_mtk *ssusb)
{
struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
@@ -328,6 +371,8 @@ int ssusb_otg_switch_init(struct ssusb_mtk *ssusb)
if (otg_sx->manual_drd_enabled)
ssusb_dr_debugfs_init(ssusb);
+ else if (otg_sx->role_sw_used)
+ ret = ssusb_role_sw_register(otg_sx);
else
ret = ssusb_extcon_register(otg_sx);
@@ -340,4 +385,5 @@ void ssusb_otg_switch_exit(struct ssusb_mtk *ssusb)
cancel_work_sync(&otg_sx->id_work);
cancel_work_sync(&otg_sx->vbus_work);
+ usb_role_switch_unregister(otg_sx->role_sw);
}
diff --git a/drivers/usb/mtu3/mtu3_dr.h b/drivers/usb/mtu3/mtu3_dr.h
index ba6fe357ce29..5e58c4dbd54a 100644
--- a/drivers/usb/mtu3/mtu3_dr.h
+++ b/drivers/usb/mtu3/mtu3_dr.h
@@ -71,7 +71,7 @@ static inline void ssusb_gadget_exit(struct ssusb_mtk *ssusb)
#if IS_ENABLED(CONFIG_USB_MTU3_DUAL_ROLE)
int ssusb_otg_switch_init(struct ssusb_mtk *ssusb);
void ssusb_otg_switch_exit(struct ssusb_mtk *ssusb);
-void ssusb_mode_manual_switch(struct ssusb_mtk *ssusb, int to_host);
+void ssusb_mode_switch(struct ssusb_mtk *ssusb, int to_host);
int ssusb_set_vbus(struct otg_switch_mtk *otg_sx, int is_on);
void ssusb_set_force_mode(struct ssusb_mtk *ssusb,
enum mtu3_dr_force_mode mode);
@@ -86,8 +86,8 @@ static inline int ssusb_otg_switch_init(struct ssusb_mtk *ssusb)
static inline void ssusb_otg_switch_exit(struct ssusb_mtk *ssusb)
{}
-static inline void
-ssusb_mode_manual_switch(struct ssusb_mtk *ssusb, int to_host) {}
+static inline void ssusb_mode_switch(struct ssusb_mtk *ssusb, int to_host)
+{}
static inline int ssusb_set_vbus(struct otg_switch_mtk *otg_sx, int is_on)
{
diff --git a/drivers/usb/mtu3/mtu3_plat.c b/drivers/usb/mtu3/mtu3_plat.c
index fd0f6c5dfbc1..9c256ea3cdf5 100644
--- a/drivers/usb/mtu3/mtu3_plat.c
+++ b/drivers/usb/mtu3/mtu3_plat.c
@@ -299,8 +299,9 @@ static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb)
otg_sx->is_u3_drd = of_property_read_bool(node, "mediatek,usb3-drd");
otg_sx->manual_drd_enabled =
of_property_read_bool(node, "enable-manual-drd");
+ otg_sx->role_sw_used = of_property_read_bool(node, "usb-role-switch");
- if (of_property_read_bool(node, "extcon")) {
+ if (!otg_sx->role_sw_used && of_property_read_bool(node, "extcon")) {
otg_sx->edev = extcon_get_edev_by_phandle(ssusb->dev, 0);
if (IS_ERR(otg_sx->edev)) {
dev_err(ssusb->dev, "couldn't get extcon device\n");
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
index 9f5a4819a744..bd63450af76a 100644
--- a/drivers/usb/musb/musb_core.c
+++ b/drivers/usb/musb/musb_core.c
@@ -1721,7 +1721,7 @@ mode_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct musb *musb = dev_to_musb(dev);
unsigned long flags;
- int ret = -EINVAL;
+ int ret;
spin_lock_irqsave(&musb->lock, flags);
ret = sprintf(buf, "%s\n", usb_otg_state_string(musb->xceiv->otg->state));
@@ -1829,16 +1829,13 @@ static ssize_t srp_store(struct device *dev, struct device_attribute *attr,
}
static DEVICE_ATTR_WO(srp);
-static struct attribute *musb_attributes[] = {
+static struct attribute *musb_attrs[] = {
&dev_attr_mode.attr,
&dev_attr_vbus.attr,
&dev_attr_srp.attr,
NULL
};
-
-static const struct attribute_group musb_attr_group = {
- .attrs = musb_attributes,
-};
+ATTRIBUTE_GROUPS(musb);
#define MUSB_QUIRK_B_INVALID_VBUS_91 (MUSB_DEVCTL_BDEVICE | \
(2 << MUSB_DEVCTL_VBUS_SHIFT) | \
@@ -2038,10 +2035,6 @@ static void musb_free(struct musb *musb)
* cleanup after everything's been de-activated.
*/
-#ifdef CONFIG_SYSFS
- sysfs_remove_group(&musb->controller->kobj, &musb_attr_group);
-#endif
-
if (musb->nIrq >= 0) {
if (musb->irq_wake)
disable_irq_wake(musb->nIrq);
@@ -2390,22 +2383,12 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
musb_init_debugfs(musb);
- status = sysfs_create_group(&musb->controller->kobj, &musb_attr_group);
- if (status)
- goto fail5;
-
musb->is_initialized = 1;
pm_runtime_mark_last_busy(musb->controller);
pm_runtime_put_autosuspend(musb->controller);
return 0;
-fail5:
- musb_exit_debugfs(musb);
-
- musb_gadget_cleanup(musb);
- musb_host_cleanup(musb);
-
fail3:
cancel_delayed_work_sync(&musb->irq_work);
cancel_delayed_work_sync(&musb->finish_resume_work);
@@ -2798,6 +2781,7 @@ static struct platform_driver musb_driver = {
.name = (char *)musb_driver_name,
.bus = &platform_bus_type,
.pm = MUSB_DEV_PM_OPS,
+ .dev_groups = musb_groups,
},
.probe = musb_probe,
.remove = musb_remove,
diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
index eb308ec35c66..5a44b70372d9 100644
--- a/drivers/usb/musb/musb_host.c
+++ b/drivers/usb/musb/musb_host.c
@@ -2689,7 +2689,7 @@ static const struct hc_driver musb_hc_driver = {
.description = "musb-hcd",
.product_desc = "MUSB HDRC host driver",
.hcd_priv_size = sizeof(struct musb *),
- .flags = HCD_USB2 | HCD_MEMORY,
+ .flags = HCD_USB2 | HCD_DMA | HCD_MEMORY,
/* not using irq handler or reset hooks from usbcore, since
* those must be shared with peripheral code for OTG configs
diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c
index aaf363f19714..4bb4b1d42f32 100644
--- a/drivers/usb/phy/phy-ab8500-usb.c
+++ b/drivers/usb/phy/phy-ab8500-usb.c
@@ -330,6 +330,7 @@ static int ab8505_usb_link_status_update(struct ab8500_usb *ab,
switch (lsts) {
case USB_LINK_ACA_RID_B_8505:
event = UX500_MUSB_RIDB;
+ /* Fall through */
case USB_LINK_NOT_CONFIGURED_8505:
case USB_LINK_RESERVED0_8505:
case USB_LINK_RESERVED1_8505:
@@ -350,6 +351,7 @@ static int ab8505_usb_link_status_update(struct ab8500_usb *ab,
case USB_LINK_ACA_RID_C_NM_8505:
event = UX500_MUSB_RIDC;
+ /* Fall through */
case USB_LINK_STD_HOST_NC_8505:
case USB_LINK_STD_HOST_C_NS_8505:
case USB_LINK_STD_HOST_C_S_8505:
@@ -368,6 +370,7 @@ static int ab8505_usb_link_status_update(struct ab8500_usb *ab,
case USB_LINK_ACA_RID_A_8505:
case USB_LINK_ACA_DOCK_CHGR_8505:
event = UX500_MUSB_RIDA;
+ /* Fall through */
case USB_LINK_HM_IDGND_8505:
if (ab->mode == USB_IDLE) {
ab->mode = USB_HOST;
@@ -422,6 +425,7 @@ static int ab8500_usb_link_status_update(struct ab8500_usb *ab,
switch (lsts) {
case USB_LINK_ACA_RID_B_8500:
event = UX500_MUSB_RIDB;
+ /* Fall through */
case USB_LINK_NOT_CONFIGURED_8500:
case USB_LINK_NOT_VALID_LINK_8500:
ab->mode = USB_IDLE;
@@ -438,6 +442,7 @@ static int ab8500_usb_link_status_update(struct ab8500_usb *ab,
case USB_LINK_ACA_RID_C_HS_8500:
case USB_LINK_ACA_RID_C_HS_CHIRP_8500:
event = UX500_MUSB_RIDC;
+ /* Fall through */
case USB_LINK_STD_HOST_NC_8500:
case USB_LINK_STD_HOST_C_NS_8500:
case USB_LINK_STD_HOST_C_S_8500:
@@ -457,6 +462,7 @@ static int ab8500_usb_link_status_update(struct ab8500_usb *ab,
case USB_LINK_ACA_RID_A_8500:
event = UX500_MUSB_RIDA;
+ /* Fall through */
case USB_LINK_HM_IDGND_8500:
if (ab->mode == USB_IDLE) {
ab->mode = USB_HOST;
@@ -712,10 +718,8 @@ static int ab8500_usb_irq_setup(struct platform_device *pdev,
if (ab->flags & AB8500_USB_FLAG_USE_LINK_STATUS_IRQ) {
irq = platform_get_irq_byname(pdev, "USB_LINK_STATUS");
- if (irq < 0) {
- dev_err(&pdev->dev, "Link status irq not found\n");
+ if (irq < 0)
return irq;
- }
err = devm_request_threaded_irq(&pdev->dev, irq, NULL,
ab8500_usb_link_status_irq,
IRQF_NO_SUSPEND | IRQF_SHARED | IRQF_ONESHOT,
@@ -728,10 +732,8 @@ static int ab8500_usb_irq_setup(struct platform_device *pdev,
if (ab->flags & AB8500_USB_FLAG_USE_ID_WAKEUP_IRQ) {
irq = platform_get_irq_byname(pdev, "ID_WAKEUP_F");
- if (irq < 0) {
- dev_err(&pdev->dev, "ID fall irq not found\n");
+ if (irq < 0)
return irq;
- }
err = devm_request_threaded_irq(&pdev->dev, irq, NULL,
ab8500_usb_disconnect_irq,
IRQF_NO_SUSPEND | IRQF_SHARED | IRQF_ONESHOT,
@@ -744,10 +746,8 @@ static int ab8500_usb_irq_setup(struct platform_device *pdev,
if (ab->flags & AB8500_USB_FLAG_USE_VBUS_DET_IRQ) {
irq = platform_get_irq_byname(pdev, "VBUS_DET_F");
- if (irq < 0) {
- dev_err(&pdev->dev, "VBUS fall irq not found\n");
+ if (irq < 0)
return irq;
- }
err = devm_request_threaded_irq(&pdev->dev, irq, NULL,
ab8500_usb_disconnect_irq,
IRQF_NO_SUSPEND | IRQF_SHARED | IRQF_ONESHOT,
diff --git a/drivers/usb/phy/phy-fsl-usb.c b/drivers/usb/phy/phy-fsl-usb.c
index f7c96d209eda..b451f4695f3f 100644
--- a/drivers/usb/phy/phy-fsl-usb.c
+++ b/drivers/usb/phy/phy-fsl-usb.c
@@ -65,7 +65,7 @@ struct fsl_otg_timer *b_data_pulse_tmr, *b_vbus_pulse_tmr, *b_srp_fail_tmr,
static struct list_head active_timers;
-static struct fsl_otg_config fsl_otg_initdata = {
+static const struct fsl_otg_config fsl_otg_initdata = {
.otg_port = 1,
};
@@ -1043,6 +1043,11 @@ static ssize_t show_fsl_usb2_otg_state(struct device *dev,
static DEVICE_ATTR(fsl_usb2_otg_state, S_IRUGO, show_fsl_usb2_otg_state, NULL);
+static struct attribute *fsl_otg_attrs[] = {
+ &dev_attr_fsl_usb2_otg_state.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(fsl_otg);
/* Char driver interface to control some OTG input */
@@ -1132,10 +1137,6 @@ static int fsl_otg_probe(struct platform_device *pdev)
return ret;
}
- ret = device_create_file(&pdev->dev, &dev_attr_fsl_usb2_otg_state);
- if (ret)
- dev_warn(&pdev->dev, "Can't register sysfs attribute\n");
-
return ret;
}
@@ -1152,8 +1153,6 @@ static int fsl_otg_remove(struct platform_device *pdev)
kfree(fsl_otg_dev->phy.otg);
kfree(fsl_otg_dev);
- device_remove_file(&pdev->dev, &dev_attr_fsl_usb2_otg_state);
-
unregister_chrdev(FSL_OTG_MAJOR, FSL_OTG_NAME);
if (pdata->exit)
@@ -1168,6 +1167,7 @@ struct platform_driver fsl_otg_driver = {
.driver = {
.name = driver_name,
.owner = THIS_MODULE,
+ .dev_groups = fsl_otg_groups,
},
};
diff --git a/drivers/usb/phy/phy-mv-usb.c b/drivers/usb/phy/phy-mv-usb.c
index cf7ecdc9a9d4..06b47f1028b3 100644
--- a/drivers/usb/phy/phy-mv-usb.c
+++ b/drivers/usb/phy/phy-mv-usb.c
@@ -641,12 +641,15 @@ static const struct attribute_group inputs_attr_group = {
.attrs = inputs_attrs,
};
+static const struct attribute_group *mv_otg_groups[] = {
+ &inputs_attr_group,
+ NULL,
+};
+
static int mv_otg_remove(struct platform_device *pdev)
{
struct mv_otg *mvotg = platform_get_drvdata(pdev);
- sysfs_remove_group(&mvotg->pdev->dev.kobj, &inputs_attr_group);
-
if (mvotg->qwork) {
flush_workqueue(mvotg->qwork);
destroy_workqueue(mvotg->qwork);
@@ -809,13 +812,6 @@ static int mv_otg_probe(struct platform_device *pdev)
goto err_disable_clk;
}
- retval = sysfs_create_group(&pdev->dev.kobj, &inputs_attr_group);
- if (retval < 0) {
- dev_dbg(&pdev->dev,
- "Can't register sysfs attr group: %d\n", retval);
- goto err_remove_phy;
- }
-
spin_lock_init(&mvotg->wq_lock);
if (spin_trylock(&mvotg->wq_lock)) {
mv_otg_run_state_machine(mvotg, 2 * HZ);
@@ -828,8 +824,6 @@ static int mv_otg_probe(struct platform_device *pdev)
return 0;
-err_remove_phy:
- usb_remove_phy(&mvotg->phy);
err_disable_clk:
mv_otg_disable_internal(mvotg);
err_destroy_workqueue:
@@ -883,6 +877,7 @@ static struct platform_driver mv_otg_driver = {
.remove = mv_otg_remove,
.driver = {
.name = driver_name,
+ .dev_groups = mv_otg_groups,
},
#ifdef CONFIG_PM
.suspend = mv_otg_suspend,
diff --git a/drivers/usb/phy/phy-tahvo.c b/drivers/usb/phy/phy-tahvo.c
index 0981abc3d1ad..baebb1f5a973 100644
--- a/drivers/usb/phy/phy-tahvo.c
+++ b/drivers/usb/phy/phy-tahvo.c
@@ -312,15 +312,12 @@ static ssize_t otg_mode_store(struct device *device,
}
static DEVICE_ATTR_RW(otg_mode);
-static struct attribute *tahvo_attributes[] = {
+static struct attribute *tahvo_attrs[] = {
&dev_attr_vbus.attr,
&dev_attr_otg_mode.attr,
NULL
};
-
-static const struct attribute_group tahvo_attr_group = {
- .attrs = tahvo_attributes,
-};
+ATTRIBUTE_GROUPS(tahvo);
static int tahvo_usb_probe(struct platform_device *pdev)
{
@@ -406,17 +403,8 @@ static int tahvo_usb_probe(struct platform_device *pdev)
goto err_remove_phy;
}
- /* Attributes */
- ret = sysfs_create_group(&pdev->dev.kobj, &tahvo_attr_group);
- if (ret) {
- dev_err(&pdev->dev, "cannot create sysfs group: %d\n", ret);
- goto err_free_irq;
- }
-
return 0;
-err_free_irq:
- free_irq(tu->irq, tu);
err_remove_phy:
usb_remove_phy(&tu->phy);
err_disable_clk:
@@ -430,7 +418,6 @@ static int tahvo_usb_remove(struct platform_device *pdev)
{
struct tahvo_usb *tu = platform_get_drvdata(pdev);
- sysfs_remove_group(&pdev->dev.kobj, &tahvo_attr_group);
free_irq(tu->irq, tu);
usb_remove_phy(&tu->phy);
if (!IS_ERR(tu->ick))
@@ -444,6 +431,7 @@ static struct platform_driver tahvo_usb_driver = {
.remove = tahvo_usb_remove,
.driver = {
.name = "tahvo-usb",
+ .dev_groups = tahvo_groups,
},
};
module_platform_driver(tahvo_usb_driver);
diff --git a/drivers/usb/phy/phy-twl6030-usb.c b/drivers/usb/phy/phy-twl6030-usb.c
index dade34d70419..bfebf1f2e991 100644
--- a/drivers/usb/phy/phy-twl6030-usb.c
+++ b/drivers/usb/phy/phy-twl6030-usb.c
@@ -196,6 +196,12 @@ static ssize_t vbus_show(struct device *dev,
}
static DEVICE_ATTR_RO(vbus);
+static struct attribute *twl6030_attrs[] = {
+ &dev_attr_vbus.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(twl6030);
+
static irqreturn_t twl6030_usb_irq(int irq, void *_twl)
{
struct twl6030_usb *twl = _twl;
@@ -361,8 +367,6 @@ static int twl6030_usb_probe(struct platform_device *pdev)
}
platform_set_drvdata(pdev, twl);
- if (device_create_file(&pdev->dev, &dev_attr_vbus))
- dev_warn(&pdev->dev, "could not create sysfs file\n");
INIT_WORK(&twl->set_vbus_work, otg_set_vbus_work);
INIT_DELAYED_WORK(&twl->get_status_work, twl6030_status_work);
@@ -373,7 +377,6 @@ static int twl6030_usb_probe(struct platform_device *pdev)
if (status < 0) {
dev_err(&pdev->dev, "can't get IRQ %d, err %d\n",
twl->irq1, status);
- device_remove_file(twl->dev, &dev_attr_vbus);
return status;
}
@@ -384,7 +387,6 @@ static int twl6030_usb_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "can't get IRQ %d, err %d\n",
twl->irq2, status);
free_irq(twl->irq1, twl);
- device_remove_file(twl->dev, &dev_attr_vbus);
return status;
}
@@ -408,7 +410,6 @@ static int twl6030_usb_remove(struct platform_device *pdev)
free_irq(twl->irq1, twl);
free_irq(twl->irq2, twl);
regulator_put(twl->usb3v3);
- device_remove_file(twl->dev, &dev_attr_vbus);
cancel_work_sync(&twl->set_vbus_work);
return 0;
@@ -426,6 +427,7 @@ static struct platform_driver twl6030_usb_driver = {
.driver = {
.name = "twl6030_usb",
.of_match_table = of_match_ptr(twl6030_usb_id_table),
+ .dev_groups = twl6030_groups,
},
};
diff --git a/drivers/usb/renesas_usbhs/mod_host.c b/drivers/usb/renesas_usbhs/mod_host.c
index ddd3be48f948..ae54221011c3 100644
--- a/drivers/usb/renesas_usbhs/mod_host.c
+++ b/drivers/usb/renesas_usbhs/mod_host.c
@@ -1283,7 +1283,7 @@ static const struct hc_driver usbhsh_driver = {
/*
* generic hardware linkage
*/
- .flags = HCD_USB2,
+ .flags = HCD_DMA | HCD_USB2,
.start = usbhsh_host_start,
.stop = usbhsh_host_stop,
diff --git a/drivers/usb/roles/class.c b/drivers/usb/roles/class.c
index 0526efbc4922..94b4e7db2b94 100644
--- a/drivers/usb/roles/class.c
+++ b/drivers/usb/roles/class.c
@@ -102,6 +102,19 @@ static void *usb_role_switch_match(struct device_connection *con, int ep,
return dev ? to_role_switch(dev) : ERR_PTR(-EPROBE_DEFER);
}
+static struct usb_role_switch *
+usb_role_switch_is_parent(struct fwnode_handle *fwnode)
+{
+ struct fwnode_handle *parent = fwnode_get_parent(fwnode);
+ struct device *dev;
+
+ if (!parent || !fwnode_property_present(parent, "usb-role-switch"))
+ return NULL;
+
+ dev = class_find_device_by_fwnode(role_class, parent);
+ return dev ? to_role_switch(dev) : ERR_PTR(-EPROBE_DEFER);
+}
+
/**
* usb_role_switch_get - Find USB role switch linked with the caller
* @dev: The caller device
@@ -113,8 +126,10 @@ struct usb_role_switch *usb_role_switch_get(struct device *dev)
{
struct usb_role_switch *sw;
- sw = device_connection_find_match(dev, "usb-role-switch", NULL,
- usb_role_switch_match);
+ sw = usb_role_switch_is_parent(dev_fwnode(dev));
+ if (!sw)
+ sw = device_connection_find_match(dev, "usb-role-switch", NULL,
+ usb_role_switch_match);
if (!IS_ERR_OR_NULL(sw))
WARN_ON(!try_module_get(sw->dev.parent->driver->owner));
@@ -124,6 +139,28 @@ struct usb_role_switch *usb_role_switch_get(struct device *dev)
EXPORT_SYMBOL_GPL(usb_role_switch_get);
/**
+ * fwnode_usb_role_switch_get - Find USB role switch linked with the caller
+ * @fwnode: The caller device node
+ *
+ * This is similar to the usb_role_switch_get() function above, but it searches
+ * the switch using fwnode instead of device entry.
+ */
+struct usb_role_switch *fwnode_usb_role_switch_get(struct fwnode_handle *fwnode)
+{
+ struct usb_role_switch *sw;
+
+ sw = usb_role_switch_is_parent(fwnode);
+ if (!sw)
+ sw = fwnode_connection_find_match(fwnode, "usb-role-switch",
+ NULL, usb_role_switch_match);
+ if (!IS_ERR_OR_NULL(sw))
+ WARN_ON(!try_module_get(sw->dev.parent->driver->owner));
+
+ return sw;
+}
+EXPORT_SYMBOL_GPL(fwnode_usb_role_switch_get);
+
+/**
* usb_role_switch_put - Release handle to a switch
* @sw: USB Role Switch
*
diff --git a/drivers/usb/roles/intel-xhci-usb-role-switch.c b/drivers/usb/roles/intel-xhci-usb-role-switch.c
index 7325a84dd1c8..409851306e99 100644
--- a/drivers/usb/roles/intel-xhci-usb-role-switch.c
+++ b/drivers/usb/roles/intel-xhci-usb-role-switch.c
@@ -19,6 +19,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
+#include <linux/property.h>
#include <linux/usb/role.h>
/* register definition */
@@ -26,6 +27,12 @@
#define SW_VBUS_VALID BIT(24)
#define SW_IDPIN_EN BIT(21)
#define SW_IDPIN BIT(20)
+#define SW_SWITCH_EN BIT(16)
+
+#define DRD_CONFIG_DYNAMIC 0
+#define DRD_CONFIG_STATIC_HOST 1
+#define DRD_CONFIG_STATIC_DEVICE 2
+#define DRD_CONFIG_MASK 3
#define DUAL_ROLE_CFG1 0x6c
#define HOST_MODE BIT(29)
@@ -37,6 +44,7 @@
struct intel_xhci_usb_data {
struct usb_role_switch *role_sw;
void __iomem *base;
+ bool enable_sw_switch;
};
static const struct software_node intel_xhci_usb_node = {
@@ -49,6 +57,7 @@ static int intel_xhci_usb_set_role(struct device *dev, enum usb_role role)
unsigned long timeout;
acpi_status status;
u32 glk, val;
+ u32 drd_config = DRD_CONFIG_DYNAMIC;
/*
* On many CHT devices ACPI event (_AEI) handlers read / modify /
@@ -63,24 +72,35 @@ static int intel_xhci_usb_set_role(struct device *dev, enum usb_role role)
pm_runtime_get_sync(dev);
- /* Set idpin value as requested */
+ /*
+ * Set idpin value as requested.
+ * Since some devices rely on firmware setting DRD_CONFIG and
+ * SW_SWITCH_EN bits to be zero for role switch,
+ * do not set these bits for those devices.
+ */
val = readl(data->base + DUAL_ROLE_CFG0);
switch (role) {
case USB_ROLE_NONE:
val |= SW_IDPIN;
val &= ~SW_VBUS_VALID;
+ drd_config = DRD_CONFIG_DYNAMIC;
break;
case USB_ROLE_HOST:
val &= ~SW_IDPIN;
val &= ~SW_VBUS_VALID;
+ drd_config = DRD_CONFIG_STATIC_HOST;
break;
case USB_ROLE_DEVICE:
val |= SW_IDPIN;
val |= SW_VBUS_VALID;
+ drd_config = DRD_CONFIG_STATIC_DEVICE;
break;
}
val |= SW_IDPIN_EN;
-
+ if (data->enable_sw_switch) {
+ val &= ~DRD_CONFIG_MASK;
+ val |= SW_SWITCH_EN | drd_config;
+ }
writel(val, data->base + DUAL_ROLE_CFG0);
acpi_release_global_lock(glk);
@@ -156,6 +176,9 @@ static int intel_xhci_usb_probe(struct platform_device *pdev)
sw_desc.allow_userspace_control = true,
sw_desc.fwnode = software_node_fwnode(&intel_xhci_usb_node);
+ data->enable_sw_switch = !device_property_read_bool(dev,
+ "sw_switch_disable");
+
data->role_sw = usb_role_switch_register(dev, &sw_desc);
if (IS_ERR(data->role_sw)) {
fwnode_handle_put(sw_desc.fwnode);
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index 4b3a049561f3..f0688c44b04c 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -2023,6 +2023,46 @@ static int ftdi_read_eeprom(struct usb_serial *serial, void *dst, u16 addr,
return 0;
}
+static int ftdi_gpio_init_ft232h(struct usb_serial_port *port)
+{
+ struct ftdi_private *priv = usb_get_serial_port_data(port);
+ u16 cbus_config;
+ u8 *buf;
+ int ret;
+ int i;
+
+ buf = kmalloc(4, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ ret = ftdi_read_eeprom(port->serial, buf, 0x1a, 4);
+ if (ret < 0)
+ goto out_free;
+
+ /*
+ * FT232H CBUS Memory Map
+ *
+ * 0x1a: X- (upper nibble -> AC5)
+ * 0x1b: -X (lower nibble -> AC6)
+ * 0x1c: XX (upper nibble -> AC9 | lower nibble -> AC8)
+ */
+ cbus_config = buf[2] << 8 | (buf[1] & 0xf) << 4 | (buf[0] & 0xf0) >> 4;
+
+ priv->gc.ngpio = 4;
+ priv->gpio_altfunc = 0xff;
+
+ for (i = 0; i < priv->gc.ngpio; ++i) {
+ if ((cbus_config & 0xf) == FTDI_FTX_CBUS_MUX_GPIO)
+ priv->gpio_altfunc &= ~BIT(i);
+ cbus_config >>= 4;
+ }
+
+out_free:
+ kfree(buf);
+
+ return ret;
+}
+
static int ftdi_gpio_init_ft232r(struct usb_serial_port *port)
{
struct ftdi_private *priv = usb_get_serial_port_data(port);
@@ -2098,6 +2138,9 @@ static int ftdi_gpio_init(struct usb_serial_port *port)
int result;
switch (priv->chip_type) {
+ case FT232H:
+ result = ftdi_gpio_init_ft232h(port);
+ break;
case FT232RL:
result = ftdi_gpio_init_ft232r(port);
break;
diff --git a/drivers/usb/storage/debug.h b/drivers/usb/storage/debug.h
index 6d64f342f587..16ce06039a4d 100644
--- a/drivers/usb/storage/debug.h
+++ b/drivers/usb/storage/debug.h
@@ -29,8 +29,6 @@
#include <linux/kernel.h>
-#define USB_STORAGE "usb-storage: "
-
#ifdef CONFIG_USB_STORAGE_DEBUG
void usb_stor_show_command(const struct us_data *us, struct scsi_cmnd *srb);
void usb_stor_show_sense(const struct us_data *us, unsigned char key,
diff --git a/drivers/usb/storage/isd200.c b/drivers/usb/storage/isd200.c
index 2b474d60b4db..28e1128d53a4 100644
--- a/drivers/usb/storage/isd200.c
+++ b/drivers/usb/storage/isd200.c
@@ -1511,7 +1511,7 @@ static int isd200_Initialization(struct us_data *us)
static void isd200_ata_command(struct scsi_cmnd *srb, struct us_data *us)
{
- int sendToTransport = 1, orig_bufflen;
+ int sendToTransport, orig_bufflen;
union ata_cdb ataCdb;
/* Make sure driver was initialized */
diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c
index 05b80211290d..6737fab94959 100644
--- a/drivers/usb/storage/scsiglue.c
+++ b/drivers/usb/storage/scsiglue.c
@@ -40,6 +40,7 @@
#include <scsi/scsi_eh.h>
#include "usb.h"
+#include <linux/usb/hcd.h>
#include "scsiglue.h"
#include "debug.h"
#include "transport.h"
@@ -141,11 +142,10 @@ static int slave_configure(struct scsi_device *sdev)
/*
* Some USB host controllers can't do DMA; they have to use PIO.
- * They indicate this by setting their dma_mask to NULL. For
- * such controllers we need to make sure the block layer sets
+ * For such controllers we need to make sure the block layer sets
* up bounce buffers in addressable memory.
*/
- if (!us->pusb_dev->bus->controller->dma_mask)
+ if (!hcd_uses_dma(bus_to_hcd(us->pusb_dev->bus)))
blk_queue_bounce_limit(sdev->request_queue, BLK_BOUNCE_HIGH);
/*
@@ -379,7 +379,7 @@ static int queuecommand_lck(struct scsi_cmnd *srb,
/* check for state-transition errors */
if (us->srb != NULL) {
- printk(KERN_ERR USB_STORAGE "Error in %s: us->srb = %p\n",
+ printk(KERN_ERR "usb-storage: Error in %s: us->srb = %p\n",
__func__, us->srb);
return SCSI_MLQUEUE_HOST_BUSY;
}
diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig
index 89d9193bd1cf..895e2418de53 100644
--- a/drivers/usb/typec/Kconfig
+++ b/drivers/usb/typec/Kconfig
@@ -53,6 +53,7 @@ source "drivers/usb/typec/ucsi/Kconfig"
config TYPEC_TPS6598X
tristate "TI TPS6598x USB Power Delivery controller driver"
depends on I2C
+ select REGMAP_I2C
help
Say Y or M here if your system has TI TPS65982 or TPS65983 USB Power
Delivery controller.
diff --git a/drivers/usb/typec/mux.c b/drivers/usb/typec/mux.c
index 61b7bc58dd81..57907f26f681 100644
--- a/drivers/usb/typec/mux.c
+++ b/drivers/usb/typec/mux.c
@@ -215,7 +215,7 @@ static void *typec_mux_match(struct device_connection *con, int ep, void *data)
}
/* Alternate Mode muxes */
- nval = fwnode_property_read_u16_array(con->fwnode, "svid", NULL, 0);
+ nval = fwnode_property_count_u16(con->fwnode, "svid");
if (nval <= 0)
return NULL;
diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c
index c524088246ee..ed8655c6af8c 100644
--- a/drivers/usb/typec/tcpm/fusb302.c
+++ b/drivers/usb/typec/tcpm/fusb302.c
@@ -26,6 +26,7 @@
#include <linux/spinlock.h>
#include <linux/string.h>
#include <linux/types.h>
+#include <linux/usb.h>
#include <linux/usb/typec.h>
#include <linux/usb/tcpm.h>
#include <linux/usb/pd.h>
@@ -75,7 +76,6 @@ struct fusb302_chip {
struct i2c_client *i2c_client;
struct tcpm_port *tcpm_port;
struct tcpc_dev tcpc_dev;
- struct tcpc_config tcpc_config;
struct regulator *vbus;
@@ -207,23 +207,19 @@ static int fusb302_debug_show(struct seq_file *s, void *v)
}
DEFINE_SHOW_ATTRIBUTE(fusb302_debug);
-static struct dentry *rootdir;
-
static void fusb302_debugfs_init(struct fusb302_chip *chip)
{
- mutex_init(&chip->logbuffer_lock);
- if (!rootdir)
- rootdir = debugfs_create_dir("fusb302", NULL);
+ char name[NAME_MAX];
- chip->dentry = debugfs_create_file(dev_name(chip->dev),
- S_IFREG | 0444, rootdir,
+ mutex_init(&chip->logbuffer_lock);
+ snprintf(name, NAME_MAX, "fusb302-%s", dev_name(chip->dev));
+ chip->dentry = debugfs_create_file(name, S_IFREG | 0444, usb_debug_root,
chip, &fusb302_debug_fops);
}
static void fusb302_debugfs_exit(struct fusb302_chip *chip)
{
debugfs_remove(chip->dentry);
- debugfs_remove(rootdir);
}
#else
@@ -1110,23 +1106,6 @@ done:
mutex_unlock(&chip->lock);
}
-#define PDO_FIXED_FLAGS \
- (PDO_FIXED_DUAL_ROLE | PDO_FIXED_DATA_SWAP | PDO_FIXED_USB_COMM)
-
-static const u32 src_pdo[] = {
- PDO_FIXED(5000, 400, PDO_FIXED_FLAGS),
-};
-
-static const struct tcpc_config fusb302_tcpc_config = {
- .src_pdo = src_pdo,
- .nr_src_pdo = ARRAY_SIZE(src_pdo),
- .operating_snk_mw = 2500,
- .type = TYPEC_PORT_DRP,
- .data = TYPEC_PORT_DRD,
- .default_role = TYPEC_SINK,
- .alt_modes = NULL,
-};
-
static void init_tcpc_dev(struct tcpc_dev *fusb302_tcpc_dev)
{
fusb302_tcpc_dev->init = tcpm_init;
@@ -1670,27 +1649,36 @@ static int init_gpio(struct fusb302_chip *chip)
return 0;
}
-static int fusb302_composite_snk_pdo_array(struct fusb302_chip *chip)
-{
- struct device *dev = chip->dev;
- u32 max_uv, max_ua;
+#define PDO_FIXED_FLAGS \
+ (PDO_FIXED_DUAL_ROLE | PDO_FIXED_DATA_SWAP | PDO_FIXED_USB_COMM)
- chip->snk_pdo[0] = PDO_FIXED(5000, 400, PDO_FIXED_FLAGS);
+static const u32 src_pdo[] = {
+ PDO_FIXED(5000, 400, PDO_FIXED_FLAGS)
+};
- /*
- * As max_snk_ma/mv/mw is not needed for tcpc_config,
- * those settings should be passed in via sink PDO, so
- * "fcs, max-sink-*" properties will be deprecated, to
- * perserve compatibility with existing users of them,
- * we read those properties to convert them to be a var
- * PDO.
- */
- if (device_property_read_u32(dev, "fcs,max-sink-microvolt", &max_uv) ||
- device_property_read_u32(dev, "fcs,max-sink-microamp", &max_ua))
- return 1;
+static const u32 snk_pdo[] = {
+ PDO_FIXED(5000, 400, PDO_FIXED_FLAGS)
+};
+
+static const struct property_entry port_props[] = {
+ PROPERTY_ENTRY_STRING("data-role", "dual"),
+ PROPERTY_ENTRY_STRING("power-role", "dual"),
+ PROPERTY_ENTRY_STRING("try-power-role", "sink"),
+ PROPERTY_ENTRY_U32_ARRAY("source-pdos", src_pdo),
+ PROPERTY_ENTRY_U32_ARRAY("sink-pdos", snk_pdo),
+ PROPERTY_ENTRY_U32("op-sink-microwatt", 2500),
+ { }
+};
- chip->snk_pdo[1] = PDO_VAR(5000, max_uv / 1000, max_ua / 1000);
- return 2;
+static struct fwnode_handle *fusb302_fwnode_get(struct device *dev)
+{
+ struct fwnode_handle *fwnode;
+
+ fwnode = device_get_named_child_node(dev, "connector");
+ if (!fwnode)
+ fwnode = fwnode_create_software_node(port_props, NULL);
+
+ return fwnode;
}
static int fusb302_probe(struct i2c_client *client,
@@ -1701,7 +1689,6 @@ static int fusb302_probe(struct i2c_client *client,
struct device *dev = &client->dev;
const char *name;
int ret = 0;
- u32 v;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) {
dev_err(&client->dev,
@@ -1714,20 +1701,8 @@ static int fusb302_probe(struct i2c_client *client,
chip->i2c_client = client;
chip->dev = &client->dev;
- chip->tcpc_config = fusb302_tcpc_config;
- chip->tcpc_dev.config = &chip->tcpc_config;
mutex_init(&chip->lock);
- chip->tcpc_dev.fwnode =
- device_get_named_child_node(dev, "connector");
-
- if (!device_property_read_u32(dev, "fcs,operating-sink-microwatt", &v))
- chip->tcpc_config.operating_snk_mw = v / 1000;
-
- /* Composite sink PDO */
- chip->tcpc_config.nr_snk_pdo = fusb302_composite_snk_pdo_array(chip);
- chip->tcpc_config.snk_pdo = chip->snk_pdo;
-
/*
* Devicetree platforms should get extcon via phandle (not yet
* supported). On ACPI platforms, we get the name from a device prop.
@@ -1753,6 +1728,7 @@ static int fusb302_probe(struct i2c_client *client,
INIT_WORK(&chip->irq_work, fusb302_irq_work);
INIT_DELAYED_WORK(&chip->bc_lvl_handler, fusb302_bc_lvl_handler_work);
init_tcpc_dev(&chip->tcpc_dev);
+ fusb302_debugfs_init(chip);
if (client->irq) {
chip->gpio_int_n_irq = client->irq;
@@ -1762,8 +1738,15 @@ static int fusb302_probe(struct i2c_client *client,
goto destroy_workqueue;
}
+ chip->tcpc_dev.fwnode = fusb302_fwnode_get(dev);
+ if (IS_ERR(chip->tcpc_dev.fwnode)) {
+ ret = PTR_ERR(chip->tcpc_dev.fwnode);
+ goto destroy_workqueue;
+ }
+
chip->tcpm_port = tcpm_register_port(&client->dev, &chip->tcpc_dev);
if (IS_ERR(chip->tcpm_port)) {
+ fwnode_handle_put(chip->tcpc_dev.fwnode);
ret = PTR_ERR(chip->tcpm_port);
if (ret != -EPROBE_DEFER)
dev_err(dev, "cannot register tcpm port, ret=%d", ret);
@@ -1778,14 +1761,15 @@ static int fusb302_probe(struct i2c_client *client,
goto tcpm_unregister_port;
}
enable_irq_wake(chip->gpio_int_n_irq);
- fusb302_debugfs_init(chip);
i2c_set_clientdata(client, chip);
return ret;
tcpm_unregister_port:
tcpm_unregister_port(chip->tcpm_port);
+ fwnode_handle_put(chip->tcpc_dev.fwnode);
destroy_workqueue:
+ fusb302_debugfs_exit(chip);
destroy_workqueue(chip->wq);
return ret;
@@ -1800,6 +1784,7 @@ static int fusb302_remove(struct i2c_client *client)
cancel_work_sync(&chip->irq_work);
cancel_delayed_work_sync(&chip->bc_lvl_handler);
tcpm_unregister_port(chip->tcpm_port);
+ fwnode_handle_put(chip->tcpc_dev.fwnode);
destroy_workqueue(chip->wq);
fusb302_debugfs_exit(chip);
diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
index bcfdb55fd198..96562744101c 100644
--- a/drivers/usb/typec/tcpm/tcpm.c
+++ b/drivers/usb/typec/tcpm/tcpm.c
@@ -19,6 +19,7 @@
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
+#include <linux/usb.h>
#include <linux/usb/pd.h>
#include <linux/usb/pd_ado.h>
#include <linux/usb/pd_bdo.h>
@@ -571,17 +572,13 @@ static int tcpm_debug_show(struct seq_file *s, void *v)
}
DEFINE_SHOW_ATTRIBUTE(tcpm_debug);
-static struct dentry *rootdir;
-
static void tcpm_debugfs_init(struct tcpm_port *port)
{
- mutex_init(&port->logbuffer_lock);
- /* /sys/kernel/debug/tcpm/usbcX */
- if (!rootdir)
- rootdir = debugfs_create_dir("tcpm", NULL);
+ char name[NAME_MAX];
- port->dentry = debugfs_create_file(dev_name(port->dev),
- S_IFREG | 0444, rootdir,
+ mutex_init(&port->logbuffer_lock);
+ snprintf(name, NAME_MAX, "tcpm-%s", dev_name(port->dev));
+ port->dentry = debugfs_create_file(name, S_IFREG | 0444, usb_debug_root,
port, &tcpm_debug_fops);
}
@@ -597,10 +594,6 @@ static void tcpm_debugfs_exit(struct tcpm_port *port)
mutex_unlock(&port->logbuffer_lock);
debugfs_remove(port->dentry);
- if (list_empty(&rootdir->d_subdirs)) {
- debugfs_remove(rootdir);
- rootdir = NULL;
- }
}
#else
@@ -4434,8 +4427,7 @@ static int tcpm_fw_get_caps(struct tcpm_port *port,
goto sink;
/* Get source pdos */
- ret = fwnode_property_read_u32_array(fwnode, "source-pdos",
- NULL, 0);
+ ret = fwnode_property_count_u32(fwnode, "source-pdos");
if (ret <= 0)
return -EINVAL;
@@ -4459,8 +4451,7 @@ static int tcpm_fw_get_caps(struct tcpm_port *port,
return -EINVAL;
sink:
/* Get sink pdos */
- ret = fwnode_property_read_u32_array(fwnode, "sink-pdos",
- NULL, 0);
+ ret = fwnode_property_count_u32(fwnode, "sink-pdos");
if (ret <= 0)
return -EINVAL;
diff --git a/drivers/usb/typec/tcpm/wcove.c b/drivers/usb/typec/tcpm/wcove.c
index 6b317c150bdd..edc271da14f4 100644
--- a/drivers/usb/typec/tcpm/wcove.c
+++ b/drivers/usb/typec/tcpm/wcove.c
@@ -617,10 +617,8 @@ static int wcove_typec_probe(struct platform_device *pdev)
wcove->regmap = pmic->regmap;
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "Failed to get IRQ: %d\n", irq);
+ if (irq < 0)
return irq;
- }
irq = regmap_irq_get_virq(pmic->irq_chip_data_chgr, irq);
if (irq < 0)
diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c
index 8e9f8fba55af..907e20e1a71e 100644
--- a/drivers/usb/typec/ucsi/ucsi_ccg.c
+++ b/drivers/usb/typec/ucsi/ucsi_ccg.c
@@ -1104,14 +1104,11 @@ static ssize_t do_flash_store(struct device *dev,
static DEVICE_ATTR_WO(do_flash);
-static struct attribute *ucsi_ccg_sysfs_attrs[] = {
+static struct attribute *ucsi_ccg_attrs[] = {
&dev_attr_do_flash.attr,
NULL,
};
-
-static struct attribute_group ucsi_ccg_attr_group = {
- .attrs = ucsi_ccg_sysfs_attrs,
-};
+ATTRIBUTE_GROUPS(ucsi_ccg);
static int ucsi_ccg_probe(struct i2c_client *client,
const struct i2c_device_id *id)
@@ -1189,10 +1186,6 @@ static int ucsi_ccg_probe(struct i2c_client *client,
i2c_set_clientdata(client, uc);
- status = sysfs_create_group(&uc->dev->kobj, &ucsi_ccg_attr_group);
- if (status)
- dev_err(uc->dev, "cannot create sysfs group: %d\n", status);
-
pm_runtime_set_active(uc->dev);
pm_runtime_enable(uc->dev);
pm_runtime_idle(uc->dev);
@@ -1209,7 +1202,6 @@ static int ucsi_ccg_remove(struct i2c_client *client)
ucsi_unregister_ppm(uc->ucsi);
pm_runtime_disable(uc->dev);
free_irq(uc->irq, uc);
- sysfs_remove_group(&uc->dev->kobj, &ucsi_ccg_attr_group);
return 0;
}
@@ -1270,6 +1262,7 @@ static struct i2c_driver ucsi_ccg_driver = {
.driver = {
.name = "ucsi_ccg",
.pm = &ucsi_ccg_pm,
+ .dev_groups = ucsi_ccg_groups,
},
.probe = ucsi_ccg_probe,
.remove = ucsi_ccg_remove,
diff --git a/drivers/usb/usb-skeleton.c b/drivers/usb/usb-skeleton.c
index f101347e3ea3..c31d17d05810 100644
--- a/drivers/usb/usb-skeleton.c
+++ b/drivers/usb/usb-skeleton.c
@@ -35,9 +35,11 @@ MODULE_DEVICE_TABLE(usb, skel_table);
/* our private defines. if this grows any larger, use your own .h file */
#define MAX_TRANSFER (PAGE_SIZE - 512)
-/* MAX_TRANSFER is chosen so that the VM is not stressed by
- allocations > PAGE_SIZE and the number of packets in a page
- is an integer 512 is the largest possible packet on EHCI */
+/*
+ * MAX_TRANSFER is chosen so that the VM is not stressed by
+ * allocations > PAGE_SIZE and the number of packets in a page
+ * is an integer 512 is the largest possible packet on EHCI
+ */
#define WRITES_IN_FLIGHT 8
/* arbitrarily chosen */
diff --git a/drivers/usb/usbip/stub.h b/drivers/usb/usbip/stub.h
index 35618ceb2791..d11270560c24 100644
--- a/drivers/usb/usbip/stub.h
+++ b/drivers/usb/usbip/stub.h
@@ -52,7 +52,11 @@ struct stub_priv {
unsigned long seqnum;
struct list_head list;
struct stub_device *sdev;
- struct urb *urb;
+ struct urb **urbs;
+ struct scatterlist *sgl;
+ int num_urbs;
+ int completed_urbs;
+ int urb_status;
int unlinking;
};
@@ -86,6 +90,7 @@ extern struct usb_device_driver stub_driver;
struct bus_id_priv *get_busid_priv(const char *busid);
void put_busid_priv(struct bus_id_priv *bid);
int del_match_busid(char *busid);
+void stub_free_priv_and_urb(struct stub_priv *priv);
void stub_device_cleanup_urbs(struct stub_device *sdev);
/* stub_rx.c */
diff --git a/drivers/usb/usbip/stub_dev.c b/drivers/usb/usbip/stub_dev.c
index 7931e6cecc70..2305d425e6c9 100644
--- a/drivers/usb/usbip/stub_dev.c
+++ b/drivers/usb/usbip/stub_dev.c
@@ -106,38 +106,13 @@ err:
}
static DEVICE_ATTR_WO(usbip_sockfd);
-static int stub_add_files(struct device *dev)
-{
- int err = 0;
-
- err = device_create_file(dev, &dev_attr_usbip_status);
- if (err)
- goto err_status;
-
- err = device_create_file(dev, &dev_attr_usbip_sockfd);
- if (err)
- goto err_sockfd;
-
- err = device_create_file(dev, &dev_attr_usbip_debug);
- if (err)
- goto err_debug;
-
- return 0;
-
-err_debug:
- device_remove_file(dev, &dev_attr_usbip_sockfd);
-err_sockfd:
- device_remove_file(dev, &dev_attr_usbip_status);
-err_status:
- return err;
-}
-
-static void stub_remove_files(struct device *dev)
-{
- device_remove_file(dev, &dev_attr_usbip_status);
- device_remove_file(dev, &dev_attr_usbip_sockfd);
- device_remove_file(dev, &dev_attr_usbip_debug);
-}
+static struct attribute *usbip_attrs[] = {
+ &dev_attr_usbip_status.attr,
+ &dev_attr_usbip_sockfd.attr,
+ &dev_attr_usbip_debug.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(usbip);
static void stub_shutdown_connection(struct usbip_device *ud)
{
@@ -379,17 +354,8 @@ static int stub_probe(struct usb_device *udev)
goto err_port;
}
- rc = stub_add_files(&udev->dev);
- if (rc) {
- dev_err(&udev->dev, "stub_add_files for %s\n", udev_busid);
- goto err_files;
- }
-
return 0;
-err_files:
- usb_hub_release_port(udev->parent, udev->portnum,
- (struct usb_dev_state *) udev);
err_port:
dev_set_drvdata(&udev->dev, NULL);
usb_put_dev(udev);
@@ -457,7 +423,6 @@ static void stub_disconnect(struct usb_device *udev)
/*
* NOTE: rx/tx threads are invoked for each usb_device.
*/
- stub_remove_files(&udev->dev);
/* release port */
rc = usb_hub_release_port(udev->parent, udev->portnum,
@@ -526,4 +491,5 @@ struct usb_device_driver stub_driver = {
.resume = stub_resume,
#endif
.supports_autosuspend = 0,
+ .dev_groups = usbip_groups,
};
diff --git a/drivers/usb/usbip/stub_main.c b/drivers/usb/usbip/stub_main.c
index 2e4bfccd4bfc..c1c0bbc9f8b1 100644
--- a/drivers/usb/usbip/stub_main.c
+++ b/drivers/usb/usbip/stub_main.c
@@ -6,6 +6,7 @@
#include <linux/string.h>
#include <linux/module.h>
#include <linux/device.h>
+#include <linux/scatterlist.h>
#include "usbip_common.h"
#include "stub.h"
@@ -281,13 +282,49 @@ static struct stub_priv *stub_priv_pop_from_listhead(struct list_head *listhead)
struct stub_priv *priv, *tmp;
list_for_each_entry_safe(priv, tmp, listhead, list) {
- list_del(&priv->list);
+ list_del_init(&priv->list);
return priv;
}
return NULL;
}
+void stub_free_priv_and_urb(struct stub_priv *priv)
+{
+ struct urb *urb;
+ int i;
+
+ for (i = 0; i < priv->num_urbs; i++) {
+ urb = priv->urbs[i];
+
+ if (!urb)
+ return;
+
+ kfree(urb->setup_packet);
+ urb->setup_packet = NULL;
+
+
+ if (urb->transfer_buffer && !priv->sgl) {
+ kfree(urb->transfer_buffer);
+ urb->transfer_buffer = NULL;
+ }
+
+ if (urb->num_sgs) {
+ sgl_free(urb->sg);
+ urb->sg = NULL;
+ urb->num_sgs = 0;
+ }
+
+ usb_free_urb(urb);
+ }
+ if (!list_empty(&priv->list))
+ list_del(&priv->list);
+ if (priv->sgl)
+ sgl_free(priv->sgl);
+ kfree(priv->urbs);
+ kmem_cache_free(stub_priv_cache, priv);
+}
+
static struct stub_priv *stub_priv_pop(struct stub_device *sdev)
{
unsigned long flags;
@@ -314,25 +351,15 @@ done:
void stub_device_cleanup_urbs(struct stub_device *sdev)
{
struct stub_priv *priv;
- struct urb *urb;
+ int i;
dev_dbg(&sdev->udev->dev, "Stub device cleaning up urbs\n");
while ((priv = stub_priv_pop(sdev))) {
- urb = priv->urb;
- dev_dbg(&sdev->udev->dev, "free urb seqnum %lu\n",
- priv->seqnum);
- usb_kill_urb(urb);
-
- kmem_cache_free(stub_priv_cache, priv);
+ for (i = 0; i < priv->num_urbs; i++)
+ usb_kill_urb(priv->urbs[i]);
- kfree(urb->transfer_buffer);
- urb->transfer_buffer = NULL;
-
- kfree(urb->setup_packet);
- urb->setup_packet = NULL;
-
- usb_free_urb(urb);
+ stub_free_priv_and_urb(priv);
}
}
diff --git a/drivers/usb/usbip/stub_rx.c b/drivers/usb/usbip/stub_rx.c
index b0a855acafa3..66edfeea68fe 100644
--- a/drivers/usb/usbip/stub_rx.c
+++ b/drivers/usb/usbip/stub_rx.c
@@ -7,6 +7,7 @@
#include <linux/kthread.h>
#include <linux/usb.h>
#include <linux/usb/hcd.h>
+#include <linux/scatterlist.h>
#include "usbip_common.h"
#include "stub.h"
@@ -201,7 +202,7 @@ static void tweak_special_requests(struct urb *urb)
static int stub_recv_cmd_unlink(struct stub_device *sdev,
struct usbip_header *pdu)
{
- int ret;
+ int ret, i;
unsigned long flags;
struct stub_priv *priv;
@@ -246,12 +247,14 @@ static int stub_recv_cmd_unlink(struct stub_device *sdev,
* so a driver in a client host will know the failure
* of the unlink request ?
*/
- ret = usb_unlink_urb(priv->urb);
- if (ret != -EINPROGRESS)
- dev_err(&priv->urb->dev->dev,
- "failed to unlink a urb # %lu, ret %d\n",
- priv->seqnum, ret);
-
+ for (i = priv->completed_urbs; i < priv->num_urbs; i++) {
+ ret = usb_unlink_urb(priv->urbs[i]);
+ if (ret != -EINPROGRESS)
+ dev_err(&priv->urbs[i]->dev->dev,
+ "failed to unlink %d/%d urb of seqnum %lu, ret %d\n",
+ i + 1, priv->num_urbs,
+ priv->seqnum, ret);
+ }
return 0;
}
@@ -433,14 +436,36 @@ static void masking_bogus_flags(struct urb *urb)
urb->transfer_flags &= allowed;
}
+static int stub_recv_xbuff(struct usbip_device *ud, struct stub_priv *priv)
+{
+ int ret;
+ int i;
+
+ for (i = 0; i < priv->num_urbs; i++) {
+ ret = usbip_recv_xbuff(ud, priv->urbs[i]);
+ if (ret < 0)
+ break;
+ }
+
+ return ret;
+}
+
static void stub_recv_cmd_submit(struct stub_device *sdev,
struct usbip_header *pdu)
{
- int ret;
struct stub_priv *priv;
struct usbip_device *ud = &sdev->ud;
struct usb_device *udev = sdev->udev;
+ struct scatterlist *sgl = NULL, *sg;
+ void *buffer = NULL;
+ unsigned long long buf_len;
+ int nents;
+ int num_urbs = 1;
int pipe = get_pipe(sdev, pdu);
+ int use_sg = pdu->u.cmd_submit.transfer_flags & URB_DMA_MAP_SG;
+ int support_sg = 1;
+ int np = 0;
+ int ret, i;
if (pipe == -1)
return;
@@ -449,76 +474,139 @@ static void stub_recv_cmd_submit(struct stub_device *sdev,
if (!priv)
return;
- /* setup a urb */
- if (usb_pipeisoc(pipe))
- priv->urb = usb_alloc_urb(pdu->u.cmd_submit.number_of_packets,
- GFP_KERNEL);
- else
- priv->urb = usb_alloc_urb(0, GFP_KERNEL);
+ buf_len = (unsigned long long)pdu->u.cmd_submit.transfer_buffer_length;
- if (!priv->urb) {
- usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC);
- return;
+ /* allocate urb transfer buffer, if needed */
+ if (buf_len) {
+ if (use_sg) {
+ sgl = sgl_alloc(buf_len, GFP_KERNEL, &nents);
+ if (!sgl)
+ goto err_malloc;
+ } else {
+ buffer = kzalloc(buf_len, GFP_KERNEL);
+ if (!buffer)
+ goto err_malloc;
+ }
}
- /* allocate urb transfer buffer, if needed */
- if (pdu->u.cmd_submit.transfer_buffer_length > 0) {
- priv->urb->transfer_buffer =
- kzalloc(pdu->u.cmd_submit.transfer_buffer_length,
- GFP_KERNEL);
- if (!priv->urb->transfer_buffer) {
+ /* Check if the server's HCD supports SG */
+ if (use_sg && !udev->bus->sg_tablesize) {
+ /*
+ * If the server's HCD doesn't support SG, break a single SG
+ * request into several URBs and map each SG list entry to
+ * corresponding URB buffer. The previously allocated SG
+ * list is stored in priv->sgl (If the server's HCD support SG,
+ * SG list is stored only in urb->sg) and it is used as an
+ * indicator that the server split single SG request into
+ * several URBs. Later, priv->sgl is used by stub_complete() and
+ * stub_send_ret_submit() to reassemble the divied URBs.
+ */
+ support_sg = 0;
+ num_urbs = nents;
+ priv->completed_urbs = 0;
+ pdu->u.cmd_submit.transfer_flags &= ~URB_DMA_MAP_SG;
+ }
+
+ /* allocate urb array */
+ priv->num_urbs = num_urbs;
+ priv->urbs = kmalloc_array(num_urbs, sizeof(*priv->urbs), GFP_KERNEL);
+ if (!priv->urbs)
+ goto err_urbs;
+
+ /* setup a urb */
+ if (support_sg) {
+ if (usb_pipeisoc(pipe))
+ np = pdu->u.cmd_submit.number_of_packets;
+
+ priv->urbs[0] = usb_alloc_urb(np, GFP_KERNEL);
+ if (!priv->urbs[0])
+ goto err_urb;
+
+ if (buf_len) {
+ if (use_sg) {
+ priv->urbs[0]->sg = sgl;
+ priv->urbs[0]->num_sgs = nents;
+ priv->urbs[0]->transfer_buffer = NULL;
+ } else {
+ priv->urbs[0]->transfer_buffer = buffer;
+ }
+ }
+
+ /* copy urb setup packet */
+ priv->urbs[0]->setup_packet = kmemdup(&pdu->u.cmd_submit.setup,
+ 8, GFP_KERNEL);
+ if (!priv->urbs[0]->setup_packet) {
usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC);
return;
}
- }
- /* copy urb setup packet */
- priv->urb->setup_packet = kmemdup(&pdu->u.cmd_submit.setup, 8,
- GFP_KERNEL);
- if (!priv->urb->setup_packet) {
- dev_err(&udev->dev, "allocate setup_packet\n");
- usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC);
- return;
+ usbip_pack_pdu(pdu, priv->urbs[0], USBIP_CMD_SUBMIT, 0);
+ } else {
+ for_each_sg(sgl, sg, nents, i) {
+ priv->urbs[i] = usb_alloc_urb(0, GFP_KERNEL);
+ /* The URBs which is previously allocated will be freed
+ * in stub_device_cleanup_urbs() if error occurs.
+ */
+ if (!priv->urbs[i])
+ goto err_urb;
+
+ usbip_pack_pdu(pdu, priv->urbs[i], USBIP_CMD_SUBMIT, 0);
+ priv->urbs[i]->transfer_buffer = sg_virt(sg);
+ priv->urbs[i]->transfer_buffer_length = sg->length;
+ }
+ priv->sgl = sgl;
}
- /* set other members from the base header of pdu */
- priv->urb->context = (void *) priv;
- priv->urb->dev = udev;
- priv->urb->pipe = pipe;
- priv->urb->complete = stub_complete;
+ for (i = 0; i < num_urbs; i++) {
+ /* set other members from the base header of pdu */
+ priv->urbs[i]->context = (void *) priv;
+ priv->urbs[i]->dev = udev;
+ priv->urbs[i]->pipe = pipe;
+ priv->urbs[i]->complete = stub_complete;
- usbip_pack_pdu(pdu, priv->urb, USBIP_CMD_SUBMIT, 0);
+ /* no need to submit an intercepted request, but harmless? */
+ tweak_special_requests(priv->urbs[i]);
+ masking_bogus_flags(priv->urbs[i]);
+ }
- if (usbip_recv_xbuff(ud, priv->urb) < 0)
+ if (stub_recv_xbuff(ud, priv) < 0)
return;
- if (usbip_recv_iso(ud, priv->urb) < 0)
+ if (usbip_recv_iso(ud, priv->urbs[0]) < 0)
return;
- /* no need to submit an intercepted request, but harmless? */
- tweak_special_requests(priv->urb);
-
- masking_bogus_flags(priv->urb);
/* urb is now ready to submit */
- ret = usb_submit_urb(priv->urb, GFP_KERNEL);
-
- if (ret == 0)
- usbip_dbg_stub_rx("submit urb ok, seqnum %u\n",
- pdu->base.seqnum);
- else {
- dev_err(&udev->dev, "submit_urb error, %d\n", ret);
- usbip_dump_header(pdu);
- usbip_dump_urb(priv->urb);
-
- /*
- * Pessimistic.
- * This connection will be discarded.
- */
- usbip_event_add(ud, SDEV_EVENT_ERROR_SUBMIT);
+ for (i = 0; i < priv->num_urbs; i++) {
+ ret = usb_submit_urb(priv->urbs[i], GFP_KERNEL);
+
+ if (ret == 0)
+ usbip_dbg_stub_rx("submit urb ok, seqnum %u\n",
+ pdu->base.seqnum);
+ else {
+ dev_err(&udev->dev, "submit_urb error, %d\n", ret);
+ usbip_dump_header(pdu);
+ usbip_dump_urb(priv->urbs[i]);
+
+ /*
+ * Pessimistic.
+ * This connection will be discarded.
+ */
+ usbip_event_add(ud, SDEV_EVENT_ERROR_SUBMIT);
+ break;
+ }
}
usbip_dbg_stub_rx("Leave\n");
+ return;
+
+err_urb:
+ kfree(priv->urbs);
+err_urbs:
+ kfree(buffer);
+ sgl_free(sgl);
+err_malloc:
+ usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC);
}
/* recv a pdu */
diff --git a/drivers/usb/usbip/stub_tx.c b/drivers/usb/usbip/stub_tx.c
index f0ec41a50cbc..36010a82b359 100644
--- a/drivers/usb/usbip/stub_tx.c
+++ b/drivers/usb/usbip/stub_tx.c
@@ -5,25 +5,11 @@
#include <linux/kthread.h>
#include <linux/socket.h>
+#include <linux/scatterlist.h>
#include "usbip_common.h"
#include "stub.h"
-static void stub_free_priv_and_urb(struct stub_priv *priv)
-{
- struct urb *urb = priv->urb;
-
- kfree(urb->setup_packet);
- urb->setup_packet = NULL;
-
- kfree(urb->transfer_buffer);
- urb->transfer_buffer = NULL;
-
- list_del(&priv->list);
- kmem_cache_free(stub_priv_cache, priv);
- usb_free_urb(urb);
-}
-
/* be in spin_lock_irqsave(&sdev->priv_lock, flags) */
void stub_enqueue_ret_unlink(struct stub_device *sdev, __u32 seqnum,
__u32 status)
@@ -85,6 +71,22 @@ void stub_complete(struct urb *urb)
break;
}
+ /*
+ * If the server breaks single SG request into the several URBs, the
+ * URBs must be reassembled before sending completed URB to the vhci.
+ * Don't wake up the tx thread until all the URBs are completed.
+ */
+ if (priv->sgl) {
+ priv->completed_urbs++;
+
+ /* Only save the first error status */
+ if (urb->status && !priv->urb_status)
+ priv->urb_status = urb->status;
+
+ if (priv->completed_urbs < priv->num_urbs)
+ return;
+ }
+
/* link a urb to the queue of tx. */
spin_lock_irqsave(&sdev->priv_lock, flags);
if (sdev->ud.tcp_socket == NULL) {
@@ -156,18 +158,22 @@ static int stub_send_ret_submit(struct stub_device *sdev)
size_t total_size = 0;
while ((priv = dequeue_from_priv_tx(sdev)) != NULL) {
- int ret;
- struct urb *urb = priv->urb;
+ struct urb *urb = priv->urbs[0];
struct usbip_header pdu_header;
struct usbip_iso_packet_descriptor *iso_buffer = NULL;
struct kvec *iov = NULL;
+ struct scatterlist *sg;
+ u32 actual_length = 0;
int iovnum = 0;
+ int ret;
+ int i;
txsize = 0;
memset(&pdu_header, 0, sizeof(pdu_header));
memset(&msg, 0, sizeof(msg));
- if (urb->actual_length > 0 && !urb->transfer_buffer) {
+ if (urb->actual_length > 0 && !urb->transfer_buffer &&
+ !urb->num_sgs) {
dev_err(&sdev->udev->dev,
"urb: actual_length %d transfer_buffer null\n",
urb->actual_length);
@@ -176,6 +182,11 @@ static int stub_send_ret_submit(struct stub_device *sdev)
if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS)
iovnum = 2 + urb->number_of_packets;
+ else if (usb_pipein(urb->pipe) && urb->actual_length > 0 &&
+ urb->num_sgs)
+ iovnum = 1 + urb->num_sgs;
+ else if (usb_pipein(urb->pipe) && priv->sgl)
+ iovnum = 1 + priv->num_urbs;
else
iovnum = 2;
@@ -192,6 +203,15 @@ static int stub_send_ret_submit(struct stub_device *sdev)
setup_ret_submit_pdu(&pdu_header, urb);
usbip_dbg_stub_tx("setup txdata seqnum: %d\n",
pdu_header.base.seqnum);
+
+ if (priv->sgl) {
+ for (i = 0; i < priv->num_urbs; i++)
+ actual_length += priv->urbs[i]->actual_length;
+
+ pdu_header.u.ret_submit.status = priv->urb_status;
+ pdu_header.u.ret_submit.actual_length = actual_length;
+ }
+
usbip_header_correct_endian(&pdu_header, 1);
iov[iovnum].iov_base = &pdu_header;
@@ -200,12 +220,47 @@ static int stub_send_ret_submit(struct stub_device *sdev)
txsize += sizeof(pdu_header);
/* 2. setup transfer buffer */
- if (usb_pipein(urb->pipe) &&
+ if (usb_pipein(urb->pipe) && priv->sgl) {
+ /* If the server split a single SG request into several
+ * URBs because the server's HCD doesn't support SG,
+ * reassemble the split URB buffers into a single
+ * return command.
+ */
+ for (i = 0; i < priv->num_urbs; i++) {
+ iov[iovnum].iov_base =
+ priv->urbs[i]->transfer_buffer;
+ iov[iovnum].iov_len =
+ priv->urbs[i]->actual_length;
+ iovnum++;
+ }
+ txsize += actual_length;
+ } else if (usb_pipein(urb->pipe) &&
usb_pipetype(urb->pipe) != PIPE_ISOCHRONOUS &&
urb->actual_length > 0) {
- iov[iovnum].iov_base = urb->transfer_buffer;
- iov[iovnum].iov_len = urb->actual_length;
- iovnum++;
+ if (urb->num_sgs) {
+ unsigned int copy = urb->actual_length;
+ int size;
+
+ for_each_sg(urb->sg, sg, urb->num_sgs, i) {
+ if (copy == 0)
+ break;
+
+ if (copy < sg->length)
+ size = copy;
+ else
+ size = sg->length;
+
+ iov[iovnum].iov_base = sg_virt(sg);
+ iov[iovnum].iov_len = size;
+
+ iovnum++;
+ copy -= size;
+ }
+ } else {
+ iov[iovnum].iov_base = urb->transfer_buffer;
+ iov[iovnum].iov_len = urb->actual_length;
+ iovnum++;
+ }
txsize += urb->actual_length;
} else if (usb_pipein(urb->pipe) &&
usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
diff --git a/drivers/usb/usbip/usbip_common.c b/drivers/usb/usbip/usbip_common.c
index 45da3e01c7b0..6532d68e8808 100644
--- a/drivers/usb/usbip/usbip_common.c
+++ b/drivers/usb/usbip/usbip_common.c
@@ -680,8 +680,12 @@ EXPORT_SYMBOL_GPL(usbip_pad_iso);
/* some members of urb must be substituted before. */
int usbip_recv_xbuff(struct usbip_device *ud, struct urb *urb)
{
- int ret;
+ struct scatterlist *sg;
+ int ret = 0;
+ int recv;
int size;
+ int copy;
+ int i;
if (ud->side == USBIP_STUB || ud->side == USBIP_VUDC) {
/* the direction of urb must be OUT. */
@@ -701,29 +705,48 @@ int usbip_recv_xbuff(struct usbip_device *ud, struct urb *urb)
if (!(size > 0))
return 0;
- if (size > urb->transfer_buffer_length) {
+ if (size > urb->transfer_buffer_length)
/* should not happen, probably malicious packet */
- if (ud->side == USBIP_STUB) {
- usbip_event_add(ud, SDEV_EVENT_ERROR_TCP);
- return 0;
- } else {
- usbip_event_add(ud, VDEV_EVENT_ERROR_TCP);
- return -EPIPE;
- }
- }
+ goto error;
- ret = usbip_recv(ud->tcp_socket, urb->transfer_buffer, size);
- if (ret != size) {
- dev_err(&urb->dev->dev, "recv xbuf, %d\n", ret);
- if (ud->side == USBIP_STUB || ud->side == USBIP_VUDC) {
- usbip_event_add(ud, SDEV_EVENT_ERROR_TCP);
- } else {
- usbip_event_add(ud, VDEV_EVENT_ERROR_TCP);
- return -EPIPE;
+ if (urb->num_sgs) {
+ copy = size;
+ for_each_sg(urb->sg, sg, urb->num_sgs, i) {
+ int recv_size;
+
+ if (copy < sg->length)
+ recv_size = copy;
+ else
+ recv_size = sg->length;
+
+ recv = usbip_recv(ud->tcp_socket, sg_virt(sg),
+ recv_size);
+
+ if (recv != recv_size)
+ goto error;
+
+ copy -= recv;
+ ret += recv;
}
+
+ if (ret != size)
+ goto error;
+ } else {
+ ret = usbip_recv(ud->tcp_socket, urb->transfer_buffer, size);
+ if (ret != size)
+ goto error;
}
return ret;
+
+error:
+ dev_err(&urb->dev->dev, "recv xbuf, %d\n", ret);
+ if (ud->side == USBIP_STUB || ud->side == USBIP_VUDC)
+ usbip_event_add(ud, SDEV_EVENT_ERROR_TCP);
+ else
+ usbip_event_add(ud, VDEV_EVENT_ERROR_TCP);
+
+ return -EPIPE;
}
EXPORT_SYMBOL_GPL(usbip_recv_xbuff);
diff --git a/drivers/usb/usbip/vhci_hcd.c b/drivers/usb/usbip/vhci_hcd.c
index 000ab7225717..585a84d319bd 100644
--- a/drivers/usb/usbip/vhci_hcd.c
+++ b/drivers/usb/usbip/vhci_hcd.c
@@ -697,7 +697,8 @@ static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag
}
vdev = &vhci_hcd->vdev[portnum-1];
- if (!urb->transfer_buffer && urb->transfer_buffer_length) {
+ if (!urb->transfer_buffer && !urb->num_sgs &&
+ urb->transfer_buffer_length) {
dev_dbg(dev, "Null URB transfer buffer\n");
return -EINVAL;
}
@@ -1143,6 +1144,15 @@ static int vhci_setup(struct usb_hcd *hcd)
hcd->speed = HCD_USB3;
hcd->self.root_hub->speed = USB_SPEED_SUPER;
}
+
+ /*
+ * Support SG.
+ * sg_tablesize is an arbitrary value to alleviate memory pressure
+ * on the host.
+ */
+ hcd->self.sg_tablesize = 32;
+ hcd->self.no_sg_constraint = 1;
+
return 0;
}
diff --git a/drivers/usb/usbip/vhci_rx.c b/drivers/usb/usbip/vhci_rx.c
index 44cd64518925..33f8972ba842 100644
--- a/drivers/usb/usbip/vhci_rx.c
+++ b/drivers/usb/usbip/vhci_rx.c
@@ -90,6 +90,9 @@ static void vhci_recv_ret_submit(struct vhci_device *vdev,
if (usbip_dbg_flag_vhci_rx)
usbip_dump_urb(urb);
+ if (urb->num_sgs)
+ urb->transfer_flags &= ~URB_DMA_MAP_SG;
+
usbip_dbg_vhci_rx("now giveback urb %u\n", pdu->base.seqnum);
spin_lock_irqsave(&vhci->lock, flags);
diff --git a/drivers/usb/usbip/vhci_tx.c b/drivers/usb/usbip/vhci_tx.c
index 2fa26d0578d7..c3803785f6ef 100644
--- a/drivers/usb/usbip/vhci_tx.c
+++ b/drivers/usb/usbip/vhci_tx.c
@@ -5,6 +5,7 @@
#include <linux/kthread.h>
#include <linux/slab.h>
+#include <linux/scatterlist.h>
#include "usbip_common.h"
#include "vhci.h"
@@ -50,19 +51,23 @@ static struct vhci_priv *dequeue_from_priv_tx(struct vhci_device *vdev)
static int vhci_send_cmd_submit(struct vhci_device *vdev)
{
+ struct usbip_iso_packet_descriptor *iso_buffer = NULL;
struct vhci_priv *priv = NULL;
+ struct scatterlist *sg;
struct msghdr msg;
- struct kvec iov[3];
+ struct kvec *iov;
size_t txsize;
size_t total_size = 0;
+ int iovnum;
+ int err = -ENOMEM;
+ int i;
while ((priv = dequeue_from_priv_tx(vdev)) != NULL) {
int ret;
struct urb *urb = priv->urb;
struct usbip_header pdu_header;
- struct usbip_iso_packet_descriptor *iso_buffer = NULL;
txsize = 0;
memset(&pdu_header, 0, sizeof(pdu_header));
@@ -72,18 +77,45 @@ static int vhci_send_cmd_submit(struct vhci_device *vdev)
usbip_dbg_vhci_tx("setup txdata urb seqnum %lu\n",
priv->seqnum);
+ if (urb->num_sgs && usb_pipeout(urb->pipe))
+ iovnum = 2 + urb->num_sgs;
+ else
+ iovnum = 3;
+
+ iov = kcalloc(iovnum, sizeof(*iov), GFP_KERNEL);
+ if (!iov) {
+ usbip_event_add(&vdev->ud, SDEV_EVENT_ERROR_MALLOC);
+ return -ENOMEM;
+ }
+
+ if (urb->num_sgs)
+ urb->transfer_flags |= URB_DMA_MAP_SG;
+
/* 1. setup usbip_header */
setup_cmd_submit_pdu(&pdu_header, urb);
usbip_header_correct_endian(&pdu_header, 1);
+ iovnum = 0;
- iov[0].iov_base = &pdu_header;
- iov[0].iov_len = sizeof(pdu_header);
+ iov[iovnum].iov_base = &pdu_header;
+ iov[iovnum].iov_len = sizeof(pdu_header);
txsize += sizeof(pdu_header);
+ iovnum++;
/* 2. setup transfer buffer */
if (!usb_pipein(urb->pipe) && urb->transfer_buffer_length > 0) {
- iov[1].iov_base = urb->transfer_buffer;
- iov[1].iov_len = urb->transfer_buffer_length;
+ if (urb->num_sgs &&
+ !usb_endpoint_xfer_isoc(&urb->ep->desc)) {
+ for_each_sg(urb->sg, sg, urb->num_sgs, i) {
+ iov[iovnum].iov_base = sg_virt(sg);
+ iov[iovnum].iov_len = sg->length;
+ iovnum++;
+ }
+ } else {
+ iov[iovnum].iov_base = urb->transfer_buffer;
+ iov[iovnum].iov_len =
+ urb->transfer_buffer_length;
+ iovnum++;
+ }
txsize += urb->transfer_buffer_length;
}
@@ -95,23 +127,26 @@ static int vhci_send_cmd_submit(struct vhci_device *vdev)
if (!iso_buffer) {
usbip_event_add(&vdev->ud,
SDEV_EVENT_ERROR_MALLOC);
- return -1;
+ goto err_iso_buffer;
}
- iov[2].iov_base = iso_buffer;
- iov[2].iov_len = len;
+ iov[iovnum].iov_base = iso_buffer;
+ iov[iovnum].iov_len = len;
+ iovnum++;
txsize += len;
}
- ret = kernel_sendmsg(vdev->ud.tcp_socket, &msg, iov, 3, txsize);
+ ret = kernel_sendmsg(vdev->ud.tcp_socket, &msg, iov, iovnum,
+ txsize);
if (ret != txsize) {
pr_err("sendmsg failed!, ret=%d for %zd\n", ret,
txsize);
- kfree(iso_buffer);
usbip_event_add(&vdev->ud, VDEV_EVENT_ERROR_TCP);
- return -1;
+ err = -EPIPE;
+ goto err_tx;
}
+ kfree(iov);
kfree(iso_buffer);
usbip_dbg_vhci_tx("send txdata\n");
@@ -119,6 +154,13 @@ static int vhci_send_cmd_submit(struct vhci_device *vdev)
}
return total_size;
+
+err_tx:
+ kfree(iso_buffer);
+err_iso_buffer:
+ kfree(iov);
+
+ return err;
}
static struct vhci_unlink *dequeue_from_unlink_tx(struct vhci_device *vdev)
diff --git a/drivers/usb/usbip/vudc.h b/drivers/usb/usbip/vudc.h
index cf968192e59f..1bd4bc005829 100644
--- a/drivers/usb/usbip/vudc.h
+++ b/drivers/usb/usbip/vudc.h
@@ -115,7 +115,7 @@ struct vudc_device {
struct list_head dev_entry;
};
-extern const struct attribute_group vudc_attr_group;
+extern const struct attribute_group *vudc_groups[];
/* visible everywhere */
diff --git a/drivers/usb/usbip/vudc_dev.c b/drivers/usb/usbip/vudc_dev.c
index a72c17ff1c6a..c8eeabdd9b56 100644
--- a/drivers/usb/usbip/vudc_dev.c
+++ b/drivers/usb/usbip/vudc_dev.c
@@ -616,18 +616,10 @@ int vudc_probe(struct platform_device *pdev)
if (ret < 0)
goto err_add_udc;
- ret = sysfs_create_group(&pdev->dev.kobj, &vudc_attr_group);
- if (ret) {
- dev_err(&udc->pdev->dev, "create sysfs files\n");
- goto err_sysfs;
- }
-
platform_set_drvdata(pdev, udc);
return ret;
-err_sysfs:
- usb_del_gadget_udc(&udc->gadget);
err_add_udc:
cleanup_vudc_hw(udc);
err_init_vudc_hw:
@@ -640,7 +632,6 @@ int vudc_remove(struct platform_device *pdev)
{
struct vudc *udc = platform_get_drvdata(pdev);
- sysfs_remove_group(&pdev->dev.kobj, &vudc_attr_group);
usb_del_gadget_udc(&udc->gadget);
cleanup_vudc_hw(udc);
kfree(udc);
diff --git a/drivers/usb/usbip/vudc_main.c b/drivers/usb/usbip/vudc_main.c
index 390733e6937e..678faa82598c 100644
--- a/drivers/usb/usbip/vudc_main.c
+++ b/drivers/usb/usbip/vudc_main.c
@@ -22,6 +22,7 @@ static struct platform_driver vudc_driver = {
.remove = vudc_remove,
.driver = {
.name = GADGET_NAME,
+ .dev_groups = vudc_groups,
},
};
diff --git a/drivers/usb/usbip/vudc_sysfs.c b/drivers/usb/usbip/vudc_sysfs.c
index 6dcd3ff655c3..100f680c572a 100644
--- a/drivers/usb/usbip/vudc_sysfs.c
+++ b/drivers/usb/usbip/vudc_sysfs.c
@@ -215,7 +215,12 @@ static struct bin_attribute *dev_bin_attrs[] = {
NULL,
};
-const struct attribute_group vudc_attr_group = {
+static const struct attribute_group vudc_attr_group = {
.attrs = dev_attrs,
.bin_attrs = dev_bin_attrs,
};
+
+const struct attribute_group *vudc_groups[] = {
+ &vudc_attr_group,
+ NULL,
+};
diff --git a/include/linux/device.h b/include/linux/device.h
index ec598ede9455..297239a08bb7 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -1014,10 +1014,14 @@ struct device_connection {
struct list_head list;
};
+typedef void *(*devcon_match_fn_t)(struct device_connection *con, int ep,
+ void *data);
+
+void *fwnode_connection_find_match(struct fwnode_handle *fwnode,
+ const char *con_id, void *data,
+ devcon_match_fn_t match);
void *device_connection_find_match(struct device *dev, const char *con_id,
- void *data,
- void *(*match)(struct device_connection *con,
- int ep, void *data));
+ void *data, devcon_match_fn_t match);
struct device *device_connection_find(struct device *dev, const char *con_id);
diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h
index f7d1eea32c78..14702e2d6fa8 100644
--- a/include/linux/dma-mapping.h
+++ b/include/linux/dma-mapping.h
@@ -149,11 +149,6 @@ static inline int valid_dma_direction(int dma_direction)
(dma_direction == DMA_FROM_DEVICE));
}
-static inline int is_device_dma_capable(struct device *dev)
-{
- return dev->dma_mask != NULL && *dev->dma_mask != DMA_MASK_NONE;
-}
-
#ifdef CONFIG_DMA_DECLARE_COHERENT
/*
* These three functions are only for dma allocator.
diff --git a/include/linux/oxu210hp.h b/include/linux/oxu210hp.h
deleted file mode 100644
index 94cd25165c08..000000000000
--- a/include/linux/oxu210hp.h
+++ /dev/null
@@ -1,8 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/* platform data for the OXU210HP HCD */
-
-struct oxu210hp_platform_data {
- unsigned int bus16:1;
- unsigned int use_hcd_otg:1;
- unsigned int use_hcd_sph:1;
-};
diff --git a/include/linux/platform_data/dwc3-omap.h b/include/linux/platform_data/dwc3-omap.h
deleted file mode 100644
index 1d36ca874cc8..000000000000
--- a/include/linux/platform_data/dwc3-omap.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/**
- * dwc3-omap.h - OMAP Specific Glue layer, header.
- *
- * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
- * All rights reserved.
- *
- * Author: Felipe Balbi <balbi@ti.com>
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. The names of the above-listed copyright holders may not be used
- * to endorse or promote products derived from this software without
- * specific prior written permission.
- *
- * ALTERNATIVELY, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2, as published by the Free
- * Software Foundation.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-enum dwc3_omap_utmi_mode {
- DWC3_OMAP_UTMI_MODE_UNKNOWN = 0,
- DWC3_OMAP_UTMI_MODE_HW,
- DWC3_OMAP_UTMI_MODE_SW,
-};
diff --git a/include/linux/platform_device.h b/include/linux/platform_device.h
index 35bc4355a9df..1b5cec067533 100644
--- a/include/linux/platform_device.h
+++ b/include/linux/platform_device.h
@@ -24,6 +24,7 @@ struct platform_device {
int id;
bool id_auto;
struct device dev;
+ u64 dma_mask;
u32 num_resources;
struct resource *resource;
@@ -48,7 +49,6 @@ extern void platform_device_unregister(struct platform_device *);
extern struct bus_type platform_bus_type;
extern struct device platform_bus;
-extern void arch_setup_pdev_archdata(struct platform_device *);
extern struct resource *platform_get_resource(struct platform_device *,
unsigned int, unsigned int);
extern struct device *
diff --git a/include/linux/usb.h b/include/linux/usb.h
index e87826e23d59..e656e7b4b1e4 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -426,7 +426,6 @@ struct usb_bus {
struct device *sysdev; /* as seen from firmware or bus */
int busnum; /* Bus number (in order of reg) */
const char *bus_name; /* stable id (PCI slot_name etc) */
- u8 uses_dma; /* Does the host controller use DMA? */
u8 uses_pio_for_control; /*
* Does the host controller use PIO
* for control transfers?
@@ -1151,6 +1150,8 @@ struct usbdrv_wrap {
* @id_table: USB drivers use ID table to support hotplugging.
* Export this with MODULE_DEVICE_TABLE(usb,...). This must be set
* or your driver's probe function will never get called.
+ * @dev_groups: Attributes attached to the device that will be created once it
+ * is bound to the driver.
* @dynids: used internally to hold the list of dynamically added device
* ids for this driver.
* @drvwrap: Driver-model core structure wrapper.
@@ -1198,6 +1199,7 @@ struct usb_driver {
int (*post_reset)(struct usb_interface *intf);
const struct usb_device_id *id_table;
+ const struct attribute_group **dev_groups;
struct usb_dynids dynids;
struct usbdrv_wrap drvwrap;
@@ -1221,6 +1223,8 @@ struct usb_driver {
* module is being unloaded.
* @suspend: Called when the device is going to be suspended by the system.
* @resume: Called when the device is being resumed by the system.
+ * @dev_groups: Attributes attached to the device that will be created once it
+ * is bound to the driver.
* @drvwrap: Driver-model core structure wrapper.
* @supports_autosuspend: if set to 0, the USB core will not allow autosuspend
* for devices bound to this driver.
@@ -1235,6 +1239,7 @@ struct usb_device_driver {
int (*suspend) (struct usb_device *udev, pm_message_t message);
int (*resume) (struct usb_device *udev, pm_message_t message);
+ const struct attribute_group **dev_groups;
struct usbdrv_wrap drvwrap;
unsigned int supports_autosuspend:1;
};
diff --git a/include/linux/usb/ch9.h b/include/linux/usb/ch9.h
index da82606be605..58b83066bea4 100644
--- a/include/linux/usb/ch9.h
+++ b/include/linux/usb/ch9.h
@@ -70,4 +70,31 @@ extern enum usb_device_speed usb_get_maximum_speed(struct device *dev);
*/
extern const char *usb_state_string(enum usb_device_state state);
+#ifdef CONFIG_TRACING
+/**
+ * usb_decode_ctrl - Returns human readable representation of control request.
+ * @str: buffer to return a human-readable representation of control request.
+ * This buffer should have about 200 bytes.
+ * @size: size of str buffer.
+ * @bRequestType: matches the USB bmRequestType field
+ * @bRequest: matches the USB bRequest field
+ * @wValue: matches the USB wValue field (CPU byte order)
+ * @wIndex: matches the USB wIndex field (CPU byte order)
+ * @wLength: matches the USB wLength field (CPU byte order)
+ *
+ * Function returns decoded, formatted and human-readable description of
+ * control request packet.
+ *
+ * The usage scenario for this is for tracepoints, so function as a return
+ * use the same value as in parameters. This approach allows to use this
+ * function in TP_printk
+ *
+ * Important: wValue, wIndex, wLength parameters before invoking this function
+ * should be processed by le16_to_cpu macro.
+ */
+extern const char *usb_decode_ctrl(char *str, size_t size, __u8 bRequestType,
+ __u8 bRequest, __u16 wValue, __u16 wIndex,
+ __u16 wLength);
+#endif
+
#endif /* __LINUX_USB_CH9_H */
diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
index fb19141151d8..124462d65eac 100644
--- a/include/linux/usb/gadget.h
+++ b/include/linux/usb/gadget.h
@@ -291,6 +291,9 @@ struct usb_dcd_config_params {
#define USB_DEFAULT_U1_DEV_EXIT_LAT 0x01 /* Less then 1 microsec */
__le16 bU2DevExitLat; /* U2 Device exit Latency */
#define USB_DEFAULT_U2_DEV_EXIT_LAT 0x1F4 /* Less then 500 microsec */
+ __u8 besl_baseline; /* Recommended baseline BESL (0-15) */
+ __u8 besl_deep; /* Recommended deep BESL (0-15) */
+#define USB_DEFAULT_BESL_UNSPECIFIED 0xFF /* No recommended value */
};
diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h
index a20e7815d814..712b2a603645 100644
--- a/include/linux/usb/hcd.h
+++ b/include/linux/usb/hcd.h
@@ -256,6 +256,7 @@ struct hc_driver {
int flags;
#define HCD_MEMORY 0x0001 /* HC regs use memory (else I/O) */
+#define HCD_DMA 0x0002 /* HC uses DMA */
#define HCD_SHARED 0x0004 /* Two (or more) usb_hcds share HW */
#define HCD_USB11 0x0010 /* USB 1.1 */
#define HCD_USB2 0x0020 /* USB 2.0 */
@@ -422,8 +423,10 @@ static inline bool hcd_periodic_completion_in_progress(struct usb_hcd *hcd,
return hcd->high_prio_bh.completing_ep == ep;
}
-#define hcd_uses_dma(hcd) \
- (IS_ENABLED(CONFIG_HAS_DMA) && (hcd)->self.uses_dma)
+static inline bool hcd_uses_dma(struct usb_hcd *hcd)
+{
+ return IS_ENABLED(CONFIG_HAS_DMA) && (hcd->driver->flags & HCD_DMA);
+}
extern int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb);
extern int usb_hcd_check_unlink_urb(struct usb_hcd *hcd, struct urb *urb,
@@ -594,6 +597,10 @@ extern void usb_ep0_reinit(struct usb_device *);
#define GetPortStatus HUB_CLASS_REQ(USB_DIR_IN, USB_RT_PORT, USB_REQ_GET_STATUS)
#define SetHubFeature HUB_CLASS_REQ(USB_DIR_OUT, USB_RT_HUB, USB_REQ_SET_FEATURE)
#define SetPortFeature HUB_CLASS_REQ(USB_DIR_OUT, USB_RT_PORT, USB_REQ_SET_FEATURE)
+#define ClearTTBuffer HUB_CLASS_REQ(USB_DIR_OUT, USB_RT_PORT, HUB_CLEAR_TT_BUFFER)
+#define ResetTT HUB_CLASS_REQ(USB_DIR_OUT, USB_RT_PORT, HUB_RESET_TT)
+#define GetTTState HUB_CLASS_REQ(USB_DIR_IN, USB_RT_PORT, HUB_GET_TT_STATE)
+#define StopTT HUB_CLASS_REQ(USB_DIR_OUT, USB_RT_PORT, HUB_STOP_TT)
/*-------------------------------------------------------------------------*/
diff --git a/include/linux/usb/role.h b/include/linux/usb/role.h
index c05ffa6abda9..2d77f97df72d 100644
--- a/include/linux/usb/role.h
+++ b/include/linux/usb/role.h
@@ -42,14 +42,51 @@ struct usb_role_switch_desc {
bool allow_userspace_control;
};
+
+#if IS_ENABLED(CONFIG_USB_ROLE_SWITCH)
int usb_role_switch_set_role(struct usb_role_switch *sw, enum usb_role role);
enum usb_role usb_role_switch_get_role(struct usb_role_switch *sw);
struct usb_role_switch *usb_role_switch_get(struct device *dev);
+struct usb_role_switch *fwnode_usb_role_switch_get(struct fwnode_handle *node);
void usb_role_switch_put(struct usb_role_switch *sw);
struct usb_role_switch *
usb_role_switch_register(struct device *parent,
const struct usb_role_switch_desc *desc);
void usb_role_switch_unregister(struct usb_role_switch *sw);
+#else
+static inline int usb_role_switch_set_role(struct usb_role_switch *sw,
+ enum usb_role role)
+{
+ return 0;
+}
+
+static inline enum usb_role usb_role_switch_get_role(struct usb_role_switch *sw)
+{
+ return USB_ROLE_NONE;
+}
+
+static inline struct usb_role_switch *usb_role_switch_get(struct device *dev)
+{
+ return ERR_PTR(-ENODEV);
+}
+
+static inline struct usb_role_switch *
+fwnode_usb_role_switch_get(struct fwnode_handle *node)
+{
+ return ERR_PTR(-ENODEV);
+}
+
+static inline void usb_role_switch_put(struct usb_role_switch *sw) { }
+
+static inline struct usb_role_switch *
+usb_role_switch_register(struct device *parent,
+ const struct usb_role_switch_desc *desc)
+{
+ return ERR_PTR(-ENODEV);
+}
+
+static inline void usb_role_switch_unregister(struct usb_role_switch *sw) { }
+#endif
#endif /* __LINUX_USB_ROLE_H */
diff --git a/include/linux/usb/usb338x.h b/include/linux/usb/usb338x.h
index 7189e3387bf9..20020c1336d5 100644
--- a/include/linux/usb/usb338x.h
+++ b/include/linux/usb/usb338x.h
@@ -113,7 +113,10 @@ struct usb338x_ll_regs {
u32 ll_ltssm_ctrl1;
u32 ll_ltssm_ctrl2;
u32 ll_ltssm_ctrl3;
- u32 unused[2];
+ u32 unused1;
+
+ /* 0x710 */
+ u32 unused2;
u32 ll_general_ctrl0;
u32 ll_general_ctrl1;
#define PM_U3_AUTO_EXIT 29
@@ -136,29 +139,41 @@ struct usb338x_ll_regs {
u32 ll_general_ctrl2;
#define SELECT_INVERT_LANE_POLARITY 7
#define FORCE_INVERT_LANE_POLARITY 6
+
+ /* 0x720 */
u32 ll_general_ctrl3;
u32 ll_general_ctrl4;
u32 ll_error_gen;
-} __packed;
+ u32 unused3;
+
+ /* 0x730 */
+ u32 unused4[4];
-struct usb338x_ll_lfps_regs {
- /* offset 0x748 */
+ /* 0x740 */
+ u32 unused5[2];
u32 ll_lfps_5;
#define TIMER_LFPS_6US 16
u32 ll_lfps_6;
#define TIMER_LFPS_80US 0
-} __packed;
-struct usb338x_ll_tsn_regs {
- /* offset 0x77C */
+ /* 0x750 */
+ u32 unused6[8];
+
+ /* 0x770 */
+ u32 unused7[3];
u32 ll_tsn_counters_2;
#define HOT_TX_NORESET_TS2 24
+
+ /* 0x780 */
u32 ll_tsn_counters_3;
#define HOT_RX_RESET_TS2 0
-} __packed;
+ u32 unused8[3];
-struct usb338x_ll_chi_regs {
- /* offset 0x79C */
+ /* 0x790 */
+ u32 unused9;
+ u32 ll_lfps_timers_2;
+#define LFPS_TIMERS_2_WORKAROUND_VALUE 0x084d
+ u32 unused10;
u32 ll_tsn_chicken_bit;
#define RECOVERY_IDLE_TO_RECOVER_FMW 3
} __packed;
diff --git a/include/uapi/linux/usb/ch9.h b/include/uapi/linux/usb/ch9.h
index d5a5caec8fbc..2b623f36af6b 100644
--- a/include/uapi/linux/usb/ch9.h
+++ b/include/uapi/linux/usb/ch9.h
@@ -894,6 +894,8 @@ struct usb_ext_cap_descriptor { /* Link Power Management */
#define USB_BESL_SUPPORT (1 << 2) /* supports BESL */
#define USB_BESL_BASELINE_VALID (1 << 3) /* Baseline BESL valid*/
#define USB_BESL_DEEP_VALID (1 << 4) /* Deep BESL valid */
+#define USB_SET_BESL_BASELINE(p) (((p) & 0xf) << 8)
+#define USB_SET_BESL_DEEP(p) (((p) & 0xf) << 12)
#define USB_GET_BESL_BASELINE(p) (((p) & (0xf << 8)) >> 8)
#define USB_GET_BESL_DEEP(p) (((p) & (0xf << 12)) >> 12)
} __attribute__((packed));
diff --git a/include/uapi/linux/usbdevice_fs.h b/include/uapi/linux/usbdevice_fs.h
index 78efe870c2b7..cf525cddeb94 100644
--- a/include/uapi/linux/usbdevice_fs.h
+++ b/include/uapi/linux/usbdevice_fs.h
@@ -158,6 +158,7 @@ struct usbdevfs_hub_portinfo {
#define USBDEVFS_CAP_MMAP 0x20
#define USBDEVFS_CAP_DROP_PRIVILEGES 0x40
#define USBDEVFS_CAP_CONNINFO_EX 0x80
+#define USBDEVFS_CAP_SUSPEND 0x100
/* USBDEVFS_DISCONNECT_CLAIM flags & struct */
@@ -223,5 +224,8 @@ struct usbdevfs_streams {
* extending size of the data returned.
*/
#define USBDEVFS_CONNINFO_EX(len) _IOC(_IOC_READ, 'U', 32, len)
+#define USBDEVFS_FORBID_SUSPEND _IO('U', 33)
+#define USBDEVFS_ALLOW_SUSPEND _IO('U', 34)
+#define USBDEVFS_WAIT_FOR_RESUME _IO('U', 35)
#endif /* _UAPI_LINUX_USBDEVICE_FS_H */
diff --git a/tools/usb/usbip/libsrc/usbip_common.c b/tools/usb/usbip/libsrc/usbip_common.c
index bb424638d75b..b8d7d480595a 100644
--- a/tools/usb/usbip/libsrc/usbip_common.c
+++ b/tools/usb/usbip/libsrc/usbip_common.c
@@ -226,8 +226,10 @@ int read_usb_device(struct udev_device *sdev, struct usbip_usb_device *udev)
path = udev_device_get_syspath(sdev);
name = udev_device_get_sysname(sdev);
- strncpy(udev->path, path, SYSFS_PATH_MAX);
- strncpy(udev->busid, name, SYSFS_BUS_ID_SIZE);
+ strncpy(udev->path, path, SYSFS_PATH_MAX - 1);
+ udev->path[SYSFS_PATH_MAX - 1] = '\0';
+ strncpy(udev->busid, name, SYSFS_BUS_ID_SIZE - 1);
+ udev->busid[SYSFS_BUS_ID_SIZE - 1] = '\0';
sscanf(name, "%u-%u", &busnum, &devnum);
udev->busnum = busnum;
diff --git a/tools/usb/usbip/libsrc/usbip_device_driver.c b/tools/usb/usbip/libsrc/usbip_device_driver.c
index 5a3726eb44ab..051d7d3f443b 100644
--- a/tools/usb/usbip/libsrc/usbip_device_driver.c
+++ b/tools/usb/usbip/libsrc/usbip_device_driver.c
@@ -91,7 +91,8 @@ int read_usb_vudc_device(struct udev_device *sdev, struct usbip_usb_device *dev)
copy_descr_attr16(dev, &descr, idProduct);
copy_descr_attr16(dev, &descr, bcdDevice);
- strncpy(dev->path, path, SYSFS_PATH_MAX);
+ strncpy(dev->path, path, SYSFS_PATH_MAX - 1);
+ dev->path[SYSFS_PATH_MAX - 1] = '\0';
dev->speed = USB_SPEED_UNKNOWN;
speed = udev_device_get_sysattr_value(sdev, "current_speed");
@@ -110,7 +111,8 @@ int read_usb_vudc_device(struct udev_device *sdev, struct usbip_usb_device *dev)
dev->busnum = 0;
name = udev_device_get_sysname(plat);
- strncpy(dev->busid, name, SYSFS_BUS_ID_SIZE);
+ strncpy(dev->busid, name, SYSFS_BUS_ID_SIZE - 1);
+ dev->busid[SYSFS_BUS_ID_SIZE - 1] = '\0';
return 0;
err:
fclose(fd);