aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/ABI/stable/sysfs-bus-usb14
-rw-r--r--Documentation/ABI/testing/sysfs-bus-usb-lvstest47
-rw-r--r--Documentation/DocBook/gadget.tmpl10
-rw-r--r--Documentation/devicetree/bindings/phy/berlin-sata-phy.txt34
-rw-r--r--Documentation/devicetree/bindings/phy/hix5hd2-phy.txt22
-rw-r--r--Documentation/devicetree/bindings/phy/phy-bindings.txt4
-rw-r--r--Documentation/devicetree/bindings/phy/phy-miphy365x.txt76
-rw-r--r--Documentation/devicetree/bindings/phy/qcom-apq8064-sata-phy.txt24
-rw-r--r--Documentation/devicetree/bindings/phy/qcom-ipq806x-sata-phy.txt23
-rw-r--r--Documentation/devicetree/bindings/phy/samsung-phy.txt2
-rw-r--r--Documentation/devicetree/bindings/phy/ti-phy.txt23
-rw-r--r--Documentation/devicetree/bindings/usb/ci-hdrc-imx.txt2
-rw-r--r--Documentation/devicetree/bindings/usb/nvidia,tegra20-usb-phy.txt8
-rw-r--r--Documentation/devicetree/bindings/usb/usb-xhci.txt3
-rw-r--r--Documentation/phy.txt10
-rw-r--r--Documentation/usb/hotplug.txt8
-rw-r--r--Documentation/usb/power-management.txt245
-rw-r--r--arch/arm/boot/dts/tegra114.dtsi5
-rw-r--r--arch/arm/boot/dts/tegra124.dtsi7
-rw-r--r--arch/arm/boot/dts/tegra20.dtsi7
-rw-r--r--arch/arm/boot/dts/tegra30.dtsi7
-rw-r--r--drivers/phy/Kconfig78
-rw-r--r--drivers/phy/Makefile5
-rw-r--r--drivers/phy/phy-bcm-kona-usb2.c2
-rw-r--r--drivers/phy/phy-berlin-sata.c284
-rw-r--r--drivers/phy/phy-core.c56
-rw-r--r--drivers/phy/phy-exynos-dp-video.c7
-rw-r--r--drivers/phy/phy-exynos-mipi-video.c7
-rw-r--r--drivers/phy/phy-exynos4x12-usb2.c125
-rw-r--r--drivers/phy/phy-exynos5-usbdrd.c9
-rw-r--r--drivers/phy/phy-exynos5250-sata.c2
-rw-r--r--drivers/phy/phy-exynos5250-usb2.c2
-rw-r--r--drivers/phy/phy-hix5hd2-sata.c192
-rw-r--r--drivers/phy/phy-miphy365x.c636
-rw-r--r--drivers/phy/phy-mvebu-sata.c2
-rw-r--r--drivers/phy/phy-omap-control.c52
-rw-r--r--drivers/phy/phy-omap-usb2.c2
-rw-r--r--drivers/phy/phy-qcom-apq8064-sata.c289
-rw-r--r--drivers/phy/phy-qcom-ipq806x-sata.c211
-rw-r--r--drivers/phy/phy-samsung-usb2.c9
-rw-r--r--drivers/phy/phy-samsung-usb2.h5
-rw-r--r--drivers/phy/phy-sun4i-usb.c7
-rw-r--r--drivers/phy/phy-ti-pipe3.c107
-rw-r--r--drivers/phy/phy-twl4030-usb.c2
-rw-r--r--drivers/phy/phy-xgene.c2
-rw-r--r--drivers/usb/chipidea/ci_hdrc_imx.c3
-rw-r--r--drivers/usb/chipidea/debug.c4
-rw-r--r--drivers/usb/class/usbtmc.c2
-rw-r--r--drivers/usb/core/config.c11
-rw-r--r--drivers/usb/core/devio.c2
-rw-r--r--drivers/usb/core/driver.c7
-rw-r--r--drivers/usb/core/hcd-pci.c2
-rw-r--r--drivers/usb/core/hcd.c4
-rw-r--r--drivers/usb/core/hub.c94
-rw-r--r--drivers/usb/core/hub.h2
-rw-r--r--drivers/usb/core/port.c21
-rw-r--r--drivers/usb/core/quirks.c7
-rw-r--r--drivers/usb/core/urb.c1
-rw-r--r--drivers/usb/core/usb.c1
-rw-r--r--drivers/usb/dwc2/gadget.c12
-rw-r--r--drivers/usb/dwc3/Kconfig7
-rw-r--r--drivers/usb/dwc3/core.c51
-rw-r--r--drivers/usb/dwc3/core.h13
-rw-r--r--drivers/usb/dwc3/dwc3-omap.c172
-rw-r--r--drivers/usb/dwc3/gadget.c7
-rw-r--r--drivers/usb/dwc3/host.c14
-rw-r--r--drivers/usb/gadget/Kconfig824
-rw-r--r--drivers/usb/gadget/Makefile101
-rw-r--r--drivers/usb/gadget/composite.c1
-rw-r--r--drivers/usb/gadget/configfs.c4
-rw-r--r--drivers/usb/gadget/function/Makefile34
-rw-r--r--drivers/usb/gadget/function/f_acm.c (renamed from drivers/usb/gadget/f_acm.c)0
-rw-r--r--drivers/usb/gadget/function/f_ecm.c (renamed from drivers/usb/gadget/f_ecm.c)0
-rw-r--r--drivers/usb/gadget/function/f_eem.c (renamed from drivers/usb/gadget/f_eem.c)22
-rw-r--r--drivers/usb/gadget/function/f_fs.c (renamed from drivers/usb/gadget/f_fs.c)352
-rw-r--r--drivers/usb/gadget/function/f_hid.c (renamed from drivers/usb/gadget/f_hid.c)0
-rw-r--r--drivers/usb/gadget/function/f_loopback.c (renamed from drivers/usb/gadget/f_loopback.c)0
-rw-r--r--drivers/usb/gadget/function/f_mass_storage.c (renamed from drivers/usb/gadget/f_mass_storage.c)0
-rw-r--r--drivers/usb/gadget/function/f_mass_storage.h (renamed from drivers/usb/gadget/f_mass_storage.h)0
-rw-r--r--drivers/usb/gadget/function/f_midi.c (renamed from drivers/usb/gadget/f_midi.c)0
-rw-r--r--drivers/usb/gadget/function/f_ncm.c (renamed from drivers/usb/gadget/f_ncm.c)480
-rw-r--r--drivers/usb/gadget/function/f_obex.c (renamed from drivers/usb/gadget/f_obex.c)0
-rw-r--r--drivers/usb/gadget/function/f_phonet.c (renamed from drivers/usb/gadget/f_phonet.c)0
-rw-r--r--drivers/usb/gadget/function/f_rndis.c (renamed from drivers/usb/gadget/f_rndis.c)4
-rw-r--r--drivers/usb/gadget/function/f_serial.c (renamed from drivers/usb/gadget/f_serial.c)0
-rw-r--r--drivers/usb/gadget/function/f_sourcesink.c (renamed from drivers/usb/gadget/f_sourcesink.c)0
-rw-r--r--drivers/usb/gadget/function/f_subset.c (renamed from drivers/usb/gadget/f_subset.c)0
-rw-r--r--drivers/usb/gadget/function/f_uac1.c (renamed from drivers/usb/gadget/f_uac1.c)0
-rw-r--r--drivers/usb/gadget/function/f_uac2.c (renamed from drivers/usb/gadget/f_uac2.c)24
-rw-r--r--drivers/usb/gadget/function/f_uvc.c (renamed from drivers/usb/gadget/f_uvc.c)0
-rw-r--r--drivers/usb/gadget/function/f_uvc.h (renamed from drivers/usb/gadget/f_uvc.h)0
-rw-r--r--drivers/usb/gadget/function/g_zero.h (renamed from drivers/usb/gadget/g_zero.h)0
-rw-r--r--drivers/usb/gadget/function/ndis.h (renamed from drivers/usb/gadget/ndis.h)0
-rw-r--r--drivers/usb/gadget/function/rndis.c (renamed from drivers/usb/gadget/rndis.c)0
-rw-r--r--drivers/usb/gadget/function/rndis.h (renamed from drivers/usb/gadget/rndis.h)0
-rw-r--r--drivers/usb/gadget/function/storage_common.c (renamed from drivers/usb/gadget/storage_common.c)0
-rw-r--r--drivers/usb/gadget/function/storage_common.h (renamed from drivers/usb/gadget/storage_common.h)0
-rw-r--r--drivers/usb/gadget/function/u_ecm.h (renamed from drivers/usb/gadget/u_ecm.h)0
-rw-r--r--drivers/usb/gadget/function/u_eem.h (renamed from drivers/usb/gadget/u_eem.h)0
-rw-r--r--drivers/usb/gadget/function/u_ether.c (renamed from drivers/usb/gadget/u_ether.c)19
-rw-r--r--drivers/usb/gadget/function/u_ether.h (renamed from drivers/usb/gadget/u_ether.h)2
-rw-r--r--drivers/usb/gadget/function/u_ether_configfs.h (renamed from drivers/usb/gadget/u_ether_configfs.h)0
-rw-r--r--drivers/usb/gadget/function/u_fs.h (renamed from drivers/usb/gadget/u_fs.h)7
-rw-r--r--drivers/usb/gadget/function/u_gether.h (renamed from drivers/usb/gadget/u_gether.h)0
-rw-r--r--drivers/usb/gadget/function/u_ncm.h (renamed from drivers/usb/gadget/u_ncm.h)0
-rw-r--r--drivers/usb/gadget/function/u_phonet.h (renamed from drivers/usb/gadget/u_phonet.h)0
-rw-r--r--drivers/usb/gadget/function/u_rndis.h (renamed from drivers/usb/gadget/u_rndis.h)0
-rw-r--r--drivers/usb/gadget/function/u_serial.c (renamed from drivers/usb/gadget/u_serial.c)0
-rw-r--r--drivers/usb/gadget/function/u_serial.h (renamed from drivers/usb/gadget/u_serial.h)0
-rw-r--r--drivers/usb/gadget/function/u_uac1.c (renamed from drivers/usb/gadget/u_uac1.c)0
-rw-r--r--drivers/usb/gadget/function/u_uac1.h (renamed from drivers/usb/gadget/u_uac1.h)0
-rw-r--r--drivers/usb/gadget/function/uvc.h (renamed from drivers/usb/gadget/uvc.h)0
-rw-r--r--drivers/usb/gadget/function/uvc_queue.c (renamed from drivers/usb/gadget/uvc_queue.c)0
-rw-r--r--drivers/usb/gadget/function/uvc_queue.h (renamed from drivers/usb/gadget/uvc_queue.h)0
-rw-r--r--drivers/usb/gadget/function/uvc_v4l2.c (renamed from drivers/usb/gadget/uvc_v4l2.c)0
-rw-r--r--drivers/usb/gadget/function/uvc_video.c (renamed from drivers/usb/gadget/uvc_video.c)0
-rw-r--r--drivers/usb/gadget/legacy/Kconfig475
-rw-r--r--drivers/usb/gadget/legacy/Makefile44
-rw-r--r--drivers/usb/gadget/legacy/acm_ms.c (renamed from drivers/usb/gadget/acm_ms.c)14
-rw-r--r--drivers/usb/gadget/legacy/audio.c (renamed from drivers/usb/gadget/audio.c)12
-rw-r--r--drivers/usb/gadget/legacy/cdc2.c (renamed from drivers/usb/gadget/cdc2.c)14
-rw-r--r--drivers/usb/gadget/legacy/dbgp.c (renamed from drivers/usb/gadget/dbgp.c)0
-rw-r--r--drivers/usb/gadget/legacy/ether.c (renamed from drivers/usb/gadget/ether.c)14
-rw-r--r--drivers/usb/gadget/legacy/g_ffs.c (renamed from drivers/usb/gadget/g_ffs.c)2
-rw-r--r--drivers/usb/gadget/legacy/gmidi.c (renamed from drivers/usb/gadget/gmidi.c)13
-rw-r--r--drivers/usb/gadget/legacy/hid.c (renamed from drivers/usb/gadget/hid.c)0
-rw-r--r--drivers/usb/gadget/legacy/inode.c (renamed from drivers/usb/gadget/inode.c)0
-rw-r--r--drivers/usb/gadget/legacy/mass_storage.c (renamed from drivers/usb/gadget/mass_storage.c)0
-rw-r--r--drivers/usb/gadget/legacy/multi.c (renamed from drivers/usb/gadget/multi.c)13
-rw-r--r--drivers/usb/gadget/legacy/ncm.c (renamed from drivers/usb/gadget/ncm.c)14
-rw-r--r--drivers/usb/gadget/legacy/nokia.c (renamed from drivers/usb/gadget/nokia.c)12
-rw-r--r--drivers/usb/gadget/legacy/printer.c (renamed from drivers/usb/gadget/printer.c)0
-rw-r--r--drivers/usb/gadget/legacy/serial.c (renamed from drivers/usb/gadget/serial.c)0
-rw-r--r--drivers/usb/gadget/legacy/tcm_usb_gadget.c (renamed from drivers/usb/gadget/tcm_usb_gadget.c)0
-rw-r--r--drivers/usb/gadget/legacy/tcm_usb_gadget.h (renamed from drivers/usb/gadget/tcm_usb_gadget.h)0
-rw-r--r--drivers/usb/gadget/legacy/webcam.c (renamed from drivers/usb/gadget/webcam.c)15
-rw-r--r--drivers/usb/gadget/legacy/zero.c (renamed from drivers/usb/gadget/zero.c)14
-rw-r--r--drivers/usb/gadget/net2280.c2905
-rw-r--r--drivers/usb/gadget/net2280.h308
-rw-r--r--drivers/usb/gadget/u_os_desc.h59
-rw-r--r--drivers/usb/gadget/udc/Kconfig385
-rw-r--r--drivers/usb/gadget/udc/Makefile31
-rw-r--r--drivers/usb/gadget/udc/amd5536udc.c (renamed from drivers/usb/gadget/amd5536udc.c)0
-rw-r--r--drivers/usb/gadget/udc/amd5536udc.h (renamed from drivers/usb/gadget/amd5536udc.h)0
-rw-r--r--drivers/usb/gadget/udc/at91_udc.c (renamed from drivers/usb/gadget/at91_udc.c)0
-rw-r--r--drivers/usb/gadget/udc/at91_udc.h (renamed from drivers/usb/gadget/at91_udc.h)0
-rw-r--r--drivers/usb/gadget/udc/atmel_usba_udc.c (renamed from drivers/usb/gadget/atmel_usba_udc.c)2
-rw-r--r--drivers/usb/gadget/udc/atmel_usba_udc.h (renamed from drivers/usb/gadget/atmel_usba_udc.h)0
-rw-r--r--drivers/usb/gadget/udc/bcm63xx_udc.c (renamed from drivers/usb/gadget/bcm63xx_udc.c)0
-rw-r--r--drivers/usb/gadget/udc/dummy_hcd.c (renamed from drivers/usb/gadget/dummy_hcd.c)0
-rw-r--r--drivers/usb/gadget/udc/fotg210-udc.c (renamed from drivers/usb/gadget/fotg210-udc.c)0
-rw-r--r--drivers/usb/gadget/udc/fotg210.h (renamed from drivers/usb/gadget/fotg210.h)0
-rw-r--r--drivers/usb/gadget/udc/fsl_mxc_udc.c (renamed from drivers/usb/gadget/fsl_mxc_udc.c)2
-rw-r--r--drivers/usb/gadget/udc/fsl_qe_udc.c (renamed from drivers/usb/gadget/fsl_qe_udc.c)19
-rw-r--r--drivers/usb/gadget/udc/fsl_qe_udc.h (renamed from drivers/usb/gadget/fsl_qe_udc.h)0
-rw-r--r--drivers/usb/gadget/udc/fsl_udc_core.c (renamed from drivers/usb/gadget/fsl_udc_core.c)19
-rw-r--r--drivers/usb/gadget/udc/fsl_usb2_udc.h (renamed from drivers/usb/gadget/fsl_usb2_udc.h)3
-rw-r--r--drivers/usb/gadget/udc/fusb300_udc.c (renamed from drivers/usb/gadget/fusb300_udc.c)4
-rw-r--r--drivers/usb/gadget/udc/fusb300_udc.h (renamed from drivers/usb/gadget/fusb300_udc.h)0
-rw-r--r--drivers/usb/gadget/udc/gadget_chips.h (renamed from drivers/usb/gadget/gadget_chips.h)0
-rw-r--r--drivers/usb/gadget/udc/goku_udc.c (renamed from drivers/usb/gadget/goku_udc.c)0
-rw-r--r--drivers/usb/gadget/udc/goku_udc.h (renamed from drivers/usb/gadget/goku_udc.h)0
-rw-r--r--drivers/usb/gadget/udc/gr_udc.c (renamed from drivers/usb/gadget/gr_udc.c)2
-rw-r--r--drivers/usb/gadget/udc/gr_udc.h (renamed from drivers/usb/gadget/gr_udc.h)0
-rw-r--r--drivers/usb/gadget/udc/lpc32xx_udc.c (renamed from drivers/usb/gadget/lpc32xx_udc.c)7
-rw-r--r--drivers/usb/gadget/udc/m66592-udc.c (renamed from drivers/usb/gadget/m66592-udc.c)4
-rw-r--r--drivers/usb/gadget/udc/m66592-udc.h (renamed from drivers/usb/gadget/m66592-udc.h)0
-rw-r--r--drivers/usb/gadget/udc/mv_u3d.h (renamed from drivers/usb/gadget/mv_u3d.h)0
-rw-r--r--drivers/usb/gadget/udc/mv_u3d_core.c (renamed from drivers/usb/gadget/mv_u3d_core.c)0
-rw-r--r--drivers/usb/gadget/udc/mv_udc.h (renamed from drivers/usb/gadget/mv_udc.h)0
-rw-r--r--drivers/usb/gadget/udc/mv_udc_core.c (renamed from drivers/usb/gadget/mv_udc_core.c)4
-rw-r--r--drivers/usb/gadget/udc/net2272.c (renamed from drivers/usb/gadget/net2272.c)2
-rw-r--r--drivers/usb/gadget/udc/net2272.h (renamed from drivers/usb/gadget/net2272.h)0
-rw-r--r--drivers/usb/gadget/udc/net2280.c3827
-rw-r--r--drivers/usb/gadget/udc/net2280.h403
-rw-r--r--drivers/usb/gadget/udc/omap_udc.c (renamed from drivers/usb/gadget/omap_udc.c)5
-rw-r--r--drivers/usb/gadget/udc/omap_udc.h (renamed from drivers/usb/gadget/omap_udc.h)0
-rw-r--r--drivers/usb/gadget/udc/pch_udc.c (renamed from drivers/usb/gadget/pch_udc.c)0
-rw-r--r--drivers/usb/gadget/udc/pxa25x_udc.c (renamed from drivers/usb/gadget/pxa25x_udc.c)75
-rw-r--r--drivers/usb/gadget/udc/pxa25x_udc.h (renamed from drivers/usb/gadget/pxa25x_udc.h)0
-rw-r--r--drivers/usb/gadget/udc/pxa27x_udc.c (renamed from drivers/usb/gadget/pxa27x_udc.c)6
-rw-r--r--drivers/usb/gadget/udc/pxa27x_udc.h (renamed from drivers/usb/gadget/pxa27x_udc.h)0
-rw-r--r--drivers/usb/gadget/udc/r8a66597-udc.c (renamed from drivers/usb/gadget/r8a66597-udc.c)92
-rw-r--r--drivers/usb/gadget/udc/r8a66597-udc.h (renamed from drivers/usb/gadget/r8a66597-udc.h)0
-rw-r--r--drivers/usb/gadget/udc/s3c-hsudc.c (renamed from drivers/usb/gadget/s3c-hsudc.c)0
-rw-r--r--drivers/usb/gadget/udc/s3c2410_udc.c (renamed from drivers/usb/gadget/s3c2410_udc.c)8
-rw-r--r--drivers/usb/gadget/udc/s3c2410_udc.h (renamed from drivers/usb/gadget/s3c2410_udc.h)0
-rw-r--r--drivers/usb/gadget/udc/udc-core.c (renamed from drivers/usb/gadget/udc-core.c)0
-rw-r--r--drivers/usb/host/Kconfig8
-rw-r--r--drivers/usb/host/Makefile3
-rw-r--r--drivers/usb/host/ehci-exynos.c2
-rw-r--r--drivers/usb/host/ehci-mem.c2
-rw-r--r--drivers/usb/host/ehci-msm.c2
-rw-r--r--drivers/usb/host/ehci-pci.c25
-rw-r--r--drivers/usb/host/ehci-spear.c2
-rw-r--r--drivers/usb/host/ehci-tegra.c67
-rw-r--r--drivers/usb/host/fhci-dbg.c8
-rw-r--r--drivers/usb/host/fotg210-hcd.c48
-rw-r--r--drivers/usb/host/max3421-hcd.c48
-rw-r--r--drivers/usb/host/ohci-dbg.c9
-rw-r--r--drivers/usb/host/ohci-exynos.c2
-rw-r--r--drivers/usb/host/ohci-hcd.c381
-rw-r--r--drivers/usb/host/ohci-hub.c11
-rw-r--r--drivers/usb/host/ohci-mem.c1
-rw-r--r--drivers/usb/host/ohci-q.c262
-rw-r--r--drivers/usb/host/ohci-spear.c2
-rw-r--r--drivers/usb/host/ohci.h23
-rw-r--r--drivers/usb/host/oxu210hp-hcd.c48
-rw-r--r--drivers/usb/host/uhci-grlib.c31
-rw-r--r--drivers/usb/host/uhci-hcd.c2
-rw-r--r--drivers/usb/host/uhci-platform.c22
-rw-r--r--drivers/usb/host/xhci-pci.c8
-rw-r--r--drivers/usb/host/xhci-plat.c52
-rw-r--r--drivers/usb/host/xhci-rcar.c148
-rw-r--r--drivers/usb/host/xhci-rcar.h27
-rw-r--r--drivers/usb/host/xhci-ring.c4
-rw-r--r--drivers/usb/host/xhci.c160
-rw-r--r--drivers/usb/host/xhci.h2
-rw-r--r--drivers/usb/misc/Kconfig7
-rw-r--r--drivers/usb/misc/Makefile1
-rw-r--r--drivers/usb/misc/lvstest.c460
-rw-r--r--drivers/usb/misc/usb3503.c37
-rw-r--r--drivers/usb/musb/blackfin.c20
-rw-r--r--drivers/usb/musb/davinci.c20
-rw-r--r--drivers/usb/musb/jz4740.c3
-rw-r--r--drivers/usb/musb/musb_core.c41
-rw-r--r--drivers/usb/musb/musb_core.h12
-rw-r--r--drivers/usb/musb/musb_cppi41.c70
-rw-r--r--drivers/usb/musb/musb_dma.h1
-rw-r--r--drivers/usb/musb/musb_dsps.c104
-rw-r--r--drivers/usb/musb/musb_host.c19
-rw-r--r--drivers/usb/musb/musb_regs.h7
-rw-r--r--drivers/usb/musb/tusb6010.c16
-rw-r--r--drivers/usb/musb/ux500.c28
-rw-r--r--drivers/usb/phy/phy-am335x.c12
-rw-r--r--drivers/usb/phy/phy-gpio-vbus-usb.c45
-rw-r--r--drivers/usb/phy/phy-msm-usb.c12
-rw-r--r--drivers/usb/phy/phy-tegra-usb.c11
-rw-r--r--drivers/usb/renesas_usbhs/Makefile2
-rw-r--r--drivers/usb/renesas_usbhs/common.c66
-rw-r--r--drivers/usb/renesas_usbhs/common.h2
-rw-r--r--drivers/usb/renesas_usbhs/mod_gadget.c2
-rw-r--r--drivers/usb/renesas_usbhs/pipe.c11
-rw-r--r--drivers/usb/renesas_usbhs/pipe.h1
-rw-r--r--drivers/usb/renesas_usbhs/rcar2.c77
-rw-r--r--drivers/usb/renesas_usbhs/rcar2.h4
-rw-r--r--drivers/usb/serial/cp210x.c3
-rw-r--r--drivers/usb/serial/ftdi_sio.c84
-rw-r--r--drivers/usb/serial/ftdi_sio_ids.h13
-rw-r--r--drivers/usb/serial/kl5kusb105.c30
-rw-r--r--drivers/usb/serial/mos7840.c5
-rw-r--r--drivers/usb/storage/uas-detect.h40
-rw-r--r--drivers/usb/storage/uas.c2
-rw-r--r--drivers/uwb/whci.c2
-rw-r--r--include/dt-bindings/phy/phy-miphy365x.h14
-rw-r--r--include/linux/phy/omap_control_phy.h10
-rw-r--r--include/linux/phy/phy.h17
-rw-r--r--include/linux/usb/composite.h15
-rw-r--r--include/linux/usb/quirks.h11
-rw-r--r--include/linux/usb/renesas_usbhs.h6
-rw-r--r--include/linux/usb/usb338x.h199
-rw-r--r--include/linux/usb/xhci_pdriver.h27
-rw-r--r--include/uapi/linux/usb/functionfs.h82
-rw-r--r--tools/usb/ffs-aio-example/multibuff/device_app/aio_multibuff.c39
-rw-r--r--tools/usb/ffs-aio-example/multibuff/host_app/test.c27
-rw-r--r--tools/usb/ffs-aio-example/simple/device_app/aio_simple.c39
-rw-r--r--tools/usb/ffs-aio-example/simple/host_app/test.c27
267 files changed, 11203 insertions, 5803 deletions
diff --git a/Documentation/ABI/stable/sysfs-bus-usb b/Documentation/ABI/stable/sysfs-bus-usb
index a6b685724740..e2bc700a6f9c 100644
--- a/Documentation/ABI/stable/sysfs-bus-usb
+++ b/Documentation/ABI/stable/sysfs-bus-usb
@@ -3,13 +3,13 @@ Date: May 2007
KernelVersion: 2.6.23
Contact: Alan Stern <stern@rowland.harvard.edu>
Description:
- If CONFIG_USB_PERSIST is set, then each USB device directory
- will contain a file named power/persist. The file holds a
- boolean value (0 or 1) indicating whether or not the
- "USB-Persist" facility is enabled for the device. Since the
- facility is inherently dangerous, it is disabled by default
- for all devices except hubs. For more information, see
- Documentation/usb/persist.txt.
+ USB device directories can contain a file named power/persist.
+ The file holds a boolean value (0 or 1) indicating whether or
+ not the "USB-Persist" facility is enabled for the device. For
+ hubs this facility is always enabled and their device
+ directories will not contain this file.
+
+ For more information, see Documentation/usb/persist.txt.
What: /sys/bus/usb/devices/.../power/autosuspend
Date: March 2007
diff --git a/Documentation/ABI/testing/sysfs-bus-usb-lvstest b/Documentation/ABI/testing/sysfs-bus-usb-lvstest
new file mode 100644
index 000000000000..aae68fc2d842
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-usb-lvstest
@@ -0,0 +1,47 @@
+Link Layer Validation Device is a standard device for testing of Super
+Speed Link Layer tests. These nodes are available in sysfs only when lvs
+driver is bound with root hub device.
+
+What: /sys/bus/usb/devices/.../get_dev_desc
+Date: March 2014
+Contact: Pratyush Anand <pratyush.anand@st.com>
+Description:
+ Write to this node to issue "Get Device Descriptor"
+ for Link Layer Validation device. It is needed for TD.7.06.
+
+What: /sys/bus/usb/devices/.../u1_timeout
+Date: March 2014
+Contact: Pratyush Anand <pratyush.anand@st.com>
+Description:
+ Set "U1 timeout" for the downstream port where Link Layer
+ Validation device is connected. Timeout value must be between 0
+ and 127. It is needed for TD.7.18, TD.7.19, TD.7.20 and TD.7.21.
+
+What: /sys/bus/usb/devices/.../u2_timeout
+Date: March 2014
+Contact: Pratyush Anand <pratyush.anand@st.com>
+Description:
+ Set "U2 timeout" for the downstream port where Link Layer
+ Validation device is connected. Timeout value must be between 0
+ and 127. It is needed for TD.7.18, TD.7.19, TD.7.20 and TD.7.21.
+
+What: /sys/bus/usb/devices/.../hot_reset
+Date: March 2014
+Contact: Pratyush Anand <pratyush.anand@st.com>
+Description:
+ Write to this node to issue "Reset" for Link Layer Validation
+ device. It is needed for TD.7.29, TD.7.31, TD.7.34 and TD.7.35.
+
+What: /sys/bus/usb/devices/.../u3_entry
+Date: March 2014
+Contact: Pratyush Anand <pratyush.anand@st.com>
+Description:
+ Write to this node to issue "U3 entry" for Link Layer
+ Validation device. It is needed for TD.7.35 and TD.7.36.
+
+What: /sys/bus/usb/devices/.../u3_exit
+Date: March 2014
+Contact: Pratyush Anand <pratyush.anand@st.com>
+Description:
+ Write to this node to issue "U3 exit" for Link Layer
+ Validation device. It is needed for TD.7.36.
diff --git a/Documentation/DocBook/gadget.tmpl b/Documentation/DocBook/gadget.tmpl
index 2c425d70f7e2..641629221176 100644
--- a/Documentation/DocBook/gadget.tmpl
+++ b/Documentation/DocBook/gadget.tmpl
@@ -556,11 +556,11 @@ been converted to this framework.
Near-term plans include converting all of them, except for "gadgetfs".
</para>
-!Edrivers/usb/gadget/f_acm.c
-!Edrivers/usb/gadget/f_ecm.c
-!Edrivers/usb/gadget/f_subset.c
-!Edrivers/usb/gadget/f_obex.c
-!Edrivers/usb/gadget/f_serial.c
+!Edrivers/usb/gadget/function/f_acm.c
+!Edrivers/usb/gadget/function/f_ecm.c
+!Edrivers/usb/gadget/function/f_subset.c
+!Edrivers/usb/gadget/function/f_obex.c
+!Edrivers/usb/gadget/function/f_serial.c
</sect1>
diff --git a/Documentation/devicetree/bindings/phy/berlin-sata-phy.txt b/Documentation/devicetree/bindings/phy/berlin-sata-phy.txt
new file mode 100644
index 000000000000..88f8c23384c0
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/berlin-sata-phy.txt
@@ -0,0 +1,34 @@
+Berlin SATA PHY
+---------------
+
+Required properties:
+- compatible: should be "marvell,berlin2q-sata-phy"
+- address-cells: should be 1
+- size-cells: should be 0
+- phy-cells: from the generic PHY bindings, must be 1
+- reg: address and length of the register
+- clocks: reference to the clock entry
+
+Sub-nodes:
+Each PHY should be represented as a sub-node.
+
+Sub-nodes required properties:
+- reg: the PHY number
+
+Example:
+ sata_phy: phy@f7e900a0 {
+ compatible = "marvell,berlin2q-sata-phy";
+ reg = <0xf7e900a0 0x200>;
+ clocks = <&chip CLKID_SATA>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ #phy-cells = <1>;
+
+ sata-phy@0 {
+ reg = <0>;
+ };
+
+ sata-phy@1 {
+ reg = <1>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/phy/hix5hd2-phy.txt b/Documentation/devicetree/bindings/phy/hix5hd2-phy.txt
new file mode 100644
index 000000000000..296168b74d24
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/hix5hd2-phy.txt
@@ -0,0 +1,22 @@
+Hisilicon hix5hd2 SATA PHY
+-----------------------
+
+Required properties:
+- compatible: should be "hisilicon,hix5hd2-sata-phy"
+- reg: offset and length of the PHY registers
+- #phy-cells: must be 0
+Refer to phy/phy-bindings.txt for the generic PHY binding properties
+
+Optional Properties:
+- hisilicon,peripheral-syscon: phandle of syscon used to control peripheral.
+- hisilicon,power-reg: offset and bit number within peripheral-syscon,
+ register of controlling sata power supply.
+
+Example:
+ sata_phy: phy@f9900000 {
+ compatible = "hisilicon,hix5hd2-sata-phy";
+ reg = <0xf9900000 0x10000>;
+ #phy-cells = <0>;
+ hisilicon,peripheral-syscon = <&peripheral_ctrl>;
+ hisilicon,power-reg = <0x8 10>;
+ };
diff --git a/Documentation/devicetree/bindings/phy/phy-bindings.txt b/Documentation/devicetree/bindings/phy/phy-bindings.txt
index 8ae844fc0c60..2aa1840200ed 100644
--- a/Documentation/devicetree/bindings/phy/phy-bindings.txt
+++ b/Documentation/devicetree/bindings/phy/phy-bindings.txt
@@ -10,6 +10,10 @@ Required Properties:
provider can use the values in cells to find the appropriate
PHY.
+Optional Properties:
+phy-supply: Phandle to a regulator that provides power to the PHY. This
+ regulator will be managed during the PHY power on/off sequence.
+
For example:
phys: phy {
diff --git a/Documentation/devicetree/bindings/phy/phy-miphy365x.txt b/Documentation/devicetree/bindings/phy/phy-miphy365x.txt
new file mode 100644
index 000000000000..42c880886cf7
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/phy-miphy365x.txt
@@ -0,0 +1,76 @@
+STMicroelectronics STi MIPHY365x PHY binding
+============================================
+
+This binding describes a miphy device that is used to control PHY hardware
+for SATA and PCIe.
+
+Required properties (controller (parent) node):
+- compatible : Should be "st,miphy365x-phy"
+- st,syscfg : Should be a phandle of the system configuration register group
+ which contain the SATA, PCIe mode setting bits
+
+Required nodes : A sub-node is required for each channel the controller
+ provides. Address range information including the usual
+ 'reg' and 'reg-names' properties are used inside these
+ nodes to describe the controller's topology. These nodes
+ are translated by the driver's .xlate() function.
+
+Required properties (port (child) node):
+- #phy-cells : Should be 1 (See second example)
+ Cell after port phandle is device type from:
+ - MIPHY_TYPE_SATA
+ - MIPHY_TYPE_PCI
+- reg : Address and length of register sets for each device in
+ "reg-names"
+- reg-names : The names of the register addresses corresponding to the
+ registers filled in "reg":
+ - sata: For SATA devices
+ - pcie: For PCIe devices
+ - syscfg: To specify the syscfg based config register
+
+Optional properties (port (child) node):
+- st,sata-gen : Generation of locally attached SATA IP. Expected values
+ are {1,2,3). If not supplied generation 1 hardware will
+ be expected
+- st,pcie-tx-pol-inv : Bool property to invert the polarity PCIe Tx (Txn/Txp)
+- st,sata-tx-pol-inv : Bool property to invert the polarity SATA Tx (Txn/Txp)
+
+Example:
+
+ miphy365x_phy: miphy365x@fe382000 {
+ compatible = "st,miphy365x-phy";
+ st,syscfg = <&syscfg_rear>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ phy_port0: port@fe382000 {
+ reg = <0xfe382000 0x100>, <0xfe394000 0x100>, <0x824 0x4>;
+ reg-names = "sata", "pcie", "syscfg";
+ #phy-cells = <1>;
+ st,sata-gen = <3>;
+ };
+
+ phy_port1: port@fe38a000 {
+ reg = <0xfe38a000 0x100>, <0xfe804000 0x100>, <0x828 0x4>;;
+ reg-names = "sata", "pcie", "syscfg";
+ #phy-cells = <1>;
+ st,pcie-tx-pol-inv;
+ };
+ };
+
+Specifying phy control of devices
+=================================
+
+Device nodes should specify the configuration required in their "phys"
+property, containing a phandle to the phy port node and a device type.
+
+Example:
+
+#include <dt-bindings/phy/phy-miphy365x.h>
+
+ sata0: sata@fe380000 {
+ ...
+ phys = <&phy_port0 MIPHY_TYPE_SATA>;
+ ...
+ };
diff --git a/Documentation/devicetree/bindings/phy/qcom-apq8064-sata-phy.txt b/Documentation/devicetree/bindings/phy/qcom-apq8064-sata-phy.txt
new file mode 100644
index 000000000000..952f6c96bab9
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/qcom-apq8064-sata-phy.txt
@@ -0,0 +1,24 @@
+Qualcomm APQ8064 SATA PHY Controller
+------------------------------------
+
+SATA PHY nodes are defined to describe on-chip SATA Physical layer controllers.
+Each SATA PHY controller should have its own node.
+
+Required properties:
+- compatible: compatible list, contains "qcom,apq8064-sata-phy".
+- reg: offset and length of the SATA PHY register set;
+- #phy-cells: must be zero
+- clocks: a list of phandles and clock-specifier pairs, one for each entry in
+ clock-names.
+- clock-names: must be "cfg" for phy config clock.
+
+Example:
+ sata_phy: sata-phy@1b400000 {
+ compatible = "qcom,apq8064-sata-phy";
+ reg = <0x1b400000 0x200>;
+
+ clocks = <&gcc SATA_PHY_CFG_CLK>;
+ clock-names = "cfg";
+
+ #phy-cells = <0>;
+ };
diff --git a/Documentation/devicetree/bindings/phy/qcom-ipq806x-sata-phy.txt b/Documentation/devicetree/bindings/phy/qcom-ipq806x-sata-phy.txt
new file mode 100644
index 000000000000..76bfbd056202
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/qcom-ipq806x-sata-phy.txt
@@ -0,0 +1,23 @@
+Qualcomm IPQ806x SATA PHY Controller
+------------------------------------
+
+SATA PHY nodes are defined to describe on-chip SATA Physical layer controllers.
+Each SATA PHY controller should have its own node.
+
+Required properties:
+- compatible: compatible list, contains "qcom,ipq806x-sata-phy"
+- reg: offset and length of the SATA PHY register set;
+- #phy-cells: must be zero
+- clocks: must be exactly one entry
+- clock-names: must be "cfg"
+
+Example:
+ sata_phy: sata-phy@1b400000 {
+ compatible = "qcom,ipq806x-sata-phy";
+ reg = <0x1b400000 0x200>;
+
+ clocks = <&gcc SATA_PHY_CFG_CLK>;
+ clock-names = "cfg";
+
+ #phy-cells = <0>;
+ };
diff --git a/Documentation/devicetree/bindings/phy/samsung-phy.txt b/Documentation/devicetree/bindings/phy/samsung-phy.txt
index 2049261d8c31..6099a5c94283 100644
--- a/Documentation/devicetree/bindings/phy/samsung-phy.txt
+++ b/Documentation/devicetree/bindings/phy/samsung-phy.txt
@@ -26,6 +26,7 @@ Samsung S5P/EXYNOS SoC series USB PHY
Required properties:
- compatible : should be one of the listed compatibles:
+ - "samsung,exynos3250-usb2-phy"
- "samsung,exynos4210-usb2-phy"
- "samsung,exynos4x12-usb2-phy"
- "samsung,exynos5250-usb2-phy"
@@ -46,6 +47,7 @@ and Exynos 4212) it is as follows:
1 - USB host ("host"),
2 - HSIC0 ("hsic0"),
3 - HSIC1 ("hsic1"),
+Exynos3250 has only USB device phy available as phy 0.
Exynos 4210 and Exynos 4212 use mode switching and require that mode switch
register is supplied.
diff --git a/Documentation/devicetree/bindings/phy/ti-phy.txt b/Documentation/devicetree/bindings/phy/ti-phy.txt
index 9ce458f32945..305e3df3d9b1 100644
--- a/Documentation/devicetree/bindings/phy/ti-phy.txt
+++ b/Documentation/devicetree/bindings/phy/ti-phy.txt
@@ -9,15 +9,17 @@ Required properties:
e.g. USB2_PHY on OMAP5.
"ti,control-phy-pipe3" - if it has DPLL and individual Rx & Tx power control
e.g. USB3 PHY and SATA PHY on OMAP5.
+ "ti,control-phy-pcie" - for pcie to support external clock for pcie and to
+ set PCS delay value.
+ e.g. PCIE PHY in DRA7x
"ti,control-phy-usb2-dra7" - if it has power down register like USB2 PHY on
DRA7 platform.
"ti,control-phy-usb2-am437" - if it has power down register like USB2 PHY on
AM437 platform.
- - reg : Address and length of the register set for the device. It contains
- the address of "otghs_control" for control-phy-otghs or "power" register
- for other types.
- - reg-names: should be "otghs_control" control-phy-otghs and "power" for
- other types.
+ - reg : register ranges as listed in the reg-names property
+ - reg-names: "otghs_control" for control-phy-otghs
+ "power", "pcie_pcs" and "control_sma" for control-phy-pcie
+ "power" for all other types
omap_control_usb: omap-control-usb@4a002300 {
compatible = "ti,control-phy-otghs";
@@ -56,8 +58,8 @@ usb2phy@4a0ad080 {
TI PIPE3 PHY
Required properties:
- - compatible: Should be "ti,phy-usb3" or "ti,phy-pipe3-sata".
- "ti,omap-usb3" is deprecated.
+ - compatible: Should be "ti,phy-usb3", "ti,phy-pipe3-sata" or
+ "ti,phy-pipe3-pcie. "ti,omap-usb3" is deprecated.
- reg : Address and length of the register set for the device.
- reg-names: The names of the register addresses corresponding to the registers
filled in "reg".
@@ -69,10 +71,17 @@ Required properties:
* "wkupclk" - wakeup clock.
* "sysclk" - system clock.
* "refclk" - reference clock.
+ * "dpll_ref" - external dpll ref clk
+ * "dpll_ref_m2" - external dpll ref clk
+ * "phy-div" - divider for apll
+ * "div-clk" - apll clock
Optional properties:
- ctrl-module : phandle of the control module used by PHY driver to power on
the PHY.
+ - id: If there are multiple instance of the same type, in order to
+ differentiate between each instance "id" can be used (e.g., multi-lane PCIe
+ PHY). If "id" is not provided, it is set to default value of '1'.
This is usually a subnode of ocp2scp to which it is connected.
diff --git a/Documentation/devicetree/bindings/usb/ci-hdrc-imx.txt b/Documentation/devicetree/bindings/usb/ci-hdrc-imx.txt
index a6a32cb7f777..1bae71e9ad47 100644
--- a/Documentation/devicetree/bindings/usb/ci-hdrc-imx.txt
+++ b/Documentation/devicetree/bindings/usb/ci-hdrc-imx.txt
@@ -4,6 +4,7 @@ Required properties:
- compatible: Should be "fsl,imx27-usb"
- reg: Should contain registers location and length
- interrupts: Should contain controller interrupt
+- fsl,usbphy: phandle of usb phy that connects to the port
Recommended properies:
- phy_type: the type of the phy connected to the core. Should be one
@@ -12,7 +13,6 @@ Recommended properies:
- dr_mode: One of "host", "peripheral" or "otg". Defaults to "otg"
Optional properties:
-- fsl,usbphy: phandler of usb phy that connects to the only one port
- fsl,usbmisc: phandler of non-core register device, with one argument
that indicate usb controller index
- vbus-supply: regulator for vbus
diff --git a/Documentation/devicetree/bindings/usb/nvidia,tegra20-usb-phy.txt b/Documentation/devicetree/bindings/usb/nvidia,tegra20-usb-phy.txt
index ba797d3e6326..c9205fbf26e2 100644
--- a/Documentation/devicetree/bindings/usb/nvidia,tegra20-usb-phy.txt
+++ b/Documentation/devicetree/bindings/usb/nvidia,tegra20-usb-phy.txt
@@ -20,6 +20,12 @@ Required properties :
Present if phy_type == utmi.
- ulpi-link: The clock Tegra provides to the ULPI PHY (cdev2).
Present if phy_type == ulpi, and ULPI link mode is in use.
+ - resets : Must contain an entry for each entry in reset-names.
+ See ../reset/reset.txt for details.
+ - reset-names : Must include the following entries:
+ - usb: The PHY's own reset signal.
+ - utmi-pads: The reset of the PHY containing the chip-wide UTMI pad control
+ registers. Required even if phy_type == ulpi.
Required properties for phy_type == ulpi:
- nvidia,phy-reset-gpio : The GPIO used to reset the PHY.
@@ -56,6 +62,8 @@ Optional properties:
host means this is a host controller
peripheral means it is device controller
otg means it can operate as either ("on the go")
+ - nvidia,has-utmi-pad-registers : boolean indicates whether this controller
+ contains the UTMI pad control registers common to all USB controllers.
VBUS control (required for dr_mode == otg, optional for dr_mode == host):
- vbus-supply: regulator for VBUS
diff --git a/Documentation/devicetree/bindings/usb/usb-xhci.txt b/Documentation/devicetree/bindings/usb/usb-xhci.txt
index 5a79377c6a96..86f67f0886bc 100644
--- a/Documentation/devicetree/bindings/usb/usb-xhci.txt
+++ b/Documentation/devicetree/bindings/usb/usb-xhci.txt
@@ -9,8 +9,9 @@ Required properties:
register set for the device.
- interrupts: one XHCI interrupt should be described here.
-Optional property:
+Optional properties:
- clocks: reference to a clock
+ - usb3-lpm-capable: determines if platform is USB3 LPM capable
Example:
usb@f0931000 {
diff --git a/Documentation/phy.txt b/Documentation/phy.txt
index ebff6ee52441..c6594af94d25 100644
--- a/Documentation/phy.txt
+++ b/Documentation/phy.txt
@@ -53,10 +53,12 @@ unregister the PHY.
The PHY driver should create the PHY in order for other peripheral controllers
to make use of it. The PHY framework provides 2 APIs to create the PHY.
-struct phy *phy_create(struct device *dev, const struct phy_ops *ops,
- struct phy_init_data *init_data);
-struct phy *devm_phy_create(struct device *dev, const struct phy_ops *ops,
- struct phy_init_data *init_data);
+struct phy *phy_create(struct device *dev, struct device_node *node,
+ const struct phy_ops *ops,
+ struct phy_init_data *init_data);
+struct phy *devm_phy_create(struct device *dev, struct device_node *node,
+ const struct phy_ops *ops,
+ struct phy_init_data *init_data);
The PHY drivers can use one of the above 2 APIs to create the PHY by passing
the device pointer, phy ops and init_data.
diff --git a/Documentation/usb/hotplug.txt b/Documentation/usb/hotplug.txt
index 6424b130485c..a80b0e9a7a0b 100644
--- a/Documentation/usb/hotplug.txt
+++ b/Documentation/usb/hotplug.txt
@@ -105,13 +105,13 @@ macros such as these, and use driver_info to store more information.
A short example, for a driver that supports several specific USB devices
and their quirks, might have a MODULE_DEVICE_TABLE like this:
- static const struct usb_device_id mydriver_id_table = {
+ static const struct usb_device_id mydriver_id_table[] = {
{ USB_DEVICE (0x9999, 0xaaaa), driver_info: QUIRK_X },
{ USB_DEVICE (0xbbbb, 0x8888), driver_info: QUIRK_Y|QUIRK_Z },
...
{ } /* end with an all-zeroes entry */
- }
- MODULE_DEVICE_TABLE (usb, mydriver_id_table);
+ };
+ MODULE_DEVICE_TABLE(usb, mydriver_id_table);
Most USB device drivers should pass these tables to the USB subsystem as
well as to the module management subsystem. Not all, though: some driver
@@ -134,7 +134,7 @@ something like this:
if exposing any operations through usbdevfs:
.ioctl = my_ioctl,
*/
- }
+ };
When the USB subsystem knows about a driver's device ID table, it's used when
choosing drivers to probe(). The thread doing new device processing checks
diff --git a/Documentation/usb/power-management.txt b/Documentation/usb/power-management.txt
index 1392b61d6ebe..7b90fe034c4b 100644
--- a/Documentation/usb/power-management.txt
+++ b/Documentation/usb/power-management.txt
@@ -2,8 +2,27 @@
Alan Stern <stern@rowland.harvard.edu>
- October 28, 2010
-
+ Last-updated: February 2014
+
+
+ Contents:
+ ---------
+ * What is Power Management?
+ * What is Remote Wakeup?
+ * When is a USB device idle?
+ * Forms of dynamic PM
+ * The user interface for dynamic PM
+ * Changing the default idle-delay time
+ * Warnings
+ * The driver interface for Power Management
+ * The driver interface for autosuspend and autoresume
+ * Other parts of the driver interface
+ * Mutual exclusion
+ * Interaction between dynamic PM and system PM
+ * xHCI hardware link PM
+ * USB Port Power Control
+ * User Interface for Port Power Control
+ * Suggested Userspace Port Power Policy
What is Power Management?
@@ -516,3 +535,225 @@ relevant attribute files is usb2_hardware_lpm.
driver will enable hardware LPM for the device. You
can write y/Y/1 or n/N/0 to the file to enable/disable
USB2 hardware LPM manually. This is for test purpose mainly.
+
+
+ USB Port Power Control
+ ----------------------
+
+In addition to suspending endpoint devices and enabling hardware
+controlled link power management, the USB subsystem also has the
+capability to disable power to ports under some conditions. Power is
+controlled through Set/ClearPortFeature(PORT_POWER) requests to a hub.
+In the case of a root or platform-internal hub the host controller
+driver translates PORT_POWER requests into platform firmware (ACPI)
+method calls to set the port power state. For more background see the
+Linux Plumbers Conference 2012 slides [1] and video [2]:
+
+Upon receiving a ClearPortFeature(PORT_POWER) request a USB port is
+logically off, and may trigger the actual loss of VBUS to the port [3].
+VBUS may be maintained in the case where a hub gangs multiple ports into
+a shared power well causing power to remain until all ports in the gang
+are turned off. VBUS may also be maintained by hub ports configured for
+a charging application. In any event a logically off port will lose
+connection with its device, not respond to hotplug events, and not
+respond to remote wakeup events*.
+
+WARNING: turning off a port may result in the inability to hot add a device.
+Please see "User Interface for Port Power Control" for details.
+
+As far as the effect on the device itself it is similar to what a device
+goes through during system suspend, i.e. the power session is lost. Any
+USB device or driver that misbehaves with system suspend will be
+similarly affected by a port power cycle event. For this reason the
+implementation shares the same device recovery path (and honors the same
+quirks) as the system resume path for the hub.
+
+[1]: http://dl.dropbox.com/u/96820575/sarah-sharp-lpt-port-power-off2-mini.pdf
+[2]: http://linuxplumbers.ubicast.tv/videos/usb-port-power-off-kerneluserspace-api/
+[3]: USB 3.1 Section 10.12
+* wakeup note: if a device is configured to send wakeup events the port
+ power control implementation will block poweroff attempts on that
+ port.
+
+
+ User Interface for Port Power Control
+ -------------------------------------
+
+The port power control mechanism uses the PM runtime system. Poweroff is
+requested by clearing the power/pm_qos_no_power_off flag of the port device
+(defaults to 1). If the port is disconnected it will immediately receive a
+ClearPortFeature(PORT_POWER) request. Otherwise, it will honor the pm runtime
+rules and require the attached child device and all descendants to be suspended.
+This mechanism is dependent on the hub advertising port power switching in its
+hub descriptor (wHubCharacteristics logical power switching mode field).
+
+Note, some interface devices/drivers do not support autosuspend. Userspace may
+need to unbind the interface drivers before the usb_device will suspend. An
+unbound interface device is suspended by default. When unbinding, be careful
+to unbind interface drivers, not the driver of the parent usb device. Also,
+leave hub interface drivers bound. If the driver for the usb device (not
+interface) is unbound the kernel is no longer able to resume the device. If a
+hub interface driver is unbound, control of its child ports is lost and all
+attached child-devices will disconnect. A good rule of thumb is that if the
+'driver/module' link for a device points to /sys/module/usbcore then unbinding
+it will interfere with port power control.
+
+Example of the relevant files for port power control. Note, in this example
+these files are relative to a usb hub device (prefix).
+
+ prefix=/sys/devices/pci0000:00/0000:00:14.0/usb3/3-1
+
+ attached child device +
+ hub port device + |
+ hub interface device + | |
+ v v v
+ $prefix/3-1:1.0/3-1-port1/device
+
+ $prefix/3-1:1.0/3-1-port1/power/pm_qos_no_power_off
+ $prefix/3-1:1.0/3-1-port1/device/power/control
+ $prefix/3-1:1.0/3-1-port1/device/3-1.1:<intf0>/driver/unbind
+ $prefix/3-1:1.0/3-1-port1/device/3-1.1:<intf1>/driver/unbind
+ ...
+ $prefix/3-1:1.0/3-1-port1/device/3-1.1:<intfN>/driver/unbind
+
+In addition to these files some ports may have a 'peer' link to a port on
+another hub. The expectation is that all superspeed ports have a
+hi-speed peer.
+
+$prefix/3-1:1.0/3-1-port1/peer -> ../../../../usb2/2-1/2-1:1.0/2-1-port1
+../../../../usb2/2-1/2-1:1.0/2-1-port1/peer -> ../../../../usb3/3-1/3-1:1.0/3-1-port1
+
+Distinct from 'companion ports', or 'ehci/xhci shared switchover ports'
+peer ports are simply the hi-speed and superspeed interface pins that
+are combined into a single usb3 connector. Peer ports share the same
+ancestor XHCI device.
+
+While a superspeed port is powered off a device may downgrade its
+connection and attempt to connect to the hi-speed pins. The
+implementation takes steps to prevent this:
+
+1/ Port suspend is sequenced to guarantee that hi-speed ports are powered-off
+ before their superspeed peer is permitted to power-off. The implication is
+ that the setting pm_qos_no_power_off to zero on a superspeed port may not cause
+ the port to power-off until its highspeed peer has gone to its runtime suspend
+ state. Userspace must take care to order the suspensions if it wants to
+ guarantee that a superspeed port will power-off.
+
+2/ Port resume is sequenced to force a superspeed port to power-on prior to its
+ highspeed peer.
+
+3/ Port resume always triggers an attached child device to resume. After a
+ power session is lost the device may have been removed, or need reset.
+ Resuming the child device when the parent port regains power resolves those
+ states and clamps the maximum port power cycle frequency at the rate the child
+ device can suspend (autosuspend-delay) and resume (reset-resume latency).
+
+Sysfs files relevant for port power control:
+ <hubdev-portX>/power/pm_qos_no_power_off:
+ This writable flag controls the state of an idle port.
+ Once all children and descendants have suspended the
+ port may suspend/poweroff provided that
+ pm_qos_no_power_off is '0'. If pm_qos_no_power_off is
+ '1' the port will remain active/powered regardless of
+ the stats of descendants. Defaults to 1.
+
+ <hubdev-portX>/power/runtime_status:
+ This file reflects whether the port is 'active' (power is on)
+ or 'suspended' (logically off). There is no indication to
+ userspace whether VBUS is still supplied.
+
+ <hubdev-portX>/connect_type:
+ An advisory read-only flag to userspace indicating the
+ location and connection type of the port. It returns
+ one of four values 'hotplug', 'hardwired', 'not used',
+ and 'unknown'. All values, besides unknown, are set by
+ platform firmware.
+
+ "hotplug" indicates an externally connectable/visible
+ port on the platform. Typically userspace would choose
+ to keep such a port powered to handle new device
+ connection events.
+
+ "hardwired" refers to a port that is not visible but
+ connectable. Examples are internal ports for USB
+ bluetooth that can be disconnected via an external
+ switch or a port with a hardwired USB camera. It is
+ expected to be safe to allow these ports to suspend
+ provided pm_qos_no_power_off is coordinated with any
+ switch that gates connections. Userspace must arrange
+ for the device to be connected prior to the port
+ powering off, or to activate the port prior to enabling
+ connection via a switch.
+
+ "not used" refers to an internal port that is expected
+ to never have a device connected to it. These may be
+ empty internal ports, or ports that are not physically
+ exposed on a platform. Considered safe to be
+ powered-off at all times.
+
+ "unknown" means platform firmware does not provide
+ information for this port. Most commonly refers to
+ external hub ports which should be considered 'hotplug'
+ for policy decisions.
+
+ NOTE1: since we are relying on the BIOS to get this ACPI
+ information correct, the USB port descriptions may be
+ missing or wrong.
+
+ NOTE2: Take care in clearing pm_qos_no_power_off. Once
+ power is off this port will
+ not respond to new connect events.
+
+ Once a child device is attached additional constraints are
+ applied before the port is allowed to poweroff.
+
+ <child>/power/control:
+ Must be 'auto', and the port will not
+ power down until <child>/power/runtime_status
+ reflects the 'suspended' state. Default
+ value is controlled by child device driver.
+
+ <child>/power/persist:
+ This defaults to '1' for most devices and indicates if
+ kernel can persist the device's configuration across a
+ power session loss (suspend / port-power event). When
+ this value is '0' (quirky devices), port poweroff is
+ disabled.
+
+ <child>/driver/unbind:
+ Wakeup capable devices will block port poweroff. At
+ this time the only mechanism to clear the usb-internal
+ wakeup-capability for an interface device is to unbind
+ its driver.
+
+Summary of poweroff pre-requisite settings relative to a port device:
+
+ echo 0 > power/pm_qos_no_power_off
+ echo 0 > peer/power/pm_qos_no_power_off # if it exists
+ echo auto > power/control # this is the default value
+ echo auto > <child>/power/control
+ echo 1 > <child>/power/persist # this is the default value
+
+ Suggested Userspace Port Power Policy
+ -------------------------------------
+
+As noted above userspace needs to be careful and deliberate about what
+ports are enabled for poweroff.
+
+The default configuration is that all ports start with
+power/pm_qos_no_power_off set to '1' causing ports to always remain
+active.
+
+Given confidence in the platform firmware's description of the ports
+(ACPI _PLD record for a port populates 'connect_type') userspace can
+clear pm_qos_no_power_off for all 'not used' ports. The same can be
+done for 'hardwired' ports provided poweroff is coordinated with any
+connection switch for the port.
+
+A more aggressive userspace policy is to enable USB port power off for
+all ports (set <hubdev-portX>/power/pm_qos_no_power_off to '0') when
+some external factor indicates the user has stopped interacting with the
+system. For example, a distro may want to enable power off all USB
+ports when the screen blanks, and re-power them when the screen becomes
+active. Smart phones and tablets may want to power off USB ports when
+the user pushes the power button.
diff --git a/arch/arm/boot/dts/tegra114.dtsi b/arch/arm/boot/dts/tegra114.dtsi
index fdc559ab2db3..7da20ca633dd 100644
--- a/arch/arm/boot/dts/tegra114.dtsi
+++ b/arch/arm/boot/dts/tegra114.dtsi
@@ -657,6 +657,8 @@
<&tegra_car TEGRA114_CLK_PLL_U>,
<&tegra_car TEGRA114_CLK_USBD>;
clock-names = "reg", "pll_u", "utmi-pads";
+ resets = <&tegra_car 22>, <&tegra_car 22>;
+ reset-names = "usb", "utmi-pads";
nvidia,hssync-start-delay = <0>;
nvidia,idle-wait-delay = <17>;
nvidia,elastic-limit = <16>;
@@ -667,6 +669,7 @@
nvidia,hssquelch-level = <2>;
nvidia,hsdiscon-level = <5>;
nvidia,xcvr-hsslew = <12>;
+ nvidia,has-utmi-pad-registers;
status = "disabled";
};
@@ -690,6 +693,8 @@
<&tegra_car TEGRA114_CLK_PLL_U>,
<&tegra_car TEGRA114_CLK_USBD>;
clock-names = "reg", "pll_u", "utmi-pads";
+ resets = <&tegra_car 59>, <&tegra_car 22>;
+ reset-names = "usb", "utmi-pads";
nvidia,hssync-start-delay = <0>;
nvidia,idle-wait-delay = <17>;
nvidia,elastic-limit = <16>;
diff --git a/arch/arm/boot/dts/tegra124.dtsi b/arch/arm/boot/dts/tegra124.dtsi
index 6e6bc4e8185c..aa8753a7c211 100644
--- a/arch/arm/boot/dts/tegra124.dtsi
+++ b/arch/arm/boot/dts/tegra124.dtsi
@@ -613,6 +613,8 @@
<&tegra_car TEGRA124_CLK_PLL_U>,
<&tegra_car TEGRA124_CLK_USBD>;
clock-names = "reg", "pll_u", "utmi-pads";
+ resets = <&tegra_car 59>, <&tegra_car 22>;
+ reset-names = "usb", "utmi-pads";
nvidia,hssync-start-delay = <0>;
nvidia,idle-wait-delay = <17>;
nvidia,elastic-limit = <16>;
@@ -647,6 +649,8 @@
<&tegra_car TEGRA124_CLK_PLL_U>,
<&tegra_car TEGRA124_CLK_USBD>;
clock-names = "reg", "pll_u", "utmi-pads";
+ resets = <&tegra_car 22>, <&tegra_car 22>;
+ reset-names = "usb", "utmi-pads";
nvidia,hssync-start-delay = <0>;
nvidia,idle-wait-delay = <17>;
nvidia,elastic-limit = <16>;
@@ -657,6 +661,7 @@
nvidia,hssquelch-level = <2>;
nvidia,hsdiscon-level = <5>;
nvidia,xcvr-hsslew = <12>;
+ nvidia,has-utmi-pad-registers;
status = "disabled";
};
@@ -681,6 +686,8 @@
<&tegra_car TEGRA124_CLK_PLL_U>,
<&tegra_car TEGRA124_CLK_USBD>;
clock-names = "reg", "pll_u", "utmi-pads";
+ resets = <&tegra_car 58>, <&tegra_car 22>;
+ reset-names = "usb", "utmi-pads";
nvidia,hssync-start-delay = <0>;
nvidia,idle-wait-delay = <17>;
nvidia,elastic-limit = <16>;
diff --git a/arch/arm/boot/dts/tegra20.dtsi b/arch/arm/boot/dts/tegra20.dtsi
index a7ddf70df50b..935df8906f25 100644
--- a/arch/arm/boot/dts/tegra20.dtsi
+++ b/arch/arm/boot/dts/tegra20.dtsi
@@ -630,6 +630,8 @@
<&tegra_car TEGRA20_CLK_CLK_M>,
<&tegra_car TEGRA20_CLK_USBD>;
clock-names = "reg", "pll_u", "timer", "utmi-pads";
+ resets = <&tegra_car 22>, <&tegra_car 22>;
+ reset-names = "usb", "utmi-pads";
nvidia,has-legacy-mode;
nvidia,hssync-start-delay = <9>;
nvidia,idle-wait-delay = <17>;
@@ -638,6 +640,7 @@
nvidia,xcvr-setup = <9>;
nvidia,xcvr-lsfslew = <1>;
nvidia,xcvr-lsrslew = <1>;
+ nvidia,has-utmi-pad-registers;
status = "disabled";
};
@@ -661,6 +664,8 @@
<&tegra_car TEGRA20_CLK_PLL_U>,
<&tegra_car TEGRA20_CLK_CDEV2>;
clock-names = "reg", "pll_u", "ulpi-link";
+ resets = <&tegra_car 58>, <&tegra_car 22>;
+ reset-names = "usb", "utmi-pads";
status = "disabled";
};
@@ -685,6 +690,8 @@
<&tegra_car TEGRA20_CLK_CLK_M>,
<&tegra_car TEGRA20_CLK_USBD>;
clock-names = "reg", "pll_u", "timer", "utmi-pads";
+ resets = <&tegra_car 59>, <&tegra_car 22>;
+ reset-names = "usb", "utmi-pads";
nvidia,hssync-start-delay = <9>;
nvidia,idle-wait-delay = <17>;
nvidia,elastic-limit = <16>;
diff --git a/arch/arm/boot/dts/tegra30.dtsi b/arch/arm/boot/dts/tegra30.dtsi
index dec4fc823901..54805ce5efe0 100644
--- a/arch/arm/boot/dts/tegra30.dtsi
+++ b/arch/arm/boot/dts/tegra30.dtsi
@@ -775,6 +775,8 @@
<&tegra_car TEGRA30_CLK_PLL_U>,
<&tegra_car TEGRA30_CLK_USBD>;
clock-names = "reg", "pll_u", "utmi-pads";
+ resets = <&tegra_car 22>, <&tegra_car 22>;
+ reset-names = "usb", "utmi-pads";
nvidia,hssync-start-delay = <9>;
nvidia,idle-wait-delay = <17>;
nvidia,elastic-limit = <16>;
@@ -786,6 +788,7 @@
nvidia,xcvr-hsslew = <32>;
nvidia,hssquelch-level = <2>;
nvidia,hsdiscon-level = <5>;
+ nvidia,has-utmi-pad-registers;
status = "disabled";
};
@@ -809,6 +812,8 @@
<&tegra_car TEGRA30_CLK_PLL_U>,
<&tegra_car TEGRA30_CLK_USBD>;
clock-names = "reg", "pll_u", "utmi-pads";
+ resets = <&tegra_car 58>, <&tegra_car 22>;
+ reset-names = "usb", "utmi-pads";
nvidia,hssync-start-delay = <9>;
nvidia,idle-wait-delay = <17>;
nvidia,elastic-limit = <16>;
@@ -843,6 +848,8 @@
<&tegra_car TEGRA30_CLK_PLL_U>,
<&tegra_car TEGRA30_CLK_USBD>;
clock-names = "reg", "pll_u", "utmi-pads";
+ resets = <&tegra_car 59>, <&tegra_car 22>;
+ reset-names = "usb", "utmi-pads";
nvidia,hssync-start-delay = <0>;
nvidia,idle-wait-delay = <17>;
nvidia,elastic-limit = <16>;
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 64b98d242ea6..cc97c897945a 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -15,6 +15,13 @@ config GENERIC_PHY
phy users can obtain reference to the PHY. All the users of this
framework should select this config.
+config PHY_BERLIN_SATA
+ tristate "Marvell Berlin SATA PHY driver"
+ depends on ARCH_BERLIN && HAS_IOMEM && OF
+ select GENERIC_PHY
+ help
+ Enable this to support the SATA PHY on Marvell Berlin SoCs.
+
config PHY_EXYNOS_MIPI_VIDEO
tristate "S5P/EXYNOS SoC series MIPI CSI-2/DSI PHY driver"
depends on HAS_IOMEM
@@ -27,10 +34,20 @@ config PHY_EXYNOS_MIPI_VIDEO
config PHY_MVEBU_SATA
def_bool y
- depends on ARCH_KIRKWOOD || ARCH_DOVE || MACH_DOVE || MACH_KIRKWOOD
+ depends on ARCH_DOVE || MACH_DOVE || MACH_KIRKWOOD
depends on OF
select GENERIC_PHY
+config PHY_MIPHY365X
+ tristate "STMicroelectronics MIPHY365X PHY driver for STiH41x series"
+ depends on ARCH_STI
+ depends on GENERIC_PHY
+ depends on HAS_IOMEM
+ depends on OF
+ help
+ Enable this to support the miphy transceiver (for SATA/PCIE)
+ that is part of STMicroelectronics STiH41x SoC series.
+
config OMAP_CONTROL_PHY
tristate "OMAP CONTROL PHY Driver"
depends on ARCH_OMAP2PLUS || COMPILE_TEST
@@ -109,6 +126,14 @@ config PHY_EXYNOS5250_SATA
SATA 3.0 Gb/s, SATA 6.0 Gb/s speeds. It supports one SATA host
port to accept one SATA device.
+config PHY_HIX5HD2_SATA
+ tristate "HIX5HD2 SATA PHY Driver"
+ depends on ARCH_HIX5HD2 && OF && HAS_IOMEM
+ select GENERIC_PHY
+ select MFD_SYSCON
+ help
+ Support for SATA PHY on Hisilicon hix5hd2 Soc.
+
config PHY_SUN4I_USB
tristate "Allwinner sunxi SoC USB PHY driver"
depends on ARCH_SUNXI && HAS_IOMEM && OF
@@ -124,50 +149,39 @@ config PHY_SUN4I_USB
config PHY_SAMSUNG_USB2
tristate "Samsung USB 2.0 PHY driver"
depends on HAS_IOMEM
+ depends on USB_EHCI_EXYNOS || USB_OHCI_EXYNOS || USB_DWC2
select GENERIC_PHY
select MFD_SYSCON
+ default ARCH_EXYNOS
help
Enable this to support the Samsung USB 2.0 PHY driver for Samsung
- SoCs. This driver provides the interface for USB 2.0 PHY. Support for
- particular SoCs has to be enabled in addition to this driver. Number
- and type of supported phys depends on the SoC.
+ SoCs. This driver provides the interface for USB 2.0 PHY. Support
+ for particular PHYs will be enabled based on the SoC type in addition
+ to this driver.
config PHY_EXYNOS4210_USB2
- bool "Support for Exynos 4210"
+ bool
depends on PHY_SAMSUNG_USB2
- depends on CPU_EXYNOS4210
- help
- Enable USB PHY support for Exynos 4210. This option requires that
- Samsung USB 2.0 PHY driver is enabled and means that support for this
- particular SoC is compiled in the driver. In case of Exynos 4210 four
- phys are available - device, host, HSIC0 and HSIC1.
+ default CPU_EXYNOS4210
config PHY_EXYNOS4X12_USB2
- bool "Support for Exynos 4x12"
+ bool
depends on PHY_SAMSUNG_USB2
- depends on (SOC_EXYNOS4212 || SOC_EXYNOS4412)
- help
- Enable USB PHY support for Exynos 4x12. This option requires that
- Samsung USB 2.0 PHY driver is enabled and means that support for this
- particular SoC is compiled in the driver. In case of Exynos 4x12 four
- phys are available - device, host, HSIC0 and HSIC1.
+ default SOC_EXYNOS3250 || SOC_EXYNOS4212 || SOC_EXYNOS4412
config PHY_EXYNOS5250_USB2
- bool "Support for Exynos 5250"
+ bool
depends on PHY_SAMSUNG_USB2
- depends on SOC_EXYNOS5250
- help
- Enable USB PHY support for Exynos 5250. This option requires that
- Samsung USB 2.0 PHY driver is enabled and means that support for this
- particular SoC is compiled in the driver. In case of Exynos 5250 four
- phys are available - device, host, HSIC0 and HSIC.
+ default SOC_EXYNOS5250 || SOC_EXYNOS5420
config PHY_EXYNOS5_USBDRD
tristate "Exynos5 SoC series USB DRD PHY driver"
depends on ARCH_EXYNOS5 && OF
depends on HAS_IOMEM
+ depends on USB_DWC3_EXYNOS
select GENERIC_PHY
select MFD_SYSCON
+ default y
help
Enable USB DRD PHY support for Exynos 5 SoC series.
This driver provides PHY interface for USB 3.0 DRD controller
@@ -180,4 +194,18 @@ config PHY_XGENE
help
This option enables support for APM X-Gene SoC multi-purpose PHY.
+config PHY_QCOM_APQ8064_SATA
+ tristate "Qualcomm APQ8064 SATA SerDes/PHY driver"
+ depends on ARCH_QCOM
+ depends on HAS_IOMEM
+ depends on OF
+ select GENERIC_PHY
+
+config PHY_QCOM_IPQ806X_SATA
+ tristate "Qualcomm IPQ806x SATA SerDes/PHY driver"
+ depends on ARCH_QCOM
+ depends on HAS_IOMEM
+ depends on OF
+ select GENERIC_PHY
+
endmenu
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index b4f1d5770601..971ad0aac388 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -3,15 +3,18 @@
#
obj-$(CONFIG_GENERIC_PHY) += phy-core.o
+obj-$(CONFIG_PHY_BERLIN_SATA) += phy-berlin-sata.o
obj-$(CONFIG_BCM_KONA_USB2_PHY) += phy-bcm-kona-usb2.o
obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO) += phy-exynos-dp-video.o
obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o
obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o
+obj-$(CONFIG_PHY_MIPHY365X) += phy-miphy365x.o
obj-$(CONFIG_OMAP_CONTROL_PHY) += phy-omap-control.o
obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o
obj-$(CONFIG_TI_PIPE3) += phy-ti-pipe3.o
obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o
obj-$(CONFIG_PHY_EXYNOS5250_SATA) += phy-exynos5250-sata.o
+obj-$(CONFIG_PHY_HIX5HD2_SATA) += phy-hix5hd2-sata.o
obj-$(CONFIG_PHY_SUN4I_USB) += phy-sun4i-usb.o
obj-$(CONFIG_PHY_SAMSUNG_USB2) += phy-exynos-usb2.o
phy-exynos-usb2-y += phy-samsung-usb2.o
@@ -20,3 +23,5 @@ phy-exynos-usb2-$(CONFIG_PHY_EXYNOS4X12_USB2) += phy-exynos4x12-usb2.o
phy-exynos-usb2-$(CONFIG_PHY_EXYNOS5250_USB2) += phy-exynos5250-usb2.o
obj-$(CONFIG_PHY_EXYNOS5_USBDRD) += phy-exynos5-usbdrd.o
obj-$(CONFIG_PHY_XGENE) += phy-xgene.o
+obj-$(CONFIG_PHY_QCOM_APQ8064_SATA) += phy-qcom-apq8064-sata.o
+obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA) += phy-qcom-ipq806x-sata.o
diff --git a/drivers/phy/phy-bcm-kona-usb2.c b/drivers/phy/phy-bcm-kona-usb2.c
index e94f5a6a5645..894fe74c1e44 100644
--- a/drivers/phy/phy-bcm-kona-usb2.c
+++ b/drivers/phy/phy-bcm-kona-usb2.c
@@ -117,7 +117,7 @@ static int bcm_kona_usb2_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, phy);
- gphy = devm_phy_create(dev, &ops, NULL);
+ gphy = devm_phy_create(dev, NULL, &ops, NULL);
if (IS_ERR(gphy))
return PTR_ERR(gphy);
diff --git a/drivers/phy/phy-berlin-sata.c b/drivers/phy/phy-berlin-sata.c
new file mode 100644
index 000000000000..5c3a0424aeb4
--- /dev/null
+++ b/drivers/phy/phy-berlin-sata.c
@@ -0,0 +1,284 @@
+/*
+ * Marvell Berlin SATA PHY driver
+ *
+ * Copyright (C) 2014 Marvell Technology Group Ltd.
+ *
+ * Antoine Ténart <antoine.tenart@free-electrons.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/phy/phy.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+
+#define HOST_VSA_ADDR 0x0
+#define HOST_VSA_DATA 0x4
+#define PORT_SCR_CTL 0x2c
+#define PORT_VSR_ADDR 0x78
+#define PORT_VSR_DATA 0x7c
+
+#define CONTROL_REGISTER 0x0
+#define MBUS_SIZE_CONTROL 0x4
+
+#define POWER_DOWN_PHY0 BIT(6)
+#define POWER_DOWN_PHY1 BIT(14)
+#define MBUS_WRITE_REQUEST_SIZE_128 (BIT(2) << 16)
+#define MBUS_READ_REQUEST_SIZE_128 (BIT(2) << 19)
+
+#define PHY_BASE 0x200
+
+/* register 0x01 */
+#define REF_FREF_SEL_25 BIT(0)
+#define PHY_MODE_SATA (0x0 << 5)
+
+/* register 0x02 */
+#define USE_MAX_PLL_RATE BIT(12)
+
+/* register 0x23 */
+#define DATA_BIT_WIDTH_10 (0x0 << 10)
+#define DATA_BIT_WIDTH_20 (0x1 << 10)
+#define DATA_BIT_WIDTH_40 (0x2 << 10)
+
+/* register 0x25 */
+#define PHY_GEN_MAX_1_5 (0x0 << 10)
+#define PHY_GEN_MAX_3_0 (0x1 << 10)
+#define PHY_GEN_MAX_6_0 (0x2 << 10)
+
+struct phy_berlin_desc {
+ struct phy *phy;
+ u32 power_bit;
+ unsigned index;
+};
+
+struct phy_berlin_priv {
+ void __iomem *base;
+ spinlock_t lock;
+ struct clk *clk;
+ struct phy_berlin_desc **phys;
+ unsigned nphys;
+};
+
+static inline void phy_berlin_sata_reg_setbits(void __iomem *ctrl_reg, u32 reg,
+ u32 mask, u32 val)
+{
+ u32 regval;
+
+ /* select register */
+ writel(PHY_BASE + reg, ctrl_reg + PORT_VSR_ADDR);
+
+ /* set bits */
+ regval = readl(ctrl_reg + PORT_VSR_DATA);
+ regval &= ~mask;
+ regval |= val;
+ writel(regval, ctrl_reg + PORT_VSR_DATA);
+}
+
+static int phy_berlin_sata_power_on(struct phy *phy)
+{
+ struct phy_berlin_desc *desc = phy_get_drvdata(phy);
+ struct phy_berlin_priv *priv = dev_get_drvdata(phy->dev.parent);
+ void __iomem *ctrl_reg = priv->base + 0x60 + (desc->index * 0x80);
+ int ret = 0;
+ u32 regval;
+
+ clk_prepare_enable(priv->clk);
+
+ spin_lock(&priv->lock);
+
+ /* Power on PHY */
+ writel(CONTROL_REGISTER, priv->base + HOST_VSA_ADDR);
+ regval = readl(priv->base + HOST_VSA_DATA);
+ regval &= ~desc->power_bit;
+ writel(regval, priv->base + HOST_VSA_DATA);
+
+ /* Configure MBus */
+ writel(MBUS_SIZE_CONTROL, priv->base + HOST_VSA_ADDR);
+ regval = readl(priv->base + HOST_VSA_DATA);
+ regval |= MBUS_WRITE_REQUEST_SIZE_128 | MBUS_READ_REQUEST_SIZE_128;
+ writel(regval, priv->base + HOST_VSA_DATA);
+
+ /* set PHY mode and ref freq to 25 MHz */
+ phy_berlin_sata_reg_setbits(ctrl_reg, 0x1, 0xff,
+ REF_FREF_SEL_25 | PHY_MODE_SATA);
+
+ /* set PHY up to 6 Gbps */
+ phy_berlin_sata_reg_setbits(ctrl_reg, 0x25, 0xc00, PHY_GEN_MAX_6_0);
+
+ /* set 40 bits width */
+ phy_berlin_sata_reg_setbits(ctrl_reg, 0x23, 0xc00, DATA_BIT_WIDTH_40);
+
+ /* use max pll rate */
+ phy_berlin_sata_reg_setbits(ctrl_reg, 0x2, 0x0, USE_MAX_PLL_RATE);
+
+ /* set Gen3 controller speed */
+ regval = readl(ctrl_reg + PORT_SCR_CTL);
+ regval &= ~GENMASK(7, 4);
+ regval |= 0x30;
+ writel(regval, ctrl_reg + PORT_SCR_CTL);
+
+ spin_unlock(&priv->lock);
+
+ clk_disable_unprepare(priv->clk);
+
+ return ret;
+}
+
+static int phy_berlin_sata_power_off(struct phy *phy)
+{
+ struct phy_berlin_desc *desc = phy_get_drvdata(phy);
+ struct phy_berlin_priv *priv = dev_get_drvdata(phy->dev.parent);
+ u32 regval;
+
+ clk_prepare_enable(priv->clk);
+
+ spin_lock(&priv->lock);
+
+ /* Power down PHY */
+ writel(CONTROL_REGISTER, priv->base + HOST_VSA_ADDR);
+ regval = readl(priv->base + HOST_VSA_DATA);
+ regval |= desc->power_bit;
+ writel(regval, priv->base + HOST_VSA_DATA);
+
+ spin_unlock(&priv->lock);
+
+ clk_disable_unprepare(priv->clk);
+
+ return 0;
+}
+
+static struct phy *phy_berlin_sata_phy_xlate(struct device *dev,
+ struct of_phandle_args *args)
+{
+ struct phy_berlin_priv *priv = dev_get_drvdata(dev);
+ int i;
+
+ if (WARN_ON(args->args[0] >= priv->nphys))
+ return ERR_PTR(-ENODEV);
+
+ for (i = 0; i < priv->nphys; i++) {
+ if (priv->phys[i]->index == args->args[0])
+ break;
+ }
+
+ if (i == priv->nphys)
+ return ERR_PTR(-ENODEV);
+
+ return priv->phys[i]->phy;
+}
+
+static struct phy_ops phy_berlin_sata_ops = {
+ .power_on = phy_berlin_sata_power_on,
+ .power_off = phy_berlin_sata_power_off,
+ .owner = THIS_MODULE,
+};
+
+static u32 phy_berlin_power_down_bits[] = {
+ POWER_DOWN_PHY0,
+ POWER_DOWN_PHY1,
+};
+
+static int phy_berlin_sata_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *child;
+ struct phy *phy;
+ struct phy_provider *phy_provider;
+ struct phy_berlin_priv *priv;
+ struct resource *res;
+ int i = 0;
+ u32 phy_id;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -EINVAL;
+
+ priv->base = devm_ioremap(dev, res->start, resource_size(res));
+ if (!priv->base)
+ return -ENOMEM;
+
+ priv->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(priv->clk))
+ return PTR_ERR(priv->clk);
+
+ priv->nphys = of_get_child_count(dev->of_node);
+ if (priv->nphys == 0)
+ return -ENODEV;
+
+ priv->phys = devm_kzalloc(dev, priv->nphys * sizeof(*priv->phys),
+ GFP_KERNEL);
+ if (!priv->phys)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, priv);
+ spin_lock_init(&priv->lock);
+
+ for_each_available_child_of_node(dev->of_node, child) {
+ struct phy_berlin_desc *phy_desc;
+
+ if (of_property_read_u32(child, "reg", &phy_id)) {
+ dev_err(dev, "missing reg property in node %s\n",
+ child->name);
+ return -EINVAL;
+ }
+
+ if (phy_id >= ARRAY_SIZE(phy_berlin_power_down_bits)) {
+ dev_err(dev, "invalid reg in node %s\n", child->name);
+ return -EINVAL;
+ }
+
+ phy_desc = devm_kzalloc(dev, sizeof(*phy_desc), GFP_KERNEL);
+ if (!phy_desc)
+ return -ENOMEM;
+
+ phy = devm_phy_create(dev, NULL, &phy_berlin_sata_ops, NULL);
+ if (IS_ERR(phy)) {
+ dev_err(dev, "failed to create PHY %d\n", phy_id);
+ return PTR_ERR(phy);
+ }
+
+ phy_desc->phy = phy;
+ phy_desc->power_bit = phy_berlin_power_down_bits[phy_id];
+ phy_desc->index = phy_id;
+ phy_set_drvdata(phy, phy_desc);
+
+ priv->phys[i++] = phy_desc;
+
+ /* Make sure the PHY is off */
+ phy_berlin_sata_power_off(phy);
+ }
+
+ phy_provider =
+ devm_of_phy_provider_register(dev, phy_berlin_sata_phy_xlate);
+ if (IS_ERR(phy_provider))
+ return PTR_ERR(phy_provider);
+
+ return 0;
+}
+
+static const struct of_device_id phy_berlin_sata_of_match[] = {
+ { .compatible = "marvell,berlin2q-sata-phy" },
+ { },
+};
+
+static struct platform_driver phy_berlin_sata_driver = {
+ .probe = phy_berlin_sata_probe,
+ .driver = {
+ .name = "phy-berlin-sata",
+ .owner = THIS_MODULE,
+ .of_match_table = phy_berlin_sata_of_match,
+ },
+};
+module_platform_driver(phy_berlin_sata_driver);
+
+MODULE_DESCRIPTION("Marvell Berlin SATA PHY driver");
+MODULE_AUTHOR("Antoine Ténart <antoine.tenart@free-electrons.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
index 49c446530101..ff5eec5af817 100644
--- a/drivers/phy/phy-core.c
+++ b/drivers/phy/phy-core.c
@@ -21,6 +21,7 @@
#include <linux/phy/phy.h>
#include <linux/idr.h>
#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
static struct class *phy_class;
static DEFINE_MUTEX(phy_provider_mutex);
@@ -86,10 +87,15 @@ static struct phy *phy_lookup(struct device *device, const char *port)
static struct phy_provider *of_phy_provider_lookup(struct device_node *node)
{
struct phy_provider *phy_provider;
+ struct device_node *child;
list_for_each_entry(phy_provider, &phy_provider_list, list) {
if (phy_provider->dev->of_node == node)
return phy_provider;
+
+ for_each_child_of_node(phy_provider->dev->of_node, child)
+ if (child == node)
+ return phy_provider;
}
return ERR_PTR(-EPROBE_DEFER);
@@ -226,6 +232,12 @@ int phy_power_on(struct phy *phy)
if (!phy)
return 0;
+ if (phy->pwr) {
+ ret = regulator_enable(phy->pwr);
+ if (ret)
+ return ret;
+ }
+
ret = phy_pm_runtime_get_sync(phy);
if (ret < 0 && ret != -ENOTSUPP)
return ret;
@@ -247,6 +259,8 @@ int phy_power_on(struct phy *phy)
out:
mutex_unlock(&phy->mutex);
phy_pm_runtime_put_sync(phy);
+ if (phy->pwr)
+ regulator_disable(phy->pwr);
return ret;
}
@@ -272,6 +286,9 @@ int phy_power_off(struct phy *phy)
mutex_unlock(&phy->mutex);
phy_pm_runtime_put(phy);
+ if (phy->pwr)
+ regulator_disable(phy->pwr);
+
return 0;
}
EXPORT_SYMBOL_GPL(phy_power_off);
@@ -398,13 +415,20 @@ struct phy *of_phy_simple_xlate(struct device *dev, struct of_phandle_args
struct phy *phy;
struct class_dev_iter iter;
struct device_node *node = dev->of_node;
+ struct device_node *child;
class_dev_iter_init(&iter, phy_class, NULL, NULL);
while ((dev = class_dev_iter_next(&iter))) {
phy = to_phy(dev);
- if (node != phy->dev.of_node)
+ if (node != phy->dev.of_node) {
+ for_each_child_of_node(node, child) {
+ if (child == phy->dev.of_node)
+ goto phy_found;
+ }
continue;
+ }
+phy_found:
class_dev_iter_exit(&iter);
return phy;
}
@@ -562,13 +586,15 @@ EXPORT_SYMBOL_GPL(devm_of_phy_get);
/**
* phy_create() - create a new phy
* @dev: device that is creating the new phy
+ * @node: device node of the phy
* @ops: function pointers for performing phy operations
* @init_data: contains the list of PHY consumers or NULL
*
* Called to create a phy using phy framework.
*/
-struct phy *phy_create(struct device *dev, const struct phy_ops *ops,
- struct phy_init_data *init_data)
+struct phy *phy_create(struct device *dev, struct device_node *node,
+ const struct phy_ops *ops,
+ struct phy_init_data *init_data)
{
int ret;
int id;
@@ -588,12 +614,22 @@ struct phy *phy_create(struct device *dev, const struct phy_ops *ops,
goto free_phy;
}
+ /* phy-supply */
+ phy->pwr = regulator_get_optional(dev, "phy");
+ if (IS_ERR(phy->pwr)) {
+ if (PTR_ERR(phy->pwr) == -EPROBE_DEFER) {
+ ret = -EPROBE_DEFER;
+ goto free_ida;
+ }
+ phy->pwr = NULL;
+ }
+
device_initialize(&phy->dev);
mutex_init(&phy->mutex);
phy->dev.class = phy_class;
phy->dev.parent = dev;
- phy->dev.of_node = dev->of_node;
+ phy->dev.of_node = node ?: dev->of_node;
phy->id = id;
phy->ops = ops;
phy->init_data = init_data;
@@ -617,6 +653,9 @@ put_dev:
put_device(&phy->dev); /* calls phy_release() which frees resources */
return ERR_PTR(ret);
+free_ida:
+ ida_simple_remove(&phy_ida, phy->id);
+
free_phy:
kfree(phy);
return ERR_PTR(ret);
@@ -626,6 +665,7 @@ EXPORT_SYMBOL_GPL(phy_create);
/**
* devm_phy_create() - create a new phy
* @dev: device that is creating the new phy
+ * @node: device node of the phy
* @ops: function pointers for performing phy operations
* @init_data: contains the list of PHY consumers or NULL
*
@@ -634,8 +674,9 @@ EXPORT_SYMBOL_GPL(phy_create);
* On driver detach, release function is invoked on the devres data,
* then, devres data is freed.
*/
-struct phy *devm_phy_create(struct device *dev, const struct phy_ops *ops,
- struct phy_init_data *init_data)
+struct phy *devm_phy_create(struct device *dev, struct device_node *node,
+ const struct phy_ops *ops,
+ struct phy_init_data *init_data)
{
struct phy **ptr, *phy;
@@ -643,7 +684,7 @@ struct phy *devm_phy_create(struct device *dev, const struct phy_ops *ops,
if (!ptr)
return ERR_PTR(-ENOMEM);
- phy = phy_create(dev, ops, init_data);
+ phy = phy_create(dev, node, ops, init_data);
if (!IS_ERR(phy)) {
*ptr = phy;
devres_add(dev, ptr);
@@ -800,6 +841,7 @@ static void phy_release(struct device *dev)
phy = to_phy(dev);
dev_vdbg(dev, "releasing '%s'\n", dev_name(dev));
+ regulator_put(phy->pwr);
ida_simple_remove(&phy_ida, phy->id);
kfree(phy);
}
diff --git a/drivers/phy/phy-exynos-dp-video.c b/drivers/phy/phy-exynos-dp-video.c
index 0786fef842e7..8b3026e2af7f 100644
--- a/drivers/phy/phy-exynos-dp-video.c
+++ b/drivers/phy/phy-exynos-dp-video.c
@@ -9,6 +9,7 @@
* published by the Free Software Foundation.
*/
+#include <linux/err.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -76,7 +77,7 @@ static int exynos_dp_video_phy_probe(struct platform_device *pdev)
if (IS_ERR(state->regs))
return PTR_ERR(state->regs);
- phy = devm_phy_create(dev, &exynos_dp_video_phy_ops, NULL);
+ phy = devm_phy_create(dev, NULL, &exynos_dp_video_phy_ops, NULL);
if (IS_ERR(phy)) {
dev_err(dev, "failed to create Display Port PHY\n");
return PTR_ERR(phy);
@@ -84,10 +85,8 @@ static int exynos_dp_video_phy_probe(struct platform_device *pdev)
phy_set_drvdata(phy, state);
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
- if (IS_ERR(phy_provider))
- return PTR_ERR(phy_provider);
- return 0;
+ return PTR_ERR_OR_ZERO(phy_provider);
}
static const struct of_device_id exynos_dp_video_phy_of_match[] = {
diff --git a/drivers/phy/phy-exynos-mipi-video.c b/drivers/phy/phy-exynos-mipi-video.c
index ff026689358c..b55a92e12496 100644
--- a/drivers/phy/phy-exynos-mipi-video.c
+++ b/drivers/phy/phy-exynos-mipi-video.c
@@ -9,6 +9,7 @@
* published by the Free Software Foundation.
*/
+#include <linux/err.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -135,7 +136,7 @@ static int exynos_mipi_video_phy_probe(struct platform_device *pdev)
spin_lock_init(&state->slock);
for (i = 0; i < EXYNOS_MIPI_PHYS_NUM; i++) {
- struct phy *phy = devm_phy_create(dev,
+ struct phy *phy = devm_phy_create(dev, NULL,
&exynos_mipi_video_phy_ops, NULL);
if (IS_ERR(phy)) {
dev_err(dev, "failed to create PHY %d\n", i);
@@ -149,10 +150,8 @@ static int exynos_mipi_video_phy_probe(struct platform_device *pdev)
phy_provider = devm_of_phy_provider_register(dev,
exynos_mipi_video_phy_xlate);
- if (IS_ERR(phy_provider))
- return PTR_ERR(phy_provider);
- return 0;
+ return PTR_ERR_OR_ZERO(phy_provider);
}
static const struct of_device_id exynos_mipi_video_phy_of_match[] = {
diff --git a/drivers/phy/phy-exynos4x12-usb2.c b/drivers/phy/phy-exynos4x12-usb2.c
index d92a7cc5698a..0b9de88579b1 100644
--- a/drivers/phy/phy-exynos4x12-usb2.c
+++ b/drivers/phy/phy-exynos4x12-usb2.c
@@ -67,6 +67,8 @@
#define EXYNOS_4x12_UPHYCLK_PHYFSEL_24MHZ (0x5 << 0)
#define EXYNOS_4x12_UPHYCLK_PHYFSEL_50MHZ (0x7 << 0)
+#define EXYNOS_3250_UPHYCLK_REFCLKSEL (0x2 << 8)
+
#define EXYNOS_4x12_UPHYCLK_PHY0_ID_PULLUP BIT(3)
#define EXYNOS_4x12_UPHYCLK_PHY0_COMMON_ON BIT(4)
#define EXYNOS_4x12_UPHYCLK_PHY1_COMMON_ON BIT(7)
@@ -86,13 +88,23 @@
#define EXYNOS_4x12_URSTCON_OTG_HLINK BIT(1)
#define EXYNOS_4x12_URSTCON_OTG_PHYLINK BIT(2)
#define EXYNOS_4x12_URSTCON_HOST_PHY BIT(3)
+/* The following bit defines are presented in the
+ * order taken from the Exynos4412 reference manual.
+ *
+ * During experiments with the hardware and debugging
+ * it was determined that the hardware behaves contrary
+ * to the manual.
+ *
+ * The following bit values were chaned accordingly to the
+ * results of real hardware experiments.
+ */
#define EXYNOS_4x12_URSTCON_PHY1 BIT(4)
-#define EXYNOS_4x12_URSTCON_HSIC0 BIT(5)
-#define EXYNOS_4x12_URSTCON_HSIC1 BIT(6)
+#define EXYNOS_4x12_URSTCON_HSIC0 BIT(6)
+#define EXYNOS_4x12_URSTCON_HSIC1 BIT(5)
#define EXYNOS_4x12_URSTCON_HOST_LINK_ALL BIT(7)
-#define EXYNOS_4x12_URSTCON_HOST_LINK_P0 BIT(8)
+#define EXYNOS_4x12_URSTCON_HOST_LINK_P0 BIT(10)
#define EXYNOS_4x12_URSTCON_HOST_LINK_P1 BIT(9)
-#define EXYNOS_4x12_URSTCON_HOST_LINK_P2 BIT(10)
+#define EXYNOS_4x12_URSTCON_HOST_LINK_P2 BIT(8)
/* Isolation, configured in the power management unit */
#define EXYNOS_4x12_USB_ISOL_OFFSET 0x704
@@ -187,7 +199,12 @@ static void exynos4x12_setup_clk(struct samsung_usb2_phy_instance *inst)
clk = readl(drv->reg_phy + EXYNOS_4x12_UPHYCLK);
clk &= ~EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK;
+
+ if (drv->cfg->has_refclk_sel)
+ clk = EXYNOS_3250_UPHYCLK_REFCLKSEL;
+
clk |= drv->ref_reg_val << EXYNOS_4x12_UPHYCLK_PHYFSEL_OFFSET;
+ clk |= EXYNOS_4x12_UPHYCLK_PHY1_COMMON_ON;
writel(clk, drv->reg_phy + EXYNOS_4x12_UPHYCLK);
}
@@ -198,27 +215,22 @@ static void exynos4x12_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on)
u32 phypwr = 0;
u32 rst;
u32 pwr;
- u32 mode = 0;
- u32 switch_mode = 0;
switch (inst->cfg->id) {
case EXYNOS4x12_DEVICE:
phypwr = EXYNOS_4x12_UPHYPWR_PHY0;
rstbits = EXYNOS_4x12_URSTCON_PHY0;
- mode = EXYNOS_4x12_MODE_SWITCH_DEVICE;
- switch_mode = 1;
break;
case EXYNOS4x12_HOST:
phypwr = EXYNOS_4x12_UPHYPWR_PHY1;
- rstbits = EXYNOS_4x12_URSTCON_HOST_PHY;
- mode = EXYNOS_4x12_MODE_SWITCH_HOST;
- switch_mode = 1;
+ rstbits = EXYNOS_4x12_URSTCON_HOST_PHY |
+ EXYNOS_4x12_URSTCON_PHY1 |
+ EXYNOS_4x12_URSTCON_HOST_LINK_P0;
break;
case EXYNOS4x12_HSIC0:
phypwr = EXYNOS_4x12_UPHYPWR_HSIC0;
- rstbits = EXYNOS_4x12_URSTCON_HSIC1 |
- EXYNOS_4x12_URSTCON_HOST_LINK_P0 |
- EXYNOS_4x12_URSTCON_HOST_PHY;
+ rstbits = EXYNOS_4x12_URSTCON_HSIC0 |
+ EXYNOS_4x12_URSTCON_HOST_LINK_P1;
break;
case EXYNOS4x12_HSIC1:
phypwr = EXYNOS_4x12_UPHYPWR_HSIC1;
@@ -228,11 +240,6 @@ static void exynos4x12_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on)
};
if (on) {
- if (switch_mode)
- regmap_update_bits(drv->reg_sys,
- EXYNOS_4x12_MODE_SWITCH_OFFSET,
- EXYNOS_4x12_MODE_SWITCH_MASK, mode);
-
pwr = readl(drv->reg_phy + EXYNOS_4x12_UPHYPWR);
pwr &= ~phypwr;
writel(pwr, drv->reg_phy + EXYNOS_4x12_UPHYPWR);
@@ -253,41 +260,78 @@ static void exynos4x12_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on)
}
}
-static int exynos4x12_power_on(struct samsung_usb2_phy_instance *inst)
+static void exynos4x12_power_on_int(struct samsung_usb2_phy_instance *inst)
{
- struct samsung_usb2_phy_driver *drv = inst->drv;
+ if (inst->int_cnt++ > 0)
+ return;
- inst->enabled = 1;
exynos4x12_setup_clk(inst);
- exynos4x12_phy_pwr(inst, 1);
exynos4x12_isol(inst, 0);
+ exynos4x12_phy_pwr(inst, 1);
+}
+
+static int exynos4x12_power_on(struct samsung_usb2_phy_instance *inst)
+{
+ struct samsung_usb2_phy_driver *drv = inst->drv;
+
+ if (inst->ext_cnt++ > 0)
+ return 0;
- /* Power on the device, as it is necessary for HSIC to work */
- if (inst->cfg->id == EXYNOS4x12_HSIC0) {
- struct samsung_usb2_phy_instance *device =
- &drv->instances[EXYNOS4x12_DEVICE];
- exynos4x12_phy_pwr(device, 1);
- exynos4x12_isol(device, 0);
+ if (inst->cfg->id == EXYNOS4x12_HOST) {
+ regmap_update_bits(drv->reg_sys, EXYNOS_4x12_MODE_SWITCH_OFFSET,
+ EXYNOS_4x12_MODE_SWITCH_MASK,
+ EXYNOS_4x12_MODE_SWITCH_HOST);
+ exynos4x12_power_on_int(&drv->instances[EXYNOS4x12_DEVICE]);
}
+ if (inst->cfg->id == EXYNOS4x12_DEVICE && drv->cfg->has_mode_switch)
+ regmap_update_bits(drv->reg_sys, EXYNOS_4x12_MODE_SWITCH_OFFSET,
+ EXYNOS_4x12_MODE_SWITCH_MASK,
+ EXYNOS_4x12_MODE_SWITCH_DEVICE);
+
+ if (inst->cfg->id == EXYNOS4x12_HSIC0 ||
+ inst->cfg->id == EXYNOS4x12_HSIC1) {
+ exynos4x12_power_on_int(&drv->instances[EXYNOS4x12_DEVICE]);
+ exynos4x12_power_on_int(&drv->instances[EXYNOS4x12_HOST]);
+ }
+
+ exynos4x12_power_on_int(inst);
+
return 0;
}
-static int exynos4x12_power_off(struct samsung_usb2_phy_instance *inst)
+static void exynos4x12_power_off_int(struct samsung_usb2_phy_instance *inst)
{
- struct samsung_usb2_phy_driver *drv = inst->drv;
- struct samsung_usb2_phy_instance *device =
- &drv->instances[EXYNOS4x12_DEVICE];
+ if (inst->int_cnt-- > 1)
+ return;
- inst->enabled = 0;
exynos4x12_isol(inst, 1);
exynos4x12_phy_pwr(inst, 0);
+}
+
+static int exynos4x12_power_off(struct samsung_usb2_phy_instance *inst)
+{
+ struct samsung_usb2_phy_driver *drv = inst->drv;
+
+ if (inst->ext_cnt-- > 1)
+ return 0;
+
+ if (inst->cfg->id == EXYNOS4x12_DEVICE && drv->cfg->has_mode_switch)
+ regmap_update_bits(drv->reg_sys, EXYNOS_4x12_MODE_SWITCH_OFFSET,
+ EXYNOS_4x12_MODE_SWITCH_MASK,
+ EXYNOS_4x12_MODE_SWITCH_HOST);
+
+ if (inst->cfg->id == EXYNOS4x12_HOST)
+ exynos4x12_power_off_int(&drv->instances[EXYNOS4x12_DEVICE]);
- if (inst->cfg->id == EXYNOS4x12_HSIC0 && !device->enabled) {
- exynos4x12_isol(device, 1);
- exynos4x12_phy_pwr(device, 0);
+ if (inst->cfg->id == EXYNOS4x12_HSIC0 ||
+ inst->cfg->id == EXYNOS4x12_HSIC1) {
+ exynos4x12_power_off_int(&drv->instances[EXYNOS4x12_DEVICE]);
+ exynos4x12_power_off_int(&drv->instances[EXYNOS4x12_HOST]);
}
+ exynos4x12_power_off_int(inst);
+
return 0;
}
@@ -320,6 +364,13 @@ static const struct samsung_usb2_common_phy exynos4x12_phys[] = {
{},
};
+const struct samsung_usb2_phy_config exynos3250_usb2_phy_config = {
+ .has_refclk_sel = 1,
+ .num_phys = 1,
+ .phys = exynos4x12_phys,
+ .rate_to_clk = exynos4x12_rate_to_clk,
+};
+
const struct samsung_usb2_phy_config exynos4x12_usb2_phy_config = {
.has_mode_switch = 1,
.num_phys = EXYNOS4x12_NUM_PHYS,
diff --git a/drivers/phy/phy-exynos5-usbdrd.c b/drivers/phy/phy-exynos5-usbdrd.c
index 76d862b2202f..b05302b09c9f 100644
--- a/drivers/phy/phy-exynos5-usbdrd.c
+++ b/drivers/phy/phy-exynos5-usbdrd.c
@@ -506,7 +506,7 @@ static struct phy_ops exynos5_usbdrd_phy_ops = {
.owner = THIS_MODULE,
};
-const struct exynos5_usbdrd_phy_config phy_cfg_exynos5[] = {
+static const struct exynos5_usbdrd_phy_config phy_cfg_exynos5[] = {
{
.id = EXYNOS5_DRDPHY_UTMI,
.phy_isol = exynos5_usbdrd_phy_isol,
@@ -521,13 +521,13 @@ const struct exynos5_usbdrd_phy_config phy_cfg_exynos5[] = {
},
};
-const struct exynos5_usbdrd_phy_drvdata exynos5420_usbdrd_phy = {
+static const struct exynos5_usbdrd_phy_drvdata exynos5420_usbdrd_phy = {
.phy_cfg = phy_cfg_exynos5,
.pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL,
.pmu_offset_usbdrd1_phy = EXYNOS5420_USBDRD1_PHY_CONTROL,
};
-const struct exynos5_usbdrd_phy_drvdata exynos5250_usbdrd_phy = {
+static const struct exynos5_usbdrd_phy_drvdata exynos5250_usbdrd_phy = {
.phy_cfg = phy_cfg_exynos5,
.pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL,
};
@@ -635,7 +635,8 @@ static int exynos5_usbdrd_phy_probe(struct platform_device *pdev)
dev_vdbg(dev, "Creating usbdrd_phy phy\n");
for (i = 0; i < EXYNOS5_DRDPHYS_NUM; i++) {
- struct phy *phy = devm_phy_create(dev, &exynos5_usbdrd_phy_ops,
+ struct phy *phy = devm_phy_create(dev, NULL,
+ &exynos5_usbdrd_phy_ops,
NULL);
if (IS_ERR(phy)) {
dev_err(dev, "Failed to create usbdrd_phy phy\n");
diff --git a/drivers/phy/phy-exynos5250-sata.c b/drivers/phy/phy-exynos5250-sata.c
index 05689450f93b..19a679aca4ac 100644
--- a/drivers/phy/phy-exynos5250-sata.c
+++ b/drivers/phy/phy-exynos5250-sata.c
@@ -210,7 +210,7 @@ static int exynos_sata_phy_probe(struct platform_device *pdev)
return ret;
}
- sata_phy->phy = devm_phy_create(dev, &exynos_sata_phy_ops, NULL);
+ sata_phy->phy = devm_phy_create(dev, NULL, &exynos_sata_phy_ops, NULL);
if (IS_ERR(sata_phy->phy)) {
clk_disable_unprepare(sata_phy->phyclk);
dev_err(dev, "failed to create PHY\n");
diff --git a/drivers/phy/phy-exynos5250-usb2.c b/drivers/phy/phy-exynos5250-usb2.c
index 94179afda951..1c139aa0d074 100644
--- a/drivers/phy/phy-exynos5250-usb2.c
+++ b/drivers/phy/phy-exynos5250-usb2.c
@@ -318,7 +318,6 @@ static int exynos5250_power_on(struct samsung_usb2_phy_instance *inst)
break;
}
- inst->enabled = 1;
exynos5250_isol(inst, 0);
return 0;
@@ -331,7 +330,6 @@ static int exynos5250_power_off(struct samsung_usb2_phy_instance *inst)
u32 otg;
u32 hsic;
- inst->enabled = 0;
exynos5250_isol(inst, 1);
switch (inst->cfg->id) {
diff --git a/drivers/phy/phy-hix5hd2-sata.c b/drivers/phy/phy-hix5hd2-sata.c
new file mode 100644
index 000000000000..6a08fa5f81eb
--- /dev/null
+++ b/drivers/phy/phy-hix5hd2-sata.c
@@ -0,0 +1,192 @@
+/*
+ * Copyright (c) 2014 Linaro Ltd.
+ * Copyright (c) 2014 Hisilicon Limited.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#define SATA_PHY0_CTLL 0xa0
+#define MPLL_MULTIPLIER_SHIFT 1
+#define MPLL_MULTIPLIER_MASK 0xfe
+#define MPLL_MULTIPLIER_50M 0x3c
+#define MPLL_MULTIPLIER_100M 0x1e
+#define PHY_RESET BIT(0)
+#define REF_SSP_EN BIT(9)
+#define SSC_EN BIT(10)
+#define REF_USE_PAD BIT(23)
+
+#define SATA_PORT_PHYCTL 0x174
+#define SPEED_MODE_MASK 0x6f0000
+#define HALF_RATE_SHIFT 16
+#define PHY_CONFIG_SHIFT 18
+#define GEN2_EN_SHIFT 21
+#define SPEED_CTRL BIT(20)
+
+#define SATA_PORT_PHYCTL1 0x148
+#define AMPLITUDE_MASK 0x3ffffe
+#define AMPLITUDE_GEN3 0x68
+#define AMPLITUDE_GEN3_SHIFT 15
+#define AMPLITUDE_GEN2 0x56
+#define AMPLITUDE_GEN2_SHIFT 8
+#define AMPLITUDE_GEN1 0x56
+#define AMPLITUDE_GEN1_SHIFT 1
+
+#define SATA_PORT_PHYCTL2 0x14c
+#define PREEMPH_MASK 0x3ffff
+#define PREEMPH_GEN3 0x20
+#define PREEMPH_GEN3_SHIFT 12
+#define PREEMPH_GEN2 0x15
+#define PREEMPH_GEN2_SHIFT 6
+#define PREEMPH_GEN1 0x5
+#define PREEMPH_GEN1_SHIFT 0
+
+struct hix5hd2_priv {
+ void __iomem *base;
+ struct regmap *peri_ctrl;
+};
+
+enum phy_speed_mode {
+ SPEED_MODE_GEN1 = 0,
+ SPEED_MODE_GEN2 = 1,
+ SPEED_MODE_GEN3 = 2,
+};
+
+static int hix5hd2_sata_phy_init(struct phy *phy)
+{
+ struct hix5hd2_priv *priv = phy_get_drvdata(phy);
+ u32 val, data[2];
+ int ret;
+
+ if (priv->peri_ctrl) {
+ ret = of_property_read_u32_array(phy->dev.of_node,
+ "hisilicon,power-reg",
+ &data[0], 2);
+ if (ret) {
+ dev_err(&phy->dev, "Fail read hisilicon,power-reg\n");
+ return ret;
+ }
+
+ regmap_update_bits(priv->peri_ctrl, data[0],
+ BIT(data[1]), BIT(data[1]));
+ }
+
+ /* reset phy */
+ val = readl_relaxed(priv->base + SATA_PHY0_CTLL);
+ val &= ~(MPLL_MULTIPLIER_MASK | REF_USE_PAD);
+ val |= MPLL_MULTIPLIER_50M << MPLL_MULTIPLIER_SHIFT |
+ REF_SSP_EN | PHY_RESET;
+ writel_relaxed(val, priv->base + SATA_PHY0_CTLL);
+ msleep(20);
+ val &= ~PHY_RESET;
+ writel_relaxed(val, priv->base + SATA_PHY0_CTLL);
+
+ val = readl_relaxed(priv->base + SATA_PORT_PHYCTL1);
+ val &= ~AMPLITUDE_MASK;
+ val |= AMPLITUDE_GEN3 << AMPLITUDE_GEN3_SHIFT |
+ AMPLITUDE_GEN2 << AMPLITUDE_GEN2_SHIFT |
+ AMPLITUDE_GEN1 << AMPLITUDE_GEN1_SHIFT;
+ writel_relaxed(val, priv->base + SATA_PORT_PHYCTL1);
+
+ val = readl_relaxed(priv->base + SATA_PORT_PHYCTL2);
+ val &= ~PREEMPH_MASK;
+ val |= PREEMPH_GEN3 << PREEMPH_GEN3_SHIFT |
+ PREEMPH_GEN2 << PREEMPH_GEN2_SHIFT |
+ PREEMPH_GEN1 << PREEMPH_GEN1_SHIFT;
+ writel_relaxed(val, priv->base + SATA_PORT_PHYCTL2);
+
+ /* ensure PHYCTRL setting takes effect */
+ val = readl_relaxed(priv->base + SATA_PORT_PHYCTL);
+ val &= ~SPEED_MODE_MASK;
+ val |= SPEED_MODE_GEN1 << HALF_RATE_SHIFT |
+ SPEED_MODE_GEN1 << PHY_CONFIG_SHIFT |
+ SPEED_MODE_GEN1 << GEN2_EN_SHIFT | SPEED_CTRL;
+ writel_relaxed(val, priv->base + SATA_PORT_PHYCTL);
+
+ msleep(20);
+ val &= ~SPEED_MODE_MASK;
+ val |= SPEED_MODE_GEN3 << HALF_RATE_SHIFT |
+ SPEED_MODE_GEN3 << PHY_CONFIG_SHIFT |
+ SPEED_MODE_GEN3 << GEN2_EN_SHIFT | SPEED_CTRL;
+ writel_relaxed(val, priv->base + SATA_PORT_PHYCTL);
+
+ val &= ~(SPEED_MODE_MASK | SPEED_CTRL);
+ val |= SPEED_MODE_GEN2 << HALF_RATE_SHIFT |
+ SPEED_MODE_GEN2 << PHY_CONFIG_SHIFT |
+ SPEED_MODE_GEN2 << GEN2_EN_SHIFT;
+ writel_relaxed(val, priv->base + SATA_PORT_PHYCTL);
+
+ return 0;
+}
+
+static struct phy_ops hix5hd2_sata_phy_ops = {
+ .init = hix5hd2_sata_phy_init,
+ .owner = THIS_MODULE,
+};
+
+static int hix5hd2_sata_phy_probe(struct platform_device *pdev)
+{
+ struct phy_provider *phy_provider;
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ struct phy *phy;
+ struct hix5hd2_priv *priv;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ priv->base = devm_ioremap(dev, res->start, resource_size(res));
+ if (!priv->base)
+ return -ENOMEM;
+
+ priv->peri_ctrl = syscon_regmap_lookup_by_phandle(dev->of_node,
+ "hisilicon,peripheral-syscon");
+ if (IS_ERR(priv->peri_ctrl))
+ priv->peri_ctrl = NULL;
+
+ phy = devm_phy_create(dev, NULL, &hix5hd2_sata_phy_ops, NULL);
+ if (IS_ERR(phy)) {
+ dev_err(dev, "failed to create PHY\n");
+ return PTR_ERR(phy);
+ }
+
+ phy_set_drvdata(phy, priv);
+ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+ if (IS_ERR(phy_provider))
+ return PTR_ERR(phy_provider);
+
+ return 0;
+}
+
+static const struct of_device_id hix5hd2_sata_phy_of_match[] = {
+ {.compatible = "hisilicon,hix5hd2-sata-phy",},
+ { },
+};
+MODULE_DEVICE_TABLE(of, hix5hd2_sata_phy_of_match);
+
+static struct platform_driver hix5hd2_sata_phy_driver = {
+ .probe = hix5hd2_sata_phy_probe,
+ .driver = {
+ .name = "hix5hd2-sata-phy",
+ .owner = THIS_MODULE,
+ .of_match_table = hix5hd2_sata_phy_of_match,
+ }
+};
+module_platform_driver(hix5hd2_sata_phy_driver);
+
+MODULE_AUTHOR("Jiancheng Xue <xuejiancheng@huawei.com>");
+MODULE_DESCRIPTION("HISILICON HIX5HD2 SATA PHY driver");
+MODULE_ALIAS("platform:hix5hd2-sata-phy");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/phy-miphy365x.c b/drivers/phy/phy-miphy365x.c
new file mode 100644
index 000000000000..e111baf187ce
--- /dev/null
+++ b/drivers/phy/phy-miphy365x.c
@@ -0,0 +1,636 @@
+/*
+ * Copyright (C) 2014 STMicroelectronics – All Rights Reserved
+ *
+ * STMicroelectronics PHY driver MiPHY365 (for SoC STiH416).
+ *
+ * Authors: Alexandre Torgue <alexandre.torgue@st.com>
+ * Lee Jones <lee.jones@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_address.h>
+#include <linux/clk.h>
+#include <linux/phy/phy.h>
+#include <linux/delay.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+
+#include <dt-bindings/phy/phy-miphy365x.h>
+
+#define HFC_TIMEOUT 100
+
+#define SYSCFG_SELECT_SATA_MASK BIT(1)
+#define SYSCFG_SELECT_SATA_POS 1
+
+/* MiPHY365x register definitions */
+#define RESET_REG 0x00
+#define RST_PLL BIT(1)
+#define RST_PLL_CAL BIT(2)
+#define RST_RX BIT(4)
+#define RST_MACRO BIT(7)
+
+#define STATUS_REG 0x01
+#define IDLL_RDY BIT(0)
+#define PLL_RDY BIT(1)
+#define DES_BIT_LOCK BIT(2)
+#define DES_SYMBOL_LOCK BIT(3)
+
+#define CTRL_REG 0x02
+#define TERM_EN BIT(0)
+#define PCI_EN BIT(2)
+#define DES_BIT_LOCK_EN BIT(3)
+#define TX_POL BIT(5)
+
+#define INT_CTRL_REG 0x03
+
+#define BOUNDARY1_REG 0x10
+#define SPDSEL_SEL BIT(0)
+
+#define BOUNDARY3_REG 0x12
+#define TX_SPDSEL_GEN1_VAL 0
+#define TX_SPDSEL_GEN2_VAL 0x01
+#define TX_SPDSEL_GEN3_VAL 0x02
+#define RX_SPDSEL_GEN1_VAL 0
+#define RX_SPDSEL_GEN2_VAL (0x01 << 3)
+#define RX_SPDSEL_GEN3_VAL (0x02 << 3)
+
+#define PCIE_REG 0x16
+
+#define BUF_SEL_REG 0x20
+#define CONF_GEN_SEL_GEN3 0x02
+#define CONF_GEN_SEL_GEN2 0x01
+#define PD_VDDTFILTER BIT(4)
+
+#define TXBUF1_REG 0x21
+#define SWING_VAL 0x04
+#define SWING_VAL_GEN1 0x03
+#define PREEMPH_VAL (0x3 << 5)
+
+#define TXBUF2_REG 0x22
+#define TXSLEW_VAL 0x2
+#define TXSLEW_VAL_GEN1 0x4
+
+#define RXBUF_OFFSET_CTRL_REG 0x23
+
+#define RXBUF_REG 0x25
+#define SDTHRES_VAL 0x01
+#define EQ_ON3 (0x03 << 4)
+#define EQ_ON1 (0x01 << 4)
+
+#define COMP_CTRL1_REG 0x40
+#define START_COMSR BIT(0)
+#define START_COMZC BIT(1)
+#define COMSR_DONE BIT(2)
+#define COMZC_DONE BIT(3)
+#define COMP_AUTO_LOAD BIT(4)
+
+#define COMP_CTRL2_REG 0x41
+#define COMP_2MHZ_RAT_GEN1 0x1e
+#define COMP_2MHZ_RAT 0xf
+
+#define COMP_CTRL3_REG 0x42
+#define COMSR_COMP_REF 0x33
+
+#define COMP_IDLL_REG 0x47
+#define COMZC_IDLL 0x2a
+
+#define PLL_CTRL1_REG 0x50
+#define PLL_START_CAL BIT(0)
+#define BUF_EN BIT(2)
+#define SYNCHRO_TX BIT(3)
+#define SSC_EN BIT(6)
+#define CONFIG_PLL BIT(7)
+
+#define PLL_CTRL2_REG 0x51
+#define BYPASS_PLL_CAL BIT(1)
+
+#define PLL_RAT_REG 0x52
+
+#define PLL_SSC_STEP_MSB_REG 0x56
+#define PLL_SSC_STEP_MSB_VAL 0x03
+
+#define PLL_SSC_STEP_LSB_REG 0x57
+#define PLL_SSC_STEP_LSB_VAL 0x63
+
+#define PLL_SSC_PER_MSB_REG 0x58
+#define PLL_SSC_PER_MSB_VAL 0
+
+#define PLL_SSC_PER_LSB_REG 0x59
+#define PLL_SSC_PER_LSB_VAL 0xf1
+
+#define IDLL_TEST_REG 0x72
+#define START_CLK_HF BIT(6)
+
+#define DES_BITLOCK_REG 0x86
+#define BIT_LOCK_LEVEL 0x01
+#define BIT_LOCK_CNT_512 (0x03 << 5)
+
+struct miphy365x_phy {
+ struct phy *phy;
+ void __iomem *base;
+ bool pcie_tx_pol_inv;
+ bool sata_tx_pol_inv;
+ u32 sata_gen;
+ u64 ctrlreg;
+ u8 type;
+};
+
+struct miphy365x_dev {
+ struct device *dev;
+ struct regmap *regmap;
+ struct mutex miphy_mutex;
+ struct miphy365x_phy **phys;
+};
+
+/*
+ * These values are represented in Device tree. They are considered to be ABI
+ * and although they can be extended any existing values must not change.
+ */
+enum miphy_sata_gen {
+ SATA_GEN1 = 1,
+ SATA_GEN2,
+ SATA_GEN3
+};
+
+static u8 rx_tx_spd[] = {
+ TX_SPDSEL_GEN1_VAL | RX_SPDSEL_GEN1_VAL,
+ TX_SPDSEL_GEN2_VAL | RX_SPDSEL_GEN2_VAL,
+ TX_SPDSEL_GEN3_VAL | RX_SPDSEL_GEN3_VAL
+};
+
+/*
+ * This function selects the system configuration,
+ * either two SATA, one SATA and one PCIe, or two PCIe lanes.
+ */
+static int miphy365x_set_path(struct miphy365x_phy *miphy_phy,
+ struct miphy365x_dev *miphy_dev)
+{
+ bool sata = (miphy_phy->type == MIPHY_TYPE_SATA);
+
+ return regmap_update_bits(miphy_dev->regmap,
+ (unsigned int)miphy_phy->ctrlreg,
+ SYSCFG_SELECT_SATA_MASK,
+ sata << SYSCFG_SELECT_SATA_POS);
+}
+
+static int miphy365x_init_pcie_port(struct miphy365x_phy *miphy_phy,
+ struct miphy365x_dev *miphy_dev)
+{
+ u8 val;
+
+ if (miphy_phy->pcie_tx_pol_inv) {
+ /* Invert Tx polarity and clear pci_txdetect_pol bit */
+ val = TERM_EN | PCI_EN | DES_BIT_LOCK_EN | TX_POL;
+ writeb_relaxed(val, miphy_phy->base + CTRL_REG);
+ writeb_relaxed(0x00, miphy_phy->base + PCIE_REG);
+ }
+
+ return 0;
+}
+
+static inline int miphy365x_hfc_not_rdy(struct miphy365x_phy *miphy_phy,
+ struct miphy365x_dev *miphy_dev)
+{
+ unsigned long timeout = jiffies + msecs_to_jiffies(HFC_TIMEOUT);
+ u8 mask = IDLL_RDY | PLL_RDY;
+ u8 regval;
+
+ do {
+ regval = readb_relaxed(miphy_phy->base + STATUS_REG);
+ if (!(regval & mask))
+ return 0;
+
+ usleep_range(2000, 2500);
+ } while (time_before(jiffies, timeout));
+
+ dev_err(miphy_dev->dev, "HFC ready timeout!\n");
+ return -EBUSY;
+}
+
+static inline int miphy365x_rdy(struct miphy365x_phy *miphy_phy,
+ struct miphy365x_dev *miphy_dev)
+{
+ unsigned long timeout = jiffies + msecs_to_jiffies(HFC_TIMEOUT);
+ u8 mask = IDLL_RDY | PLL_RDY;
+ u8 regval;
+
+ do {
+ regval = readb_relaxed(miphy_phy->base + STATUS_REG);
+ if ((regval & mask) == mask)
+ return 0;
+
+ usleep_range(2000, 2500);
+ } while (time_before(jiffies, timeout));
+
+ dev_err(miphy_dev->dev, "PHY not ready timeout!\n");
+ return -EBUSY;
+}
+
+static inline void miphy365x_set_comp(struct miphy365x_phy *miphy_phy,
+ struct miphy365x_dev *miphy_dev)
+{
+ u8 val, mask;
+
+ if (miphy_phy->sata_gen == SATA_GEN1)
+ writeb_relaxed(COMP_2MHZ_RAT_GEN1,
+ miphy_phy->base + COMP_CTRL2_REG);
+ else
+ writeb_relaxed(COMP_2MHZ_RAT,
+ miphy_phy->base + COMP_CTRL2_REG);
+
+ if (miphy_phy->sata_gen != SATA_GEN3) {
+ writeb_relaxed(COMSR_COMP_REF,
+ miphy_phy->base + COMP_CTRL3_REG);
+ /*
+ * Force VCO current to value defined by address 0x5A
+ * and disable PCIe100Mref bit
+ * Enable auto load compensation for pll_i_bias
+ */
+ writeb_relaxed(BYPASS_PLL_CAL, miphy_phy->base + PLL_CTRL2_REG);
+ writeb_relaxed(COMZC_IDLL, miphy_phy->base + COMP_IDLL_REG);
+ }
+
+ /*
+ * Force restart compensation and enable auto load
+ * for Comzc_Tx, Comzc_Rx and Comsr on macro
+ */
+ val = START_COMSR | START_COMZC | COMP_AUTO_LOAD;
+ writeb_relaxed(val, miphy_phy->base + COMP_CTRL1_REG);
+
+ mask = COMSR_DONE | COMZC_DONE;
+ while ((readb_relaxed(miphy_phy->base + COMP_CTRL1_REG) & mask) != mask)
+ cpu_relax();
+}
+
+static inline void miphy365x_set_ssc(struct miphy365x_phy *miphy_phy,
+ struct miphy365x_dev *miphy_dev)
+{
+ u8 val;
+
+ /*
+ * SSC Settings. SSC will be enabled through Link
+ * SSC Ampl. = 0.4%
+ * SSC Freq = 31KHz
+ */
+ writeb_relaxed(PLL_SSC_STEP_MSB_VAL,
+ miphy_phy->base + PLL_SSC_STEP_MSB_REG);
+ writeb_relaxed(PLL_SSC_STEP_LSB_VAL,
+ miphy_phy->base + PLL_SSC_STEP_LSB_REG);
+ writeb_relaxed(PLL_SSC_PER_MSB_VAL,
+ miphy_phy->base + PLL_SSC_PER_MSB_REG);
+ writeb_relaxed(PLL_SSC_PER_LSB_VAL,
+ miphy_phy->base + PLL_SSC_PER_LSB_REG);
+
+ /* SSC Settings complete */
+ if (miphy_phy->sata_gen == SATA_GEN1) {
+ val = PLL_START_CAL | BUF_EN | SYNCHRO_TX | CONFIG_PLL;
+ writeb_relaxed(val, miphy_phy->base + PLL_CTRL1_REG);
+ } else {
+ val = SSC_EN | PLL_START_CAL | BUF_EN | SYNCHRO_TX | CONFIG_PLL;
+ writeb_relaxed(val, miphy_phy->base + PLL_CTRL1_REG);
+ }
+}
+
+static int miphy365x_init_sata_port(struct miphy365x_phy *miphy_phy,
+ struct miphy365x_dev *miphy_dev)
+{
+ int ret;
+ u8 val;
+
+ /*
+ * Force PHY macro reset, PLL calibration reset, PLL reset
+ * and assert Deserializer Reset
+ */
+ val = RST_PLL | RST_PLL_CAL | RST_RX | RST_MACRO;
+ writeb_relaxed(val, miphy_phy->base + RESET_REG);
+
+ if (miphy_phy->sata_tx_pol_inv)
+ writeb_relaxed(TX_POL, miphy_phy->base + CTRL_REG);
+
+ /*
+ * Force macro1 to use rx_lspd, tx_lspd
+ * Force Rx_Clock on first I-DLL phase
+ * Force Des in HP mode on macro, rx_lspd, tx_lspd for Gen2/3
+ */
+ writeb_relaxed(SPDSEL_SEL, miphy_phy->base + BOUNDARY1_REG);
+ writeb_relaxed(START_CLK_HF, miphy_phy->base + IDLL_TEST_REG);
+ val = rx_tx_spd[miphy_phy->sata_gen];
+ writeb_relaxed(val, miphy_phy->base + BOUNDARY3_REG);
+
+ /* Wait for HFC_READY = 0 */
+ ret = miphy365x_hfc_not_rdy(miphy_phy, miphy_dev);
+ if (ret)
+ return ret;
+
+ /* Compensation Recalibration */
+ miphy365x_set_comp(miphy_phy, miphy_dev);
+
+ switch (miphy_phy->sata_gen) {
+ case SATA_GEN3:
+ /*
+ * TX Swing target 550-600mv peak to peak diff
+ * Tx Slew target 90-110ps rising/falling time
+ * Rx Eq ON3, Sigdet threshold SDTH1
+ */
+ val = PD_VDDTFILTER | CONF_GEN_SEL_GEN3;
+ writeb_relaxed(val, miphy_phy->base + BUF_SEL_REG);
+ val = SWING_VAL | PREEMPH_VAL;
+ writeb_relaxed(val, miphy_phy->base + TXBUF1_REG);
+ writeb_relaxed(TXSLEW_VAL, miphy_phy->base + TXBUF2_REG);
+ writeb_relaxed(0x00, miphy_phy->base + RXBUF_OFFSET_CTRL_REG);
+ val = SDTHRES_VAL | EQ_ON3;
+ writeb_relaxed(val, miphy_phy->base + RXBUF_REG);
+ break;
+ case SATA_GEN2:
+ /*
+ * conf gen sel=0x1 to program Gen2 banked registers
+ * VDDT filter ON
+ * Tx Swing target 550-600mV peak-to-peak diff
+ * Tx Slew target 90-110 ps rising/falling time
+ * RX Equalization ON1, Sigdet threshold SDTH1
+ */
+ writeb_relaxed(CONF_GEN_SEL_GEN2,
+ miphy_phy->base + BUF_SEL_REG);
+ writeb_relaxed(SWING_VAL, miphy_phy->base + TXBUF1_REG);
+ writeb_relaxed(TXSLEW_VAL, miphy_phy->base + TXBUF2_REG);
+ val = SDTHRES_VAL | EQ_ON1;
+ writeb_relaxed(val, miphy_phy->base + RXBUF_REG);
+ break;
+ case SATA_GEN1:
+ /*
+ * conf gen sel = 00b to program Gen1 banked registers
+ * VDDT filter ON
+ * Tx Swing target 500-550mV peak-to-peak diff
+ * Tx Slew target120-140 ps rising/falling time
+ */
+ writeb_relaxed(PD_VDDTFILTER, miphy_phy->base + BUF_SEL_REG);
+ writeb_relaxed(SWING_VAL_GEN1, miphy_phy->base + TXBUF1_REG);
+ writeb_relaxed(TXSLEW_VAL_GEN1, miphy_phy->base + TXBUF2_REG);
+ break;
+ default:
+ break;
+ }
+
+ /* Force Macro1 in partial mode & release pll cal reset */
+ writeb_relaxed(RST_RX, miphy_phy->base + RESET_REG);
+ usleep_range(100, 150);
+
+ miphy365x_set_ssc(miphy_phy, miphy_dev);
+
+ /* Wait for phy_ready */
+ ret = miphy365x_rdy(miphy_phy, miphy_dev);
+ if (ret)
+ return ret;
+
+ /*
+ * Enable macro1 to use rx_lspd & tx_lspd
+ * Release Rx_Clock on first I-DLL phase on macro1
+ * Assert deserializer reset
+ * des_bit_lock_en is set
+ * bit lock detection strength
+ * Deassert deserializer reset
+ */
+ writeb_relaxed(0x00, miphy_phy->base + BOUNDARY1_REG);
+ writeb_relaxed(0x00, miphy_phy->base + IDLL_TEST_REG);
+ writeb_relaxed(RST_RX, miphy_phy->base + RESET_REG);
+ val = miphy_phy->sata_tx_pol_inv ?
+ (TX_POL | DES_BIT_LOCK_EN) : DES_BIT_LOCK_EN;
+ writeb_relaxed(val, miphy_phy->base + CTRL_REG);
+
+ val = BIT_LOCK_CNT_512 | BIT_LOCK_LEVEL;
+ writeb_relaxed(val, miphy_phy->base + DES_BITLOCK_REG);
+ writeb_relaxed(0x00, miphy_phy->base + RESET_REG);
+
+ return 0;
+}
+
+static int miphy365x_init(struct phy *phy)
+{
+ struct miphy365x_phy *miphy_phy = phy_get_drvdata(phy);
+ struct miphy365x_dev *miphy_dev = dev_get_drvdata(phy->dev.parent);
+ int ret = 0;
+
+ mutex_lock(&miphy_dev->miphy_mutex);
+
+ ret = miphy365x_set_path(miphy_phy, miphy_dev);
+ if (ret) {
+ mutex_unlock(&miphy_dev->miphy_mutex);
+ return ret;
+ }
+
+ /* Initialise Miphy for PCIe or SATA */
+ if (miphy_phy->type == MIPHY_TYPE_PCIE)
+ ret = miphy365x_init_pcie_port(miphy_phy, miphy_dev);
+ else
+ ret = miphy365x_init_sata_port(miphy_phy, miphy_dev);
+
+ mutex_unlock(&miphy_dev->miphy_mutex);
+
+ return ret;
+}
+
+int miphy365x_get_addr(struct device *dev, struct miphy365x_phy *miphy_phy,
+ int index)
+{
+ struct device_node *phynode = miphy_phy->phy->dev.of_node;
+ const char *name;
+ const __be32 *taddr;
+ int type = miphy_phy->type;
+ int ret;
+
+ ret = of_property_read_string_index(phynode, "reg-names", index, &name);
+ if (ret) {
+ dev_err(dev, "no reg-names property not found\n");
+ return ret;
+ }
+
+ if (!strncmp(name, "syscfg", 6)) {
+ taddr = of_get_address(phynode, index, NULL, NULL);
+ if (!taddr) {
+ dev_err(dev, "failed to fetch syscfg address\n");
+ return -EINVAL;
+ }
+
+ miphy_phy->ctrlreg = of_translate_address(phynode, taddr);
+ if (miphy_phy->ctrlreg == OF_BAD_ADDR) {
+ dev_err(dev, "failed to translate syscfg address\n");
+ return -EINVAL;
+ }
+
+ return 0;
+ }
+
+ if (!((!strncmp(name, "sata", 4) && type == MIPHY_TYPE_SATA) ||
+ (!strncmp(name, "pcie", 4) && type == MIPHY_TYPE_PCIE)))
+ return 0;
+
+ miphy_phy->base = of_iomap(phynode, index);
+ if (!miphy_phy->base) {
+ dev_err(dev, "Failed to map %s\n", phynode->full_name);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static struct phy *miphy365x_xlate(struct device *dev,
+ struct of_phandle_args *args)
+{
+ struct miphy365x_dev *miphy_dev = dev_get_drvdata(dev);
+ struct miphy365x_phy *miphy_phy = NULL;
+ struct device_node *phynode = args->np;
+ int ret, index;
+
+ if (!of_device_is_available(phynode)) {
+ dev_warn(dev, "Requested PHY is disabled\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ if (args->args_count != 1) {
+ dev_err(dev, "Invalid number of cells in 'phy' property\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ for (index = 0; index < of_get_child_count(dev->of_node); index++)
+ if (phynode == miphy_dev->phys[index]->phy->dev.of_node) {
+ miphy_phy = miphy_dev->phys[index];
+ break;
+ }
+
+ if (!miphy_phy) {
+ dev_err(dev, "Failed to find appropriate phy\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ miphy_phy->type = args->args[0];
+
+ if (!(miphy_phy->type == MIPHY_TYPE_SATA ||
+ miphy_phy->type == MIPHY_TYPE_PCIE)) {
+ dev_err(dev, "Unsupported device type: %d\n", miphy_phy->type);
+ return ERR_PTR(-EINVAL);
+ }
+
+ /* Each port handles SATA and PCIE - third entry is always sysconf. */
+ for (index = 0; index < 3; index++) {
+ ret = miphy365x_get_addr(dev, miphy_phy, index);
+ if (ret < 0)
+ return ERR_PTR(ret);
+ }
+
+ return miphy_phy->phy;
+}
+
+static struct phy_ops miphy365x_ops = {
+ .init = miphy365x_init,
+ .owner = THIS_MODULE,
+};
+
+static int miphy365x_of_probe(struct device_node *phynode,
+ struct miphy365x_phy *miphy_phy)
+{
+ of_property_read_u32(phynode, "st,sata-gen", &miphy_phy->sata_gen);
+ if (!miphy_phy->sata_gen)
+ miphy_phy->sata_gen = SATA_GEN1;
+
+ miphy_phy->pcie_tx_pol_inv =
+ of_property_read_bool(phynode, "st,pcie-tx-pol-inv");
+
+ miphy_phy->sata_tx_pol_inv =
+ of_property_read_bool(phynode, "st,sata-tx-pol-inv");
+
+ return 0;
+}
+
+static int miphy365x_probe(struct platform_device *pdev)
+{
+ struct device_node *child, *np = pdev->dev.of_node;
+ struct miphy365x_dev *miphy_dev;
+ struct phy_provider *provider;
+ struct phy *phy;
+ int chancount, port = 0;
+ int ret;
+
+ miphy_dev = devm_kzalloc(&pdev->dev, sizeof(*miphy_dev), GFP_KERNEL);
+ if (!miphy_dev)
+ return -ENOMEM;
+
+ chancount = of_get_child_count(np);
+ miphy_dev->phys = devm_kzalloc(&pdev->dev, sizeof(phy) * chancount,
+ GFP_KERNEL);
+ if (!miphy_dev->phys)
+ return -ENOMEM;
+
+ miphy_dev->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
+ if (IS_ERR(miphy_dev->regmap)) {
+ dev_err(miphy_dev->dev, "No syscfg phandle specified\n");
+ return PTR_ERR(miphy_dev->regmap);
+ }
+
+ miphy_dev->dev = &pdev->dev;
+
+ dev_set_drvdata(&pdev->dev, miphy_dev);
+
+ mutex_init(&miphy_dev->miphy_mutex);
+
+ for_each_child_of_node(np, child) {
+ struct miphy365x_phy *miphy_phy;
+
+ miphy_phy = devm_kzalloc(&pdev->dev, sizeof(*miphy_phy),
+ GFP_KERNEL);
+ if (!miphy_phy)
+ return -ENOMEM;
+
+ miphy_dev->phys[port] = miphy_phy;
+
+ phy = devm_phy_create(&pdev->dev, child, &miphy365x_ops, NULL);
+ if (IS_ERR(phy)) {
+ dev_err(&pdev->dev, "failed to create PHY\n");
+ return PTR_ERR(phy);
+ }
+
+ miphy_dev->phys[port]->phy = phy;
+
+ ret = miphy365x_of_probe(child, miphy_phy);
+ if (ret)
+ return ret;
+
+ phy_set_drvdata(phy, miphy_dev->phys[port]);
+ port++;
+ }
+
+ provider = devm_of_phy_provider_register(&pdev->dev, miphy365x_xlate);
+ if (IS_ERR(provider))
+ return PTR_ERR(provider);
+
+ return 0;
+}
+
+static const struct of_device_id miphy365x_of_match[] = {
+ { .compatible = "st,miphy365x-phy", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, miphy365x_of_match);
+
+static struct platform_driver miphy365x_driver = {
+ .probe = miphy365x_probe,
+ .driver = {
+ .name = "miphy365x-phy",
+ .owner = THIS_MODULE,
+ .of_match_table = miphy365x_of_match,
+ }
+};
+module_platform_driver(miphy365x_driver);
+
+MODULE_AUTHOR("Alexandre Torgue <alexandre.torgue@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics miphy365x driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/phy-mvebu-sata.c b/drivers/phy/phy-mvebu-sata.c
index d70ecd6a1b3f..cc3c0e166daf 100644
--- a/drivers/phy/phy-mvebu-sata.c
+++ b/drivers/phy/phy-mvebu-sata.c
@@ -99,7 +99,7 @@ static int phy_mvebu_sata_probe(struct platform_device *pdev)
if (IS_ERR(priv->clk))
return PTR_ERR(priv->clk);
- phy = devm_phy_create(&pdev->dev, &phy_mvebu_sata_ops, NULL);
+ phy = devm_phy_create(&pdev->dev, NULL, &phy_mvebu_sata_ops, NULL);
if (IS_ERR(phy))
return PTR_ERR(phy);
diff --git a/drivers/phy/phy-omap-control.c b/drivers/phy/phy-omap-control.c
index 311b4f9a5132..9487bf112267 100644
--- a/drivers/phy/phy-omap-control.c
+++ b/drivers/phy/phy-omap-control.c
@@ -27,6 +27,41 @@
#include <linux/phy/omap_control_phy.h>
/**
+ * omap_control_pcie_pcs - set the PCS delay count
+ * @dev: the control module device
+ * @id: index of the pcie PHY (should be 1 or 2)
+ * @delay: 8 bit delay value
+ */
+void omap_control_pcie_pcs(struct device *dev, u8 id, u8 delay)
+{
+ u32 val;
+ struct omap_control_phy *control_phy;
+
+ if (IS_ERR(dev) || !dev) {
+ pr_err("%s: invalid device\n", __func__);
+ return;
+ }
+
+ control_phy = dev_get_drvdata(dev);
+ if (!control_phy) {
+ dev_err(dev, "%s: invalid control phy device\n", __func__);
+ return;
+ }
+
+ if (control_phy->type != OMAP_CTRL_TYPE_PCIE) {
+ dev_err(dev, "%s: unsupported operation\n", __func__);
+ return;
+ }
+
+ val = readl(control_phy->pcie_pcs);
+ val &= ~(OMAP_CTRL_PCIE_PCS_MASK <<
+ (id * OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT));
+ val |= delay << (id * OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT);
+ writel(val, control_phy->pcie_pcs);
+}
+EXPORT_SYMBOL_GPL(omap_control_pcie_pcs);
+
+/**
* omap_control_phy_power - power on/off the phy using control module reg
* @dev: the control module device
* @on: 0 or 1, based on powering on or off the PHY
@@ -61,6 +96,7 @@ void omap_control_phy_power(struct device *dev, int on)
val |= OMAP_CTRL_DEV_PHY_PD;
break;
+ case OMAP_CTRL_TYPE_PCIE:
case OMAP_CTRL_TYPE_PIPE3:
rate = clk_get_rate(control_phy->sys_clk);
rate = rate/1000000;
@@ -211,6 +247,7 @@ EXPORT_SYMBOL_GPL(omap_control_usb_set_mode);
static const enum omap_control_phy_type otghs_data = OMAP_CTRL_TYPE_OTGHS;
static const enum omap_control_phy_type usb2_data = OMAP_CTRL_TYPE_USB2;
static const enum omap_control_phy_type pipe3_data = OMAP_CTRL_TYPE_PIPE3;
+static const enum omap_control_phy_type pcie_data = OMAP_CTRL_TYPE_PCIE;
static const enum omap_control_phy_type dra7usb2_data = OMAP_CTRL_TYPE_DRA7USB2;
static const enum omap_control_phy_type am437usb2_data = OMAP_CTRL_TYPE_AM437USB2;
@@ -228,6 +265,10 @@ static const struct of_device_id omap_control_phy_id_table[] = {
.data = &pipe3_data,
},
{
+ .compatible = "ti,control-phy-pcie",
+ .data = &pcie_data,
+ },
+ {
.compatible = "ti,control-phy-usb2-dra7",
.data = &dra7usb2_data,
},
@@ -279,7 +320,8 @@ static int omap_control_phy_probe(struct platform_device *pdev)
}
}
- if (control_phy->type == OMAP_CTRL_TYPE_PIPE3) {
+ if (control_phy->type == OMAP_CTRL_TYPE_PIPE3 ||
+ control_phy->type == OMAP_CTRL_TYPE_PCIE) {
control_phy->sys_clk = devm_clk_get(control_phy->dev,
"sys_clkin");
if (IS_ERR(control_phy->sys_clk)) {
@@ -288,6 +330,14 @@ static int omap_control_phy_probe(struct platform_device *pdev)
}
}
+ if (control_phy->type == OMAP_CTRL_TYPE_PCIE) {
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "pcie_pcs");
+ control_phy->pcie_pcs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(control_phy->pcie_pcs))
+ return PTR_ERR(control_phy->pcie_pcs);
+ }
+
dev_set_drvdata(control_phy->dev, control_phy);
return 0;
diff --git a/drivers/phy/phy-omap-usb2.c b/drivers/phy/phy-omap-usb2.c
index 34b396146c8a..93d78359246c 100644
--- a/drivers/phy/phy-omap-usb2.c
+++ b/drivers/phy/phy-omap-usb2.c
@@ -263,7 +263,7 @@ static int omap_usb2_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, phy);
- generic_phy = devm_phy_create(phy->dev, &ops, NULL);
+ generic_phy = devm_phy_create(phy->dev, NULL, &ops, NULL);
if (IS_ERR(generic_phy))
return PTR_ERR(generic_phy);
diff --git a/drivers/phy/phy-qcom-apq8064-sata.c b/drivers/phy/phy-qcom-apq8064-sata.c
new file mode 100644
index 000000000000..b3ef7d805765
--- /dev/null
+++ b/drivers/phy/phy-qcom-apq8064-sata.c
@@ -0,0 +1,289 @@
+/*
+ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/time.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/phy/phy.h>
+
+/* PHY registers */
+#define UNIPHY_PLL_REFCLK_CFG 0x000
+#define UNIPHY_PLL_PWRGEN_CFG 0x014
+#define UNIPHY_PLL_GLB_CFG 0x020
+#define UNIPHY_PLL_SDM_CFG0 0x038
+#define UNIPHY_PLL_SDM_CFG1 0x03C
+#define UNIPHY_PLL_SDM_CFG2 0x040
+#define UNIPHY_PLL_SDM_CFG3 0x044
+#define UNIPHY_PLL_SDM_CFG4 0x048
+#define UNIPHY_PLL_SSC_CFG0 0x04C
+#define UNIPHY_PLL_SSC_CFG1 0x050
+#define UNIPHY_PLL_SSC_CFG2 0x054
+#define UNIPHY_PLL_SSC_CFG3 0x058
+#define UNIPHY_PLL_LKDET_CFG0 0x05C
+#define UNIPHY_PLL_LKDET_CFG1 0x060
+#define UNIPHY_PLL_LKDET_CFG2 0x064
+#define UNIPHY_PLL_CAL_CFG0 0x06C
+#define UNIPHY_PLL_CAL_CFG8 0x08C
+#define UNIPHY_PLL_CAL_CFG9 0x090
+#define UNIPHY_PLL_CAL_CFG10 0x094
+#define UNIPHY_PLL_CAL_CFG11 0x098
+#define UNIPHY_PLL_STATUS 0x0C0
+
+#define SATA_PHY_SER_CTRL 0x100
+#define SATA_PHY_TX_DRIV_CTRL0 0x104
+#define SATA_PHY_TX_DRIV_CTRL1 0x108
+#define SATA_PHY_TX_IMCAL0 0x11C
+#define SATA_PHY_TX_IMCAL2 0x124
+#define SATA_PHY_RX_IMCAL0 0x128
+#define SATA_PHY_EQUAL 0x13C
+#define SATA_PHY_OOB_TERM 0x144
+#define SATA_PHY_CDR_CTRL0 0x148
+#define SATA_PHY_CDR_CTRL1 0x14C
+#define SATA_PHY_CDR_CTRL2 0x150
+#define SATA_PHY_CDR_CTRL3 0x154
+#define SATA_PHY_PI_CTRL0 0x168
+#define SATA_PHY_POW_DWN_CTRL0 0x180
+#define SATA_PHY_POW_DWN_CTRL1 0x184
+#define SATA_PHY_TX_DATA_CTRL 0x188
+#define SATA_PHY_ALIGNP 0x1A4
+#define SATA_PHY_TX_IMCAL_STAT 0x1E4
+#define SATA_PHY_RX_IMCAL_STAT 0x1E8
+
+#define UNIPHY_PLL_LOCK BIT(0)
+#define SATA_PHY_TX_CAL BIT(0)
+#define SATA_PHY_RX_CAL BIT(0)
+
+/* default timeout set to 1 sec */
+#define TIMEOUT_MS 10000
+#define DELAY_INTERVAL_US 100
+
+struct qcom_apq8064_sata_phy {
+ void __iomem *mmio;
+ struct clk *cfg_clk;
+ struct device *dev;
+};
+
+/* Helper function to do poll and timeout */
+static int read_poll_timeout(void __iomem *addr, u32 mask)
+{
+ unsigned long timeout = jiffies + msecs_to_jiffies(TIMEOUT_MS);
+
+ do {
+ if (readl_relaxed(addr) & mask)
+ return 0;
+
+ usleep_range(DELAY_INTERVAL_US, DELAY_INTERVAL_US + 50);
+ } while (!time_after(jiffies, timeout));
+
+ return (readl_relaxed(addr) & mask) ? 0 : -ETIMEDOUT;
+}
+
+static int qcom_apq8064_sata_phy_init(struct phy *generic_phy)
+{
+ struct qcom_apq8064_sata_phy *phy = phy_get_drvdata(generic_phy);
+ void __iomem *base = phy->mmio;
+ int ret = 0;
+
+ /* SATA phy initialization */
+ writel_relaxed(0x01, base + SATA_PHY_SER_CTRL);
+ writel_relaxed(0xB1, base + SATA_PHY_POW_DWN_CTRL0);
+ /* Make sure the power down happens before power up */
+ mb();
+ usleep_range(10, 60);
+
+ writel_relaxed(0x01, base + SATA_PHY_POW_DWN_CTRL0);
+ writel_relaxed(0x3E, base + SATA_PHY_POW_DWN_CTRL1);
+ writel_relaxed(0x01, base + SATA_PHY_RX_IMCAL0);
+ writel_relaxed(0x01, base + SATA_PHY_TX_IMCAL0);
+ writel_relaxed(0x02, base + SATA_PHY_TX_IMCAL2);
+
+ /* Write UNIPHYPLL registers to configure PLL */
+ writel_relaxed(0x04, base + UNIPHY_PLL_REFCLK_CFG);
+ writel_relaxed(0x00, base + UNIPHY_PLL_PWRGEN_CFG);
+
+ writel_relaxed(0x0A, base + UNIPHY_PLL_CAL_CFG0);
+ writel_relaxed(0xF3, base + UNIPHY_PLL_CAL_CFG8);
+ writel_relaxed(0x01, base + UNIPHY_PLL_CAL_CFG9);
+ writel_relaxed(0xED, base + UNIPHY_PLL_CAL_CFG10);
+ writel_relaxed(0x02, base + UNIPHY_PLL_CAL_CFG11);
+
+ writel_relaxed(0x36, base + UNIPHY_PLL_SDM_CFG0);
+ writel_relaxed(0x0D, base + UNIPHY_PLL_SDM_CFG1);
+ writel_relaxed(0xA3, base + UNIPHY_PLL_SDM_CFG2);
+ writel_relaxed(0xF0, base + UNIPHY_PLL_SDM_CFG3);
+ writel_relaxed(0x00, base + UNIPHY_PLL_SDM_CFG4);
+
+ writel_relaxed(0x19, base + UNIPHY_PLL_SSC_CFG0);
+ writel_relaxed(0xE1, base + UNIPHY_PLL_SSC_CFG1);
+ writel_relaxed(0x00, base + UNIPHY_PLL_SSC_CFG2);
+ writel_relaxed(0x11, base + UNIPHY_PLL_SSC_CFG3);
+
+ writel_relaxed(0x04, base + UNIPHY_PLL_LKDET_CFG0);
+ writel_relaxed(0xFF, base + UNIPHY_PLL_LKDET_CFG1);
+
+ writel_relaxed(0x02, base + UNIPHY_PLL_GLB_CFG);
+ /* make sure global config LDO power down happens before power up */
+ mb();
+
+ writel_relaxed(0x03, base + UNIPHY_PLL_GLB_CFG);
+ writel_relaxed(0x05, base + UNIPHY_PLL_LKDET_CFG2);
+
+ /* PLL Lock wait */
+ ret = read_poll_timeout(base + UNIPHY_PLL_STATUS, UNIPHY_PLL_LOCK);
+ if (ret) {
+ dev_err(phy->dev, "poll timeout UNIPHY_PLL_STATUS\n");
+ return ret;
+ }
+
+ /* TX Calibration */
+ ret = read_poll_timeout(base + SATA_PHY_TX_IMCAL_STAT, SATA_PHY_TX_CAL);
+ if (ret) {
+ dev_err(phy->dev, "poll timeout SATA_PHY_TX_IMCAL_STAT\n");
+ return ret;
+ }
+
+ /* RX Calibration */
+ ret = read_poll_timeout(base + SATA_PHY_RX_IMCAL_STAT, SATA_PHY_RX_CAL);
+ if (ret) {
+ dev_err(phy->dev, "poll timeout SATA_PHY_RX_IMCAL_STAT\n");
+ return ret;
+ }
+
+ /* SATA phy calibrated succesfully, power up to functional mode */
+ writel_relaxed(0x3E, base + SATA_PHY_POW_DWN_CTRL1);
+ writel_relaxed(0x01, base + SATA_PHY_RX_IMCAL0);
+ writel_relaxed(0x01, base + SATA_PHY_TX_IMCAL0);
+
+ writel_relaxed(0x00, base + SATA_PHY_POW_DWN_CTRL1);
+ writel_relaxed(0x59, base + SATA_PHY_CDR_CTRL0);
+ writel_relaxed(0x04, base + SATA_PHY_CDR_CTRL1);
+ writel_relaxed(0x00, base + SATA_PHY_CDR_CTRL2);
+ writel_relaxed(0x00, base + SATA_PHY_PI_CTRL0);
+ writel_relaxed(0x00, base + SATA_PHY_CDR_CTRL3);
+ writel_relaxed(0x01, base + SATA_PHY_POW_DWN_CTRL0);
+
+ writel_relaxed(0x11, base + SATA_PHY_TX_DATA_CTRL);
+ writel_relaxed(0x43, base + SATA_PHY_ALIGNP);
+ writel_relaxed(0x04, base + SATA_PHY_OOB_TERM);
+
+ writel_relaxed(0x01, base + SATA_PHY_EQUAL);
+ writel_relaxed(0x09, base + SATA_PHY_TX_DRIV_CTRL0);
+ writel_relaxed(0x09, base + SATA_PHY_TX_DRIV_CTRL1);
+
+ return 0;
+}
+
+static int qcom_apq8064_sata_phy_exit(struct phy *generic_phy)
+{
+ struct qcom_apq8064_sata_phy *phy = phy_get_drvdata(generic_phy);
+ void __iomem *base = phy->mmio;
+
+ /* Power down PHY */
+ writel_relaxed(0xF8, base + SATA_PHY_POW_DWN_CTRL0);
+ writel_relaxed(0xFE, base + SATA_PHY_POW_DWN_CTRL1);
+
+ /* Power down PLL block */
+ writel_relaxed(0x00, base + UNIPHY_PLL_GLB_CFG);
+
+ return 0;
+}
+
+static struct phy_ops qcom_apq8064_sata_phy_ops = {
+ .init = qcom_apq8064_sata_phy_init,
+ .exit = qcom_apq8064_sata_phy_exit,
+ .owner = THIS_MODULE,
+};
+
+static int qcom_apq8064_sata_phy_probe(struct platform_device *pdev)
+{
+ struct qcom_apq8064_sata_phy *phy;
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ struct phy_provider *phy_provider;
+ struct phy *generic_phy;
+ int ret;
+
+ phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
+ if (!phy)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ phy->mmio = devm_ioremap_resource(dev, res);
+ if (IS_ERR(phy->mmio))
+ return PTR_ERR(phy->mmio);
+
+ generic_phy = devm_phy_create(dev, NULL, &qcom_apq8064_sata_phy_ops,
+ NULL);
+ if (IS_ERR(generic_phy)) {
+ dev_err(dev, "%s: failed to create phy\n", __func__);
+ return PTR_ERR(generic_phy);
+ }
+
+ phy->dev = dev;
+ phy_set_drvdata(generic_phy, phy);
+ platform_set_drvdata(pdev, phy);
+
+ phy->cfg_clk = devm_clk_get(dev, "cfg");
+ if (IS_ERR(phy->cfg_clk)) {
+ dev_err(dev, "Failed to get sata cfg clock\n");
+ return PTR_ERR(phy->cfg_clk);
+ }
+
+ ret = clk_prepare_enable(phy->cfg_clk);
+ if (ret)
+ return ret;
+
+ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+ if (IS_ERR(phy_provider)) {
+ clk_disable_unprepare(phy->cfg_clk);
+ dev_err(dev, "%s: failed to register phy\n", __func__);
+ return PTR_ERR(phy_provider);
+ }
+
+ return 0;
+}
+
+static int qcom_apq8064_sata_phy_remove(struct platform_device *pdev)
+{
+ struct qcom_apq8064_sata_phy *phy = platform_get_drvdata(pdev);
+
+ clk_disable_unprepare(phy->cfg_clk);
+
+ return 0;
+}
+
+static const struct of_device_id qcom_apq8064_sata_phy_of_match[] = {
+ { .compatible = "qcom,apq8064-sata-phy" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, qcom_apq8064_sata_phy_of_match);
+
+static struct platform_driver qcom_apq8064_sata_phy_driver = {
+ .probe = qcom_apq8064_sata_phy_probe,
+ .remove = qcom_apq8064_sata_phy_remove,
+ .driver = {
+ .name = "qcom-apq8064-sata-phy",
+ .owner = THIS_MODULE,
+ .of_match_table = qcom_apq8064_sata_phy_of_match,
+ }
+};
+module_platform_driver(qcom_apq8064_sata_phy_driver);
+
+MODULE_DESCRIPTION("QCOM apq8064 SATA PHY driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/phy-qcom-ipq806x-sata.c b/drivers/phy/phy-qcom-ipq806x-sata.c
new file mode 100644
index 000000000000..909b5a87fc6a
--- /dev/null
+++ b/drivers/phy/phy-qcom-ipq806x-sata.c
@@ -0,0 +1,211 @@
+/*
+ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/time.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/phy/phy.h>
+
+struct qcom_ipq806x_sata_phy {
+ void __iomem *mmio;
+ struct clk *cfg_clk;
+ struct device *dev;
+};
+
+#define __set(v, a, b) (((v) << (b)) & GENMASK(a, b))
+
+#define SATA_PHY_P0_PARAM0 0x200
+#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3(x) __set(x, 17, 12)
+#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3_MASK GENMASK(17, 12)
+#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2(x) __set(x, 11, 6)
+#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2_MASK GENMASK(11, 6)
+#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1(x) __set(x, 5, 0)
+#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1_MASK GENMASK(5, 0)
+
+#define SATA_PHY_P0_PARAM1 0x204
+#define SATA_PHY_P0_PARAM1_RESERVED_BITS31_21(x) __set(x, 31, 21)
+#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3(x) __set(x, 20, 14)
+#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3_MASK GENMASK(20, 14)
+#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2(x) __set(x, 13, 7)
+#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2_MASK GENMASK(13, 7)
+#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1(x) __set(x, 6, 0)
+#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1_MASK GENMASK(6, 0)
+
+#define SATA_PHY_P0_PARAM2 0x208
+#define SATA_PHY_P0_PARAM2_RX_EQ(x) __set(x, 20, 18)
+#define SATA_PHY_P0_PARAM2_RX_EQ_MASK GENMASK(20, 18)
+
+#define SATA_PHY_P0_PARAM3 0x20C
+#define SATA_PHY_SSC_EN 0x8
+#define SATA_PHY_P0_PARAM4 0x210
+#define SATA_PHY_REF_SSP_EN 0x2
+#define SATA_PHY_RESET 0x1
+
+static int qcom_ipq806x_sata_phy_init(struct phy *generic_phy)
+{
+ struct qcom_ipq806x_sata_phy *phy = phy_get_drvdata(generic_phy);
+ u32 reg;
+
+ /* Setting SSC_EN to 1 */
+ reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM3);
+ reg = reg | SATA_PHY_SSC_EN;
+ writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM3);
+
+ reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM0) &
+ ~(SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3_MASK |
+ SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2_MASK |
+ SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1_MASK);
+ reg |= SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3(0xf);
+ writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM0);
+
+ reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM1) &
+ ~(SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3_MASK |
+ SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2_MASK |
+ SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1_MASK);
+ reg |= SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3(0x55) |
+ SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2(0x55) |
+ SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1(0x55);
+ writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM1);
+
+ reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM2) &
+ ~SATA_PHY_P0_PARAM2_RX_EQ_MASK;
+ reg |= SATA_PHY_P0_PARAM2_RX_EQ(0x3);
+ writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM2);
+
+ /* Setting PHY_RESET to 1 */
+ reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4);
+ reg = reg | SATA_PHY_RESET;
+ writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4);
+
+ /* Setting REF_SSP_EN to 1 */
+ reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4);
+ reg = reg | SATA_PHY_REF_SSP_EN | SATA_PHY_RESET;
+ writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4);
+
+ /* make sure all changes complete before we let the PHY out of reset */
+ mb();
+
+ /* sleep for max. 50us more to combine processor wakeups */
+ usleep_range(20, 20 + 50);
+
+ /* Clearing PHY_RESET to 0 */
+ reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4);
+ reg = reg & ~SATA_PHY_RESET;
+ writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4);
+
+ return 0;
+}
+
+static int qcom_ipq806x_sata_phy_exit(struct phy *generic_phy)
+{
+ struct qcom_ipq806x_sata_phy *phy = phy_get_drvdata(generic_phy);
+ u32 reg;
+
+ /* Setting PHY_RESET to 1 */
+ reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4);
+ reg = reg | SATA_PHY_RESET;
+ writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4);
+
+ return 0;
+}
+
+static struct phy_ops qcom_ipq806x_sata_phy_ops = {
+ .init = qcom_ipq806x_sata_phy_init,
+ .exit = qcom_ipq806x_sata_phy_exit,
+ .owner = THIS_MODULE,
+};
+
+static int qcom_ipq806x_sata_phy_probe(struct platform_device *pdev)
+{
+ struct qcom_ipq806x_sata_phy *phy;
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ struct phy_provider *phy_provider;
+ struct phy *generic_phy;
+ int ret;
+
+ phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
+ if (!phy)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ phy->mmio = devm_ioremap_resource(dev, res);
+ if (IS_ERR(phy->mmio))
+ return PTR_ERR(phy->mmio);
+
+ generic_phy = devm_phy_create(dev, NULL, &qcom_ipq806x_sata_phy_ops,
+ NULL);
+ if (IS_ERR(generic_phy)) {
+ dev_err(dev, "%s: failed to create phy\n", __func__);
+ return PTR_ERR(generic_phy);
+ }
+
+ phy->dev = dev;
+ phy_set_drvdata(generic_phy, phy);
+ platform_set_drvdata(pdev, phy);
+
+ phy->cfg_clk = devm_clk_get(dev, "cfg");
+ if (IS_ERR(phy->cfg_clk)) {
+ dev_err(dev, "Failed to get sata cfg clock\n");
+ return PTR_ERR(phy->cfg_clk);
+ }
+
+ ret = clk_prepare_enable(phy->cfg_clk);
+ if (ret)
+ return ret;
+
+ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+ if (IS_ERR(phy_provider)) {
+ clk_disable_unprepare(phy->cfg_clk);
+ dev_err(dev, "%s: failed to register phy\n", __func__);
+ return PTR_ERR(phy_provider);
+ }
+
+ return 0;
+}
+
+static int qcom_ipq806x_sata_phy_remove(struct platform_device *pdev)
+{
+ struct qcom_ipq806x_sata_phy *phy = platform_get_drvdata(pdev);
+
+ clk_disable_unprepare(phy->cfg_clk);
+
+ return 0;
+}
+
+static const struct of_device_id qcom_ipq806x_sata_phy_of_match[] = {
+ { .compatible = "qcom,ipq806x-sata-phy" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, qcom_ipq806x_sata_phy_of_match);
+
+static struct platform_driver qcom_ipq806x_sata_phy_driver = {
+ .probe = qcom_ipq806x_sata_phy_probe,
+ .remove = qcom_ipq806x_sata_phy_remove,
+ .driver = {
+ .name = "qcom-ipq806x-sata-phy",
+ .owner = THIS_MODULE,
+ .of_match_table = qcom_ipq806x_sata_phy_of_match,
+ }
+};
+module_platform_driver(qcom_ipq806x_sata_phy_driver);
+
+MODULE_DESCRIPTION("QCOM IPQ806x SATA PHY driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/phy-samsung-usb2.c b/drivers/phy/phy-samsung-usb2.c
index 1e69a32c221d..ae30640a411d 100644
--- a/drivers/phy/phy-samsung-usb2.c
+++ b/drivers/phy/phy-samsung-usb2.c
@@ -87,6 +87,12 @@ static struct phy *samsung_usb2_phy_xlate(struct device *dev,
}
static const struct of_device_id samsung_usb2_phy_of_match[] = {
+#ifdef CONFIG_PHY_EXYNOS4X12_USB2
+ {
+ .compatible = "samsung,exynos3250-usb2-phy",
+ .data = &exynos3250_usb2_phy_config,
+ },
+#endif
#ifdef CONFIG_PHY_EXYNOS4210_USB2
{
.compatible = "samsung,exynos4210-usb2-phy",
@@ -190,7 +196,8 @@ static int samsung_usb2_phy_probe(struct platform_device *pdev)
struct samsung_usb2_phy_instance *p = &drv->instances[i];
dev_dbg(dev, "Creating phy \"%s\"\n", label);
- p->phy = devm_phy_create(dev, &samsung_usb2_phy_ops, NULL);
+ p->phy = devm_phy_create(dev, NULL, &samsung_usb2_phy_ops,
+ NULL);
if (IS_ERR(p->phy)) {
dev_err(drv->dev, "Failed to create usb2_phy \"%s\"\n",
label);
diff --git a/drivers/phy/phy-samsung-usb2.h b/drivers/phy/phy-samsung-usb2.h
index 45b3170652bd..b03da0ef39ac 100644
--- a/drivers/phy/phy-samsung-usb2.h
+++ b/drivers/phy/phy-samsung-usb2.h
@@ -29,7 +29,8 @@ struct samsung_usb2_phy_instance {
const struct samsung_usb2_common_phy *cfg;
struct phy *phy;
struct samsung_usb2_phy_driver *drv;
- bool enabled;
+ int int_cnt;
+ int ext_cnt;
};
struct samsung_usb2_phy_driver {
@@ -59,8 +60,10 @@ struct samsung_usb2_phy_config {
int (*rate_to_clk)(unsigned long, u32 *);
unsigned int num_phys;
bool has_mode_switch;
+ bool has_refclk_sel;
};
+extern const struct samsung_usb2_phy_config exynos3250_usb2_phy_config;
extern const struct samsung_usb2_phy_config exynos4210_usb2_phy_config;
extern const struct samsung_usb2_phy_config exynos4x12_usb2_phy_config;
extern const struct samsung_usb2_phy_config exynos5250_usb2_phy_config;
diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c
index 115d8d5190d5..61ebea49709b 100644
--- a/drivers/phy/phy-sun4i-usb.c
+++ b/drivers/phy/phy-sun4i-usb.c
@@ -22,6 +22,7 @@
*/
#include <linux/clk.h>
+#include <linux/err.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -294,7 +295,7 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
return PTR_ERR(phy->pmu);
}
- phy->phy = devm_phy_create(dev, &sun4i_usb_phy_ops, NULL);
+ phy->phy = devm_phy_create(dev, NULL, &sun4i_usb_phy_ops, NULL);
if (IS_ERR(phy->phy)) {
dev_err(dev, "failed to create PHY %d\n", i);
return PTR_ERR(phy->phy);
@@ -306,10 +307,8 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
dev_set_drvdata(dev, data);
phy_provider = devm_of_phy_provider_register(dev, sun4i_usb_phy_xlate);
- if (IS_ERR(phy_provider))
- return PTR_ERR(phy_provider);
- return 0;
+ return PTR_ERR_OR_ZERO(phy_provider);
}
static const struct of_device_id sun4i_usb_phy_of_match[] = {
diff --git a/drivers/phy/phy-ti-pipe3.c b/drivers/phy/phy-ti-pipe3.c
index 591367654613..b964aa967b46 100644
--- a/drivers/phy/phy-ti-pipe3.c
+++ b/drivers/phy/phy-ti-pipe3.c
@@ -80,7 +80,9 @@ struct ti_pipe3 {
struct clk *wkupclk;
struct clk *sys_clk;
struct clk *refclk;
+ struct clk *div_clk;
struct pipe3_dpll_map *dpll_map;
+ u8 id;
};
static struct pipe3_dpll_map dpll_map_usb[] = {
@@ -215,6 +217,11 @@ static int ti_pipe3_init(struct phy *x)
u32 val;
int ret = 0;
+ if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-pcie")) {
+ omap_control_pcie_pcs(phy->control_dev, phy->id, 0xF1);
+ return 0;
+ }
+
/* Bring it out of IDLE if it is IDLE */
val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2);
if (val & PLL_IDLE) {
@@ -238,8 +245,11 @@ static int ti_pipe3_exit(struct phy *x)
u32 val;
unsigned long timeout;
- /* SATA DPLL can't be powered down due to Errata i783 */
- if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-sata"))
+ /* SATA DPLL can't be powered down due to Errata i783 and PCIe
+ * does not have internal DPLL
+ */
+ if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-sata") ||
+ of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-pcie"))
return 0;
/* Put DPLL in IDLE mode */
@@ -286,32 +296,41 @@ static int ti_pipe3_probe(struct platform_device *pdev)
struct device_node *control_node;
struct platform_device *control_pdev;
const struct of_device_id *match;
-
- match = of_match_device(of_match_ptr(ti_pipe3_id_table), &pdev->dev);
- if (!match)
- return -EINVAL;
+ struct clk *clk;
phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
if (!phy) {
dev_err(&pdev->dev, "unable to alloc mem for TI PIPE3 PHY\n");
return -ENOMEM;
}
+ phy->dev = &pdev->dev;
- phy->dpll_map = (struct pipe3_dpll_map *)match->data;
- if (!phy->dpll_map) {
- dev_err(&pdev->dev, "no DPLL data\n");
- return -EINVAL;
- }
+ if (!of_device_is_compatible(node, "ti,phy-pipe3-pcie")) {
+ match = of_match_device(of_match_ptr(ti_pipe3_id_table),
+ &pdev->dev);
+ if (!match)
+ return -EINVAL;
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pll_ctrl");
- phy->pll_ctrl_base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(phy->pll_ctrl_base))
- return PTR_ERR(phy->pll_ctrl_base);
+ phy->dpll_map = (struct pipe3_dpll_map *)match->data;
+ if (!phy->dpll_map) {
+ dev_err(&pdev->dev, "no DPLL data\n");
+ return -EINVAL;
+ }
- phy->dev = &pdev->dev;
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "pll_ctrl");
+ phy->pll_ctrl_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(phy->pll_ctrl_base))
+ return PTR_ERR(phy->pll_ctrl_base);
- if (!of_device_is_compatible(node, "ti,phy-pipe3-sata")) {
+ phy->sys_clk = devm_clk_get(phy->dev, "sysclk");
+ if (IS_ERR(phy->sys_clk)) {
+ dev_err(&pdev->dev, "unable to get sysclk\n");
+ return -EINVAL;
+ }
+ }
+ if (!of_device_is_compatible(node, "ti,phy-pipe3-sata")) {
phy->wkupclk = devm_clk_get(phy->dev, "wkupclk");
if (IS_ERR(phy->wkupclk)) {
dev_err(&pdev->dev, "unable to get wkupclk\n");
@@ -328,10 +347,38 @@ static int ti_pipe3_probe(struct platform_device *pdev)
phy->refclk = ERR_PTR(-ENODEV);
}
- phy->sys_clk = devm_clk_get(phy->dev, "sysclk");
- if (IS_ERR(phy->sys_clk)) {
- dev_err(&pdev->dev, "unable to get sysclk\n");
- return -EINVAL;
+ if (of_device_is_compatible(node, "ti,phy-pipe3-pcie")) {
+ if (of_property_read_u8(node, "id", &phy->id) < 0)
+ phy->id = 1;
+
+ clk = devm_clk_get(phy->dev, "dpll_ref");
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "unable to get dpll ref clk\n");
+ return PTR_ERR(clk);
+ }
+ clk_set_rate(clk, 1500000000);
+
+ clk = devm_clk_get(phy->dev, "dpll_ref_m2");
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "unable to get dpll ref m2 clk\n");
+ return PTR_ERR(clk);
+ }
+ clk_set_rate(clk, 100000000);
+
+ clk = devm_clk_get(phy->dev, "phy-div");
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "unable to get phy-div clk\n");
+ return PTR_ERR(clk);
+ }
+ clk_set_rate(clk, 100000000);
+
+ phy->div_clk = devm_clk_get(phy->dev, "div-clk");
+ if (IS_ERR(phy->div_clk)) {
+ dev_err(&pdev->dev, "unable to get div-clk\n");
+ return PTR_ERR(phy->div_clk);
+ }
+ } else {
+ phy->div_clk = ERR_PTR(-ENODEV);
}
control_node = of_parse_phandle(node, "ctrl-module", 0);
@@ -353,7 +400,7 @@ static int ti_pipe3_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, phy);
pm_runtime_enable(phy->dev);
- generic_phy = devm_phy_create(phy->dev, &ops, NULL);
+ generic_phy = devm_phy_create(phy->dev, NULL, &ops, NULL);
if (IS_ERR(generic_phy))
return PTR_ERR(generic_phy);
@@ -387,6 +434,8 @@ static int ti_pipe3_runtime_suspend(struct device *dev)
clk_disable_unprepare(phy->wkupclk);
if (!IS_ERR(phy->refclk))
clk_disable_unprepare(phy->refclk);
+ if (!IS_ERR(phy->div_clk))
+ clk_disable_unprepare(phy->div_clk);
return 0;
}
@@ -412,8 +461,19 @@ static int ti_pipe3_runtime_resume(struct device *dev)
}
}
+ if (!IS_ERR(phy->div_clk)) {
+ ret = clk_prepare_enable(phy->div_clk);
+ if (ret) {
+ dev_err(phy->dev, "Failed to enable div_clk %d\n", ret);
+ goto err3;
+ }
+ }
return 0;
+err3:
+ if (!IS_ERR(phy->wkupclk))
+ clk_disable_unprepare(phy->wkupclk);
+
err2:
if (!IS_ERR(phy->refclk))
clk_disable_unprepare(phy->refclk);
@@ -446,6 +506,9 @@ static const struct of_device_id ti_pipe3_id_table[] = {
.compatible = "ti,phy-pipe3-sata",
.data = dpll_map_sata,
},
+ {
+ .compatible = "ti,phy-pipe3-pcie",
+ },
{}
};
MODULE_DEVICE_TABLE(of, ti_pipe3_id_table);
diff --git a/drivers/phy/phy-twl4030-usb.c b/drivers/phy/phy-twl4030-usb.c
index 2e0e9b3774c8..e1a6623d4696 100644
--- a/drivers/phy/phy-twl4030-usb.c
+++ b/drivers/phy/phy-twl4030-usb.c
@@ -695,7 +695,7 @@ static int twl4030_usb_probe(struct platform_device *pdev)
otg->set_host = twl4030_set_host;
otg->set_peripheral = twl4030_set_peripheral;
- phy = devm_phy_create(twl->dev, &ops, init_data);
+ phy = devm_phy_create(twl->dev, NULL, &ops, init_data);
if (IS_ERR(phy)) {
dev_dbg(&pdev->dev, "Failed to create PHY\n");
return PTR_ERR(phy);
diff --git a/drivers/phy/phy-xgene.c b/drivers/phy/phy-xgene.c
index 4aa1ccd1511f..db809b97219e 100644
--- a/drivers/phy/phy-xgene.c
+++ b/drivers/phy/phy-xgene.c
@@ -1707,7 +1707,7 @@ static int xgene_phy_probe(struct platform_device *pdev)
ctx->dev = &pdev->dev;
platform_set_drvdata(pdev, ctx);
- ctx->phy = devm_phy_create(ctx->dev, &xgene_phy_ops, NULL);
+ ctx->phy = devm_phy_create(ctx->dev, NULL, &xgene_phy_ops, NULL);
if (IS_ERR(ctx->phy)) {
dev_dbg(&pdev->dev, "Failed to create PHY\n");
rc = PTR_ERR(ctx->phy);
diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c
index 2e58f8dfd311..65444b02bd68 100644
--- a/drivers/usb/chipidea/ci_hdrc_imx.c
+++ b/drivers/usb/chipidea/ci_hdrc_imx.c
@@ -133,6 +133,9 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
data->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "fsl,usbphy", 0);
if (IS_ERR(data->phy)) {
ret = PTR_ERR(data->phy);
+ /* Return -EINVAL if no usbphy is available */
+ if (ret == -ENODEV)
+ ret = -EINVAL;
goto err_clk;
}
diff --git a/drivers/usb/chipidea/debug.c b/drivers/usb/chipidea/debug.c
index 7cccab6ff308..795d6538d630 100644
--- a/drivers/usb/chipidea/debug.c
+++ b/drivers/usb/chipidea/debug.c
@@ -208,7 +208,7 @@ static const struct file_operations ci_requests_fops = {
.release = single_release,
};
-int ci_otg_show(struct seq_file *s, void *unused)
+static int ci_otg_show(struct seq_file *s, void *unused)
{
struct ci_hdrc *ci = s->private;
struct otg_fsm *fsm;
@@ -331,7 +331,7 @@ static const struct file_operations ci_role_fops = {
.release = single_release,
};
-int ci_registers_show(struct seq_file *s, void *unused)
+static int ci_registers_show(struct seq_file *s, void *unused)
{
struct ci_hdrc *ci = s->private;
u32 tmp_reg;
diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c
index 103a6e9ee49d..ec978408a2ee 100644
--- a/drivers/usb/class/usbtmc.c
+++ b/drivers/usb/class/usbtmc.c
@@ -715,7 +715,7 @@ static int usbtmc_ioctl_clear(struct usbtmc_device_data *data)
u8 *buffer;
int rv;
int n;
- int actual;
+ int actual = 0;
int max_size;
dev = &data->intf->dev;
diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c
index 1ab4df1de2da..b2a540b43f97 100644
--- a/drivers/usb/core/config.c
+++ b/drivers/usb/core/config.c
@@ -199,6 +199,17 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
if (n == 0)
n = 9; /* 32 ms = 2^(9-1) uframes */
j = 16;
+
+ /*
+ * Adjust bInterval for quirked devices.
+ * This quirk fixes bIntervals reported in
+ * linear microframes.
+ */
+ if (to_usb_device(ddev)->quirks &
+ USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL) {
+ n = clamp(fls(d->bInterval), i, j);
+ i = j = n;
+ }
break;
default: /* USB_SPEED_FULL or _LOW */
/* For low-speed, 10 ms is the official minimum.
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index 257876ea03a1..0b59731c3021 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -1509,7 +1509,7 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb
u = (is_in ? URB_DIR_IN : URB_DIR_OUT);
if (uurb->flags & USBDEVFS_URB_ISO_ASAP)
u |= URB_ISO_ASAP;
- if (uurb->flags & USBDEVFS_URB_SHORT_NOT_OK)
+ if (uurb->flags & USBDEVFS_URB_SHORT_NOT_OK && is_in)
u |= URB_SHORT_NOT_OK;
if (uurb->flags & USBDEVFS_URB_NO_FSBR)
u |= URB_NO_FSBR;
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 4aeb10034de7..9bffd26cea05 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -417,10 +417,11 @@ static int usb_unbind_interface(struct device *dev)
*/
lpm_disable_error = usb_unlocked_disable_lpm(udev);
- /* Terminate all URBs for this interface unless the driver
- * supports "soft" unbinding.
+ /*
+ * Terminate all URBs for this interface unless the driver
+ * supports "soft" unbinding and the device is still present.
*/
- if (!driver->soft_unbind)
+ if (!driver->soft_unbind || udev->state == USB_STATE_NOTATTACHED)
usb_disable_interface(udev, intf, false);
driver->disconnect(intf);
diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c
index 82044b5d6113..efc953119ce2 100644
--- a/drivers/usb/core/hcd-pci.c
+++ b/drivers/usb/core/hcd-pci.c
@@ -380,6 +380,8 @@ void usb_hcd_pci_shutdown(struct pci_dev *dev)
if (test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) &&
hcd->driver->shutdown) {
hcd->driver->shutdown(hcd);
+ if (usb_hcd_is_primary_hcd(hcd) && hcd->irq > 0)
+ free_irq(hcd->irq, hcd);
pci_disable_device(dev);
}
}
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index bec31e2efb88..487abcfcccd8 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -855,8 +855,6 @@ static ssize_t authorized_default_show(struct device *dev,
struct usb_bus *usb_bus = rh_usb_dev->bus;
struct usb_hcd *usb_hcd;
- if (usb_bus == NULL) /* FIXME: not sure if this case is possible */
- return -ENODEV;
usb_hcd = bus_to_hcd(usb_bus);
return snprintf(buf, PAGE_SIZE, "%u\n", usb_hcd->authorized_default);
}
@@ -871,8 +869,6 @@ static ssize_t authorized_default_store(struct device *dev,
struct usb_bus *usb_bus = rh_usb_dev->bus;
struct usb_hcd *usb_hcd;
- if (usb_bus == NULL) /* FIXME: not sure if this case is possible */
- return -ENODEV;
usb_hcd = bus_to_hcd(usb_bus);
result = sscanf(buf, "%u\n", &val);
if (result == 1) {
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 0e950ad8cb25..8a4dcbc7a75f 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -2606,13 +2606,20 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
/* Is a USB 3.0 port in the Inactive or Compliance Mode state?
* Port worm reset is required to recover
*/
-static bool hub_port_warm_reset_required(struct usb_hub *hub, u16 portstatus)
+static bool hub_port_warm_reset_required(struct usb_hub *hub, int port1,
+ u16 portstatus)
{
- return hub_is_superspeed(hub->hdev) &&
- (((portstatus & USB_PORT_STAT_LINK_STATE) ==
- USB_SS_PORT_LS_SS_INACTIVE) ||
- ((portstatus & USB_PORT_STAT_LINK_STATE) ==
- USB_SS_PORT_LS_COMP_MOD)) ;
+ u16 link_state;
+
+ if (!hub_is_superspeed(hub->hdev))
+ return false;
+
+ if (test_bit(port1, hub->warm_reset_bits))
+ return true;
+
+ link_state = portstatus & USB_PORT_STAT_LINK_STATE;
+ return link_state == USB_SS_PORT_LS_SS_INACTIVE
+ || link_state == USB_SS_PORT_LS_COMP_MOD;
}
static int hub_port_wait_reset(struct usb_hub *hub, int port1,
@@ -2649,7 +2656,7 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
if ((portstatus & USB_PORT_STAT_RESET))
return -EBUSY;
- if (hub_port_warm_reset_required(hub, portstatus))
+ if (hub_port_warm_reset_required(hub, port1, portstatus))
return -ENOTCONN;
/* Device went away? */
@@ -2749,9 +2756,10 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
if (status < 0)
goto done;
- if (hub_port_warm_reset_required(hub, portstatus))
+ if (hub_port_warm_reset_required(hub, port1, portstatus))
warm = true;
}
+ clear_bit(port1, hub->warm_reset_bits);
/* Reset the port */
for (i = 0; i < PORT_RESET_TRIES; i++) {
@@ -2788,7 +2796,8 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
&portstatus, &portchange) < 0)
goto done;
- if (!hub_port_warm_reset_required(hub, portstatus))
+ if (!hub_port_warm_reset_required(hub, port1,
+ portstatus))
goto done;
/*
@@ -2875,8 +2884,13 @@ static int check_port_resume_type(struct usb_device *udev,
{
struct usb_port *port_dev = hub->ports[port1 - 1];
+ /* Is a warm reset needed to recover the connection? */
+ if (status == 0 && udev->reset_resume
+ && hub_port_warm_reset_required(hub, port1, portstatus)) {
+ /* pass */;
+ }
/* Is the device still present? */
- if (status || port_is_suspended(hub, portstatus) ||
+ else if (status || port_is_suspended(hub, portstatus) ||
!port_is_power_on(hub, portstatus) ||
!(portstatus & USB_PORT_STAT_CONNECTION)) {
if (status >= 0)
@@ -3264,6 +3278,43 @@ static int finish_port_resume(struct usb_device *udev)
}
/*
+ * There are some SS USB devices which take longer time for link training.
+ * XHCI specs 4.19.4 says that when Link training is successful, port
+ * sets CSC bit to 1. So if SW reads port status before successful link
+ * training, then it will not find device to be present.
+ * USB Analyzer log with such buggy devices show that in some cases
+ * device switch on the RX termination after long delay of host enabling
+ * the VBUS. In few other cases it has been seen that device fails to
+ * negotiate link training in first attempt. It has been
+ * reported till now that few devices take as long as 2000 ms to train
+ * the link after host enabling its VBUS and termination. Following
+ * routine implements a 2000 ms timeout for link training. If in a case
+ * link trains before timeout, loop will exit earlier.
+ *
+ * FIXME: If a device was connected before suspend, but was removed
+ * while system was asleep, then the loop in the following routine will
+ * only exit at timeout.
+ *
+ * This routine should only be called when persist is enabled for a SS
+ * device.
+ */
+static int wait_for_ss_port_enable(struct usb_device *udev,
+ struct usb_hub *hub, int *port1,
+ u16 *portchange, u16 *portstatus)
+{
+ int status = 0, delay_ms = 0;
+
+ while (delay_ms < 2000) {
+ if (status || *portstatus & USB_PORT_STAT_CONNECTION)
+ break;
+ msleep(20);
+ delay_ms += 20;
+ status = hub_port_status(hub, *port1, portstatus, portchange);
+ }
+ return status;
+}
+
+/*
* usb_port_resume - re-activate a suspended usb device's upstream port
* @udev: device to re-activate, not a root hub
* Context: must be able to sleep; device not locked; pm locks held
@@ -3359,6 +3410,10 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
}
}
+ if (udev->persist_enabled && hub_is_superspeed(hub->hdev))
+ status = wait_for_ss_port_enable(udev, hub, &port1, &portchange,
+ &portstatus);
+
status = check_port_resume_type(udev,
hub, port1, status, portchange, portstatus);
if (status == 0)
@@ -3879,7 +3934,8 @@ int usb_disable_lpm(struct usb_device *udev)
if (!udev || !udev->parent ||
udev->speed != USB_SPEED_SUPER ||
- !udev->lpm_capable)
+ !udev->lpm_capable ||
+ udev->state < USB_STATE_DEFAULT)
return 0;
hcd = bus_to_hcd(udev->bus);
@@ -3935,7 +3991,8 @@ void usb_enable_lpm(struct usb_device *udev)
if (!udev || !udev->parent ||
udev->speed != USB_SPEED_SUPER ||
- !udev->lpm_capable)
+ !udev->lpm_capable ||
+ udev->state < USB_STATE_DEFAULT)
return;
udev->lpm_disable_count--;
@@ -4550,6 +4607,7 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
struct usb_port *port_dev = hub->ports[port1 - 1];
struct usb_device *udev = port_dev->child;
+ static int unreliable_port = -1;
/* Disconnect any existing devices under this port */
if (udev) {
@@ -4570,10 +4628,14 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
USB_PORT_STAT_C_ENABLE)) {
status = hub_port_debounce_be_stable(hub, port1);
if (status < 0) {
- if (status != -ENODEV && printk_ratelimit())
- dev_err(&port_dev->dev,
- "connect-debounce failed\n");
+ if (status != -ENODEV &&
+ port1 != unreliable_port &&
+ printk_ratelimit())
+ dev_err(&udev->dev, "connect-debounce failed, port %d disabled\n",
+ port1);
+
portstatus &= ~USB_PORT_STAT_CONNECTION;
+ unreliable_port = port1;
} else {
portstatus = status;
}
@@ -4889,7 +4951,7 @@ static void port_event(struct usb_hub *hub, int port1)
* Warm reset a USB3 protocol port if it's in
* SS.Inactive state.
*/
- if (hub_port_warm_reset_required(hub, portstatus)) {
+ if (hub_port_warm_reset_required(hub, port1, portstatus)) {
dev_dbg(&port_dev->dev, "do warm reset\n");
if (!udev || !(portstatus & USB_PORT_STAT_CONNECTION)
|| udev->state == USB_STATE_NOTATTACHED) {
diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h
index 326308e53961..c77d8778af4b 100644
--- a/drivers/usb/core/hub.h
+++ b/drivers/usb/core/hub.h
@@ -52,6 +52,8 @@ struct usb_hub {
unsigned long power_bits[1]; /* ports that are powered */
unsigned long child_usage_bits[1]; /* ports powered on for
children */
+ unsigned long warm_reset_bits[1]; /* ports requesting warm
+ reset recovery */
#if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */
#error event_bits[] is too short!
#endif
diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c
index fe1b6d0967e3..cd3f9dc24a06 100644
--- a/drivers/usb/core/port.c
+++ b/drivers/usb/core/port.c
@@ -103,16 +103,19 @@ static int usb_port_runtime_resume(struct device *dev)
msleep(hub_power_on_good_delay(hub));
if (udev && !retval) {
/*
- * Attempt to wait for usb hub port to be reconnected in order
- * to make the resume procedure successful. The device may have
- * disconnected while the port was powered off, so ignore the
- * return status.
+ * Our preference is to simply wait for the port to reconnect,
+ * as that is the lowest latency method to restart the port.
+ * However, there are cases where toggling port power results in
+ * the host port and the device port getting out of sync causing
+ * a link training live lock. Upon timeout, flag the port as
+ * needing warm reset recovery (to be performed later by
+ * usb_port_resume() as requested via usb_wakeup_notification())
*/
- retval = hub_port_debounce_be_connected(hub, port1);
- if (retval < 0)
- dev_dbg(&port_dev->dev, "can't get reconnection after setting port power on, status %d\n",
- retval);
- retval = 0;
+ if (hub_port_debounce_be_connected(hub, port1) < 0) {
+ dev_dbg(&port_dev->dev, "reconnect timeout\n");
+ if (hub_is_superspeed(hdev))
+ set_bit(port1, hub->warm_reset_bits);
+ }
/* Force the child awake to revalidate after the power loss. */
if (!test_and_set_bit(port1, hub->child_usage_bits)) {
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index 739ee8e8bdfd..bae636e2a1a3 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -145,6 +145,10 @@ static const struct usb_device_id usb_quirk_list[] = {
/* SKYMEDI USB_DRIVE */
{ USB_DEVICE(0x1516, 0x8628), .driver_info = USB_QUIRK_RESET_RESUME },
+ /* Razer - Razer Blade Keyboard */
+ { USB_DEVICE(0x1532, 0x0116), .driver_info =
+ USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL },
+
/* BUILDWIN Photo Frame */
{ USB_DEVICE(0x1908, 0x1315), .driver_info =
USB_QUIRK_HONOR_BNUMINTERFACES },
@@ -152,6 +156,9 @@ static const struct usb_device_id usb_quirk_list[] = {
/* INTEL VALUE SSD */
{ USB_DEVICE(0x8086, 0xf1a5), .driver_info = USB_QUIRK_RESET_RESUME },
+ /* USB3503 */
+ { USB_DEVICE(0x0424, 0x3503), .driver_info = USB_QUIRK_RESET_RESUME },
+
{ } /* terminating entry must be last */
};
diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c
index 991386ceb4ec..c9e8ee81b6b7 100644
--- a/drivers/usb/core/urb.c
+++ b/drivers/usb/core/urb.c
@@ -454,6 +454,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
URB_FREE_BUFFER);
switch (xfertype) {
case USB_ENDPOINT_XFER_BULK:
+ case USB_ENDPOINT_XFER_INT:
if (is_out)
allowed |= URB_ZERO_PACKET;
/* FALLTHROUGH */
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 4d1144990d4c..2dd2362198d2 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -501,6 +501,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
}
return dev;
}
+EXPORT_SYMBOL_GPL(usb_alloc_dev);
/**
* usb_get_dev - increments the reference count of the usb device structure
diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c
index f3c56a2fed5b..0ba9c335b584 100644
--- a/drivers/usb/dwc2/gadget.c
+++ b/drivers/usb/dwc2/gadget.c
@@ -1,6 +1,4 @@
/**
- * linux/drivers/usb/gadget/s3c-hsotg.c
- *
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
@@ -1022,7 +1020,8 @@ static void s3c_hsotg_disconnect(struct s3c_hsotg *hsotg);
*
* Set stall for ep0 as response for setup request.
*/
-static void s3c_hsotg_stall_ep0(struct s3c_hsotg *hsotg) {
+static void s3c_hsotg_stall_ep0(struct s3c_hsotg *hsotg)
+{
struct s3c_hsotg_ep *ep0 = &hsotg->eps[0];
u32 reg;
u32 ctrl;
@@ -1994,7 +1993,7 @@ static void kill_all_requests(struct s3c_hsotg *hsotg,
s3c_hsotg_complete_request(hsotg, ep, req,
result);
}
- if(hsotg->dedicated_fifos)
+ if (hsotg->dedicated_fifos)
if ((readl(hsotg->regs + DTXFSTS(ep->index)) & 0xffff) * 4 < 3072)
s3c_hsotg_txfifo_flush(hsotg, ep->index);
}
@@ -3390,10 +3389,8 @@ static int s3c_hsotg_probe(struct platform_device *pdev)
int i;
hsotg = devm_kzalloc(&pdev->dev, sizeof(struct s3c_hsotg), GFP_KERNEL);
- if (!hsotg) {
- dev_err(dev, "cannot get memory\n");
+ if (!hsotg)
return -ENOMEM;
- }
/*
* Attempt to find a generic PHY, then look for an old style
@@ -3512,7 +3509,6 @@ static int s3c_hsotg_probe(struct platform_device *pdev)
eps = kcalloc(hsotg->num_of_eps + 1, sizeof(struct s3c_hsotg_ep),
GFP_KERNEL);
if (!eps) {
- dev_err(dev, "cannot get memory\n");
ret = -ENOMEM;
goto err_supplies;
}
diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig
index 261c3b428220..785510a0a0c3 100644
--- a/drivers/usb/dwc3/Kconfig
+++ b/drivers/usb/dwc3/Kconfig
@@ -93,4 +93,11 @@ config USB_DWC3_VERBOSE
help
Say Y here to enable verbose debugging messages on DWC3 Driver.
+config DWC3_HOST_USB3_LPM_ENABLE
+ bool "Enable USB3 LPM Capability"
+ depends on USB_DWC3_HOST=y || USB_DWC3_DUAL_ROLE=y
+ default n
+ help
+ Select this when you want to enable USB3 LPM with dwc3 xhci host.
+
endif
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index eb69eb9f06c8..b769c1faaf03 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -386,6 +386,13 @@ static int dwc3_core_init(struct dwc3 *dwc)
}
dwc->revision = reg;
+ /* Handle USB2.0-only core configuration */
+ if (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) ==
+ DWC3_GHWPARAMS3_SSPHY_IFC_DIS) {
+ if (dwc->maximum_speed == USB_SPEED_SUPER)
+ dwc->maximum_speed = USB_SPEED_HIGH;
+ }
+
/* issue device SoftReset too */
timeout = jiffies + msecs_to_jiffies(500);
dwc3_writel(dwc->regs, DWC3_DCTL, DWC3_DCTL_CSFTRST);
@@ -656,6 +663,31 @@ static int dwc3_probe(struct platform_device *pdev)
return -ENODEV;
}
+ dwc->xhci_resources[0].start = res->start;
+ dwc->xhci_resources[0].end = dwc->xhci_resources[0].start +
+ DWC3_XHCI_REGS_END;
+ dwc->xhci_resources[0].flags = res->flags;
+ dwc->xhci_resources[0].name = res->name;
+
+ res->start += DWC3_GLOBALS_REGS_START;
+
+ /*
+ * Request memory region but exclude xHCI regs,
+ * since it will be requested by the xhci-plat driver.
+ */
+ regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ dwc->regs = regs;
+ dwc->regs_size = resource_size(res);
+ /*
+ * restore res->start back to its original value so that,
+ * in case the probe is deferred, we don't end up getting error in
+ * request the memory region the next time probe is called.
+ */
+ res->start -= DWC3_GLOBALS_REGS_START;
+
if (node) {
dwc->maximum_speed = of_usb_get_maximum_speed(node);
@@ -676,28 +708,9 @@ static int dwc3_probe(struct platform_device *pdev)
if (ret)
return ret;
- dwc->xhci_resources[0].start = res->start;
- dwc->xhci_resources[0].end = dwc->xhci_resources[0].start +
- DWC3_XHCI_REGS_END;
- dwc->xhci_resources[0].flags = res->flags;
- dwc->xhci_resources[0].name = res->name;
-
- res->start += DWC3_GLOBALS_REGS_START;
-
- /*
- * Request memory region but exclude xHCI regs,
- * since it will be requested by the xhci-plat driver.
- */
- regs = devm_ioremap_resource(dev, res);
- if (IS_ERR(regs))
- return PTR_ERR(regs);
-
spin_lock_init(&dwc->lock);
platform_set_drvdata(pdev, dwc);
- dwc->regs = regs;
- dwc->regs_size = resource_size(res);
-
dev->dma_mask = dev->parent->dma_mask;
dev->dma_parms = dev->parent->dma_parms;
dma_set_coherent_mask(dev, dev->parent->coherent_dma_mask);
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 57332e3768e4..48fb264065db 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -191,6 +191,19 @@
#define DWC3_GHWPARAMS1_PWROPT(n) ((n) << 24)
#define DWC3_GHWPARAMS1_PWROPT_MASK DWC3_GHWPARAMS1_PWROPT(3)
+/* Global HWPARAMS3 Register */
+#define DWC3_GHWPARAMS3_SSPHY_IFC(n) ((n) & 3)
+#define DWC3_GHWPARAMS3_SSPHY_IFC_DIS 0
+#define DWC3_GHWPARAMS3_SSPHY_IFC_ENA 1
+#define DWC3_GHWPARAMS3_HSPHY_IFC(n) (((n) & (3 << 2)) >> 2)
+#define DWC3_GHWPARAMS3_HSPHY_IFC_DIS 0
+#define DWC3_GHWPARAMS3_HSPHY_IFC_UTMI 1
+#define DWC3_GHWPARAMS3_HSPHY_IFC_ULPI 2
+#define DWC3_GHWPARAMS3_HSPHY_IFC_UTMI_ULPI 3
+#define DWC3_GHWPARAMS3_FSPHY_IFC(n) (((n) & (3 << 4)) >> 4)
+#define DWC3_GHWPARAMS3_FSPHY_IFC_DIS 0
+#define DWC3_GHWPARAMS3_FSPHY_IFC_ENA 1
+
/* Global HWPARAMS4 Register */
#define DWC3_GHWPARAMS4_HIBER_SCRATCHBUFS(n) (((n) & (0x0f << 13)) >> 13)
#define DWC3_MAX_HIBER_SCRATCHBUFS 15
diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c
index 07a736acd0f2..ef4936ff626c 100644
--- a/drivers/usb/dwc3/dwc3-omap.c
+++ b/drivers/usb/dwc3/dwc3-omap.c
@@ -77,10 +77,6 @@
#define USBOTGSS_DEV_EBC_EN 0x0110
#define USBOTGSS_DEBUG_OFFSET 0x0600
-/* REVISION REGISTER */
-#define USBOTGSS_REVISION_XMAJOR(reg) ((reg >> 8) & 0x7)
-#define USBOTGSS_REVISION_XMAJOR1 1
-#define USBOTGSS_REVISION_XMAJOR2 2
/* SYSCONFIG REGISTER */
#define USBOTGSS_SYSCONFIG_DMADISABLE (1 << 16)
@@ -129,7 +125,6 @@ struct dwc3_omap {
u32 irq_eoi_offset;
u32 debug_offset;
u32 irq0_offset;
- u32 revision;
u32 dma_status:1;
@@ -383,6 +378,87 @@ static int dwc3_omap_vbus_notifier(struct notifier_block *nb,
return NOTIFY_DONE;
}
+static void dwc3_omap_map_offset(struct dwc3_omap *omap)
+{
+ struct device_node *node = omap->dev->of_node;
+
+ /*
+ * Differentiate between OMAP5 and AM437x.
+ *
+ * For OMAP5(ES2.0) and AM437x wrapper revision is same, even
+ * though there are changes in wrapper register offsets.
+ *
+ * Using dt compatible to differentiate AM437x.
+ */
+ if (of_device_is_compatible(node, "ti,am437x-dwc3")) {
+ omap->irq_eoi_offset = USBOTGSS_EOI_OFFSET;
+ omap->irq0_offset = USBOTGSS_IRQ0_OFFSET;
+ omap->irqmisc_offset = USBOTGSS_IRQMISC_OFFSET;
+ omap->utmi_otg_offset = USBOTGSS_UTMI_OTG_OFFSET;
+ omap->debug_offset = USBOTGSS_DEBUG_OFFSET;
+ }
+}
+
+static void dwc3_omap_set_utmi_mode(struct dwc3_omap *omap)
+{
+ u32 reg;
+ struct device_node *node = omap->dev->of_node;
+ int utmi_mode = 0;
+
+ reg = dwc3_omap_read_utmi_status(omap);
+
+ of_property_read_u32(node, "utmi-mode", &utmi_mode);
+
+ switch (utmi_mode) {
+ case DWC3_OMAP_UTMI_MODE_SW:
+ reg |= USBOTGSS_UTMI_OTG_STATUS_SW_MODE;
+ break;
+ case DWC3_OMAP_UTMI_MODE_HW:
+ reg &= ~USBOTGSS_UTMI_OTG_STATUS_SW_MODE;
+ break;
+ default:
+ dev_dbg(omap->dev, "UNKNOWN utmi mode %d\n", utmi_mode);
+ }
+
+ dwc3_omap_write_utmi_status(omap, reg);
+}
+
+static int dwc3_omap_extcon_register(struct dwc3_omap *omap)
+{
+ u32 ret;
+ struct device_node *node = omap->dev->of_node;
+ struct extcon_dev *edev;
+
+ if (of_property_read_bool(node, "extcon")) {
+ edev = extcon_get_edev_by_phandle(omap->dev, 0);
+ if (IS_ERR(edev)) {
+ dev_vdbg(omap->dev, "couldn't get extcon device\n");
+ return -EPROBE_DEFER;
+ }
+
+ omap->vbus_nb.notifier_call = dwc3_omap_vbus_notifier;
+ ret = extcon_register_interest(&omap->extcon_vbus_dev,
+ edev->name, "USB",
+ &omap->vbus_nb);
+ if (ret < 0)
+ dev_vdbg(omap->dev, "failed to register notifier for USB\n");
+
+ omap->id_nb.notifier_call = dwc3_omap_id_notifier;
+ ret = extcon_register_interest(&omap->extcon_id_dev,
+ edev->name, "USB-HOST",
+ &omap->id_nb);
+ if (ret < 0)
+ dev_vdbg(omap->dev, "failed to register notifier for USB-HOST\n");
+
+ if (extcon_get_cable_state(edev, "USB") == true)
+ dwc3_omap_set_mailbox(omap, OMAP_DWC3_VBUS_VALID);
+ if (extcon_get_cable_state(edev, "USB-HOST") == true)
+ dwc3_omap_set_mailbox(omap, OMAP_DWC3_ID_GROUND);
+ }
+
+ return 0;
+}
+
static int dwc3_omap_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
@@ -390,15 +466,11 @@ static int dwc3_omap_probe(struct platform_device *pdev)
struct dwc3_omap *omap;
struct resource *res;
struct device *dev = &pdev->dev;
- struct extcon_dev *edev;
struct regulator *vbus_reg = NULL;
int ret;
int irq;
- int utmi_mode = 0;
- int x_major;
-
u32 reg;
void __iomem *base;
@@ -448,58 +520,8 @@ static int dwc3_omap_probe(struct platform_device *pdev)
goto err0;
}
- reg = dwc3_omap_readl(omap->base, USBOTGSS_REVISION);
- omap->revision = reg;
- x_major = USBOTGSS_REVISION_XMAJOR(reg);
-
- /* Differentiate between OMAP5 and AM437x */
- switch (x_major) {
- case USBOTGSS_REVISION_XMAJOR1:
- case USBOTGSS_REVISION_XMAJOR2:
- omap->irq_eoi_offset = 0;
- omap->irq0_offset = 0;
- omap->irqmisc_offset = 0;
- omap->utmi_otg_offset = 0;
- omap->debug_offset = 0;
- break;
- default:
- /* Default to the latest revision */
- omap->irq_eoi_offset = USBOTGSS_EOI_OFFSET;
- omap->irq0_offset = USBOTGSS_IRQ0_OFFSET;
- omap->irqmisc_offset = USBOTGSS_IRQMISC_OFFSET;
- omap->utmi_otg_offset = USBOTGSS_UTMI_OTG_OFFSET;
- omap->debug_offset = USBOTGSS_DEBUG_OFFSET;
- break;
- }
-
- /* For OMAP5(ES2.0) and AM437x x_major is 2 even though there are
- * changes in wrapper registers, Using dt compatible for aegis
- */
-
- if (of_device_is_compatible(node, "ti,am437x-dwc3")) {
- omap->irq_eoi_offset = USBOTGSS_EOI_OFFSET;
- omap->irq0_offset = USBOTGSS_IRQ0_OFFSET;
- omap->irqmisc_offset = USBOTGSS_IRQMISC_OFFSET;
- omap->utmi_otg_offset = USBOTGSS_UTMI_OTG_OFFSET;
- omap->debug_offset = USBOTGSS_DEBUG_OFFSET;
- }
-
- reg = dwc3_omap_read_utmi_status(omap);
-
- of_property_read_u32(node, "utmi-mode", &utmi_mode);
-
- switch (utmi_mode) {
- case DWC3_OMAP_UTMI_MODE_SW:
- reg |= USBOTGSS_UTMI_OTG_STATUS_SW_MODE;
- break;
- case DWC3_OMAP_UTMI_MODE_HW:
- reg &= ~USBOTGSS_UTMI_OTG_STATUS_SW_MODE;
- break;
- default:
- dev_dbg(dev, "UNKNOWN utmi mode %d\n", utmi_mode);
- }
-
- dwc3_omap_write_utmi_status(omap, reg);
+ dwc3_omap_map_offset(omap);
+ dwc3_omap_set_utmi_mode(omap);
/* check the DMA Status */
reg = dwc3_omap_readl(omap->base, USBOTGSS_SYSCONFIG);
@@ -515,31 +537,9 @@ static int dwc3_omap_probe(struct platform_device *pdev)
dwc3_omap_enable_irqs(omap);
- if (of_property_read_bool(node, "extcon")) {
- edev = extcon_get_edev_by_phandle(dev, 0);
- if (IS_ERR(edev)) {
- dev_vdbg(dev, "couldn't get extcon device\n");
- ret = -EPROBE_DEFER;
- goto err2;
- }
-
- omap->vbus_nb.notifier_call = dwc3_omap_vbus_notifier;
- ret = extcon_register_interest(&omap->extcon_vbus_dev,
- edev->name, "USB", &omap->vbus_nb);
- if (ret < 0)
- dev_vdbg(dev, "failed to register notifier for USB\n");
- omap->id_nb.notifier_call = dwc3_omap_id_notifier;
- ret = extcon_register_interest(&omap->extcon_id_dev, edev->name,
- "USB-HOST", &omap->id_nb);
- if (ret < 0)
- dev_vdbg(dev,
- "failed to register notifier for USB-HOST\n");
-
- if (extcon_get_cable_state(edev, "USB") == true)
- dwc3_omap_set_mailbox(omap, OMAP_DWC3_VBUS_VALID);
- if (extcon_get_cable_state(edev, "USB-HOST") == true)
- dwc3_omap_set_mailbox(omap, OMAP_DWC3_ID_GROUND);
- }
+ ret = dwc3_omap_extcon_register(omap);
+ if (ret < 0)
+ goto err2;
ret = of_platform_populate(node, NULL, NULL, dev);
if (ret) {
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index dab7927d1009..349cacc577d8 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -1971,8 +1971,7 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
}
static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc,
- struct dwc3_ep *dep, const struct dwc3_event_depevt *event,
- int start_new)
+ struct dwc3_ep *dep, const struct dwc3_event_depevt *event)
{
unsigned status = 0;
int clean_busy;
@@ -2039,7 +2038,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
return;
}
- dwc3_endpoint_transfer_complete(dwc, dep, event, 1);
+ dwc3_endpoint_transfer_complete(dwc, dep, event);
break;
case DWC3_DEPEVT_XFERINPROGRESS:
if (!usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
@@ -2048,7 +2047,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
return;
}
- dwc3_endpoint_transfer_complete(dwc, dep, event, 0);
+ dwc3_endpoint_transfer_complete(dwc, dep, event);
break;
case DWC3_DEPEVT_XFERNOTREADY:
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c
index 32db328cc769..dcb8ca084598 100644
--- a/drivers/usb/dwc3/host.c
+++ b/drivers/usb/dwc3/host.c
@@ -16,12 +16,14 @@
*/
#include <linux/platform_device.h>
+#include <linux/usb/xhci_pdriver.h>
#include "core.h"
int dwc3_host_init(struct dwc3 *dwc)
{
struct platform_device *xhci;
+ struct usb_xhci_pdata pdata;
int ret;
xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO);
@@ -46,6 +48,18 @@ int dwc3_host_init(struct dwc3 *dwc)
goto err1;
}
+ memset(&pdata, 0, sizeof(pdata));
+
+#ifdef CONFIG_DWC3_HOST_USB3_LPM_ENABLE
+ pdata.usb3_lpm_capable = 1;
+#endif
+
+ ret = platform_device_add_data(xhci, &pdata, sizeof(pdata));
+ if (ret) {
+ dev_err(dwc->dev, "couldn't add platform data to xHCI device\n");
+ goto err1;
+ }
+
ret = platform_device_add(xhci);
if (ret) {
dev_err(dwc->dev, "failed to register xHCI device\n");
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index ba18e9c110cc..5c822afb6d70 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -127,368 +127,7 @@ config USB_GADGET_STORAGE_NUM_BUFFERS
a module parameter as well.
If unsure, say 2.
-#
-# USB Peripheral Controller Support
-#
-# The order here is alphabetical, except that integrated controllers go
-# before discrete ones so they will be the initial/default value:
-# - integrated/SOC controllers first
-# - licensed IP used in both SOC and discrete versions
-# - discrete ones (including all PCI-only controllers)
-# - debug/dummy gadget+hcd is last.
-#
-menu "USB Peripheral Controller"
-
-#
-# Integrated controllers
-#
-
-config USB_AT91
- tristate "Atmel AT91 USB Device Port"
- depends on ARCH_AT91
- help
- Many Atmel AT91 processors (such as the AT91RM2000) have a
- full speed USB Device Port with support for five configurable
- endpoints (plus endpoint zero).
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "at91_udc" and force all
- gadget drivers to also be dynamically linked.
-
-config USB_LPC32XX
- tristate "LPC32XX USB Peripheral Controller"
- depends on ARCH_LPC32XX && I2C
- select USB_ISP1301
- help
- This option selects the USB device controller in the LPC32xx SoC.
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "lpc32xx_udc" and force all
- gadget drivers to also be dynamically linked.
-
-config USB_ATMEL_USBA
- tristate "Atmel USBA"
- depends on AVR32 || ARCH_AT91
- help
- USBA is the integrated high-speed USB Device controller on
- the AT32AP700x, some AT91SAM9 and AT91CAP9 processors from Atmel.
-
-config USB_BCM63XX_UDC
- tristate "Broadcom BCM63xx Peripheral Controller"
- depends on BCM63XX
- help
- Many Broadcom BCM63xx chipsets (such as the BCM6328) have a
- high speed USB Device Port with support for four fixed endpoints
- (plus endpoint zero).
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "bcm63xx_udc".
-
-config USB_FSL_USB2
- tristate "Freescale Highspeed USB DR Peripheral Controller"
- depends on FSL_SOC || ARCH_MXC
- select USB_FSL_MPH_DR_OF if OF
- help
- Some of Freescale PowerPC and i.MX processors have a High Speed
- Dual-Role(DR) USB controller, which supports device mode.
-
- The number of programmable endpoints is different through
- SOC revisions.
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "fsl_usb2_udc" and force
- all gadget drivers to also be dynamically linked.
-
-config USB_FUSB300
- tristate "Faraday FUSB300 USB Peripheral Controller"
- depends on !PHYS_ADDR_T_64BIT && HAS_DMA
- help
- Faraday usb device controller FUSB300 driver
-
-config USB_FOTG210_UDC
- depends on HAS_DMA
- tristate "Faraday FOTG210 USB Peripheral Controller"
- help
- Faraday USB2.0 OTG controller which can be configured as
- high speed or full speed USB device. This driver supppors
- Bulk Transfer so far.
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "fotg210_udc".
-
-config USB_GR_UDC
- tristate "Aeroflex Gaisler GRUSBDC USB Peripheral Controller Driver"
- depends on HAS_DMA
- help
- Select this to support Aeroflex Gaisler GRUSBDC cores from the GRLIB
- VHDL IP core library.
-
-config USB_OMAP
- tristate "OMAP USB Device Controller"
- depends on ARCH_OMAP1
- depends on ISP1301_OMAP || !(MACH_OMAP_H2 || MACH_OMAP_H3)
- help
- Many Texas Instruments OMAP processors have flexible full
- speed USB device controllers, with support for up to 30
- endpoints (plus endpoint zero). This driver supports the
- controller in the OMAP 1611, and should work with controllers
- in other OMAP processors too, given minor tweaks.
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "omap_udc" and force all
- gadget drivers to also be dynamically linked.
-
-config USB_PXA25X
- tristate "PXA 25x or IXP 4xx"
- depends on (ARCH_PXA && PXA25x) || ARCH_IXP4XX
- help
- Intel's PXA 25x series XScale ARM-5TE processors include
- an integrated full speed USB 1.1 device controller. The
- controller in the IXP 4xx series is register-compatible.
-
- It has fifteen fixed-function endpoints, as well as endpoint
- zero (for control transfers).
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "pxa25x_udc" and force all
- gadget drivers to also be dynamically linked.
-
-# if there's only one gadget driver, using only two bulk endpoints,
-# don't waste memory for the other endpoints
-config USB_PXA25X_SMALL
- depends on USB_PXA25X
- bool
- default n if USB_ETH_RNDIS
- default y if USB_ZERO
- default y if USB_ETH
- default y if USB_G_SERIAL
-
-config USB_R8A66597
- tristate "Renesas R8A66597 USB Peripheral Controller"
- depends on HAS_DMA
- help
- R8A66597 is a discrete USB host and peripheral controller chip that
- supports both full and high speed USB 2.0 data transfers.
- It has nine configurable endpoints, and endpoint zero.
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "r8a66597_udc" and force all
- gadget drivers to also be dynamically linked.
-
-config USB_RENESAS_USBHS_UDC
- tristate 'Renesas USBHS controller'
- depends on USB_RENESAS_USBHS
- help
- Renesas USBHS is a discrete USB host and peripheral controller chip
- that supports both full and high speed USB 2.0 data transfers.
- It has nine or more configurable endpoints, and endpoint zero.
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "renesas_usbhs" and force all
- gadget drivers to also be dynamically linked.
-
-config USB_PXA27X
- tristate "PXA 27x"
- help
- Intel's PXA 27x series XScale ARM v5TE processors include
- an integrated full speed USB 1.1 device controller.
-
- It has up to 23 endpoints, as well as endpoint zero (for
- control transfers).
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "pxa27x_udc" and force all
- gadget drivers to also be dynamically linked.
-
-config USB_S3C2410
- tristate "S3C2410 USB Device Controller"
- depends on ARCH_S3C24XX
- help
- Samsung's S3C2410 is an ARM-4 processor with an integrated
- full speed USB 1.1 device controller. It has 4 configurable
- endpoints, as well as endpoint zero (for control transfers).
-
- This driver has been tested on the S3C2410, S3C2412, and
- S3C2440 processors.
-
-config USB_S3C2410_DEBUG
- boolean "S3C2410 udc debug messages"
- depends on USB_S3C2410
-
-config USB_S3C_HSUDC
- tristate "S3C2416, S3C2443 and S3C2450 USB Device Controller"
- depends on ARCH_S3C24XX
- help
- Samsung's S3C2416, S3C2443 and S3C2450 is an ARM9 based SoC
- integrated with dual speed USB 2.0 device controller. It has
- 8 endpoints, as well as endpoint zero.
-
- This driver has been tested on S3C2416 and S3C2450 processors.
-
-config USB_MV_UDC
- tristate "Marvell USB2.0 Device Controller"
- depends on HAS_DMA
- help
- Marvell Socs (including PXA and MMP series) include a high speed
- USB2.0 OTG controller, which can be configured as high speed or
- full speed USB peripheral.
-
-config USB_MV_U3D
- depends on HAS_DMA
- tristate "MARVELL PXA2128 USB 3.0 controller"
- help
- MARVELL PXA2128 Processor series include a super speed USB3.0 device
- controller, which support super speed USB peripheral.
-
-#
-# Controllers available in both integrated and discrete versions
-#
-
-config USB_M66592
- tristate "Renesas M66592 USB Peripheral Controller"
- help
- M66592 is a discrete USB peripheral controller chip that
- supports both full and high speed USB 2.0 data transfers.
- It has seven configurable endpoints, and endpoint zero.
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "m66592_udc" and force all
- gadget drivers to also be dynamically linked.
-
-#
-# Controllers available only in discrete form (and all PCI controllers)
-#
-
-config USB_AMD5536UDC
- tristate "AMD5536 UDC"
- depends on PCI
- help
- The AMD5536 UDC is part of the AMD Geode CS5536, an x86 southbridge.
- It is a USB Highspeed DMA capable USB device controller. Beside ep0
- it provides 4 IN and 4 OUT endpoints (bulk or interrupt type).
- The UDC port supports OTG operation, and may be used as a host port
- if it's not being used to implement peripheral or OTG roles.
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "amd5536udc" and force all
- gadget drivers to also be dynamically linked.
-
-config USB_FSL_QE
- tristate "Freescale QE/CPM USB Device Controller"
- depends on FSL_SOC && (QUICC_ENGINE || CPM)
- help
- Some of Freescale PowerPC processors have a Full Speed
- QE/CPM2 USB controller, which support device mode with 4
- programmable endpoints. This driver supports the
- controller in the MPC8360 and MPC8272, and should work with
- controllers having QE or CPM2, given minor tweaks.
-
- Set CONFIG_USB_GADGET to "m" to build this driver as a
- dynamically linked module called "fsl_qe_udc".
-
-config USB_NET2272
- tristate "PLX NET2272"
- help
- PLX NET2272 is a USB peripheral controller which supports
- both full and high speed USB 2.0 data transfers.
-
- It has three configurable endpoints, as well as endpoint zero
- (for control transfer).
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "net2272" and force all
- gadget drivers to also be dynamically linked.
-
-config USB_NET2272_DMA
- boolean "Support external DMA controller"
- depends on USB_NET2272 && HAS_DMA
- help
- The NET2272 part can optionally support an external DMA
- controller, but your board has to have support in the
- driver itself.
-
- If unsure, say "N" here. The driver works fine in PIO mode.
-
-config USB_NET2280
- tristate "NetChip 228x"
- depends on PCI
- help
- NetChip 2280 / 2282 is a PCI based USB peripheral controller which
- supports both full and high speed USB 2.0 data transfers.
-
- It has six configurable endpoints, as well as endpoint zero
- (for control transfers) and several endpoints with dedicated
- functions.
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "net2280" and force all
- gadget drivers to also be dynamically linked.
-
-config USB_GOKU
- tristate "Toshiba TC86C001 'Goku-S'"
- depends on PCI
- help
- The Toshiba TC86C001 is a PCI device which includes controllers
- for full speed USB devices, IDE, I2C, SIO, plus a USB host (OHCI).
-
- The device controller has three configurable (bulk or interrupt)
- endpoints, plus endpoint zero (for control transfers).
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "goku_udc" and to force all
- gadget drivers to also be dynamically linked.
-
-config USB_EG20T
- tristate "Intel EG20T PCH/LAPIS Semiconductor IOH(ML7213/ML7831) UDC"
- depends on PCI
- help
- This is a USB device driver for EG20T PCH.
- EG20T PCH is the platform controller hub that is used in Intel's
- general embedded platform. EG20T PCH has USB device interface.
- Using this interface, it is able to access system devices connected
- to USB device.
- This driver enables USB device function.
- USB device is a USB peripheral controller which
- supports both full and high speed USB 2.0 data transfers.
- This driver supports both control transfer and bulk transfer modes.
- This driver dose not support interrupt transfer or isochronous
- transfer modes.
-
- This driver also can be used for LAPIS Semiconductor's ML7213 which is
- for IVI(In-Vehicle Infotainment) use.
- ML7831 is for general purpose use.
- ML7213/ML7831 is companion chip for Intel Atom E6xx series.
- ML7213/ML7831 is completely compatible for Intel EG20T PCH.
-
-#
-# LAST -- dummy/emulated controller
-#
-
-config USB_DUMMY_HCD
- tristate "Dummy HCD (DEVELOPMENT)"
- depends on USB=y || (USB=m && USB_GADGET=m)
- help
- This host controller driver emulates USB, looping all data transfer
- requests back to a USB "gadget driver" in the same host. The host
- side is the master; the gadget side is the slave. Gadget drivers
- can be high, full, or low speed; and they have access to endpoints
- like those from NET2280, PXA2xx, or SA1100 hardware.
-
- This may help in some stages of creating a driver to embed in a
- Linux device, since it lets you debug several parts of the gadget
- driver without its hardware or drivers being involved.
-
- Since such a gadget side driver needs to interoperate with a host
- side Linux-USB device driver, this may help to debug both sides
- of a USB protocol stack.
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "dummy_hcd" and force all
- gadget drivers to also be dynamically linked.
-
-# NOTE: Please keep dummy_hcd LAST so that "real hardware" appears
-# first and will be selected by default.
-
-endmenu
+source "drivers/usb/gadget/udc/Kconfig"
#
# USB Gadget Drivers
@@ -714,466 +353,7 @@ config USB_CONFIGFS_F_FS
implemented in kernel space (for instance Ethernet, serial or
mass storage) and other are implemented in user space.
-config USB_ZERO
- tristate "Gadget Zero (DEVELOPMENT)"
- select USB_LIBCOMPOSITE
- select USB_F_SS_LB
- help
- Gadget Zero is a two-configuration device. It either sinks and
- sources bulk data; or it loops back a configurable number of
- transfers. It also implements control requests, for "chapter 9"
- conformance. The driver needs only two bulk-capable endpoints, so
- it can work on top of most device-side usb controllers. It's
- useful for testing, and is also a working example showing how
- USB "gadget drivers" can be written.
-
- Make this be the first driver you try using on top of any new
- USB peripheral controller driver. Then you can use host-side
- test software, like the "usbtest" driver, to put your hardware
- and its driver through a basic set of functional tests.
-
- Gadget Zero also works with the host-side "usb-skeleton" driver,
- and with many kinds of host-side test software. You may need
- to tweak product and vendor IDs before host software knows about
- this device, and arrange to select an appropriate configuration.
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "g_zero".
-
-config USB_ZERO_HNPTEST
- boolean "HNP Test Device"
- depends on USB_ZERO && USB_OTG
- help
- You can configure this device to enumerate using the device
- identifiers of the USB-OTG test device. That means that when
- this gadget connects to another OTG device, with this one using
- the "B-Peripheral" role, that device will use HNP to let this
- one serve as the USB host instead (in the "B-Host" role).
-
-config USB_AUDIO
- tristate "Audio Gadget"
- depends on SND
- select USB_LIBCOMPOSITE
- select SND_PCM
- help
- This Gadget Audio driver is compatible with USB Audio Class
- specification 2.0. It implements 1 AudioControl interface,
- 1 AudioStreaming Interface each for USB-OUT and USB-IN.
- Number of channels, sample rate and sample size can be
- specified as module parameters.
- This driver doesn't expect any real Audio codec to be present
- on the device - the audio streams are simply sinked to and
- sourced from a virtual ALSA sound card created. The user-space
- application may choose to do whatever it wants with the data
- received from the USB Host and choose to provide whatever it
- wants as audio data to the USB Host.
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "g_audio".
-
-config GADGET_UAC1
- bool "UAC 1.0 (Legacy)"
- depends on USB_AUDIO
- help
- If you instead want older UAC Spec-1.0 driver that also has audio
- paths hardwired to the Audio codec chip on-board and doesn't work
- without one.
-
-config USB_ETH
- tristate "Ethernet Gadget (with CDC Ethernet support)"
- depends on NET
- select USB_LIBCOMPOSITE
- select USB_U_ETHER
- select USB_F_ECM
- select USB_F_SUBSET
- select CRC32
- help
- This driver implements Ethernet style communication, in one of
- several ways:
-
- - The "Communication Device Class" (CDC) Ethernet Control Model.
- That protocol is often avoided with pure Ethernet adapters, in
- favor of simpler vendor-specific hardware, but is widely
- supported by firmware for smart network devices.
-
- - On hardware can't implement that protocol, a simple CDC subset
- is used, placing fewer demands on USB.
-
- - CDC Ethernet Emulation Model (EEM) is a newer standard that has
- a simpler interface that can be used by more USB hardware.
-
- RNDIS support is an additional option, more demanding than than
- subset.
-
- Within the USB device, this gadget driver exposes a network device
- "usbX", where X depends on what other networking devices you have.
- Treat it like a two-node Ethernet link: host, and gadget.
-
- The Linux-USB host-side "usbnet" driver interoperates with this
- driver, so that deep I/O queues can be supported. On 2.4 kernels,
- use "CDCEther" instead, if you're using the CDC option. That CDC
- mode should also interoperate with standard CDC Ethernet class
- drivers on other host operating systems.
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "g_ether".
-
-config USB_ETH_RNDIS
- bool "RNDIS support"
- depends on USB_ETH
- select USB_LIBCOMPOSITE
- select USB_F_RNDIS
- default y
- help
- Microsoft Windows XP bundles the "Remote NDIS" (RNDIS) protocol,
- and Microsoft provides redistributable binary RNDIS drivers for
- older versions of Windows.
-
- If you say "y" here, the Ethernet gadget driver will try to provide
- a second device configuration, supporting RNDIS to talk to such
- Microsoft USB hosts.
-
- To make MS-Windows work with this, use Documentation/usb/linux.inf
- as the "driver info file". For versions of MS-Windows older than
- XP, you'll need to download drivers from Microsoft's website; a URL
- is given in comments found in that info file.
-
-config USB_ETH_EEM
- bool "Ethernet Emulation Model (EEM) support"
- depends on USB_ETH
- select USB_LIBCOMPOSITE
- select USB_F_EEM
- default n
- help
- CDC EEM is a newer USB standard that is somewhat simpler than CDC ECM
- and therefore can be supported by more hardware. Technically ECM and
- EEM are designed for different applications. The ECM model extends
- the network interface to the target (e.g. a USB cable modem), and the
- EEM model is for mobile devices to communicate with hosts using
- ethernet over USB. For Linux gadgets, however, the interface with
- the host is the same (a usbX device), so the differences are minimal.
-
- If you say "y" here, the Ethernet gadget driver will use the EEM
- protocol rather than ECM. If unsure, say "n".
-
-config USB_G_NCM
- tristate "Network Control Model (NCM) support"
- depends on NET
- select USB_LIBCOMPOSITE
- select USB_U_ETHER
- select USB_F_NCM
- select CRC32
- help
- This driver implements USB CDC NCM subclass standard. NCM is
- an advanced protocol for Ethernet encapsulation, allows grouping
- of several ethernet frames into one USB transfer and different
- alignment possibilities.
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "g_ncm".
-
-config USB_GADGETFS
- tristate "Gadget Filesystem"
- help
- This driver provides a filesystem based API that lets user mode
- programs implement a single-configuration USB device, including
- endpoint I/O and control requests that don't relate to enumeration.
- All endpoints, transfer speeds, and transfer types supported by
- the hardware are available, through read() and write() calls.
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "gadgetfs".
-
-config USB_FUNCTIONFS
- tristate "Function Filesystem"
- select USB_LIBCOMPOSITE
- select USB_F_FS
- select USB_FUNCTIONFS_GENERIC if !(USB_FUNCTIONFS_ETH || USB_FUNCTIONFS_RNDIS)
- help
- The Function Filesystem (FunctionFS) lets one create USB
- composite functions in user space in the same way GadgetFS
- lets one create USB gadgets in user space. This allows creation
- of composite gadgets such that some of the functions are
- implemented in kernel space (for instance Ethernet, serial or
- mass storage) and other are implemented in user space.
-
- If you say "y" or "m" here you will be able what kind of
- configurations the gadget will provide.
-
- Say "y" to link the driver statically, or "m" to build
- a dynamically linked module called "g_ffs".
-
-config USB_FUNCTIONFS_ETH
- bool "Include configuration with CDC ECM (Ethernet)"
- depends on USB_FUNCTIONFS && NET
- select USB_U_ETHER
- select USB_F_ECM
- select USB_F_SUBSET
- help
- Include a configuration with CDC ECM function (Ethernet) and the
- Function Filesystem.
-
-config USB_FUNCTIONFS_RNDIS
- bool "Include configuration with RNDIS (Ethernet)"
- depends on USB_FUNCTIONFS && NET
- select USB_U_ETHER
- select USB_F_RNDIS
- help
- Include a configuration with RNDIS function (Ethernet) and the Filesystem.
-
-config USB_FUNCTIONFS_GENERIC
- bool "Include 'pure' configuration"
- depends on USB_FUNCTIONFS
- help
- Include a configuration with the Function Filesystem alone with
- no Ethernet interface.
-
-config USB_MASS_STORAGE
- tristate "Mass Storage Gadget"
- depends on BLOCK
- select USB_LIBCOMPOSITE
- select USB_F_MASS_STORAGE
- help
- The Mass Storage Gadget acts as a USB Mass Storage disk drive.
- As its storage repository it can use a regular file or a block
- device (in much the same way as the "loop" device driver),
- specified as a module parameter or sysfs option.
-
- This driver is a replacement for now removed File-backed
- Storage Gadget (g_file_storage).
-
- Say "y" to link the driver statically, or "m" to build
- a dynamically linked module called "g_mass_storage".
-
-config USB_GADGET_TARGET
- tristate "USB Gadget Target Fabric Module"
- depends on TARGET_CORE
- select USB_LIBCOMPOSITE
- help
- This fabric is an USB gadget. Two USB protocols are supported that is
- BBB or BOT (Bulk Only Transport) and UAS (USB Attached SCSI). BOT is
- advertised on alternative interface 0 (primary) and UAS is on
- alternative interface 1. Both protocols can work on USB2.0 and USB3.0.
- UAS utilizes the USB 3.0 feature called streams support.
-
-config USB_G_SERIAL
- tristate "Serial Gadget (with CDC ACM and CDC OBEX support)"
- depends on TTY
- select USB_U_SERIAL
- select USB_F_ACM
- select USB_F_SERIAL
- select USB_F_OBEX
- select USB_LIBCOMPOSITE
- help
- The Serial Gadget talks to the Linux-USB generic serial driver.
- This driver supports a CDC-ACM module option, which can be used
- to interoperate with MS-Windows hosts or with the Linux-USB
- "cdc-acm" driver.
-
- This driver also supports a CDC-OBEX option. You will need a
- user space OBEX server talking to /dev/ttyGS*, since the kernel
- itself doesn't implement the OBEX protocol.
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "g_serial".
-
- For more information, see Documentation/usb/gadget_serial.txt
- which includes instructions and a "driver info file" needed to
- make MS-Windows work with CDC ACM.
-
-config USB_MIDI_GADGET
- tristate "MIDI Gadget"
- depends on SND
- select USB_LIBCOMPOSITE
- select SND_RAWMIDI
- help
- The MIDI Gadget acts as a USB Audio device, with one MIDI
- input and one MIDI output. These MIDI jacks appear as
- a sound "card" in the ALSA sound system. Other MIDI
- connections can then be made on the gadget system, using
- ALSA's aconnect utility etc.
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "g_midi".
-
-config USB_G_PRINTER
- tristate "Printer Gadget"
- select USB_LIBCOMPOSITE
- help
- The Printer Gadget channels data between the USB host and a
- userspace program driving the print engine. The user space
- program reads and writes the device file /dev/g_printer to
- receive or send printer data. It can use ioctl calls to
- the device file to get or set printer status.
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "g_printer".
-
- For more information, see Documentation/usb/gadget_printer.txt
- which includes sample code for accessing the device file.
-
-if TTY
-
-config USB_CDC_COMPOSITE
- tristate "CDC Composite Device (Ethernet and ACM)"
- depends on NET
- select USB_LIBCOMPOSITE
- select USB_U_SERIAL
- select USB_U_ETHER
- select USB_F_ACM
- select USB_F_ECM
- help
- This driver provides two functions in one configuration:
- a CDC Ethernet (ECM) link, and a CDC ACM (serial port) link.
-
- This driver requires four bulk and two interrupt endpoints,
- plus the ability to handle altsettings. Not all peripheral
- controllers are that capable.
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module.
-
-config USB_G_NOKIA
- tristate "Nokia composite gadget"
- depends on PHONET
- select USB_LIBCOMPOSITE
- select USB_U_SERIAL
- select USB_U_ETHER
- select USB_F_ACM
- select USB_F_OBEX
- select USB_F_PHONET
- select USB_F_ECM
- help
- The Nokia composite gadget provides support for acm, obex
- and phonet in only one composite gadget driver.
-
- It's only really useful for N900 hardware. If you're building
- a kernel for N900, say Y or M here. If unsure, say N.
-
-config USB_G_ACM_MS
- tristate "CDC Composite Device (ACM and mass storage)"
- depends on BLOCK
- select USB_LIBCOMPOSITE
- select USB_U_SERIAL
- select USB_F_ACM
- select USB_F_MASS_STORAGE
- help
- This driver provides two functions in one configuration:
- a mass storage, and a CDC ACM (serial port) link.
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "g_acm_ms".
-
-config USB_G_MULTI
- tristate "Multifunction Composite Gadget"
- depends on BLOCK && NET
- select USB_G_MULTI_CDC if !USB_G_MULTI_RNDIS
- select USB_LIBCOMPOSITE
- select USB_U_SERIAL
- select USB_U_ETHER
- select USB_F_ACM
- select USB_F_MASS_STORAGE
- help
- The Multifunction Composite Gadget provides Ethernet (RNDIS
- and/or CDC Ethernet), mass storage and ACM serial link
- interfaces.
-
- You will be asked to choose which of the two configurations is
- to be available in the gadget. At least one configuration must
- be chosen to make the gadget usable. Selecting more than one
- configuration will prevent Windows from automatically detecting
- the gadget as a composite gadget, so an INF file will be needed to
- use the gadget.
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "g_multi".
-
-config USB_G_MULTI_RNDIS
- bool "RNDIS + CDC Serial + Storage configuration"
- depends on USB_G_MULTI
- select USB_F_RNDIS
- default y
- help
- This option enables a configuration with RNDIS, CDC Serial and
- Mass Storage functions available in the Multifunction Composite
- Gadget. This is the configuration dedicated for Windows since RNDIS
- is Microsoft's protocol.
-
- If unsure, say "y".
-
-config USB_G_MULTI_CDC
- bool "CDC Ethernet + CDC Serial + Storage configuration"
- depends on USB_G_MULTI
- default n
- select USB_F_ECM
- help
- This option enables a configuration with CDC Ethernet (ECM), CDC
- Serial and Mass Storage functions available in the Multifunction
- Composite Gadget.
-
- If unsure, say "y".
-
-endif # TTY
-
-config USB_G_HID
- tristate "HID Gadget"
- select USB_LIBCOMPOSITE
- help
- The HID gadget driver provides generic emulation of USB
- Human Interface Devices (HID).
-
- For more information, see Documentation/usb/gadget_hid.txt which
- includes sample code for accessing the device files.
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "g_hid".
-
-# Standalone / single function gadgets
-config USB_G_DBGP
- tristate "EHCI Debug Device Gadget"
- depends on TTY
- select USB_LIBCOMPOSITE
- help
- This gadget emulates an EHCI Debug device. This is useful when you want
- to interact with an EHCI Debug Port.
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "g_dbgp".
-
-if USB_G_DBGP
-choice
- prompt "EHCI Debug Device mode"
- default USB_G_DBGP_SERIAL
-
-config USB_G_DBGP_PRINTK
- depends on USB_G_DBGP
- bool "printk"
- help
- Directly printk() received data. No interaction.
-
-config USB_G_DBGP_SERIAL
- depends on USB_G_DBGP
- select USB_U_SERIAL
- bool "serial"
- help
- Userland can interact using /dev/ttyGSxxx.
-endchoice
-endif
-
-# put drivers that need isochronous transfer support (for audio
-# or video class gadget drivers), or specific hardware, here.
-config USB_G_WEBCAM
- tristate "USB Webcam Gadget"
- depends on VIDEO_DEV
- select USB_LIBCOMPOSITE
- select VIDEOBUF2_VMALLOC
- help
- The Webcam Gadget acts as a composite USB Audio and Video Class
- device. It provides a userspace API to process UVC control requests
- and stream video data to the host.
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "g_webcam".
+source "drivers/usb/gadget/legacy/Kconfig"
endchoice
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 49514ea60a98..a186afeaa700 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -1,105 +1,12 @@
#
# USB peripheral controller drivers
#
-ccflags-$(CONFIG_USB_GADGET_DEBUG) := -DDEBUG
-ccflags-$(CONFIG_USB_GADGET_VERBOSE) += -DVERBOSE_DEBUG
+subdir-ccflags-$(CONFIG_USB_GADGET_DEBUG) := -DDEBUG
+subdir-ccflags-$(CONFIG_USB_GADGET_VERBOSE) += -DVERBOSE_DEBUG
+ccflags-y += -I$(PWD)/drivers/usb/gadget/udc
-obj-$(CONFIG_USB_GADGET) += udc-core.o
obj-$(CONFIG_USB_LIBCOMPOSITE) += libcomposite.o
libcomposite-y := usbstring.o config.o epautoconf.o
libcomposite-y += composite.o functions.o configfs.o u_f.o
-obj-$(CONFIG_USB_DUMMY_HCD) += dummy_hcd.o
-obj-$(CONFIG_USB_NET2272) += net2272.o
-obj-$(CONFIG_USB_NET2280) += net2280.o
-obj-$(CONFIG_USB_AMD5536UDC) += amd5536udc.o
-obj-$(CONFIG_USB_PXA25X) += pxa25x_udc.o
-obj-$(CONFIG_USB_PXA27X) += pxa27x_udc.o
-obj-$(CONFIG_USB_GOKU) += goku_udc.o
-obj-$(CONFIG_USB_OMAP) += omap_udc.o
-obj-$(CONFIG_USB_S3C2410) += s3c2410_udc.o
-obj-$(CONFIG_USB_AT91) += at91_udc.o
-obj-$(CONFIG_USB_ATMEL_USBA) += atmel_usba_udc.o
-obj-$(CONFIG_USB_BCM63XX_UDC) += bcm63xx_udc.o
-obj-$(CONFIG_USB_FSL_USB2) += fsl_usb2_udc.o
-fsl_usb2_udc-y := fsl_udc_core.o
-fsl_usb2_udc-$(CONFIG_ARCH_MXC) += fsl_mxc_udc.o
-obj-$(CONFIG_USB_M66592) += m66592-udc.o
-obj-$(CONFIG_USB_R8A66597) += r8a66597-udc.o
-obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o
-obj-$(CONFIG_USB_S3C_HSUDC) += s3c-hsudc.o
-obj-$(CONFIG_USB_LPC32XX) += lpc32xx_udc.o
-obj-$(CONFIG_USB_EG20T) += pch_udc.o
-obj-$(CONFIG_USB_MV_UDC) += mv_udc.o
-mv_udc-y := mv_udc_core.o
-obj-$(CONFIG_USB_FUSB300) += fusb300_udc.o
-obj-$(CONFIG_USB_FOTG210_UDC) += fotg210-udc.o
-obj-$(CONFIG_USB_MV_U3D) += mv_u3d_core.o
-obj-$(CONFIG_USB_GR_UDC) += gr_udc.o
-# USB Functions
-usb_f_acm-y := f_acm.o
-obj-$(CONFIG_USB_F_ACM) += usb_f_acm.o
-usb_f_ss_lb-y := f_loopback.o f_sourcesink.o
-obj-$(CONFIG_USB_F_SS_LB) += usb_f_ss_lb.o
-obj-$(CONFIG_USB_U_SERIAL) += u_serial.o
-usb_f_serial-y := f_serial.o
-obj-$(CONFIG_USB_F_SERIAL) += usb_f_serial.o
-usb_f_obex-y := f_obex.o
-obj-$(CONFIG_USB_F_OBEX) += usb_f_obex.o
-obj-$(CONFIG_USB_U_ETHER) += u_ether.o
-usb_f_ncm-y := f_ncm.o
-obj-$(CONFIG_USB_F_NCM) += usb_f_ncm.o
-usb_f_ecm-y := f_ecm.o
-obj-$(CONFIG_USB_F_ECM) += usb_f_ecm.o
-usb_f_phonet-y := f_phonet.o
-obj-$(CONFIG_USB_F_PHONET) += usb_f_phonet.o
-usb_f_eem-y := f_eem.o
-obj-$(CONFIG_USB_F_EEM) += usb_f_eem.o
-usb_f_ecm_subset-y := f_subset.o
-obj-$(CONFIG_USB_F_SUBSET) += usb_f_ecm_subset.o
-usb_f_rndis-y := f_rndis.o rndis.o
-obj-$(CONFIG_USB_F_RNDIS) += usb_f_rndis.o
-usb_f_mass_storage-y := f_mass_storage.o storage_common.o
-obj-$(CONFIG_USB_F_MASS_STORAGE)+= usb_f_mass_storage.o
-usb_f_fs-y := f_fs.o
-obj-$(CONFIG_USB_F_FS) += usb_f_fs.o
-
-#
-# USB gadget drivers
-#
-g_zero-y := zero.o
-g_audio-y := audio.o
-g_ether-y := ether.o
-g_serial-y := serial.o
-g_midi-y := gmidi.o
-gadgetfs-y := inode.o
-g_mass_storage-y := mass_storage.o
-g_printer-y := printer.o
-g_cdc-y := cdc2.o
-g_multi-y := multi.o
-g_hid-y := hid.o
-g_dbgp-y := dbgp.o
-g_nokia-y := nokia.o
-g_webcam-y := webcam.o
-g_ncm-y := ncm.o
-g_acm_ms-y := acm_ms.o
-g_tcm_usb_gadget-y := tcm_usb_gadget.o
-
-obj-$(CONFIG_USB_ZERO) += g_zero.o
-obj-$(CONFIG_USB_AUDIO) += g_audio.o
-obj-$(CONFIG_USB_ETH) += g_ether.o
-obj-$(CONFIG_USB_GADGETFS) += gadgetfs.o
-obj-$(CONFIG_USB_FUNCTIONFS) += g_ffs.o
-obj-$(CONFIG_USB_MASS_STORAGE) += g_mass_storage.o
-obj-$(CONFIG_USB_G_SERIAL) += g_serial.o
-obj-$(CONFIG_USB_G_PRINTER) += g_printer.o
-obj-$(CONFIG_USB_MIDI_GADGET) += g_midi.o
-obj-$(CONFIG_USB_CDC_COMPOSITE) += g_cdc.o
-obj-$(CONFIG_USB_G_HID) += g_hid.o
-obj-$(CONFIG_USB_G_DBGP) += g_dbgp.o
-obj-$(CONFIG_USB_G_MULTI) += g_multi.o
-obj-$(CONFIG_USB_G_NOKIA) += g_nokia.o
-obj-$(CONFIG_USB_G_WEBCAM) += g_webcam.o
-obj-$(CONFIG_USB_G_NCM) += g_ncm.o
-obj-$(CONFIG_USB_G_ACM_MS) += g_acm_ms.o
-obj-$(CONFIG_USB_GADGET_TARGET) += tcm_usb_gadget.o
+obj-$(CONFIG_USB_GADGET) += udc/ function/ legacy/
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index f80151932053..6935a822ce2b 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -1956,6 +1956,7 @@ void composite_dev_cleanup(struct usb_composite_dev *cdev)
}
if (cdev->req) {
kfree(cdev->req->buf);
+ usb_ep_dequeue(cdev->gadget->ep0, cdev->req);
usb_ep_free_request(cdev->gadget->ep0, cdev->req);
}
cdev->next_string_id = 0;
diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c
index 97142146eead..811c2c7cc269 100644
--- a/drivers/usb/gadget/configfs.c
+++ b/drivers/usb/gadget/configfs.c
@@ -1021,12 +1021,10 @@ static ssize_t ext_prop_data_store(struct usb_os_desc_ext_prop *ext_prop,
if (page[len - 1] == '\n' || page[len - 1] == '\0')
--len;
- new_data = kzalloc(len, GFP_KERNEL);
+ new_data = kmemdup(page, len, GFP_KERNEL);
if (!new_data)
return -ENOMEM;
- memcpy(new_data, page, len);
-
if (desc->opts_mutex)
mutex_lock(desc->opts_mutex);
kfree(ext_prop->data);
diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile
new file mode 100644
index 000000000000..6d91f21b52a6
--- /dev/null
+++ b/drivers/usb/gadget/function/Makefile
@@ -0,0 +1,34 @@
+#
+# USB peripheral controller drivers
+#
+
+ccflags-y := -I$(PWD)/drivers/usb/gadget/
+ccflags-y += -I$(PWD)/drivers/usb/gadget/udc/
+
+# USB Functions
+usb_f_acm-y := f_acm.o
+obj-$(CONFIG_USB_F_ACM) += usb_f_acm.o
+usb_f_ss_lb-y := f_loopback.o f_sourcesink.o
+obj-$(CONFIG_USB_F_SS_LB) += usb_f_ss_lb.o
+obj-$(CONFIG_USB_U_SERIAL) += u_serial.o
+usb_f_serial-y := f_serial.o
+obj-$(CONFIG_USB_F_SERIAL) += usb_f_serial.o
+usb_f_obex-y := f_obex.o
+obj-$(CONFIG_USB_F_OBEX) += usb_f_obex.o
+obj-$(CONFIG_USB_U_ETHER) += u_ether.o
+usb_f_ncm-y := f_ncm.o
+obj-$(CONFIG_USB_F_NCM) += usb_f_ncm.o
+usb_f_ecm-y := f_ecm.o
+obj-$(CONFIG_USB_F_ECM) += usb_f_ecm.o
+usb_f_phonet-y := f_phonet.o
+obj-$(CONFIG_USB_F_PHONET) += usb_f_phonet.o
+usb_f_eem-y := f_eem.o
+obj-$(CONFIG_USB_F_EEM) += usb_f_eem.o
+usb_f_ecm_subset-y := f_subset.o
+obj-$(CONFIG_USB_F_SUBSET) += usb_f_ecm_subset.o
+usb_f_rndis-y := f_rndis.o rndis.o
+obj-$(CONFIG_USB_F_RNDIS) += usb_f_rndis.o
+usb_f_mass_storage-y := f_mass_storage.o storage_common.o
+obj-$(CONFIG_USB_F_MASS_STORAGE)+= usb_f_mass_storage.o
+usb_f_fs-y := f_fs.o
+obj-$(CONFIG_USB_F_FS) += usb_f_fs.o
diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/function/f_acm.c
index ab1065afbbd0..ab1065afbbd0 100644
--- a/drivers/usb/gadget/f_acm.c
+++ b/drivers/usb/gadget/function/f_acm.c
diff --git a/drivers/usb/gadget/f_ecm.c b/drivers/usb/gadget/function/f_ecm.c
index 798760fa7e70..798760fa7e70 100644
--- a/drivers/usb/gadget/f_ecm.c
+++ b/drivers/usb/gadget/function/f_ecm.c
diff --git a/drivers/usb/gadget/f_eem.c b/drivers/usb/gadget/function/f_eem.c
index d61c11d765d0..4d8b236ea608 100644
--- a/drivers/usb/gadget/f_eem.c
+++ b/drivers/usb/gadget/function/f_eem.c
@@ -355,20 +355,18 @@ static struct sk_buff *eem_wrap(struct gether *port, struct sk_buff *skb)
int padlen = 0;
u16 len = skb->len;
- if (!skb_cloned(skb)) {
- int headroom = skb_headroom(skb);
- int tailroom = skb_tailroom(skb);
+ int headroom = skb_headroom(skb);
+ int tailroom = skb_tailroom(skb);
- /* When (len + EEM_HLEN + ETH_FCS_LEN) % in->maxpacket) is 0,
- * stick two bytes of zero-length EEM packet on the end.
- */
- if (((len + EEM_HLEN + ETH_FCS_LEN) % in->maxpacket) == 0)
- padlen += 2;
+ /* When (len + EEM_HLEN + ETH_FCS_LEN) % in->maxpacket) is 0,
+ * stick two bytes of zero-length EEM packet on the end.
+ */
+ if (((len + EEM_HLEN + ETH_FCS_LEN) % in->maxpacket) == 0)
+ padlen += 2;
- if ((tailroom >= (ETH_FCS_LEN + padlen)) &&
- (headroom >= EEM_HLEN))
- goto done;
- }
+ if ((tailroom >= (ETH_FCS_LEN + padlen)) &&
+ (headroom >= EEM_HLEN) && !skb_cloned(skb))
+ goto done;
skb2 = skb_copy_expand(skb, EEM_HLEN, ETH_FCS_LEN + padlen, GFP_ATOMIC);
dev_kfree_skb_any(skb);
diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index 8598c27c7d43..dc30adf15a01 100644
--- a/drivers/usb/gadget/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -34,6 +34,7 @@
#include "u_fs.h"
#include "u_f.h"
+#include "u_os_desc.h"
#include "configfs.h"
#define FUNCTIONFS_MAGIC 0xa647361 /* Chosen by a honest dice roll ;) */
@@ -1646,13 +1647,22 @@ enum ffs_entity_type {
FFS_DESCRIPTOR, FFS_INTERFACE, FFS_STRING, FFS_ENDPOINT
};
+enum ffs_os_desc_type {
+ FFS_OS_DESC, FFS_OS_DESC_EXT_COMPAT, FFS_OS_DESC_EXT_PROP
+};
+
typedef int (*ffs_entity_callback)(enum ffs_entity_type entity,
u8 *valuep,
struct usb_descriptor_header *desc,
void *priv);
-static int __must_check ffs_do_desc(char *data, unsigned len,
- ffs_entity_callback entity, void *priv)
+typedef int (*ffs_os_desc_callback)(enum ffs_os_desc_type entity,
+ struct usb_os_desc_header *h, void *data,
+ unsigned len, void *priv);
+
+static int __must_check ffs_do_single_desc(char *data, unsigned len,
+ ffs_entity_callback entity,
+ void *priv)
{
struct usb_descriptor_header *_ds = (void *)data;
u8 length;
@@ -1804,7 +1814,7 @@ static int __must_check ffs_do_descs(unsigned count, char *data, unsigned len,
if (!data)
return _len - len;
- ret = ffs_do_desc(data, len, entity, priv);
+ ret = ffs_do_single_desc(data, len, entity, priv);
if (unlikely(ret < 0)) {
pr_debug("%s returns %d\n", __func__, ret);
return ret;
@@ -1857,11 +1867,191 @@ static int __ffs_data_do_entity(enum ffs_entity_type type,
return 0;
}
+static int __ffs_do_os_desc_header(enum ffs_os_desc_type *next_type,
+ struct usb_os_desc_header *desc)
+{
+ u16 bcd_version = le16_to_cpu(desc->bcdVersion);
+ u16 w_index = le16_to_cpu(desc->wIndex);
+
+ if (bcd_version != 1) {
+ pr_vdebug("unsupported os descriptors version: %d",
+ bcd_version);
+ return -EINVAL;
+ }
+ switch (w_index) {
+ case 0x4:
+ *next_type = FFS_OS_DESC_EXT_COMPAT;
+ break;
+ case 0x5:
+ *next_type = FFS_OS_DESC_EXT_PROP;
+ break;
+ default:
+ pr_vdebug("unsupported os descriptor type: %d", w_index);
+ return -EINVAL;
+ }
+
+ return sizeof(*desc);
+}
+
+/*
+ * Process all extended compatibility/extended property descriptors
+ * of a feature descriptor
+ */
+static int __must_check ffs_do_single_os_desc(char *data, unsigned len,
+ enum ffs_os_desc_type type,
+ u16 feature_count,
+ ffs_os_desc_callback entity,
+ void *priv,
+ struct usb_os_desc_header *h)
+{
+ int ret;
+ const unsigned _len = len;
+
+ ENTER();
+
+ /* loop over all ext compat/ext prop descriptors */
+ while (feature_count--) {
+ ret = entity(type, h, data, len, priv);
+ if (unlikely(ret < 0)) {
+ pr_debug("bad OS descriptor, type: %d\n", type);
+ return ret;
+ }
+ data += ret;
+ len -= ret;
+ }
+ return _len - len;
+}
+
+/* Process a number of complete Feature Descriptors (Ext Compat or Ext Prop) */
+static int __must_check ffs_do_os_descs(unsigned count,
+ char *data, unsigned len,
+ ffs_os_desc_callback entity, void *priv)
+{
+ const unsigned _len = len;
+ unsigned long num = 0;
+
+ ENTER();
+
+ for (num = 0; num < count; ++num) {
+ int ret;
+ enum ffs_os_desc_type type;
+ u16 feature_count;
+ struct usb_os_desc_header *desc = (void *)data;
+
+ if (len < sizeof(*desc))
+ return -EINVAL;
+
+ /*
+ * Record "descriptor" entity.
+ * Process dwLength, bcdVersion, wIndex, get b/wCount.
+ * Move the data pointer to the beginning of extended
+ * compatibilities proper or extended properties proper
+ * portions of the data
+ */
+ if (le32_to_cpu(desc->dwLength) > len)
+ return -EINVAL;
+
+ ret = __ffs_do_os_desc_header(&type, desc);
+ if (unlikely(ret < 0)) {
+ pr_debug("entity OS_DESCRIPTOR(%02lx); ret = %d\n",
+ num, ret);
+ return ret;
+ }
+ /*
+ * 16-bit hex "?? 00" Little Endian looks like 8-bit hex "??"
+ */
+ feature_count = le16_to_cpu(desc->wCount);
+ if (type == FFS_OS_DESC_EXT_COMPAT &&
+ (feature_count > 255 || desc->Reserved))
+ return -EINVAL;
+ len -= ret;
+ data += ret;
+
+ /*
+ * Process all function/property descriptors
+ * of this Feature Descriptor
+ */
+ ret = ffs_do_single_os_desc(data, len, type,
+ feature_count, entity, priv, desc);
+ if (unlikely(ret < 0)) {
+ pr_debug("%s returns %d\n", __func__, ret);
+ return ret;
+ }
+
+ len -= ret;
+ data += ret;
+ }
+ return _len - len;
+}
+
+/**
+ * Validate contents of the buffer from userspace related to OS descriptors.
+ */
+static int __ffs_data_do_os_desc(enum ffs_os_desc_type type,
+ struct usb_os_desc_header *h, void *data,
+ unsigned len, void *priv)
+{
+ struct ffs_data *ffs = priv;
+ u8 length;
+
+ ENTER();
+
+ switch (type) {
+ case FFS_OS_DESC_EXT_COMPAT: {
+ struct usb_ext_compat_desc *d = data;
+ int i;
+
+ if (len < sizeof(*d) ||
+ d->bFirstInterfaceNumber >= ffs->interfaces_count ||
+ d->Reserved1)
+ return -EINVAL;
+ for (i = 0; i < ARRAY_SIZE(d->Reserved2); ++i)
+ if (d->Reserved2[i])
+ return -EINVAL;
+
+ length = sizeof(struct usb_ext_compat_desc);
+ }
+ break;
+ case FFS_OS_DESC_EXT_PROP: {
+ struct usb_ext_prop_desc *d = data;
+ u32 type, pdl;
+ u16 pnl;
+
+ if (len < sizeof(*d) || h->interface >= ffs->interfaces_count)
+ return -EINVAL;
+ length = le32_to_cpu(d->dwSize);
+ type = le32_to_cpu(d->dwPropertyDataType);
+ if (type < USB_EXT_PROP_UNICODE ||
+ type > USB_EXT_PROP_UNICODE_MULTI) {
+ pr_vdebug("unsupported os descriptor property type: %d",
+ type);
+ return -EINVAL;
+ }
+ pnl = le16_to_cpu(d->wPropertyNameLength);
+ pdl = le32_to_cpu(*(u32 *)((u8 *)data + 10 + pnl));
+ if (length != 14 + pnl + pdl) {
+ pr_vdebug("invalid os descriptor length: %d pnl:%d pdl:%d (descriptor %d)\n",
+ length, pnl, pdl, type);
+ return -EINVAL;
+ }
+ ++ffs->ms_os_descs_ext_prop_count;
+ /* property name reported to the host as "WCHAR"s */
+ ffs->ms_os_descs_ext_prop_name_len += pnl * 2;
+ ffs->ms_os_descs_ext_prop_data_len += pdl;
+ }
+ break;
+ default:
+ pr_vdebug("unknown descriptor: %d\n", type);
+ return -EINVAL;
+ }
+ return length;
+}
+
static int __ffs_data_got_descs(struct ffs_data *ffs,
char *const _data, size_t len)
{
char *data = _data, *raw_descs;
- unsigned counts[3], flags;
+ unsigned os_descs_count = 0, counts[3], flags;
int ret = -EINVAL, i;
ENTER();
@@ -1879,7 +2069,8 @@ static int __ffs_data_got_descs(struct ffs_data *ffs,
flags = get_unaligned_le32(data + 8);
if (flags & ~(FUNCTIONFS_HAS_FS_DESC |
FUNCTIONFS_HAS_HS_DESC |
- FUNCTIONFS_HAS_SS_DESC)) {
+ FUNCTIONFS_HAS_SS_DESC |
+ FUNCTIONFS_HAS_MS_OS_DESC)) {
ret = -ENOSYS;
goto error;
}
@@ -1902,6 +2093,11 @@ static int __ffs_data_got_descs(struct ffs_data *ffs,
len -= 4;
}
}
+ if (flags & (1 << i)) {
+ os_descs_count = get_unaligned_le32(data);
+ data += 4;
+ len -= 4;
+ };
/* Read descriptors */
raw_descs = data;
@@ -1915,6 +2111,14 @@ static int __ffs_data_got_descs(struct ffs_data *ffs,
data += ret;
len -= ret;
}
+ if (os_descs_count) {
+ ret = ffs_do_os_descs(os_descs_count, data, len,
+ __ffs_data_do_os_desc, ffs);
+ if (ret < 0)
+ goto error;
+ data += ret;
+ len -= ret;
+ }
if (raw_descs == data || len) {
ret = -EINVAL;
@@ -1927,6 +2131,7 @@ static int __ffs_data_got_descs(struct ffs_data *ffs,
ffs->fs_descs_count = counts[0];
ffs->hs_descs_count = counts[1];
ffs->ss_descs_count = counts[2];
+ ffs->ms_os_descs_count = os_descs_count;
return 0;
@@ -2268,6 +2473,85 @@ static int __ffs_func_bind_do_nums(enum ffs_entity_type type, u8 *valuep,
return 0;
}
+static int __ffs_func_bind_do_os_desc(enum ffs_os_desc_type type,
+ struct usb_os_desc_header *h, void *data,
+ unsigned len, void *priv)
+{
+ struct ffs_function *func = priv;
+ u8 length = 0;
+
+ switch (type) {
+ case FFS_OS_DESC_EXT_COMPAT: {
+ struct usb_ext_compat_desc *desc = data;
+ struct usb_os_desc_table *t;
+
+ t = &func->function.os_desc_table[desc->bFirstInterfaceNumber];
+ t->if_id = func->interfaces_nums[desc->bFirstInterfaceNumber];
+ memcpy(t->os_desc->ext_compat_id, &desc->CompatibleID,
+ ARRAY_SIZE(desc->CompatibleID) +
+ ARRAY_SIZE(desc->SubCompatibleID));
+ length = sizeof(*desc);
+ }
+ break;
+ case FFS_OS_DESC_EXT_PROP: {
+ struct usb_ext_prop_desc *desc = data;
+ struct usb_os_desc_table *t;
+ struct usb_os_desc_ext_prop *ext_prop;
+ char *ext_prop_name;
+ char *ext_prop_data;
+
+ t = &func->function.os_desc_table[h->interface];
+ t->if_id = func->interfaces_nums[h->interface];
+
+ ext_prop = func->ffs->ms_os_descs_ext_prop_avail;
+ func->ffs->ms_os_descs_ext_prop_avail += sizeof(*ext_prop);
+
+ ext_prop->type = le32_to_cpu(desc->dwPropertyDataType);
+ ext_prop->name_len = le16_to_cpu(desc->wPropertyNameLength);
+ ext_prop->data_len = le32_to_cpu(*(u32 *)
+ usb_ext_prop_data_len_ptr(data, ext_prop->name_len));
+ length = ext_prop->name_len + ext_prop->data_len + 14;
+
+ ext_prop_name = func->ffs->ms_os_descs_ext_prop_name_avail;
+ func->ffs->ms_os_descs_ext_prop_name_avail +=
+ ext_prop->name_len;
+
+ ext_prop_data = func->ffs->ms_os_descs_ext_prop_data_avail;
+ func->ffs->ms_os_descs_ext_prop_data_avail +=
+ ext_prop->data_len;
+ memcpy(ext_prop_data,
+ usb_ext_prop_data_ptr(data, ext_prop->name_len),
+ ext_prop->data_len);
+ /* unicode data reported to the host as "WCHAR"s */
+ switch (ext_prop->type) {
+ case USB_EXT_PROP_UNICODE:
+ case USB_EXT_PROP_UNICODE_ENV:
+ case USB_EXT_PROP_UNICODE_LINK:
+ case USB_EXT_PROP_UNICODE_MULTI:
+ ext_prop->data_len *= 2;
+ break;
+ }
+ ext_prop->data = ext_prop_data;
+
+ memcpy(ext_prop_name, usb_ext_prop_name_ptr(data),
+ ext_prop->name_len);
+ /* property name reported to the host as "WCHAR"s */
+ ext_prop->name_len *= 2;
+ ext_prop->name = ext_prop_name;
+
+ t->os_desc->ext_prop_len +=
+ ext_prop->name_len + ext_prop->data_len + 14;
+ ++t->os_desc->ext_prop_count;
+ list_add_tail(&ext_prop->entry, &t->os_desc->ext_prop);
+ }
+ break;
+ default:
+ pr_vdebug("unknown descriptor: %d\n", type);
+ }
+
+ return length;
+}
+
static inline struct f_fs_opts *ffs_do_functionfs_bind(struct usb_function *f,
struct usb_configuration *c)
{
@@ -2329,7 +2613,7 @@ static int _ffs_func_bind(struct usb_configuration *c,
const int super = gadget_is_superspeed(func->gadget) &&
func->ffs->ss_descs_count;
- int fs_len, hs_len, ret;
+ int fs_len, hs_len, ss_len, ret, i;
/* Make it a single chunk, less management later on */
vla_group(d);
@@ -2341,6 +2625,18 @@ static int _ffs_func_bind(struct usb_configuration *c,
vla_item_with_sz(d, struct usb_descriptor_header *, ss_descs,
super ? ffs->ss_descs_count + 1 : 0);
vla_item_with_sz(d, short, inums, ffs->interfaces_count);
+ vla_item_with_sz(d, struct usb_os_desc_table, os_desc_table,
+ c->cdev->use_os_string ? ffs->interfaces_count : 0);
+ vla_item_with_sz(d, char[16], ext_compat,
+ c->cdev->use_os_string ? ffs->interfaces_count : 0);
+ vla_item_with_sz(d, struct usb_os_desc, os_desc,
+ c->cdev->use_os_string ? ffs->interfaces_count : 0);
+ vla_item_with_sz(d, struct usb_os_desc_ext_prop, ext_prop,
+ ffs->ms_os_descs_ext_prop_count);
+ vla_item_with_sz(d, char, ext_prop_name,
+ ffs->ms_os_descs_ext_prop_name_len);
+ vla_item_with_sz(d, char, ext_prop_data,
+ ffs->ms_os_descs_ext_prop_data_len);
vla_item_with_sz(d, char, raw_descs, ffs->raw_descs_length);
char *vlabuf;
@@ -2351,12 +2647,16 @@ static int _ffs_func_bind(struct usb_configuration *c,
return -ENOTSUPP;
/* Allocate a single chunk, less management later on */
- vlabuf = kmalloc(vla_group_size(d), GFP_KERNEL);
+ vlabuf = kzalloc(vla_group_size(d), GFP_KERNEL);
if (unlikely(!vlabuf))
return -ENOMEM;
- /* Zero */
- memset(vla_ptr(vlabuf, d, eps), 0, d_eps__sz);
+ ffs->ms_os_descs_ext_prop_avail = vla_ptr(vlabuf, d, ext_prop);
+ ffs->ms_os_descs_ext_prop_name_avail =
+ vla_ptr(vlabuf, d, ext_prop_name);
+ ffs->ms_os_descs_ext_prop_data_avail =
+ vla_ptr(vlabuf, d, ext_prop_data);
+
/* Copy descriptors */
memcpy(vla_ptr(vlabuf, d, raw_descs), ffs->raw_descs,
ffs->raw_descs_length);
@@ -2410,12 +2710,16 @@ static int _ffs_func_bind(struct usb_configuration *c,
if (likely(super)) {
func->function.ss_descriptors = vla_ptr(vlabuf, d, ss_descs);
- ret = ffs_do_descs(ffs->ss_descs_count,
+ ss_len = ffs_do_descs(ffs->ss_descs_count,
vla_ptr(vlabuf, d, raw_descs) + fs_len + hs_len,
d_raw_descs__sz - fs_len - hs_len,
__ffs_func_bind_do_descs, func);
- if (unlikely(ret < 0))
+ if (unlikely(ss_len < 0)) {
+ ret = ss_len;
goto error;
+ }
+ } else {
+ ss_len = 0;
}
/*
@@ -2431,6 +2735,28 @@ static int _ffs_func_bind(struct usb_configuration *c,
if (unlikely(ret < 0))
goto error;
+ func->function.os_desc_table = vla_ptr(vlabuf, d, os_desc_table);
+ if (c->cdev->use_os_string)
+ for (i = 0; i < ffs->interfaces_count; ++i) {
+ struct usb_os_desc *desc;
+
+ desc = func->function.os_desc_table[i].os_desc =
+ vla_ptr(vlabuf, d, os_desc) +
+ i * sizeof(struct usb_os_desc);
+ desc->ext_compat_id =
+ vla_ptr(vlabuf, d, ext_compat) + i * 16;
+ INIT_LIST_HEAD(&desc->ext_prop);
+ }
+ ret = ffs_do_os_descs(ffs->ms_os_descs_count,
+ vla_ptr(vlabuf, d, raw_descs) +
+ fs_len + hs_len + ss_len,
+ d_raw_descs__sz - fs_len - hs_len - ss_len,
+ __ffs_func_bind_do_os_desc, func);
+ if (unlikely(ret < 0))
+ goto error;
+ func->function.os_desc_n =
+ c->cdev->use_os_string ? ffs->interfaces_count : 0;
+
/* And we're done */
ffs_event_add(ffs, FUNCTIONFS_BIND);
return 0;
@@ -2901,12 +3227,12 @@ static void *ffs_acquire_dev(const char *dev_name)
ffs_dev = _ffs_find_dev(dev_name);
if (!ffs_dev)
- ffs_dev = ERR_PTR(-ENODEV);
+ ffs_dev = ERR_PTR(-ENOENT);
else if (ffs_dev->mounted)
ffs_dev = ERR_PTR(-EBUSY);
else if (ffs_dev->ffs_acquire_dev_callback &&
ffs_dev->ffs_acquire_dev_callback(ffs_dev))
- ffs_dev = ERR_PTR(-ENODEV);
+ ffs_dev = ERR_PTR(-ENOENT);
else
ffs_dev->mounted = true;
diff --git a/drivers/usb/gadget/f_hid.c b/drivers/usb/gadget/function/f_hid.c
index a95290a1289f..a95290a1289f 100644
--- a/drivers/usb/gadget/f_hid.c
+++ b/drivers/usb/gadget/function/f_hid.c
diff --git a/drivers/usb/gadget/f_loopback.c b/drivers/usb/gadget/function/f_loopback.c
index 4557cd03f0b1..4557cd03f0b1 100644
--- a/drivers/usb/gadget/f_loopback.c
+++ b/drivers/usb/gadget/function/f_loopback.c
diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c
index b96393908860..b96393908860 100644
--- a/drivers/usb/gadget/f_mass_storage.c
+++ b/drivers/usb/gadget/function/f_mass_storage.c
diff --git a/drivers/usb/gadget/f_mass_storage.h b/drivers/usb/gadget/function/f_mass_storage.h
index b4866fcef30b..b4866fcef30b 100644
--- a/drivers/usb/gadget/f_mass_storage.h
+++ b/drivers/usb/gadget/function/f_mass_storage.h
diff --git a/drivers/usb/gadget/f_midi.c b/drivers/usb/gadget/function/f_midi.c
index 807b31c0edc3..807b31c0edc3 100644
--- a/drivers/usb/gadget/f_midi.c
+++ b/drivers/usb/gadget/function/f_midi.c
diff --git a/drivers/usb/gadget/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c
index a9499fd30792..bcdc882cd415 100644
--- a/drivers/usb/gadget/f_ncm.c
+++ b/drivers/usb/gadget/function/f_ncm.c
@@ -68,6 +68,18 @@ struct f_ncm {
* callback and ethernet open/close
*/
spinlock_t lock;
+
+ struct net_device *netdev;
+
+ /* For multi-frame NDP TX */
+ struct sk_buff *skb_tx_data;
+ struct sk_buff *skb_tx_ndp;
+ u16 ndp_dgram_count;
+ bool timer_force_tx;
+ struct tasklet_struct tx_tasklet;
+ struct hrtimer task_timer;
+
+ bool timer_stopping;
};
static inline struct f_ncm *func_to_ncm(struct usb_function *f)
@@ -92,15 +104,20 @@ static inline unsigned ncm_bitrate(struct usb_gadget *g)
* If the host can group frames, allow it to do that, 16K is selected,
* because it's used by default by the current linux host driver
*/
-#define NTB_DEFAULT_IN_SIZE USB_CDC_NCM_NTB_MIN_IN_SIZE
+#define NTB_DEFAULT_IN_SIZE 16384
#define NTB_OUT_SIZE 16384
-/*
- * skbs of size less than that will not be aligned
- * to NCM's dwNtbInMaxSize to save bus bandwidth
+/* Allocation for storing the NDP, 32 should suffice for a
+ * 16k packet. This allows a maximum of 32 * 507 Byte packets to
+ * be transmitted in a single 16kB skb, though when sending full size
+ * packets this limit will be plenty.
+ * Smaller packets are not likely to be trying to maximize the
+ * throughput and will be mstly sending smaller infrequent frames.
*/
+#define TX_MAX_NUM_DPE 32
-#define MAX_TX_NONFIXED (512 * 3)
+/* Delay for the transmit to wait before sending an unfilled NTB frame. */
+#define TX_TIMEOUT_NSECS 300000
#define FORMATS_SUPPORTED (USB_CDC_NCM_NTB16_SUPPORTED | \
USB_CDC_NCM_NTB32_SUPPORTED)
@@ -355,14 +372,15 @@ struct ndp_parser_opts {
u32 ndp_sign;
unsigned nth_size;
unsigned ndp_size;
+ unsigned dpe_size;
unsigned ndplen_align;
/* sizes in u16 units */
unsigned dgram_item_len; /* index or length */
unsigned block_length;
- unsigned fp_index;
+ unsigned ndp_index;
unsigned reserved1;
unsigned reserved2;
- unsigned next_fp_index;
+ unsigned next_ndp_index;
};
#define INIT_NDP16_OPTS { \
@@ -370,13 +388,14 @@ struct ndp_parser_opts {
.ndp_sign = USB_CDC_NCM_NDP16_NOCRC_SIGN, \
.nth_size = sizeof(struct usb_cdc_ncm_nth16), \
.ndp_size = sizeof(struct usb_cdc_ncm_ndp16), \
+ .dpe_size = sizeof(struct usb_cdc_ncm_dpe16), \
.ndplen_align = 4, \
.dgram_item_len = 1, \
.block_length = 1, \
- .fp_index = 1, \
+ .ndp_index = 1, \
.reserved1 = 0, \
.reserved2 = 0, \
- .next_fp_index = 1, \
+ .next_ndp_index = 1, \
}
@@ -385,13 +404,14 @@ struct ndp_parser_opts {
.ndp_sign = USB_CDC_NCM_NDP32_NOCRC_SIGN, \
.nth_size = sizeof(struct usb_cdc_ncm_nth32), \
.ndp_size = sizeof(struct usb_cdc_ncm_ndp32), \
+ .dpe_size = sizeof(struct usb_cdc_ncm_dpe32), \
.ndplen_align = 8, \
.dgram_item_len = 2, \
.block_length = 2, \
- .fp_index = 2, \
+ .ndp_index = 2, \
.reserved1 = 1, \
.reserved2 = 2, \
- .next_fp_index = 2, \
+ .next_ndp_index = 2, \
}
static const struct ndp_parser_opts ndp16_opts = INIT_NDP16_OPTS;
@@ -803,6 +823,8 @@ static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
if (ncm->port.in_ep->driver_data) {
DBG(cdev, "reset ncm\n");
+ ncm->timer_stopping = true;
+ ncm->netdev = NULL;
gether_disconnect(&ncm->port);
ncm_reset_values(ncm);
}
@@ -839,6 +861,8 @@ static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
net = gether_connect(&ncm->port);
if (IS_ERR(net))
return PTR_ERR(net);
+ ncm->netdev = net;
+ ncm->timer_stopping = false;
}
spin_lock(&ncm->lock);
@@ -865,95 +889,232 @@ static int ncm_get_alt(struct usb_function *f, unsigned intf)
return ncm->port.in_ep->driver_data ? 1 : 0;
}
+static struct sk_buff *package_for_tx(struct f_ncm *ncm)
+{
+ __le16 *ntb_iter;
+ struct sk_buff *skb2 = NULL;
+ unsigned ndp_pad;
+ unsigned ndp_index;
+ unsigned new_len;
+
+ const struct ndp_parser_opts *opts = ncm->parser_opts;
+ const int ndp_align = le16_to_cpu(ntb_parameters.wNdpInAlignment);
+ const int dgram_idx_len = 2 * 2 * opts->dgram_item_len;
+
+ /* Stop the timer */
+ hrtimer_try_to_cancel(&ncm->task_timer);
+
+ ndp_pad = ALIGN(ncm->skb_tx_data->len, ndp_align) -
+ ncm->skb_tx_data->len;
+ ndp_index = ncm->skb_tx_data->len + ndp_pad;
+ new_len = ndp_index + dgram_idx_len + ncm->skb_tx_ndp->len;
+
+ /* Set the final BlockLength and wNdpIndex */
+ ntb_iter = (void *) ncm->skb_tx_data->data;
+ /* Increment pointer to BlockLength */
+ ntb_iter += 2 + 1 + 1;
+ put_ncm(&ntb_iter, opts->block_length, new_len);
+ put_ncm(&ntb_iter, opts->ndp_index, ndp_index);
+
+ /* Set the final NDP wLength */
+ new_len = opts->ndp_size +
+ (ncm->ndp_dgram_count * dgram_idx_len);
+ ncm->ndp_dgram_count = 0;
+ /* Increment from start to wLength */
+ ntb_iter = (void *) ncm->skb_tx_ndp->data;
+ ntb_iter += 2;
+ put_unaligned_le16(new_len, ntb_iter);
+
+ /* Merge the skbs */
+ swap(skb2, ncm->skb_tx_data);
+ if (ncm->skb_tx_data) {
+ dev_kfree_skb_any(ncm->skb_tx_data);
+ ncm->skb_tx_data = NULL;
+ }
+
+ /* Insert NDP alignment. */
+ ntb_iter = (void *) skb_put(skb2, ndp_pad);
+ memset(ntb_iter, 0, ndp_pad);
+
+ /* Copy NTB across. */
+ ntb_iter = (void *) skb_put(skb2, ncm->skb_tx_ndp->len);
+ memcpy(ntb_iter, ncm->skb_tx_ndp->data, ncm->skb_tx_ndp->len);
+ dev_kfree_skb_any(ncm->skb_tx_ndp);
+ ncm->skb_tx_ndp = NULL;
+
+ /* Insert zero'd datagram. */
+ ntb_iter = (void *) skb_put(skb2, dgram_idx_len);
+ memset(ntb_iter, 0, dgram_idx_len);
+
+ return skb2;
+}
+
static struct sk_buff *ncm_wrap_ntb(struct gether *port,
struct sk_buff *skb)
{
struct f_ncm *ncm = func_to_ncm(&port->func);
- struct sk_buff *skb2;
+ struct sk_buff *skb2 = NULL;
int ncb_len = 0;
- __le16 *tmp;
- int div;
- int rem;
- int pad;
- int ndp_align;
- int ndp_pad;
+ __le16 *ntb_data;
+ __le16 *ntb_ndp;
+ int dgram_pad;
+
unsigned max_size = ncm->port.fixed_in_len;
const struct ndp_parser_opts *opts = ncm->parser_opts;
- unsigned crc_len = ncm->is_crc ? sizeof(uint32_t) : 0;
-
- div = le16_to_cpu(ntb_parameters.wNdpInDivisor);
- rem = le16_to_cpu(ntb_parameters.wNdpInPayloadRemainder);
- ndp_align = le16_to_cpu(ntb_parameters.wNdpInAlignment);
+ const int ndp_align = le16_to_cpu(ntb_parameters.wNdpInAlignment);
+ const int div = le16_to_cpu(ntb_parameters.wNdpInDivisor);
+ const int rem = le16_to_cpu(ntb_parameters.wNdpInPayloadRemainder);
+ const int dgram_idx_len = 2 * 2 * opts->dgram_item_len;
- ncb_len += opts->nth_size;
- ndp_pad = ALIGN(ncb_len, ndp_align) - ncb_len;
- ncb_len += ndp_pad;
- ncb_len += opts->ndp_size;
- ncb_len += 2 * 2 * opts->dgram_item_len; /* Datagram entry */
- ncb_len += 2 * 2 * opts->dgram_item_len; /* Zero datagram entry */
- pad = ALIGN(ncb_len, div) + rem - ncb_len;
- ncb_len += pad;
-
- if (ncb_len + skb->len + crc_len > max_size) {
- dev_kfree_skb_any(skb);
+ if (!skb && !ncm->skb_tx_data)
return NULL;
- }
- skb2 = skb_copy_expand(skb, ncb_len,
- max_size - skb->len - ncb_len - crc_len,
- GFP_ATOMIC);
- dev_kfree_skb_any(skb);
- if (!skb2)
- return NULL;
+ if (skb) {
+ /* Add the CRC if required up front */
+ if (ncm->is_crc) {
+ uint32_t crc;
+ __le16 *crc_pos;
+
+ crc = ~crc32_le(~0,
+ skb->data,
+ skb->len);
+ crc_pos = (void *) skb_put(skb, sizeof(uint32_t));
+ put_unaligned_le32(crc, crc_pos);
+ }
- skb = skb2;
+ /* If the new skb is too big for the current NCM NTB then
+ * set the current stored skb to be sent now and clear it
+ * ready for new data.
+ * NOTE: Assume maximum align for speed of calculation.
+ */
+ if (ncm->skb_tx_data
+ && (ncm->ndp_dgram_count >= TX_MAX_NUM_DPE
+ || (ncm->skb_tx_data->len +
+ div + rem + skb->len +
+ ncm->skb_tx_ndp->len + ndp_align + (2 * dgram_idx_len))
+ > max_size)) {
+ skb2 = package_for_tx(ncm);
+ if (!skb2)
+ goto err;
+ }
- tmp = (void *) skb_push(skb, ncb_len);
- memset(tmp, 0, ncb_len);
+ if (!ncm->skb_tx_data) {
+ ncb_len = opts->nth_size;
+ dgram_pad = ALIGN(ncb_len, div) + rem - ncb_len;
+ ncb_len += dgram_pad;
- put_unaligned_le32(opts->nth_sign, tmp); /* dwSignature */
- tmp += 2;
- /* wHeaderLength */
- put_unaligned_le16(opts->nth_size, tmp++);
- tmp++; /* skip wSequence */
- put_ncm(&tmp, opts->block_length, skb->len); /* (d)wBlockLength */
- /* (d)wFpIndex */
- /* the first pointer is right after the NTH + align */
- put_ncm(&tmp, opts->fp_index, opts->nth_size + ndp_pad);
+ /* Create a new skb for the NTH and datagrams. */
+ ncm->skb_tx_data = alloc_skb(max_size, GFP_ATOMIC);
+ if (!ncm->skb_tx_data)
+ goto err;
- tmp = (void *)tmp + ndp_pad;
+ ntb_data = (void *) skb_put(ncm->skb_tx_data, ncb_len);
+ memset(ntb_data, 0, ncb_len);
+ /* dwSignature */
+ put_unaligned_le32(opts->nth_sign, ntb_data);
+ ntb_data += 2;
+ /* wHeaderLength */
+ put_unaligned_le16(opts->nth_size, ntb_data++);
+
+ /* Allocate an skb for storing the NDP,
+ * TX_MAX_NUM_DPE should easily suffice for a
+ * 16k packet.
+ */
+ ncm->skb_tx_ndp = alloc_skb((int)(opts->ndp_size
+ + opts->dpe_size
+ * TX_MAX_NUM_DPE),
+ GFP_ATOMIC);
+ if (!ncm->skb_tx_ndp)
+ goto err;
+ ntb_ndp = (void *) skb_put(ncm->skb_tx_ndp,
+ opts->ndp_size);
+ memset(ntb_ndp, 0, ncb_len);
+ /* dwSignature */
+ put_unaligned_le32(ncm->ndp_sign, ntb_ndp);
+ ntb_ndp += 2;
- /* NDP */
- put_unaligned_le32(ncm->ndp_sign, tmp); /* dwSignature */
- tmp += 2;
- /* wLength */
- put_unaligned_le16(ncb_len - opts->nth_size - pad, tmp++);
+ /* There is always a zeroed entry */
+ ncm->ndp_dgram_count = 1;
- tmp += opts->reserved1;
- tmp += opts->next_fp_index; /* skip reserved (d)wNextFpIndex */
- tmp += opts->reserved2;
+ /* Note: we skip opts->next_ndp_index */
+ }
- if (ncm->is_crc) {
- uint32_t crc;
+ /* Delay the timer. */
+ hrtimer_start(&ncm->task_timer,
+ ktime_set(0, TX_TIMEOUT_NSECS),
+ HRTIMER_MODE_REL);
+
+ /* Add the datagram position entries */
+ ntb_ndp = (void *) skb_put(ncm->skb_tx_ndp, dgram_idx_len);
+ memset(ntb_ndp, 0, dgram_idx_len);
+
+ ncb_len = ncm->skb_tx_data->len;
+ dgram_pad = ALIGN(ncb_len, div) + rem - ncb_len;
+ ncb_len += dgram_pad;
+
+ /* (d)wDatagramIndex */
+ put_ncm(&ntb_ndp, opts->dgram_item_len, ncb_len);
+ /* (d)wDatagramLength */
+ put_ncm(&ntb_ndp, opts->dgram_item_len, skb->len);
+ ncm->ndp_dgram_count++;
+
+ /* Add the new data to the skb */
+ ntb_data = (void *) skb_put(ncm->skb_tx_data, dgram_pad);
+ memset(ntb_data, 0, dgram_pad);
+ ntb_data = (void *) skb_put(ncm->skb_tx_data, skb->len);
+ memcpy(ntb_data, skb->data, skb->len);
+ dev_kfree_skb_any(skb);
+ skb = NULL;
- crc = ~crc32_le(~0,
- skb->data + ncb_len,
- skb->len - ncb_len);
- put_unaligned_le32(crc, skb->data + skb->len);
- skb_put(skb, crc_len);
+ } else if (ncm->skb_tx_data && ncm->timer_force_tx) {
+ /* If the tx was requested because of a timeout then send */
+ skb2 = package_for_tx(ncm);
+ if (!skb2)
+ goto err;
}
- /* (d)wDatagramIndex[0] */
- put_ncm(&tmp, opts->dgram_item_len, ncb_len);
- /* (d)wDatagramLength[0] */
- put_ncm(&tmp, opts->dgram_item_len, skb->len - ncb_len);
- /* (d)wDatagramIndex[1] and (d)wDatagramLength[1] already zeroed */
+ return skb2;
- if (skb->len > MAX_TX_NONFIXED)
- memset(skb_put(skb, max_size - skb->len),
- 0, max_size - skb->len);
+err:
+ ncm->netdev->stats.tx_dropped++;
+
+ if (skb)
+ dev_kfree_skb_any(skb);
+ if (ncm->skb_tx_data)
+ dev_kfree_skb_any(ncm->skb_tx_data);
+ if (ncm->skb_tx_ndp)
+ dev_kfree_skb_any(ncm->skb_tx_ndp);
- return skb;
+ return NULL;
+}
+
+/*
+ * This transmits the NTB if there are frames waiting.
+ */
+static void ncm_tx_tasklet(unsigned long data)
+{
+ struct f_ncm *ncm = (void *)data;
+
+ if (ncm->timer_stopping)
+ return;
+
+ /* Only send if data is available. */
+ if (ncm->skb_tx_data) {
+ ncm->timer_force_tx = true;
+ ncm->netdev->netdev_ops->ndo_start_xmit(NULL, ncm->netdev);
+ ncm->timer_force_tx = false;
+ }
+}
+
+/*
+ * The transmit should only be run if no skb data has been sent
+ * for a certain duration.
+ */
+static enum hrtimer_restart ncm_tx_timeout(struct hrtimer *data)
+{
+ struct f_ncm *ncm = container_of(data, struct f_ncm, task_timer);
+ tasklet_schedule(&ncm->tx_tasklet);
+ return HRTIMER_NORESTART;
}
static int ncm_unwrap_ntb(struct gether *port,
@@ -963,6 +1124,7 @@ static int ncm_unwrap_ntb(struct gether *port,
struct f_ncm *ncm = func_to_ncm(&port->func);
__le16 *tmp = (void *) skb->data;
unsigned index, index2;
+ int ndp_index;
unsigned dg_len, dg_len2;
unsigned ndp_len;
struct sk_buff *skb2;
@@ -995,91 +1157,101 @@ static int ncm_unwrap_ntb(struct gether *port,
goto err;
}
- index = get_ncm(&tmp, opts->fp_index);
- /* NCM 3.2 */
- if (((index % 4) != 0) && (index < opts->nth_size)) {
- INFO(port->func.config->cdev, "Bad index: %x\n",
- index);
- goto err;
- }
-
- /* walk through NDP */
- tmp = ((void *)skb->data) + index;
- if (get_unaligned_le32(tmp) != ncm->ndp_sign) {
- INFO(port->func.config->cdev, "Wrong NDP SIGN\n");
- goto err;
- }
- tmp += 2;
-
- ndp_len = get_unaligned_le16(tmp++);
- /*
- * NCM 3.3.1
- * entry is 2 items
- * item size is 16/32 bits, opts->dgram_item_len * 2 bytes
- * minimal: struct usb_cdc_ncm_ndpX + normal entry + zero entry
- */
- if ((ndp_len < opts->ndp_size + 2 * 2 * (opts->dgram_item_len * 2))
- || (ndp_len % opts->ndplen_align != 0)) {
- INFO(port->func.config->cdev, "Bad NDP length: %x\n", ndp_len);
- goto err;
- }
- tmp += opts->reserved1;
- tmp += opts->next_fp_index; /* skip reserved (d)wNextFpIndex */
- tmp += opts->reserved2;
-
- ndp_len -= opts->ndp_size;
- index2 = get_ncm(&tmp, opts->dgram_item_len);
- dg_len2 = get_ncm(&tmp, opts->dgram_item_len);
- dgram_counter = 0;
+ ndp_index = get_ncm(&tmp, opts->ndp_index);
+ /* Run through all the NDP's in the NTB */
do {
- index = index2;
- dg_len = dg_len2;
- if (dg_len < 14 + crc_len) { /* ethernet header + crc */
- INFO(port->func.config->cdev, "Bad dgram length: %x\n",
- dg_len);
+ /* NCM 3.2 */
+ if (((ndp_index % 4) != 0) &&
+ (ndp_index < opts->nth_size)) {
+ INFO(port->func.config->cdev, "Bad index: %#X\n",
+ ndp_index);
goto err;
}
- if (ncm->is_crc) {
- uint32_t crc, crc2;
-
- crc = get_unaligned_le32(skb->data +
- index + dg_len - crc_len);
- crc2 = ~crc32_le(~0,
- skb->data + index,
- dg_len - crc_len);
- if (crc != crc2) {
- INFO(port->func.config->cdev, "Bad CRC\n");
- goto err;
- }
+
+ /* walk through NDP */
+ tmp = (void *)(skb->data + ndp_index);
+ if (get_unaligned_le32(tmp) != ncm->ndp_sign) {
+ INFO(port->func.config->cdev, "Wrong NDP SIGN\n");
+ goto err;
}
+ tmp += 2;
+ ndp_len = get_unaligned_le16(tmp++);
+ /*
+ * NCM 3.3.1
+ * entry is 2 items
+ * item size is 16/32 bits, opts->dgram_item_len * 2 bytes
+ * minimal: struct usb_cdc_ncm_ndpX + normal entry + zero entry
+ * Each entry is a dgram index and a dgram length.
+ */
+ if ((ndp_len < opts->ndp_size
+ + 2 * 2 * (opts->dgram_item_len * 2))
+ || (ndp_len % opts->ndplen_align != 0)) {
+ INFO(port->func.config->cdev, "Bad NDP length: %#X\n",
+ ndp_len);
+ goto err;
+ }
+ tmp += opts->reserved1;
+ /* Check for another NDP (d)wNextNdpIndex */
+ ndp_index = get_ncm(&tmp, opts->next_ndp_index);
+ tmp += opts->reserved2;
+
+ ndp_len -= opts->ndp_size;
index2 = get_ncm(&tmp, opts->dgram_item_len);
dg_len2 = get_ncm(&tmp, opts->dgram_item_len);
+ dgram_counter = 0;
+
+ do {
+ index = index2;
+ dg_len = dg_len2;
+ if (dg_len < 14 + crc_len) { /* ethernet hdr + crc */
+ INFO(port->func.config->cdev,
+ "Bad dgram length: %#X\n", dg_len);
+ goto err;
+ }
+ if (ncm->is_crc) {
+ uint32_t crc, crc2;
+
+ crc = get_unaligned_le32(skb->data +
+ index + dg_len -
+ crc_len);
+ crc2 = ~crc32_le(~0,
+ skb->data + index,
+ dg_len - crc_len);
+ if (crc != crc2) {
+ INFO(port->func.config->cdev,
+ "Bad CRC\n");
+ goto err;
+ }
+ }
+
+ index2 = get_ncm(&tmp, opts->dgram_item_len);
+ dg_len2 = get_ncm(&tmp, opts->dgram_item_len);
- if (index2 == 0 || dg_len2 == 0) {
- skb2 = skb;
- } else {
- skb2 = skb_clone(skb, GFP_ATOMIC);
+ /*
+ * Copy the data into a new skb.
+ * This ensures the truesize is correct
+ */
+ skb2 = netdev_alloc_skb_ip_align(ncm->netdev,
+ dg_len - crc_len);
if (skb2 == NULL)
goto err;
- }
+ memcpy(skb_put(skb2, dg_len - crc_len),
+ skb->data + index, dg_len - crc_len);
- if (!skb_pull(skb2, index)) {
- ret = -EOVERFLOW;
- goto err;
- }
+ skb_queue_tail(list, skb2);
- skb_trim(skb2, dg_len - crc_len);
- skb_queue_tail(list, skb2);
+ ndp_len -= 2 * (opts->dgram_item_len * 2);
- ndp_len -= 2 * (opts->dgram_item_len * 2);
+ dgram_counter++;
- dgram_counter++;
+ if (index2 == 0 || dg_len2 == 0)
+ break;
+ } while (ndp_len > 2 * (opts->dgram_item_len * 2));
+ } while (ndp_index);
- if (index2 == 0 || dg_len2 == 0)
- break;
- } while (ndp_len > 2 * (opts->dgram_item_len * 2)); /* zero entry */
+ dev_kfree_skb_any(skb);
VDBG(port->func.config->cdev,
"Parsed NTB with %d frames\n", dgram_counter);
@@ -1097,8 +1269,11 @@ static void ncm_disable(struct usb_function *f)
DBG(cdev, "ncm deactivated\n");
- if (ncm->port.in_ep->driver_data)
+ if (ncm->port.in_ep->driver_data) {
+ ncm->timer_stopping = true;
+ ncm->netdev = NULL;
gether_disconnect(&ncm->port);
+ }
if (ncm->notify->driver_data) {
usb_ep_disable(ncm->notify);
@@ -1267,6 +1442,10 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
ncm->port.open = ncm_open;
ncm->port.close = ncm_close;
+ tasklet_init(&ncm->tx_tasklet, ncm_tx_tasklet, (unsigned long) ncm);
+ hrtimer_init(&ncm->task_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ ncm->task_timer.function = ncm_tx_timeout;
+
DBG(cdev, "CDC Network: %s speed IN/%s OUT/%s NOTIFY/%s\n",
gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
ncm->port.in_ep->name, ncm->port.out_ep->name,
@@ -1380,6 +1559,10 @@ static void ncm_unbind(struct usb_configuration *c, struct usb_function *f)
DBG(c->cdev, "ncm unbind\n");
+ hrtimer_cancel(&ncm->task_timer);
+ tasklet_kill(&ncm->tx_tasklet);
+
+ ncm_string_defs[0].id = 0;
usb_free_all_descriptors(f);
kfree(ncm->notify_req->buf);
@@ -1416,6 +1599,7 @@ static struct usb_function *ncm_alloc(struct usb_function_instance *fi)
ncm->port.ioport = netdev_priv(opts->net);
mutex_unlock(&opts->lock);
ncm->port.is_fixed = true;
+ ncm->port.supports_multi_frame = true;
ncm->port.func.name = "cdc_network";
/* descriptors are per-instance copies */
diff --git a/drivers/usb/gadget/f_obex.c b/drivers/usb/gadget/function/f_obex.c
index aebae1853bce..aebae1853bce 100644
--- a/drivers/usb/gadget/f_obex.c
+++ b/drivers/usb/gadget/function/f_obex.c
diff --git a/drivers/usb/gadget/f_phonet.c b/drivers/usb/gadget/function/f_phonet.c
index f2b781773eed..f2b781773eed 100644
--- a/drivers/usb/gadget/f_phonet.c
+++ b/drivers/usb/gadget/function/f_phonet.c
diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c
index 9c41e9515b8e..ddb09dc6d1f2 100644
--- a/drivers/usb/gadget/f_rndis.c
+++ b/drivers/usb/gadget/function/f_rndis.c
@@ -727,6 +727,10 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
rndis_control_intf.bInterfaceNumber = status;
rndis_union_desc.bMasterInterface0 = status;
+ if (cdev->use_os_string)
+ f->os_desc_table[0].if_id =
+ rndis_iad_descriptor.bFirstInterface;
+
status = usb_interface_id(c, f);
if (status < 0)
goto fail;
diff --git a/drivers/usb/gadget/f_serial.c b/drivers/usb/gadget/function/f_serial.c
index 9ecbcbf36a45..9ecbcbf36a45 100644
--- a/drivers/usb/gadget/f_serial.c
+++ b/drivers/usb/gadget/function/f_serial.c
diff --git a/drivers/usb/gadget/f_sourcesink.c b/drivers/usb/gadget/function/f_sourcesink.c
index d3cd52db78fe..d3cd52db78fe 100644
--- a/drivers/usb/gadget/f_sourcesink.c
+++ b/drivers/usb/gadget/function/f_sourcesink.c
diff --git a/drivers/usb/gadget/f_subset.c b/drivers/usb/gadget/function/f_subset.c
index 1ea8baf33333..1ea8baf33333 100644
--- a/drivers/usb/gadget/f_subset.c
+++ b/drivers/usb/gadget/function/f_subset.c
diff --git a/drivers/usb/gadget/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c
index 2b4c82d84bfc..2b4c82d84bfc 100644
--- a/drivers/usb/gadget/f_uac1.c
+++ b/drivers/usb/gadget/function/f_uac1.c
diff --git a/drivers/usb/gadget/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
index 6261db4a9910..3ed89ecc8d6d 100644
--- a/drivers/usb/gadget/f_uac2.c
+++ b/drivers/usb/gadget/function/f_uac2.c
@@ -348,14 +348,34 @@ static int uac2_pcm_open(struct snd_pcm_substream *substream)
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
spin_lock_init(&uac2->p_prm.lock);
runtime->hw.rate_min = p_srate;
- runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE; /* ! p_ssize ! */
+ switch (p_ssize) {
+ case 3:
+ runtime->hw.formats = SNDRV_PCM_FMTBIT_S24_3LE;
+ break;
+ case 4:
+ runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE;
+ break;
+ default:
+ runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE;
+ break;
+ }
runtime->hw.channels_min = num_channels(p_chmask);
runtime->hw.period_bytes_min = 2 * uac2->p_prm.max_psize
/ runtime->hw.periods_min;
} else {
spin_lock_init(&uac2->c_prm.lock);
runtime->hw.rate_min = c_srate;
- runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE; /* ! c_ssize ! */
+ switch (c_ssize) {
+ case 3:
+ runtime->hw.formats = SNDRV_PCM_FMTBIT_S24_3LE;
+ break;
+ case 4:
+ runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE;
+ break;
+ default:
+ runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE;
+ break;
+ }
runtime->hw.channels_min = num_channels(c_chmask);
runtime->hw.period_bytes_min = 2 * uac2->c_prm.max_psize
/ runtime->hw.periods_min;
diff --git a/drivers/usb/gadget/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c
index e2a1f50bd93c..e2a1f50bd93c 100644
--- a/drivers/usb/gadget/f_uvc.c
+++ b/drivers/usb/gadget/function/f_uvc.c
diff --git a/drivers/usb/gadget/f_uvc.h b/drivers/usb/gadget/function/f_uvc.h
index ec52752f7326..ec52752f7326 100644
--- a/drivers/usb/gadget/f_uvc.h
+++ b/drivers/usb/gadget/function/f_uvc.h
diff --git a/drivers/usb/gadget/g_zero.h b/drivers/usb/gadget/function/g_zero.h
index 15f180904f8a..15f180904f8a 100644
--- a/drivers/usb/gadget/g_zero.h
+++ b/drivers/usb/gadget/function/g_zero.h
diff --git a/drivers/usb/gadget/ndis.h b/drivers/usb/gadget/function/ndis.h
index a19f72dec0cd..a19f72dec0cd 100644
--- a/drivers/usb/gadget/ndis.h
+++ b/drivers/usb/gadget/function/ndis.h
diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/function/rndis.c
index 95d2324f6977..95d2324f6977 100644
--- a/drivers/usb/gadget/rndis.c
+++ b/drivers/usb/gadget/function/rndis.c
diff --git a/drivers/usb/gadget/rndis.h b/drivers/usb/gadget/function/rndis.h
index 0f4abb4c3775..0f4abb4c3775 100644
--- a/drivers/usb/gadget/rndis.h
+++ b/drivers/usb/gadget/function/rndis.h
diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/function/storage_common.c
index 648f9e489b39..648f9e489b39 100644
--- a/drivers/usb/gadget/storage_common.c
+++ b/drivers/usb/gadget/function/storage_common.c
diff --git a/drivers/usb/gadget/storage_common.h b/drivers/usb/gadget/function/storage_common.h
index 70c891469f57..70c891469f57 100644
--- a/drivers/usb/gadget/storage_common.h
+++ b/drivers/usb/gadget/function/storage_common.h
diff --git a/drivers/usb/gadget/u_ecm.h b/drivers/usb/gadget/function/u_ecm.h
index 262cc03cc2c0..262cc03cc2c0 100644
--- a/drivers/usb/gadget/u_ecm.h
+++ b/drivers/usb/gadget/function/u_ecm.h
diff --git a/drivers/usb/gadget/u_eem.h b/drivers/usb/gadget/function/u_eem.h
index e3ae97874c4f..e3ae97874c4f 100644
--- a/drivers/usb/gadget/u_eem.h
+++ b/drivers/usb/gadget/function/u_eem.h
diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/function/u_ether.c
index 97b027724ee7..d50adda913cf 100644
--- a/drivers/usb/gadget/u_ether.c
+++ b/drivers/usb/gadget/function/u_ether.c
@@ -483,7 +483,7 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
struct net_device *net)
{
struct eth_dev *dev = netdev_priv(net);
- int length = skb->len;
+ int length = 0;
int retval;
struct usb_request *req = NULL;
unsigned long flags;
@@ -500,13 +500,13 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
}
spin_unlock_irqrestore(&dev->lock, flags);
- if (!in) {
+ if (skb && !in) {
dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
/* apply outgoing CDC or RNDIS filters */
- if (!is_promisc(cdc_filter)) {
+ if (skb && !is_promisc(cdc_filter)) {
u8 *dest = skb->data;
if (is_multicast_ether_addr(dest)) {
@@ -557,11 +557,17 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
if (dev->port_usb)
skb = dev->wrap(dev->port_usb, skb);
spin_unlock_irqrestore(&dev->lock, flags);
- if (!skb)
+ if (!skb) {
+ /* Multi frame CDC protocols may store the frame for
+ * later which is not a dropped frame.
+ */
+ if (dev->port_usb->supports_multi_frame)
+ goto multiframe;
goto drop;
-
- length = skb->len;
+ }
}
+
+ length = skb->len;
req->buf = skb->data;
req->context = skb;
req->complete = tx_complete;
@@ -604,6 +610,7 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
dev_kfree_skb_any(skb);
drop:
dev->net->stats.tx_dropped++;
+multiframe:
spin_lock_irqsave(&dev->req_lock, flags);
if (list_empty(&dev->tx_reqs))
netif_start_queue(net);
diff --git a/drivers/usb/gadget/u_ether.h b/drivers/usb/gadget/function/u_ether.h
index 0f0290acea7e..334b38947916 100644
--- a/drivers/usb/gadget/u_ether.h
+++ b/drivers/usb/gadget/function/u_ether.h
@@ -18,6 +18,7 @@
#include <linux/if_ether.h>
#include <linux/usb/composite.h>
#include <linux/usb/cdc.h>
+#include <linux/netdevice.h>
#include "gadget_chips.h"
@@ -74,6 +75,7 @@ struct gether {
bool is_fixed;
u32 fixed_out_len;
u32 fixed_in_len;
+ bool supports_multi_frame;
struct sk_buff *(*wrap)(struct gether *port,
struct sk_buff *skb);
int (*unwrap)(struct gether *port,
diff --git a/drivers/usb/gadget/u_ether_configfs.h b/drivers/usb/gadget/function/u_ether_configfs.h
index bcbd30146cfd..bcbd30146cfd 100644
--- a/drivers/usb/gadget/u_ether_configfs.h
+++ b/drivers/usb/gadget/function/u_ether_configfs.h
diff --git a/drivers/usb/gadget/u_fs.h b/drivers/usb/gadget/function/u_fs.h
index bf0ba375d459..63d6e71569c1 100644
--- a/drivers/usb/gadget/u_fs.h
+++ b/drivers/usb/gadget/function/u_fs.h
@@ -216,6 +216,13 @@ struct ffs_data {
unsigned fs_descs_count;
unsigned hs_descs_count;
unsigned ss_descs_count;
+ unsigned ms_os_descs_count;
+ unsigned ms_os_descs_ext_prop_count;
+ unsigned ms_os_descs_ext_prop_name_len;
+ unsigned ms_os_descs_ext_prop_data_len;
+ void *ms_os_descs_ext_prop_avail;
+ void *ms_os_descs_ext_prop_name_avail;
+ void *ms_os_descs_ext_prop_data_avail;
unsigned short strings_count;
unsigned short interfaces_count;
diff --git a/drivers/usb/gadget/u_gether.h b/drivers/usb/gadget/function/u_gether.h
index d4078426ba5d..d4078426ba5d 100644
--- a/drivers/usb/gadget/u_gether.h
+++ b/drivers/usb/gadget/function/u_gether.h
diff --git a/drivers/usb/gadget/u_ncm.h b/drivers/usb/gadget/function/u_ncm.h
index ce0f3a78ca13..ce0f3a78ca13 100644
--- a/drivers/usb/gadget/u_ncm.h
+++ b/drivers/usb/gadget/function/u_ncm.h
diff --git a/drivers/usb/gadget/u_phonet.h b/drivers/usb/gadget/function/u_phonet.h
index 98ced18779ea..98ced18779ea 100644
--- a/drivers/usb/gadget/u_phonet.h
+++ b/drivers/usb/gadget/function/u_phonet.h
diff --git a/drivers/usb/gadget/u_rndis.h b/drivers/usb/gadget/function/u_rndis.h
index e902aa42a297..e902aa42a297 100644
--- a/drivers/usb/gadget/u_rndis.h
+++ b/drivers/usb/gadget/function/u_rndis.h
diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/function/u_serial.c
index ad0aca812002..ad0aca812002 100644
--- a/drivers/usb/gadget/u_serial.c
+++ b/drivers/usb/gadget/function/u_serial.c
diff --git a/drivers/usb/gadget/u_serial.h b/drivers/usb/gadget/function/u_serial.h
index c20210c0babd..c20210c0babd 100644
--- a/drivers/usb/gadget/u_serial.h
+++ b/drivers/usb/gadget/function/u_serial.h
diff --git a/drivers/usb/gadget/u_uac1.c b/drivers/usb/gadget/function/u_uac1.c
index 7a55fea43430..7a55fea43430 100644
--- a/drivers/usb/gadget/u_uac1.c
+++ b/drivers/usb/gadget/function/u_uac1.c
diff --git a/drivers/usb/gadget/u_uac1.h b/drivers/usb/gadget/function/u_uac1.h
index 18c2e729faf6..18c2e729faf6 100644
--- a/drivers/usb/gadget/u_uac1.h
+++ b/drivers/usb/gadget/function/u_uac1.h
diff --git a/drivers/usb/gadget/uvc.h b/drivers/usb/gadget/function/uvc.h
index 7a9111de8054..7a9111de8054 100644
--- a/drivers/usb/gadget/uvc.h
+++ b/drivers/usb/gadget/function/uvc.h
diff --git a/drivers/usb/gadget/uvc_queue.c b/drivers/usb/gadget/function/uvc_queue.c
index 1c29bc954db9..1c29bc954db9 100644
--- a/drivers/usb/gadget/uvc_queue.c
+++ b/drivers/usb/gadget/function/uvc_queue.c
diff --git a/drivers/usb/gadget/uvc_queue.h b/drivers/usb/gadget/function/uvc_queue.h
index 8e76ce982f1e..8e76ce982f1e 100644
--- a/drivers/usb/gadget/uvc_queue.h
+++ b/drivers/usb/gadget/function/uvc_queue.h
diff --git a/drivers/usb/gadget/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c
index ad48e81155e2..ad48e81155e2 100644
--- a/drivers/usb/gadget/uvc_v4l2.c
+++ b/drivers/usb/gadget/function/uvc_v4l2.c
diff --git a/drivers/usb/gadget/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c
index 71e896d4c5ae..71e896d4c5ae 100644
--- a/drivers/usb/gadget/uvc_video.c
+++ b/drivers/usb/gadget/function/uvc_video.c
diff --git a/drivers/usb/gadget/legacy/Kconfig b/drivers/usb/gadget/legacy/Kconfig
new file mode 100644
index 000000000000..aa376f006333
--- /dev/null
+++ b/drivers/usb/gadget/legacy/Kconfig
@@ -0,0 +1,475 @@
+#
+# USB Gadget support on a system involves
+# (a) a peripheral controller, and
+# (b) the gadget driver using it.
+#
+# NOTE: Gadget support ** DOES NOT ** depend on host-side CONFIG_USB !!
+#
+# - Host systems (like PCs) need CONFIG_USB (with "A" jacks).
+# - Peripherals (like PDAs) need CONFIG_USB_GADGET (with "B" jacks).
+# - Some systems have both kinds of controllers.
+#
+# With help from a special transceiver and a "Mini-AB" jack, systems with
+# both kinds of controller can also support "USB On-the-Go" (CONFIG_USB_OTG).
+#
+
+config USB_ZERO
+ tristate "Gadget Zero (DEVELOPMENT)"
+ select USB_LIBCOMPOSITE
+ select USB_F_SS_LB
+ help
+ Gadget Zero is a two-configuration device. It either sinks and
+ sources bulk data; or it loops back a configurable number of
+ transfers. It also implements control requests, for "chapter 9"
+ conformance. The driver needs only two bulk-capable endpoints, so
+ it can work on top of most device-side usb controllers. It's
+ useful for testing, and is also a working example showing how
+ USB "gadget drivers" can be written.
+
+ Make this be the first driver you try using on top of any new
+ USB peripheral controller driver. Then you can use host-side
+ test software, like the "usbtest" driver, to put your hardware
+ and its driver through a basic set of functional tests.
+
+ Gadget Zero also works with the host-side "usb-skeleton" driver,
+ and with many kinds of host-side test software. You may need
+ to tweak product and vendor IDs before host software knows about
+ this device, and arrange to select an appropriate configuration.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "g_zero".
+
+config USB_ZERO_HNPTEST
+ boolean "HNP Test Device"
+ depends on USB_ZERO && USB_OTG
+ help
+ You can configure this device to enumerate using the device
+ identifiers of the USB-OTG test device. That means that when
+ this gadget connects to another OTG device, with this one using
+ the "B-Peripheral" role, that device will use HNP to let this
+ one serve as the USB host instead (in the "B-Host" role).
+
+config USB_AUDIO
+ tristate "Audio Gadget"
+ depends on SND
+ select USB_LIBCOMPOSITE
+ select SND_PCM
+ help
+ This Gadget Audio driver is compatible with USB Audio Class
+ specification 2.0. It implements 1 AudioControl interface,
+ 1 AudioStreaming Interface each for USB-OUT and USB-IN.
+ Number of channels, sample rate and sample size can be
+ specified as module parameters.
+ This driver doesn't expect any real Audio codec to be present
+ on the device - the audio streams are simply sinked to and
+ sourced from a virtual ALSA sound card created. The user-space
+ application may choose to do whatever it wants with the data
+ received from the USB Host and choose to provide whatever it
+ wants as audio data to the USB Host.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "g_audio".
+
+config GADGET_UAC1
+ bool "UAC 1.0 (Legacy)"
+ depends on USB_AUDIO
+ help
+ If you instead want older UAC Spec-1.0 driver that also has audio
+ paths hardwired to the Audio codec chip on-board and doesn't work
+ without one.
+
+config USB_ETH
+ tristate "Ethernet Gadget (with CDC Ethernet support)"
+ depends on NET
+ select USB_LIBCOMPOSITE
+ select USB_U_ETHER
+ select USB_F_ECM
+ select USB_F_SUBSET
+ select CRC32
+ help
+ This driver implements Ethernet style communication, in one of
+ several ways:
+
+ - The "Communication Device Class" (CDC) Ethernet Control Model.
+ That protocol is often avoided with pure Ethernet adapters, in
+ favor of simpler vendor-specific hardware, but is widely
+ supported by firmware for smart network devices.
+
+ - On hardware can't implement that protocol, a simple CDC subset
+ is used, placing fewer demands on USB.
+
+ - CDC Ethernet Emulation Model (EEM) is a newer standard that has
+ a simpler interface that can be used by more USB hardware.
+
+ RNDIS support is an additional option, more demanding than than
+ subset.
+
+ Within the USB device, this gadget driver exposes a network device
+ "usbX", where X depends on what other networking devices you have.
+ Treat it like a two-node Ethernet link: host, and gadget.
+
+ The Linux-USB host-side "usbnet" driver interoperates with this
+ driver, so that deep I/O queues can be supported. On 2.4 kernels,
+ use "CDCEther" instead, if you're using the CDC option. That CDC
+ mode should also interoperate with standard CDC Ethernet class
+ drivers on other host operating systems.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "g_ether".
+
+config USB_ETH_RNDIS
+ bool "RNDIS support"
+ depends on USB_ETH
+ select USB_LIBCOMPOSITE
+ select USB_F_RNDIS
+ default y
+ help
+ Microsoft Windows XP bundles the "Remote NDIS" (RNDIS) protocol,
+ and Microsoft provides redistributable binary RNDIS drivers for
+ older versions of Windows.
+
+ If you say "y" here, the Ethernet gadget driver will try to provide
+ a second device configuration, supporting RNDIS to talk to such
+ Microsoft USB hosts.
+
+ To make MS-Windows work with this, use Documentation/usb/linux.inf
+ as the "driver info file". For versions of MS-Windows older than
+ XP, you'll need to download drivers from Microsoft's website; a URL
+ is given in comments found in that info file.
+
+config USB_ETH_EEM
+ bool "Ethernet Emulation Model (EEM) support"
+ depends on USB_ETH
+ select USB_LIBCOMPOSITE
+ select USB_F_EEM
+ default n
+ help
+ CDC EEM is a newer USB standard that is somewhat simpler than CDC ECM
+ and therefore can be supported by more hardware. Technically ECM and
+ EEM are designed for different applications. The ECM model extends
+ the network interface to the target (e.g. a USB cable modem), and the
+ EEM model is for mobile devices to communicate with hosts using
+ ethernet over USB. For Linux gadgets, however, the interface with
+ the host is the same (a usbX device), so the differences are minimal.
+
+ If you say "y" here, the Ethernet gadget driver will use the EEM
+ protocol rather than ECM. If unsure, say "n".
+
+config USB_G_NCM
+ tristate "Network Control Model (NCM) support"
+ depends on NET
+ select USB_LIBCOMPOSITE
+ select USB_U_ETHER
+ select USB_F_NCM
+ select CRC32
+ help
+ This driver implements USB CDC NCM subclass standard. NCM is
+ an advanced protocol for Ethernet encapsulation, allows grouping
+ of several ethernet frames into one USB transfer and different
+ alignment possibilities.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "g_ncm".
+
+config USB_GADGETFS
+ tristate "Gadget Filesystem"
+ help
+ This driver provides a filesystem based API that lets user mode
+ programs implement a single-configuration USB device, including
+ endpoint I/O and control requests that don't relate to enumeration.
+ All endpoints, transfer speeds, and transfer types supported by
+ the hardware are available, through read() and write() calls.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "gadgetfs".
+
+config USB_FUNCTIONFS
+ tristate "Function Filesystem"
+ select USB_LIBCOMPOSITE
+ select USB_F_FS
+ select USB_FUNCTIONFS_GENERIC if !(USB_FUNCTIONFS_ETH || USB_FUNCTIONFS_RNDIS)
+ help
+ The Function Filesystem (FunctionFS) lets one create USB
+ composite functions in user space in the same way GadgetFS
+ lets one create USB gadgets in user space. This allows creation
+ of composite gadgets such that some of the functions are
+ implemented in kernel space (for instance Ethernet, serial or
+ mass storage) and other are implemented in user space.
+
+ If you say "y" or "m" here you will be able what kind of
+ configurations the gadget will provide.
+
+ Say "y" to link the driver statically, or "m" to build
+ a dynamically linked module called "g_ffs".
+
+config USB_FUNCTIONFS_ETH
+ bool "Include configuration with CDC ECM (Ethernet)"
+ depends on USB_FUNCTIONFS && NET
+ select USB_U_ETHER
+ select USB_F_ECM
+ select USB_F_SUBSET
+ help
+ Include a configuration with CDC ECM function (Ethernet) and the
+ Function Filesystem.
+
+config USB_FUNCTIONFS_RNDIS
+ bool "Include configuration with RNDIS (Ethernet)"
+ depends on USB_FUNCTIONFS && NET
+ select USB_U_ETHER
+ select USB_F_RNDIS
+ help
+ Include a configuration with RNDIS function (Ethernet) and the Filesystem.
+
+config USB_FUNCTIONFS_GENERIC
+ bool "Include 'pure' configuration"
+ depends on USB_FUNCTIONFS
+ help
+ Include a configuration with the Function Filesystem alone with
+ no Ethernet interface.
+
+config USB_MASS_STORAGE
+ tristate "Mass Storage Gadget"
+ depends on BLOCK
+ select USB_LIBCOMPOSITE
+ select USB_F_MASS_STORAGE
+ help
+ The Mass Storage Gadget acts as a USB Mass Storage disk drive.
+ As its storage repository it can use a regular file or a block
+ device (in much the same way as the "loop" device driver),
+ specified as a module parameter or sysfs option.
+
+ This driver is a replacement for now removed File-backed
+ Storage Gadget (g_file_storage).
+
+ Say "y" to link the driver statically, or "m" to build
+ a dynamically linked module called "g_mass_storage".
+
+config USB_GADGET_TARGET
+ tristate "USB Gadget Target Fabric Module"
+ depends on TARGET_CORE
+ select USB_LIBCOMPOSITE
+ help
+ This fabric is an USB gadget. Two USB protocols are supported that is
+ BBB or BOT (Bulk Only Transport) and UAS (USB Attached SCSI). BOT is
+ advertised on alternative interface 0 (primary) and UAS is on
+ alternative interface 1. Both protocols can work on USB2.0 and USB3.0.
+ UAS utilizes the USB 3.0 feature called streams support.
+
+config USB_G_SERIAL
+ tristate "Serial Gadget (with CDC ACM and CDC OBEX support)"
+ depends on TTY
+ select USB_U_SERIAL
+ select USB_F_ACM
+ select USB_F_SERIAL
+ select USB_F_OBEX
+ select USB_LIBCOMPOSITE
+ help
+ The Serial Gadget talks to the Linux-USB generic serial driver.
+ This driver supports a CDC-ACM module option, which can be used
+ to interoperate with MS-Windows hosts or with the Linux-USB
+ "cdc-acm" driver.
+
+ This driver also supports a CDC-OBEX option. You will need a
+ user space OBEX server talking to /dev/ttyGS*, since the kernel
+ itself doesn't implement the OBEX protocol.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "g_serial".
+
+ For more information, see Documentation/usb/gadget_serial.txt
+ which includes instructions and a "driver info file" needed to
+ make MS-Windows work with CDC ACM.
+
+config USB_MIDI_GADGET
+ tristate "MIDI Gadget"
+ depends on SND
+ select USB_LIBCOMPOSITE
+ select SND_RAWMIDI
+ help
+ The MIDI Gadget acts as a USB Audio device, with one MIDI
+ input and one MIDI output. These MIDI jacks appear as
+ a sound "card" in the ALSA sound system. Other MIDI
+ connections can then be made on the gadget system, using
+ ALSA's aconnect utility etc.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "g_midi".
+
+config USB_G_PRINTER
+ tristate "Printer Gadget"
+ select USB_LIBCOMPOSITE
+ help
+ The Printer Gadget channels data between the USB host and a
+ userspace program driving the print engine. The user space
+ program reads and writes the device file /dev/g_printer to
+ receive or send printer data. It can use ioctl calls to
+ the device file to get or set printer status.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "g_printer".
+
+ For more information, see Documentation/usb/gadget_printer.txt
+ which includes sample code for accessing the device file.
+
+if TTY
+
+config USB_CDC_COMPOSITE
+ tristate "CDC Composite Device (Ethernet and ACM)"
+ depends on NET
+ select USB_LIBCOMPOSITE
+ select USB_U_SERIAL
+ select USB_U_ETHER
+ select USB_F_ACM
+ select USB_F_ECM
+ help
+ This driver provides two functions in one configuration:
+ a CDC Ethernet (ECM) link, and a CDC ACM (serial port) link.
+
+ This driver requires four bulk and two interrupt endpoints,
+ plus the ability to handle altsettings. Not all peripheral
+ controllers are that capable.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module.
+
+config USB_G_NOKIA
+ tristate "Nokia composite gadget"
+ depends on PHONET
+ select USB_LIBCOMPOSITE
+ select USB_U_SERIAL
+ select USB_U_ETHER
+ select USB_F_ACM
+ select USB_F_OBEX
+ select USB_F_PHONET
+ select USB_F_ECM
+ help
+ The Nokia composite gadget provides support for acm, obex
+ and phonet in only one composite gadget driver.
+
+ It's only really useful for N900 hardware. If you're building
+ a kernel for N900, say Y or M here. If unsure, say N.
+
+config USB_G_ACM_MS
+ tristate "CDC Composite Device (ACM and mass storage)"
+ depends on BLOCK
+ select USB_LIBCOMPOSITE
+ select USB_U_SERIAL
+ select USB_F_ACM
+ select USB_F_MASS_STORAGE
+ help
+ This driver provides two functions in one configuration:
+ a mass storage, and a CDC ACM (serial port) link.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "g_acm_ms".
+
+config USB_G_MULTI
+ tristate "Multifunction Composite Gadget"
+ depends on BLOCK && NET
+ select USB_G_MULTI_CDC if !USB_G_MULTI_RNDIS
+ select USB_LIBCOMPOSITE
+ select USB_U_SERIAL
+ select USB_U_ETHER
+ select USB_F_ACM
+ select USB_F_MASS_STORAGE
+ help
+ The Multifunction Composite Gadget provides Ethernet (RNDIS
+ and/or CDC Ethernet), mass storage and ACM serial link
+ interfaces.
+
+ You will be asked to choose which of the two configurations is
+ to be available in the gadget. At least one configuration must
+ be chosen to make the gadget usable. Selecting more than one
+ configuration will prevent Windows from automatically detecting
+ the gadget as a composite gadget, so an INF file will be needed to
+ use the gadget.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "g_multi".
+
+config USB_G_MULTI_RNDIS
+ bool "RNDIS + CDC Serial + Storage configuration"
+ depends on USB_G_MULTI
+ select USB_F_RNDIS
+ default y
+ help
+ This option enables a configuration with RNDIS, CDC Serial and
+ Mass Storage functions available in the Multifunction Composite
+ Gadget. This is the configuration dedicated for Windows since RNDIS
+ is Microsoft's protocol.
+
+ If unsure, say "y".
+
+config USB_G_MULTI_CDC
+ bool "CDC Ethernet + CDC Serial + Storage configuration"
+ depends on USB_G_MULTI
+ default n
+ select USB_F_ECM
+ help
+ This option enables a configuration with CDC Ethernet (ECM), CDC
+ Serial and Mass Storage functions available in the Multifunction
+ Composite Gadget.
+
+ If unsure, say "y".
+
+endif # TTY
+
+config USB_G_HID
+ tristate "HID Gadget"
+ select USB_LIBCOMPOSITE
+ help
+ The HID gadget driver provides generic emulation of USB
+ Human Interface Devices (HID).
+
+ For more information, see Documentation/usb/gadget_hid.txt which
+ includes sample code for accessing the device files.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "g_hid".
+
+# Standalone / single function gadgets
+config USB_G_DBGP
+ tristate "EHCI Debug Device Gadget"
+ depends on TTY
+ select USB_LIBCOMPOSITE
+ help
+ This gadget emulates an EHCI Debug device. This is useful when you want
+ to interact with an EHCI Debug Port.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "g_dbgp".
+
+if USB_G_DBGP
+choice
+ prompt "EHCI Debug Device mode"
+ default USB_G_DBGP_SERIAL
+
+config USB_G_DBGP_PRINTK
+ depends on USB_G_DBGP
+ bool "printk"
+ help
+ Directly printk() received data. No interaction.
+
+config USB_G_DBGP_SERIAL
+ depends on USB_G_DBGP
+ select USB_U_SERIAL
+ bool "serial"
+ help
+ Userland can interact using /dev/ttyGSxxx.
+endchoice
+endif
+
+# put drivers that need isochronous transfer support (for audio
+# or video class gadget drivers), or specific hardware, here.
+config USB_G_WEBCAM
+ tristate "USB Webcam Gadget"
+ depends on VIDEO_DEV
+ select USB_LIBCOMPOSITE
+ select VIDEOBUF2_VMALLOC
+ help
+ The Webcam Gadget acts as a composite USB Audio and Video Class
+ device. It provides a userspace API to process UVC control requests
+ and stream video data to the host.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "g_webcam".
diff --git a/drivers/usb/gadget/legacy/Makefile b/drivers/usb/gadget/legacy/Makefile
new file mode 100644
index 000000000000..a11aad5635df
--- /dev/null
+++ b/drivers/usb/gadget/legacy/Makefile
@@ -0,0 +1,44 @@
+#
+# USB gadget drivers
+#
+
+ccflags-y := -I$(PWD)/drivers/usb/gadget/
+ccflags-y += -I$(PWD)/drivers/usb/gadget/udc/
+ccflags-y += -I$(PWD)/drivers/usb/gadget/function/
+
+g_zero-y := zero.o
+g_audio-y := audio.o
+g_ether-y := ether.o
+g_serial-y := serial.o
+g_midi-y := gmidi.o
+gadgetfs-y := inode.o
+g_mass_storage-y := mass_storage.o
+g_printer-y := printer.o
+g_cdc-y := cdc2.o
+g_multi-y := multi.o
+g_hid-y := hid.o
+g_dbgp-y := dbgp.o
+g_nokia-y := nokia.o
+g_webcam-y := webcam.o
+g_ncm-y := ncm.o
+g_acm_ms-y := acm_ms.o
+g_tcm_usb_gadget-y := tcm_usb_gadget.o
+
+obj-$(CONFIG_USB_ZERO) += g_zero.o
+obj-$(CONFIG_USB_AUDIO) += g_audio.o
+obj-$(CONFIG_USB_ETH) += g_ether.o
+obj-$(CONFIG_USB_GADGETFS) += gadgetfs.o
+obj-$(CONFIG_USB_FUNCTIONFS) += g_ffs.o
+obj-$(CONFIG_USB_MASS_STORAGE) += g_mass_storage.o
+obj-$(CONFIG_USB_G_SERIAL) += g_serial.o
+obj-$(CONFIG_USB_G_PRINTER) += g_printer.o
+obj-$(CONFIG_USB_MIDI_GADGET) += g_midi.o
+obj-$(CONFIG_USB_CDC_COMPOSITE) += g_cdc.o
+obj-$(CONFIG_USB_G_HID) += g_hid.o
+obj-$(CONFIG_USB_G_DBGP) += g_dbgp.o
+obj-$(CONFIG_USB_G_MULTI) += g_multi.o
+obj-$(CONFIG_USB_G_NOKIA) += g_nokia.o
+obj-$(CONFIG_USB_G_WEBCAM) += g_webcam.o
+obj-$(CONFIG_USB_G_NCM) += g_ncm.o
+obj-$(CONFIG_USB_G_ACM_MS) += g_acm_ms.o
+obj-$(CONFIG_USB_GADGET_TARGET) += tcm_usb_gadget.o
diff --git a/drivers/usb/gadget/acm_ms.c b/drivers/usb/gadget/legacy/acm_ms.c
index a252444cc0a7..c30b7b572465 100644
--- a/drivers/usb/gadget/acm_ms.c
+++ b/drivers/usb/gadget/legacy/acm_ms.c
@@ -267,18 +267,8 @@ static __refdata struct usb_composite_driver acm_ms_driver = {
.unbind = __exit_p(acm_ms_unbind),
};
+module_usb_composite_driver(acm_ms_driver);
+
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_AUTHOR("Klaus Schwarzkopf <schwarzkopf@sensortherm.de>");
MODULE_LICENSE("GPL v2");
-
-static int __init init(void)
-{
- return usb_composite_probe(&acm_ms_driver);
-}
-module_init(init);
-
-static void __exit cleanup(void)
-{
- usb_composite_unregister(&acm_ms_driver);
-}
-module_exit(cleanup);
diff --git a/drivers/usb/gadget/audio.c b/drivers/usb/gadget/legacy/audio.c
index 231b0efe8fdc..6eb695e5e43a 100644
--- a/drivers/usb/gadget/audio.c
+++ b/drivers/usb/gadget/legacy/audio.c
@@ -172,17 +172,7 @@ static __refdata struct usb_composite_driver audio_driver = {
.unbind = __exit_p(audio_unbind),
};
-static int __init init(void)
-{
- return usb_composite_probe(&audio_driver);
-}
-module_init(init);
-
-static void __exit cleanup(void)
-{
- usb_composite_unregister(&audio_driver);
-}
-module_exit(cleanup);
+module_usb_composite_driver(audio_driver);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_AUTHOR("Bryan Wu <cooloney@kernel.org>");
diff --git a/drivers/usb/gadget/cdc2.c b/drivers/usb/gadget/legacy/cdc2.c
index e126b6b248e6..2e85d9473478 100644
--- a/drivers/usb/gadget/cdc2.c
+++ b/drivers/usb/gadget/legacy/cdc2.c
@@ -231,18 +231,8 @@ static __refdata struct usb_composite_driver cdc_driver = {
.unbind = __exit_p(cdc_unbind),
};
+module_usb_composite_driver(cdc_driver);
+
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_AUTHOR("David Brownell");
MODULE_LICENSE("GPL");
-
-static int __init init(void)
-{
- return usb_composite_probe(&cdc_driver);
-}
-module_init(init);
-
-static void __exit cleanup(void)
-{
- usb_composite_unregister(&cdc_driver);
-}
-module_exit(cleanup);
diff --git a/drivers/usb/gadget/dbgp.c b/drivers/usb/gadget/legacy/dbgp.c
index 986fc511a2ed..986fc511a2ed 100644
--- a/drivers/usb/gadget/dbgp.c
+++ b/drivers/usb/gadget/legacy/dbgp.c
diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/legacy/ether.c
index c1c113ef950c..c5fdc61cdc4a 100644
--- a/drivers/usb/gadget/ether.c
+++ b/drivers/usb/gadget/legacy/ether.c
@@ -475,18 +475,8 @@ static __refdata struct usb_composite_driver eth_driver = {
.unbind = __exit_p(eth_unbind),
};
+module_usb_composite_driver(eth_driver);
+
MODULE_DESCRIPTION(PREFIX DRIVER_DESC);
MODULE_AUTHOR("David Brownell, Benedikt Spanger");
MODULE_LICENSE("GPL");
-
-static int __init init(void)
-{
- return usb_composite_probe(&eth_driver);
-}
-module_init(init);
-
-static void __exit cleanup(void)
-{
- usb_composite_unregister(&eth_driver);
-}
-module_exit(cleanup);
diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/legacy/g_ffs.c
index fe12e6a27448..06acfa55864a 100644
--- a/drivers/usb/gadget/g_ffs.c
+++ b/drivers/usb/gadget/legacy/g_ffs.c
@@ -276,7 +276,7 @@ module_exit(gfs_exit);
static void *functionfs_acquire_dev(struct ffs_dev *dev)
{
if (!try_module_get(THIS_MODULE))
- return ERR_PTR(-ENODEV);
+ return ERR_PTR(-ENOENT);
return 0;
}
diff --git a/drivers/usb/gadget/gmidi.c b/drivers/usb/gadget/legacy/gmidi.c
index e879e2c9f461..3d696b86ff76 100644
--- a/drivers/usb/gadget/gmidi.c
+++ b/drivers/usb/gadget/legacy/gmidi.c
@@ -163,15 +163,4 @@ static __refdata struct usb_composite_driver midi_driver = {
.unbind = __exit_p(midi_unbind),
};
-static int __init midi_init(void)
-{
- return usb_composite_probe(&midi_driver);
-}
-module_init(midi_init);
-
-static void __exit midi_cleanup(void)
-{
- usb_composite_unregister(&midi_driver);
-}
-module_exit(midi_cleanup);
-
+module_usb_composite_driver(midi_driver);
diff --git a/drivers/usb/gadget/hid.c b/drivers/usb/gadget/legacy/hid.c
index 778613eb37af..778613eb37af 100644
--- a/drivers/usb/gadget/hid.c
+++ b/drivers/usb/gadget/legacy/hid.c
diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/legacy/inode.c
index 2e4ce7704908..2e4ce7704908 100644
--- a/drivers/usb/gadget/inode.c
+++ b/drivers/usb/gadget/legacy/inode.c
diff --git a/drivers/usb/gadget/mass_storage.c b/drivers/usb/gadget/legacy/mass_storage.c
index 8e27a8c96444..8e27a8c96444 100644
--- a/drivers/usb/gadget/mass_storage.c
+++ b/drivers/usb/gadget/legacy/mass_storage.c
diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/legacy/multi.c
index 940f6cde8e89..39d27bb343b4 100644
--- a/drivers/usb/gadget/multi.c
+++ b/drivers/usb/gadget/legacy/multi.c
@@ -507,15 +507,4 @@ static __refdata struct usb_composite_driver multi_driver = {
.needs_serial = 1,
};
-
-static int __init multi_init(void)
-{
- return usb_composite_probe(&multi_driver);
-}
-module_init(multi_init);
-
-static void __exit multi_exit(void)
-{
- usb_composite_unregister(&multi_driver);
-}
-module_exit(multi_exit);
+module_usb_composite_driver(multi_driver);
diff --git a/drivers/usb/gadget/ncm.c b/drivers/usb/gadget/legacy/ncm.c
index 81956feca1bd..e90e23db2acb 100644
--- a/drivers/usb/gadget/ncm.c
+++ b/drivers/usb/gadget/legacy/ncm.c
@@ -204,18 +204,8 @@ static __refdata struct usb_composite_driver ncm_driver = {
.unbind = __exit_p(gncm_unbind),
};
+module_usb_composite_driver(ncm_driver);
+
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_AUTHOR("Yauheni Kaliuta");
MODULE_LICENSE("GPL");
-
-static int __init init(void)
-{
- return usb_composite_probe(&ncm_driver);
-}
-module_init(init);
-
-static void __exit cleanup(void)
-{
- usb_composite_unregister(&ncm_driver);
-}
-module_exit(cleanup);
diff --git a/drivers/usb/gadget/nokia.c b/drivers/usb/gadget/legacy/nokia.c
index 3ab386167519..9b8fd701648c 100644
--- a/drivers/usb/gadget/nokia.c
+++ b/drivers/usb/gadget/legacy/nokia.c
@@ -347,14 +347,4 @@ static __refdata struct usb_composite_driver nokia_driver = {
.unbind = __exit_p(nokia_unbind),
};
-static int __init nokia_init(void)
-{
- return usb_composite_probe(&nokia_driver);
-}
-module_init(nokia_init);
-
-static void __exit nokia_cleanup(void)
-{
- usb_composite_unregister(&nokia_driver);
-}
-module_exit(nokia_cleanup);
+module_usb_composite_driver(nokia_driver);
diff --git a/drivers/usb/gadget/printer.c b/drivers/usb/gadget/legacy/printer.c
index 6474081dcbaf..6474081dcbaf 100644
--- a/drivers/usb/gadget/printer.c
+++ b/drivers/usb/gadget/legacy/printer.c
diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/legacy/serial.c
index 1f5f978d35d5..1f5f978d35d5 100644
--- a/drivers/usb/gadget/serial.c
+++ b/drivers/usb/gadget/legacy/serial.c
diff --git a/drivers/usb/gadget/tcm_usb_gadget.c b/drivers/usb/gadget/legacy/tcm_usb_gadget.c
index 6cdb7a534f23..6cdb7a534f23 100644
--- a/drivers/usb/gadget/tcm_usb_gadget.c
+++ b/drivers/usb/gadget/legacy/tcm_usb_gadget.c
diff --git a/drivers/usb/gadget/tcm_usb_gadget.h b/drivers/usb/gadget/legacy/tcm_usb_gadget.h
index 8289219925b8..8289219925b8 100644
--- a/drivers/usb/gadget/tcm_usb_gadget.h
+++ b/drivers/usb/gadget/legacy/tcm_usb_gadget.h
diff --git a/drivers/usb/gadget/webcam.c b/drivers/usb/gadget/legacy/webcam.c
index 8cef1e658c29..a11d8e420bfe 100644
--- a/drivers/usb/gadget/webcam.c
+++ b/drivers/usb/gadget/legacy/webcam.c
@@ -390,20 +390,7 @@ static __refdata struct usb_composite_driver webcam_driver = {
.unbind = webcam_unbind,
};
-static int __init
-webcam_init(void)
-{
- return usb_composite_probe(&webcam_driver);
-}
-
-static void __exit
-webcam_cleanup(void)
-{
- usb_composite_unregister(&webcam_driver);
-}
-
-module_init(webcam_init);
-module_exit(webcam_cleanup);
+module_usb_composite_driver(webcam_driver);
MODULE_AUTHOR("Laurent Pinchart");
MODULE_DESCRIPTION("Webcam Video Gadget");
diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/legacy/zero.c
index 134f354ede62..c3d496828b74 100644
--- a/drivers/usb/gadget/zero.c
+++ b/drivers/usb/gadget/legacy/zero.c
@@ -411,17 +411,7 @@ static __refdata struct usb_composite_driver zero_driver = {
.resume = zero_resume,
};
+module_usb_composite_driver(zero_driver);
+
MODULE_AUTHOR("David Brownell");
MODULE_LICENSE("GPL");
-
-static int __init init(void)
-{
- return usb_composite_probe(&zero_driver);
-}
-module_init(init);
-
-static void __exit cleanup(void)
-{
- usb_composite_unregister(&zero_driver);
-}
-module_exit(cleanup);
diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c
deleted file mode 100644
index 300b3a71383b..000000000000
--- a/drivers/usb/gadget/net2280.c
+++ /dev/null
@@ -1,2905 +0,0 @@
-/*
- * Driver for the PLX NET2280 USB device controller.
- * Specs and errata are available from <http://www.plxtech.com>.
- *
- * PLX Technology Inc. (formerly NetChip Technology) supported the
- * development of this driver.
- *
- *
- * CODE STATUS HIGHLIGHTS
- *
- * This driver should work well with most "gadget" drivers, including
- * the Mass Storage, Serial, and Ethernet/RNDIS gadget drivers
- * as well as Gadget Zero and Gadgetfs.
- *
- * DMA is enabled by default. Drivers using transfer queues might use
- * DMA chaining to remove IRQ latencies between transfers. (Except when
- * short OUT transfers happen.) Drivers can use the req->no_interrupt
- * hint to completely eliminate some IRQs, if a later IRQ is guaranteed
- * and DMA chaining is enabled.
- *
- * Note that almost all the errata workarounds here are only needed for
- * rev1 chips. Rev1a silicon (0110) fixes almost all of them.
- */
-
-/*
- * Copyright (C) 2003 David Brownell
- * Copyright (C) 2003-2005 PLX Technology, Inc.
- *
- * Modified Seth Levy 2005 PLX Technology, Inc. to provide compatibility
- * with 2282 chip
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#undef DEBUG /* messages on error and most fault paths */
-#undef VERBOSE /* extra debug messages (success too) */
-
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/dma-mapping.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/ioport.h>
-#include <linux/slab.h>
-#include <linux/errno.h>
-#include <linux/init.h>
-#include <linux/timer.h>
-#include <linux/list.h>
-#include <linux/interrupt.h>
-#include <linux/moduleparam.h>
-#include <linux/device.h>
-#include <linux/usb/ch9.h>
-#include <linux/usb/gadget.h>
-#include <linux/prefetch.h>
-
-#include <asm/byteorder.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/unaligned.h>
-
-
-#define DRIVER_DESC "PLX NET228x USB Peripheral Controller"
-#define DRIVER_VERSION "2005 Sept 27"
-
-#define EP_DONTUSE 13 /* nonzero */
-
-#define USE_RDK_LEDS /* GPIO pins control three LEDs */
-
-
-static const char driver_name [] = "net2280";
-static const char driver_desc [] = DRIVER_DESC;
-
-static const char ep0name [] = "ep0";
-static const char *const ep_name [] = {
- ep0name,
- "ep-a", "ep-b", "ep-c", "ep-d",
- "ep-e", "ep-f",
-};
-
-/* use_dma -- general goodness, fewer interrupts, less cpu load (vs PIO)
- * use_dma_chaining -- dma descriptor queueing gives even more irq reduction
- *
- * The net2280 DMA engines are not tightly integrated with their FIFOs;
- * not all cases are (yet) handled well in this driver or the silicon.
- * Some gadget drivers work better with the dma support here than others.
- * These two parameters let you use PIO or more aggressive DMA.
- */
-static bool use_dma = 1;
-static bool use_dma_chaining = 0;
-
-/* "modprobe net2280 use_dma=n" etc */
-module_param (use_dma, bool, S_IRUGO);
-module_param (use_dma_chaining, bool, S_IRUGO);
-
-
-/* mode 0 == ep-{a,b,c,d} 1K fifo each
- * mode 1 == ep-{a,b} 2K fifo each, ep-{c,d} unavailable
- * mode 2 == ep-a 2K fifo, ep-{b,c} 1K each, ep-d unavailable
- */
-static ushort fifo_mode = 0;
-
-/* "modprobe net2280 fifo_mode=1" etc */
-module_param (fifo_mode, ushort, 0644);
-
-/* enable_suspend -- When enabled, the driver will respond to
- * USB suspend requests by powering down the NET2280. Otherwise,
- * USB suspend requests will be ignored. This is acceptable for
- * self-powered devices
- */
-static bool enable_suspend = 0;
-
-/* "modprobe net2280 enable_suspend=1" etc */
-module_param (enable_suspend, bool, S_IRUGO);
-
-/* force full-speed operation */
-static bool full_speed;
-module_param(full_speed, bool, 0444);
-MODULE_PARM_DESC(full_speed, "force full-speed mode -- for testing only!");
-
-#define DIR_STRING(bAddress) (((bAddress) & USB_DIR_IN) ? "in" : "out")
-
-#if defined(CONFIG_USB_GADGET_DEBUG_FILES) || defined (DEBUG)
-static char *type_string (u8 bmAttributes)
-{
- switch ((bmAttributes) & USB_ENDPOINT_XFERTYPE_MASK) {
- case USB_ENDPOINT_XFER_BULK: return "bulk";
- case USB_ENDPOINT_XFER_ISOC: return "iso";
- case USB_ENDPOINT_XFER_INT: return "intr";
- }
- return "control";
-}
-#endif
-
-#include "net2280.h"
-
-#define valid_bit cpu_to_le32 (1 << VALID_BIT)
-#define dma_done_ie cpu_to_le32 (1 << DMA_DONE_INTERRUPT_ENABLE)
-
-/*-------------------------------------------------------------------------*/
-
-static int
-net2280_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
-{
- struct net2280 *dev;
- struct net2280_ep *ep;
- u32 max, tmp;
- unsigned long flags;
-
- ep = container_of (_ep, struct net2280_ep, ep);
- if (!_ep || !desc || ep->desc || _ep->name == ep0name
- || desc->bDescriptorType != USB_DT_ENDPOINT)
- return -EINVAL;
- dev = ep->dev;
- if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
- return -ESHUTDOWN;
-
- /* erratum 0119 workaround ties up an endpoint number */
- if ((desc->bEndpointAddress & 0x0f) == EP_DONTUSE)
- return -EDOM;
-
- /* sanity check ep-e/ep-f since their fifos are small */
- max = usb_endpoint_maxp (desc) & 0x1fff;
- if (ep->num > 4 && max > 64)
- return -ERANGE;
-
- spin_lock_irqsave (&dev->lock, flags);
- _ep->maxpacket = max & 0x7ff;
- ep->desc = desc;
-
- /* ep_reset() has already been called */
- ep->stopped = 0;
- ep->wedged = 0;
- ep->out_overflow = 0;
-
- /* set speed-dependent max packet; may kick in high bandwidth */
- set_idx_reg (dev->regs, REG_EP_MAXPKT (dev, ep->num), max);
-
- /* FIFO lines can't go to different packets. PIO is ok, so
- * use it instead of troublesome (non-bulk) multi-packet DMA.
- */
- if (ep->dma && (max % 4) != 0 && use_dma_chaining) {
- DEBUG (ep->dev, "%s, no dma for maxpacket %d\n",
- ep->ep.name, ep->ep.maxpacket);
- ep->dma = NULL;
- }
-
- /* set type, direction, address; reset fifo counters */
- writel ((1 << FIFO_FLUSH), &ep->regs->ep_stat);
- tmp = (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK);
- if (tmp == USB_ENDPOINT_XFER_INT) {
- /* erratum 0105 workaround prevents hs NYET */
- if (dev->chiprev == 0100
- && dev->gadget.speed == USB_SPEED_HIGH
- && !(desc->bEndpointAddress & USB_DIR_IN))
- writel ((1 << CLEAR_NAK_OUT_PACKETS_MODE),
- &ep->regs->ep_rsp);
- } else if (tmp == USB_ENDPOINT_XFER_BULK) {
- /* catch some particularly blatant driver bugs */
- if ((dev->gadget.speed == USB_SPEED_HIGH
- && max != 512)
- || (dev->gadget.speed == USB_SPEED_FULL
- && max > 64)) {
- spin_unlock_irqrestore (&dev->lock, flags);
- return -ERANGE;
- }
- }
- ep->is_iso = (tmp == USB_ENDPOINT_XFER_ISOC) ? 1 : 0;
- tmp <<= ENDPOINT_TYPE;
- tmp |= desc->bEndpointAddress;
- tmp |= (4 << ENDPOINT_BYTE_COUNT); /* default full fifo lines */
- tmp |= 1 << ENDPOINT_ENABLE;
- wmb ();
-
- /* for OUT transfers, block the rx fifo until a read is posted */
- ep->is_in = (tmp & USB_DIR_IN) != 0;
- if (!ep->is_in)
- writel ((1 << SET_NAK_OUT_PACKETS), &ep->regs->ep_rsp);
- else if (dev->pdev->device != 0x2280) {
- /* Added for 2282, Don't use nak packets on an in endpoint,
- * this was ignored on 2280
- */
- writel ((1 << CLEAR_NAK_OUT_PACKETS)
- | (1 << CLEAR_NAK_OUT_PACKETS_MODE), &ep->regs->ep_rsp);
- }
-
- writel (tmp, &ep->regs->ep_cfg);
-
- /* enable irqs */
- if (!ep->dma) { /* pio, per-packet */
- tmp = (1 << ep->num) | readl (&dev->regs->pciirqenb0);
- writel (tmp, &dev->regs->pciirqenb0);
-
- tmp = (1 << DATA_PACKET_RECEIVED_INTERRUPT_ENABLE)
- | (1 << DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE);
- if (dev->pdev->device == 0x2280)
- tmp |= readl (&ep->regs->ep_irqenb);
- writel (tmp, &ep->regs->ep_irqenb);
- } else { /* dma, per-request */
- tmp = (1 << (8 + ep->num)); /* completion */
- tmp |= readl (&dev->regs->pciirqenb1);
- writel (tmp, &dev->regs->pciirqenb1);
-
- /* for short OUT transfers, dma completions can't
- * advance the queue; do it pio-style, by hand.
- * NOTE erratum 0112 workaround #2
- */
- if ((desc->bEndpointAddress & USB_DIR_IN) == 0) {
- tmp = (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT_ENABLE);
- writel (tmp, &ep->regs->ep_irqenb);
-
- tmp = (1 << ep->num) | readl (&dev->regs->pciirqenb0);
- writel (tmp, &dev->regs->pciirqenb0);
- }
- }
-
- tmp = desc->bEndpointAddress;
- DEBUG (dev, "enabled %s (ep%d%s-%s) %s max %04x\n",
- _ep->name, tmp & 0x0f, DIR_STRING (tmp),
- type_string (desc->bmAttributes),
- ep->dma ? "dma" : "pio", max);
-
- /* pci writes may still be posted */
- spin_unlock_irqrestore (&dev->lock, flags);
- return 0;
-}
-
-static int handshake (u32 __iomem *ptr, u32 mask, u32 done, int usec)
-{
- u32 result;
-
- do {
- result = readl (ptr);
- if (result == ~(u32)0) /* "device unplugged" */
- return -ENODEV;
- result &= mask;
- if (result == done)
- return 0;
- udelay (1);
- usec--;
- } while (usec > 0);
- return -ETIMEDOUT;
-}
-
-static const struct usb_ep_ops net2280_ep_ops;
-
-static void ep_reset (struct net2280_regs __iomem *regs, struct net2280_ep *ep)
-{
- u32 tmp;
-
- ep->desc = NULL;
- INIT_LIST_HEAD (&ep->queue);
-
- usb_ep_set_maxpacket_limit(&ep->ep, ~0);
- ep->ep.ops = &net2280_ep_ops;
-
- /* disable the dma, irqs, endpoint... */
- if (ep->dma) {
- writel (0, &ep->dma->dmactl);
- writel ( (1 << DMA_SCATTER_GATHER_DONE_INTERRUPT)
- | (1 << DMA_TRANSACTION_DONE_INTERRUPT)
- | (1 << DMA_ABORT)
- , &ep->dma->dmastat);
-
- tmp = readl (&regs->pciirqenb0);
- tmp &= ~(1 << ep->num);
- writel (tmp, &regs->pciirqenb0);
- } else {
- tmp = readl (&regs->pciirqenb1);
- tmp &= ~(1 << (8 + ep->num)); /* completion */
- writel (tmp, &regs->pciirqenb1);
- }
- writel (0, &ep->regs->ep_irqenb);
-
- /* init to our chosen defaults, notably so that we NAK OUT
- * packets until the driver queues a read (+note erratum 0112)
- */
- if (!ep->is_in || ep->dev->pdev->device == 0x2280) {
- tmp = (1 << SET_NAK_OUT_PACKETS_MODE)
- | (1 << SET_NAK_OUT_PACKETS)
- | (1 << CLEAR_EP_HIDE_STATUS_PHASE)
- | (1 << CLEAR_INTERRUPT_MODE);
- } else {
- /* added for 2282 */
- tmp = (1 << CLEAR_NAK_OUT_PACKETS_MODE)
- | (1 << CLEAR_NAK_OUT_PACKETS)
- | (1 << CLEAR_EP_HIDE_STATUS_PHASE)
- | (1 << CLEAR_INTERRUPT_MODE);
- }
-
- if (ep->num != 0) {
- tmp |= (1 << CLEAR_ENDPOINT_TOGGLE)
- | (1 << CLEAR_ENDPOINT_HALT);
- }
- writel (tmp, &ep->regs->ep_rsp);
-
- /* scrub most status bits, and flush any fifo state */
- if (ep->dev->pdev->device == 0x2280)
- tmp = (1 << FIFO_OVERFLOW)
- | (1 << FIFO_UNDERFLOW);
- else
- tmp = 0;
-
- writel (tmp | (1 << TIMEOUT)
- | (1 << USB_STALL_SENT)
- | (1 << USB_IN_NAK_SENT)
- | (1 << USB_IN_ACK_RCVD)
- | (1 << USB_OUT_PING_NAK_SENT)
- | (1 << USB_OUT_ACK_SENT)
- | (1 << FIFO_FLUSH)
- | (1 << SHORT_PACKET_OUT_DONE_INTERRUPT)
- | (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT)
- | (1 << DATA_PACKET_RECEIVED_INTERRUPT)
- | (1 << DATA_PACKET_TRANSMITTED_INTERRUPT)
- | (1 << DATA_OUT_PING_TOKEN_INTERRUPT)
- | (1 << DATA_IN_TOKEN_INTERRUPT)
- , &ep->regs->ep_stat);
-
- /* fifo size is handled separately */
-}
-
-static void nuke (struct net2280_ep *);
-
-static int net2280_disable (struct usb_ep *_ep)
-{
- struct net2280_ep *ep;
- unsigned long flags;
-
- ep = container_of (_ep, struct net2280_ep, ep);
- if (!_ep || !ep->desc || _ep->name == ep0name)
- return -EINVAL;
-
- spin_lock_irqsave (&ep->dev->lock, flags);
- nuke (ep);
- ep_reset (ep->dev->regs, ep);
-
- VDEBUG (ep->dev, "disabled %s %s\n",
- ep->dma ? "dma" : "pio", _ep->name);
-
- /* synch memory views with the device */
- (void) readl (&ep->regs->ep_cfg);
-
- if (use_dma && !ep->dma && ep->num >= 1 && ep->num <= 4)
- ep->dma = &ep->dev->dma [ep->num - 1];
-
- spin_unlock_irqrestore (&ep->dev->lock, flags);
- return 0;
-}
-
-/*-------------------------------------------------------------------------*/
-
-static struct usb_request *
-net2280_alloc_request (struct usb_ep *_ep, gfp_t gfp_flags)
-{
- struct net2280_ep *ep;
- struct net2280_request *req;
-
- if (!_ep)
- return NULL;
- ep = container_of (_ep, struct net2280_ep, ep);
-
- req = kzalloc(sizeof(*req), gfp_flags);
- if (!req)
- return NULL;
-
- INIT_LIST_HEAD (&req->queue);
-
- /* this dma descriptor may be swapped with the previous dummy */
- if (ep->dma) {
- struct net2280_dma *td;
-
- td = pci_pool_alloc (ep->dev->requests, gfp_flags,
- &req->td_dma);
- if (!td) {
- kfree (req);
- return NULL;
- }
- td->dmacount = 0; /* not VALID */
- td->dmadesc = td->dmaaddr;
- req->td = td;
- }
- return &req->req;
-}
-
-static void
-net2280_free_request (struct usb_ep *_ep, struct usb_request *_req)
-{
- struct net2280_ep *ep;
- struct net2280_request *req;
-
- ep = container_of (_ep, struct net2280_ep, ep);
- if (!_ep || !_req)
- return;
-
- req = container_of (_req, struct net2280_request, req);
- WARN_ON (!list_empty (&req->queue));
- if (req->td)
- pci_pool_free (ep->dev->requests, req->td, req->td_dma);
- kfree (req);
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* load a packet into the fifo we use for usb IN transfers.
- * works for all endpoints.
- *
- * NOTE: pio with ep-a..ep-d could stuff multiple packets into the fifo
- * at a time, but this code is simpler because it knows it only writes
- * one packet. ep-a..ep-d should use dma instead.
- */
-static void
-write_fifo (struct net2280_ep *ep, struct usb_request *req)
-{
- struct net2280_ep_regs __iomem *regs = ep->regs;
- u8 *buf;
- u32 tmp;
- unsigned count, total;
-
- /* INVARIANT: fifo is currently empty. (testable) */
-
- if (req) {
- buf = req->buf + req->actual;
- prefetch (buf);
- total = req->length - req->actual;
- } else {
- total = 0;
- buf = NULL;
- }
-
- /* write just one packet at a time */
- count = ep->ep.maxpacket;
- if (count > total) /* min() cannot be used on a bitfield */
- count = total;
-
- VDEBUG (ep->dev, "write %s fifo (IN) %d bytes%s req %p\n",
- ep->ep.name, count,
- (count != ep->ep.maxpacket) ? " (short)" : "",
- req);
- while (count >= 4) {
- /* NOTE be careful if you try to align these. fifo lines
- * should normally be full (4 bytes) and successive partial
- * lines are ok only in certain cases.
- */
- tmp = get_unaligned ((u32 *)buf);
- cpu_to_le32s (&tmp);
- writel (tmp, &regs->ep_data);
- buf += 4;
- count -= 4;
- }
-
- /* last fifo entry is "short" unless we wrote a full packet.
- * also explicitly validate last word in (periodic) transfers
- * when maxpacket is not a multiple of 4 bytes.
- */
- if (count || total < ep->ep.maxpacket) {
- tmp = count ? get_unaligned ((u32 *)buf) : count;
- cpu_to_le32s (&tmp);
- set_fifo_bytecount (ep, count & 0x03);
- writel (tmp, &regs->ep_data);
- }
-
- /* pci writes may still be posted */
-}
-
-/* work around erratum 0106: PCI and USB race over the OUT fifo.
- * caller guarantees chiprev 0100, out endpoint is NAKing, and
- * there's no real data in the fifo.
- *
- * NOTE: also used in cases where that erratum doesn't apply:
- * where the host wrote "too much" data to us.
- */
-static void out_flush (struct net2280_ep *ep)
-{
- u32 __iomem *statp;
- u32 tmp;
-
- ASSERT_OUT_NAKING (ep);
-
- statp = &ep->regs->ep_stat;
- writel ( (1 << DATA_OUT_PING_TOKEN_INTERRUPT)
- | (1 << DATA_PACKET_RECEIVED_INTERRUPT)
- , statp);
- writel ((1 << FIFO_FLUSH), statp);
- mb ();
- tmp = readl (statp);
- if (tmp & (1 << DATA_OUT_PING_TOKEN_INTERRUPT)
- /* high speed did bulk NYET; fifo isn't filling */
- && ep->dev->gadget.speed == USB_SPEED_FULL) {
- unsigned usec;
-
- usec = 50; /* 64 byte bulk/interrupt */
- handshake (statp, (1 << USB_OUT_PING_NAK_SENT),
- (1 << USB_OUT_PING_NAK_SENT), usec);
- /* NAK done; now CLEAR_NAK_OUT_PACKETS is safe */
- }
-}
-
-/* unload packet(s) from the fifo we use for usb OUT transfers.
- * returns true iff the request completed, because of short packet
- * or the request buffer having filled with full packets.
- *
- * for ep-a..ep-d this will read multiple packets out when they
- * have been accepted.
- */
-static int
-read_fifo (struct net2280_ep *ep, struct net2280_request *req)
-{
- struct net2280_ep_regs __iomem *regs = ep->regs;
- u8 *buf = req->req.buf + req->req.actual;
- unsigned count, tmp, is_short;
- unsigned cleanup = 0, prevent = 0;
-
- /* erratum 0106 ... packets coming in during fifo reads might
- * be incompletely rejected. not all cases have workarounds.
- */
- if (ep->dev->chiprev == 0x0100
- && ep->dev->gadget.speed == USB_SPEED_FULL) {
- udelay (1);
- tmp = readl (&ep->regs->ep_stat);
- if ((tmp & (1 << NAK_OUT_PACKETS)))
- cleanup = 1;
- else if ((tmp & (1 << FIFO_FULL))) {
- start_out_naking (ep);
- prevent = 1;
- }
- /* else: hope we don't see the problem */
- }
-
- /* never overflow the rx buffer. the fifo reads packets until
- * it sees a short one; we might not be ready for them all.
- */
- prefetchw (buf);
- count = readl (&regs->ep_avail);
- if (unlikely (count == 0)) {
- udelay (1);
- tmp = readl (&ep->regs->ep_stat);
- count = readl (&regs->ep_avail);
- /* handled that data already? */
- if (count == 0 && (tmp & (1 << NAK_OUT_PACKETS)) == 0)
- return 0;
- }
-
- tmp = req->req.length - req->req.actual;
- if (count > tmp) {
- /* as with DMA, data overflow gets flushed */
- if ((tmp % ep->ep.maxpacket) != 0) {
- ERROR (ep->dev,
- "%s out fifo %d bytes, expected %d\n",
- ep->ep.name, count, tmp);
- req->req.status = -EOVERFLOW;
- cleanup = 1;
- /* NAK_OUT_PACKETS will be set, so flushing is safe;
- * the next read will start with the next packet
- */
- } /* else it's a ZLP, no worries */
- count = tmp;
- }
- req->req.actual += count;
-
- is_short = (count == 0) || ((count % ep->ep.maxpacket) != 0);
-
- VDEBUG (ep->dev, "read %s fifo (OUT) %d bytes%s%s%s req %p %d/%d\n",
- ep->ep.name, count, is_short ? " (short)" : "",
- cleanup ? " flush" : "", prevent ? " nak" : "",
- req, req->req.actual, req->req.length);
-
- while (count >= 4) {
- tmp = readl (&regs->ep_data);
- cpu_to_le32s (&tmp);
- put_unaligned (tmp, (u32 *)buf);
- buf += 4;
- count -= 4;
- }
- if (count) {
- tmp = readl (&regs->ep_data);
- /* LE conversion is implicit here: */
- do {
- *buf++ = (u8) tmp;
- tmp >>= 8;
- } while (--count);
- }
- if (cleanup)
- out_flush (ep);
- if (prevent) {
- writel ((1 << CLEAR_NAK_OUT_PACKETS), &ep->regs->ep_rsp);
- (void) readl (&ep->regs->ep_rsp);
- }
-
- return is_short || ((req->req.actual == req->req.length)
- && !req->req.zero);
-}
-
-/* fill out dma descriptor to match a given request */
-static void
-fill_dma_desc (struct net2280_ep *ep, struct net2280_request *req, int valid)
-{
- struct net2280_dma *td = req->td;
- u32 dmacount = req->req.length;
-
- /* don't let DMA continue after a short OUT packet,
- * so overruns can't affect the next transfer.
- * in case of overruns on max-size packets, we can't
- * stop the fifo from filling but we can flush it.
- */
- if (ep->is_in)
- dmacount |= (1 << DMA_DIRECTION);
- if ((!ep->is_in && (dmacount % ep->ep.maxpacket) != 0)
- || ep->dev->pdev->device != 0x2280)
- dmacount |= (1 << END_OF_CHAIN);
-
- req->valid = valid;
- if (valid)
- dmacount |= (1 << VALID_BIT);
- if (likely(!req->req.no_interrupt || !use_dma_chaining))
- dmacount |= (1 << DMA_DONE_INTERRUPT_ENABLE);
-
- /* td->dmadesc = previously set by caller */
- td->dmaaddr = cpu_to_le32 (req->req.dma);
-
- /* 2280 may be polling VALID_BIT through ep->dma->dmadesc */
- wmb ();
- td->dmacount = cpu_to_le32(dmacount);
-}
-
-static const u32 dmactl_default =
- (1 << DMA_SCATTER_GATHER_DONE_INTERRUPT)
- | (1 << DMA_CLEAR_COUNT_ENABLE)
- /* erratum 0116 workaround part 1 (use POLLING) */
- | (POLL_100_USEC << DESCRIPTOR_POLLING_RATE)
- | (1 << DMA_VALID_BIT_POLLING_ENABLE)
- | (1 << DMA_VALID_BIT_ENABLE)
- | (1 << DMA_SCATTER_GATHER_ENABLE)
- /* erratum 0116 workaround part 2 (no AUTOSTART) */
- | (1 << DMA_ENABLE);
-
-static inline void spin_stop_dma (struct net2280_dma_regs __iomem *dma)
-{
- handshake (&dma->dmactl, (1 << DMA_ENABLE), 0, 50);
-}
-
-static inline void stop_dma (struct net2280_dma_regs __iomem *dma)
-{
- writel (readl (&dma->dmactl) & ~(1 << DMA_ENABLE), &dma->dmactl);
- spin_stop_dma (dma);
-}
-
-static void start_queue (struct net2280_ep *ep, u32 dmactl, u32 td_dma)
-{
- struct net2280_dma_regs __iomem *dma = ep->dma;
- unsigned int tmp = (1 << VALID_BIT) | (ep->is_in << DMA_DIRECTION);
-
- if (ep->dev->pdev->device != 0x2280)
- tmp |= (1 << END_OF_CHAIN);
-
- writel (tmp, &dma->dmacount);
- writel (readl (&dma->dmastat), &dma->dmastat);
-
- writel (td_dma, &dma->dmadesc);
- writel (dmactl, &dma->dmactl);
-
- /* erratum 0116 workaround part 3: pci arbiter away from net2280 */
- (void) readl (&ep->dev->pci->pcimstctl);
-
- writel ((1 << DMA_START), &dma->dmastat);
-
- if (!ep->is_in)
- stop_out_naking (ep);
-}
-
-static void start_dma (struct net2280_ep *ep, struct net2280_request *req)
-{
- u32 tmp;
- struct net2280_dma_regs __iomem *dma = ep->dma;
-
- /* FIXME can't use DMA for ZLPs */
-
- /* on this path we "know" there's no dma active (yet) */
- WARN_ON (readl (&dma->dmactl) & (1 << DMA_ENABLE));
- writel (0, &ep->dma->dmactl);
-
- /* previous OUT packet might have been short */
- if (!ep->is_in && ((tmp = readl (&ep->regs->ep_stat))
- & (1 << NAK_OUT_PACKETS)) != 0) {
- writel ((1 << SHORT_PACKET_TRANSFERRED_INTERRUPT),
- &ep->regs->ep_stat);
-
- tmp = readl (&ep->regs->ep_avail);
- if (tmp) {
- writel (readl (&dma->dmastat), &dma->dmastat);
-
- /* transfer all/some fifo data */
- writel (req->req.dma, &dma->dmaaddr);
- tmp = min (tmp, req->req.length);
-
- /* dma irq, faking scatterlist status */
- req->td->dmacount = cpu_to_le32 (req->req.length - tmp);
- writel ((1 << DMA_DONE_INTERRUPT_ENABLE)
- | tmp, &dma->dmacount);
- req->td->dmadesc = 0;
- req->valid = 1;
-
- writel ((1 << DMA_ENABLE), &dma->dmactl);
- writel ((1 << DMA_START), &dma->dmastat);
- return;
- }
- }
-
- tmp = dmactl_default;
-
- /* force packet boundaries between dma requests, but prevent the
- * controller from automagically writing a last "short" packet
- * (zero length) unless the driver explicitly said to do that.
- */
- if (ep->is_in) {
- if (likely ((req->req.length % ep->ep.maxpacket) != 0
- || req->req.zero)) {
- tmp |= (1 << DMA_FIFO_VALIDATE);
- ep->in_fifo_validate = 1;
- } else
- ep->in_fifo_validate = 0;
- }
-
- /* init req->td, pointing to the current dummy */
- req->td->dmadesc = cpu_to_le32 (ep->td_dma);
- fill_dma_desc (ep, req, 1);
-
- if (!use_dma_chaining)
- req->td->dmacount |= cpu_to_le32 (1 << END_OF_CHAIN);
-
- start_queue (ep, tmp, req->td_dma);
-}
-
-static inline void
-queue_dma (struct net2280_ep *ep, struct net2280_request *req, int valid)
-{
- struct net2280_dma *end;
- dma_addr_t tmp;
-
- /* swap new dummy for old, link; fill and maybe activate */
- end = ep->dummy;
- ep->dummy = req->td;
- req->td = end;
-
- tmp = ep->td_dma;
- ep->td_dma = req->td_dma;
- req->td_dma = tmp;
-
- end->dmadesc = cpu_to_le32 (ep->td_dma);
-
- fill_dma_desc (ep, req, valid);
-}
-
-static void
-done (struct net2280_ep *ep, struct net2280_request *req, int status)
-{
- struct net2280 *dev;
- unsigned stopped = ep->stopped;
-
- list_del_init (&req->queue);
-
- if (req->req.status == -EINPROGRESS)
- req->req.status = status;
- else
- status = req->req.status;
-
- dev = ep->dev;
- if (ep->dma)
- usb_gadget_unmap_request(&dev->gadget, &req->req, ep->is_in);
-
- if (status && status != -ESHUTDOWN)
- VDEBUG (dev, "complete %s req %p stat %d len %u/%u\n",
- ep->ep.name, &req->req, status,
- req->req.actual, req->req.length);
-
- /* don't modify queue heads during completion callback */
- ep->stopped = 1;
- spin_unlock (&dev->lock);
- req->req.complete (&ep->ep, &req->req);
- spin_lock (&dev->lock);
- ep->stopped = stopped;
-}
-
-/*-------------------------------------------------------------------------*/
-
-static int
-net2280_queue (struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
-{
- struct net2280_request *req;
- struct net2280_ep *ep;
- struct net2280 *dev;
- unsigned long flags;
-
- /* we always require a cpu-view buffer, so that we can
- * always use pio (as fallback or whatever).
- */
- req = container_of (_req, struct net2280_request, req);
- if (!_req || !_req->complete || !_req->buf
- || !list_empty (&req->queue))
- return -EINVAL;
- if (_req->length > (~0 & DMA_BYTE_COUNT_MASK))
- return -EDOM;
- ep = container_of (_ep, struct net2280_ep, ep);
- if (!_ep || (!ep->desc && ep->num != 0))
- return -EINVAL;
- dev = ep->dev;
- if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
- return -ESHUTDOWN;
-
- /* FIXME implement PIO fallback for ZLPs with DMA */
- if (ep->dma && _req->length == 0)
- return -EOPNOTSUPP;
-
- /* set up dma mapping in case the caller didn't */
- if (ep->dma) {
- int ret;
-
- ret = usb_gadget_map_request(&dev->gadget, _req,
- ep->is_in);
- if (ret)
- return ret;
- }
-
-#if 0
- VDEBUG (dev, "%s queue req %p, len %d buf %p\n",
- _ep->name, _req, _req->length, _req->buf);
-#endif
-
- spin_lock_irqsave (&dev->lock, flags);
-
- _req->status = -EINPROGRESS;
- _req->actual = 0;
-
- /* kickstart this i/o queue? */
- if (list_empty (&ep->queue) && !ep->stopped) {
- /* use DMA if the endpoint supports it, else pio */
- if (ep->dma)
- start_dma (ep, req);
- else {
- /* maybe there's no control data, just status ack */
- if (ep->num == 0 && _req->length == 0) {
- allow_status (ep);
- done (ep, req, 0);
- VDEBUG (dev, "%s status ack\n", ep->ep.name);
- goto done;
- }
-
- /* PIO ... stuff the fifo, or unblock it. */
- if (ep->is_in)
- write_fifo (ep, _req);
- else if (list_empty (&ep->queue)) {
- u32 s;
-
- /* OUT FIFO might have packet(s) buffered */
- s = readl (&ep->regs->ep_stat);
- if ((s & (1 << FIFO_EMPTY)) == 0) {
- /* note: _req->short_not_ok is
- * ignored here since PIO _always_
- * stops queue advance here, and
- * _req->status doesn't change for
- * short reads (only _req->actual)
- */
- if (read_fifo (ep, req)) {
- done (ep, req, 0);
- if (ep->num == 0)
- allow_status (ep);
- /* don't queue it */
- req = NULL;
- } else
- s = readl (&ep->regs->ep_stat);
- }
-
- /* don't NAK, let the fifo fill */
- if (req && (s & (1 << NAK_OUT_PACKETS)))
- writel ((1 << CLEAR_NAK_OUT_PACKETS),
- &ep->regs->ep_rsp);
- }
- }
-
- } else if (ep->dma) {
- int valid = 1;
-
- if (ep->is_in) {
- int expect;
-
- /* preventing magic zlps is per-engine state, not
- * per-transfer; irq logic must recover hiccups.
- */
- expect = likely (req->req.zero
- || (req->req.length % ep->ep.maxpacket) != 0);
- if (expect != ep->in_fifo_validate)
- valid = 0;
- }
- queue_dma (ep, req, valid);
-
- } /* else the irq handler advances the queue. */
-
- ep->responded = 1;
- if (req)
- list_add_tail (&req->queue, &ep->queue);
-done:
- spin_unlock_irqrestore (&dev->lock, flags);
-
- /* pci writes may still be posted */
- return 0;
-}
-
-static inline void
-dma_done (
- struct net2280_ep *ep,
- struct net2280_request *req,
- u32 dmacount,
- int status
-)
-{
- req->req.actual = req->req.length - (DMA_BYTE_COUNT_MASK & dmacount);
- done (ep, req, status);
-}
-
-static void restart_dma (struct net2280_ep *ep);
-
-static void scan_dma_completions (struct net2280_ep *ep)
-{
- /* only look at descriptors that were "naturally" retired,
- * so fifo and list head state won't matter
- */
- while (!list_empty (&ep->queue)) {
- struct net2280_request *req;
- u32 tmp;
-
- req = list_entry (ep->queue.next,
- struct net2280_request, queue);
- if (!req->valid)
- break;
- rmb ();
- tmp = le32_to_cpup (&req->td->dmacount);
- if ((tmp & (1 << VALID_BIT)) != 0)
- break;
-
- /* SHORT_PACKET_TRANSFERRED_INTERRUPT handles "usb-short"
- * cases where DMA must be aborted; this code handles
- * all non-abort DMA completions.
- */
- if (unlikely (req->td->dmadesc == 0)) {
- /* paranoia */
- tmp = readl (&ep->dma->dmacount);
- if (tmp & DMA_BYTE_COUNT_MASK)
- break;
- /* single transfer mode */
- dma_done (ep, req, tmp, 0);
- break;
- } else if (!ep->is_in
- && (req->req.length % ep->ep.maxpacket) != 0) {
- tmp = readl (&ep->regs->ep_stat);
-
- /* AVOID TROUBLE HERE by not issuing short reads from
- * your gadget driver. That helps avoids errata 0121,
- * 0122, and 0124; not all cases trigger the warning.
- */
- if ((tmp & (1 << NAK_OUT_PACKETS)) == 0) {
- WARNING (ep->dev, "%s lost packet sync!\n",
- ep->ep.name);
- req->req.status = -EOVERFLOW;
- } else if ((tmp = readl (&ep->regs->ep_avail)) != 0) {
- /* fifo gets flushed later */
- ep->out_overflow = 1;
- DEBUG (ep->dev, "%s dma, discard %d len %d\n",
- ep->ep.name, tmp,
- req->req.length);
- req->req.status = -EOVERFLOW;
- }
- }
- dma_done (ep, req, tmp, 0);
- }
-}
-
-static void restart_dma (struct net2280_ep *ep)
-{
- struct net2280_request *req;
- u32 dmactl = dmactl_default;
-
- if (ep->stopped)
- return;
- req = list_entry (ep->queue.next, struct net2280_request, queue);
-
- if (!use_dma_chaining) {
- start_dma (ep, req);
- return;
- }
-
- /* the 2280 will be processing the queue unless queue hiccups after
- * the previous transfer:
- * IN: wanted automagic zlp, head doesn't (or vice versa)
- * DMA_FIFO_VALIDATE doesn't init from dma descriptors.
- * OUT: was "usb-short", we must restart.
- */
- if (ep->is_in && !req->valid) {
- struct net2280_request *entry, *prev = NULL;
- int reqmode, done = 0;
-
- DEBUG (ep->dev, "%s dma hiccup td %p\n", ep->ep.name, req->td);
- ep->in_fifo_validate = likely (req->req.zero
- || (req->req.length % ep->ep.maxpacket) != 0);
- if (ep->in_fifo_validate)
- dmactl |= (1 << DMA_FIFO_VALIDATE);
- list_for_each_entry (entry, &ep->queue, queue) {
- __le32 dmacount;
-
- if (entry == req)
- continue;
- dmacount = entry->td->dmacount;
- if (!done) {
- reqmode = likely (entry->req.zero
- || (entry->req.length
- % ep->ep.maxpacket) != 0);
- if (reqmode == ep->in_fifo_validate) {
- entry->valid = 1;
- dmacount |= valid_bit;
- entry->td->dmacount = dmacount;
- prev = entry;
- continue;
- } else {
- /* force a hiccup */
- prev->td->dmacount |= dma_done_ie;
- done = 1;
- }
- }
-
- /* walk the rest of the queue so unlinks behave */
- entry->valid = 0;
- dmacount &= ~valid_bit;
- entry->td->dmacount = dmacount;
- prev = entry;
- }
- }
-
- writel (0, &ep->dma->dmactl);
- start_queue (ep, dmactl, req->td_dma);
-}
-
-static void abort_dma (struct net2280_ep *ep)
-{
- /* abort the current transfer */
- if (likely (!list_empty (&ep->queue))) {
- /* FIXME work around errata 0121, 0122, 0124 */
- writel ((1 << DMA_ABORT), &ep->dma->dmastat);
- spin_stop_dma (ep->dma);
- } else
- stop_dma (ep->dma);
- scan_dma_completions (ep);
-}
-
-/* dequeue ALL requests */
-static void nuke (struct net2280_ep *ep)
-{
- struct net2280_request *req;
-
- /* called with spinlock held */
- ep->stopped = 1;
- if (ep->dma)
- abort_dma (ep);
- while (!list_empty (&ep->queue)) {
- req = list_entry (ep->queue.next,
- struct net2280_request,
- queue);
- done (ep, req, -ESHUTDOWN);
- }
-}
-
-/* dequeue JUST ONE request */
-static int net2280_dequeue (struct usb_ep *_ep, struct usb_request *_req)
-{
- struct net2280_ep *ep;
- struct net2280_request *req;
- unsigned long flags;
- u32 dmactl;
- int stopped;
-
- ep = container_of (_ep, struct net2280_ep, ep);
- if (!_ep || (!ep->desc && ep->num != 0) || !_req)
- return -EINVAL;
-
- spin_lock_irqsave (&ep->dev->lock, flags);
- stopped = ep->stopped;
-
- /* quiesce dma while we patch the queue */
- dmactl = 0;
- ep->stopped = 1;
- if (ep->dma) {
- dmactl = readl (&ep->dma->dmactl);
- /* WARNING erratum 0127 may kick in ... */
- stop_dma (ep->dma);
- scan_dma_completions (ep);
- }
-
- /* make sure it's still queued on this endpoint */
- list_for_each_entry (req, &ep->queue, queue) {
- if (&req->req == _req)
- break;
- }
- if (&req->req != _req) {
- spin_unlock_irqrestore (&ep->dev->lock, flags);
- return -EINVAL;
- }
-
- /* queue head may be partially complete. */
- if (ep->queue.next == &req->queue) {
- if (ep->dma) {
- DEBUG (ep->dev, "unlink (%s) dma\n", _ep->name);
- _req->status = -ECONNRESET;
- abort_dma (ep);
- if (likely (ep->queue.next == &req->queue)) {
- // NOTE: misreports single-transfer mode
- req->td->dmacount = 0; /* invalidate */
- dma_done (ep, req,
- readl (&ep->dma->dmacount),
- -ECONNRESET);
- }
- } else {
- DEBUG (ep->dev, "unlink (%s) pio\n", _ep->name);
- done (ep, req, -ECONNRESET);
- }
- req = NULL;
-
- /* patch up hardware chaining data */
- } else if (ep->dma && use_dma_chaining) {
- if (req->queue.prev == ep->queue.next) {
- writel (le32_to_cpu (req->td->dmadesc),
- &ep->dma->dmadesc);
- if (req->td->dmacount & dma_done_ie)
- writel (readl (&ep->dma->dmacount)
- | le32_to_cpu(dma_done_ie),
- &ep->dma->dmacount);
- } else {
- struct net2280_request *prev;
-
- prev = list_entry (req->queue.prev,
- struct net2280_request, queue);
- prev->td->dmadesc = req->td->dmadesc;
- if (req->td->dmacount & dma_done_ie)
- prev->td->dmacount |= dma_done_ie;
- }
- }
-
- if (req)
- done (ep, req, -ECONNRESET);
- ep->stopped = stopped;
-
- if (ep->dma) {
- /* turn off dma on inactive queues */
- if (list_empty (&ep->queue))
- stop_dma (ep->dma);
- else if (!ep->stopped) {
- /* resume current request, or start new one */
- if (req)
- writel (dmactl, &ep->dma->dmactl);
- else
- start_dma (ep, list_entry (ep->queue.next,
- struct net2280_request, queue));
- }
- }
-
- spin_unlock_irqrestore (&ep->dev->lock, flags);
- return 0;
-}
-
-/*-------------------------------------------------------------------------*/
-
-static int net2280_fifo_status (struct usb_ep *_ep);
-
-static int
-net2280_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedged)
-{
- struct net2280_ep *ep;
- unsigned long flags;
- int retval = 0;
-
- ep = container_of (_ep, struct net2280_ep, ep);
- if (!_ep || (!ep->desc && ep->num != 0))
- return -EINVAL;
- if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
- return -ESHUTDOWN;
- if (ep->desc /* not ep0 */ && (ep->desc->bmAttributes & 0x03)
- == USB_ENDPOINT_XFER_ISOC)
- return -EINVAL;
-
- spin_lock_irqsave (&ep->dev->lock, flags);
- if (!list_empty (&ep->queue))
- retval = -EAGAIN;
- else if (ep->is_in && value && net2280_fifo_status (_ep) != 0)
- retval = -EAGAIN;
- else {
- VDEBUG (ep->dev, "%s %s %s\n", _ep->name,
- value ? "set" : "clear",
- wedged ? "wedge" : "halt");
- /* set/clear, then synch memory views with the device */
- if (value) {
- if (ep->num == 0)
- ep->dev->protocol_stall = 1;
- else
- set_halt (ep);
- if (wedged)
- ep->wedged = 1;
- } else {
- clear_halt (ep);
- ep->wedged = 0;
- }
- (void) readl (&ep->regs->ep_rsp);
- }
- spin_unlock_irqrestore (&ep->dev->lock, flags);
-
- return retval;
-}
-
-static int
-net2280_set_halt(struct usb_ep *_ep, int value)
-{
- return net2280_set_halt_and_wedge(_ep, value, 0);
-}
-
-static int
-net2280_set_wedge(struct usb_ep *_ep)
-{
- if (!_ep || _ep->name == ep0name)
- return -EINVAL;
- return net2280_set_halt_and_wedge(_ep, 1, 1);
-}
-
-static int
-net2280_fifo_status (struct usb_ep *_ep)
-{
- struct net2280_ep *ep;
- u32 avail;
-
- ep = container_of (_ep, struct net2280_ep, ep);
- if (!_ep || (!ep->desc && ep->num != 0))
- return -ENODEV;
- if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
- return -ESHUTDOWN;
-
- avail = readl (&ep->regs->ep_avail) & ((1 << 12) - 1);
- if (avail > ep->fifo_size)
- return -EOVERFLOW;
- if (ep->is_in)
- avail = ep->fifo_size - avail;
- return avail;
-}
-
-static void
-net2280_fifo_flush (struct usb_ep *_ep)
-{
- struct net2280_ep *ep;
-
- ep = container_of (_ep, struct net2280_ep, ep);
- if (!_ep || (!ep->desc && ep->num != 0))
- return;
- if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
- return;
-
- writel ((1 << FIFO_FLUSH), &ep->regs->ep_stat);
- (void) readl (&ep->regs->ep_rsp);
-}
-
-static const struct usb_ep_ops net2280_ep_ops = {
- .enable = net2280_enable,
- .disable = net2280_disable,
-
- .alloc_request = net2280_alloc_request,
- .free_request = net2280_free_request,
-
- .queue = net2280_queue,
- .dequeue = net2280_dequeue,
-
- .set_halt = net2280_set_halt,
- .set_wedge = net2280_set_wedge,
- .fifo_status = net2280_fifo_status,
- .fifo_flush = net2280_fifo_flush,
-};
-
-/*-------------------------------------------------------------------------*/
-
-static int net2280_get_frame (struct usb_gadget *_gadget)
-{
- struct net2280 *dev;
- unsigned long flags;
- u16 retval;
-
- if (!_gadget)
- return -ENODEV;
- dev = container_of (_gadget, struct net2280, gadget);
- spin_lock_irqsave (&dev->lock, flags);
- retval = get_idx_reg (dev->regs, REG_FRAME) & 0x03ff;
- spin_unlock_irqrestore (&dev->lock, flags);
- return retval;
-}
-
-static int net2280_wakeup (struct usb_gadget *_gadget)
-{
- struct net2280 *dev;
- u32 tmp;
- unsigned long flags;
-
- if (!_gadget)
- return 0;
- dev = container_of (_gadget, struct net2280, gadget);
-
- spin_lock_irqsave (&dev->lock, flags);
- tmp = readl (&dev->usb->usbctl);
- if (tmp & (1 << DEVICE_REMOTE_WAKEUP_ENABLE))
- writel (1 << GENERATE_RESUME, &dev->usb->usbstat);
- spin_unlock_irqrestore (&dev->lock, flags);
-
- /* pci writes may still be posted */
- return 0;
-}
-
-static int net2280_set_selfpowered (struct usb_gadget *_gadget, int value)
-{
- struct net2280 *dev;
- u32 tmp;
- unsigned long flags;
-
- if (!_gadget)
- return 0;
- dev = container_of (_gadget, struct net2280, gadget);
-
- spin_lock_irqsave (&dev->lock, flags);
- tmp = readl (&dev->usb->usbctl);
- if (value)
- tmp |= (1 << SELF_POWERED_STATUS);
- else
- tmp &= ~(1 << SELF_POWERED_STATUS);
- writel (tmp, &dev->usb->usbctl);
- spin_unlock_irqrestore (&dev->lock, flags);
-
- return 0;
-}
-
-static int net2280_pullup(struct usb_gadget *_gadget, int is_on)
-{
- struct net2280 *dev;
- u32 tmp;
- unsigned long flags;
-
- if (!_gadget)
- return -ENODEV;
- dev = container_of (_gadget, struct net2280, gadget);
-
- spin_lock_irqsave (&dev->lock, flags);
- tmp = readl (&dev->usb->usbctl);
- dev->softconnect = (is_on != 0);
- if (is_on)
- tmp |= (1 << USB_DETECT_ENABLE);
- else
- tmp &= ~(1 << USB_DETECT_ENABLE);
- writel (tmp, &dev->usb->usbctl);
- spin_unlock_irqrestore (&dev->lock, flags);
-
- return 0;
-}
-
-static int net2280_start(struct usb_gadget *_gadget,
- struct usb_gadget_driver *driver);
-static int net2280_stop(struct usb_gadget *_gadget,
- struct usb_gadget_driver *driver);
-
-static const struct usb_gadget_ops net2280_ops = {
- .get_frame = net2280_get_frame,
- .wakeup = net2280_wakeup,
- .set_selfpowered = net2280_set_selfpowered,
- .pullup = net2280_pullup,
- .udc_start = net2280_start,
- .udc_stop = net2280_stop,
-};
-
-/*-------------------------------------------------------------------------*/
-
-#ifdef CONFIG_USB_GADGET_DEBUG_FILES
-
-/* FIXME move these into procfs, and use seq_file.
- * Sysfs _still_ doesn't behave for arbitrarily sized files,
- * and also doesn't help products using this with 2.4 kernels.
- */
-
-/* "function" sysfs attribute */
-static ssize_t function_show(struct device *_dev, struct device_attribute *attr,
- char *buf)
-{
- struct net2280 *dev = dev_get_drvdata (_dev);
-
- if (!dev->driver
- || !dev->driver->function
- || strlen (dev->driver->function) > PAGE_SIZE)
- return 0;
- return scnprintf (buf, PAGE_SIZE, "%s\n", dev->driver->function);
-}
-static DEVICE_ATTR_RO(function);
-
-static ssize_t registers_show(struct device *_dev,
- struct device_attribute *attr, char *buf)
-{
- struct net2280 *dev;
- char *next;
- unsigned size, t;
- unsigned long flags;
- int i;
- u32 t1, t2;
- const char *s;
-
- dev = dev_get_drvdata (_dev);
- next = buf;
- size = PAGE_SIZE;
- spin_lock_irqsave (&dev->lock, flags);
-
- if (dev->driver)
- s = dev->driver->driver.name;
- else
- s = "(none)";
-
- /* Main Control Registers */
- t = scnprintf (next, size, "%s version " DRIVER_VERSION
- ", chiprev %04x, dma %s\n\n"
- "devinit %03x fifoctl %08x gadget '%s'\n"
- "pci irqenb0 %02x irqenb1 %08x "
- "irqstat0 %04x irqstat1 %08x\n",
- driver_name, dev->chiprev,
- use_dma
- ? (use_dma_chaining ? "chaining" : "enabled")
- : "disabled",
- readl (&dev->regs->devinit),
- readl (&dev->regs->fifoctl),
- s,
- readl (&dev->regs->pciirqenb0),
- readl (&dev->regs->pciirqenb1),
- readl (&dev->regs->irqstat0),
- readl (&dev->regs->irqstat1));
- size -= t;
- next += t;
-
- /* USB Control Registers */
- t1 = readl (&dev->usb->usbctl);
- t2 = readl (&dev->usb->usbstat);
- if (t1 & (1 << VBUS_PIN)) {
- if (t2 & (1 << HIGH_SPEED))
- s = "high speed";
- else if (dev->gadget.speed == USB_SPEED_UNKNOWN)
- s = "powered";
- else
- s = "full speed";
- /* full speed bit (6) not working?? */
- } else
- s = "not attached";
- t = scnprintf (next, size,
- "stdrsp %08x usbctl %08x usbstat %08x "
- "addr 0x%02x (%s)\n",
- readl (&dev->usb->stdrsp), t1, t2,
- readl (&dev->usb->ouraddr), s);
- size -= t;
- next += t;
-
- /* PCI Master Control Registers */
-
- /* DMA Control Registers */
-
- /* Configurable EP Control Registers */
- for (i = 0; i < 7; i++) {
- struct net2280_ep *ep;
-
- ep = &dev->ep [i];
- if (i && !ep->desc)
- continue;
-
- t1 = readl (&ep->regs->ep_cfg);
- t2 = readl (&ep->regs->ep_rsp) & 0xff;
- t = scnprintf (next, size,
- "\n%s\tcfg %05x rsp (%02x) %s%s%s%s%s%s%s%s"
- "irqenb %02x\n",
- ep->ep.name, t1, t2,
- (t2 & (1 << CLEAR_NAK_OUT_PACKETS))
- ? "NAK " : "",
- (t2 & (1 << CLEAR_EP_HIDE_STATUS_PHASE))
- ? "hide " : "",
- (t2 & (1 << CLEAR_EP_FORCE_CRC_ERROR))
- ? "CRC " : "",
- (t2 & (1 << CLEAR_INTERRUPT_MODE))
- ? "interrupt " : "",
- (t2 & (1<<CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE))
- ? "status " : "",
- (t2 & (1 << CLEAR_NAK_OUT_PACKETS_MODE))
- ? "NAKmode " : "",
- (t2 & (1 << CLEAR_ENDPOINT_TOGGLE))
- ? "DATA1 " : "DATA0 ",
- (t2 & (1 << CLEAR_ENDPOINT_HALT))
- ? "HALT " : "",
- readl (&ep->regs->ep_irqenb));
- size -= t;
- next += t;
-
- t = scnprintf (next, size,
- "\tstat %08x avail %04x "
- "(ep%d%s-%s)%s\n",
- readl (&ep->regs->ep_stat),
- readl (&ep->regs->ep_avail),
- t1 & 0x0f, DIR_STRING (t1),
- type_string (t1 >> 8),
- ep->stopped ? "*" : "");
- size -= t;
- next += t;
-
- if (!ep->dma)
- continue;
-
- t = scnprintf (next, size,
- " dma\tctl %08x stat %08x count %08x\n"
- "\taddr %08x desc %08x\n",
- readl (&ep->dma->dmactl),
- readl (&ep->dma->dmastat),
- readl (&ep->dma->dmacount),
- readl (&ep->dma->dmaaddr),
- readl (&ep->dma->dmadesc));
- size -= t;
- next += t;
-
- }
-
- /* Indexed Registers */
- // none yet
-
- /* Statistics */
- t = scnprintf (next, size, "\nirqs: ");
- size -= t;
- next += t;
- for (i = 0; i < 7; i++) {
- struct net2280_ep *ep;
-
- ep = &dev->ep [i];
- if (i && !ep->irqs)
- continue;
- t = scnprintf (next, size, " %s/%lu", ep->ep.name, ep->irqs);
- size -= t;
- next += t;
-
- }
- t = scnprintf (next, size, "\n");
- size -= t;
- next += t;
-
- spin_unlock_irqrestore (&dev->lock, flags);
-
- return PAGE_SIZE - size;
-}
-static DEVICE_ATTR_RO(registers);
-
-static ssize_t queues_show(struct device *_dev, struct device_attribute *attr,
- char *buf)
-{
- struct net2280 *dev;
- char *next;
- unsigned size;
- unsigned long flags;
- int i;
-
- dev = dev_get_drvdata (_dev);
- next = buf;
- size = PAGE_SIZE;
- spin_lock_irqsave (&dev->lock, flags);
-
- for (i = 0; i < 7; i++) {
- struct net2280_ep *ep = &dev->ep [i];
- struct net2280_request *req;
- int t;
-
- if (i != 0) {
- const struct usb_endpoint_descriptor *d;
-
- d = ep->desc;
- if (!d)
- continue;
- t = d->bEndpointAddress;
- t = scnprintf (next, size,
- "\n%s (ep%d%s-%s) max %04x %s fifo %d\n",
- ep->ep.name, t & USB_ENDPOINT_NUMBER_MASK,
- (t & USB_DIR_IN) ? "in" : "out",
- ({ char *val;
- switch (d->bmAttributes & 0x03) {
- case USB_ENDPOINT_XFER_BULK:
- val = "bulk"; break;
- case USB_ENDPOINT_XFER_INT:
- val = "intr"; break;
- default:
- val = "iso"; break;
- } val; }),
- usb_endpoint_maxp (d) & 0x1fff,
- ep->dma ? "dma" : "pio", ep->fifo_size
- );
- } else /* ep0 should only have one transfer queued */
- t = scnprintf (next, size, "ep0 max 64 pio %s\n",
- ep->is_in ? "in" : "out");
- if (t <= 0 || t > size)
- goto done;
- size -= t;
- next += t;
-
- if (list_empty (&ep->queue)) {
- t = scnprintf (next, size, "\t(nothing queued)\n");
- if (t <= 0 || t > size)
- goto done;
- size -= t;
- next += t;
- continue;
- }
- list_for_each_entry (req, &ep->queue, queue) {
- if (ep->dma && req->td_dma == readl (&ep->dma->dmadesc))
- t = scnprintf (next, size,
- "\treq %p len %d/%d "
- "buf %p (dmacount %08x)\n",
- &req->req, req->req.actual,
- req->req.length, req->req.buf,
- readl (&ep->dma->dmacount));
- else
- t = scnprintf (next, size,
- "\treq %p len %d/%d buf %p\n",
- &req->req, req->req.actual,
- req->req.length, req->req.buf);
- if (t <= 0 || t > size)
- goto done;
- size -= t;
- next += t;
-
- if (ep->dma) {
- struct net2280_dma *td;
-
- td = req->td;
- t = scnprintf (next, size, "\t td %08x "
- " count %08x buf %08x desc %08x\n",
- (u32) req->td_dma,
- le32_to_cpu (td->dmacount),
- le32_to_cpu (td->dmaaddr),
- le32_to_cpu (td->dmadesc));
- if (t <= 0 || t > size)
- goto done;
- size -= t;
- next += t;
- }
- }
- }
-
-done:
- spin_unlock_irqrestore (&dev->lock, flags);
- return PAGE_SIZE - size;
-}
-static DEVICE_ATTR_RO(queues);
-
-
-#else
-
-#define device_create_file(a,b) (0)
-#define device_remove_file(a,b) do { } while (0)
-
-#endif
-
-/*-------------------------------------------------------------------------*/
-
-/* another driver-specific mode might be a request type doing dma
- * to/from another device fifo instead of to/from memory.
- */
-
-static void set_fifo_mode (struct net2280 *dev, int mode)
-{
- /* keeping high bits preserves BAR2 */
- writel ((0xffff << PCI_BASE2_RANGE) | mode, &dev->regs->fifoctl);
-
- /* always ep-{a,b,e,f} ... maybe not ep-c or ep-d */
- INIT_LIST_HEAD (&dev->gadget.ep_list);
- list_add_tail (&dev->ep [1].ep.ep_list, &dev->gadget.ep_list);
- list_add_tail (&dev->ep [2].ep.ep_list, &dev->gadget.ep_list);
- switch (mode) {
- case 0:
- list_add_tail (&dev->ep [3].ep.ep_list, &dev->gadget.ep_list);
- list_add_tail (&dev->ep [4].ep.ep_list, &dev->gadget.ep_list);
- dev->ep [1].fifo_size = dev->ep [2].fifo_size = 1024;
- break;
- case 1:
- dev->ep [1].fifo_size = dev->ep [2].fifo_size = 2048;
- break;
- case 2:
- list_add_tail (&dev->ep [3].ep.ep_list, &dev->gadget.ep_list);
- dev->ep [1].fifo_size = 2048;
- dev->ep [2].fifo_size = 1024;
- break;
- }
- /* fifo sizes for ep0, ep-c, ep-d, ep-e, and ep-f never change */
- list_add_tail (&dev->ep [5].ep.ep_list, &dev->gadget.ep_list);
- list_add_tail (&dev->ep [6].ep.ep_list, &dev->gadget.ep_list);
-}
-
-/* keeping it simple:
- * - one bus driver, initted first;
- * - one function driver, initted second
- *
- * most of the work to support multiple net2280 controllers would
- * be to associate this gadget driver (yes?) with all of them, or
- * perhaps to bind specific drivers to specific devices.
- */
-
-static void usb_reset (struct net2280 *dev)
-{
- u32 tmp;
-
- dev->gadget.speed = USB_SPEED_UNKNOWN;
- (void) readl (&dev->usb->usbctl);
-
- net2280_led_init (dev);
-
- /* disable automatic responses, and irqs */
- writel (0, &dev->usb->stdrsp);
- writel (0, &dev->regs->pciirqenb0);
- writel (0, &dev->regs->pciirqenb1);
-
- /* clear old dma and irq state */
- for (tmp = 0; tmp < 4; tmp++) {
- struct net2280_ep *ep = &dev->ep [tmp + 1];
-
- if (ep->dma)
- abort_dma (ep);
- }
- writel (~0, &dev->regs->irqstat0),
- writel (~(1 << SUSPEND_REQUEST_INTERRUPT), &dev->regs->irqstat1),
-
- /* reset, and enable pci */
- tmp = readl (&dev->regs->devinit)
- | (1 << PCI_ENABLE)
- | (1 << FIFO_SOFT_RESET)
- | (1 << USB_SOFT_RESET)
- | (1 << M8051_RESET);
- writel (tmp, &dev->regs->devinit);
-
- /* standard fifo and endpoint allocations */
- set_fifo_mode (dev, (fifo_mode <= 2) ? fifo_mode : 0);
-}
-
-static void usb_reinit (struct net2280 *dev)
-{
- u32 tmp;
- int init_dma;
-
- /* use_dma changes are ignored till next device re-init */
- init_dma = use_dma;
-
- /* basic endpoint init */
- for (tmp = 0; tmp < 7; tmp++) {
- struct net2280_ep *ep = &dev->ep [tmp];
-
- ep->ep.name = ep_name [tmp];
- ep->dev = dev;
- ep->num = tmp;
-
- if (tmp > 0 && tmp <= 4) {
- ep->fifo_size = 1024;
- if (init_dma)
- ep->dma = &dev->dma [tmp - 1];
- } else
- ep->fifo_size = 64;
- ep->regs = &dev->epregs [tmp];
- ep_reset (dev->regs, ep);
- }
- usb_ep_set_maxpacket_limit(&dev->ep [0].ep, 64);
- usb_ep_set_maxpacket_limit(&dev->ep [5].ep, 64);
- usb_ep_set_maxpacket_limit(&dev->ep [6].ep, 64);
-
- dev->gadget.ep0 = &dev->ep [0].ep;
- dev->ep [0].stopped = 0;
- INIT_LIST_HEAD (&dev->gadget.ep0->ep_list);
-
- /* we want to prevent lowlevel/insecure access from the USB host,
- * but erratum 0119 means this enable bit is ignored
- */
- for (tmp = 0; tmp < 5; tmp++)
- writel (EP_DONTUSE, &dev->dep [tmp].dep_cfg);
-}
-
-static void ep0_start (struct net2280 *dev)
-{
- writel ( (1 << CLEAR_EP_HIDE_STATUS_PHASE)
- | (1 << CLEAR_NAK_OUT_PACKETS)
- | (1 << CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE)
- , &dev->epregs [0].ep_rsp);
-
- /*
- * hardware optionally handles a bunch of standard requests
- * that the API hides from drivers anyway. have it do so.
- * endpoint status/features are handled in software, to
- * help pass tests for some dubious behavior.
- */
- writel ( (1 << SET_TEST_MODE)
- | (1 << SET_ADDRESS)
- | (1 << DEVICE_SET_CLEAR_DEVICE_REMOTE_WAKEUP)
- | (1 << GET_DEVICE_STATUS)
- | (1 << GET_INTERFACE_STATUS)
- , &dev->usb->stdrsp);
- writel ( (1 << USB_ROOT_PORT_WAKEUP_ENABLE)
- | (1 << SELF_POWERED_USB_DEVICE)
- | (1 << REMOTE_WAKEUP_SUPPORT)
- | (dev->softconnect << USB_DETECT_ENABLE)
- | (1 << SELF_POWERED_STATUS)
- , &dev->usb->usbctl);
-
- /* enable irqs so we can see ep0 and general operation */
- writel ( (1 << SETUP_PACKET_INTERRUPT_ENABLE)
- | (1 << ENDPOINT_0_INTERRUPT_ENABLE)
- , &dev->regs->pciirqenb0);
- writel ( (1 << PCI_INTERRUPT_ENABLE)
- | (1 << PCI_MASTER_ABORT_RECEIVED_INTERRUPT_ENABLE)
- | (1 << PCI_TARGET_ABORT_RECEIVED_INTERRUPT_ENABLE)
- | (1 << PCI_RETRY_ABORT_INTERRUPT_ENABLE)
- | (1 << VBUS_INTERRUPT_ENABLE)
- | (1 << ROOT_PORT_RESET_INTERRUPT_ENABLE)
- | (1 << SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE)
- , &dev->regs->pciirqenb1);
-
- /* don't leave any writes posted */
- (void) readl (&dev->usb->usbctl);
-}
-
-/* when a driver is successfully registered, it will receive
- * control requests including set_configuration(), which enables
- * non-control requests. then usb traffic follows until a
- * disconnect is reported. then a host may connect again, or
- * the driver might get unbound.
- */
-static int net2280_start(struct usb_gadget *_gadget,
- struct usb_gadget_driver *driver)
-{
- struct net2280 *dev;
- int retval;
- unsigned i;
-
- /* insist on high speed support from the driver, since
- * (dev->usb->xcvrdiag & FORCE_FULL_SPEED_MODE)
- * "must not be used in normal operation"
- */
- if (!driver || driver->max_speed < USB_SPEED_HIGH
- || !driver->setup)
- return -EINVAL;
-
- dev = container_of (_gadget, struct net2280, gadget);
-
- for (i = 0; i < 7; i++)
- dev->ep [i].irqs = 0;
-
- /* hook up the driver ... */
- dev->softconnect = 1;
- driver->driver.bus = NULL;
- dev->driver = driver;
-
- retval = device_create_file (&dev->pdev->dev, &dev_attr_function);
- if (retval) goto err_unbind;
- retval = device_create_file (&dev->pdev->dev, &dev_attr_queues);
- if (retval) goto err_func;
-
- /* Enable force-full-speed testing mode, if desired */
- if (full_speed)
- writel(1 << FORCE_FULL_SPEED_MODE, &dev->usb->xcvrdiag);
-
- /* ... then enable host detection and ep0; and we're ready
- * for set_configuration as well as eventual disconnect.
- */
- net2280_led_active (dev, 1);
- ep0_start (dev);
-
- DEBUG (dev, "%s ready, usbctl %08x stdrsp %08x\n",
- driver->driver.name,
- readl (&dev->usb->usbctl),
- readl (&dev->usb->stdrsp));
-
- /* pci writes may still be posted */
- return 0;
-
-err_func:
- device_remove_file (&dev->pdev->dev, &dev_attr_function);
-err_unbind:
- dev->driver = NULL;
- return retval;
-}
-
-static void
-stop_activity (struct net2280 *dev, struct usb_gadget_driver *driver)
-{
- int i;
-
- /* don't disconnect if it's not connected */
- if (dev->gadget.speed == USB_SPEED_UNKNOWN)
- driver = NULL;
-
- /* stop hardware; prevent new request submissions;
- * and kill any outstanding requests.
- */
- usb_reset (dev);
- for (i = 0; i < 7; i++)
- nuke (&dev->ep [i]);
-
- /* report disconnect; the driver is already quiesced */
- if (driver) {
- spin_unlock(&dev->lock);
- driver->disconnect(&dev->gadget);
- spin_lock(&dev->lock);
- }
-
- usb_reinit (dev);
-}
-
-static int net2280_stop(struct usb_gadget *_gadget,
- struct usb_gadget_driver *driver)
-{
- struct net2280 *dev;
- unsigned long flags;
-
- dev = container_of (_gadget, struct net2280, gadget);
-
- spin_lock_irqsave (&dev->lock, flags);
- stop_activity (dev, driver);
- spin_unlock_irqrestore (&dev->lock, flags);
-
- dev->driver = NULL;
-
- net2280_led_active (dev, 0);
-
- /* Disable full-speed test mode */
- writel(0, &dev->usb->xcvrdiag);
-
- device_remove_file (&dev->pdev->dev, &dev_attr_function);
- device_remove_file (&dev->pdev->dev, &dev_attr_queues);
-
- DEBUG(dev, "unregistered driver '%s'\n",
- driver ? driver->driver.name : "");
-
- return 0;
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* handle ep0, ep-e, ep-f with 64 byte packets: packet per irq.
- * also works for dma-capable endpoints, in pio mode or just
- * to manually advance the queue after short OUT transfers.
- */
-static void handle_ep_small (struct net2280_ep *ep)
-{
- struct net2280_request *req;
- u32 t;
- /* 0 error, 1 mid-data, 2 done */
- int mode = 1;
-
- if (!list_empty (&ep->queue))
- req = list_entry (ep->queue.next,
- struct net2280_request, queue);
- else
- req = NULL;
-
- /* ack all, and handle what we care about */
- t = readl (&ep->regs->ep_stat);
- ep->irqs++;
-#if 0
- VDEBUG (ep->dev, "%s ack ep_stat %08x, req %p\n",
- ep->ep.name, t, req ? &req->req : 0);
-#endif
- if (!ep->is_in || ep->dev->pdev->device == 0x2280)
- writel (t & ~(1 << NAK_OUT_PACKETS), &ep->regs->ep_stat);
- else
- /* Added for 2282 */
- writel (t, &ep->regs->ep_stat);
-
- /* for ep0, monitor token irqs to catch data stage length errors
- * and to synchronize on status.
- *
- * also, to defer reporting of protocol stalls ... here's where
- * data or status first appears, handling stalls here should never
- * cause trouble on the host side..
- *
- * control requests could be slightly faster without token synch for
- * status, but status can jam up that way.
- */
- if (unlikely (ep->num == 0)) {
- if (ep->is_in) {
- /* status; stop NAKing */
- if (t & (1 << DATA_OUT_PING_TOKEN_INTERRUPT)) {
- if (ep->dev->protocol_stall) {
- ep->stopped = 1;
- set_halt (ep);
- }
- if (!req)
- allow_status (ep);
- mode = 2;
- /* reply to extra IN data tokens with a zlp */
- } else if (t & (1 << DATA_IN_TOKEN_INTERRUPT)) {
- if (ep->dev->protocol_stall) {
- ep->stopped = 1;
- set_halt (ep);
- mode = 2;
- } else if (ep->responded &&
- !req && !ep->stopped)
- write_fifo (ep, NULL);
- }
- } else {
- /* status; stop NAKing */
- if (t & (1 << DATA_IN_TOKEN_INTERRUPT)) {
- if (ep->dev->protocol_stall) {
- ep->stopped = 1;
- set_halt (ep);
- }
- mode = 2;
- /* an extra OUT token is an error */
- } else if (((t & (1 << DATA_OUT_PING_TOKEN_INTERRUPT))
- && req
- && req->req.actual == req->req.length)
- || (ep->responded && !req)) {
- ep->dev->protocol_stall = 1;
- set_halt (ep);
- ep->stopped = 1;
- if (req)
- done (ep, req, -EOVERFLOW);
- req = NULL;
- }
- }
- }
-
- if (unlikely (!req))
- return;
-
- /* manual DMA queue advance after short OUT */
- if (likely (ep->dma)) {
- if (t & (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT)) {
- u32 count;
- int stopped = ep->stopped;
-
- /* TRANSFERRED works around OUT_DONE erratum 0112.
- * we expect (N <= maxpacket) bytes; host wrote M.
- * iff (M < N) we won't ever see a DMA interrupt.
- */
- ep->stopped = 1;
- for (count = 0; ; t = readl (&ep->regs->ep_stat)) {
-
- /* any preceding dma transfers must finish.
- * dma handles (M >= N), may empty the queue
- */
- scan_dma_completions (ep);
- if (unlikely (list_empty (&ep->queue)
- || ep->out_overflow)) {
- req = NULL;
- break;
- }
- req = list_entry (ep->queue.next,
- struct net2280_request, queue);
-
- /* here either (M < N), a "real" short rx;
- * or (M == N) and the queue didn't empty
- */
- if (likely (t & (1 << FIFO_EMPTY))) {
- count = readl (&ep->dma->dmacount);
- count &= DMA_BYTE_COUNT_MASK;
- if (readl (&ep->dma->dmadesc)
- != req->td_dma)
- req = NULL;
- break;
- }
- udelay(1);
- }
-
- /* stop DMA, leave ep NAKing */
- writel ((1 << DMA_ABORT), &ep->dma->dmastat);
- spin_stop_dma (ep->dma);
-
- if (likely (req)) {
- req->td->dmacount = 0;
- t = readl (&ep->regs->ep_avail);
- dma_done (ep, req, count,
- (ep->out_overflow || t)
- ? -EOVERFLOW : 0);
- }
-
- /* also flush to prevent erratum 0106 trouble */
- if (unlikely (ep->out_overflow
- || (ep->dev->chiprev == 0x0100
- && ep->dev->gadget.speed
- == USB_SPEED_FULL))) {
- out_flush (ep);
- ep->out_overflow = 0;
- }
-
- /* (re)start dma if needed, stop NAKing */
- ep->stopped = stopped;
- if (!list_empty (&ep->queue))
- restart_dma (ep);
- } else
- DEBUG (ep->dev, "%s dma ep_stat %08x ??\n",
- ep->ep.name, t);
- return;
-
- /* data packet(s) received (in the fifo, OUT) */
- } else if (t & (1 << DATA_PACKET_RECEIVED_INTERRUPT)) {
- if (read_fifo (ep, req) && ep->num != 0)
- mode = 2;
-
- /* data packet(s) transmitted (IN) */
- } else if (t & (1 << DATA_PACKET_TRANSMITTED_INTERRUPT)) {
- unsigned len;
-
- len = req->req.length - req->req.actual;
- if (len > ep->ep.maxpacket)
- len = ep->ep.maxpacket;
- req->req.actual += len;
-
- /* if we wrote it all, we're usually done */
- if (req->req.actual == req->req.length) {
- if (ep->num == 0) {
- /* send zlps until the status stage */
- } else if (!req->req.zero || len != ep->ep.maxpacket)
- mode = 2;
- }
-
- /* there was nothing to do ... */
- } else if (mode == 1)
- return;
-
- /* done */
- if (mode == 2) {
- /* stream endpoints often resubmit/unlink in completion */
- done (ep, req, 0);
-
- /* maybe advance queue to next request */
- if (ep->num == 0) {
- /* NOTE: net2280 could let gadget driver start the
- * status stage later. since not all controllers let
- * them control that, the api doesn't (yet) allow it.
- */
- if (!ep->stopped)
- allow_status (ep);
- req = NULL;
- } else {
- if (!list_empty (&ep->queue) && !ep->stopped)
- req = list_entry (ep->queue.next,
- struct net2280_request, queue);
- else
- req = NULL;
- if (req && !ep->is_in)
- stop_out_naking (ep);
- }
- }
-
- /* is there a buffer for the next packet?
- * for best streaming performance, make sure there is one.
- */
- if (req && !ep->stopped) {
-
- /* load IN fifo with next packet (may be zlp) */
- if (t & (1 << DATA_PACKET_TRANSMITTED_INTERRUPT))
- write_fifo (ep, &req->req);
- }
-}
-
-static struct net2280_ep *
-get_ep_by_addr (struct net2280 *dev, u16 wIndex)
-{
- struct net2280_ep *ep;
-
- if ((wIndex & USB_ENDPOINT_NUMBER_MASK) == 0)
- return &dev->ep [0];
- list_for_each_entry (ep, &dev->gadget.ep_list, ep.ep_list) {
- u8 bEndpointAddress;
-
- if (!ep->desc)
- continue;
- bEndpointAddress = ep->desc->bEndpointAddress;
- if ((wIndex ^ bEndpointAddress) & USB_DIR_IN)
- continue;
- if ((wIndex & 0x0f) == (bEndpointAddress & 0x0f))
- return ep;
- }
- return NULL;
-}
-
-static void handle_stat0_irqs (struct net2280 *dev, u32 stat)
-{
- struct net2280_ep *ep;
- u32 num, scratch;
-
- /* most of these don't need individual acks */
- stat &= ~(1 << INTA_ASSERTED);
- if (!stat)
- return;
- // DEBUG (dev, "irqstat0 %04x\n", stat);
-
- /* starting a control request? */
- if (unlikely (stat & (1 << SETUP_PACKET_INTERRUPT))) {
- union {
- u32 raw [2];
- struct usb_ctrlrequest r;
- } u;
- int tmp;
- struct net2280_request *req;
-
- if (dev->gadget.speed == USB_SPEED_UNKNOWN) {
- if (readl (&dev->usb->usbstat) & (1 << HIGH_SPEED))
- dev->gadget.speed = USB_SPEED_HIGH;
- else
- dev->gadget.speed = USB_SPEED_FULL;
- net2280_led_speed (dev, dev->gadget.speed);
- DEBUG(dev, "%s\n", usb_speed_string(dev->gadget.speed));
- }
-
- ep = &dev->ep [0];
- ep->irqs++;
-
- /* make sure any leftover request state is cleared */
- stat &= ~(1 << ENDPOINT_0_INTERRUPT);
- while (!list_empty (&ep->queue)) {
- req = list_entry (ep->queue.next,
- struct net2280_request, queue);
- done (ep, req, (req->req.actual == req->req.length)
- ? 0 : -EPROTO);
- }
- ep->stopped = 0;
- dev->protocol_stall = 0;
-
- if (ep->dev->pdev->device == 0x2280)
- tmp = (1 << FIFO_OVERFLOW)
- | (1 << FIFO_UNDERFLOW);
- else
- tmp = 0;
-
- writel (tmp | (1 << TIMEOUT)
- | (1 << USB_STALL_SENT)
- | (1 << USB_IN_NAK_SENT)
- | (1 << USB_IN_ACK_RCVD)
- | (1 << USB_OUT_PING_NAK_SENT)
- | (1 << USB_OUT_ACK_SENT)
- | (1 << SHORT_PACKET_OUT_DONE_INTERRUPT)
- | (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT)
- | (1 << DATA_PACKET_RECEIVED_INTERRUPT)
- | (1 << DATA_PACKET_TRANSMITTED_INTERRUPT)
- | (1 << DATA_OUT_PING_TOKEN_INTERRUPT)
- | (1 << DATA_IN_TOKEN_INTERRUPT)
- , &ep->regs->ep_stat);
- u.raw [0] = readl (&dev->usb->setup0123);
- u.raw [1] = readl (&dev->usb->setup4567);
-
- cpu_to_le32s (&u.raw [0]);
- cpu_to_le32s (&u.raw [1]);
-
- tmp = 0;
-
-#define w_value le16_to_cpu(u.r.wValue)
-#define w_index le16_to_cpu(u.r.wIndex)
-#define w_length le16_to_cpu(u.r.wLength)
-
- /* ack the irq */
- writel (1 << SETUP_PACKET_INTERRUPT, &dev->regs->irqstat0);
- stat ^= (1 << SETUP_PACKET_INTERRUPT);
-
- /* watch control traffic at the token level, and force
- * synchronization before letting the status stage happen.
- * FIXME ignore tokens we'll NAK, until driver responds.
- * that'll mean a lot less irqs for some drivers.
- */
- ep->is_in = (u.r.bRequestType & USB_DIR_IN) != 0;
- if (ep->is_in) {
- scratch = (1 << DATA_PACKET_TRANSMITTED_INTERRUPT)
- | (1 << DATA_OUT_PING_TOKEN_INTERRUPT)
- | (1 << DATA_IN_TOKEN_INTERRUPT);
- stop_out_naking (ep);
- } else
- scratch = (1 << DATA_PACKET_RECEIVED_INTERRUPT)
- | (1 << DATA_OUT_PING_TOKEN_INTERRUPT)
- | (1 << DATA_IN_TOKEN_INTERRUPT);
- writel (scratch, &dev->epregs [0].ep_irqenb);
-
- /* we made the hardware handle most lowlevel requests;
- * everything else goes uplevel to the gadget code.
- */
- ep->responded = 1;
- switch (u.r.bRequest) {
- case USB_REQ_GET_STATUS: {
- struct net2280_ep *e;
- __le32 status;
-
- /* hw handles device and interface status */
- if (u.r.bRequestType != (USB_DIR_IN|USB_RECIP_ENDPOINT))
- goto delegate;
- if ((e = get_ep_by_addr (dev, w_index)) == NULL
- || w_length > 2)
- goto do_stall;
-
- if (readl (&e->regs->ep_rsp)
- & (1 << SET_ENDPOINT_HALT))
- status = cpu_to_le32 (1);
- else
- status = cpu_to_le32 (0);
-
- /* don't bother with a request object! */
- writel (0, &dev->epregs [0].ep_irqenb);
- set_fifo_bytecount (ep, w_length);
- writel ((__force u32)status, &dev->epregs [0].ep_data);
- allow_status (ep);
- VDEBUG (dev, "%s stat %02x\n", ep->ep.name, status);
- goto next_endpoints;
- }
- break;
- case USB_REQ_CLEAR_FEATURE: {
- struct net2280_ep *e;
-
- /* hw handles device features */
- if (u.r.bRequestType != USB_RECIP_ENDPOINT)
- goto delegate;
- if (w_value != USB_ENDPOINT_HALT
- || w_length != 0)
- goto do_stall;
- if ((e = get_ep_by_addr (dev, w_index)) == NULL)
- goto do_stall;
- if (e->wedged) {
- VDEBUG(dev, "%s wedged, halt not cleared\n",
- ep->ep.name);
- } else {
- VDEBUG(dev, "%s clear halt\n", ep->ep.name);
- clear_halt(e);
- }
- allow_status (ep);
- goto next_endpoints;
- }
- break;
- case USB_REQ_SET_FEATURE: {
- struct net2280_ep *e;
-
- /* hw handles device features */
- if (u.r.bRequestType != USB_RECIP_ENDPOINT)
- goto delegate;
- if (w_value != USB_ENDPOINT_HALT
- || w_length != 0)
- goto do_stall;
- if ((e = get_ep_by_addr (dev, w_index)) == NULL)
- goto do_stall;
- if (e->ep.name == ep0name)
- goto do_stall;
- set_halt (e);
- allow_status (ep);
- VDEBUG (dev, "%s set halt\n", ep->ep.name);
- goto next_endpoints;
- }
- break;
- default:
-delegate:
- VDEBUG (dev, "setup %02x.%02x v%04x i%04x l%04x "
- "ep_cfg %08x\n",
- u.r.bRequestType, u.r.bRequest,
- w_value, w_index, w_length,
- readl (&ep->regs->ep_cfg));
- ep->responded = 0;
- spin_unlock (&dev->lock);
- tmp = dev->driver->setup (&dev->gadget, &u.r);
- spin_lock (&dev->lock);
- }
-
- /* stall ep0 on error */
- if (tmp < 0) {
-do_stall:
- VDEBUG (dev, "req %02x.%02x protocol STALL; stat %d\n",
- u.r.bRequestType, u.r.bRequest, tmp);
- dev->protocol_stall = 1;
- }
-
- /* some in/out token irq should follow; maybe stall then.
- * driver must queue a request (even zlp) or halt ep0
- * before the host times out.
- */
- }
-
-#undef w_value
-#undef w_index
-#undef w_length
-
-next_endpoints:
- /* endpoint data irq ? */
- scratch = stat & 0x7f;
- stat &= ~0x7f;
- for (num = 0; scratch; num++) {
- u32 t;
-
- /* do this endpoint's FIFO and queue need tending? */
- t = 1 << num;
- if ((scratch & t) == 0)
- continue;
- scratch ^= t;
-
- ep = &dev->ep [num];
- handle_ep_small (ep);
- }
-
- if (stat)
- DEBUG (dev, "unhandled irqstat0 %08x\n", stat);
-}
-
-#define DMA_INTERRUPTS ( \
- (1 << DMA_D_INTERRUPT) \
- | (1 << DMA_C_INTERRUPT) \
- | (1 << DMA_B_INTERRUPT) \
- | (1 << DMA_A_INTERRUPT))
-#define PCI_ERROR_INTERRUPTS ( \
- (1 << PCI_MASTER_ABORT_RECEIVED_INTERRUPT) \
- | (1 << PCI_TARGET_ABORT_RECEIVED_INTERRUPT) \
- | (1 << PCI_RETRY_ABORT_INTERRUPT))
-
-static void handle_stat1_irqs (struct net2280 *dev, u32 stat)
-{
- struct net2280_ep *ep;
- u32 tmp, num, mask, scratch;
-
- /* after disconnect there's nothing else to do! */
- tmp = (1 << VBUS_INTERRUPT) | (1 << ROOT_PORT_RESET_INTERRUPT);
- mask = (1 << HIGH_SPEED) | (1 << FULL_SPEED);
-
- /* VBUS disconnect is indicated by VBUS_PIN and VBUS_INTERRUPT set.
- * Root Port Reset is indicated by ROOT_PORT_RESET_INTERRUPT set and
- * both HIGH_SPEED and FULL_SPEED clear (as ROOT_PORT_RESET_INTERRUPT
- * only indicates a change in the reset state).
- */
- if (stat & tmp) {
- writel (tmp, &dev->regs->irqstat1);
- if ((((stat & (1 << ROOT_PORT_RESET_INTERRUPT))
- && ((readl (&dev->usb->usbstat) & mask)
- == 0))
- || ((readl (&dev->usb->usbctl)
- & (1 << VBUS_PIN)) == 0)
- ) && ( dev->gadget.speed != USB_SPEED_UNKNOWN)) {
- DEBUG (dev, "disconnect %s\n",
- dev->driver->driver.name);
- stop_activity (dev, dev->driver);
- ep0_start (dev);
- return;
- }
- stat &= ~tmp;
-
- /* vBUS can bounce ... one of many reasons to ignore the
- * notion of hotplug events on bus connect/disconnect!
- */
- if (!stat)
- return;
- }
-
- /* NOTE: chip stays in PCI D0 state for now, but it could
- * enter D1 to save more power
- */
- tmp = (1 << SUSPEND_REQUEST_CHANGE_INTERRUPT);
- if (stat & tmp) {
- writel (tmp, &dev->regs->irqstat1);
- if (stat & (1 << SUSPEND_REQUEST_INTERRUPT)) {
- if (dev->driver->suspend)
- dev->driver->suspend (&dev->gadget);
- if (!enable_suspend)
- stat &= ~(1 << SUSPEND_REQUEST_INTERRUPT);
- } else {
- if (dev->driver->resume)
- dev->driver->resume (&dev->gadget);
- /* at high speed, note erratum 0133 */
- }
- stat &= ~tmp;
- }
-
- /* clear any other status/irqs */
- if (stat)
- writel (stat, &dev->regs->irqstat1);
-
- /* some status we can just ignore */
- if (dev->pdev->device == 0x2280)
- stat &= ~((1 << CONTROL_STATUS_INTERRUPT)
- | (1 << SUSPEND_REQUEST_INTERRUPT)
- | (1 << RESUME_INTERRUPT)
- | (1 << SOF_INTERRUPT));
- else
- stat &= ~((1 << CONTROL_STATUS_INTERRUPT)
- | (1 << RESUME_INTERRUPT)
- | (1 << SOF_DOWN_INTERRUPT)
- | (1 << SOF_INTERRUPT));
-
- if (!stat)
- return;
- // DEBUG (dev, "irqstat1 %08x\n", stat);
-
- /* DMA status, for ep-{a,b,c,d} */
- scratch = stat & DMA_INTERRUPTS;
- stat &= ~DMA_INTERRUPTS;
- scratch >>= 9;
- for (num = 0; scratch; num++) {
- struct net2280_dma_regs __iomem *dma;
-
- tmp = 1 << num;
- if ((tmp & scratch) == 0)
- continue;
- scratch ^= tmp;
-
- ep = &dev->ep [num + 1];
- dma = ep->dma;
-
- if (!dma)
- continue;
-
- /* clear ep's dma status */
- tmp = readl (&dma->dmastat);
- writel (tmp, &dma->dmastat);
-
- /* chaining should stop on abort, short OUT from fifo,
- * or (stat0 codepath) short OUT transfer.
- */
- if (!use_dma_chaining) {
- if ((tmp & (1 << DMA_TRANSACTION_DONE_INTERRUPT))
- == 0) {
- DEBUG (ep->dev, "%s no xact done? %08x\n",
- ep->ep.name, tmp);
- continue;
- }
- stop_dma (ep->dma);
- }
-
- /* OUT transfers terminate when the data from the
- * host is in our memory. Process whatever's done.
- * On this path, we know transfer's last packet wasn't
- * less than req->length. NAK_OUT_PACKETS may be set,
- * or the FIFO may already be holding new packets.
- *
- * IN transfers can linger in the FIFO for a very
- * long time ... we ignore that for now, accounting
- * precisely (like PIO does) needs per-packet irqs
- */
- scan_dma_completions (ep);
-
- /* disable dma on inactive queues; else maybe restart */
- if (list_empty (&ep->queue)) {
- if (use_dma_chaining)
- stop_dma (ep->dma);
- } else {
- tmp = readl (&dma->dmactl);
- if (!use_dma_chaining
- || (tmp & (1 << DMA_ENABLE)) == 0)
- restart_dma (ep);
- else if (ep->is_in && use_dma_chaining) {
- struct net2280_request *req;
- __le32 dmacount;
-
- /* the descriptor at the head of the chain
- * may still have VALID_BIT clear; that's
- * used to trigger changing DMA_FIFO_VALIDATE
- * (affects automagic zlp writes).
- */
- req = list_entry (ep->queue.next,
- struct net2280_request, queue);
- dmacount = req->td->dmacount;
- dmacount &= cpu_to_le32 (
- (1 << VALID_BIT)
- | DMA_BYTE_COUNT_MASK);
- if (dmacount && (dmacount & valid_bit) == 0)
- restart_dma (ep);
- }
- }
- ep->irqs++;
- }
-
- /* NOTE: there are other PCI errors we might usefully notice.
- * if they appear very often, here's where to try recovering.
- */
- if (stat & PCI_ERROR_INTERRUPTS) {
- ERROR (dev, "pci dma error; stat %08x\n", stat);
- stat &= ~PCI_ERROR_INTERRUPTS;
- /* these are fatal errors, but "maybe" they won't
- * happen again ...
- */
- stop_activity (dev, dev->driver);
- ep0_start (dev);
- stat = 0;
- }
-
- if (stat)
- DEBUG (dev, "unhandled irqstat1 %08x\n", stat);
-}
-
-static irqreturn_t net2280_irq (int irq, void *_dev)
-{
- struct net2280 *dev = _dev;
-
- /* shared interrupt, not ours */
- if (!(readl(&dev->regs->irqstat0) & (1 << INTA_ASSERTED)))
- return IRQ_NONE;
-
- spin_lock (&dev->lock);
-
- /* handle disconnect, dma, and more */
- handle_stat1_irqs (dev, readl (&dev->regs->irqstat1));
-
- /* control requests and PIO */
- handle_stat0_irqs (dev, readl (&dev->regs->irqstat0));
-
- spin_unlock (&dev->lock);
-
- return IRQ_HANDLED;
-}
-
-/*-------------------------------------------------------------------------*/
-
-static void gadget_release (struct device *_dev)
-{
- struct net2280 *dev = dev_get_drvdata (_dev);
-
- kfree (dev);
-}
-
-/* tear down the binding between this driver and the pci device */
-
-static void net2280_remove (struct pci_dev *pdev)
-{
- struct net2280 *dev = pci_get_drvdata (pdev);
-
- usb_del_gadget_udc(&dev->gadget);
-
- BUG_ON(dev->driver);
-
- /* then clean up the resources we allocated during probe() */
- net2280_led_shutdown (dev);
- if (dev->requests) {
- int i;
- for (i = 1; i < 5; i++) {
- if (!dev->ep [i].dummy)
- continue;
- pci_pool_free (dev->requests, dev->ep [i].dummy,
- dev->ep [i].td_dma);
- }
- pci_pool_destroy (dev->requests);
- }
- if (dev->got_irq)
- free_irq (pdev->irq, dev);
- if (dev->regs)
- iounmap (dev->regs);
- if (dev->region)
- release_mem_region (pci_resource_start (pdev, 0),
- pci_resource_len (pdev, 0));
- if (dev->enabled)
- pci_disable_device (pdev);
- device_remove_file (&pdev->dev, &dev_attr_registers);
-
- INFO (dev, "unbind\n");
-}
-
-/* wrap this driver around the specified device, but
- * don't respond over USB until a gadget driver binds to us.
- */
-
-static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id)
-{
- struct net2280 *dev;
- unsigned long resource, len;
- void __iomem *base = NULL;
- int retval, i;
-
- /* alloc, and start init */
- dev = kzalloc (sizeof *dev, GFP_KERNEL);
- if (dev == NULL){
- retval = -ENOMEM;
- goto done;
- }
-
- pci_set_drvdata (pdev, dev);
- spin_lock_init (&dev->lock);
- dev->pdev = pdev;
- dev->gadget.ops = &net2280_ops;
- dev->gadget.max_speed = USB_SPEED_HIGH;
-
- /* the "gadget" abstracts/virtualizes the controller */
- dev->gadget.name = driver_name;
-
- /* now all the pci goodies ... */
- if (pci_enable_device (pdev) < 0) {
- retval = -ENODEV;
- goto done;
- }
- dev->enabled = 1;
-
- /* BAR 0 holds all the registers
- * BAR 1 is 8051 memory; unused here (note erratum 0103)
- * BAR 2 is fifo memory; unused here
- */
- resource = pci_resource_start (pdev, 0);
- len = pci_resource_len (pdev, 0);
- if (!request_mem_region (resource, len, driver_name)) {
- DEBUG (dev, "controller already in use\n");
- retval = -EBUSY;
- goto done;
- }
- dev->region = 1;
-
- /* FIXME provide firmware download interface to put
- * 8051 code into the chip, e.g. to turn on PCI PM.
- */
-
- base = ioremap_nocache (resource, len);
- if (base == NULL) {
- DEBUG (dev, "can't map memory\n");
- retval = -EFAULT;
- goto done;
- }
- dev->regs = (struct net2280_regs __iomem *) base;
- dev->usb = (struct net2280_usb_regs __iomem *) (base + 0x0080);
- dev->pci = (struct net2280_pci_regs __iomem *) (base + 0x0100);
- dev->dma = (struct net2280_dma_regs __iomem *) (base + 0x0180);
- dev->dep = (struct net2280_dep_regs __iomem *) (base + 0x0200);
- dev->epregs = (struct net2280_ep_regs __iomem *) (base + 0x0300);
-
- /* put into initial config, link up all endpoints */
- writel (0, &dev->usb->usbctl);
- usb_reset (dev);
- usb_reinit (dev);
-
- /* irq setup after old hardware is cleaned up */
- if (!pdev->irq) {
- ERROR (dev, "No IRQ. Check PCI setup!\n");
- retval = -ENODEV;
- goto done;
- }
-
- if (request_irq (pdev->irq, net2280_irq, IRQF_SHARED, driver_name, dev)
- != 0) {
- ERROR (dev, "request interrupt %d failed\n", pdev->irq);
- retval = -EBUSY;
- goto done;
- }
- dev->got_irq = 1;
-
- /* DMA setup */
- /* NOTE: we know only the 32 LSBs of dma addresses may be nonzero */
- dev->requests = pci_pool_create ("requests", pdev,
- sizeof (struct net2280_dma),
- 0 /* no alignment requirements */,
- 0 /* or page-crossing issues */);
- if (!dev->requests) {
- DEBUG (dev, "can't get request pool\n");
- retval = -ENOMEM;
- goto done;
- }
- for (i = 1; i < 5; i++) {
- struct net2280_dma *td;
-
- td = pci_pool_alloc (dev->requests, GFP_KERNEL,
- &dev->ep [i].td_dma);
- if (!td) {
- DEBUG (dev, "can't get dummy %d\n", i);
- retval = -ENOMEM;
- goto done;
- }
- td->dmacount = 0; /* not VALID */
- td->dmadesc = td->dmaaddr;
- dev->ep [i].dummy = td;
- }
-
- /* enable lower-overhead pci memory bursts during DMA */
- writel ( (1 << DMA_MEMORY_WRITE_AND_INVALIDATE_ENABLE)
- // 256 write retries may not be enough...
- // | (1 << PCI_RETRY_ABORT_ENABLE)
- | (1 << DMA_READ_MULTIPLE_ENABLE)
- | (1 << DMA_READ_LINE_ENABLE)
- , &dev->pci->pcimstctl);
- /* erratum 0115 shouldn't appear: Linux inits PCI_LATENCY_TIMER */
- pci_set_master (pdev);
- pci_try_set_mwi (pdev);
-
- /* ... also flushes any posted pci writes */
- dev->chiprev = get_idx_reg (dev->regs, REG_CHIPREV) & 0xffff;
-
- /* done */
- INFO (dev, "%s\n", driver_desc);
- INFO (dev, "irq %d, pci mem %p, chip rev %04x\n",
- pdev->irq, base, dev->chiprev);
- INFO (dev, "version: " DRIVER_VERSION "; dma %s\n",
- use_dma
- ? (use_dma_chaining ? "chaining" : "enabled")
- : "disabled");
- retval = device_create_file (&pdev->dev, &dev_attr_registers);
- if (retval) goto done;
-
- retval = usb_add_gadget_udc_release(&pdev->dev, &dev->gadget,
- gadget_release);
- if (retval)
- goto done;
- return 0;
-
-done:
- if (dev)
- net2280_remove (pdev);
- return retval;
-}
-
-/* make sure the board is quiescent; otherwise it will continue
- * generating IRQs across the upcoming reboot.
- */
-
-static void net2280_shutdown (struct pci_dev *pdev)
-{
- struct net2280 *dev = pci_get_drvdata (pdev);
-
- /* disable IRQs */
- writel (0, &dev->regs->pciirqenb0);
- writel (0, &dev->regs->pciirqenb1);
-
- /* disable the pullup so the host will think we're gone */
- writel (0, &dev->usb->usbctl);
-
- /* Disable full-speed test mode */
- writel(0, &dev->usb->xcvrdiag);
-}
-
-
-/*-------------------------------------------------------------------------*/
-
-static const struct pci_device_id pci_ids [] = { {
- .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe),
- .class_mask = ~0,
- .vendor = 0x17cc,
- .device = 0x2280,
- .subvendor = PCI_ANY_ID,
- .subdevice = PCI_ANY_ID,
-}, {
- .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe),
- .class_mask = ~0,
- .vendor = 0x17cc,
- .device = 0x2282,
- .subvendor = PCI_ANY_ID,
- .subdevice = PCI_ANY_ID,
-
-}, { /* end: all zeroes */ }
-};
-MODULE_DEVICE_TABLE (pci, pci_ids);
-
-/* pci driver glue; this is a "new style" PCI driver module */
-static struct pci_driver net2280_pci_driver = {
- .name = (char *) driver_name,
- .id_table = pci_ids,
-
- .probe = net2280_probe,
- .remove = net2280_remove,
- .shutdown = net2280_shutdown,
-
- /* FIXME add power management support */
-};
-
-MODULE_DESCRIPTION (DRIVER_DESC);
-MODULE_AUTHOR ("David Brownell");
-MODULE_LICENSE ("GPL");
-
-static int __init init (void)
-{
- if (!use_dma)
- use_dma_chaining = 0;
- return pci_register_driver (&net2280_pci_driver);
-}
-module_init (init);
-
-static void __exit cleanup (void)
-{
- pci_unregister_driver (&net2280_pci_driver);
-}
-module_exit (cleanup);
diff --git a/drivers/usb/gadget/net2280.h b/drivers/usb/gadget/net2280.h
deleted file mode 100644
index a844be0d683a..000000000000
--- a/drivers/usb/gadget/net2280.h
+++ /dev/null
@@ -1,308 +0,0 @@
-/*
- * NetChip 2280 high/full speed USB device controller.
- * Unlike many such controllers, this one talks PCI.
- */
-
-/*
- * Copyright (C) 2002 NetChip Technology, Inc. (http://www.netchip.com)
- * Copyright (C) 2003 David Brownell
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#include <linux/usb/net2280.h>
-
-/*-------------------------------------------------------------------------*/
-
-#ifdef __KERNEL__
-
-/* indexed registers [11.10] are accessed indirectly
- * caller must own the device lock.
- */
-
-static inline u32
-get_idx_reg (struct net2280_regs __iomem *regs, u32 index)
-{
- writel (index, &regs->idxaddr);
- /* NOTE: synchs device/cpu memory views */
- return readl (&regs->idxdata);
-}
-
-static inline void
-set_idx_reg (struct net2280_regs __iomem *regs, u32 index, u32 value)
-{
- writel (index, &regs->idxaddr);
- writel (value, &regs->idxdata);
- /* posted, may not be visible yet */
-}
-
-#endif /* __KERNEL__ */
-
-
-#define REG_DIAG 0x0
-#define RETRY_COUNTER 16
-#define FORCE_PCI_SERR 11
-#define FORCE_PCI_INTERRUPT 10
-#define FORCE_USB_INTERRUPT 9
-#define FORCE_CPU_INTERRUPT 8
-#define ILLEGAL_BYTE_ENABLES 5
-#define FAST_TIMES 4
-#define FORCE_RECEIVE_ERROR 2
-#define FORCE_TRANSMIT_CRC_ERROR 0
-#define REG_FRAME 0x02 /* from last sof */
-#define REG_CHIPREV 0x03 /* in bcd */
-#define REG_HS_NAK_RATE 0x0a /* NAK per N uframes */
-
-#define CHIPREV_1 0x0100
-#define CHIPREV_1A 0x0110
-
-#ifdef __KERNEL__
-
-/* ep a-f highspeed and fullspeed maxpacket, addresses
- * computed from ep->num
- */
-#define REG_EP_MAXPKT(dev,num) (((num) + 1) * 0x10 + \
- (((dev)->gadget.speed == USB_SPEED_HIGH) ? 0 : 1))
-
-/*-------------------------------------------------------------------------*/
-
-/* [8.3] for scatter/gather i/o
- * use struct net2280_dma_regs bitfields
- */
-struct net2280_dma {
- __le32 dmacount;
- __le32 dmaaddr; /* the buffer */
- __le32 dmadesc; /* next dma descriptor */
- __le32 _reserved;
-} __attribute__ ((aligned (16)));
-
-/*-------------------------------------------------------------------------*/
-
-/* DRIVER DATA STRUCTURES and UTILITIES */
-
-struct net2280_ep {
- struct usb_ep ep;
- struct net2280_ep_regs __iomem *regs;
- struct net2280_dma_regs __iomem *dma;
- struct net2280_dma *dummy;
- dma_addr_t td_dma; /* of dummy */
- struct net2280 *dev;
- unsigned long irqs;
-
- /* analogous to a host-side qh */
- struct list_head queue;
- const struct usb_endpoint_descriptor *desc;
- unsigned num : 8,
- fifo_size : 12,
- in_fifo_validate : 1,
- out_overflow : 1,
- stopped : 1,
- wedged : 1,
- is_in : 1,
- is_iso : 1,
- responded : 1;
-};
-
-static inline void allow_status (struct net2280_ep *ep)
-{
- /* ep0 only */
- writel ( (1 << CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE)
- | (1 << CLEAR_NAK_OUT_PACKETS)
- | (1 << CLEAR_NAK_OUT_PACKETS_MODE)
- , &ep->regs->ep_rsp);
- ep->stopped = 1;
-}
-
-/* count (<= 4) bytes in the next fifo write will be valid */
-static inline void set_fifo_bytecount (struct net2280_ep *ep, unsigned count)
-{
- writeb (count, 2 + (u8 __iomem *) &ep->regs->ep_cfg);
-}
-
-struct net2280_request {
- struct usb_request req;
- struct net2280_dma *td;
- dma_addr_t td_dma;
- struct list_head queue;
- unsigned mapped : 1,
- valid : 1;
-};
-
-struct net2280 {
- /* each pci device provides one gadget, several endpoints */
- struct usb_gadget gadget;
- spinlock_t lock;
- struct net2280_ep ep [7];
- struct usb_gadget_driver *driver;
- unsigned enabled : 1,
- protocol_stall : 1,
- softconnect : 1,
- got_irq : 1,
- region : 1;
- u16 chiprev;
-
- /* pci state used to access those endpoints */
- struct pci_dev *pdev;
- struct net2280_regs __iomem *regs;
- struct net2280_usb_regs __iomem *usb;
- struct net2280_pci_regs __iomem *pci;
- struct net2280_dma_regs __iomem *dma;
- struct net2280_dep_regs __iomem *dep;
- struct net2280_ep_regs __iomem *epregs;
-
- struct pci_pool *requests;
- // statistics...
-};
-
-static inline void set_halt (struct net2280_ep *ep)
-{
- /* ep0 and bulk/intr endpoints */
- writel ( (1 << CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE)
- /* set NAK_OUT for erratum 0114 */
- | ((ep->dev->chiprev == CHIPREV_1) << SET_NAK_OUT_PACKETS)
- | (1 << SET_ENDPOINT_HALT)
- , &ep->regs->ep_rsp);
-}
-
-static inline void clear_halt (struct net2280_ep *ep)
-{
- /* ep0 and bulk/intr endpoints */
- writel ( (1 << CLEAR_ENDPOINT_HALT)
- | (1 << CLEAR_ENDPOINT_TOGGLE)
- /* unless the gadget driver left a short packet in the
- * fifo, this reverses the erratum 0114 workaround.
- */
- | ((ep->dev->chiprev == CHIPREV_1) << CLEAR_NAK_OUT_PACKETS)
- , &ep->regs->ep_rsp);
-}
-
-#ifdef USE_RDK_LEDS
-
-static inline void net2280_led_init (struct net2280 *dev)
-{
- /* LED3 (green) is on during USB activity. note erratum 0113. */
- writel ((1 << GPIO3_LED_SELECT)
- | (1 << GPIO3_OUTPUT_ENABLE)
- | (1 << GPIO2_OUTPUT_ENABLE)
- | (1 << GPIO1_OUTPUT_ENABLE)
- | (1 << GPIO0_OUTPUT_ENABLE)
- , &dev->regs->gpioctl);
-}
-
-/* indicate speed with bi-color LED 0/1 */
-static inline
-void net2280_led_speed (struct net2280 *dev, enum usb_device_speed speed)
-{
- u32 val = readl (&dev->regs->gpioctl);
- switch (speed) {
- case USB_SPEED_HIGH: /* green */
- val &= ~(1 << GPIO0_DATA);
- val |= (1 << GPIO1_DATA);
- break;
- case USB_SPEED_FULL: /* red */
- val &= ~(1 << GPIO1_DATA);
- val |= (1 << GPIO0_DATA);
- break;
- default: /* (off/black) */
- val &= ~((1 << GPIO1_DATA) | (1 << GPIO0_DATA));
- break;
- }
- writel (val, &dev->regs->gpioctl);
-}
-
-/* indicate power with LED 2 */
-static inline void net2280_led_active (struct net2280 *dev, int is_active)
-{
- u32 val = readl (&dev->regs->gpioctl);
-
- // FIXME this LED never seems to turn on.
- if (is_active)
- val |= GPIO2_DATA;
- else
- val &= ~GPIO2_DATA;
- writel (val, &dev->regs->gpioctl);
-}
-static inline void net2280_led_shutdown (struct net2280 *dev)
-{
- /* turn off all four GPIO*_DATA bits */
- writel (readl (&dev->regs->gpioctl) & ~0x0f,
- &dev->regs->gpioctl);
-}
-
-#else
-
-#define net2280_led_init(dev) do { } while (0)
-#define net2280_led_speed(dev, speed) do { } while (0)
-#define net2280_led_shutdown(dev) do { } while (0)
-
-#endif
-
-/*-------------------------------------------------------------------------*/
-
-#define xprintk(dev,level,fmt,args...) \
- printk(level "%s %s: " fmt , driver_name , \
- pci_name(dev->pdev) , ## args)
-
-#ifdef DEBUG
-#undef DEBUG
-#define DEBUG(dev,fmt,args...) \
- xprintk(dev , KERN_DEBUG , fmt , ## args)
-#else
-#define DEBUG(dev,fmt,args...) \
- do { } while (0)
-#endif /* DEBUG */
-
-#ifdef VERBOSE
-#define VDEBUG DEBUG
-#else
-#define VDEBUG(dev,fmt,args...) \
- do { } while (0)
-#endif /* VERBOSE */
-
-#define ERROR(dev,fmt,args...) \
- xprintk(dev , KERN_ERR , fmt , ## args)
-#define WARNING(dev,fmt,args...) \
- xprintk(dev , KERN_WARNING , fmt , ## args)
-#define INFO(dev,fmt,args...) \
- xprintk(dev , KERN_INFO , fmt , ## args)
-
-/*-------------------------------------------------------------------------*/
-
-static inline void start_out_naking (struct net2280_ep *ep)
-{
- /* NOTE: hardware races lurk here, and PING protocol issues */
- writel ((1 << SET_NAK_OUT_PACKETS), &ep->regs->ep_rsp);
- /* synch with device */
- readl (&ep->regs->ep_rsp);
-}
-
-#ifdef DEBUG
-static inline void assert_out_naking (struct net2280_ep *ep, const char *where)
-{
- u32 tmp = readl (&ep->regs->ep_stat);
-
- if ((tmp & (1 << NAK_OUT_PACKETS)) == 0) {
- DEBUG (ep->dev, "%s %s %08x !NAK\n",
- ep->ep.name, where, tmp);
- writel ((1 << SET_NAK_OUT_PACKETS),
- &ep->regs->ep_rsp);
- }
-}
-#define ASSERT_OUT_NAKING(ep) assert_out_naking(ep,__func__)
-#else
-#define ASSERT_OUT_NAKING(ep) do {} while (0)
-#endif
-
-static inline void stop_out_naking (struct net2280_ep *ep)
-{
- u32 tmp;
-
- tmp = readl (&ep->regs->ep_stat);
- if ((tmp & (1 << NAK_OUT_PACKETS)) != 0)
- writel ((1 << CLEAR_NAK_OUT_PACKETS), &ep->regs->ep_rsp);
-}
-
-#endif /* __KERNEL__ */
diff --git a/drivers/usb/gadget/u_os_desc.h b/drivers/usb/gadget/u_os_desc.h
index ea5cf8c2da28..947b7ddff691 100644
--- a/drivers/usb/gadget/u_os_desc.h
+++ b/drivers/usb/gadget/u_os_desc.h
@@ -35,27 +35,63 @@
#define USB_EXT_PROP_UNICODE_LINK 6
#define USB_EXT_PROP_UNICODE_MULTI 7
+static inline u8 *__usb_ext_prop_ptr(u8 *buf, size_t offset)
+{
+ return buf + offset;
+}
+
+static inline u8 *usb_ext_prop_size_ptr(u8 *buf)
+{
+ return __usb_ext_prop_ptr(buf, USB_EXT_PROP_DW_SIZE);
+}
+
+static inline u8 *usb_ext_prop_type_ptr(u8 *buf)
+{
+ return __usb_ext_prop_ptr(buf, USB_EXT_PROP_DW_PROPERTY_DATA_TYPE);
+}
+
+static inline u8 *usb_ext_prop_name_len_ptr(u8 *buf)
+{
+ return __usb_ext_prop_ptr(buf, USB_EXT_PROP_W_PROPERTY_NAME_LENGTH);
+}
+
+static inline u8 *usb_ext_prop_name_ptr(u8 *buf)
+{
+ return __usb_ext_prop_ptr(buf, USB_EXT_PROP_B_PROPERTY_NAME);
+}
+
+static inline u8 *usb_ext_prop_data_len_ptr(u8 *buf, size_t off)
+{
+ return __usb_ext_prop_ptr(buf,
+ USB_EXT_PROP_DW_PROPERTY_DATA_LENGTH + off);
+}
+
+static inline u8 *usb_ext_prop_data_ptr(u8 *buf, size_t off)
+{
+ return __usb_ext_prop_ptr(buf, USB_EXT_PROP_B_PROPERTY_DATA + off);
+}
+
static inline void usb_ext_prop_put_size(u8 *buf, int dw_size)
{
- put_unaligned_le32(dw_size, &buf[USB_EXT_PROP_DW_SIZE]);
+ put_unaligned_le32(dw_size, usb_ext_prop_size_ptr(buf));
}
static inline void usb_ext_prop_put_type(u8 *buf, int type)
{
- put_unaligned_le32(type, &buf[USB_EXT_PROP_DW_PROPERTY_DATA_TYPE]);
+ put_unaligned_le32(type, usb_ext_prop_type_ptr(buf));
}
static inline int usb_ext_prop_put_name(u8 *buf, const char *name, int pnl)
{
int result;
- put_unaligned_le16(pnl, &buf[USB_EXT_PROP_W_PROPERTY_NAME_LENGTH]);
+ put_unaligned_le16(pnl, usb_ext_prop_name_len_ptr(buf));
result = utf8s_to_utf16s(name, strlen(name), UTF16_LITTLE_ENDIAN,
- (wchar_t *) &buf[USB_EXT_PROP_B_PROPERTY_NAME], pnl - 2);
+ (wchar_t *) usb_ext_prop_name_ptr(buf), pnl - 2);
if (result < 0)
return result;
- put_unaligned_le16(0, &buf[USB_EXT_PROP_B_PROPERTY_NAME + pnl]);
+ put_unaligned_le16(0, &buf[USB_EXT_PROP_B_PROPERTY_NAME + pnl - 2]);
return pnl;
}
@@ -63,26 +99,23 @@ static inline int usb_ext_prop_put_name(u8 *buf, const char *name, int pnl)
static inline void usb_ext_prop_put_binary(u8 *buf, int pnl, const u8 *data,
int data_len)
{
- put_unaligned_le32(data_len,
- &buf[USB_EXT_PROP_DW_PROPERTY_DATA_LENGTH + pnl]);
- memcpy(&buf[USB_EXT_PROP_B_PROPERTY_DATA + pnl], data, data_len);
+ put_unaligned_le32(data_len, usb_ext_prop_data_len_ptr(buf, pnl));
+ memcpy(usb_ext_prop_data_ptr(buf, pnl), data, data_len);
}
static inline int usb_ext_prop_put_unicode(u8 *buf, int pnl, const char *string,
int data_len)
{
int result;
- put_unaligned_le32(data_len,
- &buf[USB_EXT_PROP_DW_PROPERTY_DATA_LENGTH + pnl]);
-
+ put_unaligned_le32(data_len, usb_ext_prop_data_len_ptr(buf, pnl));
result = utf8s_to_utf16s(string, data_len >> 1, UTF16_LITTLE_ENDIAN,
- (wchar_t *) &buf[USB_EXT_PROP_B_PROPERTY_DATA + pnl],
+ (wchar_t *) usb_ext_prop_data_ptr(buf, pnl),
data_len - 2);
if (result < 0)
return result;
put_unaligned_le16(0,
- &buf[USB_EXT_PROP_B_PROPERTY_DATA + pnl + data_len]);
+ &buf[USB_EXT_PROP_B_PROPERTY_DATA + pnl + data_len - 2]);
return data_len;
}
diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig
new file mode 100644
index 000000000000..5151f947a4f5
--- /dev/null
+++ b/drivers/usb/gadget/udc/Kconfig
@@ -0,0 +1,385 @@
+#
+# USB Gadget support on a system involves
+# (a) a peripheral controller, and
+# (b) the gadget driver using it.
+#
+# NOTE: Gadget support ** DOES NOT ** depend on host-side CONFIG_USB !!
+#
+# - Host systems (like PCs) need CONFIG_USB (with "A" jacks).
+# - Peripherals (like PDAs) need CONFIG_USB_GADGET (with "B" jacks).
+# - Some systems have both kinds of controllers.
+#
+# With help from a special transceiver and a "Mini-AB" jack, systems with
+# both kinds of controller can also support "USB On-the-Go" (CONFIG_USB_OTG).
+#
+
+#
+# USB Peripheral Controller Support
+#
+# The order here is alphabetical, except that integrated controllers go
+# before discrete ones so they will be the initial/default value:
+# - integrated/SOC controllers first
+# - licensed IP used in both SOC and discrete versions
+# - discrete ones (including all PCI-only controllers)
+# - debug/dummy gadget+hcd is last.
+#
+menu "USB Peripheral Controller"
+
+#
+# Integrated controllers
+#
+
+config USB_AT91
+ tristate "Atmel AT91 USB Device Port"
+ depends on ARCH_AT91
+ help
+ Many Atmel AT91 processors (such as the AT91RM2000) have a
+ full speed USB Device Port with support for five configurable
+ endpoints (plus endpoint zero).
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "at91_udc" and force all
+ gadget drivers to also be dynamically linked.
+
+config USB_LPC32XX
+ tristate "LPC32XX USB Peripheral Controller"
+ depends on ARCH_LPC32XX && I2C
+ select USB_ISP1301
+ help
+ This option selects the USB device controller in the LPC32xx SoC.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "lpc32xx_udc" and force all
+ gadget drivers to also be dynamically linked.
+
+config USB_ATMEL_USBA
+ tristate "Atmel USBA"
+ depends on AVR32 || ARCH_AT91
+ help
+ USBA is the integrated high-speed USB Device controller on
+ the AT32AP700x, some AT91SAM9 and AT91CAP9 processors from Atmel.
+
+config USB_BCM63XX_UDC
+ tristate "Broadcom BCM63xx Peripheral Controller"
+ depends on BCM63XX
+ help
+ Many Broadcom BCM63xx chipsets (such as the BCM6328) have a
+ high speed USB Device Port with support for four fixed endpoints
+ (plus endpoint zero).
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "bcm63xx_udc".
+
+config USB_FSL_USB2
+ tristate "Freescale Highspeed USB DR Peripheral Controller"
+ depends on FSL_SOC || ARCH_MXC
+ select USB_FSL_MPH_DR_OF if OF
+ help
+ Some of Freescale PowerPC and i.MX processors have a High Speed
+ Dual-Role(DR) USB controller, which supports device mode.
+
+ The number of programmable endpoints is different through
+ SOC revisions.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "fsl_usb2_udc" and force
+ all gadget drivers to also be dynamically linked.
+
+config USB_FUSB300
+ tristate "Faraday FUSB300 USB Peripheral Controller"
+ depends on !PHYS_ADDR_T_64BIT && HAS_DMA
+ help
+ Faraday usb device controller FUSB300 driver
+
+config USB_FOTG210_UDC
+ depends on HAS_DMA
+ tristate "Faraday FOTG210 USB Peripheral Controller"
+ help
+ Faraday USB2.0 OTG controller which can be configured as
+ high speed or full speed USB device. This driver supppors
+ Bulk Transfer so far.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "fotg210_udc".
+
+config USB_GR_UDC
+ tristate "Aeroflex Gaisler GRUSBDC USB Peripheral Controller Driver"
+ depends on HAS_DMA
+ help
+ Select this to support Aeroflex Gaisler GRUSBDC cores from the GRLIB
+ VHDL IP core library.
+
+config USB_OMAP
+ tristate "OMAP USB Device Controller"
+ depends on ARCH_OMAP1
+ depends on ISP1301_OMAP || !(MACH_OMAP_H2 || MACH_OMAP_H3)
+ help
+ Many Texas Instruments OMAP processors have flexible full
+ speed USB device controllers, with support for up to 30
+ endpoints (plus endpoint zero). This driver supports the
+ controller in the OMAP 1611, and should work with controllers
+ in other OMAP processors too, given minor tweaks.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "omap_udc" and force all
+ gadget drivers to also be dynamically linked.
+
+config USB_PXA25X
+ tristate "PXA 25x or IXP 4xx"
+ depends on (ARCH_PXA && PXA25x) || ARCH_IXP4XX
+ help
+ Intel's PXA 25x series XScale ARM-5TE processors include
+ an integrated full speed USB 1.1 device controller. The
+ controller in the IXP 4xx series is register-compatible.
+
+ It has fifteen fixed-function endpoints, as well as endpoint
+ zero (for control transfers).
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "pxa25x_udc" and force all
+ gadget drivers to also be dynamically linked.
+
+# if there's only one gadget driver, using only two bulk endpoints,
+# don't waste memory for the other endpoints
+config USB_PXA25X_SMALL
+ depends on USB_PXA25X
+ bool
+ default n if USB_ETH_RNDIS
+ default y if USB_ZERO
+ default y if USB_ETH
+ default y if USB_G_SERIAL
+
+config USB_R8A66597
+ tristate "Renesas R8A66597 USB Peripheral Controller"
+ depends on HAS_DMA
+ help
+ R8A66597 is a discrete USB host and peripheral controller chip that
+ supports both full and high speed USB 2.0 data transfers.
+ It has nine configurable endpoints, and endpoint zero.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "r8a66597_udc" and force all
+ gadget drivers to also be dynamically linked.
+
+config USB_RENESAS_USBHS_UDC
+ tristate 'Renesas USBHS controller'
+ depends on USB_RENESAS_USBHS
+ help
+ Renesas USBHS is a discrete USB host and peripheral controller chip
+ that supports both full and high speed USB 2.0 data transfers.
+ It has nine or more configurable endpoints, and endpoint zero.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "renesas_usbhs" and force all
+ gadget drivers to also be dynamically linked.
+
+config USB_PXA27X
+ tristate "PXA 27x"
+ help
+ Intel's PXA 27x series XScale ARM v5TE processors include
+ an integrated full speed USB 1.1 device controller.
+
+ It has up to 23 endpoints, as well as endpoint zero (for
+ control transfers).
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "pxa27x_udc" and force all
+ gadget drivers to also be dynamically linked.
+
+config USB_S3C2410
+ tristate "S3C2410 USB Device Controller"
+ depends on ARCH_S3C24XX
+ help
+ Samsung's S3C2410 is an ARM-4 processor with an integrated
+ full speed USB 1.1 device controller. It has 4 configurable
+ endpoints, as well as endpoint zero (for control transfers).
+
+ This driver has been tested on the S3C2410, S3C2412, and
+ S3C2440 processors.
+
+config USB_S3C2410_DEBUG
+ boolean "S3C2410 udc debug messages"
+ depends on USB_S3C2410
+
+config USB_S3C_HSUDC
+ tristate "S3C2416, S3C2443 and S3C2450 USB Device Controller"
+ depends on ARCH_S3C24XX
+ help
+ Samsung's S3C2416, S3C2443 and S3C2450 is an ARM9 based SoC
+ integrated with dual speed USB 2.0 device controller. It has
+ 8 endpoints, as well as endpoint zero.
+
+ This driver has been tested on S3C2416 and S3C2450 processors.
+
+config USB_MV_UDC
+ tristate "Marvell USB2.0 Device Controller"
+ depends on HAS_DMA
+ help
+ Marvell Socs (including PXA and MMP series) include a high speed
+ USB2.0 OTG controller, which can be configured as high speed or
+ full speed USB peripheral.
+
+config USB_MV_U3D
+ depends on HAS_DMA
+ tristate "MARVELL PXA2128 USB 3.0 controller"
+ help
+ MARVELL PXA2128 Processor series include a super speed USB3.0 device
+ controller, which support super speed USB peripheral.
+
+#
+# Controllers available in both integrated and discrete versions
+#
+
+config USB_M66592
+ tristate "Renesas M66592 USB Peripheral Controller"
+ help
+ M66592 is a discrete USB peripheral controller chip that
+ supports both full and high speed USB 2.0 data transfers.
+ It has seven configurable endpoints, and endpoint zero.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "m66592_udc" and force all
+ gadget drivers to also be dynamically linked.
+
+#
+# Controllers available only in discrete form (and all PCI controllers)
+#
+
+config USB_AMD5536UDC
+ tristate "AMD5536 UDC"
+ depends on PCI
+ help
+ The AMD5536 UDC is part of the AMD Geode CS5536, an x86 southbridge.
+ It is a USB Highspeed DMA capable USB device controller. Beside ep0
+ it provides 4 IN and 4 OUT endpoints (bulk or interrupt type).
+ The UDC port supports OTG operation, and may be used as a host port
+ if it's not being used to implement peripheral or OTG roles.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "amd5536udc" and force all
+ gadget drivers to also be dynamically linked.
+
+config USB_FSL_QE
+ tristate "Freescale QE/CPM USB Device Controller"
+ depends on FSL_SOC && (QUICC_ENGINE || CPM)
+ help
+ Some of Freescale PowerPC processors have a Full Speed
+ QE/CPM2 USB controller, which support device mode with 4
+ programmable endpoints. This driver supports the
+ controller in the MPC8360 and MPC8272, and should work with
+ controllers having QE or CPM2, given minor tweaks.
+
+ Set CONFIG_USB_GADGET to "m" to build this driver as a
+ dynamically linked module called "fsl_qe_udc".
+
+config USB_NET2272
+ tristate "PLX NET2272"
+ help
+ PLX NET2272 is a USB peripheral controller which supports
+ both full and high speed USB 2.0 data transfers.
+
+ It has three configurable endpoints, as well as endpoint zero
+ (for control transfer).
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "net2272" and force all
+ gadget drivers to also be dynamically linked.
+
+config USB_NET2272_DMA
+ boolean "Support external DMA controller"
+ depends on USB_NET2272 && HAS_DMA
+ help
+ The NET2272 part can optionally support an external DMA
+ controller, but your board has to have support in the
+ driver itself.
+
+ If unsure, say "N" here. The driver works fine in PIO mode.
+
+config USB_NET2280
+ tristate "NetChip 228x / PLX USB338x"
+ depends on PCI
+ help
+ NetChip 2280 / 2282 is a PCI based USB peripheral controller which
+ supports both full and high speed USB 2.0 data transfers.
+
+ It has six configurable endpoints, as well as endpoint zero
+ (for control transfers) and several endpoints with dedicated
+ functions.
+
+ PLX 3380 / 3382 is a PCIe based USB peripheral controller which
+ supports full, high speed USB 2.0 and super speed USB 3.0
+ data transfers.
+
+ It has eight configurable endpoints, as well as endpoint zero
+ (for control transfers) and several endpoints with dedicated
+ functions.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "net2280" and force all
+ gadget drivers to also be dynamically linked.
+
+config USB_GOKU
+ tristate "Toshiba TC86C001 'Goku-S'"
+ depends on PCI
+ help
+ The Toshiba TC86C001 is a PCI device which includes controllers
+ for full speed USB devices, IDE, I2C, SIO, plus a USB host (OHCI).
+
+ The device controller has three configurable (bulk or interrupt)
+ endpoints, plus endpoint zero (for control transfers).
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "goku_udc" and to force all
+ gadget drivers to also be dynamically linked.
+
+config USB_EG20T
+ tristate "Intel EG20T PCH/LAPIS Semiconductor IOH(ML7213/ML7831) UDC"
+ depends on PCI
+ help
+ This is a USB device driver for EG20T PCH.
+ EG20T PCH is the platform controller hub that is used in Intel's
+ general embedded platform. EG20T PCH has USB device interface.
+ Using this interface, it is able to access system devices connected
+ to USB device.
+ This driver enables USB device function.
+ USB device is a USB peripheral controller which
+ supports both full and high speed USB 2.0 data transfers.
+ This driver supports both control transfer and bulk transfer modes.
+ This driver dose not support interrupt transfer or isochronous
+ transfer modes.
+
+ This driver also can be used for LAPIS Semiconductor's ML7213 which is
+ for IVI(In-Vehicle Infotainment) use.
+ ML7831 is for general purpose use.
+ ML7213/ML7831 is companion chip for Intel Atom E6xx series.
+ ML7213/ML7831 is completely compatible for Intel EG20T PCH.
+
+#
+# LAST -- dummy/emulated controller
+#
+
+config USB_DUMMY_HCD
+ tristate "Dummy HCD (DEVELOPMENT)"
+ depends on USB=y || (USB=m && USB_GADGET=m)
+ help
+ This host controller driver emulates USB, looping all data transfer
+ requests back to a USB "gadget driver" in the same host. The host
+ side is the master; the gadget side is the slave. Gadget drivers
+ can be high, full, or low speed; and they have access to endpoints
+ like those from NET2280, PXA2xx, or SA1100 hardware.
+
+ This may help in some stages of creating a driver to embed in a
+ Linux device, since it lets you debug several parts of the gadget
+ driver without its hardware or drivers being involved.
+
+ Since such a gadget side driver needs to interoperate with a host
+ side Linux-USB device driver, this may help to debug both sides
+ of a USB protocol stack.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "dummy_hcd" and force all
+ gadget drivers to also be dynamically linked.
+
+# NOTE: Please keep dummy_hcd LAST so that "real hardware" appears
+# first and will be selected by default.
+
+endmenu
diff --git a/drivers/usb/gadget/udc/Makefile b/drivers/usb/gadget/udc/Makefile
new file mode 100644
index 000000000000..4096122bb283
--- /dev/null
+++ b/drivers/usb/gadget/udc/Makefile
@@ -0,0 +1,31 @@
+#
+# USB peripheral controller drivers
+#
+obj-$(CONFIG_USB_GADGET) += udc-core.o
+obj-$(CONFIG_USB_DUMMY_HCD) += dummy_hcd.o
+obj-$(CONFIG_USB_NET2272) += net2272.o
+obj-$(CONFIG_USB_NET2280) += net2280.o
+obj-$(CONFIG_USB_AMD5536UDC) += amd5536udc.o
+obj-$(CONFIG_USB_PXA25X) += pxa25x_udc.o
+obj-$(CONFIG_USB_PXA27X) += pxa27x_udc.o
+obj-$(CONFIG_USB_GOKU) += goku_udc.o
+obj-$(CONFIG_USB_OMAP) += omap_udc.o
+obj-$(CONFIG_USB_S3C2410) += s3c2410_udc.o
+obj-$(CONFIG_USB_AT91) += at91_udc.o
+obj-$(CONFIG_USB_ATMEL_USBA) += atmel_usba_udc.o
+obj-$(CONFIG_USB_BCM63XX_UDC) += bcm63xx_udc.o
+obj-$(CONFIG_USB_FSL_USB2) += fsl_usb2_udc.o
+fsl_usb2_udc-y := fsl_udc_core.o
+fsl_usb2_udc-$(CONFIG_ARCH_MXC) += fsl_mxc_udc.o
+obj-$(CONFIG_USB_M66592) += m66592-udc.o
+obj-$(CONFIG_USB_R8A66597) += r8a66597-udc.o
+obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o
+obj-$(CONFIG_USB_S3C_HSUDC) += s3c-hsudc.o
+obj-$(CONFIG_USB_LPC32XX) += lpc32xx_udc.o
+obj-$(CONFIG_USB_EG20T) += pch_udc.o
+obj-$(CONFIG_USB_MV_UDC) += mv_udc.o
+mv_udc-y := mv_udc_core.o
+obj-$(CONFIG_USB_FUSB300) += fusb300_udc.o
+obj-$(CONFIG_USB_FOTG210_UDC) += fotg210-udc.o
+obj-$(CONFIG_USB_MV_U3D) += mv_u3d_core.o
+obj-$(CONFIG_USB_GR_UDC) += gr_udc.o
diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/udc/amd5536udc.c
index 41b062eb4de0..41b062eb4de0 100644
--- a/drivers/usb/gadget/amd5536udc.c
+++ b/drivers/usb/gadget/udc/amd5536udc.c
diff --git a/drivers/usb/gadget/amd5536udc.h b/drivers/usb/gadget/udc/amd5536udc.h
index 6744d3b83109..6744d3b83109 100644
--- a/drivers/usb/gadget/amd5536udc.h
+++ b/drivers/usb/gadget/udc/amd5536udc.h
diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/udc/at91_udc.c
index cfd18bcca723..cfd18bcca723 100644
--- a/drivers/usb/gadget/at91_udc.c
+++ b/drivers/usb/gadget/udc/at91_udc.c
diff --git a/drivers/usb/gadget/at91_udc.h b/drivers/usb/gadget/udc/at91_udc.h
index 017524663381..017524663381 100644
--- a/drivers/usb/gadget/at91_udc.h
+++ b/drivers/usb/gadget/udc/at91_udc.h
diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c
index 76023ce449a3..906e65f0e4fa 100644
--- a/drivers/usb/gadget/atmel_usba_udc.c
+++ b/drivers/usb/gadget/udc/atmel_usba_udc.c
@@ -1979,7 +1979,7 @@ static struct usba_ep * usba_udc_pdata(struct platform_device *pdev,
return eps;
}
-static int __init usba_udc_probe(struct platform_device *pdev)
+static int usba_udc_probe(struct platform_device *pdev)
{
struct resource *regs, *fifo;
struct clk *pclk, *hclk;
diff --git a/drivers/usb/gadget/atmel_usba_udc.h b/drivers/usb/gadget/udc/atmel_usba_udc.h
index a70706e8cb02..a70706e8cb02 100644
--- a/drivers/usb/gadget/atmel_usba_udc.h
+++ b/drivers/usb/gadget/udc/atmel_usba_udc.h
diff --git a/drivers/usb/gadget/bcm63xx_udc.c b/drivers/usb/gadget/udc/bcm63xx_udc.c
index e969eb809a85..e969eb809a85 100644
--- a/drivers/usb/gadget/bcm63xx_udc.c
+++ b/drivers/usb/gadget/udc/bcm63xx_udc.c
diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c
index 2b54955d3166..2b54955d3166 100644
--- a/drivers/usb/gadget/dummy_hcd.c
+++ b/drivers/usb/gadget/udc/dummy_hcd.c
diff --git a/drivers/usb/gadget/fotg210-udc.c b/drivers/usb/gadget/udc/fotg210-udc.c
index e143d69f6017..e143d69f6017 100644
--- a/drivers/usb/gadget/fotg210-udc.c
+++ b/drivers/usb/gadget/udc/fotg210-udc.c
diff --git a/drivers/usb/gadget/fotg210.h b/drivers/usb/gadget/udc/fotg210.h
index bbf991bcbe7c..bbf991bcbe7c 100644
--- a/drivers/usb/gadget/fotg210.h
+++ b/drivers/usb/gadget/udc/fotg210.h
diff --git a/drivers/usb/gadget/fsl_mxc_udc.c b/drivers/usb/gadget/udc/fsl_mxc_udc.c
index 9b140fc4d3bc..f16e149c5b3e 100644
--- a/drivers/usb/gadget/fsl_mxc_udc.c
+++ b/drivers/usb/gadget/udc/fsl_mxc_udc.c
@@ -18,6 +18,8 @@
#include <linux/platform_device.h>
#include <linux/io.h>
+#include "fsl_usb2_udc.h"
+
static struct clk *mxc_ahb_clk;
static struct clk *mxc_per_clk;
static struct clk *mxc_ipg_clk;
diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/udc/fsl_qe_udc.c
index ad5483335167..732430804841 100644
--- a/drivers/usb/gadget/fsl_qe_udc.c
+++ b/drivers/usb/gadget/udc/fsl_qe_udc.c
@@ -2539,7 +2539,7 @@ static int qe_udc_probe(struct platform_device *ofdev)
goto err2;
/* create a buf for ZLP send, need to remain zeroed */
- udc->nullbuf = kzalloc(256, GFP_KERNEL);
+ udc->nullbuf = devm_kzalloc(&ofdev->dev, 256, GFP_KERNEL);
if (udc->nullbuf == NULL) {
dev_err(udc->dev, "cannot alloc nullbuf\n");
ret = -ENOMEM;
@@ -2547,10 +2547,10 @@ static int qe_udc_probe(struct platform_device *ofdev)
}
/* buffer for data of get_status request */
- udc->statusbuf = kzalloc(2, GFP_KERNEL);
+ udc->statusbuf = devm_kzalloc(&ofdev->dev, 2, GFP_KERNEL);
if (udc->statusbuf == NULL) {
ret = -ENOMEM;
- goto err4;
+ goto err3;
}
udc->nullp = virt_to_phys((void *)udc->nullbuf);
@@ -2581,13 +2581,13 @@ static int qe_udc_probe(struct platform_device *ofdev)
if (ret) {
dev_err(udc->dev, "cannot request irq %d err %d\n",
udc->usb_irq, ret);
- goto err5;
+ goto err4;
}
ret = usb_add_gadget_udc_release(&ofdev->dev, &udc->gadget,
qe_udc_release);
if (ret)
- goto err6;
+ goto err5;
platform_set_drvdata(ofdev, udc);
dev_info(udc->dev,
@@ -2595,9 +2595,9 @@ static int qe_udc_probe(struct platform_device *ofdev)
(udc->soc_type == PORT_QE) ? "QE" : "CPM");
return 0;
-err6:
- free_irq(udc->usb_irq, udc);
err5:
+ free_irq(udc->usb_irq, udc);
+err4:
irq_dispose_mapping(udc->usb_irq);
err_noirq:
if (udc->nullmap) {
@@ -2610,9 +2610,6 @@ err_noirq:
udc->nullp, 256,
DMA_TO_DEVICE);
}
- kfree(udc->statusbuf);
-err4:
- kfree(udc->nullbuf);
err3:
ep = &udc->eps[0];
cpm_muram_free(cpm_muram_offset(ep->rxbase));
@@ -2660,8 +2657,6 @@ static int qe_udc_remove(struct platform_device *ofdev)
udc->nullp, 256,
DMA_TO_DEVICE);
}
- kfree(udc->statusbuf);
- kfree(udc->nullbuf);
ep = &udc->eps[0];
cpm_muram_free(cpm_muram_offset(ep->rxbase));
diff --git a/drivers/usb/gadget/fsl_qe_udc.h b/drivers/usb/gadget/udc/fsl_qe_udc.h
index 7026919fc901..7026919fc901 100644
--- a/drivers/usb/gadget/fsl_qe_udc.h
+++ b/drivers/usb/gadget/udc/fsl_qe_udc.h
diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c
index 28e4fc957026..75b23ea077a7 100644
--- a/drivers/usb/gadget/fsl_udc_core.c
+++ b/drivers/usb/gadget/udc/fsl_udc_core.c
@@ -59,9 +59,9 @@
static const char driver_name[] = "fsl-usb2-udc";
static const char driver_desc[] = DRIVER_DESC;
-static struct usb_dr_device *dr_regs;
+static struct usb_dr_device __iomem *dr_regs;
-static struct usb_sys_interface *usb_sys_regs;
+static struct usb_sys_interface __iomem *usb_sys_regs;
/* it is initialized in probe() */
static struct fsl_udc *udc_controller = NULL;
@@ -159,6 +159,8 @@ static inline void fsl_set_accessors(struct fsl_usb2_platform_data *pdata) {}
* request is still in progress.
*--------------------------------------------------------------*/
static void done(struct fsl_ep *ep, struct fsl_req *req, int status)
+__releases(ep->udc->lock)
+__acquires(ep->udc->lock)
{
struct fsl_udc *udc = NULL;
unsigned char stopped = ep->stopped;
@@ -1392,6 +1394,8 @@ stall:
static void setup_received_irq(struct fsl_udc *udc,
struct usb_ctrlrequest *setup)
+__releases(udc->lock)
+__acquires(udc->lock)
{
u16 wValue = le16_to_cpu(setup->wValue);
u16 wIndex = le16_to_cpu(setup->wIndex);
@@ -1957,8 +1961,7 @@ static int fsl_udc_start(struct usb_gadget *g,
&udc_controller->gadget);
if (retval < 0) {
ERR("can't bind to transceiver\n");
- driver->unbind(&udc_controller->gadget);
- udc_controller->driver = 0;
+ udc_controller->driver = NULL;
return retval;
}
}
@@ -2246,7 +2249,7 @@ static void fsl_udc_release(struct device *dev)
* init resource for globle controller
* Return the udc handle on success or NULL on failure
------------------------------------------------------------------*/
-static int __init struct_udc_setup(struct fsl_udc *udc,
+static int struct_udc_setup(struct fsl_udc *udc,
struct platform_device *pdev)
{
struct fsl_usb2_platform_data *pdata;
@@ -2298,7 +2301,7 @@ static int __init struct_udc_setup(struct fsl_udc *udc,
* ep0out is not used so do nothing here
* ep0in should be taken care
*--------------------------------------------------------------*/
-static int __init struct_ep_setup(struct fsl_udc *udc, unsigned char index,
+static int struct_ep_setup(struct fsl_udc *udc, unsigned char index,
char *name, int link)
{
struct fsl_ep *ep = &udc->eps[index];
@@ -2331,7 +2334,7 @@ static int __init struct_ep_setup(struct fsl_udc *udc, unsigned char index,
* all intialization operations implemented here except enabling usb_intr reg
* board setup should have been done in the platform code
*/
-static int __init fsl_udc_probe(struct platform_device *pdev)
+static int fsl_udc_probe(struct platform_device *pdev)
{
struct fsl_usb2_platform_data *pdata;
struct resource *res;
@@ -2380,7 +2383,7 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
goto err_release_mem_region;
}
- pdata->regs = (void *)dr_regs;
+ pdata->regs = (void __iomem *)dr_regs;
/*
* do platform specific init: check the clock, grab/config pins, etc.
diff --git a/drivers/usb/gadget/fsl_usb2_udc.h b/drivers/usb/gadget/udc/fsl_usb2_udc.h
index c6703bb07b23..84715625b2b3 100644
--- a/drivers/usb/gadget/fsl_usb2_udc.h
+++ b/drivers/usb/gadget/udc/fsl_usb2_udc.h
@@ -12,6 +12,9 @@
#ifndef __FSL_USB2_UDC_H
#define __FSL_USB2_UDC_H
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
/* ### define USB registers here
*/
#define USB_MAX_CTRL_PAYLOAD 64
diff --git a/drivers/usb/gadget/fusb300_udc.c b/drivers/usb/gadget/udc/fusb300_udc.c
index 3deb4e938071..d40255f784df 100644
--- a/drivers/usb/gadget/fusb300_udc.c
+++ b/drivers/usb/gadget/udc/fusb300_udc.c
@@ -1325,8 +1325,6 @@ static int fusb300_udc_stop(struct usb_gadget *g,
{
struct fusb300 *fusb300 = to_fusb300(g);
- driver->unbind(&fusb300->gadget);
-
init_controller(fusb300);
fusb300->driver = NULL;
@@ -1359,7 +1357,7 @@ static int __exit fusb300_remove(struct platform_device *pdev)
return 0;
}
-static int __init fusb300_probe(struct platform_device *pdev)
+static int fusb300_probe(struct platform_device *pdev)
{
struct resource *res, *ires, *ires1;
void __iomem *reg = NULL;
diff --git a/drivers/usb/gadget/fusb300_udc.h b/drivers/usb/gadget/udc/fusb300_udc.h
index ae811d8d38b4..ae811d8d38b4 100644
--- a/drivers/usb/gadget/fusb300_udc.h
+++ b/drivers/usb/gadget/udc/fusb300_udc.h
diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/udc/gadget_chips.h
index bcd04bc66b98..bcd04bc66b98 100644
--- a/drivers/usb/gadget/gadget_chips.h
+++ b/drivers/usb/gadget/udc/gadget_chips.h
diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/udc/goku_udc.c
index 6c85839e15ad..6c85839e15ad 100644
--- a/drivers/usb/gadget/goku_udc.c
+++ b/drivers/usb/gadget/udc/goku_udc.c
diff --git a/drivers/usb/gadget/goku_udc.h b/drivers/usb/gadget/udc/goku_udc.h
index 86d2adafe149..86d2adafe149 100644
--- a/drivers/usb/gadget/goku_udc.h
+++ b/drivers/usb/gadget/udc/goku_udc.h
diff --git a/drivers/usb/gadget/gr_udc.c b/drivers/usb/gadget/udc/gr_udc.c
index c7004ee89c90..08df5c4f46ce 100644
--- a/drivers/usb/gadget/gr_udc.c
+++ b/drivers/usb/gadget/udc/gr_udc.c
@@ -2213,7 +2213,7 @@ out:
return retval;
}
-static struct of_device_id gr_match[] = {
+static const struct of_device_id gr_match[] = {
{.name = "GAISLER_USBDC"},
{.name = "01_021"},
{},
diff --git a/drivers/usb/gadget/gr_udc.h b/drivers/usb/gadget/udc/gr_udc.h
index 8388897d9ec3..8388897d9ec3 100644
--- a/drivers/usb/gadget/gr_udc.h
+++ b/drivers/usb/gadget/udc/gr_udc.h
diff --git a/drivers/usb/gadget/lpc32xx_udc.c b/drivers/usb/gadget/udc/lpc32xx_udc.c
index e471580a2a3b..1629ad7dcb80 100644
--- a/drivers/usb/gadget/lpc32xx_udc.c
+++ b/drivers/usb/gadget/udc/lpc32xx_udc.c
@@ -3036,7 +3036,7 @@ struct lpc32xx_usbd_cfg lpc32xx_usbddata = {
static u64 lpc32xx_usbd_dmamask = ~(u32) 0x7F;
-static int __init lpc32xx_udc_probe(struct platform_device *pdev)
+static int lpc32xx_udc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct lpc32xx_udc *udc;
@@ -3045,11 +3045,10 @@ static int __init lpc32xx_udc_probe(struct platform_device *pdev)
dma_addr_t dma_handle;
struct device_node *isp1301_node;
- udc = kzalloc(sizeof(*udc), GFP_KERNEL);
+ udc = kmemdup(&controller_template, sizeof(*udc), GFP_KERNEL);
if (!udc)
return -ENOMEM;
- memcpy(udc, &controller_template, sizeof(*udc));
for (i = 0; i <= 15; i++)
udc->ep[i].udc = udc;
udc->gadget.ep0 = &udc->ep[0].ep;
@@ -3397,7 +3396,7 @@ static int lpc32xx_udc_resume(struct platform_device *pdev)
#endif
#ifdef CONFIG_OF
-static struct of_device_id lpc32xx_udc_of_match[] = {
+static const struct of_device_id lpc32xx_udc_of_match[] = {
{ .compatible = "nxp,lpc3220-udc", },
{ },
};
diff --git a/drivers/usb/gadget/m66592-udc.c b/drivers/usb/gadget/udc/m66592-udc.c
index 0d17174b86f8..de88d33b44b2 100644
--- a/drivers/usb/gadget/m66592-udc.c
+++ b/drivers/usb/gadget/udc/m66592-udc.c
@@ -1492,8 +1492,6 @@ static int m66592_udc_stop(struct usb_gadget *g,
m66592_bclr(m66592, M66592_VBSE | M66592_URST, M66592_INTENB0);
- driver->unbind(&m66592->gadget);
-
init_controller(m66592);
disable_controller(m66592);
@@ -1553,7 +1551,7 @@ static void nop_completion(struct usb_ep *ep, struct usb_request *r)
{
}
-static int __init m66592_probe(struct platform_device *pdev)
+static int m66592_probe(struct platform_device *pdev)
{
struct resource *res, *ires;
void __iomem *reg = NULL;
diff --git a/drivers/usb/gadget/m66592-udc.h b/drivers/usb/gadget/udc/m66592-udc.h
index 96d49d7bfb6b..96d49d7bfb6b 100644
--- a/drivers/usb/gadget/m66592-udc.h
+++ b/drivers/usb/gadget/udc/m66592-udc.h
diff --git a/drivers/usb/gadget/mv_u3d.h b/drivers/usb/gadget/udc/mv_u3d.h
index e32a787ac373..e32a787ac373 100644
--- a/drivers/usb/gadget/mv_u3d.h
+++ b/drivers/usb/gadget/udc/mv_u3d.h
diff --git a/drivers/usb/gadget/mv_u3d_core.c b/drivers/usb/gadget/udc/mv_u3d_core.c
index 16248711c152..16248711c152 100644
--- a/drivers/usb/gadget/mv_u3d_core.c
+++ b/drivers/usb/gadget/udc/mv_u3d_core.c
diff --git a/drivers/usb/gadget/mv_udc.h b/drivers/usb/gadget/udc/mv_udc.h
index be77f207dbaf..be77f207dbaf 100644
--- a/drivers/usb/gadget/mv_udc.h
+++ b/drivers/usb/gadget/udc/mv_udc.h
diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/udc/mv_udc_core.c
index fcff3a571b45..040fb169b162 100644
--- a/drivers/usb/gadget/mv_udc_core.c
+++ b/drivers/usb/gadget/udc/mv_udc_core.c
@@ -332,7 +332,7 @@ static int queue_dtd(struct mv_ep *ep, struct mv_req *req)
/* clear active and halt bit, in case set from a previous error */
dqh->size_ioc_int_sts &= ~(DTD_STATUS_ACTIVE | DTD_STATUS_HALTED);
- /* Ensure that updates to the QH will occure before priming. */
+ /* Ensure that updates to the QH will occur before priming. */
wmb();
/* Prime the Endpoint */
@@ -1656,7 +1656,7 @@ static void handle_setup_packet(struct mv_udc *udc, u8 ep_num,
dev_dbg(&udc->dev->dev, "SETUP %02x.%02x v%04x i%04x l%04x\n",
setup->bRequestType, setup->bRequest,
setup->wValue, setup->wIndex, setup->wLength);
- /* We process some stardard setup requests here */
+ /* We process some standard setup requests here */
if ((setup->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
switch (setup->bRequest) {
case USB_REQ_GET_STATUS:
diff --git a/drivers/usb/gadget/net2272.c b/drivers/usb/gadget/udc/net2272.c
index ca15405583e2..059cfe527982 100644
--- a/drivers/usb/gadget/net2272.c
+++ b/drivers/usb/gadget/udc/net2272.c
@@ -1453,7 +1453,7 @@ static int net2272_start(struct usb_gadget *_gadget,
struct net2272 *dev;
unsigned i;
- if (!driver || !driver->unbind || !driver->setup ||
+ if (!driver || !driver->setup ||
driver->max_speed != USB_SPEED_HIGH)
return -EINVAL;
diff --git a/drivers/usb/gadget/net2272.h b/drivers/usb/gadget/udc/net2272.h
index e59505789359..e59505789359 100644
--- a/drivers/usb/gadget/net2272.h
+++ b/drivers/usb/gadget/udc/net2272.h
diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c
new file mode 100644
index 000000000000..f4eac113690e
--- /dev/null
+++ b/drivers/usb/gadget/udc/net2280.c
@@ -0,0 +1,3827 @@
+/*
+ * Driver for the PLX NET2280 USB device controller.
+ * Specs and errata are available from <http://www.plxtech.com>.
+ *
+ * PLX Technology Inc. (formerly NetChip Technology) supported the
+ * development of this driver.
+ *
+ *
+ * CODE STATUS HIGHLIGHTS
+ *
+ * This driver should work well with most "gadget" drivers, including
+ * the Mass Storage, Serial, and Ethernet/RNDIS gadget drivers
+ * as well as Gadget Zero and Gadgetfs.
+ *
+ * DMA is enabled by default. Drivers using transfer queues might use
+ * DMA chaining to remove IRQ latencies between transfers. (Except when
+ * short OUT transfers happen.) Drivers can use the req->no_interrupt
+ * hint to completely eliminate some IRQs, if a later IRQ is guaranteed
+ * and DMA chaining is enabled.
+ *
+ * MSI is enabled by default. The legacy IRQ is used if MSI couldn't
+ * be enabled.
+ *
+ * Note that almost all the errata workarounds here are only needed for
+ * rev1 chips. Rev1a silicon (0110) fixes almost all of them.
+ */
+
+/*
+ * Copyright (C) 2003 David Brownell
+ * Copyright (C) 2003-2005 PLX Technology, Inc.
+ * Copyright (C) 2014 Ricardo Ribalda - Qtechnology/AS
+ *
+ * Modified Seth Levy 2005 PLX Technology, Inc. to provide compatibility
+ * with 2282 chip
+ *
+ * Modified Ricardo Ribalda Qtechnology AS to provide compatibility
+ * with usb 338x chip. Based on PLX driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/prefetch.h>
+#include <linux/io.h>
+
+#include <asm/byteorder.h>
+#include <asm/irq.h>
+#include <asm/unaligned.h>
+
+#define DRIVER_DESC "PLX NET228x/USB338x USB Peripheral Controller"
+#define DRIVER_VERSION "2005 Sept 27/v3.0"
+
+#define EP_DONTUSE 13 /* nonzero */
+
+#define USE_RDK_LEDS /* GPIO pins control three LEDs */
+
+
+static const char driver_name[] = "net2280";
+static const char driver_desc[] = DRIVER_DESC;
+
+static const u32 ep_bit[9] = { 0, 17, 2, 19, 4, 1, 18, 3, 20 };
+static const char ep0name[] = "ep0";
+static const char *const ep_name[] = {
+ ep0name,
+ "ep-a", "ep-b", "ep-c", "ep-d",
+ "ep-e", "ep-f", "ep-g", "ep-h",
+};
+
+/* use_dma -- general goodness, fewer interrupts, less cpu load (vs PIO)
+ * use_dma_chaining -- dma descriptor queueing gives even more irq reduction
+ *
+ * The net2280 DMA engines are not tightly integrated with their FIFOs;
+ * not all cases are (yet) handled well in this driver or the silicon.
+ * Some gadget drivers work better with the dma support here than others.
+ * These two parameters let you use PIO or more aggressive DMA.
+ */
+static bool use_dma = true;
+static bool use_dma_chaining;
+static bool use_msi = true;
+
+/* "modprobe net2280 use_dma=n" etc */
+module_param(use_dma, bool, 0444);
+module_param(use_dma_chaining, bool, 0444);
+module_param(use_msi, bool, 0444);
+
+/* mode 0 == ep-{a,b,c,d} 1K fifo each
+ * mode 1 == ep-{a,b} 2K fifo each, ep-{c,d} unavailable
+ * mode 2 == ep-a 2K fifo, ep-{b,c} 1K each, ep-d unavailable
+ */
+static ushort fifo_mode;
+
+/* "modprobe net2280 fifo_mode=1" etc */
+module_param(fifo_mode, ushort, 0644);
+
+/* enable_suspend -- When enabled, the driver will respond to
+ * USB suspend requests by powering down the NET2280. Otherwise,
+ * USB suspend requests will be ignored. This is acceptable for
+ * self-powered devices
+ */
+static bool enable_suspend;
+
+/* "modprobe net2280 enable_suspend=1" etc */
+module_param(enable_suspend, bool, 0444);
+
+/* force full-speed operation */
+static bool full_speed;
+module_param(full_speed, bool, 0444);
+MODULE_PARM_DESC(full_speed, "force full-speed mode -- for testing only!");
+
+#define DIR_STRING(bAddress) (((bAddress) & USB_DIR_IN) ? "in" : "out")
+
+static char *type_string(u8 bmAttributes)
+{
+ switch ((bmAttributes) & USB_ENDPOINT_XFERTYPE_MASK) {
+ case USB_ENDPOINT_XFER_BULK: return "bulk";
+ case USB_ENDPOINT_XFER_ISOC: return "iso";
+ case USB_ENDPOINT_XFER_INT: return "intr";
+ }
+ return "control";
+}
+
+#include "net2280.h"
+
+#define valid_bit cpu_to_le32(BIT(VALID_BIT))
+#define dma_done_ie cpu_to_le32(BIT(DMA_DONE_INTERRUPT_ENABLE))
+
+/*-------------------------------------------------------------------------*/
+static inline void enable_pciirqenb(struct net2280_ep *ep)
+{
+ u32 tmp = readl(&ep->dev->regs->pciirqenb0);
+
+ if (ep->dev->quirks & PLX_LEGACY)
+ tmp |= BIT(ep->num);
+ else
+ tmp |= BIT(ep_bit[ep->num]);
+ writel(tmp, &ep->dev->regs->pciirqenb0);
+
+ return;
+}
+
+static int
+net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
+{
+ struct net2280 *dev;
+ struct net2280_ep *ep;
+ u32 max, tmp;
+ unsigned long flags;
+ static const u32 ep_key[9] = { 1, 0, 1, 0, 1, 1, 0, 1, 0 };
+
+ ep = container_of(_ep, struct net2280_ep, ep);
+ if (!_ep || !desc || ep->desc || _ep->name == ep0name ||
+ desc->bDescriptorType != USB_DT_ENDPOINT)
+ return -EINVAL;
+ dev = ep->dev;
+ if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
+ return -ESHUTDOWN;
+
+ /* erratum 0119 workaround ties up an endpoint number */
+ if ((desc->bEndpointAddress & 0x0f) == EP_DONTUSE)
+ return -EDOM;
+
+ if (dev->quirks & PLX_SUPERSPEED) {
+ if ((desc->bEndpointAddress & 0x0f) >= 0x0c)
+ return -EDOM;
+ ep->is_in = !!usb_endpoint_dir_in(desc);
+ if (dev->enhanced_mode && ep->is_in && ep_key[ep->num])
+ return -EINVAL;
+ }
+
+ /* sanity check ep-e/ep-f since their fifos are small */
+ max = usb_endpoint_maxp(desc) & 0x1fff;
+ if (ep->num > 4 && max > 64 && (dev->quirks & PLX_LEGACY))
+ return -ERANGE;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ _ep->maxpacket = max & 0x7ff;
+ ep->desc = desc;
+
+ /* ep_reset() has already been called */
+ ep->stopped = 0;
+ ep->wedged = 0;
+ ep->out_overflow = 0;
+
+ /* set speed-dependent max packet; may kick in high bandwidth */
+ set_max_speed(ep, max);
+
+ /* FIFO lines can't go to different packets. PIO is ok, so
+ * use it instead of troublesome (non-bulk) multi-packet DMA.
+ */
+ if (ep->dma && (max % 4) != 0 && use_dma_chaining) {
+ ep_dbg(ep->dev, "%s, no dma for maxpacket %d\n",
+ ep->ep.name, ep->ep.maxpacket);
+ ep->dma = NULL;
+ }
+
+ /* set type, direction, address; reset fifo counters */
+ writel(BIT(FIFO_FLUSH), &ep->regs->ep_stat);
+ tmp = (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK);
+ if (tmp == USB_ENDPOINT_XFER_INT) {
+ /* erratum 0105 workaround prevents hs NYET */
+ if (dev->chiprev == 0100 &&
+ dev->gadget.speed == USB_SPEED_HIGH &&
+ !(desc->bEndpointAddress & USB_DIR_IN))
+ writel(BIT(CLEAR_NAK_OUT_PACKETS_MODE),
+ &ep->regs->ep_rsp);
+ } else if (tmp == USB_ENDPOINT_XFER_BULK) {
+ /* catch some particularly blatant driver bugs */
+ if ((dev->gadget.speed == USB_SPEED_SUPER && max != 1024) ||
+ (dev->gadget.speed == USB_SPEED_HIGH && max != 512) ||
+ (dev->gadget.speed == USB_SPEED_FULL && max > 64)) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return -ERANGE;
+ }
+ }
+ ep->is_iso = (tmp == USB_ENDPOINT_XFER_ISOC);
+ /* Enable this endpoint */
+ if (dev->quirks & PLX_LEGACY) {
+ tmp <<= ENDPOINT_TYPE;
+ tmp |= desc->bEndpointAddress;
+ /* default full fifo lines */
+ tmp |= (4 << ENDPOINT_BYTE_COUNT);
+ tmp |= BIT(ENDPOINT_ENABLE);
+ ep->is_in = (tmp & USB_DIR_IN) != 0;
+ } else {
+ /* In Legacy mode, only OUT endpoints are used */
+ if (dev->enhanced_mode && ep->is_in) {
+ tmp <<= IN_ENDPOINT_TYPE;
+ tmp |= BIT(IN_ENDPOINT_ENABLE);
+ /* Not applicable to Legacy */
+ tmp |= BIT(ENDPOINT_DIRECTION);
+ } else {
+ tmp <<= OUT_ENDPOINT_TYPE;
+ tmp |= BIT(OUT_ENDPOINT_ENABLE);
+ tmp |= (ep->is_in << ENDPOINT_DIRECTION);
+ }
+
+ tmp |= usb_endpoint_num(desc);
+ tmp |= (ep->ep.maxburst << MAX_BURST_SIZE);
+ }
+
+ /* Make sure all the registers are written before ep_rsp*/
+ wmb();
+
+ /* for OUT transfers, block the rx fifo until a read is posted */
+ if (!ep->is_in)
+ writel(BIT(SET_NAK_OUT_PACKETS), &ep->regs->ep_rsp);
+ else if (!(dev->quirks & PLX_2280)) {
+ /* Added for 2282, Don't use nak packets on an in endpoint,
+ * this was ignored on 2280
+ */
+ writel(BIT(CLEAR_NAK_OUT_PACKETS) |
+ BIT(CLEAR_NAK_OUT_PACKETS_MODE), &ep->regs->ep_rsp);
+ }
+
+ writel(tmp, &ep->cfg->ep_cfg);
+
+ /* enable irqs */
+ if (!ep->dma) { /* pio, per-packet */
+ enable_pciirqenb(ep);
+
+ tmp = BIT(DATA_PACKET_RECEIVED_INTERRUPT_ENABLE) |
+ BIT(DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE);
+ if (dev->quirks & PLX_2280)
+ tmp |= readl(&ep->regs->ep_irqenb);
+ writel(tmp, &ep->regs->ep_irqenb);
+ } else { /* dma, per-request */
+ tmp = BIT((8 + ep->num)); /* completion */
+ tmp |= readl(&dev->regs->pciirqenb1);
+ writel(tmp, &dev->regs->pciirqenb1);
+
+ /* for short OUT transfers, dma completions can't
+ * advance the queue; do it pio-style, by hand.
+ * NOTE erratum 0112 workaround #2
+ */
+ if ((desc->bEndpointAddress & USB_DIR_IN) == 0) {
+ tmp = BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT_ENABLE);
+ writel(tmp, &ep->regs->ep_irqenb);
+
+ enable_pciirqenb(ep);
+ }
+ }
+
+ tmp = desc->bEndpointAddress;
+ ep_dbg(dev, "enabled %s (ep%d%s-%s) %s max %04x\n",
+ _ep->name, tmp & 0x0f, DIR_STRING(tmp),
+ type_string(desc->bmAttributes),
+ ep->dma ? "dma" : "pio", max);
+
+ /* pci writes may still be posted */
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return 0;
+}
+
+static int handshake(u32 __iomem *ptr, u32 mask, u32 done, int usec)
+{
+ u32 result;
+
+ do {
+ result = readl(ptr);
+ if (result == ~(u32)0) /* "device unplugged" */
+ return -ENODEV;
+ result &= mask;
+ if (result == done)
+ return 0;
+ udelay(1);
+ usec--;
+ } while (usec > 0);
+ return -ETIMEDOUT;
+}
+
+static const struct usb_ep_ops net2280_ep_ops;
+
+static void ep_reset_228x(struct net2280_regs __iomem *regs,
+ struct net2280_ep *ep)
+{
+ u32 tmp;
+
+ ep->desc = NULL;
+ INIT_LIST_HEAD(&ep->queue);
+
+ usb_ep_set_maxpacket_limit(&ep->ep, ~0);
+ ep->ep.ops = &net2280_ep_ops;
+
+ /* disable the dma, irqs, endpoint... */
+ if (ep->dma) {
+ writel(0, &ep->dma->dmactl);
+ writel(BIT(DMA_SCATTER_GATHER_DONE_INTERRUPT) |
+ BIT(DMA_TRANSACTION_DONE_INTERRUPT) |
+ BIT(DMA_ABORT),
+ &ep->dma->dmastat);
+
+ tmp = readl(&regs->pciirqenb0);
+ tmp &= ~BIT(ep->num);
+ writel(tmp, &regs->pciirqenb0);
+ } else {
+ tmp = readl(&regs->pciirqenb1);
+ tmp &= ~BIT((8 + ep->num)); /* completion */
+ writel(tmp, &regs->pciirqenb1);
+ }
+ writel(0, &ep->regs->ep_irqenb);
+
+ /* init to our chosen defaults, notably so that we NAK OUT
+ * packets until the driver queues a read (+note erratum 0112)
+ */
+ if (!ep->is_in || (ep->dev->quirks & PLX_2280)) {
+ tmp = BIT(SET_NAK_OUT_PACKETS_MODE) |
+ BIT(SET_NAK_OUT_PACKETS) |
+ BIT(CLEAR_EP_HIDE_STATUS_PHASE) |
+ BIT(CLEAR_INTERRUPT_MODE);
+ } else {
+ /* added for 2282 */
+ tmp = BIT(CLEAR_NAK_OUT_PACKETS_MODE) |
+ BIT(CLEAR_NAK_OUT_PACKETS) |
+ BIT(CLEAR_EP_HIDE_STATUS_PHASE) |
+ BIT(CLEAR_INTERRUPT_MODE);
+ }
+
+ if (ep->num != 0) {
+ tmp |= BIT(CLEAR_ENDPOINT_TOGGLE) |
+ BIT(CLEAR_ENDPOINT_HALT);
+ }
+ writel(tmp, &ep->regs->ep_rsp);
+
+ /* scrub most status bits, and flush any fifo state */
+ if (ep->dev->quirks & PLX_2280)
+ tmp = BIT(FIFO_OVERFLOW) |
+ BIT(FIFO_UNDERFLOW);
+ else
+ tmp = 0;
+
+ writel(tmp | BIT(TIMEOUT) |
+ BIT(USB_STALL_SENT) |
+ BIT(USB_IN_NAK_SENT) |
+ BIT(USB_IN_ACK_RCVD) |
+ BIT(USB_OUT_PING_NAK_SENT) |
+ BIT(USB_OUT_ACK_SENT) |
+ BIT(FIFO_FLUSH) |
+ BIT(SHORT_PACKET_OUT_DONE_INTERRUPT) |
+ BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT) |
+ BIT(DATA_PACKET_RECEIVED_INTERRUPT) |
+ BIT(DATA_PACKET_TRANSMITTED_INTERRUPT) |
+ BIT(DATA_OUT_PING_TOKEN_INTERRUPT) |
+ BIT(DATA_IN_TOKEN_INTERRUPT),
+ &ep->regs->ep_stat);
+
+ /* fifo size is handled separately */
+}
+
+static void ep_reset_338x(struct net2280_regs __iomem *regs,
+ struct net2280_ep *ep)
+{
+ u32 tmp, dmastat;
+
+ ep->desc = NULL;
+ INIT_LIST_HEAD(&ep->queue);
+
+ usb_ep_set_maxpacket_limit(&ep->ep, ~0);
+ ep->ep.ops = &net2280_ep_ops;
+
+ /* disable the dma, irqs, endpoint... */
+ if (ep->dma) {
+ writel(0, &ep->dma->dmactl);
+ writel(BIT(DMA_ABORT_DONE_INTERRUPT) |
+ BIT(DMA_PAUSE_DONE_INTERRUPT) |
+ BIT(DMA_SCATTER_GATHER_DONE_INTERRUPT) |
+ BIT(DMA_TRANSACTION_DONE_INTERRUPT),
+ /* | BIT(DMA_ABORT), */
+ &ep->dma->dmastat);
+
+ dmastat = readl(&ep->dma->dmastat);
+ if (dmastat == 0x5002) {
+ ep_warn(ep->dev, "The dmastat return = %x!!\n",
+ dmastat);
+ writel(0x5a, &ep->dma->dmastat);
+ }
+
+ tmp = readl(&regs->pciirqenb0);
+ tmp &= ~BIT(ep_bit[ep->num]);
+ writel(tmp, &regs->pciirqenb0);
+ } else {
+ if (ep->num < 5) {
+ tmp = readl(&regs->pciirqenb1);
+ tmp &= ~BIT((8 + ep->num)); /* completion */
+ writel(tmp, &regs->pciirqenb1);
+ }
+ }
+ writel(0, &ep->regs->ep_irqenb);
+
+ writel(BIT(SHORT_PACKET_OUT_DONE_INTERRUPT) |
+ BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT) |
+ BIT(FIFO_OVERFLOW) |
+ BIT(DATA_PACKET_RECEIVED_INTERRUPT) |
+ BIT(DATA_PACKET_TRANSMITTED_INTERRUPT) |
+ BIT(DATA_OUT_PING_TOKEN_INTERRUPT) |
+ BIT(DATA_IN_TOKEN_INTERRUPT), &ep->regs->ep_stat);
+}
+
+static void nuke(struct net2280_ep *);
+
+static int net2280_disable(struct usb_ep *_ep)
+{
+ struct net2280_ep *ep;
+ unsigned long flags;
+
+ ep = container_of(_ep, struct net2280_ep, ep);
+ if (!_ep || !ep->desc || _ep->name == ep0name)
+ return -EINVAL;
+
+ spin_lock_irqsave(&ep->dev->lock, flags);
+ nuke(ep);
+
+ if (ep->dev->quirks & PLX_SUPERSPEED)
+ ep_reset_338x(ep->dev->regs, ep);
+ else
+ ep_reset_228x(ep->dev->regs, ep);
+
+ ep_vdbg(ep->dev, "disabled %s %s\n",
+ ep->dma ? "dma" : "pio", _ep->name);
+
+ /* synch memory views with the device */
+ (void)readl(&ep->cfg->ep_cfg);
+
+ if (use_dma && !ep->dma && ep->num >= 1 && ep->num <= 4)
+ ep->dma = &ep->dev->dma[ep->num - 1];
+
+ spin_unlock_irqrestore(&ep->dev->lock, flags);
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_request
+*net2280_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
+{
+ struct net2280_ep *ep;
+ struct net2280_request *req;
+
+ if (!_ep)
+ return NULL;
+ ep = container_of(_ep, struct net2280_ep, ep);
+
+ req = kzalloc(sizeof(*req), gfp_flags);
+ if (!req)
+ return NULL;
+
+ INIT_LIST_HEAD(&req->queue);
+
+ /* this dma descriptor may be swapped with the previous dummy */
+ if (ep->dma) {
+ struct net2280_dma *td;
+
+ td = pci_pool_alloc(ep->dev->requests, gfp_flags,
+ &req->td_dma);
+ if (!td) {
+ kfree(req);
+ return NULL;
+ }
+ td->dmacount = 0; /* not VALID */
+ td->dmadesc = td->dmaaddr;
+ req->td = td;
+ }
+ return &req->req;
+}
+
+static void net2280_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct net2280_ep *ep;
+ struct net2280_request *req;
+
+ ep = container_of(_ep, struct net2280_ep, ep);
+ if (!_ep || !_req)
+ return;
+
+ req = container_of(_req, struct net2280_request, req);
+ WARN_ON(!list_empty(&req->queue));
+ if (req->td)
+ pci_pool_free(ep->dev->requests, req->td, req->td_dma);
+ kfree(req);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* load a packet into the fifo we use for usb IN transfers.
+ * works for all endpoints.
+ *
+ * NOTE: pio with ep-a..ep-d could stuff multiple packets into the fifo
+ * at a time, but this code is simpler because it knows it only writes
+ * one packet. ep-a..ep-d should use dma instead.
+ */
+static void write_fifo(struct net2280_ep *ep, struct usb_request *req)
+{
+ struct net2280_ep_regs __iomem *regs = ep->regs;
+ u8 *buf;
+ u32 tmp;
+ unsigned count, total;
+
+ /* INVARIANT: fifo is currently empty. (testable) */
+
+ if (req) {
+ buf = req->buf + req->actual;
+ prefetch(buf);
+ total = req->length - req->actual;
+ } else {
+ total = 0;
+ buf = NULL;
+ }
+
+ /* write just one packet at a time */
+ count = ep->ep.maxpacket;
+ if (count > total) /* min() cannot be used on a bitfield */
+ count = total;
+
+ ep_vdbg(ep->dev, "write %s fifo (IN) %d bytes%s req %p\n",
+ ep->ep.name, count,
+ (count != ep->ep.maxpacket) ? " (short)" : "",
+ req);
+ while (count >= 4) {
+ /* NOTE be careful if you try to align these. fifo lines
+ * should normally be full (4 bytes) and successive partial
+ * lines are ok only in certain cases.
+ */
+ tmp = get_unaligned((u32 *)buf);
+ cpu_to_le32s(&tmp);
+ writel(tmp, &regs->ep_data);
+ buf += 4;
+ count -= 4;
+ }
+
+ /* last fifo entry is "short" unless we wrote a full packet.
+ * also explicitly validate last word in (periodic) transfers
+ * when maxpacket is not a multiple of 4 bytes.
+ */
+ if (count || total < ep->ep.maxpacket) {
+ tmp = count ? get_unaligned((u32 *)buf) : count;
+ cpu_to_le32s(&tmp);
+ set_fifo_bytecount(ep, count & 0x03);
+ writel(tmp, &regs->ep_data);
+ }
+
+ /* pci writes may still be posted */
+}
+
+/* work around erratum 0106: PCI and USB race over the OUT fifo.
+ * caller guarantees chiprev 0100, out endpoint is NAKing, and
+ * there's no real data in the fifo.
+ *
+ * NOTE: also used in cases where that erratum doesn't apply:
+ * where the host wrote "too much" data to us.
+ */
+static void out_flush(struct net2280_ep *ep)
+{
+ u32 __iomem *statp;
+ u32 tmp;
+
+ ASSERT_OUT_NAKING(ep);
+
+ statp = &ep->regs->ep_stat;
+ writel(BIT(DATA_OUT_PING_TOKEN_INTERRUPT) |
+ BIT(DATA_PACKET_RECEIVED_INTERRUPT),
+ statp);
+ writel(BIT(FIFO_FLUSH), statp);
+ /* Make sure that stap is written */
+ mb();
+ tmp = readl(statp);
+ if (tmp & BIT(DATA_OUT_PING_TOKEN_INTERRUPT) &&
+ /* high speed did bulk NYET; fifo isn't filling */
+ ep->dev->gadget.speed == USB_SPEED_FULL) {
+ unsigned usec;
+
+ usec = 50; /* 64 byte bulk/interrupt */
+ handshake(statp, BIT(USB_OUT_PING_NAK_SENT),
+ BIT(USB_OUT_PING_NAK_SENT), usec);
+ /* NAK done; now CLEAR_NAK_OUT_PACKETS is safe */
+ }
+}
+
+/* unload packet(s) from the fifo we use for usb OUT transfers.
+ * returns true iff the request completed, because of short packet
+ * or the request buffer having filled with full packets.
+ *
+ * for ep-a..ep-d this will read multiple packets out when they
+ * have been accepted.
+ */
+static int read_fifo(struct net2280_ep *ep, struct net2280_request *req)
+{
+ struct net2280_ep_regs __iomem *regs = ep->regs;
+ u8 *buf = req->req.buf + req->req.actual;
+ unsigned count, tmp, is_short;
+ unsigned cleanup = 0, prevent = 0;
+
+ /* erratum 0106 ... packets coming in during fifo reads might
+ * be incompletely rejected. not all cases have workarounds.
+ */
+ if (ep->dev->chiprev == 0x0100 &&
+ ep->dev->gadget.speed == USB_SPEED_FULL) {
+ udelay(1);
+ tmp = readl(&ep->regs->ep_stat);
+ if ((tmp & BIT(NAK_OUT_PACKETS)))
+ cleanup = 1;
+ else if ((tmp & BIT(FIFO_FULL))) {
+ start_out_naking(ep);
+ prevent = 1;
+ }
+ /* else: hope we don't see the problem */
+ }
+
+ /* never overflow the rx buffer. the fifo reads packets until
+ * it sees a short one; we might not be ready for them all.
+ */
+ prefetchw(buf);
+ count = readl(&regs->ep_avail);
+ if (unlikely(count == 0)) {
+ udelay(1);
+ tmp = readl(&ep->regs->ep_stat);
+ count = readl(&regs->ep_avail);
+ /* handled that data already? */
+ if (count == 0 && (tmp & BIT(NAK_OUT_PACKETS)) == 0)
+ return 0;
+ }
+
+ tmp = req->req.length - req->req.actual;
+ if (count > tmp) {
+ /* as with DMA, data overflow gets flushed */
+ if ((tmp % ep->ep.maxpacket) != 0) {
+ ep_err(ep->dev,
+ "%s out fifo %d bytes, expected %d\n",
+ ep->ep.name, count, tmp);
+ req->req.status = -EOVERFLOW;
+ cleanup = 1;
+ /* NAK_OUT_PACKETS will be set, so flushing is safe;
+ * the next read will start with the next packet
+ */
+ } /* else it's a ZLP, no worries */
+ count = tmp;
+ }
+ req->req.actual += count;
+
+ is_short = (count == 0) || ((count % ep->ep.maxpacket) != 0);
+
+ ep_vdbg(ep->dev, "read %s fifo (OUT) %d bytes%s%s%s req %p %d/%d\n",
+ ep->ep.name, count, is_short ? " (short)" : "",
+ cleanup ? " flush" : "", prevent ? " nak" : "",
+ req, req->req.actual, req->req.length);
+
+ while (count >= 4) {
+ tmp = readl(&regs->ep_data);
+ cpu_to_le32s(&tmp);
+ put_unaligned(tmp, (u32 *)buf);
+ buf += 4;
+ count -= 4;
+ }
+ if (count) {
+ tmp = readl(&regs->ep_data);
+ /* LE conversion is implicit here: */
+ do {
+ *buf++ = (u8) tmp;
+ tmp >>= 8;
+ } while (--count);
+ }
+ if (cleanup)
+ out_flush(ep);
+ if (prevent) {
+ writel(BIT(CLEAR_NAK_OUT_PACKETS), &ep->regs->ep_rsp);
+ (void) readl(&ep->regs->ep_rsp);
+ }
+
+ return is_short || ((req->req.actual == req->req.length) &&
+ !req->req.zero);
+}
+
+/* fill out dma descriptor to match a given request */
+static void fill_dma_desc(struct net2280_ep *ep,
+ struct net2280_request *req, int valid)
+{
+ struct net2280_dma *td = req->td;
+ u32 dmacount = req->req.length;
+
+ /* don't let DMA continue after a short OUT packet,
+ * so overruns can't affect the next transfer.
+ * in case of overruns on max-size packets, we can't
+ * stop the fifo from filling but we can flush it.
+ */
+ if (ep->is_in)
+ dmacount |= BIT(DMA_DIRECTION);
+ if ((!ep->is_in && (dmacount % ep->ep.maxpacket) != 0) ||
+ !(ep->dev->quirks & PLX_2280))
+ dmacount |= BIT(END_OF_CHAIN);
+
+ req->valid = valid;
+ if (valid)
+ dmacount |= BIT(VALID_BIT);
+ if (likely(!req->req.no_interrupt || !use_dma_chaining))
+ dmacount |= BIT(DMA_DONE_INTERRUPT_ENABLE);
+
+ /* td->dmadesc = previously set by caller */
+ td->dmaaddr = cpu_to_le32 (req->req.dma);
+
+ /* 2280 may be polling VALID_BIT through ep->dma->dmadesc */
+ wmb();
+ td->dmacount = cpu_to_le32(dmacount);
+}
+
+static const u32 dmactl_default =
+ BIT(DMA_SCATTER_GATHER_DONE_INTERRUPT) |
+ BIT(DMA_CLEAR_COUNT_ENABLE) |
+ /* erratum 0116 workaround part 1 (use POLLING) */
+ (POLL_100_USEC << DESCRIPTOR_POLLING_RATE) |
+ BIT(DMA_VALID_BIT_POLLING_ENABLE) |
+ BIT(DMA_VALID_BIT_ENABLE) |
+ BIT(DMA_SCATTER_GATHER_ENABLE) |
+ /* erratum 0116 workaround part 2 (no AUTOSTART) */
+ BIT(DMA_ENABLE);
+
+static inline void spin_stop_dma(struct net2280_dma_regs __iomem *dma)
+{
+ handshake(&dma->dmactl, BIT(DMA_ENABLE), 0, 50);
+}
+
+static inline void stop_dma(struct net2280_dma_regs __iomem *dma)
+{
+ writel(readl(&dma->dmactl) & ~BIT(DMA_ENABLE), &dma->dmactl);
+ spin_stop_dma(dma);
+}
+
+static void start_queue(struct net2280_ep *ep, u32 dmactl, u32 td_dma)
+{
+ struct net2280_dma_regs __iomem *dma = ep->dma;
+ unsigned int tmp = BIT(VALID_BIT) | (ep->is_in << DMA_DIRECTION);
+
+ if (!(ep->dev->quirks & PLX_2280))
+ tmp |= BIT(END_OF_CHAIN);
+
+ writel(tmp, &dma->dmacount);
+ writel(readl(&dma->dmastat), &dma->dmastat);
+
+ writel(td_dma, &dma->dmadesc);
+ if (ep->dev->quirks & PLX_SUPERSPEED)
+ dmactl |= BIT(DMA_REQUEST_OUTSTANDING);
+ writel(dmactl, &dma->dmactl);
+
+ /* erratum 0116 workaround part 3: pci arbiter away from net2280 */
+ (void) readl(&ep->dev->pci->pcimstctl);
+
+ writel(BIT(DMA_START), &dma->dmastat);
+
+ if (!ep->is_in)
+ stop_out_naking(ep);
+}
+
+static void start_dma(struct net2280_ep *ep, struct net2280_request *req)
+{
+ u32 tmp;
+ struct net2280_dma_regs __iomem *dma = ep->dma;
+
+ /* FIXME can't use DMA for ZLPs */
+
+ /* on this path we "know" there's no dma active (yet) */
+ WARN_ON(readl(&dma->dmactl) & BIT(DMA_ENABLE));
+ writel(0, &ep->dma->dmactl);
+
+ /* previous OUT packet might have been short */
+ if (!ep->is_in && (readl(&ep->regs->ep_stat) &
+ BIT(NAK_OUT_PACKETS))) {
+ writel(BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT),
+ &ep->regs->ep_stat);
+
+ tmp = readl(&ep->regs->ep_avail);
+ if (tmp) {
+ writel(readl(&dma->dmastat), &dma->dmastat);
+
+ /* transfer all/some fifo data */
+ writel(req->req.dma, &dma->dmaaddr);
+ tmp = min(tmp, req->req.length);
+
+ /* dma irq, faking scatterlist status */
+ req->td->dmacount = cpu_to_le32(req->req.length - tmp);
+ writel(BIT(DMA_DONE_INTERRUPT_ENABLE) | tmp,
+ &dma->dmacount);
+ req->td->dmadesc = 0;
+ req->valid = 1;
+
+ writel(BIT(DMA_ENABLE), &dma->dmactl);
+ writel(BIT(DMA_START), &dma->dmastat);
+ return;
+ }
+ }
+
+ tmp = dmactl_default;
+
+ /* force packet boundaries between dma requests, but prevent the
+ * controller from automagically writing a last "short" packet
+ * (zero length) unless the driver explicitly said to do that.
+ */
+ if (ep->is_in) {
+ if (likely((req->req.length % ep->ep.maxpacket) ||
+ req->req.zero)){
+ tmp |= BIT(DMA_FIFO_VALIDATE);
+ ep->in_fifo_validate = 1;
+ } else
+ ep->in_fifo_validate = 0;
+ }
+
+ /* init req->td, pointing to the current dummy */
+ req->td->dmadesc = cpu_to_le32 (ep->td_dma);
+ fill_dma_desc(ep, req, 1);
+
+ if (!use_dma_chaining)
+ req->td->dmacount |= cpu_to_le32(BIT(END_OF_CHAIN));
+
+ start_queue(ep, tmp, req->td_dma);
+}
+
+static inline void resume_dma(struct net2280_ep *ep)
+{
+ writel(readl(&ep->dma->dmactl) | BIT(DMA_ENABLE), &ep->dma->dmactl);
+
+ ep->dma_started = true;
+}
+
+static inline void ep_stop_dma(struct net2280_ep *ep)
+{
+ writel(readl(&ep->dma->dmactl) & ~BIT(DMA_ENABLE), &ep->dma->dmactl);
+ spin_stop_dma(ep->dma);
+
+ ep->dma_started = false;
+}
+
+static inline void
+queue_dma(struct net2280_ep *ep, struct net2280_request *req, int valid)
+{
+ struct net2280_dma *end;
+ dma_addr_t tmp;
+
+ /* swap new dummy for old, link; fill and maybe activate */
+ end = ep->dummy;
+ ep->dummy = req->td;
+ req->td = end;
+
+ tmp = ep->td_dma;
+ ep->td_dma = req->td_dma;
+ req->td_dma = tmp;
+
+ end->dmadesc = cpu_to_le32 (ep->td_dma);
+
+ fill_dma_desc(ep, req, valid);
+}
+
+static void
+done(struct net2280_ep *ep, struct net2280_request *req, int status)
+{
+ struct net2280 *dev;
+ unsigned stopped = ep->stopped;
+
+ list_del_init(&req->queue);
+
+ if (req->req.status == -EINPROGRESS)
+ req->req.status = status;
+ else
+ status = req->req.status;
+
+ dev = ep->dev;
+ if (ep->dma)
+ usb_gadget_unmap_request(&dev->gadget, &req->req, ep->is_in);
+
+ if (status && status != -ESHUTDOWN)
+ ep_vdbg(dev, "complete %s req %p stat %d len %u/%u\n",
+ ep->ep.name, &req->req, status,
+ req->req.actual, req->req.length);
+
+ /* don't modify queue heads during completion callback */
+ ep->stopped = 1;
+ spin_unlock(&dev->lock);
+ req->req.complete(&ep->ep, &req->req);
+ spin_lock(&dev->lock);
+ ep->stopped = stopped;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int
+net2280_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
+{
+ struct net2280_request *req;
+ struct net2280_ep *ep;
+ struct net2280 *dev;
+ unsigned long flags;
+
+ /* we always require a cpu-view buffer, so that we can
+ * always use pio (as fallback or whatever).
+ */
+ req = container_of(_req, struct net2280_request, req);
+ if (!_req || !_req->complete || !_req->buf ||
+ !list_empty(&req->queue))
+ return -EINVAL;
+ if (_req->length > (~0 & DMA_BYTE_COUNT_MASK))
+ return -EDOM;
+ ep = container_of(_ep, struct net2280_ep, ep);
+ if (!_ep || (!ep->desc && ep->num != 0))
+ return -EINVAL;
+ dev = ep->dev;
+ if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
+ return -ESHUTDOWN;
+
+ /* FIXME implement PIO fallback for ZLPs with DMA */
+ if (ep->dma && _req->length == 0)
+ return -EOPNOTSUPP;
+
+ /* set up dma mapping in case the caller didn't */
+ if (ep->dma) {
+ int ret;
+
+ ret = usb_gadget_map_request(&dev->gadget, _req,
+ ep->is_in);
+ if (ret)
+ return ret;
+ }
+
+#if 0
+ ep_vdbg(dev, "%s queue req %p, len %d buf %p\n",
+ _ep->name, _req, _req->length, _req->buf);
+#endif
+
+ spin_lock_irqsave(&dev->lock, flags);
+
+ _req->status = -EINPROGRESS;
+ _req->actual = 0;
+
+ /* kickstart this i/o queue? */
+ if (list_empty(&ep->queue) && !ep->stopped) {
+ /* DMA request while EP halted */
+ if (ep->dma &&
+ (readl(&ep->regs->ep_rsp) & BIT(CLEAR_ENDPOINT_HALT)) &&
+ (dev->quirks & PLX_SUPERSPEED)) {
+ int valid = 1;
+ if (ep->is_in) {
+ int expect;
+ expect = likely(req->req.zero ||
+ ((req->req.length %
+ ep->ep.maxpacket) != 0));
+ if (expect != ep->in_fifo_validate)
+ valid = 0;
+ }
+ queue_dma(ep, req, valid);
+ }
+ /* use DMA if the endpoint supports it, else pio */
+ else if (ep->dma)
+ start_dma(ep, req);
+ else {
+ /* maybe there's no control data, just status ack */
+ if (ep->num == 0 && _req->length == 0) {
+ allow_status(ep);
+ done(ep, req, 0);
+ ep_vdbg(dev, "%s status ack\n", ep->ep.name);
+ goto done;
+ }
+
+ /* PIO ... stuff the fifo, or unblock it. */
+ if (ep->is_in)
+ write_fifo(ep, _req);
+ else if (list_empty(&ep->queue)) {
+ u32 s;
+
+ /* OUT FIFO might have packet(s) buffered */
+ s = readl(&ep->regs->ep_stat);
+ if ((s & BIT(FIFO_EMPTY)) == 0) {
+ /* note: _req->short_not_ok is
+ * ignored here since PIO _always_
+ * stops queue advance here, and
+ * _req->status doesn't change for
+ * short reads (only _req->actual)
+ */
+ if (read_fifo(ep, req) &&
+ ep->num == 0) {
+ done(ep, req, 0);
+ allow_status(ep);
+ /* don't queue it */
+ req = NULL;
+ } else if (read_fifo(ep, req) &&
+ ep->num != 0) {
+ done(ep, req, 0);
+ req = NULL;
+ } else
+ s = readl(&ep->regs->ep_stat);
+ }
+
+ /* don't NAK, let the fifo fill */
+ if (req && (s & BIT(NAK_OUT_PACKETS)))
+ writel(BIT(CLEAR_NAK_OUT_PACKETS),
+ &ep->regs->ep_rsp);
+ }
+ }
+
+ } else if (ep->dma) {
+ int valid = 1;
+
+ if (ep->is_in) {
+ int expect;
+
+ /* preventing magic zlps is per-engine state, not
+ * per-transfer; irq logic must recover hiccups.
+ */
+ expect = likely(req->req.zero ||
+ (req->req.length % ep->ep.maxpacket));
+ if (expect != ep->in_fifo_validate)
+ valid = 0;
+ }
+ queue_dma(ep, req, valid);
+
+ } /* else the irq handler advances the queue. */
+
+ ep->responded = 1;
+ if (req)
+ list_add_tail(&req->queue, &ep->queue);
+done:
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ /* pci writes may still be posted */
+ return 0;
+}
+
+static inline void
+dma_done(struct net2280_ep *ep, struct net2280_request *req, u32 dmacount,
+ int status)
+{
+ req->req.actual = req->req.length - (DMA_BYTE_COUNT_MASK & dmacount);
+ done(ep, req, status);
+}
+
+static void restart_dma(struct net2280_ep *ep);
+
+static void scan_dma_completions(struct net2280_ep *ep)
+{
+ /* only look at descriptors that were "naturally" retired,
+ * so fifo and list head state won't matter
+ */
+ while (!list_empty(&ep->queue)) {
+ struct net2280_request *req;
+ u32 tmp;
+
+ req = list_entry(ep->queue.next,
+ struct net2280_request, queue);
+ if (!req->valid)
+ break;
+ rmb();
+ tmp = le32_to_cpup(&req->td->dmacount);
+ if ((tmp & BIT(VALID_BIT)) != 0)
+ break;
+
+ /* SHORT_PACKET_TRANSFERRED_INTERRUPT handles "usb-short"
+ * cases where DMA must be aborted; this code handles
+ * all non-abort DMA completions.
+ */
+ if (unlikely(req->td->dmadesc == 0)) {
+ /* paranoia */
+ tmp = readl(&ep->dma->dmacount);
+ if (tmp & DMA_BYTE_COUNT_MASK)
+ break;
+ /* single transfer mode */
+ dma_done(ep, req, tmp, 0);
+ break;
+ } else if (!ep->is_in &&
+ (req->req.length % ep->ep.maxpacket) != 0) {
+ tmp = readl(&ep->regs->ep_stat);
+ if (ep->dev->quirks & PLX_SUPERSPEED)
+ return dma_done(ep, req, tmp, 0);
+
+ /* AVOID TROUBLE HERE by not issuing short reads from
+ * your gadget driver. That helps avoids errata 0121,
+ * 0122, and 0124; not all cases trigger the warning.
+ */
+ if ((tmp & BIT(NAK_OUT_PACKETS)) == 0) {
+ ep_warn(ep->dev, "%s lost packet sync!\n",
+ ep->ep.name);
+ req->req.status = -EOVERFLOW;
+ } else {
+ tmp = readl(&ep->regs->ep_avail);
+ if (tmp) {
+ /* fifo gets flushed later */
+ ep->out_overflow = 1;
+ ep_dbg(ep->dev,
+ "%s dma, discard %d len %d\n",
+ ep->ep.name, tmp,
+ req->req.length);
+ req->req.status = -EOVERFLOW;
+ }
+ }
+ }
+ dma_done(ep, req, tmp, 0);
+ }
+}
+
+static void restart_dma(struct net2280_ep *ep)
+{
+ struct net2280_request *req;
+ u32 dmactl = dmactl_default;
+
+ if (ep->stopped)
+ return;
+ req = list_entry(ep->queue.next, struct net2280_request, queue);
+
+ if (!use_dma_chaining) {
+ start_dma(ep, req);
+ return;
+ }
+
+ /* the 2280 will be processing the queue unless queue hiccups after
+ * the previous transfer:
+ * IN: wanted automagic zlp, head doesn't (or vice versa)
+ * DMA_FIFO_VALIDATE doesn't init from dma descriptors.
+ * OUT: was "usb-short", we must restart.
+ */
+ if (ep->is_in && !req->valid) {
+ struct net2280_request *entry, *prev = NULL;
+ int reqmode, done = 0;
+
+ ep_dbg(ep->dev, "%s dma hiccup td %p\n", ep->ep.name, req->td);
+ ep->in_fifo_validate = likely(req->req.zero ||
+ (req->req.length % ep->ep.maxpacket) != 0);
+ if (ep->in_fifo_validate)
+ dmactl |= BIT(DMA_FIFO_VALIDATE);
+ list_for_each_entry(entry, &ep->queue, queue) {
+ __le32 dmacount;
+
+ if (entry == req)
+ continue;
+ dmacount = entry->td->dmacount;
+ if (!done) {
+ reqmode = likely(entry->req.zero ||
+ (entry->req.length % ep->ep.maxpacket));
+ if (reqmode == ep->in_fifo_validate) {
+ entry->valid = 1;
+ dmacount |= valid_bit;
+ entry->td->dmacount = dmacount;
+ prev = entry;
+ continue;
+ } else {
+ /* force a hiccup */
+ prev->td->dmacount |= dma_done_ie;
+ done = 1;
+ }
+ }
+
+ /* walk the rest of the queue so unlinks behave */
+ entry->valid = 0;
+ dmacount &= ~valid_bit;
+ entry->td->dmacount = dmacount;
+ prev = entry;
+ }
+ }
+
+ writel(0, &ep->dma->dmactl);
+ start_queue(ep, dmactl, req->td_dma);
+}
+
+static void abort_dma_228x(struct net2280_ep *ep)
+{
+ /* abort the current transfer */
+ if (likely(!list_empty(&ep->queue))) {
+ /* FIXME work around errata 0121, 0122, 0124 */
+ writel(BIT(DMA_ABORT), &ep->dma->dmastat);
+ spin_stop_dma(ep->dma);
+ } else
+ stop_dma(ep->dma);
+ scan_dma_completions(ep);
+}
+
+static void abort_dma_338x(struct net2280_ep *ep)
+{
+ writel(BIT(DMA_ABORT), &ep->dma->dmastat);
+ spin_stop_dma(ep->dma);
+}
+
+static void abort_dma(struct net2280_ep *ep)
+{
+ if (ep->dev->quirks & PLX_LEGACY)
+ return abort_dma_228x(ep);
+ return abort_dma_338x(ep);
+}
+
+/* dequeue ALL requests */
+static void nuke(struct net2280_ep *ep)
+{
+ struct net2280_request *req;
+
+ /* called with spinlock held */
+ ep->stopped = 1;
+ if (ep->dma)
+ abort_dma(ep);
+ while (!list_empty(&ep->queue)) {
+ req = list_entry(ep->queue.next,
+ struct net2280_request,
+ queue);
+ done(ep, req, -ESHUTDOWN);
+ }
+}
+
+/* dequeue JUST ONE request */
+static int net2280_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct net2280_ep *ep;
+ struct net2280_request *req;
+ unsigned long flags;
+ u32 dmactl;
+ int stopped;
+
+ ep = container_of(_ep, struct net2280_ep, ep);
+ if (!_ep || (!ep->desc && ep->num != 0) || !_req)
+ return -EINVAL;
+
+ spin_lock_irqsave(&ep->dev->lock, flags);
+ stopped = ep->stopped;
+
+ /* quiesce dma while we patch the queue */
+ dmactl = 0;
+ ep->stopped = 1;
+ if (ep->dma) {
+ dmactl = readl(&ep->dma->dmactl);
+ /* WARNING erratum 0127 may kick in ... */
+ stop_dma(ep->dma);
+ scan_dma_completions(ep);
+ }
+
+ /* make sure it's still queued on this endpoint */
+ list_for_each_entry(req, &ep->queue, queue) {
+ if (&req->req == _req)
+ break;
+ }
+ if (&req->req != _req) {
+ spin_unlock_irqrestore(&ep->dev->lock, flags);
+ return -EINVAL;
+ }
+
+ /* queue head may be partially complete. */
+ if (ep->queue.next == &req->queue) {
+ if (ep->dma) {
+ ep_dbg(ep->dev, "unlink (%s) dma\n", _ep->name);
+ _req->status = -ECONNRESET;
+ abort_dma(ep);
+ if (likely(ep->queue.next == &req->queue)) {
+ /* NOTE: misreports single-transfer mode*/
+ req->td->dmacount = 0; /* invalidate */
+ dma_done(ep, req,
+ readl(&ep->dma->dmacount),
+ -ECONNRESET);
+ }
+ } else {
+ ep_dbg(ep->dev, "unlink (%s) pio\n", _ep->name);
+ done(ep, req, -ECONNRESET);
+ }
+ req = NULL;
+
+ /* patch up hardware chaining data */
+ } else if (ep->dma && use_dma_chaining) {
+ if (req->queue.prev == ep->queue.next) {
+ writel(le32_to_cpu(req->td->dmadesc),
+ &ep->dma->dmadesc);
+ if (req->td->dmacount & dma_done_ie)
+ writel(readl(&ep->dma->dmacount) |
+ le32_to_cpu(dma_done_ie),
+ &ep->dma->dmacount);
+ } else {
+ struct net2280_request *prev;
+
+ prev = list_entry(req->queue.prev,
+ struct net2280_request, queue);
+ prev->td->dmadesc = req->td->dmadesc;
+ if (req->td->dmacount & dma_done_ie)
+ prev->td->dmacount |= dma_done_ie;
+ }
+ }
+
+ if (req)
+ done(ep, req, -ECONNRESET);
+ ep->stopped = stopped;
+
+ if (ep->dma) {
+ /* turn off dma on inactive queues */
+ if (list_empty(&ep->queue))
+ stop_dma(ep->dma);
+ else if (!ep->stopped) {
+ /* resume current request, or start new one */
+ if (req)
+ writel(dmactl, &ep->dma->dmactl);
+ else
+ start_dma(ep, list_entry(ep->queue.next,
+ struct net2280_request, queue));
+ }
+ }
+
+ spin_unlock_irqrestore(&ep->dev->lock, flags);
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int net2280_fifo_status(struct usb_ep *_ep);
+
+static int
+net2280_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedged)
+{
+ struct net2280_ep *ep;
+ unsigned long flags;
+ int retval = 0;
+
+ ep = container_of(_ep, struct net2280_ep, ep);
+ if (!_ep || (!ep->desc && ep->num != 0))
+ return -EINVAL;
+ if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
+ return -ESHUTDOWN;
+ if (ep->desc /* not ep0 */ && (ep->desc->bmAttributes & 0x03)
+ == USB_ENDPOINT_XFER_ISOC)
+ return -EINVAL;
+
+ spin_lock_irqsave(&ep->dev->lock, flags);
+ if (!list_empty(&ep->queue))
+ retval = -EAGAIN;
+ else if (ep->is_in && value && net2280_fifo_status(_ep) != 0)
+ retval = -EAGAIN;
+ else {
+ ep_vdbg(ep->dev, "%s %s %s\n", _ep->name,
+ value ? "set" : "clear",
+ wedged ? "wedge" : "halt");
+ /* set/clear, then synch memory views with the device */
+ if (value) {
+ if (ep->num == 0)
+ ep->dev->protocol_stall = 1;
+ else
+ set_halt(ep);
+ if (wedged)
+ ep->wedged = 1;
+ } else {
+ clear_halt(ep);
+ if (ep->dev->quirks & PLX_SUPERSPEED &&
+ !list_empty(&ep->queue) && ep->td_dma)
+ restart_dma(ep);
+ ep->wedged = 0;
+ }
+ (void) readl(&ep->regs->ep_rsp);
+ }
+ spin_unlock_irqrestore(&ep->dev->lock, flags);
+
+ return retval;
+}
+
+static int net2280_set_halt(struct usb_ep *_ep, int value)
+{
+ return net2280_set_halt_and_wedge(_ep, value, 0);
+}
+
+static int net2280_set_wedge(struct usb_ep *_ep)
+{
+ if (!_ep || _ep->name == ep0name)
+ return -EINVAL;
+ return net2280_set_halt_and_wedge(_ep, 1, 1);
+}
+
+static int net2280_fifo_status(struct usb_ep *_ep)
+{
+ struct net2280_ep *ep;
+ u32 avail;
+
+ ep = container_of(_ep, struct net2280_ep, ep);
+ if (!_ep || (!ep->desc && ep->num != 0))
+ return -ENODEV;
+ if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
+ return -ESHUTDOWN;
+
+ avail = readl(&ep->regs->ep_avail) & (BIT(12) - 1);
+ if (avail > ep->fifo_size)
+ return -EOVERFLOW;
+ if (ep->is_in)
+ avail = ep->fifo_size - avail;
+ return avail;
+}
+
+static void net2280_fifo_flush(struct usb_ep *_ep)
+{
+ struct net2280_ep *ep;
+
+ ep = container_of(_ep, struct net2280_ep, ep);
+ if (!_ep || (!ep->desc && ep->num != 0))
+ return;
+ if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
+ return;
+
+ writel(BIT(FIFO_FLUSH), &ep->regs->ep_stat);
+ (void) readl(&ep->regs->ep_rsp);
+}
+
+static const struct usb_ep_ops net2280_ep_ops = {
+ .enable = net2280_enable,
+ .disable = net2280_disable,
+
+ .alloc_request = net2280_alloc_request,
+ .free_request = net2280_free_request,
+
+ .queue = net2280_queue,
+ .dequeue = net2280_dequeue,
+
+ .set_halt = net2280_set_halt,
+ .set_wedge = net2280_set_wedge,
+ .fifo_status = net2280_fifo_status,
+ .fifo_flush = net2280_fifo_flush,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int net2280_get_frame(struct usb_gadget *_gadget)
+{
+ struct net2280 *dev;
+ unsigned long flags;
+ u16 retval;
+
+ if (!_gadget)
+ return -ENODEV;
+ dev = container_of(_gadget, struct net2280, gadget);
+ spin_lock_irqsave(&dev->lock, flags);
+ retval = get_idx_reg(dev->regs, REG_FRAME) & 0x03ff;
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return retval;
+}
+
+static int net2280_wakeup(struct usb_gadget *_gadget)
+{
+ struct net2280 *dev;
+ u32 tmp;
+ unsigned long flags;
+
+ if (!_gadget)
+ return 0;
+ dev = container_of(_gadget, struct net2280, gadget);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ tmp = readl(&dev->usb->usbctl);
+ if (tmp & BIT(DEVICE_REMOTE_WAKEUP_ENABLE))
+ writel(BIT(GENERATE_RESUME), &dev->usb->usbstat);
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ /* pci writes may still be posted */
+ return 0;
+}
+
+static int net2280_set_selfpowered(struct usb_gadget *_gadget, int value)
+{
+ struct net2280 *dev;
+ u32 tmp;
+ unsigned long flags;
+
+ if (!_gadget)
+ return 0;
+ dev = container_of(_gadget, struct net2280, gadget);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ tmp = readl(&dev->usb->usbctl);
+ if (value) {
+ tmp |= BIT(SELF_POWERED_STATUS);
+ dev->selfpowered = 1;
+ } else {
+ tmp &= ~BIT(SELF_POWERED_STATUS);
+ dev->selfpowered = 0;
+ }
+ writel(tmp, &dev->usb->usbctl);
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ return 0;
+}
+
+static int net2280_pullup(struct usb_gadget *_gadget, int is_on)
+{
+ struct net2280 *dev;
+ u32 tmp;
+ unsigned long flags;
+
+ if (!_gadget)
+ return -ENODEV;
+ dev = container_of(_gadget, struct net2280, gadget);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ tmp = readl(&dev->usb->usbctl);
+ dev->softconnect = (is_on != 0);
+ if (is_on)
+ tmp |= BIT(USB_DETECT_ENABLE);
+ else
+ tmp &= ~BIT(USB_DETECT_ENABLE);
+ writel(tmp, &dev->usb->usbctl);
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ return 0;
+}
+
+static int net2280_start(struct usb_gadget *_gadget,
+ struct usb_gadget_driver *driver);
+static int net2280_stop(struct usb_gadget *_gadget,
+ struct usb_gadget_driver *driver);
+
+static const struct usb_gadget_ops net2280_ops = {
+ .get_frame = net2280_get_frame,
+ .wakeup = net2280_wakeup,
+ .set_selfpowered = net2280_set_selfpowered,
+ .pullup = net2280_pullup,
+ .udc_start = net2280_start,
+ .udc_stop = net2280_stop,
+};
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+
+/* FIXME move these into procfs, and use seq_file.
+ * Sysfs _still_ doesn't behave for arbitrarily sized files,
+ * and also doesn't help products using this with 2.4 kernels.
+ */
+
+/* "function" sysfs attribute */
+static ssize_t function_show(struct device *_dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct net2280 *dev = dev_get_drvdata(_dev);
+
+ if (!dev->driver || !dev->driver->function ||
+ strlen(dev->driver->function) > PAGE_SIZE)
+ return 0;
+ return scnprintf(buf, PAGE_SIZE, "%s\n", dev->driver->function);
+}
+static DEVICE_ATTR_RO(function);
+
+static ssize_t registers_show(struct device *_dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct net2280 *dev;
+ char *next;
+ unsigned size, t;
+ unsigned long flags;
+ int i;
+ u32 t1, t2;
+ const char *s;
+
+ dev = dev_get_drvdata(_dev);
+ next = buf;
+ size = PAGE_SIZE;
+ spin_lock_irqsave(&dev->lock, flags);
+
+ if (dev->driver)
+ s = dev->driver->driver.name;
+ else
+ s = "(none)";
+
+ /* Main Control Registers */
+ t = scnprintf(next, size, "%s version " DRIVER_VERSION
+ ", chiprev %04x, dma %s\n\n"
+ "devinit %03x fifoctl %08x gadget '%s'\n"
+ "pci irqenb0 %02x irqenb1 %08x "
+ "irqstat0 %04x irqstat1 %08x\n",
+ driver_name, dev->chiprev,
+ use_dma
+ ? (use_dma_chaining ? "chaining" : "enabled")
+ : "disabled",
+ readl(&dev->regs->devinit),
+ readl(&dev->regs->fifoctl),
+ s,
+ readl(&dev->regs->pciirqenb0),
+ readl(&dev->regs->pciirqenb1),
+ readl(&dev->regs->irqstat0),
+ readl(&dev->regs->irqstat1));
+ size -= t;
+ next += t;
+
+ /* USB Control Registers */
+ t1 = readl(&dev->usb->usbctl);
+ t2 = readl(&dev->usb->usbstat);
+ if (t1 & BIT(VBUS_PIN)) {
+ if (t2 & BIT(HIGH_SPEED))
+ s = "high speed";
+ else if (dev->gadget.speed == USB_SPEED_UNKNOWN)
+ s = "powered";
+ else
+ s = "full speed";
+ /* full speed bit (6) not working?? */
+ } else
+ s = "not attached";
+ t = scnprintf(next, size,
+ "stdrsp %08x usbctl %08x usbstat %08x "
+ "addr 0x%02x (%s)\n",
+ readl(&dev->usb->stdrsp), t1, t2,
+ readl(&dev->usb->ouraddr), s);
+ size -= t;
+ next += t;
+
+ /* PCI Master Control Registers */
+
+ /* DMA Control Registers */
+
+ /* Configurable EP Control Registers */
+ for (i = 0; i < dev->n_ep; i++) {
+ struct net2280_ep *ep;
+
+ ep = &dev->ep[i];
+ if (i && !ep->desc)
+ continue;
+
+ t1 = readl(&ep->cfg->ep_cfg);
+ t2 = readl(&ep->regs->ep_rsp) & 0xff;
+ t = scnprintf(next, size,
+ "\n%s\tcfg %05x rsp (%02x) %s%s%s%s%s%s%s%s"
+ "irqenb %02x\n",
+ ep->ep.name, t1, t2,
+ (t2 & BIT(CLEAR_NAK_OUT_PACKETS))
+ ? "NAK " : "",
+ (t2 & BIT(CLEAR_EP_HIDE_STATUS_PHASE))
+ ? "hide " : "",
+ (t2 & BIT(CLEAR_EP_FORCE_CRC_ERROR))
+ ? "CRC " : "",
+ (t2 & BIT(CLEAR_INTERRUPT_MODE))
+ ? "interrupt " : "",
+ (t2 & BIT(CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE))
+ ? "status " : "",
+ (t2 & BIT(CLEAR_NAK_OUT_PACKETS_MODE))
+ ? "NAKmode " : "",
+ (t2 & BIT(CLEAR_ENDPOINT_TOGGLE))
+ ? "DATA1 " : "DATA0 ",
+ (t2 & BIT(CLEAR_ENDPOINT_HALT))
+ ? "HALT " : "",
+ readl(&ep->regs->ep_irqenb));
+ size -= t;
+ next += t;
+
+ t = scnprintf(next, size,
+ "\tstat %08x avail %04x "
+ "(ep%d%s-%s)%s\n",
+ readl(&ep->regs->ep_stat),
+ readl(&ep->regs->ep_avail),
+ t1 & 0x0f, DIR_STRING(t1),
+ type_string(t1 >> 8),
+ ep->stopped ? "*" : "");
+ size -= t;
+ next += t;
+
+ if (!ep->dma)
+ continue;
+
+ t = scnprintf(next, size,
+ " dma\tctl %08x stat %08x count %08x\n"
+ "\taddr %08x desc %08x\n",
+ readl(&ep->dma->dmactl),
+ readl(&ep->dma->dmastat),
+ readl(&ep->dma->dmacount),
+ readl(&ep->dma->dmaaddr),
+ readl(&ep->dma->dmadesc));
+ size -= t;
+ next += t;
+
+ }
+
+ /* Indexed Registers (none yet) */
+
+ /* Statistics */
+ t = scnprintf(next, size, "\nirqs: ");
+ size -= t;
+ next += t;
+ for (i = 0; i < dev->n_ep; i++) {
+ struct net2280_ep *ep;
+
+ ep = &dev->ep[i];
+ if (i && !ep->irqs)
+ continue;
+ t = scnprintf(next, size, " %s/%lu", ep->ep.name, ep->irqs);
+ size -= t;
+ next += t;
+
+ }
+ t = scnprintf(next, size, "\n");
+ size -= t;
+ next += t;
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ return PAGE_SIZE - size;
+}
+static DEVICE_ATTR_RO(registers);
+
+static ssize_t queues_show(struct device *_dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct net2280 *dev;
+ char *next;
+ unsigned size;
+ unsigned long flags;
+ int i;
+
+ dev = dev_get_drvdata(_dev);
+ next = buf;
+ size = PAGE_SIZE;
+ spin_lock_irqsave(&dev->lock, flags);
+
+ for (i = 0; i < dev->n_ep; i++) {
+ struct net2280_ep *ep = &dev->ep[i];
+ struct net2280_request *req;
+ int t;
+
+ if (i != 0) {
+ const struct usb_endpoint_descriptor *d;
+
+ d = ep->desc;
+ if (!d)
+ continue;
+ t = d->bEndpointAddress;
+ t = scnprintf(next, size,
+ "\n%s (ep%d%s-%s) max %04x %s fifo %d\n",
+ ep->ep.name, t & USB_ENDPOINT_NUMBER_MASK,
+ (t & USB_DIR_IN) ? "in" : "out",
+ type_string(d->bmAttributes),
+ usb_endpoint_maxp(d) & 0x1fff,
+ ep->dma ? "dma" : "pio", ep->fifo_size
+ );
+ } else /* ep0 should only have one transfer queued */
+ t = scnprintf(next, size, "ep0 max 64 pio %s\n",
+ ep->is_in ? "in" : "out");
+ if (t <= 0 || t > size)
+ goto done;
+ size -= t;
+ next += t;
+
+ if (list_empty(&ep->queue)) {
+ t = scnprintf(next, size, "\t(nothing queued)\n");
+ if (t <= 0 || t > size)
+ goto done;
+ size -= t;
+ next += t;
+ continue;
+ }
+ list_for_each_entry(req, &ep->queue, queue) {
+ if (ep->dma && req->td_dma == readl(&ep->dma->dmadesc))
+ t = scnprintf(next, size,
+ "\treq %p len %d/%d "
+ "buf %p (dmacount %08x)\n",
+ &req->req, req->req.actual,
+ req->req.length, req->req.buf,
+ readl(&ep->dma->dmacount));
+ else
+ t = scnprintf(next, size,
+ "\treq %p len %d/%d buf %p\n",
+ &req->req, req->req.actual,
+ req->req.length, req->req.buf);
+ if (t <= 0 || t > size)
+ goto done;
+ size -= t;
+ next += t;
+
+ if (ep->dma) {
+ struct net2280_dma *td;
+
+ td = req->td;
+ t = scnprintf(next, size, "\t td %08x "
+ " count %08x buf %08x desc %08x\n",
+ (u32) req->td_dma,
+ le32_to_cpu(td->dmacount),
+ le32_to_cpu(td->dmaaddr),
+ le32_to_cpu(td->dmadesc));
+ if (t <= 0 || t > size)
+ goto done;
+ size -= t;
+ next += t;
+ }
+ }
+ }
+
+done:
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return PAGE_SIZE - size;
+}
+static DEVICE_ATTR_RO(queues);
+
+
+#else
+
+#define device_create_file(a, b) (0)
+#define device_remove_file(a, b) do { } while (0)
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+/* another driver-specific mode might be a request type doing dma
+ * to/from another device fifo instead of to/from memory.
+ */
+
+static void set_fifo_mode(struct net2280 *dev, int mode)
+{
+ /* keeping high bits preserves BAR2 */
+ writel((0xffff << PCI_BASE2_RANGE) | mode, &dev->regs->fifoctl);
+
+ /* always ep-{a,b,e,f} ... maybe not ep-c or ep-d */
+ INIT_LIST_HEAD(&dev->gadget.ep_list);
+ list_add_tail(&dev->ep[1].ep.ep_list, &dev->gadget.ep_list);
+ list_add_tail(&dev->ep[2].ep.ep_list, &dev->gadget.ep_list);
+ switch (mode) {
+ case 0:
+ list_add_tail(&dev->ep[3].ep.ep_list, &dev->gadget.ep_list);
+ list_add_tail(&dev->ep[4].ep.ep_list, &dev->gadget.ep_list);
+ dev->ep[1].fifo_size = dev->ep[2].fifo_size = 1024;
+ break;
+ case 1:
+ dev->ep[1].fifo_size = dev->ep[2].fifo_size = 2048;
+ break;
+ case 2:
+ list_add_tail(&dev->ep[3].ep.ep_list, &dev->gadget.ep_list);
+ dev->ep[1].fifo_size = 2048;
+ dev->ep[2].fifo_size = 1024;
+ break;
+ }
+ /* fifo sizes for ep0, ep-c, ep-d, ep-e, and ep-f never change */
+ list_add_tail(&dev->ep[5].ep.ep_list, &dev->gadget.ep_list);
+ list_add_tail(&dev->ep[6].ep.ep_list, &dev->gadget.ep_list);
+}
+
+static void defect7374_disable_data_eps(struct net2280 *dev)
+{
+ /*
+ * For Defect 7374, disable data EPs (and more):
+ * - This phase undoes the earlier phase of the Defect 7374 workaround,
+ * returing ep regs back to normal.
+ */
+ struct net2280_ep *ep;
+ int i;
+ unsigned char ep_sel;
+ u32 tmp_reg;
+
+ for (i = 1; i < 5; i++) {
+ ep = &dev->ep[i];
+ writel(0, &ep->cfg->ep_cfg);
+ }
+
+ /* CSROUT, CSRIN, PCIOUT, PCIIN, STATIN, RCIN */
+ for (i = 0; i < 6; i++)
+ writel(0, &dev->dep[i].dep_cfg);
+
+ for (ep_sel = 0; ep_sel <= 21; ep_sel++) {
+ /* Select an endpoint for subsequent operations: */
+ tmp_reg = readl(&dev->plregs->pl_ep_ctrl);
+ writel(((tmp_reg & ~0x1f) | ep_sel), &dev->plregs->pl_ep_ctrl);
+
+ if (ep_sel < 2 || (ep_sel > 9 && ep_sel < 14) ||
+ ep_sel == 18 || ep_sel == 20)
+ continue;
+
+ /* Change settings on some selected endpoints */
+ tmp_reg = readl(&dev->plregs->pl_ep_cfg_4);
+ tmp_reg &= ~BIT(NON_CTRL_IN_TOLERATE_BAD_DIR);
+ writel(tmp_reg, &dev->plregs->pl_ep_cfg_4);
+ tmp_reg = readl(&dev->plregs->pl_ep_ctrl);
+ tmp_reg |= BIT(EP_INITIALIZED);
+ writel(tmp_reg, &dev->plregs->pl_ep_ctrl);
+ }
+}
+
+static void defect7374_enable_data_eps_zero(struct net2280 *dev)
+{
+ u32 tmp = 0, tmp_reg;
+ u32 fsmvalue, scratch;
+ int i;
+ unsigned char ep_sel;
+
+ scratch = get_idx_reg(dev->regs, SCRATCH);
+ fsmvalue = scratch & (0xf << DEFECT7374_FSM_FIELD);
+ scratch &= ~(0xf << DEFECT7374_FSM_FIELD);
+
+ /*See if firmware needs to set up for workaround*/
+ if (fsmvalue != DEFECT7374_FSM_SS_CONTROL_READ) {
+ ep_warn(dev, "Operate Defect 7374 workaround soft this time");
+ ep_warn(dev, "It will operate on cold-reboot and SS connect");
+
+ /*GPEPs:*/
+ tmp = ((0 << ENDPOINT_NUMBER) | BIT(ENDPOINT_DIRECTION) |
+ (2 << OUT_ENDPOINT_TYPE) | (2 << IN_ENDPOINT_TYPE) |
+ ((dev->enhanced_mode) ?
+ BIT(OUT_ENDPOINT_ENABLE) : BIT(ENDPOINT_ENABLE)) |
+ BIT(IN_ENDPOINT_ENABLE));
+
+ for (i = 1; i < 5; i++)
+ writel(tmp, &dev->ep[i].cfg->ep_cfg);
+
+ /* CSRIN, PCIIN, STATIN, RCIN*/
+ tmp = ((0 << ENDPOINT_NUMBER) | BIT(ENDPOINT_ENABLE));
+ writel(tmp, &dev->dep[1].dep_cfg);
+ writel(tmp, &dev->dep[3].dep_cfg);
+ writel(tmp, &dev->dep[4].dep_cfg);
+ writel(tmp, &dev->dep[5].dep_cfg);
+
+ /*Implemented for development and debug.
+ * Can be refined/tuned later.*/
+ for (ep_sel = 0; ep_sel <= 21; ep_sel++) {
+ /* Select an endpoint for subsequent operations: */
+ tmp_reg = readl(&dev->plregs->pl_ep_ctrl);
+ writel(((tmp_reg & ~0x1f) | ep_sel),
+ &dev->plregs->pl_ep_ctrl);
+
+ if (ep_sel == 1) {
+ tmp =
+ (readl(&dev->plregs->pl_ep_ctrl) |
+ BIT(CLEAR_ACK_ERROR_CODE) | 0);
+ writel(tmp, &dev->plregs->pl_ep_ctrl);
+ continue;
+ }
+
+ if (ep_sel == 0 || (ep_sel > 9 && ep_sel < 14) ||
+ ep_sel == 18 || ep_sel == 20)
+ continue;
+
+ tmp = (readl(&dev->plregs->pl_ep_cfg_4) |
+ BIT(NON_CTRL_IN_TOLERATE_BAD_DIR) | 0);
+ writel(tmp, &dev->plregs->pl_ep_cfg_4);
+
+ tmp = readl(&dev->plregs->pl_ep_ctrl) &
+ ~BIT(EP_INITIALIZED);
+ writel(tmp, &dev->plregs->pl_ep_ctrl);
+
+ }
+
+ /* Set FSM to focus on the first Control Read:
+ * - Tip: Connection speed is known upon the first
+ * setup request.*/
+ scratch |= DEFECT7374_FSM_WAITING_FOR_CONTROL_READ;
+ set_idx_reg(dev->regs, SCRATCH, scratch);
+
+ } else{
+ ep_warn(dev, "Defect 7374 workaround soft will NOT operate");
+ ep_warn(dev, "It will operate on cold-reboot and SS connect");
+ }
+}
+
+/* keeping it simple:
+ * - one bus driver, initted first;
+ * - one function driver, initted second
+ *
+ * most of the work to support multiple net2280 controllers would
+ * be to associate this gadget driver (yes?) with all of them, or
+ * perhaps to bind specific drivers to specific devices.
+ */
+
+static void usb_reset_228x(struct net2280 *dev)
+{
+ u32 tmp;
+
+ dev->gadget.speed = USB_SPEED_UNKNOWN;
+ (void) readl(&dev->usb->usbctl);
+
+ net2280_led_init(dev);
+
+ /* disable automatic responses, and irqs */
+ writel(0, &dev->usb->stdrsp);
+ writel(0, &dev->regs->pciirqenb0);
+ writel(0, &dev->regs->pciirqenb1);
+
+ /* clear old dma and irq state */
+ for (tmp = 0; tmp < 4; tmp++) {
+ struct net2280_ep *ep = &dev->ep[tmp + 1];
+ if (ep->dma)
+ abort_dma(ep);
+ }
+
+ writel(~0, &dev->regs->irqstat0),
+ writel(~(u32)BIT(SUSPEND_REQUEST_INTERRUPT), &dev->regs->irqstat1),
+
+ /* reset, and enable pci */
+ tmp = readl(&dev->regs->devinit) |
+ BIT(PCI_ENABLE) |
+ BIT(FIFO_SOFT_RESET) |
+ BIT(USB_SOFT_RESET) |
+ BIT(M8051_RESET);
+ writel(tmp, &dev->regs->devinit);
+
+ /* standard fifo and endpoint allocations */
+ set_fifo_mode(dev, (fifo_mode <= 2) ? fifo_mode : 0);
+}
+
+static void usb_reset_338x(struct net2280 *dev)
+{
+ u32 tmp;
+ u32 fsmvalue;
+
+ dev->gadget.speed = USB_SPEED_UNKNOWN;
+ (void)readl(&dev->usb->usbctl);
+
+ net2280_led_init(dev);
+
+ fsmvalue = get_idx_reg(dev->regs, SCRATCH) &
+ (0xf << DEFECT7374_FSM_FIELD);
+
+ /* See if firmware needs to set up for workaround: */
+ if (fsmvalue != DEFECT7374_FSM_SS_CONTROL_READ) {
+ ep_info(dev, "%s: Defect 7374 FsmValue 0x%08x\n", __func__,
+ fsmvalue);
+ } else {
+ /* disable automatic responses, and irqs */
+ writel(0, &dev->usb->stdrsp);
+ writel(0, &dev->regs->pciirqenb0);
+ writel(0, &dev->regs->pciirqenb1);
+ }
+
+ /* clear old dma and irq state */
+ for (tmp = 0; tmp < 4; tmp++) {
+ struct net2280_ep *ep = &dev->ep[tmp + 1];
+
+ if (ep->dma)
+ abort_dma(ep);
+ }
+
+ writel(~0, &dev->regs->irqstat0), writel(~0, &dev->regs->irqstat1);
+
+ if (fsmvalue == DEFECT7374_FSM_SS_CONTROL_READ) {
+ /* reset, and enable pci */
+ tmp = readl(&dev->regs->devinit) |
+ BIT(PCI_ENABLE) |
+ BIT(FIFO_SOFT_RESET) |
+ BIT(USB_SOFT_RESET) |
+ BIT(M8051_RESET);
+
+ writel(tmp, &dev->regs->devinit);
+ }
+
+ /* always ep-{1,2,3,4} ... maybe not ep-3 or ep-4 */
+ INIT_LIST_HEAD(&dev->gadget.ep_list);
+
+ for (tmp = 1; tmp < dev->n_ep; tmp++)
+ list_add_tail(&dev->ep[tmp].ep.ep_list, &dev->gadget.ep_list);
+
+}
+
+static void usb_reset(struct net2280 *dev)
+{
+ if (dev->quirks & PLX_LEGACY)
+ return usb_reset_228x(dev);
+ return usb_reset_338x(dev);
+}
+
+static void usb_reinit_228x(struct net2280 *dev)
+{
+ u32 tmp;
+ int init_dma;
+
+ /* use_dma changes are ignored till next device re-init */
+ init_dma = use_dma;
+
+ /* basic endpoint init */
+ for (tmp = 0; tmp < 7; tmp++) {
+ struct net2280_ep *ep = &dev->ep[tmp];
+
+ ep->ep.name = ep_name[tmp];
+ ep->dev = dev;
+ ep->num = tmp;
+
+ if (tmp > 0 && tmp <= 4) {
+ ep->fifo_size = 1024;
+ if (init_dma)
+ ep->dma = &dev->dma[tmp - 1];
+ } else
+ ep->fifo_size = 64;
+ ep->regs = &dev->epregs[tmp];
+ ep->cfg = &dev->epregs[tmp];
+ ep_reset_228x(dev->regs, ep);
+ }
+ usb_ep_set_maxpacket_limit(&dev->ep[0].ep, 64);
+ usb_ep_set_maxpacket_limit(&dev->ep[5].ep, 64);
+ usb_ep_set_maxpacket_limit(&dev->ep[6].ep, 64);
+
+ dev->gadget.ep0 = &dev->ep[0].ep;
+ dev->ep[0].stopped = 0;
+ INIT_LIST_HEAD(&dev->gadget.ep0->ep_list);
+
+ /* we want to prevent lowlevel/insecure access from the USB host,
+ * but erratum 0119 means this enable bit is ignored
+ */
+ for (tmp = 0; tmp < 5; tmp++)
+ writel(EP_DONTUSE, &dev->dep[tmp].dep_cfg);
+}
+
+static void usb_reinit_338x(struct net2280 *dev)
+{
+ int init_dma;
+ int i;
+ u32 tmp, val;
+ u32 fsmvalue;
+ static const u32 ne[9] = { 0, 1, 2, 3, 4, 1, 2, 3, 4 };
+ static const u32 ep_reg_addr[9] = { 0x00, 0xC0, 0x00, 0xC0, 0x00,
+ 0x00, 0xC0, 0x00, 0xC0 };
+
+ /* use_dma changes are ignored till next device re-init */
+ init_dma = use_dma;
+
+ /* basic endpoint init */
+ for (i = 0; i < dev->n_ep; i++) {
+ struct net2280_ep *ep = &dev->ep[i];
+
+ ep->ep.name = ep_name[i];
+ ep->dev = dev;
+ ep->num = i;
+
+ if (i > 0 && i <= 4 && init_dma)
+ ep->dma = &dev->dma[i - 1];
+
+ if (dev->enhanced_mode) {
+ ep->cfg = &dev->epregs[ne[i]];
+ ep->regs = (struct net2280_ep_regs __iomem *)
+ (((void __iomem *)&dev->epregs[ne[i]]) +
+ ep_reg_addr[i]);
+ ep->fiforegs = &dev->fiforegs[i];
+ } else {
+ ep->cfg = &dev->epregs[i];
+ ep->regs = &dev->epregs[i];
+ ep->fiforegs = &dev->fiforegs[i];
+ }
+
+ ep->fifo_size = (i != 0) ? 2048 : 512;
+
+ ep_reset_338x(dev->regs, ep);
+ }
+ usb_ep_set_maxpacket_limit(&dev->ep[0].ep, 512);
+
+ dev->gadget.ep0 = &dev->ep[0].ep;
+ dev->ep[0].stopped = 0;
+
+ /* Link layer set up */
+ fsmvalue = get_idx_reg(dev->regs, SCRATCH) &
+ (0xf << DEFECT7374_FSM_FIELD);
+
+ /* See if driver needs to set up for workaround: */
+ if (fsmvalue != DEFECT7374_FSM_SS_CONTROL_READ)
+ ep_info(dev, "%s: Defect 7374 FsmValue %08x\n",
+ __func__, fsmvalue);
+ else {
+ tmp = readl(&dev->usb_ext->usbctl2) &
+ ~(BIT(U1_ENABLE) | BIT(U2_ENABLE) | BIT(LTM_ENABLE));
+ writel(tmp, &dev->usb_ext->usbctl2);
+ }
+
+ /* Hardware Defect and Workaround */
+ val = readl(&dev->ll_lfps_regs->ll_lfps_5);
+ val &= ~(0xf << TIMER_LFPS_6US);
+ val |= 0x5 << TIMER_LFPS_6US;
+ writel(val, &dev->ll_lfps_regs->ll_lfps_5);
+
+ val = readl(&dev->ll_lfps_regs->ll_lfps_6);
+ val &= ~(0xffff << TIMER_LFPS_80US);
+ val |= 0x0100 << TIMER_LFPS_80US;
+ writel(val, &dev->ll_lfps_regs->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 &= ~(0x1f << HOT_TX_NORESET_TS2);
+ val |= 0x10 << HOT_TX_NORESET_TS2;
+ writel(val, &dev->ll_tsn_regs->ll_tsn_counters_2);
+
+ val = readl(&dev->ll_tsn_regs->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);
+
+ /*
+ * Set Recovery Idle to Recover bit:
+ * - On SS connections, setting Recovery Idle to Recover Fmw improves
+ * link robustness with various hosts and hubs.
+ * - 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 |= BIT(RECOVERY_IDLE_TO_RECOVER_FMW);
+ writel(val, &dev->ll_chicken_reg->ll_tsn_chicken_bit);
+
+ INIT_LIST_HEAD(&dev->gadget.ep0->ep_list);
+
+ /* disable dedicated endpoints */
+ writel(0x0D, &dev->dep[0].dep_cfg);
+ writel(0x0D, &dev->dep[1].dep_cfg);
+ writel(0x0E, &dev->dep[2].dep_cfg);
+ writel(0x0E, &dev->dep[3].dep_cfg);
+ writel(0x0F, &dev->dep[4].dep_cfg);
+ writel(0x0C, &dev->dep[5].dep_cfg);
+}
+
+static void usb_reinit(struct net2280 *dev)
+{
+ if (dev->quirks & PLX_LEGACY)
+ return usb_reinit_228x(dev);
+ return usb_reinit_338x(dev);
+}
+
+static void ep0_start_228x(struct net2280 *dev)
+{
+ writel(BIT(CLEAR_EP_HIDE_STATUS_PHASE) |
+ BIT(CLEAR_NAK_OUT_PACKETS) |
+ BIT(CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE),
+ &dev->epregs[0].ep_rsp);
+
+ /*
+ * hardware optionally handles a bunch of standard requests
+ * that the API hides from drivers anyway. have it do so.
+ * endpoint status/features are handled in software, to
+ * help pass tests for some dubious behavior.
+ */
+ writel(BIT(SET_TEST_MODE) |
+ BIT(SET_ADDRESS) |
+ BIT(DEVICE_SET_CLEAR_DEVICE_REMOTE_WAKEUP) |
+ BIT(GET_DEVICE_STATUS) |
+ BIT(GET_INTERFACE_STATUS),
+ &dev->usb->stdrsp);
+ writel(BIT(USB_ROOT_PORT_WAKEUP_ENABLE) |
+ BIT(SELF_POWERED_USB_DEVICE) |
+ BIT(REMOTE_WAKEUP_SUPPORT) |
+ (dev->softconnect << USB_DETECT_ENABLE) |
+ BIT(SELF_POWERED_STATUS),
+ &dev->usb->usbctl);
+
+ /* enable irqs so we can see ep0 and general operation */
+ writel(BIT(SETUP_PACKET_INTERRUPT_ENABLE) |
+ BIT(ENDPOINT_0_INTERRUPT_ENABLE),
+ &dev->regs->pciirqenb0);
+ writel(BIT(PCI_INTERRUPT_ENABLE) |
+ BIT(PCI_MASTER_ABORT_RECEIVED_INTERRUPT_ENABLE) |
+ BIT(PCI_TARGET_ABORT_RECEIVED_INTERRUPT_ENABLE) |
+ BIT(PCI_RETRY_ABORT_INTERRUPT_ENABLE) |
+ BIT(VBUS_INTERRUPT_ENABLE) |
+ BIT(ROOT_PORT_RESET_INTERRUPT_ENABLE) |
+ BIT(SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE),
+ &dev->regs->pciirqenb1);
+
+ /* don't leave any writes posted */
+ (void) readl(&dev->usb->usbctl);
+}
+
+static void ep0_start_338x(struct net2280 *dev)
+{
+ u32 fsmvalue;
+
+ fsmvalue = get_idx_reg(dev->regs, SCRATCH) &
+ (0xf << DEFECT7374_FSM_FIELD);
+
+ if (fsmvalue != DEFECT7374_FSM_SS_CONTROL_READ)
+ ep_info(dev, "%s: Defect 7374 FsmValue %08x\n", __func__,
+ fsmvalue);
+ else
+ writel(BIT(CLEAR_NAK_OUT_PACKETS_MODE) |
+ BIT(SET_EP_HIDE_STATUS_PHASE),
+ &dev->epregs[0].ep_rsp);
+
+ /*
+ * hardware optionally handles a bunch of standard requests
+ * that the API hides from drivers anyway. have it do so.
+ * endpoint status/features are handled in software, to
+ * help pass tests for some dubious behavior.
+ */
+ writel(BIT(SET_ISOCHRONOUS_DELAY) |
+ BIT(SET_SEL) |
+ BIT(SET_TEST_MODE) |
+ BIT(SET_ADDRESS) |
+ BIT(GET_INTERFACE_STATUS) |
+ BIT(GET_DEVICE_STATUS),
+ &dev->usb->stdrsp);
+ dev->wakeup_enable = 1;
+ writel(BIT(USB_ROOT_PORT_WAKEUP_ENABLE) |
+ (dev->softconnect << USB_DETECT_ENABLE) |
+ BIT(DEVICE_REMOTE_WAKEUP_ENABLE),
+ &dev->usb->usbctl);
+
+ /* enable irqs so we can see ep0 and general operation */
+ writel(BIT(SETUP_PACKET_INTERRUPT_ENABLE) |
+ BIT(ENDPOINT_0_INTERRUPT_ENABLE),
+ &dev->regs->pciirqenb0);
+ writel(BIT(PCI_INTERRUPT_ENABLE) |
+ BIT(ROOT_PORT_RESET_INTERRUPT_ENABLE) |
+ BIT(SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE) |
+ BIT(VBUS_INTERRUPT_ENABLE),
+ &dev->regs->pciirqenb1);
+
+ /* don't leave any writes posted */
+ (void)readl(&dev->usb->usbctl);
+}
+
+static void ep0_start(struct net2280 *dev)
+{
+ if (dev->quirks & PLX_LEGACY)
+ return ep0_start_228x(dev);
+ return ep0_start_338x(dev);
+}
+
+/* when a driver is successfully registered, it will receive
+ * control requests including set_configuration(), which enables
+ * non-control requests. then usb traffic follows until a
+ * disconnect is reported. then a host may connect again, or
+ * the driver might get unbound.
+ */
+static int net2280_start(struct usb_gadget *_gadget,
+ struct usb_gadget_driver *driver)
+{
+ struct net2280 *dev;
+ int retval;
+ unsigned i;
+
+ /* insist on high speed support from the driver, since
+ * (dev->usb->xcvrdiag & FORCE_FULL_SPEED_MODE)
+ * "must not be used in normal operation"
+ */
+ if (!driver || driver->max_speed < USB_SPEED_HIGH ||
+ !driver->setup)
+ return -EINVAL;
+
+ dev = container_of(_gadget, struct net2280, gadget);
+
+ for (i = 0; i < dev->n_ep; i++)
+ dev->ep[i].irqs = 0;
+
+ /* hook up the driver ... */
+ dev->softconnect = 1;
+ driver->driver.bus = NULL;
+ dev->driver = driver;
+
+ retval = device_create_file(&dev->pdev->dev, &dev_attr_function);
+ if (retval)
+ goto err_unbind;
+ retval = device_create_file(&dev->pdev->dev, &dev_attr_queues);
+ if (retval)
+ goto err_func;
+
+ /* Enable force-full-speed testing mode, if desired */
+ if (full_speed && (dev->quirks & PLX_LEGACY))
+ writel(BIT(FORCE_FULL_SPEED_MODE), &dev->usb->xcvrdiag);
+
+ /* ... then enable host detection and ep0; and we're ready
+ * for set_configuration as well as eventual disconnect.
+ */
+ net2280_led_active(dev, 1);
+
+ if (dev->quirks & PLX_SUPERSPEED)
+ defect7374_enable_data_eps_zero(dev);
+
+ ep0_start(dev);
+
+ ep_dbg(dev, "%s ready, usbctl %08x stdrsp %08x\n",
+ driver->driver.name,
+ readl(&dev->usb->usbctl),
+ readl(&dev->usb->stdrsp));
+
+ /* pci writes may still be posted */
+ return 0;
+
+err_func:
+ device_remove_file(&dev->pdev->dev, &dev_attr_function);
+err_unbind:
+ dev->driver = NULL;
+ return retval;
+}
+
+static void stop_activity(struct net2280 *dev, struct usb_gadget_driver *driver)
+{
+ int i;
+
+ /* don't disconnect if it's not connected */
+ if (dev->gadget.speed == USB_SPEED_UNKNOWN)
+ driver = NULL;
+
+ /* stop hardware; prevent new request submissions;
+ * and kill any outstanding requests.
+ */
+ usb_reset(dev);
+ for (i = 0; i < dev->n_ep; i++)
+ nuke(&dev->ep[i]);
+
+ /* report disconnect; the driver is already quiesced */
+ if (driver) {
+ spin_unlock(&dev->lock);
+ driver->disconnect(&dev->gadget);
+ spin_lock(&dev->lock);
+ }
+
+ usb_reinit(dev);
+}
+
+static int net2280_stop(struct usb_gadget *_gadget,
+ struct usb_gadget_driver *driver)
+{
+ struct net2280 *dev;
+ unsigned long flags;
+
+ dev = container_of(_gadget, struct net2280, gadget);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ stop_activity(dev, driver);
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ dev->driver = NULL;
+
+ net2280_led_active(dev, 0);
+
+ /* Disable full-speed test mode */
+ if (dev->quirks & PLX_LEGACY)
+ writel(0, &dev->usb->xcvrdiag);
+
+ device_remove_file(&dev->pdev->dev, &dev_attr_function);
+ device_remove_file(&dev->pdev->dev, &dev_attr_queues);
+
+ ep_dbg(dev, "unregistered driver '%s'\n",
+ driver ? driver->driver.name : "");
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* handle ep0, ep-e, ep-f with 64 byte packets: packet per irq.
+ * also works for dma-capable endpoints, in pio mode or just
+ * to manually advance the queue after short OUT transfers.
+ */
+static void handle_ep_small(struct net2280_ep *ep)
+{
+ struct net2280_request *req;
+ u32 t;
+ /* 0 error, 1 mid-data, 2 done */
+ int mode = 1;
+
+ if (!list_empty(&ep->queue))
+ req = list_entry(ep->queue.next,
+ struct net2280_request, queue);
+ else
+ req = NULL;
+
+ /* ack all, and handle what we care about */
+ t = readl(&ep->regs->ep_stat);
+ ep->irqs++;
+#if 0
+ ep_vdbg(ep->dev, "%s ack ep_stat %08x, req %p\n",
+ ep->ep.name, t, req ? &req->req : 0);
+#endif
+ if (!ep->is_in || (ep->dev->quirks & PLX_2280))
+ writel(t & ~BIT(NAK_OUT_PACKETS), &ep->regs->ep_stat);
+ else
+ /* Added for 2282 */
+ writel(t, &ep->regs->ep_stat);
+
+ /* for ep0, monitor token irqs to catch data stage length errors
+ * and to synchronize on status.
+ *
+ * also, to defer reporting of protocol stalls ... here's where
+ * data or status first appears, handling stalls here should never
+ * cause trouble on the host side..
+ *
+ * control requests could be slightly faster without token synch for
+ * status, but status can jam up that way.
+ */
+ if (unlikely(ep->num == 0)) {
+ if (ep->is_in) {
+ /* status; stop NAKing */
+ if (t & BIT(DATA_OUT_PING_TOKEN_INTERRUPT)) {
+ if (ep->dev->protocol_stall) {
+ ep->stopped = 1;
+ set_halt(ep);
+ }
+ if (!req)
+ allow_status(ep);
+ mode = 2;
+ /* reply to extra IN data tokens with a zlp */
+ } else if (t & BIT(DATA_IN_TOKEN_INTERRUPT)) {
+ if (ep->dev->protocol_stall) {
+ ep->stopped = 1;
+ set_halt(ep);
+ mode = 2;
+ } else if (ep->responded &&
+ !req && !ep->stopped)
+ write_fifo(ep, NULL);
+ }
+ } else {
+ /* status; stop NAKing */
+ if (t & BIT(DATA_IN_TOKEN_INTERRUPT)) {
+ if (ep->dev->protocol_stall) {
+ ep->stopped = 1;
+ set_halt(ep);
+ }
+ mode = 2;
+ /* an extra OUT token is an error */
+ } else if (((t & BIT(DATA_OUT_PING_TOKEN_INTERRUPT)) &&
+ req &&
+ req->req.actual == req->req.length) ||
+ (ep->responded && !req)) {
+ ep->dev->protocol_stall = 1;
+ set_halt(ep);
+ ep->stopped = 1;
+ if (req)
+ done(ep, req, -EOVERFLOW);
+ req = NULL;
+ }
+ }
+ }
+
+ if (unlikely(!req))
+ return;
+
+ /* manual DMA queue advance after short OUT */
+ if (likely(ep->dma)) {
+ if (t & BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT)) {
+ u32 count;
+ int stopped = ep->stopped;
+
+ /* TRANSFERRED works around OUT_DONE erratum 0112.
+ * we expect (N <= maxpacket) bytes; host wrote M.
+ * iff (M < N) we won't ever see a DMA interrupt.
+ */
+ ep->stopped = 1;
+ for (count = 0; ; t = readl(&ep->regs->ep_stat)) {
+
+ /* any preceding dma transfers must finish.
+ * dma handles (M >= N), may empty the queue
+ */
+ scan_dma_completions(ep);
+ if (unlikely(list_empty(&ep->queue) ||
+ ep->out_overflow)) {
+ req = NULL;
+ break;
+ }
+ req = list_entry(ep->queue.next,
+ struct net2280_request, queue);
+
+ /* here either (M < N), a "real" short rx;
+ * or (M == N) and the queue didn't empty
+ */
+ if (likely(t & BIT(FIFO_EMPTY))) {
+ count = readl(&ep->dma->dmacount);
+ count &= DMA_BYTE_COUNT_MASK;
+ if (readl(&ep->dma->dmadesc)
+ != req->td_dma)
+ req = NULL;
+ break;
+ }
+ udelay(1);
+ }
+
+ /* stop DMA, leave ep NAKing */
+ writel(BIT(DMA_ABORT), &ep->dma->dmastat);
+ spin_stop_dma(ep->dma);
+
+ if (likely(req)) {
+ req->td->dmacount = 0;
+ t = readl(&ep->regs->ep_avail);
+ dma_done(ep, req, count,
+ (ep->out_overflow || t)
+ ? -EOVERFLOW : 0);
+ }
+
+ /* also flush to prevent erratum 0106 trouble */
+ if (unlikely(ep->out_overflow ||
+ (ep->dev->chiprev == 0x0100 &&
+ ep->dev->gadget.speed
+ == USB_SPEED_FULL))) {
+ out_flush(ep);
+ ep->out_overflow = 0;
+ }
+
+ /* (re)start dma if needed, stop NAKing */
+ ep->stopped = stopped;
+ if (!list_empty(&ep->queue))
+ restart_dma(ep);
+ } else
+ ep_dbg(ep->dev, "%s dma ep_stat %08x ??\n",
+ ep->ep.name, t);
+ return;
+
+ /* data packet(s) received (in the fifo, OUT) */
+ } else if (t & BIT(DATA_PACKET_RECEIVED_INTERRUPT)) {
+ if (read_fifo(ep, req) && ep->num != 0)
+ mode = 2;
+
+ /* data packet(s) transmitted (IN) */
+ } else if (t & BIT(DATA_PACKET_TRANSMITTED_INTERRUPT)) {
+ unsigned len;
+
+ len = req->req.length - req->req.actual;
+ if (len > ep->ep.maxpacket)
+ len = ep->ep.maxpacket;
+ req->req.actual += len;
+
+ /* if we wrote it all, we're usually done */
+ /* send zlps until the status stage */
+ if ((req->req.actual == req->req.length) &&
+ (!req->req.zero || len != ep->ep.maxpacket) && ep->num)
+ mode = 2;
+
+ /* there was nothing to do ... */
+ } else if (mode == 1)
+ return;
+
+ /* done */
+ if (mode == 2) {
+ /* stream endpoints often resubmit/unlink in completion */
+ done(ep, req, 0);
+
+ /* maybe advance queue to next request */
+ if (ep->num == 0) {
+ /* NOTE: net2280 could let gadget driver start the
+ * status stage later. since not all controllers let
+ * them control that, the api doesn't (yet) allow it.
+ */
+ if (!ep->stopped)
+ allow_status(ep);
+ req = NULL;
+ } else {
+ if (!list_empty(&ep->queue) && !ep->stopped)
+ req = list_entry(ep->queue.next,
+ struct net2280_request, queue);
+ else
+ req = NULL;
+ if (req && !ep->is_in)
+ stop_out_naking(ep);
+ }
+ }
+
+ /* is there a buffer for the next packet?
+ * for best streaming performance, make sure there is one.
+ */
+ if (req && !ep->stopped) {
+
+ /* load IN fifo with next packet (may be zlp) */
+ if (t & BIT(DATA_PACKET_TRANSMITTED_INTERRUPT))
+ write_fifo(ep, &req->req);
+ }
+}
+
+static struct net2280_ep *get_ep_by_addr(struct net2280 *dev, u16 wIndex)
+{
+ struct net2280_ep *ep;
+
+ if ((wIndex & USB_ENDPOINT_NUMBER_MASK) == 0)
+ return &dev->ep[0];
+ list_for_each_entry(ep, &dev->gadget.ep_list, ep.ep_list) {
+ u8 bEndpointAddress;
+
+ if (!ep->desc)
+ continue;
+ bEndpointAddress = ep->desc->bEndpointAddress;
+ if ((wIndex ^ bEndpointAddress) & USB_DIR_IN)
+ continue;
+ if ((wIndex & 0x0f) == (bEndpointAddress & 0x0f))
+ return ep;
+ }
+ return NULL;
+}
+
+static void defect7374_workaround(struct net2280 *dev, struct usb_ctrlrequest r)
+{
+ u32 scratch, fsmvalue;
+ u32 ack_wait_timeout, state;
+
+ /* Workaround for Defect 7374 (U1/U2 erroneously rejected): */
+ scratch = get_idx_reg(dev->regs, SCRATCH);
+ fsmvalue = scratch & (0xf << DEFECT7374_FSM_FIELD);
+ scratch &= ~(0xf << DEFECT7374_FSM_FIELD);
+
+ if (!((fsmvalue == DEFECT7374_FSM_WAITING_FOR_CONTROL_READ) &&
+ (r.bRequestType & USB_DIR_IN)))
+ return;
+
+ /* This is the first Control Read for this connection: */
+ if (!(readl(&dev->usb->usbstat) & BIT(SUPER_SPEED_MODE))) {
+ /*
+ * Connection is NOT SS:
+ * - Connection must be FS or HS.
+ * - This FSM state should allow workaround software to
+ * run after the next USB connection.
+ */
+ scratch |= DEFECT7374_FSM_NON_SS_CONTROL_READ;
+ goto restore_data_eps;
+ }
+
+ /* Connection is SS: */
+ for (ack_wait_timeout = 0;
+ ack_wait_timeout < DEFECT_7374_NUMBEROF_MAX_WAIT_LOOPS;
+ ack_wait_timeout++) {
+
+ state = readl(&dev->plregs->pl_ep_status_1)
+ & (0xff << STATE);
+ if ((state >= (ACK_GOOD_NORMAL << STATE)) &&
+ (state <= (ACK_GOOD_MORE_ACKS_TO_COME << STATE))) {
+ scratch |= DEFECT7374_FSM_SS_CONTROL_READ;
+ break;
+ }
+
+ /*
+ * We have not yet received host's Data Phase ACK
+ * - Wait and try again.
+ */
+ udelay(DEFECT_7374_PROCESSOR_WAIT_TIME);
+
+ continue;
+ }
+
+
+ if (ack_wait_timeout >= DEFECT_7374_NUMBEROF_MAX_WAIT_LOOPS) {
+ ep_err(dev, "FAIL: Defect 7374 workaround waited but failed "
+ "to detect SS host's data phase ACK.");
+ ep_err(dev, "PL_EP_STATUS_1(23:16):.Expected from 0x11 to 0x16"
+ "got 0x%2.2x.\n", state >> STATE);
+ } else {
+ ep_warn(dev, "INFO: Defect 7374 workaround waited about\n"
+ "%duSec for Control Read Data Phase ACK\n",
+ DEFECT_7374_PROCESSOR_WAIT_TIME * ack_wait_timeout);
+ }
+
+restore_data_eps:
+ /*
+ * Restore data EPs to their pre-workaround settings (disabled,
+ * initialized, and other details).
+ */
+ defect7374_disable_data_eps(dev);
+
+ set_idx_reg(dev->regs, SCRATCH, scratch);
+
+ return;
+}
+
+static void ep_stall(struct net2280_ep *ep, int stall)
+{
+ struct net2280 *dev = ep->dev;
+ u32 val;
+ static const u32 ep_pl[9] = { 0, 3, 4, 7, 8, 2, 5, 6, 9 };
+
+ if (stall) {
+ writel(BIT(SET_ENDPOINT_HALT) |
+ /* BIT(SET_NAK_PACKETS) | */
+ BIT(CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE),
+ &ep->regs->ep_rsp);
+ ep->is_halt = 1;
+ } else {
+ if (dev->gadget.speed == USB_SPEED_SUPER) {
+ /*
+ * Workaround for SS SeqNum not cleared via
+ * Endpoint Halt (Clear) bit. select endpoint
+ */
+ val = readl(&dev->plregs->pl_ep_ctrl);
+ val = (val & ~0x1f) | ep_pl[ep->num];
+ writel(val, &dev->plregs->pl_ep_ctrl);
+
+ val |= BIT(SEQUENCE_NUMBER_RESET);
+ writel(val, &dev->plregs->pl_ep_ctrl);
+ }
+ val = readl(&ep->regs->ep_rsp);
+ val |= BIT(CLEAR_ENDPOINT_HALT) |
+ BIT(CLEAR_ENDPOINT_TOGGLE);
+ writel(val,
+ /* | BIT(CLEAR_NAK_PACKETS),*/
+ &ep->regs->ep_rsp);
+ ep->is_halt = 0;
+ val = readl(&ep->regs->ep_rsp);
+ }
+}
+
+static void ep_stdrsp(struct net2280_ep *ep, int value, int wedged)
+{
+ /* set/clear, then synch memory views with the device */
+ if (value) {
+ ep->stopped = 1;
+ if (ep->num == 0)
+ ep->dev->protocol_stall = 1;
+ else {
+ if (ep->dma)
+ ep_stop_dma(ep);
+ ep_stall(ep, true);
+ }
+
+ if (wedged)
+ ep->wedged = 1;
+ } else {
+ ep->stopped = 0;
+ ep->wedged = 0;
+
+ ep_stall(ep, false);
+
+ /* Flush the queue */
+ if (!list_empty(&ep->queue)) {
+ struct net2280_request *req =
+ list_entry(ep->queue.next, struct net2280_request,
+ queue);
+ if (ep->dma)
+ resume_dma(ep);
+ else {
+ if (ep->is_in)
+ write_fifo(ep, &req->req);
+ else {
+ if (read_fifo(ep, req))
+ done(ep, req, 0);
+ }
+ }
+ }
+ }
+}
+
+static void handle_stat0_irqs_superspeed(struct net2280 *dev,
+ struct net2280_ep *ep, struct usb_ctrlrequest r)
+{
+ int tmp = 0;
+
+#define w_value le16_to_cpu(r.wValue)
+#define w_index le16_to_cpu(r.wIndex)
+#define w_length le16_to_cpu(r.wLength)
+
+ switch (r.bRequest) {
+ struct net2280_ep *e;
+ u16 status;
+
+ case USB_REQ_SET_CONFIGURATION:
+ dev->addressed_state = !w_value;
+ goto usb3_delegate;
+
+ case USB_REQ_GET_STATUS:
+ switch (r.bRequestType) {
+ case (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE):
+ status = dev->wakeup_enable ? 0x02 : 0x00;
+ if (dev->selfpowered)
+ status |= BIT(0);
+ status |= (dev->u1_enable << 2 | dev->u2_enable << 3 |
+ dev->ltm_enable << 4);
+ writel(0, &dev->epregs[0].ep_irqenb);
+ set_fifo_bytecount(ep, sizeof(status));
+ writel((__force u32) status, &dev->epregs[0].ep_data);
+ allow_status_338x(ep);
+ break;
+
+ case (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_ENDPOINT):
+ e = get_ep_by_addr(dev, w_index);
+ if (!e)
+ goto do_stall3;
+ status = readl(&e->regs->ep_rsp) &
+ BIT(CLEAR_ENDPOINT_HALT);
+ writel(0, &dev->epregs[0].ep_irqenb);
+ set_fifo_bytecount(ep, sizeof(status));
+ writel((__force u32) status, &dev->epregs[0].ep_data);
+ allow_status_338x(ep);
+ break;
+
+ default:
+ goto usb3_delegate;
+ }
+ break;
+
+ case USB_REQ_CLEAR_FEATURE:
+ switch (r.bRequestType) {
+ case (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE):
+ if (!dev->addressed_state) {
+ switch (w_value) {
+ case USB_DEVICE_U1_ENABLE:
+ dev->u1_enable = 0;
+ writel(readl(&dev->usb_ext->usbctl2) &
+ ~BIT(U1_ENABLE),
+ &dev->usb_ext->usbctl2);
+ allow_status_338x(ep);
+ goto next_endpoints3;
+
+ case USB_DEVICE_U2_ENABLE:
+ dev->u2_enable = 0;
+ writel(readl(&dev->usb_ext->usbctl2) &
+ ~BIT(U2_ENABLE),
+ &dev->usb_ext->usbctl2);
+ allow_status_338x(ep);
+ goto next_endpoints3;
+
+ case USB_DEVICE_LTM_ENABLE:
+ dev->ltm_enable = 0;
+ writel(readl(&dev->usb_ext->usbctl2) &
+ ~BIT(LTM_ENABLE),
+ &dev->usb_ext->usbctl2);
+ allow_status_338x(ep);
+ goto next_endpoints3;
+
+ default:
+ break;
+ }
+ }
+ if (w_value == USB_DEVICE_REMOTE_WAKEUP) {
+ dev->wakeup_enable = 0;
+ writel(readl(&dev->usb->usbctl) &
+ ~BIT(DEVICE_REMOTE_WAKEUP_ENABLE),
+ &dev->usb->usbctl);
+ allow_status_338x(ep);
+ break;
+ }
+ goto usb3_delegate;
+
+ case (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_ENDPOINT):
+ e = get_ep_by_addr(dev, w_index);
+ if (!e)
+ goto do_stall3;
+ if (w_value != USB_ENDPOINT_HALT)
+ goto do_stall3;
+ ep_vdbg(dev, "%s clear halt\n", e->ep.name);
+ ep_stall(e, false);
+ if (!list_empty(&e->queue) && e->td_dma)
+ restart_dma(e);
+ allow_status(ep);
+ ep->stopped = 1;
+ break;
+
+ default:
+ goto usb3_delegate;
+ }
+ break;
+ case USB_REQ_SET_FEATURE:
+ switch (r.bRequestType) {
+ case (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE):
+ if (!dev->addressed_state) {
+ switch (w_value) {
+ case USB_DEVICE_U1_ENABLE:
+ dev->u1_enable = 1;
+ writel(readl(&dev->usb_ext->usbctl2) |
+ BIT(U1_ENABLE),
+ &dev->usb_ext->usbctl2);
+ allow_status_338x(ep);
+ goto next_endpoints3;
+
+ case USB_DEVICE_U2_ENABLE:
+ dev->u2_enable = 1;
+ writel(readl(&dev->usb_ext->usbctl2) |
+ BIT(U2_ENABLE),
+ &dev->usb_ext->usbctl2);
+ allow_status_338x(ep);
+ goto next_endpoints3;
+
+ case USB_DEVICE_LTM_ENABLE:
+ dev->ltm_enable = 1;
+ writel(readl(&dev->usb_ext->usbctl2) |
+ BIT(LTM_ENABLE),
+ &dev->usb_ext->usbctl2);
+ allow_status_338x(ep);
+ goto next_endpoints3;
+ default:
+ break;
+ }
+ }
+
+ if (w_value == USB_DEVICE_REMOTE_WAKEUP) {
+ dev->wakeup_enable = 1;
+ writel(readl(&dev->usb->usbctl) |
+ BIT(DEVICE_REMOTE_WAKEUP_ENABLE),
+ &dev->usb->usbctl);
+ allow_status_338x(ep);
+ break;
+ }
+ goto usb3_delegate;
+
+ case (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_ENDPOINT):
+ e = get_ep_by_addr(dev, w_index);
+ if (!e || (w_value != USB_ENDPOINT_HALT))
+ goto do_stall3;
+ ep_stdrsp(e, true, false);
+ allow_status_338x(ep);
+ break;
+
+ default:
+ goto usb3_delegate;
+ }
+
+ break;
+ default:
+
+usb3_delegate:
+ ep_vdbg(dev, "setup %02x.%02x v%04x i%04x l%04x ep_cfg %08x\n",
+ r.bRequestType, r.bRequest,
+ w_value, w_index, w_length,
+ readl(&ep->cfg->ep_cfg));
+
+ ep->responded = 0;
+ spin_unlock(&dev->lock);
+ tmp = dev->driver->setup(&dev->gadget, &r);
+ spin_lock(&dev->lock);
+ }
+do_stall3:
+ if (tmp < 0) {
+ ep_vdbg(dev, "req %02x.%02x protocol STALL; stat %d\n",
+ r.bRequestType, r.bRequest, tmp);
+ dev->protocol_stall = 1;
+ /* TD 9.9 Halt Endpoint test. TD 9.22 Set feature test */
+ ep_stall(ep, true);
+ }
+
+next_endpoints3:
+
+#undef w_value
+#undef w_index
+#undef w_length
+
+ return;
+}
+
+static void handle_stat0_irqs(struct net2280 *dev, u32 stat)
+{
+ struct net2280_ep *ep;
+ u32 num, scratch;
+
+ /* most of these don't need individual acks */
+ stat &= ~BIT(INTA_ASSERTED);
+ if (!stat)
+ return;
+ /* ep_dbg(dev, "irqstat0 %04x\n", stat); */
+
+ /* starting a control request? */
+ if (unlikely(stat & BIT(SETUP_PACKET_INTERRUPT))) {
+ union {
+ u32 raw[2];
+ struct usb_ctrlrequest r;
+ } u;
+ int tmp;
+ struct net2280_request *req;
+
+ if (dev->gadget.speed == USB_SPEED_UNKNOWN) {
+ u32 val = readl(&dev->usb->usbstat);
+ if (val & BIT(SUPER_SPEED)) {
+ dev->gadget.speed = USB_SPEED_SUPER;
+ usb_ep_set_maxpacket_limit(&dev->ep[0].ep,
+ EP0_SS_MAX_PACKET_SIZE);
+ } else if (val & BIT(HIGH_SPEED)) {
+ dev->gadget.speed = USB_SPEED_HIGH;
+ usb_ep_set_maxpacket_limit(&dev->ep[0].ep,
+ EP0_HS_MAX_PACKET_SIZE);
+ } else {
+ dev->gadget.speed = USB_SPEED_FULL;
+ usb_ep_set_maxpacket_limit(&dev->ep[0].ep,
+ EP0_HS_MAX_PACKET_SIZE);
+ }
+ net2280_led_speed(dev, dev->gadget.speed);
+ ep_dbg(dev, "%s\n",
+ usb_speed_string(dev->gadget.speed));
+ }
+
+ ep = &dev->ep[0];
+ ep->irqs++;
+
+ /* make sure any leftover request state is cleared */
+ stat &= ~BIT(ENDPOINT_0_INTERRUPT);
+ while (!list_empty(&ep->queue)) {
+ req = list_entry(ep->queue.next,
+ struct net2280_request, queue);
+ done(ep, req, (req->req.actual == req->req.length)
+ ? 0 : -EPROTO);
+ }
+ ep->stopped = 0;
+ dev->protocol_stall = 0;
+ if (dev->quirks & PLX_SUPERSPEED)
+ ep->is_halt = 0;
+ else{
+ if (ep->dev->quirks & PLX_2280)
+ tmp = BIT(FIFO_OVERFLOW) |
+ BIT(FIFO_UNDERFLOW);
+ else
+ tmp = 0;
+
+ writel(tmp | BIT(TIMEOUT) |
+ BIT(USB_STALL_SENT) |
+ BIT(USB_IN_NAK_SENT) |
+ BIT(USB_IN_ACK_RCVD) |
+ BIT(USB_OUT_PING_NAK_SENT) |
+ BIT(USB_OUT_ACK_SENT) |
+ BIT(SHORT_PACKET_OUT_DONE_INTERRUPT) |
+ BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT) |
+ BIT(DATA_PACKET_RECEIVED_INTERRUPT) |
+ BIT(DATA_PACKET_TRANSMITTED_INTERRUPT) |
+ BIT(DATA_OUT_PING_TOKEN_INTERRUPT) |
+ BIT(DATA_IN_TOKEN_INTERRUPT),
+ &ep->regs->ep_stat);
+ }
+ u.raw[0] = readl(&dev->usb->setup0123);
+ u.raw[1] = readl(&dev->usb->setup4567);
+
+ cpu_to_le32s(&u.raw[0]);
+ cpu_to_le32s(&u.raw[1]);
+
+ if (dev->quirks & PLX_SUPERSPEED)
+ defect7374_workaround(dev, u.r);
+
+ tmp = 0;
+
+#define w_value le16_to_cpu(u.r.wValue)
+#define w_index le16_to_cpu(u.r.wIndex)
+#define w_length le16_to_cpu(u.r.wLength)
+
+ /* ack the irq */
+ writel(BIT(SETUP_PACKET_INTERRUPT), &dev->regs->irqstat0);
+ stat ^= BIT(SETUP_PACKET_INTERRUPT);
+
+ /* watch control traffic at the token level, and force
+ * synchronization before letting the status stage happen.
+ * FIXME ignore tokens we'll NAK, until driver responds.
+ * that'll mean a lot less irqs for some drivers.
+ */
+ ep->is_in = (u.r.bRequestType & USB_DIR_IN) != 0;
+ if (ep->is_in) {
+ scratch = BIT(DATA_PACKET_TRANSMITTED_INTERRUPT) |
+ BIT(DATA_OUT_PING_TOKEN_INTERRUPT) |
+ BIT(DATA_IN_TOKEN_INTERRUPT);
+ stop_out_naking(ep);
+ } else
+ scratch = BIT(DATA_PACKET_RECEIVED_INTERRUPT) |
+ BIT(DATA_OUT_PING_TOKEN_INTERRUPT) |
+ BIT(DATA_IN_TOKEN_INTERRUPT);
+ writel(scratch, &dev->epregs[0].ep_irqenb);
+
+ /* we made the hardware handle most lowlevel requests;
+ * everything else goes uplevel to the gadget code.
+ */
+ ep->responded = 1;
+
+ if (dev->gadget.speed == USB_SPEED_SUPER) {
+ handle_stat0_irqs_superspeed(dev, ep, u.r);
+ goto next_endpoints;
+ }
+
+ switch (u.r.bRequest) {
+ case USB_REQ_GET_STATUS: {
+ struct net2280_ep *e;
+ __le32 status;
+
+ /* hw handles device and interface status */
+ if (u.r.bRequestType != (USB_DIR_IN|USB_RECIP_ENDPOINT))
+ goto delegate;
+ e = get_ep_by_addr(dev, w_index);
+ if (!e || w_length > 2)
+ goto do_stall;
+
+ if (readl(&e->regs->ep_rsp) & BIT(SET_ENDPOINT_HALT))
+ status = cpu_to_le32(1);
+ else
+ status = cpu_to_le32(0);
+
+ /* don't bother with a request object! */
+ writel(0, &dev->epregs[0].ep_irqenb);
+ set_fifo_bytecount(ep, w_length);
+ writel((__force u32)status, &dev->epregs[0].ep_data);
+ allow_status(ep);
+ ep_vdbg(dev, "%s stat %02x\n", ep->ep.name, status);
+ goto next_endpoints;
+ }
+ break;
+ case USB_REQ_CLEAR_FEATURE: {
+ struct net2280_ep *e;
+
+ /* hw handles device features */
+ if (u.r.bRequestType != USB_RECIP_ENDPOINT)
+ goto delegate;
+ if (w_value != USB_ENDPOINT_HALT || w_length != 0)
+ goto do_stall;
+ e = get_ep_by_addr(dev, w_index);
+ if (!e)
+ goto do_stall;
+ if (e->wedged) {
+ ep_vdbg(dev, "%s wedged, halt not cleared\n",
+ ep->ep.name);
+ } else {
+ ep_vdbg(dev, "%s clear halt\n", e->ep.name);
+ clear_halt(e);
+ if ((ep->dev->quirks & PLX_SUPERSPEED) &&
+ !list_empty(&e->queue) && e->td_dma)
+ restart_dma(e);
+ }
+ allow_status(ep);
+ goto next_endpoints;
+ }
+ break;
+ case USB_REQ_SET_FEATURE: {
+ struct net2280_ep *e;
+
+ /* hw handles device features */
+ if (u.r.bRequestType != USB_RECIP_ENDPOINT)
+ goto delegate;
+ if (w_value != USB_ENDPOINT_HALT || w_length != 0)
+ goto do_stall;
+ e = get_ep_by_addr(dev, w_index);
+ if (!e)
+ goto do_stall;
+ if (e->ep.name == ep0name)
+ goto do_stall;
+ set_halt(e);
+ if ((dev->quirks & PLX_SUPERSPEED) && e->dma)
+ abort_dma(e);
+ allow_status(ep);
+ ep_vdbg(dev, "%s set halt\n", ep->ep.name);
+ goto next_endpoints;
+ }
+ break;
+ default:
+delegate:
+ ep_vdbg(dev, "setup %02x.%02x v%04x i%04x l%04x "
+ "ep_cfg %08x\n",
+ u.r.bRequestType, u.r.bRequest,
+ w_value, w_index, w_length,
+ readl(&ep->cfg->ep_cfg));
+ ep->responded = 0;
+ spin_unlock(&dev->lock);
+ tmp = dev->driver->setup(&dev->gadget, &u.r);
+ spin_lock(&dev->lock);
+ }
+
+ /* stall ep0 on error */
+ if (tmp < 0) {
+do_stall:
+ ep_vdbg(dev, "req %02x.%02x protocol STALL; stat %d\n",
+ u.r.bRequestType, u.r.bRequest, tmp);
+ dev->protocol_stall = 1;
+ }
+
+ /* some in/out token irq should follow; maybe stall then.
+ * driver must queue a request (even zlp) or halt ep0
+ * before the host times out.
+ */
+ }
+
+#undef w_value
+#undef w_index
+#undef w_length
+
+next_endpoints:
+ /* endpoint data irq ? */
+ scratch = stat & 0x7f;
+ stat &= ~0x7f;
+ for (num = 0; scratch; num++) {
+ u32 t;
+
+ /* do this endpoint's FIFO and queue need tending? */
+ t = BIT(num);
+ if ((scratch & t) == 0)
+ continue;
+ scratch ^= t;
+
+ ep = &dev->ep[num];
+ handle_ep_small(ep);
+ }
+
+ if (stat)
+ ep_dbg(dev, "unhandled irqstat0 %08x\n", stat);
+}
+
+#define DMA_INTERRUPTS (BIT(DMA_D_INTERRUPT) | \
+ BIT(DMA_C_INTERRUPT) | \
+ BIT(DMA_B_INTERRUPT) | \
+ BIT(DMA_A_INTERRUPT))
+#define PCI_ERROR_INTERRUPTS ( \
+ BIT(PCI_MASTER_ABORT_RECEIVED_INTERRUPT) | \
+ BIT(PCI_TARGET_ABORT_RECEIVED_INTERRUPT) | \
+ BIT(PCI_RETRY_ABORT_INTERRUPT))
+
+static void handle_stat1_irqs(struct net2280 *dev, u32 stat)
+{
+ struct net2280_ep *ep;
+ u32 tmp, num, mask, scratch;
+
+ /* after disconnect there's nothing else to do! */
+ tmp = BIT(VBUS_INTERRUPT) | BIT(ROOT_PORT_RESET_INTERRUPT);
+ mask = BIT(SUPER_SPEED) | BIT(HIGH_SPEED) | BIT(FULL_SPEED);
+
+ /* VBUS disconnect is indicated by VBUS_PIN and VBUS_INTERRUPT set.
+ * Root Port Reset is indicated by ROOT_PORT_RESET_INTERRUPT set and
+ * both HIGH_SPEED and FULL_SPEED clear (as ROOT_PORT_RESET_INTERRUPT
+ * only indicates a change in the reset state).
+ */
+ if (stat & tmp) {
+ writel(tmp, &dev->regs->irqstat1);
+ if ((((stat & BIT(ROOT_PORT_RESET_INTERRUPT)) &&
+ (readl(&dev->usb->usbstat) & mask)) ||
+ ((readl(&dev->usb->usbctl) &
+ BIT(VBUS_PIN)) == 0)) &&
+ (dev->gadget.speed != USB_SPEED_UNKNOWN)) {
+ ep_dbg(dev, "disconnect %s\n",
+ dev->driver->driver.name);
+ stop_activity(dev, dev->driver);
+ ep0_start(dev);
+ return;
+ }
+ stat &= ~tmp;
+
+ /* vBUS can bounce ... one of many reasons to ignore the
+ * notion of hotplug events on bus connect/disconnect!
+ */
+ if (!stat)
+ return;
+ }
+
+ /* NOTE: chip stays in PCI D0 state for now, but it could
+ * enter D1 to save more power
+ */
+ tmp = BIT(SUSPEND_REQUEST_CHANGE_INTERRUPT);
+ if (stat & tmp) {
+ writel(tmp, &dev->regs->irqstat1);
+ if (stat & BIT(SUSPEND_REQUEST_INTERRUPT)) {
+ if (dev->driver->suspend)
+ dev->driver->suspend(&dev->gadget);
+ if (!enable_suspend)
+ stat &= ~BIT(SUSPEND_REQUEST_INTERRUPT);
+ } else {
+ if (dev->driver->resume)
+ dev->driver->resume(&dev->gadget);
+ /* at high speed, note erratum 0133 */
+ }
+ stat &= ~tmp;
+ }
+
+ /* clear any other status/irqs */
+ if (stat)
+ writel(stat, &dev->regs->irqstat1);
+
+ /* some status we can just ignore */
+ if (dev->quirks & PLX_2280)
+ stat &= ~(BIT(CONTROL_STATUS_INTERRUPT) |
+ BIT(SUSPEND_REQUEST_INTERRUPT) |
+ BIT(RESUME_INTERRUPT) |
+ BIT(SOF_INTERRUPT));
+ else
+ stat &= ~(BIT(CONTROL_STATUS_INTERRUPT) |
+ BIT(RESUME_INTERRUPT) |
+ BIT(SOF_DOWN_INTERRUPT) |
+ BIT(SOF_INTERRUPT));
+
+ if (!stat)
+ return;
+ /* ep_dbg(dev, "irqstat1 %08x\n", stat);*/
+
+ /* DMA status, for ep-{a,b,c,d} */
+ scratch = stat & DMA_INTERRUPTS;
+ stat &= ~DMA_INTERRUPTS;
+ scratch >>= 9;
+ for (num = 0; scratch; num++) {
+ struct net2280_dma_regs __iomem *dma;
+
+ tmp = BIT(num);
+ if ((tmp & scratch) == 0)
+ continue;
+ scratch ^= tmp;
+
+ ep = &dev->ep[num + 1];
+ dma = ep->dma;
+
+ if (!dma)
+ continue;
+
+ /* clear ep's dma status */
+ tmp = readl(&dma->dmastat);
+ writel(tmp, &dma->dmastat);
+
+ /* dma sync*/
+ if (dev->quirks & PLX_SUPERSPEED) {
+ u32 r_dmacount = readl(&dma->dmacount);
+ if (!ep->is_in && (r_dmacount & 0x00FFFFFF) &&
+ (tmp & BIT(DMA_TRANSACTION_DONE_INTERRUPT)))
+ continue;
+ }
+
+ /* chaining should stop on abort, short OUT from fifo,
+ * or (stat0 codepath) short OUT transfer.
+ */
+ if (!use_dma_chaining) {
+ if (!(tmp & BIT(DMA_TRANSACTION_DONE_INTERRUPT))) {
+ ep_dbg(ep->dev, "%s no xact done? %08x\n",
+ ep->ep.name, tmp);
+ continue;
+ }
+ stop_dma(ep->dma);
+ }
+
+ /* OUT transfers terminate when the data from the
+ * host is in our memory. Process whatever's done.
+ * On this path, we know transfer's last packet wasn't
+ * less than req->length. NAK_OUT_PACKETS may be set,
+ * or the FIFO may already be holding new packets.
+ *
+ * IN transfers can linger in the FIFO for a very
+ * long time ... we ignore that for now, accounting
+ * precisely (like PIO does) needs per-packet irqs
+ */
+ scan_dma_completions(ep);
+
+ /* disable dma on inactive queues; else maybe restart */
+ if (list_empty(&ep->queue)) {
+ if (use_dma_chaining)
+ stop_dma(ep->dma);
+ } else {
+ tmp = readl(&dma->dmactl);
+ if (!use_dma_chaining || (tmp & BIT(DMA_ENABLE)) == 0)
+ restart_dma(ep);
+ else if (ep->is_in && use_dma_chaining) {
+ struct net2280_request *req;
+ __le32 dmacount;
+
+ /* the descriptor at the head of the chain
+ * may still have VALID_BIT clear; that's
+ * used to trigger changing DMA_FIFO_VALIDATE
+ * (affects automagic zlp writes).
+ */
+ req = list_entry(ep->queue.next,
+ struct net2280_request, queue);
+ dmacount = req->td->dmacount;
+ dmacount &= cpu_to_le32(BIT(VALID_BIT) |
+ DMA_BYTE_COUNT_MASK);
+ if (dmacount && (dmacount & valid_bit) == 0)
+ restart_dma(ep);
+ }
+ }
+ ep->irqs++;
+ }
+
+ /* NOTE: there are other PCI errors we might usefully notice.
+ * if they appear very often, here's where to try recovering.
+ */
+ if (stat & PCI_ERROR_INTERRUPTS) {
+ ep_err(dev, "pci dma error; stat %08x\n", stat);
+ stat &= ~PCI_ERROR_INTERRUPTS;
+ /* these are fatal errors, but "maybe" they won't
+ * happen again ...
+ */
+ stop_activity(dev, dev->driver);
+ ep0_start(dev);
+ stat = 0;
+ }
+
+ if (stat)
+ ep_dbg(dev, "unhandled irqstat1 %08x\n", stat);
+}
+
+static irqreturn_t net2280_irq(int irq, void *_dev)
+{
+ struct net2280 *dev = _dev;
+
+ /* shared interrupt, not ours */
+ if ((dev->quirks & PLX_LEGACY) &&
+ (!(readl(&dev->regs->irqstat0) & BIT(INTA_ASSERTED))))
+ return IRQ_NONE;
+
+ spin_lock(&dev->lock);
+
+ /* handle disconnect, dma, and more */
+ handle_stat1_irqs(dev, readl(&dev->regs->irqstat1));
+
+ /* control requests and PIO */
+ handle_stat0_irqs(dev, readl(&dev->regs->irqstat0));
+
+ if (dev->quirks & PLX_SUPERSPEED) {
+ /* re-enable interrupt to trigger any possible new interrupt */
+ u32 pciirqenb1 = readl(&dev->regs->pciirqenb1);
+ writel(pciirqenb1 & 0x7FFFFFFF, &dev->regs->pciirqenb1);
+ writel(pciirqenb1, &dev->regs->pciirqenb1);
+ }
+
+ spin_unlock(&dev->lock);
+
+ return IRQ_HANDLED;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void gadget_release(struct device *_dev)
+{
+ struct net2280 *dev = dev_get_drvdata(_dev);
+
+ kfree(dev);
+}
+
+/* tear down the binding between this driver and the pci device */
+
+static void net2280_remove(struct pci_dev *pdev)
+{
+ struct net2280 *dev = pci_get_drvdata(pdev);
+
+ usb_del_gadget_udc(&dev->gadget);
+
+ BUG_ON(dev->driver);
+
+ /* then clean up the resources we allocated during probe() */
+ net2280_led_shutdown(dev);
+ if (dev->requests) {
+ int i;
+ for (i = 1; i < 5; i++) {
+ if (!dev->ep[i].dummy)
+ continue;
+ pci_pool_free(dev->requests, dev->ep[i].dummy,
+ dev->ep[i].td_dma);
+ }
+ pci_pool_destroy(dev->requests);
+ }
+ if (dev->got_irq)
+ free_irq(pdev->irq, dev);
+ if (use_msi && dev->quirks & PLX_SUPERSPEED)
+ pci_disable_msi(pdev);
+ if (dev->regs)
+ iounmap(dev->regs);
+ if (dev->region)
+ release_mem_region(pci_resource_start(pdev, 0),
+ pci_resource_len(pdev, 0));
+ if (dev->enabled)
+ pci_disable_device(pdev);
+ device_remove_file(&pdev->dev, &dev_attr_registers);
+
+ ep_info(dev, "unbind\n");
+}
+
+/* wrap this driver around the specified device, but
+ * don't respond over USB until a gadget driver binds to us.
+ */
+
+static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ struct net2280 *dev;
+ unsigned long resource, len;
+ void __iomem *base = NULL;
+ int retval, i;
+
+ if (!use_dma)
+ use_dma_chaining = 0;
+
+ /* alloc, and start init */
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (dev == NULL) {
+ retval = -ENOMEM;
+ goto done;
+ }
+
+ pci_set_drvdata(pdev, dev);
+ spin_lock_init(&dev->lock);
+ dev->quirks = id->driver_data;
+ dev->pdev = pdev;
+ dev->gadget.ops = &net2280_ops;
+ dev->gadget.max_speed = (dev->quirks & PLX_SUPERSPEED) ?
+ USB_SPEED_SUPER : USB_SPEED_HIGH;
+
+ /* the "gadget" abstracts/virtualizes the controller */
+ dev->gadget.name = driver_name;
+
+ /* now all the pci goodies ... */
+ if (pci_enable_device(pdev) < 0) {
+ retval = -ENODEV;
+ goto done;
+ }
+ dev->enabled = 1;
+
+ /* BAR 0 holds all the registers
+ * BAR 1 is 8051 memory; unused here (note erratum 0103)
+ * BAR 2 is fifo memory; unused here
+ */
+ resource = pci_resource_start(pdev, 0);
+ len = pci_resource_len(pdev, 0);
+ if (!request_mem_region(resource, len, driver_name)) {
+ ep_dbg(dev, "controller already in use\n");
+ retval = -EBUSY;
+ goto done;
+ }
+ dev->region = 1;
+
+ /* FIXME provide firmware download interface to put
+ * 8051 code into the chip, e.g. to turn on PCI PM.
+ */
+
+ base = ioremap_nocache(resource, len);
+ if (base == NULL) {
+ ep_dbg(dev, "can't map memory\n");
+ retval = -EFAULT;
+ goto done;
+ }
+ dev->regs = (struct net2280_regs __iomem *) base;
+ dev->usb = (struct net2280_usb_regs __iomem *) (base + 0x0080);
+ dev->pci = (struct net2280_pci_regs __iomem *) (base + 0x0100);
+ dev->dma = (struct net2280_dma_regs __iomem *) (base + 0x0180);
+ dev->dep = (struct net2280_dep_regs __iomem *) (base + 0x0200);
+ dev->epregs = (struct net2280_ep_regs __iomem *) (base + 0x0300);
+
+ if (dev->quirks & PLX_SUPERSPEED) {
+ u32 fsmvalue;
+ u32 usbstat;
+ dev->usb_ext = (struct usb338x_usb_ext_regs __iomem *)
+ (base + 0x00b4);
+ dev->fiforegs = (struct usb338x_fifo_regs __iomem *)
+ (base + 0x0500);
+ 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);
+ dev->enhanced_mode = !!(usbstat & BIT(11));
+ dev->n_ep = (dev->enhanced_mode) ? 9 : 5;
+ /* put into initial config, link up all endpoints */
+ fsmvalue = get_idx_reg(dev->regs, SCRATCH) &
+ (0xf << DEFECT7374_FSM_FIELD);
+ /* See if firmware needs to set up for workaround: */
+ if (fsmvalue == DEFECT7374_FSM_SS_CONTROL_READ)
+ writel(0, &dev->usb->usbctl);
+ } else{
+ dev->enhanced_mode = 0;
+ dev->n_ep = 7;
+ /* put into initial config, link up all endpoints */
+ writel(0, &dev->usb->usbctl);
+ }
+
+ usb_reset(dev);
+ usb_reinit(dev);
+
+ /* irq setup after old hardware is cleaned up */
+ if (!pdev->irq) {
+ ep_err(dev, "No IRQ. Check PCI setup!\n");
+ retval = -ENODEV;
+ goto done;
+ }
+
+ if (use_msi && (dev->quirks & PLX_SUPERSPEED))
+ if (pci_enable_msi(pdev))
+ ep_err(dev, "Failed to enable MSI mode\n");
+
+ if (request_irq(pdev->irq, net2280_irq, IRQF_SHARED,
+ driver_name, dev)) {
+ ep_err(dev, "request interrupt %d failed\n", pdev->irq);
+ retval = -EBUSY;
+ goto done;
+ }
+ dev->got_irq = 1;
+
+ /* DMA setup */
+ /* NOTE: we know only the 32 LSBs of dma addresses may be nonzero */
+ dev->requests = pci_pool_create("requests", pdev,
+ sizeof(struct net2280_dma),
+ 0 /* no alignment requirements */,
+ 0 /* or page-crossing issues */);
+ if (!dev->requests) {
+ ep_dbg(dev, "can't get request pool\n");
+ retval = -ENOMEM;
+ goto done;
+ }
+ for (i = 1; i < 5; i++) {
+ struct net2280_dma *td;
+
+ td = pci_pool_alloc(dev->requests, GFP_KERNEL,
+ &dev->ep[i].td_dma);
+ if (!td) {
+ ep_dbg(dev, "can't get dummy %d\n", i);
+ retval = -ENOMEM;
+ goto done;
+ }
+ td->dmacount = 0; /* not VALID */
+ td->dmadesc = td->dmaaddr;
+ dev->ep[i].dummy = td;
+ }
+
+ /* enable lower-overhead pci memory bursts during DMA */
+ if (dev->quirks & PLX_LEGACY)
+ writel(BIT(DMA_MEMORY_WRITE_AND_INVALIDATE_ENABLE) |
+ /*
+ * 256 write retries may not be enough...
+ BIT(PCI_RETRY_ABORT_ENABLE) |
+ */
+ BIT(DMA_READ_MULTIPLE_ENABLE) |
+ BIT(DMA_READ_LINE_ENABLE),
+ &dev->pci->pcimstctl);
+ /* erratum 0115 shouldn't appear: Linux inits PCI_LATENCY_TIMER */
+ pci_set_master(pdev);
+ pci_try_set_mwi(pdev);
+
+ /* ... also flushes any posted pci writes */
+ dev->chiprev = get_idx_reg(dev->regs, REG_CHIPREV) & 0xffff;
+
+ /* done */
+ ep_info(dev, "%s\n", driver_desc);
+ ep_info(dev, "irq %d, pci mem %p, chip rev %04x\n",
+ pdev->irq, base, dev->chiprev);
+ ep_info(dev, "version: " DRIVER_VERSION "; dma %s %s\n",
+ use_dma ? (use_dma_chaining ? "chaining" : "enabled")
+ : "disabled",
+ dev->enhanced_mode ? "enhanced mode" : "legacy mode");
+ retval = device_create_file(&pdev->dev, &dev_attr_registers);
+ if (retval)
+ goto done;
+
+ retval = usb_add_gadget_udc_release(&pdev->dev, &dev->gadget,
+ gadget_release);
+ if (retval)
+ goto done;
+ return 0;
+
+done:
+ if (dev)
+ net2280_remove(pdev);
+ return retval;
+}
+
+/* make sure the board is quiescent; otherwise it will continue
+ * generating IRQs across the upcoming reboot.
+ */
+
+static void net2280_shutdown(struct pci_dev *pdev)
+{
+ struct net2280 *dev = pci_get_drvdata(pdev);
+
+ /* disable IRQs */
+ writel(0, &dev->regs->pciirqenb0);
+ writel(0, &dev->regs->pciirqenb1);
+
+ /* disable the pullup so the host will think we're gone */
+ writel(0, &dev->usb->usbctl);
+
+ /* Disable full-speed test mode */
+ if (dev->quirks & PLX_LEGACY)
+ writel(0, &dev->usb->xcvrdiag);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static const struct pci_device_id pci_ids[] = { {
+ .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe),
+ .class_mask = ~0,
+ .vendor = PCI_VENDOR_ID_PLX_LEGACY,
+ .device = 0x2280,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = PLX_LEGACY | PLX_2280,
+ }, {
+ .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe),
+ .class_mask = ~0,
+ .vendor = PCI_VENDOR_ID_PLX_LEGACY,
+ .device = 0x2282,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = PLX_LEGACY,
+ },
+ {
+ .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe),
+ .class_mask = ~0,
+ .vendor = PCI_VENDOR_ID_PLX,
+ .device = 0x3380,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = PLX_SUPERSPEED,
+ },
+ {
+ .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe),
+ .class_mask = ~0,
+ .vendor = PCI_VENDOR_ID_PLX,
+ .device = 0x3382,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = PLX_SUPERSPEED,
+ },
+{ /* end: all zeroes */ }
+};
+MODULE_DEVICE_TABLE(pci, pci_ids);
+
+/* pci driver glue; this is a "new style" PCI driver module */
+static struct pci_driver net2280_pci_driver = {
+ .name = (char *) driver_name,
+ .id_table = pci_ids,
+
+ .probe = net2280_probe,
+ .remove = net2280_remove,
+ .shutdown = net2280_shutdown,
+
+ /* FIXME add power management support */
+};
+
+module_pci_driver(net2280_pci_driver);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("David Brownell");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/udc/net2280.h b/drivers/usb/gadget/udc/net2280.h
new file mode 100644
index 000000000000..03f15242d794
--- /dev/null
+++ b/drivers/usb/gadget/udc/net2280.h
@@ -0,0 +1,403 @@
+/*
+ * NetChip 2280 high/full speed USB device controller.
+ * Unlike many such controllers, this one talks PCI.
+ */
+
+/*
+ * Copyright (C) 2002 NetChip Technology, Inc. (http://www.netchip.com)
+ * Copyright (C) 2003 David Brownell
+ * Copyright (C) 2014 Ricardo Ribalda - Qtechnology/AS
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/usb/net2280.h>
+#include <linux/usb/usb338x.h>
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef __KERNEL__
+
+/* indexed registers [11.10] are accessed indirectly
+ * caller must own the device lock.
+ */
+
+static inline u32 get_idx_reg(struct net2280_regs __iomem *regs, u32 index)
+{
+ writel(index, &regs->idxaddr);
+ /* NOTE: synchs device/cpu memory views */
+ return readl(&regs->idxdata);
+}
+
+static inline void
+set_idx_reg(struct net2280_regs __iomem *regs, u32 index, u32 value)
+{
+ writel(index, &regs->idxaddr);
+ writel(value, &regs->idxdata);
+ /* posted, may not be visible yet */
+}
+
+#endif /* __KERNEL__ */
+
+#define PCI_VENDOR_ID_PLX_LEGACY 0x17cc
+
+#define PLX_LEGACY BIT(0)
+#define PLX_2280 BIT(1)
+#define PLX_SUPERSPEED BIT(2)
+
+#define REG_DIAG 0x0
+#define RETRY_COUNTER 16
+#define FORCE_PCI_SERR 11
+#define FORCE_PCI_INTERRUPT 10
+#define FORCE_USB_INTERRUPT 9
+#define FORCE_CPU_INTERRUPT 8
+#define ILLEGAL_BYTE_ENABLES 5
+#define FAST_TIMES 4
+#define FORCE_RECEIVE_ERROR 2
+#define FORCE_TRANSMIT_CRC_ERROR 0
+#define REG_FRAME 0x02 /* from last sof */
+#define REG_CHIPREV 0x03 /* in bcd */
+#define REG_HS_NAK_RATE 0x0a /* NAK per N uframes */
+
+#define CHIPREV_1 0x0100
+#define CHIPREV_1A 0x0110
+
+/* DEFECT 7374 */
+#define DEFECT_7374_NUMBEROF_MAX_WAIT_LOOPS 200
+#define DEFECT_7374_PROCESSOR_WAIT_TIME 10
+
+/* ep0 max packet size */
+#define EP0_SS_MAX_PACKET_SIZE 0x200
+#define EP0_HS_MAX_PACKET_SIZE 0x40
+#ifdef __KERNEL__
+
+/*-------------------------------------------------------------------------*/
+
+/* [8.3] for scatter/gather i/o
+ * use struct net2280_dma_regs bitfields
+ */
+struct net2280_dma {
+ __le32 dmacount;
+ __le32 dmaaddr; /* the buffer */
+ __le32 dmadesc; /* next dma descriptor */
+ __le32 _reserved;
+} __aligned(16);
+
+/*-------------------------------------------------------------------------*/
+
+/* DRIVER DATA STRUCTURES and UTILITIES */
+
+struct net2280_ep {
+ struct usb_ep ep;
+ struct net2280_ep_regs __iomem *cfg;
+ struct net2280_ep_regs __iomem *regs;
+ struct net2280_dma_regs __iomem *dma;
+ struct net2280_dma *dummy;
+ struct usb338x_fifo_regs __iomem *fiforegs;
+ dma_addr_t td_dma; /* of dummy */
+ struct net2280 *dev;
+ unsigned long irqs;
+ unsigned is_halt:1, dma_started:1;
+
+ /* analogous to a host-side qh */
+ struct list_head queue;
+ const struct usb_endpoint_descriptor *desc;
+ unsigned num : 8,
+ fifo_size : 12,
+ in_fifo_validate : 1,
+ out_overflow : 1,
+ stopped : 1,
+ wedged : 1,
+ is_in : 1,
+ is_iso : 1,
+ responded : 1;
+};
+
+static inline void allow_status(struct net2280_ep *ep)
+{
+ /* ep0 only */
+ writel(BIT(CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE) |
+ BIT(CLEAR_NAK_OUT_PACKETS) |
+ BIT(CLEAR_NAK_OUT_PACKETS_MODE),
+ &ep->regs->ep_rsp);
+ ep->stopped = 1;
+}
+
+static void allow_status_338x(struct net2280_ep *ep)
+{
+ /*
+ * Control Status Phase Handshake was set by the chip when the setup
+ * packet arrived. While set, the chip automatically NAKs the host's
+ * Status Phase tokens.
+ */
+ writel(BIT(CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE), &ep->regs->ep_rsp);
+
+ ep->stopped = 1;
+
+ /* TD 9.9 Halt Endpoint test. TD 9.22 set feature test. */
+ ep->responded = 0;
+}
+
+struct net2280_request {
+ struct usb_request req;
+ struct net2280_dma *td;
+ dma_addr_t td_dma;
+ struct list_head queue;
+ unsigned mapped : 1,
+ valid : 1;
+};
+
+struct net2280 {
+ /* each pci device provides one gadget, several endpoints */
+ struct usb_gadget gadget;
+ spinlock_t lock;
+ struct net2280_ep ep[9];
+ struct usb_gadget_driver *driver;
+ unsigned enabled : 1,
+ protocol_stall : 1,
+ softconnect : 1,
+ got_irq : 1,
+ region:1,
+ u1_enable:1,
+ u2_enable:1,
+ ltm_enable:1,
+ wakeup_enable:1,
+ selfpowered:1,
+ addressed_state:1;
+ u16 chiprev;
+ int enhanced_mode;
+ int n_ep;
+ kernel_ulong_t quirks;
+
+
+ /* pci state used to access those endpoints */
+ struct pci_dev *pdev;
+ struct net2280_regs __iomem *regs;
+ struct net2280_usb_regs __iomem *usb;
+ struct usb338x_usb_ext_regs __iomem *usb_ext;
+ struct net2280_pci_regs __iomem *pci;
+ struct net2280_dma_regs __iomem *dma;
+ struct net2280_dep_regs __iomem *dep;
+ struct net2280_ep_regs __iomem *epregs;
+ struct usb338x_fifo_regs __iomem *fiforegs;
+ 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 pci_pool *requests;
+ /* statistics...*/
+};
+
+static inline void set_halt(struct net2280_ep *ep)
+{
+ /* ep0 and bulk/intr endpoints */
+ writel(BIT(CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE) |
+ /* set NAK_OUT for erratum 0114 */
+ ((ep->dev->chiprev == CHIPREV_1) << SET_NAK_OUT_PACKETS) |
+ BIT(SET_ENDPOINT_HALT),
+ &ep->regs->ep_rsp);
+}
+
+static inline void clear_halt(struct net2280_ep *ep)
+{
+ /* ep0 and bulk/intr endpoints */
+ writel(BIT(CLEAR_ENDPOINT_HALT) |
+ BIT(CLEAR_ENDPOINT_TOGGLE) |
+ /*
+ * unless the gadget driver left a short packet in the
+ * fifo, this reverses the erratum 0114 workaround.
+ */
+ ((ep->dev->chiprev == CHIPREV_1) << CLEAR_NAK_OUT_PACKETS),
+ &ep->regs->ep_rsp);
+}
+
+/*
+ * FSM value for Defect 7374 (U1U2 Test) is managed in
+ * chip's SCRATCH register:
+ */
+#define DEFECT7374_FSM_FIELD 28
+
+/* Waiting for Control Read:
+ * - A transition to this state indicates a fresh USB connection,
+ * before the first Setup Packet. The connection speed is not
+ * known. Firmware is waiting for the first Control Read.
+ * - Starting state: This state can be thought of as the FSM's typical
+ * starting state.
+ * - Tip: Upon the first SS Control Read the FSM never
+ * returns to this state.
+ */
+#define DEFECT7374_FSM_WAITING_FOR_CONTROL_READ BIT(DEFECT7374_FSM_FIELD)
+
+/* Non-SS Control Read:
+ * - A transition to this state indicates detection of the first HS
+ * or FS Control Read.
+ * - Tip: Upon the first SS Control Read the FSM never
+ * returns to this state.
+ */
+#define DEFECT7374_FSM_NON_SS_CONTROL_READ (2 << DEFECT7374_FSM_FIELD)
+
+/* SS Control Read:
+ * - A transition to this state indicates detection of the
+ * first SS Control Read.
+ * - This state indicates workaround completion. Workarounds no longer
+ * need to be applied (as long as the chip remains powered up).
+ * - Tip: Once in this state the FSM state does not change (until
+ * the chip's power is lost and restored).
+ * - This can be thought of as the final state of the FSM;
+ * the FSM 'locks-up' in this state until the chip loses power.
+ */
+#define DEFECT7374_FSM_SS_CONTROL_READ (3 << DEFECT7374_FSM_FIELD)
+
+#ifdef USE_RDK_LEDS
+
+static inline void net2280_led_init(struct net2280 *dev)
+{
+ /* LED3 (green) is on during USB activity. note erratum 0113. */
+ writel(BIT(GPIO3_LED_SELECT) |
+ BIT(GPIO3_OUTPUT_ENABLE) |
+ BIT(GPIO2_OUTPUT_ENABLE) |
+ BIT(GPIO1_OUTPUT_ENABLE) |
+ BIT(GPIO0_OUTPUT_ENABLE),
+ &dev->regs->gpioctl);
+}
+
+/* indicate speed with bi-color LED 0/1 */
+static inline
+void net2280_led_speed(struct net2280 *dev, enum usb_device_speed speed)
+{
+ u32 val = readl(&dev->regs->gpioctl);
+ switch (speed) {
+ case USB_SPEED_SUPER: /* green + red */
+ val |= BIT(GPIO0_DATA) | BIT(GPIO1_DATA);
+ break;
+ case USB_SPEED_HIGH: /* green */
+ val &= ~BIT(GPIO0_DATA);
+ val |= BIT(GPIO1_DATA);
+ break;
+ case USB_SPEED_FULL: /* red */
+ val &= ~BIT(GPIO1_DATA);
+ val |= BIT(GPIO0_DATA);
+ break;
+ default: /* (off/black) */
+ val &= ~(BIT(GPIO1_DATA) | BIT(GPIO0_DATA));
+ break;
+ }
+ writel(val, &dev->regs->gpioctl);
+}
+
+/* indicate power with LED 2 */
+static inline void net2280_led_active(struct net2280 *dev, int is_active)
+{
+ u32 val = readl(&dev->regs->gpioctl);
+
+ /* FIXME this LED never seems to turn on.*/
+ if (is_active)
+ val |= GPIO2_DATA;
+ else
+ val &= ~GPIO2_DATA;
+ writel(val, &dev->regs->gpioctl);
+}
+
+static inline void net2280_led_shutdown(struct net2280 *dev)
+{
+ /* turn off all four GPIO*_DATA bits */
+ writel(readl(&dev->regs->gpioctl) & ~0x0f,
+ &dev->regs->gpioctl);
+}
+
+#else
+
+#define net2280_led_init(dev) do { } while (0)
+#define net2280_led_speed(dev, speed) do { } while (0)
+#define net2280_led_shutdown(dev) do { } while (0)
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+#define ep_dbg(ndev, fmt, args...) \
+ dev_dbg((&((ndev)->pdev->dev)), fmt, ##args)
+
+#define ep_vdbg(ndev, fmt, args...) \
+ dev_vdbg((&((ndev)->pdev->dev)), fmt, ##args)
+
+#define ep_info(ndev, fmt, args...) \
+ dev_info((&((ndev)->pdev->dev)), fmt, ##args)
+
+#define ep_warn(ndev, fmt, args...) \
+ dev_warn((&((ndev)->pdev->dev)), fmt, ##args)
+
+#define ep_err(ndev, fmt, args...) \
+ dev_err((&((ndev)->pdev->dev)), fmt, ##args)
+
+/*-------------------------------------------------------------------------*/
+
+static inline void set_fifo_bytecount(struct net2280_ep *ep, unsigned count)
+{
+ if (ep->dev->pdev->vendor == 0x17cc)
+ writeb(count, 2 + (u8 __iomem *) &ep->regs->ep_cfg);
+ else{
+ u32 tmp = readl(&ep->cfg->ep_cfg) &
+ (~(0x07 << EP_FIFO_BYTE_COUNT));
+ writel(tmp | (count << EP_FIFO_BYTE_COUNT), &ep->cfg->ep_cfg);
+ }
+}
+
+static inline void start_out_naking(struct net2280_ep *ep)
+{
+ /* NOTE: hardware races lurk here, and PING protocol issues */
+ writel(BIT(SET_NAK_OUT_PACKETS), &ep->regs->ep_rsp);
+ /* synch with device */
+ readl(&ep->regs->ep_rsp);
+}
+
+#ifdef DEBUG
+static inline void assert_out_naking(struct net2280_ep *ep, const char *where)
+{
+ u32 tmp = readl(&ep->regs->ep_stat);
+
+ if ((tmp & BIT(NAK_OUT_PACKETS)) == 0) {
+ ep_dbg(ep->dev, "%s %s %08x !NAK\n",
+ ep->ep.name, where, tmp);
+ writel(BIT(SET_NAK_OUT_PACKETS),
+ &ep->regs->ep_rsp);
+ }
+}
+#define ASSERT_OUT_NAKING(ep) assert_out_naking(ep, __func__)
+#else
+#define ASSERT_OUT_NAKING(ep) do {} while (0)
+#endif
+
+static inline void stop_out_naking(struct net2280_ep *ep)
+{
+ u32 tmp;
+
+ tmp = readl(&ep->regs->ep_stat);
+ if ((tmp & BIT(NAK_OUT_PACKETS)) != 0)
+ writel(BIT(CLEAR_NAK_OUT_PACKETS), &ep->regs->ep_rsp);
+}
+
+
+static inline void set_max_speed(struct net2280_ep *ep, u32 max)
+{
+ u32 reg;
+ static const u32 ep_enhanced[9] = { 0x10, 0x60, 0x30, 0x80,
+ 0x50, 0x20, 0x70, 0x40, 0x90 };
+
+ if (ep->dev->enhanced_mode)
+ reg = ep_enhanced[ep->num];
+ else{
+ reg = (ep->num + 1) * 0x10;
+ if (ep->dev->gadget.speed != USB_SPEED_HIGH)
+ reg += 1;
+ }
+
+ set_idx_reg(ep->dev->regs, reg, max);
+}
+
+#endif /* __KERNEL__ */
diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/udc/omap_udc.c
index 2ae4f6d69f74..e731373fd4d7 100644
--- a/drivers/usb/gadget/omap_udc.c
+++ b/drivers/usb/gadget/udc/omap_udc.c
@@ -2079,10 +2079,7 @@ static int omap_udc_start(struct usb_gadget *g,
&udc->gadget);
if (status < 0) {
ERR("can't bind to transceiver\n");
- if (driver->unbind) {
- driver->unbind(&udc->gadget);
- udc->driver = NULL;
- }
+ udc->driver = NULL;
goto done;
}
} else {
diff --git a/drivers/usb/gadget/omap_udc.h b/drivers/usb/gadget/udc/omap_udc.h
index cfadeb5fc5de..cfadeb5fc5de 100644
--- a/drivers/usb/gadget/omap_udc.h
+++ b/drivers/usb/gadget/udc/omap_udc.h
diff --git a/drivers/usb/gadget/pch_udc.c b/drivers/usb/gadget/udc/pch_udc.c
index eb8c3bedb57a..eb8c3bedb57a 100644
--- a/drivers/usb/gadget/pch_udc.c
+++ b/drivers/usb/gadget/udc/pch_udc.c
diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/udc/pxa25x_udc.c
index 9984437d2952..251e4d5ee152 100644
--- a/drivers/usb/gadget/pxa25x_udc.c
+++ b/drivers/usb/gadget/udc/pxa25x_udc.c
@@ -16,6 +16,7 @@
/* #define VERBOSE_DEBUG */
#include <linux/device.h>
+#include <linux/gpio.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/ioport.h>
@@ -40,7 +41,6 @@
#include <asm/byteorder.h>
#include <asm/dma.h>
-#include <asm/gpio.h>
#include <asm/mach-types.h>
#include <asm/unaligned.h>
@@ -2105,11 +2105,9 @@ static int pxa25x_udc_probe(struct platform_device *pdev)
if (irq < 0)
return -ENODEV;
- dev->clk = clk_get(&pdev->dev, NULL);
- if (IS_ERR(dev->clk)) {
- retval = PTR_ERR(dev->clk);
- goto err_clk;
- }
+ dev->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(dev->clk))
+ return PTR_ERR(dev->clk);
pr_debug("%s: IRQ %d%s%s\n", driver_name, irq,
dev->has_cfr ? "" : " (!cfr)",
@@ -2120,15 +2118,16 @@ static int pxa25x_udc_probe(struct platform_device *pdev)
dev->dev = &pdev->dev;
dev->mach = dev_get_platdata(&pdev->dev);
- dev->transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
+ dev->transceiver = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
if (gpio_is_valid(dev->mach->gpio_pullup)) {
- if ((retval = gpio_request(dev->mach->gpio_pullup,
- "pca25x_udc GPIO PULLUP"))) {
+ retval = devm_gpio_request(&pdev->dev, dev->mach->gpio_pullup,
+ "pca25x_udc GPIO PULLUP");
+ if (retval) {
dev_dbg(&pdev->dev,
"can't get pullup gpio %d, err: %d\n",
dev->mach->gpio_pullup, retval);
- goto err_gpio_pullup;
+ goto err;
}
gpio_direction_output(dev->mach->gpio_pullup, 0);
}
@@ -2146,30 +2145,32 @@ static int pxa25x_udc_probe(struct platform_device *pdev)
dev->vbus = 0;
/* irq setup after old hardware state is cleaned up */
- retval = request_irq(irq, pxa25x_udc_irq,
- 0, driver_name, dev);
+ retval = devm_request_irq(&pdev->dev, irq, pxa25x_udc_irq, 0,
+ driver_name, dev);
if (retval != 0) {
pr_err("%s: can't get irq %d, err %d\n",
driver_name, irq, retval);
- goto err_irq1;
+ goto err;
}
dev->got_irq = 1;
#ifdef CONFIG_ARCH_LUBBOCK
if (machine_is_lubbock()) {
- retval = request_irq(LUBBOCK_USB_DISC_IRQ, lubbock_vbus_irq,
- 0, driver_name, dev);
+ retval = devm_request_irq(&pdev->dev, LUBBOCK_USB_DISC_IRQ,
+ lubbock_vbus_irq, 0, driver_name,
+ dev);
if (retval != 0) {
pr_err("%s: can't get irq %i, err %d\n",
driver_name, LUBBOCK_USB_DISC_IRQ, retval);
- goto err_irq_lub;
+ goto err;
}
- retval = request_irq(LUBBOCK_USB_IRQ, lubbock_vbus_irq,
- 0, driver_name, dev);
+ retval = devm_request_irq(&pdev->dev, LUBBOCK_USB_IRQ,
+ lubbock_vbus_irq, 0, driver_name,
+ dev);
if (retval != 0) {
pr_err("%s: can't get irq %i, err %d\n",
driver_name, LUBBOCK_USB_IRQ, retval);
- goto lubbock_fail0;
+ goto err;
}
} else
#endif
@@ -2180,22 +2181,9 @@ static int pxa25x_udc_probe(struct platform_device *pdev)
return retval;
remove_debug_files(dev);
-#ifdef CONFIG_ARCH_LUBBOCK
-lubbock_fail0:
- free_irq(LUBBOCK_USB_DISC_IRQ, dev);
- err_irq_lub:
- free_irq(irq, dev);
-#endif
- err_irq1:
- if (gpio_is_valid(dev->mach->gpio_pullup))
- gpio_free(dev->mach->gpio_pullup);
- err_gpio_pullup:
- if (!IS_ERR_OR_NULL(dev->transceiver)) {
- usb_put_phy(dev->transceiver);
+ err:
+ if (!IS_ERR_OR_NULL(dev->transceiver))
dev->transceiver = NULL;
- }
- clk_put(dev->clk);
- err_clk:
return retval;
}
@@ -2217,25 +2205,8 @@ static int pxa25x_udc_remove(struct platform_device *pdev)
remove_debug_files(dev);
- if (dev->got_irq) {
- free_irq(platform_get_irq(pdev, 0), dev);
- dev->got_irq = 0;
- }
-#ifdef CONFIG_ARCH_LUBBOCK
- if (machine_is_lubbock()) {
- free_irq(LUBBOCK_USB_DISC_IRQ, dev);
- free_irq(LUBBOCK_USB_IRQ, dev);
- }
-#endif
- if (gpio_is_valid(dev->mach->gpio_pullup))
- gpio_free(dev->mach->gpio_pullup);
-
- clk_put(dev->clk);
-
- if (!IS_ERR_OR_NULL(dev->transceiver)) {
- usb_put_phy(dev->transceiver);
+ if (!IS_ERR_OR_NULL(dev->transceiver))
dev->transceiver = NULL;
- }
the_controller = NULL;
return 0;
diff --git a/drivers/usb/gadget/pxa25x_udc.h b/drivers/usb/gadget/udc/pxa25x_udc.h
index 3fe5931dc21a..3fe5931dc21a 100644
--- a/drivers/usb/gadget/pxa25x_udc.h
+++ b/drivers/usb/gadget/udc/pxa25x_udc.h
diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c
index cdf4d678be96..597d39f89420 100644
--- a/drivers/usb/gadget/pxa27x_udc.c
+++ b/drivers/usb/gadget/udc/pxa27x_udc.c
@@ -2446,6 +2446,9 @@ static int pxa_udc_probe(struct platform_device *pdev)
retval = PTR_ERR(udc->clk);
goto err_clk;
}
+ retval = clk_prepare(udc->clk);
+ if (retval)
+ goto err_clk_prepare;
retval = -ENOMEM;
udc->regs = ioremap(regs->start, resource_size(regs));
@@ -2483,6 +2486,8 @@ err_add_udc:
err_irq:
iounmap(udc->regs);
err_map:
+ clk_unprepare(udc->clk);
+err_clk_prepare:
clk_put(udc->clk);
udc->clk = NULL;
err_clk:
@@ -2509,6 +2514,7 @@ static int pxa_udc_remove(struct platform_device *_dev)
udc->transceiver = NULL;
the_controller = NULL;
+ clk_unprepare(udc->clk);
clk_put(udc->clk);
iounmap(udc->regs);
diff --git a/drivers/usb/gadget/pxa27x_udc.h b/drivers/usb/gadget/udc/pxa27x_udc.h
index 28f2b53530f5..28f2b53530f5 100644
--- a/drivers/usb/gadget/pxa27x_udc.h
+++ b/drivers/usb/gadget/udc/pxa27x_udc.h
diff --git a/drivers/usb/gadget/r8a66597-udc.c b/drivers/usb/gadget/udc/r8a66597-udc.c
index b698a490cc7d..46008421c1ec 100644
--- a/drivers/usb/gadget/r8a66597-udc.c
+++ b/drivers/usb/gadget/udc/r8a66597-udc.c
@@ -1826,18 +1826,12 @@ static int __exit r8a66597_remove(struct platform_device *pdev)
usb_del_gadget_udc(&r8a66597->gadget);
del_timer_sync(&r8a66597->timer);
- iounmap(r8a66597->reg);
- if (r8a66597->pdata->sudmac)
- iounmap(r8a66597->sudmac_reg);
- free_irq(platform_get_irq(pdev, 0), r8a66597);
r8a66597_free_request(&r8a66597->ep[0].ep, r8a66597->ep0_req);
if (r8a66597->pdata->on_chip) {
clk_disable_unprepare(r8a66597->clk);
- clk_put(r8a66597->clk);
}
- kfree(r8a66597);
return 0;
}
@@ -1845,28 +1839,24 @@ static void nop_completion(struct usb_ep *ep, struct usb_request *r)
{
}
-static int __init r8a66597_sudmac_ioremap(struct r8a66597 *r8a66597,
+static int r8a66597_sudmac_ioremap(struct r8a66597 *r8a66597,
struct platform_device *pdev)
{
struct resource *res;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sudmac");
- if (!res) {
- dev_err(&pdev->dev, "platform_get_resource error(sudmac).\n");
- return -ENODEV;
- }
-
- r8a66597->sudmac_reg = ioremap(res->start, resource_size(res));
- if (r8a66597->sudmac_reg == NULL) {
+ r8a66597->sudmac_reg = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(r8a66597->sudmac_reg)) {
dev_err(&pdev->dev, "ioremap error(sudmac).\n");
- return -ENOMEM;
+ return PTR_ERR(r8a66597->sudmac_reg);
}
return 0;
}
-static int __init r8a66597_probe(struct platform_device *pdev)
+static int r8a66597_probe(struct platform_device *pdev)
{
+ struct device *dev = &pdev->dev;
char clk_name[8];
struct resource *res, *ires;
int irq;
@@ -1877,39 +1867,27 @@ static int __init r8a66597_probe(struct platform_device *pdev)
unsigned long irq_trigger;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- ret = -ENODEV;
- dev_err(&pdev->dev, "platform_get_resource error.\n");
- goto clean_up;
- }
+ reg = devm_ioremap_resource(&pdev->dev, res);
+ if (!reg)
+ return -ENODEV;
ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
irq = ires->start;
irq_trigger = ires->flags & IRQF_TRIGGER_MASK;
if (irq < 0) {
- ret = -ENODEV;
- dev_err(&pdev->dev, "platform_get_irq error.\n");
- goto clean_up;
- }
-
- reg = ioremap(res->start, resource_size(res));
- if (reg == NULL) {
- ret = -ENOMEM;
- dev_err(&pdev->dev, "ioremap error.\n");
- goto clean_up;
+ dev_err(dev, "platform_get_irq error.\n");
+ return -ENODEV;
}
/* initialize ucd */
- r8a66597 = kzalloc(sizeof(struct r8a66597), GFP_KERNEL);
- if (r8a66597 == NULL) {
- ret = -ENOMEM;
- goto clean_up;
- }
+ r8a66597 = devm_kzalloc(dev, sizeof(struct r8a66597), GFP_KERNEL);
+ if (r8a66597 == NULL)
+ return -ENOMEM;
spin_lock_init(&r8a66597->lock);
platform_set_drvdata(pdev, r8a66597);
- r8a66597->pdata = dev_get_platdata(&pdev->dev);
+ r8a66597->pdata = dev_get_platdata(dev);
r8a66597->irq_sense_low = irq_trigger == IRQF_TRIGGER_LOW;
r8a66597->gadget.ops = &r8a66597_gadget_ops;
@@ -1923,12 +1901,10 @@ static int __init r8a66597_probe(struct platform_device *pdev)
if (r8a66597->pdata->on_chip) {
snprintf(clk_name, sizeof(clk_name), "usb%d", pdev->id);
- r8a66597->clk = clk_get(&pdev->dev, clk_name);
+ r8a66597->clk = devm_clk_get(dev, clk_name);
if (IS_ERR(r8a66597->clk)) {
- dev_err(&pdev->dev, "cannot get clock \"%s\"\n",
- clk_name);
- ret = PTR_ERR(r8a66597->clk);
- goto clean_up;
+ dev_err(dev, "cannot get clock \"%s\"\n", clk_name);
+ return PTR_ERR(r8a66597->clk);
}
clk_prepare_enable(r8a66597->clk);
}
@@ -1941,10 +1917,10 @@ static int __init r8a66597_probe(struct platform_device *pdev)
disable_controller(r8a66597); /* make sure controller is disabled */
- ret = request_irq(irq, r8a66597_irq, IRQF_SHARED,
- udc_name, r8a66597);
+ ret = devm_request_irq(dev, irq, r8a66597_irq, IRQF_SHARED,
+ udc_name, r8a66597);
if (ret < 0) {
- dev_err(&pdev->dev, "request_irq error (%d)\n", ret);
+ dev_err(dev, "request_irq error (%d)\n", ret);
goto clean_up2;
}
@@ -1978,37 +1954,25 @@ static int __init r8a66597_probe(struct platform_device *pdev)
GFP_KERNEL);
if (r8a66597->ep0_req == NULL) {
ret = -ENOMEM;
- goto clean_up3;
+ goto clean_up2;
}
r8a66597->ep0_req->complete = nop_completion;
- ret = usb_add_gadget_udc(&pdev->dev, &r8a66597->gadget);
+ ret = usb_add_gadget_udc(dev, &r8a66597->gadget);
if (ret)
goto err_add_udc;
- dev_info(&pdev->dev, "version %s\n", DRIVER_VERSION);
+ dev_info(dev, "version %s\n", DRIVER_VERSION);
return 0;
err_add_udc:
r8a66597_free_request(&r8a66597->ep[0].ep, r8a66597->ep0_req);
-clean_up3:
- free_irq(irq, r8a66597);
clean_up2:
- if (r8a66597->pdata->on_chip) {
+ if (r8a66597->pdata->on_chip)
clk_disable_unprepare(r8a66597->clk);
- clk_put(r8a66597->clk);
- }
-clean_up:
- if (r8a66597) {
- if (r8a66597->sudmac_reg)
- iounmap(r8a66597->sudmac_reg);
- if (r8a66597->ep0_req)
- r8a66597_free_request(&r8a66597->ep[0].ep,
- r8a66597->ep0_req);
- kfree(r8a66597);
- }
- if (reg)
- iounmap(reg);
+
+ if (r8a66597->ep0_req)
+ r8a66597_free_request(&r8a66597->ep[0].ep, r8a66597->ep0_req);
return ret;
}
diff --git a/drivers/usb/gadget/r8a66597-udc.h b/drivers/usb/gadget/udc/r8a66597-udc.h
index 45c4b2df1785..45c4b2df1785 100644
--- a/drivers/usb/gadget/r8a66597-udc.h
+++ b/drivers/usb/gadget/udc/r8a66597-udc.h
diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/udc/s3c-hsudc.c
index 10c6a128250c..10c6a128250c 100644
--- a/drivers/usb/gadget/s3c-hsudc.c
+++ b/drivers/usb/gadget/udc/s3c-hsudc.c
diff --git a/drivers/usb/gadget/s3c2410_udc.c b/drivers/usb/gadget/udc/s3c2410_udc.c
index 7987aa049fab..357b58e0087b 100644
--- a/drivers/usb/gadget/s3c2410_udc.c
+++ b/drivers/usb/gadget/udc/s3c2410_udc.c
@@ -1788,7 +1788,7 @@ static int s3c2410_udc_probe(struct platform_device *pdev)
return PTR_ERR(usb_bus_clock);
}
- clk_enable(usb_bus_clock);
+ clk_prepare_enable(usb_bus_clock);
udc_clock = clk_get(NULL, "usb-device");
if (IS_ERR(udc_clock)) {
@@ -1796,7 +1796,7 @@ static int s3c2410_udc_probe(struct platform_device *pdev)
return PTR_ERR(udc_clock);
}
- clk_enable(udc_clock);
+ clk_prepare_enable(udc_clock);
mdelay(10);
@@ -1952,13 +1952,13 @@ static int s3c2410_udc_remove(struct platform_device *pdev)
release_mem_region(rsrc_start, rsrc_len);
if (!IS_ERR(udc_clock) && udc_clock != NULL) {
- clk_disable(udc_clock);
+ clk_disable_unprepare(udc_clock);
clk_put(udc_clock);
udc_clock = NULL;
}
if (!IS_ERR(usb_bus_clock) && usb_bus_clock != NULL) {
- clk_disable(usb_bus_clock);
+ clk_disable_unprepare(usb_bus_clock);
clk_put(usb_bus_clock);
usb_bus_clock = NULL;
}
diff --git a/drivers/usb/gadget/s3c2410_udc.h b/drivers/usb/gadget/udc/s3c2410_udc.h
index 93bf225f1969..93bf225f1969 100644
--- a/drivers/usb/gadget/s3c2410_udc.h
+++ b/drivers/usb/gadget/udc/s3c2410_udc.h
diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc/udc-core.c
index b0d98172bc07..b0d98172bc07 100644
--- a/drivers/usb/gadget/udc-core.c
+++ b/drivers/usb/gadget/udc/udc-core.c
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 03314f861bee..82800a775501 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -37,6 +37,14 @@ config USB_XHCI_MVEBU
Say 'Y' to enable the support for the xHCI host controller
found in Marvell Armada 375/38x ARM SOCs.
+config USB_XHCI_RCAR
+ tristate "xHCI support for Renesas R-Car SoCs"
+ select USB_XHCI_PLATFORM
+ depends on ARCH_SHMOBILE || COMPILE_TEST
+ ---help---
+ Say 'Y' to enable the support for the xHCI host controller
+ found in Renesas R-Car ARM SoCs.
+
endif # USB_XHCI_HCD
config USB_EHCI_HCD
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index af89a903d97e..144c038ef70f 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -22,6 +22,9 @@ ifneq ($(CONFIG_USB_XHCI_PLATFORM), )
ifneq ($(CONFIG_USB_XHCI_MVEBU), )
xhci-hcd-y += xhci-mvebu.o
endif
+ifneq ($(CONFIG_USB_XHCI_RCAR), )
+ xhci-hcd-y += xhci-rcar.o
+endif
endif
obj-$(CONFIG_USB_WHCI_HCD) += whci/
diff --git a/drivers/usb/host/ehci-exynos.c b/drivers/usb/host/ehci-exynos.c
index d1c76216350f..cda0a2f5c467 100644
--- a/drivers/usb/host/ehci-exynos.c
+++ b/drivers/usb/host/ehci-exynos.c
@@ -88,7 +88,7 @@ static int exynos_ehci_get_phy(struct device *dev,
return -EINVAL;
}
- phy = devm_of_phy_get(dev, child, 0);
+ phy = devm_of_phy_get(dev, child, NULL);
of_node_put(child);
if (IS_ERR(phy)) {
ret = PTR_ERR(phy);
diff --git a/drivers/usb/host/ehci-mem.c b/drivers/usb/host/ehci-mem.c
index c0fb6a8ae6a3..b6205fac3567 100644
--- a/drivers/usb/host/ehci-mem.c
+++ b/drivers/usb/host/ehci-mem.c
@@ -209,7 +209,7 @@ static int ehci_mem_init (struct ehci_hcd *ehci, gfp_t flags)
ehci->periodic = (__le32 *)
dma_alloc_coherent (ehci_to_hcd(ehci)->self.controller,
ehci->periodic_size * sizeof(__le32),
- &ehci->periodic_dma, 0);
+ &ehci->periodic_dma, flags);
if (ehci->periodic == NULL) {
goto fail;
}
diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c
index 982c09bebe0f..934b39d5e44a 100644
--- a/drivers/usb/host/ehci-msm.c
+++ b/drivers/usb/host/ehci-msm.c
@@ -190,7 +190,7 @@ static const struct dev_pm_ops ehci_msm_dev_pm_ops = {
.resume = ehci_msm_pm_resume,
};
-static struct of_device_id msm_ehci_dt_match[] = {
+static const struct of_device_id msm_ehci_dt_match[] = {
{ .compatible = "qcom,ehci-host", },
{}
};
diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c
index 3e86bf4371b3..ca7b964124af 100644
--- a/drivers/usb/host/ehci-pci.c
+++ b/drivers/usb/host/ehci-pci.c
@@ -35,6 +35,21 @@ static const char hcd_name[] = "ehci-pci";
#define PCI_DEVICE_ID_INTEL_CE4100_USB 0x2e70
/*-------------------------------------------------------------------------*/
+#define PCI_DEVICE_ID_INTEL_QUARK_X1000_SOC 0x0939
+static inline bool is_intel_quark_x1000(struct pci_dev *pdev)
+{
+ return pdev->vendor == PCI_VENDOR_ID_INTEL &&
+ pdev->device == PCI_DEVICE_ID_INTEL_QUARK_X1000_SOC;
+}
+
+/*
+ * 0x84 is the offset of in/out threshold register,
+ * and it is the same offset as the register of 'hostpc'.
+ */
+#define intel_quark_x1000_insnreg01 hostpc
+
+/* Maximum usable threshold value is 0x7f dwords for both IN and OUT */
+#define INTEL_QUARK_X1000_EHCI_MAX_THRESHOLD 0x007f007f
/* called after powerup, by probe or system-pm "wakeup" */
static int ehci_pci_reinit(struct ehci_hcd *ehci, struct pci_dev *pdev)
@@ -50,6 +65,16 @@ static int ehci_pci_reinit(struct ehci_hcd *ehci, struct pci_dev *pdev)
if (!retval)
ehci_dbg(ehci, "MWI active\n");
+ /* Reset the threshold limit */
+ if (is_intel_quark_x1000(pdev)) {
+ /*
+ * For the Intel QUARK X1000, raise the I/O threshold to the
+ * maximum usable value in order to improve performance.
+ */
+ ehci_writel(ehci, INTEL_QUARK_X1000_EHCI_MAX_THRESHOLD,
+ ehci->regs->intel_quark_x1000_insnreg01);
+ }
+
return 0;
}
diff --git a/drivers/usb/host/ehci-spear.c b/drivers/usb/host/ehci-spear.c
index 1d59958ad0ce..1355ff0946b9 100644
--- a/drivers/usb/host/ehci-spear.c
+++ b/drivers/usb/host/ehci-spear.c
@@ -150,7 +150,7 @@ static int spear_ehci_hcd_drv_remove(struct platform_device *pdev)
return 0;
}
-static struct of_device_id spear_ehci_id_table[] = {
+static const struct of_device_id spear_ehci_id_table[] = {
{ .compatible = "st,spear600-ehci", },
{ },
};
diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c
index 6fdcb8ad2296..7aafb05e7a40 100644
--- a/drivers/usb/host/ehci-tegra.c
+++ b/drivers/usb/host/ehci-tegra.c
@@ -46,6 +46,7 @@
#define DRV_NAME "tegra-ehci"
static struct hc_driver __read_mostly tegra_ehci_hc_driver;
+static bool usb1_reset_attempted;
struct tegra_ehci_soc_config {
bool has_hostpc;
@@ -60,6 +61,61 @@ struct tegra_ehci_hcd {
enum tegra_usb_phy_port_speed port_speed;
};
+/*
+ * The 1st USB controller contains some UTMI pad registers that are global for
+ * all the controllers on the chip. Those registers are also cleared when
+ * reset is asserted to the 1st controller. This means that the 1st controller
+ * can only be reset when no other controlled has finished probing. So we'll
+ * reset the 1st controller before doing any other setup on any of the
+ * controllers, and then never again.
+ *
+ * Since this is a PHY issue, the Tegra PHY driver should probably be doing
+ * the resetting of the USB controllers. But to keep compatibility with old
+ * device trees that don't have reset phandles in the PHYs, do it here.
+ * Those old DTs will be vulnerable to total USB breakage if the 1st EHCI
+ * device isn't the first one to finish probing, so warn them.
+ */
+static int tegra_reset_usb_controller(struct platform_device *pdev)
+{
+ struct device_node *phy_np;
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct tegra_ehci_hcd *tegra =
+ (struct tegra_ehci_hcd *)hcd_to_ehci(hcd)->priv;
+
+ phy_np = of_parse_phandle(pdev->dev.of_node, "nvidia,phy", 0);
+ if (!phy_np)
+ return -ENOENT;
+
+ if (!usb1_reset_attempted) {
+ struct reset_control *usb1_reset;
+
+ usb1_reset = of_reset_control_get(phy_np, "usb");
+ if (IS_ERR(usb1_reset)) {
+ dev_warn(&pdev->dev,
+ "can't get utmi-pads reset from the PHY\n");
+ dev_warn(&pdev->dev,
+ "continuing, but please update your DT\n");
+ } else {
+ reset_control_assert(usb1_reset);
+ udelay(1);
+ reset_control_deassert(usb1_reset);
+ }
+
+ reset_control_put(usb1_reset);
+ usb1_reset_attempted = true;
+ }
+
+ if (!of_property_read_bool(phy_np, "nvidia,has-utmi-pad-registers")) {
+ reset_control_assert(tegra->rst);
+ udelay(1);
+ reset_control_deassert(tegra->rst);
+ }
+
+ of_node_put(phy_np);
+
+ return 0;
+}
+
static int tegra_ehci_internal_port_reset(
struct ehci_hcd *ehci,
u32 __iomem *portsc_reg
@@ -326,7 +382,7 @@ static const struct tegra_ehci_soc_config tegra20_soc_config = {
.has_hostpc = false,
};
-static struct of_device_id tegra_ehci_of_match[] = {
+static const struct of_device_id tegra_ehci_of_match[] = {
{ .compatible = "nvidia,tegra30-ehci", .data = &tegra30_soc_config },
{ .compatible = "nvidia,tegra20-ehci", .data = &tegra20_soc_config },
{ },
@@ -389,9 +445,9 @@ static int tegra_ehci_probe(struct platform_device *pdev)
if (err)
goto cleanup_hcd_create;
- reset_control_assert(tegra->rst);
- udelay(1);
- reset_control_deassert(tegra->rst);
+ err = tegra_reset_usb_controller(pdev);
+ if (err)
+ goto cleanup_clk_en;
u_phy = devm_usb_get_phy_by_phandle(&pdev->dev, "nvidia,phy", 0);
if (IS_ERR(u_phy)) {
@@ -479,10 +535,11 @@ static int tegra_ehci_remove(struct platform_device *pdev)
usb_phy_shutdown(hcd->phy);
usb_remove_hcd(hcd);
- usb_put_hcd(hcd);
clk_disable_unprepare(tegra->clk);
+ usb_put_hcd(hcd);
+
return 0;
}
diff --git a/drivers/usb/host/fhci-dbg.c b/drivers/usb/host/fhci-dbg.c
index f238cb37305c..b58e7a60913a 100644
--- a/drivers/usb/host/fhci-dbg.c
+++ b/drivers/usb/host/fhci-dbg.c
@@ -129,11 +129,7 @@ void fhci_dfs_destroy(struct fhci_hcd *fhci)
if (!fhci->dfs_root)
return;
- if (fhci->dfs_irq_stat)
- debugfs_remove(fhci->dfs_irq_stat);
-
- if (fhci->dfs_regs)
- debugfs_remove(fhci->dfs_regs);
-
+ debugfs_remove(fhci->dfs_irq_stat);
+ debugfs_remove(fhci->dfs_regs);
debugfs_remove(fhci->dfs_root);
}
diff --git a/drivers/usb/host/fotg210-hcd.c b/drivers/usb/host/fotg210-hcd.c
index 98a89d16cc3e..adcd2050dced 100644
--- a/drivers/usb/host/fotg210-hcd.c
+++ b/drivers/usb/host/fotg210-hcd.c
@@ -5838,41 +5838,17 @@ static int fotg210_hcd_probe(struct platform_device *pdev)
goto fail_create_hcd;
}
+ hcd->has_tt = 1;
+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(dev,
- "Found HC with no register addr. Check %s setup!\n",
- dev_name(dev));
- retval = -ENODEV;
- goto fail_request_resource;
+ hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(hcd->regs)) {
+ retval = PTR_ERR(hcd->regs);
+ goto failed;
}
hcd->rsrc_start = res->start;
hcd->rsrc_len = resource_size(res);
- hcd->has_tt = 1;
-
- if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len,
- fotg210_fotg210_hc_driver.description)) {
- dev_dbg(dev, "controller already in use\n");
- retval = -EBUSY;
- goto fail_request_resource;
- }
-
- res = platform_get_resource(pdev, IORESOURCE_IO, 0);
- if (!res) {
- dev_err(dev,
- "Found HC with no register addr. Check %s setup!\n",
- dev_name(dev));
- retval = -ENODEV;
- goto fail_request_resource;
- }
-
- hcd->regs = ioremap_nocache(res->start, resource_size(res));
- if (hcd->regs == NULL) {
- dev_dbg(dev, "error mapping memory\n");
- retval = -EFAULT;
- goto fail_ioremap;
- }
fotg210 = hcd_to_fotg210(hcd);
@@ -5880,24 +5856,20 @@ static int fotg210_hcd_probe(struct platform_device *pdev)
retval = fotg210_setup(hcd);
if (retval)
- goto fail_add_hcd;
+ goto failed;
fotg210_init(fotg210);
retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
if (retval) {
dev_err(dev, "failed to add hcd with err %d\n", retval);
- goto fail_add_hcd;
+ goto failed;
}
device_wakeup_enable(hcd->self.controller);
return retval;
-fail_add_hcd:
- iounmap(hcd->regs);
-fail_ioremap:
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
-fail_request_resource:
+failed:
usb_put_hcd(hcd);
fail_create_hcd:
dev_err(dev, "init %s fail, %d\n", dev_name(dev), retval);
@@ -5918,8 +5890,6 @@ static int fotg210_hcd_remove(struct platform_device *pdev)
return 0;
usb_remove_hcd(hcd);
- iounmap(hcd->regs);
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
return 0;
diff --git a/drivers/usb/host/max3421-hcd.c b/drivers/usb/host/max3421-hcd.c
index 858efcfda50b..6234c75da33f 100644
--- a/drivers/usb/host/max3421-hcd.c
+++ b/drivers/usb/host/max3421-hcd.c
@@ -102,6 +102,15 @@ enum scheduling_pass {
SCHED_PASS_DONE
};
+/* Bit numbers for max3421_hcd->todo: */
+enum {
+ ENABLE_IRQ = 0,
+ RESET_HCD,
+ RESET_PORT,
+ CHECK_UNLINK,
+ IOPIN_UPDATE
+};
+
struct max3421_dma_buf {
u8 data[2];
};
@@ -146,11 +155,7 @@ struct max3421_hcd {
u8 hien;
u8 mode;
u8 iopins[2];
- unsigned int do_enable_irq:1;
- unsigned int do_reset_hcd:1;
- unsigned int do_reset_port:1;
- unsigned int do_check_unlink:1;
- unsigned int do_iopin_update:1;
+ unsigned long todo;
#ifdef DEBUG
unsigned long err_stat[16];
#endif
@@ -1165,10 +1170,8 @@ max3421_irq_handler(int irq, void *dev_id)
if (max3421_hcd->spi_thread &&
max3421_hcd->spi_thread->state != TASK_RUNNING)
wake_up_process(max3421_hcd->spi_thread);
- if (!max3421_hcd->do_enable_irq) {
- max3421_hcd->do_enable_irq = 1;
+ if (!test_and_set_bit(ENABLE_IRQ, &max3421_hcd->todo))
disable_irq_nosync(spi->irq);
- }
return IRQ_HANDLED;
}
@@ -1423,10 +1426,8 @@ max3421_spi_thread(void *dev_id)
spi_wr8(hcd, MAX3421_REG_HIEN, max3421_hcd->hien);
set_current_state(TASK_INTERRUPTIBLE);
- if (max3421_hcd->do_enable_irq) {
- max3421_hcd->do_enable_irq = 0;
+ if (test_and_clear_bit(ENABLE_IRQ, &max3421_hcd->todo))
enable_irq(spi->irq);
- }
schedule();
__set_current_state(TASK_RUNNING);
}
@@ -1440,23 +1441,18 @@ max3421_spi_thread(void *dev_id)
else if (!max3421_hcd->curr_urb)
i_worked |= max3421_select_and_start_urb(hcd);
- if (max3421_hcd->do_reset_hcd) {
+ if (test_and_clear_bit(RESET_HCD, &max3421_hcd->todo))
/* reset the HCD: */
- max3421_hcd->do_reset_hcd = 0;
i_worked |= max3421_reset_hcd(hcd);
- }
- if (max3421_hcd->do_reset_port) {
+ if (test_and_clear_bit(RESET_PORT, &max3421_hcd->todo)) {
/* perform a USB bus reset: */
- max3421_hcd->do_reset_port = 0;
spi_wr8(hcd, MAX3421_REG_HCTL,
BIT(MAX3421_HCTL_BUSRST_BIT));
i_worked = 1;
}
- if (max3421_hcd->do_check_unlink) {
- max3421_hcd->do_check_unlink = 0;
+ if (test_and_clear_bit(CHECK_UNLINK, &max3421_hcd->todo))
i_worked |= max3421_check_unlink(hcd);
- }
- if (max3421_hcd->do_iopin_update) {
+ if (test_and_clear_bit(IOPIN_UPDATE, &max3421_hcd->todo)) {
/*
* IOPINS1/IOPINS2 do not auto-increment, so we can't
* use spi_wr_buf().
@@ -1469,7 +1465,6 @@ max3421_spi_thread(void *dev_id)
spi_wr8(hcd, MAX3421_REG_IOPINS1 + i, val);
max3421_hcd->iopins[i] = val;
}
- max3421_hcd->do_iopin_update = 0;
i_worked = 1;
}
}
@@ -1485,7 +1480,8 @@ max3421_reset_port(struct usb_hcd *hcd)
max3421_hcd->port_status &= ~(USB_PORT_STAT_ENABLE |
USB_PORT_STAT_LOW_SPEED);
- max3421_hcd->do_reset_port = 1;
+ max3421_hcd->port_status |= USB_PORT_STAT_RESET;
+ set_bit(RESET_PORT, &max3421_hcd->todo);
wake_up_process(max3421_hcd->spi_thread);
return 0;
}
@@ -1498,7 +1494,7 @@ max3421_reset(struct usb_hcd *hcd)
hcd->self.sg_tablesize = 0;
hcd->speed = HCD_USB2;
hcd->self.root_hub->speed = USB_SPEED_FULL;
- max3421_hcd->do_reset_hcd = 1;
+ set_bit(RESET_HCD, &max3421_hcd->todo);
wake_up_process(max3421_hcd->spi_thread);
return 0;
}
@@ -1551,7 +1547,7 @@ max3421_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags)
max3421_ep = urb->ep->hcpriv;
if (!max3421_ep) {
/* gets freed in max3421_endpoint_disable: */
- max3421_ep = kzalloc(sizeof(struct max3421_ep), mem_flags);
+ max3421_ep = kzalloc(sizeof(struct max3421_ep), GFP_ATOMIC);
if (!max3421_ep) {
retval = -ENOMEM;
goto out;
@@ -1590,7 +1586,7 @@ max3421_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
*/
retval = usb_hcd_check_unlink_urb(hcd, urb, status);
if (retval == 0) {
- max3421_hcd->do_check_unlink = 1;
+ set_bit(CHECK_UNLINK, &max3421_hcd->todo);
wake_up_process(max3421_hcd->spi_thread);
}
spin_unlock_irqrestore(&max3421_hcd->lock, flags);
@@ -1690,7 +1686,7 @@ max3421_gpout_set_value(struct usb_hcd *hcd, u8 pin_number, u8 value)
max3421_hcd->iopins[idx] |= mask;
else
max3421_hcd->iopins[idx] &= ~mask;
- max3421_hcd->do_iopin_update = 1;
+ set_bit(IOPIN_UPDATE, &max3421_hcd->todo);
wake_up_process(max3421_hcd->spi_thread);
}
diff --git a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c
index 45032e933e18..04f2186939d2 100644
--- a/drivers/usb/host/ohci-dbg.c
+++ b/drivers/usb/host/ohci-dbg.c
@@ -236,7 +236,7 @@ ohci_dump_roothub (
}
}
-static void ohci_dump (struct ohci_hcd *controller, int verbose)
+static void ohci_dump(struct ohci_hcd *controller)
{
ohci_dbg (controller, "OHCI controller state\n");
@@ -464,15 +464,16 @@ show_list (struct ohci_hcd *ohci, char *buf, size_t count, struct ed *ed)
static ssize_t fill_async_buffer(struct debug_buffer *buf)
{
struct ohci_hcd *ohci;
- size_t temp;
+ size_t temp, size;
unsigned long flags;
ohci = buf->ohci;
+ size = PAGE_SIZE;
/* display control and bulk lists together, for simplicity */
spin_lock_irqsave (&ohci->lock, flags);
- temp = show_list(ohci, buf->page, buf->count, ohci->ed_controltail);
- temp += show_list(ohci, buf->page + temp, buf->count - temp,
+ temp = show_list(ohci, buf->page, size, ohci->ed_controltail);
+ temp += show_list(ohci, buf->page + temp, size - temp,
ohci->ed_bulktail);
spin_unlock_irqrestore (&ohci->lock, flags);
diff --git a/drivers/usb/host/ohci-exynos.c b/drivers/usb/host/ohci-exynos.c
index 060a6a414750..a72ab8fe8cd3 100644
--- a/drivers/usb/host/ohci-exynos.c
+++ b/drivers/usb/host/ohci-exynos.c
@@ -87,7 +87,7 @@ static int exynos_ohci_get_phy(struct device *dev,
return -EINVAL;
}
- phy = devm_of_phy_get(dev, child, 0);
+ phy = devm_of_phy_get(dev, child, NULL);
of_node_put(child);
if (IS_ERR(phy)) {
ret = PTR_ERR(phy);
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index f98d03f3144c..46987735a2e3 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -72,12 +72,14 @@
static const char hcd_name [] = "ohci_hcd";
#define STATECHANGE_DELAY msecs_to_jiffies(300)
+#define IO_WATCHDOG_DELAY msecs_to_jiffies(250)
#include "ohci.h"
#include "pci-quirks.h"
-static void ohci_dump (struct ohci_hcd *ohci, int verbose);
-static void ohci_stop (struct usb_hcd *hcd);
+static void ohci_dump(struct ohci_hcd *ohci);
+static void ohci_stop(struct usb_hcd *hcd);
+static void io_watchdog_func(unsigned long _ohci);
#include "ohci-hub.c"
#include "ohci-dbg.c"
@@ -109,6 +111,33 @@ MODULE_PARM_DESC (no_handshake, "true (not default) disables BIOS handshake");
/*-------------------------------------------------------------------------*/
+static int number_of_tds(struct urb *urb)
+{
+ int len, i, num, this_sg_len;
+ struct scatterlist *sg;
+
+ len = urb->transfer_buffer_length;
+ i = urb->num_mapped_sgs;
+
+ if (len > 0 && i > 0) { /* Scatter-gather transfer */
+ num = 0;
+ sg = urb->sg;
+ for (;;) {
+ this_sg_len = min_t(int, sg_dma_len(sg), len);
+ num += DIV_ROUND_UP(this_sg_len, 4096);
+ len -= this_sg_len;
+ if (--i <= 0 || len <= 0)
+ break;
+ sg = sg_next(sg);
+ }
+
+ } else { /* Non-SG transfer */
+ /* one TD for every 4096 Bytes (could be up to 8K) */
+ num = DIV_ROUND_UP(len, 4096);
+ }
+ return num;
+}
+
/*
* queue up an urb for anything except the root hub
*/
@@ -142,12 +171,8 @@ static int ohci_urb_enqueue (
// case PIPE_INTERRUPT:
// case PIPE_BULK:
default:
- /* one TD for every 4096 Bytes (can be up to 8K) */
- size += urb->transfer_buffer_length / 4096;
- /* ... and for any remaining bytes ... */
- if ((urb->transfer_buffer_length % 4096) != 0)
- size++;
- /* ... and maybe a zero length packet to wrap it up */
+ size += number_of_tds(urb);
+ /* maybe a zero-length packet to wrap it up */
if (size == 0)
size++;
else if ((urb->transfer_flags & URB_ZERO_PACKET) != 0
@@ -202,6 +227,16 @@ static int ohci_urb_enqueue (
usb_hcd_unlink_urb_from_ep(hcd, urb);
goto fail;
}
+
+ /* Start up the I/O watchdog timer, if it's not running */
+ if (!timer_pending(&ohci->io_watchdog) &&
+ list_empty(&ohci->eds_in_use)) {
+ ohci->prev_frame_no = ohci_frame_no(ohci);
+ mod_timer(&ohci->io_watchdog,
+ jiffies + IO_WATCHDOG_DELAY);
+ }
+ list_add(&ed->in_use_list, &ohci->eds_in_use);
+
if (ed->type == PIPE_ISOCHRONOUS) {
u16 frame = ohci_frame_no(ohci);
@@ -277,30 +312,24 @@ static int ohci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
unsigned long flags;
int rc;
+ urb_priv_t *urb_priv;
spin_lock_irqsave (&ohci->lock, flags);
rc = usb_hcd_check_unlink_urb(hcd, urb, status);
- if (rc) {
- ; /* Do nothing */
- } else if (ohci->rh_state == OHCI_RH_RUNNING) {
- urb_priv_t *urb_priv;
+ if (rc == 0) {
/* Unless an IRQ completed the unlink while it was being
* handed to us, flag it for unlink and giveback, and force
* some upcoming INTR_SF to call finish_unlinks()
*/
urb_priv = urb->hcpriv;
- if (urb_priv) {
- if (urb_priv->ed->state == ED_OPER)
- start_ed_unlink (ohci, urb_priv->ed);
+ if (urb_priv->ed->state == ED_OPER)
+ start_ed_unlink(ohci, urb_priv->ed);
+
+ if (ohci->rh_state != OHCI_RH_RUNNING) {
+ /* With HC dead, we can clean up right away */
+ ohci_work(ohci);
}
- } else {
- /*
- * with HC dead, we won't respect hc queue pointers
- * any more ... just clean up every urb's memory.
- */
- if (urb->hcpriv)
- finish_urb(ohci, urb, status);
}
spin_unlock_irqrestore (&ohci->lock, flags);
return rc;
@@ -332,9 +361,7 @@ rescan:
if (ohci->rh_state != OHCI_RH_RUNNING) {
sanitize:
ed->state = ED_IDLE;
- if (quirk_zfmicro(ohci) && ed->type == PIPE_INTERRUPT)
- ohci->eds_scheduled--;
- finish_unlinks (ohci, 0);
+ ohci_work(ohci);
}
switch (ed->state) {
@@ -342,11 +369,6 @@ sanitize:
/* major IRQ delivery trouble loses INTR_SF too... */
if (limit-- == 0) {
ohci_warn(ohci, "ED unlink timeout\n");
- if (quirk_zfmicro(ohci)) {
- ohci_warn(ohci, "Attempting ZF TD recovery\n");
- ohci->ed_to_check = ed;
- ohci->zf_delay = 2;
- }
goto sanitize;
}
spin_unlock_irqrestore (&ohci->lock, flags);
@@ -406,93 +428,7 @@ ohci_shutdown (struct usb_hcd *hcd)
udelay(10);
ohci_writel(ohci, ohci->fminterval, &ohci->regs->fminterval);
-}
-
-static int check_ed(struct ohci_hcd *ohci, struct ed *ed)
-{
- return (hc32_to_cpu(ohci, ed->hwINFO) & ED_IN) != 0
- && (hc32_to_cpu(ohci, ed->hwHeadP) & TD_MASK)
- == (hc32_to_cpu(ohci, ed->hwTailP) & TD_MASK)
- && !list_empty(&ed->td_list);
-}
-
-/* ZF Micro watchdog timer callback. The ZF Micro chipset sometimes completes
- * an interrupt TD but neglects to add it to the donelist. On systems with
- * this chipset, we need to periodically check the state of the queues to look
- * for such "lost" TDs.
- */
-static void unlink_watchdog_func(unsigned long _ohci)
-{
- unsigned long flags;
- unsigned max;
- unsigned seen_count = 0;
- unsigned i;
- struct ed **seen = NULL;
- struct ohci_hcd *ohci = (struct ohci_hcd *) _ohci;
-
- spin_lock_irqsave(&ohci->lock, flags);
- max = ohci->eds_scheduled;
- if (!max)
- goto done;
-
- if (ohci->ed_to_check)
- goto out;
-
- seen = kcalloc(max, sizeof *seen, GFP_ATOMIC);
- if (!seen)
- goto out;
-
- for (i = 0; i < NUM_INTS; i++) {
- struct ed *ed = ohci->periodic[i];
-
- while (ed) {
- unsigned temp;
-
- /* scan this branch of the periodic schedule tree */
- for (temp = 0; temp < seen_count; temp++) {
- if (seen[temp] == ed) {
- /* we've checked it and what's after */
- ed = NULL;
- break;
- }
- }
- if (!ed)
- break;
- seen[seen_count++] = ed;
- if (!check_ed(ohci, ed)) {
- ed = ed->ed_next;
- continue;
- }
-
- /* HC's TD list is empty, but HCD sees at least one
- * TD that's not been sent through the donelist.
- */
- ohci->ed_to_check = ed;
- ohci->zf_delay = 2;
-
- /* The HC may wait until the next frame to report the
- * TD as done through the donelist and INTR_WDH. (We
- * just *assume* it's not a multi-TD interrupt URB;
- * those could defer the IRQ more than one frame, using
- * DI...) Check again after the next INTR_SF.
- */
- ohci_writel(ohci, OHCI_INTR_SF,
- &ohci->regs->intrstatus);
- ohci_writel(ohci, OHCI_INTR_SF,
- &ohci->regs->intrenable);
-
- /* flush those writes */
- (void) ohci_readl(ohci, &ohci->regs->control);
-
- goto out;
- }
- }
-out:
- kfree(seen);
- if (ohci->eds_scheduled)
- mod_timer(&ohci->unlink_watchdog, round_jiffies(jiffies + HZ));
-done:
- spin_unlock_irqrestore(&ohci->lock, flags);
+ ohci->rh_state = OHCI_RH_HALTED;
}
/*-------------------------------------------------------------------------*
@@ -506,6 +442,9 @@ static int ohci_init (struct ohci_hcd *ohci)
int ret;
struct usb_hcd *hcd = ohci_to_hcd(ohci);
+ /* Accept arbitrarily long scatter-gather lists */
+ hcd->self.sg_tablesize = ~0;
+
if (distrust_firmware)
ohci->flags |= OHCI_QUIRK_HUB_POWER;
@@ -558,8 +497,12 @@ static int ohci_init (struct ohci_hcd *ohci)
if (ohci->hcca)
return 0;
+ setup_timer(&ohci->io_watchdog, io_watchdog_func,
+ (unsigned long) ohci);
+ set_timer_slack(&ohci->io_watchdog, msecs_to_jiffies(20));
+
ohci->hcca = dma_alloc_coherent (hcd->self.controller,
- sizeof *ohci->hcca, &ohci->hcca_dma, 0);
+ sizeof(*ohci->hcca), &ohci->hcca_dma, GFP_KERNEL);
if (!ohci->hcca)
return -ENOMEM;
@@ -735,16 +678,7 @@ retry:
// POTPGT delay is bits 24-31, in 2 ms units.
mdelay ((val >> 23) & 0x1fe);
- if (quirk_zfmicro(ohci)) {
- /* Create timer to watch for bad queue state on ZF Micro */
- setup_timer(&ohci->unlink_watchdog, unlink_watchdog_func,
- (unsigned long) ohci);
-
- ohci->eds_scheduled = 0;
- ohci->ed_to_check = NULL;
- }
-
- ohci_dump (ohci, 1);
+ ohci_dump(ohci);
return 0;
}
@@ -777,6 +711,142 @@ static int ohci_start(struct usb_hcd *hcd)
/*-------------------------------------------------------------------------*/
+/*
+ * Some OHCI controllers are known to lose track of completed TDs. They
+ * don't add the TDs to the hardware done queue, which means we never see
+ * them as being completed.
+ *
+ * This watchdog routine checks for such problems. Without some way to
+ * tell when those TDs have completed, we would never take their EDs off
+ * the unlink list. As a result, URBs could never be dequeued and
+ * endpoints could never be released.
+ */
+static void io_watchdog_func(unsigned long _ohci)
+{
+ struct ohci_hcd *ohci = (struct ohci_hcd *) _ohci;
+ bool takeback_all_pending = false;
+ u32 status;
+ u32 head;
+ struct ed *ed;
+ struct td *td, *td_start, *td_next;
+ unsigned frame_no;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ohci->lock, flags);
+
+ /*
+ * One way to lose track of completed TDs is if the controller
+ * never writes back the done queue head. If it hasn't been
+ * written back since the last time this function ran and if it
+ * was non-empty at that time, something is badly wrong with the
+ * hardware.
+ */
+ status = ohci_readl(ohci, &ohci->regs->intrstatus);
+ if (!(status & OHCI_INTR_WDH) && ohci->wdh_cnt == ohci->prev_wdh_cnt) {
+ if (ohci->prev_donehead) {
+ ohci_err(ohci, "HcDoneHead not written back; disabled\n");
+ died:
+ usb_hc_died(ohci_to_hcd(ohci));
+ ohci_dump(ohci);
+ ohci_shutdown(ohci_to_hcd(ohci));
+ goto done;
+ } else {
+ /* No write back because the done queue was empty */
+ takeback_all_pending = true;
+ }
+ }
+
+ /* Check every ED which might have pending TDs */
+ list_for_each_entry(ed, &ohci->eds_in_use, in_use_list) {
+ if (ed->pending_td) {
+ if (takeback_all_pending ||
+ OKAY_TO_TAKEBACK(ohci, ed)) {
+ unsigned tmp = hc32_to_cpu(ohci, ed->hwINFO);
+
+ ohci_dbg(ohci, "takeback pending TD for dev %d ep 0x%x\n",
+ 0x007f & tmp,
+ (0x000f & (tmp >> 7)) +
+ ((tmp & ED_IN) >> 5));
+ add_to_done_list(ohci, ed->pending_td);
+ }
+ }
+
+ /* Starting from the latest pending TD, */
+ td = ed->pending_td;
+
+ /* or the last TD on the done list, */
+ if (!td) {
+ list_for_each_entry(td_next, &ed->td_list, td_list) {
+ if (!td_next->next_dl_td)
+ break;
+ td = td_next;
+ }
+ }
+
+ /* find the last TD processed by the controller. */
+ head = hc32_to_cpu(ohci, ACCESS_ONCE(ed->hwHeadP)) & TD_MASK;
+ td_start = td;
+ td_next = list_prepare_entry(td, &ed->td_list, td_list);
+ list_for_each_entry_continue(td_next, &ed->td_list, td_list) {
+ if (head == (u32) td_next->td_dma)
+ break;
+ td = td_next; /* head pointer has passed this TD */
+ }
+ if (td != td_start) {
+ /*
+ * In case a WDH cycle is in progress, we will wait
+ * for the next two cycles to complete before assuming
+ * this TD will never get on the done queue.
+ */
+ ed->takeback_wdh_cnt = ohci->wdh_cnt + 2;
+ ed->pending_td = td;
+ }
+ }
+
+ ohci_work(ohci);
+
+ if (ohci->rh_state == OHCI_RH_RUNNING) {
+
+ /*
+ * Sometimes a controller just stops working. We can tell
+ * by checking that the frame counter has advanced since
+ * the last time we ran.
+ *
+ * But be careful: Some controllers violate the spec by
+ * stopping their frame counter when no ports are active.
+ */
+ frame_no = ohci_frame_no(ohci);
+ if (frame_no == ohci->prev_frame_no) {
+ int active_cnt = 0;
+ int i;
+ unsigned tmp;
+
+ for (i = 0; i < ohci->num_ports; ++i) {
+ tmp = roothub_portstatus(ohci, i);
+ /* Enabled and not suspended? */
+ if ((tmp & RH_PS_PES) && !(tmp & RH_PS_PSS))
+ ++active_cnt;
+ }
+
+ if (active_cnt > 0) {
+ ohci_err(ohci, "frame counter not updating; disabled\n");
+ goto died;
+ }
+ }
+ if (!list_empty(&ohci->eds_in_use)) {
+ ohci->prev_frame_no = frame_no;
+ ohci->prev_wdh_cnt = ohci->wdh_cnt;
+ ohci->prev_donehead = ohci_readl(ohci,
+ &ohci->regs->donehead);
+ mod_timer(&ohci->io_watchdog,
+ jiffies + IO_WATCHDOG_DELAY);
+ }
+ }
+
+ done:
+ spin_unlock_irqrestore(&ohci->lock, flags);
+}
+
/* an interrupt happens */
static irqreturn_t ohci_irq (struct usb_hcd *hcd)
@@ -825,7 +895,7 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd)
usb_hc_died(hcd);
}
- ohci_dump (ohci, 1);
+ ohci_dump(ohci);
ohci_usb_reset (ohci);
}
@@ -863,58 +933,30 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd)
usb_hcd_resume_root_hub(hcd);
}
- if (ints & OHCI_INTR_WDH) {
- spin_lock (&ohci->lock);
- dl_done_list (ohci);
- spin_unlock (&ohci->lock);
- }
-
- if (quirk_zfmicro(ohci) && (ints & OHCI_INTR_SF)) {
- spin_lock(&ohci->lock);
- if (ohci->ed_to_check) {
- struct ed *ed = ohci->ed_to_check;
-
- if (check_ed(ohci, ed)) {
- /* HC thinks the TD list is empty; HCD knows
- * at least one TD is outstanding
- */
- if (--ohci->zf_delay == 0) {
- struct td *td = list_entry(
- ed->td_list.next,
- struct td, td_list);
- ohci_warn(ohci,
- "Reclaiming orphan TD %p\n",
- td);
- takeback_td(ohci, td);
- ohci->ed_to_check = NULL;
- }
- } else
- ohci->ed_to_check = NULL;
- }
- spin_unlock(&ohci->lock);
- }
+ spin_lock(&ohci->lock);
+ if (ints & OHCI_INTR_WDH)
+ update_done_list(ohci);
/* could track INTR_SO to reduce available PCI/... bandwidth */
/* handle any pending URB/ED unlinks, leaving INTR_SF enabled
* when there's still unlinking to be done (next frame).
*/
- spin_lock (&ohci->lock);
- if (ohci->ed_rm_list)
- finish_unlinks (ohci, ohci_frame_no(ohci));
- if ((ints & OHCI_INTR_SF) != 0
- && !ohci->ed_rm_list
- && !ohci->ed_to_check
+ ohci_work(ohci);
+ if ((ints & OHCI_INTR_SF) != 0 && !ohci->ed_rm_list
&& ohci->rh_state == OHCI_RH_RUNNING)
ohci_writel (ohci, OHCI_INTR_SF, &regs->intrdisable);
- spin_unlock (&ohci->lock);
if (ohci->rh_state == OHCI_RH_RUNNING) {
ohci_writel (ohci, ints, &regs->intrstatus);
+ if (ints & OHCI_INTR_WDH)
+ ++ohci->wdh_cnt;
+
ohci_writel (ohci, OHCI_INTR_MIE, &regs->intrenable);
// flush those writes
(void) ohci_readl (ohci, &ohci->regs->control);
}
+ spin_unlock(&ohci->lock);
return IRQ_HANDLED;
}
@@ -925,18 +967,17 @@ static void ohci_stop (struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
- ohci_dump (ohci, 1);
+ ohci_dump(ohci);
if (quirk_nec(ohci))
flush_work(&ohci->nec_work);
+ del_timer_sync(&ohci->io_watchdog);
ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable);
ohci_usb_reset(ohci);
free_irq(hcd->irq, hcd);
hcd->irq = 0;
- if (quirk_zfmicro(ohci))
- del_timer(&ohci->unlink_watchdog);
if (quirk_amdiso(ohci))
usb_amd_dev_put();
@@ -993,7 +1034,7 @@ int ohci_restart(struct ohci_hcd *ohci)
if (!urb->unlinked)
urb->unlinked = -ESHUTDOWN;
}
- finish_unlinks (ohci, 0);
+ ohci_work(ohci);
spin_unlock_irq(&ohci->lock);
/* paranoia, in case that didn't work: */
diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c
index b4940de1eba3..17d32b0ea565 100644
--- a/drivers/usb/host/ohci-hub.c
+++ b/drivers/usb/host/ohci-hub.c
@@ -39,8 +39,8 @@
#define OHCI_SCHED_ENABLES \
(OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_PLE|OHCI_CTRL_IE)
-static void dl_done_list (struct ohci_hcd *);
-static void finish_unlinks (struct ohci_hcd *, u16);
+static void update_done_list(struct ohci_hcd *);
+static void ohci_work(struct ohci_hcd *);
#ifdef CONFIG_PM
static int ohci_rh_suspend (struct ohci_hcd *ohci, int autostop)
@@ -87,8 +87,8 @@ __acquires(ohci->lock)
msleep (8);
spin_lock_irq (&ohci->lock);
}
- dl_done_list (ohci);
- finish_unlinks (ohci, ohci_frame_no(ohci));
+ update_done_list(ohci);
+ ohci_work(ohci);
/*
* Some controllers don't handle "global" suspend properly if
@@ -309,6 +309,9 @@ static int ohci_bus_suspend (struct usb_hcd *hcd)
else
rc = ohci_rh_suspend (ohci, 0);
spin_unlock_irq (&ohci->lock);
+
+ if (rc == 0)
+ del_timer_sync(&ohci->io_watchdog);
return rc;
}
diff --git a/drivers/usb/host/ohci-mem.c b/drivers/usb/host/ohci-mem.c
index 2f20d3dc895b..c9e315c6808a 100644
--- a/drivers/usb/host/ohci-mem.c
+++ b/drivers/usb/host/ohci-mem.c
@@ -28,6 +28,7 @@ static void ohci_hcd_init (struct ohci_hcd *ohci)
ohci->next_statechange = jiffies;
spin_lock_init (&ohci->lock);
INIT_LIST_HEAD (&ohci->pending);
+ INIT_LIST_HEAD(&ohci->eds_in_use);
}
/*-------------------------------------------------------------------------*/
diff --git a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c
index d4253e319428..1463c398d322 100644
--- a/drivers/usb/host/ohci-q.c
+++ b/drivers/usb/host/ohci-q.c
@@ -187,10 +187,6 @@ static int ed_schedule (struct ohci_hcd *ohci, struct ed *ed)
ed->ed_prev = NULL;
ed->ed_next = NULL;
ed->hwNextED = 0;
- if (quirk_zfmicro(ohci)
- && (ed->type == PIPE_INTERRUPT)
- && !(ohci->eds_scheduled++))
- mod_timer(&ohci->unlink_watchdog, round_jiffies(jiffies + HZ));
wmb ();
/* we care about rm_list when setting CLE/BLE in case the HC was at
@@ -311,8 +307,7 @@ static void periodic_unlink (struct ohci_hcd *ohci, struct ed *ed)
* - ED_OPER: when there's any request queued, the ED gets rescheduled
* immediately. HC should be working on them.
*
- * - ED_IDLE: when there's no TD queue. there's no reason for the HC
- * to care about this ED; safe to disable the endpoint.
+ * - ED_IDLE: when there's no TD queue or the HC isn't running.
*
* When finish_unlinks() runs later, after SOF interrupt, it will often
* complete one or more URB unlinks before making that state change.
@@ -602,6 +597,8 @@ static void td_submit_urb (
u32 info = 0;
int is_out = usb_pipeout (urb->pipe);
int periodic = 0;
+ int i, this_sg_len, n;
+ struct scatterlist *sg;
/* OHCI handles the bulk/interrupt data toggles itself. We just
* use the device toggle bits for resetting, and rely on the fact
@@ -615,10 +612,24 @@ static void td_submit_urb (
list_add (&urb_priv->pending, &ohci->pending);
- if (data_len)
- data = urb->transfer_dma;
- else
- data = 0;
+ i = urb->num_mapped_sgs;
+ if (data_len > 0 && i > 0) {
+ sg = urb->sg;
+ data = sg_dma_address(sg);
+
+ /*
+ * urb->transfer_buffer_length may be smaller than the
+ * size of the scatterlist (or vice versa)
+ */
+ this_sg_len = min_t(int, sg_dma_len(sg), data_len);
+ } else {
+ sg = NULL;
+ if (data_len)
+ data = urb->transfer_dma;
+ else
+ data = 0;
+ this_sg_len = data_len;
+ }
/* NOTE: TD_CC is set so we can tell which TDs the HC processed by
* using TD_CC_GET, as well as by seeing them on the done list.
@@ -639,17 +650,29 @@ static void td_submit_urb (
? TD_T_TOGGLE | TD_CC | TD_DP_OUT
: TD_T_TOGGLE | TD_CC | TD_DP_IN;
/* TDs _could_ transfer up to 8K each */
- while (data_len > 4096) {
- td_fill (ohci, info, data, 4096, urb, cnt);
- data += 4096;
- data_len -= 4096;
+ for (;;) {
+ n = min(this_sg_len, 4096);
+
+ /* maybe avoid ED halt on final TD short read */
+ if (n >= data_len || (i == 1 && n >= this_sg_len)) {
+ if (!(urb->transfer_flags & URB_SHORT_NOT_OK))
+ info |= TD_R;
+ }
+ td_fill(ohci, info, data, n, urb, cnt);
+ this_sg_len -= n;
+ data_len -= n;
+ data += n;
cnt++;
+
+ if (this_sg_len <= 0) {
+ if (--i <= 0 || data_len <= 0)
+ break;
+ sg = sg_next(sg);
+ data = sg_dma_address(sg);
+ this_sg_len = min_t(int, sg_dma_len(sg),
+ data_len);
+ }
}
- /* maybe avoid ED halt on final TD short read */
- if (!(urb->transfer_flags & URB_SHORT_NOT_OK))
- info |= TD_R;
- td_fill (ohci, info, data, data_len, urb, cnt);
- cnt++;
if ((urb->transfer_flags & URB_ZERO_PACKET)
&& cnt < urb_priv->length) {
td_fill (ohci, info, 0, 0, urb, cnt);
@@ -869,13 +892,46 @@ static void ed_halted(struct ohci_hcd *ohci, struct td *td, int cc)
}
}
-/* replies to the request have to be on a FIFO basis so
- * we unreverse the hc-reversed done-list
- */
-static struct td *dl_reverse_done_list (struct ohci_hcd *ohci)
+/* Add a TD to the done list */
+static void add_to_done_list(struct ohci_hcd *ohci, struct td *td)
+{
+ struct td *td2, *td_prev;
+ struct ed *ed;
+
+ if (td->next_dl_td)
+ return; /* Already on the list */
+
+ /* Add all the TDs going back until we reach one that's on the list */
+ ed = td->ed;
+ td2 = td_prev = td;
+ list_for_each_entry_continue_reverse(td2, &ed->td_list, td_list) {
+ if (td2->next_dl_td)
+ break;
+ td2->next_dl_td = td_prev;
+ td_prev = td2;
+ }
+
+ if (ohci->dl_end)
+ ohci->dl_end->next_dl_td = td_prev;
+ else
+ ohci->dl_start = td_prev;
+
+ /*
+ * Make td->next_dl_td point to td itself, to mark the fact
+ * that td is on the done list.
+ */
+ ohci->dl_end = td->next_dl_td = td;
+
+ /* Did we just add the latest pending TD? */
+ td2 = ed->pending_td;
+ if (td2 && td2->next_dl_td)
+ ed->pending_td = NULL;
+}
+
+/* Get the entries on the hardware done queue and put them on our list */
+static void update_done_list(struct ohci_hcd *ohci)
{
u32 td_dma;
- struct td *td_rev = NULL;
struct td *td = NULL;
td_dma = hc32_to_cpup (ohci, &ohci->hcca->done_head);
@@ -883,7 +939,7 @@ static struct td *dl_reverse_done_list (struct ohci_hcd *ohci)
wmb();
/* get TD from hc's singly linked list, and
- * prepend to ours. ed->td_list changes later.
+ * add to ours. ed->td_list changes later.
*/
while (td_dma) {
int cc;
@@ -905,19 +961,17 @@ static struct td *dl_reverse_done_list (struct ohci_hcd *ohci)
&& (td->ed->hwHeadP & cpu_to_hc32 (ohci, ED_H)))
ed_halted(ohci, td, cc);
- td->next_dl_td = td_rev;
- td_rev = td;
td_dma = hc32_to_cpup (ohci, &td->hwNextTD);
+ add_to_done_list(ohci, td);
}
- return td_rev;
}
/*-------------------------------------------------------------------------*/
/* there are some urbs/eds to unlink; called in_irq(), with HCD locked */
-static void
-finish_unlinks (struct ohci_hcd *ohci, u16 tick)
+static void finish_unlinks(struct ohci_hcd *ohci)
{
+ unsigned tick = ohci_frame_no(ohci);
struct ed *ed, **last;
rescan_all:
@@ -926,41 +980,48 @@ rescan_all:
int completed, modified;
__hc32 *prev;
+ /* Is this ED already invisible to the hardware? */
+ if (ed->state == ED_IDLE)
+ goto ed_idle;
+
/* only take off EDs that the HC isn't using, accounting for
* frame counter wraps and EDs with partially retired TDs
*/
- if (likely(ohci->rh_state == OHCI_RH_RUNNING)) {
- if (tick_before (tick, ed->tick)) {
+ if (likely(ohci->rh_state == OHCI_RH_RUNNING) &&
+ tick_before(tick, ed->tick)) {
skip_ed:
- last = &ed->ed_next;
- continue;
- }
+ last = &ed->ed_next;
+ continue;
+ }
+ if (!list_empty(&ed->td_list)) {
+ struct td *td;
+ u32 head;
- if (!list_empty (&ed->td_list)) {
- struct td *td;
- u32 head;
-
- td = list_entry (ed->td_list.next, struct td,
- td_list);
- head = hc32_to_cpu (ohci, ed->hwHeadP) &
- TD_MASK;
-
- /* INTR_WDH may need to clean up first */
- if (td->td_dma != head) {
- if (ed == ohci->ed_to_check)
- ohci->ed_to_check = NULL;
- else
- goto skip_ed;
- }
- }
+ td = list_first_entry(&ed->td_list, struct td, td_list);
+
+ /* INTR_WDH may need to clean up first */
+ head = hc32_to_cpu(ohci, ed->hwHeadP) & TD_MASK;
+ if (td->td_dma != head &&
+ ohci->rh_state == OHCI_RH_RUNNING)
+ goto skip_ed;
+
+ /* Don't mess up anything already on the done list */
+ if (td->next_dl_td)
+ goto skip_ed;
}
+ /* ED's now officially unlinked, hc doesn't see */
+ ed->state = ED_IDLE;
+ ed->hwHeadP &= ~cpu_to_hc32(ohci, ED_H);
+ ed->hwNextED = 0;
+ wmb();
+ ed->hwINFO &= ~cpu_to_hc32(ohci, ED_SKIP | ED_DEQUEUE);
+ed_idle:
+
/* reentrancy: if we drop the schedule lock, someone might
* have modified this list. normally it's just prepending
* entries (which we'd ignore), but paranoia won't hurt.
*/
- *last = ed->ed_next;
- ed->ed_next = NULL;
modified = 0;
/* unlink urbs as requested, but rescan the list after
@@ -1018,19 +1079,21 @@ rescan_this:
if (completed && !list_empty (&ed->td_list))
goto rescan_this;
- /* ED's now officially unlinked, hc doesn't see */
- ed->state = ED_IDLE;
- if (quirk_zfmicro(ohci) && ed->type == PIPE_INTERRUPT)
- ohci->eds_scheduled--;
- ed->hwHeadP &= ~cpu_to_hc32(ohci, ED_H);
- ed->hwNextED = 0;
- wmb ();
- ed->hwINFO &= ~cpu_to_hc32 (ohci, ED_SKIP | ED_DEQUEUE);
-
- /* but if there's work queued, reschedule */
- if (!list_empty (&ed->td_list)) {
- if (ohci->rh_state == OHCI_RH_RUNNING)
- ed_schedule (ohci, ed);
+ /*
+ * If no TDs are queued, take ED off the ed_rm_list.
+ * Otherwise, if the HC is running, reschedule.
+ * If not, leave it on the list for further dequeues.
+ */
+ if (list_empty(&ed->td_list)) {
+ *last = ed->ed_next;
+ ed->ed_next = NULL;
+ list_del(&ed->in_use_list);
+ } else if (ohci->rh_state == OHCI_RH_RUNNING) {
+ *last = ed->ed_next;
+ ed->ed_next = NULL;
+ ed_schedule(ohci, ed);
+ } else {
+ last = &ed->ed_next;
}
if (modified)
@@ -1082,12 +1145,7 @@ rescan_this:
/*-------------------------------------------------------------------------*/
-/*
- * Used to take back a TD from the host controller. This would normally be
- * called from within dl_done_list, however it may be called directly if the
- * HC no longer sees the TD and it has not appeared on the donelist (after
- * two frames). This bug has been observed on ZF Micro systems.
- */
+/* Take back a TD from the host controller */
static void takeback_td(struct ohci_hcd *ohci, struct td *td)
{
struct urb *urb = td->urb;
@@ -1134,37 +1192,43 @@ static void takeback_td(struct ohci_hcd *ohci, struct td *td)
*
* This is the main path for handing urbs back to drivers. The only other
* normal path is finish_unlinks(), which unlinks URBs using ed_rm_list,
- * instead of scanning the (re-reversed) donelist as this does. There's
- * an abnormal path too, handling a quirk in some Compaq silicon: URBs
- * with TDs that appear to be orphaned are directly reclaimed.
+ * instead of scanning the (re-reversed) donelist as this does.
*/
-static void
-dl_done_list (struct ohci_hcd *ohci)
+static void process_done_list(struct ohci_hcd *ohci)
{
- struct td *td = dl_reverse_done_list (ohci);
+ struct td *td;
- while (td) {
- struct td *td_next = td->next_dl_td;
- struct ed *ed = td->ed;
+ while (ohci->dl_start) {
+ td = ohci->dl_start;
+ if (td == ohci->dl_end)
+ ohci->dl_start = ohci->dl_end = NULL;
+ else
+ ohci->dl_start = td->next_dl_td;
- /*
- * Some OHCI controllers (NVIDIA for sure, maybe others)
- * occasionally forget to add TDs to the done queue. Since
- * TDs for a given endpoint are always processed in order,
- * if we find a TD on the donelist then all of its
- * predecessors must be finished as well.
- */
- for (;;) {
- struct td *td2;
+ takeback_td(ohci, td);
+ }
+}
- td2 = list_first_entry(&ed->td_list, struct td,
- td_list);
- if (td2 == td)
- break;
- takeback_td(ohci, td2);
- }
+/*
+ * TD takeback and URB giveback must be single-threaded.
+ * This routine takes care of it all.
+ */
+static void ohci_work(struct ohci_hcd *ohci)
+{
+ if (ohci->working) {
+ ohci->restart_work = 1;
+ return;
+ }
+ ohci->working = 1;
- takeback_td(ohci, td);
- td = td_next;
+ restart:
+ process_done_list(ohci);
+ if (ohci->ed_rm_list)
+ finish_unlinks(ohci);
+
+ if (ohci->restart_work) {
+ ohci->restart_work = 0;
+ goto restart;
}
+ ohci->working = 0;
}
diff --git a/drivers/usb/host/ohci-spear.c b/drivers/usb/host/ohci-spear.c
index 8b29a0c04c23..8d5876692e7c 100644
--- a/drivers/usb/host/ohci-spear.c
+++ b/drivers/usb/host/ohci-spear.c
@@ -162,7 +162,7 @@ static int spear_ohci_hcd_drv_resume(struct platform_device *dev)
}
#endif
-static struct of_device_id spear_ohci_id_table[] = {
+static const struct of_device_id spear_ohci_id_table[] = {
{ .compatible = "st,spear600-ohci", },
{ },
};
diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h
index 05e02a709d4f..59f424567a8d 100644
--- a/drivers/usb/host/ohci.h
+++ b/drivers/usb/host/ohci.h
@@ -47,6 +47,7 @@ struct ed {
struct ed *ed_next; /* on schedule or rm_list */
struct ed *ed_prev; /* for non-interrupt EDs */
struct list_head td_list; /* "shadow list" of our TDs */
+ struct list_head in_use_list;
/* create --> IDLE --> OPER --> ... --> IDLE --> destroy
* usually: OPER --> UNLINK --> (IDLE | OPER) --> ...
@@ -66,6 +67,13 @@ struct ed {
/* HC may see EDs on rm_list until next frame (frame_no == tick) */
u16 tick;
+
+ /* Detect TDs not added to the done queue */
+ unsigned takeback_wdh_cnt;
+ struct td *pending_td;
+#define OKAY_TO_TAKEBACK(ohci, ed) \
+ ((int) (ohci->wdh_cnt - ed->takeback_wdh_cnt) >= 0)
+
} __attribute__ ((aligned(16)));
#define ED_MASK ((u32)~0x0f) /* strip hw status in low addr bits */
@@ -380,7 +388,9 @@ struct ohci_hcd {
struct dma_pool *td_cache;
struct dma_pool *ed_cache;
struct td *td_hash [TD_HASH_SIZE];
+ struct td *dl_start, *dl_end; /* the done list */
struct list_head pending;
+ struct list_head eds_in_use; /* all EDs with at least 1 TD */
/*
* driver state
@@ -392,6 +402,8 @@ struct ohci_hcd {
unsigned long next_statechange; /* suspend/resume */
u32 fminterval; /* saved register */
unsigned autostop:1; /* rh auto stopping/stopped */
+ unsigned working:1;
+ unsigned restart_work:1;
unsigned long flags; /* for HC bugs */
#define OHCI_QUIRK_AMD756 0x01 /* erratum #4 */
@@ -409,13 +421,12 @@ struct ohci_hcd {
// there are also chip quirks/bugs in init logic
- struct work_struct nec_work; /* Worker for NEC quirk */
+ unsigned prev_frame_no;
+ unsigned wdh_cnt, prev_wdh_cnt;
+ u32 prev_donehead;
+ struct timer_list io_watchdog;
- /* Needed for ZF Micro quirk */
- struct timer_list unlink_watchdog;
- unsigned eds_scheduled;
- struct ed *ed_to_check;
- unsigned zf_delay;
+ struct work_struct nec_work; /* Worker for NEC quirk */
struct dentry *debug_dir;
struct dentry *debug_async;
diff --git a/drivers/usb/host/oxu210hp-hcd.c b/drivers/usb/host/oxu210hp-hcd.c
index e07248b6ab67..da5fb0e3c363 100644
--- a/drivers/usb/host/oxu210hp-hcd.c
+++ b/drivers/usb/host/oxu210hp-hcd.c
@@ -3826,49 +3826,36 @@ static int oxu_drv_probe(struct platform_device *pdev)
dev_dbg(&pdev->dev, "IRQ resource %d\n", irq);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(&pdev->dev, "no registers address! Check %s setup!\n",
- dev_name(&pdev->dev));
- return -ENODEV;
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base)) {
+ ret = PTR_ERR(base);
+ goto error;
}
memstart = res->start;
memlen = resource_size(res);
- dev_dbg(&pdev->dev, "MEM resource %lx-%lx\n", memstart, memlen);
- if (!request_mem_region(memstart, memlen,
- oxu_hc_driver.description)) {
- dev_dbg(&pdev->dev, "memory area already in use\n");
- return -EBUSY;
- }
ret = irq_set_irq_type(irq, IRQF_TRIGGER_FALLING);
if (ret) {
dev_err(&pdev->dev, "error setting irq type\n");
ret = -EFAULT;
- goto error_set_irq_type;
- }
-
- base = ioremap(memstart, memlen);
- if (!base) {
- dev_dbg(&pdev->dev, "error mapping memory\n");
- ret = -EFAULT;
- goto error_ioremap;
+ goto error;
}
/* Allocate a driver data struct to hold useful info for both
* SPH & OTG devices
*/
- info = kzalloc(sizeof(struct oxu_info), GFP_KERNEL);
+ info = devm_kzalloc(&pdev->dev, sizeof(struct oxu_info), GFP_KERNEL);
if (!info) {
dev_dbg(&pdev->dev, "error allocating memory\n");
ret = -EFAULT;
- goto error_alloc;
+ goto error;
}
platform_set_drvdata(pdev, info);
ret = oxu_init(pdev, memstart, memlen, base, irq);
if (ret < 0) {
dev_dbg(&pdev->dev, "cannot init USB devices\n");
- goto error_init;
+ goto error;
}
dev_info(&pdev->dev, "devices enabled and running\n");
@@ -3876,16 +3863,7 @@ static int oxu_drv_probe(struct platform_device *pdev)
return 0;
-error_init:
- kfree(info);
-
-error_alloc:
- iounmap(base);
-
-error_set_irq_type:
-error_ioremap:
- release_mem_region(memstart, memlen);
-
+error:
dev_err(&pdev->dev, "init %s fail, %d\n", dev_name(&pdev->dev), ret);
return ret;
}
@@ -3899,18 +3877,10 @@ static void oxu_remove(struct platform_device *pdev, struct usb_hcd *hcd)
static int oxu_drv_remove(struct platform_device *pdev)
{
struct oxu_info *info = platform_get_drvdata(pdev);
- unsigned long memstart = info->hcd[0]->rsrc_start,
- memlen = info->hcd[0]->rsrc_len;
- void *base = info->hcd[0]->regs;
oxu_remove(pdev, info->hcd[0]);
oxu_remove(pdev, info->hcd[1]);
- iounmap(base);
- release_mem_region(memstart, memlen);
-
- kfree(info);
-
return 0;
}
diff --git a/drivers/usb/host/uhci-grlib.c b/drivers/usb/host/uhci-grlib.c
index ab25dc397e8b..05f57ffdf9ab 100644
--- a/drivers/usb/host/uhci-grlib.c
+++ b/drivers/usb/host/uhci-grlib.c
@@ -17,6 +17,7 @@
* (C) Copyright 2004-2007 Alan Stern, stern@rowland.harvard.edu
*/
+#include <linux/device.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
@@ -113,24 +114,17 @@ static int uhci_hcd_grlib_probe(struct platform_device *op)
hcd->rsrc_start = res.start;
hcd->rsrc_len = resource_size(&res);
- if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
- printk(KERN_ERR "%s: request_mem_region failed\n", __FILE__);
- rv = -EBUSY;
- goto err_rmr;
- }
-
irq = irq_of_parse_and_map(dn, 0);
if (irq == NO_IRQ) {
printk(KERN_ERR "%s: irq_of_parse_and_map failed\n", __FILE__);
rv = -EBUSY;
- goto err_irq;
+ goto err_usb;
}
- hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
- if (!hcd->regs) {
- printk(KERN_ERR "%s: ioremap failed\n", __FILE__);
- rv = -ENOMEM;
- goto err_ioremap;
+ hcd->regs = devm_ioremap_resource(&op->dev, &res);
+ if (IS_ERR(hcd->regs)) {
+ rv = PTR_ERR(hcd->regs);
+ goto err_irq;
}
uhci = hcd_to_uhci(hcd);
@@ -139,18 +133,14 @@ static int uhci_hcd_grlib_probe(struct platform_device *op)
rv = usb_add_hcd(hcd, irq, 0);
if (rv)
- goto err_uhci;
+ goto err_irq;
device_wakeup_enable(hcd->self.controller);
return 0;
-err_uhci:
- iounmap(hcd->regs);
-err_ioremap:
- irq_dispose_mapping(irq);
err_irq:
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
-err_rmr:
+ irq_dispose_mapping(irq);
+err_usb:
usb_put_hcd(hcd);
return rv;
@@ -164,10 +154,7 @@ static int uhci_hcd_grlib_remove(struct platform_device *op)
usb_remove_hcd(hcd);
- iounmap(hcd->regs);
irq_dispose_mapping(hcd->irq);
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
-
usb_put_hcd(hcd);
return 0;
diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c
index 27f35e8f161b..a7de8e8bb458 100644
--- a/drivers/usb/host/uhci-hcd.c
+++ b/drivers/usb/host/uhci-hcd.c
@@ -591,7 +591,7 @@ static int uhci_start(struct usb_hcd *hcd)
uhci->frame = dma_alloc_coherent(uhci_dev(uhci),
UHCI_NUMFRAMES * sizeof(*uhci->frame),
- &uhci->frame_dma_handle, 0);
+ &uhci->frame_dma_handle, GFP_KERNEL);
if (!uhci->frame) {
dev_err(uhci_dev(uhci),
"unable to allocate consistent memory for frame list\n");
diff --git a/drivers/usb/host/uhci-platform.c b/drivers/usb/host/uhci-platform.c
index 01833ab2b5c3..b987f1d10058 100644
--- a/drivers/usb/host/uhci-platform.c
+++ b/drivers/usb/host/uhci-platform.c
@@ -8,6 +8,7 @@
*/
#include <linux/of.h>
+#include <linux/device.h>
#include <linux/platform_device.h>
static int uhci_platform_init(struct usb_hcd *hcd)
@@ -88,33 +89,22 @@ static int uhci_hcd_platform_probe(struct platform_device *pdev)
hcd->rsrc_start = res->start;
hcd->rsrc_len = resource_size(res);
- if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
- pr_err("%s: request_mem_region failed\n", __func__);
- ret = -EBUSY;
+ hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(hcd->regs)) {
+ ret = PTR_ERR(hcd->regs);
goto err_rmr;
}
-
- hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
- if (!hcd->regs) {
- pr_err("%s: ioremap failed\n", __func__);
- ret = -ENOMEM;
- goto err_irq;
- }
uhci = hcd_to_uhci(hcd);
uhci->regs = hcd->regs;
ret = usb_add_hcd(hcd, pdev->resource[1].start, IRQF_SHARED);
if (ret)
- goto err_uhci;
+ goto err_rmr;
device_wakeup_enable(hcd->self.controller);
return 0;
-err_uhci:
- iounmap(hcd->regs);
-err_irq:
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
err_rmr:
usb_put_hcd(hcd);
@@ -126,8 +116,6 @@ static int uhci_hcd_platform_remove(struct platform_device *pdev)
struct usb_hcd *hcd = platform_get_drvdata(pdev);
usb_remove_hcd(hcd);
- iounmap(hcd->regs);
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
return 0;
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index e20520f42753..687d36608155 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -33,7 +33,7 @@
#define PCI_DEVICE_ID_FRESCO_LOGIC_FL1400 0x1400
#define PCI_VENDOR_ID_ETRON 0x1b6f
-#define PCI_DEVICE_ID_ASROCK_P67 0x7023
+#define PCI_DEVICE_ID_EJ168 0x7023
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_XHCI 0x8c31
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31
@@ -140,9 +140,10 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
xhci->quirks |= XHCI_SPURIOUS_REBOOT;
}
if (pdev->vendor == PCI_VENDOR_ID_ETRON &&
- pdev->device == PCI_DEVICE_ID_ASROCK_P67) {
+ pdev->device == PCI_DEVICE_ID_EJ168) {
xhci->quirks |= XHCI_RESET_ON_RESUME;
xhci->quirks |= XHCI_TRUST_TX_LENGTH;
+ xhci->quirks |= XHCI_BROKEN_STREAMS;
}
if (pdev->vendor == PCI_VENDOR_ID_RENESAS &&
pdev->device == 0x0015)
@@ -230,7 +231,8 @@ static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
goto put_usb3_hcd;
/* Roothub already marked as USB 3.0 speed */
- if (HCC_MAX_PSA(xhci->hcc_params) >= 4)
+ if (!(xhci->quirks & XHCI_BROKEN_STREAMS) &&
+ HCC_MAX_PSA(xhci->hcc_params) >= 4)
xhci->shared_hcd->can_do_streams = 1;
/* USB-2 and USB-3 roothubs initialized, allow runtime pm suspend */
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index 29d8adb5c8d1..1a0cf9f31e43 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -17,9 +17,11 @@
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
+#include <linux/usb/xhci_pdriver.h>
#include "xhci.h"
#include "xhci-mvebu.h"
+#include "xhci-rcar.h"
static void xhci_plat_quirks(struct device *dev, struct xhci_hcd *xhci)
{
@@ -34,11 +36,27 @@ static void xhci_plat_quirks(struct device *dev, struct xhci_hcd *xhci)
/* called during probe() after chip reset completes */
static int xhci_plat_setup(struct usb_hcd *hcd)
{
+ struct device_node *of_node = hcd->self.controller->of_node;
+ int ret;
+
+ if (of_device_is_compatible(of_node, "renesas,xhci-r8a7790") ||
+ of_device_is_compatible(of_node, "renesas,xhci-r8a7791")) {
+ ret = xhci_rcar_init_quirk(hcd);
+ if (ret)
+ return ret;
+ }
+
return xhci_gen_setup(hcd, xhci_plat_quirks);
}
static int xhci_plat_start(struct usb_hcd *hcd)
{
+ struct device_node *of_node = hcd->self.controller->of_node;
+
+ if (of_device_is_compatible(of_node, "renesas,xhci-r8a7790") ||
+ of_device_is_compatible(of_node, "renesas,xhci-r8a7791"))
+ xhci_rcar_start(hcd);
+
return xhci_run(hcd);
}
@@ -90,10 +108,15 @@ static const struct hc_driver xhci_plat_xhci_driver = {
.hub_status_data = xhci_hub_status_data,
.bus_suspend = xhci_bus_suspend,
.bus_resume = xhci_bus_resume,
+
+ .enable_usb3_lpm_timeout = xhci_enable_usb3_lpm_timeout,
+ .disable_usb3_lpm_timeout = xhci_disable_usb3_lpm_timeout,
};
static int xhci_plat_probe(struct platform_device *pdev)
{
+ struct device_node *node = pdev->dev.of_node;
+ struct usb_xhci_pdata *pdata = dev_get_platdata(&pdev->dev);
const struct hc_driver *driver;
struct xhci_hcd *xhci;
struct resource *res;
@@ -140,20 +163,12 @@ static int xhci_plat_probe(struct platform_device *pdev)
hcd->rsrc_start = res->start;
hcd->rsrc_len = resource_size(res);
- if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len,
- driver->description)) {
- dev_dbg(&pdev->dev, "controller already in use\n");
- ret = -EBUSY;
+ hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(hcd->regs)) {
+ ret = PTR_ERR(hcd->regs);
goto put_hcd;
}
- hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
- if (!hcd->regs) {
- dev_dbg(&pdev->dev, "error mapping memory\n");
- ret = -EFAULT;
- goto release_mem_region;
- }
-
/*
* Not all platforms have a clk so it is not an error if the
* clock does not exists.
@@ -162,7 +177,7 @@ static int xhci_plat_probe(struct platform_device *pdev)
if (!IS_ERR(clk)) {
ret = clk_prepare_enable(clk);
if (ret)
- goto unmap_registers;
+ goto put_hcd;
}
ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
@@ -182,6 +197,9 @@ static int xhci_plat_probe(struct platform_device *pdev)
goto dealloc_usb2_hcd;
}
+ if ((node && of_property_read_bool(node, "usb3-lpm-capable")) ||
+ (pdata && pdata->usb3_lpm_capable))
+ xhci->quirks |= XHCI_LPM_SUPPORT;
/*
* Set the xHCI pointer before xhci_plat_setup() (aka hcd_driver.reset)
* is called by usb_add_hcd().
@@ -207,12 +225,6 @@ disable_clk:
if (!IS_ERR(clk))
clk_disable_unprepare(clk);
-unmap_registers:
- iounmap(hcd->regs);
-
-release_mem_region:
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
-
put_hcd:
usb_put_hcd(hcd);
@@ -231,8 +243,6 @@ static int xhci_plat_remove(struct platform_device *dev)
usb_remove_hcd(hcd);
if (!IS_ERR(clk))
clk_disable_unprepare(clk);
- iounmap(hcd->regs);
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
kfree(xhci);
@@ -270,6 +280,8 @@ static const struct of_device_id usb_xhci_of_match[] = {
{ .compatible = "xhci-platform" },
{ .compatible = "marvell,armada-375-xhci"},
{ .compatible = "marvell,armada-380-xhci"},
+ { .compatible = "renesas,xhci-r8a7790"},
+ { .compatible = "renesas,xhci-r8a7791"},
{ },
};
MODULE_DEVICE_TABLE(of, usb_xhci_of_match);
diff --git a/drivers/usb/host/xhci-rcar.c b/drivers/usb/host/xhci-rcar.c
new file mode 100644
index 000000000000..ff0d1b44ea58
--- /dev/null
+++ b/drivers/usb/host/xhci-rcar.c
@@ -0,0 +1,148 @@
+/*
+ * xHCI host controller driver for R-Car SoCs
+ *
+ * Copyright (C) 2014 Renesas Electronics Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+
+#include <linux/firmware.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/usb/phy.h>
+
+#include "xhci.h"
+#include "xhci-rcar.h"
+
+#define FIRMWARE_NAME "r8a779x_usb3_v1.dlmem"
+MODULE_FIRMWARE(FIRMWARE_NAME);
+
+/*** Register Offset ***/
+#define RCAR_USB3_INT_ENA 0x224 /* Interrupt Enable */
+#define RCAR_USB3_DL_CTRL 0x250 /* FW Download Control & Status */
+#define RCAR_USB3_FW_DATA0 0x258 /* FW Data0 */
+
+#define RCAR_USB3_LCLK 0xa44 /* LCLK Select */
+#define RCAR_USB3_CONF1 0xa48 /* USB3.0 Configuration1 */
+#define RCAR_USB3_CONF2 0xa5c /* USB3.0 Configuration2 */
+#define RCAR_USB3_CONF3 0xaa8 /* USB3.0 Configuration3 */
+#define RCAR_USB3_RX_POL 0xab0 /* USB3.0 RX Polarity */
+#define RCAR_USB3_TX_POL 0xab8 /* USB3.0 TX Polarity */
+
+/*** Register Settings ***/
+/* Interrupt Enable */
+#define RCAR_USB3_INT_XHC_ENA 0x00000001
+#define RCAR_USB3_INT_PME_ENA 0x00000002
+#define RCAR_USB3_INT_HSE_ENA 0x00000004
+#define RCAR_USB3_INT_ENA_VAL (RCAR_USB3_INT_XHC_ENA | \
+ RCAR_USB3_INT_PME_ENA | RCAR_USB3_INT_HSE_ENA)
+
+/* FW Download Control & Status */
+#define RCAR_USB3_DL_CTRL_ENABLE 0x00000001
+#define RCAR_USB3_DL_CTRL_FW_SUCCESS 0x00000010
+#define RCAR_USB3_DL_CTRL_FW_SET_DATA0 0x00000100
+
+/* LCLK Select */
+#define RCAR_USB3_LCLK_ENA_VAL 0x01030001
+
+/* USB3.0 Configuration */
+#define RCAR_USB3_CONF1_VAL 0x00030204
+#define RCAR_USB3_CONF2_VAL 0x00030300
+#define RCAR_USB3_CONF3_VAL 0x13802007
+
+/* USB3.0 Polarity */
+#define RCAR_USB3_RX_POL_VAL BIT(21)
+#define RCAR_USB3_TX_POL_VAL BIT(4)
+
+void xhci_rcar_start(struct usb_hcd *hcd)
+{
+ u32 temp;
+
+ if (hcd->regs != NULL) {
+ /* Interrupt Enable */
+ temp = readl(hcd->regs + RCAR_USB3_INT_ENA);
+ temp |= RCAR_USB3_INT_ENA_VAL;
+ writel(temp, hcd->regs + RCAR_USB3_INT_ENA);
+ /* LCLK Select */
+ writel(RCAR_USB3_LCLK_ENA_VAL, hcd->regs + RCAR_USB3_LCLK);
+ /* USB3.0 Configuration */
+ writel(RCAR_USB3_CONF1_VAL, hcd->regs + RCAR_USB3_CONF1);
+ writel(RCAR_USB3_CONF2_VAL, hcd->regs + RCAR_USB3_CONF2);
+ writel(RCAR_USB3_CONF3_VAL, hcd->regs + RCAR_USB3_CONF3);
+ /* USB3.0 Polarity */
+ writel(RCAR_USB3_RX_POL_VAL, hcd->regs + RCAR_USB3_RX_POL);
+ writel(RCAR_USB3_TX_POL_VAL, hcd->regs + RCAR_USB3_TX_POL);
+ }
+}
+
+static int xhci_rcar_download_firmware(struct device *dev, void __iomem *regs)
+{
+ const struct firmware *fw;
+ int retval, index, j, time;
+ int timeout = 10000;
+ u32 data, val, temp;
+
+ /* request R-Car USB3.0 firmware */
+ retval = request_firmware(&fw, FIRMWARE_NAME, dev);
+ if (retval)
+ return retval;
+
+ /* download R-Car USB3.0 firmware */
+ temp = readl(regs + RCAR_USB3_DL_CTRL);
+ temp |= RCAR_USB3_DL_CTRL_ENABLE;
+ writel(temp, regs + RCAR_USB3_DL_CTRL);
+
+ for (index = 0; index < fw->size; index += 4) {
+ /* to avoid reading beyond the end of the buffer */
+ for (data = 0, j = 3; j >= 0; j--) {
+ if ((j + index) < fw->size)
+ data |= fw->data[index + j] << (8 * j);
+ }
+ writel(data, regs + RCAR_USB3_FW_DATA0);
+ temp = readl(regs + RCAR_USB3_DL_CTRL);
+ temp |= RCAR_USB3_DL_CTRL_FW_SET_DATA0;
+ writel(temp, regs + RCAR_USB3_DL_CTRL);
+
+ for (time = 0; time < timeout; time++) {
+ val = readl(regs + RCAR_USB3_DL_CTRL);
+ if ((val & RCAR_USB3_DL_CTRL_FW_SET_DATA0) == 0)
+ break;
+ udelay(1);
+ }
+ if (time == timeout) {
+ retval = -ETIMEDOUT;
+ break;
+ }
+ }
+
+ temp = readl(regs + RCAR_USB3_DL_CTRL);
+ temp &= ~RCAR_USB3_DL_CTRL_ENABLE;
+ writel(temp, regs + RCAR_USB3_DL_CTRL);
+
+ for (time = 0; time < timeout; time++) {
+ val = readl(regs + RCAR_USB3_DL_CTRL);
+ if (val & RCAR_USB3_DL_CTRL_FW_SUCCESS) {
+ retval = 0;
+ break;
+ }
+ udelay(1);
+ }
+ if (time == timeout)
+ retval = -ETIMEDOUT;
+
+ release_firmware(fw);
+
+ return retval;
+}
+
+/* This function needs to initialize a "phy" of usb before */
+int xhci_rcar_init_quirk(struct usb_hcd *hcd)
+{
+ /* If hcd->regs is NULL, we don't just call the following function */
+ if (!hcd->regs)
+ return 0;
+
+ return xhci_rcar_download_firmware(hcd->self.controller, hcd->regs);
+}
diff --git a/drivers/usb/host/xhci-rcar.h b/drivers/usb/host/xhci-rcar.h
new file mode 100644
index 000000000000..58501256715d
--- /dev/null
+++ b/drivers/usb/host/xhci-rcar.h
@@ -0,0 +1,27 @@
+/*
+ * drivers/usb/host/xhci-rcar.h
+ *
+ * Copyright (C) 2014 Renesas Electronics Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+
+#ifndef _XHCI_RCAR_H
+#define _XHCI_RCAR_H
+
+#if IS_ENABLED(CONFIG_USB_XHCI_RCAR)
+void xhci_rcar_start(struct usb_hcd *hcd);
+int xhci_rcar_init_quirk(struct usb_hcd *hcd);
+#else
+static inline void xhci_rcar_start(struct usb_hcd *hcd)
+{
+}
+
+static inline int xhci_rcar_init_quirk(struct usb_hcd *hcd)
+{
+ return 0;
+}
+#endif
+#endif /* _XHCI_RCAR_H */
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 749fc68eb5c1..60fb52ae864b 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -1118,6 +1118,10 @@ static void xhci_handle_cmd_reset_ep(struct xhci_hcd *xhci, int slot_id,
if (xhci->quirks & XHCI_RESET_EP_QUIRK) {
struct xhci_command *command;
command = xhci_alloc_command(xhci, false, false, GFP_ATOMIC);
+ if (!command) {
+ xhci_warn(xhci, "WARN Cannot submit cfg ep: ENOMEM\n");
+ return;
+ }
xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
"Queueing configure endpoint command");
xhci_queue_configure_endpoint(xhci, command,
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 7436d5f5e67a..b6f21175b872 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -1553,6 +1553,10 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
*/
if (!(ep->ep_state & EP_HALT_PENDING)) {
command = xhci_alloc_command(xhci, false, false, GFP_ATOMIC);
+ if (!command) {
+ ret = -ENOMEM;
+ goto done;
+ }
ep->ep_state |= EP_HALT_PENDING;
ep->stop_cmds_pending++;
ep->stop_cmd_timer.expires = jiffies +
@@ -1586,12 +1590,10 @@ int xhci_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
struct xhci_hcd *xhci;
struct xhci_container_ctx *in_ctx, *out_ctx;
struct xhci_input_control_ctx *ctrl_ctx;
- struct xhci_slot_ctx *slot_ctx;
- unsigned int last_ctx;
unsigned int ep_index;
struct xhci_ep_ctx *ep_ctx;
u32 drop_flag;
- u32 new_add_flags, new_drop_flags, new_slot_info;
+ u32 new_add_flags, new_drop_flags;
int ret;
ret = xhci_check_args(hcd, udev, ep, 1, true, __func__);
@@ -1638,24 +1640,13 @@ int xhci_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
ctrl_ctx->add_flags &= cpu_to_le32(~drop_flag);
new_add_flags = le32_to_cpu(ctrl_ctx->add_flags);
- last_ctx = xhci_last_valid_endpoint(le32_to_cpu(ctrl_ctx->add_flags));
- slot_ctx = xhci_get_slot_ctx(xhci, in_ctx);
- /* Update the last valid endpoint context, if we deleted the last one */
- if ((le32_to_cpu(slot_ctx->dev_info) & LAST_CTX_MASK) >
- LAST_CTX(last_ctx)) {
- slot_ctx->dev_info &= cpu_to_le32(~LAST_CTX_MASK);
- slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(last_ctx));
- }
- new_slot_info = le32_to_cpu(slot_ctx->dev_info);
-
xhci_endpoint_zero(xhci, xhci->devs[udev->slot_id], ep);
- xhci_dbg(xhci, "drop ep 0x%x, slot id %d, new drop flags = %#x, new add flags = %#x, new slot info = %#x\n",
+ xhci_dbg(xhci, "drop ep 0x%x, slot id %d, new drop flags = %#x, new add flags = %#x\n",
(unsigned int) ep->desc.bEndpointAddress,
udev->slot_id,
(unsigned int) new_drop_flags,
- (unsigned int) new_add_flags,
- (unsigned int) new_slot_info);
+ (unsigned int) new_add_flags);
return 0;
}
@@ -1678,11 +1669,9 @@ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
struct xhci_hcd *xhci;
struct xhci_container_ctx *in_ctx, *out_ctx;
unsigned int ep_index;
- struct xhci_slot_ctx *slot_ctx;
struct xhci_input_control_ctx *ctrl_ctx;
u32 added_ctxs;
- unsigned int last_ctx;
- u32 new_add_flags, new_drop_flags, new_slot_info;
+ u32 new_add_flags, new_drop_flags;
struct xhci_virt_device *virt_dev;
int ret = 0;
@@ -1697,7 +1686,6 @@ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
return -ENODEV;
added_ctxs = xhci_get_endpoint_flag(&ep->desc);
- last_ctx = xhci_last_valid_endpoint(added_ctxs);
if (added_ctxs == SLOT_FLAG || added_ctxs == EP0_FLAG) {
/* FIXME when we have to issue an evaluate endpoint command to
* deal with ep0 max packet size changing once we get the
@@ -1763,24 +1751,14 @@ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
*/
new_drop_flags = le32_to_cpu(ctrl_ctx->drop_flags);
- slot_ctx = xhci_get_slot_ctx(xhci, in_ctx);
- /* Update the last valid endpoint context, if we just added one past */
- if ((le32_to_cpu(slot_ctx->dev_info) & LAST_CTX_MASK) <
- LAST_CTX(last_ctx)) {
- slot_ctx->dev_info &= cpu_to_le32(~LAST_CTX_MASK);
- slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(last_ctx));
- }
- new_slot_info = le32_to_cpu(slot_ctx->dev_info);
-
/* Store the usb_device pointer for later use */
ep->hcpriv = udev;
- xhci_dbg(xhci, "add ep 0x%x, slot id %d, new drop flags = %#x, new add flags = %#x, new slot info = %#x\n",
+ xhci_dbg(xhci, "add ep 0x%x, slot id %d, new drop flags = %#x, new add flags = %#x\n",
(unsigned int) ep->desc.bEndpointAddress,
udev->slot_id,
(unsigned int) new_drop_flags,
- (unsigned int) new_add_flags,
- (unsigned int) new_slot_info);
+ (unsigned int) new_add_flags);
return 0;
}
@@ -1830,15 +1808,15 @@ static int xhci_configure_endpoint_result(struct xhci_hcd *xhci,
ret = -ETIME;
break;
case COMP_ENOMEM:
- dev_warn(&udev->dev, "Not enough host controller resources "
- "for new device state.\n");
+ dev_warn(&udev->dev,
+ "Not enough host controller resources for new device state.\n");
ret = -ENOMEM;
/* FIXME: can we allocate more resources for the HC? */
break;
case COMP_BW_ERR:
case COMP_2ND_BW_ERR:
- dev_warn(&udev->dev, "Not enough bandwidth "
- "for new device state.\n");
+ dev_warn(&udev->dev,
+ "Not enough bandwidth for new device state.\n");
ret = -ENOSPC;
/* FIXME: can we go back to the old state? */
break;
@@ -1850,8 +1828,8 @@ static int xhci_configure_endpoint_result(struct xhci_hcd *xhci,
ret = -EINVAL;
break;
case COMP_DEV_ERR:
- dev_warn(&udev->dev, "ERROR: Incompatible device for endpoint "
- "configure command.\n");
+ dev_warn(&udev->dev,
+ "ERROR: Incompatible device for endpoint configure command.\n");
ret = -ENODEV;
break;
case COMP_SUCCESS:
@@ -1860,8 +1838,8 @@ static int xhci_configure_endpoint_result(struct xhci_hcd *xhci,
ret = 0;
break;
default:
- xhci_err(xhci, "ERROR: unexpected command completion "
- "code 0x%x.\n", *cmd_status);
+ xhci_err(xhci, "ERROR: unexpected command completion code 0x%x.\n",
+ *cmd_status);
ret = -EINVAL;
break;
}
@@ -1881,24 +1859,24 @@ static int xhci_evaluate_context_result(struct xhci_hcd *xhci,
ret = -ETIME;
break;
case COMP_EINVAL:
- dev_warn(&udev->dev, "WARN: xHCI driver setup invalid evaluate "
- "context command.\n");
+ dev_warn(&udev->dev,
+ "WARN: xHCI driver setup invalid evaluate context command.\n");
ret = -EINVAL;
break;
case COMP_EBADSLT:
- dev_warn(&udev->dev, "WARN: slot not enabled for"
- "evaluate context command.\n");
+ dev_warn(&udev->dev,
+ "WARN: slot not enabled for evaluate context command.\n");
ret = -EINVAL;
break;
case COMP_CTX_STATE:
- dev_warn(&udev->dev, "WARN: invalid context state for "
- "evaluate context command.\n");
+ dev_warn(&udev->dev,
+ "WARN: invalid context state for evaluate context command.\n");
xhci_dbg_ctx(xhci, virt_dev->out_ctx, 1);
ret = -EINVAL;
break;
case COMP_DEV_ERR:
- dev_warn(&udev->dev, "ERROR: Incompatible device for evaluate "
- "context command.\n");
+ dev_warn(&udev->dev,
+ "ERROR: Incompatible device for evaluate context command.\n");
ret = -ENODEV;
break;
case COMP_MEL_ERR:
@@ -1912,8 +1890,8 @@ static int xhci_evaluate_context_result(struct xhci_hcd *xhci,
ret = 0;
break;
default:
- xhci_err(xhci, "ERROR: unexpected command completion "
- "code 0x%x.\n", *cmd_status);
+ xhci_err(xhci, "ERROR: unexpected command completion code 0x%x.\n",
+ *cmd_status);
ret = -EINVAL;
break;
}
@@ -2750,8 +2728,19 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev)
ret = 0;
goto command_cleanup;
}
- xhci_dbg(xhci, "New Input Control Context:\n");
+ /* Fix up Context Entries field. Minimum value is EP0 == BIT(1). */
slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->in_ctx);
+ for (i = 31; i >= 1; i--) {
+ __le32 le32 = cpu_to_le32(BIT(i));
+
+ if ((virt_dev->eps[i-1].ring && !(ctrl_ctx->drop_flags & le32))
+ || (ctrl_ctx->add_flags & le32) || i == 1) {
+ slot_ctx->dev_info &= cpu_to_le32(~LAST_CTX_MASK);
+ slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(i));
+ break;
+ }
+ }
+ xhci_dbg(xhci, "New Input Control Context:\n");
xhci_dbg_ctx(xhci, virt_dev->in_ctx,
LAST_CTX_TO_EP_NUM(le32_to_cpu(slot_ctx->dev_info)));
@@ -3163,7 +3152,8 @@ int xhci_alloc_streams(struct usb_hcd *hcd, struct usb_device *udev,
num_streams);
/* MaxPSASize value 0 (2 streams) means streams are not supported */
- if (HCC_MAX_PSA(xhci->hcc_params) < 4) {
+ if ((xhci->quirks & XHCI_BROKEN_STREAMS) ||
+ HCC_MAX_PSA(xhci->hcc_params) < 4) {
xhci_dbg(xhci, "xHCI controller does not support streams.\n");
return -ENOSYS;
}
@@ -4303,8 +4293,7 @@ static u16 xhci_get_timeout_no_hub_lpm(struct usb_device *udev,
return USB3_LPM_DISABLED;
}
-/* Returns the hub-encoded U1 timeout value.
- * The U1 timeout should be the maximum of the following values:
+/* The U1 timeout should be the maximum of the following values:
* - For control endpoints, U1 system exit latency (SEL) * 3
* - For bulk endpoints, U1 SEL * 5
* - For interrupt endpoints:
@@ -4312,7 +4301,8 @@ static u16 xhci_get_timeout_no_hub_lpm(struct usb_device *udev,
* - Periodic EPs, max(105% of bInterval, U1 SEL * 2)
* - For isochronous endpoints, max(105% of bInterval, U1 SEL * 2)
*/
-static u16 xhci_calculate_intel_u1_timeout(struct usb_device *udev,
+static unsigned long long xhci_calculate_intel_u1_timeout(
+ struct usb_device *udev,
struct usb_endpoint_descriptor *desc)
{
unsigned long long timeout_ns;
@@ -4344,11 +4334,28 @@ static u16 xhci_calculate_intel_u1_timeout(struct usb_device *udev,
return 0;
}
- /* The U1 timeout is encoded in 1us intervals. */
- timeout_ns = DIV_ROUND_UP_ULL(timeout_ns, 1000);
- /* Don't return a timeout of zero, because that's USB3_LPM_DISABLED. */
+ return timeout_ns;
+}
+
+/* Returns the hub-encoded U1 timeout value. */
+static u16 xhci_calculate_u1_timeout(struct xhci_hcd *xhci,
+ struct usb_device *udev,
+ struct usb_endpoint_descriptor *desc)
+{
+ unsigned long long timeout_ns;
+
+ if (xhci->quirks & XHCI_INTEL_HOST)
+ timeout_ns = xhci_calculate_intel_u1_timeout(udev, desc);
+ else
+ timeout_ns = udev->u1_params.sel;
+
+ /* The U1 timeout is encoded in 1us intervals.
+ * Don't return a timeout of zero, because that's USB3_LPM_DISABLED.
+ */
if (timeout_ns == USB3_LPM_DISABLED)
- timeout_ns++;
+ timeout_ns = 1;
+ else
+ timeout_ns = DIV_ROUND_UP_ULL(timeout_ns, 1000);
/* If the necessary timeout value is bigger than what we can set in the
* USB 3.0 hub, we have to disable hub-initiated U1.
@@ -4360,14 +4367,14 @@ static u16 xhci_calculate_intel_u1_timeout(struct usb_device *udev,
return xhci_get_timeout_no_hub_lpm(udev, USB3_LPM_U1);
}
-/* Returns the hub-encoded U2 timeout value.
- * The U2 timeout should be the maximum of:
+/* The U2 timeout should be the maximum of:
* - 10 ms (to avoid the bandwidth impact on the scheduler)
* - largest bInterval of any active periodic endpoint (to avoid going
* into lower power link states between intervals).
* - the U2 Exit Latency of the device
*/
-static u16 xhci_calculate_intel_u2_timeout(struct usb_device *udev,
+static unsigned long long xhci_calculate_intel_u2_timeout(
+ struct usb_device *udev,
struct usb_endpoint_descriptor *desc)
{
unsigned long long timeout_ns;
@@ -4383,6 +4390,21 @@ static u16 xhci_calculate_intel_u2_timeout(struct usb_device *udev,
if (u2_del_ns > timeout_ns)
timeout_ns = u2_del_ns;
+ return timeout_ns;
+}
+
+/* Returns the hub-encoded U2 timeout value. */
+static u16 xhci_calculate_u2_timeout(struct xhci_hcd *xhci,
+ struct usb_device *udev,
+ struct usb_endpoint_descriptor *desc)
+{
+ unsigned long long timeout_ns;
+
+ if (xhci->quirks & XHCI_INTEL_HOST)
+ timeout_ns = xhci_calculate_intel_u2_timeout(udev, desc);
+ else
+ timeout_ns = udev->u2_params.sel;
+
/* The U2 timeout is encoded in 256us intervals */
timeout_ns = DIV_ROUND_UP_ULL(timeout_ns, 256 * 1000);
/* If the necessary timeout value is bigger than what we can set in the
@@ -4401,13 +4423,10 @@ static u16 xhci_call_host_update_timeout_for_endpoint(struct xhci_hcd *xhci,
enum usb3_link_state state,
u16 *timeout)
{
- if (state == USB3_LPM_U1) {
- if (xhci->quirks & XHCI_INTEL_HOST)
- return xhci_calculate_intel_u1_timeout(udev, desc);
- } else {
- if (xhci->quirks & XHCI_INTEL_HOST)
- return xhci_calculate_intel_u2_timeout(udev, desc);
- }
+ if (state == USB3_LPM_U1)
+ return xhci_calculate_u1_timeout(xhci, udev, desc);
+ else if (state == USB3_LPM_U2)
+ return xhci_calculate_u2_timeout(xhci, udev, desc);
return USB3_LPM_DISABLED;
}
@@ -4484,7 +4503,8 @@ static int xhci_check_tier_policy(struct xhci_hcd *xhci,
{
if (xhci->quirks & XHCI_INTEL_HOST)
return xhci_check_intel_tier_policy(udev, state);
- return -EINVAL;
+ else
+ return 0;
}
/* Returns the U1 or U2 timeout that should be enabled.
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 9ffecd56600d..dace5152e179 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1558,6 +1558,8 @@ struct xhci_hcd {
#define XHCI_PLAT (1 << 16)
#define XHCI_SLOW_SUSPEND (1 << 17)
#define XHCI_SPURIOUS_WAKEUP (1 << 18)
+/* For controllers with a broken beyond repair streams implementation */
+#define XHCI_BROKEN_STREAMS (1 << 19)
unsigned int num_active_eps;
unsigned int limit_active_eps;
/* There are two roothubs to keep track of bus suspend info for */
diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig
index 1bca274dc3b5..76d77206e011 100644
--- a/drivers/usb/misc/Kconfig
+++ b/drivers/usb/misc/Kconfig
@@ -248,3 +248,10 @@ config USB_HSIC_USB3503
select REGMAP_I2C
help
This option enables support for SMSC USB3503 HSIC to USB 2.0 Driver.
+
+config USB_LINK_LAYER_TEST
+ tristate "USB Link Layer Test driver"
+ help
+ This driver is for generating specific traffic for Super Speed Link
+ Layer Test Device. Say Y only when you want to conduct USB Super Speed
+ Link Layer Test for host controllers.
diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile
index e748fd5dbe94..65b0402c1ca1 100644
--- a/drivers/usb/misc/Makefile
+++ b/drivers/usb/misc/Makefile
@@ -27,3 +27,4 @@ obj-$(CONFIG_USB_YUREX) += yurex.o
obj-$(CONFIG_USB_HSIC_USB3503) += usb3503.o
obj-$(CONFIG_USB_SISUSBVGA) += sisusbvga/
+obj-$(CONFIG_USB_LINK_LAYER_TEST) += lvstest.o
diff --git a/drivers/usb/misc/lvstest.c b/drivers/usb/misc/lvstest.c
new file mode 100644
index 000000000000..7d589c156fb1
--- /dev/null
+++ b/drivers/usb/misc/lvstest.c
@@ -0,0 +1,460 @@
+/*
+ * drivers/usb/misc/lvstest.c
+ *
+ * Test pattern generation for Link Layer Validation System Tests
+ *
+ * Copyright (C) 2014 ST Microelectronics
+ * Pratyush Anand <pratyush.anand@st.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/usb/ch11.h>
+#include <linux/usb/hcd.h>
+#include <linux/usb/phy.h>
+
+struct lvs_rh {
+ /* root hub interface */
+ struct usb_interface *intf;
+ /* if lvs device connected */
+ bool present;
+ /* port no at which lvs device is present */
+ int portnum;
+ /* urb buffer */
+ u8 buffer[8];
+ /* class descriptor */
+ struct usb_hub_descriptor descriptor;
+ /* urb for polling interrupt pipe */
+ struct urb *urb;
+ /* LVS RH work queue */
+ struct workqueue_struct *rh_queue;
+ /* LVH RH work */
+ struct work_struct rh_work;
+ /* RH port status */
+ struct usb_port_status port_status;
+};
+
+static struct usb_device *create_lvs_device(struct usb_interface *intf)
+{
+ struct usb_device *udev, *hdev;
+ struct usb_hcd *hcd;
+ struct lvs_rh *lvs = usb_get_intfdata(intf);
+
+ if (!lvs->present) {
+ dev_err(&intf->dev, "No LVS device is present\n");
+ return NULL;
+ }
+
+ hdev = interface_to_usbdev(intf);
+ hcd = bus_to_hcd(hdev->bus);
+
+ udev = usb_alloc_dev(hdev, hdev->bus, lvs->portnum);
+ if (!udev) {
+ dev_err(&intf->dev, "Could not allocate lvs udev\n");
+ return NULL;
+ }
+ udev->speed = USB_SPEED_SUPER;
+ udev->ep0.desc.wMaxPacketSize = cpu_to_le16(512);
+ usb_set_device_state(udev, USB_STATE_DEFAULT);
+
+ if (hcd->driver->enable_device) {
+ if (hcd->driver->enable_device(hcd, udev) < 0) {
+ dev_err(&intf->dev, "Failed to enable\n");
+ usb_put_dev(udev);
+ return NULL;
+ }
+ }
+
+ return udev;
+}
+
+static void destroy_lvs_device(struct usb_device *udev)
+{
+ struct usb_device *hdev = udev->parent;
+ struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
+
+ if (hcd->driver->free_dev)
+ hcd->driver->free_dev(hcd, udev);
+
+ usb_put_dev(udev);
+}
+
+static int lvs_rh_clear_port_feature(struct usb_device *hdev,
+ int port1, int feature)
+{
+ return usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
+ USB_REQ_CLEAR_FEATURE, USB_RT_PORT, feature, port1,
+ NULL, 0, 1000);
+}
+
+static int lvs_rh_set_port_feature(struct usb_device *hdev,
+ int port1, int feature)
+{
+ return usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
+ USB_REQ_SET_FEATURE, USB_RT_PORT, feature, port1,
+ NULL, 0, 1000);
+}
+
+static ssize_t u3_entry_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct usb_interface *intf = to_usb_interface(dev);
+ struct usb_device *hdev = interface_to_usbdev(intf);
+ struct lvs_rh *lvs = usb_get_intfdata(intf);
+ struct usb_device *udev;
+ int ret;
+
+ udev = create_lvs_device(intf);
+ if (!udev) {
+ dev_err(dev, "failed to create lvs device\n");
+ return -ENOMEM;
+ }
+
+ ret = lvs_rh_set_port_feature(hdev, lvs->portnum,
+ USB_PORT_FEAT_SUSPEND);
+ if (ret < 0)
+ dev_err(dev, "can't issue U3 entry %d\n", ret);
+
+ destroy_lvs_device(udev);
+
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+static DEVICE_ATTR_WO(u3_entry);
+
+static ssize_t u3_exit_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct usb_interface *intf = to_usb_interface(dev);
+ struct usb_device *hdev = interface_to_usbdev(intf);
+ struct lvs_rh *lvs = usb_get_intfdata(intf);
+ struct usb_device *udev;
+ int ret;
+
+ udev = create_lvs_device(intf);
+ if (!udev) {
+ dev_err(dev, "failed to create lvs device\n");
+ return -ENOMEM;
+ }
+
+ ret = lvs_rh_clear_port_feature(hdev, lvs->portnum,
+ USB_PORT_FEAT_SUSPEND);
+ if (ret < 0)
+ dev_err(dev, "can't issue U3 exit %d\n", ret);
+
+ destroy_lvs_device(udev);
+
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+static DEVICE_ATTR_WO(u3_exit);
+
+static ssize_t hot_reset_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct usb_interface *intf = to_usb_interface(dev);
+ struct usb_device *hdev = interface_to_usbdev(intf);
+ struct lvs_rh *lvs = usb_get_intfdata(intf);
+ int ret;
+
+ ret = lvs_rh_set_port_feature(hdev, lvs->portnum,
+ USB_PORT_FEAT_RESET);
+ if (ret < 0) {
+ dev_err(dev, "can't issue hot reset %d\n", ret);
+ return ret;
+ }
+
+ return count;
+}
+static DEVICE_ATTR_WO(hot_reset);
+
+static ssize_t u2_timeout_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct usb_interface *intf = to_usb_interface(dev);
+ struct usb_device *hdev = interface_to_usbdev(intf);
+ struct lvs_rh *lvs = usb_get_intfdata(intf);
+ unsigned long val;
+ int ret;
+
+ ret = kstrtoul(buf, 10, &val);
+ if (ret < 0) {
+ dev_err(dev, "couldn't parse string %d\n", ret);
+ return ret;
+ }
+
+ if (val < 0 || val > 127)
+ return -EINVAL;
+
+ ret = lvs_rh_set_port_feature(hdev, lvs->portnum | (val << 8),
+ USB_PORT_FEAT_U2_TIMEOUT);
+ if (ret < 0) {
+ dev_err(dev, "Error %d while setting U2 timeout %ld\n", ret, val);
+ return ret;
+ }
+
+ return count;
+}
+static DEVICE_ATTR_WO(u2_timeout);
+
+static ssize_t u1_timeout_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct usb_interface *intf = to_usb_interface(dev);
+ struct usb_device *hdev = interface_to_usbdev(intf);
+ struct lvs_rh *lvs = usb_get_intfdata(intf);
+ unsigned long val;
+ int ret;
+
+ ret = kstrtoul(buf, 10, &val);
+ if (ret < 0) {
+ dev_err(dev, "couldn't parse string %d\n", ret);
+ return ret;
+ }
+
+ if (val < 0 || val > 127)
+ return -EINVAL;
+
+ ret = lvs_rh_set_port_feature(hdev, lvs->portnum | (val << 8),
+ USB_PORT_FEAT_U1_TIMEOUT);
+ if (ret < 0) {
+ dev_err(dev, "Error %d while setting U1 timeout %ld\n", ret, val);
+ return ret;
+ }
+
+ return count;
+}
+static DEVICE_ATTR_WO(u1_timeout);
+
+static ssize_t get_dev_desc_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct usb_interface *intf = to_usb_interface(dev);
+ struct usb_device *udev;
+ struct usb_device_descriptor *descriptor;
+ int ret;
+
+ descriptor = kmalloc(sizeof(*descriptor), GFP_KERNEL);
+ if (!descriptor) {
+ dev_err(dev, "failed to allocate descriptor memory\n");
+ return -ENOMEM;
+ }
+
+ udev = create_lvs_device(intf);
+ if (!udev) {
+ dev_err(dev, "failed to create lvs device\n");
+ ret = -ENOMEM;
+ goto free_desc;
+ }
+
+ ret = usb_control_msg(udev, (PIPE_CONTROL << 30) | USB_DIR_IN,
+ USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, USB_DT_DEVICE << 8,
+ 0, descriptor, sizeof(*descriptor),
+ USB_CTRL_GET_TIMEOUT);
+ if (ret < 0)
+ dev_err(dev, "can't read device descriptor %d\n", ret);
+
+ destroy_lvs_device(udev);
+
+free_desc:
+ kfree(descriptor);
+
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+static DEVICE_ATTR_WO(get_dev_desc);
+
+static struct attribute *lvs_attributes[] = {
+ &dev_attr_get_dev_desc.attr,
+ &dev_attr_u1_timeout.attr,
+ &dev_attr_u2_timeout.attr,
+ &dev_attr_hot_reset.attr,
+ &dev_attr_u3_entry.attr,
+ &dev_attr_u3_exit.attr,
+ NULL
+};
+
+static const struct attribute_group lvs_attr_group = {
+ .attrs = lvs_attributes,
+};
+
+static void lvs_rh_work(struct work_struct *work)
+{
+ struct lvs_rh *lvs = container_of(work, struct lvs_rh, rh_work);
+ struct usb_interface *intf = lvs->intf;
+ struct usb_device *hdev = interface_to_usbdev(intf);
+ struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
+ struct usb_hub_descriptor *descriptor = &lvs->descriptor;
+ struct usb_port_status *port_status = &lvs->port_status;
+ int i, ret = 0;
+ u16 portchange;
+
+ /* Examine each root port */
+ for (i = 1; i <= descriptor->bNbrPorts; i++) {
+ ret = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),
+ USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, 0, i,
+ port_status, sizeof(*port_status), 1000);
+ if (ret < 4)
+ continue;
+
+ portchange = le16_to_cpu(port_status->wPortChange);
+
+ if (portchange & USB_PORT_STAT_C_LINK_STATE)
+ lvs_rh_clear_port_feature(hdev, i,
+ USB_PORT_FEAT_C_PORT_LINK_STATE);
+ if (portchange & USB_PORT_STAT_C_ENABLE)
+ lvs_rh_clear_port_feature(hdev, i,
+ USB_PORT_FEAT_C_ENABLE);
+ if (portchange & USB_PORT_STAT_C_RESET)
+ lvs_rh_clear_port_feature(hdev, i,
+ USB_PORT_FEAT_C_RESET);
+ if (portchange & USB_PORT_STAT_C_BH_RESET)
+ lvs_rh_clear_port_feature(hdev, i,
+ USB_PORT_FEAT_C_BH_PORT_RESET);
+ if (portchange & USB_PORT_STAT_C_CONNECTION) {
+ lvs_rh_clear_port_feature(hdev, i,
+ USB_PORT_FEAT_C_CONNECTION);
+
+ if (le16_to_cpu(port_status->wPortStatus) &
+ USB_PORT_STAT_CONNECTION) {
+ lvs->present = true;
+ lvs->portnum = i;
+ if (hcd->phy)
+ usb_phy_notify_connect(hcd->phy,
+ USB_SPEED_SUPER);
+ } else {
+ lvs->present = false;
+ if (hcd->phy)
+ usb_phy_notify_disconnect(hcd->phy,
+ USB_SPEED_SUPER);
+ }
+ break;
+ }
+ }
+
+ ret = usb_submit_urb(lvs->urb, GFP_KERNEL);
+ if (ret != 0 && ret != -ENODEV && ret != -EPERM)
+ dev_err(&intf->dev, "urb resubmit error %d\n", ret);
+}
+
+static void lvs_rh_irq(struct urb *urb)
+{
+ struct lvs_rh *lvs = urb->context;
+
+ queue_work(lvs->rh_queue, &lvs->rh_work);
+}
+
+static int lvs_rh_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *hdev;
+ struct usb_host_interface *desc;
+ struct usb_endpoint_descriptor *endpoint;
+ struct lvs_rh *lvs;
+ unsigned int pipe;
+ int ret, maxp;
+
+ hdev = interface_to_usbdev(intf);
+ desc = intf->cur_altsetting;
+ endpoint = &desc->endpoint[0].desc;
+
+ /* valid only for SS root hub */
+ if (hdev->descriptor.bDeviceProtocol != USB_HUB_PR_SS || hdev->parent) {
+ dev_err(&intf->dev, "Bind LVS driver with SS root Hub only\n");
+ return -EINVAL;
+ }
+
+ lvs = devm_kzalloc(&intf->dev, sizeof(*lvs), GFP_KERNEL);
+ if (!lvs)
+ return -ENOMEM;
+
+ lvs->intf = intf;
+ usb_set_intfdata(intf, lvs);
+
+ /* how many number of ports this root hub has */
+ ret = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),
+ USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RT_HUB,
+ USB_DT_SS_HUB << 8, 0, &lvs->descriptor,
+ USB_DT_SS_HUB_SIZE, USB_CTRL_GET_TIMEOUT);
+ if (ret < (USB_DT_HUB_NONVAR_SIZE + 2)) {
+ dev_err(&hdev->dev, "wrong root hub descriptor read %d\n", ret);
+ return ret;
+ }
+
+ /* submit urb to poll interrupt endpoint */
+ lvs->urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!lvs->urb) {
+ dev_err(&intf->dev, "couldn't allocate lvs urb\n");
+ return -ENOMEM;
+ }
+
+ lvs->rh_queue = create_singlethread_workqueue("lvs_rh_queue");
+ if (!lvs->rh_queue) {
+ dev_err(&intf->dev, "couldn't create workqueue\n");
+ ret = -ENOMEM;
+ goto free_urb;
+ }
+
+ 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 destroy_queue;
+ }
+
+ 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,
+ lvs_rh_irq, lvs, endpoint->bInterval);
+
+ 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;
+ }
+
+ return ret;
+
+sysfs_remove:
+ sysfs_remove_group(&intf->dev.kobj, &lvs_attr_group);
+destroy_queue:
+ destroy_workqueue(lvs->rh_queue);
+free_urb:
+ usb_free_urb(lvs->urb);
+ return ret;
+}
+
+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);
+ destroy_workqueue(lvs->rh_queue);
+ usb_free_urb(lvs->urb);
+}
+
+static struct usb_driver lvs_driver = {
+ .name = "lvs",
+ .probe = lvs_rh_probe,
+ .disconnect = lvs_rh_disconnect,
+};
+
+module_usb_driver(lvs_driver);
+
+MODULE_DESCRIPTION("Link Layer Validation System Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/misc/usb3503.c b/drivers/usb/misc/usb3503.c
index f43c61989cef..47cb143716a1 100644
--- a/drivers/usb/misc/usb3503.c
+++ b/drivers/usb/misc/usb3503.c
@@ -149,8 +149,6 @@ static int usb3503_switch_mode(struct usb3503 *hub, enum usb3503_mode mode)
case USB3503_MODE_STANDBY:
usb3503_reset(hub, 0);
-
- hub->mode = mode;
dev_info(dev, "switched to STANDBY mode\n");
break;
@@ -192,7 +190,8 @@ static int usb3503_probe(struct usb3503 *hub)
clk = devm_clk_get(dev, "refclk");
if (IS_ERR(clk) && PTR_ERR(clk) != -ENOENT) {
- dev_err(dev, "unable to request refclk (%d)\n", err);
+ dev_err(dev, "unable to request refclk (%ld)\n",
+ PTR_ERR(clk));
return PTR_ERR(clk);
}
@@ -346,6 +345,37 @@ static int usb3503_platform_probe(struct platform_device *pdev)
return usb3503_probe(hub);
}
+#ifdef CONFIG_PM_SLEEP
+static int usb3503_i2c_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct usb3503 *hub = i2c_get_clientdata(client);
+
+ usb3503_switch_mode(hub, USB3503_MODE_STANDBY);
+
+ if (hub->clk)
+ clk_disable_unprepare(hub->clk);
+
+ return 0;
+}
+
+static int usb3503_i2c_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct usb3503 *hub = i2c_get_clientdata(client);
+
+ if (hub->clk)
+ clk_prepare_enable(hub->clk);
+
+ usb3503_switch_mode(hub, hub->mode);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(usb3503_i2c_pm_ops, usb3503_i2c_suspend,
+ usb3503_i2c_resume);
+
static const struct i2c_device_id usb3503_id[] = {
{ USB3503_I2C_NAME, 0 },
{ }
@@ -364,6 +394,7 @@ MODULE_DEVICE_TABLE(of, usb3503_of_match);
static struct i2c_driver usb3503_i2c_driver = {
.driver = {
.name = USB3503_I2C_NAME,
+ .pm = &usb3503_i2c_pm_ops,
.of_match_table = of_match_ptr(usb3503_of_match),
},
.probe = usb3503_i2c_probe,
diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c
index d40d5f0b5528..ac4422b33dcd 100644
--- a/drivers/usb/musb/blackfin.c
+++ b/drivers/usb/musb/blackfin.c
@@ -455,7 +455,7 @@ static int bfin_probe(struct platform_device *pdev)
int ret = -ENOMEM;
- glue = kzalloc(sizeof(*glue), GFP_KERNEL);
+ glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);
if (!glue) {
dev_err(&pdev->dev, "failed to allocate glue context\n");
goto err0;
@@ -464,7 +464,7 @@ static int bfin_probe(struct platform_device *pdev)
musb = platform_device_alloc("musb-hdrc", PLATFORM_DEVID_AUTO);
if (!musb) {
dev_err(&pdev->dev, "failed to allocate musb device\n");
- goto err1;
+ goto err0;
}
musb->dev.parent = &pdev->dev;
@@ -478,7 +478,7 @@ static int bfin_probe(struct platform_device *pdev)
glue->phy = usb_phy_generic_register();
if (IS_ERR(glue->phy))
- goto err2;
+ goto err1;
platform_set_drvdata(pdev, glue);
memset(musb_resources, 0x00, sizeof(*musb_resources) *
@@ -498,31 +498,28 @@ static int bfin_probe(struct platform_device *pdev)
ARRAY_SIZE(musb_resources));
if (ret) {
dev_err(&pdev->dev, "failed to add resources\n");
- goto err3;
+ goto err2;
}
ret = platform_device_add_data(musb, pdata, sizeof(*pdata));
if (ret) {
dev_err(&pdev->dev, "failed to add platform_data\n");
- goto err3;
+ goto err2;
}
ret = platform_device_add(musb);
if (ret) {
dev_err(&pdev->dev, "failed to register musb device\n");
- goto err3;
+ goto err2;
}
return 0;
-err3:
- usb_phy_generic_unregister(glue->phy);
-
err2:
- platform_device_put(musb);
+ usb_phy_generic_unregister(glue->phy);
err1:
- kfree(glue);
+ platform_device_put(musb);
err0:
return ret;
@@ -534,7 +531,6 @@ static int bfin_remove(struct platform_device *pdev)
platform_device_unregister(glue->musb);
usb_phy_generic_unregister(glue->phy);
- kfree(glue);
return 0;
}
diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c
index de8492b06e46..110b78415bf0 100644
--- a/drivers/usb/musb/davinci.c
+++ b/drivers/usb/musb/davinci.c
@@ -519,23 +519,23 @@ static int davinci_probe(struct platform_device *pdev)
int ret = -ENOMEM;
- glue = kzalloc(sizeof(*glue), GFP_KERNEL);
+ glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);
if (!glue) {
dev_err(&pdev->dev, "failed to allocate glue context\n");
goto err0;
}
- clk = clk_get(&pdev->dev, "usb");
+ clk = devm_clk_get(&pdev->dev, "usb");
if (IS_ERR(clk)) {
dev_err(&pdev->dev, "failed to get clock\n");
ret = PTR_ERR(clk);
- goto err3;
+ goto err0;
}
ret = clk_enable(clk);
if (ret) {
dev_err(&pdev->dev, "failed to enable clock\n");
- goto err4;
+ goto err0;
}
glue->dev = &pdev->dev;
@@ -579,20 +579,14 @@ static int davinci_probe(struct platform_device *pdev)
if (IS_ERR(musb)) {
ret = PTR_ERR(musb);
dev_err(&pdev->dev, "failed to register musb device: %d\n", ret);
- goto err5;
+ goto err1;
}
return 0;
-err5:
+err1:
clk_disable(clk);
-err4:
- clk_put(clk);
-
-err3:
- kfree(glue);
-
err0:
return ret;
}
@@ -604,8 +598,6 @@ static int davinci_remove(struct platform_device *pdev)
platform_device_unregister(glue->musb);
usb_phy_generic_unregister();
clk_disable(glue->clk);
- clk_put(glue->clk);
- kfree(glue);
return 0;
}
diff --git a/drivers/usb/musb/jz4740.c b/drivers/usb/musb/jz4740.c
index 5f30537f1927..d1187290d4e3 100644
--- a/drivers/usb/musb/jz4740.c
+++ b/drivers/usb/musb/jz4740.c
@@ -19,6 +19,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/usb/usb_phy_generic.h>
#include "musb_core.h"
@@ -80,6 +81,7 @@ static struct musb_hdrc_platform_data jz4740_musb_platform_data = {
static int jz4740_musb_init(struct musb *musb)
{
+ usb_phy_generic_register();
musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2);
if (!musb->xceiv) {
pr_err("HS UDC: no transceiver configured\n");
@@ -182,6 +184,7 @@ static int jz4740_remove(struct platform_device *pdev)
struct jz4740_glue *glue = platform_get_drvdata(pdev);
platform_device_unregister(glue->musb);
+ usb_phy_generic_unregister(pdev);
clk_disable_unprepare(glue->clk);
return 0;
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
index eff3c5cf84f4..b841ee0bff06 100644
--- a/drivers/usb/musb/musb_core.c
+++ b/drivers/usb/musb/musb_core.c
@@ -850,7 +850,8 @@ b_host:
/* handle babble condition */
if (int_usb & MUSB_INTR_BABBLE && is_host_active(musb))
- schedule_work(&musb->recover_work);
+ schedule_delayed_work(&musb->recover_work,
+ msecs_to_jiffies(100));
#if 0
/* REVISIT ... this would be for multiplexing periodic endpoints, or
@@ -1517,7 +1518,7 @@ irqreturn_t musb_interrupt(struct musb *musb)
devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
dev_dbg(musb->controller, "** IRQ %s usb%04x tx%04x rx%04x\n",
- (devctl & MUSB_DEVCTL_HM) ? "host" : "peripheral",
+ is_host_active(musb) ? "host" : "peripheral",
musb->int_usb, musb->int_tx, musb->int_rx);
/* the core can interrupt us for multiple reasons; docs have
@@ -1531,7 +1532,7 @@ irqreturn_t musb_interrupt(struct musb *musb)
/* handle endpoint 0 first */
if (musb->int_tx & 1) {
- if (devctl & MUSB_DEVCTL_HM)
+ if (is_host_active(musb))
retval |= musb_h_ep0_irq(musb);
else
retval |= musb_g_ep0_irq(musb);
@@ -1545,7 +1546,7 @@ irqreturn_t musb_interrupt(struct musb *musb)
/* musb_ep_select(musb->mregs, ep_num); */
/* REVISIT just retval = ep->rx_irq(...) */
retval = IRQ_HANDLED;
- if (devctl & MUSB_DEVCTL_HM)
+ if (is_host_active(musb))
musb_host_rx(musb, ep_num);
else
musb_g_rx(musb, ep_num);
@@ -1563,7 +1564,7 @@ irqreturn_t musb_interrupt(struct musb *musb)
/* musb_ep_select(musb->mregs, ep_num); */
/* REVISIT just retval |= ep->tx_irq(...) */
retval = IRQ_HANDLED;
- if (devctl & MUSB_DEVCTL_HM)
+ if (is_host_active(musb))
musb_host_tx(musb, ep_num);
else
musb_g_tx(musb, ep_num);
@@ -1585,15 +1586,13 @@ MODULE_PARM_DESC(use_dma, "enable/disable use of DMA");
void musb_dma_completion(struct musb *musb, u8 epnum, u8 transmit)
{
- u8 devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
-
/* called with controller lock already held */
if (!epnum) {
#ifndef CONFIG_USB_TUSB_OMAP_DMA
if (!is_cppi_enabled()) {
/* endpoint 0 */
- if (devctl & MUSB_DEVCTL_HM)
+ if (is_host_active(musb))
musb_h_ep0_irq(musb);
else
musb_g_ep0_irq(musb);
@@ -1602,13 +1601,13 @@ void musb_dma_completion(struct musb *musb, u8 epnum, u8 transmit)
} else {
/* endpoints 1..15 */
if (transmit) {
- if (devctl & MUSB_DEVCTL_HM)
+ if (is_host_active(musb))
musb_host_tx(musb, epnum);
else
musb_g_tx(musb, epnum);
} else {
/* receive */
- if (devctl & MUSB_DEVCTL_HM)
+ if (is_host_active(musb))
musb_host_rx(musb, epnum);
else
musb_g_rx(musb, epnum);
@@ -1753,20 +1752,22 @@ static void musb_irq_work(struct work_struct *data)
/* Recover from babble interrupt conditions */
static void musb_recover_work(struct work_struct *data)
{
- struct musb *musb = container_of(data, struct musb, recover_work);
- int status;
+ struct musb *musb = container_of(data, struct musb, recover_work.work);
+ int status, ret;
- musb_platform_reset(musb);
+ ret = musb_platform_reset(musb);
+ if (ret)
+ return;
usb_phy_vbus_off(musb->xceiv);
- udelay(100);
+ usleep_range(100, 200);
usb_phy_vbus_on(musb->xceiv);
- udelay(100);
+ usleep_range(100, 200);
/*
- * When a babble condition occurs, the musb controller removes the
- * session bit and the endpoint config is lost.
+ * When a babble condition occurs, the musb controller
+ * removes the session bit and the endpoint config is lost.
*/
if (musb->dyn_fifo)
status = ep_config_from_table(musb);
@@ -1945,7 +1946,7 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
/* Init IRQ workqueue before request_irq */
INIT_WORK(&musb->irq_work, musb_irq_work);
- INIT_WORK(&musb->recover_work, musb_recover_work);
+ INIT_DELAYED_WORK(&musb->recover_work, musb_recover_work);
INIT_DELAYED_WORK(&musb->deassert_reset_work, musb_deassert_reset);
INIT_DELAYED_WORK(&musb->finish_resume_work, musb_host_finish_resume);
@@ -2041,7 +2042,7 @@ fail4:
fail3:
cancel_work_sync(&musb->irq_work);
- cancel_work_sync(&musb->recover_work);
+ cancel_delayed_work_sync(&musb->recover_work);
cancel_delayed_work_sync(&musb->finish_resume_work);
cancel_delayed_work_sync(&musb->deassert_reset_work);
if (musb->dma_controller)
@@ -2107,7 +2108,7 @@ static int musb_remove(struct platform_device *pdev)
dma_controller_destroy(musb->dma_controller);
cancel_work_sync(&musb->irq_work);
- cancel_work_sync(&musb->recover_work);
+ cancel_delayed_work_sync(&musb->recover_work);
cancel_delayed_work_sync(&musb->finish_resume_work);
cancel_delayed_work_sync(&musb->deassert_reset_work);
musb_free(musb);
diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h
index d155a156f240..414e57a984bb 100644
--- a/drivers/usb/musb/musb_core.h
+++ b/drivers/usb/musb/musb_core.h
@@ -192,7 +192,7 @@ struct musb_platform_ops {
int (*set_mode)(struct musb *musb, u8 mode);
void (*try_idle)(struct musb *musb, unsigned long timeout);
- void (*reset)(struct musb *musb);
+ int (*reset)(struct musb *musb);
int (*vbus_status)(struct musb *musb);
void (*set_vbus)(struct musb *musb, int on);
@@ -297,7 +297,7 @@ struct musb {
irqreturn_t (*isr)(int, void *);
struct work_struct irq_work;
- struct work_struct recover_work;
+ struct delayed_work recover_work;
struct delayed_work deassert_reset_work;
struct delayed_work finish_resume_work;
u16 hwvers;
@@ -555,10 +555,12 @@ static inline void musb_platform_try_idle(struct musb *musb,
musb->ops->try_idle(musb, timeout);
}
-static inline void musb_platform_reset(struct musb *musb)
+static inline int musb_platform_reset(struct musb *musb)
{
- if (musb->ops->reset)
- musb->ops->reset(musb);
+ if (!musb->ops->reset)
+ return -EINVAL;
+
+ return musb->ops->reset(musb);
}
static inline int musb_platform_get_vbus_status(struct musb *musb)
diff --git a/drivers/usb/musb/musb_cppi41.c b/drivers/usb/musb/musb_cppi41.c
index 5341bb223b7c..47ae6455d073 100644
--- a/drivers/usb/musb/musb_cppi41.c
+++ b/drivers/usb/musb/musb_cppi41.c
@@ -39,7 +39,6 @@ struct cppi41_dma_channel {
u32 transferred;
u32 packet_sz;
struct list_head tx_check;
- struct work_struct dma_completion;
};
#define MUSB_DMA_NUM_CHANNELS 15
@@ -74,15 +73,18 @@ static void save_rx_toggle(struct cppi41_dma_channel *cppi41_channel)
static void update_rx_toggle(struct cppi41_dma_channel *cppi41_channel)
{
+ struct musb_hw_ep *hw_ep = cppi41_channel->hw_ep;
+ struct musb *musb = hw_ep->musb;
u16 csr;
u8 toggle;
if (cppi41_channel->is_tx)
return;
- if (!is_host_active(cppi41_channel->controller->musb))
+ if (!is_host_active(musb))
return;
- csr = musb_readw(cppi41_channel->hw_ep->regs, MUSB_RXCSR);
+ musb_ep_select(musb->mregs, hw_ep->epnum);
+ csr = musb_readw(hw_ep->regs, MUSB_RXCSR);
toggle = csr & MUSB_RXCSR_H_DATATOGGLE ? 1 : 0;
/*
@@ -107,24 +109,13 @@ static bool musb_is_tx_fifo_empty(struct musb_hw_ep *hw_ep)
void __iomem *epio = musb->endpoints[epnum].regs;
u16 csr;
+ musb_ep_select(musb->mregs, hw_ep->epnum);
csr = musb_readw(epio, MUSB_TXCSR);
if (csr & MUSB_TXCSR_TXPKTRDY)
return false;
return true;
}
-static bool is_isoc(struct musb_hw_ep *hw_ep, bool in)
-{
- if (in && hw_ep->in_qh) {
- if (hw_ep->in_qh->type == USB_ENDPOINT_XFER_ISOC)
- return true;
- } else if (hw_ep->out_qh) {
- if (hw_ep->out_qh->type == USB_ENDPOINT_XFER_ISOC)
- return true;
- }
- return false;
-}
-
static void cppi41_dma_callback(void *private_data);
static void cppi41_trans_done(struct cppi41_dma_channel *cppi41_channel)
@@ -139,6 +130,7 @@ static void cppi41_trans_done(struct cppi41_dma_channel *cppi41_channel)
cppi41_channel->channel.actual_len =
cppi41_channel->transferred;
cppi41_channel->channel.status = MUSB_DMA_STATUS_FREE;
+ cppi41_channel->channel.rx_packet_done = true;
musb_dma_completion(musb, hw_ep->epnum, cppi41_channel->is_tx);
} else {
/* next iteration, reload */
@@ -172,6 +164,7 @@ static void cppi41_trans_done(struct cppi41_dma_channel *cppi41_channel)
dma_async_issue_pending(dc);
if (!cppi41_channel->is_tx) {
+ musb_ep_select(musb->mregs, hw_ep->epnum);
csr = musb_readw(epio, MUSB_RXCSR);
csr |= MUSB_RXCSR_H_REQPKT;
musb_writew(epio, MUSB_RXCSR, csr);
@@ -179,32 +172,6 @@ static void cppi41_trans_done(struct cppi41_dma_channel *cppi41_channel)
}
}
-static void cppi_trans_done_work(struct work_struct *work)
-{
- unsigned long flags;
- struct cppi41_dma_channel *cppi41_channel =
- container_of(work, struct cppi41_dma_channel, dma_completion);
- struct cppi41_dma_controller *controller = cppi41_channel->controller;
- struct musb *musb = controller->musb;
- struct musb_hw_ep *hw_ep = cppi41_channel->hw_ep;
- bool empty;
-
- if (!cppi41_channel->is_tx && is_isoc(hw_ep, 1)) {
- spin_lock_irqsave(&musb->lock, flags);
- cppi41_trans_done(cppi41_channel);
- spin_unlock_irqrestore(&musb->lock, flags);
- } else {
- empty = musb_is_tx_fifo_empty(hw_ep);
- if (empty) {
- spin_lock_irqsave(&musb->lock, flags);
- cppi41_trans_done(cppi41_channel);
- spin_unlock_irqrestore(&musb->lock, flags);
- } else {
- schedule_work(&cppi41_channel->dma_completion);
- }
- }
-}
-
static enum hrtimer_restart cppi41_recheck_tx_req(struct hrtimer *timer)
{
struct cppi41_dma_controller *controller;
@@ -233,7 +200,7 @@ static enum hrtimer_restart cppi41_recheck_tx_req(struct hrtimer *timer)
if (!list_empty(&controller->early_tx_list)) {
ret = HRTIMER_RESTART;
hrtimer_forward_now(&controller->early_tx,
- ktime_set(0, 150 * NSEC_PER_USEC));
+ ktime_set(0, 50 * NSEC_PER_USEC));
}
spin_unlock_irqrestore(&musb->lock, flags);
@@ -268,14 +235,6 @@ static void cppi41_dma_callback(void *private_data)
transferred < cppi41_channel->packet_sz)
cppi41_channel->prog_len = 0;
- if (!cppi41_channel->is_tx) {
- if (is_isoc(hw_ep, 1))
- schedule_work(&cppi41_channel->dma_completion);
- else
- cppi41_trans_done(cppi41_channel);
- goto out;
- }
-
empty = musb_is_tx_fifo_empty(hw_ep);
if (empty) {
cppi41_trans_done(cppi41_channel);
@@ -312,15 +271,13 @@ static void cppi41_dma_callback(void *private_data)
goto out;
}
}
- if (is_isoc(hw_ep, 0)) {
- schedule_work(&cppi41_channel->dma_completion);
- goto out;
- }
list_add_tail(&cppi41_channel->tx_check,
&controller->early_tx_list);
if (!hrtimer_is_queued(&controller->early_tx)) {
+ unsigned long usecs = cppi41_channel->total_len / 10;
+
hrtimer_start_range_ns(&controller->early_tx,
- ktime_set(0, 140 * NSEC_PER_USEC),
+ ktime_set(0, usecs * NSEC_PER_USEC),
40 * NSEC_PER_USEC,
HRTIMER_MODE_REL);
}
@@ -450,6 +407,7 @@ static bool cppi41_configure_channel(struct dma_channel *channel,
dma_desc->callback = cppi41_dma_callback;
dma_desc->callback_param = channel;
cppi41_channel->cookie = dma_desc->tx_submit(dma_desc);
+ cppi41_channel->channel.rx_packet_done = false;
save_rx_toggle(cppi41_channel);
dma_async_issue_pending(dc);
@@ -672,8 +630,6 @@ static int cppi41_dma_controller_start(struct cppi41_dma_controller *controller)
cppi41_channel->port_num = port;
cppi41_channel->is_tx = is_tx;
INIT_LIST_HEAD(&cppi41_channel->tx_check);
- INIT_WORK(&cppi41_channel->dma_completion,
- cppi_trans_done_work);
musb_dma = &cppi41_channel->channel;
musb_dma->private_data = cppi41_channel;
diff --git a/drivers/usb/musb/musb_dma.h b/drivers/usb/musb/musb_dma.h
index 1345a4ff041a..1d44faa86252 100644
--- a/drivers/usb/musb/musb_dma.h
+++ b/drivers/usb/musb/musb_dma.h
@@ -129,6 +129,7 @@ struct dma_channel {
size_t actual_len;
enum dma_channel_status status;
bool desired_mode;
+ bool rx_packet_done;
};
/*
diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c
index 09529f94e72d..c791ba5da91a 100644
--- a/drivers/usb/musb/musb_dsps.c
+++ b/drivers/usb/musb/musb_dsps.c
@@ -56,16 +56,24 @@ static const struct of_device_id musb_dsps_of_match[];
* dependent on musb core layer symbols.
*/
static inline u8 dsps_readb(const void __iomem *addr, unsigned offset)
- { return __raw_readb(addr + offset); }
+{
+ return __raw_readb(addr + offset);
+}
static inline u32 dsps_readl(const void __iomem *addr, unsigned offset)
- { return __raw_readl(addr + offset); }
+{
+ return __raw_readl(addr + offset);
+}
static inline void dsps_writeb(void __iomem *addr, unsigned offset, u8 data)
- { __raw_writeb(data, addr + offset); }
+{
+ __raw_writeb(data, addr + offset);
+}
static inline void dsps_writel(void __iomem *addr, unsigned offset, u32 data)
- { __raw_writel(data, addr + offset); }
+{
+ __raw_writel(data, addr + offset);
+}
/**
* DSPS musb wrapper register offset.
@@ -136,6 +144,7 @@ struct dsps_glue {
const struct dsps_musb_wrapper *wrp; /* wrapper register offsets */
struct timer_list timer; /* otg_workaround timer */
unsigned long last_timer; /* last timer data for each instance */
+ bool sw_babble_enabled;
struct dsps_context context;
struct debugfs_regset32 regset;
@@ -469,6 +478,19 @@ static int dsps_musb_init(struct musb *musb)
val &= ~(1 << wrp->otg_disable);
dsps_writel(musb->ctrl_base, wrp->phy_utmi, val);
+ /*
+ * Check whether the dsps version has babble control enabled.
+ * In latest silicon revision the babble control logic is enabled.
+ * If MUSB_BABBLE_CTL returns 0x4 then we have the babble control
+ * logic enabled.
+ */
+ val = dsps_readb(musb->mregs, MUSB_BABBLE_CTL);
+ if (val == MUSB_BABBLE_RCV_DISABLE) {
+ glue->sw_babble_enabled = true;
+ val |= MUSB_BABBLE_SW_SESSION_CTRL;
+ dsps_writeb(musb->mregs, MUSB_BABBLE_CTL, val);
+ }
+
ret = dsps_musb_dbg_init(musb, glue);
if (ret)
return ret;
@@ -535,14 +557,82 @@ static int dsps_musb_set_mode(struct musb *musb, u8 mode)
return 0;
}
-static void dsps_musb_reset(struct musb *musb)
+static bool sw_babble_control(struct musb *musb)
+{
+ u8 babble_ctl;
+ bool session_restart = false;
+
+ babble_ctl = dsps_readb(musb->mregs, MUSB_BABBLE_CTL);
+ dev_dbg(musb->controller, "babble: MUSB_BABBLE_CTL value %x\n",
+ babble_ctl);
+ /*
+ * check line monitor flag to check whether babble is
+ * due to noise
+ */
+ dev_dbg(musb->controller, "STUCK_J is %s\n",
+ babble_ctl & MUSB_BABBLE_STUCK_J ? "set" : "reset");
+
+ if (babble_ctl & MUSB_BABBLE_STUCK_J) {
+ int timeout = 10;
+
+ /*
+ * babble is due to noise, then set transmit idle (d7 bit)
+ * to resume normal operation
+ */
+ babble_ctl = dsps_readb(musb->mregs, MUSB_BABBLE_CTL);
+ babble_ctl |= MUSB_BABBLE_FORCE_TXIDLE;
+ dsps_writeb(musb->mregs, MUSB_BABBLE_CTL, babble_ctl);
+
+ /* wait till line monitor flag cleared */
+ dev_dbg(musb->controller, "Set TXIDLE, wait J to clear\n");
+ do {
+ babble_ctl = dsps_readb(musb->mregs, MUSB_BABBLE_CTL);
+ udelay(1);
+ } while ((babble_ctl & MUSB_BABBLE_STUCK_J) && timeout--);
+
+ /* check whether stuck_at_j bit cleared */
+ if (babble_ctl & MUSB_BABBLE_STUCK_J) {
+ /*
+ * real babble condition has occurred
+ * restart the controller to start the
+ * session again
+ */
+ dev_dbg(musb->controller, "J not cleared, misc (%x)\n",
+ babble_ctl);
+ session_restart = true;
+ }
+ } else {
+ session_restart = true;
+ }
+
+ return session_restart;
+}
+
+static int dsps_musb_reset(struct musb *musb)
{
struct device *dev = musb->controller;
struct dsps_glue *glue = dev_get_drvdata(dev->parent);
const struct dsps_musb_wrapper *wrp = glue->wrp;
+ int session_restart = 0;
+
+ if (glue->sw_babble_enabled)
+ session_restart = sw_babble_control(musb);
+ /*
+ * In case of new silicon version babble condition can be recovered
+ * without resetting the MUSB. But for older silicon versions, MUSB
+ * reset is needed
+ */
+ if (session_restart || !glue->sw_babble_enabled) {
+ dev_info(musb->controller, "Restarting MUSB to recover from Babble\n");
+ dsps_writel(musb->ctrl_base, wrp->control, (1 << wrp->reset));
+ usleep_range(100, 200);
+ usb_phy_shutdown(musb->xceiv);
+ usleep_range(100, 200);
+ usb_phy_init(musb->xceiv);
+ session_restart = 1;
+ }
- dsps_writel(musb->ctrl_base, wrp->control, (1 << wrp->reset));
- udelay(100);
+ return !session_restart;
}
static struct musb_platform_ops dsps_ops = {
diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
index eb06291a40c8..855793d701bb 100644
--- a/drivers/usb/musb/musb_host.c
+++ b/drivers/usb/musb/musb_host.c
@@ -120,7 +120,7 @@ static void musb_h_tx_flush_fifo(struct musb_hw_ep *ep)
if (csr != lastcsr)
dev_dbg(musb->controller, "Host TX FIFONOTEMPTY csr: %02x\n", csr);
lastcsr = csr;
- csr |= MUSB_TXCSR_FLUSHFIFO;
+ csr |= MUSB_TXCSR_FLUSHFIFO | MUSB_TXCSR_TXPKTRDY;
musb_writew(epio, MUSB_TXCSR, csr);
csr = musb_readw(epio, MUSB_TXCSR);
if (WARN(retries-- < 1,
@@ -1295,7 +1295,7 @@ done:
if (status) {
if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) {
dma->status = MUSB_DMA_STATUS_CORE_ABORT;
- (void) musb->dma_controller->channel_abort(dma);
+ musb->dma_controller->channel_abort(dma);
}
/* do the proper sequence to abort the transfer in the
@@ -1640,7 +1640,7 @@ void musb_host_rx(struct musb *musb, u8 epnum)
/* clean up dma and collect transfer count */
if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) {
dma->status = MUSB_DMA_STATUS_CORE_ABORT;
- (void) musb->dma_controller->channel_abort(dma);
+ musb->dma_controller->channel_abort(dma);
xfer_len = dma->actual_len;
}
musb_h_flush_rxfifo(hw_ep, MUSB_RXCSR_CLRDATATOG);
@@ -1671,7 +1671,7 @@ void musb_host_rx(struct musb *musb, u8 epnum)
*/
if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) {
dma->status = MUSB_DMA_STATUS_CORE_ABORT;
- (void) musb->dma_controller->channel_abort(dma);
+ musb->dma_controller->channel_abort(dma);
xfer_len = dma->actual_len;
done = true;
}
@@ -1734,10 +1734,11 @@ void musb_host_rx(struct musb *musb, u8 epnum)
}
} else {
- /* done if urb buffer is full or short packet is recd */
- done = (urb->actual_length + xfer_len >=
- urb->transfer_buffer_length
- || dma->actual_len < qh->maxpacket);
+ /* done if urb buffer is full or short packet is recd */
+ done = (urb->actual_length + xfer_len >=
+ urb->transfer_buffer_length
+ || dma->actual_len < qh->maxpacket
+ || dma->rx_packet_done);
}
/* send IN token for next packet, without AUTOREQ */
@@ -1957,7 +1958,7 @@ static int musb_schedule(
struct musb_qh *qh,
int is_in)
{
- int idle;
+ int idle = 0;
int best_diff;
int best_end, epnum;
struct musb_hw_ep *hw_ep = NULL;
diff --git a/drivers/usb/musb/musb_regs.h b/drivers/usb/musb/musb_regs.h
index 03f2655af290..b9bcda5e3945 100644
--- a/drivers/usb/musb/musb_regs.h
+++ b/drivers/usb/musb/musb_regs.h
@@ -72,6 +72,12 @@
#define MUSB_DEVCTL_HR 0x02
#define MUSB_DEVCTL_SESSION 0x01
+/* BABBLE_CTL */
+#define MUSB_BABBLE_FORCE_TXIDLE 0x80
+#define MUSB_BABBLE_SW_SESSION_CTRL 0x40
+#define MUSB_BABBLE_STUCK_J 0x20
+#define MUSB_BABBLE_RCV_DISABLE 0x04
+
/* MUSB ULPI VBUSCONTROL */
#define MUSB_ULPI_USE_EXTVBUS 0x01
#define MUSB_ULPI_USE_EXTVBUSIND 0x02
@@ -246,6 +252,7 @@
*/
#define MUSB_DEVCTL 0x60 /* 8 bit */
+#define MUSB_BABBLE_CTL 0x61 /* 8 bit */
/* These are always controlled through the INDEX register */
#define MUSB_TXFIFOSZ 0x62 /* 8-bit (see masks) */
diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c
index 159ef4be1ef2..7dfc6cb732c9 100644
--- a/drivers/usb/musb/tusb6010.c
+++ b/drivers/usb/musb/tusb6010.c
@@ -22,6 +22,7 @@
#include <linux/usb.h>
#include <linux/irq.h>
#include <linux/io.h>
+#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/usb/usb_phy_generic.h>
@@ -1160,12 +1161,12 @@ static int tusb_probe(struct platform_device *pdev)
struct platform_device *musb;
struct tusb6010_glue *glue;
struct platform_device_info pinfo;
- int ret = -ENOMEM;
+ int ret;
- glue = kzalloc(sizeof(*glue), GFP_KERNEL);
+ glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);
if (!glue) {
dev_err(&pdev->dev, "failed to allocate glue context\n");
- goto err0;
+ return -ENOMEM;
}
glue->dev = &pdev->dev;
@@ -1204,16 +1205,10 @@ static int tusb_probe(struct platform_device *pdev)
if (IS_ERR(musb)) {
ret = PTR_ERR(musb);
dev_err(&pdev->dev, "failed to register musb device: %d\n", ret);
- goto err3;
+ return ret;
}
return 0;
-
-err3:
- kfree(glue);
-
-err0:
- return ret;
}
static int tusb_remove(struct platform_device *pdev)
@@ -1222,7 +1217,6 @@ static int tusb_remove(struct platform_device *pdev)
platform_device_unregister(glue->musb);
usb_phy_generic_unregister(glue->phy);
- kfree(glue);
return 0;
}
diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c
index f202e5088461..dc666e96f45f 100644
--- a/drivers/usb/musb/ux500.c
+++ b/drivers/usb/musb/ux500.c
@@ -246,7 +246,7 @@ static int ux500_probe(struct platform_device *pdev)
}
}
- glue = kzalloc(sizeof(*glue), GFP_KERNEL);
+ glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);
if (!glue) {
dev_err(&pdev->dev, "failed to allocate glue context\n");
goto err0;
@@ -255,20 +255,20 @@ static int ux500_probe(struct platform_device *pdev)
musb = platform_device_alloc("musb-hdrc", PLATFORM_DEVID_AUTO);
if (!musb) {
dev_err(&pdev->dev, "failed to allocate musb device\n");
- goto err1;
+ goto err0;
}
- clk = clk_get(&pdev->dev, NULL);
+ clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(clk)) {
dev_err(&pdev->dev, "failed to get clock\n");
ret = PTR_ERR(clk);
- goto err3;
+ goto err1;
}
ret = clk_prepare_enable(clk);
if (ret) {
dev_err(&pdev->dev, "failed to enable clock\n");
- goto err4;
+ goto err1;
}
musb->dev.parent = &pdev->dev;
@@ -301,34 +301,28 @@ static int ux500_probe(struct platform_device *pdev)
ARRAY_SIZE(musb_resources));
if (ret) {
dev_err(&pdev->dev, "failed to add resources\n");
- goto err5;
+ goto err2;
}
ret = platform_device_add_data(musb, pdata, sizeof(*pdata));
if (ret) {
dev_err(&pdev->dev, "failed to add platform_data\n");
- goto err5;
+ goto err2;
}
ret = platform_device_add(musb);
if (ret) {
dev_err(&pdev->dev, "failed to register musb device\n");
- goto err5;
+ goto err2;
}
return 0;
-err5:
+err2:
clk_disable_unprepare(clk);
-err4:
- clk_put(clk);
-
-err3:
- platform_device_put(musb);
-
err1:
- kfree(glue);
+ platform_device_put(musb);
err0:
return ret;
@@ -340,8 +334,6 @@ static int ux500_remove(struct platform_device *pdev)
platform_device_unregister(glue->musb);
clk_disable_unprepare(glue->clk);
- clk_put(glue->clk);
- kfree(glue);
return 0;
}
diff --git a/drivers/usb/phy/phy-am335x.c b/drivers/usb/phy/phy-am335x.c
index 585e50cb1980..b70e05537180 100644
--- a/drivers/usb/phy/phy-am335x.c
+++ b/drivers/usb/phy/phy-am335x.c
@@ -122,16 +122,10 @@ static int am335x_phy_resume(struct device *dev)
return 0;
}
-
-static const struct dev_pm_ops am335x_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(am335x_phy_suspend, am335x_phy_resume)
-};
-
-#define DEV_PM_OPS (&am335x_pm_ops)
-#else
-#define DEV_PM_OPS NULL
#endif
+static SIMPLE_DEV_PM_OPS(am335x_pm_ops, am335x_phy_suspend, am335x_phy_resume);
+
static const struct of_device_id am335x_phy_ids[] = {
{ .compatible = "ti,am335x-usb-phy" },
{ }
@@ -144,7 +138,7 @@ static struct platform_driver am335x_phy_driver = {
.driver = {
.name = "am335x-phy-driver",
.owner = THIS_MODULE,
- .pm = DEV_PM_OPS,
+ .pm = &am335x_pm_ops,
.of_match_table = am335x_phy_ids,
},
};
diff --git a/drivers/usb/phy/phy-gpio-vbus-usb.c b/drivers/usb/phy/phy-gpio-vbus-usb.c
index 69462e09d014..ea9e705555df 100644
--- a/drivers/usb/phy/phy-gpio-vbus-usb.c
+++ b/drivers/usb/phy/phy-gpio-vbus-usb.c
@@ -253,11 +253,13 @@ static int gpio_vbus_probe(struct platform_device *pdev)
return -EINVAL;
gpio = pdata->gpio_vbus;
- gpio_vbus = kzalloc(sizeof(struct gpio_vbus_data), GFP_KERNEL);
+ gpio_vbus = devm_kzalloc(&pdev->dev, sizeof(struct gpio_vbus_data),
+ GFP_KERNEL);
if (!gpio_vbus)
return -ENOMEM;
- gpio_vbus->phy.otg = kzalloc(sizeof(struct usb_otg), GFP_KERNEL);
+ gpio_vbus->phy.otg = devm_kzalloc(&pdev->dev, sizeof(struct usb_otg),
+ GFP_KERNEL);
if (!gpio_vbus->phy.otg) {
kfree(gpio_vbus);
return -ENOMEM;
@@ -274,11 +276,11 @@ static int gpio_vbus_probe(struct platform_device *pdev)
gpio_vbus->phy.otg->phy = &gpio_vbus->phy;
gpio_vbus->phy.otg->set_peripheral = gpio_vbus_set_peripheral;
- err = gpio_request(gpio, "vbus_detect");
+ err = devm_gpio_request(&pdev->dev, gpio, "vbus_detect");
if (err) {
dev_err(&pdev->dev, "can't request vbus gpio %d, err: %d\n",
gpio, err);
- goto err_gpio;
+ return err;
}
gpio_direction_input(gpio);
@@ -296,27 +298,27 @@ static int gpio_vbus_probe(struct platform_device *pdev)
/* if data line pullup is in use, initialize it to "not pulling up" */
gpio = pdata->gpio_pullup;
if (gpio_is_valid(gpio)) {
- err = gpio_request(gpio, "udc_pullup");
+ err = devm_gpio_request(&pdev->dev, gpio, "udc_pullup");
if (err) {
dev_err(&pdev->dev,
"can't request pullup gpio %d, err: %d\n",
gpio, err);
- gpio_free(pdata->gpio_vbus);
- goto err_gpio;
+ return err;
}
gpio_direction_output(gpio, pdata->gpio_pullup_inverted);
}
- err = request_irq(irq, gpio_vbus_irq, irqflags, "vbus_detect", pdev);
+ err = devm_request_irq(&pdev->dev, irq, gpio_vbus_irq, irqflags,
+ "vbus_detect", pdev);
if (err) {
dev_err(&pdev->dev, "can't request irq %i, err: %d\n",
irq, err);
- goto err_irq;
+ return err;
}
INIT_DELAYED_WORK(&gpio_vbus->work, gpio_vbus_work);
- gpio_vbus->vbus_draw = regulator_get(&pdev->dev, "vbus_draw");
+ gpio_vbus->vbus_draw = devm_regulator_get(&pdev->dev, "vbus_draw");
if (IS_ERR(gpio_vbus->vbus_draw)) {
dev_dbg(&pdev->dev, "can't get vbus_draw regulator, err: %ld\n",
PTR_ERR(gpio_vbus->vbus_draw));
@@ -328,44 +330,23 @@ static int gpio_vbus_probe(struct platform_device *pdev)
if (err) {
dev_err(&pdev->dev, "can't register transceiver, err: %d\n",
err);
- goto err_otg;
+ return err;
}
device_init_wakeup(&pdev->dev, pdata->wakeup);
return 0;
-err_otg:
- regulator_put(gpio_vbus->vbus_draw);
- free_irq(irq, pdev);
-err_irq:
- if (gpio_is_valid(pdata->gpio_pullup))
- gpio_free(pdata->gpio_pullup);
- gpio_free(pdata->gpio_vbus);
-err_gpio:
- kfree(gpio_vbus->phy.otg);
- kfree(gpio_vbus);
- return err;
}
static int gpio_vbus_remove(struct platform_device *pdev)
{
struct gpio_vbus_data *gpio_vbus = platform_get_drvdata(pdev);
- struct gpio_vbus_mach_info *pdata = dev_get_platdata(&pdev->dev);
- int gpio = pdata->gpio_vbus;
device_init_wakeup(&pdev->dev, 0);
cancel_delayed_work_sync(&gpio_vbus->work);
- regulator_put(gpio_vbus->vbus_draw);
usb_remove_phy(&gpio_vbus->phy);
- free_irq(gpio_vbus->irq, pdev);
- if (gpio_is_valid(pdata->gpio_pullup))
- gpio_free(pdata->gpio_pullup);
- gpio_free(gpio);
- kfree(gpio_vbus->phy.otg);
- kfree(gpio_vbus);
-
return 0;
}
diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c
index c929370cdaa6..e4108eec5ef4 100644
--- a/drivers/usb/phy/phy-msm-usb.c
+++ b/drivers/usb/phy/phy-msm-usb.c
@@ -279,11 +279,11 @@ static int msm_otg_link_clk_reset(struct msm_otg *motg, bool assert)
static int msm_otg_phy_clk_reset(struct msm_otg *motg)
{
- int ret;
+ int ret = 0;
- if (motg->pdata->phy_clk_reset)
+ if (motg->pdata->phy_clk_reset && motg->phy_reset_clk)
ret = motg->pdata->phy_clk_reset(motg->phy_reset_clk);
- else
+ else if (motg->phy_rst)
ret = reset_control_reset(motg->phy_rst);
if (ret)
@@ -1429,7 +1429,7 @@ static void msm_otg_debugfs_cleanup(void)
debugfs_remove(msm_otg_dbg_root);
}
-static struct of_device_id msm_otg_dt_match[] = {
+static const struct of_device_id msm_otg_dt_match[] = {
{
.compatible = "qcom,usb-otg-ci",
.data = (void *) CI_45NM_INTEGRATED_PHY
@@ -1466,7 +1466,7 @@ static int msm_otg_read_dt(struct platform_device *pdev, struct msm_otg *motg)
motg->phy_rst = devm_reset_control_get(&pdev->dev, "phy");
if (IS_ERR(motg->phy_rst))
- return PTR_ERR(motg->phy_rst);
+ motg->phy_rst = NULL;
pdata->mode = of_usb_get_dr_mode(node);
if (pdata->mode == USB_DR_MODE_UNKNOWN)
@@ -1558,7 +1558,7 @@ static int msm_otg_probe(struct platform_device *pdev)
np ? "phy" : "usb_phy_clk");
if (IS_ERR(motg->phy_reset_clk)) {
dev_err(&pdev->dev, "failed to get usb_phy_clk\n");
- return PTR_ERR(motg->phy_reset_clk);
+ motg->phy_reset_clk = NULL;
}
motg->clk = devm_clk_get(&pdev->dev, np ? "core" : "usb_hs_clk");
diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c
index bbe4f8e6e8d7..13b4fa287da8 100644
--- a/drivers/usb/phy/phy-tegra-usb.c
+++ b/drivers/usb/phy/phy-tegra-usb.c
@@ -33,7 +33,6 @@
#include <linux/usb/otg.h>
#include <linux/usb/ulpi.h>
#include <linux/usb/of.h>
-#include <asm/mach-types.h>
#include <linux/usb/ehci_def.h>
#include <linux/usb/tegra_usb_phy.h>
#include <linux/regulator/consumer.h>
@@ -686,10 +685,8 @@ static int ulpi_phy_power_off(struct tegra_usb_phy *phy)
return gpio_direction_output(phy->reset_gpio, 0);
}
-static void tegra_usb_phy_close(struct usb_phy *x)
+static void tegra_usb_phy_close(struct tegra_usb_phy *phy)
{
- struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy);
-
if (!IS_ERR(phy->vbus))
regulator_disable(phy->vbus);
@@ -965,7 +962,7 @@ static const struct tegra_phy_soc_config tegra30_soc_config = {
.requires_extra_tuning_parameters = true,
};
-static struct of_device_id tegra_usb_phy_id_table[] = {
+static const struct of_device_id tegra_usb_phy_id_table[] = {
{ .compatible = "nvidia,tegra30-usb-phy", .data = &tegra30_soc_config },
{ .compatible = "nvidia,tegra20-usb-phy", .data = &tegra20_soc_config },
{ },
@@ -1061,14 +1058,13 @@ static int tegra_usb_phy_probe(struct platform_device *pdev)
if (err < 0)
return err;
- tegra_phy->u_phy.shutdown = tegra_usb_phy_close;
tegra_phy->u_phy.set_suspend = tegra_usb_phy_suspend;
platform_set_drvdata(pdev, tegra_phy);
err = usb_add_phy_dev(&tegra_phy->u_phy);
if (err < 0) {
- tegra_usb_phy_close(&tegra_phy->u_phy);
+ tegra_usb_phy_close(tegra_phy);
return err;
}
@@ -1080,6 +1076,7 @@ static int tegra_usb_phy_remove(struct platform_device *pdev)
struct tegra_usb_phy *tegra_phy = platform_get_drvdata(pdev);
usb_remove_phy(&tegra_phy->u_phy);
+ tegra_usb_phy_close(tegra_phy);
return 0;
}
diff --git a/drivers/usb/renesas_usbhs/Makefile b/drivers/usb/renesas_usbhs/Makefile
index bc8aef4311a1..9e47f477b6d2 100644
--- a/drivers/usb/renesas_usbhs/Makefile
+++ b/drivers/usb/renesas_usbhs/Makefile
@@ -4,7 +4,7 @@
obj-$(CONFIG_USB_RENESAS_USBHS) += renesas_usbhs.o
-renesas_usbhs-y := common.o mod.o pipe.o fifo.o
+renesas_usbhs-y := common.o mod.o pipe.o fifo.o rcar2.o
ifneq ($(CONFIG_USB_RENESAS_USBHS_HCD),)
renesas_usbhs-y += mod_host.o
diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c
index 17267b0a2e95..1b9bf8d83235 100644
--- a/drivers/usb/renesas_usbhs/common.c
+++ b/drivers/usb/renesas_usbhs/common.c
@@ -15,12 +15,14 @@
*
*/
#include <linux/err.h>
+#include <linux/gpio.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include "common.h"
+#include "rcar2.h"
/*
* image of renesas_usbhs
@@ -284,6 +286,8 @@ static void usbhsc_set_buswait(struct usbhs_priv *priv)
/*
* platform default param
*/
+
+/* commonly used on old SH-Mobile SoCs */
static u32 usbhsc_default_pipe_type[] = {
USB_ENDPOINT_XFER_CONTROL,
USB_ENDPOINT_XFER_ISOC,
@@ -297,6 +301,26 @@ static u32 usbhsc_default_pipe_type[] = {
USB_ENDPOINT_XFER_INT,
};
+/* commonly used on newer SH-Mobile and R-Car SoCs */
+static u32 usbhsc_new_pipe_type[] = {
+ USB_ENDPOINT_XFER_CONTROL,
+ USB_ENDPOINT_XFER_ISOC,
+ USB_ENDPOINT_XFER_ISOC,
+ USB_ENDPOINT_XFER_BULK,
+ USB_ENDPOINT_XFER_BULK,
+ USB_ENDPOINT_XFER_BULK,
+ USB_ENDPOINT_XFER_INT,
+ USB_ENDPOINT_XFER_INT,
+ USB_ENDPOINT_XFER_INT,
+ USB_ENDPOINT_XFER_BULK,
+ USB_ENDPOINT_XFER_BULK,
+ USB_ENDPOINT_XFER_BULK,
+ USB_ENDPOINT_XFER_BULK,
+ USB_ENDPOINT_XFER_BULK,
+ USB_ENDPOINT_XFER_BULK,
+ USB_ENDPOINT_XFER_BULK,
+};
+
/*
* power control
*/
@@ -423,8 +447,7 @@ static int usbhs_probe(struct platform_device *pdev)
int ret;
/* check platform information */
- if (!info ||
- !info->platform_callback.get_id) {
+ if (!info) {
dev_err(&pdev->dev, "no platform information\n");
return -EINVAL;
}
@@ -451,13 +474,32 @@ static int usbhs_probe(struct platform_device *pdev)
/*
* care platform info
*/
- memcpy(&priv->pfunc,
- &info->platform_callback,
- sizeof(struct renesas_usbhs_platform_callback));
+
memcpy(&priv->dparam,
&info->driver_param,
sizeof(struct renesas_usbhs_driver_param));
+ switch (priv->dparam.type) {
+ case USBHS_TYPE_R8A7790:
+ case USBHS_TYPE_R8A7791:
+ priv->pfunc = usbhs_rcar2_ops;
+ if (!priv->dparam.pipe_type) {
+ priv->dparam.pipe_type = usbhsc_new_pipe_type;
+ priv->dparam.pipe_size =
+ ARRAY_SIZE(usbhsc_new_pipe_type);
+ }
+ break;
+ default:
+ if (!info->platform_callback.get_id) {
+ dev_err(&pdev->dev, "no platform callbacks");
+ return -EINVAL;
+ }
+ memcpy(&priv->pfunc,
+ &info->platform_callback,
+ sizeof(struct renesas_usbhs_platform_callback));
+ break;
+ }
+
/* set driver callback functions for platform */
dfunc = &info->driver_callback;
dfunc->notify_hotplug = usbhsc_drvcllbck_notify_hotplug;
@@ -507,6 +549,20 @@ static int usbhs_probe(struct platform_device *pdev)
*/
usbhs_sys_clock_ctrl(priv, 0);
+ /* check GPIO determining if USB function should be enabled */
+ if (priv->dparam.enable_gpio) {
+ gpio_request_one(priv->dparam.enable_gpio, GPIOF_IN, NULL);
+ ret = !gpio_get_value(priv->dparam.enable_gpio);
+ gpio_free(priv->dparam.enable_gpio);
+ if (ret) {
+ dev_warn(&pdev->dev,
+ "USB function not selected (GPIO %d)\n",
+ priv->dparam.enable_gpio);
+ ret = -ENOTSUPP;
+ goto probe_end_mod_exit;
+ }
+ }
+
/*
* platform call
*
diff --git a/drivers/usb/renesas_usbhs/common.h b/drivers/usb/renesas_usbhs/common.h
index c69dd2fba360..a7996da6a1bd 100644
--- a/drivers/usb/renesas_usbhs/common.h
+++ b/drivers/usb/renesas_usbhs/common.h
@@ -268,6 +268,8 @@ struct usbhs_priv {
* fifo control
*/
struct usbhs_fifo_info fifo_info;
+
+ struct usb_phy *phy;
};
/*
diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c
index 458f3766bef1..04e6505777d0 100644
--- a/drivers/usb/renesas_usbhs/mod_gadget.c
+++ b/drivers/usb/renesas_usbhs/mod_gadget.c
@@ -600,8 +600,10 @@ static int usbhsg_ep_enable(struct usb_ep *ep,
static int usbhsg_ep_disable(struct usb_ep *ep)
{
struct usbhsg_uep *uep = usbhsg_ep_to_uep(ep);
+ struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
usbhsg_pipe_disable(uep);
+ usbhs_pipe_free(pipe);
uep->pipe->mod_private = NULL;
uep->pipe = NULL;
diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c
index 7926e1c700f1..75fbcf6b102e 100644
--- a/drivers/usb/renesas_usbhs/pipe.c
+++ b/drivers/usb/renesas_usbhs/pipe.c
@@ -640,6 +640,11 @@ static struct usbhs_pipe *usbhsp_get_pipe(struct usbhs_priv *priv, u32 type)
return pipe;
}
+static void usbhsp_put_pipe(struct usbhs_pipe *pipe)
+{
+ usbhsp_flags_init(pipe);
+}
+
void usbhs_pipe_init(struct usbhs_priv *priv,
int (*dma_map_ctrl)(struct usbhs_pkt *pkt, int map))
{
@@ -710,6 +715,7 @@ struct usbhs_pipe *usbhs_pipe_malloc(struct usbhs_priv *priv,
usbhsp_pipe_select(pipe);
usbhsp_pipe_cfg_set(pipe, 0xFFFF, pipecfg);
usbhsp_pipe_buf_set(pipe, 0xFFFF, pipebuf);
+ usbhs_pipe_clear(pipe);
usbhs_pipe_sequence_data0(pipe);
@@ -726,6 +732,11 @@ struct usbhs_pipe *usbhs_pipe_malloc(struct usbhs_priv *priv,
return pipe;
}
+void usbhs_pipe_free(struct usbhs_pipe *pipe)
+{
+ usbhsp_put_pipe(pipe);
+}
+
void usbhs_pipe_select_fifo(struct usbhs_pipe *pipe, struct usbhs_fifo *fifo)
{
if (pipe->fifo)
diff --git a/drivers/usb/renesas_usbhs/pipe.h b/drivers/usb/renesas_usbhs/pipe.h
index 3e5349879838..406f36d050e4 100644
--- a/drivers/usb/renesas_usbhs/pipe.h
+++ b/drivers/usb/renesas_usbhs/pipe.h
@@ -75,6 +75,7 @@ struct usbhs_pipe_info {
char *usbhs_pipe_name(struct usbhs_pipe *pipe);
struct usbhs_pipe
*usbhs_pipe_malloc(struct usbhs_priv *priv, int endpoint_type, int dir_in);
+void usbhs_pipe_free(struct usbhs_pipe *pipe);
int usbhs_pipe_probe(struct usbhs_priv *priv);
void usbhs_pipe_remove(struct usbhs_priv *priv);
int usbhs_pipe_is_dir_in(struct usbhs_pipe *pipe);
diff --git a/drivers/usb/renesas_usbhs/rcar2.c b/drivers/usb/renesas_usbhs/rcar2.c
new file mode 100644
index 000000000000..e6b9dcc1c289
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/rcar2.c
@@ -0,0 +1,77 @@
+/*
+ * Renesas USB driver R-Car Gen. 2 initialization and power control
+ *
+ * Copyright (C) 2014 Ulrich Hecht
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_data/gpio-rcar.h>
+#include <linux/usb/phy.h>
+#include "common.h"
+#include "rcar2.h"
+
+static int usbhs_rcar2_hardware_init(struct platform_device *pdev)
+{
+ struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev);
+ struct usb_phy *phy;
+
+ phy = usb_get_phy_dev(&pdev->dev, 0);
+ if (IS_ERR(phy))
+ return PTR_ERR(phy);
+
+ priv->phy = phy;
+ return 0;
+}
+
+static int usbhs_rcar2_hardware_exit(struct platform_device *pdev)
+{
+ struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev);
+
+ if (!priv->phy)
+ return 0;
+
+ usb_put_phy(priv->phy);
+ priv->phy = NULL;
+
+ return 0;
+}
+
+static int usbhs_rcar2_power_ctrl(struct platform_device *pdev,
+ void __iomem *base, int enable)
+{
+ struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev);
+
+ if (!priv->phy)
+ return -ENODEV;
+
+ if (enable) {
+ int retval = usb_phy_init(priv->phy);
+
+ if (!retval)
+ retval = usb_phy_set_suspend(priv->phy, 0);
+ return retval;
+ }
+
+ usb_phy_set_suspend(priv->phy, 1);
+ usb_phy_shutdown(priv->phy);
+ return 0;
+}
+
+static int usbhs_rcar2_get_id(struct platform_device *pdev)
+{
+ return USBHS_GADGET;
+}
+
+const struct renesas_usbhs_platform_callback usbhs_rcar2_ops = {
+ .hardware_init = usbhs_rcar2_hardware_init,
+ .hardware_exit = usbhs_rcar2_hardware_exit,
+ .power_ctrl = usbhs_rcar2_power_ctrl,
+ .get_id = usbhs_rcar2_get_id,
+};
diff --git a/drivers/usb/renesas_usbhs/rcar2.h b/drivers/usb/renesas_usbhs/rcar2.h
new file mode 100644
index 000000000000..f07f10d9b3b2
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/rcar2.h
@@ -0,0 +1,4 @@
+#include "common.h"
+
+extern const struct renesas_usbhs_platform_callback
+ usbhs_rcar2_ops;
diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c
index 330df5ce435b..e4bb62225cb9 100644
--- a/drivers/usb/serial/cp210x.c
+++ b/drivers/usb/serial/cp210x.c
@@ -856,9 +856,6 @@ static int cp210x_startup(struct usb_serial *serial)
struct usb_host_interface *cur_altsetting;
struct cp210x_serial_private *spriv;
- /* cp210x buffers behave strangely unless device is reset */
- usb_reset_device(serial->dev);
-
spriv = kzalloc(sizeof(*spriv), GFP_KERNEL);
if (!spriv)
return -ENOMEM;
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index 8a3813be1b28..216ce3078270 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -87,7 +87,6 @@ struct ftdi_sio_quirk {
};
static int ftdi_jtag_probe(struct usb_serial *serial);
-static int ftdi_mtxorb_hack_setup(struct usb_serial *serial);
static int ftdi_NDI_device_setup(struct usb_serial *serial);
static int ftdi_stmclite_probe(struct usb_serial *serial);
static int ftdi_8u2232c_probe(struct usb_serial *serial);
@@ -98,10 +97,6 @@ static struct ftdi_sio_quirk ftdi_jtag_quirk = {
.probe = ftdi_jtag_probe,
};
-static struct ftdi_sio_quirk ftdi_mtxorb_hack_quirk = {
- .probe = ftdi_mtxorb_hack_setup,
-};
-
static struct ftdi_sio_quirk ftdi_NDI_device_quirk = {
.probe = ftdi_NDI_device_setup,
};
@@ -256,14 +251,12 @@ static const struct usb_device_id id_table_combined[] = {
{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0124_PID) },
{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0125_PID) },
{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0126_PID) },
- { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0127_PID),
- .driver_info = (kernel_ulong_t)&ftdi_mtxorb_hack_quirk },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0127_PID) },
{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0128_PID) },
{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0129_PID) },
{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012A_PID) },
{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012B_PID) },
- { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012C_PID),
- .driver_info = (kernel_ulong_t)&ftdi_mtxorb_hack_quirk },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012C_PID) },
{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012D_PID) },
{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012E_PID) },
{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012F_PID) },
@@ -302,18 +295,12 @@ static const struct usb_device_id id_table_combined[] = {
{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0150_PID) },
{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0151_PID) },
{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0152_PID) },
- { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0153_PID),
- .driver_info = (kernel_ulong_t)&ftdi_mtxorb_hack_quirk },
- { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0154_PID),
- .driver_info = (kernel_ulong_t)&ftdi_mtxorb_hack_quirk },
- { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0155_PID),
- .driver_info = (kernel_ulong_t)&ftdi_mtxorb_hack_quirk },
- { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0156_PID),
- .driver_info = (kernel_ulong_t)&ftdi_mtxorb_hack_quirk },
- { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0157_PID),
- .driver_info = (kernel_ulong_t)&ftdi_mtxorb_hack_quirk },
- { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0158_PID),
- .driver_info = (kernel_ulong_t)&ftdi_mtxorb_hack_quirk },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0153_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0154_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0155_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0156_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0157_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0158_PID) },
{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0159_PID) },
{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_015A_PID) },
{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_015B_PID) },
@@ -673,6 +660,8 @@ static const struct usb_device_id id_table_combined[] = {
{ USB_DEVICE(FTDI_VID, XSENS_CONVERTER_5_PID) },
{ USB_DEVICE(FTDI_VID, XSENS_CONVERTER_6_PID) },
{ USB_DEVICE(FTDI_VID, XSENS_CONVERTER_7_PID) },
+ { USB_DEVICE(XSENS_VID, XSENS_CONVERTER_PID) },
+ { USB_DEVICE(XSENS_VID, XSENS_MTW_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_OMNI1509) },
{ USB_DEVICE(MOBILITY_VID, MOBILITY_USB_SERIAL_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_ACTIVE_ROBOTS_PID) },
@@ -1559,45 +1548,40 @@ static void ftdi_determine_type(struct usb_serial_port *port)
}
-/* Determine the maximum packet size for the device. This depends on the chip
- * type and the USB host capabilities. The value should be obtained from the
- * device descriptor as the chip will use the appropriate values for the host.*/
+/*
+ * Determine the maximum packet size for the device. This depends on the chip
+ * type and the USB host capabilities. The value should be obtained from the
+ * device descriptor as the chip will use the appropriate values for the host.
+ */
static void ftdi_set_max_packet_size(struct usb_serial_port *port)
{
struct ftdi_private *priv = usb_get_serial_port_data(port);
- struct usb_serial *serial = port->serial;
- struct usb_device *udev = serial->dev;
-
- struct usb_interface *interface = serial->interface;
+ struct usb_interface *interface = port->serial->interface;
struct usb_endpoint_descriptor *ep_desc;
-
unsigned num_endpoints;
unsigned i;
num_endpoints = interface->cur_altsetting->desc.bNumEndpoints;
- dev_info(&udev->dev, "Number of endpoints %d\n", num_endpoints);
-
if (!num_endpoints)
return;
- /* NOTE: some customers have programmed FT232R/FT245R devices
- * with an endpoint size of 0 - not good. In this case, we
+ /*
+ * NOTE: Some customers have programmed FT232R/FT245R devices
+ * with an endpoint size of 0 - not good. In this case, we
* want to override the endpoint descriptor setting and use a
- * value of 64 for wMaxPacketSize */
+ * value of 64 for wMaxPacketSize.
+ */
for (i = 0; i < num_endpoints; i++) {
- dev_info(&udev->dev, "Endpoint %d MaxPacketSize %d\n", i+1,
- interface->cur_altsetting->endpoint[i].desc.wMaxPacketSize);
ep_desc = &interface->cur_altsetting->endpoint[i].desc;
- if (ep_desc->wMaxPacketSize == 0) {
+ if (!ep_desc->wMaxPacketSize) {
ep_desc->wMaxPacketSize = cpu_to_le16(0x40);
- dev_info(&udev->dev, "Overriding wMaxPacketSize on endpoint %d\n", i);
+ dev_warn(&port->dev, "Overriding wMaxPacketSize on endpoint %d\n",
+ usb_endpoint_num(ep_desc));
}
}
- /* set max packet size based on descriptor */
+ /* Set max packet size based on last descriptor. */
priv->max_packet_size = usb_endpoint_maxp(ep_desc);
-
- dev_info(&udev->dev, "Setting MaxPacketSize %d\n", priv->max_packet_size);
}
@@ -1866,24 +1850,6 @@ static int ftdi_stmclite_probe(struct usb_serial *serial)
return 0;
}
-/*
- * The Matrix Orbital VK204-25-USB has an invalid IN endpoint.
- * We have to correct it if we want to read from it.
- */
-static int ftdi_mtxorb_hack_setup(struct usb_serial *serial)
-{
- struct usb_host_endpoint *ep = serial->dev->ep_in[1];
- struct usb_endpoint_descriptor *ep_desc = &ep->desc;
-
- if (ep->enabled && ep_desc->wMaxPacketSize == 0) {
- ep_desc->wMaxPacketSize = cpu_to_le16(0x40);
- dev_info(&serial->dev->dev,
- "Fixing invalid wMaxPacketSize on read pipe\n");
- }
-
- return 0;
-}
-
static int ftdi_sio_port_remove(struct usb_serial_port *port)
{
struct ftdi_private *priv = usb_get_serial_port_data(port);
diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h
index c4777bc6aee0..1e58d90a0b6c 100644
--- a/drivers/usb/serial/ftdi_sio_ids.h
+++ b/drivers/usb/serial/ftdi_sio_ids.h
@@ -140,12 +140,15 @@
/*
* Xsens Technologies BV products (http://www.xsens.com).
*/
-#define XSENS_CONVERTER_0_PID 0xD388
-#define XSENS_CONVERTER_1_PID 0xD389
+#define XSENS_VID 0x2639
+#define XSENS_CONVERTER_PID 0xD00D /* Xsens USB-serial converter */
+#define XSENS_MTW_PID 0x0200 /* Xsens MTw */
+#define XSENS_CONVERTER_0_PID 0xD388 /* Xsens USB converter */
+#define XSENS_CONVERTER_1_PID 0xD389 /* Xsens Wireless Receiver */
#define XSENS_CONVERTER_2_PID 0xD38A
-#define XSENS_CONVERTER_3_PID 0xD38B
-#define XSENS_CONVERTER_4_PID 0xD38C
-#define XSENS_CONVERTER_5_PID 0xD38D
+#define XSENS_CONVERTER_3_PID 0xD38B /* Xsens USB-serial converter */
+#define XSENS_CONVERTER_4_PID 0xD38C /* Xsens Wireless Receiver */
+#define XSENS_CONVERTER_5_PID 0xD38D /* Xsens Awinda Station */
#define XSENS_CONVERTER_6_PID 0xD38E
#define XSENS_CONVERTER_7_PID 0xD38F
diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c
index d7440b7557af..e020ad28a00c 100644
--- a/drivers/usb/serial/kl5kusb105.c
+++ b/drivers/usb/serial/kl5kusb105.c
@@ -62,8 +62,6 @@ static void klsi_105_close(struct usb_serial_port *port);
static void klsi_105_set_termios(struct tty_struct *tty,
struct usb_serial_port *port, struct ktermios *old);
static int klsi_105_tiocmget(struct tty_struct *tty);
-static int klsi_105_tiocmset(struct tty_struct *tty,
- unsigned int set, unsigned int clear);
static void klsi_105_process_read_urb(struct urb *urb);
static int klsi_105_prepare_write_buffer(struct usb_serial_port *port,
void *dest, size_t size);
@@ -93,7 +91,6 @@ static struct usb_serial_driver kl5kusb105d_device = {
.set_termios = klsi_105_set_termios,
/*.break_ctl = klsi_105_break_ctl,*/
.tiocmget = klsi_105_tiocmget,
- .tiocmset = klsi_105_tiocmset,
.port_probe = klsi_105_port_probe,
.port_remove = klsi_105_port_remove,
.throttle = usb_serial_generic_throttle,
@@ -602,33 +599,6 @@ static int klsi_105_tiocmget(struct tty_struct *tty)
return (int)line_state;
}
-static int klsi_105_tiocmset(struct tty_struct *tty,
- unsigned int set, unsigned int clear)
-{
- int retval = -EINVAL;
-
-/* if this ever gets implemented, it should be done something like this:
- struct usb_serial *serial = port->serial;
- struct klsi_105_private *priv = usb_get_serial_port_data(port);
- unsigned long flags;
- int control;
-
- spin_lock_irqsave (&priv->lock, flags);
- if (set & TIOCM_RTS)
- priv->control_state |= TIOCM_RTS;
- if (set & TIOCM_DTR)
- priv->control_state |= TIOCM_DTR;
- if (clear & TIOCM_RTS)
- priv->control_state &= ~TIOCM_RTS;
- if (clear & TIOCM_DTR)
- priv->control_state &= ~TIOCM_DTR;
- control = priv->control_state;
- spin_unlock_irqrestore (&priv->lock, flags);
- retval = mct_u232_set_modem_ctrl(serial, control);
-*/
- return retval;
-}
-
module_usb_serial_driver(serial_drivers, id_table);
MODULE_AUTHOR(DRIVER_AUTHOR);
diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c
index 393be562d875..3d88eefdf1d1 100644
--- a/drivers/usb/serial/mos7840.c
+++ b/drivers/usb/serial/mos7840.c
@@ -1181,10 +1181,7 @@ static void mos7840_close(struct usb_serial_port *port)
/* Freeing Write URBs */
for (j = 0; j < NUM_URBS; ++j) {
if (mos7840_port->write_urb_pool[j]) {
- if (mos7840_port->write_urb_pool[j]->transfer_buffer)
- kfree(mos7840_port->write_urb_pool[j]->
- transfer_buffer);
-
+ kfree(mos7840_port->write_urb_pool[j]->transfer_buffer);
usb_free_urb(mos7840_port->write_urb_pool[j]);
}
}
diff --git a/drivers/usb/storage/uas-detect.h b/drivers/usb/storage/uas-detect.h
index bb05b984d5f6..503ac5c8d80f 100644
--- a/drivers/usb/storage/uas-detect.h
+++ b/drivers/usb/storage/uas-detect.h
@@ -9,32 +9,15 @@ static int uas_is_interface(struct usb_host_interface *intf)
intf->desc.bInterfaceProtocol == USB_PR_UAS);
}
-static int uas_isnt_supported(struct usb_device *udev)
-{
- struct usb_hcd *hcd = bus_to_hcd(udev->bus);
-
- dev_warn(&udev->dev, "The driver for the USB controller %s does not "
- "support scatter-gather which is\n",
- hcd->driver->description);
- dev_warn(&udev->dev, "required by the UAS driver. Please try an"
- "alternative USB controller if you wish to use UAS.\n");
- return -ENODEV;
-}
-
static int uas_find_uas_alt_setting(struct usb_interface *intf)
{
int i;
- struct usb_device *udev = interface_to_usbdev(intf);
- int sg_supported = udev->bus->sg_tablesize != 0;
for (i = 0; i < intf->num_altsetting; i++) {
struct usb_host_interface *alt = &intf->altsetting[i];
- if (uas_is_interface(alt)) {
- if (!sg_supported)
- return uas_isnt_supported(udev);
+ if (uas_is_interface(alt))
return alt->desc.bAlternateSetting;
- }
}
return -ENODEV;
@@ -81,9 +64,6 @@ static int uas_use_uas_driver(struct usb_interface *intf,
if (flags & US_FL_IGNORE_UAS)
return 0;
- if (udev->speed >= USB_SPEED_SUPER && !hcd->can_do_streams)
- return 0;
-
alt = uas_find_uas_alt_setting(intf);
if (alt < 0)
return 0;
@@ -92,5 +72,23 @@ static int uas_use_uas_driver(struct usb_interface *intf,
if (r < 0)
return 0;
+ if (udev->bus->sg_tablesize == 0) {
+ dev_warn(&udev->dev,
+ "The driver for the USB controller %s does not support scatter-gather which is\n",
+ hcd->driver->description);
+ dev_warn(&udev->dev,
+ "required by the UAS driver. Please try an other USB controller if you wish to use UAS.\n");
+ return 0;
+ }
+
+ if (udev->speed >= USB_SPEED_SUPER && !hcd->can_do_streams) {
+ dev_warn(&udev->dev,
+ "USB controller %s does not support streams, which are required by the UAS driver.\n",
+ hcd_to_bus(hcd)->bus_name);
+ dev_warn(&udev->dev,
+ "Please try an other USB controller if you wish to use UAS.\n");
+ return 0;
+ }
+
return 1;
}
diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c
index 511b22953167..3f42785f653c 100644
--- a/drivers/usb/storage/uas.c
+++ b/drivers/usb/storage/uas.c
@@ -1026,7 +1026,7 @@ static int uas_configure_endpoints(struct uas_dev_info *devinfo)
usb_endpoint_num(&eps[3]->desc));
if (udev->speed != USB_SPEED_SUPER) {
- devinfo->qdepth = 256;
+ devinfo->qdepth = 32;
devinfo->use_streams = 0;
} else {
devinfo->qdepth = usb_alloc_streams(devinfo->intf, eps + 1,
diff --git a/drivers/uwb/whci.c b/drivers/uwb/whci.c
index c9df8ba97dae..46b7cfcdfbca 100644
--- a/drivers/uwb/whci.c
+++ b/drivers/uwb/whci.c
@@ -175,7 +175,7 @@ static int whci_probe(struct pci_dev *pci, const struct pci_device_id *id)
err = -ENOMEM;
card = kzalloc(sizeof(struct whci_card)
- + sizeof(struct whci_dev *) * (n_caps + 1),
+ + sizeof(struct umc_dev *) * (n_caps + 1),
GFP_KERNEL);
if (card == NULL)
goto error_kzalloc;
diff --git a/include/dt-bindings/phy/phy-miphy365x.h b/include/dt-bindings/phy/phy-miphy365x.h
new file mode 100644
index 000000000000..8ef8aba6edd6
--- /dev/null
+++ b/include/dt-bindings/phy/phy-miphy365x.h
@@ -0,0 +1,14 @@
+/*
+ * This header provides constants for the phy framework
+ * based on the STMicroelectronics MiPHY365x.
+ *
+ * Author: Lee Jones <lee.jones@linaro.org>
+ */
+#ifndef _DT_BINDINGS_PHY_MIPHY
+#define _DT_BINDINGS_PHY_MIPHY
+
+#define MIPHY_TYPE_SATA 1
+#define MIPHY_TYPE_PCIE 2
+#define MIPHY_TYPE_USB 3
+
+#endif /* _DT_BINDINGS_PHY_MIPHY */
diff --git a/include/linux/phy/omap_control_phy.h b/include/linux/phy/omap_control_phy.h
index 5450403c7546..e9e6cfbfbb58 100644
--- a/include/linux/phy/omap_control_phy.h
+++ b/include/linux/phy/omap_control_phy.h
@@ -23,6 +23,7 @@ enum omap_control_phy_type {
OMAP_CTRL_TYPE_OTGHS = 1, /* Mailbox OTGHS_CONTROL */
OMAP_CTRL_TYPE_USB2, /* USB2_PHY, power down in CONTROL_DEV_CONF */
OMAP_CTRL_TYPE_PIPE3, /* PIPE3 PHY, DPLL & seperate Rx/Tx power */
+ OMAP_CTRL_TYPE_PCIE, /* RX TX control of ACSPCIE */
OMAP_CTRL_TYPE_DRA7USB2, /* USB2 PHY, power and power_aux e.g. DRA7 */
OMAP_CTRL_TYPE_AM437USB2, /* USB2 PHY, power e.g. AM437x */
};
@@ -33,6 +34,7 @@ struct omap_control_phy {
u32 __iomem *otghs_control;
u32 __iomem *power;
u32 __iomem *power_aux;
+ u32 __iomem *pcie_pcs;
struct clk *sys_clk;
@@ -63,6 +65,9 @@ enum omap_control_usb_mode {
#define OMAP_CTRL_PIPE3_PHY_TX_RX_POWERON 0x3
#define OMAP_CTRL_PIPE3_PHY_TX_RX_POWEROFF 0x0
+#define OMAP_CTRL_PCIE_PCS_MASK 0xff
+#define OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT 0x8
+
#define OMAP_CTRL_USB2_PHY_PD BIT(28)
#define AM437X_CTRL_USB2_PHY_PD BIT(0)
@@ -74,6 +79,7 @@ enum omap_control_usb_mode {
void omap_control_phy_power(struct device *dev, int on);
void omap_control_usb_set_mode(struct device *dev,
enum omap_control_usb_mode mode);
+void omap_control_pcie_pcs(struct device *dev, u8 id, u8 delay);
#else
static inline void omap_control_phy_power(struct device *dev, int on)
@@ -84,6 +90,10 @@ static inline void omap_control_usb_set_mode(struct device *dev,
enum omap_control_usb_mode mode)
{
}
+
+static inline void omap_control_pcie_pcs(struct device *dev, u8 id, u8 delay)
+{
+}
#endif
#endif /* __OMAP_CONTROL_PHY_H__ */
diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h
index 2760744cb2a7..8cb6f815475b 100644
--- a/include/linux/phy/phy.h
+++ b/include/linux/phy/phy.h
@@ -18,6 +18,7 @@
#include <linux/of.h>
#include <linux/device.h>
#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
struct phy;
@@ -65,6 +66,7 @@ struct phy {
int init_count;
int power_count;
struct phy_attrs attrs;
+ struct regulator *pwr;
};
/**
@@ -156,9 +158,10 @@ void devm_phy_put(struct device *dev, struct phy *phy);
struct phy *of_phy_get(struct device_node *np, const char *con_id);
struct phy *of_phy_simple_xlate(struct device *dev,
struct of_phandle_args *args);
-struct phy *phy_create(struct device *dev, const struct phy_ops *ops,
- struct phy_init_data *init_data);
-struct phy *devm_phy_create(struct device *dev,
+struct phy *phy_create(struct device *dev, struct device_node *node,
+ const struct phy_ops *ops,
+ struct phy_init_data *init_data);
+struct phy *devm_phy_create(struct device *dev, struct device_node *node,
const struct phy_ops *ops, struct phy_init_data *init_data);
void phy_destroy(struct phy *phy);
void devm_phy_destroy(struct device *dev, struct phy *phy);
@@ -297,13 +300,17 @@ static inline struct phy *of_phy_simple_xlate(struct device *dev,
}
static inline struct phy *phy_create(struct device *dev,
- const struct phy_ops *ops, struct phy_init_data *init_data)
+ struct device_node *node,
+ const struct phy_ops *ops,
+ struct phy_init_data *init_data)
{
return ERR_PTR(-ENOSYS);
}
static inline struct phy *devm_phy_create(struct device *dev,
- const struct phy_ops *ops, struct phy_init_data *init_data)
+ struct device_node *node,
+ const struct phy_ops *ops,
+ struct phy_init_data *init_data)
{
return ERR_PTR(-ENOSYS);
}
diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h
index 7373203140e7..c330f5ef42cf 100644
--- a/include/linux/usb/composite.h
+++ b/include/linux/usb/composite.h
@@ -386,6 +386,21 @@ struct usb_composite_driver {
extern int usb_composite_probe(struct usb_composite_driver *driver);
extern void usb_composite_unregister(struct usb_composite_driver *driver);
+
+/**
+ * module_usb_composite_driver() - Helper macro for registering a USB gadget
+ * composite driver
+ * @__usb_composite_driver: usb_composite_driver struct
+ *
+ * Helper macro for USB gadget composite drivers which do not do anything
+ * special in module init/exit. This eliminates a lot of boilerplate. Each
+ * module may only use this macro once, and calling it replaces module_init()
+ * and module_exit()
+ */
+#define module_usb_composite_driver(__usb_composite_driver) \
+ module_driver(__usb_composite_driver, usb_composite_probe, \
+ usb_composite_unregister)
+
extern void usb_composite_setup_continue(struct usb_composite_dev *cdev);
extern int composite_dev_prepare(struct usb_composite_driver *composite,
struct usb_composite_dev *cdev);
diff --git a/include/linux/usb/quirks.h b/include/linux/usb/quirks.h
index 52f944dfe2fd..55a17b188daa 100644
--- a/include/linux/usb/quirks.h
+++ b/include/linux/usb/quirks.h
@@ -30,4 +30,15 @@
descriptor */
#define USB_QUIRK_DELAY_INIT 0x00000040
+/*
+ * For high speed and super speed interupt endpoints, the USB 2.0 and
+ * USB 3.0 spec require the interval in microframes
+ * (1 microframe = 125 microseconds) to be calculated as
+ * interval = 2 ^ (bInterval-1).
+ *
+ * Devices with this quirk report their bInterval as the result of this
+ * calculation instead of the exponent variable used in the calculation.
+ */
+#define USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL 0x00000080
+
#endif /* __LINUX_USB_QUIRKS_H */
diff --git a/include/linux/usb/renesas_usbhs.h b/include/linux/usb/renesas_usbhs.h
index e452ba6ec6bd..d5952bb66752 100644
--- a/include/linux/usb/renesas_usbhs.h
+++ b/include/linux/usb/renesas_usbhs.h
@@ -153,6 +153,9 @@ struct renesas_usbhs_driver_param {
*/
int pio_dma_border; /* default is 64byte */
+ u32 type;
+ u32 enable_gpio;
+
/*
* option:
*/
@@ -160,6 +163,9 @@ struct renesas_usbhs_driver_param {
u32 has_sudmac:1; /* for SUDMAC */
};
+#define USBHS_TYPE_R8A7790 1
+#define USBHS_TYPE_R8A7791 2
+
/*
* option:
*
diff --git a/include/linux/usb/usb338x.h b/include/linux/usb/usb338x.h
new file mode 100644
index 000000000000..f92eb635b9d3
--- /dev/null
+++ b/include/linux/usb/usb338x.h
@@ -0,0 +1,199 @@
+/*
+ * USB 338x super/high/full speed USB device controller.
+ * Unlike many such controllers, this one talks PCI.
+ *
+ * Copyright (C) 2002 NetChip Technology, Inc. (http://www.netchip.com)
+ * Copyright (C) 2003 David Brownell
+ * Copyright (C) 2014 Ricardo Ribalda - Qtechnology/AS
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __LINUX_USB_USB338X_H
+#define __LINUX_USB_USB338X_H
+
+#include <linux/usb/net2280.h>
+
+/*
+ * Extra defined bits for net2280 registers
+ */
+#define SCRATCH 0x0b
+
+#define DEFECT7374_FSM_FIELD 28
+#define SUPER_SPEED 8
+#define DMA_REQUEST_OUTSTANDING 5
+#define DMA_PAUSE_DONE_INTERRUPT 26
+#define SET_ISOCHRONOUS_DELAY 24
+#define SET_SEL 22
+#define SUPER_SPEED_MODE 8
+
+/*ep_cfg*/
+#define MAX_BURST_SIZE 24
+#define EP_FIFO_BYTE_COUNT 16
+#define IN_ENDPOINT_ENABLE 14
+#define IN_ENDPOINT_TYPE 12
+#define OUT_ENDPOINT_ENABLE 10
+#define OUT_ENDPOINT_TYPE 8
+
+struct usb338x_usb_ext_regs {
+ u32 usbclass;
+#define DEVICE_PROTOCOL 16
+#define DEVICE_SUB_CLASS 8
+#define DEVICE_CLASS 0
+ u32 ss_sel;
+#define U2_SYSTEM_EXIT_LATENCY 8
+#define U1_SYSTEM_EXIT_LATENCY 0
+ u32 ss_del;
+#define U2_DEVICE_EXIT_LATENCY 8
+#define U1_DEVICE_EXIT_LATENCY 0
+ u32 usb2lpm;
+#define USB_L1_LPM_HIRD 2
+#define USB_L1_LPM_REMOTE_WAKE 1
+#define USB_L1_LPM_SUPPORT 0
+ u32 usb3belt;
+#define BELT_MULTIPLIER 10
+#define BEST_EFFORT_LATENCY_TOLERANCE 0
+ u32 usbctl2;
+#define LTM_ENABLE 7
+#define U2_ENABLE 6
+#define U1_ENABLE 5
+#define FUNCTION_SUSPEND 4
+#define USB3_CORE_ENABLE 3
+#define USB2_CORE_ENABLE 2
+#define SERIAL_NUMBER_STRING_ENABLE 0
+ u32 in_timeout;
+#define GPEP3_TIMEOUT 19
+#define GPEP2_TIMEOUT 18
+#define GPEP1_TIMEOUT 17
+#define GPEP0_TIMEOUT 16
+#define GPEP3_TIMEOUT_VALUE 13
+#define GPEP3_TIMEOUT_ENABLE 12
+#define GPEP2_TIMEOUT_VALUE 9
+#define GPEP2_TIMEOUT_ENABLE 8
+#define GPEP1_TIMEOUT_VALUE 5
+#define GPEP1_TIMEOUT_ENABLE 4
+#define GPEP0_TIMEOUT_VALUE 1
+#define GPEP0_TIMEOUT_ENABLE 0
+ u32 isodelay;
+#define ISOCHRONOUS_DELAY 0
+} __packed;
+
+struct usb338x_fifo_regs {
+ /* offset 0x0500, 0x0520, 0x0540, 0x0560, 0x0580 */
+ u32 ep_fifo_size_base;
+#define IN_FIFO_BASE_ADDRESS 22
+#define IN_FIFO_SIZE 16
+#define OUT_FIFO_BASE_ADDRESS 6
+#define OUT_FIFO_SIZE 0
+ u32 ep_fifo_out_wrptr;
+ u32 ep_fifo_out_rdptr;
+ u32 ep_fifo_in_wrptr;
+ u32 ep_fifo_in_rdptr;
+ u32 unused[3];
+} __packed;
+
+
+/* Link layer */
+struct usb338x_ll_regs {
+ /* offset 0x700 */
+ u32 ll_ltssm_ctrl1;
+ u32 ll_ltssm_ctrl2;
+ u32 ll_ltssm_ctrl3;
+ u32 unused[2];
+ u32 ll_general_ctrl0;
+ u32 ll_general_ctrl1;
+#define PM_U3_AUTO_EXIT 29
+#define PM_U2_AUTO_EXIT 28
+#define PM_U1_AUTO_EXIT 27
+#define PM_FORCE_U2_ENTRY 26
+#define PM_FORCE_U1_ENTRY 25
+#define PM_LGO_COLLISION_SEND_LAU 24
+#define PM_DIR_LINK_REJECT 23
+#define PM_FORCE_LINK_ACCEPT 22
+#define PM_DIR_ENTRY_U3 20
+#define PM_DIR_ENTRY_U2 19
+#define PM_DIR_ENTRY_U1 18
+#define PM_U2_ENABLE 17
+#define PM_U1_ENABLE 16
+#define SKP_THRESHOLD_ADJUST_FMW 8
+#define RESEND_DPP_ON_LRTY_FMW 7
+#define DL_BIT_VALUE_FMW 6
+#define FORCE_DL_BIT 5
+ u32 ll_general_ctrl2;
+#define SELECT_INVERT_LANE_POLARITY 7
+#define FORCE_INVERT_LANE_POLARITY 6
+ u32 ll_general_ctrl3;
+ u32 ll_general_ctrl4;
+ u32 ll_error_gen;
+} __packed;
+
+struct usb338x_ll_lfps_regs {
+ /* offset 0x748 */
+ 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 */
+ u32 ll_tsn_counters_2;
+#define HOT_TX_NORESET_TS2 24
+ u32 ll_tsn_counters_3;
+#define HOT_RX_RESET_TS2 0
+} __packed;
+
+struct usb338x_ll_chi_regs {
+ /* offset 0x79C */
+ u32 ll_tsn_chicken_bit;
+#define RECOVERY_IDLE_TO_RECOVER_FMW 3
+} __packed;
+
+/* protocol layer */
+struct usb338x_pl_regs {
+ /* offset 0x800 */
+ u32 pl_reg_1;
+ u32 pl_reg_2;
+ u32 pl_reg_3;
+ u32 pl_reg_4;
+ u32 pl_ep_ctrl;
+ /* Protocol Layer Endpoint Control*/
+#define PL_EP_CTRL 0x810
+#define ENDPOINT_SELECT 0
+ /* [4:0] */
+#define EP_INITIALIZED 16
+#define SEQUENCE_NUMBER_RESET 17
+#define CLEAR_ACK_ERROR_CODE 20
+ u32 pl_reg_6;
+ u32 pl_reg_7;
+ u32 pl_reg_8;
+ u32 pl_ep_status_1;
+ /* Protocol Layer Endpoint Status 1*/
+#define PL_EP_STATUS_1 0x820
+#define STATE 16
+#define ACK_GOOD_NORMAL 0x11
+#define ACK_GOOD_MORE_ACKS_TO_COME 0x16
+ u32 pl_ep_status_2;
+ u32 pl_ep_status_3;
+ /* Protocol Layer Endpoint Status 3*/
+#define PL_EP_STATUS_3 0x828
+#define SEQUENCE_NUMBER 0
+ u32 pl_ep_status_4;
+ /* Protocol Layer Endpoint Status 4*/
+#define PL_EP_STATUS_4 0x82c
+ u32 pl_ep_cfg_4;
+ /* Protocol Layer Endpoint Configuration 4*/
+#define PL_EP_CFG_4 0x830
+#define NON_CTRL_IN_TOLERATE_BAD_DIR 6
+} __packed;
+
+#endif /* __LINUX_USB_USB338X_H */
diff --git a/include/linux/usb/xhci_pdriver.h b/include/linux/usb/xhci_pdriver.h
new file mode 100644
index 000000000000..376654b5b0f7
--- /dev/null
+++ b/include/linux/usb/xhci_pdriver.h
@@ -0,0 +1,27 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ */
+
+#ifndef __USB_CORE_XHCI_PDRIVER_H
+#define __USB_CORE_XHCI_PDRIVER_H
+
+/**
+ * struct usb_xhci_pdata - platform_data for generic xhci platform driver
+ *
+ * @usb3_lpm_capable: determines if this xhci platform supports USB3
+ * LPM capability
+ *
+ */
+struct usb_xhci_pdata {
+ unsigned usb3_lpm_capable:1;
+};
+
+#endif /* __USB_CORE_XHCI_PDRIVER_H */
diff --git a/include/uapi/linux/usb/functionfs.h b/include/uapi/linux/usb/functionfs.h
index 24b68c59dcf8..0154b2859fd7 100644
--- a/include/uapi/linux/usb/functionfs.h
+++ b/include/uapi/linux/usb/functionfs.h
@@ -18,10 +18,9 @@ enum functionfs_flags {
FUNCTIONFS_HAS_FS_DESC = 1,
FUNCTIONFS_HAS_HS_DESC = 2,
FUNCTIONFS_HAS_SS_DESC = 4,
+ FUNCTIONFS_HAS_MS_OS_DESC = 8,
};
-#ifndef __KERNEL__
-
/* Descriptor of an non-audio endpoint */
struct usb_endpoint_descriptor_no_audio {
__u8 bLength;
@@ -41,6 +40,37 @@ struct usb_functionfs_descs_head {
__le32 hs_count;
} __attribute__((packed, deprecated));
+/* MS OS Descriptor header */
+struct usb_os_desc_header {
+ __u8 interface;
+ __le32 dwLength;
+ __le16 bcdVersion;
+ __le16 wIndex;
+ union {
+ struct {
+ __u8 bCount;
+ __u8 Reserved;
+ };
+ __le16 wCount;
+ };
+} __attribute__((packed));
+
+struct usb_ext_compat_desc {
+ __u8 bFirstInterfaceNumber;
+ __u8 Reserved1;
+ __u8 CompatibleID[8];
+ __u8 SubCompatibleID[8];
+ __u8 Reserved2[6];
+};
+
+struct usb_ext_prop_desc {
+ __le32 dwSize;
+ __le32 dwPropertyDataType;
+ __le16 wPropertyNameLength;
+} __attribute__((packed));
+
+#ifndef __KERNEL__
+
/*
* Descriptors format:
*
@@ -52,9 +82,11 @@ struct usb_functionfs_descs_head {
* | | fs_count | LE32 | number of full-speed descriptors |
* | | hs_count | LE32 | number of high-speed descriptors |
* | | ss_count | LE32 | number of super-speed descriptors |
+ * | | os_count | LE32 | number of MS OS descriptors |
* | | fs_descrs | Descriptor[] | list of full-speed descriptors |
* | | hs_descrs | Descriptor[] | list of high-speed descriptors |
* | | ss_descrs | Descriptor[] | list of super-speed descriptors |
+ * | | os_descrs | OSDesc[] | list of MS OS descriptors |
*
* Depending on which flags are set, various fields may be missing in the
* structure. Any flags that are not recognised cause the whole block to be
@@ -81,6 +113,52 @@ struct usb_functionfs_descs_head {
* | 0 | bLength | U8 | length of the descriptor |
* | 1 | bDescriptorType | U8 | descriptor type |
* | 2 | payload | | descriptor's payload |
+ *
+ * OSDesc[] is an array of valid MS OS Feature Descriptors which have one of
+ * the following formats:
+ *
+ * | off | name | type | description |
+ * |-----+-----------------+------+--------------------------|
+ * | 0 | inteface | U8 | related interface number |
+ * | 1 | dwLength | U32 | length of the descriptor |
+ * | 5 | bcdVersion | U16 | currently supported: 1 |
+ * | 7 | wIndex | U16 | currently supported: 4 |
+ * | 9 | bCount | U8 | number of ext. compat. |
+ * | 10 | Reserved | U8 | 0 |
+ * | 11 | ExtCompat[] | | list of ext. compat. d. |
+ *
+ * | off | name | type | description |
+ * |-----+-----------------+------+--------------------------|
+ * | 0 | inteface | U8 | related interface number |
+ * | 1 | dwLength | U32 | length of the descriptor |
+ * | 5 | bcdVersion | U16 | currently supported: 1 |
+ * | 7 | wIndex | U16 | currently supported: 5 |
+ * | 9 | wCount | U16 | number of ext. compat. |
+ * | 11 | ExtProp[] | | list of ext. prop. d. |
+ *
+ * ExtCompat[] is an array of valid Extended Compatiblity descriptors
+ * which have the following format:
+ *
+ * | off | name | type | description |
+ * |-----+-----------------------+------+-------------------------------------|
+ * | 0 | bFirstInterfaceNumber | U8 | index of the interface or of the 1st|
+ * | | | | interface in an IAD group |
+ * | 1 | Reserved | U8 | 0 |
+ * | 2 | CompatibleID | U8[8]| compatible ID string |
+ * | 10 | SubCompatibleID | U8[8]| subcompatible ID string |
+ * | 18 | Reserved | U8[6]| 0 |
+ *
+ * ExtProp[] is an array of valid Extended Properties descriptors
+ * which have the following format:
+ *
+ * | off | name | type | description |
+ * |-----+-----------------------+------+-------------------------------------|
+ * | 0 | dwSize | U32 | length of the descriptor |
+ * | 4 | dwPropertyDataType | U32 | 1..7 |
+ * | 8 | wPropertyNameLength | U16 | bPropertyName length (NL) |
+ * | 10 | bPropertyName |U8[NL]| name of this property |
+ * |10+NL| dwPropertyDataLength | U32 | bPropertyData length (DL) |
+ * |14+NL| bProperty |U8[DL]| payload of this property |
*/
struct usb_functionfs_strings_head {
diff --git a/tools/usb/ffs-aio-example/multibuff/device_app/aio_multibuff.c b/tools/usb/ffs-aio-example/multibuff/device_app/aio_multibuff.c
index 87216a0c4a8b..af4b0508be77 100644
--- a/tools/usb/ffs-aio-example/multibuff/device_app/aio_multibuff.c
+++ b/tools/usb/ffs-aio-example/multibuff/device_app/aio_multibuff.c
@@ -1,3 +1,30 @@
+/*
+ * This is free and unencumbered software released into the public domain.
+ *
+ * Anyone is free to copy, modify, publish, use, compile, sell, or
+ * distribute this software, either in source code form or as a compiled
+ * binary, for any purpose, commercial or non-commercial, and by any
+ * means.
+ *
+ * In jurisdictions that recognize copyright laws, the author or authors
+ * of this software dedicate any and all copyright interest in the
+ * software to the public domain. We make this dedication for the benefit
+ * of the public at large and to the detriment of our heirs and
+ * successors. We intend this dedication to be an overt act of
+ * relinquishment in perpetuity of all present and future rights to this
+ * software under copyright law.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * For more information, please refer to <http://unlicense.org/>
+ */
+
#define _BSD_SOURCE /* for endian.h */
#include <endian.h>
@@ -27,7 +54,9 @@
/******************** Descriptors and Strings *******************************/
static const struct {
- struct usb_functionfs_descs_head header;
+ struct usb_functionfs_descs_head_v2 header;
+ __le32 fs_count;
+ __le32 hs_count;
struct {
struct usb_interface_descriptor intf;
struct usb_endpoint_descriptor_no_audio bulk_sink;
@@ -35,11 +64,12 @@ static const struct {
} __attribute__ ((__packed__)) fs_descs, hs_descs;
} __attribute__ ((__packed__)) descriptors = {
.header = {
- .magic = htole32(FUNCTIONFS_DESCRIPTORS_MAGIC),
+ .magic = htole32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2),
+ .flags = htole32(FUNCTIONFS_HAS_FS_DESC |
+ FUNCTIONFS_HAS_HS_DESC),
.length = htole32(sizeof(descriptors)),
- .fs_count = 3,
- .hs_count = 3,
},
+ .fs_count = htole32(3),
.fs_descs = {
.intf = {
.bLength = sizeof(descriptors.fs_descs.intf),
@@ -61,6 +91,7 @@ static const struct {
.bmAttributes = USB_ENDPOINT_XFER_BULK,
},
},
+ .hs_count = htole32(3),
.hs_descs = {
.intf = {
.bLength = sizeof(descriptors.hs_descs.intf),
diff --git a/tools/usb/ffs-aio-example/multibuff/host_app/test.c b/tools/usb/ffs-aio-example/multibuff/host_app/test.c
index b0ad8747d03f..daa3abe6bebd 100644
--- a/tools/usb/ffs-aio-example/multibuff/host_app/test.c
+++ b/tools/usb/ffs-aio-example/multibuff/host_app/test.c
@@ -1,3 +1,30 @@
+/*
+ * This is free and unencumbered software released into the public domain.
+ *
+ * Anyone is free to copy, modify, publish, use, compile, sell, or
+ * distribute this software, either in source code form or as a compiled
+ * binary, for any purpose, commercial or non-commercial, and by any
+ * means.
+ *
+ * In jurisdictions that recognize copyright laws, the author or authors
+ * of this software dedicate any and all copyright interest in the
+ * software to the public domain. We make this dedication for the benefit
+ * of the public at large and to the detriment of our heirs and
+ * successors. We intend this dedication to be an overt act of
+ * relinquishment in perpetuity of all present and future rights to this
+ * software under copyright law.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * For more information, please refer to <http://unlicense.org/>
+ */
+
#include <libusb.h>
#include <stdio.h>
#include <string.h>
diff --git a/tools/usb/ffs-aio-example/simple/device_app/aio_simple.c b/tools/usb/ffs-aio-example/simple/device_app/aio_simple.c
index f558664a3317..adc310a6d489 100644
--- a/tools/usb/ffs-aio-example/simple/device_app/aio_simple.c
+++ b/tools/usb/ffs-aio-example/simple/device_app/aio_simple.c
@@ -1,3 +1,30 @@
+/*
+ * This is free and unencumbered software released into the public domain.
+ *
+ * Anyone is free to copy, modify, publish, use, compile, sell, or
+ * distribute this software, either in source code form or as a compiled
+ * binary, for any purpose, commercial or non-commercial, and by any
+ * means.
+ *
+ * In jurisdictions that recognize copyright laws, the author or authors
+ * of this software dedicate any and all copyright interest in the
+ * software to the public domain. We make this dedication for the benefit
+ * of the public at large and to the detriment of our heirs and
+ * successors. We intend this dedication to be an overt act of
+ * relinquishment in perpetuity of all present and future rights to this
+ * software under copyright law.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * For more information, please refer to <http://unlicense.org/>
+ */
+
#define _BSD_SOURCE /* for endian.h */
#include <endian.h>
@@ -25,7 +52,9 @@
/******************** Descriptors and Strings *******************************/
static const struct {
- struct usb_functionfs_descs_head header;
+ struct usb_functionfs_descs_head_v2 header;
+ __le32 fs_count;
+ __le32 hs_count;
struct {
struct usb_interface_descriptor intf;
struct usb_endpoint_descriptor_no_audio bulk_sink;
@@ -33,11 +62,12 @@ static const struct {
} __attribute__ ((__packed__)) fs_descs, hs_descs;
} __attribute__ ((__packed__)) descriptors = {
.header = {
- .magic = htole32(FUNCTIONFS_DESCRIPTORS_MAGIC),
+ .magic = htole32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2),
+ .flags = htole32(FUNCTIONFS_HAS_FS_DESC |
+ FUNCTIONFS_HAS_HS_DESC),
.length = htole32(sizeof(descriptors)),
- .fs_count = 3,
- .hs_count = 3,
},
+ .fs_count = htole32(3),
.fs_descs = {
.intf = {
.bLength = sizeof(descriptors.fs_descs.intf),
@@ -59,6 +89,7 @@ static const struct {
.bmAttributes = USB_ENDPOINT_XFER_BULK,
},
},
+ .hs_count = htole32(3),
.hs_descs = {
.intf = {
.bLength = sizeof(descriptors.hs_descs.intf),
diff --git a/tools/usb/ffs-aio-example/simple/host_app/test.c b/tools/usb/ffs-aio-example/simple/host_app/test.c
index 64b6a57d8ca3..acd6332811f3 100644
--- a/tools/usb/ffs-aio-example/simple/host_app/test.c
+++ b/tools/usb/ffs-aio-example/simple/host_app/test.c
@@ -1,3 +1,30 @@
+/*
+ * This is free and unencumbered software released into the public domain.
+ *
+ * Anyone is free to copy, modify, publish, use, compile, sell, or
+ * distribute this software, either in source code form or as a compiled
+ * binary, for any purpose, commercial or non-commercial, and by any
+ * means.
+ *
+ * In jurisdictions that recognize copyright laws, the author or authors
+ * of this software dedicate any and all copyright interest in the
+ * software to the public domain. We make this dedication for the benefit
+ * of the public at large and to the detriment of our heirs and
+ * successors. We intend this dedication to be an overt act of
+ * relinquishment in perpetuity of all present and future rights to this
+ * software under copyright law.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * For more information, please refer to <http://unlicense.org/>
+ */
+
#include <libusb.h>
#include <stdio.h>
#include <string.h>