summaryrefslogtreecommitdiff
path: root/drivers/usb
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/Makefile1
-rw-r--r--drivers/usb/atm/cxacru.c114
-rw-r--r--drivers/usb/atm/speedtch.c21
-rw-r--r--drivers/usb/atm/ueagle-atm.c3
-rw-r--r--drivers/usb/atm/usbatm.c8
-rw-r--r--drivers/usb/c67x00/c67x00-drv.c2
-rw-r--r--drivers/usb/c67x00/c67x00-sched.c6
-rw-r--r--drivers/usb/cdns3/cdns3-gadget.c17
-rw-r--r--drivers/usb/cdns3/cdns3-pci-wrap.c7
-rw-r--r--drivers/usb/cdns3/cdns3-plat.c2
-rw-r--r--drivers/usb/cdns3/cdns3-ti.c107
-rw-r--r--drivers/usb/cdns3/cdns3-trace.h61
-rw-r--r--drivers/usb/cdns3/cdnsp-debug.h5
-rw-r--r--drivers/usb/cdns3/cdnsp-ep0.c18
-rw-r--r--drivers/usb/cdns3/cdnsp-gadget.c67
-rw-r--r--drivers/usb/cdns3/cdnsp-gadget.h16
-rw-r--r--drivers/usb/cdns3/cdnsp-mem.c9
-rw-r--r--drivers/usb/cdns3/cdnsp-pci.c19
-rw-r--r--drivers/usb/cdns3/cdnsp-ring.c10
-rw-r--r--drivers/usb/cdns3/cdnsp-trace.h25
-rw-r--r--drivers/usb/cdns3/core.c7
-rw-r--r--drivers/usb/cdns3/core.h5
-rw-r--r--drivers/usb/cdns3/host.c11
-rw-r--r--drivers/usb/chipidea/ci_hdrc_imx.c109
-rw-r--r--drivers/usb/chipidea/ci_hdrc_imx.h1
-rw-r--r--drivers/usb/chipidea/core.c78
-rw-r--r--drivers/usb/chipidea/otg.c7
-rw-r--r--drivers/usb/chipidea/otg_fsm.c4
-rw-r--r--drivers/usb/chipidea/udc.c25
-rw-r--r--drivers/usb/chipidea/usbmisc_imx.c236
-rw-r--r--drivers/usb/class/cdc-acm.c89
-rw-r--r--drivers/usb/class/cdc-acm.h2
-rw-r--r--drivers/usb/class/cdc-wdm.c54
-rw-r--r--drivers/usb/class/usblp.c41
-rw-r--r--drivers/usb/class/usbtmc.c105
-rw-r--r--drivers/usb/common/ulpi.c12
-rw-r--r--drivers/usb/common/usb-conn-gpio.c27
-rw-r--r--drivers/usb/core/Makefile6
-rw-r--r--drivers/usb/core/config.c84
-rw-r--r--drivers/usb/core/devio.c17
-rw-r--r--drivers/usb/core/driver.c77
-rw-r--r--drivers/usb/core/endpoint.c10
-rw-r--r--drivers/usb/core/generic.c2
-rw-r--r--drivers/usb/core/hcd-pci.c2
-rw-r--r--drivers/usb/core/hcd.c91
-rw-r--r--drivers/usb/core/hub.c196
-rw-r--r--drivers/usb/core/hub.h1
-rw-r--r--drivers/usb/core/ledtrig-usbport.c4
-rw-r--r--drivers/usb/core/message.c138
-rw-r--r--drivers/usb/core/of.c8
-rw-r--r--drivers/usb/core/offload.c152
-rw-r--r--drivers/usb/core/phy.c20
-rw-r--r--drivers/usb/core/port.c25
-rw-r--r--drivers/usb/core/quirks.c50
-rw-r--r--drivers/usb/core/sysfs.c2
-rw-r--r--drivers/usb/core/trace.c6
-rw-r--r--drivers/usb/core/trace.h61
-rw-r--r--drivers/usb/core/urb.c50
-rw-r--r--drivers/usb/core/usb-acpi.c6
-rw-r--r--drivers/usb/core/usb.c150
-rw-r--r--drivers/usb/dwc2/core.c2
-rw-r--r--drivers/usb/dwc2/core.h23
-rw-r--r--drivers/usb/dwc2/gadget.c166
-rw-r--r--drivers/usb/dwc2/hcd.c109
-rw-r--r--drivers/usb/dwc2/hcd_queue.c11
-rw-r--r--drivers/usb/dwc2/params.c28
-rw-r--r--drivers/usb/dwc2/platform.c54
-rw-r--r--drivers/usb/dwc3/Kconfig45
-rw-r--r--drivers/usb/dwc3/Makefile5
-rw-r--r--drivers/usb/dwc3/core.c499
-rw-r--r--drivers/usb/dwc3/core.h56
-rw-r--r--drivers/usb/dwc3/debug.h18
-rw-r--r--drivers/usb/dwc3/debugfs.c58
-rw-r--r--drivers/usb/dwc3/drd.c88
-rw-r--r--drivers/usb/dwc3/dwc3-am62.c13
-rw-r--r--drivers/usb/dwc3/dwc3-apple.c523
-rw-r--r--drivers/usb/dwc3/dwc3-exynos.c27
-rw-r--r--drivers/usb/dwc3/dwc3-generic-plat.c256
-rw-r--r--drivers/usb/dwc3/dwc3-google.c627
-rw-r--r--drivers/usb/dwc3/dwc3-imx.c448
-rw-r--r--drivers/usb/dwc3/dwc3-imx8mp.c60
-rw-r--r--drivers/usb/dwc3/dwc3-meson-g12a.c3
-rw-r--r--drivers/usb/dwc3/dwc3-of-simple.c7
-rw-r--r--drivers/usb/dwc3/dwc3-pci.c91
-rw-r--r--drivers/usb/dwc3/dwc3-qcom-legacy.c935
-rw-r--r--drivers/usb/dwc3/dwc3-qcom.c428
-rw-r--r--drivers/usb/dwc3/dwc3-st.c2
-rw-r--r--drivers/usb/dwc3/dwc3-xilinx.c111
-rw-r--r--drivers/usb/dwc3/ep0.c43
-rw-r--r--drivers/usb/dwc3/gadget.c388
-rw-r--r--drivers/usb/dwc3/gadget.h4
-rw-r--r--drivers/usb/dwc3/glue.h196
-rw-r--r--drivers/usb/dwc3/host.c12
-rw-r--r--drivers/usb/dwc3/io.h11
-rw-r--r--drivers/usb/dwc3/trace.h95
-rw-r--r--drivers/usb/dwc3/ulpi.c35
-rw-r--r--drivers/usb/early/xhci-dbc.c4
-rw-r--r--drivers/usb/fotg210/fotg210-hcd.c19
-rw-r--r--drivers/usb/fotg210/fotg210-udc.c6
-rw-r--r--drivers/usb/gadget/composite.c45
-rw-r--r--drivers/usb/gadget/config.c53
-rw-r--r--drivers/usb/gadget/configfs.c45
-rw-r--r--drivers/usb/gadget/epautoconf.c2
-rw-r--r--drivers/usb/gadget/function/f_acm.c48
-rw-r--r--drivers/usb/gadget/function/f_ecm.c96
-rw-r--r--drivers/usb/gadget/function/f_eem.c72
-rw-r--r--drivers/usb/gadget/function/f_fs.c269
-rw-r--r--drivers/usb/gadget/function/f_hid.c214
-rw-r--r--drivers/usb/gadget/function/f_loopback.c6
-rw-r--r--drivers/usb/gadget/function/f_mass_storage.c34
-rw-r--r--drivers/usb/gadget/function/f_mass_storage.h2
-rw-r--r--drivers/usb/gadget/function/f_midi.c120
-rw-r--r--drivers/usb/gadget/function/f_midi2.c44
-rw-r--r--drivers/usb/gadget/function/f_ncm.c124
-rw-r--r--drivers/usb/gadget/function/f_obex.c6
-rw-r--r--drivers/usb/gadget/function/f_phonet.c15
-rw-r--r--drivers/usb/gadget/function/f_printer.c6
-rw-r--r--drivers/usb/gadget/function/f_rndis.c138
-rw-r--r--drivers/usb/gadget/function/f_serial.c13
-rw-r--r--drivers/usb/gadget/function/f_sourcesink.c58
-rw-r--r--drivers/usb/gadget/function/f_subset.c67
-rw-r--r--drivers/usb/gadget/function/f_tcm.c36
-rw-r--r--drivers/usb/gadget/function/f_uac1.c10
-rw-r--r--drivers/usb/gadget/function/f_uac1_legacy.c55
-rw-r--r--drivers/usb/gadget/function/f_uac2.c10
-rw-r--r--drivers/usb/gadget/function/f_uvc.c47
-rw-r--r--drivers/usb/gadget/function/g_zero.h1
-rw-r--r--drivers/usb/gadget/function/rndis.c2
-rw-r--r--drivers/usb/gadget/function/u_audio.c12
-rw-r--r--drivers/usb/gadget/function/u_ecm.h21
-rw-r--r--drivers/usb/gadget/function/u_eem.h21
-rw-r--r--drivers/usb/gadget/function/u_ether.c39
-rw-r--r--drivers/usb/gadget/function/u_ether.h26
-rw-r--r--drivers/usb/gadget/function/u_ether_configfs.h2
-rw-r--r--drivers/usb/gadget/function/u_fs.h2
-rw-r--r--drivers/usb/gadget/function/u_gether.h22
-rw-r--r--drivers/usb/gadget/function/u_hid.h2
-rw-r--r--drivers/usb/gadget/function/u_midi.h2
-rw-r--r--drivers/usb/gadget/function/u_ncm.h23
-rw-r--r--drivers/usb/gadget/function/u_rndis.h31
-rw-r--r--drivers/usb/gadget/function/u_serial.c62
-rw-r--r--drivers/usb/gadget/function/u_uac1_legacy.c2
-rw-r--r--drivers/usb/gadget/function/uvc.h11
-rw-r--r--drivers/usb/gadget/function/uvc_configfs.c75
-rw-r--r--drivers/usb/gadget/function/uvc_configfs.h4
-rw-r--r--drivers/usb/gadget/function/uvc_queue.c25
-rw-r--r--drivers/usb/gadget/function/uvc_v4l2.c15
-rw-r--r--drivers/usb/gadget/function/uvc_video.c16
-rw-r--r--drivers/usb/gadget/legacy/dbgp.c2
-rw-r--r--drivers/usb/gadget/legacy/g_ffs.c4
-rw-r--r--drivers/usb/gadget/legacy/hid.c2
-rw-r--r--drivers/usb/gadget/legacy/inode.c66
-rw-r--r--drivers/usb/gadget/legacy/raw_gadget.c7
-rw-r--r--drivers/usb/gadget/legacy/zero.c31
-rw-r--r--drivers/usb/gadget/udc/Kconfig44
-rw-r--r--drivers/usb/gadget/udc/Makefile5
-rw-r--r--drivers/usb/gadget/udc/amd5536udc_pci.c2
-rw-r--r--drivers/usb/gadget/udc/aspeed-vhub/core.c32
-rw-r--r--drivers/usb/gadget/udc/aspeed-vhub/dev.c7
-rw-r--r--drivers/usb/gadget/udc/aspeed-vhub/vhub.h1
-rw-r--r--drivers/usb/gadget/udc/aspeed_udc.c2
-rw-r--r--drivers/usb/gadget/udc/at91_udc.c4
-rw-r--r--drivers/usb/gadget/udc/atmel_usba_udc.c4
-rw-r--r--drivers/usb/gadget/udc/bcm63xx_udc.c2
-rw-r--r--drivers/usb/gadget/udc/bdc/bdc_core.c7
-rw-r--r--drivers/usb/gadget/udc/bdc/bdc_ep.c15
-rw-r--r--drivers/usb/gadget/udc/cdns2/cdns2-gadget.c3
-rw-r--r--drivers/usb/gadget/udc/cdns2/cdns2-trace.h69
-rw-r--r--drivers/usb/gadget/udc/core.c30
-rw-r--r--drivers/usb/gadget/udc/dummy_hcd.c67
-rw-r--r--drivers/usb/gadget/udc/fsl_qe_udc.c8
-rw-r--r--drivers/usb/gadget/udc/fsl_udc_core.c6
-rw-r--r--drivers/usb/gadget/udc/fusb300_udc.c1516
-rw-r--r--drivers/usb/gadget/udc/fusb300_udc.h675
-rw-r--r--drivers/usb/gadget/udc/goku_udc.c4
-rw-r--r--drivers/usb/gadget/udc/gr_udc.c2
-rw-r--r--drivers/usb/gadget/udc/lpc32xx_udc.c46
-rw-r--r--drivers/usb/gadget/udc/m66592-udc.c12
-rw-r--r--drivers/usb/gadget/udc/max3420_udc.c2
-rw-r--r--drivers/usb/gadget/udc/mv_u3d.h317
-rw-r--r--drivers/usb/gadget/udc/mv_u3d_core.c2062
-rw-r--r--drivers/usb/gadget/udc/mv_udc.h309
-rw-r--r--drivers/usb/gadget/udc/mv_udc_core.c2426
-rw-r--r--drivers/usb/gadget/udc/net2272.c2723
-rw-r--r--drivers/usb/gadget/udc/net2272.h584
-rw-r--r--drivers/usb/gadget/udc/net2280.c12
-rw-r--r--drivers/usb/gadget/udc/omap_udc.c12
-rw-r--r--drivers/usb/gadget/udc/pch_udc.c4
-rw-r--r--drivers/usb/gadget/udc/pxa25x_udc.c17
-rw-r--r--drivers/usb/gadget/udc/pxa27x_udc.c68
-rw-r--r--drivers/usb/gadget/udc/r8a66597-udc.c6
-rw-r--r--drivers/usb/gadget/udc/renesas_usb3.c25
-rw-r--r--drivers/usb/gadget/udc/renesas_usbf.c6
-rw-r--r--drivers/usb/gadget/udc/snps_udc_core.c8
-rw-r--r--drivers/usb/gadget/udc/tegra-xudc.c36
-rw-r--r--drivers/usb/gadget/udc/trace.h5
-rw-r--r--drivers/usb/gadget/udc/udc-xilinx.c8
-rw-r--r--drivers/usb/host/Kconfig27
-rw-r--r--drivers/usb/host/Makefile5
-rw-r--r--drivers/usb/host/ehci-brcm.c4
-rw-r--r--drivers/usb/host/ehci-dbg.c4
-rw-r--r--drivers/usb/host/ehci-fsl.c25
-rw-r--r--drivers/usb/host/ehci-hcd.c11
-rw-r--r--drivers/usb/host/ehci-mem.c2
-rw-r--r--drivers/usb/host/ehci-orion.c9
-rw-r--r--drivers/usb/host/ehci-pci.c1
-rw-r--r--drivers/usb/host/ehci-platform.c45
-rw-r--r--drivers/usb/host/ehci-sched.c11
-rw-r--r--drivers/usb/host/ehci-sysfs.c18
-rw-r--r--drivers/usb/host/fhci-hcd.c27
-rw-r--r--drivers/usb/host/fhci-mem.c4
-rw-r--r--drivers/usb/host/fhci-tds.c4
-rw-r--r--drivers/usb/host/fhci.h3
-rw-r--r--drivers/usb/host/fsl-mph-dr-of.c3
-rw-r--r--drivers/usb/host/isp116x-hcd.c2
-rw-r--r--drivers/usb/host/isp1362-hcd.c2769
-rw-r--r--drivers/usb/host/isp1362.h914
-rw-r--r--drivers/usb/host/max3421-hcd.c15
-rw-r--r--drivers/usb/host/octeon-hcd.c9
-rw-r--r--drivers/usb/host/ohci-at91.c2
-rw-r--r--drivers/usb/host/ohci-da8xx.c17
-rw-r--r--drivers/usb/host/ohci-dbg.c4
-rw-r--r--drivers/usb/host/ohci-hcd.c10
-rw-r--r--drivers/usb/host/ohci-hub.c2
-rw-r--r--drivers/usb/host/ohci-nxp.c18
-rw-r--r--drivers/usb/host/ohci-pci.c23
-rw-r--r--drivers/usb/host/ohci-platform.c25
-rw-r--r--drivers/usb/host/ohci-s3c2410.c8
-rw-r--r--drivers/usb/host/ohci-spear.c3
-rw-r--r--drivers/usb/host/oxu210hp-hcd.c10
-rw-r--r--drivers/usb/host/r8a66597-hcd.c16
-rw-r--r--drivers/usb/host/sl811-hcd.c7
-rw-r--r--drivers/usb/host/sl811_cs.c2
-rw-r--r--drivers/usb/host/uhci-debug.c2
-rw-r--r--drivers/usb/host/uhci-hcd.c10
-rw-r--r--drivers/usb/host/uhci-hcd.h1
-rw-r--r--drivers/usb/host/uhci-platform.c31
-rw-r--r--drivers/usb/host/uhci-q.c4
-rw-r--r--drivers/usb/host/xen-hcd.c10
-rw-r--r--drivers/usb/host/xhci-caps.h167
-rw-r--r--drivers/usb/host/xhci-dbgcap.c381
-rw-r--r--drivers/usb/host/xhci-dbgcap.h43
-rw-r--r--drivers/usb/host/xhci-dbgtty.c26
-rw-r--r--drivers/usb/host/xhci-debugfs.c175
-rw-r--r--drivers/usb/host/xhci-histb.c2
-rw-r--r--drivers/usb/host/xhci-hub.c483
-rw-r--r--drivers/usb/host/xhci-mem.c508
-rw-r--r--drivers/usb/host/xhci-mtk-sch.c9
-rw-r--r--drivers/usb/host/xhci-mtk.c5
-rw-r--r--drivers/usb/host/xhci-mtk.h10
-rw-r--r--drivers/usb/host/xhci-mvebu.c12
-rw-r--r--drivers/usb/host/xhci-mvebu.h6
-rw-r--r--drivers/usb/host/xhci-pci-renesas.c7
-rw-r--r--drivers/usb/host/xhci-pci.c82
-rw-r--r--drivers/usb/host/xhci-plat.c77
-rw-r--r--drivers/usb/host/xhci-plat.h3
-rw-r--r--drivers/usb/host/xhci-port.h5
-rw-r--r--drivers/usb/host/xhci-rcar-regs.h49
-rw-r--r--drivers/usb/host/xhci-rcar.c100
-rw-r--r--drivers/usb/host/xhci-ring.c715
-rw-r--r--drivers/usb/host/xhci-rzg3e-regs.h12
-rw-r--r--drivers/usb/host/xhci-sideband.c494
-rw-r--r--drivers/usb/host/xhci-tegra.c99
-rw-r--r--drivers/usb/host/xhci-trace.h59
-rw-r--r--drivers/usb/host/xhci.c623
-rw-r--r--drivers/usb/host/xhci.h382
-rw-r--r--drivers/usb/image/mdc800.c8
-rw-r--r--drivers/usb/image/microtek.c87
-rw-r--r--drivers/usb/isp1760/isp1760-hcd.c10
-rw-r--r--drivers/usb/isp1760/isp1760-udc.c8
-rw-r--r--drivers/usb/misc/Kconfig20
-rw-r--r--drivers/usb/misc/Makefile1
-rw-r--r--drivers/usb/misc/adutux.c2
-rw-r--r--drivers/usb/misc/apple-mfi-fastcharge.c30
-rw-r--r--drivers/usb/misc/appledisplay.c10
-rw-r--r--drivers/usb/misc/chaoskey.c18
-rw-r--r--drivers/usb/misc/cypress_cy7c63.c6
-rw-r--r--drivers/usb/misc/cytherm.c6
-rw-r--r--drivers/usb/misc/idmouse.c2
-rw-r--r--drivers/usb/misc/iowarrior.c44
-rw-r--r--drivers/usb/misc/ldusb.c2
-rw-r--r--drivers/usb/misc/legousbtower.c2
-rw-r--r--drivers/usb/misc/lvstest.c2
-rw-r--r--drivers/usb/misc/onboard_usb_dev.c132
-rw-r--r--drivers/usb/misc/onboard_usb_dev.h70
-rw-r--r--drivers/usb/misc/onboard_usb_dev_pdevs.c2
-rw-r--r--drivers/usb/misc/qcom_eud.c36
-rw-r--r--drivers/usb/misc/sisusbvga/sisusbvga.c2
-rw-r--r--drivers/usb/misc/trancevibrator.c5
-rw-r--r--drivers/usb/misc/usb-ljca.c62
-rw-r--r--drivers/usb/misc/usb251xb.c114
-rw-r--r--drivers/usb/misc/usbio.c752
-rw-r--r--drivers/usb/misc/usblcd.c2
-rw-r--r--drivers/usb/misc/usbsevseg.c5
-rw-r--r--drivers/usb/misc/usbtest.c18
-rw-r--r--drivers/usb/misc/uss720.c35
-rw-r--r--drivers/usb/misc/yurex.c4
-rw-r--r--drivers/usb/mon/mon_bin.c19
-rw-r--r--drivers/usb/mon/mon_main.c2
-rw-r--r--drivers/usb/mon/mon_stat.c2
-rw-r--r--drivers/usb/mon/mon_text.c2
-rw-r--r--drivers/usb/mtu3/mtu3.h34
-rw-r--r--drivers/usb/mtu3/mtu3_core.c4
-rw-r--r--drivers/usb/mtu3/mtu3_gadget.c2
-rw-r--r--drivers/usb/mtu3/mtu3_plat.c1
-rw-r--r--drivers/usb/mtu3/mtu3_qmu.c2
-rw-r--r--drivers/usb/musb/Kconfig3
-rw-r--r--drivers/usb/musb/da8xx.c9
-rw-r--r--drivers/usb/musb/jz4740.c4
-rw-r--r--drivers/usb/musb/mediatek.c2
-rw-r--r--drivers/usb/musb/mpfs.c9
-rw-r--r--drivers/usb/musb/musb_core.c27
-rw-r--r--drivers/usb/musb/musb_cppi41.c6
-rw-r--r--drivers/usb/musb/musb_debugfs.c5
-rw-r--r--drivers/usb/musb/musb_dsps.c13
-rw-r--r--drivers/usb/musb/musb_gadget.c8
-rw-r--r--drivers/usb/musb/musb_host.c2
-rw-r--r--drivers/usb/musb/musbhsdma.c2
-rw-r--r--drivers/usb/musb/omap2430.c38
-rw-r--r--drivers/usb/musb/sunxi.c4
-rw-r--r--drivers/usb/musb/tusb6010.c10
-rw-r--r--drivers/usb/musb/tusb6010_omap.c6
-rw-r--r--drivers/usb/musb/ux500_dma.c2
-rw-r--r--drivers/usb/phy/Kconfig12
-rw-r--r--drivers/usb/phy/Makefile1
-rw-r--r--drivers/usb/phy/phy-fsl-usb.c5
-rw-r--r--drivers/usb/phy/phy-fsl-usb.h2
-rw-r--r--drivers/usb/phy/phy-generic.c74
-rw-r--r--drivers/usb/phy/phy-isp1301.c7
-rw-r--r--drivers/usb/phy/phy-mv-usb.c881
-rw-r--r--drivers/usb/phy/phy-mxs-usb.c8
-rw-r--r--drivers/usb/phy/phy-tegra-usb.c386
-rw-r--r--drivers/usb/phy/phy-twl6030-usb.c3
-rw-r--r--drivers/usb/phy/phy-ulpi.c23
-rw-r--r--drivers/usb/phy/phy.c4
-rw-r--r--drivers/usb/renesas_usbhs/common.c122
-rw-r--r--drivers/usb/renesas_usbhs/fifo.c2
-rw-r--r--drivers/usb/renesas_usbhs/mod_gadget.c20
-rw-r--r--drivers/usb/renesas_usbhs/mod_host.c4
-rw-r--r--drivers/usb/renesas_usbhs/pipe.c5
-rw-r--r--drivers/usb/roles/class.c9
-rw-r--r--drivers/usb/serial/ark3116.c2
-rw-r--r--drivers/usb/serial/belkin_sa.c44
-rw-r--r--drivers/usb/serial/bus.c2
-rw-r--r--drivers/usb/serial/ch341.c2
-rw-r--r--drivers/usb/serial/console.c2
-rw-r--r--drivers/usb/serial/cp210x.c12
-rw-r--r--drivers/usb/serial/cyberjack.c2
-rw-r--r--drivers/usb/serial/cypress_m8.c2
-rw-r--r--drivers/usb/serial/digi_acceleport.c4
-rw-r--r--drivers/usb/serial/f81232.c77
-rw-r--r--drivers/usb/serial/ftdi_sio.c220
-rw-r--r--drivers/usb/serial/ftdi_sio_ids.h11
-rw-r--r--drivers/usb/serial/garmin_gps.c7
-rw-r--r--drivers/usb/serial/io_edgeport.c9
-rw-r--r--drivers/usb/serial/io_ti.c23
-rw-r--r--drivers/usb/serial/io_usbvend.h1
-rw-r--r--drivers/usb/serial/ipw.c2
-rw-r--r--drivers/usb/serial/ir-usb.c2
-rw-r--r--drivers/usb/serial/iuu_phoenix.c4
-rw-r--r--drivers/usb/serial/keyspan.c4
-rw-r--r--drivers/usb/serial/keyspan_pda.c2
-rw-r--r--drivers/usb/serial/kl5kusb105.c4
-rw-r--r--drivers/usb/serial/kobil_sct.c212
-rw-r--r--drivers/usb/serial/mct_u232.c2
-rw-r--r--drivers/usb/serial/metro-usb.c2
-rw-r--r--drivers/usb/serial/mos7720.c4
-rw-r--r--drivers/usb/serial/mos7840.c22
-rw-r--r--drivers/usb/serial/omninet.c2
-rw-r--r--drivers/usb/serial/opticon.c4
-rw-r--r--drivers/usb/serial/option.c88
-rw-r--r--drivers/usb/serial/oti6858.c4
-rw-r--r--drivers/usb/serial/pl2303.c6
-rw-r--r--drivers/usb/serial/qcserial.c2
-rw-r--r--drivers/usb/serial/quatech2.c4
-rw-r--r--drivers/usb/serial/sierra.c4
-rw-r--r--drivers/usb/serial/spcp8x5.c2
-rw-r--r--drivers/usb/serial/ssu100.c2
-rw-r--r--drivers/usb/serial/symbolserial.c2
-rw-r--r--drivers/usb/serial/ti_usb_3410_5052.c27
-rw-r--r--drivers/usb/serial/upd78f0730.c2
-rw-r--r--drivers/usb/serial/usb-serial-simple.c7
-rw-r--r--drivers/usb/serial/usb-serial.c15
-rw-r--r--drivers/usb/serial/usb_wwan.c2
-rw-r--r--drivers/usb/serial/whiteheat.c5
-rw-r--r--drivers/usb/serial/xr_serial.c4
-rw-r--r--drivers/usb/storage/alauda.c10
-rw-r--r--drivers/usb/storage/datafab.c16
-rw-r--r--drivers/usb/storage/debug.c4
-rw-r--r--drivers/usb/storage/ene_ub6250.c7
-rw-r--r--drivers/usb/storage/initializers.c2
-rw-r--r--drivers/usb/storage/isd200.c2
-rw-r--r--drivers/usb/storage/jumpshot.c12
-rw-r--r--drivers/usb/storage/karma.c2
-rw-r--r--drivers/usb/storage/onetouch.c2
-rw-r--r--drivers/usb/storage/protocol.c3
-rw-r--r--drivers/usb/storage/realtek_cr.c21
-rw-r--r--drivers/usb/storage/scsiglue.c2
-rw-r--r--drivers/usb/storage/sddr09.c20
-rw-r--r--drivers/usb/storage/sddr55.c17
-rw-r--r--drivers/usb/storage/shuttle_usbat.c4
-rw-r--r--drivers/usb/storage/sierra_ms.c5
-rw-r--r--drivers/usb/storage/transport.c18
-rw-r--r--drivers/usb/storage/uas.c38
-rw-r--r--drivers/usb/storage/unusual_devs.h36
-rw-r--r--drivers/usb/storage/unusual_uas.h16
-rw-r--r--drivers/usb/storage/usb.c22
-rw-r--r--drivers/usb/typec/Makefile2
-rw-r--r--drivers/usb/typec/altmodes/displayport.c58
-rw-r--r--drivers/usb/typec/altmodes/thunderbolt.c56
-rw-r--r--drivers/usb/typec/anx7411.c3
-rw-r--r--drivers/usb/typec/bus.c27
-rw-r--r--drivers/usb/typec/bus.h6
-rw-r--r--drivers/usb/typec/class.c187
-rw-r--r--drivers/usb/typec/class.h4
-rw-r--r--drivers/usb/typec/hd3ss3220.c87
-rw-r--r--drivers/usb/typec/mode_selection.c283
-rw-r--r--drivers/usb/typec/mux.c37
-rw-r--r--drivers/usb/typec/mux/Kconfig10
-rw-r--r--drivers/usb/typec/mux/Makefile1
-rw-r--r--drivers/usb/typec/mux/fsa4480.c5
-rw-r--r--drivers/usb/typec/mux/intel_pmc_mux.c2
-rw-r--r--drivers/usb/typec/mux/ps883x.c500
-rw-r--r--drivers/usb/typec/mux/tusb1046.c2
-rw-r--r--drivers/usb/typec/pd.c101
-rw-r--r--drivers/usb/typec/port-mapper.c23
-rw-r--r--drivers/usb/typec/retimer.c2
-rw-r--r--drivers/usb/typec/tcpm/Kconfig2
-rw-r--r--drivers/usb/typec/tcpm/fusb302.c27
-rw-r--r--drivers/usb/typec/tcpm/maxim_contaminant.c58
-rw-r--r--drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c2
-rw-r--r--drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c2
-rw-r--r--drivers/usb/typec/tcpm/tcpci.c56
-rw-r--r--drivers/usb/typec/tcpm/tcpci_maxim.h2
-rw-r--r--drivers/usb/typec/tcpm/tcpci_maxim_core.c113
-rw-r--r--drivers/usb/typec/tcpm/tcpci_rt1711h.c45
-rw-r--r--drivers/usb/typec/tcpm/tcpm.c1155
-rw-r--r--drivers/usb/typec/tipd/core.c568
-rw-r--r--drivers/usb/typec/tipd/tps6598x.h7
-rw-r--r--drivers/usb/typec/tipd/trace.h41
-rw-r--r--drivers/usb/typec/ucsi/Kconfig15
-rw-r--r--drivers/usb/typec/ucsi/Makefile5
-rw-r--r--drivers/usb/typec/ucsi/cros_ec_ucsi.c71
-rw-r--r--drivers/usb/typec/ucsi/debugfs.c45
-rw-r--r--drivers/usb/typec/ucsi/displayport.c21
-rw-r--r--drivers/usb/typec/ucsi/psy.c87
-rw-r--r--drivers/usb/typec/ucsi/thunderbolt.c212
-rw-r--r--drivers/usb/typec/ucsi/trace.c19
-rw-r--r--drivers/usb/typec/ucsi/trace.h1
-rw-r--r--drivers/usb/typec/ucsi/ucsi.c174
-rw-r--r--drivers/usb/typec/ucsi/ucsi.h75
-rw-r--r--drivers/usb/typec/ucsi/ucsi_acpi.c29
-rw-r--r--drivers/usb/typec/ucsi/ucsi_ccg.c104
-rw-r--r--drivers/usb/typec/ucsi/ucsi_glink.c92
-rw-r--r--drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c528
-rw-r--r--drivers/usb/typec/ucsi/ucsi_stm32g0.c7
-rw-r--r--drivers/usb/typec/ucsi/ucsi_yoga_c630.c176
-rw-r--r--drivers/usb/usb-skeleton.c2
-rw-r--r--drivers/usb/usbip/stub_dev.c7
-rw-r--r--drivers/usb/usbip/stub_rx.c2
-rw-r--r--drivers/usb/usbip/stub_tx.c17
-rw-r--r--drivers/usb/usbip/usbip_common.c83
-rw-r--r--drivers/usb/usbip/usbip_event.c2
-rw-r--r--drivers/usb/usbip/vhci_hcd.c124
-rw-r--r--drivers/usb/usbip/vhci_sysfs.c8
-rw-r--r--drivers/usb/usbip/vhci_tx.c2
-rw-r--r--drivers/usb/usbip/vudc_dev.c10
-rw-r--r--drivers/usb/usbip/vudc_sysfs.c2
-rw-r--r--drivers/usb/usbip/vudc_transfer.c2
-rw-r--r--drivers/usb/usbip/vudc_tx.c8
470 files changed, 18089 insertions, 21721 deletions
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index 949eca0adebe..6f3c86149887 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -30,7 +30,6 @@ obj-$(CONFIG_USB_UHCI_HCD) += host/
obj-$(CONFIG_USB_FHCI_HCD) += host/
obj-$(CONFIG_USB_XHCI_HCD) += host/
obj-$(CONFIG_USB_SL811_HCD) += host/
-obj-$(CONFIG_USB_ISP1362_HCD) += host/
obj-$(CONFIG_USB_R8A66597_HCD) += host/
obj-$(CONFIG_USB_FSL_USB2) += host/
obj-$(CONFIG_USB_FOTG210_HCD) += host/
diff --git a/drivers/usb/atm/cxacru.c b/drivers/usb/atm/cxacru.c
index 47d06af33747..f1900c567ba4 100644
--- a/drivers/usb/atm/cxacru.c
+++ b/drivers/usb/atm/cxacru.c
@@ -582,7 +582,7 @@ struct cxacru_timer {
static void cxacru_timeout_kill(struct timer_list *t)
{
- struct cxacru_timer *timer = from_timer(timer, t, timer);
+ struct cxacru_timer *timer = timer_container_of(timer, t, timer);
usb_unlink_urb(timer->urb);
}
@@ -597,8 +597,8 @@ static int cxacru_start_wait_urb(struct urb *urb, struct completion *done,
timer_setup_on_stack(&timer.timer, cxacru_timeout_kill, 0);
mod_timer(&timer.timer, jiffies + msecs_to_jiffies(CMD_TIMEOUT));
wait_for_completion(done);
- del_timer_sync(&timer.timer);
- destroy_timer_on_stack(&timer.timer);
+ timer_delete_sync(&timer.timer);
+ timer_destroy_on_stack(&timer.timer);
if (actual_length)
*actual_length = urb->actual_length;
@@ -980,25 +980,60 @@ cleanup:
return ret;
}
-static void cxacru_upload_firmware(struct cxacru_data *instance,
- const struct firmware *fw,
- const struct firmware *bp)
+
+static int cxacru_find_firmware(struct cxacru_data *instance,
+ char *phase, const struct firmware **fw_p)
{
- int ret;
+ struct usbatm_data *usbatm = instance->usbatm;
+ struct device *dev = &usbatm->usb_intf->dev;
+ char buf[16];
+
+ sprintf(buf, "cxacru-%s.bin", phase);
+ usb_dbg(usbatm, "cxacru_find_firmware: looking for %s\n", buf);
+
+ if (request_firmware(fw_p, buf, dev)) {
+ usb_dbg(usbatm, "no stage %s firmware found\n", phase);
+ return -ENOENT;
+ }
+
+ usb_info(usbatm, "found firmware %s\n", buf);
+
+ return 0;
+}
+
+static int cxacru_heavy_init(struct usbatm_data *usbatm_instance,
+ struct usb_interface *usb_intf)
+{
+ const struct firmware *fw, *bp;
+ struct cxacru_data *instance = usbatm_instance->driver_data;
struct usbatm_data *usbatm = instance->usbatm;
struct usb_device *usb_dev = usbatm->usb_dev;
__le16 signature[] = { usb_dev->descriptor.idVendor,
usb_dev->descriptor.idProduct };
__le32 val;
+ int ret;
- usb_dbg(usbatm, "%s\n", __func__);
+ ret = cxacru_find_firmware(instance, "fw", &fw);
+ if (ret) {
+ usb_warn(usbatm_instance, "firmware (cxacru-fw.bin) unavailable (system misconfigured?)\n");
+ return ret;
+ }
+
+ if (instance->modem_type->boot_rom_patch) {
+ ret = cxacru_find_firmware(instance, "bp", &bp);
+ if (ret) {
+ usb_warn(usbatm_instance, "boot ROM patch (cxacru-bp.bin) unavailable (system misconfigured?)\n");
+ release_firmware(fw);
+ return ret;
+ }
+ }
/* FirmwarePllFClkValue */
val = cpu_to_le32(instance->modem_type->pll_f_clk);
ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, PLLFCLK_ADDR, (u8 *) &val, 4);
if (ret) {
usb_err(usbatm, "FirmwarePllFClkValue failed: %d\n", ret);
- return;
+ goto done;
}
/* FirmwarePllBClkValue */
@@ -1006,7 +1041,7 @@ static void cxacru_upload_firmware(struct cxacru_data *instance,
ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, PLLBCLK_ADDR, (u8 *) &val, 4);
if (ret) {
usb_err(usbatm, "FirmwarePllBClkValue failed: %d\n", ret);
- return;
+ goto done;
}
/* Enable SDRAM */
@@ -1014,7 +1049,7 @@ static void cxacru_upload_firmware(struct cxacru_data *instance,
ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, SDRAMEN_ADDR, (u8 *) &val, 4);
if (ret) {
usb_err(usbatm, "Enable SDRAM failed: %d\n", ret);
- return;
+ goto done;
}
/* Firmware */
@@ -1022,7 +1057,7 @@ static void cxacru_upload_firmware(struct cxacru_data *instance,
ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, FW_ADDR, fw->data, fw->size);
if (ret) {
usb_err(usbatm, "Firmware upload failed: %d\n", ret);
- return;
+ goto done;
}
/* Boot ROM patch */
@@ -1031,7 +1066,7 @@ static void cxacru_upload_firmware(struct cxacru_data *instance,
ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, BR_ADDR, bp->data, bp->size);
if (ret) {
usb_err(usbatm, "Boot ROM patching failed: %d\n", ret);
- return;
+ goto done;
}
}
@@ -1039,7 +1074,7 @@ static void cxacru_upload_firmware(struct cxacru_data *instance,
ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, SIG_ADDR, (u8 *) signature, 4);
if (ret) {
usb_err(usbatm, "Signature storing failed: %d\n", ret);
- return;
+ goto done;
}
usb_info(usbatm, "starting device\n");
@@ -1051,7 +1086,7 @@ static void cxacru_upload_firmware(struct cxacru_data *instance,
}
if (ret) {
usb_err(usbatm, "Passing control to firmware failed: %d\n", ret);
- return;
+ goto done;
}
/* Delay to allow firmware to start up. */
@@ -1065,53 +1100,10 @@ static void cxacru_upload_firmware(struct cxacru_data *instance,
ret = cxacru_cm(instance, CM_REQUEST_CARD_GET_STATUS, NULL, 0, NULL, 0);
if (ret < 0) {
usb_err(usbatm, "modem failed to initialize: %d\n", ret);
- return;
- }
-}
-
-static int cxacru_find_firmware(struct cxacru_data *instance,
- char *phase, const struct firmware **fw_p)
-{
- struct usbatm_data *usbatm = instance->usbatm;
- struct device *dev = &usbatm->usb_intf->dev;
- char buf[16];
-
- sprintf(buf, "cxacru-%s.bin", phase);
- usb_dbg(usbatm, "cxacru_find_firmware: looking for %s\n", buf);
-
- if (request_firmware(fw_p, buf, dev)) {
- usb_dbg(usbatm, "no stage %s firmware found\n", phase);
- return -ENOENT;
- }
-
- usb_info(usbatm, "found firmware %s\n", buf);
-
- return 0;
-}
-
-static int cxacru_heavy_init(struct usbatm_data *usbatm_instance,
- struct usb_interface *usb_intf)
-{
- const struct firmware *fw, *bp;
- struct cxacru_data *instance = usbatm_instance->driver_data;
- int ret = cxacru_find_firmware(instance, "fw", &fw);
-
- if (ret) {
- usb_warn(usbatm_instance, "firmware (cxacru-fw.bin) unavailable (system misconfigured?)\n");
- return ret;
+ goto done;
}
- if (instance->modem_type->boot_rom_patch) {
- ret = cxacru_find_firmware(instance, "bp", &bp);
- if (ret) {
- usb_warn(usbatm_instance, "boot ROM patch (cxacru-bp.bin) unavailable (system misconfigured?)\n");
- release_firmware(fw);
- return ret;
- }
- }
-
- cxacru_upload_firmware(instance, fw, bp);
-
+done:
if (instance->modem_type->boot_rom_patch)
release_firmware(bp);
release_firmware(fw);
@@ -1138,7 +1130,7 @@ static int cxacru_bind(struct usbatm_data *usbatm_instance,
int ret;
/* instance init */
- instance = kzalloc(sizeof(*instance), GFP_KERNEL);
+ instance = kzalloc_obj(*instance);
if (!instance)
return -ENOMEM;
diff --git a/drivers/usb/atm/speedtch.c b/drivers/usb/atm/speedtch.c
index 973548b5c15c..9d28c652bcd8 100644
--- a/drivers/usb/atm/speedtch.c
+++ b/drivers/usb/atm/speedtch.c
@@ -13,6 +13,7 @@
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/firmware.h>
+#include <linux/hex.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
@@ -559,8 +560,9 @@ static void speedtch_check_status(struct work_struct *work)
static void speedtch_status_poll(struct timer_list *t)
{
- struct speedtch_instance_data *instance = from_timer(instance, t,
- status_check_timer);
+ struct speedtch_instance_data *instance = timer_container_of(instance,
+ t,
+ status_check_timer);
schedule_work(&instance->status_check_work);
@@ -573,8 +575,9 @@ static void speedtch_status_poll(struct timer_list *t)
static void speedtch_resubmit_int(struct timer_list *t)
{
- struct speedtch_instance_data *instance = from_timer(instance, t,
- resubmit_timer);
+ struct speedtch_instance_data *instance = timer_container_of(instance,
+ t,
+ resubmit_timer);
struct urb *int_urb = instance->int_urb;
int ret;
@@ -612,7 +615,7 @@ static void speedtch_handle_int(struct urb *int_urb)
}
if ((count == 6) && !memcmp(up_int, instance->int_data, 6)) {
- del_timer(&instance->status_check_timer);
+ timer_delete(&instance->status_check_timer);
atm_info(usbatm, "DSL line goes up\n");
} else if ((count == 6) && !memcmp(down_int, instance->int_data, 6)) {
atm_info(usbatm, "DSL line goes down\n");
@@ -688,7 +691,7 @@ static void speedtch_atm_stop(struct usbatm_data *usbatm, struct atm_dev *atm_de
atm_dbg(usbatm, "%s entered\n", __func__);
- del_timer_sync(&instance->status_check_timer);
+ timer_delete_sync(&instance->status_check_timer);
/*
* Since resubmit_timer and int_urb can schedule themselves and
@@ -697,14 +700,14 @@ static void speedtch_atm_stop(struct usbatm_data *usbatm, struct atm_dev *atm_de
instance->int_urb = NULL; /* signal shutdown */
mb();
usb_kill_urb(int_urb);
- del_timer_sync(&instance->resubmit_timer);
+ timer_delete_sync(&instance->resubmit_timer);
/*
* At this point, speedtch_handle_int and speedtch_resubmit_int
* can run or be running, but instance->int_urb == NULL means that
* they will not reschedule
*/
usb_kill_urb(int_urb);
- del_timer_sync(&instance->resubmit_timer);
+ timer_delete_sync(&instance->resubmit_timer);
usb_free_urb(int_urb);
flush_work(&instance->status_check_work);
@@ -801,7 +804,7 @@ static int speedtch_bind(struct usbatm_data *usbatm,
}
}
- instance = kzalloc(sizeof(*instance), GFP_KERNEL);
+ instance = kzalloc_obj(*instance);
if (!instance) {
ret = -ENOMEM;
diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c
index cd0f7b4bd82a..f3ae72feb5bf 100644
--- a/drivers/usb/atm/ueagle-atm.c
+++ b/drivers/usb/atm/ueagle-atm.c
@@ -15,6 +15,7 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/crc32.h>
+#include <linux/hex.h>
#include <linux/usb.h>
#include <linux/firmware.h>
#include <linux/ctype.h>
@@ -2515,7 +2516,7 @@ static int uea_bind(struct usbatm_data *usbatm, struct usb_interface *intf,
return ret;
}
- sc = kzalloc(sizeof(struct uea_softc), GFP_KERNEL);
+ sc = kzalloc_obj(struct uea_softc);
if (!sc)
return -ENOMEM;
diff --git a/drivers/usb/atm/usbatm.c b/drivers/usb/atm/usbatm.c
index d1e622bb1406..9600e1ec0993 100644
--- a/drivers/usb/atm/usbatm.c
+++ b/drivers/usb/atm/usbatm.c
@@ -804,7 +804,7 @@ static int usbatm_atm_open(struct atm_vcc *vcc)
goto fail;
}
- new = kzalloc(sizeof(struct usbatm_vcc_data), GFP_KERNEL);
+ new = kzalloc_obj(struct usbatm_vcc_data);
if (!new) {
ret = -ENOMEM;
goto fail;
@@ -993,7 +993,7 @@ static int usbatm_heavy_init(struct usbatm_data *instance)
static void usbatm_tasklet_schedule(struct timer_list *t)
{
- struct usbatm_channel *channel = from_timer(channel, t, delay);
+ struct usbatm_channel *channel = timer_container_of(channel, t, delay);
tasklet_schedule(&channel->tasklet);
}
@@ -1237,8 +1237,8 @@ void usbatm_usb_disconnect(struct usb_interface *intf)
for (i = 0; i < num_rcv_urbs + num_snd_urbs; i++)
usb_kill_urb(instance->urbs[i]);
- del_timer_sync(&instance->rx_channel.delay);
- del_timer_sync(&instance->tx_channel.delay);
+ timer_delete_sync(&instance->rx_channel.delay);
+ timer_delete_sync(&instance->tx_channel.delay);
/* turn usbatm_[rt]x_process into something close to a no-op */
/* no need to take the spinlock */
diff --git a/drivers/usb/c67x00/c67x00-drv.c b/drivers/usb/c67x00/c67x00-drv.c
index 8f38e2c5369a..7ca6682c96c4 100644
--- a/drivers/usb/c67x00/c67x00-drv.c
+++ b/drivers/usb/c67x00/c67x00-drv.c
@@ -121,7 +121,7 @@ static int c67x00_drv_probe(struct platform_device *pdev)
if (!pdata)
return -ENODEV;
- c67x00 = kzalloc(sizeof(*c67x00), GFP_KERNEL);
+ c67x00 = kzalloc_obj(*c67x00);
if (!c67x00)
return -ENOMEM;
diff --git a/drivers/usb/c67x00/c67x00-sched.c b/drivers/usb/c67x00/c67x00-sched.c
index a09fa68a6ce7..a832f5696f2a 100644
--- a/drivers/usb/c67x00/c67x00-sched.c
+++ b/drivers/usb/c67x00/c67x00-sched.c
@@ -246,7 +246,7 @@ c67x00_ep_data_alloc(struct c67x00_hcd *c67x00, struct urb *urb)
}
/* Allocate and initialize a new c67x00 endpoint data structure */
- ep_data = kzalloc(sizeof(*ep_data), GFP_ATOMIC);
+ ep_data = kzalloc_obj(*ep_data, GFP_ATOMIC);
if (!ep_data)
return NULL;
@@ -349,7 +349,7 @@ int c67x00_urb_enqueue(struct usb_hcd *hcd,
int port = get_root_port(urb->dev)-1;
/* Allocate and initialize urb private data */
- urbp = kzalloc(sizeof(*urbp), mem_flags);
+ urbp = kzalloc_obj(*urbp, mem_flags);
if (!urbp) {
ret = -ENOMEM;
goto err_urbp;
@@ -574,7 +574,7 @@ static int c67x00_create_td(struct c67x00_hcd *c67x00, struct urb *urb,
|| usb_pipeint(urb->pipe)))
return -EMSGSIZE; /* Not really an error, but expected */
- td = kzalloc(sizeof(*td), GFP_ATOMIC);
+ td = kzalloc_obj(*td, GFP_ATOMIC);
if (!td)
return -ENOMEM;
diff --git a/drivers/usb/cdns3/cdns3-gadget.c b/drivers/usb/cdns3/cdns3-gadget.c
index fd1beb10bba7..8382231af357 100644
--- a/drivers/usb/cdns3/cdns3-gadget.c
+++ b/drivers/usb/cdns3/cdns3-gadget.c
@@ -903,7 +903,7 @@ static int cdns3_prepare_aligned_request_buf(struct cdns3_request *priv_req)
buf = priv_req->aligned_buf;
if (!buf || priv_req->request.length > buf->size) {
- buf = kzalloc(sizeof(*buf), GFP_ATOMIC);
+ buf = kzalloc_obj(*buf, GFP_ATOMIC);
if (!buf)
return -ENOMEM;
@@ -1963,6 +1963,7 @@ static irqreturn_t cdns3_device_thread_irq_handler(int irq, void *data)
unsigned int bit;
unsigned long reg;
+ local_bh_disable();
spin_lock_irqsave(&priv_dev->lock, flags);
reg = readl(&priv_dev->regs->usb_ists);
@@ -2004,6 +2005,7 @@ static irqreturn_t cdns3_device_thread_irq_handler(int irq, void *data)
irqend:
writel(~0, &priv_dev->regs->ep_ien);
spin_unlock_irqrestore(&priv_dev->lock, flags);
+ local_bh_enable();
return ret;
}
@@ -2313,7 +2315,7 @@ struct usb_request *cdns3_gadget_ep_alloc_request(struct usb_ep *ep,
struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
struct cdns3_request *priv_req;
- priv_req = kzalloc(sizeof(*priv_req), gfp_flags);
+ priv_req = kzalloc_obj(*priv_req, gfp_flags);
if (!priv_req)
return NULL;
@@ -2587,6 +2589,9 @@ static int __cdns3_gadget_ep_queue(struct usb_ep *ep,
struct cdns3_request *priv_req;
int ret = 0;
+ if (!ep->desc)
+ return -ESHUTDOWN;
+
request->actual = 0;
request->status = -EINPROGRESS;
priv_req = to_cdns3_request(request);
@@ -3249,7 +3254,6 @@ static void cdns3_gadget_exit(struct cdns *cdns)
priv_dev = cdns->gadget_dev;
- pm_runtime_mark_last_busy(cdns->dev);
pm_runtime_put_autosuspend(cdns->dev);
usb_del_gadget(&priv_dev->gadget);
@@ -3286,7 +3290,7 @@ static int cdns3_gadget_start(struct cdns *cdns)
u32 max_speed;
int ret;
- priv_dev = kzalloc(sizeof(*priv_dev), GFP_KERNEL);
+ priv_dev = kzalloc_obj(*priv_dev);
if (!priv_dev)
return -ENOMEM;
@@ -3427,6 +3431,7 @@ static int __cdns3_gadget_init(struct cdns *cdns)
ret = cdns3_gadget_start(cdns);
if (ret) {
pm_runtime_put_sync(cdns->dev);
+ cdns_drd_gadget_off(cdns);
return ret;
}
@@ -3468,7 +3473,7 @@ __must_hold(&cdns->lock)
return 0;
}
-static int cdns3_gadget_resume(struct cdns *cdns, bool hibernated)
+static int cdns3_gadget_resume(struct cdns *cdns, bool lost_power)
{
struct cdns3_device *priv_dev = cdns->gadget_dev;
@@ -3476,7 +3481,7 @@ static int cdns3_gadget_resume(struct cdns *cdns, bool hibernated)
return 0;
cdns3_gadget_config(priv_dev);
- if (hibernated)
+ if (lost_power)
writel(USB_CONF_DEVEN, &priv_dev->regs->usb_conf);
return 0;
diff --git a/drivers/usb/cdns3/cdns3-pci-wrap.c b/drivers/usb/cdns3/cdns3-pci-wrap.c
index 3b3b3dc75f35..eb5760f75b9d 100644
--- a/drivers/usb/cdns3/cdns3-pci-wrap.c
+++ b/drivers/usb/cdns3/cdns3-pci-wrap.c
@@ -97,11 +97,9 @@ static int cdns3_pci_probe(struct pci_dev *pdev,
if (pci_is_enabled(func)) {
wrap = pci_get_drvdata(func);
} else {
- wrap = kzalloc(sizeof(*wrap), GFP_KERNEL);
- if (!wrap) {
- pci_disable_device(pdev);
+ wrap = kzalloc_obj(*wrap);
+ if (!wrap)
return -ENOMEM;
- }
}
res = wrap->dev_res;
@@ -160,7 +158,6 @@ static int cdns3_pci_probe(struct pci_dev *pdev,
/* register platform device */
wrap->plat_dev = platform_device_register_full(&plat_info);
if (IS_ERR(wrap->plat_dev)) {
- pci_disable_device(pdev);
err = PTR_ERR(wrap->plat_dev);
kfree(wrap);
return err;
diff --git a/drivers/usb/cdns3/cdns3-plat.c b/drivers/usb/cdns3/cdns3-plat.c
index 59ec505e198a..735df88774e4 100644
--- a/drivers/usb/cdns3/cdns3-plat.c
+++ b/drivers/usb/cdns3/cdns3-plat.c
@@ -179,8 +179,6 @@ err_phy3_init:
/**
* cdns3_plat_remove() - unbind drd driver and clean up
* @pdev: Pointer to Linux platform device
- *
- * Returns 0 on success otherwise negative errno
*/
static void cdns3_plat_remove(struct platform_device *pdev)
{
diff --git a/drivers/usb/cdns3/cdns3-ti.c b/drivers/usb/cdns3/cdns3-ti.c
index 040bb91e9c01..302ebf6d8e53 100644
--- a/drivers/usb/cdns3/cdns3-ti.c
+++ b/drivers/usb/cdns3/cdns3-ti.c
@@ -58,6 +58,7 @@ struct cdns_ti {
unsigned vbus_divider:1;
struct clk *usb2_refclk;
struct clk *lpm_clk;
+ int usb2_refclk_rate_code;
};
static const int cdns_ti_rate_table[] = { /* in KHZ */
@@ -98,15 +99,50 @@ static const struct of_dev_auxdata cdns_ti_auxdata[] = {
{},
};
+static void cdns_ti_reset_and_init_hw(struct cdns_ti *data)
+{
+ u32 reg;
+
+ /* assert RESET */
+ reg = cdns_ti_readl(data, USBSS_W1);
+ reg &= ~USBSS_W1_PWRUP_RST;
+ cdns_ti_writel(data, USBSS_W1, reg);
+
+ /* set static config */
+ reg = cdns_ti_readl(data, USBSS_STATIC_CONFIG);
+ reg &= ~USBSS1_STATIC_PLL_REF_SEL_MASK;
+ reg |= data->usb2_refclk_rate_code << USBSS1_STATIC_PLL_REF_SEL_SHIFT;
+
+ reg &= ~USBSS1_STATIC_VBUS_SEL_MASK;
+ if (data->vbus_divider)
+ reg |= 1 << USBSS1_STATIC_VBUS_SEL_SHIFT;
+
+ cdns_ti_writel(data, USBSS_STATIC_CONFIG, reg);
+ reg = cdns_ti_readl(data, USBSS_STATIC_CONFIG);
+
+ /* set USB2_ONLY mode if requested */
+ reg = cdns_ti_readl(data, USBSS_W1);
+ if (data->usb2_only)
+ reg |= USBSS_W1_USB2_ONLY;
+
+ /* set default modestrap */
+ reg |= USBSS_W1_MODESTRAP_SEL;
+ reg &= ~USBSS_W1_MODESTRAP_MASK;
+ reg |= USBSS_MODESTRAP_MODE_NONE << USBSS_W1_MODESTRAP_SHIFT;
+ cdns_ti_writel(data, USBSS_W1, reg);
+
+ /* de-assert RESET */
+ reg |= USBSS_W1_PWRUP_RST;
+ cdns_ti_writel(data, USBSS_W1, reg);
+}
+
static int cdns_ti_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *node = pdev->dev.of_node;
struct cdns_ti *data;
- int error;
- u32 reg;
- int rate_code, i;
unsigned long rate;
+ int error, i;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
@@ -146,7 +182,17 @@ static int cdns_ti_probe(struct platform_device *pdev)
return -EINVAL;
}
- rate_code = i;
+ data->usb2_refclk_rate_code = i;
+ data->vbus_divider = device_property_read_bool(dev, "ti,vbus-divider");
+ data->usb2_only = device_property_read_bool(dev, "ti,usb2-only");
+
+ /*
+ * The call below to pm_runtime_get_sync() MIGHT reset hardware, if it
+ * detects it as uninitialised. We want to enforce a reset at probe,
+ * and so do it manually here. This means the first runtime_resume()
+ * will be a no-op.
+ */
+ cdns_ti_reset_and_init_hw(data);
pm_runtime_enable(dev);
error = pm_runtime_get_sync(dev);
@@ -155,40 +201,6 @@ static int cdns_ti_probe(struct platform_device *pdev)
goto err;
}
- /* assert RESET */
- reg = cdns_ti_readl(data, USBSS_W1);
- reg &= ~USBSS_W1_PWRUP_RST;
- cdns_ti_writel(data, USBSS_W1, reg);
-
- /* set static config */
- reg = cdns_ti_readl(data, USBSS_STATIC_CONFIG);
- reg &= ~USBSS1_STATIC_PLL_REF_SEL_MASK;
- reg |= rate_code << USBSS1_STATIC_PLL_REF_SEL_SHIFT;
-
- reg &= ~USBSS1_STATIC_VBUS_SEL_MASK;
- data->vbus_divider = device_property_read_bool(dev, "ti,vbus-divider");
- if (data->vbus_divider)
- reg |= 1 << USBSS1_STATIC_VBUS_SEL_SHIFT;
-
- cdns_ti_writel(data, USBSS_STATIC_CONFIG, reg);
- reg = cdns_ti_readl(data, USBSS_STATIC_CONFIG);
-
- /* set USB2_ONLY mode if requested */
- reg = cdns_ti_readl(data, USBSS_W1);
- data->usb2_only = device_property_read_bool(dev, "ti,usb2-only");
- if (data->usb2_only)
- reg |= USBSS_W1_USB2_ONLY;
-
- /* set default modestrap */
- reg |= USBSS_W1_MODESTRAP_SEL;
- reg &= ~USBSS_W1_MODESTRAP_MASK;
- reg |= USBSS_MODESTRAP_MODE_NONE << USBSS_W1_MODESTRAP_SHIFT;
- cdns_ti_writel(data, USBSS_W1, reg);
-
- /* de-assert RESET */
- reg |= USBSS_W1_PWRUP_RST;
- cdns_ti_writel(data, USBSS_W1, reg);
-
error = of_platform_populate(node, NULL, cdns_ti_auxdata, dev);
if (error) {
dev_err(dev, "failed to create children: %d\n", error);
@@ -224,6 +236,24 @@ static void cdns_ti_remove(struct platform_device *pdev)
platform_set_drvdata(pdev, NULL);
}
+static int cdns_ti_runtime_resume(struct device *dev)
+{
+ const u32 mask = USBSS_W1_PWRUP_RST | USBSS_W1_MODESTRAP_SEL;
+ struct cdns_ti *data = dev_get_drvdata(dev);
+ u32 w1;
+
+ w1 = cdns_ti_readl(data, USBSS_W1);
+ if ((w1 & mask) != mask)
+ cdns_ti_reset_and_init_hw(data);
+
+ return 0;
+}
+
+static const struct dev_pm_ops cdns_ti_pm_ops = {
+ RUNTIME_PM_OPS(NULL, cdns_ti_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
+};
+
static const struct of_device_id cdns_ti_of_match[] = {
{ .compatible = "ti,j721e-usb", },
{ .compatible = "ti,am64-usb", },
@@ -237,6 +267,7 @@ static struct platform_driver cdns_ti_driver = {
.driver = {
.name = "cdns3-ti",
.of_match_table = cdns_ti_of_match,
+ .pm = pm_ptr(&cdns_ti_pm_ops),
},
};
diff --git a/drivers/usb/cdns3/cdns3-trace.h b/drivers/usb/cdns3/cdns3-trace.h
index c4e542f1b9b7..21a6a2ce7a3d 100644
--- a/drivers/usb/cdns3/cdns3-trace.h
+++ b/drivers/usb/cdns3/cdns3-trace.h
@@ -283,39 +283,6 @@ TRACE_EVENT(cdns3_ep0_queue,
__entry->length)
);
-DECLARE_EVENT_CLASS(cdns3_stream_split_transfer_len,
- TP_PROTO(struct cdns3_request *req),
- TP_ARGS(req),
- TP_STRUCT__entry(
- __string(name, req->priv_ep->name)
- __field(struct cdns3_request *, req)
- __field(unsigned int, length)
- __field(unsigned int, actual)
- __field(unsigned int, stream_id)
- ),
- TP_fast_assign(
- __assign_str(name);
- __entry->req = req;
- __entry->actual = req->request.length;
- __entry->length = req->request.actual;
- __entry->stream_id = req->request.stream_id;
- ),
- TP_printk("%s: req: %p,request length: %u actual length: %u SID: %u",
- __get_str(name), __entry->req, __entry->length,
- __entry->actual, __entry->stream_id)
-);
-
-DEFINE_EVENT(cdns3_stream_split_transfer_len, cdns3_stream_transfer_split,
- TP_PROTO(struct cdns3_request *req),
- TP_ARGS(req)
-);
-
-DEFINE_EVENT(cdns3_stream_split_transfer_len,
- cdns3_stream_transfer_split_next_part,
- TP_PROTO(struct cdns3_request *req),
- TP_ARGS(req)
-);
-
DECLARE_EVENT_CLASS(cdns3_log_aligned_request,
TP_PROTO(struct cdns3_request *priv_req),
TP_ARGS(priv_req),
@@ -354,34 +321,6 @@ DEFINE_EVENT(cdns3_log_aligned_request, cdns3_prepare_aligned_request,
TP_ARGS(req)
);
-DECLARE_EVENT_CLASS(cdns3_log_map_request,
- TP_PROTO(struct cdns3_request *priv_req),
- TP_ARGS(priv_req),
- TP_STRUCT__entry(
- __string(name, priv_req->priv_ep->name)
- __field(struct usb_request *, req)
- __field(void *, buf)
- __field(dma_addr_t, dma)
- ),
- TP_fast_assign(
- __assign_str(name);
- __entry->req = &priv_req->request;
- __entry->buf = priv_req->request.buf;
- __entry->dma = priv_req->request.dma;
- ),
- TP_printk("%s: req: %p, req buf %p, dma %p",
- __get_str(name), __entry->req, __entry->buf, &__entry->dma
- )
-);
-DEFINE_EVENT(cdns3_log_map_request, cdns3_map_request,
- TP_PROTO(struct cdns3_request *req),
- TP_ARGS(req)
-);
-DEFINE_EVENT(cdns3_log_map_request, cdns3_mapped_request,
- TP_PROTO(struct cdns3_request *req),
- TP_ARGS(req)
-);
-
DECLARE_EVENT_CLASS(cdns3_log_trb,
TP_PROTO(struct cdns3_endpoint *priv_ep, struct cdns3_trb *trb),
TP_ARGS(priv_ep, trb),
diff --git a/drivers/usb/cdns3/cdnsp-debug.h b/drivers/usb/cdns3/cdnsp-debug.h
index cd138acdcce1..86860686d836 100644
--- a/drivers/usb/cdns3/cdnsp-debug.h
+++ b/drivers/usb/cdns3/cdnsp-debug.h
@@ -327,12 +327,13 @@ static inline const char *cdnsp_decode_trb(char *str, size_t size, u32 field0,
case TRB_RESET_EP:
case TRB_HALT_ENDPOINT:
ret = scnprintf(str, size,
- "%s: ep%d%s(%d) ctx %08x%08x slot %ld flags %c",
+ "%s: ep%d%s(%d) ctx %08x%08x slot %ld flags %c %c",
cdnsp_trb_type_string(type),
ep_num, ep_id % 2 ? "out" : "in",
TRB_TO_EP_INDEX(field3), field1, field0,
TRB_TO_SLOT_ID(field3),
- field3 & TRB_CYCLE ? 'C' : 'c');
+ field3 & TRB_CYCLE ? 'C' : 'c',
+ field3 & TRB_ESP ? 'P' : 'p');
break;
case TRB_STOP_RING:
ret = scnprintf(str, size,
diff --git a/drivers/usb/cdns3/cdnsp-ep0.c b/drivers/usb/cdns3/cdnsp-ep0.c
index f317d3c84781..5cd9b898ce97 100644
--- a/drivers/usb/cdns3/cdnsp-ep0.c
+++ b/drivers/usb/cdns3/cdnsp-ep0.c
@@ -414,6 +414,7 @@ static int cdnsp_ep0_std_request(struct cdnsp_device *pdev,
void cdnsp_setup_analyze(struct cdnsp_device *pdev)
{
struct usb_ctrlrequest *ctrl = &pdev->setup;
+ struct cdnsp_ep *pep;
int ret = -EINVAL;
u16 len;
@@ -427,10 +428,21 @@ void cdnsp_setup_analyze(struct cdnsp_device *pdev)
goto out;
}
+ pep = &pdev->eps[0];
+
/* Restore the ep0 to Stopped/Running state. */
- if (pdev->eps[0].ep_state & EP_HALTED) {
- trace_cdnsp_ep0_halted("Restore to normal state");
- cdnsp_halt_endpoint(pdev, &pdev->eps[0], 0);
+ if (pep->ep_state & EP_HALTED) {
+ if (GET_EP_CTX_STATE(pep->out_ctx) == EP_STATE_HALTED)
+ cdnsp_halt_endpoint(pdev, pep, 0);
+
+ /*
+ * Halt Endpoint Command for SSP2 for ep0 preserve current
+ * endpoint state and driver has to synchronize the
+ * software endpoint state with endpoint output context
+ * state.
+ */
+ pep->ep_state &= ~EP_HALTED;
+ pep->ep_state |= EP_STOPPED;
}
/*
diff --git a/drivers/usb/cdns3/cdnsp-gadget.c b/drivers/usb/cdns3/cdnsp-gadget.c
index 97edf767ecee..6b3815f8a6e5 100644
--- a/drivers/usb/cdns3/cdnsp-gadget.c
+++ b/drivers/usb/cdns3/cdnsp-gadget.c
@@ -29,7 +29,8 @@
unsigned int cdnsp_port_speed(unsigned int port_status)
{
/*Detect gadget speed based on PORTSC register*/
- if (DEV_SUPERSPEEDPLUS(port_status))
+ if (DEV_SUPERSPEEDPLUS(port_status) ||
+ DEV_SSP_GEN1x2(port_status) || DEV_SSP_GEN2x2(port_status))
return USB_SPEED_SUPER_PLUS;
else if (DEV_SUPERSPEED(port_status))
return USB_SPEED_SUPER;
@@ -139,6 +140,26 @@ static void cdnsp_clear_port_change_bit(struct cdnsp_device *pdev,
(portsc & PORT_CHANGE_BITS), port_regs);
}
+static void cdnsp_set_apb_timeout_value(struct cdnsp_device *pdev)
+{
+ struct cdns *cdns = dev_get_drvdata(pdev->dev);
+ __le32 __iomem *reg;
+ void __iomem *base;
+ u32 offset = 0;
+ u32 val;
+
+ if (!cdns->override_apb_timeout)
+ return;
+
+ base = &pdev->cap_regs->hc_capbase;
+ offset = cdnsp_find_next_ext_cap(base, offset, D_XEC_PRE_REGS_CAP);
+ reg = base + offset + REG_CHICKEN_BITS_3_OFFSET;
+
+ val = le32_to_cpu(readl(reg));
+ val = CHICKEN_APB_TIMEOUT_SET(val, cdns->override_apb_timeout);
+ writel(cpu_to_le32(val), reg);
+}
+
static void cdnsp_set_chicken_bits_2(struct cdnsp_device *pdev, u32 bit)
{
__le32 __iomem *reg;
@@ -527,6 +548,7 @@ int cdnsp_wait_for_cmd_compl(struct cdnsp_device *pdev)
dma_addr_t cmd_deq_dma;
union cdnsp_trb *event;
u32 cycle_state;
+ u32 retry = 10;
int ret, val;
u64 cmd_dma;
u32 flags;
@@ -558,8 +580,23 @@ int cdnsp_wait_for_cmd_compl(struct cdnsp_device *pdev)
flags = le32_to_cpu(event->event_cmd.flags);
/* Check the owner of the TRB. */
- if ((flags & TRB_CYCLE) != cycle_state)
+ if ((flags & TRB_CYCLE) != cycle_state) {
+ /*
+ * Give some extra time to get chance controller
+ * to finish command before returning error code.
+ * Checking CMD_RING_BUSY is not sufficient because
+ * this bit is cleared to '0' when the Command
+ * Descriptor has been executed by controller
+ * and not when command completion event has
+ * be added to event ring.
+ */
+ if (retry--) {
+ udelay(20);
+ continue;
+ }
+
return -EINVAL;
+ }
cmd_dma = le64_to_cpu(event->event_cmd.cmd_trb);
@@ -1065,7 +1102,7 @@ static struct usb_request *cdnsp_gadget_ep_alloc_request(struct usb_ep *ep,
struct cdnsp_ep *pep = to_cdnsp_ep(ep);
struct cdnsp_request *preq;
- preq = kzalloc(sizeof(*preq), gfp_flags);
+ preq = kzalloc_obj(*preq, gfp_flags);
if (!preq)
return NULL;
@@ -1773,6 +1810,8 @@ static void cdnsp_get_rev_cap(struct cdnsp_device *pdev)
reg += cdnsp_find_next_ext_cap(reg, 0, RTL_REV_CAP);
pdev->rev_cap = reg;
+ pdev->rtl_revision = readl(&pdev->rev_cap->rtl_revision);
+
dev_info(pdev->dev, "Rev: %08x/%08x, eps: %08x, buff: %08x/%08x\n",
readl(&pdev->rev_cap->ctrl_revision),
readl(&pdev->rev_cap->rtl_revision),
@@ -1798,6 +1837,15 @@ static int cdnsp_gen_setup(struct cdnsp_device *pdev)
pdev->hci_version = HC_VERSION(pdev->hcc_params);
pdev->hcc_params = readl(&pdev->cap_regs->hcc_params);
+ /*
+ * Override the APB timeout value to give the controller more time for
+ * enabling UTMI clock and synchronizing APB and UTMI clock domains.
+ * This fix is platform specific and is required to fixes issue with
+ * reading incorrect value from PORTSC register after resuming
+ * from L1 state.
+ */
+ cdnsp_set_apb_timeout_value(pdev);
+
cdnsp_get_rev_cap(pdev);
/* Make sure the Device Controller is halted. */
@@ -1857,7 +1905,7 @@ static int __cdnsp_gadget_init(struct cdns *cdns)
cdns_drd_gadget_on(cdns);
- pdev = kzalloc(sizeof(*pdev), GFP_KERNEL);
+ pdev = kzalloc_obj(*pdev);
if (!pdev)
return -ENOMEM;
@@ -1928,7 +1976,10 @@ static int __cdnsp_gadget_init(struct cdns *cdns)
return 0;
del_gadget:
- usb_del_gadget_udc(&pdev->gadget);
+ usb_del_gadget(&pdev->gadget);
+ cdnsp_gadget_free_endpoints(pdev);
+ usb_put_gadget(&pdev->gadget);
+ goto halt_pdev;
free_endpoints:
cdnsp_gadget_free_endpoints(pdev);
halt_pdev:
@@ -1948,10 +1999,10 @@ static void cdnsp_gadget_exit(struct cdns *cdns)
struct cdnsp_device *pdev = cdns->gadget_dev;
devm_free_irq(pdev->dev, cdns->dev_irq, pdev);
- pm_runtime_mark_last_busy(cdns->dev);
pm_runtime_put_autosuspend(cdns->dev);
- usb_del_gadget_udc(&pdev->gadget);
+ usb_del_gadget(&pdev->gadget);
cdnsp_gadget_free_endpoints(pdev);
+ usb_put_gadget(&pdev->gadget);
cdnsp_mem_cleanup(pdev);
kfree(pdev);
cdns->gadget_dev = NULL;
@@ -1974,7 +2025,7 @@ static int cdnsp_gadget_suspend(struct cdns *cdns, bool do_wakeup)
return 0;
}
-static int cdnsp_gadget_resume(struct cdns *cdns, bool hibernated)
+static int cdnsp_gadget_resume(struct cdns *cdns, bool lost_power)
{
struct cdnsp_device *pdev = cdns->gadget_dev;
enum usb_device_speed max_speed;
diff --git a/drivers/usb/cdns3/cdnsp-gadget.h b/drivers/usb/cdns3/cdnsp-gadget.h
index 84887dfea763..a91cca509db0 100644
--- a/drivers/usb/cdns3/cdnsp-gadget.h
+++ b/drivers/usb/cdns3/cdnsp-gadget.h
@@ -285,11 +285,15 @@ struct cdnsp_port_regs {
#define XDEV_HS (0x3 << 10)
#define XDEV_SS (0x4 << 10)
#define XDEV_SSP (0x5 << 10)
+#define XDEV_SSP1x2 (0x6 << 10)
+#define XDEV_SSP2x2 (0x7 << 10)
#define DEV_UNDEFSPEED(p) (((p) & DEV_SPEED_MASK) == (0x0 << 10))
#define DEV_FULLSPEED(p) (((p) & DEV_SPEED_MASK) == XDEV_FS)
#define DEV_HIGHSPEED(p) (((p) & DEV_SPEED_MASK) == XDEV_HS)
#define DEV_SUPERSPEED(p) (((p) & DEV_SPEED_MASK) == XDEV_SS)
#define DEV_SUPERSPEEDPLUS(p) (((p) & DEV_SPEED_MASK) == XDEV_SSP)
+#define DEV_SSP_GEN1x2(p) (((p) & DEV_SPEED_MASK) == XDEV_SSP1x2)
+#define DEV_SSP_GEN2x2(p) (((p) & DEV_SPEED_MASK) == XDEV_SSP2x2)
#define DEV_SUPERSPEED_ANY(p) (((p) & DEV_SPEED_MASK) >= XDEV_SS)
#define DEV_PORT_SPEED(p) (((p) >> 10) & 0x0f)
/* Port Link State Write Strobe - set this when changing link state */
@@ -520,6 +524,9 @@ struct cdnsp_rev_cap {
#define REG_CHICKEN_BITS_2_OFFSET 0x48
#define CHICKEN_XDMA_2_TP_CACHE_DIS BIT(28)
+#define REG_CHICKEN_BITS_3_OFFSET 0x4C
+#define CHICKEN_APB_TIMEOUT_SET(p, val) (((p) & ~GENMASK(21, 0)) | (val))
+
/* XBUF Extended Capability ID. */
#define XBUF_CAP_ID 0xCB
#define XBUF_RX_TAG_MASK_0_OFFSET 0x1C
@@ -980,6 +987,12 @@ enum cdnsp_setup_dev {
#define STREAM_ID_FOR_TRB(p) ((((p)) << 16) & GENMASK(31, 16))
#define SCT_FOR_TRB(p) (((p) << 1) & 0x7)
+/*
+ * Halt Endpoint Command TRB field.
+ * The ESP bit only exists in the SSP2 controller.
+ */
+#define TRB_ESP BIT(9)
+
/* Link TRB specific fields. */
#define TRB_TC BIT(1)
@@ -1357,6 +1370,7 @@ struct cdnsp_port {
* @rev_cap: Controller Capabilities Registers.
* @hcs_params1: Cached register copies of read-only HCSPARAMS1
* @hcc_params: Cached register copies of read-only HCCPARAMS1
+ * @rtl_revision: Cached controller rtl revision.
* @setup: Temporary buffer for setup packet.
* @ep0_preq: Internal allocated request used during enumeration.
* @ep0_stage: ep0 stage during enumeration process.
@@ -1411,6 +1425,8 @@ struct cdnsp_device {
__u32 hcs_params1;
__u32 hcs_params3;
__u32 hcc_params;
+ #define RTL_REVISION_NEW_LPM 0x2700
+ __u32 rtl_revision;
/* Lock used in interrupt thread context. */
spinlock_t lock;
struct usb_ctrlrequest setup;
diff --git a/drivers/usb/cdns3/cdnsp-mem.c b/drivers/usb/cdns3/cdnsp-mem.c
index 97866bfb2da9..a2a1b21f2ef8 100644
--- a/drivers/usb/cdns3/cdnsp-mem.c
+++ b/drivers/usb/cdns3/cdnsp-mem.c
@@ -35,7 +35,7 @@ static struct cdnsp_segment *cdnsp_segment_alloc(struct cdnsp_device *pdev,
dma_addr_t dma;
int i;
- seg = kzalloc(sizeof(*seg), flags);
+ seg = kzalloc_obj(*seg, flags);
if (!seg)
return NULL;
@@ -376,7 +376,7 @@ static struct cdnsp_ring *cdnsp_ring_alloc(struct cdnsp_device *pdev,
struct cdnsp_ring *ring;
int ret;
- ring = kzalloc(sizeof *(ring), flags);
+ ring = kzalloc_obj(*(ring), flags);
if (!ring)
return NULL;
@@ -575,9 +575,8 @@ int cdnsp_alloc_stream_info(struct cdnsp_device *pdev,
stream_info->num_stream_ctxs = num_stream_ctxs;
/* Initialize the array of virtual pointers to stream rings. */
- stream_info->stream_rings = kcalloc(num_streams,
- sizeof(struct cdnsp_ring *),
- GFP_ATOMIC);
+ stream_info->stream_rings = kzalloc_objs(struct cdnsp_ring *,
+ num_streams, GFP_ATOMIC);
if (!stream_info->stream_rings)
return -ENOMEM;
diff --git a/drivers/usb/cdns3/cdnsp-pci.c b/drivers/usb/cdns3/cdnsp-pci.c
index a51144504ff3..566d94e49102 100644
--- a/drivers/usb/cdns3/cdnsp-pci.c
+++ b/drivers/usb/cdns3/cdnsp-pci.c
@@ -28,6 +28,8 @@
#define PCI_DRIVER_NAME "cdns-pci-usbssp"
#define PLAT_DRIVER_NAME "cdns-usbssp"
+#define CHICKEN_APB_TIMEOUT_VALUE 0x1C20
+
static struct pci_dev *cdnsp_get_second_fun(struct pci_dev *pdev)
{
/*
@@ -80,10 +82,10 @@ static int cdnsp_pci_probe(struct pci_dev *pdev,
if (pci_is_enabled(func)) {
cdnsp = pci_get_drvdata(func);
} else {
- cdnsp = kzalloc(sizeof(*cdnsp), GFP_KERNEL);
+ cdnsp = kzalloc_obj(*cdnsp);
if (!cdnsp) {
ret = -ENOMEM;
- goto disable_pci;
+ goto put_pci;
}
}
@@ -139,6 +141,14 @@ static int cdnsp_pci_probe(struct pci_dev *pdev,
cdnsp->otg_irq = pdev->irq;
}
+ /*
+ * Cadence PCI based platform require some longer timeout for APB
+ * to fixes domain clock synchronization issue after resuming
+ * controller from L1 state.
+ */
+ cdnsp->override_apb_timeout = CHICKEN_APB_TIMEOUT_VALUE;
+ pci_set_drvdata(pdev, cdnsp);
+
if (pci_is_enabled(func)) {
cdnsp->dev = dev;
cdnsp->gadget_init = cdnsp_gadget_init;
@@ -148,8 +158,6 @@ static int cdnsp_pci_probe(struct pci_dev *pdev,
goto free_cdnsp;
}
- pci_set_drvdata(pdev, cdnsp);
-
device_wakeup_enable(&pdev->dev);
if (pci_dev_run_wake(pdev))
pm_runtime_put_noidle(&pdev->dev);
@@ -160,9 +168,6 @@ free_cdnsp:
if (!pci_is_enabled(func))
kfree(cdnsp);
-disable_pci:
- pci_disable_device(pdev);
-
put_pci:
pci_dev_put(func);
diff --git a/drivers/usb/cdns3/cdnsp-ring.c b/drivers/usb/cdns3/cdnsp-ring.c
index 46852529499d..0758f171f73e 100644
--- a/drivers/usb/cdns3/cdnsp-ring.c
+++ b/drivers/usb/cdns3/cdnsp-ring.c
@@ -308,7 +308,8 @@ static bool cdnsp_ring_ep_doorbell(struct cdnsp_device *pdev,
writel(db_value, reg_addr);
- cdnsp_force_l0_go(pdev);
+ if (pdev->rtl_revision < RTL_REVISION_NEW_LPM)
+ cdnsp_force_l0_go(pdev);
/* Doorbell was set. */
return true;
@@ -771,7 +772,9 @@ static int cdnsp_update_port_id(struct cdnsp_device *pdev, u32 port_id)
}
if (port_id != old_port) {
- cdnsp_disable_slot(pdev);
+ if (pdev->slot_id)
+ cdnsp_disable_slot(pdev);
+
pdev->active_port = port;
cdnsp_enable_slot(pdev);
}
@@ -2482,7 +2485,8 @@ void cdnsp_queue_halt_endpoint(struct cdnsp_device *pdev, unsigned int ep_index)
{
cdnsp_queue_command(pdev, 0, 0, 0, TRB_TYPE(TRB_HALT_ENDPOINT) |
SLOT_ID_FOR_TRB(pdev->slot_id) |
- EP_ID_FOR_TRB(ep_index));
+ EP_ID_FOR_TRB(ep_index) |
+ (!ep_index ? TRB_ESP : 0));
}
void cdnsp_force_header_wakeup(struct cdnsp_device *pdev, int intf_num)
diff --git a/drivers/usb/cdns3/cdnsp-trace.h b/drivers/usb/cdns3/cdnsp-trace.h
index f2bcf77a5d0a..9b33a736c3de 100644
--- a/drivers/usb/cdns3/cdnsp-trace.h
+++ b/drivers/usb/cdns3/cdnsp-trace.h
@@ -178,11 +178,6 @@ DEFINE_EVENT(cdnsp_log_simple, cdnsp_ep0_set_config,
TP_ARGS(msg)
);
-DEFINE_EVENT(cdnsp_log_simple, cdnsp_ep0_halted,
- TP_PROTO(char *msg),
- TP_ARGS(msg)
-);
-
DEFINE_EVENT(cdnsp_log_simple, cdnsp_ep_halt,
TP_PROTO(char *msg),
TP_ARGS(msg)
@@ -399,11 +394,6 @@ DEFINE_EVENT(cdnsp_log_trb, cdnsp_cmd_timeout,
TP_ARGS(ring, trb)
);
-DEFINE_EVENT(cdnsp_log_trb, cdnsp_defered_event,
- TP_PROTO(struct cdnsp_ring *ring, struct cdnsp_generic_trb *trb),
- TP_ARGS(ring, trb)
-);
-
DECLARE_EVENT_CLASS(cdnsp_log_pdev,
TP_PROTO(struct cdnsp_device *pdev),
TP_ARGS(pdev),
@@ -433,16 +423,6 @@ DEFINE_EVENT(cdnsp_log_pdev, cdnsp_alloc_priv_device,
TP_ARGS(vdev)
);
-DEFINE_EVENT(cdnsp_log_pdev, cdnsp_free_priv_device,
- TP_PROTO(struct cdnsp_device *vdev),
- TP_ARGS(vdev)
-);
-
-DEFINE_EVENT(cdnsp_log_pdev, cdnsp_setup_device,
- TP_PROTO(struct cdnsp_device *vdev),
- TP_ARGS(vdev)
-);
-
DEFINE_EVENT(cdnsp_log_pdev, cdnsp_setup_addressable_priv_device,
TP_PROTO(struct cdnsp_device *vdev),
TP_ARGS(vdev)
@@ -575,11 +555,6 @@ DEFINE_EVENT(cdnsp_log_ep_ctx, cdnsp_handle_cmd_stop_ep,
TP_ARGS(ctx)
);
-DEFINE_EVENT(cdnsp_log_ep_ctx, cdnsp_handle_cmd_flush_ep,
- TP_PROTO(struct cdnsp_ep_ctx *ctx),
- TP_ARGS(ctx)
-);
-
DEFINE_EVENT(cdnsp_log_ep_ctx, cdnsp_handle_cmd_set_deq_ep,
TP_PROTO(struct cdnsp_ep_ctx *ctx),
TP_ARGS(ctx)
diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c
index 98980a23e1c2..f0e32227c0b7 100644
--- a/drivers/usb/cdns3/core.c
+++ b/drivers/usb/cdns3/core.c
@@ -524,11 +524,12 @@ EXPORT_SYMBOL_GPL(cdns_suspend);
int cdns_resume(struct cdns *cdns)
{
+ bool power_lost = cdns_power_is_lost(cdns);
enum usb_role real_role;
bool role_changed = false;
int ret = 0;
- if (cdns_power_is_lost(cdns)) {
+ if (power_lost) {
if (!cdns->role_sw) {
real_role = cdns_hw_role_state_machine(cdns);
if (real_role != cdns->role) {
@@ -550,8 +551,8 @@ int cdns_resume(struct cdns *cdns)
}
}
- if (cdns->roles[cdns->role]->resume)
- cdns->roles[cdns->role]->resume(cdns, cdns_power_is_lost(cdns));
+ if (!role_changed && cdns->roles[cdns->role]->resume)
+ cdns->roles[cdns->role]->resume(cdns, power_lost);
return 0;
}
diff --git a/drivers/usb/cdns3/core.h b/drivers/usb/cdns3/core.h
index 57d47348dc19..801be9e61340 100644
--- a/drivers/usb/cdns3/core.h
+++ b/drivers/usb/cdns3/core.h
@@ -30,7 +30,7 @@ struct cdns_role_driver {
int (*start)(struct cdns *cdns);
void (*stop)(struct cdns *cdns);
int (*suspend)(struct cdns *cdns, bool do_wakeup);
- int (*resume)(struct cdns *cdns, bool hibernated);
+ int (*resume)(struct cdns *cdns, bool lost_power);
const char *name;
#define CDNS_ROLE_STATE_INACTIVE 0
#define CDNS_ROLE_STATE_ACTIVE 1
@@ -79,6 +79,8 @@ struct cdns3_platform_data {
* @pdata: platform data from glue layer
* @lock: spinlock structure
* @xhci_plat_data: xhci private data structure pointer
+ * @override_apb_timeout: hold value of APB timeout. For value 0 the default
+ * value in CHICKEN_BITS_3 will be preserved.
* @gadget_init: pointer to gadget initialization function
*/
struct cdns {
@@ -117,6 +119,7 @@ struct cdns {
struct cdns3_platform_data *pdata;
spinlock_t lock;
struct xhci_plat_priv *xhci_plat_data;
+ u32 override_apb_timeout;
int (*gadget_init)(struct cdns *cdns);
};
diff --git a/drivers/usb/cdns3/host.c b/drivers/usb/cdns3/host.c
index 7ba760ee62e3..f0df114c2b53 100644
--- a/drivers/usb/cdns3/host.c
+++ b/drivers/usb/cdns3/host.c
@@ -138,6 +138,16 @@ static void cdns_host_exit(struct cdns *cdns)
cdns_drd_host_off(cdns);
}
+static int cdns_host_resume(struct cdns *cdns, bool power_lost)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(cdns->host_dev);
+ struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd);
+
+ priv->power_lost = power_lost;
+
+ return 0;
+}
+
int cdns_host_init(struct cdns *cdns)
{
struct cdns_role_driver *rdrv;
@@ -148,6 +158,7 @@ int cdns_host_init(struct cdns *cdns)
rdrv->start = __cdns_host_init;
rdrv->stop = cdns_host_exit;
+ rdrv->resume = cdns_host_resume;
rdrv->state = CDNS_ROLE_STATE_INACTIVE;
rdrv->name = "host";
diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c
index 1a7fc638213e..56d2ba824a0b 100644
--- a/drivers/usb/chipidea/ci_hdrc_imx.c
+++ b/drivers/usb/chipidea/ci_hdrc_imx.c
@@ -1,11 +1,13 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2012 Freescale Semiconductor, Inc.
+ * Copyright 2025 NXP
* Copyright (C) 2012 Marek Vasut <marex@denx.de>
* on behalf of DENX Software Engineering GmbH
*/
#include <linux/module.h>
+#include <linux/irq.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
@@ -77,6 +79,14 @@ static const struct ci_hdrc_imx_platform_flag imx8ulp_usb_data = {
CI_HDRC_HAS_PORTSC_PEC_MISSED,
};
+static const struct ci_hdrc_imx_platform_flag imx95_usb_data = {
+ .flags = CI_HDRC_SUPPORTS_RUNTIME_PM | CI_HDRC_OUT_BAND_WAKEUP,
+};
+
+static const struct ci_hdrc_imx_platform_flag s32g_usb_data = {
+ .flags = CI_HDRC_DISABLE_HOST_STREAMING,
+};
+
static const struct of_device_id ci_hdrc_imx_dt_ids[] = {
{ .compatible = "fsl,imx23-usb", .data = &imx23_usb_data},
{ .compatible = "fsl,imx28-usb", .data = &imx28_usb_data},
@@ -88,6 +98,8 @@ static const struct of_device_id ci_hdrc_imx_dt_ids[] = {
{ .compatible = "fsl,imx7d-usb", .data = &imx7d_usb_data},
{ .compatible = "fsl,imx7ulp-usb", .data = &imx7ulp_usb_data},
{ .compatible = "fsl,imx8ulp-usb", .data = &imx8ulp_usb_data},
+ { .compatible = "fsl,imx95-usb", .data = &imx95_usb_data},
+ { .compatible = "nxp,s32g2-usb", .data = &s32g_usb_data},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, ci_hdrc_imx_dt_ids);
@@ -98,6 +110,7 @@ struct ci_hdrc_imx_data {
struct clk *clk;
struct clk *clk_wakeup;
struct imx_usbmisc_data *usbmisc_data;
+ int wakeup_irq;
bool supports_runtime_pm;
bool override_phy_control;
bool in_lpm;
@@ -329,6 +342,12 @@ static int ci_hdrc_imx_notify_event(struct ci_hdrc *ci, unsigned int event)
if (ci->usb_phy)
schedule_work(&ci->usb_phy->chg_work);
break;
+ case CI_HDRC_CONTROLLER_PULLUP_EVENT:
+ if (ci->role == CI_ROLE_GADGET &&
+ ci->gadget.speed == USB_SPEED_HIGH)
+ imx_usbmisc_pullup(data->usbmisc_data,
+ ci->gadget.connected);
+ break;
default:
break;
}
@@ -336,6 +355,23 @@ static int ci_hdrc_imx_notify_event(struct ci_hdrc *ci, unsigned int event)
return ret;
}
+static irqreturn_t ci_wakeup_irq_handler(int irq, void *data)
+{
+ struct ci_hdrc_imx_data *imx_data = data;
+
+ disable_irq_nosync(irq);
+ pm_runtime_resume(&imx_data->ci_pdev->dev);
+
+ return IRQ_HANDLED;
+}
+
+static void ci_hdrc_imx_disable_regulator(void *arg)
+{
+ struct ci_hdrc_imx_data *data = arg;
+
+ regulator_disable(data->hsic_pad_regulator);
+}
+
static int ci_hdrc_imx_probe(struct platform_device *pdev)
{
struct ci_hdrc_imx_data *data;
@@ -349,6 +385,7 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
const struct ci_hdrc_imx_platform_flag *imx_platform_flag;
struct device_node *np = pdev->dev.of_node;
struct device *dev = &pdev->dev;
+ const char *irq_name;
imx_platform_flag = of_device_get_match_data(&pdev->dev);
@@ -394,6 +431,13 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
"Failed to enable HSIC pad regulator\n");
goto err_put;
}
+ ret = devm_add_action_or_reset(dev,
+ ci_hdrc_imx_disable_regulator, data);
+ if (ret) {
+ dev_err(dev,
+ "Failed to add regulator devm action\n");
+ goto err_put;
+ }
}
}
@@ -432,11 +476,11 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
ret = imx_get_clks(dev);
if (ret)
- goto disable_hsic_regulator;
+ goto qos_remove_request;
ret = imx_prepare_enable_clks(dev);
if (ret)
- goto disable_hsic_regulator;
+ goto qos_remove_request;
ret = clk_prepare_enable(data->clk_wakeup);
if (ret)
@@ -470,16 +514,36 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
of_usb_get_phy_mode(np) == USBPHY_INTERFACE_MODE_ULPI) {
pdata.flags |= CI_HDRC_OVERRIDE_PHY_CONTROL;
data->override_phy_control = true;
- usb_phy_init(pdata.usb_phy);
+ ret = usb_phy_init(pdata.usb_phy);
+ if (ret) {
+ dev_err(dev, "Failed to init phy\n");
+ goto err_clk;
+ }
}
if (pdata.flags & CI_HDRC_SUPPORTS_RUNTIME_PM)
data->supports_runtime_pm = true;
+ data->wakeup_irq = platform_get_irq_optional(pdev, 1);
+ if (data->wakeup_irq > 0) {
+ irq_name = devm_kasprintf(dev, GFP_KERNEL, "%s:wakeup", pdata.name);
+ if (!irq_name) {
+ dev_err_probe(dev, -ENOMEM, "failed to create irq_name\n");
+ goto err_clk;
+ }
+
+ ret = devm_request_threaded_irq(dev, data->wakeup_irq,
+ NULL, ci_wakeup_irq_handler,
+ IRQF_ONESHOT | IRQF_NO_AUTOEN,
+ irq_name, data);
+ if (ret)
+ goto err_clk;
+ }
+
ret = imx_usbmisc_init(data->usbmisc_data);
if (ret) {
dev_err(dev, "usbmisc init failed, ret=%d\n", ret);
- goto err_clk;
+ goto phy_shutdown;
}
data->ci_pdev = ci_hdrc_add_device(dev,
@@ -488,7 +552,7 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
if (IS_ERR(data->ci_pdev)) {
ret = PTR_ERR(data->ci_pdev);
dev_err_probe(dev, ret, "ci_hdrc_add_device failed\n");
- goto err_clk;
+ goto phy_shutdown;
}
if (data->usbmisc_data) {
@@ -522,19 +586,20 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
disable_device:
ci_hdrc_remove_device(data->ci_pdev);
+phy_shutdown:
+ if (data->override_phy_control)
+ usb_phy_shutdown(data->phy);
err_clk:
clk_disable_unprepare(data->clk_wakeup);
err_wakeup_clk:
imx_disable_unprepare_clks(dev);
-disable_hsic_regulator:
- if (data->hsic_pad_regulator)
- /* don't overwrite original ret (cf. EPROBE_DEFER) */
- regulator_disable(data->hsic_pad_regulator);
+qos_remove_request:
if (pdata.flags & CI_HDRC_PMQOS)
cpu_latency_qos_remove_request(&data->pm_qos_req);
data->ci_pdev = NULL;
err_put:
- put_device(data->usbmisc_data->dev);
+ if (data->usbmisc_data)
+ put_device(data->usbmisc_data->dev);
return ret;
}
@@ -556,10 +621,9 @@ static void ci_hdrc_imx_remove(struct platform_device *pdev)
clk_disable_unprepare(data->clk_wakeup);
if (data->plat_data->flags & CI_HDRC_PMQOS)
cpu_latency_qos_remove_request(&data->pm_qos_req);
- if (data->hsic_pad_regulator)
- regulator_disable(data->hsic_pad_regulator);
}
- put_device(data->usbmisc_data->dev);
+ if (data->usbmisc_data)
+ put_device(data->usbmisc_data->dev);
}
static void ci_hdrc_imx_shutdown(struct platform_device *pdev)
@@ -584,6 +648,10 @@ static int imx_controller_suspend(struct device *dev,
}
imx_disable_unprepare_clks(dev);
+
+ if (data->wakeup_irq > 0)
+ enable_irq(data->wakeup_irq);
+
if (data->plat_data->flags & CI_HDRC_PMQOS)
cpu_latency_qos_remove_request(&data->pm_qos_req);
@@ -608,6 +676,10 @@ static int imx_controller_resume(struct device *dev,
if (data->plat_data->flags & CI_HDRC_PMQOS)
cpu_latency_qos_add_request(&data->pm_qos_req, 0);
+ if (data->wakeup_irq > 0 &&
+ !irqd_irq_disabled(irq_get_irq_data(data->wakeup_irq)))
+ disable_irq_nosync(data->wakeup_irq);
+
ret = imx_prepare_enable_clks(dev);
if (ret)
return ret;
@@ -643,6 +715,14 @@ static int ci_hdrc_imx_suspend(struct device *dev)
return ret;
pinctrl_pm_select_sleep_state(dev);
+
+ if (data->wakeup_irq > 0 && device_may_wakeup(dev)) {
+ enable_irq_wake(data->wakeup_irq);
+
+ if (data->plat_data->flags & CI_HDRC_OUT_BAND_WAKEUP)
+ device_set_out_band_wakeup(dev);
+ }
+
return ret;
}
@@ -651,6 +731,9 @@ static int ci_hdrc_imx_resume(struct device *dev)
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
int ret;
+ if (data->wakeup_irq > 0 && device_may_wakeup(dev))
+ disable_irq_wake(data->wakeup_irq);
+
pinctrl_pm_select_default_state(dev);
ret = imx_controller_resume(dev, PMSG_RESUME);
if (!ret && data->supports_runtime_pm) {
diff --git a/drivers/usb/chipidea/ci_hdrc_imx.h b/drivers/usb/chipidea/ci_hdrc_imx.h
index 88b8da79d518..cb95c84d0322 100644
--- a/drivers/usb/chipidea/ci_hdrc_imx.h
+++ b/drivers/usb/chipidea/ci_hdrc_imx.h
@@ -37,5 +37,6 @@ int imx_usbmisc_hsic_set_connect(struct imx_usbmisc_data *data);
int imx_usbmisc_charger_detection(struct imx_usbmisc_data *data, bool connect);
int imx_usbmisc_suspend(struct imx_usbmisc_data *data, bool wakeup);
int imx_usbmisc_resume(struct imx_usbmisc_data *data, bool wakeup);
+int imx_usbmisc_pullup(struct imx_usbmisc_data *data, bool on);
#endif /* __DRIVER_USB_CHIPIDEA_CI_HDRC_IMX_H */
diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c
index 694b4a8e4e1d..7cfabb04a4fb 100644
--- a/drivers/usb/chipidea/core.c
+++ b/drivers/usb/chipidea/core.c
@@ -27,6 +27,7 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/pm_runtime.h>
+#include <linux/pm_domain.h>
#include <linux/pinctrl/consumer.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
@@ -543,30 +544,31 @@ static irqreturn_t ci_irq_handler(int irq, void *data)
if (ret == IRQ_HANDLED)
return ret;
}
- }
- /*
- * Handle id change interrupt, it indicates device/host function
- * switch.
- */
- if (ci->is_otg && (otgsc & OTGSC_IDIE) && (otgsc & OTGSC_IDIS)) {
- ci->id_event = true;
- /* Clear ID change irq status */
- hw_write_otgsc(ci, OTGSC_IDIS, OTGSC_IDIS);
- ci_otg_queue_work(ci);
- return IRQ_HANDLED;
- }
+ /*
+ * Handle id change interrupt, it indicates device/host function
+ * switch.
+ */
+ if ((otgsc & OTGSC_IDIE) && (otgsc & OTGSC_IDIS)) {
+ ci->id_event = true;
+ /* Clear ID change irq status */
+ hw_write_otgsc(ci, OTGSC_IDIS, OTGSC_IDIS);
+ }
- /*
- * Handle vbus change interrupt, it indicates device connection
- * and disconnection events.
- */
- if (ci->is_otg && (otgsc & OTGSC_BSVIE) && (otgsc & OTGSC_BSVIS)) {
- ci->b_sess_valid_event = true;
- /* Clear BSV irq */
- hw_write_otgsc(ci, OTGSC_BSVIS, OTGSC_BSVIS);
- ci_otg_queue_work(ci);
- return IRQ_HANDLED;
+ /*
+ * Handle vbus change interrupt, it indicates device connection
+ * and disconnection events.
+ */
+ if ((otgsc & OTGSC_BSVIE) && (otgsc & OTGSC_BSVIS)) {
+ ci->b_sess_valid_event = true;
+ /* Clear BSV irq */
+ hw_write_otgsc(ci, OTGSC_BSVIS, OTGSC_BSVIS);
+ }
+
+ if (ci->id_event || ci->b_sess_valid_event) {
+ ci_otg_queue_work(ci);
+ return IRQ_HANDLED;
+ }
}
/* Handle device/host interrupt */
@@ -617,28 +619,13 @@ static int ci_usb_role_switch_set(struct usb_role_switch *sw,
struct ci_hdrc *ci = usb_role_switch_get_drvdata(sw);
struct ci_hdrc_cable *cable;
- if (role == USB_ROLE_HOST) {
- cable = &ci->platdata->id_extcon;
- cable->changed = true;
- cable->connected = true;
- cable = &ci->platdata->vbus_extcon;
- cable->changed = true;
- cable->connected = false;
- } else if (role == USB_ROLE_DEVICE) {
- cable = &ci->platdata->id_extcon;
- cable->changed = true;
- cable->connected = false;
- cable = &ci->platdata->vbus_extcon;
- cable->changed = true;
- cable->connected = true;
- } else {
- cable = &ci->platdata->id_extcon;
- cable->changed = true;
- cable->connected = false;
- cable = &ci->platdata->vbus_extcon;
- cable->changed = true;
- cable->connected = false;
- }
+ cable = &ci->platdata->id_extcon;
+ cable->changed = true;
+ cable->connected = (role == USB_ROLE_HOST);
+
+ cable = &ci->platdata->vbus_extcon;
+ cable->changed = true;
+ cable->connected = (role == USB_ROLE_DEVICE);
ci_irq(ci);
return 0;
@@ -915,6 +902,8 @@ struct platform_device *ci_hdrc_add_device(struct device *dev,
if (ret)
goto err;
+ dev_pm_domain_detach(&pdev->dev, false);
+
return pdev;
err:
@@ -1372,7 +1361,6 @@ static int ci_controller_resume(struct device *dev)
ci->in_lpm = false;
if (ci->wakeup_int) {
ci->wakeup_int = false;
- pm_runtime_mark_last_busy(ci->dev);
pm_runtime_put_autosuspend(ci->dev);
enable_irq(ci->irq);
if (ci_otg_is_fsm_mode(ci))
diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c
index 647e98f4e351..fecc7d7e2f0d 100644
--- a/drivers/usb/chipidea/otg.c
+++ b/drivers/usb/chipidea/otg.c
@@ -130,6 +130,9 @@ enum ci_role ci_otg_role(struct ci_hdrc *ci)
void ci_handle_vbus_change(struct ci_hdrc *ci)
{
+ if (ci->role != CI_ROLE_GADGET)
+ return;
+
if (!ci->is_otg) {
if (ci->platdata->flags & CI_HDRC_FORCE_VBUS_ACTIVE_ALWAYS)
usb_gadget_vbus_connect(&ci->gadget);
@@ -187,8 +190,8 @@ void ci_handle_id_switch(struct ci_hdrc *ci)
ci_role_stop(ci);
- if (role == CI_ROLE_GADGET &&
- IS_ERR(ci->platdata->vbus_extcon.edev))
+ if (role == CI_ROLE_GADGET && !ci->role_switch &&
+ IS_ERR(ci->platdata->vbus_extcon.edev))
/*
* Wait vbus lower than OTGSC_BSV before connecting
* to host. If connecting status is from an external
diff --git a/drivers/usb/chipidea/otg_fsm.c b/drivers/usb/chipidea/otg_fsm.c
index c17516c29b63..929536dc96ec 100644
--- a/drivers/usb/chipidea/otg_fsm.c
+++ b/drivers/usb/chipidea/otg_fsm.c
@@ -424,8 +424,7 @@ static enum hrtimer_restart ci_otg_hrtimer_func(struct hrtimer *t)
/* Initialize timers */
static int ci_otg_init_timers(struct ci_hdrc *ci)
{
- hrtimer_init(&ci->otg_fsm_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
- ci->otg_fsm_hrtimer.function = ci_otg_hrtimer_func;
+ hrtimer_setup(&ci->otg_fsm_hrtimer, ci_otg_hrtimer_func, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
return 0;
}
@@ -630,7 +629,6 @@ int ci_otg_fsm_work(struct ci_hdrc *ci)
ci_otg_queue_work(ci);
}
} else if (ci->fsm.otg->state == OTG_STATE_A_HOST) {
- pm_runtime_mark_last_busy(ci->dev);
pm_runtime_put_autosuspend(ci->dev);
return 0;
}
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
index 8a9b31fd5c89..f2de86d0ce40 100644
--- a/drivers/usb/chipidea/udc.c
+++ b/drivers/usb/chipidea/udc.c
@@ -360,8 +360,8 @@ static int add_td_to_list(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq,
{
int i;
u32 temp;
- struct td_node *lastnode, *node = kzalloc(sizeof(struct td_node),
- GFP_ATOMIC);
+ struct td_node *lastnode, *node = kzalloc_obj(struct td_node,
+ GFP_ATOMIC);
if (node == NULL)
return -ENOMEM;
@@ -931,6 +931,13 @@ __acquires(hwep->lock)
list_del_init(&hwreq->queue);
hwreq->req.status = -ESHUTDOWN;
+ /* Unmap DMA and clean up bounce buffers before giving back */
+ usb_gadget_unmap_request_by_dev(hwep->ci->dev->parent,
+ &hwreq->req, hwep->dir);
+
+ if (hwreq->sgt.sgl)
+ sglist_do_debounce(hwreq, false);
+
if (hwreq->req.complete != NULL) {
spin_unlock(hwep->lock);
usb_gadget_giveback_request(&hwep->ep, &hwreq->req);
@@ -1638,7 +1645,7 @@ static struct usb_request *ep_alloc_request(struct usb_ep *ep, gfp_t gfp_flags)
if (ep == NULL)
return NULL;
- hwreq = kzalloc(sizeof(struct ci_hw_req), gfp_flags);
+ hwreq = kzalloc_obj(struct ci_hw_req, gfp_flags);
if (hwreq != NULL) {
INIT_LIST_HEAD(&hwreq->queue);
INIT_LIST_HEAD(&hwreq->tds);
@@ -1970,6 +1977,11 @@ static int ci_udc_pullup(struct usb_gadget *_gadget, int is_on)
hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS);
else
hw_write(ci, OP_USBCMD, USBCMD_RS, 0);
+
+ if (ci->platdata->notify_event) {
+ _gadget->connected = is_on;
+ ci->platdata->notify_event(ci, CI_HDRC_CONTROLLER_PULLUP_EVENT);
+ }
pm_runtime_put_sync(ci->dev);
return 0;
@@ -2374,6 +2386,10 @@ static void udc_suspend(struct ci_hdrc *ci)
*/
if (hw_read(ci, OP_ENDPTLISTADDR, ~0) == 0)
hw_write(ci, OP_ENDPTLISTADDR, ~0, ~0);
+
+ if (ci->gadget.connected &&
+ (!ci->suspended || !device_may_wakeup(ci->dev)))
+ usb_gadget_disconnect(&ci->gadget);
}
static void udc_resume(struct ci_hdrc *ci, bool power_lost)
@@ -2384,6 +2400,9 @@ static void udc_resume(struct ci_hdrc *ci, bool power_lost)
OTGSC_BSVIS | OTGSC_BSVIE);
if (ci->vbus_active)
usb_gadget_vbus_disconnect(&ci->gadget);
+ } else if (ci->vbus_active && ci->driver &&
+ !ci->gadget.connected) {
+ usb_gadget_connect(&ci->gadget);
}
/* Restore value 0 if it was set for power lost check */
diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c
index 1394881fde5f..bb027d2bd700 100644
--- a/drivers/usb/chipidea/usbmisc_imx.c
+++ b/drivers/usb/chipidea/usbmisc_imx.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2012 Freescale Semiconductor, Inc.
+ * Copyright 2025 NXP
*/
#include <linux/module.h>
@@ -139,6 +140,34 @@
#define MX6_USB_OTG_WAKEUP_BITS (MX6_BM_WAKEUP_ENABLE | MX6_BM_VBUS_WAKEUP | \
MX6_BM_ID_WAKEUP | MX6SX_BM_DPDM_WAKEUP_EN)
+/*
+ * HSIO Block Control Register
+ */
+
+#define BLKCTL_USB_WAKEUP_CTRL 0x0
+#define BLKCTL_OTG_WAKE_ENABLE BIT(31)
+#define BLKCTL_OTG_VBUS_SESSVALID BIT(4)
+#define BLKCTL_OTG_ID_WAKEUP_EN BIT(2)
+#define BLKCTL_OTG_VBUS_WAKEUP_EN BIT(1)
+#define BLKCTL_OTG_DPDM_WAKEUP_EN BIT(0)
+
+#define BLKCTL_WAKEUP_SOURCE (BLKCTL_OTG_WAKE_ENABLE | \
+ BLKCTL_OTG_ID_WAKEUP_EN | \
+ BLKCTL_OTG_VBUS_WAKEUP_EN | \
+ BLKCTL_OTG_DPDM_WAKEUP_EN)
+
+#define S32G_WAKEUP_IE BIT(0)
+#define S32G_CORE_IE BIT(1)
+#define S32G_PWRFLTEN BIT(7)
+#define S32G_WAKEUPCTRL BIT(10)
+#define S32G_WAKEUPEN BIT(11)
+
+/* Workaround errata ERR050474 (handle packages that aren't 4 byte aligned) */
+#define S32G_UCMALLBE BIT(15)
+
+#define S32G_WAKEUP_BITS (S32G_WAKEUP_IE | S32G_CORE_IE | S32G_WAKEUPEN | \
+ S32G_WAKEUPCTRL)
+
struct usbmisc_ops {
/* It's called once when probe a usb device */
int (*init)(struct imx_usbmisc_data *data);
@@ -154,11 +183,15 @@ struct usbmisc_ops {
int (*charger_detection)(struct imx_usbmisc_data *data);
/* It's called when system resume from usb power lost */
int (*power_lost_check)(struct imx_usbmisc_data *data);
+ /* It's called when device controller changed pullup status */
+ void (*pullup)(struct imx_usbmisc_data *data, bool on);
+ /* It's called during suspend/resume to save power */
void (*vbus_comparator_on)(struct imx_usbmisc_data *data, bool on);
};
struct imx_usbmisc {
void __iomem *base;
+ void __iomem *blkctl;
spinlock_t lock;
const struct usbmisc_ops *ops;
};
@@ -440,7 +473,7 @@ static int usbmisc_imx6q_init(struct imx_usbmisc_data *data)
else if (data->oc_pol_configured)
reg &= ~MX6_BM_OVER_CUR_POLARITY;
}
- /* If the polarity is not set keep it as setup by the bootlader */
+ /* If the polarity is not set keep it as setup by the bootloader */
if (data->pwr_pol == 1)
reg |= MX6_BM_PWR_POLARITY;
writel(reg, usbmisc->base + data->index * 4);
@@ -597,6 +630,57 @@ static int usbmisc_vf610_init(struct imx_usbmisc_data *data)
return 0;
}
+static int usbmisc_s32g_set_wakeup(struct imx_usbmisc_data *data, bool enabled)
+{
+ struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
+ unsigned long flags;
+ u32 reg;
+
+ spin_lock_irqsave(&usbmisc->lock, flags);
+
+ reg = readl(usbmisc->base);
+ if (enabled)
+ reg |= S32G_WAKEUP_BITS;
+ else
+ reg &= ~S32G_WAKEUP_BITS;
+
+ writel(reg, usbmisc->base);
+ spin_unlock_irqrestore(&usbmisc->lock, flags);
+
+ return 0;
+}
+
+static int usbmisc_s32g_init(struct imx_usbmisc_data *data, u32 extra_flags)
+{
+ struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
+ unsigned long flags;
+ u32 reg;
+
+ spin_lock_irqsave(&usbmisc->lock, flags);
+
+ reg = readl(usbmisc->base);
+
+ reg |= S32G_PWRFLTEN;
+ reg |= extra_flags;
+
+ writel(reg, usbmisc->base);
+
+ spin_unlock_irqrestore(&usbmisc->lock, flags);
+ usbmisc_s32g_set_wakeup(data, false);
+
+ return 0;
+}
+
+static int usbmisc_s32g2_init(struct imx_usbmisc_data *data)
+{
+ return usbmisc_s32g_init(data, S32G_UCMALLBE);
+}
+
+static int usbmisc_s32g3_init(struct imx_usbmisc_data *data)
+{
+ return usbmisc_s32g_init(data, 0);
+}
+
static int usbmisc_imx7d_set_wakeup
(struct imx_usbmisc_data *data, bool enabled)
{
@@ -645,7 +729,7 @@ static int usbmisc_imx7d_init(struct imx_usbmisc_data *data)
else if (data->oc_pol_configured)
reg &= ~MX6_BM_OVER_CUR_POLARITY;
}
- /* If the polarity is not set keep it as setup by the bootlader */
+ /* If the polarity is not set keep it as setup by the bootloader */
if (data->pwr_pol == 1)
reg |= MX6_BM_PWR_POLARITY;
writel(reg, usbmisc->base);
@@ -939,7 +1023,7 @@ static int usbmisc_imx7ulp_init(struct imx_usbmisc_data *data)
else if (data->oc_pol_configured)
reg &= ~MX6_BM_OVER_CUR_POLARITY;
}
- /* If the polarity is not set keep it as setup by the bootlader */
+ /* If the polarity is not set keep it as setup by the bootloader */
if (data->pwr_pol == 1)
reg |= MX6_BM_PWR_POLARITY;
@@ -978,6 +1062,34 @@ static int usbmisc_imx7ulp_init(struct imx_usbmisc_data *data)
return 0;
}
+static void usbmisc_imx7d_pullup(struct imx_usbmisc_data *data, bool on)
+{
+ struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
+ unsigned long flags;
+ u32 val;
+
+ if (on)
+ return;
+
+ spin_lock_irqsave(&usbmisc->lock, flags);
+ val = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2);
+ val &= ~MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_MASK;
+ val |= MX7D_USBNC_USB_CTRL2_OPMODE(1);
+ val |= MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_EN;
+ writel(val, usbmisc->base + MX7D_USBNC_USB_CTRL2);
+ spin_unlock_irqrestore(&usbmisc->lock, flags);
+
+ /* Last for at least 1 micro-frame to let host see disconnect signal */
+ usleep_range(125, 150);
+
+ spin_lock_irqsave(&usbmisc->lock, flags);
+ val &= ~MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_MASK;
+ val |= MX7D_USBNC_USB_CTRL2_OPMODE(0);
+ val &= ~MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_EN;
+ writel(val, usbmisc->base + MX7D_USBNC_USB_CTRL2);
+ spin_unlock_irqrestore(&usbmisc->lock, flags);
+}
+
static int usbmisc_imx7d_power_lost_check(struct imx_usbmisc_data *data)
{
struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
@@ -1016,6 +1128,49 @@ static int usbmisc_imx6sx_power_lost_check(struct imx_usbmisc_data *data)
return 0;
}
+static int usbmisc_s32g_power_lost_check(struct imx_usbmisc_data *data)
+{
+ return 1;
+}
+
+static u32 usbmisc_blkctl_wakeup_setting(struct imx_usbmisc_data *data)
+{
+ u32 wakeup_setting = BLKCTL_WAKEUP_SOURCE;
+
+ if (data->ext_id || data->available_role != USB_DR_MODE_OTG)
+ wakeup_setting &= ~BLKCTL_OTG_ID_WAKEUP_EN;
+
+ if (data->ext_vbus || data->available_role == USB_DR_MODE_HOST)
+ wakeup_setting &= ~BLKCTL_OTG_VBUS_WAKEUP_EN;
+
+ /* Select session valid as VBUS wakeup source */
+ wakeup_setting |= BLKCTL_OTG_VBUS_SESSVALID;
+
+ return wakeup_setting;
+}
+
+static int usbmisc_imx95_set_wakeup(struct imx_usbmisc_data *data, bool enabled)
+{
+ struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
+ unsigned long flags;
+ u32 val;
+
+ if (!usbmisc->blkctl)
+ return 0;
+
+ spin_lock_irqsave(&usbmisc->lock, flags);
+ val = readl(usbmisc->blkctl + BLKCTL_USB_WAKEUP_CTRL);
+ val &= ~BLKCTL_WAKEUP_SOURCE;
+
+ if (enabled)
+ val |= usbmisc_blkctl_wakeup_setting(data);
+
+ writel(val, usbmisc->blkctl + BLKCTL_USB_WAKEUP_CTRL);
+ spin_unlock_irqrestore(&usbmisc->lock, flags);
+
+ return 0;
+}
+
static const struct usbmisc_ops imx25_usbmisc_ops = {
.init = usbmisc_imx25_init,
.post = usbmisc_imx25_post,
@@ -1057,6 +1212,7 @@ static const struct usbmisc_ops imx7d_usbmisc_ops = {
.set_wakeup = usbmisc_imx7d_set_wakeup,
.charger_detection = imx7d_charger_detection,
.power_lost_check = usbmisc_imx7d_power_lost_check,
+ .pullup = usbmisc_imx7d_pullup,
.vbus_comparator_on = usbmisc_imx7d_vbus_comparator_on,
};
@@ -1068,6 +1224,35 @@ static const struct usbmisc_ops imx7ulp_usbmisc_ops = {
.power_lost_check = usbmisc_imx7d_power_lost_check,
};
+static const struct usbmisc_ops imx94_usbmisc_ops = {
+ .init = usbmisc_imx7d_init,
+ .set_wakeup = usbmisc_imx95_set_wakeup,
+ .charger_detection = imx7d_charger_detection,
+ .power_lost_check = usbmisc_imx7d_power_lost_check,
+ .vbus_comparator_on = usbmisc_imx7d_vbus_comparator_on,
+};
+
+static const struct usbmisc_ops imx95_usbmisc_ops = {
+ .init = usbmisc_imx7d_init,
+ .set_wakeup = usbmisc_imx95_set_wakeup,
+ .charger_detection = imx7d_charger_detection,
+ .power_lost_check = usbmisc_imx7d_power_lost_check,
+ .pullup = usbmisc_imx7d_pullup,
+ .vbus_comparator_on = usbmisc_imx7d_vbus_comparator_on,
+};
+
+static const struct usbmisc_ops s32g2_usbmisc_ops = {
+ .init = usbmisc_s32g2_init,
+ .set_wakeup = usbmisc_s32g_set_wakeup,
+ .power_lost_check = usbmisc_s32g_power_lost_check,
+};
+
+static const struct usbmisc_ops s32g3_usbmisc_ops = {
+ .init = usbmisc_s32g3_init,
+ .set_wakeup = usbmisc_s32g_set_wakeup,
+ .power_lost_check = usbmisc_s32g_power_lost_check,
+};
+
static inline bool is_imx53_usbmisc(struct imx_usbmisc_data *data)
{
struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
@@ -1162,6 +1347,21 @@ int imx_usbmisc_charger_detection(struct imx_usbmisc_data *data, bool connect)
}
EXPORT_SYMBOL_GPL(imx_usbmisc_charger_detection);
+int imx_usbmisc_pullup(struct imx_usbmisc_data *data, bool on)
+{
+ struct imx_usbmisc *usbmisc;
+
+ if (!data)
+ return 0;
+
+ usbmisc = dev_get_drvdata(data->dev);
+ if (usbmisc->ops->pullup)
+ usbmisc->ops->pullup(data, on);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(imx_usbmisc_pullup);
+
int imx_usbmisc_suspend(struct imx_usbmisc_data *data, bool wakeup)
{
struct imx_usbmisc *usbmisc;
@@ -1185,7 +1385,7 @@ int imx_usbmisc_suspend(struct imx_usbmisc_data *data, bool wakeup)
if (usbmisc->ops->hsic_set_clk && data->hsic)
ret = usbmisc->ops->hsic_set_clk(data, false);
if (ret) {
- dev_err(data->dev, "set_wakeup failed, ret=%d\n", ret);
+ dev_err(data->dev, "hsic_set_clk failed, ret=%d\n", ret);
return ret;
}
@@ -1224,7 +1424,7 @@ int imx_usbmisc_resume(struct imx_usbmisc_data *data, bool wakeup)
if (usbmisc->ops->hsic_set_clk && data->hsic)
ret = usbmisc->ops->hsic_set_clk(data, true);
if (ret) {
- dev_err(data->dev, "set_wakeup failed, ret=%d\n", ret);
+ dev_err(data->dev, "hsic_set_clk failed, ret=%d\n", ret);
goto hsic_set_clk_fail;
}
@@ -1289,6 +1489,22 @@ static const struct of_device_id usbmisc_imx_dt_ids[] = {
.compatible = "fsl,imx8ulp-usbmisc",
.data = &imx7ulp_usbmisc_ops,
},
+ {
+ .compatible = "fsl,imx94-usbmisc",
+ .data = &imx94_usbmisc_ops,
+ },
+ {
+ .compatible = "fsl,imx95-usbmisc",
+ .data = &imx95_usbmisc_ops,
+ },
+ {
+ .compatible = "nxp,s32g2-usbmisc",
+ .data = &s32g2_usbmisc_ops,
+ },
+ {
+ .compatible = "nxp,s32g3-usbmisc",
+ .data = &s32g3_usbmisc_ops,
+ },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, usbmisc_imx_dt_ids);
@@ -1296,6 +1512,7 @@ MODULE_DEVICE_TABLE(of, usbmisc_imx_dt_ids);
static int usbmisc_imx_probe(struct platform_device *pdev)
{
struct imx_usbmisc *data;
+ struct resource *res;
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
if (!data)
@@ -1307,6 +1524,15 @@ static int usbmisc_imx_probe(struct platform_device *pdev)
if (IS_ERR(data->base))
return PTR_ERR(data->base);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (res) {
+ data->blkctl = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(data->blkctl))
+ return PTR_ERR(data->blkctl);
+ } else if (device_is_compatible(&pdev->dev, "fsl,imx95-usbmisc")) {
+ dev_warn(&pdev->dev, "wakeup setting is missing\n");
+ }
+
data->ops = of_device_get_match_data(&pdev->dev);
platform_set_drvdata(pdev, data);
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index c2ecfa3c8349..54059e4fc6ed 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -114,6 +114,8 @@ static int acm_ctrl_msg(struct acm *acm, int request, int value,
int retval;
retval = usb_autopm_get_interface(acm->control);
+#define VENDOR_CLASS_DATA_IFACE BIT(9) /* data interface uses vendor-specific class */
+#define ALWAYS_POLL_CTRL BIT(10) /* keep ctrl URB active even without an open TTY */
if (retval)
return retval;
@@ -710,12 +712,14 @@ static int acm_port_activate(struct tty_port *port, struct tty_struct *tty)
set_bit(TTY_NO_WRITE_SPLIT, &tty->flags);
acm->control->needs_remote_wakeup = 1;
- acm->ctrlurb->dev = acm->dev;
- retval = usb_submit_urb(acm->ctrlurb, GFP_KERNEL);
- if (retval) {
- dev_err(&acm->control->dev,
- "%s - usb_submit_urb(ctrl irq) failed\n", __func__);
- goto error_submit_urb;
+ if (!(acm->quirks & ALWAYS_POLL_CTRL)) {
+ acm->ctrlurb->dev = acm->dev;
+ retval = usb_submit_urb(acm->ctrlurb, GFP_KERNEL);
+ if (retval) {
+ dev_err(&acm->control->dev,
+ "%s - usb_submit_urb(ctrl irq) failed\n", __func__);
+ goto error_submit_urb;
+ }
}
acm_tty_set_termios(tty, NULL);
@@ -788,6 +792,14 @@ static void acm_port_shutdown(struct tty_port *port)
acm_unpoison_urbs(acm);
+ if (acm->quirks & ALWAYS_POLL_CTRL) {
+ acm->ctrlurb->dev = acm->dev;
+ if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL))
+ dev_dbg(&acm->control->dev,
+ "ctrl polling restart failed after port close\n");
+ /* port_shutdown() cleared DTR/RTS; restore them */
+ acm_set_control(acm, USB_CDC_CTRL_DTR | USB_CDC_CTRL_RTS);
+ }
}
static void acm_tty_cleanup(struct tty_struct *tty)
@@ -1225,6 +1237,12 @@ static int acm_probe(struct usb_interface *intf,
if (!data_interface || !control_interface)
return -ENODEV;
goto skip_normal_probe;
+ } else if (quirks == NO_UNION_12) {
+ data_interface = usb_ifnum_to_if(usb_dev, 2);
+ control_interface = usb_ifnum_to_if(usb_dev, 1);
+ if (!data_interface || !control_interface)
+ return -ENODEV;
+ goto skip_normal_probe;
}
/* normal probing*/
@@ -1322,6 +1340,9 @@ skip_normal_probe:
dev_dbg(&intf->dev,
"Your device has switched interfaces.\n");
swap(control_interface, data_interface);
+ } else if (quirks & VENDOR_CLASS_DATA_IFACE) {
+ dev_dbg(&intf->dev,
+ "Vendor-specific data interface class, continuing.\n");
} else {
return -EINVAL;
}
@@ -1350,7 +1371,7 @@ skip_normal_probe:
made_compressed_probe:
dev_dbg(&intf->dev, "interfaces are valid\n");
- acm = kzalloc(sizeof(struct acm), GFP_KERNEL);
+ acm = kzalloc_obj(struct acm);
if (!acm)
return -ENOMEM;
@@ -1379,6 +1400,8 @@ made_compressed_probe:
acm->ctrl_caps = h.usb_cdc_acm_descriptor->bmCapabilities;
if (quirks & NO_CAP_LINE)
acm->ctrl_caps &= ~USB_CDC_CAP_LINE;
+ if (quirks & MISSING_CAP_BRK)
+ acm->ctrl_caps |= USB_CDC_CAP_BRK;
acm->ctrlsize = ctrlsize;
acm->readsize = readsize;
acm->rx_buflimit = num_rx_buf;
@@ -1475,7 +1498,7 @@ made_compressed_probe:
if (!acm->country_codes)
goto skip_countries;
acm->country_code_size = cfd->bLength - 4;
- memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0,
+ memcpy(acm->country_codes, cfd->wCountryCodes,
cfd->bLength - 4);
acm->country_rel_date = cfd->iCountryCodeRelDate;
@@ -1514,12 +1537,21 @@ skip_countries:
acm->line.bDataBits = 8;
acm_set_line(acm, &acm->line);
+ if (quirks & ALWAYS_POLL_CTRL)
+ acm_set_control(acm, USB_CDC_CTRL_DTR | USB_CDC_CTRL_RTS);
+
if (!acm->combined_interfaces) {
rv = usb_driver_claim_interface(&acm_driver, data_interface, acm);
if (rv)
goto err_remove_files;
}
+ if (quirks & CLEAR_HALT_CONDITIONS) {
+ /* errors intentionally ignored */
+ usb_clear_halt(usb_dev, acm->in);
+ usb_clear_halt(usb_dev, acm->out);
+ }
+
tty_dev = tty_port_register_device(&acm->port, acm_tty_driver, minor,
&control_interface->dev);
if (IS_ERR(tty_dev)) {
@@ -1527,13 +1559,15 @@ skip_countries:
goto err_release_data_interface;
}
- if (quirks & CLEAR_HALT_CONDITIONS) {
- usb_clear_halt(usb_dev, acm->in);
- usb_clear_halt(usb_dev, acm->out);
- }
-
dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor);
+ if (acm->quirks & ALWAYS_POLL_CTRL) {
+ acm->ctrlurb->dev = acm->dev;
+ if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL))
+ dev_warn(&intf->dev,
+ "failed to start persistent ctrl polling\n");
+ }
+
return 0;
err_release_data_interface:
@@ -1571,7 +1605,6 @@ err_put_port:
static void acm_disconnect(struct usb_interface *intf)
{
struct acm *acm = usb_get_intfdata(intf);
- struct tty_struct *tty;
int i;
/* sibling interface is already cleaning up */
@@ -1598,11 +1631,7 @@ static void acm_disconnect(struct usb_interface *intf)
usb_set_intfdata(acm->data, NULL);
mutex_unlock(&acm->mutex);
- tty = tty_port_tty_get(&acm->port);
- if (tty) {
- tty_vhangup(tty);
- tty_kref_put(tty);
- }
+ tty_port_tty_vhangup(&acm->port);
cancel_delayed_work_sync(&acm->dwork);
@@ -1665,7 +1694,7 @@ static int acm_resume(struct usb_interface *intf)
acm_unpoison_urbs(acm);
- if (tty_port_initialized(&acm->port)) {
+ if (tty_port_initialized(&acm->port) || (acm->quirks & ALWAYS_POLL_CTRL)) {
rv = usb_submit_urb(acm->ctrlurb, GFP_ATOMIC);
for (;;) {
@@ -1750,6 +1779,9 @@ static const struct usb_device_id acm_ids[] = {
{ USB_DEVICE(0x045b, 0x024D), /* Renesas R-Car E3 USB Download mode */
.driver_info = DISABLE_ECHO, /* Don't echo banner */
},
+ { USB_DEVICE(0x04b8, 0x0d12), /* EPSON HMD Com&Sens */
+ .driver_info = NO_UNION_12, /* union descriptor is garbage */
+ },
{ USB_DEVICE(0x0e8d, 0x0003), /* FIREFLY, MediaTek Inc; andrey.arapov@gmail.com */
.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
},
@@ -2006,6 +2038,23 @@ static const struct usb_device_id acm_ids[] = {
.driver_info = IGNORE_DEVICE,
},
+ /* CH343 supports CAP_BRK, but doesn't advertise it */
+ { USB_DEVICE(0x1a86, 0x55d3), .driver_info = MISSING_CAP_BRK, },
+
+ /*
+ * Lenovo Yoga Book 9 14IAH10 (83KJ) — INGENIC 17EF:6161 touchscreen
+ * composite device. The CDC ACM control interface (0) uses a standard
+ * Union descriptor, but the data interface (1) is declared as vendor-
+ * specific class (0xff) with no CDC data descriptors, so cdc-acm would
+ * normally reject it. The firmware also requires continuous polling of
+ * the notification endpoint (EP 0x82) to suppress a 20-second watchdog
+ * reset; ALWAYS_POLL_CTRL keeps the ctrlurb active even when no TTY is
+ * open. Match only the control interface by class to avoid probing the
+ * vendor-specific data interface.
+ */
+ { USB_DEVICE_INTERFACE_CLASS(0x17ef, 0x6161, USB_CLASS_COMM),
+ .driver_info = VENDOR_CLASS_DATA_IFACE | ALWAYS_POLL_CTRL },
+
/* control interfaces without any protocol set */
{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
USB_CDC_PROTO_NONE) },
diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h
index 759ac15631d3..25fd5329a878 100644
--- a/drivers/usb/class/cdc-acm.h
+++ b/drivers/usb/class/cdc-acm.h
@@ -113,3 +113,5 @@ struct acm {
#define CLEAR_HALT_CONDITIONS BIT(5)
#define SEND_ZERO_PACKET BIT(6)
#define DISABLE_ECHO BIT(7)
+#define MISSING_CAP_BRK BIT(8)
+#define NO_UNION_12 BIT(9)
diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c
index 86ee39db013f..7556c0dac908 100644
--- a/drivers/usb/class/cdc-wdm.c
+++ b/drivers/usb/class/cdc-wdm.c
@@ -92,7 +92,6 @@ struct wdm_device {
u16 wMaxCommand;
u16 wMaxPacketSize;
__le16 inum;
- int reslength;
int length;
int read;
int count;
@@ -214,6 +213,11 @@ static void wdm_in_callback(struct urb *urb)
if (desc->rerr == 0 && status != -EPIPE)
desc->rerr = status;
+ if (length == 0) {
+ dev_dbg(&desc->intf->dev, "received ZLP\n");
+ goto skip_zlp;
+ }
+
if (length + desc->length > desc->wMaxCommand) {
/* The buffer would overflow */
set_bit(WDM_OVERFLOW, &desc->flags);
@@ -221,19 +225,20 @@ static void wdm_in_callback(struct urb *urb)
/* we may already be in overflow */
if (!test_bit(WDM_OVERFLOW, &desc->flags)) {
memmove(desc->ubuf + desc->length, desc->inbuf, length);
- desc->length += length;
- desc->reslength = length;
+ smp_wmb(); /* against wdm_read() */
+ WRITE_ONCE(desc->length, desc->length + length);
}
}
skip_error:
if (desc->rerr) {
/*
- * Since there was an error, userspace may decide to not read
- * any data after poll'ing.
+ * If there was a ZLP or an error, userspace may decide to not
+ * read any data after poll'ing.
* We should respond to further attempts from the device to send
* data, so that we can get unstuck.
*/
+skip_zlp:
schedule_work(&desc->service_outs_intr);
} else {
set_bit(WDM_READ, &desc->flags);
@@ -529,6 +534,7 @@ static ssize_t wdm_read
return -ERESTARTSYS;
cntr = READ_ONCE(desc->length);
+ smp_rmb(); /* against wdm_in_callback() */
if (cntr == 0) {
desc->read = 0;
retry:
@@ -585,15 +591,6 @@ retry:
goto retry;
}
- if (!desc->reslength) { /* zero length read */
- dev_dbg(&desc->intf->dev, "zero length - clearing WDM_READ\n");
- clear_bit(WDM_READ, &desc->flags);
- rv = service_outstanding_interrupt(desc);
- spin_unlock_irq(&desc->iuspin);
- if (rv < 0)
- goto err;
- goto retry;
- }
cntr = desc->length;
spin_unlock_irq(&desc->iuspin);
}
@@ -726,7 +723,7 @@ static int wdm_open(struct inode *inode, struct file *file)
rv = -EBUSY;
goto out;
}
-
+ smp_rmb(); /* ordered against wdm_wwan_port_stop() */
rv = usb_autopm_get_interface(desc->intf);
if (rv < 0) {
dev_err(&desc->intf->dev, "Error autopm - %d\n", rv);
@@ -829,6 +826,7 @@ static struct usb_class_driver wdm_class = {
static int wdm_wwan_port_start(struct wwan_port *port)
{
struct wdm_device *desc = wwan_port_get_drvdata(port);
+ int rv;
/* The interface is both exposed via the WWAN framework and as a
* legacy usbmisc chardev. If chardev is already open, just fail
@@ -848,7 +846,15 @@ static int wdm_wwan_port_start(struct wwan_port *port)
wwan_port_txon(port);
/* Start getting events */
- return usb_submit_urb(desc->validity, GFP_KERNEL);
+ rv = usb_submit_urb(desc->validity, GFP_KERNEL);
+ if (rv < 0) {
+ wwan_port_txoff(port);
+ desc->manage_power(desc->intf, 0);
+ /* this must be last lest we race with chardev open */
+ clear_bit(WDM_WWAN_IN_USE, &desc->flags);
+ }
+
+ return rv;
}
static void wdm_wwan_port_stop(struct wwan_port *port)
@@ -859,8 +865,10 @@ static void wdm_wwan_port_stop(struct wwan_port *port)
poison_urbs(desc);
desc->manage_power(desc->intf, 0);
clear_bit(WDM_READ, &desc->flags);
- clear_bit(WDM_WWAN_IN_USE, &desc->flags);
unpoison_urbs(desc);
+ smp_wmb(); /* ordered against wdm_open() */
+ /* this must be last lest we open a poisoned device */
+ clear_bit(WDM_WWAN_IN_USE, &desc->flags);
}
static void wdm_wwan_port_tx_complete(struct urb *urb)
@@ -868,7 +876,7 @@ static void wdm_wwan_port_tx_complete(struct urb *urb)
struct sk_buff *skb = urb->context;
struct wdm_device *desc = skb_shinfo(skb)->destructor_arg;
- usb_autopm_put_interface(desc->intf);
+ usb_autopm_put_interface_async(desc->intf);
wwan_port_txon(desc->wwanp);
kfree_skb(skb);
}
@@ -898,7 +906,7 @@ static int wdm_wwan_port_tx(struct wwan_port *port, struct sk_buff *skb)
req->bRequestType = (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE);
req->bRequest = USB_CDC_SEND_ENCAPSULATED_COMMAND;
req->wValue = 0;
- req->wIndex = desc->inum;
+ req->wIndex = desc->inum; /* already converted */
req->wLength = cpu_to_le16(skb->len);
skb_shinfo(skb)->destructor_arg = desc;
@@ -1005,7 +1013,7 @@ static void service_interrupt_work(struct work_struct *work)
spin_lock_irq(&desc->iuspin);
service_outstanding_interrupt(desc);
- if (!desc->resp_count) {
+ if (!desc->resp_count && (desc->length || desc->rerr)) {
set_bit(WDM_READ, &desc->flags);
wake_up(&desc->wait);
}
@@ -1021,7 +1029,7 @@ static int wdm_create(struct usb_interface *intf, struct usb_endpoint_descriptor
int rv = -ENOMEM;
struct wdm_device *desc;
- desc = kzalloc(sizeof(struct wdm_device), GFP_KERNEL);
+ desc = kzalloc_obj(struct wdm_device);
if (!desc)
goto out;
INIT_LIST_HEAD(&desc->device_list);
@@ -1044,10 +1052,10 @@ static int wdm_create(struct usb_interface *intf, struct usb_endpoint_descriptor
desc->wMaxPacketSize = usb_endpoint_maxp(ep);
- desc->orq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
+ desc->orq = kmalloc_obj(struct usb_ctrlrequest);
if (!desc->orq)
goto err;
- desc->irq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
+ desc->irq = kmalloc_obj(struct usb_ctrlrequest);
if (!desc->irq)
goto err;
diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c
index e2527faa6592..746414763da5 100644
--- a/drivers/usb/class/usblp.c
+++ b/drivers/usb/class/usblp.c
@@ -34,6 +34,7 @@
#include <linux/module.h>
#include <linux/kernel.h>
+#include <linux/minmax.h>
#include <linux/sched/signal.h>
#include <linux/signal.h>
#include <linux/poll.h>
@@ -366,7 +367,8 @@ static int usblp_check_status(struct usblp *usblp, int err)
int error;
mutex_lock(&usblp->mut);
- if ((error = usblp_read_status(usblp, usblp->statusbuf)) < 0) {
+ error = usblp_read_status(usblp, usblp->statusbuf);
+ if (error < 0) {
mutex_unlock(&usblp->mut);
printk_ratelimited(KERN_ERR
"usblp%d: error %d reading printer status\n",
@@ -751,14 +753,16 @@ static ssize_t usblp_write(struct file *file, const char __user *buffer, size_t
rv = -EINTR;
goto raise_biglock;
}
- if ((rv = usblp_wwait(usblp, !!(file->f_flags & O_NONBLOCK))) < 0)
+ rv = usblp_wwait(usblp, !!(file->f_flags & O_NONBLOCK));
+ if (rv < 0)
goto raise_wait;
while (writecount < count) {
/*
* Step 1: Submit next block.
*/
- if ((transfer_length = count - writecount) > USBLP_BUF_SIZE)
+ transfer_length = count - writecount;
+ if (transfer_length > USBLP_BUF_SIZE)
transfer_length = USBLP_BUF_SIZE;
rv = -ENOMEM;
@@ -776,7 +780,9 @@ static ssize_t usblp_write(struct file *file, const char __user *buffer, size_t
spin_lock_irq(&usblp->lock);
usblp->wcomplete = 0;
spin_unlock_irq(&usblp->lock);
- if ((rv = usb_submit_urb(writeurb, GFP_KERNEL)) < 0) {
+
+ rv = usb_submit_urb(writeurb, GFP_KERNEL);
+ if (rv < 0) {
usblp->wstatus = 0;
spin_lock_irq(&usblp->lock);
usblp->no_paper = 0;
@@ -857,22 +863,24 @@ static ssize_t usblp_read(struct file *file, char __user *buffer, size_t len, lo
goto done;
}
- if ((avail = usblp->rstatus) < 0) {
+ avail = usblp->rstatus;
+ if (avail < 0) {
printk(KERN_ERR "usblp%d: error %d reading from printer\n",
- usblp->minor, (int)avail);
+ usblp->minor, (int)avail);
usblp_submit_read(usblp);
count = -EIO;
goto done;
}
- count = len < avail - usblp->readcount ? len : avail - usblp->readcount;
+ count = min_t(ssize_t, len, avail - usblp->readcount);
if (count != 0 &&
copy_to_user(buffer, usblp->readbuf + usblp->readcount, count)) {
count = -EFAULT;
goto done;
}
- if ((usblp->readcount += count) == avail) {
+ usblp->readcount += count;
+ if (usblp->readcount == avail) {
if (usblp_submit_read(usblp) < 0) {
/* We don't want to leak USB return codes into errno. */
if (count == 0)
@@ -973,7 +981,8 @@ static int usblp_rwait_and_lock(struct usblp *usblp, int nonblock)
break;
}
set_current_state(TASK_INTERRUPTIBLE);
- if ((rc = usblp_rtest(usblp, nonblock)) < 0) {
+ rc = usblp_rtest(usblp, nonblock);
+ if (rc < 0) {
mutex_unlock(&usblp->mut);
break;
}
@@ -1031,7 +1040,8 @@ static int usblp_submit_read(struct usblp *usblp)
usblp->readcount = 0; /* XXX Why here? */
usblp->rcomplete = 0;
spin_unlock_irqrestore(&usblp->lock, flags);
- if ((rc = usb_submit_urb(urb, GFP_KERNEL)) < 0) {
+ rc = usb_submit_urb(urb, GFP_KERNEL);
+ if (rc < 0) {
dev_dbg(&usblp->intf->dev, "error submitting urb (%d)\n", rc);
spin_lock_irqsave(&usblp->lock, flags);
usblp->rstatus = rc;
@@ -1132,7 +1142,7 @@ static int usblp_probe(struct usb_interface *intf,
/* Malloc and start initializing usblp structure so we can use it
* directly. */
- usblp = kzalloc(sizeof(struct usblp), GFP_KERNEL);
+ usblp = kzalloc_obj(struct usblp);
if (!usblp) {
retval = -ENOMEM;
goto abort_ret;
@@ -1150,7 +1160,8 @@ static int usblp_probe(struct usb_interface *intf,
/* Malloc device ID string buffer to the largest expected length,
* since we can re-query it on an ioctl and a dynamic string
* could change in length. */
- if (!(usblp->device_id_string = kmalloc(USBLP_DEVICE_ID_SIZE, GFP_KERNEL))) {
+ usblp->device_id_string = kmalloc(USBLP_DEVICE_ID_SIZE, GFP_KERNEL);
+ if (!usblp->device_id_string) {
retval = -ENOMEM;
goto abort;
}
@@ -1160,13 +1171,14 @@ static int usblp_probe(struct usb_interface *intf,
* malloc both regardless of bidirectionality, because the
* alternate setting can be changed later via an ioctl.
*/
- if (!(usblp->readbuf = kmalloc(USBLP_BUF_SIZE_IN, GFP_KERNEL))) {
+ usblp->readbuf = kmalloc(USBLP_BUF_SIZE_IN, GFP_KERNEL);
+ if (!usblp->readbuf) {
retval = -ENOMEM;
goto abort;
}
/* Allocate buffer for printer status */
- usblp->statusbuf = kmalloc(STATUS_BUF_SIZE, GFP_KERNEL);
+ usblp->statusbuf = kzalloc(STATUS_BUF_SIZE, GFP_KERNEL);
if (!usblp->statusbuf) {
retval = -ENOMEM;
goto abort;
@@ -1365,6 +1377,7 @@ static int usblp_cache_device_id_string(struct usblp *usblp)
{
int err, length;
+ memset(usblp->device_id_string, 0, USBLP_DEVICE_ID_SIZE);
err = usblp_get_id(usblp, 0, usblp->device_id_string, USBLP_DEVICE_ID_SIZE - 1);
if (err < 0) {
dev_dbg(&usblp->intf->dev,
diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c
index 34e46ef308ab..bd9347804dec 100644
--- a/drivers/usb/class/usbtmc.c
+++ b/drivers/usb/class/usbtmc.c
@@ -172,7 +172,7 @@ static int usbtmc_open(struct inode *inode, struct file *filp)
return -ENODEV;
}
- file_data = kzalloc(sizeof(*file_data), GFP_KERNEL);
+ file_data = kzalloc_obj(*file_data);
if (!file_data)
return -ENOMEM;
@@ -254,6 +254,9 @@ static int usbtmc_release(struct inode *inode, struct file *file)
list_del(&file_data->file_elem);
spin_unlock_irq(&file_data->data->dev_lock);
+
+ /* flush anchored URBs */
+ usbtmc_draw_down(file_data);
mutex_unlock(&file_data->data->io_mutex);
kref_put(&file_data->data->kref, usbtmc_delete);
@@ -482,6 +485,8 @@ static int usbtmc_get_stb(struct usbtmc_file_data *file_data, __u8 *stb)
u8 *buffer;
u8 tag;
int rv;
+ long wait_rv;
+ unsigned long expire;
dev_dbg(dev, "Enter ioctl_read_stb iin_ep_present: %d\n",
data->iin_ep_present);
@@ -511,16 +516,18 @@ static int usbtmc_get_stb(struct usbtmc_file_data *file_data, __u8 *stb)
}
if (data->iin_ep_present) {
- rv = wait_event_interruptible_timeout(
+ expire = msecs_to_jiffies(file_data->timeout);
+ wait_rv = wait_event_interruptible_timeout(
data->waitq,
atomic_read(&data->iin_data_valid) != 0,
- file_data->timeout);
- if (rv < 0) {
- dev_dbg(dev, "wait interrupted %d\n", rv);
+ expire);
+ if (wait_rv < 0) {
+ dev_dbg(dev, "wait interrupted %ld\n", wait_rv);
+ rv = wait_rv;
goto exit;
}
- if (rv == 0) {
+ if (wait_rv == 0) {
dev_dbg(dev, "wait timed out\n");
rv = -ETIMEDOUT;
goto exit;
@@ -539,6 +546,8 @@ static int usbtmc_get_stb(struct usbtmc_file_data *file_data, __u8 *stb)
dev_dbg(dev, "stb:0x%02x received %d\n", (unsigned int)*stb, rv);
+ rv = 0;
+
exit:
/* bump interrupt bTag */
data->iin_bTag += 1;
@@ -559,14 +568,15 @@ static int usbtmc488_ioctl_read_stb(struct usbtmc_file_data *file_data,
rv = usbtmc_get_stb(file_data, &stb);
- if (rv > 0) {
- srq_asserted = atomic_xchg(&file_data->srq_asserted,
- srq_asserted);
- if (srq_asserted)
- stb |= 0x40; /* Set RQS bit */
+ if (rv < 0)
+ return rv;
+
+ srq_asserted = atomic_xchg(&file_data->srq_asserted, srq_asserted);
+ if (srq_asserted)
+ stb |= 0x40; /* Set RQS bit */
+
+ rv = put_user(stb, (__u8 __user *)arg);
- rv = put_user(stb, (__u8 __user *)arg);
- }
return rv;
}
@@ -602,9 +612,9 @@ static int usbtmc488_ioctl_wait_srq(struct usbtmc_file_data *file_data,
{
struct usbtmc_device_data *data = file_data->data;
struct device *dev = &data->intf->dev;
- int rv;
u32 timeout;
unsigned long expire;
+ long wait_rv;
if (!data->iin_ep_present) {
dev_dbg(dev, "no interrupt endpoint present\n");
@@ -618,25 +628,24 @@ static int usbtmc488_ioctl_wait_srq(struct usbtmc_file_data *file_data,
mutex_unlock(&data->io_mutex);
- rv = wait_event_interruptible_timeout(
- data->waitq,
- atomic_read(&file_data->srq_asserted) != 0 ||
- atomic_read(&file_data->closing),
- expire);
+ wait_rv = wait_event_interruptible_timeout(
+ data->waitq,
+ atomic_read(&file_data->srq_asserted) != 0 ||
+ atomic_read(&file_data->closing),
+ expire);
mutex_lock(&data->io_mutex);
/* Note! disconnect or close could be called in the meantime */
if (atomic_read(&file_data->closing) || data->zombie)
- rv = -ENODEV;
+ return -ENODEV;
- if (rv < 0) {
- /* dev can be invalid now! */
- pr_debug("%s - wait interrupted %d\n", __func__, rv);
- return rv;
+ if (wait_rv < 0) {
+ dev_dbg(dev, "%s - wait interrupted %ld\n", __func__, wait_rv);
+ return wait_rv;
}
- if (rv == 0) {
+ if (wait_rv == 0) {
dev_dbg(dev, "%s - wait timed out\n", __func__);
return -ETIMEDOUT;
}
@@ -721,7 +730,7 @@ static int usbtmc488_ioctl_trigger(struct usbtmc_file_data *file_data)
buffer[1] = data->bTag;
buffer[2] = ~data->bTag;
- retval = usb_bulk_msg(data->usb_dev,
+ retval = usb_bulk_msg_killable(data->usb_dev,
usb_sndbulkpipe(data->usb_dev,
data->bulk_out),
buffer, USBTMC_HEADER_SIZE,
@@ -830,6 +839,7 @@ static ssize_t usbtmc_generic_read(struct usbtmc_file_data *file_data,
unsigned long expire;
int bufcount = 1;
int again = 0;
+ long wait_rv;
/* mutex already locked */
@@ -942,19 +952,24 @@ static ssize_t usbtmc_generic_read(struct usbtmc_file_data *file_data,
if (!(flags & USBTMC_FLAG_ASYNC)) {
dev_dbg(dev, "%s: before wait time %lu\n",
__func__, expire);
- retval = wait_event_interruptible_timeout(
+ wait_rv = wait_event_interruptible_timeout(
file_data->wait_bulk_in,
usbtmc_do_transfer(file_data),
expire);
- dev_dbg(dev, "%s: wait returned %d\n",
- __func__, retval);
+ dev_dbg(dev, "%s: wait returned %ld\n",
+ __func__, wait_rv);
- if (retval <= 0) {
- if (retval == 0)
- retval = -ETIMEDOUT;
+ if (wait_rv < 0) {
+ retval = wait_rv;
goto error;
}
+
+ if (wait_rv == 0) {
+ retval = -ETIMEDOUT;
+ goto error;
+ }
+
}
urb = usb_get_from_anchor(&file_data->in_anchor);
@@ -1335,7 +1350,7 @@ static int send_request_dev_dep_msg_in(struct usbtmc_file_data *file_data,
buffer[11] = 0; /* Reserved */
/* Send bulk URB */
- retval = usb_bulk_msg(data->usb_dev,
+ retval = usb_bulk_msg_killable(data->usb_dev,
usb_sndbulkpipe(data->usb_dev,
data->bulk_out),
buffer, USBTMC_HEADER_SIZE,
@@ -1380,7 +1395,10 @@ static ssize_t usbtmc_read(struct file *filp, char __user *buf,
if (!buffer)
return -ENOMEM;
- mutex_lock(&data->io_mutex);
+ retval = mutex_lock_interruptible(&data->io_mutex);
+ if (retval < 0)
+ goto exit_nolock;
+
if (data->zombie) {
retval = -ENODEV;
goto exit;
@@ -1404,7 +1422,7 @@ static ssize_t usbtmc_read(struct file *filp, char __user *buf,
actual = 0;
/* Send bulk URB */
- retval = usb_bulk_msg(data->usb_dev,
+ retval = usb_bulk_msg_killable(data->usb_dev,
usb_rcvbulkpipe(data->usb_dev,
data->bulk_in),
buffer, bufsize, &actual,
@@ -1503,6 +1521,7 @@ static ssize_t usbtmc_read(struct file *filp, char __user *buf,
exit:
mutex_unlock(&data->io_mutex);
+exit_nolock:
kfree(buffer);
return retval;
}
@@ -1920,10 +1939,8 @@ static int usbtmc_ioctl_request(struct usbtmc_device_data *data,
u8 *buffer = NULL;
int rv;
unsigned int is_in, pipe;
- unsigned long res;
- res = copy_from_user(&request, arg, sizeof(struct usbtmc_ctrlrequest));
- if (res)
+ if (copy_from_user(&request, arg, sizeof(struct usbtmc_ctrlrequest)))
return -EFAULT;
if (request.req.wLength > USBTMC_BUFSIZE)
@@ -1940,9 +1957,8 @@ static int usbtmc_ioctl_request(struct usbtmc_device_data *data,
if (!is_in) {
/* Send control data to device */
- res = copy_from_user(buffer, request.data,
- request.req.wLength);
- if (res) {
+ if (copy_from_user(buffer, request.data,
+ request.req.wLength)) {
rv = -EFAULT;
goto exit;
}
@@ -1968,8 +1984,7 @@ static int usbtmc_ioctl_request(struct usbtmc_device_data *data,
if (rv && is_in) {
/* Read control data from device */
- res = copy_to_user(request.data, buffer, rv);
- if (res)
+ if (copy_to_user(request.data, buffer, rv))
rv = -EFAULT;
}
@@ -2186,7 +2201,7 @@ static long usbtmc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
case USBTMC_IOCTL_GET_STB:
retval = usbtmc_get_stb(file_data, &tmp_byte);
- if (retval > 0)
+ if (!retval)
retval = put_user(tmp_byte, (__u8 __user *)arg);
break;
@@ -2366,7 +2381,7 @@ static int usbtmc_probe(struct usb_interface *intf,
dev_dbg(&intf->dev, "%s called\n", __func__);
- data = kzalloc(sizeof(*data), GFP_KERNEL);
+ data = kzalloc_obj(*data);
if (!data)
return -ENOMEM;
diff --git a/drivers/usb/common/ulpi.c b/drivers/usb/common/ulpi.c
index 4a2ee447b213..9b69148128e5 100644
--- a/drivers/usb/common/ulpi.c
+++ b/drivers/usb/common/ulpi.c
@@ -286,12 +286,15 @@ static int ulpi_register(struct device *dev, struct ulpi *ulpi)
ACPI_COMPANION_SET(&ulpi->dev, ACPI_COMPANION(dev));
ret = ulpi_of_register(ulpi);
- if (ret)
+ if (ret) {
+ kfree(ulpi);
return ret;
+ }
ret = ulpi_read_id(ulpi);
if (ret) {
of_node_put(ulpi->dev.of_node);
+ kfree(ulpi);
return ret;
}
@@ -324,17 +327,16 @@ struct ulpi *ulpi_register_interface(struct device *dev,
struct ulpi *ulpi;
int ret;
- ulpi = kzalloc(sizeof(*ulpi), GFP_KERNEL);
+ ulpi = kzalloc_obj(*ulpi);
if (!ulpi)
return ERR_PTR(-ENOMEM);
ulpi->ops = ops;
ret = ulpi_register(dev, ulpi);
- if (ret) {
- kfree(ulpi);
+ if (ret)
return ERR_PTR(ret);
- }
+
return ulpi;
}
diff --git a/drivers/usb/common/usb-conn-gpio.c b/drivers/usb/common/usb-conn-gpio.c
index aa710b50791b..421c3af38e06 100644
--- a/drivers/usb/common/usb-conn-gpio.c
+++ b/drivers/usb/common/usb-conn-gpio.c
@@ -21,6 +21,9 @@
#include <linux/regulator/consumer.h>
#include <linux/string_choices.h>
#include <linux/usb/role.h>
+#include <linux/idr.h>
+
+static DEFINE_IDA(usb_conn_ida);
#define USB_GPIO_DEB_MS 20 /* ms */
#define USB_GPIO_DEB_US ((USB_GPIO_DEB_MS) * 1000) /* us */
@@ -30,6 +33,7 @@
struct usb_conn_info {
struct device *dev;
+ int conn_id; /* store the IDA-allocated ID */
struct usb_role_switch *role_sw;
enum usb_role last_role;
struct regulator *vbus;
@@ -158,10 +162,20 @@ static int usb_conn_psy_register(struct usb_conn_info *info)
struct device *dev = info->dev;
struct power_supply_desc *desc = &info->desc;
struct power_supply_config cfg = {
- .of_node = dev->of_node,
+ .fwnode = dev_fwnode(dev),
};
- desc->name = "usb-charger";
+ info->conn_id = ida_alloc(&usb_conn_ida, GFP_KERNEL);
+ if (info->conn_id < 0)
+ return info->conn_id;
+
+ desc->name = devm_kasprintf(dev, GFP_KERNEL, "usb-charger-%d",
+ info->conn_id);
+ if (!desc->name) {
+ ida_free(&usb_conn_ida, info->conn_id);
+ return -ENOMEM;
+ }
+
desc->properties = usb_charger_properties;
desc->num_properties = ARRAY_SIZE(usb_charger_properties);
desc->get_property = usb_charger_get_property;
@@ -169,8 +183,10 @@ static int usb_conn_psy_register(struct usb_conn_info *info)
cfg.drv_data = info;
info->charger = devm_power_supply_register(dev, desc, &cfg);
- if (IS_ERR(info->charger))
- dev_err(dev, "Unable to register charger\n");
+ if (IS_ERR(info->charger)) {
+ dev_err(dev, "Unable to register charger %d\n", info->conn_id);
+ ida_free(&usb_conn_ida, info->conn_id);
+ }
return PTR_ERR_OR_ZERO(info->charger);
}
@@ -278,6 +294,9 @@ static void usb_conn_remove(struct platform_device *pdev)
cancel_delayed_work_sync(&info->dw_det);
+ if (info->charger)
+ ida_free(&usb_conn_ida, info->conn_id);
+
if (info->last_role == USB_ROLE_HOST && info->vbus)
regulator_disable(info->vbus);
diff --git a/drivers/usb/core/Makefile b/drivers/usb/core/Makefile
index ac006abd13b3..60ea76160122 100644
--- a/drivers/usb/core/Makefile
+++ b/drivers/usb/core/Makefile
@@ -3,12 +3,16 @@
# Makefile for USB Core files and filesystem
#
+# define_trace.h needs to know how to find our header
+CFLAGS_trace.o := -I$(src)
+
usbcore-y := usb.o hub.o hcd.o urb.o message.o driver.o
usbcore-y += config.o file.o buffer.o sysfs.o endpoint.o
usbcore-y += devio.o notify.o generic.o quirks.o devices.o
-usbcore-y += phy.o port.o
+usbcore-y += phy.o port.o trace.o
usbcore-$(CONFIG_OF) += of.o
+usbcore-$(CONFIG_USB_XHCI_SIDEBAND) += offload.o
usbcore-$(CONFIG_USB_PCI) += hcd-pci.o
usbcore-$(CONFIG_ACPI) += usb-acpi.o
diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c
index f7bf8d1de3ad..417140b012bb 100644
--- a/drivers/usb/core/config.c
+++ b/drivers/usb/core/config.c
@@ -54,8 +54,8 @@ static void usb_parse_ssp_isoc_endpoint_companion(struct device *ddev,
* follows the SuperSpeed Endpoint Companion descriptor
*/
desc = (struct usb_ssp_isoc_ep_comp_descriptor *) buffer;
- if (desc->bDescriptorType != USB_DT_SSP_ISOC_ENDPOINT_COMP ||
- size < USB_DT_SSP_ISOC_EP_COMP_SIZE) {
+ if (size < USB_DT_SSP_ISOC_EP_COMP_SIZE ||
+ desc->bDescriptorType != USB_DT_SSP_ISOC_ENDPOINT_COMP) {
dev_notice(ddev, "Invalid SuperSpeedPlus isoc endpoint companion"
"for config %d interface %d altsetting %d ep %d.\n",
cfgno, inum, asnum, ep->desc.bEndpointAddress);
@@ -64,6 +64,37 @@ static void usb_parse_ssp_isoc_endpoint_companion(struct device *ddev,
memcpy(&ep->ssp_isoc_ep_comp, desc, USB_DT_SSP_ISOC_EP_COMP_SIZE);
}
+static void usb_parse_eusb2_isoc_endpoint_companion(struct device *ddev,
+ int cfgno, int inum, int asnum, struct usb_host_endpoint *ep,
+ unsigned char *buffer, int size)
+{
+ struct usb_eusb2_isoc_ep_comp_descriptor *desc;
+ struct usb_descriptor_header *h;
+
+ /*
+ * eUSB2 isochronous endpoint companion descriptor for this endpoint
+ * shall be declared before the next endpoint or interface descriptor
+ */
+ while (size >= USB_DT_EUSB2_ISOC_EP_COMP_SIZE) {
+ h = (struct usb_descriptor_header *)buffer;
+
+ if (h->bDescriptorType == USB_DT_EUSB2_ISOC_ENDPOINT_COMP) {
+ desc = (struct usb_eusb2_isoc_ep_comp_descriptor *)buffer;
+ ep->eusb2_isoc_ep_comp = *desc;
+ return;
+ }
+ if (h->bDescriptorType == USB_DT_ENDPOINT ||
+ h->bDescriptorType == USB_DT_INTERFACE)
+ break;
+
+ buffer += h->bLength;
+ size -= h->bLength;
+ }
+
+ dev_notice(ddev, "No eUSB2 isoc ep %d companion for config %d interface %d altsetting %d\n",
+ ep->desc.bEndpointAddress, cfgno, inum, asnum);
+}
+
static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno,
int inum, int asnum, struct usb_host_endpoint *ep,
unsigned char *buffer, int size)
@@ -76,8 +107,14 @@ static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno,
*/
desc = (struct usb_ss_ep_comp_descriptor *) buffer;
- if (desc->bDescriptorType != USB_DT_SS_ENDPOINT_COMP ||
- size < USB_DT_SS_EP_COMP_SIZE) {
+ if (size < USB_DT_SS_EP_COMP_SIZE) {
+ dev_notice(ddev,
+ "invalid SuperSpeed endpoint companion descriptor "
+ "of length %d, skipping\n", size);
+ return;
+ }
+
+ if (desc->bDescriptorType != USB_DT_SS_ENDPOINT_COMP) {
dev_notice(ddev, "No SuperSpeed endpoint companion for config %d "
" interface %d altsetting %d ep %d: "
"using minimum values\n",
@@ -258,8 +295,10 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno,
int n, i, j, retval;
unsigned int maxp;
const unsigned short *maxpacket_maxes;
+ u16 bcdUSB;
d = (struct usb_endpoint_descriptor *) buffer;
+ bcdUSB = le16_to_cpu(udev->descriptor.bcdUSB);
buffer += d->bLength;
size -= d->bLength;
@@ -274,7 +313,7 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno,
goto skip_to_next_endpoint_or_interface_descriptor;
}
- i = d->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+ i = usb_endpoint_num(d);
if (i == 0) {
dev_notice(ddev, "config %d interface %d altsetting %d has an "
"invalid descriptor for endpoint zero, skipping\n",
@@ -409,15 +448,17 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno,
/*
* Validate the wMaxPacketSize field.
- * Some devices have isochronous endpoints in altsetting 0;
- * the USB-2 spec requires such endpoints to have wMaxPacketSize = 0
- * (see the end of section 5.6.3), so don't warn about them.
+ * eUSB2 devices (see USB 2.0 Double Isochronous IN ECN 9.6.6 Endpoint)
+ * and devices with isochronous endpoints in altsetting 0 (see USB 2.0
+ * end of section 5.6.3) have wMaxPacketSize = 0.
+ * So don't warn about those.
*/
maxp = le16_to_cpu(endpoint->desc.wMaxPacketSize);
- if (maxp == 0 && !(usb_endpoint_xfer_isoc(d) && asnum == 0)) {
+
+ if (maxp == 0 && bcdUSB != 0x0220 &&
+ !(usb_endpoint_xfer_isoc(d) && asnum == 0))
dev_notice(ddev, "config %d interface %d altsetting %d endpoint 0x%X has invalid wMaxPacketSize 0\n",
cfgno, inum, asnum, d->bEndpointAddress);
- }
/* Find the highest legal maxpacket size for this endpoint */
i = 0; /* additional transactions per microframe */
@@ -465,6 +506,12 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno,
maxp);
}
+ /* Parse a possible eUSB2 periodic endpoint companion descriptor */
+ if (udev->speed == USB_SPEED_HIGH && bcdUSB == 0x0220 &&
+ !le16_to_cpu(d->wMaxPacketSize) && usb_endpoint_is_isoc_in(d))
+ usb_parse_eusb2_isoc_endpoint_companion(ddev, cfgno, inum, asnum,
+ endpoint, buffer, size);
+
/* Parse a possible SuperSpeed endpoint companion descriptor */
if (udev->speed >= USB_SPEED_SUPER)
usb_parse_ss_endpoint_companion(ddev, cfgno,
@@ -776,7 +823,7 @@ static int usb_parse_configuration(struct usb_device *dev, int cfgidx,
nalts[i] = j = USB_MAXALTSETTING;
}
- intfc = kzalloc(struct_size(intfc, altsetting, j), GFP_KERNEL);
+ intfc = kzalloc_flex(*intfc, altsetting, j);
config->intf_cache[i] = intfc;
if (!intfc)
return -ENOMEM;
@@ -880,7 +927,11 @@ int usb_get_configuration(struct usb_device *dev)
dev->descriptor.bNumConfigurations = ncfg = USB_MAXCONFIG;
}
- if (ncfg < 1) {
+ if (ncfg < 1 && dev->quirks & USB_QUIRK_FORCE_ONE_CONFIG) {
+ dev_info(ddev, "Device claims zero configurations, forcing to 1\n");
+ dev->descriptor.bNumConfigurations = 1;
+ ncfg = 1;
+ } else if (ncfg < 1) {
dev_err(ddev, "no configurations\n");
return -EINVAL;
}
@@ -993,7 +1044,12 @@ int usb_get_bos_descriptor(struct usb_device *dev)
__u8 cap_type;
int ret;
- bos = kzalloc(sizeof(*bos), GFP_KERNEL);
+ if (dev->quirks & USB_QUIRK_NO_BOS) {
+ dev_dbg(ddev, "skipping BOS descriptor\n");
+ return -ENOMSG;
+ }
+
+ bos = kzalloc_obj(*bos);
if (!bos)
return -ENOMEM;
@@ -1014,7 +1070,7 @@ int usb_get_bos_descriptor(struct usb_device *dev)
if (total_len < length)
return -EINVAL;
- dev->bos = kzalloc(sizeof(*dev->bos), GFP_KERNEL);
+ dev->bos = kzalloc_obj(*dev->bos);
if (!dev->bos)
return -ENOMEM;
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index f6ce6e26e0d4..e191934623c7 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -245,7 +245,7 @@ static int usbdev_mmap(struct file *file, struct vm_area_struct *vma)
if (ret)
goto error;
- usbm = kzalloc(sizeof(struct usb_memory), GFP_KERNEL);
+ usbm = kzalloc_obj(struct usb_memory);
if (!usbm) {
ret = -ENOMEM;
goto error_decrease_mem;
@@ -402,7 +402,7 @@ static struct async *alloc_async(unsigned int numisoframes)
{
struct async *as;
- as = kzalloc(sizeof(struct async), GFP_KERNEL);
+ as = kzalloc_obj(struct async);
if (!as)
return NULL;
as->urb = usb_alloc_urb(numisoframes, GFP_KERNEL);
@@ -970,7 +970,7 @@ static int parse_usbdevfs_streams(struct usb_dev_state *ps,
if (num_streams_ret && (num_streams < 2 || num_streams > 65536))
return -EINVAL;
- eps = kmalloc_array(num_eps, sizeof(*eps), GFP_KERNEL);
+ eps = kmalloc_objs(*eps, num_eps);
if (!eps)
return -ENOMEM;
@@ -1039,7 +1039,7 @@ static int usbdev_open(struct inode *inode, struct file *file)
int ret;
ret = -ENOMEM;
- ps = kzalloc(sizeof(struct usb_dev_state), GFP_KERNEL);
+ ps = kzalloc_obj(struct usb_dev_state);
if (!ps)
goto out_free_ps;
@@ -1196,7 +1196,7 @@ static int do_proc_control(struct usb_dev_state *ps,
urb = usb_alloc_urb(0, GFP_NOIO);
if (!urb)
goto done;
- dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_NOIO);
+ dr = kmalloc_obj(struct usb_ctrlrequest, GFP_NOIO);
if (!dr)
goto done;
@@ -1670,7 +1670,7 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb
/* min 8 byte setup packet */
if (uurb->buffer_length < 8)
return -EINVAL;
- dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
+ dr = kmalloc_obj(struct usb_ctrlrequest);
if (!dr)
return -ENOMEM;
if (copy_from_user(dr, uurb->buffer, 8)) {
@@ -1805,9 +1805,8 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb
as->mem_usage = u;
if (num_sgs) {
- as->urb->sg = kmalloc_array(num_sgs,
- sizeof(struct scatterlist),
- GFP_KERNEL | __GFP_NOWARN);
+ as->urb->sg = kmalloc_objs(struct scatterlist, num_sgs,
+ GFP_KERNEL | __GFP_NOWARN);
if (!as->urb->sg) {
ret = -ENOMEM;
goto error;
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 460d4dde5994..f63004417058 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -57,7 +57,7 @@ ssize_t usb_store_new_id(struct usb_dynids *dynids,
if (fields < 2)
return -EINVAL;
- dynid = kzalloc(sizeof(*dynid), GFP_KERNEL);
+ dynid = kzalloc_obj(*dynid);
if (!dynid)
return -ENOMEM;
@@ -119,11 +119,11 @@ ssize_t usb_show_dynids(struct usb_dynids *dynids, char *buf)
guard(mutex)(&usb_dynids_lock);
list_for_each_entry(dynid, &dynids->list, node)
if (dynid->id.bInterfaceClass != 0)
- count += scnprintf(&buf[count], PAGE_SIZE - count, "%04x %04x %02x\n",
+ count += sysfs_emit_at(buf, count, "%04x %04x %02x\n",
dynid->id.idVendor, dynid->id.idProduct,
dynid->id.bInterfaceClass);
else
- count += scnprintf(&buf[count], PAGE_SIZE - count, "%04x %04x\n",
+ count += sysfs_emit_at(buf, count, "%04x %04x\n",
dynid->id.idVendor, dynid->id.idProduct);
return count;
}
@@ -332,10 +332,10 @@ static int usb_probe_interface(struct device *dev)
return error;
if (udev->authorized == 0) {
- dev_err(&intf->dev, "Device is not authorized for usage\n");
+ dev_info(&intf->dev, "Device is not authorized for usage\n");
return error;
} else if (intf->authorized == 0) {
- dev_err(&intf->dev, "Interface %d is not authorized for usage\n",
+ dev_info(&intf->dev, "Interface %d is not authorized for usage\n",
intf->altsetting->desc.bInterfaceNumber);
return error;
}
@@ -1415,16 +1415,34 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
int status = 0;
int i = 0, n = 0;
struct usb_interface *intf;
+ bool offload_active = false;
if (udev->state == USB_STATE_NOTATTACHED ||
udev->state == USB_STATE_SUSPENDED)
goto done;
+ usb_offload_set_pm_locked(udev, true);
+ if (msg.event == PM_EVENT_SUSPEND && usb_offload_check(udev)) {
+ dev_dbg(&udev->dev, "device offloaded, skip suspend.\n");
+ offload_active = true;
+ }
+
/* Suspend all the interfaces and then udev itself */
if (udev->actconfig) {
n = udev->actconfig->desc.bNumInterfaces;
for (i = n - 1; i >= 0; --i) {
intf = udev->actconfig->interface[i];
+ /*
+ * Don't suspend interfaces with remote wakeup while
+ * the controller is active. This preserves pending
+ * interrupt urbs, allowing interrupt events to be
+ * handled during system suspend.
+ */
+ if (offload_active && intf->needs_remote_wakeup) {
+ dev_dbg(&intf->dev,
+ "device offloaded, skip suspend.\n");
+ continue;
+ }
status = usb_suspend_interface(udev, intf, msg);
/* Ignore errors during system sleep transitions */
@@ -1435,7 +1453,8 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
}
}
if (status == 0) {
- status = usb_suspend_device(udev, msg);
+ if (!offload_active)
+ status = usb_suspend_device(udev, msg);
/*
* Ignore errors from non-root-hub devices during
@@ -1480,13 +1499,17 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
*/
} else {
udev->can_submit = 0;
- for (i = 0; i < 16; ++i) {
- usb_hcd_flush_endpoint(udev, udev->ep_out[i]);
- usb_hcd_flush_endpoint(udev, udev->ep_in[i]);
+ if (!offload_active) {
+ for (i = 0; i < 16; ++i) {
+ usb_hcd_flush_endpoint(udev, udev->ep_out[i]);
+ usb_hcd_flush_endpoint(udev, udev->ep_in[i]);
+ }
}
}
done:
+ if (status != 0)
+ usb_offload_set_pm_locked(udev, false);
dev_vdbg(&udev->dev, "%s: status %d\n", __func__, status);
return status;
}
@@ -1516,21 +1539,40 @@ static int usb_resume_both(struct usb_device *udev, pm_message_t msg)
int status = 0;
int i;
struct usb_interface *intf;
+ bool offload_active = false;
if (udev->state == USB_STATE_NOTATTACHED) {
status = -ENODEV;
goto done;
}
udev->can_submit = 1;
+ if (msg.event == PM_EVENT_RESUME)
+ offload_active = usb_offload_check(udev);
/* Resume the device */
- if (udev->state == USB_STATE_SUSPENDED || udev->reset_resume)
- status = usb_resume_device(udev, msg);
+ if (udev->state == USB_STATE_SUSPENDED || udev->reset_resume) {
+ if (!offload_active)
+ status = usb_resume_device(udev, msg);
+ else
+ dev_dbg(&udev->dev,
+ "device offloaded, skip resume.\n");
+ }
/* Resume the interfaces */
if (status == 0 && udev->actconfig) {
for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
intf = udev->actconfig->interface[i];
+ /*
+ * Interfaces with remote wakeup aren't suspended
+ * while the controller is active. This preserves
+ * pending interrupt urbs, allowing interrupt events
+ * to be handled during system suspend.
+ */
+ if (offload_active && intf->needs_remote_wakeup) {
+ dev_dbg(&intf->dev,
+ "device offloaded, skip resume.\n");
+ continue;
+ }
usb_resume_interface(udev, intf, msg,
udev->reset_resume);
}
@@ -1539,6 +1581,7 @@ static int usb_resume_both(struct usb_device *udev, pm_message_t msg)
done:
dev_vdbg(&udev->dev, "%s: status %d\n", __func__, status);
+ usb_offload_set_pm_locked(udev, false);
if (!status)
udev->reset_resume = 0;
return status;
@@ -1723,8 +1766,6 @@ int usb_autoresume_device(struct usb_device *udev)
dev_vdbg(&udev->dev, "%s: cnt %d -> %d\n",
__func__, atomic_read(&udev->dev.power.usage_count),
status);
- if (status > 0)
- status = 0;
return status;
}
@@ -1774,13 +1815,11 @@ EXPORT_SYMBOL_GPL(usb_autopm_put_interface);
void usb_autopm_put_interface_async(struct usb_interface *intf)
{
struct usb_device *udev = interface_to_usbdev(intf);
- int status;
usb_mark_last_busy(udev);
- status = pm_runtime_put(&intf->dev);
- dev_vdbg(&intf->dev, "%s: cnt %d -> %d\n",
- __func__, atomic_read(&intf->dev.power.usage_count),
- status);
+ pm_runtime_put(&intf->dev);
+ dev_vdbg(&intf->dev, "%s: cnt %d\n",
+ __func__, atomic_read(&intf->dev.power.usage_count));
}
EXPORT_SYMBOL_GPL(usb_autopm_put_interface_async);
@@ -1829,8 +1868,6 @@ int usb_autopm_get_interface(struct usb_interface *intf)
dev_vdbg(&intf->dev, "%s: cnt %d -> %d\n",
__func__, atomic_read(&intf->dev.power.usage_count),
status);
- if (status > 0)
- status = 0;
return status;
}
EXPORT_SYMBOL_GPL(usb_autopm_get_interface);
diff --git a/drivers/usb/core/endpoint.c b/drivers/usb/core/endpoint.c
index e48399401608..e00eaf9e22cd 100644
--- a/drivers/usb/core/endpoint.c
+++ b/drivers/usb/core/endpoint.c
@@ -26,14 +26,6 @@ struct ep_device {
#define to_ep_device(_dev) \
container_of(_dev, struct ep_device, dev)
-struct ep_attribute {
- struct attribute attr;
- ssize_t (*show)(struct usb_device *,
- struct usb_endpoint_descriptor *, char *);
-};
-#define to_ep_attribute(_attr) \
- container_of(_attr, struct ep_attribute, attr)
-
#define usb_ep_attr(field, format_string) \
static ssize_t field##_show(struct device *dev, \
struct device_attribute *attr, \
@@ -154,7 +146,7 @@ int usb_create_ep_devs(struct device *parent,
struct ep_device *ep_dev;
int retval;
- ep_dev = kzalloc(sizeof(*ep_dev), GFP_KERNEL);
+ ep_dev = kzalloc_obj(*ep_dev);
if (!ep_dev) {
retval = -ENOMEM;
goto exit;
diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c
index 9c6ae5e1198b..a48994e11ef3 100644
--- a/drivers/usb/core/generic.c
+++ b/drivers/usb/core/generic.c
@@ -243,7 +243,7 @@ int usb_generic_driver_probe(struct usb_device *udev)
* with the driver core and lets interface drivers bind to them.
*/
if (udev->authorized == 0)
- dev_err(&udev->dev, "Device is not authorized for usage\n");
+ dev_info(&udev->dev, "Device is not authorized for usage\n");
else {
c = usb_choose_configuration(udev);
if (c >= 0) {
diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c
index 56b534f59907..cd223475917e 100644
--- a/drivers/usb/core/hcd-pci.c
+++ b/drivers/usb/core/hcd-pci.c
@@ -210,7 +210,7 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct hc_driver *driver)
hcd->amd_resume_bug = usb_hcd_amd_resume_bug(dev, driver);
if (driver->flags & HCD_MEMORY) {
- /* EHCI, OHCI */
+ /* XHCI, EHCI, OHCI */
hcd->rsrc_start = pci_resource_start(dev, 0);
hcd->rsrc_len = pci_resource_len(dev, 0);
if (!devm_request_mem_region(&dev->dev, hcd->rsrc_start,
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index a75cf1f6d741..89221f1ce769 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -77,10 +77,6 @@
/*-------------------------------------------------------------------------*/
-/* Keep track of which host controller drivers are loaded */
-unsigned long usb_hcds_loaded;
-EXPORT_SYMBOL_GPL(usb_hcds_loaded);
-
/* host controllers we manage */
DEFINE_IDR (usb_bus_idr);
EXPORT_SYMBOL_GPL (usb_bus_idr);
@@ -775,7 +771,7 @@ EXPORT_SYMBOL_GPL(usb_hcd_poll_rh_status);
/* timer callback */
static void rh_timer_func (struct timer_list *t)
{
- struct usb_hcd *_hcd = from_timer(_hcd, t, rh_timer);
+ struct usb_hcd *_hcd = timer_container_of(_hcd, t, rh_timer);
usb_hcd_poll_rh_status(_hcd);
}
@@ -842,7 +838,7 @@ static int usb_rh_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
} else { /* Status URB */
if (!hcd->uses_new_polling)
- del_timer (&hcd->rh_timer);
+ timer_delete(&hcd->rh_timer);
if (urb == hcd->status_urb) {
hcd->status_urb = NULL;
usb_hcd_unlink_urb_from_ep(hcd, urb);
@@ -1342,29 +1338,35 @@ void usb_hcd_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb)
dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
if (IS_ENABLED(CONFIG_HAS_DMA) &&
- (urb->transfer_flags & URB_DMA_MAP_SG))
+ (urb->transfer_flags & URB_DMA_MAP_SG)) {
dma_unmap_sg(hcd->self.sysdev,
urb->sg,
urb->num_sgs,
dir);
- else if (IS_ENABLED(CONFIG_HAS_DMA) &&
- (urb->transfer_flags & URB_DMA_MAP_PAGE))
+ } else if (IS_ENABLED(CONFIG_HAS_DMA) &&
+ (urb->transfer_flags & URB_DMA_MAP_PAGE)) {
dma_unmap_page(hcd->self.sysdev,
urb->transfer_dma,
urb->transfer_buffer_length,
dir);
- else if (IS_ENABLED(CONFIG_HAS_DMA) &&
- (urb->transfer_flags & URB_DMA_MAP_SINGLE))
+ } else if (IS_ENABLED(CONFIG_HAS_DMA) &&
+ (urb->transfer_flags & URB_DMA_MAP_SINGLE)) {
dma_unmap_single(hcd->self.sysdev,
urb->transfer_dma,
urb->transfer_buffer_length,
dir);
- else if (urb->transfer_flags & URB_MAP_LOCAL)
+ } else if (urb->transfer_flags & URB_MAP_LOCAL) {
hcd_free_coherent(urb->dev->bus,
&urb->transfer_dma,
&urb->transfer_buffer,
urb->transfer_buffer_length,
dir);
+ } else if ((urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP) && urb->sgt) {
+ dma_sync_sgtable_for_cpu(hcd->self.sysdev, urb->sgt, dir);
+ if (dir == DMA_FROM_DEVICE)
+ invalidate_kernel_vmap_range(urb->transfer_buffer,
+ urb->transfer_buffer_length);
+ }
/* Make it safe to call this routine more than once */
urb->transfer_flags &= ~(URB_DMA_MAP_SG | URB_DMA_MAP_PAGE |
@@ -1425,8 +1427,15 @@ int usb_hcd_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
}
dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
- if (urb->transfer_buffer_length != 0
- && !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) {
+ if (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP) {
+ if (!urb->sgt)
+ return 0;
+
+ if (dir == DMA_TO_DEVICE)
+ flush_kernel_vmap_range(urb->transfer_buffer,
+ urb->transfer_buffer_length);
+ dma_sync_sgtable_for_device(hcd->self.sysdev, urb->sgt, dir);
+ } else if (urb->transfer_buffer_length != 0) {
if (hcd->localmem_pool) {
ret = hcd_alloc_coherent(
urb->dev->bus, mem_flags,
@@ -1609,7 +1618,7 @@ int usb_hcd_unlink_urb (struct urb *urb, int status)
if (retval == 0)
retval = -EINPROGRESS;
else if (retval != -EIDRM && retval != -EBUSY)
- dev_dbg(&udev->dev, "hcd_unlink_urb %pK fail %d\n",
+ dev_dbg(&udev->dev, "hcd_unlink_urb %p fail %d\n",
urb, retval);
usb_put_dev(udev);
}
@@ -1623,7 +1632,6 @@ static void __usb_hcd_giveback_urb(struct urb *urb)
struct usb_hcd *hcd = bus_to_hcd(urb->dev->bus);
struct usb_anchor *anchor = urb->anchor;
int status = urb->unlinked;
- unsigned long flags;
urb->hcpriv = NULL;
if (unlikely((urb->transfer_flags & URB_SHORT_NOT_OK) &&
@@ -1641,14 +1649,13 @@ static void __usb_hcd_giveback_urb(struct urb *urb)
/* pass ownership to the completion handler */
urb->status = status;
/*
- * Only collect coverage in the softirq context and disable interrupts
- * to avoid scenarios with nested remote coverage collection sections
- * that KCOV does not support.
- * See the comment next to kcov_remote_start_usb_softirq() for details.
+ * This function can be called in task context inside another remote
+ * coverage collection section, but kcov doesn't support that kind of
+ * recursion yet. Only collect coverage in softirq context for now.
*/
- flags = kcov_remote_start_usb_softirq((u64)urb->dev->bus->busnum);
+ kcov_remote_start_usb_softirq((u64)urb->dev->bus->busnum);
urb->complete(urb);
- kcov_remote_stop_softirq(flags);
+ kcov_remote_stop_softirq();
usb_anchor_resume_wakeups(anchor);
atomic_dec(&urb->use_count);
@@ -1706,10 +1713,10 @@ static void usb_giveback_urb_bh(struct work_struct *work)
* @urb: urb being returned to the USB device driver.
* @status: completion status code for the URB.
*
- * Context: atomic. The completion callback is invoked in caller's context.
- * For HCDs with HCD_BH flag set, the completion callback is invoked in BH
- * context (except for URBs submitted to the root hub which always complete in
- * caller's context).
+ * Context: atomic. The completion callback is invoked either in a work queue
+ * (BH) context or in the caller's context, depending on whether the HCD_BH
+ * flag is set in the @hcd structure, except that URBs submitted to the
+ * root hub always complete in BH context.
*
* This hands the URB from HCD to its USB device driver, using its
* completion function. The HCD has freed all per-urb resources
@@ -1786,7 +1793,7 @@ rescan:
/* kick hcd */
unlink1(hcd, urb, -ESHUTDOWN);
dev_dbg (hcd->self.controller,
- "shutdown urb %pK ep%d%s-%s\n",
+ "shutdown urb %p ep%d%s-%s\n",
urb, usb_endpoint_num(&ep->desc),
is_in ? "in" : "out",
usb_ep_type_string(usb_endpoint_type(&ep->desc)));
@@ -2153,7 +2160,7 @@ static struct urb *request_single_step_set_feature_urb(
urb->complete = usb_ehset_completion;
urb->status = -EINPROGRESS;
urb->actual_length = 0;
- urb->transfer_flags = URB_DIR_IN;
+ urb->transfer_flags = URB_DIR_IN | URB_NO_TRANSFER_DMA_MAP;
usb_get_urb(urb);
atomic_inc(&urb->use_count);
atomic_inc(&urb->dev->urbnum);
@@ -2186,7 +2193,7 @@ int ehset_single_step_set_feature(struct usb_hcd *hcd, int port)
if (!buf)
return -ENOMEM;
- dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
+ dr = kmalloc_obj(struct usb_ctrlrequest);
if (!dr) {
kfree(buf);
return -ENOMEM;
@@ -2217,9 +2224,15 @@ int ehset_single_step_set_feature(struct usb_hcd *hcd, int port)
/* Complete remaining DATA and STATUS stages using the same URB */
urb->status = -EINPROGRESS;
+ urb->transfer_flags &= ~URB_NO_TRANSFER_DMA_MAP;
usb_get_urb(urb);
atomic_inc(&urb->use_count);
atomic_inc(&urb->dev->urbnum);
+ if (map_urb_for_dma(hcd, urb, GFP_KERNEL)) {
+ usb_put_urb(urb);
+ goto out1;
+ }
+
retval = hcd->driver->submit_single_step_set_feature(hcd, urb, 0);
if (!retval && !wait_for_completion_timeout(&done,
msecs_to_jiffies(2000))) {
@@ -2390,7 +2403,7 @@ void usb_hcd_resume_root_hub (struct usb_hcd *hcd)
if (hcd->rh_registered) {
pm_wakeup_event(&hcd->self.root_hub->dev, 0);
set_bit(HCD_FLAG_WAKEUP_PENDING, &hcd->flags);
- queue_work(pm_wq, &hcd->wakeup_work);
+ queue_work(system_freezable_wq, &hcd->wakeup_work);
}
spin_unlock_irqrestore (&hcd_root_hub_lock, flags);
}
@@ -2554,16 +2567,14 @@ struct usb_hcd *__usb_create_hcd(const struct hc_driver *driver,
if (!hcd)
return NULL;
if (primary_hcd == NULL) {
- hcd->address0_mutex = kmalloc(sizeof(*hcd->address0_mutex),
- GFP_KERNEL);
+ hcd->address0_mutex = kmalloc_obj(*hcd->address0_mutex);
if (!hcd->address0_mutex) {
kfree(hcd);
dev_dbg(dev, "hcd address0 mutex alloc failed\n");
return NULL;
}
mutex_init(hcd->address0_mutex);
- hcd->bandwidth_mutex = kmalloc(sizeof(*hcd->bandwidth_mutex),
- GFP_KERNEL);
+ hcd->bandwidth_mutex = kmalloc_obj(*hcd->bandwidth_mutex);
if (!hcd->bandwidth_mutex) {
kfree(hcd->address0_mutex);
kfree(hcd);
@@ -2679,18 +2690,18 @@ static void hcd_release(struct kref *kref)
kfree(hcd);
}
-struct usb_hcd *usb_get_hcd (struct usb_hcd *hcd)
+struct usb_hcd *usb_get_hcd(struct usb_hcd *hcd)
{
if (hcd)
- kref_get (&hcd->kref);
+ kref_get(&hcd->kref);
return hcd;
}
EXPORT_SYMBOL_GPL(usb_get_hcd);
-void usb_put_hcd (struct usb_hcd *hcd)
+void usb_put_hcd(struct usb_hcd *hcd)
{
if (hcd)
- kref_put (&hcd->kref, hcd_release);
+ kref_put(&hcd->kref, hcd_release);
}
EXPORT_SYMBOL_GPL(usb_put_hcd);
@@ -2768,14 +2779,14 @@ static void usb_stop_hcd(struct usb_hcd *hcd)
{
hcd->rh_pollable = 0;
clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
- del_timer_sync(&hcd->rh_timer);
+ timer_delete_sync(&hcd->rh_timer);
hcd->driver->stop(hcd);
hcd->state = HC_STATE_HALT;
/* In case the HCD restarted the timer, stop it again. */
clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
- del_timer_sync(&hcd->rh_timer);
+ timer_delete_sync(&hcd->rh_timer);
}
/**
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index dcba4281ea48..24960ba9caa9 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -28,6 +28,7 @@
#include <linux/usb/otg.h>
#include <linux/usb/quirks.h>
#include <linux/workqueue.h>
+#include <linux/minmax.h>
#include <linux/mutex.h>
#include <linux/random.h>
#include <linux/pm_qos.h>
@@ -40,6 +41,7 @@
#include "hub.h"
#include "phy.h"
#include "otg_productlist.h"
+#include "trace.h"
#define USB_VENDOR_GENESYS_LOGIC 0x05e3
#define USB_VENDOR_SMSC 0x0424
@@ -68,6 +70,12 @@
*/
#define USB_SHORT_SET_ADDRESS_REQ_TIMEOUT 500 /* ms */
+/*
+ * Give SS hubs 200ms time after wake to train downstream links before
+ * assuming no port activity and allowing hub to runtime suspend back.
+ */
+#define USB_SS_PORT_U0_WAKE_TIME 200 /* ms */
+
/* Protect struct usb_device->state and ->children members
* Note: Both are also protected by ->dev.sem, except that ->state can
* change to USB_STATE_NOTATTACHED even when the semaphore isn't held. */
@@ -271,10 +279,7 @@ static void usb_set_lpm_pel(struct usb_device *udev,
* device and the parent hub into U0. The exit latency is the bigger of
* the device exit latency or the hub exit latency.
*/
- if (udev_exit_latency > hub_exit_latency)
- first_link_pel = udev_exit_latency * 1000;
- else
- first_link_pel = hub_exit_latency * 1000;
+ first_link_pel = max(udev_exit_latency, hub_exit_latency) * 1000;
/*
* When the hub starts to receive the LFPS, there is a slight delay for
@@ -288,10 +293,7 @@ static void usb_set_lpm_pel(struct usb_device *udev,
* According to figure C-7 in the USB 3.0 spec, the PEL for this device
* is the greater of the two exit latencies.
*/
- if (first_link_pel > hub_pel)
- udev_lpm_params->pel = first_link_pel;
- else
- udev_lpm_params->pel = hub_pel;
+ udev_lpm_params->pel = max(first_link_pel, hub_pel);
}
/*
@@ -697,7 +699,7 @@ static void hub_resubmit_irq_urb(struct usb_hub *hub)
static void hub_retry_irq_urb(struct timer_list *t)
{
- struct usb_hub *hub = from_timer(hub, t, irq_urb_retry);
+ struct usb_hub *hub = timer_container_of(hub, t, irq_urb_retry);
hub_resubmit_irq_urb(hub);
}
@@ -927,7 +929,7 @@ int usb_hub_clear_tt_buffer(struct urb *urb)
* since each TT has "at least two" buffers that can need it (and
* there can be many TTs per hub). even if they're uncommon.
*/
- clear = kmalloc(sizeof *clear, GFP_ATOMIC);
+ clear = kmalloc_obj(*clear, GFP_ATOMIC);
if (clear == NULL) {
dev_err(&udev->dev, "can't save CLEAR_TT_BUFFER state\n");
/* FIXME recover somehow ... RESET_TT? */
@@ -1095,6 +1097,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
goto init2;
goto init3;
}
+
hub_get(hub);
/* The superspeed hub except for root hub has to use Hub Depth
@@ -1343,6 +1346,17 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
device_unlock(&hdev->dev);
}
+ if (type == HUB_RESUME && hub_is_superspeed(hub->hdev)) {
+ /* give usb3 downstream links training time after hub resume */
+ usb_autopm_get_interface_no_resume(
+ to_usb_interface(hub->intfdev));
+
+ queue_delayed_work(system_power_efficient_wq,
+ &hub->post_resume_work,
+ msecs_to_jiffies(USB_SS_PORT_U0_WAKE_TIME));
+ return;
+ }
+
hub_put(hub);
}
@@ -1361,6 +1375,14 @@ static void hub_init_func3(struct work_struct *ws)
hub_activate(hub, HUB_INIT3);
}
+static void hub_post_resume(struct work_struct *ws)
+{
+ struct usb_hub *hub = container_of(ws, struct usb_hub, post_resume_work.work);
+
+ usb_autopm_put_interface_async(to_usb_interface(hub->intfdev));
+ hub_put(hub);
+}
+
enum hub_quiescing_type {
HUB_DISCONNECT, HUB_PRE_RESET, HUB_SUSPEND
};
@@ -1385,7 +1407,8 @@ static void hub_quiesce(struct usb_hub *hub, enum hub_quiescing_type type)
}
/* Stop hub_wq and related activity */
- del_timer_sync(&hub->irq_urb_retry);
+ timer_delete_sync(&hub->irq_urb_retry);
+ flush_delayed_work(&hub->post_resume_work);
usb_kill_urb(hub->urb);
if (hub->has_indicators)
cancel_delayed_work_sync(&hub->leds);
@@ -1438,20 +1461,20 @@ static int hub_configure(struct usb_hub *hub,
unsigned full_load;
unsigned maxchild;
- hub->buffer = kmalloc(sizeof(*hub->buffer), GFP_KERNEL);
+ hub->buffer = kmalloc_obj(*hub->buffer);
if (!hub->buffer) {
ret = -ENOMEM;
goto fail;
}
- hub->status = kmalloc(sizeof(*hub->status), GFP_KERNEL);
+ hub->status = kmalloc_obj(*hub->status);
if (!hub->status) {
ret = -ENOMEM;
goto fail;
}
mutex_init(&hub->status_mutex);
- hub->descriptor = kzalloc(sizeof(*hub->descriptor), GFP_KERNEL);
+ hub->descriptor = kzalloc_obj(*hub->descriptor);
if (!hub->descriptor) {
ret = -ENOMEM;
goto fail;
@@ -1499,7 +1522,7 @@ static int hub_configure(struct usb_hub *hub,
dev_info(hub_dev, "%d port%s detected\n", maxchild,
str_plural(maxchild));
- hub->ports = kcalloc(maxchild, sizeof(struct usb_port *), GFP_KERNEL);
+ hub->ports = kzalloc_objs(struct usb_port *, maxchild);
if (!hub->ports) {
ret = -ENOMEM;
goto fail;
@@ -1935,7 +1958,7 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
/* We found a hub */
dev_info(&intf->dev, "USB hub found\n");
- hub = kzalloc(sizeof(*hub), GFP_KERNEL);
+ hub = kzalloc_obj(*hub);
if (!hub)
return -ENOMEM;
@@ -1944,6 +1967,7 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
hub->hdev = hdev;
INIT_DELAYED_WORK(&hub->leds, led_work);
INIT_DELAYED_WORK(&hub->init_work, NULL);
+ INIT_DELAYED_WORK(&hub->post_resume_work, hub_post_resume);
INIT_WORK(&hub->events, hub_event);
INIT_LIST_HEAD(&hub->onboard_devs);
spin_lock_init(&hub->irq_urb_lock);
@@ -2119,6 +2143,21 @@ static void update_port_device_state(struct usb_device *udev)
}
}
+static void update_usb_device_state(struct usb_device *udev,
+ enum usb_device_state new_state)
+{
+ if (udev->state == USB_STATE_SUSPENDED &&
+ new_state != USB_STATE_SUSPENDED)
+ udev->active_duration -= jiffies;
+ else if (new_state == USB_STATE_SUSPENDED &&
+ udev->state != USB_STATE_SUSPENDED)
+ udev->active_duration += jiffies;
+
+ udev->state = new_state;
+ update_port_device_state(udev);
+ trace_usb_set_device_state(udev);
+}
+
static void recursively_mark_NOTATTACHED(struct usb_device *udev)
{
struct usb_hub *hub = usb_hub_to_struct_hub(udev);
@@ -2128,10 +2167,7 @@ static void recursively_mark_NOTATTACHED(struct usb_device *udev)
if (hub->ports[i]->child)
recursively_mark_NOTATTACHED(hub->ports[i]->child);
}
- if (udev->state == USB_STATE_SUSPENDED)
- udev->active_duration -= jiffies;
- udev->state = USB_STATE_NOTATTACHED;
- update_port_device_state(udev);
+ update_usb_device_state(udev, USB_STATE_NOTATTACHED);
}
/**
@@ -2181,14 +2217,7 @@ void usb_set_device_state(struct usb_device *udev,
else
wakeup = 0;
}
- if (udev->state == USB_STATE_SUSPENDED &&
- new_state != USB_STATE_SUSPENDED)
- udev->active_duration -= jiffies;
- else if (new_state == USB_STATE_SUSPENDED &&
- udev->state != USB_STATE_SUSPENDED)
- udev->active_duration += jiffies;
- udev->state = new_state;
- update_port_device_state(udev);
+ update_usb_device_state(udev, new_state);
} else
recursively_mark_NOTATTACHED(udev);
spin_unlock_irqrestore(&device_state_lock, flags);
@@ -2337,6 +2366,9 @@ void usb_disconnect(struct usb_device **pdev)
usb_remove_ep_devs(&udev->ep0);
usb_unlock_device(udev);
+ if (udev->usb4_link)
+ device_link_del(udev->usb4_link);
+
/* Unregister the device. The device driver is responsible
* for de-configuring the device and invoking the remove-device
* notifier chain (used by usbfs and possibly others).
@@ -4110,7 +4142,7 @@ static int usb_req_set_sel(struct usb_device *udev)
* which may be initiated by an error path of a mass storage driver.
* Therefore, use GFP_NOIO.
*/
- sel_values = kmalloc(sizeof *(sel_values), GFP_NOIO);
+ sel_values = kmalloc_obj(*(sel_values), GFP_NOIO);
if (!sel_values)
return -ENOMEM;
@@ -4160,7 +4192,7 @@ static int usb_set_device_initiated_lpm(struct usb_device *udev,
"for unconfigured device.\n",
__func__, str_enable_disable(enable),
usb3_lpm_names[state]);
- return 0;
+ return -EINVAL;
}
if (enable) {
@@ -4234,9 +4266,9 @@ static int usb_set_lpm_timeout(struct usb_device *udev,
}
/*
- * Don't allow device intiated U1/U2 if the system exit latency + one bus
- * interval is greater than the minimum service interval of any active
- * periodic endpoint. See USB 3.2 section 9.4.9
+ * Don't allow device intiated U1/U2 if device isn't in the configured state,
+ * or the system exit latency + one bus interval is greater than the minimum
+ * service interval of any active periodic endpoint. See USB 3.2 section 9.4.9
*/
static bool usb_device_may_initiate_lpm(struct usb_device *udev,
enum usb3_link_state state)
@@ -4244,7 +4276,7 @@ static bool usb_device_may_initiate_lpm(struct usb_device *udev,
unsigned int sel; /* us */
int i, j;
- if (!udev->lpm_devinit_allow)
+ if (!udev->lpm_devinit_allow || !udev->actconfig)
return false;
if (state == USB3_LPM_U1)
@@ -4292,7 +4324,7 @@ static bool usb_device_may_initiate_lpm(struct usb_device *udev,
* driver know about it. If that call fails, it should be harmless, and just
* take up more slightly more bus bandwidth for unnecessary U1/U2 exit latency.
*/
-static void usb_enable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
+static int usb_enable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
enum usb3_link_state state)
{
int timeout;
@@ -4301,7 +4333,7 @@ static void usb_enable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
/* Skip if the device BOS descriptor couldn't be read */
if (!udev->bos)
- return;
+ return -EINVAL;
u1_mel = udev->bos->ss_cap->bU1devExitLat;
u2_mel = udev->bos->ss_cap->bU2DevExitLat;
@@ -4312,7 +4344,7 @@ static void usb_enable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
*/
if ((state == USB3_LPM_U1 && u1_mel == 0) ||
(state == USB3_LPM_U2 && u2_mel == 0))
- return;
+ return -EINVAL;
/* We allow the host controller to set the U1/U2 timeout internally
* first, so that it can change its schedule to account for the
@@ -4323,13 +4355,13 @@ static void usb_enable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
/* xHCI host controller doesn't want to enable this LPM state. */
if (timeout == 0)
- return;
+ return -EINVAL;
if (timeout < 0) {
dev_warn(&udev->dev, "Could not enable %s link state, "
"xHCI error %i.\n", usb3_lpm_names[state],
timeout);
- return;
+ return timeout;
}
if (usb_set_lpm_timeout(udev, state, timeout)) {
@@ -4338,29 +4370,15 @@ static void usb_enable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
* host know that this link state won't be enabled.
*/
hcd->driver->disable_usb3_lpm_timeout(hcd, udev, state);
- return;
- }
-
- /* Only a configured device will accept the Set Feature
- * U1/U2_ENABLE
- */
- if (udev->actconfig &&
- usb_device_may_initiate_lpm(udev, state)) {
- if (usb_set_device_initiated_lpm(udev, state, true)) {
- /*
- * Request to enable device initiated U1/U2 failed,
- * better to turn off lpm in this case.
- */
- usb_set_lpm_timeout(udev, state, 0);
- hcd->driver->disable_usb3_lpm_timeout(hcd, udev, state);
- return;
- }
+ return -EBUSY;
}
if (state == USB3_LPM_U1)
udev->usb3_lpm_u1_enabled = 1;
else if (state == USB3_LPM_U2)
udev->usb3_lpm_u2_enabled = 1;
+
+ return 0;
}
/*
* Disable the hub-initiated U1/U2 idle timeouts, and disable device-initiated
@@ -4393,8 +4411,6 @@ static int usb_disable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
if (usb_set_lpm_timeout(udev, state, 0))
return -EBUSY;
- usb_set_device_initiated_lpm(udev, state, false);
-
if (hcd->driver->disable_usb3_lpm_timeout(hcd, udev, state))
dev_warn(&udev->dev, "Could not disable xHCI %s timeout, "
"bus schedule bandwidth may be impacted.\n",
@@ -4424,6 +4440,7 @@ static int usb_disable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
int usb_disable_lpm(struct usb_device *udev)
{
struct usb_hcd *hcd;
+ int err;
if (!udev || !udev->parent ||
udev->speed < USB_SPEED_SUPER ||
@@ -4441,14 +4458,19 @@ int usb_disable_lpm(struct usb_device *udev)
/* If LPM is enabled, attempt to disable it. */
if (usb_disable_link_state(hcd, udev, USB3_LPM_U1))
- goto enable_lpm;
+ goto disable_failed;
if (usb_disable_link_state(hcd, udev, USB3_LPM_U2))
- goto enable_lpm;
+ goto disable_failed;
+
+ err = usb_set_device_initiated_lpm(udev, USB3_LPM_U1, false);
+ if (!err)
+ usb_set_device_initiated_lpm(udev, USB3_LPM_U2, false);
return 0;
-enable_lpm:
- usb_enable_lpm(udev);
+disable_failed:
+ udev->lpm_disable_count--;
+
return -EBUSY;
}
EXPORT_SYMBOL_GPL(usb_disable_lpm);
@@ -4509,10 +4531,24 @@ void usb_enable_lpm(struct usb_device *udev)
port_dev = hub->ports[udev->portnum - 1];
if (port_dev->usb3_lpm_u1_permit)
- usb_enable_link_state(hcd, udev, USB3_LPM_U1);
+ if (usb_enable_link_state(hcd, udev, USB3_LPM_U1))
+ return;
if (port_dev->usb3_lpm_u2_permit)
- usb_enable_link_state(hcd, udev, USB3_LPM_U2);
+ if (usb_enable_link_state(hcd, udev, USB3_LPM_U2))
+ return;
+
+ /*
+ * Enable device initiated U1/U2 with a SetFeature(U1/U2_ENABLE) request
+ * if system exit latency is short enough and device is configured
+ */
+ if (usb_device_may_initiate_lpm(udev, USB3_LPM_U1)) {
+ if (usb_set_device_initiated_lpm(udev, USB3_LPM_U1, true))
+ return;
+
+ if (usb_device_may_initiate_lpm(udev, USB3_LPM_U2))
+ usb_set_device_initiated_lpm(udev, USB3_LPM_U2, true);
+ }
}
EXPORT_SYMBOL_GPL(usb_enable_lpm);
@@ -4708,8 +4744,6 @@ void usb_ep0_reinit(struct usb_device *udev)
}
EXPORT_SYMBOL_GPL(usb_ep0_reinit);
-#define usb_sndaddr0pipe() (PIPE_CONTROL << 30)
-
static int hub_set_address(struct usb_device *udev, int devnum)
{
int retval;
@@ -4733,7 +4767,7 @@ static int hub_set_address(struct usb_device *udev, int devnum)
if (hcd->driver->address_device)
retval = hcd->driver->address_device(hcd, udev, timeout_ms);
else
- retval = usb_control_msg(udev, usb_sndaddr0pipe(),
+ retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
USB_REQ_SET_ADDRESS, 0, devnum, 0,
NULL, 0, timeout_ms);
if (retval == 0) {
@@ -5202,7 +5236,7 @@ check_highspeed(struct usb_hub *hub, struct usb_device *udev, int port1)
if (udev->quirks & USB_QUIRK_DEVICE_QUALIFIER)
return;
- qual = kmalloc(sizeof *qual, GFP_KERNEL);
+ qual = kmalloc_obj(*qual);
if (qual == NULL)
return;
@@ -5718,6 +5752,7 @@ static void port_event(struct usb_hub *hub, int port1)
struct usb_device *hdev = hub->hdev;
u16 portstatus, portchange;
int i = 0;
+ int err;
connect_change = test_bit(port1, hub->change_bits);
clear_bit(port1, hub->event_bits);
@@ -5814,8 +5849,11 @@ static void port_event(struct usb_hub *hub, int port1)
} else if (!udev || !(portstatus & USB_PORT_STAT_CONNECTION)
|| udev->state == USB_STATE_NOTATTACHED) {
dev_dbg(&port_dev->dev, "do warm reset, port only\n");
- if (hub_port_reset(hub, port1, NULL,
- HUB_BH_RESET_TIME, true) < 0)
+ err = hub_port_reset(hub, port1, NULL,
+ HUB_BH_RESET_TIME, true);
+ if (!udev && err == -ENOTCONN)
+ connect_change = 0;
+ else if (err < 0)
hub_port_disable(hub, port1, 1);
} else {
dev_dbg(&port_dev->dev, "do warm reset, full device\n");
@@ -6040,7 +6078,7 @@ int usb_hub_init(void)
* device was gone before the EHCI controller had handed its port
* over to the companion full-speed controller.
*/
- hub_wq = alloc_workqueue("usb_hub_wq", WQ_FREEZABLE, 0);
+ hub_wq = alloc_workqueue("usb_hub_wq", WQ_FREEZABLE | WQ_PERCPU, 0);
if (hub_wq)
return 0;
@@ -6135,6 +6173,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
struct usb_hub *parent_hub;
struct usb_hcd *hcd = bus_to_hcd(udev->bus);
struct usb_device_descriptor descriptor;
+ struct usb_interface *intf;
struct usb_host_bos *bos;
int i, j, ret = 0;
int port1 = udev->portnum;
@@ -6192,6 +6231,18 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
if (!udev->actconfig)
goto done;
+ /*
+ * Some devices can't handle setting default altsetting 0 with a
+ * Set-Interface request. Disable host-side endpoints of those
+ * interfaces here. Enable and reset them back after host has set
+ * its internal endpoint structures during usb_hcd_alloc_bandwith()
+ */
+ for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
+ intf = udev->actconfig->interface[i];
+ if (intf->cur_altsetting->desc.bAlternateSetting == 0)
+ usb_disable_interface(udev, intf, true);
+ }
+
mutex_lock(hcd->bandwidth_mutex);
ret = usb_hcd_alloc_bandwidth(udev, udev->actconfig, NULL, NULL);
if (ret < 0) {
@@ -6223,12 +6274,11 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
*/
for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
struct usb_host_config *config = udev->actconfig;
- struct usb_interface *intf = config->interface[i];
struct usb_interface_descriptor *desc;
+ intf = config->interface[i];
desc = &intf->cur_altsetting->desc;
if (desc->bAlternateSetting == 0) {
- usb_disable_interface(udev, intf, true);
usb_enable_interface(udev, intf, true);
ret = 0;
} else {
diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h
index e6ae73f8a95d..9ebc5ef54a32 100644
--- a/drivers/usb/core/hub.h
+++ b/drivers/usb/core/hub.h
@@ -70,6 +70,7 @@ struct usb_hub {
u8 indicator[USB_MAXCHILDREN];
struct delayed_work leds;
struct delayed_work init_work;
+ struct delayed_work post_resume_work;
struct work_struct events;
spinlock_t irq_urb_lock;
struct timer_list irq_urb_retry;
diff --git a/drivers/usb/core/ledtrig-usbport.c b/drivers/usb/core/ledtrig-usbport.c
index 5e3c515991f3..8881644777a7 100644
--- a/drivers/usb/core/ledtrig-usbport.c
+++ b/drivers/usb/core/ledtrig-usbport.c
@@ -190,7 +190,7 @@ static int usbport_trig_add_port(struct usbport_trig_data *usbport_data,
size_t len;
int err;
- port = kzalloc(sizeof(*port), GFP_KERNEL);
+ port = kzalloc_obj(*port);
if (!port) {
err = -ENOMEM;
goto err_out;
@@ -305,7 +305,7 @@ static int usbport_trig_activate(struct led_classdev *led_cdev)
struct usbport_trig_data *usbport_data;
int err;
- usbport_data = kzalloc(sizeof(*usbport_data), GFP_KERNEL);
+ usbport_data = kzalloc_obj(*usbport_data);
if (!usbport_data)
return -ENOMEM;
usbport_data->led_cdev = led_cdev;
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index d2b2787be409..75e2bfd744a9 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -42,16 +42,19 @@ static void usb_api_blocking_completion(struct urb *urb)
/*
- * Starts urb and waits for completion or timeout. Note that this call
- * is NOT interruptible. Many device driver i/o requests should be
- * interruptible and therefore these drivers should implement their
- * own interruptible routines.
+ * Starts urb and waits for completion or timeout.
+ * Whether or not the wait is killable depends on the flag passed in.
+ * For example, compare usb_bulk_msg() and usb_bulk_msg_killable().
+ *
+ * For non-killable waits, we enforce a maximum limit on the timeout value.
*/
-static int usb_start_wait_urb(struct urb *urb, int timeout, int *actual_length)
+static int usb_start_wait_urb(struct urb *urb, int timeout, int *actual_length,
+ bool killable)
{
struct api_context ctx;
unsigned long expire;
int retval;
+ long rc;
init_completion(&ctx.done);
urb->context = &ctx;
@@ -60,13 +63,24 @@ static int usb_start_wait_urb(struct urb *urb, int timeout, int *actual_length)
if (unlikely(retval))
goto out;
- expire = timeout ? msecs_to_jiffies(timeout) : MAX_SCHEDULE_TIMEOUT;
- if (!wait_for_completion_timeout(&ctx.done, expire)) {
+ if (!killable && (timeout <= 0 || timeout > USB_MAX_SYNCHRONOUS_TIMEOUT))
+ timeout = USB_MAX_SYNCHRONOUS_TIMEOUT;
+ expire = (timeout > 0) ? msecs_to_jiffies(timeout) : MAX_SCHEDULE_TIMEOUT;
+ if (killable)
+ rc = wait_for_completion_killable_timeout(&ctx.done, expire);
+ else
+ rc = wait_for_completion_timeout(&ctx.done, expire);
+ if (rc <= 0) {
usb_kill_urb(urb);
- retval = (ctx.status == -ENOENT ? -ETIMEDOUT : ctx.status);
+ if (ctx.status != -ENOENT)
+ retval = ctx.status;
+ else if (rc == 0)
+ retval = -ETIMEDOUT;
+ else
+ retval = rc;
dev_dbg(&urb->dev->dev,
- "%s timed out on ep%d%s len=%u/%u\n",
+ "%s timed out or killed on ep%d%s len=%u/%u\n",
current->comm,
usb_endpoint_num(&urb->ep->desc),
usb_urb_dir_in(urb) ? "in" : "out",
@@ -100,7 +114,7 @@ static int usb_internal_control_msg(struct usb_device *usb_dev,
usb_fill_control_urb(urb, usb_dev, pipe, (unsigned char *)cmd, data,
len, usb_api_blocking_completion, NULL);
- retv = usb_start_wait_urb(urb, timeout, &length);
+ retv = usb_start_wait_urb(urb, timeout, &length, false);
if (retv < 0)
return retv;
else
@@ -117,8 +131,7 @@ static int usb_internal_control_msg(struct usb_device *usb_dev,
* @index: USB message index value
* @data: pointer to the data to send
* @size: length in bytes of the data to send
- * @timeout: time in msecs to wait for the message to complete before timing
- * out (if 0 the wait is forever)
+ * @timeout: time in msecs to wait for the message to complete before timing out
*
* Context: task context, might sleep.
*
@@ -141,7 +154,7 @@ int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request,
struct usb_ctrlrequest *dr;
int ret;
- dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_NOIO);
+ dr = kmalloc_obj(struct usb_ctrlrequest, GFP_NOIO);
if (!dr)
return -ENOMEM;
@@ -173,8 +186,7 @@ EXPORT_SYMBOL_GPL(usb_control_msg);
* @index: USB message index value
* @driver_data: pointer to the data to send
* @size: length in bytes of the data to send
- * @timeout: time in msecs to wait for the message to complete before timing
- * out (if 0 the wait is forever)
+ * @timeout: time in msecs to wait for the message to complete before timing out
* @memflags: the flags for memory allocation for buffers
*
* Context: !in_interrupt ()
@@ -232,8 +244,7 @@ EXPORT_SYMBOL_GPL(usb_control_msg_send);
* @index: USB message index value
* @driver_data: pointer to the data to be filled in by the message
* @size: length in bytes of the data to be received
- * @timeout: time in msecs to wait for the message to complete before timing
- * out (if 0 the wait is forever)
+ * @timeout: time in msecs to wait for the message to complete before timing out
* @memflags: the flags for memory allocation for buffers
*
* Context: !in_interrupt ()
@@ -304,8 +315,7 @@ EXPORT_SYMBOL_GPL(usb_control_msg_recv);
* @len: length in bytes of the data to send
* @actual_length: pointer to a location to put the actual length transferred
* in bytes
- * @timeout: time in msecs to wait for the message to complete before
- * timing out (if 0 the wait is forever)
+ * @timeout: time in msecs to wait for the message to complete before timing out
*
* Context: task context, might sleep.
*
@@ -337,8 +347,7 @@ EXPORT_SYMBOL_GPL(usb_interrupt_msg);
* @len: length in bytes of the data to send
* @actual_length: pointer to a location to put the actual length transferred
* in bytes
- * @timeout: time in msecs to wait for the message to complete before
- * timing out (if 0 the wait is forever)
+ * @timeout: time in msecs to wait for the message to complete before timing out
*
* Context: task context, might sleep.
*
@@ -385,10 +394,59 @@ int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe,
usb_fill_bulk_urb(urb, usb_dev, pipe, data, len,
usb_api_blocking_completion, NULL);
- return usb_start_wait_urb(urb, timeout, actual_length);
+ return usb_start_wait_urb(urb, timeout, actual_length, false);
}
EXPORT_SYMBOL_GPL(usb_bulk_msg);
+/**
+ * usb_bulk_msg_killable - Builds a bulk urb, sends it off and waits for completion in a killable state
+ * @usb_dev: pointer to the usb device to send the message to
+ * @pipe: endpoint "pipe" to send the message to
+ * @data: pointer to the data to send
+ * @len: length in bytes of the data to send
+ * @actual_length: pointer to a location to put the actual length transferred
+ * in bytes
+ * @timeout: time in msecs to wait for the message to complete before
+ * timing out (if <= 0, the wait is as long as possible)
+ *
+ * Context: task context, might sleep.
+ *
+ * This function is just like usb_blk_msg(), except that it waits in a
+ * killable state and there is no limit on the timeout length.
+ *
+ * Return:
+ * If successful, 0. Otherwise a negative error number. The number of actual
+ * bytes transferred will be stored in the @actual_length parameter.
+ *
+ */
+int usb_bulk_msg_killable(struct usb_device *usb_dev, unsigned int pipe,
+ void *data, int len, int *actual_length, int timeout)
+{
+ struct urb *urb;
+ struct usb_host_endpoint *ep;
+
+ ep = usb_pipe_endpoint(usb_dev, pipe);
+ if (!ep || len < 0)
+ return -EINVAL;
+
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb)
+ return -ENOMEM;
+
+ if ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+ USB_ENDPOINT_XFER_INT) {
+ pipe = (pipe & ~(3 << 30)) | (PIPE_INTERRUPT << 30);
+ usb_fill_int_urb(urb, usb_dev, pipe, data, len,
+ usb_api_blocking_completion, NULL,
+ ep->desc.bInterval);
+ } else
+ usb_fill_bulk_urb(urb, usb_dev, pipe, data, len,
+ usb_api_blocking_completion, NULL);
+
+ return usb_start_wait_urb(urb, timeout, actual_length, true);
+}
+EXPORT_SYMBOL_GPL(usb_bulk_msg_killable);
+
/*-------------------------------------------------------------------*/
static void sg_clean(struct usb_sg_request *io)
@@ -526,7 +584,7 @@ int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev,
}
/* initialize all the urbs we'll use */
- io->urbs = kmalloc_array(io->entries, sizeof(*io->urbs), mem_flags);
+ io->urbs = kmalloc_objs(*io->urbs, io->entries, mem_flags);
if (!io->urbs)
goto nomem;
@@ -1005,7 +1063,7 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size)
}
EXPORT_SYMBOL_GPL(usb_string);
-/* one UTF-8-encoded 16-bit character has at most three bytes */
+/* one 16-bit character, when UTF-8-encoded, has at most three bytes */
#define MAX_USB_STRING_SIZE (127 * 3 + 1)
/**
@@ -1026,16 +1084,18 @@ char *usb_cache_string(struct usb_device *udev, int index)
return NULL;
buf = kmalloc(MAX_USB_STRING_SIZE, GFP_NOIO);
- if (buf) {
- len = usb_string(udev, index, buf, MAX_USB_STRING_SIZE);
- if (len > 0) {
- smallbuf = kmalloc(++len, GFP_NOIO);
- if (!smallbuf)
- return buf;
- memcpy(smallbuf, buf, len);
- }
+ if (!buf)
+ return NULL;
+
+ len = usb_string(udev, index, buf, MAX_USB_STRING_SIZE);
+ if (len <= 0) {
kfree(buf);
+ return NULL;
}
+
+ smallbuf = krealloc(buf, len + 1, GFP_NOIO);
+ if (unlikely(!smallbuf))
+ return buf;
return smallbuf;
}
EXPORT_SYMBOL_GPL(usb_cache_string);
@@ -1058,7 +1118,7 @@ struct usb_device_descriptor *usb_get_device_descriptor(struct usb_device *udev)
struct usb_device_descriptor *desc;
int ret;
- desc = kmalloc(sizeof(*desc), GFP_NOIO);
+ desc = kmalloc_obj(*desc, GFP_NOIO);
if (!desc)
return ERR_PTR(-ENOMEM);
@@ -2028,15 +2088,13 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
n = nintf = 0;
if (cp) {
nintf = cp->desc.bNumInterfaces;
- new_interfaces = kmalloc_array(nintf, sizeof(*new_interfaces),
- GFP_NOIO);
+ new_interfaces = kmalloc_objs(*new_interfaces, nintf, GFP_NOIO);
if (!new_interfaces)
return -ENOMEM;
for (; n < nintf; ++n) {
- new_interfaces[n] = kzalloc(
- sizeof(struct usb_interface),
- GFP_NOIO);
+ new_interfaces[n] = kzalloc_obj(struct usb_interface,
+ GFP_NOIO);
if (!new_interfaces[n]) {
ret = -ENOMEM;
free_interfaces:
@@ -2289,7 +2347,7 @@ int usb_driver_set_configuration(struct usb_device *udev, int config)
{
struct set_config_request *req;
- req = kmalloc(sizeof(*req), GFP_KERNEL);
+ req = kmalloc_obj(*req);
if (!req)
return -ENOMEM;
req->udev = udev;
@@ -2431,7 +2489,7 @@ int cdc_parse_cdc_header(struct usb_cdc_parsed_header *hdr,
break;
case USB_CDC_MBIM_EXTENDED_TYPE:
if (elength < sizeof(struct usb_cdc_mbim_extended_desc))
- break;
+ goto next_desc;
hdr->usb_cdc_mbim_extended_desc =
(struct usb_cdc_mbim_extended_desc *)buffer;
break;
diff --git a/drivers/usb/core/of.c b/drivers/usb/core/of.c
index 763e4122ed5b..a6ee20d8774e 100644
--- a/drivers/usb/core/of.c
+++ b/drivers/usb/core/of.c
@@ -79,17 +79,13 @@ EXPORT_SYMBOL_GPL(usb_of_has_combined_node);
static bool usb_of_has_devices_or_graph(const struct usb_device *hub)
{
const struct device_node *np = hub->dev.of_node;
- struct device_node *child;
if (of_graph_is_present(np))
return true;
- for_each_child_of_node(np, child) {
- if (of_property_present(child, "reg")) {
- of_node_put(child);
+ for_each_child_of_node_scoped(np, child)
+ if (of_property_present(child, "reg"))
return true;
- }
- }
return false;
}
diff --git a/drivers/usb/core/offload.c b/drivers/usb/core/offload.c
new file mode 100644
index 000000000000..9db3cfedd29c
--- /dev/null
+++ b/drivers/usb/core/offload.c
@@ -0,0 +1,152 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * offload.c - USB offload related functions
+ *
+ * Copyright (c) 2025, Google LLC.
+ *
+ * Author: Guan-Yu Lin
+ */
+
+#include <linux/usb.h>
+
+#include "usb.h"
+
+/**
+ * usb_offload_get - increment the offload_usage of a USB device
+ * @udev: the USB device to increment its offload_usage
+ *
+ * Incrementing the offload_usage of a usb_device indicates that offload is
+ * enabled on this usb_device; that is, another entity is actively handling USB
+ * transfers. This information allows the USB driver to adjust its power
+ * management policy based on offload activity.
+ *
+ * Return: 0 on success. A negative error code otherwise.
+ */
+int usb_offload_get(struct usb_device *udev)
+{
+ int ret = 0;
+
+ if (!usb_get_dev(udev))
+ return -ENODEV;
+
+ if (pm_runtime_get_if_active(&udev->dev) != 1) {
+ ret = -EBUSY;
+ goto err_rpm;
+ }
+
+ spin_lock(&udev->offload_lock);
+
+ if (udev->offload_pm_locked) {
+ ret = -EAGAIN;
+ goto err;
+ }
+
+ udev->offload_usage++;
+
+err:
+ spin_unlock(&udev->offload_lock);
+ pm_runtime_put_autosuspend(&udev->dev);
+err_rpm:
+ usb_put_dev(udev);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_offload_get);
+
+/**
+ * usb_offload_put - drop the offload_usage of a USB device
+ * @udev: the USB device to drop its offload_usage
+ *
+ * The inverse operation of usb_offload_get, which drops the offload_usage of
+ * a USB device. This information allows the USB driver to adjust its power
+ * management policy based on offload activity.
+ *
+ * Return: 0 on success. A negative error code otherwise.
+ */
+int usb_offload_put(struct usb_device *udev)
+{
+ int ret = 0;
+
+ if (!usb_get_dev(udev))
+ return -ENODEV;
+
+ if (pm_runtime_get_if_active(&udev->dev) != 1) {
+ ret = -EBUSY;
+ goto err_rpm;
+ }
+
+ spin_lock(&udev->offload_lock);
+
+ if (udev->offload_pm_locked) {
+ ret = -EAGAIN;
+ goto err;
+ }
+
+ /* Drop the count when it wasn't 0, ignore the operation otherwise. */
+ if (udev->offload_usage)
+ udev->offload_usage--;
+
+err:
+ spin_unlock(&udev->offload_lock);
+ pm_runtime_put_autosuspend(&udev->dev);
+err_rpm:
+ usb_put_dev(udev);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_offload_put);
+
+/**
+ * usb_offload_check - check offload activities on a USB device
+ * @udev: the USB device to check its offload activity.
+ *
+ * Check if there are any offload activity on the USB device right now. This
+ * information could be used for power management or other forms of resource
+ * management.
+ *
+ * The caller must hold @udev's device lock. In addition, the caller should
+ * ensure the device itself and the downstream usb devices are all marked as
+ * "offload_pm_locked" to ensure the correctness of the return value.
+ *
+ * Returns true on any offload activity, false otherwise.
+ */
+bool usb_offload_check(struct usb_device *udev) __must_hold(&udev->dev->mutex)
+{
+ struct usb_device *child;
+ bool active = false;
+ int port1;
+
+ if (udev->offload_usage)
+ return true;
+
+ usb_hub_for_each_child(udev, port1, child) {
+ usb_lock_device(child);
+ active = usb_offload_check(child);
+ usb_unlock_device(child);
+
+ if (active)
+ break;
+ }
+
+ return active;
+}
+EXPORT_SYMBOL_GPL(usb_offload_check);
+
+/**
+ * usb_offload_set_pm_locked - set the PM lock state of a USB device
+ * @udev: the USB device to modify
+ * @locked: the new lock state
+ *
+ * Setting @locked to true prevents offload_usage from being modified. This
+ * ensures that offload activities cannot be started or stopped during critical
+ * power management transitions, maintaining a stable state for the duration
+ * of the transition.
+ */
+void usb_offload_set_pm_locked(struct usb_device *udev, bool locked)
+{
+ spin_lock(&udev->offload_lock);
+ udev->offload_pm_locked = locked;
+ spin_unlock(&udev->offload_lock);
+}
+EXPORT_SYMBOL_GPL(usb_offload_set_pm_locked);
diff --git a/drivers/usb/core/phy.c b/drivers/usb/core/phy.c
index faa20054ad5a..4d966cc9cdc9 100644
--- a/drivers/usb/core/phy.c
+++ b/drivers/usb/core/phy.c
@@ -114,7 +114,7 @@ EXPORT_SYMBOL_GPL(usb_phy_roothub_alloc);
struct usb_phy_roothub *usb_phy_roothub_alloc_usb3_phy(struct device *dev)
{
struct usb_phy_roothub *phy_roothub;
- int num_phys;
+ int num_phys, usb2_phy_index;
if (!IS_ENABLED(CONFIG_GENERIC_PHY))
return NULL;
@@ -124,6 +124,16 @@ struct usb_phy_roothub *usb_phy_roothub_alloc_usb3_phy(struct device *dev)
if (num_phys <= 0)
return NULL;
+ /*
+ * If 'usb2-phy' is not present, usb_phy_roothub_alloc() added
+ * all PHYs to the primary HCD's phy_roothub already, so skip
+ * adding 'usb3-phy' here to avoid double use of that.
+ */
+ usb2_phy_index = of_property_match_string(dev->of_node, "phy-names",
+ "usb2-phy");
+ if (usb2_phy_index < 0)
+ return NULL;
+
phy_roothub = devm_kzalloc(dev, sizeof(*phy_roothub), GFP_KERNEL);
if (!phy_roothub)
return ERR_PTR(-ENOMEM);
@@ -200,16 +210,10 @@ int usb_phy_roothub_set_mode(struct usb_phy_roothub *phy_roothub,
list_for_each_entry(roothub_entry, head, list) {
err = phy_set_mode(roothub_entry->phy, mode);
if (err)
- goto err_out;
+ return err;
}
return 0;
-
-err_out:
- list_for_each_entry_continue_reverse(roothub_entry, head, list)
- phy_power_off(roothub_entry->phy);
-
- return err;
}
EXPORT_SYMBOL_GPL(usb_phy_roothub_set_mode);
diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c
index f54198171b6a..b1364f0c384c 100644
--- a/drivers/usb/core/port.c
+++ b/drivers/usb/core/port.c
@@ -21,6 +21,20 @@ static int usb_port_block_power_off;
static const struct attribute_group *port_dev_group[];
+static bool usb_port_allow_power_off(struct usb_device *hdev,
+ struct usb_hub *hub,
+ struct usb_port *port_dev)
+{
+ if (hub_is_port_power_switchable(hub))
+ return true;
+
+ if (!IS_ENABLED(CONFIG_ACPI))
+ return false;
+
+ return port_dev->connect_type == USB_PORT_CONNECT_TYPE_HARD_WIRED &&
+ usb_acpi_power_manageable(hdev, port_dev->portnum - 1);
+}
+
static ssize_t early_stop_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -141,6 +155,7 @@ static ssize_t disable_store(struct device *dev, struct device_attribute *attr,
usb_disconnect(&port_dev->child);
rc = usb_hub_set_port_power(hdev, hub, port1, !disabled);
+ msleep(2 * hub_power_on_good_delay(hub));
if (disabled) {
usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_CONNECTION);
@@ -739,11 +754,11 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1)
struct usb_device *hdev = hub->hdev;
int retval;
- port_dev = kzalloc(sizeof(*port_dev), GFP_KERNEL);
+ port_dev = kzalloc_obj(*port_dev);
if (!port_dev)
return -ENOMEM;
- port_dev->req = kzalloc(sizeof(*(port_dev->req)), GFP_KERNEL);
+ port_dev->req = kzalloc_obj(*(port_dev->req));
if (!port_dev->req) {
kfree(port_dev);
return -ENOMEM;
@@ -805,10 +820,10 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1)
device_enable_async_suspend(&port_dev->dev);
/*
- * Keep hidden the ability to enable port-poweroff if the hub
- * does not support power switching.
+ * Keep hidden the ability to enable port-poweroff if neither the
+ * USB hub nor platform firmware can manage downstream port power.
*/
- if (!hub_is_port_power_switchable(hub))
+ if (!usb_port_allow_power_off(hdev, hub, port_dev))
return 0;
/* Attempt to let userspace take over the policy. */
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index 8efbacc5bc34..0ffdaefba508 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -61,8 +61,7 @@ static int quirks_param_set(const char *value, const struct kernel_param *kp)
quirk_list = NULL;
}
- quirk_list = kcalloc(quirk_count, sizeof(struct quirk_entry),
- GFP_KERNEL);
+ quirk_list = kzalloc_objs(struct quirk_entry, quirk_count);
if (!quirk_list) {
quirk_count = 0;
mutex_unlock(&quirk_mutex);
@@ -141,6 +140,8 @@ static int quirks_param_set(const char *value, const struct kernel_param *kp)
case 'p':
flags |= USB_QUIRK_SHORT_SET_ADDRESS_REQ_TIMEOUT;
break;
+ case 'q':
+ flags |= USB_QUIRK_FORCE_ONE_CONFIG;
/* Ignore unrecognized flag characters */
}
}
@@ -208,6 +209,10 @@ static const struct usb_device_id usb_quirk_list[] = {
/* HP v222w 16GB Mini USB Drive */
{ USB_DEVICE(0x03f0, 0x3f40), .driver_info = USB_QUIRK_DELAY_INIT },
+ /* Huawei 4G LTE module ME906S */
+ { USB_DEVICE(0x03f0, 0xa31d), .driver_info =
+ USB_QUIRK_DISCONNECT_SUSPEND },
+
/* Creative SB Audigy 2 NX */
{ USB_DEVICE(0x041e, 0x3020), .driver_info = USB_QUIRK_RESET_RESUME },
@@ -227,7 +232,8 @@ static const struct usb_device_id usb_quirk_list[] = {
{ USB_DEVICE(0x046a, 0x0023), .driver_info = USB_QUIRK_RESET_RESUME },
/* Logitech HD Webcam C270 */
- { USB_DEVICE(0x046d, 0x0825), .driver_info = USB_QUIRK_RESET_RESUME },
+ { USB_DEVICE(0x046d, 0x0825), .driver_info = USB_QUIRK_RESET_RESUME |
+ USB_QUIRK_NO_LPM},
/* Logitech HD Pro Webcams C920, C920-C, C922, C925e and C930e */
{ USB_DEVICE(0x046d, 0x082d), .driver_info = USB_QUIRK_DELAY_INIT },
@@ -369,6 +375,16 @@ static const struct usb_device_id usb_quirk_list[] = {
{ USB_DEVICE(0x0781, 0x5583), .driver_info = USB_QUIRK_NO_LPM },
{ USB_DEVICE(0x0781, 0x5591), .driver_info = USB_QUIRK_NO_LPM },
+ /* SanDisk Corp. SanDisk 3.2Gen1 */
+ { USB_DEVICE(0x0781, 0x5596), .driver_info = USB_QUIRK_DELAY_INIT },
+ { USB_DEVICE(0x0781, 0x55a3), .driver_info = USB_QUIRK_DELAY_INIT },
+
+ /* SanDisk Extreme 55AE */
+ { USB_DEVICE(0x0781, 0x55ae), .driver_info = USB_QUIRK_NO_LPM },
+
+ /* Avermedia Live Gamer Ultra 2.1 (GC553G2) - BOS descriptor fetch hangs at SuperSpeed Plus */
+ { USB_DEVICE(0x07ca, 0x2553), .driver_info = USB_QUIRK_NO_BOS },
+
/* Realforce 87U Keyboard */
{ USB_DEVICE(0x0853, 0x011b), .driver_info = USB_QUIRK_NO_LPM },
@@ -383,6 +399,10 @@ static const struct usb_device_id usb_quirk_list[] = {
{ USB_DEVICE(0x0904, 0x6103), .driver_info =
USB_QUIRK_LINEAR_FRAME_INTR_BINTERVAL },
+ /* Silicon Motion Flash Drive */
+ { USB_DEVICE(0x090c, 0x1000), .driver_info = USB_QUIRK_DELAY_INIT },
+ { USB_DEVICE(0x090c, 0x2000), .driver_info = USB_QUIRK_DELAY_INIT },
+
/* Sound Devices USBPre2 */
{ USB_DEVICE(0x0926, 0x0202), .driver_info =
USB_QUIRK_ENDPOINT_IGNORE },
@@ -426,6 +446,9 @@ static const struct usb_device_id usb_quirk_list[] = {
{ USB_DEVICE(0x0b05, 0x17e0), .driver_info =
USB_QUIRK_IGNORE_REMOTE_WAKEUP },
+ /* ASUS TUF 4K PRO - BOS descriptor fetch hangs at SuperSpeed Plus */
+ { USB_DEVICE(0x0b05, 0x1ab9), .driver_info = USB_QUIRK_NO_BOS },
+
/* Realtek Semiconductor Corp. Mass Storage Device (Multicard Reader)*/
{ USB_DEVICE(0x0bda, 0x0151), .driver_info = USB_QUIRK_CONFIG_INTF_STRINGS },
@@ -439,6 +462,9 @@ static const struct usb_device_id usb_quirk_list[] = {
{ USB_DEVICE(0x0c45, 0x7056), .driver_info =
USB_QUIRK_IGNORE_REMOTE_WAKEUP },
+ /* Elgato 4K X - BOS descriptor fetch hangs at SuperSpeed Plus */
+ { USB_DEVICE(0x0fd9, 0x009b), .driver_info = USB_QUIRK_NO_BOS },
+
/* Sony Xperia XZ1 Compact (lilac) smartphone in fastboot mode */
{ USB_DEVICE(0x0fce, 0x0dde), .driver_info = USB_QUIRK_NO_LPM },
@@ -456,6 +482,8 @@ static const struct usb_device_id usb_quirk_list[] = {
/* Huawei 4G LTE module */
{ USB_DEVICE(0x12d1, 0x15bb), .driver_info =
USB_QUIRK_DISCONNECT_SUSPEND },
+ { USB_DEVICE(0x12d1, 0x15c1), .driver_info =
+ USB_QUIRK_DISCONNECT_SUSPEND },
{ USB_DEVICE(0x12d1, 0x15c3), .driver_info =
USB_QUIRK_DISCONNECT_SUSPEND },
@@ -465,6 +493,8 @@ static const struct usb_device_id usb_quirk_list[] = {
/* Razer - Razer Blade Keyboard */
{ USB_DEVICE(0x1532, 0x0116), .driver_info =
USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL },
+ /* Razer - Razer Kiyo Pro Webcam */
+ { USB_DEVICE(0x1532, 0x0e05), .driver_info = USB_QUIRK_NO_LPM },
/* Lenovo ThinkPad OneLink+ Dock twin hub controllers (VIA Labs VL812) */
{ USB_DEVICE(0x17ef, 0x1018), .driver_info = USB_QUIRK_RESET_RESUME },
@@ -539,6 +569,9 @@ static const struct usb_device_id usb_quirk_list[] = {
{ USB_DEVICE(0x2040, 0x7200), .driver_info =
USB_QUIRK_CONFIG_INTF_STRINGS },
+ /* VLI disk */
+ { USB_DEVICE(0x2109, 0x0711), .driver_info = USB_QUIRK_NO_LPM },
+
/* Raydium Touchscreen */
{ USB_DEVICE(0x2386, 0x3114), .driver_info = USB_QUIRK_NO_LPM },
@@ -546,6 +579,9 @@ static const struct usb_device_id usb_quirk_list[] = {
{ USB_DEVICE(0x2386, 0x350e), .driver_info = USB_QUIRK_NO_LPM },
+ /* UGREEN 35871 - BOS descriptor fetch hangs at SuperSpeed Plus */
+ { USB_DEVICE(0x2b89, 0x5871), .driver_info = USB_QUIRK_NO_BOS },
+
/* APTIV AUTOMOTIVE HUB */
{ USB_DEVICE(0x2c48, 0x0132), .driver_info =
USB_QUIRK_SHORT_SET_ADDRESS_REQ_TIMEOUT },
@@ -556,12 +592,18 @@ static const struct usb_device_id usb_quirk_list[] = {
/* Alcor Link AK9563 SC Reader used in 2022 Lenovo ThinkPads */
{ USB_DEVICE(0x2ce3, 0x9563), .driver_info = USB_QUIRK_NO_LPM },
+ /* ezcap401 - BOS descriptor fetch hangs at SuperSpeed Plus */
+ { USB_DEVICE(0x32ed, 0x0401), .driver_info = USB_QUIRK_NO_BOS },
+
/* DELL USB GEN2 */
{ USB_DEVICE(0x413c, 0xb062), .driver_info = USB_QUIRK_NO_LPM | USB_QUIRK_RESET_RESUME },
/* VCOM device */
{ USB_DEVICE(0x4296, 0x7570), .driver_info = USB_QUIRK_CONFIG_INTF_STRINGS },
+ /* Noji-MCS SmartCard Reader */
+ { USB_DEVICE(0x5131, 0x2007), .driver_info = USB_QUIRK_FORCE_ONE_CONFIG },
+
/* INTEL VALUE SSD */
{ USB_DEVICE(0x8086, 0xf1a5), .driver_info = USB_QUIRK_RESET_RESUME },
@@ -723,7 +765,7 @@ void usb_detect_quirks(struct usb_device *udev)
udev->quirks ^= usb_detect_dynamic_quirks(udev);
if (udev->quirks)
- dev_dbg(&udev->dev, "USB quirks for this device: %x\n",
+ dev_dbg(&udev->dev, "USB quirks for this device: 0x%x\n",
udev->quirks);
#ifdef CONFIG_USB_DEFAULT_PERSIST
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c
index 23f3cb1989f4..a07866f1060c 100644
--- a/drivers/usb/core/sysfs.c
+++ b/drivers/usb/core/sysfs.c
@@ -944,7 +944,7 @@ static umode_t dev_bin_attrs_are_visible(struct kobject *kobj,
}
static const struct attribute_group dev_bin_attr_grp = {
- .bin_attrs_new = dev_bin_attrs,
+ .bin_attrs = dev_bin_attrs,
.is_bin_visible = dev_bin_attrs_are_visible,
};
diff --git a/drivers/usb/core/trace.c b/drivers/usb/core/trace.c
new file mode 100644
index 000000000000..607bcf639d27
--- /dev/null
+++ b/drivers/usb/core/trace.c
@@ -0,0 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2025 Google LLC
+ */
+#define CREATE_TRACE_POINTS
+#include "trace.h"
diff --git a/drivers/usb/core/trace.h b/drivers/usb/core/trace.h
new file mode 100644
index 000000000000..903e57dc273a
--- /dev/null
+++ b/drivers/usb/core/trace.h
@@ -0,0 +1,61 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2025 Google LLC
+ */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM usbcore
+
+#if !defined(_USB_CORE_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _USB_CORE_TRACE_H
+
+#include <linux/types.h>
+#include <linux/tracepoint.h>
+#include <linux/usb.h>
+
+DECLARE_EVENT_CLASS(usb_core_log_usb_device,
+ TP_PROTO(struct usb_device *udev),
+ TP_ARGS(udev),
+ TP_STRUCT__entry(
+ __string(name, dev_name(&udev->dev))
+ __field(enum usb_device_speed, speed)
+ __field(enum usb_device_state, state)
+ __field(unsigned short, bus_mA)
+ __field(unsigned, authorized)
+ ),
+ TP_fast_assign(
+ __assign_str(name);
+ __entry->speed = udev->speed;
+ __entry->state = udev->state;
+ __entry->bus_mA = udev->bus_mA;
+ __entry->authorized = udev->authorized;
+ ),
+ TP_printk("usb %s speed %s state %s %dmA [%s]",
+ __get_str(name),
+ usb_speed_string(__entry->speed),
+ usb_state_string(__entry->state),
+ __entry->bus_mA,
+ __entry->authorized ? "authorized" : "unauthorized")
+);
+
+DEFINE_EVENT(usb_core_log_usb_device, usb_set_device_state,
+ TP_PROTO(struct usb_device *udev),
+ TP_ARGS(udev)
+);
+
+DEFINE_EVENT(usb_core_log_usb_device, usb_alloc_dev,
+ TP_PROTO(struct usb_device *udev),
+ TP_ARGS(udev)
+);
+
+
+#endif /* _USB_CORE_TRACE_H */
+
+/* this part has to be here */
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE trace
+
+#include <trace/define_trace.h>
diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c
index 7576920e2d5a..c06b44ca507b 100644
--- a/drivers/usb/core/urb.c
+++ b/drivers/usb/core/urb.c
@@ -72,8 +72,7 @@ struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags)
{
struct urb *urb;
- urb = kmalloc(struct_size(urb, iso_frame_desc, iso_packets),
- mem_flags);
+ urb = kmalloc_flex(*urb, iso_frame_desc, iso_packets, mem_flags);
if (!urb)
return NULL;
usb_init_urb(urb);
@@ -372,11 +371,12 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
struct usb_host_endpoint *ep;
int is_out;
unsigned int allowed;
+ bool is_eusb2_isoch_double;
if (!urb || !urb->complete)
return -EINVAL;
if (urb->hcpriv) {
- WARN_ONCE(1, "URB %pK submitted while active\n", urb);
+ WARN_ONCE(1, "URB %p submitted while active\n", urb);
return -EBUSY;
}
@@ -434,7 +434,8 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
return -ENODEV;
max = usb_endpoint_maxp(&ep->desc);
- if (max <= 0) {
+ is_eusb2_isoch_double = usb_endpoint_is_hs_isoc_double(dev, ep);
+ if (!max && !is_eusb2_isoch_double) {
dev_dbg(&dev->dev,
"bogus endpoint ep%d%s in %s (bad maxpacket %d)\n",
usb_endpoint_num(&ep->desc), is_out ? "out" : "in",
@@ -467,9 +468,13 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
max = le32_to_cpu(isoc_ep_comp->dwBytesPerInterval);
}
- /* "high bandwidth" mode, 1-3 packets/uframe? */
- if (dev->speed == USB_SPEED_HIGH)
- max *= usb_endpoint_maxp_mult(&ep->desc);
+ /* High speed, 1-3 packets/uframe, max 6 for eUSB2 double bw */
+ if (dev->speed == USB_SPEED_HIGH) {
+ if (is_eusb2_isoch_double)
+ max = le32_to_cpu(ep->eusb2_isoc_ep_comp.dwBytesPerInterval);
+ else
+ max *= usb_endpoint_maxp_mult(&ep->desc);
+ }
if (urb->number_of_packets <= 0)
return -EINVAL;
@@ -500,7 +505,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
/* Check that the pipe's type matches the endpoint's type */
if (usb_pipe_type_check(urb->dev, urb->pipe))
- dev_WARN(&dev->dev, "BOGUS urb xfer, pipe %x != type %x\n",
+ dev_warn_once(&dev->dev, "BOGUS urb xfer, pipe %x != type %x\n",
usb_pipetype(urb->pipe), pipetypes[xfertype]);
/* Check against a simple/standard policy */
@@ -597,10 +602,9 @@ EXPORT_SYMBOL_GPL(usb_submit_urb);
* code).
*
* Drivers should not call this routine or related routines, such as
- * usb_kill_urb() or usb_unlink_anchored_urbs(), after their disconnect
- * method has returned. The disconnect function should synchronize with
- * a driver's I/O routines to insure that all URB-related activity has
- * completed before it returns.
+ * usb_kill_urb(), after their disconnect method has returned. The
+ * disconnect function should synchronize with a driver's I/O routines
+ * to insure that all URB-related activity has completed before it returns.
*
* This request is asynchronous, however the HCD might call the ->complete()
* callback during unlink. Therefore when drivers call usb_unlink_urb(), they
@@ -890,28 +894,6 @@ void usb_unpoison_anchored_urbs(struct usb_anchor *anchor)
spin_unlock_irqrestore(&anchor->lock, flags);
}
EXPORT_SYMBOL_GPL(usb_unpoison_anchored_urbs);
-/**
- * usb_unlink_anchored_urbs - asynchronously cancel transfer requests en masse
- * @anchor: anchor the requests are bound to
- *
- * this allows all outstanding URBs to be unlinked starting
- * from the back of the queue. This function is asynchronous.
- * The unlinking is just triggered. It may happen after this
- * function has returned.
- *
- * This routine should not be called by a driver after its disconnect
- * method has returned.
- */
-void usb_unlink_anchored_urbs(struct usb_anchor *anchor)
-{
- struct urb *victim;
-
- while ((victim = usb_get_from_anchor(anchor)) != NULL) {
- usb_unlink_urb(victim);
- usb_put_urb(victim);
- }
-}
-EXPORT_SYMBOL_GPL(usb_unlink_anchored_urbs);
/**
* usb_anchor_suspend_wakeups
diff --git a/drivers/usb/core/usb-acpi.c b/drivers/usb/core/usb-acpi.c
index 935c0efea0b6..489dbdc96f94 100644
--- a/drivers/usb/core/usb-acpi.c
+++ b/drivers/usb/core/usb-acpi.c
@@ -157,7 +157,7 @@ EXPORT_SYMBOL_GPL(usb_acpi_set_power_state);
*/
static int usb_acpi_add_usb4_devlink(struct usb_device *udev)
{
- const struct device_link *link;
+ struct device_link *link;
struct usb_port *port_dev;
struct usb_hub *hub;
@@ -165,6 +165,8 @@ static int usb_acpi_add_usb4_devlink(struct usb_device *udev)
return 0;
hub = usb_hub_to_struct_hub(udev->parent);
+ if (!hub)
+ return 0;
port_dev = hub->ports[udev->portnum - 1];
struct fwnode_handle *nhi_fwnode __free(fwnode_handle) =
@@ -186,6 +188,8 @@ static int usb_acpi_add_usb4_devlink(struct usb_device *udev)
dev_dbg(&port_dev->dev, "Created device link from %s to %s\n",
dev_name(&port_dev->child->dev), dev_name(nhi_fwnode->dev));
+ udev->usb4_link = link;
+
return 0;
}
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 0b4685aad2d5..df166cafe106 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -46,6 +46,7 @@
#include <linux/dma-mapping.h>
#include "hub.h"
+#include "trace.h"
const char *usbcore_name = "usbcore";
@@ -647,7 +648,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
struct usb_hcd *usb_hcd = bus_to_hcd(bus);
unsigned raw_port = port1;
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ dev = kzalloc_obj(*dev);
if (!dev)
return NULL;
@@ -670,6 +671,8 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
set_dev_node(&dev->dev, dev_to_node(bus->sysdev));
dev->state = USB_STATE_ATTACHED;
dev->lpm_disable_count = 1;
+ spin_lock_init(&dev->offload_lock);
+ dev->offload_usage = 0;
atomic_set(&dev->urbnum, 0);
INIT_LIST_HEAD(&dev->ep0.urb_list);
@@ -695,15 +698,16 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
device_set_of_node_from_dev(&dev->dev, bus->sysdev);
dev_set_name(&dev->dev, "usb%d", bus->busnum);
} else {
+ int n;
+
/* match any labeling on the hubs; it's one-based */
if (parent->devpath[0] == '0') {
- snprintf(dev->devpath, sizeof dev->devpath,
- "%d", port1);
+ n = snprintf(dev->devpath, sizeof(dev->devpath), "%d", port1);
/* Root ports are not counted in route string */
dev->route = 0;
} else {
- snprintf(dev->devpath, sizeof dev->devpath,
- "%s.%d", parent->devpath, port1);
+ n = snprintf(dev->devpath, sizeof(dev->devpath), "%s.%d",
+ parent->devpath, port1);
/* Route string assumes hubs have less than 16 ports */
if (port1 < 15)
dev->route = parent->route +
@@ -712,6 +716,11 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
dev->route = parent->route +
(15 << ((parent->level - 1)*4));
}
+ if (n >= sizeof(dev->devpath)) {
+ usb_put_hcd(bus_to_hcd(bus));
+ usb_put_dev(dev);
+ return NULL;
+ }
dev->dev.parent = &parent->dev;
dev_set_name(&dev->dev, "%d-%s", bus->busnum, dev->devpath);
@@ -739,6 +748,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
#endif
dev->authorized = usb_dev_authorized(dev, usb_hcd);
+ trace_usb_alloc_dev(dev);
return dev;
}
EXPORT_SYMBOL_GPL(usb_alloc_dev);
@@ -1024,6 +1034,136 @@ void usb_free_coherent(struct usb_device *dev, size_t size, void *addr,
}
EXPORT_SYMBOL_GPL(usb_free_coherent);
+/**
+ * usb_alloc_noncoherent - allocate dma-noncoherent buffer for URB_NO_xxx_DMA_MAP
+ * @dev: device the buffer will be used with
+ * @size: requested buffer size
+ * @mem_flags: affect whether allocation may block
+ * @dma: used to return DMA address of buffer
+ * @dir: DMA transfer direction
+ * @table: used to return sg_table of allocated memory
+ *
+ * To explicit manage the memory ownership for the kernel vs the device by
+ * USB core, the user needs save sg_table to urb->sgt. Then USB core will
+ * do DMA sync for CPU and device properly.
+ *
+ * When the buffer is no longer used, free it with usb_free_noncoherent().
+ *
+ * Return: Either null (indicating no buffer could be allocated), or the
+ * cpu-space pointer to a buffer that may be used to perform DMA to the
+ * specified device. Such cpu-space buffers are returned along with the DMA
+ * address (through the pointer provided).
+ */
+void *usb_alloc_noncoherent(struct usb_device *dev, size_t size,
+ gfp_t mem_flags, dma_addr_t *dma,
+ enum dma_data_direction dir,
+ struct sg_table **table)
+{
+ struct device *dmadev;
+ struct sg_table *sgt;
+ void *buffer;
+
+ if (!dev || !dev->bus)
+ return NULL;
+
+ dmadev = bus_to_hcd(dev->bus)->self.sysdev;
+
+ sgt = dma_alloc_noncontiguous(dmadev, size, dir, mem_flags, 0);
+ if (!sgt)
+ return NULL;
+
+ buffer = dma_vmap_noncontiguous(dmadev, size, sgt);
+ if (!buffer) {
+ dma_free_noncontiguous(dmadev, size, sgt, dir);
+ return NULL;
+ }
+
+ *table = sgt;
+ *dma = sg_dma_address(sgt->sgl);
+
+ return buffer;
+}
+EXPORT_SYMBOL_GPL(usb_alloc_noncoherent);
+
+/**
+ * usb_free_noncoherent - free memory allocated with usb_alloc_noncoherent()
+ * @dev: device the buffer was used with
+ * @size: requested buffer size
+ * @addr: CPU address of buffer
+ * @dir: DMA transfer direction
+ * @table: describe the allocated and DMA mapped memory,
+ *
+ * This reclaims an I/O buffer, letting it be reused. The memory must have
+ * been allocated using usb_alloc_noncoherent(), and the parameters must match
+ * those provided in that allocation request.
+ */
+void usb_free_noncoherent(struct usb_device *dev, size_t size,
+ void *addr, enum dma_data_direction dir,
+ struct sg_table *table)
+{
+ struct device *dmadev;
+
+ if (!dev || !dev->bus)
+ return;
+ if (!addr)
+ return;
+
+ dmadev = bus_to_hcd(dev->bus)->self.sysdev;
+ dma_vunmap_noncontiguous(dmadev, addr);
+ dma_free_noncontiguous(dmadev, size, table, dir);
+}
+EXPORT_SYMBOL_GPL(usb_free_noncoherent);
+
+/**
+ * usb_endpoint_max_periodic_payload - Get maximum payload bytes per service
+ * interval
+ * @udev: The USB device
+ * @ep: The endpoint
+ *
+ * Returns: the maximum number of bytes isochronous or interrupt endpoint @ep
+ * can transfer during a service interval, or 0 for other endpoints.
+ */
+u32 usb_endpoint_max_periodic_payload(struct usb_device *udev,
+ const struct usb_host_endpoint *ep)
+{
+ if (!usb_endpoint_xfer_isoc(&ep->desc) &&
+ !usb_endpoint_xfer_int(&ep->desc))
+ return 0;
+
+ switch (udev->speed) {
+ case USB_SPEED_SUPER_PLUS:
+ if (USB_SS_SSP_ISOC_COMP(ep->ss_ep_comp.bmAttributes))
+ return le32_to_cpu(ep->ssp_isoc_ep_comp.dwBytesPerInterval);
+ fallthrough;
+ case USB_SPEED_SUPER:
+ return le16_to_cpu(ep->ss_ep_comp.wBytesPerInterval);
+ default:
+ if (usb_endpoint_is_hs_isoc_double(udev, ep))
+ return le32_to_cpu(ep->eusb2_isoc_ep_comp.dwBytesPerInterval);
+ return usb_endpoint_maxp(&ep->desc) * usb_endpoint_maxp_mult(&ep->desc);
+ }
+}
+EXPORT_SYMBOL_GPL(usb_endpoint_max_periodic_payload);
+
+/**
+ * usb_endpoint_is_hs_isoc_double - Tell whether an endpoint uses USB 2
+ * Isochronous Double IN Bandwidth
+ * @udev: The USB device
+ * @ep: The endpoint
+ *
+ * Returns: true if an endpoint @ep conforms to USB 2 Isochronous Double IN
+ * Bandwidth ECN, false otherwise.
+ */
+bool usb_endpoint_is_hs_isoc_double(struct usb_device *udev,
+ const struct usb_host_endpoint *ep)
+{
+ return ep->eusb2_isoc_ep_comp.bDescriptorType &&
+ le16_to_cpu(udev->descriptor.bcdUSB) == 0x220 &&
+ usb_endpoint_is_isoc_in(&ep->desc) &&
+ !le16_to_cpu(ep->desc.wMaxPacketSize);
+}
+EXPORT_SYMBOL_GPL(usb_endpoint_is_hs_isoc_double);
+
/*
* Notifications of device and interface registration
*/
diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c
index 9919ab725d54..f375c5185bfe 100644
--- a/drivers/usb/dwc2/core.c
+++ b/drivers/usb/dwc2/core.c
@@ -43,6 +43,7 @@ int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg)
/* Backup global regs */
gr = &hsotg->gr_backup;
+ gr->gintsts = dwc2_readl(hsotg, GINTSTS);
gr->gotgctl = dwc2_readl(hsotg, GOTGCTL);
gr->gintmsk = dwc2_readl(hsotg, GINTMSK);
gr->gahbcfg = dwc2_readl(hsotg, GAHBCFG);
@@ -577,6 +578,7 @@ void dwc2_force_dr_mode(struct dwc2_hsotg *hsotg)
{
switch (hsotg->dr_mode) {
case USB_DR_MODE_HOST:
+ dwc2_force_mode(hsotg, true);
/*
* NOTE: This is required for some rockchip soc based
* platforms on their host-only dwc2.
diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h
index 2bd74f3033ed..34127b890b2a 100644
--- a/drivers/usb/dwc2/core.h
+++ b/drivers/usb/dwc2/core.h
@@ -667,6 +667,7 @@ struct dwc2_hw_params {
/**
* struct dwc2_gregs_backup - Holds global registers state before
* entering partial power down
+ * @gintsts: Backup of GINTSTS register
* @gotgctl: Backup of GOTGCTL register
* @gintmsk: Backup of GINTMSK register
* @gahbcfg: Backup of GAHBCFG register
@@ -683,6 +684,7 @@ struct dwc2_hw_params {
* @valid: True if registers values backuped.
*/
struct dwc2_gregs_backup {
+ u32 gintsts;
u32 gotgctl;
u32 gintmsk;
u32 gahbcfg;
@@ -1127,6 +1129,9 @@ struct dwc2_hsotg {
#define DWC2_FS_IOT_ID 0x55310000
#define DWC2_HS_IOT_ID 0x55320000
+#define DWC2_RESTORE_DCTL BIT(0)
+#define DWC2_RESTORE_DCFG BIT(1)
+
#if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
union dwc2_hcd_internal_flags {
u32 d32;
@@ -1420,7 +1425,7 @@ int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode);
#define dwc2_is_device_connected(hsotg) (hsotg->connected)
#define dwc2_is_device_enabled(hsotg) (hsotg->enabled)
int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg);
-int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg, int remote_wakeup);
+int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg, unsigned int flags);
int dwc2_gadget_enter_hibernation(struct dwc2_hsotg *hsotg);
int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg,
int rem_wakeup, int reset);
@@ -1435,6 +1440,9 @@ int dwc2_hsotg_tx_fifo_total_depth(struct dwc2_hsotg *hsotg);
int dwc2_hsotg_tx_fifo_average_depth(struct dwc2_hsotg *hsotg);
void dwc2_gadget_init_lpm(struct dwc2_hsotg *hsotg);
void dwc2_gadget_program_ref_clk(struct dwc2_hsotg *hsotg);
+int dwc2_gadget_backup_critical_registers(struct dwc2_hsotg *hsotg);
+int dwc2_gadget_restore_critical_registers(struct dwc2_hsotg *hsotg,
+ unsigned int flags);
static inline void dwc2_clear_fifo_map(struct dwc2_hsotg *hsotg)
{ hsotg->fifo_map = 0; }
#else
@@ -1459,7 +1467,7 @@ static inline int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg,
static inline int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg)
{ return 0; }
static inline int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg,
- int remote_wakeup)
+ unsigned int flags)
{ return 0; }
static inline int dwc2_gadget_enter_hibernation(struct dwc2_hsotg *hsotg)
{ return 0; }
@@ -1482,6 +1490,11 @@ static inline int dwc2_hsotg_tx_fifo_average_depth(struct dwc2_hsotg *hsotg)
{ return 0; }
static inline void dwc2_gadget_init_lpm(struct dwc2_hsotg *hsotg) {}
static inline void dwc2_gadget_program_ref_clk(struct dwc2_hsotg *hsotg) {}
+static inline int dwc2_gadget_backup_critical_registers(struct dwc2_hsotg *hsotg)
+{ return 0; }
+static inline int dwc2_gadget_restore_critical_registers(struct dwc2_hsotg *hsotg,
+ unsigned int flags)
+{ return 0; }
static inline void dwc2_clear_fifo_map(struct dwc2_hsotg *hsotg) {}
#endif
@@ -1505,6 +1518,8 @@ int dwc2_host_exit_partial_power_down(struct dwc2_hsotg *hsotg,
void dwc2_host_enter_clock_gating(struct dwc2_hsotg *hsotg);
void dwc2_host_exit_clock_gating(struct dwc2_hsotg *hsotg, int rem_wakeup);
bool dwc2_host_can_poweroff_phy(struct dwc2_hsotg *dwc2);
+int dwc2_host_backup_critical_registers(struct dwc2_hsotg *hsotg);
+int dwc2_host_restore_critical_registers(struct dwc2_hsotg *hsotg);
static inline void dwc2_host_schedule_phy_reset(struct dwc2_hsotg *hsotg)
{ schedule_work(&hsotg->phy_reset_work); }
#else
@@ -1544,6 +1559,10 @@ static inline void dwc2_host_exit_clock_gating(struct dwc2_hsotg *hsotg,
int rem_wakeup) {}
static inline bool dwc2_host_can_poweroff_phy(struct dwc2_hsotg *dwc2)
{ return false; }
+static inline int dwc2_host_backup_critical_registers(struct dwc2_hsotg *hsotg)
+{ return 0; }
+static inline int dwc2_host_restore_critical_registers(struct dwc2_hsotg *hsotg)
+{ return 0; }
static inline void dwc2_host_schedule_phy_reset(struct dwc2_hsotg *hsotg) {}
#endif
diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c
index bd4c788f03bc..c8b02c27d27d 100644
--- a/drivers/usb/dwc2/gadget.c
+++ b/drivers/usb/dwc2/gadget.c
@@ -397,7 +397,7 @@ static struct usb_request *dwc2_hsotg_ep_alloc_request(struct usb_ep *ep,
{
struct dwc2_hsotg_req *req;
- req = kzalloc(sizeof(*req), flags);
+ req = kzalloc_obj(*req, flags);
if (!req)
return NULL;
@@ -4049,7 +4049,7 @@ static int dwc2_hsotg_ep_enable(struct usb_ep *ep,
return -EINVAL;
}
- ep_type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+ ep_type = usb_endpoint_type(desc);
mps = usb_endpoint_maxp(desc);
mc = usb_endpoint_maxp_mult(desc);
@@ -4604,6 +4604,14 @@ static int dwc2_hsotg_udc_stop(struct usb_gadget *gadget)
if (!hsotg)
return -ENODEV;
+ /* Exit clock gating when driver is stopped. */
+ if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_NONE &&
+ hsotg->bus_suspended && !hsotg->params.no_clock_gating) {
+ spin_lock_irqsave(&hsotg->lock, flags);
+ dwc2_gadget_exit_clock_gating(hsotg, 0);
+ spin_unlock_irqrestore(&hsotg->lock, flags);
+ }
+
/* all endpoints should be shutdown */
for (ep = 1; ep < hsotg->num_of_eps; ep++) {
if (hsotg->eps_in[ep])
@@ -5204,11 +5212,11 @@ int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg)
* if controller power were disabled.
*
* @hsotg: Programming view of the DWC_otg controller
- * @remote_wakeup: Indicates whether resume is initiated by Device or Host.
+ * @flags: Defines which registers should be restored.
*
* Return: 0 if successful, negative error code otherwise
*/
-int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg, int remote_wakeup)
+int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg, unsigned int flags)
{
struct dwc2_dregs_backup *dr;
int i;
@@ -5224,7 +5232,10 @@ int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg, int remote_wakeup)
}
dr->valid = false;
- if (!remote_wakeup)
+ if (flags & DWC2_RESTORE_DCFG)
+ dwc2_writel(hsotg, dr->dcfg, DCFG);
+
+ if (flags & DWC2_RESTORE_DCTL)
dwc2_writel(hsotg, dr->dctl, DCTL);
dwc2_writel(hsotg, dr->daintmsk, DAINTMSK);
@@ -5310,6 +5321,49 @@ void dwc2_gadget_program_ref_clk(struct dwc2_hsotg *hsotg)
dev_dbg(hsotg->dev, "GREFCLK=0x%08x\n", dwc2_readl(hsotg, GREFCLK));
}
+int dwc2_gadget_backup_critical_registers(struct dwc2_hsotg *hsotg)
+{
+ int ret;
+
+ /* Backup all registers */
+ ret = dwc2_backup_global_registers(hsotg);
+ if (ret) {
+ dev_err(hsotg->dev, "%s: failed to backup global registers\n",
+ __func__);
+ return ret;
+ }
+
+ ret = dwc2_backup_device_registers(hsotg);
+ if (ret) {
+ dev_err(hsotg->dev, "%s: failed to backup device registers\n",
+ __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
+int dwc2_gadget_restore_critical_registers(struct dwc2_hsotg *hsotg,
+ unsigned int flags)
+{
+ int ret;
+
+ ret = dwc2_restore_global_registers(hsotg);
+ if (ret) {
+ dev_err(hsotg->dev, "%s: failed to restore registers\n",
+ __func__);
+ return ret;
+ }
+ ret = dwc2_restore_device_registers(hsotg, flags);
+ if (ret) {
+ dev_err(hsotg->dev, "%s: failed to restore device registers\n",
+ __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
/**
* dwc2_gadget_enter_hibernation() - Put controller in Hibernation.
*
@@ -5327,18 +5381,9 @@ int dwc2_gadget_enter_hibernation(struct dwc2_hsotg *hsotg)
/* Change to L2(suspend) state */
hsotg->lx_state = DWC2_L2;
dev_dbg(hsotg->dev, "Start of hibernation completed\n");
- ret = dwc2_backup_global_registers(hsotg);
- if (ret) {
- dev_err(hsotg->dev, "%s: failed to backup global registers\n",
- __func__);
- return ret;
- }
- ret = dwc2_backup_device_registers(hsotg);
- if (ret) {
- dev_err(hsotg->dev, "%s: failed to backup device registers\n",
- __func__);
+ ret = dwc2_gadget_backup_critical_registers(hsotg);
+ if (ret)
return ret;
- }
gpwrdn = GPWRDN_PWRDNRSTN;
udelay(10);
@@ -5346,20 +5391,34 @@ int dwc2_gadget_enter_hibernation(struct dwc2_hsotg *hsotg)
if (gusbcfg & GUSBCFG_ULPI_UTMI_SEL) {
/* ULPI interface */
gpwrdn |= GPWRDN_ULPI_LATCH_EN_DURING_HIB_ENTRY;
- }
- dwc2_writel(hsotg, gpwrdn, GPWRDN);
- udelay(10);
+ dwc2_writel(hsotg, gpwrdn, GPWRDN);
+ udelay(10);
- /* Suspend the Phy Clock */
- pcgcctl = dwc2_readl(hsotg, PCGCTL);
- pcgcctl |= PCGCTL_STOPPCLK;
- dwc2_writel(hsotg, pcgcctl, PCGCTL);
- udelay(10);
+ /* Suspend the Phy Clock */
+ pcgcctl = dwc2_readl(hsotg, PCGCTL);
+ pcgcctl |= PCGCTL_STOPPCLK;
+ dwc2_writel(hsotg, pcgcctl, PCGCTL);
+ udelay(10);
- gpwrdn = dwc2_readl(hsotg, GPWRDN);
- gpwrdn |= GPWRDN_PMUACTV;
- dwc2_writel(hsotg, gpwrdn, GPWRDN);
- udelay(10);
+ gpwrdn = dwc2_readl(hsotg, GPWRDN);
+ gpwrdn |= GPWRDN_PMUACTV;
+ dwc2_writel(hsotg, gpwrdn, GPWRDN);
+ udelay(10);
+ } else {
+ /* UTMI+ Interface */
+ dwc2_writel(hsotg, gpwrdn, GPWRDN);
+ udelay(10);
+
+ gpwrdn = dwc2_readl(hsotg, GPWRDN);
+ gpwrdn |= GPWRDN_PMUACTV;
+ dwc2_writel(hsotg, gpwrdn, GPWRDN);
+ udelay(10);
+
+ pcgcctl = dwc2_readl(hsotg, PCGCTL);
+ pcgcctl |= PCGCTL_STOPPCLK;
+ dwc2_writel(hsotg, pcgcctl, PCGCTL);
+ udelay(10);
+ }
/* Set flag to indicate that we are in hibernation */
hsotg->hibernated = 1;
@@ -5415,6 +5474,7 @@ int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg,
u32 gpwrdn;
u32 dctl;
int ret = 0;
+ unsigned int flags = 0;
struct dwc2_gregs_backup *gr;
struct dwc2_dregs_backup *dr;
@@ -5477,6 +5537,7 @@ int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg,
dctl = dwc2_readl(hsotg, DCTL);
dctl |= DCTL_PWRONPRGDONE;
dwc2_writel(hsotg, dctl, DCTL);
+ flags |= DWC2_RESTORE_DCTL;
}
/* Wait for interrupts which must be cleared */
mdelay(2);
@@ -5484,20 +5545,9 @@ int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg,
dwc2_writel(hsotg, 0xffffffff, GINTSTS);
/* Restore global registers */
- ret = dwc2_restore_global_registers(hsotg);
- if (ret) {
- dev_err(hsotg->dev, "%s: failed to restore registers\n",
- __func__);
- return ret;
- }
-
- /* Restore device registers */
- ret = dwc2_restore_device_registers(hsotg, rem_wakeup);
- if (ret) {
- dev_err(hsotg->dev, "%s: failed to restore device registers\n",
- __func__);
+ ret = dwc2_gadget_restore_critical_registers(hsotg, flags);
+ if (ret)
return ret;
- }
if (rem_wakeup) {
mdelay(10);
@@ -5531,19 +5581,9 @@ int dwc2_gadget_enter_partial_power_down(struct dwc2_hsotg *hsotg)
dev_dbg(hsotg->dev, "Entering device partial power down started.\n");
/* Backup all registers */
- ret = dwc2_backup_global_registers(hsotg);
- if (ret) {
- dev_err(hsotg->dev, "%s: failed to backup global registers\n",
- __func__);
- return ret;
- }
-
- ret = dwc2_backup_device_registers(hsotg);
- if (ret) {
- dev_err(hsotg->dev, "%s: failed to backup device registers\n",
- __func__);
+ ret = dwc2_gadget_backup_critical_registers(hsotg);
+ if (ret)
return ret;
- }
/*
* Clear any pending interrupts since dwc2 will not be able to
@@ -5590,11 +5630,8 @@ int dwc2_gadget_exit_partial_power_down(struct dwc2_hsotg *hsotg,
{
u32 pcgcctl;
u32 dctl;
- struct dwc2_dregs_backup *dr;
int ret = 0;
- dr = &hsotg->dr_backup;
-
dev_dbg(hsotg->dev, "Exiting device partial Power Down started.\n");
pcgcctl = dwc2_readl(hsotg, PCGCTL);
@@ -5611,21 +5648,10 @@ int dwc2_gadget_exit_partial_power_down(struct dwc2_hsotg *hsotg,
udelay(100);
if (restore) {
- ret = dwc2_restore_global_registers(hsotg);
- if (ret) {
- dev_err(hsotg->dev, "%s: failed to restore registers\n",
- __func__);
- return ret;
- }
- /* Restore DCFG */
- dwc2_writel(hsotg, dr->dcfg, DCFG);
-
- ret = dwc2_restore_device_registers(hsotg, 0);
- if (ret) {
- dev_err(hsotg->dev, "%s: failed to restore device registers\n",
- __func__);
+ ret = dwc2_gadget_restore_critical_registers(hsotg, DWC2_RESTORE_DCTL |
+ DWC2_RESTORE_DCFG);
+ if (ret)
return ret;
- }
}
/* Set the Power-On Programming done bit */
diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index 8c3941ecaaf5..1a763ad4f721 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -3227,7 +3227,7 @@ host:
static void dwc2_wakeup_detected(struct timer_list *t)
{
- struct dwc2_hsotg *hsotg = from_timer(hsotg, t, wkp_timer);
+ struct dwc2_hsotg *hsotg = timer_container_of(hsotg, t, wkp_timer);
u32 hprt0;
dev_dbg(hsotg->dev, "%s()\n", __func__);
@@ -3823,7 +3823,7 @@ static struct dwc2_hcd_urb *dwc2_hcd_urb_alloc(struct dwc2_hsotg *hsotg,
{
struct dwc2_hcd_urb *urb;
- urb = kzalloc(struct_size(urb, iso_descs, iso_desc_count), mem_flags);
+ urb = kzalloc_flex(*urb, iso_descs, iso_desc_count, mem_flags);
if (urb)
urb->packet_count = iso_desc_count;
return urb;
@@ -4743,7 +4743,7 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
qh_allocated = true;
}
- qtd = kzalloc(sizeof(*qtd), mem_flags);
+ qtd = kzalloc_obj(*qtd, mem_flags);
if (!qtd) {
retval = -ENOMEM;
goto fail1;
@@ -5081,7 +5081,7 @@ static void dwc2_hcd_free(struct dwc2_hsotg *hsotg)
cancel_work_sync(&hsotg->phy_reset_work);
- del_timer(&hsotg->wkp_timer);
+ timer_delete(&hsotg->wkp_timer);
}
static void dwc2_hcd_release(struct dwc2_hsotg *hsotg)
@@ -5218,7 +5218,7 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg)
memset(&hsotg->hc_ptr_array[0], 0, sizeof(hsotg->hc_ptr_array));
for (i = 0; i < num_channels; i++) {
- channel = kzalloc(sizeof(*channel), GFP_KERNEL);
+ channel = kzalloc_obj(*channel);
if (!channel)
goto error3;
channel->hc_num = i;
@@ -5474,6 +5474,49 @@ int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg)
return 0;
}
+int dwc2_host_backup_critical_registers(struct dwc2_hsotg *hsotg)
+{
+ int ret;
+
+ /* Backup all registers */
+ ret = dwc2_backup_global_registers(hsotg);
+ if (ret) {
+ dev_err(hsotg->dev, "%s: failed to backup global registers\n",
+ __func__);
+ return ret;
+ }
+
+ ret = dwc2_backup_host_registers(hsotg);
+ if (ret) {
+ dev_err(hsotg->dev, "%s: failed to backup host registers\n",
+ __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
+int dwc2_host_restore_critical_registers(struct dwc2_hsotg *hsotg)
+{
+ int ret;
+
+ ret = dwc2_restore_global_registers(hsotg);
+ if (ret) {
+ dev_err(hsotg->dev, "%s: failed to restore registers\n",
+ __func__);
+ return ret;
+ }
+
+ ret = dwc2_restore_host_registers(hsotg);
+ if (ret) {
+ dev_err(hsotg->dev, "%s: failed to restore host registers\n",
+ __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
/**
* dwc2_host_enter_hibernation() - Put controller in Hibernation.
*
@@ -5489,18 +5532,9 @@ int dwc2_host_enter_hibernation(struct dwc2_hsotg *hsotg)
u32 gpwrdn;
dev_dbg(hsotg->dev, "Preparing host for hibernation\n");
- ret = dwc2_backup_global_registers(hsotg);
- if (ret) {
- dev_err(hsotg->dev, "%s: failed to backup global registers\n",
- __func__);
- return ret;
- }
- ret = dwc2_backup_host_registers(hsotg);
- if (ret) {
- dev_err(hsotg->dev, "%s: failed to backup host registers\n",
- __func__);
+ ret = dwc2_host_backup_critical_registers(hsotg);
+ if (ret)
return ret;
- }
/* Enter USB Suspend Mode */
hprt0 = dwc2_readl(hsotg, HPRT0);
@@ -5694,20 +5728,9 @@ int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup,
dwc2_writel(hsotg, 0xffffffff, GINTSTS);
/* Restore global registers */
- ret = dwc2_restore_global_registers(hsotg);
- if (ret) {
- dev_err(hsotg->dev, "%s: failed to restore registers\n",
- __func__);
+ ret = dwc2_host_restore_critical_registers(hsotg);
+ if (ret)
return ret;
- }
-
- /* Restore host registers */
- ret = dwc2_restore_host_registers(hsotg);
- if (ret) {
- dev_err(hsotg->dev, "%s: failed to restore host registers\n",
- __func__);
- return ret;
- }
if (rem_wakeup) {
dwc2_hcd_rem_wakeup(hsotg);
@@ -5774,19 +5797,9 @@ int dwc2_host_enter_partial_power_down(struct dwc2_hsotg *hsotg)
dev_warn(hsotg->dev, "Suspend wasn't generated\n");
/* Backup all registers */
- ret = dwc2_backup_global_registers(hsotg);
- if (ret) {
- dev_err(hsotg->dev, "%s: failed to backup global registers\n",
- __func__);
- return ret;
- }
-
- ret = dwc2_backup_host_registers(hsotg);
- if (ret) {
- dev_err(hsotg->dev, "%s: failed to backup host registers\n",
- __func__);
+ ret = dwc2_host_backup_critical_registers(hsotg);
+ if (ret)
return ret;
- }
/*
* Clear any pending interrupts since dwc2 will not be able to
@@ -5855,19 +5868,9 @@ int dwc2_host_exit_partial_power_down(struct dwc2_hsotg *hsotg,
udelay(100);
if (restore) {
- ret = dwc2_restore_global_registers(hsotg);
- if (ret) {
- dev_err(hsotg->dev, "%s: failed to restore registers\n",
- __func__);
- return ret;
- }
-
- ret = dwc2_restore_host_registers(hsotg);
- if (ret) {
- dev_err(hsotg->dev, "%s: failed to restore host registers\n",
- __func__);
+ ret = dwc2_host_restore_critical_registers(hsotg);
+ if (ret)
return ret;
- }
}
/* Drive resume signaling and exit suspend mode on the port. */
diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c
index 238c6fd50e75..4f00ff1dc3e1 100644
--- a/drivers/usb/dwc2/hcd_queue.c
+++ b/drivers/usb/dwc2/hcd_queue.c
@@ -1215,7 +1215,7 @@ static void dwc2_do_unreserve(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
*/
static void dwc2_unreserve_timer_fn(struct timer_list *t)
{
- struct dwc2_qh *qh = from_timer(qh, t, unreserve_timer);
+ struct dwc2_qh *qh = timer_container_of(qh, t, unreserve_timer);
struct dwc2_hsotg *hsotg = qh->hsotg;
unsigned long flags;
@@ -1302,7 +1302,7 @@ static int dwc2_schedule_periodic(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
}
/* Cancel pending unreserve; if canceled OK, unreserve was pending */
- if (del_timer(&qh->unreserve_timer))
+ if (timer_delete(&qh->unreserve_timer))
WARN_ON(!qh->unreserve_pending);
/*
@@ -1459,8 +1459,7 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
/* Initialize QH */
qh->hsotg = hsotg;
timer_setup(&qh->unreserve_timer, dwc2_unreserve_timer_fn, 0);
- hrtimer_init(&qh->wait_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- qh->wait_timer.function = &dwc2_wait_timer_fn;
+ hrtimer_setup(&qh->wait_timer, &dwc2_wait_timer_fn, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
qh->ep_type = ep_type;
qh->ep_is_in = ep_is_in;
@@ -1586,7 +1585,7 @@ struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
return NULL;
/* Allocate memory */
- qh = kzalloc(sizeof(*qh), mem_flags);
+ qh = kzalloc_obj(*qh, mem_flags);
if (!qh)
return NULL;
@@ -1615,7 +1614,7 @@ struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
{
/* Make sure any unreserve work is finished. */
- if (del_timer_sync(&qh->unreserve_timer)) {
+ if (timer_delete_sync(&qh->unreserve_timer)) {
unsigned long flags;
spin_lock_irqsave(&hsotg->lock, flags);
diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c
index 4d73fae80b12..091bfcfef753 100644
--- a/drivers/usb/dwc2/params.c
+++ b/drivers/usb/dwc2/params.c
@@ -334,7 +334,7 @@ const struct of_device_id dwc2_of_match_table[] = {
.data = dwc2_set_amlogic_a1_params },
{ .compatible = "amcc,dwc-otg", .data = dwc2_set_amcc_params },
{ .compatible = "apm,apm82181-dwc-otg", .data = dwc2_set_amcc_params },
- { .compatible = "sophgo,cv1800-usb",
+ { .compatible = "sophgo,cv1800b-usb",
.data = dwc2_set_cv1800_params },
{ .compatible = "st,stm32f4x9-fsotg",
.data = dwc2_set_stm32f4x9_fsotg_params },
@@ -1029,11 +1029,33 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
return 0;
}
+static int dwc2_limit_speed(struct dwc2_hsotg *hsotg)
+{
+ enum usb_device_speed usb_speed;
+
+ usb_speed = usb_get_maximum_speed(hsotg->dev);
+ switch (usb_speed) {
+ case USB_SPEED_LOW:
+ dev_err(hsotg->dev, "Maximum speed cannot be forced to low-speed\n");
+ return -EINVAL;
+ case USB_SPEED_FULL:
+ if (hsotg->params.speed == DWC2_SPEED_PARAM_LOW)
+ break;
+ hsotg->params.speed = DWC2_SPEED_PARAM_FULL;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
typedef void (*set_params_cb)(struct dwc2_hsotg *data);
int dwc2_init_params(struct dwc2_hsotg *hsotg)
{
set_params_cb set_params;
+ int ret;
dwc2_set_default_params(hsotg);
dwc2_get_device_properties(hsotg);
@@ -1051,6 +1073,10 @@ int dwc2_init_params(struct dwc2_hsotg *hsotg)
}
}
+ ret = dwc2_limit_speed(hsotg);
+ if (ret)
+ return ret;
+
dwc2_check_params(hsotg);
return 0;
diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c
index 91c80a92d9b8..ef0d73077034 100644
--- a/drivers/usb/dwc2/platform.c
+++ b/drivers/usb/dwc2/platform.c
@@ -369,8 +369,11 @@ static void dwc2_driver_shutdown(struct platform_device *dev)
{
struct dwc2_hsotg *hsotg = platform_get_drvdata(dev);
- dwc2_disable_global_interrupts(hsotg);
- synchronize_irq(hsotg->irq);
+ if (hsotg->ll_hw_enabled) {
+ dwc2_disable_global_interrupts(hsotg);
+ synchronize_irq(hsotg->irq);
+ dwc2_lowlevel_hw_disable(hsotg);
+ }
}
/**
@@ -646,9 +649,13 @@ error:
static int __maybe_unused dwc2_suspend(struct device *dev)
{
struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev);
- bool is_device_mode = dwc2_is_device_mode(dwc2);
+ bool is_device_mode;
int ret = 0;
+ if (!dwc2->ll_hw_enabled)
+ return 0;
+
+ is_device_mode = dwc2_is_device_mode(dwc2);
if (is_device_mode)
dwc2_hsotg_suspend(dwc2);
@@ -685,6 +692,14 @@ static int __maybe_unused dwc2_suspend(struct device *dev)
regulator_disable(dwc2->usb33d);
}
+ if (is_device_mode)
+ ret = dwc2_gadget_backup_critical_registers(dwc2);
+ else
+ ret = dwc2_host_backup_critical_registers(dwc2);
+
+ if (ret)
+ return ret;
+
if (dwc2->ll_hw_enabled &&
(is_device_mode || dwc2_host_can_poweroff_phy(dwc2))) {
ret = __dwc2_lowlevel_hw_disable(dwc2);
@@ -694,11 +709,32 @@ static int __maybe_unused dwc2_suspend(struct device *dev)
return ret;
}
+static int dwc2_restore_critical_registers(struct dwc2_hsotg *hsotg)
+{
+ struct dwc2_gregs_backup *gr;
+
+ gr = &hsotg->gr_backup;
+
+ if (!gr->valid) {
+ dev_err(hsotg->dev, "No valid register backup, failed to restore\n");
+ return -EINVAL;
+ }
+
+ if (gr->gintsts & GINTSTS_CURMODE_HOST)
+ return dwc2_host_restore_critical_registers(hsotg);
+
+ return dwc2_gadget_restore_critical_registers(hsotg, DWC2_RESTORE_DCTL |
+ DWC2_RESTORE_DCFG);
+}
+
static int __maybe_unused dwc2_resume(struct device *dev)
{
struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev);
int ret = 0;
+ if (!dwc2->ll_hw_enabled)
+ return 0;
+
if (dwc2->phy_off_for_suspend && dwc2->ll_hw_enabled) {
ret = __dwc2_lowlevel_hw_enable(dwc2);
if (ret)
@@ -706,6 +742,18 @@ static int __maybe_unused dwc2_resume(struct device *dev)
}
dwc2->phy_off_for_suspend = false;
+ /*
+ * During suspend it's possible that the power domain for the
+ * DWC2 controller is disabled and all register values get lost.
+ * In case the GUSBCFG register is not initialized, it's clear the
+ * registers must be restored.
+ */
+ if (!(dwc2_readl(dwc2, GUSBCFG) & GUSBCFG_TOUTCAL_MASK)) {
+ ret = dwc2_restore_critical_registers(dwc2);
+ if (ret)
+ return ret;
+ }
+
if (dwc2->params.activate_stm_id_vb_detection) {
unsigned long flags;
u32 ggpio, gotgctl;
diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig
index 310d182e10b5..18169727a413 100644
--- a/drivers/usb/dwc3/Kconfig
+++ b/drivers/usb/dwc3/Kconfig
@@ -150,6 +150,18 @@ config USB_DWC3_IMX8MP
functionality.
Say 'Y' or 'M' if you have one such device.
+config USB_DWC3_IMX
+ tristate "NXP iMX Platform"
+ depends on OF && COMMON_CLK
+ depends on (ARCH_MXC && ARM64) || COMPILE_TEST
+ default USB_DWC3
+ help
+ NXP iMX SoC use DesignWare Core IP for USB2/3
+ functionality.
+ This driver also handles the wakeup feature outside
+ of DesignWare Core.
+ Say 'Y' or 'M' if you have one such device.
+
config USB_DWC3_XILINX
tristate "Xilinx Platforms"
depends on (ARCH_ZYNQMP || COMPILE_TEST) && OF
@@ -189,4 +201,37 @@ config USB_DWC3_RTK
or dual-role mode.
Say 'Y' or 'M' if you have such device.
+config USB_DWC3_GENERIC_PLAT
+ tristate "DWC3 Generic Platform Driver"
+ depends on OF && COMMON_CLK
+ default USB_DWC3
+ help
+ Support USB3 functionality in simple SoC integrations.
+ Currently supports SpacemiT DWC USB3. Platforms using
+ dwc3-of-simple can easily switch to dwc3-generic by flattening
+ the dwc3 child node in the device tree.
+ Say 'Y' or 'M' here if your platform integrates DWC3 in a similar way.
+
+config USB_DWC3_APPLE
+ tristate "Apple Silicon DWC3 Platform Driver"
+ depends on OF && ARCH_APPLE
+ default USB_DWC3
+ select USB_ROLE_SWITCH
+ help
+ Support Apple Silicon SoCs with DesignWare Core USB3 IP.
+ The DesignWare Core USB3 IP has to be used in dual-role
+ mode on these machines.
+ Say 'Y' or 'M' if you have such device.
+
+config USB_DWC3_GOOGLE
+ tristate "Google Platform"
+ help
+ Support the DesignWare Core USB3 IP found on Google Tensor SoCs,
+ starting with the G5 generation (Laguna). This driver includes
+ support for hibernation in host mode.
+ Say 'Y' or 'M' if you have one such device.
+
+ To compile this driver as a module, choose M here: the
+ module will be called dwc3-google.ko.
+
endif
diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile
index 124eda2522d9..f37971197203 100644
--- a/drivers/usb/dwc3/Makefile
+++ b/drivers/usb/dwc3/Makefile
@@ -43,6 +43,7 @@ endif
##
obj-$(CONFIG_USB_DWC3_AM62) += dwc3-am62.o
+obj-$(CONFIG_USB_DWC3_APPLE) += dwc3-apple.o
obj-$(CONFIG_USB_DWC3_OMAP) += dwc3-omap.o
obj-$(CONFIG_USB_DWC3_EXYNOS) += dwc3-exynos.o
obj-$(CONFIG_USB_DWC3_PCI) += dwc3-pci.o
@@ -52,7 +53,11 @@ obj-$(CONFIG_USB_DWC3_MESON_G12A) += dwc3-meson-g12a.o
obj-$(CONFIG_USB_DWC3_OF_SIMPLE) += dwc3-of-simple.o
obj-$(CONFIG_USB_DWC3_ST) += dwc3-st.o
obj-$(CONFIG_USB_DWC3_QCOM) += dwc3-qcom.o
+obj-$(CONFIG_USB_DWC3_QCOM) += dwc3-qcom-legacy.o
obj-$(CONFIG_USB_DWC3_IMX8MP) += dwc3-imx8mp.o
+obj-$(CONFIG_USB_DWC3_IMX) += dwc3-imx.o
obj-$(CONFIG_USB_DWC3_XILINX) += dwc3-xilinx.o
obj-$(CONFIG_USB_DWC3_OCTEON) += dwc3-octeon.o
obj-$(CONFIG_USB_DWC3_RTK) += dwc3-rtk.o
+obj-$(CONFIG_USB_DWC3_GENERIC_PLAT) += dwc3-generic-plat.o
+obj-$(CONFIG_USB_DWC3_GOOGLE) += dwc3-google.o
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 66a08b527165..65213896de99 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -25,7 +25,9 @@
#include <linux/of.h>
#include <linux/of_graph.h>
#include <linux/acpi.h>
+#include <linux/pci.h>
#include <linux/pinctrl/consumer.h>
+#include <linux/pinctrl/devinfo.h>
#include <linux/reset.h>
#include <linux/bitfield.h>
@@ -36,6 +38,7 @@
#include "core.h"
#include "gadget.h"
+#include "glue.h"
#include "io.h"
#include "debug.h"
@@ -111,32 +114,33 @@ void dwc3_enable_susphy(struct dwc3 *dwc, bool enable)
int i;
for (i = 0; i < dwc->num_usb3_ports; i++) {
- reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(i));
+ reg = dwc3_readl(dwc, DWC3_GUSB3PIPECTL(i));
if (enable && !dwc->dis_u3_susphy_quirk)
reg |= DWC3_GUSB3PIPECTL_SUSPHY;
else
reg &= ~DWC3_GUSB3PIPECTL_SUSPHY;
- dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(i), reg);
+ dwc3_writel(dwc, DWC3_GUSB3PIPECTL(i), reg);
}
for (i = 0; i < dwc->num_usb2_ports; i++) {
- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(i));
+ reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(i));
if (enable && !dwc->dis_u2_susphy_quirk)
reg |= DWC3_GUSB2PHYCFG_SUSPHY;
else
reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(i), reg);
+ dwc3_writel(dwc, DWC3_GUSB2PHYCFG(i), reg);
}
}
+EXPORT_SYMBOL_GPL(dwc3_enable_susphy);
void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode, bool ignore_susphy)
{
unsigned int hw_mode;
u32 reg;
- reg = dwc3_readl(dwc->regs, DWC3_GCTL);
+ reg = dwc3_readl(dwc, DWC3_GCTL);
/*
* For DRD controllers, GUSB3PIPECTL.SUSPENDENABLE and
@@ -151,10 +155,12 @@ void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode, bool ignore_susphy)
reg &= ~(DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG));
reg |= DWC3_GCTL_PRTCAPDIR(mode);
- dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+ dwc3_writel(dwc, DWC3_GCTL, reg);
dwc->current_dr_role = mode;
+ trace_dwc3_set_prtcap(dwc, mode);
}
+EXPORT_SYMBOL_GPL(dwc3_set_prtcap);
static void __dwc3_set_mode(struct work_struct *work)
{
@@ -210,9 +216,9 @@ static void __dwc3_set_mode(struct work_struct *work)
if (dwc->current_dr_role && ((DWC3_IP_IS(DWC3) ||
DWC3_VER_IS_PRIOR(DWC31, 190A)) &&
desired_dr_role != DWC3_GCTL_PRTCAP_OTG)) {
- reg = dwc3_readl(dwc->regs, DWC3_GCTL);
+ reg = dwc3_readl(dwc, DWC3_GCTL);
reg |= DWC3_GCTL_CORESOFTRESET;
- dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+ dwc3_writel(dwc, DWC3_GCTL, reg);
/*
* Wait for internal clocks to synchronized. DWC_usb31 and
@@ -222,9 +228,9 @@ static void __dwc3_set_mode(struct work_struct *work)
*/
msleep(100);
- reg = dwc3_readl(dwc->regs, DWC3_GCTL);
+ reg = dwc3_readl(dwc, DWC3_GCTL);
reg &= ~DWC3_GCTL_CORESOFTRESET;
- dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+ dwc3_writel(dwc, DWC3_GCTL, reg);
}
spin_lock_irqsave(&dwc->lock, flags);
@@ -248,9 +254,9 @@ static void __dwc3_set_mode(struct work_struct *work)
phy_set_mode(dwc->usb3_generic_phy[i], PHY_MODE_USB_HOST);
if (dwc->dis_split_quirk) {
- reg = dwc3_readl(dwc->regs, DWC3_GUCTL3);
+ reg = dwc3_readl(dwc, DWC3_GUCTL3);
reg |= DWC3_GUCTL3_SPLITDISABLE;
- dwc3_writel(dwc->regs, DWC3_GUCTL3, reg);
+ dwc3_writel(dwc, DWC3_GUCTL3, reg);
}
}
break;
@@ -277,7 +283,6 @@ static void __dwc3_set_mode(struct work_struct *work)
}
out:
- pm_runtime_mark_last_busy(dwc->dev);
pm_runtime_put_autosuspend(dwc->dev);
mutex_unlock(&dwc->mutex);
}
@@ -301,11 +306,11 @@ u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type)
struct dwc3 *dwc = dep->dwc;
u32 reg;
- dwc3_writel(dwc->regs, DWC3_GDBGFIFOSPACE,
- DWC3_GDBGFIFOSPACE_NUM(dep->number) |
- DWC3_GDBGFIFOSPACE_TYPE(type));
+ dwc3_writel(dwc, DWC3_GDBGFIFOSPACE,
+ DWC3_GDBGFIFOSPACE_NUM(dep->number) |
+ DWC3_GDBGFIFOSPACE_TYPE(type));
- reg = dwc3_readl(dwc->regs, DWC3_GDBGFIFOSPACE);
+ reg = dwc3_readl(dwc, DWC3_GDBGFIFOSPACE);
return DWC3_GDBGFIFOSPACE_SPACE_AVAILABLE(reg);
}
@@ -327,7 +332,7 @@ int dwc3_core_soft_reset(struct dwc3 *dwc)
if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_HOST)
return 0;
- reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg = dwc3_readl(dwc, DWC3_DCTL);
reg |= DWC3_DCTL_CSFTRST;
reg &= ~DWC3_DCTL_RUN_STOP;
dwc3_gadget_dctl_write_safe(dwc, reg);
@@ -342,7 +347,7 @@ int dwc3_core_soft_reset(struct dwc3 *dwc)
retries = 10;
do {
- reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg = dwc3_readl(dwc, DWC3_DCTL);
if (!(reg & DWC3_DCTL_CSFTRST))
goto done;
@@ -382,12 +387,12 @@ static void dwc3_frame_length_adjustment(struct dwc3 *dwc)
if (dwc->fladj == 0)
return;
- reg = dwc3_readl(dwc->regs, DWC3_GFLADJ);
+ reg = dwc3_readl(dwc, DWC3_GFLADJ);
dft = reg & DWC3_GFLADJ_30MHZ_MASK;
if (dft != dwc->fladj) {
reg &= ~DWC3_GFLADJ_30MHZ_MASK;
reg |= DWC3_GFLADJ_30MHZ_SDBND_SEL | dwc->fladj;
- dwc3_writel(dwc->regs, DWC3_GFLADJ, reg);
+ dwc3_writel(dwc, DWC3_GFLADJ, reg);
}
}
@@ -419,10 +424,10 @@ static void dwc3_ref_clk_period(struct dwc3 *dwc)
return;
}
- reg = dwc3_readl(dwc->regs, DWC3_GUCTL);
+ reg = dwc3_readl(dwc, DWC3_GUCTL);
reg &= ~DWC3_GUCTL_REFCLKPER_MASK;
reg |= FIELD_PREP(DWC3_GUCTL_REFCLKPER_MASK, period);
- dwc3_writel(dwc->regs, DWC3_GUCTL, reg);
+ dwc3_writel(dwc, DWC3_GUCTL, reg);
if (DWC3_VER_IS_PRIOR(DWC3, 250A))
return;
@@ -450,7 +455,7 @@ static void dwc3_ref_clk_period(struct dwc3 *dwc)
*/
decr = 480000000 / rate;
- reg = dwc3_readl(dwc->regs, DWC3_GFLADJ);
+ reg = dwc3_readl(dwc, DWC3_GFLADJ);
reg &= ~DWC3_GFLADJ_REFCLK_FLADJ_MASK
& ~DWC3_GFLADJ_240MHZDECR
& ~DWC3_GFLADJ_240MHZDECR_PLS1;
@@ -461,7 +466,7 @@ static void dwc3_ref_clk_period(struct dwc3 *dwc)
if (dwc->gfladj_refclk_lpm_sel)
reg |= DWC3_GFLADJ_REFCLK_LPM_SEL;
- dwc3_writel(dwc->regs, DWC3_GFLADJ, reg);
+ dwc3_writel(dwc, DWC3_GFLADJ, reg);
}
/**
@@ -564,16 +569,16 @@ int dwc3_event_buffers_setup(struct dwc3 *dwc)
evt = dwc->ev_buf;
evt->lpos = 0;
- dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(0),
- lower_32_bits(evt->dma));
- dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(0),
- upper_32_bits(evt->dma));
- dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0),
- DWC3_GEVNTSIZ_SIZE(evt->length));
+ dwc3_writel(dwc, DWC3_GEVNTADRLO(0),
+ lower_32_bits(evt->dma));
+ dwc3_writel(dwc, DWC3_GEVNTADRHI(0),
+ upper_32_bits(evt->dma));
+ dwc3_writel(dwc, DWC3_GEVNTSIZ(0),
+ DWC3_GEVNTSIZ_SIZE(evt->length));
/* Clear any stale event */
- reg = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(0));
- dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), reg);
+ reg = dwc3_readl(dwc, DWC3_GEVNTCOUNT(0));
+ dwc3_writel(dwc, DWC3_GEVNTCOUNT(0), reg);
return 0;
}
@@ -588,7 +593,7 @@ void dwc3_event_buffers_cleanup(struct dwc3 *dwc)
* Exynos platforms may not be able to access event buffer if the
* controller failed to halt on dwc3_core_exit().
*/
- reg = dwc3_readl(dwc->regs, DWC3_DSTS);
+ reg = dwc3_readl(dwc, DWC3_DSTS);
if (!(reg & DWC3_DSTS_DEVCTRLHLT))
return;
@@ -596,14 +601,14 @@ void dwc3_event_buffers_cleanup(struct dwc3 *dwc)
evt->lpos = 0;
- dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(0), 0);
- dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(0), 0);
- dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0), DWC3_GEVNTSIZ_INTMASK
+ dwc3_writel(dwc, DWC3_GEVNTADRLO(0), 0);
+ dwc3_writel(dwc, DWC3_GEVNTADRHI(0), 0);
+ dwc3_writel(dwc, DWC3_GEVNTSIZ(0), DWC3_GEVNTSIZ_INTMASK
| DWC3_GEVNTSIZ_SIZE(0));
/* Clear any stale event */
- reg = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(0));
- dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), reg);
+ reg = dwc3_readl(dwc, DWC3_GEVNTCOUNT(0));
+ dwc3_writel(dwc, DWC3_GEVNTCOUNT(0), reg);
}
static void dwc3_core_num_eps(struct dwc3 *dwc)
@@ -617,18 +622,18 @@ static void dwc3_cache_hwparams(struct dwc3 *dwc)
{
struct dwc3_hwparams *parms = &dwc->hwparams;
- parms->hwparams0 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS0);
- parms->hwparams1 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS1);
- parms->hwparams2 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS2);
- parms->hwparams3 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS3);
- parms->hwparams4 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS4);
- parms->hwparams5 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS5);
- parms->hwparams6 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS6);
- parms->hwparams7 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS7);
- parms->hwparams8 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS8);
+ parms->hwparams0 = dwc3_readl(dwc, DWC3_GHWPARAMS0);
+ parms->hwparams1 = dwc3_readl(dwc, DWC3_GHWPARAMS1);
+ parms->hwparams2 = dwc3_readl(dwc, DWC3_GHWPARAMS2);
+ parms->hwparams3 = dwc3_readl(dwc, DWC3_GHWPARAMS3);
+ parms->hwparams4 = dwc3_readl(dwc, DWC3_GHWPARAMS4);
+ parms->hwparams5 = dwc3_readl(dwc, DWC3_GHWPARAMS5);
+ parms->hwparams6 = dwc3_readl(dwc, DWC3_GHWPARAMS6);
+ parms->hwparams7 = dwc3_readl(dwc, DWC3_GHWPARAMS7);
+ parms->hwparams8 = dwc3_readl(dwc, DWC3_GHWPARAMS8);
if (DWC3_IP_IS(DWC32))
- parms->hwparams9 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS9);
+ parms->hwparams9 = dwc3_readl(dwc, DWC3_GHWPARAMS9);
}
static void dwc3_config_soc_bus(struct dwc3 *dwc)
@@ -636,10 +641,10 @@ static void dwc3_config_soc_bus(struct dwc3 *dwc)
if (dwc->gsbuscfg0_reqinfo != DWC3_GSBUSCFG0_REQINFO_UNSPECIFIED) {
u32 reg;
- reg = dwc3_readl(dwc->regs, DWC3_GSBUSCFG0);
+ reg = dwc3_readl(dwc, DWC3_GSBUSCFG0);
reg &= ~DWC3_GSBUSCFG0_REQINFO(~0);
reg |= DWC3_GSBUSCFG0_REQINFO(dwc->gsbuscfg0_reqinfo);
- dwc3_writel(dwc->regs, DWC3_GSBUSCFG0, reg);
+ dwc3_writel(dwc, DWC3_GSBUSCFG0, reg);
}
}
@@ -663,7 +668,7 @@ static int dwc3_ss_phy_setup(struct dwc3 *dwc, int index)
{
u32 reg;
- reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(index));
+ reg = dwc3_readl(dwc, DWC3_GUSB3PIPECTL(index));
/*
* Make sure UX_EXIT_PX is cleared as that causes issues with some
@@ -701,7 +706,7 @@ static int dwc3_ss_phy_setup(struct dwc3 *dwc, int index)
if (dwc->dis_del_phy_power_chg_quirk)
reg &= ~DWC3_GUSB3PIPECTL_DEPOCHANGE;
- dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(index), reg);
+ dwc3_writel(dwc, DWC3_GUSB3PIPECTL(index), reg);
return 0;
}
@@ -710,7 +715,7 @@ static int dwc3_hs_phy_setup(struct dwc3 *dwc, int index)
{
u32 reg;
- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(index));
+ reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(index));
/* Select the HS PHY interface */
switch (DWC3_GHWPARAMS3_HSPHY_IFC(dwc->hwparams.hwparams3)) {
@@ -722,7 +727,7 @@ static int dwc3_hs_phy_setup(struct dwc3 *dwc, int index)
} else if (dwc->hsphy_interface &&
!strncmp(dwc->hsphy_interface, "ulpi", 4)) {
reg |= DWC3_GUSB2PHYCFG_ULPI_UTMI;
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(index), reg);
+ dwc3_writel(dwc, DWC3_GUSB2PHYCFG(index), reg);
} else {
/* Relying on default value. */
if (!(reg & DWC3_GUSB2PHYCFG_ULPI_UTMI))
@@ -772,11 +777,29 @@ static int dwc3_hs_phy_setup(struct dwc3 *dwc, int index)
if (dwc->ulpi_ext_vbus_drv)
reg |= DWC3_GUSB2PHYCFG_ULPIEXTVBUSDRV;
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(index), reg);
+ dwc3_writel(dwc, DWC3_GUSB2PHYCFG(index), reg);
return 0;
}
+static void dwc3_ulpi_setup(struct dwc3 *dwc)
+{
+ int index;
+ u32 reg;
+
+ /* Don't do anything if there is no ULPI PHY */
+ if (!dwc->ulpi)
+ return;
+
+ if (dwc->enable_usb2_transceiver_delay) {
+ for (index = 0; index < dwc->num_usb2_ports; index++) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(index));
+ reg |= DWC3_GUSB2PHYCFG_XCVRDLY;
+ dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(index), reg);
+ }
+ }
+}
+
/**
* dwc3_phy_setup - Configure USB PHY Interface of DWC3 Core
* @dwc: Pointer to our controller context structure
@@ -972,7 +995,7 @@ static void dwc3_clk_disable(struct dwc3 *dwc)
clk_disable_unprepare(dwc->bus_clk);
}
-static void dwc3_core_exit(struct dwc3 *dwc)
+void dwc3_core_exit(struct dwc3 *dwc)
{
dwc3_event_buffers_cleanup(dwc);
dwc3_phy_power_off(dwc);
@@ -980,20 +1003,23 @@ static void dwc3_core_exit(struct dwc3 *dwc)
dwc3_clk_disable(dwc);
reset_control_assert(dwc->reset);
}
+EXPORT_SYMBOL_GPL(dwc3_core_exit);
static bool dwc3_core_is_valid(struct dwc3 *dwc)
{
u32 reg;
- reg = dwc3_readl(dwc->regs, DWC3_GSNPSID);
+ reg = dwc3_readl(dwc, DWC3_GSNPSID);
dwc->ip = DWC3_GSNPS_ID(reg);
+ if (dwc->ip == DWC4_IP)
+ dwc->ip = DWC32_IP;
/* This should read as U3 followed by revision number */
if (DWC3_IP_IS(DWC3)) {
dwc->revision = reg;
} else if (DWC3_IP_IS(DWC31) || DWC3_IP_IS(DWC32)) {
- dwc->revision = dwc3_readl(dwc->regs, DWC3_VER_NUMBER);
- dwc->version_type = dwc3_readl(dwc->regs, DWC3_VER_TYPE);
+ dwc->revision = dwc3_readl(dwc, DWC3_VER_NUMBER);
+ dwc->version_type = dwc3_readl(dwc, DWC3_VER_TYPE);
} else {
return false;
}
@@ -1007,7 +1033,7 @@ static void dwc3_core_setup_global_control(struct dwc3 *dwc)
unsigned int hw_mode;
u32 reg;
- reg = dwc3_readl(dwc->regs, DWC3_GCTL);
+ reg = dwc3_readl(dwc, DWC3_GCTL);
reg &= ~DWC3_GCTL_SCALEDOWN_MASK;
hw_mode = DWC3_GHWPARAMS0_MODE(dwc->hwparams.hwparams0);
power_opt = DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1);
@@ -1085,7 +1111,7 @@ static void dwc3_core_setup_global_control(struct dwc3 *dwc)
if (DWC3_VER_IS_PRIOR(DWC3, 190A))
reg |= DWC3_GCTL_U2RSTECN;
- dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+ dwc3_writel(dwc, DWC3_GCTL, reg);
}
static int dwc3_core_get_phy(struct dwc3 *dwc);
@@ -1105,7 +1131,7 @@ static void dwc3_set_incr_burst_type(struct dwc3 *dwc)
int ret;
int i;
- cfg = dwc3_readl(dwc->regs, DWC3_GSBUSCFG0);
+ cfg = dwc3_readl(dwc, DWC3_GSBUSCFG0);
/*
* Handle property "snps,incr-burst-type-adjustment".
@@ -1180,7 +1206,7 @@ static void dwc3_set_incr_burst_type(struct dwc3 *dwc)
break;
}
- dwc3_writel(dwc->regs, DWC3_GSBUSCFG0, cfg);
+ dwc3_writel(dwc, DWC3_GSBUSCFG0, cfg);
}
static void dwc3_set_power_down_clk_scale(struct dwc3 *dwc)
@@ -1205,12 +1231,12 @@ static void dwc3_set_power_down_clk_scale(struct dwc3 *dwc)
* (3x or more) to be within the requirement.
*/
scale = DIV_ROUND_UP(clk_get_rate(dwc->susp_clk), 16000);
- reg = dwc3_readl(dwc->regs, DWC3_GCTL);
+ reg = dwc3_readl(dwc, DWC3_GCTL);
if ((reg & DWC3_GCTL_PWRDNSCALE_MASK) < DWC3_GCTL_PWRDNSCALE(scale) ||
(reg & DWC3_GCTL_PWRDNSCALE_MASK) > DWC3_GCTL_PWRDNSCALE(scale*3)) {
reg &= ~(DWC3_GCTL_PWRDNSCALE_MASK);
reg |= DWC3_GCTL_PWRDNSCALE(scale);
- dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+ dwc3_writel(dwc, DWC3_GCTL, reg);
}
}
@@ -1233,7 +1259,7 @@ static void dwc3_config_threshold(struct dwc3 *dwc)
tx_maxburst = dwc->tx_max_burst_prd;
if (rx_thr_num && rx_maxburst) {
- reg = dwc3_readl(dwc->regs, DWC3_GRXTHRCFG);
+ reg = dwc3_readl(dwc, DWC3_GRXTHRCFG);
reg |= DWC31_RXTHRNUMPKTSEL_PRD;
reg &= ~DWC31_RXTHRNUMPKT_PRD(~0);
@@ -1242,11 +1268,11 @@ static void dwc3_config_threshold(struct dwc3 *dwc)
reg &= ~DWC31_MAXRXBURSTSIZE_PRD(~0);
reg |= DWC31_MAXRXBURSTSIZE_PRD(rx_maxburst);
- dwc3_writel(dwc->regs, DWC3_GRXTHRCFG, reg);
+ dwc3_writel(dwc, DWC3_GRXTHRCFG, reg);
}
if (tx_thr_num && tx_maxburst) {
- reg = dwc3_readl(dwc->regs, DWC3_GTXTHRCFG);
+ reg = dwc3_readl(dwc, DWC3_GTXTHRCFG);
reg |= DWC31_TXTHRNUMPKTSEL_PRD;
reg &= ~DWC31_TXTHRNUMPKT_PRD(~0);
@@ -1255,7 +1281,7 @@ static void dwc3_config_threshold(struct dwc3 *dwc)
reg &= ~DWC31_MAXTXBURSTSIZE_PRD(~0);
reg |= DWC31_MAXTXBURSTSIZE_PRD(tx_maxburst);
- dwc3_writel(dwc->regs, DWC3_GTXTHRCFG, reg);
+ dwc3_writel(dwc, DWC3_GTXTHRCFG, reg);
}
}
@@ -1266,7 +1292,7 @@ static void dwc3_config_threshold(struct dwc3 *dwc)
if (DWC3_IP_IS(DWC3)) {
if (rx_thr_num && rx_maxburst) {
- reg = dwc3_readl(dwc->regs, DWC3_GRXTHRCFG);
+ reg = dwc3_readl(dwc, DWC3_GRXTHRCFG);
reg |= DWC3_GRXTHRCFG_PKTCNTSEL;
reg &= ~DWC3_GRXTHRCFG_RXPKTCNT(~0);
@@ -1275,11 +1301,11 @@ static void dwc3_config_threshold(struct dwc3 *dwc)
reg &= ~DWC3_GRXTHRCFG_MAXRXBURSTSIZE(~0);
reg |= DWC3_GRXTHRCFG_MAXRXBURSTSIZE(rx_maxburst);
- dwc3_writel(dwc->regs, DWC3_GRXTHRCFG, reg);
+ dwc3_writel(dwc, DWC3_GRXTHRCFG, reg);
}
if (tx_thr_num && tx_maxburst) {
- reg = dwc3_readl(dwc->regs, DWC3_GTXTHRCFG);
+ reg = dwc3_readl(dwc, DWC3_GTXTHRCFG);
reg |= DWC3_GTXTHRCFG_PKTCNTSEL;
reg &= ~DWC3_GTXTHRCFG_TXPKTCNT(~0);
@@ -1288,11 +1314,11 @@ static void dwc3_config_threshold(struct dwc3 *dwc)
reg &= ~DWC3_GTXTHRCFG_MAXTXBURSTSIZE(~0);
reg |= DWC3_GTXTHRCFG_MAXTXBURSTSIZE(tx_maxburst);
- dwc3_writel(dwc->regs, DWC3_GTXTHRCFG, reg);
+ dwc3_writel(dwc, DWC3_GTXTHRCFG, reg);
}
} else {
if (rx_thr_num && rx_maxburst) {
- reg = dwc3_readl(dwc->regs, DWC3_GRXTHRCFG);
+ reg = dwc3_readl(dwc, DWC3_GRXTHRCFG);
reg |= DWC31_GRXTHRCFG_PKTCNTSEL;
reg &= ~DWC31_GRXTHRCFG_RXPKTCNT(~0);
@@ -1301,11 +1327,11 @@ static void dwc3_config_threshold(struct dwc3 *dwc)
reg &= ~DWC31_GRXTHRCFG_MAXRXBURSTSIZE(~0);
reg |= DWC31_GRXTHRCFG_MAXRXBURSTSIZE(rx_maxburst);
- dwc3_writel(dwc->regs, DWC3_GRXTHRCFG, reg);
+ dwc3_writel(dwc, DWC3_GRXTHRCFG, reg);
}
if (tx_thr_num && tx_maxburst) {
- reg = dwc3_readl(dwc->regs, DWC3_GTXTHRCFG);
+ reg = dwc3_readl(dwc, DWC3_GTXTHRCFG);
reg |= DWC31_GTXTHRCFG_PKTCNTSEL;
reg &= ~DWC31_GTXTHRCFG_TXPKTCNT(~0);
@@ -1314,7 +1340,7 @@ static void dwc3_config_threshold(struct dwc3 *dwc)
reg &= ~DWC31_GTXTHRCFG_MAXTXBURSTSIZE(~0);
reg |= DWC31_GTXTHRCFG_MAXTXBURSTSIZE(tx_maxburst);
- dwc3_writel(dwc->regs, DWC3_GTXTHRCFG, reg);
+ dwc3_writel(dwc, DWC3_GTXTHRCFG, reg);
}
}
}
@@ -1325,7 +1351,7 @@ static void dwc3_config_threshold(struct dwc3 *dwc)
*
* Returns 0 on success otherwise negative errno.
*/
-static int dwc3_core_init(struct dwc3 *dwc)
+int dwc3_core_init(struct dwc3 *dwc)
{
unsigned int hw_mode;
u32 reg;
@@ -1333,12 +1359,6 @@ static int dwc3_core_init(struct dwc3 *dwc)
hw_mode = DWC3_GHWPARAMS0_MODE(dwc->hwparams.hwparams0);
- /*
- * Write Linux Version Code to our GUID register so it's easy to figure
- * out which kernel version a bug was found.
- */
- dwc3_writel(dwc->regs, DWC3_GUID, LINUX_VERSION_CODE);
-
ret = dwc3_phy_setup(dwc);
if (ret)
return ret;
@@ -1355,6 +1375,8 @@ static int dwc3_core_init(struct dwc3 *dwc)
dwc->ulpi_ready = true;
}
+ dwc3_ulpi_setup(dwc);
+
if (!dwc->phys_ready) {
ret = dwc3_core_get_phy(dwc);
if (ret)
@@ -1370,6 +1392,12 @@ static int dwc3_core_init(struct dwc3 *dwc)
if (ret)
goto err_exit_phy;
+ /*
+ * Write Linux Version Code to our GUID register so it's easy to figure
+ * out which kernel version a bug was found.
+ */
+ dwc3_writel(dwc, DWC3_GUID, LINUX_VERSION_CODE);
+
dwc3_core_setup_global_control(dwc);
dwc3_core_num_eps(dwc);
@@ -1402,9 +1430,9 @@ static int dwc3_core_init(struct dwc3 *dwc)
* DWC_usb31 controller.
*/
if (DWC3_VER_IS_WITHIN(DWC3, 310A, ANY)) {
- reg = dwc3_readl(dwc->regs, DWC3_GUCTL2);
+ reg = dwc3_readl(dwc, DWC3_GUCTL2);
reg |= DWC3_GUCTL2_RST_ACTBITLATER;
- dwc3_writel(dwc->regs, DWC3_GUCTL2, reg);
+ dwc3_writel(dwc, DWC3_GUCTL2, reg);
}
/*
@@ -1417,9 +1445,9 @@ static int dwc3_core_init(struct dwc3 *dwc)
* setting GUCTL2[19] by default; instead, use GUCTL2[19] = 0.
*/
if (DWC3_VER_IS(DWC3, 320A)) {
- reg = dwc3_readl(dwc->regs, DWC3_GUCTL2);
+ reg = dwc3_readl(dwc, DWC3_GUCTL2);
reg &= ~DWC3_GUCTL2_LC_TIMER;
- dwc3_writel(dwc->regs, DWC3_GUCTL2, reg);
+ dwc3_writel(dwc, DWC3_GUCTL2, reg);
}
/*
@@ -1432,13 +1460,13 @@ static int dwc3_core_init(struct dwc3 *dwc)
* legacy ULPI PHYs.
*/
if (dwc->resume_hs_terminations) {
- reg = dwc3_readl(dwc->regs, DWC3_GUCTL1);
+ reg = dwc3_readl(dwc, DWC3_GUCTL1);
reg |= DWC3_GUCTL1_RESUME_OPMODE_HS_HOST;
- dwc3_writel(dwc->regs, DWC3_GUCTL1, reg);
+ dwc3_writel(dwc, DWC3_GUCTL1, reg);
}
if (!DWC3_VER_IS_PRIOR(DWC3, 250A)) {
- reg = dwc3_readl(dwc->regs, DWC3_GUCTL1);
+ reg = dwc3_readl(dwc, DWC3_GUCTL1);
/*
* Enable hardware control of sending remote wakeup
@@ -1473,24 +1501,20 @@ static int dwc3_core_init(struct dwc3 *dwc)
reg &= ~DWC3_GUCTL1_DEV_FORCE_20_CLK_FOR_30_CLK;
}
- dwc3_writel(dwc->regs, DWC3_GUCTL1, reg);
+ dwc3_writel(dwc, DWC3_GUCTL1, reg);
}
dwc3_config_threshold(dwc);
- /*
- * Modify this for all supported Super Speed ports when
- * multiport support is added.
- */
if (hw_mode != DWC3_GHWPARAMS0_MODE_GADGET &&
(DWC3_IP_IS(DWC31)) &&
dwc->maximum_speed == USB_SPEED_SUPER) {
int i;
for (i = 0; i < dwc->num_usb3_ports; i++) {
- reg = dwc3_readl(dwc->regs, DWC3_LLUCTL(i));
+ reg = dwc3_readl(dwc, DWC3_LLUCTL(i));
reg |= DWC3_LLUCTL_FORCE_GEN1;
- dwc3_writel(dwc->regs, DWC3_LLUCTL(i), reg);
+ dwc3_writel(dwc, DWC3_LLUCTL(i), reg);
}
}
@@ -1509,9 +1533,9 @@ static int dwc3_core_init(struct dwc3 *dwc)
* function is available only from version 1.70a.
*/
if (DWC3_VER_IS_WITHIN(DWC31, 170A, 180A)) {
- reg = dwc3_readl(dwc->regs, DWC3_GUCTL3);
+ reg = dwc3_readl(dwc, DWC3_GUCTL3);
reg |= DWC3_GUCTL3_USB20_RETRY_DISABLE;
- dwc3_writel(dwc->regs, DWC3_GUCTL3, reg);
+ dwc3_writel(dwc, DWC3_GUCTL3, reg);
}
return 0;
@@ -1525,6 +1549,7 @@ err_exit_ulpi:
return ret;
}
+EXPORT_SYMBOL_GPL(dwc3_core_init);
static int dwc3_core_get_phy(struct dwc3 *dwc)
{
@@ -1663,14 +1688,24 @@ static void dwc3_core_exit_mode(struct dwc3 *dwc)
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE, true);
}
-static void dwc3_get_software_properties(struct dwc3 *dwc)
+static void dwc3_get_software_properties(struct dwc3 *dwc,
+ const struct dwc3_properties *properties)
{
struct device *tmpdev;
u16 gsbuscfg0_reqinfo;
int ret;
+ if (properties->needs_full_reinit)
+ dwc->needs_full_reinit = true;
+
dwc->gsbuscfg0_reqinfo = DWC3_GSBUSCFG0_REQINFO_UNSPECIFIED;
+ if (properties->gsbuscfg0_reqinfo !=
+ DWC3_GSBUSCFG0_REQINFO_UNSPECIFIED) {
+ dwc->gsbuscfg0_reqinfo = properties->gsbuscfg0_reqinfo;
+ return;
+ }
+
/*
* Iterate over all parent nodes for finding swnode properties
* and non-DT (non-ABI) properties.
@@ -1699,6 +1734,7 @@ static void dwc3_get_properties(struct dwc3 *dwc)
u8 tx_thr_num_pkt_prd = 0;
u8 tx_max_burst_prd = 0;
u8 tx_fifo_resize_max_num;
+ u16 num_hc_interrupters;
/* default to highest possible threshold */
lpm_nyet_threshold = 0xf;
@@ -1719,6 +1755,9 @@ static void dwc3_get_properties(struct dwc3 *dwc)
*/
tx_fifo_resize_max_num = 6;
+ /* default to a single XHCI interrupter */
+ num_hc_interrupters = 1;
+
dwc->maximum_speed = usb_get_maximum_speed(dev);
dwc->max_ssp_rate = usb_get_maximum_ssp_rate(dev);
dwc->dr_mode = usb_get_dr_mode(dev);
@@ -1765,6 +1804,12 @@ static void dwc3_get_properties(struct dwc3 *dwc)
&tx_thr_num_pkt_prd);
device_property_read_u8(dev, "snps,tx-max-burst-prd",
&tx_max_burst_prd);
+ device_property_read_u16(dev, "num-hc-interrupters",
+ &num_hc_interrupters);
+ /* DWC3 core allowed to have a max of 8 interrupters */
+ if (num_hc_interrupters > 8)
+ num_hc_interrupters = 8;
+
dwc->do_fifo_resize = device_property_read_bool(dev,
"tx-fifo-resize");
if (dwc->do_fifo_resize)
@@ -1851,6 +1896,8 @@ static void dwc3_get_properties(struct dwc3 *dwc)
dwc->tx_max_burst_prd = tx_max_burst_prd;
dwc->tx_fifo_resize_max_num = tx_fifo_resize_max_num;
+
+ dwc->num_hc_interrupters = num_hc_interrupters;
}
/* check whether the core supports IMOD */
@@ -2131,6 +2178,20 @@ static int dwc3_get_num_ports(struct dwc3 *dwc)
return 0;
}
+static void dwc3_vbus_draw_work(struct work_struct *work)
+{
+ struct dwc3 *dwc = container_of(work, struct dwc3, vbus_draw_work);
+ union power_supply_propval val = {0};
+ int ret;
+
+ val.intval = 1000 * (dwc->current_limit);
+ ret = power_supply_set_property(dwc->usb_psy, POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, &val);
+
+ if (ret < 0)
+ dev_dbg(dwc->dev, "Error (%d) setting vbus draw (%d mA)\n",
+ ret, dwc->current_limit);
+}
+
static struct power_supply *dwc3_get_usb_power_supply(struct dwc3 *dwc)
{
struct power_supply *usb_psy;
@@ -2145,30 +2206,20 @@ static struct power_supply *dwc3_get_usb_power_supply(struct dwc3 *dwc)
if (!usb_psy)
return ERR_PTR(-EPROBE_DEFER);
+ INIT_WORK(&dwc->vbus_draw_work, dwc3_vbus_draw_work);
return usb_psy;
}
-static int dwc3_probe(struct platform_device *pdev)
+int dwc3_core_probe(const struct dwc3_probe_data *data)
{
- struct device *dev = &pdev->dev;
- struct resource *res, dwc_res;
+ struct dwc3 *dwc = data->dwc;
+ struct device *dev = dwc->dev;
+ struct resource dwc_res;
unsigned int hw_mode;
void __iomem *regs;
- struct dwc3 *dwc;
+ struct resource *res = data->res;
int ret;
- dwc = devm_kzalloc(dev, sizeof(*dwc), GFP_KERNEL);
- if (!dwc)
- return -ENOMEM;
-
- dwc->dev = dev;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(dev, "missing memory resource\n");
- return -ENODEV;
- }
-
dwc->xhci_resources[0].start = res->start;
dwc->xhci_resources[0].end = dwc->xhci_resources[0].start +
DWC3_XHCI_REGS_END;
@@ -2202,21 +2253,23 @@ static int dwc3_probe(struct platform_device *pdev)
dwc3_get_properties(dwc);
- dwc3_get_software_properties(dwc);
+ dwc3_get_software_properties(dwc, &data->properties);
dwc->usb_psy = dwc3_get_usb_power_supply(dwc);
if (IS_ERR(dwc->usb_psy))
return dev_err_probe(dev, PTR_ERR(dwc->usb_psy), "couldn't get usb power supply\n");
- dwc->reset = devm_reset_control_array_get_optional_shared(dev);
- if (IS_ERR(dwc->reset)) {
- ret = PTR_ERR(dwc->reset);
- goto err_put_psy;
- }
+ if (!data->ignore_clocks_and_resets) {
+ dwc->reset = devm_reset_control_array_get_optional_shared(dev);
+ if (IS_ERR(dwc->reset)) {
+ ret = PTR_ERR(dwc->reset);
+ goto err_put_psy;
+ }
- ret = dwc3_get_clocks(dwc);
- if (ret)
- goto err_put_psy;
+ ret = dwc3_get_clocks(dwc);
+ if (ret)
+ goto err_put_psy;
+ }
ret = reset_control_deassert(dwc->reset);
if (ret)
@@ -2232,10 +2285,10 @@ static int dwc3_probe(struct platform_device *pdev)
goto err_disable_clks;
}
- platform_set_drvdata(pdev, dwc);
+ dev_set_drvdata(dev, dwc);
dwc3_cache_hwparams(dwc);
- if (!dwc->sysdev_is_parent &&
+ if (!dev_is_pci(dwc->sysdev) &&
DWC3_GHWPARAMS0_AWIDTH(dwc->hwparams.hwparams0) == 64) {
ret = dma_set_mask_and_coherent(dwc->sysdev, DMA_BIT_MASK(64));
if (ret)
@@ -2293,9 +2346,11 @@ static int dwc3_probe(struct platform_device *pdev)
dwc3_check_params(dwc);
dwc3_debugfs_init(dwc);
- ret = dwc3_core_init_mode(dwc);
- if (ret)
- goto err_exit_debugfs;
+ if (!data->skip_core_init_mode) {
+ ret = dwc3_core_init_mode(dwc);
+ if (ret)
+ goto err_exit_debugfs;
+ }
pm_runtime_put(dev);
@@ -2327,12 +2382,37 @@ err_put_psy:
return ret;
}
+EXPORT_SYMBOL_GPL(dwc3_core_probe);
-static void dwc3_remove(struct platform_device *pdev)
+static int dwc3_probe(struct platform_device *pdev)
{
- struct dwc3 *dwc = platform_get_drvdata(pdev);
+ struct dwc3_probe_data probe_data = {};
+ struct resource *res;
+ struct dwc3 *dwc;
- pm_runtime_get_sync(&pdev->dev);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "missing memory resource\n");
+ return -ENODEV;
+ }
+
+ dwc = devm_kzalloc(&pdev->dev, sizeof(*dwc), GFP_KERNEL);
+ if (!dwc)
+ return -ENOMEM;
+
+ dwc->dev = &pdev->dev;
+ dwc->glue_ops = NULL;
+
+ probe_data.dwc = dwc;
+ probe_data.res = res;
+ probe_data.properties = DWC3_DEFAULT_PROPERTIES;
+
+ return dwc3_core_probe(&probe_data);
+}
+
+void dwc3_core_remove(struct dwc3 *dwc)
+{
+ pm_runtime_get_sync(dwc->dev);
dwc3_core_exit_mode(dwc);
dwc3_debugfs_exit(dwc);
@@ -2340,21 +2420,29 @@ static void dwc3_remove(struct platform_device *pdev)
dwc3_core_exit(dwc);
dwc3_ulpi_exit(dwc);
- pm_runtime_allow(&pdev->dev);
- pm_runtime_disable(&pdev->dev);
- pm_runtime_dont_use_autosuspend(&pdev->dev);
- pm_runtime_put_noidle(&pdev->dev);
+ pm_runtime_allow(dwc->dev);
+ pm_runtime_disable(dwc->dev);
+ pm_runtime_dont_use_autosuspend(dwc->dev);
+ pm_runtime_put_noidle(dwc->dev);
/*
* HACK: Clear the driver data, which is currently accessed by parent
* glue drivers, before allowing the parent to suspend.
*/
- platform_set_drvdata(pdev, NULL);
- pm_runtime_set_suspended(&pdev->dev);
+ dev_set_drvdata(dwc->dev, NULL);
+ pm_runtime_set_suspended(dwc->dev);
dwc3_free_event_buffers(dwc);
- if (dwc->usb_psy)
+ if (dwc->usb_psy) {
+ cancel_work_sync(&dwc->vbus_draw_work);
power_supply_put(dwc->usb_psy);
+ }
+}
+EXPORT_SYMBOL_GPL(dwc3_core_remove);
+
+static void dwc3_remove(struct platform_device *pdev)
+{
+ dwc3_core_remove(platform_get_drvdata(pdev));
}
#ifdef CONFIG_PM
@@ -2388,11 +2476,12 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
{
u32 reg;
int i;
+ int ret;
if (!pm_runtime_suspended(dwc->dev) && !PMSG_IS_AUTO(msg)) {
- dwc->susphy_state = (dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)) &
+ dwc->susphy_state = (dwc3_readl(dwc, DWC3_GUSB2PHYCFG(0)) &
DWC3_GUSB2PHYCFG_SUSPHY) ||
- (dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)) &
+ (dwc3_readl(dwc, DWC3_GUSB3PIPECTL(0)) &
DWC3_GUSB3PIPECTL_SUSPHY);
/*
* TI AM62 platform requires SUSPHY to be
@@ -2406,12 +2495,15 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
case DWC3_GCTL_PRTCAP_DEVICE:
if (pm_runtime_suspended(dwc->dev))
break;
- dwc3_gadget_suspend(dwc);
+ ret = dwc3_gadget_suspend(dwc);
+ if (ret)
+ return ret;
synchronize_irq(dwc->irq_gadget);
dwc3_core_exit(dwc);
break;
case DWC3_GCTL_PRTCAP_HOST:
- if (!PMSG_IS_AUTO(msg) && !device_may_wakeup(dwc->dev)) {
+ if (!PMSG_IS_AUTO(msg) &&
+ (!device_may_wakeup(dwc->dev) || dwc->needs_full_reinit)) {
dwc3_core_exit(dwc);
break;
}
@@ -2420,10 +2512,10 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
if (dwc->dis_u2_susphy_quirk ||
dwc->dis_enblslpm_quirk) {
for (i = 0; i < dwc->num_usb2_ports; i++) {
- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(i));
+ reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(i));
reg |= DWC3_GUSB2PHYCFG_ENBLSLPM |
DWC3_GUSB2PHYCFG_SUSPHY;
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(i), reg);
+ dwc3_writel(dwc, DWC3_GUSB2PHYCFG(i), reg);
}
/* Give some time for USB2 PHY to suspend */
@@ -2441,7 +2533,9 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
break;
if (dwc->current_otg_role == DWC3_OTG_ROLE_DEVICE) {
- dwc3_gadget_suspend(dwc);
+ ret = dwc3_gadget_suspend(dwc);
+ if (ret)
+ return ret;
synchronize_irq(dwc->irq_gadget);
}
@@ -2472,7 +2566,8 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg)
dwc3_gadget_resume(dwc);
break;
case DWC3_GCTL_PRTCAP_HOST:
- if (!PMSG_IS_AUTO(msg) && !device_may_wakeup(dwc->dev)) {
+ if (!PMSG_IS_AUTO(msg) &&
+ (!device_may_wakeup(dwc->dev) || dwc->needs_full_reinit)) {
ret = dwc3_core_init_for_resume(dwc);
if (ret)
return ret;
@@ -2481,14 +2576,14 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg)
}
/* Restore GUSB2PHYCFG bits that were modified in suspend */
for (i = 0; i < dwc->num_usb2_ports; i++) {
- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(i));
+ reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(i));
if (dwc->dis_u2_susphy_quirk)
reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
if (dwc->dis_enblslpm_quirk)
reg &= ~DWC3_GUSB2PHYCFG_ENBLSLPM;
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(i), reg);
+ dwc3_writel(dwc, DWC3_GUSB2PHYCFG(i), reg);
}
for (i = 0; i < dwc->num_usb2_ports; i++)
@@ -2544,9 +2639,8 @@ static int dwc3_runtime_checks(struct dwc3 *dwc)
return 0;
}
-static int dwc3_runtime_suspend(struct device *dev)
+int dwc3_runtime_suspend(struct dwc3 *dwc)
{
- struct dwc3 *dwc = dev_get_drvdata(dev);
int ret;
if (dwc3_runtime_checks(dwc))
@@ -2558,10 +2652,11 @@ static int dwc3_runtime_suspend(struct device *dev)
return 0;
}
+EXPORT_SYMBOL_GPL(dwc3_runtime_suspend);
-static int dwc3_runtime_resume(struct device *dev)
+int dwc3_runtime_resume(struct dwc3 *dwc)
{
- struct dwc3 *dwc = dev_get_drvdata(dev);
+ struct device *dev = dwc->dev;
int ret;
ret = dwc3_resume_common(dwc, PMSG_AUTO_RESUME);
@@ -2571,7 +2666,7 @@ static int dwc3_runtime_resume(struct device *dev)
switch (dwc->current_dr_role) {
case DWC3_GCTL_PRTCAP_DEVICE:
if (dwc->pending_events) {
- pm_runtime_put(dwc->dev);
+ pm_runtime_put(dev);
dwc->pending_events = false;
enable_irq(dwc->irq_gadget);
}
@@ -2586,10 +2681,11 @@ static int dwc3_runtime_resume(struct device *dev)
return 0;
}
+EXPORT_SYMBOL_GPL(dwc3_runtime_resume);
-static int dwc3_runtime_idle(struct device *dev)
+int dwc3_runtime_idle(struct dwc3 *dwc)
{
- struct dwc3 *dwc = dev_get_drvdata(dev);
+ struct device *dev = dwc->dev;
switch (dwc->current_dr_role) {
case DWC3_GCTL_PRTCAP_DEVICE:
@@ -2602,17 +2698,32 @@ static int dwc3_runtime_idle(struct device *dev)
break;
}
- pm_runtime_mark_last_busy(dev);
pm_runtime_autosuspend(dev);
return 0;
}
+EXPORT_SYMBOL_GPL(dwc3_runtime_idle);
+
+static int dwc3_plat_runtime_suspend(struct device *dev)
+{
+ return dwc3_runtime_suspend(dev_get_drvdata(dev));
+}
+
+static int dwc3_plat_runtime_resume(struct device *dev)
+{
+ return dwc3_runtime_resume(dev_get_drvdata(dev));
+}
+
+static int dwc3_plat_runtime_idle(struct device *dev)
+{
+ return dwc3_runtime_idle(dev_get_drvdata(dev));
+}
#endif /* CONFIG_PM */
#ifdef CONFIG_PM_SLEEP
-static int dwc3_suspend(struct device *dev)
+int dwc3_pm_suspend(struct dwc3 *dwc)
{
- struct dwc3 *dwc = dev_get_drvdata(dev);
+ struct device *dev = dwc->dev;
int ret;
ret = dwc3_suspend_common(dwc, PMSG_SUSPEND);
@@ -2623,10 +2734,11 @@ static int dwc3_suspend(struct device *dev)
return 0;
}
+EXPORT_SYMBOL_GPL(dwc3_pm_suspend);
-static int dwc3_resume(struct device *dev)
+int dwc3_pm_resume(struct dwc3 *dwc)
{
- struct dwc3 *dwc = dev_get_drvdata(dev);
+ struct device *dev = dwc->dev;
int ret = 0;
pinctrl_pm_select_default_state(dev);
@@ -2645,34 +2757,73 @@ out:
return ret;
}
+EXPORT_SYMBOL_GPL(dwc3_pm_resume);
-static void dwc3_complete(struct device *dev)
+void dwc3_pm_complete(struct dwc3 *dwc)
{
- struct dwc3 *dwc = dev_get_drvdata(dev);
u32 reg;
if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_HOST &&
dwc->dis_split_quirk) {
- reg = dwc3_readl(dwc->regs, DWC3_GUCTL3);
+ reg = dwc3_readl(dwc, DWC3_GUCTL3);
reg |= DWC3_GUCTL3_SPLITDISABLE;
- dwc3_writel(dwc->regs, DWC3_GUCTL3, reg);
+ dwc3_writel(dwc, DWC3_GUCTL3, reg);
}
}
+EXPORT_SYMBOL_GPL(dwc3_pm_complete);
+
+int dwc3_pm_prepare(struct dwc3 *dwc)
+{
+ struct device *dev = dwc->dev;
+
+ /*
+ * Indicate to the PM core that it may safely leave the device in
+ * runtime suspend if runtime-suspended already in device mode.
+ */
+ if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_DEVICE &&
+ pm_runtime_suspended(dev) &&
+ !dev_pinctrl(dev))
+ return 1;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(dwc3_pm_prepare);
+
+static int dwc3_plat_suspend(struct device *dev)
+{
+ return dwc3_pm_suspend(dev_get_drvdata(dev));
+}
+
+static int dwc3_plat_resume(struct device *dev)
+{
+ return dwc3_pm_resume(dev_get_drvdata(dev));
+}
+
+static void dwc3_plat_complete(struct device *dev)
+{
+ dwc3_pm_complete(dev_get_drvdata(dev));
+}
+
+static int dwc3_plat_prepare(struct device *dev)
+{
+ return dwc3_pm_prepare(dev_get_drvdata(dev));
+}
#else
-#define dwc3_complete NULL
+#define dwc3_plat_complete NULL
+#define dwc3_plat_prepare NULL
#endif /* CONFIG_PM_SLEEP */
static const struct dev_pm_ops dwc3_dev_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(dwc3_suspend, dwc3_resume)
- .complete = dwc3_complete,
-
+ SET_SYSTEM_SLEEP_PM_OPS(dwc3_plat_suspend, dwc3_plat_resume)
+ .complete = dwc3_plat_complete,
+ .prepare = dwc3_plat_prepare,
/*
* Runtime suspend halts the controller on disconnection. It relies on
* platforms with custom connection notification to start the controller
* again.
*/
- SET_RUNTIME_PM_OPS(dwc3_runtime_suspend, dwc3_runtime_resume,
- dwc3_runtime_idle)
+ SET_RUNTIME_PM_OPS(dwc3_plat_runtime_suspend, dwc3_plat_runtime_resume,
+ dwc3_plat_runtime_idle)
};
#ifdef CONFIG_OF
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index aaa39e663f60..e0dee9d28740 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -165,10 +165,10 @@
#define DWC3_DCFG1 0xc740 /* DWC_usb32 only */
#define DWC3_DEP_BASE(n) (0xc800 + ((n) * 0x10))
-#define DWC3_DEPCMDPAR2 0x00
-#define DWC3_DEPCMDPAR1 0x04
-#define DWC3_DEPCMDPAR0 0x08
-#define DWC3_DEPCMD 0x0c
+#define DWC3_DEPCMDPAR2(n) (DWC3_DEP_BASE(n) + 0x00)
+#define DWC3_DEPCMDPAR1(n) (DWC3_DEP_BASE(n) + 0x04)
+#define DWC3_DEPCMDPAR0(n) (DWC3_DEP_BASE(n) + 0x08)
+#define DWC3_DEPCMD(n) (DWC3_DEP_BASE(n) + 0x0c)
#define DWC3_DEV_IMOD(n) (0xca00 + ((n) * 0x4))
@@ -302,6 +302,7 @@
#define DWC3_GUSB2PHYCFG_SUSPHY BIT(6)
#define DWC3_GUSB2PHYCFG_ULPI_UTMI BIT(4)
#define DWC3_GUSB2PHYCFG_ENBLSLPM BIT(8)
+#define DWC3_GUSB2PHYCFG_XCVRDLY BIT(9)
#define DWC3_GUSB2PHYCFG_PHYIF(n) (n << 3)
#define DWC3_GUSB2PHYCFG_PHYIF_MASK DWC3_GUSB2PHYCFG_PHYIF(1)
#define DWC3_GUSB2PHYCFG_USBTRDTIM(n) (n << 10)
@@ -749,8 +750,6 @@ struct dwc3_ep {
struct list_head pending_list;
struct list_head started_list;
- void __iomem *regs;
-
struct dwc3_trb *trb_pool;
dma_addr_t trb_pool_dma;
struct dwc3 *dwc;
@@ -993,6 +992,17 @@ struct dwc3_scratchpad_array {
};
/**
+ * struct dwc3_glue_ops - The ops indicate the notifications that
+ * need to be passed on to glue layer
+ * @pre_set_role: Notify glue of role switch notifications
+ * @pre_run_stop: Notify run stop enable/disable information to glue
+ */
+struct dwc3_glue_ops {
+ void (*pre_set_role)(struct dwc3 *dwc, enum usb_role role);
+ void (*pre_run_stop)(struct dwc3 *dwc, bool is_on);
+};
+
+/**
* struct dwc3 - representation of our controller
* @drd_work: workqueue used for role swapping
* @ep0_trb: trb which is used for the ctrl_req
@@ -1012,6 +1022,7 @@ struct dwc3_scratchpad_array {
* @eps: endpoint array
* @gadget: device side representation of the peripheral controller
* @gadget_driver: pointer to the gadget driver
+ * @glue_ops: Vendor callbacks for flattened device implementations.
* @bus_clk: clock for accessing the registers
* @ref_clk: reference clock
* @susp_clk: clock used when the SS phy is in low power (S3) state
@@ -1048,6 +1059,8 @@ struct dwc3_scratchpad_array {
* @role_switch_default_mode: default operation mode of controller while
* usb role is USB_ROLE_NONE.
* @usb_psy: pointer to power supply interface.
+ * @vbus_draw_work: Work to set the vbus drawing limit
+ * @current_limit: How much current to draw from vbus, in milliAmperes.
* @usb2_phy: pointer to USB2 PHY
* @usb3_phy: pointer to USB3 PHY
* @usb2_generic_phy: pointer to array of USB2 PHYs
@@ -1083,6 +1096,7 @@ struct dwc3_scratchpad_array {
* @tx_max_burst_prd: max periodic ESS transmit burst size
* @tx_fifo_resize_max_num: max number of fifos allocated during txfifo resize
* @clear_stall_protocol: endpoint number that requires a delayed status phase
+ * @num_hc_interrupters: number of host controller interrupters
* @hsphy_interface: "utmi" or "ulpi"
* @connected: true when we're connected to a host, false otherwise
* @softconnect: true when gadget connect is called, false when disconnect runs
@@ -1106,6 +1120,8 @@ struct dwc3_scratchpad_array {
* @usb3_lpm_capable: set if hadrware supports Link Power Management
* @usb2_lpm_disable: set to disable usb2 lpm for host
* @usb2_gadget_lpm_disable: set to disable usb2 lpm for gadget
+ * @needs_full_reinit: set to indicate the core may lose power and need full
+ * initialization during system pm
* @disable_scramble_quirk: set if we enable the disable scramble quirk
* @u2exit_lfps_quirk: set if we enable u2exit lfps quirk
* @u2ss_inp3_quirk: set if we enable P3 OK for U2/SS Inactive quirk
@@ -1136,7 +1152,7 @@ struct dwc3_scratchpad_array {
* VBUS with an external supply.
* @parkmode_disable_ss_quirk: set if we need to disable all SuperSpeed
* instances in park mode.
- * @parkmode_disable_hs_quirk: set if we need to disable all HishSpeed
+ * @parkmode_disable_hs_quirk: set if we need to disable all HighSpeed
* instances in park mode.
* @gfladj_refclk_lpm_sel: set if we need to enable SOF/ITP counter
* running based on ref_clk
@@ -1148,6 +1164,8 @@ struct dwc3_scratchpad_array {
* 3 - Reserved
* @dis_metastability_quirk: set to disable metastability quirk.
* @dis_split_quirk: set to disable split boundary.
+ * @enable_usb2_transceiver_delay: Set to insert a delay before the
+ * assertion of the TxValid signal during a HS Chirp.
* @sys_wakeup: set if the device may do system wakeup.
* @wakeup_configured: set if the device is configured for remote wakeup.
* @suspended: set to track suspend event due to U3/L2.
@@ -1164,6 +1182,9 @@ struct dwc3_scratchpad_array {
* @gsbuscfg0_reqinfo: store GSBUSCFG0.DATRDREQINFO, DESRDREQINFO,
* DATWRREQINFO, and DESWRREQINFO value passed from
* glue driver.
+ * @wakeup_pending_funcs: Indicates whether any interface has requested for
+ * function wakeup in bitmap format where bit position
+ * represents interface_id.
*/
struct dwc3 {
struct work_struct drd_work;
@@ -1193,6 +1214,8 @@ struct dwc3 {
struct usb_gadget *gadget;
struct usb_gadget_driver *gadget_driver;
+ const struct dwc3_glue_ops *glue_ops;
+
struct clk *bus_clk;
struct clk *ref_clk;
struct clk *susp_clk;
@@ -1228,6 +1251,8 @@ struct dwc3 {
enum usb_dr_mode role_switch_default_mode;
struct power_supply *usb_psy;
+ struct work_struct vbus_draw_work;
+ unsigned int current_limit;
u32 fladj;
u32 ref_clk_per;
@@ -1247,6 +1272,7 @@ struct dwc3 {
#define DWC3_IP 0x5533
#define DWC31_IP 0x3331
#define DWC32_IP 0x3332
+#define DWC4_IP 0x3430
u32 revision;
@@ -1330,6 +1356,7 @@ struct dwc3 {
u8 tx_max_burst_prd;
u8 tx_fifo_resize_max_num;
u8 clear_stall_protocol;
+ u16 num_hc_interrupters;
const char *hsphy_interface;
@@ -1351,6 +1378,7 @@ struct dwc3 {
unsigned usb3_lpm_capable:1;
unsigned usb2_lpm_disable:1;
unsigned usb2_gadget_lpm_disable:1;
+ unsigned needs_full_reinit:1;
unsigned disable_scramble_quirk:1;
unsigned u2exit_lfps_quirk:1;
@@ -1381,6 +1409,7 @@ struct dwc3 {
unsigned dis_metastability_quirk:1;
unsigned dis_split_quirk:1;
+ unsigned enable_usb2_transceiver_delay:1;
unsigned async_callbacks:1;
unsigned sys_wakeup:1;
unsigned wakeup_configured:1;
@@ -1394,6 +1423,7 @@ struct dwc3 {
int num_ep_resized;
struct dentry *debug_root;
u32 gsbuscfg0_reqinfo;
+ u32 wakeup_pending_funcs;
};
#define INCRX_BURST_MODE 0
@@ -1608,6 +1638,18 @@ void dwc3_event_buffers_cleanup(struct dwc3 *dwc);
int dwc3_core_soft_reset(struct dwc3 *dwc);
void dwc3_enable_susphy(struct dwc3 *dwc, bool enable);
+static inline void dwc3_pre_set_role(struct dwc3 *dwc, enum usb_role role)
+{
+ if (dwc->glue_ops && dwc->glue_ops->pre_set_role)
+ dwc->glue_ops->pre_set_role(dwc, role);
+}
+
+static inline void dwc3_pre_run_stop(struct dwc3 *dwc, bool is_on)
+{
+ if (dwc->glue_ops && dwc->glue_ops->pre_run_stop)
+ dwc->glue_ops->pre_run_stop(dwc, is_on);
+}
+
#if IS_ENABLED(CONFIG_USB_DWC3_HOST) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
int dwc3_host_init(struct dwc3 *dwc);
void dwc3_host_exit(struct dwc3 *dwc);
diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h
index 09d703852a92..6e1cdcdce7cc 100644
--- a/drivers/usb/dwc3/debug.h
+++ b/drivers/usb/dwc3/debug.h
@@ -14,6 +14,24 @@
#include "core.h"
/**
+ * dwc3_mode_string - returns mode name
+ * @mode: GCTL.PrtCapDir value
+ */
+static inline const char *dwc3_mode_string(u32 mode)
+{
+ switch (mode) {
+ case DWC3_GCTL_PRTCAP_HOST:
+ return "host";
+ case DWC3_GCTL_PRTCAP_DEVICE:
+ return "device";
+ case DWC3_GCTL_PRTCAP_OTG:
+ return "otg";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+/**
* dwc3_gadget_ep_cmd_string - returns endpoint command string
* @cmd: command code
*/
diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c
index ebf03468fac4..65d909e4b785 100644
--- a/drivers/usb/dwc3/debugfs.c
+++ b/drivers/usb/dwc3/debugfs.c
@@ -36,23 +36,19 @@
#define dump_ep_register_set(n) \
{ \
.name = "DEPCMDPAR2("__stringify(n)")", \
- .offset = DWC3_DEP_BASE(n) + \
- DWC3_DEPCMDPAR2, \
+ .offset = DWC3_DEPCMDPAR2(n), \
}, \
{ \
.name = "DEPCMDPAR1("__stringify(n)")", \
- .offset = DWC3_DEP_BASE(n) + \
- DWC3_DEPCMDPAR1, \
+ .offset = DWC3_DEPCMDPAR1(n), \
}, \
{ \
.name = "DEPCMDPAR0("__stringify(n)")", \
- .offset = DWC3_DEP_BASE(n) + \
- DWC3_DEPCMDPAR0, \
+ .offset = DWC3_DEPCMDPAR0(n), \
}, \
{ \
.name = "DEPCMD("__stringify(n)")", \
- .offset = DWC3_DEP_BASE(n) + \
- DWC3_DEPCMD, \
+ .offset = DWC3_DEPCMD(n), \
}
@@ -300,14 +296,14 @@ static void dwc3_host_lsp(struct seq_file *s)
reg = DWC3_GDBGLSPMUX_HOSTSELECT(sel);
- dwc3_writel(dwc->regs, DWC3_GDBGLSPMUX, reg);
- val = dwc3_readl(dwc->regs, DWC3_GDBGLSP);
+ dwc3_writel(dwc, DWC3_GDBGLSPMUX, reg);
+ val = dwc3_readl(dwc, DWC3_GDBGLSP);
seq_printf(s, "GDBGLSP[%d] = 0x%08x\n", sel, val);
if (dbc_enabled && sel < 256) {
reg |= DWC3_GDBGLSPMUX_ENDBC;
- dwc3_writel(dwc->regs, DWC3_GDBGLSPMUX, reg);
- val = dwc3_readl(dwc->regs, DWC3_GDBGLSP);
+ dwc3_writel(dwc, DWC3_GDBGLSPMUX, reg);
+ val = dwc3_readl(dwc, DWC3_GDBGLSP);
seq_printf(s, "GDBGLSP_DBC[%d] = 0x%08x\n", sel, val);
}
}
@@ -320,8 +316,8 @@ static void dwc3_gadget_lsp(struct seq_file *s)
for (i = 0; i < 16; i++) {
reg = DWC3_GDBGLSPMUX_DEVSELECT(i);
- dwc3_writel(dwc->regs, DWC3_GDBGLSPMUX, reg);
- reg = dwc3_readl(dwc->regs, DWC3_GDBGLSP);
+ dwc3_writel(dwc, DWC3_GDBGLSPMUX, reg);
+ reg = dwc3_readl(dwc, DWC3_GDBGLSP);
seq_printf(s, "GDBGLSP[%d] = 0x%08x\n", i, reg);
}
}
@@ -339,7 +335,7 @@ static int dwc3_lsp_show(struct seq_file *s, void *unused)
return ret;
spin_lock_irqsave(&dwc->lock, flags);
- reg = dwc3_readl(dwc->regs, DWC3_GSTS);
+ reg = dwc3_readl(dwc, DWC3_GSTS);
current_mode = DWC3_GSTS_CURMOD(reg);
switch (current_mode) {
@@ -402,6 +398,7 @@ static int dwc3_mode_show(struct seq_file *s, void *unused)
struct dwc3 *dwc = s->private;
unsigned long flags;
u32 reg;
+ u32 mode;
int ret;
ret = pm_runtime_resume_and_get(dwc->dev);
@@ -409,21 +406,18 @@ static int dwc3_mode_show(struct seq_file *s, void *unused)
return ret;
spin_lock_irqsave(&dwc->lock, flags);
- reg = dwc3_readl(dwc->regs, DWC3_GCTL);
+ reg = dwc3_readl(dwc, DWC3_GCTL);
spin_unlock_irqrestore(&dwc->lock, flags);
- switch (DWC3_GCTL_PRTCAP(reg)) {
+ mode = DWC3_GCTL_PRTCAP(reg);
+ switch (mode) {
case DWC3_GCTL_PRTCAP_HOST:
- seq_puts(s, "host\n");
- break;
case DWC3_GCTL_PRTCAP_DEVICE:
- seq_puts(s, "device\n");
- break;
case DWC3_GCTL_PRTCAP_OTG:
- seq_puts(s, "otg\n");
+ seq_printf(s, "%s\n", dwc3_mode_string(mode));
break;
default:
- seq_printf(s, "UNKNOWN %08x\n", DWC3_GCTL_PRTCAP(reg));
+ seq_printf(s, "UNKNOWN %08x\n", mode);
}
pm_runtime_put_sync(dwc->dev);
@@ -484,7 +478,7 @@ static int dwc3_testmode_show(struct seq_file *s, void *unused)
return ret;
spin_lock_irqsave(&dwc->lock, flags);
- reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg = dwc3_readl(dwc, DWC3_DCTL);
reg &= DWC3_DCTL_TSTCTRL_MASK;
reg >>= 1;
spin_unlock_irqrestore(&dwc->lock, flags);
@@ -583,7 +577,7 @@ static int dwc3_link_state_show(struct seq_file *s, void *unused)
return ret;
spin_lock_irqsave(&dwc->lock, flags);
- reg = dwc3_readl(dwc->regs, DWC3_GSTS);
+ reg = dwc3_readl(dwc, DWC3_GSTS);
if (DWC3_GSTS_CURMOD(reg) != DWC3_GSTS_CURMOD_DEVICE) {
seq_puts(s, "Not available\n");
spin_unlock_irqrestore(&dwc->lock, flags);
@@ -591,7 +585,7 @@ static int dwc3_link_state_show(struct seq_file *s, void *unused)
return 0;
}
- reg = dwc3_readl(dwc->regs, DWC3_DSTS);
+ reg = dwc3_readl(dwc, DWC3_DSTS);
state = DWC3_DSTS_USBLNKST(reg);
speed = reg & DWC3_DSTS_CONNECTSPD;
@@ -645,14 +639,14 @@ static ssize_t dwc3_link_state_write(struct file *file,
return ret;
spin_lock_irqsave(&dwc->lock, flags);
- reg = dwc3_readl(dwc->regs, DWC3_GSTS);
+ reg = dwc3_readl(dwc, DWC3_GSTS);
if (DWC3_GSTS_CURMOD(reg) != DWC3_GSTS_CURMOD_DEVICE) {
spin_unlock_irqrestore(&dwc->lock, flags);
pm_runtime_put_sync(dwc->dev);
return -EINVAL;
}
- reg = dwc3_readl(dwc->regs, DWC3_DSTS);
+ reg = dwc3_readl(dwc, DWC3_DSTS);
speed = reg & DWC3_DSTS_CONNECTSPD;
if (speed < DWC3_DSTS_SUPERSPEED &&
@@ -948,10 +942,10 @@ static int dwc3_ep_info_register_show(struct seq_file *s, void *unused)
spin_lock_irqsave(&dwc->lock, flags);
reg = DWC3_GDBGLSPMUX_EPSELECT(dep->number);
- dwc3_writel(dwc->regs, DWC3_GDBGLSPMUX, reg);
+ dwc3_writel(dwc, DWC3_GDBGLSPMUX, reg);
- lower_32_bits = dwc3_readl(dwc->regs, DWC3_GDBGEPINFO0);
- upper_32_bits = dwc3_readl(dwc->regs, DWC3_GDBGEPINFO1);
+ lower_32_bits = dwc3_readl(dwc, DWC3_GDBGEPINFO0);
+ upper_32_bits = dwc3_readl(dwc, DWC3_GDBGEPINFO1);
ep_info = ((u64)upper_32_bits << 32) | lower_32_bits;
seq_printf(s, "0x%016llx\n", ep_info);
@@ -1009,7 +1003,7 @@ void dwc3_debugfs_init(struct dwc3 *dwc)
{
struct dentry *root;
- dwc->regset = kzalloc(sizeof(*dwc->regset), GFP_KERNEL);
+ dwc->regset = kzalloc_obj(*dwc->regset);
if (!dwc->regset)
return;
diff --git a/drivers/usb/dwc3/drd.c b/drivers/usb/dwc3/drd.c
index 7977860932b1..f3e37d383627 100644
--- a/drivers/usb/dwc3/drd.c
+++ b/drivers/usb/dwc3/drd.c
@@ -18,25 +18,25 @@
static void dwc3_otg_disable_events(struct dwc3 *dwc, u32 disable_mask)
{
- u32 reg = dwc3_readl(dwc->regs, DWC3_OEVTEN);
+ u32 reg = dwc3_readl(dwc, DWC3_OEVTEN);
reg &= ~(disable_mask);
- dwc3_writel(dwc->regs, DWC3_OEVTEN, reg);
+ dwc3_writel(dwc, DWC3_OEVTEN, reg);
}
static void dwc3_otg_enable_events(struct dwc3 *dwc, u32 enable_mask)
{
- u32 reg = dwc3_readl(dwc->regs, DWC3_OEVTEN);
+ u32 reg = dwc3_readl(dwc, DWC3_OEVTEN);
reg |= (enable_mask);
- dwc3_writel(dwc->regs, DWC3_OEVTEN, reg);
+ dwc3_writel(dwc, DWC3_OEVTEN, reg);
}
static void dwc3_otg_clear_events(struct dwc3 *dwc)
{
- u32 reg = dwc3_readl(dwc->regs, DWC3_OEVT);
+ u32 reg = dwc3_readl(dwc, DWC3_OEVT);
- dwc3_writel(dwc->regs, DWC3_OEVTEN, reg);
+ dwc3_writel(dwc, DWC3_OEVTEN, reg);
}
#define DWC3_OTG_ALL_EVENTS (DWC3_OEVTEN_XHCIRUNSTPSETEN | \
@@ -72,18 +72,18 @@ static irqreturn_t dwc3_otg_irq(int irq, void *_dwc)
struct dwc3 *dwc = _dwc;
irqreturn_t ret = IRQ_NONE;
- reg = dwc3_readl(dwc->regs, DWC3_OEVT);
+ reg = dwc3_readl(dwc, DWC3_OEVT);
if (reg) {
/* ignore non OTG events, we can't disable them in OEVTEN */
if (!(reg & DWC3_OTG_ALL_EVENTS)) {
- dwc3_writel(dwc->regs, DWC3_OEVT, reg);
+ dwc3_writel(dwc, DWC3_OEVT, reg);
return IRQ_NONE;
}
if (dwc->current_otg_role == DWC3_OTG_ROLE_HOST &&
!(reg & DWC3_OEVT_DEVICEMODE))
dwc->otg_restart_host = true;
- dwc3_writel(dwc->regs, DWC3_OEVT, reg);
+ dwc3_writel(dwc, DWC3_OEVT, reg);
ret = IRQ_WAKE_THREAD;
}
@@ -100,23 +100,23 @@ static void dwc3_otgregs_init(struct dwc3 *dwc)
* the signal outputs sent to the PHY, the OTG FSM logic of the
* core and also the resets to the VBUS filters inside the core.
*/
- reg = dwc3_readl(dwc->regs, DWC3_OCFG);
+ reg = dwc3_readl(dwc, DWC3_OCFG);
reg |= DWC3_OCFG_SFTRSTMASK;
- dwc3_writel(dwc->regs, DWC3_OCFG, reg);
+ dwc3_writel(dwc, DWC3_OCFG, reg);
/* Disable hibernation for simplicity */
- reg = dwc3_readl(dwc->regs, DWC3_GCTL);
+ reg = dwc3_readl(dwc, DWC3_GCTL);
reg &= ~DWC3_GCTL_GBLHIBERNATIONEN;
- dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+ dwc3_writel(dwc, DWC3_GCTL, reg);
/*
* Initialize OTG registers as per
* Figure 11-4 OTG Driver Overall Programming Flow
*/
/* OCFG.SRPCap = 0, OCFG.HNPCap = 0 */
- reg = dwc3_readl(dwc->regs, DWC3_OCFG);
+ reg = dwc3_readl(dwc, DWC3_OCFG);
reg &= ~(DWC3_OCFG_SRPCAP | DWC3_OCFG_HNPCAP);
- dwc3_writel(dwc->regs, DWC3_OCFG, reg);
+ dwc3_writel(dwc, DWC3_OCFG, reg);
/* OEVT = FFFF */
dwc3_otg_clear_events(dwc);
/* OEVTEN = 0 */
@@ -127,11 +127,11 @@ static void dwc3_otgregs_init(struct dwc3 *dwc)
* OCTL.PeriMode = 1, OCTL.DevSetHNPEn = 0, OCTL.HstSetHNPEn = 0,
* OCTL.HNPReq = 0
*/
- reg = dwc3_readl(dwc->regs, DWC3_OCTL);
+ reg = dwc3_readl(dwc, DWC3_OCTL);
reg |= DWC3_OCTL_PERIMODE;
reg &= ~(DWC3_OCTL_DEVSETHNPEN | DWC3_OCTL_HSTSETHNPEN |
DWC3_OCTL_HNPREQ);
- dwc3_writel(dwc->regs, DWC3_OCTL, reg);
+ dwc3_writel(dwc, DWC3_OCTL, reg);
}
static int dwc3_otg_get_irq(struct dwc3 *dwc)
@@ -175,9 +175,9 @@ void dwc3_otg_init(struct dwc3 *dwc)
/* GCTL.PrtCapDir=2'b11 */
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_OTG, true);
/* GUSB2PHYCFG0.SusPHY=0 */
- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
+ reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(0));
reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+ dwc3_writel(dwc, DWC3_GUSB2PHYCFG(0), reg);
/* Initialize OTG registers */
dwc3_otgregs_init(dwc);
@@ -203,17 +203,17 @@ void dwc3_otg_host_init(struct dwc3 *dwc)
* OCTL.PeriMode=0, OCTL.TermSelDLPulse = 0,
* OCTL.DevSetHNPEn = 0, OCTL.HstSetHNPEn = 0
*/
- reg = dwc3_readl(dwc->regs, DWC3_OCTL);
+ reg = dwc3_readl(dwc, DWC3_OCTL);
reg &= ~(DWC3_OCTL_PERIMODE | DWC3_OCTL_TERMSELIDPULSE |
DWC3_OCTL_DEVSETHNPEN | DWC3_OCTL_HSTSETHNPEN);
- dwc3_writel(dwc->regs, DWC3_OCTL, reg);
+ dwc3_writel(dwc, DWC3_OCTL, reg);
/*
* OCFG.DisPrtPwrCutoff = 0/1
*/
- reg = dwc3_readl(dwc->regs, DWC3_OCFG);
+ reg = dwc3_readl(dwc, DWC3_OCFG);
reg &= ~DWC3_OCFG_DISPWRCUTTOFF;
- dwc3_writel(dwc->regs, DWC3_OCFG, reg);
+ dwc3_writel(dwc, DWC3_OCFG, reg);
/*
* OCFG.SRPCap = 1, OCFG.HNPCap = GHWPARAMS6.HNP_CAP
@@ -229,15 +229,15 @@ void dwc3_otg_host_init(struct dwc3 *dwc)
/* GUSB2PHYCFG.ULPIAutoRes = 1/0, GUSB2PHYCFG.SusPHY = 1 */
if (!dwc->dis_u2_susphy_quirk) {
- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
+ reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(0));
reg |= DWC3_GUSB2PHYCFG_SUSPHY;
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+ dwc3_writel(dwc, DWC3_GUSB2PHYCFG(0), reg);
}
/* Set Port Power to enable VBUS: OCTL.PrtPwrCtl = 1 */
- reg = dwc3_readl(dwc->regs, DWC3_OCTL);
+ reg = dwc3_readl(dwc, DWC3_OCTL);
reg |= DWC3_OCTL_PRTPWRCTL;
- dwc3_writel(dwc->regs, DWC3_OCTL, reg);
+ dwc3_writel(dwc, DWC3_OCTL, reg);
}
/* should be called after Host controller driver is stopped */
@@ -258,9 +258,9 @@ static void dwc3_otg_host_exit(struct dwc3 *dwc)
*/
/* OCTL.HstSetHNPEn = 0, OCTL.PrtPwrCtl=0 */
- reg = dwc3_readl(dwc->regs, DWC3_OCTL);
+ reg = dwc3_readl(dwc, DWC3_OCTL);
reg &= ~(DWC3_OCTL_HSTSETHNPEN | DWC3_OCTL_PRTPWRCTL);
- dwc3_writel(dwc->regs, DWC3_OCTL, reg);
+ dwc3_writel(dwc, DWC3_OCTL, reg);
}
/* should be called before the gadget controller driver is started */
@@ -274,27 +274,27 @@ static void dwc3_otg_device_init(struct dwc3 *dwc)
* OCFG.HNPCap = GHWPARAMS6.HNP_CAP, OCFG.SRPCap = 1
* but we keep them 0 for simple dual-role operation.
*/
- reg = dwc3_readl(dwc->regs, DWC3_OCFG);
+ reg = dwc3_readl(dwc, DWC3_OCFG);
/* OCFG.OTGSftRstMsk = 0/1 */
reg |= DWC3_OCFG_SFTRSTMASK;
- dwc3_writel(dwc->regs, DWC3_OCFG, reg);
+ dwc3_writel(dwc, DWC3_OCFG, reg);
/*
* OCTL.PeriMode = 1
* OCTL.TermSelDLPulse = 0/1, OCTL.HNPReq = 0
* OCTL.DevSetHNPEn = 0, OCTL.HstSetHNPEn = 0
*/
- reg = dwc3_readl(dwc->regs, DWC3_OCTL);
+ reg = dwc3_readl(dwc, DWC3_OCTL);
reg |= DWC3_OCTL_PERIMODE;
reg &= ~(DWC3_OCTL_TERMSELIDPULSE | DWC3_OCTL_HNPREQ |
DWC3_OCTL_DEVSETHNPEN | DWC3_OCTL_HSTSETHNPEN);
- dwc3_writel(dwc->regs, DWC3_OCTL, reg);
+ dwc3_writel(dwc, DWC3_OCTL, reg);
/* OEVTEN.OTGBDevSesVldDetEvntEn = 1 */
dwc3_otg_enable_events(dwc, DWC3_OEVTEN_BDEVSESSVLDDETEN);
/* GUSB2PHYCFG.ULPIAutoRes = 0, GUSB2PHYCFG0.SusPHY = 1 */
if (!dwc->dis_u2_susphy_quirk) {
- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
+ reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(0));
reg |= DWC3_GUSB2PHYCFG_SUSPHY;
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+ dwc3_writel(dwc, DWC3_GUSB2PHYCFG(0), reg);
}
/* GCTL.GblHibernationEn = 0. Already 0. */
}
@@ -319,10 +319,10 @@ static void dwc3_otg_device_exit(struct dwc3 *dwc)
DWC3_OEVTEN_BDEVBHOSTENDEN);
/* OCTL.DevSetHNPEn = 0, OCTL.HNPReq = 0, OCTL.PeriMode=1 */
- reg = dwc3_readl(dwc->regs, DWC3_OCTL);
+ reg = dwc3_readl(dwc, DWC3_OCTL);
reg &= ~(DWC3_OCTL_DEVSETHNPEN | DWC3_OCTL_HNPREQ);
reg |= DWC3_OCTL_PERIMODE;
- dwc3_writel(dwc->regs, DWC3_OCTL, reg);
+ dwc3_writel(dwc, DWC3_OCTL, reg);
}
void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus)
@@ -341,7 +341,7 @@ void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus)
return;
if (!ignore_idstatus) {
- reg = dwc3_readl(dwc->regs, DWC3_OSTS);
+ reg = dwc3_readl(dwc, DWC3_OSTS);
id = !!(reg & DWC3_OSTS_CONIDSTS);
dwc->desired_otg_role = id ? DWC3_OTG_ROLE_DEVICE :
@@ -381,6 +381,7 @@ void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus)
dwc3_otgregs_init(dwc);
dwc3_otg_host_init(dwc);
spin_unlock_irqrestore(&dwc->lock, flags);
+ dwc3_pre_set_role(dwc, USB_ROLE_HOST);
ret = dwc3_host_init(dwc);
if (ret) {
dev_err(dwc->dev, "failed to initialize host\n");
@@ -406,6 +407,7 @@ void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus)
otg_set_vbus(dwc->usb2_phy->otg, false);
if (dwc->usb2_generic_phy[0])
phy_set_mode(dwc->usb2_generic_phy[0], PHY_MODE_USB_DEVICE);
+ dwc3_pre_set_role(dwc, USB_ROLE_DEVICE);
ret = dwc3_gadget_init(dwc);
if (ret)
dev_err(dwc->dev, "failed to initialize peripheral\n");
@@ -433,10 +435,12 @@ static int dwc3_drd_notifier(struct notifier_block *nb,
unsigned long event, void *ptr)
{
struct dwc3 *dwc = container_of(nb, struct dwc3, edev_nb);
+ u32 mode = event ? DWC3_GCTL_PRTCAP_HOST : DWC3_GCTL_PRTCAP_DEVICE;
+ enum usb_role role = mode == DWC3_GCTL_PRTCAP_HOST ?
+ USB_ROLE_HOST : USB_ROLE_DEVICE;
- dwc3_set_mode(dwc, event ?
- DWC3_GCTL_PRTCAP_HOST :
- DWC3_GCTL_PRTCAP_DEVICE);
+ dwc3_pre_set_role(dwc, role);
+ dwc3_set_mode(dwc, mode);
return NOTIFY_DONE;
}
@@ -464,6 +468,7 @@ static int dwc3_usb_role_switch_set(struct usb_role_switch *sw,
break;
}
+ dwc3_pre_set_role(dwc, role);
dwc3_set_mode(dwc, mode);
return 0;
}
@@ -514,6 +519,7 @@ static int dwc3_setup_role_switch(struct dwc3 *dwc)
dwc3_role_switch.set = dwc3_usb_role_switch_set;
dwc3_role_switch.get = dwc3_usb_role_switch_get;
dwc3_role_switch.driver_data = dwc;
+ dwc3_role_switch.allow_userspace_control = true;
dwc->role_sw = usb_role_switch_register(dwc->dev, &dwc3_role_switch);
if (IS_ERR(dwc->role_sw))
return PTR_ERR(dwc->role_sw);
diff --git a/drivers/usb/dwc3/dwc3-am62.c b/drivers/usb/dwc3/dwc3-am62.c
index c158364bc03e..e11d7643f966 100644
--- a/drivers/usb/dwc3/dwc3-am62.c
+++ b/drivers/usb/dwc3/dwc3-am62.c
@@ -153,11 +153,11 @@ static int phy_syscon_pll_refclk(struct dwc3_am62 *am62)
{
struct device *dev = am62->dev;
struct device_node *node = dev->of_node;
- struct of_phandle_args args;
struct regmap *syscon;
int ret;
- syscon = syscon_regmap_lookup_by_phandle(node, "ti,syscon-phy-pll-refclk");
+ syscon = syscon_regmap_lookup_by_phandle_args(node, "ti,syscon-phy-pll-refclk",
+ 1, &am62->offset);
if (IS_ERR(syscon)) {
dev_err(dev, "unable to get ti,syscon-phy-pll-refclk regmap\n");
return PTR_ERR(syscon);
@@ -165,14 +165,6 @@ static int phy_syscon_pll_refclk(struct dwc3_am62 *am62)
am62->syscon = syscon;
- ret = of_parse_phandle_with_fixed_args(node, "ti,syscon-phy-pll-refclk", 1,
- 0, &args);
- if (ret)
- return ret;
-
- of_node_put(args.np);
- am62->offset = args.args[0];
-
/* Core voltage. PHY_CORE_VOLTAGE bit Recommended to be 0 always */
ret = regmap_update_bits(am62->syscon, am62->offset, PHY_CORE_VOLTAGE_MASK, 0);
if (ret) {
@@ -300,7 +292,6 @@ static int dwc3_ti_probe(struct platform_device *pdev)
/* Setting up autosuspend */
pm_runtime_set_autosuspend_delay(dev, DWC3_AM62_AUTOSUSPEND_DELAY);
pm_runtime_use_autosuspend(dev);
- pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
return 0;
diff --git a/drivers/usb/dwc3/dwc3-apple.c b/drivers/usb/dwc3/dwc3-apple.c
new file mode 100644
index 000000000000..40c3ccfddb67
--- /dev/null
+++ b/drivers/usb/dwc3/dwc3-apple.c
@@ -0,0 +1,523 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Apple Silicon DWC3 Glue driver
+ * Copyright (C) The Asahi Linux Contributors
+ *
+ * Based on:
+ * - dwc3-qcom.c Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ * - dwc3-of-simple.c Copyright (c) 2015 Texas Instruments Incorporated - https://www.ti.com
+ */
+
+#include <linux/of.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+
+#include "glue.h"
+
+/*
+ * This platform requires a very specific sequence of operations to bring up dwc3 and its USB3 PHY:
+ *
+ * 1) The PHY itself has to be brought up; for this we need to know the mode (USB3,
+ * USB3+DisplayPort, USB4, etc) and the lane orientation. This happens through typec_mux_set.
+ * 2) DWC3 has to be brought up but we must not touch the gadget area or start xhci yet.
+ * 3) The PHY bring-up has to be finalized and dwc3's PIPE interface has to be switched to the
+ * USB3 PHY, this is done inside phy_set_mode.
+ * 4) We can now initialize xhci or gadget mode.
+ *
+ * We can switch 1 and 2 but 3 has to happen after (1 and 2) and 4 has to happen after 3.
+ *
+ * And then to bring this all down again:
+ *
+ * 1) DWC3 has to exit host or gadget mode and must no longer touch those registers
+ * 2) The PHY has to switch dwc3's PIPE interface back to the dummy backend
+ * 3) The PHY itself can be shut down, this happens from typec_mux_set
+ *
+ * We also can't transition the PHY from one mode to another while dwc3 is up and running (this is
+ * slightly wrong, some transitions are possible, others aren't but because we have no documentation
+ * for this I'd rather play it safe).
+ *
+ * After both the PHY and dwc3 are initialized we will only ever see a single "new device connected"
+ * event. If we just keep them running only the first device plugged in will ever work. XHCI's port
+ * status register actually does show the correct state but no interrupt ever comes in. In gadget
+ * mode we don't even get a USBDisconnected event and everything looks like there's still something
+ * connected on the other end.
+ * This can be partially explained because the USB2 D+/D- lines are connected through a stateful
+ * eUSB2 repeater which in turn is controlled by a variant of the TI TPS6598x USB PD chip which
+ * resets the repeater out-of-band everytime the CC lines are (dis)connected. This then requires a
+ * PHY reset to make sure the PHY and the eUSB2 repeater state are synchronized again.
+ *
+ * And to make this all extra fun: If we get the order of some of this wrong either the port is just
+ * broken until a phy+dwc3 reset, or it's broken until a full SoC reset (likely because we can't
+ * reset some parts of the PHY), or some watchdog kicks in after a few seconds and forces a full SoC
+ * reset (mostly seen this with USB4/Thunderbolt but there's clearly some watchdog that hates
+ * invalid states).
+ *
+ * Hence there's really no good way to keep dwc3 fully up and running after we disconnect a cable
+ * because then we can't shut down the PHY anymore. And if we kept the PHY running in whatever mode
+ * it was until the next cable is connected we'd need to tear it all down and bring it back up again
+ * anyway to detect and use the next device.
+ *
+ * Instead, we just shut down everything when a cable is disconnected and transition to
+ * DWC3_APPLE_NO_CABLE.
+ * During initial probe we don't have any information about the connected cable and can't bring up
+ * the PHY properly and thus also can't fully bring up dwc3. Instead, we just keep everything off
+ * and defer the first dwc3 probe until we get the first cable connected event. Until then we stay
+ * in DWC3_APPLE_PROBE_PENDING.
+ * Once a cable is connected we then keep track of the controller mode here by transitioning to
+ * DWC3_APPLE_HOST or DWC3_APPLE_DEVICE.
+ */
+enum dwc3_apple_state {
+ DWC3_APPLE_PROBE_PENDING, /* Before first cable connection, dwc3_core_probe not called */
+ DWC3_APPLE_NO_CABLE, /* No cable connected, dwc3 suspended after dwc3_core_exit */
+ DWC3_APPLE_HOST, /* Cable connected, dwc3 in host mode */
+ DWC3_APPLE_DEVICE, /* Cable connected, dwc3 in device mode */
+};
+
+/**
+ * struct dwc3_apple - Apple-specific DWC3 USB controller
+ * @dwc: Core DWC3 structure
+ * @dev: Pointer to the device structure
+ * @mmio_resource: Resource to be passed to dwc3_core_probe
+ * @apple_regs: Apple-specific DWC3 registers
+ * @reset: Reset control
+ * @role_sw: USB role switch
+ * @lock: Mutex for synchronizing access
+ * @state: Current state of the controller, see documentation for the enum for details
+ */
+struct dwc3_apple {
+ struct dwc3 dwc;
+
+ struct device *dev;
+ struct resource *mmio_resource;
+ void __iomem *apple_regs;
+
+ struct reset_control *reset;
+ struct usb_role_switch *role_sw;
+
+ struct mutex lock;
+
+ enum dwc3_apple_state state;
+};
+
+#define to_dwc3_apple(d) container_of((d), struct dwc3_apple, dwc)
+
+/*
+ * Apple Silicon dwc3 vendor-specific registers
+ *
+ * These registers were identified by tracing XNU's memory access patterns and correlating them with
+ * debug output over serial to determine their names. We don't exactly know what these do but
+ * without these USB3 devices sometimes don't work.
+ */
+#define APPLE_DWC3_REGS_START 0xcd00
+#define APPLE_DWC3_REGS_END 0xcdff
+
+#define APPLE_DWC3_CIO_LFPS_OFFSET 0xcd38
+#define APPLE_DWC3_CIO_LFPS_OFFSET_VALUE 0xf800f80
+
+#define APPLE_DWC3_CIO_BW_NGT_OFFSET 0xcd3c
+#define APPLE_DWC3_CIO_BW_NGT_OFFSET_VALUE 0xfc00fc0
+
+#define APPLE_DWC3_CIO_LINK_TIMER 0xcd40
+#define APPLE_DWC3_CIO_PENDING_HP_TIMER GENMASK(23, 16)
+#define APPLE_DWC3_CIO_PENDING_HP_TIMER_VALUE 0x14
+#define APPLE_DWC3_CIO_PM_LC_TIMER GENMASK(15, 8)
+#define APPLE_DWC3_CIO_PM_LC_TIMER_VALUE 0xa
+#define APPLE_DWC3_CIO_PM_ENTRY_TIMER GENMASK(7, 0)
+#define APPLE_DWC3_CIO_PM_ENTRY_TIMER_VALUE 0x10
+
+static inline void dwc3_apple_writel(struct dwc3_apple *appledwc, u32 offset, u32 value)
+{
+ writel(value, appledwc->apple_regs + offset - APPLE_DWC3_REGS_START);
+}
+
+static inline u32 dwc3_apple_readl(struct dwc3_apple *appledwc, u32 offset)
+{
+ return readl(appledwc->apple_regs + offset - APPLE_DWC3_REGS_START);
+}
+
+static inline void dwc3_apple_mask(struct dwc3_apple *appledwc, u32 offset, u32 mask, u32 value)
+{
+ u32 reg;
+
+ reg = dwc3_apple_readl(appledwc, offset);
+ reg &= ~mask;
+ reg |= value;
+ dwc3_apple_writel(appledwc, offset, reg);
+}
+
+static void dwc3_apple_setup_cio(struct dwc3_apple *appledwc)
+{
+ dwc3_apple_writel(appledwc, APPLE_DWC3_CIO_LFPS_OFFSET, APPLE_DWC3_CIO_LFPS_OFFSET_VALUE);
+ dwc3_apple_writel(appledwc, APPLE_DWC3_CIO_BW_NGT_OFFSET,
+ APPLE_DWC3_CIO_BW_NGT_OFFSET_VALUE);
+ dwc3_apple_mask(appledwc, APPLE_DWC3_CIO_LINK_TIMER, APPLE_DWC3_CIO_PENDING_HP_TIMER,
+ FIELD_PREP(APPLE_DWC3_CIO_PENDING_HP_TIMER,
+ APPLE_DWC3_CIO_PENDING_HP_TIMER_VALUE));
+ dwc3_apple_mask(appledwc, APPLE_DWC3_CIO_LINK_TIMER, APPLE_DWC3_CIO_PM_LC_TIMER,
+ FIELD_PREP(APPLE_DWC3_CIO_PM_LC_TIMER, APPLE_DWC3_CIO_PM_LC_TIMER_VALUE));
+ dwc3_apple_mask(appledwc, APPLE_DWC3_CIO_LINK_TIMER, APPLE_DWC3_CIO_PM_ENTRY_TIMER,
+ FIELD_PREP(APPLE_DWC3_CIO_PM_ENTRY_TIMER,
+ APPLE_DWC3_CIO_PM_ENTRY_TIMER_VALUE));
+}
+
+static void dwc3_apple_set_ptrcap(struct dwc3_apple *appledwc, u32 mode)
+{
+ guard(spinlock_irqsave)(&appledwc->dwc.lock);
+ dwc3_set_prtcap(&appledwc->dwc, mode, false);
+}
+
+static int dwc3_apple_core_probe(struct dwc3_apple *appledwc)
+{
+ struct dwc3_probe_data probe_data = {};
+ int ret;
+
+ lockdep_assert_held(&appledwc->lock);
+ WARN_ON_ONCE(appledwc->state != DWC3_APPLE_PROBE_PENDING);
+
+ appledwc->dwc.dev = appledwc->dev;
+ probe_data.dwc = &appledwc->dwc;
+ probe_data.res = appledwc->mmio_resource;
+ probe_data.ignore_clocks_and_resets = true;
+ probe_data.skip_core_init_mode = true;
+ probe_data.properties = DWC3_DEFAULT_PROPERTIES;
+
+ ret = dwc3_core_probe(&probe_data);
+ if (ret)
+ return ret;
+
+ appledwc->state = DWC3_APPLE_NO_CABLE;
+ return 0;
+}
+
+static int dwc3_apple_core_init(struct dwc3_apple *appledwc)
+{
+ int ret;
+
+ lockdep_assert_held(&appledwc->lock);
+
+ switch (appledwc->state) {
+ case DWC3_APPLE_PROBE_PENDING:
+ ret = dwc3_apple_core_probe(appledwc);
+ if (ret)
+ dev_err(appledwc->dev, "Failed to probe DWC3 Core, err=%d\n", ret);
+ break;
+ case DWC3_APPLE_NO_CABLE:
+ ret = dwc3_core_init(&appledwc->dwc);
+ if (ret)
+ dev_err(appledwc->dev, "Failed to initialize DWC3 Core, err=%d\n", ret);
+ break;
+ default:
+ /* Unreachable unless there's a bug in this driver */
+ WARN_ON_ONCE(1);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int dwc3_apple_init(struct dwc3_apple *appledwc, enum dwc3_apple_state state)
+{
+ int ret, ret_reset;
+
+ lockdep_assert_held(&appledwc->lock);
+
+ /*
+ * The USB2 PHY on this platform must be configured for host or device mode while it is
+ * still powered off and before dwc3 tries to access it. Otherwise, the new configuration
+ * will sometimes only take affect after the *next* time dwc3 is brought up which causes
+ * the connected device to just not work.
+ * The USB3 PHY must be configured later after dwc3 has already been initialized.
+ */
+ switch (state) {
+ case DWC3_APPLE_HOST:
+ phy_set_mode(appledwc->dwc.usb2_generic_phy[0], PHY_MODE_USB_HOST);
+ break;
+ case DWC3_APPLE_DEVICE:
+ phy_set_mode(appledwc->dwc.usb2_generic_phy[0], PHY_MODE_USB_DEVICE);
+ break;
+ default:
+ /* Unreachable unless there's a bug in this driver */
+ return -EINVAL;
+ }
+
+ ret = reset_control_deassert(appledwc->reset);
+ if (ret) {
+ dev_err(appledwc->dev, "Failed to deassert reset, err=%d\n", ret);
+ return ret;
+ }
+
+ ret = dwc3_apple_core_init(appledwc);
+ if (ret)
+ goto reset_assert;
+
+ /*
+ * Now that the core is initialized and already went through dwc3_core_soft_reset we can
+ * configure some unknown Apple-specific settings and then bring up xhci or gadget mode.
+ */
+ dwc3_apple_setup_cio(appledwc);
+
+ switch (state) {
+ case DWC3_APPLE_HOST:
+ appledwc->dwc.dr_mode = USB_DR_MODE_HOST;
+ dwc3_apple_set_ptrcap(appledwc, DWC3_GCTL_PRTCAP_HOST);
+ /*
+ * This platform requires SUSPHY to be enabled here already in order to properly
+ * configure the PHY and switch dwc3's PIPE interface to USB3 PHY. The USB2 PHY
+ * has already been configured to the correct mode earlier.
+ */
+ dwc3_enable_susphy(&appledwc->dwc, true);
+ phy_set_mode(appledwc->dwc.usb3_generic_phy[0], PHY_MODE_USB_HOST);
+ ret = dwc3_host_init(&appledwc->dwc);
+ if (ret) {
+ dev_err(appledwc->dev, "Failed to initialize host, ret=%d\n", ret);
+ goto core_exit;
+ }
+
+ break;
+ case DWC3_APPLE_DEVICE:
+ appledwc->dwc.dr_mode = USB_DR_MODE_PERIPHERAL;
+ dwc3_apple_set_ptrcap(appledwc, DWC3_GCTL_PRTCAP_DEVICE);
+ /*
+ * This platform requires SUSPHY to be enabled here already in order to properly
+ * configure the PHY and switch dwc3's PIPE interface to USB3 PHY. The USB2 PHY
+ * has already been configured to the correct mode earlier.
+ */
+ dwc3_enable_susphy(&appledwc->dwc, true);
+ phy_set_mode(appledwc->dwc.usb3_generic_phy[0], PHY_MODE_USB_DEVICE);
+ ret = dwc3_gadget_init(&appledwc->dwc);
+ if (ret) {
+ dev_err(appledwc->dev, "Failed to initialize gadget, ret=%d\n", ret);
+ goto core_exit;
+ }
+ break;
+ default:
+ /* Unreachable unless there's a bug in this driver */
+ WARN_ON_ONCE(1);
+ ret = -EINVAL;
+ goto core_exit;
+ }
+
+ appledwc->state = state;
+ return 0;
+
+core_exit:
+ dwc3_core_exit(&appledwc->dwc);
+reset_assert:
+ ret_reset = reset_control_assert(appledwc->reset);
+ if (ret_reset)
+ dev_warn(appledwc->dev, "Failed to assert reset, err=%d\n", ret_reset);
+
+ return ret;
+}
+
+static int dwc3_apple_exit(struct dwc3_apple *appledwc)
+{
+ int ret = 0;
+
+ lockdep_assert_held(&appledwc->lock);
+
+ switch (appledwc->state) {
+ case DWC3_APPLE_PROBE_PENDING:
+ case DWC3_APPLE_NO_CABLE:
+ /* Nothing to do if we're already off */
+ return 0;
+ case DWC3_APPLE_DEVICE:
+ dwc3_gadget_exit(&appledwc->dwc);
+ break;
+ case DWC3_APPLE_HOST:
+ dwc3_host_exit(&appledwc->dwc);
+ break;
+ }
+
+ /*
+ * This platform requires SUSPHY to be enabled in order to properly power down the PHY
+ * and switch dwc3's PIPE interface back to a dummy PHY (i.e. no USB3 support and USB2 via
+ * a different PHY connected through ULPI).
+ */
+ dwc3_enable_susphy(&appledwc->dwc, true);
+ dwc3_core_exit(&appledwc->dwc);
+ appledwc->state = DWC3_APPLE_NO_CABLE;
+
+ ret = reset_control_assert(appledwc->reset);
+ if (ret) {
+ dev_err(appledwc->dev, "Failed to assert reset, err=%d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int dwc3_usb_role_switch_set(struct usb_role_switch *sw, enum usb_role role)
+{
+ struct dwc3_apple *appledwc = usb_role_switch_get_drvdata(sw);
+ int ret;
+
+ guard(mutex)(&appledwc->lock);
+
+ /*
+ * Skip role switches if appledwc is already in the desired state. The
+ * USB-C port controller on M2 and M1/M2 Pro/Max/Ultra devices issues
+ * additional interrupts which results in usb_role_switch_set_role()
+ * calls with the current role.
+ * Ignore those calls here to ensure the USB-C port controller and
+ * appledwc are in a consistent state.
+ * This matches the behaviour in __dwc3_set_mode().
+ * Do no handle USB_ROLE_NONE for DWC3_APPLE_NO_CABLE and
+ * DWC3_APPLE_PROBE_PENDING since that is no-op anyway.
+ */
+ if (appledwc->state == DWC3_APPLE_HOST && role == USB_ROLE_HOST)
+ return 0;
+ if (appledwc->state == DWC3_APPLE_DEVICE && role == USB_ROLE_DEVICE)
+ return 0;
+
+ /*
+ * We need to tear all of dwc3 down and re-initialize it every time a cable is
+ * connected or disconnected or when the mode changes. See the documentation for enum
+ * dwc3_apple_state for details.
+ */
+ ret = dwc3_apple_exit(appledwc);
+ if (ret)
+ return ret;
+
+ switch (role) {
+ case USB_ROLE_NONE:
+ /* Nothing to do if no cable is connected */
+ return 0;
+ case USB_ROLE_HOST:
+ return dwc3_apple_init(appledwc, DWC3_APPLE_HOST);
+ case USB_ROLE_DEVICE:
+ return dwc3_apple_init(appledwc, DWC3_APPLE_DEVICE);
+ default:
+ dev_err(appledwc->dev, "Invalid target role: %d\n", role);
+ return -EINVAL;
+ }
+}
+
+static enum usb_role dwc3_usb_role_switch_get(struct usb_role_switch *sw)
+{
+ struct dwc3_apple *appledwc = usb_role_switch_get_drvdata(sw);
+
+ guard(mutex)(&appledwc->lock);
+
+ switch (appledwc->state) {
+ case DWC3_APPLE_HOST:
+ return USB_ROLE_HOST;
+ case DWC3_APPLE_DEVICE:
+ return USB_ROLE_DEVICE;
+ case DWC3_APPLE_NO_CABLE:
+ case DWC3_APPLE_PROBE_PENDING:
+ return USB_ROLE_NONE;
+ default:
+ /* Unreachable unless there's a bug in this driver */
+ dev_err(appledwc->dev, "Invalid internal state: %d\n", appledwc->state);
+ return USB_ROLE_NONE;
+ }
+}
+
+static int dwc3_apple_setup_role_switch(struct dwc3_apple *appledwc)
+{
+ struct usb_role_switch_desc dwc3_role_switch = { NULL };
+
+ dwc3_role_switch.fwnode = dev_fwnode(appledwc->dev);
+ dwc3_role_switch.set = dwc3_usb_role_switch_set;
+ dwc3_role_switch.get = dwc3_usb_role_switch_get;
+ dwc3_role_switch.driver_data = appledwc;
+ appledwc->role_sw = usb_role_switch_register(appledwc->dev, &dwc3_role_switch);
+ if (IS_ERR(appledwc->role_sw))
+ return PTR_ERR(appledwc->role_sw);
+
+ return 0;
+}
+
+static int dwc3_apple_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct dwc3_apple *appledwc;
+ int ret;
+
+ appledwc = devm_kzalloc(&pdev->dev, sizeof(*appledwc), GFP_KERNEL);
+ if (!appledwc)
+ return -ENOMEM;
+
+ appledwc->dev = &pdev->dev;
+ mutex_init(&appledwc->lock);
+
+ appledwc->reset = devm_reset_control_get_exclusive(dev, NULL);
+ if (IS_ERR(appledwc->reset))
+ return dev_err_probe(&pdev->dev, PTR_ERR(appledwc->reset),
+ "Failed to get reset control\n");
+
+ ret = reset_control_assert(appledwc->reset);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to assert reset, err=%d\n", ret);
+ return ret;
+ }
+
+ appledwc->mmio_resource = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dwc3-core");
+ if (!appledwc->mmio_resource) {
+ dev_err(dev, "Failed to get DWC3 MMIO\n");
+ return -EINVAL;
+ }
+
+ appledwc->apple_regs = devm_platform_ioremap_resource_byname(pdev, "dwc3-apple");
+ if (IS_ERR(appledwc->apple_regs))
+ return dev_err_probe(dev, PTR_ERR(appledwc->apple_regs),
+ "Failed to map Apple-specific MMIO\n");
+
+ /*
+ * On this platform, DWC3 can only be brought up after parts of the PHY have been
+ * initialized with knowledge of the target mode and cable orientation from typec_set_mux.
+ * Since this has not happened here we cannot setup DWC3 yet and instead defer this until
+ * the first cable is connected. See the documentation for enum dwc3_apple_state for
+ * details.
+ */
+ appledwc->state = DWC3_APPLE_PROBE_PENDING;
+ ret = dwc3_apple_setup_role_switch(appledwc);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "Failed to setup role switch\n");
+
+ return 0;
+}
+
+static void dwc3_apple_remove(struct platform_device *pdev)
+{
+ struct dwc3 *dwc = platform_get_drvdata(pdev);
+ struct dwc3_apple *appledwc = to_dwc3_apple(dwc);
+
+ guard(mutex)(&appledwc->lock);
+
+ usb_role_switch_unregister(appledwc->role_sw);
+
+ /*
+ * If we're still in DWC3_APPLE_PROBE_PENDING we never got any cable connected event and
+ * dwc3_core_probe was never called and there's hence no need to call dwc3_core_remove.
+ * dwc3_apple_exit can be called unconditionally because it checks the state itself.
+ */
+ dwc3_apple_exit(appledwc);
+ if (appledwc->state != DWC3_APPLE_PROBE_PENDING)
+ dwc3_core_remove(&appledwc->dwc);
+}
+
+static const struct of_device_id dwc3_apple_of_match[] = {
+ { .compatible = "apple,t8103-dwc3" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, dwc3_apple_of_match);
+
+static struct platform_driver dwc3_apple_driver = {
+ .probe = dwc3_apple_probe,
+ .remove = dwc3_apple_remove,
+ .driver = {
+ .name = "dwc3-apple",
+ .of_match_table = dwc3_apple_of_match,
+ },
+};
+
+module_platform_driver(dwc3_apple_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Sven Peter <sven@kernel.org>");
+MODULE_DESCRIPTION("DesignWare DWC3 Apple Silicon Glue Driver");
diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c
index f5d963fae9e0..e934f94e8fd8 100644
--- a/drivers/usb/dwc3/dwc3-exynos.c
+++ b/drivers/usb/dwc3/dwc3-exynos.c
@@ -145,6 +145,12 @@ static void dwc3_exynos_remove(struct platform_device *pdev)
regulator_disable(exynos->vdd10);
}
+static const struct dwc3_exynos_driverdata exynos2200_drvdata = {
+ .clk_names = { "link_aclk" },
+ .num_clks = 1,
+ .suspend_clk_idx = -1,
+};
+
static const struct dwc3_exynos_driverdata exynos5250_drvdata = {
.clk_names = { "usbdrd30" },
.num_clks = 1,
@@ -163,6 +169,12 @@ static const struct dwc3_exynos_driverdata exynos7_drvdata = {
.suspend_clk_idx = 1,
};
+static const struct dwc3_exynos_driverdata exynos7870_drvdata = {
+ .clk_names = { "bus_early", "ref", "ctrl" },
+ .num_clks = 3,
+ .suspend_clk_idx = -1,
+};
+
static const struct dwc3_exynos_driverdata exynos850_drvdata = {
.clk_names = { "bus_early", "ref" },
.num_clks = 2,
@@ -175,8 +187,17 @@ static const struct dwc3_exynos_driverdata gs101_drvdata = {
.suspend_clk_idx = 1,
};
+static const struct dwc3_exynos_driverdata exynosautov920_drvdata = {
+ .clk_names = { "ref", "susp_clk"},
+ .num_clks = 2,
+ .suspend_clk_idx = 1,
+};
+
static const struct of_device_id exynos_dwc3_match[] = {
{
+ .compatible = "samsung,exynos2200-dwusb3",
+ .data = &exynos2200_drvdata,
+ }, {
.compatible = "samsung,exynos5250-dwusb3",
.data = &exynos5250_drvdata,
}, {
@@ -186,9 +207,15 @@ static const struct of_device_id exynos_dwc3_match[] = {
.compatible = "samsung,exynos7-dwusb3",
.data = &exynos7_drvdata,
}, {
+ .compatible = "samsung,exynos7870-dwusb3",
+ .data = &exynos7870_drvdata,
+ }, {
.compatible = "samsung,exynos850-dwusb3",
.data = &exynos850_drvdata,
}, {
+ .compatible = "samsung,exynosautov920-dwusb3",
+ .data = &exynosautov920_drvdata,
+ }, {
.compatible = "google,gs101-dwusb3",
.data = &gs101_drvdata,
}, {
diff --git a/drivers/usb/dwc3/dwc3-generic-plat.c b/drivers/usb/dwc3/dwc3-generic-plat.c
new file mode 100644
index 000000000000..ca69ac0eb07c
--- /dev/null
+++ b/drivers/usb/dwc3/dwc3-generic-plat.c
@@ -0,0 +1,256 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * dwc3-generic-plat.c - DesignWare USB3 generic platform driver
+ *
+ * Copyright (C) 2025 Ze Huang <huang.ze@linux.dev>
+ *
+ * Inspired by dwc3-qcom.c and dwc3-of-simple.c
+ */
+
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regulator/consumer.h>
+#include <linux/usb/otg.h>
+#include "glue.h"
+
+#define EIC7700_HSP_BUS_FILTER_EN BIT(0)
+#define EIC7700_HSP_BUS_CLKEN_GM BIT(9)
+#define EIC7700_HSP_BUS_CLKEN_GS BIT(16)
+#define EIC7700_HSP_AXI_LP_XM_CSYSREQ BIT(0)
+#define EIC7700_HSP_AXI_LP_XS_CSYSREQ BIT(16)
+
+struct dwc3_generic {
+ struct device *dev;
+ struct dwc3 dwc;
+ struct clk_bulk_data *clks;
+ int num_clocks;
+ struct reset_control *resets;
+};
+
+struct dwc3_generic_config {
+ int (*init)(struct dwc3_generic *dwc3g);
+ struct dwc3_properties properties;
+};
+
+#define to_dwc3_generic(d) container_of((d), struct dwc3_generic, dwc)
+
+static void dwc3_generic_reset_control_assert(void *data)
+{
+ reset_control_assert(data);
+}
+
+static int dwc3_eic7700_init(struct dwc3_generic *dwc3g)
+{
+ struct device *dev = dwc3g->dev;
+ struct regmap *regmap;
+ u32 hsp_usb_axi_lp;
+ u32 hsp_usb_bus;
+ u32 args[2];
+ u32 val;
+
+ regmap = syscon_regmap_lookup_by_phandle_args(dev->of_node,
+ "eswin,hsp-sp-csr",
+ ARRAY_SIZE(args), args);
+ if (IS_ERR(regmap)) {
+ dev_err(dev, "No hsp-sp-csr phandle specified\n");
+ return PTR_ERR(regmap);
+ }
+
+ hsp_usb_bus = args[0];
+ hsp_usb_axi_lp = args[1];
+
+ regmap_read(regmap, hsp_usb_bus, &val);
+ regmap_write(regmap, hsp_usb_bus, val | EIC7700_HSP_BUS_FILTER_EN |
+ EIC7700_HSP_BUS_CLKEN_GM | EIC7700_HSP_BUS_CLKEN_GS);
+
+ regmap_write(regmap, hsp_usb_axi_lp, EIC7700_HSP_AXI_LP_XM_CSYSREQ |
+ EIC7700_HSP_AXI_LP_XS_CSYSREQ);
+ return 0;
+}
+
+static int dwc3_spacemit_k1_init(struct dwc3_generic *dwc3g)
+{
+ struct device *dev = dwc3g->dev;
+
+ if (usb_get_dr_mode(dev) == USB_DR_MODE_HOST) {
+ int ret = devm_regulator_get_enable_optional(dev, "vbus");
+
+ if (ret && ret != -ENODEV)
+ return dev_err_probe(dev, ret, "failed to enable VBUS\n");
+ }
+
+ return 0;
+}
+
+static int dwc3_generic_probe(struct platform_device *pdev)
+{
+ const struct dwc3_generic_config *plat_config;
+ struct dwc3_probe_data probe_data = {};
+ struct device *dev = &pdev->dev;
+ struct dwc3_generic *dwc3g;
+ struct resource *res;
+ int ret;
+
+ dwc3g = devm_kzalloc(dev, sizeof(*dwc3g), GFP_KERNEL);
+ if (!dwc3g)
+ return -ENOMEM;
+
+ dwc3g->dev = dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "missing memory resource\n");
+ return -ENODEV;
+ }
+
+ dwc3g->resets = devm_reset_control_array_get_optional_exclusive(dev);
+ if (IS_ERR(dwc3g->resets))
+ return dev_err_probe(dev, PTR_ERR(dwc3g->resets), "failed to get resets\n");
+
+ ret = reset_control_assert(dwc3g->resets);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to assert resets\n");
+
+ /* Not strict timing, just for safety */
+ udelay(2);
+
+ ret = reset_control_deassert(dwc3g->resets);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to deassert resets\n");
+
+ ret = devm_add_action_or_reset(dev, dwc3_generic_reset_control_assert, dwc3g->resets);
+ if (ret)
+ return ret;
+
+ ret = devm_clk_bulk_get_all_enabled(dwc3g->dev, &dwc3g->clks);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to get clocks\n");
+
+ dwc3g->num_clocks = ret;
+ dwc3g->dwc.dev = dev;
+ probe_data.dwc = &dwc3g->dwc;
+ probe_data.res = res;
+ probe_data.ignore_clocks_and_resets = true;
+
+ plat_config = of_device_get_match_data(dev);
+ if (!plat_config) {
+ probe_data.properties = DWC3_DEFAULT_PROPERTIES;
+ goto core_probe;
+ }
+
+ probe_data.properties = plat_config->properties;
+ if (plat_config->init) {
+ ret = plat_config->init(dwc3g);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to init platform\n");
+ }
+
+core_probe:
+ ret = dwc3_core_probe(&probe_data);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to register DWC3 Core\n");
+
+ return 0;
+}
+
+static void dwc3_generic_remove(struct platform_device *pdev)
+{
+ struct dwc3 *dwc = platform_get_drvdata(pdev);
+
+ dwc3_core_remove(dwc);
+}
+
+static int dwc3_generic_suspend(struct device *dev)
+{
+ struct dwc3 *dwc = dev_get_drvdata(dev);
+ struct dwc3_generic *dwc3g = to_dwc3_generic(dwc);
+ int ret;
+
+ ret = dwc3_pm_suspend(dwc);
+ if (ret)
+ return ret;
+
+ clk_bulk_disable_unprepare(dwc3g->num_clocks, dwc3g->clks);
+
+ return 0;
+}
+
+static int dwc3_generic_resume(struct device *dev)
+{
+ struct dwc3 *dwc = dev_get_drvdata(dev);
+ struct dwc3_generic *dwc3g = to_dwc3_generic(dwc);
+ int ret;
+
+ ret = clk_bulk_prepare_enable(dwc3g->num_clocks, dwc3g->clks);
+ if (ret)
+ return ret;
+
+ ret = dwc3_pm_resume(dwc);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int dwc3_generic_runtime_suspend(struct device *dev)
+{
+ return dwc3_runtime_suspend(dev_get_drvdata(dev));
+}
+
+static int dwc3_generic_runtime_resume(struct device *dev)
+{
+ return dwc3_runtime_resume(dev_get_drvdata(dev));
+}
+
+static int dwc3_generic_runtime_idle(struct device *dev)
+{
+ return dwc3_runtime_idle(dev_get_drvdata(dev));
+}
+
+static const struct dev_pm_ops dwc3_generic_dev_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(dwc3_generic_suspend, dwc3_generic_resume)
+ RUNTIME_PM_OPS(dwc3_generic_runtime_suspend, dwc3_generic_runtime_resume,
+ dwc3_generic_runtime_idle)
+};
+
+static const struct dwc3_generic_config spacemit_k1_dwc3 = {
+ .init = dwc3_spacemit_k1_init,
+ .properties = DWC3_DEFAULT_PROPERTIES,
+};
+
+static const struct dwc3_generic_config fsl_ls1028_dwc3 = {
+ .properties.gsbuscfg0_reqinfo = 0x2222,
+};
+
+static const struct dwc3_generic_config eic7700_dwc3 = {
+ .init = dwc3_eic7700_init,
+ .properties = DWC3_DEFAULT_PROPERTIES,
+};
+
+static const struct of_device_id dwc3_generic_of_match[] = {
+ { .compatible = "spacemit,k1-dwc3", &spacemit_k1_dwc3},
+ { .compatible = "spacemit,k3-dwc3", },
+ { .compatible = "fsl,ls1028a-dwc3", &fsl_ls1028_dwc3},
+ { .compatible = "eswin,eic7700-dwc3", &eic7700_dwc3},
+ { .compatible = "starfive,jhb100-dwc3", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, dwc3_generic_of_match);
+
+static struct platform_driver dwc3_generic_driver = {
+ .probe = dwc3_generic_probe,
+ .remove = dwc3_generic_remove,
+ .driver = {
+ .name = "dwc3-generic-plat",
+ .of_match_table = dwc3_generic_of_match,
+ .pm = pm_ptr(&dwc3_generic_dev_pm_ops),
+ },
+};
+module_platform_driver(dwc3_generic_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("DesignWare USB3 generic platform driver");
diff --git a/drivers/usb/dwc3/dwc3-google.c b/drivers/usb/dwc3/dwc3-google.c
new file mode 100644
index 000000000000..4ca567ec01d0
--- /dev/null
+++ b/drivers/usb/dwc3/dwc3-google.c
@@ -0,0 +1,627 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * dwc3-google.c - Google DWC3 Specific Glue Layer
+ *
+ * Copyright (c) 2025, Google LLC
+ * Author: Roy Luo <royluo@google.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/iopoll.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include "core.h"
+#include "glue.h"
+
+/* HOST CFG registers */
+#define HC_STATUS_OFFSET 0x0
+#define HC_STATUS_CURRENT_POWER_STATE_U2PMU GENMASK(1, 0)
+#define HC_STATUS_CURRENT_POWER_STATE_U3PMU GENMASK(4, 3)
+
+#define HOST_CFG1_OFFSET 0x4
+#define HOST_CFG1_PME_EN BIT(3)
+#define HOST_CFG1_PM_POWER_STATE_REQUEST GENMASK(5, 4)
+#define HOST_CFG1_PM_POWER_STATE_D0 0x0
+#define HOST_CFG1_PM_POWER_STATE_D3 0x3
+
+/* USBINT registers */
+#define USBINT_CFG1_OFFSET 0x0
+#define USBINT_CFG1_USBDRD_PME_GEN_U2P_INTR_MSK BIT(2)
+#define USBINT_CFG1_USBDRD_PME_GEN_U3P_INTR_MSK BIT(3)
+#define USBINT_CFG1_USBDRD_PME_GEN_U2P_INTR_INT_EN BIT(8)
+#define USBINT_CFG1_USBDRD_PME_GEN_U3P_INTR_INT_EN BIT(9)
+#define USBINT_CFG1_USBDRD_PME_GEN_U2_INTR_CLR BIT(14)
+#define USBINT_CFG1_USBDRD_PME_GEN_U3_INTR_CLR BIT(15)
+
+#define USBINT_STATUS_OFFSET 0x4
+#define USBINT_STATUS_USBDRD_PME_GEN_U2P_INTR_STS_RAW BIT(2)
+#define USBINT_STATUS_USBDRD_PME_GEN_U3P_INTR_STS_RAW BIT(3)
+
+#define USBCS_TOP_CTRL_CFG1_OFFSET 0xc
+#define USBCS_TOP_CTRL_CFG1_USB2ONLY_MODE BIT(5)
+
+#define DWC3_GOOGLE_MAX_RESETS 4
+
+struct dwc3_google {
+ struct device *dev;
+ struct dwc3 dwc;
+ struct clk_bulk_data *clks;
+ int num_clks;
+ struct reset_control_bulk_data rsts[DWC3_GOOGLE_MAX_RESETS];
+ int num_rsts;
+ struct reset_control *non_sticky_rst;
+ struct device *usb_psw_pd;
+ struct device_link *usb_psw_pd_dl;
+ struct notifier_block usb_psw_pd_nb;
+ struct device *usb_top_pd;
+ struct device_link *usb_top_pd_dl;
+ struct regmap *usb_cfg_regmap;
+ unsigned int host_cfg_offset;
+ unsigned int usbint_cfg_offset;
+ int hs_pme_irq;
+ int ss_pme_irq;
+ bool is_usb2only;
+ bool is_hibernation;
+};
+
+#define to_dwc3_google(d) container_of_const((d), struct dwc3_google, dwc)
+
+static int dwc3_google_rst_init(struct dwc3_google *google)
+{
+ int ret;
+
+ google->num_rsts = 4;
+ google->rsts[0].id = "non_sticky";
+ google->rsts[1].id = "sticky";
+ google->rsts[2].id = "drd_bus";
+ google->rsts[3].id = "top";
+
+ ret = devm_reset_control_bulk_get_exclusive(google->dev,
+ google->num_rsts,
+ google->rsts);
+
+ if (ret < 0)
+ return ret;
+
+ google->non_sticky_rst = google->rsts[0].rstc;
+
+ return 0;
+}
+
+static int dwc3_google_set_pmu_state(struct dwc3_google *google, int state)
+{
+ u32 reg;
+ int ret;
+
+ regmap_read(google->usb_cfg_regmap,
+ google->host_cfg_offset + HOST_CFG1_OFFSET, &reg);
+
+ reg &= ~HOST_CFG1_PM_POWER_STATE_REQUEST;
+ reg |= (FIELD_PREP(HOST_CFG1_PM_POWER_STATE_REQUEST, state) |
+ HOST_CFG1_PME_EN);
+ regmap_write(google->usb_cfg_regmap,
+ google->host_cfg_offset + HOST_CFG1_OFFSET, reg);
+
+ ret = regmap_read_poll_timeout(google->usb_cfg_regmap,
+ google->host_cfg_offset + HC_STATUS_OFFSET, reg,
+ (FIELD_GET(HC_STATUS_CURRENT_POWER_STATE_U2PMU,
+ reg) == state &&
+ FIELD_GET(HC_STATUS_CURRENT_POWER_STATE_U3PMU,
+ reg) == state),
+ 10, 10000);
+
+ if (ret)
+ dev_err(google->dev, "failed to set PMU state %d\n", state);
+
+ return ret;
+}
+
+/*
+ * Clear pme interrupts and report their status.
+ * The hardware requires write-1 then write-0 sequence to clear the interrupt bits.
+ */
+static u32 dwc3_google_clear_pme_irqs(struct dwc3_google *google)
+{
+ u32 irq_status, reg_set, reg_clear;
+
+ regmap_read(google->usb_cfg_regmap,
+ google->usbint_cfg_offset + USBINT_STATUS_OFFSET, &irq_status);
+
+ irq_status &= (USBINT_STATUS_USBDRD_PME_GEN_U2P_INTR_STS_RAW |
+ USBINT_STATUS_USBDRD_PME_GEN_U3P_INTR_STS_RAW);
+ if (!irq_status)
+ return irq_status;
+
+ regmap_read(google->usb_cfg_regmap,
+ google->usbint_cfg_offset + USBINT_CFG1_OFFSET, &reg_set);
+
+ reg_clear = reg_set;
+ if (irq_status & USBINT_STATUS_USBDRD_PME_GEN_U2P_INTR_STS_RAW) {
+ reg_set |= USBINT_CFG1_USBDRD_PME_GEN_U2_INTR_CLR;
+ reg_clear &= ~USBINT_CFG1_USBDRD_PME_GEN_U2_INTR_CLR;
+ }
+ if (irq_status & USBINT_STATUS_USBDRD_PME_GEN_U3P_INTR_STS_RAW) {
+ reg_set |= USBINT_CFG1_USBDRD_PME_GEN_U3_INTR_CLR;
+ reg_clear &= ~USBINT_CFG1_USBDRD_PME_GEN_U3_INTR_CLR;
+ }
+
+ regmap_write(google->usb_cfg_regmap,
+ google->usbint_cfg_offset + USBINT_CFG1_OFFSET, reg_set);
+ regmap_write(google->usb_cfg_regmap,
+ google->usbint_cfg_offset + USBINT_CFG1_OFFSET, reg_clear);
+
+ return irq_status;
+}
+
+static void dwc3_google_enable_pme_irq(struct dwc3_google *google)
+{
+ u32 reg;
+
+ regmap_read(google->usb_cfg_regmap,
+ google->usbint_cfg_offset + USBINT_CFG1_OFFSET, &reg);
+ reg &= ~(USBINT_CFG1_USBDRD_PME_GEN_U2P_INTR_MSK |
+ USBINT_CFG1_USBDRD_PME_GEN_U3P_INTR_MSK);
+ reg |= (USBINT_CFG1_USBDRD_PME_GEN_U2P_INTR_INT_EN |
+ USBINT_CFG1_USBDRD_PME_GEN_U3P_INTR_INT_EN);
+ regmap_write(google->usb_cfg_regmap,
+ google->usbint_cfg_offset + USBINT_CFG1_OFFSET, reg);
+
+ enable_irq(google->hs_pme_irq);
+ enable_irq(google->ss_pme_irq);
+ enable_irq_wake(google->hs_pme_irq);
+ enable_irq_wake(google->ss_pme_irq);
+}
+
+static void dwc3_google_disable_pme_irq(struct dwc3_google *google)
+{
+ u32 reg;
+
+ regmap_read(google->usb_cfg_regmap,
+ google->usbint_cfg_offset + USBINT_CFG1_OFFSET, &reg);
+ reg &= ~(USBINT_CFG1_USBDRD_PME_GEN_U2P_INTR_INT_EN |
+ USBINT_CFG1_USBDRD_PME_GEN_U3P_INTR_INT_EN);
+ reg |= (USBINT_CFG1_USBDRD_PME_GEN_U2P_INTR_MSK |
+ USBINT_CFG1_USBDRD_PME_GEN_U3P_INTR_MSK);
+ regmap_write(google->usb_cfg_regmap,
+ google->usbint_cfg_offset + USBINT_CFG1_OFFSET, reg);
+
+ disable_irq_wake(google->hs_pme_irq);
+ disable_irq_wake(google->ss_pme_irq);
+ disable_irq_nosync(google->hs_pme_irq);
+ disable_irq_nosync(google->ss_pme_irq);
+}
+
+static irqreturn_t dwc3_google_resume_irq(int irq, void *data)
+{
+ struct dwc3_google *google = data;
+ struct dwc3 *dwc = &google->dwc;
+ u32 irq_status, dr_role;
+
+ irq_status = dwc3_google_clear_pme_irqs(google);
+ dr_role = dwc->current_dr_role;
+
+ if (!irq_status || !google->is_hibernation ||
+ dr_role != DWC3_GCTL_PRTCAP_HOST) {
+ dev_dbg(google->dev, "spurious pme irq %d, hibernation %d, dr_role %u\n",
+ irq, google->is_hibernation, dr_role);
+ return IRQ_HANDLED;
+ }
+
+ if (dwc->xhci)
+ pm_runtime_resume(&dwc->xhci->dev);
+
+ return IRQ_HANDLED;
+}
+
+static int dwc3_google_request_irq(struct dwc3_google *google, struct platform_device *pdev,
+ const char *irq_name, const char *req_name)
+{
+ int ret;
+ int irq;
+
+ irq = platform_get_irq_byname(pdev, irq_name);
+ if (irq < 0)
+ return irq;
+
+ irq_set_status_flags(irq, IRQ_NOAUTOEN);
+ ret = devm_request_threaded_irq(google->dev, irq, NULL,
+ dwc3_google_resume_irq,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+ req_name, google);
+ if (ret < 0) {
+ dev_err(google->dev, "failed to request irq %s\n", req_name);
+ return ret;
+ }
+
+ return irq;
+}
+
+static int dwc3_google_usb_psw_pd_notifier(struct notifier_block *nb, unsigned long action, void *d)
+{
+ struct dwc3_google *google = container_of(nb, struct dwc3_google, usb_psw_pd_nb);
+ int ret;
+
+ if (!google->is_hibernation)
+ return NOTIFY_OK;
+
+ if (action == GENPD_NOTIFY_OFF) {
+ dev_dbg(google->dev, "enter D3 power state\n");
+ dwc3_google_set_pmu_state(google, HOST_CFG1_PM_POWER_STATE_D3);
+ ret = reset_control_assert(google->non_sticky_rst);
+ if (ret)
+ dev_err(google->dev, "non sticky reset assert failed: %d\n", ret);
+ } else if (action == GENPD_NOTIFY_ON) {
+ dev_dbg(google->dev, "enter D0 power state\n");
+ dwc3_google_clear_pme_irqs(google);
+ ret = reset_control_deassert(google->non_sticky_rst);
+ if (ret)
+ dev_err(google->dev, "non sticky reset deassert failed: %d\n", ret);
+ dwc3_google_set_pmu_state(google, HOST_CFG1_PM_POWER_STATE_D0);
+ }
+
+ return NOTIFY_OK;
+}
+
+static void dwc3_google_pm_domain_deinit(struct dwc3_google *google)
+{
+ if (google->usb_top_pd_dl)
+ device_link_del(google->usb_top_pd_dl);
+
+ if (!IS_ERR_OR_NULL(google->usb_top_pd)) {
+ device_set_wakeup_capable(google->usb_top_pd, false);
+ dev_pm_domain_detach(google->usb_top_pd, true);
+ }
+
+ if (google->usb_psw_pd_dl)
+ device_link_del(google->usb_psw_pd_dl);
+
+ if (!IS_ERR_OR_NULL(google->usb_psw_pd)) {
+ dev_pm_genpd_remove_notifier(google->usb_psw_pd);
+ dev_pm_domain_detach(google->usb_psw_pd, true);
+ }
+}
+
+static int dwc3_google_pm_domain_init(struct dwc3_google *google)
+{
+ int ret;
+
+ /*
+ * Establish PM RUNTIME link between dwc dev and its power domain usb_psw_pd,
+ * register notifier block to handle hibernation.
+ */
+ google->usb_psw_pd = dev_pm_domain_attach_by_name(google->dev, "psw");
+ if (IS_ERR_OR_NULL(google->usb_psw_pd)) {
+ dev_err(google->dev, "failed to get psw pd");
+ ret = google->usb_psw_pd ? PTR_ERR(google->usb_psw_pd) : -ENODATA;
+ return ret;
+ }
+
+ google->usb_psw_pd_nb.notifier_call = dwc3_google_usb_psw_pd_notifier;
+ ret = dev_pm_genpd_add_notifier(google->usb_psw_pd, &google->usb_psw_pd_nb);
+ if (ret) {
+ dev_err(google->dev, "failed to add psw pd notifier");
+ goto err;
+ }
+
+ google->usb_psw_pd_dl = device_link_add(google->dev, google->usb_psw_pd,
+ DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME |
+ DL_FLAG_RPM_ACTIVE);
+ if (!google->usb_psw_pd_dl) {
+ dev_err(google->usb_psw_pd, "failed to add device link");
+ ret = -ENODEV;
+ goto err;
+ }
+
+ /*
+ * usb_top_pd is the parent power domain of usb_psw_pd. Keeping usb_top_pd on
+ * while usb_psw_pd is off places the controller in a power-gated state,
+ * essential for hibernation. Acquire a handle to usb_top_pd and sets it as
+ * wakeup-capable to allow the domain to be left on during system suspend.
+ */
+ google->usb_top_pd = dev_pm_domain_attach_by_name(google->dev, "top");
+ if (IS_ERR_OR_NULL(google->usb_top_pd)) {
+ dev_err(google->dev, "failed to get top pd");
+ ret = google->usb_top_pd ? PTR_ERR(google->usb_top_pd) : -ENODATA;
+ goto err;
+ }
+ device_set_wakeup_capable(google->usb_top_pd, true);
+
+ google->usb_top_pd_dl = device_link_add(google->dev, google->usb_top_pd,
+ DL_FLAG_STATELESS);
+ if (!google->usb_top_pd_dl) {
+ dev_err(google->usb_top_pd, "failed to add device link");
+ ret = -ENODEV;
+ goto err;
+ }
+
+ return 0;
+
+err:
+ dwc3_google_pm_domain_deinit(google);
+
+ return ret;
+}
+
+static void dwc3_google_program_usb2only(struct dwc3_google *google)
+{
+ u32 reg;
+
+ regmap_read(google->usb_cfg_regmap,
+ google->usbint_cfg_offset + USBCS_TOP_CTRL_CFG1_OFFSET, &reg);
+ reg |= USBCS_TOP_CTRL_CFG1_USB2ONLY_MODE;
+ regmap_write(google->usb_cfg_regmap,
+ google->usbint_cfg_offset + USBCS_TOP_CTRL_CFG1_OFFSET, reg);
+}
+
+static int dwc3_google_probe(struct platform_device *pdev)
+{
+ struct dwc3_probe_data probe_data = {};
+ struct device *dev = &pdev->dev;
+ struct dwc3_google *google;
+ struct resource *res;
+ int ret;
+ u32 args[2];
+
+ google = devm_kzalloc(&pdev->dev, sizeof(*google), GFP_KERNEL);
+ if (!google)
+ return -ENOMEM;
+
+ google->dev = &pdev->dev;
+
+ ret = dwc3_google_pm_domain_init(google);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to init pdom\n");
+
+ google->usb_cfg_regmap =
+ syscon_regmap_lookup_by_phandle_args(dev->of_node,
+ "google,usb-cfg-csr",
+ ARRAY_SIZE(args), args);
+ if (IS_ERR(google->usb_cfg_regmap)) {
+ ret = dev_err_probe(dev, PTR_ERR(google->usb_cfg_regmap),
+ "invalid usb cfg csr\n");
+ goto err_deinit_pdom;
+ }
+
+ google->host_cfg_offset = args[0];
+ google->usbint_cfg_offset = args[1];
+
+ if (device_property_match_string(dev, "phy-names", "usb3-phy") < 0) {
+ google->is_usb2only = true;
+ dwc3_google_program_usb2only(google);
+ }
+
+ ret = devm_clk_bulk_get_all_enabled(dev, &google->clks);
+ if (ret < 0) {
+ ret = dev_err_probe(dev, ret, "failed to get and enable clks\n");
+ goto err_deinit_pdom;
+ }
+ google->num_clks = ret;
+
+ ret = dwc3_google_rst_init(google);
+ if (ret) {
+ ret = dev_err_probe(dev, ret, "failed to get resets\n");
+ goto err_deinit_pdom;
+ }
+
+ ret = reset_control_bulk_deassert(google->num_rsts, google->rsts);
+ if (ret) {
+ ret = dev_err_probe(dev, ret, "failed to deassert rsts\n");
+ goto err_deinit_pdom;
+ }
+
+ ret = dwc3_google_request_irq(google, pdev, "hs_pme", "USB HS wakeup");
+ if (ret < 0) {
+ ret = dev_err_probe(dev, ret, "failed to request hs pme irq");
+ goto err_reset_assert;
+ }
+ google->hs_pme_irq = ret;
+
+ ret = dwc3_google_request_irq(google, pdev, "ss_pme", "USB SS wakeup");
+ if (ret < 0) {
+ ret = dev_err_probe(dev, ret, "failed to request ss pme irq");
+ goto err_reset_assert;
+ }
+ google->ss_pme_irq = ret;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ ret = dev_err_probe(dev, -ENODEV, "invalid memory\n");
+ goto err_reset_assert;
+ }
+
+ device_init_wakeup(dev, true);
+
+ google->dwc.dev = dev;
+ probe_data.dwc = &google->dwc;
+ probe_data.res = res;
+ probe_data.ignore_clocks_and_resets = true;
+ ret = dwc3_core_probe(&probe_data);
+ if (ret) {
+ ret = dev_err_probe(dev, ret, "failed to register DWC3 Core\n");
+ goto err_reset_assert;
+ }
+
+ return 0;
+
+err_reset_assert:
+ reset_control_bulk_assert(google->num_rsts, google->rsts);
+
+err_deinit_pdom:
+ dwc3_google_pm_domain_deinit(google);
+
+ return ret;
+}
+
+static void dwc3_google_remove(struct platform_device *pdev)
+{
+ struct dwc3 *dwc = platform_get_drvdata(pdev);
+ struct dwc3_google *google = to_dwc3_google(dwc);
+
+ dwc3_core_remove(&google->dwc);
+
+ reset_control_bulk_assert(google->num_rsts, google->rsts);
+
+ dwc3_google_pm_domain_deinit(google);
+}
+
+static int dwc3_google_suspend(struct dwc3_google *google, pm_message_t msg)
+{
+ if (pm_runtime_suspended(google->dev))
+ return 0;
+
+ if (google->dwc.current_dr_role == DWC3_GCTL_PRTCAP_HOST) {
+ /*
+ * Follow dwc3_suspend_common() guidelines for deciding between
+ * a full teardown and hibernation.
+ */
+ if (PMSG_IS_AUTO(msg) || device_may_wakeup(google->dev)) {
+ dev_dbg(google->dev, "enter hibernation");
+ pm_runtime_get_sync(google->usb_top_pd);
+ device_wakeup_enable(google->usb_top_pd);
+ dwc3_google_enable_pme_irq(google);
+ google->is_hibernation = true;
+ return 0;
+ }
+ }
+
+ reset_control_bulk_assert(google->num_rsts, google->rsts);
+ clk_bulk_disable_unprepare(google->num_clks, google->clks);
+
+ return 0;
+}
+
+static int dwc3_google_resume(struct dwc3_google *google, pm_message_t msg)
+{
+ int ret;
+
+ if (google->is_hibernation) {
+ dev_dbg(google->dev, "exit hibernation");
+ dwc3_google_disable_pme_irq(google);
+ device_wakeup_disable(google->usb_top_pd);
+ pm_runtime_put_sync(google->usb_top_pd);
+ google->is_hibernation = false;
+ return 0;
+ }
+
+ if (google->is_usb2only)
+ dwc3_google_program_usb2only(google);
+
+ ret = clk_bulk_prepare_enable(google->num_clks, google->clks);
+ if (ret)
+ return ret;
+
+ ret = reset_control_bulk_deassert(google->num_rsts, google->rsts);
+ if (ret) {
+ clk_bulk_disable_unprepare(google->num_clks, google->clks);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int dwc3_google_pm_suspend(struct device *dev)
+{
+ struct dwc3 *dwc = dev_get_drvdata(dev);
+ struct dwc3_google *google = to_dwc3_google(dwc);
+ int ret;
+
+ ret = dwc3_pm_suspend(&google->dwc);
+ if (ret)
+ return ret;
+
+ return dwc3_google_suspend(google, PMSG_SUSPEND);
+}
+
+static int dwc3_google_pm_resume(struct device *dev)
+{
+ struct dwc3 *dwc = dev_get_drvdata(dev);
+ struct dwc3_google *google = to_dwc3_google(dwc);
+ int ret;
+
+ ret = dwc3_google_resume(google, PMSG_RESUME);
+ if (ret)
+ return ret;
+
+ return dwc3_pm_resume(&google->dwc);
+}
+
+static void dwc3_google_complete(struct device *dev)
+{
+ struct dwc3 *dwc = dev_get_drvdata(dev);
+
+ dwc3_pm_complete(dwc);
+}
+
+static int dwc3_google_prepare(struct device *dev)
+{
+ struct dwc3 *dwc = dev_get_drvdata(dev);
+
+ return dwc3_pm_prepare(dwc);
+}
+
+static int dwc3_google_runtime_suspend(struct device *dev)
+{
+ struct dwc3 *dwc = dev_get_drvdata(dev);
+ struct dwc3_google *google = to_dwc3_google(dwc);
+ int ret;
+
+ ret = dwc3_runtime_suspend(&google->dwc);
+ if (ret)
+ return ret;
+
+ return dwc3_google_suspend(google, PMSG_AUTO_SUSPEND);
+}
+
+static int dwc3_google_runtime_resume(struct device *dev)
+{
+ struct dwc3 *dwc = dev_get_drvdata(dev);
+ struct dwc3_google *google = to_dwc3_google(dwc);
+ int ret;
+
+ ret = dwc3_google_resume(google, PMSG_AUTO_RESUME);
+ if (ret)
+ return ret;
+
+ return dwc3_runtime_resume(&google->dwc);
+}
+
+static int dwc3_google_runtime_idle(struct device *dev)
+{
+ return dwc3_runtime_idle(dev_get_drvdata(dev));
+}
+
+static const struct dev_pm_ops dwc3_google_dev_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(dwc3_google_pm_suspend, dwc3_google_pm_resume)
+ RUNTIME_PM_OPS(dwc3_google_runtime_suspend, dwc3_google_runtime_resume,
+ dwc3_google_runtime_idle)
+ .complete = pm_sleep_ptr(dwc3_google_complete),
+ .prepare = pm_sleep_ptr(dwc3_google_prepare),
+};
+
+static const struct of_device_id dwc3_google_of_match[] = {
+ { .compatible = "google,lga-dwc3" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, dwc3_google_of_match);
+
+static struct platform_driver dwc3_google_driver = {
+ .probe = dwc3_google_probe,
+ .remove = dwc3_google_remove,
+ .driver = {
+ .name = "dwc3-google",
+ .pm = pm_ptr(&dwc3_google_dev_pm_ops),
+ .of_match_table = dwc3_google_of_match,
+ },
+};
+
+module_platform_driver(dwc3_google_driver);
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("DesignWare DWC3 Google Glue Driver");
diff --git a/drivers/usb/dwc3/dwc3-imx.c b/drivers/usb/dwc3/dwc3-imx.c
new file mode 100644
index 000000000000..973a486b544d
--- /dev/null
+++ b/drivers/usb/dwc3/dwc3-imx.c
@@ -0,0 +1,448 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * dwc3-imx.c - NXP i.MX Soc USB3 Specific Glue layer
+ *
+ * Copyright 2026 NXP
+ */
+
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+
+#include "core.h"
+#include "glue.h"
+
+/* USB wakeup registers */
+#define USB_WAKEUP_CTRL 0x00
+
+/* Global wakeup interrupt enable, also used to clear interrupt */
+#define USB_WAKEUP_EN BIT(31)
+/* Wakeup from connect or disconnect, only for superspeed */
+#define USB_WAKEUP_SS_CONN BIT(5)
+/* 0 select vbus_valid, 1 select sessvld */
+#define USB_WAKEUP_VBUS_SRC_SESS_VAL BIT(4)
+/* Enable signal for wake up from u3 state */
+#define USB_WAKEUP_U3_EN BIT(3)
+/* Enable signal for wake up from id change */
+#define USB_WAKEUP_ID_EN BIT(2)
+/* Enable signal for wake up from vbus change */
+#define USB_WAKEUP_VBUS_EN BIT(1)
+/* Enable signal for wake up from dp/dm change */
+#define USB_WAKEUP_DPDM_EN BIT(0)
+
+#define USB_WAKEUP_EN_MASK GENMASK(5, 0)
+
+/* USB glue registers */
+#define USB_CTRL0 0x00
+#define USB_CTRL1 0x04
+
+#define USB_CTRL0_PORTPWR_EN BIT(12) /* 1 - PPC enabled (default) */
+#define USB_CTRL0_USB3_FIXED BIT(22) /* 1 - USB3 permanent attached */
+#define USB_CTRL0_USB2_FIXED BIT(23) /* 1 - USB2 permanent attached */
+
+#define USB_CTRL1_OC_POLARITY BIT(16) /* 0 - HIGH / 1 - LOW */
+#define USB_CTRL1_PWR_POLARITY BIT(17) /* 0 - HIGH / 1 - LOW */
+
+struct dwc3_imx {
+ struct dwc3 dwc;
+ struct device *dev;
+ void __iomem *blkctl_base;
+ void __iomem *glue_base;
+ struct clk *hsio_clk;
+ struct clk *suspend_clk;
+ int irq;
+ bool pm_suspended;
+ bool wakeup_pending;
+ unsigned permanent_attached:1;
+ unsigned disable_pwr_ctrl:1;
+ unsigned overcur_active_low:1;
+ unsigned power_active_low:1;
+};
+
+#define to_dwc3_imx(d) container_of((d), struct dwc3_imx, dwc)
+
+static void dwc3_imx_get_property(struct dwc3_imx *dwc_imx)
+{
+ struct device *dev = dwc_imx->dev;
+
+ dwc_imx->permanent_attached =
+ device_property_read_bool(dev, "fsl,permanently-attached");
+ dwc_imx->disable_pwr_ctrl =
+ device_property_read_bool(dev, "fsl,disable-port-power-control");
+ dwc_imx->overcur_active_low =
+ device_property_read_bool(dev, "fsl,over-current-active-low");
+ dwc_imx->power_active_low =
+ device_property_read_bool(dev, "fsl,power-active-low");
+}
+
+static void dwc3_imx_configure_glue(struct dwc3_imx *dwc_imx)
+{
+ u32 value;
+
+ if (!dwc_imx->glue_base)
+ return;
+
+ value = readl(dwc_imx->glue_base + USB_CTRL0);
+
+ if (dwc_imx->permanent_attached)
+ value |= USB_CTRL0_USB2_FIXED | USB_CTRL0_USB3_FIXED;
+ else
+ value &= ~(USB_CTRL0_USB2_FIXED | USB_CTRL0_USB3_FIXED);
+
+ if (dwc_imx->disable_pwr_ctrl)
+ value &= ~USB_CTRL0_PORTPWR_EN;
+ else
+ value |= USB_CTRL0_PORTPWR_EN;
+
+ writel(value, dwc_imx->glue_base + USB_CTRL0);
+
+ value = readl(dwc_imx->glue_base + USB_CTRL1);
+ if (dwc_imx->overcur_active_low)
+ value |= USB_CTRL1_OC_POLARITY;
+ else
+ value &= ~USB_CTRL1_OC_POLARITY;
+
+ if (dwc_imx->power_active_low)
+ value |= USB_CTRL1_PWR_POLARITY;
+ else
+ value &= ~USB_CTRL1_PWR_POLARITY;
+
+ writel(value, dwc_imx->glue_base + USB_CTRL1);
+}
+
+static void dwc3_imx_wakeup_enable(struct dwc3_imx *dwc_imx, pm_message_t msg)
+{
+ struct dwc3 *dwc = &dwc_imx->dwc;
+ u32 val;
+
+ val = readl(dwc_imx->blkctl_base + USB_WAKEUP_CTRL);
+
+ if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_HOST && dwc->xhci) {
+ val |= USB_WAKEUP_EN | USB_WAKEUP_DPDM_EN;
+ if (PMSG_IS_AUTO(msg))
+ val |= USB_WAKEUP_SS_CONN | USB_WAKEUP_U3_EN;
+ } else {
+ val |= USB_WAKEUP_EN | USB_WAKEUP_VBUS_EN |
+ USB_WAKEUP_VBUS_SRC_SESS_VAL;
+ }
+
+ writel(val, dwc_imx->blkctl_base + USB_WAKEUP_CTRL);
+}
+
+static void dwc3_imx_wakeup_disable(struct dwc3_imx *dwc_imx)
+{
+ u32 val;
+
+ val = readl(dwc_imx->blkctl_base + USB_WAKEUP_CTRL);
+ val &= ~(USB_WAKEUP_EN | USB_WAKEUP_EN_MASK);
+ writel(val, dwc_imx->blkctl_base + USB_WAKEUP_CTRL);
+}
+
+static irqreturn_t dwc3_imx_interrupt(int irq, void *data)
+{
+ struct dwc3_imx *dwc_imx = data;
+ struct dwc3 *dwc = &dwc_imx->dwc;
+
+ if (!dwc_imx->pm_suspended)
+ return IRQ_HANDLED;
+
+ disable_irq_nosync(dwc_imx->irq);
+ dwc_imx->wakeup_pending = true;
+
+ if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_HOST && dwc->xhci)
+ pm_runtime_resume(&dwc->xhci->dev);
+ else if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_DEVICE)
+ pm_runtime_get(dwc->dev);
+
+ return IRQ_HANDLED;
+}
+
+static void dwc3_imx_pre_set_role(struct dwc3 *dwc, enum usb_role role)
+{
+ if (role == USB_ROLE_HOST)
+ /*
+ * For xhci host, we need disable dwc core auto
+ * suspend, because during this auto suspend delay(5s),
+ * xhci host RUN_STOP is cleared and wakeup is not
+ * enabled, if device is inserted, xhci host can't
+ * response the connection.
+ */
+ pm_runtime_dont_use_autosuspend(dwc->dev);
+ else
+ pm_runtime_use_autosuspend(dwc->dev);
+}
+
+static struct dwc3_glue_ops dwc3_imx_glue_ops = {
+ .pre_set_role = dwc3_imx_pre_set_role,
+};
+
+static const struct property_entry dwc3_imx_properties[] = {
+ PROPERTY_ENTRY_BOOL("xhci-missing-cas-quirk"),
+ PROPERTY_ENTRY_BOOL("xhci-skip-phy-init-quirk"),
+ {},
+};
+
+static const struct software_node dwc3_imx_swnode = {
+ .properties = dwc3_imx_properties,
+};
+
+static int dwc3_imx_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct dwc3_imx *dwc_imx;
+ struct dwc3 *dwc;
+ struct resource *res;
+ const char *irq_name;
+ struct dwc3_probe_data probe_data = {};
+ int ret, irq;
+
+ dwc_imx = devm_kzalloc(dev, sizeof(*dwc_imx), GFP_KERNEL);
+ if (!dwc_imx)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, dwc_imx);
+ dwc_imx->dev = dev;
+
+ dwc3_imx_get_property(dwc_imx);
+
+ dwc_imx->blkctl_base = devm_platform_ioremap_resource_byname(pdev, "blkctl");
+ if (IS_ERR(dwc_imx->blkctl_base))
+ return PTR_ERR(dwc_imx->blkctl_base);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "glue");
+ if (!res) {
+ dev_warn(dev, "Base address for glue layer missing\n");
+ } else {
+ dwc_imx->glue_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(dwc_imx->glue_base))
+ return PTR_ERR(dwc_imx->glue_base);
+ }
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "core");
+ if (!res)
+ return dev_err_probe(dev, -ENODEV, "missing core memory resource\n");
+
+ dwc_imx->hsio_clk = devm_clk_get_enabled(dev, "hsio");
+ if (IS_ERR(dwc_imx->hsio_clk))
+ return dev_err_probe(dev, PTR_ERR(dwc_imx->hsio_clk),
+ "Failed to get hsio clk\n");
+
+ dwc_imx->suspend_clk = devm_clk_get_enabled(dev, "suspend");
+ if (IS_ERR(dwc_imx->suspend_clk))
+ return dev_err_probe(dev, PTR_ERR(dwc_imx->suspend_clk),
+ "Failed to get suspend clk\n");
+
+ irq = platform_get_irq_byname(pdev, "wakeup");
+ if (irq < 0)
+ return irq;
+ dwc_imx->irq = irq;
+
+ irq_name = devm_kasprintf(dev, GFP_KERNEL, "%s:wakeup", dev_name(dev));
+ if (!irq_name)
+ return dev_err_probe(dev, -ENOMEM, "failed to create irq_name\n");
+
+ ret = devm_request_threaded_irq(dev, irq, NULL, dwc3_imx_interrupt,
+ IRQF_ONESHOT | IRQF_NO_AUTOEN,
+ irq_name, dwc_imx);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to request IRQ #%d\n", irq);
+
+ ret = device_add_software_node(dev, &dwc3_imx_swnode);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to add software node\n");
+
+ dwc3_imx_configure_glue(dwc_imx);
+
+ dwc = &dwc_imx->dwc;
+ dwc->dev = dev;
+ dwc->glue_ops = &dwc3_imx_glue_ops;
+
+ probe_data.res = res;
+ probe_data.dwc = dwc;
+ probe_data.properties = DWC3_DEFAULT_PROPERTIES;
+ probe_data.properties.needs_full_reinit = true;
+
+ ret = dwc3_core_probe(&probe_data);
+ if (ret) {
+ device_remove_software_node(dev);
+ return ret;
+ }
+
+ device_set_wakeup_capable(dev, true);
+ return 0;
+}
+
+static void dwc3_imx_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct dwc3 *dwc = dev_get_drvdata(dev);
+
+ dwc3_core_remove(dwc);
+ device_remove_software_node(dev);
+}
+
+static void dwc3_imx_suspend(struct dwc3_imx *dwc_imx, pm_message_t msg)
+{
+ if (dwc_imx->pm_suspended)
+ return;
+
+ if (PMSG_IS_AUTO(msg) || device_may_wakeup(dwc_imx->dev))
+ dwc3_imx_wakeup_enable(dwc_imx, msg);
+
+ enable_irq(dwc_imx->irq);
+ dwc_imx->pm_suspended = true;
+}
+
+static void dwc3_imx_resume(struct dwc3_imx *dwc_imx, pm_message_t msg)
+{
+ struct dwc3 *dwc = &dwc_imx->dwc;
+
+ if (!dwc_imx->pm_suspended)
+ return;
+
+ dwc_imx->pm_suspended = false;
+ if (!dwc_imx->wakeup_pending)
+ disable_irq_nosync(dwc_imx->irq);
+
+ dwc3_imx_wakeup_disable(dwc_imx);
+
+ /* Upon power loss any previous configuration is lost, restore it */
+ dwc3_imx_configure_glue(dwc_imx);
+
+ if (dwc_imx->wakeup_pending) {
+ dwc_imx->wakeup_pending = false;
+ if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_DEVICE)
+ pm_runtime_put_autosuspend(dwc->dev);
+ else
+ /*
+ * Add wait for xhci switch from suspend
+ * clock to normal clock to detect connection.
+ */
+ usleep_range(9000, 10000);
+ }
+}
+
+static int dwc3_imx_runtime_suspend(struct device *dev)
+{
+ struct dwc3 *dwc = dev_get_drvdata(dev);
+ struct dwc3_imx *dwc_imx = to_dwc3_imx(dwc);
+ int ret;
+
+ ret = dwc3_runtime_suspend(dwc);
+ if (ret)
+ return ret;
+
+ dwc3_imx_suspend(dwc_imx, PMSG_AUTO_SUSPEND);
+ return 0;
+}
+
+static int dwc3_imx_runtime_resume(struct device *dev)
+{
+ struct dwc3 *dwc = dev_get_drvdata(dev);
+ struct dwc3_imx *dwc_imx = to_dwc3_imx(dwc);
+
+ dwc3_imx_resume(dwc_imx, PMSG_AUTO_RESUME);
+ return dwc3_runtime_resume(dwc);
+}
+
+static int dwc3_imx_runtime_idle(struct device *dev)
+{
+ return dwc3_runtime_idle(dev_get_drvdata(dev));
+}
+
+static int dwc3_imx_pm_suspend(struct device *dev)
+{
+ struct dwc3 *dwc = dev_get_drvdata(dev);
+ struct dwc3_imx *dwc_imx = to_dwc3_imx(dwc);
+ int ret;
+
+ ret = dwc3_pm_suspend(dwc);
+ if (ret)
+ return ret;
+
+ dwc3_imx_suspend(dwc_imx, PMSG_SUSPEND);
+
+ if (device_may_wakeup(dev)) {
+ enable_irq_wake(dwc_imx->irq);
+ device_set_out_band_wakeup(dev);
+ } else {
+ clk_disable_unprepare(dwc_imx->suspend_clk);
+ }
+
+ clk_disable_unprepare(dwc_imx->hsio_clk);
+
+ return 0;
+}
+
+static int dwc3_imx_pm_resume(struct device *dev)
+{
+ struct dwc3 *dwc = dev_get_drvdata(dev);
+ struct dwc3_imx *dwc_imx = to_dwc3_imx(dwc);
+ int ret;
+
+ if (device_may_wakeup(dwc_imx->dev)) {
+ disable_irq_wake(dwc_imx->irq);
+ } else {
+ ret = clk_prepare_enable(dwc_imx->suspend_clk);
+ if (ret)
+ return ret;
+ }
+
+ ret = clk_prepare_enable(dwc_imx->hsio_clk);
+ if (ret) {
+ clk_disable_unprepare(dwc_imx->suspend_clk);
+ return ret;
+ }
+
+ dwc3_imx_resume(dwc_imx, PMSG_RESUME);
+
+ ret = dwc3_pm_resume(dwc);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void dwc3_imx_complete(struct device *dev)
+{
+ dwc3_pm_complete(dev_get_drvdata(dev));
+}
+
+static int dwc3_imx_prepare(struct device *dev)
+{
+ return dwc3_pm_prepare(dev_get_drvdata(dev));
+}
+
+static const struct dev_pm_ops dwc3_imx_dev_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(dwc3_imx_pm_suspend, dwc3_imx_pm_resume)
+ RUNTIME_PM_OPS(dwc3_imx_runtime_suspend, dwc3_imx_runtime_resume,
+ dwc3_imx_runtime_idle)
+ .complete = pm_sleep_ptr(dwc3_imx_complete),
+ .prepare = pm_sleep_ptr(dwc3_imx_prepare),
+};
+
+static const struct of_device_id dwc3_imx_of_match[] = {
+ { .compatible = "nxp,imx8mp-dwc3", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, dwc3_imx_of_match);
+
+static struct platform_driver dwc3_imx_driver = {
+ .probe = dwc3_imx_probe,
+ .remove = dwc3_imx_remove,
+ .driver = {
+ .name = "imx-dwc3",
+ .pm = pm_ptr(&dwc3_imx_dev_pm_ops),
+ .of_match_table = dwc3_imx_of_match,
+ },
+};
+
+module_platform_driver(dwc3_imx_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("DesignWare USB3 i.MX Glue Layer");
diff --git a/drivers/usb/dwc3/dwc3-imx8mp.c b/drivers/usb/dwc3/dwc3-imx8mp.c
index 3edc5aca76f9..1cf96540b66e 100644
--- a/drivers/usb/dwc3/dwc3-imx8mp.c
+++ b/drivers/usb/dwc3/dwc3-imx8mp.c
@@ -51,7 +51,7 @@
struct dwc3_imx8mp {
struct device *dev;
- struct platform_device *dwc3;
+ struct platform_device *dwc3_pdev;
void __iomem *hsio_blk_base;
void __iomem *glue_base;
struct clk *hsio_clk;
@@ -100,7 +100,7 @@ static void imx8mp_configure_glue(struct dwc3_imx8mp *dwc3_imx)
static void dwc3_imx8mp_wakeup_enable(struct dwc3_imx8mp *dwc3_imx,
pm_message_t msg)
{
- struct dwc3 *dwc3 = platform_get_drvdata(dwc3_imx->dwc3);
+ struct dwc3 *dwc3 = platform_get_drvdata(dwc3_imx->dwc3_pdev);
u32 val;
if (!dwc3)
@@ -142,7 +142,7 @@ static const struct software_node dwc3_imx8mp_swnode = {
static irqreturn_t dwc3_imx8mp_interrupt(int irq, void *_dwc3_imx)
{
struct dwc3_imx8mp *dwc3_imx = _dwc3_imx;
- struct dwc3 *dwc = platform_get_drvdata(dwc3_imx->dwc3);
+ struct dwc3 *dwc = platform_get_drvdata(dwc3_imx->dwc3_pdev);
if (!dwc3_imx->pm_suspended)
return IRQ_HANDLED;
@@ -158,11 +158,31 @@ static irqreturn_t dwc3_imx8mp_interrupt(int irq, void *_dwc3_imx)
return IRQ_HANDLED;
}
+static void dwc3_imx_pre_set_role(struct dwc3 *dwc, enum usb_role role)
+{
+ if (role == USB_ROLE_HOST)
+ /*
+ * For xhci host, we need disable dwc core auto
+ * suspend, because during this auto suspend delay(5s),
+ * xhci host RUN_STOP is cleared and wakeup is not
+ * enabled, if device is inserted, xhci host can't
+ * response the connection.
+ */
+ pm_runtime_dont_use_autosuspend(dwc->dev);
+ else
+ pm_runtime_use_autosuspend(dwc->dev);
+}
+
+struct dwc3_glue_ops dwc3_imx_glue_ops = {
+ .pre_set_role = dwc3_imx_pre_set_role,
+};
+
static int dwc3_imx8mp_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *node = dev->of_node;
struct dwc3_imx8mp *dwc3_imx;
+ struct dwc3 *dwc3;
struct resource *res;
int err, irq;
@@ -233,18 +253,29 @@ static int dwc3_imx8mp_probe(struct platform_device *pdev)
goto remove_swnode;
}
- dwc3_imx->dwc3 = of_find_device_by_node(dwc3_np);
- if (!dwc3_imx->dwc3) {
+ dwc3_imx->dwc3_pdev = of_find_device_by_node(dwc3_np);
+ if (!dwc3_imx->dwc3_pdev) {
dev_err(dev, "failed to get dwc3 platform device\n");
err = -ENODEV;
goto depopulate;
}
+ dwc3 = platform_get_drvdata(dwc3_imx->dwc3_pdev);
+ if (!dwc3) {
+ err = dev_err_probe(dev, -EPROBE_DEFER, "failed to get dwc3 platform data\n");
+ goto put_dwc3;
+ }
+
+ dwc3->glue_ops = &dwc3_imx_glue_ops;
+
+ if (dwc3->dr_mode == USB_DR_MODE_HOST)
+ pm_runtime_dont_use_autosuspend(dwc3->dev);
+
err = devm_request_threaded_irq(dev, irq, NULL, dwc3_imx8mp_interrupt,
IRQF_ONESHOT, dev_name(dev), dwc3_imx);
if (err) {
dev_err(dev, "failed to request IRQ #%d --> %d\n", irq, err);
- goto depopulate;
+ goto put_dwc3;
}
device_set_wakeup_capable(dev, true);
@@ -252,6 +283,8 @@ static int dwc3_imx8mp_probe(struct platform_device *pdev)
return 0;
+put_dwc3:
+ put_device(&dwc3_imx->dwc3_pdev->dev);
depopulate:
of_platform_depopulate(dev);
remove_swnode:
@@ -265,8 +298,11 @@ disable_rpm:
static void dwc3_imx8mp_remove(struct platform_device *pdev)
{
+ struct dwc3_imx8mp *dwc3_imx = platform_get_drvdata(pdev);
struct device *dev = &pdev->dev;
+ put_device(&dwc3_imx->dwc3_pdev->dev);
+
pm_runtime_get_sync(dev);
of_platform_depopulate(dev);
device_remove_software_node(dev);
@@ -291,7 +327,7 @@ static int dwc3_imx8mp_suspend(struct dwc3_imx8mp *dwc3_imx, pm_message_t msg)
static int dwc3_imx8mp_resume(struct dwc3_imx8mp *dwc3_imx, pm_message_t msg)
{
- struct dwc3 *dwc = platform_get_drvdata(dwc3_imx->dwc3);
+ struct dwc3 *dwc = platform_get_drvdata(dwc3_imx->dwc3_pdev);
int ret = 0;
if (!dwc3_imx->pm_suspended)
@@ -307,7 +343,6 @@ static int dwc3_imx8mp_resume(struct dwc3_imx8mp *dwc3_imx, pm_message_t msg)
if (dwc3_imx->wakeup_pending) {
dwc3_imx->wakeup_pending = false;
if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_DEVICE) {
- pm_runtime_mark_last_busy(dwc->dev);
pm_runtime_put_autosuspend(dwc->dev);
} else {
/*
@@ -329,10 +364,15 @@ static int dwc3_imx8mp_pm_suspend(struct device *dev)
ret = dwc3_imx8mp_suspend(dwc3_imx, PMSG_SUSPEND);
- if (device_may_wakeup(dwc3_imx->dev))
+ if (device_may_wakeup(dwc3_imx->dev)) {
enable_irq_wake(dwc3_imx->irq);
- else
+
+ if (device_is_compatible(dev, "fsl,imx95-dwc3"))
+ device_set_out_band_wakeup(dev);
+
+ } else {
clk_disable_unprepare(dwc3_imx->suspend_clk);
+ }
clk_disable_unprepare(dwc3_imx->hsio_clk);
dev_dbg(dev, "dwc3 imx8mp pm suspend.\n");
diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c
index 7d80bf7b18b0..55e144ba8cfc 100644
--- a/drivers/usb/dwc3/dwc3-meson-g12a.c
+++ b/drivers/usb/dwc3/dwc3-meson-g12a.c
@@ -837,6 +837,9 @@ static void dwc3_meson_g12a_remove(struct platform_device *pdev)
usb_role_switch_unregister(priv->role_switch);
+ put_device(priv->switch_desc.udc);
+ put_device(priv->switch_desc.usb2_port);
+
of_platform_depopulate(dev);
for (i = 0 ; i < PHY_COUNT ; ++i) {
diff --git a/drivers/usb/dwc3/dwc3-of-simple.c b/drivers/usb/dwc3/dwc3-of-simple.c
index a4954a21be93..c116143335d9 100644
--- a/drivers/usb/dwc3/dwc3-of-simple.c
+++ b/drivers/usb/dwc3/dwc3-of-simple.c
@@ -70,11 +70,11 @@ static int dwc3_of_simple_probe(struct platform_device *pdev)
simple->num_clocks = ret;
ret = clk_bulk_prepare_enable(simple->num_clocks, simple->clks);
if (ret)
- goto err_resetc_assert;
+ goto err_clk_put_all;
ret = of_platform_populate(np, NULL, NULL, dev);
if (ret)
- goto err_clk_put;
+ goto err_clk_disable;
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
@@ -82,8 +82,9 @@ static int dwc3_of_simple_probe(struct platform_device *pdev)
return 0;
-err_clk_put:
+err_clk_disable:
clk_bulk_disable_unprepare(simple->num_clocks, simple->clks);
+err_clk_put_all:
clk_bulk_put_all(simple->num_clocks, simple->clks);
err_resetc_assert:
diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c
index 052852f80146..6c1cbb722ca8 100644
--- a/drivers/usb/dwc3/dwc3-pci.c
+++ b/drivers/usb/dwc3/dwc3-pci.c
@@ -21,39 +21,42 @@
#include <linux/acpi.h>
#include <linux/delay.h>
+#define PCI_DEVICE_ID_INTEL_CMLLP 0x02ee
+#define PCI_DEVICE_ID_INTEL_CMLH 0x06ee
+#define PCI_DEVICE_ID_INTEL_BXT 0x0aaa
#define PCI_DEVICE_ID_INTEL_BYT 0x0f37
#define PCI_DEVICE_ID_INTEL_MRFLD 0x119e
-#define PCI_DEVICE_ID_INTEL_BSW 0x22b7
-#define PCI_DEVICE_ID_INTEL_SPTLP 0x9d30
-#define PCI_DEVICE_ID_INTEL_SPTH 0xa130
-#define PCI_DEVICE_ID_INTEL_BXT 0x0aaa
#define PCI_DEVICE_ID_INTEL_BXT_M 0x1aaa
-#define PCI_DEVICE_ID_INTEL_APL 0x5aaa
-#define PCI_DEVICE_ID_INTEL_KBP 0xa2b0
-#define PCI_DEVICE_ID_INTEL_CMLLP 0x02ee
-#define PCI_DEVICE_ID_INTEL_CMLH 0x06ee
+#define PCI_DEVICE_ID_INTEL_BSW 0x22b7
#define PCI_DEVICE_ID_INTEL_GLK 0x31aa
-#define PCI_DEVICE_ID_INTEL_CNPLP 0x9dee
-#define PCI_DEVICE_ID_INTEL_CNPH 0xa36e
-#define PCI_DEVICE_ID_INTEL_CNPV 0xa3b0
#define PCI_DEVICE_ID_INTEL_ICLLP 0x34ee
-#define PCI_DEVICE_ID_INTEL_EHL 0x4b7e
-#define PCI_DEVICE_ID_INTEL_TGPLP 0xa0ee
#define PCI_DEVICE_ID_INTEL_TGPH 0x43ee
-#define PCI_DEVICE_ID_INTEL_JSP 0x4dee
#define PCI_DEVICE_ID_INTEL_ADL 0x460e
-#define PCI_DEVICE_ID_INTEL_ADL_PCH 0x51ee
#define PCI_DEVICE_ID_INTEL_ADLN 0x465e
+#define PCI_DEVICE_ID_INTEL_EHL 0x4b7e
+#define PCI_DEVICE_ID_INTEL_WCL 0x4d7e
+#define PCI_DEVICE_ID_INTEL_JSP 0x4dee
+#define PCI_DEVICE_ID_INTEL_ADL_PCH 0x51ee
#define PCI_DEVICE_ID_INTEL_ADLN_PCH 0x54ee
-#define PCI_DEVICE_ID_INTEL_ADLS 0x7ae1
-#define PCI_DEVICE_ID_INTEL_RPL 0xa70e
+#define PCI_DEVICE_ID_INTEL_APL 0x5aaa
+#define PCI_DEVICE_ID_INTEL_NVLS_PCH 0x6e6f
+#define PCI_DEVICE_ID_INTEL_ARLH_PCH 0x777e
#define PCI_DEVICE_ID_INTEL_RPLS 0x7a61
+#define PCI_DEVICE_ID_INTEL_MTL 0x7e7e
+#define PCI_DEVICE_ID_INTEL_ADLS 0x7ae1
#define PCI_DEVICE_ID_INTEL_MTLM 0x7eb1
#define PCI_DEVICE_ID_INTEL_MTLP 0x7ec1
#define PCI_DEVICE_ID_INTEL_MTLS 0x7f6f
-#define PCI_DEVICE_ID_INTEL_MTL 0x7e7e
-#define PCI_DEVICE_ID_INTEL_ARLH_PCH 0x777e
#define PCI_DEVICE_ID_INTEL_TGL 0x9a15
+#define PCI_DEVICE_ID_INTEL_SPTLP 0x9d30
+#define PCI_DEVICE_ID_INTEL_CNPLP 0x9dee
+#define PCI_DEVICE_ID_INTEL_TGPLP 0xa0ee
+#define PCI_DEVICE_ID_INTEL_SPTH 0xa130
+#define PCI_DEVICE_ID_INTEL_KBP 0xa2b0
+#define PCI_DEVICE_ID_INTEL_CNPH 0xa36e
+#define PCI_DEVICE_ID_INTEL_CNPV 0xa3b0
+#define PCI_DEVICE_ID_INTEL_RPL 0xa70e
+#define PCI_DEVICE_ID_INTEL_NVLH 0xd37f
#define PCI_DEVICE_ID_INTEL_PTLH 0xe332
#define PCI_DEVICE_ID_INTEL_PTLH_PCH 0xe37e
#define PCI_DEVICE_ID_INTEL_PTLU 0xe432
@@ -148,11 +151,21 @@ static const struct property_entry dwc3_pci_intel_byt_properties[] = {
{}
};
+/*
+ * Intel Merrifield SoC uses these endpoints for tracing and they cannot
+ * be re-allocated if being used because the side band flow control signals
+ * are hard wired to certain endpoints:
+ * - 1 High BW Bulk IN (IN#1) (RTIT)
+ * - 1 1KB BW Bulk IN (IN#8) + 1 1KB BW Bulk OUT (Run Control) (OUT#8)
+ */
+static const u8 dwc3_pci_mrfld_reserved_endpoints[] = { 3, 16, 17 };
+
static const struct property_entry dwc3_pci_mrfld_properties[] = {
PROPERTY_ENTRY_STRING("dr_mode", "otg"),
PROPERTY_ENTRY_STRING("linux,extcon-name", "mrfld_bcove_pwrsrc"),
PROPERTY_ENTRY_BOOL("snps,dis_u3_susphy_quirk"),
PROPERTY_ENTRY_BOOL("snps,dis_u2_susphy_quirk"),
+ PROPERTY_ENTRY_U8_ARRAY("snps,reserved-endpoints", dwc3_pci_mrfld_reserved_endpoints),
PROPERTY_ENTRY_BOOL("snps,usb2-gadget-lpm-disable"),
PROPERTY_ENTRY_BOOL("linux,sysdev_is_parent"),
{}
@@ -311,7 +324,6 @@ static void dwc3_pci_resume_work(struct work_struct *work)
return;
}
- pm_runtime_mark_last_busy(&dwc3->dev);
pm_runtime_put_sync_autosuspend(&dwc3->dev);
}
#endif
@@ -401,39 +413,42 @@ static void dwc3_pci_remove(struct pci_dev *pci)
}
static const struct pci_device_id dwc3_pci_id_table[] = {
- { PCI_DEVICE_DATA(INTEL, BSW, &dwc3_pci_intel_swnode) },
- { PCI_DEVICE_DATA(INTEL, BYT, &dwc3_pci_intel_byt_swnode) },
- { PCI_DEVICE_DATA(INTEL, MRFLD, &dwc3_pci_intel_mrfld_swnode) },
{ PCI_DEVICE_DATA(INTEL, CMLLP, &dwc3_pci_intel_swnode) },
{ PCI_DEVICE_DATA(INTEL, CMLH, &dwc3_pci_intel_swnode) },
- { PCI_DEVICE_DATA(INTEL, SPTLP, &dwc3_pci_intel_swnode) },
- { PCI_DEVICE_DATA(INTEL, SPTH, &dwc3_pci_intel_swnode) },
{ PCI_DEVICE_DATA(INTEL, BXT, &dwc3_pci_intel_swnode) },
+ { PCI_DEVICE_DATA(INTEL, BYT, &dwc3_pci_intel_byt_swnode) },
+ { PCI_DEVICE_DATA(INTEL, MRFLD, &dwc3_pci_intel_mrfld_swnode) },
{ PCI_DEVICE_DATA(INTEL, BXT_M, &dwc3_pci_intel_swnode) },
- { PCI_DEVICE_DATA(INTEL, APL, &dwc3_pci_intel_swnode) },
- { PCI_DEVICE_DATA(INTEL, KBP, &dwc3_pci_intel_swnode) },
+ { PCI_DEVICE_DATA(INTEL, BSW, &dwc3_pci_intel_swnode) },
{ PCI_DEVICE_DATA(INTEL, GLK, &dwc3_pci_intel_swnode) },
- { PCI_DEVICE_DATA(INTEL, CNPLP, &dwc3_pci_intel_swnode) },
- { PCI_DEVICE_DATA(INTEL, CNPH, &dwc3_pci_intel_swnode) },
- { PCI_DEVICE_DATA(INTEL, CNPV, &dwc3_pci_intel_swnode) },
{ PCI_DEVICE_DATA(INTEL, ICLLP, &dwc3_pci_intel_swnode) },
- { PCI_DEVICE_DATA(INTEL, EHL, &dwc3_pci_intel_swnode) },
- { PCI_DEVICE_DATA(INTEL, TGPLP, &dwc3_pci_intel_swnode) },
{ PCI_DEVICE_DATA(INTEL, TGPH, &dwc3_pci_intel_swnode) },
- { PCI_DEVICE_DATA(INTEL, JSP, &dwc3_pci_intel_swnode) },
{ PCI_DEVICE_DATA(INTEL, ADL, &dwc3_pci_intel_swnode) },
- { PCI_DEVICE_DATA(INTEL, ADL_PCH, &dwc3_pci_intel_swnode) },
{ PCI_DEVICE_DATA(INTEL, ADLN, &dwc3_pci_intel_swnode) },
+ { PCI_DEVICE_DATA(INTEL, EHL, &dwc3_pci_intel_swnode) },
+ { PCI_DEVICE_DATA(INTEL, WCL, &dwc3_pci_intel_swnode) },
+ { PCI_DEVICE_DATA(INTEL, JSP, &dwc3_pci_intel_swnode) },
+ { PCI_DEVICE_DATA(INTEL, ADL_PCH, &dwc3_pci_intel_swnode) },
{ PCI_DEVICE_DATA(INTEL, ADLN_PCH, &dwc3_pci_intel_swnode) },
- { PCI_DEVICE_DATA(INTEL, ADLS, &dwc3_pci_intel_swnode) },
- { PCI_DEVICE_DATA(INTEL, RPL, &dwc3_pci_intel_swnode) },
+ { PCI_DEVICE_DATA(INTEL, APL, &dwc3_pci_intel_swnode) },
+ { PCI_DEVICE_DATA(INTEL, NVLS_PCH, &dwc3_pci_intel_swnode) },
+ { PCI_DEVICE_DATA(INTEL, ARLH_PCH, &dwc3_pci_intel_swnode) },
{ PCI_DEVICE_DATA(INTEL, RPLS, &dwc3_pci_intel_swnode) },
+ { PCI_DEVICE_DATA(INTEL, MTL, &dwc3_pci_intel_swnode) },
+ { PCI_DEVICE_DATA(INTEL, ADLS, &dwc3_pci_intel_swnode) },
{ PCI_DEVICE_DATA(INTEL, MTLM, &dwc3_pci_intel_swnode) },
{ PCI_DEVICE_DATA(INTEL, MTLP, &dwc3_pci_intel_swnode) },
- { PCI_DEVICE_DATA(INTEL, MTL, &dwc3_pci_intel_swnode) },
{ PCI_DEVICE_DATA(INTEL, MTLS, &dwc3_pci_intel_swnode) },
- { PCI_DEVICE_DATA(INTEL, ARLH_PCH, &dwc3_pci_intel_swnode) },
{ PCI_DEVICE_DATA(INTEL, TGL, &dwc3_pci_intel_swnode) },
+ { PCI_DEVICE_DATA(INTEL, SPTLP, &dwc3_pci_intel_swnode) },
+ { PCI_DEVICE_DATA(INTEL, CNPLP, &dwc3_pci_intel_swnode) },
+ { PCI_DEVICE_DATA(INTEL, TGPLP, &dwc3_pci_intel_swnode) },
+ { PCI_DEVICE_DATA(INTEL, SPTH, &dwc3_pci_intel_swnode) },
+ { PCI_DEVICE_DATA(INTEL, KBP, &dwc3_pci_intel_swnode) },
+ { PCI_DEVICE_DATA(INTEL, CNPH, &dwc3_pci_intel_swnode) },
+ { PCI_DEVICE_DATA(INTEL, CNPV, &dwc3_pci_intel_swnode) },
+ { PCI_DEVICE_DATA(INTEL, RPL, &dwc3_pci_intel_swnode) },
+ { PCI_DEVICE_DATA(INTEL, NVLH, &dwc3_pci_intel_swnode) },
{ PCI_DEVICE_DATA(INTEL, PTLH, &dwc3_pci_intel_swnode) },
{ PCI_DEVICE_DATA(INTEL, PTLH_PCH, &dwc3_pci_intel_swnode) },
{ PCI_DEVICE_DATA(INTEL, PTLU, &dwc3_pci_intel_swnode) },
diff --git a/drivers/usb/dwc3/dwc3-qcom-legacy.c b/drivers/usb/dwc3/dwc3-qcom-legacy.c
new file mode 100644
index 000000000000..d3fad0fcfdac
--- /dev/null
+++ b/drivers/usb/dwc3/dwc3-qcom-legacy.c
@@ -0,0 +1,935 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * Inspired by dwc3-of-simple.c
+ */
+
+#include <linux/cleanup.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/clk.h>
+#include <linux/irq.h>
+#include <linux/of_clk.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/extcon.h>
+#include <linux/interconnect.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/phy/phy.h>
+#include <linux/usb/of.h>
+#include <linux/reset.h>
+#include <linux/iopoll.h>
+#include <linux/usb/hcd.h>
+#include <linux/usb.h>
+#include "core.h"
+
+/* USB QSCRATCH Hardware registers */
+#define QSCRATCH_HS_PHY_CTRL 0x10
+#define UTMI_OTG_VBUS_VALID BIT(20)
+#define SW_SESSVLD_SEL BIT(28)
+
+#define QSCRATCH_SS_PHY_CTRL 0x30
+#define LANE0_PWR_PRESENT BIT(24)
+
+#define QSCRATCH_GENERAL_CFG 0x08
+#define PIPE_UTMI_CLK_SEL BIT(0)
+#define PIPE3_PHYSTATUS_SW BIT(3)
+#define PIPE_UTMI_CLK_DIS BIT(8)
+
+#define PWR_EVNT_LPM_IN_L2_MASK BIT(4)
+#define PWR_EVNT_LPM_OUT_L2_MASK BIT(5)
+
+#define SDM845_QSCRATCH_BASE_OFFSET 0xf8800
+#define SDM845_QSCRATCH_SIZE 0x400
+#define SDM845_DWC3_CORE_SIZE 0xcd00
+
+/* Interconnect path bandwidths in MBps */
+#define USB_MEMORY_AVG_HS_BW MBps_to_icc(240)
+#define USB_MEMORY_PEAK_HS_BW MBps_to_icc(700)
+#define USB_MEMORY_AVG_SS_BW MBps_to_icc(1000)
+#define USB_MEMORY_PEAK_SS_BW MBps_to_icc(2500)
+#define APPS_USB_AVG_BW 0
+#define APPS_USB_PEAK_BW MBps_to_icc(40)
+
+/* Qualcomm SoCs with multiport support has up to 4 ports */
+#define DWC3_QCOM_MAX_PORTS 4
+
+static const u32 pwr_evnt_irq_stat_reg[DWC3_QCOM_MAX_PORTS] = {
+ 0x58,
+ 0x1dc,
+ 0x228,
+ 0x238,
+};
+
+struct dwc3_qcom_port {
+ int qusb2_phy_irq;
+ int dp_hs_phy_irq;
+ int dm_hs_phy_irq;
+ int ss_phy_irq;
+ enum usb_device_speed usb2_speed;
+};
+
+struct dwc3_qcom {
+ struct device *dev;
+ void __iomem *qscratch_base;
+ struct platform_device *dwc3;
+ struct clk **clks;
+ int num_clocks;
+ struct reset_control *resets;
+ struct dwc3_qcom_port ports[DWC3_QCOM_MAX_PORTS];
+ u8 num_ports;
+
+ struct extcon_dev *edev;
+ struct extcon_dev *host_edev;
+ struct notifier_block vbus_nb;
+ struct notifier_block host_nb;
+
+ enum usb_dr_mode mode;
+ bool is_suspended;
+ bool pm_suspended;
+ struct icc_path *icc_path_ddr;
+ struct icc_path *icc_path_apps;
+};
+
+static inline void dwc3_qcom_setbits(void __iomem *base, u32 offset, u32 val)
+{
+ u32 reg;
+
+ reg = readl(base + offset);
+ reg |= val;
+ writel(reg, base + offset);
+
+ /* ensure that above write is through */
+ readl(base + offset);
+}
+
+static inline void dwc3_qcom_clrbits(void __iomem *base, u32 offset, u32 val)
+{
+ u32 reg;
+
+ reg = readl(base + offset);
+ reg &= ~val;
+ writel(reg, base + offset);
+
+ /* ensure that above write is through */
+ readl(base + offset);
+}
+
+static void dwc3_qcom_vbus_override_enable(struct dwc3_qcom *qcom, bool enable)
+{
+ if (enable) {
+ dwc3_qcom_setbits(qcom->qscratch_base, QSCRATCH_SS_PHY_CTRL,
+ LANE0_PWR_PRESENT);
+ dwc3_qcom_setbits(qcom->qscratch_base, QSCRATCH_HS_PHY_CTRL,
+ UTMI_OTG_VBUS_VALID | SW_SESSVLD_SEL);
+ } else {
+ dwc3_qcom_clrbits(qcom->qscratch_base, QSCRATCH_SS_PHY_CTRL,
+ LANE0_PWR_PRESENT);
+ dwc3_qcom_clrbits(qcom->qscratch_base, QSCRATCH_HS_PHY_CTRL,
+ UTMI_OTG_VBUS_VALID | SW_SESSVLD_SEL);
+ }
+}
+
+static int dwc3_qcom_vbus_notifier(struct notifier_block *nb,
+ unsigned long event, void *ptr)
+{
+ struct dwc3_qcom *qcom = container_of(nb, struct dwc3_qcom, vbus_nb);
+
+ /* enable vbus override for device mode */
+ dwc3_qcom_vbus_override_enable(qcom, event);
+ qcom->mode = event ? USB_DR_MODE_PERIPHERAL : USB_DR_MODE_HOST;
+
+ return NOTIFY_DONE;
+}
+
+static int dwc3_qcom_host_notifier(struct notifier_block *nb,
+ unsigned long event, void *ptr)
+{
+ struct dwc3_qcom *qcom = container_of(nb, struct dwc3_qcom, host_nb);
+
+ /* disable vbus override in host mode */
+ dwc3_qcom_vbus_override_enable(qcom, !event);
+ qcom->mode = event ? USB_DR_MODE_HOST : USB_DR_MODE_PERIPHERAL;
+
+ return NOTIFY_DONE;
+}
+
+static int dwc3_qcom_register_extcon(struct dwc3_qcom *qcom)
+{
+ struct device *dev = qcom->dev;
+ struct extcon_dev *host_edev;
+ int ret;
+
+ if (!of_property_present(dev->of_node, "extcon"))
+ return 0;
+
+ qcom->edev = extcon_get_edev_by_phandle(dev, 0);
+ if (IS_ERR(qcom->edev))
+ return dev_err_probe(dev, PTR_ERR(qcom->edev),
+ "Failed to get extcon\n");
+
+ qcom->vbus_nb.notifier_call = dwc3_qcom_vbus_notifier;
+
+ qcom->host_edev = extcon_get_edev_by_phandle(dev, 1);
+ if (IS_ERR(qcom->host_edev))
+ qcom->host_edev = NULL;
+
+ ret = devm_extcon_register_notifier(dev, qcom->edev, EXTCON_USB,
+ &qcom->vbus_nb);
+ if (ret < 0) {
+ dev_err(dev, "VBUS notifier register failed\n");
+ return ret;
+ }
+
+ if (qcom->host_edev)
+ host_edev = qcom->host_edev;
+ else
+ host_edev = qcom->edev;
+
+ qcom->host_nb.notifier_call = dwc3_qcom_host_notifier;
+ ret = devm_extcon_register_notifier(dev, host_edev, EXTCON_USB_HOST,
+ &qcom->host_nb);
+ if (ret < 0) {
+ dev_err(dev, "Host notifier register failed\n");
+ return ret;
+ }
+
+ /* Update initial VBUS override based on extcon state */
+ if (extcon_get_state(qcom->edev, EXTCON_USB) ||
+ !extcon_get_state(host_edev, EXTCON_USB_HOST))
+ dwc3_qcom_vbus_notifier(&qcom->vbus_nb, true, qcom->edev);
+ else
+ dwc3_qcom_vbus_notifier(&qcom->vbus_nb, false, qcom->edev);
+
+ return 0;
+}
+
+static int dwc3_qcom_interconnect_enable(struct dwc3_qcom *qcom)
+{
+ int ret;
+
+ ret = icc_enable(qcom->icc_path_ddr);
+ if (ret)
+ return ret;
+
+ ret = icc_enable(qcom->icc_path_apps);
+ if (ret)
+ icc_disable(qcom->icc_path_ddr);
+
+ return ret;
+}
+
+static int dwc3_qcom_interconnect_disable(struct dwc3_qcom *qcom)
+{
+ int ret;
+
+ ret = icc_disable(qcom->icc_path_ddr);
+ if (ret)
+ return ret;
+
+ ret = icc_disable(qcom->icc_path_apps);
+ if (ret)
+ icc_enable(qcom->icc_path_ddr);
+
+ return ret;
+}
+
+/**
+ * dwc3_qcom_interconnect_init() - Get interconnect path handles
+ * and set bandwidth.
+ * @qcom: Pointer to the concerned usb core.
+ *
+ */
+static int dwc3_qcom_interconnect_init(struct dwc3_qcom *qcom)
+{
+ enum usb_device_speed max_speed;
+ struct device *dev = qcom->dev;
+ int ret;
+
+ qcom->icc_path_ddr = of_icc_get(dev, "usb-ddr");
+ if (IS_ERR(qcom->icc_path_ddr)) {
+ return dev_err_probe(dev, PTR_ERR(qcom->icc_path_ddr),
+ "failed to get usb-ddr path\n");
+ }
+
+ qcom->icc_path_apps = of_icc_get(dev, "apps-usb");
+ if (IS_ERR(qcom->icc_path_apps)) {
+ ret = dev_err_probe(dev, PTR_ERR(qcom->icc_path_apps),
+ "failed to get apps-usb path\n");
+ goto put_path_ddr;
+ }
+
+ max_speed = usb_get_maximum_speed(&qcom->dwc3->dev);
+ if (max_speed >= USB_SPEED_SUPER || max_speed == USB_SPEED_UNKNOWN) {
+ ret = icc_set_bw(qcom->icc_path_ddr,
+ USB_MEMORY_AVG_SS_BW, USB_MEMORY_PEAK_SS_BW);
+ } else {
+ ret = icc_set_bw(qcom->icc_path_ddr,
+ USB_MEMORY_AVG_HS_BW, USB_MEMORY_PEAK_HS_BW);
+ }
+ if (ret) {
+ dev_err(dev, "failed to set bandwidth for usb-ddr path: %d\n", ret);
+ goto put_path_apps;
+ }
+
+ ret = icc_set_bw(qcom->icc_path_apps, APPS_USB_AVG_BW, APPS_USB_PEAK_BW);
+ if (ret) {
+ dev_err(dev, "failed to set bandwidth for apps-usb path: %d\n", ret);
+ goto put_path_apps;
+ }
+
+ return 0;
+
+put_path_apps:
+ icc_put(qcom->icc_path_apps);
+put_path_ddr:
+ icc_put(qcom->icc_path_ddr);
+ return ret;
+}
+
+/**
+ * dwc3_qcom_interconnect_exit() - Release interconnect path handles
+ * @qcom: Pointer to the concerned usb core.
+ *
+ * This function is used to release interconnect path handle.
+ */
+static void dwc3_qcom_interconnect_exit(struct dwc3_qcom *qcom)
+{
+ icc_put(qcom->icc_path_ddr);
+ icc_put(qcom->icc_path_apps);
+}
+
+/* Only usable in contexts where the role can not change. */
+static bool dwc3_qcom_is_host(struct dwc3_qcom *qcom)
+{
+ struct dwc3 *dwc;
+
+ /*
+ * FIXME: Fix this layering violation.
+ */
+ dwc = platform_get_drvdata(qcom->dwc3);
+
+ /* Core driver may not have probed yet. */
+ if (!dwc)
+ return false;
+
+ return dwc->xhci;
+}
+
+static enum usb_device_speed dwc3_qcom_read_usb2_speed(struct dwc3_qcom *qcom, int port_index)
+{
+ struct dwc3 *dwc = platform_get_drvdata(qcom->dwc3);
+ struct usb_device *udev;
+ struct usb_hcd __maybe_unused *hcd;
+
+ /*
+ * FIXME: Fix this layering violation.
+ */
+ hcd = platform_get_drvdata(dwc->xhci);
+
+#ifdef CONFIG_USB
+ udev = usb_hub_find_child(hcd->self.root_hub, port_index + 1);
+#else
+ udev = NULL;
+#endif
+ if (!udev)
+ return USB_SPEED_UNKNOWN;
+
+ return udev->speed;
+}
+
+static void dwc3_qcom_enable_wakeup_irq(int irq, unsigned int polarity)
+{
+ if (!irq)
+ return;
+
+ if (polarity)
+ irq_set_irq_type(irq, polarity);
+
+ enable_irq(irq);
+ enable_irq_wake(irq);
+}
+
+static void dwc3_qcom_disable_wakeup_irq(int irq)
+{
+ if (!irq)
+ return;
+
+ disable_irq_wake(irq);
+ disable_irq_nosync(irq);
+}
+
+static void dwc3_qcom_disable_port_interrupts(struct dwc3_qcom_port *port)
+{
+ dwc3_qcom_disable_wakeup_irq(port->qusb2_phy_irq);
+
+ if (port->usb2_speed == USB_SPEED_LOW) {
+ dwc3_qcom_disable_wakeup_irq(port->dm_hs_phy_irq);
+ } else if ((port->usb2_speed == USB_SPEED_HIGH) ||
+ (port->usb2_speed == USB_SPEED_FULL)) {
+ dwc3_qcom_disable_wakeup_irq(port->dp_hs_phy_irq);
+ } else {
+ dwc3_qcom_disable_wakeup_irq(port->dp_hs_phy_irq);
+ dwc3_qcom_disable_wakeup_irq(port->dm_hs_phy_irq);
+ }
+
+ dwc3_qcom_disable_wakeup_irq(port->ss_phy_irq);
+}
+
+static void dwc3_qcom_enable_port_interrupts(struct dwc3_qcom_port *port)
+{
+ dwc3_qcom_enable_wakeup_irq(port->qusb2_phy_irq, 0);
+
+ /*
+ * Configure DP/DM line interrupts based on the USB2 device attached to
+ * the root hub port. When HS/FS device is connected, configure the DP line
+ * as falling edge to detect both disconnect and remote wakeup scenarios. When
+ * LS device is connected, configure DM line as falling edge to detect both
+ * disconnect and remote wakeup. When no device is connected, configure both
+ * DP and DM lines as rising edge to detect HS/HS/LS device connect scenario.
+ */
+
+ if (port->usb2_speed == USB_SPEED_LOW) {
+ dwc3_qcom_enable_wakeup_irq(port->dm_hs_phy_irq,
+ IRQ_TYPE_EDGE_FALLING);
+ } else if ((port->usb2_speed == USB_SPEED_HIGH) ||
+ (port->usb2_speed == USB_SPEED_FULL)) {
+ dwc3_qcom_enable_wakeup_irq(port->dp_hs_phy_irq,
+ IRQ_TYPE_EDGE_FALLING);
+ } else {
+ dwc3_qcom_enable_wakeup_irq(port->dp_hs_phy_irq,
+ IRQ_TYPE_EDGE_RISING);
+ dwc3_qcom_enable_wakeup_irq(port->dm_hs_phy_irq,
+ IRQ_TYPE_EDGE_RISING);
+ }
+
+ dwc3_qcom_enable_wakeup_irq(port->ss_phy_irq, 0);
+}
+
+static void dwc3_qcom_disable_interrupts(struct dwc3_qcom *qcom)
+{
+ int i;
+
+ for (i = 0; i < qcom->num_ports; i++)
+ dwc3_qcom_disable_port_interrupts(&qcom->ports[i]);
+}
+
+static void dwc3_qcom_enable_interrupts(struct dwc3_qcom *qcom)
+{
+ int i;
+
+ for (i = 0; i < qcom->num_ports; i++)
+ dwc3_qcom_enable_port_interrupts(&qcom->ports[i]);
+}
+
+static int dwc3_qcom_suspend(struct dwc3_qcom *qcom, bool wakeup)
+{
+ u32 val;
+ int i, ret;
+
+ if (qcom->is_suspended)
+ return 0;
+
+ for (i = 0; i < qcom->num_ports; i++) {
+ val = readl(qcom->qscratch_base + pwr_evnt_irq_stat_reg[i]);
+ if (!(val & PWR_EVNT_LPM_IN_L2_MASK))
+ dev_err(qcom->dev, "port-%d HS-PHY not in L2\n", i + 1);
+ }
+
+ for (i = qcom->num_clocks - 1; i >= 0; i--)
+ clk_disable_unprepare(qcom->clks[i]);
+
+ ret = dwc3_qcom_interconnect_disable(qcom);
+ if (ret)
+ dev_warn(qcom->dev, "failed to disable interconnect: %d\n", ret);
+
+ /*
+ * The role is stable during suspend as role switching is done from a
+ * freezable workqueue.
+ */
+ if (dwc3_qcom_is_host(qcom) && wakeup) {
+ for (i = 0; i < qcom->num_ports; i++)
+ qcom->ports[i].usb2_speed = dwc3_qcom_read_usb2_speed(qcom, i);
+ dwc3_qcom_enable_interrupts(qcom);
+ }
+
+ qcom->is_suspended = true;
+
+ return 0;
+}
+
+static int dwc3_qcom_resume(struct dwc3_qcom *qcom, bool wakeup)
+{
+ int ret;
+ int i;
+
+ if (!qcom->is_suspended)
+ return 0;
+
+ if (dwc3_qcom_is_host(qcom) && wakeup)
+ dwc3_qcom_disable_interrupts(qcom);
+
+ for (i = 0; i < qcom->num_clocks; i++) {
+ ret = clk_prepare_enable(qcom->clks[i]);
+ if (ret < 0) {
+ while (--i >= 0)
+ clk_disable_unprepare(qcom->clks[i]);
+ return ret;
+ }
+ }
+
+ ret = dwc3_qcom_interconnect_enable(qcom);
+ if (ret)
+ dev_warn(qcom->dev, "failed to enable interconnect: %d\n", ret);
+
+ /* Clear existing events from PHY related to L2 in/out */
+ for (i = 0; i < qcom->num_ports; i++) {
+ dwc3_qcom_setbits(qcom->qscratch_base,
+ pwr_evnt_irq_stat_reg[i],
+ PWR_EVNT_LPM_IN_L2_MASK | PWR_EVNT_LPM_OUT_L2_MASK);
+ }
+
+ qcom->is_suspended = false;
+
+ return 0;
+}
+
+static irqreturn_t qcom_dwc3_resume_irq(int irq, void *data)
+{
+ struct dwc3_qcom *qcom = data;
+ struct dwc3 *dwc = platform_get_drvdata(qcom->dwc3);
+
+ /* If pm_suspended then let pm_resume take care of resuming h/w */
+ if (qcom->pm_suspended)
+ return IRQ_HANDLED;
+
+ /*
+ * This is safe as role switching is done from a freezable workqueue
+ * and the wakeup interrupts are disabled as part of resume.
+ */
+ if (dwc3_qcom_is_host(qcom))
+ pm_runtime_resume(&dwc->xhci->dev);
+
+ return IRQ_HANDLED;
+}
+
+static void dwc3_qcom_select_utmi_clk(struct dwc3_qcom *qcom)
+{
+ /* Configure dwc3 to use UTMI clock as PIPE clock not present */
+ dwc3_qcom_setbits(qcom->qscratch_base, QSCRATCH_GENERAL_CFG,
+ PIPE_UTMI_CLK_DIS);
+
+ usleep_range(100, 1000);
+
+ dwc3_qcom_setbits(qcom->qscratch_base, QSCRATCH_GENERAL_CFG,
+ PIPE_UTMI_CLK_SEL | PIPE3_PHYSTATUS_SW);
+
+ usleep_range(100, 1000);
+
+ dwc3_qcom_clrbits(qcom->qscratch_base, QSCRATCH_GENERAL_CFG,
+ PIPE_UTMI_CLK_DIS);
+}
+
+static int dwc3_qcom_request_irq(struct dwc3_qcom *qcom, int irq,
+ const char *name)
+{
+ int ret;
+
+ /* Keep wakeup interrupts disabled until suspend */
+ ret = devm_request_threaded_irq(qcom->dev, irq, NULL,
+ qcom_dwc3_resume_irq,
+ IRQF_ONESHOT | IRQF_NO_AUTOEN,
+ name, qcom);
+ if (ret)
+ dev_err(qcom->dev, "failed to request irq %s: %d\n", name, ret);
+
+ return ret;
+}
+
+static int dwc3_qcom_setup_port_irq(struct platform_device *pdev, int port_index, bool is_multiport)
+{
+ struct dwc3_qcom *qcom = platform_get_drvdata(pdev);
+ const char *irq_name;
+ int irq;
+ int ret;
+
+ if (is_multiport)
+ irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "dp_hs_phy_%d", port_index + 1);
+ else
+ irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "dp_hs_phy_irq");
+ if (!irq_name)
+ return -ENOMEM;
+
+ irq = platform_get_irq_byname_optional(pdev, irq_name);
+ if (irq > 0) {
+ ret = dwc3_qcom_request_irq(qcom, irq, irq_name);
+ if (ret)
+ return ret;
+ qcom->ports[port_index].dp_hs_phy_irq = irq;
+ }
+
+ if (is_multiport)
+ irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "dm_hs_phy_%d", port_index + 1);
+ else
+ irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "dm_hs_phy_irq");
+ if (!irq_name)
+ return -ENOMEM;
+
+ irq = platform_get_irq_byname_optional(pdev, irq_name);
+ if (irq > 0) {
+ ret = dwc3_qcom_request_irq(qcom, irq, irq_name);
+ if (ret)
+ return ret;
+ qcom->ports[port_index].dm_hs_phy_irq = irq;
+ }
+
+ if (is_multiport)
+ irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "ss_phy_%d", port_index + 1);
+ else
+ irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "ss_phy_irq");
+ if (!irq_name)
+ return -ENOMEM;
+
+ irq = platform_get_irq_byname_optional(pdev, irq_name);
+ if (irq > 0) {
+ ret = dwc3_qcom_request_irq(qcom, irq, irq_name);
+ if (ret)
+ return ret;
+ qcom->ports[port_index].ss_phy_irq = irq;
+ }
+
+ if (is_multiport)
+ return 0;
+
+ irq = platform_get_irq_byname_optional(pdev, "qusb2_phy");
+ if (irq > 0) {
+ ret = dwc3_qcom_request_irq(qcom, irq, "qusb2_phy");
+ if (ret)
+ return ret;
+ qcom->ports[port_index].qusb2_phy_irq = irq;
+ }
+
+ return 0;
+}
+
+static int dwc3_qcom_find_num_ports(struct platform_device *pdev)
+{
+ char irq_name[14];
+ int port_num;
+ int irq;
+
+ irq = platform_get_irq_byname_optional(pdev, "dp_hs_phy_1");
+ if (irq <= 0)
+ return 1;
+
+ for (port_num = 2; port_num <= DWC3_QCOM_MAX_PORTS; port_num++) {
+ sprintf(irq_name, "dp_hs_phy_%d", port_num);
+
+ irq = platform_get_irq_byname_optional(pdev, irq_name);
+ if (irq <= 0)
+ return port_num - 1;
+ }
+
+ return DWC3_QCOM_MAX_PORTS;
+}
+
+static int dwc3_qcom_setup_irq(struct platform_device *pdev)
+{
+ struct dwc3_qcom *qcom = platform_get_drvdata(pdev);
+ bool is_multiport;
+ int ret;
+ int i;
+
+ qcom->num_ports = dwc3_qcom_find_num_ports(pdev);
+ is_multiport = (qcom->num_ports > 1);
+
+ for (i = 0; i < qcom->num_ports; i++) {
+ ret = dwc3_qcom_setup_port_irq(pdev, i, is_multiport);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int dwc3_qcom_clk_init(struct dwc3_qcom *qcom, int count)
+{
+ struct device *dev = qcom->dev;
+ struct device_node *np = dev->of_node;
+ int i;
+
+ if (!np || !count)
+ return 0;
+
+ if (count < 0)
+ return count;
+
+ qcom->num_clocks = count;
+
+ qcom->clks = devm_kcalloc(dev, qcom->num_clocks,
+ sizeof(struct clk *), GFP_KERNEL);
+ if (!qcom->clks)
+ return -ENOMEM;
+
+ for (i = 0; i < qcom->num_clocks; i++) {
+ struct clk *clk;
+ int ret;
+
+ clk = of_clk_get(np, i);
+ if (IS_ERR(clk)) {
+ while (--i >= 0)
+ clk_put(qcom->clks[i]);
+ return PTR_ERR(clk);
+ }
+
+ ret = clk_prepare_enable(clk);
+ if (ret < 0) {
+ while (--i >= 0) {
+ clk_disable_unprepare(qcom->clks[i]);
+ clk_put(qcom->clks[i]);
+ }
+ clk_put(clk);
+
+ return ret;
+ }
+
+ qcom->clks[i] = clk;
+ }
+
+ return 0;
+}
+
+static int dwc3_qcom_of_register_core(struct platform_device *pdev)
+{
+ struct dwc3_qcom *qcom = platform_get_drvdata(pdev);
+ struct device_node *np = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ struct device_node *dwc3_np __free(device_node) = of_get_compatible_child(np,
+ "snps,dwc3");
+ if (!dwc3_np) {
+ dev_err(dev, "failed to find dwc3 core child\n");
+ return -ENODEV;
+ }
+
+ ret = of_platform_populate(np, NULL, NULL, dev);
+ if (ret) {
+ dev_err(dev, "failed to register dwc3 core - %d\n", ret);
+ return ret;
+ }
+
+ qcom->dwc3 = of_find_device_by_node(dwc3_np);
+ if (!qcom->dwc3) {
+ ret = -ENODEV;
+ dev_err(dev, "failed to get dwc3 platform device\n");
+ of_platform_depopulate(dev);
+ }
+
+ return ret;
+}
+
+static int dwc3_qcom_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
+ struct dwc3_qcom *qcom;
+ int ret, i;
+ bool ignore_pipe_clk;
+ bool wakeup_source;
+
+ qcom = devm_kzalloc(&pdev->dev, sizeof(*qcom), GFP_KERNEL);
+ if (!qcom)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, qcom);
+ qcom->dev = &pdev->dev;
+
+ qcom->resets = devm_reset_control_array_get_optional_exclusive(dev);
+ if (IS_ERR(qcom->resets)) {
+ return dev_err_probe(&pdev->dev, PTR_ERR(qcom->resets),
+ "failed to get resets\n");
+ }
+
+ ret = reset_control_assert(qcom->resets);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to assert resets, err=%d\n", ret);
+ return ret;
+ }
+
+ usleep_range(10, 1000);
+
+ ret = reset_control_deassert(qcom->resets);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to deassert resets, err=%d\n", ret);
+ goto reset_assert;
+ }
+
+ ret = dwc3_qcom_clk_init(qcom, of_clk_get_parent_count(np));
+ if (ret) {
+ dev_err_probe(dev, ret, "failed to get clocks\n");
+ goto reset_assert;
+ }
+
+ qcom->qscratch_base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(qcom->qscratch_base)) {
+ ret = PTR_ERR(qcom->qscratch_base);
+ goto clk_disable;
+ }
+
+ ret = dwc3_qcom_setup_irq(pdev);
+ if (ret) {
+ dev_err(dev, "failed to setup IRQs, err=%d\n", ret);
+ goto clk_disable;
+ }
+
+ /*
+ * Disable pipe_clk requirement if specified. Used when dwc3
+ * operates without SSPHY and only HS/FS/LS modes are supported.
+ */
+ ignore_pipe_clk = device_property_read_bool(dev,
+ "qcom,select-utmi-as-pipe-clk");
+ if (ignore_pipe_clk)
+ dwc3_qcom_select_utmi_clk(qcom);
+
+ ret = dwc3_qcom_of_register_core(pdev);
+ if (ret) {
+ dev_err(dev, "failed to register DWC3 Core, err=%d\n", ret);
+ goto clk_disable;
+ }
+
+ ret = dwc3_qcom_interconnect_init(qcom);
+ if (ret)
+ goto depopulate;
+
+ qcom->mode = usb_get_dr_mode(&qcom->dwc3->dev);
+
+ /* enable vbus override for device mode */
+ if (qcom->mode != USB_DR_MODE_HOST)
+ dwc3_qcom_vbus_override_enable(qcom, true);
+
+ /* register extcon to override sw_vbus on Vbus change later */
+ ret = dwc3_qcom_register_extcon(qcom);
+ if (ret)
+ goto interconnect_exit;
+
+ wakeup_source = of_property_read_bool(dev->of_node, "wakeup-source");
+ device_init_wakeup(&pdev->dev, wakeup_source);
+ device_init_wakeup(&qcom->dwc3->dev, wakeup_source);
+
+ qcom->is_suspended = false;
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ pm_runtime_forbid(dev);
+
+ return 0;
+
+interconnect_exit:
+ dwc3_qcom_interconnect_exit(qcom);
+depopulate:
+ of_platform_depopulate(&pdev->dev);
+ platform_device_put(qcom->dwc3);
+clk_disable:
+ for (i = qcom->num_clocks - 1; i >= 0; i--) {
+ clk_disable_unprepare(qcom->clks[i]);
+ clk_put(qcom->clks[i]);
+ }
+reset_assert:
+ reset_control_assert(qcom->resets);
+
+ return ret;
+}
+
+static void dwc3_qcom_remove(struct platform_device *pdev)
+{
+ struct dwc3_qcom *qcom = platform_get_drvdata(pdev);
+ struct device *dev = &pdev->dev;
+ int i;
+
+ of_platform_depopulate(&pdev->dev);
+ platform_device_put(qcom->dwc3);
+
+ for (i = qcom->num_clocks - 1; i >= 0; i--) {
+ clk_disable_unprepare(qcom->clks[i]);
+ clk_put(qcom->clks[i]);
+ }
+ qcom->num_clocks = 0;
+
+ dwc3_qcom_interconnect_exit(qcom);
+ reset_control_assert(qcom->resets);
+
+ pm_runtime_allow(dev);
+ pm_runtime_disable(dev);
+}
+
+static int __maybe_unused dwc3_qcom_pm_suspend(struct device *dev)
+{
+ struct dwc3_qcom *qcom = dev_get_drvdata(dev);
+ bool wakeup = device_may_wakeup(dev);
+ int ret;
+
+ ret = dwc3_qcom_suspend(qcom, wakeup);
+ if (ret)
+ return ret;
+
+ qcom->pm_suspended = true;
+
+ return 0;
+}
+
+static int __maybe_unused dwc3_qcom_pm_resume(struct device *dev)
+{
+ struct dwc3_qcom *qcom = dev_get_drvdata(dev);
+ bool wakeup = device_may_wakeup(dev);
+ int ret;
+
+ ret = dwc3_qcom_resume(qcom, wakeup);
+ if (ret)
+ return ret;
+
+ qcom->pm_suspended = false;
+
+ return 0;
+}
+
+static int __maybe_unused dwc3_qcom_runtime_suspend(struct device *dev)
+{
+ struct dwc3_qcom *qcom = dev_get_drvdata(dev);
+
+ return dwc3_qcom_suspend(qcom, true);
+}
+
+static int __maybe_unused dwc3_qcom_runtime_resume(struct device *dev)
+{
+ struct dwc3_qcom *qcom = dev_get_drvdata(dev);
+
+ return dwc3_qcom_resume(qcom, true);
+}
+
+static const struct dev_pm_ops dwc3_qcom_dev_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(dwc3_qcom_pm_suspend, dwc3_qcom_pm_resume)
+ SET_RUNTIME_PM_OPS(dwc3_qcom_runtime_suspend, dwc3_qcom_runtime_resume,
+ NULL)
+};
+
+static const struct of_device_id dwc3_qcom_of_match[] = {
+ { .compatible = "qcom,dwc3" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, dwc3_qcom_of_match);
+
+static struct platform_driver dwc3_qcom_driver = {
+ .probe = dwc3_qcom_probe,
+ .remove = dwc3_qcom_remove,
+ .driver = {
+ .name = "dwc3-qcom-legacy",
+ .pm = &dwc3_qcom_dev_pm_ops,
+ .of_match_table = dwc3_qcom_of_match,
+ },
+};
+
+module_platform_driver(dwc3_qcom_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("DesignWare DWC3 QCOM legacy glue Driver");
diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c
index 58683bb672e9..f43f73ac36ff 100644
--- a/drivers/usb/dwc3/dwc3-qcom.c
+++ b/drivers/usb/dwc3/dwc3-qcom.c
@@ -4,7 +4,6 @@
* Inspired by dwc3-of-simple.c
*/
-#include <linux/cleanup.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/clk.h>
@@ -12,9 +11,7 @@
#include <linux/of_clk.h>
#include <linux/module.h>
#include <linux/kernel.h>
-#include <linux/extcon.h>
#include <linux/interconnect.h>
-#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/phy/phy.h>
#include <linux/usb/of.h>
@@ -23,6 +20,7 @@
#include <linux/usb/hcd.h>
#include <linux/usb.h>
#include "core.h"
+#include "glue.h"
/* USB QSCRATCH Hardware registers */
#define QSCRATCH_HS_PHY_CTRL 0x10
@@ -73,25 +71,24 @@ struct dwc3_qcom_port {
struct dwc3_qcom {
struct device *dev;
void __iomem *qscratch_base;
- struct platform_device *dwc3;
- struct clk **clks;
+ struct dwc3 dwc;
+ struct clk_bulk_data *clks;
int num_clocks;
struct reset_control *resets;
struct dwc3_qcom_port ports[DWC3_QCOM_MAX_PORTS];
u8 num_ports;
- struct extcon_dev *edev;
- struct extcon_dev *host_edev;
- struct notifier_block vbus_nb;
- struct notifier_block host_nb;
-
enum usb_dr_mode mode;
bool is_suspended;
bool pm_suspended;
struct icc_path *icc_path_ddr;
struct icc_path *icc_path_apps;
+
+ enum usb_role current_role;
};
+#define to_dwc3_qcom(d) container_of((d), struct dwc3_qcom, dwc)
+
static inline void dwc3_qcom_setbits(void __iomem *base, u32 offset, u32 val)
{
u32 reg;
@@ -131,80 +128,6 @@ static void dwc3_qcom_vbus_override_enable(struct dwc3_qcom *qcom, bool enable)
}
}
-static int dwc3_qcom_vbus_notifier(struct notifier_block *nb,
- unsigned long event, void *ptr)
-{
- struct dwc3_qcom *qcom = container_of(nb, struct dwc3_qcom, vbus_nb);
-
- /* enable vbus override for device mode */
- dwc3_qcom_vbus_override_enable(qcom, event);
- qcom->mode = event ? USB_DR_MODE_PERIPHERAL : USB_DR_MODE_HOST;
-
- return NOTIFY_DONE;
-}
-
-static int dwc3_qcom_host_notifier(struct notifier_block *nb,
- unsigned long event, void *ptr)
-{
- struct dwc3_qcom *qcom = container_of(nb, struct dwc3_qcom, host_nb);
-
- /* disable vbus override in host mode */
- dwc3_qcom_vbus_override_enable(qcom, !event);
- qcom->mode = event ? USB_DR_MODE_HOST : USB_DR_MODE_PERIPHERAL;
-
- return NOTIFY_DONE;
-}
-
-static int dwc3_qcom_register_extcon(struct dwc3_qcom *qcom)
-{
- struct device *dev = qcom->dev;
- struct extcon_dev *host_edev;
- int ret;
-
- if (!of_property_present(dev->of_node, "extcon"))
- return 0;
-
- qcom->edev = extcon_get_edev_by_phandle(dev, 0);
- if (IS_ERR(qcom->edev))
- return dev_err_probe(dev, PTR_ERR(qcom->edev),
- "Failed to get extcon\n");
-
- qcom->vbus_nb.notifier_call = dwc3_qcom_vbus_notifier;
-
- qcom->host_edev = extcon_get_edev_by_phandle(dev, 1);
- if (IS_ERR(qcom->host_edev))
- qcom->host_edev = NULL;
-
- ret = devm_extcon_register_notifier(dev, qcom->edev, EXTCON_USB,
- &qcom->vbus_nb);
- if (ret < 0) {
- dev_err(dev, "VBUS notifier register failed\n");
- return ret;
- }
-
- if (qcom->host_edev)
- host_edev = qcom->host_edev;
- else
- host_edev = qcom->edev;
-
- qcom->host_nb.notifier_call = dwc3_qcom_host_notifier;
- ret = devm_extcon_register_notifier(dev, host_edev, EXTCON_USB_HOST,
- &qcom->host_nb);
- if (ret < 0) {
- dev_err(dev, "Host notifier register failed\n");
- return ret;
- }
-
- /* Update initial VBUS override based on extcon state */
- if (extcon_get_state(qcom->edev, EXTCON_USB) ||
- !extcon_get_state(host_edev, EXTCON_USB_HOST))
- dwc3_qcom_vbus_notifier(&qcom->vbus_nb, true, qcom->edev);
- else
- dwc3_qcom_vbus_notifier(&qcom->vbus_nb, false, qcom->edev);
-
- return 0;
-}
-
static int dwc3_qcom_interconnect_enable(struct dwc3_qcom *qcom)
{
int ret;
@@ -260,7 +183,7 @@ static int dwc3_qcom_interconnect_init(struct dwc3_qcom *qcom)
goto put_path_ddr;
}
- max_speed = usb_get_maximum_speed(&qcom->dwc3->dev);
+ max_speed = usb_get_maximum_speed(qcom->dwc.dev);
if (max_speed >= USB_SPEED_SUPER || max_speed == USB_SPEED_UNKNOWN) {
ret = icc_set_bw(qcom->icc_path_ddr,
USB_MEMORY_AVG_SS_BW, USB_MEMORY_PEAK_SS_BW);
@@ -303,25 +226,14 @@ static void dwc3_qcom_interconnect_exit(struct dwc3_qcom *qcom)
/* Only usable in contexts where the role can not change. */
static bool dwc3_qcom_is_host(struct dwc3_qcom *qcom)
{
- struct dwc3 *dwc;
-
- /*
- * FIXME: Fix this layering violation.
- */
- dwc = platform_get_drvdata(qcom->dwc3);
-
- /* Core driver may not have probed yet. */
- if (!dwc)
- return false;
-
- return dwc->xhci;
+ return qcom->dwc.xhci;
}
static enum usb_device_speed dwc3_qcom_read_usb2_speed(struct dwc3_qcom *qcom, int port_index)
{
- struct dwc3 *dwc = platform_get_drvdata(qcom->dwc3);
struct usb_device *udev;
struct usb_hcd __maybe_unused *hcd;
+ struct dwc3 *dwc = &qcom->dwc;
/*
* FIXME: Fix this layering violation.
@@ -436,9 +348,7 @@ static int dwc3_qcom_suspend(struct dwc3_qcom *qcom, bool wakeup)
if (!(val & PWR_EVNT_LPM_IN_L2_MASK))
dev_err(qcom->dev, "port-%d HS-PHY not in L2\n", i + 1);
}
-
- for (i = qcom->num_clocks - 1; i >= 0; i--)
- clk_disable_unprepare(qcom->clks[i]);
+ clk_bulk_disable_unprepare(qcom->num_clocks, qcom->clks);
ret = dwc3_qcom_interconnect_disable(qcom);
if (ret)
@@ -470,14 +380,9 @@ static int dwc3_qcom_resume(struct dwc3_qcom *qcom, bool wakeup)
if (dwc3_qcom_is_host(qcom) && wakeup)
dwc3_qcom_disable_interrupts(qcom);
- for (i = 0; i < qcom->num_clocks; i++) {
- ret = clk_prepare_enable(qcom->clks[i]);
- if (ret < 0) {
- while (--i >= 0)
- clk_disable_unprepare(qcom->clks[i]);
- return ret;
- }
- }
+ ret = clk_bulk_prepare_enable(qcom->num_clocks, qcom->clks);
+ if (ret < 0)
+ return ret;
ret = dwc3_qcom_interconnect_enable(qcom);
if (ret)
@@ -498,7 +403,7 @@ static int dwc3_qcom_resume(struct dwc3_qcom *qcom, bool wakeup)
static irqreturn_t qcom_dwc3_resume_irq(int irq, void *data)
{
struct dwc3_qcom *qcom = data;
- struct dwc3 *dwc = platform_get_drvdata(qcom->dwc3);
+ struct dwc3 *dwc = &qcom->dwc;
/* If pm_suspended then let pm_resume take care of resuming h/w */
if (qcom->pm_suspended)
@@ -547,9 +452,10 @@ static int dwc3_qcom_request_irq(struct dwc3_qcom *qcom, int irq,
return ret;
}
-static int dwc3_qcom_setup_port_irq(struct platform_device *pdev, int port_index, bool is_multiport)
+static int dwc3_qcom_setup_port_irq(struct dwc3_qcom *qcom,
+ struct platform_device *pdev,
+ int port_index, bool is_multiport)
{
- struct dwc3_qcom *qcom = platform_get_drvdata(pdev);
const char *irq_name;
int irq;
int ret;
@@ -620,23 +526,22 @@ static int dwc3_qcom_find_num_ports(struct platform_device *pdev)
int irq;
irq = platform_get_irq_byname_optional(pdev, "dp_hs_phy_1");
- if (irq <= 0)
+ if (irq < 0)
return 1;
for (port_num = 2; port_num <= DWC3_QCOM_MAX_PORTS; port_num++) {
sprintf(irq_name, "dp_hs_phy_%d", port_num);
irq = platform_get_irq_byname_optional(pdev, irq_name);
- if (irq <= 0)
+ if (irq < 0)
return port_num - 1;
}
return DWC3_QCOM_MAX_PORTS;
}
-static int dwc3_qcom_setup_irq(struct platform_device *pdev)
+static int dwc3_qcom_setup_irq(struct dwc3_qcom *qcom, struct platform_device *pdev)
{
- struct dwc3_qcom *qcom = platform_get_drvdata(pdev);
bool is_multiport;
int ret;
int i;
@@ -645,7 +550,7 @@ static int dwc3_qcom_setup_irq(struct platform_device *pdev)
is_multiport = (qcom->num_ports > 1);
for (i = 0; i < qcom->num_ports; i++) {
- ret = dwc3_qcom_setup_port_irq(pdev, i, is_multiport);
+ ret = dwc3_qcom_setup_port_irq(qcom, pdev, i, is_multiport);
if (ret)
return ret;
}
@@ -653,89 +558,63 @@ static int dwc3_qcom_setup_irq(struct platform_device *pdev)
return 0;
}
-static int dwc3_qcom_clk_init(struct dwc3_qcom *qcom, int count)
+static void dwc3_qcom_set_role_notifier(struct dwc3 *dwc, enum usb_role next_role)
{
- struct device *dev = qcom->dev;
- struct device_node *np = dev->of_node;
- int i;
-
- if (!np || !count)
- return 0;
-
- if (count < 0)
- return count;
+ struct dwc3_qcom *qcom = to_dwc3_qcom(dwc);
- qcom->num_clocks = count;
-
- qcom->clks = devm_kcalloc(dev, qcom->num_clocks,
- sizeof(struct clk *), GFP_KERNEL);
- if (!qcom->clks)
- return -ENOMEM;
+ if (qcom->current_role == next_role)
+ return;
- for (i = 0; i < qcom->num_clocks; i++) {
- struct clk *clk;
- int ret;
-
- clk = of_clk_get(np, i);
- if (IS_ERR(clk)) {
- while (--i >= 0)
- clk_put(qcom->clks[i]);
- return PTR_ERR(clk);
- }
-
- ret = clk_prepare_enable(clk);
- if (ret < 0) {
- while (--i >= 0) {
- clk_disable_unprepare(qcom->clks[i]);
- clk_put(qcom->clks[i]);
- }
- clk_put(clk);
+ if (pm_runtime_resume_and_get(qcom->dev)) {
+ dev_dbg(qcom->dev, "Failed to resume device\n");
+ return;
+ }
- return ret;
- }
+ if (qcom->current_role == USB_ROLE_DEVICE)
+ dwc3_qcom_vbus_override_enable(qcom, false);
+ else if (qcom->current_role != USB_ROLE_DEVICE)
+ dwc3_qcom_vbus_override_enable(qcom, true);
- qcom->clks[i] = clk;
- }
+ pm_runtime_mark_last_busy(qcom->dev);
+ pm_runtime_put_sync(qcom->dev);
- return 0;
+ /*
+ * Current role changes via usb_role_switch_set_role callback protected
+ * internally by mutex lock.
+ */
+ qcom->current_role = next_role;
}
-static int dwc3_qcom_of_register_core(struct platform_device *pdev)
+static void dwc3_qcom_run_stop_notifier(struct dwc3 *dwc, bool is_on)
{
- struct dwc3_qcom *qcom = platform_get_drvdata(pdev);
- struct device_node *np = pdev->dev.of_node;
- struct device *dev = &pdev->dev;
- int ret;
+ struct dwc3_qcom *qcom = to_dwc3_qcom(dwc);
- struct device_node *dwc3_np __free(device_node) = of_get_compatible_child(np,
- "snps,dwc3");
- if (!dwc3_np) {
- dev_err(dev, "failed to find dwc3 core child\n");
- return -ENODEV;
- }
-
- ret = of_platform_populate(np, NULL, NULL, dev);
- if (ret) {
- dev_err(dev, "failed to register dwc3 core - %d\n", ret);
- return ret;
- }
-
- qcom->dwc3 = of_find_device_by_node(dwc3_np);
- if (!qcom->dwc3) {
- ret = -ENODEV;
- dev_err(dev, "failed to get dwc3 platform device\n");
- of_platform_depopulate(dev);
- }
+ /*
+ * When autosuspend is enabled and controller goes to suspend
+ * after removing UDC from userspace, the next UDC write needs
+ * setting of QSCRATCH VBUS_VALID to "1" to generate a connect
+ * done event.
+ */
+ if (!is_on)
+ return;
- return ret;
+ dwc3_qcom_vbus_override_enable(qcom, true);
+ pm_runtime_mark_last_busy(qcom->dev);
}
+struct dwc3_glue_ops dwc3_qcom_glue_ops = {
+ .pre_set_role = dwc3_qcom_set_role_notifier,
+ .pre_run_stop = dwc3_qcom_run_stop_notifier,
+};
+
static int dwc3_qcom_probe(struct platform_device *pdev)
{
- struct device_node *np = pdev->dev.of_node;
+ struct dwc3_probe_data probe_data = {};
struct device *dev = &pdev->dev;
struct dwc3_qcom *qcom;
- int ret, i;
+ struct resource res;
+ struct resource *r;
+ int ret;
bool ignore_pipe_clk;
bool wakeup_source;
@@ -743,7 +622,6 @@ static int dwc3_qcom_probe(struct platform_device *pdev)
if (!qcom)
return -ENOMEM;
- platform_set_drvdata(pdev, qcom);
qcom->dev = &pdev->dev;
qcom->resets = devm_reset_control_array_get_optional_exclusive(dev);
@@ -752,6 +630,11 @@ static int dwc3_qcom_probe(struct platform_device *pdev)
"failed to get resets\n");
}
+ ret = devm_clk_bulk_get_all(&pdev->dev, &qcom->clks);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to get clocks\n");
+ qcom->num_clocks = ret;
+
ret = reset_control_assert(qcom->resets);
if (ret) {
dev_err(&pdev->dev, "failed to assert resets, err=%d\n", ret);
@@ -763,22 +646,29 @@ static int dwc3_qcom_probe(struct platform_device *pdev)
ret = reset_control_deassert(qcom->resets);
if (ret) {
dev_err(&pdev->dev, "failed to deassert resets, err=%d\n", ret);
- goto reset_assert;
+ return ret;
}
- ret = dwc3_qcom_clk_init(qcom, of_clk_get_parent_count(np));
- if (ret) {
- dev_err_probe(dev, ret, "failed to get clocks\n");
- goto reset_assert;
+ ret = clk_bulk_prepare_enable(qcom->num_clocks, qcom->clks);
+ if (ret < 0)
+ return ret;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r) {
+ ret = -EINVAL;
+ goto clk_disable;
}
+ res = *r;
+ res.end = res.start + SDM845_QSCRATCH_BASE_OFFSET;
- qcom->qscratch_base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(qcom->qscratch_base)) {
- ret = PTR_ERR(qcom->qscratch_base);
+ qcom->qscratch_base = devm_ioremap(dev, res.end, SDM845_QSCRATCH_SIZE);
+ if (!qcom->qscratch_base) {
+ dev_err(dev, "failed to map qscratch region\n");
+ ret = -ENOMEM;
goto clk_disable;
}
- ret = dwc3_qcom_setup_irq(pdev);
+ ret = dwc3_qcom_setup_irq(qcom, pdev);
if (ret) {
dev_err(dev, "failed to setup IRQs, err=%d\n", ret);
goto clk_disable;
@@ -793,82 +683,79 @@ static int dwc3_qcom_probe(struct platform_device *pdev)
if (ignore_pipe_clk)
dwc3_qcom_select_utmi_clk(qcom);
- ret = dwc3_qcom_of_register_core(pdev);
- if (ret) {
- dev_err(dev, "failed to register DWC3 Core, err=%d\n", ret);
+ qcom->mode = usb_get_dr_mode(dev);
+
+ if (qcom->mode == USB_DR_MODE_HOST) {
+ qcom->current_role = USB_ROLE_HOST;
+ } else if (qcom->mode == USB_DR_MODE_PERIPHERAL) {
+ qcom->current_role = USB_ROLE_DEVICE;
+ dwc3_qcom_vbus_override_enable(qcom, true);
+ } else {
+ if ((device_property_read_bool(dev, "usb-role-switch")) &&
+ (usb_get_role_switch_default_mode(dev) == USB_DR_MODE_HOST))
+ qcom->current_role = USB_ROLE_HOST;
+ else
+ qcom->current_role = USB_ROLE_DEVICE;
+ }
+
+ qcom->dwc.glue_ops = &dwc3_qcom_glue_ops;
+
+ qcom->dwc.dev = dev;
+ probe_data.dwc = &qcom->dwc;
+ probe_data.res = &res;
+ probe_data.ignore_clocks_and_resets = true;
+ probe_data.properties = DWC3_DEFAULT_PROPERTIES;
+ ret = dwc3_core_probe(&probe_data);
+ if (ret) {
+ ret = dev_err_probe(dev, ret, "failed to register DWC3 Core\n");
goto clk_disable;
}
ret = dwc3_qcom_interconnect_init(qcom);
if (ret)
- goto depopulate;
-
- qcom->mode = usb_get_dr_mode(&qcom->dwc3->dev);
-
- /* enable vbus override for device mode */
- if (qcom->mode != USB_DR_MODE_HOST)
- dwc3_qcom_vbus_override_enable(qcom, true);
-
- /* register extcon to override sw_vbus on Vbus change later */
- ret = dwc3_qcom_register_extcon(qcom);
- if (ret)
- goto interconnect_exit;
+ goto remove_core;
wakeup_source = of_property_read_bool(dev->of_node, "wakeup-source");
device_init_wakeup(&pdev->dev, wakeup_source);
- device_init_wakeup(&qcom->dwc3->dev, wakeup_source);
qcom->is_suspended = false;
- pm_runtime_set_active(dev);
- pm_runtime_enable(dev);
- pm_runtime_forbid(dev);
return 0;
-interconnect_exit:
- dwc3_qcom_interconnect_exit(qcom);
-depopulate:
- of_platform_depopulate(&pdev->dev);
- platform_device_put(qcom->dwc3);
+remove_core:
+ dwc3_core_remove(&qcom->dwc);
clk_disable:
- for (i = qcom->num_clocks - 1; i >= 0; i--) {
- clk_disable_unprepare(qcom->clks[i]);
- clk_put(qcom->clks[i]);
- }
-reset_assert:
- reset_control_assert(qcom->resets);
+ clk_bulk_disable_unprepare(qcom->num_clocks, qcom->clks);
return ret;
}
static void dwc3_qcom_remove(struct platform_device *pdev)
{
- struct dwc3_qcom *qcom = platform_get_drvdata(pdev);
- struct device *dev = &pdev->dev;
- int i;
+ struct dwc3 *dwc = platform_get_drvdata(pdev);
+ struct dwc3_qcom *qcom = to_dwc3_qcom(dwc);
- of_platform_depopulate(&pdev->dev);
- platform_device_put(qcom->dwc3);
-
- for (i = qcom->num_clocks - 1; i >= 0; i--) {
- clk_disable_unprepare(qcom->clks[i]);
- clk_put(qcom->clks[i]);
- }
- qcom->num_clocks = 0;
+ if (pm_runtime_resume_and_get(qcom->dev) < 0)
+ return;
+ dwc3_core_remove(&qcom->dwc);
+ clk_bulk_disable_unprepare(qcom->num_clocks, qcom->clks);
dwc3_qcom_interconnect_exit(qcom);
- reset_control_assert(qcom->resets);
- pm_runtime_allow(dev);
- pm_runtime_disable(dev);
+ pm_runtime_put_noidle(qcom->dev);
}
-static int __maybe_unused dwc3_qcom_pm_suspend(struct device *dev)
+static int dwc3_qcom_pm_suspend(struct device *dev)
{
- struct dwc3_qcom *qcom = dev_get_drvdata(dev);
+ struct dwc3 *dwc = dev_get_drvdata(dev);
+ struct dwc3_qcom *qcom = to_dwc3_qcom(dwc);
bool wakeup = device_may_wakeup(dev);
int ret;
+ ret = dwc3_pm_suspend(&qcom->dwc);
+ if (ret)
+ return ret;
+
ret = dwc3_qcom_suspend(qcom, wakeup);
if (ret)
return ret;
@@ -878,9 +765,10 @@ static int __maybe_unused dwc3_qcom_pm_suspend(struct device *dev)
return 0;
}
-static int __maybe_unused dwc3_qcom_pm_resume(struct device *dev)
+static int dwc3_qcom_pm_resume(struct device *dev)
{
- struct dwc3_qcom *qcom = dev_get_drvdata(dev);
+ struct dwc3 *dwc = dev_get_drvdata(dev);
+ struct dwc3_qcom *qcom = to_dwc3_qcom(dwc);
bool wakeup = device_may_wakeup(dev);
int ret;
@@ -890,31 +778,68 @@ static int __maybe_unused dwc3_qcom_pm_resume(struct device *dev)
qcom->pm_suspended = false;
+ ret = dwc3_pm_resume(&qcom->dwc);
+ if (ret)
+ return ret;
+
return 0;
}
-static int __maybe_unused dwc3_qcom_runtime_suspend(struct device *dev)
+static void dwc3_qcom_complete(struct device *dev)
+{
+ struct dwc3 *dwc = dev_get_drvdata(dev);
+
+ dwc3_pm_complete(dwc);
+}
+
+static int dwc3_qcom_prepare(struct device *dev)
{
- struct dwc3_qcom *qcom = dev_get_drvdata(dev);
+ struct dwc3 *dwc = dev_get_drvdata(dev);
+
+ return dwc3_pm_prepare(dwc);
+}
+
+static int dwc3_qcom_runtime_suspend(struct device *dev)
+{
+ struct dwc3 *dwc = dev_get_drvdata(dev);
+ struct dwc3_qcom *qcom = to_dwc3_qcom(dwc);
+ int ret;
+
+ ret = dwc3_runtime_suspend(&qcom->dwc);
+ if (ret)
+ return ret;
return dwc3_qcom_suspend(qcom, true);
}
-static int __maybe_unused dwc3_qcom_runtime_resume(struct device *dev)
+static int dwc3_qcom_runtime_resume(struct device *dev)
{
- struct dwc3_qcom *qcom = dev_get_drvdata(dev);
+ struct dwc3 *dwc = dev_get_drvdata(dev);
+ struct dwc3_qcom *qcom = to_dwc3_qcom(dwc);
+ int ret;
+
+ ret = dwc3_qcom_resume(qcom, true);
+ if (ret)
+ return ret;
- return dwc3_qcom_resume(qcom, true);
+ return dwc3_runtime_resume(&qcom->dwc);
+}
+
+static int dwc3_qcom_runtime_idle(struct device *dev)
+{
+ return dwc3_runtime_idle(dev_get_drvdata(dev));
}
static const struct dev_pm_ops dwc3_qcom_dev_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(dwc3_qcom_pm_suspend, dwc3_qcom_pm_resume)
- SET_RUNTIME_PM_OPS(dwc3_qcom_runtime_suspend, dwc3_qcom_runtime_resume,
- NULL)
+ SYSTEM_SLEEP_PM_OPS(dwc3_qcom_pm_suspend, dwc3_qcom_pm_resume)
+ RUNTIME_PM_OPS(dwc3_qcom_runtime_suspend, dwc3_qcom_runtime_resume,
+ dwc3_qcom_runtime_idle)
+ .complete = pm_sleep_ptr(dwc3_qcom_complete),
+ .prepare = pm_sleep_ptr(dwc3_qcom_prepare),
};
static const struct of_device_id dwc3_qcom_of_match[] = {
- { .compatible = "qcom,dwc3" },
+ { .compatible = "qcom,snps-dwc3" },
{ }
};
MODULE_DEVICE_TABLE(of, dwc3_qcom_of_match);
@@ -922,9 +847,10 @@ MODULE_DEVICE_TABLE(of, dwc3_qcom_of_match);
static struct platform_driver dwc3_qcom_driver = {
.probe = dwc3_qcom_probe,
.remove = dwc3_qcom_remove,
+ .shutdown = dwc3_qcom_remove,
.driver = {
.name = "dwc3-qcom",
- .pm = &dwc3_qcom_dev_pm_ops,
+ .pm = pm_ptr(&dwc3_qcom_dev_pm_ops),
.of_match_table = dwc3_qcom_of_match,
},
};
diff --git a/drivers/usb/dwc3/dwc3-st.c b/drivers/usb/dwc3/dwc3-st.c
index ef7c43008946..5d513decaacd 100644
--- a/drivers/usb/dwc3/dwc3-st.c
+++ b/drivers/usb/dwc3/dwc3-st.c
@@ -225,7 +225,7 @@ static int st_dwc3_probe(struct platform_device *pdev)
dwc3_data->syscfg_reg_off = res->start;
- dev_vdbg(dev, "glue-logic addr 0x%pK, syscfg-reg offset 0x%x\n",
+ dev_vdbg(dev, "glue-logic addr 0x%p, syscfg-reg offset 0x%x\n",
dwc3_data->glue_base, dwc3_data->syscfg_reg_off);
struct device_node *child __free(device_node) = of_get_compatible_child(node,
diff --git a/drivers/usb/dwc3/dwc3-xilinx.c b/drivers/usb/dwc3/dwc3-xilinx.c
index a33a42ba0249..f41b0da5e89d 100644
--- a/drivers/usb/dwc3/dwc3-xilinx.c
+++ b/drivers/usb/dwc3/dwc3-xilinx.c
@@ -32,6 +32,9 @@
#define XLNX_USB_TRAFFIC_ROUTE_CONFIG 0x005C
#define XLNX_USB_TRAFFIC_ROUTE_FPD 0x1
+/* USB 2.0 IP Register */
+#define XLNX_USB2_TRAFFIC_ROUTE_CONFIG 0x0044
+
#define XLNX_USB_FPD_PIPE_CLK 0x7c
#define PIPE_CLK_DESELECT 1
#define PIPE_CLK_SELECT 0
@@ -66,6 +69,23 @@ static void dwc3_xlnx_mask_phy_rst(struct dwc3_xlnx *priv_data, bool mask)
writel(reg, priv_data->regs + XLNX_USB_PHY_RST_EN);
}
+static void dwc3_xlnx_set_coherency(struct dwc3_xlnx *priv_data, u32 coherency_offset)
+{
+ struct device *dev = priv_data->dev;
+ u32 reg;
+
+ /*
+ * This routes the USB DMA traffic to go through FPD path instead
+ * of reaching DDR directly. This traffic routing is needed to
+ * make SMMU and CCI work with USB DMA.
+ */
+ if (of_dma_is_coherent(dev->of_node) || device_iommu_mapped(dev)) {
+ reg = readl(priv_data->regs + coherency_offset);
+ reg |= XLNX_USB_TRAFFIC_ROUTE_FPD;
+ writel(reg, priv_data->regs + coherency_offset);
+ }
+}
+
static int dwc3_xlnx_init_versal(struct dwc3_xlnx *priv_data)
{
struct device *dev = priv_data->dev;
@@ -92,6 +112,7 @@ static int dwc3_xlnx_init_versal(struct dwc3_xlnx *priv_data)
}
dwc3_xlnx_mask_phy_rst(priv_data, true);
+ dwc3_xlnx_set_coherency(priv_data, XLNX_USB2_TRAFFIC_ROUTE_CONFIG);
return 0;
}
@@ -102,7 +123,6 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data)
struct reset_control *crst, *hibrst, *apbrst;
struct gpio_desc *reset_gpio;
int ret = 0;
- u32 reg;
priv_data->usb3_phy = devm_phy_optional_get(dev, "usb3-phy");
if (IS_ERR(priv_data->usb3_phy)) {
@@ -112,21 +132,6 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data)
goto err;
}
- /*
- * The following core resets are not required unless a USB3 PHY
- * is used, and the subsequent register settings are not required
- * unless a core reset is performed (they should be set properly
- * by the first-stage boot loader, but may be reverted by a core
- * reset). They may also break the configuration if USB3 is actually
- * in use but the usb3-phy entry is missing from the device tree.
- * Therefore, skip these operations in this case.
- */
- if (!priv_data->usb3_phy) {
- /* Deselect the PIPE Clock Select bit in FPD PIPE Clock register */
- writel(PIPE_CLK_DESELECT, priv_data->regs + XLNX_USB_FPD_PIPE_CLK);
- goto skip_usb3_phy;
- }
-
crst = devm_reset_control_get_exclusive(dev, "usb_crst");
if (IS_ERR(crst)) {
ret = PTR_ERR(crst);
@@ -151,22 +156,31 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data)
goto err;
}
- ret = reset_control_assert(crst);
- if (ret < 0) {
- dev_err(dev, "Failed to assert core reset\n");
- goto err;
- }
-
- ret = reset_control_assert(hibrst);
- if (ret < 0) {
- dev_err(dev, "Failed to assert hibernation reset\n");
- goto err;
- }
-
- ret = reset_control_assert(apbrst);
- if (ret < 0) {
- dev_err(dev, "Failed to assert APB reset\n");
- goto err;
+ /*
+ * Asserting the core resets is not required unless a USB3 PHY is used.
+ * They may also break the configuration if USB3 is actually in use but
+ * the usb3-phy entry is missing from the device tree. Therefore, skip
+ * a full reset cycle and just deassert the resets if the phy is
+ * absent.
+ */
+ if (priv_data->usb3_phy) {
+ ret = reset_control_assert(crst);
+ if (ret < 0) {
+ dev_err(dev, "Failed to assert core reset\n");
+ goto err;
+ }
+
+ ret = reset_control_assert(hibrst);
+ if (ret < 0) {
+ dev_err(dev, "Failed to assert hibernation reset\n");
+ goto err;
+ }
+
+ ret = reset_control_assert(apbrst);
+ if (ret < 0) {
+ dev_err(dev, "Failed to assert APB reset\n");
+ goto err;
+ }
}
ret = phy_init(priv_data->usb3_phy);
@@ -181,11 +195,15 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data)
goto err;
}
- /* Set PIPE Power Present signal in FPD Power Present Register*/
- writel(FPD_POWER_PRSNT_OPTION, priv_data->regs + XLNX_USB_FPD_POWER_PRSNT);
-
- /* Set the PIPE Clock Select bit in FPD PIPE Clock register */
- writel(PIPE_CLK_SELECT, priv_data->regs + XLNX_USB_FPD_PIPE_CLK);
+ if (priv_data->usb3_phy) {
+ /* Set PIPE Power Present signal in FPD Power Present Register*/
+ writel(FPD_POWER_PRSNT_OPTION, priv_data->regs + XLNX_USB_FPD_POWER_PRSNT);
+ /* Set the PIPE Clock Select bit in FPD PIPE Clock register */
+ writel(PIPE_CLK_SELECT, priv_data->regs + XLNX_USB_FPD_PIPE_CLK);
+ } else {
+ /* Deselect the PIPE Clock Select bit in FPD PIPE Clock register */
+ writel(PIPE_CLK_DESELECT, priv_data->regs + XLNX_USB_FPD_PIPE_CLK);
+ }
ret = reset_control_deassert(crst);
if (ret < 0) {
@@ -205,33 +223,20 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data)
goto err;
}
-skip_usb3_phy:
/* ulpi reset via gpio-modepin or gpio-framework driver */
- reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+ reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(reset_gpio)) {
return dev_err_probe(dev, PTR_ERR(reset_gpio),
"Failed to request reset GPIO\n");
}
if (reset_gpio) {
- /* Toggle ulpi to reset the phy. */
- gpiod_set_value_cansleep(reset_gpio, 1);
usleep_range(5000, 10000);
gpiod_set_value_cansleep(reset_gpio, 0);
usleep_range(5000, 10000);
}
- /*
- * This routes the USB DMA traffic to go through FPD path instead
- * of reaching DDR directly. This traffic routing is needed to
- * make SMMU and CCI work with USB DMA.
- */
- if (of_dma_is_coherent(dev->of_node) || device_iommu_mapped(dev)) {
- reg = readl(priv_data->regs + XLNX_USB_TRAFFIC_ROUTE_CONFIG);
- reg |= XLNX_USB_TRAFFIC_ROUTE_FPD;
- writel(reg, priv_data->regs + XLNX_USB_TRAFFIC_ROUTE_CONFIG);
- }
-
+ dwc3_xlnx_set_coherency(priv_data, XLNX_USB_TRAFFIC_ROUTE_CONFIG);
err:
return ret;
}
@@ -375,7 +380,6 @@ static int __maybe_unused dwc3_xlnx_runtime_resume(struct device *dev)
static int __maybe_unused dwc3_xlnx_runtime_idle(struct device *dev)
{
- pm_runtime_mark_last_busy(dev);
pm_runtime_autosuspend(dev);
return 0;
@@ -424,6 +428,7 @@ static const struct dev_pm_ops dwc3_xlnx_dev_pm_ops = {
static struct platform_driver dwc3_xlnx_driver = {
.probe = dwc3_xlnx_probe,
.remove = dwc3_xlnx_remove,
+ .shutdown = dwc3_xlnx_remove,
.driver = {
.name = "dwc3-xilinx",
.of_match_table = dwc3_xlnx_of_match,
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 666ac432f52d..bfe616194dfa 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -94,6 +94,7 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep,
req->request.actual = 0;
req->request.status = -EINPROGRESS;
req->epnum = dep->number;
+ req->status = DWC3_REQUEST_STATUS_QUEUED;
list_add_tail(&req->list, &dep->pending_list);
@@ -288,7 +289,9 @@ void dwc3_ep0_out_start(struct dwc3 *dwc)
dwc3_ep0_prepare_one_trb(dep, dwc->ep0_trb_addr, 8,
DWC3_TRBCTL_CONTROL_SETUP, false);
ret = dwc3_ep0_start_trans(dep);
- WARN_ON(ret < 0);
+ if (ret < 0)
+ dev_err(dwc->dev, "ep0 out start transfer failed: %d\n", ret);
+
for (i = 2; i < DWC3_ENDPOINTS_NUM; i++) {
struct dwc3_ep *dwc3_ep;
@@ -358,7 +361,7 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc,
if ((dwc->speed == DWC3_DSTS_SUPERSPEED) ||
(dwc->speed == DWC3_DSTS_SUPERSPEED_PLUS)) {
- reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg = dwc3_readl(dwc, DWC3_DCTL);
if (reg & DWC3_DCTL_INITU1ENA)
usb_status |= 1 << USB_DEV_STAT_U1_ENABLED;
if (reg & DWC3_DCTL_INITU2ENA)
@@ -414,12 +417,12 @@ static int dwc3_ep0_handle_u1(struct dwc3 *dwc, enum usb_device_state state,
if (set && dwc->dis_u1_entry_quirk)
return -EINVAL;
- reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg = dwc3_readl(dwc, DWC3_DCTL);
if (set)
reg |= DWC3_DCTL_INITU1ENA;
else
reg &= ~DWC3_DCTL_INITU1ENA;
- dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+ dwc3_writel(dwc, DWC3_DCTL, reg);
return 0;
}
@@ -438,12 +441,12 @@ static int dwc3_ep0_handle_u2(struct dwc3 *dwc, enum usb_device_state state,
if (set && dwc->dis_u2_entry_quirk)
return -EINVAL;
- reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg = dwc3_readl(dwc, DWC3_DCTL);
if (set)
reg |= DWC3_DCTL_INITU2ENA;
else
reg &= ~DWC3_DCTL_INITU2ENA;
- dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+ dwc3_writel(dwc, DWC3_DCTL, reg);
return 0;
}
@@ -609,10 +612,10 @@ static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
return -EINVAL;
}
- reg = dwc3_readl(dwc->regs, DWC3_DCFG);
+ reg = dwc3_readl(dwc, DWC3_DCFG);
reg &= ~(DWC3_DCFG_DEVADDR_MASK);
reg |= DWC3_DCFG_DEVADDR(addr);
- dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+ dwc3_writel(dwc, DWC3_DCFG, reg);
if (addr)
usb_gadget_set_state(dwc->gadget, USB_STATE_ADDRESS);
@@ -669,12 +672,12 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
* Enable transition to U1/U2 state when
* nothing is pending from application.
*/
- reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg = dwc3_readl(dwc, DWC3_DCTL);
if (!dwc->dis_u1_entry_quirk)
reg |= DWC3_DCTL_ACCEPTU1ENA;
if (!dwc->dis_u2_entry_quirk)
reg |= DWC3_DCTL_ACCEPTU2ENA;
- dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+ dwc3_writel(dwc, DWC3_DCTL, reg);
}
break;
@@ -714,7 +717,7 @@ static void dwc3_ep0_set_sel_cmpl(struct usb_ep *ep, struct usb_request *req)
dwc->u2sel = le16_to_cpu(timing.u2sel);
dwc->u2pel = le16_to_cpu(timing.u2pel);
- reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg = dwc3_readl(dwc, DWC3_DCTL);
if (reg & DWC3_DCTL_INITU2ENA)
param = dwc->u2pel;
if (reg & DWC3_DCTL_INITU1ENA)
@@ -830,7 +833,7 @@ static void dwc3_ep0_inspect_setup(struct dwc3 *dwc,
if (!dwc->gadget_driver || !dwc->softconnect || !dwc->connected)
goto out;
- trace_dwc3_ctrl_req(ctrl);
+ trace_dwc3_ctrl_req(dwc, ctrl);
len = le16_to_cpu(ctrl->wLength);
if (!len) {
@@ -1061,7 +1064,9 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
ret = dwc3_ep0_start_trans(dep);
}
- WARN_ON(ret < 0);
+ if (ret < 0)
+ dev_err(dwc->dev,
+ "ep0 data phase start transfer failed: %d\n", ret);
}
static int dwc3_ep0_start_control_status(struct dwc3_ep *dep)
@@ -1078,7 +1083,12 @@ static int dwc3_ep0_start_control_status(struct dwc3_ep *dep)
static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep)
{
- WARN_ON(dwc3_ep0_start_control_status(dep));
+ int ret;
+
+ ret = dwc3_ep0_start_control_status(dep);
+ if (ret)
+ dev_err(dwc->dev,
+ "ep0 status phase start transfer failed: %d\n", ret);
}
static void dwc3_ep0_do_control_status(struct dwc3 *dwc,
@@ -1121,7 +1131,10 @@ void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep)
cmd |= DWC3_DEPCMD_PARAM(dep->resource_index);
memset(&params, 0, sizeof(params));
ret = dwc3_send_gadget_ep_cmd(dep, cmd, &params);
- WARN_ON_ONCE(ret);
+ if (ret)
+ dev_err_ratelimited(dwc->dev,
+ "ep0 data phase end transfer failed: %d\n", ret);
+
dep->resource_index = 0;
}
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 89a4dc8ebf94..3d4ca68e584c 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -42,7 +42,7 @@ int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode)
{
u32 reg;
- reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg = dwc3_readl(dwc, DWC3_DCTL);
reg &= ~DWC3_DCTL_TSTCTRL_MASK;
switch (mode) {
@@ -73,7 +73,7 @@ int dwc3_gadget_get_link_state(struct dwc3 *dwc)
{
u32 reg;
- reg = dwc3_readl(dwc->regs, DWC3_DSTS);
+ reg = dwc3_readl(dwc, DWC3_DSTS);
return DWC3_DSTS_USBLNKST(reg);
}
@@ -97,7 +97,7 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)
*/
if (!DWC3_VER_IS_PRIOR(DWC3, 194A)) {
while (--retries) {
- reg = dwc3_readl(dwc->regs, DWC3_DSTS);
+ reg = dwc3_readl(dwc, DWC3_DSTS);
if (reg & DWC3_DSTS_DCNRD)
udelay(5);
else
@@ -108,15 +108,15 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)
return -ETIMEDOUT;
}
- reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg = dwc3_readl(dwc, DWC3_DCTL);
reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK;
/* set no action before sending new link state change */
- dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+ dwc3_writel(dwc, DWC3_DCTL, reg);
/* set requested state */
reg |= DWC3_DCTL_ULSTCHNGREQ(state);
- dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+ dwc3_writel(dwc, DWC3_DCTL, reg);
/*
* The following code is racy when called from dwc3_gadget_wakeup,
@@ -128,7 +128,7 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)
/* wait for a change in DSTS */
retries = 10000;
while (--retries) {
- reg = dwc3_readl(dwc->regs, DWC3_DSTS);
+ reg = dwc3_readl(dwc, DWC3_DSTS);
if (DWC3_DSTS_USBLNKST(reg) == state)
return 0;
@@ -228,6 +228,13 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
{
struct dwc3 *dwc = dep->dwc;
+ /*
+ * The request might have been processed and completed while the
+ * spinlock was released. Skip processing if already completed.
+ */
+ if (req->status == DWC3_REQUEST_STATUS_COMPLETED)
+ return;
+
dwc3_gadget_del_and_unmap_request(dep, req, status);
req->status = DWC3_REQUEST_STATUS_COMPLETED;
@@ -253,11 +260,11 @@ int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned int cmd,
int ret = 0;
u32 reg;
- dwc3_writel(dwc->regs, DWC3_DGCMDPAR, param);
- dwc3_writel(dwc->regs, DWC3_DGCMD, cmd | DWC3_DGCMD_CMDACT);
+ dwc3_writel(dwc, DWC3_DGCMDPAR, param);
+ dwc3_writel(dwc, DWC3_DGCMD, cmd | DWC3_DGCMD_CMDACT);
do {
- reg = dwc3_readl(dwc->regs, DWC3_DGCMD);
+ reg = dwc3_readl(dwc, DWC3_DGCMD);
if (!(reg & DWC3_DGCMD_CMDACT)) {
status = DWC3_DGCMD_STATUS(reg);
if (status)
@@ -271,13 +278,11 @@ int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned int cmd,
status = -ETIMEDOUT;
}
- trace_dwc3_gadget_generic_cmd(cmd, param, status);
+ trace_dwc3_gadget_generic_cmd(dwc, cmd, param, status);
return ret;
}
-static int __dwc3_gadget_wakeup(struct dwc3 *dwc, bool async);
-
/**
* dwc3_send_gadget_ep_cmd - issue an endpoint command
* @dep: the endpoint to which the command is going to be issued
@@ -315,6 +320,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
int cmd_status = 0;
int ret = -EINVAL;
+ u8 epnum = dep->number;
/*
* When operating in USB 2.0 speeds (HS/FS), if GUSB2PHYCFG.ENBLSLPM or
@@ -328,7 +334,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
*/
if (dwc->gadget->speed <= USB_SPEED_HIGH ||
DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_ENDTRANSFER) {
- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
+ reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(0));
if (unlikely(reg & DWC3_GUSB2PHYCFG_SUSPHY)) {
saved_config |= DWC3_GUSB2PHYCFG_SUSPHY;
reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
@@ -340,7 +346,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
}
if (saved_config)
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+ dwc3_writel(dwc, DWC3_GUSB2PHYCFG(0), reg);
}
/*
@@ -350,9 +356,9 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
* improve performance.
*/
if (DWC3_DEPCMD_CMD(cmd) != DWC3_DEPCMD_UPDATETRANSFER) {
- dwc3_writel(dep->regs, DWC3_DEPCMDPAR0, params->param0);
- dwc3_writel(dep->regs, DWC3_DEPCMDPAR1, params->param1);
- dwc3_writel(dep->regs, DWC3_DEPCMDPAR2, params->param2);
+ dwc3_writel(dwc, DWC3_DEPCMDPAR0(epnum), params->param0);
+ dwc3_writel(dwc, DWC3_DEPCMDPAR1(epnum), params->param1);
+ dwc3_writel(dwc, DWC3_DEPCMDPAR2(epnum), params->param2);
}
/*
@@ -376,7 +382,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
else
cmd |= DWC3_DEPCMD_CMDACT;
- dwc3_writel(dep->regs, DWC3_DEPCMD, cmd);
+ dwc3_writel(dwc, DWC3_DEPCMD(epnum), cmd);
if (!(cmd & DWC3_DEPCMD_CMDACT) ||
(DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_ENDTRANSFER &&
@@ -386,7 +392,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
}
do {
- reg = dwc3_readl(dep->regs, DWC3_DEPCMD);
+ reg = dwc3_readl(dwc, DWC3_DEPCMD(epnum));
if (!(reg & DWC3_DEPCMD_CMDACT)) {
cmd_status = DWC3_DEPCMD_STATUS(reg);
@@ -442,9 +448,9 @@ skip_status:
mdelay(1);
if (saved_config) {
- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
+ reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(0));
reg |= saved_config;
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+ dwc3_writel(dwc, DWC3_GUSB2PHYCFG(0), reg);
}
return ret;
@@ -547,6 +553,7 @@ static int dwc3_gadget_set_xfer_resource(struct dwc3_ep *dep)
int dwc3_gadget_start_config(struct dwc3 *dwc, unsigned int resource_index)
{
struct dwc3_gadget_ep_cmd_params params;
+ struct dwc3_ep *dep;
u32 cmd;
int i;
int ret;
@@ -563,8 +570,13 @@ int dwc3_gadget_start_config(struct dwc3 *dwc, unsigned int resource_index)
return ret;
/* Reset resource allocation flags */
- for (i = resource_index; i < dwc->num_eps && dwc->eps[i]; i++)
- dwc->eps[i]->flags &= ~DWC3_EP_RESOURCE_ALLOCATED;
+ for (i = resource_index; i < dwc->num_eps; i++) {
+ dep = dwc->eps[i];
+ if (!dep)
+ continue;
+
+ dep->flags &= ~DWC3_EP_RESOURCE_ALLOCATED;
+ }
return 0;
}
@@ -715,7 +727,7 @@ static int dwc3_gadget_calc_ram_depth(struct dwc3 *dwc)
u32 reg;
/* Check if TXFIFOs start at non-zero addr */
- reg = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(0));
+ reg = dwc3_readl(dwc, DWC3_GTXFIFOSIZ(0));
fifo_0_start = DWC3_GTXFIFOSIZ_TXFSTADDR(reg);
ram_depth -= (fifo_0_start >> 16);
@@ -743,7 +755,7 @@ void dwc3_gadget_clear_tx_fifos(struct dwc3 *dwc)
/* Read ep0IN related TXFIFO size */
dep = dwc->eps[1];
- size = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(0));
+ size = dwc3_readl(dwc, DWC3_GTXFIFOSIZ(0));
if (DWC3_IP_IS(DWC3))
fifo_depth = DWC3_GTXFIFOSIZ_TXFDEP(size);
else
@@ -751,15 +763,17 @@ void dwc3_gadget_clear_tx_fifos(struct dwc3 *dwc)
dwc->last_fifo_depth = fifo_depth;
/* Clear existing TXFIFO for all IN eps except ep0 */
- for (num = 3; num < min_t(int, dwc->num_eps, DWC3_ENDPOINTS_NUM);
- num += 2) {
+ for (num = 3; num < min_t(int, dwc->num_eps, DWC3_ENDPOINTS_NUM); num += 2) {
dep = dwc->eps[num];
+ if (!dep)
+ continue;
+
/* Don't change TXFRAMNUM on usb31 version */
size = DWC3_IP_IS(DWC3) ? 0 :
- dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(num >> 1)) &
+ dwc3_readl(dwc, DWC3_GTXFIFOSIZ(num >> 1)) &
DWC31_GTXFIFOSIZ_TXFRAMNUM;
- dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(num >> 1), size);
+ dwc3_writel(dwc, DWC3_GTXFIFOSIZ(num >> 1), size);
dep->flags &= ~DWC3_EP_TXFIFO_RESIZED;
}
dwc->num_ep_resized = 0;
@@ -862,7 +876,7 @@ static int dwc3_gadget_resize_tx_fifos(struct dwc3_ep *dep)
fifo_size++;
/* Check if TXFIFOs start at non-zero addr */
- tmp = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(0));
+ tmp = dwc3_readl(dwc, DWC3_GTXFIFOSIZ(0));
fifo_0_start = DWC3_GTXFIFOSIZ_TXFSTADDR(tmp);
fifo_size |= (fifo_0_start + (dwc->last_fifo_depth << 16));
@@ -885,7 +899,7 @@ static int dwc3_gadget_resize_tx_fifos(struct dwc3_ep *dep)
return -ENOMEM;
}
- dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(dep->number >> 1), fifo_size);
+ dwc3_writel(dwc, DWC3_GTXFIFOSIZ(dep->number >> 1), fifo_size);
dep->flags |= DWC3_EP_TXFIFO_RESIZED;
dwc->num_ep_resized++;
@@ -918,11 +932,9 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
if (ret)
return ret;
- if (!(dep->flags & DWC3_EP_RESOURCE_ALLOCATED)) {
- ret = dwc3_gadget_set_xfer_resource(dep);
- if (ret)
- return ret;
- }
+ ret = dwc3_gadget_set_xfer_resource(dep);
+ if (ret)
+ return ret;
if (!(dep->flags & DWC3_EP_ENABLED)) {
struct dwc3_trb *trb_st_hw;
@@ -931,9 +943,9 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
dep->type = usb_endpoint_type(desc);
dep->flags |= DWC3_EP_ENABLED;
- reg = dwc3_readl(dwc->regs, DWC3_DALEPENA);
+ reg = dwc3_readl(dwc, DWC3_DALEPENA);
reg |= DWC3_DALEPENA_EP(dep->number);
- dwc3_writel(dwc->regs, DWC3_DALEPENA, reg);
+ dwc3_writel(dwc, DWC3_DALEPENA, reg);
dep->trb_dequeue = 0;
dep->trb_enqueue = 0;
@@ -1068,9 +1080,9 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
if (dep->flags & DWC3_EP_STALL)
__dwc3_gadget_ep_set_halt(dep, 0, false);
- reg = dwc3_readl(dwc->regs, DWC3_DALEPENA);
+ reg = dwc3_readl(dwc, DWC3_DALEPENA);
reg &= ~DWC3_DALEPENA_EP(dep->number);
- dwc3_writel(dwc->regs, DWC3_DALEPENA, reg);
+ dwc3_writel(dwc, DWC3_DALEPENA, reg);
dwc3_remove_requests(dwc, dep, -ESHUTDOWN);
@@ -1176,7 +1188,7 @@ static struct usb_request *dwc3_gadget_ep_alloc_request(struct usb_ep *ep,
struct dwc3_request *req;
struct dwc3_ep *dep = to_dwc3_ep(ep);
- req = kzalloc(sizeof(*req), gfp_flags);
+ req = kzalloc_obj(*req, gfp_flags);
if (!req)
return NULL;
@@ -1676,7 +1688,7 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep)
* transfer, there's no need to update the transfer.
*/
if (!ret && !starting)
- return ret;
+ return 0;
req = next_request(&dep->started_list);
if (!req) {
@@ -1731,7 +1743,7 @@ static int __dwc3_gadget_get_frame(struct dwc3 *dwc)
{
u32 reg;
- reg = dwc3_readl(dwc->regs, DWC3_DSTS);
+ reg = dwc3_readl(dwc, DWC3_DSTS);
return DWC3_DSTS_SOFFN(reg);
}
@@ -1768,7 +1780,11 @@ static int __dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool int
dep->flags |= DWC3_EP_DELAY_STOP;
return 0;
}
- WARN_ON_ONCE(ret);
+
+ if (ret)
+ dev_err_ratelimited(dep->dwc->dev,
+ "end transfer failed: %d\n", ret);
+
dep->resource_index = 0;
if (!interrupt)
@@ -1971,12 +1987,12 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
return -ESHUTDOWN;
}
- if (WARN(req->dep != dep, "request %pK belongs to '%s'\n",
+ if (WARN(req->dep != dep, "request %p belongs to '%s'\n",
&req->request, req->dep->name))
return -EINVAL;
if (WARN(req->status < DWC3_REQUEST_STATUS_COMPLETED,
- "%s: request %pK already in flight\n",
+ "%s: request %p already in flight\n",
dep->name, &req->request))
return -EINVAL;
@@ -2165,7 +2181,7 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
}
}
- dev_err(dwc->dev, "request %pK was not queued to %s\n",
+ dev_err(dwc->dev, "request %p was not queued to %s\n",
request, ep->name);
ret = -EINVAL;
out:
@@ -2335,13 +2351,13 @@ static void dwc3_gadget_enable_linksts_evts(struct dwc3 *dwc, bool set)
if (DWC3_VER_IS_PRIOR(DWC3, 250A))
return;
- reg = dwc3_readl(dwc->regs, DWC3_DEVTEN);
+ reg = dwc3_readl(dwc, DWC3_DEVTEN);
if (set)
reg |= DWC3_DEVTEN_ULSTCNGEN;
else
reg &= ~DWC3_DEVTEN_ULSTCNGEN;
- dwc3_writel(dwc->regs, DWC3_DEVTEN, reg);
+ dwc3_writel(dwc, DWC3_DEVTEN, reg);
}
static int dwc3_gadget_get_frame(struct usb_gadget *g)
@@ -2351,10 +2367,8 @@ static int dwc3_gadget_get_frame(struct usb_gadget *g)
return __dwc3_gadget_get_frame(dwc);
}
-static int __dwc3_gadget_wakeup(struct dwc3 *dwc, bool async)
+static int __dwc3_gadget_wakeup(struct dwc3 *dwc)
{
- int retries;
-
int ret;
u32 reg;
@@ -2366,7 +2380,7 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc, bool async)
*
* We can check that via USB Link State bits in DSTS register.
*/
- reg = dwc3_readl(dwc->regs, DWC3_DSTS);
+ reg = dwc3_readl(dwc, DWC3_DSTS);
link_state = DWC3_DSTS_USBLNKST(reg);
@@ -2382,8 +2396,7 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc, bool async)
return -EINVAL;
}
- if (async)
- dwc3_gadget_enable_linksts_evts(dwc, true);
+ dwc3_gadget_enable_linksts_evts(dwc, true);
ret = dwc3_gadget_set_link_state(dwc, DWC3_LINK_STATE_RECOV);
if (ret < 0) {
@@ -2395,34 +2408,15 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc, bool async)
/* Recent versions do this automatically */
if (DWC3_VER_IS_PRIOR(DWC3, 194A)) {
/* write zeroes to Link Change Request */
- reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg = dwc3_readl(dwc, DWC3_DCTL);
reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK;
- dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+ dwc3_writel(dwc, DWC3_DCTL, reg);
}
/*
* Since link status change events are enabled we will receive
- * an U0 event when wakeup is successful. So bail out.
+ * an U0 event when wakeup is successful.
*/
- if (async)
- return 0;
-
- /* poll until Link State changes to ON */
- retries = 20000;
-
- while (retries--) {
- reg = dwc3_readl(dwc->regs, DWC3_DSTS);
-
- /* in HS, means ON */
- if (DWC3_DSTS_USBLNKST(reg) == DWC3_LINK_STATE_U0)
- break;
- }
-
- if (DWC3_DSTS_USBLNKST(reg) != DWC3_LINK_STATE_U0) {
- dev_err(dwc->dev, "failed to send remote wakeup\n");
- return -EINVAL;
- }
-
return 0;
}
@@ -2443,7 +2437,7 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g)
spin_unlock_irqrestore(&dwc->lock, flags);
return -EINVAL;
}
- ret = __dwc3_gadget_wakeup(dwc, true);
+ ret = __dwc3_gadget_wakeup(dwc);
spin_unlock_irqrestore(&dwc->lock, flags);
@@ -2471,14 +2465,10 @@ static int dwc3_gadget_func_wakeup(struct usb_gadget *g, int intf_id)
*/
link_state = dwc3_gadget_get_link_state(dwc);
if (link_state == DWC3_LINK_STATE_U3) {
- ret = __dwc3_gadget_wakeup(dwc, false);
- if (ret) {
- spin_unlock_irqrestore(&dwc->lock, flags);
- return -EINVAL;
- }
- dwc3_resume_gadget(dwc);
- dwc->suspended = false;
- dwc->link_state = DWC3_LINK_STATE_U0;
+ dwc->wakeup_pending_funcs |= BIT(intf_id);
+ ret = __dwc3_gadget_wakeup(dwc);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ return ret;
}
ret = dwc3_send_gadget_generic_command(dwc, DWC3_DGCMD_DEV_NOTIFICATION,
@@ -2540,7 +2530,7 @@ static void __dwc3_gadget_set_ssp_rate(struct dwc3 *dwc)
if (ssp_rate == USB_SSP_GEN_UNKNOWN)
ssp_rate = dwc->max_ssp_rate;
- reg = dwc3_readl(dwc->regs, DWC3_DCFG);
+ reg = dwc3_readl(dwc, DWC3_DCFG);
reg &= ~DWC3_DCFG_SPEED_MASK;
reg &= ~DWC3_DCFG_NUMLANES(~0);
@@ -2553,7 +2543,7 @@ static void __dwc3_gadget_set_ssp_rate(struct dwc3 *dwc)
dwc->max_ssp_rate != USB_SSP_GEN_2x1)
reg |= DWC3_DCFG_NUMLANES(1);
- dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+ dwc3_writel(dwc, DWC3_DCFG, reg);
}
static void __dwc3_gadget_set_speed(struct dwc3 *dwc)
@@ -2571,7 +2561,7 @@ static void __dwc3_gadget_set_speed(struct dwc3 *dwc)
return;
}
- reg = dwc3_readl(dwc->regs, DWC3_DCFG);
+ reg = dwc3_readl(dwc, DWC3_DCFG);
reg &= ~(DWC3_DCFG_SPEED_MASK);
/*
@@ -2622,7 +2612,7 @@ static void __dwc3_gadget_set_speed(struct dwc3 *dwc)
speed < USB_SPEED_SUPER_PLUS)
reg &= ~DWC3_DCFG_NUMLANES(~0);
- dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+ dwc3_writel(dwc, DWC3_DCFG, reg);
}
static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)
@@ -2647,7 +2637,7 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)
* mentioned in the dwc3 programming guide. It has been tested on an
* Exynos platforms.
*/
- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
+ reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(0));
if (reg & DWC3_GUSB2PHYCFG_SUSPHY) {
saved_config |= DWC3_GUSB2PHYCFG_SUSPHY;
reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
@@ -2659,9 +2649,9 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)
}
if (saved_config)
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+ dwc3_writel(dwc, DWC3_GUSB2PHYCFG(0), reg);
- reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg = dwc3_readl(dwc, DWC3_DCTL);
if (is_on) {
if (DWC3_VER_IS_WITHIN(DWC3, ANY, 187A)) {
reg &= ~DWC3_DCTL_TRGTULST_MASK;
@@ -2680,18 +2670,19 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)
dwc->pullups_connected = false;
}
+ dwc3_pre_run_stop(dwc, is_on);
dwc3_gadget_dctl_write_safe(dwc, reg);
do {
usleep_range(1000, 2000);
- reg = dwc3_readl(dwc->regs, DWC3_DSTS);
+ reg = dwc3_readl(dwc, DWC3_DSTS);
reg &= DWC3_DSTS_DEVCTRLHLT;
} while (--timeout && !(!is_on ^ !reg));
if (saved_config) {
- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
+ reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(0));
reg |= saved_config;
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+ dwc3_writel(dwc, DWC3_GUSB2PHYCFG(0), reg);
}
if (!timeout)
@@ -2867,13 +2858,13 @@ static void dwc3_gadget_enable_irq(struct dwc3 *dwc)
if (!DWC3_VER_IS_PRIOR(DWC3, 230A))
reg |= DWC3_DEVTEN_U3L2L1SUSPEN;
- dwc3_writel(dwc->regs, DWC3_DEVTEN, reg);
+ dwc3_writel(dwc, DWC3_DEVTEN, reg);
}
static void dwc3_gadget_disable_irq(struct dwc3 *dwc)
{
/* mask all interrupts */
- dwc3_writel(dwc->regs, DWC3_DEVTEN, 0x00);
+ dwc3_writel(dwc, DWC3_DEVTEN, 0x00);
}
static irqreturn_t dwc3_interrupt(int irq, void *_dwc);
@@ -2914,10 +2905,10 @@ static void dwc3_gadget_setup_nump(struct dwc3 *dwc)
nump = min_t(u32, nump, 16);
/* update NumP */
- reg = dwc3_readl(dwc->regs, DWC3_DCFG);
+ reg = dwc3_readl(dwc, DWC3_DCFG);
reg &= ~DWC3_DCFG_NUMP_MASK;
reg |= nump << DWC3_DCFG_NUMP_SHIFT;
- dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+ dwc3_writel(dwc, DWC3_DCFG, reg);
}
static int __dwc3_gadget_start(struct dwc3 *dwc)
@@ -2931,10 +2922,10 @@ static int __dwc3_gadget_start(struct dwc3 *dwc)
* the core supports IMOD, disable it.
*/
if (dwc->imod_interval) {
- dwc3_writel(dwc->regs, DWC3_DEV_IMOD(0), dwc->imod_interval);
- dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), DWC3_GEVNTCOUNT_EHB);
+ dwc3_writel(dwc, DWC3_DEV_IMOD(0), dwc->imod_interval);
+ dwc3_writel(dwc, DWC3_GEVNTCOUNT(0), DWC3_GEVNTCOUNT_EHB);
} else if (dwc3_has_imod(dwc)) {
- dwc3_writel(dwc->regs, DWC3_DEV_IMOD(0), 0);
+ dwc3_writel(dwc, DWC3_DEV_IMOD(0), 0);
}
/*
@@ -2944,13 +2935,13 @@ static int __dwc3_gadget_start(struct dwc3 *dwc)
* This way, we maximize the chances that we'll be able to get several
* bursts of data without going through any sort of endpoint throttling.
*/
- reg = dwc3_readl(dwc->regs, DWC3_GRXTHRCFG);
+ reg = dwc3_readl(dwc, DWC3_GRXTHRCFG);
if (DWC3_IP_IS(DWC3))
reg &= ~DWC3_GRXTHRCFG_PKTCNTSEL;
else
reg &= ~DWC31_GRXTHRCFG_PKTCNTSEL;
- dwc3_writel(dwc->regs, DWC3_GRXTHRCFG, reg);
+ dwc3_writel(dwc, DWC3_GRXTHRCFG, reg);
dwc3_gadget_setup_nump(dwc);
@@ -2961,15 +2952,15 @@ static int __dwc3_gadget_start(struct dwc3 *dwc)
* ACK with NumP=0 and PP=0 (for IN direction). This slightly improves
* the stream performance.
*/
- reg = dwc3_readl(dwc->regs, DWC3_DCFG);
+ reg = dwc3_readl(dwc, DWC3_DCFG);
reg |= DWC3_DCFG_IGNSTRMPP;
- dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+ dwc3_writel(dwc, DWC3_DCFG, reg);
/* Enable MST by default if the device is capable of MST */
if (DWC3_MST_CAPABLE(&dwc->hwparams)) {
- reg = dwc3_readl(dwc->regs, DWC3_DCFG1);
+ reg = dwc3_readl(dwc, DWC3_DCFG1);
reg &= ~DWC3_DCFG1_DIS_MST_ENH;
- dwc3_writel(dwc->regs, DWC3_DCFG1, reg);
+ dwc3_writel(dwc, DWC3_DCFG1, reg);
}
/* Start with SuperSpeed Default */
@@ -3133,8 +3124,6 @@ static void dwc3_gadget_set_ssp_rate(struct usb_gadget *g,
static int dwc3_gadget_vbus_draw(struct usb_gadget *g, unsigned int mA)
{
struct dwc3 *dwc = gadget_to_dwc(g);
- union power_supply_propval val = {0};
- int ret;
if (dwc->usb2_phy)
return usb_phy_set_power(dwc->usb2_phy, mA);
@@ -3142,10 +3131,10 @@ static int dwc3_gadget_vbus_draw(struct usb_gadget *g, unsigned int mA)
if (!dwc->usb_psy)
return -EOPNOTSUPP;
- val.intval = 1000 * mA;
- ret = power_supply_set_property(dwc->usb_psy, POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, &val);
+ dwc->current_limit = mA;
+ schedule_work(&dwc->vbus_draw_work);
- return ret;
+ return 0;
}
/**
@@ -3249,7 +3238,7 @@ static int dwc3_gadget_init_in_endpoint(struct dwc3_ep *dep)
/* MDWIDTH is represented in bits, we need it in bytes */
mdwidth /= 8;
- size = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(dep->number >> 1));
+ size = dwc3_readl(dwc, DWC3_GTXFIFOSIZ(dep->number >> 1));
if (DWC3_IP_IS(DWC3))
size = DWC3_GTXFIFOSIZ_TXFDEP(size);
else
@@ -3298,7 +3287,7 @@ static int dwc3_gadget_init_out_endpoint(struct dwc3_ep *dep)
mdwidth /= 8;
/* All OUT endpoints share a single RxFIFO space */
- size = dwc3_readl(dwc->regs, DWC3_GRXFIFOSIZ(0));
+ size = dwc3_readl(dwc, DWC3_GRXFIFOSIZ(0));
if (DWC3_IP_IS(DWC3))
size = DWC3_GRXFIFOSIZ_RXFDEP(size);
else
@@ -3384,14 +3373,13 @@ static int dwc3_gadget_init_endpoint(struct dwc3 *dwc, u8 epnum)
int ret;
u8 num = epnum >> 1;
- dep = kzalloc(sizeof(*dep), GFP_KERNEL);
+ dep = kzalloc_obj(*dep);
if (!dep)
return -ENOMEM;
dep->dwc = dwc;
dep->number = epnum;
dep->direction = direction;
- dep->regs = dwc->regs + DWC3_DEP_BASE(epnum);
dwc->eps[epnum] = dep;
dep->combo_num = 0;
dep->start_cmd_status = 0;
@@ -3429,14 +3417,53 @@ static int dwc3_gadget_init_endpoint(struct dwc3 *dwc, u8 epnum)
return 0;
}
+static int dwc3_gadget_get_reserved_endpoints(struct dwc3 *dwc, const char *propname,
+ u8 *eps, u8 num)
+{
+ u8 count;
+ int ret;
+
+ if (!device_property_present(dwc->dev, propname))
+ return 0;
+
+ ret = device_property_count_u8(dwc->dev, propname);
+ if (ret < 0)
+ return ret;
+ count = ret;
+
+ ret = device_property_read_u8_array(dwc->dev, propname, eps, min(num, count));
+ if (ret)
+ return ret;
+
+ return count;
+}
+
static int dwc3_gadget_init_endpoints(struct dwc3 *dwc, u8 total)
{
+ const char *propname = "snps,reserved-endpoints";
u8 epnum;
+ u8 reserved_eps[DWC3_ENDPOINTS_NUM];
+ u8 count;
+ u8 num;
+ int ret;
INIT_LIST_HEAD(&dwc->gadget->ep_list);
+ ret = dwc3_gadget_get_reserved_endpoints(dwc, propname,
+ reserved_eps, ARRAY_SIZE(reserved_eps));
+ if (ret < 0) {
+ dev_err(dwc->dev, "failed to read %s\n", propname);
+ return ret;
+ }
+ count = ret;
+
for (epnum = 0; epnum < total; epnum++) {
- int ret;
+ for (num = 0; num < count; num++) {
+ if (epnum == reserved_eps[num])
+ break;
+ }
+ if (num < count)
+ continue;
ret = dwc3_gadget_init_endpoint(dwc, epnum);
if (ret)
@@ -3478,7 +3505,7 @@ static void dwc3_gadget_free_endpoints(struct dwc3 *dwc)
static int dwc3_gadget_ep_reclaim_completed_trb(struct dwc3_ep *dep,
struct dwc3_request *req, struct dwc3_trb *trb,
- const struct dwc3_event_depevt *event, int status, int chain)
+ const struct dwc3_event_depevt *event, int status)
{
unsigned int count;
@@ -3497,7 +3524,7 @@ static int dwc3_gadget_ep_reclaim_completed_trb(struct dwc3_ep *dep,
* We're going to do that here to avoid problems of HW trying
* to use bogus TRBs for transfers.
*/
- if (chain && (trb->ctrl & DWC3_TRB_CTRL_HWO))
+ if (trb->ctrl & DWC3_TRB_CTRL_HWO)
trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
/*
@@ -3530,7 +3557,8 @@ static int dwc3_gadget_ep_reclaim_completed_trb(struct dwc3_ep *dep,
if ((trb->ctrl & DWC3_TRB_CTRL_HWO) && status != -ESHUTDOWN)
return 1;
- if (event->status & DEPEVT_STATUS_SHORT && !chain)
+ if (event->status & DEPEVT_STATUS_SHORT &&
+ !(trb->ctrl & DWC3_TRB_CTRL_CHN))
return 1;
if ((trb->ctrl & DWC3_TRB_CTRL_ISP_IMI) &&
@@ -3557,8 +3585,7 @@ static int dwc3_gadget_ep_reclaim_trb_sg(struct dwc3_ep *dep,
trb = &dep->trb_pool[dep->trb_dequeue];
ret = dwc3_gadget_ep_reclaim_completed_trb(dep, req,
- trb, event, status,
- !!(trb->ctrl & DWC3_TRB_CTRL_CHN));
+ trb, event, status);
if (ret)
break;
}
@@ -3703,6 +3730,8 @@ out:
for (i = 0; i < DWC3_ENDPOINTS_NUM; i++) {
dep = dwc->eps[i];
+ if (!dep)
+ continue;
if (!(dep->flags & DWC3_EP_ENABLED))
continue;
@@ -3711,9 +3740,9 @@ out:
return no_started_trb;
}
- reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg = dwc3_readl(dwc, DWC3_DCTL);
reg |= dwc->u1u2;
- dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+ dwc3_writel(dwc, DWC3_DCTL, reg);
dwc->u1u2 = 0;
}
@@ -3758,6 +3787,15 @@ static void dwc3_gadget_endpoint_transfer_complete(struct dwc3_ep *dep,
static void dwc3_gadget_endpoint_transfer_not_ready(struct dwc3_ep *dep,
const struct dwc3_event_depevt *event)
{
+ /*
+ * During a device-initiated disconnect, a late xferNotReady event can
+ * be generated after the End Transfer command resets the event filter,
+ * but before the controller is halted. Ignore it to prevent a new
+ * transfer from starting.
+ */
+ if (!dep->dwc->connected)
+ return;
+
dwc3_gadget_endpoint_frame_from_event(dep, event);
/*
@@ -3839,7 +3877,7 @@ static void dwc3_gadget_endpoint_stream_event(struct dwc3_ep *dep,
case DEPEVT_STREAM_NOSTREAM:
dep->flags &= ~DWC3_EP_STREAM_PRIMED;
if (dep->flags & DWC3_EP_FORCE_RESTART_STREAM)
- queue_delayed_work(system_wq, &dep->nostream_work,
+ queue_delayed_work(system_percpu_wq, &dep->nostream_work,
msecs_to_jiffies(100));
break;
}
@@ -3852,6 +3890,10 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
u8 epnum = event->endpoint_number;
dep = dwc->eps[epnum];
+ if (!dep) {
+ dev_warn(dwc->dev, "spurious event, endpoint %u is not allocated\n", epnum);
+ return;
+ }
if (!(dep->flags & DWC3_EP_ENABLED)) {
if ((epnum > 1) && !(dep->flags & DWC3_EP_TRANSFER_STARTED))
@@ -4016,7 +4058,9 @@ static void dwc3_clear_stall_all_ep(struct dwc3 *dwc)
dep->flags &= ~DWC3_EP_STALL;
ret = dwc3_send_clear_stall_ep_cmd(dep);
- WARN_ON_ONCE(ret);
+ if (ret)
+ dev_err_ratelimited(dwc->dev,
+ "failed to clear STALL on %s\n", dep->name);
}
}
@@ -4028,7 +4072,7 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
dwc3_gadget_set_link_state(dwc, DWC3_LINK_STATE_RX_DET);
- reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg = dwc3_readl(dwc, DWC3_DCTL);
reg &= ~DWC3_DCTL_INITU1ENA;
reg &= ~DWC3_DCTL_INITU2ENA;
dwc3_gadget_dctl_write_safe(dwc, reg);
@@ -4117,7 +4161,7 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
dwc3_stop_active_transfers(dwc);
dwc->connected = true;
- reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg = dwc3_readl(dwc, DWC3_DCTL);
reg &= ~DWC3_DCTL_TSTCTRL_MASK;
dwc3_gadget_dctl_write_safe(dwc, reg);
dwc->test_mode = false;
@@ -4126,9 +4170,9 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
dwc3_clear_stall_all_ep(dwc);
/* Reset device address to zero */
- reg = dwc3_readl(dwc->regs, DWC3_DCFG);
+ reg = dwc3_readl(dwc, DWC3_DCFG);
reg &= ~(DWC3_DCFG_DEVADDR_MASK);
- dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+ dwc3_writel(dwc, DWC3_DCFG, reg);
}
static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
@@ -4142,7 +4186,7 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
if (!dwc->softconnect)
return;
- reg = dwc3_readl(dwc->regs, DWC3_DSTS);
+ reg = dwc3_readl(dwc, DWC3_DSTS);
speed = reg & DWC3_DSTS_CONNECTSPD;
dwc->speed = speed;
@@ -4217,11 +4261,11 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
!dwc->usb2_gadget_lpm_disable &&
(speed != DWC3_DSTS_SUPERSPEED) &&
(speed != DWC3_DSTS_SUPERSPEED_PLUS)) {
- reg = dwc3_readl(dwc->regs, DWC3_DCFG);
+ reg = dwc3_readl(dwc, DWC3_DCFG);
reg |= DWC3_DCFG_LPM_CAP;
- dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+ dwc3_writel(dwc, DWC3_DCFG, reg);
- reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg = dwc3_readl(dwc, DWC3_DCTL);
reg &= ~(DWC3_DCTL_HIRD_THRES_MASK | DWC3_DCTL_L1_HIBER_EN);
reg |= DWC3_DCTL_HIRD_THRES(dwc->hird_threshold |
@@ -4244,12 +4288,12 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
dwc3_gadget_dctl_write_safe(dwc, reg);
} else {
if (dwc->usb2_gadget_lpm_disable) {
- reg = dwc3_readl(dwc->regs, DWC3_DCFG);
+ reg = dwc3_readl(dwc, DWC3_DCFG);
reg &= ~DWC3_DCFG_LPM_CAP;
- dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+ dwc3_writel(dwc, DWC3_DCFG, reg);
}
- reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg = dwc3_readl(dwc, DWC3_DCTL);
reg &= ~DWC3_DCTL_HIRD_THRES_MASK;
dwc3_gadget_dctl_write_safe(dwc, reg);
}
@@ -4300,6 +4344,8 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
{
enum dwc3_link_state next = evtinfo & DWC3_LINK_STATE_MASK;
unsigned int pwropt;
+ int ret;
+ int intf_id;
/*
* WORKAROUND: DWC3 < 2.50a have an issue when configured without
@@ -4353,7 +4399,7 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
switch (dwc->link_state) {
case DWC3_LINK_STATE_U1:
case DWC3_LINK_STATE_U2:
- reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg = dwc3_readl(dwc, DWC3_DCTL);
u1u2 = reg & (DWC3_DCTL_INITU2ENA
| DWC3_DCTL_ACCEPTU2ENA
| DWC3_DCTL_INITU1ENA
@@ -4375,7 +4421,7 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
switch (next) {
case DWC3_LINK_STATE_U0:
- if (dwc->gadget->wakeup_armed) {
+ if (dwc->gadget->wakeup_armed || dwc->wakeup_pending_funcs) {
dwc3_gadget_enable_linksts_evts(dwc, false);
dwc3_resume_gadget(dwc);
dwc->suspended = false;
@@ -4398,6 +4444,18 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
}
dwc->link_state = next;
+
+ /* Proceed with func wakeup if any interfaces that has requested */
+ while (dwc->wakeup_pending_funcs && (next == DWC3_LINK_STATE_U0)) {
+ intf_id = ffs(dwc->wakeup_pending_funcs) - 1;
+ ret = dwc3_send_gadget_generic_command(dwc, DWC3_DGCMD_DEV_NOTIFICATION,
+ DWC3_DGCMDPAR_DN_FUNC_WAKE |
+ DWC3_DGCMDPAR_INTF_SEL(intf_id));
+ if (ret)
+ dev_err(dwc->dev, "Failed to send DN wake for intf %d\n", intf_id);
+
+ dwc->wakeup_pending_funcs &= ~BIT(intf_id);
+ }
}
static void dwc3_gadget_suspend_interrupt(struct dwc3 *dwc,
@@ -4498,7 +4556,7 @@ static irqreturn_t dwc3_process_event_buf(struct dwc3_event_buffer *evt)
ret = IRQ_HANDLED;
/* Unmask interrupt */
- dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0),
+ dwc3_writel(dwc, DWC3_GEVNTSIZ(0),
DWC3_GEVNTSIZ_SIZE(evt->length));
evt->flags &= ~DWC3_EVENT_PENDING;
@@ -4509,8 +4567,8 @@ static irqreturn_t dwc3_process_event_buf(struct dwc3_event_buffer *evt)
wmb();
if (dwc->imod_interval) {
- dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), DWC3_GEVNTCOUNT_EHB);
- dwc3_writel(dwc->regs, DWC3_DEV_IMOD(0), dwc->imod_interval);
+ dwc3_writel(dwc, DWC3_GEVNTCOUNT(0), DWC3_GEVNTCOUNT_EHB);
+ dwc3_writel(dwc, DWC3_DEV_IMOD(0), dwc->imod_interval);
}
return ret;
@@ -4559,16 +4617,22 @@ static irqreturn_t dwc3_check_event_buf(struct dwc3_event_buffer *evt)
if (evt->flags & DWC3_EVENT_PENDING)
return IRQ_HANDLED;
- count = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(0));
+ count = dwc3_readl(dwc, DWC3_GEVNTCOUNT(0));
count &= DWC3_GEVNTCOUNT_MASK;
if (!count)
return IRQ_NONE;
+ if (count > evt->length) {
+ dev_err_ratelimited(dwc->dev, "invalid count(%u) > evt->length(%u)\n",
+ count, evt->length);
+ return IRQ_NONE;
+ }
+
evt->count = count;
evt->flags |= DWC3_EVENT_PENDING;
/* Mask interrupt */
- dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0),
+ dwc3_writel(dwc, DWC3_GEVNTSIZ(0),
DWC3_GEVNTSIZ_INTMASK | DWC3_GEVNTSIZ_SIZE(evt->length));
amount = min(count, evt->length - evt->lpos);
@@ -4577,7 +4641,7 @@ static irqreturn_t dwc3_check_event_buf(struct dwc3_event_buffer *evt)
if (amount < count)
memcpy(evt->cache, evt->buf, count - amount);
- dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), count);
+ dwc3_writel(dwc, DWC3_GEVNTCOUNT(0), count);
return IRQ_WAKE_THREAD;
}
@@ -4664,7 +4728,7 @@ int dwc3_gadget_init(struct dwc3 *dwc)
}
init_completion(&dwc->ep0_in_setup);
- dwc->gadget = kzalloc(sizeof(struct usb_gadget), GFP_KERNEL);
+ dwc->gadget = kzalloc_obj(struct usb_gadget);
if (!dwc->gadget) {
ret = -ENOMEM;
goto err3;
@@ -4751,6 +4815,7 @@ err1:
err0:
return ret;
}
+EXPORT_SYMBOL_GPL(dwc3_gadget_init);
/* -------------------------------------------------------------------------- */
@@ -4759,7 +4824,7 @@ void dwc3_gadget_exit(struct dwc3 *dwc)
if (!dwc->gadget)
return;
- dwc3_enable_susphy(dwc, false);
+ dwc3_enable_susphy(dwc, true);
usb_del_gadget(dwc->gadget);
dwc3_gadget_free_endpoints(dwc);
usb_put_gadget(dwc->gadget);
@@ -4769,6 +4834,7 @@ void dwc3_gadget_exit(struct dwc3 *dwc)
dma_free_coherent(dwc->sysdev, sizeof(*dwc->ep0_trb) * 2,
dwc->ep0_trb, dwc->ep0_trb_addr);
}
+EXPORT_SYMBOL_GPL(dwc3_gadget_exit);
int dwc3_gadget_suspend(struct dwc3 *dwc)
{
@@ -4776,26 +4842,22 @@ int dwc3_gadget_suspend(struct dwc3 *dwc)
int ret;
ret = dwc3_gadget_soft_disconnect(dwc);
- if (ret)
- goto err;
-
- spin_lock_irqsave(&dwc->lock, flags);
- if (dwc->gadget_driver)
- dwc3_disconnect_gadget(dwc);
- spin_unlock_irqrestore(&dwc->lock, flags);
-
- return 0;
-
-err:
/*
* Attempt to reset the controller's state. Likely no
* communication can be established until the host
* performs a port reset.
*/
- if (dwc->softconnect)
+ if (ret && dwc->softconnect) {
dwc3_gadget_soft_connect(dwc);
+ return -EAGAIN;
+ }
- return ret;
+ spin_lock_irqsave(&dwc->lock, flags);
+ if (dwc->gadget_driver)
+ dwc3_disconnect_gadget(dwc);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ return 0;
}
int dwc3_gadget_resume(struct dwc3 *dwc)
diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h
index d73e735e4081..45f113b3c146 100644
--- a/drivers/usb/dwc3/gadget.h
+++ b/drivers/usb/dwc3/gadget.h
@@ -132,7 +132,7 @@ static inline void dwc3_gadget_ep_get_transfer_index(struct dwc3_ep *dep)
{
u32 res_id;
- res_id = dwc3_readl(dep->regs, DWC3_DEPCMD);
+ res_id = dwc3_readl(dep->dwc, DWC3_DEPCMD(dep->number));
dep->resource_index = DWC3_DEPCMD_GET_RSC_IDX(res_id);
}
@@ -147,7 +147,7 @@ static inline void dwc3_gadget_ep_get_transfer_index(struct dwc3_ep *dep)
static inline void dwc3_gadget_dctl_write_safe(struct dwc3 *dwc, u32 value)
{
value &= ~DWC3_DCTL_ULSTCHNGREQ_MASK;
- dwc3_writel(dwc->regs, DWC3_DCTL, value);
+ dwc3_writel(dwc, DWC3_DCTL, value);
}
#endif /* __DRIVERS_USB_DWC3_GADGET_H */
diff --git a/drivers/usb/dwc3/glue.h b/drivers/usb/dwc3/glue.h
new file mode 100644
index 000000000000..d738e1739ae0
--- /dev/null
+++ b/drivers/usb/dwc3/glue.h
@@ -0,0 +1,196 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * glue.h - DesignWare USB3 DRD glue header
+ */
+
+#ifndef __DRIVERS_USB_DWC3_GLUE_H
+#define __DRIVERS_USB_DWC3_GLUE_H
+
+#include <linux/types.h>
+#include "core.h"
+
+/**
+ * dwc3_properties: DWC3 core properties
+ * @gsbuscfg0_reqinfo: Value to be programmed in the GSBUSCFG0.REQINFO field
+ * @needs_full_reinit: indicate the controller may not remain power during system
+ * pm and need full initialization
+ */
+struct dwc3_properties {
+ u32 gsbuscfg0_reqinfo;
+ unsigned needs_full_reinit:1;
+};
+
+#define DWC3_DEFAULT_PROPERTIES ((struct dwc3_properties){ \
+ .gsbuscfg0_reqinfo = DWC3_GSBUSCFG0_REQINFO_UNSPECIFIED, \
+ })
+
+/**
+ * dwc3_probe_data: Initialization parameters passed to dwc3_core_probe()
+ * @dwc: Reference to dwc3 context structure
+ * @res: resource for the DWC3 core mmio region
+ * @ignore_clocks_and_resets: clocks and resets defined for the device should
+ * be ignored by the DWC3 core, as they are managed by the glue
+ * @skip_core_init_mode: Skip the finial initialization of the target mode, as
+ * it must be managed by the glue
+ * @properties: dwc3 software manage properties
+ */
+struct dwc3_probe_data {
+ struct dwc3 *dwc;
+ struct resource *res;
+ bool ignore_clocks_and_resets;
+ bool skip_core_init_mode;
+ struct dwc3_properties properties;
+};
+
+/**
+ * dwc3_core_probe - Initialize the core dwc3 driver
+ * @data: Initialization and configuration parameters for the controller
+ *
+ * Initializes the DesignWare USB3 core driver by setting up resources,
+ * registering interrupts, performing hardware setup, and preparing
+ * the controller for operation in the appropriate mode (host, gadget,
+ * or OTG). This is the main initialization function called by glue
+ * layer drivers to set up the core controller.
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int dwc3_core_probe(const struct dwc3_probe_data *data);
+
+/**
+ * dwc3_core_remove - Deinitialize and remove the core dwc3 driver
+ * @dwc: Pointer to DWC3 controller context
+ *
+ * Cleans up resources and disables the dwc3 core driver. This should be called
+ * during driver removal or when the glue layer needs to shut down the
+ * controller completely.
+ */
+void dwc3_core_remove(struct dwc3 *dwc);
+
+/*
+ * The following callbacks are provided for glue drivers to call from their
+ * own pm callbacks provided in struct dev_pm_ops. Glue drivers can perform
+ * platform-specific work before or after calling these functions and delegate
+ * the core suspend/resume operations to the core driver.
+ */
+int dwc3_runtime_suspend(struct dwc3 *dwc);
+int dwc3_runtime_resume(struct dwc3 *dwc);
+int dwc3_runtime_idle(struct dwc3 *dwc);
+int dwc3_pm_suspend(struct dwc3 *dwc);
+int dwc3_pm_resume(struct dwc3 *dwc);
+void dwc3_pm_complete(struct dwc3 *dwc);
+int dwc3_pm_prepare(struct dwc3 *dwc);
+
+
+/* All of the following functions must only be used with skip_core_init_mode */
+
+/**
+ * dwc3_core_init - Initialize DWC3 core hardware
+ * @dwc: Pointer to DWC3 controller context
+ *
+ * Configures and initializes the core hardware, usually done by dwc3_core_probe.
+ * This function is provided for platforms that use skip_core_init_mode and need
+ * to finalize the core initialization after some platform-specific setup.
+ * It must only be called when using skip_core_init_mode and before
+ * dwc3_host_init or dwc3_gadget_init.
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int dwc3_core_init(struct dwc3 *dwc);
+
+/**
+ * dwc3_core_exit - Shut down DWC3 core hardware
+ * @dwc: Pointer to DWC3 controller context
+ *
+ * Disables and cleans up the core hardware state. This is usually handled
+ * internally by dwc3 and must only be called when using skip_core_init_mode
+ * and only after dwc3_core_init. Afterwards, dwc3_core_init may be called
+ * again.
+ */
+void dwc3_core_exit(struct dwc3 *dwc);
+
+/**
+ * dwc3_host_init - Initialize host mode operation
+ * @dwc: Pointer to DWC3 controller context
+ *
+ * Initializes the controller for USB host mode operation, usually done by
+ * dwc3_core_probe or from within the dwc3 USB role switch callback.
+ * This function is provided for platforms that use skip_core_init_mode and need
+ * to finalize the host initialization after some platform-specific setup.
+ * It must not be called before dwc3_core_init or when skip_core_init_mode is
+ * not used. It must also not be called when gadget or host mode has already
+ * been initialized.
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int dwc3_host_init(struct dwc3 *dwc);
+
+/**
+ * dwc3_host_exit - Shut down host mode operation
+ * @dwc: Pointer to DWC3 controller context
+ *
+ * Disables and cleans up host mode resources, usually done by
+ * the dwc3 USB role switch callback before switching controller mode.
+ * It must only be called when skip_core_init_mode is used and only after
+ * dwc3_host_init.
+ */
+void dwc3_host_exit(struct dwc3 *dwc);
+
+/**
+ * dwc3_gadget_init - Initialize gadget mode operation
+ * @dwc: Pointer to DWC3 controller context
+ *
+ * Initializes the controller for USB gadget mode operation, usually done by
+ * dwc3_core_probe or from within the dwc3 USB role switch callback. This
+ * function is provided for platforms that use skip_core_init_mode and need to
+ * finalize the gadget initialization after some platform-specific setup.
+ * It must not be called before dwc3_core_init or when skip_core_init_mode is
+ * not used. It must also not be called when gadget or host mode has already
+ * been initialized.
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int dwc3_gadget_init(struct dwc3 *dwc);
+
+/**
+ * dwc3_gadget_exit - Shut down gadget mode operation
+ * @dwc: Pointer to DWC3 controller context
+ *
+ * Disables and cleans up gadget mode resources, usually done by
+ * the dwc3 USB role switch callback before switching controller mode.
+ * It must only be called when skip_core_init_mode is used and only after
+ * dwc3_gadget_init.
+ */
+void dwc3_gadget_exit(struct dwc3 *dwc);
+
+/**
+ * dwc3_enable_susphy - Control SUSPHY status for all USB ports
+ * @dwc: Pointer to DWC3 controller context
+ * @enable: True to enable SUSPHY, false to disable
+ *
+ * Enables or disables the USB3 PHY SUSPEND and USB2 PHY SUSPHY feature for
+ * all available ports.
+ * This is usually handled by the dwc3 core code and should only be used
+ * when skip_core_init_mode is used and the glue layer needs to manage SUSPHY
+ * settings itself, e.g., due to platform-specific requirements during mode
+ * switches.
+ */
+void dwc3_enable_susphy(struct dwc3 *dwc, bool enable);
+
+/**
+ * dwc3_set_prtcap - Set the USB controller PRTCAP mode
+ * @dwc: Pointer to DWC3 controller context
+ * @mode: Target mode, must be one of DWC3_GCTL_PRTCAP_{HOST,DEVICE,OTG}
+ * @ignore_susphy: If true, skip disabling the SUSPHY and keep the current state
+ *
+ * Updates PRTCAP of the controller and current_dr_role inside the dwc3
+ * structure. For DRD controllers, this also disables SUSPHY unless explicitly
+ * told to skip via the ignore_susphy parameter.
+ *
+ * This is usually handled by the dwc3 core code and should only be used
+ * when skip_core_init_mode is used and the glue layer needs to manage mode
+ * transitions itself due to platform-specific requirements. It must be called
+ * with the correct mode before calling dwc3_host_init or dwc3_gadget_init.
+ */
+void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode, bool ignore_susphy);
+
+#endif
diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c
index b48e108fc8fe..96b588bd08cd 100644
--- a/drivers/usb/dwc3/host.c
+++ b/drivers/usb/dwc3/host.c
@@ -37,7 +37,10 @@ static void dwc3_power_off_all_roothub_ports(struct dwc3 *dwc)
/* xhci regs are not mapped yet, do it temporarily here */
if (dwc->xhci_resources[0].start) {
- xhci_regs = ioremap(dwc->xhci_resources[0].start, DWC3_XHCI_REGS_END);
+ if (dwc->xhci_resources[0].flags & IORESOURCE_MEM_NONPOSTED)
+ xhci_regs = ioremap_np(dwc->xhci_resources[0].start, DWC3_XHCI_REGS_END);
+ else
+ xhci_regs = ioremap(dwc->xhci_resources[0].start, DWC3_XHCI_REGS_END);
if (!xhci_regs) {
dev_err(dwc->dev, "Failed to ioremap xhci_regs\n");
return;
@@ -182,6 +185,9 @@ int dwc3_host_init(struct dwc3 *dwc)
if (DWC3_VER_IS_WITHIN(DWC3, ANY, 300A))
props[prop_idx++] = PROPERTY_ENTRY_BOOL("quirk-broken-port-ped");
+ props[prop_idx++] = PROPERTY_ENTRY_U16("num-hc-interrupters",
+ dwc->num_hc_interrupters);
+
if (prop_idx) {
ret = device_create_managed_software_node(&xhci->dev, props, NULL);
if (ret) {
@@ -214,13 +220,15 @@ err:
platform_device_put(xhci);
return ret;
}
+EXPORT_SYMBOL_GPL(dwc3_host_init);
void dwc3_host_exit(struct dwc3 *dwc)
{
if (dwc->sys_wakeup)
device_init_wakeup(&dwc->xhci->dev, false);
- dwc3_enable_susphy(dwc, false);
+ dwc3_enable_susphy(dwc, true);
platform_device_unregister(dwc->xhci);
dwc->xhci = NULL;
}
+EXPORT_SYMBOL_GPL(dwc3_host_exit);
diff --git a/drivers/usb/dwc3/io.h b/drivers/usb/dwc3/io.h
index 1e96ea339d48..cad9a2ae1547 100644
--- a/drivers/usb/dwc3/io.h
+++ b/drivers/usb/dwc3/io.h
@@ -16,9 +16,10 @@
#include "debug.h"
#include "core.h"
-static inline u32 dwc3_readl(void __iomem *base, u32 offset)
+static inline u32 dwc3_readl(struct dwc3 *dwc, u32 offset)
{
u32 value;
+ void __iomem *base = dwc->regs;
/*
* We requested the mem region starting from the Globals address
@@ -32,13 +33,15 @@ static inline u32 dwc3_readl(void __iomem *base, u32 offset)
* documentation, so we revert it back to the proper addresses, the
* same way they are described on SNPS documentation
*/
- trace_dwc3_readl(base - DWC3_GLOBALS_REGS_START, offset, value);
+ trace_dwc3_readl(dwc, base - DWC3_GLOBALS_REGS_START, offset, value);
return value;
}
-static inline void dwc3_writel(void __iomem *base, u32 offset, u32 value)
+static inline void dwc3_writel(struct dwc3 *dwc, u32 offset, u32 value)
{
+ void __iomem *base = dwc->regs;
+
/*
* We requested the mem region starting from the Globals address
* space, see dwc3_probe in core.c.
@@ -51,7 +54,7 @@ static inline void dwc3_writel(void __iomem *base, u32 offset, u32 value)
* documentation, so we revert it back to the proper addresses, the
* same way they are described on SNPS documentation
*/
- trace_dwc3_writel(base - DWC3_GLOBALS_REGS_START, offset, value);
+ trace_dwc3_writel(dwc, base - DWC3_GLOBALS_REGS_START, offset, value);
}
#endif /* __DRIVERS_USB_DWC3_IO_H */
diff --git a/drivers/usb/dwc3/trace.h b/drivers/usb/dwc3/trace.h
index bdeb1aaf65d8..5253da23d8b0 100644
--- a/drivers/usb/dwc3/trace.h
+++ b/drivers/usb/dwc3/trace.h
@@ -19,47 +19,71 @@
#include "core.h"
#include "debug.h"
+DECLARE_EVENT_CLASS(dwc3_log_set_prtcap,
+ TP_PROTO(struct dwc3 *dwc, u32 mode),
+ TP_ARGS(dwc, mode),
+ TP_STRUCT__entry(
+ __field(phys_addr_t, base_address)
+ __field(u32, mode)
+ ),
+ TP_fast_assign(
+ __entry->base_address = dwc->xhci_resources[0].start;
+ __entry->mode = mode;
+ ),
+ TP_printk("%pa: mode %s", &__entry->base_address, dwc3_mode_string(__entry->mode))
+);
+
+DEFINE_EVENT(dwc3_log_set_prtcap, dwc3_set_prtcap,
+ TP_PROTO(struct dwc3 *dwc, u32 mode),
+ TP_ARGS(dwc, mode)
+);
+
DECLARE_EVENT_CLASS(dwc3_log_io,
- TP_PROTO(void *base, u32 offset, u32 value),
- TP_ARGS(base, offset, value),
+ TP_PROTO(struct dwc3 *dwc, void *base, u32 offset, u32 value),
+ TP_ARGS(dwc, base, offset, value),
TP_STRUCT__entry(
+ __field(phys_addr_t, base_address)
__field(void *, base)
__field(u32, offset)
__field(u32, value)
),
TP_fast_assign(
+ __entry->base_address = dwc->xhci_resources[0].start;
__entry->base = base;
__entry->offset = offset;
__entry->value = value;
),
- TP_printk("addr %p offset %04x value %08x",
+ TP_printk("%pa: addr %p offset %04x value %08x",
+ &__entry->base_address,
__entry->base + __entry->offset,
__entry->offset,
__entry->value)
);
DEFINE_EVENT(dwc3_log_io, dwc3_readl,
- TP_PROTO(void __iomem *base, u32 offset, u32 value),
- TP_ARGS(base, offset, value)
+ TP_PROTO(struct dwc3 *dwc, void __iomem *base, u32 offset, u32 value),
+ TP_ARGS(dwc, base, offset, value)
);
DEFINE_EVENT(dwc3_log_io, dwc3_writel,
- TP_PROTO(void __iomem *base, u32 offset, u32 value),
- TP_ARGS(base, offset, value)
+ TP_PROTO(struct dwc3 *dwc, void __iomem *base, u32 offset, u32 value),
+ TP_ARGS(dwc, base, offset, value)
);
DECLARE_EVENT_CLASS(dwc3_log_event,
TP_PROTO(u32 event, struct dwc3 *dwc),
TP_ARGS(event, dwc),
TP_STRUCT__entry(
+ __field(phys_addr_t, base_address)
__field(u32, event)
__field(u32, ep0state)
),
TP_fast_assign(
+ __entry->base_address = dwc->xhci_resources[0].start;
__entry->event = event;
__entry->ep0state = dwc->ep0state;
),
- TP_printk("event (%08x): %s", __entry->event,
+ TP_printk("%pa: event (%08x): %s", &__entry->base_address, __entry->event,
dwc3_decode_event(__get_buf(DWC3_MSG_MAX), DWC3_MSG_MAX,
__entry->event, __entry->ep0state))
);
@@ -70,9 +94,10 @@ DEFINE_EVENT(dwc3_log_event, dwc3_event,
);
DECLARE_EVENT_CLASS(dwc3_log_ctrl,
- TP_PROTO(struct usb_ctrlrequest *ctrl),
- TP_ARGS(ctrl),
+ TP_PROTO(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl),
+ TP_ARGS(dwc, ctrl),
TP_STRUCT__entry(
+ __field(phys_addr_t, base_address)
__field(__u8, bRequestType)
__field(__u8, bRequest)
__field(__u16, wValue)
@@ -80,13 +105,15 @@ DECLARE_EVENT_CLASS(dwc3_log_ctrl,
__field(__u16, wLength)
),
TP_fast_assign(
+ __entry->base_address = dwc->xhci_resources[0].start;
__entry->bRequestType = ctrl->bRequestType;
__entry->bRequest = ctrl->bRequest;
__entry->wValue = le16_to_cpu(ctrl->wValue);
__entry->wIndex = le16_to_cpu(ctrl->wIndex);
__entry->wLength = le16_to_cpu(ctrl->wLength);
),
- TP_printk("%s", usb_decode_ctrl(__get_buf(DWC3_MSG_MAX), DWC3_MSG_MAX,
+ TP_printk("%pa: %s", &__entry->base_address, usb_decode_ctrl(__get_buf(DWC3_MSG_MAX),
+ DWC3_MSG_MAX,
__entry->bRequestType,
__entry->bRequest, __entry->wValue,
__entry->wIndex, __entry->wLength)
@@ -94,14 +121,15 @@ DECLARE_EVENT_CLASS(dwc3_log_ctrl,
);
DEFINE_EVENT(dwc3_log_ctrl, dwc3_ctrl_req,
- TP_PROTO(struct usb_ctrlrequest *ctrl),
- TP_ARGS(ctrl)
+ TP_PROTO(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl),
+ TP_ARGS(dwc, ctrl)
);
DECLARE_EVENT_CLASS(dwc3_log_request,
TP_PROTO(struct dwc3_request *req),
TP_ARGS(req),
TP_STRUCT__entry(
+ __field(phys_addr_t, base_address)
__string(name, req->dep->name)
__field(struct dwc3_request *, req)
__field(unsigned int, actual)
@@ -112,7 +140,7 @@ DECLARE_EVENT_CLASS(dwc3_log_request,
__field(int, no_interrupt)
),
TP_fast_assign(
- __assign_str(name);
+ __entry->base_address = req->dep->dwc->xhci_resources[0].start;
__entry->req = req;
__entry->actual = req->request.actual;
__entry->length = req->request.length;
@@ -121,8 +149,10 @@ DECLARE_EVENT_CLASS(dwc3_log_request,
__entry->short_not_ok = req->request.short_not_ok;
__entry->no_interrupt = req->request.no_interrupt;
),
- TP_printk("%s: req %p length %u/%u %s%s%s ==> %d",
- __get_str(name), __entry->req, __entry->actual, __entry->length,
+ TP_printk("%pa: %s: req %p length %u/%u %s%s%s ==> %d",
+ &__entry->base_address,
+ __get_str(name), __entry->req,
+ __entry->actual, __entry->length,
__entry->zero ? "Z" : "z",
__entry->short_not_ok ? "S" : "s",
__entry->no_interrupt ? "i" : "I",
@@ -156,28 +186,30 @@ DEFINE_EVENT(dwc3_log_request, dwc3_gadget_giveback,
);
DECLARE_EVENT_CLASS(dwc3_log_generic_cmd,
- TP_PROTO(unsigned int cmd, u32 param, int status),
- TP_ARGS(cmd, param, status),
+ TP_PROTO(struct dwc3 *dwc, unsigned int cmd, u32 param, int status),
+ TP_ARGS(dwc, cmd, param, status),
TP_STRUCT__entry(
+ __field(phys_addr_t, base_address)
__field(unsigned int, cmd)
__field(u32, param)
__field(int, status)
),
TP_fast_assign(
+ __entry->base_address = dwc->xhci_resources[0].start;
__entry->cmd = cmd;
__entry->param = param;
__entry->status = status;
),
- TP_printk("cmd '%s' [%x] param %08x --> status: %s",
- dwc3_gadget_generic_cmd_string(__entry->cmd),
+ TP_printk("%pa: cmd '%s' [%x] param %08x --> status: %s",
+ &__entry->base_address, dwc3_gadget_generic_cmd_string(__entry->cmd),
__entry->cmd, __entry->param,
dwc3_gadget_generic_cmd_status_string(__entry->status)
)
);
DEFINE_EVENT(dwc3_log_generic_cmd, dwc3_gadget_generic_cmd,
- TP_PROTO(unsigned int cmd, u32 param, int status),
- TP_ARGS(cmd, param, status)
+ TP_PROTO(struct dwc3 *dwc, unsigned int cmd, u32 param, int status),
+ TP_ARGS(dwc, cmd, param, status)
);
DECLARE_EVENT_CLASS(dwc3_log_gadget_ep_cmd,
@@ -185,6 +217,7 @@ DECLARE_EVENT_CLASS(dwc3_log_gadget_ep_cmd,
struct dwc3_gadget_ep_cmd_params *params, int cmd_status),
TP_ARGS(dep, cmd, params, cmd_status),
TP_STRUCT__entry(
+ __field(phys_addr_t, base_address)
__string(name, dep->name)
__field(unsigned int, cmd)
__field(u32, param0)
@@ -193,6 +226,7 @@ DECLARE_EVENT_CLASS(dwc3_log_gadget_ep_cmd,
__field(int, cmd_status)
),
TP_fast_assign(
+ __entry->base_address = dep->dwc->xhci_resources[0].start;
__assign_str(name);
__entry->cmd = cmd;
__entry->param0 = params->param0;
@@ -200,8 +234,9 @@ DECLARE_EVENT_CLASS(dwc3_log_gadget_ep_cmd,
__entry->param2 = params->param2;
__entry->cmd_status = cmd_status;
),
- TP_printk("%s: cmd '%s' [%x] params %08x %08x %08x --> status: %s",
- __get_str(name), dwc3_gadget_ep_cmd_string(__entry->cmd),
+ TP_printk("%pa: %s: cmd '%s' [%x] params %08x %08x %08x --> status: %s",
+ &__entry->base_address, __get_str(name),
+ dwc3_gadget_ep_cmd_string(__entry->cmd),
__entry->cmd, __entry->param0,
__entry->param1, __entry->param2,
dwc3_ep_cmd_status_string(__entry->cmd_status)
@@ -218,6 +253,7 @@ DECLARE_EVENT_CLASS(dwc3_log_trb,
TP_PROTO(struct dwc3_ep *dep, struct dwc3_trb *trb),
TP_ARGS(dep, trb),
TP_STRUCT__entry(
+ __field(phys_addr_t, base_address)
__string(name, dep->name)
__field(struct dwc3_trb *, trb)
__field(u32, bpl)
@@ -229,6 +265,7 @@ DECLARE_EVENT_CLASS(dwc3_log_trb,
__field(u32, dequeue)
),
TP_fast_assign(
+ __entry->base_address = dep->dwc->xhci_resources[0].start;
__assign_str(name);
__entry->trb = trb;
__entry->bpl = trb->bpl;
@@ -239,8 +276,8 @@ DECLARE_EVENT_CLASS(dwc3_log_trb,
__entry->enqueue = dep->trb_enqueue;
__entry->dequeue = dep->trb_dequeue;
),
- TP_printk("%s: trb %p (E%d:D%d) buf %08x%08x size %s%d ctrl %08x sofn %08x (%c%c%c%c:%c%c:%s)",
- __get_str(name), __entry->trb, __entry->enqueue,
+ TP_printk("%pa: %s: trb %p (E%d:D%d) buf %08x%08x size %s%d ctrl %08x sofn %08x (%c%c%c%c:%c%c:%s)",
+ &__entry->base_address, __get_str(name), __entry->trb, __entry->enqueue,
__entry->dequeue, __entry->bph, __entry->bpl,
({char *s;
int pcm = ((__entry->size >> 24) & 3) + 1;
@@ -290,6 +327,7 @@ DECLARE_EVENT_CLASS(dwc3_log_ep,
TP_PROTO(struct dwc3_ep *dep),
TP_ARGS(dep),
TP_STRUCT__entry(
+ __field(phys_addr_t, base_address)
__string(name, dep->name)
__field(unsigned int, maxpacket)
__field(unsigned int, maxpacket_limit)
@@ -301,6 +339,7 @@ DECLARE_EVENT_CLASS(dwc3_log_ep,
__field(u8, trb_dequeue)
),
TP_fast_assign(
+ __entry->base_address = dep->dwc->xhci_resources[0].start;
__assign_str(name);
__entry->maxpacket = dep->endpoint.maxpacket;
__entry->maxpacket_limit = dep->endpoint.maxpacket_limit;
@@ -311,8 +350,8 @@ DECLARE_EVENT_CLASS(dwc3_log_ep,
__entry->trb_enqueue = dep->trb_enqueue;
__entry->trb_dequeue = dep->trb_dequeue;
),
- TP_printk("%s: mps %d/%d streams %d burst %d ring %d/%d flags %c:%c%c%c%c:%c",
- __get_str(name), __entry->maxpacket,
+ TP_printk("%pa: %s: mps %d/%d streams %d burst %d ring %d/%d flags %c:%c%c%c%c:%c",
+ &__entry->base_address, __get_str(name), __entry->maxpacket,
__entry->maxpacket_limit, __entry->max_streams,
__entry->maxburst, __entry->trb_enqueue,
__entry->trb_dequeue,
diff --git a/drivers/usb/dwc3/ulpi.c b/drivers/usb/dwc3/ulpi.c
index f23f4c9a557e..a256b7f5d78b 100644
--- a/drivers/usb/dwc3/ulpi.c
+++ b/drivers/usb/dwc3/ulpi.c
@@ -10,10 +10,13 @@
#include <linux/delay.h>
#include <linux/time64.h>
#include <linux/ulpi/regs.h>
+#include <linux/ulpi/driver.h>
#include "core.h"
#include "io.h"
+#define USB_VENDOR_MICROCHIP 0x0424
+
#define DWC3_ULPI_ADDR(a) \
((a >= ULPI_EXT_VENDOR_SPECIFIC) ? \
DWC3_GUSB2PHYACC_ADDR(ULPI_ACCESS_EXTENDED) | \
@@ -33,13 +36,13 @@ static int dwc3_ulpi_busyloop(struct dwc3 *dwc, u8 addr, bool read)
if (read)
ns += DWC3_ULPI_BASE_DELAY;
- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
+ reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(0));
if (reg & DWC3_GUSB2PHYCFG_SUSPHY)
usleep_range(1000, 1200);
while (count--) {
ndelay(ns);
- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYACC(0));
+ reg = dwc3_readl(dwc, DWC3_GUSB2PHYACC(0));
if (reg & DWC3_GUSB2PHYACC_DONE)
return 0;
cpu_relax();
@@ -55,13 +58,13 @@ static int dwc3_ulpi_read(struct device *dev, u8 addr)
int ret;
reg = DWC3_GUSB2PHYACC_NEWREGREQ | DWC3_ULPI_ADDR(addr);
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYACC(0), reg);
+ dwc3_writel(dwc, DWC3_GUSB2PHYACC(0), reg);
ret = dwc3_ulpi_busyloop(dwc, addr, true);
if (ret)
return ret;
- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYACC(0));
+ reg = dwc3_readl(dwc, DWC3_GUSB2PHYACC(0));
return DWC3_GUSB2PHYACC_DATA(reg);
}
@@ -73,7 +76,7 @@ static int dwc3_ulpi_write(struct device *dev, u8 addr, u8 val)
reg = DWC3_GUSB2PHYACC_NEWREGREQ | DWC3_ULPI_ADDR(addr);
reg |= DWC3_GUSB2PHYACC_WRITE | val;
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYACC(0), reg);
+ dwc3_writel(dwc, DWC3_GUSB2PHYACC(0), reg);
return dwc3_ulpi_busyloop(dwc, addr, false);
}
@@ -83,6 +86,26 @@ static const struct ulpi_ops dwc3_ulpi_ops = {
.write = dwc3_ulpi_write,
};
+static void dwc3_ulpi_detect_config(struct dwc3 *dwc)
+{
+ struct ulpi *ulpi = dwc->ulpi;
+
+ switch (ulpi->id.vendor) {
+ case USB_VENDOR_MICROCHIP:
+ switch (ulpi->id.product) {
+ case 0x0009:
+ /* Microchip USB3340 ULPI PHY */
+ dwc->enable_usb2_transceiver_delay = true;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
int dwc3_ulpi_init(struct dwc3 *dwc)
{
/* Register the interface */
@@ -92,6 +115,8 @@ int dwc3_ulpi_init(struct dwc3 *dwc)
return PTR_ERR(dwc->ulpi);
}
+ dwc3_ulpi_detect_config(dwc);
+
return 0;
}
diff --git a/drivers/usb/early/xhci-dbc.c b/drivers/usb/early/xhci-dbc.c
index 341408410ed9..41118bba9197 100644
--- a/drivers/usb/early/xhci-dbc.c
+++ b/drivers/usb/early/xhci-dbc.c
@@ -681,6 +681,10 @@ int __init early_xdbc_setup_hardware(void)
xdbc.table_base = NULL;
xdbc.out_buf = NULL;
+
+ early_iounmap(xdbc.xhci_base, xdbc.xhci_length);
+ xdbc.xhci_base = NULL;
+ xdbc.xhci_length = 0;
}
return ret;
diff --git a/drivers/usb/fotg210/fotg210-hcd.c b/drivers/usb/fotg210/fotg210-hcd.c
index 3d404d19a205..1a48329a4e08 100644
--- a/drivers/usb/fotg210/fotg210-hcd.c
+++ b/drivers/usb/fotg210/fotg210-hcd.c
@@ -515,7 +515,7 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
unsigned i;
__hc32 tag;
- seen = kmalloc_array(DBG_SCHED_LIMIT, sizeof(*seen), GFP_ATOMIC);
+ seen = kmalloc_objs(*seen, DBG_SCHED_LIMIT, GFP_ATOMIC);
if (!seen)
return 0;
@@ -738,7 +738,7 @@ static struct debug_buffer
{
struct debug_buffer *buf;
- buf = kzalloc(sizeof(struct debug_buffer), GFP_KERNEL);
+ buf = kzalloc_obj(struct debug_buffer);
if (buf) {
buf->bus = bus;
@@ -1847,7 +1847,7 @@ static struct fotg210_qh *fotg210_qh_alloc(struct fotg210_hcd *fotg210,
struct fotg210_qh *qh;
dma_addr_t dma;
- qh = kzalloc(sizeof(*qh), GFP_ATOMIC);
+ qh = kzalloc_obj(*qh, GFP_ATOMIC);
if (!qh)
goto done;
qh->hw = (struct fotg210_qh_hw *)
@@ -3899,7 +3899,7 @@ static struct fotg210_iso_stream *iso_stream_alloc(gfp_t mem_flags)
{
struct fotg210_iso_stream *stream;
- stream = kzalloc(sizeof(*stream), mem_flags);
+ stream = kzalloc_obj(*stream, mem_flags);
if (likely(stream != NULL)) {
INIT_LIST_HEAD(&stream->td_list);
INIT_LIST_HEAD(&stream->free_list);
@@ -4007,7 +4007,7 @@ static struct fotg210_iso_sched *iso_sched_alloc(unsigned packets,
{
struct fotg210_iso_sched *iso_sched;
- iso_sched = kzalloc(struct_size(iso_sched, packet, packets), mem_flags);
+ iso_sched = kzalloc_flex(*iso_sched, packet, packets, mem_flags);
if (likely(iso_sched != NULL))
INIT_LIST_HEAD(&iso_sched->td_list);
@@ -4901,8 +4901,7 @@ static int hcd_fotg210_init(struct usb_hcd *hcd)
*/
fotg210->need_io_watchdog = 1;
- hrtimer_init(&fotg210->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
- fotg210->hrtimer.function = fotg210_hrtimer_func;
+ hrtimer_setup(&fotg210->hrtimer, fotg210_hrtimer_func, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
fotg210->next_hrtimer_event = FOTG210_HRTIMER_NO_EVENT;
hcc_params = fotg210_readl(fotg210, &fotg210->caps->hcc_params);
@@ -5626,11 +5625,6 @@ int __init fotg210_hcd_init(void)
if (usb_disabled())
return -ENODEV;
- set_bit(USB_EHCI_LOADED, &usb_hcds_loaded);
- if (test_bit(USB_UHCI_LOADED, &usb_hcds_loaded) ||
- test_bit(USB_OHCI_LOADED, &usb_hcds_loaded))
- pr_warn("Warning! fotg210_hcd should always be loaded before uhci_hcd and ohci_hcd, not after\n");
-
pr_debug("%s: block sizes: qh %zd qtd %zd itd %zd\n",
hcd_name, sizeof(struct fotg210_qh),
sizeof(struct fotg210_qtd),
@@ -5644,5 +5638,4 @@ int __init fotg210_hcd_init(void)
void __exit fotg210_hcd_cleanup(void)
{
debugfs_remove(fotg210_debug_root);
- clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded);
}
diff --git a/drivers/usb/fotg210/fotg210-udc.c b/drivers/usb/fotg210/fotg210-udc.c
index 0bae12e34f9a..d9e024873a42 100644
--- a/drivers/usb/fotg210/fotg210-udc.c
+++ b/drivers/usb/fotg210/fotg210-udc.c
@@ -244,7 +244,7 @@ static struct usb_request *fotg210_ep_alloc_request(struct usb_ep *_ep,
{
struct fotg210_request *req;
- req = kzalloc(sizeof(struct fotg210_request), gfp_flags);
+ req = kzalloc_obj(struct fotg210_request, gfp_flags);
if (!req)
return NULL;
@@ -1183,7 +1183,7 @@ int fotg210_udc_probe(struct platform_device *pdev, struct fotg210 *fotg)
return irq;
/* initialize udc */
- fotg210 = kzalloc(sizeof(struct fotg210_udc), GFP_KERNEL);
+ fotg210 = kzalloc_obj(struct fotg210_udc);
if (fotg210 == NULL)
return -ENOMEM;
@@ -1207,7 +1207,7 @@ int fotg210_udc_probe(struct platform_device *pdev, struct fotg210 *fotg)
ret = -ENOMEM;
for (i = 0; i < FOTG210_MAX_NUM_EP; i++) {
- fotg210->ep[i] = kzalloc(sizeof(struct fotg210_ep), GFP_KERNEL);
+ fotg210->ep[i] = kzalloc_obj(struct fotg210_ep);
if (!fotg210->ep[i])
goto err_alloc;
}
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 869ad99afb48..a902184bdf82 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -1011,7 +1011,7 @@ static int set_config(struct usb_composite_dev *cdev,
ep = (struct usb_endpoint_descriptor *)*descriptors;
addr = ((ep->bEndpointAddress & 0x80) >> 3)
- | (ep->bEndpointAddress & 0x0f);
+ | usb_endpoint_num(ep);
set_bit(addr, f->endpoints);
}
@@ -1194,30 +1194,6 @@ static void remove_config(struct usb_composite_dev *cdev,
}
}
-/**
- * usb_remove_config() - remove a configuration from a device.
- * @cdev: wraps the USB gadget
- * @config: the configuration
- *
- * Drivers must call usb_gadget_disconnect before calling this function
- * to disconnect the device from the host and make sure the host will not
- * try to enumerate the device while we are changing the config list.
- */
-void usb_remove_config(struct usb_composite_dev *cdev,
- struct usb_configuration *config)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&cdev->lock, flags);
-
- if (cdev->config == config)
- reset_config(cdev);
-
- spin_unlock_irqrestore(&cdev->lock, flags);
-
- remove_config(cdev, config);
-}
-
/*-------------------------------------------------------------------------*/
/* We support strings in multiple languages ... string descriptor zero
@@ -2011,15 +1987,13 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
if (f->get_status) {
status = f->get_status(f);
+
if (status < 0)
break;
- } else {
- /* Set D0 and D1 bits based on func wakeup capability */
- if (f->config->bmAttributes & USB_CONFIG_ATT_WAKEUP) {
- status |= USB_INTRF_STAT_FUNC_RW_CAP;
- if (f->func_wakeup_armed)
- status |= USB_INTRF_STAT_FUNC_RW;
- }
+
+ /* if D5 is not set, then device is not wakeup capable */
+ if (!(f->config->bmAttributes & USB_CONFIG_ATT_WAKEUP))
+ status &= ~(USB_INTRF_STAT_FUNC_RW_CAP | USB_INTRF_STAT_FUNC_RW);
}
put_unaligned_le16(status & 0x0000ffff, req->buf);
@@ -2491,6 +2465,11 @@ int composite_os_desc_req_prepare(struct usb_composite_dev *cdev,
if (!cdev->os_desc_req->buf) {
ret = -ENOMEM;
usb_ep_free_request(ep0, cdev->os_desc_req);
+ /*
+ * Set os_desc_req to NULL so that composite_dev_cleanup()
+ * will not try to free it again.
+ */
+ cdev->os_desc_req = NULL;
goto end;
}
cdev->os_desc_req->context = cdev;
@@ -2552,7 +2531,7 @@ static int composite_bind(struct usb_gadget *gadget,
struct usb_composite_driver *composite = to_cdriver(gdriver);
int status = -ENOMEM;
- cdev = kzalloc(sizeof *cdev, GFP_KERNEL);
+ cdev = kzalloc_obj(*cdev);
if (!cdev)
return status;
diff --git a/drivers/usb/gadget/config.c b/drivers/usb/gadget/config.c
index 95f144a54ed9..256364d4b941 100644
--- a/drivers/usb/gadget/config.c
+++ b/drivers/usb/gadget/config.c
@@ -54,59 +54,6 @@ usb_descriptor_fillbuf(void *buf, unsigned buflen,
EXPORT_SYMBOL_GPL(usb_descriptor_fillbuf);
/**
- * usb_gadget_config_buf - builts a complete configuration descriptor
- * @config: Header for the descriptor, including characteristics such
- * as power requirements and number of interfaces.
- * @buf: Buffer for the resulting configuration descriptor.
- * @length: Length of buffer. If this is not big enough to hold the
- * entire configuration descriptor, an error code will be returned.
- * @desc: Null-terminated vector of pointers to the descriptors (interface,
- * endpoint, etc) defining all functions in this device configuration.
- *
- * This copies descriptors into the response buffer, building a descriptor
- * for that configuration. It returns the buffer length or a negative
- * status code. The config.wTotalLength field is set to match the length
- * of the result, but other descriptor fields (including power usage and
- * interface count) must be set by the caller.
- *
- * Gadget drivers could use this when constructing a config descriptor
- * in response to USB_REQ_GET_DESCRIPTOR. They will need to patch the
- * resulting bDescriptorType value if USB_DT_OTHER_SPEED_CONFIG is needed.
- */
-int usb_gadget_config_buf(
- const struct usb_config_descriptor *config,
- void *buf,
- unsigned length,
- const struct usb_descriptor_header **desc
-)
-{
- struct usb_config_descriptor *cp = buf;
- int len;
-
- /* config descriptor first */
- if (length < USB_DT_CONFIG_SIZE || !desc)
- return -EINVAL;
- *cp = *config;
-
- /* then interface/endpoint/class/vendor/... */
- len = usb_descriptor_fillbuf(USB_DT_CONFIG_SIZE + (u8 *)buf,
- length - USB_DT_CONFIG_SIZE, desc);
- if (len < 0)
- return len;
- len += USB_DT_CONFIG_SIZE;
- if (len > 0xffff)
- return -EINVAL;
-
- /* patch up the config descriptor */
- cp->bLength = USB_DT_CONFIG_SIZE;
- cp->bDescriptorType = USB_DT_CONFIG;
- cp->wTotalLength = cpu_to_le16(len);
- cp->bmAttributes |= USB_CONFIG_ATT_ONE;
- return len;
-}
-EXPORT_SYMBOL_GPL(usb_gadget_config_buf);
-
-/**
* usb_copy_descriptors - copy a vector of USB descriptors
* @src: null-terminated vector to copy
* Context: initialization code, which may sleep
diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c
index fba2a56dae97..183a25f65ac8 100644
--- a/drivers/usb/gadget/configfs.c
+++ b/drivers/usb/gadget/configfs.c
@@ -409,7 +409,7 @@ static void gadget_info_attr_release(struct config_item *item)
kfree(gi);
}
-static struct configfs_item_operations gadget_root_item_ops = {
+static const struct configfs_item_operations gadget_root_item_ops = {
.release = gadget_info_attr_release,
};
@@ -514,7 +514,7 @@ static void config_usb_cfg_unlink(
WARN(1, "Unable to locate function to unbind\n");
}
-static struct configfs_item_operations gadget_config_item_ops = {
+static const struct configfs_item_operations gadget_config_item_ops = {
.release = gadget_config_attr_release,
.allow_link = config_usb_cfg_link,
.drop_link = config_usb_cfg_unlink,
@@ -663,7 +663,7 @@ static void function_drop(
config_item_put(item);
}
-static struct configfs_group_operations functions_ops = {
+static const struct configfs_group_operations functions_ops = {
.make_group = &function_make,
.drop_item = &function_drop,
};
@@ -727,7 +727,7 @@ static struct config_group *config_desc_make(
if (ret)
return ERR_PTR(ret);
- cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
+ cfg = kzalloc_obj(*cfg);
if (!cfg)
return ERR_PTR(-ENOMEM);
cfg->c.label = kstrdup(buf, GFP_KERNEL);
@@ -766,7 +766,7 @@ static void config_desc_drop(
config_item_put(item);
}
-static struct configfs_group_operations config_desc_ops = {
+static const struct configfs_group_operations config_desc_ops = {
.make_group = &config_desc_make,
.drop_item = &config_desc_drop,
};
@@ -799,7 +799,7 @@ static void gadget_language_attr_release(struct config_item *item)
kfree(gs);
}
-static struct configfs_item_operations gadget_language_langid_item_ops = {
+static const struct configfs_item_operations gadget_language_langid_item_ops = {
.release = gadget_language_attr_release,
};
@@ -852,7 +852,7 @@ static void gadget_string_release(struct config_item *item)
kfree(string);
}
-static struct configfs_item_operations gadget_string_item_ops = {
+static const struct configfs_item_operations gadget_string_item_ops = {
.release = gadget_string_release,
};
@@ -870,7 +870,7 @@ static struct config_item *gadget_language_string_make(struct config_group *grou
language = to_gadget_language(&group->cg_item);
- string = kzalloc(sizeof(*string), GFP_KERNEL);
+ string = kzalloc_obj(*string);
if (!string)
return ERR_PTR(-ENOMEM);
@@ -901,7 +901,7 @@ static void gadget_language_string_drop(struct config_group *group,
string->usb_string.id = i++;
}
-static struct configfs_group_operations gadget_language_langid_group_ops = {
+static const struct configfs_group_operations gadget_language_langid_group_ops = {
.make_item = gadget_language_string_make,
.drop_item = gadget_language_string_drop,
};
@@ -922,7 +922,7 @@ static struct config_group *gadget_language_make(struct config_group *group,
int langs = 0;
int ret;
- new = kzalloc(sizeof(*new), GFP_KERNEL);
+ new = kzalloc_obj(*new);
if (!new)
return ERR_PTR(-ENOMEM);
@@ -960,7 +960,7 @@ static void gadget_language_drop(struct config_group *group,
config_item_put(item);
}
-static struct configfs_group_operations gadget_language_group_ops = {
+static const struct configfs_group_operations gadget_language_group_ops = {
.make_group = &gadget_language_make,
.drop_item = &gadget_language_drop,
};
@@ -1065,6 +1065,8 @@ static ssize_t webusb_landingPage_store(struct config_item *item, const char *pa
unsigned int bytes_to_strip = 0;
int l = len;
+ if (!len)
+ return len;
if (page[l - 1] == '\n') {
--l;
++bytes_to_strip;
@@ -1188,6 +1190,8 @@ static ssize_t os_desc_qw_sign_store(struct config_item *item, const char *page,
struct gadget_info *gi = os_desc_item_to_gadget_info(item);
int res, l;
+ if (!len)
+ return len;
l = min_t(int, len, OS_STRING_QW_SIGN_LEN >> 1);
if (page[l - 1] == '\n')
--l;
@@ -1262,7 +1266,7 @@ static void os_desc_unlink(struct config_item *os_desc_ci,
mutex_unlock(&gi->lock);
}
-static struct configfs_item_operations os_desc_ops = {
+static const struct configfs_item_operations os_desc_ops = {
.allow_link = os_desc_link,
.drop_link = os_desc_unlink,
};
@@ -1387,7 +1391,7 @@ static void usb_os_desc_ext_prop_release(struct config_item *item)
kfree(ext_prop); /* frees a whole chunk */
}
-static struct configfs_item_operations ext_prop_ops = {
+static const struct configfs_item_operations ext_prop_ops = {
.release = usb_os_desc_ext_prop_release,
};
@@ -1452,7 +1456,7 @@ static void ext_prop_drop(struct config_group *group, struct config_item *item)
config_item_put(item);
}
-static struct configfs_group_operations interf_grp_ops = {
+static const struct configfs_group_operations interf_grp_ops = {
.make_item = &ext_prop_make,
.drop_item = &ext_prop_drop,
};
@@ -1625,8 +1629,7 @@ configfs_attach_gadget_strings(struct gadget_info *gi)
if (!nlangs)
return NULL;
- gadget_strings = kcalloc(nlangs + 1, /* including NULL terminator */
- sizeof(struct usb_gadget_strings *), GFP_KERNEL);
+ gadget_strings = kzalloc_objs(struct usb_gadget_strings *, nlangs + 1)/* including NULL terminator */;
if (!gadget_strings)
return ERR_PTR(-ENOMEM);
@@ -1641,8 +1644,8 @@ configfs_attach_gadget_strings(struct gadget_info *gi)
goto cleanup;
}
- stringtab = kcalloc(language->nstrings + 1, sizeof(struct usb_string),
- GFP_KERNEL);
+ stringtab = kzalloc_objs(struct usb_string,
+ language->nstrings + 1);
if (!stringtab) {
us = ERR_PTR(-ENOMEM);
goto cleanup;
@@ -1746,6 +1749,8 @@ static int configfs_composite_bind(struct usb_gadget *gadget,
cdev->use_os_string = true;
cdev->b_vendor_code = gi->b_vendor_code;
memcpy(cdev->qw_sign, gi->qw_sign, OS_STRING_QW_SIGN_LEN);
+ } else {
+ cdev->use_os_string = false;
}
if (gadget_is_otg(gadget) && !otg_desc[0]) {
@@ -1986,7 +1991,7 @@ static struct config_group *gadgets_make(
{
struct gadget_info *gi;
- gi = kzalloc(sizeof(*gi), GFP_KERNEL);
+ gi = kzalloc_obj(*gi);
if (!gi)
return ERR_PTR(-ENOMEM);
@@ -2055,7 +2060,7 @@ static void gadgets_drop(struct config_group *group, struct config_item *item)
config_item_put(item);
}
-static struct configfs_group_operations gadgets_ops = {
+static const struct configfs_group_operations gadgets_ops = {
.make_group = &gadgets_make,
.drop_item = &gadgets_drop,
};
diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c
index ed5a92c474e5..30016b805bfd 100644
--- a/drivers/usb/gadget/epautoconf.c
+++ b/drivers/usb/gadget/epautoconf.c
@@ -158,7 +158,7 @@ struct usb_ep *usb_ep_autoconfig(
if (!ep)
return NULL;
- type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+ type = usb_endpoint_type(desc);
/* report (variable) full speed bulk maxpacket */
if (type == USB_ENDPOINT_XFER_BULK) {
diff --git a/drivers/usb/gadget/function/f_acm.c b/drivers/usb/gadget/function/f_acm.c
index 7061720b9732..c628d02bfb1d 100644
--- a/drivers/usb/gadget/function/f_acm.c
+++ b/drivers/usb/gadget/function/f_acm.c
@@ -11,12 +11,15 @@
/* #define VERBOSE_DEBUG */
+#include <linux/cleanup.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/err.h>
+#include <linux/usb/gadget.h>
+
#include "u_serial.h"
@@ -613,6 +616,7 @@ acm_bind(struct usb_configuration *c, struct usb_function *f)
struct usb_string *us;
int status;
struct usb_ep *ep;
+ struct usb_request *request __free(free_usb_request) = NULL;
/* REVISIT might want instance-specific strings to help
* distinguish instances ...
@@ -630,7 +634,7 @@ acm_bind(struct usb_configuration *c, struct usb_function *f)
/* allocate instance-specific interface IDs, and patch descriptors */
status = usb_interface_id(c, f);
if (status < 0)
- goto fail;
+ return status;
acm->ctrl_id = status;
acm_iad_descriptor.bFirstInterface = status;
@@ -639,43 +643,41 @@ acm_bind(struct usb_configuration *c, struct usb_function *f)
status = usb_interface_id(c, f);
if (status < 0)
- goto fail;
+ return status;
acm->data_id = status;
acm_data_interface_desc.bInterfaceNumber = status;
acm_union_desc.bSlaveInterface0 = status;
acm_call_mgmt_descriptor.bDataInterface = status;
- status = -ENODEV;
-
/* allocate instance-specific endpoints */
ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_in_desc);
if (!ep)
- goto fail;
+ return -ENODEV;
acm->port.in = ep;
ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_out_desc);
if (!ep)
- goto fail;
+ return -ENODEV;
acm->port.out = ep;
ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_notify_desc);
if (!ep)
- goto fail;
+ return -ENODEV;
acm->notify = ep;
acm_iad_descriptor.bFunctionProtocol = acm->bInterfaceProtocol;
acm_control_interface_desc.bInterfaceProtocol = acm->bInterfaceProtocol;
/* allocate notification */
- acm->notify_req = gs_alloc_req(ep,
- sizeof(struct usb_cdc_notification) + 2,
- GFP_KERNEL);
- if (!acm->notify_req)
- goto fail;
+ request = gs_alloc_req(ep,
+ sizeof(struct usb_cdc_notification) + 2,
+ GFP_KERNEL);
+ if (!request)
+ return -ENODEV;
- acm->notify_req->complete = acm_cdc_notify_complete;
- acm->notify_req->context = acm;
+ request->complete = acm_cdc_notify_complete;
+ request->context = acm;
/* support all relevant hardware speeds... we expect that when
* hardware is dual speed, all bulk-capable endpoints work at
@@ -692,7 +694,9 @@ acm_bind(struct usb_configuration *c, struct usb_function *f)
status = usb_assign_descriptors(f, acm_fs_function, acm_hs_function,
acm_ss_function, acm_ss_function);
if (status)
- goto fail;
+ return status;
+
+ acm->notify_req = no_free_ptr(request);
dev_dbg(&cdev->gadget->dev,
"acm ttyGS%d: IN/%s OUT/%s NOTIFY/%s\n",
@@ -700,14 +704,6 @@ acm_bind(struct usb_configuration *c, struct usb_function *f)
acm->port.in->name, acm->port.out->name,
acm->notify->name);
return 0;
-
-fail:
- if (acm->notify_req)
- gs_free_req(acm->notify, acm->notify_req);
-
- ERROR(cdev, "%s/%p: can't bind, err %d\n", f->name, f, status);
-
- return status;
}
static void acm_unbind(struct usb_configuration *c, struct usb_function *f)
@@ -752,7 +748,7 @@ static struct usb_function *acm_alloc_func(struct usb_function_instance *fi)
struct f_serial_opts *opts;
struct f_acm *acm;
- acm = kzalloc(sizeof(*acm), GFP_KERNEL);
+ acm = kzalloc_obj(*acm);
if (!acm)
return ERR_PTR(-ENOMEM);
@@ -797,7 +793,7 @@ static void acm_attr_release(struct config_item *item)
usb_put_function_instance(&opts->func_inst);
}
-static struct configfs_item_operations acm_item_ops = {
+static const struct configfs_item_operations acm_item_ops = {
.release = acm_attr_release,
};
@@ -886,7 +882,7 @@ static struct usb_function_instance *acm_alloc_instance(void)
struct f_serial_opts *opts;
int ret;
- opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+ opts = kzalloc_obj(*opts);
if (!opts)
return ERR_PTR(-ENOMEM);
opts->protocol = USB_CDC_ACM_PROTO_AT_V25TER;
diff --git a/drivers/usb/gadget/function/f_ecm.c b/drivers/usb/gadget/function/f_ecm.c
index 80841de845b0..e495bac4efeb 100644
--- a/drivers/usb/gadget/function/f_ecm.c
+++ b/drivers/usb/gadget/function/f_ecm.c
@@ -8,6 +8,7 @@
/* #define VERBOSE_DEBUG */
+#include <linux/cleanup.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -15,6 +16,8 @@
#include <linux/etherdevice.h>
#include <linux/string_choices.h>
+#include <linux/usb/gadget.h>
+
#include "u_ether.h"
#include "u_ether_configfs.h"
#include "u_ecm.h"
@@ -678,24 +681,26 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f)
struct usb_ep *ep;
struct f_ecm_opts *ecm_opts;
+ struct net_device *net __free(detach_gadget) = NULL;
+ struct usb_request *request __free(free_usb_request) = NULL;
if (!can_support_ecm(cdev->gadget))
return -EINVAL;
ecm_opts = container_of(f->fi, struct f_ecm_opts, func_inst);
- mutex_lock(&ecm_opts->lock);
-
- gether_set_gadget(ecm_opts->net, cdev->gadget);
-
- if (!ecm_opts->bound) {
- status = gether_register_netdev(ecm_opts->net);
- ecm_opts->bound = true;
- }
-
- mutex_unlock(&ecm_opts->lock);
- if (status)
- return status;
+ scoped_guard(mutex, &ecm_opts->lock)
+ if (ecm_opts->bind_count == 0 && !ecm_opts->bound) {
+ if (!device_is_registered(&ecm_opts->net->dev)) {
+ gether_set_gadget(ecm_opts->net, cdev->gadget);
+ status = gether_register_netdev(ecm_opts->net);
+ } else
+ status = gether_attach_gadget(ecm_opts->net, cdev->gadget);
+
+ if (status)
+ return status;
+ net = ecm_opts->net;
+ }
ecm_string_defs[1].s = ecm->ethaddr;
@@ -711,7 +716,7 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f)
/* allocate instance-specific interface IDs */
status = usb_interface_id(c, f);
if (status < 0)
- goto fail;
+ return status;
ecm->ctrl_id = status;
ecm_iad_descriptor.bFirstInterface = status;
@@ -720,24 +725,22 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f)
status = usb_interface_id(c, f);
if (status < 0)
- goto fail;
+ return status;
ecm->data_id = status;
ecm_data_nop_intf.bInterfaceNumber = status;
ecm_data_intf.bInterfaceNumber = status;
ecm_union_desc.bSlaveInterface0 = status;
- status = -ENODEV;
-
/* allocate instance-specific endpoints */
ep = usb_ep_autoconfig(cdev->gadget, &fs_ecm_in_desc);
if (!ep)
- goto fail;
+ return -ENODEV;
ecm->port.in_ep = ep;
ep = usb_ep_autoconfig(cdev->gadget, &fs_ecm_out_desc);
if (!ep)
- goto fail;
+ return -ENODEV;
ecm->port.out_ep = ep;
/* NOTE: a status/notification endpoint is *OPTIONAL* but we
@@ -746,20 +749,18 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f)
*/
ep = usb_ep_autoconfig(cdev->gadget, &fs_ecm_notify_desc);
if (!ep)
- goto fail;
+ return -ENODEV;
ecm->notify = ep;
- status = -ENOMEM;
-
/* allocate notification request and buffer */
- ecm->notify_req = usb_ep_alloc_request(ep, GFP_KERNEL);
- if (!ecm->notify_req)
- goto fail;
- ecm->notify_req->buf = kmalloc(ECM_STATUS_BYTECOUNT, GFP_KERNEL);
- if (!ecm->notify_req->buf)
- goto fail;
- ecm->notify_req->context = ecm;
- ecm->notify_req->complete = ecm_notify_complete;
+ request = usb_ep_alloc_request(ep, GFP_KERNEL);
+ if (!request)
+ return -ENOMEM;
+ request->buf = kmalloc(ECM_STATUS_BYTECOUNT, GFP_KERNEL);
+ if (!request->buf)
+ return -ENOMEM;
+ request->context = ecm;
+ request->complete = ecm_notify_complete;
/* support all relevant hardware speeds... we expect that when
* hardware is dual speed, all bulk-capable endpoints work at
@@ -778,7 +779,7 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f)
status = usb_assign_descriptors(f, ecm_fs_function, ecm_hs_function,
ecm_ss_function, ecm_ss_function);
if (status)
- goto fail;
+ return status;
/* NOTE: all that is done without knowing or caring about
* the network link ... which is unavailable to this code
@@ -788,20 +789,15 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f)
ecm->port.open = ecm_open;
ecm->port.close = ecm_close;
+ ecm->notify_req = no_free_ptr(request);
+
+ ecm_opts->bind_count++;
+ retain_and_null_ptr(net);
+
DBG(cdev, "CDC Ethernet: IN/%s OUT/%s NOTIFY/%s\n",
ecm->port.in_ep->name, ecm->port.out_ep->name,
ecm->notify->name);
return 0;
-
-fail:
- if (ecm->notify_req) {
- kfree(ecm->notify_req->buf);
- usb_ep_free_request(ecm->notify, ecm->notify_req);
- }
-
- ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
-
- return status;
}
static inline struct f_ecm_opts *to_f_ecm_opts(struct config_item *item)
@@ -844,7 +840,7 @@ static void ecm_free_inst(struct usb_function_instance *f)
struct f_ecm_opts *opts;
opts = container_of(f, struct f_ecm_opts, func_inst);
- if (opts->bound)
+ if (device_is_registered(&opts->net->dev))
gether_cleanup(netdev_priv(opts->net));
else
free_netdev(opts->net);
@@ -855,7 +851,7 @@ static struct usb_function_instance *ecm_alloc_inst(void)
{
struct f_ecm_opts *opts;
- opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+ opts = kzalloc_obj(*opts);
if (!opts)
return ERR_PTR(-ENOMEM);
mutex_init(&opts->lock);
@@ -892,6 +888,12 @@ static void ecm_resume(struct usb_function *f)
gether_resume(&ecm->port);
}
+static int ecm_get_status(struct usb_function *f)
+{
+ return (f->func_wakeup_armed ? USB_INTRF_STAT_FUNC_RW : 0) |
+ USB_INTRF_STAT_FUNC_RW_CAP;
+}
+
static void ecm_free(struct usb_function *f)
{
struct f_ecm *ecm;
@@ -908,9 +910,12 @@ static void ecm_free(struct usb_function *f)
static void ecm_unbind(struct usb_configuration *c, struct usb_function *f)
{
struct f_ecm *ecm = func_to_ecm(f);
+ struct f_ecm_opts *ecm_opts;
DBG(c->cdev, "ecm unbind\n");
+ ecm_opts = container_of(f->fi, struct f_ecm_opts, func_inst);
+
usb_free_all_descriptors(f);
if (atomic_read(&ecm->notify_count)) {
@@ -920,6 +925,10 @@ static void ecm_unbind(struct usb_configuration *c, struct usb_function *f)
kfree(ecm->notify_req->buf);
usb_ep_free_request(ecm->notify, ecm->notify_req);
+
+ ecm_opts->bind_count--;
+ if (ecm_opts->bind_count == 0 && !ecm_opts->bound)
+ gether_detach_gadget(ecm_opts->net);
}
static struct usb_function *ecm_alloc(struct usb_function_instance *fi)
@@ -929,7 +938,7 @@ static struct usb_function *ecm_alloc(struct usb_function_instance *fi)
int status;
/* allocate and initialize one new instance */
- ecm = kzalloc(sizeof(*ecm), GFP_KERNEL);
+ ecm = kzalloc_obj(*ecm);
if (!ecm)
return ERR_PTR(-ENOMEM);
@@ -960,6 +969,7 @@ static struct usb_function *ecm_alloc(struct usb_function_instance *fi)
ecm->port.func.disable = ecm_disable;
ecm->port.func.free_func = ecm_free;
ecm->port.func.suspend = ecm_suspend;
+ ecm->port.func.get_status = ecm_get_status;
ecm->port.func.resume = ecm_resume;
return &ecm->port.func;
diff --git a/drivers/usb/gadget/function/f_eem.c b/drivers/usb/gadget/function/f_eem.c
index 6de81ea17274..ac37d7c1d168 100644
--- a/drivers/usb/gadget/function/f_eem.c
+++ b/drivers/usb/gadget/function/f_eem.c
@@ -7,6 +7,7 @@
* Copyright (C) 2009 EF Johnson Technologies
*/
+#include <linux/cleanup.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
@@ -251,24 +252,22 @@ static int eem_bind(struct usb_configuration *c, struct usb_function *f)
struct usb_ep *ep;
struct f_eem_opts *eem_opts;
+ struct net_device *net __free(detach_gadget) = NULL;
eem_opts = container_of(f->fi, struct f_eem_opts, func_inst);
- /*
- * in drivers/usb/gadget/configfs.c:configfs_composite_bind()
- * configurations are bound in sequence with list_for_each_entry,
- * in each configuration its functions are bound in sequence
- * with list_for_each_entry, so we assume no race condition
- * with regard to eem_opts->bound access
- */
- if (!eem_opts->bound) {
- mutex_lock(&eem_opts->lock);
- gether_set_gadget(eem_opts->net, cdev->gadget);
- status = gether_register_netdev(eem_opts->net);
- mutex_unlock(&eem_opts->lock);
- if (status)
- return status;
- eem_opts->bound = true;
- }
+
+ scoped_guard(mutex, &eem_opts->lock)
+ if (eem_opts->bind_count == 0 && !eem_opts->bound) {
+ if (!device_is_registered(&eem_opts->net->dev)) {
+ gether_set_gadget(eem_opts->net, cdev->gadget);
+ status = gether_register_netdev(eem_opts->net);
+ } else
+ status = gether_attach_gadget(eem_opts->net, cdev->gadget);
+
+ if (status)
+ return status;
+ net = eem_opts->net;
+ }
us = usb_gstrings_attach(cdev, eem_strings,
ARRAY_SIZE(eem_string_defs));
@@ -279,21 +278,19 @@ static int eem_bind(struct usb_configuration *c, struct usb_function *f)
/* allocate instance-specific interface IDs */
status = usb_interface_id(c, f);
if (status < 0)
- goto fail;
+ return status;
eem->ctrl_id = status;
eem_intf.bInterfaceNumber = status;
- status = -ENODEV;
-
/* allocate instance-specific endpoints */
ep = usb_ep_autoconfig(cdev->gadget, &eem_fs_in_desc);
if (!ep)
- goto fail;
+ return -ENODEV;
eem->port.in_ep = ep;
ep = usb_ep_autoconfig(cdev->gadget, &eem_fs_out_desc);
if (!ep)
- goto fail;
+ return -ENODEV;
eem->port.out_ep = ep;
/* support all relevant hardware speeds... we expect that when
@@ -309,16 +306,14 @@ static int eem_bind(struct usb_configuration *c, struct usb_function *f)
status = usb_assign_descriptors(f, eem_fs_function, eem_hs_function,
eem_ss_function, eem_ss_function);
if (status)
- goto fail;
+ return status;
+
+ eem_opts->bind_count++;
+ retain_and_null_ptr(net);
DBG(cdev, "CDC Ethernet (EEM): IN/%s OUT/%s\n",
eem->port.in_ep->name, eem->port.out_ep->name);
return 0;
-
-fail:
- ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
-
- return status;
}
static void eem_cmd_complete(struct usb_ep *ep, struct usb_request *req)
@@ -462,7 +457,7 @@ static int eem_unwrap(struct gether *port,
goto next;
}
- ctx = kmalloc(sizeof(*ctx), GFP_KERNEL);
+ ctx = kmalloc_obj(*ctx);
if (!ctx) {
kfree(req->buf);
usb_ep_free_request(ep, req);
@@ -477,8 +472,13 @@ static int eem_unwrap(struct gether *port,
req->complete = eem_cmd_complete;
req->zero = 1;
req->context = ctx;
- if (usb_ep_queue(port->in_ep, req, GFP_ATOMIC))
+ if (usb_ep_queue(port->in_ep, req, GFP_ATOMIC)) {
DBG(cdev, "echo response queue fail\n");
+ kfree(ctx);
+ kfree(req->buf);
+ usb_ep_free_request(ep, req);
+ dev_kfree_skb_any(skb2);
+ }
break;
case 1: /* echo response */
@@ -592,7 +592,7 @@ static void eem_free_inst(struct usb_function_instance *f)
struct f_eem_opts *opts;
opts = container_of(f, struct f_eem_opts, func_inst);
- if (opts->bound)
+ if (device_is_registered(&opts->net->dev))
gether_cleanup(netdev_priv(opts->net));
else
free_netdev(opts->net);
@@ -603,7 +603,7 @@ static struct usb_function_instance *eem_alloc_inst(void)
{
struct f_eem_opts *opts;
- opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+ opts = kzalloc_obj(*opts);
if (!opts)
return ERR_PTR(-ENOMEM);
mutex_init(&opts->lock);
@@ -635,9 +635,17 @@ static void eem_free(struct usb_function *f)
static void eem_unbind(struct usb_configuration *c, struct usb_function *f)
{
+ struct f_eem_opts *opts;
+
DBG(c->cdev, "eem unbind\n");
+ opts = container_of(f->fi, struct f_eem_opts, func_inst);
+
usb_free_all_descriptors(f);
+
+ opts->bind_count--;
+ if (opts->bind_count == 0 && !opts->bound)
+ gether_detach_gadget(opts->net);
}
static struct usb_function *eem_alloc(struct usb_function_instance *fi)
@@ -646,7 +654,7 @@ static struct usb_function *eem_alloc(struct usb_function_instance *fi)
struct f_eem_opts *opts;
/* allocate and initialize one new instance */
- eem = kzalloc(sizeof(*eem), GFP_KERNEL);
+ eem = kzalloc_obj(*eem);
if (!eem)
return ERR_PTR(-ENOMEM);
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index 2dea9e42a0f8..002c3441bea3 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -59,7 +59,6 @@ static struct ffs_data *__must_check ffs_data_new(const char *dev_name)
__attribute__((malloc));
/* Opened counter handling. */
-static void ffs_data_opened(struct ffs_data *ffs);
static void ffs_data_closed(struct ffs_data *ffs);
/* Called with ffs->mutex held; take over ownership of data. */
@@ -160,8 +159,6 @@ struct ffs_epfile {
struct ffs_data *ffs;
struct ffs_ep *ep; /* P: ffs->eps_lock */
- struct dentry *dentry;
-
/*
* Buffer for holding data from partial reads which may happen since
* we’re rounding user read requests to a multiple of a max packet size.
@@ -271,11 +268,11 @@ struct ffs_desc_helper {
};
static int __must_check ffs_epfiles_create(struct ffs_data *ffs);
-static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count);
+static void ffs_epfiles_destroy(struct super_block *sb,
+ struct ffs_epfile *epfiles, unsigned count);
-static struct dentry *
-ffs_sb_create_file(struct super_block *sb, const char *name, void *data,
- const struct file_operations *fops);
+static int ffs_sb_create_file(struct super_block *sb, const char *name,
+ void *data, const struct file_operations *fops);
/* Devices management *******************************************************/
@@ -638,15 +635,26 @@ done_mutex:
return ret;
}
+
+static void ffs_data_reset(struct ffs_data *ffs);
+
static int ffs_ep0_open(struct inode *inode, struct file *file)
{
- struct ffs_data *ffs = inode->i_private;
+ struct ffs_data *ffs = inode->i_sb->s_fs_info;
- if (ffs->state == FFS_CLOSING)
+ spin_lock_irq(&ffs->eps_lock);
+ if (ffs->state == FFS_CLOSING) {
+ spin_unlock_irq(&ffs->eps_lock);
return -EBUSY;
-
+ }
+ if (!ffs->opened++ && ffs->state == FFS_DEACTIVATED) {
+ ffs->state = FFS_CLOSING;
+ spin_unlock_irq(&ffs->eps_lock);
+ ffs_data_reset(ffs);
+ } else {
+ spin_unlock_irq(&ffs->eps_lock);
+ }
file->private_data = ffs;
- ffs_data_opened(ffs);
return stream_open(inode, file);
}
@@ -806,7 +814,7 @@ static void *ffs_build_sg_list(struct sg_table *sgt, size_t sz)
return NULL;
n_pages = PAGE_ALIGN(sz) >> PAGE_SHIFT;
- pages = kvmalloc_array(n_pages, sizeof(struct page *), GFP_KERNEL);
+ pages = kvmalloc_objs(struct page *, n_pages);
if (!pages) {
vfree(vaddr);
@@ -854,7 +862,6 @@ static void ffs_user_copy_worker(struct work_struct *work)
work);
int ret = io_data->status;
bool kiocb_has_eventfd = io_data->kiocb->ki_flags & IOCB_EVENTFD;
- unsigned long flags;
if (io_data->read && ret > 0) {
kthread_use_mm(io_data->mm);
@@ -867,10 +874,7 @@ static void ffs_user_copy_worker(struct work_struct *work)
if (io_data->ffs->ffs_eventfd && !kiocb_has_eventfd)
eventfd_signal(io_data->ffs->ffs_eventfd);
- spin_lock_irqsave(&io_data->ffs->eps_lock, flags);
usb_ep_free_request(io_data->ep, io_data->req);
- io_data->req = NULL;
- spin_unlock_irqrestore(&io_data->ffs->eps_lock, flags);
if (io_data->read)
kfree(io_data->to_free);
@@ -953,7 +957,7 @@ static ssize_t __ffs_epfile_read_data(struct ffs_epfile *epfile,
data_len, ret);
data_len -= ret;
- buf = kmalloc(struct_size(buf, storage, data_len), GFP_KERNEL);
+ buf = kmalloc_flex(*buf, storage, data_len);
if (!buf)
return -ENOMEM;
buf->length = data_len;
@@ -1197,33 +1201,41 @@ error:
static int
ffs_epfile_open(struct inode *inode, struct file *file)
{
- struct ffs_epfile *epfile = inode->i_private;
+ struct ffs_data *ffs = inode->i_sb->s_fs_info;
+ struct ffs_epfile *epfile;
- if (WARN_ON(epfile->ffs->state != FFS_ACTIVE))
+ spin_lock_irq(&ffs->eps_lock);
+ if (!ffs->opened) {
+ spin_unlock_irq(&ffs->eps_lock);
return -ENODEV;
+ }
+ /*
+ * we want the state to be FFS_ACTIVE; FFS_ACTIVE alone is
+ * not enough, though - we might have been through FFS_CLOSING
+ * and back to FFS_ACTIVE, with our file already removed.
+ */
+ epfile = smp_load_acquire(&inode->i_private);
+ if (unlikely(ffs->state != FFS_ACTIVE || !epfile)) {
+ spin_unlock_irq(&ffs->eps_lock);
+ return -ENODEV;
+ }
+ ffs->opened++;
+ spin_unlock_irq(&ffs->eps_lock);
file->private_data = epfile;
- ffs_data_opened(epfile->ffs);
-
return stream_open(inode, file);
}
static int ffs_aio_cancel(struct kiocb *kiocb)
{
struct ffs_io_data *io_data = kiocb->private;
- struct ffs_epfile *epfile = kiocb->ki_filp->private_data;
- unsigned long flags;
int value;
- spin_lock_irqsave(&epfile->ffs->eps_lock, flags);
-
if (io_data && io_data->ep && io_data->req)
value = usb_ep_dequeue(io_data->ep, io_data->req);
else
value = -EINVAL;
- spin_unlock_irqrestore(&epfile->ffs->eps_lock, flags);
-
return value;
}
@@ -1233,7 +1245,7 @@ static ssize_t ffs_epfile_write_iter(struct kiocb *kiocb, struct iov_iter *from)
ssize_t res;
if (!is_sync_kiocb(kiocb)) {
- p = kzalloc(sizeof(io_data), GFP_KERNEL);
+ p = kzalloc_obj(io_data);
if (!p)
return -ENOMEM;
p->aio = true;
@@ -1268,7 +1280,7 @@ static ssize_t ffs_epfile_read_iter(struct kiocb *kiocb, struct iov_iter *to)
ssize_t res;
if (!is_sync_kiocb(kiocb)) {
- p = kzalloc(sizeof(io_data), GFP_KERNEL);
+ p = kzalloc_obj(io_data);
if (!p)
return -ENOMEM;
p->aio = true;
@@ -1316,9 +1328,7 @@ static void ffs_dmabuf_release(struct kref *ref)
struct dma_buf *dmabuf = attach->dmabuf;
pr_vdebug("FFS DMABUF release\n");
- dma_resv_lock(dmabuf->resv, NULL);
- dma_buf_unmap_attachment(attach, priv->sgt, priv->dir);
- dma_resv_unlock(dmabuf->resv);
+ dma_buf_unmap_attachment_unlocked(attach, priv->sgt, priv->dir);
dma_buf_detach(attach->dmabuf, attach);
dma_buf_put(dmabuf);
@@ -1342,7 +1352,7 @@ static void ffs_dmabuf_put(struct dma_buf_attachment *attach)
static int
ffs_epfile_release(struct inode *inode, struct file *file)
{
- struct ffs_epfile *epfile = inode->i_private;
+ struct ffs_epfile *epfile = file->private_data;
struct ffs_dmabuf_priv *priv, *tmp;
struct ffs_data *ffs = epfile->ffs;
@@ -1493,13 +1503,13 @@ static int ffs_dmabuf_attach(struct file *file, int fd)
goto err_dmabuf_put;
}
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ priv = kzalloc_obj(*priv);
if (!priv) {
err = -ENOMEM;
goto err_dmabuf_detach;
}
- dir = epfile->in ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+ dir = epfile->in ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
err = ffs_dma_resv_lock(dmabuf, nonblock);
if (err)
@@ -1629,7 +1639,7 @@ static int ffs_dmabuf_transfer(struct file *file,
/* Make sure we don't have writers */
timeout = nonblock ? 0 : msecs_to_jiffies(DMABUF_ENQUEUE_TIMEOUT_MS);
retl = dma_resv_wait_timeout(dmabuf->resv,
- dma_resv_usage_rw(epfile->in),
+ dma_resv_usage_rw(!epfile->in),
true, timeout);
if (retl == 0)
retl = -EBUSY;
@@ -1642,7 +1652,7 @@ static int ffs_dmabuf_transfer(struct file *file,
if (ret)
goto err_resv_unlock;
- fence = kmalloc(sizeof(*fence), GFP_KERNEL);
+ fence = kmalloc_obj(*fence);
if (!fence) {
ret = -ENOMEM;
goto err_resv_unlock;
@@ -1674,7 +1684,7 @@ static int ffs_dmabuf_transfer(struct file *file,
dma_fence_init(&fence->base, &ffs_dmabuf_fence_ops,
&priv->lock, priv->context, seqno);
- resv_dir = epfile->in ? DMA_RESV_USAGE_WRITE : DMA_RESV_USAGE_READ;
+ resv_dir = epfile->in ? DMA_RESV_USAGE_READ : DMA_RESV_USAGE_WRITE;
dma_resv_add_fence(dmabuf->resv, &fence->base, resv_dir);
dma_resv_unlock(dmabuf->resv);
@@ -1734,10 +1744,8 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code,
{
int fd;
- if (copy_from_user(&fd, (void __user *)value, sizeof(fd))) {
- ret = -EFAULT;
- break;
- }
+ if (copy_from_user(&fd, (void __user *)value, sizeof(fd)))
+ return -EFAULT;
return ffs_dmabuf_attach(file, fd);
}
@@ -1745,10 +1753,8 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code,
{
int fd;
- if (copy_from_user(&fd, (void __user *)value, sizeof(fd))) {
- ret = -EFAULT;
- break;
- }
+ if (copy_from_user(&fd, (void __user *)value, sizeof(fd)))
+ return -EFAULT;
return ffs_dmabuf_detach(file, fd);
}
@@ -1756,10 +1762,8 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code,
{
struct usb_ffs_dmabuf_transfer_req req;
- if (copy_from_user(&req, (void __user *)value, sizeof(req))) {
- ret = -EFAULT;
- break;
- }
+ if (copy_from_user(&req, (void __user *)value, sizeof(req)))
+ return -EFAULT;
return ffs_dmabuf_transfer(file, &req);
}
@@ -1876,32 +1880,32 @@ ffs_sb_make_inode(struct super_block *sb, void *data,
}
/* Create "regular" file */
-static struct dentry *ffs_sb_create_file(struct super_block *sb,
- const char *name, void *data,
- const struct file_operations *fops)
+static int ffs_sb_create_file(struct super_block *sb, const char *name,
+ void *data, const struct file_operations *fops)
{
struct ffs_data *ffs = sb->s_fs_info;
struct dentry *dentry;
struct inode *inode;
- dentry = d_alloc_name(sb->s_root, name);
- if (!dentry)
- return NULL;
-
inode = ffs_sb_make_inode(sb, data, fops, NULL, &ffs->file_perms);
- if (!inode) {
- dput(dentry);
- return NULL;
+ if (!inode)
+ return -ENOMEM;
+ dentry = simple_start_creating(sb->s_root, name);
+ if (IS_ERR(dentry)) {
+ iput(inode);
+ return PTR_ERR(dentry);
}
- d_add(dentry, inode);
- return dentry;
+ d_make_persistent(dentry, inode);
+
+ simple_done_creating(dentry);
+ return 0;
}
/* Super block */
static const struct super_operations ffs_sb_operations = {
.statfs = simple_statfs,
- .drop_inode = generic_delete_inode,
+ .drop_inode = inode_just_drop,
};
struct ffs_sb_fill_data {
@@ -1938,10 +1942,7 @@ static int ffs_sb_fill(struct super_block *sb, struct fs_context *fc)
return -ENOMEM;
/* EP0 file */
- if (!ffs_sb_create_file(sb, "ep0", ffs, &ffs_ep0_operations))
- return -ENOMEM;
-
- return 0;
+ return ffs_sb_create_file(sb, "ep0", ffs, &ffs_ep0_operations);
}
enum {
@@ -2066,7 +2067,7 @@ static int ffs_fs_init_fs_context(struct fs_context *fc)
{
struct ffs_sb_fill_data *ctx;
- ctx = kzalloc(sizeof(struct ffs_sb_fill_data), GFP_KERNEL);
+ ctx = kzalloc_obj(struct ffs_sb_fill_data);
if (!ctx)
return -ENOMEM;
@@ -2084,9 +2085,16 @@ static int ffs_fs_init_fs_context(struct fs_context *fc)
static void
ffs_fs_kill_sb(struct super_block *sb)
{
- kill_litter_super(sb);
- if (sb->s_fs_info)
- ffs_data_closed(sb->s_fs_info);
+ kill_anon_super(sb);
+ if (sb->s_fs_info) {
+ struct ffs_data *ffs = sb->s_fs_info;
+ ffs->state = FFS_CLOSING;
+ ffs_data_reset(ffs);
+ // no configfs accesses from that point on,
+ // so no further schedule_work() is possible
+ cancel_work_sync(&ffs->reset_work);
+ ffs_data_put(ffs);
+ }
}
static struct file_system_type ffs_fs_type = {
@@ -2124,23 +2132,12 @@ static void functionfs_cleanup(void)
/* ffs_data and ffs_function construction and destruction code **************/
static void ffs_data_clear(struct ffs_data *ffs);
-static void ffs_data_reset(struct ffs_data *ffs);
static void ffs_data_get(struct ffs_data *ffs)
{
refcount_inc(&ffs->ref);
}
-static void ffs_data_opened(struct ffs_data *ffs)
-{
- refcount_inc(&ffs->ref);
- if (atomic_add_return(1, &ffs->opened) == 1 &&
- ffs->state == FFS_DEACTIVATED) {
- ffs->state = FFS_CLOSING;
- ffs_data_reset(ffs);
- }
-}
-
static void ffs_data_put(struct ffs_data *ffs)
{
if (refcount_dec_and_test(&ffs->ref)) {
@@ -2158,40 +2155,35 @@ static void ffs_data_put(struct ffs_data *ffs)
static void ffs_data_closed(struct ffs_data *ffs)
{
- struct ffs_epfile *epfiles;
- unsigned long flags;
-
- if (atomic_dec_and_test(&ffs->opened)) {
- if (ffs->no_disconnect) {
- ffs->state = FFS_DEACTIVATED;
- spin_lock_irqsave(&ffs->eps_lock, flags);
- epfiles = ffs->epfiles;
- ffs->epfiles = NULL;
- spin_unlock_irqrestore(&ffs->eps_lock,
- flags);
-
- if (epfiles)
- ffs_epfiles_destroy(epfiles,
- ffs->eps_count);
-
- if (ffs->setup_state == FFS_SETUP_PENDING)
- __ffs_ep0_stall(ffs);
- } else {
- ffs->state = FFS_CLOSING;
- ffs_data_reset(ffs);
- }
+ spin_lock_irq(&ffs->eps_lock);
+ if (--ffs->opened) { // not the last opener?
+ spin_unlock_irq(&ffs->eps_lock);
+ return;
}
- if (atomic_read(&ffs->opened) < 0) {
+ if (ffs->no_disconnect) {
+ struct ffs_epfile *epfiles;
+
+ ffs->state = FFS_DEACTIVATED;
+ epfiles = ffs->epfiles;
+ ffs->epfiles = NULL;
+ spin_unlock_irq(&ffs->eps_lock);
+
+ if (epfiles)
+ ffs_epfiles_destroy(ffs->sb, epfiles,
+ ffs->eps_count);
+
+ if (ffs->setup_state == FFS_SETUP_PENDING)
+ __ffs_ep0_stall(ffs);
+ } else {
ffs->state = FFS_CLOSING;
+ spin_unlock_irq(&ffs->eps_lock);
ffs_data_reset(ffs);
}
-
- ffs_data_put(ffs);
}
static struct ffs_data *ffs_data_new(const char *dev_name)
{
- struct ffs_data *ffs = kzalloc(sizeof *ffs, GFP_KERNEL);
+ struct ffs_data *ffs = kzalloc_obj(*ffs);
if (!ffs)
return NULL;
@@ -2202,7 +2194,7 @@ static struct ffs_data *ffs_data_new(const char *dev_name)
}
refcount_set(&ffs->ref, 1);
- atomic_set(&ffs->opened, 0);
+ ffs->opened = 0;
ffs->state = FFS_READ_DESCRIPTORS;
mutex_init(&ffs->mutex);
spin_lock_init(&ffs->eps_lock);
@@ -2236,7 +2228,7 @@ static void ffs_data_clear(struct ffs_data *ffs)
* copy of epfile will save us from use-after-free.
*/
if (epfiles) {
- ffs_epfiles_destroy(epfiles, ffs->eps_count);
+ ffs_epfiles_destroy(ffs->sb, epfiles, ffs->eps_count);
ffs->epfiles = NULL;
}
@@ -2254,6 +2246,7 @@ static void ffs_data_reset(struct ffs_data *ffs)
{
ffs_data_clear(ffs);
+ spin_lock_irq(&ffs->eps_lock);
ffs->raw_descs_data = NULL;
ffs->raw_descs = NULL;
ffs->raw_strings = NULL;
@@ -2277,6 +2270,7 @@ static void ffs_data_reset(struct ffs_data *ffs)
ffs->ms_os_descs_ext_prop_count = 0;
ffs->ms_os_descs_ext_prop_name_len = 0;
ffs->ms_os_descs_ext_prop_data_len = 0;
+ spin_unlock_irq(&ffs->eps_lock);
}
@@ -2333,9 +2327,10 @@ static int ffs_epfiles_create(struct ffs_data *ffs)
{
struct ffs_epfile *epfile, *epfiles;
unsigned i, count;
+ int err;
count = ffs->eps_count;
- epfiles = kcalloc(count, sizeof(*epfiles), GFP_KERNEL);
+ epfiles = kzalloc_objs(*epfiles, count);
if (!epfiles)
return -ENOMEM;
@@ -2349,12 +2344,11 @@ static int ffs_epfiles_create(struct ffs_data *ffs)
sprintf(epfile->name, "ep%02x", ffs->eps_addrmap[i]);
else
sprintf(epfile->name, "ep%u", i);
- epfile->dentry = ffs_sb_create_file(ffs->sb, epfile->name,
- epfile,
- &ffs_epfile_operations);
- if (!epfile->dentry) {
- ffs_epfiles_destroy(epfiles, i - 1);
- return -ENOMEM;
+ err = ffs_sb_create_file(ffs->sb, epfile->name,
+ epfile, &ffs_epfile_operations);
+ if (err) {
+ ffs_epfiles_destroy(ffs->sb, epfiles, i - 1);
+ return err;
}
}
@@ -2362,17 +2356,20 @@ static int ffs_epfiles_create(struct ffs_data *ffs)
return 0;
}
-static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count)
+static void clear_one(struct dentry *dentry)
+{
+ smp_store_release(&dentry->d_inode->i_private, NULL);
+}
+
+static void ffs_epfiles_destroy(struct super_block *sb,
+ struct ffs_epfile *epfiles, unsigned count)
{
struct ffs_epfile *epfile = epfiles;
+ struct dentry *root = sb->s_root;
for (; count; --count, ++epfile) {
BUG_ON(mutex_is_locked(&epfile->mutex));
- if (epfile->dentry) {
- d_delete(epfile->dentry);
- dput(epfile->dentry);
- epfile->dentry = NULL;
- }
+ simple_remove_by_name(root, epfile->name, clear_one);
}
kfree(epfiles);
@@ -2418,7 +2415,12 @@ static int ffs_func_eps_enable(struct ffs_function *func)
ep = func->eps;
epfile = ffs->epfiles;
count = ffs->eps_count;
- while(count--) {
+ if (!epfile) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ while (count--) {
ep->ep->driver_data = ep;
ret = config_ep_by_speed(func->gadget, &func->function, ep->ep);
@@ -2442,6 +2444,7 @@ static int ffs_func_eps_enable(struct ffs_function *func)
}
wake_up_interruptible(&ffs->wait);
+done:
spin_unlock_irqrestore(&func->ffs->eps_lock, flags);
return ret;
@@ -3295,7 +3298,7 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep,
if (ffs_ep->descs[ep_desc_id]) {
pr_err("two %sspeed descriptors for EP %d\n",
speed_names[ep_desc_id],
- ds->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+ usb_endpoint_num(ds));
return -EINVAL;
}
ffs_ep->descs[ep_desc_id] = ds;
@@ -3735,6 +3738,7 @@ static int ffs_func_set_alt(struct usb_function *f,
{
struct ffs_function *func = ffs_func_from_usb(f);
struct ffs_data *ffs = func->ffs;
+ unsigned long flags;
int ret = 0, intf;
if (alt > MAX_ALT_SETTINGS)
@@ -3747,12 +3751,15 @@ static int ffs_func_set_alt(struct usb_function *f,
if (ffs->func)
ffs_func_eps_disable(ffs->func);
+ spin_lock_irqsave(&ffs->eps_lock, flags);
if (ffs->state == FFS_DEACTIVATED) {
ffs->state = FFS_CLOSING;
+ spin_unlock_irqrestore(&ffs->eps_lock, flags);
INIT_WORK(&ffs->reset_work, ffs_reset_work);
schedule_work(&ffs->reset_work);
return -ENODEV;
}
+ spin_unlock_irqrestore(&ffs->eps_lock, flags);
if (ffs->state != FFS_ACTIVE)
return -ENODEV;
@@ -3770,16 +3777,20 @@ static void ffs_func_disable(struct usb_function *f)
{
struct ffs_function *func = ffs_func_from_usb(f);
struct ffs_data *ffs = func->ffs;
+ unsigned long flags;
if (ffs->func)
ffs_func_eps_disable(ffs->func);
+ spin_lock_irqsave(&ffs->eps_lock, flags);
if (ffs->state == FFS_DEACTIVATED) {
ffs->state = FFS_CLOSING;
+ spin_unlock_irqrestore(&ffs->eps_lock, flags);
INIT_WORK(&ffs->reset_work, ffs_reset_work);
schedule_work(&ffs->reset_work);
return;
}
+ spin_unlock_irqrestore(&ffs->eps_lock, flags);
if (ffs->state == FFS_ACTIVE) {
ffs->func = NULL;
@@ -3983,7 +3994,7 @@ static void ffs_attr_release(struct config_item *item)
usb_put_function_instance(&opts->func_inst);
}
-static struct configfs_item_operations ffs_item_ops = {
+static const struct configfs_item_operations ffs_item_ops = {
.release = ffs_attr_release,
};
@@ -4020,7 +4031,7 @@ static struct usb_function_instance *ffs_alloc_inst(void)
struct f_fs_opts *opts;
struct ffs_dev *dev;
- opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+ opts = kzalloc_obj(*opts);
if (!opts)
return ERR_PTR(-ENOMEM);
@@ -4096,7 +4107,7 @@ static struct usb_function *ffs_alloc(struct usb_function_instance *fi)
{
struct ffs_function *func;
- func = kzalloc(sizeof(*func), GFP_KERNEL);
+ func = kzalloc_obj(*func);
if (!func)
return ERR_PTR(-ENOMEM);
@@ -4127,7 +4138,7 @@ static struct ffs_dev *_ffs_alloc_dev(void)
if (_ffs_get_single_dev())
return ERR_PTR(-EBUSY);
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ dev = kzalloc_obj(*dev);
if (!dev)
return ERR_PTR(-ENOMEM);
diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c
index 740311c4fa24..c5a12a6760ea 100644
--- a/drivers/usb/gadget/function/f_hid.c
+++ b/drivers/usb/gadget/function/f_hid.c
@@ -62,6 +62,9 @@ struct f_hidg {
unsigned short report_desc_length;
char *report_desc;
unsigned short report_length;
+ unsigned char interval;
+ bool interval_user_set;
+
/*
* use_out_ep - if true, the OUT Endpoint (interrupt out method)
* will be used to receive reports from the host
@@ -75,6 +78,7 @@ struct f_hidg {
/* recv report */
spinlock_t read_spinlock;
wait_queue_head_t read_queue;
+ bool disabled;
/* recv report - interrupt out only (use_out_ep == 1) */
struct list_head completed_out_req;
unsigned int qlen;
@@ -102,7 +106,7 @@ struct f_hidg {
struct list_head report_list;
struct device dev;
- struct cdev cdev;
+ struct cdev *cdev;
struct usb_function func;
struct usb_ep *in_ep;
@@ -144,8 +148,8 @@ static struct hid_descriptor hidg_desc = {
.bcdHID = cpu_to_le16(0x0101),
.bCountryCode = 0x00,
.bNumDescriptors = 0x1,
- /*.desc[0].bDescriptorType = DYNAMIC */
- /*.desc[0].wDescriptorLenght = DYNAMIC */
+ /*.rpt_desc.bDescriptorType = DYNAMIC */
+ /*.rpt_desc.wDescriptorLength = DYNAMIC */
};
/* Super-Speed Support */
@@ -156,10 +160,7 @@ static struct usb_endpoint_descriptor hidg_ss_in_ep_desc = {
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_INT,
/*.wMaxPacketSize = DYNAMIC */
- .bInterval = 4, /* FIXME: Add this field in the
- * HID gadget configuration?
- * (struct hidg_func_descriptor)
- */
+ /*.bInterval = DYNAMIC */
};
static struct usb_ss_ep_comp_descriptor hidg_ss_in_comp_desc = {
@@ -177,10 +178,7 @@ static struct usb_endpoint_descriptor hidg_ss_out_ep_desc = {
.bEndpointAddress = USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_INT,
/*.wMaxPacketSize = DYNAMIC */
- .bInterval = 4, /* FIXME: Add this field in the
- * HID gadget configuration?
- * (struct hidg_func_descriptor)
- */
+ /*.bInterval = DYNAMIC */
};
static struct usb_ss_ep_comp_descriptor hidg_ss_out_comp_desc = {
@@ -218,10 +216,7 @@ static struct usb_endpoint_descriptor hidg_hs_in_ep_desc = {
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_INT,
/*.wMaxPacketSize = DYNAMIC */
- .bInterval = 4, /* FIXME: Add this field in the
- * HID gadget configuration?
- * (struct hidg_func_descriptor)
- */
+ /* .bInterval = DYNAMIC */
};
static struct usb_endpoint_descriptor hidg_hs_out_ep_desc = {
@@ -230,10 +225,7 @@ static struct usb_endpoint_descriptor hidg_hs_out_ep_desc = {
.bEndpointAddress = USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_INT,
/*.wMaxPacketSize = DYNAMIC */
- .bInterval = 4, /* FIXME: Add this field in the
- * HID gadget configuration?
- * (struct hidg_func_descriptor)
- */
+ /*.bInterval = DYNAMIC */
};
static struct usb_descriptor_header *hidg_hs_descriptors_intout[] = {
@@ -259,10 +251,7 @@ static struct usb_endpoint_descriptor hidg_fs_in_ep_desc = {
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_INT,
/*.wMaxPacketSize = DYNAMIC */
- .bInterval = 10, /* FIXME: Add this field in the
- * HID gadget configuration?
- * (struct hidg_func_descriptor)
- */
+ /*.bInterval = DYNAMIC */
};
static struct usb_endpoint_descriptor hidg_fs_out_ep_desc = {
@@ -271,10 +260,7 @@ static struct usb_endpoint_descriptor hidg_fs_out_ep_desc = {
.bEndpointAddress = USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_INT,
/*.wMaxPacketSize = DYNAMIC */
- .bInterval = 10, /* FIXME: Add this field in the
- * HID gadget configuration?
- * (struct hidg_func_descriptor)
- */
+ /*.bInterval = DYNAMIC */
};
static struct usb_descriptor_header *hidg_fs_descriptors_intout[] = {
@@ -329,7 +315,7 @@ static ssize_t f_hidg_intout_read(struct file *file, char __user *buffer,
spin_lock_irqsave(&hidg->read_spinlock, flags);
-#define READ_COND_INTOUT (!list_empty(&hidg->completed_out_req))
+#define READ_COND_INTOUT (!list_empty(&hidg->completed_out_req) || hidg->disabled)
/* wait for at least one buffer to complete */
while (!READ_COND_INTOUT) {
@@ -343,6 +329,11 @@ static ssize_t f_hidg_intout_read(struct file *file, char __user *buffer,
spin_lock_irqsave(&hidg->read_spinlock, flags);
}
+ if (hidg->disabled) {
+ spin_unlock_irqrestore(&hidg->read_spinlock, flags);
+ return -ESHUTDOWN;
+ }
+
/* pick the first one */
list = list_first_entry(&hidg->completed_out_req,
struct f_hidg_req_list, list);
@@ -387,7 +378,7 @@ static ssize_t f_hidg_intout_read(struct file *file, char __user *buffer,
return count;
}
-#define READ_COND_SSREPORT (hidg->set_report_buf != NULL)
+#define READ_COND_SSREPORT (hidg->set_report_buf != NULL || hidg->disabled)
static ssize_t f_hidg_ssreport_read(struct file *file, char __user *buffer,
size_t count, loff_t *ptr)
@@ -520,7 +511,7 @@ try_again:
}
req->status = 0;
- req->zero = 0;
+ req->zero = 1;
req->length = count;
req->complete = f_hidg_req_complete;
req->context = hidg;
@@ -659,7 +650,7 @@ static int f_hidg_get_report(struct file *file, struct usb_hidg_report __user *b
struct report_entry *ptr;
__u8 report_id;
- entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+ entry = kmalloc_obj(*entry);
if (!entry)
return -ENOMEM;
@@ -758,8 +749,9 @@ static int f_hidg_release(struct inode *inode, struct file *fd)
static int f_hidg_open(struct inode *inode, struct file *fd)
{
+ struct kobject *parent = inode->i_cdev->kobj.parent;
struct f_hidg *hidg =
- container_of(inode->i_cdev, struct f_hidg, cdev);
+ container_of(parent, struct f_hidg, dev.kobj);
fd->private_data = hidg;
@@ -784,7 +776,7 @@ static void hidg_intout_complete(struct usb_ep *ep, struct usb_request *req)
switch (req->status) {
case 0:
- req_list = kzalloc(sizeof(*req_list), GFP_ATOMIC);
+ req_list = kzalloc_obj(*req_list, GFP_ATOMIC);
if (!req_list) {
ERROR(cdev, "Unable to allocate mem for req_list\n");
goto free_req;
@@ -939,8 +931,8 @@ static int hidg_setup(struct usb_function *f,
struct hid_descriptor hidg_desc_copy = hidg_desc;
VDBG(cdev, "USB_REQ_GET_DESCRIPTOR: HID\n");
- hidg_desc_copy.desc[0].bDescriptorType = HID_DT_REPORT;
- hidg_desc_copy.desc[0].wDescriptorLength =
+ hidg_desc_copy.rpt_desc.bDescriptorType = HID_DT_REPORT;
+ hidg_desc_copy.rpt_desc.wDescriptorLength =
cpu_to_le16(hidg->report_desc_length);
length = min_t(unsigned short, length,
@@ -976,7 +968,7 @@ stall:
return -EOPNOTSUPP;
respond:
- req->zero = 0;
+ req->zero = 1;
req->length = length;
status = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
if (status < 0)
@@ -1012,6 +1004,11 @@ static void hidg_disable(struct usb_function *f)
}
spin_unlock_irqrestore(&hidg->get_report_spinlock, flags);
+ spin_lock_irqsave(&hidg->read_spinlock, flags);
+ hidg->disabled = true;
+ spin_unlock_irqrestore(&hidg->read_spinlock, flags);
+ wake_up(&hidg->read_queue);
+
spin_lock_irqsave(&hidg->write_spinlock, flags);
if (!hidg->write_pending) {
free_ep_req(hidg->in_ep, hidg->req);
@@ -1097,6 +1094,10 @@ static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
}
}
+ spin_lock_irqsave(&hidg->read_spinlock, flags);
+ hidg->disabled = false;
+ spin_unlock_irqrestore(&hidg->read_spinlock, flags);
+
if (hidg->in_ep != NULL) {
spin_lock_irqsave(&hidg->write_spinlock, flags);
hidg->req = req_in;
@@ -1202,6 +1203,18 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f)
hidg_hs_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
hidg_fs_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
hidg_ss_out_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
+
+ /* IN endpoints: FS default=10ms, HS default=4µ-frame; user override if set */
+ if (!hidg->interval_user_set) {
+ hidg_fs_in_ep_desc.bInterval = 10;
+ hidg_hs_in_ep_desc.bInterval = 4;
+ hidg_ss_in_ep_desc.bInterval = 4;
+ } else {
+ hidg_fs_in_ep_desc.bInterval = hidg->interval;
+ hidg_hs_in_ep_desc.bInterval = hidg->interval;
+ hidg_ss_in_ep_desc.bInterval = hidg->interval;
+ }
+
hidg_ss_out_comp_desc.wBytesPerInterval =
cpu_to_le16(hidg->report_length);
hidg_hs_out_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
@@ -1210,8 +1223,8 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f)
* We can use hidg_desc struct here but we should not relay
* that its content won't change after returning from this function.
*/
- hidg_desc.desc[0].bDescriptorType = HID_DT_REPORT;
- hidg_desc.desc[0].wDescriptorLength =
+ hidg_desc.rpt_desc.bDescriptorType = HID_DT_REPORT;
+ hidg_desc.rpt_desc.wDescriptorLength =
cpu_to_le16(hidg->report_desc_length);
hidg_hs_in_ep_desc.bEndpointAddress =
@@ -1224,54 +1237,61 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f)
hidg_ss_out_ep_desc.bEndpointAddress =
hidg_fs_out_ep_desc.bEndpointAddress;
- if (hidg->use_out_ep)
+ if (hidg->use_out_ep) {
+ /* OUT endpoints: same defaults (FS=10, HS=4) unless user set */
+ if (!hidg->interval_user_set) {
+ hidg_fs_out_ep_desc.bInterval = 10;
+ hidg_hs_out_ep_desc.bInterval = 4;
+ hidg_ss_out_ep_desc.bInterval = 4;
+ } else {
+ hidg_fs_out_ep_desc.bInterval = hidg->interval;
+ hidg_hs_out_ep_desc.bInterval = hidg->interval;
+ hidg_ss_out_ep_desc.bInterval = hidg->interval;
+ }
status = usb_assign_descriptors(f,
- hidg_fs_descriptors_intout,
- hidg_hs_descriptors_intout,
- hidg_ss_descriptors_intout,
- hidg_ss_descriptors_intout);
- else
+ hidg_fs_descriptors_intout,
+ hidg_hs_descriptors_intout,
+ hidg_ss_descriptors_intout,
+ hidg_ss_descriptors_intout);
+ } else {
status = usb_assign_descriptors(f,
hidg_fs_descriptors_ssreport,
hidg_hs_descriptors_ssreport,
hidg_ss_descriptors_ssreport,
hidg_ss_descriptors_ssreport);
-
+ }
if (status)
goto fail;
- spin_lock_init(&hidg->write_spinlock);
hidg->write_pending = 1;
hidg->req = NULL;
- spin_lock_init(&hidg->read_spinlock);
- spin_lock_init(&hidg->get_report_spinlock);
- init_waitqueue_head(&hidg->write_queue);
- init_waitqueue_head(&hidg->read_queue);
- init_waitqueue_head(&hidg->get_queue);
- init_waitqueue_head(&hidg->get_id_queue);
- INIT_LIST_HEAD(&hidg->completed_out_req);
- INIT_LIST_HEAD(&hidg->report_list);
INIT_WORK(&hidg->work, get_report_workqueue_handler);
hidg->workqueue = alloc_workqueue("report_work",
- WQ_FREEZABLE |
- WQ_MEM_RECLAIM,
+ WQ_FREEZABLE | WQ_MEM_RECLAIM | WQ_PERCPU,
1);
if (!hidg->workqueue) {
status = -ENOMEM;
- goto fail;
+ goto fail_free_descs;
}
/* create char device */
- cdev_init(&hidg->cdev, &f_hidg_fops);
- status = cdev_device_add(&hidg->cdev, &hidg->dev);
+ hidg->cdev = cdev_alloc();
+ if (!hidg->cdev) {
+ status = -ENOMEM;
+ goto fail_free_all;
+ }
+ hidg->cdev->ops = &f_hidg_fops;
+
+ status = cdev_device_add(hidg->cdev, &hidg->dev);
if (status)
- goto fail_free_descs;
+ goto fail_free_all;
return 0;
-fail_free_descs:
+fail_free_all:
destroy_workqueue(hidg->workqueue);
+fail_free_descs:
usb_free_all_descriptors(f);
fail:
ERROR(f->config->cdev, "hidg_bind FAILED\n");
@@ -1310,7 +1330,7 @@ static void hid_attr_release(struct config_item *item)
usb_put_function_instance(&opts->func_inst);
}
-static struct configfs_item_operations hidg_item_ops = {
+static const struct configfs_item_operations hidg_item_ops = {
.release = hid_attr_release,
};
@@ -1408,6 +1428,53 @@ end:
CONFIGFS_ATTR(f_hid_opts_, report_desc);
+static ssize_t f_hid_opts_interval_show(struct config_item *item, char *page)
+{
+ struct f_hid_opts *opts = to_f_hid_opts(item);
+ int result;
+
+ mutex_lock(&opts->lock);
+ result = sprintf(page, "%d\n", opts->interval);
+ mutex_unlock(&opts->lock);
+
+ return result;
+}
+
+static ssize_t f_hid_opts_interval_store(struct config_item *item,
+ const char *page, size_t len)
+{
+ struct f_hid_opts *opts = to_f_hid_opts(item);
+ int ret;
+ unsigned int tmp;
+
+ mutex_lock(&opts->lock);
+ if (opts->refcnt) {
+ ret = -EBUSY;
+ goto end;
+ }
+
+ /* parse into a wider type first */
+ ret = kstrtouint(page, 0, &tmp);
+ if (ret)
+ goto end;
+
+ /* range-check against unsigned char max */
+ if (tmp > 255) {
+ ret = -EINVAL;
+ goto end;
+ }
+
+ opts->interval = (unsigned char)tmp;
+ opts->interval_user_set = true;
+ ret = len;
+
+end:
+ mutex_unlock(&opts->lock);
+ return ret;
+}
+
+CONFIGFS_ATTR(f_hid_opts_, interval);
+
static ssize_t f_hid_opts_dev_show(struct config_item *item, char *page)
{
struct f_hid_opts *opts = to_f_hid_opts(item);
@@ -1422,6 +1489,7 @@ static struct configfs_attribute *hid_attrs[] = {
&f_hid_opts_attr_protocol,
&f_hid_opts_attr_no_out_endpoint,
&f_hid_opts_attr_report_length,
+ &f_hid_opts_attr_interval,
&f_hid_opts_attr_report_desc,
&f_hid_opts_attr_dev,
NULL,
@@ -1464,10 +1532,14 @@ static struct usb_function_instance *hidg_alloc_inst(void)
struct usb_function_instance *ret;
int status = 0;
- opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+ opts = kzalloc_obj(*opts);
if (!opts)
return ERR_PTR(-ENOMEM);
mutex_init(&opts->lock);
+
+ opts->interval = 4;
+ opts->interval_user_set = false;
+
opts->func_inst.free_func_inst = hidg_free_inst;
ret = &opts->func_inst;
@@ -1514,7 +1586,7 @@ static void hidg_unbind(struct usb_configuration *c, struct usb_function *f)
{
struct f_hidg *hidg = func_to_hidg(f);
- cdev_device_del(&hidg->cdev, &hidg->dev);
+ cdev_device_del(hidg->cdev, &hidg->dev);
destroy_workqueue(hidg->workqueue);
usb_free_all_descriptors(f);
}
@@ -1526,7 +1598,7 @@ static struct usb_function *hidg_alloc(struct usb_function_instance *fi)
int ret;
/* allocate and initialize one new instance */
- hidg = kzalloc(sizeof(*hidg), GFP_KERNEL);
+ hidg = kzalloc_obj(*hidg);
if (!hidg)
return ERR_PTR(-ENOMEM);
@@ -1534,6 +1606,16 @@ static struct usb_function *hidg_alloc(struct usb_function_instance *fi)
mutex_lock(&opts->lock);
+ spin_lock_init(&hidg->write_spinlock);
+ spin_lock_init(&hidg->read_spinlock);
+ spin_lock_init(&hidg->get_report_spinlock);
+ init_waitqueue_head(&hidg->write_queue);
+ init_waitqueue_head(&hidg->read_queue);
+ init_waitqueue_head(&hidg->get_queue);
+ init_waitqueue_head(&hidg->get_id_queue);
+ INIT_LIST_HEAD(&hidg->completed_out_req);
+ INIT_LIST_HEAD(&hidg->report_list);
+
device_initialize(&hidg->dev);
hidg->dev.release = hidg_release;
hidg->dev.class = &hidg_class;
@@ -1546,6 +1628,8 @@ static struct usb_function *hidg_alloc(struct usb_function_instance *fi)
hidg->bInterfaceProtocol = opts->protocol;
hidg->report_length = opts->report_length;
hidg->report_desc_length = opts->report_desc_length;
+ hidg->interval = opts->interval;
+ hidg->interval_user_set = opts->interval_user_set;
if (opts->report_desc) {
hidg->report_desc = kmemdup(opts->report_desc,
opts->report_desc_length,
diff --git a/drivers/usb/gadget/function/f_loopback.c b/drivers/usb/gadget/function/f_loopback.c
index 49b009a7d5d7..d2d07fb49e70 100644
--- a/drivers/usb/gadget/function/f_loopback.c
+++ b/drivers/usb/gadget/function/f_loopback.c
@@ -425,7 +425,7 @@ static struct usb_function *loopback_alloc(struct usb_function_instance *fi)
struct f_loopback *loop;
struct f_lb_opts *lb_opts;
- loop = kzalloc(sizeof *loop, GFP_KERNEL);
+ loop = kzalloc_obj(*loop);
if (!loop)
return ERR_PTR(-ENOMEM);
@@ -464,7 +464,7 @@ static void lb_attr_release(struct config_item *item)
usb_put_function_instance(&lb_opts->func_inst);
}
-static struct configfs_item_operations lb_item_ops = {
+static const struct configfs_item_operations lb_item_ops = {
.release = lb_attr_release,
};
@@ -568,7 +568,7 @@ static struct usb_function_instance *loopback_alloc_instance(void)
{
struct f_lb_opts *lb_opts;
- lb_opts = kzalloc(sizeof(*lb_opts), GFP_KERNEL);
+ lb_opts = kzalloc_obj(*lb_opts);
if (!lb_opts)
return ERR_PTR(-ENOMEM);
mutex_init(&lb_opts->lock);
diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c
index 2eae8fc2e0db..b7b06cb79ff5 100644
--- a/drivers/usb/gadget/function/f_mass_storage.c
+++ b/drivers/usb/gadget/function/f_mass_storage.c
@@ -180,6 +180,7 @@
#include <linux/kthread.h>
#include <linux/sched/signal.h>
#include <linux/limits.h>
+#include <linux/overflow.h>
#include <linux/pagemap.h>
#include <linux/rwsem.h>
#include <linux/slab.h>
@@ -1853,8 +1854,15 @@ static int check_command_size_in_blocks(struct fsg_common *common,
int cmnd_size, enum data_direction data_dir,
unsigned int mask, int needs_medium, const char *name)
{
- if (common->curlun)
- common->data_size_from_cmnd <<= common->curlun->blkbits;
+ if (common->curlun) {
+ if (check_shl_overflow(common->data_size_from_cmnd,
+ common->curlun->blkbits,
+ &common->data_size_from_cmnd)) {
+ common->phase_error = 1;
+ return -EINVAL;
+ }
+ }
+
return check_command(common, cmnd_size, data_dir,
mask, needs_medium, name);
}
@@ -2142,8 +2150,8 @@ static int do_scsi_command(struct fsg_common *common)
* of Posix locks.
*/
case FORMAT_UNIT:
- case RELEASE:
- case RESERVE:
+ case RELEASE_6:
+ case RESERVE_6:
case SEND_DIAGNOSTIC:
default:
@@ -2699,7 +2707,7 @@ static void fsg_lun_release(struct device *dev)
static struct fsg_common *fsg_common_setup(struct fsg_common *common)
{
if (!common) {
- common = kzalloc(sizeof(*common), GFP_KERNEL);
+ common = kzalloc_obj(*common);
if (!common)
return ERR_PTR(-ENOMEM);
common->free_storage_on_release = 1;
@@ -2740,7 +2748,7 @@ int fsg_common_set_num_buffers(struct fsg_common *common, unsigned int n)
struct fsg_buffhd *bh, *buffhds;
int i;
- buffhds = kcalloc(n, sizeof(*buffhds), GFP_KERNEL);
+ buffhds = kzalloc_objs(*buffhds, n);
if (!buffhds)
return -ENOMEM;
@@ -2887,7 +2895,7 @@ int fsg_common_create_lun(struct fsg_common *common, struct fsg_lun_config *cfg,
return -EINVAL;
}
- lun = kzalloc(sizeof(*lun), GFP_KERNEL);
+ lun = kzalloc_obj(*lun);
if (!lun)
return -ENOMEM;
@@ -3153,7 +3161,7 @@ static void fsg_lun_attr_release(struct config_item *item)
kfree(lun_opts);
}
-static struct configfs_item_operations fsg_lun_item_ops = {
+static const struct configfs_item_operations fsg_lun_item_ops = {
.release = fsg_lun_attr_release,
};
@@ -3311,7 +3319,7 @@ static struct config_group *fsg_lun_make(struct config_group *group,
goto out;
}
- opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+ opts = kzalloc_obj(*opts);
if (!opts) {
ret = -ENOMEM;
goto out;
@@ -3369,7 +3377,7 @@ static void fsg_attr_release(struct config_item *item)
usb_put_function_instance(&opts->func_inst);
}
-static struct configfs_item_operations fsg_item_ops = {
+static const struct configfs_item_operations fsg_item_ops = {
.release = fsg_attr_release,
};
@@ -3462,7 +3470,7 @@ static struct configfs_attribute *fsg_attrs[] = {
NULL,
};
-static struct configfs_group_operations fsg_group_ops = {
+static const struct configfs_group_operations fsg_group_ops = {
.make_group = fsg_lun_make,
.drop_item = fsg_lun_drop,
};
@@ -3489,7 +3497,7 @@ static struct usb_function_instance *fsg_alloc_inst(void)
struct fsg_lun_config config;
int rc;
- opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+ opts = kzalloc_obj(*opts);
if (!opts)
return ERR_PTR(-ENOMEM);
mutex_init(&opts->lock);
@@ -3554,7 +3562,7 @@ static struct usb_function *fsg_alloc(struct usb_function_instance *fi)
struct fsg_common *common = opts->common;
struct fsg_dev *fsg;
- fsg = kzalloc(sizeof(*fsg), GFP_KERNEL);
+ fsg = kzalloc_obj(*fsg);
if (unlikely(!fsg))
return ERR_PTR(-ENOMEM);
diff --git a/drivers/usb/gadget/function/f_mass_storage.h b/drivers/usb/gadget/function/f_mass_storage.h
index 3b8c4ce2a40a..82ecd3fedb3a 100644
--- a/drivers/usb/gadget/function/f_mass_storage.h
+++ b/drivers/usb/gadget/function/f_mass_storage.h
@@ -110,7 +110,7 @@ struct fsg_config {
};
static inline struct fsg_opts *
-fsg_opts_from_func_inst(const struct usb_function_instance *fi)
+fsg_opts_from_func_inst(struct usb_function_instance *fi)
{
return container_of(fi, struct fsg_opts, func_inst);
}
diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c
index da82598fcef8..4d9e4bd700d8 100644
--- a/drivers/usb/gadget/function/f_midi.c
+++ b/drivers/usb/gadget/function/f_midi.c
@@ -875,6 +875,7 @@ static int f_midi_bind(struct usb_configuration *c, struct usb_function *f)
struct usb_composite_dev *cdev = c->cdev;
struct f_midi *midi = func_to_midi(f);
struct usb_string *us;
+ struct f_midi_opts *opts;
int status, n, jack = 1, i = 0, endpoint_descriptor_index = 0;
midi->gadget = cdev->gadget;
@@ -883,6 +884,10 @@ static int f_midi_bind(struct usb_configuration *c, struct usb_function *f)
if (status < 0)
goto fail_register;
+ opts = container_of(f->fi, struct f_midi_opts, func_inst);
+ if (opts->interface_string)
+ midi_string_defs[STRING_FUNC_IDX].s = opts->interface_string;
+
/* maybe allocate device-global string ID */
us = usb_gstrings_attach(c->cdev, midi_strings,
ARRAY_SIZE(midi_string_defs));
@@ -926,8 +931,7 @@ static int f_midi_bind(struct usb_configuration *c, struct usb_function *f)
goto fail;
/* allocate temporary function list */
- midi_function = kcalloc((MAX_PORTS * 4) + 11, sizeof(*midi_function),
- GFP_KERNEL);
+ midi_function = kzalloc_objs(*midi_function, (MAX_PORTS * 4) + 11);
if (!midi_function) {
status = -ENOMEM;
goto fail;
@@ -1086,7 +1090,7 @@ static void midi_attr_release(struct config_item *item)
usb_put_function_instance(&opts->func_inst);
}
-static struct configfs_item_operations midi_item_ops = {
+static const struct configfs_item_operations midi_item_ops = {
.release = midi_attr_release,
};
@@ -1178,59 +1182,60 @@ end: \
\
CONFIGFS_ATTR(f_midi_opts_, name);
+#define F_MIDI_OPT_STRING(name) \
+static ssize_t f_midi_opts_##name##_show(struct config_item *item, char *page) \
+{ \
+ struct f_midi_opts *opts = to_f_midi_opts(item); \
+ ssize_t result; \
+ \
+ mutex_lock(&opts->lock); \
+ if (opts->name) { \
+ result = strscpy(page, opts->name, PAGE_SIZE); \
+ } else { \
+ page[0] = 0; \
+ result = 0; \
+ } \
+ \
+ mutex_unlock(&opts->lock); \
+ \
+ return result; \
+} \
+ \
+static ssize_t f_midi_opts_##name##_store(struct config_item *item, \
+ const char *page, size_t len) \
+{ \
+ struct f_midi_opts *opts = to_f_midi_opts(item); \
+ int ret; \
+ char *c; \
+ \
+ mutex_lock(&opts->lock); \
+ if (opts->refcnt > 1) { \
+ ret = -EBUSY; \
+ goto end; \
+ } \
+ \
+ c = kstrndup(page, len, GFP_KERNEL); \
+ if (!c) { \
+ ret = -ENOMEM; \
+ goto end; \
+ } \
+ kfree(opts->name); \
+ opts->name = c; \
+ ret = len; \
+end: \
+ mutex_unlock(&opts->lock); \
+ return ret; \
+} \
+ \
+CONFIGFS_ATTR(f_midi_opts_, name)
+
F_MIDI_OPT_SIGNED(index, true, SNDRV_CARDS);
F_MIDI_OPT(buflen, false, 0);
F_MIDI_OPT(qlen, false, 0);
F_MIDI_OPT(in_ports, true, MAX_PORTS);
F_MIDI_OPT(out_ports, true, MAX_PORTS);
-
-static ssize_t f_midi_opts_id_show(struct config_item *item, char *page)
-{
- struct f_midi_opts *opts = to_f_midi_opts(item);
- ssize_t result;
-
- mutex_lock(&opts->lock);
- if (opts->id) {
- result = strscpy(page, opts->id, PAGE_SIZE);
- } else {
- page[0] = 0;
- result = 0;
- }
-
- mutex_unlock(&opts->lock);
-
- return result;
-}
-
-static ssize_t f_midi_opts_id_store(struct config_item *item,
- const char *page, size_t len)
-{
- struct f_midi_opts *opts = to_f_midi_opts(item);
- int ret;
- char *c;
-
- mutex_lock(&opts->lock);
- if (opts->refcnt > 1) {
- ret = -EBUSY;
- goto end;
- }
-
- c = kstrndup(page, len, GFP_KERNEL);
- if (!c) {
- ret = -ENOMEM;
- goto end;
- }
- if (opts->id_allocated)
- kfree(opts->id);
- opts->id = c;
- opts->id_allocated = true;
- ret = len;
-end:
- mutex_unlock(&opts->lock);
- return ret;
-}
-
-CONFIGFS_ATTR(f_midi_opts_, id);
+F_MIDI_OPT_STRING(id);
+F_MIDI_OPT_STRING(interface_string);
static struct configfs_attribute *midi_attrs[] = {
&f_midi_opts_attr_index,
@@ -1239,6 +1244,7 @@ static struct configfs_attribute *midi_attrs[] = {
&f_midi_opts_attr_in_ports,
&f_midi_opts_attr_out_ports,
&f_midi_opts_attr_id,
+ &f_midi_opts_attr_interface_string,
NULL,
};
@@ -1262,8 +1268,8 @@ static void f_midi_free_inst(struct usb_function_instance *f)
mutex_unlock(&opts->lock);
if (free) {
- if (opts->id_allocated)
- kfree(opts->id);
+ kfree(opts->id);
+ kfree(opts->interface_string);
kfree(opts);
}
}
@@ -1272,14 +1278,15 @@ static struct usb_function_instance *f_midi_alloc_inst(void)
{
struct f_midi_opts *opts;
- opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+ opts = kzalloc_obj(*opts);
if (!opts)
return ERR_PTR(-ENOMEM);
mutex_init(&opts->lock);
opts->func_inst.free_func_inst = f_midi_free_inst;
opts->index = SNDRV_DEFAULT_IDX1;
- opts->id = SNDRV_DEFAULT_STR1;
+ opts->id = NULL;
+ opts->interface_string = NULL;
opts->buflen = 512;
opts->qlen = 32;
opts->in_ports = 1;
@@ -1353,8 +1360,7 @@ static struct usb_function *f_midi_alloc(struct usb_function_instance *fi)
}
/* allocate and initialize one new instance */
- midi = kzalloc(struct_size(midi, in_ports_array, opts->in_ports),
- GFP_KERNEL);
+ midi = kzalloc_flex(*midi, in_ports_array, opts->in_ports);
if (!midi) {
status = -ENOMEM;
goto setup_fail;
diff --git a/drivers/usb/gadget/function/f_midi2.c b/drivers/usb/gadget/function/f_midi2.c
index 12e866fb311d..19fdac024343 100644
--- a/drivers/usb/gadget/function/f_midi2.c
+++ b/drivers/usb/gadget/function/f_midi2.c
@@ -475,7 +475,7 @@ static void reply_ump_stream_ep_info(struct f_midi2_ep *ep)
/* reply a UMP EP device info */
static void reply_ump_stream_ep_device(struct f_midi2_ep *ep)
{
- struct snd_ump_stream_msg_devince_info rep = {
+ struct snd_ump_stream_msg_device_info rep = {
.type = UMP_MSG_TYPE_STREAM,
.status = UMP_STREAM_MSG_STATUS_DEVICE_INFO,
.manufacture_id = ep->info.manufacturer,
@@ -1187,8 +1187,7 @@ static int f_midi2_init_ep(struct f_midi2 *midi2, struct f_midi2_ep *ep,
return -ENODEV;
usb_ep->complete = complete;
- usb_ep->reqs = kcalloc(midi2->info.num_reqs, sizeof(*usb_ep->reqs),
- GFP_KERNEL);
+ usb_ep->reqs = kzalloc_objs(*usb_ep->reqs, midi2->info.num_reqs);
if (!usb_ep->reqs)
return -ENOMEM;
for (i = 0; i < midi2->info.num_reqs; i++) {
@@ -1542,9 +1541,9 @@ static int f_midi2_create_card(struct f_midi2 *midi2)
return err;
midi2->card = card;
- strcpy(card->driver, "f_midi2");
- strcpy(card->shortname, "MIDI 2.0 Gadget");
- strcpy(card->longname, "MIDI 2.0 Gadget");
+ strscpy(card->driver, "f_midi2");
+ strscpy(card->shortname, "MIDI 2.0 Gadget");
+ strscpy(card->longname, "MIDI 2.0 Gadget");
id = 0;
for (i = 0; i < midi2->num_eps; i++) {
@@ -1599,6 +1598,7 @@ static int f_midi2_create_card(struct f_midi2 *midi2)
strscpy(fb->info.name, ump_fb_name(b),
sizeof(fb->info.name));
}
+ snd_ump_update_group_attrs(ump);
}
for (i = 0; i < midi2->num_eps; i++) {
@@ -1736,9 +1736,12 @@ static int f_midi2_create_usb_configs(struct f_midi2 *midi2,
case USB_SPEED_HIGH:
midi2_midi1_ep_out_desc.wMaxPacketSize = cpu_to_le16(512);
midi2_midi1_ep_in_desc.wMaxPacketSize = cpu_to_le16(512);
- for (i = 0; i < midi2->num_eps; i++)
+ for (i = 0; i < midi2->num_eps; i++) {
midi2_midi2_ep_out_desc[i].wMaxPacketSize =
cpu_to_le16(512);
+ midi2_midi2_ep_in_desc[i].wMaxPacketSize =
+ cpu_to_le16(512);
+ }
fallthrough;
case USB_SPEED_FULL:
midi1_in_eps = midi2_midi1_ep_in_descs;
@@ -1747,9 +1750,12 @@ static int f_midi2_create_usb_configs(struct f_midi2 *midi2,
case USB_SPEED_SUPER:
midi2_midi1_ep_out_desc.wMaxPacketSize = cpu_to_le16(1024);
midi2_midi1_ep_in_desc.wMaxPacketSize = cpu_to_le16(1024);
- for (i = 0; i < midi2->num_eps; i++)
+ for (i = 0; i < midi2->num_eps; i++) {
midi2_midi2_ep_out_desc[i].wMaxPacketSize =
cpu_to_le16(1024);
+ midi2_midi2_ep_in_desc[i].wMaxPacketSize =
+ cpu_to_le16(1024);
+ }
midi1_in_eps = midi2_midi1_ep_in_ss_descs;
midi1_out_eps = midi2_midi1_ep_out_ss_descs;
break;
@@ -2309,7 +2315,7 @@ static void f_midi2_block_opts_release(struct config_item *item)
kfree(opts);
}
-static struct configfs_item_operations f_midi2_block_item_ops = {
+static const struct configfs_item_operations f_midi2_block_item_ops = {
.release = f_midi2_block_opts_release,
};
@@ -2333,7 +2339,7 @@ static int f_midi2_block_opts_create(struct f_midi2_ep_opts *ep_opts,
goto out;
}
- block_opts = kzalloc(sizeof(*block_opts), GFP_KERNEL);
+ block_opts = kzalloc_obj(*block_opts);
if (!block_opts) {
ret = -ENOMEM;
goto out;
@@ -2472,11 +2478,11 @@ static void f_midi2_ep_opts_release(struct config_item *item)
kfree(opts);
}
-static struct configfs_item_operations f_midi2_ep_item_ops = {
+static const struct configfs_item_operations f_midi2_ep_item_ops = {
.release = f_midi2_ep_opts_release,
};
-static struct configfs_group_operations f_midi2_ep_group_ops = {
+static const struct configfs_group_operations f_midi2_ep_group_ops = {
.make_group = f_midi2_opts_block_make,
.drop_item = f_midi2_opts_block_drop,
};
@@ -2495,7 +2501,7 @@ static int f_midi2_ep_opts_create(struct f_midi2_opts *opts,
{
struct f_midi2_ep_opts *ep_opts;
- ep_opts = kzalloc(sizeof(*ep_opts), GFP_KERNEL);
+ ep_opts = kzalloc_obj(*ep_opts);
if (!ep_opts)
return -ENOMEM;
@@ -2611,11 +2617,11 @@ static void f_midi2_opts_release(struct config_item *item)
usb_put_function_instance(&opts->func_inst);
}
-static struct configfs_item_operations f_midi2_item_ops = {
+static const struct configfs_item_operations f_midi2_item_ops = {
.release = f_midi2_opts_release,
};
-static struct configfs_group_operations f_midi2_group_ops = {
+static const struct configfs_group_operations f_midi2_group_ops = {
.make_group = f_midi2_opts_ep_make,
.drop_item = f_midi2_opts_ep_drop,
};
@@ -2645,7 +2651,7 @@ static struct usb_function_instance *f_midi2_alloc_inst(void)
struct f_midi2_block_opts *block_opts;
int ret;
- opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+ opts = kzalloc_obj(*opts);
if (!opts)
return ERR_PTR(-ENOMEM);
@@ -2806,7 +2812,7 @@ static struct usb_function *f_midi2_alloc(struct usb_function_instance *fi)
struct f_midi2_block *bp;
int i, num_eps, blk;
- midi2 = kzalloc(sizeof(*midi2), GFP_KERNEL);
+ midi2 = kzalloc_obj(*midi2);
if (!midi2)
return ERR_PTR(-ENOMEM);
@@ -2848,8 +2854,8 @@ static struct usb_function *f_midi2_alloc(struct usb_function_instance *fi)
}
}
- midi2->string_defs = kcalloc(midi2->total_blocks + 1,
- sizeof(*midi2->string_defs), GFP_KERNEL);
+ midi2->string_defs = kzalloc_objs(*midi2->string_defs,
+ midi2->total_blocks + 1);
if (!midi2->string_defs) {
do_f_midi2_free(midi2, opts);
return ERR_PTR(-ENOMEM);
diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c
index f60576a65ca6..c5bf8a448d64 100644
--- a/drivers/usb/gadget/function/f_ncm.c
+++ b/drivers/usb/gadget/function/f_ncm.c
@@ -11,6 +11,7 @@
* Copyright (C) 2008 Nokia Corporation
*/
+#include <linux/cleanup.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/module.h>
@@ -20,6 +21,7 @@
#include <linux/string_choices.h>
#include <linux/usb/cdc.h>
+#include <linux/usb/gadget.h>
#include "u_ether.h"
#include "u_ether_configfs.h"
@@ -1208,8 +1210,8 @@ parse_ntb:
block_len = get_ncm(&tmp, opts->block_length);
/* (d)wBlockLength */
- if (block_len > ntb_max) {
- INFO(port->func.config->cdev, "OUT size exceeded\n");
+ if ((block_len < opts->nth_size + opts->ndp_size) || (block_len > ntb_max)) {
+ INFO(port->func.config->cdev, "Bad block length: %#X\n", block_len);
goto err;
}
@@ -1436,39 +1438,42 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
struct usb_ep *ep;
struct f_ncm_opts *ncm_opts;
+ struct usb_os_desc_table *os_desc_table __free(kfree) = NULL;
+ struct net_device *net __free(detach_gadget) = NULL;
+ struct usb_request *request __free(free_usb_request) = NULL;
+
if (!can_support_ecm(cdev->gadget))
return -EINVAL;
ncm_opts = container_of(f->fi, struct f_ncm_opts, func_inst);
if (cdev->use_os_string) {
- f->os_desc_table = kzalloc(sizeof(*f->os_desc_table),
- GFP_KERNEL);
- if (!f->os_desc_table)
+ os_desc_table = kzalloc(sizeof(*os_desc_table), GFP_KERNEL);
+ if (!os_desc_table)
return -ENOMEM;
- f->os_desc_n = 1;
- f->os_desc_table[0].os_desc = &ncm_opts->ncm_os_desc;
}
- mutex_lock(&ncm_opts->lock);
- gether_set_gadget(ncm_opts->net, cdev->gadget);
- if (!ncm_opts->bound) {
- ncm_opts->net->mtu = (ncm_opts->max_segment_size - ETH_HLEN);
- status = gether_register_netdev(ncm_opts->net);
- }
- mutex_unlock(&ncm_opts->lock);
-
- if (status)
- goto fail;
+ scoped_guard(mutex, &ncm_opts->lock)
+ if (ncm_opts->bind_count == 0) {
+ if (!device_is_registered(&ncm_opts->net->dev)) {
+ ncm_opts->net->mtu = (ncm_opts->max_segment_size - ETH_HLEN);
+ gether_set_gadget(ncm_opts->net, cdev->gadget);
+ status = gether_register_netdev(ncm_opts->net);
+ } else
+ status = gether_attach_gadget(ncm_opts->net, cdev->gadget);
+
+ if (status)
+ return status;
+ net = ncm_opts->net;
+ }
- ncm_opts->bound = true;
+ ncm_string_defs[1].s = ncm->ethaddr;
us = usb_gstrings_attach(cdev, ncm_strings,
ARRAY_SIZE(ncm_string_defs));
- if (IS_ERR(us)) {
- status = PTR_ERR(us);
- goto fail;
- }
+ if (IS_ERR(us))
+ return PTR_ERR(us);
+
ncm_control_intf.iInterface = us[STRING_CTRL_IDX].id;
ncm_data_nop_intf.iInterface = us[STRING_DATA_IDX].id;
ncm_data_intf.iInterface = us[STRING_DATA_IDX].id;
@@ -1478,20 +1483,16 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
/* allocate instance-specific interface IDs */
status = usb_interface_id(c, f);
if (status < 0)
- goto fail;
+ return status;
ncm->ctrl_id = status;
ncm_iad_desc.bFirstInterface = status;
ncm_control_intf.bInterfaceNumber = status;
ncm_union_desc.bMasterInterface0 = status;
- if (cdev->use_os_string)
- f->os_desc_table[0].if_id =
- ncm_iad_desc.bFirstInterface;
-
status = usb_interface_id(c, f);
if (status < 0)
- goto fail;
+ return status;
ncm->data_id = status;
ncm_data_nop_intf.bInterfaceNumber = status;
@@ -1500,35 +1501,31 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
ecm_desc.wMaxSegmentSize = cpu_to_le16(ncm_opts->max_segment_size);
- status = -ENODEV;
-
/* allocate instance-specific endpoints */
ep = usb_ep_autoconfig(cdev->gadget, &fs_ncm_in_desc);
if (!ep)
- goto fail;
+ return -ENODEV;
ncm->port.in_ep = ep;
ep = usb_ep_autoconfig(cdev->gadget, &fs_ncm_out_desc);
if (!ep)
- goto fail;
+ return -ENODEV;
ncm->port.out_ep = ep;
ep = usb_ep_autoconfig(cdev->gadget, &fs_ncm_notify_desc);
if (!ep)
- goto fail;
+ return -ENODEV;
ncm->notify = ep;
- status = -ENOMEM;
-
/* allocate notification request and buffer */
- ncm->notify_req = usb_ep_alloc_request(ep, GFP_KERNEL);
- if (!ncm->notify_req)
- goto fail;
- ncm->notify_req->buf = kmalloc(NCM_STATUS_BYTECOUNT, GFP_KERNEL);
- if (!ncm->notify_req->buf)
- goto fail;
- ncm->notify_req->context = ncm;
- ncm->notify_req->complete = ncm_notify_complete;
+ request = usb_ep_alloc_request(ep, GFP_KERNEL);
+ if (!request)
+ return -ENOMEM;
+ request->buf = kmalloc(NCM_STATUS_BYTECOUNT, GFP_KERNEL);
+ if (!request->buf)
+ return -ENOMEM;
+ request->context = ncm;
+ request->complete = ncm_notify_complete;
/*
* support all relevant hardware speeds... we expect that when
@@ -1548,7 +1545,7 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
status = usb_assign_descriptors(f, ncm_fs_function, ncm_hs_function,
ncm_ss_function, ncm_ss_function);
if (status)
- goto fail;
+ return status;
/*
* NOTE: all that is done without knowing or caring about
@@ -1559,26 +1556,23 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
ncm->port.open = ncm_open;
ncm->port.close = ncm_close;
- hrtimer_init(&ncm->task_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
- ncm->task_timer.function = ncm_tx_timeout;
+ hrtimer_setup(&ncm->task_timer, ncm_tx_timeout, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
+
+ if (cdev->use_os_string) {
+ os_desc_table[0].os_desc = &ncm_opts->ncm_os_desc;
+ os_desc_table[0].if_id = ncm_iad_desc.bFirstInterface;
+ f->os_desc_table = no_free_ptr(os_desc_table);
+ f->os_desc_n = 1;
+ }
+ ncm->notify_req = no_free_ptr(request);
+
+ ncm_opts->bind_count++;
+ retain_and_null_ptr(net);
DBG(cdev, "CDC Network: IN/%s OUT/%s NOTIFY/%s\n",
ncm->port.in_ep->name, ncm->port.out_ep->name,
ncm->notify->name);
return 0;
-
-fail:
- kfree(f->os_desc_table);
- f->os_desc_n = 0;
-
- if (ncm->notify_req) {
- kfree(ncm->notify_req->buf);
- usb_ep_free_request(ncm->notify, ncm->notify_req);
- }
-
- ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
-
- return status;
}
static inline struct f_ncm_opts *to_f_ncm_opts(struct config_item *item)
@@ -1666,7 +1660,7 @@ static void ncm_free_inst(struct usb_function_instance *f)
struct f_ncm_opts *opts;
opts = container_of(f, struct f_ncm_opts, func_inst);
- if (opts->bound)
+ if (device_is_registered(&opts->net->dev))
gether_cleanup(netdev_priv(opts->net));
else
free_netdev(opts->net);
@@ -1681,7 +1675,7 @@ static struct usb_function_instance *ncm_alloc_inst(void)
char *names[1];
struct config_group *ncm_interf_group;
- opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+ opts = kzalloc_obj(*opts);
if (!opts)
return ERR_PTR(-ENOMEM);
opts->ncm_os_desc.ext_compat_id = opts->ncm_ext_compat_id;
@@ -1729,9 +1723,12 @@ static void ncm_free(struct usb_function *f)
static void ncm_unbind(struct usb_configuration *c, struct usb_function *f)
{
struct f_ncm *ncm = func_to_ncm(f);
+ struct f_ncm_opts *ncm_opts;
DBG(c->cdev, "ncm unbind\n");
+ ncm_opts = container_of(f->fi, struct f_ncm_opts, func_inst);
+
hrtimer_cancel(&ncm->task_timer);
kfree(f->os_desc_table);
@@ -1747,6 +1744,10 @@ static void ncm_unbind(struct usb_configuration *c, struct usb_function *f)
kfree(ncm->notify_req->buf);
usb_ep_free_request(ncm->notify, ncm->notify_req);
+
+ ncm_opts->bind_count--;
+ if (ncm_opts->bind_count == 0)
+ gether_detach_gadget(ncm_opts->net);
}
static struct usb_function *ncm_alloc(struct usb_function_instance *fi)
@@ -1772,7 +1773,6 @@ static struct usb_function *ncm_alloc(struct usb_function_instance *fi)
mutex_unlock(&opts->lock);
return ERR_PTR(-EINVAL);
}
- ncm_string_defs[STRING_MAC_IDX].s = ncm->ethaddr;
spin_lock_init(&ncm->lock);
ncm_reset_values(ncm);
diff --git a/drivers/usb/gadget/function/f_obex.c b/drivers/usb/gadget/function/f_obex.c
index 1305e2326cdf..715bb0decd3b 100644
--- a/drivers/usb/gadget/function/f_obex.c
+++ b/drivers/usb/gadget/function/f_obex.c
@@ -390,7 +390,7 @@ static void obex_attr_release(struct config_item *item)
usb_put_function_instance(&opts->func_inst);
}
-static struct configfs_item_operations obex_item_ops = {
+static const struct configfs_item_operations obex_item_ops = {
.release = obex_attr_release,
};
@@ -426,7 +426,7 @@ static struct usb_function_instance *obex_alloc_inst(void)
struct f_serial_opts *opts;
int ret;
- opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+ opts = kzalloc_obj(*opts);
if (!opts)
return ERR_PTR(-ENOMEM);
@@ -461,7 +461,7 @@ static struct usb_function *obex_alloc(struct usb_function_instance *fi)
struct f_serial_opts *opts;
/* allocate and initialize one new instance */
- obex = kzalloc(sizeof(*obex), GFP_KERNEL);
+ obex = kzalloc_obj(*obex);
if (!obex)
return ERR_PTR(-ENOMEM);
diff --git a/drivers/usb/gadget/function/f_phonet.c b/drivers/usb/gadget/function/f_phonet.c
index 0aa9e8224cae..b1ee9a7c2e94 100644
--- a/drivers/usb/gadget/function/f_phonet.c
+++ b/drivers/usb/gadget/function/f_phonet.c
@@ -333,6 +333,15 @@ static void pn_rx_complete(struct usb_ep *ep, struct usb_request *req)
if (unlikely(!skb))
break;
+ if (unlikely(skb_shinfo(skb)->nr_frags >= MAX_SKB_FRAGS)) {
+ /* Frame count from host exceeds frags[] capacity */
+ dev_kfree_skb_any(skb);
+ if (fp->rx.skb == skb)
+ fp->rx.skb = NULL;
+ dev->stats.rx_length_errors++;
+ break;
+ }
+
if (skb->len == 0) { /* First fragment */
skb->protocol = htons(ETH_P_PHONET);
skb_reset_mac_header(skb);
@@ -585,7 +594,7 @@ static void phonet_attr_release(struct config_item *item)
usb_put_function_instance(&opts->func_inst);
}
-static struct configfs_item_operations phonet_item_ops = {
+static const struct configfs_item_operations phonet_item_ops = {
.release = phonet_attr_release,
};
@@ -623,7 +632,7 @@ static struct usb_function_instance *phonet_alloc_inst(void)
{
struct f_phonet_opts *opts;
- opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+ opts = kzalloc_obj(*opts);
if (!opts)
return ERR_PTR(-ENOMEM);
@@ -669,7 +678,7 @@ static struct usb_function *phonet_alloc(struct usb_function_instance *fi)
struct f_phonet *fp;
struct f_phonet_opts *opts;
- fp = kzalloc(struct_size(fp, out_reqv, phonet_rxq_size), GFP_KERNEL);
+ fp = kzalloc_flex(*fp, out_reqv, phonet_rxq_size);
if (!fp)
return ERR_PTR(-ENOMEM);
diff --git a/drivers/usb/gadget/function/f_printer.c b/drivers/usb/gadget/function/f_printer.c
index d295ade8fa67..e4f7828ae75d 100644
--- a/drivers/usb/gadget/function/f_printer.c
+++ b/drivers/usb/gadget/function/f_printer.c
@@ -1220,7 +1220,7 @@ static void printer_attr_release(struct config_item *item)
usb_put_function_instance(&opts->func_inst);
}
-static struct configfs_item_operations printer_item_ops = {
+static const struct configfs_item_operations printer_item_ops = {
.release = printer_attr_release,
};
@@ -1374,7 +1374,7 @@ static struct usb_function_instance *gprinter_alloc_inst(void)
struct usb_function_instance *ret;
int status = 0;
- opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+ opts = kzalloc_obj(*opts);
if (!opts)
return ERR_PTR(-ENOMEM);
@@ -1482,7 +1482,7 @@ static struct usb_function *gprinter_alloc(struct usb_function_instance *fi)
return ERR_PTR(-ENOENT);
}
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ dev = kzalloc_obj(*dev);
if (!dev) {
mutex_unlock(&opts->lock);
return ERR_PTR(-ENOMEM);
diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c
index 7cec19d65fb5..7de1c5f8e326 100644
--- a/drivers/usb/gadget/function/f_rndis.c
+++ b/drivers/usb/gadget/function/f_rndis.c
@@ -11,6 +11,7 @@
/* #define VERBOSE_DEBUG */
+#include <linux/cleanup.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -19,6 +20,8 @@
#include <linux/atomic.h>
+#include <linux/usb/gadget.h>
+
#include "u_ether.h"
#include "u_ether_configfs.h"
#include "u_rndis.h"
@@ -662,6 +665,9 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
struct usb_ep *ep;
struct f_rndis_opts *rndis_opts;
+ struct usb_os_desc_table *os_desc_table __free(kfree) = NULL;
+ struct net_device *net __free(detach_gadget) = NULL;
+ struct usb_request *request __free(free_usb_request) = NULL;
if (!can_support_rndis(c))
return -EINVAL;
@@ -669,39 +675,33 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
rndis_opts = container_of(f->fi, struct f_rndis_opts, func_inst);
if (cdev->use_os_string) {
- f->os_desc_table = kzalloc(sizeof(*f->os_desc_table),
- GFP_KERNEL);
- if (!f->os_desc_table)
+ os_desc_table = kzalloc_obj(*os_desc_table);
+ if (!os_desc_table)
return -ENOMEM;
- f->os_desc_n = 1;
- f->os_desc_table[0].os_desc = &rndis_opts->rndis_os_desc;
}
- rndis_iad_descriptor.bFunctionClass = rndis_opts->class;
- rndis_iad_descriptor.bFunctionSubClass = rndis_opts->subclass;
- rndis_iad_descriptor.bFunctionProtocol = rndis_opts->protocol;
-
- /*
- * in drivers/usb/gadget/configfs.c:configfs_composite_bind()
- * configurations are bound in sequence with list_for_each_entry,
- * in each configuration its functions are bound in sequence
- * with list_for_each_entry, so we assume no race condition
- * with regard to rndis_opts->bound access
- */
- if (!rndis_opts->bound) {
- gether_set_gadget(rndis_opts->net, cdev->gadget);
- status = gether_register_netdev(rndis_opts->net);
- if (status)
- goto fail;
- rndis_opts->bound = true;
+ scoped_guard(mutex, &rndis_opts->lock) {
+ rndis_iad_descriptor.bFunctionClass = rndis_opts->class;
+ rndis_iad_descriptor.bFunctionSubClass = rndis_opts->subclass;
+ rndis_iad_descriptor.bFunctionProtocol = rndis_opts->protocol;
+
+ if (rndis_opts->bind_count == 0 && !rndis_opts->borrowed_net) {
+ if (!device_is_registered(&rndis_opts->net->dev)) {
+ gether_set_gadget(rndis_opts->net, cdev->gadget);
+ status = gether_register_netdev(rndis_opts->net);
+ } else
+ status = gether_attach_gadget(rndis_opts->net, cdev->gadget);
+
+ if (status)
+ return status;
+ net = rndis_opts->net;
+ }
}
us = usb_gstrings_attach(cdev, rndis_strings,
ARRAY_SIZE(rndis_string_defs));
- if (IS_ERR(us)) {
- status = PTR_ERR(us);
- goto fail;
- }
+ if (IS_ERR(us))
+ return PTR_ERR(us);
rndis_control_intf.iInterface = us[0].id;
rndis_data_intf.iInterface = us[1].id;
rndis_iad_descriptor.iFunction = us[2].id;
@@ -709,36 +709,30 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
/* allocate instance-specific interface IDs */
status = usb_interface_id(c, f);
if (status < 0)
- goto fail;
+ return status;
rndis->ctrl_id = status;
rndis_iad_descriptor.bFirstInterface = status;
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;
+ return status;
rndis->data_id = status;
rndis_data_intf.bInterfaceNumber = status;
rndis_union_desc.bSlaveInterface0 = status;
- status = -ENODEV;
-
/* allocate instance-specific endpoints */
ep = usb_ep_autoconfig(cdev->gadget, &fs_in_desc);
if (!ep)
- goto fail;
+ return -ENODEV;
rndis->port.in_ep = ep;
ep = usb_ep_autoconfig(cdev->gadget, &fs_out_desc);
if (!ep)
- goto fail;
+ return -ENODEV;
rndis->port.out_ep = ep;
/* NOTE: a status/notification endpoint is, strictly speaking,
@@ -747,21 +741,19 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
*/
ep = usb_ep_autoconfig(cdev->gadget, &fs_notify_desc);
if (!ep)
- goto fail;
+ return -ENODEV;
rndis->notify = ep;
- status = -ENOMEM;
-
/* allocate notification request and buffer */
- rndis->notify_req = usb_ep_alloc_request(ep, GFP_KERNEL);
- if (!rndis->notify_req)
- goto fail;
- rndis->notify_req->buf = kmalloc(STATUS_BYTECOUNT, GFP_KERNEL);
- if (!rndis->notify_req->buf)
- goto fail;
- rndis->notify_req->length = STATUS_BYTECOUNT;
- rndis->notify_req->context = rndis;
- rndis->notify_req->complete = rndis_response_complete;
+ request = usb_ep_alloc_request(ep, GFP_KERNEL);
+ if (!request)
+ return -ENOMEM;
+ request->buf = kmalloc(STATUS_BYTECOUNT, GFP_KERNEL);
+ if (!request->buf)
+ return -ENOMEM;
+ request->length = STATUS_BYTECOUNT;
+ request->context = rndis;
+ request->complete = rndis_response_complete;
/* support all relevant hardware speeds... we expect that when
* hardware is dual speed, all bulk-capable endpoints work at
@@ -778,7 +770,7 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
status = usb_assign_descriptors(f, eth_fs_function, eth_hs_function,
eth_ss_function, eth_ss_function);
if (status)
- goto fail;
+ return status;
rndis->port.open = rndis_open;
rndis->port.close = rndis_close;
@@ -789,10 +781,22 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
if (rndis->manufacturer && rndis->vendorID &&
rndis_set_param_vendor(rndis->params, rndis->vendorID,
rndis->manufacturer)) {
- status = -EINVAL;
- goto fail_free_descs;
+ usb_free_all_descriptors(f);
+ return -EINVAL;
}
+ if (cdev->use_os_string) {
+ os_desc_table[0].os_desc = &rndis_opts->rndis_os_desc;
+ os_desc_table[0].if_id = rndis_iad_descriptor.bFirstInterface;
+ f->os_desc_table = no_free_ptr(os_desc_table);
+ f->os_desc_n = 1;
+
+ }
+ rndis->notify_req = no_free_ptr(request);
+
+ rndis_opts->bind_count++;
+ retain_and_null_ptr(net);
+
/* NOTE: all that is done without knowing or caring about
* the network link ... which is unavailable to this code
* until we're activated via set_alt().
@@ -802,21 +806,6 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
rndis->port.in_ep->name, rndis->port.out_ep->name,
rndis->notify->name);
return 0;
-
-fail_free_descs:
- usb_free_all_descriptors(f);
-fail:
- kfree(f->os_desc_table);
- f->os_desc_n = 0;
-
- if (rndis->notify_req) {
- kfree(rndis->notify_req->buf);
- usb_ep_free_request(rndis->notify, rndis->notify_req);
- }
-
- ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
-
- return status;
}
void rndis_borrow_net(struct usb_function_instance *f, struct net_device *net)
@@ -824,11 +813,11 @@ void rndis_borrow_net(struct usb_function_instance *f, struct net_device *net)
struct f_rndis_opts *opts;
opts = container_of(f, struct f_rndis_opts, func_inst);
- if (opts->bound)
+ if (device_is_registered(&opts->net->dev))
gether_cleanup(netdev_priv(opts->net));
else
free_netdev(opts->net);
- opts->borrowed_net = opts->bound = true;
+ opts->borrowed_net = true;
opts->net = net;
}
EXPORT_SYMBOL_GPL(rndis_borrow_net);
@@ -886,7 +875,7 @@ static void rndis_free_inst(struct usb_function_instance *f)
opts = container_of(f, struct f_rndis_opts, func_inst);
if (!opts->borrowed_net) {
- if (opts->bound)
+ if (device_is_registered(&opts->net->dev))
gether_cleanup(netdev_priv(opts->net));
else
free_netdev(opts->net);
@@ -903,7 +892,7 @@ static struct usb_function_instance *rndis_alloc_inst(void)
char *names[1];
struct config_group *rndis_interf_group;
- opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+ opts = kzalloc_obj(*opts);
if (!opts)
return ERR_PTR(-ENOMEM);
opts->rndis_os_desc.ext_compat_id = opts->rndis_ext_compat_id;
@@ -955,6 +944,9 @@ static void rndis_free(struct usb_function *f)
static void rndis_unbind(struct usb_configuration *c, struct usb_function *f)
{
struct f_rndis *rndis = func_to_rndis(f);
+ struct f_rndis_opts *rndis_opts;
+
+ rndis_opts = container_of(f->fi, struct f_rndis_opts, func_inst);
kfree(f->os_desc_table);
f->os_desc_n = 0;
@@ -962,6 +954,10 @@ static void rndis_unbind(struct usb_configuration *c, struct usb_function *f)
kfree(rndis->notify_req->buf);
usb_ep_free_request(rndis->notify, rndis->notify_req);
+
+ rndis_opts->bind_count--;
+ if (rndis_opts->bind_count == 0 && !rndis_opts->borrowed_net)
+ gether_detach_gadget(rndis_opts->net);
}
static struct usb_function *rndis_alloc(struct usb_function_instance *fi)
@@ -971,7 +967,7 @@ static struct usb_function *rndis_alloc(struct usb_function_instance *fi)
struct rndis_params *params;
/* allocate and initialize one new instance */
- rndis = kzalloc(sizeof(*rndis), GFP_KERNEL);
+ rndis = kzalloc_obj(*rndis);
if (!rndis)
return ERR_PTR(-ENOMEM);
diff --git a/drivers/usb/gadget/function/f_serial.c b/drivers/usb/gadget/function/f_serial.c
index 8f7e7a2b2ff2..ae8813c67bc7 100644
--- a/drivers/usb/gadget/function/f_serial.c
+++ b/drivers/usb/gadget/function/f_serial.c
@@ -260,7 +260,7 @@ static void serial_attr_release(struct config_item *item)
usb_put_function_instance(&opts->func_inst);
}
-static struct configfs_item_operations serial_item_ops = {
+static const struct configfs_item_operations serial_item_ops = {
.release = serial_attr_release,
};
@@ -317,7 +317,7 @@ static struct usb_function_instance *gser_alloc_inst(void)
struct f_serial_opts *opts;
int ret;
- opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+ opts = kzalloc_obj(*opts);
if (!opts)
return ERR_PTR(-ENOMEM);
@@ -364,13 +364,19 @@ static void gser_suspend(struct usb_function *f)
gserial_suspend(&gser->port);
}
+static int gser_get_status(struct usb_function *f)
+{
+ return (f->func_wakeup_armed ? USB_INTRF_STAT_FUNC_RW : 0) |
+ USB_INTRF_STAT_FUNC_RW_CAP;
+}
+
static struct usb_function *gser_alloc(struct usb_function_instance *fi)
{
struct f_gser *gser;
struct f_serial_opts *opts;
/* allocate and initialize one new instance */
- gser = kzalloc(sizeof(*gser), GFP_KERNEL);
+ gser = kzalloc_obj(*gser);
if (!gser)
return ERR_PTR(-ENOMEM);
@@ -387,6 +393,7 @@ static struct usb_function *gser_alloc(struct usb_function_instance *fi)
gser->port.func.free_func = gser_free;
gser->port.func.resume = gser_resume;
gser->port.func.suspend = gser_suspend;
+ gser->port.func.get_status = gser_get_status;
return &gser->port.func;
}
diff --git a/drivers/usb/gadget/function/f_sourcesink.c b/drivers/usb/gadget/function/f_sourcesink.c
index ec5fd25020fd..d2b707e41669 100644
--- a/drivers/usb/gadget/function/f_sourcesink.c
+++ b/drivers/usb/gadget/function/f_sourcesink.c
@@ -46,6 +46,7 @@ struct f_sourcesink {
unsigned isoc_mult;
unsigned isoc_maxburst;
unsigned buflen;
+ unsigned bulk_maxburst;
unsigned bulk_qlen;
unsigned iso_qlen;
};
@@ -328,6 +329,12 @@ sourcesink_bind(struct usb_configuration *c, struct usb_function *f)
source_sink_intf_alt0.bInterfaceNumber = id;
source_sink_intf_alt1.bInterfaceNumber = id;
+ if (ss->bulk_maxburst > 15)
+ ss->bulk_maxburst = 15;
+
+ ss_source_comp_desc.bMaxBurst = ss->bulk_maxburst;
+ ss_sink_comp_desc.bMaxBurst = ss->bulk_maxburst;
+
/* allocate bulk endpoints */
ss->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_source_desc);
if (!ss->in_ep) {
@@ -837,7 +844,7 @@ static struct usb_function *source_sink_alloc_func(
struct f_sourcesink *ss;
struct f_ss_opts *ss_opts;
- ss = kzalloc(sizeof(*ss), GFP_KERNEL);
+ ss = kzalloc_obj(*ss);
if (!ss)
return ERR_PTR(-ENOMEM);
@@ -853,6 +860,7 @@ static struct usb_function *source_sink_alloc_func(
ss->isoc_mult = ss_opts->isoc_mult;
ss->isoc_maxburst = ss_opts->isoc_maxburst;
ss->buflen = ss_opts->bulk_buflen;
+ ss->bulk_maxburst = ss_opts->bulk_maxburst;
ss->bulk_qlen = ss_opts->bulk_qlen;
ss->iso_qlen = ss_opts->iso_qlen;
@@ -882,7 +890,7 @@ static void ss_attr_release(struct config_item *item)
usb_put_function_instance(&ss_opts->func_inst);
}
-static struct configfs_item_operations ss_item_ops = {
+static const struct configfs_item_operations ss_item_ops = {
.release = ss_attr_release,
};
@@ -1101,6 +1109,49 @@ end:
CONFIGFS_ATTR(f_ss_opts_, isoc_maxburst);
+static ssize_t f_ss_opts_bulk_maxburst_show(struct config_item *item, char *page)
+{
+ struct f_ss_opts *opts = to_f_ss_opts(item);
+ int result;
+
+ mutex_lock(&opts->lock);
+ result = sysfs_emit(page, "%u\n", opts->bulk_maxburst);
+ mutex_unlock(&opts->lock);
+
+ return result;
+}
+
+static ssize_t f_ss_opts_bulk_maxburst_store(struct config_item *item,
+ const char *page, size_t len)
+{
+ struct f_ss_opts *opts = to_f_ss_opts(item);
+ int ret;
+ u8 num;
+
+ mutex_lock(&opts->lock);
+ if (opts->refcnt) {
+ ret = -EBUSY;
+ goto end;
+ }
+
+ ret = kstrtou8(page, 0, &num);
+ if (ret)
+ goto end;
+
+ if (num > 15) {
+ ret = -EINVAL;
+ goto end;
+ }
+
+ opts->bulk_maxburst = num;
+ ret = len;
+end:
+ mutex_unlock(&opts->lock);
+ return ret;
+}
+
+CONFIGFS_ATTR(f_ss_opts_, bulk_maxburst);
+
static ssize_t f_ss_opts_bulk_buflen_show(struct config_item *item, char *page)
{
struct f_ss_opts *opts = to_f_ss_opts(item);
@@ -1222,6 +1273,7 @@ static struct configfs_attribute *ss_attrs[] = {
&f_ss_opts_attr_isoc_mult,
&f_ss_opts_attr_isoc_maxburst,
&f_ss_opts_attr_bulk_buflen,
+ &f_ss_opts_attr_bulk_maxburst,
&f_ss_opts_attr_bulk_qlen,
&f_ss_opts_attr_iso_qlen,
NULL,
@@ -1245,7 +1297,7 @@ static struct usb_function_instance *source_sink_alloc_inst(void)
{
struct f_ss_opts *ss_opts;
- ss_opts = kzalloc(sizeof(*ss_opts), GFP_KERNEL);
+ ss_opts = kzalloc_obj(*ss_opts);
if (!ss_opts)
return ERR_PTR(-ENOMEM);
mutex_init(&ss_opts->lock);
diff --git a/drivers/usb/gadget/function/f_subset.c b/drivers/usb/gadget/function/f_subset.c
index ea3fdd842462..6e3265b8a3a0 100644
--- a/drivers/usb/gadget/function/f_subset.c
+++ b/drivers/usb/gadget/function/f_subset.c
@@ -6,6 +6,7 @@
* Copyright (C) 2008 Nokia Corporation
*/
+#include <linux/cleanup.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -298,25 +299,22 @@ geth_bind(struct usb_configuration *c, struct usb_function *f)
struct usb_ep *ep;
struct f_gether_opts *gether_opts;
+ struct net_device *net __free(detach_gadget) = NULL;
gether_opts = container_of(f->fi, struct f_gether_opts, func_inst);
- /*
- * in drivers/usb/gadget/configfs.c:configfs_composite_bind()
- * configurations are bound in sequence with list_for_each_entry,
- * in each configuration its functions are bound in sequence
- * with list_for_each_entry, so we assume no race condition
- * with regard to gether_opts->bound access
- */
- if (!gether_opts->bound) {
- mutex_lock(&gether_opts->lock);
- gether_set_gadget(gether_opts->net, cdev->gadget);
- status = gether_register_netdev(gether_opts->net);
- mutex_unlock(&gether_opts->lock);
- if (status)
- return status;
- gether_opts->bound = true;
- }
+ scoped_guard(mutex, &gether_opts->lock)
+ if (gether_opts->bind_count == 0 && !gether_opts->bound) {
+ if (!device_is_registered(&gether_opts->net->dev)) {
+ gether_set_gadget(gether_opts->net, cdev->gadget);
+ status = gether_register_netdev(gether_opts->net);
+ } else
+ status = gether_attach_gadget(gether_opts->net, cdev->gadget);
+
+ if (status)
+ return status;
+ net = gether_opts->net;
+ }
us = usb_gstrings_attach(cdev, geth_strings,
ARRAY_SIZE(geth_string_defs));
@@ -329,20 +327,18 @@ geth_bind(struct usb_configuration *c, struct usb_function *f)
/* allocate instance-specific interface IDs */
status = usb_interface_id(c, f);
if (status < 0)
- goto fail;
+ return status;
subset_data_intf.bInterfaceNumber = status;
- status = -ENODEV;
-
/* allocate instance-specific endpoints */
ep = usb_ep_autoconfig(cdev->gadget, &fs_subset_in_desc);
if (!ep)
- goto fail;
+ return -ENODEV;
geth->port.in_ep = ep;
ep = usb_ep_autoconfig(cdev->gadget, &fs_subset_out_desc);
if (!ep)
- goto fail;
+ return -ENODEV;
geth->port.out_ep = ep;
/* support all relevant hardware speeds... we expect that when
@@ -360,21 +356,19 @@ geth_bind(struct usb_configuration *c, struct usb_function *f)
status = usb_assign_descriptors(f, fs_eth_function, hs_eth_function,
ss_eth_function, ss_eth_function);
if (status)
- goto fail;
+ return status;
/* NOTE: all that is done without knowing or caring about
* the network link ... which is unavailable to this code
* until we're activated via set_alt().
*/
+ gether_opts->bind_count++;
+ retain_and_null_ptr(net);
+
DBG(cdev, "CDC Subset: IN/%s OUT/%s\n",
geth->port.in_ep->name, geth->port.out_ep->name);
return 0;
-
-fail:
- ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
-
- return status;
}
static inline struct f_gether_opts *to_f_gether_opts(struct config_item *item)
@@ -417,7 +411,7 @@ static void geth_free_inst(struct usb_function_instance *f)
struct f_gether_opts *opts;
opts = container_of(f, struct f_gether_opts, func_inst);
- if (opts->bound)
+ if (device_is_registered(&opts->net->dev))
gether_cleanup(netdev_priv(opts->net));
else
free_netdev(opts->net);
@@ -428,7 +422,7 @@ static struct usb_function_instance *geth_alloc_inst(void)
{
struct f_gether_opts *opts;
- opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+ opts = kzalloc_obj(*opts);
if (!opts)
return ERR_PTR(-ENOMEM);
mutex_init(&opts->lock);
@@ -449,15 +443,28 @@ static struct usb_function_instance *geth_alloc_inst(void)
static void geth_free(struct usb_function *f)
{
struct f_gether *eth;
+ struct f_gether_opts *opts;
+
+ opts = container_of(f->fi, struct f_gether_opts, func_inst);
eth = func_to_geth(f);
+ scoped_guard(mutex, &opts->lock)
+ opts->refcnt--;
kfree(eth);
}
static void geth_unbind(struct usb_configuration *c, struct usb_function *f)
{
+ struct f_gether_opts *opts;
+
+ opts = container_of(f->fi, struct f_gether_opts, func_inst);
+
geth_string_defs[0].id = 0;
usb_free_all_descriptors(f);
+
+ opts->bind_count--;
+ if (opts->bind_count == 0 && !opts->bound)
+ gether_detach_gadget(opts->net);
}
static struct usb_function *geth_alloc(struct usb_function_instance *fi)
@@ -467,7 +474,7 @@ static struct usb_function *geth_alloc(struct usb_function_instance *fi)
int status;
/* allocate and initialize one new instance */
- geth = kzalloc(sizeof(*geth), GFP_KERNEL);
+ geth = kzalloc_obj(*geth);
if (!geth)
return ERR_PTR(-ENOMEM);
diff --git a/drivers/usb/gadget/function/f_tcm.c b/drivers/usb/gadget/function/f_tcm.c
index 5a2e1237f85c..34d9f49e9987 100644
--- a/drivers/usb/gadget/function/f_tcm.c
+++ b/drivers/usb/gadget/function/f_tcm.c
@@ -1222,12 +1222,19 @@ static void usbg_submit_cmd(struct usbg_cmd *cmd)
se_cmd = &cmd->se_cmd;
tpg = cmd->fu->tpg;
tv_nexus = tpg->tpg_nexus;
+ if (!tv_nexus) {
+ struct usb_gadget *gadget = fuas_to_gadget(cmd->fu);
+
+ dev_err(&gadget->dev, "Missing nexus, ignoring command\n");
+ return;
+ }
+
dir = get_cmd_dir(cmd->cmd_buf);
if (dir < 0)
goto out;
target_submit_cmd(se_cmd, tv_nexus->tvn_se_sess, cmd->cmd_buf,
- cmd->sense_iu.sense, cmd->unpacked_lun, 0,
+ cmd->sense_iu.sense, cmd->unpacked_lun, cmd->data_len,
cmd->prio_attr, dir, flags);
return;
@@ -1389,6 +1396,7 @@ static int usbg_submit_command(struct f_uas *fu, struct usb_request *req)
cmd->tmr_func = 0;
cmd->tmr_rsp = RC_RESPONSE_UNKNOWN;
cmd->flags = 0;
+ cmd->data_len = 0;
cmd_iu = (struct command_iu *)iu;
@@ -1482,6 +1490,13 @@ static void bot_cmd_work(struct work_struct *work)
se_cmd = &cmd->se_cmd;
tpg = cmd->fu->tpg;
tv_nexus = tpg->tpg_nexus;
+ if (!tv_nexus) {
+ struct usb_gadget *gadget = fuas_to_gadget(cmd->fu);
+
+ dev_err(&gadget->dev, "Missing nexus, ignoring command\n");
+ return;
+ }
+
dir = get_cmd_dir(cmd->cmd_buf);
if (dir < 0)
goto out;
@@ -1641,14 +1656,14 @@ static struct se_portal_group *usbg_make_tpg(struct se_wwn *wwn,
struct usbg_tport *tport = container_of(wwn, struct usbg_tport,
tport_wwn);
struct usbg_tpg *tpg;
- unsigned long tpgt;
+ u16 tpgt;
int ret;
struct f_tcm_opts *opts;
unsigned i;
if (strstr(name, "tpgt_") != name)
return ERR_PTR(-EINVAL);
- if (kstrtoul(name + 5, 0, &tpgt) || tpgt > UINT_MAX)
+ if (kstrtou16(name + 5, 0, &tpgt))
return ERR_PTR(-EINVAL);
ret = -ENODEV;
mutex_lock(&tpg_instances_lock);
@@ -1675,7 +1690,7 @@ static struct se_portal_group *usbg_make_tpg(struct se_wwn *wwn,
goto unlock_dep;
}
- tpg = kzalloc(sizeof(struct usbg_tpg), GFP_KERNEL);
+ tpg = kzalloc_obj(struct usbg_tpg);
ret = -ENOMEM;
if (!tpg)
goto unref_dep;
@@ -1767,7 +1782,7 @@ static struct se_wwn *usbg_make_tport(
if (!wnn_name)
return ERR_PTR(-EINVAL);
- tport = kzalloc(sizeof(struct usbg_tport), GFP_KERNEL);
+ tport = kzalloc_obj(struct usbg_tport);
if (!(tport))
return ERR_PTR(-ENOMEM);
@@ -1860,7 +1875,7 @@ static int tcm_usbg_make_nexus(struct usbg_tpg *tpg, char *name)
goto out_unlock;
}
- tv_nexus = kzalloc(sizeof(*tv_nexus), GFP_KERNEL);
+ tv_nexus = kzalloc_obj(*tv_nexus);
if (!tv_nexus) {
ret = -ENOMEM;
goto out_unlock;
@@ -2015,6 +2030,7 @@ static const struct target_core_fabric_ops usbg_ops = {
.tfc_wwn_attrs = usbg_wwn_attrs,
.tfc_tpg_base_attrs = usbg_base_attrs,
+ .default_compl_type = TARGET_QUEUE_COMPL,
.default_submit_type = TARGET_DIRECT_SUBMIT,
.direct_submit_supp = 1,
};
@@ -2399,7 +2415,7 @@ static int tcm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
if ((alt == USB_G_ALT_INT_BBB) || (alt == USB_G_ALT_INT_UAS)) {
struct guas_setup_wq *work;
- work = kmalloc(sizeof(*work), GFP_ATOMIC);
+ work = kmalloc_obj(*work, GFP_ATOMIC);
if (!work)
return -ENOMEM;
INIT_WORK(&work->work, tcm_delayed_set_alt);
@@ -2446,7 +2462,7 @@ static void tcm_attr_release(struct config_item *item)
usb_put_function_instance(&opts->func_inst);
}
-static struct configfs_item_operations tcm_item_ops = {
+static const struct configfs_item_operations tcm_item_ops = {
.release = tcm_attr_release,
};
@@ -2534,7 +2550,7 @@ static struct usb_function_instance *tcm_alloc_inst(void)
int i;
- opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+ opts = kzalloc_obj(*opts);
if (!opts)
return ERR_PTR(-ENOMEM);
@@ -2589,7 +2605,7 @@ static struct usb_function *tcm_alloc(struct usb_function_instance *fi)
return ERR_PTR(-ENODEV);
}
- fu = kzalloc(sizeof(*fu), GFP_KERNEL);
+ fu = kzalloc_obj(*fu);
if (!fu) {
mutex_unlock(&tpg_instances_lock);
return ERR_PTR(-ENOMEM);
diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c
index c87e74afc881..85c502e98f57 100644
--- a/drivers/usb/gadget/function/f_uac1.c
+++ b/drivers/usb/gadget/function/f_uac1.c
@@ -451,7 +451,7 @@ static int audio_notify(struct g_audio *audio, int unit_id, int cs)
goto err_dec_int_count;
}
- msg = kmalloc(sizeof(*msg), GFP_ATOMIC);
+ msg = kmalloc_obj(*msg, GFP_ATOMIC);
if (msg == NULL) {
ret = -ENOMEM;
goto err_free_request;
@@ -1512,7 +1512,7 @@ static void f_uac1_attr_release(struct config_item *item)
usb_put_function_instance(&opts->func_inst);
}
-static struct configfs_item_operations f_uac1_item_ops = {
+static const struct configfs_item_operations f_uac1_item_ops = {
.release = f_uac1_attr_release,
};
@@ -1634,7 +1634,7 @@ static ssize_t f_uac1_opts_##name##_show(struct config_item *item, \
int result; \
\
mutex_lock(&opts->lock); \
- result = scnprintf(page, sizeof(opts->name), "%s", opts->name); \
+ result = sysfs_emit(page, "%s", opts->name); \
mutex_unlock(&opts->lock); \
\
return result; \
@@ -1748,7 +1748,7 @@ static struct usb_function_instance *f_audio_alloc_inst(void)
{
struct f_uac1_opts *opts;
- opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+ opts = kzalloc_obj(*opts);
if (!opts)
return ERR_PTR(-ENOMEM);
@@ -1831,7 +1831,7 @@ static struct usb_function *f_audio_alloc(struct usb_function_instance *fi)
struct f_uac1_opts *opts;
/* allocate and initialize one new instance */
- uac1 = kzalloc(sizeof(*uac1), GFP_KERNEL);
+ uac1 = kzalloc_obj(*uac1);
if (!uac1)
return ERR_PTR(-ENOMEM);
diff --git a/drivers/usb/gadget/function/f_uac1_legacy.c b/drivers/usb/gadget/function/f_uac1_legacy.c
index 49cf5aae90ca..5d201a2e30e7 100644
--- a/drivers/usb/gadget/function/f_uac1_legacy.c
+++ b/drivers/usb/gadget/function/f_uac1_legacy.c
@@ -251,7 +251,7 @@ static struct f_audio_buf *f_audio_buffer_alloc(int buf_size)
{
struct f_audio_buf *copy_buf;
- copy_buf = kzalloc(sizeof *copy_buf, GFP_ATOMIC);
+ copy_buf = kzalloc_obj(*copy_buf, GFP_ATOMIC);
if (!copy_buf)
return ERR_PTR(-ENOMEM);
@@ -360,19 +360,46 @@ static int f_audio_out_ep_complete(struct usb_ep *ep, struct usb_request *req)
static void f_audio_complete(struct usb_ep *ep, struct usb_request *req)
{
struct f_audio *audio = req->context;
- int status = req->status;
- u32 data = 0;
struct usb_ep *out_ep = audio->out_ep;
- switch (status) {
-
- case 0: /* normal completion? */
- if (ep == out_ep)
+ switch (req->status) {
+ case 0:
+ if (ep == out_ep) {
f_audio_out_ep_complete(ep, req);
- else if (audio->set_con) {
- memcpy(&data, req->buf, req->length);
- audio->set_con->set(audio->set_con, audio->set_cmd,
- le16_to_cpu(data));
+ } else if (audio->set_con) {
+ struct usb_audio_control *con = audio->set_con;
+ u8 type = con->type;
+ u32 data;
+ bool valid_request = false;
+
+ switch (type) {
+ case UAC_FU_MUTE: {
+ u8 value;
+
+ if (req->actual == sizeof(value)) {
+ memcpy(&value, req->buf, sizeof(value));
+ data = value;
+ valid_request = true;
+ }
+ break;
+ }
+ case UAC_FU_VOLUME: {
+ __le16 value;
+
+ if (req->actual == sizeof(value)) {
+ memcpy(&value, req->buf, sizeof(value));
+ data = le16_to_cpu(value);
+ valid_request = true;
+ }
+ break;
+ }
+ }
+
+ if (valid_request)
+ con->set(con, audio->set_cmd, data);
+ else
+ usb_ep_set_halt(ep);
+
audio->set_con = NULL;
}
break;
@@ -812,7 +839,7 @@ static void f_uac1_attr_release(struct config_item *item)
usb_put_function_instance(&opts->func_inst);
}
-static struct configfs_item_operations f_uac1_item_ops = {
+static const struct configfs_item_operations f_uac1_item_ops = {
.release = f_uac1_attr_release,
};
@@ -942,7 +969,7 @@ static struct usb_function_instance *f_audio_alloc_inst(void)
{
struct f_uac1_legacy_opts *opts;
- opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+ opts = kzalloc_obj(*opts);
if (!opts)
return ERR_PTR(-ENOMEM);
@@ -985,7 +1012,7 @@ static struct usb_function *f_audio_alloc(struct usb_function_instance *fi)
struct f_uac1_legacy_opts *opts;
/* allocate and initialize one new instance */
- audio = kzalloc(sizeof(*audio), GFP_KERNEL);
+ audio = kzalloc_obj(*audio);
if (!audio)
return ERR_PTR(-ENOMEM);
diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
index 9b324821c93b..897787d0803c 100644
--- a/drivers/usb/gadget/function/f_uac2.c
+++ b/drivers/usb/gadget/function/f_uac2.c
@@ -1388,7 +1388,7 @@ afunc_notify(struct g_audio *agdev, int unit_id, int cs)
goto err_dec_int_count;
}
- msg = kzalloc(sizeof(*msg), GFP_ATOMIC);
+ msg = kzalloc_obj(*msg, GFP_ATOMIC);
if (msg == NULL) {
ret = -ENOMEM;
goto err_free_request;
@@ -1874,7 +1874,7 @@ static void f_uac2_attr_release(struct config_item *item)
usb_put_function_instance(&opts->func_inst);
}
-static struct configfs_item_operations f_uac2_item_ops = {
+static const struct configfs_item_operations f_uac2_item_ops = {
.release = f_uac2_attr_release,
};
@@ -2052,7 +2052,7 @@ static ssize_t f_uac2_opts_##name##_show(struct config_item *item, \
int result; \
\
mutex_lock(&opts->lock); \
- result = scnprintf(page, sizeof(opts->name), "%s", opts->name); \
+ result = sysfs_emit(page, "%s", opts->name); \
mutex_unlock(&opts->lock); \
\
return result; \
@@ -2189,7 +2189,7 @@ static struct usb_function_instance *afunc_alloc_inst(void)
{
struct f_uac2_opts *opts;
- opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+ opts = kzalloc_obj(*opts);
if (!opts)
return ERR_PTR(-ENOMEM);
@@ -2278,7 +2278,7 @@ static struct usb_function *afunc_alloc(struct usb_function_instance *fi)
struct f_uac2 *uac2;
struct f_uac2_opts *opts;
- uac2 = kzalloc(sizeof(*uac2), GFP_KERNEL);
+ uac2 = kzalloc_obj(*uac2);
if (uac2 == NULL)
return ERR_PTR(-ENOMEM);
diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c
index aa6ab666741a..8d404d88391c 100644
--- a/drivers/usb/gadget/function/f_uvc.c
+++ b/drivers/usb/gadget/function/f_uvc.c
@@ -362,6 +362,10 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt)
return ret;
usb_ep_enable(uvc->video.ep);
+ uvc->video.max_req_size = uvc->video.ep->maxpacket
+ * max_t(unsigned int, uvc->video.ep->maxburst, 1)
+ * (uvc->video.ep->mult);
+
memset(&v4l2_event, 0, sizeof(v4l2_event));
v4l2_event.type = UVC_EVENT_STREAMON;
v4l2_event_queue(&uvc->vdev, &v4l2_event);
@@ -409,6 +413,12 @@ uvc_function_disconnect(struct uvc_device *uvc)
{
int ret;
+ guard(mutex)(&uvc->lock);
+ if (uvc->func_unbound) {
+ dev_dbg(&uvc->vdev.dev, "skipping function deactivate (unbound)\n");
+ return;
+ }
+
if ((ret = usb_function_deactivate(&uvc->func)) < 0)
uvcg_info(&uvc->func, "UVC disconnect failed with %d\n", ret);
}
@@ -427,6 +437,15 @@ static ssize_t function_name_show(struct device *dev,
static DEVICE_ATTR_RO(function_name);
+static void uvc_vdev_release(struct video_device *vdev)
+{
+ struct uvc_device *uvc = video_get_drvdata(vdev);
+
+ /* Signal uvc_function_unbind() that the video device has been released */
+ if (uvc->vdev_release_done)
+ complete(uvc->vdev_release_done);
+}
+
static int
uvc_register_video(struct uvc_device *uvc)
{
@@ -439,7 +458,7 @@ uvc_register_video(struct uvc_device *uvc)
uvc->vdev.v4l2_dev->dev = &cdev->gadget->dev;
uvc->vdev.fops = &uvc_v4l2_fops;
uvc->vdev.ioctl_ops = &uvc_v4l2_ioctl_ops;
- uvc->vdev.release = video_device_release_empty;
+ uvc->vdev.release = uvc_vdev_release;
uvc->vdev.vfl_dir = VFL_DIR_TX;
uvc->vdev.lock = &uvc->video.mutex;
uvc->vdev.device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
@@ -655,6 +674,8 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
int ret = -EINVAL;
uvcg_info(f, "%s()\n", __func__);
+ scoped_guard(mutex, &uvc->lock)
+ uvc->func_unbound = false;
opts = fi_to_f_uvc_opts(f->fi);
/* Sanity check the streaming endpoint module parameters. */
@@ -883,7 +904,7 @@ static struct usb_function_instance *uvc_alloc_inst(void)
struct uvc_descriptor_header **ctl_cls;
int ret;
- opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+ opts = kzalloc_obj(*opts);
if (!opts)
return ERR_PTR(-ENOMEM);
opts->func_inst.free_func_inst = uvc_free_inst;
@@ -984,12 +1005,19 @@ static void uvc_free(struct usb_function *f)
static void uvc_function_unbind(struct usb_configuration *c,
struct usb_function *f)
{
+ DECLARE_COMPLETION_ONSTACK(vdev_release_done);
struct usb_composite_dev *cdev = c->cdev;
struct uvc_device *uvc = to_uvc(f);
struct uvc_video *video = &uvc->video;
long wait_ret = 1;
+ bool connected;
uvcg_info(f, "%s()\n", __func__);
+ scoped_guard(mutex, &uvc->lock) {
+ uvc->func_unbound = true;
+ uvc->vdev_release_done = &vdev_release_done;
+ connected = uvc->func_connected;
+ }
kthread_cancel_work_sync(&video->hw_submit);
@@ -1002,7 +1030,7 @@ static void uvc_function_unbind(struct usb_configuration *c,
* though the video device removal uevent. Allow some time for the
* application to close out before things get deleted.
*/
- if (uvc->func_connected) {
+ if (connected) {
uvcg_dbg(f, "waiting for clean disconnect\n");
wait_ret = wait_event_interruptible_timeout(uvc->func_connected_queue,
uvc->func_connected == false, msecs_to_jiffies(500));
@@ -1013,7 +1041,10 @@ static void uvc_function_unbind(struct usb_configuration *c,
video_unregister_device(&uvc->vdev);
v4l2_device_unregister(&uvc->v4l2_dev);
- if (uvc->func_connected) {
+ scoped_guard(mutex, &uvc->lock)
+ connected = uvc->func_connected;
+
+ if (connected) {
/*
* Wait for the release to occur to ensure there are no longer any
* pending operations that may cause panics when resources are cleaned
@@ -1025,6 +1056,10 @@ static void uvc_function_unbind(struct usb_configuration *c,
uvcg_dbg(f, "done waiting for release with ret: %ld\n", wait_ret);
}
+ /* Wait for the video device to be released */
+ wait_for_completion(&vdev_release_done);
+ uvc->vdev_release_done = NULL;
+
usb_ep_free_request(cdev->gadget->ep0, uvc->control_req);
kfree(uvc->control_buf);
@@ -1038,11 +1073,13 @@ static struct usb_function *uvc_alloc(struct usb_function_instance *fi)
struct uvc_descriptor_header **strm_cls;
struct config_item *streaming, *header, *h;
- uvc = kzalloc(sizeof(*uvc), GFP_KERNEL);
+ uvc = kzalloc_obj(*uvc);
if (uvc == NULL)
return ERR_PTR(-ENOMEM);
mutex_init(&uvc->video.mutex);
+ mutex_init(&uvc->lock);
+ uvc->func_unbound = true;
uvc->state = UVC_STATE_DISCONNECTED;
init_waitqueue_head(&uvc->func_connected_queue);
opts = fi_to_f_uvc_opts(fi);
diff --git a/drivers/usb/gadget/function/g_zero.h b/drivers/usb/gadget/function/g_zero.h
index 98b8462ad538..7bd66004821f 100644
--- a/drivers/usb/gadget/function/g_zero.h
+++ b/drivers/usb/gadget/function/g_zero.h
@@ -34,6 +34,7 @@ struct f_ss_opts {
unsigned isoc_mult;
unsigned isoc_maxburst;
unsigned bulk_buflen;
+ unsigned bulk_maxburst;
unsigned bulk_qlen;
unsigned iso_qlen;
diff --git a/drivers/usb/gadget/function/rndis.c b/drivers/usb/gadget/function/rndis.c
index afd75d72412c..3da54a7d7aba 100644
--- a/drivers/usb/gadget/function/rndis.c
+++ b/drivers/usb/gadget/function/rndis.c
@@ -892,7 +892,7 @@ struct rndis_params *rndis_register(void (*resp_avail)(void *v), void *v)
return ERR_PTR(-ENODEV);
}
- params = kzalloc(sizeof(*params), GFP_KERNEL);
+ params = kzalloc_obj(*params);
if (!params) {
rndis_put_nr(i);
diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c
index ca8dbec65f73..e53f2927b539 100644
--- a/drivers/usb/gadget/function/u_audio.c
+++ b/drivers/usb/gadget/function/u_audio.c
@@ -1191,7 +1191,7 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
if (!g_audio)
return -EINVAL;
- uac = kzalloc(sizeof(*uac), GFP_KERNEL);
+ uac = kzalloc_obj(*uac);
if (!uac)
return -ENOMEM;
g_audio->uac = uac;
@@ -1209,9 +1209,8 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
prm->max_psize = g_audio->out_ep_maxpsize;
prm->srate = params->c_srates[0];
- prm->reqs = kcalloc(params->req_number,
- sizeof(struct usb_request *),
- GFP_KERNEL);
+ prm->reqs = kzalloc_objs(struct usb_request *,
+ params->req_number);
if (!prm->reqs) {
err = -ENOMEM;
goto fail;
@@ -1234,9 +1233,8 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
prm->max_psize = g_audio->in_ep_maxpsize;
prm->srate = params->p_srates[0];
- prm->reqs = kcalloc(params->req_number,
- sizeof(struct usb_request *),
- GFP_KERNEL);
+ prm->reqs = kzalloc_objs(struct usb_request *,
+ params->req_number);
if (!prm->reqs) {
err = -ENOMEM;
goto fail;
diff --git a/drivers/usb/gadget/function/u_ecm.h b/drivers/usb/gadget/function/u_ecm.h
index 77cfb89932be..7f666b9dea02 100644
--- a/drivers/usb/gadget/function/u_ecm.h
+++ b/drivers/usb/gadget/function/u_ecm.h
@@ -15,17 +15,26 @@
#include <linux/usb/composite.h>
+/**
+ * struct f_ecm_opts - ECM function options
+ * @func_inst: USB function instance.
+ * @net: The net_device associated with the ECM function.
+ * @bound: True if the net_device is shared and pre-registered during the
+ * legacy composite driver's bind phase (e.g., multi.c). If false,
+ * the ECM function will register the net_device during its own
+ * bind phase.
+ * @bind_count: Tracks the number of configurations the ECM function is
+ * bound to, preventing double-registration of the @net device.
+ * @lock: Protects the data from concurrent access by configfs read/write
+ * and create symlink/remove symlink operations.
+ * @refcnt: Reference counter for the function instance.
+ */
struct f_ecm_opts {
struct usb_function_instance func_inst;
struct net_device *net;
bool bound;
+ int bind_count;
- /*
- * Read/write access to configfs attributes is handled by configfs.
- *
- * This is to protect the data from concurrent access by read/write
- * and create symlink/remove symlink.
- */
struct mutex lock;
int refcnt;
};
diff --git a/drivers/usb/gadget/function/u_eem.h b/drivers/usb/gadget/function/u_eem.h
index 3bd85dfcd71c..78ef55815219 100644
--- a/drivers/usb/gadget/function/u_eem.h
+++ b/drivers/usb/gadget/function/u_eem.h
@@ -15,17 +15,26 @@
#include <linux/usb/composite.h>
+/**
+ * struct f_eem_opts - EEM function options
+ * @func_inst: USB function instance.
+ * @net: The net_device associated with the EEM function.
+ * @bound: True if the net_device is shared and pre-registered during the
+ * legacy composite driver's bind phase (e.g., multi.c). If false,
+ * the EEM function will register the net_device during its own
+ * bind phase.
+ * @bind_count: Tracks the number of configurations the EEM function is
+ * bound to, preventing double-registration of the @net device.
+ * @lock: Protects the data from concurrent access by configfs read/write
+ * and create symlink/remove symlink operations.
+ * @refcnt: Reference counter for the function instance.
+ */
struct f_eem_opts {
struct usb_function_instance func_inst;
struct net_device *net;
bool bound;
+ int bind_count;
- /*
- * Read/write access to configfs attributes is handled by configfs.
- *
- * This is to protect the data from concurrent access by read/write
- * and create symlink/remove symlink.
- */
struct mutex lock;
int refcnt;
};
diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c
index f58590bf5e02..59d85d6a84a8 100644
--- a/drivers/usb/gadget/function/u_ether.c
+++ b/drivers/usb/gadget/function/u_ether.c
@@ -16,6 +16,7 @@
#include <linux/ctype.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
+#include <linux/hex.h>
#include <linux/if_vlan.h>
#include <linux/string_helpers.h>
#include <linux/usb/composite.h>
@@ -112,8 +113,10 @@ static void eth_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *p)
strscpy(p->driver, "g_ether", sizeof(p->driver));
strscpy(p->version, UETH__VERSION, sizeof(p->version));
- strscpy(p->fw_version, dev->gadget->name, sizeof(p->fw_version));
- strscpy(p->bus_info, dev_name(&dev->gadget->dev), sizeof(p->bus_info));
+ if (dev->gadget) {
+ strscpy(p->fw_version, dev->gadget->name, sizeof(p->fw_version));
+ strscpy(p->bus_info, dev_name(&dev->gadget->dev), sizeof(p->bus_info));
+ }
}
/* REVISIT can also support:
@@ -896,6 +899,28 @@ void gether_set_gadget(struct net_device *net, struct usb_gadget *g)
}
EXPORT_SYMBOL_GPL(gether_set_gadget);
+int gether_attach_gadget(struct net_device *net, struct usb_gadget *g)
+{
+ int ret;
+
+ ret = device_move(&net->dev, &g->dev, DPM_ORDER_DEV_AFTER_PARENT);
+ if (ret)
+ return ret;
+
+ gether_set_gadget(net, g);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(gether_attach_gadget);
+
+void gether_detach_gadget(struct net_device *net)
+{
+ struct eth_dev *dev = netdev_priv(net);
+
+ device_move(&net->dev, NULL, DPM_ORDER_NONE);
+ dev->gadget = NULL;
+}
+EXPORT_SYMBOL_GPL(gether_detach_gadget);
+
int gether_set_dev_addr(struct net_device *net, const char *dev_addr)
{
struct eth_dev *dev;
@@ -1200,6 +1225,11 @@ void gether_disconnect(struct gether *link)
DBG(dev, "%s\n", __func__);
+ spin_lock(&dev->lock);
+ dev->port_usb = NULL;
+ link->is_suspend = false;
+ spin_unlock(&dev->lock);
+
netif_stop_queue(dev->net);
netif_carrier_off(dev->net);
@@ -1237,11 +1267,6 @@ void gether_disconnect(struct gether *link)
dev->header_len = 0;
dev->unwrap = NULL;
dev->wrap = NULL;
-
- spin_lock(&dev->lock);
- dev->port_usb = NULL;
- link->is_suspend = false;
- spin_unlock(&dev->lock);
}
EXPORT_SYMBOL_GPL(gether_disconnect);
diff --git a/drivers/usb/gadget/function/u_ether.h b/drivers/usb/gadget/function/u_ether.h
index 34be220cef77..c85a1cf3c115 100644
--- a/drivers/usb/gadget/function/u_ether.h
+++ b/drivers/usb/gadget/function/u_ether.h
@@ -151,6 +151,32 @@ static inline struct net_device *gether_setup_default(void)
void gether_set_gadget(struct net_device *net, struct usb_gadget *g);
/**
+ * gether_attach_gadget - Reparent net_device to the gadget device.
+ * @net: The network device to reparent.
+ * @g: The target USB gadget device to parent to.
+ *
+ * This function moves the network device to be a child of the USB gadget
+ * device in the device hierarchy. This is typically done when the function
+ * is bound to a configuration.
+ *
+ * Returns 0 on success, or a negative error code on failure.
+ */
+int gether_attach_gadget(struct net_device *net, struct usb_gadget *g);
+
+/**
+ * gether_detach_gadget - Detach net_device from its gadget parent.
+ * @net: The network device to detach.
+ *
+ * This function moves the network device to be a child of the virtual
+ * devices parent, effectively detaching it from the USB gadget device
+ * hierarchy. This is typically done when the function is unbound
+ * from a configuration but the instance is not yet freed.
+ */
+void gether_detach_gadget(struct net_device *net);
+
+DEFINE_FREE(detach_gadget, struct net_device *, if (_T) gether_detach_gadget(_T))
+
+/**
* gether_set_dev_addr - initialize an ethernet-over-usb link with eth address
* @net: device representing this link
* @dev_addr: eth address of this device
diff --git a/drivers/usb/gadget/function/u_ether_configfs.h b/drivers/usb/gadget/function/u_ether_configfs.h
index f558c3139ebe..51f0d79e5eca 100644
--- a/drivers/usb/gadget/function/u_ether_configfs.h
+++ b/drivers/usb/gadget/function/u_ether_configfs.h
@@ -21,7 +21,7 @@
usb_put_function_instance(&opts->func_inst); \
} \
\
- static struct configfs_item_operations _f_##_item_ops = { \
+ static const struct configfs_item_operations _f_##_item_ops = { \
.release = _f_##_attr_release, \
}
diff --git a/drivers/usb/gadget/function/u_fs.h b/drivers/usb/gadget/function/u_fs.h
index 4b3365f23fd7..6a80182aadd7 100644
--- a/drivers/usb/gadget/function/u_fs.h
+++ b/drivers/usb/gadget/function/u_fs.h
@@ -176,7 +176,7 @@ struct ffs_data {
/* reference counter */
refcount_t ref;
/* how many files are opened (EP0 and others) */
- atomic_t opened;
+ int opened;
/* EP0 state */
enum ffs_state state;
diff --git a/drivers/usb/gadget/function/u_gether.h b/drivers/usb/gadget/function/u_gether.h
index 2f7a373ed449..e7b6b51f69c1 100644
--- a/drivers/usb/gadget/function/u_gether.h
+++ b/drivers/usb/gadget/function/u_gether.h
@@ -15,17 +15,25 @@
#include <linux/usb/composite.h>
+/**
+ * struct f_gether_opts - subset function options
+ * @func_inst: USB function instance.
+ * @net: The net_device associated with the subset function.
+ * @bound: True if the net_device is shared and pre-registered during the
+ * legacy composite driver's bind phase (e.g., multi.c). If false,
+ * the subset function will register the net_device during its own
+ * bind phase.
+ * @bind_count: Tracks the number of configurations the subset function is
+ * bound to, preventing double-registration of the @net device.
+ * @lock: Protects the data from concurrent access by configfs read/write
+ * and create symlink/remove symlink operations.
+ * @refcnt: Reference counter for the function instance.
+ */
struct f_gether_opts {
struct usb_function_instance func_inst;
struct net_device *net;
bool bound;
-
- /*
- * Read/write access to configfs attributes is handled by configfs.
- *
- * This is to protect the data from concurrent access by read/write
- * and create symlink/remove symlink.
- */
+ int bind_count;
struct mutex lock;
int refcnt;
};
diff --git a/drivers/usb/gadget/function/u_hid.h b/drivers/usb/gadget/function/u_hid.h
index 84bb70292855..a9ed9720caee 100644
--- a/drivers/usb/gadget/function/u_hid.h
+++ b/drivers/usb/gadget/function/u_hid.h
@@ -25,6 +25,8 @@ struct f_hid_opts {
unsigned short report_desc_length;
unsigned char *report_desc;
bool report_desc_alloc;
+ unsigned char interval;
+ bool interval_user_set;
/*
* Protect the data form concurrent access by read/write
diff --git a/drivers/usb/gadget/function/u_midi.h b/drivers/usb/gadget/function/u_midi.h
index 2e400b495cb8..41cb8aa73f09 100644
--- a/drivers/usb/gadget/function/u_midi.h
+++ b/drivers/usb/gadget/function/u_midi.h
@@ -19,7 +19,7 @@ struct f_midi_opts {
struct usb_function_instance func_inst;
int index;
char *id;
- bool id_allocated;
+ char *interface_string;
unsigned int in_ports;
unsigned int out_ports;
unsigned int buflen;
diff --git a/drivers/usb/gadget/function/u_ncm.h b/drivers/usb/gadget/function/u_ncm.h
index 49ec095cdb4b..ce2f6358688a 100644
--- a/drivers/usb/gadget/function/u_ncm.h
+++ b/drivers/usb/gadget/function/u_ncm.h
@@ -15,20 +15,29 @@
#include <linux/usb/composite.h>
+/**
+ * struct f_ncm_opts - NCM function options
+ * @func_inst: USB function instance.
+ * @net: The net_device associated with the NCM function.
+ * @bind_count: Tracks the number of configurations the NCM function is
+ * bound to, preventing double-registration of the @net device.
+ * @ncm_interf_group: ConfigFS group for NCM interface.
+ * @ncm_os_desc: USB OS descriptor for NCM.
+ * @ncm_ext_compat_id: Extended compatibility ID.
+ * @lock: Protects the data from concurrent access by configfs read/write
+ * and create symlink/remove symlink operations.
+ * @refcnt: Reference counter for the function instance.
+ * @max_segment_size: Maximum segment size.
+ */
struct f_ncm_opts {
struct usb_function_instance func_inst;
struct net_device *net;
- bool bound;
+ int bind_count;
struct config_group *ncm_interf_group;
struct usb_os_desc ncm_os_desc;
char ncm_ext_compat_id[16];
- /*
- * Read/write access to configfs attributes is handled by configfs.
- *
- * This is to protect the data from concurrent access by read/write
- * and create symlink/remove symlink.
- */
+
struct mutex lock;
int refcnt;
diff --git a/drivers/usb/gadget/function/u_rndis.h b/drivers/usb/gadget/function/u_rndis.h
index a8c409b2f52f..4e64619714dc 100644
--- a/drivers/usb/gadget/function/u_rndis.h
+++ b/drivers/usb/gadget/function/u_rndis.h
@@ -15,12 +15,34 @@
#include <linux/usb/composite.h>
+/**
+ * struct f_rndis_opts - RNDIS function options
+ * @func_inst: USB function instance.
+ * @vendor_id: Vendor ID.
+ * @manufacturer: Manufacturer string.
+ * @net: The net_device associated with the RNDIS function.
+ * @bind_count: Tracks the number of configurations the RNDIS function is
+ * bound to, preventing double-registration of the @net device.
+ * @borrowed_net: True if the net_device is shared and pre-registered during
+ * the legacy composite driver's bind phase (e.g., multi.c).
+ * If false, the RNDIS function will register the net_device
+ * during its own bind phase.
+ * @rndis_interf_group: ConfigFS group for RNDIS interface.
+ * @rndis_os_desc: USB OS descriptor for RNDIS.
+ * @rndis_ext_compat_id: Extended compatibility ID.
+ * @class: USB class.
+ * @subclass: USB subclass.
+ * @protocol: USB protocol.
+ * @lock: Protects the data from concurrent access by configfs read/write
+ * and create symlink/remove symlink operations.
+ * @refcnt: Reference counter for the function instance.
+ */
struct f_rndis_opts {
struct usb_function_instance func_inst;
u32 vendor_id;
const char *manufacturer;
struct net_device *net;
- bool bound;
+ int bind_count;
bool borrowed_net;
struct config_group *rndis_interf_group;
@@ -30,13 +52,6 @@ struct f_rndis_opts {
u8 class;
u8 subclass;
u8 protocol;
-
- /*
- * Read/write access to configfs attributes is handled by configfs.
- *
- * This is to protect the data from concurrent access by read/write
- * and create symlink/remove symlink.
- */
struct mutex lock;
int refcnt;
};
diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c
index 36fff45e8c9b..cdd1dfc666c4 100644
--- a/drivers/usb/gadget/function/u_serial.c
+++ b/drivers/usb/gadget/function/u_serial.c
@@ -295,8 +295,8 @@ __acquires(&port->port_lock)
break;
}
- if (do_tty_wake && port->port.tty)
- tty_wakeup(port->port.tty);
+ if (do_tty_wake)
+ tty_port_tty_wakeup(&port->port);
return status;
}
@@ -544,20 +544,16 @@ static int gs_alloc_requests(struct usb_ep *ep, struct list_head *head,
static int gs_start_io(struct gs_port *port)
{
struct list_head *head = &port->read_pool;
- struct usb_ep *ep;
+ struct usb_ep *ep = port->port_usb->out;
int status;
unsigned started;
- if (!port->port_usb || !port->port.tty)
- return -EIO;
-
/* Allocate RX and TX I/O buffers. We can't easily do this much
* earlier (with GFP_KERNEL) because the requests are coupled to
* endpoints, as are the packet sizes we'll be using. Different
* configurations may use different endpoints with a given port;
* and high speed vs full speed changes packet sizes too.
*/
- ep = port->port_usb->out;
status = gs_alloc_requests(ep, head, gs_read_complete,
&port->read_allocated);
if (status)
@@ -578,7 +574,7 @@ static int gs_start_io(struct gs_port *port)
gs_start_tx(port);
/* Unblock any pending writes into our circular buffer, in case
* we didn't in gs_start_tx() */
- tty_wakeup(port->port.tty);
+ tty_port_tty_wakeup(&port->port);
} else {
/* Free reqs only if we are still connected */
if (port->port_usb) {
@@ -592,6 +588,17 @@ static int gs_start_io(struct gs_port *port)
return status;
}
+static int gserial_wakeup_host(struct gserial *gser)
+{
+ struct usb_function *func = &gser->func;
+ struct usb_gadget *gadget = func->config->cdev->gadget;
+
+ if (func->func_suspended)
+ return usb_func_wakeup(func);
+ else
+ return usb_gadget_wakeup(gadget);
+}
+
/*-------------------------------------------------------------------------*/
/* TTY Driver */
@@ -746,6 +753,8 @@ static ssize_t gs_write(struct tty_struct *tty, const u8 *buf, size_t count)
{
struct gs_port *port = tty->driver_data;
unsigned long flags;
+ int ret = 0;
+ struct gserial *gser = port->port_usb;
pr_vdebug("gs_write: ttyGS%d (%p) writing %zu bytes\n",
port->port_num, tty, count);
@@ -753,6 +762,17 @@ static ssize_t gs_write(struct tty_struct *tty, const u8 *buf, size_t count)
spin_lock_irqsave(&port->port_lock, flags);
if (count)
count = kfifo_in(&port->port_write_buf, buf, count);
+
+ if (port->suspended) {
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ ret = gserial_wakeup_host(gser);
+ if (ret) {
+ pr_debug("ttyGS%d: Remote wakeup failed:%d\n", port->port_num, ret);
+ return count;
+ }
+ spin_lock_irqsave(&port->port_lock, flags);
+ }
+
/* treat count == 0 as flush_chars() */
if (port->port_usb)
gs_start_tx(port);
@@ -781,10 +801,22 @@ static void gs_flush_chars(struct tty_struct *tty)
{
struct gs_port *port = tty->driver_data;
unsigned long flags;
+ int ret = 0;
+ struct gserial *gser = port->port_usb;
pr_vdebug("gs_flush_chars: (%d,%p)\n", port->port_num, tty);
spin_lock_irqsave(&port->port_lock, flags);
+ if (port->suspended) {
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ ret = gserial_wakeup_host(gser);
+ if (ret) {
+ pr_debug("ttyGS%d: Remote wakeup failed:%d\n", port->port_num, ret);
+ return;
+ }
+ spin_lock_irqsave(&port->port_lock, flags);
+ }
+
if (port->port_usb)
gs_start_tx(port);
spin_unlock_irqrestore(&port->port_lock, flags);
@@ -1050,11 +1082,11 @@ static int gs_console_init(struct gs_port *port)
if (port->console)
return 0;
- cons = kzalloc(sizeof(*port->console), GFP_KERNEL);
+ cons = kzalloc_obj(*port->console);
if (!cons)
return -ENOMEM;
- strcpy(cons->console.name, "ttyGS");
+ strscpy(cons->console.name, "ttyGS");
cons->console.write = gs_console_write;
cons->console.device = gs_console_device;
cons->console.flags = CON_PRINTBUFFER;
@@ -1183,7 +1215,7 @@ gs_port_alloc(unsigned port_num, struct usb_cdc_line_coding *coding)
goto out;
}
- port = kzalloc(sizeof(struct gs_port), GFP_KERNEL);
+ port = kzalloc_obj(struct gs_port);
if (port == NULL) {
ret = -ENOMEM;
goto out;
@@ -1464,6 +1496,14 @@ void gserial_suspend(struct gserial *gser)
return;
}
+ if (port->write_busy || port->write_started) {
+ /* Wakeup to host if there are ongoing transfers */
+ spin_unlock_irqrestore(&serial_port_lock, flags);
+ if (!gserial_wakeup_host(gser))
+ return;
+ spin_lock_irqsave(&serial_port_lock, flags);
+ }
+
spin_lock(&port->port_lock);
spin_unlock(&serial_port_lock);
port->suspended = true;
diff --git a/drivers/usb/gadget/function/u_uac1_legacy.c b/drivers/usb/gadget/function/u_uac1_legacy.c
index dd21c251542c..01016102fa17 100644
--- a/drivers/usb/gadget/function/u_uac1_legacy.c
+++ b/drivers/usb/gadget/function/u_uac1_legacy.c
@@ -106,7 +106,7 @@ static int playback_default_hw_params(struct gaudio_snd_dev *snd)
snd->channels = 2;
snd->rate = 48000;
- params = kzalloc(sizeof(*params), GFP_KERNEL);
+ params = kzalloc_obj(*params);
if (!params)
return -ENOMEM;
diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h
index 6f44dd732315..7abfdd5e1eef 100644
--- a/drivers/usb/gadget/function/uvc.h
+++ b/drivers/usb/gadget/function/uvc.h
@@ -107,7 +107,7 @@ struct uvc_video {
unsigned int width;
unsigned int height;
unsigned int imagesize;
- unsigned int interval;
+ unsigned int interval; /* in 100ns units */
struct mutex mutex; /* protects frame parameters */
unsigned int uvc_num_requests;
@@ -117,6 +117,7 @@ struct uvc_video {
/* Requests */
bool is_enabled; /* tracks whether video stream is enabled */
unsigned int req_size;
+ unsigned int max_req_size;
struct list_head ureqs; /* all uvc_requests allocated by uvc_video */
/* USB requests that the video pump thread can encode into */
@@ -154,6 +155,9 @@ struct uvc_device {
enum uvc_state state;
struct usb_function func;
struct uvc_video video;
+ struct completion *vdev_release_done;
+ struct mutex lock; /* protects func_unbound and func_connected */
+ bool func_unbound;
bool func_connected;
wait_queue_head_t func_connected_queue;
@@ -196,6 +200,11 @@ struct uvc_file_handle {
#define to_uvc_file_handle(handle) \
container_of(handle, struct uvc_file_handle, vfh)
+static inline struct uvc_file_handle *file_to_uvc_file_handle(struct file *filp)
+{
+ return container_of(file_to_v4l2_fh(filp), struct uvc_file_handle, vfh);
+}
+
/* ------------------------------------------------------------------------
* Functions
*/
diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c
index f131943254a4..70a1415ea401 100644
--- a/drivers/usb/gadget/function/uvc_configfs.c
+++ b/drivers/usb/gadget/function/uvc_configfs.c
@@ -12,6 +12,7 @@
#include "uvc_configfs.h"
+#include <linux/hex.h>
#include <linux/sort.h>
#include <linux/usb/uvc.h>
#include <linux/usb/video.h>
@@ -127,7 +128,7 @@ static void uvcg_config_item_release(struct config_item *item)
kfree(group);
}
-static struct configfs_item_operations uvcg_config_item_ops = {
+static const struct configfs_item_operations uvcg_config_item_ops = {
.release = uvcg_config_item_release,
};
@@ -157,7 +158,7 @@ static int uvcg_config_create_group(struct config_group *parent,
{
struct config_group *group;
- group = kzalloc(sizeof(*group), GFP_KERNEL);
+ group = kzalloc_obj(*group);
if (!group)
return -ENOMEM;
@@ -269,7 +270,7 @@ static struct config_item *uvcg_control_header_make(struct config_group *group,
{
struct uvcg_control_header *h;
- h = kzalloc(sizeof(*h), GFP_KERNEL);
+ h = kzalloc_obj(*h);
if (!h)
return ERR_PTR(-ENOMEM);
@@ -284,7 +285,7 @@ static struct config_item *uvcg_control_header_make(struct config_group *group,
return &h->item;
}
-static struct configfs_group_operations uvcg_control_header_grp_ops = {
+static const struct configfs_group_operations uvcg_control_header_grp_ops = {
.make_item = uvcg_control_header_make,
};
@@ -1232,7 +1233,7 @@ static void uvcg_extension_drop_link(struct config_item *src, struct config_item
mutex_unlock(su_mutex);
}
-static struct configfs_item_operations uvcg_extension_item_ops = {
+static const struct configfs_item_operations uvcg_extension_item_ops = {
.release = uvcg_extension_release,
.allow_link = uvcg_extension_allow_link,
.drop_link = uvcg_extension_drop_link,
@@ -1272,7 +1273,7 @@ static struct config_item *uvcg_extension_make(struct config_group *group, const
opts_item = group->cg_item.ci_parent->ci_parent;
opts = to_f_uvc_opts(opts_item);
- xu = kzalloc(sizeof(*xu), GFP_KERNEL);
+ xu = kzalloc_obj(*xu);
if (!xu)
return ERR_PTR(-ENOMEM);
@@ -1297,7 +1298,7 @@ static struct config_item *uvcg_extension_make(struct config_group *group, const
return &xu->item;
}
-static struct configfs_group_operations uvcg_extensions_grp_ops = {
+static const struct configfs_group_operations uvcg_extensions_grp_ops = {
.make_item = uvcg_extension_make,
.drop_item = uvcg_extension_drop,
};
@@ -1413,7 +1414,7 @@ out:
mutex_unlock(su_mutex);
}
-static struct configfs_item_operations uvcg_control_class_item_ops = {
+static const struct configfs_item_operations uvcg_control_class_item_ops = {
.release = uvcg_config_item_release,
.allow_link = uvcg_control_class_allow_link,
.drop_link = uvcg_control_class_drop_link,
@@ -1436,7 +1437,7 @@ static int uvcg_control_class_create_children(struct config_group *parent)
for (i = 0; i < ARRAY_SIZE(names); ++i) {
struct uvcg_control_class_group *group;
- group = kzalloc(sizeof(*group), GFP_KERNEL);
+ group = kzalloc_obj(*group);
if (!group)
return -ENOMEM;
@@ -1663,7 +1664,7 @@ static void uvcg_format_drop_link(struct config_item *src, struct config_item *t
mutex_unlock(su_mutex);
}
-static struct configfs_item_operations uvcg_format_item_operations = {
+static const struct configfs_item_operations uvcg_format_item_operations = {
.release = uvcg_config_item_release,
.allow_link = uvcg_format_allow_link,
.drop_link = uvcg_format_drop_link,
@@ -1784,7 +1785,7 @@ static int uvcg_streaming_header_allow_link(struct config_item *src,
uvcg_format_set_indices(to_config_group(target));
- format_ptr = kzalloc(sizeof(*format_ptr), GFP_KERNEL);
+ format_ptr = kzalloc_obj(*format_ptr);
if (!format_ptr) {
ret = -ENOMEM;
goto out;
@@ -1839,7 +1840,7 @@ out:
mutex_unlock(su_mutex);
}
-static struct configfs_item_operations uvcg_streaming_header_item_ops = {
+static const struct configfs_item_operations uvcg_streaming_header_item_ops = {
.release = uvcg_config_item_release,
.allow_link = uvcg_streaming_header_allow_link,
.drop_link = uvcg_streaming_header_drop_link,
@@ -1898,7 +1899,7 @@ static struct config_item
{
struct uvcg_streaming_header *h;
- h = kzalloc(sizeof(*h), GFP_KERNEL);
+ h = kzalloc_obj(*h);
if (!h)
return ERR_PTR(-ENOMEM);
@@ -1913,7 +1914,7 @@ static struct config_item
return &h->item;
}
-static struct configfs_group_operations uvcg_streaming_header_grp_ops = {
+static const struct configfs_group_operations uvcg_streaming_header_grp_ops = {
.make_item = uvcg_streaming_header_make,
};
@@ -2162,7 +2163,7 @@ static struct config_item *uvcg_frame_make(struct config_group *group,
struct config_item *opts_item;
struct uvcg_frame_ptr *frame_ptr;
- h = kzalloc(sizeof(*h), GFP_KERNEL);
+ h = kzalloc_obj(*h);
if (!h)
return ERR_PTR(-ENOMEM);
@@ -2196,7 +2197,7 @@ static struct config_item *uvcg_frame_make(struct config_group *group,
return ERR_PTR(-EINVAL);
}
- frame_ptr = kzalloc(sizeof(*frame_ptr), GFP_KERNEL);
+ frame_ptr = kzalloc_obj(*frame_ptr);
if (!frame_ptr) {
mutex_unlock(&opts->lock);
kfree(h);
@@ -2260,7 +2261,7 @@ static void uvcg_format_set_indices(struct config_group *fmt)
* streaming/uncompressed/<NAME>
*/
-static struct configfs_group_operations uvcg_uncompressed_group_ops = {
+static const struct configfs_group_operations uvcg_uncompressed_group_ops = {
.make_item = uvcg_frame_make,
.drop_item = uvcg_frame_drop,
};
@@ -2482,7 +2483,7 @@ static struct config_group *uvcg_uncompressed_make(struct config_group *group,
if (!color_match)
return ERR_PTR(-EINVAL);
- h = kzalloc(sizeof(*h), GFP_KERNEL);
+ h = kzalloc_obj(*h);
if (!h)
return ERR_PTR(-ENOMEM);
@@ -2507,7 +2508,7 @@ static struct config_group *uvcg_uncompressed_make(struct config_group *group,
return &h->fmt.group;
}
-static struct configfs_group_operations uvcg_uncompressed_grp_ops = {
+static const struct configfs_group_operations uvcg_uncompressed_grp_ops = {
.make_group = uvcg_uncompressed_make,
};
@@ -2524,7 +2525,7 @@ static const struct uvcg_config_group_type uvcg_uncompressed_grp_type = {
* streaming/mjpeg/<NAME>
*/
-static struct configfs_group_operations uvcg_mjpeg_group_ops = {
+static const struct configfs_group_operations uvcg_mjpeg_group_ops = {
.make_item = uvcg_frame_make,
.drop_item = uvcg_frame_drop,
};
@@ -2674,7 +2675,7 @@ static struct config_group *uvcg_mjpeg_make(struct config_group *group,
if (!color_match)
return ERR_PTR(-EINVAL);
- h = kzalloc(sizeof(*h), GFP_KERNEL);
+ h = kzalloc_obj(*h);
if (!h)
return ERR_PTR(-ENOMEM);
@@ -2697,7 +2698,7 @@ static struct config_group *uvcg_mjpeg_make(struct config_group *group,
return &h->fmt.group;
}
-static struct configfs_group_operations uvcg_mjpeg_grp_ops = {
+static const struct configfs_group_operations uvcg_mjpeg_grp_ops = {
.make_group = uvcg_mjpeg_make,
};
@@ -2714,7 +2715,7 @@ static const struct uvcg_config_group_type uvcg_mjpeg_grp_type = {
* streaming/framebased/<NAME>
*/
-static struct configfs_group_operations uvcg_framebased_group_ops = {
+static const struct configfs_group_operations uvcg_framebased_group_ops = {
.make_item = uvcg_frame_make,
.drop_item = uvcg_frame_drop,
};
@@ -2916,9 +2917,16 @@ static struct config_group *uvcg_framebased_make(struct config_group *group,
'H', '2', '6', '4', 0x00, 0x00, 0x10, 0x00,
0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71
};
+ struct uvcg_color_matching *color_match;
+ struct config_item *streaming;
struct uvcg_framebased *h;
- h = kzalloc(sizeof(*h), GFP_KERNEL);
+ streaming = group->cg_item.ci_parent;
+ color_match = uvcg_format_get_default_color_match(streaming);
+ if (!color_match)
+ return ERR_PTR(-EINVAL);
+
+ h = kzalloc_obj(*h);
if (!h)
return ERR_PTR(-ENOMEM);
@@ -2936,13 +2944,16 @@ static struct config_group *uvcg_framebased_make(struct config_group *group,
INIT_LIST_HEAD(&h->fmt.frames);
h->fmt.type = UVCG_FRAMEBASED;
+
+ h->fmt.color_matching = color_match;
+ color_match->refcnt++;
config_group_init_type_name(&h->fmt.group, name,
&uvcg_framebased_type);
return &h->fmt.group;
}
-static struct configfs_group_operations uvcg_framebased_grp_ops = {
+static const struct configfs_group_operations uvcg_framebased_grp_ops = {
.make_group = uvcg_framebased_make,
};
@@ -3045,7 +3056,7 @@ static void uvcg_color_matching_release(struct config_item *item)
kfree(color_match);
}
-static struct configfs_item_operations uvcg_color_matching_item_ops = {
+static const struct configfs_item_operations uvcg_color_matching_item_ops = {
.release = uvcg_color_matching_release,
};
@@ -3064,7 +3075,7 @@ static struct config_group *uvcg_color_matching_make(struct config_group *group,
{
struct uvcg_color_matching *color_match;
- color_match = kzalloc(sizeof(*color_match), GFP_KERNEL);
+ color_match = kzalloc_obj(*color_match);
if (!color_match)
return ERR_PTR(-ENOMEM);
@@ -3078,7 +3089,7 @@ static struct config_group *uvcg_color_matching_make(struct config_group *group,
return &color_match->group;
}
-static struct configfs_group_operations uvcg_color_matching_grp_group_ops = {
+static const struct configfs_group_operations uvcg_color_matching_grp_group_ops = {
.make_group = uvcg_color_matching_make,
};
@@ -3086,7 +3097,7 @@ static int uvcg_color_matching_create_children(struct config_group *parent)
{
struct uvcg_color_matching *color_match;
- color_match = kzalloc(sizeof(*color_match), GFP_KERNEL);
+ color_match = kzalloc_obj(*color_match);
if (!color_match)
return -ENOMEM;
@@ -3519,7 +3530,7 @@ out:
mutex_unlock(su_mutex);
}
-static struct configfs_item_operations uvcg_streaming_class_item_ops = {
+static const struct configfs_item_operations uvcg_streaming_class_item_ops = {
.release = uvcg_config_item_release,
.allow_link = uvcg_streaming_class_allow_link,
.drop_link = uvcg_streaming_class_drop_link,
@@ -3542,7 +3553,7 @@ static int uvcg_streaming_class_create_children(struct config_group *parent)
for (i = 0; i < ARRAY_SIZE(names); ++i) {
struct uvcg_streaming_class_group *group;
- group = kzalloc(sizeof(*group), GFP_KERNEL);
+ group = kzalloc_obj(*group);
if (!group)
return -ENOMEM;
@@ -3687,7 +3698,7 @@ static void uvc_func_drop_link(struct config_item *src, struct config_item *tgt)
mutex_unlock(&opts->lock);
}
-static struct configfs_item_operations uvc_func_item_ops = {
+static const struct configfs_item_operations uvc_func_item_ops = {
.release = uvc_func_item_release,
.allow_link = uvc_func_allow_link,
.drop_link = uvc_func_drop_link,
diff --git a/drivers/usb/gadget/function/uvc_configfs.h b/drivers/usb/gadget/function/uvc_configfs.h
index 2f78cd4f396f..9391614135e9 100644
--- a/drivers/usb/gadget/function/uvc_configfs.h
+++ b/drivers/usb/gadget/function/uvc_configfs.h
@@ -74,10 +74,12 @@ static inline struct uvcg_format *to_uvcg_format(struct config_item *item)
struct uvcg_streaming_header {
struct config_item item;
- struct uvc_input_header_descriptor desc;
unsigned linked;
struct list_head formats;
unsigned num_fmt;
+
+ /* Must be last --ends in a flexible-array member. */
+ struct uvc_input_header_descriptor desc;
};
static inline struct uvcg_streaming_header *to_uvcg_streaming_header(struct config_item *item)
diff --git a/drivers/usb/gadget/function/uvc_queue.c b/drivers/usb/gadget/function/uvc_queue.c
index 5eaeae3e2441..586e5524c171 100644
--- a/drivers/usb/gadget/function/uvc_queue.c
+++ b/drivers/usb/gadget/function/uvc_queue.c
@@ -86,10 +86,17 @@ static int uvc_buffer_prepare(struct vb2_buffer *vb)
buf->bytesused = 0;
} else {
buf->bytesused = vb2_get_plane_payload(vb, 0);
- buf->req_payload_size =
- DIV_ROUND_UP(buf->bytesused +
- (video->reqs_per_frame * UVCG_REQUEST_HEADER_LEN),
- video->reqs_per_frame);
+
+ if (video->reqs_per_frame != 0) {
+ buf->req_payload_size =
+ DIV_ROUND_UP(buf->bytesused +
+ (video->reqs_per_frame * UVCG_REQUEST_HEADER_LEN),
+ video->reqs_per_frame);
+ if (buf->req_payload_size > video->req_size)
+ buf->req_payload_size = video->req_size;
+ } else {
+ buf->req_payload_size = video->max_req_size;
+ }
}
return 0;
@@ -122,8 +129,6 @@ static const struct vb2_ops uvc_queue_qops = {
.queue_setup = uvc_queue_setup,
.buf_prepare = uvc_buffer_prepare,
.buf_queue = uvc_buffer_queue,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
};
int uvcg_queue_init(struct uvc_video_queue *queue, struct device *dev, enum v4l2_buf_type type,
@@ -177,7 +182,15 @@ int uvcg_alloc_buffers(struct uvc_video_queue *queue,
{
int ret;
+retry:
ret = vb2_reqbufs(&queue->queue, rb);
+ if (ret < 0 && queue->use_sg) {
+ uvc_trace(UVC_TRACE_IOCTL,
+ "failed to alloc buffer with sg enabled, try non-sg mode\n");
+ queue->use_sg = 0;
+ queue->queue.mem_ops = &vb2_vmalloc_memops;
+ goto retry;
+ }
return ret ? ret : rb->count;
}
diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c
index fc9a8d31a1e9..514e5930b9ca 100644
--- a/drivers/usb/gadget/function/uvc_v4l2.c
+++ b/drivers/usb/gadget/function/uvc_v4l2.c
@@ -574,6 +574,8 @@ uvc_v4l2_subscribe_event(struct v4l2_fh *fh,
if (sub->type < UVC_EVENT_FIRST || sub->type > UVC_EVENT_LAST)
return -EINVAL;
+ guard(mutex)(&uvc->lock);
+
if (sub->type == UVC_EVENT_SETUP && uvc->func_connected)
return -EBUSY;
@@ -595,7 +597,8 @@ static void uvc_v4l2_disable(struct uvc_device *uvc)
uvc_function_disconnect(uvc);
uvcg_video_disable(&uvc->video);
uvcg_free_buffers(&uvc->video.queue);
- uvc->func_connected = false;
+ scoped_guard(mutex, &uvc->lock)
+ uvc->func_connected = false;
wake_up_interruptible(&uvc->func_connected_queue);
}
@@ -667,15 +670,14 @@ uvc_v4l2_open(struct file *file)
struct uvc_device *uvc = video_get_drvdata(vdev);
struct uvc_file_handle *handle;
- handle = kzalloc(sizeof(*handle), GFP_KERNEL);
+ handle = kzalloc_obj(*handle);
if (handle == NULL)
return -ENOMEM;
v4l2_fh_init(&handle->vfh, vdev);
- v4l2_fh_add(&handle->vfh);
+ v4l2_fh_add(&handle->vfh, file);
handle->device = &uvc->video;
- file->private_data = &handle->vfh;
return 0;
}
@@ -685,7 +687,7 @@ uvc_v4l2_release(struct file *file)
{
struct video_device *vdev = video_devdata(file);
struct uvc_device *uvc = video_get_drvdata(vdev);
- struct uvc_file_handle *handle = to_uvc_file_handle(file->private_data);
+ struct uvc_file_handle *handle = file_to_uvc_file_handle(file);
struct uvc_video *video = handle->device;
mutex_lock(&video->mutex);
@@ -693,8 +695,7 @@ uvc_v4l2_release(struct file *file)
uvc_v4l2_disable(uvc);
mutex_unlock(&video->mutex);
- file->private_data = NULL;
- v4l2_fh_del(&handle->vfh);
+ v4l2_fh_del(&handle->vfh, file);
v4l2_fh_exit(&handle->vfh);
kfree(handle);
diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c
index fb77b0b21790..2f9700b3f1b6 100644
--- a/drivers/usb/gadget/function/uvc_video.c
+++ b/drivers/usb/gadget/function/uvc_video.c
@@ -499,13 +499,11 @@ uvc_video_prep_requests(struct uvc_video *video)
{
struct uvc_device *uvc = container_of(video, struct uvc_device, video);
struct usb_composite_dev *cdev = uvc->func.config->cdev;
- unsigned int interval_duration = video->ep->desc->bInterval * 1250;
+ unsigned int interval_duration;
unsigned int max_req_size, req_size, header_size;
unsigned int nreq;
- max_req_size = video->ep->maxpacket
- * max_t(unsigned int, video->ep->maxburst, 1)
- * (video->ep->mult);
+ max_req_size = video->max_req_size;
if (!usb_endpoint_xfer_isoc(video->ep->desc)) {
video->req_size = max_req_size;
@@ -515,8 +513,11 @@ uvc_video_prep_requests(struct uvc_video *video)
return;
}
+ interval_duration = 1 << (video->ep->desc->bInterval - 1);
if (cdev->gadget->speed < USB_SPEED_HIGH)
- interval_duration = video->ep->desc->bInterval * 10000;
+ interval_duration *= 10000;
+ else
+ interval_duration *= 1250;
nreq = DIV_ROUND_UP(video->interval, interval_duration);
@@ -558,7 +559,7 @@ uvc_video_alloc_requests(struct uvc_video *video)
uvc_video_prep_requests(video);
for (i = 0; i < video->uvc_num_requests; i++) {
- ureq = kzalloc(sizeof(struct uvc_request), GFP_KERNEL);
+ ureq = kzalloc_obj(struct uvc_request);
if (ureq == NULL)
goto error;
@@ -837,7 +838,6 @@ int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc)
video->interval = 666666;
/* Initialize the video buffers queue. */
- uvcg_queue_init(&video->queue, uvc->v4l2_dev.dev->parent,
+ return uvcg_queue_init(&video->queue, uvc->v4l2_dev.dev->parent,
V4L2_BUF_TYPE_VIDEO_OUTPUT, &video->mutex);
- return 0;
}
diff --git a/drivers/usb/gadget/legacy/dbgp.c b/drivers/usb/gadget/legacy/dbgp.c
index d70fb5bc2357..03032ea6f701 100644
--- a/drivers/usb/gadget/legacy/dbgp.c
+++ b/drivers/usb/gadget/legacy/dbgp.c
@@ -298,7 +298,7 @@ static int dbgp_bind(struct usb_gadget *gadget,
dbgp.req->length = DBGP_REQ_EP0_LEN;
#ifdef CONFIG_USB_G_DBGP_SERIAL
- dbgp.serial = kzalloc(sizeof(struct gserial), GFP_KERNEL);
+ dbgp.serial = kzalloc_obj(struct gserial);
if (!dbgp.serial) {
stp = 3;
err = -ENOMEM;
diff --git a/drivers/usb/gadget/legacy/g_ffs.c b/drivers/usb/gadget/legacy/g_ffs.c
index a9544fea8723..dc59e625aca8 100644
--- a/drivers/usb/gadget/legacy/g_ffs.c
+++ b/drivers/usb/gadget/legacy/g_ffs.c
@@ -188,7 +188,7 @@ static int __init gfs_init(void)
/*
* Allocate in one chunk for easier maintenance
*/
- f_ffs[0] = kcalloc(func_num * N_CONF, sizeof(*f_ffs), GFP_KERNEL);
+ f_ffs[0] = kzalloc_objs(*f_ffs[0], func_num * N_CONF);
if (!f_ffs[0]) {
ret = -ENOMEM;
goto no_func;
@@ -196,7 +196,7 @@ static int __init gfs_init(void)
for (i = 1; i < N_CONF; ++i)
f_ffs[i] = f_ffs[0] + i * func_num;
- fi_ffs = kcalloc(func_num, sizeof(*fi_ffs), GFP_KERNEL);
+ fi_ffs = kzalloc_objs(*fi_ffs, func_num);
if (!fi_ffs) {
ret = -ENOMEM;
goto no_func;
diff --git a/drivers/usb/gadget/legacy/hid.c b/drivers/usb/gadget/legacy/hid.c
index 3684546e8801..89722413e5fd 100644
--- a/drivers/usb/gadget/legacy/hid.c
+++ b/drivers/usb/gadget/legacy/hid.c
@@ -227,7 +227,7 @@ static int hidg_plat_driver_probe(struct platform_device *pdev)
return -ENODEV;
}
- entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ entry = kzalloc_obj(*entry);
if (!entry)
return -ENOMEM;
diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c
index b6a30d88a800..d87a8ab51510 100644
--- a/drivers/usb/gadget/legacy/inode.c
+++ b/drivers/usb/gadget/legacy/inode.c
@@ -150,7 +150,6 @@ struct dev_data {
void *buf;
wait_queue_head_t wait;
struct super_block *sb;
- struct dentry *dentry;
/* except this scratch i/o buffer for ep0 */
u8 rbuf[RBUF_SIZE];
@@ -174,7 +173,7 @@ static struct dev_data *dev_new (void)
{
struct dev_data *dev;
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ dev = kzalloc_obj(*dev);
if (!dev)
return NULL;
dev->state = STATE_DEV_DISABLED;
@@ -208,7 +207,6 @@ struct ep_data {
struct usb_endpoint_descriptor desc, hs_desc;
struct list_head epfiles;
wait_queue_head_t wait;
- struct dentry *dentry;
};
static inline void get_ep (struct ep_data *data)
@@ -616,7 +614,7 @@ ep_read_iter(struct kiocb *iocb, struct iov_iter *to)
if (value >= 0 && (copy_to_iter(buf, value, to) != value))
value = -EFAULT;
} else {
- struct kiocb_priv *priv = kzalloc(sizeof *priv, GFP_KERNEL);
+ struct kiocb_priv *priv = kzalloc_obj(*priv);
value = -ENOMEM;
if (!priv)
goto fail;
@@ -684,7 +682,7 @@ ep_write_iter(struct kiocb *iocb, struct iov_iter *from)
} else if (is_sync_kiocb(iocb)) {
value = ep_io(epdata, buf, len);
} else {
- struct kiocb_priv *priv = kzalloc(sizeof *priv, GFP_KERNEL);
+ struct kiocb_priv *priv = kzalloc_obj(*priv);
value = -ENOMEM;
if (priv) {
value = ep_aio(iocb, priv, epdata, buf, len);
@@ -1561,18 +1559,12 @@ static void destroy_ep_files (struct dev_data *dev)
spin_lock_irq (&dev->lock);
while (!list_empty(&dev->epfiles)) {
struct ep_data *ep;
- struct inode *parent;
- struct dentry *dentry;
/* break link to FS */
ep = list_first_entry (&dev->epfiles, struct ep_data, epfiles);
list_del_init (&ep->epfiles);
spin_unlock_irq (&dev->lock);
- dentry = ep->dentry;
- ep->dentry = NULL;
- parent = d_inode(dentry->d_parent);
-
/* break link to controller */
mutex_lock(&ep->lock);
if (ep->state == STATE_EP_ENABLED)
@@ -1583,13 +1575,11 @@ static void destroy_ep_files (struct dev_data *dev)
mutex_unlock(&ep->lock);
wake_up (&ep->wait);
- put_ep (ep);
/* break link to dcache */
- inode_lock(parent);
- d_delete (dentry);
- dput (dentry);
- inode_unlock(parent);
+ simple_remove_by_name(dev->sb->s_root, ep->name, NULL);
+
+ put_ep (ep);
spin_lock_irq (&dev->lock);
}
@@ -1597,25 +1587,25 @@ static void destroy_ep_files (struct dev_data *dev)
}
-static struct dentry *
-gadgetfs_create_file (struct super_block *sb, char const *name,
+static int gadgetfs_create_file (struct super_block *sb, char const *name,
void *data, const struct file_operations *fops);
static int activate_ep_files (struct dev_data *dev)
{
struct usb_ep *ep;
struct ep_data *data;
+ int err;
gadget_for_each_ep (ep, dev->gadget) {
- data = kzalloc(sizeof(*data), GFP_KERNEL);
+ data = kzalloc_obj(*data);
if (!data)
goto enomem0;
data->state = STATE_EP_DISABLED;
mutex_init(&data->lock);
init_waitqueue_head (&data->wait);
- strncpy (data->name, ep->name, sizeof (data->name) - 1);
+ strscpy(data->name, ep->name);
refcount_set (&data->count, 1);
data->dev = dev;
get_dev (dev);
@@ -1627,9 +1617,9 @@ static int activate_ep_files (struct dev_data *dev)
if (!data->req)
goto enomem1;
- data->dentry = gadgetfs_create_file (dev->sb, data->name,
+ err = gadgetfs_create_file (dev->sb, data->name,
data, &ep_io_operations);
- if (!data->dentry)
+ if (err)
goto enomem2;
list_add_tail (&data->epfiles, &dev->epfiles);
}
@@ -1993,30 +1983,32 @@ gadgetfs_make_inode (struct super_block *sb,
/* creates in fs root directory, so non-renamable and non-linkable.
* so inode and dentry are paired, until device reconfig.
*/
-static struct dentry *
-gadgetfs_create_file (struct super_block *sb, char const *name,
+static int gadgetfs_create_file (struct super_block *sb, char const *name,
void *data, const struct file_operations *fops)
{
struct dentry *dentry;
struct inode *inode;
- dentry = d_alloc_name(sb->s_root, name);
- if (!dentry)
- return NULL;
-
inode = gadgetfs_make_inode (sb, data, fops,
S_IFREG | (default_perm & S_IRWXUGO));
- if (!inode) {
- dput(dentry);
- return NULL;
+ if (!inode)
+ return -ENOMEM;
+
+ dentry = simple_start_creating(sb->s_root, name);
+ if (IS_ERR(dentry)) {
+ iput(inode);
+ return PTR_ERR(dentry);
}
- d_add (dentry, inode);
- return dentry;
+
+ d_make_persistent(dentry, inode);
+
+ simple_done_creating(dentry);
+ return 0;
}
static const struct super_operations gadget_fs_operations = {
.statfs = simple_statfs,
- .drop_inode = generic_delete_inode,
+ .drop_inode = inode_just_drop,
};
static int
@@ -2064,8 +2056,8 @@ gadgetfs_fill_super (struct super_block *sb, struct fs_context *fc)
goto Enomem;
dev->sb = sb;
- dev->dentry = gadgetfs_create_file(sb, CHIP, dev, &ep0_operations);
- if (!dev->dentry) {
+ rc = gadgetfs_create_file(sb, CHIP, dev, &ep0_operations);
+ if (rc) {
put_dev(dev);
goto Enomem;
}
@@ -2107,7 +2099,7 @@ static void
gadgetfs_kill_sb (struct super_block *sb)
{
mutex_lock(&sb_mutex);
- kill_litter_super (sb);
+ kill_anon_super (sb);
if (the_device) {
put_dev (the_device);
the_device = NULL;
diff --git a/drivers/usb/gadget/legacy/raw_gadget.c b/drivers/usb/gadget/legacy/raw_gadget.c
index 20165e1582d9..4febf8dac7ca 100644
--- a/drivers/usb/gadget/legacy/raw_gadget.c
+++ b/drivers/usb/gadget/legacy/raw_gadget.c
@@ -40,6 +40,7 @@ MODULE_LICENSE("GPL");
static DEFINE_IDA(driver_id_numbers);
#define DRIVER_DRIVER_NAME_LENGTH_MAX 32
+#define USB_RAW_IO_LENGTH_MAX KMALLOC_MAX_SIZE
#define RAW_EVENT_QUEUE_SIZE 16
@@ -189,7 +190,7 @@ static struct raw_dev *dev_new(void)
{
struct raw_dev *dev;
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ dev = kzalloc_obj(*dev);
if (!dev)
return NULL;
/* Matches kref_put() in raw_release(). */
@@ -667,7 +668,7 @@ static void *raw_alloc_io_data(struct usb_raw_ep_io *io, void __user *ptr,
return ERR_PTR(-EINVAL);
if (!usb_raw_io_flags_valid(io->flags))
return ERR_PTR(-EINVAL);
- if (io->length > PAGE_SIZE)
+ if (io->length > USB_RAW_IO_LENGTH_MAX)
return ERR_PTR(-EINVAL);
if (get_from_user)
data = memdup_user(ptr + sizeof(*io), io->length);
@@ -1250,7 +1251,7 @@ static int raw_ioctl_eps_info(struct raw_dev *dev, unsigned long value)
struct usb_raw_eps_info *info;
struct raw_ep *ep;
- info = kzalloc(sizeof(*info), GFP_KERNEL);
+ info = kzalloc_obj(*info);
if (!info) {
ret = -ENOMEM;
goto out;
diff --git a/drivers/usb/gadget/legacy/zero.c b/drivers/usb/gadget/legacy/zero.c
index e25e0d8dd387..08a21bd0c2ba 100644
--- a/drivers/usb/gadget/legacy/zero.c
+++ b/drivers/usb/gadget/legacy/zero.c
@@ -147,6 +147,12 @@ static struct usb_gadget_strings *dev_strings[] = {
NULL,
};
+static struct usb_function *func_lb;
+static struct usb_function_instance *func_inst_lb;
+
+static struct usb_function *func_ss;
+static struct usb_function_instance *func_inst_ss;
+
/*-------------------------------------------------------------------------*/
static struct timer_list autoresume_timer;
@@ -156,6 +162,7 @@ static void zero_autoresume(struct timer_list *unused)
{
struct usb_composite_dev *cdev = autoresume_cdev;
struct usb_gadget *g = cdev->gadget;
+ int status;
/* unconfigured devices can't issue wakeups */
if (!cdev->config)
@@ -165,10 +172,18 @@ static void zero_autoresume(struct timer_list *unused)
* more significant than just a timer firing; likely
* because of some direct user request.
*/
- if (g->speed != USB_SPEED_UNKNOWN) {
- int status = usb_gadget_wakeup(g);
- INFO(cdev, "%s --> %d\n", __func__, status);
+ if (g->speed == USB_SPEED_UNKNOWN)
+ return;
+
+ if (g->speed >= USB_SPEED_SUPER) {
+ if (loopdefault)
+ status = usb_func_wakeup(func_lb);
+ else
+ status = usb_func_wakeup(func_ss);
+ } else {
+ status = usb_gadget_wakeup(g);
}
+ INFO(cdev, "%s --> %d\n", __func__, status);
}
static void zero_suspend(struct usb_composite_dev *cdev)
@@ -194,7 +209,7 @@ static void zero_suspend(struct usb_composite_dev *cdev)
static void zero_resume(struct usb_composite_dev *cdev)
{
DBG(cdev, "%s\n", __func__);
- del_timer(&autoresume_timer);
+ timer_delete(&autoresume_timer);
}
/*-------------------------------------------------------------------------*/
@@ -206,9 +221,6 @@ static struct usb_configuration loopback_driver = {
/* .iConfiguration = DYNAMIC */
};
-static struct usb_function *func_ss;
-static struct usb_function_instance *func_inst_ss;
-
static int ss_config_setup(struct usb_configuration *c,
const struct usb_ctrlrequest *ctrl)
{
@@ -248,9 +260,6 @@ module_param_named(isoc_maxburst, gzero_options.isoc_maxburst, uint,
S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(isoc_maxburst, "0 - 15 (ss only)");
-static struct usb_function *func_lb;
-static struct usb_function_instance *func_inst_lb;
-
module_param_named(qlen, gzero_options.qlen, uint, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(qlen, "depth of loopback queue");
@@ -398,7 +407,7 @@ err_put_func_inst_ss:
static int zero_unbind(struct usb_composite_dev *cdev)
{
- del_timer_sync(&autoresume_timer);
+ timer_delete_sync(&autoresume_timer);
if (!IS_ERR_OR_NULL(func_ss))
usb_put_function(func_ss);
usb_put_function_instance(func_inst_ss);
diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig
index aae1787320d4..26460340fbc9 100644
--- a/drivers/usb/gadget/udc/Kconfig
+++ b/drivers/usb/gadget/udc/Kconfig
@@ -102,12 +102,6 @@ config USB_FSL_USB2
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_GR_UDC
tristate "Aeroflex Gaisler GRUSBDC USB Peripheral Controller Driver"
depends on HAS_DMA
@@ -228,21 +222,6 @@ config USB_PXA27X
dynamically linked module called "pxa27x_udc" and force all
gadget drivers to also be dynamically linked.
-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.
-
config USB_SNP_CORE
depends on (USB_AMD5536UDC || USB_SNP_UDC_PLAT)
depends on HAS_DMA
@@ -326,29 +305,6 @@ config USB_FSL_QE
Set CONFIG_USB_GADGET to "m" to build this driver as a
dynamically linked module called "fsl_qe_udc".
-config USB_NET2272
- depends on HAS_IOMEM
- 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
- bool "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 NET228x / PLX USB3x8x"
depends on USB_PCI
diff --git a/drivers/usb/gadget/udc/Makefile b/drivers/usb/gadget/udc/Makefile
index b52f93e9c61d..1b9b1a4f9c57 100644
--- a/drivers/usb/gadget/udc/Makefile
+++ b/drivers/usb/gadget/udc/Makefile
@@ -9,7 +9,6 @@ udc-core-y := core.o trace.o
#
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_SNP_CORE) += snps_udc_core.o
obj-$(CONFIG_USB_AMD5536UDC) += amd5536udc_pci.o
@@ -31,10 +30,6 @@ obj-$(CONFIG_USB_RENESAS_USBF) += renesas_usbf.o
obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.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_MV_U3D) += mv_u3d_core.o
obj-$(CONFIG_USB_GR_UDC) += gr_udc.o
obj-$(CONFIG_USB_GADGET_XILINX) += udc-xilinx.o
obj-$(CONFIG_USB_SNP_UDC_PLAT) += snps_udc_plat.o
diff --git a/drivers/usb/gadget/udc/amd5536udc_pci.c b/drivers/usb/gadget/udc/amd5536udc_pci.c
index a36913ae31f9..cd767b1e3fe7 100644
--- a/drivers/usb/gadget/udc/amd5536udc_pci.c
+++ b/drivers/usb/gadget/udc/amd5536udc_pci.c
@@ -95,7 +95,7 @@ static int udc_pci_probe(
}
/* init */
- dev = kzalloc(sizeof(struct udc), GFP_KERNEL);
+ dev = kzalloc_obj(struct udc);
if (!dev)
return -ENOMEM;
diff --git a/drivers/usb/gadget/udc/aspeed-vhub/core.c b/drivers/usb/gadget/udc/aspeed-vhub/core.c
index f2685f89b3e5..4a8b9ff8368f 100644
--- a/drivers/usb/gadget/udc/aspeed-vhub/core.c
+++ b/drivers/usb/gadget/udc/aspeed-vhub/core.c
@@ -23,6 +23,7 @@
#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/dma-mapping.h>
+#include <linux/reset.h>
#include "vhub.h"
@@ -77,7 +78,7 @@ struct usb_request *ast_vhub_alloc_request(struct usb_ep *u_ep,
{
struct ast_vhub_req *req;
- req = kzalloc(sizeof(*req), gfp_flags);
+ req = kzalloc_obj(*req, gfp_flags);
if (!req)
return NULL;
return &req->req;
@@ -280,6 +281,8 @@ static void ast_vhub_remove(struct platform_device *pdev)
if (vhub->clk)
clk_disable_unprepare(vhub->clk);
+ reset_control_assert(vhub->rst);
+
spin_unlock_irqrestore(&vhub->lock, flags);
if (vhub->ep0_bufs)
@@ -294,6 +297,7 @@ static void ast_vhub_remove(struct platform_device *pdev)
static int ast_vhub_probe(struct platform_device *pdev)
{
enum usb_device_speed max_speed;
+ const u64 *dma_mask_ptr;
struct ast_vhub *vhub;
struct resource *res;
int i, rc = 0;
@@ -348,6 +352,16 @@ static int ast_vhub_probe(struct platform_device *pdev)
goto err;
}
+ vhub->rst = devm_reset_control_get_optional_shared(&pdev->dev, NULL);
+ if (IS_ERR(vhub->rst)) {
+ rc = PTR_ERR(vhub->rst);
+ goto err;
+ }
+
+ rc = reset_control_deassert(vhub->rst);
+ if (rc)
+ goto err;
+
/* Check if we need to limit the HW to USB1 */
max_speed = usb_get_maximum_speed(&pdev->dev);
if (max_speed != USB_SPEED_UNKNOWN && max_speed < USB_SPEED_HIGH)
@@ -370,6 +384,12 @@ static int ast_vhub_probe(struct platform_device *pdev)
goto err;
}
+ dma_mask_ptr = (u64 *)of_device_get_match_data(&pdev->dev);
+ if (dma_mask_ptr) {
+ rc = dma_coerce_mask_and_coherent(&pdev->dev, *dma_mask_ptr);
+ if (rc)
+ goto err;
+ }
/*
* Allocate DMA buffers for all EP0s in one chunk,
* one per port and one for the vHub itself
@@ -412,15 +432,25 @@ static int ast_vhub_probe(struct platform_device *pdev)
return rc;
}
+static const u64 dma_mask_32 = DMA_BIT_MASK(32);
+static const u64 dma_mask_64 = DMA_BIT_MASK(64);
+
static const struct of_device_id ast_vhub_dt_ids[] = {
{
.compatible = "aspeed,ast2400-usb-vhub",
+ .data = &dma_mask_32,
},
{
.compatible = "aspeed,ast2500-usb-vhub",
+ .data = &dma_mask_32,
},
{
.compatible = "aspeed,ast2600-usb-vhub",
+ .data = &dma_mask_32,
+ },
+ {
+ .compatible = "aspeed,ast2700-usb-vhub",
+ .data = &dma_mask_64,
},
{ }
};
diff --git a/drivers/usb/gadget/udc/aspeed-vhub/dev.c b/drivers/usb/gadget/udc/aspeed-vhub/dev.c
index 573109ca5b79..2ecd049dacc2 100644
--- a/drivers/usb/gadget/udc/aspeed-vhub/dev.c
+++ b/drivers/usb/gadget/udc/aspeed-vhub/dev.c
@@ -548,6 +548,9 @@ int ast_vhub_init_dev(struct ast_vhub *vhub, unsigned int idx)
d->vhub = vhub;
d->index = idx;
d->name = devm_kasprintf(parent, GFP_KERNEL, "port%d", idx+1);
+ if (!d->name)
+ return -ENOMEM;
+
d->regs = vhub->regs + 0x100 + 0x10 * idx;
ast_vhub_init_ep0(vhub, &d->ep0, d);
@@ -557,7 +560,7 @@ int ast_vhub_init_dev(struct ast_vhub *vhub, unsigned int idx)
* endpoint 0.
*/
d->max_epns = min_t(u32, vhub->max_epns, 30);
- d->epns = kcalloc(d->max_epns, sizeof(*d->epns), GFP_KERNEL);
+ d->epns = kzalloc_objs(*d->epns, d->max_epns);
if (!d->epns)
return -ENOMEM;
@@ -566,7 +569,7 @@ int ast_vhub_init_dev(struct ast_vhub *vhub, unsigned int idx)
* named "parent" devices for each port so we create a sub device
* here for that purpose
*/
- d->port_dev = kzalloc(sizeof(struct device), GFP_KERNEL);
+ d->port_dev = kzalloc_obj(struct device);
if (!d->port_dev) {
rc = -ENOMEM;
goto fail_alloc;
diff --git a/drivers/usb/gadget/udc/aspeed-vhub/vhub.h b/drivers/usb/gadget/udc/aspeed-vhub/vhub.h
index 6b9dfa6e10eb..aca2050e2db0 100644
--- a/drivers/usb/gadget/udc/aspeed-vhub/vhub.h
+++ b/drivers/usb/gadget/udc/aspeed-vhub/vhub.h
@@ -388,6 +388,7 @@ struct ast_vhub {
spinlock_t lock;
struct work_struct wake_work;
struct clk *clk;
+ struct reset_control *rst;
/* EP0 DMA buffers allocated in one chunk */
void *ep0_bufs;
diff --git a/drivers/usb/gadget/udc/aspeed_udc.c b/drivers/usb/gadget/udc/aspeed_udc.c
index 353bfb1ff0a1..7fc6696b7694 100644
--- a/drivers/usb/gadget/udc/aspeed_udc.c
+++ b/drivers/usb/gadget/udc/aspeed_udc.c
@@ -452,7 +452,7 @@ static struct usb_request *ast_udc_ep_alloc_request(struct usb_ep *_ep,
struct ast_udc_ep *ep = to_ast_ep(_ep);
struct ast_udc_request *req;
- req = kzalloc(sizeof(struct ast_udc_request), gfp_flags);
+ req = kzalloc_obj(struct ast_udc_request, gfp_flags);
if (!req) {
EP_DBG(ep, "request allocation failed\n");
return NULL;
diff --git a/drivers/usb/gadget/udc/at91_udc.c b/drivers/usb/gadget/udc/at91_udc.c
index aa4c61094dc6..5aa360ba4f03 100644
--- a/drivers/usb/gadget/udc/at91_udc.c
+++ b/drivers/usb/gadget/udc/at91_udc.c
@@ -585,7 +585,7 @@ at91_ep_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
{
struct at91_request *req;
- req = kzalloc(sizeof (struct at91_request), gfp_flags);
+ req = kzalloc_obj(struct at91_request, gfp_flags);
if (!req)
return NULL;
@@ -1541,7 +1541,7 @@ static void at91_vbus_timer_work(struct work_struct *work)
static void at91_vbus_timer(struct timer_list *t)
{
- struct at91_udc *udc = from_timer(udc, t, vbus_timer);
+ struct at91_udc *udc = timer_container_of(udc, t, vbus_timer);
/*
* If we are polling vbus it is likely that the gpio is on an
diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c
index 0c6f2ad81d37..72a2f95ff018 100644
--- a/drivers/usb/gadget/udc/atmel_usba_udc.c
+++ b/drivers/usb/gadget/udc/atmel_usba_udc.c
@@ -40,7 +40,7 @@ static int queue_dbg_open(struct inode *inode, struct file *file)
struct usba_request *req, *req_copy;
struct list_head *queue_data;
- queue_data = kmalloc(sizeof(*queue_data), GFP_KERNEL);
+ queue_data = kmalloc_obj(*queue_data);
if (!queue_data)
return -ENOMEM;
INIT_LIST_HEAD(queue_data);
@@ -702,7 +702,7 @@ usba_ep_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
DBG(DBG_GADGET, "ep_alloc_request: %p, 0x%x\n", _ep, gfp_flags);
- req = kzalloc(sizeof(*req), gfp_flags);
+ req = kzalloc_obj(*req, gfp_flags);
if (!req)
return NULL;
diff --git a/drivers/usb/gadget/udc/bcm63xx_udc.c b/drivers/usb/gadget/udc/bcm63xx_udc.c
index 502612a5650e..c4f9ea45bdbb 100644
--- a/drivers/usb/gadget/udc/bcm63xx_udc.c
+++ b/drivers/usb/gadget/udc/bcm63xx_udc.c
@@ -1115,7 +1115,7 @@ static struct usb_request *bcm63xx_udc_alloc_request(struct usb_ep *ep,
{
struct bcm63xx_req *breq;
- breq = kzalloc(sizeof(*breq), mem_flags);
+ breq = kzalloc_obj(*breq, mem_flags);
if (!breq)
return NULL;
return &breq->req;
diff --git a/drivers/usb/gadget/udc/bdc/bdc_core.c b/drivers/usb/gadget/udc/bdc/bdc_core.c
index 5c3d8b64c0e7..438201dc96ca 100644
--- a/drivers/usb/gadget/udc/bdc/bdc_core.c
+++ b/drivers/usb/gadget/udc/bdc/bdc_core.c
@@ -35,8 +35,8 @@ static int poll_oip(struct bdc *bdc, u32 usec)
u32 status;
int ret;
- ret = readl_poll_timeout(bdc->regs + BDC_BDCSC, status,
- (BDC_CSTS(status) != BDC_OIP), 10, usec);
+ ret = readl_poll_timeout_atomic(bdc->regs + BDC_BDCSC, status,
+ (BDC_CSTS(status) != BDC_OIP), 10, usec);
if (ret)
dev_err(bdc->dev, "operation timedout BDCSC: 0x%08x\n", status);
else
@@ -397,8 +397,7 @@ static int bdc_mem_alloc(struct bdc *bdc)
"ieps:%d eops:%d num_eps:%d\n",
num_ieps, num_oeps, bdc->num_eps);
/* allocate array of ep pointers */
- bdc->bdc_ep_array = kcalloc(bdc->num_eps, sizeof(struct bdc_ep *),
- GFP_KERNEL);
+ bdc->bdc_ep_array = kzalloc_objs(struct bdc_ep *, bdc->num_eps);
if (!bdc->bdc_ep_array)
goto fail;
diff --git a/drivers/usb/gadget/udc/bdc/bdc_ep.c b/drivers/usb/gadget/udc/bdc/bdc_ep.c
index f995cfa9b99e..a7a22e5ec47b 100644
--- a/drivers/usb/gadget/udc/bdc/bdc_ep.c
+++ b/drivers/usb/gadget/udc/bdc/bdc_ep.c
@@ -138,16 +138,15 @@ static int ep_bd_list_alloc(struct bdc_ep *ep)
__func__, ep, num_tabs);
/* Allocate memory for table array */
- ep->bd_list.bd_table_array = kcalloc(num_tabs,
- sizeof(struct bd_table *),
- GFP_ATOMIC);
+ ep->bd_list.bd_table_array = kzalloc_objs(struct bd_table *, num_tabs,
+ GFP_ATOMIC);
if (!ep->bd_list.bd_table_array)
return -ENOMEM;
/* Allocate memory for each table */
for (index = 0; index < num_tabs; index++) {
/* Allocate memory for bd_table structure */
- bd_table = kzalloc(sizeof(*bd_table), GFP_ATOMIC);
+ bd_table = kzalloc_obj(*bd_table, GFP_ATOMIC);
if (!bd_table)
goto fail;
@@ -1648,6 +1647,10 @@ void bdc_sr_xsf(struct bdc *bdc, struct bdc_sr *sreport)
u8 ep_num;
ep_num = (le32_to_cpu(sreport->offset[3])>>4) & 0x1f;
+ if (ep_num >= bdc->num_eps) {
+ dev_err(bdc->dev, "xsf for invalid ep %u\n", ep_num);
+ return;
+ }
ep = bdc->bdc_ep_array[ep_num];
if (!ep || !(ep->flags & BDC_EP_ENABLED)) {
dev_err(bdc->dev, "xsf for ep not enabled\n");
@@ -1829,7 +1832,7 @@ static struct usb_request *bdc_gadget_alloc_request(struct usb_ep *_ep,
struct bdc_req *req;
struct bdc_ep *ep;
- req = kzalloc(sizeof(*req), gfp_flags);
+ req = kzalloc_obj(*req, gfp_flags);
if (!req)
return NULL;
@@ -1946,7 +1949,7 @@ static int init_ep(struct bdc *bdc, u32 epnum, u32 dir)
struct bdc_ep *ep;
dev_dbg(bdc->dev, "%s epnum=%d dir=%d\n", __func__, epnum, dir);
- ep = kzalloc(sizeof(*ep), GFP_KERNEL);
+ ep = kzalloc_obj(*ep);
if (!ep)
return -ENOMEM;
diff --git a/drivers/usb/gadget/udc/cdns2/cdns2-gadget.c b/drivers/usb/gadget/udc/cdns2/cdns2-gadget.c
index 7e69944ef18a..308d3c468ab1 100644
--- a/drivers/usb/gadget/udc/cdns2/cdns2-gadget.c
+++ b/drivers/usb/gadget/udc/cdns2/cdns2-gadget.c
@@ -1505,7 +1505,7 @@ struct usb_request *cdns2_gadget_ep_alloc_request(struct usb_ep *ep,
struct cdns2_endpoint *pep = ep_to_cdns2_ep(ep);
struct cdns2_request *preq;
- preq = kzalloc(sizeof(*preq), gfp_flags);
+ preq = kzalloc_obj(*preq, gfp_flags);
if (!preq)
return NULL;
@@ -2415,7 +2415,6 @@ int cdns2_gadget_resume(struct cdns2_device *pdev, bool hibernated)
void cdns2_gadget_remove(struct cdns2_device *pdev)
{
- pm_runtime_mark_last_busy(pdev->dev);
pm_runtime_put_autosuspend(pdev->dev);
usb_del_gadget(&pdev->gadget);
diff --git a/drivers/usb/gadget/udc/cdns2/cdns2-trace.h b/drivers/usb/gadget/udc/cdns2/cdns2-trace.h
index ade1752956b1..f4df0e2ff853 100644
--- a/drivers/usb/gadget/udc/cdns2/cdns2-trace.h
+++ b/drivers/usb/gadget/udc/cdns2/cdns2-trace.h
@@ -47,16 +47,6 @@ DEFINE_EVENT(cdns2_log_enable_disable, cdns2_pullup,
TP_ARGS(set)
);
-DEFINE_EVENT(cdns2_log_enable_disable, cdns2_lpm,
- TP_PROTO(int set),
- TP_ARGS(set)
-);
-
-DEFINE_EVENT(cdns2_log_enable_disable, cdns2_may_wakeup,
- TP_PROTO(int set),
- TP_ARGS(set)
-);
-
DECLARE_EVENT_CLASS(cdns2_log_simple,
TP_PROTO(char *msg),
TP_ARGS(msg),
@@ -79,11 +69,6 @@ DEFINE_EVENT(cdns2_log_simple, cdns2_ep0_status_stage,
TP_ARGS(msg)
);
-DEFINE_EVENT(cdns2_log_simple, cdns2_ep0_set_config,
- TP_PROTO(char *msg),
- TP_ARGS(msg)
-);
-
DEFINE_EVENT(cdns2_log_simple, cdns2_ep0_setup,
TP_PROTO(char *msg),
TP_ARGS(msg)
@@ -340,11 +325,6 @@ DEFINE_EVENT(cdns2_log_request, cdns2_free_request,
TP_ARGS(preq)
);
-DEFINE_EVENT(cdns2_log_request, cdns2_ep_queue,
- TP_PROTO(struct cdns2_request *preq),
- TP_ARGS(preq)
-);
-
DEFINE_EVENT(cdns2_log_request, cdns2_request_dequeue,
TP_PROTO(struct cdns2_request *preq),
TP_ARGS(preq)
@@ -355,50 +335,6 @@ DEFINE_EVENT(cdns2_log_request, cdns2_request_giveback,
TP_ARGS(preq)
);
-TRACE_EVENT(cdns2_ep0_enqueue,
- TP_PROTO(struct cdns2_device *dev_priv, struct usb_request *request),
- TP_ARGS(dev_priv, request),
- TP_STRUCT__entry(
- __field(int, dir)
- __field(int, length)
- ),
- TP_fast_assign(
- __entry->dir = dev_priv->eps[0].dir;
- __entry->length = request->length;
- ),
- TP_printk("Queue to ep0%s length: %u", __entry->dir ? "in" : "out",
- __entry->length)
-);
-
-DECLARE_EVENT_CLASS(cdns2_log_map_request,
- TP_PROTO(struct cdns2_request *priv_req),
- TP_ARGS(priv_req),
- TP_STRUCT__entry(
- __string(name, priv_req->pep->name)
- __field(struct usb_request *, req)
- __field(void *, buf)
- __field(dma_addr_t, dma)
- ),
- TP_fast_assign(
- __assign_str(name);
- __entry->req = &priv_req->request;
- __entry->buf = priv_req->request.buf;
- __entry->dma = priv_req->request.dma;
- ),
- TP_printk("%s: req: %p, req buf %p, dma %p",
- __get_str(name), __entry->req, __entry->buf, &__entry->dma
- )
-);
-
-DEFINE_EVENT(cdns2_log_map_request, cdns2_map_request,
- TP_PROTO(struct cdns2_request *req),
- TP_ARGS(req)
-);
-DEFINE_EVENT(cdns2_log_map_request, cdns2_mapped_request,
- TP_PROTO(struct cdns2_request *req),
- TP_ARGS(req)
-);
-
DECLARE_EVENT_CLASS(cdns2_log_trb,
TP_PROTO(struct cdns2_endpoint *pep, struct cdns2_trb *trb),
TP_ARGS(pep, trb),
@@ -507,11 +443,6 @@ DEFINE_EVENT(cdns2_log_ep, cdns2_gadget_ep_disable,
TP_ARGS(pep)
);
-DEFINE_EVENT(cdns2_log_ep, cdns2_iso_out_ep_disable,
- TP_PROTO(struct cdns2_endpoint *pep),
- TP_ARGS(pep)
-);
-
DEFINE_EVENT(cdns2_log_ep, cdns2_ep_busy_try_halt_again,
TP_PROTO(struct cdns2_endpoint *pep),
TP_ARGS(pep)
diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c
index 4b3d5075621a..e8861eaad907 100644
--- a/drivers/usb/gadget/udc/core.c
+++ b/drivers/usb/gadget/udc/core.c
@@ -194,6 +194,9 @@ struct usb_request *usb_ep_alloc_request(struct usb_ep *ep,
req = ep->ops->alloc_request(ep, gfp_flags);
+ if (req)
+ req->ep = ep;
+
trace_usb_ep_alloc_request(ep, req, req ? 0 : -ENOMEM);
return req;
@@ -1123,8 +1126,14 @@ static void usb_gadget_state_work(struct work_struct *work)
void usb_gadget_set_state(struct usb_gadget *gadget,
enum usb_device_state state)
{
+ unsigned long flags;
+
+ spin_lock_irqsave(&gadget->state_lock, flags);
gadget->state = state;
- schedule_work(&gadget->work);
+ if (!gadget->teardown)
+ schedule_work(&gadget->work);
+ spin_unlock_irqrestore(&gadget->state_lock, flags);
+ trace_usb_gadget_set_state(gadget, 0);
}
EXPORT_SYMBOL_GPL(usb_gadget_set_state);
@@ -1257,8 +1266,9 @@ static inline void usb_gadget_udc_stop_locked(struct usb_udc *udc)
* @speed: The maximum speed to allowed to run
*
* This call is issued by the UDC Class driver before calling
- * usb_gadget_udc_start() in order to make sure that we don't try to
- * connect on speeds the gadget driver doesn't support.
+ * usb_gadget_udc_start_locked() in order to make sure that
+ * we don't try to connect on speeds the gadget driver
+ * doesn't support.
*/
static inline void usb_gadget_udc_set_speed(struct usb_udc *udc,
enum usb_device_speed speed)
@@ -1357,6 +1367,8 @@ static void usb_udc_nop_release(struct device *dev)
void usb_initialize_gadget(struct device *parent, struct usb_gadget *gadget,
void (*release)(struct device *dev))
{
+ spin_lock_init(&gadget->state_lock);
+ gadget->teardown = false;
INIT_WORK(&gadget->work, usb_gadget_state_work);
gadget->dev.parent = parent;
@@ -1382,7 +1394,7 @@ int usb_add_gadget(struct usb_gadget *gadget)
struct usb_udc *udc;
int ret = -ENOMEM;
- udc = kzalloc(sizeof(*udc), GFP_KERNEL);
+ udc = kzalloc_obj(*udc);
if (!udc)
goto error;
@@ -1531,6 +1543,7 @@ EXPORT_SYMBOL_GPL(usb_add_gadget_udc);
void usb_del_gadget(struct usb_gadget *gadget)
{
struct usb_udc *udc = gadget->udc;
+ unsigned long flags;
if (!udc)
return;
@@ -1544,6 +1557,13 @@ void usb_del_gadget(struct usb_gadget *gadget)
kobject_uevent(&udc->dev.kobj, KOBJ_REMOVE);
sysfs_remove_link(&udc->dev.kobj, "gadget");
device_del(&gadget->dev);
+ /*
+ * Set the teardown flag before flushing the work to prevent new work
+ * from being scheduled while we are cleaning up.
+ */
+ spin_lock_irqsave(&gadget->state_lock, flags);
+ gadget->teardown = true;
+ spin_unlock_irqrestore(&gadget->state_lock, flags);
flush_work(&gadget->work);
ida_free(&gadget_id_numbers, gadget->id_number);
cancel_work_sync(&udc->vbus_work);
@@ -1570,7 +1590,7 @@ static int gadget_match_driver(struct device *dev, const struct device_driver *d
{
struct usb_gadget *gadget = dev_to_usb_gadget(dev);
struct usb_udc *udc = gadget->udc;
- struct usb_gadget_driver *driver = container_of(drv,
+ const struct usb_gadget_driver *driver = container_of(drv,
struct usb_gadget_driver, driver);
/* If the driver specifies a udc_name, it must match the UDC's name */
diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c
index bda08c5ba7c0..f094491b1041 100644
--- a/drivers/usb/gadget/udc/dummy_hcd.c
+++ b/drivers/usb/gadget/udc/dummy_hcd.c
@@ -462,8 +462,13 @@ static void set_link_state(struct dummy_hcd *dum_hcd)
/* Report reset and disconnect events to the driver */
if (dum->ints_enabled && (disconnect || reset)) {
- stop_activity(dum);
++dum->callback_usage;
+ /*
+ * stop_activity() can drop dum->lock, so it must
+ * not come between the dum->ints_enabled test
+ * and the ++dum->callback_usage.
+ */
+ stop_activity(dum);
spin_unlock(&dum->lock);
if (reset)
usb_gadget_udc_reset(&dum->gadget, dum->driver);
@@ -623,7 +628,7 @@ static int dummy_enable(struct usb_ep *_ep,
dev_dbg(udc_dev(dum), "enabled %s (ep%d%s-%s) maxpacket %d stream %s\n",
_ep->name,
- desc->bEndpointAddress & 0x0f,
+ usb_endpoint_num(desc),
(desc->bEndpointAddress & USB_DIR_IN) ? "in" : "out",
usb_ep_type_string(usb_endpoint_type(desc)),
max, str_enabled_disabled(ep->stream_en));
@@ -666,7 +671,7 @@ static struct usb_request *dummy_alloc_request(struct usb_ep *_ep,
if (!_ep)
return NULL;
- req = kzalloc(sizeof(*req), mem_flags);
+ req = kzalloc_obj(*req, mem_flags);
if (!req)
return NULL;
INIT_LIST_HEAD(&req->queue);
@@ -765,8 +770,7 @@ static int dummy_dequeue(struct usb_ep *_ep, struct usb_request *_req)
if (!dum->driver)
return -ESHUTDOWN;
- local_irq_save(flags);
- spin_lock(&dum->lock);
+ spin_lock_irqsave(&dum->lock, flags);
list_for_each_entry(iter, &ep->queue, queue) {
if (&iter->req != _req)
continue;
@@ -776,15 +780,16 @@ static int dummy_dequeue(struct usb_ep *_ep, struct usb_request *_req)
retval = 0;
break;
}
- spin_unlock(&dum->lock);
if (retval == 0) {
dev_dbg(udc_dev(dum),
"dequeued req %p from %s, len %d buf %p\n",
req, _ep->name, _req->length, _req->buf);
+ spin_unlock(&dum->lock);
usb_gadget_giveback_request(_ep, _req);
+ spin_lock(&dum->lock);
}
- local_irq_restore(flags);
+ spin_unlock_irqrestore(&dum->lock, flags);
return retval;
}
@@ -908,21 +913,6 @@ static int dummy_pullup(struct usb_gadget *_gadget, int value)
spin_lock_irqsave(&dum->lock, flags);
dum->pullup = (value != 0);
set_link_state(dum_hcd);
- if (value == 0) {
- /*
- * Emulate synchronize_irq(): wait for callbacks to finish.
- * This seems to be the best place to emulate the call to
- * synchronize_irq() that's in usb_gadget_remove_driver().
- * Doing it in dummy_udc_stop() would be too late since it
- * is called after the unbind callback and unbind shouldn't
- * be invoked until all the other callbacks are finished.
- */
- while (dum->callback_usage > 0) {
- spin_unlock_irqrestore(&dum->lock, flags);
- usleep_range(1000, 2000);
- spin_lock_irqsave(&dum->lock, flags);
- }
- }
spin_unlock_irqrestore(&dum->lock, flags);
usb_hcd_poll_rh_status(dummy_hcd_to_hcd(dum_hcd));
@@ -945,6 +935,20 @@ static void dummy_udc_async_callbacks(struct usb_gadget *_gadget, bool enable)
spin_lock_irq(&dum->lock);
dum->ints_enabled = enable;
+ if (!enable) {
+ /*
+ * Emulate synchronize_irq(): wait for callbacks to finish.
+ * This has to happen after emulated interrupts are disabled
+ * (dum->ints_enabled is clear) and before the unbind callback,
+ * just like the call to synchronize_irq() in
+ * gadget/udc/core:gadget_unbind_driver().
+ */
+ while (dum->callback_usage > 0) {
+ spin_unlock_irq(&dum->lock);
+ usleep_range(1000, 2000);
+ spin_lock_irq(&dum->lock);
+ }
+ }
spin_unlock_irq(&dum->lock);
}
@@ -1270,7 +1274,7 @@ static int dummy_urb_enqueue(
unsigned long flags;
int rc;
- urbp = kmalloc(sizeof *urbp, mem_flags);
+ urbp = kmalloc_obj(*urbp, mem_flags);
if (!urbp)
return -ENOMEM;
urbp->urb = urb;
@@ -1534,6 +1538,12 @@ top:
/* rescan to continue with any other queued i/o */
if (rescan)
goto top;
+
+ /* request not fully transferred; stop iterating to
+ * preserve data ordering across queued requests.
+ */
+ if (req->req.actual < req->req.length)
+ break;
}
return sent;
}
@@ -1787,7 +1797,8 @@ static int handle_control_request(struct dummy_hcd *dum_hcd, struct urb *urb,
*/
static enum hrtimer_restart dummy_timer(struct hrtimer *t)
{
- struct dummy_hcd *dum_hcd = from_timer(dum_hcd, t, timer);
+ struct dummy_hcd *dum_hcd = timer_container_of(dum_hcd, t,
+ timer);
struct dummy *dum = dum_hcd->dum;
struct urbp *urbp, *tmp;
unsigned long flags;
@@ -2479,8 +2490,7 @@ static DEVICE_ATTR_RO(urbs);
static int dummy_start_ss(struct dummy_hcd *dum_hcd)
{
- hrtimer_init(&dum_hcd->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
- dum_hcd->timer.function = dummy_timer;
+ hrtimer_setup(&dum_hcd->timer, dummy_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
dum_hcd->rh_state = DUMMY_RH_RUNNING;
dum_hcd->stream_en_ep = 0;
INIT_LIST_HEAD(&dum_hcd->urbp_list);
@@ -2509,8 +2519,7 @@ static int dummy_start(struct usb_hcd *hcd)
return dummy_start_ss(dum_hcd);
spin_lock_init(&dum_hcd->dum->lock);
- hrtimer_init(&dum_hcd->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
- dum_hcd->timer.function = dummy_timer;
+ hrtimer_setup(&dum_hcd->timer, dummy_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
dum_hcd->rh_state = DUMMY_RH_RUNNING;
INIT_LIST_HEAD(&dum_hcd->urbp_list);
@@ -2820,7 +2829,7 @@ static int __init dummy_hcd_init(void)
}
}
for (i = 0; i < mod_data.num; i++) {
- dum[i] = kzalloc(sizeof(struct dummy), GFP_KERNEL);
+ dum[i] = kzalloc_obj(struct dummy);
if (!dum[i]) {
retval = -ENOMEM;
goto err_add_pdata;
diff --git a/drivers/usb/gadget/udc/fsl_qe_udc.c b/drivers/usb/gadget/udc/fsl_qe_udc.c
index aacfde06387c..bf87285ad13c 100644
--- a/drivers/usb/gadget/udc/fsl_qe_udc.c
+++ b/drivers/usb/gadget/udc/fsl_qe_udc.c
@@ -417,7 +417,7 @@ static int qe_ep_rxbd_update(struct qe_ep *ep)
bd = ep->rxbase;
- ep->rxframe = kmalloc(sizeof(*ep->rxframe), GFP_ATOMIC);
+ ep->rxframe = kmalloc_obj(*ep->rxframe, GFP_ATOMIC);
if (!ep->rxframe)
return -ENOMEM;
@@ -666,7 +666,7 @@ static int qe_ep_init(struct qe_udc *udc,
}
if ((ep->tm == USBP_TM_CTL) || (ep->dir == USB_DIR_IN)) {
- ep->txframe = kmalloc(sizeof(*ep->txframe), GFP_ATOMIC);
+ ep->txframe = kmalloc_obj(*ep->txframe, GFP_ATOMIC);
if (!ep->txframe)
goto en_done2;
qe_frame_init(ep->txframe);
@@ -1670,7 +1670,7 @@ static struct usb_request *qe_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
{
struct qe_req *req;
- req = kzalloc(sizeof(*req), gfp_flags);
+ req = kzalloc_obj(*req, gfp_flags);
if (!req)
return NULL;
@@ -2344,7 +2344,7 @@ static struct qe_udc *qe_udc_config(struct platform_device *ofdev)
u64 size;
u32 offset;
- udc = kzalloc(sizeof(*udc), GFP_KERNEL);
+ udc = kzalloc_obj(*udc);
if (!udc)
goto cleanup;
diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c
index 4dea8bc30cf6..600ce8cc0fef 100644
--- a/drivers/usb/gadget/udc/fsl_udc_core.c
+++ b/drivers/usb/gadget/udc/fsl_udc_core.c
@@ -678,7 +678,7 @@ fsl_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
{
struct fsl_req *req;
- req = kzalloc(sizeof *req, gfp_flags);
+ req = kzalloc_obj(*req, gfp_flags);
if (!req)
return NULL;
@@ -2250,7 +2250,7 @@ static int struct_udc_setup(struct fsl_udc *udc,
pdata = dev_get_platdata(&pdev->dev);
udc->phy_mode = pdata->phy_mode;
- udc->eps = kcalloc(udc->max_ep, sizeof(struct fsl_ep), GFP_KERNEL);
+ udc->eps = kzalloc_objs(struct fsl_ep, udc->max_ep);
if (!udc->eps) {
dev_err(&udc->gadget.dev, "kmalloc udc endpoint status failed\n");
goto eps_alloc_failed;
@@ -2369,7 +2369,7 @@ static int fsl_udc_probe(struct platform_device *pdev)
unsigned int i;
u32 dccparams;
- udc_controller = kzalloc(sizeof(struct fsl_udc), GFP_KERNEL);
+ udc_controller = kzalloc_obj(struct fsl_udc);
if (udc_controller == NULL)
return -ENOMEM;
diff --git a/drivers/usb/gadget/udc/fusb300_udc.c b/drivers/usb/gadget/udc/fusb300_udc.c
deleted file mode 100644
index 5e94a99b3e53..000000000000
--- a/drivers/usb/gadget/udc/fusb300_udc.c
+++ /dev/null
@@ -1,1516 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Fusb300 UDC (USB gadget)
- *
- * Copyright (C) 2010 Faraday Technology Corp.
- *
- * Author : Yuan-hsin Chen <yhchen@faraday-tech.com>
- */
-#include <linux/dma-mapping.h>
-#include <linux/err.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/usb/ch9.h>
-#include <linux/usb/gadget.h>
-
-#include "fusb300_udc.h"
-
-MODULE_DESCRIPTION("FUSB300 USB gadget driver");
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Yuan-Hsin Chen, Feng-Hsin Chiang <john453@faraday-tech.com>");
-MODULE_ALIAS("platform:fusb300_udc");
-
-#define DRIVER_VERSION "20 October 2010"
-
-static const char udc_name[] = "fusb300_udc";
-static const char * const fusb300_ep_name[] = {
- "ep0", "ep1", "ep2", "ep3", "ep4", "ep5", "ep6", "ep7", "ep8", "ep9",
- "ep10", "ep11", "ep12", "ep13", "ep14", "ep15"
-};
-
-static void done(struct fusb300_ep *ep, struct fusb300_request *req,
- int status);
-
-static void fusb300_enable_bit(struct fusb300 *fusb300, u32 offset,
- u32 value)
-{
- u32 reg = ioread32(fusb300->reg + offset);
-
- reg |= value;
- iowrite32(reg, fusb300->reg + offset);
-}
-
-static void fusb300_disable_bit(struct fusb300 *fusb300, u32 offset,
- u32 value)
-{
- u32 reg = ioread32(fusb300->reg + offset);
-
- reg &= ~value;
- iowrite32(reg, fusb300->reg + offset);
-}
-
-
-static void fusb300_ep_setting(struct fusb300_ep *ep,
- struct fusb300_ep_info info)
-{
- ep->epnum = info.epnum;
- ep->type = info.type;
-}
-
-static int fusb300_ep_release(struct fusb300_ep *ep)
-{
- if (!ep->epnum)
- return 0;
- ep->epnum = 0;
- ep->stall = 0;
- ep->wedged = 0;
- return 0;
-}
-
-static void fusb300_set_fifo_entry(struct fusb300 *fusb300,
- u32 ep)
-{
- u32 val = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET1(ep));
-
- val &= ~FUSB300_EPSET1_FIFOENTRY_MSK;
- val |= FUSB300_EPSET1_FIFOENTRY(FUSB300_FIFO_ENTRY_NUM);
- iowrite32(val, fusb300->reg + FUSB300_OFFSET_EPSET1(ep));
-}
-
-static void fusb300_set_start_entry(struct fusb300 *fusb300,
- u8 ep)
-{
- u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET1(ep));
- u32 start_entry = fusb300->fifo_entry_num * FUSB300_FIFO_ENTRY_NUM;
-
- reg &= ~FUSB300_EPSET1_START_ENTRY_MSK ;
- reg |= FUSB300_EPSET1_START_ENTRY(start_entry);
- iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET1(ep));
- if (fusb300->fifo_entry_num == FUSB300_MAX_FIFO_ENTRY) {
- fusb300->fifo_entry_num = 0;
- fusb300->addrofs = 0;
- pr_err("fifo entry is over the maximum number!\n");
- } else
- fusb300->fifo_entry_num++;
-}
-
-/* set fusb300_set_start_entry first before fusb300_set_epaddrofs */
-static void fusb300_set_epaddrofs(struct fusb300 *fusb300,
- struct fusb300_ep_info info)
-{
- u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET2(info.epnum));
-
- reg &= ~FUSB300_EPSET2_ADDROFS_MSK;
- reg |= FUSB300_EPSET2_ADDROFS(fusb300->addrofs);
- iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET2(info.epnum));
- fusb300->addrofs += (info.maxpacket + 7) / 8 * FUSB300_FIFO_ENTRY_NUM;
-}
-
-static void ep_fifo_setting(struct fusb300 *fusb300,
- struct fusb300_ep_info info)
-{
- fusb300_set_fifo_entry(fusb300, info.epnum);
- fusb300_set_start_entry(fusb300, info.epnum);
- fusb300_set_epaddrofs(fusb300, info);
-}
-
-static void fusb300_set_eptype(struct fusb300 *fusb300,
- struct fusb300_ep_info info)
-{
- u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum));
-
- reg &= ~FUSB300_EPSET1_TYPE_MSK;
- reg |= FUSB300_EPSET1_TYPE(info.type);
- iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum));
-}
-
-static void fusb300_set_epdir(struct fusb300 *fusb300,
- struct fusb300_ep_info info)
-{
- u32 reg;
-
- if (!info.dir_in)
- return;
- reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum));
- reg &= ~FUSB300_EPSET1_DIR_MSK;
- reg |= FUSB300_EPSET1_DIRIN;
- iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum));
-}
-
-static void fusb300_set_ep_active(struct fusb300 *fusb300,
- u8 ep)
-{
- u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET1(ep));
-
- reg |= FUSB300_EPSET1_ACTEN;
- iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET1(ep));
-}
-
-static void fusb300_set_epmps(struct fusb300 *fusb300,
- struct fusb300_ep_info info)
-{
- u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET2(info.epnum));
-
- reg &= ~FUSB300_EPSET2_MPS_MSK;
- reg |= FUSB300_EPSET2_MPS(info.maxpacket);
- iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET2(info.epnum));
-}
-
-static void fusb300_set_interval(struct fusb300 *fusb300,
- struct fusb300_ep_info info)
-{
- u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum));
-
- reg &= ~FUSB300_EPSET1_INTERVAL(0x7);
- reg |= FUSB300_EPSET1_INTERVAL(info.interval);
- iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum));
-}
-
-static void fusb300_set_bwnum(struct fusb300 *fusb300,
- struct fusb300_ep_info info)
-{
- u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum));
-
- reg &= ~FUSB300_EPSET1_BWNUM(0x3);
- reg |= FUSB300_EPSET1_BWNUM(info.bw_num);
- iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum));
-}
-
-static void set_ep_reg(struct fusb300 *fusb300,
- struct fusb300_ep_info info)
-{
- fusb300_set_eptype(fusb300, info);
- fusb300_set_epdir(fusb300, info);
- fusb300_set_epmps(fusb300, info);
-
- if (info.interval)
- fusb300_set_interval(fusb300, info);
-
- if (info.bw_num)
- fusb300_set_bwnum(fusb300, info);
-
- fusb300_set_ep_active(fusb300, info.epnum);
-}
-
-static int config_ep(struct fusb300_ep *ep,
- const struct usb_endpoint_descriptor *desc)
-{
- struct fusb300 *fusb300 = ep->fusb300;
- struct fusb300_ep_info info;
-
- ep->ep.desc = desc;
-
- info.interval = 0;
- info.addrofs = 0;
- info.bw_num = 0;
-
- info.type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
- info.dir_in = (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ? 1 : 0;
- info.maxpacket = usb_endpoint_maxp(desc);
- info.epnum = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
-
- if ((info.type == USB_ENDPOINT_XFER_INT) ||
- (info.type == USB_ENDPOINT_XFER_ISOC)) {
- info.interval = desc->bInterval;
- if (info.type == USB_ENDPOINT_XFER_ISOC)
- info.bw_num = usb_endpoint_maxp_mult(desc);
- }
-
- ep_fifo_setting(fusb300, info);
-
- set_ep_reg(fusb300, info);
-
- fusb300_ep_setting(ep, info);
-
- fusb300->ep[info.epnum] = ep;
-
- return 0;
-}
-
-static int fusb300_enable(struct usb_ep *_ep,
- const struct usb_endpoint_descriptor *desc)
-{
- struct fusb300_ep *ep;
-
- ep = container_of(_ep, struct fusb300_ep, ep);
-
- if (ep->fusb300->reenum) {
- ep->fusb300->fifo_entry_num = 0;
- ep->fusb300->addrofs = 0;
- ep->fusb300->reenum = 0;
- }
-
- return config_ep(ep, desc);
-}
-
-static int fusb300_disable(struct usb_ep *_ep)
-{
- struct fusb300_ep *ep;
- struct fusb300_request *req;
- unsigned long flags;
-
- ep = container_of(_ep, struct fusb300_ep, ep);
-
- BUG_ON(!ep);
-
- while (!list_empty(&ep->queue)) {
- req = list_entry(ep->queue.next, struct fusb300_request, queue);
- spin_lock_irqsave(&ep->fusb300->lock, flags);
- done(ep, req, -ECONNRESET);
- spin_unlock_irqrestore(&ep->fusb300->lock, flags);
- }
-
- return fusb300_ep_release(ep);
-}
-
-static struct usb_request *fusb300_alloc_request(struct usb_ep *_ep,
- gfp_t gfp_flags)
-{
- struct fusb300_request *req;
-
- req = kzalloc(sizeof(struct fusb300_request), gfp_flags);
- if (!req)
- return NULL;
- INIT_LIST_HEAD(&req->queue);
-
- return &req->req;
-}
-
-static void fusb300_free_request(struct usb_ep *_ep, struct usb_request *_req)
-{
- struct fusb300_request *req;
-
- req = container_of(_req, struct fusb300_request, req);
- kfree(req);
-}
-
-static int enable_fifo_int(struct fusb300_ep *ep)
-{
- struct fusb300 *fusb300 = ep->fusb300;
-
- if (ep->epnum) {
- fusb300_enable_bit(fusb300, FUSB300_OFFSET_IGER0,
- FUSB300_IGER0_EEPn_FIFO_INT(ep->epnum));
- } else {
- pr_err("can't enable_fifo_int ep0\n");
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int disable_fifo_int(struct fusb300_ep *ep)
-{
- struct fusb300 *fusb300 = ep->fusb300;
-
- if (ep->epnum) {
- fusb300_disable_bit(fusb300, FUSB300_OFFSET_IGER0,
- FUSB300_IGER0_EEPn_FIFO_INT(ep->epnum));
- } else {
- pr_err("can't disable_fifo_int ep0\n");
- return -EINVAL;
- }
-
- return 0;
-}
-
-static void fusb300_set_cxlen(struct fusb300 *fusb300, u32 length)
-{
- u32 reg;
-
- reg = ioread32(fusb300->reg + FUSB300_OFFSET_CSR);
- reg &= ~FUSB300_CSR_LEN_MSK;
- reg |= FUSB300_CSR_LEN(length);
- iowrite32(reg, fusb300->reg + FUSB300_OFFSET_CSR);
-}
-
-/* write data to cx fifo */
-static void fusb300_wrcxf(struct fusb300_ep *ep,
- struct fusb300_request *req)
-{
- int i = 0;
- u8 *tmp;
- u32 data;
- struct fusb300 *fusb300 = ep->fusb300;
- u32 length = req->req.length - req->req.actual;
-
- tmp = req->req.buf + req->req.actual;
-
- if (length > SS_CTL_MAX_PACKET_SIZE) {
- fusb300_set_cxlen(fusb300, SS_CTL_MAX_PACKET_SIZE);
- for (i = (SS_CTL_MAX_PACKET_SIZE >> 2); i > 0; i--) {
- data = *tmp | *(tmp + 1) << 8 | *(tmp + 2) << 16 |
- *(tmp + 3) << 24;
- iowrite32(data, fusb300->reg + FUSB300_OFFSET_CXPORT);
- tmp += 4;
- }
- req->req.actual += SS_CTL_MAX_PACKET_SIZE;
- } else { /* length is less than max packet size */
- fusb300_set_cxlen(fusb300, length);
- for (i = length >> 2; i > 0; i--) {
- data = *tmp | *(tmp + 1) << 8 | *(tmp + 2) << 16 |
- *(tmp + 3) << 24;
- printk(KERN_DEBUG " 0x%x\n", data);
- iowrite32(data, fusb300->reg + FUSB300_OFFSET_CXPORT);
- tmp = tmp + 4;
- }
- switch (length % 4) {
- case 1:
- data = *tmp;
- printk(KERN_DEBUG " 0x%x\n", data);
- iowrite32(data, fusb300->reg + FUSB300_OFFSET_CXPORT);
- break;
- case 2:
- data = *tmp | *(tmp + 1) << 8;
- printk(KERN_DEBUG " 0x%x\n", data);
- iowrite32(data, fusb300->reg + FUSB300_OFFSET_CXPORT);
- break;
- case 3:
- data = *tmp | *(tmp + 1) << 8 | *(tmp + 2) << 16;
- printk(KERN_DEBUG " 0x%x\n", data);
- iowrite32(data, fusb300->reg + FUSB300_OFFSET_CXPORT);
- break;
- default:
- break;
- }
- req->req.actual += length;
- }
-}
-
-static void fusb300_set_epnstall(struct fusb300 *fusb300, u8 ep)
-{
- fusb300_enable_bit(fusb300, FUSB300_OFFSET_EPSET0(ep),
- FUSB300_EPSET0_STL);
-}
-
-static void fusb300_clear_epnstall(struct fusb300 *fusb300, u8 ep)
-{
- u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET0(ep));
-
- if (reg & FUSB300_EPSET0_STL) {
- printk(KERN_DEBUG "EP%d stall... Clear!!\n", ep);
- reg |= FUSB300_EPSET0_STL_CLR;
- iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET0(ep));
- }
-}
-
-static void ep0_queue(struct fusb300_ep *ep, struct fusb300_request *req)
-{
- if (ep->fusb300->ep0_dir) { /* if IN */
- if (req->req.length) {
- fusb300_wrcxf(ep, req);
- } else
- printk(KERN_DEBUG "%s : req->req.length = 0x%x\n",
- __func__, req->req.length);
- if ((req->req.length == req->req.actual) ||
- (req->req.actual < ep->ep.maxpacket))
- done(ep, req, 0);
- } else { /* OUT */
- if (!req->req.length)
- done(ep, req, 0);
- else
- fusb300_enable_bit(ep->fusb300, FUSB300_OFFSET_IGER1,
- FUSB300_IGER1_CX_OUT_INT);
- }
-}
-
-static int fusb300_queue(struct usb_ep *_ep, struct usb_request *_req,
- gfp_t gfp_flags)
-{
- struct fusb300_ep *ep;
- struct fusb300_request *req;
- unsigned long flags;
- int request = 0;
-
- ep = container_of(_ep, struct fusb300_ep, ep);
- req = container_of(_req, struct fusb300_request, req);
-
- if (ep->fusb300->gadget.speed == USB_SPEED_UNKNOWN)
- return -ESHUTDOWN;
-
- spin_lock_irqsave(&ep->fusb300->lock, flags);
-
- if (list_empty(&ep->queue))
- request = 1;
-
- list_add_tail(&req->queue, &ep->queue);
-
- req->req.actual = 0;
- req->req.status = -EINPROGRESS;
-
- if (ep->ep.desc == NULL) /* ep0 */
- ep0_queue(ep, req);
- else if (request && !ep->stall)
- enable_fifo_int(ep);
-
- spin_unlock_irqrestore(&ep->fusb300->lock, flags);
-
- return 0;
-}
-
-static int fusb300_dequeue(struct usb_ep *_ep, struct usb_request *_req)
-{
- struct fusb300_ep *ep;
- struct fusb300_request *req;
- unsigned long flags;
-
- ep = container_of(_ep, struct fusb300_ep, ep);
- req = container_of(_req, struct fusb300_request, req);
-
- spin_lock_irqsave(&ep->fusb300->lock, flags);
- if (!list_empty(&ep->queue))
- done(ep, req, -ECONNRESET);
- spin_unlock_irqrestore(&ep->fusb300->lock, flags);
-
- return 0;
-}
-
-static int fusb300_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedge)
-{
- struct fusb300_ep *ep;
- struct fusb300 *fusb300;
- unsigned long flags;
- int ret = 0;
-
- ep = container_of(_ep, struct fusb300_ep, ep);
-
- fusb300 = ep->fusb300;
-
- spin_lock_irqsave(&ep->fusb300->lock, flags);
-
- if (!list_empty(&ep->queue)) {
- ret = -EAGAIN;
- goto out;
- }
-
- if (value) {
- fusb300_set_epnstall(fusb300, ep->epnum);
- ep->stall = 1;
- if (wedge)
- ep->wedged = 1;
- } else {
- fusb300_clear_epnstall(fusb300, ep->epnum);
- ep->stall = 0;
- ep->wedged = 0;
- }
-
-out:
- spin_unlock_irqrestore(&ep->fusb300->lock, flags);
- return ret;
-}
-
-static int fusb300_set_halt(struct usb_ep *_ep, int value)
-{
- return fusb300_set_halt_and_wedge(_ep, value, 0);
-}
-
-static int fusb300_set_wedge(struct usb_ep *_ep)
-{
- return fusb300_set_halt_and_wedge(_ep, 1, 1);
-}
-
-static void fusb300_fifo_flush(struct usb_ep *_ep)
-{
-}
-
-static const struct usb_ep_ops fusb300_ep_ops = {
- .enable = fusb300_enable,
- .disable = fusb300_disable,
-
- .alloc_request = fusb300_alloc_request,
- .free_request = fusb300_free_request,
-
- .queue = fusb300_queue,
- .dequeue = fusb300_dequeue,
-
- .set_halt = fusb300_set_halt,
- .fifo_flush = fusb300_fifo_flush,
- .set_wedge = fusb300_set_wedge,
-};
-
-/*****************************************************************************/
-static void fusb300_clear_int(struct fusb300 *fusb300, u32 offset,
- u32 value)
-{
- iowrite32(value, fusb300->reg + offset);
-}
-
-static void fusb300_reset(void)
-{
-}
-
-static void fusb300_set_cxstall(struct fusb300 *fusb300)
-{
- fusb300_enable_bit(fusb300, FUSB300_OFFSET_CSR,
- FUSB300_CSR_STL);
-}
-
-static void fusb300_set_cxdone(struct fusb300 *fusb300)
-{
- fusb300_enable_bit(fusb300, FUSB300_OFFSET_CSR,
- FUSB300_CSR_DONE);
-}
-
-/* read data from cx fifo */
-static void fusb300_rdcxf(struct fusb300 *fusb300,
- u8 *buffer, u32 length)
-{
- int i = 0;
- u8 *tmp;
- u32 data;
-
- tmp = buffer;
-
- for (i = (length >> 2); i > 0; i--) {
- data = ioread32(fusb300->reg + FUSB300_OFFSET_CXPORT);
- printk(KERN_DEBUG " 0x%x\n", data);
- *tmp = data & 0xFF;
- *(tmp + 1) = (data >> 8) & 0xFF;
- *(tmp + 2) = (data >> 16) & 0xFF;
- *(tmp + 3) = (data >> 24) & 0xFF;
- tmp = tmp + 4;
- }
-
- switch (length % 4) {
- case 1:
- data = ioread32(fusb300->reg + FUSB300_OFFSET_CXPORT);
- printk(KERN_DEBUG " 0x%x\n", data);
- *tmp = data & 0xFF;
- break;
- case 2:
- data = ioread32(fusb300->reg + FUSB300_OFFSET_CXPORT);
- printk(KERN_DEBUG " 0x%x\n", data);
- *tmp = data & 0xFF;
- *(tmp + 1) = (data >> 8) & 0xFF;
- break;
- case 3:
- data = ioread32(fusb300->reg + FUSB300_OFFSET_CXPORT);
- printk(KERN_DEBUG " 0x%x\n", data);
- *tmp = data & 0xFF;
- *(tmp + 1) = (data >> 8) & 0xFF;
- *(tmp + 2) = (data >> 16) & 0xFF;
- break;
- default:
- break;
- }
-}
-
-static void fusb300_rdfifo(struct fusb300_ep *ep,
- struct fusb300_request *req,
- u32 length)
-{
- int i = 0;
- u8 *tmp;
- u32 data, reg;
- struct fusb300 *fusb300 = ep->fusb300;
-
- tmp = req->req.buf + req->req.actual;
- req->req.actual += length;
-
- if (req->req.actual > req->req.length)
- printk(KERN_DEBUG "req->req.actual > req->req.length\n");
-
- for (i = (length >> 2); i > 0; i--) {
- data = ioread32(fusb300->reg +
- FUSB300_OFFSET_EPPORT(ep->epnum));
- *tmp = data & 0xFF;
- *(tmp + 1) = (data >> 8) & 0xFF;
- *(tmp + 2) = (data >> 16) & 0xFF;
- *(tmp + 3) = (data >> 24) & 0xFF;
- tmp = tmp + 4;
- }
-
- switch (length % 4) {
- case 1:
- data = ioread32(fusb300->reg +
- FUSB300_OFFSET_EPPORT(ep->epnum));
- *tmp = data & 0xFF;
- break;
- case 2:
- data = ioread32(fusb300->reg +
- FUSB300_OFFSET_EPPORT(ep->epnum));
- *tmp = data & 0xFF;
- *(tmp + 1) = (data >> 8) & 0xFF;
- break;
- case 3:
- data = ioread32(fusb300->reg +
- FUSB300_OFFSET_EPPORT(ep->epnum));
- *tmp = data & 0xFF;
- *(tmp + 1) = (data >> 8) & 0xFF;
- *(tmp + 2) = (data >> 16) & 0xFF;
- break;
- default:
- break;
- }
-
- do {
- reg = ioread32(fusb300->reg + FUSB300_OFFSET_IGR1);
- reg &= FUSB300_IGR1_SYNF0_EMPTY_INT;
- if (i)
- printk(KERN_INFO "sync fifo is not empty!\n");
- i++;
- } while (!reg);
-}
-
-static u8 fusb300_get_epnstall(struct fusb300 *fusb300, u8 ep)
-{
- u8 value;
- u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET0(ep));
-
- value = reg & FUSB300_EPSET0_STL;
-
- return value;
-}
-
-static u8 fusb300_get_cxstall(struct fusb300 *fusb300)
-{
- u8 value;
- u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_CSR);
-
- value = (reg & FUSB300_CSR_STL) >> 1;
-
- return value;
-}
-
-static void request_error(struct fusb300 *fusb300)
-{
- fusb300_set_cxstall(fusb300);
- printk(KERN_DEBUG "request error!!\n");
-}
-
-static void get_status(struct fusb300 *fusb300, struct usb_ctrlrequest *ctrl)
-__releases(fusb300->lock)
-__acquires(fusb300->lock)
-{
- u8 ep;
- u16 status = 0;
- u16 w_index = ctrl->wIndex;
-
- switch (ctrl->bRequestType & USB_RECIP_MASK) {
- case USB_RECIP_DEVICE:
- status = 1 << USB_DEVICE_SELF_POWERED;
- break;
- case USB_RECIP_INTERFACE:
- status = 0;
- break;
- case USB_RECIP_ENDPOINT:
- ep = w_index & USB_ENDPOINT_NUMBER_MASK;
- if (ep) {
- if (fusb300_get_epnstall(fusb300, ep))
- status = 1 << USB_ENDPOINT_HALT;
- } else {
- if (fusb300_get_cxstall(fusb300))
- status = 0;
- }
- break;
-
- default:
- request_error(fusb300);
- return; /* exit */
- }
-
- fusb300->ep0_data = cpu_to_le16(status);
- fusb300->ep0_req->buf = &fusb300->ep0_data;
- fusb300->ep0_req->length = 2;
-
- spin_unlock(&fusb300->lock);
- fusb300_queue(fusb300->gadget.ep0, fusb300->ep0_req, GFP_KERNEL);
- spin_lock(&fusb300->lock);
-}
-
-static void set_feature(struct fusb300 *fusb300, struct usb_ctrlrequest *ctrl)
-{
- u8 ep;
-
- switch (ctrl->bRequestType & USB_RECIP_MASK) {
- case USB_RECIP_DEVICE:
- fusb300_set_cxdone(fusb300);
- break;
- case USB_RECIP_INTERFACE:
- fusb300_set_cxdone(fusb300);
- break;
- case USB_RECIP_ENDPOINT: {
- u16 w_index = le16_to_cpu(ctrl->wIndex);
-
- ep = w_index & USB_ENDPOINT_NUMBER_MASK;
- if (ep)
- fusb300_set_epnstall(fusb300, ep);
- else
- fusb300_set_cxstall(fusb300);
- fusb300_set_cxdone(fusb300);
- }
- break;
- default:
- request_error(fusb300);
- break;
- }
-}
-
-static void fusb300_clear_seqnum(struct fusb300 *fusb300, u8 ep)
-{
- fusb300_enable_bit(fusb300, FUSB300_OFFSET_EPSET0(ep),
- FUSB300_EPSET0_CLRSEQNUM);
-}
-
-static void clear_feature(struct fusb300 *fusb300, struct usb_ctrlrequest *ctrl)
-{
- struct fusb300_ep *ep =
- fusb300->ep[ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK];
-
- switch (ctrl->bRequestType & USB_RECIP_MASK) {
- case USB_RECIP_DEVICE:
- fusb300_set_cxdone(fusb300);
- break;
- case USB_RECIP_INTERFACE:
- fusb300_set_cxdone(fusb300);
- break;
- case USB_RECIP_ENDPOINT:
- if (ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK) {
- if (ep->wedged) {
- fusb300_set_cxdone(fusb300);
- break;
- }
- if (ep->stall) {
- ep->stall = 0;
- fusb300_clear_seqnum(fusb300, ep->epnum);
- fusb300_clear_epnstall(fusb300, ep->epnum);
- if (!list_empty(&ep->queue))
- enable_fifo_int(ep);
- }
- }
- fusb300_set_cxdone(fusb300);
- break;
- default:
- request_error(fusb300);
- break;
- }
-}
-
-static void fusb300_set_dev_addr(struct fusb300 *fusb300, u16 addr)
-{
- u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_DAR);
-
- reg &= ~FUSB300_DAR_DRVADDR_MSK;
- reg |= FUSB300_DAR_DRVADDR(addr);
-
- iowrite32(reg, fusb300->reg + FUSB300_OFFSET_DAR);
-}
-
-static void set_address(struct fusb300 *fusb300, struct usb_ctrlrequest *ctrl)
-{
- if (ctrl->wValue >= 0x0100)
- request_error(fusb300);
- else {
- fusb300_set_dev_addr(fusb300, ctrl->wValue);
- fusb300_set_cxdone(fusb300);
- }
-}
-
-#define UVC_COPY_DESCRIPTORS(mem, src) \
- do { \
- const struct usb_descriptor_header * const *__src; \
- for (__src = src; *__src; ++__src) { \
- memcpy(mem, *__src, (*__src)->bLength); \
- mem += (*__src)->bLength; \
- } \
- } while (0)
-
-static int setup_packet(struct fusb300 *fusb300, struct usb_ctrlrequest *ctrl)
-{
- u8 *p = (u8 *)ctrl;
- u8 ret = 0;
- u8 i = 0;
-
- fusb300_rdcxf(fusb300, p, 8);
- fusb300->ep0_dir = ctrl->bRequestType & USB_DIR_IN;
- fusb300->ep0_length = ctrl->wLength;
-
- /* check request */
- if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
- switch (ctrl->bRequest) {
- case USB_REQ_GET_STATUS:
- get_status(fusb300, ctrl);
- break;
- case USB_REQ_CLEAR_FEATURE:
- clear_feature(fusb300, ctrl);
- break;
- case USB_REQ_SET_FEATURE:
- set_feature(fusb300, ctrl);
- break;
- case USB_REQ_SET_ADDRESS:
- set_address(fusb300, ctrl);
- break;
- case USB_REQ_SET_CONFIGURATION:
- fusb300_enable_bit(fusb300, FUSB300_OFFSET_DAR,
- FUSB300_DAR_SETCONFG);
- /* clear sequence number */
- for (i = 1; i <= FUSB300_MAX_NUM_EP; i++)
- fusb300_clear_seqnum(fusb300, i);
- fusb300->reenum = 1;
- ret = 1;
- break;
- default:
- ret = 1;
- break;
- }
- } else
- ret = 1;
-
- return ret;
-}
-
-static void done(struct fusb300_ep *ep, struct fusb300_request *req,
- int status)
-{
- list_del_init(&req->queue);
-
- /* don't modify queue heads during completion callback */
- if (ep->fusb300->gadget.speed == USB_SPEED_UNKNOWN)
- req->req.status = -ESHUTDOWN;
- else
- req->req.status = status;
-
- spin_unlock(&ep->fusb300->lock);
- usb_gadget_giveback_request(&ep->ep, &req->req);
- spin_lock(&ep->fusb300->lock);
-
- if (ep->epnum) {
- disable_fifo_int(ep);
- if (!list_empty(&ep->queue))
- enable_fifo_int(ep);
- } else
- fusb300_set_cxdone(ep->fusb300);
-}
-
-static void fusb300_fill_idma_prdtbl(struct fusb300_ep *ep, dma_addr_t d,
- u32 len)
-{
- u32 value;
- u32 reg;
-
- /* wait SW owner */
- do {
- reg = ioread32(ep->fusb300->reg +
- FUSB300_OFFSET_EPPRD_W0(ep->epnum));
- reg &= FUSB300_EPPRD0_H;
- } while (reg);
-
- iowrite32(d, ep->fusb300->reg + FUSB300_OFFSET_EPPRD_W1(ep->epnum));
-
- value = FUSB300_EPPRD0_BTC(len) | FUSB300_EPPRD0_H |
- FUSB300_EPPRD0_F | FUSB300_EPPRD0_L | FUSB300_EPPRD0_I;
- iowrite32(value, ep->fusb300->reg + FUSB300_OFFSET_EPPRD_W0(ep->epnum));
-
- iowrite32(0x0, ep->fusb300->reg + FUSB300_OFFSET_EPPRD_W2(ep->epnum));
-
- fusb300_enable_bit(ep->fusb300, FUSB300_OFFSET_EPPRDRDY,
- FUSB300_EPPRDR_EP_PRD_RDY(ep->epnum));
-}
-
-static void fusb300_wait_idma_finished(struct fusb300_ep *ep)
-{
- u32 reg;
-
- do {
- reg = ioread32(ep->fusb300->reg + FUSB300_OFFSET_IGR1);
- if ((reg & FUSB300_IGR1_VBUS_CHG_INT) ||
- (reg & FUSB300_IGR1_WARM_RST_INT) ||
- (reg & FUSB300_IGR1_HOT_RST_INT) ||
- (reg & FUSB300_IGR1_USBRST_INT)
- )
- goto IDMA_RESET;
- reg = ioread32(ep->fusb300->reg + FUSB300_OFFSET_IGR0);
- reg &= FUSB300_IGR0_EPn_PRD_INT(ep->epnum);
- } while (!reg);
-
- fusb300_clear_int(ep->fusb300, FUSB300_OFFSET_IGR0,
- FUSB300_IGR0_EPn_PRD_INT(ep->epnum));
- return;
-
-IDMA_RESET:
- reg = ioread32(ep->fusb300->reg + FUSB300_OFFSET_IGER0);
- reg &= ~FUSB300_IGER0_EEPn_PRD_INT(ep->epnum);
- iowrite32(reg, ep->fusb300->reg + FUSB300_OFFSET_IGER0);
-}
-
-static void fusb300_set_idma(struct fusb300_ep *ep,
- struct fusb300_request *req)
-{
- int ret;
-
- ret = usb_gadget_map_request(&ep->fusb300->gadget,
- &req->req, DMA_TO_DEVICE);
- if (ret)
- return;
-
- fusb300_enable_bit(ep->fusb300, FUSB300_OFFSET_IGER0,
- FUSB300_IGER0_EEPn_PRD_INT(ep->epnum));
-
- fusb300_fill_idma_prdtbl(ep, req->req.dma, req->req.length);
- /* check idma is done */
- fusb300_wait_idma_finished(ep);
-
- usb_gadget_unmap_request(&ep->fusb300->gadget,
- &req->req, DMA_TO_DEVICE);
-}
-
-static void in_ep_fifo_handler(struct fusb300_ep *ep)
-{
- struct fusb300_request *req = list_entry(ep->queue.next,
- struct fusb300_request, queue);
-
- if (req->req.length)
- fusb300_set_idma(ep, req);
- done(ep, req, 0);
-}
-
-static void out_ep_fifo_handler(struct fusb300_ep *ep)
-{
- struct fusb300 *fusb300 = ep->fusb300;
- struct fusb300_request *req = list_entry(ep->queue.next,
- struct fusb300_request, queue);
- u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPFFR(ep->epnum));
- u32 length = reg & FUSB300_FFR_BYCNT;
-
- fusb300_rdfifo(ep, req, length);
-
- /* finish out transfer */
- if ((req->req.length == req->req.actual) || (length < ep->ep.maxpacket))
- done(ep, req, 0);
-}
-
-static void check_device_mode(struct fusb300 *fusb300)
-{
- u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_GCR);
-
- switch (reg & FUSB300_GCR_DEVEN_MSK) {
- case FUSB300_GCR_DEVEN_SS:
- fusb300->gadget.speed = USB_SPEED_SUPER;
- break;
- case FUSB300_GCR_DEVEN_HS:
- fusb300->gadget.speed = USB_SPEED_HIGH;
- break;
- case FUSB300_GCR_DEVEN_FS:
- fusb300->gadget.speed = USB_SPEED_FULL;
- break;
- default:
- fusb300->gadget.speed = USB_SPEED_UNKNOWN;
- break;
- }
- printk(KERN_INFO "dev_mode = %d\n", (reg & FUSB300_GCR_DEVEN_MSK));
-}
-
-
-static void fusb300_ep0out(struct fusb300 *fusb300)
-{
- struct fusb300_ep *ep = fusb300->ep[0];
- u32 reg;
-
- if (!list_empty(&ep->queue)) {
- struct fusb300_request *req;
-
- req = list_first_entry(&ep->queue,
- struct fusb300_request, queue);
- if (req->req.length)
- fusb300_rdcxf(ep->fusb300, req->req.buf,
- req->req.length);
- done(ep, req, 0);
- reg = ioread32(fusb300->reg + FUSB300_OFFSET_IGER1);
- reg &= ~FUSB300_IGER1_CX_OUT_INT;
- iowrite32(reg, fusb300->reg + FUSB300_OFFSET_IGER1);
- } else
- pr_err("%s : empty queue\n", __func__);
-}
-
-static void fusb300_ep0in(struct fusb300 *fusb300)
-{
- struct fusb300_request *req;
- struct fusb300_ep *ep = fusb300->ep[0];
-
- if ((!list_empty(&ep->queue)) && (fusb300->ep0_dir)) {
- req = list_entry(ep->queue.next,
- struct fusb300_request, queue);
- if (req->req.length)
- fusb300_wrcxf(ep, req);
- if ((req->req.length - req->req.actual) < ep->ep.maxpacket)
- done(ep, req, 0);
- } else
- fusb300_set_cxdone(fusb300);
-}
-
-static void fusb300_grp2_handler(void)
-{
-}
-
-static void fusb300_grp3_handler(void)
-{
-}
-
-static void fusb300_grp4_handler(void)
-{
-}
-
-static void fusb300_grp5_handler(void)
-{
-}
-
-static irqreturn_t fusb300_irq(int irq, void *_fusb300)
-{
- struct fusb300 *fusb300 = _fusb300;
- u32 int_grp1 = ioread32(fusb300->reg + FUSB300_OFFSET_IGR1);
- u32 int_grp1_en = ioread32(fusb300->reg + FUSB300_OFFSET_IGER1);
- u32 int_grp0 = ioread32(fusb300->reg + FUSB300_OFFSET_IGR0);
- u32 int_grp0_en = ioread32(fusb300->reg + FUSB300_OFFSET_IGER0);
- struct usb_ctrlrequest ctrl;
- u8 in;
- u32 reg;
- int i;
-
- spin_lock(&fusb300->lock);
-
- int_grp1 &= int_grp1_en;
- int_grp0 &= int_grp0_en;
-
- if (int_grp1 & FUSB300_IGR1_WARM_RST_INT) {
- fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
- FUSB300_IGR1_WARM_RST_INT);
- printk(KERN_INFO"fusb300_warmreset\n");
- fusb300_reset();
- }
-
- if (int_grp1 & FUSB300_IGR1_HOT_RST_INT) {
- fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
- FUSB300_IGR1_HOT_RST_INT);
- printk(KERN_INFO"fusb300_hotreset\n");
- fusb300_reset();
- }
-
- if (int_grp1 & FUSB300_IGR1_USBRST_INT) {
- fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
- FUSB300_IGR1_USBRST_INT);
- fusb300_reset();
- }
- /* COMABT_INT has a highest priority */
-
- if (int_grp1 & FUSB300_IGR1_CX_COMABT_INT) {
- fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
- FUSB300_IGR1_CX_COMABT_INT);
- printk(KERN_INFO"fusb300_ep0abt\n");
- }
-
- if (int_grp1 & FUSB300_IGR1_VBUS_CHG_INT) {
- fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
- FUSB300_IGR1_VBUS_CHG_INT);
- printk(KERN_INFO"fusb300_vbus_change\n");
- }
-
- if (int_grp1 & FUSB300_IGR1_U3_EXIT_FAIL_INT) {
- fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
- FUSB300_IGR1_U3_EXIT_FAIL_INT);
- }
-
- if (int_grp1 & FUSB300_IGR1_U2_EXIT_FAIL_INT) {
- fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
- FUSB300_IGR1_U2_EXIT_FAIL_INT);
- }
-
- if (int_grp1 & FUSB300_IGR1_U1_EXIT_FAIL_INT) {
- fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
- FUSB300_IGR1_U1_EXIT_FAIL_INT);
- }
-
- if (int_grp1 & FUSB300_IGR1_U2_ENTRY_FAIL_INT) {
- fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
- FUSB300_IGR1_U2_ENTRY_FAIL_INT);
- }
-
- if (int_grp1 & FUSB300_IGR1_U1_ENTRY_FAIL_INT) {
- fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
- FUSB300_IGR1_U1_ENTRY_FAIL_INT);
- }
-
- if (int_grp1 & FUSB300_IGR1_U3_EXIT_INT) {
- fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
- FUSB300_IGR1_U3_EXIT_INT);
- printk(KERN_INFO "FUSB300_IGR1_U3_EXIT_INT\n");
- }
-
- if (int_grp1 & FUSB300_IGR1_U2_EXIT_INT) {
- fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
- FUSB300_IGR1_U2_EXIT_INT);
- printk(KERN_INFO "FUSB300_IGR1_U2_EXIT_INT\n");
- }
-
- if (int_grp1 & FUSB300_IGR1_U1_EXIT_INT) {
- fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
- FUSB300_IGR1_U1_EXIT_INT);
- printk(KERN_INFO "FUSB300_IGR1_U1_EXIT_INT\n");
- }
-
- if (int_grp1 & FUSB300_IGR1_U3_ENTRY_INT) {
- fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
- FUSB300_IGR1_U3_ENTRY_INT);
- printk(KERN_INFO "FUSB300_IGR1_U3_ENTRY_INT\n");
- fusb300_enable_bit(fusb300, FUSB300_OFFSET_SSCR1,
- FUSB300_SSCR1_GO_U3_DONE);
- }
-
- if (int_grp1 & FUSB300_IGR1_U2_ENTRY_INT) {
- fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
- FUSB300_IGR1_U2_ENTRY_INT);
- printk(KERN_INFO "FUSB300_IGR1_U2_ENTRY_INT\n");
- }
-
- if (int_grp1 & FUSB300_IGR1_U1_ENTRY_INT) {
- fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
- FUSB300_IGR1_U1_ENTRY_INT);
- printk(KERN_INFO "FUSB300_IGR1_U1_ENTRY_INT\n");
- }
-
- if (int_grp1 & FUSB300_IGR1_RESM_INT) {
- fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
- FUSB300_IGR1_RESM_INT);
- printk(KERN_INFO "fusb300_resume\n");
- }
-
- if (int_grp1 & FUSB300_IGR1_SUSP_INT) {
- fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
- FUSB300_IGR1_SUSP_INT);
- printk(KERN_INFO "fusb300_suspend\n");
- }
-
- if (int_grp1 & FUSB300_IGR1_HS_LPM_INT) {
- fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
- FUSB300_IGR1_HS_LPM_INT);
- printk(KERN_INFO "fusb300_HS_LPM_INT\n");
- }
-
- if (int_grp1 & FUSB300_IGR1_DEV_MODE_CHG_INT) {
- fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1,
- FUSB300_IGR1_DEV_MODE_CHG_INT);
- check_device_mode(fusb300);
- }
-
- if (int_grp1 & FUSB300_IGR1_CX_COMFAIL_INT) {
- fusb300_set_cxstall(fusb300);
- printk(KERN_INFO "fusb300_ep0fail\n");
- }
-
- if (int_grp1 & FUSB300_IGR1_CX_SETUP_INT) {
- printk(KERN_INFO "fusb300_ep0setup\n");
- if (setup_packet(fusb300, &ctrl)) {
- spin_unlock(&fusb300->lock);
- if (fusb300->driver->setup(&fusb300->gadget, &ctrl) < 0)
- fusb300_set_cxstall(fusb300);
- spin_lock(&fusb300->lock);
- }
- }
-
- if (int_grp1 & FUSB300_IGR1_CX_CMDEND_INT)
- printk(KERN_INFO "fusb300_cmdend\n");
-
-
- if (int_grp1 & FUSB300_IGR1_CX_OUT_INT) {
- printk(KERN_INFO "fusb300_cxout\n");
- fusb300_ep0out(fusb300);
- }
-
- if (int_grp1 & FUSB300_IGR1_CX_IN_INT) {
- printk(KERN_INFO "fusb300_cxin\n");
- fusb300_ep0in(fusb300);
- }
-
- if (int_grp1 & FUSB300_IGR1_INTGRP5)
- fusb300_grp5_handler();
-
- if (int_grp1 & FUSB300_IGR1_INTGRP4)
- fusb300_grp4_handler();
-
- if (int_grp1 & FUSB300_IGR1_INTGRP3)
- fusb300_grp3_handler();
-
- if (int_grp1 & FUSB300_IGR1_INTGRP2)
- fusb300_grp2_handler();
-
- if (int_grp0) {
- for (i = 1; i < FUSB300_MAX_NUM_EP; i++) {
- if (int_grp0 & FUSB300_IGR0_EPn_FIFO_INT(i)) {
- reg = ioread32(fusb300->reg +
- FUSB300_OFFSET_EPSET1(i));
- in = (reg & FUSB300_EPSET1_DIRIN) ? 1 : 0;
- if (in)
- in_ep_fifo_handler(fusb300->ep[i]);
- else
- out_ep_fifo_handler(fusb300->ep[i]);
- }
- }
- }
-
- spin_unlock(&fusb300->lock);
-
- return IRQ_HANDLED;
-}
-
-static void fusb300_set_u2_timeout(struct fusb300 *fusb300,
- u32 time)
-{
- u32 reg;
-
- reg = ioread32(fusb300->reg + FUSB300_OFFSET_TT);
- reg &= ~0xff;
- reg |= FUSB300_SSCR2_U2TIMEOUT(time);
-
- iowrite32(reg, fusb300->reg + FUSB300_OFFSET_TT);
-}
-
-static void fusb300_set_u1_timeout(struct fusb300 *fusb300,
- u32 time)
-{
- u32 reg;
-
- reg = ioread32(fusb300->reg + FUSB300_OFFSET_TT);
- reg &= ~(0xff << 8);
- reg |= FUSB300_SSCR2_U1TIMEOUT(time);
-
- iowrite32(reg, fusb300->reg + FUSB300_OFFSET_TT);
-}
-
-static void init_controller(struct fusb300 *fusb300)
-{
- u32 reg;
- u32 mask = 0;
- u32 val = 0;
-
- /* split on */
- mask = val = FUSB300_AHBBCR_S0_SPLIT_ON | FUSB300_AHBBCR_S1_SPLIT_ON;
- reg = ioread32(fusb300->reg + FUSB300_OFFSET_AHBCR);
- reg &= ~mask;
- reg |= val;
- iowrite32(reg, fusb300->reg + FUSB300_OFFSET_AHBCR);
-
- /* enable high-speed LPM */
- mask = val = FUSB300_HSCR_HS_LPM_PERMIT;
- reg = ioread32(fusb300->reg + FUSB300_OFFSET_HSCR);
- reg &= ~mask;
- reg |= val;
- iowrite32(reg, fusb300->reg + FUSB300_OFFSET_HSCR);
-
- /*set u1 u2 timer*/
- fusb300_set_u2_timeout(fusb300, 0xff);
- fusb300_set_u1_timeout(fusb300, 0xff);
-
- /* enable all grp1 interrupt */
- iowrite32(0xcfffff9f, fusb300->reg + FUSB300_OFFSET_IGER1);
-}
-/*------------------------------------------------------------------------*/
-static int fusb300_udc_start(struct usb_gadget *g,
- struct usb_gadget_driver *driver)
-{
- struct fusb300 *fusb300 = to_fusb300(g);
-
- /* hook up the driver */
- fusb300->driver = driver;
-
- return 0;
-}
-
-static int fusb300_udc_stop(struct usb_gadget *g)
-{
- struct fusb300 *fusb300 = to_fusb300(g);
-
- init_controller(fusb300);
- fusb300->driver = NULL;
-
- return 0;
-}
-/*--------------------------------------------------------------------------*/
-
-static int fusb300_udc_pullup(struct usb_gadget *_gadget, int is_active)
-{
- return 0;
-}
-
-static const struct usb_gadget_ops fusb300_gadget_ops = {
- .pullup = fusb300_udc_pullup,
- .udc_start = fusb300_udc_start,
- .udc_stop = fusb300_udc_stop,
-};
-
-static void fusb300_remove(struct platform_device *pdev)
-{
- struct fusb300 *fusb300 = platform_get_drvdata(pdev);
- int i;
-
- usb_del_gadget_udc(&fusb300->gadget);
- iounmap(fusb300->reg);
- free_irq(platform_get_irq(pdev, 0), fusb300);
- free_irq(platform_get_irq(pdev, 1), fusb300);
-
- fusb300_free_request(&fusb300->ep[0]->ep, fusb300->ep0_req);
- for (i = 0; i < FUSB300_MAX_NUM_EP; i++)
- kfree(fusb300->ep[i]);
- kfree(fusb300);
-}
-
-static int fusb300_probe(struct platform_device *pdev)
-{
- struct resource *res, *ires, *ires1;
- void __iomem *reg = NULL;
- struct fusb300 *fusb300 = NULL;
- struct fusb300_ep *_ep[FUSB300_MAX_NUM_EP];
- int ret = 0;
- int i;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- ret = -ENODEV;
- pr_err("platform_get_resource error.\n");
- goto clean_up;
- }
-
- ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!ires) {
- ret = -ENODEV;
- dev_err(&pdev->dev,
- "platform_get_resource IORESOURCE_IRQ error.\n");
- goto clean_up;
- }
-
- ires1 = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
- if (!ires1) {
- ret = -ENODEV;
- dev_err(&pdev->dev,
- "platform_get_resource IORESOURCE_IRQ 1 error.\n");
- goto clean_up;
- }
-
- reg = ioremap(res->start, resource_size(res));
- if (reg == NULL) {
- ret = -ENOMEM;
- pr_err("ioremap error.\n");
- goto clean_up;
- }
-
- /* initialize udc */
- fusb300 = kzalloc(sizeof(struct fusb300), GFP_KERNEL);
- if (fusb300 == NULL) {
- ret = -ENOMEM;
- goto clean_up;
- }
-
- for (i = 0; i < FUSB300_MAX_NUM_EP; i++) {
- _ep[i] = kzalloc(sizeof(struct fusb300_ep), GFP_KERNEL);
- if (_ep[i] == NULL) {
- ret = -ENOMEM;
- goto clean_up;
- }
- fusb300->ep[i] = _ep[i];
- }
-
- spin_lock_init(&fusb300->lock);
-
- platform_set_drvdata(pdev, fusb300);
-
- fusb300->gadget.ops = &fusb300_gadget_ops;
-
- fusb300->gadget.max_speed = USB_SPEED_HIGH;
- fusb300->gadget.name = udc_name;
- fusb300->reg = reg;
-
- ret = request_irq(ires->start, fusb300_irq, IRQF_SHARED,
- udc_name, fusb300);
- if (ret < 0) {
- pr_err("request_irq error (%d)\n", ret);
- goto clean_up;
- }
-
- ret = request_irq(ires1->start, fusb300_irq,
- IRQF_SHARED, udc_name, fusb300);
- if (ret < 0) {
- pr_err("request_irq1 error (%d)\n", ret);
- goto err_request_irq1;
- }
-
- INIT_LIST_HEAD(&fusb300->gadget.ep_list);
-
- for (i = 0; i < FUSB300_MAX_NUM_EP ; i++) {
- struct fusb300_ep *ep = fusb300->ep[i];
-
- if (i != 0) {
- INIT_LIST_HEAD(&fusb300->ep[i]->ep.ep_list);
- list_add_tail(&fusb300->ep[i]->ep.ep_list,
- &fusb300->gadget.ep_list);
- }
- ep->fusb300 = fusb300;
- INIT_LIST_HEAD(&ep->queue);
- ep->ep.name = fusb300_ep_name[i];
- ep->ep.ops = &fusb300_ep_ops;
- usb_ep_set_maxpacket_limit(&ep->ep, HS_BULK_MAX_PACKET_SIZE);
-
- if (i == 0) {
- ep->ep.caps.type_control = true;
- } else {
- ep->ep.caps.type_iso = true;
- ep->ep.caps.type_bulk = true;
- ep->ep.caps.type_int = true;
- }
-
- ep->ep.caps.dir_in = true;
- ep->ep.caps.dir_out = true;
- }
- usb_ep_set_maxpacket_limit(&fusb300->ep[0]->ep, HS_CTL_MAX_PACKET_SIZE);
- fusb300->ep[0]->epnum = 0;
- fusb300->gadget.ep0 = &fusb300->ep[0]->ep;
- INIT_LIST_HEAD(&fusb300->gadget.ep0->ep_list);
-
- fusb300->ep0_req = fusb300_alloc_request(&fusb300->ep[0]->ep,
- GFP_KERNEL);
- if (fusb300->ep0_req == NULL) {
- ret = -ENOMEM;
- goto err_alloc_request;
- }
-
- init_controller(fusb300);
- ret = usb_add_gadget_udc(&pdev->dev, &fusb300->gadget);
- if (ret)
- goto err_add_udc;
-
- dev_info(&pdev->dev, "version %s\n", DRIVER_VERSION);
-
- return 0;
-
-err_add_udc:
- fusb300_free_request(&fusb300->ep[0]->ep, fusb300->ep0_req);
-
-err_alloc_request:
- free_irq(ires1->start, fusb300);
-
-err_request_irq1:
- free_irq(ires->start, fusb300);
-
-clean_up:
- if (fusb300) {
- if (fusb300->ep0_req)
- fusb300_free_request(&fusb300->ep[0]->ep,
- fusb300->ep0_req);
- for (i = 0; i < FUSB300_MAX_NUM_EP; i++)
- kfree(fusb300->ep[i]);
- kfree(fusb300);
- }
- if (reg)
- iounmap(reg);
-
- return ret;
-}
-
-static struct platform_driver fusb300_driver = {
- .probe = fusb300_probe,
- .remove = fusb300_remove,
- .driver = {
- .name = udc_name,
- },
-};
-
-module_platform_driver(fusb300_driver);
diff --git a/drivers/usb/gadget/udc/fusb300_udc.h b/drivers/usb/gadget/udc/fusb300_udc.h
deleted file mode 100644
index eb3d6d379ba7..000000000000
--- a/drivers/usb/gadget/udc/fusb300_udc.h
+++ /dev/null
@@ -1,675 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Fusb300 UDC (USB gadget)
- *
- * Copyright (C) 2010 Faraday Technology Corp.
- *
- * Author : Yuan-hsin Chen <yhchen@faraday-tech.com>
- */
-
-
-#ifndef __FUSB300_UDC_H__
-#define __FUSB300_UDC_H__
-
-#include <linux/kernel.h>
-
-#define FUSB300_OFFSET_GCR 0x00
-#define FUSB300_OFFSET_GTM 0x04
-#define FUSB300_OFFSET_DAR 0x08
-#define FUSB300_OFFSET_CSR 0x0C
-#define FUSB300_OFFSET_CXPORT 0x10
-#define FUSB300_OFFSET_EPSET0(n) (0x20 + (n - 1) * 0x30)
-#define FUSB300_OFFSET_EPSET1(n) (0x24 + (n - 1) * 0x30)
-#define FUSB300_OFFSET_EPSET2(n) (0x28 + (n - 1) * 0x30)
-#define FUSB300_OFFSET_EPFFR(n) (0x2c + (n - 1) * 0x30)
-#define FUSB300_OFFSET_EPSTRID(n) (0x40 + (n - 1) * 0x30)
-#define FUSB300_OFFSET_HSPTM 0x300
-#define FUSB300_OFFSET_HSCR 0x304
-#define FUSB300_OFFSET_SSCR0 0x308
-#define FUSB300_OFFSET_SSCR1 0x30C
-#define FUSB300_OFFSET_TT 0x310
-#define FUSB300_OFFSET_DEVNOTF 0x314
-#define FUSB300_OFFSET_DNC1 0x318
-#define FUSB300_OFFSET_CS 0x31C
-#define FUSB300_OFFSET_SOF 0x324
-#define FUSB300_OFFSET_EFCS 0x328
-#define FUSB300_OFFSET_IGR0 0x400
-#define FUSB300_OFFSET_IGR1 0x404
-#define FUSB300_OFFSET_IGR2 0x408
-#define FUSB300_OFFSET_IGR3 0x40C
-#define FUSB300_OFFSET_IGR4 0x410
-#define FUSB300_OFFSET_IGR5 0x414
-#define FUSB300_OFFSET_IGER0 0x420
-#define FUSB300_OFFSET_IGER1 0x424
-#define FUSB300_OFFSET_IGER2 0x428
-#define FUSB300_OFFSET_IGER3 0x42C
-#define FUSB300_OFFSET_IGER4 0x430
-#define FUSB300_OFFSET_IGER5 0x434
-#define FUSB300_OFFSET_DMAHMER 0x500
-#define FUSB300_OFFSET_EPPRDRDY 0x504
-#define FUSB300_OFFSET_DMAEPMR 0x508
-#define FUSB300_OFFSET_DMAENR 0x50C
-#define FUSB300_OFFSET_DMAAPR 0x510
-#define FUSB300_OFFSET_AHBCR 0x514
-#define FUSB300_OFFSET_EPPRD_W0(n) (0x520 + (n - 1) * 0x10)
-#define FUSB300_OFFSET_EPPRD_W1(n) (0x524 + (n - 1) * 0x10)
-#define FUSB300_OFFSET_EPPRD_W2(n) (0x528 + (n - 1) * 0x10)
-#define FUSB300_OFFSET_EPRD_PTR(n) (0x52C + (n - 1) * 0x10)
-#define FUSB300_OFFSET_BUFDBG_START 0x800
-#define FUSB300_OFFSET_BUFDBG_END 0xBFC
-#define FUSB300_OFFSET_EPPORT(n) (0x1010 + (n - 1) * 0x10)
-
-/*
- * * Global Control Register (offset = 000H)
- * */
-#define FUSB300_GCR_SF_RST (1 << 8)
-#define FUSB300_GCR_VBUS_STATUS (1 << 7)
-#define FUSB300_GCR_FORCE_HS_SUSP (1 << 6)
-#define FUSB300_GCR_SYNC_FIFO1_CLR (1 << 5)
-#define FUSB300_GCR_SYNC_FIFO0_CLR (1 << 4)
-#define FUSB300_GCR_FIFOCLR (1 << 3)
-#define FUSB300_GCR_GLINTEN (1 << 2)
-#define FUSB300_GCR_DEVEN_FS 0x3
-#define FUSB300_GCR_DEVEN_HS 0x2
-#define FUSB300_GCR_DEVEN_SS 0x1
-#define FUSB300_GCR_DEVDIS 0x0
-#define FUSB300_GCR_DEVEN_MSK 0x3
-
-
-/*
- * *Global Test Mode (offset = 004H)
- * */
-#define FUSB300_GTM_TST_DIS_SOFGEN (1 << 16)
-#define FUSB300_GTM_TST_CUR_EP_ENTRY(n) ((n & 0xF) << 12)
-#define FUSB300_GTM_TST_EP_ENTRY(n) ((n & 0xF) << 8)
-#define FUSB300_GTM_TST_EP_NUM(n) ((n & 0xF) << 4)
-#define FUSB300_GTM_TST_FIFO_DEG (1 << 1)
-#define FUSB300_GTM_TSTMODE (1 << 0)
-
-/*
- * * Device Address Register (offset = 008H)
- * */
-#define FUSB300_DAR_SETCONFG (1 << 7)
-#define FUSB300_DAR_DRVADDR(x) (x & 0x7F)
-#define FUSB300_DAR_DRVADDR_MSK 0x7F
-
-/*
- * *Control Transfer Configuration and Status Register
- * (CX_Config_Status, offset = 00CH)
- * */
-#define FUSB300_CSR_LEN(x) ((x & 0xFFFF) << 8)
-#define FUSB300_CSR_LEN_MSK (0xFFFF << 8)
-#define FUSB300_CSR_EMP (1 << 4)
-#define FUSB300_CSR_FUL (1 << 3)
-#define FUSB300_CSR_CLR (1 << 2)
-#define FUSB300_CSR_STL (1 << 1)
-#define FUSB300_CSR_DONE (1 << 0)
-
-/*
- * * EPn Setting 0 (EPn_SET0, offset = 020H+(n-1)*30H, n=1~15 )
- * */
-#define FUSB300_EPSET0_STL_CLR (1 << 3)
-#define FUSB300_EPSET0_CLRSEQNUM (1 << 2)
-#define FUSB300_EPSET0_STL (1 << 0)
-
-/*
- * * EPn Setting 1 (EPn_SET1, offset = 024H+(n-1)*30H, n=1~15)
- * */
-#define FUSB300_EPSET1_START_ENTRY(x) ((x & 0xFF) << 24)
-#define FUSB300_EPSET1_START_ENTRY_MSK (0xFF << 24)
-#define FUSB300_EPSET1_FIFOENTRY(x) ((x & 0x1F) << 12)
-#define FUSB300_EPSET1_FIFOENTRY_MSK (0x1f << 12)
-#define FUSB300_EPSET1_INTERVAL(x) ((x & 0x7) << 6)
-#define FUSB300_EPSET1_BWNUM(x) ((x & 0x3) << 4)
-#define FUSB300_EPSET1_TYPEISO (1 << 2)
-#define FUSB300_EPSET1_TYPEBLK (2 << 2)
-#define FUSB300_EPSET1_TYPEINT (3 << 2)
-#define FUSB300_EPSET1_TYPE(x) ((x & 0x3) << 2)
-#define FUSB300_EPSET1_TYPE_MSK (0x3 << 2)
-#define FUSB300_EPSET1_DIROUT (0 << 1)
-#define FUSB300_EPSET1_DIRIN (1 << 1)
-#define FUSB300_EPSET1_DIR(x) ((x & 0x1) << 1)
-#define FUSB300_EPSET1_DIRIN (1 << 1)
-#define FUSB300_EPSET1_DIR_MSK ((0x1) << 1)
-#define FUSB300_EPSET1_ACTDIS 0
-#define FUSB300_EPSET1_ACTEN 1
-
-/*
- * *EPn Setting 2 (EPn_SET2, offset = 028H+(n-1)*30H, n=1~15)
- * */
-#define FUSB300_EPSET2_ADDROFS(x) ((x & 0x7FFF) << 16)
-#define FUSB300_EPSET2_ADDROFS_MSK (0x7fff << 16)
-#define FUSB300_EPSET2_MPS(x) (x & 0x7FF)
-#define FUSB300_EPSET2_MPS_MSK 0x7FF
-
-/*
- * * EPn FIFO Register (offset = 2cH+(n-1)*30H)
- * */
-#define FUSB300_FFR_RST (1 << 31)
-#define FUSB300_FF_FUL (1 << 30)
-#define FUSB300_FF_EMPTY (1 << 29)
-#define FUSB300_FFR_BYCNT 0x1FFFF
-
-/*
- * *EPn Stream ID (EPn_STR_ID, offset = 040H+(n-1)*30H, n=1~15)
- * */
-#define FUSB300_STRID_STREN (1 << 16)
-#define FUSB300_STRID_STRID(x) (x & 0xFFFF)
-
-/*
- * *HS PHY Test Mode (offset = 300H)
- * */
-#define FUSB300_HSPTM_TSTPKDONE (1 << 4)
-#define FUSB300_HSPTM_TSTPKT (1 << 3)
-#define FUSB300_HSPTM_TSTSET0NAK (1 << 2)
-#define FUSB300_HSPTM_TSTKSTA (1 << 1)
-#define FUSB300_HSPTM_TSTJSTA (1 << 0)
-
-/*
- * *HS Control Register (offset = 304H)
- * */
-#define FUSB300_HSCR_HS_LPM_PERMIT (1 << 8)
-#define FUSB300_HSCR_HS_LPM_RMWKUP (1 << 7)
-#define FUSB300_HSCR_CAP_LPM_RMWKUP (1 << 6)
-#define FUSB300_HSCR_HS_GOSUSP (1 << 5)
-#define FUSB300_HSCR_HS_GORMWKU (1 << 4)
-#define FUSB300_HSCR_CAP_RMWKUP (1 << 3)
-#define FUSB300_HSCR_IDLECNT_0MS 0
-#define FUSB300_HSCR_IDLECNT_1MS 1
-#define FUSB300_HSCR_IDLECNT_2MS 2
-#define FUSB300_HSCR_IDLECNT_3MS 3
-#define FUSB300_HSCR_IDLECNT_4MS 4
-#define FUSB300_HSCR_IDLECNT_5MS 5
-#define FUSB300_HSCR_IDLECNT_6MS 6
-#define FUSB300_HSCR_IDLECNT_7MS 7
-
-/*
- * * SS Controller Register 0 (offset = 308H)
- * */
-#define FUSB300_SSCR0_MAX_INTERVAL(x) ((x & 0x7) << 4)
-#define FUSB300_SSCR0_U2_FUN_EN (1 << 1)
-#define FUSB300_SSCR0_U1_FUN_EN (1 << 0)
-
-/*
- * * SS Controller Register 1 (offset = 30CH)
- * */
-#define FUSB300_SSCR1_GO_U3_DONE (1 << 8)
-#define FUSB300_SSCR1_TXDEEMPH_LEVEL (1 << 7)
-#define FUSB300_SSCR1_DIS_SCRMB (1 << 6)
-#define FUSB300_SSCR1_FORCE_RECOVERY (1 << 5)
-#define FUSB300_SSCR1_U3_WAKEUP_EN (1 << 4)
-#define FUSB300_SSCR1_U2_EXIT_EN (1 << 3)
-#define FUSB300_SSCR1_U1_EXIT_EN (1 << 2)
-#define FUSB300_SSCR1_U2_ENTRY_EN (1 << 1)
-#define FUSB300_SSCR1_U1_ENTRY_EN (1 << 0)
-
-/*
- * *SS Controller Register 2 (offset = 310H)
- * */
-#define FUSB300_SSCR2_SS_TX_SWING (1 << 25)
-#define FUSB300_SSCR2_FORCE_LINKPM_ACCEPT (1 << 24)
-#define FUSB300_SSCR2_U2_INACT_TIMEOUT(x) ((x & 0xFF) << 16)
-#define FUSB300_SSCR2_U1TIMEOUT(x) ((x & 0xFF) << 8)
-#define FUSB300_SSCR2_U2TIMEOUT(x) (x & 0xFF)
-
-/*
- * *SS Device Notification Control (DEV_NOTF, offset = 314H)
- * */
-#define FUSB300_DEVNOTF_CONTEXT0(x) ((x & 0xFFFFFF) << 8)
-#define FUSB300_DEVNOTF_TYPE_DIS 0
-#define FUSB300_DEVNOTF_TYPE_FUNCWAKE 1
-#define FUSB300_DEVNOTF_TYPE_LTM 2
-#define FUSB300_DEVNOTF_TYPE_BUSINT_ADJMSG 3
-
-/*
- * *BFM Arbiter Priority Register (BFM_ARB offset = 31CH)
- * */
-#define FUSB300_BFMARB_ARB_M1 (1 << 3)
-#define FUSB300_BFMARB_ARB_M0 (1 << 2)
-#define FUSB300_BFMARB_ARB_S1 (1 << 1)
-#define FUSB300_BFMARB_ARB_S0 1
-
-/*
- * *Vendor Specific IO Control Register (offset = 320H)
- * */
-#define FUSB300_VSIC_VCTLOAD_N (1 << 8)
-#define FUSB300_VSIC_VCTL(x) (x & 0x3F)
-
-/*
- * *SOF Mask Timer (offset = 324H)
- * */
-#define FUSB300_SOF_MASK_TIMER_HS 0x044c
-#define FUSB300_SOF_MASK_TIMER_FS 0x2710
-
-/*
- * *Error Flag and Control Status (offset = 328H)
- * */
-#define FUSB300_EFCS_PM_STATE_U3 3
-#define FUSB300_EFCS_PM_STATE_U2 2
-#define FUSB300_EFCS_PM_STATE_U1 1
-#define FUSB300_EFCS_PM_STATE_U0 0
-
-/*
- * *Interrupt Group 0 Register (offset = 400H)
- * */
-#define FUSB300_IGR0_EP15_PRD_INT (1 << 31)
-#define FUSB300_IGR0_EP14_PRD_INT (1 << 30)
-#define FUSB300_IGR0_EP13_PRD_INT (1 << 29)
-#define FUSB300_IGR0_EP12_PRD_INT (1 << 28)
-#define FUSB300_IGR0_EP11_PRD_INT (1 << 27)
-#define FUSB300_IGR0_EP10_PRD_INT (1 << 26)
-#define FUSB300_IGR0_EP9_PRD_INT (1 << 25)
-#define FUSB300_IGR0_EP8_PRD_INT (1 << 24)
-#define FUSB300_IGR0_EP7_PRD_INT (1 << 23)
-#define FUSB300_IGR0_EP6_PRD_INT (1 << 22)
-#define FUSB300_IGR0_EP5_PRD_INT (1 << 21)
-#define FUSB300_IGR0_EP4_PRD_INT (1 << 20)
-#define FUSB300_IGR0_EP3_PRD_INT (1 << 19)
-#define FUSB300_IGR0_EP2_PRD_INT (1 << 18)
-#define FUSB300_IGR0_EP1_PRD_INT (1 << 17)
-#define FUSB300_IGR0_EPn_PRD_INT(n) (1 << (n + 16))
-
-#define FUSB300_IGR0_EP15_FIFO_INT (1 << 15)
-#define FUSB300_IGR0_EP14_FIFO_INT (1 << 14)
-#define FUSB300_IGR0_EP13_FIFO_INT (1 << 13)
-#define FUSB300_IGR0_EP12_FIFO_INT (1 << 12)
-#define FUSB300_IGR0_EP11_FIFO_INT (1 << 11)
-#define FUSB300_IGR0_EP10_FIFO_INT (1 << 10)
-#define FUSB300_IGR0_EP9_FIFO_INT (1 << 9)
-#define FUSB300_IGR0_EP8_FIFO_INT (1 << 8)
-#define FUSB300_IGR0_EP7_FIFO_INT (1 << 7)
-#define FUSB300_IGR0_EP6_FIFO_INT (1 << 6)
-#define FUSB300_IGR0_EP5_FIFO_INT (1 << 5)
-#define FUSB300_IGR0_EP4_FIFO_INT (1 << 4)
-#define FUSB300_IGR0_EP3_FIFO_INT (1 << 3)
-#define FUSB300_IGR0_EP2_FIFO_INT (1 << 2)
-#define FUSB300_IGR0_EP1_FIFO_INT (1 << 1)
-#define FUSB300_IGR0_EPn_FIFO_INT(n) (1 << n)
-
-/*
- * *Interrupt Group 1 Register (offset = 404H)
- * */
-#define FUSB300_IGR1_INTGRP5 (1 << 31)
-#define FUSB300_IGR1_VBUS_CHG_INT (1 << 30)
-#define FUSB300_IGR1_SYNF1_EMPTY_INT (1 << 29)
-#define FUSB300_IGR1_SYNF0_EMPTY_INT (1 << 28)
-#define FUSB300_IGR1_U3_EXIT_FAIL_INT (1 << 27)
-#define FUSB300_IGR1_U2_EXIT_FAIL_INT (1 << 26)
-#define FUSB300_IGR1_U1_EXIT_FAIL_INT (1 << 25)
-#define FUSB300_IGR1_U2_ENTRY_FAIL_INT (1 << 24)
-#define FUSB300_IGR1_U1_ENTRY_FAIL_INT (1 << 23)
-#define FUSB300_IGR1_U3_EXIT_INT (1 << 22)
-#define FUSB300_IGR1_U2_EXIT_INT (1 << 21)
-#define FUSB300_IGR1_U1_EXIT_INT (1 << 20)
-#define FUSB300_IGR1_U3_ENTRY_INT (1 << 19)
-#define FUSB300_IGR1_U2_ENTRY_INT (1 << 18)
-#define FUSB300_IGR1_U1_ENTRY_INT (1 << 17)
-#define FUSB300_IGR1_HOT_RST_INT (1 << 16)
-#define FUSB300_IGR1_WARM_RST_INT (1 << 15)
-#define FUSB300_IGR1_RESM_INT (1 << 14)
-#define FUSB300_IGR1_SUSP_INT (1 << 13)
-#define FUSB300_IGR1_HS_LPM_INT (1 << 12)
-#define FUSB300_IGR1_USBRST_INT (1 << 11)
-#define FUSB300_IGR1_DEV_MODE_CHG_INT (1 << 9)
-#define FUSB300_IGR1_CX_COMABT_INT (1 << 8)
-#define FUSB300_IGR1_CX_COMFAIL_INT (1 << 7)
-#define FUSB300_IGR1_CX_CMDEND_INT (1 << 6)
-#define FUSB300_IGR1_CX_OUT_INT (1 << 5)
-#define FUSB300_IGR1_CX_IN_INT (1 << 4)
-#define FUSB300_IGR1_CX_SETUP_INT (1 << 3)
-#define FUSB300_IGR1_INTGRP4 (1 << 2)
-#define FUSB300_IGR1_INTGRP3 (1 << 1)
-#define FUSB300_IGR1_INTGRP2 (1 << 0)
-
-/*
- * *Interrupt Group 2 Register (offset = 408H)
- * */
-#define FUSB300_IGR2_EP6_STR_ACCEPT_INT (1 << 29)
-#define FUSB300_IGR2_EP6_STR_RESUME_INT (1 << 28)
-#define FUSB300_IGR2_EP6_STR_REQ_INT (1 << 27)
-#define FUSB300_IGR2_EP6_STR_NOTRDY_INT (1 << 26)
-#define FUSB300_IGR2_EP6_STR_PRIME_INT (1 << 25)
-#define FUSB300_IGR2_EP5_STR_ACCEPT_INT (1 << 24)
-#define FUSB300_IGR2_EP5_STR_RESUME_INT (1 << 23)
-#define FUSB300_IGR2_EP5_STR_REQ_INT (1 << 22)
-#define FUSB300_IGR2_EP5_STR_NOTRDY_INT (1 << 21)
-#define FUSB300_IGR2_EP5_STR_PRIME_INT (1 << 20)
-#define FUSB300_IGR2_EP4_STR_ACCEPT_INT (1 << 19)
-#define FUSB300_IGR2_EP4_STR_RESUME_INT (1 << 18)
-#define FUSB300_IGR2_EP4_STR_REQ_INT (1 << 17)
-#define FUSB300_IGR2_EP4_STR_NOTRDY_INT (1 << 16)
-#define FUSB300_IGR2_EP4_STR_PRIME_INT (1 << 15)
-#define FUSB300_IGR2_EP3_STR_ACCEPT_INT (1 << 14)
-#define FUSB300_IGR2_EP3_STR_RESUME_INT (1 << 13)
-#define FUSB300_IGR2_EP3_STR_REQ_INT (1 << 12)
-#define FUSB300_IGR2_EP3_STR_NOTRDY_INT (1 << 11)
-#define FUSB300_IGR2_EP3_STR_PRIME_INT (1 << 10)
-#define FUSB300_IGR2_EP2_STR_ACCEPT_INT (1 << 9)
-#define FUSB300_IGR2_EP2_STR_RESUME_INT (1 << 8)
-#define FUSB300_IGR2_EP2_STR_REQ_INT (1 << 7)
-#define FUSB300_IGR2_EP2_STR_NOTRDY_INT (1 << 6)
-#define FUSB300_IGR2_EP2_STR_PRIME_INT (1 << 5)
-#define FUSB300_IGR2_EP1_STR_ACCEPT_INT (1 << 4)
-#define FUSB300_IGR2_EP1_STR_RESUME_INT (1 << 3)
-#define FUSB300_IGR2_EP1_STR_REQ_INT (1 << 2)
-#define FUSB300_IGR2_EP1_STR_NOTRDY_INT (1 << 1)
-#define FUSB300_IGR2_EP1_STR_PRIME_INT (1 << 0)
-
-#define FUSB300_IGR2_EP_STR_ACCEPT_INT(n) (1 << (5 * n - 1))
-#define FUSB300_IGR2_EP_STR_RESUME_INT(n) (1 << (5 * n - 2))
-#define FUSB300_IGR2_EP_STR_REQ_INT(n) (1 << (5 * n - 3))
-#define FUSB300_IGR2_EP_STR_NOTRDY_INT(n) (1 << (5 * n - 4))
-#define FUSB300_IGR2_EP_STR_PRIME_INT(n) (1 << (5 * n - 5))
-
-/*
- * *Interrupt Group 3 Register (offset = 40CH)
- * */
-#define FUSB300_IGR3_EP12_STR_ACCEPT_INT (1 << 29)
-#define FUSB300_IGR3_EP12_STR_RESUME_INT (1 << 28)
-#define FUSB300_IGR3_EP12_STR_REQ_INT (1 << 27)
-#define FUSB300_IGR3_EP12_STR_NOTRDY_INT (1 << 26)
-#define FUSB300_IGR3_EP12_STR_PRIME_INT (1 << 25)
-#define FUSB300_IGR3_EP11_STR_ACCEPT_INT (1 << 24)
-#define FUSB300_IGR3_EP11_STR_RESUME_INT (1 << 23)
-#define FUSB300_IGR3_EP11_STR_REQ_INT (1 << 22)
-#define FUSB300_IGR3_EP11_STR_NOTRDY_INT (1 << 21)
-#define FUSB300_IGR3_EP11_STR_PRIME_INT (1 << 20)
-#define FUSB300_IGR3_EP10_STR_ACCEPT_INT (1 << 19)
-#define FUSB300_IGR3_EP10_STR_RESUME_INT (1 << 18)
-#define FUSB300_IGR3_EP10_STR_REQ_INT (1 << 17)
-#define FUSB300_IGR3_EP10_STR_NOTRDY_INT (1 << 16)
-#define FUSB300_IGR3_EP10_STR_PRIME_INT (1 << 15)
-#define FUSB300_IGR3_EP9_STR_ACCEPT_INT (1 << 14)
-#define FUSB300_IGR3_EP9_STR_RESUME_INT (1 << 13)
-#define FUSB300_IGR3_EP9_STR_REQ_INT (1 << 12)
-#define FUSB300_IGR3_EP9_STR_NOTRDY_INT (1 << 11)
-#define FUSB300_IGR3_EP9_STR_PRIME_INT (1 << 10)
-#define FUSB300_IGR3_EP8_STR_ACCEPT_INT (1 << 9)
-#define FUSB300_IGR3_EP8_STR_RESUME_INT (1 << 8)
-#define FUSB300_IGR3_EP8_STR_REQ_INT (1 << 7)
-#define FUSB300_IGR3_EP8_STR_NOTRDY_INT (1 << 6)
-#define FUSB300_IGR3_EP8_STR_PRIME_INT (1 << 5)
-#define FUSB300_IGR3_EP7_STR_ACCEPT_INT (1 << 4)
-#define FUSB300_IGR3_EP7_STR_RESUME_INT (1 << 3)
-#define FUSB300_IGR3_EP7_STR_REQ_INT (1 << 2)
-#define FUSB300_IGR3_EP7_STR_NOTRDY_INT (1 << 1)
-#define FUSB300_IGR3_EP7_STR_PRIME_INT (1 << 0)
-
-#define FUSB300_IGR3_EP_STR_ACCEPT_INT(n) (1 << (5 * (n - 6) - 1))
-#define FUSB300_IGR3_EP_STR_RESUME_INT(n) (1 << (5 * (n - 6) - 2))
-#define FUSB300_IGR3_EP_STR_REQ_INT(n) (1 << (5 * (n - 6) - 3))
-#define FUSB300_IGR3_EP_STR_NOTRDY_INT(n) (1 << (5 * (n - 6) - 4))
-#define FUSB300_IGR3_EP_STR_PRIME_INT(n) (1 << (5 * (n - 6) - 5))
-
-/*
- * *Interrupt Group 4 Register (offset = 410H)
- * */
-#define FUSB300_IGR4_EP15_RX0_INT (1 << 31)
-#define FUSB300_IGR4_EP14_RX0_INT (1 << 30)
-#define FUSB300_IGR4_EP13_RX0_INT (1 << 29)
-#define FUSB300_IGR4_EP12_RX0_INT (1 << 28)
-#define FUSB300_IGR4_EP11_RX0_INT (1 << 27)
-#define FUSB300_IGR4_EP10_RX0_INT (1 << 26)
-#define FUSB300_IGR4_EP9_RX0_INT (1 << 25)
-#define FUSB300_IGR4_EP8_RX0_INT (1 << 24)
-#define FUSB300_IGR4_EP7_RX0_INT (1 << 23)
-#define FUSB300_IGR4_EP6_RX0_INT (1 << 22)
-#define FUSB300_IGR4_EP5_RX0_INT (1 << 21)
-#define FUSB300_IGR4_EP4_RX0_INT (1 << 20)
-#define FUSB300_IGR4_EP3_RX0_INT (1 << 19)
-#define FUSB300_IGR4_EP2_RX0_INT (1 << 18)
-#define FUSB300_IGR4_EP1_RX0_INT (1 << 17)
-#define FUSB300_IGR4_EP_RX0_INT(x) (1 << (x + 16))
-#define FUSB300_IGR4_EP15_STR_ACCEPT_INT (1 << 14)
-#define FUSB300_IGR4_EP15_STR_RESUME_INT (1 << 13)
-#define FUSB300_IGR4_EP15_STR_REQ_INT (1 << 12)
-#define FUSB300_IGR4_EP15_STR_NOTRDY_INT (1 << 11)
-#define FUSB300_IGR4_EP15_STR_PRIME_INT (1 << 10)
-#define FUSB300_IGR4_EP14_STR_ACCEPT_INT (1 << 9)
-#define FUSB300_IGR4_EP14_STR_RESUME_INT (1 << 8)
-#define FUSB300_IGR4_EP14_STR_REQ_INT (1 << 7)
-#define FUSB300_IGR4_EP14_STR_NOTRDY_INT (1 << 6)
-#define FUSB300_IGR4_EP14_STR_PRIME_INT (1 << 5)
-#define FUSB300_IGR4_EP13_STR_ACCEPT_INT (1 << 4)
-#define FUSB300_IGR4_EP13_STR_RESUME_INT (1 << 3)
-#define FUSB300_IGR4_EP13_STR_REQ_INT (1 << 2)
-#define FUSB300_IGR4_EP13_STR_NOTRDY_INT (1 << 1)
-#define FUSB300_IGR4_EP13_STR_PRIME_INT (1 << 0)
-
-#define FUSB300_IGR4_EP_STR_ACCEPT_INT(n) (1 << (5 * (n - 12) - 1))
-#define FUSB300_IGR4_EP_STR_RESUME_INT(n) (1 << (5 * (n - 12) - 2))
-#define FUSB300_IGR4_EP_STR_REQ_INT(n) (1 << (5 * (n - 12) - 3))
-#define FUSB300_IGR4_EP_STR_NOTRDY_INT(n) (1 << (5 * (n - 12) - 4))
-#define FUSB300_IGR4_EP_STR_PRIME_INT(n) (1 << (5 * (n - 12) - 5))
-
-/*
- * *Interrupt Group 5 Register (offset = 414H)
- * */
-#define FUSB300_IGR5_EP_STL_INT(n) (1 << n)
-
-/*
- * *Interrupt Enable Group 0 Register (offset = 420H)
- * */
-#define FUSB300_IGER0_EEP15_PRD_INT (1 << 31)
-#define FUSB300_IGER0_EEP14_PRD_INT (1 << 30)
-#define FUSB300_IGER0_EEP13_PRD_INT (1 << 29)
-#define FUSB300_IGER0_EEP12_PRD_INT (1 << 28)
-#define FUSB300_IGER0_EEP11_PRD_INT (1 << 27)
-#define FUSB300_IGER0_EEP10_PRD_INT (1 << 26)
-#define FUSB300_IGER0_EEP9_PRD_INT (1 << 25)
-#define FUSB300_IGER0_EP8_PRD_INT (1 << 24)
-#define FUSB300_IGER0_EEP7_PRD_INT (1 << 23)
-#define FUSB300_IGER0_EEP6_PRD_INT (1 << 22)
-#define FUSB300_IGER0_EEP5_PRD_INT (1 << 21)
-#define FUSB300_IGER0_EEP4_PRD_INT (1 << 20)
-#define FUSB300_IGER0_EEP3_PRD_INT (1 << 19)
-#define FUSB300_IGER0_EEP2_PRD_INT (1 << 18)
-#define FUSB300_IGER0_EEP1_PRD_INT (1 << 17)
-#define FUSB300_IGER0_EEPn_PRD_INT(n) (1 << (n + 16))
-
-#define FUSB300_IGER0_EEP15_FIFO_INT (1 << 15)
-#define FUSB300_IGER0_EEP14_FIFO_INT (1 << 14)
-#define FUSB300_IGER0_EEP13_FIFO_INT (1 << 13)
-#define FUSB300_IGER0_EEP12_FIFO_INT (1 << 12)
-#define FUSB300_IGER0_EEP11_FIFO_INT (1 << 11)
-#define FUSB300_IGER0_EEP10_FIFO_INT (1 << 10)
-#define FUSB300_IGER0_EEP9_FIFO_INT (1 << 9)
-#define FUSB300_IGER0_EEP8_FIFO_INT (1 << 8)
-#define FUSB300_IGER0_EEP7_FIFO_INT (1 << 7)
-#define FUSB300_IGER0_EEP6_FIFO_INT (1 << 6)
-#define FUSB300_IGER0_EEP5_FIFO_INT (1 << 5)
-#define FUSB300_IGER0_EEP4_FIFO_INT (1 << 4)
-#define FUSB300_IGER0_EEP3_FIFO_INT (1 << 3)
-#define FUSB300_IGER0_EEP2_FIFO_INT (1 << 2)
-#define FUSB300_IGER0_EEP1_FIFO_INT (1 << 1)
-#define FUSB300_IGER0_EEPn_FIFO_INT(n) (1 << n)
-
-/*
- * *Interrupt Enable Group 1 Register (offset = 424H)
- * */
-#define FUSB300_IGER1_EINT_GRP5 (1 << 31)
-#define FUSB300_IGER1_VBUS_CHG_INT (1 << 30)
-#define FUSB300_IGER1_SYNF1_EMPTY_INT (1 << 29)
-#define FUSB300_IGER1_SYNF0_EMPTY_INT (1 << 28)
-#define FUSB300_IGER1_U3_EXIT_FAIL_INT (1 << 27)
-#define FUSB300_IGER1_U2_EXIT_FAIL_INT (1 << 26)
-#define FUSB300_IGER1_U1_EXIT_FAIL_INT (1 << 25)
-#define FUSB300_IGER1_U2_ENTRY_FAIL_INT (1 << 24)
-#define FUSB300_IGER1_U1_ENTRY_FAIL_INT (1 << 23)
-#define FUSB300_IGER1_U3_EXIT_INT (1 << 22)
-#define FUSB300_IGER1_U2_EXIT_INT (1 << 21)
-#define FUSB300_IGER1_U1_EXIT_INT (1 << 20)
-#define FUSB300_IGER1_U3_ENTRY_INT (1 << 19)
-#define FUSB300_IGER1_U2_ENTRY_INT (1 << 18)
-#define FUSB300_IGER1_U1_ENTRY_INT (1 << 17)
-#define FUSB300_IGER1_HOT_RST_INT (1 << 16)
-#define FUSB300_IGER1_WARM_RST_INT (1 << 15)
-#define FUSB300_IGER1_RESM_INT (1 << 14)
-#define FUSB300_IGER1_SUSP_INT (1 << 13)
-#define FUSB300_IGER1_LPM_INT (1 << 12)
-#define FUSB300_IGER1_HS_RST_INT (1 << 11)
-#define FUSB300_IGER1_EDEV_MODE_CHG_INT (1 << 9)
-#define FUSB300_IGER1_CX_COMABT_INT (1 << 8)
-#define FUSB300_IGER1_CX_COMFAIL_INT (1 << 7)
-#define FUSB300_IGER1_CX_CMDEND_INT (1 << 6)
-#define FUSB300_IGER1_CX_OUT_INT (1 << 5)
-#define FUSB300_IGER1_CX_IN_INT (1 << 4)
-#define FUSB300_IGER1_CX_SETUP_INT (1 << 3)
-#define FUSB300_IGER1_INTGRP4 (1 << 2)
-#define FUSB300_IGER1_INTGRP3 (1 << 1)
-#define FUSB300_IGER1_INTGRP2 (1 << 0)
-
-/*
- * *Interrupt Enable Group 2 Register (offset = 428H)
- * */
-#define FUSB300_IGER2_EEP_STR_ACCEPT_INT(n) (1 << (5 * n - 1))
-#define FUSB300_IGER2_EEP_STR_RESUME_INT(n) (1 << (5 * n - 2))
-#define FUSB300_IGER2_EEP_STR_REQ_INT(n) (1 << (5 * n - 3))
-#define FUSB300_IGER2_EEP_STR_NOTRDY_INT(n) (1 << (5 * n - 4))
-#define FUSB300_IGER2_EEP_STR_PRIME_INT(n) (1 << (5 * n - 5))
-
-/*
- * *Interrupt Enable Group 3 Register (offset = 42CH)
- * */
-
-#define FUSB300_IGER3_EEP_STR_ACCEPT_INT(n) (1 << (5 * (n - 6) - 1))
-#define FUSB300_IGER3_EEP_STR_RESUME_INT(n) (1 << (5 * (n - 6) - 2))
-#define FUSB300_IGER3_EEP_STR_REQ_INT(n) (1 << (5 * (n - 6) - 3))
-#define FUSB300_IGER3_EEP_STR_NOTRDY_INT(n) (1 << (5 * (n - 6) - 4))
-#define FUSB300_IGER3_EEP_STR_PRIME_INT(n) (1 << (5 * (n - 6) - 5))
-
-/*
- * *Interrupt Enable Group 4 Register (offset = 430H)
- * */
-
-#define FUSB300_IGER4_EEP_RX0_INT(n) (1 << (n + 16))
-#define FUSB300_IGER4_EEP_STR_ACCEPT_INT(n) (1 << (5 * (n - 6) - 1))
-#define FUSB300_IGER4_EEP_STR_RESUME_INT(n) (1 << (5 * (n - 6) - 2))
-#define FUSB300_IGER4_EEP_STR_REQ_INT(n) (1 << (5 * (n - 6) - 3))
-#define FUSB300_IGER4_EEP_STR_NOTRDY_INT(n) (1 << (5 * (n - 6) - 4))
-#define FUSB300_IGER4_EEP_STR_PRIME_INT(n) (1 << (5 * (n - 6) - 5))
-
-/* EP PRD Ready (EP_PRD_RDY, offset = 504H) */
-
-#define FUSB300_EPPRDR_EP15_PRD_RDY (1 << 15)
-#define FUSB300_EPPRDR_EP14_PRD_RDY (1 << 14)
-#define FUSB300_EPPRDR_EP13_PRD_RDY (1 << 13)
-#define FUSB300_EPPRDR_EP12_PRD_RDY (1 << 12)
-#define FUSB300_EPPRDR_EP11_PRD_RDY (1 << 11)
-#define FUSB300_EPPRDR_EP10_PRD_RDY (1 << 10)
-#define FUSB300_EPPRDR_EP9_PRD_RDY (1 << 9)
-#define FUSB300_EPPRDR_EP8_PRD_RDY (1 << 8)
-#define FUSB300_EPPRDR_EP7_PRD_RDY (1 << 7)
-#define FUSB300_EPPRDR_EP6_PRD_RDY (1 << 6)
-#define FUSB300_EPPRDR_EP5_PRD_RDY (1 << 5)
-#define FUSB300_EPPRDR_EP4_PRD_RDY (1 << 4)
-#define FUSB300_EPPRDR_EP3_PRD_RDY (1 << 3)
-#define FUSB300_EPPRDR_EP2_PRD_RDY (1 << 2)
-#define FUSB300_EPPRDR_EP1_PRD_RDY (1 << 1)
-#define FUSB300_EPPRDR_EP_PRD_RDY(n) (1 << n)
-
-/* AHB Bus Control Register (offset = 514H) */
-#define FUSB300_AHBBCR_S1_SPLIT_ON (1 << 17)
-#define FUSB300_AHBBCR_S0_SPLIT_ON (1 << 16)
-#define FUSB300_AHBBCR_S1_1entry (0 << 12)
-#define FUSB300_AHBBCR_S1_4entry (3 << 12)
-#define FUSB300_AHBBCR_S1_8entry (5 << 12)
-#define FUSB300_AHBBCR_S1_16entry (7 << 12)
-#define FUSB300_AHBBCR_S0_1entry (0 << 8)
-#define FUSB300_AHBBCR_S0_4entry (3 << 8)
-#define FUSB300_AHBBCR_S0_8entry (5 << 8)
-#define FUSB300_AHBBCR_S0_16entry (7 << 8)
-#define FUSB300_AHBBCR_M1_BURST_SINGLE (0 << 4)
-#define FUSB300_AHBBCR_M1_BURST_INCR (1 << 4)
-#define FUSB300_AHBBCR_M1_BURST_INCR4 (3 << 4)
-#define FUSB300_AHBBCR_M1_BURST_INCR8 (5 << 4)
-#define FUSB300_AHBBCR_M1_BURST_INCR16 (7 << 4)
-#define FUSB300_AHBBCR_M0_BURST_SINGLE 0
-#define FUSB300_AHBBCR_M0_BURST_INCR 1
-#define FUSB300_AHBBCR_M0_BURST_INCR4 3
-#define FUSB300_AHBBCR_M0_BURST_INCR8 5
-#define FUSB300_AHBBCR_M0_BURST_INCR16 7
-#define FUSB300_IGER5_EEP_STL_INT(n) (1 << n)
-
-/* WORD 0 Data Structure of PRD Table */
-#define FUSB300_EPPRD0_M (1 << 30)
-#define FUSB300_EPPRD0_O (1 << 29)
-/* The finished prd */
-#define FUSB300_EPPRD0_F (1 << 28)
-#define FUSB300_EPPRD0_I (1 << 27)
-#define FUSB300_EPPRD0_A (1 << 26)
-/* To decide HW point to first prd at next time */
-#define FUSB300_EPPRD0_L (1 << 25)
-#define FUSB300_EPPRD0_H (1 << 24)
-#define FUSB300_EPPRD0_BTC(n) (n & 0xFFFFFF)
-
-/*----------------------------------------------------------------------*/
-#define FUSB300_MAX_NUM_EP 16
-
-#define FUSB300_FIFO_ENTRY_NUM 8
-#define FUSB300_MAX_FIFO_ENTRY 8
-
-#define SS_CTL_MAX_PACKET_SIZE 0x200
-#define SS_BULK_MAX_PACKET_SIZE 0x400
-#define SS_INT_MAX_PACKET_SIZE 0x400
-#define SS_ISO_MAX_PACKET_SIZE 0x400
-
-#define HS_BULK_MAX_PACKET_SIZE 0x200
-#define HS_CTL_MAX_PACKET_SIZE 0x40
-#define HS_INT_MAX_PACKET_SIZE 0x400
-#define HS_ISO_MAX_PACKET_SIZE 0x400
-
-struct fusb300_ep_info {
- u8 epnum;
- u8 type;
- u8 interval;
- u8 dir_in;
- u16 maxpacket;
- u16 addrofs;
- u16 bw_num;
-};
-
-struct fusb300_request {
-
- struct usb_request req;
- struct list_head queue;
-};
-
-
-struct fusb300_ep {
- struct usb_ep ep;
- struct fusb300 *fusb300;
-
- struct list_head queue;
- unsigned stall:1;
- unsigned wedged:1;
- unsigned use_dma:1;
-
- unsigned char epnum;
- unsigned char type;
-};
-
-struct fusb300 {
- spinlock_t lock;
- void __iomem *reg;
-
- unsigned long irq_trigger;
-
- struct usb_gadget gadget;
- struct usb_gadget_driver *driver;
-
- struct fusb300_ep *ep[FUSB300_MAX_NUM_EP];
-
- struct usb_request *ep0_req; /* for internal request */
- __le16 ep0_data;
- u32 ep0_length; /* for internal request */
- u8 ep0_dir; /* 0/0x80 out/in */
-
- u8 fifo_entry_num; /* next start fifo entry */
- u32 addrofs; /* next fifo address offset */
- u8 reenum; /* if re-enumeration */
-};
-
-#define to_fusb300(g) (container_of((g), struct fusb300, gadget))
-
-#endif
diff --git a/drivers/usb/gadget/udc/goku_udc.c b/drivers/usb/gadget/udc/goku_udc.c
index b860c2e76449..db42a5e3e805 100644
--- a/drivers/usb/gadget/udc/goku_udc.c
+++ b/drivers/usb/gadget/udc/goku_udc.c
@@ -273,7 +273,7 @@ goku_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
if (!_ep)
return NULL;
- req = kzalloc(sizeof *req, gfp_flags);
+ req = kzalloc_obj(*req, gfp_flags);
if (!req)
return NULL;
@@ -1755,7 +1755,7 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id)
}
/* alloc, and start init */
- dev = kzalloc (sizeof *dev, GFP_KERNEL);
+ dev = kzalloc_obj(*dev);
if (!dev) {
retval = -ENOMEM;
goto err;
diff --git a/drivers/usb/gadget/udc/gr_udc.c b/drivers/usb/gadget/udc/gr_udc.c
index bf5b3c964c18..cdcbfae140e9 100644
--- a/drivers/usb/gadget/udc/gr_udc.c
+++ b/drivers/usb/gadget/udc/gr_udc.c
@@ -347,7 +347,7 @@ static struct usb_request *gr_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
{
struct gr_request *req;
- req = kzalloc(sizeof(*req), gfp_flags);
+ req = kzalloc_obj(*req, gfp_flags);
if (!req)
return NULL;
diff --git a/drivers/usb/gadget/udc/lpc32xx_udc.c b/drivers/usb/gadget/udc/lpc32xx_udc.c
index 89d6daf2bda7..044c31869cfb 100644
--- a/drivers/usb/gadget/udc/lpc32xx_udc.c
+++ b/drivers/usb/gadget/udc/lpc32xx_udc.c
@@ -1629,7 +1629,7 @@ static int lpc32xx_ep_enable(struct usb_ep *_ep,
return -ESHUTDOWN;
}
- tmp = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+ tmp = usb_endpoint_type(desc);
switch (tmp) {
case USB_ENDPOINT_XFER_CONTROL:
return -EINVAL;
@@ -1705,7 +1705,7 @@ static struct usb_request *lpc32xx_ep_alloc_request(struct usb_ep *_ep,
{
struct lpc32xx_request *req;
- req = kzalloc(sizeof(struct lpc32xx_request), gfp_flags);
+ req = kzalloc_obj(struct lpc32xx_request, gfp_flags);
if (!req)
return NULL;
@@ -3020,7 +3020,7 @@ static int lpc32xx_udc_probe(struct platform_device *pdev)
pdev->dev.dma_mask = &lpc32xx_usbd_dmamask;
retval = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
if (retval)
- return retval;
+ goto err_put_client;
udc->board = &lpc32xx_usbddata;
@@ -3038,28 +3038,32 @@ static int lpc32xx_udc_probe(struct platform_device *pdev)
/* Get IRQs */
for (i = 0; i < 4; i++) {
udc->udp_irq[i] = platform_get_irq(pdev, i);
- if (udc->udp_irq[i] < 0)
- return udc->udp_irq[i];
+ if (udc->udp_irq[i] < 0) {
+ retval = udc->udp_irq[i];
+ goto err_put_client;
+ }
}
udc->udp_baseaddr = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(udc->udp_baseaddr)) {
dev_err(udc->dev, "IO map failure\n");
- return PTR_ERR(udc->udp_baseaddr);
+ retval = PTR_ERR(udc->udp_baseaddr);
+ goto err_put_client;
}
/* Get USB device clock */
udc->usb_slv_clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(udc->usb_slv_clk)) {
dev_err(udc->dev, "failed to acquire USB device clock\n");
- return PTR_ERR(udc->usb_slv_clk);
+ retval = PTR_ERR(udc->usb_slv_clk);
+ goto err_put_client;
}
/* Enable USB device clock */
retval = clk_prepare_enable(udc->usb_slv_clk);
if (retval < 0) {
dev_err(udc->dev, "failed to start USB device clock\n");
- return retval;
+ goto err_put_client;
}
/* Setup deferred workqueue data */
@@ -3080,7 +3084,7 @@ static int lpc32xx_udc_probe(struct platform_device *pdev)
if (!udc->udca_v_base) {
dev_err(udc->dev, "error getting UDCA region\n");
retval = -ENOMEM;
- goto i2c_fail;
+ goto err_disable_clk;
}
udc->udca_p_base = dma_handle;
dev_dbg(udc->dev, "DMA buffer(0x%x bytes), P:0x%08x, V:0x%p\n",
@@ -3093,7 +3097,7 @@ static int lpc32xx_udc_probe(struct platform_device *pdev)
if (!udc->dd_cache) {
dev_err(udc->dev, "error getting DD DMA region\n");
retval = -ENOMEM;
- goto dma_alloc_fail;
+ goto err_free_dma;
}
/* Clear USB peripheral and initialize gadget endpoints */
@@ -3107,14 +3111,14 @@ static int lpc32xx_udc_probe(struct platform_device *pdev)
if (retval < 0) {
dev_err(udc->dev, "LP request irq %d failed\n",
udc->udp_irq[IRQ_USB_LP]);
- goto irq_req_fail;
+ goto err_destroy_pool;
}
retval = devm_request_irq(dev, udc->udp_irq[IRQ_USB_HP],
lpc32xx_usb_hp_irq, 0, "udc_hp", udc);
if (retval < 0) {
dev_err(udc->dev, "HP request irq %d failed\n",
udc->udp_irq[IRQ_USB_HP]);
- goto irq_req_fail;
+ goto err_destroy_pool;
}
retval = devm_request_irq(dev, udc->udp_irq[IRQ_USB_DEVDMA],
@@ -3122,7 +3126,7 @@ static int lpc32xx_udc_probe(struct platform_device *pdev)
if (retval < 0) {
dev_err(udc->dev, "DEV request irq %d failed\n",
udc->udp_irq[IRQ_USB_DEVDMA]);
- goto irq_req_fail;
+ goto err_destroy_pool;
}
/* The transceiver interrupt is used for VBUS detection and will
@@ -3133,7 +3137,7 @@ static int lpc32xx_udc_probe(struct platform_device *pdev)
if (retval < 0) {
dev_err(udc->dev, "VBUS request irq %d failed\n",
udc->udp_irq[IRQ_USB_ATX]);
- goto irq_req_fail;
+ goto err_destroy_pool;
}
/* Initialize wait queue */
@@ -3142,7 +3146,7 @@ static int lpc32xx_udc_probe(struct platform_device *pdev)
retval = usb_add_gadget_udc(dev, &udc->gadget);
if (retval < 0)
- goto add_gadget_fail;
+ goto err_destroy_pool;
dev_set_drvdata(dev, udc);
device_init_wakeup(dev, 1);
@@ -3154,14 +3158,16 @@ static int lpc32xx_udc_probe(struct platform_device *pdev)
dev_info(udc->dev, "%s version %s\n", driver_name, DRIVER_VERSION);
return 0;
-add_gadget_fail:
-irq_req_fail:
+err_destroy_pool:
dma_pool_destroy(udc->dd_cache);
-dma_alloc_fail:
+err_free_dma:
dma_free_coherent(&pdev->dev, UDCA_BUFF_SIZE,
udc->udca_v_base, udc->udca_p_base);
-i2c_fail:
+err_disable_clk:
clk_disable_unprepare(udc->usb_slv_clk);
+err_put_client:
+ put_device(&udc->isp1301_i2c_client->dev);
+
dev_err(udc->dev, "%s probe failed, %d\n", driver_name, retval);
return retval;
@@ -3190,6 +3196,8 @@ static void lpc32xx_udc_remove(struct platform_device *pdev)
udc->udca_v_base, udc->udca_p_base);
clk_disable_unprepare(udc->usb_slv_clk);
+
+ put_device(&udc->isp1301_i2c_client->dev);
}
#ifdef CONFIG_PM
diff --git a/drivers/usb/gadget/udc/m66592-udc.c b/drivers/usb/gadget/udc/m66592-udc.c
index a938b2af0944..d77c11c4eb38 100644
--- a/drivers/usb/gadget/udc/m66592-udc.c
+++ b/drivers/usb/gadget/udc/m66592-udc.c
@@ -359,7 +359,7 @@ static void m66592_ep_setting(struct m66592 *m66592, struct m66592_ep *ep,
ep->pipenum = pipenum;
ep->ep.maxpacket = usb_endpoint_maxp(desc);
m66592->pipenum2ep[pipenum] = ep;
- m66592->epaddr2ep[desc->bEndpointAddress&USB_ENDPOINT_NUMBER_MASK] = ep;
+ m66592->epaddr2ep[usb_endpoint_num(desc)] = ep;
INIT_LIST_HEAD(&ep->queue);
}
@@ -391,7 +391,7 @@ static int alloc_pipe_config(struct m66592_ep *ep,
BUG_ON(ep->pipenum);
- switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
+ switch (usb_endpoint_type(desc)) {
case USB_ENDPOINT_XFER_BULK:
if (m66592->bulk >= M66592_MAX_NUM_BULK) {
if (m66592->isochronous >= M66592_MAX_NUM_ISOC) {
@@ -433,7 +433,7 @@ static int alloc_pipe_config(struct m66592_ep *ep,
}
ep->type = info.type;
- info.epnum = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+ info.epnum = usb_endpoint_num(desc);
info.maxpacket = usb_endpoint_maxp(desc);
info.interval = desc->bInterval;
if (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
@@ -1261,7 +1261,7 @@ static irqreturn_t m66592_irq(int irq, void *_m66592)
static void m66592_timer(struct timer_list *t)
{
- struct m66592 *m66592 = from_timer(m66592, t, timer);
+ struct m66592 *m66592 = timer_container_of(m66592, t, timer);
unsigned long flags;
u16 tmp;
@@ -1330,7 +1330,7 @@ static struct usb_request *m66592_alloc_request(struct usb_ep *_ep,
{
struct m66592_request *req;
- req = kzalloc(sizeof(struct m66592_request), gfp_flags);
+ req = kzalloc_obj(struct m66592_request, gfp_flags);
if (!req)
return NULL;
@@ -1571,7 +1571,7 @@ static int m66592_probe(struct platform_device *pdev)
}
/* initialize ucd */
- m66592 = kzalloc(sizeof(struct m66592), GFP_KERNEL);
+ m66592 = kzalloc_obj(struct m66592);
if (m66592 == NULL) {
ret = -ENOMEM;
goto clean_up;
diff --git a/drivers/usb/gadget/udc/max3420_udc.c b/drivers/usb/gadget/udc/max3420_udc.c
index 7349ea774adf..11fd61cba5af 100644
--- a/drivers/usb/gadget/udc/max3420_udc.c
+++ b/drivers/usb/gadget/udc/max3420_udc.c
@@ -1007,7 +1007,7 @@ static struct usb_request *max3420_alloc_request(struct usb_ep *_ep,
struct max3420_ep *ep = to_max3420_ep(_ep);
struct max3420_req *req;
- req = kzalloc(sizeof(*req), gfp_flags);
+ req = kzalloc_obj(*req, gfp_flags);
if (!req)
return NULL;
diff --git a/drivers/usb/gadget/udc/mv_u3d.h b/drivers/usb/gadget/udc/mv_u3d.h
deleted file mode 100644
index 66b84f792f64..000000000000
--- a/drivers/usb/gadget/udc/mv_u3d.h
+++ /dev/null
@@ -1,317 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Copyright (C) 2011 Marvell International Ltd. All rights reserved.
- */
-
-#ifndef __MV_U3D_H
-#define __MV_U3D_H
-
-#define MV_U3D_EP_CONTEXT_ALIGNMENT 32
-#define MV_U3D_TRB_ALIGNMENT 16
-#define MV_U3D_DMA_BOUNDARY 4096
-#define MV_U3D_EP0_MAX_PKT_SIZE 512
-
-/* ep0 transfer state */
-#define MV_U3D_WAIT_FOR_SETUP 0
-#define MV_U3D_DATA_STATE_XMIT 1
-#define MV_U3D_DATA_STATE_NEED_ZLP 2
-#define MV_U3D_WAIT_FOR_OUT_STATUS 3
-#define MV_U3D_DATA_STATE_RECV 4
-#define MV_U3D_STATUS_STAGE 5
-
-#define MV_U3D_EP_MAX_LENGTH_TRANSFER 0x10000
-
-/* USB3 Interrupt Status */
-#define MV_U3D_USBINT_SETUP 0x00000001
-#define MV_U3D_USBINT_RX_COMPLETE 0x00000002
-#define MV_U3D_USBINT_TX_COMPLETE 0x00000004
-#define MV_U3D_USBINT_UNDER_RUN 0x00000008
-#define MV_U3D_USBINT_RXDESC_ERR 0x00000010
-#define MV_U3D_USBINT_TXDESC_ERR 0x00000020
-#define MV_U3D_USBINT_RX_TRB_COMPLETE 0x00000040
-#define MV_U3D_USBINT_TX_TRB_COMPLETE 0x00000080
-#define MV_U3D_USBINT_VBUS_VALID 0x00010000
-#define MV_U3D_USBINT_STORAGE_CMD_FULL 0x00020000
-#define MV_U3D_USBINT_LINK_CHG 0x01000000
-
-/* USB3 Interrupt Enable */
-#define MV_U3D_INTR_ENABLE_SETUP 0x00000001
-#define MV_U3D_INTR_ENABLE_RX_COMPLETE 0x00000002
-#define MV_U3D_INTR_ENABLE_TX_COMPLETE 0x00000004
-#define MV_U3D_INTR_ENABLE_UNDER_RUN 0x00000008
-#define MV_U3D_INTR_ENABLE_RXDESC_ERR 0x00000010
-#define MV_U3D_INTR_ENABLE_TXDESC_ERR 0x00000020
-#define MV_U3D_INTR_ENABLE_RX_TRB_COMPLETE 0x00000040
-#define MV_U3D_INTR_ENABLE_TX_TRB_COMPLETE 0x00000080
-#define MV_U3D_INTR_ENABLE_RX_BUFFER_ERR 0x00000100
-#define MV_U3D_INTR_ENABLE_VBUS_VALID 0x00010000
-#define MV_U3D_INTR_ENABLE_STORAGE_CMD_FULL 0x00020000
-#define MV_U3D_INTR_ENABLE_LINK_CHG 0x01000000
-#define MV_U3D_INTR_ENABLE_PRIME_STATUS 0x02000000
-
-/* USB3 Link Change */
-#define MV_U3D_LINK_CHANGE_LINK_UP 0x00000001
-#define MV_U3D_LINK_CHANGE_SUSPEND 0x00000002
-#define MV_U3D_LINK_CHANGE_RESUME 0x00000004
-#define MV_U3D_LINK_CHANGE_WRESET 0x00000008
-#define MV_U3D_LINK_CHANGE_HRESET 0x00000010
-#define MV_U3D_LINK_CHANGE_VBUS_INVALID 0x00000020
-#define MV_U3D_LINK_CHANGE_INACT 0x00000040
-#define MV_U3D_LINK_CHANGE_DISABLE_AFTER_U0 0x00000080
-#define MV_U3D_LINK_CHANGE_U1 0x00000100
-#define MV_U3D_LINK_CHANGE_U2 0x00000200
-#define MV_U3D_LINK_CHANGE_U3 0x00000400
-
-/* bridge setting */
-#define MV_U3D_BRIDGE_SETTING_VBUS_VALID (1 << 16)
-
-/* Command Register Bit Masks */
-#define MV_U3D_CMD_RUN_STOP 0x00000001
-#define MV_U3D_CMD_CTRL_RESET 0x00000002
-
-/* ep control register */
-#define MV_U3D_EPXCR_EP_TYPE_CONTROL 0
-#define MV_U3D_EPXCR_EP_TYPE_ISOC 1
-#define MV_U3D_EPXCR_EP_TYPE_BULK 2
-#define MV_U3D_EPXCR_EP_TYPE_INT 3
-#define MV_U3D_EPXCR_EP_ENABLE_SHIFT 4
-#define MV_U3D_EPXCR_MAX_BURST_SIZE_SHIFT 12
-#define MV_U3D_EPXCR_MAX_PACKET_SIZE_SHIFT 16
-#define MV_U3D_USB_BULK_BURST_OUT 6
-#define MV_U3D_USB_BULK_BURST_IN 14
-
-#define MV_U3D_EPXCR_EP_FLUSH (1 << 7)
-#define MV_U3D_EPXCR_EP_HALT (1 << 1)
-#define MV_U3D_EPXCR_EP_INIT (1)
-
-/* TX/RX Status Register */
-#define MV_U3D_XFERSTATUS_COMPLETE_SHIFT 24
-#define MV_U3D_COMPLETE_INVALID 0
-#define MV_U3D_COMPLETE_SUCCESS 1
-#define MV_U3D_COMPLETE_BUFF_ERR 2
-#define MV_U3D_COMPLETE_SHORT_PACKET 3
-#define MV_U3D_COMPLETE_TRB_ERR 5
-#define MV_U3D_XFERSTATUS_TRB_LENGTH_MASK (0xFFFFFF)
-
-#define MV_U3D_USB_LINK_BYPASS_VBUS 0x8
-
-#define MV_U3D_LTSSM_PHY_INIT_DONE 0x80000000
-#define MV_U3D_LTSSM_NEVER_GO_COMPLIANCE 0x40000000
-
-#define MV_U3D_USB3_OP_REGS_OFFSET 0x100
-#define MV_U3D_USB3_PHY_OFFSET 0xB800
-
-#define DCS_ENABLE 0x1
-
-/* timeout */
-#define MV_U3D_RESET_TIMEOUT 10000
-#define MV_U3D_FLUSH_TIMEOUT 100000
-#define MV_U3D_OWN_TIMEOUT 10000
-#define LOOPS_USEC_SHIFT 4
-#define LOOPS_USEC (1 << LOOPS_USEC_SHIFT)
-#define LOOPS(timeout) ((timeout) >> LOOPS_USEC_SHIFT)
-
-/* ep direction */
-#define MV_U3D_EP_DIR_IN 1
-#define MV_U3D_EP_DIR_OUT 0
-#define mv_u3d_ep_dir(ep) (((ep)->ep_num == 0) ? \
- ((ep)->u3d->ep0_dir) : ((ep)->direction))
-
-/* usb capability registers */
-struct mv_u3d_cap_regs {
- u32 rsvd[5];
- u32 dboff; /* doorbell register offset */
- u32 rtsoff; /* runtime register offset */
- u32 vuoff; /* vendor unique register offset */
-};
-
-/* operation registers */
-struct mv_u3d_op_regs {
- u32 usbcmd; /* Command register */
- u32 rsvd1[11];
- u32 dcbaapl; /* Device Context Base Address low register */
- u32 dcbaaph; /* Device Context Base Address high register */
- u32 rsvd2[243];
- u32 portsc; /* port status and control register*/
- u32 portlinkinfo; /* port link info register*/
- u32 rsvd3[9917];
- u32 doorbell; /* doorbell register */
-};
-
-/* control endpoint enable registers */
-struct epxcr {
- u32 epxoutcr0; /* ep out control 0 register */
- u32 epxoutcr1; /* ep out control 1 register */
- u32 epxincr0; /* ep in control 0 register */
- u32 epxincr1; /* ep in control 1 register */
-};
-
-/* transfer status registers */
-struct xferstatus {
- u32 curdeqlo; /* current TRB pointer low */
- u32 curdeqhi; /* current TRB pointer high */
- u32 statuslo; /* transfer status low */
- u32 statushi; /* transfer status high */
-};
-
-/* vendor unique control registers */
-struct mv_u3d_vuc_regs {
- u32 ctrlepenable; /* control endpoint enable register */
- u32 setuplock; /* setup lock register */
- u32 endcomplete; /* endpoint transfer complete register */
- u32 intrcause; /* interrupt cause register */
- u32 intrenable; /* interrupt enable register */
- u32 trbcomplete; /* TRB complete register */
- u32 linkchange; /* link change register */
- u32 rsvd1[5];
- u32 trbunderrun; /* TRB underrun register */
- u32 rsvd2[43];
- u32 bridgesetting; /* bridge setting register */
- u32 rsvd3[7];
- struct xferstatus txst[16]; /* TX status register */
- struct xferstatus rxst[16]; /* RX status register */
- u32 ltssm; /* LTSSM control register */
- u32 pipe; /* PIPE control register */
- u32 linkcr0; /* link control 0 register */
- u32 linkcr1; /* link control 1 register */
- u32 rsvd6[60];
- u32 mib0; /* MIB0 counter register */
- u32 usblink; /* usb link control register */
- u32 ltssmstate; /* LTSSM state register */
- u32 linkerrorcause; /* link error cause register */
- u32 rsvd7[60];
- u32 devaddrtiebrkr; /* device address and tie breaker */
- u32 itpinfo0; /* ITP info 0 register */
- u32 itpinfo1; /* ITP info 1 register */
- u32 rsvd8[61];
- struct epxcr epcr[16]; /* ep control register */
- u32 rsvd9[64];
- u32 phyaddr; /* PHY address register */
- u32 phydata; /* PHY data register */
-};
-
-/* Endpoint context structure */
-struct mv_u3d_ep_context {
- u32 rsvd0;
- u32 rsvd1;
- u32 trb_addr_lo; /* TRB address low 32 bit */
- u32 trb_addr_hi; /* TRB address high 32 bit */
- u32 rsvd2;
- u32 rsvd3;
- struct usb_ctrlrequest setup_buffer; /* setup data buffer */
-};
-
-/* TRB control data structure */
-struct mv_u3d_trb_ctrl {
- u32 own:1; /* owner of TRB */
- u32 rsvd1:3;
- u32 chain:1; /* associate this TRB with the
- next TRB on the Ring */
- u32 ioc:1; /* interrupt on complete */
- u32 rsvd2:4;
- u32 type:6; /* TRB type */
-#define TYPE_NORMAL 1
-#define TYPE_DATA 3
-#define TYPE_LINK 6
- u32 dir:1; /* Working at data stage of control endpoint
- operation. 0 is OUT and 1 is IN. */
- u32 rsvd3:15;
-};
-
-/* TRB data structure
- * For multiple TRB, all the TRBs' physical address should be continuous.
- */
-struct mv_u3d_trb_hw {
- u32 buf_addr_lo; /* data buffer address low 32 bit */
- u32 buf_addr_hi; /* data buffer address high 32 bit */
- u32 trb_len; /* transfer length */
- struct mv_u3d_trb_ctrl ctrl; /* TRB control data */
-};
-
-/* TRB structure */
-struct mv_u3d_trb {
- struct mv_u3d_trb_hw *trb_hw; /* point to the trb_hw structure */
- dma_addr_t trb_dma; /* dma address for this trb_hw */
- struct list_head trb_list; /* trb list */
-};
-
-/* device data structure */
-struct mv_u3d {
- struct usb_gadget gadget;
- struct usb_gadget_driver *driver;
- spinlock_t lock; /* device lock */
- struct completion *done;
- struct device *dev;
- int irq;
-
- /* usb controller registers */
- struct mv_u3d_cap_regs __iomem *cap_regs;
- struct mv_u3d_op_regs __iomem *op_regs;
- struct mv_u3d_vuc_regs __iomem *vuc_regs;
- void __iomem *phy_regs;
-
- unsigned int max_eps;
- struct mv_u3d_ep_context *ep_context;
- size_t ep_context_size;
- dma_addr_t ep_context_dma;
-
- struct dma_pool *trb_pool; /* for TRB data structure */
- struct mv_u3d_ep *eps;
-
- struct mv_u3d_req *status_req; /* ep0 status request */
- struct usb_ctrlrequest local_setup_buff; /* store setup data*/
-
- unsigned int resume_state; /* USB state to resume */
- unsigned int usb_state; /* USB current state */
- unsigned int ep0_state; /* Endpoint zero state */
- unsigned int ep0_dir;
-
- unsigned int dev_addr; /* device address */
-
- unsigned int errors;
-
- unsigned softconnect:1;
- unsigned vbus_active:1; /* vbus is active or not */
- unsigned remote_wakeup:1; /* support remote wakeup */
- unsigned clock_gating:1; /* clock gating or not */
- unsigned active:1; /* udc is active or not */
- unsigned vbus_valid_detect:1; /* udc vbus detection */
-
- struct mv_usb_addon_irq *vbus;
- unsigned int power;
-
- struct clk *clk;
-};
-
-/* endpoint data structure */
-struct mv_u3d_ep {
- struct usb_ep ep;
- struct mv_u3d *u3d;
- struct list_head queue; /* ep request queued hardware */
- struct list_head req_list; /* list of ep request */
- struct mv_u3d_ep_context *ep_context; /* ep context */
- u32 direction;
- char name[14];
- u32 processing; /* there is ep request
- queued on haredware */
- spinlock_t req_lock; /* ep lock */
- unsigned wedge:1;
- unsigned enabled:1;
- unsigned ep_type:2;
- unsigned ep_num:8;
-};
-
-/* request data structure */
-struct mv_u3d_req {
- struct usb_request req;
- struct mv_u3d_ep *ep;
- struct list_head queue; /* ep requst queued on hardware */
- struct list_head list; /* ep request list */
- struct list_head trb_list; /* trb list of a request */
-
- struct mv_u3d_trb *trb_head; /* point to first trb of a request */
- unsigned trb_count; /* TRB number in the chain */
- unsigned chain; /* TRB chain or not */
-};
-
-#endif
diff --git a/drivers/usb/gadget/udc/mv_u3d_core.c b/drivers/usb/gadget/udc/mv_u3d_core.c
deleted file mode 100644
index 062f43e146aa..000000000000
--- a/drivers/usb/gadget/udc/mv_u3d_core.c
+++ /dev/null
@@ -1,2062 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Copyright (C) 2011 Marvell International Ltd. All rights reserved.
- */
-
-#include <linux/module.h>
-#include <linux/dma-mapping.h>
-#include <linux/dmapool.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/ioport.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/errno.h>
-#include <linux/timer.h>
-#include <linux/list.h>
-#include <linux/notifier.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/pm.h>
-#include <linux/io.h>
-#include <linux/irq.h>
-#include <linux/platform_device.h>
-#include <linux/platform_data/mv_usb.h>
-#include <linux/clk.h>
-
-#include "mv_u3d.h"
-
-#define DRIVER_DESC "Marvell PXA USB3.0 Device Controller driver"
-
-static const char driver_name[] = "mv_u3d";
-
-static void mv_u3d_nuke(struct mv_u3d_ep *ep, int status);
-static void mv_u3d_stop_activity(struct mv_u3d *u3d,
- struct usb_gadget_driver *driver);
-
-/* for endpoint 0 operations */
-static const struct usb_endpoint_descriptor mv_u3d_ep0_desc = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
- .bEndpointAddress = 0,
- .bmAttributes = USB_ENDPOINT_XFER_CONTROL,
- .wMaxPacketSize = MV_U3D_EP0_MAX_PKT_SIZE,
-};
-
-static void mv_u3d_ep0_reset(struct mv_u3d *u3d)
-{
- struct mv_u3d_ep *ep;
- u32 epxcr;
- int i;
-
- for (i = 0; i < 2; i++) {
- ep = &u3d->eps[i];
- ep->u3d = u3d;
-
- /* ep0 ep context, ep0 in and out share the same ep context */
- ep->ep_context = &u3d->ep_context[1];
- }
-
- /* reset ep state machine */
- /* reset ep0 out */
- epxcr = ioread32(&u3d->vuc_regs->epcr[0].epxoutcr0);
- epxcr |= MV_U3D_EPXCR_EP_INIT;
- iowrite32(epxcr, &u3d->vuc_regs->epcr[0].epxoutcr0);
- udelay(5);
- epxcr &= ~MV_U3D_EPXCR_EP_INIT;
- iowrite32(epxcr, &u3d->vuc_regs->epcr[0].epxoutcr0);
-
- epxcr = ((MV_U3D_EP0_MAX_PKT_SIZE
- << MV_U3D_EPXCR_MAX_PACKET_SIZE_SHIFT)
- | (1 << MV_U3D_EPXCR_MAX_BURST_SIZE_SHIFT)
- | (1 << MV_U3D_EPXCR_EP_ENABLE_SHIFT)
- | MV_U3D_EPXCR_EP_TYPE_CONTROL);
- iowrite32(epxcr, &u3d->vuc_regs->epcr[0].epxoutcr1);
-
- /* reset ep0 in */
- epxcr = ioread32(&u3d->vuc_regs->epcr[0].epxincr0);
- epxcr |= MV_U3D_EPXCR_EP_INIT;
- iowrite32(epxcr, &u3d->vuc_regs->epcr[0].epxincr0);
- udelay(5);
- epxcr &= ~MV_U3D_EPXCR_EP_INIT;
- iowrite32(epxcr, &u3d->vuc_regs->epcr[0].epxincr0);
-
- epxcr = ((MV_U3D_EP0_MAX_PKT_SIZE
- << MV_U3D_EPXCR_MAX_PACKET_SIZE_SHIFT)
- | (1 << MV_U3D_EPXCR_MAX_BURST_SIZE_SHIFT)
- | (1 << MV_U3D_EPXCR_EP_ENABLE_SHIFT)
- | MV_U3D_EPXCR_EP_TYPE_CONTROL);
- iowrite32(epxcr, &u3d->vuc_regs->epcr[0].epxincr1);
-}
-
-static void mv_u3d_ep0_stall(struct mv_u3d *u3d)
-{
- u32 tmp;
- dev_dbg(u3d->dev, "%s\n", __func__);
-
- /* set TX and RX to stall */
- tmp = ioread32(&u3d->vuc_regs->epcr[0].epxoutcr0);
- tmp |= MV_U3D_EPXCR_EP_HALT;
- iowrite32(tmp, &u3d->vuc_regs->epcr[0].epxoutcr0);
-
- tmp = ioread32(&u3d->vuc_regs->epcr[0].epxincr0);
- tmp |= MV_U3D_EPXCR_EP_HALT;
- iowrite32(tmp, &u3d->vuc_regs->epcr[0].epxincr0);
-
- /* update ep0 state */
- u3d->ep0_state = MV_U3D_WAIT_FOR_SETUP;
- u3d->ep0_dir = MV_U3D_EP_DIR_OUT;
-}
-
-static int mv_u3d_process_ep_req(struct mv_u3d *u3d, int index,
- struct mv_u3d_req *curr_req)
-{
- struct mv_u3d_trb *curr_trb;
- int actual, remaining_length = 0;
- int direction, ep_num;
- int retval = 0;
- u32 tmp, status, length;
-
- direction = index % 2;
- ep_num = index / 2;
-
- actual = curr_req->req.length;
-
- while (!list_empty(&curr_req->trb_list)) {
- curr_trb = list_entry(curr_req->trb_list.next,
- struct mv_u3d_trb, trb_list);
- if (!curr_trb->trb_hw->ctrl.own) {
- dev_err(u3d->dev, "%s, TRB own error!\n",
- u3d->eps[index].name);
- return 1;
- }
-
- curr_trb->trb_hw->ctrl.own = 0;
- if (direction == MV_U3D_EP_DIR_OUT)
- tmp = ioread32(&u3d->vuc_regs->rxst[ep_num].statuslo);
- else
- tmp = ioread32(&u3d->vuc_regs->txst[ep_num].statuslo);
-
- status = tmp >> MV_U3D_XFERSTATUS_COMPLETE_SHIFT;
- length = tmp & MV_U3D_XFERSTATUS_TRB_LENGTH_MASK;
-
- if (status == MV_U3D_COMPLETE_SUCCESS ||
- (status == MV_U3D_COMPLETE_SHORT_PACKET &&
- direction == MV_U3D_EP_DIR_OUT)) {
- remaining_length += length;
- actual -= remaining_length;
- } else {
- dev_err(u3d->dev,
- "complete_tr error: ep=%d %s: error = 0x%x\n",
- index >> 1, direction ? "SEND" : "RECV",
- status);
- retval = -EPROTO;
- }
-
- list_del_init(&curr_trb->trb_list);
- }
- if (retval)
- return retval;
-
- curr_req->req.actual = actual;
- return 0;
-}
-
-/*
- * mv_u3d_done() - retire a request; caller blocked irqs
- * @status : request status to be set, only works when
- * request is still in progress.
- */
-static
-void mv_u3d_done(struct mv_u3d_ep *ep, struct mv_u3d_req *req, int status)
- __releases(&ep->udc->lock)
- __acquires(&ep->udc->lock)
-{
- struct mv_u3d *u3d = (struct mv_u3d *)ep->u3d;
-
- dev_dbg(u3d->dev, "mv_u3d_done: remove req->queue\n");
- /* Removed the req from ep queue */
- list_del_init(&req->queue);
-
- /* req.status should be set as -EINPROGRESS in ep_queue() */
- if (req->req.status == -EINPROGRESS)
- req->req.status = status;
- else
- status = req->req.status;
-
- /* Free trb for the request */
- if (!req->chain)
- dma_pool_free(u3d->trb_pool,
- req->trb_head->trb_hw, req->trb_head->trb_dma);
- else {
- dma_unmap_single(ep->u3d->gadget.dev.parent,
- (dma_addr_t)req->trb_head->trb_dma,
- req->trb_count * sizeof(struct mv_u3d_trb_hw),
- DMA_BIDIRECTIONAL);
- kfree(req->trb_head->trb_hw);
- }
- kfree(req->trb_head);
-
- usb_gadget_unmap_request(&u3d->gadget, &req->req, mv_u3d_ep_dir(ep));
-
- if (status && (status != -ESHUTDOWN)) {
- dev_dbg(u3d->dev, "complete %s req %p stat %d len %u/%u",
- ep->ep.name, &req->req, status,
- req->req.actual, req->req.length);
- }
-
- spin_unlock(&ep->u3d->lock);
-
- usb_gadget_giveback_request(&ep->ep, &req->req);
-
- spin_lock(&ep->u3d->lock);
-}
-
-static int mv_u3d_queue_trb(struct mv_u3d_ep *ep, struct mv_u3d_req *req)
-{
- u32 tmp, direction;
- struct mv_u3d *u3d;
- struct mv_u3d_ep_context *ep_context;
- int retval = 0;
-
- u3d = ep->u3d;
- direction = mv_u3d_ep_dir(ep);
-
- /* ep0 in and out share the same ep context slot 1*/
- if (ep->ep_num == 0)
- ep_context = &(u3d->ep_context[1]);
- else
- ep_context = &(u3d->ep_context[ep->ep_num * 2 + direction]);
-
- /* check if the pipe is empty or not */
- if (!list_empty(&ep->queue)) {
- dev_err(u3d->dev, "add trb to non-empty queue!\n");
- retval = -ENOMEM;
- WARN_ON(1);
- } else {
- ep_context->rsvd0 = cpu_to_le32(1);
- ep_context->rsvd1 = 0;
-
- /* Configure the trb address and set the DCS bit.
- * Both DCS bit and own bit in trb should be set.
- */
- ep_context->trb_addr_lo =
- cpu_to_le32(req->trb_head->trb_dma | DCS_ENABLE);
- ep_context->trb_addr_hi = 0;
-
- /* Ensure that updates to the EP Context will
- * occure before Ring Bell.
- */
- wmb();
-
- /* ring bell the ep */
- if (ep->ep_num == 0)
- tmp = 0x1;
- else
- tmp = ep->ep_num * 2
- + ((direction == MV_U3D_EP_DIR_OUT) ? 0 : 1);
-
- iowrite32(tmp, &u3d->op_regs->doorbell);
- }
- return retval;
-}
-
-static struct mv_u3d_trb *mv_u3d_build_trb_one(struct mv_u3d_req *req,
- unsigned *length, dma_addr_t *dma)
-{
- u32 temp;
- unsigned int direction;
- struct mv_u3d_trb *trb;
- struct mv_u3d_trb_hw *trb_hw;
- struct mv_u3d *u3d;
-
- /* how big will this transfer be? */
- *length = req->req.length - req->req.actual;
- BUG_ON(*length > (unsigned)MV_U3D_EP_MAX_LENGTH_TRANSFER);
-
- u3d = req->ep->u3d;
-
- trb = kzalloc(sizeof(*trb), GFP_ATOMIC);
- if (!trb)
- return NULL;
-
- /*
- * Be careful that no _GFP_HIGHMEM is set,
- * or we can not use dma_to_virt
- * cannot use GFP_KERNEL in spin lock
- */
- trb_hw = dma_pool_alloc(u3d->trb_pool, GFP_ATOMIC, dma);
- if (!trb_hw) {
- kfree(trb);
- dev_err(u3d->dev,
- "%s, dma_pool_alloc fail\n", __func__);
- return NULL;
- }
- trb->trb_dma = *dma;
- trb->trb_hw = trb_hw;
-
- /* initialize buffer page pointers */
- temp = (u32)(req->req.dma + req->req.actual);
-
- trb_hw->buf_addr_lo = cpu_to_le32(temp);
- trb_hw->buf_addr_hi = 0;
- trb_hw->trb_len = cpu_to_le32(*length);
- trb_hw->ctrl.own = 1;
-
- if (req->ep->ep_num == 0)
- trb_hw->ctrl.type = TYPE_DATA;
- else
- trb_hw->ctrl.type = TYPE_NORMAL;
-
- req->req.actual += *length;
-
- direction = mv_u3d_ep_dir(req->ep);
- if (direction == MV_U3D_EP_DIR_IN)
- trb_hw->ctrl.dir = 1;
- else
- trb_hw->ctrl.dir = 0;
-
- /* Enable interrupt for the last trb of a request */
- if (!req->req.no_interrupt)
- trb_hw->ctrl.ioc = 1;
-
- trb_hw->ctrl.chain = 0;
-
- wmb();
- return trb;
-}
-
-static int mv_u3d_build_trb_chain(struct mv_u3d_req *req, unsigned *length,
- struct mv_u3d_trb *trb, int *is_last)
-{
- u32 temp;
- unsigned int direction;
- struct mv_u3d *u3d;
-
- /* how big will this transfer be? */
- *length = min(req->req.length - req->req.actual,
- (unsigned)MV_U3D_EP_MAX_LENGTH_TRANSFER);
-
- u3d = req->ep->u3d;
-
- trb->trb_dma = 0;
-
- /* initialize buffer page pointers */
- temp = (u32)(req->req.dma + req->req.actual);
-
- trb->trb_hw->buf_addr_lo = cpu_to_le32(temp);
- trb->trb_hw->buf_addr_hi = 0;
- trb->trb_hw->trb_len = cpu_to_le32(*length);
- trb->trb_hw->ctrl.own = 1;
-
- if (req->ep->ep_num == 0)
- trb->trb_hw->ctrl.type = TYPE_DATA;
- else
- trb->trb_hw->ctrl.type = TYPE_NORMAL;
-
- req->req.actual += *length;
-
- direction = mv_u3d_ep_dir(req->ep);
- if (direction == MV_U3D_EP_DIR_IN)
- trb->trb_hw->ctrl.dir = 1;
- else
- trb->trb_hw->ctrl.dir = 0;
-
- /* zlp is needed if req->req.zero is set */
- if (req->req.zero) {
- if (*length == 0 || (*length % req->ep->ep.maxpacket) != 0)
- *is_last = 1;
- else
- *is_last = 0;
- } else if (req->req.length == req->req.actual)
- *is_last = 1;
- else
- *is_last = 0;
-
- /* Enable interrupt for the last trb of a request */
- if (*is_last && !req->req.no_interrupt)
- trb->trb_hw->ctrl.ioc = 1;
-
- if (*is_last)
- trb->trb_hw->ctrl.chain = 0;
- else {
- trb->trb_hw->ctrl.chain = 1;
- dev_dbg(u3d->dev, "chain trb\n");
- }
-
- wmb();
-
- return 0;
-}
-
-/* generate TRB linked list for a request
- * usb controller only supports continous trb chain,
- * that trb structure physical address should be continous.
- */
-static int mv_u3d_req_to_trb(struct mv_u3d_req *req)
-{
- unsigned count;
- int is_last;
- struct mv_u3d_trb *trb;
- struct mv_u3d_trb_hw *trb_hw;
- struct mv_u3d *u3d;
- dma_addr_t dma;
- unsigned length;
- unsigned trb_num;
-
- u3d = req->ep->u3d;
-
- INIT_LIST_HEAD(&req->trb_list);
-
- length = req->req.length - req->req.actual;
- /* normally the request transfer length is less than 16KB.
- * we use buil_trb_one() to optimize it.
- */
- if (length <= (unsigned)MV_U3D_EP_MAX_LENGTH_TRANSFER) {
- trb = mv_u3d_build_trb_one(req, &count, &dma);
- list_add_tail(&trb->trb_list, &req->trb_list);
- req->trb_head = trb;
- req->trb_count = 1;
- req->chain = 0;
- } else {
- trb_num = length / MV_U3D_EP_MAX_LENGTH_TRANSFER;
- if (length % MV_U3D_EP_MAX_LENGTH_TRANSFER)
- trb_num++;
-
- trb = kcalloc(trb_num, sizeof(*trb), GFP_ATOMIC);
- if (!trb)
- return -ENOMEM;
-
- trb_hw = kcalloc(trb_num, sizeof(*trb_hw), GFP_ATOMIC);
- if (!trb_hw) {
- kfree(trb);
- return -ENOMEM;
- }
-
- do {
- trb->trb_hw = trb_hw;
- if (mv_u3d_build_trb_chain(req, &count,
- trb, &is_last)) {
- dev_err(u3d->dev,
- "%s, mv_u3d_build_trb_chain fail\n",
- __func__);
- return -EIO;
- }
-
- list_add_tail(&trb->trb_list, &req->trb_list);
- req->trb_count++;
- trb++;
- trb_hw++;
- } while (!is_last);
-
- req->trb_head = list_entry(req->trb_list.next,
- struct mv_u3d_trb, trb_list);
- req->trb_head->trb_dma = dma_map_single(u3d->gadget.dev.parent,
- req->trb_head->trb_hw,
- trb_num * sizeof(*trb_hw),
- DMA_BIDIRECTIONAL);
- if (dma_mapping_error(u3d->gadget.dev.parent,
- req->trb_head->trb_dma)) {
- kfree(req->trb_head->trb_hw);
- kfree(req->trb_head);
- return -EFAULT;
- }
-
- req->chain = 1;
- }
-
- return 0;
-}
-
-static int
-mv_u3d_start_queue(struct mv_u3d_ep *ep)
-{
- struct mv_u3d *u3d = ep->u3d;
- struct mv_u3d_req *req;
- int ret;
-
- if (!list_empty(&ep->req_list) && !ep->processing)
- req = list_entry(ep->req_list.next, struct mv_u3d_req, list);
- else
- return 0;
-
- ep->processing = 1;
-
- /* set up dma mapping */
- ret = usb_gadget_map_request(&u3d->gadget, &req->req,
- mv_u3d_ep_dir(ep));
- if (ret)
- goto break_processing;
-
- req->req.status = -EINPROGRESS;
- req->req.actual = 0;
- req->trb_count = 0;
-
- /* build trbs */
- ret = mv_u3d_req_to_trb(req);
- if (ret) {
- dev_err(u3d->dev, "%s, mv_u3d_req_to_trb fail\n", __func__);
- goto break_processing;
- }
-
- /* and push them to device queue */
- ret = mv_u3d_queue_trb(ep, req);
- if (ret)
- goto break_processing;
-
- /* irq handler advances the queue */
- list_add_tail(&req->queue, &ep->queue);
-
- return 0;
-
-break_processing:
- ep->processing = 0;
- return ret;
-}
-
-static int mv_u3d_ep_enable(struct usb_ep *_ep,
- const struct usb_endpoint_descriptor *desc)
-{
- struct mv_u3d *u3d;
- struct mv_u3d_ep *ep;
- u16 max = 0;
- unsigned maxburst = 0;
- u32 epxcr, direction;
-
- if (!_ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT)
- return -EINVAL;
-
- ep = container_of(_ep, struct mv_u3d_ep, ep);
- u3d = ep->u3d;
-
- if (!u3d->driver || u3d->gadget.speed == USB_SPEED_UNKNOWN)
- return -ESHUTDOWN;
-
- direction = mv_u3d_ep_dir(ep);
- max = le16_to_cpu(desc->wMaxPacketSize);
-
- if (!_ep->maxburst)
- _ep->maxburst = 1;
- maxburst = _ep->maxburst;
-
- /* Set the max burst size */
- switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
- case USB_ENDPOINT_XFER_BULK:
- if (maxburst > 16) {
- dev_dbg(u3d->dev,
- "max burst should not be greater "
- "than 16 on bulk ep\n");
- maxburst = 1;
- _ep->maxburst = maxburst;
- }
- dev_dbg(u3d->dev,
- "maxburst: %d on bulk %s\n", maxburst, ep->name);
- break;
- case USB_ENDPOINT_XFER_CONTROL:
- /* control transfer only supports maxburst as one */
- maxburst = 1;
- _ep->maxburst = maxburst;
- break;
- case USB_ENDPOINT_XFER_INT:
- if (maxburst != 1) {
- dev_dbg(u3d->dev,
- "max burst should be 1 on int ep "
- "if transfer size is not 1024\n");
- maxburst = 1;
- _ep->maxburst = maxburst;
- }
- break;
- case USB_ENDPOINT_XFER_ISOC:
- if (maxburst != 1) {
- dev_dbg(u3d->dev,
- "max burst should be 1 on isoc ep "
- "if transfer size is not 1024\n");
- maxburst = 1;
- _ep->maxburst = maxburst;
- }
- break;
- default:
- goto en_done;
- }
-
- ep->ep.maxpacket = max;
- ep->ep.desc = desc;
- ep->enabled = 1;
-
- /* Enable the endpoint for Rx or Tx and set the endpoint type */
- if (direction == MV_U3D_EP_DIR_OUT) {
- epxcr = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0);
- epxcr |= MV_U3D_EPXCR_EP_INIT;
- iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0);
- udelay(5);
- epxcr &= ~MV_U3D_EPXCR_EP_INIT;
- iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0);
-
- epxcr = ((max << MV_U3D_EPXCR_MAX_PACKET_SIZE_SHIFT)
- | ((maxburst - 1) << MV_U3D_EPXCR_MAX_BURST_SIZE_SHIFT)
- | (1 << MV_U3D_EPXCR_EP_ENABLE_SHIFT)
- | (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK));
- iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxoutcr1);
- } else {
- epxcr = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxincr0);
- epxcr |= MV_U3D_EPXCR_EP_INIT;
- iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxincr0);
- udelay(5);
- epxcr &= ~MV_U3D_EPXCR_EP_INIT;
- iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxincr0);
-
- epxcr = ((max << MV_U3D_EPXCR_MAX_PACKET_SIZE_SHIFT)
- | ((maxburst - 1) << MV_U3D_EPXCR_MAX_BURST_SIZE_SHIFT)
- | (1 << MV_U3D_EPXCR_EP_ENABLE_SHIFT)
- | (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK));
- iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxincr1);
- }
-
- return 0;
-en_done:
- return -EINVAL;
-}
-
-static int mv_u3d_ep_disable(struct usb_ep *_ep)
-{
- struct mv_u3d *u3d;
- struct mv_u3d_ep *ep;
- u32 epxcr, direction;
- unsigned long flags;
-
- if (!_ep)
- return -EINVAL;
-
- ep = container_of(_ep, struct mv_u3d_ep, ep);
- if (!ep->ep.desc)
- return -EINVAL;
-
- u3d = ep->u3d;
-
- direction = mv_u3d_ep_dir(ep);
-
- /* nuke all pending requests (does flush) */
- spin_lock_irqsave(&u3d->lock, flags);
- mv_u3d_nuke(ep, -ESHUTDOWN);
- spin_unlock_irqrestore(&u3d->lock, flags);
-
- /* Disable the endpoint for Rx or Tx and reset the endpoint type */
- if (direction == MV_U3D_EP_DIR_OUT) {
- epxcr = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxoutcr1);
- epxcr &= ~((1 << MV_U3D_EPXCR_EP_ENABLE_SHIFT)
- | USB_ENDPOINT_XFERTYPE_MASK);
- iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxoutcr1);
- } else {
- epxcr = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxincr1);
- epxcr &= ~((1 << MV_U3D_EPXCR_EP_ENABLE_SHIFT)
- | USB_ENDPOINT_XFERTYPE_MASK);
- iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxincr1);
- }
-
- ep->enabled = 0;
-
- ep->ep.desc = NULL;
- return 0;
-}
-
-static struct usb_request *
-mv_u3d_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
-{
- struct mv_u3d_req *req;
-
- req = kzalloc(sizeof *req, gfp_flags);
- if (!req)
- return NULL;
-
- INIT_LIST_HEAD(&req->queue);
-
- return &req->req;
-}
-
-static void mv_u3d_free_request(struct usb_ep *_ep, struct usb_request *_req)
-{
- struct mv_u3d_req *req = container_of(_req, struct mv_u3d_req, req);
-
- kfree(req);
-}
-
-static void mv_u3d_ep_fifo_flush(struct usb_ep *_ep)
-{
- struct mv_u3d *u3d;
- u32 direction;
- struct mv_u3d_ep *ep = container_of(_ep, struct mv_u3d_ep, ep);
- unsigned int loops;
- u32 tmp;
-
- /* if endpoint is not enabled, cannot flush endpoint */
- if (!ep->enabled)
- return;
-
- u3d = ep->u3d;
- direction = mv_u3d_ep_dir(ep);
-
- /* ep0 need clear bit after flushing fifo. */
- if (!ep->ep_num) {
- if (direction == MV_U3D_EP_DIR_OUT) {
- tmp = ioread32(&u3d->vuc_regs->epcr[0].epxoutcr0);
- tmp |= MV_U3D_EPXCR_EP_FLUSH;
- iowrite32(tmp, &u3d->vuc_regs->epcr[0].epxoutcr0);
- udelay(10);
- tmp &= ~MV_U3D_EPXCR_EP_FLUSH;
- iowrite32(tmp, &u3d->vuc_regs->epcr[0].epxoutcr0);
- } else {
- tmp = ioread32(&u3d->vuc_regs->epcr[0].epxincr0);
- tmp |= MV_U3D_EPXCR_EP_FLUSH;
- iowrite32(tmp, &u3d->vuc_regs->epcr[0].epxincr0);
- udelay(10);
- tmp &= ~MV_U3D_EPXCR_EP_FLUSH;
- iowrite32(tmp, &u3d->vuc_regs->epcr[0].epxincr0);
- }
- return;
- }
-
- if (direction == MV_U3D_EP_DIR_OUT) {
- tmp = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0);
- tmp |= MV_U3D_EPXCR_EP_FLUSH;
- iowrite32(tmp, &u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0);
-
- /* Wait until flushing completed */
- loops = LOOPS(MV_U3D_FLUSH_TIMEOUT);
- while (ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0) &
- MV_U3D_EPXCR_EP_FLUSH) {
- /*
- * EP_FLUSH bit should be cleared to indicate this
- * operation is complete
- */
- if (loops == 0) {
- dev_dbg(u3d->dev,
- "EP FLUSH TIMEOUT for ep%d%s\n", ep->ep_num,
- direction ? "in" : "out");
- return;
- }
- loops--;
- udelay(LOOPS_USEC);
- }
- } else { /* EP_DIR_IN */
- tmp = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxincr0);
- tmp |= MV_U3D_EPXCR_EP_FLUSH;
- iowrite32(tmp, &u3d->vuc_regs->epcr[ep->ep_num].epxincr0);
-
- /* Wait until flushing completed */
- loops = LOOPS(MV_U3D_FLUSH_TIMEOUT);
- while (ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxincr0) &
- MV_U3D_EPXCR_EP_FLUSH) {
- /*
- * EP_FLUSH bit should be cleared to indicate this
- * operation is complete
- */
- if (loops == 0) {
- dev_dbg(u3d->dev,
- "EP FLUSH TIMEOUT for ep%d%s\n", ep->ep_num,
- direction ? "in" : "out");
- return;
- }
- loops--;
- udelay(LOOPS_USEC);
- }
- }
-}
-
-/* queues (submits) an I/O request to an endpoint */
-static int
-mv_u3d_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
-{
- struct mv_u3d_ep *ep;
- struct mv_u3d_req *req;
- struct mv_u3d *u3d;
- unsigned long flags;
- int is_first_req = 0;
-
- if (unlikely(!_ep || !_req))
- return -EINVAL;
-
- ep = container_of(_ep, struct mv_u3d_ep, ep);
- u3d = ep->u3d;
-
- req = container_of(_req, struct mv_u3d_req, req);
-
- if (!ep->ep_num
- && u3d->ep0_state == MV_U3D_STATUS_STAGE
- && !_req->length) {
- dev_dbg(u3d->dev, "ep0 status stage\n");
- u3d->ep0_state = MV_U3D_WAIT_FOR_SETUP;
- return 0;
- }
-
- dev_dbg(u3d->dev, "%s: %s, req: 0x%p\n",
- __func__, _ep->name, req);
-
- /* catch various bogus parameters */
- if (!req->req.complete || !req->req.buf
- || !list_empty(&req->queue)) {
- dev_err(u3d->dev,
- "%s, bad params, _req: 0x%p,"
- "req->req.complete: 0x%p, req->req.buf: 0x%p,"
- "list_empty: 0x%x\n",
- __func__, _req,
- req->req.complete, req->req.buf,
- list_empty(&req->queue));
- return -EINVAL;
- }
- if (unlikely(!ep->ep.desc)) {
- dev_err(u3d->dev, "%s, bad ep\n", __func__);
- return -EINVAL;
- }
- if (ep->ep.desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
- if (req->req.length > ep->ep.maxpacket)
- return -EMSGSIZE;
- }
-
- if (!u3d->driver || u3d->gadget.speed == USB_SPEED_UNKNOWN) {
- dev_err(u3d->dev,
- "bad params of driver/speed\n");
- return -ESHUTDOWN;
- }
-
- req->ep = ep;
-
- /* Software list handles usb request. */
- spin_lock_irqsave(&ep->req_lock, flags);
- is_first_req = list_empty(&ep->req_list);
- list_add_tail(&req->list, &ep->req_list);
- spin_unlock_irqrestore(&ep->req_lock, flags);
- if (!is_first_req) {
- dev_dbg(u3d->dev, "list is not empty\n");
- return 0;
- }
-
- dev_dbg(u3d->dev, "call mv_u3d_start_queue from usb_ep_queue\n");
- spin_lock_irqsave(&u3d->lock, flags);
- mv_u3d_start_queue(ep);
- spin_unlock_irqrestore(&u3d->lock, flags);
- return 0;
-}
-
-/* dequeues (cancels, unlinks) an I/O request from an endpoint */
-static int mv_u3d_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
-{
- struct mv_u3d_ep *ep;
- struct mv_u3d_req *req = NULL, *iter;
- struct mv_u3d *u3d;
- struct mv_u3d_ep_context *ep_context;
- struct mv_u3d_req *next_req;
-
- unsigned long flags;
- int ret = 0;
-
- if (!_ep || !_req)
- return -EINVAL;
-
- ep = container_of(_ep, struct mv_u3d_ep, ep);
- u3d = ep->u3d;
-
- spin_lock_irqsave(&ep->u3d->lock, flags);
-
- /* make sure it's actually queued on this endpoint */
- list_for_each_entry(iter, &ep->queue, queue) {
- if (&iter->req != _req)
- continue;
- req = iter;
- break;
- }
- if (!req) {
- ret = -EINVAL;
- goto out;
- }
-
- /* The request is in progress, or completed but not dequeued */
- if (ep->queue.next == &req->queue) {
- _req->status = -ECONNRESET;
- mv_u3d_ep_fifo_flush(_ep);
-
- /* The request isn't the last request in this ep queue */
- if (req->queue.next != &ep->queue) {
- dev_dbg(u3d->dev,
- "it is the last request in this ep queue\n");
- ep_context = ep->ep_context;
- next_req = list_entry(req->queue.next,
- struct mv_u3d_req, queue);
-
- /* Point first TRB of next request to the EP context. */
- iowrite32((unsigned long) next_req->trb_head,
- &ep_context->trb_addr_lo);
- } else {
- struct mv_u3d_ep_context *ep_context;
- ep_context = ep->ep_context;
- ep_context->trb_addr_lo = 0;
- ep_context->trb_addr_hi = 0;
- }
-
- } else
- WARN_ON(1);
-
- mv_u3d_done(ep, req, -ECONNRESET);
-
- /* remove the req from the ep req list */
- if (!list_empty(&ep->req_list)) {
- struct mv_u3d_req *curr_req;
- curr_req = list_entry(ep->req_list.next,
- struct mv_u3d_req, list);
- if (curr_req == req) {
- list_del_init(&req->list);
- ep->processing = 0;
- }
- }
-
-out:
- spin_unlock_irqrestore(&ep->u3d->lock, flags);
- return ret;
-}
-
-static void
-mv_u3d_ep_set_stall(struct mv_u3d *u3d, u8 ep_num, u8 direction, int stall)
-{
- u32 tmp;
- struct mv_u3d_ep *ep = u3d->eps;
-
- dev_dbg(u3d->dev, "%s\n", __func__);
- if (direction == MV_U3D_EP_DIR_OUT) {
- tmp = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0);
- if (stall)
- tmp |= MV_U3D_EPXCR_EP_HALT;
- else
- tmp &= ~MV_U3D_EPXCR_EP_HALT;
- iowrite32(tmp, &u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0);
- } else {
- tmp = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxincr0);
- if (stall)
- tmp |= MV_U3D_EPXCR_EP_HALT;
- else
- tmp &= ~MV_U3D_EPXCR_EP_HALT;
- iowrite32(tmp, &u3d->vuc_regs->epcr[ep->ep_num].epxincr0);
- }
-}
-
-static int mv_u3d_ep_set_halt_wedge(struct usb_ep *_ep, int halt, int wedge)
-{
- struct mv_u3d_ep *ep;
- unsigned long flags;
- int status = 0;
- struct mv_u3d *u3d;
-
- ep = container_of(_ep, struct mv_u3d_ep, ep);
- u3d = ep->u3d;
- if (!ep->ep.desc) {
- status = -EINVAL;
- goto out;
- }
-
- if (ep->ep.desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
- status = -EOPNOTSUPP;
- goto out;
- }
-
- /*
- * Attempt to halt IN ep will fail if any transfer requests
- * are still queue
- */
- if (halt && (mv_u3d_ep_dir(ep) == MV_U3D_EP_DIR_IN)
- && !list_empty(&ep->queue)) {
- status = -EAGAIN;
- goto out;
- }
-
- spin_lock_irqsave(&ep->u3d->lock, flags);
- mv_u3d_ep_set_stall(u3d, ep->ep_num, mv_u3d_ep_dir(ep), halt);
- if (halt && wedge)
- ep->wedge = 1;
- else if (!halt)
- ep->wedge = 0;
- spin_unlock_irqrestore(&ep->u3d->lock, flags);
-
- if (ep->ep_num == 0)
- u3d->ep0_dir = MV_U3D_EP_DIR_OUT;
-out:
- return status;
-}
-
-static int mv_u3d_ep_set_halt(struct usb_ep *_ep, int halt)
-{
- return mv_u3d_ep_set_halt_wedge(_ep, halt, 0);
-}
-
-static int mv_u3d_ep_set_wedge(struct usb_ep *_ep)
-{
- return mv_u3d_ep_set_halt_wedge(_ep, 1, 1);
-}
-
-static const struct usb_ep_ops mv_u3d_ep_ops = {
- .enable = mv_u3d_ep_enable,
- .disable = mv_u3d_ep_disable,
-
- .alloc_request = mv_u3d_alloc_request,
- .free_request = mv_u3d_free_request,
-
- .queue = mv_u3d_ep_queue,
- .dequeue = mv_u3d_ep_dequeue,
-
- .set_wedge = mv_u3d_ep_set_wedge,
- .set_halt = mv_u3d_ep_set_halt,
- .fifo_flush = mv_u3d_ep_fifo_flush,
-};
-
-static void mv_u3d_controller_stop(struct mv_u3d *u3d)
-{
- u32 tmp;
-
- if (!u3d->clock_gating && u3d->vbus_valid_detect)
- iowrite32(MV_U3D_INTR_ENABLE_VBUS_VALID,
- &u3d->vuc_regs->intrenable);
- else
- iowrite32(0, &u3d->vuc_regs->intrenable);
- iowrite32(~0x0, &u3d->vuc_regs->endcomplete);
- iowrite32(~0x0, &u3d->vuc_regs->trbunderrun);
- iowrite32(~0x0, &u3d->vuc_regs->trbcomplete);
- iowrite32(~0x0, &u3d->vuc_regs->linkchange);
- iowrite32(0x1, &u3d->vuc_regs->setuplock);
-
- /* Reset the RUN bit in the command register to stop USB */
- tmp = ioread32(&u3d->op_regs->usbcmd);
- tmp &= ~MV_U3D_CMD_RUN_STOP;
- iowrite32(tmp, &u3d->op_regs->usbcmd);
- dev_dbg(u3d->dev, "after u3d_stop, USBCMD 0x%x\n",
- ioread32(&u3d->op_regs->usbcmd));
-}
-
-static void mv_u3d_controller_start(struct mv_u3d *u3d)
-{
- u32 usbintr;
- u32 temp;
-
- /* enable link LTSSM state machine */
- temp = ioread32(&u3d->vuc_regs->ltssm);
- temp |= MV_U3D_LTSSM_PHY_INIT_DONE;
- iowrite32(temp, &u3d->vuc_regs->ltssm);
-
- /* Enable interrupts */
- usbintr = MV_U3D_INTR_ENABLE_LINK_CHG | MV_U3D_INTR_ENABLE_TXDESC_ERR |
- MV_U3D_INTR_ENABLE_RXDESC_ERR | MV_U3D_INTR_ENABLE_TX_COMPLETE |
- MV_U3D_INTR_ENABLE_RX_COMPLETE | MV_U3D_INTR_ENABLE_SETUP |
- (u3d->vbus_valid_detect ? MV_U3D_INTR_ENABLE_VBUS_VALID : 0);
- iowrite32(usbintr, &u3d->vuc_regs->intrenable);
-
- /* Enable ctrl ep */
- iowrite32(0x1, &u3d->vuc_regs->ctrlepenable);
-
- /* Set the Run bit in the command register */
- iowrite32(MV_U3D_CMD_RUN_STOP, &u3d->op_regs->usbcmd);
- dev_dbg(u3d->dev, "after u3d_start, USBCMD 0x%x\n",
- ioread32(&u3d->op_regs->usbcmd));
-}
-
-static int mv_u3d_controller_reset(struct mv_u3d *u3d)
-{
- unsigned int loops;
- u32 tmp;
-
- /* Stop the controller */
- tmp = ioread32(&u3d->op_regs->usbcmd);
- tmp &= ~MV_U3D_CMD_RUN_STOP;
- iowrite32(tmp, &u3d->op_regs->usbcmd);
-
- /* Reset the controller to get default values */
- iowrite32(MV_U3D_CMD_CTRL_RESET, &u3d->op_regs->usbcmd);
-
- /* wait for reset to complete */
- loops = LOOPS(MV_U3D_RESET_TIMEOUT);
- while (ioread32(&u3d->op_regs->usbcmd) & MV_U3D_CMD_CTRL_RESET) {
- if (loops == 0) {
- dev_err(u3d->dev,
- "Wait for RESET completed TIMEOUT\n");
- return -ETIMEDOUT;
- }
- loops--;
- udelay(LOOPS_USEC);
- }
-
- /* Configure the Endpoint Context Address */
- iowrite32(u3d->ep_context_dma, &u3d->op_regs->dcbaapl);
- iowrite32(0, &u3d->op_regs->dcbaaph);
-
- return 0;
-}
-
-static int mv_u3d_enable(struct mv_u3d *u3d)
-{
- struct mv_usb_platform_data *pdata = dev_get_platdata(u3d->dev);
- int retval;
-
- if (u3d->active)
- return 0;
-
- if (!u3d->clock_gating) {
- u3d->active = 1;
- return 0;
- }
-
- dev_dbg(u3d->dev, "enable u3d\n");
- clk_enable(u3d->clk);
- if (pdata->phy_init) {
- retval = pdata->phy_init(u3d->phy_regs);
- if (retval) {
- dev_err(u3d->dev,
- "init phy error %d\n", retval);
- clk_disable(u3d->clk);
- return retval;
- }
- }
- u3d->active = 1;
-
- return 0;
-}
-
-static void mv_u3d_disable(struct mv_u3d *u3d)
-{
- struct mv_usb_platform_data *pdata = dev_get_platdata(u3d->dev);
- if (u3d->clock_gating && u3d->active) {
- dev_dbg(u3d->dev, "disable u3d\n");
- if (pdata->phy_deinit)
- pdata->phy_deinit(u3d->phy_regs);
- clk_disable(u3d->clk);
- u3d->active = 0;
- }
-}
-
-static int mv_u3d_vbus_session(struct usb_gadget *gadget, int is_active)
-{
- struct mv_u3d *u3d;
- unsigned long flags;
- int retval = 0;
-
- u3d = container_of(gadget, struct mv_u3d, gadget);
-
- spin_lock_irqsave(&u3d->lock, flags);
-
- u3d->vbus_active = (is_active != 0);
- dev_dbg(u3d->dev, "%s: softconnect %d, vbus_active %d\n",
- __func__, u3d->softconnect, u3d->vbus_active);
- /*
- * 1. external VBUS detect: we can disable/enable clock on demand.
- * 2. UDC VBUS detect: we have to enable clock all the time.
- * 3. No VBUS detect: we have to enable clock all the time.
- */
- if (u3d->driver && u3d->softconnect && u3d->vbus_active) {
- retval = mv_u3d_enable(u3d);
- if (retval == 0) {
- /*
- * after clock is disabled, we lost all the register
- * context. We have to re-init registers
- */
- mv_u3d_controller_reset(u3d);
- mv_u3d_ep0_reset(u3d);
- mv_u3d_controller_start(u3d);
- }
- } else if (u3d->driver && u3d->softconnect) {
- if (!u3d->active)
- goto out;
-
- /* stop all the transfer in queue*/
- mv_u3d_stop_activity(u3d, u3d->driver);
- mv_u3d_controller_stop(u3d);
- mv_u3d_disable(u3d);
- }
-
-out:
- spin_unlock_irqrestore(&u3d->lock, flags);
- return retval;
-}
-
-/* constrain controller's VBUS power usage
- * This call is used by gadget drivers during SET_CONFIGURATION calls,
- * reporting how much power the device may consume. For example, this
- * could affect how quickly batteries are recharged.
- *
- * Returns zero on success, else negative errno.
- */
-static int mv_u3d_vbus_draw(struct usb_gadget *gadget, unsigned mA)
-{
- struct mv_u3d *u3d = container_of(gadget, struct mv_u3d, gadget);
-
- u3d->power = mA;
-
- return 0;
-}
-
-static int mv_u3d_pullup(struct usb_gadget *gadget, int is_on)
-{
- struct mv_u3d *u3d = container_of(gadget, struct mv_u3d, gadget);
- unsigned long flags;
- int retval = 0;
-
- spin_lock_irqsave(&u3d->lock, flags);
-
- dev_dbg(u3d->dev, "%s: softconnect %d, vbus_active %d\n",
- __func__, u3d->softconnect, u3d->vbus_active);
- u3d->softconnect = (is_on != 0);
- if (u3d->driver && u3d->softconnect && u3d->vbus_active) {
- retval = mv_u3d_enable(u3d);
- if (retval == 0) {
- /*
- * after clock is disabled, we lost all the register
- * context. We have to re-init registers
- */
- mv_u3d_controller_reset(u3d);
- mv_u3d_ep0_reset(u3d);
- mv_u3d_controller_start(u3d);
- }
- } else if (u3d->driver && u3d->vbus_active) {
- /* stop all the transfer in queue*/
- mv_u3d_stop_activity(u3d, u3d->driver);
- mv_u3d_controller_stop(u3d);
- mv_u3d_disable(u3d);
- }
-
- spin_unlock_irqrestore(&u3d->lock, flags);
-
- return retval;
-}
-
-static int mv_u3d_start(struct usb_gadget *g,
- struct usb_gadget_driver *driver)
-{
- struct mv_u3d *u3d = container_of(g, struct mv_u3d, gadget);
- struct mv_usb_platform_data *pdata = dev_get_platdata(u3d->dev);
- unsigned long flags;
-
- if (u3d->driver)
- return -EBUSY;
-
- spin_lock_irqsave(&u3d->lock, flags);
-
- if (!u3d->clock_gating) {
- clk_enable(u3d->clk);
- if (pdata->phy_init)
- pdata->phy_init(u3d->phy_regs);
- }
-
- /* hook up the driver ... */
- u3d->driver = driver;
-
- u3d->ep0_dir = USB_DIR_OUT;
-
- spin_unlock_irqrestore(&u3d->lock, flags);
-
- u3d->vbus_valid_detect = 1;
-
- return 0;
-}
-
-static int mv_u3d_stop(struct usb_gadget *g)
-{
- struct mv_u3d *u3d = container_of(g, struct mv_u3d, gadget);
- struct mv_usb_platform_data *pdata = dev_get_platdata(u3d->dev);
- unsigned long flags;
-
- u3d->vbus_valid_detect = 0;
- spin_lock_irqsave(&u3d->lock, flags);
-
- /* enable clock to access controller register */
- clk_enable(u3d->clk);
- if (pdata->phy_init)
- pdata->phy_init(u3d->phy_regs);
-
- mv_u3d_controller_stop(u3d);
- /* stop all usb activities */
- u3d->gadget.speed = USB_SPEED_UNKNOWN;
- mv_u3d_stop_activity(u3d, NULL);
- mv_u3d_disable(u3d);
-
- if (pdata->phy_deinit)
- pdata->phy_deinit(u3d->phy_regs);
- clk_disable(u3d->clk);
-
- spin_unlock_irqrestore(&u3d->lock, flags);
-
- u3d->driver = NULL;
-
- return 0;
-}
-
-/* device controller usb_gadget_ops structure */
-static const struct usb_gadget_ops mv_u3d_ops = {
- /* notify controller that VBUS is powered or not */
- .vbus_session = mv_u3d_vbus_session,
-
- /* constrain controller's VBUS power usage */
- .vbus_draw = mv_u3d_vbus_draw,
-
- .pullup = mv_u3d_pullup,
- .udc_start = mv_u3d_start,
- .udc_stop = mv_u3d_stop,
-};
-
-static int mv_u3d_eps_init(struct mv_u3d *u3d)
-{
- struct mv_u3d_ep *ep;
- char name[14];
- int i;
-
- /* initialize ep0, ep0 in/out use eps[1] */
- ep = &u3d->eps[1];
- ep->u3d = u3d;
- strscpy(ep->name, "ep0");
- ep->ep.name = ep->name;
- ep->ep.ops = &mv_u3d_ep_ops;
- ep->wedge = 0;
- usb_ep_set_maxpacket_limit(&ep->ep, MV_U3D_EP0_MAX_PKT_SIZE);
- ep->ep.caps.type_control = true;
- ep->ep.caps.dir_in = true;
- ep->ep.caps.dir_out = true;
- ep->ep_num = 0;
- ep->ep.desc = &mv_u3d_ep0_desc;
- INIT_LIST_HEAD(&ep->queue);
- INIT_LIST_HEAD(&ep->req_list);
- ep->ep_type = USB_ENDPOINT_XFER_CONTROL;
-
- /* add ep0 ep_context */
- ep->ep_context = &u3d->ep_context[1];
-
- /* initialize other endpoints */
- for (i = 2; i < u3d->max_eps * 2; i++) {
- ep = &u3d->eps[i];
- if (i & 1) {
- snprintf(name, sizeof(name), "ep%din", i >> 1);
- ep->direction = MV_U3D_EP_DIR_IN;
- ep->ep.caps.dir_in = true;
- } else {
- snprintf(name, sizeof(name), "ep%dout", i >> 1);
- ep->direction = MV_U3D_EP_DIR_OUT;
- ep->ep.caps.dir_out = true;
- }
- ep->u3d = u3d;
- strscpy(ep->name, name);
- ep->ep.name = ep->name;
-
- ep->ep.caps.type_iso = true;
- ep->ep.caps.type_bulk = true;
- ep->ep.caps.type_int = true;
-
- ep->ep.ops = &mv_u3d_ep_ops;
- usb_ep_set_maxpacket_limit(&ep->ep, (unsigned short) ~0);
- ep->ep_num = i / 2;
-
- INIT_LIST_HEAD(&ep->queue);
- list_add_tail(&ep->ep.ep_list, &u3d->gadget.ep_list);
-
- INIT_LIST_HEAD(&ep->req_list);
- spin_lock_init(&ep->req_lock);
- ep->ep_context = &u3d->ep_context[i];
- }
-
- return 0;
-}
-
-/* delete all endpoint requests, called with spinlock held */
-static void mv_u3d_nuke(struct mv_u3d_ep *ep, int status)
-{
- /* endpoint fifo flush */
- mv_u3d_ep_fifo_flush(&ep->ep);
-
- while (!list_empty(&ep->queue)) {
- struct mv_u3d_req *req = NULL;
- req = list_entry(ep->queue.next, struct mv_u3d_req, queue);
- mv_u3d_done(ep, req, status);
- }
-}
-
-/* stop all USB activities */
-static
-void mv_u3d_stop_activity(struct mv_u3d *u3d, struct usb_gadget_driver *driver)
-{
- struct mv_u3d_ep *ep;
-
- mv_u3d_nuke(&u3d->eps[1], -ESHUTDOWN);
-
- list_for_each_entry(ep, &u3d->gadget.ep_list, ep.ep_list) {
- mv_u3d_nuke(ep, -ESHUTDOWN);
- }
-
- /* report disconnect; the driver is already quiesced */
- if (driver) {
- spin_unlock(&u3d->lock);
- driver->disconnect(&u3d->gadget);
- spin_lock(&u3d->lock);
- }
-}
-
-static void mv_u3d_irq_process_error(struct mv_u3d *u3d)
-{
- /* Increment the error count */
- u3d->errors++;
- dev_err(u3d->dev, "%s\n", __func__);
-}
-
-static void mv_u3d_irq_process_link_change(struct mv_u3d *u3d)
-{
- u32 linkchange;
-
- linkchange = ioread32(&u3d->vuc_regs->linkchange);
- iowrite32(linkchange, &u3d->vuc_regs->linkchange);
-
- dev_dbg(u3d->dev, "linkchange: 0x%x\n", linkchange);
-
- if (linkchange & MV_U3D_LINK_CHANGE_LINK_UP) {
- dev_dbg(u3d->dev, "link up: ltssm state: 0x%x\n",
- ioread32(&u3d->vuc_regs->ltssmstate));
-
- u3d->usb_state = USB_STATE_DEFAULT;
- u3d->ep0_dir = MV_U3D_EP_DIR_OUT;
- u3d->ep0_state = MV_U3D_WAIT_FOR_SETUP;
-
- /* set speed */
- u3d->gadget.speed = USB_SPEED_SUPER;
- }
-
- if (linkchange & MV_U3D_LINK_CHANGE_SUSPEND) {
- dev_dbg(u3d->dev, "link suspend\n");
- u3d->resume_state = u3d->usb_state;
- u3d->usb_state = USB_STATE_SUSPENDED;
- }
-
- if (linkchange & MV_U3D_LINK_CHANGE_RESUME) {
- dev_dbg(u3d->dev, "link resume\n");
- u3d->usb_state = u3d->resume_state;
- u3d->resume_state = 0;
- }
-
- if (linkchange & MV_U3D_LINK_CHANGE_WRESET) {
- dev_dbg(u3d->dev, "warm reset\n");
- u3d->usb_state = USB_STATE_POWERED;
- }
-
- if (linkchange & MV_U3D_LINK_CHANGE_HRESET) {
- dev_dbg(u3d->dev, "hot reset\n");
- u3d->usb_state = USB_STATE_DEFAULT;
- }
-
- if (linkchange & MV_U3D_LINK_CHANGE_INACT)
- dev_dbg(u3d->dev, "inactive\n");
-
- if (linkchange & MV_U3D_LINK_CHANGE_DISABLE_AFTER_U0)
- dev_dbg(u3d->dev, "ss.disabled\n");
-
- if (linkchange & MV_U3D_LINK_CHANGE_VBUS_INVALID) {
- dev_dbg(u3d->dev, "vbus invalid\n");
- u3d->usb_state = USB_STATE_ATTACHED;
- u3d->vbus_valid_detect = 1;
- /* if external vbus detect is not supported,
- * we handle it here.
- */
- if (!u3d->vbus) {
- spin_unlock(&u3d->lock);
- mv_u3d_vbus_session(&u3d->gadget, 0);
- spin_lock(&u3d->lock);
- }
- }
-}
-
-static void mv_u3d_ch9setaddress(struct mv_u3d *u3d,
- struct usb_ctrlrequest *setup)
-{
- u32 tmp;
-
- if (u3d->usb_state != USB_STATE_DEFAULT) {
- dev_err(u3d->dev,
- "%s, cannot setaddr in this state (%d)\n",
- __func__, u3d->usb_state);
- goto err;
- }
-
- u3d->dev_addr = (u8)setup->wValue;
-
- dev_dbg(u3d->dev, "%s: 0x%x\n", __func__, u3d->dev_addr);
-
- if (u3d->dev_addr > 127) {
- dev_err(u3d->dev,
- "%s, u3d address is wrong (out of range)\n", __func__);
- u3d->dev_addr = 0;
- goto err;
- }
-
- /* update usb state */
- u3d->usb_state = USB_STATE_ADDRESS;
-
- /* set the new address */
- tmp = ioread32(&u3d->vuc_regs->devaddrtiebrkr);
- tmp &= ~0x7F;
- tmp |= (u32)u3d->dev_addr;
- iowrite32(tmp, &u3d->vuc_regs->devaddrtiebrkr);
-
- return;
-err:
- mv_u3d_ep0_stall(u3d);
-}
-
-static int mv_u3d_is_set_configuration(struct usb_ctrlrequest *setup)
-{
- if ((setup->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)
- if (setup->bRequest == USB_REQ_SET_CONFIGURATION)
- return 1;
-
- return 0;
-}
-
-static void mv_u3d_handle_setup_packet(struct mv_u3d *u3d, u8 ep_num,
- struct usb_ctrlrequest *setup)
- __releases(&u3c->lock)
- __acquires(&u3c->lock)
-{
- bool delegate = false;
-
- mv_u3d_nuke(&u3d->eps[ep_num * 2 + MV_U3D_EP_DIR_IN], -ESHUTDOWN);
-
- dev_dbg(u3d->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 */
- if ((setup->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
- switch (setup->bRequest) {
- case USB_REQ_GET_STATUS:
- delegate = true;
- break;
-
- case USB_REQ_SET_ADDRESS:
- mv_u3d_ch9setaddress(u3d, setup);
- break;
-
- case USB_REQ_CLEAR_FEATURE:
- delegate = true;
- break;
-
- case USB_REQ_SET_FEATURE:
- delegate = true;
- break;
-
- default:
- delegate = true;
- }
- } else
- delegate = true;
-
- /* delegate USB standard requests to the gadget driver */
- if (delegate) {
- /* USB requests handled by gadget */
- if (setup->wLength) {
- /* DATA phase from gadget, STATUS phase from u3d */
- u3d->ep0_dir = (setup->bRequestType & USB_DIR_IN)
- ? MV_U3D_EP_DIR_IN : MV_U3D_EP_DIR_OUT;
- spin_unlock(&u3d->lock);
- if (u3d->driver->setup(&u3d->gadget,
- &u3d->local_setup_buff) < 0) {
- dev_err(u3d->dev, "setup error!\n");
- mv_u3d_ep0_stall(u3d);
- }
- spin_lock(&u3d->lock);
- } else {
- /* no DATA phase, STATUS phase from gadget */
- u3d->ep0_dir = MV_U3D_EP_DIR_IN;
- u3d->ep0_state = MV_U3D_STATUS_STAGE;
- spin_unlock(&u3d->lock);
- if (u3d->driver->setup(&u3d->gadget,
- &u3d->local_setup_buff) < 0)
- mv_u3d_ep0_stall(u3d);
- spin_lock(&u3d->lock);
- }
-
- if (mv_u3d_is_set_configuration(setup)) {
- dev_dbg(u3d->dev, "u3d configured\n");
- u3d->usb_state = USB_STATE_CONFIGURED;
- }
- }
-}
-
-static void mv_u3d_get_setup_data(struct mv_u3d *u3d, u8 ep_num, u8 *buffer_ptr)
-{
- struct mv_u3d_ep_context *epcontext;
-
- epcontext = &u3d->ep_context[ep_num * 2 + MV_U3D_EP_DIR_IN];
-
- /* Copy the setup packet to local buffer */
- memcpy(buffer_ptr, (u8 *) &epcontext->setup_buffer, 8);
-}
-
-static void mv_u3d_irq_process_setup(struct mv_u3d *u3d)
-{
- u32 tmp, i;
- /* Process all Setup packet received interrupts */
- tmp = ioread32(&u3d->vuc_regs->setuplock);
- if (tmp) {
- for (i = 0; i < u3d->max_eps; i++) {
- if (tmp & (1 << i)) {
- mv_u3d_get_setup_data(u3d, i,
- (u8 *)(&u3d->local_setup_buff));
- mv_u3d_handle_setup_packet(u3d, i,
- &u3d->local_setup_buff);
- }
- }
- }
-
- iowrite32(tmp, &u3d->vuc_regs->setuplock);
-}
-
-static void mv_u3d_irq_process_tr_complete(struct mv_u3d *u3d)
-{
- u32 tmp, bit_pos;
- int i, ep_num = 0, direction = 0;
- struct mv_u3d_ep *curr_ep;
- struct mv_u3d_req *curr_req, *temp_req;
- int status;
-
- tmp = ioread32(&u3d->vuc_regs->endcomplete);
-
- dev_dbg(u3d->dev, "tr_complete: ep: 0x%x\n", tmp);
- if (!tmp)
- return;
- iowrite32(tmp, &u3d->vuc_regs->endcomplete);
-
- for (i = 0; i < u3d->max_eps * 2; i++) {
- ep_num = i >> 1;
- direction = i % 2;
-
- bit_pos = 1 << (ep_num + 16 * direction);
-
- if (!(bit_pos & tmp))
- continue;
-
- if (i == 0)
- curr_ep = &u3d->eps[1];
- else
- curr_ep = &u3d->eps[i];
-
- /* remove req out of ep request list after completion */
- dev_dbg(u3d->dev, "tr comp: check req_list\n");
- spin_lock(&curr_ep->req_lock);
- if (!list_empty(&curr_ep->req_list)) {
- struct mv_u3d_req *req;
- req = list_entry(curr_ep->req_list.next,
- struct mv_u3d_req, list);
- list_del_init(&req->list);
- curr_ep->processing = 0;
- }
- spin_unlock(&curr_ep->req_lock);
-
- /* process the req queue until an uncomplete request */
- list_for_each_entry_safe(curr_req, temp_req,
- &curr_ep->queue, queue) {
- status = mv_u3d_process_ep_req(u3d, i, curr_req);
- if (status)
- break;
- /* write back status to req */
- curr_req->req.status = status;
-
- /* ep0 request completion */
- if (ep_num == 0) {
- mv_u3d_done(curr_ep, curr_req, 0);
- break;
- } else {
- mv_u3d_done(curr_ep, curr_req, status);
- }
- }
-
- dev_dbg(u3d->dev, "call mv_u3d_start_queue from ep complete\n");
- mv_u3d_start_queue(curr_ep);
- }
-}
-
-static irqreturn_t mv_u3d_irq(int irq, void *dev)
-{
- struct mv_u3d *u3d = (struct mv_u3d *)dev;
- u32 status, intr;
- u32 bridgesetting;
- u32 trbunderrun;
-
- spin_lock(&u3d->lock);
-
- status = ioread32(&u3d->vuc_regs->intrcause);
- intr = ioread32(&u3d->vuc_regs->intrenable);
- status &= intr;
-
- if (status == 0) {
- spin_unlock(&u3d->lock);
- dev_err(u3d->dev, "irq error!\n");
- return IRQ_NONE;
- }
-
- if (status & MV_U3D_USBINT_VBUS_VALID) {
- bridgesetting = ioread32(&u3d->vuc_regs->bridgesetting);
- if (bridgesetting & MV_U3D_BRIDGE_SETTING_VBUS_VALID) {
- /* write vbus valid bit of bridge setting to clear */
- bridgesetting = MV_U3D_BRIDGE_SETTING_VBUS_VALID;
- iowrite32(bridgesetting, &u3d->vuc_regs->bridgesetting);
- dev_dbg(u3d->dev, "vbus valid\n");
-
- u3d->usb_state = USB_STATE_POWERED;
- u3d->vbus_valid_detect = 0;
- /* if external vbus detect is not supported,
- * we handle it here.
- */
- if (!u3d->vbus) {
- spin_unlock(&u3d->lock);
- mv_u3d_vbus_session(&u3d->gadget, 1);
- spin_lock(&u3d->lock);
- }
- } else
- dev_err(u3d->dev, "vbus bit is not set\n");
- }
-
- /* RX data is already in the 16KB FIFO.*/
- if (status & MV_U3D_USBINT_UNDER_RUN) {
- trbunderrun = ioread32(&u3d->vuc_regs->trbunderrun);
- dev_err(u3d->dev, "under run, ep%d\n", trbunderrun);
- iowrite32(trbunderrun, &u3d->vuc_regs->trbunderrun);
- mv_u3d_irq_process_error(u3d);
- }
-
- if (status & (MV_U3D_USBINT_RXDESC_ERR | MV_U3D_USBINT_TXDESC_ERR)) {
- /* write one to clear */
- iowrite32(status & (MV_U3D_USBINT_RXDESC_ERR
- | MV_U3D_USBINT_TXDESC_ERR),
- &u3d->vuc_regs->intrcause);
- dev_err(u3d->dev, "desc err 0x%x\n", status);
- mv_u3d_irq_process_error(u3d);
- }
-
- if (status & MV_U3D_USBINT_LINK_CHG)
- mv_u3d_irq_process_link_change(u3d);
-
- if (status & MV_U3D_USBINT_TX_COMPLETE)
- mv_u3d_irq_process_tr_complete(u3d);
-
- if (status & MV_U3D_USBINT_RX_COMPLETE)
- mv_u3d_irq_process_tr_complete(u3d);
-
- if (status & MV_U3D_USBINT_SETUP)
- mv_u3d_irq_process_setup(u3d);
-
- spin_unlock(&u3d->lock);
- return IRQ_HANDLED;
-}
-
-static void mv_u3d_remove(struct platform_device *dev)
-{
- struct mv_u3d *u3d = platform_get_drvdata(dev);
-
- BUG_ON(u3d == NULL);
-
- usb_del_gadget_udc(&u3d->gadget);
-
- /* free memory allocated in probe */
- dma_pool_destroy(u3d->trb_pool);
-
- if (u3d->ep_context)
- dma_free_coherent(&dev->dev, u3d->ep_context_size,
- u3d->ep_context, u3d->ep_context_dma);
-
- kfree(u3d->eps);
-
- if (u3d->irq)
- free_irq(u3d->irq, u3d);
-
- if (u3d->cap_regs)
- iounmap(u3d->cap_regs);
- u3d->cap_regs = NULL;
-
- kfree(u3d->status_req);
-
- clk_put(u3d->clk);
-
- kfree(u3d);
-}
-
-static int mv_u3d_probe(struct platform_device *dev)
-{
- struct mv_u3d *u3d;
- struct mv_usb_platform_data *pdata = dev_get_platdata(&dev->dev);
- int retval = 0;
- struct resource *r;
- size_t size;
-
- if (!dev_get_platdata(&dev->dev)) {
- dev_err(&dev->dev, "missing platform_data\n");
- retval = -ENODEV;
- goto err_pdata;
- }
-
- u3d = kzalloc(sizeof(*u3d), GFP_KERNEL);
- if (!u3d) {
- retval = -ENOMEM;
- goto err_alloc_private;
- }
-
- spin_lock_init(&u3d->lock);
-
- platform_set_drvdata(dev, u3d);
-
- u3d->dev = &dev->dev;
- u3d->vbus = pdata->vbus;
-
- u3d->clk = clk_get(&dev->dev, NULL);
- if (IS_ERR(u3d->clk)) {
- retval = PTR_ERR(u3d->clk);
- goto err_get_clk;
- }
-
- r = platform_get_resource_byname(dev, IORESOURCE_MEM, "capregs");
- if (!r) {
- dev_err(&dev->dev, "no I/O memory resource defined\n");
- retval = -ENODEV;
- goto err_get_cap_regs;
- }
-
- u3d->cap_regs = (struct mv_u3d_cap_regs __iomem *)
- ioremap(r->start, resource_size(r));
- if (!u3d->cap_regs) {
- dev_err(&dev->dev, "failed to map I/O memory\n");
- retval = -EBUSY;
- goto err_map_cap_regs;
- } else {
- dev_dbg(&dev->dev, "cap_regs address: 0x%lx/0x%lx\n",
- (unsigned long) r->start,
- (unsigned long) u3d->cap_regs);
- }
-
- /* we will access controller register, so enable the u3d controller */
- retval = clk_enable(u3d->clk);
- if (retval) {
- dev_err(&dev->dev, "clk_enable error %d\n", retval);
- goto err_u3d_enable;
- }
-
- if (pdata->phy_init) {
- retval = pdata->phy_init(u3d->phy_regs);
- if (retval) {
- dev_err(&dev->dev, "init phy error %d\n", retval);
- clk_disable(u3d->clk);
- goto err_phy_init;
- }
- }
-
- u3d->op_regs = (struct mv_u3d_op_regs __iomem *)(u3d->cap_regs
- + MV_U3D_USB3_OP_REGS_OFFSET);
-
- u3d->vuc_regs = (struct mv_u3d_vuc_regs __iomem *)(u3d->cap_regs
- + ioread32(&u3d->cap_regs->vuoff));
-
- u3d->max_eps = 16;
-
- /*
- * some platform will use usb to download image, it may not disconnect
- * usb gadget before loading kernel. So first stop u3d here.
- */
- mv_u3d_controller_stop(u3d);
- iowrite32(0xFFFFFFFF, &u3d->vuc_regs->intrcause);
-
- if (pdata->phy_deinit)
- pdata->phy_deinit(u3d->phy_regs);
- clk_disable(u3d->clk);
-
- size = u3d->max_eps * sizeof(struct mv_u3d_ep_context) * 2;
- size = (size + MV_U3D_EP_CONTEXT_ALIGNMENT - 1)
- & ~(MV_U3D_EP_CONTEXT_ALIGNMENT - 1);
- u3d->ep_context = dma_alloc_coherent(&dev->dev, size,
- &u3d->ep_context_dma, GFP_KERNEL);
- if (!u3d->ep_context) {
- dev_err(&dev->dev, "allocate ep context memory failed\n");
- retval = -ENOMEM;
- goto err_alloc_ep_context;
- }
- u3d->ep_context_size = size;
-
- /* create TRB dma_pool resource */
- u3d->trb_pool = dma_pool_create("u3d_trb",
- &dev->dev,
- sizeof(struct mv_u3d_trb_hw),
- MV_U3D_TRB_ALIGNMENT,
- MV_U3D_DMA_BOUNDARY);
-
- if (!u3d->trb_pool) {
- retval = -ENOMEM;
- goto err_alloc_trb_pool;
- }
-
- size = u3d->max_eps * sizeof(struct mv_u3d_ep) * 2;
- u3d->eps = kzalloc(size, GFP_KERNEL);
- if (!u3d->eps) {
- retval = -ENOMEM;
- goto err_alloc_eps;
- }
-
- /* initialize ep0 status request structure */
- u3d->status_req = kzalloc(sizeof(struct mv_u3d_req) + 8, GFP_KERNEL);
- if (!u3d->status_req) {
- retval = -ENOMEM;
- goto err_alloc_status_req;
- }
- INIT_LIST_HEAD(&u3d->status_req->queue);
-
- /* allocate a small amount of memory to get valid address */
- u3d->status_req->req.buf = (char *)u3d->status_req
- + sizeof(struct mv_u3d_req);
- u3d->status_req->req.dma = virt_to_phys(u3d->status_req->req.buf);
-
- u3d->resume_state = USB_STATE_NOTATTACHED;
- u3d->usb_state = USB_STATE_ATTACHED;
- u3d->ep0_dir = MV_U3D_EP_DIR_OUT;
- u3d->remote_wakeup = 0;
-
- r = platform_get_resource(dev, IORESOURCE_IRQ, 0);
- if (!r) {
- dev_err(&dev->dev, "no IRQ resource defined\n");
- retval = -ENODEV;
- goto err_get_irq;
- }
- u3d->irq = r->start;
-
- /* initialize gadget structure */
- u3d->gadget.ops = &mv_u3d_ops; /* usb_gadget_ops */
- u3d->gadget.ep0 = &u3d->eps[1].ep; /* gadget ep0 */
- INIT_LIST_HEAD(&u3d->gadget.ep_list); /* ep_list */
- u3d->gadget.speed = USB_SPEED_UNKNOWN; /* speed */
-
- /* the "gadget" abstracts/virtualizes the controller */
- u3d->gadget.name = driver_name; /* gadget name */
-
- mv_u3d_eps_init(u3d);
-
- if (request_irq(u3d->irq, mv_u3d_irq,
- IRQF_SHARED, driver_name, u3d)) {
- u3d->irq = 0;
- dev_err(&dev->dev, "Request irq %d for u3d failed\n",
- u3d->irq);
- retval = -ENODEV;
- goto err_request_irq;
- }
-
- /* external vbus detection */
- if (u3d->vbus) {
- u3d->clock_gating = 1;
- dev_err(&dev->dev, "external vbus detection\n");
- }
-
- if (!u3d->clock_gating)
- u3d->vbus_active = 1;
-
- /* enable usb3 controller vbus detection */
- u3d->vbus_valid_detect = 1;
-
- retval = usb_add_gadget_udc(&dev->dev, &u3d->gadget);
- if (retval)
- goto err_unregister;
-
- dev_dbg(&dev->dev, "successful probe usb3 device %s clock gating.\n",
- u3d->clock_gating ? "with" : "without");
-
- return 0;
-
-err_unregister:
- free_irq(u3d->irq, u3d);
-err_get_irq:
-err_request_irq:
- kfree(u3d->status_req);
-err_alloc_status_req:
- kfree(u3d->eps);
-err_alloc_eps:
- dma_pool_destroy(u3d->trb_pool);
-err_alloc_trb_pool:
- dma_free_coherent(&dev->dev, u3d->ep_context_size,
- u3d->ep_context, u3d->ep_context_dma);
-err_alloc_ep_context:
-err_phy_init:
-err_u3d_enable:
- iounmap(u3d->cap_regs);
-err_map_cap_regs:
-err_get_cap_regs:
- clk_put(u3d->clk);
-err_get_clk:
- kfree(u3d);
-err_alloc_private:
-err_pdata:
- return retval;
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int mv_u3d_suspend(struct device *dev)
-{
- struct mv_u3d *u3d = dev_get_drvdata(dev);
-
- /*
- * only cable is unplugged, usb can suspend.
- * So do not care about clock_gating == 1, it is handled by
- * vbus session.
- */
- if (!u3d->clock_gating) {
- mv_u3d_controller_stop(u3d);
-
- spin_lock_irq(&u3d->lock);
- /* stop all usb activities */
- mv_u3d_stop_activity(u3d, u3d->driver);
- spin_unlock_irq(&u3d->lock);
-
- mv_u3d_disable(u3d);
- }
-
- return 0;
-}
-
-static int mv_u3d_resume(struct device *dev)
-{
- struct mv_u3d *u3d = dev_get_drvdata(dev);
- int retval;
-
- if (!u3d->clock_gating) {
- retval = mv_u3d_enable(u3d);
- if (retval)
- return retval;
-
- if (u3d->driver && u3d->softconnect) {
- mv_u3d_controller_reset(u3d);
- mv_u3d_ep0_reset(u3d);
- mv_u3d_controller_start(u3d);
- }
- }
-
- return 0;
-}
-#endif
-
-static SIMPLE_DEV_PM_OPS(mv_u3d_pm_ops, mv_u3d_suspend, mv_u3d_resume);
-
-static void mv_u3d_shutdown(struct platform_device *dev)
-{
- struct mv_u3d *u3d = platform_get_drvdata(dev);
- u32 tmp;
-
- tmp = ioread32(&u3d->op_regs->usbcmd);
- tmp &= ~MV_U3D_CMD_RUN_STOP;
- iowrite32(tmp, &u3d->op_regs->usbcmd);
-}
-
-static struct platform_driver mv_u3d_driver = {
- .probe = mv_u3d_probe,
- .remove = mv_u3d_remove,
- .shutdown = mv_u3d_shutdown,
- .driver = {
- .name = "mv-u3d",
- .pm = &mv_u3d_pm_ops,
- },
-};
-
-module_platform_driver(mv_u3d_driver);
-MODULE_ALIAS("platform:mv-u3d");
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_AUTHOR("Yu Xu <yuxu@marvell.com>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/udc/mv_udc.h b/drivers/usb/gadget/udc/mv_udc.h
deleted file mode 100644
index b3f759c0962c..000000000000
--- a/drivers/usb/gadget/udc/mv_udc.h
+++ /dev/null
@@ -1,309 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Copyright (C) 2011 Marvell International Ltd. All rights reserved.
- */
-
-#ifndef __MV_UDC_H
-#define __MV_UDC_H
-
-#define VUSBHS_MAX_PORTS 8
-
-#define DQH_ALIGNMENT 2048
-#define DTD_ALIGNMENT 64
-#define DMA_BOUNDARY 4096
-
-#define EP_DIR_IN 1
-#define EP_DIR_OUT 0
-
-#define DMA_ADDR_INVALID (~(dma_addr_t)0)
-
-#define EP0_MAX_PKT_SIZE 64
-/* ep0 transfer state */
-#define WAIT_FOR_SETUP 0
-#define DATA_STATE_XMIT 1
-#define DATA_STATE_NEED_ZLP 2
-#define WAIT_FOR_OUT_STATUS 3
-#define DATA_STATE_RECV 4
-
-#define CAPLENGTH_MASK (0xff)
-#define DCCPARAMS_DEN_MASK (0x1f)
-
-#define HCSPARAMS_PPC (0x10)
-
-/* Frame Index Register Bit Masks */
-#define USB_FRINDEX_MASKS 0x3fff
-
-/* Command Register Bit Masks */
-#define USBCMD_RUN_STOP (0x00000001)
-#define USBCMD_CTRL_RESET (0x00000002)
-#define USBCMD_SETUP_TRIPWIRE_SET (0x00002000)
-#define USBCMD_SETUP_TRIPWIRE_CLEAR (~USBCMD_SETUP_TRIPWIRE_SET)
-
-#define USBCMD_ATDTW_TRIPWIRE_SET (0x00004000)
-#define USBCMD_ATDTW_TRIPWIRE_CLEAR (~USBCMD_ATDTW_TRIPWIRE_SET)
-
-/* bit 15,3,2 are for frame list size */
-#define USBCMD_FRAME_SIZE_1024 (0x00000000) /* 000 */
-#define USBCMD_FRAME_SIZE_512 (0x00000004) /* 001 */
-#define USBCMD_FRAME_SIZE_256 (0x00000008) /* 010 */
-#define USBCMD_FRAME_SIZE_128 (0x0000000C) /* 011 */
-#define USBCMD_FRAME_SIZE_64 (0x00008000) /* 100 */
-#define USBCMD_FRAME_SIZE_32 (0x00008004) /* 101 */
-#define USBCMD_FRAME_SIZE_16 (0x00008008) /* 110 */
-#define USBCMD_FRAME_SIZE_8 (0x0000800C) /* 111 */
-
-#define EPCTRL_TX_ALL_MASK (0xFFFF0000)
-#define EPCTRL_RX_ALL_MASK (0x0000FFFF)
-
-#define EPCTRL_TX_DATA_TOGGLE_RST (0x00400000)
-#define EPCTRL_TX_EP_STALL (0x00010000)
-#define EPCTRL_RX_EP_STALL (0x00000001)
-#define EPCTRL_RX_DATA_TOGGLE_RST (0x00000040)
-#define EPCTRL_RX_ENABLE (0x00000080)
-#define EPCTRL_TX_ENABLE (0x00800000)
-#define EPCTRL_CONTROL (0x00000000)
-#define EPCTRL_ISOCHRONOUS (0x00040000)
-#define EPCTRL_BULK (0x00080000)
-#define EPCTRL_INT (0x000C0000)
-#define EPCTRL_TX_TYPE (0x000C0000)
-#define EPCTRL_RX_TYPE (0x0000000C)
-#define EPCTRL_DATA_TOGGLE_INHIBIT (0x00000020)
-#define EPCTRL_TX_EP_TYPE_SHIFT (18)
-#define EPCTRL_RX_EP_TYPE_SHIFT (2)
-
-#define EPCOMPLETE_MAX_ENDPOINTS (16)
-
-/* endpoint list address bit masks */
-#define USB_EP_LIST_ADDRESS_MASK 0xfffff800
-
-#define PORTSCX_W1C_BITS 0x2a
-#define PORTSCX_PORT_RESET 0x00000100
-#define PORTSCX_PORT_POWER 0x00001000
-#define PORTSCX_FORCE_FULL_SPEED_CONNECT 0x01000000
-#define PORTSCX_PAR_XCVR_SELECT 0xC0000000
-#define PORTSCX_PORT_FORCE_RESUME 0x00000040
-#define PORTSCX_PORT_SUSPEND 0x00000080
-#define PORTSCX_PORT_SPEED_FULL 0x00000000
-#define PORTSCX_PORT_SPEED_LOW 0x04000000
-#define PORTSCX_PORT_SPEED_HIGH 0x08000000
-#define PORTSCX_PORT_SPEED_MASK 0x0C000000
-
-/* USB MODE Register Bit Masks */
-#define USBMODE_CTRL_MODE_IDLE 0x00000000
-#define USBMODE_CTRL_MODE_DEVICE 0x00000002
-#define USBMODE_CTRL_MODE_HOST 0x00000003
-#define USBMODE_CTRL_MODE_RSV 0x00000001
-#define USBMODE_SETUP_LOCK_OFF 0x00000008
-#define USBMODE_STREAM_DISABLE 0x00000010
-
-/* USB STS Register Bit Masks */
-#define USBSTS_INT 0x00000001
-#define USBSTS_ERR 0x00000002
-#define USBSTS_PORT_CHANGE 0x00000004
-#define USBSTS_FRM_LST_ROLL 0x00000008
-#define USBSTS_SYS_ERR 0x00000010
-#define USBSTS_IAA 0x00000020
-#define USBSTS_RESET 0x00000040
-#define USBSTS_SOF 0x00000080
-#define USBSTS_SUSPEND 0x00000100
-#define USBSTS_HC_HALTED 0x00001000
-#define USBSTS_RCL 0x00002000
-#define USBSTS_PERIODIC_SCHEDULE 0x00004000
-#define USBSTS_ASYNC_SCHEDULE 0x00008000
-
-
-/* Interrupt Enable Register Bit Masks */
-#define USBINTR_INT_EN (0x00000001)
-#define USBINTR_ERR_INT_EN (0x00000002)
-#define USBINTR_PORT_CHANGE_DETECT_EN (0x00000004)
-
-#define USBINTR_ASYNC_ADV_AAE (0x00000020)
-#define USBINTR_ASYNC_ADV_AAE_ENABLE (0x00000020)
-#define USBINTR_ASYNC_ADV_AAE_DISABLE (0xFFFFFFDF)
-
-#define USBINTR_RESET_EN (0x00000040)
-#define USBINTR_SOF_UFRAME_EN (0x00000080)
-#define USBINTR_DEVICE_SUSPEND (0x00000100)
-
-#define USB_DEVICE_ADDRESS_MASK (0xfe000000)
-#define USB_DEVICE_ADDRESS_BIT_SHIFT (25)
-
-struct mv_cap_regs {
- u32 caplength_hciversion;
- u32 hcsparams; /* HC structural parameters */
- u32 hccparams; /* HC Capability Parameters*/
- u32 reserved[5];
- u32 dciversion; /* DC version number and reserved 16 bits */
- u32 dccparams; /* DC Capability Parameters */
-};
-
-struct mv_op_regs {
- u32 usbcmd; /* Command register */
- u32 usbsts; /* Status register */
- u32 usbintr; /* Interrupt enable */
- u32 frindex; /* Frame index */
- u32 reserved1[1];
- u32 deviceaddr; /* Device Address */
- u32 eplistaddr; /* Endpoint List Address */
- u32 ttctrl; /* HOST TT status and control */
- u32 burstsize; /* Programmable Burst Size */
- u32 txfilltuning; /* Host Transmit Pre-Buffer Packet Tuning */
- u32 reserved[4];
- u32 epnak; /* Endpoint NAK */
- u32 epnaken; /* Endpoint NAK Enable */
- u32 configflag; /* Configured Flag register */
- u32 portsc[VUSBHS_MAX_PORTS]; /* Port Status/Control x, x = 1..8 */
- u32 otgsc;
- u32 usbmode; /* USB Host/Device mode */
- u32 epsetupstat; /* Endpoint Setup Status */
- u32 epprime; /* Endpoint Initialize */
- u32 epflush; /* Endpoint De-initialize */
- u32 epstatus; /* Endpoint Status */
- u32 epcomplete; /* Endpoint Interrupt On Complete */
- u32 epctrlx[16]; /* Endpoint Control, where x = 0.. 15 */
- u32 mcr; /* Mux Control */
- u32 isr; /* Interrupt Status */
- u32 ier; /* Interrupt Enable */
-};
-
-struct mv_udc {
- struct usb_gadget gadget;
- struct usb_gadget_driver *driver;
- spinlock_t lock;
- struct completion *done;
- struct platform_device *dev;
- int irq;
-
- struct mv_cap_regs __iomem *cap_regs;
- struct mv_op_regs __iomem *op_regs;
- void __iomem *phy_regs;
- unsigned int max_eps;
- struct mv_dqh *ep_dqh;
- size_t ep_dqh_size;
- dma_addr_t ep_dqh_dma;
-
- struct dma_pool *dtd_pool;
- struct mv_ep *eps;
-
- struct mv_dtd *dtd_head;
- struct mv_dtd *dtd_tail;
- unsigned int dtd_entries;
-
- struct mv_req *status_req;
- struct usb_ctrlrequest local_setup_buff;
-
- unsigned int resume_state; /* USB state to resume */
- unsigned int usb_state; /* USB current state */
- unsigned int ep0_state; /* Endpoint zero state */
- unsigned int ep0_dir;
-
- unsigned int dev_addr;
- unsigned int test_mode;
-
- int errors;
- unsigned softconnect:1,
- vbus_active:1,
- remote_wakeup:1,
- softconnected:1,
- force_fs:1,
- clock_gating:1,
- active:1,
- stopped:1; /* stop bit is setted */
-
- struct work_struct vbus_work;
- struct workqueue_struct *qwork;
-
- struct usb_phy *transceiver;
-
- struct mv_usb_platform_data *pdata;
-
- /* some SOC has mutiple clock sources for USB*/
- struct clk *clk;
-};
-
-/* endpoint data structure */
-struct mv_ep {
- struct usb_ep ep;
- struct mv_udc *udc;
- struct list_head queue;
- struct mv_dqh *dqh;
- u32 direction;
- char name[14];
- unsigned stopped:1,
- wedge:1,
- ep_type:2,
- ep_num:8;
-};
-
-/* request data structure */
-struct mv_req {
- struct usb_request req;
- struct mv_dtd *dtd, *head, *tail;
- struct mv_ep *ep;
- struct list_head queue;
- unsigned int test_mode;
- unsigned dtd_count;
- unsigned mapped:1;
-};
-
-#define EP_QUEUE_HEAD_MULT_POS 30
-#define EP_QUEUE_HEAD_ZLT_SEL 0x20000000
-#define EP_QUEUE_HEAD_MAX_PKT_LEN_POS 16
-#define EP_QUEUE_HEAD_MAX_PKT_LEN(ep_info) (((ep_info)>>16)&0x07ff)
-#define EP_QUEUE_HEAD_IOS 0x00008000
-#define EP_QUEUE_HEAD_NEXT_TERMINATE 0x00000001
-#define EP_QUEUE_HEAD_IOC 0x00008000
-#define EP_QUEUE_HEAD_MULTO 0x00000C00
-#define EP_QUEUE_HEAD_STATUS_HALT 0x00000040
-#define EP_QUEUE_HEAD_STATUS_ACTIVE 0x00000080
-#define EP_QUEUE_CURRENT_OFFSET_MASK 0x00000FFF
-#define EP_QUEUE_HEAD_NEXT_POINTER_MASK 0xFFFFFFE0
-#define EP_QUEUE_FRINDEX_MASK 0x000007FF
-#define EP_MAX_LENGTH_TRANSFER 0x4000
-
-struct mv_dqh {
- /* Bits 16..26 Bit 15 is Interrupt On Setup */
- u32 max_packet_length;
- u32 curr_dtd_ptr; /* Current dTD Pointer */
- u32 next_dtd_ptr; /* Next dTD Pointer */
- /* Total bytes (16..30), IOC (15), INT (8), STS (0-7) */
- u32 size_ioc_int_sts;
- u32 buff_ptr0; /* Buffer pointer Page 0 (12-31) */
- u32 buff_ptr1; /* Buffer pointer Page 1 (12-31) */
- u32 buff_ptr2; /* Buffer pointer Page 2 (12-31) */
- u32 buff_ptr3; /* Buffer pointer Page 3 (12-31) */
- u32 buff_ptr4; /* Buffer pointer Page 4 (12-31) */
- u32 reserved1;
- /* 8 bytes of setup data that follows the Setup PID */
- u8 setup_buffer[8];
- u32 reserved2[4];
-};
-
-
-#define DTD_NEXT_TERMINATE (0x00000001)
-#define DTD_IOC (0x00008000)
-#define DTD_STATUS_ACTIVE (0x00000080)
-#define DTD_STATUS_HALTED (0x00000040)
-#define DTD_STATUS_DATA_BUFF_ERR (0x00000020)
-#define DTD_STATUS_TRANSACTION_ERR (0x00000008)
-#define DTD_RESERVED_FIELDS (0x00007F00)
-#define DTD_ERROR_MASK (0x68)
-#define DTD_ADDR_MASK (0xFFFFFFE0)
-#define DTD_PACKET_SIZE 0x7FFF0000
-#define DTD_LENGTH_BIT_POS (16)
-
-struct mv_dtd {
- u32 dtd_next;
- u32 size_ioc_sts;
- u32 buff_ptr0; /* Buffer pointer Page 0 */
- u32 buff_ptr1; /* Buffer pointer Page 1 */
- u32 buff_ptr2; /* Buffer pointer Page 2 */
- u32 buff_ptr3; /* Buffer pointer Page 3 */
- u32 buff_ptr4; /* Buffer pointer Page 4 */
- u32 scratch_ptr;
- /* 32 bytes */
- dma_addr_t td_dma; /* dma address for this td */
- struct mv_dtd *next_dtd_virt;
-};
-
-#endif
diff --git a/drivers/usb/gadget/udc/mv_udc_core.c b/drivers/usb/gadget/udc/mv_udc_core.c
deleted file mode 100644
index ff103e6b3048..000000000000
--- a/drivers/usb/gadget/udc/mv_udc_core.c
+++ /dev/null
@@ -1,2426 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Copyright (C) 2011 Marvell International Ltd. All rights reserved.
- * Author: Chao Xie <chao.xie@marvell.com>
- * Neil Zhang <zhangwm@marvell.com>
- */
-
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/dma-mapping.h>
-#include <linux/dmapool.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/ioport.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/errno.h>
-#include <linux/err.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/usb/otg.h>
-#include <linux/pm.h>
-#include <linux/io.h>
-#include <linux/irq.h>
-#include <linux/platform_device.h>
-#include <linux/clk.h>
-#include <linux/platform_data/mv_usb.h>
-#include <linux/unaligned.h>
-
-#include "mv_udc.h"
-
-#define DRIVER_DESC "Marvell PXA USB Device Controller driver"
-
-#define ep_dir(ep) (((ep)->ep_num == 0) ? \
- ((ep)->udc->ep0_dir) : ((ep)->direction))
-
-/* timeout value -- usec */
-#define RESET_TIMEOUT 10000
-#define FLUSH_TIMEOUT 10000
-#define EPSTATUS_TIMEOUT 10000
-#define PRIME_TIMEOUT 10000
-#define READSAFE_TIMEOUT 1000
-
-#define LOOPS_USEC_SHIFT 1
-#define LOOPS_USEC (1 << LOOPS_USEC_SHIFT)
-#define LOOPS(timeout) ((timeout) >> LOOPS_USEC_SHIFT)
-
-static DECLARE_COMPLETION(release_done);
-
-static const char driver_name[] = "mv_udc";
-
-static void nuke(struct mv_ep *ep, int status);
-static void stop_activity(struct mv_udc *udc, struct usb_gadget_driver *driver);
-
-/* for endpoint 0 operations */
-static const struct usb_endpoint_descriptor mv_ep0_desc = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
- .bEndpointAddress = 0,
- .bmAttributes = USB_ENDPOINT_XFER_CONTROL,
- .wMaxPacketSize = EP0_MAX_PKT_SIZE,
-};
-
-static void ep0_reset(struct mv_udc *udc)
-{
- struct mv_ep *ep;
- u32 epctrlx;
- int i = 0;
-
- /* ep0 in and out */
- for (i = 0; i < 2; i++) {
- ep = &udc->eps[i];
- ep->udc = udc;
-
- /* ep0 dQH */
- ep->dqh = &udc->ep_dqh[i];
-
- /* configure ep0 endpoint capabilities in dQH */
- ep->dqh->max_packet_length =
- (EP0_MAX_PKT_SIZE << EP_QUEUE_HEAD_MAX_PKT_LEN_POS)
- | EP_QUEUE_HEAD_IOS;
-
- ep->dqh->next_dtd_ptr = EP_QUEUE_HEAD_NEXT_TERMINATE;
-
- epctrlx = readl(&udc->op_regs->epctrlx[0]);
- if (i) { /* TX */
- epctrlx |= EPCTRL_TX_ENABLE
- | (USB_ENDPOINT_XFER_CONTROL
- << EPCTRL_TX_EP_TYPE_SHIFT);
-
- } else { /* RX */
- epctrlx |= EPCTRL_RX_ENABLE
- | (USB_ENDPOINT_XFER_CONTROL
- << EPCTRL_RX_EP_TYPE_SHIFT);
- }
-
- writel(epctrlx, &udc->op_regs->epctrlx[0]);
- }
-}
-
-/* protocol ep0 stall, will automatically be cleared on new transaction */
-static void ep0_stall(struct mv_udc *udc)
-{
- u32 epctrlx;
-
- /* set TX and RX to stall */
- epctrlx = readl(&udc->op_regs->epctrlx[0]);
- epctrlx |= EPCTRL_RX_EP_STALL | EPCTRL_TX_EP_STALL;
- writel(epctrlx, &udc->op_regs->epctrlx[0]);
-
- /* update ep0 state */
- udc->ep0_state = WAIT_FOR_SETUP;
- udc->ep0_dir = EP_DIR_OUT;
-}
-
-static int process_ep_req(struct mv_udc *udc, int index,
- struct mv_req *curr_req)
-{
- struct mv_dtd *curr_dtd;
- struct mv_dqh *curr_dqh;
- int actual, remaining_length;
- int i, direction;
- int retval = 0;
- u32 errors;
- u32 bit_pos;
-
- curr_dqh = &udc->ep_dqh[index];
- direction = index % 2;
-
- curr_dtd = curr_req->head;
- actual = curr_req->req.length;
-
- for (i = 0; i < curr_req->dtd_count; i++) {
- if (curr_dtd->size_ioc_sts & DTD_STATUS_ACTIVE) {
- dev_dbg(&udc->dev->dev, "%s, dTD not completed\n",
- udc->eps[index].name);
- return 1;
- }
-
- errors = curr_dtd->size_ioc_sts & DTD_ERROR_MASK;
- if (!errors) {
- remaining_length =
- (curr_dtd->size_ioc_sts & DTD_PACKET_SIZE)
- >> DTD_LENGTH_BIT_POS;
- actual -= remaining_length;
-
- if (remaining_length) {
- if (direction) {
- dev_dbg(&udc->dev->dev,
- "TX dTD remains data\n");
- retval = -EPROTO;
- break;
- } else
- break;
- }
- } else {
- dev_info(&udc->dev->dev,
- "complete_tr error: ep=%d %s: error = 0x%x\n",
- index >> 1, direction ? "SEND" : "RECV",
- errors);
- if (errors & DTD_STATUS_HALTED) {
- /* Clear the errors and Halt condition */
- curr_dqh->size_ioc_int_sts &= ~errors;
- retval = -EPIPE;
- } else if (errors & DTD_STATUS_DATA_BUFF_ERR) {
- retval = -EPROTO;
- } else if (errors & DTD_STATUS_TRANSACTION_ERR) {
- retval = -EILSEQ;
- }
- }
- if (i != curr_req->dtd_count - 1)
- curr_dtd = (struct mv_dtd *)curr_dtd->next_dtd_virt;
- }
- if (retval)
- return retval;
-
- if (direction == EP_DIR_OUT)
- bit_pos = 1 << curr_req->ep->ep_num;
- else
- bit_pos = 1 << (16 + curr_req->ep->ep_num);
-
- while (curr_dqh->curr_dtd_ptr == curr_dtd->td_dma) {
- if (curr_dtd->dtd_next == EP_QUEUE_HEAD_NEXT_TERMINATE) {
- while (readl(&udc->op_regs->epstatus) & bit_pos)
- udelay(1);
- break;
- }
- udelay(1);
- }
-
- curr_req->req.actual = actual;
-
- return 0;
-}
-
-/*
- * done() - retire a request; caller blocked irqs
- * @status : request status to be set, only works when
- * request is still in progress.
- */
-static void done(struct mv_ep *ep, struct mv_req *req, int status)
- __releases(&ep->udc->lock)
- __acquires(&ep->udc->lock)
-{
- struct mv_udc *udc = NULL;
- unsigned char stopped = ep->stopped;
- struct mv_dtd *curr_td, *next_td;
- int j;
-
- udc = (struct mv_udc *)ep->udc;
- /* Removed the req from fsl_ep->queue */
- list_del_init(&req->queue);
-
- /* req.status should be set as -EINPROGRESS in ep_queue() */
- if (req->req.status == -EINPROGRESS)
- req->req.status = status;
- else
- status = req->req.status;
-
- /* Free dtd for the request */
- next_td = req->head;
- for (j = 0; j < req->dtd_count; j++) {
- curr_td = next_td;
- if (j != req->dtd_count - 1)
- next_td = curr_td->next_dtd_virt;
- dma_pool_free(udc->dtd_pool, curr_td, curr_td->td_dma);
- }
-
- usb_gadget_unmap_request(&udc->gadget, &req->req, ep_dir(ep));
-
- if (status && (status != -ESHUTDOWN))
- dev_info(&udc->dev->dev, "complete %s req %p stat %d len %u/%u",
- ep->ep.name, &req->req, status,
- req->req.actual, req->req.length);
-
- ep->stopped = 1;
-
- spin_unlock(&ep->udc->lock);
-
- usb_gadget_giveback_request(&ep->ep, &req->req);
-
- spin_lock(&ep->udc->lock);
- ep->stopped = stopped;
-}
-
-static int queue_dtd(struct mv_ep *ep, struct mv_req *req)
-{
- struct mv_udc *udc;
- struct mv_dqh *dqh;
- u32 bit_pos, direction;
- u32 usbcmd, epstatus;
- unsigned int loops;
- int retval = 0;
-
- udc = ep->udc;
- direction = ep_dir(ep);
- dqh = &(udc->ep_dqh[ep->ep_num * 2 + direction]);
- bit_pos = 1 << (((direction == EP_DIR_OUT) ? 0 : 16) + ep->ep_num);
-
- /* check if the pipe is empty */
- if (!(list_empty(&ep->queue))) {
- struct mv_req *lastreq;
- lastreq = list_entry(ep->queue.prev, struct mv_req, queue);
- lastreq->tail->dtd_next =
- req->head->td_dma & EP_QUEUE_HEAD_NEXT_POINTER_MASK;
-
- wmb();
-
- if (readl(&udc->op_regs->epprime) & bit_pos)
- goto done;
-
- loops = LOOPS(READSAFE_TIMEOUT);
- while (1) {
- /* start with setting the semaphores */
- usbcmd = readl(&udc->op_regs->usbcmd);
- usbcmd |= USBCMD_ATDTW_TRIPWIRE_SET;
- writel(usbcmd, &udc->op_regs->usbcmd);
-
- /* read the endpoint status */
- epstatus = readl(&udc->op_regs->epstatus) & bit_pos;
-
- /*
- * Reread the ATDTW semaphore bit to check if it is
- * cleared. When hardware see a hazard, it will clear
- * the bit or else we remain set to 1 and we can
- * proceed with priming of endpoint if not already
- * primed.
- */
- if (readl(&udc->op_regs->usbcmd)
- & USBCMD_ATDTW_TRIPWIRE_SET)
- break;
-
- loops--;
- if (loops == 0) {
- dev_err(&udc->dev->dev,
- "Timeout for ATDTW_TRIPWIRE...\n");
- retval = -ETIME;
- goto done;
- }
- udelay(LOOPS_USEC);
- }
-
- /* Clear the semaphore */
- usbcmd = readl(&udc->op_regs->usbcmd);
- usbcmd &= USBCMD_ATDTW_TRIPWIRE_CLEAR;
- writel(usbcmd, &udc->op_regs->usbcmd);
-
- if (epstatus)
- goto done;
- }
-
- /* Write dQH next pointer and terminate bit to 0 */
- dqh->next_dtd_ptr = req->head->td_dma
- & EP_QUEUE_HEAD_NEXT_POINTER_MASK;
-
- /* 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 occur before priming. */
- wmb();
-
- /* Prime the Endpoint */
- writel(bit_pos, &udc->op_regs->epprime);
-
-done:
- return retval;
-}
-
-static struct mv_dtd *build_dtd(struct mv_req *req, unsigned *length,
- dma_addr_t *dma, int *is_last)
-{
- struct mv_dtd *dtd;
- struct mv_udc *udc;
- struct mv_dqh *dqh;
- u32 temp, mult = 0;
-
- /* how big will this transfer be? */
- if (usb_endpoint_xfer_isoc(req->ep->ep.desc)) {
- dqh = req->ep->dqh;
- mult = (dqh->max_packet_length >> EP_QUEUE_HEAD_MULT_POS)
- & 0x3;
- *length = min(req->req.length - req->req.actual,
- (unsigned)(mult * req->ep->ep.maxpacket));
- } else
- *length = min(req->req.length - req->req.actual,
- (unsigned)EP_MAX_LENGTH_TRANSFER);
-
- udc = req->ep->udc;
-
- /*
- * Be careful that no _GFP_HIGHMEM is set,
- * or we can not use dma_to_virt
- */
- dtd = dma_pool_alloc(udc->dtd_pool, GFP_ATOMIC, dma);
- if (dtd == NULL)
- return dtd;
-
- dtd->td_dma = *dma;
- /* initialize buffer page pointers */
- temp = (u32)(req->req.dma + req->req.actual);
- dtd->buff_ptr0 = cpu_to_le32(temp);
- temp &= ~0xFFF;
- dtd->buff_ptr1 = cpu_to_le32(temp + 0x1000);
- dtd->buff_ptr2 = cpu_to_le32(temp + 0x2000);
- dtd->buff_ptr3 = cpu_to_le32(temp + 0x3000);
- dtd->buff_ptr4 = cpu_to_le32(temp + 0x4000);
-
- req->req.actual += *length;
-
- /* zlp is needed if req->req.zero is set */
- if (req->req.zero) {
- if (*length == 0 || (*length % req->ep->ep.maxpacket) != 0)
- *is_last = 1;
- else
- *is_last = 0;
- } else if (req->req.length == req->req.actual)
- *is_last = 1;
- else
- *is_last = 0;
-
- /* Fill in the transfer size; set active bit */
- temp = ((*length << DTD_LENGTH_BIT_POS) | DTD_STATUS_ACTIVE);
-
- /* Enable interrupt for the last dtd of a request */
- if (*is_last && !req->req.no_interrupt)
- temp |= DTD_IOC;
-
- temp |= mult << 10;
-
- dtd->size_ioc_sts = temp;
-
- mb();
-
- return dtd;
-}
-
-/* generate dTD linked list for a request */
-static int req_to_dtd(struct mv_req *req)
-{
- unsigned count;
- int is_last, is_first = 1;
- struct mv_dtd *dtd, *last_dtd = NULL;
- dma_addr_t dma;
-
- do {
- dtd = build_dtd(req, &count, &dma, &is_last);
- if (dtd == NULL)
- return -ENOMEM;
-
- if (is_first) {
- is_first = 0;
- req->head = dtd;
- } else {
- last_dtd->dtd_next = dma;
- last_dtd->next_dtd_virt = dtd;
- }
- last_dtd = dtd;
- req->dtd_count++;
- } while (!is_last);
-
- /* set terminate bit to 1 for the last dTD */
- dtd->dtd_next = DTD_NEXT_TERMINATE;
-
- req->tail = dtd;
-
- return 0;
-}
-
-static int mv_ep_enable(struct usb_ep *_ep,
- const struct usb_endpoint_descriptor *desc)
-{
- struct mv_udc *udc;
- struct mv_ep *ep;
- struct mv_dqh *dqh;
- u16 max = 0;
- u32 bit_pos, epctrlx, direction;
- const unsigned char zlt = 1;
- unsigned char ios, mult;
- unsigned long flags;
-
- ep = container_of(_ep, struct mv_ep, ep);
- udc = ep->udc;
-
- if (!_ep || !desc
- || desc->bDescriptorType != USB_DT_ENDPOINT)
- return -EINVAL;
-
- if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN)
- return -ESHUTDOWN;
-
- direction = ep_dir(ep);
- max = usb_endpoint_maxp(desc);
-
- /*
- * disable HW zero length termination select
- * driver handles zero length packet through req->req.zero
- */
- bit_pos = 1 << ((direction == EP_DIR_OUT ? 0 : 16) + ep->ep_num);
-
- /* Check if the Endpoint is Primed */
- if ((readl(&udc->op_regs->epprime) & bit_pos)
- || (readl(&udc->op_regs->epstatus) & bit_pos)) {
- dev_info(&udc->dev->dev,
- "ep=%d %s: Init ERROR: ENDPTPRIME=0x%x,"
- " ENDPTSTATUS=0x%x, bit_pos=0x%x\n",
- (unsigned)ep->ep_num, direction ? "SEND" : "RECV",
- (unsigned)readl(&udc->op_regs->epprime),
- (unsigned)readl(&udc->op_regs->epstatus),
- (unsigned)bit_pos);
- goto en_done;
- }
-
- /* Set the max packet length, interrupt on Setup and Mult fields */
- ios = 0;
- mult = 0;
- switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
- case USB_ENDPOINT_XFER_BULK:
- case USB_ENDPOINT_XFER_INT:
- break;
- case USB_ENDPOINT_XFER_CONTROL:
- ios = 1;
- break;
- case USB_ENDPOINT_XFER_ISOC:
- /* Calculate transactions needed for high bandwidth iso */
- mult = usb_endpoint_maxp_mult(desc);
- /* 3 transactions at most */
- if (mult > 3)
- goto en_done;
- break;
- default:
- goto en_done;
- }
-
- spin_lock_irqsave(&udc->lock, flags);
- /* Get the endpoint queue head address */
- dqh = ep->dqh;
- dqh->max_packet_length = (max << EP_QUEUE_HEAD_MAX_PKT_LEN_POS)
- | (mult << EP_QUEUE_HEAD_MULT_POS)
- | (zlt ? EP_QUEUE_HEAD_ZLT_SEL : 0)
- | (ios ? EP_QUEUE_HEAD_IOS : 0);
- dqh->next_dtd_ptr = 1;
- dqh->size_ioc_int_sts = 0;
-
- ep->ep.maxpacket = max;
- ep->ep.desc = desc;
- ep->stopped = 0;
-
- /* Enable the endpoint for Rx or Tx and set the endpoint type */
- epctrlx = readl(&udc->op_regs->epctrlx[ep->ep_num]);
- if (direction == EP_DIR_IN) {
- epctrlx &= ~EPCTRL_TX_ALL_MASK;
- epctrlx |= EPCTRL_TX_ENABLE | EPCTRL_TX_DATA_TOGGLE_RST
- | ((desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
- << EPCTRL_TX_EP_TYPE_SHIFT);
- } else {
- epctrlx &= ~EPCTRL_RX_ALL_MASK;
- epctrlx |= EPCTRL_RX_ENABLE | EPCTRL_RX_DATA_TOGGLE_RST
- | ((desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
- << EPCTRL_RX_EP_TYPE_SHIFT);
- }
- writel(epctrlx, &udc->op_regs->epctrlx[ep->ep_num]);
-
- /*
- * Implement Guideline (GL# USB-7) The unused endpoint type must
- * be programmed to bulk.
- */
- epctrlx = readl(&udc->op_regs->epctrlx[ep->ep_num]);
- if ((epctrlx & EPCTRL_RX_ENABLE) == 0) {
- epctrlx |= (USB_ENDPOINT_XFER_BULK
- << EPCTRL_RX_EP_TYPE_SHIFT);
- writel(epctrlx, &udc->op_regs->epctrlx[ep->ep_num]);
- }
-
- epctrlx = readl(&udc->op_regs->epctrlx[ep->ep_num]);
- if ((epctrlx & EPCTRL_TX_ENABLE) == 0) {
- epctrlx |= (USB_ENDPOINT_XFER_BULK
- << EPCTRL_TX_EP_TYPE_SHIFT);
- writel(epctrlx, &udc->op_regs->epctrlx[ep->ep_num]);
- }
-
- spin_unlock_irqrestore(&udc->lock, flags);
-
- return 0;
-en_done:
- return -EINVAL;
-}
-
-static int mv_ep_disable(struct usb_ep *_ep)
-{
- struct mv_udc *udc;
- struct mv_ep *ep;
- struct mv_dqh *dqh;
- u32 epctrlx, direction;
- unsigned long flags;
-
- ep = container_of(_ep, struct mv_ep, ep);
- if ((_ep == NULL) || !ep->ep.desc)
- return -EINVAL;
-
- udc = ep->udc;
-
- /* Get the endpoint queue head address */
- dqh = ep->dqh;
-
- spin_lock_irqsave(&udc->lock, flags);
-
- direction = ep_dir(ep);
-
- /* Reset the max packet length and the interrupt on Setup */
- dqh->max_packet_length = 0;
-
- /* Disable the endpoint for Rx or Tx and reset the endpoint type */
- epctrlx = readl(&udc->op_regs->epctrlx[ep->ep_num]);
- epctrlx &= ~((direction == EP_DIR_IN)
- ? (EPCTRL_TX_ENABLE | EPCTRL_TX_TYPE)
- : (EPCTRL_RX_ENABLE | EPCTRL_RX_TYPE));
- writel(epctrlx, &udc->op_regs->epctrlx[ep->ep_num]);
-
- /* nuke all pending requests (does flush) */
- nuke(ep, -ESHUTDOWN);
-
- ep->ep.desc = NULL;
- ep->stopped = 1;
-
- spin_unlock_irqrestore(&udc->lock, flags);
-
- return 0;
-}
-
-static struct usb_request *
-mv_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
-{
- struct mv_req *req;
-
- req = kzalloc(sizeof *req, gfp_flags);
- if (!req)
- return NULL;
-
- req->req.dma = DMA_ADDR_INVALID;
- INIT_LIST_HEAD(&req->queue);
-
- return &req->req;
-}
-
-static void mv_free_request(struct usb_ep *_ep, struct usb_request *_req)
-{
- struct mv_req *req = NULL;
-
- req = container_of(_req, struct mv_req, req);
-
- if (_req)
- kfree(req);
-}
-
-static void mv_ep_fifo_flush(struct usb_ep *_ep)
-{
- struct mv_udc *udc;
- u32 bit_pos, direction;
- struct mv_ep *ep;
- unsigned int loops;
-
- if (!_ep)
- return;
-
- ep = container_of(_ep, struct mv_ep, ep);
- if (!ep->ep.desc)
- return;
-
- udc = ep->udc;
- direction = ep_dir(ep);
-
- if (ep->ep_num == 0)
- bit_pos = (1 << 16) | 1;
- else if (direction == EP_DIR_OUT)
- bit_pos = 1 << ep->ep_num;
- else
- bit_pos = 1 << (16 + ep->ep_num);
-
- loops = LOOPS(EPSTATUS_TIMEOUT);
- do {
- unsigned int inter_loops;
-
- if (loops == 0) {
- dev_err(&udc->dev->dev,
- "TIMEOUT for ENDPTSTATUS=0x%x, bit_pos=0x%x\n",
- (unsigned)readl(&udc->op_regs->epstatus),
- (unsigned)bit_pos);
- return;
- }
- /* Write 1 to the Flush register */
- writel(bit_pos, &udc->op_regs->epflush);
-
- /* Wait until flushing completed */
- inter_loops = LOOPS(FLUSH_TIMEOUT);
- while (readl(&udc->op_regs->epflush)) {
- /*
- * ENDPTFLUSH bit should be cleared to indicate this
- * operation is complete
- */
- if (inter_loops == 0) {
- dev_err(&udc->dev->dev,
- "TIMEOUT for ENDPTFLUSH=0x%x,"
- "bit_pos=0x%x\n",
- (unsigned)readl(&udc->op_regs->epflush),
- (unsigned)bit_pos);
- return;
- }
- inter_loops--;
- udelay(LOOPS_USEC);
- }
- loops--;
- } while (readl(&udc->op_regs->epstatus) & bit_pos);
-}
-
-/* queues (submits) an I/O request to an endpoint */
-static int
-mv_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
-{
- struct mv_ep *ep = container_of(_ep, struct mv_ep, ep);
- struct mv_req *req = container_of(_req, struct mv_req, req);
- struct mv_udc *udc = ep->udc;
- unsigned long flags;
- int retval;
-
- /* catch various bogus parameters */
- if (!_req || !req->req.complete || !req->req.buf
- || !list_empty(&req->queue)) {
- dev_err(&udc->dev->dev, "%s, bad params", __func__);
- return -EINVAL;
- }
- if (unlikely(!_ep || !ep->ep.desc)) {
- dev_err(&udc->dev->dev, "%s, bad ep", __func__);
- return -EINVAL;
- }
-
- udc = ep->udc;
- if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN)
- return -ESHUTDOWN;
-
- req->ep = ep;
-
- /* map virtual address to hardware */
- retval = usb_gadget_map_request(&udc->gadget, _req, ep_dir(ep));
- if (retval)
- return retval;
-
- req->req.status = -EINPROGRESS;
- req->req.actual = 0;
- req->dtd_count = 0;
-
- spin_lock_irqsave(&udc->lock, flags);
-
- /* build dtds and push them to device queue */
- if (!req_to_dtd(req)) {
- retval = queue_dtd(ep, req);
- if (retval) {
- spin_unlock_irqrestore(&udc->lock, flags);
- dev_err(&udc->dev->dev, "Failed to queue dtd\n");
- goto err_unmap_dma;
- }
- } else {
- spin_unlock_irqrestore(&udc->lock, flags);
- dev_err(&udc->dev->dev, "Failed to dma_pool_alloc\n");
- retval = -ENOMEM;
- goto err_unmap_dma;
- }
-
- /* Update ep0 state */
- if (ep->ep_num == 0)
- udc->ep0_state = DATA_STATE_XMIT;
-
- /* irq handler advances the queue */
- list_add_tail(&req->queue, &ep->queue);
- spin_unlock_irqrestore(&udc->lock, flags);
-
- return 0;
-
-err_unmap_dma:
- usb_gadget_unmap_request(&udc->gadget, _req, ep_dir(ep));
-
- return retval;
-}
-
-static void mv_prime_ep(struct mv_ep *ep, struct mv_req *req)
-{
- struct mv_dqh *dqh = ep->dqh;
- u32 bit_pos;
-
- /* Write dQH next pointer and terminate bit to 0 */
- dqh->next_dtd_ptr = req->head->td_dma
- & EP_QUEUE_HEAD_NEXT_POINTER_MASK;
-
- /* 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. */
- wmb();
-
- bit_pos = 1 << (((ep_dir(ep) == EP_DIR_OUT) ? 0 : 16) + ep->ep_num);
-
- /* Prime the Endpoint */
- writel(bit_pos, &ep->udc->op_regs->epprime);
-}
-
-/* dequeues (cancels, unlinks) an I/O request from an endpoint */
-static int mv_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
-{
- struct mv_ep *ep = container_of(_ep, struct mv_ep, ep);
- struct mv_req *req = NULL, *iter;
- struct mv_udc *udc = ep->udc;
- unsigned long flags;
- int stopped, ret = 0;
- u32 epctrlx;
-
- if (!_ep || !_req)
- return -EINVAL;
-
- spin_lock_irqsave(&ep->udc->lock, flags);
- stopped = ep->stopped;
-
- /* Stop the ep before we deal with the queue */
- ep->stopped = 1;
- epctrlx = readl(&udc->op_regs->epctrlx[ep->ep_num]);
- if (ep_dir(ep) == EP_DIR_IN)
- epctrlx &= ~EPCTRL_TX_ENABLE;
- else
- epctrlx &= ~EPCTRL_RX_ENABLE;
- writel(epctrlx, &udc->op_regs->epctrlx[ep->ep_num]);
-
- /* make sure it's actually queued on this endpoint */
- list_for_each_entry(iter, &ep->queue, queue) {
- if (&iter->req != _req)
- continue;
- req = iter;
- break;
- }
- if (!req) {
- ret = -EINVAL;
- goto out;
- }
-
- /* The request is in progress, or completed but not dequeued */
- if (ep->queue.next == &req->queue) {
- _req->status = -ECONNRESET;
- mv_ep_fifo_flush(_ep); /* flush current transfer */
-
- /* The request isn't the last request in this ep queue */
- if (req->queue.next != &ep->queue) {
- struct mv_req *next_req;
-
- next_req = list_entry(req->queue.next,
- struct mv_req, queue);
-
- /* Point the QH to the first TD of next request */
- mv_prime_ep(ep, next_req);
- } else {
- struct mv_dqh *qh;
-
- qh = ep->dqh;
- qh->next_dtd_ptr = 1;
- qh->size_ioc_int_sts = 0;
- }
-
- /* The request hasn't been processed, patch up the TD chain */
- } else {
- struct mv_req *prev_req;
-
- prev_req = list_entry(req->queue.prev, struct mv_req, queue);
- writel(readl(&req->tail->dtd_next),
- &prev_req->tail->dtd_next);
-
- }
-
- done(ep, req, -ECONNRESET);
-
- /* Enable EP */
-out:
- epctrlx = readl(&udc->op_regs->epctrlx[ep->ep_num]);
- if (ep_dir(ep) == EP_DIR_IN)
- epctrlx |= EPCTRL_TX_ENABLE;
- else
- epctrlx |= EPCTRL_RX_ENABLE;
- writel(epctrlx, &udc->op_regs->epctrlx[ep->ep_num]);
- ep->stopped = stopped;
-
- spin_unlock_irqrestore(&ep->udc->lock, flags);
- return ret;
-}
-
-static void ep_set_stall(struct mv_udc *udc, u8 ep_num, u8 direction, int stall)
-{
- u32 epctrlx;
-
- epctrlx = readl(&udc->op_regs->epctrlx[ep_num]);
-
- if (stall) {
- if (direction == EP_DIR_IN)
- epctrlx |= EPCTRL_TX_EP_STALL;
- else
- epctrlx |= EPCTRL_RX_EP_STALL;
- } else {
- if (direction == EP_DIR_IN) {
- epctrlx &= ~EPCTRL_TX_EP_STALL;
- epctrlx |= EPCTRL_TX_DATA_TOGGLE_RST;
- } else {
- epctrlx &= ~EPCTRL_RX_EP_STALL;
- epctrlx |= EPCTRL_RX_DATA_TOGGLE_RST;
- }
- }
- writel(epctrlx, &udc->op_regs->epctrlx[ep_num]);
-}
-
-static int ep_is_stall(struct mv_udc *udc, u8 ep_num, u8 direction)
-{
- u32 epctrlx;
-
- epctrlx = readl(&udc->op_regs->epctrlx[ep_num]);
-
- if (direction == EP_DIR_OUT)
- return (epctrlx & EPCTRL_RX_EP_STALL) ? 1 : 0;
- else
- return (epctrlx & EPCTRL_TX_EP_STALL) ? 1 : 0;
-}
-
-static int mv_ep_set_halt_wedge(struct usb_ep *_ep, int halt, int wedge)
-{
- struct mv_ep *ep;
- unsigned long flags;
- int status = 0;
- struct mv_udc *udc;
-
- ep = container_of(_ep, struct mv_ep, ep);
- udc = ep->udc;
- if (!_ep || !ep->ep.desc) {
- status = -EINVAL;
- goto out;
- }
-
- if (ep->ep.desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
- status = -EOPNOTSUPP;
- goto out;
- }
-
- /*
- * Attempt to halt IN ep will fail if any transfer requests
- * are still queue
- */
- if (halt && (ep_dir(ep) == EP_DIR_IN) && !list_empty(&ep->queue)) {
- status = -EAGAIN;
- goto out;
- }
-
- spin_lock_irqsave(&ep->udc->lock, flags);
- ep_set_stall(udc, ep->ep_num, ep_dir(ep), halt);
- if (halt && wedge)
- ep->wedge = 1;
- else if (!halt)
- ep->wedge = 0;
- spin_unlock_irqrestore(&ep->udc->lock, flags);
-
- if (ep->ep_num == 0) {
- udc->ep0_state = WAIT_FOR_SETUP;
- udc->ep0_dir = EP_DIR_OUT;
- }
-out:
- return status;
-}
-
-static int mv_ep_set_halt(struct usb_ep *_ep, int halt)
-{
- return mv_ep_set_halt_wedge(_ep, halt, 0);
-}
-
-static int mv_ep_set_wedge(struct usb_ep *_ep)
-{
- return mv_ep_set_halt_wedge(_ep, 1, 1);
-}
-
-static const struct usb_ep_ops mv_ep_ops = {
- .enable = mv_ep_enable,
- .disable = mv_ep_disable,
-
- .alloc_request = mv_alloc_request,
- .free_request = mv_free_request,
-
- .queue = mv_ep_queue,
- .dequeue = mv_ep_dequeue,
-
- .set_wedge = mv_ep_set_wedge,
- .set_halt = mv_ep_set_halt,
- .fifo_flush = mv_ep_fifo_flush, /* flush fifo */
-};
-
-static int udc_clock_enable(struct mv_udc *udc)
-{
- return clk_prepare_enable(udc->clk);
-}
-
-static void udc_clock_disable(struct mv_udc *udc)
-{
- clk_disable_unprepare(udc->clk);
-}
-
-static void udc_stop(struct mv_udc *udc)
-{
- u32 tmp;
-
- /* Disable interrupts */
- tmp = readl(&udc->op_regs->usbintr);
- tmp &= ~(USBINTR_INT_EN | USBINTR_ERR_INT_EN |
- USBINTR_PORT_CHANGE_DETECT_EN | USBINTR_RESET_EN);
- writel(tmp, &udc->op_regs->usbintr);
-
- udc->stopped = 1;
-
- /* Reset the Run the bit in the command register to stop VUSB */
- tmp = readl(&udc->op_regs->usbcmd);
- tmp &= ~USBCMD_RUN_STOP;
- writel(tmp, &udc->op_regs->usbcmd);
-}
-
-static void udc_start(struct mv_udc *udc)
-{
- u32 usbintr;
-
- usbintr = USBINTR_INT_EN | USBINTR_ERR_INT_EN
- | USBINTR_PORT_CHANGE_DETECT_EN
- | USBINTR_RESET_EN | USBINTR_DEVICE_SUSPEND;
- /* Enable interrupts */
- writel(usbintr, &udc->op_regs->usbintr);
-
- udc->stopped = 0;
-
- /* Set the Run bit in the command register */
- writel(USBCMD_RUN_STOP, &udc->op_regs->usbcmd);
-}
-
-static int udc_reset(struct mv_udc *udc)
-{
- unsigned int loops;
- u32 tmp, portsc;
-
- /* Stop the controller */
- tmp = readl(&udc->op_regs->usbcmd);
- tmp &= ~USBCMD_RUN_STOP;
- writel(tmp, &udc->op_regs->usbcmd);
-
- /* Reset the controller to get default values */
- writel(USBCMD_CTRL_RESET, &udc->op_regs->usbcmd);
-
- /* wait for reset to complete */
- loops = LOOPS(RESET_TIMEOUT);
- while (readl(&udc->op_regs->usbcmd) & USBCMD_CTRL_RESET) {
- if (loops == 0) {
- dev_err(&udc->dev->dev,
- "Wait for RESET completed TIMEOUT\n");
- return -ETIMEDOUT;
- }
- loops--;
- udelay(LOOPS_USEC);
- }
-
- /* set controller to device mode */
- tmp = readl(&udc->op_regs->usbmode);
- tmp |= USBMODE_CTRL_MODE_DEVICE;
-
- /* turn setup lockout off, require setup tripwire in usbcmd */
- tmp |= USBMODE_SETUP_LOCK_OFF;
-
- writel(tmp, &udc->op_regs->usbmode);
-
- writel(0x0, &udc->op_regs->epsetupstat);
-
- /* Configure the Endpoint List Address */
- writel(udc->ep_dqh_dma & USB_EP_LIST_ADDRESS_MASK,
- &udc->op_regs->eplistaddr);
-
- portsc = readl(&udc->op_regs->portsc[0]);
- if (readl(&udc->cap_regs->hcsparams) & HCSPARAMS_PPC)
- portsc &= (~PORTSCX_W1C_BITS | ~PORTSCX_PORT_POWER);
-
- if (udc->force_fs)
- portsc |= PORTSCX_FORCE_FULL_SPEED_CONNECT;
- else
- portsc &= (~PORTSCX_FORCE_FULL_SPEED_CONNECT);
-
- writel(portsc, &udc->op_regs->portsc[0]);
-
- tmp = readl(&udc->op_regs->epctrlx[0]);
- tmp &= ~(EPCTRL_TX_EP_STALL | EPCTRL_RX_EP_STALL);
- writel(tmp, &udc->op_regs->epctrlx[0]);
-
- return 0;
-}
-
-static int mv_udc_enable_internal(struct mv_udc *udc)
-{
- int retval;
-
- if (udc->active)
- return 0;
-
- dev_dbg(&udc->dev->dev, "enable udc\n");
- retval = udc_clock_enable(udc);
- if (retval)
- return retval;
-
- if (udc->pdata->phy_init) {
- retval = udc->pdata->phy_init(udc->phy_regs);
- if (retval) {
- dev_err(&udc->dev->dev,
- "init phy error %d\n", retval);
- udc_clock_disable(udc);
- return retval;
- }
- }
- udc->active = 1;
-
- return 0;
-}
-
-static int mv_udc_enable(struct mv_udc *udc)
-{
- if (udc->clock_gating)
- return mv_udc_enable_internal(udc);
-
- return 0;
-}
-
-static void mv_udc_disable_internal(struct mv_udc *udc)
-{
- if (udc->active) {
- dev_dbg(&udc->dev->dev, "disable udc\n");
- if (udc->pdata->phy_deinit)
- udc->pdata->phy_deinit(udc->phy_regs);
- udc_clock_disable(udc);
- udc->active = 0;
- }
-}
-
-static void mv_udc_disable(struct mv_udc *udc)
-{
- if (udc->clock_gating)
- mv_udc_disable_internal(udc);
-}
-
-static int mv_udc_get_frame(struct usb_gadget *gadget)
-{
- struct mv_udc *udc;
- u16 retval;
-
- if (!gadget)
- return -ENODEV;
-
- udc = container_of(gadget, struct mv_udc, gadget);
-
- retval = readl(&udc->op_regs->frindex) & USB_FRINDEX_MASKS;
-
- return retval;
-}
-
-/* Tries to wake up the host connected to this gadget */
-static int mv_udc_wakeup(struct usb_gadget *gadget)
-{
- struct mv_udc *udc = container_of(gadget, struct mv_udc, gadget);
- u32 portsc;
-
- /* Remote wakeup feature not enabled by host */
- if (!udc->remote_wakeup)
- return -ENOTSUPP;
-
- portsc = readl(&udc->op_regs->portsc);
- /* not suspended? */
- if (!(portsc & PORTSCX_PORT_SUSPEND))
- return 0;
- /* trigger force resume */
- portsc |= PORTSCX_PORT_FORCE_RESUME;
- writel(portsc, &udc->op_regs->portsc[0]);
- return 0;
-}
-
-static int mv_udc_vbus_session(struct usb_gadget *gadget, int is_active)
-{
- struct mv_udc *udc;
- unsigned long flags;
- int retval = 0;
-
- udc = container_of(gadget, struct mv_udc, gadget);
- spin_lock_irqsave(&udc->lock, flags);
-
- udc->vbus_active = (is_active != 0);
-
- dev_dbg(&udc->dev->dev, "%s: softconnect %d, vbus_active %d\n",
- __func__, udc->softconnect, udc->vbus_active);
-
- if (udc->driver && udc->softconnect && udc->vbus_active) {
- retval = mv_udc_enable(udc);
- if (retval == 0) {
- /* Clock is disabled, need re-init registers */
- udc_reset(udc);
- ep0_reset(udc);
- udc_start(udc);
- }
- } else if (udc->driver && udc->softconnect) {
- if (!udc->active)
- goto out;
-
- /* stop all the transfer in queue*/
- stop_activity(udc, udc->driver);
- udc_stop(udc);
- mv_udc_disable(udc);
- }
-
-out:
- spin_unlock_irqrestore(&udc->lock, flags);
- return retval;
-}
-
-static int mv_udc_pullup(struct usb_gadget *gadget, int is_on)
-{
- struct mv_udc *udc;
- unsigned long flags;
- int retval = 0;
-
- udc = container_of(gadget, struct mv_udc, gadget);
- spin_lock_irqsave(&udc->lock, flags);
-
- udc->softconnect = (is_on != 0);
-
- dev_dbg(&udc->dev->dev, "%s: softconnect %d, vbus_active %d\n",
- __func__, udc->softconnect, udc->vbus_active);
-
- if (udc->driver && udc->softconnect && udc->vbus_active) {
- retval = mv_udc_enable(udc);
- if (retval == 0) {
- /* Clock is disabled, need re-init registers */
- udc_reset(udc);
- ep0_reset(udc);
- udc_start(udc);
- }
- } else if (udc->driver && udc->vbus_active) {
- /* stop all the transfer in queue*/
- stop_activity(udc, udc->driver);
- udc_stop(udc);
- mv_udc_disable(udc);
- }
-
- spin_unlock_irqrestore(&udc->lock, flags);
- return retval;
-}
-
-static int mv_udc_start(struct usb_gadget *, struct usb_gadget_driver *);
-static int mv_udc_stop(struct usb_gadget *);
-/* device controller usb_gadget_ops structure */
-static const struct usb_gadget_ops mv_ops = {
-
- /* returns the current frame number */
- .get_frame = mv_udc_get_frame,
-
- /* tries to wake up the host connected to this gadget */
- .wakeup = mv_udc_wakeup,
-
- /* notify controller that VBUS is powered or not */
- .vbus_session = mv_udc_vbus_session,
-
- /* D+ pullup, software-controlled connect/disconnect to USB host */
- .pullup = mv_udc_pullup,
- .udc_start = mv_udc_start,
- .udc_stop = mv_udc_stop,
-};
-
-static int eps_init(struct mv_udc *udc)
-{
- struct mv_ep *ep;
- char name[14];
- int i;
-
- /* initialize ep0 */
- ep = &udc->eps[0];
- ep->udc = udc;
- strncpy(ep->name, "ep0", sizeof(ep->name));
- ep->ep.name = ep->name;
- ep->ep.ops = &mv_ep_ops;
- ep->wedge = 0;
- ep->stopped = 0;
- usb_ep_set_maxpacket_limit(&ep->ep, EP0_MAX_PKT_SIZE);
- ep->ep.caps.type_control = true;
- ep->ep.caps.dir_in = true;
- ep->ep.caps.dir_out = true;
- ep->ep_num = 0;
- ep->ep.desc = &mv_ep0_desc;
- INIT_LIST_HEAD(&ep->queue);
-
- ep->ep_type = USB_ENDPOINT_XFER_CONTROL;
-
- /* initialize other endpoints */
- for (i = 2; i < udc->max_eps * 2; i++) {
- ep = &udc->eps[i];
- if (i % 2) {
- snprintf(name, sizeof(name), "ep%din", i / 2);
- ep->direction = EP_DIR_IN;
- ep->ep.caps.dir_in = true;
- } else {
- snprintf(name, sizeof(name), "ep%dout", i / 2);
- ep->direction = EP_DIR_OUT;
- ep->ep.caps.dir_out = true;
- }
- ep->udc = udc;
- strncpy(ep->name, name, sizeof(ep->name));
- ep->ep.name = ep->name;
-
- ep->ep.caps.type_iso = true;
- ep->ep.caps.type_bulk = true;
- ep->ep.caps.type_int = true;
-
- ep->ep.ops = &mv_ep_ops;
- ep->stopped = 0;
- usb_ep_set_maxpacket_limit(&ep->ep, (unsigned short) ~0);
- ep->ep_num = i / 2;
-
- INIT_LIST_HEAD(&ep->queue);
- list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list);
-
- ep->dqh = &udc->ep_dqh[i];
- }
-
- return 0;
-}
-
-/* delete all endpoint requests, called with spinlock held */
-static void nuke(struct mv_ep *ep, int status)
-{
- /* called with spinlock held */
- ep->stopped = 1;
-
- /* endpoint fifo flush */
- mv_ep_fifo_flush(&ep->ep);
-
- while (!list_empty(&ep->queue)) {
- struct mv_req *req = NULL;
- req = list_entry(ep->queue.next, struct mv_req, queue);
- done(ep, req, status);
- }
-}
-
-static void gadget_reset(struct mv_udc *udc, struct usb_gadget_driver *driver)
-{
- struct mv_ep *ep;
-
- nuke(&udc->eps[0], -ESHUTDOWN);
-
- list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) {
- nuke(ep, -ESHUTDOWN);
- }
-
- /* report reset; the driver is already quiesced */
- if (driver) {
- spin_unlock(&udc->lock);
- usb_gadget_udc_reset(&udc->gadget, driver);
- spin_lock(&udc->lock);
- }
-}
-/* stop all USB activities */
-static void stop_activity(struct mv_udc *udc, struct usb_gadget_driver *driver)
-{
- struct mv_ep *ep;
-
- nuke(&udc->eps[0], -ESHUTDOWN);
-
- list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) {
- nuke(ep, -ESHUTDOWN);
- }
-
- /* report disconnect; the driver is already quiesced */
- if (driver) {
- spin_unlock(&udc->lock);
- driver->disconnect(&udc->gadget);
- spin_lock(&udc->lock);
- }
-}
-
-static int mv_udc_start(struct usb_gadget *gadget,
- struct usb_gadget_driver *driver)
-{
- struct mv_udc *udc;
- int retval = 0;
- unsigned long flags;
-
- udc = container_of(gadget, struct mv_udc, gadget);
-
- if (udc->driver)
- return -EBUSY;
-
- spin_lock_irqsave(&udc->lock, flags);
-
- /* hook up the driver ... */
- udc->driver = driver;
-
- udc->usb_state = USB_STATE_ATTACHED;
- udc->ep0_state = WAIT_FOR_SETUP;
- udc->ep0_dir = EP_DIR_OUT;
-
- spin_unlock_irqrestore(&udc->lock, flags);
-
- if (udc->transceiver) {
- retval = otg_set_peripheral(udc->transceiver->otg,
- &udc->gadget);
- if (retval) {
- dev_err(&udc->dev->dev,
- "unable to register peripheral to otg\n");
- udc->driver = NULL;
- return retval;
- }
- }
-
- /* When boot with cable attached, there will be no vbus irq occurred */
- if (udc->qwork)
- queue_work(udc->qwork, &udc->vbus_work);
-
- return 0;
-}
-
-static int mv_udc_stop(struct usb_gadget *gadget)
-{
- struct mv_udc *udc;
- unsigned long flags;
-
- udc = container_of(gadget, struct mv_udc, gadget);
-
- spin_lock_irqsave(&udc->lock, flags);
-
- mv_udc_enable(udc);
- udc_stop(udc);
-
- /* stop all usb activities */
- udc->gadget.speed = USB_SPEED_UNKNOWN;
- stop_activity(udc, NULL);
- mv_udc_disable(udc);
-
- spin_unlock_irqrestore(&udc->lock, flags);
-
- /* unbind gadget driver */
- udc->driver = NULL;
-
- return 0;
-}
-
-static void mv_set_ptc(struct mv_udc *udc, u32 mode)
-{
- u32 portsc;
-
- portsc = readl(&udc->op_regs->portsc[0]);
- portsc |= mode << 16;
- writel(portsc, &udc->op_regs->portsc[0]);
-}
-
-static void prime_status_complete(struct usb_ep *ep, struct usb_request *_req)
-{
- struct mv_ep *mvep = container_of(ep, struct mv_ep, ep);
- struct mv_req *req = container_of(_req, struct mv_req, req);
- struct mv_udc *udc;
- unsigned long flags;
-
- udc = mvep->udc;
-
- dev_info(&udc->dev->dev, "switch to test mode %d\n", req->test_mode);
-
- spin_lock_irqsave(&udc->lock, flags);
- if (req->test_mode) {
- mv_set_ptc(udc, req->test_mode);
- req->test_mode = 0;
- }
- spin_unlock_irqrestore(&udc->lock, flags);
-}
-
-static int
-udc_prime_status(struct mv_udc *udc, u8 direction, u16 status, bool empty)
-{
- int retval = 0;
- struct mv_req *req;
- struct mv_ep *ep;
-
- ep = &udc->eps[0];
- udc->ep0_dir = direction;
- udc->ep0_state = WAIT_FOR_OUT_STATUS;
-
- req = udc->status_req;
-
- /* fill in the request structure */
- if (empty == false) {
- *((u16 *) req->req.buf) = cpu_to_le16(status);
- req->req.length = 2;
- } else
- req->req.length = 0;
-
- req->ep = ep;
- req->req.status = -EINPROGRESS;
- req->req.actual = 0;
- if (udc->test_mode) {
- req->req.complete = prime_status_complete;
- req->test_mode = udc->test_mode;
- udc->test_mode = 0;
- } else
- req->req.complete = NULL;
- req->dtd_count = 0;
-
- if (req->req.dma == DMA_ADDR_INVALID) {
- req->req.dma = dma_map_single(ep->udc->gadget.dev.parent,
- req->req.buf, req->req.length,
- ep_dir(ep) ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
- req->mapped = 1;
- }
-
- /* prime the data phase */
- if (!req_to_dtd(req)) {
- retval = queue_dtd(ep, req);
- if (retval) {
- dev_err(&udc->dev->dev,
- "Failed to queue dtd when prime status\n");
- goto out;
- }
- } else{ /* no mem */
- retval = -ENOMEM;
- dev_err(&udc->dev->dev,
- "Failed to dma_pool_alloc when prime status\n");
- goto out;
- }
-
- list_add_tail(&req->queue, &ep->queue);
-
- return 0;
-out:
- usb_gadget_unmap_request(&udc->gadget, &req->req, ep_dir(ep));
-
- return retval;
-}
-
-static void mv_udc_testmode(struct mv_udc *udc, u16 index)
-{
- if (index <= USB_TEST_FORCE_ENABLE) {
- udc->test_mode = index;
- if (udc_prime_status(udc, EP_DIR_IN, 0, true))
- ep0_stall(udc);
- } else
- dev_err(&udc->dev->dev,
- "This test mode(%d) is not supported\n", index);
-}
-
-static void ch9setaddress(struct mv_udc *udc, struct usb_ctrlrequest *setup)
-{
- udc->dev_addr = (u8)setup->wValue;
-
- /* update usb state */
- udc->usb_state = USB_STATE_ADDRESS;
-
- if (udc_prime_status(udc, EP_DIR_IN, 0, true))
- ep0_stall(udc);
-}
-
-static void ch9getstatus(struct mv_udc *udc, u8 ep_num,
- struct usb_ctrlrequest *setup)
-{
- u16 status = 0;
- int retval;
-
- if ((setup->bRequestType & (USB_DIR_IN | USB_TYPE_MASK))
- != (USB_DIR_IN | USB_TYPE_STANDARD))
- return;
-
- if ((setup->bRequestType & USB_RECIP_MASK) == USB_RECIP_DEVICE) {
- status = 1 << USB_DEVICE_SELF_POWERED;
- status |= udc->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP;
- } else if ((setup->bRequestType & USB_RECIP_MASK)
- == USB_RECIP_INTERFACE) {
- /* get interface status */
- status = 0;
- } else if ((setup->bRequestType & USB_RECIP_MASK)
- == USB_RECIP_ENDPOINT) {
- u8 ep_num, direction;
-
- ep_num = setup->wIndex & USB_ENDPOINT_NUMBER_MASK;
- direction = (setup->wIndex & USB_ENDPOINT_DIR_MASK)
- ? EP_DIR_IN : EP_DIR_OUT;
- status = ep_is_stall(udc, ep_num, direction)
- << USB_ENDPOINT_HALT;
- }
-
- retval = udc_prime_status(udc, EP_DIR_IN, status, false);
- if (retval)
- ep0_stall(udc);
- else
- udc->ep0_state = DATA_STATE_XMIT;
-}
-
-static void ch9clearfeature(struct mv_udc *udc, struct usb_ctrlrequest *setup)
-{
- u8 ep_num;
- u8 direction;
- struct mv_ep *ep;
-
- if ((setup->bRequestType & (USB_TYPE_MASK | USB_RECIP_MASK))
- == ((USB_TYPE_STANDARD | USB_RECIP_DEVICE))) {
- switch (setup->wValue) {
- case USB_DEVICE_REMOTE_WAKEUP:
- udc->remote_wakeup = 0;
- break;
- default:
- goto out;
- }
- } else if ((setup->bRequestType & (USB_TYPE_MASK | USB_RECIP_MASK))
- == ((USB_TYPE_STANDARD | USB_RECIP_ENDPOINT))) {
- switch (setup->wValue) {
- case USB_ENDPOINT_HALT:
- ep_num = setup->wIndex & USB_ENDPOINT_NUMBER_MASK;
- direction = (setup->wIndex & USB_ENDPOINT_DIR_MASK)
- ? EP_DIR_IN : EP_DIR_OUT;
- if (setup->wValue != 0 || setup->wLength != 0
- || ep_num > udc->max_eps)
- goto out;
- ep = &udc->eps[ep_num * 2 + direction];
- if (ep->wedge == 1)
- break;
- spin_unlock(&udc->lock);
- ep_set_stall(udc, ep_num, direction, 0);
- spin_lock(&udc->lock);
- break;
- default:
- goto out;
- }
- } else
- goto out;
-
- if (udc_prime_status(udc, EP_DIR_IN, 0, true))
- ep0_stall(udc);
-out:
- return;
-}
-
-static void ch9setfeature(struct mv_udc *udc, struct usb_ctrlrequest *setup)
-{
- u8 ep_num;
- u8 direction;
-
- if ((setup->bRequestType & (USB_TYPE_MASK | USB_RECIP_MASK))
- == ((USB_TYPE_STANDARD | USB_RECIP_DEVICE))) {
- switch (setup->wValue) {
- case USB_DEVICE_REMOTE_WAKEUP:
- udc->remote_wakeup = 1;
- break;
- case USB_DEVICE_TEST_MODE:
- if (setup->wIndex & 0xFF
- || udc->gadget.speed != USB_SPEED_HIGH)
- ep0_stall(udc);
-
- if (udc->usb_state != USB_STATE_CONFIGURED
- && udc->usb_state != USB_STATE_ADDRESS
- && udc->usb_state != USB_STATE_DEFAULT)
- ep0_stall(udc);
-
- mv_udc_testmode(udc, (setup->wIndex >> 8));
- goto out;
- default:
- goto out;
- }
- } else if ((setup->bRequestType & (USB_TYPE_MASK | USB_RECIP_MASK))
- == ((USB_TYPE_STANDARD | USB_RECIP_ENDPOINT))) {
- switch (setup->wValue) {
- case USB_ENDPOINT_HALT:
- ep_num = setup->wIndex & USB_ENDPOINT_NUMBER_MASK;
- direction = (setup->wIndex & USB_ENDPOINT_DIR_MASK)
- ? EP_DIR_IN : EP_DIR_OUT;
- if (setup->wValue != 0 || setup->wLength != 0
- || ep_num > udc->max_eps)
- goto out;
- spin_unlock(&udc->lock);
- ep_set_stall(udc, ep_num, direction, 1);
- spin_lock(&udc->lock);
- break;
- default:
- goto out;
- }
- } else
- goto out;
-
- if (udc_prime_status(udc, EP_DIR_IN, 0, true))
- ep0_stall(udc);
-out:
- return;
-}
-
-static void handle_setup_packet(struct mv_udc *udc, u8 ep_num,
- struct usb_ctrlrequest *setup)
- __releases(&ep->udc->lock)
- __acquires(&ep->udc->lock)
-{
- bool delegate = false;
-
- nuke(&udc->eps[ep_num * 2 + EP_DIR_OUT], -ESHUTDOWN);
-
- 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 standard setup requests here */
- if ((setup->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
- switch (setup->bRequest) {
- case USB_REQ_GET_STATUS:
- ch9getstatus(udc, ep_num, setup);
- break;
-
- case USB_REQ_SET_ADDRESS:
- ch9setaddress(udc, setup);
- break;
-
- case USB_REQ_CLEAR_FEATURE:
- ch9clearfeature(udc, setup);
- break;
-
- case USB_REQ_SET_FEATURE:
- ch9setfeature(udc, setup);
- break;
-
- default:
- delegate = true;
- }
- } else
- delegate = true;
-
- /* delegate USB standard requests to the gadget driver */
- if (delegate == true) {
- /* USB requests handled by gadget */
- if (setup->wLength) {
- /* DATA phase from gadget, STATUS phase from udc */
- udc->ep0_dir = (setup->bRequestType & USB_DIR_IN)
- ? EP_DIR_IN : EP_DIR_OUT;
- spin_unlock(&udc->lock);
- if (udc->driver->setup(&udc->gadget,
- &udc->local_setup_buff) < 0)
- ep0_stall(udc);
- spin_lock(&udc->lock);
- udc->ep0_state = (setup->bRequestType & USB_DIR_IN)
- ? DATA_STATE_XMIT : DATA_STATE_RECV;
- } else {
- /* no DATA phase, IN STATUS phase from gadget */
- udc->ep0_dir = EP_DIR_IN;
- spin_unlock(&udc->lock);
- if (udc->driver->setup(&udc->gadget,
- &udc->local_setup_buff) < 0)
- ep0_stall(udc);
- spin_lock(&udc->lock);
- udc->ep0_state = WAIT_FOR_OUT_STATUS;
- }
- }
-}
-
-/* complete DATA or STATUS phase of ep0 prime status phase if needed */
-static void ep0_req_complete(struct mv_udc *udc,
- struct mv_ep *ep0, struct mv_req *req)
-{
- u32 new_addr;
-
- if (udc->usb_state == USB_STATE_ADDRESS) {
- /* set the new address */
- new_addr = (u32)udc->dev_addr;
- writel(new_addr << USB_DEVICE_ADDRESS_BIT_SHIFT,
- &udc->op_regs->deviceaddr);
- }
-
- done(ep0, req, 0);
-
- switch (udc->ep0_state) {
- case DATA_STATE_XMIT:
- /* receive status phase */
- if (udc_prime_status(udc, EP_DIR_OUT, 0, true))
- ep0_stall(udc);
- break;
- case DATA_STATE_RECV:
- /* send status phase */
- if (udc_prime_status(udc, EP_DIR_IN, 0 , true))
- ep0_stall(udc);
- break;
- case WAIT_FOR_OUT_STATUS:
- udc->ep0_state = WAIT_FOR_SETUP;
- break;
- case WAIT_FOR_SETUP:
- dev_err(&udc->dev->dev, "unexpect ep0 packets\n");
- break;
- default:
- ep0_stall(udc);
- break;
- }
-}
-
-static void get_setup_data(struct mv_udc *udc, u8 ep_num, u8 *buffer_ptr)
-{
- u32 temp;
- struct mv_dqh *dqh;
-
- dqh = &udc->ep_dqh[ep_num * 2 + EP_DIR_OUT];
-
- /* Clear bit in ENDPTSETUPSTAT */
- writel((1 << ep_num), &udc->op_regs->epsetupstat);
-
- /* while a hazard exists when setup package arrives */
- do {
- /* Set Setup Tripwire */
- temp = readl(&udc->op_regs->usbcmd);
- writel(temp | USBCMD_SETUP_TRIPWIRE_SET, &udc->op_regs->usbcmd);
-
- /* Copy the setup packet to local buffer */
- memcpy(buffer_ptr, (u8 *) dqh->setup_buffer, 8);
- } while (!(readl(&udc->op_regs->usbcmd) & USBCMD_SETUP_TRIPWIRE_SET));
-
- /* Clear Setup Tripwire */
- temp = readl(&udc->op_regs->usbcmd);
- writel(temp & ~USBCMD_SETUP_TRIPWIRE_SET, &udc->op_regs->usbcmd);
-}
-
-static void irq_process_tr_complete(struct mv_udc *udc)
-{
- u32 tmp, bit_pos;
- int i, ep_num = 0, direction = 0;
- struct mv_ep *curr_ep;
- struct mv_req *curr_req, *temp_req;
- int status;
-
- /*
- * We use separate loops for ENDPTSETUPSTAT and ENDPTCOMPLETE
- * because the setup packets are to be read ASAP
- */
-
- /* Process all Setup packet received interrupts */
- tmp = readl(&udc->op_regs->epsetupstat);
-
- if (tmp) {
- for (i = 0; i < udc->max_eps; i++) {
- if (tmp & (1 << i)) {
- get_setup_data(udc, i,
- (u8 *)(&udc->local_setup_buff));
- handle_setup_packet(udc, i,
- &udc->local_setup_buff);
- }
- }
- }
-
- /* Don't clear the endpoint setup status register here.
- * It is cleared as a setup packet is read out of the buffer
- */
-
- /* Process non-setup transaction complete interrupts */
- tmp = readl(&udc->op_regs->epcomplete);
-
- if (!tmp)
- return;
-
- writel(tmp, &udc->op_regs->epcomplete);
-
- for (i = 0; i < udc->max_eps * 2; i++) {
- ep_num = i >> 1;
- direction = i % 2;
-
- bit_pos = 1 << (ep_num + 16 * direction);
-
- if (!(bit_pos & tmp))
- continue;
-
- if (i == 1)
- curr_ep = &udc->eps[0];
- else
- curr_ep = &udc->eps[i];
- /* process the req queue until an uncomplete request */
- list_for_each_entry_safe(curr_req, temp_req,
- &curr_ep->queue, queue) {
- status = process_ep_req(udc, i, curr_req);
- if (status)
- break;
-
- /* write back status to req */
- curr_req->req.status = status;
-
- /* ep0 request completion */
- if (ep_num == 0) {
- ep0_req_complete(udc, curr_ep, curr_req);
- break;
- } else {
- done(curr_ep, curr_req, status);
- }
- }
- }
-}
-
-static void irq_process_reset(struct mv_udc *udc)
-{
- u32 tmp;
- unsigned int loops;
-
- udc->ep0_dir = EP_DIR_OUT;
- udc->ep0_state = WAIT_FOR_SETUP;
- udc->remote_wakeup = 0; /* default to 0 on reset */
-
- /* The address bits are past bit 25-31. Set the address */
- tmp = readl(&udc->op_regs->deviceaddr);
- tmp &= ~(USB_DEVICE_ADDRESS_MASK);
- writel(tmp, &udc->op_regs->deviceaddr);
-
- /* Clear all the setup token semaphores */
- tmp = readl(&udc->op_regs->epsetupstat);
- writel(tmp, &udc->op_regs->epsetupstat);
-
- /* Clear all the endpoint complete status bits */
- tmp = readl(&udc->op_regs->epcomplete);
- writel(tmp, &udc->op_regs->epcomplete);
-
- /* wait until all endptprime bits cleared */
- loops = LOOPS(PRIME_TIMEOUT);
- while (readl(&udc->op_regs->epprime) & 0xFFFFFFFF) {
- if (loops == 0) {
- dev_err(&udc->dev->dev,
- "Timeout for ENDPTPRIME = 0x%x\n",
- readl(&udc->op_regs->epprime));
- break;
- }
- loops--;
- udelay(LOOPS_USEC);
- }
-
- /* Write 1s to the Flush register */
- writel((u32)~0, &udc->op_regs->epflush);
-
- if (readl(&udc->op_regs->portsc[0]) & PORTSCX_PORT_RESET) {
- dev_info(&udc->dev->dev, "usb bus reset\n");
- udc->usb_state = USB_STATE_DEFAULT;
- /* reset all the queues, stop all USB activities */
- gadget_reset(udc, udc->driver);
- } else {
- dev_info(&udc->dev->dev, "USB reset portsc 0x%x\n",
- readl(&udc->op_regs->portsc));
-
- /*
- * re-initialize
- * controller reset
- */
- udc_reset(udc);
-
- /* reset all the queues, stop all USB activities */
- stop_activity(udc, udc->driver);
-
- /* reset ep0 dQH and endptctrl */
- ep0_reset(udc);
-
- /* enable interrupt and set controller to run state */
- udc_start(udc);
-
- udc->usb_state = USB_STATE_ATTACHED;
- }
-}
-
-static void handle_bus_resume(struct mv_udc *udc)
-{
- udc->usb_state = udc->resume_state;
- udc->resume_state = 0;
-
- /* report resume to the driver */
- if (udc->driver) {
- if (udc->driver->resume) {
- spin_unlock(&udc->lock);
- udc->driver->resume(&udc->gadget);
- spin_lock(&udc->lock);
- }
- }
-}
-
-static void irq_process_suspend(struct mv_udc *udc)
-{
- udc->resume_state = udc->usb_state;
- udc->usb_state = USB_STATE_SUSPENDED;
-
- if (udc->driver->suspend) {
- spin_unlock(&udc->lock);
- udc->driver->suspend(&udc->gadget);
- spin_lock(&udc->lock);
- }
-}
-
-static void irq_process_port_change(struct mv_udc *udc)
-{
- u32 portsc;
-
- portsc = readl(&udc->op_regs->portsc[0]);
- if (!(portsc & PORTSCX_PORT_RESET)) {
- /* Get the speed */
- u32 speed = portsc & PORTSCX_PORT_SPEED_MASK;
- switch (speed) {
- case PORTSCX_PORT_SPEED_HIGH:
- udc->gadget.speed = USB_SPEED_HIGH;
- break;
- case PORTSCX_PORT_SPEED_FULL:
- udc->gadget.speed = USB_SPEED_FULL;
- break;
- case PORTSCX_PORT_SPEED_LOW:
- udc->gadget.speed = USB_SPEED_LOW;
- break;
- default:
- udc->gadget.speed = USB_SPEED_UNKNOWN;
- break;
- }
- }
-
- if (portsc & PORTSCX_PORT_SUSPEND) {
- udc->resume_state = udc->usb_state;
- udc->usb_state = USB_STATE_SUSPENDED;
- if (udc->driver->suspend) {
- spin_unlock(&udc->lock);
- udc->driver->suspend(&udc->gadget);
- spin_lock(&udc->lock);
- }
- }
-
- if (!(portsc & PORTSCX_PORT_SUSPEND)
- && udc->usb_state == USB_STATE_SUSPENDED) {
- handle_bus_resume(udc);
- }
-
- if (!udc->resume_state)
- udc->usb_state = USB_STATE_DEFAULT;
-}
-
-static void irq_process_error(struct mv_udc *udc)
-{
- /* Increment the error count */
- udc->errors++;
-}
-
-static irqreturn_t mv_udc_irq(int irq, void *dev)
-{
- struct mv_udc *udc = (struct mv_udc *)dev;
- u32 status, intr;
-
- /* Disable ISR when stopped bit is set */
- if (udc->stopped)
- return IRQ_NONE;
-
- spin_lock(&udc->lock);
-
- status = readl(&udc->op_regs->usbsts);
- intr = readl(&udc->op_regs->usbintr);
- status &= intr;
-
- if (status == 0) {
- spin_unlock(&udc->lock);
- return IRQ_NONE;
- }
-
- /* Clear all the interrupts occurred */
- writel(status, &udc->op_regs->usbsts);
-
- if (status & USBSTS_ERR)
- irq_process_error(udc);
-
- if (status & USBSTS_RESET)
- irq_process_reset(udc);
-
- if (status & USBSTS_PORT_CHANGE)
- irq_process_port_change(udc);
-
- if (status & USBSTS_INT)
- irq_process_tr_complete(udc);
-
- if (status & USBSTS_SUSPEND)
- irq_process_suspend(udc);
-
- spin_unlock(&udc->lock);
-
- return IRQ_HANDLED;
-}
-
-static irqreturn_t mv_udc_vbus_irq(int irq, void *dev)
-{
- struct mv_udc *udc = (struct mv_udc *)dev;
-
- /* polling VBUS and init phy may cause too much time*/
- if (udc->qwork)
- queue_work(udc->qwork, &udc->vbus_work);
-
- return IRQ_HANDLED;
-}
-
-static void mv_udc_vbus_work(struct work_struct *work)
-{
- struct mv_udc *udc;
- unsigned int vbus;
-
- udc = container_of(work, struct mv_udc, vbus_work);
- if (!udc->pdata->vbus)
- return;
-
- vbus = udc->pdata->vbus->poll();
- dev_info(&udc->dev->dev, "vbus is %d\n", vbus);
-
- if (vbus == VBUS_HIGH)
- mv_udc_vbus_session(&udc->gadget, 1);
- else if (vbus == VBUS_LOW)
- mv_udc_vbus_session(&udc->gadget, 0);
-}
-
-/* release device structure */
-static void gadget_release(struct device *_dev)
-{
- struct mv_udc *udc;
-
- udc = dev_get_drvdata(_dev);
-
- complete(udc->done);
-}
-
-static void mv_udc_remove(struct platform_device *pdev)
-{
- struct mv_udc *udc;
-
- udc = platform_get_drvdata(pdev);
-
- usb_del_gadget_udc(&udc->gadget);
-
- if (udc->qwork)
- destroy_workqueue(udc->qwork);
-
- /* free memory allocated in probe */
- dma_pool_destroy(udc->dtd_pool);
-
- if (udc->ep_dqh)
- dma_free_coherent(&pdev->dev, udc->ep_dqh_size,
- udc->ep_dqh, udc->ep_dqh_dma);
-
- mv_udc_disable(udc);
-
- /* free dev, wait for the release() finished */
- wait_for_completion(udc->done);
-}
-
-static int mv_udc_probe(struct platform_device *pdev)
-{
- struct mv_usb_platform_data *pdata = dev_get_platdata(&pdev->dev);
- struct mv_udc *udc;
- int retval = 0;
- struct resource *r;
- size_t size;
-
- if (pdata == NULL) {
- dev_err(&pdev->dev, "missing platform_data\n");
- return -ENODEV;
- }
-
- udc = devm_kzalloc(&pdev->dev, sizeof(*udc), GFP_KERNEL);
- if (udc == NULL)
- return -ENOMEM;
-
- udc->done = &release_done;
- udc->pdata = dev_get_platdata(&pdev->dev);
- spin_lock_init(&udc->lock);
-
- udc->dev = pdev;
-
- if (pdata->mode == MV_USB_MODE_OTG) {
- udc->transceiver = devm_usb_get_phy(&pdev->dev,
- USB_PHY_TYPE_USB2);
- if (IS_ERR(udc->transceiver)) {
- retval = PTR_ERR(udc->transceiver);
-
- if (retval == -ENXIO)
- return retval;
-
- udc->transceiver = NULL;
- return -EPROBE_DEFER;
- }
- }
-
- /* udc only have one sysclk. */
- udc->clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(udc->clk))
- return PTR_ERR(udc->clk);
-
- r = platform_get_resource_byname(udc->dev, IORESOURCE_MEM, "capregs");
- if (r == NULL) {
- dev_err(&pdev->dev, "no I/O memory resource defined\n");
- return -ENODEV;
- }
-
- udc->cap_regs = (struct mv_cap_regs __iomem *)
- devm_ioremap(&pdev->dev, r->start, resource_size(r));
- if (udc->cap_regs == NULL) {
- dev_err(&pdev->dev, "failed to map I/O memory\n");
- return -EBUSY;
- }
-
- r = platform_get_resource_byname(udc->dev, IORESOURCE_MEM, "phyregs");
- if (r == NULL) {
- dev_err(&pdev->dev, "no phy I/O memory resource defined\n");
- return -ENODEV;
- }
-
- udc->phy_regs = devm_ioremap(&pdev->dev, r->start, resource_size(r));
- if (udc->phy_regs == NULL) {
- dev_err(&pdev->dev, "failed to map phy I/O memory\n");
- return -EBUSY;
- }
-
- /* we will acces controller register, so enable the clk */
- retval = mv_udc_enable_internal(udc);
- if (retval)
- return retval;
-
- udc->op_regs =
- (struct mv_op_regs __iomem *)((unsigned long)udc->cap_regs
- + (readl(&udc->cap_regs->caplength_hciversion)
- & CAPLENGTH_MASK));
- udc->max_eps = readl(&udc->cap_regs->dccparams) & DCCPARAMS_DEN_MASK;
-
- /*
- * some platform will use usb to download image, it may not disconnect
- * usb gadget before loading kernel. So first stop udc here.
- */
- udc_stop(udc);
- writel(0xFFFFFFFF, &udc->op_regs->usbsts);
-
- size = udc->max_eps * sizeof(struct mv_dqh) *2;
- size = (size + DQH_ALIGNMENT - 1) & ~(DQH_ALIGNMENT - 1);
- udc->ep_dqh = dma_alloc_coherent(&pdev->dev, size,
- &udc->ep_dqh_dma, GFP_KERNEL);
-
- if (udc->ep_dqh == NULL) {
- dev_err(&pdev->dev, "allocate dQH memory failed\n");
- retval = -ENOMEM;
- goto err_disable_clock;
- }
- udc->ep_dqh_size = size;
-
- /* create dTD dma_pool resource */
- udc->dtd_pool = dma_pool_create("mv_dtd",
- &pdev->dev,
- sizeof(struct mv_dtd),
- DTD_ALIGNMENT,
- DMA_BOUNDARY);
-
- if (!udc->dtd_pool) {
- retval = -ENOMEM;
- goto err_free_dma;
- }
-
- size = udc->max_eps * sizeof(struct mv_ep) *2;
- udc->eps = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
- if (udc->eps == NULL) {
- retval = -ENOMEM;
- goto err_destroy_dma;
- }
-
- /* initialize ep0 status request structure */
- udc->status_req = devm_kzalloc(&pdev->dev, sizeof(struct mv_req),
- GFP_KERNEL);
- if (!udc->status_req) {
- retval = -ENOMEM;
- goto err_destroy_dma;
- }
- INIT_LIST_HEAD(&udc->status_req->queue);
-
- /* allocate a small amount of memory to get valid address */
- udc->status_req->req.buf = devm_kzalloc(&pdev->dev, 8, GFP_KERNEL);
- if (!udc->status_req->req.buf) {
- retval = -ENOMEM;
- goto err_destroy_dma;
- }
- udc->status_req->req.dma = DMA_ADDR_INVALID;
-
- udc->resume_state = USB_STATE_NOTATTACHED;
- udc->usb_state = USB_STATE_POWERED;
- udc->ep0_dir = EP_DIR_OUT;
- udc->remote_wakeup = 0;
-
- r = platform_get_resource(udc->dev, IORESOURCE_IRQ, 0);
- if (r == NULL) {
- dev_err(&pdev->dev, "no IRQ resource defined\n");
- retval = -ENODEV;
- goto err_destroy_dma;
- }
- udc->irq = r->start;
- if (devm_request_irq(&pdev->dev, udc->irq, mv_udc_irq,
- IRQF_SHARED, driver_name, udc)) {
- dev_err(&pdev->dev, "Request irq %d for UDC failed\n",
- udc->irq);
- retval = -ENODEV;
- goto err_destroy_dma;
- }
-
- /* initialize gadget structure */
- udc->gadget.ops = &mv_ops; /* usb_gadget_ops */
- udc->gadget.ep0 = &udc->eps[0].ep; /* gadget ep0 */
- INIT_LIST_HEAD(&udc->gadget.ep_list); /* ep_list */
- udc->gadget.speed = USB_SPEED_UNKNOWN; /* speed */
- udc->gadget.max_speed = USB_SPEED_HIGH; /* support dual speed */
-
- /* the "gadget" abstracts/virtualizes the controller */
- udc->gadget.name = driver_name; /* gadget name */
-
- eps_init(udc);
-
- /* VBUS detect: we can disable/enable clock on demand.*/
- if (udc->transceiver)
- udc->clock_gating = 1;
- else if (pdata->vbus) {
- udc->clock_gating = 1;
- retval = devm_request_threaded_irq(&pdev->dev,
- pdata->vbus->irq, NULL,
- mv_udc_vbus_irq, IRQF_ONESHOT, "vbus", udc);
- if (retval) {
- dev_info(&pdev->dev,
- "Can not request irq for VBUS, "
- "disable clock gating\n");
- udc->clock_gating = 0;
- }
-
- udc->qwork = create_singlethread_workqueue("mv_udc_queue");
- if (!udc->qwork) {
- dev_err(&pdev->dev, "cannot create workqueue\n");
- retval = -ENOMEM;
- goto err_destroy_dma;
- }
-
- INIT_WORK(&udc->vbus_work, mv_udc_vbus_work);
- }
-
- /*
- * When clock gating is supported, we can disable clk and phy.
- * If not, it means that VBUS detection is not supported, we
- * have to enable vbus active all the time to let controller work.
- */
- if (udc->clock_gating)
- mv_udc_disable_internal(udc);
- else
- udc->vbus_active = 1;
-
- retval = usb_add_gadget_udc_release(&pdev->dev, &udc->gadget,
- gadget_release);
- if (retval)
- goto err_create_workqueue;
-
- platform_set_drvdata(pdev, udc);
- dev_info(&pdev->dev, "successful probe UDC device %s clock gating.\n",
- udc->clock_gating ? "with" : "without");
-
- return 0;
-
-err_create_workqueue:
- if (udc->qwork)
- destroy_workqueue(udc->qwork);
-err_destroy_dma:
- dma_pool_destroy(udc->dtd_pool);
-err_free_dma:
- dma_free_coherent(&pdev->dev, udc->ep_dqh_size,
- udc->ep_dqh, udc->ep_dqh_dma);
-err_disable_clock:
- mv_udc_disable_internal(udc);
-
- return retval;
-}
-
-#ifdef CONFIG_PM
-static int mv_udc_suspend(struct device *dev)
-{
- struct mv_udc *udc;
-
- udc = dev_get_drvdata(dev);
-
- /* if OTG is enabled, the following will be done in OTG driver*/
- if (udc->transceiver)
- return 0;
-
- if (udc->pdata->vbus && udc->pdata->vbus->poll)
- if (udc->pdata->vbus->poll() == VBUS_HIGH) {
- dev_info(&udc->dev->dev, "USB cable is connected!\n");
- return -EAGAIN;
- }
-
- /*
- * only cable is unplugged, udc can suspend.
- * So do not care about clock_gating == 1.
- */
- if (!udc->clock_gating) {
- udc_stop(udc);
-
- spin_lock_irq(&udc->lock);
- /* stop all usb activities */
- stop_activity(udc, udc->driver);
- spin_unlock_irq(&udc->lock);
-
- mv_udc_disable_internal(udc);
- }
-
- return 0;
-}
-
-static int mv_udc_resume(struct device *dev)
-{
- struct mv_udc *udc;
- int retval;
-
- udc = dev_get_drvdata(dev);
-
- /* if OTG is enabled, the following will be done in OTG driver*/
- if (udc->transceiver)
- return 0;
-
- if (!udc->clock_gating) {
- retval = mv_udc_enable_internal(udc);
- if (retval)
- return retval;
-
- if (udc->driver && udc->softconnect) {
- udc_reset(udc);
- ep0_reset(udc);
- udc_start(udc);
- }
- }
-
- return 0;
-}
-
-static const struct dev_pm_ops mv_udc_pm_ops = {
- .suspend = mv_udc_suspend,
- .resume = mv_udc_resume,
-};
-#endif
-
-static void mv_udc_shutdown(struct platform_device *pdev)
-{
- struct mv_udc *udc;
- u32 mode;
-
- udc = platform_get_drvdata(pdev);
- /* reset controller mode to IDLE */
- mv_udc_enable(udc);
- mode = readl(&udc->op_regs->usbmode);
- mode &= ~3;
- writel(mode, &udc->op_regs->usbmode);
- mv_udc_disable(udc);
-}
-
-static struct platform_driver udc_driver = {
- .probe = mv_udc_probe,
- .remove = mv_udc_remove,
- .shutdown = mv_udc_shutdown,
- .driver = {
- .name = "mv-udc",
-#ifdef CONFIG_PM
- .pm = &mv_udc_pm_ops,
-#endif
- },
-};
-
-module_platform_driver(udc_driver);
-MODULE_ALIAS("platform:mv-udc");
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_AUTHOR("Chao Xie <chao.xie@marvell.com>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/udc/net2272.c b/drivers/usb/gadget/udc/net2272.c
deleted file mode 100644
index 7ecddbf5c90d..000000000000
--- a/drivers/usb/gadget/udc/net2272.c
+++ /dev/null
@@ -1,2723 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Driver for PLX NET2272 USB device controller
- *
- * Copyright (C) 2005-2006 PLX Technology, Inc.
- * Copyright (C) 2006-2011 Analog Devices, Inc.
- */
-
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/errno.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/ioport.h>
-#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/pci.h>
-#include <linux/platform_device.h>
-#include <linux/prefetch.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/timer.h>
-#include <linux/usb.h>
-#include <linux/usb/ch9.h>
-#include <linux/usb/gadget.h>
-
-#include <asm/byteorder.h>
-#include <linux/unaligned.h>
-
-#include "net2272.h"
-
-#define DRIVER_DESC "PLX NET2272 USB Peripheral Controller"
-
-static const char driver_name[] = "net2272";
-static const char driver_vers[] = "2006 October 17/mainline";
-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",
-};
-
-#ifdef CONFIG_USB_NET2272_DMA
-/*
- * use_dma: the NET2272 can use an external DMA controller.
- * Note that since there is no generic DMA api, some functions,
- * notably request_dma, start_dma, and cancel_dma will need to be
- * modified for your platform's particular dma controller.
- *
- * If use_dma is disabled, pio will be used instead.
- */
-static bool use_dma = false;
-module_param(use_dma, bool, 0644);
-
-/*
- * dma_ep: selects the endpoint for use with dma (1=ep-a, 2=ep-b)
- * The NET2272 can only use dma for a single endpoint at a time.
- * At some point this could be modified to allow either endpoint
- * to take control of dma as it becomes available.
- *
- * Note that DMA should not be used on OUT endpoints unless it can
- * be guaranteed that no short packets will arrive on an IN endpoint
- * while the DMA operation is pending. Otherwise the OUT DMA will
- * terminate prematurely (See NET2272 Errata 630-0213-0101)
- */
-static ushort dma_ep = 1;
-module_param(dma_ep, ushort, 0644);
-
-/*
- * dma_mode: net2272 dma mode setting (see LOCCTL1 definition):
- * mode 0 == Slow DREQ mode
- * mode 1 == Fast DREQ mode
- * mode 2 == Burst mode
- */
-static ushort dma_mode = 2;
-module_param(dma_mode, ushort, 0644);
-#else
-#define use_dma 0
-#define dma_ep 1
-#define dma_mode 2
-#endif
-
-/*
- * fifo_mode: net2272 buffer configuration:
- * mode 0 == ep-{a,b,c} 512db each
- * mode 1 == ep-a 1k, ep-{b,c} 512db
- * mode 2 == ep-a 1k, ep-b 1k, ep-c 512db
- * mode 3 == ep-a 1k, ep-b disabled, ep-c 512db
- */
-static ushort fifo_mode;
-module_param(fifo_mode, ushort, 0644);
-
-/*
- * enable_suspend: When enabled, the driver will respond to
- * USB suspend requests by powering down the NET2272. Otherwise,
- * USB suspend requests will be ignored. This is acceptable for
- * self-powered devices. For bus powered devices set this to 1.
- */
-static ushort enable_suspend;
-module_param(enable_suspend, ushort, 0644);
-
-static void assert_out_naking(struct net2272_ep *ep, const char *where)
-{
- u8 tmp;
-
-#ifndef DEBUG
- return;
-#endif
-
- tmp = net2272_ep_read(ep, EP_STAT0);
- if ((tmp & (1 << NAK_OUT_PACKETS)) == 0) {
- dev_dbg(ep->dev->dev, "%s %s %02x !NAK\n",
- ep->ep.name, where, tmp);
- net2272_ep_write(ep, EP_RSPSET, 1 << ALT_NAK_OUT_PACKETS);
- }
-}
-#define ASSERT_OUT_NAKING(ep) assert_out_naking(ep, __func__)
-
-static void stop_out_naking(struct net2272_ep *ep)
-{
- u8 tmp = net2272_ep_read(ep, EP_STAT0);
-
- if ((tmp & (1 << NAK_OUT_PACKETS)) != 0)
- net2272_ep_write(ep, EP_RSPCLR, 1 << ALT_NAK_OUT_PACKETS);
-}
-
-#define PIPEDIR(bAddress) (usb_pipein(bAddress) ? "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";
- default: return "control";
- }
-}
-
-static char *buf_state_string(unsigned state)
-{
- switch (state) {
- case BUFF_FREE: return "free";
- case BUFF_VALID: return "valid";
- case BUFF_LCL: return "local";
- case BUFF_USB: return "usb";
- default: return "unknown";
- }
-}
-
-static char *dma_mode_string(void)
-{
- if (!use_dma)
- return "PIO";
- switch (dma_mode) {
- case 0: return "SLOW DREQ";
- case 1: return "FAST DREQ";
- case 2: return "BURST";
- default: return "invalid";
- }
-}
-
-static void net2272_dequeue_all(struct net2272_ep *);
-static int net2272_kick_dma(struct net2272_ep *, struct net2272_request *);
-static int net2272_fifo_status(struct usb_ep *);
-
-static const struct usb_ep_ops net2272_ep_ops;
-
-/*---------------------------------------------------------------------------*/
-
-static int
-net2272_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
-{
- struct net2272 *dev;
- struct net2272_ep *ep;
- u32 max;
- u8 tmp;
- unsigned long flags;
-
- ep = container_of(_ep, struct net2272_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;
-
- max = usb_endpoint_maxp(desc);
-
- spin_lock_irqsave(&dev->lock, flags);
- _ep->maxpacket = max;
- ep->desc = desc;
-
- /* net2272_ep_reset() has already been called */
- ep->stopped = 0;
- ep->wedged = 0;
-
- /* set speed-dependent max packet */
- net2272_ep_write(ep, EP_MAXPKT0, max & 0xff);
- net2272_ep_write(ep, EP_MAXPKT1, (max & 0xff00) >> 8);
-
- /* set type, direction, address; reset fifo counters */
- net2272_ep_write(ep, EP_STAT1, 1 << BUFFER_FLUSH);
- tmp = usb_endpoint_type(desc);
- if (usb_endpoint_xfer_bulk(desc)) {
- /* 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 = usb_endpoint_xfer_isoc(desc) ? 1 : 0;
- tmp <<= ENDPOINT_TYPE;
- tmp |= ((desc->bEndpointAddress & 0x0f) << ENDPOINT_NUMBER);
- tmp |= usb_endpoint_dir_in(desc) << ENDPOINT_DIRECTION;
- tmp |= (1 << ENDPOINT_ENABLE);
-
- /* for OUT transfers, block the rx fifo until a read is posted */
- ep->is_in = usb_endpoint_dir_in(desc);
- if (!ep->is_in)
- net2272_ep_write(ep, EP_RSPSET, 1 << ALT_NAK_OUT_PACKETS);
-
- net2272_ep_write(ep, EP_CFG, tmp);
-
- /* enable irqs */
- tmp = (1 << ep->num) | net2272_read(dev, IRQENB0);
- net2272_write(dev, IRQENB0, tmp);
-
- tmp = (1 << DATA_PACKET_RECEIVED_INTERRUPT_ENABLE)
- | (1 << DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE)
- | net2272_ep_read(ep, EP_IRQENB);
- net2272_ep_write(ep, EP_IRQENB, tmp);
-
- tmp = desc->bEndpointAddress;
- dev_dbg(dev->dev, "enabled %s (ep%d%s-%s) max %04x cfg %02x\n",
- _ep->name, tmp & 0x0f, PIPEDIR(tmp),
- type_string(desc->bmAttributes), max,
- net2272_ep_read(ep, EP_CFG));
-
- spin_unlock_irqrestore(&dev->lock, flags);
- return 0;
-}
-
-static void net2272_ep_reset(struct net2272_ep *ep)
-{
- u8 tmp;
-
- ep->desc = NULL;
- INIT_LIST_HEAD(&ep->queue);
-
- usb_ep_set_maxpacket_limit(&ep->ep, ~0);
- ep->ep.ops = &net2272_ep_ops;
-
- /* disable irqs, endpoint */
- net2272_ep_write(ep, EP_IRQENB, 0);
-
- /* init to our chosen defaults, notably so that we NAK OUT
- * packets until the driver queues a read.
- */
- tmp = (1 << NAK_OUT_PACKETS_MODE) | (1 << ALT_NAK_OUT_PACKETS);
- net2272_ep_write(ep, EP_RSPSET, tmp);
-
- tmp = (1 << INTERRUPT_MODE) | (1 << HIDE_STATUS_PHASE);
- if (ep->num != 0)
- tmp |= (1 << ENDPOINT_TOGGLE) | (1 << ENDPOINT_HALT);
-
- net2272_ep_write(ep, EP_RSPCLR, tmp);
-
- /* scrub most status bits, and flush any fifo state */
- net2272_ep_write(ep, EP_STAT0,
- (1 << DATA_IN_TOKEN_INTERRUPT)
- | (1 << DATA_OUT_TOKEN_INTERRUPT)
- | (1 << DATA_PACKET_TRANSMITTED_INTERRUPT)
- | (1 << DATA_PACKET_RECEIVED_INTERRUPT)
- | (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT));
-
- net2272_ep_write(ep, EP_STAT1,
- (1 << TIMEOUT)
- | (1 << USB_OUT_ACK_SENT)
- | (1 << USB_OUT_NAK_SENT)
- | (1 << USB_IN_ACK_RCVD)
- | (1 << USB_IN_NAK_SENT)
- | (1 << USB_STALL_SENT)
- | (1 << LOCAL_OUT_ZLP)
- | (1 << BUFFER_FLUSH));
-
- /* fifo size is handled separately */
-}
-
-static int net2272_disable(struct usb_ep *_ep)
-{
- struct net2272_ep *ep;
- unsigned long flags;
-
- ep = container_of(_ep, struct net2272_ep, ep);
- if (!_ep || !ep->desc || _ep->name == ep0name)
- return -EINVAL;
-
- spin_lock_irqsave(&ep->dev->lock, flags);
- net2272_dequeue_all(ep);
- net2272_ep_reset(ep);
-
- dev_vdbg(ep->dev->dev, "disabled %s\n", _ep->name);
-
- spin_unlock_irqrestore(&ep->dev->lock, flags);
- return 0;
-}
-
-/*---------------------------------------------------------------------------*/
-
-static struct usb_request *
-net2272_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
-{
- struct net2272_request *req;
-
- if (!_ep)
- return NULL;
-
- req = kzalloc(sizeof(*req), gfp_flags);
- if (!req)
- return NULL;
-
- INIT_LIST_HEAD(&req->queue);
-
- return &req->req;
-}
-
-static void
-net2272_free_request(struct usb_ep *_ep, struct usb_request *_req)
-{
- struct net2272_request *req;
-
- if (!_ep || !_req)
- return;
-
- req = container_of(_req, struct net2272_request, req);
- WARN_ON(!list_empty(&req->queue));
- kfree(req);
-}
-
-static void
-net2272_done(struct net2272_ep *ep, struct net2272_request *req, int status)
-{
- struct net2272 *dev;
- unsigned stopped = ep->stopped;
-
- if (ep->num == 0) {
- if (ep->dev->protocol_stall) {
- ep->stopped = 1;
- set_halt(ep);
- }
- allow_status(ep);
- }
-
- list_del_init(&req->queue);
-
- if (req->req.status == -EINPROGRESS)
- req->req.status = status;
- else
- status = req->req.status;
-
- dev = ep->dev;
- if (use_dma && ep->dma)
- usb_gadget_unmap_request(&dev->gadget, &req->req,
- ep->is_in);
-
- if (status && status != -ESHUTDOWN)
- dev_vdbg(dev->dev, "complete %s req %p stat %d len %u/%u buf %p\n",
- ep->ep.name, &req->req, status,
- req->req.actual, req->req.length, req->req.buf);
-
- /* don't modify queue heads during completion callback */
- ep->stopped = 1;
- spin_unlock(&dev->lock);
- usb_gadget_giveback_request(&ep->ep, &req->req);
- spin_lock(&dev->lock);
- ep->stopped = stopped;
-}
-
-static int
-net2272_write_packet(struct net2272_ep *ep, u8 *buf,
- struct net2272_request *req, unsigned max)
-{
- u16 __iomem *ep_data = net2272_reg_addr(ep->dev, EP_DATA);
- u16 *bufp;
- unsigned length, count;
- u8 tmp;
-
- length = min(req->req.length - req->req.actual, max);
- req->req.actual += length;
-
- dev_vdbg(ep->dev->dev, "write packet %s req %p max %u len %u avail %u\n",
- ep->ep.name, req, max, length,
- (net2272_ep_read(ep, EP_AVAIL1) << 8) | net2272_ep_read(ep, EP_AVAIL0));
-
- count = length;
- bufp = (u16 *)buf;
-
- while (likely(count >= 2)) {
- /* no byte-swap required; chip endian set during init */
- writew(*bufp++, ep_data);
- count -= 2;
- }
- buf = (u8 *)bufp;
-
- /* write final byte by placing the NET2272 into 8-bit mode */
- if (unlikely(count)) {
- tmp = net2272_read(ep->dev, LOCCTL);
- net2272_write(ep->dev, LOCCTL, tmp & ~(1 << DATA_WIDTH));
- writeb(*buf, ep_data);
- net2272_write(ep->dev, LOCCTL, tmp);
- }
- return length;
-}
-
-/* returns: 0: still running, 1: completed, negative: errno */
-static int
-net2272_write_fifo(struct net2272_ep *ep, struct net2272_request *req)
-{
- u8 *buf;
- unsigned count, max;
- int status;
-
- dev_vdbg(ep->dev->dev, "write_fifo %s actual %d len %d\n",
- ep->ep.name, req->req.actual, req->req.length);
-
- /*
- * Keep loading the endpoint until the final packet is loaded,
- * or the endpoint buffer is full.
- */
- top:
- /*
- * Clear interrupt status
- * - Packet Transmitted interrupt will become set again when the
- * host successfully takes another packet
- */
- net2272_ep_write(ep, EP_STAT0, (1 << DATA_PACKET_TRANSMITTED_INTERRUPT));
- while (!(net2272_ep_read(ep, EP_STAT0) & (1 << BUFFER_FULL))) {
- buf = req->req.buf + req->req.actual;
- prefetch(buf);
-
- /* force pagesel */
- net2272_ep_read(ep, EP_STAT0);
-
- max = (net2272_ep_read(ep, EP_AVAIL1) << 8) |
- (net2272_ep_read(ep, EP_AVAIL0));
-
- if (max < ep->ep.maxpacket)
- max = (net2272_ep_read(ep, EP_AVAIL1) << 8)
- | (net2272_ep_read(ep, EP_AVAIL0));
-
- count = net2272_write_packet(ep, buf, req, max);
- /* see if we are done */
- if (req->req.length == req->req.actual) {
- /* validate short or zlp packet */
- if (count < ep->ep.maxpacket)
- set_fifo_bytecount(ep, 0);
- net2272_done(ep, req, 0);
-
- if (!list_empty(&ep->queue)) {
- req = list_entry(ep->queue.next,
- struct net2272_request,
- queue);
- status = net2272_kick_dma(ep, req);
-
- if (status < 0)
- if ((net2272_ep_read(ep, EP_STAT0)
- & (1 << BUFFER_EMPTY)))
- goto top;
- }
- return 1;
- }
- net2272_ep_write(ep, EP_STAT0, (1 << DATA_PACKET_TRANSMITTED_INTERRUPT));
- }
- return 0;
-}
-
-static void
-net2272_out_flush(struct net2272_ep *ep)
-{
- ASSERT_OUT_NAKING(ep);
-
- net2272_ep_write(ep, EP_STAT0, (1 << DATA_OUT_TOKEN_INTERRUPT)
- | (1 << DATA_PACKET_RECEIVED_INTERRUPT));
- net2272_ep_write(ep, EP_STAT1, 1 << BUFFER_FLUSH);
-}
-
-static int
-net2272_read_packet(struct net2272_ep *ep, u8 *buf,
- struct net2272_request *req, unsigned avail)
-{
- u16 __iomem *ep_data = net2272_reg_addr(ep->dev, EP_DATA);
- unsigned is_short;
- u16 *bufp;
-
- req->req.actual += avail;
-
- dev_vdbg(ep->dev->dev, "read packet %s req %p len %u avail %u\n",
- ep->ep.name, req, avail,
- (net2272_ep_read(ep, EP_AVAIL1) << 8) | net2272_ep_read(ep, EP_AVAIL0));
-
- is_short = (avail < ep->ep.maxpacket);
-
- if (unlikely(avail == 0)) {
- /* remove any zlp from the buffer */
- (void)readw(ep_data);
- return is_short;
- }
-
- /* Ensure we get the final byte */
- if (unlikely(avail % 2))
- avail++;
- bufp = (u16 *)buf;
-
- do {
- *bufp++ = readw(ep_data);
- avail -= 2;
- } while (avail);
-
- /*
- * To avoid false endpoint available race condition must read
- * ep stat0 twice in the case of a short transfer
- */
- if (net2272_ep_read(ep, EP_STAT0) & (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT))
- net2272_ep_read(ep, EP_STAT0);
-
- return is_short;
-}
-
-static int
-net2272_read_fifo(struct net2272_ep *ep, struct net2272_request *req)
-{
- u8 *buf;
- unsigned is_short;
- int count;
- int tmp;
- int cleanup = 0;
-
- dev_vdbg(ep->dev->dev, "read_fifo %s actual %d len %d\n",
- ep->ep.name, req->req.actual, req->req.length);
-
- top:
- do {
- buf = req->req.buf + req->req.actual;
- prefetchw(buf);
-
- count = (net2272_ep_read(ep, EP_AVAIL1) << 8)
- | net2272_ep_read(ep, EP_AVAIL0);
-
- net2272_ep_write(ep, EP_STAT0,
- (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT) |
- (1 << DATA_PACKET_RECEIVED_INTERRUPT));
-
- tmp = req->req.length - req->req.actual;
-
- if (count > tmp) {
- if ((tmp % ep->ep.maxpacket) != 0) {
- dev_err(ep->dev->dev,
- "%s out fifo %d bytes, expected %d\n",
- ep->ep.name, count, tmp);
- cleanup = 1;
- }
- count = (tmp > 0) ? tmp : 0;
- }
-
- is_short = net2272_read_packet(ep, buf, req, count);
-
- /* completion */
- if (unlikely(cleanup || is_short ||
- req->req.actual == req->req.length)) {
-
- if (cleanup) {
- net2272_out_flush(ep);
- net2272_done(ep, req, -EOVERFLOW);
- } else
- net2272_done(ep, req, 0);
-
- /* re-initialize endpoint transfer registers
- * otherwise they may result in erroneous pre-validation
- * for subsequent control reads
- */
- if (unlikely(ep->num == 0)) {
- net2272_ep_write(ep, EP_TRANSFER2, 0);
- net2272_ep_write(ep, EP_TRANSFER1, 0);
- net2272_ep_write(ep, EP_TRANSFER0, 0);
- }
-
- if (!list_empty(&ep->queue)) {
- int status;
-
- req = list_entry(ep->queue.next,
- struct net2272_request, queue);
- status = net2272_kick_dma(ep, req);
- if ((status < 0) &&
- !(net2272_ep_read(ep, EP_STAT0) & (1 << BUFFER_EMPTY)))
- goto top;
- }
- return 1;
- }
- } while (!(net2272_ep_read(ep, EP_STAT0) & (1 << BUFFER_EMPTY)));
-
- return 0;
-}
-
-static void
-net2272_pio_advance(struct net2272_ep *ep)
-{
- struct net2272_request *req;
-
- if (unlikely(list_empty(&ep->queue)))
- return;
-
- req = list_entry(ep->queue.next, struct net2272_request, queue);
- (ep->is_in ? net2272_write_fifo : net2272_read_fifo)(ep, req);
-}
-
-/* returns 0 on success, else negative errno */
-static int
-net2272_request_dma(struct net2272 *dev, unsigned ep, u32 buf,
- unsigned len, unsigned dir)
-{
- dev_vdbg(dev->dev, "request_dma ep %d buf %08x len %d dir %d\n",
- ep, buf, len, dir);
-
- /* The NET2272 only supports a single dma channel */
- if (dev->dma_busy)
- return -EBUSY;
- /*
- * EP_TRANSFER (used to determine the number of bytes received
- * in an OUT transfer) is 24 bits wide; don't ask for more than that.
- */
- if ((dir == 1) && (len > 0x1000000))
- return -EINVAL;
-
- dev->dma_busy = 1;
-
- /* initialize platform's dma */
-#ifdef CONFIG_USB_PCI
- /* NET2272 addr, buffer addr, length, etc. */
- switch (dev->dev_id) {
- case PCI_DEVICE_ID_RDK1:
- /* Setup PLX 9054 DMA mode */
- writel((1 << LOCAL_BUS_WIDTH) |
- (1 << TA_READY_INPUT_ENABLE) |
- (0 << LOCAL_BURST_ENABLE) |
- (1 << DONE_INTERRUPT_ENABLE) |
- (1 << LOCAL_ADDRESSING_MODE) |
- (1 << DEMAND_MODE) |
- (1 << DMA_EOT_ENABLE) |
- (1 << FAST_SLOW_TERMINATE_MODE_SELECT) |
- (1 << DMA_CHANNEL_INTERRUPT_SELECT),
- dev->rdk1.plx9054_base_addr + DMAMODE0);
-
- writel(0x100000, dev->rdk1.plx9054_base_addr + DMALADR0);
- writel(buf, dev->rdk1.plx9054_base_addr + DMAPADR0);
- writel(len, dev->rdk1.plx9054_base_addr + DMASIZ0);
- writel((dir << DIRECTION_OF_TRANSFER) |
- (1 << INTERRUPT_AFTER_TERMINAL_COUNT),
- dev->rdk1.plx9054_base_addr + DMADPR0);
- writel((1 << LOCAL_DMA_CHANNEL_0_INTERRUPT_ENABLE) |
- readl(dev->rdk1.plx9054_base_addr + INTCSR),
- dev->rdk1.plx9054_base_addr + INTCSR);
-
- break;
- }
-#endif
-
- net2272_write(dev, DMAREQ,
- (0 << DMA_BUFFER_VALID) |
- (1 << DMA_REQUEST_ENABLE) |
- (1 << DMA_CONTROL_DACK) |
- (dev->dma_eot_polarity << EOT_POLARITY) |
- (dev->dma_dack_polarity << DACK_POLARITY) |
- (dev->dma_dreq_polarity << DREQ_POLARITY) |
- ((ep >> 1) << DMA_ENDPOINT_SELECT));
-
- (void) net2272_read(dev, SCRATCH);
-
- return 0;
-}
-
-static void
-net2272_start_dma(struct net2272 *dev)
-{
- /* start platform's dma controller */
-#ifdef CONFIG_USB_PCI
- switch (dev->dev_id) {
- case PCI_DEVICE_ID_RDK1:
- writeb((1 << CHANNEL_ENABLE) | (1 << CHANNEL_START),
- dev->rdk1.plx9054_base_addr + DMACSR0);
- break;
- }
-#endif
-}
-
-/* returns 0 on success, else negative errno */
-static int
-net2272_kick_dma(struct net2272_ep *ep, struct net2272_request *req)
-{
- unsigned size;
- u8 tmp;
-
- if (!use_dma || (ep->num < 1) || (ep->num > 2) || !ep->dma)
- return -EINVAL;
-
- /* don't use dma for odd-length transfers
- * otherwise, we'd need to deal with the last byte with pio
- */
- if (req->req.length & 1)
- return -EINVAL;
-
- dev_vdbg(ep->dev->dev, "kick_dma %s req %p dma %08llx\n",
- ep->ep.name, req, (unsigned long long) req->req.dma);
-
- net2272_ep_write(ep, EP_RSPSET, 1 << ALT_NAK_OUT_PACKETS);
-
- /* The NET2272 can only use DMA on one endpoint at a time */
- if (ep->dev->dma_busy)
- return -EBUSY;
-
- /* Make sure we only DMA an even number of bytes (we'll use
- * pio to complete the transfer)
- */
- size = req->req.length;
- size &= ~1;
-
- /* device-to-host transfer */
- if (ep->is_in) {
- /* initialize platform's dma controller */
- if (net2272_request_dma(ep->dev, ep->num, req->req.dma, size, 0))
- /* unable to obtain DMA channel; return error and use pio mode */
- return -EBUSY;
- req->req.actual += size;
-
- /* host-to-device transfer */
- } else {
- tmp = net2272_ep_read(ep, EP_STAT0);
-
- /* initialize platform's dma controller */
- if (net2272_request_dma(ep->dev, ep->num, req->req.dma, size, 1))
- /* unable to obtain DMA channel; return error and use pio mode */
- return -EBUSY;
-
- if (!(tmp & (1 << BUFFER_EMPTY)))
- ep->not_empty = 1;
- else
- ep->not_empty = 0;
-
-
- /* allow the endpoint's buffer to fill */
- net2272_ep_write(ep, EP_RSPCLR, 1 << ALT_NAK_OUT_PACKETS);
-
- /* this transfer completed and data's already in the fifo
- * return error so pio gets used.
- */
- if (tmp & (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT)) {
-
- /* deassert dreq */
- net2272_write(ep->dev, DMAREQ,
- (0 << DMA_BUFFER_VALID) |
- (0 << DMA_REQUEST_ENABLE) |
- (1 << DMA_CONTROL_DACK) |
- (ep->dev->dma_eot_polarity << EOT_POLARITY) |
- (ep->dev->dma_dack_polarity << DACK_POLARITY) |
- (ep->dev->dma_dreq_polarity << DREQ_POLARITY) |
- ((ep->num >> 1) << DMA_ENDPOINT_SELECT));
-
- return -EBUSY;
- }
- }
-
- /* Don't use per-packet interrupts: use dma interrupts only */
- net2272_ep_write(ep, EP_IRQENB, 0);
-
- net2272_start_dma(ep->dev);
-
- return 0;
-}
-
-static void net2272_cancel_dma(struct net2272 *dev)
-{
-#ifdef CONFIG_USB_PCI
- switch (dev->dev_id) {
- case PCI_DEVICE_ID_RDK1:
- writeb(0, dev->rdk1.plx9054_base_addr + DMACSR0);
- writeb(1 << CHANNEL_ABORT, dev->rdk1.plx9054_base_addr + DMACSR0);
- while (!(readb(dev->rdk1.plx9054_base_addr + DMACSR0) &
- (1 << CHANNEL_DONE)))
- continue; /* wait for dma to stabalize */
-
- /* dma abort generates an interrupt */
- writeb(1 << CHANNEL_CLEAR_INTERRUPT,
- dev->rdk1.plx9054_base_addr + DMACSR0);
- break;
- }
-#endif
-
- dev->dma_busy = 0;
-}
-
-/*---------------------------------------------------------------------------*/
-
-static int
-net2272_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
-{
- struct net2272_request *req;
- struct net2272_ep *ep;
- struct net2272 *dev;
- unsigned long flags;
- int status = -1;
- u8 s;
-
- req = container_of(_req, struct net2272_request, req);
- if (!_req || !_req->complete || !_req->buf
- || !list_empty(&req->queue))
- return -EINVAL;
- ep = container_of(_ep, struct net2272_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;
-
- /* set up dma mapping in case the caller didn't */
- if (use_dma && ep->dma) {
- status = usb_gadget_map_request(&dev->gadget, _req,
- ep->is_in);
- if (status)
- return status;
- }
-
- dev_vdbg(dev->dev, "%s queue req %p, len %d buf %p dma %08llx %s\n",
- _ep->name, _req, _req->length, _req->buf,
- (unsigned long long) _req->dma, _req->zero ? "zero" : "!zero");
-
- spin_lock_irqsave(&dev->lock, flags);
-
- _req->status = -EINPROGRESS;
- _req->actual = 0;
-
- /* kickstart this i/o queue? */
- if (list_empty(&ep->queue) && !ep->stopped) {
- /* maybe there's no control data, just status ack */
- if (ep->num == 0 && _req->length == 0) {
- net2272_done(ep, req, 0);
- dev_vdbg(dev->dev, "%s status ack\n", ep->ep.name);
- goto done;
- }
-
- /* Return zlp, don't let it block subsequent packets */
- s = net2272_ep_read(ep, EP_STAT0);
- if (s & (1 << BUFFER_EMPTY)) {
- /* Buffer is empty check for a blocking zlp, handle it */
- if ((s & (1 << NAK_OUT_PACKETS)) &&
- net2272_ep_read(ep, EP_STAT1) & (1 << LOCAL_OUT_ZLP)) {
- dev_dbg(dev->dev, "WARNING: returning ZLP short packet termination!\n");
- /*
- * Request is going to terminate with a short packet ...
- * hope the client is ready for it!
- */
- status = net2272_read_fifo(ep, req);
- /* clear short packet naking */
- net2272_ep_write(ep, EP_STAT0, (1 << NAK_OUT_PACKETS));
- goto done;
- }
- }
-
- /* try dma first */
- status = net2272_kick_dma(ep, req);
-
- if (status < 0) {
- /* dma failed (most likely in use by another endpoint)
- * fallback to pio
- */
- status = 0;
-
- if (ep->is_in)
- status = net2272_write_fifo(ep, req);
- else {
- s = net2272_ep_read(ep, EP_STAT0);
- if ((s & (1 << BUFFER_EMPTY)) == 0)
- status = net2272_read_fifo(ep, req);
- }
-
- if (unlikely(status != 0)) {
- if (status > 0)
- status = 0;
- req = NULL;
- }
- }
- }
- if (likely(req))
- list_add_tail(&req->queue, &ep->queue);
-
- if (likely(!list_empty(&ep->queue)))
- net2272_ep_write(ep, EP_RSPCLR, 1 << ALT_NAK_OUT_PACKETS);
- done:
- spin_unlock_irqrestore(&dev->lock, flags);
-
- return 0;
-}
-
-/* dequeue ALL requests */
-static void
-net2272_dequeue_all(struct net2272_ep *ep)
-{
- struct net2272_request *req;
-
- /* called with spinlock held */
- ep->stopped = 1;
-
- while (!list_empty(&ep->queue)) {
- req = list_entry(ep->queue.next,
- struct net2272_request,
- queue);
- net2272_done(ep, req, -ESHUTDOWN);
- }
-}
-
-/* dequeue JUST ONE request */
-static int
-net2272_dequeue(struct usb_ep *_ep, struct usb_request *_req)
-{
- struct net2272_ep *ep;
- struct net2272_request *req = NULL, *iter;
- unsigned long flags;
- int stopped;
-
- ep = container_of(_ep, struct net2272_ep, ep);
- if (!_ep || (!ep->desc && ep->num != 0) || !_req)
- return -EINVAL;
-
- spin_lock_irqsave(&ep->dev->lock, flags);
- stopped = ep->stopped;
- ep->stopped = 1;
-
- /* make sure it's still queued on this endpoint */
- list_for_each_entry(iter, &ep->queue, queue) {
- if (&iter->req != _req)
- continue;
- req = iter;
- break;
- }
- if (!req) {
- ep->stopped = stopped;
- spin_unlock_irqrestore(&ep->dev->lock, flags);
- return -EINVAL;
- }
-
- /* queue head may be partially complete */
- if (ep->queue.next == &req->queue) {
- dev_dbg(ep->dev->dev, "unlink (%s) pio\n", _ep->name);
- net2272_done(ep, req, -ECONNRESET);
- }
- ep->stopped = stopped;
-
- spin_unlock_irqrestore(&ep->dev->lock, flags);
- return 0;
-}
-
-/*---------------------------------------------------------------------------*/
-
-static int
-net2272_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedged)
-{
- struct net2272_ep *ep;
- unsigned long flags;
- int ret = 0;
-
- ep = container_of(_ep, struct net2272_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 */ && usb_endpoint_xfer_isoc(ep->desc))
- return -EINVAL;
-
- spin_lock_irqsave(&ep->dev->lock, flags);
- if (!list_empty(&ep->queue))
- ret = -EAGAIN;
- else if (ep->is_in && value && net2272_fifo_status(_ep) != 0)
- ret = -EAGAIN;
- else {
- dev_vdbg(ep->dev->dev, "%s %s %s\n", _ep->name,
- value ? "set" : "clear",
- wedged ? "wedge" : "halt");
- /* set/clear */
- 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;
- }
- }
- spin_unlock_irqrestore(&ep->dev->lock, flags);
-
- return ret;
-}
-
-static int
-net2272_set_halt(struct usb_ep *_ep, int value)
-{
- return net2272_set_halt_and_wedge(_ep, value, 0);
-}
-
-static int
-net2272_set_wedge(struct usb_ep *_ep)
-{
- if (!_ep || _ep->name == ep0name)
- return -EINVAL;
- return net2272_set_halt_and_wedge(_ep, 1, 1);
-}
-
-static int
-net2272_fifo_status(struct usb_ep *_ep)
-{
- struct net2272_ep *ep;
- u16 avail;
-
- ep = container_of(_ep, struct net2272_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 = net2272_ep_read(ep, EP_AVAIL1) << 8;
- avail |= net2272_ep_read(ep, EP_AVAIL0);
- if (avail > ep->fifo_size)
- return -EOVERFLOW;
- if (ep->is_in)
- avail = ep->fifo_size - avail;
- return avail;
-}
-
-static void
-net2272_fifo_flush(struct usb_ep *_ep)
-{
- struct net2272_ep *ep;
-
- ep = container_of(_ep, struct net2272_ep, ep);
- if (!_ep || (!ep->desc && ep->num != 0))
- return;
- if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
- return;
-
- net2272_ep_write(ep, EP_STAT1, 1 << BUFFER_FLUSH);
-}
-
-static const struct usb_ep_ops net2272_ep_ops = {
- .enable = net2272_enable,
- .disable = net2272_disable,
-
- .alloc_request = net2272_alloc_request,
- .free_request = net2272_free_request,
-
- .queue = net2272_queue,
- .dequeue = net2272_dequeue,
-
- .set_halt = net2272_set_halt,
- .set_wedge = net2272_set_wedge,
- .fifo_status = net2272_fifo_status,
- .fifo_flush = net2272_fifo_flush,
-};
-
-/*---------------------------------------------------------------------------*/
-
-static int
-net2272_get_frame(struct usb_gadget *_gadget)
-{
- struct net2272 *dev;
- unsigned long flags;
- u16 ret;
-
- if (!_gadget)
- return -ENODEV;
- dev = container_of(_gadget, struct net2272, gadget);
- spin_lock_irqsave(&dev->lock, flags);
-
- ret = net2272_read(dev, FRAME1) << 8;
- ret |= net2272_read(dev, FRAME0);
-
- spin_unlock_irqrestore(&dev->lock, flags);
- return ret;
-}
-
-static int
-net2272_wakeup(struct usb_gadget *_gadget)
-{
- struct net2272 *dev;
- u8 tmp;
- unsigned long flags;
-
- if (!_gadget)
- return 0;
- dev = container_of(_gadget, struct net2272, gadget);
-
- spin_lock_irqsave(&dev->lock, flags);
- tmp = net2272_read(dev, USBCTL0);
- if (tmp & (1 << IO_WAKEUP_ENABLE))
- net2272_write(dev, USBCTL1, (1 << GENERATE_RESUME));
-
- spin_unlock_irqrestore(&dev->lock, flags);
-
- return 0;
-}
-
-static int
-net2272_set_selfpowered(struct usb_gadget *_gadget, int value)
-{
- if (!_gadget)
- return -ENODEV;
-
- _gadget->is_selfpowered = (value != 0);
-
- return 0;
-}
-
-static int
-net2272_pullup(struct usb_gadget *_gadget, int is_on)
-{
- struct net2272 *dev;
- u8 tmp;
- unsigned long flags;
-
- if (!_gadget)
- return -ENODEV;
- dev = container_of(_gadget, struct net2272, gadget);
-
- spin_lock_irqsave(&dev->lock, flags);
- tmp = net2272_read(dev, USBCTL0);
- dev->softconnect = (is_on != 0);
- if (is_on)
- tmp |= (1 << USB_DETECT_ENABLE);
- else
- tmp &= ~(1 << USB_DETECT_ENABLE);
- net2272_write(dev, USBCTL0, tmp);
- spin_unlock_irqrestore(&dev->lock, flags);
-
- return 0;
-}
-
-static int net2272_start(struct usb_gadget *_gadget,
- struct usb_gadget_driver *driver);
-static int net2272_stop(struct usb_gadget *_gadget);
-static void net2272_async_callbacks(struct usb_gadget *_gadget, bool enable);
-
-static const struct usb_gadget_ops net2272_ops = {
- .get_frame = net2272_get_frame,
- .wakeup = net2272_wakeup,
- .set_selfpowered = net2272_set_selfpowered,
- .pullup = net2272_pullup,
- .udc_start = net2272_start,
- .udc_stop = net2272_stop,
- .udc_async_callbacks = net2272_async_callbacks,
-};
-
-/*---------------------------------------------------------------------------*/
-
-static ssize_t
-registers_show(struct device *_dev, struct device_attribute *attr, char *buf)
-{
- struct net2272 *dev;
- char *next;
- unsigned size, t;
- unsigned long flags;
- u8 t1, t2;
- int i;
- const char *s;
-
- dev = dev_get_drvdata(_dev);
- next = buf;
- size = PAGE_SIZE;
- spin_lock_irqsave(&dev->lock, flags);
-
- /* Main Control Registers */
- t = scnprintf(next, size, "%s version %s,"
- "chiprev %02x, locctl %02x\n"
- "irqenb0 %02x irqenb1 %02x "
- "irqstat0 %02x irqstat1 %02x\n",
- driver_name, driver_vers, dev->chiprev,
- net2272_read(dev, LOCCTL),
- net2272_read(dev, IRQENB0),
- net2272_read(dev, IRQENB1),
- net2272_read(dev, IRQSTAT0),
- net2272_read(dev, IRQSTAT1));
- size -= t;
- next += t;
-
- /* DMA */
- t1 = net2272_read(dev, DMAREQ);
- t = scnprintf(next, size, "\ndmareq %02x: %s %s%s%s%s\n",
- t1, ep_name[(t1 & 0x01) + 1],
- t1 & (1 << DMA_CONTROL_DACK) ? "dack " : "",
- t1 & (1 << DMA_REQUEST_ENABLE) ? "reqenb " : "",
- t1 & (1 << DMA_REQUEST) ? "req " : "",
- t1 & (1 << DMA_BUFFER_VALID) ? "valid " : "");
- size -= t;
- next += t;
-
- /* USB Control Registers */
- t1 = net2272_read(dev, USBCTL1);
- if (t1 & (1 << VBUS_PIN)) {
- if (t1 & (1 << USB_HIGH_SPEED))
- s = "high speed";
- else if (dev->gadget.speed == USB_SPEED_UNKNOWN)
- s = "powered";
- else
- s = "full speed";
- } else
- s = "not attached";
- t = scnprintf(next, size,
- "usbctl0 %02x usbctl1 %02x addr 0x%02x (%s)\n",
- net2272_read(dev, USBCTL0), t1,
- net2272_read(dev, OURADDR), s);
- size -= t;
- next += t;
-
- /* Endpoint Registers */
- for (i = 0; i < 4; ++i) {
- struct net2272_ep *ep;
-
- ep = &dev->ep[i];
- if (i && !ep->desc)
- continue;
-
- t1 = net2272_ep_read(ep, EP_CFG);
- t2 = net2272_ep_read(ep, EP_RSPSET);
- t = scnprintf(next, size,
- "\n%s\tcfg %02x rsp (%02x) %s%s%s%s%s%s%s%s"
- "irqenb %02x\n",
- ep->ep.name, t1, t2,
- (t2 & (1 << ALT_NAK_OUT_PACKETS)) ? "NAK " : "",
- (t2 & (1 << HIDE_STATUS_PHASE)) ? "hide " : "",
- (t2 & (1 << AUTOVALIDATE)) ? "auto " : "",
- (t2 & (1 << INTERRUPT_MODE)) ? "interrupt " : "",
- (t2 & (1 << CONTROL_STATUS_PHASE_HANDSHAKE)) ? "status " : "",
- (t2 & (1 << NAK_OUT_PACKETS_MODE)) ? "NAKmode " : "",
- (t2 & (1 << ENDPOINT_TOGGLE)) ? "DATA1 " : "DATA0 ",
- (t2 & (1 << ENDPOINT_HALT)) ? "HALT " : "",
- net2272_ep_read(ep, EP_IRQENB));
- size -= t;
- next += t;
-
- t = scnprintf(next, size,
- "\tstat0 %02x stat1 %02x avail %04x "
- "(ep%d%s-%s)%s\n",
- net2272_ep_read(ep, EP_STAT0),
- net2272_ep_read(ep, EP_STAT1),
- (net2272_ep_read(ep, EP_AVAIL1) << 8) | net2272_ep_read(ep, EP_AVAIL0),
- t1 & 0x0f,
- ep->is_in ? "in" : "out",
- type_string(t1 >> 5),
- ep->stopped ? "*" : "");
- size -= t;
- next += t;
-
- t = scnprintf(next, size,
- "\tep_transfer %06x\n",
- ((net2272_ep_read(ep, EP_TRANSFER2) & 0xff) << 16) |
- ((net2272_ep_read(ep, EP_TRANSFER1) & 0xff) << 8) |
- ((net2272_ep_read(ep, EP_TRANSFER0) & 0xff)));
- size -= t;
- next += t;
-
- t1 = net2272_ep_read(ep, EP_BUFF_STATES) & 0x03;
- t2 = (net2272_ep_read(ep, EP_BUFF_STATES) >> 2) & 0x03;
- t = scnprintf(next, size,
- "\tbuf-a %s buf-b %s\n",
- buf_state_string(t1),
- buf_state_string(t2));
- size -= t;
- next += t;
- }
-
- spin_unlock_irqrestore(&dev->lock, flags);
-
- return PAGE_SIZE - size;
-}
-static DEVICE_ATTR_RO(registers);
-
-/*---------------------------------------------------------------------------*/
-
-static void
-net2272_set_fifo_mode(struct net2272 *dev, int mode)
-{
- u8 tmp;
-
- tmp = net2272_read(dev, LOCCTL) & 0x3f;
- tmp |= (mode << 6);
- net2272_write(dev, LOCCTL, tmp);
-
- INIT_LIST_HEAD(&dev->gadget.ep_list);
-
- /* always ep-a, ep-c ... maybe not ep-b */
- list_add_tail(&dev->ep[1].ep.ep_list, &dev->gadget.ep_list);
-
- switch (mode) {
- case 0:
- list_add_tail(&dev->ep[2].ep.ep_list, &dev->gadget.ep_list);
- dev->ep[1].fifo_size = dev->ep[2].fifo_size = 512;
- break;
- case 1:
- list_add_tail(&dev->ep[2].ep.ep_list, &dev->gadget.ep_list);
- dev->ep[1].fifo_size = 1024;
- dev->ep[2].fifo_size = 512;
- break;
- case 2:
- list_add_tail(&dev->ep[2].ep.ep_list, &dev->gadget.ep_list);
- dev->ep[1].fifo_size = dev->ep[2].fifo_size = 1024;
- break;
- case 3:
- dev->ep[1].fifo_size = 1024;
- break;
- }
-
- /* ep-c is always 2 512 byte buffers */
- list_add_tail(&dev->ep[3].ep.ep_list, &dev->gadget.ep_list);
- dev->ep[3].fifo_size = 512;
-}
-
-/*---------------------------------------------------------------------------*/
-
-static void
-net2272_usb_reset(struct net2272 *dev)
-{
- dev->gadget.speed = USB_SPEED_UNKNOWN;
-
- net2272_cancel_dma(dev);
-
- net2272_write(dev, IRQENB0, 0);
- net2272_write(dev, IRQENB1, 0);
-
- /* clear irq state */
- net2272_write(dev, IRQSTAT0, 0xff);
- net2272_write(dev, IRQSTAT1, ~(1 << SUSPEND_REQUEST_INTERRUPT));
-
- net2272_write(dev, DMAREQ,
- (0 << DMA_BUFFER_VALID) |
- (0 << DMA_REQUEST_ENABLE) |
- (1 << DMA_CONTROL_DACK) |
- (dev->dma_eot_polarity << EOT_POLARITY) |
- (dev->dma_dack_polarity << DACK_POLARITY) |
- (dev->dma_dreq_polarity << DREQ_POLARITY) |
- ((dma_ep >> 1) << DMA_ENDPOINT_SELECT));
-
- net2272_cancel_dma(dev);
- net2272_set_fifo_mode(dev, (fifo_mode <= 3) ? fifo_mode : 0);
-
- /* Set the NET2272 ep fifo data width to 16-bit mode and for correct byte swapping
- * note that the higher level gadget drivers are expected to convert data to little endian.
- * Enable byte swap for your local bus/cpu if needed by setting BYTE_SWAP in LOCCTL here
- */
- net2272_write(dev, LOCCTL, net2272_read(dev, LOCCTL) | (1 << DATA_WIDTH));
- net2272_write(dev, LOCCTL1, (dma_mode << DMA_MODE));
-}
-
-static void
-net2272_usb_reinit(struct net2272 *dev)
-{
- int i;
-
- /* basic endpoint init */
- for (i = 0; i < 4; ++i) {
- struct net2272_ep *ep = &dev->ep[i];
-
- ep->ep.name = ep_name[i];
- ep->dev = dev;
- ep->num = i;
- ep->not_empty = 0;
-
- if (use_dma && ep->num == dma_ep)
- ep->dma = 1;
-
- if (i > 0 && i <= 3)
- ep->fifo_size = 512;
- else
- ep->fifo_size = 64;
- net2272_ep_reset(ep);
-
- if (i == 0) {
- ep->ep.caps.type_control = true;
- } else {
- ep->ep.caps.type_iso = true;
- ep->ep.caps.type_bulk = true;
- ep->ep.caps.type_int = true;
- }
-
- ep->ep.caps.dir_in = true;
- ep->ep.caps.dir_out = true;
- }
- usb_ep_set_maxpacket_limit(&dev->ep[0].ep, 64);
-
- dev->gadget.ep0 = &dev->ep[0].ep;
- dev->ep[0].stopped = 0;
- INIT_LIST_HEAD(&dev->gadget.ep0->ep_list);
-}
-
-static void
-net2272_ep0_start(struct net2272 *dev)
-{
- struct net2272_ep *ep0 = &dev->ep[0];
-
- net2272_ep_write(ep0, EP_RSPSET,
- (1 << NAK_OUT_PACKETS_MODE) |
- (1 << ALT_NAK_OUT_PACKETS));
- net2272_ep_write(ep0, EP_RSPCLR,
- (1 << HIDE_STATUS_PHASE) |
- (1 << CONTROL_STATUS_PHASE_HANDSHAKE));
- net2272_write(dev, USBCTL0,
- (dev->softconnect << USB_DETECT_ENABLE) |
- (1 << USB_ROOT_PORT_WAKEUP_ENABLE) |
- (1 << IO_WAKEUP_ENABLE));
- net2272_write(dev, IRQENB0,
- (1 << SETUP_PACKET_INTERRUPT_ENABLE) |
- (1 << ENDPOINT_0_INTERRUPT_ENABLE) |
- (1 << DMA_DONE_INTERRUPT_ENABLE));
- net2272_write(dev, IRQENB1,
- (1 << VBUS_INTERRUPT_ENABLE) |
- (1 << ROOT_PORT_RESET_INTERRUPT_ENABLE) |
- (1 << SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE));
-}
-
-/* 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 net2272_start(struct usb_gadget *_gadget,
- struct usb_gadget_driver *driver)
-{
- struct net2272 *dev;
- unsigned i;
-
- if (!driver || !driver->setup ||
- driver->max_speed != USB_SPEED_HIGH)
- return -EINVAL;
-
- dev = container_of(_gadget, struct net2272, gadget);
-
- for (i = 0; i < 4; ++i)
- dev->ep[i].irqs = 0;
- /* hook up the driver ... */
- dev->softconnect = 1;
- dev->driver = driver;
-
- /* ... then enable host detection and ep0; and we're ready
- * for set_configuration as well as eventual disconnect.
- */
- net2272_ep0_start(dev);
-
- return 0;
-}
-
-static void
-stop_activity(struct net2272 *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.
- */
- net2272_usb_reset(dev);
- for (i = 0; i < 4; ++i)
- net2272_dequeue_all(&dev->ep[i]);
-
- /* report disconnect; the driver is already quiesced */
- if (dev->async_callbacks && driver) {
- spin_unlock(&dev->lock);
- driver->disconnect(&dev->gadget);
- spin_lock(&dev->lock);
- }
-
- net2272_usb_reinit(dev);
-}
-
-static int net2272_stop(struct usb_gadget *_gadget)
-{
- struct net2272 *dev;
- unsigned long flags;
-
- dev = container_of(_gadget, struct net2272, gadget);
-
- spin_lock_irqsave(&dev->lock, flags);
- stop_activity(dev, NULL);
- spin_unlock_irqrestore(&dev->lock, flags);
-
- dev->driver = NULL;
-
- return 0;
-}
-
-static void net2272_async_callbacks(struct usb_gadget *_gadget, bool enable)
-{
- struct net2272 *dev = container_of(_gadget, struct net2272, gadget);
-
- spin_lock_irq(&dev->lock);
- dev->async_callbacks = enable;
- spin_unlock_irq(&dev->lock);
-}
-
-/*---------------------------------------------------------------------------*/
-/* handle ep-a/ep-b dma completions */
-static void
-net2272_handle_dma(struct net2272_ep *ep)
-{
- struct net2272_request *req;
- unsigned len;
- int status;
-
- if (!list_empty(&ep->queue))
- req = list_entry(ep->queue.next,
- struct net2272_request, queue);
- else
- req = NULL;
-
- dev_vdbg(ep->dev->dev, "handle_dma %s req %p\n", ep->ep.name, req);
-
- /* Ensure DREQ is de-asserted */
- net2272_write(ep->dev, DMAREQ,
- (0 << DMA_BUFFER_VALID)
- | (0 << DMA_REQUEST_ENABLE)
- | (1 << DMA_CONTROL_DACK)
- | (ep->dev->dma_eot_polarity << EOT_POLARITY)
- | (ep->dev->dma_dack_polarity << DACK_POLARITY)
- | (ep->dev->dma_dreq_polarity << DREQ_POLARITY)
- | (ep->dma << DMA_ENDPOINT_SELECT));
-
- ep->dev->dma_busy = 0;
-
- net2272_ep_write(ep, EP_IRQENB,
- (1 << DATA_PACKET_RECEIVED_INTERRUPT_ENABLE)
- | (1 << DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE)
- | net2272_ep_read(ep, EP_IRQENB));
-
- /* device-to-host transfer completed */
- if (ep->is_in) {
- /* validate a short packet or zlp if necessary */
- if ((req->req.length % ep->ep.maxpacket != 0) ||
- req->req.zero)
- set_fifo_bytecount(ep, 0);
-
- net2272_done(ep, req, 0);
- if (!list_empty(&ep->queue)) {
- req = list_entry(ep->queue.next,
- struct net2272_request, queue);
- status = net2272_kick_dma(ep, req);
- if (status < 0)
- net2272_pio_advance(ep);
- }
-
- /* host-to-device transfer completed */
- } else {
- /* terminated with a short packet? */
- if (net2272_read(ep->dev, IRQSTAT0) &
- (1 << DMA_DONE_INTERRUPT)) {
- /* abort system dma */
- net2272_cancel_dma(ep->dev);
- }
-
- /* EP_TRANSFER will contain the number of bytes
- * actually received.
- * NOTE: There is no overflow detection on EP_TRANSFER:
- * We can't deal with transfers larger than 2^24 bytes!
- */
- len = (net2272_ep_read(ep, EP_TRANSFER2) << 16)
- | (net2272_ep_read(ep, EP_TRANSFER1) << 8)
- | (net2272_ep_read(ep, EP_TRANSFER0));
-
- if (ep->not_empty)
- len += 4;
-
- req->req.actual += len;
-
- /* get any remaining data */
- net2272_pio_advance(ep);
- }
-}
-
-/*---------------------------------------------------------------------------*/
-
-static void
-net2272_handle_ep(struct net2272_ep *ep)
-{
- struct net2272_request *req;
- u8 stat0, stat1;
-
- if (!list_empty(&ep->queue))
- req = list_entry(ep->queue.next,
- struct net2272_request, queue);
- else
- req = NULL;
-
- /* ack all, and handle what we care about */
- stat0 = net2272_ep_read(ep, EP_STAT0);
- stat1 = net2272_ep_read(ep, EP_STAT1);
- ep->irqs++;
-
- dev_vdbg(ep->dev->dev, "%s ack ep_stat0 %02x, ep_stat1 %02x, req %p\n",
- ep->ep.name, stat0, stat1, req ? &req->req : NULL);
-
- net2272_ep_write(ep, EP_STAT0, stat0 &
- ~((1 << NAK_OUT_PACKETS)
- | (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT)));
- net2272_ep_write(ep, EP_STAT1, stat1);
-
- /* data packet(s) received (in the fifo, OUT)
- * direction must be validated, otherwise control read status phase
- * could be interpreted as a valid packet
- */
- if (!ep->is_in && (stat0 & (1 << DATA_PACKET_RECEIVED_INTERRUPT)))
- net2272_pio_advance(ep);
- /* data packet(s) transmitted (IN) */
- else if (stat0 & (1 << DATA_PACKET_TRANSMITTED_INTERRUPT))
- net2272_pio_advance(ep);
-}
-
-static struct net2272_ep *
-net2272_get_ep_by_addr(struct net2272 *dev, u16 wIndex)
-{
- struct net2272_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;
-}
-
-/*
- * USB Test Packet:
- * JKJKJKJK * 9
- * JJKKJJKK * 8
- * JJJJKKKK * 8
- * JJJJJJJKKKKKKK * 8
- * JJJJJJJK * 8
- * {JKKKKKKK * 10}, JK
- */
-static const u8 net2272_test_packet[] = {
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
- 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE,
- 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0x7F, 0xBF, 0xDF, 0xEF, 0xF7, 0xFB, 0xFD,
- 0xFC, 0x7E, 0xBF, 0xDF, 0xEF, 0xF7, 0xFD, 0x7E
-};
-
-static void
-net2272_set_test_mode(struct net2272 *dev, int mode)
-{
- int i;
-
- /* Disable all net2272 interrupts:
- * Nothing but a power cycle should stop the test.
- */
- net2272_write(dev, IRQENB0, 0x00);
- net2272_write(dev, IRQENB1, 0x00);
-
- /* Force tranceiver to high-speed */
- net2272_write(dev, XCVRDIAG, 1 << FORCE_HIGH_SPEED);
-
- net2272_write(dev, PAGESEL, 0);
- net2272_write(dev, EP_STAT0, 1 << DATA_PACKET_TRANSMITTED_INTERRUPT);
- net2272_write(dev, EP_RSPCLR,
- (1 << CONTROL_STATUS_PHASE_HANDSHAKE)
- | (1 << HIDE_STATUS_PHASE));
- net2272_write(dev, EP_CFG, 1 << ENDPOINT_DIRECTION);
- net2272_write(dev, EP_STAT1, 1 << BUFFER_FLUSH);
-
- /* wait for status phase to complete */
- while (!(net2272_read(dev, EP_STAT0) &
- (1 << DATA_PACKET_TRANSMITTED_INTERRUPT)))
- ;
-
- /* Enable test mode */
- net2272_write(dev, USBTEST, mode);
-
- /* load test packet */
- if (mode == USB_TEST_PACKET) {
- /* switch to 8 bit mode */
- net2272_write(dev, LOCCTL, net2272_read(dev, LOCCTL) &
- ~(1 << DATA_WIDTH));
-
- for (i = 0; i < sizeof(net2272_test_packet); ++i)
- net2272_write(dev, EP_DATA, net2272_test_packet[i]);
-
- /* Validate test packet */
- net2272_write(dev, EP_TRANSFER0, 0);
- }
-}
-
-static void
-net2272_handle_stat0_irqs(struct net2272 *dev, u8 stat)
-{
- struct net2272_ep *ep;
- u8 num, scratch;
-
- /* starting a control request? */
- if (unlikely(stat & (1 << SETUP_PACKET_INTERRUPT))) {
- union {
- u8 raw[8];
- struct usb_ctrlrequest r;
- } u;
- int tmp = 0;
- struct net2272_request *req;
-
- if (dev->gadget.speed == USB_SPEED_UNKNOWN) {
- if (net2272_read(dev, USBCTL1) & (1 << USB_HIGH_SPEED))
- dev->gadget.speed = USB_SPEED_HIGH;
- else
- dev->gadget.speed = USB_SPEED_FULL;
- dev_dbg(dev->dev, "%s\n",
- usb_speed_string(dev->gadget.speed));
- }
-
- ep = &dev->ep[0];
- ep->irqs++;
-
- /* make sure any leftover interrupt state is cleared */
- stat &= ~(1 << ENDPOINT_0_INTERRUPT);
- while (!list_empty(&ep->queue)) {
- req = list_entry(ep->queue.next,
- struct net2272_request, queue);
- net2272_done(ep, req,
- (req->req.actual == req->req.length) ? 0 : -EPROTO);
- }
- ep->stopped = 0;
- dev->protocol_stall = 0;
- net2272_ep_write(ep, EP_STAT0,
- (1 << DATA_IN_TOKEN_INTERRUPT)
- | (1 << DATA_OUT_TOKEN_INTERRUPT)
- | (1 << DATA_PACKET_TRANSMITTED_INTERRUPT)
- | (1 << DATA_PACKET_RECEIVED_INTERRUPT)
- | (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT));
- net2272_ep_write(ep, EP_STAT1,
- (1 << TIMEOUT)
- | (1 << USB_OUT_ACK_SENT)
- | (1 << USB_OUT_NAK_SENT)
- | (1 << USB_IN_ACK_RCVD)
- | (1 << USB_IN_NAK_SENT)
- | (1 << USB_STALL_SENT)
- | (1 << LOCAL_OUT_ZLP));
-
- /*
- * Ensure Control Read pre-validation setting is beyond maximum size
- * - Control Writes can leave non-zero values in EP_TRANSFER. If
- * an EP0 transfer following the Control Write is a Control Read,
- * the NET2272 sees the non-zero EP_TRANSFER as an unexpected
- * pre-validation count.
- * - Setting EP_TRANSFER beyond the maximum EP0 transfer size ensures
- * the pre-validation count cannot cause an unexpected validatation
- */
- net2272_write(dev, PAGESEL, 0);
- net2272_write(dev, EP_TRANSFER2, 0xff);
- net2272_write(dev, EP_TRANSFER1, 0xff);
- net2272_write(dev, EP_TRANSFER0, 0xff);
-
- u.raw[0] = net2272_read(dev, SETUP0);
- u.raw[1] = net2272_read(dev, SETUP1);
- u.raw[2] = net2272_read(dev, SETUP2);
- u.raw[3] = net2272_read(dev, SETUP3);
- u.raw[4] = net2272_read(dev, SETUP4);
- u.raw[5] = net2272_read(dev, SETUP5);
- u.raw[6] = net2272_read(dev, SETUP6);
- u.raw[7] = net2272_read(dev, SETUP7);
- /*
- * If you have a big endian cpu make sure le16_to_cpus
- * performs the proper byte swapping here...
- */
- le16_to_cpus(&u.r.wValue);
- le16_to_cpus(&u.r.wIndex);
- le16_to_cpus(&u.r.wLength);
-
- /* ack the irq */
- net2272_write(dev, IRQSTAT0, 1 << SETUP_PACKET_INTERRUPT);
- stat ^= (1 << SETUP_PACKET_INTERRUPT);
-
- /* watch control traffic at the token level, and force
- * synchronization before letting the status phase happen.
- */
- ep->is_in = (u.r.bRequestType & USB_DIR_IN) != 0;
- if (ep->is_in) {
- scratch = (1 << DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE)
- | (1 << DATA_OUT_TOKEN_INTERRUPT_ENABLE)
- | (1 << DATA_IN_TOKEN_INTERRUPT_ENABLE);
- stop_out_naking(ep);
- } else
- scratch = (1 << DATA_PACKET_RECEIVED_INTERRUPT_ENABLE)
- | (1 << DATA_OUT_TOKEN_INTERRUPT_ENABLE)
- | (1 << DATA_IN_TOKEN_INTERRUPT_ENABLE);
- net2272_ep_write(ep, EP_IRQENB, scratch);
-
- if ((u.r.bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD)
- goto delegate;
- switch (u.r.bRequest) {
- case USB_REQ_GET_STATUS: {
- struct net2272_ep *e;
- u16 status = 0;
-
- switch (u.r.bRequestType & USB_RECIP_MASK) {
- case USB_RECIP_ENDPOINT:
- e = net2272_get_ep_by_addr(dev, u.r.wIndex);
- if (!e || u.r.wLength > 2)
- goto do_stall;
- if (net2272_ep_read(e, EP_RSPSET) & (1 << ENDPOINT_HALT))
- status = cpu_to_le16(1);
- else
- status = cpu_to_le16(0);
-
- /* don't bother with a request object! */
- net2272_ep_write(&dev->ep[0], EP_IRQENB, 0);
- writew(status, net2272_reg_addr(dev, EP_DATA));
- set_fifo_bytecount(&dev->ep[0], 0);
- allow_status(ep);
- dev_vdbg(dev->dev, "%s stat %02x\n",
- ep->ep.name, status);
- goto next_endpoints;
- case USB_RECIP_DEVICE:
- if (u.r.wLength > 2)
- goto do_stall;
- if (dev->gadget.is_selfpowered)
- status = (1 << USB_DEVICE_SELF_POWERED);
-
- /* don't bother with a request object! */
- net2272_ep_write(&dev->ep[0], EP_IRQENB, 0);
- writew(status, net2272_reg_addr(dev, EP_DATA));
- set_fifo_bytecount(&dev->ep[0], 0);
- allow_status(ep);
- dev_vdbg(dev->dev, "device stat %02x\n", status);
- goto next_endpoints;
- case USB_RECIP_INTERFACE:
- if (u.r.wLength > 2)
- goto do_stall;
-
- /* don't bother with a request object! */
- net2272_ep_write(&dev->ep[0], EP_IRQENB, 0);
- writew(status, net2272_reg_addr(dev, EP_DATA));
- set_fifo_bytecount(&dev->ep[0], 0);
- allow_status(ep);
- dev_vdbg(dev->dev, "interface status %02x\n", status);
- goto next_endpoints;
- }
-
- break;
- }
- case USB_REQ_CLEAR_FEATURE: {
- struct net2272_ep *e;
-
- if (u.r.bRequestType != USB_RECIP_ENDPOINT)
- goto delegate;
- if (u.r.wValue != USB_ENDPOINT_HALT ||
- u.r.wLength != 0)
- goto do_stall;
- e = net2272_get_ep_by_addr(dev, u.r.wIndex);
- if (!e)
- goto do_stall;
- if (e->wedged) {
- dev_vdbg(dev->dev, "%s wedged, halt not cleared\n",
- ep->ep.name);
- } else {
- dev_vdbg(dev->dev, "%s clear halt\n", ep->ep.name);
- clear_halt(e);
- }
- allow_status(ep);
- goto next_endpoints;
- }
- case USB_REQ_SET_FEATURE: {
- struct net2272_ep *e;
-
- if (u.r.bRequestType == USB_RECIP_DEVICE) {
- if (u.r.wIndex != NORMAL_OPERATION)
- net2272_set_test_mode(dev, (u.r.wIndex >> 8));
- allow_status(ep);
- dev_vdbg(dev->dev, "test mode: %d\n", u.r.wIndex);
- goto next_endpoints;
- } else if (u.r.bRequestType != USB_RECIP_ENDPOINT)
- goto delegate;
- if (u.r.wValue != USB_ENDPOINT_HALT ||
- u.r.wLength != 0)
- goto do_stall;
- e = net2272_get_ep_by_addr(dev, u.r.wIndex);
- if (!e)
- goto do_stall;
- set_halt(e);
- allow_status(ep);
- dev_vdbg(dev->dev, "%s set halt\n", ep->ep.name);
- goto next_endpoints;
- }
- case USB_REQ_SET_ADDRESS: {
- net2272_write(dev, OURADDR, u.r.wValue & 0xff);
- allow_status(ep);
- break;
- }
- default:
- delegate:
- dev_vdbg(dev->dev, "setup %02x.%02x v%04x i%04x "
- "ep_cfg %08x\n",
- u.r.bRequestType, u.r.bRequest,
- u.r.wValue, u.r.wIndex,
- net2272_ep_read(ep, EP_CFG));
- if (dev->async_callbacks) {
- 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:
- dev_vdbg(dev->dev, "req %02x.%02x protocol STALL; stat %d\n",
- u.r.bRequestType, u.r.bRequest, tmp);
- dev->protocol_stall = 1;
- }
- /* endpoint dma irq? */
- } else if (stat & (1 << DMA_DONE_INTERRUPT)) {
- net2272_cancel_dma(dev);
- net2272_write(dev, IRQSTAT0, 1 << DMA_DONE_INTERRUPT);
- stat &= ~(1 << DMA_DONE_INTERRUPT);
- num = (net2272_read(dev, DMAREQ) & (1 << DMA_ENDPOINT_SELECT))
- ? 2 : 1;
-
- ep = &dev->ep[num];
- net2272_handle_dma(ep);
- }
-
- next_endpoints:
- /* endpoint data irq? */
- scratch = stat & 0x0f;
- stat &= ~0x0f;
- for (num = 0; scratch; num++) {
- u8 t;
-
- /* does this endpoint's FIFO and queue need tending? */
- t = 1 << num;
- if ((scratch & t) == 0)
- continue;
- scratch ^= t;
-
- ep = &dev->ep[num];
- net2272_handle_ep(ep);
- }
-
- /* some interrupts we can just ignore */
- stat &= ~(1 << SOF_INTERRUPT);
-
- if (stat)
- dev_dbg(dev->dev, "unhandled irqstat0 %02x\n", stat);
-}
-
-static void
-net2272_handle_stat1_irqs(struct net2272 *dev, u8 stat)
-{
- u8 tmp, mask;
-
- /* after disconnect there's nothing else to do! */
- tmp = (1 << VBUS_INTERRUPT) | (1 << ROOT_PORT_RESET_INTERRUPT);
- mask = (1 << USB_HIGH_SPEED) | (1 << USB_FULL_SPEED);
-
- if (stat & tmp) {
- bool reset = false;
- bool disconnect = false;
-
- /*
- * Ignore disconnects and resets if the speed hasn't been set.
- * VBUS can bounce and there's always an initial reset.
- */
- net2272_write(dev, IRQSTAT1, tmp);
- if (dev->gadget.speed != USB_SPEED_UNKNOWN) {
- if ((stat & (1 << VBUS_INTERRUPT)) &&
- (net2272_read(dev, USBCTL1) &
- (1 << VBUS_PIN)) == 0) {
- disconnect = true;
- dev_dbg(dev->dev, "disconnect %s\n",
- dev->driver->driver.name);
- } else if ((stat & (1 << ROOT_PORT_RESET_INTERRUPT)) &&
- (net2272_read(dev, USBCTL1) & mask)
- == 0) {
- reset = true;
- dev_dbg(dev->dev, "reset %s\n",
- dev->driver->driver.name);
- }
-
- if (disconnect || reset) {
- stop_activity(dev, dev->driver);
- net2272_ep0_start(dev);
- if (dev->async_callbacks) {
- spin_unlock(&dev->lock);
- if (reset)
- usb_gadget_udc_reset(&dev->gadget, dev->driver);
- else
- (dev->driver->disconnect)(&dev->gadget);
- spin_lock(&dev->lock);
- }
- return;
- }
- }
- stat &= ~tmp;
-
- if (!stat)
- return;
- }
-
- tmp = (1 << SUSPEND_REQUEST_CHANGE_INTERRUPT);
- if (stat & tmp) {
- net2272_write(dev, IRQSTAT1, tmp);
- if (stat & (1 << SUSPEND_REQUEST_INTERRUPT)) {
- if (dev->async_callbacks && dev->driver->suspend)
- dev->driver->suspend(&dev->gadget);
- if (!enable_suspend) {
- stat &= ~(1 << SUSPEND_REQUEST_INTERRUPT);
- dev_dbg(dev->dev, "Suspend disabled, ignoring\n");
- }
- } else {
- if (dev->async_callbacks && dev->driver->resume)
- dev->driver->resume(&dev->gadget);
- }
- stat &= ~tmp;
- }
-
- /* clear any other status/irqs */
- if (stat)
- net2272_write(dev, IRQSTAT1, stat);
-
- /* some status we can just ignore */
- stat &= ~((1 << CONTROL_STATUS_INTERRUPT)
- | (1 << SUSPEND_REQUEST_INTERRUPT)
- | (1 << RESUME_INTERRUPT));
- if (!stat)
- return;
- else
- dev_dbg(dev->dev, "unhandled irqstat1 %02x\n", stat);
-}
-
-static irqreturn_t net2272_irq(int irq, void *_dev)
-{
- struct net2272 *dev = _dev;
-#if defined(PLX_PCI_RDK) || defined(PLX_PCI_RDK2)
- u32 intcsr;
-#endif
-#if defined(PLX_PCI_RDK)
- u8 dmareq;
-#endif
- spin_lock(&dev->lock);
-#if defined(PLX_PCI_RDK)
- intcsr = readl(dev->rdk1.plx9054_base_addr + INTCSR);
-
- if ((intcsr & LOCAL_INTERRUPT_TEST) == LOCAL_INTERRUPT_TEST) {
- writel(intcsr & ~(1 << PCI_INTERRUPT_ENABLE),
- dev->rdk1.plx9054_base_addr + INTCSR);
- net2272_handle_stat1_irqs(dev, net2272_read(dev, IRQSTAT1));
- net2272_handle_stat0_irqs(dev, net2272_read(dev, IRQSTAT0));
- intcsr = readl(dev->rdk1.plx9054_base_addr + INTCSR);
- writel(intcsr | (1 << PCI_INTERRUPT_ENABLE),
- dev->rdk1.plx9054_base_addr + INTCSR);
- }
- if ((intcsr & DMA_CHANNEL_0_TEST) == DMA_CHANNEL_0_TEST) {
- writeb((1 << CHANNEL_CLEAR_INTERRUPT | (0 << CHANNEL_ENABLE)),
- dev->rdk1.plx9054_base_addr + DMACSR0);
-
- dmareq = net2272_read(dev, DMAREQ);
- if (dmareq & 0x01)
- net2272_handle_dma(&dev->ep[2]);
- else
- net2272_handle_dma(&dev->ep[1]);
- }
-#endif
-#if defined(PLX_PCI_RDK2)
- /* see if PCI int for us by checking irqstat */
- intcsr = readl(dev->rdk2.fpga_base_addr + RDK2_IRQSTAT);
- if (!(intcsr & (1 << NET2272_PCI_IRQ))) {
- spin_unlock(&dev->lock);
- return IRQ_NONE;
- }
- /* check dma interrupts */
-#endif
- /* Platform/device interrupt handler */
-#if !defined(PLX_PCI_RDK)
- net2272_handle_stat1_irqs(dev, net2272_read(dev, IRQSTAT1));
- net2272_handle_stat0_irqs(dev, net2272_read(dev, IRQSTAT0));
-#endif
- spin_unlock(&dev->lock);
-
- return IRQ_HANDLED;
-}
-
-static int net2272_present(struct net2272 *dev)
-{
- /*
- * Quick test to see if CPU can communicate properly with the NET2272.
- * Verifies connection using writes and reads to write/read and
- * read-only registers.
- *
- * This routine is strongly recommended especially during early bring-up
- * of new hardware, however for designs that do not apply Power On System
- * Tests (POST) it may discarded (or perhaps minimized).
- */
- unsigned int ii;
- u8 val, refval;
-
- /* Verify NET2272 write/read SCRATCH register can write and read */
- refval = net2272_read(dev, SCRATCH);
- for (ii = 0; ii < 0x100; ii += 7) {
- net2272_write(dev, SCRATCH, ii);
- val = net2272_read(dev, SCRATCH);
- if (val != ii) {
- dev_dbg(dev->dev,
- "%s: write/read SCRATCH register test failed: "
- "wrote:0x%2.2x, read:0x%2.2x\n",
- __func__, ii, val);
- return -EINVAL;
- }
- }
- /* To be nice, we write the original SCRATCH value back: */
- net2272_write(dev, SCRATCH, refval);
-
- /* Verify NET2272 CHIPREV register is read-only: */
- refval = net2272_read(dev, CHIPREV_2272);
- for (ii = 0; ii < 0x100; ii += 7) {
- net2272_write(dev, CHIPREV_2272, ii);
- val = net2272_read(dev, CHIPREV_2272);
- if (val != refval) {
- dev_dbg(dev->dev,
- "%s: write/read CHIPREV register test failed: "
- "wrote 0x%2.2x, read:0x%2.2x expected:0x%2.2x\n",
- __func__, ii, val, refval);
- return -EINVAL;
- }
- }
-
- /*
- * Verify NET2272's "NET2270 legacy revision" register
- * - NET2272 has two revision registers. The NET2270 legacy revision
- * register should read the same value, regardless of the NET2272
- * silicon revision. The legacy register applies to NET2270
- * firmware being applied to the NET2272.
- */
- val = net2272_read(dev, CHIPREV_LEGACY);
- if (val != NET2270_LEGACY_REV) {
- /*
- * Unexpected legacy revision value
- * - Perhaps the chip is a NET2270?
- */
- dev_dbg(dev->dev,
- "%s: WARNING: UNEXPECTED NET2272 LEGACY REGISTER VALUE:\n"
- " - CHIPREV_LEGACY: expected 0x%2.2x, got:0x%2.2x. (Not NET2272?)\n",
- __func__, NET2270_LEGACY_REV, val);
- return -EINVAL;
- }
-
- /*
- * Verify NET2272 silicon revision
- * - This revision register is appropriate for the silicon version
- * of the NET2272
- */
- val = net2272_read(dev, CHIPREV_2272);
- switch (val) {
- case CHIPREV_NET2272_R1:
- /*
- * NET2272 Rev 1 has DMA related errata:
- * - Newer silicon (Rev 1A or better) required
- */
- dev_dbg(dev->dev,
- "%s: Rev 1 detected: newer silicon recommended for DMA support\n",
- __func__);
- break;
- case CHIPREV_NET2272_R1A:
- break;
- default:
- /* NET2272 silicon version *may* not work with this firmware */
- dev_dbg(dev->dev,
- "%s: unexpected silicon revision register value: "
- " CHIPREV_2272: 0x%2.2x\n",
- __func__, val);
- /*
- * Return Success, even though the chip rev is not an expected value
- * - Older, pre-built firmware can attempt to operate on newer silicon
- * - Often, new silicon is perfectly compatible
- */
- }
-
- /* Success: NET2272 checks out OK */
- return 0;
-}
-
-static void
-net2272_gadget_release(struct device *_dev)
-{
- struct net2272 *dev = container_of(_dev, struct net2272, gadget.dev);
-
- kfree(dev);
-}
-
-/*---------------------------------------------------------------------------*/
-
-static void
-net2272_remove(struct net2272 *dev)
-{
- if (dev->added)
- usb_del_gadget(&dev->gadget);
- free_irq(dev->irq, dev);
- iounmap(dev->base_addr);
- device_remove_file(dev->dev, &dev_attr_registers);
-
- dev_info(dev->dev, "unbind\n");
-}
-
-static struct net2272 *net2272_probe_init(struct device *dev, unsigned int irq)
-{
- struct net2272 *ret;
-
- if (!irq) {
- dev_dbg(dev, "No IRQ!\n");
- return ERR_PTR(-ENODEV);
- }
-
- /* alloc, and start init */
- ret = kzalloc(sizeof(*ret), GFP_KERNEL);
- if (!ret)
- return ERR_PTR(-ENOMEM);
-
- spin_lock_init(&ret->lock);
- ret->irq = irq;
- ret->dev = dev;
- ret->gadget.ops = &net2272_ops;
- ret->gadget.max_speed = USB_SPEED_HIGH;
-
- /* the "gadget" abstracts/virtualizes the controller */
- ret->gadget.name = driver_name;
- usb_initialize_gadget(dev, &ret->gadget, net2272_gadget_release);
-
- return ret;
-}
-
-static int
-net2272_probe_fin(struct net2272 *dev, unsigned int irqflags)
-{
- int ret;
-
- /* See if there... */
- if (net2272_present(dev)) {
- dev_warn(dev->dev, "2272 not found!\n");
- ret = -ENODEV;
- goto err;
- }
-
- net2272_usb_reset(dev);
- net2272_usb_reinit(dev);
-
- ret = request_irq(dev->irq, net2272_irq, irqflags, driver_name, dev);
- if (ret) {
- dev_err(dev->dev, "request interrupt %i failed\n", dev->irq);
- goto err;
- }
-
- dev->chiprev = net2272_read(dev, CHIPREV_2272);
-
- /* done */
- dev_info(dev->dev, "%s\n", driver_desc);
- dev_info(dev->dev, "irq %i, mem %p, chip rev %04x, dma %s\n",
- dev->irq, dev->base_addr, dev->chiprev,
- dma_mode_string());
- dev_info(dev->dev, "version: %s\n", driver_vers);
-
- ret = device_create_file(dev->dev, &dev_attr_registers);
- if (ret)
- goto err_irq;
-
- ret = usb_add_gadget(&dev->gadget);
- if (ret)
- goto err_add_udc;
- dev->added = 1;
-
- return 0;
-
-err_add_udc:
- device_remove_file(dev->dev, &dev_attr_registers);
- err_irq:
- free_irq(dev->irq, dev);
- err:
- return ret;
-}
-
-#ifdef CONFIG_USB_PCI
-
-/*
- * wrap this driver around the specified device, but
- * don't respond over USB until a gadget driver binds to us
- */
-
-static int
-net2272_rdk1_probe(struct pci_dev *pdev, struct net2272 *dev)
-{
- unsigned long resource, len, tmp;
- void __iomem *mem_mapped_addr[4];
- int ret, i;
-
- /*
- * BAR 0 holds PLX 9054 config registers
- * BAR 1 is i/o memory; unused here
- * BAR 2 holds EPLD config registers
- * BAR 3 holds NET2272 registers
- */
-
- /* Find and map all address spaces */
- for (i = 0; i < 4; ++i) {
- if (i == 1)
- continue; /* BAR1 unused */
-
- resource = pci_resource_start(pdev, i);
- len = pci_resource_len(pdev, i);
-
- if (!request_mem_region(resource, len, driver_name)) {
- dev_dbg(dev->dev, "controller already in use\n");
- ret = -EBUSY;
- goto err;
- }
-
- mem_mapped_addr[i] = ioremap(resource, len);
- if (mem_mapped_addr[i] == NULL) {
- release_mem_region(resource, len);
- dev_dbg(dev->dev, "can't map memory\n");
- ret = -EFAULT;
- goto err;
- }
- }
-
- dev->rdk1.plx9054_base_addr = mem_mapped_addr[0];
- dev->rdk1.epld_base_addr = mem_mapped_addr[2];
- dev->base_addr = mem_mapped_addr[3];
-
- /* Set PLX 9054 bus width (16 bits) */
- tmp = readl(dev->rdk1.plx9054_base_addr + LBRD1);
- writel((tmp & ~(3 << MEMORY_SPACE_LOCAL_BUS_WIDTH)) | W16_BIT,
- dev->rdk1.plx9054_base_addr + LBRD1);
-
- /* Enable PLX 9054 Interrupts */
- writel(readl(dev->rdk1.plx9054_base_addr + INTCSR) |
- (1 << PCI_INTERRUPT_ENABLE) |
- (1 << LOCAL_INTERRUPT_INPUT_ENABLE),
- dev->rdk1.plx9054_base_addr + INTCSR);
-
- writeb((1 << CHANNEL_CLEAR_INTERRUPT | (0 << CHANNEL_ENABLE)),
- dev->rdk1.plx9054_base_addr + DMACSR0);
-
- /* reset */
- writeb((1 << EPLD_DMA_ENABLE) |
- (1 << DMA_CTL_DACK) |
- (1 << DMA_TIMEOUT_ENABLE) |
- (1 << USER) |
- (0 << MPX_MODE) |
- (1 << BUSWIDTH) |
- (1 << NET2272_RESET),
- dev->base_addr + EPLD_IO_CONTROL_REGISTER);
-
- mb();
- writeb(readb(dev->base_addr + EPLD_IO_CONTROL_REGISTER) &
- ~(1 << NET2272_RESET),
- dev->base_addr + EPLD_IO_CONTROL_REGISTER);
- udelay(200);
-
- return 0;
-
- err:
- while (--i >= 0) {
- if (i == 1)
- continue; /* BAR1 unused */
- iounmap(mem_mapped_addr[i]);
- release_mem_region(pci_resource_start(pdev, i),
- pci_resource_len(pdev, i));
- }
-
- return ret;
-}
-
-static int
-net2272_rdk2_probe(struct pci_dev *pdev, struct net2272 *dev)
-{
- unsigned long resource, len;
- void __iomem *mem_mapped_addr[2];
- int ret, i;
-
- /*
- * BAR 0 holds FGPA config registers
- * BAR 1 holds NET2272 registers
- */
-
- /* Find and map all address spaces, bar2-3 unused in rdk 2 */
- for (i = 0; i < 2; ++i) {
- resource = pci_resource_start(pdev, i);
- len = pci_resource_len(pdev, i);
-
- if (!request_mem_region(resource, len, driver_name)) {
- dev_dbg(dev->dev, "controller already in use\n");
- ret = -EBUSY;
- goto err;
- }
-
- mem_mapped_addr[i] = ioremap(resource, len);
- if (mem_mapped_addr[i] == NULL) {
- release_mem_region(resource, len);
- dev_dbg(dev->dev, "can't map memory\n");
- ret = -EFAULT;
- goto err;
- }
- }
-
- dev->rdk2.fpga_base_addr = mem_mapped_addr[0];
- dev->base_addr = mem_mapped_addr[1];
-
- mb();
- /* Set 2272 bus width (16 bits) and reset */
- writel((1 << CHIP_RESET), dev->rdk2.fpga_base_addr + RDK2_LOCCTLRDK);
- udelay(200);
- writel((1 << BUS_WIDTH), dev->rdk2.fpga_base_addr + RDK2_LOCCTLRDK);
- /* Print fpga version number */
- dev_info(dev->dev, "RDK2 FPGA version %08x\n",
- readl(dev->rdk2.fpga_base_addr + RDK2_FPGAREV));
- /* Enable FPGA Interrupts */
- writel((1 << NET2272_PCI_IRQ), dev->rdk2.fpga_base_addr + RDK2_IRQENB);
-
- return 0;
-
- err:
- while (--i >= 0) {
- iounmap(mem_mapped_addr[i]);
- release_mem_region(pci_resource_start(pdev, i),
- pci_resource_len(pdev, i));
- }
-
- return ret;
-}
-
-static int
-net2272_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
-{
- struct net2272 *dev;
- int ret;
-
- dev = net2272_probe_init(&pdev->dev, pdev->irq);
- if (IS_ERR(dev))
- return PTR_ERR(dev);
- dev->dev_id = pdev->device;
-
- if (pci_enable_device(pdev) < 0) {
- ret = -ENODEV;
- goto err_put;
- }
-
- pci_set_master(pdev);
-
- switch (pdev->device) {
- case PCI_DEVICE_ID_RDK1: ret = net2272_rdk1_probe(pdev, dev); break;
- case PCI_DEVICE_ID_RDK2: ret = net2272_rdk2_probe(pdev, dev); break;
- default: BUG();
- }
- if (ret)
- goto err_pci;
-
- ret = net2272_probe_fin(dev, 0);
- if (ret)
- goto err_pci;
-
- pci_set_drvdata(pdev, dev);
-
- return 0;
-
- err_pci:
- pci_disable_device(pdev);
- err_put:
- usb_put_gadget(&dev->gadget);
-
- return ret;
-}
-
-static void
-net2272_rdk1_remove(struct pci_dev *pdev, struct net2272 *dev)
-{
- int i;
-
- /* disable PLX 9054 interrupts */
- writel(readl(dev->rdk1.plx9054_base_addr + INTCSR) &
- ~(1 << PCI_INTERRUPT_ENABLE),
- dev->rdk1.plx9054_base_addr + INTCSR);
-
- /* clean up resources allocated during probe() */
- iounmap(dev->rdk1.plx9054_base_addr);
- iounmap(dev->rdk1.epld_base_addr);
-
- for (i = 0; i < 4; ++i) {
- if (i == 1)
- continue; /* BAR1 unused */
- release_mem_region(pci_resource_start(pdev, i),
- pci_resource_len(pdev, i));
- }
-}
-
-static void
-net2272_rdk2_remove(struct pci_dev *pdev, struct net2272 *dev)
-{
- int i;
-
- /* disable fpga interrupts
- writel(readl(dev->rdk1.plx9054_base_addr + INTCSR) &
- ~(1 << PCI_INTERRUPT_ENABLE),
- dev->rdk1.plx9054_base_addr + INTCSR);
- */
-
- /* clean up resources allocated during probe() */
- iounmap(dev->rdk2.fpga_base_addr);
-
- for (i = 0; i < 2; ++i)
- release_mem_region(pci_resource_start(pdev, i),
- pci_resource_len(pdev, i));
-}
-
-static void
-net2272_pci_remove(struct pci_dev *pdev)
-{
- struct net2272 *dev = pci_get_drvdata(pdev);
-
- net2272_remove(dev);
-
- switch (pdev->device) {
- case PCI_DEVICE_ID_RDK1: net2272_rdk1_remove(pdev, dev); break;
- case PCI_DEVICE_ID_RDK2: net2272_rdk2_remove(pdev, dev); break;
- default: BUG();
- }
-
- pci_disable_device(pdev);
-
- usb_put_gadget(&dev->gadget);
-}
-
-/* Table of matching PCI IDs */
-static struct pci_device_id pci_ids[] = {
- { /* RDK 1 card */
- .class = ((PCI_CLASS_BRIDGE_OTHER << 8) | 0xfe),
- .class_mask = 0,
- .vendor = PCI_VENDOR_ID_PLX,
- .device = PCI_DEVICE_ID_RDK1,
- .subvendor = PCI_ANY_ID,
- .subdevice = PCI_ANY_ID,
- },
- { /* RDK 2 card */
- .class = ((PCI_CLASS_BRIDGE_OTHER << 8) | 0xfe),
- .class_mask = 0,
- .vendor = PCI_VENDOR_ID_PLX,
- .device = PCI_DEVICE_ID_RDK2,
- .subvendor = PCI_ANY_ID,
- .subdevice = PCI_ANY_ID,
- },
- { }
-};
-MODULE_DEVICE_TABLE(pci, pci_ids);
-
-static struct pci_driver net2272_pci_driver = {
- .name = driver_name,
- .id_table = pci_ids,
-
- .probe = net2272_pci_probe,
- .remove = net2272_pci_remove,
-};
-
-static int net2272_pci_register(void)
-{
- return pci_register_driver(&net2272_pci_driver);
-}
-
-static void net2272_pci_unregister(void)
-{
- pci_unregister_driver(&net2272_pci_driver);
-}
-
-#else
-static inline int net2272_pci_register(void) { return 0; }
-static inline void net2272_pci_unregister(void) { }
-#endif
-
-/*---------------------------------------------------------------------------*/
-
-static int
-net2272_plat_probe(struct platform_device *pdev)
-{
- struct net2272 *dev;
- int ret;
- unsigned int irqflags;
- resource_size_t base, len;
- struct resource *iomem, *iomem_bus, *irq_res;
-
- irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- iomem_bus = platform_get_resource(pdev, IORESOURCE_BUS, 0);
- if (!irq_res || !iomem) {
- dev_err(&pdev->dev, "must provide irq/base addr");
- return -EINVAL;
- }
-
- dev = net2272_probe_init(&pdev->dev, irq_res->start);
- if (IS_ERR(dev))
- return PTR_ERR(dev);
-
- irqflags = 0;
- if (irq_res->flags & IORESOURCE_IRQ_HIGHEDGE)
- irqflags |= IRQF_TRIGGER_RISING;
- if (irq_res->flags & IORESOURCE_IRQ_LOWEDGE)
- irqflags |= IRQF_TRIGGER_FALLING;
- if (irq_res->flags & IORESOURCE_IRQ_HIGHLEVEL)
- irqflags |= IRQF_TRIGGER_HIGH;
- if (irq_res->flags & IORESOURCE_IRQ_LOWLEVEL)
- irqflags |= IRQF_TRIGGER_LOW;
-
- base = iomem->start;
- len = resource_size(iomem);
- if (iomem_bus)
- dev->base_shift = iomem_bus->start;
-
- if (!request_mem_region(base, len, driver_name)) {
- dev_dbg(dev->dev, "get request memory region!\n");
- ret = -EBUSY;
- goto err;
- }
- dev->base_addr = ioremap(base, len);
- if (!dev->base_addr) {
- dev_dbg(dev->dev, "can't map memory\n");
- ret = -EFAULT;
- goto err_req;
- }
-
- ret = net2272_probe_fin(dev, irqflags);
- if (ret)
- goto err_io;
-
- platform_set_drvdata(pdev, dev);
- dev_info(&pdev->dev, "running in 16-bit, %sbyte swap local bus mode\n",
- (net2272_read(dev, LOCCTL) & (1 << BYTE_SWAP)) ? "" : "no ");
-
- return 0;
-
- err_io:
- iounmap(dev->base_addr);
- err_req:
- release_mem_region(base, len);
- err:
- usb_put_gadget(&dev->gadget);
-
- return ret;
-}
-
-static void
-net2272_plat_remove(struct platform_device *pdev)
-{
- struct net2272 *dev = platform_get_drvdata(pdev);
-
- net2272_remove(dev);
-
- release_mem_region(pdev->resource[0].start,
- resource_size(&pdev->resource[0]));
-
- usb_put_gadget(&dev->gadget);
-}
-
-static struct platform_driver net2272_plat_driver = {
- .probe = net2272_plat_probe,
- .remove = net2272_plat_remove,
- .driver = {
- .name = driver_name,
- },
- /* FIXME .suspend, .resume */
-};
-MODULE_ALIAS("platform:net2272");
-
-static int __init net2272_init(void)
-{
- int ret;
-
- ret = net2272_pci_register();
- if (ret)
- return ret;
- ret = platform_driver_register(&net2272_plat_driver);
- if (ret)
- goto err_pci;
- return ret;
-
-err_pci:
- net2272_pci_unregister();
- return ret;
-}
-module_init(net2272_init);
-
-static void __exit net2272_cleanup(void)
-{
- net2272_pci_unregister();
- platform_driver_unregister(&net2272_plat_driver);
-}
-module_exit(net2272_cleanup);
-
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_AUTHOR("PLX Technology, Inc.");
-MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/udc/net2272.h b/drivers/usb/gadget/udc/net2272.h
deleted file mode 100644
index a9994f737588..000000000000
--- a/drivers/usb/gadget/udc/net2272.h
+++ /dev/null
@@ -1,584 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * PLX NET2272 high/full speed USB device controller
- *
- * Copyright (C) 2005-2006 PLX Technology, Inc.
- * Copyright (C) 2006-2011 Analog Devices, Inc.
- */
-
-#ifndef __NET2272_H__
-#define __NET2272_H__
-
-/* Main Registers */
-#define REGADDRPTR 0x00
-#define REGDATA 0x01
-#define IRQSTAT0 0x02
-#define ENDPOINT_0_INTERRUPT 0
-#define ENDPOINT_A_INTERRUPT 1
-#define ENDPOINT_B_INTERRUPT 2
-#define ENDPOINT_C_INTERRUPT 3
-#define VIRTUALIZED_ENDPOINT_INTERRUPT 4
-#define SETUP_PACKET_INTERRUPT 5
-#define DMA_DONE_INTERRUPT 6
-#define SOF_INTERRUPT 7
-#define IRQSTAT1 0x03
-#define CONTROL_STATUS_INTERRUPT 1
-#define VBUS_INTERRUPT 2
-#define SUSPEND_REQUEST_INTERRUPT 3
-#define SUSPEND_REQUEST_CHANGE_INTERRUPT 4
-#define RESUME_INTERRUPT 5
-#define ROOT_PORT_RESET_INTERRUPT 6
-#define RESET_STATUS 7
-#define PAGESEL 0x04
-#define DMAREQ 0x1c
-#define DMA_ENDPOINT_SELECT 0
-#define DREQ_POLARITY 1
-#define DACK_POLARITY 2
-#define EOT_POLARITY 3
-#define DMA_CONTROL_DACK 4
-#define DMA_REQUEST_ENABLE 5
-#define DMA_REQUEST 6
-#define DMA_BUFFER_VALID 7
-#define SCRATCH 0x1d
-#define IRQENB0 0x20
-#define ENDPOINT_0_INTERRUPT_ENABLE 0
-#define ENDPOINT_A_INTERRUPT_ENABLE 1
-#define ENDPOINT_B_INTERRUPT_ENABLE 2
-#define ENDPOINT_C_INTERRUPT_ENABLE 3
-#define VIRTUALIZED_ENDPOINT_INTERRUPT_ENABLE 4
-#define SETUP_PACKET_INTERRUPT_ENABLE 5
-#define DMA_DONE_INTERRUPT_ENABLE 6
-#define SOF_INTERRUPT_ENABLE 7
-#define IRQENB1 0x21
-#define VBUS_INTERRUPT_ENABLE 2
-#define SUSPEND_REQUEST_INTERRUPT_ENABLE 3
-#define SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE 4
-#define RESUME_INTERRUPT_ENABLE 5
-#define ROOT_PORT_RESET_INTERRUPT_ENABLE 6
-#define LOCCTL 0x22
-#define DATA_WIDTH 0
-#define LOCAL_CLOCK_OUTPUT 1
-#define LOCAL_CLOCK_OUTPUT_OFF 0
-#define LOCAL_CLOCK_OUTPUT_3_75MHZ 1
-#define LOCAL_CLOCK_OUTPUT_7_5MHZ 2
-#define LOCAL_CLOCK_OUTPUT_15MHZ 3
-#define LOCAL_CLOCK_OUTPUT_30MHZ 4
-#define LOCAL_CLOCK_OUTPUT_60MHZ 5
-#define DMA_SPLIT_BUS_MODE 4
-#define BYTE_SWAP 5
-#define BUFFER_CONFIGURATION 6
-#define BUFFER_CONFIGURATION_EPA512_EPB512 0
-#define BUFFER_CONFIGURATION_EPA1024_EPB512 1
-#define BUFFER_CONFIGURATION_EPA1024_EPB1024 2
-#define BUFFER_CONFIGURATION_EPA1024DB 3
-#define CHIPREV_LEGACY 0x23
-#define NET2270_LEGACY_REV 0x40
-#define LOCCTL1 0x24
-#define DMA_MODE 0
-#define SLOW_DREQ 0
-#define FAST_DREQ 1
-#define BURST_MODE 2
-#define DMA_DACK_ENABLE 2
-#define CHIPREV_2272 0x25
-#define CHIPREV_NET2272_R1 0x10
-#define CHIPREV_NET2272_R1A 0x11
-/* USB Registers */
-#define USBCTL0 0x18
-#define IO_WAKEUP_ENABLE 1
-#define USB_DETECT_ENABLE 3
-#define USB_ROOT_PORT_WAKEUP_ENABLE 5
-#define USBCTL1 0x19
-#define VBUS_PIN 0
-#define USB_FULL_SPEED 1
-#define USB_HIGH_SPEED 2
-#define GENERATE_RESUME 3
-#define VIRTUAL_ENDPOINT_ENABLE 4
-#define FRAME0 0x1a
-#define FRAME1 0x1b
-#define OURADDR 0x30
-#define FORCE_IMMEDIATE 7
-#define USBDIAG 0x31
-#define FORCE_TRANSMIT_CRC_ERROR 0
-#define PREVENT_TRANSMIT_BIT_STUFF 1
-#define FORCE_RECEIVE_ERROR 2
-#define FAST_TIMES 4
-#define USBTEST 0x32
-#define TEST_MODE_SELECT 0
-#define NORMAL_OPERATION 0
-#define XCVRDIAG 0x33
-#define FORCE_FULL_SPEED 2
-#define FORCE_HIGH_SPEED 3
-#define OPMODE 4
-#define NORMAL_OPERATION 0
-#define NON_DRIVING 1
-#define DISABLE_BITSTUFF_AND_NRZI_ENCODE 2
-#define LINESTATE 6
-#define SE0_STATE 0
-#define J_STATE 1
-#define K_STATE 2
-#define SE1_STATE 3
-#define VIRTOUT0 0x34
-#define VIRTOUT1 0x35
-#define VIRTIN0 0x36
-#define VIRTIN1 0x37
-#define SETUP0 0x40
-#define SETUP1 0x41
-#define SETUP2 0x42
-#define SETUP3 0x43
-#define SETUP4 0x44
-#define SETUP5 0x45
-#define SETUP6 0x46
-#define SETUP7 0x47
-/* Endpoint Registers (Paged via PAGESEL) */
-#define EP_DATA 0x05
-#define EP_STAT0 0x06
-#define DATA_IN_TOKEN_INTERRUPT 0
-#define DATA_OUT_TOKEN_INTERRUPT 1
-#define DATA_PACKET_TRANSMITTED_INTERRUPT 2
-#define DATA_PACKET_RECEIVED_INTERRUPT 3
-#define SHORT_PACKET_TRANSFERRED_INTERRUPT 4
-#define NAK_OUT_PACKETS 5
-#define BUFFER_EMPTY 6
-#define BUFFER_FULL 7
-#define EP_STAT1 0x07
-#define TIMEOUT 0
-#define USB_OUT_ACK_SENT 1
-#define USB_OUT_NAK_SENT 2
-#define USB_IN_ACK_RCVD 3
-#define USB_IN_NAK_SENT 4
-#define USB_STALL_SENT 5
-#define LOCAL_OUT_ZLP 6
-#define BUFFER_FLUSH 7
-#define EP_TRANSFER0 0x08
-#define EP_TRANSFER1 0x09
-#define EP_TRANSFER2 0x0a
-#define EP_IRQENB 0x0b
-#define DATA_IN_TOKEN_INTERRUPT_ENABLE 0
-#define DATA_OUT_TOKEN_INTERRUPT_ENABLE 1
-#define DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE 2
-#define DATA_PACKET_RECEIVED_INTERRUPT_ENABLE 3
-#define SHORT_PACKET_TRANSFERRED_INTERRUPT_ENABLE 4
-#define EP_AVAIL0 0x0c
-#define EP_AVAIL1 0x0d
-#define EP_RSPCLR 0x0e
-#define EP_RSPSET 0x0f
-#define ENDPOINT_HALT 0
-#define ENDPOINT_TOGGLE 1
-#define NAK_OUT_PACKETS_MODE 2
-#define CONTROL_STATUS_PHASE_HANDSHAKE 3
-#define INTERRUPT_MODE 4
-#define AUTOVALIDATE 5
-#define HIDE_STATUS_PHASE 6
-#define ALT_NAK_OUT_PACKETS 7
-#define EP_MAXPKT0 0x28
-#define EP_MAXPKT1 0x29
-#define ADDITIONAL_TRANSACTION_OPPORTUNITIES 3
-#define NONE_ADDITIONAL_TRANSACTION 0
-#define ONE_ADDITIONAL_TRANSACTION 1
-#define TWO_ADDITIONAL_TRANSACTION 2
-#define EP_CFG 0x2a
-#define ENDPOINT_NUMBER 0
-#define ENDPOINT_DIRECTION 4
-#define ENDPOINT_TYPE 5
-#define ENDPOINT_ENABLE 7
-#define EP_HBW 0x2b
-#define HIGH_BANDWIDTH_OUT_TRANSACTION_PID 0
-#define DATA0_PID 0
-#define DATA1_PID 1
-#define DATA2_PID 2
-#define MDATA_PID 3
-#define EP_BUFF_STATES 0x2c
-#define BUFFER_A_STATE 0
-#define BUFFER_B_STATE 2
-#define BUFF_FREE 0
-#define BUFF_VALID 1
-#define BUFF_LCL 2
-#define BUFF_USB 3
-
-/*---------------------------------------------------------------------------*/
-
-#define PCI_DEVICE_ID_RDK1 0x9054
-
-/* PCI-RDK EPLD Registers */
-#define RDK_EPLD_IO_REGISTER1 0x00000000
-#define RDK_EPLD_USB_RESET 0
-#define RDK_EPLD_USB_POWERDOWN 1
-#define RDK_EPLD_USB_WAKEUP 2
-#define RDK_EPLD_USB_EOT 3
-#define RDK_EPLD_DPPULL 4
-#define RDK_EPLD_IO_REGISTER2 0x00000004
-#define RDK_EPLD_BUSWIDTH 0
-#define RDK_EPLD_USER 2
-#define RDK_EPLD_RESET_INTERRUPT_ENABLE 3
-#define RDK_EPLD_DMA_TIMEOUT_ENABLE 4
-#define RDK_EPLD_STATUS_REGISTER 0x00000008
-#define RDK_EPLD_USB_LRESET 0
-#define RDK_EPLD_REVISION_REGISTER 0x0000000c
-
-/* PCI-RDK PLX 9054 Registers */
-#define INTCSR 0x68
-#define PCI_INTERRUPT_ENABLE 8
-#define LOCAL_INTERRUPT_INPUT_ENABLE 11
-#define LOCAL_INPUT_INTERRUPT_ACTIVE 15
-#define LOCAL_DMA_CHANNEL_0_INTERRUPT_ENABLE 18
-#define LOCAL_DMA_CHANNEL_1_INTERRUPT_ENABLE 19
-#define DMA_CHANNEL_0_INTERRUPT_ACTIVE 21
-#define DMA_CHANNEL_1_INTERRUPT_ACTIVE 22
-#define CNTRL 0x6C
-#define RELOAD_CONFIGURATION_REGISTERS 29
-#define PCI_ADAPTER_SOFTWARE_RESET 30
-#define DMAMODE0 0x80
-#define LOCAL_BUS_WIDTH 0
-#define INTERNAL_WAIT_STATES 2
-#define TA_READY_INPUT_ENABLE 6
-#define LOCAL_BURST_ENABLE 8
-#define SCATTER_GATHER_MODE 9
-#define DONE_INTERRUPT_ENABLE 10
-#define LOCAL_ADDRESSING_MODE 11
-#define DEMAND_MODE 12
-#define DMA_EOT_ENABLE 14
-#define FAST_SLOW_TERMINATE_MODE_SELECT 15
-#define DMA_CHANNEL_INTERRUPT_SELECT 17
-#define DMAPADR0 0x84
-#define DMALADR0 0x88
-#define DMASIZ0 0x8c
-#define DMADPR0 0x90
-#define DESCRIPTOR_LOCATION 0
-#define END_OF_CHAIN 1
-#define INTERRUPT_AFTER_TERMINAL_COUNT 2
-#define DIRECTION_OF_TRANSFER 3
-#define DMACSR0 0xa8
-#define CHANNEL_ENABLE 0
-#define CHANNEL_START 1
-#define CHANNEL_ABORT 2
-#define CHANNEL_CLEAR_INTERRUPT 3
-#define CHANNEL_DONE 4
-#define DMATHR 0xb0
-#define LBRD1 0xf8
-#define MEMORY_SPACE_LOCAL_BUS_WIDTH 0
-#define W8_BIT 0
-#define W16_BIT 1
-
-/* Special OR'ing of INTCSR bits */
-#define LOCAL_INTERRUPT_TEST \
- ((1 << LOCAL_INPUT_INTERRUPT_ACTIVE) | \
- (1 << LOCAL_INTERRUPT_INPUT_ENABLE))
-
-#define DMA_CHANNEL_0_TEST \
- ((1 << DMA_CHANNEL_0_INTERRUPT_ACTIVE) | \
- (1 << LOCAL_DMA_CHANNEL_0_INTERRUPT_ENABLE))
-
-#define DMA_CHANNEL_1_TEST \
- ((1 << DMA_CHANNEL_1_INTERRUPT_ACTIVE) | \
- (1 << LOCAL_DMA_CHANNEL_1_INTERRUPT_ENABLE))
-
-/* EPLD Registers */
-#define RDK_EPLD_IO_REGISTER1 0x00000000
-#define RDK_EPLD_USB_RESET 0
-#define RDK_EPLD_USB_POWERDOWN 1
-#define RDK_EPLD_USB_WAKEUP 2
-#define RDK_EPLD_USB_EOT 3
-#define RDK_EPLD_DPPULL 4
-#define RDK_EPLD_IO_REGISTER2 0x00000004
-#define RDK_EPLD_BUSWIDTH 0
-#define RDK_EPLD_USER 2
-#define RDK_EPLD_RESET_INTERRUPT_ENABLE 3
-#define RDK_EPLD_DMA_TIMEOUT_ENABLE 4
-#define RDK_EPLD_STATUS_REGISTER 0x00000008
-#define RDK_EPLD_USB_LRESET 0
-#define RDK_EPLD_REVISION_REGISTER 0x0000000c
-
-#define EPLD_IO_CONTROL_REGISTER 0x400
-#define NET2272_RESET 0
-#define BUSWIDTH 1
-#define MPX_MODE 3
-#define USER 4
-#define DMA_TIMEOUT_ENABLE 5
-#define DMA_CTL_DACK 6
-#define EPLD_DMA_ENABLE 7
-#define EPLD_DMA_CONTROL_REGISTER 0x800
-#define SPLIT_DMA_MODE 0
-#define SPLIT_DMA_DIRECTION 1
-#define SPLIT_DMA_ENABLE 2
-#define SPLIT_DMA_INTERRUPT_ENABLE 3
-#define SPLIT_DMA_INTERRUPT 4
-#define EPLD_DMA_MODE 5
-#define EPLD_DMA_CONTROLLER_ENABLE 7
-#define SPLIT_DMA_ADDRESS_LOW 0xc00
-#define SPLIT_DMA_ADDRESS_HIGH 0x1000
-#define SPLIT_DMA_BYTE_COUNT_LOW 0x1400
-#define SPLIT_DMA_BYTE_COUNT_HIGH 0x1800
-#define EPLD_REVISION_REGISTER 0x1c00
-#define SPLIT_DMA_RAM 0x4000
-#define DMA_RAM_SIZE 0x1000
-
-/*---------------------------------------------------------------------------*/
-
-#define PCI_DEVICE_ID_RDK2 0x3272
-
-/* PCI-RDK version 2 registers */
-
-/* Main Control Registers */
-
-#define RDK2_IRQENB 0x00
-#define RDK2_IRQSTAT 0x04
-#define PB7 23
-#define PB6 22
-#define PB5 21
-#define PB4 20
-#define PB3 19
-#define PB2 18
-#define PB1 17
-#define PB0 16
-#define GP3 23
-#define GP2 23
-#define GP1 23
-#define GP0 23
-#define DMA_RETRY_ABORT 6
-#define DMA_PAUSE_DONE 5
-#define DMA_ABORT_DONE 4
-#define DMA_OUT_FIFO_TRANSFER_DONE 3
-#define DMA_LOCAL_DONE 2
-#define DMA_PCI_DONE 1
-#define NET2272_PCI_IRQ 0
-
-#define RDK2_LOCCTLRDK 0x08
-#define CHIP_RESET 3
-#define SPLIT_DMA 2
-#define MULTIPLEX_MODE 1
-#define BUS_WIDTH 0
-
-#define RDK2_GPIOCTL 0x10
-#define GP3_OUT_ENABLE 7
-#define GP2_OUT_ENABLE 6
-#define GP1_OUT_ENABLE 5
-#define GP0_OUT_ENABLE 4
-#define GP3_DATA 3
-#define GP2_DATA 2
-#define GP1_DATA 1
-#define GP0_DATA 0
-
-#define RDK2_LEDSW 0x14
-#define LED3 27
-#define LED2 26
-#define LED1 25
-#define LED0 24
-#define PBUTTON 16
-#define DIPSW 0
-
-#define RDK2_DIAG 0x18
-#define RDK2_FAST_TIMES 2
-#define FORCE_PCI_SERR 1
-#define FORCE_PCI_INT 0
-#define RDK2_FPGAREV 0x1C
-
-/* Dma Control registers */
-#define RDK2_DMACTL 0x80
-#define ADDR_HOLD 24
-#define RETRY_COUNT 16 /* 23:16 */
-#define FIFO_THRESHOLD 11 /* 15:11 */
-#define MEM_WRITE_INVALIDATE 10
-#define READ_MULTIPLE 9
-#define READ_LINE 8
-#define RDK2_DMA_MODE 6 /* 7:6 */
-#define CONTROL_DACK 5
-#define EOT_ENABLE 4
-#define EOT_POLARITY 3
-#define DACK_POLARITY 2
-#define DREQ_POLARITY 1
-#define DMA_ENABLE 0
-
-#define RDK2_DMASTAT 0x84
-#define GATHER_COUNT 12 /* 14:12 */
-#define FIFO_COUNT 6 /* 11:6 */
-#define FIFO_FLUSH 5
-#define FIFO_TRANSFER 4
-#define PAUSE_DONE 3
-#define ABORT_DONE 2
-#define DMA_ABORT 1
-#define DMA_START 0
-
-#define RDK2_DMAPCICOUNT 0x88
-#define DMA_DIRECTION 31
-#define DMA_PCI_BYTE_COUNT 0 /* 0:23 */
-
-#define RDK2_DMALOCCOUNT 0x8C /* 0:23 dma local byte count */
-
-#define RDK2_DMAADDR 0x90 /* 2:31 PCI bus starting address */
-
-/*---------------------------------------------------------------------------*/
-
-#define REG_INDEXED_THRESHOLD (1 << 5)
-
-/* DRIVER DATA STRUCTURES and UTILITIES */
-struct net2272_ep {
- struct usb_ep ep;
- struct net2272 *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,
- stopped:1,
- wedged:1,
- is_in:1,
- is_iso:1,
- dma:1,
- not_empty:1;
-};
-
-struct net2272 {
- /* each device provides one gadget, several endpoints */
- struct usb_gadget gadget;
- struct device *dev;
- unsigned short dev_id;
-
- spinlock_t lock;
- struct net2272_ep ep[4];
- struct usb_gadget_driver *driver;
- unsigned protocol_stall:1,
- softconnect:1,
- wakeup:1,
- added:1,
- async_callbacks:1,
- dma_eot_polarity:1,
- dma_dack_polarity:1,
- dma_dreq_polarity:1,
- dma_busy:1;
- u16 chiprev;
- u8 pagesel;
-
- unsigned int irq;
- unsigned short fifo_mode;
-
- unsigned int base_shift;
- u16 __iomem *base_addr;
- union {
-#ifdef CONFIG_USB_PCI
- struct {
- void __iomem *plx9054_base_addr;
- void __iomem *epld_base_addr;
- } rdk1;
- struct {
- /* Bar0, Bar1 is base_addr both mem-mapped */
- void __iomem *fpga_base_addr;
- } rdk2;
-#endif
- };
-};
-
-static void __iomem *
-net2272_reg_addr(struct net2272 *dev, unsigned int reg)
-{
- return dev->base_addr + (reg << dev->base_shift);
-}
-
-static void
-net2272_write(struct net2272 *dev, unsigned int reg, u8 value)
-{
- if (reg >= REG_INDEXED_THRESHOLD) {
- /*
- * Indexed register; use REGADDRPTR/REGDATA
- * - Save and restore REGADDRPTR. This prevents REGADDRPTR from
- * changes between other code sections, but it is time consuming.
- * - Performance tips: either do not save and restore REGADDRPTR (if it
- * is safe) or do save/restore operations only in critical sections.
- u8 tmp = readb(dev->base_addr + REGADDRPTR);
- */
- writeb((u8)reg, net2272_reg_addr(dev, REGADDRPTR));
- writeb(value, net2272_reg_addr(dev, REGDATA));
- /* writeb(tmp, net2272_reg_addr(dev, REGADDRPTR)); */
- } else
- writeb(value, net2272_reg_addr(dev, reg));
-}
-
-static u8
-net2272_read(struct net2272 *dev, unsigned int reg)
-{
- u8 ret;
-
- if (reg >= REG_INDEXED_THRESHOLD) {
- /*
- * Indexed register; use REGADDRPTR/REGDATA
- * - Save and restore REGADDRPTR. This prevents REGADDRPTR from
- * changes between other code sections, but it is time consuming.
- * - Performance tips: either do not save and restore REGADDRPTR (if it
- * is safe) or do save/restore operations only in critical sections.
- u8 tmp = readb(dev->base_addr + REGADDRPTR);
- */
- writeb((u8)reg, net2272_reg_addr(dev, REGADDRPTR));
- ret = readb(net2272_reg_addr(dev, REGDATA));
- /* writeb(tmp, net2272_reg_addr(dev, REGADDRPTR)); */
- } else
- ret = readb(net2272_reg_addr(dev, reg));
-
- return ret;
-}
-
-static void
-net2272_ep_write(struct net2272_ep *ep, unsigned int reg, u8 value)
-{
- struct net2272 *dev = ep->dev;
-
- if (dev->pagesel != ep->num) {
- net2272_write(dev, PAGESEL, ep->num);
- dev->pagesel = ep->num;
- }
- net2272_write(dev, reg, value);
-}
-
-static u8
-net2272_ep_read(struct net2272_ep *ep, unsigned int reg)
-{
- struct net2272 *dev = ep->dev;
-
- if (dev->pagesel != ep->num) {
- net2272_write(dev, PAGESEL, ep->num);
- dev->pagesel = ep->num;
- }
- return net2272_read(dev, reg);
-}
-
-static void allow_status(struct net2272_ep *ep)
-{
- /* ep0 only */
- net2272_ep_write(ep, EP_RSPCLR,
- (1 << CONTROL_STATUS_PHASE_HANDSHAKE) |
- (1 << ALT_NAK_OUT_PACKETS) |
- (1 << NAK_OUT_PACKETS_MODE));
- ep->stopped = 1;
-}
-
-static void set_halt(struct net2272_ep *ep)
-{
- /* ep0 and bulk/intr endpoints */
- net2272_ep_write(ep, EP_RSPCLR, 1 << CONTROL_STATUS_PHASE_HANDSHAKE);
- net2272_ep_write(ep, EP_RSPSET, 1 << ENDPOINT_HALT);
-}
-
-static void clear_halt(struct net2272_ep *ep)
-{
- /* ep0 and bulk/intr endpoints */
- net2272_ep_write(ep, EP_RSPCLR,
- (1 << ENDPOINT_HALT) | (1 << ENDPOINT_TOGGLE));
-}
-
-/* count (<= 4) bytes in the next fifo write will be valid */
-static void set_fifo_bytecount(struct net2272_ep *ep, unsigned count)
-{
- /* net2272_ep_write will truncate to u8 for us */
- net2272_ep_write(ep, EP_TRANSFER2, count >> 16);
- net2272_ep_write(ep, EP_TRANSFER1, count >> 8);
- net2272_ep_write(ep, EP_TRANSFER0, count);
-}
-
-struct net2272_request {
- struct usb_request req;
- struct list_head queue;
- unsigned mapped:1,
- valid:1;
-};
-
-#endif
diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c
index b2903e4bbf54..d02765bd49ce 100644
--- a/drivers/usb/gadget/udc/net2280.c
+++ b/drivers/usb/gadget/udc/net2280.c
@@ -203,13 +203,13 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
}
/* erratum 0119 workaround ties up an endpoint number */
- if ((desc->bEndpointAddress & 0x0f) == EP_DONTUSE) {
+ if (usb_endpoint_num(desc) == EP_DONTUSE) {
ret = -EDOM;
goto print_err;
}
if (dev->quirks & PLX_PCIE) {
- if ((desc->bEndpointAddress & 0x0f) >= 0x0c) {
+ if (usb_endpoint_num(desc) >= 0x0c) {
ret = -EDOM;
goto print_err;
}
@@ -255,7 +255,7 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
else
tmp &= ~USB3380_EP_CFG_MASK_OUT;
}
- type = (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK);
+ type = usb_endpoint_type(desc);
if (type == USB_ENDPOINT_XFER_INT) {
/* erratum 0105 workaround prevents hs NYET */
if (dev->chiprev == 0100 &&
@@ -554,7 +554,7 @@ static struct usb_request
}
ep = container_of(_ep, struct net2280_ep, ep);
- req = kzalloc(sizeof(*req), gfp_flags);
+ req = kzalloc_obj(*req, gfp_flags);
if (!req)
return NULL;
@@ -1334,7 +1334,7 @@ net2280_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedged)
retval = -ESHUTDOWN;
goto print_err;
}
- if (ep->desc /* not ep0 */ && (ep->desc->bmAttributes & 0x03)
+ if (ep->desc /* not ep0 */ && usb_endpoint_type(ep->desc)
== USB_ENDPOINT_XFER_ISOC) {
retval = -EINVAL;
goto print_err;
@@ -3625,7 +3625,7 @@ static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id)
int retval, i;
/* alloc, and start init */
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ dev = kzalloc_obj(*dev);
if (dev == NULL) {
retval = -ENOMEM;
goto done;
diff --git a/drivers/usb/gadget/udc/omap_udc.c b/drivers/usb/gadget/udc/omap_udc.c
index 8902abe3ca76..f3ca79cece1b 100644
--- a/drivers/usb/gadget/udc/omap_udc.c
+++ b/drivers/usb/gadget/udc/omap_udc.c
@@ -252,7 +252,7 @@ static int omap_ep_disable(struct usb_ep *_ep)
ep->has_dma = 0;
omap_writew(UDC_SET_HALT, UDC_CTRL);
list_del_init(&ep->iso);
- del_timer(&ep->timer);
+ timer_delete(&ep->timer);
spin_unlock_irqrestore(&ep->udc->lock, flags);
@@ -267,7 +267,7 @@ omap_alloc_request(struct usb_ep *ep, gfp_t gfp_flags)
{
struct omap_req *req;
- req = kzalloc(sizeof(*req), gfp_flags);
+ req = kzalloc_obj(*req, gfp_flags);
if (!req)
return NULL;
@@ -733,8 +733,6 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel)
if (status == 0) {
omap_writew(reg, UDC_TXDMA_CFG);
/* EMIFF or SDRC */
- omap_set_dma_src_burst_mode(ep->lch,
- OMAP_DMA_DATA_BURST_4);
omap_set_dma_src_data_pack(ep->lch, 1);
/* TIPB */
omap_set_dma_dest_params(ep->lch,
@@ -756,8 +754,6 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel)
UDC_DATA_DMA,
0, 0);
/* EMIFF or SDRC */
- omap_set_dma_dest_burst_mode(ep->lch,
- OMAP_DMA_DATA_BURST_4);
omap_set_dma_dest_data_pack(ep->lch, 1);
}
}
@@ -1860,7 +1856,7 @@ static irqreturn_t omap_udc_irq(int irq, void *_udc)
static void pio_out_timer(struct timer_list *t)
{
- struct omap_ep *ep = from_timer(ep, t, timer);
+ struct omap_ep *ep = timer_container_of(ep, t, timer);
unsigned long flags;
u16 stat_flg;
@@ -2627,7 +2623,7 @@ omap_udc_setup(struct platform_device *odev, struct usb_phy *xceiv)
/* UDC_PULLUP_EN gates the chip clock */
/* OTG_SYSCON_1 |= DEV_IDLE_EN; */
- udc = kzalloc(sizeof(*udc), GFP_KERNEL);
+ udc = kzalloc_obj(*udc);
if (!udc)
return -ENOMEM;
diff --git a/drivers/usb/gadget/udc/pch_udc.c b/drivers/usb/gadget/udc/pch_udc.c
index 169f72665739..0a6886428739 100644
--- a/drivers/usb/gadget/udc/pch_udc.c
+++ b/drivers/usb/gadget/udc/pch_udc.c
@@ -988,7 +988,7 @@ static void pch_udc_ep_enable(struct pch_udc_ep *ep,
pch_udc_ep_fifo_flush(ep, ep->in);
/* Configure the endpoint */
val = ep->num << UDC_CSR_NE_NUM_SHIFT | ep->in << UDC_CSR_NE_DIR_SHIFT |
- ((desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) <<
+ (usb_endpoint_type(desc) <<
UDC_CSR_NE_TYPE_SHIFT) |
(cfg->cur_cfg << UDC_CSR_NE_CFG_SHIFT) |
(cfg->cur_intf << UDC_CSR_NE_INTF_SHIFT) |
@@ -1717,7 +1717,7 @@ static struct usb_request *pch_udc_alloc_request(struct usb_ep *usbep,
if (!usbep)
return NULL;
ep = container_of(usbep, struct pch_udc_ep, ep);
- req = kzalloc(sizeof *req, gfp);
+ req = kzalloc_obj(*req, gfp);
if (!req)
return NULL;
req->req.dma = DMA_ADDR_INVALID;
diff --git a/drivers/usb/gadget/udc/pxa25x_udc.c b/drivers/usb/gadget/udc/pxa25x_udc.c
index 1e9998024aaa..b3d58d7c3a77 100644
--- a/drivers/usb/gadget/udc/pxa25x_udc.c
+++ b/drivers/usb/gadget/udc/pxa25x_udc.c
@@ -508,7 +508,7 @@ pxa25x_ep_alloc_request (struct usb_ep *_ep, gfp_t gfp_flags)
{
struct pxa25x_request *req;
- req = kzalloc(sizeof(*req), gfp_flags);
+ req = kzalloc_obj(*req, gfp_flags);
if (!req)
return NULL;
@@ -1503,7 +1503,7 @@ reset_gadget(struct pxa25x_udc *dev, struct usb_gadget_driver *driver)
ep->stopped = 1;
nuke(ep, -ESHUTDOWN);
}
- del_timer_sync(&dev->timer);
+ timer_delete_sync(&dev->timer);
/* report reset; the driver is already quiesced */
if (driver)
@@ -1530,7 +1530,7 @@ stop_activity(struct pxa25x_udc *dev, struct usb_gadget_driver *driver)
ep->stopped = 1;
nuke(ep, -ESHUTDOWN);
}
- del_timer_sync(&dev->timer);
+ timer_delete_sync(&dev->timer);
/* report disconnect; the driver is already quiesced */
if (driver)
@@ -1574,7 +1574,7 @@ static inline void clear_ep_state (struct pxa25x_udc *dev)
static void udc_watchdog(struct timer_list *t)
{
- struct pxa25x_udc *dev = from_timer(dev, t, timer);
+ struct pxa25x_udc *dev = timer_container_of(dev, t, timer);
local_irq_disable();
if (dev->ep0state == EP0_STALL
@@ -1607,14 +1607,14 @@ static void handle_ep0 (struct pxa25x_udc *dev)
if (udccs0 & UDCCS0_SST) {
nuke(ep, -EPIPE);
udc_ep0_set_UDCCS(dev, UDCCS0_SST);
- del_timer(&dev->timer);
+ timer_delete(&dev->timer);
ep0_idle(dev);
}
/* previous request unfinished? non-error iff back-to-back ... */
if ((udccs0 & UDCCS0_SA) != 0 && dev->ep0state != EP0_IDLE) {
nuke(ep, 0);
- del_timer(&dev->timer);
+ timer_delete(&dev->timer);
ep0_idle(dev);
}
@@ -2348,15 +2348,14 @@ static int pxa25x_udc_probe(struct platform_device *pdev)
dev->transceiver = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
if (gpio_is_valid(dev->mach->gpio_pullup)) {
- retval = devm_gpio_request(&pdev->dev, dev->mach->gpio_pullup,
- "pca25x_udc GPIO PULLUP");
+ retval = devm_gpio_request_one(&pdev->dev, dev->mach->gpio_pullup,
+ GPIOF_OUT_INIT_LOW, "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_direction_output(dev->mach->gpio_pullup, 0);
}
timer_setup(&dev->timer, udc_watchdog, 0);
diff --git a/drivers/usb/gadget/udc/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c
index 897f53601b5b..1abea0d48c35 100644
--- a/drivers/usb/gadget/udc/pxa27x_udc.c
+++ b/drivers/usb/gadget/udc/pxa27x_udc.c
@@ -573,7 +573,7 @@ pxa_ep_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
{
struct pxa27x_request *req;
- req = kzalloc(sizeof *req, gfp_flags);
+ req = kzalloc_obj(*req, gfp_flags);
if (!req)
return NULL;
@@ -1462,7 +1462,7 @@ static int pxa_udc_wakeup(struct usb_gadget *_gadget)
return 0;
}
-static void udc_enable(struct pxa_udc *udc);
+static int udc_enable(struct pxa_udc *udc);
static void udc_disable(struct pxa_udc *udc);
/**
@@ -1519,14 +1519,20 @@ static int should_disable_udc(struct pxa_udc *udc)
static int pxa_udc_pullup(struct usb_gadget *_gadget, int is_active)
{
struct pxa_udc *udc = to_gadget_udc(_gadget);
+ int ret;
if (!udc->gpiod && !udc->udc_command)
return -EOPNOTSUPP;
dplus_pullup(udc, is_active);
- if (should_enable_udc(udc))
- udc_enable(udc);
+ if (should_enable_udc(udc)) {
+ ret = udc_enable(udc);
+ if (ret) {
+ dplus_pullup(udc, !is_active);
+ return ret;
+ }
+ }
if (should_disable_udc(udc))
udc_disable(udc);
return 0;
@@ -1545,10 +1551,16 @@ static int pxa_udc_pullup(struct usb_gadget *_gadget, int is_active)
static int pxa_udc_vbus_session(struct usb_gadget *_gadget, int is_active)
{
struct pxa_udc *udc = to_gadget_udc(_gadget);
+ int ret;
udc->vbus_sensed = is_active;
- if (should_enable_udc(udc))
- udc_enable(udc);
+ if (should_enable_udc(udc)) {
+ ret = udc_enable(udc);
+ if (ret) {
+ udc->vbus_sensed = !is_active;
+ return ret;
+ }
+ }
if (should_disable_udc(udc))
udc_disable(udc);
@@ -1691,12 +1703,18 @@ static void udc_init_data(struct pxa_udc *dev)
* Enables the udc device : enables clocks, udc interrupts, control endpoint
* interrupts, sets usb as UDC client and setups endpoints.
*/
-static void udc_enable(struct pxa_udc *udc)
+static int udc_enable(struct pxa_udc *udc)
{
+ int ret;
+
if (udc->enabled)
- return;
+ return 0;
- clk_enable(udc->clk);
+ ret = clk_enable(udc->clk);
+ if (ret) {
+ dev_err(udc->dev, "clk_enable failed: %d\n", ret);
+ return ret;
+ }
udc_writel(udc, UDCICR0, 0);
udc_writel(udc, UDCICR1, 0);
udc_clear_mask_UDCCR(udc, UDCCR_UDE);
@@ -1726,6 +1744,8 @@ static void udc_enable(struct pxa_udc *udc)
pio_irq_enable(&udc->pxa_ep[0]);
udc->enabled = 1;
+
+ return 0;
}
/**
@@ -1761,10 +1781,16 @@ static int pxa27x_udc_start(struct usb_gadget *g,
}
}
- if (should_enable_udc(udc))
- udc_enable(udc);
+ if (should_enable_udc(udc)) {
+ retval = udc_enable(udc);
+ if (retval)
+ goto fail_enable;
+ }
return 0;
+fail_enable:
+ if (!IS_ERR_OR_NULL(udc->transceiver))
+ otg_set_peripheral(udc->transceiver->otg, NULL);
fail:
udc->driver = NULL;
return retval;
@@ -2430,10 +2456,16 @@ static int pxa_udc_probe(struct platform_device *pdev)
goto err_add_gadget;
pxa_init_debugfs(udc);
- if (should_enable_udc(udc))
- udc_enable(udc);
+ if (should_enable_udc(udc)) {
+ retval = udc_enable(udc);
+ if (retval)
+ goto err_enable;
+ }
return 0;
+err_enable:
+ usb_del_gadget_udc(&udc->gadget);
+ pxa_cleanup_debugfs(udc);
err_add_gadget:
if (!IS_ERR_OR_NULL(udc->transceiver))
usb_unregister_notifier(udc->transceiver, &pxa27x_udc_phy);
@@ -2509,13 +2541,19 @@ static int pxa_udc_resume(struct platform_device *_dev)
{
struct pxa_udc *udc = platform_get_drvdata(_dev);
struct pxa_ep *ep;
+ int ret;
ep = &udc->pxa_ep[0];
udc_ep_writel(ep, UDCCSR, udc->udccsr0 & (UDCCSR0_FST | UDCCSR0_DME));
dplus_pullup(udc, udc->pullup_resume);
- if (should_enable_udc(udc))
- udc_enable(udc);
+ if (should_enable_udc(udc)) {
+ ret = udc_enable(udc);
+ if (ret) {
+ dplus_pullup(udc, !udc->pullup_resume);
+ return ret;
+ }
+ }
/*
* We do not handle OTG yet.
*
diff --git a/drivers/usb/gadget/udc/r8a66597-udc.c b/drivers/usb/gadget/udc/r8a66597-udc.c
index ff6f846b1d41..e7a5d8553c0e 100644
--- a/drivers/usb/gadget/udc/r8a66597-udc.c
+++ b/drivers/usb/gadget/udc/r8a66597-udc.c
@@ -1516,7 +1516,7 @@ static irqreturn_t r8a66597_irq(int irq, void *_r8a66597)
static void r8a66597_timer(struct timer_list *t)
{
- struct r8a66597 *r8a66597 = from_timer(r8a66597, t, timer);
+ struct r8a66597 *r8a66597 = timer_container_of(r8a66597, t, timer);
unsigned long flags;
u16 tmp;
@@ -1580,7 +1580,7 @@ static struct usb_request *r8a66597_alloc_request(struct usb_ep *_ep,
{
struct r8a66597_request *req;
- req = kzalloc(sizeof(struct r8a66597_request), gfp_flags);
+ req = kzalloc_obj(struct r8a66597_request, gfp_flags);
if (!req)
return NULL;
@@ -1810,7 +1810,7 @@ static void r8a66597_remove(struct platform_device *pdev)
struct r8a66597 *r8a66597 = platform_get_drvdata(pdev);
usb_del_gadget_udc(&r8a66597->gadget);
- del_timer_sync(&r8a66597->timer);
+ timer_delete_sync(&r8a66597->timer);
r8a66597_free_request(&r8a66597->ep[0].ep, r8a66597->ep0_req);
if (r8a66597->pdata->on_chip) {
diff --git a/drivers/usb/gadget/udc/renesas_usb3.c b/drivers/usb/gadget/udc/renesas_usb3.c
index 89b304cf6d03..2c9c3e935a5e 100644
--- a/drivers/usb/gadget/udc/renesas_usb3.c
+++ b/drivers/usb/gadget/udc/renesas_usb3.c
@@ -1669,6 +1669,10 @@ static bool usb3_std_req_get_status(struct renesas_usb3 *usb3,
break;
case USB_RECIP_ENDPOINT:
num = le16_to_cpu(ctrl->wIndex) & USB_ENDPOINT_NUMBER_MASK;
+ if (num >= usb3->num_usb3_eps) {
+ stall = true;
+ break;
+ }
usb3_ep = usb3_get_ep(usb3, num);
if (usb3_ep->halt)
status |= 1 << USB_ENDPOINT_HALT;
@@ -1781,7 +1785,8 @@ static bool usb3_std_req_feature_endpoint(struct renesas_usb3 *usb3,
struct renesas_usb3_ep *usb3_ep;
struct renesas_usb3_request *usb3_req;
- if (le16_to_cpu(ctrl->wValue) != USB_ENDPOINT_HALT)
+ if ((le16_to_cpu(ctrl->wValue) != USB_ENDPOINT_HALT) ||
+ (num >= usb3->num_usb3_eps))
return true; /* stall */
usb3_ep = usb3_get_ep(usb3, num);
@@ -2265,7 +2270,7 @@ static struct usb_request *__renesas_usb3_ep_alloc_request(gfp_t gfp_flags)
{
struct renesas_usb3_request *usb3_req;
- usb3_req = kzalloc(sizeof(struct renesas_usb3_request), gfp_flags);
+ usb3_req = kzalloc_obj(struct renesas_usb3_request, gfp_flags);
if (!usb3_req)
return NULL;
@@ -2397,8 +2402,7 @@ static int renesas_usb3_stop(struct usb_gadget *gadget)
rzv2m_usb3drd_reset(usb3_to_dev(usb3)->parent, false);
renesas_usb3_stop_controller(usb3);
- if (usb3->phy)
- phy_exit(usb3->phy);
+ phy_exit(usb3->phy);
pm_runtime_put(usb3_to_dev(usb3));
@@ -2658,6 +2662,7 @@ static void renesas_usb3_remove(struct platform_device *pdev)
struct renesas_usb3 *usb3 = platform_get_drvdata(pdev);
debugfs_remove_recursive(usb3->dentry);
+ put_device(usb3->host_dev);
device_remove_file(&pdev->dev, &dev_attr_role);
cancel_work_sync(&usb3->role_work);
@@ -2974,7 +2979,6 @@ err_alloc_prd:
return ret;
}
-#ifdef CONFIG_PM_SLEEP
static int renesas_usb3_suspend(struct device *dev)
{
struct renesas_usb3 *usb3 = dev_get_drvdata(dev);
@@ -2984,8 +2988,7 @@ static int renesas_usb3_suspend(struct device *dev)
return 0;
renesas_usb3_stop_controller(usb3);
- if (usb3->phy)
- phy_exit(usb3->phy);
+ phy_exit(usb3->phy);
pm_runtime_put(dev);
return 0;
@@ -3006,17 +3009,16 @@ static int renesas_usb3_resume(struct device *dev)
return 0;
}
-#endif
-static SIMPLE_DEV_PM_OPS(renesas_usb3_pm_ops, renesas_usb3_suspend,
- renesas_usb3_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(renesas_usb3_pm_ops, renesas_usb3_suspend,
+ renesas_usb3_resume);
static struct platform_driver renesas_usb3_driver = {
.probe = renesas_usb3_probe,
.remove = renesas_usb3_remove,
.driver = {
.name = udc_name,
- .pm = &renesas_usb3_pm_ops,
+ .pm = pm_sleep_ptr(&renesas_usb3_pm_ops),
.of_match_table = usb3_of_match,
},
};
@@ -3025,4 +3027,3 @@ module_platform_driver(renesas_usb3_driver);
MODULE_DESCRIPTION("Renesas USB3.0 Peripheral driver");
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>");
-MODULE_ALIAS("platform:renesas_usb3");
diff --git a/drivers/usb/gadget/udc/renesas_usbf.c b/drivers/usb/gadget/udc/renesas_usbf.c
index 14f4b2cf05a4..5d510665da1f 100644
--- a/drivers/usb/gadget/udc/renesas_usbf.c
+++ b/drivers/usb/gadget/udc/renesas_usbf.c
@@ -2081,7 +2081,7 @@ static struct usb_request *usbf_ep_alloc_request(struct usb_ep *_ep,
if (!_ep)
return NULL;
- req = kzalloc(sizeof(*req), gfp_flags);
+ req = kzalloc_obj(*req, gfp_flags);
if (!req)
return NULL;
@@ -3262,7 +3262,9 @@ static int usbf_probe(struct platform_device *pdev)
if (IS_ERR(udc->regs))
return PTR_ERR(udc->regs);
- devm_pm_runtime_enable(&pdev->dev);
+ ret = devm_pm_runtime_enable(&pdev->dev);
+ if (ret)
+ return ret;
ret = pm_runtime_resume_and_get(&pdev->dev);
if (ret < 0)
return ret;
diff --git a/drivers/usb/gadget/udc/snps_udc_core.c b/drivers/usb/gadget/udc/snps_udc_core.c
index 1f8a99d2a643..0e0db68e0b27 100644
--- a/drivers/usb/gadget/udc/snps_udc_core.c
+++ b/drivers/usb/gadget/udc/snps_udc_core.c
@@ -523,7 +523,7 @@ udc_alloc_request(struct usb_ep *usbep, gfp_t gfp)
ep = container_of(usbep, struct udc_ep, ep);
VDBG(ep->dev, "udc_alloc_req(): ep%d\n", ep->num);
- req = kzalloc(sizeof(struct udc_request), gfp);
+ req = kzalloc_obj(struct udc_request, gfp);
if (!req)
return NULL;
@@ -3035,12 +3035,12 @@ void udc_remove(struct udc *dev)
stop_timer++;
if (timer_pending(&udc_timer))
wait_for_completion(&on_exit);
- del_timer_sync(&udc_timer);
+ timer_delete_sync(&udc_timer);
/* remove pollstall timer */
stop_pollstall_timer++;
if (timer_pending(&udc_pollstall_timer))
wait_for_completion(&on_pollstall_exit);
- del_timer_sync(&udc_pollstall_timer);
+ timer_delete_sync(&udc_pollstall_timer);
udc = NULL;
}
EXPORT_SYMBOL_GPL(udc_remove);
@@ -3151,7 +3151,7 @@ int udc_probe(struct udc *dev)
tmp, dev->phys_addr, dev->chiprev,
(dev->chiprev == UDC_HSA0_REV) ?
"A0" : "B1");
- strcpy(tmp, UDC_DRIVER_VERSION_STRING);
+ strscpy(tmp, UDC_DRIVER_VERSION_STRING);
if (dev->chiprev == UDC_HSA0_REV) {
dev_err(dev->dev, "chip revision is A0; too old\n");
retval = -ENODEV;
diff --git a/drivers/usb/gadget/udc/tegra-xudc.c b/drivers/usb/gadget/udc/tegra-xudc.c
index c7fdbc55fb0b..e9d33be02866 100644
--- a/drivers/usb/gadget/udc/tegra-xudc.c
+++ b/drivers/usb/gadget/udc/tegra-xudc.c
@@ -502,6 +502,7 @@ struct tegra_xudc {
struct clk_bulk_data *clks;
bool device_mode;
+ bool current_device_mode;
struct work_struct usb_role_sw_work;
struct phy **usb3_phy;
@@ -715,6 +716,8 @@ static void tegra_xudc_device_mode_on(struct tegra_xudc *xudc)
phy_set_mode_ext(xudc->curr_utmi_phy, PHY_MODE_USB_OTG,
USB_ROLE_DEVICE);
+
+ xudc->current_device_mode = true;
}
static void tegra_xudc_device_mode_off(struct tegra_xudc *xudc)
@@ -725,6 +728,8 @@ static void tegra_xudc_device_mode_off(struct tegra_xudc *xudc)
dev_dbg(xudc->dev, "device mode off\n");
+ xudc->current_device_mode = false;
+
connected = !!(xudc_readl(xudc, PORTSC) & PORTSC_CCS);
reinit_completion(&xudc->disconnect_complete);
@@ -807,8 +812,7 @@ static void tegra_xudc_update_data_role(struct tegra_xudc *xudc,
return;
}
- xudc->device_mode = (usbphy->last_event == USB_EVENT_VBUS) ? true :
- false;
+ xudc->device_mode = usbphy->last_event == USB_EVENT_VBUS;
phy_index = tegra_xudc_get_phy_index(xudc, usbphy);
dev_dbg(xudc->dev, "%s(): current phy index is %d\n", __func__,
@@ -1554,12 +1558,6 @@ static int __tegra_xudc_ep_set_halt(struct tegra_xudc_ep *ep, bool halt)
return -ENOTSUPP;
}
- if (!!(xudc_readl(xudc, EP_HALT) & BIT(ep->index)) == halt) {
- dev_dbg(xudc->dev, "EP %u already %s\n", ep->index,
- halt ? "halted" : "not halted");
- return 0;
- }
-
if (halt) {
ep_halt(xudc, ep->index);
} else {
@@ -1749,6 +1747,10 @@ static int __tegra_xudc_ep_disable(struct tegra_xudc_ep *ep)
val = xudc_readl(xudc, CTRL);
val &= ~CTRL_RUN;
xudc_writel(xudc, val, CTRL);
+
+ val = xudc_readl(xudc, ST);
+ if (val & ST_RC)
+ xudc_writel(xudc, ST_RC, ST);
}
dev_info(xudc->dev, "ep %u disabled\n", ep->index);
@@ -1904,7 +1906,7 @@ tegra_xudc_ep_alloc_request(struct usb_ep *usb_ep, gfp_t gfp)
{
struct tegra_xudc_request *req;
- req = kzalloc(sizeof(*req), gfp);
+ req = kzalloc_obj(*req, gfp);
if (!req)
return NULL;
@@ -3390,17 +3392,18 @@ static void tegra_xudc_device_params_init(struct tegra_xudc *xudc)
{
u32 val, imod;
+ val = xudc_readl(xudc, BLCG);
if (xudc->soc->has_ipfs) {
- val = xudc_readl(xudc, BLCG);
val |= BLCG_ALL;
val &= ~(BLCG_DFPCI | BLCG_UFPCI | BLCG_FE |
BLCG_COREPLL_PWRDN);
val |= BLCG_IOPLL_0_PWRDN;
val |= BLCG_IOPLL_1_PWRDN;
val |= BLCG_IOPLL_2_PWRDN;
-
- xudc_writel(xudc, val, BLCG);
+ } else {
+ val &= ~BLCG_COREPLL_PWRDN;
}
+ xudc_writel(xudc, val, BLCG);
if (xudc->soc->port_speed_quirk)
tegra_xudc_limit_port_speed(xudc);
@@ -3951,6 +3954,7 @@ static void tegra_xudc_remove(struct platform_device *pdev)
static int __maybe_unused tegra_xudc_powergate(struct tegra_xudc *xudc)
{
unsigned long flags;
+ u32 val;
dev_dbg(xudc->dev, "entering ELPG\n");
@@ -3963,6 +3967,10 @@ static int __maybe_unused tegra_xudc_powergate(struct tegra_xudc *xudc)
spin_unlock_irqrestore(&xudc->lock, flags);
+ val = xudc_readl(xudc, BLCG);
+ val |= BLCG_COREPLL_PWRDN;
+ xudc_writel(xudc, val, BLCG);
+
clk_bulk_disable_unprepare(xudc->soc->num_clks, xudc->clks);
regulator_bulk_disable(xudc->soc->num_supplies, xudc->supplies);
@@ -4040,10 +4048,10 @@ static int __maybe_unused tegra_xudc_resume(struct device *dev)
spin_lock_irqsave(&xudc->lock, flags);
xudc->suspended = false;
+ if (xudc->device_mode != xudc->current_device_mode)
+ schedule_work(&xudc->usb_role_sw_work);
spin_unlock_irqrestore(&xudc->lock, flags);
- schedule_work(&xudc->usb_role_sw_work);
-
pm_runtime_enable(dev);
return 0;
diff --git a/drivers/usb/gadget/udc/trace.h b/drivers/usb/gadget/udc/trace.h
index 4e334298b0e8..fa3e6ddf0a12 100644
--- a/drivers/usb/gadget/udc/trace.h
+++ b/drivers/usb/gadget/udc/trace.h
@@ -81,6 +81,11 @@ DECLARE_EVENT_CLASS(udc_log_gadget,
__entry->ret)
);
+DEFINE_EVENT(udc_log_gadget, usb_gadget_set_state,
+ TP_PROTO(struct usb_gadget *g, int ret),
+ TP_ARGS(g, ret)
+);
+
DEFINE_EVENT(udc_log_gadget, usb_gadget_frame_number,
TP_PROTO(struct usb_gadget *g, int ret),
TP_ARGS(g, ret)
diff --git a/drivers/usb/gadget/udc/udc-xilinx.c b/drivers/usb/gadget/udc/udc-xilinx.c
index ae2aeb271897..bef06fe7543b 100644
--- a/drivers/usb/gadget/udc/udc-xilinx.c
+++ b/drivers/usb/gadget/udc/udc-xilinx.c
@@ -813,10 +813,10 @@ static int __xudc_ep_enable(struct xusb_ep *ep,
ep->is_in = ((desc->bEndpointAddress & USB_DIR_IN) != 0);
/* Bit 3...0:endpoint number */
- ep->epnumber = (desc->bEndpointAddress & 0x0f);
+ ep->epnumber = usb_endpoint_num(desc);
ep->desc = desc;
ep->ep_usb.desc = desc;
- tmp = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+ tmp = usb_endpoint_type(desc);
ep->ep_usb.maxpacket = maxpacket = le16_to_cpu(desc->wMaxPacketSize);
switch (tmp) {
@@ -970,7 +970,7 @@ static struct usb_request *xudc_ep_alloc_request(struct usb_ep *_ep,
struct xusb_ep *ep = to_xusb_ep(_ep);
struct xusb_req *req;
- req = kzalloc(sizeof(*req), gfp_flags);
+ req = kzalloc_obj(*req, gfp_flags);
if (!req)
return NULL;
@@ -2178,8 +2178,6 @@ fail:
/**
* xudc_remove - Releases the resources allocated during the initialization.
* @pdev: pointer to the platform device structure.
- *
- * Return: 0 always
*/
static void xudc_remove(struct platform_device *pdev)
{
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index d011d6c753ed..0a277a07cf70 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -93,7 +93,7 @@ config USB_XHCI_RCAR
default ARCH_RENESAS
help
Say 'Y' to enable the support for the xHCI host controller
- found in Renesas R-Car ARM SoCs.
+ found in Renesas R-Car and RZ/G3E alike ARM SoCs.
config USB_XHCI_RZV2M
bool "xHCI support for Renesas RZ/V2M SoC"
@@ -104,6 +104,15 @@ config USB_XHCI_RZV2M
Say 'Y' to enable the support for the xHCI host controller
found in Renesas RZ/V2M SoC.
+config USB_XHCI_SIDEBAND
+ bool "xHCI support for sideband"
+ help
+ Say 'Y' to enable the support for the xHCI sideband capability.
+ Provide a mechanism for a sideband datapath for payload associated
+ with audio class endpoints. This allows for an audio DSP to use
+ xHCI USB endpoints directly, allowing CPU to sleep while playing
+ audio.
+
config USB_XHCI_TEGRA
tristate "xHCI support for NVIDIA Tegra SoCs"
depends on PHY_TEGRA_XUSB
@@ -225,7 +234,7 @@ config USB_EHCI_HCD_OMAP
tristate "EHCI support for OMAP3 and later chips"
depends on ARCH_OMAP || COMPILE_TEST
depends on NOP_USB_XCEIV
- default y
+ default ARCH_OMAP
help
Enables support for the on-chip EHCI controller on
OMAP3 and later chips.
@@ -374,18 +383,6 @@ config USB_ISP116X_HCD
To compile this driver as a module, choose M here: the
module will be called isp116x-hcd.
-config USB_ISP1362_HCD
- tristate "ISP1362 HCD support"
- depends on HAS_IOPORT
- depends on COMPILE_TEST # nothing uses this
- help
- Supports the Philips ISP1362 chip as a host controller
-
- This driver does not support isochronous transfers.
-
- To compile this driver as a module, choose M here: the
- module will be called isp1362-hcd.
-
config USB_MAX3421_HCD
tristate "MAX3421 HCD (USB-over-SPI) support"
depends on USB && SPI
@@ -607,7 +604,7 @@ config USB_UHCI_ASPEED
config USB_FHCI_HCD
tristate "Freescale QE USB Host Controller support"
- depends on OF_GPIO && QE_GPIO && QUICC_ENGINE
+ depends on QE_GPIO && QUICC_ENGINE
select FSL_GTM
select QE_USB
help
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index be4e5245c52f..a07e7ba9cd53 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -32,6 +32,10 @@ endif
xhci-rcar-hcd-y += xhci-rcar.o
xhci-rcar-hcd-$(CONFIG_USB_XHCI_RZV2M) += xhci-rzv2m.o
+ifneq ($(CONFIG_USB_XHCI_SIDEBAND),)
+ xhci-hcd-y += xhci-sideband.o
+endif
+
obj-$(CONFIG_USB_PCI) += pci-quirks.o
obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o
@@ -48,7 +52,6 @@ obj-$(CONFIG_USB_EHCI_BRCMSTB) += ehci-brcm.o
obj-$(CONFIG_USB_OXU210HP_HCD) += oxu210hp-hcd.o
obj-$(CONFIG_USB_ISP116X_HCD) += isp116x-hcd.o
-obj-$(CONFIG_USB_ISP1362_HCD) += isp1362-hcd.o
obj-$(CONFIG_USB_OHCI_HCD) += ohci-hcd.o
obj-$(CONFIG_USB_OHCI_HCD_PCI) += ohci-pci.o
diff --git a/drivers/usb/host/ehci-brcm.c b/drivers/usb/host/ehci-brcm.c
index 888e8f6670d2..5e3156f94cc6 100644
--- a/drivers/usb/host/ehci-brcm.c
+++ b/drivers/usb/host/ehci-brcm.c
@@ -31,8 +31,8 @@ static inline void ehci_brcm_wait_for_sof(struct ehci_hcd *ehci, u32 delay)
int res;
/* Wait for next microframe (every 125 usecs) */
- res = readl_relaxed_poll_timeout(&ehci->regs->frame_index, val,
- val != frame_idx, 1, 130);
+ res = readl_relaxed_poll_timeout_atomic(&ehci->regs->frame_index,
+ val, val != frame_idx, 1, 130);
if (res)
ehci_err(ehci, "Error waiting for SOF\n");
udelay(delay);
diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c
index 435001128221..3cf743c81eb4 100644
--- a/drivers/usb/host/ehci-dbg.c
+++ b/drivers/usb/host/ehci-dbg.c
@@ -629,7 +629,7 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
unsigned i;
__hc32 tag;
- seen = kmalloc_array(DBG_SCHED_LIMIT, sizeof(*seen), GFP_ATOMIC);
+ seen = kmalloc_objs(*seen, DBG_SCHED_LIMIT, GFP_ATOMIC);
if (!seen)
return 0;
seen_count = 0;
@@ -917,7 +917,7 @@ static struct debug_buffer *alloc_buffer(struct usb_bus *bus,
{
struct debug_buffer *buf;
- buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+ buf = kzalloc_obj(*buf);
if (buf) {
buf->bus = bus;
diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c
index 26f13278d4d6..6ed2fa5418a4 100644
--- a/drivers/usb/host/ehci-fsl.c
+++ b/drivers/usb/host/ehci-fsl.c
@@ -410,15 +410,13 @@ static int ehci_fsl_setup(struct usb_hcd *hcd)
return retval;
}
-struct ehci_fsl {
- struct ehci_hcd ehci;
-
-#ifdef CONFIG_PM
+struct ehci_fsl_priv {
/* Saved USB PHY settings, need to restore after deep sleep. */
u32 usb_ctrl;
-#endif
};
+#define hcd_to_ehci_fsl_priv(h) ((struct ehci_fsl_priv *) hcd_to_ehci(h)->priv)
+
#ifdef CONFIG_PM
#ifdef CONFIG_PPC_MPC512x
@@ -566,17 +564,10 @@ static inline int ehci_fsl_mpc512x_drv_resume(struct device *dev)
}
#endif /* CONFIG_PPC_MPC512x */
-static struct ehci_fsl *hcd_to_ehci_fsl(struct usb_hcd *hcd)
-{
- struct ehci_hcd *ehci = hcd_to_ehci(hcd);
-
- return container_of(ehci, struct ehci_fsl, ehci);
-}
-
static int ehci_fsl_drv_suspend(struct device *dev)
{
struct usb_hcd *hcd = dev_get_drvdata(dev);
- struct ehci_fsl *ehci_fsl = hcd_to_ehci_fsl(hcd);
+ struct ehci_fsl_priv *priv = hcd_to_ehci_fsl_priv(hcd);
void __iomem *non_ehci = hcd->regs;
if (of_device_is_compatible(dev->parent->of_node,
@@ -589,14 +580,14 @@ static int ehci_fsl_drv_suspend(struct device *dev)
if (!fsl_deep_sleep())
return 0;
- ehci_fsl->usb_ctrl = ioread32be(non_ehci + FSL_SOC_USB_CTRL);
+ priv->usb_ctrl = ioread32be(non_ehci + FSL_SOC_USB_CTRL);
return 0;
}
static int ehci_fsl_drv_resume(struct device *dev)
{
struct usb_hcd *hcd = dev_get_drvdata(dev);
- struct ehci_fsl *ehci_fsl = hcd_to_ehci_fsl(hcd);
+ struct ehci_fsl_priv *priv = hcd_to_ehci_fsl_priv(hcd);
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
void __iomem *non_ehci = hcd->regs;
@@ -612,7 +603,7 @@ static int ehci_fsl_drv_resume(struct device *dev)
usb_root_hub_lost_power(hcd->self.root_hub);
/* Restore USB PHY settings and enable the controller. */
- iowrite32be(ehci_fsl->usb_ctrl, non_ehci + FSL_SOC_USB_CTRL);
+ iowrite32be(priv->usb_ctrl, non_ehci + FSL_SOC_USB_CTRL);
ehci_reset(ehci);
ehci_fsl_reinit(ehci);
@@ -671,7 +662,7 @@ static int ehci_start_port_reset(struct usb_hcd *hcd, unsigned port)
#endif /* CONFIG_USB_OTG */
static const struct ehci_driver_overrides ehci_fsl_overrides __initconst = {
- .extra_priv_size = sizeof(struct ehci_fsl),
+ .extra_priv_size = sizeof(struct ehci_fsl_priv),
.reset = ehci_fsl_setup,
};
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 6de79ac5e6a4..3c46bb18c7f3 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -466,8 +466,7 @@ static int ehci_init(struct usb_hcd *hcd)
*/
ehci->need_io_watchdog = 1;
- hrtimer_init(&ehci->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
- ehci->hrtimer.function = ehci_hrtimer_func;
+ hrtimer_setup(&ehci->hrtimer, ehci_hrtimer_func, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
ehci->next_hrtimer_event = EHCI_HRTIMER_NO_EVENT;
hcc_params = ehci_readl(ehci, &ehci->caps->hcc_params);
@@ -1355,12 +1354,6 @@ static int __init ehci_hcd_init(void)
if (usb_disabled())
return -ENODEV;
- set_bit(USB_EHCI_LOADED, &usb_hcds_loaded);
- if (test_bit(USB_UHCI_LOADED, &usb_hcds_loaded) ||
- test_bit(USB_OHCI_LOADED, &usb_hcds_loaded))
- printk(KERN_WARNING "Warning! ehci_hcd should always be loaded"
- " before uhci_hcd and ohci_hcd, not after\n");
-
pr_debug("%s: block sizes: qh %zd qtd %zd itd %zd sitd %zd\n",
hcd_name,
sizeof(struct ehci_qh), sizeof(struct ehci_qtd),
@@ -1391,7 +1384,6 @@ clean0:
debugfs_remove(ehci_debug_root);
ehci_debug_root = NULL;
#endif
- clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded);
return retval;
}
module_init(ehci_hcd_init);
@@ -1405,6 +1397,5 @@ static void __exit ehci_hcd_cleanup(void)
#ifdef CONFIG_DYNAMIC_DEBUG
debugfs_remove(ehci_debug_root);
#endif
- clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded);
}
module_exit(ehci_hcd_cleanup);
diff --git a/drivers/usb/host/ehci-mem.c b/drivers/usb/host/ehci-mem.c
index 4c6c08b675b5..a2d4499e67e0 100644
--- a/drivers/usb/host/ehci-mem.c
+++ b/drivers/usb/host/ehci-mem.c
@@ -69,7 +69,7 @@ static struct ehci_qh *ehci_qh_alloc (struct ehci_hcd *ehci, gfp_t flags)
struct ehci_qh *qh;
dma_addr_t dma;
- qh = kzalloc(sizeof *qh, GFP_ATOMIC);
+ qh = kzalloc_obj(*qh, GFP_ATOMIC);
if (!qh)
goto done;
qh->hw = (struct ehci_qh_hw *)
diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c
index 34abff8669f8..eaaa49712a8c 100644
--- a/drivers/usb/host/ehci-orion.c
+++ b/drivers/usb/host/ehci-orion.c
@@ -12,7 +12,6 @@
#include <linux/clk.h>
#include <linux/platform_data/usb-ehci-orion.h>
#include <linux/of.h>
-#include <linux/phy/phy.h>
#include <linux/usb.h>
#include <linux/usb/hcd.h>
#include <linux/io.h>
@@ -60,7 +59,6 @@
struct orion_ehci_hcd {
struct clk *clk;
- struct phy *phy;
};
static struct hc_driver __read_mostly ehci_orion_hc_driver;
@@ -276,13 +274,6 @@ static int ehci_orion_drv_probe(struct platform_device *pdev)
goto err_put_hcd;
}
- priv->phy = devm_phy_optional_get(&pdev->dev, "usb");
- if (IS_ERR(priv->phy)) {
- err = PTR_ERR(priv->phy);
- if (err != -ENOSYS)
- goto err_dis_clk;
- }
-
/*
* (Re-)program MBUS remapping windows if we are asked to.
*/
diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c
index 889dc4426271..bd3a63555594 100644
--- a/drivers/usb/host/ehci-pci.c
+++ b/drivers/usb/host/ehci-pci.c
@@ -21,7 +21,6 @@ static const char hcd_name[] = "ehci-pci";
/* defined here to avoid adding to pci_ids.h for single instance use */
#define PCI_DEVICE_ID_INTEL_CE4100_USB 0x2e70
-#define PCI_VENDOR_ID_ASPEED 0x1a03
#define PCI_DEVICE_ID_ASPEED_EHCI 0x2603
/*-------------------------------------------------------------------------*/
diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c
index cdf41886e8ca..f61f095cedab 100644
--- a/drivers/usb/host/ehci-platform.c
+++ b/drivers/usb/host/ehci-platform.c
@@ -27,6 +27,7 @@
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
#include <linux/sys_soc.h>
@@ -111,8 +112,7 @@ static void ehci_platform_power_off(struct platform_device *dev)
int clk;
for (clk = EHCI_MAX_CLKS - 1; clk >= 0; clk--)
- if (priv->clks[clk])
- clk_disable_unprepare(priv->clks[clk]);
+ clk_disable_unprepare(priv->clks[clk]);
}
static struct hc_driver __read_mostly ehci_platform_hc_driver;
@@ -198,7 +198,8 @@ static void quirk_poll_work(struct work_struct *work)
static void quirk_poll_timer(struct timer_list *t)
{
- struct ehci_platform_priv *priv = from_timer(priv, t, poll_timer);
+ struct ehci_platform_priv *priv = timer_container_of(priv, t,
+ poll_timer);
struct ehci_hcd *ehci = container_of((void *)priv, struct ehci_hcd,
priv);
@@ -224,7 +225,7 @@ static void quirk_poll_init(struct ehci_platform_priv *priv)
static void quirk_poll_end(struct ehci_platform_priv *priv)
{
- del_timer_sync(&priv->poll_timer);
+ timer_delete_sync(&priv->poll_timer);
cancel_delayed_work(&priv->poll_work);
}
@@ -238,9 +239,11 @@ static int ehci_platform_probe(struct platform_device *dev)
struct usb_hcd *hcd;
struct resource *res_mem;
struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev);
+ const struct of_device_id *match;
struct ehci_platform_priv *priv;
struct ehci_hcd *ehci;
int err, irq, clk = 0;
+ bool dma_mask_64;
if (usb_disabled())
return -ENODEV;
@@ -252,8 +255,13 @@ static int ehci_platform_probe(struct platform_device *dev)
if (!pdata)
pdata = &ehci_platform_defaults;
+ dma_mask_64 = pdata->dma_mask_64;
+ match = of_match_device(dev->dev.driver->of_match_table, &dev->dev);
+ if (match && match->data)
+ dma_mask_64 = true;
+
err = dma_coerce_mask_and_coherent(&dev->dev,
- pdata->dma_mask_64 ? DMA_BIT_MASK(64) : DMA_BIT_MASK(32));
+ dma_mask_64 ? DMA_BIT_MASK(64) : DMA_BIT_MASK(32));
if (err) {
dev_err(&dev->dev, "Error: DMA mask configuration failed\n");
return err;
@@ -297,7 +305,9 @@ static int ehci_platform_probe(struct platform_device *dev)
if (of_device_is_compatible(dev->dev.of_node,
"aspeed,ast2500-ehci") ||
of_device_is_compatible(dev->dev.of_node,
- "aspeed,ast2600-ehci"))
+ "aspeed,ast2600-ehci") ||
+ of_device_is_compatible(dev->dev.of_node,
+ "aspeed,ast2700-ehci"))
ehci->is_aspeed = 1;
if (soc_device_match(quirk_poll_match))
@@ -444,6 +454,17 @@ static int __maybe_unused ehci_platform_suspend(struct device *dev)
if (pdata->power_suspend)
pdata->power_suspend(pdev);
+ ret = reset_control_assert(priv->rsts);
+ if (ret) {
+ if (pdata->power_on)
+ pdata->power_on(pdev);
+
+ ehci_resume(hcd, false);
+
+ if (priv->quirk_poll)
+ quirk_poll_init(priv);
+ }
+
return ret;
}
@@ -454,11 +475,18 @@ static int __maybe_unused ehci_platform_resume(struct device *dev)
struct platform_device *pdev = to_platform_device(dev);
struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd);
struct device *companion_dev;
+ int err;
+
+ err = reset_control_deassert(priv->rsts);
+ if (err)
+ return err;
if (pdata->power_on) {
- int err = pdata->power_on(pdev);
- if (err < 0)
+ err = pdata->power_on(pdev);
+ if (err < 0) {
+ reset_control_assert(priv->rsts);
return err;
+ }
}
companion_dev = usb_of_get_companion_dev(hcd->self.controller);
@@ -484,6 +512,7 @@ static const struct of_device_id vt8500_ehci_ids[] = {
{ .compatible = "wm,prizm-ehci", },
{ .compatible = "generic-ehci", },
{ .compatible = "cavium,octeon-6335-ehci", },
+ { .compatible = "aspeed,ast2700-ehci", .data = (void *)1 },
{}
};
MODULE_DEVICE_TABLE(of, vt8500_ehci_ids);
diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c
index 7e834587e7de..a241337c9af8 100644
--- a/drivers/usb/host/ehci-sched.c
+++ b/drivers/usb/host/ehci-sched.c
@@ -117,9 +117,8 @@ static struct ehci_tt *find_tt(struct usb_device *udev)
if (utt->multi) {
tt_index = utt->hcpriv;
if (!tt_index) { /* Create the index array */
- tt_index = kcalloc(utt->hub->maxchild,
- sizeof(*tt_index),
- GFP_ATOMIC);
+ tt_index = kzalloc_objs(*tt_index, utt->hub->maxchild,
+ GFP_ATOMIC);
if (!tt_index)
return ERR_PTR(-ENOMEM);
utt->hcpriv = tt_index;
@@ -137,7 +136,7 @@ static struct ehci_tt *find_tt(struct usb_device *udev)
struct ehci_hcd *ehci =
hcd_to_ehci(bus_to_hcd(udev->bus));
- tt = kzalloc(sizeof(*tt), GFP_ATOMIC);
+ tt = kzalloc_obj(*tt, GFP_ATOMIC);
if (!tt) {
if (allocated_index) {
utt->hcpriv = NULL;
@@ -1003,7 +1002,7 @@ iso_stream_alloc(gfp_t mem_flags)
{
struct ehci_iso_stream *stream;
- stream = kzalloc(sizeof(*stream), mem_flags);
+ stream = kzalloc_obj(*stream, mem_flags);
if (likely(stream != NULL)) {
INIT_LIST_HEAD(&stream->td_list);
INIT_LIST_HEAD(&stream->free_list);
@@ -1167,7 +1166,7 @@ iso_sched_alloc(unsigned packets, gfp_t mem_flags)
{
struct ehci_iso_sched *iso_sched;
- iso_sched = kzalloc(struct_size(iso_sched, packet, packets), mem_flags);
+ iso_sched = kzalloc_flex(*iso_sched, packet, packets, mem_flags);
if (likely(iso_sched != NULL))
INIT_LIST_HEAD(&iso_sched->td_list);
diff --git a/drivers/usb/host/ehci-sysfs.c b/drivers/usb/host/ehci-sysfs.c
index 8f75cb7b197c..5e6b545c30e6 100644
--- a/drivers/usb/host/ehci-sysfs.c
+++ b/drivers/usb/host/ehci-sysfs.c
@@ -12,21 +12,17 @@ static ssize_t companion_show(struct device *dev,
char *buf)
{
struct ehci_hcd *ehci;
- int nports, index, n;
- int count = PAGE_SIZE;
- char *ptr = buf;
+ int nports, index;
+ int len = 0;
ehci = hcd_to_ehci(dev_get_drvdata(dev));
nports = HCS_N_PORTS(ehci->hcs_params);
for (index = 0; index < nports; ++index) {
- if (test_bit(index, &ehci->companion_ports)) {
- n = scnprintf(ptr, count, "%d\n", index + 1);
- ptr += n;
- count -= n;
- }
+ if (test_bit(index, &ehci->companion_ports))
+ len += sysfs_emit_at(buf, len, "%d\n", index + 1);
}
- return ptr - buf;
+ return len;
}
/*
@@ -70,11 +66,9 @@ static ssize_t uframe_periodic_max_show(struct device *dev,
char *buf)
{
struct ehci_hcd *ehci;
- int n;
ehci = hcd_to_ehci(dev_get_drvdata(dev));
- n = scnprintf(buf, PAGE_SIZE, "%d\n", ehci->uframe_periodic_max);
- return n;
+ return sysfs_emit(buf, "%d\n", ehci->uframe_periodic_max);
}
diff --git a/drivers/usb/host/fhci-hcd.c b/drivers/usb/host/fhci-hcd.c
index 22a0942f0bce..71e785f445a3 100644
--- a/drivers/usb/host/fhci-hcd.c
+++ b/drivers/usb/host/fhci-hcd.c
@@ -193,7 +193,7 @@ static int fhci_mem_init(struct fhci_hcd *fhci)
{
int i;
- fhci->hc_list = kzalloc(sizeof(*fhci->hc_list), GFP_KERNEL);
+ fhci->hc_list = kzalloc_obj(*fhci->hc_list);
if (!fhci->hc_list)
goto err;
@@ -203,7 +203,7 @@ static int fhci_mem_init(struct fhci_hcd *fhci)
INIT_LIST_HEAD(&fhci->hc_list->intr_list);
INIT_LIST_HEAD(&fhci->hc_list->done_list);
- fhci->vroot_hub = kzalloc(sizeof(*fhci->vroot_hub), GFP_KERNEL);
+ fhci->vroot_hub = kzalloc_obj(*fhci->vroot_hub);
if (!fhci->vroot_hub)
goto err;
@@ -217,7 +217,7 @@ static int fhci_mem_init(struct fhci_hcd *fhci)
for (i = 0; i < MAX_TDS; i++) {
struct td *td;
- td = kmalloc(sizeof(*td), GFP_KERNEL);
+ td = kmalloc_obj(*td);
if (!td)
goto err;
fhci_recycle_empty_td(fhci, td);
@@ -225,7 +225,7 @@ static int fhci_mem_init(struct fhci_hcd *fhci)
for (i = 0; i < MAX_EDS; i++) {
struct ed *ed;
- ed = kmalloc(sizeof(*ed), GFP_KERNEL);
+ ed = kmalloc_obj(*ed);
if (!ed)
goto err;
fhci_recycle_empty_ed(fhci, ed);
@@ -264,7 +264,7 @@ static int fhci_usb_init(struct fhci_hcd *fhci)
usb->max_frame_usage = FRAME_TIME_USAGE;
usb->sw_transaction_time = SW_FIX_TIME_BETWEEN_TRANSACTION;
- usb->actual_frame = kzalloc(sizeof(*usb->actual_frame), GFP_KERNEL);
+ usb->actual_frame = kzalloc_obj(*usb->actual_frame);
if (!usb->actual_frame) {
fhci_usb_free(usb);
return -ENOMEM;
@@ -306,7 +306,7 @@ static struct fhci_usb *fhci_create_lld(struct fhci_hcd *fhci)
struct fhci_usb *usb;
/* allocate memory for SCC data structure */
- usb = kzalloc(sizeof(*usb), GFP_KERNEL);
+ usb = kzalloc_obj(*usb);
if (!usb)
return NULL;
@@ -426,16 +426,11 @@ static int fhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
}
/* allocate the private part of the URB */
- urb_priv = kzalloc(sizeof(*urb_priv), mem_flags);
+ urb_priv = kzalloc_flex(*urb_priv, tds, size, mem_flags);
if (!urb_priv)
return -ENOMEM;
- /* allocate the private part of the URB */
- urb_priv->tds = kcalloc(size, sizeof(*urb_priv->tds), mem_flags);
- if (!urb_priv->tds) {
- kfree(urb_priv);
- return -ENOMEM;
- }
+ urb_priv->num_of_tds = size;
spin_lock_irqsave(&fhci->lock, flags);
@@ -444,8 +439,6 @@ static int fhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
goto err;
/* fill the private part of the URB */
- urb_priv->num_of_tds = size;
-
urb->status = -EINPROGRESS;
urb->actual_length = 0;
urb->error_count = 0;
@@ -453,10 +446,8 @@ static int fhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
fhci_queue_urb(fhci, urb);
err:
- if (ret) {
- kfree(urb_priv->tds);
+ if (ret)
kfree(urb_priv);
- }
spin_unlock_irqrestore(&fhci->lock, flags);
return ret;
}
diff --git a/drivers/usb/host/fhci-mem.c b/drivers/usb/host/fhci-mem.c
index 658aedc6adc1..d5951fc1964a 100644
--- a/drivers/usb/host/fhci-mem.c
+++ b/drivers/usb/host/fhci-mem.c
@@ -42,7 +42,7 @@ static struct td *get_empty_td(struct fhci_hcd *fhci)
td = list_entry(fhci->empty_tds.next, struct td, node);
list_del(fhci->empty_tds.next);
} else {
- td = kmalloc(sizeof(*td), GFP_ATOMIC);
+ td = kmalloc_obj(*td, GFP_ATOMIC);
if (!td)
fhci_err(fhci, "No memory to allocate to TD\n");
else
@@ -66,7 +66,7 @@ struct ed *fhci_get_empty_ed(struct fhci_hcd *fhci)
ed = list_entry(fhci->empty_eds.next, struct ed, node);
list_del(fhci->empty_eds.next);
} else {
- ed = kmalloc(sizeof(*ed), GFP_ATOMIC);
+ ed = kmalloc_obj(*ed, GFP_ATOMIC);
if (!ed)
fhci_err(fhci, "No memory to allocate to ED\n");
else
diff --git a/drivers/usb/host/fhci-tds.c b/drivers/usb/host/fhci-tds.c
index d98fd9e1af0b..d7482dc0604e 100644
--- a/drivers/usb/host/fhci-tds.c
+++ b/drivers/usb/host/fhci-tds.c
@@ -161,7 +161,7 @@ u32 fhci_create_ep(struct fhci_usb *usb, enum fhci_mem_alloc data_mem,
return -EINVAL;
}
- ep = kzalloc(sizeof(*ep), GFP_KERNEL);
+ ep = kzalloc_obj(*ep);
if (!ep)
return -ENOMEM;
@@ -183,7 +183,7 @@ u32 fhci_create_ep(struct fhci_usb *usb, enum fhci_mem_alloc data_mem,
struct packet *pkt;
u8 *buff;
- pkt = kmalloc(sizeof(*pkt), GFP_KERNEL);
+ pkt = kmalloc_obj(*pkt);
if (!pkt) {
err_for = "frame";
goto err;
diff --git a/drivers/usb/host/fhci.h b/drivers/usb/host/fhci.h
index 1f57b0989485..e221b28e8608 100644
--- a/drivers/usb/host/fhci.h
+++ b/drivers/usb/host/fhci.h
@@ -387,9 +387,10 @@ struct urb_priv {
int tds_cnt;
int state;
- struct td **tds;
struct ed *ed;
struct timer_list time_out;
+
+ struct td *tds[] __counted_by(num_of_tds);
};
struct endpoint {
diff --git a/drivers/usb/host/fsl-mph-dr-of.c b/drivers/usb/host/fsl-mph-dr-of.c
index 4e67b9471986..edfb63543029 100644
--- a/drivers/usb/host/fsl-mph-dr-of.c
+++ b/drivers/usb/host/fsl-mph-dr-of.c
@@ -327,8 +327,7 @@ static void fsl_usb2_mpc5121_exit(struct platform_device *pdev)
pdata->regs = NULL;
- if (pdata->clk)
- clk_disable_unprepare(pdata->clk);
+ clk_disable_unprepare(pdata->clk);
}
static struct fsl_usb2_platform_data fsl_usb2_mpc5121_pd = {
diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c
index 71c22c4bd163..b5e9056895e4 100644
--- a/drivers/usb/host/isp116x-hcd.c
+++ b/drivers/usb/host/isp116x-hcd.c
@@ -703,7 +703,7 @@ static int isp116x_urb_enqueue(struct usb_hcd *hcd,
}
/* avoid all allocations within spinlocks: request or endpoint */
if (!hep->hcpriv) {
- ep = kzalloc(sizeof *ep, mem_flags);
+ ep = kzalloc_obj(*ep, mem_flags);
if (!ep)
return -ENOMEM;
}
diff --git a/drivers/usb/host/isp1362-hcd.c b/drivers/usb/host/isp1362-hcd.c
deleted file mode 100644
index 2d3a082cb52f..000000000000
--- a/drivers/usb/host/isp1362-hcd.c
+++ /dev/null
@@ -1,2769 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * ISP1362 HCD (Host Controller Driver) for USB.
- *
- * Copyright (C) 2005 Lothar Wassmann <LW@KARO-electronics.de>
- *
- * Derived from the SL811 HCD, rewritten for ISP116x.
- * Copyright (C) 2005 Olav Kongas <ok@artecdesign.ee>
- *
- * Portions:
- * Copyright (C) 2004 Psion Teklogix (for NetBook PRO)
- * Copyright (C) 2004 David Brownell
- */
-
-/*
- * The ISP1362 chip requires a large delay (300ns and 462ns) between
- * accesses to the address and data register.
- * The following timing options exist:
- *
- * 1. Configure your memory controller to add such delays if it can (the best)
- * 2. Implement platform-specific delay function possibly
- * combined with configuring the memory controller; see
- * include/linux/usb_isp1362.h for more info.
- * 3. Use ndelay (easiest, poorest).
- *
- * Use the corresponding macros USE_PLATFORM_DELAY and USE_NDELAY in the
- * platform specific section of isp1362.h to select the appropriate variant.
- *
- * Also note that according to the Philips "ISP1362 Errata" document
- * Rev 1.00 from 27 May data corruption may occur when the #WR signal
- * is reasserted (even with #CS deasserted) within 132ns after a
- * write cycle to any controller register. If the hardware doesn't
- * implement the recommended fix (gating the #WR with #CS) software
- * must ensure that no further write cycle (not necessarily to the chip!)
- * is issued by the CPU within this interval.
-
- * For PXA25x this can be ensured by using VLIO with the maximum
- * recovery time (MSCx = 0x7f8c) with a memory clock of 99.53 MHz.
- */
-
-#undef ISP1362_DEBUG
-
-/*
- * The PXA255 UDC apparently doesn't handle GET_STATUS, GET_CONFIG and
- * GET_INTERFACE requests correctly when the SETUP and DATA stages of the
- * requests are carried out in separate frames. This will delay any SETUP
- * packets until the start of the next frame so that this situation is
- * unlikely to occur (and makes usbtest happy running with a PXA255 target
- * device).
- */
-#undef BUGGY_PXA2XX_UDC_USBTEST
-
-#undef PTD_TRACE
-#undef URB_TRACE
-#undef VERBOSE
-#undef REGISTERS
-
-/* This enables a memory test on the ISP1362 chip memory to make sure the
- * chip access timing is correct.
- */
-#undef CHIP_BUFFER_TEST
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/ioport.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/errno.h>
-#include <linux/list.h>
-#include <linux/interrupt.h>
-#include <linux/usb.h>
-#include <linux/usb/isp1362.h>
-#include <linux/usb/hcd.h>
-#include <linux/platform_device.h>
-#include <linux/pm.h>
-#include <linux/io.h>
-#include <linux/bitmap.h>
-#include <linux/prefetch.h>
-#include <linux/debugfs.h>
-#include <linux/seq_file.h>
-
-#include <asm/irq.h>
-#include <asm/byteorder.h>
-#include <linux/unaligned.h>
-
-static int dbg_level;
-#ifdef ISP1362_DEBUG
-module_param(dbg_level, int, 0644);
-#else
-module_param(dbg_level, int, 0);
-#endif
-
-#include "../core/usb.h"
-#include "isp1362.h"
-
-
-#define DRIVER_VERSION "2005-04-04"
-#define DRIVER_DESC "ISP1362 USB Host Controller Driver"
-
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_LICENSE("GPL");
-
-static const char hcd_name[] = "isp1362-hcd";
-
-static void isp1362_hc_stop(struct usb_hcd *hcd);
-static int isp1362_hc_start(struct usb_hcd *hcd);
-
-/*-------------------------------------------------------------------------*/
-
-/*
- * When called from the interrupthandler only isp1362_hcd->irqenb is modified,
- * since the interrupt handler will write isp1362_hcd->irqenb to HCuPINT upon
- * completion.
- * We don't need a 'disable' counterpart, since interrupts will be disabled
- * only by the interrupt handler.
- */
-static inline void isp1362_enable_int(struct isp1362_hcd *isp1362_hcd, u16 mask)
-{
- if ((isp1362_hcd->irqenb | mask) == isp1362_hcd->irqenb)
- return;
- if (mask & ~isp1362_hcd->irqenb)
- isp1362_write_reg16(isp1362_hcd, HCuPINT, mask & ~isp1362_hcd->irqenb);
- isp1362_hcd->irqenb |= mask;
- if (isp1362_hcd->irq_active)
- return;
- isp1362_write_reg16(isp1362_hcd, HCuPINTENB, isp1362_hcd->irqenb);
-}
-
-/*-------------------------------------------------------------------------*/
-
-static inline struct isp1362_ep_queue *get_ptd_queue(struct isp1362_hcd *isp1362_hcd,
- u16 offset)
-{
- struct isp1362_ep_queue *epq = NULL;
-
- if (offset < isp1362_hcd->istl_queue[1].buf_start)
- epq = &isp1362_hcd->istl_queue[0];
- else if (offset < isp1362_hcd->intl_queue.buf_start)
- epq = &isp1362_hcd->istl_queue[1];
- else if (offset < isp1362_hcd->atl_queue.buf_start)
- epq = &isp1362_hcd->intl_queue;
- else if (offset < isp1362_hcd->atl_queue.buf_start +
- isp1362_hcd->atl_queue.buf_size)
- epq = &isp1362_hcd->atl_queue;
-
- if (epq)
- DBG(1, "%s: PTD $%04x is on %s queue\n", __func__, offset, epq->name);
- else
- pr_warn("%s: invalid PTD $%04x\n", __func__, offset);
-
- return epq;
-}
-
-static inline int get_ptd_offset(struct isp1362_ep_queue *epq, u8 index)
-{
- int offset;
-
- if (index * epq->blk_size > epq->buf_size) {
- pr_warn("%s: Bad %s index %d(%d)\n",
- __func__, epq->name, index,
- epq->buf_size / epq->blk_size);
- return -EINVAL;
- }
- offset = epq->buf_start + index * epq->blk_size;
- DBG(3, "%s: %s PTD[%02x] # %04x\n", __func__, epq->name, index, offset);
-
- return offset;
-}
-
-/*-------------------------------------------------------------------------*/
-
-static inline u16 max_transfer_size(struct isp1362_ep_queue *epq, size_t size,
- int mps)
-{
- u16 xfer_size = min_t(size_t, MAX_XFER_SIZE, size);
-
- xfer_size = min_t(size_t, xfer_size, epq->buf_avail * epq->blk_size - PTD_HEADER_SIZE);
- if (xfer_size < size && xfer_size % mps)
- xfer_size -= xfer_size % mps;
-
- return xfer_size;
-}
-
-static int claim_ptd_buffers(struct isp1362_ep_queue *epq,
- struct isp1362_ep *ep, u16 len)
-{
- int ptd_offset = -EINVAL;
- int num_ptds = ((len + PTD_HEADER_SIZE - 1) / epq->blk_size) + 1;
- int found;
-
- BUG_ON(len > epq->buf_size);
-
- if (!epq->buf_avail)
- return -ENOMEM;
-
- if (ep->num_ptds)
- pr_err("%s: %s len %d/%d num_ptds %d buf_map %08lx skip_map %08lx\n", __func__,
- epq->name, len, epq->blk_size, num_ptds, epq->buf_map, epq->skip_map);
- BUG_ON(ep->num_ptds != 0);
-
- found = bitmap_find_next_zero_area(&epq->buf_map, epq->buf_count, 0,
- num_ptds, 0);
- if (found >= epq->buf_count)
- return -EOVERFLOW;
-
- DBG(1, "%s: Found %d PTDs[%d] for %d/%d byte\n", __func__,
- num_ptds, found, len, (int)(epq->blk_size - PTD_HEADER_SIZE));
- ptd_offset = get_ptd_offset(epq, found);
- WARN_ON(ptd_offset < 0);
- ep->ptd_offset = ptd_offset;
- ep->num_ptds += num_ptds;
- epq->buf_avail -= num_ptds;
- BUG_ON(epq->buf_avail > epq->buf_count);
- ep->ptd_index = found;
- bitmap_set(&epq->buf_map, found, num_ptds);
- DBG(1, "%s: Done %s PTD[%d] $%04x, avail %d count %d claimed %d %08lx:%08lx\n",
- __func__, epq->name, ep->ptd_index, ep->ptd_offset,
- epq->buf_avail, epq->buf_count, num_ptds, epq->buf_map, epq->skip_map);
-
- return found;
-}
-
-static inline void release_ptd_buffers(struct isp1362_ep_queue *epq, struct isp1362_ep *ep)
-{
- int last = ep->ptd_index + ep->num_ptds;
-
- if (last > epq->buf_count)
- pr_err("%s: ep %p req %d len %d %s PTD[%d] $%04x num_ptds %d buf_count %d buf_avail %d buf_map %08lx skip_map %08lx\n",
- __func__, ep, ep->num_req, ep->length, epq->name, ep->ptd_index,
- ep->ptd_offset, ep->num_ptds, epq->buf_count, epq->buf_avail,
- epq->buf_map, epq->skip_map);
- BUG_ON(last > epq->buf_count);
-
- bitmap_clear(&epq->buf_map, ep->ptd_index, ep->num_ptds);
- bitmap_set(&epq->skip_map, ep->ptd_index, ep->num_ptds);
- epq->buf_avail += ep->num_ptds;
- epq->ptd_count--;
-
- BUG_ON(epq->buf_avail > epq->buf_count);
- BUG_ON(epq->ptd_count > epq->buf_count);
-
- DBG(1, "%s: Done %s PTDs $%04x released %d avail %d count %d\n",
- __func__, epq->name,
- ep->ptd_offset, ep->num_ptds, epq->buf_avail, epq->buf_count);
- DBG(1, "%s: buf_map %08lx skip_map %08lx\n", __func__,
- epq->buf_map, epq->skip_map);
-
- ep->num_ptds = 0;
- ep->ptd_offset = -EINVAL;
- ep->ptd_index = -EINVAL;
-}
-
-/*-------------------------------------------------------------------------*/
-
-/*
- Set up PTD's.
-*/
-static void prepare_ptd(struct isp1362_hcd *isp1362_hcd, struct urb *urb,
- struct isp1362_ep *ep, struct isp1362_ep_queue *epq,
- u16 fno)
-{
- struct ptd *ptd;
- int toggle;
- int dir;
- u16 len;
- size_t buf_len = urb->transfer_buffer_length - urb->actual_length;
-
- DBG(3, "%s: %s ep %p\n", __func__, epq->name, ep);
-
- ptd = &ep->ptd;
-
- ep->data = (unsigned char *)urb->transfer_buffer + urb->actual_length;
-
- switch (ep->nextpid) {
- case USB_PID_IN:
- toggle = usb_gettoggle(urb->dev, ep->epnum, 0);
- dir = PTD_DIR_IN;
- if (usb_pipecontrol(urb->pipe)) {
- len = min_t(size_t, ep->maxpacket, buf_len);
- } else if (usb_pipeisoc(urb->pipe)) {
- len = min_t(size_t, urb->iso_frame_desc[fno].length, MAX_XFER_SIZE);
- ep->data = urb->transfer_buffer + urb->iso_frame_desc[fno].offset;
- } else
- len = max_transfer_size(epq, buf_len, ep->maxpacket);
- DBG(1, "%s: IN len %d/%d/%d from URB\n", __func__, len, ep->maxpacket,
- (int)buf_len);
- break;
- case USB_PID_OUT:
- toggle = usb_gettoggle(urb->dev, ep->epnum, 1);
- dir = PTD_DIR_OUT;
- if (usb_pipecontrol(urb->pipe))
- len = min_t(size_t, ep->maxpacket, buf_len);
- else if (usb_pipeisoc(urb->pipe))
- len = min_t(size_t, urb->iso_frame_desc[0].length, MAX_XFER_SIZE);
- else
- len = max_transfer_size(epq, buf_len, ep->maxpacket);
- if (len == 0)
- pr_info("%s: Sending ZERO packet: %d\n", __func__,
- urb->transfer_flags & URB_ZERO_PACKET);
- DBG(1, "%s: OUT len %d/%d/%d from URB\n", __func__, len, ep->maxpacket,
- (int)buf_len);
- break;
- case USB_PID_SETUP:
- toggle = 0;
- dir = PTD_DIR_SETUP;
- len = sizeof(struct usb_ctrlrequest);
- DBG(1, "%s: SETUP len %d\n", __func__, len);
- ep->data = urb->setup_packet;
- break;
- case USB_PID_ACK:
- toggle = 1;
- len = 0;
- dir = (urb->transfer_buffer_length && usb_pipein(urb->pipe)) ?
- PTD_DIR_OUT : PTD_DIR_IN;
- DBG(1, "%s: ACK len %d\n", __func__, len);
- break;
- default:
- toggle = dir = len = 0;
- pr_err("%s@%d: ep->nextpid %02x\n", __func__, __LINE__, ep->nextpid);
- BUG_ON(1);
- }
-
- ep->length = len;
- if (!len)
- ep->data = NULL;
-
- ptd->count = PTD_CC_MSK | PTD_ACTIVE_MSK | PTD_TOGGLE(toggle);
- ptd->mps = PTD_MPS(ep->maxpacket) | PTD_SPD(urb->dev->speed == USB_SPEED_LOW) |
- PTD_EP(ep->epnum);
- ptd->len = PTD_LEN(len) | PTD_DIR(dir);
- ptd->faddr = PTD_FA(usb_pipedevice(urb->pipe));
-
- if (usb_pipeint(urb->pipe)) {
- ptd->faddr |= PTD_SF_INT(ep->branch);
- ptd->faddr |= PTD_PR(ep->interval ? __ffs(ep->interval) : 0);
- }
- if (usb_pipeisoc(urb->pipe))
- ptd->faddr |= PTD_SF_ISO(fno);
-
- DBG(1, "%s: Finished\n", __func__);
-}
-
-static void isp1362_write_ptd(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep *ep,
- struct isp1362_ep_queue *epq)
-{
- struct ptd *ptd = &ep->ptd;
- int len = PTD_GET_DIR(ptd) == PTD_DIR_IN ? 0 : ep->length;
-
- prefetch(ptd);
- isp1362_write_buffer(isp1362_hcd, ptd, ep->ptd_offset, PTD_HEADER_SIZE);
- if (len)
- isp1362_write_buffer(isp1362_hcd, ep->data,
- ep->ptd_offset + PTD_HEADER_SIZE, len);
-
- dump_ptd(ptd);
- dump_ptd_out_data(ptd, ep->data);
-}
-
-static void isp1362_read_ptd(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep *ep,
- struct isp1362_ep_queue *epq)
-{
- struct ptd *ptd = &ep->ptd;
- int act_len;
-
- WARN_ON(list_empty(&ep->active));
- BUG_ON(ep->ptd_offset < 0);
-
- list_del_init(&ep->active);
- DBG(1, "%s: ep %p removed from active list %p\n", __func__, ep, &epq->active);
-
- prefetchw(ptd);
- isp1362_read_buffer(isp1362_hcd, ptd, ep->ptd_offset, PTD_HEADER_SIZE);
- dump_ptd(ptd);
- act_len = PTD_GET_COUNT(ptd);
- if (PTD_GET_DIR(ptd) != PTD_DIR_IN || act_len == 0)
- return;
- if (act_len > ep->length)
- pr_err("%s: ep %p PTD $%04x act_len %d ep->length %d\n", __func__, ep,
- ep->ptd_offset, act_len, ep->length);
- BUG_ON(act_len > ep->length);
- /* Only transfer the amount of data that has actually been overwritten
- * in the chip buffer. We don't want any data that doesn't belong to the
- * transfer to leak out of the chip to the callers transfer buffer!
- */
- prefetchw(ep->data);
- isp1362_read_buffer(isp1362_hcd, ep->data,
- ep->ptd_offset + PTD_HEADER_SIZE, act_len);
- dump_ptd_in_data(ptd, ep->data);
-}
-
-/*
- * INT PTDs will stay in the chip until data is available.
- * This function will remove a PTD from the chip when the URB is dequeued.
- * Must be called with the spinlock held and IRQs disabled
- */
-static void remove_ptd(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep *ep)
-
-{
- int index;
- struct isp1362_ep_queue *epq;
-
- DBG(1, "%s: ep %p PTD[%d] $%04x\n", __func__, ep, ep->ptd_index, ep->ptd_offset);
- BUG_ON(ep->ptd_offset < 0);
-
- epq = get_ptd_queue(isp1362_hcd, ep->ptd_offset);
- BUG_ON(!epq);
-
- /* put ep in remove_list for cleanup */
- WARN_ON(!list_empty(&ep->remove_list));
- list_add_tail(&ep->remove_list, &isp1362_hcd->remove_list);
- /* let SOF interrupt handle the cleanup */
- isp1362_enable_int(isp1362_hcd, HCuPINT_SOF);
-
- index = ep->ptd_index;
- if (index < 0)
- /* ISO queues don't have SKIP registers */
- return;
-
- DBG(1, "%s: Disabling PTD[%02x] $%04x %08lx|%08x\n", __func__,
- index, ep->ptd_offset, epq->skip_map, 1 << index);
-
- /* prevent further processing of PTD (will be effective after next SOF) */
- epq->skip_map |= 1 << index;
- if (epq == &isp1362_hcd->atl_queue) {
- DBG(2, "%s: ATLSKIP = %08x -> %08lx\n", __func__,
- isp1362_read_reg32(isp1362_hcd, HCATLSKIP), epq->skip_map);
- isp1362_write_reg32(isp1362_hcd, HCATLSKIP, epq->skip_map);
- if (~epq->skip_map == 0)
- isp1362_clr_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_ATL_ACTIVE);
- } else if (epq == &isp1362_hcd->intl_queue) {
- DBG(2, "%s: INTLSKIP = %08x -> %08lx\n", __func__,
- isp1362_read_reg32(isp1362_hcd, HCINTLSKIP), epq->skip_map);
- isp1362_write_reg32(isp1362_hcd, HCINTLSKIP, epq->skip_map);
- if (~epq->skip_map == 0)
- isp1362_clr_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_INTL_ACTIVE);
- }
-}
-
-/*
- Take done or failed requests out of schedule. Give back
- processed urbs.
-*/
-static void finish_request(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep *ep,
- struct urb *urb, int status)
- __releases(isp1362_hcd->lock)
- __acquires(isp1362_hcd->lock)
-{
- urb->hcpriv = NULL;
- ep->error_count = 0;
-
- if (usb_pipecontrol(urb->pipe))
- ep->nextpid = USB_PID_SETUP;
-
- URB_DBG("%s: req %d FA %d ep%d%s %s: len %d/%d %s stat %d\n", __func__,
- ep->num_req, usb_pipedevice(urb->pipe),
- usb_pipeendpoint(urb->pipe),
- !usb_pipein(urb->pipe) ? "out" : "in",
- usb_pipecontrol(urb->pipe) ? "ctrl" :
- usb_pipeint(urb->pipe) ? "int" :
- usb_pipebulk(urb->pipe) ? "bulk" :
- "iso",
- urb->actual_length, urb->transfer_buffer_length,
- !(urb->transfer_flags & URB_SHORT_NOT_OK) ?
- "short_ok" : "", urb->status);
-
-
- usb_hcd_unlink_urb_from_ep(isp1362_hcd_to_hcd(isp1362_hcd), urb);
- spin_unlock(&isp1362_hcd->lock);
- usb_hcd_giveback_urb(isp1362_hcd_to_hcd(isp1362_hcd), urb, status);
- spin_lock(&isp1362_hcd->lock);
-
- /* take idle endpoints out of the schedule right away */
- if (!list_empty(&ep->hep->urb_list))
- return;
-
- /* async deschedule */
- if (!list_empty(&ep->schedule)) {
- list_del_init(&ep->schedule);
- return;
- }
-
-
- if (ep->interval) {
- /* periodic deschedule */
- DBG(1, "deschedule qh%d/%p branch %d load %d bandwidth %d -> %d\n", ep->interval,
- ep, ep->branch, ep->load,
- isp1362_hcd->load[ep->branch],
- isp1362_hcd->load[ep->branch] - ep->load);
- isp1362_hcd->load[ep->branch] -= ep->load;
- ep->branch = PERIODIC_SIZE;
- }
-}
-
-/*
- * Analyze transfer results, handle partial transfers and errors
-*/
-static void postproc_ep(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep *ep)
-{
- struct urb *urb = get_urb(ep);
- struct usb_device *udev;
- struct ptd *ptd;
- int short_ok;
- u16 len;
- int urbstat = -EINPROGRESS;
- u8 cc;
-
- DBG(2, "%s: ep %p req %d\n", __func__, ep, ep->num_req);
-
- udev = urb->dev;
- ptd = &ep->ptd;
- cc = PTD_GET_CC(ptd);
- if (cc == PTD_NOTACCESSED) {
- pr_err("%s: req %d PTD %p Untouched by ISP1362\n", __func__,
- ep->num_req, ptd);
- cc = PTD_DEVNOTRESP;
- }
-
- short_ok = !(urb->transfer_flags & URB_SHORT_NOT_OK);
- len = urb->transfer_buffer_length - urb->actual_length;
-
- /* Data underrun is special. For allowed underrun
- we clear the error and continue as normal. For
- forbidden underrun we finish the DATA stage
- immediately while for control transfer,
- we do a STATUS stage.
- */
- if (cc == PTD_DATAUNDERRUN) {
- if (short_ok) {
- DBG(1, "%s: req %d Allowed data underrun short_%sok %d/%d/%d byte\n",
- __func__, ep->num_req, short_ok ? "" : "not_",
- PTD_GET_COUNT(ptd), ep->maxpacket, len);
- cc = PTD_CC_NOERROR;
- urbstat = 0;
- } else {
- DBG(1, "%s: req %d Data Underrun %s nextpid %02x short_%sok %d/%d/%d byte\n",
- __func__, ep->num_req,
- usb_pipein(urb->pipe) ? "IN" : "OUT", ep->nextpid,
- short_ok ? "" : "not_",
- PTD_GET_COUNT(ptd), ep->maxpacket, len);
- /* save the data underrun error code for later and
- * proceed with the status stage
- */
- urb->actual_length += PTD_GET_COUNT(ptd);
- if (usb_pipecontrol(urb->pipe)) {
- ep->nextpid = USB_PID_ACK;
- BUG_ON(urb->actual_length > urb->transfer_buffer_length);
-
- if (urb->status == -EINPROGRESS)
- urb->status = cc_to_error[PTD_DATAUNDERRUN];
- } else {
- usb_settoggle(udev, ep->epnum, ep->nextpid == USB_PID_OUT,
- PTD_GET_TOGGLE(ptd));
- urbstat = cc_to_error[PTD_DATAUNDERRUN];
- }
- goto out;
- }
- }
-
- if (cc != PTD_CC_NOERROR) {
- if (++ep->error_count >= 3 || cc == PTD_CC_STALL || cc == PTD_DATAOVERRUN) {
- urbstat = cc_to_error[cc];
- DBG(1, "%s: req %d nextpid %02x, status %d, error %d, error_count %d\n",
- __func__, ep->num_req, ep->nextpid, urbstat, cc,
- ep->error_count);
- }
- goto out;
- }
-
- switch (ep->nextpid) {
- case USB_PID_OUT:
- if (PTD_GET_COUNT(ptd) != ep->length)
- pr_err("%s: count=%d len=%d\n", __func__,
- PTD_GET_COUNT(ptd), ep->length);
- BUG_ON(PTD_GET_COUNT(ptd) != ep->length);
- urb->actual_length += ep->length;
- BUG_ON(urb->actual_length > urb->transfer_buffer_length);
- usb_settoggle(udev, ep->epnum, 1, PTD_GET_TOGGLE(ptd));
- if (urb->actual_length == urb->transfer_buffer_length) {
- DBG(3, "%s: req %d xfer complete %d/%d status %d -> 0\n", __func__,
- ep->num_req, len, ep->maxpacket, urbstat);
- if (usb_pipecontrol(urb->pipe)) {
- DBG(3, "%s: req %d %s Wait for ACK\n", __func__,
- ep->num_req,
- usb_pipein(urb->pipe) ? "IN" : "OUT");
- ep->nextpid = USB_PID_ACK;
- } else {
- if (len % ep->maxpacket ||
- !(urb->transfer_flags & URB_ZERO_PACKET)) {
- urbstat = 0;
- DBG(3, "%s: req %d URB %s status %d count %d/%d/%d\n",
- __func__, ep->num_req, usb_pipein(urb->pipe) ? "IN" : "OUT",
- urbstat, len, ep->maxpacket, urb->actual_length);
- }
- }
- }
- break;
- case USB_PID_IN:
- len = PTD_GET_COUNT(ptd);
- BUG_ON(len > ep->length);
- urb->actual_length += len;
- BUG_ON(urb->actual_length > urb->transfer_buffer_length);
- usb_settoggle(udev, ep->epnum, 0, PTD_GET_TOGGLE(ptd));
- /* if transfer completed or (allowed) data underrun */
- if ((urb->transfer_buffer_length == urb->actual_length) ||
- len % ep->maxpacket) {
- DBG(3, "%s: req %d xfer complete %d/%d status %d -> 0\n", __func__,
- ep->num_req, len, ep->maxpacket, urbstat);
- if (usb_pipecontrol(urb->pipe)) {
- DBG(3, "%s: req %d %s Wait for ACK\n", __func__,
- ep->num_req,
- usb_pipein(urb->pipe) ? "IN" : "OUT");
- ep->nextpid = USB_PID_ACK;
- } else {
- urbstat = 0;
- DBG(3, "%s: req %d URB %s status %d count %d/%d/%d\n",
- __func__, ep->num_req, usb_pipein(urb->pipe) ? "IN" : "OUT",
- urbstat, len, ep->maxpacket, urb->actual_length);
- }
- }
- break;
- case USB_PID_SETUP:
- if (urb->transfer_buffer_length == urb->actual_length) {
- ep->nextpid = USB_PID_ACK;
- } else if (usb_pipeout(urb->pipe)) {
- usb_settoggle(udev, 0, 1, 1);
- ep->nextpid = USB_PID_OUT;
- } else {
- usb_settoggle(udev, 0, 0, 1);
- ep->nextpid = USB_PID_IN;
- }
- break;
- case USB_PID_ACK:
- DBG(3, "%s: req %d got ACK %d -> 0\n", __func__, ep->num_req,
- urbstat);
- WARN_ON(urbstat != -EINPROGRESS);
- urbstat = 0;
- ep->nextpid = 0;
- break;
- default:
- BUG_ON(1);
- }
-
- out:
- if (urbstat != -EINPROGRESS) {
- DBG(2, "%s: Finishing ep %p req %d urb %p status %d\n", __func__,
- ep, ep->num_req, urb, urbstat);
- finish_request(isp1362_hcd, ep, urb, urbstat);
- }
-}
-
-static void finish_unlinks(struct isp1362_hcd *isp1362_hcd)
-{
- struct isp1362_ep *ep;
- struct isp1362_ep *tmp;
-
- list_for_each_entry_safe(ep, tmp, &isp1362_hcd->remove_list, remove_list) {
- struct isp1362_ep_queue *epq =
- get_ptd_queue(isp1362_hcd, ep->ptd_offset);
- int index = ep->ptd_index;
-
- BUG_ON(epq == NULL);
- if (index >= 0) {
- DBG(1, "%s: remove PTD[%d] $%04x\n", __func__, index, ep->ptd_offset);
- BUG_ON(ep->num_ptds == 0);
- release_ptd_buffers(epq, ep);
- }
- if (!list_empty(&ep->hep->urb_list)) {
- struct urb *urb = get_urb(ep);
-
- DBG(1, "%s: Finishing req %d ep %p from remove_list\n", __func__,
- ep->num_req, ep);
- finish_request(isp1362_hcd, ep, urb, -ESHUTDOWN);
- }
- WARN_ON(list_empty(&ep->active));
- if (!list_empty(&ep->active)) {
- list_del_init(&ep->active);
- DBG(1, "%s: ep %p removed from active list\n", __func__, ep);
- }
- list_del_init(&ep->remove_list);
- DBG(1, "%s: ep %p removed from remove_list\n", __func__, ep);
- }
- DBG(1, "%s: Done\n", __func__);
-}
-
-static inline void enable_atl_transfers(struct isp1362_hcd *isp1362_hcd, int count)
-{
- if (count > 0) {
- if (count < isp1362_hcd->atl_queue.ptd_count)
- isp1362_write_reg16(isp1362_hcd, HCATLDTC, count);
- isp1362_enable_int(isp1362_hcd, HCuPINT_ATL);
- isp1362_write_reg32(isp1362_hcd, HCATLSKIP, isp1362_hcd->atl_queue.skip_map);
- isp1362_set_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_ATL_ACTIVE);
- } else
- isp1362_enable_int(isp1362_hcd, HCuPINT_SOF);
-}
-
-static inline void enable_intl_transfers(struct isp1362_hcd *isp1362_hcd)
-{
- isp1362_enable_int(isp1362_hcd, HCuPINT_INTL);
- isp1362_set_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_INTL_ACTIVE);
- isp1362_write_reg32(isp1362_hcd, HCINTLSKIP, isp1362_hcd->intl_queue.skip_map);
-}
-
-static inline void enable_istl_transfers(struct isp1362_hcd *isp1362_hcd, int flip)
-{
- isp1362_enable_int(isp1362_hcd, flip ? HCuPINT_ISTL1 : HCuPINT_ISTL0);
- isp1362_set_mask16(isp1362_hcd, HCBUFSTAT, flip ?
- HCBUFSTAT_ISTL1_FULL : HCBUFSTAT_ISTL0_FULL);
-}
-
-static int submit_req(struct isp1362_hcd *isp1362_hcd, struct urb *urb,
- struct isp1362_ep *ep, struct isp1362_ep_queue *epq)
-{
- int index;
-
- prepare_ptd(isp1362_hcd, urb, ep, epq, 0);
- index = claim_ptd_buffers(epq, ep, ep->length);
- if (index == -ENOMEM) {
- DBG(1, "%s: req %d No free %s PTD available: %d, %08lx:%08lx\n", __func__,
- ep->num_req, epq->name, ep->num_ptds, epq->buf_map, epq->skip_map);
- return index;
- } else if (index == -EOVERFLOW) {
- DBG(1, "%s: req %d Not enough space for %d byte %s PTD %d %08lx:%08lx\n",
- __func__, ep->num_req, ep->length, epq->name, ep->num_ptds,
- epq->buf_map, epq->skip_map);
- return index;
- } else
- BUG_ON(index < 0);
- list_add_tail(&ep->active, &epq->active);
- DBG(1, "%s: ep %p req %d len %d added to active list %p\n", __func__,
- ep, ep->num_req, ep->length, &epq->active);
- DBG(1, "%s: Submitting %s PTD $%04x for ep %p req %d\n", __func__, epq->name,
- ep->ptd_offset, ep, ep->num_req);
- isp1362_write_ptd(isp1362_hcd, ep, epq);
- __clear_bit(ep->ptd_index, &epq->skip_map);
-
- return 0;
-}
-
-static void start_atl_transfers(struct isp1362_hcd *isp1362_hcd)
-{
- int ptd_count = 0;
- struct isp1362_ep_queue *epq = &isp1362_hcd->atl_queue;
- struct isp1362_ep *ep;
- int defer = 0;
-
- if (atomic_read(&epq->finishing)) {
- DBG(1, "%s: finish_transfers is active for %s\n", __func__, epq->name);
- return;
- }
-
- list_for_each_entry(ep, &isp1362_hcd->async, schedule) {
- struct urb *urb = get_urb(ep);
- int ret;
-
- if (!list_empty(&ep->active)) {
- DBG(2, "%s: Skipping active %s ep %p\n", __func__, epq->name, ep);
- continue;
- }
-
- DBG(1, "%s: Processing %s ep %p req %d\n", __func__, epq->name,
- ep, ep->num_req);
-
- ret = submit_req(isp1362_hcd, urb, ep, epq);
- if (ret == -ENOMEM) {
- defer = 1;
- break;
- } else if (ret == -EOVERFLOW) {
- defer = 1;
- continue;
- }
-#ifdef BUGGY_PXA2XX_UDC_USBTEST
- defer = ep->nextpid == USB_PID_SETUP;
-#endif
- ptd_count++;
- }
-
- /* Avoid starving of endpoints */
- if (isp1362_hcd->async.next != isp1362_hcd->async.prev) {
- DBG(2, "%s: Cycling ASYNC schedule %d\n", __func__, ptd_count);
- list_move(&isp1362_hcd->async, isp1362_hcd->async.next);
- }
- if (ptd_count || defer)
- enable_atl_transfers(isp1362_hcd, defer ? 0 : ptd_count);
-
- epq->ptd_count += ptd_count;
- if (epq->ptd_count > epq->stat_maxptds) {
- epq->stat_maxptds = epq->ptd_count;
- DBG(0, "%s: max_ptds: %d\n", __func__, epq->stat_maxptds);
- }
-}
-
-static void start_intl_transfers(struct isp1362_hcd *isp1362_hcd)
-{
- int ptd_count = 0;
- struct isp1362_ep_queue *epq = &isp1362_hcd->intl_queue;
- struct isp1362_ep *ep;
-
- if (atomic_read(&epq->finishing)) {
- DBG(1, "%s: finish_transfers is active for %s\n", __func__, epq->name);
- return;
- }
-
- list_for_each_entry(ep, &isp1362_hcd->periodic, schedule) {
- struct urb *urb = get_urb(ep);
- int ret;
-
- if (!list_empty(&ep->active)) {
- DBG(1, "%s: Skipping active %s ep %p\n", __func__,
- epq->name, ep);
- continue;
- }
-
- DBG(1, "%s: Processing %s ep %p req %d\n", __func__,
- epq->name, ep, ep->num_req);
- ret = submit_req(isp1362_hcd, urb, ep, epq);
- if (ret == -ENOMEM)
- break;
- else if (ret == -EOVERFLOW)
- continue;
- ptd_count++;
- }
-
- if (ptd_count) {
- static int last_count;
-
- if (ptd_count != last_count) {
- DBG(0, "%s: ptd_count: %d\n", __func__, ptd_count);
- last_count = ptd_count;
- }
- enable_intl_transfers(isp1362_hcd);
- }
-
- epq->ptd_count += ptd_count;
- if (epq->ptd_count > epq->stat_maxptds)
- epq->stat_maxptds = epq->ptd_count;
-}
-
-static inline int next_ptd(struct isp1362_ep_queue *epq, struct isp1362_ep *ep)
-{
- u16 ptd_offset = ep->ptd_offset;
- int num_ptds = (ep->length + PTD_HEADER_SIZE + (epq->blk_size - 1)) / epq->blk_size;
-
- DBG(2, "%s: PTD offset $%04x + %04x => %d * %04x -> $%04x\n", __func__, ptd_offset,
- ep->length, num_ptds, epq->blk_size, ptd_offset + num_ptds * epq->blk_size);
-
- ptd_offset += num_ptds * epq->blk_size;
- if (ptd_offset < epq->buf_start + epq->buf_size)
- return ptd_offset;
- else
- return -ENOMEM;
-}
-
-static void start_iso_transfers(struct isp1362_hcd *isp1362_hcd)
-{
- int ptd_count = 0;
- int flip = isp1362_hcd->istl_flip;
- struct isp1362_ep_queue *epq;
- int ptd_offset;
- struct isp1362_ep *ep;
- struct isp1362_ep *tmp;
- u16 fno = isp1362_read_reg32(isp1362_hcd, HCFMNUM);
-
- fill2:
- epq = &isp1362_hcd->istl_queue[flip];
- if (atomic_read(&epq->finishing)) {
- DBG(1, "%s: finish_transfers is active for %s\n", __func__, epq->name);
- return;
- }
-
- if (!list_empty(&epq->active))
- return;
-
- ptd_offset = epq->buf_start;
- list_for_each_entry_safe(ep, tmp, &isp1362_hcd->isoc, schedule) {
- struct urb *urb = get_urb(ep);
- s16 diff = fno - (u16)urb->start_frame;
-
- DBG(1, "%s: Processing %s ep %p\n", __func__, epq->name, ep);
-
- if (diff > urb->number_of_packets) {
- /* time frame for this URB has elapsed */
- finish_request(isp1362_hcd, ep, urb, -EOVERFLOW);
- continue;
- } else if (diff < -1) {
- /* URB is not due in this frame or the next one.
- * Comparing with '-1' instead of '0' accounts for double
- * buffering in the ISP1362 which enables us to queue the PTD
- * one frame ahead of time
- */
- } else if (diff == -1) {
- /* submit PTD's that are due in the next frame */
- prepare_ptd(isp1362_hcd, urb, ep, epq, fno);
- if (ptd_offset + PTD_HEADER_SIZE + ep->length >
- epq->buf_start + epq->buf_size) {
- pr_err("%s: Not enough ISO buffer space for %d byte PTD\n",
- __func__, ep->length);
- continue;
- }
- ep->ptd_offset = ptd_offset;
- list_add_tail(&ep->active, &epq->active);
-
- ptd_offset = next_ptd(epq, ep);
- if (ptd_offset < 0) {
- pr_warn("%s: req %d No more %s PTD buffers available\n",
- __func__, ep->num_req, epq->name);
- break;
- }
- }
- }
- list_for_each_entry(ep, &epq->active, active) {
- if (epq->active.next == &ep->active)
- ep->ptd.mps |= PTD_LAST_MSK;
- isp1362_write_ptd(isp1362_hcd, ep, epq);
- ptd_count++;
- }
-
- if (ptd_count)
- enable_istl_transfers(isp1362_hcd, flip);
-
- epq->ptd_count += ptd_count;
- if (epq->ptd_count > epq->stat_maxptds)
- epq->stat_maxptds = epq->ptd_count;
-
- /* check, whether the second ISTL buffer may also be filled */
- if (!(isp1362_read_reg16(isp1362_hcd, HCBUFSTAT) &
- (flip ? HCBUFSTAT_ISTL0_FULL : HCBUFSTAT_ISTL1_FULL))) {
- fno++;
- ptd_count = 0;
- flip = 1 - flip;
- goto fill2;
- }
-}
-
-static void finish_transfers(struct isp1362_hcd *isp1362_hcd, unsigned long done_map,
- struct isp1362_ep_queue *epq)
-{
- struct isp1362_ep *ep;
- struct isp1362_ep *tmp;
-
- if (list_empty(&epq->active)) {
- DBG(1, "%s: Nothing to do for %s queue\n", __func__, epq->name);
- return;
- }
-
- DBG(1, "%s: Finishing %s transfers %08lx\n", __func__, epq->name, done_map);
-
- atomic_inc(&epq->finishing);
- list_for_each_entry_safe(ep, tmp, &epq->active, active) {
- int index = ep->ptd_index;
-
- DBG(1, "%s: Checking %s PTD[%02x] $%04x\n", __func__, epq->name,
- index, ep->ptd_offset);
-
- BUG_ON(index < 0);
- if (__test_and_clear_bit(index, &done_map)) {
- isp1362_read_ptd(isp1362_hcd, ep, epq);
- epq->free_ptd = index;
- BUG_ON(ep->num_ptds == 0);
- release_ptd_buffers(epq, ep);
-
- DBG(1, "%s: ep %p req %d removed from active list\n", __func__,
- ep, ep->num_req);
- if (!list_empty(&ep->remove_list)) {
- list_del_init(&ep->remove_list);
- DBG(1, "%s: ep %p removed from remove list\n", __func__, ep);
- }
- DBG(1, "%s: Postprocessing %s ep %p req %d\n", __func__, epq->name,
- ep, ep->num_req);
- postproc_ep(isp1362_hcd, ep);
- }
- if (!done_map)
- break;
- }
- if (done_map)
- pr_warn("%s: done_map not clear: %08lx:%08lx\n",
- __func__, done_map, epq->skip_map);
- atomic_dec(&epq->finishing);
-}
-
-static void finish_iso_transfers(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep_queue *epq)
-{
- struct isp1362_ep *ep;
- struct isp1362_ep *tmp;
-
- if (list_empty(&epq->active)) {
- DBG(1, "%s: Nothing to do for %s queue\n", __func__, epq->name);
- return;
- }
-
- DBG(1, "%s: Finishing %s transfers\n", __func__, epq->name);
-
- atomic_inc(&epq->finishing);
- list_for_each_entry_safe(ep, tmp, &epq->active, active) {
- DBG(1, "%s: Checking PTD $%04x\n", __func__, ep->ptd_offset);
-
- isp1362_read_ptd(isp1362_hcd, ep, epq);
- DBG(1, "%s: Postprocessing %s ep %p\n", __func__, epq->name, ep);
- postproc_ep(isp1362_hcd, ep);
- }
- WARN_ON(epq->blk_size != 0);
- atomic_dec(&epq->finishing);
-}
-
-static irqreturn_t isp1362_irq(struct usb_hcd *hcd)
-{
- int handled = 0;
- struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
- u16 irqstat;
- u16 svc_mask;
-
- spin_lock(&isp1362_hcd->lock);
-
- BUG_ON(isp1362_hcd->irq_active++);
-
- isp1362_write_reg16(isp1362_hcd, HCuPINTENB, 0);
-
- irqstat = isp1362_read_reg16(isp1362_hcd, HCuPINT);
- DBG(3, "%s: got IRQ %04x:%04x\n", __func__, irqstat, isp1362_hcd->irqenb);
-
- /* only handle interrupts that are currently enabled */
- irqstat &= isp1362_hcd->irqenb;
- isp1362_write_reg16(isp1362_hcd, HCuPINT, irqstat);
- svc_mask = irqstat;
-
- if (irqstat & HCuPINT_SOF) {
- isp1362_hcd->irqenb &= ~HCuPINT_SOF;
- isp1362_hcd->irq_stat[ISP1362_INT_SOF]++;
- handled = 1;
- svc_mask &= ~HCuPINT_SOF;
- DBG(3, "%s: SOF\n", __func__);
- isp1362_hcd->fmindex = isp1362_read_reg32(isp1362_hcd, HCFMNUM);
- if (!list_empty(&isp1362_hcd->remove_list))
- finish_unlinks(isp1362_hcd);
- if (!list_empty(&isp1362_hcd->async) && !(irqstat & HCuPINT_ATL)) {
- if (list_empty(&isp1362_hcd->atl_queue.active)) {
- start_atl_transfers(isp1362_hcd);
- } else {
- isp1362_enable_int(isp1362_hcd, HCuPINT_ATL);
- isp1362_write_reg32(isp1362_hcd, HCATLSKIP,
- isp1362_hcd->atl_queue.skip_map);
- isp1362_set_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_ATL_ACTIVE);
- }
- }
- }
-
- if (irqstat & HCuPINT_ISTL0) {
- isp1362_hcd->irq_stat[ISP1362_INT_ISTL0]++;
- handled = 1;
- svc_mask &= ~HCuPINT_ISTL0;
- isp1362_clr_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_ISTL0_FULL);
- DBG(1, "%s: ISTL0\n", __func__);
- WARN_ON((int)!!isp1362_hcd->istl_flip);
- WARN_ON(isp1362_read_reg16(isp1362_hcd, HCBUFSTAT) &
- HCBUFSTAT_ISTL0_ACTIVE);
- WARN_ON(!(isp1362_read_reg16(isp1362_hcd, HCBUFSTAT) &
- HCBUFSTAT_ISTL0_DONE));
- isp1362_hcd->irqenb &= ~HCuPINT_ISTL0;
- }
-
- if (irqstat & HCuPINT_ISTL1) {
- isp1362_hcd->irq_stat[ISP1362_INT_ISTL1]++;
- handled = 1;
- svc_mask &= ~HCuPINT_ISTL1;
- isp1362_clr_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_ISTL1_FULL);
- DBG(1, "%s: ISTL1\n", __func__);
- WARN_ON(!(int)isp1362_hcd->istl_flip);
- WARN_ON(isp1362_read_reg16(isp1362_hcd, HCBUFSTAT) &
- HCBUFSTAT_ISTL1_ACTIVE);
- WARN_ON(!(isp1362_read_reg16(isp1362_hcd, HCBUFSTAT) &
- HCBUFSTAT_ISTL1_DONE));
- isp1362_hcd->irqenb &= ~HCuPINT_ISTL1;
- }
-
- if (irqstat & (HCuPINT_ISTL0 | HCuPINT_ISTL1)) {
- WARN_ON((irqstat & (HCuPINT_ISTL0 | HCuPINT_ISTL1)) ==
- (HCuPINT_ISTL0 | HCuPINT_ISTL1));
- finish_iso_transfers(isp1362_hcd,
- &isp1362_hcd->istl_queue[isp1362_hcd->istl_flip]);
- start_iso_transfers(isp1362_hcd);
- isp1362_hcd->istl_flip = 1 - isp1362_hcd->istl_flip;
- }
-
- if (irqstat & HCuPINT_INTL) {
- u32 done_map = isp1362_read_reg32(isp1362_hcd, HCINTLDONE);
- u32 skip_map = isp1362_read_reg32(isp1362_hcd, HCINTLSKIP);
- isp1362_hcd->irq_stat[ISP1362_INT_INTL]++;
-
- DBG(2, "%s: INTL\n", __func__);
-
- svc_mask &= ~HCuPINT_INTL;
-
- isp1362_write_reg32(isp1362_hcd, HCINTLSKIP, skip_map | done_map);
- if (~(done_map | skip_map) == 0)
- /* All PTDs are finished, disable INTL processing entirely */
- isp1362_clr_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_INTL_ACTIVE);
-
- handled = 1;
- WARN_ON(!done_map);
- if (done_map) {
- DBG(3, "%s: INTL done_map %08x\n", __func__, done_map);
- finish_transfers(isp1362_hcd, done_map, &isp1362_hcd->intl_queue);
- start_intl_transfers(isp1362_hcd);
- }
- }
-
- if (irqstat & HCuPINT_ATL) {
- u32 done_map = isp1362_read_reg32(isp1362_hcd, HCATLDONE);
- u32 skip_map = isp1362_read_reg32(isp1362_hcd, HCATLSKIP);
- isp1362_hcd->irq_stat[ISP1362_INT_ATL]++;
-
- DBG(2, "%s: ATL\n", __func__);
-
- svc_mask &= ~HCuPINT_ATL;
-
- isp1362_write_reg32(isp1362_hcd, HCATLSKIP, skip_map | done_map);
- if (~(done_map | skip_map) == 0)
- isp1362_clr_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_ATL_ACTIVE);
- if (done_map) {
- DBG(3, "%s: ATL done_map %08x\n", __func__, done_map);
- finish_transfers(isp1362_hcd, done_map, &isp1362_hcd->atl_queue);
- start_atl_transfers(isp1362_hcd);
- }
- handled = 1;
- }
-
- if (irqstat & HCuPINT_OPR) {
- u32 intstat = isp1362_read_reg32(isp1362_hcd, HCINTSTAT);
- isp1362_hcd->irq_stat[ISP1362_INT_OPR]++;
-
- svc_mask &= ~HCuPINT_OPR;
- DBG(2, "%s: OPR %08x:%08x\n", __func__, intstat, isp1362_hcd->intenb);
- intstat &= isp1362_hcd->intenb;
- if (intstat & OHCI_INTR_UE) {
- pr_err("Unrecoverable error\n");
- /* FIXME: do here reset or cleanup or whatever */
- }
- if (intstat & OHCI_INTR_RHSC) {
- isp1362_hcd->rhstatus = isp1362_read_reg32(isp1362_hcd, HCRHSTATUS);
- isp1362_hcd->rhport[0] = isp1362_read_reg32(isp1362_hcd, HCRHPORT1);
- isp1362_hcd->rhport[1] = isp1362_read_reg32(isp1362_hcd, HCRHPORT2);
- }
- if (intstat & OHCI_INTR_RD) {
- pr_info("%s: RESUME DETECTED\n", __func__);
- isp1362_show_reg(isp1362_hcd, HCCONTROL);
- usb_hcd_resume_root_hub(hcd);
- }
- isp1362_write_reg32(isp1362_hcd, HCINTSTAT, intstat);
- irqstat &= ~HCuPINT_OPR;
- handled = 1;
- }
-
- if (irqstat & HCuPINT_SUSP) {
- isp1362_hcd->irq_stat[ISP1362_INT_SUSP]++;
- handled = 1;
- svc_mask &= ~HCuPINT_SUSP;
-
- pr_info("%s: SUSPEND IRQ\n", __func__);
- }
-
- if (irqstat & HCuPINT_CLKRDY) {
- isp1362_hcd->irq_stat[ISP1362_INT_CLKRDY]++;
- handled = 1;
- isp1362_hcd->irqenb &= ~HCuPINT_CLKRDY;
- svc_mask &= ~HCuPINT_CLKRDY;
- pr_info("%s: CLKRDY IRQ\n", __func__);
- }
-
- if (svc_mask)
- pr_err("%s: Unserviced interrupt(s) %04x\n", __func__, svc_mask);
-
- isp1362_write_reg16(isp1362_hcd, HCuPINTENB, isp1362_hcd->irqenb);
- isp1362_hcd->irq_active--;
- spin_unlock(&isp1362_hcd->lock);
-
- return IRQ_RETVAL(handled);
-}
-
-/*-------------------------------------------------------------------------*/
-
-#define MAX_PERIODIC_LOAD 900 /* out of 1000 usec */
-static int balance(struct isp1362_hcd *isp1362_hcd, u16 interval, u16 load)
-{
- int i, branch = -ENOSPC;
-
- /* search for the least loaded schedule branch of that interval
- * which has enough bandwidth left unreserved.
- */
- for (i = 0; i < interval; i++) {
- if (branch < 0 || isp1362_hcd->load[branch] > isp1362_hcd->load[i]) {
- int j;
-
- for (j = i; j < PERIODIC_SIZE; j += interval) {
- if ((isp1362_hcd->load[j] + load) > MAX_PERIODIC_LOAD) {
- pr_err("%s: new load %d load[%02x] %d max %d\n", __func__,
- load, j, isp1362_hcd->load[j], MAX_PERIODIC_LOAD);
- break;
- }
- }
- if (j < PERIODIC_SIZE)
- continue;
- branch = i;
- }
- }
- return branch;
-}
-
-/* NB! ALL the code above this point runs with isp1362_hcd->lock
- held, irqs off
-*/
-
-/*-------------------------------------------------------------------------*/
-
-static int isp1362_urb_enqueue(struct usb_hcd *hcd,
- struct urb *urb,
- gfp_t mem_flags)
-{
- struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
- struct usb_device *udev = urb->dev;
- unsigned int pipe = urb->pipe;
- int is_out = !usb_pipein(pipe);
- int type = usb_pipetype(pipe);
- int epnum = usb_pipeendpoint(pipe);
- struct usb_host_endpoint *hep = urb->ep;
- struct isp1362_ep *ep = NULL;
- unsigned long flags;
- int retval = 0;
-
- DBG(3, "%s: urb %p\n", __func__, urb);
-
- if (type == PIPE_ISOCHRONOUS) {
- pr_err("Isochronous transfers not supported\n");
- return -ENOSPC;
- }
-
- URB_DBG("%s: FA %d ep%d%s %s: len %d %s%s\n", __func__,
- usb_pipedevice(pipe), epnum,
- is_out ? "out" : "in",
- usb_pipecontrol(pipe) ? "ctrl" :
- usb_pipeint(pipe) ? "int" :
- usb_pipebulk(pipe) ? "bulk" :
- "iso",
- urb->transfer_buffer_length,
- (urb->transfer_flags & URB_ZERO_PACKET) ? "ZERO_PACKET " : "",
- !(urb->transfer_flags & URB_SHORT_NOT_OK) ?
- "short_ok" : "");
-
- /* avoid all allocations within spinlocks: request or endpoint */
- if (!hep->hcpriv) {
- ep = kzalloc(sizeof *ep, mem_flags);
- if (!ep)
- return -ENOMEM;
- }
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
-
- /* don't submit to a dead or disabled port */
- if (!((isp1362_hcd->rhport[0] | isp1362_hcd->rhport[1]) &
- USB_PORT_STAT_ENABLE) ||
- !HC_IS_RUNNING(hcd->state)) {
- kfree(ep);
- retval = -ENODEV;
- goto fail_not_linked;
- }
-
- retval = usb_hcd_link_urb_to_ep(hcd, urb);
- if (retval) {
- kfree(ep);
- goto fail_not_linked;
- }
-
- if (hep->hcpriv) {
- ep = hep->hcpriv;
- } else {
- INIT_LIST_HEAD(&ep->schedule);
- INIT_LIST_HEAD(&ep->active);
- INIT_LIST_HEAD(&ep->remove_list);
- ep->udev = usb_get_dev(udev);
- ep->hep = hep;
- ep->epnum = epnum;
- ep->maxpacket = usb_maxpacket(udev, urb->pipe);
- ep->ptd_offset = -EINVAL;
- ep->ptd_index = -EINVAL;
- usb_settoggle(udev, epnum, is_out, 0);
-
- if (type == PIPE_CONTROL)
- ep->nextpid = USB_PID_SETUP;
- else if (is_out)
- ep->nextpid = USB_PID_OUT;
- else
- ep->nextpid = USB_PID_IN;
-
- switch (type) {
- case PIPE_ISOCHRONOUS:
- case PIPE_INTERRUPT:
- if (urb->interval > PERIODIC_SIZE)
- urb->interval = PERIODIC_SIZE;
- ep->interval = urb->interval;
- ep->branch = PERIODIC_SIZE;
- ep->load = usb_calc_bus_time(udev->speed, !is_out,
- type == PIPE_ISOCHRONOUS,
- usb_maxpacket(udev, pipe)) / 1000;
- break;
- }
- hep->hcpriv = ep;
- }
- ep->num_req = isp1362_hcd->req_serial++;
-
- /* maybe put endpoint into schedule */
- switch (type) {
- case PIPE_CONTROL:
- case PIPE_BULK:
- if (list_empty(&ep->schedule)) {
- DBG(1, "%s: Adding ep %p req %d to async schedule\n",
- __func__, ep, ep->num_req);
- list_add_tail(&ep->schedule, &isp1362_hcd->async);
- }
- break;
- case PIPE_ISOCHRONOUS:
- case PIPE_INTERRUPT:
- urb->interval = ep->interval;
-
- /* urb submitted for already existing EP */
- if (ep->branch < PERIODIC_SIZE)
- break;
-
- retval = balance(isp1362_hcd, ep->interval, ep->load);
- if (retval < 0) {
- pr_err("%s: balance returned %d\n", __func__, retval);
- goto fail;
- }
- ep->branch = retval;
- retval = 0;
- isp1362_hcd->fmindex = isp1362_read_reg32(isp1362_hcd, HCFMNUM);
- DBG(1, "%s: Current frame %04x branch %02x start_frame %04x(%04x)\n",
- __func__, isp1362_hcd->fmindex, ep->branch,
- ((isp1362_hcd->fmindex + PERIODIC_SIZE - 1) &
- ~(PERIODIC_SIZE - 1)) + ep->branch,
- (isp1362_hcd->fmindex & (PERIODIC_SIZE - 1)) + ep->branch);
-
- if (list_empty(&ep->schedule)) {
- if (type == PIPE_ISOCHRONOUS) {
- u16 frame = isp1362_hcd->fmindex;
-
- frame += max_t(u16, 8, ep->interval);
- frame &= ~(ep->interval - 1);
- frame |= ep->branch;
- if (frame_before(frame, isp1362_hcd->fmindex))
- frame += ep->interval;
- urb->start_frame = frame;
-
- DBG(1, "%s: Adding ep %p to isoc schedule\n", __func__, ep);
- list_add_tail(&ep->schedule, &isp1362_hcd->isoc);
- } else {
- DBG(1, "%s: Adding ep %p to periodic schedule\n", __func__, ep);
- list_add_tail(&ep->schedule, &isp1362_hcd->periodic);
- }
- } else
- DBG(1, "%s: ep %p already scheduled\n", __func__, ep);
-
- DBG(2, "%s: load %d bandwidth %d -> %d\n", __func__,
- ep->load / ep->interval, isp1362_hcd->load[ep->branch],
- isp1362_hcd->load[ep->branch] + ep->load);
- isp1362_hcd->load[ep->branch] += ep->load;
- }
-
- urb->hcpriv = hep;
- ALIGNSTAT(isp1362_hcd, urb->transfer_buffer);
-
- switch (type) {
- case PIPE_CONTROL:
- case PIPE_BULK:
- start_atl_transfers(isp1362_hcd);
- break;
- case PIPE_INTERRUPT:
- start_intl_transfers(isp1362_hcd);
- break;
- case PIPE_ISOCHRONOUS:
- start_iso_transfers(isp1362_hcd);
- break;
- default:
- BUG();
- }
- fail:
- if (retval)
- usb_hcd_unlink_urb_from_ep(hcd, urb);
-
-
- fail_not_linked:
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
- if (retval)
- DBG(0, "%s: urb %p failed with %d\n", __func__, urb, retval);
- return retval;
-}
-
-static int isp1362_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
-{
- struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
- struct usb_host_endpoint *hep;
- unsigned long flags;
- struct isp1362_ep *ep;
- int retval = 0;
-
- DBG(3, "%s: urb %p\n", __func__, urb);
-
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
- retval = usb_hcd_check_unlink_urb(hcd, urb, status);
- if (retval)
- goto done;
-
- hep = urb->hcpriv;
-
- if (!hep) {
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
- return -EIDRM;
- }
-
- ep = hep->hcpriv;
- if (ep) {
- /* In front of queue? */
- if (ep->hep->urb_list.next == &urb->urb_list) {
- if (!list_empty(&ep->active)) {
- DBG(1, "%s: urb %p ep %p req %d active PTD[%d] $%04x\n", __func__,
- urb, ep, ep->num_req, ep->ptd_index, ep->ptd_offset);
- /* disable processing and queue PTD for removal */
- remove_ptd(isp1362_hcd, ep);
- urb = NULL;
- }
- }
- if (urb) {
- DBG(1, "%s: Finishing ep %p req %d\n", __func__, ep,
- ep->num_req);
- finish_request(isp1362_hcd, ep, urb, status);
- } else
- DBG(1, "%s: urb %p active; wait4irq\n", __func__, urb);
- } else {
- pr_warn("%s: No EP in URB %p\n", __func__, urb);
- retval = -EINVAL;
- }
-done:
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
-
- DBG(3, "%s: exit\n", __func__);
-
- return retval;
-}
-
-static void isp1362_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep)
-{
- struct isp1362_ep *ep = hep->hcpriv;
- struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
- unsigned long flags;
-
- DBG(1, "%s: ep %p\n", __func__, ep);
- if (!ep)
- return;
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
- if (!list_empty(&hep->urb_list)) {
- if (!list_empty(&ep->active) && list_empty(&ep->remove_list)) {
- DBG(1, "%s: Removing ep %p req %d PTD[%d] $%04x\n", __func__,
- ep, ep->num_req, ep->ptd_index, ep->ptd_offset);
- remove_ptd(isp1362_hcd, ep);
- pr_info("%s: Waiting for Interrupt to clean up\n", __func__);
- }
- }
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
- /* Wait for interrupt to clear out active list */
- while (!list_empty(&ep->active))
- msleep(1);
-
- DBG(1, "%s: Freeing EP %p\n", __func__, ep);
-
- usb_put_dev(ep->udev);
- kfree(ep);
- hep->hcpriv = NULL;
-}
-
-static int isp1362_get_frame(struct usb_hcd *hcd)
-{
- struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
- u32 fmnum;
- unsigned long flags;
-
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
- fmnum = isp1362_read_reg32(isp1362_hcd, HCFMNUM);
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
-
- return (int)fmnum;
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* Adapted from ohci-hub.c */
-static int isp1362_hub_status_data(struct usb_hcd *hcd, char *buf)
-{
- struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
- int ports, i, changed = 0;
- unsigned long flags;
-
- if (!HC_IS_RUNNING(hcd->state))
- return -ESHUTDOWN;
-
- /* Report no status change now, if we are scheduled to be
- called later */
- if (timer_pending(&hcd->rh_timer))
- return 0;
-
- ports = isp1362_hcd->rhdesca & RH_A_NDP;
- BUG_ON(ports > 2);
-
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
- /* init status */
- if (isp1362_hcd->rhstatus & (RH_HS_LPSC | RH_HS_OCIC))
- buf[0] = changed = 1;
- else
- buf[0] = 0;
-
- for (i = 0; i < ports; i++) {
- u32 status = isp1362_hcd->rhport[i];
-
- if (status & (RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC |
- RH_PS_OCIC | RH_PS_PRSC)) {
- changed = 1;
- buf[0] |= 1 << (i + 1);
- continue;
- }
-
- if (!(status & RH_PS_CCS))
- continue;
- }
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
- return changed;
-}
-
-static void isp1362_hub_descriptor(struct isp1362_hcd *isp1362_hcd,
- struct usb_hub_descriptor *desc)
-{
- u32 reg = isp1362_hcd->rhdesca;
-
- DBG(3, "%s: enter\n", __func__);
-
- desc->bDescriptorType = USB_DT_HUB;
- desc->bDescLength = 9;
- desc->bHubContrCurrent = 0;
- desc->bNbrPorts = reg & 0x3;
- /* Power switching, device type, overcurrent. */
- desc->wHubCharacteristics = cpu_to_le16((reg >> 8) &
- (HUB_CHAR_LPSM |
- HUB_CHAR_COMPOUND |
- HUB_CHAR_OCPM));
- DBG(0, "%s: hubcharacteristics = %02x\n", __func__,
- desc->wHubCharacteristics);
- desc->bPwrOn2PwrGood = (reg >> 24) & 0xff;
- /* ports removable, and legacy PortPwrCtrlMask */
- desc->u.hs.DeviceRemovable[0] = desc->bNbrPorts == 1 ? 1 << 1 : 3 << 1;
- desc->u.hs.DeviceRemovable[1] = ~0;
-
- DBG(3, "%s: exit\n", __func__);
-}
-
-/* Adapted from ohci-hub.c */
-static int isp1362_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
- u16 wIndex, char *buf, u16 wLength)
-{
- struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
- int retval = 0;
- unsigned long flags;
- unsigned long t1;
- int ports = isp1362_hcd->rhdesca & RH_A_NDP;
- u32 tmp = 0;
-
- switch (typeReq) {
- case ClearHubFeature:
- DBG(0, "ClearHubFeature: ");
- switch (wValue) {
- case C_HUB_OVER_CURRENT:
- DBG(0, "C_HUB_OVER_CURRENT\n");
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
- isp1362_write_reg32(isp1362_hcd, HCRHSTATUS, RH_HS_OCIC);
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
- break;
- case C_HUB_LOCAL_POWER:
- DBG(0, "C_HUB_LOCAL_POWER\n");
- break;
- default:
- goto error;
- }
- break;
- case SetHubFeature:
- DBG(0, "SetHubFeature: ");
- switch (wValue) {
- case C_HUB_OVER_CURRENT:
- case C_HUB_LOCAL_POWER:
- DBG(0, "C_HUB_OVER_CURRENT or C_HUB_LOCAL_POWER\n");
- break;
- default:
- goto error;
- }
- break;
- case GetHubDescriptor:
- DBG(0, "GetHubDescriptor\n");
- isp1362_hub_descriptor(isp1362_hcd, (struct usb_hub_descriptor *)buf);
- break;
- case GetHubStatus:
- DBG(0, "GetHubStatus\n");
- put_unaligned(cpu_to_le32(0), (__le32 *) buf);
- break;
- case GetPortStatus:
-#ifndef VERBOSE
- DBG(0, "GetPortStatus\n");
-#endif
- if (!wIndex || wIndex > ports)
- goto error;
- tmp = isp1362_hcd->rhport[--wIndex];
- put_unaligned(cpu_to_le32(tmp), (__le32 *) buf);
- break;
- case ClearPortFeature:
- DBG(0, "ClearPortFeature: ");
- if (!wIndex || wIndex > ports)
- goto error;
- wIndex--;
-
- switch (wValue) {
- case USB_PORT_FEAT_ENABLE:
- DBG(0, "USB_PORT_FEAT_ENABLE\n");
- tmp = RH_PS_CCS;
- break;
- case USB_PORT_FEAT_C_ENABLE:
- DBG(0, "USB_PORT_FEAT_C_ENABLE\n");
- tmp = RH_PS_PESC;
- break;
- case USB_PORT_FEAT_SUSPEND:
- DBG(0, "USB_PORT_FEAT_SUSPEND\n");
- tmp = RH_PS_POCI;
- break;
- case USB_PORT_FEAT_C_SUSPEND:
- DBG(0, "USB_PORT_FEAT_C_SUSPEND\n");
- tmp = RH_PS_PSSC;
- break;
- case USB_PORT_FEAT_POWER:
- DBG(0, "USB_PORT_FEAT_POWER\n");
- tmp = RH_PS_LSDA;
-
- break;
- case USB_PORT_FEAT_C_CONNECTION:
- DBG(0, "USB_PORT_FEAT_C_CONNECTION\n");
- tmp = RH_PS_CSC;
- break;
- case USB_PORT_FEAT_C_OVER_CURRENT:
- DBG(0, "USB_PORT_FEAT_C_OVER_CURRENT\n");
- tmp = RH_PS_OCIC;
- break;
- case USB_PORT_FEAT_C_RESET:
- DBG(0, "USB_PORT_FEAT_C_RESET\n");
- tmp = RH_PS_PRSC;
- break;
- default:
- goto error;
- }
-
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
- isp1362_write_reg32(isp1362_hcd, HCRHPORT1 + wIndex, tmp);
- isp1362_hcd->rhport[wIndex] =
- isp1362_read_reg32(isp1362_hcd, HCRHPORT1 + wIndex);
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
- break;
- case SetPortFeature:
- DBG(0, "SetPortFeature: ");
- if (!wIndex || wIndex > ports)
- goto error;
- wIndex--;
- switch (wValue) {
- case USB_PORT_FEAT_SUSPEND:
- DBG(0, "USB_PORT_FEAT_SUSPEND\n");
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
- isp1362_write_reg32(isp1362_hcd, HCRHPORT1 + wIndex, RH_PS_PSS);
- isp1362_hcd->rhport[wIndex] =
- isp1362_read_reg32(isp1362_hcd, HCRHPORT1 + wIndex);
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
- break;
- case USB_PORT_FEAT_POWER:
- DBG(0, "USB_PORT_FEAT_POWER\n");
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
- isp1362_write_reg32(isp1362_hcd, HCRHPORT1 + wIndex, RH_PS_PPS);
- isp1362_hcd->rhport[wIndex] =
- isp1362_read_reg32(isp1362_hcd, HCRHPORT1 + wIndex);
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
- break;
- case USB_PORT_FEAT_RESET:
- DBG(0, "USB_PORT_FEAT_RESET\n");
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
-
- t1 = jiffies + msecs_to_jiffies(USB_RESET_WIDTH);
- while (time_before(jiffies, t1)) {
- /* spin until any current reset finishes */
- for (;;) {
- tmp = isp1362_read_reg32(isp1362_hcd, HCRHPORT1 + wIndex);
- if (!(tmp & RH_PS_PRS))
- break;
- udelay(500);
- }
- if (!(tmp & RH_PS_CCS))
- break;
- /* Reset lasts 10ms (claims datasheet) */
- isp1362_write_reg32(isp1362_hcd, HCRHPORT1 + wIndex, (RH_PS_PRS));
-
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
- msleep(10);
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
- }
-
- isp1362_hcd->rhport[wIndex] = isp1362_read_reg32(isp1362_hcd,
- HCRHPORT1 + wIndex);
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
- break;
- default:
- goto error;
- }
- break;
-
- default:
- error:
- /* "protocol stall" on error */
- DBG(0, "PROTOCOL STALL\n");
- retval = -EPIPE;
- }
-
- return retval;
-}
-
-#ifdef CONFIG_PM
-static int isp1362_bus_suspend(struct usb_hcd *hcd)
-{
- int status = 0;
- struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
- unsigned long flags;
-
- if (time_before(jiffies, isp1362_hcd->next_statechange))
- msleep(5);
-
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
-
- isp1362_hcd->hc_control = isp1362_read_reg32(isp1362_hcd, HCCONTROL);
- switch (isp1362_hcd->hc_control & OHCI_CTRL_HCFS) {
- case OHCI_USB_RESUME:
- DBG(0, "%s: resume/suspend?\n", __func__);
- isp1362_hcd->hc_control &= ~OHCI_CTRL_HCFS;
- isp1362_hcd->hc_control |= OHCI_USB_RESET;
- isp1362_write_reg32(isp1362_hcd, HCCONTROL, isp1362_hcd->hc_control);
- fallthrough;
- case OHCI_USB_RESET:
- status = -EBUSY;
- pr_warn("%s: needs reinit!\n", __func__);
- goto done;
- case OHCI_USB_SUSPEND:
- pr_warn("%s: already suspended?\n", __func__);
- goto done;
- }
- DBG(0, "%s: suspend root hub\n", __func__);
-
- /* First stop any processing */
- hcd->state = HC_STATE_QUIESCING;
- if (!list_empty(&isp1362_hcd->atl_queue.active) ||
- !list_empty(&isp1362_hcd->intl_queue.active) ||
- !list_empty(&isp1362_hcd->istl_queue[0] .active) ||
- !list_empty(&isp1362_hcd->istl_queue[1] .active)) {
- int limit;
-
- isp1362_write_reg32(isp1362_hcd, HCATLSKIP, ~0);
- isp1362_write_reg32(isp1362_hcd, HCINTLSKIP, ~0);
- isp1362_write_reg16(isp1362_hcd, HCBUFSTAT, 0);
- isp1362_write_reg16(isp1362_hcd, HCuPINTENB, 0);
- isp1362_write_reg32(isp1362_hcd, HCINTSTAT, OHCI_INTR_SF);
-
- DBG(0, "%s: stopping schedules ...\n", __func__);
- limit = 2000;
- while (limit > 0) {
- udelay(250);
- limit -= 250;
- if (isp1362_read_reg32(isp1362_hcd, HCINTSTAT) & OHCI_INTR_SF)
- break;
- }
- mdelay(7);
- if (isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_ATL) {
- u32 done_map = isp1362_read_reg32(isp1362_hcd, HCATLDONE);
- finish_transfers(isp1362_hcd, done_map, &isp1362_hcd->atl_queue);
- }
- if (isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_INTL) {
- u32 done_map = isp1362_read_reg32(isp1362_hcd, HCINTLDONE);
- finish_transfers(isp1362_hcd, done_map, &isp1362_hcd->intl_queue);
- }
- if (isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_ISTL0)
- finish_iso_transfers(isp1362_hcd, &isp1362_hcd->istl_queue[0]);
- if (isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_ISTL1)
- finish_iso_transfers(isp1362_hcd, &isp1362_hcd->istl_queue[1]);
- }
- DBG(0, "%s: HCINTSTAT: %08x\n", __func__,
- isp1362_read_reg32(isp1362_hcd, HCINTSTAT));
- isp1362_write_reg32(isp1362_hcd, HCINTSTAT,
- isp1362_read_reg32(isp1362_hcd, HCINTSTAT));
-
- /* Suspend hub */
- isp1362_hcd->hc_control = OHCI_USB_SUSPEND;
- isp1362_show_reg(isp1362_hcd, HCCONTROL);
- isp1362_write_reg32(isp1362_hcd, HCCONTROL, isp1362_hcd->hc_control);
- isp1362_show_reg(isp1362_hcd, HCCONTROL);
-
-#if 1
- isp1362_hcd->hc_control = isp1362_read_reg32(isp1362_hcd, HCCONTROL);
- if ((isp1362_hcd->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_SUSPEND) {
- pr_err("%s: controller won't suspend %08x\n", __func__,
- isp1362_hcd->hc_control);
- status = -EBUSY;
- } else
-#endif
- {
- /* no resumes until devices finish suspending */
- isp1362_hcd->next_statechange = jiffies + msecs_to_jiffies(5);
- }
-done:
- if (status == 0) {
- hcd->state = HC_STATE_SUSPENDED;
- DBG(0, "%s: HCD suspended: %08x\n", __func__,
- isp1362_read_reg32(isp1362_hcd, HCCONTROL));
- }
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
- return status;
-}
-
-static int isp1362_bus_resume(struct usb_hcd *hcd)
-{
- struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
- u32 port;
- unsigned long flags;
- int status = -EINPROGRESS;
-
- if (time_before(jiffies, isp1362_hcd->next_statechange))
- msleep(5);
-
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
- isp1362_hcd->hc_control = isp1362_read_reg32(isp1362_hcd, HCCONTROL);
- pr_info("%s: HCCONTROL: %08x\n", __func__, isp1362_hcd->hc_control);
- if (hcd->state == HC_STATE_RESUMING) {
- pr_warn("%s: duplicate resume\n", __func__);
- status = 0;
- } else
- switch (isp1362_hcd->hc_control & OHCI_CTRL_HCFS) {
- case OHCI_USB_SUSPEND:
- DBG(0, "%s: resume root hub\n", __func__);
- isp1362_hcd->hc_control &= ~OHCI_CTRL_HCFS;
- isp1362_hcd->hc_control |= OHCI_USB_RESUME;
- isp1362_write_reg32(isp1362_hcd, HCCONTROL, isp1362_hcd->hc_control);
- break;
- case OHCI_USB_RESUME:
- /* HCFS changes sometime after INTR_RD */
- DBG(0, "%s: remote wakeup\n", __func__);
- break;
- case OHCI_USB_OPER:
- DBG(0, "%s: odd resume\n", __func__);
- status = 0;
- hcd->self.root_hub->dev.power.power_state = PMSG_ON;
- break;
- default: /* RESET, we lost power */
- DBG(0, "%s: root hub hardware reset\n", __func__);
- status = -EBUSY;
- }
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
- if (status == -EBUSY) {
- DBG(0, "%s: Restarting HC\n", __func__);
- isp1362_hc_stop(hcd);
- return isp1362_hc_start(hcd);
- }
- if (status != -EINPROGRESS)
- return status;
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
- port = isp1362_read_reg32(isp1362_hcd, HCRHDESCA) & RH_A_NDP;
- while (port--) {
- u32 stat = isp1362_read_reg32(isp1362_hcd, HCRHPORT1 + port);
-
- /* force global, not selective, resume */
- if (!(stat & RH_PS_PSS)) {
- DBG(0, "%s: Not Resuming RH port %d\n", __func__, port);
- continue;
- }
- DBG(0, "%s: Resuming RH port %d\n", __func__, port);
- isp1362_write_reg32(isp1362_hcd, HCRHPORT1 + port, RH_PS_POCI);
- }
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
-
- /* Some controllers (lucent) need extra-long delays */
- hcd->state = HC_STATE_RESUMING;
- mdelay(20 /* usb 11.5.1.10 */ + 15);
-
- isp1362_hcd->hc_control = OHCI_USB_OPER;
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
- isp1362_show_reg(isp1362_hcd, HCCONTROL);
- isp1362_write_reg32(isp1362_hcd, HCCONTROL, isp1362_hcd->hc_control);
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
- /* TRSMRCY */
- msleep(10);
-
- /* keep it alive for ~5x suspend + resume costs */
- isp1362_hcd->next_statechange = jiffies + msecs_to_jiffies(250);
-
- hcd->self.root_hub->dev.power.power_state = PMSG_ON;
- hcd->state = HC_STATE_RUNNING;
- return 0;
-}
-#else
-#define isp1362_bus_suspend NULL
-#define isp1362_bus_resume NULL
-#endif
-
-/*-------------------------------------------------------------------------*/
-
-static void dump_irq(struct seq_file *s, char *label, u16 mask)
-{
- seq_printf(s, "%-15s %04x%s%s%s%s%s%s\n", label, mask,
- mask & HCuPINT_CLKRDY ? " clkrdy" : "",
- mask & HCuPINT_SUSP ? " susp" : "",
- mask & HCuPINT_OPR ? " opr" : "",
- mask & HCuPINT_EOT ? " eot" : "",
- mask & HCuPINT_ATL ? " atl" : "",
- mask & HCuPINT_SOF ? " sof" : "");
-}
-
-static void dump_int(struct seq_file *s, char *label, u32 mask)
-{
- seq_printf(s, "%-15s %08x%s%s%s%s%s%s%s\n", label, mask,
- mask & OHCI_INTR_MIE ? " MIE" : "",
- mask & OHCI_INTR_RHSC ? " rhsc" : "",
- mask & OHCI_INTR_FNO ? " fno" : "",
- mask & OHCI_INTR_UE ? " ue" : "",
- mask & OHCI_INTR_RD ? " rd" : "",
- mask & OHCI_INTR_SF ? " sof" : "",
- mask & OHCI_INTR_SO ? " so" : "");
-}
-
-static void dump_ctrl(struct seq_file *s, char *label, u32 mask)
-{
- seq_printf(s, "%-15s %08x%s%s%s\n", label, mask,
- mask & OHCI_CTRL_RWC ? " rwc" : "",
- mask & OHCI_CTRL_RWE ? " rwe" : "",
- ({
- char *hcfs;
- switch (mask & OHCI_CTRL_HCFS) {
- case OHCI_USB_OPER:
- hcfs = " oper";
- break;
- case OHCI_USB_RESET:
- hcfs = " reset";
- break;
- case OHCI_USB_RESUME:
- hcfs = " resume";
- break;
- case OHCI_USB_SUSPEND:
- hcfs = " suspend";
- break;
- default:
- hcfs = " ?";
- }
- hcfs;
- }));
-}
-
-static void dump_regs(struct seq_file *s, struct isp1362_hcd *isp1362_hcd)
-{
- seq_printf(s, "HCREVISION [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCREVISION),
- isp1362_read_reg32(isp1362_hcd, HCREVISION));
- seq_printf(s, "HCCONTROL [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCCONTROL),
- isp1362_read_reg32(isp1362_hcd, HCCONTROL));
- seq_printf(s, "HCCMDSTAT [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCCMDSTAT),
- isp1362_read_reg32(isp1362_hcd, HCCMDSTAT));
- seq_printf(s, "HCINTSTAT [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCINTSTAT),
- isp1362_read_reg32(isp1362_hcd, HCINTSTAT));
- seq_printf(s, "HCINTENB [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCINTENB),
- isp1362_read_reg32(isp1362_hcd, HCINTENB));
- seq_printf(s, "HCFMINTVL [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCFMINTVL),
- isp1362_read_reg32(isp1362_hcd, HCFMINTVL));
- seq_printf(s, "HCFMREM [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCFMREM),
- isp1362_read_reg32(isp1362_hcd, HCFMREM));
- seq_printf(s, "HCFMNUM [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCFMNUM),
- isp1362_read_reg32(isp1362_hcd, HCFMNUM));
- seq_printf(s, "HCLSTHRESH [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCLSTHRESH),
- isp1362_read_reg32(isp1362_hcd, HCLSTHRESH));
- seq_printf(s, "HCRHDESCA [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCRHDESCA),
- isp1362_read_reg32(isp1362_hcd, HCRHDESCA));
- seq_printf(s, "HCRHDESCB [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCRHDESCB),
- isp1362_read_reg32(isp1362_hcd, HCRHDESCB));
- seq_printf(s, "HCRHSTATUS [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCRHSTATUS),
- isp1362_read_reg32(isp1362_hcd, HCRHSTATUS));
- seq_printf(s, "HCRHPORT1 [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCRHPORT1),
- isp1362_read_reg32(isp1362_hcd, HCRHPORT1));
- seq_printf(s, "HCRHPORT2 [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCRHPORT2),
- isp1362_read_reg32(isp1362_hcd, HCRHPORT2));
- seq_printf(s, "\n");
- seq_printf(s, "HCHWCFG [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCHWCFG),
- isp1362_read_reg16(isp1362_hcd, HCHWCFG));
- seq_printf(s, "HCDMACFG [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCDMACFG),
- isp1362_read_reg16(isp1362_hcd, HCDMACFG));
- seq_printf(s, "HCXFERCTR [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCXFERCTR),
- isp1362_read_reg16(isp1362_hcd, HCXFERCTR));
- seq_printf(s, "HCuPINT [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCuPINT),
- isp1362_read_reg16(isp1362_hcd, HCuPINT));
- seq_printf(s, "HCuPINTENB [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCuPINTENB),
- isp1362_read_reg16(isp1362_hcd, HCuPINTENB));
- seq_printf(s, "HCCHIPID [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCCHIPID),
- isp1362_read_reg16(isp1362_hcd, HCCHIPID));
- seq_printf(s, "HCSCRATCH [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCSCRATCH),
- isp1362_read_reg16(isp1362_hcd, HCSCRATCH));
- seq_printf(s, "HCBUFSTAT [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCBUFSTAT),
- isp1362_read_reg16(isp1362_hcd, HCBUFSTAT));
- seq_printf(s, "HCDIRADDR [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCDIRADDR),
- isp1362_read_reg32(isp1362_hcd, HCDIRADDR));
-#if 0
- seq_printf(s, "HCDIRDATA [%02x] %04x\n", ISP1362_REG_NO(HCDIRDATA),
- isp1362_read_reg16(isp1362_hcd, HCDIRDATA));
-#endif
- seq_printf(s, "HCISTLBUFSZ[%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCISTLBUFSZ),
- isp1362_read_reg16(isp1362_hcd, HCISTLBUFSZ));
- seq_printf(s, "HCISTLRATE [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCISTLRATE),
- isp1362_read_reg16(isp1362_hcd, HCISTLRATE));
- seq_printf(s, "\n");
- seq_printf(s, "HCINTLBUFSZ[%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCINTLBUFSZ),
- isp1362_read_reg16(isp1362_hcd, HCINTLBUFSZ));
- seq_printf(s, "HCINTLBLKSZ[%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCINTLBLKSZ),
- isp1362_read_reg16(isp1362_hcd, HCINTLBLKSZ));
- seq_printf(s, "HCINTLDONE [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCINTLDONE),
- isp1362_read_reg32(isp1362_hcd, HCINTLDONE));
- seq_printf(s, "HCINTLSKIP [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCINTLSKIP),
- isp1362_read_reg32(isp1362_hcd, HCINTLSKIP));
- seq_printf(s, "HCINTLLAST [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCINTLLAST),
- isp1362_read_reg32(isp1362_hcd, HCINTLLAST));
- seq_printf(s, "HCINTLCURR [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCINTLCURR),
- isp1362_read_reg16(isp1362_hcd, HCINTLCURR));
- seq_printf(s, "\n");
- seq_printf(s, "HCATLBUFSZ [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCATLBUFSZ),
- isp1362_read_reg16(isp1362_hcd, HCATLBUFSZ));
- seq_printf(s, "HCATLBLKSZ [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCATLBLKSZ),
- isp1362_read_reg16(isp1362_hcd, HCATLBLKSZ));
-#if 0
- seq_printf(s, "HCATLDONE [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCATLDONE),
- isp1362_read_reg32(isp1362_hcd, HCATLDONE));
-#endif
- seq_printf(s, "HCATLSKIP [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCATLSKIP),
- isp1362_read_reg32(isp1362_hcd, HCATLSKIP));
- seq_printf(s, "HCATLLAST [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCATLLAST),
- isp1362_read_reg32(isp1362_hcd, HCATLLAST));
- seq_printf(s, "HCATLCURR [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCATLCURR),
- isp1362_read_reg16(isp1362_hcd, HCATLCURR));
- seq_printf(s, "\n");
- seq_printf(s, "HCATLDTC [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCATLDTC),
- isp1362_read_reg16(isp1362_hcd, HCATLDTC));
- seq_printf(s, "HCATLDTCTO [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCATLDTCTO),
- isp1362_read_reg16(isp1362_hcd, HCATLDTCTO));
-}
-
-static int isp1362_show(struct seq_file *s, void *unused)
-{
- struct isp1362_hcd *isp1362_hcd = s->private;
- struct isp1362_ep *ep;
- int i;
-
- seq_printf(s, "%s\n%s version %s\n",
- isp1362_hcd_to_hcd(isp1362_hcd)->product_desc, hcd_name, DRIVER_VERSION);
-
- /* collect statistics to help estimate potential win for
- * DMA engines that care about alignment (PXA)
- */
- seq_printf(s, "alignment: 16b/%ld 8b/%ld 4b/%ld 2b/%ld 1b/%ld\n",
- isp1362_hcd->stat16, isp1362_hcd->stat8, isp1362_hcd->stat4,
- isp1362_hcd->stat2, isp1362_hcd->stat1);
- seq_printf(s, "max # ptds in ATL fifo: %d\n", isp1362_hcd->atl_queue.stat_maxptds);
- seq_printf(s, "max # ptds in INTL fifo: %d\n", isp1362_hcd->intl_queue.stat_maxptds);
- seq_printf(s, "max # ptds in ISTL fifo: %d\n",
- max(isp1362_hcd->istl_queue[0] .stat_maxptds,
- isp1362_hcd->istl_queue[1] .stat_maxptds));
-
- /* FIXME: don't show the following in suspended state */
- spin_lock_irq(&isp1362_hcd->lock);
-
- dump_irq(s, "hc_irq_enable", isp1362_read_reg16(isp1362_hcd, HCuPINTENB));
- dump_irq(s, "hc_irq_status", isp1362_read_reg16(isp1362_hcd, HCuPINT));
- dump_int(s, "ohci_int_enable", isp1362_read_reg32(isp1362_hcd, HCINTENB));
- dump_int(s, "ohci_int_status", isp1362_read_reg32(isp1362_hcd, HCINTSTAT));
- dump_ctrl(s, "ohci_control", isp1362_read_reg32(isp1362_hcd, HCCONTROL));
-
- for (i = 0; i < NUM_ISP1362_IRQS; i++)
- if (isp1362_hcd->irq_stat[i])
- seq_printf(s, "%-15s: %d\n",
- ISP1362_INT_NAME(i), isp1362_hcd->irq_stat[i]);
-
- dump_regs(s, isp1362_hcd);
- list_for_each_entry(ep, &isp1362_hcd->async, schedule) {
- struct urb *urb;
-
- seq_printf(s, "%p, ep%d%s, maxpacket %d:\n", ep, ep->epnum,
- ({
- char *s;
- switch (ep->nextpid) {
- case USB_PID_IN:
- s = "in";
- break;
- case USB_PID_OUT:
- s = "out";
- break;
- case USB_PID_SETUP:
- s = "setup";
- break;
- case USB_PID_ACK:
- s = "status";
- break;
- default:
- s = "?";
- break;
- }
- s;}), ep->maxpacket) ;
- list_for_each_entry(urb, &ep->hep->urb_list, urb_list) {
- seq_printf(s, " urb%p, %d/%d\n", urb,
- urb->actual_length,
- urb->transfer_buffer_length);
- }
- }
- if (!list_empty(&isp1362_hcd->async))
- seq_printf(s, "\n");
- dump_ptd_queue(&isp1362_hcd->atl_queue);
-
- seq_printf(s, "periodic size= %d\n", PERIODIC_SIZE);
-
- list_for_each_entry(ep, &isp1362_hcd->periodic, schedule) {
- seq_printf(s, "branch:%2d load:%3d PTD[%d] $%04x:\n", ep->branch,
- isp1362_hcd->load[ep->branch], ep->ptd_index, ep->ptd_offset);
-
- seq_printf(s, " %d/%p (%sdev%d ep%d%s max %d)\n",
- ep->interval, ep,
- (ep->udev->speed == USB_SPEED_FULL) ? "" : "ls ",
- ep->udev->devnum, ep->epnum,
- (ep->epnum == 0) ? "" :
- ((ep->nextpid == USB_PID_IN) ?
- "in" : "out"), ep->maxpacket);
- }
- dump_ptd_queue(&isp1362_hcd->intl_queue);
-
- seq_printf(s, "ISO:\n");
-
- list_for_each_entry(ep, &isp1362_hcd->isoc, schedule) {
- seq_printf(s, " %d/%p (%sdev%d ep%d%s max %d)\n",
- ep->interval, ep,
- (ep->udev->speed == USB_SPEED_FULL) ? "" : "ls ",
- ep->udev->devnum, ep->epnum,
- (ep->epnum == 0) ? "" :
- ((ep->nextpid == USB_PID_IN) ?
- "in" : "out"), ep->maxpacket);
- }
-
- spin_unlock_irq(&isp1362_hcd->lock);
- seq_printf(s, "\n");
-
- return 0;
-}
-DEFINE_SHOW_ATTRIBUTE(isp1362);
-
-/* expect just one isp1362_hcd per system */
-static void create_debug_file(struct isp1362_hcd *isp1362_hcd)
-{
- debugfs_create_file("isp1362", S_IRUGO, usb_debug_root, isp1362_hcd,
- &isp1362_fops);
-}
-
-static void remove_debug_file(struct isp1362_hcd *isp1362_hcd)
-{
- debugfs_lookup_and_remove("isp1362", usb_debug_root);
-}
-
-/*-------------------------------------------------------------------------*/
-
-static void __isp1362_sw_reset(struct isp1362_hcd *isp1362_hcd)
-{
- int tmp = 20;
-
- isp1362_write_reg16(isp1362_hcd, HCSWRES, HCSWRES_MAGIC);
- isp1362_write_reg32(isp1362_hcd, HCCMDSTAT, OHCI_HCR);
- while (--tmp) {
- mdelay(1);
- if (!(isp1362_read_reg32(isp1362_hcd, HCCMDSTAT) & OHCI_HCR))
- break;
- }
- if (!tmp)
- pr_err("Software reset timeout\n");
-}
-
-static void isp1362_sw_reset(struct isp1362_hcd *isp1362_hcd)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
- __isp1362_sw_reset(isp1362_hcd);
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
-}
-
-static int isp1362_mem_config(struct usb_hcd *hcd)
-{
- struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
- unsigned long flags;
- u32 total;
- u16 istl_size = ISP1362_ISTL_BUFSIZE;
- u16 intl_blksize = ISP1362_INTL_BLKSIZE + PTD_HEADER_SIZE;
- u16 intl_size = ISP1362_INTL_BUFFERS * intl_blksize;
- u16 atl_blksize = ISP1362_ATL_BLKSIZE + PTD_HEADER_SIZE;
- u16 atl_buffers = (ISP1362_BUF_SIZE - (istl_size + intl_size)) / atl_blksize;
- u16 atl_size;
- int i;
-
- WARN_ON(istl_size & 3);
- WARN_ON(atl_blksize & 3);
- WARN_ON(intl_blksize & 3);
- WARN_ON(atl_blksize < PTD_HEADER_SIZE);
- WARN_ON(intl_blksize < PTD_HEADER_SIZE);
-
- BUG_ON((unsigned)ISP1362_INTL_BUFFERS > 32);
- if (atl_buffers > 32)
- atl_buffers = 32;
- atl_size = atl_buffers * atl_blksize;
- total = atl_size + intl_size + istl_size;
- dev_info(hcd->self.controller, "ISP1362 Memory usage:\n");
- dev_info(hcd->self.controller, " ISTL: 2 * %4d: %4d @ $%04x:$%04x\n",
- istl_size / 2, istl_size, 0, istl_size / 2);
- dev_info(hcd->self.controller, " INTL: %4d * (%3zu+8): %4d @ $%04x\n",
- ISP1362_INTL_BUFFERS, intl_blksize - PTD_HEADER_SIZE,
- intl_size, istl_size);
- dev_info(hcd->self.controller, " ATL : %4d * (%3zu+8): %4d @ $%04x\n",
- atl_buffers, atl_blksize - PTD_HEADER_SIZE,
- atl_size, istl_size + intl_size);
- dev_info(hcd->self.controller, " USED/FREE: %4d %4d\n", total,
- ISP1362_BUF_SIZE - total);
-
- if (total > ISP1362_BUF_SIZE) {
- dev_err(hcd->self.controller, "%s: Memory requested: %d, available %d\n",
- __func__, total, ISP1362_BUF_SIZE);
- return -ENOMEM;
- }
-
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
-
- for (i = 0; i < 2; i++) {
- isp1362_hcd->istl_queue[i].buf_start = i * istl_size / 2,
- isp1362_hcd->istl_queue[i].buf_size = istl_size / 2;
- isp1362_hcd->istl_queue[i].blk_size = 4;
- INIT_LIST_HEAD(&isp1362_hcd->istl_queue[i].active);
- snprintf(isp1362_hcd->istl_queue[i].name,
- sizeof(isp1362_hcd->istl_queue[i].name), "ISTL%d", i);
- DBG(3, "%s: %5s buf $%04x %d\n", __func__,
- isp1362_hcd->istl_queue[i].name,
- isp1362_hcd->istl_queue[i].buf_start,
- isp1362_hcd->istl_queue[i].buf_size);
- }
- isp1362_write_reg16(isp1362_hcd, HCISTLBUFSZ, istl_size / 2);
-
- isp1362_hcd->intl_queue.buf_start = istl_size;
- isp1362_hcd->intl_queue.buf_size = intl_size;
- isp1362_hcd->intl_queue.buf_count = ISP1362_INTL_BUFFERS;
- isp1362_hcd->intl_queue.blk_size = intl_blksize;
- isp1362_hcd->intl_queue.buf_avail = isp1362_hcd->intl_queue.buf_count;
- isp1362_hcd->intl_queue.skip_map = ~0;
- INIT_LIST_HEAD(&isp1362_hcd->intl_queue.active);
-
- isp1362_write_reg16(isp1362_hcd, HCINTLBUFSZ,
- isp1362_hcd->intl_queue.buf_size);
- isp1362_write_reg16(isp1362_hcd, HCINTLBLKSZ,
- isp1362_hcd->intl_queue.blk_size - PTD_HEADER_SIZE);
- isp1362_write_reg32(isp1362_hcd, HCINTLSKIP, ~0);
- isp1362_write_reg32(isp1362_hcd, HCINTLLAST,
- 1 << (ISP1362_INTL_BUFFERS - 1));
-
- isp1362_hcd->atl_queue.buf_start = istl_size + intl_size;
- isp1362_hcd->atl_queue.buf_size = atl_size;
- isp1362_hcd->atl_queue.buf_count = atl_buffers;
- isp1362_hcd->atl_queue.blk_size = atl_blksize;
- isp1362_hcd->atl_queue.buf_avail = isp1362_hcd->atl_queue.buf_count;
- isp1362_hcd->atl_queue.skip_map = ~0;
- INIT_LIST_HEAD(&isp1362_hcd->atl_queue.active);
-
- isp1362_write_reg16(isp1362_hcd, HCATLBUFSZ,
- isp1362_hcd->atl_queue.buf_size);
- isp1362_write_reg16(isp1362_hcd, HCATLBLKSZ,
- isp1362_hcd->atl_queue.blk_size - PTD_HEADER_SIZE);
- isp1362_write_reg32(isp1362_hcd, HCATLSKIP, ~0);
- isp1362_write_reg32(isp1362_hcd, HCATLLAST,
- 1 << (atl_buffers - 1));
-
- snprintf(isp1362_hcd->atl_queue.name,
- sizeof(isp1362_hcd->atl_queue.name), "ATL");
- snprintf(isp1362_hcd->intl_queue.name,
- sizeof(isp1362_hcd->intl_queue.name), "INTL");
- DBG(3, "%s: %5s buf $%04x %2d * %4d = %4d\n", __func__,
- isp1362_hcd->intl_queue.name,
- isp1362_hcd->intl_queue.buf_start,
- ISP1362_INTL_BUFFERS, isp1362_hcd->intl_queue.blk_size,
- isp1362_hcd->intl_queue.buf_size);
- DBG(3, "%s: %5s buf $%04x %2d * %4d = %4d\n", __func__,
- isp1362_hcd->atl_queue.name,
- isp1362_hcd->atl_queue.buf_start,
- atl_buffers, isp1362_hcd->atl_queue.blk_size,
- isp1362_hcd->atl_queue.buf_size);
-
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
-
- return 0;
-}
-
-static int isp1362_hc_reset(struct usb_hcd *hcd)
-{
- int ret = 0;
- struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
- unsigned long t;
- unsigned long timeout = 100;
- unsigned long flags;
- int clkrdy = 0;
-
- pr_debug("%s:\n", __func__);
-
- if (isp1362_hcd->board && isp1362_hcd->board->reset) {
- isp1362_hcd->board->reset(hcd->self.controller, 1);
- msleep(20);
- if (isp1362_hcd->board->clock)
- isp1362_hcd->board->clock(hcd->self.controller, 1);
- isp1362_hcd->board->reset(hcd->self.controller, 0);
- } else
- isp1362_sw_reset(isp1362_hcd);
-
- /* chip has been reset. First we need to see a clock */
- t = jiffies + msecs_to_jiffies(timeout);
- while (!clkrdy && time_before_eq(jiffies, t)) {
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
- clkrdy = isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_CLKRDY;
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
- if (!clkrdy)
- msleep(4);
- }
-
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
- isp1362_write_reg16(isp1362_hcd, HCuPINT, HCuPINT_CLKRDY);
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
- if (!clkrdy) {
- pr_err("Clock not ready after %lums\n", timeout);
- ret = -ENODEV;
- }
- return ret;
-}
-
-static void isp1362_hc_stop(struct usb_hcd *hcd)
-{
- struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
- unsigned long flags;
- u32 tmp;
-
- pr_debug("%s:\n", __func__);
-
- del_timer_sync(&hcd->rh_timer);
-
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
-
- isp1362_write_reg16(isp1362_hcd, HCuPINTENB, 0);
-
- /* Switch off power for all ports */
- tmp = isp1362_read_reg32(isp1362_hcd, HCRHDESCA);
- tmp &= ~(RH_A_NPS | RH_A_PSM);
- isp1362_write_reg32(isp1362_hcd, HCRHDESCA, tmp);
- isp1362_write_reg32(isp1362_hcd, HCRHSTATUS, RH_HS_LPS);
-
- /* Reset the chip */
- if (isp1362_hcd->board && isp1362_hcd->board->reset)
- isp1362_hcd->board->reset(hcd->self.controller, 1);
- else
- __isp1362_sw_reset(isp1362_hcd);
-
- if (isp1362_hcd->board && isp1362_hcd->board->clock)
- isp1362_hcd->board->clock(hcd->self.controller, 0);
-
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
-}
-
-#ifdef CHIP_BUFFER_TEST
-static int isp1362_chip_test(struct isp1362_hcd *isp1362_hcd)
-{
- int ret = 0;
- u16 *ref;
- unsigned long flags;
-
- ref = kmalloc(2 * ISP1362_BUF_SIZE, GFP_KERNEL);
- if (ref) {
- int offset;
- u16 *tst = &ref[ISP1362_BUF_SIZE / 2];
-
- for (offset = 0; offset < ISP1362_BUF_SIZE / 2; offset++) {
- ref[offset] = ~offset;
- tst[offset] = offset;
- }
-
- for (offset = 0; offset < 4; offset++) {
- int j;
-
- for (j = 0; j < 8; j++) {
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
- isp1362_write_buffer(isp1362_hcd, (u8 *)ref + offset, 0, j);
- isp1362_read_buffer(isp1362_hcd, (u8 *)tst + offset, 0, j);
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
-
- if (memcmp(ref, tst, j)) {
- ret = -ENODEV;
- pr_err("%s: memory check with %d byte offset %d failed\n",
- __func__, j, offset);
- dump_data((u8 *)ref + offset, j);
- dump_data((u8 *)tst + offset, j);
- }
- }
- }
-
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
- isp1362_write_buffer(isp1362_hcd, ref, 0, ISP1362_BUF_SIZE);
- isp1362_read_buffer(isp1362_hcd, tst, 0, ISP1362_BUF_SIZE);
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
-
- if (memcmp(ref, tst, ISP1362_BUF_SIZE)) {
- ret = -ENODEV;
- pr_err("%s: memory check failed\n", __func__);
- dump_data((u8 *)tst, ISP1362_BUF_SIZE / 2);
- }
-
- for (offset = 0; offset < 256; offset++) {
- int test_size = 0;
-
- yield();
-
- memset(tst, 0, ISP1362_BUF_SIZE);
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
- isp1362_write_buffer(isp1362_hcd, tst, 0, ISP1362_BUF_SIZE);
- isp1362_read_buffer(isp1362_hcd, tst, 0, ISP1362_BUF_SIZE);
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
- if (memcmp(tst, tst + (ISP1362_BUF_SIZE / (2 * sizeof(*tst))),
- ISP1362_BUF_SIZE / 2)) {
- pr_err("%s: Failed to clear buffer\n", __func__);
- dump_data((u8 *)tst, ISP1362_BUF_SIZE);
- break;
- }
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
- isp1362_write_buffer(isp1362_hcd, ref, offset * 2, PTD_HEADER_SIZE);
- isp1362_write_buffer(isp1362_hcd, ref + PTD_HEADER_SIZE / sizeof(*ref),
- offset * 2 + PTD_HEADER_SIZE, test_size);
- isp1362_read_buffer(isp1362_hcd, tst, offset * 2,
- PTD_HEADER_SIZE + test_size);
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
- if (memcmp(ref, tst, PTD_HEADER_SIZE + test_size)) {
- dump_data(((u8 *)ref) + offset, PTD_HEADER_SIZE + test_size);
- dump_data((u8 *)tst, PTD_HEADER_SIZE + test_size);
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
- isp1362_read_buffer(isp1362_hcd, tst, offset * 2,
- PTD_HEADER_SIZE + test_size);
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
- if (memcmp(ref, tst, PTD_HEADER_SIZE + test_size)) {
- ret = -ENODEV;
- pr_err("%s: memory check with offset %02x failed\n",
- __func__, offset);
- break;
- }
- pr_warn("%s: memory check with offset %02x ok after second read\n",
- __func__, offset);
- }
- }
- kfree(ref);
- }
- return ret;
-}
-#endif
-
-static int isp1362_hc_start(struct usb_hcd *hcd)
-{
- int ret;
- struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
- struct isp1362_platform_data *board = isp1362_hcd->board;
- u16 hwcfg;
- u16 chipid;
- unsigned long flags;
-
- pr_debug("%s:\n", __func__);
-
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
- chipid = isp1362_read_reg16(isp1362_hcd, HCCHIPID);
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
-
- if ((chipid & HCCHIPID_MASK) != HCCHIPID_MAGIC) {
- pr_err("%s: Invalid chip ID %04x\n", __func__, chipid);
- return -ENODEV;
- }
-
-#ifdef CHIP_BUFFER_TEST
- ret = isp1362_chip_test(isp1362_hcd);
- if (ret)
- return -ENODEV;
-#endif
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
- /* clear interrupt status and disable all interrupt sources */
- isp1362_write_reg16(isp1362_hcd, HCuPINT, 0xff);
- isp1362_write_reg16(isp1362_hcd, HCuPINTENB, 0);
-
- /* HW conf */
- hwcfg = HCHWCFG_INT_ENABLE | HCHWCFG_DBWIDTH(1);
- if (board->sel15Kres)
- hwcfg |= HCHWCFG_PULLDOWN_DS2 |
- ((MAX_ROOT_PORTS > 1) ? HCHWCFG_PULLDOWN_DS1 : 0);
- if (board->clknotstop)
- hwcfg |= HCHWCFG_CLKNOTSTOP;
- if (board->oc_enable)
- hwcfg |= HCHWCFG_ANALOG_OC;
- if (board->int_act_high)
- hwcfg |= HCHWCFG_INT_POL;
- if (board->int_edge_triggered)
- hwcfg |= HCHWCFG_INT_TRIGGER;
- if (board->dreq_act_high)
- hwcfg |= HCHWCFG_DREQ_POL;
- if (board->dack_act_high)
- hwcfg |= HCHWCFG_DACK_POL;
- isp1362_write_reg16(isp1362_hcd, HCHWCFG, hwcfg);
- isp1362_show_reg(isp1362_hcd, HCHWCFG);
- isp1362_write_reg16(isp1362_hcd, HCDMACFG, 0);
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
-
- ret = isp1362_mem_config(hcd);
- if (ret)
- return ret;
-
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
-
- /* Root hub conf */
- isp1362_hcd->rhdesca = 0;
- if (board->no_power_switching)
- isp1362_hcd->rhdesca |= RH_A_NPS;
- if (board->power_switching_mode)
- isp1362_hcd->rhdesca |= RH_A_PSM;
- if (board->potpg)
- isp1362_hcd->rhdesca |= (board->potpg << 24) & RH_A_POTPGT;
- else
- isp1362_hcd->rhdesca |= (25 << 24) & RH_A_POTPGT;
-
- isp1362_write_reg32(isp1362_hcd, HCRHDESCA, isp1362_hcd->rhdesca & ~RH_A_OCPM);
- isp1362_write_reg32(isp1362_hcd, HCRHDESCA, isp1362_hcd->rhdesca | RH_A_OCPM);
- isp1362_hcd->rhdesca = isp1362_read_reg32(isp1362_hcd, HCRHDESCA);
-
- isp1362_hcd->rhdescb = RH_B_PPCM;
- isp1362_write_reg32(isp1362_hcd, HCRHDESCB, isp1362_hcd->rhdescb);
- isp1362_hcd->rhdescb = isp1362_read_reg32(isp1362_hcd, HCRHDESCB);
-
- isp1362_read_reg32(isp1362_hcd, HCFMINTVL);
- isp1362_write_reg32(isp1362_hcd, HCFMINTVL, (FSMP(FI) << 16) | FI);
- isp1362_write_reg32(isp1362_hcd, HCLSTHRESH, LSTHRESH);
-
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
-
- isp1362_hcd->hc_control = OHCI_USB_OPER;
- hcd->state = HC_STATE_RUNNING;
-
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
- /* Set up interrupts */
- isp1362_hcd->intenb = OHCI_INTR_MIE | OHCI_INTR_RHSC | OHCI_INTR_UE;
- isp1362_hcd->intenb |= OHCI_INTR_RD;
- isp1362_hcd->irqenb = HCuPINT_OPR | HCuPINT_SUSP;
- isp1362_write_reg32(isp1362_hcd, HCINTENB, isp1362_hcd->intenb);
- isp1362_write_reg16(isp1362_hcd, HCuPINTENB, isp1362_hcd->irqenb);
-
- /* Go operational */
- isp1362_write_reg32(isp1362_hcd, HCCONTROL, isp1362_hcd->hc_control);
- /* enable global power */
- isp1362_write_reg32(isp1362_hcd, HCRHSTATUS, RH_HS_LPSC | RH_HS_DRWE);
-
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
-
- return 0;
-}
-
-/*-------------------------------------------------------------------------*/
-
-static const struct hc_driver isp1362_hc_driver = {
- .description = hcd_name,
- .product_desc = "ISP1362 Host Controller",
- .hcd_priv_size = sizeof(struct isp1362_hcd),
-
- .irq = isp1362_irq,
- .flags = HCD_USB11 | HCD_MEMORY,
-
- .reset = isp1362_hc_reset,
- .start = isp1362_hc_start,
- .stop = isp1362_hc_stop,
-
- .urb_enqueue = isp1362_urb_enqueue,
- .urb_dequeue = isp1362_urb_dequeue,
- .endpoint_disable = isp1362_endpoint_disable,
-
- .get_frame_number = isp1362_get_frame,
-
- .hub_status_data = isp1362_hub_status_data,
- .hub_control = isp1362_hub_control,
- .bus_suspend = isp1362_bus_suspend,
- .bus_resume = isp1362_bus_resume,
-};
-
-/*-------------------------------------------------------------------------*/
-
-static void isp1362_remove(struct platform_device *pdev)
-{
- struct usb_hcd *hcd = platform_get_drvdata(pdev);
- struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
-
- remove_debug_file(isp1362_hcd);
- DBG(0, "%s: Removing HCD\n", __func__);
- usb_remove_hcd(hcd);
- DBG(0, "%s: put_hcd\n", __func__);
- usb_put_hcd(hcd);
- DBG(0, "%s: Done\n", __func__);
-}
-
-static int isp1362_probe(struct platform_device *pdev)
-{
- struct usb_hcd *hcd;
- struct isp1362_hcd *isp1362_hcd;
- struct resource *data, *irq_res;
- void __iomem *addr_reg;
- void __iomem *data_reg;
- int irq;
- int retval = 0;
- unsigned int irq_flags = 0;
-
- if (usb_disabled())
- return -ENODEV;
-
- /* basic sanity checks first. board-specific init logic should
- * have initialized this the three resources and probably board
- * specific platform_data. we don't probe for IRQs, and do only
- * minimal sanity checking.
- */
- if (pdev->num_resources < 3)
- return -ENODEV;
-
- irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!irq_res)
- return -ENODEV;
-
- irq = irq_res->start;
-
- addr_reg = devm_platform_ioremap_resource(pdev, 1);
- if (IS_ERR(addr_reg))
- return PTR_ERR(addr_reg);
-
- data_reg = devm_platform_get_and_ioremap_resource(pdev, 0, &data);
- if (IS_ERR(data_reg))
- return PTR_ERR(data_reg);
-
- /* allocate and initialize hcd */
- hcd = usb_create_hcd(&isp1362_hc_driver, &pdev->dev, dev_name(&pdev->dev));
- if (!hcd)
- return -ENOMEM;
-
- hcd->rsrc_start = data->start;
- isp1362_hcd = hcd_to_isp1362_hcd(hcd);
- isp1362_hcd->data_reg = data_reg;
- isp1362_hcd->addr_reg = addr_reg;
-
- isp1362_hcd->next_statechange = jiffies;
- spin_lock_init(&isp1362_hcd->lock);
- INIT_LIST_HEAD(&isp1362_hcd->async);
- INIT_LIST_HEAD(&isp1362_hcd->periodic);
- INIT_LIST_HEAD(&isp1362_hcd->isoc);
- INIT_LIST_HEAD(&isp1362_hcd->remove_list);
- isp1362_hcd->board = dev_get_platdata(&pdev->dev);
-#if USE_PLATFORM_DELAY
- if (!isp1362_hcd->board->delay) {
- dev_err(hcd->self.controller, "No platform delay function given\n");
- retval = -ENODEV;
- goto err;
- }
-#endif
-
- if (irq_res->flags & IORESOURCE_IRQ_HIGHEDGE)
- irq_flags |= IRQF_TRIGGER_RISING;
- if (irq_res->flags & IORESOURCE_IRQ_LOWEDGE)
- irq_flags |= IRQF_TRIGGER_FALLING;
- if (irq_res->flags & IORESOURCE_IRQ_HIGHLEVEL)
- irq_flags |= IRQF_TRIGGER_HIGH;
- if (irq_res->flags & IORESOURCE_IRQ_LOWLEVEL)
- irq_flags |= IRQF_TRIGGER_LOW;
-
- retval = usb_add_hcd(hcd, irq, irq_flags | IRQF_SHARED);
- if (retval != 0)
- goto err;
- device_wakeup_enable(hcd->self.controller);
-
- dev_info(&pdev->dev, "%s, irq %d\n", hcd->product_desc, irq);
-
- create_debug_file(isp1362_hcd);
-
- return 0;
-
- err:
- usb_put_hcd(hcd);
-
- return retval;
-}
-
-#ifdef CONFIG_PM
-static int isp1362_suspend(struct platform_device *pdev, pm_message_t state)
-{
- struct usb_hcd *hcd = platform_get_drvdata(pdev);
- struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
- unsigned long flags;
- int retval = 0;
-
- DBG(0, "%s: Suspending device\n", __func__);
-
- if (state.event == PM_EVENT_FREEZE) {
- DBG(0, "%s: Suspending root hub\n", __func__);
- retval = isp1362_bus_suspend(hcd);
- } else {
- DBG(0, "%s: Suspending RH ports\n", __func__);
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
- isp1362_write_reg32(isp1362_hcd, HCRHSTATUS, RH_HS_LPS);
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
- }
- if (retval == 0)
- pdev->dev.power.power_state = state;
- return retval;
-}
-
-static int isp1362_resume(struct platform_device *pdev)
-{
- struct usb_hcd *hcd = platform_get_drvdata(pdev);
- struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
- unsigned long flags;
-
- DBG(0, "%s: Resuming\n", __func__);
-
- if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND) {
- DBG(0, "%s: Resume RH ports\n", __func__);
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
- isp1362_write_reg32(isp1362_hcd, HCRHSTATUS, RH_HS_LPSC);
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
- return 0;
- }
-
- pdev->dev.power.power_state = PMSG_ON;
-
- return isp1362_bus_resume(isp1362_hcd_to_hcd(isp1362_hcd));
-}
-#else
-#define isp1362_suspend NULL
-#define isp1362_resume NULL
-#endif
-
-static struct platform_driver isp1362_driver = {
- .probe = isp1362_probe,
- .remove = isp1362_remove,
-
- .suspend = isp1362_suspend,
- .resume = isp1362_resume,
- .driver = {
- .name = hcd_name,
- },
-};
-
-module_platform_driver(isp1362_driver);
diff --git a/drivers/usb/host/isp1362.h b/drivers/usb/host/isp1362.h
deleted file mode 100644
index 74ca4be24723..000000000000
--- a/drivers/usb/host/isp1362.h
+++ /dev/null
@@ -1,914 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * ISP1362 HCD (Host Controller Driver) for USB.
- *
- * COPYRIGHT (C) by L. Wassmann <LW@KARO-electronics.de>
- */
-
-/* ------------------------------------------------------------------------- */
-
-#define MAX_ROOT_PORTS 2
-
-#define USE_32BIT 0
-
-/* These options are mutually exclusive */
-#define USE_PLATFORM_DELAY 0
-#define USE_NDELAY 0
-
-#define DUMMY_DELAY_ACCESS do {} while (0)
-
-/* ------------------------------------------------------------------------- */
-
-#define USB_RESET_WIDTH 50
-#define MAX_XFER_SIZE 1023
-
-/* Buffer sizes */
-#define ISP1362_BUF_SIZE 4096
-#define ISP1362_ISTL_BUFSIZE 512
-#define ISP1362_INTL_BLKSIZE 64
-#define ISP1362_INTL_BUFFERS 16
-#define ISP1362_ATL_BLKSIZE 64
-
-#define ISP1362_REG_WRITE_OFFSET 0x80
-
-#define REG_WIDTH_16 0x000
-#define REG_WIDTH_32 0x100
-#define REG_WIDTH_MASK 0x100
-#define REG_NO_MASK 0x0ff
-
-#ifdef ISP1362_DEBUG
-typedef const unsigned int isp1362_reg_t;
-
-#define REG_ACCESS_R 0x200
-#define REG_ACCESS_W 0x400
-#define REG_ACCESS_RW 0x600
-#define REG_ACCESS_MASK 0x600
-
-#define ISP1362_REG_NO(r) ((r) & REG_NO_MASK)
-
-#define ISP1362_REG(name, addr, width, rw) \
-static isp1362_reg_t ISP1362_REG_##name = ((addr) | (width) | (rw))
-
-#define REG_ACCESS_TEST(r) BUG_ON(((r) & ISP1362_REG_WRITE_OFFSET) && !((r) & REG_ACCESS_W))
-#define REG_WIDTH_TEST(r, w) BUG_ON(((r) & REG_WIDTH_MASK) != (w))
-#else
-typedef const unsigned char isp1362_reg_t;
-#define ISP1362_REG_NO(r) (r)
-
-#define ISP1362_REG(name, addr, width, rw) \
-static isp1362_reg_t __maybe_unused ISP1362_REG_##name = addr
-
-#define REG_ACCESS_TEST(r) do {} while (0)
-#define REG_WIDTH_TEST(r, w) do {} while (0)
-#endif
-
-/* OHCI compatible registers */
-/*
- * Note: Some of the ISP1362 'OHCI' registers implement only
- * a subset of the bits defined in the OHCI spec.
- *
- * Bitmasks for the individual bits of these registers are defined in "ohci.h"
- */
-ISP1362_REG(HCREVISION, 0x00, REG_WIDTH_32, REG_ACCESS_R);
-ISP1362_REG(HCCONTROL, 0x01, REG_WIDTH_32, REG_ACCESS_RW);
-ISP1362_REG(HCCMDSTAT, 0x02, REG_WIDTH_32, REG_ACCESS_RW);
-ISP1362_REG(HCINTSTAT, 0x03, REG_WIDTH_32, REG_ACCESS_RW);
-ISP1362_REG(HCINTENB, 0x04, REG_WIDTH_32, REG_ACCESS_RW);
-ISP1362_REG(HCINTDIS, 0x05, REG_WIDTH_32, REG_ACCESS_RW);
-ISP1362_REG(HCFMINTVL, 0x0d, REG_WIDTH_32, REG_ACCESS_RW);
-ISP1362_REG(HCFMREM, 0x0e, REG_WIDTH_32, REG_ACCESS_RW);
-ISP1362_REG(HCFMNUM, 0x0f, REG_WIDTH_32, REG_ACCESS_RW);
-ISP1362_REG(HCLSTHRESH, 0x11, REG_WIDTH_32, REG_ACCESS_RW);
-ISP1362_REG(HCRHDESCA, 0x12, REG_WIDTH_32, REG_ACCESS_RW);
-ISP1362_REG(HCRHDESCB, 0x13, REG_WIDTH_32, REG_ACCESS_RW);
-ISP1362_REG(HCRHSTATUS, 0x14, REG_WIDTH_32, REG_ACCESS_RW);
-ISP1362_REG(HCRHPORT1, 0x15, REG_WIDTH_32, REG_ACCESS_RW);
-ISP1362_REG(HCRHPORT2, 0x16, REG_WIDTH_32, REG_ACCESS_RW);
-
-/* Philips ISP1362 specific registers */
-ISP1362_REG(HCHWCFG, 0x20, REG_WIDTH_16, REG_ACCESS_RW);
-#define HCHWCFG_DISABLE_SUSPEND (1 << 15)
-#define HCHWCFG_GLOBAL_PWRDOWN (1 << 14)
-#define HCHWCFG_PULLDOWN_DS2 (1 << 13)
-#define HCHWCFG_PULLDOWN_DS1 (1 << 12)
-#define HCHWCFG_CLKNOTSTOP (1 << 11)
-#define HCHWCFG_ANALOG_OC (1 << 10)
-#define HCHWCFG_ONEINT (1 << 9)
-#define HCHWCFG_DACK_MODE (1 << 8)
-#define HCHWCFG_ONEDMA (1 << 7)
-#define HCHWCFG_DACK_POL (1 << 6)
-#define HCHWCFG_DREQ_POL (1 << 5)
-#define HCHWCFG_DBWIDTH_MASK (0x03 << 3)
-#define HCHWCFG_DBWIDTH(n) (((n) << 3) & HCHWCFG_DBWIDTH_MASK)
-#define HCHWCFG_INT_POL (1 << 2)
-#define HCHWCFG_INT_TRIGGER (1 << 1)
-#define HCHWCFG_INT_ENABLE (1 << 0)
-
-ISP1362_REG(HCDMACFG, 0x21, REG_WIDTH_16, REG_ACCESS_RW);
-#define HCDMACFG_CTR_ENABLE (1 << 7)
-#define HCDMACFG_BURST_LEN_MASK (0x03 << 5)
-#define HCDMACFG_BURST_LEN(n) (((n) << 5) & HCDMACFG_BURST_LEN_MASK)
-#define HCDMACFG_BURST_LEN_1 HCDMACFG_BURST_LEN(0)
-#define HCDMACFG_BURST_LEN_4 HCDMACFG_BURST_LEN(1)
-#define HCDMACFG_BURST_LEN_8 HCDMACFG_BURST_LEN(2)
-#define HCDMACFG_DMA_ENABLE (1 << 4)
-#define HCDMACFG_BUF_TYPE_MASK (0x07 << 1)
-#define HCDMACFG_BUF_TYPE(n) (((n) << 1) & HCDMACFG_BUF_TYPE_MASK)
-#define HCDMACFG_BUF_ISTL0 HCDMACFG_BUF_TYPE(0)
-#define HCDMACFG_BUF_ISTL1 HCDMACFG_BUF_TYPE(1)
-#define HCDMACFG_BUF_INTL HCDMACFG_BUF_TYPE(2)
-#define HCDMACFG_BUF_ATL HCDMACFG_BUF_TYPE(3)
-#define HCDMACFG_BUF_DIRECT HCDMACFG_BUF_TYPE(4)
-#define HCDMACFG_DMA_RW_SELECT (1 << 0)
-
-ISP1362_REG(HCXFERCTR, 0x22, REG_WIDTH_16, REG_ACCESS_RW);
-
-ISP1362_REG(HCuPINT, 0x24, REG_WIDTH_16, REG_ACCESS_RW);
-#define HCuPINT_SOF (1 << 0)
-#define HCuPINT_ISTL0 (1 << 1)
-#define HCuPINT_ISTL1 (1 << 2)
-#define HCuPINT_EOT (1 << 3)
-#define HCuPINT_OPR (1 << 4)
-#define HCuPINT_SUSP (1 << 5)
-#define HCuPINT_CLKRDY (1 << 6)
-#define HCuPINT_INTL (1 << 7)
-#define HCuPINT_ATL (1 << 8)
-#define HCuPINT_OTG (1 << 9)
-
-ISP1362_REG(HCuPINTENB, 0x25, REG_WIDTH_16, REG_ACCESS_RW);
-/* same bit definitions apply as for HCuPINT */
-
-ISP1362_REG(HCCHIPID, 0x27, REG_WIDTH_16, REG_ACCESS_R);
-#define HCCHIPID_MASK 0xff00
-#define HCCHIPID_MAGIC 0x3600
-
-ISP1362_REG(HCSCRATCH, 0x28, REG_WIDTH_16, REG_ACCESS_RW);
-
-ISP1362_REG(HCSWRES, 0x29, REG_WIDTH_16, REG_ACCESS_W);
-#define HCSWRES_MAGIC 0x00f6
-
-ISP1362_REG(HCBUFSTAT, 0x2c, REG_WIDTH_16, REG_ACCESS_RW);
-#define HCBUFSTAT_ISTL0_FULL (1 << 0)
-#define HCBUFSTAT_ISTL1_FULL (1 << 1)
-#define HCBUFSTAT_INTL_ACTIVE (1 << 2)
-#define HCBUFSTAT_ATL_ACTIVE (1 << 3)
-#define HCBUFSTAT_RESET_HWPP (1 << 4)
-#define HCBUFSTAT_ISTL0_ACTIVE (1 << 5)
-#define HCBUFSTAT_ISTL1_ACTIVE (1 << 6)
-#define HCBUFSTAT_ISTL0_DONE (1 << 8)
-#define HCBUFSTAT_ISTL1_DONE (1 << 9)
-#define HCBUFSTAT_PAIRED_PTDPP (1 << 10)
-
-ISP1362_REG(HCDIRADDR, 0x32, REG_WIDTH_32, REG_ACCESS_RW);
-#define HCDIRADDR_ADDR_MASK 0x0000ffff
-#define HCDIRADDR_ADDR(n) (((n) << 0) & HCDIRADDR_ADDR_MASK)
-#define HCDIRADDR_COUNT_MASK 0xffff0000
-#define HCDIRADDR_COUNT(n) (((n) << 16) & HCDIRADDR_COUNT_MASK)
-ISP1362_REG(HCDIRDATA, 0x45, REG_WIDTH_16, REG_ACCESS_RW);
-
-ISP1362_REG(HCISTLBUFSZ, 0x30, REG_WIDTH_16, REG_ACCESS_RW);
-ISP1362_REG(HCISTL0PORT, 0x40, REG_WIDTH_16, REG_ACCESS_RW);
-ISP1362_REG(HCISTL1PORT, 0x42, REG_WIDTH_16, REG_ACCESS_RW);
-ISP1362_REG(HCISTLRATE, 0x47, REG_WIDTH_16, REG_ACCESS_RW);
-
-ISP1362_REG(HCINTLBUFSZ, 0x33, REG_WIDTH_16, REG_ACCESS_RW);
-ISP1362_REG(HCINTLPORT, 0x43, REG_WIDTH_16, REG_ACCESS_RW);
-ISP1362_REG(HCINTLBLKSZ, 0x53, REG_WIDTH_16, REG_ACCESS_RW);
-ISP1362_REG(HCINTLDONE, 0x17, REG_WIDTH_32, REG_ACCESS_R);
-ISP1362_REG(HCINTLSKIP, 0x18, REG_WIDTH_32, REG_ACCESS_RW);
-ISP1362_REG(HCINTLLAST, 0x19, REG_WIDTH_32, REG_ACCESS_RW);
-ISP1362_REG(HCINTLCURR, 0x1a, REG_WIDTH_16, REG_ACCESS_R);
-
-ISP1362_REG(HCATLBUFSZ, 0x34, REG_WIDTH_16, REG_ACCESS_RW);
-ISP1362_REG(HCATLPORT, 0x44, REG_WIDTH_16, REG_ACCESS_RW);
-ISP1362_REG(HCATLBLKSZ, 0x54, REG_WIDTH_16, REG_ACCESS_RW);
-ISP1362_REG(HCATLDONE, 0x1b, REG_WIDTH_32, REG_ACCESS_R);
-ISP1362_REG(HCATLSKIP, 0x1c, REG_WIDTH_32, REG_ACCESS_RW);
-ISP1362_REG(HCATLLAST, 0x1d, REG_WIDTH_32, REG_ACCESS_RW);
-ISP1362_REG(HCATLCURR, 0x1e, REG_WIDTH_16, REG_ACCESS_R);
-
-ISP1362_REG(HCATLDTC, 0x51, REG_WIDTH_16, REG_ACCESS_RW);
-ISP1362_REG(HCATLDTCTO, 0x52, REG_WIDTH_16, REG_ACCESS_RW);
-
-
-ISP1362_REG(OTGCONTROL, 0x62, REG_WIDTH_16, REG_ACCESS_RW);
-ISP1362_REG(OTGSTATUS, 0x67, REG_WIDTH_16, REG_ACCESS_R);
-ISP1362_REG(OTGINT, 0x68, REG_WIDTH_16, REG_ACCESS_RW);
-ISP1362_REG(OTGINTENB, 0x69, REG_WIDTH_16, REG_ACCESS_RW);
-ISP1362_REG(OTGTIMER, 0x6A, REG_WIDTH_16, REG_ACCESS_RW);
-ISP1362_REG(OTGALTTMR, 0x6C, REG_WIDTH_16, REG_ACCESS_RW);
-
-/* Philips transfer descriptor, cpu-endian */
-struct ptd {
- u16 count;
-#define PTD_COUNT_MSK (0x3ff << 0)
-#define PTD_TOGGLE_MSK (1 << 10)
-#define PTD_ACTIVE_MSK (1 << 11)
-#define PTD_CC_MSK (0xf << 12)
- u16 mps;
-#define PTD_MPS_MSK (0x3ff << 0)
-#define PTD_SPD_MSK (1 << 10)
-#define PTD_LAST_MSK (1 << 11)
-#define PTD_EP_MSK (0xf << 12)
- u16 len;
-#define PTD_LEN_MSK (0x3ff << 0)
-#define PTD_DIR_MSK (3 << 10)
-#define PTD_DIR_SETUP (0)
-#define PTD_DIR_OUT (1)
-#define PTD_DIR_IN (2)
- u16 faddr;
-#define PTD_FA_MSK (0x7f << 0)
-/* PTD Byte 7: [StartingFrame (if ISO PTD) | StartingFrame[0..4], PollingRate[0..2] (if INT PTD)] */
-#define PTD_SF_ISO_MSK (0xff << 8)
-#define PTD_SF_INT_MSK (0x1f << 8)
-#define PTD_PR_MSK (0x07 << 13)
-} __attribute__ ((packed, aligned(2)));
-#define PTD_HEADER_SIZE sizeof(struct ptd)
-
-/* ------------------------------------------------------------------------- */
-/* Copied from ohci.h: */
-/*
- * Hardware transfer status codes -- CC from PTD
- */
-#define PTD_CC_NOERROR 0x00
-#define PTD_CC_CRC 0x01
-#define PTD_CC_BITSTUFFING 0x02
-#define PTD_CC_DATATOGGLEM 0x03
-#define PTD_CC_STALL 0x04
-#define PTD_DEVNOTRESP 0x05
-#define PTD_PIDCHECKFAIL 0x06
-#define PTD_UNEXPECTEDPID 0x07
-#define PTD_DATAOVERRUN 0x08
-#define PTD_DATAUNDERRUN 0x09
- /* 0x0A, 0x0B reserved for hardware */
-#define PTD_BUFFEROVERRUN 0x0C
-#define PTD_BUFFERUNDERRUN 0x0D
- /* 0x0E, 0x0F reserved for HCD */
-#define PTD_NOTACCESSED 0x0F
-
-
-/* map OHCI TD status codes (CC) to errno values */
-static const int cc_to_error[16] = {
- /* No Error */ 0,
- /* CRC Error */ -EILSEQ,
- /* Bit Stuff */ -EPROTO,
- /* Data Togg */ -EILSEQ,
- /* Stall */ -EPIPE,
- /* DevNotResp */ -ETIMEDOUT,
- /* PIDCheck */ -EPROTO,
- /* UnExpPID */ -EPROTO,
- /* DataOver */ -EOVERFLOW,
- /* DataUnder */ -EREMOTEIO,
- /* (for hw) */ -EIO,
- /* (for hw) */ -EIO,
- /* BufferOver */ -ECOMM,
- /* BuffUnder */ -ENOSR,
- /* (for HCD) */ -EALREADY,
- /* (for HCD) */ -EALREADY
-};
-
-
-/*
- * HcControl (control) register masks
- */
-#define OHCI_CTRL_HCFS (3 << 6) /* host controller functional state */
-#define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */
-#define OHCI_CTRL_RWE (1 << 10) /* remote wakeup enable */
-
-/* pre-shifted values for HCFS */
-# define OHCI_USB_RESET (0 << 6)
-# define OHCI_USB_RESUME (1 << 6)
-# define OHCI_USB_OPER (2 << 6)
-# define OHCI_USB_SUSPEND (3 << 6)
-
-/*
- * HcCommandStatus (cmdstatus) register masks
- */
-#define OHCI_HCR (1 << 0) /* host controller reset */
-#define OHCI_SOC (3 << 16) /* scheduling overrun count */
-
-/*
- * masks used with interrupt registers:
- * HcInterruptStatus (intrstatus)
- * HcInterruptEnable (intrenable)
- * HcInterruptDisable (intrdisable)
- */
-#define OHCI_INTR_SO (1 << 0) /* scheduling overrun */
-#define OHCI_INTR_WDH (1 << 1) /* writeback of done_head */
-#define OHCI_INTR_SF (1 << 2) /* start frame */
-#define OHCI_INTR_RD (1 << 3) /* resume detect */
-#define OHCI_INTR_UE (1 << 4) /* unrecoverable error */
-#define OHCI_INTR_FNO (1 << 5) /* frame number overflow */
-#define OHCI_INTR_RHSC (1 << 6) /* root hub status change */
-#define OHCI_INTR_OC (1 << 30) /* ownership change */
-#define OHCI_INTR_MIE (1 << 31) /* master interrupt enable */
-
-/* roothub.portstatus [i] bits */
-#define RH_PS_CCS 0x00000001 /* current connect status */
-#define RH_PS_PES 0x00000002 /* port enable status*/
-#define RH_PS_PSS 0x00000004 /* port suspend status */
-#define RH_PS_POCI 0x00000008 /* port over current indicator */
-#define RH_PS_PRS 0x00000010 /* port reset status */
-#define RH_PS_PPS 0x00000100 /* port power status */
-#define RH_PS_LSDA 0x00000200 /* low speed device attached */
-#define RH_PS_CSC 0x00010000 /* connect status change */
-#define RH_PS_PESC 0x00020000 /* port enable status change */
-#define RH_PS_PSSC 0x00040000 /* port suspend status change */
-#define RH_PS_OCIC 0x00080000 /* over current indicator change */
-#define RH_PS_PRSC 0x00100000 /* port reset status change */
-
-/* roothub.status bits */
-#define RH_HS_LPS 0x00000001 /* local power status */
-#define RH_HS_OCI 0x00000002 /* over current indicator */
-#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */
-#define RH_HS_LPSC 0x00010000 /* local power status change */
-#define RH_HS_OCIC 0x00020000 /* over current indicator change */
-#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */
-
-/* roothub.b masks */
-#define RH_B_DR 0x0000ffff /* device removable flags */
-#define RH_B_PPCM 0xffff0000 /* port power control mask */
-
-/* roothub.a masks */
-#define RH_A_NDP (0xff << 0) /* number of downstream ports */
-#define RH_A_PSM (1 << 8) /* power switching mode */
-#define RH_A_NPS (1 << 9) /* no power switching */
-#define RH_A_DT (1 << 10) /* device type (mbz) */
-#define RH_A_OCPM (1 << 11) /* over current protection mode */
-#define RH_A_NOCP (1 << 12) /* no over current protection */
-#define RH_A_POTPGT (0xff << 24) /* power on to power good time */
-
-#define FI 0x2edf /* 12000 bits per frame (-1) */
-#define FSMP(fi) (0x7fff & ((6 * ((fi) - 210)) / 7))
-#define LSTHRESH 0x628 /* lowspeed bit threshold */
-
-/* ------------------------------------------------------------------------- */
-
-/* PTD accessor macros. */
-#define PTD_GET_COUNT(p) (((p)->count & PTD_COUNT_MSK) >> 0)
-#define PTD_COUNT(v) (((v) << 0) & PTD_COUNT_MSK)
-#define PTD_GET_TOGGLE(p) (((p)->count & PTD_TOGGLE_MSK) >> 10)
-#define PTD_TOGGLE(v) (((v) << 10) & PTD_TOGGLE_MSK)
-#define PTD_GET_ACTIVE(p) (((p)->count & PTD_ACTIVE_MSK) >> 11)
-#define PTD_ACTIVE(v) (((v) << 11) & PTD_ACTIVE_MSK)
-#define PTD_GET_CC(p) (((p)->count & PTD_CC_MSK) >> 12)
-#define PTD_CC(v) (((v) << 12) & PTD_CC_MSK)
-#define PTD_GET_MPS(p) (((p)->mps & PTD_MPS_MSK) >> 0)
-#define PTD_MPS(v) (((v) << 0) & PTD_MPS_MSK)
-#define PTD_GET_SPD(p) (((p)->mps & PTD_SPD_MSK) >> 10)
-#define PTD_SPD(v) (((v) << 10) & PTD_SPD_MSK)
-#define PTD_GET_LAST(p) (((p)->mps & PTD_LAST_MSK) >> 11)
-#define PTD_LAST(v) (((v) << 11) & PTD_LAST_MSK)
-#define PTD_GET_EP(p) (((p)->mps & PTD_EP_MSK) >> 12)
-#define PTD_EP(v) (((v) << 12) & PTD_EP_MSK)
-#define PTD_GET_LEN(p) (((p)->len & PTD_LEN_MSK) >> 0)
-#define PTD_LEN(v) (((v) << 0) & PTD_LEN_MSK)
-#define PTD_GET_DIR(p) (((p)->len & PTD_DIR_MSK) >> 10)
-#define PTD_DIR(v) (((v) << 10) & PTD_DIR_MSK)
-#define PTD_GET_FA(p) (((p)->faddr & PTD_FA_MSK) >> 0)
-#define PTD_FA(v) (((v) << 0) & PTD_FA_MSK)
-#define PTD_GET_SF_INT(p) (((p)->faddr & PTD_SF_INT_MSK) >> 8)
-#define PTD_SF_INT(v) (((v) << 8) & PTD_SF_INT_MSK)
-#define PTD_GET_SF_ISO(p) (((p)->faddr & PTD_SF_ISO_MSK) >> 8)
-#define PTD_SF_ISO(v) (((v) << 8) & PTD_SF_ISO_MSK)
-#define PTD_GET_PR(p) (((p)->faddr & PTD_PR_MSK) >> 13)
-#define PTD_PR(v) (((v) << 13) & PTD_PR_MSK)
-
-#define LOG2_PERIODIC_SIZE 5 /* arbitrary; this matches OHCI */
-#define PERIODIC_SIZE (1 << LOG2_PERIODIC_SIZE)
-
-struct isp1362_ep {
- struct usb_host_endpoint *hep;
- struct usb_device *udev;
-
- /* philips transfer descriptor */
- struct ptd ptd;
-
- u8 maxpacket;
- u8 epnum;
- u8 nextpid;
- u16 error_count;
- u16 length; /* of current packet */
- s16 ptd_offset; /* buffer offset in ISP1362 where
- PTD has been stored
- (for access thru HCDIRDATA) */
- int ptd_index;
- int num_ptds;
- void *data; /* to databuf */
- /* queue of active EPs (the ones transmitted to the chip) */
- struct list_head active;
-
- /* periodic schedule */
- u8 branch;
- u16 interval;
- u16 load;
- u16 last_iso;
-
- /* async schedule */
- struct list_head schedule; /* list of all EPs that need processing */
- struct list_head remove_list;
- int num_req;
-};
-
-struct isp1362_ep_queue {
- struct list_head active; /* list of PTDs currently processed by HC */
- atomic_t finishing;
- unsigned long buf_map;
- unsigned long skip_map;
- int free_ptd;
- u16 buf_start;
- u16 buf_size;
- u16 blk_size; /* PTD buffer block size for ATL and INTL */
- u8 buf_count;
- u8 buf_avail;
- char name[16];
-
- /* for statistical tracking */
- u8 stat_maxptds; /* Max # of ptds seen simultaneously in fifo */
- u8 ptd_count; /* number of ptds submitted to this queue */
-};
-
-struct isp1362_hcd {
- spinlock_t lock;
- void __iomem *addr_reg;
- void __iomem *data_reg;
-
- struct isp1362_platform_data *board;
-
- unsigned long stat1, stat2, stat4, stat8, stat16;
-
- /* HC registers */
- u32 intenb; /* "OHCI" interrupts */
- u16 irqenb; /* uP interrupts */
-
- /* Root hub registers */
- u32 rhdesca;
- u32 rhdescb;
- u32 rhstatus;
- u32 rhport[MAX_ROOT_PORTS];
- unsigned long next_statechange;
-
- /* HC control reg shadow copy */
- u32 hc_control;
-
- /* async schedule: control, bulk */
- struct list_head async;
-
- /* periodic schedule: int */
- u16 load[PERIODIC_SIZE];
- struct list_head periodic;
- u16 fmindex;
-
- /* periodic schedule: isochronous */
- struct list_head isoc;
- unsigned int istl_flip:1;
- unsigned int irq_active:1;
-
- /* Schedules for the current frame */
- struct isp1362_ep_queue atl_queue;
- struct isp1362_ep_queue intl_queue;
- struct isp1362_ep_queue istl_queue[2];
-
- /* list of PTDs retrieved from HC */
- struct list_head remove_list;
- enum {
- ISP1362_INT_SOF,
- ISP1362_INT_ISTL0,
- ISP1362_INT_ISTL1,
- ISP1362_INT_EOT,
- ISP1362_INT_OPR,
- ISP1362_INT_SUSP,
- ISP1362_INT_CLKRDY,
- ISP1362_INT_INTL,
- ISP1362_INT_ATL,
- ISP1362_INT_OTG,
- NUM_ISP1362_IRQS
- } IRQ_NAMES;
- unsigned int irq_stat[NUM_ISP1362_IRQS];
- int req_serial;
-};
-
-static inline const char *ISP1362_INT_NAME(int n)
-{
- switch (n) {
- case ISP1362_INT_SOF: return "SOF";
- case ISP1362_INT_ISTL0: return "ISTL0";
- case ISP1362_INT_ISTL1: return "ISTL1";
- case ISP1362_INT_EOT: return "EOT";
- case ISP1362_INT_OPR: return "OPR";
- case ISP1362_INT_SUSP: return "SUSP";
- case ISP1362_INT_CLKRDY: return "CLKRDY";
- case ISP1362_INT_INTL: return "INTL";
- case ISP1362_INT_ATL: return "ATL";
- case ISP1362_INT_OTG: return "OTG";
- default: return "unknown";
- }
-}
-
-static inline void ALIGNSTAT(struct isp1362_hcd *isp1362_hcd, void *ptr)
-{
- unsigned long p = (unsigned long)ptr;
- if (!(p & 0xf))
- isp1362_hcd->stat16++;
- else if (!(p & 0x7))
- isp1362_hcd->stat8++;
- else if (!(p & 0x3))
- isp1362_hcd->stat4++;
- else if (!(p & 0x1))
- isp1362_hcd->stat2++;
- else
- isp1362_hcd->stat1++;
-}
-
-static inline struct isp1362_hcd *hcd_to_isp1362_hcd(struct usb_hcd *hcd)
-{
- return (struct isp1362_hcd *) (hcd->hcd_priv);
-}
-
-static inline struct usb_hcd *isp1362_hcd_to_hcd(struct isp1362_hcd *isp1362_hcd)
-{
- return container_of((void *)isp1362_hcd, struct usb_hcd, hcd_priv);
-}
-
-#define frame_before(f1, f2) ((s16)((u16)f1 - (u16)f2) < 0)
-
-/*
- * ISP1362 HW Interface
- */
-
-#define DBG(level, fmt...) \
- do { \
- if (dbg_level > level) \
- pr_debug(fmt); \
- } while (0)
-
-#ifdef VERBOSE
-# define VDBG(fmt...) DBG(3, fmt)
-#else
-# define VDBG(fmt...) do {} while (0)
-#endif
-
-#ifdef REGISTERS
-# define RDBG(fmt...) DBG(1, fmt)
-#else
-# define RDBG(fmt...) do {} while (0)
-#endif
-
-#ifdef URB_TRACE
-#define URB_DBG(fmt...) DBG(0, fmt)
-#else
-#define URB_DBG(fmt...) do {} while (0)
-#endif
-
-
-#if USE_PLATFORM_DELAY
-#if USE_NDELAY
-#error USE_PLATFORM_DELAY and USE_NDELAY defined simultaneously.
-#endif
-#define isp1362_delay(h, d) (h)->board->delay(isp1362_hcd_to_hcd(h)->self.controller, d)
-#elif USE_NDELAY
-#define isp1362_delay(h, d) ndelay(d)
-#else
-#define isp1362_delay(h, d) do {} while (0)
-#endif
-
-#define get_urb(ep) ({ \
- BUG_ON(list_empty(&ep->hep->urb_list)); \
- container_of(ep->hep->urb_list.next, struct urb, urb_list); \
-})
-
-/* basic access functions for ISP1362 chip registers */
-/* NOTE: The contents of the address pointer register cannot be read back! The driver must ensure,
- * that all register accesses are performed with interrupts disabled, since the interrupt
- * handler has no way of restoring the previous state.
- */
-static void isp1362_write_addr(struct isp1362_hcd *isp1362_hcd, isp1362_reg_t reg)
-{
- REG_ACCESS_TEST(reg);
- DUMMY_DELAY_ACCESS;
- writew(ISP1362_REG_NO(reg), isp1362_hcd->addr_reg);
- DUMMY_DELAY_ACCESS;
- isp1362_delay(isp1362_hcd, 1);
-}
-
-static void isp1362_write_data16(struct isp1362_hcd *isp1362_hcd, u16 val)
-{
- DUMMY_DELAY_ACCESS;
- writew(val, isp1362_hcd->data_reg);
-}
-
-static u16 isp1362_read_data16(struct isp1362_hcd *isp1362_hcd)
-{
- u16 val;
-
- DUMMY_DELAY_ACCESS;
- val = readw(isp1362_hcd->data_reg);
-
- return val;
-}
-
-static void isp1362_write_data32(struct isp1362_hcd *isp1362_hcd, u32 val)
-{
-#if USE_32BIT
- DUMMY_DELAY_ACCESS;
- writel(val, isp1362_hcd->data_reg);
-#else
- DUMMY_DELAY_ACCESS;
- writew((u16)val, isp1362_hcd->data_reg);
- DUMMY_DELAY_ACCESS;
- writew(val >> 16, isp1362_hcd->data_reg);
-#endif
-}
-
-static u32 isp1362_read_data32(struct isp1362_hcd *isp1362_hcd)
-{
- u32 val;
-
-#if USE_32BIT
- DUMMY_DELAY_ACCESS;
- val = readl(isp1362_hcd->data_reg);
-#else
- DUMMY_DELAY_ACCESS;
- val = (u32)readw(isp1362_hcd->data_reg);
- DUMMY_DELAY_ACCESS;
- val |= (u32)readw(isp1362_hcd->data_reg) << 16;
-#endif
- return val;
-}
-
-/* use readsw/writesw to access the fifo whenever possible */
-/* assume HCDIRDATA or XFERCTR & addr_reg have been set up */
-static void isp1362_read_fifo(struct isp1362_hcd *isp1362_hcd, void *buf, u16 len)
-{
- u8 *dp = buf;
- u16 data;
-
- if (!len)
- return;
-
- RDBG("%s: Reading %d byte from fifo to mem @ %p\n", __func__, len, buf);
-#if USE_32BIT
- if (len >= 4) {
- RDBG("%s: Using readsl for %d dwords\n", __func__, len >> 2);
- readsl(isp1362_hcd->data_reg, dp, len >> 2);
- dp += len & ~3;
- len &= 3;
- }
-#endif
- if (len >= 2) {
- RDBG("%s: Using readsw for %d words\n", __func__, len >> 1);
- insw((unsigned long)isp1362_hcd->data_reg, dp, len >> 1);
- dp += len & ~1;
- len &= 1;
- }
-
- BUG_ON(len & ~1);
- if (len > 0) {
- data = isp1362_read_data16(isp1362_hcd);
- RDBG("%s: Reading trailing byte %02x to mem @ %08x\n", __func__,
- (u8)data, (u32)dp);
- *dp = (u8)data;
- }
-}
-
-static void isp1362_write_fifo(struct isp1362_hcd *isp1362_hcd, void *buf, u16 len)
-{
- u8 *dp = buf;
- u16 data;
-
- if (!len)
- return;
-
- if ((unsigned long)dp & 0x1) {
- /* not aligned */
- for (; len > 1; len -= 2) {
- data = *dp++;
- data |= *dp++ << 8;
- isp1362_write_data16(isp1362_hcd, data);
- }
- if (len)
- isp1362_write_data16(isp1362_hcd, *dp);
- return;
- }
-
- RDBG("%s: Writing %d byte to fifo from memory @%p\n", __func__, len, buf);
-#if USE_32BIT
- if (len >= 4) {
- RDBG("%s: Using writesl for %d dwords\n", __func__, len >> 2);
- writesl(isp1362_hcd->data_reg, dp, len >> 2);
- dp += len & ~3;
- len &= 3;
- }
-#endif
- if (len >= 2) {
- RDBG("%s: Using writesw for %d words\n", __func__, len >> 1);
- outsw((unsigned long)isp1362_hcd->data_reg, dp, len >> 1);
- dp += len & ~1;
- len &= 1;
- }
-
- BUG_ON(len & ~1);
- if (len > 0) {
- /* finally write any trailing byte; we don't need to care
- * about the high byte of the last word written
- */
- data = (u16)*dp;
- RDBG("%s: Sending trailing byte %02x from mem @ %08x\n", __func__,
- data, (u32)dp);
- isp1362_write_data16(isp1362_hcd, data);
- }
-}
-
-#define isp1362_read_reg16(d, r) ({ \
- u16 __v; \
- REG_WIDTH_TEST(ISP1362_REG_##r, REG_WIDTH_16); \
- isp1362_write_addr(d, ISP1362_REG_##r); \
- __v = isp1362_read_data16(d); \
- RDBG("%s: Read %04x from %s[%02x]\n", __func__, __v, #r, \
- ISP1362_REG_NO(ISP1362_REG_##r)); \
- __v; \
-})
-
-#define isp1362_read_reg32(d, r) ({ \
- u32 __v; \
- REG_WIDTH_TEST(ISP1362_REG_##r, REG_WIDTH_32); \
- isp1362_write_addr(d, ISP1362_REG_##r); \
- __v = isp1362_read_data32(d); \
- RDBG("%s: Read %08x from %s[%02x]\n", __func__, __v, #r, \
- ISP1362_REG_NO(ISP1362_REG_##r)); \
- __v; \
-})
-
-#define isp1362_write_reg16(d, r, v) { \
- REG_WIDTH_TEST(ISP1362_REG_##r, REG_WIDTH_16); \
- isp1362_write_addr(d, (ISP1362_REG_##r) | ISP1362_REG_WRITE_OFFSET); \
- isp1362_write_data16(d, (u16)(v)); \
- RDBG("%s: Wrote %04x to %s[%02x]\n", __func__, (u16)(v), #r, \
- ISP1362_REG_NO(ISP1362_REG_##r)); \
-}
-
-#define isp1362_write_reg32(d, r, v) { \
- REG_WIDTH_TEST(ISP1362_REG_##r, REG_WIDTH_32); \
- isp1362_write_addr(d, (ISP1362_REG_##r) | ISP1362_REG_WRITE_OFFSET); \
- isp1362_write_data32(d, (u32)(v)); \
- RDBG("%s: Wrote %08x to %s[%02x]\n", __func__, (u32)(v), #r, \
- ISP1362_REG_NO(ISP1362_REG_##r)); \
-}
-
-#define isp1362_set_mask16(d, r, m) { \
- u16 __v; \
- __v = isp1362_read_reg16(d, r); \
- if ((__v | m) != __v) \
- isp1362_write_reg16(d, r, __v | m); \
-}
-
-#define isp1362_clr_mask16(d, r, m) { \
- u16 __v; \
- __v = isp1362_read_reg16(d, r); \
- if ((__v & ~m) != __v) \
- isp1362_write_reg16(d, r, __v & ~m); \
-}
-
-#define isp1362_set_mask32(d, r, m) { \
- u32 __v; \
- __v = isp1362_read_reg32(d, r); \
- if ((__v | m) != __v) \
- isp1362_write_reg32(d, r, __v | m); \
-}
-
-#define isp1362_clr_mask32(d, r, m) { \
- u32 __v; \
- __v = isp1362_read_reg32(d, r); \
- if ((__v & ~m) != __v) \
- isp1362_write_reg32(d, r, __v & ~m); \
-}
-
-#define isp1362_show_reg(d, r) { \
- if ((ISP1362_REG_##r & REG_WIDTH_MASK) == REG_WIDTH_32) \
- DBG(0, "%-12s[%02x]: %08x\n", #r, \
- ISP1362_REG_NO(ISP1362_REG_##r), isp1362_read_reg32(d, r)); \
- else \
- DBG(0, "%-12s[%02x]: %04x\n", #r, \
- ISP1362_REG_NO(ISP1362_REG_##r), isp1362_read_reg16(d, r)); \
-}
-
-static void isp1362_write_diraddr(struct isp1362_hcd *isp1362_hcd, u16 offset, u16 len)
-{
- len = (len + 1) & ~1;
-
- isp1362_clr_mask16(isp1362_hcd, HCDMACFG, HCDMACFG_CTR_ENABLE);
- isp1362_write_reg32(isp1362_hcd, HCDIRADDR,
- HCDIRADDR_ADDR(offset) | HCDIRADDR_COUNT(len));
-}
-
-static void isp1362_read_buffer(struct isp1362_hcd *isp1362_hcd, void *buf, u16 offset, int len)
-{
- isp1362_write_diraddr(isp1362_hcd, offset, len);
-
- DBG(3, "%s: Reading %d byte from buffer @%04x to memory @ %p\n",
- __func__, len, offset, buf);
-
- isp1362_write_reg16(isp1362_hcd, HCuPINT, HCuPINT_EOT);
-
- isp1362_write_addr(isp1362_hcd, ISP1362_REG_HCDIRDATA);
-
- isp1362_read_fifo(isp1362_hcd, buf, len);
- isp1362_write_reg16(isp1362_hcd, HCuPINT, HCuPINT_EOT);
-}
-
-static void isp1362_write_buffer(struct isp1362_hcd *isp1362_hcd, void *buf, u16 offset, int len)
-{
- isp1362_write_diraddr(isp1362_hcd, offset, len);
-
- DBG(3, "%s: Writing %d byte to buffer @%04x from memory @ %p\n",
- __func__, len, offset, buf);
-
- isp1362_write_reg16(isp1362_hcd, HCuPINT, HCuPINT_EOT);
-
- isp1362_write_addr(isp1362_hcd, ISP1362_REG_HCDIRDATA | ISP1362_REG_WRITE_OFFSET);
- isp1362_write_fifo(isp1362_hcd, buf, len);
-
- isp1362_write_reg16(isp1362_hcd, HCuPINT, HCuPINT_EOT);
-}
-
-static void __attribute__((unused)) dump_data(char *buf, int len)
-{
- if (dbg_level > 0) {
- int k;
- int lf = 0;
-
- for (k = 0; k < len; ++k) {
- if (!lf)
- DBG(0, "%04x:", k);
- printk(" %02x", ((u8 *) buf)[k]);
- lf = 1;
- if (!k)
- continue;
- if (k % 16 == 15) {
- printk("\n");
- lf = 0;
- continue;
- }
- if (k % 8 == 7)
- printk(" ");
- if (k % 4 == 3)
- printk(" ");
- }
- if (lf)
- printk("\n");
- }
-}
-
-#if defined(PTD_TRACE)
-
-static void dump_ptd(struct ptd *ptd)
-{
- DBG(0, "EP %p: CC=%x EP=%d DIR=%x CNT=%d LEN=%d MPS=%d TGL=%x ACT=%x FA=%d SPD=%x SF=%x PR=%x LST=%x\n",
- container_of(ptd, struct isp1362_ep, ptd),
- PTD_GET_CC(ptd), PTD_GET_EP(ptd), PTD_GET_DIR(ptd),
- PTD_GET_COUNT(ptd), PTD_GET_LEN(ptd), PTD_GET_MPS(ptd),
- PTD_GET_TOGGLE(ptd), PTD_GET_ACTIVE(ptd), PTD_GET_FA(ptd),
- PTD_GET_SPD(ptd), PTD_GET_SF_INT(ptd), PTD_GET_PR(ptd), PTD_GET_LAST(ptd));
- DBG(0, " %04x %04x %04x %04x\n", ptd->count, ptd->mps, ptd->len, ptd->faddr);
-}
-
-static void dump_ptd_out_data(struct ptd *ptd, u8 *buf)
-{
- if (dbg_level > 0) {
- if (PTD_GET_DIR(ptd) != PTD_DIR_IN && PTD_GET_LEN(ptd)) {
- DBG(0, "--out->\n");
- dump_data(buf, PTD_GET_LEN(ptd));
- }
- }
-}
-
-static void dump_ptd_in_data(struct ptd *ptd, u8 *buf)
-{
- if (dbg_level > 0) {
- if (PTD_GET_DIR(ptd) == PTD_DIR_IN && PTD_GET_COUNT(ptd)) {
- DBG(0, "<--in--\n");
- dump_data(buf, PTD_GET_COUNT(ptd));
- }
- DBG(0, "-----\n");
- }
-}
-
-static void dump_ptd_queue(struct isp1362_ep_queue *epq)
-{
- struct isp1362_ep *ep;
- int dbg = dbg_level;
-
- dbg_level = 1;
- list_for_each_entry(ep, &epq->active, active) {
- dump_ptd(&ep->ptd);
- dump_data(ep->data, ep->length);
- }
- dbg_level = dbg;
-}
-#else
-#define dump_ptd(ptd) do {} while (0)
-#define dump_ptd_in_data(ptd, buf) do {} while (0)
-#define dump_ptd_out_data(ptd, buf) do {} while (0)
-#define dump_ptd_data(ptd, buf) do {} while (0)
-#define dump_ptd_queue(epq) do {} while (0)
-#endif
diff --git a/drivers/usb/host/max3421-hcd.c b/drivers/usb/host/max3421-hcd.c
index 0881fdd1823e..0e17c988d36a 100644
--- a/drivers/usb/host/max3421-hcd.c
+++ b/drivers/usb/host/max3421-hcd.c
@@ -1523,7 +1523,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), GFP_ATOMIC);
+ max3421_ep = kzalloc_obj(struct max3421_ep, GFP_ATOMIC);
if (!max3421_ep) {
retval = -ENOMEM;
goto out;
@@ -1878,10 +1878,10 @@ max3421_probe(struct spi_device *spi)
INIT_LIST_HEAD(&max3421_hcd->ep_list);
spi_set_drvdata(spi, max3421_hcd);
- max3421_hcd->tx = kmalloc(sizeof(*max3421_hcd->tx), GFP_KERNEL);
+ max3421_hcd->tx = kmalloc_obj(*max3421_hcd->tx);
if (!max3421_hcd->tx)
goto error;
- max3421_hcd->rx = kmalloc(sizeof(*max3421_hcd->rx), GFP_KERNEL);
+ max3421_hcd->rx = kmalloc_obj(*max3421_hcd->rx);
if (!max3421_hcd->rx)
goto error;
@@ -1916,7 +1916,7 @@ error:
if (hcd) {
kfree(max3421_hcd->tx);
kfree(max3421_hcd->rx);
- if (max3421_hcd->spi_thread)
+ if (!IS_ERR_OR_NULL(max3421_hcd->spi_thread))
kthread_stop(max3421_hcd->spi_thread);
usb_put_hcd(hcd);
}
@@ -1946,6 +1946,12 @@ max3421_remove(struct spi_device *spi)
usb_put_hcd(hcd);
}
+static const struct spi_device_id max3421_spi_ids[] = {
+ { "max3421" },
+ { },
+};
+MODULE_DEVICE_TABLE(spi, max3421_spi_ids);
+
static const struct of_device_id max3421_of_match_table[] = {
{ .compatible = "maxim,max3421", },
{},
@@ -1955,6 +1961,7 @@ MODULE_DEVICE_TABLE(of, max3421_of_match_table);
static struct spi_driver max3421_driver = {
.probe = max3421_probe,
.remove = max3421_remove,
+ .id_table = max3421_spi_ids,
.driver = {
.name = "max3421-hcd",
.of_match_table = max3421_of_match_table,
diff --git a/drivers/usb/host/octeon-hcd.c b/drivers/usb/host/octeon-hcd.c
index 361d33b0c4d2..34ce771fa0f5 100644
--- a/drivers/usb/host/octeon-hcd.c
+++ b/drivers/usb/host/octeon-hcd.c
@@ -1098,7 +1098,7 @@ static struct cvmx_usb_pipe *cvmx_usb_open_pipe(struct octeon_hcd *usb,
{
struct cvmx_usb_pipe *pipe;
- pipe = kzalloc(sizeof(*pipe), GFP_ATOMIC);
+ pipe = kzalloc_obj(*pipe, GFP_ATOMIC);
if (!pipe)
return NULL;
if ((device_speed == CVMX_USB_SPEED_HIGH) &&
@@ -2138,7 +2138,7 @@ static struct cvmx_usb_transaction *cvmx_usb_submit_transaction(
if (unlikely(pipe->transfer_type != type))
return NULL;
- transaction = kzalloc(sizeof(*transaction), GFP_ATOMIC);
+ transaction = kzalloc_obj(*transaction, GFP_ATOMIC);
if (unlikely(!transaction))
return NULL;
@@ -3180,9 +3180,8 @@ static int octeon_usb_urb_enqueue(struct usb_hcd *hcd,
* Allocate a structure to use for our private list of
* isochronous packets.
*/
- iso_packet = kmalloc_array(urb->number_of_packets,
- sizeof(struct cvmx_usb_iso_packet),
- GFP_ATOMIC);
+ iso_packet = kmalloc_objs(struct cvmx_usb_iso_packet,
+ urb->number_of_packets, GFP_ATOMIC);
if (iso_packet) {
int i;
/* Fill the list with the data from the URB */
diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c
index 5df793dcb25d..12fdb18934cf 100644
--- a/drivers/usb/host/ohci-at91.c
+++ b/drivers/usb/host/ohci-at91.c
@@ -193,7 +193,7 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver,
if (irq < 0)
return irq;
- hcd = usb_create_hcd(driver, dev, "at91");
+ hcd = usb_create_hcd(driver, dev, dev_name(dev));
if (!hcd)
return -ENOMEM;
ohci_at91 = hcd_to_ohci_at91_priv(hcd);
diff --git a/drivers/usb/host/ohci-da8xx.c b/drivers/usb/host/ohci-da8xx.c
index 3c5ca2d7c92e..0938c0e7a8b6 100644
--- a/drivers/usb/host/ohci-da8xx.c
+++ b/drivers/usb/host/ohci-da8xx.c
@@ -18,7 +18,6 @@
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/phy/phy.h>
-#include <linux/platform_data/usb-davinci.h>
#include <linux/regulator/consumer.h>
#include <linux/usb.h>
#include <linux/usb/hcd.h>
@@ -166,17 +165,6 @@ static int ohci_da8xx_has_oci(struct usb_hcd *hcd)
return 0;
}
-static int ohci_da8xx_has_potpgt(struct usb_hcd *hcd)
-{
- struct device *dev = hcd->self.controller;
- struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev);
-
- if (hub && hub->potpgt)
- return 1;
-
- return 0;
-}
-
static int ohci_da8xx_regulator_event(struct notifier_block *nb,
unsigned long event, void *data)
{
@@ -228,7 +216,6 @@ static int ohci_da8xx_register_notify(struct usb_hcd *hcd)
static int ohci_da8xx_reset(struct usb_hcd *hcd)
{
struct device *dev = hcd->self.controller;
- struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev);
struct ohci_hcd *ohci = hcd_to_ohci(hcd);
int result;
u32 rh_a;
@@ -266,10 +253,6 @@ static int ohci_da8xx_reset(struct usb_hcd *hcd)
rh_a &= ~RH_A_NOCP;
rh_a |= RH_A_OCPM;
}
- if (ohci_da8xx_has_potpgt(hcd)) {
- rh_a &= ~RH_A_POTPGT;
- rh_a |= hub->potpgt << 24;
- }
ohci_writel(ohci, rh_a, &ohci->regs->roothub.a);
return result;
diff --git a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c
index 76bc8d56325d..9e0e06bbc570 100644
--- a/drivers/usb/host/ohci-dbg.c
+++ b/drivers/usb/host/ohci-dbg.c
@@ -492,7 +492,7 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
char *next;
unsigned i;
- seen = kmalloc_array(DBG_SCHED_LIMIT, sizeof(*seen), GFP_ATOMIC);
+ seen = kmalloc_objs(*seen, DBG_SCHED_LIMIT, GFP_ATOMIC);
if (!seen)
return 0;
seen_count = 0;
@@ -667,7 +667,7 @@ static struct debug_buffer *alloc_buffer(struct ohci_hcd *ohci,
{
struct debug_buffer *buf;
- buf = kzalloc(sizeof(struct debug_buffer), GFP_KERNEL);
+ buf = kzalloc_obj(struct debug_buffer);
if (buf) {
buf->ohci = ohci;
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index 9b24181fee60..129468b1efc2 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -190,7 +190,7 @@ static int ohci_urb_enqueue (
}
/* allocate the private part of the URB */
- urb_priv = kzalloc(struct_size(urb_priv, td, size), mem_flags);
+ urb_priv = kzalloc_flex(*urb_priv, td, size, mem_flags);
if (!urb_priv)
return -ENOMEM;
INIT_LIST_HEAD (&urb_priv->pending);
@@ -746,7 +746,8 @@ static int ohci_start(struct usb_hcd *hcd)
*/
static void io_watchdog_func(struct timer_list *t)
{
- struct ohci_hcd *ohci = from_timer(ohci, t, io_watchdog);
+ struct ohci_hcd *ohci = timer_container_of(ohci, t,
+ io_watchdog);
bool takeback_all_pending = false;
u32 status;
u32 head;
@@ -1003,7 +1004,7 @@ static void ohci_stop (struct usb_hcd *hcd)
if (quirk_nec(ohci))
flush_work(&ohci->nec_work);
- del_timer_sync(&ohci->io_watchdog);
+ timer_delete_sync(&ohci->io_watchdog);
ohci->prev_frame_no = IO_WATCHDOG_OFF;
ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable);
@@ -1281,7 +1282,6 @@ static int __init ohci_hcd_mod_init(void)
pr_debug ("%s: block sizes: ed %zd td %zd\n", hcd_name,
sizeof (struct ed), sizeof (struct td));
- set_bit(USB_OHCI_LOADED, &usb_hcds_loaded);
ohci_debug_root = debugfs_create_dir("ohci", usb_debug_root);
@@ -1331,7 +1331,6 @@ static int __init ohci_hcd_mod_init(void)
debugfs_remove(ohci_debug_root);
ohci_debug_root = NULL;
- clear_bit(USB_OHCI_LOADED, &usb_hcds_loaded);
return retval;
}
module_init(ohci_hcd_mod_init);
@@ -1351,7 +1350,6 @@ static void __exit ohci_hcd_mod_exit(void)
ps3_ohci_driver_unregister(&PS3_SYSTEM_BUS_DRIVER);
#endif
debugfs_remove(ohci_debug_root);
- clear_bit(USB_OHCI_LOADED, &usb_hcds_loaded);
}
module_exit(ohci_hcd_mod_exit);
diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c
index 90cee192e96d..b3d734ab6201 100644
--- a/drivers/usb/host/ohci-hub.c
+++ b/drivers/usb/host/ohci-hub.c
@@ -315,7 +315,7 @@ static int ohci_bus_suspend (struct usb_hcd *hcd)
spin_unlock_irq (&ohci->lock);
if (rc == 0) {
- del_timer_sync(&ohci->io_watchdog);
+ timer_delete_sync(&ohci->io_watchdog);
ohci->prev_frame_no = IO_WATCHDOG_OFF;
}
return rc;
diff --git a/drivers/usb/host/ohci-nxp.c b/drivers/usb/host/ohci-nxp.c
index 24d5a1dc5056..7663f2aa35e9 100644
--- a/drivers/usb/host/ohci-nxp.c
+++ b/drivers/usb/host/ohci-nxp.c
@@ -169,13 +169,13 @@ static int ohci_hcd_nxp_probe(struct platform_device *pdev)
ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
if (ret)
- goto fail_disable;
+ goto err_put_client;
dev_dbg(&pdev->dev, "%s: " DRIVER_DESC " (nxp)\n", hcd_name);
if (usb_disabled()) {
dev_err(&pdev->dev, "USB is disabled\n");
ret = -ENODEV;
- goto fail_disable;
+ goto err_put_client;
}
/* Enable USB host clock */
@@ -183,7 +183,7 @@ static int ohci_hcd_nxp_probe(struct platform_device *pdev)
if (IS_ERR(usb_host_clk)) {
dev_err(&pdev->dev, "failed to acquire and start USB OHCI clock\n");
ret = PTR_ERR(usb_host_clk);
- goto fail_disable;
+ goto err_put_client;
}
isp1301_configure();
@@ -192,13 +192,13 @@ static int ohci_hcd_nxp_probe(struct platform_device *pdev)
if (!hcd) {
dev_err(&pdev->dev, "Failed to allocate HC buffer\n");
ret = -ENOMEM;
- goto fail_disable;
+ goto err_put_client;
}
hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(hcd->regs)) {
ret = PTR_ERR(hcd->regs);
- goto fail_resource;
+ goto err_put_hcd;
}
hcd->rsrc_start = res->start;
hcd->rsrc_len = resource_size(res);
@@ -206,7 +206,7 @@ static int ohci_hcd_nxp_probe(struct platform_device *pdev)
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
ret = -ENXIO;
- goto fail_resource;
+ goto err_put_hcd;
}
ohci_nxp_start_hc();
@@ -220,9 +220,10 @@ static int ohci_hcd_nxp_probe(struct platform_device *pdev)
}
ohci_nxp_stop_hc();
-fail_resource:
+err_put_hcd:
usb_put_hcd(hcd);
-fail_disable:
+err_put_client:
+ put_device(&isp1301_i2c_client->dev);
isp1301_i2c_client = NULL;
return ret;
}
@@ -234,6 +235,7 @@ static void ohci_hcd_nxp_remove(struct platform_device *pdev)
usb_remove_hcd(hcd);
ohci_nxp_stop_hc();
usb_put_hcd(hcd);
+ put_device(&isp1301_i2c_client->dev);
isp1301_i2c_client = NULL;
}
diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c
index 900ea0d368e0..9f0a6b27e47c 100644
--- a/drivers/usb/host/ohci-pci.c
+++ b/drivers/usb/host/ohci-pci.c
@@ -165,6 +165,25 @@ static int ohci_quirk_amd700(struct usb_hcd *hcd)
return 0;
}
+static int ohci_quirk_loongson(struct usb_hcd *hcd)
+{
+ struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
+
+ /*
+ * Loongson's LS7A OHCI controller (rev 0x02) has a
+ * flaw. MMIO register with offset 0x60/64 is treated
+ * as legacy PS2-compatible keyboard/mouse interface.
+ * Since OHCI only use 4KB BAR resource, LS7A OHCI's
+ * 32KB BAR is wrapped around (the 2nd 4KB BAR space
+ * is the same as the 1st 4KB internally). So add 4KB
+ * offset (0x1000) to the OHCI registers as a quirk.
+ */
+ if (pdev->revision == 0x2)
+ hcd->regs += SZ_4K; /* SZ_4K = 0x1000 */
+
+ return 0;
+}
+
static int ohci_quirk_qemu(struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci(hcd);
@@ -225,6 +244,10 @@ static const struct pci_device_id ohci_pci_quirks[] = {
.driver_data = (unsigned long)ohci_quirk_amd700,
},
{
+ PCI_DEVICE(PCI_VENDOR_ID_LOONGSON, 0x7a24),
+ .driver_data = (unsigned long)ohci_quirk_loongson,
+ },
+ {
.vendor = PCI_VENDOR_ID_APPLE,
.device = 0x003f,
.subvendor = PCI_SUBVENDOR_ID_REDHAT_QUMRANET,
diff --git a/drivers/usb/host/ohci-platform.c b/drivers/usb/host/ohci-platform.c
index f47ae12cde6a..c801527d5bd2 100644
--- a/drivers/usb/host/ohci-platform.c
+++ b/drivers/usb/host/ohci-platform.c
@@ -69,8 +69,7 @@ static void ohci_platform_power_off(struct platform_device *dev)
int clk;
for (clk = OHCI_MAX_CLKS - 1; clk >= 0; clk--)
- if (priv->clks[clk])
- clk_disable_unprepare(priv->clks[clk]);
+ clk_disable_unprepare(priv->clks[clk]);
}
static struct hc_driver __read_mostly ohci_platform_hc_driver;
@@ -271,6 +270,7 @@ static int ohci_platform_suspend(struct device *dev)
struct usb_hcd *hcd = dev_get_drvdata(dev);
struct usb_ohci_pdata *pdata = dev->platform_data;
struct platform_device *pdev = to_platform_device(dev);
+ struct ohci_platform_priv *priv = hcd_to_ohci_priv(hcd);
bool do_wakeup = device_may_wakeup(dev);
int ret;
@@ -281,6 +281,14 @@ static int ohci_platform_suspend(struct device *dev)
if (pdata->power_suspend)
pdata->power_suspend(pdev);
+ ret = reset_control_assert(priv->resets);
+ if (ret) {
+ if (pdata->power_on)
+ pdata->power_on(pdev);
+
+ ohci_resume(hcd, false);
+ }
+
return ret;
}
@@ -289,11 +297,19 @@ static int ohci_platform_resume_common(struct device *dev, bool hibernated)
struct usb_hcd *hcd = dev_get_drvdata(dev);
struct usb_ohci_pdata *pdata = dev_get_platdata(dev);
struct platform_device *pdev = to_platform_device(dev);
+ struct ohci_platform_priv *priv = hcd_to_ohci_priv(hcd);
+ int err;
+
+ err = reset_control_deassert(priv->resets);
+ if (err)
+ return err;
if (pdata->power_on) {
- int err = pdata->power_on(pdev);
- if (err < 0)
+ err = pdata->power_on(pdev);
+ if (err < 0) {
+ reset_control_assert(priv->resets);
return err;
+ }
}
ohci_resume(hcd, hibernated);
@@ -376,3 +392,4 @@ MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_AUTHOR("Hauke Mehrtens");
MODULE_AUTHOR("Alan Stern");
MODULE_LICENSE("GPL");
+MODULE_SOFTDEP("pre: ehci_platform");
diff --git a/drivers/usb/host/ohci-s3c2410.c b/drivers/usb/host/ohci-s3c2410.c
index 66d970854357..e623e24d3f8e 100644
--- a/drivers/usb/host/ohci-s3c2410.c
+++ b/drivers/usb/host/ohci-s3c2410.c
@@ -448,13 +448,6 @@ static const struct dev_pm_ops ohci_hcd_s3c2410_pm_ops = {
.resume = ohci_hcd_s3c2410_drv_resume,
};
-static const struct of_device_id ohci_hcd_s3c2410_dt_ids[] = {
- { .compatible = "samsung,s3c2410-ohci" },
- { /* sentinel */ }
-};
-
-MODULE_DEVICE_TABLE(of, ohci_hcd_s3c2410_dt_ids);
-
static struct platform_driver ohci_hcd_s3c2410_driver = {
.probe = ohci_hcd_s3c2410_probe,
.remove = ohci_hcd_s3c2410_remove,
@@ -462,7 +455,6 @@ static struct platform_driver ohci_hcd_s3c2410_driver = {
.driver = {
.name = "s3c2410-ohci",
.pm = &ohci_hcd_s3c2410_pm_ops,
- .of_match_table = ohci_hcd_s3c2410_dt_ids,
},
};
diff --git a/drivers/usb/host/ohci-spear.c b/drivers/usb/host/ohci-spear.c
index d7131e5a4477..6843d7cb3f9f 100644
--- a/drivers/usb/host/ohci-spear.c
+++ b/drivers/usb/host/ohci-spear.c
@@ -103,8 +103,7 @@ static void spear_ohci_hcd_drv_remove(struct platform_device *pdev)
struct spear_ohci *sohci_p = to_spear_ohci(hcd);
usb_remove_hcd(hcd);
- if (sohci_p->clk)
- clk_disable_unprepare(sohci_p->clk);
+ clk_disable_unprepare(sohci_p->clk);
usb_put_hcd(hcd);
}
diff --git a/drivers/usb/host/oxu210hp-hcd.c b/drivers/usb/host/oxu210hp-hcd.c
index fce800ba4c61..ca96067e6d6d 100644
--- a/drivers/usb/host/oxu210hp-hcd.c
+++ b/drivers/usb/host/oxu210hp-hcd.c
@@ -1127,7 +1127,7 @@ static void ehci_mem_cleanup(struct oxu_hcd *oxu)
qh_put(oxu->async);
oxu->async = NULL;
- del_timer(&oxu->urb_timer);
+ timer_delete(&oxu->urb_timer);
oxu->periodic = NULL;
@@ -1149,7 +1149,7 @@ static int ehci_mem_init(struct oxu_hcd *oxu, gfp_t flags)
for (i = 0; i < QTD_NUM; i++)
oxu->qtd_used[i] = 0;
- oxu->murb_pool = kcalloc(MURB_NUM, sizeof(struct oxu_murb), flags);
+ oxu->murb_pool = kzalloc_objs(struct oxu_murb, MURB_NUM, flags);
if (!oxu->murb_pool)
goto fail;
@@ -2955,7 +2955,7 @@ static irqreturn_t oxu_irq(struct usb_hcd *hcd)
static void oxu_watchdog(struct timer_list *t)
{
- struct oxu_hcd *oxu = from_timer(oxu, t, watchdog);
+ struct oxu_hcd *oxu = timer_container_of(oxu, t, watchdog);
unsigned long flags;
spin_lock_irqsave(&oxu->lock, flags);
@@ -3154,7 +3154,7 @@ static void oxu_stop(struct usb_hcd *hcd)
ehci_port_power(oxu, 0);
/* no more interrupts ... */
- del_timer_sync(&oxu->watchdog);
+ timer_delete_sync(&oxu->watchdog);
spin_lock_irq(&oxu->lock);
if (HC_IS_RUNNING(hcd->state))
@@ -3887,7 +3887,7 @@ static int oxu_bus_suspend(struct usb_hcd *hcd)
spin_unlock_irq(&oxu->lock);
/* turn off now-idle HC */
- del_timer_sync(&oxu->watchdog);
+ timer_delete_sync(&oxu->watchdog);
spin_lock_irq(&oxu->lock);
ehci_halt(oxu);
hcd->state = HC_STATE_SUSPENDED;
diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c
index a44992e2561b..0a26489a40d0 100644
--- a/drivers/usb/host/r8a66597-hcd.c
+++ b/drivers/usb/host/r8a66597-hcd.c
@@ -337,7 +337,7 @@ static int make_r8a66597_device(struct r8a66597 *r8a66597,
struct r8a66597_device *dev;
int usb_address = urb->setup_packet[2]; /* urb->pipe is address 0 */
- dev = kzalloc(sizeof(struct r8a66597_device), GFP_ATOMIC);
+ dev = kzalloc_obj(struct r8a66597_device, GFP_ATOMIC);
if (dev == NULL)
return -ENOMEM;
@@ -1720,7 +1720,8 @@ static void r8a66597_root_hub_control(struct r8a66597 *r8a66597, int port)
static void r8a66597_interval_timer(struct timer_list *t)
{
- struct r8a66597_timers *timers = from_timer(timers, t, interval);
+ struct r8a66597_timers *timers = timer_container_of(timers, t,
+ interval);
struct r8a66597 *r8a66597 = timers->r8a66597;
unsigned long flags;
u16 pipenum;
@@ -1744,7 +1745,7 @@ static void r8a66597_interval_timer(struct timer_list *t)
static void r8a66597_td_timer(struct timer_list *t)
{
- struct r8a66597_timers *timers = from_timer(timers, t, td);
+ struct r8a66597_timers *timers = timer_container_of(timers, t, td);
struct r8a66597 *r8a66597 = timers->r8a66597;
unsigned long flags;
u16 pipenum;
@@ -1798,7 +1799,7 @@ static void r8a66597_td_timer(struct timer_list *t)
static void r8a66597_timer(struct timer_list *t)
{
- struct r8a66597 *r8a66597 = from_timer(r8a66597, t, rh_timer);
+ struct r8a66597 *r8a66597 = timer_container_of(r8a66597, t, rh_timer);
unsigned long flags;
int port;
@@ -1858,7 +1859,7 @@ static struct r8a66597_td *r8a66597_make_td(struct r8a66597 *r8a66597,
struct r8a66597_td *td;
u16 pipenum;
- td = kzalloc(sizeof(struct r8a66597_td), GFP_ATOMIC);
+ td = kzalloc_obj(struct r8a66597_td, GFP_ATOMIC);
if (td == NULL)
return NULL;
@@ -1900,8 +1901,7 @@ static int r8a66597_urb_enqueue(struct usb_hcd *hcd,
goto error_not_linked;
if (!hep->hcpriv) {
- hep->hcpriv = kzalloc(sizeof(struct r8a66597_pipe),
- GFP_ATOMIC);
+ hep->hcpriv = kzalloc_obj(struct r8a66597_pipe, GFP_ATOMIC);
if (!hep->hcpriv) {
ret = -ENOMEM;
goto error;
@@ -2384,7 +2384,7 @@ static void r8a66597_remove(struct platform_device *pdev)
struct r8a66597 *r8a66597 = platform_get_drvdata(pdev);
struct usb_hcd *hcd = r8a66597_to_hcd(r8a66597);
- del_timer_sync(&r8a66597->rh_timer);
+ timer_delete_sync(&r8a66597->rh_timer);
usb_remove_hcd(hcd);
iounmap(r8a66597->reg);
if (r8a66597->pdata->on_chip)
diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c
index fa2e4badd288..4ae47edd4b8b 100644
--- a/drivers/usb/host/sl811-hcd.c
+++ b/drivers/usb/host/sl811-hcd.c
@@ -814,7 +814,7 @@ static int sl811h_urb_enqueue(
/* avoid all allocations within spinlocks */
if (!hep->hcpriv) {
- ep = kzalloc(sizeof *ep, mem_flags);
+ ep = kzalloc_obj(*ep, mem_flags);
if (ep == NULL)
return -ENOMEM;
}
@@ -1124,7 +1124,7 @@ sl811h_hub_descriptor (
static void
sl811h_timer(struct timer_list *t)
{
- struct sl811 *sl811 = from_timer(sl811, t, timer);
+ struct sl811 *sl811 = timer_container_of(sl811, t, timer);
unsigned long flags;
u8 irqstat;
u8 signaling = sl811->ctrl1 & SL11H_CTL1MASK_FORCE;
@@ -1515,7 +1515,7 @@ sl811h_stop(struct usb_hcd *hcd)
struct sl811 *sl811 = hcd_to_sl811(hcd);
unsigned long flags;
- del_timer_sync(&hcd->rh_timer);
+ timer_delete_sync(&hcd->rh_timer);
spin_lock_irqsave(&sl811->lock, flags);
port_power(sl811, 0);
@@ -1748,6 +1748,7 @@ sl811h_suspend(struct platform_device *dev, pm_message_t state)
break;
case PM_EVENT_SUSPEND:
case PM_EVENT_HIBERNATE:
+ case PM_EVENT_POWEROFF:
case PM_EVENT_PRETHAW: /* explicitly discard hw state */
port_power(sl811, 0);
break;
diff --git a/drivers/usb/host/sl811_cs.c b/drivers/usb/host/sl811_cs.c
index 16d157013018..ada91ca33f65 100644
--- a/drivers/usb/host/sl811_cs.c
+++ b/drivers/usb/host/sl811_cs.c
@@ -178,7 +178,7 @@ static int sl811_cs_probe(struct pcmcia_device *link)
{
local_info_t *local;
- local = kzalloc(sizeof(local_info_t), GFP_KERNEL);
+ local = kzalloc_obj(local_info_t);
if (!local)
return -ENOMEM;
local->p_dev = link;
diff --git a/drivers/usb/host/uhci-debug.c b/drivers/usb/host/uhci-debug.c
index c4e67c4b51f6..e91ec6a4fa74 100644
--- a/drivers/usb/host/uhci-debug.c
+++ b/drivers/usb/host/uhci-debug.c
@@ -561,7 +561,7 @@ static int uhci_debug_open(struct inode *inode, struct file *file)
struct uhci_debug *up;
unsigned long flags;
- up = kmalloc(sizeof(*up), GFP_KERNEL);
+ up = kmalloc_obj(*up);
if (!up)
return -ENOMEM;
diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c
index fd2408b553cf..433d64a5fab5 100644
--- a/drivers/usb/host/uhci-hcd.c
+++ b/drivers/usb/host/uhci-hcd.c
@@ -603,8 +603,7 @@ static int uhci_start(struct usb_hcd *hcd)
goto err_alloc_frame;
}
- uhci->frame_cpu = kcalloc(UHCI_NUMFRAMES, sizeof(*uhci->frame_cpu),
- GFP_KERNEL);
+ uhci->frame_cpu = kzalloc_objs(*uhci->frame_cpu, UHCI_NUMFRAMES);
if (!uhci->frame_cpu)
goto err_alloc_frame_cpu;
@@ -716,7 +715,7 @@ static void uhci_stop(struct usb_hcd *hcd)
spin_unlock_irq(&uhci->lock);
synchronize_irq(hcd->irq);
- del_timer_sync(&uhci->fsbr_timer);
+ timer_delete_sync(&uhci->fsbr_timer);
release_uhci(uhci);
}
@@ -867,8 +866,6 @@ static int __init uhci_hcd_init(void)
if (usb_disabled())
return -ENODEV;
- set_bit(USB_UHCI_LOADED, &usb_hcds_loaded);
-
#ifdef CONFIG_DYNAMIC_DEBUG
errbuf = kmalloc(ERRBUF_LEN, GFP_KERNEL);
if (!errbuf)
@@ -912,8 +909,6 @@ up_failed:
errbuf_failed:
#endif
-
- clear_bit(USB_UHCI_LOADED, &usb_hcds_loaded);
return retval;
}
@@ -930,7 +925,6 @@ static void __exit uhci_hcd_cleanup(void)
#ifdef CONFIG_DYNAMIC_DEBUG
kfree(errbuf);
#endif
- clear_bit(USB_UHCI_LOADED, &usb_hcds_loaded);
}
module_init(uhci_hcd_init);
diff --git a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h
index 13ee2a6144b2..4326d1f3ca76 100644
--- a/drivers/usb/host/uhci-hcd.h
+++ b/drivers/usb/host/uhci-hcd.h
@@ -445,6 +445,7 @@ struct uhci_hcd {
short load[MAX_PHASE]; /* Periodic allocations */
struct clk *clk; /* (optional) clock source */
+ struct reset_control *rsts; /* (optional) clock reset */
/* Reset host controller */
void (*reset_hc) (struct uhci_hcd *uhci);
diff --git a/drivers/usb/host/uhci-platform.c b/drivers/usb/host/uhci-platform.c
index a7c934404ebc..f4419d4526c4 100644
--- a/drivers/usb/host/uhci-platform.c
+++ b/drivers/usb/host/uhci-platform.c
@@ -11,6 +11,7 @@
#include <linux/of.h>
#include <linux/device.h>
#include <linux/platform_device.h>
+#include <linux/reset.h>
static int uhci_platform_init(struct usb_hcd *hcd)
{
@@ -67,6 +68,7 @@ static const struct hc_driver uhci_platform_hc_driver = {
static int uhci_hcd_platform_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
+ bool dma_mask_64 = false;
struct usb_hcd *hcd;
struct uhci_hcd *uhci;
struct resource *res;
@@ -80,7 +82,11 @@ static int uhci_hcd_platform_probe(struct platform_device *pdev)
* Since shared usb code relies on it, set it here for now.
* Once we have dma capability bindings this can go away.
*/
- ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+ if (of_device_get_match_data(&pdev->dev))
+ dma_mask_64 = true;
+
+ ret = dma_coerce_mask_and_coherent(&pdev->dev,
+ dma_mask_64 ? DMA_BIT_MASK(64) : DMA_BIT_MASK(32));
if (ret)
return ret;
@@ -113,7 +119,8 @@ static int uhci_hcd_platform_probe(struct platform_device *pdev)
}
if (of_device_is_compatible(np, "aspeed,ast2400-uhci") ||
of_device_is_compatible(np, "aspeed,ast2500-uhci") ||
- of_device_is_compatible(np, "aspeed,ast2600-uhci")) {
+ of_device_is_compatible(np, "aspeed,ast2600-uhci") ||
+ of_device_is_compatible(np, "aspeed,ast2700-uhci")) {
uhci->is_aspeed = 1;
dev_info(&pdev->dev,
"Enabled Aspeed implementation workarounds\n");
@@ -121,7 +128,7 @@ static int uhci_hcd_platform_probe(struct platform_device *pdev)
}
/* Get and enable clock if any specified */
- uhci->clk = devm_clk_get(&pdev->dev, NULL);
+ uhci->clk = devm_clk_get_optional(&pdev->dev, NULL);
if (IS_ERR(uhci->clk)) {
ret = PTR_ERR(uhci->clk);
goto err_rmr;
@@ -132,17 +139,28 @@ static int uhci_hcd_platform_probe(struct platform_device *pdev)
goto err_rmr;
}
+ uhci->rsts = devm_reset_control_array_get_optional_shared(&pdev->dev);
+ if (IS_ERR(uhci->rsts)) {
+ ret = PTR_ERR(uhci->rsts);
+ goto err_clk;
+ }
+ ret = reset_control_deassert(uhci->rsts);
+ if (ret)
+ goto err_clk;
+
ret = platform_get_irq(pdev, 0);
if (ret < 0)
- goto err_clk;
+ goto err_reset;
ret = usb_add_hcd(hcd, ret, IRQF_SHARED);
if (ret)
- goto err_clk;
+ goto err_reset;
device_wakeup_enable(hcd->self.controller);
return 0;
+err_reset:
+ reset_control_assert(uhci->rsts);
err_clk:
clk_disable_unprepare(uhci->clk);
err_rmr:
@@ -156,6 +174,7 @@ static void uhci_hcd_platform_remove(struct platform_device *pdev)
struct usb_hcd *hcd = platform_get_drvdata(pdev);
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+ reset_control_assert(uhci->rsts);
clk_disable_unprepare(uhci->clk);
usb_remove_hcd(hcd);
usb_put_hcd(hcd);
@@ -178,6 +197,7 @@ static void uhci_hcd_platform_shutdown(struct platform_device *op)
static const struct of_device_id platform_uhci_ids[] = {
{ .compatible = "generic-uhci", },
{ .compatible = "platform-uhci", },
+ { .compatible = "aspeed,ast2700-uhci", .data = (void *)1 },
{}
};
MODULE_DEVICE_TABLE(of, platform_uhci_ids);
@@ -191,3 +211,4 @@ static struct platform_driver uhci_platform_driver = {
.of_match_table = platform_uhci_ids,
},
};
+MODULE_SOFTDEP("pre: ehci_platform");
diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c
index 35fcb826152c..9480d4ff0111 100644
--- a/drivers/usb/host/uhci-q.c
+++ b/drivers/usb/host/uhci-q.c
@@ -84,14 +84,14 @@ static void uhci_urbp_wants_fsbr(struct uhci_hcd *uhci, struct urb_priv *urbp)
uhci_fsbr_on(uhci);
else if (uhci->fsbr_expiring) {
uhci->fsbr_expiring = 0;
- del_timer(&uhci->fsbr_timer);
+ timer_delete(&uhci->fsbr_timer);
}
}
}
static void uhci_fsbr_timeout(struct timer_list *t)
{
- struct uhci_hcd *uhci = from_timer(uhci, t, fsbr_timer);
+ struct uhci_hcd *uhci = timer_container_of(uhci, t, fsbr_timer);
unsigned long flags;
spin_lock_irqsave(&uhci->lock, flags);
diff --git a/drivers/usb/host/xen-hcd.c b/drivers/usb/host/xen-hcd.c
index 46fdab940092..0a94d302911a 100644
--- a/drivers/usb/host/xen-hcd.c
+++ b/drivers/usb/host/xen-hcd.c
@@ -327,7 +327,7 @@ static int xenhcd_bus_suspend(struct usb_hcd *hcd)
}
spin_unlock_irq(&info->lock);
- del_timer_sync(&info->watchdog);
+ timer_delete_sync(&info->watchdog);
return ret;
}
@@ -1258,7 +1258,7 @@ static void xenhcd_disconnect(struct xenbus_device *dev)
static void xenhcd_watchdog(struct timer_list *timer)
{
- struct xenhcd_info *info = from_timer(info, timer, watchdog);
+ struct xenhcd_info *info = timer_container_of(info, timer, watchdog);
unsigned long flags;
spin_lock_irqsave(&info->lock, flags);
@@ -1307,7 +1307,7 @@ static void xenhcd_stop(struct usb_hcd *hcd)
{
struct xenhcd_info *info = xenhcd_hcd_to_info(hcd);
- del_timer_sync(&info->watchdog);
+ timer_delete_sync(&info->watchdog);
spin_lock_irq(&info->lock);
/* cancel all urbs */
hcd->state = HC_STATE_HALT;
@@ -1388,7 +1388,7 @@ static int xenhcd_get_frame(struct usb_hcd *hcd)
return 0;
}
-static struct hc_driver xenhcd_usb20_hc_driver = {
+static const struct hc_driver xenhcd_usb20_hc_driver = {
.description = "xen-hcd",
.product_desc = "Xen USB2.0 Virtual Host Controller",
.hcd_priv_size = sizeof(struct xenhcd_info),
@@ -1413,7 +1413,7 @@ static struct hc_driver xenhcd_usb20_hc_driver = {
#endif
};
-static struct hc_driver xenhcd_usb11_hc_driver = {
+static const struct hc_driver xenhcd_usb11_hc_driver = {
.description = "xen-hcd",
.product_desc = "Xen USB1.1 Virtual Host Controller",
.hcd_priv_size = sizeof(struct xenhcd_info),
diff --git a/drivers/usb/host/xhci-caps.h b/drivers/usb/host/xhci-caps.h
index f6b9a00a0ab9..2f59b6ab1e45 100644
--- a/drivers/usb/host/xhci-caps.h
+++ b/drivers/usb/host/xhci-caps.h
@@ -1,91 +1,120 @@
/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * xHCI Host Controller Capability Registers.
+ * xHCI Specification Section 5.3, Revision 1.2.
+ */
+
+#include <linux/bits.h>
-/* hc_capbase bitmasks */
-/* bits 7:0 - how long is the Capabilities register */
-#define HC_LENGTH(p) XHCI_HC_LENGTH(p)
-/* bits 31:16 */
+/* hc_capbase - bitmasks */
+/* bits 7:0 - Capability Registers Length */
+#define HC_LENGTH(p) ((p) & 0xff)
+/* bits 15:8 - Rsvd */
+/* bits 31:16 - Host Controller Interface Version Number */
#define HC_VERSION(p) (((p) >> 16) & 0xffff)
/* HCSPARAMS1 - hcs_params1 - bitmasks */
-/* bits 0:7, Max Device Slots */
+/* bits 7:0 - Number of Device Slots */
#define HCS_MAX_SLOTS(p) (((p) >> 0) & 0xff)
#define HCS_SLOTS_MASK 0xff
-/* bits 8:18, Max Interrupters */
+/* bits 18:8 - Number of Interrupters, max values is 1024 */
#define HCS_MAX_INTRS(p) (((p) >> 8) & 0x7ff)
-/* bits 24:31, Max Ports - max value is 0x7F = 127 ports */
-#define HCS_MAX_PORTS(p) (((p) >> 24) & 0x7f)
+/* bits 31:24, Max Ports - max value is 255 */
+#define HCS_MAX_PORTS(p) (((p) >> 24) & 0xff)
/* HCSPARAMS2 - hcs_params2 - bitmasks */
-/* bits 0:3, frames or uframes that SW needs to queue transactions
- * ahead of the HW to meet periodic deadlines */
-#define HCS_IST(p) (((p) >> 0) & 0xf)
-/* bits 4:7, max number of Event Ring segments */
+/*
+ * bits 3:0 - Isochronous Scheduling Threshold, frames or uframes that SW
+ * needs to queue transactions ahead of the HW to meet periodic deadlines.
+ * - Bits 2:0: Threshold value
+ * - Bit 3: Unit indicator
+ * - '1': Threshold in Frames
+ * - '0': Threshold in Microframes (uframes)
+ * Note: 1 Frame = 8 Microframes
+ * xHCI specification section 5.3.4.
+ */
+#define HCS_IST_VALUE(p) ((p) & 0x7)
+#define HCS_IST_UNIT BIT(3)
+/* bits 7:4 - Event Ring Segment Table Max, 2^(n) */
#define HCS_ERST_MAX(p) (((p) >> 4) & 0xf)
-/* bits 21:25 Hi 5 bits of Scratchpad buffers SW must allocate for the HW */
-/* bit 26 Scratchpad restore - for save/restore HW state - not used yet */
-/* bits 27:31 Lo 5 bits of Scratchpad buffers SW must allocate for the HW */
-#define HCS_MAX_SCRATCHPAD(p) ((((p) >> 16) & 0x3e0) | (((p) >> 27) & 0x1f))
+/* bits 20:8 - Rsvd */
+/* bits 25:21 - Max Scratchpad Buffers (Hi), 5 Most significant bits */
+#define HCS_MAX_SP_HI(p) (((p) >> 21) & 0x1f)
+/* bit 26 - Scratchpad restore, for save/restore HW state */
+/* bits 31:27 - Max Scratchpad Buffers (Lo), 5 Least significant bits */
+#define HCS_MAX_SP_LO(p) (((p) >> 27) & 0x1f)
+#define HCS_MAX_SCRATCHPAD(p) (HCS_MAX_SP_HI(p) << 5 | HCS_MAX_SP_LO(p))
/* HCSPARAMS3 - hcs_params3 - bitmasks */
-/* bits 0:7, Max U1 to U0 latency for the roothub ports */
+/* bits 7:0 - U1 Device Exit Latency, Max U1 to U0 latency for the roothub ports */
#define HCS_U1_LATENCY(p) (((p) >> 0) & 0xff)
-/* bits 16:31, Max U2 to U0 latency for the roothub ports */
+/* bits 15:8 - Rsvd */
+/* bits 31:16 - U2 Device Exit Latency, Max U2 to U0 latency for the roothub ports */
#define HCS_U2_LATENCY(p) (((p) >> 16) & 0xffff)
-/* HCCPARAMS - hcc_params - bitmasks */
-/* true: HC can use 64-bit address pointers */
-#define HCC_64BIT_ADDR(p) ((p) & (1 << 0))
-/* true: HC can do bandwidth negotiation */
-#define HCC_BANDWIDTH_NEG(p) ((p) & (1 << 1))
-/* true: HC uses 64-byte Device Context structures
- * FIXME 64-byte context structures aren't supported yet.
- */
-#define HCC_64BYTE_CONTEXT(p) ((p) & (1 << 2))
-/* true: HC has port power switches */
-#define HCC_PPC(p) ((p) & (1 << 3))
-/* true: HC has port indicators */
-#define HCS_INDICATOR(p) ((p) & (1 << 4))
-/* true: HC has Light HC Reset Capability */
-#define HCC_LIGHT_RESET(p) ((p) & (1 << 5))
-/* true: HC supports latency tolerance messaging */
-#define HCC_LTC(p) ((p) & (1 << 6))
-/* true: no secondary Stream ID Support */
-#define HCC_NSS(p) ((p) & (1 << 7))
-/* true: HC supports Stopped - Short Packet */
-#define HCC_SPC(p) ((p) & (1 << 9))
-/* true: HC has Contiguous Frame ID Capability */
-#define HCC_CFC(p) ((p) & (1 << 11))
-/* Max size for Primary Stream Arrays - 2^(n+1), where n is bits 12:15 */
+/* HCCPARAMS1 - hcc_params - bitmasks */
+/* bit 0 - 64-bit Addressing Capability */
+#define HCC_64BIT_ADDR BIT(0)
+/* bit 1 - BW Negotiation Capability */
+#define HCC_BANDWIDTH_NEG BIT(1)
+/* bit 2 - Context Size */
+#define HCC_64BYTE_CONTEXT BIT(2)
+#define CTX_SIZE(_hcc) (_hcc & HCC_64BYTE_CONTEXT ? 64 : 32)
+/* bit 3 - Port Power Control */
+#define HCC_PPC BIT(3)
+/* bit 4 - Port Indicators */
+#define HCS_INDICATOR BIT(4)
+/* bit 5 - Light HC Reset Capability */
+#define HCC_LIGHT_RESET BIT(5)
+/* bit 6 - Latency Tolerance Messaging Capability */
+#define HCC_LTC BIT(6)
+/* bit 7 - No Secondary Stream ID Support */
+#define HCC_NSS BIT(7)
+/* bit 8 - Parse All Event Data */
+/* bit 9 - Short Packet Capability */
+#define HCC_SPC BIT(9)
+/* bit 10 - Stopped EDTLA Capability */
+/* bit 11 - Contiguous Frame ID Capability */
+#define HCC_CFC BIT(11)
+/* bits 15:12 - Max size for Primary Stream Arrays, 2^(n+1) */
#define HCC_MAX_PSA(p) (1 << ((((p) >> 12) & 0xf) + 1))
-/* Extended Capabilities pointer from PCI base - section 5.3.6 */
-#define HCC_EXT_CAPS(p) XHCI_HCC_EXT_CAPS(p)
-
-#define CTX_SIZE(_hcc) (HCC_64BYTE_CONTEXT(_hcc) ? 64 : 32)
+/* bits 31:16 - xHCI Extended Capabilities Pointer, from PCI base: 2^(n) */
+#define HCC_EXT_CAPS(p) (((p) >> 16) & 0xffff)
-/* db_off bitmask - bits 0:1 reserved */
-#define DBOFF_MASK (~0x3)
+/* DBOFF - db_off - bitmasks */
+/* bits 1:0 - Rsvd */
+/* bits 31:2 - Doorbell Array Offset */
+#define DBOFF_MASK (0xfffffffc)
-/* run_regs_off bitmask - bits 0:4 reserved */
+/* RTSOFF - run_regs_off - bitmasks */
+/* bits 4:0 - Rsvd */
+/* bits 31:5 - Runtime Register Space Offse */
#define RTSOFF_MASK (~0x1f)
/* HCCPARAMS2 - hcc_params2 - bitmasks */
-/* true: HC supports U3 entry Capability */
-#define HCC2_U3C(p) ((p) & (1 << 0))
-/* true: HC supports Configure endpoint command Max exit latency too large */
-#define HCC2_CMC(p) ((p) & (1 << 1))
-/* true: HC supports Force Save context Capability */
-#define HCC2_FSC(p) ((p) & (1 << 2))
-/* true: HC supports Compliance Transition Capability */
-#define HCC2_CTC(p) ((p) & (1 << 3))
-/* true: HC support Large ESIT payload Capability > 48k */
-#define HCC2_LEC(p) ((p) & (1 << 4))
-/* true: HC support Configuration Information Capability */
-#define HCC2_CIC(p) ((p) & (1 << 5))
-/* true: HC support Extended TBC Capability, Isoc burst count > 65535 */
-#define HCC2_ETC(p) ((p) & (1 << 6))
-/* true: HC support Extended TBC TRB Status Capability */
-#define HCC2_ETC_TSC(p) ((p) & (1 << 7))
-/* true: HC support Get/Set Extended Property Capability */
-#define HCC2_GSC(p) ((p) & (1 << 8))
-/* true: HC support Virtualization Based Trusted I/O Capability */
-#define HCC2_VTC(p) ((p) & (1 << 9))
+/* bit 0 - U3 Entry Capability */
+#define HCC2_U3C BIT(0)
+/* bit 1 - Configure Endpoint Command Max Exit Latency Too Large Capability */
+#define HCC2_CMC BIT(1)
+/* bit 2 - Force Save Context Capabilitu */
+#define HCC2_FSC BIT(2)
+/* bit 3 - Compliance Transition Capability, false: compliance is enabled by default */
+#define HCC2_CTC BIT(3)
+/* bit 4 - Large ESIT Payload Capability, true: HC support ESIT payload > 48k */
+#define HCC2_LEC BIT(4)
+/* bit 5 - Configuration Information Capability */
+#define HCC2_CIC BIT(5)
+/* bit 6 - Extended TBC Capability, true: Isoc burst count > 65535 */
+#define HCC2_ETC BIT(6)
+/* bit 7 - Extended TBC TRB Status Capability */
+#define HCC2_ETC_TSC BIT(7)
+/* bit 8 - Get/Set Extended Property Capability */
+#define HCC2_GSC BIT(8)
+/* bit 9 - Virtualization Based Trusted I/O Capability */
+#define HCC2_VTC BIT(9)
+/* bit 10 - Rsvd */
+/* bit 11 - HC support Double BW on a eUSB2 HS ISOC EP */
+#define HCC2_EUSB2_DIC BIT(11)
+/* bit 12 - HC support eUSB2V2 capability */
+#define HCC2_E2V2C BIT(12)
+/* bits 31:13 - Rsvd */
diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c
index fd7895b24367..7e6f7d72f03e 100644
--- a/drivers/usb/host/xhci-dbgcap.c
+++ b/drivers/usb/host/xhci-dbgcap.c
@@ -29,6 +29,12 @@
#include "xhci-trace.h"
#include "xhci-dbgcap.h"
+static const struct dbc_str dbc_str_default = {
+ .manufacturer = "Linux Foundation",
+ .product = "Linux USB Debug Target",
+ .serial = "0001",
+};
+
static void dbc_free_ctx(struct device *dev, struct xhci_container_ctx *ctx)
{
if (!ctx)
@@ -52,89 +58,107 @@ static void dbc_ring_free(struct device *dev, struct xhci_ring *ring)
kfree(ring);
}
-static u32 xhci_dbc_populate_strings(struct dbc_str_descs *strings)
+static void xhci_dbc_init_ep_contexts(struct xhci_dbc *dbc)
+{
+ struct xhci_ep_ctx *ep_ctx;
+ unsigned int max_burst;
+ dma_addr_t deq;
+
+ max_burst = DBC_CTRL_MAXBURST(readl(&dbc->regs->control));
+
+ /* Populate bulk out endpoint context: */
+ ep_ctx = dbc_bulkout_ctx(dbc);
+ deq = dbc_bulkout_enq(dbc);
+ ep_ctx->ep_info = 0;
+ ep_ctx->ep_info2 = dbc_epctx_info2(BULK_OUT_EP, 1024, max_burst);
+ ep_ctx->deq = cpu_to_le64(deq | dbc->ring_out->cycle_state);
+
+ /* Populate bulk in endpoint context: */
+ ep_ctx = dbc_bulkin_ctx(dbc);
+ deq = dbc_bulkin_enq(dbc);
+ ep_ctx->ep_info = 0;
+ ep_ctx->ep_info2 = dbc_epctx_info2(BULK_IN_EP, 1024, max_burst);
+ ep_ctx->deq = cpu_to_le64(deq | dbc->ring_in->cycle_state);
+}
+
+static u8 get_str_desc_len(const char *desc)
+{
+ return ((struct usb_string_descriptor *)desc)->bLength;
+}
+
+static u32 dbc_prepare_info_context_str_len(struct dbc_str_descs *descs)
+{
+ u32 len;
+
+ len = get_str_desc_len(descs->serial);
+ len <<= 8;
+ len += get_str_desc_len(descs->product);
+ len <<= 8;
+ len += get_str_desc_len(descs->manufacturer);
+ len <<= 8;
+ len += get_str_desc_len(descs->string0);
+
+ return len;
+}
+
+static int xhci_dbc_populate_str_desc(char *desc, const char *src)
{
struct usb_string_descriptor *s_desc;
- u32 string_length;
+ int len;
- /* Serial string: */
- s_desc = (struct usb_string_descriptor *)strings->serial;
- utf8s_to_utf16s(DBC_STRING_SERIAL, strlen(DBC_STRING_SERIAL),
- UTF16_LITTLE_ENDIAN, (wchar_t *)s_desc->wData,
- DBC_MAX_STRING_LENGTH);
+ s_desc = (struct usb_string_descriptor *)desc;
- s_desc->bLength = (strlen(DBC_STRING_SERIAL) + 1) * 2;
+ /* len holds number of 2 byte UTF-16 characters */
+ len = utf8s_to_utf16s(src, strlen(src), UTF16_LITTLE_ENDIAN,
+ (wchar_t *)s_desc->wData, USB_MAX_STRING_LEN * 2);
+ if (len < 0)
+ return len;
+
+ s_desc->bLength = len * 2 + 2;
s_desc->bDescriptorType = USB_DT_STRING;
- string_length = s_desc->bLength;
- string_length <<= 8;
- /* Product string: */
- s_desc = (struct usb_string_descriptor *)strings->product;
- utf8s_to_utf16s(DBC_STRING_PRODUCT, strlen(DBC_STRING_PRODUCT),
- UTF16_LITTLE_ENDIAN, (wchar_t *)s_desc->wData,
- DBC_MAX_STRING_LENGTH);
+ return s_desc->bLength;
+}
- s_desc->bLength = (strlen(DBC_STRING_PRODUCT) + 1) * 2;
- s_desc->bDescriptorType = USB_DT_STRING;
- string_length += s_desc->bLength;
- string_length <<= 8;
+static void xhci_dbc_populate_str_descs(struct dbc_str_descs *str_descs,
+ struct dbc_str *str)
+{
+ /* Serial string: */
+ xhci_dbc_populate_str_desc(str_descs->serial, str->serial);
- /* Manufacture string: */
- s_desc = (struct usb_string_descriptor *)strings->manufacturer;
- utf8s_to_utf16s(DBC_STRING_MANUFACTURER,
- strlen(DBC_STRING_MANUFACTURER),
- UTF16_LITTLE_ENDIAN, (wchar_t *)s_desc->wData,
- DBC_MAX_STRING_LENGTH);
+ /* Product string: */
+ xhci_dbc_populate_str_desc(str_descs->product, str->product);
- s_desc->bLength = (strlen(DBC_STRING_MANUFACTURER) + 1) * 2;
- s_desc->bDescriptorType = USB_DT_STRING;
- string_length += s_desc->bLength;
- string_length <<= 8;
+ /* Manufacturer string: */
+ xhci_dbc_populate_str_desc(str_descs->manufacturer, str->manufacturer);
/* String0: */
- strings->string0[0] = 4;
- strings->string0[1] = USB_DT_STRING;
- strings->string0[2] = 0x09;
- strings->string0[3] = 0x04;
- string_length += 4;
-
- return string_length;
+ str_descs->string0[0] = 4;
+ str_descs->string0[1] = USB_DT_STRING;
+ str_descs->string0[2] = 0x09;
+ str_descs->string0[3] = 0x04;
}
-static void xhci_dbc_init_contexts(struct xhci_dbc *dbc, u32 string_length)
+static void xhci_dbc_init_contexts(struct xhci_dbc *dbc)
{
struct dbc_info_context *info;
- struct xhci_ep_ctx *ep_ctx;
u32 dev_info;
- dma_addr_t deq, dma;
- unsigned int max_burst;
+ dma_addr_t dma;
if (!dbc)
return;
/* Populate info Context: */
info = (struct dbc_info_context *)dbc->ctx->bytes;
- dma = dbc->string_dma;
+ dma = dbc->str_descs_dma;
info->string0 = cpu_to_le64(dma);
- info->manufacturer = cpu_to_le64(dma + DBC_MAX_STRING_LENGTH);
- info->product = cpu_to_le64(dma + DBC_MAX_STRING_LENGTH * 2);
- info->serial = cpu_to_le64(dma + DBC_MAX_STRING_LENGTH * 3);
- info->length = cpu_to_le32(string_length);
-
- /* Populate bulk out endpoint context: */
- ep_ctx = dbc_bulkout_ctx(dbc);
- max_burst = DBC_CTRL_MAXBURST(readl(&dbc->regs->control));
- deq = dbc_bulkout_enq(dbc);
- ep_ctx->ep_info = 0;
- ep_ctx->ep_info2 = dbc_epctx_info2(BULK_OUT_EP, 1024, max_burst);
- ep_ctx->deq = cpu_to_le64(deq | dbc->ring_out->cycle_state);
+ info->manufacturer = cpu_to_le64(dma + USB_MAX_STRING_DESC_LEN);
+ info->product = cpu_to_le64(dma + USB_MAX_STRING_DESC_LEN * 2);
+ info->serial = cpu_to_le64(dma + USB_MAX_STRING_DESC_LEN * 3);
+ info->length = cpu_to_le32(dbc_prepare_info_context_str_len(dbc->str_descs));
- /* Populate bulk in endpoint context: */
- ep_ctx = dbc_bulkin_ctx(dbc);
- deq = dbc_bulkin_enq(dbc);
- ep_ctx->ep_info = 0;
- ep_ctx->ep_info2 = dbc_epctx_info2(BULK_IN_EP, 1024, max_burst);
- ep_ctx->deq = cpu_to_le64(deq | dbc->ring_in->cycle_state);
+ /* Populate bulk in and out endpoint contexts: */
+ xhci_dbc_init_ep_contexts(dbc);
/* Set DbC context and info registers: */
lo_hi_writeq(dbc->ctx->dma, &dbc->regs->dccp);
@@ -214,7 +238,7 @@ dbc_alloc_request(struct xhci_dbc *dbc, unsigned int direction, gfp_t flags)
if (!dbc)
return NULL;
- req = kzalloc(sizeof(*req), flags);
+ req = kzalloc_obj(*req, flags);
if (!req)
return NULL;
@@ -365,7 +389,7 @@ int dbc_ep_queue(struct dbc_request *req)
ret = dbc_ep_do_queue(req);
spin_unlock_irqrestore(&dbc->lock, flags);
- mod_delayed_work(system_wq, &dbc->event_work, 0);
+ mod_delayed_work(system_percpu_wq, &dbc->event_work, 0);
trace_xhci_dbc_queue_request(req);
@@ -422,7 +446,7 @@ dbc_alloc_ctx(struct device *dev, gfp_t flags)
{
struct xhci_container_ctx *ctx;
- ctx = kzalloc(sizeof(*ctx), flags);
+ ctx = kzalloc_obj(*ctx, flags);
if (!ctx)
return NULL;
@@ -436,6 +460,42 @@ dbc_alloc_ctx(struct device *dev, gfp_t flags)
return ctx;
}
+static void xhci_dbc_ring_init(struct xhci_ring *ring)
+{
+ struct xhci_segment *seg = ring->first_seg;
+
+ /* clear all trbs on ring in case of old ring */
+ memset(seg->trbs, 0, TRB_SEGMENT_SIZE);
+
+ /* Only event ring does not use link TRB */
+ if (ring->type != TYPE_EVENT) {
+ union xhci_trb *trb = &seg->trbs[TRBS_PER_SEGMENT - 1];
+
+ trb->link.segment_ptr = cpu_to_le64(ring->first_seg->dma);
+ trb->link.control = cpu_to_le32(LINK_TOGGLE | TRB_TYPE(TRB_LINK));
+ }
+ xhci_initialize_ring_info(ring);
+}
+
+static int xhci_dbc_reinit_ep_rings(struct xhci_dbc *dbc)
+{
+ struct xhci_ring *in_ring = dbc->eps[BULK_IN].ring;
+ struct xhci_ring *out_ring = dbc->eps[BULK_OUT].ring;
+
+ if (!in_ring || !out_ring || !dbc->ctx) {
+ dev_warn(dbc->dev, "Can't re-init unallocated endpoints\n");
+ return -ENODEV;
+ }
+
+ xhci_dbc_ring_init(in_ring);
+ xhci_dbc_ring_init(out_ring);
+
+ /* set ep context enqueue, dequeue, and cycle to initial values */
+ xhci_dbc_init_ep_contexts(dbc);
+
+ return 0;
+}
+
static struct xhci_ring *
xhci_dbc_ring_alloc(struct device *dev, enum xhci_ring_type type, gfp_t flags)
{
@@ -443,14 +503,14 @@ xhci_dbc_ring_alloc(struct device *dev, enum xhci_ring_type type, gfp_t flags)
struct xhci_segment *seg;
dma_addr_t dma;
- ring = kzalloc(sizeof(*ring), flags);
+ ring = kzalloc_obj(*ring, flags);
if (!ring)
return NULL;
ring->num_segs = 1;
ring->type = type;
- seg = kzalloc(sizeof(*seg), flags);
+ seg = kzalloc_obj(*seg, flags);
if (!seg)
goto seg_fail;
@@ -464,15 +524,10 @@ xhci_dbc_ring_alloc(struct device *dev, enum xhci_ring_type type, gfp_t flags)
seg->dma = dma;
- /* Only event ring does not use link TRB */
- if (type != TYPE_EVENT) {
- union xhci_trb *trb = &seg->trbs[TRBS_PER_SEGMENT - 1];
-
- trb->link.segment_ptr = cpu_to_le64(dma);
- trb->link.control = cpu_to_le32(LINK_TOGGLE | TRB_TYPE(TRB_LINK));
- }
INIT_LIST_HEAD(&ring->td_list);
- xhci_initialize_ring_info(ring);
+
+ xhci_dbc_ring_init(ring);
+
return ring;
dma_fail:
kfree(seg);
@@ -485,7 +540,6 @@ static int xhci_dbc_mem_init(struct xhci_dbc *dbc, gfp_t flags)
{
int ret;
dma_addr_t deq;
- u32 string_length;
struct device *dev = dbc->dev;
/* Allocate various rings for events and transfers: */
@@ -512,11 +566,11 @@ static int xhci_dbc_mem_init(struct xhci_dbc *dbc, gfp_t flags)
goto ctx_fail;
/* Allocate the string table: */
- dbc->string_size = sizeof(*dbc->string);
- dbc->string = dma_alloc_coherent(dev, dbc->string_size,
- &dbc->string_dma, flags);
- if (!dbc->string)
- goto string_fail;
+ dbc->str_descs_size = sizeof(*dbc->str_descs);
+ dbc->str_descs = dma_alloc_coherent(dev, dbc->str_descs_size,
+ &dbc->str_descs_dma, flags);
+ if (!dbc->str_descs)
+ goto str_descs_fail;
/* Setup ERST register: */
writel(dbc->erst.num_entries, &dbc->regs->ersts);
@@ -526,16 +580,16 @@ static int xhci_dbc_mem_init(struct xhci_dbc *dbc, gfp_t flags)
dbc->ring_evt->dequeue);
lo_hi_writeq(deq, &dbc->regs->erdp);
- /* Setup strings and contexts: */
- string_length = xhci_dbc_populate_strings(dbc->string);
- xhci_dbc_init_contexts(dbc, string_length);
+ /* Setup string descriptors and contexts: */
+ xhci_dbc_populate_str_descs(dbc->str_descs, &dbc->str);
+ xhci_dbc_init_contexts(dbc);
xhci_dbc_eps_init(dbc);
dbc->state = DS_INITIALIZED;
return 0;
-string_fail:
+str_descs_fail:
dbc_free_ctx(dev, dbc->ctx);
dbc->ctx = NULL;
ctx_fail:
@@ -560,8 +614,8 @@ static void xhci_dbc_mem_cleanup(struct xhci_dbc *dbc)
xhci_dbc_eps_exit(dbc);
- dma_free_coherent(dbc->dev, dbc->string_size, dbc->string, dbc->string_dma);
- dbc->string = NULL;
+ dma_free_coherent(dbc->dev, dbc->str_descs_size, dbc->str_descs, dbc->str_descs_dma);
+ dbc->str_descs = NULL;
dbc_free_ctx(dbc->dev, dbc->ctx);
dbc->ctx = NULL;
@@ -637,7 +691,7 @@ static int xhci_dbc_start(struct xhci_dbc *dbc)
return ret;
}
- return mod_delayed_work(system_wq, &dbc->event_work,
+ return mod_delayed_work(system_percpu_wq, &dbc->event_work,
msecs_to_jiffies(dbc->poll_interval));
}
@@ -652,6 +706,10 @@ static void xhci_dbc_stop(struct xhci_dbc *dbc)
case DS_DISABLED:
return;
case DS_CONFIGURED:
+ spin_lock(&dbc->lock);
+ xhci_dbc_flush_requests(dbc);
+ spin_unlock(&dbc->lock);
+
if (dbc->driver->disconnect)
dbc->driver->disconnect(dbc);
break;
@@ -823,6 +881,7 @@ static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc)
{
dma_addr_t deq;
union xhci_trb *evt;
+ enum evtreturn ret = EVT_DONE;
u32 ctrl, portsc;
bool update_erdp = false;
@@ -847,7 +906,8 @@ static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc)
dev_info(dbc->dev, "DbC configured\n");
portsc = readl(&dbc->regs->portsc);
writel(portsc, &dbc->regs->portsc);
- return EVT_GSER;
+ ret = EVT_GSER;
+ break;
}
return EVT_DONE;
@@ -859,7 +919,7 @@ static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc)
dev_info(dbc->dev, "DbC cable unplugged\n");
dbc->state = DS_ENABLED;
xhci_dbc_flush_requests(dbc);
-
+ xhci_dbc_reinit_ep_rings(dbc);
return EVT_DISC;
}
@@ -869,7 +929,7 @@ static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc)
writel(portsc, &dbc->regs->portsc);
dbc->state = DS_ENABLED;
xhci_dbc_flush_requests(dbc);
-
+ xhci_dbc_reinit_ep_rings(dbc);
return EVT_DISC;
}
@@ -909,6 +969,8 @@ static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc)
break;
case TRB_TYPE(TRB_TRANSFER):
dbc_handle_xfer_event(dbc, evt);
+ if (ret != EVT_GSER)
+ ret = EVT_XFER_DONE;
break;
default:
break;
@@ -927,7 +989,7 @@ static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc)
lo_hi_writeq(deq, &dbc->regs->erdp);
}
- return EVT_DONE;
+ return ret;
}
static void xhci_dbc_handle_events(struct work_struct *work)
@@ -936,6 +998,7 @@ static void xhci_dbc_handle_events(struct work_struct *work)
struct xhci_dbc *dbc;
unsigned long flags;
unsigned int poll_interval;
+ unsigned long busypoll_timelimit;
dbc = container_of(to_delayed_work(work), struct xhci_dbc, event_work);
poll_interval = dbc->poll_interval;
@@ -954,17 +1017,27 @@ static void xhci_dbc_handle_events(struct work_struct *work)
dbc->driver->disconnect(dbc);
break;
case EVT_DONE:
- /* set fast poll rate if there are pending data transfers */
+ /*
+ * Set fast poll rate if there are pending out transfers, or
+ * a transfer was recently processed
+ */
+ busypoll_timelimit = dbc->xfer_timestamp +
+ msecs_to_jiffies(DBC_XFER_INACTIVITY_TIMEOUT);
+
if (!list_empty(&dbc->eps[BULK_OUT].list_pending) ||
- !list_empty(&dbc->eps[BULK_IN].list_pending))
+ time_is_after_jiffies(busypoll_timelimit))
poll_interval = 0;
break;
+ case EVT_XFER_DONE:
+ dbc->xfer_timestamp = jiffies;
+ poll_interval = 0;
+ break;
default:
dev_info(dbc->dev, "stop handling dbc events\n");
return;
}
- mod_delayed_work(system_wq, &dbc->event_work,
+ mod_delayed_work(system_percpu_wq, &dbc->event_work,
msecs_to_jiffies(poll_interval));
}
@@ -1137,6 +1210,108 @@ static ssize_t dbc_bcdDevice_store(struct device *dev,
return size;
}
+static ssize_t dbc_manufacturer_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(dev_get_drvdata(dev));
+ struct xhci_dbc *dbc = xhci->dbc;
+
+ return sysfs_emit(buf, "%s\n", dbc->str.manufacturer);
+}
+
+static ssize_t dbc_manufacturer_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(dev_get_drvdata(dev));
+ struct xhci_dbc *dbc = xhci->dbc;
+ size_t len;
+
+ if (dbc->state != DS_DISABLED)
+ return -EBUSY;
+
+ len = strcspn(buf, "\n");
+ if (!len)
+ return -EINVAL;
+
+ if (len > USB_MAX_STRING_LEN)
+ return -E2BIG;
+
+ memcpy(dbc->str.manufacturer, buf, len);
+ dbc->str.manufacturer[len] = '\0';
+
+ return size;
+}
+
+static ssize_t dbc_product_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(dev_get_drvdata(dev));
+ struct xhci_dbc *dbc = xhci->dbc;
+
+ return sysfs_emit(buf, "%s\n", dbc->str.product);
+}
+
+static ssize_t dbc_product_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(dev_get_drvdata(dev));
+ struct xhci_dbc *dbc = xhci->dbc;
+ size_t len;
+
+ if (dbc->state != DS_DISABLED)
+ return -EBUSY;
+
+ len = strcspn(buf, "\n");
+ if (!len)
+ return -EINVAL;
+
+ if (len > USB_MAX_STRING_LEN)
+ return -E2BIG;
+
+ memcpy(dbc->str.product, buf, len);
+ dbc->str.product[len] = '\0';
+
+ return size;
+}
+
+static ssize_t dbc_serial_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(dev_get_drvdata(dev));
+ struct xhci_dbc *dbc = xhci->dbc;
+
+ return sysfs_emit(buf, "%s\n", dbc->str.serial);
+}
+
+static ssize_t dbc_serial_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(dev_get_drvdata(dev));
+ struct xhci_dbc *dbc = xhci->dbc;
+ size_t len;
+
+ if (dbc->state != DS_DISABLED)
+ return -EBUSY;
+
+ len = strcspn(buf, "\n");
+ if (!len)
+ return -EINVAL;
+
+ if (len > USB_MAX_STRING_LEN)
+ return -E2BIG;
+
+ memcpy(dbc->str.serial, buf, len);
+ dbc->str.serial[len] = '\0';
+
+ return size;
+}
+
static ssize_t dbc_bInterfaceProtocol_show(struct device *dev,
struct device_attribute *attr,
char *buf)
@@ -1215,7 +1390,7 @@ static ssize_t dbc_poll_interval_ms_store(struct device *dev,
dbc->poll_interval = value;
- mod_delayed_work(system_wq, &dbc->event_work, 0);
+ mod_delayed_work(system_percpu_wq, &dbc->event_work, 0);
return size;
}
@@ -1224,6 +1399,9 @@ static DEVICE_ATTR_RW(dbc);
static DEVICE_ATTR_RW(dbc_idVendor);
static DEVICE_ATTR_RW(dbc_idProduct);
static DEVICE_ATTR_RW(dbc_bcdDevice);
+static DEVICE_ATTR_RW(dbc_serial);
+static DEVICE_ATTR_RW(dbc_product);
+static DEVICE_ATTR_RW(dbc_manufacturer);
static DEVICE_ATTR_RW(dbc_bInterfaceProtocol);
static DEVICE_ATTR_RW(dbc_poll_interval_ms);
@@ -1232,6 +1410,9 @@ static struct attribute *dbc_dev_attrs[] = {
&dev_attr_dbc_idVendor.attr,
&dev_attr_dbc_idProduct.attr,
&dev_attr_dbc_bcdDevice.attr,
+ &dev_attr_dbc_serial.attr,
+ &dev_attr_dbc_product.attr,
+ &dev_attr_dbc_manufacturer.attr,
&dev_attr_dbc_bInterfaceProtocol.attr,
&dev_attr_dbc_poll_interval_ms.attr,
NULL
@@ -1244,7 +1425,7 @@ xhci_alloc_dbc(struct device *dev, void __iomem *base, const struct dbc_driver *
struct xhci_dbc *dbc;
int ret;
- dbc = kzalloc(sizeof(*dbc), GFP_KERNEL);
+ dbc = kzalloc_obj(*dbc);
if (!dbc)
return NULL;
@@ -1257,6 +1438,9 @@ xhci_alloc_dbc(struct device *dev, void __iomem *base, const struct dbc_driver *
dbc->bInterfaceProtocol = DBC_PROTOCOL;
dbc->poll_interval = DBC_POLL_INTERVAL_DEFAULT;
+ /* initialize serial, product and manufacturer with default values */
+ dbc->str = dbc_str_default;
+
if (readl(&dbc->regs->control) & DBC_CTRL_DBC_ENABLE)
goto err;
@@ -1333,8 +1517,15 @@ int xhci_dbc_suspend(struct xhci_hcd *xhci)
if (!dbc)
return 0;
- if (dbc->state == DS_CONFIGURED)
+ switch (dbc->state) {
+ case DS_ENABLED:
+ case DS_CONNECTED:
+ case DS_CONFIGURED:
dbc->resume_required = 1;
+ break;
+ default:
+ break;
+ }
xhci_dbc_stop(dbc);
diff --git a/drivers/usb/host/xhci-dbgcap.h b/drivers/usb/host/xhci-dbgcap.h
index 9dc8f4d8077c..20ae4e7617f2 100644
--- a/drivers/usb/host/xhci-dbgcap.h
+++ b/drivers/usb/host/xhci-dbgcap.h
@@ -47,10 +47,6 @@ struct dbc_info_context {
#define DBC_DOOR_BELL_TARGET(p) (((p) & 0xff) << 8)
#define DBC_MAX_PACKET 1024
-#define DBC_MAX_STRING_LENGTH 64
-#define DBC_STRING_MANUFACTURER "Linux Foundation"
-#define DBC_STRING_PRODUCT "Linux USB Debug Target"
-#define DBC_STRING_SERIAL "0001"
#define DBC_CONTEXT_SIZE 64
/*
@@ -63,11 +59,31 @@ struct dbc_info_context {
#define DBC_PORTSC_LINK_CHANGE BIT(22)
#define DBC_PORTSC_CONFIG_CHANGE BIT(23)
+/*
+ * The maximum length of a string descriptor is 255, because the bLength
+ * field in the usb_string_descriptor struct is __u8. In practice the
+ * maximum length is 254, because a string descriptor consists of a 2 byte
+ * header followed by UTF-16 characters (2 bytes each). This allows for
+ * only 126 characters (code points) in the string, which is where
+ * USB_MAX_STRING_LEN comes from.
+ */
+#define USB_MAX_STRING_DESC_LEN 254
+
struct dbc_str_descs {
- char string0[DBC_MAX_STRING_LENGTH];
- char manufacturer[DBC_MAX_STRING_LENGTH];
- char product[DBC_MAX_STRING_LENGTH];
- char serial[DBC_MAX_STRING_LENGTH];
+ char string0[USB_MAX_STRING_DESC_LEN];
+ char manufacturer[USB_MAX_STRING_DESC_LEN];
+ char product[USB_MAX_STRING_DESC_LEN];
+ char serial[USB_MAX_STRING_DESC_LEN];
+};
+
+/*
+ * NULL terminated UTF-8 strings used to create UTF-16 strings
+ * (with maxiumum USB_MAX_STRING_LEN 2 byte characters).
+ */
+struct dbc_str {
+ char manufacturer[USB_MAX_STRING_LEN+1];
+ char product[USB_MAX_STRING_LEN+1];
+ char serial[USB_MAX_STRING_LEN+1];
};
#define DBC_PROTOCOL 1 /* GNU Remote Debug Command */
@@ -96,6 +112,7 @@ struct dbc_ep {
#define DBC_WRITE_BUF_SIZE 8192
#define DBC_POLL_INTERVAL_DEFAULT 64 /* milliseconds */
#define DBC_POLL_INTERVAL_MAX 5000 /* milliseconds */
+#define DBC_XFER_INACTIVITY_TIMEOUT 10 /* milliseconds */
/*
* Private structure for DbC hardware state:
*/
@@ -113,6 +130,7 @@ struct dbc_port {
unsigned int tx_boundary;
bool registered;
+ bool tx_running;
};
struct dbc_driver {
@@ -131,9 +149,10 @@ struct xhci_dbc {
struct xhci_erst erst;
struct xhci_container_ctx *ctx;
- struct dbc_str_descs *string;
- dma_addr_t string_dma;
- size_t string_size;
+ struct dbc_str_descs *str_descs;
+ dma_addr_t str_descs_dma;
+ size_t str_descs_size;
+ struct dbc_str str;
u16 idVendor;
u16 idProduct;
u16 bcdDevice;
@@ -142,6 +161,7 @@ struct xhci_dbc {
enum dbc_state state;
struct delayed_work event_work;
unsigned int poll_interval; /* ms */
+ unsigned long xfer_timestamp;
unsigned resume_required:1;
struct dbc_ep eps[2];
@@ -187,6 +207,7 @@ struct dbc_request {
enum evtreturn {
EVT_ERR = -1,
EVT_DONE,
+ EVT_XFER_DONE,
EVT_GSER,
EVT_DISC,
};
diff --git a/drivers/usb/host/xhci-dbgtty.c b/drivers/usb/host/xhci-dbgtty.c
index 60ed753c85bb..2e7384c6b6ec 100644
--- a/drivers/usb/host/xhci-dbgtty.c
+++ b/drivers/usb/host/xhci-dbgtty.c
@@ -47,7 +47,7 @@ dbc_kfifo_to_req(struct dbc_port *port, char *packet)
return len;
}
-static int dbc_start_tx(struct dbc_port *port)
+static int dbc_do_start_tx(struct dbc_port *port)
__releases(&port->port_lock)
__acquires(&port->port_lock)
{
@@ -57,6 +57,8 @@ static int dbc_start_tx(struct dbc_port *port)
bool do_tty_wake = false;
struct list_head *pool = &port->write_pool;
+ port->tx_running = true;
+
while (!list_empty(pool)) {
req = list_entry(pool->next, struct dbc_request, list_pool);
len = dbc_kfifo_to_req(port, req->buf);
@@ -77,12 +79,25 @@ static int dbc_start_tx(struct dbc_port *port)
}
}
+ port->tx_running = false;
+
if (do_tty_wake && port->port.tty)
tty_wakeup(port->port.tty);
return status;
}
+/* must be called with port->port_lock held */
+static int dbc_start_tx(struct dbc_port *port)
+{
+ lockdep_assert_held(&port->port_lock);
+
+ if (port->tx_running)
+ return -EBUSY;
+
+ return dbc_do_start_tx(port);
+}
+
static void dbc_start_rx(struct dbc_port *port)
__releases(&port->port_lock)
__acquires(&port->port_lock)
@@ -535,6 +550,12 @@ static void xhci_dbc_tty_unregister_device(struct xhci_dbc *dbc)
if (!port->registered)
return;
+ /*
+ * Hang up the TTY. This wakes up any blocked
+ * writers and causes subsequent writes to fail.
+ */
+ tty_port_tty_vhangup(&port->port);
+
tty_unregister_device(dbc_tty_driver, port->minor);
xhci_dbc_tty_exit_port(port);
port->registered = false;
@@ -563,7 +584,7 @@ int xhci_dbc_tty_probe(struct device *dev, void __iomem *base, struct xhci_hcd *
if (!dbc_tty_driver)
return -ENODEV;
- port = kzalloc(sizeof(*port), GFP_KERNEL);
+ port = kzalloc_obj(*port);
if (!port)
return -ENOMEM;
@@ -617,6 +638,7 @@ int dbc_tty_init(void)
dbc_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
dbc_tty_driver->subtype = SERIAL_TYPE_NORMAL;
dbc_tty_driver->init_termios = tty_std_termios;
+ dbc_tty_driver->init_termios.c_lflag &= ~ECHO;
dbc_tty_driver->init_termios.c_cflag =
B9600 | CS8 | CREAD | HUPCL | CLOCAL;
dbc_tty_driver->init_termios.c_ispeed = 9600;
diff --git a/drivers/usb/host/xhci-debugfs.c b/drivers/usb/host/xhci-debugfs.c
index 1f5ef174abea..d07276192256 100644
--- a/drivers/usb/host/xhci-debugfs.c
+++ b/drivers/usb/host/xhci-debugfs.c
@@ -87,7 +87,7 @@ static struct xhci_regset *xhci_debugfs_alloc_regset(struct xhci_hcd *xhci)
{
struct xhci_regset *regset;
- regset = kzalloc(sizeof(*regset), GFP_KERNEL);
+ regset = kzalloc_obj(*regset);
if (!regset)
return NULL;
@@ -329,7 +329,7 @@ static int xhci_portsc_show(struct seq_file *s, void *unused)
u32 portsc;
char str[XHCI_MSG_MAX];
- portsc = readl(port->addr);
+ portsc = xhci_portsc_readl(port);
seq_printf(s, "%s\n", xhci_decode_portsc(str, portsc));
return 0;
@@ -355,11 +355,11 @@ static ssize_t xhci_port_write(struct file *file, const char __user *ubuf,
if (!strncmp(buf, "compliance", 10)) {
/* If CTC is clear, compliance is enabled by default */
- if (!HCC2_CTC(xhci->hcc_params2))
+ if (!(xhci->hcc_params2 & HCC2_CTC))
return count;
spin_lock_irqsave(&xhci->lock, flags);
/* compliance mode can only be enabled on ports in RxDetect */
- portsc = readl(port->addr);
+ portsc = xhci_portsc_readl(port);
if ((portsc & PORT_PLS_MASK) != XDEV_RXDETECT) {
spin_unlock_irqrestore(&xhci->lock, flags);
return -EPERM;
@@ -367,7 +367,7 @@ static ssize_t xhci_port_write(struct file *file, const char __user *ubuf,
portsc = xhci_port_state_to_neutral(portsc);
portsc &= ~PORT_PLS_MASK;
portsc |= PORT_LINK_STROBE | XDEV_COMP_MODE;
- writel(portsc, port->addr);
+ xhci_portsc_writel(port, portsc);
spin_unlock_irqrestore(&xhci->lock, flags);
} else {
return -EINVAL;
@@ -383,6 +383,47 @@ static const struct file_operations port_fops = {
.release = single_release,
};
+static int xhci_portli_show(struct seq_file *s, void *unused)
+{
+ struct xhci_port *port = s->private;
+ struct xhci_hcd *xhci;
+ u32 portli;
+
+ portli = readl(&port->port_reg->portli);
+
+ /* port without protocol capability isn't added to a roothub */
+ if (!port->rhub) {
+ seq_printf(s, "0x%08x\n", portli);
+ return 0;
+ }
+
+ xhci = hcd_to_xhci(port->rhub->hcd);
+
+ /* PORTLI fields are valid if port is a USB3 or eUSB2V2 port */
+ if (port->rhub == &xhci->usb3_rhub)
+ seq_printf(s, "0x%08x LEC=%u RLC=%u TLC=%u\n", portli,
+ PORT_LEC(portli), PORT_RX_LANES(portli), PORT_TX_LANES(portli));
+ else if (xhci->hcc_params2 & HCC2_E2V2C)
+ seq_printf(s, "0x%08x RDR=%u TDR=%u\n", portli,
+ PORTLI_RDR(portli), PORTLI_TDR(portli));
+ else
+ seq_printf(s, "0x%08x RsvdP\n", portli);
+
+ return 0;
+}
+
+static int xhci_portli_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, xhci_portli_show, inode->i_private);
+}
+
+static const struct file_operations portli_fops = {
+ .open = xhci_portli_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
static void xhci_debugfs_create_files(struct xhci_hcd *xhci,
struct xhci_file_map *files,
size_t nentries, void *data,
@@ -435,7 +476,7 @@ void xhci_debugfs_create_endpoint(struct xhci_hcd *xhci,
if (spriv->eps[ep_index])
return;
- epriv = kzalloc(sizeof(*epriv), GFP_KERNEL);
+ epriv = kzalloc_obj(*epriv);
if (!epriv)
return;
@@ -575,7 +616,7 @@ void xhci_debugfs_create_slot(struct xhci_hcd *xhci, int slot_id)
struct xhci_slot_priv *priv;
struct xhci_virt_device *dev = xhci->devs[slot_id];
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ priv = kzalloc_obj(*priv);
if (!priv)
return;
@@ -613,22 +654,126 @@ void xhci_debugfs_remove_slot(struct xhci_hcd *xhci, int slot_id)
static void xhci_debugfs_create_ports(struct xhci_hcd *xhci,
struct dentry *parent)
{
- unsigned int num_ports;
char port_name[8];
struct xhci_port *port;
struct dentry *dir;
- num_ports = HCS_MAX_PORTS(xhci->hcs_params1);
-
parent = debugfs_create_dir("ports", parent);
- while (num_ports--) {
- scnprintf(port_name, sizeof(port_name), "port%02d",
- num_ports + 1);
+ for (int i = 0; i < xhci->max_ports; i++) {
+ scnprintf(port_name, sizeof(port_name), "port%02d", i + 1);
dir = debugfs_create_dir(port_name, parent);
- port = &xhci->hw_ports[num_ports];
+ port = &xhci->hw_ports[i];
debugfs_create_file("portsc", 0644, dir, port, &port_fops);
+ debugfs_create_file("portli", 0444, dir, port, &portli_fops);
+ }
+}
+
+static int xhci_port_bw_show(struct xhci_hcd *xhci, u8 dev_speed,
+ struct seq_file *s)
+{
+ unsigned int i;
+ int ret;
+ struct xhci_container_ctx *ctx;
+ struct usb_hcd *hcd = xhci_to_hcd(xhci);
+ struct device *dev = hcd->self.controller;
+
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0)
+ return ret;
+
+ ctx = xhci_alloc_port_bw_ctx(xhci, 0);
+ if (!ctx) {
+ pm_runtime_put_sync(dev);
+ return -ENOMEM;
+ }
+
+ /* get roothub port bandwidth */
+ ret = xhci_get_port_bandwidth(xhci, ctx, dev_speed);
+ if (ret)
+ goto err_out;
+
+ /* print all roothub ports available bandwidth
+ * refer to xhci rev1_2 protocol 6.2.6 , byte 0 is reserved
+ */
+ for (i = 1; i <= xhci->max_ports; i++)
+ seq_printf(s, "port[%d] available bw: %d%%.\n", i,
+ ctx->bytes[i]);
+err_out:
+ if (ret == -EIO) {
+ seq_puts(s, "Get Port Bandwidth failed\n");
+ ret = 0;
}
+ pm_runtime_put_sync(dev);
+ xhci_free_port_bw_ctx(xhci, ctx);
+ return ret;
+}
+
+static int xhci_ss_bw_show(struct seq_file *s, void *unused)
+{
+ int ret;
+ struct xhci_hcd *xhci = (struct xhci_hcd *)s->private;
+
+ ret = xhci_port_bw_show(xhci, DEV_PORT_SPEED(XDEV_SS), s);
+ return ret;
+}
+
+static int xhci_hs_bw_show(struct seq_file *s, void *unused)
+{
+ int ret;
+ struct xhci_hcd *xhci = (struct xhci_hcd *)s->private;
+
+ ret = xhci_port_bw_show(xhci, DEV_PORT_SPEED(XDEV_HS), s);
+ return ret;
+}
+
+static int xhci_fs_bw_show(struct seq_file *s, void *unused)
+{
+ int ret;
+ struct xhci_hcd *xhci = (struct xhci_hcd *)s->private;
+
+ ret = xhci_port_bw_show(xhci, DEV_PORT_SPEED(XDEV_FS), s);
+ return ret;
+}
+
+static struct xhci_file_map bw_context_files[] = {
+ {"SS_BW", xhci_ss_bw_show, },
+ {"HS_BW", xhci_hs_bw_show, },
+ {"FS_BW", xhci_fs_bw_show, },
+};
+
+static int bw_context_open(struct inode *inode, struct file *file)
+{
+ int i;
+ struct xhci_file_map *f_map;
+ const char *file_name = file_dentry(file)->d_iname;
+
+ for (i = 0; i < ARRAY_SIZE(bw_context_files); i++) {
+ f_map = &bw_context_files[i];
+
+ if (strcmp(f_map->name, file_name) == 0)
+ break;
+ }
+
+ return single_open(file, f_map->show, inode->i_private);
+}
+
+static const struct file_operations bw_fops = {
+ .open = bw_context_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static void xhci_debugfs_create_bandwidth(struct xhci_hcd *xhci,
+ struct dentry *parent)
+{
+ parent = debugfs_create_dir("port_bandwidth", parent);
+
+ xhci_debugfs_create_files(xhci, bw_context_files,
+ ARRAY_SIZE(bw_context_files),
+ xhci,
+ parent, &bw_fops);
}
void xhci_debugfs_init(struct xhci_hcd *xhci)
@@ -681,6 +826,8 @@ void xhci_debugfs_init(struct xhci_hcd *xhci)
xhci->debugfs_slots = debugfs_create_dir("devices", xhci->debugfs_root);
xhci_debugfs_create_ports(xhci, xhci->debugfs_root);
+
+ xhci_debugfs_create_bandwidth(xhci, xhci->debugfs_root);
}
void xhci_debugfs_exit(struct xhci_hcd *xhci)
diff --git a/drivers/usb/host/xhci-histb.c b/drivers/usb/host/xhci-histb.c
index 8a7d46dae62c..02396c8721dc 100644
--- a/drivers/usb/host/xhci-histb.c
+++ b/drivers/usb/host/xhci-histb.c
@@ -355,7 +355,7 @@ static int __maybe_unused xhci_histb_resume(struct device *dev)
if (!device_may_wakeup(dev))
xhci_histb_host_enable(histb);
- return xhci_resume(xhci, PMSG_RESUME);
+ return xhci_resume(xhci, false, false);
}
static const struct dev_pm_ops xhci_histb_pm_ops = {
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index 69c278b64084..bacd0ddd0d09 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -110,7 +110,7 @@ static int xhci_create_usb3x_bos_desc(struct xhci_hcd *xhci, char *buf,
ss_cap->bU2DevExitLat = 0; /* set later */
reg = readl(&xhci->cap_regs->hcc_params);
- if (HCC_LTC(reg))
+ if (reg & HCC_LTC)
ss_cap->bmAttributes |= USB_LTM_SUPPORT;
if ((xhci->quirks & XHCI_LPM_SUPPORT)) {
@@ -263,7 +263,7 @@ static void xhci_common_hub_descriptor(struct xhci_hcd *xhci,
desc->bNbrPorts = ports;
temp = 0;
/* Bits 1:0 - support per-port power switching, or power always on */
- if (HCC_PPC(xhci->hcc_params))
+ if (xhci->hcc_params & HCC_PPC)
temp |= HUB_CHAR_INDV_PORT_LPSM;
else
temp |= HUB_CHAR_NO_LPSM;
@@ -299,7 +299,7 @@ static void xhci_usb2_hub_descriptor(struct usb_hcd *hcd, struct xhci_hcd *xhci,
*/
memset(port_removable, 0, sizeof(port_removable));
for (i = 0; i < ports; i++) {
- portsc = readl(rhub->ports[i]->addr);
+ portsc = xhci_portsc_readl(rhub->ports[i]);
/* If a device is removable, PORTSC reports a 0, same as in the
* hub descriptor DeviceRemovable bits.
*/
@@ -356,7 +356,7 @@ static void xhci_usb3_hub_descriptor(struct usb_hcd *hcd, struct xhci_hcd *xhci,
port_removable = 0;
/* bit 0 is reserved, bit 1 is for port 1, etc. */
for (i = 0; i < ports; i++) {
- portsc = readl(rhub->ports[i]->addr);
+ portsc = xhci_portsc_readl(rhub->ports[i]);
if (portsc & PORT_DEV_REMOVE)
port_removable |= 1 << (i + 1);
}
@@ -375,11 +375,11 @@ static void xhci_hub_descriptor(struct usb_hcd *hcd, struct xhci_hcd *xhci,
}
-static unsigned int xhci_port_speed(unsigned int port_status)
+static unsigned int xhci_port_speed(int portsc)
{
- if (DEV_LOWSPEED(port_status))
+ if (DEV_LOWSPEED(portsc))
return USB_PORT_STAT_LOW_SPEED;
- if (DEV_HIGHSPEED(port_status))
+ if (DEV_HIGHSPEED(portsc))
return USB_PORT_STAT_HIGH_SPEED;
/*
* FIXME: Yes, we should check for full speed, but the core uses that as
@@ -429,9 +429,9 @@ static unsigned int xhci_port_speed(unsigned int port_status)
/**
* xhci_port_state_to_neutral() - Clean up read portsc value back into writeable
- * @state: u32 port value read from portsc register to be cleanup up
+ * @portsc: u32 port value read from portsc register to be cleanup up
*
- * Given a port state, this function returns a value that would result in the
+ * Given a portsc, this function returns a value that would result in the
* port being in the same state, if the value was written to the port status
* control register.
* Save Read Only (RO) bits and save read/write bits where
@@ -442,10 +442,10 @@ static unsigned int xhci_port_speed(unsigned int port_status)
* changing port state.
*/
-u32 xhci_port_state_to_neutral(u32 state)
+u32 xhci_port_state_to_neutral(u32 portsc)
{
/* Save read-only status and port state */
- return (state & XHCI_PORT_RO) | (state & XHCI_PORT_RWS);
+ return (portsc & XHCI_PORT_RO) | (portsc & XHCI_PORT_RWS);
}
EXPORT_SYMBOL_GPL(xhci_port_state_to_neutral);
@@ -566,19 +566,19 @@ static void xhci_disable_port(struct xhci_hcd *xhci, struct xhci_port *port)
return;
}
- portsc = readl(port->addr);
+ portsc = xhci_portsc_readl(port);
portsc = xhci_port_state_to_neutral(portsc);
/* Write 1 to disable the port */
- writel(portsc | PORT_PE, port->addr);
+ xhci_portsc_writel(port, portsc | PORT_PE);
- portsc = readl(port->addr);
+ portsc = xhci_portsc_readl(port);
xhci_dbg(xhci, "disable port %d-%d, portsc: 0x%x\n",
hcd->self.busnum, port->hcd_portnum + 1, portsc);
}
-static void xhci_clear_port_change_bit(struct xhci_hcd *xhci, u16 wValue,
- u16 wIndex, __le32 __iomem *addr, u32 port_status)
+static void xhci_clear_port_change_bit(struct xhci_hcd *xhci, u16 wValue, struct xhci_port *port,
+ u32 portsc)
{
char *port_change_bit;
u32 status;
@@ -621,11 +621,11 @@ static void xhci_clear_port_change_bit(struct xhci_hcd *xhci, u16 wValue,
return;
}
/* Change bits are all write 1 to clear */
- writel(port_status | status, addr);
- port_status = readl(addr);
+ xhci_portsc_writel(port, portsc | status);
+ portsc = xhci_portsc_readl(port);
xhci_dbg(xhci, "clear port%d %s change, portsc: 0x%x\n",
- wIndex + 1, port_change_bit, port_status);
+ port->hcd_portnum + 1, port_change_bit, portsc);
}
struct xhci_hub *xhci_get_rhub(struct usb_hcd *hcd)
@@ -650,7 +650,7 @@ static void xhci_set_port_power(struct xhci_hcd *xhci, struct xhci_port *port,
u32 temp;
hcd = port->rhub->hcd;
- temp = readl(port->addr);
+ temp = xhci_portsc_readl(port);
xhci_dbg(xhci, "set port power %d-%d %s, portsc: 0x%x\n",
hcd->self.busnum, port->hcd_portnum + 1, on ? "ON" : "OFF", temp);
@@ -659,11 +659,11 @@ static void xhci_set_port_power(struct xhci_hcd *xhci, struct xhci_port *port,
if (on) {
/* Power on */
- writel(temp | PORT_POWER, port->addr);
- readl(port->addr);
+ xhci_portsc_writel(port, temp | PORT_POWER);
+ xhci_portsc_readl(port);
} else {
/* Power off */
- writel(temp & ~PORT_POWER, port->addr);
+ xhci_portsc_writel(port, temp & ~PORT_POWER);
}
spin_unlock_irqrestore(&xhci->lock, *flags);
@@ -675,24 +675,23 @@ static void xhci_set_port_power(struct xhci_hcd *xhci, struct xhci_port *port,
spin_lock_irqsave(&xhci->lock, *flags);
}
-static void xhci_port_set_test_mode(struct xhci_hcd *xhci,
- u16 test_mode, u16 wIndex)
+static void xhci_port_set_test_mode(struct xhci_hcd *xhci, u16 test_mode, int portnum)
{
u32 temp;
struct xhci_port *port;
/* xhci only supports test mode for usb2 ports */
- port = xhci->usb2_rhub.ports[wIndex];
- temp = readl(port->addr + PORTPMSC);
+ port = xhci->usb2_rhub.ports[portnum];
+ temp = readl(&port->port_reg->portpmsc);
temp |= test_mode << PORT_TEST_MODE_SHIFT;
- writel(temp, port->addr + PORTPMSC);
+ writel(temp, &port->port_reg->portpmsc);
xhci->test_mode = test_mode;
if (test_mode == USB_TEST_FORCE_ENABLE)
xhci_start(xhci);
}
-static int xhci_enter_test_mode(struct xhci_hcd *xhci,
- u16 test_mode, u16 wIndex, unsigned long *flags)
+static int xhci_enter_test_mode(struct xhci_hcd *xhci, u16 test_mode, int portnum,
+ unsigned long *flags)
__must_hold(&xhci->lock)
{
int i, retval;
@@ -700,12 +699,11 @@ static int xhci_enter_test_mode(struct xhci_hcd *xhci,
/* Disable all Device Slots */
xhci_dbg(xhci, "Disable all slots\n");
spin_unlock_irqrestore(&xhci->lock, *flags);
- for (i = 1; i <= HCS_MAX_SLOTS(xhci->hcs_params1); i++) {
+ for (i = 1; i <= xhci->max_slots; i++) {
if (!xhci->devs[i])
continue;
- retval = xhci_disable_slot(xhci, i);
- xhci_free_virt_device(xhci, i);
+ retval = xhci_disable_and_free_slot(xhci, i);
if (retval)
xhci_err(xhci, "Failed to disable slot %d, %d. Enter test mode anyway\n",
i, retval);
@@ -727,10 +725,8 @@ static int xhci_enter_test_mode(struct xhci_hcd *xhci,
/* Disable runtime PM for test mode */
pm_runtime_forbid(xhci_to_hcd(xhci)->self.controller);
/* Set PORTPMSC.PTC field to enter selected test mode */
- /* Port is selected by wIndex. port_id = wIndex + 1 */
- xhci_dbg(xhci, "Enter Test Mode: %d, Port_id=%d\n",
- test_mode, wIndex + 1);
- xhci_port_set_test_mode(xhci, test_mode, wIndex);
+ xhci_dbg(xhci, "Enter Test Mode: %u, Port_id=%d\n", test_mode, portnum + 1);
+ xhci_port_set_test_mode(xhci, test_mode, portnum);
return retval;
}
@@ -802,11 +798,11 @@ void xhci_set_link_state(struct xhci_hcd *xhci, struct xhci_port *port,
u32 temp;
u32 portsc;
- portsc = readl(port->addr);
+ portsc = xhci_portsc_readl(port);
temp = xhci_port_state_to_neutral(portsc);
temp &= ~PORT_PLS_MASK;
temp |= PORT_LINK_STROBE | link_state;
- writel(temp, port->addr);
+ xhci_portsc_writel(port, temp);
xhci_dbg(xhci, "Set port %d-%d link state, portsc: 0x%x, write 0x%x",
port->rhub->hcd->self.busnum, port->hcd_portnum + 1,
@@ -818,7 +814,7 @@ static void xhci_set_remote_wake_mask(struct xhci_hcd *xhci,
{
u32 temp;
- temp = readl(port->addr);
+ temp = xhci_portsc_readl(port);
temp = xhci_port_state_to_neutral(temp);
if (wake_mask & USB_PORT_FEAT_REMOTE_WAKE_CONNECT)
@@ -836,7 +832,7 @@ static void xhci_set_remote_wake_mask(struct xhci_hcd *xhci,
else
temp &= ~PORT_WKOC_E;
- writel(temp, port->addr);
+ xhci_portsc_writel(port, temp);
}
/* Test and clear port RWC bit */
@@ -845,62 +841,46 @@ void xhci_test_and_clear_bit(struct xhci_hcd *xhci, struct xhci_port *port,
{
u32 temp;
- temp = readl(port->addr);
+ temp = xhci_portsc_readl(port);
if (temp & port_bit) {
temp = xhci_port_state_to_neutral(temp);
temp |= port_bit;
- writel(temp, port->addr);
+ xhci_portsc_writel(port, temp);
}
}
/* Updates Link Status for super Speed port */
-static void xhci_hub_report_usb3_link_state(struct xhci_hcd *xhci,
- u32 *status, u32 status_reg)
+static void xhci_hub_report_usb3_link_state(struct xhci_hcd *xhci, u32 *status, u32 portsc)
{
- u32 pls = status_reg & PORT_PLS_MASK;
+ u32 pls = portsc & PORT_PLS_MASK;
- /* When the CAS bit is set then warm reset
- * should be performed on port
+ /*
+ * CAS indicates that a warm reset is required, it may be set in any
+ * link state and is only present on roothubs.
*/
- if (status_reg & PORT_CAS) {
- /* The CAS bit can be set while the port is
- * in any link state.
- * Only roothubs have CAS bit, so we
- * pretend to be in compliance mode
- * unless we're already in compliance
- * or the inactive state.
+ if (portsc & PORT_CAS) {
+ /*
+ * If not already in Compliance or Inactive state,
+ * report Compliance Mode so the hub logic triggers a warm reset.
*/
- if (pls != USB_SS_PORT_LS_COMP_MOD &&
- pls != USB_SS_PORT_LS_SS_INACTIVE) {
+ if (pls != XDEV_COMP_MODE && pls != XDEV_INACTIVE)
pls = USB_SS_PORT_LS_COMP_MOD;
- }
- /* Return also connection bit -
- * hub state machine resets port
- * when this bit is set.
- */
- pls |= USB_PORT_STAT_CONNECTION;
- } else {
+
+ /* Signal a connection change to force a reset */
+ *status |= USB_PORT_STAT_CONNECTION;
+ } else if (pls == XDEV_RESUME) {
/*
- * Resume state is an xHCI internal state. Do not report it to
- * usb core, instead, pretend to be U3, thus usb core knows
- * it's not ready for transfer.
+ * Resume is an internal xHCI-only state and must not be exposed
+ * to usbcore. Report it as U3 so transfers are blocked.
*/
- if (pls == XDEV_RESUME) {
- *status |= USB_SS_PORT_LS_U3;
- return;
- }
-
+ pls = USB_SS_PORT_LS_U3;
+ } else if (pls == XDEV_COMP_MODE) {
/*
- * If CAS bit isn't set but the Port is already at
- * Compliance Mode, fake a connection so the USB core
- * notices the Compliance state and resets the port.
- * This resolves an issue generated by the SN65LVPE502CP
- * in which sometimes the port enters compliance mode
- * caused by a delay on the host-device negotiation.
+ * Some hardware may enter Compliance Mode without CAS.
+ * Fake a connection event so usbcore notices and resets the port.
*/
- if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) &&
- (pls == USB_SS_PORT_LS_COMP_MOD))
- pls |= USB_PORT_STAT_CONNECTION;
+ if (xhci->quirks & XHCI_COMP_MODE_QUIRK)
+ *status |= USB_PORT_STAT_CONNECTION;
}
/* update status field */
@@ -914,19 +894,18 @@ static void xhci_hub_report_usb3_link_state(struct xhci_hcd *xhci,
* the compliance mode timer is deleted. A port won't enter
* compliance mode if it has previously entered U0.
*/
-static void xhci_del_comp_mod_timer(struct xhci_hcd *xhci, u32 status,
- u16 wIndex)
+static void xhci_del_comp_mod_timer(struct xhci_hcd *xhci, u32 portsc, int portnum)
{
u32 all_ports_seen_u0 = ((1 << xhci->usb3_rhub.num_ports) - 1);
- bool port_in_u0 = ((status & PORT_PLS_MASK) == XDEV_U0);
+ bool port_in_u0 = ((portsc & PORT_PLS_MASK) == XDEV_U0);
if (!(xhci->quirks & XHCI_COMP_MODE_QUIRK))
return;
if ((xhci->port_status_u0 != all_ports_seen_u0) && port_in_u0) {
- xhci->port_status_u0 |= 1 << wIndex;
+ xhci->port_status_u0 |= 1 << portnum;
if (xhci->port_status_u0 == all_ports_seen_u0) {
- del_timer_sync(&xhci->comp_mode_recovery_timer);
+ timer_delete_sync(&xhci->comp_mode_recovery_timer);
xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
"All USB3 ports have entered U0 already!");
xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
@@ -942,12 +921,12 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port,
struct xhci_bus_state *bus_state;
struct xhci_hcd *xhci;
struct usb_hcd *hcd;
- u32 wIndex;
+ int portnum;
hcd = port->rhub->hcd;
bus_state = &port->rhub->bus_state;
xhci = hcd_to_xhci(hcd);
- wIndex = port->hcd_portnum;
+ portnum = port->hcd_portnum;
if ((portsc & PORT_RESET) || !(portsc & PORT_PE)) {
return -EINVAL;
@@ -955,7 +934,7 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port,
/* did port event handler already start resume timing? */
if (!port->resume_timestamp) {
/* If not, maybe we are in a host initiated resume? */
- if (test_bit(wIndex, &bus_state->resuming_ports)) {
+ if (test_bit(portnum, &bus_state->resuming_ports)) {
/* Host initiated resume doesn't time the resume
* signalling using resume_done[].
* It manually sets RESUME state, sleeps 20ms
@@ -969,20 +948,20 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port,
unsigned long timeout = jiffies +
msecs_to_jiffies(USB_RESUME_TIMEOUT);
- set_bit(wIndex, &bus_state->resuming_ports);
+ set_bit(portnum, &bus_state->resuming_ports);
port->resume_timestamp = timeout;
mod_timer(&hcd->rh_timer, timeout);
- usb_hcd_start_port_resume(&hcd->self, wIndex);
+ usb_hcd_start_port_resume(&hcd->self, portnum);
}
/* Has resume been signalled for USB_RESUME_TIME yet? */
} else if (time_after_eq(jiffies, port->resume_timestamp)) {
int time_left;
xhci_dbg(xhci, "resume USB2 port %d-%d\n",
- hcd->self.busnum, wIndex + 1);
+ hcd->self.busnum, portnum + 1);
port->resume_timestamp = 0;
- clear_bit(wIndex, &bus_state->resuming_ports);
+ clear_bit(portnum, &bus_state->resuming_ports);
reinit_completion(&port->rexit_done);
port->rexit_active = true;
@@ -1003,10 +982,10 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port,
}
xhci_ring_device(xhci, port->slot_id);
} else {
- int port_status = readl(port->addr);
+ int port_status = xhci_portsc_readl(port);
xhci_warn(xhci, "Port resume timed out, port %d-%d: 0x%x\n",
- hcd->self.busnum, wIndex + 1, port_status);
+ hcd->self.busnum, portnum + 1, port_status);
/*
* keep rexit_active set if U0 transition failed so we
* know to report PORT_STAT_SUSPEND status back to
@@ -1015,21 +994,21 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port,
*/
}
- usb_hcd_end_port_resume(&hcd->self, wIndex);
- bus_state->port_c_suspend |= 1 << wIndex;
- bus_state->suspended_ports &= ~(1 << wIndex);
+ usb_hcd_end_port_resume(&hcd->self, portnum);
+ bus_state->port_c_suspend |= 1 << portnum;
+ bus_state->suspended_ports &= ~(1 << portnum);
}
return 0;
}
-static u32 xhci_get_ext_port_status(u32 raw_port_status, u32 port_li)
+static u32 xhci_get_ext_port_status(u32 portsc, u32 port_li)
{
u32 ext_stat = 0;
int speed_id;
/* only support rx and tx lane counts of 1 in usb3.1 spec */
- speed_id = DEV_PORT_SPEED(raw_port_status);
+ speed_id = DEV_PORT_SPEED(portsc);
ext_stat |= speed_id; /* bits 3:0, RX speed id */
ext_stat |= speed_id << 4; /* bits 7:4, TX speed id */
@@ -1154,10 +1133,8 @@ static void xhci_get_usb2_port_status(struct xhci_port *port, u32 *status,
* - Stop the Synopsys redriver Compliance Mode polling.
* - Drop and reacquire the xHCI lock, in order to wait for port resume.
*/
-static u32 xhci_get_port_status(struct usb_hcd *hcd,
- struct xhci_bus_state *bus_state,
- u16 wIndex, u32 raw_port_status,
- unsigned long *flags)
+static u32 xhci_get_port_status(struct usb_hcd *hcd, struct xhci_bus_state *bus_state,
+ int portnum, u32 portsc, unsigned long *flags)
__releases(&xhci->lock)
__acquires(&xhci->lock)
{
@@ -1166,38 +1143,37 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd,
struct xhci_port *port;
rhub = xhci_get_rhub(hcd);
- port = rhub->ports[wIndex];
+ port = rhub->ports[portnum];
/* common wPortChange bits */
- if (raw_port_status & PORT_CSC)
+ if (portsc & PORT_CSC)
status |= USB_PORT_STAT_C_CONNECTION << 16;
- if (raw_port_status & PORT_PEC)
+ if (portsc & PORT_PEC)
status |= USB_PORT_STAT_C_ENABLE << 16;
- if ((raw_port_status & PORT_OCC))
+ if (portsc & PORT_OCC)
status |= USB_PORT_STAT_C_OVERCURRENT << 16;
- if ((raw_port_status & PORT_RC))
+ if (portsc & PORT_RC)
status |= USB_PORT_STAT_C_RESET << 16;
/* common wPortStatus bits */
- if (raw_port_status & PORT_CONNECT) {
+ if (portsc & PORT_CONNECT) {
status |= USB_PORT_STAT_CONNECTION;
- status |= xhci_port_speed(raw_port_status);
+ status |= xhci_port_speed(portsc);
}
- if (raw_port_status & PORT_PE)
+ if (portsc & PORT_PE)
status |= USB_PORT_STAT_ENABLE;
- if (raw_port_status & PORT_OC)
+ if (portsc & PORT_OC)
status |= USB_PORT_STAT_OVERCURRENT;
- if (raw_port_status & PORT_RESET)
+ if (portsc & PORT_RESET)
status |= USB_PORT_STAT_RESET;
/* USB2 and USB3 specific bits, including Port Link State */
if (hcd->speed >= HCD_USB3)
- xhci_get_usb3_port_status(port, &status, raw_port_status);
+ xhci_get_usb3_port_status(port, &status, portsc);
else
- xhci_get_usb2_port_status(port, &status, raw_port_status,
- flags);
+ xhci_get_usb2_port_status(port, &status, portsc, flags);
- if (bus_state->port_c_suspend & (1 << wIndex))
+ if (bus_state->port_c_suspend & (1 << portnum))
status |= USB_PORT_STAT_C_SUSPEND << 16;
return status;
@@ -1209,23 +1185,23 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
int max_ports;
unsigned long flags;
- u32 temp, status;
+ u32 portsc, portpmsc, status;
int retval = 0;
struct xhci_bus_state *bus_state;
- u16 link_state = 0;
- u16 wake_mask = 0;
- u16 timeout = 0;
- u16 test_mode = 0;
+ u16 link_state;
+ u16 wake_mask;
+ u8 timeout;
+ u8 test_mode;
+ u8 desc_type;
struct xhci_hub *rhub;
struct xhci_port **ports;
struct xhci_port *port;
- int portnum1;
+ int portnum;
rhub = xhci_get_rhub(hcd);
ports = rhub->ports;
max_ports = rhub->num_ports;
bus_state = &rhub->bus_state;
- portnum1 = wIndex & 0xff;
spin_lock_irqsave(&xhci->lock, flags);
switch (typeReq) {
@@ -1234,13 +1210,13 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
memset(buf, 0, 4);
break;
case GetHubDescriptor:
+ desc_type = (wValue & 0xff00) >> 8;
/* Check to make sure userspace is asking for the USB 3.0 hub
* descriptor for the USB 3.0 roothub. If not, we stall the
* endpoint, like external hubs do.
*/
if (hcd->speed >= HCD_USB3 &&
- (wLength < USB_DT_SS_HUB_SIZE ||
- wValue != (USB_DT_SS_HUB << 8))) {
+ (wLength < USB_DT_SS_HUB_SIZE || desc_type != USB_DT_SS_HUB)) {
xhci_dbg(xhci, "Wrong hub descriptor type for "
"USB 3.0 roothub.\n");
goto error;
@@ -1249,7 +1225,8 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
(struct usb_hub_descriptor *) buf);
break;
case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
- if ((wValue & 0xff00) != (USB_DT_BOS << 8))
+ desc_type = (wValue & 0xff00) >> 8;
+ if (desc_type != USB_DT_BOS)
goto error;
if (hcd->speed < HCD_USB3)
@@ -1259,29 +1236,28 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
spin_unlock_irqrestore(&xhci->lock, flags);
return retval;
case GetPortStatus:
- if (!portnum1 || portnum1 > max_ports)
+ portnum = (wIndex & 0xff) - 1;
+ if (!in_range(portnum, 0, max_ports))
goto error;
- wIndex--;
- port = ports[portnum1 - 1];
- temp = readl(port->addr);
- if (temp == ~(u32)0) {
+ port = ports[portnum];
+ portsc = xhci_portsc_readl(port);
+ if (portsc == ~(u32)0) {
xhci_hc_died(xhci);
retval = -ENODEV;
break;
}
- trace_xhci_get_port_status(port, temp);
- status = xhci_get_port_status(hcd, bus_state, wIndex, temp,
- &flags);
+ trace_xhci_get_port_status(port, portsc);
+ status = xhci_get_port_status(hcd, bus_state, portnum, portsc, &flags);
if (status == 0xffffffff)
goto error;
xhci_dbg(xhci, "Get port status %d-%d read: 0x%x, return 0x%x",
- hcd->self.busnum, portnum1, temp, status);
+ hcd->self.busnum, portnum + 1, portsc, status);
put_unaligned(cpu_to_le32(status), (__le32 *) buf);
/* if USB 3.1 extended port status return additional 4 bytes */
- if (wValue == 0x02) {
+ if (wValue == HUB_EXT_PORT_STATUS) {
u32 port_li;
if (hcd->speed < HCD_USB31 || wLength != 8) {
@@ -1289,39 +1265,29 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
retval = -EINVAL;
break;
}
- port_li = readl(port->addr + PORTLI);
- status = xhci_get_ext_port_status(temp, port_li);
+ port_li = readl(&port->port_reg->portli);
+ status = xhci_get_ext_port_status(portsc, port_li);
put_unaligned_le32(status, &buf[4]);
}
break;
case SetPortFeature:
- if (wValue == USB_PORT_FEAT_LINK_STATE)
- link_state = (wIndex & 0xff00) >> 3;
- if (wValue == USB_PORT_FEAT_REMOTE_WAKE_MASK)
- wake_mask = wIndex & 0xff00;
- if (wValue == USB_PORT_FEAT_TEST)
- test_mode = (wIndex & 0xff00) >> 8;
- /* The MSB of wIndex is the U1/U2 timeout */
- timeout = (wIndex & 0xff00) >> 8;
-
- wIndex &= 0xff;
- if (!portnum1 || portnum1 > max_ports)
+ portnum = (wIndex & 0xff) - 1;
+ if (!in_range(portnum, 0, max_ports))
goto error;
- port = ports[portnum1 - 1];
- wIndex--;
- temp = readl(port->addr);
- if (temp == ~(u32)0) {
+ port = ports[portnum];
+ portsc = xhci_portsc_readl(port);
+ if (portsc == ~(u32)0) {
xhci_hc_died(xhci);
retval = -ENODEV;
break;
}
- temp = xhci_port_state_to_neutral(temp);
+ portsc = xhci_port_state_to_neutral(portsc);
/* FIXME: What new port features do we need to support? */
switch (wValue) {
case USB_PORT_FEAT_SUSPEND:
- temp = readl(port->addr);
- if ((temp & PORT_PLS_MASK) != XDEV_U0) {
+ portsc = xhci_portsc_readl(port);
+ if ((portsc & PORT_PLS_MASK) != XDEV_U0) {
/* Resume the port to U0 first */
xhci_set_link_state(xhci, port, XDEV_U0);
spin_unlock_irqrestore(&xhci->lock, flags);
@@ -1332,11 +1298,11 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
* a port unless the port reports that it is in the
* enabled (PED = ‘1’,PLS < ‘3’) state.
*/
- temp = readl(port->addr);
- if ((temp & PORT_PE) == 0 || (temp & PORT_RESET)
- || (temp & PORT_PLS_MASK) >= XDEV_U3) {
+ portsc = xhci_portsc_readl(port);
+ if ((portsc & PORT_PE) == 0 || (portsc & PORT_RESET) ||
+ (portsc & PORT_PLS_MASK) >= XDEV_U3) {
xhci_warn(xhci, "USB core suspending port %d-%d not in U0/U1/U2\n",
- hcd->self.busnum, portnum1);
+ hcd->self.busnum, portnum + 1);
goto error;
}
@@ -1355,34 +1321,35 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
msleep(10); /* wait device to enter */
spin_lock_irqsave(&xhci->lock, flags);
- temp = readl(port->addr);
- bus_state->suspended_ports |= 1 << wIndex;
+ portsc = xhci_portsc_readl(port);
+ bus_state->suspended_ports |= 1 << portnum;
break;
case USB_PORT_FEAT_LINK_STATE:
- temp = readl(port->addr);
+ link_state = (wIndex & 0xff00) >> 3;
+ portsc = xhci_portsc_readl(port);
/* Disable port */
if (link_state == USB_SS_PORT_LS_SS_DISABLED) {
xhci_dbg(xhci, "Disable port %d-%d\n",
- hcd->self.busnum, portnum1);
- temp = xhci_port_state_to_neutral(temp);
+ hcd->self.busnum, portnum + 1);
+ portsc = xhci_port_state_to_neutral(portsc);
/*
* Clear all change bits, so that we get a new
* connection event.
*/
- temp |= PORT_CSC | PORT_PEC | PORT_WRC |
- PORT_OCC | PORT_RC | PORT_PLC |
- PORT_CEC;
- writel(temp | PORT_PE, port->addr);
- temp = readl(port->addr);
+ portsc |= PORT_CSC | PORT_PEC | PORT_WRC |
+ PORT_OCC | PORT_RC | PORT_PLC |
+ PORT_CEC;
+ xhci_portsc_writel(port, portsc | PORT_PE);
+ portsc = xhci_portsc_readl(port);
break;
}
/* Put link in RxDetect (enable port) */
if (link_state == USB_SS_PORT_LS_RX_DETECT) {
xhci_dbg(xhci, "Enable port %d-%d\n",
- hcd->self.busnum, portnum1);
- xhci_set_link_state(xhci, port, link_state);
- temp = readl(port->addr);
+ hcd->self.busnum, portnum + 1);
+ xhci_set_link_state(xhci, port, XDEV_RXDETECT);
+ portsc = xhci_portsc_readl(port);
break;
}
@@ -1401,32 +1368,32 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
* automatically entered as on 1.0 and prior.
*/
if (link_state == USB_SS_PORT_LS_COMP_MOD) {
- if (!HCC2_CTC(xhci->hcc_params2)) {
+ if (!(xhci->hcc_params2 & HCC2_CTC)) {
xhci_dbg(xhci, "CTC flag is 0, port already supports entering compliance mode\n");
break;
}
- if ((temp & PORT_CONNECT)) {
+ if ((portsc & PORT_CONNECT)) {
xhci_warn(xhci, "Can't set compliance mode when port is connected\n");
goto error;
}
xhci_dbg(xhci, "Enable compliance mode transition for port %d-%d\n",
- hcd->self.busnum, portnum1);
- xhci_set_link_state(xhci, port, link_state);
+ hcd->self.busnum, portnum + 1);
+ xhci_set_link_state(xhci, port, XDEV_COMP_MODE);
- temp = readl(port->addr);
+ portsc = xhci_portsc_readl(port);
break;
}
/* Port must be enabled */
- if (!(temp & PORT_PE)) {
+ if (!(portsc & PORT_PE)) {
retval = -ENODEV;
break;
}
/* Can't set port link state above '3' (U3) */
if (link_state > USB_SS_PORT_LS_U3) {
xhci_warn(xhci, "Cannot set port %d-%d link state %d\n",
- hcd->self.busnum, portnum1, link_state);
+ hcd->self.busnum, portnum + 1, link_state);
goto error;
}
@@ -1438,7 +1405,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
* completion
*/
if (link_state == USB_SS_PORT_LS_U0) {
- u32 pls = temp & PORT_PLS_MASK;
+ u32 pls = portsc & PORT_PLS_MASK;
bool wait_u0 = false;
/* already in U0 */
@@ -1451,7 +1418,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
reinit_completion(&port->u3exit_done);
}
if (pls <= XDEV_U3) /* U1, U2, U3 */
- xhci_set_link_state(xhci, port, USB_SS_PORT_LS_U0);
+ xhci_set_link_state(xhci, port, XDEV_U0);
if (!wait_u0) {
if (pls > XDEV_U3)
goto error;
@@ -1461,9 +1428,9 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
if (!wait_for_completion_timeout(&port->u3exit_done,
msecs_to_jiffies(500)))
xhci_dbg(xhci, "missing U0 port change event for port %d-%d\n",
- hcd->self.busnum, portnum1);
+ hcd->self.busnum, portnum + 1);
spin_lock_irqsave(&xhci->lock, flags);
- temp = readl(port->addr);
+ portsc = xhci_portsc_readl(port);
break;
}
@@ -1477,17 +1444,17 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
xhci_stop_device(xhci, port->slot_id, 1);
spin_lock_irqsave(&xhci->lock, flags);
}
- xhci_set_link_state(xhci, port, USB_SS_PORT_LS_U3);
+ xhci_set_link_state(xhci, port, XDEV_U3);
spin_unlock_irqrestore(&xhci->lock, flags);
while (retries--) {
usleep_range(4000, 8000);
- temp = readl(port->addr);
- if ((temp & PORT_PLS_MASK) == XDEV_U3)
+ portsc = xhci_portsc_readl(port);
+ if ((portsc & PORT_PLS_MASK) == XDEV_U3)
break;
}
spin_lock_irqsave(&xhci->lock, flags);
- temp = readl(port->addr);
- bus_state->suspended_ports |= 1 << wIndex;
+ portsc = xhci_portsc_readl(port);
+ bus_state->suspended_ports |= 1 << portnum;
}
break;
case USB_PORT_FEAT_POWER:
@@ -1500,93 +1467,98 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
xhci_set_port_power(xhci, port, true, &flags);
break;
case USB_PORT_FEAT_RESET:
- temp = (temp | PORT_RESET);
- writel(temp, port->addr);
+ portsc |= PORT_RESET;
+ xhci_portsc_writel(port, portsc);
- temp = readl(port->addr);
+ portsc = xhci_portsc_readl(port);
xhci_dbg(xhci, "set port reset, actual port %d-%d status = 0x%x\n",
- hcd->self.busnum, portnum1, temp);
+ hcd->self.busnum, portnum + 1, portsc);
break;
case USB_PORT_FEAT_REMOTE_WAKE_MASK:
+ wake_mask = wIndex & 0xff00;
xhci_set_remote_wake_mask(xhci, port, wake_mask);
- temp = readl(port->addr);
+ portsc = xhci_portsc_readl(port);
xhci_dbg(xhci, "set port remote wake mask, actual port %d-%d status = 0x%x\n",
- hcd->self.busnum, portnum1, temp);
+ hcd->self.busnum, portnum + 1, portsc);
break;
case USB_PORT_FEAT_BH_PORT_RESET:
- temp |= PORT_WR;
- writel(temp, port->addr);
- temp = readl(port->addr);
+ portsc |= PORT_WR;
+ xhci_portsc_writel(port, portsc);
+ portsc = xhci_portsc_readl(port);
break;
case USB_PORT_FEAT_U1_TIMEOUT:
if (hcd->speed < HCD_USB3)
goto error;
- temp = readl(port->addr + PORTPMSC);
- temp &= ~PORT_U1_TIMEOUT_MASK;
- temp |= PORT_U1_TIMEOUT(timeout);
- writel(temp, port->addr + PORTPMSC);
+
+ timeout = (wIndex & 0xff00) >> 8;
+ portpmsc = readl(&port->port_reg->portpmsc);
+ portpmsc &= ~PORT_U1_TIMEOUT_MASK;
+ portpmsc |= PORT_U1_TIMEOUT(timeout);
+ writel(portpmsc, &port->port_reg->portpmsc);
break;
case USB_PORT_FEAT_U2_TIMEOUT:
if (hcd->speed < HCD_USB3)
goto error;
- temp = readl(port->addr + PORTPMSC);
- temp &= ~PORT_U2_TIMEOUT_MASK;
- temp |= PORT_U2_TIMEOUT(timeout);
- writel(temp, port->addr + PORTPMSC);
+
+ timeout = (wIndex & 0xff00) >> 8;
+ portpmsc = readl(&port->port_reg->portpmsc);
+ portpmsc &= ~PORT_U2_TIMEOUT_MASK;
+ portpmsc |= PORT_U2_TIMEOUT(timeout);
+ writel(portpmsc, &port->port_reg->portpmsc);
break;
case USB_PORT_FEAT_TEST:
/* 4.19.6 Port Test Modes (USB2 Test Mode) */
if (hcd->speed != HCD_USB2)
goto error;
+
+ test_mode = (wIndex & 0xff00) >> 8;
if (test_mode > USB_TEST_FORCE_ENABLE ||
test_mode < USB_TEST_J)
goto error;
- retval = xhci_enter_test_mode(xhci, test_mode, wIndex,
- &flags);
+ retval = xhci_enter_test_mode(xhci, test_mode, portnum, &flags);
break;
default:
goto error;
}
/* unblock any posted writes */
- temp = readl(port->addr);
+ portsc = xhci_portsc_readl(port);
break;
case ClearPortFeature:
- if (!portnum1 || portnum1 > max_ports)
+ portnum = (wIndex & 0xff) - 1;
+ if (!in_range(portnum, 0, max_ports))
goto error;
- port = ports[portnum1 - 1];
-
- wIndex--;
- temp = readl(port->addr);
- if (temp == ~(u32)0) {
+ port = ports[portnum];
+ portsc = xhci_portsc_readl(port);
+ if (portsc == ~(u32)0) {
xhci_hc_died(xhci);
retval = -ENODEV;
break;
}
/* FIXME: What new port features do we need to support? */
- temp = xhci_port_state_to_neutral(temp);
+ portsc = xhci_port_state_to_neutral(portsc);
switch (wValue) {
case USB_PORT_FEAT_SUSPEND:
- temp = readl(port->addr);
+ portsc = xhci_portsc_readl(port);
xhci_dbg(xhci, "clear USB_PORT_FEAT_SUSPEND\n");
- xhci_dbg(xhci, "PORTSC %04x\n", temp);
- if (temp & PORT_RESET)
+ xhci_dbg(xhci, "PORTSC %04x\n", portsc);
+ if (portsc & PORT_RESET)
goto error;
- if ((temp & PORT_PLS_MASK) == XDEV_U3) {
- if ((temp & PORT_PE) == 0)
+ if ((portsc & PORT_PLS_MASK) == XDEV_U3) {
+ if ((portsc & PORT_PE) == 0)
goto error;
- set_bit(wIndex, &bus_state->resuming_ports);
- usb_hcd_start_port_resume(&hcd->self, wIndex);
+ set_bit(portnum, &bus_state->resuming_ports);
+ usb_hcd_start_port_resume(&hcd->self, portnum);
xhci_set_link_state(xhci, port, XDEV_RESUME);
spin_unlock_irqrestore(&xhci->lock, flags);
msleep(USB_RESUME_TIMEOUT);
spin_lock_irqsave(&xhci->lock, flags);
xhci_set_link_state(xhci, port, XDEV_U0);
- clear_bit(wIndex, &bus_state->resuming_ports);
- usb_hcd_end_port_resume(&hcd->self, wIndex);
+ clear_bit(portnum, &bus_state->resuming_ports);
+ usb_hcd_end_port_resume(&hcd->self, portnum);
}
- bus_state->port_c_suspend |= 1 << wIndex;
+ bus_state->port_c_suspend |= 1 << portnum;
if (!port->slot_id) {
xhci_dbg(xhci, "slot_id is zero\n");
@@ -1595,7 +1567,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
xhci_ring_device(xhci, port->slot_id);
break;
case USB_PORT_FEAT_C_SUSPEND:
- bus_state->port_c_suspend &= ~(1 << wIndex);
+ bus_state->port_c_suspend &= ~(1 << portnum);
fallthrough;
case USB_PORT_FEAT_C_RESET:
case USB_PORT_FEAT_C_BH_PORT_RESET:
@@ -1604,8 +1576,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
case USB_PORT_FEAT_C_ENABLE:
case USB_PORT_FEAT_C_PORT_LINK_STATE:
case USB_PORT_FEAT_C_PORT_CONFIG_ERROR:
- xhci_clear_port_change_bit(xhci, wValue, wIndex,
- port->addr, temp);
+ xhci_clear_port_change_bit(xhci, wValue, port, portsc);
break;
case USB_PORT_FEAT_ENABLE:
xhci_disable_port(xhci, port);
@@ -1672,7 +1643,7 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf)
* SS devices are only visible to roothub after link training completes.
* Keep polling roothubs for a grace period after xHC start
*/
- if (xhci->run_graceperiod) {
+ if (hcd->speed >= HCD_USB3 && xhci->run_graceperiod) {
if (time_before(jiffies, xhci->run_graceperiod))
status = 1;
else
@@ -1683,7 +1654,7 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf)
/* For each port, did anything change? If so, set that bit in buf. */
for (i = 0; i < max_ports; i++) {
- temp = readl(ports[i]->addr);
+ temp = xhci_portsc_readl(ports[i]);
if (temp == ~(u32)0) {
xhci_hc_died(xhci);
retval = -ENODEV;
@@ -1752,7 +1723,7 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
u32 t1, t2;
int retries = 10;
retry:
- t1 = readl(ports[port_index]->addr);
+ t1 = xhci_portsc_readl(ports[port_index]);
t2 = xhci_port_state_to_neutral(t1);
portsc_buf[port_index] = 0;
@@ -1830,7 +1801,7 @@ retry:
spin_lock_irqsave(&xhci->lock, flags);
}
}
- writel(portsc_buf[port_index], ports[port_index]->addr);
+ xhci_portsc_writel(ports[port_index], portsc_buf[port_index]);
}
hcd->state = HC_STATE_SUSPENDED;
bus_state->next_statechange = jiffies + msecs_to_jiffies(10);
@@ -1851,7 +1822,7 @@ static bool xhci_port_missing_cas_quirk(struct xhci_port *port)
{
u32 portsc;
- portsc = readl(port->addr);
+ portsc = xhci_portsc_readl(port);
/* if any of these are set we are not stuck */
if (portsc & (PORT_CONNECT | PORT_CAS))
@@ -1864,9 +1835,9 @@ static bool xhci_port_missing_cas_quirk(struct xhci_port *port)
/* clear wakeup/change bits, and do a warm port reset */
portsc &= ~(PORT_RWC_BITS | PORT_CEC | PORT_WAKE_BITS);
portsc |= PORT_WR;
- writel(portsc, port->addr);
+ xhci_portsc_writel(port, portsc);
/* flush write */
- readl(port->addr);
+ xhci_portsc_readl(port);
return true;
}
@@ -1878,9 +1849,10 @@ int xhci_bus_resume(struct usb_hcd *hcd)
int max_ports, port_index;
int sret;
u32 next_state;
- u32 temp, portsc;
+ u32 portsc;
struct xhci_hub *rhub;
struct xhci_port **ports;
+ bool disabled_irq = false;
rhub = xhci_get_rhub(hcd);
ports = rhub->ports;
@@ -1896,20 +1868,23 @@ int xhci_bus_resume(struct usb_hcd *hcd)
return -ESHUTDOWN;
}
- /* delay the irqs */
- temp = readl(&xhci->op_regs->command);
- temp &= ~CMD_EIE;
- writel(temp, &xhci->op_regs->command);
-
/* bus specific resume for ports we suspended at bus_suspend */
- if (hcd->speed >= HCD_USB3)
+ if (hcd->speed >= HCD_USB3) {
next_state = XDEV_U0;
- else
+ } else {
next_state = XDEV_RESUME;
-
+ if (bus_state->bus_suspended) {
+ /*
+ * prevent port event interrupts from interfering
+ * with usb2 port resume process
+ */
+ xhci_disable_interrupter(xhci, xhci->interrupters[0]);
+ disabled_irq = true;
+ }
+ }
port_index = max_ports;
while (port_index--) {
- portsc = readl(ports[port_index]->addr);
+ portsc = xhci_portsc_readl(ports[port_index]);
/* warm reset CAS limited ports stuck in polling/compliance */
if ((xhci->quirks & XHCI_MISSING_CAS) &&
@@ -1939,7 +1914,7 @@ int xhci_bus_resume(struct usb_hcd *hcd)
}
/* disable wake for all ports, write new link state if needed */
portsc &= ~(PORT_RWC_BITS | PORT_CEC | PORT_WAKE_BITS);
- writel(portsc, ports[port_index]->addr);
+ xhci_portsc_writel(ports[port_index], portsc);
}
/* USB2 specific resume signaling delay and U0 link state transition */
@@ -1960,7 +1935,7 @@ int xhci_bus_resume(struct usb_hcd *hcd)
/* poll for U0 link state complete, both USB2 and USB3 */
for_each_set_bit(port_index, &bus_state->bus_suspended, BITS_PER_LONG) {
- sret = xhci_handshake(ports[port_index]->addr, PORT_PLC,
+ sret = xhci_handshake(&ports[port_index]->port_reg->portsc, PORT_PLC,
PORT_PLC, 10 * 1000);
if (sret) {
xhci_warn(xhci, "port %d-%d resume PLC timeout\n",
@@ -1974,11 +1949,9 @@ int xhci_bus_resume(struct usb_hcd *hcd)
(void) readl(&xhci->op_regs->command);
bus_state->next_statechange = jiffies + msecs_to_jiffies(5);
- /* re-enable irqs */
- temp = readl(&xhci->op_regs->command);
- temp |= CMD_EIE;
- writel(temp, &xhci->op_regs->command);
- temp = readl(&xhci->op_regs->command);
+ /* re-enable interrupter */
+ if (disabled_irq)
+ xhci_enable_interrupter(xhci->interrupters[0]);
spin_unlock_irqrestore(&xhci->lock, flags);
return 0;
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index fdf0c1008225..997fe90f54e5 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -129,6 +129,13 @@ static void xhci_initialize_ring_segments(struct xhci_hcd *xhci, struct xhci_rin
ring->last_seg->trbs[TRBS_PER_SEGMENT - 1].link.control |= cpu_to_le32(LINK_TOGGLE);
}
+void xhci_ring_init(struct xhci_hcd *xhci, struct xhci_ring *ring)
+{
+ xhci_initialize_ring_segments(xhci, ring);
+ xhci_initialize_ring_info(ring);
+ trace_xhci_ring_alloc(ring);
+}
+
/*
* Link the src ring segments to the dst ring.
* Set Toggle Cycle for the new ring if needed.
@@ -389,9 +396,6 @@ struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci, unsigned int num_segs,
if (ret)
goto fail;
- xhci_initialize_ring_segments(xhci, ring);
- xhci_initialize_ring_info(ring);
- trace_xhci_ring_alloc(ring);
return ring;
fail:
@@ -463,7 +467,7 @@ struct xhci_container_ctx *xhci_alloc_container_ctx(struct xhci_hcd *xhci,
return NULL;
ctx->type = type;
- ctx->size = HCC_64BYTE_CONTEXT(xhci->hcc_params) ? 2048 : 1024;
+ ctx->size = xhci->hcc_params & HCC_64BYTE_CONTEXT ? 2048 : 1024;
if (type == XHCI_CTX_TYPE_INPUT)
ctx->size += CTX_SIZE(xhci->hcc_params);
@@ -484,6 +488,35 @@ void xhci_free_container_ctx(struct xhci_hcd *xhci,
kfree(ctx);
}
+struct xhci_container_ctx *xhci_alloc_port_bw_ctx(struct xhci_hcd *xhci,
+ gfp_t flags)
+{
+ struct xhci_container_ctx *ctx;
+ struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
+
+ ctx = kzalloc_node(sizeof(*ctx), flags, dev_to_node(dev));
+ if (!ctx)
+ return NULL;
+
+ ctx->size = GET_PORT_BW_ARRAY_SIZE;
+
+ ctx->bytes = dma_pool_zalloc(xhci->port_bw_pool, flags, &ctx->dma);
+ if (!ctx->bytes) {
+ kfree(ctx);
+ return NULL;
+ }
+ return ctx;
+}
+
+void xhci_free_port_bw_ctx(struct xhci_hcd *xhci,
+ struct xhci_container_ctx *ctx)
+{
+ if (!ctx)
+ return;
+ dma_pool_free(xhci->port_bw_pool, ctx->bytes, ctx->dma);
+ kfree(ctx);
+}
+
struct xhci_input_control_ctx *xhci_get_input_control_ctx(
struct xhci_container_ctx *ctx)
{
@@ -639,6 +672,8 @@ struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci,
cur_ring = stream_info->stream_rings[cur_stream];
if (!cur_ring)
goto cleanup_rings;
+
+ xhci_ring_init(xhci, cur_ring);
cur_ring->stream_id = cur_stream;
cur_ring->trb_address_map = &stream_info->trb_address_map;
/* Set deq ptr, cycle bit, and stream context type */
@@ -836,21 +871,20 @@ free_tts:
* will be manipulated by the configure endpoint, allocate device, or update
* hub functions while this function is removing the TT entries from the list.
*/
-void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id)
+void xhci_free_virt_device(struct xhci_hcd *xhci, struct xhci_virt_device *dev,
+ int slot_id)
{
- struct xhci_virt_device *dev;
int i;
int old_active_eps = 0;
/* Slot ID 0 is reserved */
- if (slot_id == 0 || !xhci->devs[slot_id])
+ if (slot_id == 0 || !dev)
return;
- dev = xhci->devs[slot_id];
-
- xhci->dcbaa->dev_context_ptrs[slot_id] = 0;
- if (!dev)
- return;
+ /* If device ctx array still points to _this_ device, clear it */
+ if (dev->out_ctx &&
+ xhci->dcbaa->dev_context_ptrs[slot_id] == cpu_to_le64(dev->out_ctx->dma))
+ xhci->dcbaa->dev_context_ptrs[slot_id] = 0;
trace_xhci_free_virt_device(dev);
@@ -891,8 +925,9 @@ void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id)
dev->udev->slot_id = 0;
if (dev->rhub_port && dev->rhub_port->slot_id == slot_id)
dev->rhub_port->slot_id = 0;
- kfree(xhci->devs[slot_id]);
- xhci->devs[slot_id] = NULL;
+ if (xhci->devs[slot_id] == dev)
+ xhci->devs[slot_id] = NULL;
+ kfree(dev);
}
/*
@@ -901,7 +936,7 @@ void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id)
* that tt_info, then free the child first. Recursive.
* We can't rely on udev at this point to find child-parent relationships.
*/
-static void xhci_free_virt_devices_depth_first(struct xhci_hcd *xhci, int slot_id)
+void xhci_free_virt_devices_depth_first(struct xhci_hcd *xhci, int slot_id)
{
struct xhci_virt_device *vdev;
struct list_head *tt_list_head;
@@ -922,7 +957,7 @@ static void xhci_free_virt_devices_depth_first(struct xhci_hcd *xhci, int slot_i
/* is this a hub device that added a tt_info to the tts list */
if (tt_info->slot_id == slot_id) {
/* are any devices using this tt_info? */
- for (i = 1; i < HCS_MAX_SLOTS(xhci->hcs_params1); i++) {
+ for (i = 1; i < xhci->max_slots; i++) {
vdev = xhci->devs[i];
if (vdev && (vdev->tt_info == tt_info))
xhci_free_virt_devices_depth_first(
@@ -933,7 +968,7 @@ static void xhci_free_virt_devices_depth_first(struct xhci_hcd *xhci, int slot_i
out:
/* we are now at a leaf device */
xhci_debugfs_remove_slot(xhci, slot_id);
- xhci_free_virt_device(xhci, slot_id);
+ xhci_free_virt_device(xhci, xhci->devs[slot_id], slot_id);
}
int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id,
@@ -948,7 +983,7 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id,
return 0;
}
- dev = kzalloc(sizeof(*dev), flags);
+ dev = kzalloc_obj(*dev, flags);
if (!dev)
return 0;
@@ -959,14 +994,14 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id,
if (!dev->out_ctx)
goto fail;
- xhci_dbg(xhci, "Slot %d output ctx = 0x%pad (dma)\n", slot_id, &dev->out_ctx->dma);
+ xhci_dbg(xhci, "Slot %d output ctx = %pad (dma)\n", slot_id, &dev->out_ctx->dma);
/* Allocate the (input) device context for address device command */
dev->in_ctx = xhci_alloc_container_ctx(xhci, XHCI_CTX_TYPE_INPUT, flags);
if (!dev->in_ctx)
goto fail;
- xhci_dbg(xhci, "Slot %d input ctx = 0x%pad (dma)\n", slot_id, &dev->in_ctx->dma);
+ xhci_dbg(xhci, "Slot %d input ctx = %pad (dma)\n", slot_id, &dev->in_ctx->dma);
/* Initialize the cancellation and bandwidth list for each ep */
for (i = 0; i < 31; i++) {
@@ -982,6 +1017,8 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id,
if (!dev->eps[0].ring)
goto fail;
+ xhci_ring_init(xhci, dev->eps[0].ring);
+
dev->udev = udev;
/* Point to output device context in dcbaa. */
@@ -1166,6 +1203,8 @@ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *ud
ep0_ctx->deq = cpu_to_le64(dev->eps[0].ring->first_seg->dma |
dev->eps[0].ring->cycle_state);
+ ep0_ctx->tx_info = cpu_to_le32(EP_AVG_TRB_LENGTH(8));
+
trace_xhci_setup_addressable_virt_device(dev);
/* Steps 7 and 8 were done in xhci_alloc_virt_device() */
@@ -1299,18 +1338,33 @@ static unsigned int xhci_get_endpoint_interval(struct usb_device *udev,
return interval;
}
-/* The "Mult" field in the endpoint context is only set for SuperSpeed isoc eps.
+/*
+ * xHCs without LEC use the "Mult" field in the endpoint context for SuperSpeed
+ * isoc eps, and High speed isoc eps that support bandwidth doubling. Standard
* High speed endpoint descriptors can define "the number of additional
* transaction opportunities per microframe", but that goes in the Max Burst
* endpoint context field.
*/
-static u32 xhci_get_endpoint_mult(struct usb_device *udev,
- struct usb_host_endpoint *ep)
+static u32 xhci_get_endpoint_mult(struct xhci_hcd *xhci,
+ struct usb_device *udev,
+ struct usb_host_endpoint *ep)
{
- if (udev->speed < USB_SPEED_SUPER ||
- !usb_endpoint_xfer_isoc(&ep->desc))
- return 0;
- return ep->ss_ep_comp.bmAttributes;
+ bool lec;
+
+ /* xHCI 1.1 with LEC set does not use mult field, except intel eUSB2 */
+ lec = xhci->hci_version > 0x100 && (xhci->hcc_params2 & HCC2_LEC);
+
+ /* eUSB2 double isoc bw devices are the only USB2 devices using mult */
+ if (usb_endpoint_is_hs_isoc_double(udev, ep) &&
+ (!lec || xhci->quirks & XHCI_INTEL_HOST))
+ return 1;
+
+ /* SuperSpeed isoc transfers on hosts without LEC uses mult field */
+ if (udev->speed >= USB_SPEED_SUPER &&
+ usb_endpoint_xfer_isoc(&ep->desc) && !lec)
+ return ep->ss_ep_comp.bmAttributes;
+
+ return 0;
}
static u32 xhci_get_endpoint_max_burst(struct usb_device *udev,
@@ -1322,8 +1376,16 @@ static u32 xhci_get_endpoint_max_burst(struct usb_device *udev,
if (udev->speed == USB_SPEED_HIGH &&
(usb_endpoint_xfer_isoc(&ep->desc) ||
- usb_endpoint_xfer_int(&ep->desc)))
+ usb_endpoint_xfer_int(&ep->desc))) {
+ /*
+ * USB 2 Isochronous Double IN Bandwidth ECN uses fixed burst
+ * size and max packets bits 12:11 are invalid.
+ */
+ if (usb_endpoint_is_hs_isoc_double(udev, ep))
+ return 2;
+
return usb_endpoint_maxp_mult(&ep->desc) - 1;
+ }
return 0;
}
@@ -1347,36 +1409,6 @@ static u32 xhci_get_endpoint_type(struct usb_host_endpoint *ep)
return 0;
}
-/* Return the maximum endpoint service interval time (ESIT) payload.
- * Basically, this is the maxpacket size, multiplied by the burst size
- * and mult size.
- */
-static u32 xhci_get_max_esit_payload(struct usb_device *udev,
- struct usb_host_endpoint *ep)
-{
- int max_burst;
- int max_packet;
-
- /* Only applies for interrupt or isochronous endpoints */
- if (usb_endpoint_xfer_control(&ep->desc) ||
- usb_endpoint_xfer_bulk(&ep->desc))
- return 0;
-
- /* SuperSpeedPlus Isoc ep sending over 48k per esit */
- if ((udev->speed >= USB_SPEED_SUPER_PLUS) &&
- USB_SS_SSP_ISOC_COMP(ep->ss_ep_comp.bmAttributes))
- return le32_to_cpu(ep->ssp_isoc_ep_comp.dwBytesPerInterval);
-
- /* SuperSpeed or SuperSpeedPlus Isoc ep with less than 48k per esit */
- if (udev->speed >= USB_SPEED_SUPER)
- return le16_to_cpu(ep->ss_ep_comp.wBytesPerInterval);
-
- max_packet = usb_endpoint_maxp(&ep->desc);
- max_burst = usb_endpoint_maxp_mult(&ep->desc);
- /* A 0 in max burst means 1 transfer per ESIT */
- return max_packet * max_burst;
-}
-
/* Set up an endpoint with one ring segment. Do not allocate stream rings.
* Drivers will have to call usb_alloc_streams() to do that.
*/
@@ -1408,18 +1440,28 @@ int xhci_endpoint_init(struct xhci_hcd *xhci,
ring_type = usb_endpoint_type(&ep->desc);
+ /* Ensure host supports double isoc bandwidth for eUSB2 devices */
+ if (usb_endpoint_is_hs_isoc_double(udev, ep) && !(xhci->hcc_params2 & HCC2_EUSB2_DIC)) {
+ dev_dbg(&udev->dev, "Double Isoc Bandwidth not supported by xhci\n");
+ return -EINVAL;
+ }
+
/*
* Get values to fill the endpoint context, mostly from ep descriptor.
* The average TRB buffer lengt for bulk endpoints is unclear as we
* have no clue on scatter gather list entry size. For Isoc and Int,
* set it to max available. See xHCI 1.1 spec 4.14.1.1 for details.
*/
- max_esit_payload = xhci_get_max_esit_payload(udev, ep);
+ max_esit_payload = usb_endpoint_max_periodic_payload(udev, ep);
interval = xhci_get_endpoint_interval(udev, ep);
/* Periodic endpoint bInterval limit quirk */
if (usb_endpoint_xfer_int(&ep->desc) ||
usb_endpoint_xfer_isoc(&ep->desc)) {
+ if ((xhci->quirks & XHCI_LIMIT_ENDPOINT_INTERVAL_9) &&
+ interval >= 9) {
+ interval = 8;
+ }
if ((xhci->quirks & XHCI_LIMIT_ENDPOINT_INTERVAL_7) &&
udev->speed >= USB_SPEED_HIGH &&
interval >= 7) {
@@ -1427,8 +1469,8 @@ int xhci_endpoint_init(struct xhci_hcd *xhci,
}
}
- mult = xhci_get_endpoint_mult(udev, ep);
- max_packet = usb_endpoint_maxp(&ep->desc);
+ mult = xhci_get_endpoint_mult(xhci, udev, ep);
+ max_packet = xhci_usb_endpoint_maxp(udev, ep);
max_burst = xhci_get_endpoint_max_burst(udev, ep);
avg_trb_len = max_esit_payload;
@@ -1449,9 +1491,6 @@ int xhci_endpoint_init(struct xhci_hcd *xhci,
/* xHCI 1.0 and 1.1 indicates that ctrl ep avg TRB Length should be 8 */
if (usb_endpoint_xfer_control(&ep->desc) && xhci->hci_version >= 0x100)
avg_trb_len = 8;
- /* xhci 1.1 with LEC support doesn't use mult field, use RsvdZ */
- if ((xhci->hci_version > 0x100) && HCC2_LEC(xhci->hcc_params2))
- mult = 0;
/* Set up the endpoint ring */
virt_dev->eps[ep_index].new_ring =
@@ -1461,6 +1500,7 @@ int xhci_endpoint_init(struct xhci_hcd *xhci,
virt_dev->eps[ep_index].skip = false;
ep_ring = virt_dev->eps[ep_index].new_ring;
+ xhci_ring_init(xhci, ep_ring);
/* Fill the endpoint context */
ep_ctx->ep_info = cpu_to_le32(EP_MAX_ESIT_PAYLOAD_HI(max_esit_payload) |
@@ -1802,10 +1842,10 @@ xhci_remove_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir)
*/
if (ir->ir_set) {
tmp = readl(&ir->ir_set->erst_size);
- tmp &= ERST_SIZE_MASK;
+ tmp &= ~ERST_SIZE_MASK;
writel(tmp, &ir->ir_set->erst_size);
- xhci_write_64(xhci, ERST_EHB, &ir->ir_set->erst_dequeue);
+ xhci_update_erst_dequeue(xhci, ir, true);
}
}
@@ -1848,6 +1888,11 @@ void xhci_remove_secondary_interrupter(struct usb_hcd *hcd, struct xhci_interrup
return;
}
+ /*
+ * Cleanup secondary interrupter to ensure there are no pending events.
+ * This also updates event ring dequeue pointer back to the start.
+ */
+ xhci_skip_sec_intr_events(xhci, ir->event_ring, ir);
intr_num = ir->intr_num;
xhci_remove_interrupter(xhci, ir);
@@ -1859,10 +1904,36 @@ void xhci_remove_secondary_interrupter(struct usb_hcd *hcd, struct xhci_interrup
}
EXPORT_SYMBOL_GPL(xhci_remove_secondary_interrupter);
+/* Cleanup roothub bandwidth data */
+void xhci_rh_bw_cleanup(struct xhci_hcd *xhci)
+{
+ struct xhci_root_port_bw_info *rh_bw;
+ struct xhci_tt_bw_info *tt_info, *tt_next;
+ struct list_head *eps, *ep, *ep_next;
+
+ for (int i = 0; i < xhci->max_ports; i++) {
+ rh_bw = &xhci->rh_bw[i];
+
+ /* Clear and free all TT bandwidth entries */
+ list_for_each_entry_safe(tt_info, tt_next, &rh_bw->tts, tt_list) {
+ list_del(&tt_info->tt_list);
+ kfree(tt_info);
+ }
+
+ /* Clear per-interval endpoint lists */
+ for (int j = 0; j < XHCI_MAX_INTERVAL; j++) {
+ eps = &rh_bw->bw_table.interval_bw[j].endpoints;
+
+ list_for_each_safe(ep, ep_next, eps)
+ list_del_init(ep);
+ }
+ }
+}
+
void xhci_mem_cleanup(struct xhci_hcd *xhci)
{
struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
- int i, j, num_ports;
+ int i;
cancel_delayed_work_sync(&xhci->cmd_timer);
@@ -1881,17 +1952,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed command ring");
xhci_cleanup_command_queue(xhci);
- num_ports = HCS_MAX_PORTS(xhci->hcs_params1);
- for (i = 0; i < num_ports && xhci->rh_bw; i++) {
- struct xhci_interval_bw_table *bwt = &xhci->rh_bw[i].bw_table;
- for (j = 0; j < XHCI_MAX_INTERVAL; j++) {
- struct list_head *ep = &bwt->interval_bw[j].endpoints;
- while (!list_empty(ep))
- list_del_init(ep->next);
- }
- }
-
- for (i = HCS_MAX_SLOTS(xhci->hcs_params1); i > 0; i--)
+ for (i = xhci->max_slots; i > 0; i--)
xhci_free_virt_devices_depth_first(xhci, i);
dma_pool_destroy(xhci->segment_pool);
@@ -1907,6 +1968,11 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
"Freed small stream array pool");
+ dma_pool_destroy(xhci->port_bw_pool);
+ xhci->port_bw_pool = NULL;
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init,
+ "Freed xhci port bw array pool");
+
dma_pool_destroy(xhci->medium_streams_pool);
xhci->medium_streams_pool = NULL;
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
@@ -1919,18 +1985,9 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
scratchpad_free(xhci);
- if (!xhci->rh_bw)
- goto no_bw;
-
- for (i = 0; i < num_ports; i++) {
- struct xhci_tt_bw_info *tt, *n;
- list_for_each_entry_safe(tt, n, &xhci->rh_bw[i].tts, tt_list) {
- list_del(&tt->tt_list);
- kfree(tt);
- }
- }
+ if (xhci->rh_bw)
+ xhci_rh_bw_cleanup(xhci);
-no_bw:
xhci->cmd_ring_reserved_trbs = 0;
xhci->usb2_rhub.num_ports = 0;
xhci->usb3_rhub.num_ports = 0;
@@ -1952,8 +2009,6 @@ no_bw:
xhci->port_caps = NULL;
xhci->interrupters = NULL;
- xhci->page_size = 0;
- xhci->page_shift = 0;
xhci->usb2_rhub.bus_state.bus_suspended = 0;
xhci->usb3_rhub.bus_state.bus_suspended = 0;
}
@@ -2110,21 +2165,34 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports,
/* FIXME: Should we disable ports not in the Extended Capabilities? */
}
-static void xhci_create_rhub_port_array(struct xhci_hcd *xhci,
- struct xhci_hub *rhub, gfp_t flags)
+static void xhci_create_rhub_port_array(struct xhci_hcd *xhci, struct xhci_hub *rhub,
+ unsigned int max_ports, gfp_t flags)
{
int port_index = 0;
int i;
struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
- if (!rhub->num_ports)
+ if (!rhub->num_ports) {
+ xhci_info(xhci, "USB%u root hub has no ports\n", rhub->maj_rev);
return;
+ }
+
+ /*
+ * Place limits on the number of roothub ports so that the hub
+ * descriptors aren't longer than the USB core will allocate.
+ */
+ if (rhub->num_ports > max_ports) {
+ xhci->usb3_rhub.num_ports = max_ports;
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Limiting USB%u root hub ports to %u",
+ rhub->maj_rev, max_ports);
+ }
+
rhub->ports = kcalloc_node(rhub->num_ports, sizeof(*rhub->ports),
flags, dev_to_node(dev));
if (!rhub->ports)
return;
- for (i = 0; i < HCS_MAX_PORTS(xhci->hcs_params1); i++) {
+ for (i = 0; i < xhci->max_ports; i++) {
if (xhci->hw_ports[i].rhub != rhub ||
xhci->hw_ports[i].hcd_portnum == DUPLICATE_ENTRY)
continue;
@@ -2147,32 +2215,28 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags)
{
void __iomem *base;
u32 offset;
- unsigned int num_ports;
int i, j;
int cap_count = 0;
u32 cap_start;
struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
- num_ports = HCS_MAX_PORTS(xhci->hcs_params1);
- xhci->hw_ports = kcalloc_node(num_ports, sizeof(*xhci->hw_ports),
- flags, dev_to_node(dev));
+ xhci->hw_ports = kcalloc_node(xhci->max_ports, sizeof(*xhci->hw_ports),
+ flags, dev_to_node(dev));
if (!xhci->hw_ports)
return -ENOMEM;
- for (i = 0; i < num_ports; i++) {
- xhci->hw_ports[i].addr = &xhci->op_regs->port_status_base +
- NUM_PORT_REGS * i;
+ for (i = 0; i < xhci->max_ports; i++) {
+ xhci->hw_ports[i].port_reg = &xhci->op_regs->port_regs[i];
xhci->hw_ports[i].hw_portnum = i;
init_completion(&xhci->hw_ports[i].rexit_done);
init_completion(&xhci->hw_ports[i].u3exit_done);
}
- xhci->rh_bw = kcalloc_node(num_ports, sizeof(*xhci->rh_bw), flags,
- dev_to_node(dev));
+ xhci->rh_bw = kcalloc_node(xhci->max_ports, sizeof(*xhci->rh_bw), flags, dev_to_node(dev));
if (!xhci->rh_bw)
return -ENOMEM;
- for (i = 0; i < num_ports; i++) {
+ for (i = 0; i < xhci->max_ports; i++) {
struct xhci_interval_bw_table *bw_table;
INIT_LIST_HEAD(&xhci->rh_bw[i].tts);
@@ -2204,9 +2268,8 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags)
offset = cap_start;
while (offset) {
- xhci_add_in_port(xhci, num_ports, base + offset, cap_count);
- if (xhci->usb2_rhub.num_ports + xhci->usb3_rhub.num_ports ==
- num_ports)
+ xhci_add_in_port(xhci, xhci->max_ports, base + offset, cap_count);
+ if (xhci->usb2_rhub.num_ports + xhci->usb3_rhub.num_ports == xhci->max_ports)
break;
offset = xhci_find_next_ext_cap(base, offset,
XHCI_EXT_CAPS_PROTOCOL);
@@ -2219,30 +2282,8 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags)
"Found %u USB 2.0 ports and %u USB 3.0 ports.",
xhci->usb2_rhub.num_ports, xhci->usb3_rhub.num_ports);
- /* Place limits on the number of roothub ports so that the hub
- * descriptors aren't longer than the USB core will allocate.
- */
- if (xhci->usb3_rhub.num_ports > USB_SS_MAXPORTS) {
- xhci_dbg_trace(xhci, trace_xhci_dbg_init,
- "Limiting USB 3.0 roothub ports to %u.",
- USB_SS_MAXPORTS);
- xhci->usb3_rhub.num_ports = USB_SS_MAXPORTS;
- }
- if (xhci->usb2_rhub.num_ports > USB_MAXCHILDREN) {
- xhci_dbg_trace(xhci, trace_xhci_dbg_init,
- "Limiting USB 2.0 roothub ports to %u.",
- USB_MAXCHILDREN);
- xhci->usb2_rhub.num_ports = USB_MAXCHILDREN;
- }
-
- if (!xhci->usb2_rhub.num_ports)
- xhci_info(xhci, "USB2 root hub has no ports\n");
-
- if (!xhci->usb3_rhub.num_ports)
- xhci_info(xhci, "USB3 root hub has no ports\n");
-
- xhci_create_rhub_port_array(xhci, &xhci->usb2_rhub, flags);
- xhci_create_rhub_port_array(xhci, &xhci->usb3_rhub, flags);
+ xhci_create_rhub_port_array(xhci, &xhci->usb2_rhub, USB_MAXCHILDREN, flags);
+ xhci_create_rhub_port_array(xhci, &xhci->usb3_rhub, USB_SS_MAXPORTS, flags);
return 0;
}
@@ -2283,37 +2324,25 @@ xhci_alloc_interrupter(struct xhci_hcd *xhci, unsigned int segs, gfp_t flags)
return ir;
}
-static int
-xhci_add_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir,
- unsigned int intr_num)
+void xhci_add_interrupter(struct xhci_hcd *xhci, unsigned int intr_num)
{
+ struct xhci_interrupter *ir;
u64 erst_base;
u32 erst_size;
- if (intr_num >= xhci->max_interrupters) {
- xhci_warn(xhci, "Can't add interrupter %d, max interrupters %d\n",
- intr_num, xhci->max_interrupters);
- return -EINVAL;
- }
-
- if (xhci->interrupters[intr_num]) {
- xhci_warn(xhci, "Interrupter %d\n already set up", intr_num);
- return -EINVAL;
- }
-
- xhci->interrupters[intr_num] = ir;
+ ir = xhci->interrupters[intr_num];
ir->intr_num = intr_num;
ir->ir_set = &xhci->run_regs->ir_set[intr_num];
/* set ERST count with the number of entries in the segment table */
erst_size = readl(&ir->ir_set->erst_size);
- erst_size &= ERST_SIZE_MASK;
+ erst_size &= ~ERST_SIZE_MASK;
erst_size |= ir->event_ring->num_segs;
writel(erst_size, &ir->ir_set->erst_size);
erst_base = xhci_read_64(xhci, &ir->ir_set->erst_base);
- erst_base &= ERST_BASE_RSVDP;
- erst_base |= ir->erst.erst_dma_addr & ~ERST_BASE_RSVDP;
+ erst_base &= ~ERST_BASE_ADDRESS_MASK;
+ erst_base |= ir->erst.erst_dma_addr & ERST_BASE_ADDRESS_MASK;
if (xhci->quirks & XHCI_WRITE_64_HI_LO)
hi_lo_writeq(erst_base, &ir->ir_set->erst_base);
else
@@ -2321,36 +2350,45 @@ xhci_add_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir,
/* Set the event ring dequeue address of this interrupter */
xhci_set_hc_event_deq(xhci, ir);
-
- return 0;
}
struct xhci_interrupter *
xhci_create_secondary_interrupter(struct usb_hcd *hcd, unsigned int segs,
- u32 imod_interval)
+ u32 imod_interval, unsigned int intr_num)
{
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
struct xhci_interrupter *ir;
unsigned int i;
int err = -ENOSPC;
- if (!xhci->interrupters || xhci->max_interrupters <= 1)
+ if (!xhci->interrupters || xhci->max_interrupters <= 1 ||
+ intr_num >= xhci->max_interrupters)
return NULL;
ir = xhci_alloc_interrupter(xhci, segs, GFP_KERNEL);
if (!ir)
return NULL;
- spin_lock_irq(&xhci->lock);
+ xhci_ring_init(xhci, ir->event_ring);
- /* Find available secondary interrupter, interrupter 0 is reserved for primary */
- for (i = 1; i < xhci->max_interrupters; i++) {
- if (xhci->interrupters[i] == NULL) {
- err = xhci_add_interrupter(xhci, ir, i);
- break;
+ spin_lock_irq(&xhci->lock);
+ if (!intr_num) {
+ /* Find available secondary interrupter, interrupter 0 is reserved for primary */
+ for (i = 1; i < xhci->max_interrupters; i++) {
+ if (!xhci->interrupters[i]) {
+ xhci->interrupters[i] = ir;
+ xhci_add_interrupter(xhci, i);
+ err = 0;
+ break;
+ }
+ }
+ } else {
+ if (!xhci->interrupters[intr_num]) {
+ xhci->interrupters[intr_num] = ir;
+ xhci_add_interrupter(xhci, intr_num);
+ err = 0;
}
}
-
spin_unlock_irq(&xhci->lock);
if (err) {
@@ -2360,13 +2398,10 @@ xhci_create_secondary_interrupter(struct usb_hcd *hcd, unsigned int segs,
return NULL;
}
- err = xhci_set_interrupter_moderation(ir, imod_interval);
- if (err)
- xhci_warn(xhci, "Failed to set interrupter %d moderation to %uns\n",
- i, imod_interval);
+ xhci_set_interrupter_moderation(ir, imod_interval);
xhci_dbg(xhci, "Add secondary interrupter %d, max interrupters %d\n",
- i, xhci->max_interrupters);
+ ir->intr_num, xhci->max_interrupters);
return ir;
}
@@ -2374,61 +2409,23 @@ EXPORT_SYMBOL_GPL(xhci_create_secondary_interrupter);
int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
{
- struct xhci_interrupter *ir;
struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
dma_addr_t dma;
- unsigned int val, val2;
- u64 val_64;
- u32 page_size, temp;
- int i;
-
- INIT_LIST_HEAD(&xhci->cmd_list);
-
- /* init command timeout work */
- INIT_DELAYED_WORK(&xhci->cmd_timer, xhci_handle_command_timeout);
- init_completion(&xhci->cmd_ring_stop_completion);
-
- page_size = readl(&xhci->op_regs->page_size);
- xhci_dbg_trace(xhci, trace_xhci_dbg_init,
- "Supported page size register = 0x%x", page_size);
- i = ffs(page_size);
- if (i < 16)
- xhci_dbg_trace(xhci, trace_xhci_dbg_init,
- "Supported page size of %iK", (1 << (i+12)) / 1024);
- else
- xhci_warn(xhci, "WARN: no supported page size\n");
- /* Use 4K pages, since that's common and the minimum the HC supports */
- xhci->page_shift = 12;
- xhci->page_size = 1 << xhci->page_shift;
- xhci_dbg_trace(xhci, trace_xhci_dbg_init,
- "HCD page size set to %iK", xhci->page_size / 1024);
- /*
- * Program the Number of Device Slots Enabled field in the CONFIG
- * register with the max value of slots the HC can handle.
- */
- val = HCS_MAX_SLOTS(readl(&xhci->cap_regs->hcs_params1));
- xhci_dbg_trace(xhci, trace_xhci_dbg_init,
- "// xHC can handle at most %d device slots.", val);
- val2 = readl(&xhci->op_regs->config_reg);
- val |= (val2 & ~HCS_SLOTS_MASK);
- xhci_dbg_trace(xhci, trace_xhci_dbg_init,
- "// Setting Max device slots reg = 0x%x.", val);
- writel(val, &xhci->op_regs->config_reg);
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Starting %s", __func__);
/*
* xHCI section 5.4.6 - Device Context array must be
* "physically contiguous and 64-byte (cache line) aligned".
*/
- xhci->dcbaa = dma_alloc_coherent(dev, sizeof(*xhci->dcbaa), &dma,
- flags);
+ xhci->dcbaa = dma_alloc_coherent(dev, sizeof(*xhci->dcbaa), &dma, flags);
if (!xhci->dcbaa)
goto fail;
+
xhci->dcbaa->dma = dma;
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
- "// Device context base array address = 0x%pad (DMA), %p (virt)",
- &xhci->dcbaa->dma, xhci->dcbaa);
- xhci_write_64(xhci, dma, &xhci->op_regs->dcbaa_ptr);
+ "Device context base array address = %pad (DMA), %p (virt)",
+ &xhci->dcbaa->dma, xhci->dcbaa);
/*
* Initialize the ring segment pool. The ring must be a contiguous
@@ -2444,92 +2441,69 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
else
xhci->segment_pool = dma_pool_create("xHCI ring segments", dev,
TRB_SEGMENT_SIZE, TRB_SEGMENT_SIZE, xhci->page_size);
+ if (!xhci->segment_pool)
+ goto fail;
/* See Table 46 and Note on Figure 55 */
- xhci->device_pool = dma_pool_create("xHCI input/output contexts", dev,
- 2112, 64, xhci->page_size);
- if (!xhci->segment_pool || !xhci->device_pool)
+ xhci->device_pool = dma_pool_create("xHCI input/output contexts", dev, 2112, 64,
+ xhci->page_size);
+ if (!xhci->device_pool)
goto fail;
- /* Linear stream context arrays don't have any boundary restrictions,
+ /*
+ * Linear stream context arrays don't have any boundary restrictions,
* and only need to be 16-byte aligned.
*/
- xhci->small_streams_pool =
- dma_pool_create("xHCI 256 byte stream ctx arrays",
- dev, SMALL_STREAM_ARRAY_SIZE, 16, 0);
- xhci->medium_streams_pool =
- dma_pool_create("xHCI 1KB stream ctx arrays",
- dev, MEDIUM_STREAM_ARRAY_SIZE, 16, 0);
- /* Any stream context array bigger than MEDIUM_STREAM_ARRAY_SIZE
- * will be allocated with dma_alloc_coherent()
+ xhci->small_streams_pool = dma_pool_create("xHCI 256 byte stream ctx arrays",
+ dev, SMALL_STREAM_ARRAY_SIZE, 16, 0);
+ if (!xhci->small_streams_pool)
+ goto fail;
+
+ /*
+ * Any stream context array bigger than MEDIUM_STREAM_ARRAY_SIZE will be
+ * allocated with dma_alloc_coherent().
*/
- if (!xhci->small_streams_pool || !xhci->medium_streams_pool)
+ xhci->medium_streams_pool = dma_pool_create("xHCI 1KB stream ctx arrays",
+ dev, MEDIUM_STREAM_ARRAY_SIZE, 16, 0);
+ if (!xhci->medium_streams_pool)
+ goto fail;
+
+ /*
+ * refer to xhci rev1_2 protocol 5.3.3 max ports is 255.
+ * refer to xhci rev1_2 protocol 6.4.3.14 port bandwidth buffer need
+ * to be 16-byte aligned.
+ */
+ xhci->port_bw_pool = dma_pool_create("xHCI 256 port bw ctx arrays",
+ dev, GET_PORT_BW_ARRAY_SIZE, 16, 0);
+ if (!xhci->port_bw_pool)
goto fail;
/* Set up the command ring to have one segments for now. */
xhci->cmd_ring = xhci_ring_alloc(xhci, 1, TYPE_COMMAND, 0, flags);
if (!xhci->cmd_ring)
goto fail;
- xhci_dbg_trace(xhci, trace_xhci_dbg_init,
- "Allocated command ring at %p", xhci->cmd_ring);
- xhci_dbg_trace(xhci, trace_xhci_dbg_init, "First segment DMA is 0x%pad",
- &xhci->cmd_ring->first_seg->dma);
-
- /* Set the address in the Command Ring Control register */
- val_64 = xhci_read_64(xhci, &xhci->op_regs->cmd_ring);
- val_64 = (val_64 & (u64) CMD_RING_RSVD_BITS) |
- (xhci->cmd_ring->first_seg->dma & (u64) ~CMD_RING_RSVD_BITS) |
- xhci->cmd_ring->cycle_state;
- xhci_dbg_trace(xhci, trace_xhci_dbg_init,
- "// Setting command ring address to 0x%016llx", val_64);
- xhci_write_64(xhci, val_64, &xhci->op_regs->cmd_ring);
-
- /* Reserve one command ring TRB for disabling LPM.
- * Since the USB core grabs the shared usb_bus bandwidth mutex before
- * disabling LPM, we only need to reserve one TRB for all devices.
- */
- xhci->cmd_ring_reserved_trbs++;
- val = readl(&xhci->cap_regs->db_off);
- val &= DBOFF_MASK;
- xhci_dbg_trace(xhci, trace_xhci_dbg_init,
- "// Doorbell array is located at offset 0x%x from cap regs base addr",
- val);
- xhci->dba = (void __iomem *) xhci->cap_regs + val;
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Allocated command ring at %p", xhci->cmd_ring);
/* Allocate and set up primary interrupter 0 with an event ring. */
- xhci_dbg_trace(xhci, trace_xhci_dbg_init,
- "Allocating primary event ring");
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Allocating primary event ring");
xhci->interrupters = kcalloc_node(xhci->max_interrupters, sizeof(*xhci->interrupters),
flags, dev_to_node(dev));
-
- ir = xhci_alloc_interrupter(xhci, 0, flags);
- if (!ir)
+ if (!xhci->interrupters)
goto fail;
- if (xhci_add_interrupter(xhci, ir, 0))
+ xhci->interrupters[0] = xhci_alloc_interrupter(xhci, 0, flags);
+ if (!xhci->interrupters[0])
goto fail;
- ir->isoc_bei_interval = AVOID_BEI_INTERVAL_MAX;
-
- for (i = 0; i < MAX_HC_SLOTS; i++)
- xhci->devs[i] = NULL;
-
if (scratchpad_alloc(xhci, flags))
goto fail;
+
if (xhci_setup_port_arrays(xhci, flags))
goto fail;
- /* Enable USB 3.0 device notifications for function remote wake, which
- * is necessary for allowing USB 3.0 devices to do remote wakeup from
- * U3 (device suspend).
- */
- temp = readl(&xhci->op_regs->dev_notification);
- temp &= ~DEV_NOTE_MASK;
- temp |= DEV_NOTE_FWAKE;
- writel(temp, &xhci->op_regs->dev_notification);
-
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Finished %s", __func__);
return 0;
fail:
diff --git a/drivers/usb/host/xhci-mtk-sch.c b/drivers/usb/host/xhci-mtk-sch.c
index 27eb384a3963..583623cd4d8f 100644
--- a/drivers/usb/host/xhci-mtk-sch.c
+++ b/drivers/usb/host/xhci-mtk-sch.c
@@ -179,8 +179,7 @@ static struct mu3h_sch_tt *find_tt(struct usb_device *udev)
if (utt->multi) {
tt_index = utt->hcpriv;
if (!tt_index) { /* Create the index array */
- tt_index = kcalloc(utt->hub->maxchild,
- sizeof(*tt_index), GFP_KERNEL);
+ tt_index = kzalloc_objs(*tt_index, utt->hub->maxchild);
if (!tt_index)
return ERR_PTR(-ENOMEM);
utt->hcpriv = tt_index;
@@ -193,7 +192,7 @@ static struct mu3h_sch_tt *find_tt(struct usb_device *udev)
tt = *ptt;
if (!tt) { /* Create the mu3h_sch_tt */
- tt = kzalloc(sizeof(*tt), GFP_KERNEL);
+ tt = kzalloc_obj(*tt);
if (!tt) {
if (allocated_index) {
utt->hcpriv = NULL;
@@ -264,7 +263,7 @@ create_sch_ep(struct xhci_hcd_mtk *mtk, struct usb_device *udev,
else
len = 1;
- sch_ep = kzalloc(struct_size(sch_ep, bw_budget_table, len), GFP_KERNEL);
+ sch_ep = kzalloc_flex(*sch_ep, bw_budget_table, len);
if (!sch_ep)
return ERR_PTR(-ENOMEM);
@@ -891,7 +890,7 @@ int xhci_mtk_sch_init(struct xhci_hcd_mtk *mtk)
/* ss IN and OUT are separated */
num_usb_bus = xhci->usb3_rhub.num_ports * 2 + xhci->usb2_rhub.num_ports;
- sch_array = kcalloc(num_usb_bus, sizeof(*sch_array), GFP_KERNEL);
+ sch_array = kzalloc_objs(*sch_array, num_usb_bus);
if (sch_array == NULL)
return -ENOMEM;
diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c
index 904831344440..06043c7c3100 100644
--- a/drivers/usb/host/xhci-mtk.c
+++ b/drivers/usb/host/xhci-mtk.c
@@ -670,7 +670,6 @@ static int xhci_mtk_probe(struct platform_device *pdev)
}
device_enable_async_suspend(dev);
- pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
pm_runtime_forbid(dev);
@@ -746,10 +745,10 @@ static int __maybe_unused xhci_mtk_suspend(struct device *dev)
xhci_dbg(xhci, "%s: stop port polling\n", __func__);
clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
- del_timer_sync(&hcd->rh_timer);
+ timer_delete_sync(&hcd->rh_timer);
if (shared_hcd) {
clear_bit(HCD_FLAG_POLL_RH, &shared_hcd->flags);
- del_timer_sync(&shared_hcd->rh_timer);
+ timer_delete_sync(&shared_hcd->rh_timer);
}
ret = xhci_mtk_host_disable(mtk);
diff --git a/drivers/usb/host/xhci-mtk.h b/drivers/usb/host/xhci-mtk.h
index f5e2bd66bb1b..2274f5995171 100644
--- a/drivers/usb/host/xhci-mtk.h
+++ b/drivers/usb/host/xhci-mtk.h
@@ -21,7 +21,7 @@
/* support at most 64 ep, use 32 size hash table */
#define SCH_EP_HASH_BITS 5
-/**
+/*
* To simplify scheduler algorithm, set a upper limit for ESIT,
* if a synchromous ep's ESIT is larger than @XHCI_MTK_MAX_ESIT,
* round down to the limit value, that means allocating more
@@ -34,6 +34,7 @@
#define XHCI_MTK_FRAMES_CNT (XHCI_MTK_MAX_ESIT / UFRAMES_PER_FRAME)
/**
+ * struct mu3h_sch_tt - TT scheduling data
* @fs_bus_bw_out: save bandwidth used by FS/LS OUT eps in each uframes
* @fs_bus_bw_in: save bandwidth used by FS/LS IN eps in each uframes
* @ls_bus_bw: save bandwidth used by LS eps in each uframes
@@ -51,7 +52,7 @@ struct mu3h_sch_tt {
};
/**
- * struct mu3h_sch_bw_info: schedule information for bandwidth domain
+ * struct mu3h_sch_bw_info - schedule information for bandwidth domain
*
* @bus_bw: array to keep track of bandwidth already used at each uframes
*
@@ -63,7 +64,7 @@ struct mu3h_sch_bw_info {
};
/**
- * struct mu3h_sch_ep_info: schedule information for endpoint
+ * struct mu3h_sch_ep_info - schedule information for endpoint
*
* @esit: unit is 125us, equal to 2 << Interval field in ep-context
* @num_esit: number of @esit in a period
@@ -77,6 +78,7 @@ struct mu3h_sch_bw_info {
* @ep_type: endpoint type
* @maxpkt: max packet size of endpoint
* @ep: address of usb_host_endpoint struct
+ * @speed: usb device speed
* @allocated: the bandwidth is aready allocated from bus_bw
* @offset: which uframe of the interval that transfer should be
* scheduled first time within the interval
@@ -125,7 +127,7 @@ struct mu3h_sch_ep_info {
#define MU3C_U2_PORT_MAX 5
/**
- * struct mu3c_ippc_regs: MTK ssusb ip port control registers
+ * struct mu3c_ippc_regs - MTK ssusb ip port control registers
* @ip_pw_ctr0~3: ip power and clock control registers
* @ip_pw_sts1~2: ip power and clock status registers
* @ip_xhci_cap: ip xHCI capability register
diff --git a/drivers/usb/host/xhci-mvebu.c b/drivers/usb/host/xhci-mvebu.c
index 87f1597a0e5a..f91c5004fade 100644
--- a/drivers/usb/host/xhci-mvebu.c
+++ b/drivers/usb/host/xhci-mvebu.c
@@ -30,7 +30,7 @@ static void xhci_mvebu_mbus_config(void __iomem *base,
writel(0, base + USB3_WIN_BASE(win));
}
- /* Program each DRAM CS in a seperate window */
+ /* Program each DRAM CS in a separate window */
for (win = 0; win < dram->num_cs; win++) {
const struct mbus_dram_window *cs = &dram->cs[win];
@@ -73,13 +73,3 @@ int xhci_mvebu_mbus_init_quirk(struct usb_hcd *hcd)
return 0;
}
-
-int xhci_mvebu_a3700_init_quirk(struct usb_hcd *hcd)
-{
- struct xhci_hcd *xhci = hcd_to_xhci(hcd);
-
- /* Without reset on resume, the HC won't work at all */
- xhci->quirks |= XHCI_RESET_ON_RESUME;
-
- return 0;
-}
diff --git a/drivers/usb/host/xhci-mvebu.h b/drivers/usb/host/xhci-mvebu.h
index 3be021793cc8..9d26e22c4842 100644
--- a/drivers/usb/host/xhci-mvebu.h
+++ b/drivers/usb/host/xhci-mvebu.h
@@ -12,16 +12,10 @@ struct usb_hcd;
#if IS_ENABLED(CONFIG_USB_XHCI_MVEBU)
int xhci_mvebu_mbus_init_quirk(struct usb_hcd *hcd);
-int xhci_mvebu_a3700_init_quirk(struct usb_hcd *hcd);
#else
static inline int xhci_mvebu_mbus_init_quirk(struct usb_hcd *hcd)
{
return 0;
}
-
-static inline int xhci_mvebu_a3700_init_quirk(struct usb_hcd *hcd)
-{
- return 0;
-}
#endif
#endif /* __LINUX_XHCI_MVEBU_H */
diff --git a/drivers/usb/host/xhci-pci-renesas.c b/drivers/usb/host/xhci-pci-renesas.c
index 620f8f0febb8..86df80399c9f 100644
--- a/drivers/usb/host/xhci-pci-renesas.c
+++ b/drivers/usb/host/xhci-pci-renesas.c
@@ -47,8 +47,9 @@
#define RENESAS_ROM_ERASE_MAGIC 0x5A65726F
#define RENESAS_ROM_WRITE_MAGIC 0x53524F4D
-#define RENESAS_RETRY 10000
-#define RENESAS_DELAY 10
+#define RENESAS_RETRY 50000 /* 50000 * RENESAS_DELAY ~= 500ms */
+#define RENESAS_CHIP_ERASE_RETRY 500000 /* 500000 * RENESAS_DELAY ~= 5s */
+#define RENESAS_DELAY 10
#define RENESAS_FW_NAME "renesas_usb_fw.mem"
@@ -407,7 +408,7 @@ static void renesas_rom_erase(struct pci_dev *pdev)
/* sleep a bit while ROM is erased */
msleep(20);
- for (i = 0; i < RENESAS_RETRY; i++) {
+ for (i = 0; i < RENESAS_CHIP_ERASE_RETRY; i++) {
retval = pci_read_config_byte(pdev, RENESAS_ROM_STATUS,
&status);
status &= RENESAS_ROM_STATUS_ERASE;
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index 54460d11f7ee..585b2f3117b0 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -71,12 +71,22 @@
#define PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_XHCI 0x15ec
#define PCI_DEVICE_ID_INTEL_TITAN_RIDGE_DD_XHCI 0x15f0
+#define PCI_DEVICE_ID_AMD_ARIEL_TYPEC_XHCI 0x13ed
+#define PCI_DEVICE_ID_AMD_ARIEL_TYPEA_XHCI 0x13ee
+#define PCI_DEVICE_ID_AMD_STARSHIP_XHCI 0x148c
+#define PCI_DEVICE_ID_AMD_FIREFLIGHT_15D4_XHCI 0x15d4
+#define PCI_DEVICE_ID_AMD_FIREFLIGHT_15D5_XHCI 0x15d5
+#define PCI_DEVICE_ID_AMD_RAVEN_15E0_XHCI 0x15e0
+#define PCI_DEVICE_ID_AMD_RAVEN_15E1_XHCI 0x15e1
+#define PCI_DEVICE_ID_AMD_RAVEN2_XHCI 0x15e5
#define PCI_DEVICE_ID_AMD_RENOIR_XHCI 0x1639
#define PCI_DEVICE_ID_AMD_PROMONTORYA_4 0x43b9
#define PCI_DEVICE_ID_AMD_PROMONTORYA_3 0x43ba
#define PCI_DEVICE_ID_AMD_PROMONTORYA_2 0x43bb
#define PCI_DEVICE_ID_AMD_PROMONTORYA_1 0x43bc
+#define PCI_DEVICE_ID_ATI_NAVI10_7316_XHCI 0x7316
+
#define PCI_DEVICE_ID_ASMEDIA_1042_XHCI 0x1042
#define PCI_DEVICE_ID_ASMEDIA_1042A_XHCI 0x1142
#define PCI_DEVICE_ID_ASMEDIA_1142_XHCI 0x1242
@@ -280,6 +290,21 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
if (pdev->vendor == PCI_VENDOR_ID_NEC)
xhci->quirks |= XHCI_NEC_HOST;
+ if (pdev->vendor == PCI_VENDOR_ID_AMD &&
+ (pdev->device == PCI_DEVICE_ID_AMD_ARIEL_TYPEC_XHCI ||
+ pdev->device == PCI_DEVICE_ID_AMD_ARIEL_TYPEA_XHCI ||
+ pdev->device == PCI_DEVICE_ID_AMD_STARSHIP_XHCI ||
+ pdev->device == PCI_DEVICE_ID_AMD_FIREFLIGHT_15D4_XHCI ||
+ pdev->device == PCI_DEVICE_ID_AMD_FIREFLIGHT_15D5_XHCI ||
+ pdev->device == PCI_DEVICE_ID_AMD_RAVEN_15E0_XHCI ||
+ pdev->device == PCI_DEVICE_ID_AMD_RAVEN_15E1_XHCI ||
+ pdev->device == PCI_DEVICE_ID_AMD_RAVEN2_XHCI))
+ xhci->quirks |= XHCI_LIMIT_ENDPOINT_INTERVAL_9;
+
+ if (pdev->vendor == PCI_VENDOR_ID_ATI &&
+ pdev->device == PCI_DEVICE_ID_ATI_NAVI10_7316_XHCI)
+ xhci->quirks |= XHCI_LIMIT_ENDPOINT_INTERVAL_9;
+
if (pdev->vendor == PCI_VENDOR_ID_AMD && xhci->hci_version == 0x96)
xhci->quirks |= XHCI_AMD_0x96_HOST;
@@ -557,6 +582,8 @@ static int xhci_pci_setup(struct usb_hcd *hcd)
if (!usb_hcd_is_primary_hcd(hcd))
return 0;
+ xhci->allow_single_roothub = 1;
+
if (xhci->quirks & XHCI_PME_STUCK_QUIRK)
xhci_pme_acpi_rtd3_enable(pdev);
@@ -585,7 +612,7 @@ int xhci_pci_common_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
int retval;
struct xhci_hcd *xhci;
- struct usb_hcd *hcd;
+ struct usb_hcd *hcd, *usb3_hcd;
struct reset_control *reset;
reset = devm_reset_control_get_optional_exclusive(&dev->dev, NULL);
@@ -611,26 +638,31 @@ int xhci_pci_common_probe(struct pci_dev *dev, const struct pci_device_id *id)
hcd = dev_get_drvdata(&dev->dev);
xhci = hcd_to_xhci(hcd);
xhci->reset = reset;
- xhci->shared_hcd = usb_create_shared_hcd(&xhci_pci_hc_driver, &dev->dev,
- pci_name(dev), hcd);
- if (!xhci->shared_hcd) {
- retval = -ENOMEM;
- goto dealloc_usb2_hcd;
- }
- retval = xhci_ext_cap_init(xhci);
- if (retval)
- goto put_usb3_hcd;
+ if (!xhci_has_one_roothub(xhci)) {
+ xhci->shared_hcd = usb_create_shared_hcd(&xhci_pci_hc_driver, &dev->dev,
+ pci_name(dev), hcd);
+ if (!xhci->shared_hcd) {
+ retval = -ENOMEM;
+ goto dealloc_usb2_hcd;
+ }
- retval = usb_add_hcd(xhci->shared_hcd, dev->irq,
- IRQF_SHARED);
- if (retval)
- goto put_usb3_hcd;
- /* Roothub already marked as USB 3.0 speed */
+ retval = xhci_ext_cap_init(xhci);
+ if (retval)
+ goto put_usb3_hcd;
+
+ retval = usb_add_hcd(xhci->shared_hcd, dev->irq, IRQF_SHARED);
+ if (retval)
+ goto put_usb3_hcd;
+ } else {
+ retval = xhci_ext_cap_init(xhci);
+ if (retval)
+ goto dealloc_usb2_hcd;
+ }
- if (!(xhci->quirks & XHCI_BROKEN_STREAMS) &&
- HCC_MAX_PSA(xhci->hcc_params) >= 4)
- xhci->shared_hcd->can_do_streams = 1;
+ usb3_hcd = xhci_get_usb3_hcd(xhci);
+ if (usb3_hcd && !(xhci->quirks & XHCI_BROKEN_STREAMS) && HCC_MAX_PSA(xhci->hcc_params) >= 4)
+ usb3_hcd->can_do_streams = 1;
/* USB-2 and USB-3 roothubs initialized, allow runtime pm suspend */
pm_runtime_put_noidle(&dev->dev);
@@ -807,8 +839,10 @@ static int xhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup)
static int xhci_pci_resume(struct usb_hcd *hcd, pm_message_t msg)
{
- struct xhci_hcd *xhci = hcd_to_xhci(hcd);
- struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
+ bool power_lost = msg.event == PM_EVENT_RESTORE;
+ bool is_auto_resume = msg.event == PM_EVENT_AUTO_RESUME;
reset_control_reset(xhci->reset);
@@ -839,7 +873,7 @@ static int xhci_pci_resume(struct usb_hcd *hcd, pm_message_t msg)
if (xhci->quirks & XHCI_PME_STUCK_QUIRK)
xhci_pme_quirk(hcd);
- return xhci_resume(xhci, msg);
+ return xhci_resume(xhci, power_lost, is_auto_resume);
}
static int xhci_pci_poweroff_late(struct usb_hcd *hcd, bool do_wakeup)
@@ -862,9 +896,9 @@ static int xhci_pci_poweroff_late(struct usb_hcd *hcd, bool do_wakeup)
if (!(xhci->quirks & XHCI_RESET_TO_DEFAULT))
return 0;
- for (i = 0; i < HCS_MAX_PORTS(xhci->hcs_params1); i++) {
+ for (i = 0; i < xhci->max_ports; i++) {
port = &xhci->hw_ports[i];
- portsc = readl(port->addr);
+ portsc = xhci_portsc_readl(port);
if ((portsc & PORT_PLS_MASK) != XDEV_U3)
continue;
@@ -885,7 +919,7 @@ static int xhci_pci_poweroff_late(struct usb_hcd *hcd, bool do_wakeup)
xhci_dbg(xhci, "port %d-%d in U3 without wakeup, disable it\n",
port->rhub->hcd->self.busnum, port->hcd_portnum + 1);
portsc = xhci_port_state_to_neutral(portsc);
- writel(portsc | PORT_PE, port->addr);
+ xhci_portsc_writel(port, portsc | PORT_PE);
}
return 0;
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index d85ffa9ffaa7..074d9c731639 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -20,6 +20,7 @@
#include <linux/acpi.h>
#include <linux/usb/of.h>
#include <linux/reset.h>
+#include <linux/usb/xhci-sideband.h>
#include "xhci.h"
#include "xhci-plat.h"
@@ -74,6 +75,16 @@ static int xhci_priv_resume_quirk(struct usb_hcd *hcd)
return priv->resume_quirk(hcd);
}
+static int xhci_priv_post_resume_quirk(struct usb_hcd *hcd)
+{
+ struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd);
+
+ if (!priv->post_resume_quirk)
+ return 0;
+
+ return priv->post_resume_quirk(hcd);
+}
+
static void xhci_plat_quirks(struct device *dev, struct xhci_hcd *xhci)
{
struct xhci_plat_priv *priv = xhci_to_priv(xhci);
@@ -106,7 +117,7 @@ static const struct xhci_plat_priv xhci_plat_marvell_armada = {
};
static const struct xhci_plat_priv xhci_plat_marvell_armada3700 = {
- .init_quirk = xhci_mvebu_a3700_init_quirk,
+ .quirks = XHCI_RESET_ON_RESUME,
};
static const struct xhci_plat_priv xhci_plat_brcm = {
@@ -152,7 +163,7 @@ int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, const s
int ret;
int irq;
struct xhci_plat_priv *priv = NULL;
- bool of_match;
+ const struct of_device_id *of_match;
if (usb_disabled())
return -ENODEV;
@@ -171,6 +182,7 @@ int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, const s
return ret;
pm_runtime_set_active(&pdev->dev);
+ pm_runtime_use_autosuspend(&pdev->dev);
pm_runtime_enable(&pdev->dev);
pm_runtime_get_noresume(&pdev->dev);
@@ -267,6 +279,8 @@ int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, const s
device_property_read_u32(tmpdev, "imod-interval-ns",
&xhci->imod_interval);
+ device_property_read_u16(tmpdev, "num-hc-interrupters",
+ &xhci->max_interrupters);
}
/*
@@ -326,7 +340,8 @@ int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, const s
}
usb3_hcd = xhci_get_usb3_hcd(xhci);
- if (usb3_hcd && HCC_MAX_PSA(xhci->hcc_params) >= 4)
+ if (usb3_hcd && HCC_MAX_PSA(xhci->hcc_params) >= 4 &&
+ !(xhci->quirks & XHCI_BROKEN_STREAMS))
usb3_hcd->can_do_streams = 1;
if (xhci->shared_hcd) {
@@ -451,7 +466,7 @@ void xhci_plat_remove(struct platform_device *dev)
}
EXPORT_SYMBOL_GPL(xhci_plat_remove);
-static int xhci_plat_suspend(struct device *dev)
+static int xhci_plat_suspend_common(struct device *dev)
{
struct usb_hcd *hcd = dev_get_drvdata(dev);
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
@@ -479,9 +494,29 @@ static int xhci_plat_suspend(struct device *dev)
return 0;
}
-static int xhci_plat_resume_common(struct device *dev, struct pm_message pmsg)
+static int xhci_plat_suspend(struct device *dev)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd);
+
+ if (xhci_sideband_check(hcd)) {
+ priv->sideband_at_suspend = 1;
+ dev_dbg(dev, "sideband instance active, skip suspend.\n");
+ return 0;
+ }
+
+ return xhci_plat_suspend_common(dev);
+}
+
+static int xhci_plat_freeze(struct device *dev)
+{
+ return xhci_plat_suspend_common(dev);
+}
+
+static int xhci_plat_resume_common(struct device *dev, bool power_lost)
{
struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd);
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
int ret;
@@ -501,7 +536,11 @@ static int xhci_plat_resume_common(struct device *dev, struct pm_message pmsg)
if (ret)
goto disable_clks;
- ret = xhci_resume(xhci, pmsg);
+ ret = xhci_resume(xhci, power_lost || priv->power_lost, false);
+ if (ret)
+ goto disable_clks;
+
+ ret = xhci_priv_post_resume_quirk(hcd);
if (ret)
goto disable_clks;
@@ -522,12 +561,26 @@ disable_clks:
static int xhci_plat_resume(struct device *dev)
{
- return xhci_plat_resume_common(dev, PMSG_RESUME);
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd);
+
+ if (priv->sideband_at_suspend) {
+ priv->sideband_at_suspend = 0;
+ dev_dbg(dev, "sideband instance active, skip resume.\n");
+ return 0;
+ }
+
+ return xhci_plat_resume_common(dev, false);
+}
+
+static int xhci_plat_thaw(struct device *dev)
+{
+ return xhci_plat_resume_common(dev, false);
}
static int xhci_plat_restore(struct device *dev)
{
- return xhci_plat_resume_common(dev, PMSG_RESTORE);
+ return xhci_plat_resume_common(dev, true);
}
static int __maybe_unused xhci_plat_runtime_suspend(struct device *dev)
@@ -548,15 +601,15 @@ static int __maybe_unused xhci_plat_runtime_resume(struct device *dev)
struct usb_hcd *hcd = dev_get_drvdata(dev);
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
- return xhci_resume(xhci, PMSG_AUTO_RESUME);
+ return xhci_resume(xhci, false, true);
}
const struct dev_pm_ops xhci_plat_pm_ops = {
.suspend = pm_sleep_ptr(xhci_plat_suspend),
.resume = pm_sleep_ptr(xhci_plat_resume),
- .freeze = pm_sleep_ptr(xhci_plat_suspend),
- .thaw = pm_sleep_ptr(xhci_plat_resume),
- .poweroff = pm_sleep_ptr(xhci_plat_suspend),
+ .freeze = pm_sleep_ptr(xhci_plat_freeze),
+ .thaw = pm_sleep_ptr(xhci_plat_thaw),
+ .poweroff = pm_sleep_ptr(xhci_plat_freeze),
.restore = pm_sleep_ptr(xhci_plat_restore),
SET_RUNTIME_PM_OPS(xhci_plat_runtime_suspend,
diff --git a/drivers/usb/host/xhci-plat.h b/drivers/usb/host/xhci-plat.h
index 6475130eac4b..00751d851831 100644
--- a/drivers/usb/host/xhci-plat.h
+++ b/drivers/usb/host/xhci-plat.h
@@ -15,10 +15,13 @@ struct usb_hcd;
struct xhci_plat_priv {
const char *firmware_name;
unsigned long long quirks;
+ bool power_lost;
+ unsigned sideband_at_suspend:1;
void (*plat_start)(struct usb_hcd *);
int (*init_quirk)(struct usb_hcd *);
int (*suspend_quirk)(struct usb_hcd *);
int (*resume_quirk)(struct usb_hcd *);
+ int (*post_resume_quirk)(struct usb_hcd *);
};
#define hcd_to_xhci_priv(h) ((struct xhci_plat_priv *)hcd_to_xhci(h)->priv)
diff --git a/drivers/usb/host/xhci-port.h b/drivers/usb/host/xhci-port.h
index f19efb966d18..889b5fb0fcd8 100644
--- a/drivers/usb/host/xhci-port.h
+++ b/drivers/usb/host/xhci-port.h
@@ -144,9 +144,14 @@
#define PORT_TEST_MODE_SHIFT 28
/* USB3 Protocol PORTLI Port Link Information */
+#define PORT_LEC(p) ((p) & 0xffff)
#define PORT_RX_LANES(p) (((p) >> 16) & 0xf)
#define PORT_TX_LANES(p) (((p) >> 20) & 0xf)
+/* eUSB2v2 protocol PORTLI Port Link information, RsvdP for normal USB2 */
+#define PORTLI_RDR(p) ((p) & 0xf)
+#define PORTLI_TDR(p) (((p) >> 4) & 0xf)
+
/* USB2 Protocol PORTHLPMC */
#define PORT_HIRDM(p)((p) & 3)
#define PORT_L1_TIMEOUT(p)(((p) & 0xff) << 2)
diff --git a/drivers/usb/host/xhci-rcar-regs.h b/drivers/usb/host/xhci-rcar-regs.h
new file mode 100644
index 000000000000..5ecbda858be0
--- /dev/null
+++ b/drivers/usb/host/xhci-rcar-regs.h
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __XHCI_RCAR_H
+#define __XHCI_RCAR_H
+
+/*** Register Offset ***/
+#define RCAR_USB3_AXH_STA 0x104 /* AXI Host Control Status */
+#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 ***/
+/* AXI Host Control Status */
+#define RCAR_USB3_AXH_STA_B3_PLL_ACTIVE 0x00010000
+#define RCAR_USB3_AXH_STA_B2_PLL_ACTIVE 0x00000001
+#define RCAR_USB3_AXH_STA_PLL_ACTIVE_MASK (RCAR_USB3_AXH_STA_B3_PLL_ACTIVE | \
+ RCAR_USB3_AXH_STA_B2_PLL_ACTIVE)
+
+/* 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)
+
+#endif /* __XHCI_RCAR_H */
diff --git a/drivers/usb/host/xhci-rcar.c b/drivers/usb/host/xhci-rcar.c
index 1cc082a3b793..8a993ee21c87 100644
--- a/drivers/usb/host/xhci-rcar.c
+++ b/drivers/usb/host/xhci-rcar.c
@@ -11,9 +11,12 @@
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/usb/phy.h>
+#include <linux/reset.h>
#include "xhci.h"
#include "xhci-plat.h"
+#include "xhci-rcar-regs.h"
+#include "xhci-rzg3e-regs.h"
#include "xhci-rzv2m.h"
#define XHCI_RCAR_FIRMWARE_NAME_V1 "r8a779x_usb3_v1.dlmem"
@@ -29,50 +32,6 @@
MODULE_FIRMWARE(XHCI_RCAR_FIRMWARE_NAME_V1);
MODULE_FIRMWARE(XHCI_RCAR_FIRMWARE_NAME_V3);
-/*** Register Offset ***/
-#define RCAR_USB3_AXH_STA 0x104 /* AXI Host Control Status */
-#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 ***/
-/* AXI Host Control Status */
-#define RCAR_USB3_AXH_STA_B3_PLL_ACTIVE 0x00010000
-#define RCAR_USB3_AXH_STA_B2_PLL_ACTIVE 0x00000001
-#define RCAR_USB3_AXH_STA_PLL_ACTIVE_MASK (RCAR_USB3_AXH_STA_B3_PLL_ACTIVE | \
- RCAR_USB3_AXH_STA_B2_PLL_ACTIVE)
-
-/* 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)
-
static void xhci_rcar_start_gen2(struct usb_hcd *hcd)
{
/* LCLK Select */
@@ -110,6 +69,48 @@ static void xhci_rcar_start(struct usb_hcd *hcd)
}
}
+static void xhci_rzg3e_start(struct usb_hcd *hcd)
+{
+ u32 int_en;
+
+ if (hcd->regs) {
+ /* Update the controller initial setting */
+ writel(0x03130200, hcd->regs + RZG3E_USB3_HOST_U3P0PIPESC(0));
+ writel(0x00160200, hcd->regs + RZG3E_USB3_HOST_U3P0PIPESC(1));
+ writel(0x03150000, hcd->regs + RZG3E_USB3_HOST_U3P0PIPESC(2));
+ writel(0x03130200, hcd->regs + RZG3E_USB3_HOST_U3P0PIPESC(3));
+ writel(0x00180000, hcd->regs + RZG3E_USB3_HOST_U3P0PIPESC(4));
+
+ /* Interrupt Enable */
+ int_en = readl(hcd->regs + RZG3E_USB3_HOST_INTEN);
+ int_en |= RZG3E_USB3_HOST_INTEN_ENA;
+ writel(int_en, hcd->regs + RZG3E_USB3_HOST_INTEN);
+ }
+}
+
+static int xhci_rzg3e_resume(struct usb_hcd *hcd)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+
+ return reset_control_deassert(xhci->reset);
+}
+
+static int xhci_rzg3e_post_resume(struct usb_hcd *hcd)
+{
+ xhci_rzg3e_start(hcd);
+
+ return 0;
+}
+
+static int xhci_rzg3e_suspend(struct usb_hcd *hcd)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+
+ reset_control_assert(xhci->reset);
+
+ return 0;
+}
+
static int xhci_rcar_download_firmware(struct usb_hcd *hcd)
{
struct device *dev = hcd->self.controller;
@@ -233,6 +234,14 @@ static const struct xhci_plat_priv xhci_plat_renesas_rzv2m = {
.plat_start = xhci_rzv2m_start,
};
+static const struct xhci_plat_priv xhci_plat_renesas_rzg3e = {
+ .quirks = XHCI_NO_64BIT_SUPPORT | XHCI_RESET_ON_RESUME | XHCI_SUSPEND_RESUME_CLKS,
+ .plat_start = xhci_rzg3e_start,
+ .suspend_quirk = xhci_rzg3e_suspend,
+ .resume_quirk = xhci_rzg3e_resume,
+ .post_resume_quirk = xhci_rzg3e_post_resume,
+};
+
static const struct of_device_id usb_xhci_of_match[] = {
{
.compatible = "renesas,xhci-r8a7790",
@@ -250,6 +259,9 @@ static const struct of_device_id usb_xhci_of_match[] = {
.compatible = "renesas,xhci-r8a7796",
.data = &xhci_plat_renesas_rcar_gen3,
}, {
+ .compatible = "renesas,r9a09g047-xhci",
+ .data = &xhci_plat_renesas_rzg3e,
+ }, {
.compatible = "renesas,rcar-gen2-xhci",
.data = &xhci_plat_renesas_rcar_gen2,
}, {
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 965bffce301e..e47e644b296e 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -82,6 +82,23 @@ dma_addr_t xhci_trb_virt_to_dma(struct xhci_segment *seg,
return seg->dma + (segment_offset * sizeof(*trb));
}
+static union xhci_trb *xhci_dma_to_trb(struct xhci_segment *start_seg,
+ dma_addr_t dma,
+ struct xhci_segment **match_seg)
+{
+ struct xhci_segment *seg;
+
+ xhci_for_each_ring_seg(start_seg, seg) {
+ if (in_range(dma, seg->dma, TRB_SEGMENT_SIZE)) {
+ if (match_seg)
+ *match_seg = seg;
+ return &seg->trbs[(dma - seg->dma) / sizeof(union xhci_trb)];
+ }
+ }
+
+ return NULL;
+}
+
static bool trb_is_noop(union xhci_trb *trb)
{
return TRB_TYPE_NOOP_LE32(trb->generic.field[3]);
@@ -128,11 +145,11 @@ static void inc_td_cnt(struct urb *urb)
urb_priv->num_tds_done++;
}
-static void trb_to_noop(union xhci_trb *trb, u32 noop_type)
+static void trb_to_noop(union xhci_trb *trb, u32 noop_type, bool unchain_links)
{
if (trb_is_link(trb)) {
- /* unchain chained link TRBs */
- trb->link.control &= cpu_to_le32(~TRB_CHAIN);
+ if (unchain_links)
+ trb->link.control &= cpu_to_le32(~TRB_CHAIN);
} else {
trb->generic.field[0] = 0;
trb->generic.field[1] = 0;
@@ -143,6 +160,11 @@ static void trb_to_noop(union xhci_trb *trb, u32 noop_type)
}
}
+static unsigned int trb_to_pos(struct xhci_segment *seg, union xhci_trb *trb)
+{
+ return seg->num * TRBS_PER_SEGMENT + (trb - seg->trbs);
+}
+
/* Updates trb to point to the next TRB in the ring, and updates seg if the next
* TRB is in a new segment. This does not skip over link TRBs, and it does not
* effect the ring dequeue or enqueue pointers.
@@ -204,6 +226,50 @@ void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring)
}
/*
+ * If enqueue points at a link TRB, follow links until an ordinary TRB is reached.
+ * Toggle the cycle bit of passed link TRBs and optionally chain them.
+ */
+static void inc_enq_past_link(struct xhci_hcd *xhci, struct xhci_ring *ring, u32 chain)
+{
+ unsigned int link_trb_count = 0;
+
+ while (trb_is_link(ring->enqueue)) {
+
+ /*
+ * Section 6.4.4.1 of the 0.95 spec says link TRBs cannot have the chain bit
+ * set, but other sections talk about dealing with the chain bit set. This was
+ * fixed in the 0.96 specification errata, but we have to assume that all 0.95
+ * xHCI hardware can't handle the chain bit being cleared on a link TRB.
+ *
+ * On 0.95 and some 0.96 HCs the chain bit is set once at segment initalization
+ * and never changed here. On all others, modify it as requested by the caller.
+ */
+ if (!xhci_link_chain_quirk(xhci, ring->type)) {
+ ring->enqueue->link.control &= cpu_to_le32(~TRB_CHAIN);
+ ring->enqueue->link.control |= cpu_to_le32(chain);
+ }
+
+ /* Give this link TRB to the hardware */
+ wmb();
+ ring->enqueue->link.control ^= cpu_to_le32(TRB_CYCLE);
+
+ /* Toggle the cycle bit after the last ring segment. */
+ if (link_trb_toggles_cycle(ring->enqueue))
+ ring->cycle_state ^= 1;
+
+ ring->enq_seg = ring->enq_seg->next;
+ ring->enqueue = ring->enq_seg->trbs;
+
+ trace_xhci_inc_enq(ring);
+
+ if (link_trb_count++ > ring->num_segs) {
+ xhci_warn(xhci, "Link TRB loop at enqueue\n");
+ break;
+ }
+ }
+}
+
+/*
* See Cycle bit rules. SW is the consumer for the event ring only.
*
* If we've just enqueued a TRB that is in the middle of a TD (meaning the
@@ -211,11 +277,6 @@ void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring)
* If we've enqueued the last TRB in a TD, make sure the following link TRBs
* have their chain bit cleared (so that each Link TRB is a separate TD).
*
- * Section 6.4.4.1 of the 0.95 spec says link TRBs cannot have the chain bit
- * set, but other sections talk about dealing with the chain bit set. This was
- * fixed in the 0.96 specification errata, but we have to assume that all 0.95
- * xHCI hardware can't handle the chain bit being cleared on a link TRB.
- *
* @more_trbs_coming: Will you enqueue more TRBs before calling
* prepare_transfer()?
*/
@@ -223,8 +284,6 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring,
bool more_trbs_coming)
{
u32 chain;
- union xhci_trb *next;
- unsigned int link_trb_count = 0;
chain = le32_to_cpu(ring->enqueue->generic.field[3]) & TRB_CHAIN;
@@ -233,48 +292,46 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring,
return;
}
- next = ++(ring->enqueue);
+ ring->enqueue++;
- /* Update the dequeue pointer further if that was a link TRB */
- while (trb_is_link(next)) {
+ /*
+ * If we are in the middle of a TD or the caller plans to enqueue more
+ * TDs as one transfer (eg. control), traverse any link TRBs right now.
+ * Otherwise, enqueue can stay on a link until the next prepare_ring().
+ * This avoids enqueue entering deq_seg and simplifies ring expansion.
+ */
+ if (trb_is_link(ring->enqueue) && (chain || more_trbs_coming))
+ inc_enq_past_link(xhci, ring, chain);
+}
- /*
- * If the caller doesn't plan on enqueueing more TDs before
- * ringing the doorbell, then we don't want to give the link TRB
- * to the hardware just yet. We'll give the link TRB back in
- * prepare_ring() just before we enqueue the TD at the top of
- * the ring.
- */
- if (!chain && !more_trbs_coming)
- break;
+static bool dma_in_range(dma_addr_t dma,
+ struct xhci_segment *start_seg, union xhci_trb *start_trb,
+ struct xhci_segment *end_seg, union xhci_trb *end_trb)
+{
+ unsigned int pos, start, end;
+ struct xhci_segment *pos_seg;
+ union xhci_trb *pos_trb = xhci_dma_to_trb(start_seg, dma, &pos_seg);
- /* If we're not dealing with 0.95 hardware or isoc rings on
- * AMD 0.96 host, carry over the chain bit of the previous TRB
- * (which may mean the chain bit is cleared).
- */
- if (!xhci_link_chain_quirk(xhci, ring->type)) {
- next->link.control &= cpu_to_le32(~TRB_CHAIN);
- next->link.control |= cpu_to_le32(chain);
- }
- /* Give this link TRB to the hardware */
- wmb();
- next->link.control ^= cpu_to_le32(TRB_CYCLE);
+ /* Is the trb dma address even part of the whole ring? */
+ if (!pos_trb)
+ return false;
- /* Toggle the cycle bit after the last ring segment. */
- if (link_trb_toggles_cycle(next))
- ring->cycle_state ^= 1;
+ pos = trb_to_pos(pos_seg, pos_trb);
+ start = trb_to_pos(start_seg, start_trb);
+ end = trb_to_pos(end_seg, end_trb);
- ring->enq_seg = ring->enq_seg->next;
- ring->enqueue = ring->enq_seg->trbs;
- next = ring->enqueue;
+ /* end position is smaller than start, search range wraps around */
+ if (end < start)
+ return !(pos > end && pos < start);
- trace_xhci_inc_enq(ring);
+ return (pos >= start && pos <= end);
+}
- if (link_trb_count++ > ring->num_segs) {
- xhci_warn(xhci, "%s: Ring link TRB loop\n", __func__);
- break;
- }
- }
+/* If the suspect DMA address is a TRB in this TD, this function returns true */
+static bool trb_in_td(struct xhci_td *td, dma_addr_t suspect_dma)
+{
+ return dma_in_range(suspect_dma, td->start_seg, td->start_trb,
+ td->end_seg, td->end_trb);
}
/*
@@ -378,7 +435,7 @@ void xhci_ring_cmd_db(struct xhci_hcd *xhci)
static bool xhci_mod_cmd_timer(struct xhci_hcd *xhci)
{
- return mod_delayed_work(system_wq, &xhci->cmd_timer,
+ return mod_delayed_work(system_percpu_wq, &xhci->cmd_timer,
msecs_to_jiffies(xhci->current_cmd->timeout_ms));
}
@@ -409,7 +466,7 @@ static void xhci_handle_stopped_cmd_ring(struct xhci_hcd *xhci,
xhci_dbg(xhci, "Turn aborted command %p to no-op\n",
i_cmd->command_trb);
- trb_to_noop(i_cmd->command_trb, TRB_CMD_NOOP);
+ trb_to_noop(i_cmd->command_trb, TRB_CMD_NOOP, false);
/*
* caller waiting for completion is called when command
@@ -462,9 +519,8 @@ static int xhci_abort_cmd_ring(struct xhci_hcd *xhci, unsigned long flags)
* In the future we should distinguish between -ENODEV and -ETIMEDOUT
* and try to recover a -ETIMEDOUT with a host controller reset.
*/
- ret = xhci_handshake_check_state(xhci, &xhci->op_regs->cmd_ring,
- CMD_RING_RUNNING, 0, 5 * 1000 * 1000,
- XHCI_STATE_REMOVING);
+ ret = xhci_handshake(&xhci->op_regs->cmd_ring,
+ CMD_RING_RUNNING, 0, 5 * 1000 * 1000);
if (ret < 0) {
xhci_err(xhci, "Abort failed to stop command ring: %d\n", ret);
xhci_halt(xhci);
@@ -643,7 +699,7 @@ static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci,
int new_cycle;
dma_addr_t addr;
u64 hw_dequeue;
- bool cycle_found = false;
+ bool hw_dequeue_found = false;
bool td_last_trb_found = false;
u32 trb_sct = 0;
int ret;
@@ -656,28 +712,27 @@ static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci,
return -ENODEV;
}
- hw_dequeue = xhci_get_hw_deq(xhci, dev, ep_index, stream_id);
+ hw_dequeue = xhci_get_hw_deq(xhci, dev, ep_index, stream_id) & TR_DEQ_PTR_MASK;
new_seg = ep_ring->deq_seg;
new_deq = ep_ring->dequeue;
- new_cycle = hw_dequeue & 0x1;
+ new_cycle = le32_to_cpu(td->end_trb->generic.field[3]) & TRB_CYCLE;
/*
- * We want to find the pointer, segment and cycle state of the new trb
- * (the one after current TD's end_trb). We know the cycle state at
- * hw_dequeue, so walk the ring until both hw_dequeue and end_trb are
- * found.
+ * Walk the ring until both the next TRB and hw_dequeue are found (don't
+ * move hw_dequeue back if it went forward due to a HW bug). Cycle state
+ * is loaded from a known good TRB, track later toggles to maintain it.
*/
do {
- if (!cycle_found && xhci_trb_virt_to_dma(new_seg, new_deq)
- == (dma_addr_t)(hw_dequeue & ~0xf)) {
- cycle_found = true;
+ if (!hw_dequeue_found && xhci_trb_virt_to_dma(new_seg, new_deq)
+ == (dma_addr_t)hw_dequeue) {
+ hw_dequeue_found = true;
if (td_last_trb_found)
break;
}
if (new_deq == td->end_trb)
td_last_trb_found = true;
- if (cycle_found && trb_is_link(new_deq) &&
+ if (td_last_trb_found && trb_is_link(new_deq) &&
link_trb_toggles_cycle(new_deq))
new_cycle ^= 0x1;
@@ -689,7 +744,7 @@ static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci,
return -EINVAL;
}
- } while (!cycle_found || !td_last_trb_found);
+ } while (!hw_dequeue_found || !td_last_trb_found);
/* Don't update the ring cycle state for the producer (us). */
addr = xhci_trb_virt_to_dma(new_seg, new_deq);
@@ -700,7 +755,7 @@ static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci,
}
if ((ep->ep_state & SET_DEQ_PENDING)) {
- xhci_warn(xhci, "Set TR Deq already pending, don't submit for 0x%pad\n",
+ xhci_warn(xhci, "Set TR Deq already pending, don't submit for %pad\n",
&addr);
return -EBUSY;
}
@@ -708,7 +763,7 @@ static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci,
/* This function gets called from contexts where it cannot sleep */
cmd = xhci_alloc_command(xhci, false, GFP_ATOMIC);
if (!cmd) {
- xhci_warn(xhci, "Can't alloc Set TR Deq cmd 0x%pad\n", &addr);
+ xhci_warn(xhci, "Can't alloc Set TR Deq cmd %pad\n", &addr);
return -ENOMEM;
}
@@ -743,13 +798,18 @@ static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci,
* (The last TRB actually points to the ring enqueue pointer, which is not part
* of this TD.) This is used to remove partially enqueued isoc TDs from a ring.
*/
-static void td_to_noop(struct xhci_td *td, bool flip_cycle)
+static void td_to_noop(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
+ struct xhci_td *td, bool flip_cycle)
{
+ bool unchain_links;
struct xhci_segment *seg = td->start_seg;
union xhci_trb *trb = td->start_trb;
+ /* link TRBs should now be unchained, but some old HCs expect otherwise */
+ unchain_links = !xhci_link_chain_quirk(xhci, ep->ring ? ep->ring->type : TYPE_STREAM);
+
while (1) {
- trb_to_noop(trb, TRB_TR_NOOP);
+ trb_to_noop(trb, TRB_TR_NOOP, unchain_links);
/* flip cycle if asked to */
if (flip_cycle && trb != td->start_trb && trb != td->end_trb)
@@ -1012,9 +1072,9 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep)
*/
hw_deq = xhci_get_hw_deq(xhci, ep->vdev, ep->ep_index,
td->urb->stream_id);
- hw_deq &= ~0xf;
+ hw_deq &= TR_DEQ_PTR_MASK;
- if (td->cancel_status == TD_HALTED || trb_in_td(xhci, td, hw_deq, false)) {
+ if (td->cancel_status == TD_HALTED || trb_in_td(td, hw_deq)) {
switch (td->cancel_status) {
case TD_CLEARED: /* TD is already no-op */
case TD_CLEARING_CACHE: /* set TR deq command already queued */
@@ -1037,16 +1097,16 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep)
"Found multiple active URBs %p and %p in stream %u?\n",
td->urb, cached_td->urb,
td->urb->stream_id);
- td_to_noop(cached_td, false);
+ td_to_noop(xhci, ep, cached_td, false);
cached_td->cancel_status = TD_CLEARED;
}
- td_to_noop(td, false);
+ td_to_noop(xhci, ep, td, false);
td->cancel_status = TD_CLEARING_CACHE;
cached_td = td;
break;
}
} else {
- td_to_noop(td, false);
+ td_to_noop(xhci, ep, td, false);
td->cancel_status = TD_CLEARED;
}
}
@@ -1071,7 +1131,7 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep)
continue;
xhci_warn(xhci, "Failed to clear cancelled cached URB %p, mark clear anyway\n",
td->urb);
- td_to_noop(td, false);
+ td_to_noop(xhci, ep, td, false);
td->cancel_status = TD_CLEARED;
}
}
@@ -1102,9 +1162,9 @@ static struct xhci_td *find_halted_td(struct xhci_virt_ep *ep)
if (!list_empty(&ep->ring->td_list)) { /* Not streams compatible */
hw_deq = xhci_get_hw_deq(ep->xhci, ep->vdev, ep->ep_index, 0);
- hw_deq &= ~0xf;
+ hw_deq &= TR_DEQ_PTR_MASK;
td = list_first_entry(&ep->ring->td_list, struct xhci_td, td_list);
- if (trb_in_td(ep->xhci, td, hw_deq, false))
+ if (trb_in_td(td, hw_deq))
return td;
}
return NULL;
@@ -1164,7 +1224,14 @@ static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, int slot_id,
*/
switch (GET_EP_CTX_STATE(ep_ctx)) {
case EP_STATE_HALTED:
- xhci_dbg(xhci, "Stop ep completion raced with stall, reset ep\n");
+ xhci_dbg(xhci, "Stop ep completion raced with stall\n");
+ /*
+ * If the halt happened before Stop Endpoint failed, its transfer event
+ * should have already been handled and Reset Endpoint should be pending.
+ */
+ if (ep->ep_state & EP_HALTED)
+ goto reset_done;
+
if (ep->ep_state & EP_HAS_STREAMS) {
reset_type = EP_SOFT_RESET;
} else {
@@ -1175,8 +1242,11 @@ static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, int slot_id,
}
/* reset ep, reset handler cleans up cancelled tds */
err = xhci_handle_halted_endpoint(xhci, ep, td, reset_type);
+ xhci_dbg(xhci, "Stop ep completion resetting ep, status %d\n", err);
if (err)
break;
+reset_done:
+ /* Reset EP handler will clean up cancelled TDs */
ep->ep_state &= ~EP_STOP_CMD_PENDING;
return;
case EP_STATE_STOPPED:
@@ -1198,8 +1268,9 @@ static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, int slot_id,
* Stopped state, but it will soon change to Running.
*
* Assume this bug on unexpected Stop Endpoint failures.
- * Keep retrying until the EP starts and stops again, on
- * chips where this is known to help. Wait for 100ms.
+ * Keep retrying until the EP starts and stops again or
+ * up to a timeout (a defective HC may never start, or a
+ * driver bug may cause stopping an already stopped EP).
*/
if (time_is_before_jiffies(ep->stop_time + msecs_to_jiffies(100)))
break;
@@ -1309,18 +1380,21 @@ static void xhci_kill_endpoint_urbs(struct xhci_hcd *xhci,
*/
void xhci_hc_died(struct xhci_hcd *xhci)
{
+ bool notify;
int i, j;
if (xhci->xhc_state & XHCI_STATE_DYING)
return;
- xhci_err(xhci, "xHCI host controller not responding, assume dead\n");
+ notify = !(xhci->xhc_state & XHCI_STATE_REMOVING);
+ if (notify)
+ xhci_err(xhci, "xHCI host controller not responding, assume dead\n");
xhci->xhc_state |= XHCI_STATE_DYING;
xhci_cleanup_command_queue(xhci);
/* return any pending urbs, remove may be waiting for them */
- for (i = 0; i <= HCS_MAX_SLOTS(xhci->hcs_params1); i++) {
+ for (i = 0; i <= xhci->max_slots; i++) {
if (!xhci->devs[i])
continue;
for (j = 0; j < 31; j++)
@@ -1328,47 +1402,10 @@ void xhci_hc_died(struct xhci_hcd *xhci)
}
/* inform usb core hc died if PCI remove isn't already handling it */
- if (!(xhci->xhc_state & XHCI_STATE_REMOVING))
+ if (notify)
usb_hc_died(xhci_to_hcd(xhci));
}
-static void update_ring_for_set_deq_completion(struct xhci_hcd *xhci,
- struct xhci_virt_device *dev,
- struct xhci_ring *ep_ring,
- unsigned int ep_index)
-{
- union xhci_trb *dequeue_temp;
-
- dequeue_temp = ep_ring->dequeue;
-
- /* If we get two back-to-back stalls, and the first stalled transfer
- * ends just before a link TRB, the dequeue pointer will be left on
- * the link TRB by the code in the while loop. So we have to update
- * the dequeue pointer one segment further, or we'll jump off
- * the segment into la-la-land.
- */
- if (trb_is_link(ep_ring->dequeue)) {
- ep_ring->deq_seg = ep_ring->deq_seg->next;
- ep_ring->dequeue = ep_ring->deq_seg->trbs;
- }
-
- while (ep_ring->dequeue != dev->eps[ep_index].queued_deq_ptr) {
- /* We have more usable TRBs */
- ep_ring->dequeue++;
- if (trb_is_link(ep_ring->dequeue)) {
- if (ep_ring->dequeue ==
- dev->eps[ep_index].queued_deq_ptr)
- break;
- ep_ring->deq_seg = ep_ring->deq_seg->next;
- ep_ring->dequeue = ep_ring->deq_seg->trbs;
- }
- if (ep_ring->dequeue == dequeue_temp) {
- xhci_dbg(xhci, "Unable to find new dequeue pointer\n");
- break;
- }
- }
-}
-
/*
* When we get a completion for a Set Transfer Ring Dequeue Pointer command,
* we need to clear the set deq pending flag in the endpoint ring state, so that
@@ -1448,7 +1485,7 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id,
u64 deq;
/* 4.6.10 deq ptr is written to the stream ctx for streams */
if (ep->ep_state & EP_HAS_STREAMS) {
- deq = le64_to_cpu(stream_ctx->stream_ring) & SCTX_DEQ_MASK;
+ deq = le64_to_cpu(stream_ctx->stream_ring) & TR_DEQ_PTR_MASK;
/*
* Cadence xHCI controllers store some endpoint state
@@ -1464,7 +1501,7 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id,
stream_ctx->reserved[1] = 0;
}
} else {
- deq = le64_to_cpu(ep_ctx->deq) & ~EP_CTX_CYCLE_MASK;
+ deq = le64_to_cpu(ep_ctx->deq) & TR_DEQ_PTR_MASK;
}
xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
"Successful Set TR Deq Ptr cmd, deq = @%08llx", deq);
@@ -1473,8 +1510,8 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id,
/* Update the ring's dequeue segment and dequeue pointer
* to reflect the new position.
*/
- update_ring_for_set_deq_completion(xhci, ep->vdev,
- ep_ring, ep_index);
+ ep_ring->deq_seg = ep->queued_deq_seg;
+ ep_ring->dequeue = ep->queued_deq_ptr;
} else {
xhci_warn(xhci, "Mismatch between completed Set TR Deq Ptr command & xHCI internal state.\n");
xhci_warn(xhci, "ep deq seg = %p, deq ptr = %p\n",
@@ -1559,7 +1596,8 @@ static void xhci_handle_cmd_enable_slot(int slot_id, struct xhci_command *comman
command->slot_id = 0;
}
-static void xhci_handle_cmd_disable_slot(struct xhci_hcd *xhci, int slot_id)
+static void xhci_handle_cmd_disable_slot(struct xhci_hcd *xhci, int slot_id,
+ u32 cmd_comp_code)
{
struct xhci_virt_device *virt_dev;
struct xhci_slot_ctx *slot_ctx;
@@ -1574,6 +1612,10 @@ static void xhci_handle_cmd_disable_slot(struct xhci_hcd *xhci, int slot_id)
if (xhci->quirks & XHCI_EP_LIMIT_QUIRK)
/* Delete default control endpoint resources */
xhci_free_device_endpoint_resources(xhci, virt_dev, true);
+ if (cmd_comp_code == COMP_SUCCESS) {
+ xhci->dcbaa->dev_context_ptrs[slot_id] = 0;
+ xhci->devs[slot_id] = NULL;
+ }
}
static void xhci_handle_cmd_config_ep(struct xhci_hcd *xhci, int slot_id)
@@ -1823,7 +1865,7 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
xhci_handle_cmd_enable_slot(slot_id, cmd, cmd_comp_code);
break;
case TRB_DISABLE_SLOT:
- xhci_handle_cmd_disable_slot(xhci, slot_id);
+ xhci_handle_cmd_disable_slot(xhci, slot_id, cmd_comp_code);
break;
case TRB_CONFIG_EP:
if (!cmd->completion)
@@ -1867,6 +1909,8 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
case TRB_NEC_GET_FW:
xhci_handle_cmd_nec_get_fw(xhci, event);
break;
+ case TRB_GET_BW:
+ break;
default:
/* Skip over unknown commands on the event ring */
xhci_info(xhci, "INFO unknown command type %d\n", cmd_type);
@@ -1947,10 +1991,10 @@ static void xhci_cavium_reset_phy_quirk(struct xhci_hcd *xhci)
static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event)
{
+ struct xhci_virt_device *vdev = NULL;
struct usb_hcd *hcd;
u32 port_id;
u32 portsc, cmd_reg;
- int max_ports;
unsigned int hcd_portnum;
struct xhci_bus_state *bus_state;
bool bogus_port_status = false;
@@ -1962,9 +2006,8 @@ static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event)
"WARN: xHC returned failed port status event\n");
port_id = GET_PORT_ID(le32_to_cpu(event->generic.field[0]));
- max_ports = HCS_MAX_PORTS(xhci->hcs_params1);
- if ((port_id <= 0) || (port_id > max_ports)) {
+ if ((port_id <= 0) || (port_id > xhci->max_ports)) {
xhci_warn(xhci, "Port change event with invalid port ID %d\n",
port_id);
return;
@@ -1978,6 +2021,9 @@ static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event)
goto cleanup;
}
+ if (port->slot_id)
+ vdev = xhci->devs[port->slot_id];
+
/* We might get interrupts after shared_hcd is removed */
if (port->rhub == &xhci->usb3_rhub && xhci->shared_hcd == NULL) {
xhci_dbg(xhci, "ignore port event for removed USB3 hcd\n");
@@ -1988,7 +2034,7 @@ static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event)
hcd = port->rhub->hcd;
bus_state = &port->rhub->bus_state;
hcd_portnum = port->hcd_portnum;
- portsc = readl(port->addr);
+ portsc = xhci_portsc_readl(port);
xhci_dbg(xhci, "Port change event, %d-%d, id %d, portsc: 0x%x\n",
hcd->self.busnum, hcd_portnum + 1, port_id, portsc);
@@ -2000,10 +2046,11 @@ static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event)
usb_hcd_resume_root_hub(hcd);
}
- if (hcd->speed >= HCD_USB3 &&
- (portsc & PORT_PLS_MASK) == XDEV_INACTIVE) {
- if (port->slot_id && xhci->devs[port->slot_id])
- xhci->devs[port->slot_id]->flags |= VDEV_PORT_ERROR;
+ if (vdev && (portsc & PORT_PLS_MASK) == XDEV_INACTIVE) {
+ if (!(portsc & PORT_RESET))
+ vdev->flags |= VDEV_PORT_ERROR;
+ } else if (vdev && portsc & PORT_RC) {
+ vdev->flags &= ~VDEV_PORT_ERROR;
}
if ((portsc & PORT_PLC) && (portsc & PORT_PLS_MASK) == XDEV_RESUME) {
@@ -2061,7 +2108,7 @@ static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event)
* so the roothub behavior is consistent with external
* USB 3.0 hub behavior.
*/
- if (port->slot_id && xhci->devs[port->slot_id])
+ if (vdev)
xhci_ring_device(xhci, port->slot_id);
if (bus_state->port_remote_wakeup & (1 << hcd_portnum)) {
xhci_test_and_clear_bit(xhci, port, PORT_PLC);
@@ -2116,67 +2163,6 @@ cleanup:
spin_lock(&xhci->lock);
}
-/*
- * If the suspect DMA address is a TRB in this TD, this function returns that
- * TRB's segment. Otherwise it returns 0.
- */
-struct xhci_segment *trb_in_td(struct xhci_hcd *xhci, struct xhci_td *td, dma_addr_t suspect_dma,
- bool debug)
-{
- dma_addr_t start_dma;
- dma_addr_t end_seg_dma;
- dma_addr_t end_trb_dma;
- struct xhci_segment *cur_seg;
-
- start_dma = xhci_trb_virt_to_dma(td->start_seg, td->start_trb);
- cur_seg = td->start_seg;
-
- do {
- if (start_dma == 0)
- return NULL;
- /* We may get an event for a Link TRB in the middle of a TD */
- end_seg_dma = xhci_trb_virt_to_dma(cur_seg,
- &cur_seg->trbs[TRBS_PER_SEGMENT - 1]);
- /* If the end TRB isn't in this segment, this is set to 0 */
- end_trb_dma = xhci_trb_virt_to_dma(cur_seg, td->end_trb);
-
- if (debug)
- xhci_warn(xhci,
- "Looking for event-dma %016llx trb-start %016llx trb-end %016llx seg-start %016llx seg-end %016llx\n",
- (unsigned long long)suspect_dma,
- (unsigned long long)start_dma,
- (unsigned long long)end_trb_dma,
- (unsigned long long)cur_seg->dma,
- (unsigned long long)end_seg_dma);
-
- if (end_trb_dma > 0) {
- /* The end TRB is in this segment, so suspect should be here */
- if (start_dma <= end_trb_dma) {
- if (suspect_dma >= start_dma && suspect_dma <= end_trb_dma)
- return cur_seg;
- } else {
- /* Case for one segment with
- * a TD wrapped around to the top
- */
- if ((suspect_dma >= start_dma &&
- suspect_dma <= end_seg_dma) ||
- (suspect_dma >= cur_seg->dma &&
- suspect_dma <= end_trb_dma))
- return cur_seg;
- }
- return NULL;
- } else {
- /* Might still be somewhere in this segment */
- if (suspect_dma >= start_dma && suspect_dma <= end_seg_dma)
- return cur_seg;
- }
- cur_seg = cur_seg->next;
- start_dma = xhci_trb_virt_to_dma(cur_seg, &cur_seg->trbs[0]);
- } while (cur_seg != td->start_seg);
-
- return NULL;
-}
-
static void xhci_clear_hub_tt_buffer(struct xhci_hcd *xhci, struct xhci_td *td,
struct xhci_virt_ep *ep)
{
@@ -2202,24 +2188,31 @@ static void xhci_clear_hub_tt_buffer(struct xhci_hcd *xhci, struct xhci_td *td,
* External device side is also halted in functional stall cases. Class driver
* will clear the device halt with a CLEAR_FEATURE(ENDPOINT_HALT) request later.
*/
-static bool xhci_halted_host_endpoint(struct xhci_ep_ctx *ep_ctx, unsigned int comp_code)
+static bool xhci_halted_host_endpoint(struct xhci_hcd *xhci, struct xhci_ep_ctx *ep_ctx,
+ unsigned int comp_code)
{
- /* Stall halts both internal and device side endpoint */
- if (comp_code == COMP_STALL_ERROR)
- return true;
+ int ep_type = CTX_TO_EP_TYPE(le32_to_cpu(ep_ctx->ep_info2));
- /* TRB completion codes that may require internal halt cleanup */
- if (comp_code == COMP_USB_TRANSACTION_ERROR ||
- comp_code == COMP_BABBLE_DETECTED_ERROR ||
- comp_code == COMP_SPLIT_TRANSACTION_ERROR)
+ switch (comp_code) {
+ case COMP_STALL_ERROR:
+ /* on xHCI this always halts, including protocol stall */
+ return true;
+ case COMP_BABBLE_DETECTED_ERROR:
/*
* The 0.95 spec says a babbling control endpoint is not halted.
* The 0.96 spec says it is. Some HW claims to be 0.95
* compliant, but it halts the control endpoint anyway.
* Check endpoint context if endpoint is halted.
*/
- if (GET_EP_CTX_STATE(ep_ctx) == EP_STATE_HALTED)
- return true;
+ if (xhci->hci_version <= 0x95 && ep_type == CTRL_EP)
+ return GET_EP_CTX_STATE(ep_ctx) == EP_STATE_HALTED;
+
+ fallthrough;
+ case COMP_USB_TRANSACTION_ERROR:
+ case COMP_SPLIT_TRANSACTION_ERROR:
+ /* these errors halt all non-isochronous endpoints */
+ return ep_type != ISOC_IN_EP && ep_type != ISOC_OUT_EP;
+ }
return false;
}
@@ -2256,41 +2249,9 @@ static void finish_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
* the ring dequeue pointer or take this TD off any lists yet.
*/
return;
- case COMP_USB_TRANSACTION_ERROR:
- case COMP_BABBLE_DETECTED_ERROR:
- case COMP_SPLIT_TRANSACTION_ERROR:
- /*
- * If endpoint context state is not halted we might be
- * racing with a reset endpoint command issued by a unsuccessful
- * stop endpoint completion (context error). In that case the
- * td should be on the cancelled list, and EP_HALTED flag set.
- *
- * Or then it's not halted due to the 0.95 spec stating that a
- * babbling control endpoint should not halt. The 0.96 spec
- * again says it should. Some HW claims to be 0.95 compliant,
- * but it halts the control endpoint anyway.
- */
- if (GET_EP_CTX_STATE(ep_ctx) != EP_STATE_HALTED) {
- /*
- * If EP_HALTED is set and TD is on the cancelled list
- * the TD and dequeue pointer will be handled by reset
- * ep command completion
- */
- if ((ep->ep_state & EP_HALTED) &&
- !list_empty(&td->cancelled_td_list)) {
- xhci_dbg(xhci, "Already resolving halted ep for 0x%llx\n",
- (unsigned long long)xhci_trb_virt_to_dma(
- td->start_seg, td->start_trb));
- return;
- }
- /* endpoint not halted, don't reset it */
- break;
- }
- /* Almost same procedure as for STALL_ERROR below */
- xhci_clear_hub_tt_buffer(xhci, td, ep);
- xhci_handle_halted_endpoint(xhci, ep, td, EP_HARD_RESET);
- return;
- case COMP_STALL_ERROR:
+ }
+
+ if (xhci_halted_host_endpoint(xhci, ep_ctx, trb_comp_code)) {
/*
* xhci internal endpoint state will go to a "halt" state for
* any stall, including default control pipe protocol stall.
@@ -2301,14 +2262,12 @@ static void finish_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
* stall later. Hub TT buffer should only be cleared for FS/LS
* devices behind HS hubs for functional stalls.
*/
- if (ep->ep_index != 0)
+ if (!(ep->ep_index == 0 && trb_comp_code == COMP_STALL_ERROR))
xhci_clear_hub_tt_buffer(xhci, td, ep);
xhci_handle_halted_endpoint(xhci, ep, td, EP_HARD_RESET);
return; /* xhci_handle_halted_endpoint marked td cancelled */
- default:
- break;
}
xhci_dequeue_td(xhci, td, ep_ring, td->status);
@@ -2385,7 +2344,7 @@ static void process_ctrl_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
case COMP_STOPPED_LENGTH_INVALID:
goto finish_td;
default:
- if (!xhci_halted_host_endpoint(ep_ctx, trb_comp_code))
+ if (!xhci_halted_host_endpoint(xhci, ep_ctx, trb_comp_code))
break;
xhci_dbg(xhci, "TRB error %u, halted endpoint index = %u\n",
trb_comp_code, ep->ep_index);
@@ -2476,6 +2435,12 @@ static void process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
if (ep_trb != td->end_trb)
td->error_mid_td = true;
break;
+ case COMP_MISSED_SERVICE_ERROR:
+ frame->status = -EXDEV;
+ sum_trbs_for_length = true;
+ if (ep_trb != td->end_trb)
+ td->error_mid_td = true;
+ break;
case COMP_INCOMPATIBLE_DEVICE_ERROR:
case COMP_STALL_ERROR:
frame->status = -EPROTO;
@@ -2644,6 +2609,22 @@ static int handle_transferless_tx_event(struct xhci_hcd *xhci, struct xhci_virt_
return 0;
}
+static bool xhci_spurious_success_tx_event(struct xhci_hcd *xhci,
+ struct xhci_ring *ring)
+{
+ switch (ring->old_trb_comp_code) {
+ case COMP_SHORT_PACKET:
+ return xhci->quirks & XHCI_SPURIOUS_SUCCESS;
+ case COMP_USB_TRANSACTION_ERROR:
+ case COMP_BABBLE_DETECTED_ERROR:
+ case COMP_ISOCH_BUFFER_OVERRUN:
+ return xhci->quirks & XHCI_ETRON_HOST &&
+ ring->type == TYPE_ISOC;
+ default:
+ return false;
+ }
+}
+
/*
* If this function returns an error condition, it means it got a Transfer
* event with a corrupted Slot ID, Endpoint ID, or TRB DMA address.
@@ -2659,11 +2640,11 @@ static int handle_tx_event(struct xhci_hcd *xhci,
int ep_index;
struct xhci_td *td = NULL;
dma_addr_t ep_trb_dma;
- struct xhci_segment *ep_seg;
union xhci_trb *ep_trb;
int status = -EINPROGRESS;
struct xhci_ep_ctx *ep_ctx;
u32 trb_comp_code;
+ bool ring_xrun_event = false;
slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags));
ep_index = TRB_TO_EP_ID(le32_to_cpu(event->flags)) - 1;
@@ -2689,6 +2670,9 @@ static int handle_tx_event(struct xhci_hcd *xhci,
if (!ep_ring)
return handle_transferless_tx_event(xhci, ep, trb_comp_code);
+ /* find the transfer trb this events points to */
+ ep_trb = xhci_dma_to_trb(ep_ring->deq_seg, ep_trb_dma, NULL);
+
/* Look for common error cases */
switch (trb_comp_code) {
/* Skip codes that require special handling depending on
@@ -2697,8 +2681,8 @@ static int handle_tx_event(struct xhci_hcd *xhci,
case COMP_SUCCESS:
if (EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)) != 0) {
trb_comp_code = COMP_SHORT_PACKET;
- xhci_dbg(xhci, "Successful completion on short TX for slot %u ep %u with last td short %d\n",
- slot_id, ep_index, ep_ring->last_td_was_short);
+ xhci_dbg(xhci, "Successful completion on short TX for slot %u ep %u with last td comp code %d\n",
+ slot_id, ep_index, ep_ring->old_trb_comp_code);
}
break;
case COMP_SHORT_PACKET:
@@ -2770,14 +2754,12 @@ static int handle_tx_event(struct xhci_hcd *xhci,
* Underrun Event for OUT Isoch endpoint.
*/
xhci_dbg(xhci, "Underrun event on slot %u ep %u\n", slot_id, ep_index);
- if (ep->skip)
- break;
- return 0;
+ ring_xrun_event = true;
+ break;
case COMP_RING_OVERRUN:
xhci_dbg(xhci, "Overrun event on slot %u ep %u\n", slot_id, ep_index);
- if (ep->skip)
- break;
- return 0;
+ ring_xrun_event = true;
+ break;
case COMP_MISSED_SERVICE_ERROR:
/*
* When encounter missed service error, one or more isoc tds
@@ -2787,9 +2769,9 @@ static int handle_tx_event(struct xhci_hcd *xhci,
*/
ep->skip = true;
xhci_dbg(xhci,
- "Miss service interval error for slot %u ep %u, set skip flag\n",
- slot_id, ep_index);
- return 0;
+ "Miss service interval error for slot %u ep %u, set skip flag%s\n",
+ slot_id, ep_index, ep_trb_dma ? ", skip now" : "");
+ break;
case COMP_NO_PING_RESPONSE_ERROR:
ep->skip = true;
xhci_dbg(xhci,
@@ -2832,11 +2814,15 @@ static int handle_tx_event(struct xhci_hcd *xhci,
*/
td = list_first_entry_or_null(&ep_ring->td_list, struct xhci_td, td_list);
- if (td && td->error_mid_td && !trb_in_td(xhci, td, ep_trb_dma, false)) {
+ if (td && td->error_mid_td && !trb_in_td(td, ep_trb_dma)) {
xhci_dbg(xhci, "Missing TD completion event after mid TD error\n");
xhci_dequeue_td(xhci, td, ep_ring, td->status);
}
+ /* If the TRB pointer is NULL, missed TDs will be skipped on the next event */
+ if (trb_comp_code == COMP_MISSED_SERVICE_ERROR && !ep_trb_dma)
+ return 0;
+
if (list_empty(&ep_ring->td_list)) {
/*
* Don't print wanings if ring is empty due to a stopped endpoint generating an
@@ -2846,7 +2832,8 @@ static int handle_tx_event(struct xhci_hcd *xhci,
*/
if (trb_comp_code != COMP_STOPPED &&
trb_comp_code != COMP_STOPPED_LENGTH_INVALID &&
- !ep_ring->last_td_was_short) {
+ !ring_xrun_event &&
+ !xhci_spurious_success_tx_event(xhci, ep_ring)) {
xhci_warn(xhci, "Event TRB for slot %u ep %u with no TDs queued\n",
slot_id, ep_index);
}
@@ -2859,15 +2846,30 @@ static int handle_tx_event(struct xhci_hcd *xhci,
td = list_first_entry(&ep_ring->td_list, struct xhci_td,
td_list);
- /* Is this a TRB in the currently executing TD? */
- ep_seg = trb_in_td(xhci, td, ep_trb_dma, false);
-
- if (!ep_seg) {
+ /* Is this TRB not part of the currently executing TD? */
+ if (!trb_in_td(td, ep_trb_dma)) {
if (ep->skip && usb_endpoint_xfer_isoc(&td->urb->ep->desc)) {
+ /* this event is unlikely to match any TD, don't skip them all */
+ if (trb_comp_code == COMP_STOPPED_LENGTH_INVALID)
+ return 0;
+
skip_isoc_td(xhci, td, ep, status);
- if (!list_empty(&ep_ring->td_list))
+
+ if (!list_empty(&ep_ring->td_list)) {
+ if (ring_xrun_event) {
+ /*
+ * If we are here, we are on xHCI 1.0 host with no
+ * idea how many TDs were missed or where the xrun
+ * occurred. New TDs may have been added after the
+ * xrun, so skip only one TD to be safe.
+ */
+ xhci_dbg(xhci, "Skipped one TD for slot %u ep %u",
+ slot_id, ep_index);
+ return 0;
+ }
continue;
+ }
xhci_dbg(xhci, "All TDs skipped for slot %u ep %u. Clear skip flag.\n",
slot_id, ep_index);
@@ -2876,6 +2878,10 @@ static int handle_tx_event(struct xhci_hcd *xhci,
goto check_endpoint_halted;
}
+ /* TD was queued after xrun, maybe xrun was on a link, don't panic yet */
+ if (ring_xrun_event)
+ return 0;
+
/*
* Skip the Force Stopped Event. The 'ep_trb' of FSE is not in the current
* TD pointed by 'ep_ring->dequeue' because that the hardware dequeue
@@ -2890,21 +2896,17 @@ static int handle_tx_event(struct xhci_hcd *xhci,
/*
* Some hosts give a spurious success event after a short
- * transfer. Ignore it.
+ * transfer or error on last TRB. Ignore it.
*/
- if ((xhci->quirks & XHCI_SPURIOUS_SUCCESS) &&
- ep_ring->last_td_was_short) {
- ep_ring->last_td_was_short = false;
+ if (xhci_spurious_success_tx_event(xhci, ep_ring)) {
+ xhci_dbg(xhci, "Spurious event dma %pad, comp_code %u after %u\n",
+ &ep_trb_dma, trb_comp_code, ep_ring->old_trb_comp_code);
+ ep_ring->old_trb_comp_code = 0;
return 0;
}
/* HC is busted, give up! */
- xhci_err(xhci,
- "ERROR Transfer event TRB DMA ptr not part of current TD ep_index %d comp_code %u\n",
- ep_index, trb_comp_code);
- trb_in_td(xhci, td, ep_trb_dma, true);
-
- return -ESHUTDOWN;
+ goto debug_finding_td;
}
if (ep->skip) {
@@ -2922,12 +2924,12 @@ static int handle_tx_event(struct xhci_hcd *xhci,
*/
} while (ep->skip);
- if (trb_comp_code == COMP_SHORT_PACKET)
- ep_ring->last_td_was_short = true;
- else
- ep_ring->last_td_was_short = false;
+ ep_ring->old_trb_comp_code = trb_comp_code;
+
+ /* Get out if a TD was queued at enqueue after the xrun occurred */
+ if (ring_xrun_event)
+ return 0;
- ep_trb = &ep_seg->trbs[(ep_trb_dma - ep_seg->dma) / sizeof(*ep_trb)];
trace_xhci_handle_transfer(ep_ring, (struct xhci_generic_trb *) ep_trb, ep_trb_dma);
/*
@@ -2952,11 +2954,19 @@ static int handle_tx_event(struct xhci_hcd *xhci,
return 0;
check_endpoint_halted:
- if (xhci_halted_host_endpoint(ep_ctx, trb_comp_code))
+ if (xhci_halted_host_endpoint(xhci, ep_ctx, trb_comp_code))
xhci_handle_halted_endpoint(xhci, ep, td, EP_HARD_RESET);
return 0;
+debug_finding_td:
+ xhci_err(xhci, "Event dma %pad for ep %d status %d not part of TD at %016llx - %016llx\n",
+ &ep_trb_dma, ep_index, trb_comp_code,
+ (unsigned long long)xhci_trb_virt_to_dma(td->start_seg, td->start_trb),
+ (unsigned long long)xhci_trb_virt_to_dma(td->end_seg, td->end_trb));
+
+ return -ESHUTDOWN;
+
err_out:
xhci_err(xhci, "@%016llx %08x %08x %08x %08x\n",
(unsigned long long) xhci_trb_virt_to_dma(
@@ -3025,9 +3035,9 @@ static int xhci_handle_event_trb(struct xhci_hcd *xhci, struct xhci_interrupter
* - When all events have finished
* - To avoid "Event Ring Full Error" condition
*/
-static void xhci_update_erst_dequeue(struct xhci_hcd *xhci,
- struct xhci_interrupter *ir,
- bool clear_ehb)
+void xhci_update_erst_dequeue(struct xhci_hcd *xhci,
+ struct xhci_interrupter *ir,
+ bool clear_ehb)
{
u64 temp_64;
dma_addr_t deq;
@@ -3058,11 +3068,14 @@ static void xhci_update_erst_dequeue(struct xhci_hcd *xhci,
static void xhci_clear_interrupt_pending(struct xhci_interrupter *ir)
{
if (!ir->ip_autoclear) {
- u32 irq_pending;
+ u32 iman;
- irq_pending = readl(&ir->ir_set->irq_pending);
- irq_pending |= IMAN_IP;
- writel(irq_pending, &ir->ir_set->irq_pending);
+ iman = readl(&ir->ir_set->iman);
+ iman |= IMAN_IP;
+ writel(iman, &ir->ir_set->iman);
+
+ /* Read operation to guarantee the write has been flushed from posted buffers */
+ readl(&ir->ir_set->iman);
}
}
@@ -3070,10 +3083,11 @@ static void xhci_clear_interrupt_pending(struct xhci_interrupter *ir)
* Handle all OS-owned events on an interrupter event ring. It may drop
* and reaquire xhci->lock between event processing.
*/
-static int xhci_handle_events(struct xhci_hcd *xhci, struct xhci_interrupter *ir)
+static int xhci_handle_events(struct xhci_hcd *xhci, struct xhci_interrupter *ir,
+ bool skip_events)
{
int event_loop = 0;
- int err;
+ int err = 0;
u64 temp;
xhci_clear_interrupt_pending(ir);
@@ -3096,7 +3110,8 @@ static int xhci_handle_events(struct xhci_hcd *xhci, struct xhci_interrupter *ir
/* Process all OS owned event TRBs on this event ring */
while (unhandled_event_trb(ir->event_ring)) {
- err = xhci_handle_event_trb(xhci, ir, ir->event_ring->dequeue);
+ if (!skip_events)
+ err = xhci_handle_event_trb(xhci, ir, ir->event_ring->dequeue);
/*
* If half a segment of events have been handled in one go then
@@ -3124,6 +3139,37 @@ static int xhci_handle_events(struct xhci_hcd *xhci, struct xhci_interrupter *ir
}
/*
+ * Move the event ring dequeue pointer to skip events kept in the secondary
+ * event ring. This is used to ensure that pending events in the ring are
+ * acknowledged, so the xHCI HCD can properly enter suspend/resume. The
+ * secondary ring is typically maintained by an external component.
+ */
+void xhci_skip_sec_intr_events(struct xhci_hcd *xhci,
+ struct xhci_ring *ring, struct xhci_interrupter *ir)
+{
+ union xhci_trb *current_trb;
+ u64 erdp_reg;
+ dma_addr_t deq;
+
+ /* disable irq, ack pending interrupt and ack all pending events */
+ xhci_disable_interrupter(xhci, ir);
+
+ /* last acked event trb is in erdp reg */
+ erdp_reg = xhci_read_64(xhci, &ir->ir_set->erst_dequeue);
+ deq = (dma_addr_t)(erdp_reg & ERST_PTR_MASK);
+ if (!deq) {
+ xhci_err(xhci, "event ring handling not required\n");
+ return;
+ }
+
+ current_trb = ir->event_ring->dequeue;
+ /* read cycle state of the last acked trb to find out CCS */
+ ring->cycle_state = le32_to_cpu(current_trb->event_cmd.flags) & TRB_CYCLE;
+
+ xhci_handle_events(xhci, ir, true);
+}
+
+/*
* xHCI spec says we can get an interrupt, and if the HC has an error condition,
* we might get bad data out of the event ring. Section 4.10.2.7 has a list of
* indicators of an event TRB error, but we check the status *first* to be safe.
@@ -3149,6 +3195,7 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd)
if (status & STS_HCE) {
xhci_warn(xhci, "WARNING: Host Controller Error\n");
+ xhci_halt(xhci);
goto out;
}
@@ -3161,13 +3208,12 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd)
/*
* Clear the op reg interrupt status first,
* so we can receive interrupts from other MSI-X interrupters.
- * Write 1 to clear the interrupt status.
+ * USBSTS bits are write 1 to clear.
*/
- status |= STS_EINT;
- writel(status, &xhci->op_regs->status);
+ writel(STS_EINT, &xhci->op_regs->status);
/* This is the handler of the primary interrupter */
- xhci_handle_events(xhci, xhci->interrupters[0]);
+ xhci_handle_events(xhci, xhci->interrupters[0], false);
out:
spin_unlock(&xhci->lock);
@@ -3216,7 +3262,6 @@ static void queue_trb(struct xhci_hcd *xhci, struct xhci_ring *ring,
static int prepare_ring(struct xhci_hcd *xhci, struct xhci_ring *ep_ring,
u32 ep_state, unsigned int num_trbs, gfp_t mem_flags)
{
- unsigned int link_trb_count = 0;
unsigned int new_segs = 0;
/* Make sure the endpoint has been added to xHC schedule */
@@ -3264,33 +3309,9 @@ static int prepare_ring(struct xhci_hcd *xhci, struct xhci_ring *ep_ring,
}
}
- while (trb_is_link(ep_ring->enqueue)) {
- /* If we're not dealing with 0.95 hardware or isoc rings
- * on AMD 0.96 host, clear the chain bit.
- */
- if (!xhci_link_chain_quirk(xhci, ep_ring->type))
- ep_ring->enqueue->link.control &=
- cpu_to_le32(~TRB_CHAIN);
- else
- ep_ring->enqueue->link.control |=
- cpu_to_le32(TRB_CHAIN);
-
- wmb();
- ep_ring->enqueue->link.control ^= cpu_to_le32(TRB_CYCLE);
-
- /* Toggle the cycle bit after the last ring segment. */
- if (link_trb_toggles_cycle(ep_ring->enqueue))
- ep_ring->cycle_state ^= 1;
-
- ep_ring->enq_seg = ep_ring->enq_seg->next;
- ep_ring->enqueue = ep_ring->enq_seg->trbs;
-
- /* prevent infinite loop if all first trbs are link trbs */
- if (link_trb_count++ > ep_ring->num_segs) {
- xhci_warn(xhci, "Ring is an endless link TRB loop\n");
- return -EINVAL;
- }
- }
+ /* Ensure that new TRBs won't overwrite a link */
+ if (trb_is_link(ep_ring->enqueue))
+ inc_enq_past_link(xhci, ep_ring, 0);
if (last_trb_on_seg(ep_ring->enq_seg, ep_ring->enqueue)) {
xhci_warn(xhci, "Missing link TRB at end of ring segment\n");
@@ -3508,7 +3529,7 @@ static u32 xhci_td_remainder(struct xhci_hcd *xhci, int transferred,
if ((xhci->quirks & XHCI_MTK_HOST) && (xhci->hci_version < 0x100))
trb_buff_len = 0;
- maxp = usb_endpoint_maxp(&urb->ep->desc);
+ maxp = xhci_usb_endpoint_maxp(urb->dev, urb->ep);
total_packet_count = DIV_ROUND_UP(td_total_len, maxp);
/* Queueing functions don't count the current TRB into transferred */
@@ -3525,7 +3546,7 @@ static int xhci_align_td(struct xhci_hcd *xhci, struct urb *urb, u32 enqd_len,
u32 new_buff_len;
size_t len;
- max_pkt = usb_endpoint_maxp(&urb->ep->desc);
+ max_pkt = xhci_usb_endpoint_maxp(urb->dev, urb->ep);
unalign = (enqd_len + *trb_buff_len) % max_pkt;
/* we got lucky, last normal TRB data on segment is packet aligned */
@@ -3776,7 +3797,7 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
* enqueue a No Op TRB, this can prevent the Setup and Data Stage
* TRB to be breaked by the Link TRB.
*/
- if (trb_is_link(ep_ring->enqueue + 1)) {
+ if (last_trb_on_seg(ep_ring->enq_seg, ep_ring->enqueue + 1)) {
field = TRB_TYPE(TRB_TR_NOOP) | ep_ring->cycle_state;
queue_trb(xhci, ep_ring, false, 0, 0,
TRB_INTR_TARGET(0), field);
@@ -3945,6 +3966,16 @@ static unsigned int xhci_get_last_burst_packet_count(struct xhci_hcd *xhci,
return total_packet_count - 1;
}
+/* Returns the Isochronous Scheduling Threshold in Microframes. 1 Frame is 8 Microframes. */
+static int xhci_ist_microframes(struct xhci_hcd *xhci)
+{
+ int ist = HCS_IST_VALUE(xhci->hcs_params2);
+
+ if (xhci->hcs_params2 & HCS_IST_UNIT)
+ ist *= 8;
+ return ist;
+}
+
/*
* Calculates Frame ID field of the isochronous TRB identifies the
* target frame that the Interval associated with this Isochronous
@@ -3964,17 +3995,7 @@ static int xhci_get_isoc_frame_id(struct xhci_hcd *xhci,
else
start_frame = (urb->start_frame + index * urb->interval) >> 3;
- /* Isochronous Scheduling Threshold (IST, bits 0~3 in HCSPARAMS2):
- *
- * If bit [3] of IST is cleared to '0', software can add a TRB no
- * later than IST[2:0] Microframes before that TRB is scheduled to
- * be executed.
- * If bit [3] of IST is set to '1', software can add a TRB no later
- * than IST[2:0] Frames before that TRB is scheduled to be executed.
- */
- ist = HCS_IST(xhci->hcs_params2) & 0x7;
- if (HCS_IST(xhci->hcs_params2) & (1 << 3))
- ist <<= 3;
+ ist = xhci_ist_microframes(xhci);
/* Software shall not schedule an Isoch TD with a Frame ID value that
* is less than the Start Frame ID or greater than the End Frame ID,
@@ -4096,7 +4117,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
addr = start_addr + urb->iso_frame_desc[i].offset;
td_len = urb->iso_frame_desc[i].length;
td_remain_len = td_len;
- max_pkt = usb_endpoint_maxp(&urb->ep->desc);
+ max_pkt = xhci_usb_endpoint_maxp(urb->dev, urb->ep);
total_pkt_count = DIV_ROUND_UP(td_len, max_pkt);
/* A zero-length transfer still involves at least one packet. */
@@ -4119,7 +4140,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
/* use SIA as default, if frame id is used overwrite it */
sia_frame_id = TRB_SIA;
if (!(urb->transfer_flags & URB_ISO_ASAP) &&
- HCC_CFC(xhci->hcc_params)) {
+ (xhci->hcc_params & HCC_CFC)) {
frame_id = xhci_get_isoc_frame_id(xhci, urb, i);
if (frame_id >= 0)
sia_frame_id = TRB_FRAME_ID(frame_id);
@@ -4203,7 +4224,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
}
/* store the next frame id */
- if (HCC_CFC(xhci->hcc_params))
+ if (xhci->hcc_params & HCC_CFC)
xep->next_frame_id = urb->start_frame + num_tds * urb->interval;
if (xhci_to_hcd(xhci)->self.bandwidth_isoc_reqs == 0) {
@@ -4228,7 +4249,7 @@ cleanup:
*/
urb_priv->td[0].end_trb = ep_ring->enqueue;
/* Every TRB except the first & last will have its cycle bit flipped. */
- td_to_noop(&urb_priv->td[0], true);
+ td_to_noop(xhci, xep, &urb_priv->td[0], true);
/* Reset the ring enqueue back to the first TRB and its cycle bit. */
ep_ring->enqueue = urb_priv->td[0].start_trb;
@@ -4282,7 +4303,7 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags,
check_interval(urb, ep_ctx);
/* Calculate the start frame and put it in urb->start_frame. */
- if (HCC_CFC(xhci->hcc_params) && !list_empty(&ep_ring->td_list)) {
+ if ((xhci->hcc_params & HCC_CFC) && !list_empty(&ep_ring->td_list)) {
if (GET_EP_CTX_STATE(ep_ctx) == EP_STATE_RUNNING) {
urb->start_frame = xep->next_frame_id;
goto skip_start_over;
@@ -4295,9 +4316,7 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags,
* Round up to the next frame and consider the time before trb really
* gets scheduled by hardare.
*/
- ist = HCS_IST(xhci->hcs_params2) & 0x7;
- if (HCS_IST(xhci->hcs_params2) & (1 << 3))
- ist <<= 3;
+ ist = xhci_ist_microframes(xhci);
start_frame += ist + XHCI_CFC_DELAY;
start_frame = roundup(start_frame, 8);
@@ -4338,7 +4357,8 @@ static int queue_command(struct xhci_hcd *xhci, struct xhci_command *cmd,
if ((xhci->xhc_state & XHCI_STATE_DYING) ||
(xhci->xhc_state & XHCI_STATE_HALTED)) {
- xhci_dbg(xhci, "xHCI dying or halted, can't queue_command\n");
+ xhci_dbg(xhci, "xHCI dying or halted, can't queue_command. state: 0x%x\n",
+ xhci->xhc_state);
return -ESHUTDOWN;
}
@@ -4414,6 +4434,17 @@ int xhci_queue_configure_endpoint(struct xhci_hcd *xhci,
command_must_succeed);
}
+/* Queue a get root hub port bandwidth command TRB */
+int xhci_queue_get_port_bw(struct xhci_hcd *xhci,
+ struct xhci_command *cmd, dma_addr_t in_ctx_ptr,
+ u8 dev_speed, bool command_must_succeed)
+{
+ return queue_command(xhci, cmd, lower_32_bits(in_ctx_ptr),
+ upper_32_bits(in_ctx_ptr), 0,
+ TRB_TYPE(TRB_GET_BW) | DEV_SPEED_FOR_TRB(dev_speed),
+ command_must_succeed);
+}
+
/* Queue an evaluate context command TRB */
int xhci_queue_evaluate_context(struct xhci_hcd *xhci, struct xhci_command *cmd,
dma_addr_t in_ctx_ptr, u32 slot_id, bool command_must_succeed)
diff --git a/drivers/usb/host/xhci-rzg3e-regs.h b/drivers/usb/host/xhci-rzg3e-regs.h
new file mode 100644
index 000000000000..7a244a47b882
--- /dev/null
+++ b/drivers/usb/host/xhci-rzg3e-regs.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __XHCI_RZG3E_H
+#define __XHCI_RZG3E_H
+
+#define RZG3E_USB3_HOST_INTEN 0x1044 /* Interrupt Enable */
+#define RZG3E_USB3_HOST_U3P0PIPESC(x) (0x10c0 + (x) * 4) /* PIPE Status and Control Register */
+
+#define RZG3E_USB3_HOST_INTEN_XHC BIT(0)
+#define RZG3E_USB3_HOST_INTEN_HSE BIT(2)
+#define RZG3E_USB3_HOST_INTEN_ENA (RZG3E_USB3_HOST_INTEN_XHC | RZG3E_USB3_HOST_INTEN_HSE)
+
+#endif /* __XHCI_RZG3E_H */
diff --git a/drivers/usb/host/xhci-sideband.c b/drivers/usb/host/xhci-sideband.c
new file mode 100644
index 000000000000..23153e136d4b
--- /dev/null
+++ b/drivers/usb/host/xhci-sideband.c
@@ -0,0 +1,494 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * xHCI host controller sideband support
+ *
+ * Copyright (c) 2023-2025, Intel Corporation.
+ *
+ * Author: Mathias Nyman
+ */
+
+#include <linux/usb/xhci-sideband.h>
+#include <linux/dma-direct.h>
+
+#include "xhci.h"
+
+/* sideband internal helpers */
+static struct sg_table *
+xhci_ring_to_sgtable(struct xhci_sideband *sb, struct xhci_ring *ring)
+{
+ struct xhci_segment *seg;
+ struct sg_table *sgt;
+ unsigned int n_pages;
+ struct page **pages;
+ struct device *dev;
+ size_t sz;
+ int i;
+
+ dev = xhci_to_hcd(sb->xhci)->self.sysdev;
+ sz = ring->num_segs * TRB_SEGMENT_SIZE;
+ n_pages = PAGE_ALIGN(sz) >> PAGE_SHIFT;
+ pages = kvmalloc_objs(struct page *, n_pages);
+ if (!pages)
+ return NULL;
+
+ sgt = kzalloc_obj(*sgt);
+ if (!sgt) {
+ kvfree(pages);
+ return NULL;
+ }
+
+ seg = ring->first_seg;
+ if (!seg)
+ goto err;
+ /*
+ * Rings can potentially have multiple segments, create an array that
+ * carries page references to allocated segments. Utilize the
+ * sg_alloc_table_from_pages() to create the sg table, and to ensure
+ * that page links are created.
+ */
+ for (i = 0; i < ring->num_segs; i++) {
+ dma_get_sgtable(dev, sgt, seg->trbs, seg->dma,
+ TRB_SEGMENT_SIZE);
+ pages[i] = sg_page(sgt->sgl);
+ sg_free_table(sgt);
+ seg = seg->next;
+ }
+
+ if (sg_alloc_table_from_pages(sgt, pages, n_pages, 0, sz, GFP_KERNEL))
+ goto err;
+
+ /*
+ * Save first segment dma address to sg dma_address field for the sideband
+ * client to have access to the IOVA of the ring.
+ */
+ sg_dma_address(sgt->sgl) = ring->first_seg->dma;
+
+ return sgt;
+
+err:
+ kvfree(pages);
+ kfree(sgt);
+
+ return NULL;
+}
+
+/* Caller must hold sb->mutex */
+static void
+__xhci_sideband_remove_endpoint(struct xhci_sideband *sb, struct xhci_virt_ep *ep)
+{
+ lockdep_assert_held(&sb->mutex);
+
+ /*
+ * Issue a stop endpoint command when an endpoint is removed.
+ * The stop ep cmd handler will handle the ring cleanup.
+ */
+ xhci_stop_endpoint_sync(sb->xhci, ep, 0, GFP_KERNEL);
+
+ ep->sideband = NULL;
+ sb->eps[ep->ep_index] = NULL;
+}
+
+/* Caller must hold sb->mutex */
+static void
+__xhci_sideband_remove_interrupter(struct xhci_sideband *sb)
+{
+ lockdep_assert_held(&sb->mutex);
+
+ if (!sb->ir)
+ return;
+
+ xhci_remove_secondary_interrupter(xhci_to_hcd(sb->xhci), sb->ir);
+ sb->ir = NULL;
+}
+
+/* sideband api functions */
+
+/**
+ * xhci_sideband_notify_ep_ring_free - notify client of xfer ring free
+ * @sb: sideband instance for this usb device
+ * @ep_index: usb endpoint index
+ *
+ * Notifies the xHCI sideband client driver of a xHCI transfer ring free
+ * routine. This will allow for the client to ensure that all transfers
+ * are completed.
+ *
+ * The callback should be synchronous, as the ring free happens after.
+ */
+void xhci_sideband_notify_ep_ring_free(struct xhci_sideband *sb,
+ unsigned int ep_index)
+{
+ struct xhci_sideband_event evt;
+
+ evt.type = XHCI_SIDEBAND_XFER_RING_FREE;
+ evt.evt_data = &ep_index;
+
+ if (sb->notify_client)
+ sb->notify_client(sb->intf, &evt);
+}
+EXPORT_SYMBOL_GPL(xhci_sideband_notify_ep_ring_free);
+
+/**
+ * xhci_sideband_add_endpoint - add endpoint to sideband access list
+ * @sb: sideband instance for this usb device
+ * @host_ep: usb host endpoint
+ *
+ * Adds an endpoint to the list of sideband accessed endpoints for this usb
+ * device.
+ * After an endpoint is added the sideband client can get the endpoint transfer
+ * ring buffer by calling xhci_sideband_endpoint_buffer()
+ *
+ * Return: 0 on success, negative error otherwise.
+ */
+int
+xhci_sideband_add_endpoint(struct xhci_sideband *sb,
+ struct usb_host_endpoint *host_ep)
+{
+ struct xhci_virt_ep *ep;
+ unsigned int ep_index;
+
+ guard(mutex)(&sb->mutex);
+
+ if (!sb->vdev)
+ return -ENODEV;
+
+ ep_index = xhci_get_endpoint_index(&host_ep->desc);
+ ep = &sb->vdev->eps[ep_index];
+
+ if (ep->ep_state & EP_HAS_STREAMS)
+ return -EINVAL;
+
+ /*
+ * Note, we don't know the DMA mask of the audio DSP device, if its
+ * smaller than for xhci it won't be able to access the endpoint ring
+ * buffer. This could be solved by not allowing the audio class driver
+ * to add the endpoint the normal way, but instead offload it immediately,
+ * and let this function add the endpoint and allocate the ring buffer
+ * with the smallest common DMA mask
+ */
+ if (sb->eps[ep_index] || ep->sideband)
+ return -EBUSY;
+
+ ep->sideband = sb;
+ sb->eps[ep_index] = ep;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(xhci_sideband_add_endpoint);
+
+/**
+ * xhci_sideband_remove_endpoint - remove endpoint from sideband access list
+ * @sb: sideband instance for this usb device
+ * @host_ep: usb host endpoint
+ *
+ * Removes an endpoint from the list of sideband accessed endpoints for this usb
+ * device.
+ * sideband client should no longer touch the endpoint transfer buffer after
+ * calling this.
+ *
+ * Return: 0 on success, negative error otherwise.
+ */
+int
+xhci_sideband_remove_endpoint(struct xhci_sideband *sb,
+ struct usb_host_endpoint *host_ep)
+{
+ struct xhci_virt_ep *ep;
+ unsigned int ep_index;
+
+ guard(mutex)(&sb->mutex);
+
+ ep_index = xhci_get_endpoint_index(&host_ep->desc);
+ ep = sb->eps[ep_index];
+
+ if (!ep || !ep->sideband || ep->sideband != sb)
+ return -ENODEV;
+
+ __xhci_sideband_remove_endpoint(sb, ep);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(xhci_sideband_remove_endpoint);
+
+int
+xhci_sideband_stop_endpoint(struct xhci_sideband *sb,
+ struct usb_host_endpoint *host_ep)
+{
+ struct xhci_virt_ep *ep;
+ unsigned int ep_index;
+
+ ep_index = xhci_get_endpoint_index(&host_ep->desc);
+ ep = sb->eps[ep_index];
+
+ if (!ep || !ep->sideband || ep->sideband != sb)
+ return -EINVAL;
+
+ return xhci_stop_endpoint_sync(sb->xhci, ep, 0, GFP_KERNEL);
+}
+EXPORT_SYMBOL_GPL(xhci_sideband_stop_endpoint);
+
+/**
+ * xhci_sideband_get_endpoint_buffer - gets the endpoint transfer buffer address
+ * @sb: sideband instance for this usb device
+ * @host_ep: usb host endpoint
+ *
+ * Returns the address of the endpoint buffer where xHC controller reads queued
+ * transfer TRBs from. This is the starting address of the ringbuffer where the
+ * sideband client should write TRBs to.
+ *
+ * Caller needs to free the returned sg_table
+ *
+ * Return: struct sg_table * if successful. NULL otherwise.
+ */
+struct sg_table *
+xhci_sideband_get_endpoint_buffer(struct xhci_sideband *sb,
+ struct usb_host_endpoint *host_ep)
+{
+ struct xhci_virt_ep *ep;
+ unsigned int ep_index;
+
+ ep_index = xhci_get_endpoint_index(&host_ep->desc);
+ ep = sb->eps[ep_index];
+
+ if (!ep || !ep->ring || !ep->sideband || ep->sideband != sb)
+ return NULL;
+
+ return xhci_ring_to_sgtable(sb, ep->ring);
+}
+EXPORT_SYMBOL_GPL(xhci_sideband_get_endpoint_buffer);
+
+/**
+ * xhci_sideband_get_event_buffer - return the event buffer for this device
+ * @sb: sideband instance for this usb device
+ *
+ * If a secondary xhci interupter is set up for this usb device then this
+ * function returns the address of the event buffer where xHC writes
+ * the transfer completion events.
+ *
+ * Caller needs to free the returned sg_table
+ *
+ * Return: struct sg_table * if successful. NULL otherwise.
+ */
+struct sg_table *
+xhci_sideband_get_event_buffer(struct xhci_sideband *sb)
+{
+ if (!sb || !sb->ir)
+ return NULL;
+
+ return xhci_ring_to_sgtable(sb, sb->ir->event_ring);
+}
+EXPORT_SYMBOL_GPL(xhci_sideband_get_event_buffer);
+
+/**
+ * xhci_sideband_check - check the existence of active sidebands
+ * @hcd: the host controller driver associated with the target host controller
+ *
+ * Allow other drivers, such as usb controller driver, to check if there are
+ * any sideband activity on the host controller. This information could be used
+ * for power management or other forms of resource management. The caller should
+ * ensure downstream usb devices are all marked as "offload_pm_locked" to ensure
+ * the correctness of the return value.
+ *
+ * Returns true on any active sideband existence, false otherwise.
+ */
+bool xhci_sideband_check(struct usb_hcd *hcd)
+{
+ struct usb_device *udev = hcd->self.root_hub;
+ bool active;
+
+ usb_lock_device(udev);
+ active = usb_offload_check(udev);
+ usb_unlock_device(udev);
+
+ return active;
+}
+EXPORT_SYMBOL_GPL(xhci_sideband_check);
+
+/**
+ * xhci_sideband_create_interrupter - creates a new interrupter for this sideband
+ * @sb: sideband instance for this usb device
+ * @num_seg: number of event ring segments to allocate
+ * @ip_autoclear: IP autoclearing support such as MSI implemented
+ *
+ * Sets up a xhci interrupter that can be used for this sideband accessed usb
+ * device. Transfer events for this device can be routed to this interrupters
+ * event ring by setting the 'Interrupter Target' field correctly when queueing
+ * the transfer TRBs.
+ * Once this interrupter is created the interrupter target ID can be obtained
+ * by calling xhci_sideband_interrupter_id()
+ *
+ * Returns 0 on success, negative error otherwise
+ */
+int
+xhci_sideband_create_interrupter(struct xhci_sideband *sb, int num_seg,
+ bool ip_autoclear, u32 imod_interval, int intr_num)
+{
+ if (!sb || !sb->xhci)
+ return -ENODEV;
+
+ guard(mutex)(&sb->mutex);
+
+ if (!sb->vdev)
+ return -ENODEV;
+
+ if (sb->ir)
+ return -EBUSY;
+
+ sb->ir = xhci_create_secondary_interrupter(xhci_to_hcd(sb->xhci),
+ num_seg, imod_interval,
+ intr_num);
+ if (!sb->ir)
+ return -ENOMEM;
+
+ sb->ir->ip_autoclear = ip_autoclear;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(xhci_sideband_create_interrupter);
+
+/**
+ * xhci_sideband_remove_interrupter - remove the interrupter from a sideband
+ * @sb: sideband instance for this usb device
+ *
+ * Removes a registered interrupt for a sideband. This would allow for other
+ * sideband users to utilize this interrupter.
+ */
+void
+xhci_sideband_remove_interrupter(struct xhci_sideband *sb)
+{
+ if (!sb)
+ return;
+
+ guard(mutex)(&sb->mutex);
+
+ __xhci_sideband_remove_interrupter(sb);
+}
+EXPORT_SYMBOL_GPL(xhci_sideband_remove_interrupter);
+
+/**
+ * xhci_sideband_interrupter_id - return the interrupter target id
+ * @sb: sideband instance for this usb device
+ *
+ * If a secondary xhci interrupter is set up for this usb device then this
+ * function returns the ID used by the interrupter. The sideband client
+ * needs to write this ID to the 'Interrupter Target' field of the transfer TRBs
+ * it queues on the endpoints transfer ring to ensure transfer completion event
+ * are written by xHC to the correct interrupter event ring.
+ *
+ * Returns interrupter id on success, negative error othgerwise
+ */
+int
+xhci_sideband_interrupter_id(struct xhci_sideband *sb)
+{
+ if (!sb || !sb->ir)
+ return -ENODEV;
+
+ return sb->ir->intr_num;
+}
+EXPORT_SYMBOL_GPL(xhci_sideband_interrupter_id);
+
+/**
+ * xhci_sideband_register - register a sideband for a usb device
+ * @intf: usb interface associated with the sideband device
+ *
+ * Allows for clients to utilize XHCI interrupters and fetch transfer and event
+ * ring parameters for executing data transfers.
+ *
+ * Return: pointer to a new xhci_sideband instance if successful. NULL otherwise.
+ */
+struct xhci_sideband *
+xhci_sideband_register(struct usb_interface *intf, enum xhci_sideband_type type,
+ int (*notify_client)(struct usb_interface *intf,
+ struct xhci_sideband_event *evt))
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct usb_hcd *hcd = bus_to_hcd(udev->bus);
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ struct xhci_virt_device *vdev;
+ struct xhci_sideband *sb;
+
+ /*
+ * Make sure the usb device is connected to a xhci controller. Fail
+ * registration if the type is anything other than XHCI_SIDEBAND_VENDOR,
+ * as this is the only type that is currently supported by xhci-sideband.
+ */
+ if (!udev->slot_id || type != XHCI_SIDEBAND_VENDOR)
+ return NULL;
+
+ sb = kzalloc_node(sizeof(*sb), GFP_KERNEL, dev_to_node(hcd->self.sysdev));
+ if (!sb)
+ return NULL;
+
+ mutex_init(&sb->mutex);
+
+ /* check this device isn't already controlled via sideband */
+ spin_lock_irq(&xhci->lock);
+
+ vdev = xhci->devs[udev->slot_id];
+
+ if (!vdev || vdev->sideband) {
+ xhci_warn(xhci, "XHCI sideband for slot %d already in use\n",
+ udev->slot_id);
+ spin_unlock_irq(&xhci->lock);
+ kfree(sb);
+ return NULL;
+ }
+
+ sb->xhci = xhci;
+ sb->vdev = vdev;
+ sb->intf = intf;
+ sb->type = type;
+ sb->notify_client = notify_client;
+ vdev->sideband = sb;
+
+ spin_unlock_irq(&xhci->lock);
+
+ return sb;
+}
+EXPORT_SYMBOL_GPL(xhci_sideband_register);
+
+/**
+ * xhci_sideband_unregister - unregister sideband access to a usb device
+ * @sb: sideband instance to be unregistered
+ *
+ * Unregisters sideband access to a usb device and frees the sideband
+ * instance.
+ * After this the endpoint and interrupter event buffers should no longer
+ * be accessed via sideband. The xhci driver can now take over handling
+ * the buffers.
+ */
+void
+xhci_sideband_unregister(struct xhci_sideband *sb)
+{
+ struct xhci_virt_device *vdev;
+ struct xhci_hcd *xhci;
+ int i;
+
+ if (!sb)
+ return;
+
+ xhci = sb->xhci;
+
+ scoped_guard(mutex, &sb->mutex) {
+ vdev = sb->vdev;
+ if (!vdev)
+ return;
+
+ for (i = 0; i < EP_CTX_PER_DEV; i++)
+ if (sb->eps[i])
+ __xhci_sideband_remove_endpoint(sb, sb->eps[i]);
+
+ __xhci_sideband_remove_interrupter(sb);
+
+ sb->vdev = NULL;
+ }
+
+ spin_lock_irq(&xhci->lock);
+ sb->xhci = NULL;
+ vdev->sideband = NULL;
+ spin_unlock_irq(&xhci->lock);
+
+ kfree(sb);
+}
+EXPORT_SYMBOL_GPL(xhci_sideband_unregister);
+MODULE_DESCRIPTION("xHCI sideband driver for secondary interrupter management");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c
index 22dc86fb5254..d2214d309e96 100644
--- a/drivers/usb/host/xhci-tegra.c
+++ b/drivers/usb/host/xhci-tegra.c
@@ -155,6 +155,8 @@
#define FW_IOCTL_TYPE_SHIFT 24
#define FW_IOCTL_CFGTBL_READ 17
+#define WAKE_IRQ_START_INDEX 2
+
struct tegra_xusb_fw_header {
__le32 boot_loadaddr_in_imem;
__le32 boot_codedfi_offset;
@@ -228,6 +230,7 @@ struct tegra_xusb_soc {
unsigned int num_supplies;
const struct tegra_xusb_phy_type *phy_types;
unsigned int num_types;
+ unsigned int max_num_wakes;
const struct tegra_xusb_context_soc *context;
struct {
@@ -263,6 +266,7 @@ struct tegra_xusb {
int xhci_irq;
int mbox_irq;
int padctl_irq;
+ int *wake_irqs;
void __iomem *ipfs_base;
void __iomem *fpci_base;
@@ -313,6 +317,7 @@ struct tegra_xusb {
bool suspended;
struct tegra_xusb_context context;
u8 lp0_utmi_pad_mask;
+ int num_wakes;
};
static struct hc_driver __read_mostly tegra_xhci_hc_driver;
@@ -1352,18 +1357,15 @@ static void tegra_xhci_id_work(struct work_struct *work)
dev_dbg(tegra->dev, "host mode %s\n", str_on_off(tegra->host_mode));
- mutex_lock(&tegra->lock);
-
if (tegra->host_mode)
phy_set_mode_ext(phy, PHY_MODE_USB_OTG, USB_ROLE_HOST);
else
phy_set_mode_ext(phy, PHY_MODE_USB_OTG, USB_ROLE_NONE);
- mutex_unlock(&tegra->lock);
-
tegra->otg_usb3_port = tegra_xusb_padctl_get_usb3_companion(tegra->padctl,
tegra->otg_usb2_port);
+ pm_runtime_get_sync(tegra->dev);
if (tegra->host_mode) {
/* switch to host mode */
if (tegra->otg_usb3_port >= 0) {
@@ -1400,6 +1402,7 @@ static void tegra_xhci_id_work(struct work_struct *work)
tegra_xhci_set_port_power(tegra, true, false);
}
+ pm_runtime_put_autosuspend(tegra->dev);
}
#if IS_ENABLED(CONFIG_PM) || IS_ENABLED(CONFIG_PM_SLEEP)
@@ -1479,7 +1482,7 @@ static int tegra_xhci_id_notify(struct notifier_block *nb,
tegra->otg_usb2_port = tegra_xusb_get_usb2_port(tegra, usbphy);
- tegra->host_mode = (usbphy->last_event == USB_EVENT_ID) ? true : false;
+ tegra->host_mode = usbphy->last_event == USB_EVENT_ID;
schedule_work(&tegra->id_work);
@@ -1534,6 +1537,47 @@ static void tegra_xusb_deinit_usb_phy(struct tegra_xusb *tegra)
otg_set_host(tegra->usbphy[i]->otg, NULL);
}
+static int tegra_xusb_setup_wakeup(struct platform_device *pdev, struct tegra_xusb *tegra)
+{
+ unsigned int i;
+
+ if (tegra->soc->max_num_wakes == 0)
+ return 0;
+
+ tegra->wake_irqs = devm_kcalloc(tegra->dev,
+ tegra->soc->max_num_wakes,
+ sizeof(*tegra->wake_irqs), GFP_KERNEL);
+ if (!tegra->wake_irqs)
+ return -ENOMEM;
+
+ /*
+ * USB wake events are independent of each other, so it is not necessary for a platform
+ * to utilize all wake-up events supported for a given device. The USB host can operate
+ * even if wake-up events are not defined or fail to be configured. Therefore, we only
+ * return critical errors, such as -ENOMEM.
+ */
+ for (i = 0; i < tegra->soc->max_num_wakes; i++) {
+ struct irq_data *data;
+
+ tegra->wake_irqs[i] = platform_get_irq_optional(pdev, i + WAKE_IRQ_START_INDEX);
+ if (tegra->wake_irqs[i] < 0)
+ break;
+
+ data = irq_get_irq_data(tegra->wake_irqs[i]);
+ if (!data) {
+ dev_warn(tegra->dev, "get wake event %d irq data fail\n", i);
+ break;
+ }
+
+ irq_set_irq_type(tegra->wake_irqs[i], irqd_get_trigger_type(data));
+ }
+
+ tegra->num_wakes = i;
+ dev_dbg(tegra->dev, "setup %d wake events\n", tegra->num_wakes);
+
+ return 0;
+}
+
static int tegra_xusb_probe(struct platform_device *pdev)
{
struct tegra_xusb *tegra;
@@ -1584,6 +1628,10 @@ static int tegra_xusb_probe(struct platform_device *pdev)
if (tegra->mbox_irq < 0)
return tegra->mbox_irq;
+ err = tegra_xusb_setup_wakeup(pdev, tegra);
+ if (err)
+ return err;
+
tegra->padctl = tegra_xusb_padctl_get(&pdev->dev);
if (IS_ERR(tegra->padctl))
return PTR_ERR(tegra->padctl);
@@ -1966,7 +2014,7 @@ static bool xhci_hub_ports_suspended(struct xhci_hub *hub)
u32 value;
for (i = 0; i < hub->num_ports; i++) {
- value = readl(hub->ports[i]->addr);
+ value = xhci_portsc_readl(hub->ports[i]);
if ((value & PORT_PE) == 0)
continue;
@@ -2092,7 +2140,7 @@ static void tegra_xhci_enable_phy_sleepwalk_wake(struct tegra_xusb *tegra)
if (!is_host_mode_phy(tegra, i, j))
continue;
- portsc = readl(rhub->ports[index]->addr);
+ portsc = xhci_portsc_readl(rhub->ports[index]);
speed = tegra_xhci_portsc_to_speed(tegra, portsc);
tegra_xusb_padctl_enable_phy_sleepwalk(padctl, phy, speed);
tegra_xusb_padctl_enable_phy_wake(padctl, phy);
@@ -2162,11 +2210,11 @@ static void tegra_xhci_program_utmi_power_lp0_exit(struct tegra_xusb *tegra)
}
}
-static int tegra_xusb_enter_elpg(struct tegra_xusb *tegra, bool runtime)
+static int tegra_xusb_enter_elpg(struct tegra_xusb *tegra, bool is_auto_resume)
{
struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd);
struct device *dev = tegra->dev;
- bool wakeup = runtime ? true : device_may_wakeup(dev);
+ bool wakeup = is_auto_resume ? true : device_may_wakeup(dev);
unsigned int i;
int err;
u32 usbcmd;
@@ -2187,7 +2235,7 @@ static int tegra_xusb_enter_elpg(struct tegra_xusb *tegra, bool runtime)
for (i = 0; i < xhci->usb2_rhub.num_ports; i++) {
if (!xhci->usb2_rhub.ports[i])
continue;
- portsc = readl(xhci->usb2_rhub.ports[i]->addr);
+ portsc = xhci_portsc_readl(xhci->usb2_rhub.ports[i]);
tegra->lp0_utmi_pad_mask &= ~BIT(i);
if (((portsc & PORT_PLS_MASK) == XDEV_U3) || ((portsc & DEV_SPEED_MASK) == XDEV_FS))
tegra->lp0_utmi_pad_mask |= BIT(i);
@@ -2232,11 +2280,11 @@ out:
return err;
}
-static int tegra_xusb_exit_elpg(struct tegra_xusb *tegra, bool runtime)
+static int tegra_xusb_exit_elpg(struct tegra_xusb *tegra, bool is_auto_resume)
{
struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd);
struct device *dev = tegra->dev;
- bool wakeup = runtime ? true : device_may_wakeup(dev);
+ bool wakeup = is_auto_resume ? true : device_may_wakeup(dev);
unsigned int i;
u32 usbcmd;
int err;
@@ -2287,7 +2335,7 @@ static int tegra_xusb_exit_elpg(struct tegra_xusb *tegra, bool runtime)
if (wakeup)
tegra_xhci_disable_phy_sleepwalk(tegra);
- err = xhci_resume(xhci, runtime ? PMSG_AUTO_RESUME : PMSG_RESUME);
+ err = xhci_resume(xhci, false, is_auto_resume);
if (err < 0) {
dev_err(tegra->dev, "failed to resume XHCI: %d\n", err);
goto disable_phy;
@@ -2352,8 +2400,13 @@ out:
pm_runtime_disable(dev);
if (device_may_wakeup(dev)) {
+ unsigned int i;
+
if (enable_irq_wake(tegra->padctl_irq))
dev_err(dev, "failed to enable padctl wakes\n");
+
+ for (i = 0; i < tegra->num_wakes; i++)
+ enable_irq_wake(tegra->wake_irqs[i]);
}
}
@@ -2381,8 +2434,13 @@ static __maybe_unused int tegra_xusb_resume(struct device *dev)
}
if (device_may_wakeup(dev)) {
+ unsigned int i;
+
if (disable_irq_wake(tegra->padctl_irq))
dev_err(dev, "failed to disable padctl wakes\n");
+
+ for (i = 0; i < tegra->num_wakes; i++)
+ disable_irq_wake(tegra->wake_irqs[i]);
}
tegra->suspended = false;
mutex_unlock(&tegra->lock);
@@ -2504,7 +2562,9 @@ static const struct tegra_xusb_soc tegra124_soc = {
.smi_intr = XUSB_CFG_ARU_SMI_INTR,
},
};
+#if IS_ENABLED(CONFIG_ARCH_TEGRA_124_SOC) || IS_ENABLED(CONFIG_ARCH_TEGRA_132_SOC)
MODULE_FIRMWARE("nvidia/tegra124/xusb.bin");
+#endif
static const char * const tegra210_supply_names[] = {
"dvddio-pex",
@@ -2542,11 +2602,15 @@ static const struct tegra_xusb_soc tegra210_soc = {
.smi_intr = XUSB_CFG_ARU_SMI_INTR,
},
};
+#if IS_ENABLED(CONFIG_ARCH_TEGRA_210_SOC)
MODULE_FIRMWARE("nvidia/tegra210/xusb.bin");
+#endif
static const char * const tegra186_supply_names[] = {
};
+#if IS_ENABLED(CONFIG_ARCH_TEGRA_186_SOC)
MODULE_FIRMWARE("nvidia/tegra186/xusb.bin");
+#endif
static const struct tegra_xusb_phy_type tegra186_phy_types[] = {
{ .name = "usb3", .num = 3, },
@@ -2619,7 +2683,9 @@ static const struct tegra_xusb_soc tegra194_soc = {
},
.lpm_support = true,
};
+#if IS_ENABLED(CONFIG_ARCH_TEGRA_194_SOC)
MODULE_FIRMWARE("nvidia/tegra194/xusb.bin");
+#endif
static const struct tegra_xusb_soc_ops tegra234_ops = {
.mbox_reg_readl = &bar2_readl,
@@ -2633,6 +2699,7 @@ static const struct tegra_xusb_soc tegra234_soc = {
.num_supplies = ARRAY_SIZE(tegra194_supply_names),
.phy_types = tegra194_phy_types,
.num_types = ARRAY_SIZE(tegra194_phy_types),
+ .max_num_wakes = 7,
.context = &tegra186_xusb_context,
.ports = {
.usb3 = { .offset = 0, .count = 4, },
@@ -2709,7 +2776,7 @@ static int tegra_xhci_hub_control(struct usb_hcd *hcd, u16 type_req, u16 value,
while (i--) {
if (!test_bit(i, &bus_state->resuming_ports))
continue;
- portsc = readl(ports[i]->addr);
+ portsc = xhci_portsc_readl(ports[i]);
if ((portsc & PORT_PLS_MASK) == XDEV_RESUME)
tegra_phy_xusb_utmi_pad_power_on(
tegra_xusb_get_phy(tegra, "usb2", (int) i));
@@ -2727,7 +2794,7 @@ static int tegra_xhci_hub_control(struct usb_hcd *hcd, u16 type_req, u16 value,
if (!index || index > rhub->num_ports)
return -EPIPE;
ports = rhub->ports;
- portsc = readl(ports[port]->addr);
+ portsc = xhci_portsc_readl(ports[port]);
if (portsc & PORT_CONNECT)
tegra_phy_xusb_utmi_pad_power_on(phy);
}
@@ -2746,7 +2813,7 @@ static int tegra_xhci_hub_control(struct usb_hcd *hcd, u16 type_req, u16 value,
if ((type_req == ClearPortFeature) && (value == USB_PORT_FEAT_C_CONNECTION)) {
ports = rhub->ports;
- portsc = readl(ports[port]->addr);
+ portsc = xhci_portsc_readl(ports[port]);
if (!(portsc & PORT_CONNECT)) {
/* We don't suspend the PAD while HNP role swap happens on the OTG
* port
diff --git a/drivers/usb/host/xhci-trace.h b/drivers/usb/host/xhci-trace.h
index bfb5c5c17012..724cba2dbb78 100644
--- a/drivers/usb/host/xhci-trace.h
+++ b/drivers/usb/host/xhci-trace.h
@@ -71,29 +71,20 @@ DEFINE_EVENT(xhci_log_msg, xhci_dbg_ring_expansion,
);
DECLARE_EVENT_CLASS(xhci_log_ctx,
- TP_PROTO(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx,
- unsigned int ep_num),
- TP_ARGS(xhci, ctx, ep_num),
+ TP_PROTO(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx),
+ TP_ARGS(xhci, ctx),
TP_STRUCT__entry(
__field(int, ctx_64)
__field(unsigned, ctx_type)
__field(dma_addr_t, ctx_dma)
__field(u8 *, ctx_va)
- __field(unsigned, ctx_ep_num)
- __dynamic_array(u32, ctx_data,
- ((HCC_64BYTE_CONTEXT(xhci->hcc_params) + 1) * 8) *
- ((ctx->type == XHCI_CTX_TYPE_INPUT) + ep_num + 1))
),
TP_fast_assign(
- __entry->ctx_64 = HCC_64BYTE_CONTEXT(xhci->hcc_params);
+ __entry->ctx_64 = xhci->hcc_params & HCC_64BYTE_CONTEXT;
__entry->ctx_type = ctx->type;
__entry->ctx_dma = ctx->dma;
__entry->ctx_va = ctx->bytes;
- __entry->ctx_ep_num = ep_num;
- memcpy(__get_dynamic_array(ctx_data), ctx->bytes,
- ((HCC_64BYTE_CONTEXT(xhci->hcc_params) + 1) * 32) *
- ((ctx->type == XHCI_CTX_TYPE_INPUT) + ep_num + 1));
),
TP_printk("ctx_64=%d, ctx_type=%u, ctx_dma=@%llx, ctx_va=@%p",
__entry->ctx_64, __entry->ctx_type,
@@ -102,9 +93,8 @@ DECLARE_EVENT_CLASS(xhci_log_ctx,
);
DEFINE_EVENT(xhci_log_ctx, xhci_address_ctx,
- TP_PROTO(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx,
- unsigned int ep_num),
- TP_ARGS(xhci, ctx, ep_num)
+ TP_PROTO(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx),
+ TP_ARGS(xhci, ctx)
);
DECLARE_EVENT_CLASS(xhci_log_trb,
@@ -541,23 +531,23 @@ DEFINE_EVENT(xhci_log_ring, xhci_inc_deq,
);
DECLARE_EVENT_CLASS(xhci_log_portsc,
- TP_PROTO(struct xhci_port *port, u32 portsc),
- TP_ARGS(port, portsc),
- TP_STRUCT__entry(
- __field(u32, busnum)
- __field(u32, portnum)
- __field(u32, portsc)
- ),
- TP_fast_assign(
- __entry->busnum = port->rhub->hcd->self.busnum;
- __entry->portnum = port->hcd_portnum;
- __entry->portsc = portsc;
- ),
- TP_printk("port %d-%d: %s",
- __entry->busnum,
- __entry->portnum,
- xhci_decode_portsc(__get_buf(XHCI_MSG_MAX), __entry->portsc)
- )
+ TP_PROTO(struct xhci_port *port, u32 portsc),
+ TP_ARGS(port, portsc),
+ TP_STRUCT__entry(
+ __field(u32, busnum)
+ __field(u32, portnum)
+ __field(u32, portsc)
+ ),
+ TP_fast_assign(
+ __entry->busnum = port->rhub->hcd->self.busnum;
+ __entry->portnum = port->hcd_portnum + 1;
+ __entry->portsc = portsc;
+ ),
+ TP_printk("port %d-%d: %s",
+ __entry->busnum,
+ __entry->portnum,
+ xhci_decode_portsc(__get_buf(XHCI_MSG_MAX), __entry->portsc)
+ )
);
DEFINE_EVENT(xhci_log_portsc, xhci_handle_port_status,
@@ -575,6 +565,11 @@ DEFINE_EVENT(xhci_log_portsc, xhci_hub_status_data,
TP_ARGS(port, portsc)
);
+DEFINE_EVENT(xhci_log_portsc, xhci_portsc_writel,
+ TP_PROTO(struct xhci_port *port, u32 portsc),
+ TP_ARGS(port, portsc)
+);
+
DECLARE_EVENT_CLASS(xhci_log_doorbell,
TP_PROTO(u32 slot, u32 doorbell),
TP_ARGS(slot, doorbell),
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 1a90ebc8a30e..a54f5b57f205 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -20,6 +20,7 @@
#include <linux/string_choices.h>
#include <linux/dmi.h>
#include <linux/dma-mapping.h>
+#include <linux/usb/xhci-sideband.h>
#include "xhci.h"
#include "xhci-trace.h"
@@ -40,6 +41,19 @@ static unsigned long long quirks;
module_param(quirks, ullong, S_IRUGO);
MODULE_PARM_DESC(quirks, "Bit flags for quirks to be enabled as default");
+void xhci_portsc_writel(struct xhci_port *port, u32 val)
+{
+ trace_xhci_portsc_writel(port, val);
+ writel(val, &port->port_reg->portsc);
+}
+EXPORT_SYMBOL_GPL(xhci_portsc_writel);
+
+u32 xhci_portsc_readl(struct xhci_port *port)
+{
+ return readl(&port->port_reg->portsc);
+}
+EXPORT_SYMBOL_GPL(xhci_portsc_readl);
+
static bool td_on_ring(struct xhci_td *td, struct xhci_ring *ring)
{
struct xhci_segment *seg;
@@ -84,29 +98,6 @@ int xhci_handshake(void __iomem *ptr, u32 mask, u32 done, u64 timeout_us)
}
/*
- * xhci_handshake_check_state - same as xhci_handshake but takes an additional
- * exit_state parameter, and bails out with an error immediately when xhc_state
- * has exit_state flag set.
- */
-int xhci_handshake_check_state(struct xhci_hcd *xhci, void __iomem *ptr,
- u32 mask, u32 done, int usec, unsigned int exit_state)
-{
- u32 result;
- int ret;
-
- ret = readl_poll_timeout_atomic(ptr, result,
- (result & mask) == done ||
- result == U32_MAX ||
- xhci->xhc_state & exit_state,
- 1, usec);
-
- if (result == U32_MAX || xhci->xhc_state & exit_state)
- return -ENODEV;
-
- return ret;
-}
-
-/*
* Disable interrupts and begin the xHCI halting process.
*/
void xhci_quiesce(struct xhci_hcd *xhci)
@@ -143,7 +134,8 @@ int xhci_halt(struct xhci_hcd *xhci)
ret = xhci_handshake(&xhci->op_regs->status,
STS_HALT, STS_HALT, XHCI_MAX_HALT_USEC);
if (ret) {
- xhci_warn(xhci, "Host halt failed, %d\n", ret);
+ if (!(xhci->xhc_state & XHCI_STATE_DYING))
+ xhci_warn(xhci, "Host halt failed, %d\n", ret);
return ret;
}
@@ -202,7 +194,8 @@ int xhci_reset(struct xhci_hcd *xhci, u64 timeout_us)
state = readl(&xhci->op_regs->status);
if (state == ~(u32)0) {
- xhci_warn(xhci, "Host not accessible, reset failed.\n");
+ if (!(xhci->xhc_state & XHCI_STATE_DYING))
+ xhci_warn(xhci, "Host not accessible, reset failed.\n");
return -ENODEV;
}
@@ -226,8 +219,7 @@ int xhci_reset(struct xhci_hcd *xhci, u64 timeout_us)
if (xhci->quirks & XHCI_INTEL_HOST)
udelay(1000);
- ret = xhci_handshake_check_state(xhci, &xhci->op_regs->command,
- CMD_RESET, 0, timeout_us, XHCI_STATE_REMOVING);
+ ret = xhci_handshake(&xhci->op_regs->command, CMD_RESET, 0, timeout_us);
if (ret)
return ret;
@@ -258,7 +250,6 @@ static void xhci_zero_64b_regs(struct xhci_hcd *xhci)
struct iommu_domain *domain;
int err, i;
u64 val;
- u32 intrs;
/*
* Some Renesas controllers get into a weird state if they are
@@ -299,10 +290,7 @@ static void xhci_zero_64b_regs(struct xhci_hcd *xhci)
if (upper_32_bits(val))
xhci_write_64(xhci, 0, &xhci->op_regs->cmd_ring);
- intrs = min_t(u32, HCS_MAX_INTRS(xhci->hcs_params1),
- ARRAY_SIZE(xhci->run_regs->ir_set));
-
- for (i = 0; i < intrs; i++) {
+ for (i = 0; i < xhci->max_interrupters; i++) {
struct xhci_intr_reg __iomem *ir;
ir = &xhci->run_regs->ir_set[i];
@@ -322,28 +310,38 @@ static void xhci_zero_64b_regs(struct xhci_hcd *xhci)
xhci_info(xhci, "Fault detected\n");
}
-static int xhci_enable_interrupter(struct xhci_interrupter *ir)
+int xhci_enable_interrupter(struct xhci_interrupter *ir)
{
u32 iman;
if (!ir || !ir->ir_set)
return -EINVAL;
- iman = readl(&ir->ir_set->irq_pending);
- writel(ER_IRQ_ENABLE(iman), &ir->ir_set->irq_pending);
+ iman = readl(&ir->ir_set->iman);
+ iman &= ~IMAN_IP;
+ iman |= IMAN_IE;
+ writel(iman, &ir->ir_set->iman);
+ /* Read operation to guarantee the write has been flushed from posted buffers */
+ readl(&ir->ir_set->iman);
return 0;
}
-static int xhci_disable_interrupter(struct xhci_interrupter *ir)
+int xhci_disable_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir)
{
u32 iman;
if (!ir || !ir->ir_set)
return -EINVAL;
- iman = readl(&ir->ir_set->irq_pending);
- writel(ER_IRQ_DISABLE(iman), &ir->ir_set->irq_pending);
+ iman = readl(&ir->ir_set->iman);
+ iman &= ~IMAN_IP;
+ iman &= ~IMAN_IE;
+ writel(iman, &ir->ir_set->iman);
+
+ iman = readl(&ir->ir_set->iman);
+ if (iman & IMAN_IP)
+ xhci_dbg(xhci, "%s: Interrupt pending\n", __func__);
return 0;
}
@@ -354,13 +352,16 @@ int xhci_set_interrupter_moderation(struct xhci_interrupter *ir,
{
u32 imod;
- if (!ir || !ir->ir_set || imod_interval > U16_MAX * 250)
+ if (!ir || !ir->ir_set)
return -EINVAL;
- imod = readl(&ir->ir_set->irq_control);
- imod &= ~ER_IRQ_INTERVAL_MASK;
- imod |= (imod_interval / 250) & ER_IRQ_INTERVAL_MASK;
- writel(imod, &ir->ir_set->irq_control);
+ /* IMODI value in IMOD register is in 250ns increments */
+ imod_interval = umin(imod_interval / 250, IMODI_MASK);
+
+ imod = readl(&ir->ir_set->imod);
+ imod &= ~IMODI_MASK;
+ imod |= imod_interval;
+ writel(imod, &ir->ir_set->imod);
return 0;
}
@@ -373,7 +374,7 @@ static void compliance_mode_recovery(struct timer_list *t)
u32 temp;
int i;
- xhci = from_timer(xhci, t, comp_mode_recovery_timer);
+ xhci = timer_container_of(xhci, t, comp_mode_recovery_timer);
rhub = &xhci->usb3_rhub;
hcd = rhub->hcd;
@@ -381,7 +382,7 @@ static void compliance_mode_recovery(struct timer_list *t)
return;
for (i = 0; i < rhub->num_ports; i++) {
- temp = readl(rhub->ports[i]->addr);
+ temp = xhci_portsc_readl(rhub->ports[i]);
if ((temp & PORT_PLS_MASK) == USB_SS_PORT_LS_COMP_MOD) {
/*
* Compliance Mode Detected. Letting USB Core
@@ -460,24 +461,115 @@ static int xhci_all_ports_seen_u0(struct xhci_hcd *xhci)
return (xhci->port_status_u0 == ((1 << xhci->usb3_rhub.num_ports) - 1));
}
+static void xhci_hcd_page_size(struct xhci_hcd *xhci)
+{
+ u32 page_size;
+
+ page_size = readl(&xhci->op_regs->page_size) & XHCI_PAGE_SIZE_MASK;
+ if (!is_power_of_2(page_size)) {
+ xhci_warn(xhci, "Invalid page size register = 0x%x\n", page_size);
+ /* Fallback to 4K page size, since that's common */
+ page_size = 1;
+ }
+
+ xhci->page_size = page_size << 12;
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init, "HCD page size set to %iK",
+ xhci->page_size >> 10);
+}
+
+static void xhci_enable_max_dev_slots(struct xhci_hcd *xhci)
+{
+ u32 config_reg;
+
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init, "xHC can handle at most %d device slots",
+ xhci->max_slots);
+
+ config_reg = readl(&xhci->op_regs->config_reg);
+ config_reg &= ~HCS_SLOTS_MASK;
+ config_reg |= xhci->max_slots;
+
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Setting Max device slots reg = 0x%x",
+ config_reg);
+ writel(config_reg, &xhci->op_regs->config_reg);
+}
+
+static void xhci_set_cmd_ring_deq(struct xhci_hcd *xhci)
+{
+ dma_addr_t deq_dma;
+ u64 crcr;
+
+ deq_dma = xhci_trb_virt_to_dma(xhci->cmd_ring->deq_seg, xhci->cmd_ring->dequeue);
+ deq_dma &= CMD_RING_PTR_MASK;
+
+ crcr = xhci_read_64(xhci, &xhci->op_regs->cmd_ring);
+ crcr &= ~CMD_RING_PTR_MASK;
+ crcr |= deq_dma;
+
+ crcr &= ~CMD_RING_CYCLE;
+ crcr |= xhci->cmd_ring->cycle_state;
+
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Setting command ring address to 0x%llx", crcr);
+ xhci_write_64(xhci, crcr, &xhci->op_regs->cmd_ring);
+}
+
+static void xhci_set_doorbell_ptr(struct xhci_hcd *xhci)
+{
+ u32 offset;
+
+ offset = readl(&xhci->cap_regs->db_off) & DBOFF_MASK;
+ xhci->dba = (void __iomem *)xhci->cap_regs + offset;
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init,
+ "Doorbell array is located at offset 0x%x from cap regs base addr", offset);
+}
/*
- * Initialize memory for HCD and xHC (one-time init).
- *
- * Program the PAGESIZE register, initialize the device context array, create
- * device contexts (?), set up a command ring segment (or two?), create event
- * ring (one for now).
+ * Enable USB 3.0 device notifications for function remote wake, which is necessary
+ * for allowing USB 3.0 devices to do remote wakeup from U3 (device suspend).
*/
-static int xhci_init(struct usb_hcd *hcd)
+static void xhci_set_dev_notifications(struct xhci_hcd *xhci)
+{
+ u32 dev_notf;
+
+ dev_notf = readl(&xhci->op_regs->dev_notification);
+ dev_notf &= ~DEV_NOTE_MASK;
+ dev_notf |= DEV_NOTE_FWAKE;
+ writel(dev_notf, &xhci->op_regs->dev_notification);
+}
+
+/* Setup basic xHCI registers */
+static void xhci_init(struct usb_hcd *hcd)
{
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
- int retval;
- xhci_dbg_trace(xhci, trace_xhci_dbg_init, "xhci_init");
- spin_lock_init(&xhci->lock);
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Starting %s", __func__);
- retval = xhci_mem_init(xhci, GFP_KERNEL);
- xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Finished xhci_init");
+ /* Set the Number of Device Slots Enabled to the maximum supported value */
+ xhci_enable_max_dev_slots(xhci);
+
+ /* Initialize the Command ring */
+ xhci_ring_init(xhci, xhci->cmd_ring);
+ /*
+ * Reserve one command ring TRB for disabling LPM.
+ * Since the USB core grabs the shared usb_bus bandwidth mutex before
+ * disabling LPM, we only need to reserve one TRB for all devices.
+ */
+ xhci->cmd_ring_reserved_trbs = 1;
+ /* Set the address in the Command Ring Control register */
+ xhci_set_cmd_ring_deq(xhci);
+
+ /* Set Device Context Base Address Array pointer */
+ xhci_write_64(xhci, xhci->dcbaa->dma, &xhci->op_regs->dcbaa_ptr);
+
+ /* Set Doorbell array pointer */
+ xhci_set_doorbell_ptr(xhci);
+
+ /* Set USB 3.0 device notifications for function remote wake */
+ xhci_set_dev_notifications(xhci);
+
+ /* Initialize the Primary interrupter */
+ xhci_ring_init(xhci, xhci->interrupters[0]->event_ring);
+ xhci_add_interrupter(xhci, 0);
+ xhci->interrupters[0]->isoc_bei_interval = AVOID_BEI_INTERVAL_MAX;
/* Initializing Compliance Mode Recovery Data If Needed */
if (xhci_compliance_mode_recovery_timer_quirk_check()) {
@@ -485,7 +577,7 @@ static int xhci_init(struct usb_hcd *hcd)
compliance_mode_recovery_timer_init(xhci);
}
- return retval;
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Finished %s", __func__);
}
/*-------------------------------------------------------------------------*/
@@ -627,7 +719,7 @@ void xhci_stop(struct usb_hcd *hcd)
/* Deleting Compliance Mode Recovery Timer */
if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) &&
(!(xhci_all_ports_seen_u0(xhci)))) {
- del_timer_sync(&xhci->comp_mode_recovery_timer);
+ timer_delete_sync(&xhci->comp_mode_recovery_timer);
xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
"%s: compliance mode recovery timer deleted",
__func__);
@@ -640,7 +732,7 @@ void xhci_stop(struct usb_hcd *hcd)
"// Disabling event ring interrupts");
temp = readl(&xhci->op_regs->status);
writel((temp & ~0x1fff) | STS_EINT, &xhci->op_regs->status);
- xhci_disable_interrupter(ir);
+ xhci_disable_interrupter(xhci, ir);
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "cleaning up memory");
xhci_mem_cleanup(xhci);
@@ -672,11 +764,11 @@ void xhci_shutdown(struct usb_hcd *hcd)
xhci_dbg(xhci, "%s: stopping usb%d port polling.\n",
__func__, hcd->self.busnum);
clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
- del_timer_sync(&hcd->rh_timer);
+ timer_delete_sync(&hcd->rh_timer);
if (xhci->shared_hcd) {
clear_bit(HCD_FLAG_POLL_RH, &xhci->shared_hcd->flags);
- del_timer_sync(&xhci->shared_hcd->rh_timer);
+ timer_delete_sync(&xhci->shared_hcd->rh_timer);
}
spin_lock_irq(&xhci->lock);
@@ -719,8 +811,8 @@ static void xhci_save_registers(struct xhci_hcd *xhci)
ir->s3_erst_size = readl(&ir->ir_set->erst_size);
ir->s3_erst_base = xhci_read_64(xhci, &ir->ir_set->erst_base);
ir->s3_erst_dequeue = xhci_read_64(xhci, &ir->ir_set->erst_dequeue);
- ir->s3_irq_pending = readl(&ir->ir_set->irq_pending);
- ir->s3_irq_control = readl(&ir->ir_set->irq_control);
+ ir->s3_iman = readl(&ir->ir_set->iman);
+ ir->s3_imod = readl(&ir->ir_set->imod);
}
}
@@ -743,28 +835,11 @@ static void xhci_restore_registers(struct xhci_hcd *xhci)
writel(ir->s3_erst_size, &ir->ir_set->erst_size);
xhci_write_64(xhci, ir->s3_erst_base, &ir->ir_set->erst_base);
xhci_write_64(xhci, ir->s3_erst_dequeue, &ir->ir_set->erst_dequeue);
- writel(ir->s3_irq_pending, &ir->ir_set->irq_pending);
- writel(ir->s3_irq_control, &ir->ir_set->irq_control);
+ writel(ir->s3_iman, &ir->ir_set->iman);
+ writel(ir->s3_imod, &ir->ir_set->imod);
}
}
-static void xhci_set_cmd_ring_deq(struct xhci_hcd *xhci)
-{
- u64 val_64;
-
- /* step 2: initialize command ring buffer */
- val_64 = xhci_read_64(xhci, &xhci->op_regs->cmd_ring);
- val_64 = (val_64 & (u64) CMD_RING_RSVD_BITS) |
- (xhci_trb_virt_to_dma(xhci->cmd_ring->deq_seg,
- xhci->cmd_ring->dequeue) &
- (u64) ~CMD_RING_RSVD_BITS) |
- xhci->cmd_ring->cycle_state;
- xhci_dbg_trace(xhci, trace_xhci_dbg_init,
- "// Setting command ring address to 0x%llx",
- (long unsigned long) val_64);
- xhci_write_64(xhci, val_64, &xhci->op_regs->cmd_ring);
-}
-
/*
* The whole command ring must be cleared to zero when we suspend the host.
*
@@ -818,7 +893,7 @@ static void xhci_disable_hub_port_wake(struct xhci_hcd *xhci,
spin_lock_irqsave(&xhci->lock, flags);
for (i = 0; i < rhub->num_ports; i++) {
- portsc = readl(rhub->ports[i]->addr);
+ portsc = xhci_portsc_readl(rhub->ports[i]);
t1 = xhci_port_state_to_neutral(portsc);
t2 = t1;
@@ -831,7 +906,7 @@ static void xhci_disable_hub_port_wake(struct xhci_hcd *xhci,
t2 |= PORT_CSC;
if (t1 != t2) {
- writel(t2, rhub->ports[i]->addr);
+ xhci_portsc_writel(rhub->ports[i], t2);
xhci_dbg(xhci, "config port %d-%d wake bits, portsc: 0x%x, write: 0x%x\n",
rhub->hcd->self.busnum, i + 1, portsc, t2);
}
@@ -858,7 +933,7 @@ static bool xhci_pending_portevent(struct xhci_hcd *xhci)
port_index = xhci->usb2_rhub.num_ports;
ports = xhci->usb2_rhub.ports;
while (port_index--) {
- portsc = readl(ports[port_index]->addr);
+ portsc = xhci_portsc_readl(ports[port_index]);
if (portsc & PORT_CHANGE_MASK ||
(portsc & PORT_PLS_MASK) == XDEV_RESUME)
return true;
@@ -866,7 +941,7 @@ static bool xhci_pending_portevent(struct xhci_hcd *xhci)
port_index = xhci->usb3_rhub.num_ports;
ports = xhci->usb3_rhub.ports;
while (port_index--) {
- portsc = readl(ports[port_index]->addr);
+ portsc = xhci_portsc_readl(ports[port_index]);
if (portsc & (PORT_CHANGE_MASK | PORT_CAS) ||
(portsc & PORT_PLS_MASK) == XDEV_RESUME)
return true;
@@ -882,11 +957,11 @@ static bool xhci_pending_portevent(struct xhci_hcd *xhci)
*/
int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup)
{
- int rc = 0;
+ int err;
unsigned int delay = XHCI_MAX_HALT_USEC * 2;
struct usb_hcd *hcd = xhci_to_hcd(xhci);
u32 command;
- u32 res;
+ u32 usbsts;
if (!hcd->state)
return 0;
@@ -908,10 +983,10 @@ int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup)
xhci_dbg(xhci, "%s: stopping usb%d port polling.\n",
__func__, hcd->self.busnum);
clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
- del_timer_sync(&hcd->rh_timer);
+ timer_delete_sync(&hcd->rh_timer);
if (xhci->shared_hcd) {
clear_bit(HCD_FLAG_POLL_RH, &xhci->shared_hcd->flags);
- del_timer_sync(&xhci->shared_hcd->rh_timer);
+ timer_delete_sync(&xhci->shared_hcd->rh_timer);
}
if (xhci->quirks & XHCI_SUSPEND_DELAY)
@@ -932,11 +1007,10 @@ int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup)
/* Some chips from Fresco Logic need an extraordinary delay */
delay *= (xhci->quirks & XHCI_SLOW_SUSPEND) ? 10 : 1;
- if (xhci_handshake(&xhci->op_regs->status,
- STS_HALT, STS_HALT, delay)) {
- xhci_warn(xhci, "WARN: xHC CMD_RUN timeout\n");
- spin_unlock_irq(&xhci->lock);
- return -ETIMEDOUT;
+ err = xhci_handshake(&xhci->op_regs->status, STS_HALT, STS_HALT, delay);
+ if (err) {
+ xhci_warn(xhci, "Clearing Run/Stop bit failed %d\n", err);
+ goto handshake_error;
}
xhci_clear_command_ring(xhci);
@@ -947,28 +1021,34 @@ int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup)
command = readl(&xhci->op_regs->command);
command |= CMD_CSS;
writel(command, &xhci->op_regs->command);
+
+ err = xhci_handshake(&xhci->op_regs->status, STS_SAVE, 0, 20 * USEC_PER_MSEC);
+ usbsts = readl(&xhci->op_regs->status);
xhci->broken_suspend = 0;
- if (xhci_handshake(&xhci->op_regs->status,
- STS_SAVE, 0, 20 * 1000)) {
- /*
- * AMD SNPS xHC 3.0 occasionally does not clear the
- * SSS bit of USBSTS and when driver tries to poll
- * to see if the xHC clears BIT(8) which never happens
- * and driver assumes that controller is not responding
- * and times out. To workaround this, its good to check
- * if SRE and HCE bits are not set (as per xhci
- * Section 5.4.2) and bypass the timeout.
- */
- res = readl(&xhci->op_regs->status);
- if ((xhci->quirks & XHCI_SNPS_BROKEN_SUSPEND) &&
- (((res & STS_SRE) == 0) &&
- ((res & STS_HCE) == 0))) {
- xhci->broken_suspend = 1;
- } else {
- xhci_warn(xhci, "WARN: xHC save state timeout\n");
- spin_unlock_irq(&xhci->lock);
- return -ETIMEDOUT;
+ if (err) {
+ /*
+ * AMD SNPS xHC 3.0 occasionally does not clear the
+ * SSS bit of USBSTS and when driver tries to poll
+ * to see if the xHC clears BIT(8) which never happens
+ * and driver assumes that controller is not responding
+ * and times out. To workaround this, its good to check
+ * if SRE and HCE bits are not set (as per xhci
+ * Section 5.4.2) and bypass the timeout.
+ */
+ if (!(xhci->quirks & XHCI_SNPS_BROKEN_SUSPEND)) {
+ xhci_warn(xhci, "Controller Save State failed %d\n", err);
+ goto handshake_error;
+ }
+
+ if (usbsts & (STS_SRE | STS_HCE)) {
+ xhci_warn(xhci, "Controller Save State failed, USBSTS 0x%08x\n", usbsts);
+ goto handshake_error;
}
+
+ xhci_dbg(xhci, "SNPS broken suspend, save state unreliable\n");
+ xhci->broken_suspend = 1;
+ } else if (usbsts & STS_SRE) {
+ xhci_warn(xhci, "Suspend Save Error (SRE), USBSTS 0x%08x\n", usbsts);
}
spin_unlock_irq(&xhci->lock);
@@ -978,13 +1058,17 @@ int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup)
*/
if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) &&
(!(xhci_all_ports_seen_u0(xhci)))) {
- del_timer_sync(&xhci->comp_mode_recovery_timer);
+ timer_delete_sync(&xhci->comp_mode_recovery_timer);
xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
"%s: compliance mode recovery timer deleted",
__func__);
}
- return rc;
+ return 0;
+
+handshake_error:
+ spin_unlock_irq(&xhci->lock);
+ return -ETIMEDOUT;
}
EXPORT_SYMBOL_GPL(xhci_suspend);
@@ -994,16 +1078,15 @@ EXPORT_SYMBOL_GPL(xhci_suspend);
* This is called when the machine transition from S3/S4 mode.
*
*/
-int xhci_resume(struct xhci_hcd *xhci, pm_message_t msg)
+int xhci_resume(struct xhci_hcd *xhci, bool power_lost, bool is_auto_resume)
{
- bool hibernated = (msg.event == PM_EVENT_RESTORE);
u32 command, temp = 0;
struct usb_hcd *hcd = xhci_to_hcd(xhci);
+ struct xhci_segment *seg;
int retval = 0;
- bool comp_timer_running = false;
bool pending_portevent = false;
bool suspended_usb3_devs = false;
- bool reinit_xhc = false;
+ bool reset_registers = false;
if (!hcd->state)
return 0;
@@ -1022,10 +1105,11 @@ int xhci_resume(struct xhci_hcd *xhci, pm_message_t msg)
spin_lock_irq(&xhci->lock);
- if (hibernated || xhci->quirks & XHCI_RESET_ON_RESUME || xhci->broken_suspend)
- reinit_xhc = true;
-
- if (!reinit_xhc) {
+ if (power_lost || xhci->broken_suspend || xhci->quirks & XHCI_RESET_ON_RESUME) {
+ xhci_dbg(xhci, "HC state lost, performing host controller reset\n");
+ reset_registers = true;
+ } else {
+ xhci_dbg(xhci, "HC state intact, continuing without reset\n");
/*
* Some controllers might lose power during suspend, so wait
* for controller not ready bit to clear, just as in xHC init.
@@ -1058,22 +1142,19 @@ int xhci_resume(struct xhci_hcd *xhci, pm_message_t msg)
spin_unlock_irq(&xhci->lock);
return -ETIMEDOUT;
}
- }
- temp = readl(&xhci->op_regs->status);
-
- /* re-initialize the HC on Restore Error, or Host Controller Error */
- if ((temp & (STS_SRE | STS_HCE)) &&
- !(xhci->xhc_state & XHCI_STATE_REMOVING)) {
- reinit_xhc = true;
- if (!xhci->broken_suspend)
+ /* re-initialize the HC on Restore Error, or Host Controller Error */
+ temp = readl(&xhci->op_regs->status);
+ if ((temp & (STS_SRE | STS_HCE)) && !(xhci->xhc_state & XHCI_STATE_REMOVING)) {
xhci_warn(xhci, "xHC error in resume, USBSTS 0x%x, Reinit\n", temp);
+ reset_registers = true;
+ }
}
- if (reinit_xhc) {
+ if (reset_registers) {
if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) &&
!(xhci_all_ports_seen_u0(xhci))) {
- del_timer_sync(&xhci->comp_mode_recovery_timer);
+ timer_delete_sync(&xhci->comp_mode_recovery_timer);
xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
"Compliance Mode Recovery Timer deleted!");
}
@@ -1086,32 +1167,41 @@ int xhci_resume(struct xhci_hcd *xhci, pm_message_t msg)
xhci_dbg(xhci, "Stop HCD\n");
xhci_halt(xhci);
xhci_zero_64b_regs(xhci);
- retval = xhci_reset(xhci, XHCI_RESET_LONG_USEC);
+ if (xhci->xhc_state & XHCI_STATE_REMOVING)
+ retval = -ENODEV;
+ else
+ retval = xhci_reset(xhci, XHCI_RESET_LONG_USEC);
spin_unlock_irq(&xhci->lock);
if (retval)
return retval;
- xhci_dbg(xhci, "// Disabling event ring interrupts\n");
- temp = readl(&xhci->op_regs->status);
- writel((temp & ~0x1fff) | STS_EINT, &xhci->op_regs->status);
- xhci_disable_interrupter(xhci->interrupters[0]);
+ cancel_delayed_work_sync(&xhci->cmd_timer);
+
+ /* Delete all remaining commands */
+ xhci_cleanup_command_queue(xhci);
+
+ /* Clear data which is re-initilized during runtime */
+ xhci_for_each_ring_seg(xhci->interrupters[0]->event_ring->first_seg, seg)
+ memset(seg->trbs, 0, sizeof(union xhci_trb) * TRBS_PER_SEGMENT);
+
+ for (int i = xhci->max_slots; i > 0; i--)
+ xhci_free_virt_devices_depth_first(xhci, i);
+
+ xhci_rh_bw_cleanup(xhci);
+
+ xhci->cmd_ring_reserved_trbs = 0;
+ xhci_for_each_ring_seg(xhci->cmd_ring->first_seg, seg)
+ memset(seg->trbs, 0, sizeof(union xhci_trb) * TRBS_PER_SEGMENT);
- xhci_dbg(xhci, "cleaning up memory\n");
- xhci_mem_cleanup(xhci);
xhci_debugfs_exit(xhci);
- xhci_dbg(xhci, "xhci_stop completed - status = %x\n",
- readl(&xhci->op_regs->status));
- /* USB core calls the PCI reinit and start functions twice:
+ xhci_init(hcd);
+
+ /*
+ * USB core calls the PCI reinit and start functions twice:
* first with the primary HCD, and then with the secondary HCD.
* If we don't do the same, the host will never be started.
*/
- xhci_dbg(xhci, "Initialize the xhci_hcd\n");
- retval = xhci_init(hcd);
- if (retval)
- return retval;
- comp_timer_running = true;
-
xhci_dbg(xhci, "Start the primary HCD\n");
retval = xhci_run(hcd);
if (!retval && xhci->shared_hcd) {
@@ -1155,41 +1245,36 @@ int xhci_resume(struct xhci_hcd *xhci, pm_message_t msg)
xhci_dbc_resume(xhci);
- if (retval == 0) {
- /*
- * Resume roothubs only if there are pending events.
- * USB 3 devices resend U3 LFPS wake after a 100ms delay if
- * the first wake signalling failed, give it that chance if
- * there are suspended USB 3 devices.
- */
- if (xhci->usb3_rhub.bus_state.suspended_ports ||
- xhci->usb3_rhub.bus_state.bus_suspended)
- suspended_usb3_devs = true;
+ /*
+ * Resume roothubs only if there are pending events.
+ * USB 3 devices resend U3 LFPS wake after a 100ms delay if
+ * the first wake signalling failed, give it that chance if
+ * there are suspended USB 3 devices.
+ */
+ if (xhci->usb3_rhub.bus_state.suspended_ports || xhci->usb3_rhub.bus_state.bus_suspended)
+ suspended_usb3_devs = true;
+ pending_portevent = xhci_pending_portevent(xhci);
+ if (suspended_usb3_devs && !pending_portevent && is_auto_resume) {
+ msleep(120);
pending_portevent = xhci_pending_portevent(xhci);
+ }
- if (suspended_usb3_devs && !pending_portevent &&
- msg.event == PM_EVENT_AUTO_RESUME) {
- msleep(120);
- pending_portevent = xhci_pending_portevent(xhci);
- }
-
- if (pending_portevent) {
- if (xhci->shared_hcd)
- usb_hcd_resume_root_hub(xhci->shared_hcd);
- usb_hcd_resume_root_hub(hcd);
- }
+ if (pending_portevent) {
+ if (xhci->shared_hcd)
+ usb_hcd_resume_root_hub(xhci->shared_hcd);
+ usb_hcd_resume_root_hub(hcd);
}
-done:
+
/*
* If system is subject to the Quirk, Compliance Mode Timer needs to
* be re-initialized Always after a system resume. Ports are subject
* to suffer the Compliance Mode issue again. It doesn't matter if
* ports have entered previously to U0 before system's suspension.
*/
- if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) && !comp_timer_running)
+ if (xhci->quirks & XHCI_COMP_MODE_QUIRK)
compliance_mode_recovery_timer_init(xhci);
-
+done:
if (xhci->quirks & XHCI_ASMEDIA_MODIFY_FLOWCONTROL)
usb_asmedia_modifyflowcontrol(to_pci_dev(hcd->self.controller));
@@ -1258,7 +1343,7 @@ static bool xhci_urb_temp_buffer_required(struct usb_hcd *hcd,
struct scatterlist *tail_sg;
tail_sg = urb->sg;
- max_pkt = usb_endpoint_maxp(&urb->ep->desc);
+ max_pkt = xhci_usb_endpoint_maxp(urb->dev, urb->ep);
if (!urb->num_sgs)
return ret;
@@ -1362,6 +1447,7 @@ static void xhci_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb)
* xhci_get_endpoint_index - Used for passing endpoint bitmasks between the core and
* HCDs. Find the index for an endpoint given its descriptor. Use the return
* value to right shift 1 for the bitmask.
+ * @desc: USB endpoint descriptor to determine index for
*
* Index = (epnum * 2) + direction - 1,
* where direction = 0 for OUT, 1 for IN.
@@ -1555,7 +1641,7 @@ static int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag
else
num_tds = 1;
- urb_priv = kzalloc(struct_size(urb_priv, td, num_tds), mem_flags);
+ urb_priv = kzalloc_flex(*urb_priv, td, num_tds, mem_flags);
if (!urb_priv)
return -ENOMEM;
@@ -2812,16 +2898,25 @@ int xhci_stop_endpoint_sync(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, int
gfp_t gfp_flags)
{
struct xhci_command *command;
+ struct xhci_ep_ctx *ep_ctx;
unsigned long flags;
- int ret;
+ int ret = -ENODEV;
command = xhci_alloc_command(xhci, true, gfp_flags);
if (!command)
return -ENOMEM;
spin_lock_irqsave(&xhci->lock, flags);
- ret = xhci_queue_stop_endpoint(xhci, command, ep->vdev->slot_id,
- ep->ep_index, suspend);
+
+ /* make sure endpoint exists and is running before stopping it */
+ if (ep->ring) {
+ ep_ctx = xhci_get_ep_ctx(xhci, ep->vdev->out_ctx, ep->ep_index);
+ if (GET_EP_CTX_STATE(ep_ctx) == EP_STATE_RUNNING)
+ ret = xhci_queue_stop_endpoint(xhci, command,
+ ep->vdev->slot_id,
+ ep->ep_index, suspend);
+ }
+
if (ret < 0) {
spin_unlock_irqrestore(&xhci->lock, flags);
goto out;
@@ -2845,6 +2940,20 @@ out:
}
EXPORT_SYMBOL_GPL(xhci_stop_endpoint_sync);
+/*
+ * xhci_usb_endpoint_maxp - get endpoint max packet size
+ * @host_ep: USB host endpoint to be checked
+ *
+ * Returns max packet from the correct descriptor
+ */
+int xhci_usb_endpoint_maxp(struct usb_device *udev,
+ struct usb_host_endpoint *host_ep)
+{
+ if (usb_endpoint_is_hs_isoc_double(udev, host_ep))
+ return le16_to_cpu(host_ep->eusb2_isoc_ep_comp.wMaxPacketSize);
+ return usb_endpoint_maxp(&host_ep->desc);
+}
+
/* Issue a configure endpoint command or evaluate context command
* and wait for it to finish.
*/
@@ -3092,6 +3201,49 @@ void xhci_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *udev)
}
EXPORT_SYMBOL_GPL(xhci_reset_bandwidth);
+/*
+ * Get the available bandwidth of the ports under the xhci roothub.
+ * EIO means the command failed: command not implemented or unsupported
+ * speed (TRB Error), some ASMedia complete with Parameter Error when
+ * querying the root hub (slot_id = 0), or other error or timeout.
+ */
+int xhci_get_port_bandwidth(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx,
+ u8 dev_speed)
+{
+ struct xhci_command *cmd;
+ unsigned long flags;
+ int ret;
+
+ if (!ctx || !xhci)
+ return -EINVAL;
+
+ cmd = xhci_alloc_command(xhci, true, GFP_KERNEL);
+ if (!cmd)
+ return -ENOMEM;
+
+ cmd->in_ctx = ctx;
+
+ /* get xhci port bandwidth, refer to xhci rev1_2 protocol 4.6.15 */
+ spin_lock_irqsave(&xhci->lock, flags);
+
+ ret = xhci_queue_get_port_bw(xhci, cmd, ctx->dma, dev_speed, 0);
+ if (ret) {
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ goto err_out;
+ }
+ xhci_ring_cmd_db(xhci);
+ spin_unlock_irqrestore(&xhci->lock, flags);
+
+ wait_for_completion(cmd->completion);
+ if (cmd->status != COMP_SUCCESS)
+ ret = -EIO;
+err_out:
+ kfree(cmd->completion);
+ kfree(cmd);
+
+ return ret;
+}
+
static void xhci_setup_input_ctx_for_config_ep(struct xhci_hcd *xhci,
struct xhci_container_ctx *in_ctx,
struct xhci_container_ctx *out_ctx,
@@ -3140,7 +3292,6 @@ rescan:
xhci_dbg(xhci, "endpoint disable with ep_state 0x%x\n",
ep->ep_state);
done:
- host_ep->hcpriv = NULL;
spin_unlock_irqrestore(&xhci->lock, flags);
}
@@ -3819,8 +3970,7 @@ static int xhci_discover_or_reset_device(struct usb_hcd *hcd,
* Obtaining a new device slot to inform the xHCI host that
* the USB device has been reset.
*/
- ret = xhci_disable_slot(xhci, udev->slot_id);
- xhci_free_virt_device(xhci, udev->slot_id);
+ ret = xhci_disable_and_free_slot(xhci, udev->slot_id);
if (!ret) {
ret = xhci_alloc_dev(hcd, udev);
if (ret == 1)
@@ -3879,6 +4029,7 @@ static int xhci_discover_or_reset_device(struct usb_hcd *hcd,
xhci_get_slot_state(xhci, virt_dev->out_ctx));
xhci_dbg(xhci, "Not freeing device rings.\n");
/* Don't treat this as an error. May change my mind later. */
+ virt_dev->flags = 0;
ret = 0;
goto command_cleanup;
case COMP_SUCCESS:
@@ -3914,6 +4065,8 @@ static int xhci_discover_or_reset_device(struct usb_hcd *hcd,
}
if (ep->ring) {
+ if (ep->sideband)
+ xhci_sideband_notify_ep_ring_free(ep->sideband, i);
xhci_debugfs_remove_endpoint(xhci, virt_dev, i);
xhci_free_endpoint_ring(xhci, virt_dev, i);
}
@@ -3975,7 +4128,7 @@ static void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev)
xhci_disable_slot(xhci, udev->slot_id);
spin_lock_irqsave(&xhci->lock, flags);
- xhci_free_virt_device(xhci, udev->slot_id);
+ xhci_free_virt_device(xhci, virt_dev, udev->slot_id);
spin_unlock_irqrestore(&xhci->lock, flags);
}
@@ -3999,7 +4152,7 @@ int xhci_disable_slot(struct xhci_hcd *xhci, u32 slot_id)
if (state == 0xffffffff || (xhci->xhc_state & XHCI_STATE_DYING) ||
(xhci->xhc_state & XHCI_STATE_HALTED)) {
spin_unlock_irqrestore(&xhci->lock, flags);
- kfree(command);
+ xhci_free_command(xhci, command);
return -ENODEV;
}
@@ -4007,7 +4160,7 @@ int xhci_disable_slot(struct xhci_hcd *xhci, u32 slot_id)
slot_id);
if (ret) {
spin_unlock_irqrestore(&xhci->lock, flags);
- kfree(command);
+ xhci_free_command(xhci, command);
return ret;
}
xhci_ring_cmd_db(xhci);
@@ -4024,6 +4177,16 @@ int xhci_disable_slot(struct xhci_hcd *xhci, u32 slot_id)
return 0;
}
+int xhci_disable_and_free_slot(struct xhci_hcd *xhci, u32 slot_id)
+{
+ struct xhci_virt_device *vdev = xhci->devs[slot_id];
+ int ret;
+
+ ret = xhci_disable_slot(xhci, slot_id);
+ xhci_free_virt_device(xhci, vdev, slot_id);
+ return ret;
+}
+
/*
* Checks if we have enough host controller resources for the default control
* endpoint.
@@ -4082,8 +4245,7 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev)
xhci_err(xhci, "Error while assigning device slot ID: %s\n",
xhci_trb_comp_code_string(command->status));
xhci_err(xhci, "Max number of devices this xHCI host supports is %u.\n",
- HCS_MAX_SLOTS(
- readl(&xhci->cap_regs->hcs_params1)));
+ xhci->max_slots);
xhci_free_command(xhci, command);
return 0;
}
@@ -4130,8 +4292,7 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev)
return 1;
disable_slot:
- xhci_disable_slot(xhci, udev->slot_id);
- xhci_free_virt_device(xhci, udev->slot_id);
+ xhci_disable_and_free_slot(xhci, udev->slot_id);
return 0;
}
@@ -4227,8 +4388,7 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
ctrl_ctx->add_flags = cpu_to_le32(SLOT_FLAG | EP0_FLAG);
ctrl_ctx->drop_flags = 0;
- trace_xhci_address_ctx(xhci, virt_dev->in_ctx,
- le32_to_cpu(slot_ctx->dev_info) >> 27);
+ trace_xhci_address_ctx(xhci, virt_dev->in_ctx);
trace_xhci_address_ctrl_ctx(ctrl_ctx);
spin_lock_irqsave(&xhci->lock, flags);
@@ -4267,8 +4427,7 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
dev_warn(&udev->dev, "Device not responding to setup %s.\n", act);
mutex_unlock(&xhci->mutex);
- ret = xhci_disable_slot(xhci, udev->slot_id);
- xhci_free_virt_device(xhci, udev->slot_id);
+ ret = xhci_disable_and_free_slot(xhci, udev->slot_id);
if (!ret) {
if (xhci_alloc_dev(hcd, udev) == 1)
xhci_setup_addressable_virt_dev(xhci, udev);
@@ -4289,7 +4448,7 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
xhci_err(xhci,
"ERROR: unexpected setup %s command completion code 0x%x.\n",
act, command->status);
- trace_xhci_address_ctx(xhci, virt_dev->out_ctx, 1);
+ trace_xhci_address_ctx(xhci, virt_dev->out_ctx);
ret = -EINVAL;
break;
}
@@ -4307,14 +4466,12 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
xhci_dbg_trace(xhci, trace_xhci_dbg_address,
"Output Context DMA address = %#08llx",
(unsigned long long)virt_dev->out_ctx->dma);
- trace_xhci_address_ctx(xhci, virt_dev->in_ctx,
- le32_to_cpu(slot_ctx->dev_info) >> 27);
+ trace_xhci_address_ctx(xhci, virt_dev->in_ctx);
/*
* USB core uses address 1 for the roothubs, so we add one to the
* address given back to us by the HC.
*/
- trace_xhci_address_ctx(xhci, virt_dev->out_ctx,
- le32_to_cpu(slot_ctx->dev_info) >> 27);
+ trace_xhci_address_ctx(xhci, virt_dev->out_ctx);
/* Zero the input context control for later use */
ctrl_ctx->add_flags = 0;
ctrl_ctx->drop_flags = 0;
@@ -4498,7 +4655,7 @@ static int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd,
{
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
struct xhci_port **ports;
- __le32 __iomem *pm_addr, *hlpm_addr;
+ struct xhci_port_regs __iomem *port_reg;
u32 pm_val, hlpm_val, field;
unsigned int port_num;
unsigned long flags;
@@ -4523,9 +4680,8 @@ static int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd,
ports = xhci->usb2_rhub.ports;
port_num = udev->portnum - 1;
- pm_addr = ports[port_num]->addr + PORTPMSC;
- pm_val = readl(pm_addr);
- hlpm_addr = ports[port_num]->addr + PORTHLPMC;
+ port_reg = ports[port_num]->port_reg;
+ pm_val = readl(&port_reg->portpmsc);
xhci_dbg(xhci, "%s port %d USB2 hardware LPM\n",
str_enable_disable(enable), port_num + 1);
@@ -4554,30 +4710,30 @@ static int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd,
spin_lock_irqsave(&xhci->lock, flags);
hlpm_val = xhci_calculate_usb2_hw_lpm_params(udev);
- writel(hlpm_val, hlpm_addr);
+ writel(hlpm_val, &port_reg->porthlmpc);
/* flush write */
- readl(hlpm_addr);
+ readl(&port_reg->porthlmpc);
} else {
hird = xhci_calculate_hird_besl(xhci, udev);
}
pm_val &= ~PORT_HIRD_MASK;
pm_val |= PORT_HIRD(hird) | PORT_RWE | PORT_L1DS(udev->slot_id);
- writel(pm_val, pm_addr);
- pm_val = readl(pm_addr);
+ writel(pm_val, &port_reg->portpmsc);
+ pm_val = readl(&port_reg->portpmsc);
pm_val |= PORT_HLE;
- writel(pm_val, pm_addr);
+ writel(pm_val, &port_reg->portpmsc);
/* flush write */
- readl(pm_addr);
+ readl(&port_reg->portpmsc);
} else {
pm_val &= ~(PORT_HLE | PORT_RWE | PORT_HIRD_MASK | PORT_L1DS_MASK);
- writel(pm_val, pm_addr);
+ writel(pm_val, &port_reg->portpmsc);
/* flush write */
- readl(pm_addr);
+ readl(&port_reg->portpmsc);
if (udev->usb2_hw_lpm_besl_capable) {
spin_unlock_irqrestore(&xhci->lock, flags);
xhci_change_max_exit_latency(xhci, udev, 0);
- readl_poll_timeout(ports[port_num]->addr, pm_val,
+ readl_poll_timeout(&ports[port_num]->port_reg->portsc, pm_val,
(pm_val & PORT_PLS_MASK) == XDEV_U0,
100, 10000);
return 0;
@@ -4759,8 +4915,8 @@ static u16 xhci_calculate_u1_timeout(struct xhci_hcd *xhci,
*/
if (timeout_ns <= USB3_LPM_U1_MAX_TIMEOUT)
return timeout_ns;
- dev_dbg(&udev->dev, "Hub-initiated U1 disabled "
- "due to long timeout %llu ms\n", timeout_ns);
+ dev_dbg(&udev->dev, "Hub-initiated U1 disabled due to long timeout %lluus\n",
+ timeout_ns);
return xhci_get_timeout_no_hub_lpm(udev, USB3_LPM_U1);
}
@@ -4817,8 +4973,8 @@ static u16 xhci_calculate_u2_timeout(struct xhci_hcd *xhci,
*/
if (timeout_ns <= USB3_LPM_U2_MAX_TIMEOUT)
return timeout_ns;
- dev_dbg(&udev->dev, "Hub-initiated U2 disabled "
- "due to long timeout %llu ms\n", timeout_ns);
+ dev_dbg(&udev->dev, "Hub-initiated U2 disabled due to long timeout %lluus\n",
+ timeout_ns * 256);
return xhci_get_timeout_no_hub_lpm(udev, USB3_LPM_U2);
}
@@ -5270,6 +5426,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
*/
struct device *dev = hcd->self.sysdev;
int retval;
+ u32 hcs_params1;
/* Accept arbitrarily long scatter-gather lists */
hcd->self.sg_tablesize = ~0;
@@ -5295,7 +5452,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
xhci->run_regs = hcd->regs +
(readl(&xhci->cap_regs->run_regs_off) & RTSOFF_MASK);
/* Cache read-only capability registers */
- xhci->hcs_params1 = readl(&xhci->cap_regs->hcs_params1);
+ hcs_params1 = readl(&xhci->cap_regs->hcs_params1);
xhci->hcs_params2 = readl(&xhci->cap_regs->hcs_params2);
xhci->hcs_params3 = readl(&xhci->cap_regs->hcs_params3);
xhci->hci_version = HC_VERSION(readl(&xhci->cap_regs->hc_capbase));
@@ -5303,10 +5460,13 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
if (xhci->hci_version > 0x100)
xhci->hcc_params2 = readl(&xhci->cap_regs->hcc_params2);
+ xhci->max_slots = HCS_MAX_SLOTS(hcs_params1);
+ xhci->max_ports = min(HCS_MAX_PORTS(hcs_params1), MAX_HC_PORTS);
/* xhci-plat or xhci-pci might have set max_interrupters already */
- if ((!xhci->max_interrupters) ||
- xhci->max_interrupters > HCS_MAX_INTRS(xhci->hcs_params1))
- xhci->max_interrupters = HCS_MAX_INTRS(xhci->hcs_params1);
+ if (!xhci->max_interrupters)
+ xhci->max_interrupters = min(HCS_MAX_INTRS(hcs_params1), MAX_HC_INTRS);
+ else if (xhci->max_interrupters > HCS_MAX_INTRS(hcs_params1))
+ xhci->max_interrupters = HCS_MAX_INTRS(hcs_params1);
xhci->quirks |= quirks;
@@ -5351,7 +5511,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
/* Set dma_mask and coherent_dma_mask to 64-bits,
* if xHC supports 64-bit addressing */
- if (HCC_64BIT_ADDR(xhci->hcc_params) &&
+ if ((xhci->hcc_params & HCC_64BIT_ADDR) &&
!dma_set_mask(dev, DMA_BIT_MASK(64))) {
xhci_dbg(xhci, "Enabling 64-bit DMA addresses.\n");
dma_set_coherent_mask(dev, DMA_BIT_MASK(64));
@@ -5367,12 +5527,21 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
}
- xhci_dbg(xhci, "Calling HCD init\n");
- /* Initialize HCD and host controller data structures. */
- retval = xhci_init(hcd);
+ spin_lock_init(&xhci->lock);
+ INIT_LIST_HEAD(&xhci->cmd_list);
+ INIT_DELAYED_WORK(&xhci->cmd_timer, xhci_handle_command_timeout);
+ init_completion(&xhci->cmd_ring_stop_completion);
+ xhci_hcd_page_size(xhci);
+
+ memset(xhci->devs, 0, MAX_HC_SLOTS * sizeof(*xhci->devs));
+
+ /* Allocate xHCI data structures */
+ retval = xhci_mem_init(xhci, GFP_KERNEL);
if (retval)
return retval;
- xhci_dbg(xhci, "Called HCD init\n");
+
+ /* Initialize HCD and host controller data structures */
+ xhci_init(hcd);
if (xhci_hcd_is_usb3(hcd))
xhci_hcd_init_usb3_data(xhci, hcd);
@@ -5525,8 +5694,8 @@ static int __init xhci_hcd_init(void)
BUILD_BUG_ON(sizeof(struct xhci_erst_entry) != 4*32/8);
BUILD_BUG_ON(sizeof(struct xhci_cap_regs) != 8*32/8);
BUILD_BUG_ON(sizeof(struct xhci_intr_reg) != 8*32/8);
- /* xhci_run_regs has eight fields and embeds 128 xhci_intr_regs */
- BUILD_BUG_ON(sizeof(struct xhci_run_regs) != (8+8*128)*32/8);
+ /* xhci_run_regs has eight fields and embeds 1024 xhci_intr_regs */
+ BUILD_BUG_ON(sizeof(struct xhci_run_regs) != (8+8*1024)*32/8);
if (usb_disabled())
return -ENODEV;
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 779b01dee068..aeecd301f207 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -12,6 +12,7 @@
#ifndef __LINUX_XHCI_HCD_H
#define __LINUX_XHCI_HCD_H
+#include <linux/bits.h>
#include <linux/usb.h>
#include <linux/timer.h>
#include <linux/kernel.h>
@@ -34,8 +35,16 @@
/* Max number of USB devices for any host controller - limit in section 6.1 */
#define MAX_HC_SLOTS 256
-/* Section 5.3.3 - MaxPorts */
+/*
+ * Max Number of Ports. xHCI specification section 5.3.3
+ * Valid values are in the range of 1 to 255.
+ */
#define MAX_HC_PORTS 127
+/*
+ * Max number of Interrupter Register Sets. xHCI specification section 5.3.3
+ * Valid values are in the range of 1 to 1024.
+ */
+#define MAX_HC_INTRS 128
/*
* xHCI register interface.
@@ -66,13 +75,19 @@ struct xhci_cap_regs {
/* Reserved up to (CAPLENGTH - 0x1C) */
};
-/* Number of registers per port */
-#define NUM_PORT_REGS 4
-
-#define PORTSC 0
-#define PORTPMSC 1
-#define PORTLI 2
-#define PORTHLPMC 3
+/*
+ * struct xhci_port_regs - Host Controller USB Port Register Set. xHCI spec 5.4.8
+ * @portsc: Port Status and Control
+ * @portpmsc: Port Power Management Status and Control
+ * @portli: Port Link Info
+ * @porthlmpc: Port Hardware LPM Control
+ */
+struct xhci_port_regs {
+ __le32 portsc;
+ __le32 portpmsc;
+ __le32 portli;
+ __le32 porthlmpc;
+};
/**
* struct xhci_op_regs - xHCI Host Controller Operational Registers.
@@ -85,16 +100,7 @@ struct xhci_cap_regs {
* @cmd_ring: CRP - 64-bit Command Ring Pointer
* @dcbaa_ptr: DCBAAP - 64-bit Device Context Base Address Array Pointer
* @config_reg: CONFIG - Configure Register
- * @port_status_base: PORTSCn - base address for Port Status and Control
- * Each port has a Port Status and Control register,
- * followed by a Port Power Management Status and Control
- * register, a Port Link Info register, and a reserved
- * register.
- * @port_power_base: PORTPMSCn - base address for
- * Port Power Management Status and Control
- * @port_link_base: PORTLIn - base address for Port Link Info (current
- * Link PM state and control) for USB 2.1 and USB 3.0
- * devices.
+ * @port_regs: Port Register Sets, from 1 to MaxPorts (defined by HCSPARAMS1).
*/
struct xhci_op_regs {
__le32 command;
@@ -110,13 +116,7 @@ struct xhci_op_regs {
__le32 config_reg;
/* rsvd: offset 0x3C-3FF */
__le32 reserved4[241];
- /* port 1 registers, which serve as a base address for other ports */
- __le32 port_status_base;
- __le32 port_power_base;
- __le32 port_link_base;
- __le32 reserved5;
- /* registers for ports 2-255 */
- __le32 reserved6[NUM_PORT_REGS*254];
+ struct xhci_port_regs port_regs[];
};
/* USBCMD - USB command - command bitmasks */
@@ -126,17 +126,17 @@ struct xhci_op_regs {
* PCI config regs). HC does NOT drive a USB reset on the downstream ports.
* The xHCI driver must reinitialize the xHC after setting this bit.
*/
-#define CMD_RESET (1 << 1)
+#define CMD_RESET BIT(1)
/* Event Interrupt Enable - a '1' allows interrupts from the host controller */
#define CMD_EIE XHCI_CMD_EIE
/* Host System Error Interrupt Enable - get out-of-band signal for HC errors */
#define CMD_HSEIE XHCI_CMD_HSEIE
/* bits 4:6 are reserved (and should be preserved on writes). */
/* light reset (port status stays unchanged) - reset completed when this is 0 */
-#define CMD_LRESET (1 << 7)
+#define CMD_LRESET BIT(7)
/* host controller save/restore state. */
-#define CMD_CSS (1 << 8)
-#define CMD_CRS (1 << 9)
+#define CMD_CSS BIT(8)
+#define CMD_CRS BIT(9)
/* Enable Wrap Event - '1' means xHC generates an event when MFINDEX wraps. */
#define CMD_EWE XHCI_CMD_EWE
/* MFINDEX power management - '1' means xHC can stop MFINDEX counter if all root
@@ -144,38 +144,34 @@ struct xhci_op_regs {
* '0' means the xHC can power it off if all ports are in the disconnect,
* disabled, or powered-off state.
*/
-#define CMD_PM_INDEX (1 << 11)
+#define CMD_PM_INDEX BIT(11)
/* bit 14 Extended TBC Enable, changes Isoc TRB fields to support larger TBC */
-#define CMD_ETE (1 << 14)
+#define CMD_ETE BIT(14)
/* bits 15:31 are reserved (and should be preserved on writes). */
#define XHCI_RESET_LONG_USEC (10 * 1000 * 1000)
#define XHCI_RESET_SHORT_USEC (250 * 1000)
-/* IMAN - Interrupt Management Register */
-#define IMAN_IE (1 << 1)
-#define IMAN_IP (1 << 0)
-
/* USBSTS - USB status - status bitmasks */
/* HC not running - set to 1 when run/stop bit is cleared. */
#define STS_HALT XHCI_STS_HALT
/* serious error, e.g. PCI parity error. The HC will clear the run/stop bit. */
-#define STS_FATAL (1 << 2)
+#define STS_FATAL BIT(2)
/* event interrupt - clear this prior to clearing any IP flags in IR set*/
-#define STS_EINT (1 << 3)
+#define STS_EINT BIT(3)
/* port change detect */
-#define STS_PORT (1 << 4)
+#define STS_PORT BIT(4)
/* bits 5:7 reserved and zeroed */
/* save state status - '1' means xHC is saving state */
-#define STS_SAVE (1 << 8)
+#define STS_SAVE BIT(8)
/* restore state status - '1' means xHC is restoring state */
-#define STS_RESTORE (1 << 9)
+#define STS_RESTORE BIT(9)
/* true: save or restore error */
-#define STS_SRE (1 << 10)
+#define STS_SRE BIT(10)
/* true: Controller Not Ready to accept doorbell or op reg writes after reset */
#define STS_CNR XHCI_STS_CNR
/* true: internal Host Controller Error - SW needs to reset and reinitialize */
-#define STS_HCE (1 << 12)
+#define STS_HCE BIT(12)
/* bits 13:31 reserved and should be preserved */
/*
@@ -184,42 +180,43 @@ struct xhci_op_regs {
* notification type that matches a bit set in this bit field.
*/
#define DEV_NOTE_MASK (0xffff)
-#define ENABLE_DEV_NOTE(x) (1 << (x))
/* Most of the device notification types should only be used for debug.
* SW does need to pay attention to function wake notifications.
*/
-#define DEV_NOTE_FWAKE ENABLE_DEV_NOTE(1)
+#define DEV_NOTE_FWAKE BIT(1)
/* CRCR - Command Ring Control Register - cmd_ring bitmasks */
-/* bit 0 is the command ring cycle state */
+/* bit 0 - Cycle bit indicates the ownership of the command ring */
+#define CMD_RING_CYCLE BIT(0)
/* stop ring operation after completion of the currently executing command */
-#define CMD_RING_PAUSE (1 << 1)
+#define CMD_RING_PAUSE BIT(1)
/* stop ring immediately - abort the currently executing command */
-#define CMD_RING_ABORT (1 << 2)
+#define CMD_RING_ABORT BIT(2)
/* true: command ring is running */
-#define CMD_RING_RUNNING (1 << 3)
-/* bits 4:5 reserved and should be preserved */
-/* Command Ring pointer - bit mask for the lower 32 bits. */
-#define CMD_RING_RSVD_BITS (0x3f)
+#define CMD_RING_RUNNING BIT(3)
+/* bits 63:6 - Command Ring pointer */
+#define CMD_RING_PTR_MASK GENMASK_ULL(63, 6)
/* CONFIG - Configure Register - config_reg bitmasks */
/* bits 0:7 - maximum number of device slots enabled (NumSlotsEn) */
#define MAX_DEVS(p) ((p) & 0xff)
/* bit 8: U3 Entry Enabled, assert PLC when root port enters U3, xhci 1.1 */
-#define CONFIG_U3E (1 << 8)
+#define CONFIG_U3E BIT(8)
/* bit 9: Configuration Information Enable, xhci 1.1 */
-#define CONFIG_CIE (1 << 9)
+#define CONFIG_CIE BIT(9)
/* bits 10:31 - reserved and should be preserved */
+/* bits 15:0 - HCD page shift bit */
+#define XHCI_PAGE_SIZE_MASK 0xffff
+
/**
- * struct xhci_intr_reg - Interrupt Register Set
- * @irq_pending: IMAN - Interrupt Management Register. Used to enable
+ * struct xhci_intr_reg - Interrupt Register Set, v1.2 section 5.5.2.
+ * @iman: IMAN - Interrupt Management Register. Used to enable
* interrupts and check for pending interrupts.
- * @irq_control: IMOD - Interrupt Moderation Register.
- * Used to throttle interrupts.
- * @erst_size: Number of segments in the Event Ring Segment Table (ERST).
- * @erst_base: ERST base address.
- * @erst_dequeue: Event ring dequeue pointer.
+ * @imod: IMOD - Interrupt Moderation Register. Used to throttle interrupts.
+ * @erst_size: ERSTSZ - Number of segments in the Event Ring Segment Table (ERST).
+ * @erst_base: ERSTBA - Event ring segment table base address.
+ * @erst_dequeue: ERDP - Event ring dequeue pointer.
*
* Each interrupter (defined by a MSI-X vector) has an event ring and an Event
* Ring Segment Table (ERST) associated with it. The event ring is comprised of
@@ -229,48 +226,51 @@ struct xhci_op_regs {
* updates the dequeue pointer.
*/
struct xhci_intr_reg {
- __le32 irq_pending;
- __le32 irq_control;
+ __le32 iman;
+ __le32 imod;
__le32 erst_size;
__le32 rsvd;
__le64 erst_base;
__le64 erst_dequeue;
};
-/* irq_pending bitmasks */
-#define ER_IRQ_PENDING(p) ((p) & 0x1)
-/* bits 2:31 need to be preserved */
-/* THIS IS BUGGY - FIXME - IP IS WRITE 1 TO CLEAR */
-#define ER_IRQ_CLEAR(p) ((p) & 0xfffffffe)
-#define ER_IRQ_ENABLE(p) ((ER_IRQ_CLEAR(p)) | 0x2)
-#define ER_IRQ_DISABLE(p) ((ER_IRQ_CLEAR(p)) & ~(0x2))
-
-/* irq_control bitmasks */
-/* Minimum interval between interrupts (in 250ns intervals). The interval
- * between interrupts will be longer if there are no events on the event ring.
- * Default is 4000 (1 ms).
+/* iman bitmasks */
+/* bit 0 - Interrupt Pending (IP), whether there is an interrupt pending. Write-1-to-clear. */
+#define IMAN_IP BIT(0)
+/* bit 1 - Interrupt Enable (IE), whether the interrupter is capable of generating an interrupt */
+#define IMAN_IE BIT(1)
+
+/* imod bitmasks */
+/*
+ * bits 15:0 - Interrupt Moderation Interval, the minimum interval between interrupts
+ * (in 250ns intervals). The interval between interrupts will be longer if there are no
+ * events on the event ring. Default is 4000 (1 ms).
*/
-#define ER_IRQ_INTERVAL_MASK (0xffff)
-/* Counter used to count down the time to the next interrupt - HW use only */
-#define ER_IRQ_COUNTER_MASK (0xffff << 16)
+#define IMODI_MASK (0xffff)
+/* bits 31:16 - Interrupt Moderation Counter, used to count down the time to the next interrupt */
+#define IMODC_MASK (0xffff << 16)
/* erst_size bitmasks */
-/* Preserve bits 16:31 of erst_size */
-#define ERST_SIZE_MASK (0xffff << 16)
+/* bits 15:0 - Event Ring Segment Table Size, number of ERST entries */
+#define ERST_SIZE_MASK (0xffff)
/* erst_base bitmasks */
-#define ERST_BASE_RSVDP (GENMASK_ULL(5, 0))
+/* bits 63:6 - Event Ring Segment Table Base Address Register */
+#define ERST_BASE_ADDRESS_MASK GENMASK_ULL(63, 6)
/* erst_dequeue bitmasks */
-/* Dequeue ERST Segment Index (DESI) - Segment number (or alias)
- * where the current dequeue pointer lies. This is an optional HW hint.
+/*
+ * bits 2:0 - Dequeue ERST Segment Index (DESI), is the segment number (or alias) where the
+ * current dequeue pointer lies. This is an optional HW hint.
*/
#define ERST_DESI_MASK (0x7)
-/* Event Handler Busy (EHB) - is the event ring scheduled to be serviced by
+/*
+ * bit 3 - Event Handler Busy (EHB), whether the event ring is scheduled to be serviced by
* a work queue (or delayed service routine)?
*/
-#define ERST_EHB (1 << 3)
-#define ERST_PTR_MASK (GENMASK_ULL(63, 4))
+#define ERST_EHB BIT(3)
+/* bits 63:4 - Event Ring Dequeue Pointer */
+#define ERST_PTR_MASK GENMASK_ULL(63, 4)
/**
* struct xhci_run_regs
@@ -284,7 +284,7 @@ struct xhci_intr_reg {
struct xhci_run_regs {
__le32 microframe_index;
__le32 rsvd[7];
- struct xhci_intr_reg ir_set[128];
+ struct xhci_intr_reg ir_set[1024];
};
/**
@@ -357,15 +357,15 @@ struct xhci_slot_ctx {
#define GET_DEV_SPEED(n) (((n) & DEV_SPEED) >> 20)
/* bit 24 reserved */
/* Is this LS/FS device connected through a HS hub? - bit 25 */
-#define DEV_MTT (0x1 << 25)
+#define DEV_MTT BIT(25)
/* Set if the device is a hub - bit 26 */
-#define DEV_HUB (0x1 << 26)
+#define DEV_HUB BIT(26)
/* Index of the last valid endpoint context in this device context - 27:31 */
#define LAST_CTX_MASK (0x1f << 27)
#define LAST_CTX(p) ((p) << 27)
#define LAST_CTX_TO_EP_NUM(p) (((p) >> 27) - 1)
-#define SLOT_FLAG (1 << 0)
-#define EP0_FLAG (1 << 1)
+#define SLOT_FLAG BIT(0)
+#define EP0_FLAG BIT(1)
/* dev_info2 bitmasks */
/* Max Exit Latency (ms) - worst case time to wake up all links in dev path */
@@ -464,7 +464,7 @@ struct xhci_ep_ctx {
#define EP_MAXPSTREAMS(p) (((p) << 10) & EP_MAXPSTREAMS_MASK)
#define CTX_TO_EP_MAXPSTREAMS(p) (((p) & EP_MAXPSTREAMS_MASK) >> 10)
/* Endpoint is set up with a Linear Stream Array (vs. Secondary Stream Array) */
-#define EP_HAS_LSA (1 << 15)
+#define EP_HAS_LSA BIT(15)
/* hosts with LEC=1 use bits 31:24 as ESIT high bits. */
#define CTX_TO_MAX_ESIT_PAYLOAD_HI(p) (((p) >> 24) & 0xff)
@@ -499,8 +499,9 @@ struct xhci_ep_ctx {
#define CTX_TO_MAX_ESIT_PAYLOAD(p) (((p) >> 16) & 0xffff)
/* deq bitmasks */
-#define EP_CTX_CYCLE_MASK (1 << 0)
-#define SCTX_DEQ_MASK (~0xfL)
+#define EP_CTX_CYCLE_MASK BIT(0)
+/* bits 63:4 - TR Dequeue Pointer */
+#define TR_DEQ_PTR_MASK GENMASK_ULL(63, 4)
/**
@@ -586,6 +587,7 @@ struct xhci_stream_info {
#define SMALL_STREAM_ARRAY_SIZE 256
#define MEDIUM_STREAM_ARRAY_SIZE 1024
+#define GET_PORT_BW_ARRAY_SIZE 256
/* Some Intel xHCI host controllers need software to keep track of the bus
* bandwidth. Keep track of endpoint info here. Each root port is allocated
@@ -660,18 +662,18 @@ struct xhci_virt_ep {
struct xhci_ring *new_ring;
unsigned int err_count;
unsigned int ep_state;
-#define SET_DEQ_PENDING (1 << 0)
-#define EP_HALTED (1 << 1) /* For stall handling */
-#define EP_STOP_CMD_PENDING (1 << 2) /* For URB cancellation */
+#define SET_DEQ_PENDING BIT(0)
+#define EP_HALTED BIT(1) /* For stall handling */
+#define EP_STOP_CMD_PENDING BIT(2) /* For URB cancellation */
/* Transitioning the endpoint to using streams, don't enqueue URBs */
-#define EP_GETTING_STREAMS (1 << 3)
-#define EP_HAS_STREAMS (1 << 4)
+#define EP_GETTING_STREAMS BIT(3)
+#define EP_HAS_STREAMS BIT(4)
/* Transitioning the endpoint to not using streams, don't enqueue URBs */
-#define EP_GETTING_NO_STREAMS (1 << 5)
-#define EP_HARD_CLEAR_TOGGLE (1 << 6)
-#define EP_SOFT_CLEAR_TOGGLE (1 << 7)
+#define EP_GETTING_NO_STREAMS BIT(5)
+#define EP_HARD_CLEAR_TOGGLE BIT(6)
+#define EP_SOFT_CLEAR_TOGGLE BIT(7)
/* usb_hub_clear_tt_buffer is in progress */
-#define EP_CLEARING_TT (1 << 8)
+#define EP_CLEARING_TT BIT(8)
/* ---- Related to URB cancellation ---- */
struct list_head cancelled_td_list;
struct xhci_hcd *xhci;
@@ -697,6 +699,8 @@ struct xhci_virt_ep {
int next_frame_id;
/* Use new Isoch TRB layout needed for extended TBC support */
bool use_extended_tbc;
+ /* set if this endpoint is controlled via sideband access*/
+ struct xhci_sideband *sideband;
};
enum xhci_overhead_type {
@@ -759,6 +763,8 @@ struct xhci_virt_device {
u16 current_mel;
/* Used for the debugfs interfaces. */
void *debugfs_private;
+ /* set if this endpoint is controlled via sideband access*/
+ struct xhci_sideband *sideband;
};
/*
@@ -794,7 +800,6 @@ struct xhci_device_context_array {
/* private xHCD pointers */
dma_addr_t dma;
};
-/* TODO: write function to set the 64-bit device DMA address */
/*
* TODO: change this to be dynamically sized at HC mem init time since the HC
* might not be able to handle the maximum number of devices possible.
@@ -950,7 +955,7 @@ struct xhci_link_trb {
};
/* control bitfields */
-#define LINK_TOGGLE (0x1<<1)
+#define LINK_TOGGLE BIT(1)
/* Command completion event TRB */
struct xhci_event_cmd {
@@ -964,13 +969,13 @@ struct xhci_event_cmd {
#define COMP_PARAM(p) ((p) & 0xffffff) /* Command Completion Parameter */
/* Address device - disable SetAddress */
-#define TRB_BSR (1<<9)
+#define TRB_BSR BIT(9)
/* Configure Endpoint - Deconfigure */
-#define TRB_DC (1<<9)
+#define TRB_DC BIT(9)
/* Stop Ring - Transfer State Preserve */
-#define TRB_TSP (1<<9)
+#define TRB_TSP BIT(9)
enum xhci_ep_reset_type {
EP_HARD_RESET,
@@ -999,6 +1004,9 @@ enum xhci_setup_dev {
/* bits 16:23 are the virtual function ID */
/* bits 24:31 are the slot ID */
+/* bits 19:16 are the dev speed */
+#define DEV_SPEED_FOR_TRB(p) ((p) << 16)
+
/* Stop Endpoint TRB - ep_index to endpoint ID for this TRB */
#define SUSPEND_PORT_FOR_TRB(p) (((p) & 1) << 23)
#define TRB_TO_SUSPEND_PORT(p) (((p) & (1 << 23)) >> 23)
@@ -1010,13 +1018,13 @@ enum xhci_setup_dev {
#define SCT_FOR_TRB(p) (((p) & 0x7) << 1)
/* Link TRB specific fields */
-#define TRB_TC (1<<1)
+#define TRB_TC BIT(1)
/* Port Status Change Event TRB fields */
/* Port ID - bits 31:24 */
#define GET_PORT_ID(p) (((p) & (0xff << 24)) >> 24)
-#define EVENT_DATA (1 << 2)
+#define EVENT_DATA BIT(2)
/* Normal TRB fields */
/* transfer_len bitmasks - bits 0:16 */
@@ -1031,36 +1039,36 @@ enum xhci_setup_dev {
#define GET_INTR_TARGET(p) (((p) >> 22) & 0x3ff)
/* Cycle bit - indicates TRB ownership by HC or HCD */
-#define TRB_CYCLE (1<<0)
+#define TRB_CYCLE BIT(0)
/*
* Force next event data TRB to be evaluated before task switch.
* Used to pass OS data back after a TD completes.
*/
-#define TRB_ENT (1<<1)
+#define TRB_ENT BIT(1)
/* Interrupt on short packet */
-#define TRB_ISP (1<<2)
+#define TRB_ISP BIT(2)
/* Set PCIe no snoop attribute */
-#define TRB_NO_SNOOP (1<<3)
+#define TRB_NO_SNOOP BIT(3)
/* Chain multiple TRBs into a TD */
-#define TRB_CHAIN (1<<4)
+#define TRB_CHAIN BIT(4)
/* Interrupt on completion */
-#define TRB_IOC (1<<5)
+#define TRB_IOC BIT(5)
/* The buffer pointer contains immediate data */
-#define TRB_IDT (1<<6)
+#define TRB_IDT BIT(6)
/* TDs smaller than this might use IDT */
#define TRB_IDT_MAX_SIZE 8
/* Block Event Interrupt */
-#define TRB_BEI (1<<9)
+#define TRB_BEI BIT(9)
/* Control transfer TRB specific fields */
-#define TRB_DIR_IN (1<<16)
+#define TRB_DIR_IN BIT(16)
#define TRB_TX_TYPE(p) ((p) << 16)
#define TRB_DATA_OUT 2
#define TRB_DATA_IN 3
/* Isochronous TRB specific fields */
-#define TRB_SIA (1<<31)
+#define TRB_SIA BIT(31)
#define TRB_FRAME_ID(p) (((p) & 0x7ff) << 20)
#define GET_FRAME_ID(p) (((p) >> 20) & 0x7ff)
/* Total burst count field, Rsvdz on xhci 1.1 with Extended TBC enabled (ETE) */
@@ -1371,7 +1379,7 @@ struct xhci_ring {
unsigned int num_trbs_free; /* used only by xhci DbC */
unsigned int bounce_buf_len;
enum xhci_ring_type type;
- bool last_td_was_short;
+ u32 old_trb_comp_code;
struct radix_tree_root *trb_address_map;
};
@@ -1444,8 +1452,8 @@ struct xhci_interrupter {
bool ip_autoclear;
u32 isoc_bei_interval;
/* For interrupter registers save and restore over suspend/resume */
- u32 s3_irq_pending;
- u32 s3_irq_control;
+ u32 s3_iman;
+ u32 s3_imod;
u32 s3_erst_size;
u64 s3_erst_base;
u64 s3_erst_dequeue;
@@ -1465,7 +1473,7 @@ struct xhci_port_cap {
};
struct xhci_port {
- __le32 __iomem *addr;
+ struct xhci_port_regs __iomem *port_reg;
int hw_portnum;
int hcd_portnum;
struct xhci_hub *rhub;
@@ -1501,7 +1509,6 @@ struct xhci_hcd {
struct xhci_doorbell_array __iomem *dba;
/* Cached register copies of read-only HC data */
- __u32 hcs_params1;
__u32 hcs_params2;
__u32 hcs_params3;
__u32 hcc_params;
@@ -1512,12 +1519,11 @@ struct xhci_hcd {
/* packed release number */
u16 hci_version;
u16 max_interrupters;
+ u8 max_slots;
+ u8 max_ports;
/* imod_interval in ns (I * 250ns) */
u32 imod_interval;
- /* 4KB min, 128MB max */
- int page_size;
- /* Valid values are 12 to 20, inclusive */
- int page_shift;
+ u32 page_size;
/* MSI-X/MSI vectors */
int nvecs;
/* optional clocks */
@@ -1530,9 +1536,9 @@ struct xhci_hcd {
struct xhci_interrupter **interrupters;
struct xhci_ring *cmd_ring;
unsigned int cmd_ring_state;
-#define CMD_RING_STATE_RUNNING (1 << 0)
-#define CMD_RING_STATE_ABORTED (1 << 1)
-#define CMD_RING_STATE_STOPPED (1 << 2)
+#define CMD_RING_STATE_RUNNING BIT(0)
+#define CMD_RING_STATE_ABORTED BIT(1)
+#define CMD_RING_STATE_STOPPED BIT(2)
struct list_head cmd_list;
unsigned int cmd_ring_reserved_trbs;
struct delayed_work cmd_timer;
@@ -1554,6 +1560,7 @@ struct xhci_hcd {
struct dma_pool *device_pool;
struct dma_pool *segment_pool;
struct dma_pool *small_streams_pool;
+ struct dma_pool *port_bw_pool;
struct dma_pool *medium_streams_pool;
/* Host controller watchdog timer structures */
@@ -1572,9 +1579,9 @@ struct xhci_hcd {
*
* There are no reports of xHCI host controllers that display this issue.
*/
-#define XHCI_STATE_DYING (1 << 0)
-#define XHCI_STATE_HALTED (1 << 1)
-#define XHCI_STATE_REMOVING (1 << 2)
+#define XHCI_STATE_DYING BIT(0)
+#define XHCI_STATE_HALTED BIT(1)
+#define XHCI_STATE_REMOVING BIT(2)
unsigned long long quirks;
#define XHCI_LINK_TRB_QUIRK BIT_ULL(0)
#define XHCI_RESET_EP_QUIRK BIT_ULL(1) /* Deprecated */
@@ -1637,6 +1644,7 @@ struct xhci_hcd {
#define XHCI_WRITE_64_HI_LO BIT_ULL(47)
#define XHCI_CDNS_SCTX_QUIRK BIT_ULL(48)
#define XHCI_ETRON_HOST BIT_ULL(49)
+#define XHCI_LIMIT_ENDPOINT_INTERVAL_9 BIT_ULL(50)
unsigned int num_active_eps;
unsigned int limit_active_eps;
@@ -1759,11 +1767,20 @@ static inline void xhci_write_64(struct xhci_hcd *xhci,
}
-/* Link TRB chain should always be set on 0.95 hosts, and AMD 0.96 ISOC rings */
+/*
+ * Reportedly, some chapters of v0.95 spec said that Link TRB always has its chain bit set.
+ * Other chapters and later specs say that it should only be set if the link is inside a TD
+ * which continues from the end of one segment to the next segment.
+ *
+ * Some 0.95 hardware was found to misbehave if any link TRB doesn't have the chain bit set.
+ *
+ * 0.96 hardware from AMD and NEC was found to ignore unchained isochronous link TRBs when
+ * "resynchronizing the pipe" after a Missed Service Error.
+ */
static inline bool xhci_link_chain_quirk(struct xhci_hcd *xhci, enum xhci_ring_type type)
{
return (xhci->quirks & XHCI_LINK_TRB_QUIRK) ||
- (type == TYPE_ISOC && (xhci->quirks & XHCI_AMD_0x96_HOST));
+ (type == TYPE_ISOC && (xhci->quirks & (XHCI_AMD_0x96_HOST | XHCI_NEC_HOST)));
}
/* xHCI debugging */
@@ -1775,7 +1792,8 @@ void xhci_dbg_trace(struct xhci_hcd *xhci, void (*trace)(struct va_format *),
/* xHCI memory management */
void xhci_mem_cleanup(struct xhci_hcd *xhci);
int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags);
-void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id);
+void xhci_free_virt_device(struct xhci_hcd *xhci, struct xhci_virt_device *dev, int slot_id);
+void xhci_free_virt_devices_depth_first(struct xhci_hcd *xhci, int slot_id);
int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id, struct usb_device *udev, gfp_t flags);
int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *udev);
void xhci_copy_ep0_dequeue_into_input_ctx(struct xhci_hcd *xhci,
@@ -1787,6 +1805,7 @@ void xhci_update_tt_active_eps(struct xhci_hcd *xhci,
struct xhci_virt_device *virt_dev,
int old_active_eps);
void xhci_clear_endpoint_bw_info(struct xhci_bw_info *bw_info);
+void xhci_rh_bw_cleanup(struct xhci_hcd *xhci);
void xhci_update_bw_info(struct xhci_hcd *xhci,
struct xhci_container_ctx *in_ctx,
struct xhci_input_control_ctx *ctrl_ctx,
@@ -1807,6 +1826,7 @@ void xhci_ring_free(struct xhci_hcd *xhci, struct xhci_ring *ring);
int xhci_ring_expansion(struct xhci_hcd *xhci, struct xhci_ring *ring,
unsigned int num_trbs, gfp_t flags);
void xhci_initialize_ring_info(struct xhci_ring *ring);
+void xhci_ring_init(struct xhci_hcd *xhci, struct xhci_ring *ring);
void xhci_free_endpoint_ring(struct xhci_hcd *xhci,
struct xhci_virt_device *virt_dev,
unsigned int ep_index);
@@ -1837,17 +1857,22 @@ struct xhci_container_ctx *xhci_alloc_container_ctx(struct xhci_hcd *xhci,
int type, gfp_t flags);
void xhci_free_container_ctx(struct xhci_hcd *xhci,
struct xhci_container_ctx *ctx);
+struct xhci_container_ctx *xhci_alloc_port_bw_ctx(struct xhci_hcd *xhci,
+ gfp_t flags);
+void xhci_free_port_bw_ctx(struct xhci_hcd *xhci,
+ struct xhci_container_ctx *ctx);
struct xhci_interrupter *
xhci_create_secondary_interrupter(struct usb_hcd *hcd, unsigned int segs,
- u32 imod_interval);
+ u32 imod_interval, unsigned int intr_num);
void xhci_remove_secondary_interrupter(struct usb_hcd
*hcd, struct xhci_interrupter *ir);
+void xhci_skip_sec_intr_events(struct xhci_hcd *xhci,
+ struct xhci_ring *ring,
+ struct xhci_interrupter *ir);
/* xHCI host controller glue */
typedef void (*xhci_get_quirks_t)(struct device *, struct xhci_hcd *);
int xhci_handshake(void __iomem *ptr, u32 mask, u32 done, u64 timeout_us);
-int xhci_handshake_check_state(struct xhci_hcd *xhci, void __iomem *ptr,
- u32 mask, u32 done, int usec, unsigned int exit_state);
void xhci_quiesce(struct xhci_hcd *xhci);
int xhci_halt(struct xhci_hcd *xhci);
int xhci_start(struct xhci_hcd *xhci);
@@ -1867,10 +1892,11 @@ void xhci_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *udev);
int xhci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hdev,
struct usb_tt *tt, gfp_t mem_flags);
int xhci_disable_slot(struct xhci_hcd *xhci, u32 slot_id);
+int xhci_disable_and_free_slot(struct xhci_hcd *xhci, u32 slot_id);
int xhci_ext_cap_init(struct xhci_hcd *xhci);
int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup);
-int xhci_resume(struct xhci_hcd *xhci, pm_message_t msg);
+int xhci_resume(struct xhci_hcd *xhci, bool power_lost, bool is_auto_resume);
irqreturn_t xhci_irq(struct usb_hcd *hcd);
irqreturn_t xhci_msi_irq(int irq, void *hcd);
@@ -1881,11 +1907,11 @@ int xhci_alloc_tt_info(struct xhci_hcd *xhci,
struct usb_tt *tt, gfp_t mem_flags);
int xhci_set_interrupter_moderation(struct xhci_interrupter *ir,
u32 imod_interval);
+int xhci_enable_interrupter(struct xhci_interrupter *ir);
+int xhci_disable_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir);
/* xHCI ring, segment, TRB, and TD functions */
dma_addr_t xhci_trb_virt_to_dma(struct xhci_segment *seg, union xhci_trb *trb);
-struct xhci_segment *trb_in_td(struct xhci_hcd *xhci, struct xhci_td *td,
- dma_addr_t suspect_dma, bool debug);
int xhci_is_vendor_info_code(struct xhci_hcd *xhci, unsigned int trb_comp_code);
void xhci_ring_cmd_db(struct xhci_hcd *xhci);
int xhci_queue_slot_control(struct xhci_hcd *xhci, struct xhci_command *cmd,
@@ -1907,6 +1933,11 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags,
int xhci_queue_configure_endpoint(struct xhci_hcd *xhci,
struct xhci_command *cmd, dma_addr_t in_ctx_ptr, u32 slot_id,
bool command_must_succeed);
+int xhci_queue_get_port_bw(struct xhci_hcd *xhci,
+ struct xhci_command *cmd, dma_addr_t in_ctx_ptr,
+ u8 dev_speed, bool command_must_succeed);
+int xhci_get_port_bandwidth(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx,
+ u8 dev_speed);
int xhci_queue_evaluate_context(struct xhci_hcd *xhci, struct xhci_command *cmd,
dma_addr_t in_ctx_ptr, u32 slot_id, bool command_must_succeed);
int xhci_queue_reset_ep(struct xhci_hcd *xhci, struct xhci_command *cmd,
@@ -1927,6 +1958,14 @@ unsigned int count_trbs(u64 addr, u64 len);
int xhci_stop_endpoint_sync(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
int suspend, gfp_t gfp_flags);
void xhci_process_cancelled_tds(struct xhci_virt_ep *ep);
+void xhci_update_erst_dequeue(struct xhci_hcd *xhci,
+ struct xhci_interrupter *ir,
+ bool clear_ehb);
+void xhci_add_interrupter(struct xhci_hcd *xhci, unsigned int intr_num);
+int xhci_usb_endpoint_maxp(struct usb_device *udev,
+ struct usb_host_endpoint *host_ep);
+void xhci_portsc_writel(struct xhci_port *port, u32 val);
+u32 xhci_portsc_readl(struct xhci_port *port);
/* xHCI roothub code */
void xhci_set_link_state(struct xhci_hcd *xhci, struct xhci_port *port,
@@ -2365,25 +2404,48 @@ static inline const char *xhci_decode_portsc(char *str, u32 portsc)
if (portsc == ~(u32)0)
return str;
- ret += sprintf(str + ret, "%s %s %s Link:%s PortSpeed:%d ",
- portsc & PORT_POWER ? "Powered" : "Powered-off",
- portsc & PORT_CONNECT ? "Connected" : "Not-connected",
- portsc & PORT_PE ? "Enabled" : "Disabled",
- xhci_portsc_link_state_string(portsc),
- DEV_PORT_SPEED(portsc));
+ ret += sprintf(str + ret, "Speed=%d ", DEV_PORT_SPEED(portsc));
+ ret += sprintf(str + ret, "Link=%s ", xhci_portsc_link_state_string(portsc));
+ /* RO/ROS: Read-only */
+ if (portsc & PORT_CONNECT)
+ ret += sprintf(str + ret, "CCS ");
if (portsc & PORT_OC)
- ret += sprintf(str + ret, "OverCurrent ");
+ ret += sprintf(str + ret, "OCA "); /* No set for USB2 ports */
+ if (portsc & PORT_CAS)
+ ret += sprintf(str + ret, "CAS ");
+ if (portsc & PORT_DEV_REMOVE)
+ ret += sprintf(str + ret, "DR ");
+
+ /* RWS; writing 1 sets the bit, writing 0 clears the bit. */
+ if (portsc & PORT_POWER)
+ ret += sprintf(str + ret, "PP ");
+ if (portsc & PORT_WKCONN_E)
+ ret += sprintf(str + ret, "WCE ");
+ if (portsc & PORT_WKDISC_E)
+ ret += sprintf(str + ret, "WDE ");
+ if (portsc & PORT_WKOC_E)
+ ret += sprintf(str + ret, "WOE ");
+
+ /* RW; writing 1 sets the bit, writing 0 clears the bit */
+ if (portsc & PORT_LINK_STROBE)
+ ret += sprintf(str + ret, "LWS "); /* LWS 0 write is ignored */
+
+ /* RW1S; writing 1 sets the bit, writing 0 has no effect */
if (portsc & PORT_RESET)
- ret += sprintf(str + ret, "In-Reset ");
+ ret += sprintf(str + ret, "PR ");
+ if (portsc & PORT_WR)
+ ret += sprintf(str + ret, "WPR "); /* RsvdZ for USB2 ports */
- ret += sprintf(str + ret, "Change: ");
+ /* RW1CS; writing 1 clears the bit, writing 0 has no effect. */
+ if (portsc & PORT_PE)
+ ret += sprintf(str + ret, "PED ");
if (portsc & PORT_CSC)
ret += sprintf(str + ret, "CSC ");
if (portsc & PORT_PEC)
- ret += sprintf(str + ret, "PEC ");
+ ret += sprintf(str + ret, "PEC "); /* No set for USB3 ports */
if (portsc & PORT_WRC)
- ret += sprintf(str + ret, "WRC ");
+ ret += sprintf(str + ret, "WRC "); /* RsvdZ for USB2 ports */
if (portsc & PORT_OCC)
ret += sprintf(str + ret, "OCC ");
if (portsc & PORT_RC)
@@ -2391,17 +2453,7 @@ static inline const char *xhci_decode_portsc(char *str, u32 portsc)
if (portsc & PORT_PLC)
ret += sprintf(str + ret, "PLC ");
if (portsc & PORT_CEC)
- ret += sprintf(str + ret, "CEC ");
- if (portsc & PORT_CAS)
- ret += sprintf(str + ret, "CAS ");
-
- ret += sprintf(str + ret, "Wake: ");
- if (portsc & PORT_WKCONN_E)
- ret += sprintf(str + ret, "WCE ");
- if (portsc & PORT_WKDISC_E)
- ret += sprintf(str + ret, "WDE ");
- if (portsc & PORT_WKOC_E)
- ret += sprintf(str + ret, "WOE ");
+ ret += sprintf(str + ret, "CEC "); /* RsvdZ for USB2 ports */
return str;
}
diff --git a/drivers/usb/image/mdc800.c b/drivers/usb/image/mdc800.c
index 7b7e1554ea20..ca287b770e8c 100644
--- a/drivers/usb/image/mdc800.c
+++ b/drivers/usb/image/mdc800.c
@@ -707,7 +707,7 @@ static ssize_t mdc800_device_read (struct file *file, char __user *buf, size_t l
if (signal_pending (current))
{
mutex_unlock(&mdc800->io_lock);
- return -EINTR;
+ return len == left ? -EINTR : len-left;
}
sts=left > (mdc800->out_count-mdc800->out_ptr)?mdc800->out_count-mdc800->out_ptr:left;
@@ -730,9 +730,11 @@ static ssize_t mdc800_device_read (struct file *file, char __user *buf, size_t l
mutex_unlock(&mdc800->io_lock);
return len-left;
}
- wait_event_timeout(mdc800->download_wait,
+ retval = wait_event_timeout(mdc800->download_wait,
mdc800->downloaded,
msecs_to_jiffies(TO_DOWNLOAD_GET_READY));
+ if (!retval)
+ usb_kill_urb(mdc800->download_urb);
mdc800->downloaded = 0;
if (mdc800->download_urb->status != 0)
{
@@ -982,7 +984,7 @@ static int __init usb_mdc800_init (void)
{
int retval = -ENODEV;
/* Allocate Memory */
- mdc800=kzalloc (sizeof (struct mdc800_data), GFP_KERNEL);
+ mdc800=kzalloc_obj(struct mdc800_data);
if (!mdc800)
goto cleanup_on_fail;
diff --git a/drivers/usb/image/microtek.c b/drivers/usb/image/microtek.c
index 934ec5310fb9..1f0b3c44d388 100644
--- a/drivers/usb/image/microtek.c
+++ b/drivers/usb/image/microtek.c
@@ -55,7 +55,6 @@
* Status:
*
* Untested with multiple scanners.
- * Untested on SMP.
* Untested on a bigendian machine.
*
* History:
@@ -170,25 +169,13 @@ static struct usb_driver mts_usb_driver = {
#define MTS_VERSION "0.4.3"
#define MTS_NAME "microtek usb (rev " MTS_VERSION "): "
-#define MTS_WARNING(x...) \
- printk( KERN_WARNING MTS_NAME x )
-#define MTS_ERROR(x...) \
- printk( KERN_ERR MTS_NAME x )
-#define MTS_INT_ERROR(x...) \
- MTS_ERROR(x)
-#define MTS_MESSAGE(x...) \
- printk( KERN_INFO MTS_NAME x )
-
#if defined MTS_DO_DEBUG
#define MTS_DEBUG(x...) \
printk( KERN_DEBUG MTS_NAME x )
-#define MTS_DEBUG_GOT_HERE() \
- MTS_DEBUG("got to %s:%d (%s)\n", __FILE__, (int)__LINE__, __func__ )
#define MTS_DEBUG_INT() \
- do { MTS_DEBUG_GOT_HERE(); \
- MTS_DEBUG("transfer = 0x%x context = 0x%x\n",(int)transfer,(int)context ); \
+ do { MTS_DEBUG("transfer = 0x%x context = 0x%x\n",(int)transfer,(int)context ); \
MTS_DEBUG("status = 0x%x data-length = 0x%x sent = 0x%x\n",transfer->status,(int)context->data_length, (int)transfer->actual_length ); \
mts_debug_dump(context->instance);\
} while(0)
@@ -197,7 +184,6 @@ static struct usb_driver mts_usb_driver = {
#define MTS_NUL_STATEMENT do { } while(0)
#define MTS_DEBUG(x...) MTS_NUL_STATEMENT
-#define MTS_DEBUG_GOT_HERE() MTS_NUL_STATEMENT
#define MTS_DEBUG_INT() MTS_NUL_STATEMENT
#endif
@@ -316,7 +302,6 @@ static inline void mts_debug_dump(struct mts_desc* dummy)
#endif
static inline void mts_urb_abort(struct mts_desc* desc) {
- MTS_DEBUG_GOT_HERE();
mts_debug_dump(desc);
usb_kill_urb( desc->urb );
@@ -332,8 +317,6 @@ static int mts_scsi_abort(struct scsi_cmnd *srb)
{
struct mts_desc* desc = (struct mts_desc*)(srb->device->host->hostdata[0]);
- MTS_DEBUG_GOT_HERE();
-
mts_urb_abort(desc);
return FAILED;
@@ -344,7 +327,6 @@ static int mts_scsi_host_reset(struct scsi_cmnd *srb)
struct mts_desc* desc = (struct mts_desc*)(srb->device->host->hostdata[0]);
int result;
- MTS_DEBUG_GOT_HERE();
mts_debug_dump(desc);
result = usb_lock_device_for_reset(desc->usb_dev, desc->usb_intf);
@@ -355,8 +337,8 @@ static int mts_scsi_host_reset(struct scsi_cmnd *srb)
return result ? FAILED : SUCCESS;
}
-static int
-mts_scsi_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *srb);
+static enum scsi_qc_status mts_scsi_queuecommand(struct Scsi_Host *shost,
+ struct scsi_cmnd *srb);
static void mts_transfer_cleanup( struct urb *transfer );
static void mts_do_sg(struct urb * transfer);
@@ -386,7 +368,8 @@ void mts_int_submit_urb (struct urb* transfer,
res = usb_submit_urb( transfer, GFP_ATOMIC );
if ( unlikely(res) ) {
- MTS_INT_ERROR( "could not submit URB! Error was %d\n",(int)res );
+ dev_err(&context->instance->usb_dev->dev,
+ "could not submit URB! Error was %d\n",(int)res );
set_host_byte(context->srb, DID_ERROR);
mts_transfer_cleanup(transfer);
}
@@ -452,12 +435,9 @@ static void mts_command_done( struct urb *transfer )
if ( unlikely(status) ) {
if (status == -ENOENT) {
/* We are being killed */
- MTS_DEBUG_GOT_HERE();
set_host_byte(context->srb, DID_ABORT);
} else {
/* A genuine error has occurred */
- MTS_DEBUG_GOT_HERE();
-
set_host_byte(context->srb, DID_ERROR);
}
mts_transfer_cleanup(transfer);
@@ -523,8 +503,6 @@ mts_build_transfer_context(struct scsi_cmnd *srb, struct mts_desc* desc)
{
int pipe;
- MTS_DEBUG_GOT_HERE();
-
desc->context.instance = desc;
desc->context.srb = srb;
@@ -559,13 +537,12 @@ mts_build_transfer_context(struct scsi_cmnd *srb, struct mts_desc* desc)
desc->context.data_pipe = pipe;
}
-static int mts_scsi_queuecommand_lck(struct scsi_cmnd *srb)
+static enum scsi_qc_status mts_scsi_queuecommand_lck(struct scsi_cmnd *srb)
{
mts_scsi_cmnd_callback callback = scsi_done;
struct mts_desc* desc = (struct mts_desc*)(srb->device->host->hostdata[0]);
int res;
- MTS_DEBUG_GOT_HERE();
mts_show_command(srb);
mts_debug_dump(desc);
@@ -601,7 +578,7 @@ static int mts_scsi_queuecommand_lck(struct scsi_cmnd *srb)
res=usb_submit_urb(desc->urb, GFP_ATOMIC);
if(unlikely(res)){
- MTS_ERROR("error %d submitting URB\n",(int)res);
+ dev_err(&desc->usb_dev->dev, "error %d submitting URB\n",(int)res);
set_host_byte(srb, DID_ERROR);
if(likely(callback != NULL))
@@ -666,64 +643,52 @@ static int mts_usb_probe(struct usb_interface *intf,
/* the current altsetting on the interface we're probing */
struct usb_host_interface *altsetting;
- MTS_DEBUG_GOT_HERE();
MTS_DEBUG( "usb-device descriptor at %x\n", (int)dev );
MTS_DEBUG( "product id = 0x%x, vendor id = 0x%x\n",
le16_to_cpu(dev->descriptor.idProduct),
le16_to_cpu(dev->descriptor.idVendor) );
- MTS_DEBUG_GOT_HERE();
-
/* the current altsetting on the interface we're probing */
altsetting = intf->cur_altsetting;
-
/* Check if the config is sane */
if ( altsetting->desc.bNumEndpoints != MTS_EP_TOTAL ) {
- MTS_WARNING( "expecting %d got %d endpoints! Bailing out.\n",
+ dev_warn(&dev->dev, "expecting %d got %d endpoints! Bailing out.\n",
(int)MTS_EP_TOTAL, (int)altsetting->desc.bNumEndpoints );
return -ENODEV;
}
for( i = 0; i < altsetting->desc.bNumEndpoints; i++ ) {
- if ((altsetting->endpoint[i].desc.bmAttributes &
- USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK) {
-
- MTS_WARNING( "can only deal with bulk endpoints; endpoint %d is not bulk.\n",
- (int)altsetting->endpoint[i].desc.bEndpointAddress );
- } else {
- if (altsetting->endpoint[i].desc.bEndpointAddress &
- USB_DIR_IN)
- *ep_in_current++
- = altsetting->endpoint[i].desc.bEndpointAddress &
- USB_ENDPOINT_NUMBER_MASK;
- else {
- if ( ep_out != -1 ) {
- MTS_WARNING( "can only deal with one output endpoints. Bailing out." );
- return -ENODEV;
- }
-
- ep_out = altsetting->endpoint[i].desc.bEndpointAddress &
- USB_ENDPOINT_NUMBER_MASK;
+ if (usb_endpoint_is_bulk_in(&altsetting->endpoint[i].desc)) {
+ *ep_in_current++ = usb_endpoint_num(&altsetting->endpoint[i].desc);
+ } else if (usb_endpoint_is_bulk_out(&altsetting->endpoint[i].desc)) {
+ if (ep_out == -1) {
+ ep_out = usb_endpoint_num(&altsetting->endpoint[i].desc);
+ } else {
+ dev_warn(&dev->dev, "can only deal with bulk endpoints; endpoint %d is not bulk.\n",
+ usb_endpoint_num(&altsetting->endpoint[i].desc));
+ return -ENODEV;
}
+ } else {
+ dev_warn(&dev->dev, "can only deal with bulk endpoints; endpoint %d is not bulk.\n",
+ usb_endpoint_num(&altsetting->endpoint[i].desc));
}
-
}
if (ep_in_current != &ep_in_set[2]) {
- MTS_WARNING("couldn't find two input bulk endpoints. Bailing out.\n");
+ dev_warn(&dev->dev, "couldn't find two input bulk endpoints. Bailing out.\n");
return -ENODEV;
}
if ( ep_out == -1 ) {
- MTS_WARNING( "couldn't find an output bulk endpoint. Bailing out.\n" );
+ dev_warn(&dev->dev, "couldn't find an output bulk endpoint. Bailing out.\n" );
return -ENODEV;
}
- new_desc = kzalloc(sizeof(struct mts_desc), GFP_KERNEL);
+ new_desc = kzalloc_obj(struct mts_desc);
if (!new_desc)
goto out;
@@ -744,15 +709,15 @@ static int mts_usb_probe(struct usb_interface *intf,
new_desc->ep_image = ep_in_set[1];
if ( new_desc->ep_out != MTS_EP_OUT )
- MTS_WARNING( "will this work? Command EP is not usually %d\n",
+ dev_warn(&dev->dev, "will this work? Command EP is not usually %d\n",
(int)new_desc->ep_out );
if ( new_desc->ep_response != MTS_EP_RESPONSE )
- MTS_WARNING( "will this work? Response EP is not usually %d\n",
+ dev_warn(&dev->dev, "will this work? Response EP is not usually %d\n",
(int)new_desc->ep_response );
if ( new_desc->ep_image != MTS_EP_IMAGE )
- MTS_WARNING( "will this work? Image data EP is not usually %d\n",
+ dev_warn(&dev->dev, "will this work? Image data EP is not usually %d\n",
(int)new_desc->ep_image );
new_desc->host = scsi_host_alloc(&mts_scsi_host_template,
diff --git a/drivers/usb/isp1760/isp1760-hcd.c b/drivers/usb/isp1760/isp1760-hcd.c
index add2d2e3b61b..6c52c6f59d69 100644
--- a/drivers/usb/isp1760/isp1760-hcd.c
+++ b/drivers/usb/isp1760/isp1760-hcd.c
@@ -2458,7 +2458,7 @@ static void isp1760_stop(struct usb_hcd *hcd)
{
struct isp1760_hcd *priv = hcd_to_priv(hcd);
- del_timer(&errata2_timer);
+ timer_delete(&errata2_timer);
isp1760_hub_control(hcd, ClearPortFeature, USB_PORT_FEAT_POWER, 1,
NULL, 0);
@@ -2572,15 +2572,15 @@ int isp1760_hcd_register(struct isp1760_hcd *priv, struct resource *mem,
priv->hcd = hcd;
- priv->atl_slots = kcalloc(mem_layout->slot_num,
- sizeof(struct isp1760_slotinfo), GFP_KERNEL);
+ priv->atl_slots = kzalloc_objs(struct isp1760_slotinfo,
+ mem_layout->slot_num);
if (!priv->atl_slots) {
ret = -ENOMEM;
goto put_hcd;
}
- priv->int_slots = kcalloc(mem_layout->slot_num,
- sizeof(struct isp1760_slotinfo), GFP_KERNEL);
+ priv->int_slots = kzalloc_objs(struct isp1760_slotinfo,
+ mem_layout->slot_num);
if (!priv->int_slots) {
ret = -ENOMEM;
goto free_atl_slots;
diff --git a/drivers/usb/isp1760/isp1760-udc.c b/drivers/usb/isp1760/isp1760-udc.c
index 5cafd23345ca..e45a54c4a560 100644
--- a/drivers/usb/isp1760/isp1760-udc.c
+++ b/drivers/usb/isp1760/isp1760-udc.c
@@ -878,7 +878,7 @@ static struct usb_request *isp1760_ep_alloc_request(struct usb_ep *ep,
{
struct isp1760_request *req;
- req = kzalloc(sizeof(*req), gfp_flags);
+ req = kzalloc_obj(*req, gfp_flags);
if (!req)
return NULL;
@@ -1145,7 +1145,7 @@ static void isp1760_udc_disconnect(struct isp1760_udc *udc)
if (udc->driver->disconnect)
udc->driver->disconnect(&udc->gadget);
- del_timer(&udc->vbus_timer);
+ timer_delete(&udc->vbus_timer);
/* TODO Reset all endpoints ? */
}
@@ -1314,7 +1314,7 @@ static int isp1760_udc_stop(struct usb_gadget *gadget)
dev_dbg(udc->isp->dev, "%s\n", __func__);
- del_timer_sync(&udc->vbus_timer);
+ timer_delete_sync(&udc->vbus_timer);
isp1760_reg_write(udc->regs, mode_reg, 0);
@@ -1423,7 +1423,7 @@ static irqreturn_t isp1760_udc_irq(int irq, void *dev)
static void isp1760_udc_vbus_poll(struct timer_list *t)
{
- struct isp1760_udc *udc = from_timer(udc, t, vbus_timer);
+ struct isp1760_udc *udc = timer_container_of(udc, t, vbus_timer);
unsigned long flags;
spin_lock_irqsave(&udc->lock, flags);
diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig
index 6497c4e81e95..0b56b773dbdf 100644
--- a/drivers/usb/misc/Kconfig
+++ b/drivers/usb/misc/Kconfig
@@ -147,6 +147,7 @@ config USB_APPLEDISPLAY
config USB_QCOM_EUD
tristate "QCOM Embedded USB Debugger(EUD) Driver"
depends on ARCH_QCOM || COMPILE_TEST
+ select QCOM_SCM
select USB_ROLE_SWITCH
help
This module enables support for Qualcomm Technologies, Inc.
@@ -178,6 +179,21 @@ config USB_LJCA
This driver can also be built as a module. If so, the module
will be called usb-ljca.
+config USB_USBIO
+ tristate "Intel USBIO Bridge support"
+ depends on USB && ACPI
+ depends on X86 || COMPILE_TEST
+ select AUXILIARY_BUS
+ help
+ This adds support for Intel USBIO drivers.
+ This enables the USBIO bridge driver module in charge to talk
+ to the USB device. Additional drivers such as GPIO_USBIO and
+ I2C_USBIO must be enabled in order to use the device's full
+ functionality.
+
+ This driver can also be built as a module. If so, the module
+ will be called usbio.
+
source "drivers/usb/misc/sisusbvga/Kconfig"
config USB_LD
@@ -231,8 +247,8 @@ config USB_EHSET_TEST_FIXTURE
VID/PID pairs. This driver then initiates a corresponding test mode on
the downstream port to which the test fixture is attached.
- See <http://www.usb.org/developers/onthego/EHSET_v1.01.pdf> for more
- information.
+ See <https://www.usb.org/sites/default/files/EHSET_v1.01%281%29.pdf>
+ for more information.
config USB_ISIGHTFW
tristate "iSight firmware loading support"
diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile
index 0cd5bc8f52fe..494ab0377f35 100644
--- a/drivers/usb/misc/Makefile
+++ b/drivers/usb/misc/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_USB_EMI62) += emi62.o
obj-$(CONFIG_USB_EZUSB_FX2) += ezusb.o
obj-$(CONFIG_APPLE_MFI_FASTCHARGE) += apple-mfi-fastcharge.o
obj-$(CONFIG_USB_LJCA) += usb-ljca.o
+obj-$(CONFIG_USB_USBIO) += usbio.o
obj-$(CONFIG_USB_IDMOUSE) += idmouse.o
obj-$(CONFIG_USB_IOWARRIOR) += iowarrior.o
obj-$(CONFIG_USB_ISIGHTFW) += isight_firmware.o
diff --git a/drivers/usb/misc/adutux.c b/drivers/usb/misc/adutux.c
index ed6a19254d2f..369d0d2ee2be 100644
--- a/drivers/usb/misc/adutux.c
+++ b/drivers/usb/misc/adutux.c
@@ -657,7 +657,7 @@ static int adu_probe(struct usb_interface *interface,
int res;
/* allocate memory for our device state and initialize it */
- dev = kzalloc(sizeof(struct adu_device), GFP_KERNEL);
+ dev = kzalloc_obj(struct adu_device);
if (!dev)
return -ENOMEM;
diff --git a/drivers/usb/misc/apple-mfi-fastcharge.c b/drivers/usb/misc/apple-mfi-fastcharge.c
index ac8695195c13..af266e19f2fd 100644
--- a/drivers/usb/misc/apple-mfi-fastcharge.c
+++ b/drivers/usb/misc/apple-mfi-fastcharge.c
@@ -44,6 +44,7 @@ MODULE_DEVICE_TABLE(usb, mfi_fc_id_table);
struct mfi_device {
struct usb_device *udev;
struct power_supply *battery;
+ struct power_supply_desc battery_desc;
int charge_type;
};
@@ -133,7 +134,6 @@ static int apple_mfi_fc_set_property(struct power_supply *psy,
ret = -EINVAL;
}
- pm_runtime_mark_last_busy(&mfi->udev->dev);
pm_runtime_put_autosuspend(&mfi->udev->dev);
return ret;
@@ -178,32 +178,48 @@ static int mfi_fc_probe(struct usb_device *udev)
{
struct power_supply_config battery_cfg = {};
struct mfi_device *mfi = NULL;
+ char *battery_name;
int err;
if (!mfi_fc_match(udev))
return -ENODEV;
- mfi = kzalloc(sizeof(struct mfi_device), GFP_KERNEL);
+ mfi = kzalloc_obj(struct mfi_device);
if (!mfi)
return -ENOMEM;
+ battery_name = kasprintf(GFP_KERNEL, "apple_mfi_fastcharge_%d-%d",
+ udev->bus->busnum, udev->devnum);
+ if (!battery_name) {
+ err = -ENOMEM;
+ goto err_free_mfi;
+ }
+
+ mfi->battery_desc = apple_mfi_fc_desc;
+ mfi->battery_desc.name = battery_name;
+
battery_cfg.drv_data = mfi;
mfi->charge_type = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
mfi->battery = power_supply_register(&udev->dev,
- &apple_mfi_fc_desc,
+ &mfi->battery_desc,
&battery_cfg);
if (IS_ERR(mfi->battery)) {
dev_err(&udev->dev, "Can't register battery\n");
err = PTR_ERR(mfi->battery);
- kfree(mfi);
- return err;
+ goto err_free_name;
}
- mfi->udev = usb_get_dev(udev);
+ mfi->udev = udev;
dev_set_drvdata(&udev->dev, mfi);
return 0;
+
+err_free_name:
+ kfree(battery_name);
+err_free_mfi:
+ kfree(mfi);
+ return err;
}
static void mfi_fc_disconnect(struct usb_device *udev)
@@ -213,8 +229,8 @@ static void mfi_fc_disconnect(struct usb_device *udev)
mfi = dev_get_drvdata(&udev->dev);
if (mfi->battery)
power_supply_unregister(mfi->battery);
+ kfree(mfi->battery_desc.name);
dev_set_drvdata(&udev->dev, NULL);
- usb_put_dev(mfi->udev);
kfree(mfi);
}
diff --git a/drivers/usb/misc/appledisplay.c b/drivers/usb/misc/appledisplay.c
index 62b5a30edc42..16883592f7fc 100644
--- a/drivers/usb/misc/appledisplay.c
+++ b/drivers/usb/misc/appledisplay.c
@@ -12,6 +12,7 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
+#include <linux/hid.h>
#include <linux/usb.h>
#include <linux/backlight.h>
#include <linux/timer.h>
@@ -20,9 +21,6 @@
#define APPLE_VENDOR_ID 0x05AC
-#define USB_REQ_GET_REPORT 0x01
-#define USB_REQ_SET_REPORT 0x09
-
#define ACD_USB_TIMEOUT 250
#define ACD_USB_EDID 0x0302
@@ -140,7 +138,7 @@ static int appledisplay_bl_update_status(struct backlight_device *bd)
retval = usb_control_msg(
pdata->udev,
usb_sndctrlpipe(pdata->udev, 0),
- USB_REQ_SET_REPORT,
+ HID_REQ_SET_REPORT,
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
ACD_USB_BRIGHTNESS,
0,
@@ -163,7 +161,7 @@ static int appledisplay_bl_get_brightness(struct backlight_device *bd)
retval = usb_control_msg(
pdata->udev,
usb_rcvctrlpipe(pdata->udev, 0),
- USB_REQ_GET_REPORT,
+ HID_REQ_GET_REPORT,
USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
ACD_USB_BRIGHTNESS,
0,
@@ -226,7 +224,7 @@ static int appledisplay_probe(struct usb_interface *iface,
int_in_endpointAddr = endpoint->bEndpointAddress;
/* allocate memory for our device state and initialize it */
- pdata = kzalloc(sizeof(struct appledisplay), GFP_KERNEL);
+ pdata = kzalloc_obj(struct appledisplay);
if (!pdata) {
retval = -ENOMEM;
goto error;
diff --git a/drivers/usb/misc/chaoskey.c b/drivers/usb/misc/chaoskey.c
index 225863321dc4..d8016540953f 100644
--- a/drivers/usb/misc/chaoskey.c
+++ b/drivers/usb/misc/chaoskey.c
@@ -143,7 +143,7 @@ static int chaoskey_probe(struct usb_interface *interface,
/* Looks good, allocate and initialize */
- dev = kzalloc(sizeof(struct chaoskey), GFP_KERNEL);
+ dev = kzalloc_obj(struct chaoskey);
if (dev == NULL)
goto out;
@@ -444,9 +444,19 @@ static ssize_t chaoskey_read(struct file *file,
goto bail;
mutex_unlock(&dev->rng_lock);
- result = mutex_lock_interruptible(&dev->lock);
- if (result)
- goto bail;
+ if (file->f_flags & O_NONBLOCK) {
+ result = mutex_trylock(&dev->lock);
+ if (result == 0) {
+ result = -EAGAIN;
+ goto bail;
+ } else {
+ result = 0;
+ }
+ } else {
+ result = mutex_lock_interruptible(&dev->lock);
+ if (result)
+ goto bail;
+ }
if (dev->valid == dev->used) {
result = _chaoskey_fill(dev);
if (result < 0) {
diff --git a/drivers/usb/misc/cypress_cy7c63.c b/drivers/usb/misc/cypress_cy7c63.c
index 75f5a740cba3..4a7f955ba85b 100644
--- a/drivers/usb/misc/cypress_cy7c63.c
+++ b/drivers/usb/misc/cypress_cy7c63.c
@@ -211,11 +211,11 @@ static int cypress_probe(struct usb_interface *interface,
int retval = -ENOMEM;
/* allocate memory for our device state and initialize it */
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ dev = kzalloc_obj(*dev);
if (!dev)
goto error_mem;
- dev->udev = usb_get_dev(interface_to_usbdev(interface));
+ dev->udev = interface_to_usbdev(interface);
/* save our data pointer in this interface device */
usb_set_intfdata(interface, dev);
@@ -239,8 +239,6 @@ static void cypress_disconnect(struct usb_interface *interface)
* device files have been removed */
usb_set_intfdata(interface, NULL);
- usb_put_dev(dev->udev);
-
dev_info(&interface->dev,
"Cypress CY7C63xxx device now disconnected\n");
diff --git a/drivers/usb/misc/cytherm.c b/drivers/usb/misc/cytherm.c
index 875016dd073c..b183df9826bc 100644
--- a/drivers/usb/misc/cytherm.c
+++ b/drivers/usb/misc/cytherm.c
@@ -307,11 +307,11 @@ static int cytherm_probe(struct usb_interface *interface,
struct usb_cytherm *dev;
int retval = -ENOMEM;
- dev = kzalloc(sizeof(struct usb_cytherm), GFP_KERNEL);
+ dev = kzalloc_obj(struct usb_cytherm);
if (!dev)
goto error_mem;
- dev->udev = usb_get_dev(udev);
+ dev->udev = udev;
usb_set_intfdata(interface, dev);
@@ -334,8 +334,6 @@ static void cytherm_disconnect(struct usb_interface *interface)
/* first remove the files, then NULL the pointer */
usb_set_intfdata(interface, NULL);
- usb_put_dev(dev->udev);
-
kfree(dev);
dev_info(&interface->dev, "Cypress thermometer now disconnected\n");
diff --git a/drivers/usb/misc/idmouse.c b/drivers/usb/misc/idmouse.c
index ea39243efee3..0f6b3464c2d6 100644
--- a/drivers/usb/misc/idmouse.c
+++ b/drivers/usb/misc/idmouse.c
@@ -330,7 +330,7 @@ static int idmouse_probe(struct usb_interface *interface,
return -ENODEV;
/* allocate memory for our device state and initialize it */
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ dev = kzalloc_obj(*dev);
if (dev == NULL)
return -ENOMEM;
diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c
index 365c10069345..22504c0a2841 100644
--- a/drivers/usb/misc/iowarrior.c
+++ b/drivers/usb/misc/iowarrior.c
@@ -21,6 +21,7 @@
#include <linux/sched.h>
#include <linux/mutex.h>
#include <linux/poll.h>
+#include <linux/hid.h>
#include <linux/usb/iowarrior.h>
#define DRIVER_AUTHOR "Christian Lucht <lucht@codemercs.com>"
@@ -74,7 +75,6 @@ struct iowarrior {
struct mutex mutex; /* locks this structure */
struct usb_device *udev; /* save off the usb device pointer */
struct usb_interface *interface; /* the interface for this device */
- unsigned char minor; /* the starting minor number for this device */
struct usb_endpoint_descriptor *int_out_endpoint; /* endpoint for reading (needed for IOW56 only) */
struct usb_endpoint_descriptor *int_in_endpoint; /* endpoint for reading */
struct urb *int_in_urb; /* the urb for reading data */
@@ -99,14 +99,13 @@ struct iowarrior {
/* globals */
/*--------------*/
-#define USB_REQ_GET_REPORT 0x01
//#if 0
static int usb_get_report(struct usb_device *dev,
struct usb_host_interface *inter, unsigned char type,
unsigned char id, void *buf, int size)
{
return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
- USB_REQ_GET_REPORT,
+ HID_REQ_GET_REPORT,
USB_DIR_IN | USB_TYPE_CLASS |
USB_RECIP_INTERFACE, (type << 8) + id,
inter->desc.bInterfaceNumber, buf, size,
@@ -114,14 +113,12 @@ static int usb_get_report(struct usb_device *dev,
}
//#endif
-#define USB_REQ_SET_REPORT 0x09
-
static int usb_set_report(struct usb_interface *intf, unsigned char type,
unsigned char id, void *buf, int size)
{
return usb_control_msg(interface_to_usbdev(intf),
usb_sndctrlpipe(interface_to_usbdev(intf), 0),
- USB_REQ_SET_REPORT,
+ HID_REQ_SET_REPORT,
USB_TYPE_CLASS | USB_RECIP_INTERFACE,
(type << 8) + id,
intf->cur_altsetting->desc.bInterfaceNumber, buf,
@@ -234,8 +231,7 @@ static void iowarrior_write_callback(struct urb *urb)
"nonzero write bulk status received: %d\n", status);
}
/* free up our allocated buffer */
- usb_free_coherent(urb->dev, urb->transfer_buffer_length,
- urb->transfer_buffer, urb->transfer_dma);
+ kfree(urb->transfer_buffer);
/* tell a waiting writer the interrupt-out-pipe is available again */
atomic_dec(&dev->write_busy);
wake_up_interruptible(&dev->write_wait);
@@ -246,7 +242,6 @@ static void iowarrior_write_callback(struct urb *urb)
*/
static inline void iowarrior_delete(struct iowarrior *dev)
{
- dev_dbg(&dev->interface->dev, "minor %d\n", dev->minor);
kfree(dev->int_in_buffer);
usb_free_urb(dev->int_in_urb);
kfree(dev->read_queue);
@@ -297,9 +292,6 @@ static ssize_t iowarrior_read(struct file *file, char __user *buffer,
goto exit;
}
- dev_dbg(&dev->interface->dev, "minor %d, count = %zd\n",
- dev->minor, count);
-
/* read count must be packet size (+ time stamp) */
if ((count != dev->report_size)
&& (count != (dev->report_size + 1))) {
@@ -367,20 +359,21 @@ static ssize_t iowarrior_write(struct file *file,
size_t count, loff_t *ppos)
{
struct iowarrior *dev;
- int retval = 0;
+ int retval;
char *buf = NULL; /* for IOW24 and IOW56 we need a buffer */
struct urb *int_out_urb = NULL;
dev = file->private_data;
- mutex_lock(&dev->mutex);
+ retval = mutex_lock_interruptible(&dev->mutex);
+ if (retval < 0)
+ return -EINTR;
+
/* verify that the device wasn't unplugged */
if (!dev->present) {
retval = -ENODEV;
goto exit;
}
- dev_dbg(&dev->interface->dev, "minor %d, count = %zd\n",
- dev->minor, count);
/* if count is 0 we're already done */
if (count == 0) {
retval = 0;
@@ -443,8 +436,7 @@ static ssize_t iowarrior_write(struct file *file,
retval = -ENOMEM;
goto error_no_urb;
}
- buf = usb_alloc_coherent(dev->udev, dev->report_size,
- GFP_KERNEL, &int_out_urb->transfer_dma);
+ buf = kmalloc(dev->report_size, GFP_KERNEL);
if (!buf) {
retval = -ENOMEM;
dev_dbg(&dev->interface->dev,
@@ -457,7 +449,6 @@ static ssize_t iowarrior_write(struct file *file,
buf, dev->report_size,
iowarrior_write_callback, dev,
dev->int_out_endpoint->bInterval);
- int_out_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
if (copy_from_user(buf, user_buffer, count)) {
retval = -EFAULT;
goto error;
@@ -483,8 +474,7 @@ static ssize_t iowarrior_write(struct file *file,
goto exit;
}
error:
- usb_free_coherent(dev->udev, dev->report_size, buf,
- int_out_urb->transfer_dma);
+ kfree(buf);
error_no_buffer:
usb_free_urb(int_out_urb);
error_no_urb:
@@ -523,9 +513,6 @@ static long iowarrior_ioctl(struct file *file, unsigned int cmd,
goto error_out;
}
- dev_dbg(&dev->interface->dev, "minor %d, cmd 0x%.4x, arg %ld\n",
- dev->minor, cmd, arg);
-
retval = 0;
switch (cmd) {
case IOW_WRITE:
@@ -671,8 +658,6 @@ static int iowarrior_release(struct inode *inode, struct file *file)
if (!dev)
return -ENODEV;
- dev_dbg(&dev->interface->dev, "minor %d\n", dev->minor);
-
/* lock our device */
mutex_lock(&dev->mutex);
@@ -775,9 +760,10 @@ static int iowarrior_probe(struct usb_interface *interface,
struct usb_host_interface *iface_desc;
int retval = -ENOMEM;
int res;
+ int minor;
/* allocate memory for our device state and initialize it */
- dev = kzalloc(sizeof(struct iowarrior), GFP_KERNEL);
+ dev = kzalloc_obj(struct iowarrior);
if (!dev)
return retval;
@@ -890,12 +876,12 @@ static int iowarrior_probe(struct usb_interface *interface,
goto error;
}
- dev->minor = interface->minor;
+ minor = interface->minor;
/* let the user know what node this device is now attached to */
dev_info(&interface->dev, "IOWarrior product=0x%x, serial=%s interface=%d "
"now attached to iowarrior%d\n", dev->product_id, dev->chip_serial,
- iface_desc->desc.bInterfaceNumber, dev->minor - IOWARRIOR_MINOR_BASE);
+ iface_desc->desc.bInterfaceNumber, minor - IOWARRIOR_MINOR_BASE);
return retval;
error:
diff --git a/drivers/usb/misc/ldusb.c b/drivers/usb/misc/ldusb.c
index f392d6f84df9..c74f142f6637 100644
--- a/drivers/usb/misc/ldusb.c
+++ b/drivers/usb/misc/ldusb.c
@@ -656,7 +656,7 @@ static int ld_usb_probe(struct usb_interface *intf, const struct usb_device_id *
/* allocate memory for our device state and initialize it */
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ dev = kzalloc_obj(*dev);
if (!dev)
goto exit;
mutex_init(&dev->mutex);
diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c
index 379cf01a6e96..052ffc2e71ee 100644
--- a/drivers/usb/misc/legousbtower.c
+++ b/drivers/usb/misc/legousbtower.c
@@ -748,7 +748,7 @@ static int tower_probe(struct usb_interface *interface, const struct usb_device_
int result;
/* allocate memory for our device state and initialize it */
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ dev = kzalloc_obj(*dev);
if (!dev)
goto exit;
diff --git a/drivers/usb/misc/lvstest.c b/drivers/usb/misc/lvstest.c
index 25ec5666a75e..9b6e10177f80 100644
--- a/drivers/usb/misc/lvstest.c
+++ b/drivers/usb/misc/lvstest.c
@@ -260,7 +260,7 @@ static ssize_t get_dev_desc_store(struct device *dev,
struct usb_device_descriptor *descriptor;
int ret;
- descriptor = kmalloc(sizeof(*descriptor), GFP_KERNEL);
+ descriptor = kmalloc_obj(*descriptor);
if (!descriptor)
return -ENOMEM;
diff --git a/drivers/usb/misc/onboard_usb_dev.c b/drivers/usb/misc/onboard_usb_dev.c
index 75ac3c6aa92d..7cdbdfe07a76 100644
--- a/drivers/usb/misc/onboard_usb_dev.c
+++ b/drivers/usb/misc/onboard_usb_dev.c
@@ -36,9 +36,10 @@
#define USB5744_CMD_CREG_ACCESS 0x99
#define USB5744_CMD_CREG_ACCESS_LSB 0x37
#define USB5744_CREG_MEM_ADDR 0x00
+#define USB5744_CREG_MEM_RD_ADDR 0x04
#define USB5744_CREG_WRITE 0x00
-#define USB5744_CREG_RUNTIMEFLAGS2 0x41
-#define USB5744_CREG_RUNTIMEFLAGS2_LSB 0x1D
+#define USB5744_CREG_READ 0x01
+#define USB5744_CREG_RUNTIMEFLAGS2 0x411D
#define USB5744_CREG_BYPASS_UDC_SUSPEND BIT(3)
static void onboard_dev_attach_usb_driver(struct work_struct *work);
@@ -201,7 +202,7 @@ static int onboard_dev_add_usbdev(struct onboard_dev *onboard_dev,
goto error;
}
- node = kzalloc(sizeof(*node), GFP_KERNEL);
+ node = kzalloc_obj(*node);
if (!node) {
err = -ENOMEM;
goto error;
@@ -309,11 +310,88 @@ static void onboard_dev_attach_usb_driver(struct work_struct *work)
pr_err("Failed to attach USB driver: %pe\n", ERR_PTR(err));
}
+#if IS_ENABLED(CONFIG_USB_ONBOARD_DEV_USB5744)
+static int onboard_dev_5744_i2c_read_byte(struct i2c_client *client, u16 addr, u8 *data)
+{
+ struct i2c_msg msg[2];
+ u8 rd_buf[3];
+ int ret;
+
+ u8 wr_buf[7] = {0, USB5744_CREG_MEM_ADDR, 4,
+ USB5744_CREG_READ, 1,
+ addr >> 8 & 0xff,
+ addr & 0xff};
+ msg[0].addr = client->addr;
+ msg[0].flags = 0;
+ msg[0].len = sizeof(wr_buf);
+ msg[0].buf = wr_buf;
+
+ ret = i2c_transfer(client->adapter, msg, 1);
+ if (ret < 0)
+ return ret;
+
+ wr_buf[0] = USB5744_CMD_CREG_ACCESS;
+ wr_buf[1] = USB5744_CMD_CREG_ACCESS_LSB;
+ wr_buf[2] = 0;
+ msg[0].len = 3;
+
+ ret = i2c_transfer(client->adapter, msg, 1);
+ if (ret < 0)
+ return ret;
+
+ wr_buf[0] = 0;
+ wr_buf[1] = USB5744_CREG_MEM_RD_ADDR;
+ msg[0].len = 2;
+
+ msg[1].addr = client->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = 2;
+ msg[1].buf = rd_buf;
+
+ ret = i2c_transfer(client->adapter, msg, 2);
+ if (ret < 0)
+ return ret;
+ *data = rd_buf[1];
+
+ return 0;
+}
+
+static int onboard_dev_5744_i2c_write_byte(struct i2c_client *client, u16 addr, u8 data)
+{
+ struct i2c_msg msg[2];
+ int ret;
+
+ u8 wr_buf[8] = {0, USB5744_CREG_MEM_ADDR, 5,
+ USB5744_CREG_WRITE, 1,
+ addr >> 8 & 0xff,
+ addr & 0xff,
+ data};
+ msg[0].addr = client->addr;
+ msg[0].flags = 0;
+ msg[0].len = sizeof(wr_buf);
+ msg[0].buf = wr_buf;
+
+ ret = i2c_transfer(client->adapter, msg, 1);
+ if (ret < 0)
+ return ret;
+
+ msg[0].len = 3;
+ wr_buf[0] = USB5744_CMD_CREG_ACCESS;
+ wr_buf[1] = USB5744_CMD_CREG_ACCESS_LSB;
+ wr_buf[2] = 0;
+
+ ret = i2c_transfer(client->adapter, msg, 1);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
static int onboard_dev_5744_i2c_init(struct i2c_client *client)
{
-#if IS_ENABLED(CONFIG_USB_ONBOARD_DEV_USB5744)
struct device *dev = &client->dev;
int ret;
+ u8 reg;
/*
* Set BYPASS_UDC_SUSPEND bit to ensure MCU is always enabled
@@ -321,20 +399,16 @@ static int onboard_dev_5744_i2c_init(struct i2c_client *client)
* The command writes 5 bytes to memory and single data byte in
* configuration register.
*/
- char wr_buf[7] = {USB5744_CREG_MEM_ADDR, 5,
- USB5744_CREG_WRITE, 1,
- USB5744_CREG_RUNTIMEFLAGS2,
- USB5744_CREG_RUNTIMEFLAGS2_LSB,
- USB5744_CREG_BYPASS_UDC_SUSPEND};
-
- ret = i2c_smbus_write_block_data(client, 0, sizeof(wr_buf), wr_buf);
+ ret = onboard_dev_5744_i2c_read_byte(client,
+ USB5744_CREG_RUNTIMEFLAGS2, &reg);
if (ret)
- return dev_err_probe(dev, ret, "BYPASS_UDC_SUSPEND bit configuration failed\n");
+ return dev_err_probe(dev, ret, "CREG_RUNTIMEFLAGS2 read failed\n");
- ret = i2c_smbus_write_word_data(client, USB5744_CMD_CREG_ACCESS,
- USB5744_CMD_CREG_ACCESS_LSB);
+ reg |= USB5744_CREG_BYPASS_UDC_SUSPEND;
+ ret = onboard_dev_5744_i2c_write_byte(client,
+ USB5744_CREG_RUNTIMEFLAGS2, reg);
if (ret)
- return dev_err_probe(dev, ret, "Configuration Register Access Command failed\n");
+ return dev_err_probe(dev, ret, "BYPASS_UDC_SUSPEND bit configuration failed\n");
/* Send SMBus command to boot hub. */
ret = i2c_smbus_write_word_data(client, USB5744_CMD_ATTACH,
@@ -343,10 +417,13 @@ static int onboard_dev_5744_i2c_init(struct i2c_client *client)
return dev_err_probe(dev, ret, "USB Attach with SMBus command failed\n");
return ret;
+}
#else
+static int onboard_dev_5744_i2c_init(struct i2c_client *client)
+{
return -ENODEV;
-#endif
}
+#endif
static int onboard_dev_probe(struct platform_device *pdev)
{
@@ -487,10 +564,14 @@ static struct platform_driver onboard_dev_driver = {
/************************** USB driver **************************/
+#define VENDOR_ID_BISON 0x5986
+#define VENDOR_ID_CORECHIPS 0x3431
#define VENDOR_ID_CYPRESS 0x04b4
#define VENDOR_ID_GENESYS 0x05e3
#define VENDOR_ID_MICROCHIP 0x0424
+#define VENDOR_ID_PARADE 0x1da0
#define VENDOR_ID_REALTEK 0x0bda
+#define VENDOR_ID_TERMINUS 0x1a40
#define VENDOR_ID_TI 0x0451
#define VENDOR_ID_VIA 0x2109
#define VENDOR_ID_XMOS 0x20B1
@@ -569,8 +650,17 @@ static void onboard_dev_usbdev_disconnect(struct usb_device *udev)
}
static const struct usb_device_id onboard_dev_id_table[] = {
- { USB_DEVICE(VENDOR_ID_CYPRESS, 0x6504) }, /* CYUSB33{0,1,2}x/CYUSB230x 3.0 HUB */
- { USB_DEVICE(VENDOR_ID_CYPRESS, 0x6506) }, /* CYUSB33{0,1,2}x/CYUSB230x 2.0 HUB */
+ { USB_DEVICE(VENDOR_ID_BISON, 0x1198) }, /* Bison Electronics Inc. Integrated Camera */
+ { USB_DEVICE(VENDOR_ID_CORECHIPS, 0x6241) }, /* SL6341 2.0 HUB */
+ { USB_DEVICE(VENDOR_ID_CORECHIPS, 0x6341) }, /* SL6341 3.0 HUB */
+ { USB_DEVICE(VENDOR_ID_CYPRESS, 0x6500) }, /* CYUSB330x 3.0 HUB */
+ { USB_DEVICE(VENDOR_ID_CYPRESS, 0x6502) }, /* CYUSB330x 2.0 HUB */
+ { USB_DEVICE(VENDOR_ID_CYPRESS, 0x6503) }, /* CYUSB33{0,1}x 2.0 HUB, Vendor Mode */
+ { USB_DEVICE(VENDOR_ID_CYPRESS, 0x6504) }, /* CYUSB331x 3.0 HUB */
+ { USB_DEVICE(VENDOR_ID_CYPRESS, 0x6506) }, /* CYUSB331x 2.0 HUB */
+ { USB_DEVICE(VENDOR_ID_CYPRESS, 0x6507) }, /* CYUSB332x 2.0 HUB, Vendor Mode */
+ { USB_DEVICE(VENDOR_ID_CYPRESS, 0x6508) }, /* CYUSB332x 3.0 HUB */
+ { USB_DEVICE(VENDOR_ID_CYPRESS, 0x650a) }, /* CYUSB332x 2.0 HUB */
{ USB_DEVICE(VENDOR_ID_CYPRESS, 0x6570) }, /* CY7C6563x 2.0 HUB */
{ USB_DEVICE(VENDOR_ID_GENESYS, 0x0608) }, /* Genesys Logic GL850G USB 2.0 HUB */
{ USB_DEVICE(VENDOR_ID_GENESYS, 0x0610) }, /* Genesys Logic GL852G USB 2.0 HUB */
@@ -580,14 +670,20 @@ static const struct usb_device_id onboard_dev_id_table[] = {
{ USB_DEVICE(VENDOR_ID_MICROCHIP, 0x2517) }, /* USB2517 USB 2.0 HUB */
{ USB_DEVICE(VENDOR_ID_MICROCHIP, 0x2744) }, /* USB5744 USB 2.0 HUB */
{ USB_DEVICE(VENDOR_ID_MICROCHIP, 0x5744) }, /* USB5744 USB 3.0 HUB */
+ { USB_DEVICE(VENDOR_ID_PARADE, 0x5511) }, /* PS5511 USB 3.2 */
+ { USB_DEVICE(VENDOR_ID_PARADE, 0x55a1) }, /* PS5511 USB 2.0 */
{ USB_DEVICE(VENDOR_ID_REALTEK, 0x0411) }, /* RTS5411 USB 3.1 HUB */
{ USB_DEVICE(VENDOR_ID_REALTEK, 0x5411) }, /* RTS5411 USB 2.1 HUB */
{ USB_DEVICE(VENDOR_ID_REALTEK, 0x0414) }, /* RTS5414 USB 3.2 HUB */
{ USB_DEVICE(VENDOR_ID_REALTEK, 0x5414) }, /* RTS5414 USB 2.1 HUB */
+ { USB_DEVICE(VENDOR_ID_REALTEK, 0x0179) }, /* RTL8188ETV 2.4GHz WiFi */
+ { USB_DEVICE(VENDOR_ID_TERMINUS, 0x0101) }, /* Terminus FE1.1s 2.0 HUB */
{ USB_DEVICE(VENDOR_ID_TI, 0x8025) }, /* TI USB8020B 3.0 HUB */
{ USB_DEVICE(VENDOR_ID_TI, 0x8027) }, /* TI USB8020B 2.0 HUB */
{ USB_DEVICE(VENDOR_ID_TI, 0x8140) }, /* TI USB8041 3.0 HUB */
{ USB_DEVICE(VENDOR_ID_TI, 0x8142) }, /* TI USB8041 2.0 HUB */
+ { USB_DEVICE(VENDOR_ID_TI, 0x8440) }, /* TI USB8044 3.0 HUB */
+ { USB_DEVICE(VENDOR_ID_TI, 0x8442) }, /* TI USB8044 2.0 HUB */
{ USB_DEVICE(VENDOR_ID_VIA, 0x0817) }, /* VIA VL817 3.1 HUB */
{ USB_DEVICE(VENDOR_ID_VIA, 0x2817) }, /* VIA VL817 2.0 HUB */
{ USB_DEVICE(VENDOR_ID_XMOS, 0x0013) }, /* XMOS XVF3500 Voice Processor */
diff --git a/drivers/usb/misc/onboard_usb_dev.h b/drivers/usb/misc/onboard_usb_dev.h
index 317b3eb99c02..ac1aa3e122ad 100644
--- a/drivers/usb/misc/onboard_usb_dev.h
+++ b/drivers/usb/misc/onboard_usb_dev.h
@@ -23,6 +23,13 @@ static const struct onboard_dev_pdata microchip_usb424_data = {
.is_hub = true,
};
+static const struct onboard_dev_pdata microchip_usb2514_data = {
+ .reset_us = 1,
+ .num_supplies = 2,
+ .supply_names = { "vdd", "vdda" },
+ .is_hub = true,
+};
+
static const struct onboard_dev_pdata microchip_usb5744_data = {
.reset_us = 0,
.power_on_delay_us = 10000,
@@ -31,6 +38,13 @@ static const struct onboard_dev_pdata microchip_usb5744_data = {
.is_hub = true,
};
+static const struct onboard_dev_pdata parade_ps5511_data = {
+ .reset_us = 500,
+ .num_supplies = 2,
+ .supply_names = { "vddd11", "vdd33"},
+ .is_hub = true,
+};
+
static const struct onboard_dev_pdata realtek_rts5411_data = {
.reset_us = 0,
.num_supplies = 1,
@@ -38,6 +52,13 @@ static const struct onboard_dev_pdata realtek_rts5411_data = {
.is_hub = true,
};
+static const struct onboard_dev_pdata realtek_rtl8188etv_data = {
+ .reset_us = 0,
+ .num_supplies = 1,
+ .supply_names = { "vdd" },
+ .is_hub = false,
+};
+
static const struct onboard_dev_pdata ti_tusb8020b_data = {
.reset_us = 3000,
.num_supplies = 1,
@@ -45,24 +66,24 @@ static const struct onboard_dev_pdata ti_tusb8020b_data = {
.is_hub = true,
};
-static const struct onboard_dev_pdata ti_tusb8041_data = {
- .reset_us = 3000,
+static const struct onboard_dev_pdata bison_intcamera_data = {
+ .reset_us = 1000,
.num_supplies = 1,
.supply_names = { "vdd" },
- .is_hub = true,
+ .is_hub = false,
};
-static const struct onboard_dev_pdata cypress_hx3_data = {
+static const struct onboard_dev_pdata corechips_sl6341_data = {
.reset_us = 10000,
.num_supplies = 2,
- .supply_names = { "vdd", "vdd2" },
+ .supply_names = { "vdd1v1", "vdd3v3" },
.is_hub = true,
};
-static const struct onboard_dev_pdata cypress_hx2vl_data = {
- .reset_us = 1,
- .num_supplies = 1,
- .supply_names = { "vdd" },
+static const struct onboard_dev_pdata cypress_hx3_data = {
+ .reset_us = 10000,
+ .num_supplies = 2,
+ .supply_names = { "vdd", "vdd2" },
.is_hub = true,
};
@@ -80,6 +101,11 @@ static const struct onboard_dev_pdata genesys_gl852g_data = {
.is_hub = true,
};
+static const struct onboard_dev_pdata usb_a_conn_data = {
+ .num_supplies = 1,
+ .supply_names = { "vbus" },
+};
+
static const struct onboard_dev_pdata vialab_vl817_data = {
.reset_us = 10,
.num_supplies = 1,
@@ -87,6 +113,13 @@ static const struct onboard_dev_pdata vialab_vl817_data = {
.is_hub = true,
};
+static const struct onboard_dev_pdata wch_ch334_data = {
+ .reset_us = 14000,
+ .num_supplies = 2,
+ .supply_names = { "vdd33", "v5" },
+ .is_hub = true,
+};
+
static const struct onboard_dev_pdata xmos_xvf3500_data = {
.reset_us = 1,
.num_supplies = 2,
@@ -95,29 +128,40 @@ static const struct onboard_dev_pdata xmos_xvf3500_data = {
};
static const struct of_device_id onboard_dev_match[] = {
+ { .compatible = "usb-a-connector", .data = &usb_a_conn_data, },
{ .compatible = "usb424,2412", .data = &microchip_usb424_data, },
- { .compatible = "usb424,2514", .data = &microchip_usb424_data, },
+ { .compatible = "usb424,2514", .data = &microchip_usb2514_data, },
{ .compatible = "usb424,2517", .data = &microchip_usb424_data, },
{ .compatible = "usb424,2744", .data = &microchip_usb5744_data, },
{ .compatible = "usb424,5744", .data = &microchip_usb5744_data, },
{ .compatible = "usb451,8025", .data = &ti_tusb8020b_data, },
{ .compatible = "usb451,8027", .data = &ti_tusb8020b_data, },
- { .compatible = "usb451,8140", .data = &ti_tusb8041_data, },
- { .compatible = "usb451,8142", .data = &ti_tusb8041_data, },
+ { .compatible = "usb451,8140", .data = &ti_tusb8020b_data, },
+ { .compatible = "usb451,8142", .data = &ti_tusb8020b_data, },
+ { .compatible = "usb451,8440", .data = &ti_tusb8020b_data, },
+ { .compatible = "usb451,8442", .data = &ti_tusb8020b_data, },
{ .compatible = "usb4b4,6504", .data = &cypress_hx3_data, },
{ .compatible = "usb4b4,6506", .data = &cypress_hx3_data, },
- { .compatible = "usb4b4,6570", .data = &cypress_hx2vl_data, },
+ { .compatible = "usb4b4,6570", .data = &microchip_usb424_data, },
{ .compatible = "usb5e3,608", .data = &genesys_gl850g_data, },
{ .compatible = "usb5e3,610", .data = &genesys_gl852g_data, },
{ .compatible = "usb5e3,620", .data = &genesys_gl852g_data, },
{ .compatible = "usb5e3,626", .data = &genesys_gl852g_data, },
+ { .compatible = "usbbda,179", .data = &realtek_rtl8188etv_data, },
{ .compatible = "usbbda,411", .data = &realtek_rts5411_data, },
{ .compatible = "usbbda,5411", .data = &realtek_rts5411_data, },
{ .compatible = "usbbda,414", .data = &realtek_rts5411_data, },
{ .compatible = "usbbda,5414", .data = &realtek_rts5411_data, },
+ { .compatible = "usb1a40,0101", .data = &vialab_vl817_data, },
+ { .compatible = "usb1a86,8091", .data = &wch_ch334_data, },
+ { .compatible = "usb1da0,5511", .data = &parade_ps5511_data, },
+ { .compatible = "usb1da0,55a1", .data = &parade_ps5511_data, },
{ .compatible = "usb2109,817", .data = &vialab_vl817_data, },
{ .compatible = "usb2109,2817", .data = &vialab_vl817_data, },
{ .compatible = "usb20b1,0013", .data = &xmos_xvf3500_data, },
+ { .compatible = "usb3431,6241", .data = &corechips_sl6341_data, },
+ { .compatible = "usb3431,6341", .data = &corechips_sl6341_data, },
+ { .compatible = "usb5986,1198", .data = &bison_intcamera_data, },
{}
};
diff --git a/drivers/usb/misc/onboard_usb_dev_pdevs.c b/drivers/usb/misc/onboard_usb_dev_pdevs.c
index 722504752ce3..cdb6949e18ec 100644
--- a/drivers/usb/misc/onboard_usb_dev_pdevs.c
+++ b/drivers/usb/misc/onboard_usb_dev_pdevs.c
@@ -109,7 +109,7 @@ void onboard_dev_create_pdevs(struct usb_device *parent_hub, struct list_head *p
goto node_put;
}
- pdle = kzalloc(sizeof(*pdle), GFP_KERNEL);
+ pdle = kzalloc_obj(*pdle);
if (!pdle) {
of_platform_device_destroy(&pdev->dev, NULL);
goto node_put;
diff --git a/drivers/usb/misc/qcom_eud.c b/drivers/usb/misc/qcom_eud.c
index 83079c414b4f..926419ca560f 100644
--- a/drivers/usb/misc/qcom_eud.c
+++ b/drivers/usb/misc/qcom_eud.c
@@ -15,6 +15,7 @@
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/usb/role.h>
+#include <linux/firmware/qcom/qcom_scm.h>
#define EUD_REG_INT1_EN_MASK 0x0024
#define EUD_REG_INT_STATUS_1 0x0044
@@ -34,7 +35,7 @@ struct eud_chip {
struct device *dev;
struct usb_role_switch *role_sw;
void __iomem *base;
- void __iomem *mode_mgr;
+ phys_addr_t mode_mgr;
unsigned int int_status;
int irq;
bool enabled;
@@ -43,18 +44,29 @@ struct eud_chip {
static int enable_eud(struct eud_chip *priv)
{
+ int ret;
+
+ ret = qcom_scm_io_writel(priv->mode_mgr + EUD_REG_EUD_EN2, 1);
+ if (ret)
+ return ret;
+
writel(EUD_ENABLE, priv->base + EUD_REG_CSR_EUD_EN);
writel(EUD_INT_VBUS | EUD_INT_SAFE_MODE,
priv->base + EUD_REG_INT1_EN_MASK);
- writel(1, priv->mode_mgr + EUD_REG_EUD_EN2);
return usb_role_switch_set_role(priv->role_sw, USB_ROLE_DEVICE);
}
-static void disable_eud(struct eud_chip *priv)
+static int disable_eud(struct eud_chip *priv)
{
+ int ret;
+
+ ret = qcom_scm_io_writel(priv->mode_mgr + EUD_REG_EUD_EN2, 0);
+ if (ret)
+ return ret;
+
writel(0, priv->base + EUD_REG_CSR_EUD_EN);
- writel(0, priv->mode_mgr + EUD_REG_EUD_EN2);
+ return 0;
}
static ssize_t enable_show(struct device *dev,
@@ -82,11 +94,12 @@ static ssize_t enable_store(struct device *dev,
chip->enabled = enable;
else
disable_eud(chip);
+
} else {
- disable_eud(chip);
+ ret = disable_eud(chip);
}
- return count;
+ return ret < 0 ? ret : count;
}
static DEVICE_ATTR_RW(enable);
@@ -178,6 +191,7 @@ static void eud_role_switch_release(void *data)
static int eud_probe(struct platform_device *pdev)
{
struct eud_chip *chip;
+ struct resource *res;
int ret;
chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
@@ -193,16 +207,16 @@ static int eud_probe(struct platform_device *pdev)
ret = devm_add_action_or_reset(chip->dev, eud_role_switch_release, chip);
if (ret)
- return dev_err_probe(chip->dev, ret,
- "failed to add role switch release action\n");
+ return ret;
chip->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(chip->base))
return PTR_ERR(chip->base);
- chip->mode_mgr = devm_platform_ioremap_resource(pdev, 1);
- if (IS_ERR(chip->mode_mgr))
- return PTR_ERR(chip->mode_mgr);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!res)
+ return -ENODEV;
+ chip->mode_mgr = res->start;
chip->irq = platform_get_irq(pdev, 0);
if (chip->irq < 0)
diff --git a/drivers/usb/misc/sisusbvga/sisusbvga.c b/drivers/usb/misc/sisusbvga/sisusbvga.c
index febf34f9f049..3e75a7c24828 100644
--- a/drivers/usb/misc/sisusbvga/sisusbvga.c
+++ b/drivers/usb/misc/sisusbvga/sisusbvga.c
@@ -2797,7 +2797,7 @@ static int sisusb_probe(struct usb_interface *intf,
dev->devnum);
/* Allocate memory for our private */
- sisusb = kzalloc(sizeof(*sisusb), GFP_KERNEL);
+ sisusb = kzalloc_obj(*sisusb);
if (!sisusb)
return -ENOMEM;
diff --git a/drivers/usb/misc/trancevibrator.c b/drivers/usb/misc/trancevibrator.c
index 26baba3ab7d7..37f6b79889a6 100644
--- a/drivers/usb/misc/trancevibrator.c
+++ b/drivers/usb/misc/trancevibrator.c
@@ -86,13 +86,13 @@ static int tv_probe(struct usb_interface *interface,
struct trancevibrator *dev;
int retval;
- dev = kzalloc(sizeof(struct trancevibrator), GFP_KERNEL);
+ dev = kzalloc_obj(struct trancevibrator);
if (!dev) {
retval = -ENOMEM;
goto error;
}
- dev->udev = usb_get_dev(udev);
+ dev->udev = udev;
usb_set_intfdata(interface, dev);
return 0;
@@ -108,7 +108,6 @@ static void tv_disconnect(struct usb_interface *interface)
dev = usb_get_intfdata (interface);
usb_set_intfdata(interface, NULL);
- usb_put_dev(dev->udev);
kfree(dev);
}
diff --git a/drivers/usb/misc/usb-ljca.c b/drivers/usb/misc/usb-ljca.c
index c562630d862c..c60121faa3da 100644
--- a/drivers/usb/misc/usb-ljca.c
+++ b/drivers/usb/misc/usb-ljca.c
@@ -164,28 +164,39 @@ struct ljca_match_ids_walk_data {
struct acpi_device *adev;
};
+/*
+ * ACPI hardware IDs for LJCA client devices.
+ *
+ * [1] Some BIOS implementations use these IDs for denoting LJCA client devices
+ * even though the IDs have been allocated for USBIO. This isn't a problem
+ * as the usb-ljca driver is probed based on the USB device's vendor and
+ * product IDs and its client drivers are probed based on auxiliary device
+ * names, not these ACPI _HIDs. List of such systems:
+ *
+ * Dell Precision 5490
+ */
static const struct acpi_device_id ljca_gpio_hids[] = {
- { "INTC1074" },
- { "INTC1096" },
- { "INTC100B" },
- { "INTC10D1" },
- { "INTC10B5" },
+ { "INTC100B" }, /* RPL LJCA GPIO */
+ { "INTC1074" }, /* CVF LJCA GPIO */
+ { "INTC1096" }, /* ADL LJCA GPIO */
+ { "INTC10B5" }, /* LNL LJCA GPIO */
+ { "INTC10D1" }, /* MTL (CVF VSC) USBIO GPIO [1] */
{},
};
static const struct acpi_device_id ljca_i2c_hids[] = {
- { "INTC1075" },
- { "INTC1097" },
- { "INTC100C" },
- { "INTC10D2" },
+ { "INTC100C" }, /* RPL LJCA I2C */
+ { "INTC1075" }, /* CVF LJCA I2C */
+ { "INTC1097" }, /* ADL LJCA I2C */
+ { "INTC10D2" }, /* MTL (CVF VSC) USBIO I2C [1] */
{},
};
static const struct acpi_device_id ljca_spi_hids[] = {
- { "INTC1091" },
- { "INTC1098" },
- { "INTC100D" },
- { "INTC10D3" },
+ { "INTC100D" }, /* RPL LJCA SPI */
+ { "INTC1091" }, /* TGL/ADL LJCA SPI */
+ { "INTC1098" }, /* ADL LJCA SPI */
+ { "INTC10D3" }, /* MTL (CVF VSC) USBIO SPI [1] */
{},
};
@@ -518,7 +529,7 @@ static int ljca_new_client_device(struct ljca_adapter *adap, u8 type, u8 id,
struct ljca_client *client;
int ret;
- client = kzalloc(sizeof *client, GFP_KERNEL);
+ client = kzalloc_obj(*client);
if (!client) {
kfree(data);
return -ENOMEM;
@@ -586,7 +597,7 @@ static int ljca_enumerate_gpio(struct ljca_adapter *adap)
return -EINVAL;
/* construct platform data */
- gpio_info = kzalloc(sizeof *gpio_info, GFP_KERNEL);
+ gpio_info = kzalloc_obj(*gpio_info);
if (!gpio_info)
return -ENOMEM;
gpio_info->num = gpio_num;
@@ -619,7 +630,7 @@ static int ljca_enumerate_i2c(struct ljca_adapter *adap)
for (i = 0; i < desc->num; i++) {
/* construct platform data */
- i2c_info = kzalloc(sizeof *i2c_info, GFP_KERNEL);
+ i2c_info = kzalloc_obj(*i2c_info);
if (!i2c_info)
return -ENOMEM;
@@ -658,7 +669,7 @@ static int ljca_enumerate_spi(struct ljca_adapter *adap)
for (i = 0; i < desc->num; i++) {
/* construct platform data */
- spi_info = kzalloc(sizeof *spi_info, GFP_KERNEL);
+ spi_info = kzalloc_obj(*spi_info);
if (!spi_info)
return -ENOMEM;
@@ -765,7 +776,7 @@ static int ljca_probe(struct usb_interface *interface,
init_completion(&adap->cmd_completion);
INIT_LIST_HEAD(&adap->client_list);
- adap->intf = usb_get_intf(interface);
+ adap->intf = interface;
adap->usb_dev = usb_dev;
adap->dev = dev;
@@ -776,7 +787,7 @@ static int ljca_probe(struct usb_interface *interface,
ret = usb_find_common_endpoints(alt, &ep_in, &ep_out, NULL, NULL);
if (ret) {
dev_err(dev, "bulk endpoints not found\n");
- goto err_put;
+ goto err_destroy_mutex;
}
adap->rx_pipe = usb_rcvbulkpipe(usb_dev, usb_endpoint_num(ep_in));
adap->tx_pipe = usb_sndbulkpipe(usb_dev, usb_endpoint_num(ep_out));
@@ -786,14 +797,14 @@ static int ljca_probe(struct usb_interface *interface,
adap->rx_buf = devm_kzalloc(dev, adap->rx_len, GFP_KERNEL);
if (!adap->rx_buf) {
ret = -ENOMEM;
- goto err_put;
+ goto err_destroy_mutex;
}
/* alloc rx urb */
adap->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!adap->rx_urb) {
ret = -ENOMEM;
- goto err_put;
+ goto err_destroy_mutex;
}
usb_fill_bulk_urb(adap->rx_urb, usb_dev, adap->rx_pipe,
adap->rx_buf, adap->rx_len, ljca_recv, adap);
@@ -825,10 +836,7 @@ static int ljca_probe(struct usb_interface *interface,
err_free:
usb_free_urb(adap->rx_urb);
-
-err_put:
- usb_put_intf(adap->intf);
-
+err_destroy_mutex:
mutex_destroy(&adap->mutex);
return ret;
@@ -853,8 +861,6 @@ static void ljca_disconnect(struct usb_interface *interface)
usb_free_urb(adap->rx_urb);
- usb_put_intf(adap->intf);
-
mutex_destroy(&adap->mutex);
}
@@ -891,7 +897,7 @@ static struct usb_driver ljca_driver = {
};
module_usb_driver(ljca_driver);
-MODULE_AUTHOR("Wentong Wu <wentong.wu@intel.com>");
+MODULE_AUTHOR("Wentong Wu");
MODULE_AUTHOR("Zhifeng Wang <zhifeng.wang@intel.com>");
MODULE_AUTHOR("Lixu Zhang <lixu.zhang@intel.com>");
MODULE_DESCRIPTION("Intel La Jolla Cove Adapter USB driver");
diff --git a/drivers/usb/misc/usb251xb.c b/drivers/usb/misc/usb251xb.c
index e24cdb667307..7c0778631bea 100644
--- a/drivers/usb/misc/usb251xb.c
+++ b/drivers/usb/misc/usb251xb.c
@@ -17,6 +17,7 @@
#include <linux/module.h>
#include <linux/nls.h>
#include <linux/of.h>
+#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
@@ -242,15 +243,19 @@ static int usb251xb_check_dev_children(struct device *dev, void *child)
static int usb251x_check_gpio_chip(struct usb251xb *hub)
{
struct gpio_chip *gc = gpiod_to_chip(hub->gpio_reset);
- struct i2c_adapter *adap = hub->i2c->adapter;
+ struct i2c_adapter *adap;
int ret;
+ if (!hub->i2c)
+ return 0;
+
if (!hub->gpio_reset)
return 0;
if (!gc)
return -EINVAL;
+ adap = hub->i2c->adapter;
ret = usb251xb_check_dev_children(&adap->dev, gc->parent);
if (ret) {
dev_err(hub->dev, "Reset GPIO chip is at the same i2c-bus\n");
@@ -271,7 +276,8 @@ static void usb251xb_reset(struct usb251xb *hub)
if (!hub->gpio_reset)
return;
- i2c_lock_bus(hub->i2c->adapter, I2C_LOCK_SEGMENT);
+ if (hub->i2c)
+ i2c_lock_bus(hub->i2c->adapter, I2C_LOCK_SEGMENT);
gpiod_set_value_cansleep(hub->gpio_reset, 1);
usleep_range(1, 10); /* >=1us RESET_N asserted */
@@ -280,7 +286,8 @@ static void usb251xb_reset(struct usb251xb *hub)
/* wait for hub recovery/stabilization */
usleep_range(500, 750); /* >=500us after RESET_N deasserted */
- i2c_unlock_bus(hub->i2c->adapter, I2C_LOCK_SEGMENT);
+ if (hub->i2c)
+ i2c_unlock_bus(hub->i2c->adapter, I2C_LOCK_SEGMENT);
}
static int usb251xb_connect(struct usb251xb *hub)
@@ -289,6 +296,12 @@ static int usb251xb_connect(struct usb251xb *hub)
int err, i;
char i2c_wb[USB251XB_I2C_REG_SZ];
+ if (!hub->i2c) {
+ usb251xb_reset(hub);
+ dev_info(dev, "hub is put in default configuration.\n");
+ return 0;
+ }
+
memset(i2c_wb, 0, USB251XB_I2C_REG_SZ);
if (hub->skip_config) {
@@ -636,10 +649,8 @@ static int usb251xb_probe(struct usb251xb *hub)
if (np && usb_data) {
err = usb251xb_get_ofdata(hub, usb_data);
- if (err) {
- dev_err(dev, "failed to get ofdata: %d\n", err);
- return err;
- }
+ if (err)
+ return dev_err_probe(dev, err, "failed to get ofdata\n");
}
/*
@@ -700,18 +711,13 @@ static int usb251xb_i2c_probe(struct i2c_client *i2c)
return usb251xb_probe(hub);
}
-static int __maybe_unused usb251xb_suspend(struct device *dev)
+static int usb251xb_suspend(struct usb251xb *hub)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct usb251xb *hub = i2c_get_clientdata(client);
-
return regulator_disable(hub->vdd);
}
-static int __maybe_unused usb251xb_resume(struct device *dev)
+static int usb251xb_resume(struct usb251xb *hub)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct usb251xb *hub = i2c_get_clientdata(client);
int err;
err = regulator_enable(hub->vdd);
@@ -721,7 +727,23 @@ static int __maybe_unused usb251xb_resume(struct device *dev)
return usb251xb_connect(hub);
}
-static SIMPLE_DEV_PM_OPS(usb251xb_pm_ops, usb251xb_suspend, usb251xb_resume);
+static int usb251xb_i2c_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct usb251xb *hub = i2c_get_clientdata(client);
+
+ return usb251xb_suspend(hub);
+}
+
+static int usb251xb_i2c_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct usb251xb *hub = i2c_get_clientdata(client);
+
+ return usb251xb_resume(hub);
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(usb251xb_i2c_pm_ops, usb251xb_i2c_suspend, usb251xb_i2c_resume);
static const struct i2c_device_id usb251xb_id[] = {
{ "usb2422" },
@@ -741,13 +763,71 @@ static struct i2c_driver usb251xb_i2c_driver = {
.driver = {
.name = DRIVER_NAME,
.of_match_table = usb251xb_of_match,
- .pm = &usb251xb_pm_ops,
+ .pm = pm_sleep_ptr(&usb251xb_i2c_pm_ops),
},
.probe = usb251xb_i2c_probe,
.id_table = usb251xb_id,
};
-module_i2c_driver(usb251xb_i2c_driver);
+static int usb251xb_plat_probe(struct platform_device *pdev)
+{
+ struct usb251xb *hub;
+
+ hub = devm_kzalloc(&pdev->dev, sizeof(*hub), GFP_KERNEL);
+ if (!hub)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, hub);
+ hub->dev = &pdev->dev;
+
+ return usb251xb_probe(hub);
+}
+
+static int usb251xb_plat_suspend(struct device *dev)
+{
+ return usb251xb_suspend(dev_get_drvdata(dev));
+}
+
+static int usb251xb_plat_resume(struct device *dev)
+{
+ return usb251xb_resume(dev_get_drvdata(dev));
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(usb251xb_plat_pm_ops, usb251xb_plat_suspend, usb251xb_plat_resume);
+
+static struct platform_driver usb251xb_plat_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .of_match_table = usb251xb_of_match,
+ .pm = pm_sleep_ptr(&usb251xb_plat_pm_ops),
+ },
+ .probe = usb251xb_plat_probe,
+};
+
+static int __init usb251xb_init(void)
+{
+ int err;
+
+ err = i2c_add_driver(&usb251xb_i2c_driver);
+ if (err)
+ return err;
+
+ err = platform_driver_register(&usb251xb_plat_driver);
+ if (err) {
+ i2c_del_driver(&usb251xb_i2c_driver);
+ return err;
+ }
+
+ return 0;
+}
+module_init(usb251xb_init);
+
+static void __exit usb251xb_exit(void)
+{
+ platform_driver_unregister(&usb251xb_plat_driver);
+ i2c_del_driver(&usb251xb_i2c_driver);
+}
+module_exit(usb251xb_exit);
MODULE_AUTHOR("Richard Leitner <richard.leitner@skidata.com>");
MODULE_DESCRIPTION("USB251x/xBi USB 2.0 Hub Controller Driver");
diff --git a/drivers/usb/misc/usbio.c b/drivers/usb/misc/usbio.c
new file mode 100644
index 000000000000..02d1e0760f0c
--- /dev/null
+++ b/drivers/usb/misc/usbio.c
@@ -0,0 +1,752 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Intel USBIO Bridge driver
+ *
+ * Copyright (c) 2025 Intel Corporation.
+ * Copyright (c) 2025 Red Hat, Inc.
+ */
+
+#include <linux/acpi.h>
+#include <linux/auxiliary_bus.h>
+#include <linux/byteorder/generic.h>
+#include <linux/cleanup.h>
+#include <linux/completion.h>
+#include <linux/dev_printk.h>
+#include <linux/device.h>
+#include <linux/lockdep.h>
+#include <linux/mutex.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/usb.h>
+#include <linux/usb/usbio.h>
+
+/*************************************
+ * USBIO Bridge Protocol Definitions *
+ *************************************/
+
+/* USBIO Control Commands */
+#define USBIO_CTRLCMD_PROTVER 0
+#define USBIO_CTRLCMD_FWVER 1
+#define USBIO_CTRLCMD_HS 2
+#define USBIO_CTRLCMD_ENUMGPIO 16
+#define USBIO_CTRLCMD_ENUMI2C 17
+
+/* USBIO Packet Flags */
+#define USBIO_PKTFLAG_ACK BIT(0)
+#define USBIO_PKTFLAG_RSP BIT(1)
+#define USBIO_PKTFLAG_CMP BIT(2)
+#define USBIO_PKTFLAG_ERR BIT(3)
+
+#define USBIO_PKTFLAGS_REQRESP (USBIO_PKTFLAG_CMP | USBIO_PKTFLAG_ACK)
+
+#define USBIO_CTRLXFER_TIMEOUT 0
+#define USBIO_BULKXFER_TIMEOUT 100
+
+struct usbio_protver {
+ u8 ver;
+} __packed;
+
+struct usbio_fwver {
+ u8 major;
+ u8 minor;
+ __le16 patch;
+ __le16 build;
+} __packed;
+
+/***********************************
+ * USBIO Bridge Device Definitions *
+ ***********************************/
+
+/**
+ * struct usbio_device - the usb device exposing IOs
+ *
+ * @dev: the device in the usb interface
+ * @udev: the detected usb device
+ * @intf: the usb interface
+ * @quirks: quirks
+ * @ctrl_mutex: protects ctrl_buf
+ * @ctrl_pipe: the control transfer pipe
+ * @ctrlbuf_len: the size of the control transfer pipe
+ * @ctrlbuf: the buffer used for control transfers
+ * @bulk_mutex: protects tx_buf, rx_buf and split bulk-transfers getting interrupted
+ * @tx_pipe: the bulk out pipe
+ * @txbuf_len: the size of the bulk out pipe
+ * @txbuf: the buffer used for bulk out transfers
+ * @rx_pipe: the bulk in pipe
+ * @rxbuf_len: the size of the bulk in pipe
+ * @rxdat_len: the data length at rx buffer
+ * @rxbuf: the buffer used for bulk in transfers
+ * @urb: the urb to read bulk pipe
+ * @done: completion object as request is done
+ * @cli_list: device's client list
+ * @nr_gpio_banks: Number of GPIO banks
+ * @gpios: GPIO bank descriptors
+ * @nr_gpio_banks: Number of I2C busses
+ * @gpios: I2C bank descriptors
+ */
+struct usbio_device {
+ struct device *dev;
+ struct usb_device *udev;
+ struct usb_interface *intf;
+ unsigned long quirks;
+
+ struct mutex ctrl_mutex;
+ unsigned int ctrl_pipe;
+ u16 ctrlbuf_len;
+ void *ctrlbuf;
+
+ struct mutex bulk_mutex;
+ unsigned int tx_pipe;
+ u16 txbuf_len;
+ void *txbuf;
+
+ unsigned int rx_pipe;
+ u16 rxbuf_len;
+ u16 rxdat_len;
+ void *rxbuf;
+ struct urb *urb;
+
+ struct completion done;
+
+ struct list_head cli_list;
+
+ unsigned int nr_gpio_banks;
+ struct usbio_gpio_bank_desc gpios[USBIO_MAX_GPIOBANKS];
+
+ unsigned int nr_i2c_buses;
+ struct usbio_i2c_bus_desc i2cs[USBIO_MAX_I2CBUSES];
+};
+
+/**
+ * struct usbio_client - represents a usbio client
+ *
+ * @auxdev: auxiliary device object
+ * @mutex: protects @bridge
+ * @bridge: usbio bridge who service the client
+ * @link: usbio bridge clients list member
+ */
+struct usbio_client {
+ struct auxiliary_device auxdev;
+ struct mutex mutex;
+ struct usbio_device *bridge;
+ struct list_head link;
+};
+
+#define adev_to_client(adev) container_of_const(adev, struct usbio_client, auxdev)
+
+static int usbio_ctrl_msg(struct usbio_device *usbio, u8 type, u8 cmd,
+ const void *obuf, u16 obuf_len, void *ibuf, u16 ibuf_len)
+{
+ u8 request = USB_TYPE_VENDOR | USB_RECIP_DEVICE;
+ struct usbio_ctrl_packet *cpkt;
+ unsigned int pipe;
+ u16 cpkt_len;
+ int ret;
+
+ lockdep_assert_held(&usbio->ctrl_mutex);
+
+ if ((obuf_len > (usbio->ctrlbuf_len - sizeof(*cpkt))) ||
+ (ibuf_len > (usbio->ctrlbuf_len - sizeof(*cpkt))))
+ return -EMSGSIZE;
+
+ /* Prepare Control Packet Header */
+ cpkt = usbio->ctrlbuf;
+ cpkt->header.type = type;
+ cpkt->header.cmd = cmd;
+ if (type == USBIO_PKTTYPE_CTRL || ibuf_len)
+ cpkt->header.flags = USBIO_PKTFLAGS_REQRESP;
+ else
+ cpkt->header.flags = USBIO_PKTFLAG_CMP;
+ cpkt->len = obuf_len;
+
+ /* Copy the data */
+ memcpy(cpkt->data, obuf, obuf_len);
+
+ pipe = usb_sndctrlpipe(usbio->udev, usbio->ctrl_pipe);
+ cpkt_len = sizeof(*cpkt) + obuf_len;
+ ret = usb_control_msg(usbio->udev, pipe, 0, request | USB_DIR_OUT, 0, 0,
+ cpkt, cpkt_len, USBIO_CTRLXFER_TIMEOUT);
+ dev_dbg(usbio->dev, "control out %d hdr %*phN data %*phN\n", ret,
+ (int)sizeof(*cpkt), cpkt, (int)cpkt->len, cpkt->data);
+
+ if (ret != cpkt_len) {
+ dev_err(usbio->dev, "USB control out failed: %d\n", ret);
+ return (ret < 0) ? ret : -EPROTO;
+ }
+
+ if (!(cpkt->header.flags & USBIO_PKTFLAG_ACK))
+ return 0;
+
+ pipe = usb_rcvctrlpipe(usbio->udev, usbio->ctrl_pipe);
+ cpkt_len = sizeof(*cpkt) + ibuf_len;
+ ret = usb_control_msg(usbio->udev, pipe, 0, request | USB_DIR_IN, 0, 0,
+ cpkt, cpkt_len, USBIO_CTRLXFER_TIMEOUT);
+ dev_dbg(usbio->dev, "control in %d hdr %*phN data %*phN\n", ret,
+ (int)sizeof(*cpkt), cpkt, (int)cpkt->len, cpkt->data);
+
+ if (ret < sizeof(*cpkt)) {
+ dev_err(usbio->dev, "USB control in failed: %d\n", ret);
+ return (ret < 0) ? ret : -EPROTO;
+ }
+
+ if (cpkt->header.type != type || cpkt->header.cmd != cmd ||
+ !(cpkt->header.flags & USBIO_PKTFLAG_RSP)) {
+ dev_err(usbio->dev, "Unexpected reply type: %u, cmd: %u, flags: %u\n",
+ cpkt->header.type, cpkt->header.cmd, cpkt->header.flags);
+ return -EPROTO;
+ }
+
+ if (cpkt->header.flags & USBIO_PKTFLAG_ERR)
+ return -EREMOTEIO;
+
+ if (ibuf_len < cpkt->len)
+ return -ENOSPC;
+
+ memcpy(ibuf, cpkt->data, cpkt->len);
+
+ return cpkt->len;
+}
+
+int usbio_control_msg(struct auxiliary_device *adev, u8 type, u8 cmd,
+ const void *obuf, u16 obuf_len, void *ibuf, u16 ibuf_len)
+{
+ struct usbio_client *client = adev_to_client(adev);
+ struct usbio_device *usbio;
+ int ret;
+
+ guard(mutex)(&client->mutex);
+
+ usbio = client->bridge;
+ if (!usbio)
+ return -ENODEV; /* Disconnected */
+
+ ret = usb_autopm_get_interface(usbio->intf);
+ if (ret)
+ return ret;
+
+ mutex_lock(&usbio->ctrl_mutex);
+
+ ret = usbio_ctrl_msg(client->bridge, type, cmd, obuf, obuf_len, ibuf, ibuf_len);
+
+ mutex_unlock(&usbio->ctrl_mutex);
+ usb_autopm_put_interface(usbio->intf);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(usbio_control_msg, "USBIO");
+
+static void usbio_bulk_recv(struct urb *urb)
+{
+ struct usbio_bulk_packet *bpkt = urb->transfer_buffer;
+ struct usbio_device *usbio = urb->context;
+
+ if (!urb->status) {
+ if (bpkt->header.flags & USBIO_PKTFLAG_RSP) {
+ usbio->rxdat_len = urb->actual_length;
+ complete(&usbio->done);
+ }
+ } else if (urb->status != -ENOENT) {
+ dev_err(usbio->dev, "Bulk in error %d\n", urb->status);
+ }
+
+ usb_submit_urb(usbio->urb, GFP_ATOMIC);
+}
+
+int usbio_bulk_msg(struct auxiliary_device *adev, u8 type, u8 cmd, bool last,
+ const void *obuf, u16 obuf_len, void *ibuf, u16 ibuf_len)
+{
+ struct usbio_client *client = adev_to_client(adev);
+ struct usbio_device *usbio = client->bridge;
+ struct usbio_bulk_packet *bpkt;
+ int ret, act = 0;
+ u16 bpkt_len;
+
+ lockdep_assert_held(&client->mutex);
+ lockdep_assert_held(&usbio->bulk_mutex);
+
+ if ((obuf_len > (usbio->txbuf_len - sizeof(*bpkt))) ||
+ (ibuf_len > (usbio->txbuf_len - sizeof(*bpkt))))
+ return -EMSGSIZE;
+
+ if (ibuf_len)
+ reinit_completion(&usbio->done);
+
+ /* If no data to send, skip to read */
+ if (!obuf_len)
+ goto read;
+
+ /* Prepare Bulk Packet Header */
+ bpkt = usbio->txbuf;
+ bpkt->header.type = type;
+ bpkt->header.cmd = cmd;
+ if (!last)
+ bpkt->header.flags = 0;
+ else if (ibuf_len)
+ bpkt->header.flags = USBIO_PKTFLAGS_REQRESP;
+ else
+ bpkt->header.flags = USBIO_PKTFLAG_CMP;
+ bpkt->len = cpu_to_le16(obuf_len);
+
+ /* Copy the data */
+ memcpy(bpkt->data, obuf, obuf_len);
+
+ bpkt_len = sizeof(*bpkt) + obuf_len;
+ ret = usb_bulk_msg(usbio->udev, usbio->tx_pipe, bpkt, bpkt_len, &act,
+ USBIO_BULKXFER_TIMEOUT);
+ dev_dbg(usbio->dev, "bulk out %d hdr %*phN data %*phN\n", act,
+ (int)sizeof(*bpkt), bpkt, obuf_len, bpkt->data);
+
+ if (ret || act != bpkt_len) {
+ dev_err(usbio->dev, "Bulk out failed: %d\n", ret);
+ return ret ?: -EPROTO;
+ }
+
+ if (!(bpkt->header.flags & USBIO_PKTFLAG_ACK))
+ return obuf_len;
+
+read:
+ ret = wait_for_completion_timeout(&usbio->done, USBIO_BULKXFER_TIMEOUT);
+ if (ret <= 0) {
+ dev_err(usbio->dev, "Bulk in wait failed: %d\n", ret);
+ return ret ?: -ETIMEDOUT;
+ }
+
+ act = usbio->rxdat_len;
+ bpkt = usbio->rxbuf;
+ bpkt_len = le16_to_cpu(bpkt->len);
+ dev_dbg(usbio->dev, "bulk in %d hdr %*phN data %*phN\n", act,
+ (int)sizeof(*bpkt), bpkt, bpkt_len, bpkt->data);
+
+ /*
+ * Unsupported bulk commands get only an usbio_packet_header with
+ * the error flag set as reply. Return -EPIPE for this case.
+ */
+ if (act == sizeof(struct usbio_packet_header) &&
+ (bpkt->header.flags & USBIO_PKTFLAG_ERR))
+ return -EPIPE;
+
+ if (act < sizeof(*bpkt)) {
+ dev_err(usbio->dev, "Bulk in short read: %d\n", act);
+ return -EPROTO;
+ }
+
+ if (bpkt->header.type != type || bpkt->header.cmd != cmd ||
+ !(bpkt->header.flags & USBIO_PKTFLAG_RSP)) {
+ dev_err(usbio->dev,
+ "Unexpected bulk in type 0x%02x cmd 0x%02x flags 0x%02x\n",
+ bpkt->header.type, bpkt->header.cmd, bpkt->header.flags);
+ return -EPROTO;
+ }
+
+ if (bpkt->header.flags & USBIO_PKTFLAG_ERR)
+ return -EREMOTEIO;
+
+ if (ibuf_len < bpkt_len)
+ return -ENOSPC;
+
+ memcpy(ibuf, bpkt->data, bpkt_len);
+
+ return bpkt_len;
+}
+EXPORT_SYMBOL_NS_GPL(usbio_bulk_msg, "USBIO");
+
+int usbio_acquire(struct auxiliary_device *adev)
+{
+ struct usbio_client *client = adev_to_client(adev);
+ struct usbio_device *usbio;
+ int ret;
+
+ mutex_lock(&client->mutex);
+
+ usbio = client->bridge;
+ if (!usbio) {
+ ret = -ENODEV; /* Disconnected */
+ goto err_unlock;
+ }
+
+ ret = usb_autopm_get_interface(usbio->intf);
+ if (ret)
+ goto err_unlock;
+
+ mutex_lock(&usbio->bulk_mutex);
+
+ /* Leave client locked until release to avoid abba deadlock issues */
+ return 0;
+
+err_unlock:
+ mutex_unlock(&client->mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(usbio_acquire, "USBIO");
+
+void usbio_release(struct auxiliary_device *adev)
+{
+ struct usbio_client *client = adev_to_client(adev);
+ struct usbio_device *usbio = client->bridge;
+
+ lockdep_assert_held(&client->mutex);
+
+ mutex_unlock(&usbio->bulk_mutex);
+ usb_autopm_put_interface(usbio->intf);
+ mutex_unlock(&client->mutex);
+}
+EXPORT_SYMBOL_NS_GPL(usbio_release, "USBIO");
+
+void usbio_get_txrxbuf_len(struct auxiliary_device *adev, u16 *txbuf_len, u16 *rxbuf_len)
+{
+ struct usbio_client *client = adev_to_client(adev);
+ struct usbio_device *usbio;
+
+ guard(mutex)(&client->mutex);
+
+ usbio = client->bridge;
+ if (!usbio)
+ return; /* Disconnected */
+
+ *txbuf_len = usbio->txbuf_len;
+ *rxbuf_len = usbio->rxbuf_len;
+}
+EXPORT_SYMBOL_NS_GPL(usbio_get_txrxbuf_len, "USBIO");
+
+unsigned long usbio_get_quirks(struct auxiliary_device *adev)
+{
+ struct usbio_client *client = adev_to_client(adev);
+ struct usbio_device *usbio;
+
+ guard(mutex)(&client->mutex);
+
+ usbio = client->bridge;
+ if (!usbio)
+ return 0; /* Disconnected */
+
+ return usbio->quirks;
+}
+EXPORT_SYMBOL_NS_GPL(usbio_get_quirks, "USBIO");
+
+static void usbio_auxdev_release(struct device *dev)
+{
+ struct auxiliary_device *adev = to_auxiliary_dev(dev);
+ struct usbio_client *client = adev_to_client(adev);
+
+ mutex_destroy(&client->mutex);
+ kfree(client);
+}
+
+static int usbio_add_client(struct usbio_device *usbio, char *name, u8 id, void *data)
+{
+ struct usbio_client *client;
+ struct auxiliary_device *adev;
+ int ret;
+
+ client = kzalloc_obj(*client);
+ if (!client)
+ return -ENOMEM;
+
+ mutex_init(&client->mutex);
+ client->bridge = usbio;
+ adev = &client->auxdev;
+ adev->name = name;
+ adev->id = id;
+
+ adev->dev.parent = usbio->dev;
+ adev->dev.platform_data = data;
+ adev->dev.release = usbio_auxdev_release;
+
+ ret = auxiliary_device_init(adev);
+ if (ret) {
+ usbio_auxdev_release(&adev->dev);
+ return ret;
+ }
+
+ ret = auxiliary_device_add(adev);
+ if (ret) {
+ auxiliary_device_uninit(adev);
+ return ret;
+ }
+
+ list_add_tail(&client->link, &usbio->cli_list);
+
+ return 0;
+}
+
+static int usbio_enum_gpios(struct usbio_device *usbio)
+{
+ struct usbio_gpio_bank_desc *gpio = usbio->gpios;
+
+ dev_dbg(usbio->dev, "GPIO Banks: %d\n", usbio->nr_gpio_banks);
+
+ for (unsigned int i = 0; i < usbio->nr_gpio_banks; i++)
+ dev_dbg(usbio->dev, "\tBank%d[%d] map: %#08x\n",
+ gpio[i].id, gpio[i].pins, gpio[i].bmap);
+
+ usbio_add_client(usbio, USBIO_GPIO_CLIENT, 0, gpio);
+
+ return 0;
+}
+
+static int usbio_enum_i2cs(struct usbio_device *usbio)
+{
+ struct usbio_i2c_bus_desc *i2c = usbio->i2cs;
+
+ dev_dbg(usbio->dev, "I2C Busses: %d\n", usbio->nr_i2c_buses);
+
+ for (unsigned int i = 0; i < usbio->nr_i2c_buses; i++) {
+ dev_dbg(usbio->dev, "\tBus%d caps: %#02x\n", i2c[i].id, i2c[i].caps);
+ usbio_add_client(usbio, USBIO_I2C_CLIENT, i, &i2c[i]);
+ }
+
+ return 0;
+}
+
+static int usbio_suspend(struct usb_interface *intf, pm_message_t msg)
+{
+ struct usbio_device *usbio = usb_get_intfdata(intf);
+
+ usb_kill_urb(usbio->urb);
+
+ return 0;
+}
+
+static int usbio_resume(struct usb_interface *intf)
+{
+ struct usbio_device *usbio = usb_get_intfdata(intf);
+
+ return usb_submit_urb(usbio->urb, GFP_KERNEL);
+}
+
+static void usbio_disconnect(struct usb_interface *intf)
+{
+ struct usbio_device *usbio = usb_get_intfdata(intf);
+ struct usbio_client *client;
+
+ /* Wakeup any clients waiting for a reply */
+ usbio->rxdat_len = 0;
+ complete(&usbio->done);
+
+ /* Let clients know the bridge is gone */
+ list_for_each_entry(client, &usbio->cli_list, link) {
+ mutex_lock(&client->mutex);
+ client->bridge = NULL;
+ mutex_unlock(&client->mutex);
+ }
+
+ /* From here on clients will no longer touch struct usbio_device */
+ usb_kill_urb(usbio->urb);
+ usb_free_urb(usbio->urb);
+
+ list_for_each_entry_reverse(client, &usbio->cli_list, link) {
+ auxiliary_device_delete(&client->auxdev);
+ auxiliary_device_uninit(&client->auxdev);
+ }
+}
+
+static int usbio_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct usb_endpoint_descriptor *ep_in, *ep_out;
+ struct device *dev = &intf->dev;
+ struct usbio_protver protver;
+ struct usbio_device *usbio;
+ struct usbio_fwver fwver;
+ int ret;
+
+ usbio = devm_kzalloc(dev, sizeof(*usbio), GFP_KERNEL);
+ if (!usbio)
+ return -ENOMEM;
+
+ ret = devm_mutex_init(dev, &usbio->ctrl_mutex);
+ if (ret)
+ return ret;
+
+ ret = devm_mutex_init(dev, &usbio->bulk_mutex);
+ if (ret)
+ return ret;
+
+ usbio->dev = dev;
+ usbio->udev = udev;
+ usbio->intf = intf;
+ usbio->quirks = id ? id->driver_info : 0;
+ init_completion(&usbio->done);
+ INIT_LIST_HEAD(&usbio->cli_list);
+ usb_set_intfdata(intf, usbio);
+
+ usbio->ctrl_pipe = usb_endpoint_num(&udev->ep0.desc);
+ usbio->ctrlbuf_len = usb_maxpacket(udev, usbio->ctrl_pipe);
+ usbio->ctrlbuf = devm_kzalloc(dev, usbio->ctrlbuf_len, GFP_KERNEL);
+ if (!usbio->ctrlbuf)
+ return -ENOMEM;
+
+ /* Find the first bulk-in and bulk-out endpoints */
+ ret = usb_find_common_endpoints(intf->cur_altsetting, &ep_in, &ep_out,
+ NULL, NULL);
+ if (ret) {
+ dev_err(dev, "Cannot find bulk endpoints: %d\n", ret);
+ return ret;
+ }
+
+ usbio->tx_pipe = usb_sndbulkpipe(udev, usb_endpoint_num(ep_out));
+
+ if (usbio->quirks & USBIO_QUIRK_BULK_MAXP_63)
+ usbio->txbuf_len = 63;
+ else
+ usbio->txbuf_len = usb_endpoint_maxp(ep_out);
+
+ usbio->txbuf = devm_kzalloc(dev, usbio->txbuf_len, GFP_KERNEL);
+ if (!usbio->txbuf)
+ return -ENOMEM;
+
+ usbio->rx_pipe = usb_rcvbulkpipe(udev, usb_endpoint_num(ep_in));
+
+ if (usbio->quirks & USBIO_QUIRK_BULK_MAXP_63)
+ usbio->rxbuf_len = 63;
+ else
+ usbio->rxbuf_len = usb_endpoint_maxp(ep_in);
+
+ usbio->rxbuf = devm_kzalloc(dev, usbio->rxbuf_len, GFP_KERNEL);
+ if (!usbio->rxbuf)
+ return -ENOMEM;
+
+ usbio->urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!usbio->urb)
+ return -ENOMEM;
+
+ usb_fill_bulk_urb(usbio->urb, udev, usbio->rx_pipe, usbio->rxbuf,
+ usbio->rxbuf_len, usbio_bulk_recv, usbio);
+ ret = usb_submit_urb(usbio->urb, GFP_KERNEL);
+ if (ret) {
+ dev_err_probe(dev, ret, "Submitting usb urb\n");
+ goto err_free_urb;
+ }
+
+ mutex_lock(&usbio->ctrl_mutex);
+
+ ret = usbio_ctrl_msg(usbio, USBIO_PKTTYPE_CTRL, USBIO_CTRLCMD_HS, NULL, 0, NULL, 0);
+ if (ret < 0)
+ goto err_unlock;
+
+ ret = usbio_ctrl_msg(usbio, USBIO_PKTTYPE_CTRL, USBIO_CTRLCMD_PROTVER, NULL, 0,
+ &protver, sizeof(protver));
+ if (ret < 0)
+ goto err_unlock;
+
+ ret = usbio_ctrl_msg(usbio, USBIO_PKTTYPE_CTRL, USBIO_CTRLCMD_FWVER, NULL, 0,
+ &fwver, sizeof(fwver));
+ if (ret < 0)
+ goto err_unlock;
+
+ ret = usbio_ctrl_msg(usbio, USBIO_PKTTYPE_CTRL, USBIO_CTRLCMD_ENUMGPIO, NULL, 0,
+ usbio->gpios, sizeof(usbio->gpios));
+ if (ret < 0 || ret % sizeof(struct usbio_gpio_bank_desc)) {
+ ret = (ret < 0) ? ret : -EPROTO;
+ goto err_unlock;
+ }
+ usbio->nr_gpio_banks = ret / sizeof(struct usbio_gpio_bank_desc);
+
+ ret = usbio_ctrl_msg(usbio, USBIO_PKTTYPE_CTRL, USBIO_CTRLCMD_ENUMI2C, NULL, 0,
+ usbio->i2cs, sizeof(usbio->i2cs));
+ if (ret < 0 || ret % sizeof(struct usbio_i2c_bus_desc)) {
+ ret = (ret < 0) ? ret : -EPROTO;
+ goto err_unlock;
+ }
+ usbio->nr_i2c_buses = ret / sizeof(struct usbio_i2c_bus_desc);
+
+ mutex_unlock(&usbio->ctrl_mutex);
+
+ dev_dbg(dev, "ProtVer(BCD): %02x FwVer: %d.%d.%d.%d\n",
+ protver.ver, fwver.major, fwver.minor,
+ le16_to_cpu(fwver.patch), le16_to_cpu(fwver.build));
+
+ usbio_enum_gpios(usbio);
+ usbio_enum_i2cs(usbio);
+
+ return 0;
+
+err_unlock:
+ mutex_unlock(&usbio->ctrl_mutex);
+ usb_kill_urb(usbio->urb);
+err_free_urb:
+ usb_free_urb(usbio->urb);
+
+ return ret;
+}
+
+static const struct usb_device_id usbio_table[] = {
+ { USB_DEVICE(0x2ac1, 0x20c1), /* Lattice NX40 */
+ .driver_info = USBIO_QUIRK_I2C_MAX_RW_LEN_52 },
+ { USB_DEVICE(0x2ac1, 0x20c9), /* Lattice NX33 */
+ .driver_info = USBIO_QUIRK_I2C_NO_INIT_ACK | USBIO_QUIRK_I2C_MAX_RW_LEN_52 |
+ USBIO_QUIRK_I2C_ALLOW_400KHZ },
+ { USB_DEVICE(0x2ac1, 0x20cb) }, /* Lattice NX33U */
+ { USB_DEVICE(0x06cb, 0x0701), /* Synaptics Sabre */
+ .driver_info = USBIO_QUIRK_BULK_MAXP_63 | USBIO_QUIRK_I2C_USE_CHUNK_LEN },
+ { }
+};
+MODULE_DEVICE_TABLE(usb, usbio_table);
+
+static struct usb_driver usbio_driver = {
+ .name = "usbio-bridge",
+ .probe = usbio_probe,
+ .disconnect = usbio_disconnect,
+ .suspend = usbio_suspend,
+ .resume = usbio_resume,
+ .id_table = usbio_table,
+ .supports_autosuspend = 1,
+};
+module_usb_driver(usbio_driver);
+
+struct usbio_match_ids_walk_data {
+ struct acpi_device *adev;
+ const struct acpi_device_id *hids;
+ unsigned int id;
+};
+
+static int usbio_match_device_ids(struct acpi_device *adev, void *data)
+{
+ struct usbio_match_ids_walk_data *wd = data;
+ unsigned int id = 0;
+ char *uid;
+
+ if (acpi_match_device_ids(adev, wd->hids))
+ return 0;
+
+ uid = acpi_device_uid(adev);
+ if (uid) {
+ for (int i = 0; i < strlen(uid); i++) {
+ if (!kstrtouint(&uid[i], 10, &id))
+ break;
+ }
+ }
+
+ if (!uid || wd->id == id) {
+ wd->adev = adev;
+ return 1;
+ }
+
+ return 0;
+}
+
+void usbio_acpi_bind(struct auxiliary_device *adev, const struct acpi_device_id *hids)
+{
+ struct device *dev = &adev->dev;
+ struct acpi_device *parent;
+ struct usbio_match_ids_walk_data wd = {
+ .adev = NULL,
+ .hids = hids,
+ .id = adev->id,
+ };
+
+ parent = ACPI_COMPANION(dev->parent);
+ if (!parent)
+ return;
+
+ acpi_dev_for_each_child(parent, usbio_match_device_ids, &wd);
+ if (wd.adev)
+ ACPI_COMPANION_SET(dev, wd.adev);
+}
+EXPORT_SYMBOL_NS_GPL(usbio_acpi_bind, "USBIO");
+
+MODULE_DESCRIPTION("Intel USBIO Bridge driver");
+MODULE_AUTHOR("Israel Cepeda <israel.a.cepeda.lopez@intel.com>");
+MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/misc/usblcd.c b/drivers/usb/misc/usblcd.c
index bb546f624a45..f794695dea9d 100644
--- a/drivers/usb/misc/usblcd.c
+++ b/drivers/usb/misc/usblcd.c
@@ -323,7 +323,7 @@ static int lcd_probe(struct usb_interface *interface,
int retval;
/* allocate memory for our device state and initialize it */
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ dev = kzalloc_obj(*dev);
if (!dev)
return -ENOMEM;
diff --git a/drivers/usb/misc/usbsevseg.c b/drivers/usb/misc/usbsevseg.c
index 546deff754ba..89d25fcef642 100644
--- a/drivers/usb/misc/usbsevseg.c
+++ b/drivers/usb/misc/usbsevseg.c
@@ -308,11 +308,11 @@ static int sevseg_probe(struct usb_interface *interface,
struct usb_sevsegdev *mydev;
int rc = -ENOMEM;
- mydev = kzalloc(sizeof(struct usb_sevsegdev), GFP_KERNEL);
+ mydev = kzalloc_obj(struct usb_sevsegdev);
if (!mydev)
goto error_mem;
- mydev->udev = usb_get_dev(udev);
+ mydev->udev = udev;
mydev->intf = interface;
usb_set_intfdata(interface, mydev);
@@ -338,7 +338,6 @@ static void sevseg_disconnect(struct usb_interface *interface)
mydev = usb_get_intfdata(interface);
usb_set_intfdata(interface, NULL);
- usb_put_dev(mydev->udev);
kfree(mydev);
dev_info(&interface->dev, "USB 7 Segment now disconnected\n");
}
diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c
index 8d379ae835bc..98071b25ac07 100644
--- a/drivers/usb/misc/usbtest.c
+++ b/drivers/usb/misc/usbtest.c
@@ -545,7 +545,7 @@ alloc_sglist(int nents, int max, int vary, struct usbtest_dev *dev, int pipe)
if (max == 0)
return NULL;
- sg = kmalloc_array(nents, sizeof(*sg), GFP_KERNEL);
+ sg = kmalloc_objs(*sg, nents);
if (!sg)
return NULL;
sg_init_table(sg, nents);
@@ -592,7 +592,7 @@ struct sg_timeout {
static void sg_timeout(struct timer_list *t)
{
- struct sg_timeout *timeout = from_timer(timeout, t, timer);
+ struct sg_timeout *timeout = timer_container_of(timeout, t, timer);
usb_sg_cancel(timeout->req);
}
@@ -626,11 +626,11 @@ static int perform_sglist(
mod_timer(&timeout.timer, jiffies +
msecs_to_jiffies(SIMPLE_IO_TIMEOUT));
usb_sg_wait(req);
- if (!del_timer_sync(&timeout.timer))
+ if (!timer_delete_sync(&timeout.timer))
retval = -ETIMEDOUT;
else
retval = req->status;
- destroy_timer_on_stack(&timeout.timer);
+ timer_destroy_on_stack(&timeout.timer);
/* FIXME check resulting data pattern */
@@ -1221,7 +1221,7 @@ test_ctrl_queue(struct usbtest_dev *dev, struct usbtest_param_32 *param)
* as with bulk/intr sglists, sglen is the queue depth; it also
* controls which subtests run (more tests than sglen) or rerun.
*/
- urb = kcalloc(param->sglen, sizeof(struct urb *), GFP_KERNEL);
+ urb = kzalloc_objs(struct urb *, param->sglen);
if (!urb)
return -ENOMEM;
for (i = 0; i < param->sglen; i++) {
@@ -1370,7 +1370,7 @@ test_ctrl_queue(struct usbtest_dev *dev, struct usbtest_param_32 *param)
if (!u)
goto cleanup;
- reqp = kmalloc(sizeof(*reqp), GFP_KERNEL);
+ reqp = kmalloc_obj(*reqp);
if (!reqp)
goto cleanup;
reqp->setup = req;
@@ -1572,7 +1572,7 @@ static int unlink_queued(struct usbtest_dev *dev, int pipe, unsigned num,
memset(buf, 0, size);
/* Allocate and init the urbs we'll queue */
- ctx.urbs = kcalloc(num, sizeof(struct urb *), GFP_KERNEL);
+ ctx.urbs = kzalloc_objs(struct urb *, num);
if (!ctx.urbs)
goto free_buf;
for (i = 0; i < num; i++) {
@@ -2052,7 +2052,7 @@ test_queue(struct usbtest_dev *dev, struct usbtest_param_32 *param,
if (param->sglen > MAX_SGLEN)
return -EINVAL;
- urbs = kcalloc(param->sglen, sizeof(*urbs), GFP_KERNEL);
+ urbs = kzalloc_objs(*urbs, param->sglen);
if (!urbs)
return -ENOMEM;
@@ -2786,7 +2786,7 @@ usbtest_probe(struct usb_interface *intf, const struct usb_device_id *id)
}
#endif
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ dev = kzalloc_obj(*dev);
if (!dev)
return -ENOMEM;
info = (struct usbtest_info *) id->driver_info;
diff --git a/drivers/usb/misc/uss720.c b/drivers/usb/misc/uss720.c
index b26c1d382d59..b7d3c44b970e 100644
--- a/drivers/usb/misc/uss720.c
+++ b/drivers/usb/misc/uss720.c
@@ -134,7 +134,7 @@ static struct uss720_async_request *submit_async_request(struct parport_uss720_p
usbdev = priv->usbdev;
if (!usbdev)
return NULL;
- rq = kzalloc(sizeof(struct uss720_async_request), mem_flags);
+ rq = kzalloc_obj(struct uss720_async_request, mem_flags);
if (!rq)
return NULL;
kref_init(&rq->ref_count);
@@ -147,7 +147,7 @@ static struct uss720_async_request *submit_async_request(struct parport_uss720_p
kref_put(&rq->ref_count, destroy_async);
return NULL;
}
- rq->dr = kmalloc(sizeof(*rq->dr), mem_flags);
+ rq->dr = kmalloc_obj(*rq->dr, mem_flags);
if (!rq->dr) {
kref_put(&rq->ref_count, destroy_async);
return NULL;
@@ -677,35 +677,32 @@ static int uss720_probe(struct usb_interface *intf,
struct parport_uss720_private *priv;
struct parport *pp;
unsigned char reg;
- int ret;
+ int ret = -ENODEV;
dev_dbg(&intf->dev, "probe: vendor id 0x%x, device id 0x%x\n",
le16_to_cpu(usbdev->descriptor.idVendor),
le16_to_cpu(usbdev->descriptor.idProduct));
/* our known interfaces have 3 alternate settings */
- if (intf->num_altsetting != 3) {
- usb_put_dev(usbdev);
- return -ENODEV;
- }
+ if (intf->num_altsetting != 3)
+ goto bail_out_early;
+
ret = usb_set_interface(usbdev, intf->altsetting->desc.bInterfaceNumber, 2);
dev_dbg(&intf->dev, "set interface result %d\n", ret);
interface = intf->cur_altsetting;
- if (interface->desc.bNumEndpoints < 2) {
- usb_put_dev(usbdev);
- return -ENODEV;
- }
+ if (interface->desc.bNumEndpoints < 2)
+ goto bail_out_early;
/*
* Allocate parport interface
*/
- priv = kzalloc(sizeof(struct parport_uss720_private), GFP_KERNEL);
- if (!priv) {
- usb_put_dev(usbdev);
- return -ENOMEM;
- }
+ ret = -ENOMEM;
+ priv = kzalloc_obj(struct parport_uss720_private);
+ if (!priv)
+ goto bail_out_early;
+
priv->pp = NULL;
priv->usbdev = usbdev;
kref_init(&priv->ref_count);
@@ -736,7 +733,7 @@ static int uss720_probe(struct usb_interface *intf,
ret = get_1284_register(pp, 0, &reg, GFP_KERNEL);
dev_dbg(&intf->dev, "reg: %7ph\n", priv->reg);
if (ret < 0)
- return ret;
+ goto probe_abort;
ret = usb_find_last_int_in_endpoint(interface, &epd);
if (!ret) {
@@ -752,6 +749,10 @@ probe_abort:
kill_all_async_requests_priv(priv);
kref_put(&priv->ref_count, destroy_priv);
return -ENODEV;
+
+bail_out_early:
+ usb_put_dev(usbdev);
+ return ret;
}
static void uss720_disconnect(struct usb_interface *intf)
diff --git a/drivers/usb/misc/yurex.c b/drivers/usb/misc/yurex.c
index 70dff0db5354..7a482cdee1e9 100644
--- a/drivers/usb/misc/yurex.c
+++ b/drivers/usb/misc/yurex.c
@@ -197,7 +197,7 @@ static int yurex_probe(struct usb_interface *interface, const struct usb_device_
int res;
/* allocate memory for our device state and initialize it */
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ dev = kzalloc_obj(*dev);
if (!dev)
goto error;
kref_init(&dev->kref);
@@ -272,6 +272,7 @@ static int yurex_probe(struct usb_interface *interface, const struct usb_device_
dev->int_buffer, YUREX_BUF_SIZE, yurex_interrupt,
dev, 1);
dev->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ dev->bbu = -1;
if (usb_submit_urb(dev->urb, GFP_KERNEL)) {
retval = -EIO;
dev_err(&interface->dev, "Could not submitting URB\n");
@@ -280,7 +281,6 @@ static int yurex_probe(struct usb_interface *interface, const struct usb_device_
/* save our data pointer in this interface device */
usb_set_intfdata(interface, dev);
- dev->bbu = -1;
/* we can register the device now, as it is ready */
retval = usb_register_dev(interface, &yurex_class);
diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c
index c93b43f5bc46..687f6a8981f3 100644
--- a/drivers/usb/mon/mon_bin.c
+++ b/drivers/usb/mon/mon_bin.c
@@ -68,18 +68,20 @@
* The magic limit was calculated so that it allows the monitoring
* application to pick data once in two ticks. This way, another application,
* which presumably drives the bus, gets to hog CPU, yet we collect our data.
- * If HZ is 100, a 480 mbit/s bus drives 614 KB every jiffy. USB has an
- * enormous overhead built into the bus protocol, so we need about 1000 KB.
+ *
+ * Originally, for a 480 Mbit/s bus this required a buffer of about 1 MB. For
+ * modern 20 Gbps buses, this value increases to over 50 MB. The maximum
+ * buffer size is set to 64 MiB to accommodate this.
*
* This is still too much for most cases, where we just snoop a few
* descriptor fetches for enumeration. So, the default is a "reasonable"
- * amount for systems with HZ=250 and incomplete bus saturation.
+ * amount for typical, low-throughput use cases.
*
* XXX What about multi-megabyte URBs which take minutes to transfer?
*/
-#define BUFF_MAX CHUNK_ALIGN(1200*1024)
-#define BUFF_DFL CHUNK_ALIGN(300*1024)
-#define BUFF_MIN CHUNK_ALIGN(8*1024)
+#define BUFF_MAX CHUNK_ALIGN(64*1024*1024)
+#define BUFF_DFL CHUNK_ALIGN(300*1024)
+#define BUFF_MIN CHUNK_ALIGN(8*1024)
/*
* The per-event API header (2 per URB).
@@ -692,7 +694,7 @@ static int mon_bin_open(struct inode *inode, struct file *file)
return -ENODEV;
}
- rp = kzalloc(sizeof(struct mon_reader_bin), GFP_KERNEL);
+ rp = kzalloc_obj(struct mon_reader_bin);
if (rp == NULL) {
rc = -ENOMEM;
goto err_alloc;
@@ -1027,8 +1029,7 @@ static long mon_bin_ioctl(struct file *file, unsigned int cmd, unsigned long arg
return -EINVAL;
size = CHUNK_ALIGN(arg);
- vec = kcalloc(size / CHUNK_SIZE, sizeof(struct mon_pgmap),
- GFP_KERNEL);
+ vec = kzalloc_objs(struct mon_pgmap, size / CHUNK_SIZE);
if (vec == NULL) {
ret = -ENOMEM;
break;
diff --git a/drivers/usb/mon/mon_main.c b/drivers/usb/mon/mon_main.c
index af852d53aac6..e4fff194fea0 100644
--- a/drivers/usb/mon/mon_main.c
+++ b/drivers/usb/mon/mon_main.c
@@ -273,7 +273,7 @@ static void mon_bus_init(struct usb_bus *ubus)
{
struct mon_bus *mbus;
- mbus = kzalloc(sizeof(struct mon_bus), GFP_KERNEL);
+ mbus = kzalloc_obj(struct mon_bus);
if (mbus == NULL)
goto err_alloc;
kref_init(&mbus->ref);
diff --git a/drivers/usb/mon/mon_stat.c b/drivers/usb/mon/mon_stat.c
index 398e02af6a2b..3ee2e483da57 100644
--- a/drivers/usb/mon/mon_stat.c
+++ b/drivers/usb/mon/mon_stat.c
@@ -29,7 +29,7 @@ static int mon_stat_open(struct inode *inode, struct file *file)
struct mon_bus *mbus;
struct snap *sp;
- sp = kmalloc(sizeof(struct snap), GFP_KERNEL);
+ sp = kmalloc_obj(struct snap);
if (sp == NULL)
return -ENOMEM;
diff --git a/drivers/usb/mon/mon_text.c b/drivers/usb/mon/mon_text.c
index 68b9b2b41189..4c8e2518e407 100644
--- a/drivers/usb/mon/mon_text.c
+++ b/drivers/usb/mon/mon_text.c
@@ -330,7 +330,7 @@ static int mon_text_open(struct inode *inode, struct file *file)
mutex_lock(&mon_lock);
mbus = inode->i_private;
- rp = kzalloc(sizeof(struct mon_reader_text), GFP_KERNEL);
+ rp = kzalloc_obj(struct mon_reader_text);
if (rp == NULL) {
rc = -ENOMEM;
goto err_alloc;
diff --git a/drivers/usb/mtu3/mtu3.h b/drivers/usb/mtu3/mtu3.h
index c11840b9a6f1..ba5a63669e5f 100644
--- a/drivers/usb/mtu3/mtu3.h
+++ b/drivers/usb/mtu3/mtu3.h
@@ -65,7 +65,7 @@ struct mtu3_request;
#define MTU3_U3_IP_SLOT_DEFAULT 2
#define MTU3_U2_IP_SLOT_DEFAULT 1
-/**
+/*
* IP TRUNK version
* from 0x1003 version, USB3 Gen2 is supported, two changes affect driver:
* 1. MAXPKT and MULTI bits layout of TXCSR1 and RXCSR1 are adjusted,
@@ -74,9 +74,9 @@ struct mtu3_request;
*/
#define MTU3_TRUNK_VERS_1003 0x1003
-/**
+/*
* Normally the device works on HS or SS, to simplify fifo management,
- * devide fifo into some 512B parts, use bitmap to manage it; And
+ * divide fifo into some 512B parts, use bitmap to manage it; And
* 128 bits size of bitmap is large enough, that means it can manage
* up to 64KB fifo size.
* NOTE: MTU3_EP_FIFO_UNIT should be power of two
@@ -85,7 +85,7 @@ struct mtu3_request;
#define MTU3_FIFO_BIT_SIZE 128
#define MTU3_U2_IP_EP0_FIFO_SIZE 64
-/**
+/*
* Maximum size of ep0 response buffer for ch9 requests,
* the SET_SEL request uses 6 so far, and GET_STATUS is 2
*/
@@ -103,6 +103,7 @@ enum mtu3_speed {
};
/**
+ * enum mtu3_g_ep0_state - endpoint 0 states
* @MU3D_EP0_STATE_SETUP: waits for SETUP or received a SETUP
* without data stage.
* @MU3D_EP0_STATE_TX: IN data stage
@@ -121,11 +122,12 @@ enum mtu3_g_ep0_state {
};
/**
- * MTU3_DR_FORCE_NONE: automatically switch host and periperal mode
+ * enum mtu3_dr_force_mode - indicates host/OTG operating mode
+ * @MTU3_DR_FORCE_NONE: automatically switch host and peripheral mode
* by IDPIN signal.
- * MTU3_DR_FORCE_HOST: force to enter host mode and override OTG
+ * @MTU3_DR_FORCE_HOST: force to enter host mode and override OTG
* IDPIN signal.
- * MTU3_DR_FORCE_DEVICE: force to enter peripheral mode.
+ * @MTU3_DR_FORCE_DEVICE: force to enter peripheral mode.
*/
enum mtu3_dr_force_mode {
MTU3_DR_FORCE_NONE = 0,
@@ -134,6 +136,7 @@ enum mtu3_dr_force_mode {
};
/**
+ * struct mtu3_fifo_info - HW FIFO description and management data
* @base: the base address of fifo
* @limit: the bitmap size in bits
* @bitmap: fifo bitmap in unit of @MTU3_EP_FIFO_UNIT
@@ -145,7 +148,7 @@ struct mtu3_fifo_info {
};
/**
- * General Purpose Descriptor (GPD):
+ * struct qmu_gpd - General Purpose Descriptor (GPD):
* The format of TX GPD is a little different from RX one.
* And the size of GPD is 16 bytes.
*
@@ -179,11 +182,13 @@ struct qmu_gpd {
} __packed;
/**
-* dma: physical base address of GPD segment
-* start: virtual base address of GPD segment
-* end: the last GPD element
-* enqueue: the first empty GPD to use
-* dequeue: the first completed GPD serviced by ISR
+* struct mtu3_gpd_ring - GPD ring descriptor
+* @dma: physical base address of GPD segment
+* @start: virtual base address of GPD segment
+* @end: the last GPD element
+* @enqueue: the first empty GPD to use
+* @dequeue: the first completed GPD serviced by ISR
+*
* NOTE: the size of GPD ring should be >= 2
*/
struct mtu3_gpd_ring {
@@ -195,6 +200,7 @@ struct mtu3_gpd_ring {
};
/**
+* struct otg_switch_mtk - OTG/dual-role switch management
* @vbus: vbus 5V used by host mode
* @edev: external connector used to detect vbus and iddig changes
* @id_nb : notifier for iddig(idpin) detection
@@ -222,6 +228,7 @@ struct otg_switch_mtk {
};
/**
+ * struct ssusb_mtk - SuperSpeed USB descriptor (MTK)
* @mac_base: register base address of device MAC, exclude xHCI's
* @ippc_base: register base address of IP Power and Clock interface (IPPC)
* @vusb33: usb3.3V shared by device/host IP
@@ -268,6 +275,7 @@ struct ssusb_mtk {
};
/**
+ * struct mtu3_ep - common mtu3 endpoint description
* @fifo_size: it is (@slot + 1) * @fifo_seg_size
* @fifo_seg_size: it is roundup_pow_of_two(@maxp)
*/
diff --git a/drivers/usb/mtu3/mtu3_core.c b/drivers/usb/mtu3/mtu3_core.c
index a3a6282893d0..66dbfe1705d5 100644
--- a/drivers/usb/mtu3/mtu3_core.c
+++ b/drivers/usb/mtu3/mtu3_core.c
@@ -290,7 +290,7 @@ static void mtu3_csr_init(struct mtu3 *mtu)
/* delay about 0.1us from detecting reset to send chirp-K */
mtu3_clrbits(mbase, U3D_LINK_RESET_INFO, WTCHRP_MSK);
- /* enable automatical HWRW from L1 */
+ /* enable automatic HWRW from L1 */
mtu3_setbits(mbase, U3D_POWER_MANAGEMENT, LPM_HRWE);
}
@@ -613,7 +613,7 @@ static int mtu3_mem_alloc(struct mtu3 *mtu)
/* one for ep0, another is reserved */
mtu->num_eps = min(in_ep_num, out_ep_num) + 1;
- ep_array = kcalloc(mtu->num_eps * 2, sizeof(*ep_array), GFP_KERNEL);
+ ep_array = kzalloc_objs(*ep_array, mtu->num_eps * 2);
if (ep_array == NULL)
return -ENOMEM;
diff --git a/drivers/usb/mtu3/mtu3_gadget.c b/drivers/usb/mtu3/mtu3_gadget.c
index bf73fbc29976..da29f467943f 100644
--- a/drivers/usb/mtu3/mtu3_gadget.c
+++ b/drivers/usb/mtu3/mtu3_gadget.c
@@ -235,7 +235,7 @@ struct usb_request *mtu3_alloc_request(struct usb_ep *ep, gfp_t gfp_flags)
struct mtu3_ep *mep = to_mtu3_ep(ep);
struct mtu3_request *mreq;
- mreq = kzalloc(sizeof(*mreq), gfp_flags);
+ mreq = kzalloc_obj(*mreq, gfp_flags);
if (!mreq)
return NULL;
diff --git a/drivers/usb/mtu3/mtu3_plat.c b/drivers/usb/mtu3/mtu3_plat.c
index 7b5a431acb56..cc8a864dbd63 100644
--- a/drivers/usb/mtu3/mtu3_plat.c
+++ b/drivers/usb/mtu3/mtu3_plat.c
@@ -431,7 +431,6 @@ static int mtu3_probe(struct platform_device *pdev)
}
device_enable_async_suspend(dev);
- pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
pm_runtime_forbid(dev);
diff --git a/drivers/usb/mtu3/mtu3_qmu.c b/drivers/usb/mtu3/mtu3_qmu.c
index 3d77408e3133..03f26589b056 100644
--- a/drivers/usb/mtu3/mtu3_qmu.c
+++ b/drivers/usb/mtu3/mtu3_qmu.c
@@ -221,7 +221,7 @@ static struct qmu_gpd *advance_deq_gpd(struct mtu3_gpd_ring *ring)
return ring->dequeue;
}
-/* check if a ring is emtpy */
+/* check if a ring is empty */
static bool gpd_ring_empty(struct mtu3_gpd_ring *ring)
{
struct qmu_gpd *enq = ring->enqueue;
diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig
index 9e45d12b81d3..f56929267eaa 100644
--- a/drivers/usb/musb/Kconfig
+++ b/drivers/usb/musb/Kconfig
@@ -84,7 +84,8 @@ config USB_MUSB_TUSB6010
config USB_MUSB_OMAP2PLUS
tristate "OMAP2430 and onwards"
- depends on ARCH_OMAP2PLUS && USB
+ depends on ARCH_OMAP2PLUS || COMPILE_TEST
+ depends on USB
depends on OMAP_CONTROL_PHY || !OMAP_CONTROL_PHY
select GENERIC_PHY
diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c
index 26fd71a5f9b2..4209f438ba18 100644
--- a/drivers/usb/musb/da8xx.c
+++ b/drivers/usb/musb/da8xx.c
@@ -123,7 +123,8 @@ static void da8xx_musb_set_vbus(struct musb *musb, int is_on)
static void otg_timer(struct timer_list *t)
{
- struct musb *musb = from_timer(musb, t, dev_timer);
+ struct musb *musb = timer_container_of(musb, t,
+ dev_timer);
void __iomem *mregs = musb->mregs;
u8 devctl;
unsigned long flags;
@@ -204,7 +205,7 @@ static void __maybe_unused da8xx_musb_try_idle(struct musb *musb, unsigned long
musb->xceiv->otg->state == OTG_STATE_A_WAIT_BCON)) {
dev_dbg(musb->controller, "%s active, deleting timer\n",
usb_otg_state_string(musb->xceiv->otg->state));
- del_timer(&musb->dev_timer);
+ timer_delete(&musb->dev_timer);
last_timer = jiffies;
return;
}
@@ -290,7 +291,7 @@ static irqreturn_t da8xx_musb_interrupt(int irq, void *hci)
MUSB_HST_MODE(musb);
musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE;
portstate(musb->port1_status |= USB_PORT_STAT_POWER);
- del_timer(&musb->dev_timer);
+ timer_delete(&musb->dev_timer);
} else if (!(musb->int_usb & MUSB_INTR_BABBLE)) {
/*
* When babble condition happens, drvvbus interrupt
@@ -419,7 +420,7 @@ static int da8xx_musb_exit(struct musb *musb)
{
struct da8xx_glue *glue = dev_get_drvdata(musb->controller->parent);
- del_timer_sync(&musb->dev_timer);
+ timer_delete_sync(&musb->dev_timer);
phy_power_off(glue->phy);
phy_exit(glue->phy);
diff --git a/drivers/usb/musb/jz4740.c b/drivers/usb/musb/jz4740.c
index acdeb1117cd3..df56c972986f 100644
--- a/drivers/usb/musb/jz4740.c
+++ b/drivers/usb/musb/jz4740.c
@@ -59,7 +59,7 @@ static irqreturn_t jz4740_musb_interrupt(int irq, void *__hci)
return IRQ_NONE;
}
-static struct musb_fifo_cfg jz4740_musb_fifo_cfg[] = {
+static const struct musb_fifo_cfg jz4740_musb_fifo_cfg[] = {
{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, },
{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, },
{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 64, },
@@ -205,7 +205,7 @@ static const struct musb_hdrc_platform_data jz4740_musb_pdata = {
.platform_ops = &jz4740_musb_ops,
};
-static struct musb_fifo_cfg jz4770_musb_fifo_cfg[] = {
+static const struct musb_fifo_cfg jz4770_musb_fifo_cfg[] = {
{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, },
{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, },
{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, },
diff --git a/drivers/usb/musb/mediatek.c b/drivers/usb/musb/mediatek.c
index aa988d74b58d..c6cbe718b1da 100644
--- a/drivers/usb/musb/mediatek.c
+++ b/drivers/usb/musb/mediatek.c
@@ -365,7 +365,7 @@ static const struct musb_platform_ops mtk_musb_ops = {
#define MTK_MUSB_MAX_EP_NUM 8
#define MTK_MUSB_RAM_BITS 11
-static struct musb_fifo_cfg mtk_musb_mode_cfg[] = {
+static const struct musb_fifo_cfg mtk_musb_mode_cfg[] = {
{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, },
{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, },
{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, },
diff --git a/drivers/usb/musb/mpfs.c b/drivers/usb/musb/mpfs.c
index 7edc8429b274..587127abd30a 100644
--- a/drivers/usb/musb/mpfs.c
+++ b/drivers/usb/musb/mpfs.c
@@ -29,7 +29,7 @@ struct mpfs_glue {
struct clk *clk;
};
-static struct musb_fifo_cfg mpfs_musb_mode_cfg[] = {
+static const struct musb_fifo_cfg mpfs_musb_mode_cfg[] = {
{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, },
{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, },
{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, },
@@ -91,7 +91,8 @@ static void mpfs_musb_set_vbus(struct musb *musb, int is_on)
static void otg_timer(struct timer_list *t)
{
- struct musb *musb = from_timer(musb, t, dev_timer);
+ struct musb *musb = timer_container_of(musb, t,
+ dev_timer);
void __iomem *mregs = musb->mregs;
u8 devctl;
unsigned long flags;
@@ -165,7 +166,7 @@ static void __maybe_unused mpfs_musb_try_idle(struct musb *musb, unsigned long t
musb->xceiv->otg->state == OTG_STATE_A_WAIT_BCON)) {
dev_dbg(musb->controller, "%s active, deleting timer\n",
usb_otg_state_string(musb->xceiv->otg->state));
- del_timer(&musb->dev_timer);
+ timer_delete(&musb->dev_timer);
last_timer = jiffies;
return;
}
@@ -232,7 +233,7 @@ static int mpfs_musb_init(struct musb *musb)
static int mpfs_musb_exit(struct musb *musb)
{
- del_timer_sync(&musb->dev_timer);
+ timer_delete_sync(&musb->dev_timer);
return 0;
}
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
index 7f349f5e781d..73ac25f53698 100644
--- a/drivers/usb/musb/musb_core.c
+++ b/drivers/usb/musb/musb_core.c
@@ -596,7 +596,7 @@ void musb_load_testpacket(struct musb *musb)
*/
static void musb_otg_timer_func(struct timer_list *t)
{
- struct musb *musb = from_timer(musb, t, otg_timer);
+ struct musb *musb = timer_container_of(musb, t, otg_timer);
unsigned long flags;
spin_lock_irqsave(&musb->lock, flags);
@@ -921,7 +921,7 @@ b_host:
musb_set_state(musb, OTG_STATE_B_HOST);
if (musb->hcd)
musb->hcd->self.is_b_host = 1;
- del_timer(&musb->otg_timer);
+ timer_delete(&musb->otg_timer);
break;
default:
if ((devctl & MUSB_DEVCTL_VBUS)
@@ -1015,7 +1015,7 @@ static void musb_handle_intr_reset(struct musb *musb)
+ msecs_to_jiffies(TA_WAIT_BCON(musb)));
break;
case OTG_STATE_A_PERIPHERAL:
- del_timer(&musb->otg_timer);
+ timer_delete(&musb->otg_timer);
musb_g_reset(musb);
break;
case OTG_STATE_B_WAIT_ACON:
@@ -1271,7 +1271,7 @@ MODULE_PARM_DESC(fifo_mode, "initial endpoint configuration");
*/
/* mode 0 - fits in 2KB */
-static struct musb_fifo_cfg mode_0_cfg[] = {
+static const struct musb_fifo_cfg mode_0_cfg[] = {
{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, },
{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, },
{ .hw_ep_num = 2, .style = FIFO_RXTX, .maxpacket = 512, },
@@ -1280,7 +1280,7 @@ static struct musb_fifo_cfg mode_0_cfg[] = {
};
/* mode 1 - fits in 4KB */
-static struct musb_fifo_cfg mode_1_cfg[] = {
+static const struct musb_fifo_cfg mode_1_cfg[] = {
{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, .mode = BUF_DOUBLE, },
{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, .mode = BUF_DOUBLE, },
{ .hw_ep_num = 2, .style = FIFO_RXTX, .maxpacket = 512, .mode = BUF_DOUBLE, },
@@ -1289,7 +1289,7 @@ static struct musb_fifo_cfg mode_1_cfg[] = {
};
/* mode 2 - fits in 4KB */
-static struct musb_fifo_cfg mode_2_cfg[] = {
+static const struct musb_fifo_cfg mode_2_cfg[] = {
{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, },
{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, },
{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, },
@@ -1299,7 +1299,7 @@ static struct musb_fifo_cfg mode_2_cfg[] = {
};
/* mode 3 - fits in 4KB */
-static struct musb_fifo_cfg mode_3_cfg[] = {
+static const struct musb_fifo_cfg mode_3_cfg[] = {
{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, .mode = BUF_DOUBLE, },
{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, .mode = BUF_DOUBLE, },
{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, },
@@ -1309,7 +1309,7 @@ static struct musb_fifo_cfg mode_3_cfg[] = {
};
/* mode 4 - fits in 16KB */
-static struct musb_fifo_cfg mode_4_cfg[] = {
+static const struct musb_fifo_cfg mode_4_cfg[] = {
{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, },
{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, },
{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, },
@@ -1340,7 +1340,7 @@ static struct musb_fifo_cfg mode_4_cfg[] = {
};
/* mode 5 - fits in 8KB */
-static struct musb_fifo_cfg mode_5_cfg[] = {
+static const struct musb_fifo_cfg mode_5_cfg[] = {
{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, },
{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, },
{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, },
@@ -1447,7 +1447,7 @@ fifo_setup(struct musb *musb, struct musb_hw_ep *hw_ep,
return offset + (maxpacket << ((c_size & MUSB_FIFOSZ_DPB) ? 1 : 0));
}
-static struct musb_fifo_cfg ep0_cfg = {
+static const struct musb_fifo_cfg ep0_cfg = {
.style = FIFO_RXTX, .maxpacket = 64,
};
@@ -1600,7 +1600,7 @@ static int musb_core_init(u16 musb_type, struct musb *musb)
/* log core options (read using indexed model) */
reg = musb_read_configdata(mbase);
- strcpy(aInfo, (reg & MUSB_CONFIGDATA_UTMIDW) ? "UTMI-16" : "UTMI-8");
+ strscpy(aInfo, (reg & MUSB_CONFIGDATA_UTMIDW) ? "UTMI-16" : "UTMI-8");
if (reg & MUSB_CONFIGDATA_DYNFIFO) {
strcat(aInfo, ", dyn FIFOs");
musb->dyn_fifo = true;
@@ -2031,7 +2031,6 @@ static void musb_pm_runtime_check_session(struct musb *musb)
if (!musb->session)
break;
trace_musb_state(musb, devctl, "Allow PM on possible host mode disconnect");
- pm_runtime_mark_last_busy(musb->controller);
pm_runtime_put_autosuspend(musb->controller);
musb->session = false;
return;
@@ -2063,7 +2062,6 @@ static void musb_pm_runtime_check_session(struct musb *musb)
msecs_to_jiffies(3000));
} else {
trace_musb_state(musb, devctl, "Allow PM with no session");
- pm_runtime_mark_last_busy(musb->controller);
pm_runtime_put_autosuspend(musb->controller);
}
@@ -2090,7 +2088,6 @@ static void musb_irq_work(struct work_struct *data)
sysfs_notify(&musb->controller->kobj, NULL, "mode");
}
- pm_runtime_mark_last_busy(musb->controller);
pm_runtime_put_autosuspend(musb->controller);
}
@@ -2564,7 +2561,6 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
musb_init_debugfs(musb);
musb->is_initialized = 1;
- pm_runtime_mark_last_busy(musb->controller);
pm_runtime_put_autosuspend(musb->controller);
return 0;
@@ -2887,7 +2883,6 @@ static int musb_resume(struct device *dev)
error);
spin_unlock_irqrestore(&musb->lock, flags);
- pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
return 0;
diff --git a/drivers/usb/musb/musb_cppi41.c b/drivers/usb/musb/musb_cppi41.c
index 9589243e8951..28e958fcc862 100644
--- a/drivers/usb/musb/musb_cppi41.c
+++ b/drivers/usb/musb/musb_cppi41.c
@@ -756,12 +756,12 @@ cppi41_dma_controller_create(struct musb *musb, void __iomem *base)
return NULL;
}
- controller = kzalloc(sizeof(*controller), GFP_KERNEL);
+ controller = kzalloc_obj(*controller);
if (!controller)
goto kzalloc_fail;
- hrtimer_init(&controller->early_tx, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- controller->early_tx.function = cppi41_recheck_tx_req;
+ hrtimer_setup(&controller->early_tx, cppi41_recheck_tx_req, CLOCK_MONOTONIC,
+ HRTIMER_MODE_REL);
INIT_LIST_HEAD(&controller->early_tx_list);
controller->controller.channel_alloc = cppi41_dma_channel_allocate;
diff --git a/drivers/usb/musb/musb_debugfs.c b/drivers/usb/musb/musb_debugfs.c
index 2d623284edf6..5092d62c2062 100644
--- a/drivers/usb/musb/musb_debugfs.c
+++ b/drivers/usb/musb/musb_debugfs.c
@@ -106,7 +106,6 @@ static int musb_regdump_show(struct seq_file *s, void *unused)
}
}
- pm_runtime_mark_last_busy(musb->controller);
pm_runtime_put_autosuspend(musb->controller);
return 0;
}
@@ -119,7 +118,6 @@ static int musb_test_mode_show(struct seq_file *s, void *unused)
pm_runtime_get_sync(musb->controller);
test = musb_readb(musb->mregs, MUSB_TESTMODE);
- pm_runtime_mark_last_busy(musb->controller);
pm_runtime_put_autosuspend(musb->controller);
if (test == (MUSB_TEST_FORCE_HOST | MUSB_TEST_FORCE_FS))
@@ -216,7 +214,6 @@ static ssize_t musb_test_mode_write(struct file *file,
musb_writeb(musb->mregs, MUSB_TESTMODE, test);
ret:
- pm_runtime_mark_last_busy(musb->controller);
pm_runtime_put_autosuspend(musb->controller);
return count;
}
@@ -243,7 +240,6 @@ static int musb_softconnect_show(struct seq_file *s, void *unused)
reg = musb_readb(musb->mregs, MUSB_DEVCTL);
connect = reg & MUSB_DEVCTL_SESSION ? 1 : 0;
- pm_runtime_mark_last_busy(musb->controller);
pm_runtime_put_autosuspend(musb->controller);
break;
default:
@@ -304,7 +300,6 @@ static ssize_t musb_softconnect_write(struct file *file,
}
}
- pm_runtime_mark_last_busy(musb->controller);
pm_runtime_put_autosuspend(musb->controller);
return count;
}
diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c
index f877faf5a930..e3935f18dd56 100644
--- a/drivers/usb/musb/musb_dsps.c
+++ b/drivers/usb/musb/musb_dsps.c
@@ -201,7 +201,7 @@ static void dsps_musb_disable(struct musb *musb)
musb_writel(reg_base, wrp->coreintr_clear, wrp->usb_bitmap);
musb_writel(reg_base, wrp->epintr_clear,
wrp->txep_bitmap | wrp->rxep_bitmap);
- del_timer_sync(&musb->dev_timer);
+ timer_delete_sync(&musb->dev_timer);
}
/* Caller must take musb->lock */
@@ -215,7 +215,7 @@ static int dsps_check_status(struct musb *musb, void *unused)
int skip_session = 0;
if (glue->vbus_irq)
- del_timer(&musb->dev_timer);
+ timer_delete(&musb->dev_timer);
/*
* We poll because DSPS IP's won't expose several OTG-critical
@@ -278,7 +278,7 @@ static int dsps_check_status(struct musb *musb, void *unused)
static void otg_timer(struct timer_list *t)
{
- struct musb *musb = from_timer(musb, t, dev_timer);
+ struct musb *musb = timer_container_of(musb, t, dev_timer);
struct device *dev = musb->controller;
unsigned long flags;
int err;
@@ -296,7 +296,6 @@ static void otg_timer(struct timer_list *t)
if (err < 0)
dev_err(dev, "%s resume work: %i\n", __func__, err);
spin_unlock_irqrestore(&musb->lock, flags);
- pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
}
@@ -499,7 +498,7 @@ static int dsps_musb_exit(struct musb *musb)
struct device *dev = musb->controller;
struct dsps_glue *glue = dev_get_drvdata(dev->parent);
- del_timer_sync(&musb->dev_timer);
+ timer_delete_sync(&musb->dev_timer);
phy_power_off(musb->phy);
phy_exit(musb->phy);
debugfs_remove_recursive(glue->dbgfs_root);
@@ -839,7 +838,7 @@ static int dsps_setup_optional_vbus_irq(struct platform_device *pdev,
{
int error;
- glue->vbus_irq = platform_get_irq_byname(pdev, "vbus");
+ glue->vbus_irq = platform_get_irq_byname_optional(pdev, "vbus");
if (glue->vbus_irq == -EPROBE_DEFER)
return -EPROBE_DEFER;
@@ -983,7 +982,7 @@ static int dsps_suspend(struct device *dev)
return ret;
}
- del_timer_sync(&musb->dev_timer);
+ timer_delete_sync(&musb->dev_timer);
mbase = musb->ctrl_base;
glue->context.control = musb_readl(mbase, wrp->control);
diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c
index 6869c58367f2..016d3f3fc1e0 100644
--- a/drivers/usb/musb/musb_gadget.c
+++ b/drivers/usb/musb/musb_gadget.c
@@ -1133,7 +1133,7 @@ struct usb_request *musb_alloc_request(struct usb_ep *ep, gfp_t gfp_flags)
struct musb_ep *musb_ep = to_musb_ep(ep);
struct musb_request *request;
- request = kzalloc(sizeof *request, gfp_flags);
+ request = kzalloc_obj(*request, gfp_flags);
if (!request)
return NULL;
@@ -1258,7 +1258,6 @@ static int musb_gadget_queue(struct usb_ep *ep, struct usb_request *req,
unlock:
spin_unlock_irqrestore(&musb->lock, lockflags);
- pm_runtime_mark_last_busy(musb->controller);
pm_runtime_put_autosuspend(musb->controller);
return status;
@@ -1642,7 +1641,6 @@ static void musb_gadget_work(struct work_struct *work)
spin_lock_irqsave(&musb->lock, flags);
musb_pullup(musb, musb->softconnect);
spin_unlock_irqrestore(&musb->lock, flags);
- pm_runtime_mark_last_busy(musb->controller);
pm_runtime_put_autosuspend(musb->controller);
}
@@ -1862,7 +1860,6 @@ static int musb_gadget_start(struct usb_gadget *g,
if (musb->xceiv && musb->xceiv->last_event == USB_EVENT_ID)
musb_platform_set_vbus(musb, 1);
- pm_runtime_mark_last_busy(musb->controller);
pm_runtime_put_autosuspend(musb->controller);
return 0;
@@ -1913,9 +1910,9 @@ static int musb_gadget_stop(struct usb_gadget *g)
* gadget driver here and have everything work;
* that currently misbehaves.
*/
+ usb_gadget_set_state(g, USB_STATE_NOTATTACHED);
/* Force check of devctl register for PM runtime */
- pm_runtime_mark_last_busy(musb->controller);
pm_runtime_put_autosuspend(musb->controller);
return 0;
@@ -2019,6 +2016,7 @@ void musb_g_disconnect(struct musb *musb)
case OTG_STATE_B_PERIPHERAL:
case OTG_STATE_B_IDLE:
musb_set_state(musb, OTG_STATE_B_IDLE);
+ usb_gadget_set_state(&musb->g, USB_STATE_NOTATTACHED);
break;
case OTG_STATE_B_SRP_INIT:
break;
diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
index 6b4481a867c5..8efd2fa472f9 100644
--- a/drivers/usb/musb/musb_host.c
+++ b/drivers/usb/musb/musb_host.c
@@ -2154,7 +2154,7 @@ static int musb_urb_enqueue(
* REVISIT consider a dedicated qh kmem_cache, so it's harder
* for bugs in other kernel code to break this driver...
*/
- qh = kzalloc(sizeof *qh, mem_flags);
+ qh = kzalloc_obj(*qh, mem_flags);
if (!qh) {
spin_lock_irqsave(&musb->lock, flags);
usb_hcd_unlink_urb_from_ep(hcd, urb);
diff --git a/drivers/usb/musb/musbhsdma.c b/drivers/usb/musb/musbhsdma.c
index 7acd1635850d..8c6c643fcb2c 100644
--- a/drivers/usb/musb/musbhsdma.c
+++ b/drivers/usb/musb/musbhsdma.c
@@ -395,7 +395,7 @@ dma_controller_alloc(struct musb *musb, void __iomem *base)
{
struct musb_dma_controller *controller;
- controller = kzalloc(sizeof(*controller), GFP_KERNEL);
+ controller = kzalloc_obj(*controller);
if (!controller)
return NULL;
diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c
index 2970967a4fd2..48bb9bfb2204 100644
--- a/drivers/usb/musb/omap2430.c
+++ b/drivers/usb/musb/omap2430.c
@@ -151,7 +151,6 @@ static void omap_musb_set_mailbox(struct omap2430_glue *glue)
default:
dev_dbg(musb->controller, "ID float\n");
}
- pm_runtime_mark_last_busy(musb->controller);
pm_runtime_put_autosuspend(musb->controller);
atomic_notifier_call_chain(&musb->xceiv->notifier,
musb->xceiv->last_event, NULL);
@@ -318,13 +317,11 @@ static int omap2430_probe(struct platform_device *pdev)
glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);
if (!glue)
- goto err0;
+ return -ENOMEM;
musb = platform_device_alloc("musb-hdrc", PLATFORM_DEVID_AUTO);
- if (!musb) {
- dev_err(&pdev->dev, "failed to allocate musb device\n");
- goto err0;
- }
+ if (!musb)
+ return -ENOMEM;
musb->dev.parent = &pdev->dev;
musb->dev.dma_mask = &omap2430_dmamask;
@@ -349,15 +346,15 @@ static int omap2430_probe(struct platform_device *pdev)
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
- goto err2;
+ goto err_put_musb;
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
if (!data)
- goto err2;
+ goto err_put_musb;
config = devm_kzalloc(&pdev->dev, sizeof(*config), GFP_KERNEL);
if (!config)
- goto err2;
+ goto err_put_musb;
of_property_read_u32(np, "mode", (u32 *)&pdata->mode);
of_property_read_u32(np, "interface-type",
@@ -380,7 +377,7 @@ static int omap2430_probe(struct platform_device *pdev)
if (!control_pdev) {
dev_err(&pdev->dev, "Failed to get control device\n");
ret = -EINVAL;
- goto err2;
+ goto err_put_musb;
}
glue->control_otghs = &control_pdev->dev;
}
@@ -400,7 +397,7 @@ static int omap2430_probe(struct platform_device *pdev)
ret = platform_device_add_resources(musb, pdev->resource, pdev->num_resources);
if (ret) {
dev_err(&pdev->dev, "failed to add resources\n");
- goto err2;
+ goto err_put_control_otghs;
}
if (populate_irqs) {
@@ -413,7 +410,7 @@ static int omap2430_probe(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
ret = -EINVAL;
- goto err2;
+ goto err_put_control_otghs;
}
musb_res[i].start = res->start;
@@ -441,14 +438,14 @@ static int omap2430_probe(struct platform_device *pdev)
ret = platform_device_add_resources(musb, musb_res, i);
if (ret) {
dev_err(&pdev->dev, "failed to add IRQ resources\n");
- goto err2;
+ goto err_put_control_otghs;
}
}
ret = platform_device_add_data(musb, pdata, sizeof(*pdata));
if (ret) {
dev_err(&pdev->dev, "failed to add platform_data\n");
- goto err2;
+ goto err_put_control_otghs;
}
pm_runtime_enable(glue->dev);
@@ -456,18 +453,19 @@ static int omap2430_probe(struct platform_device *pdev)
ret = platform_device_add(musb);
if (ret) {
dev_err(&pdev->dev, "failed to register musb device\n");
- goto err3;
+ goto err_disable_rpm;
}
return 0;
-err3:
+err_disable_rpm:
pm_runtime_disable(glue->dev);
-
-err2:
+err_put_control_otghs:
+ if (!IS_ERR(glue->control_otghs))
+ put_device(glue->control_otghs);
+err_put_musb:
platform_device_put(musb);
-err0:
return ret;
}
@@ -477,6 +475,8 @@ static void omap2430_remove(struct platform_device *pdev)
platform_device_unregister(glue->musb);
pm_runtime_disable(glue->dev);
+ if (!IS_ERR(glue->control_otghs))
+ put_device(glue->control_otghs);
}
#ifdef CONFIG_PM
diff --git a/drivers/usb/musb/sunxi.c b/drivers/usb/musb/sunxi.c
index eac1cde86be3..a6bd3e968cc7 100644
--- a/drivers/usb/musb/sunxi.c
+++ b/drivers/usb/musb/sunxi.c
@@ -629,7 +629,7 @@ static const struct musb_platform_ops sunxi_musb_ops = {
#define SUNXI_MUSB_RAM_BITS 11
/* Allwinner OTG supports up to 5 endpoints */
-static struct musb_fifo_cfg sunxi_musb_mode_cfg_5eps[] = {
+static const struct musb_fifo_cfg sunxi_musb_mode_cfg_5eps[] = {
MUSB_EP_FIFO_SINGLE(1, FIFO_TX, 512),
MUSB_EP_FIFO_SINGLE(1, FIFO_RX, 512),
MUSB_EP_FIFO_SINGLE(2, FIFO_TX, 512),
@@ -643,7 +643,7 @@ static struct musb_fifo_cfg sunxi_musb_mode_cfg_5eps[] = {
};
/* H3/V3s OTG supports only 4 endpoints */
-static struct musb_fifo_cfg sunxi_musb_mode_cfg_4eps[] = {
+static const struct musb_fifo_cfg sunxi_musb_mode_cfg_4eps[] = {
MUSB_EP_FIFO_SINGLE(1, FIFO_TX, 512),
MUSB_EP_FIFO_SINGLE(1, FIFO_RX, 512),
MUSB_EP_FIFO_SINGLE(2, FIFO_TX, 512),
diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c
index 90b760a95e4e..1d9571f24a56 100644
--- a/drivers/usb/musb/tusb6010.c
+++ b/drivers/usb/musb/tusb6010.c
@@ -457,7 +457,7 @@ static int tusb_musb_vbus_status(struct musb *musb)
static void musb_do_idle(struct timer_list *t)
{
- struct musb *musb = from_timer(musb, t, dev_timer);
+ struct musb *musb = timer_container_of(musb, t, dev_timer);
unsigned long flags;
spin_lock_irqsave(&musb->lock, flags);
@@ -525,7 +525,7 @@ static void tusb_musb_try_idle(struct musb *musb, unsigned long timeout)
&& (musb->xceiv->otg->state == OTG_STATE_A_WAIT_BCON))) {
dev_dbg(musb->controller, "%s active, deleting timer\n",
usb_otg_state_string(musb->xceiv->otg->state));
- del_timer(&musb->dev_timer);
+ timer_delete(&musb->dev_timer);
last_timer = jiffies;
return;
}
@@ -875,7 +875,7 @@ static irqreturn_t tusb_musb_interrupt(int irq, void *__hci)
}
if (int_src & TUSB_INT_SRC_USB_IP_CONN)
- del_timer(&musb->dev_timer);
+ timer_delete(&musb->dev_timer);
/* OTG state change reports (annoyingly) not issued by Mentor core */
if (int_src & (TUSB_INT_SRC_VBUS_SENSE_CHNG
@@ -984,7 +984,7 @@ static void tusb_musb_disable(struct musb *musb)
musb_writel(tbase, TUSB_DMA_INT_MASK, 0x7fffffff);
musb_writel(tbase, TUSB_GPIO_INT_MASK, 0x1ff);
- del_timer(&musb->dev_timer);
+ timer_delete(&musb->dev_timer);
if (is_dma_capable() && !dma_off) {
printk(KERN_WARNING "%s %s: dma still active\n",
@@ -1174,7 +1174,7 @@ static int tusb_musb_exit(struct musb *musb)
{
struct tusb6010_glue *glue = dev_get_drvdata(musb->controller->parent);
- del_timer_sync(&musb->dev_timer);
+ timer_delete_sync(&musb->dev_timer);
the_musb = NULL;
gpiod_set_value(glue->enable, 0);
diff --git a/drivers/usb/musb/tusb6010_omap.c b/drivers/usb/musb/tusb6010_omap.c
index 60a93b8bbe3c..36ee61a52e7e 100644
--- a/drivers/usb/musb/tusb6010_omap.c
+++ b/drivers/usb/musb/tusb6010_omap.c
@@ -596,7 +596,7 @@ tusb_dma_controller_create(struct musb *musb, void __iomem *base)
| TUSB_DMA_REQ_CONF_DMA_REQ_EN(0x3f)
| TUSB_DMA_REQ_CONF_DMA_REQ_ASSER(2));
- tusb_dma = kzalloc(sizeof(struct tusb_omap_dma), GFP_KERNEL);
+ tusb_dma = kzalloc_obj(struct tusb_omap_dma);
if (!tusb_dma)
goto out;
@@ -615,13 +615,13 @@ tusb_dma_controller_create(struct musb *musb, void __iomem *base)
struct dma_channel *ch;
struct tusb_omap_dma_ch *chdat;
- ch = kzalloc(sizeof(struct dma_channel), GFP_KERNEL);
+ ch = kzalloc_obj(struct dma_channel);
if (!ch)
goto cleanup;
dma_channel_pool[i] = ch;
- chdat = kzalloc(sizeof(struct tusb_omap_dma_ch), GFP_KERNEL);
+ chdat = kzalloc_obj(struct tusb_omap_dma_ch);
if (!chdat)
goto cleanup;
diff --git a/drivers/usb/musb/ux500_dma.c b/drivers/usb/musb/ux500_dma.c
index d5cf5e8bb1ca..bbb4978c0cd5 100644
--- a/drivers/usb/musb/ux500_dma.c
+++ b/drivers/usb/musb/ux500_dma.c
@@ -362,7 +362,7 @@ ux500_dma_controller_create(struct musb *musb, void __iomem *base)
struct resource *iomem;
int ret;
- controller = kzalloc(sizeof(*controller), GFP_KERNEL);
+ controller = kzalloc_obj(*controller);
if (!controller)
goto kzalloc_fail;
diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig
index 5f629d7cad64..b7acf3966cd7 100644
--- a/drivers/usb/phy/Kconfig
+++ b/drivers/usb/phy/Kconfig
@@ -126,18 +126,6 @@ config USB_ISP1301
To compile this driver as a module, choose M here: the
module will be called phy-isp1301.
-config USB_MV_OTG
- tristate "Marvell USB OTG support"
- depends on USB_EHCI_MV && USB_MV_UDC && PM && USB_OTG
- depends on USB_GADGET || !USB_GADGET # if USB_GADGET=m, this can't be 'y'
- select USB_PHY
- help
- Say Y here if you want to build Marvell USB OTG transceiver
- driver in kernel (including PXA and MMP series). This driver
- implements role switch between EHCI host driver and gadget driver.
-
- To compile this driver as a module, choose M here.
-
config USB_MXS_PHY
tristate "Freescale MXS USB PHY support"
depends on ARCH_MXC || ARCH_MXS
diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile
index e5d619b4d8f6..ceae46c5341d 100644
--- a/drivers/usb/phy/Makefile
+++ b/drivers/usb/phy/Makefile
@@ -18,7 +18,6 @@ obj-$(CONFIG_TWL6030_USB) += phy-twl6030-usb.o
obj-$(CONFIG_USB_TEGRA_PHY) += phy-tegra-usb.o
obj-$(CONFIG_USB_GPIO_VBUS) += phy-gpio-vbus-usb.o
obj-$(CONFIG_USB_ISP1301) += phy-isp1301.o
-obj-$(CONFIG_USB_MV_OTG) += phy-mv-usb.o
obj-$(CONFIG_USB_MXS_PHY) += phy-mxs-usb.o
obj-$(CONFIG_USB_ULPI) += phy-ulpi.o
obj-$(CONFIG_USB_ULPI_VIEWPORT) += phy-ulpi-viewport.o
diff --git a/drivers/usb/phy/phy-fsl-usb.c b/drivers/usb/phy/phy-fsl-usb.c
index 40ac68e52cee..35d79f11b03d 100644
--- a/drivers/usb/phy/phy-fsl-usb.c
+++ b/drivers/usb/phy/phy-fsl-usb.c
@@ -779,11 +779,11 @@ static int fsl_otg_conf(struct platform_device *pdev)
return 0;
/* allocate space to fsl otg device */
- fsl_otg_tc = kzalloc(sizeof(struct fsl_otg), GFP_KERNEL);
+ fsl_otg_tc = kzalloc_obj(struct fsl_otg);
if (!fsl_otg_tc)
return -ENOMEM;
- fsl_otg_tc->phy.otg = kzalloc(sizeof(struct usb_otg), GFP_KERNEL);
+ fsl_otg_tc->phy.otg = kzalloc_obj(struct usb_otg);
if (!fsl_otg_tc->phy.otg) {
kfree(fsl_otg_tc);
return -ENOMEM;
@@ -988,6 +988,7 @@ static void fsl_otg_remove(struct platform_device *pdev)
{
struct fsl_usb2_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ disable_delayed_work_sync(&fsl_otg_dev->otg_event);
usb_remove_phy(&fsl_otg_dev->phy);
free_irq(fsl_otg_dev->irq, fsl_otg_dev);
diff --git a/drivers/usb/phy/phy-fsl-usb.h b/drivers/usb/phy/phy-fsl-usb.h
index d70341ae5a92..95bfe7f1b83a 100644
--- a/drivers/usb/phy/phy-fsl-usb.h
+++ b/drivers/usb/phy/phy-fsl-usb.h
@@ -345,7 +345,7 @@ inline struct fsl_otg_timer *otg_timer_initializer
{
struct fsl_otg_timer *timer;
- timer = kmalloc(sizeof(struct fsl_otg_timer), GFP_KERNEL);
+ timer = kmalloc_obj(struct fsl_otg_timer);
if (!timer)
return NULL;
timer->function = function;
diff --git a/drivers/usb/phy/phy-generic.c b/drivers/usb/phy/phy-generic.c
index 8423be59ec0f..de26b302334d 100644
--- a/drivers/usb/phy/phy-generic.c
+++ b/drivers/usb/phy/phy-generic.c
@@ -20,7 +20,7 @@
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/regulator/consumer.h>
-#include <linux/of.h>
+#include <linux/property.h>
#include <linux/gpio/consumer.h>
#include <linux/delay.h>
@@ -49,15 +49,13 @@ static int nop_set_suspend(struct usb_phy *x, int suspend)
int ret = 0;
if (suspend) {
- if (!IS_ERR(nop->clk))
- clk_disable_unprepare(nop->clk);
+ clk_disable_unprepare(nop->clk);
if (!IS_ERR(nop->vcc) && !device_may_wakeup(x->dev))
ret = regulator_disable(nop->vcc);
} else {
if (!IS_ERR(nop->vcc) && !device_may_wakeup(x->dev))
ret = regulator_enable(nop->vcc);
- if (!IS_ERR(nop->clk))
- clk_prepare_enable(nop->clk);
+ clk_prepare_enable(nop->clk);
}
return ret;
@@ -137,11 +135,9 @@ int usb_gen_phy_init(struct usb_phy *phy)
dev_err(phy->dev, "Failed to enable power\n");
}
- if (!IS_ERR(nop->clk)) {
- ret = clk_prepare_enable(nop->clk);
- if (ret)
- return ret;
- }
+ ret = clk_prepare_enable(nop->clk);
+ if (ret)
+ return ret;
nop_reset(nop);
@@ -155,8 +151,7 @@ void usb_gen_phy_shutdown(struct usb_phy *phy)
gpiod_set_value_cansleep(nop->gpiod_reset, 1);
- if (!IS_ERR(nop->clk))
- clk_disable_unprepare(nop->clk);
+ clk_disable_unprepare(nop->clk);
if (!IS_ERR(nop->vcc)) {
if (regulator_disable(nop->vcc))
@@ -202,18 +197,9 @@ int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop)
{
enum usb_phy_type type = USB_PHY_TYPE_USB2;
int err = 0;
-
u32 clk_rate = 0;
- bool needs_clk = false;
-
- if (dev->of_node) {
- struct device_node *node = dev->of_node;
-
- if (of_property_read_u32(node, "clock-frequency", &clk_rate))
- clk_rate = 0;
- needs_clk = of_property_present(node, "clocks");
- }
+ device_property_read_u32(dev, "clock-frequency", &clk_rate);
nop->gpiod_reset = devm_gpiod_get_optional(dev, "reset",
GPIOD_ASIS);
err = PTR_ERR_OR_ZERO(nop->gpiod_reset);
@@ -235,20 +221,16 @@ int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop)
if (!nop->phy.otg)
return -ENOMEM;
- nop->clk = devm_clk_get(dev, "main_clk");
- if (IS_ERR(nop->clk)) {
- dev_dbg(dev, "Can't get phy clock: %ld\n",
- PTR_ERR(nop->clk));
- if (needs_clk)
- return PTR_ERR(nop->clk);
- }
+ nop->clk = devm_clk_get_optional(dev, "main_clk");
+ if (IS_ERR(nop->clk))
+ return dev_err_probe(dev, PTR_ERR(nop->clk),
+ "Can't get phy clock\n");
- if (!IS_ERR(nop->clk) && clk_rate) {
+ if (clk_rate) {
err = clk_set_rate(nop->clk, clk_rate);
- if (err) {
- dev_err(dev, "Error setting clock rate\n");
- return err;
- }
+ if (err)
+ return dev_err_probe(dev, err,
+ "Error setting clock rate\n");
}
nop->vcc = devm_regulator_get_optional(dev, "vcc");
@@ -282,7 +264,6 @@ EXPORT_SYMBOL_GPL(usb_phy_gen_create_phy);
static int usb_phy_generic_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct device_node *dn = dev->of_node;
struct usb_phy_generic *nop;
int err;
@@ -293,17 +274,17 @@ static int usb_phy_generic_probe(struct platform_device *pdev)
err = usb_phy_gen_create_phy(dev, nop);
if (err)
return err;
+
if (nop->gpiod_vbus) {
- err = devm_request_threaded_irq(&pdev->dev,
+ err = devm_request_threaded_irq(dev,
gpiod_to_irq(nop->gpiod_vbus),
NULL, nop_gpio_vbus_thread,
VBUS_IRQ_FLAGS, "vbus_detect",
nop);
- if (err) {
- dev_err(&pdev->dev, "can't request irq %i, err: %d\n",
- gpiod_to_irq(nop->gpiod_vbus), err);
- return err;
- }
+ if (err)
+ return dev_err_probe(dev, err, "can't request irq %i\n",
+ gpiod_to_irq(nop->gpiod_vbus));
+
nop->phy.otg->state = gpiod_get_value(nop->gpiod_vbus) ?
OTG_STATE_B_PERIPHERAL : OTG_STATE_B_IDLE;
}
@@ -312,16 +293,13 @@ static int usb_phy_generic_probe(struct platform_device *pdev)
nop->phy.shutdown = usb_gen_phy_shutdown;
err = usb_add_phy_dev(&nop->phy);
- if (err) {
- dev_err(&pdev->dev, "can't register transceiver, err: %d\n",
- err);
- return err;
- }
+ if (err)
+ return dev_err_probe(dev, err, "can't register transceiver\n");
platform_set_drvdata(pdev, nop);
- device_set_wakeup_capable(&pdev->dev,
- of_property_read_bool(dn, "wakeup-source"));
+ device_set_wakeup_capable(dev,
+ device_property_read_bool(dev, "wakeup-source"));
return 0;
}
diff --git a/drivers/usb/phy/phy-isp1301.c b/drivers/usb/phy/phy-isp1301.c
index f9b5c411aee4..2940f0c84e1b 100644
--- a/drivers/usb/phy/phy-isp1301.c
+++ b/drivers/usb/phy/phy-isp1301.c
@@ -149,7 +149,12 @@ struct i2c_client *isp1301_get_client(struct device_node *node)
return client;
/* non-DT: only one ISP1301 chip supported */
- return isp1301_i2c_client;
+ if (isp1301_i2c_client) {
+ get_device(&isp1301_i2c_client->dev);
+ return isp1301_i2c_client;
+ }
+
+ return NULL;
}
EXPORT_SYMBOL_GPL(isp1301_get_client);
diff --git a/drivers/usb/phy/phy-mv-usb.c b/drivers/usb/phy/phy-mv-usb.c
deleted file mode 100644
index 30d6c8840a5e..000000000000
--- a/drivers/usb/phy/phy-mv-usb.c
+++ /dev/null
@@ -1,881 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Copyright (C) 2011 Marvell International Ltd. All rights reserved.
- * Author: Chao Xie <chao.xie@marvell.com>
- * Neil Zhang <zhangwm@marvell.com>
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/io.h>
-#include <linux/iopoll.h>
-#include <linux/uaccess.h>
-#include <linux/device.h>
-#include <linux/proc_fs.h>
-#include <linux/clk.h>
-#include <linux/workqueue.h>
-#include <linux/platform_device.h>
-#include <linux/string_choices.h>
-
-#include <linux/usb.h>
-#include <linux/usb/ch9.h>
-#include <linux/usb/otg.h>
-#include <linux/usb/gadget.h>
-#include <linux/usb/hcd.h>
-#include <linux/platform_data/mv_usb.h>
-
-#include "phy-mv-usb.h"
-
-#define DRIVER_DESC "Marvell USB OTG transceiver driver"
-
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_LICENSE("GPL");
-
-static const char driver_name[] = "mv-otg";
-
-static char *state_string[] = {
- "undefined",
- "b_idle",
- "b_srp_init",
- "b_peripheral",
- "b_wait_acon",
- "b_host",
- "a_idle",
- "a_wait_vrise",
- "a_wait_bcon",
- "a_host",
- "a_suspend",
- "a_peripheral",
- "a_wait_vfall",
- "a_vbus_err"
-};
-
-static int mv_otg_set_vbus(struct usb_otg *otg, bool on)
-{
- struct mv_otg *mvotg = container_of(otg->usb_phy, struct mv_otg, phy);
- if (mvotg->pdata->set_vbus == NULL)
- return -ENODEV;
-
- return mvotg->pdata->set_vbus(on);
-}
-
-static int mv_otg_set_host(struct usb_otg *otg,
- struct usb_bus *host)
-{
- otg->host = host;
-
- return 0;
-}
-
-static int mv_otg_set_peripheral(struct usb_otg *otg,
- struct usb_gadget *gadget)
-{
- otg->gadget = gadget;
-
- return 0;
-}
-
-static void mv_otg_run_state_machine(struct mv_otg *mvotg,
- unsigned long delay)
-{
- dev_dbg(&mvotg->pdev->dev, "transceiver is updated\n");
- if (!mvotg->qwork)
- return;
-
- queue_delayed_work(mvotg->qwork, &mvotg->work, delay);
-}
-
-static void mv_otg_timer_await_bcon(struct timer_list *t)
-{
- struct mv_otg *mvotg = from_timer(mvotg, t,
- otg_ctrl.timer[A_WAIT_BCON_TIMER]);
-
- mvotg->otg_ctrl.a_wait_bcon_timeout = 1;
-
- dev_info(&mvotg->pdev->dev, "B Device No Response!\n");
-
- if (spin_trylock(&mvotg->wq_lock)) {
- mv_otg_run_state_machine(mvotg, 0);
- spin_unlock(&mvotg->wq_lock);
- }
-}
-
-static int mv_otg_cancel_timer(struct mv_otg *mvotg, unsigned int id)
-{
- struct timer_list *timer;
-
- if (id >= OTG_TIMER_NUM)
- return -EINVAL;
-
- timer = &mvotg->otg_ctrl.timer[id];
-
- if (timer_pending(timer))
- del_timer(timer);
-
- return 0;
-}
-
-static int mv_otg_set_timer(struct mv_otg *mvotg, unsigned int id,
- unsigned long interval)
-{
- struct timer_list *timer;
-
- if (id >= OTG_TIMER_NUM)
- return -EINVAL;
-
- timer = &mvotg->otg_ctrl.timer[id];
- if (timer_pending(timer)) {
- dev_err(&mvotg->pdev->dev, "Timer%d is already running\n", id);
- return -EBUSY;
- }
-
- timer->expires = jiffies + interval;
- add_timer(timer);
-
- return 0;
-}
-
-static int mv_otg_reset(struct mv_otg *mvotg)
-{
- u32 tmp;
- int ret;
-
- /* Stop the controller */
- tmp = readl(&mvotg->op_regs->usbcmd);
- tmp &= ~USBCMD_RUN_STOP;
- writel(tmp, &mvotg->op_regs->usbcmd);
-
- /* Reset the controller to get default values */
- writel(USBCMD_CTRL_RESET, &mvotg->op_regs->usbcmd);
-
- ret = readl_poll_timeout_atomic(&mvotg->op_regs->usbcmd, tmp,
- (tmp & USBCMD_CTRL_RESET), 10, 10000);
- if (ret < 0) {
- dev_err(&mvotg->pdev->dev,
- "Wait for RESET completed TIMEOUT\n");
- return ret;
- }
-
- writel(0x0, &mvotg->op_regs->usbintr);
- tmp = readl(&mvotg->op_regs->usbsts);
- writel(tmp, &mvotg->op_regs->usbsts);
-
- return 0;
-}
-
-static void mv_otg_init_irq(struct mv_otg *mvotg)
-{
- u32 otgsc;
-
- mvotg->irq_en = OTGSC_INTR_A_SESSION_VALID
- | OTGSC_INTR_A_VBUS_VALID;
- mvotg->irq_status = OTGSC_INTSTS_A_SESSION_VALID
- | OTGSC_INTSTS_A_VBUS_VALID;
-
- if (mvotg->pdata->vbus == NULL) {
- mvotg->irq_en |= OTGSC_INTR_B_SESSION_VALID
- | OTGSC_INTR_B_SESSION_END;
- mvotg->irq_status |= OTGSC_INTSTS_B_SESSION_VALID
- | OTGSC_INTSTS_B_SESSION_END;
- }
-
- if (mvotg->pdata->id == NULL) {
- mvotg->irq_en |= OTGSC_INTR_USB_ID;
- mvotg->irq_status |= OTGSC_INTSTS_USB_ID;
- }
-
- otgsc = readl(&mvotg->op_regs->otgsc);
- otgsc |= mvotg->irq_en;
- writel(otgsc, &mvotg->op_regs->otgsc);
-}
-
-static void mv_otg_start_host(struct mv_otg *mvotg, int on)
-{
-#ifdef CONFIG_USB
- struct usb_otg *otg = mvotg->phy.otg;
- struct usb_hcd *hcd;
-
- if (!otg->host)
- return;
-
- dev_info(&mvotg->pdev->dev, "%s host\n", on ? "start" : "stop");
-
- hcd = bus_to_hcd(otg->host);
-
- if (on) {
- usb_add_hcd(hcd, hcd->irq, IRQF_SHARED);
- device_wakeup_enable(hcd->self.controller);
- } else {
- usb_remove_hcd(hcd);
- }
-#endif /* CONFIG_USB */
-}
-
-static void mv_otg_start_periphrals(struct mv_otg *mvotg, int on)
-{
- struct usb_otg *otg = mvotg->phy.otg;
-
- if (!otg->gadget)
- return;
-
- dev_info(mvotg->phy.dev, "gadget %s\n", str_on_off(on));
-
- if (on)
- usb_gadget_vbus_connect(otg->gadget);
- else
- usb_gadget_vbus_disconnect(otg->gadget);
-}
-
-static void otg_clock_enable(struct mv_otg *mvotg)
-{
- clk_prepare_enable(mvotg->clk);
-}
-
-static void otg_clock_disable(struct mv_otg *mvotg)
-{
- clk_disable_unprepare(mvotg->clk);
-}
-
-static int mv_otg_enable_internal(struct mv_otg *mvotg)
-{
- int retval = 0;
-
- if (mvotg->active)
- return 0;
-
- dev_dbg(&mvotg->pdev->dev, "otg enabled\n");
-
- otg_clock_enable(mvotg);
- if (mvotg->pdata->phy_init) {
- retval = mvotg->pdata->phy_init(mvotg->phy_regs);
- if (retval) {
- dev_err(&mvotg->pdev->dev,
- "init phy error %d\n", retval);
- otg_clock_disable(mvotg);
- return retval;
- }
- }
- mvotg->active = 1;
-
- return 0;
-
-}
-
-static int mv_otg_enable(struct mv_otg *mvotg)
-{
- if (mvotg->clock_gating)
- return mv_otg_enable_internal(mvotg);
-
- return 0;
-}
-
-static void mv_otg_disable_internal(struct mv_otg *mvotg)
-{
- if (mvotg->active) {
- dev_dbg(&mvotg->pdev->dev, "otg disabled\n");
- if (mvotg->pdata->phy_deinit)
- mvotg->pdata->phy_deinit(mvotg->phy_regs);
- otg_clock_disable(mvotg);
- mvotg->active = 0;
- }
-}
-
-static void mv_otg_disable(struct mv_otg *mvotg)
-{
- if (mvotg->clock_gating)
- mv_otg_disable_internal(mvotg);
-}
-
-static void mv_otg_update_inputs(struct mv_otg *mvotg)
-{
- struct mv_otg_ctrl *otg_ctrl = &mvotg->otg_ctrl;
- u32 otgsc;
-
- otgsc = readl(&mvotg->op_regs->otgsc);
-
- if (mvotg->pdata->vbus) {
- if (mvotg->pdata->vbus->poll() == VBUS_HIGH) {
- otg_ctrl->b_sess_vld = 1;
- otg_ctrl->b_sess_end = 0;
- } else {
- otg_ctrl->b_sess_vld = 0;
- otg_ctrl->b_sess_end = 1;
- }
- } else {
- otg_ctrl->b_sess_vld = !!(otgsc & OTGSC_STS_B_SESSION_VALID);
- otg_ctrl->b_sess_end = !!(otgsc & OTGSC_STS_B_SESSION_END);
- }
-
- if (mvotg->pdata->id)
- otg_ctrl->id = !!mvotg->pdata->id->poll();
- else
- otg_ctrl->id = !!(otgsc & OTGSC_STS_USB_ID);
-
- if (mvotg->pdata->otg_force_a_bus_req && !otg_ctrl->id)
- otg_ctrl->a_bus_req = 1;
-
- otg_ctrl->a_sess_vld = !!(otgsc & OTGSC_STS_A_SESSION_VALID);
- otg_ctrl->a_vbus_vld = !!(otgsc & OTGSC_STS_A_VBUS_VALID);
-
- dev_dbg(&mvotg->pdev->dev, "%s: ", __func__);
- dev_dbg(&mvotg->pdev->dev, "id %d\n", otg_ctrl->id);
- dev_dbg(&mvotg->pdev->dev, "b_sess_vld %d\n", otg_ctrl->b_sess_vld);
- dev_dbg(&mvotg->pdev->dev, "b_sess_end %d\n", otg_ctrl->b_sess_end);
- dev_dbg(&mvotg->pdev->dev, "a_vbus_vld %d\n", otg_ctrl->a_vbus_vld);
- dev_dbg(&mvotg->pdev->dev, "a_sess_vld %d\n", otg_ctrl->a_sess_vld);
-}
-
-static void mv_otg_update_state(struct mv_otg *mvotg)
-{
- struct mv_otg_ctrl *otg_ctrl = &mvotg->otg_ctrl;
- int old_state = mvotg->phy.otg->state;
-
- switch (old_state) {
- case OTG_STATE_UNDEFINED:
- mvotg->phy.otg->state = OTG_STATE_B_IDLE;
- fallthrough;
- case OTG_STATE_B_IDLE:
- if (otg_ctrl->id == 0)
- mvotg->phy.otg->state = OTG_STATE_A_IDLE;
- else if (otg_ctrl->b_sess_vld)
- mvotg->phy.otg->state = OTG_STATE_B_PERIPHERAL;
- break;
- case OTG_STATE_B_PERIPHERAL:
- if (!otg_ctrl->b_sess_vld || otg_ctrl->id == 0)
- mvotg->phy.otg->state = OTG_STATE_B_IDLE;
- break;
- case OTG_STATE_A_IDLE:
- if (otg_ctrl->id)
- mvotg->phy.otg->state = OTG_STATE_B_IDLE;
- else if (!(otg_ctrl->a_bus_drop) &&
- (otg_ctrl->a_bus_req || otg_ctrl->a_srp_det))
- mvotg->phy.otg->state = OTG_STATE_A_WAIT_VRISE;
- break;
- case OTG_STATE_A_WAIT_VRISE:
- if (otg_ctrl->a_vbus_vld)
- mvotg->phy.otg->state = OTG_STATE_A_WAIT_BCON;
- break;
- case OTG_STATE_A_WAIT_BCON:
- if (otg_ctrl->id || otg_ctrl->a_bus_drop
- || otg_ctrl->a_wait_bcon_timeout) {
- mv_otg_cancel_timer(mvotg, A_WAIT_BCON_TIMER);
- mvotg->otg_ctrl.a_wait_bcon_timeout = 0;
- mvotg->phy.otg->state = OTG_STATE_A_WAIT_VFALL;
- otg_ctrl->a_bus_req = 0;
- } else if (!otg_ctrl->a_vbus_vld) {
- mv_otg_cancel_timer(mvotg, A_WAIT_BCON_TIMER);
- mvotg->otg_ctrl.a_wait_bcon_timeout = 0;
- mvotg->phy.otg->state = OTG_STATE_A_VBUS_ERR;
- } else if (otg_ctrl->b_conn) {
- mv_otg_cancel_timer(mvotg, A_WAIT_BCON_TIMER);
- mvotg->otg_ctrl.a_wait_bcon_timeout = 0;
- mvotg->phy.otg->state = OTG_STATE_A_HOST;
- }
- break;
- case OTG_STATE_A_HOST:
- if (otg_ctrl->id || !otg_ctrl->b_conn
- || otg_ctrl->a_bus_drop)
- mvotg->phy.otg->state = OTG_STATE_A_WAIT_BCON;
- else if (!otg_ctrl->a_vbus_vld)
- mvotg->phy.otg->state = OTG_STATE_A_VBUS_ERR;
- break;
- case OTG_STATE_A_WAIT_VFALL:
- if (otg_ctrl->id
- || (!otg_ctrl->b_conn && otg_ctrl->a_sess_vld)
- || otg_ctrl->a_bus_req)
- mvotg->phy.otg->state = OTG_STATE_A_IDLE;
- break;
- case OTG_STATE_A_VBUS_ERR:
- if (otg_ctrl->id || otg_ctrl->a_clr_err
- || otg_ctrl->a_bus_drop) {
- otg_ctrl->a_clr_err = 0;
- mvotg->phy.otg->state = OTG_STATE_A_WAIT_VFALL;
- }
- break;
- default:
- break;
- }
-}
-
-static void mv_otg_work(struct work_struct *work)
-{
- struct mv_otg *mvotg;
- struct usb_otg *otg;
- int old_state;
-
- mvotg = container_of(to_delayed_work(work), struct mv_otg, work);
-
-run:
- /* work queue is single thread, or we need spin_lock to protect */
- otg = mvotg->phy.otg;
- old_state = otg->state;
-
- if (!mvotg->active)
- return;
-
- mv_otg_update_inputs(mvotg);
- mv_otg_update_state(mvotg);
-
- if (old_state != mvotg->phy.otg->state) {
- dev_info(&mvotg->pdev->dev, "change from state %s to %s\n",
- state_string[old_state],
- state_string[mvotg->phy.otg->state]);
-
- switch (mvotg->phy.otg->state) {
- case OTG_STATE_B_IDLE:
- otg->default_a = 0;
- if (old_state == OTG_STATE_B_PERIPHERAL)
- mv_otg_start_periphrals(mvotg, 0);
- mv_otg_reset(mvotg);
- mv_otg_disable(mvotg);
- usb_phy_set_event(&mvotg->phy, USB_EVENT_NONE);
- break;
- case OTG_STATE_B_PERIPHERAL:
- mv_otg_enable(mvotg);
- mv_otg_start_periphrals(mvotg, 1);
- usb_phy_set_event(&mvotg->phy, USB_EVENT_ENUMERATED);
- break;
- case OTG_STATE_A_IDLE:
- otg->default_a = 1;
- mv_otg_enable(mvotg);
- if (old_state == OTG_STATE_A_WAIT_VFALL)
- mv_otg_start_host(mvotg, 0);
- mv_otg_reset(mvotg);
- break;
- case OTG_STATE_A_WAIT_VRISE:
- mv_otg_set_vbus(otg, 1);
- break;
- case OTG_STATE_A_WAIT_BCON:
- if (old_state != OTG_STATE_A_HOST)
- mv_otg_start_host(mvotg, 1);
- mv_otg_set_timer(mvotg, A_WAIT_BCON_TIMER,
- T_A_WAIT_BCON);
- /*
- * Now, we directly enter A_HOST. So set b_conn = 1
- * here. In fact, it need host driver to notify us.
- */
- mvotg->otg_ctrl.b_conn = 1;
- break;
- case OTG_STATE_A_HOST:
- break;
- case OTG_STATE_A_WAIT_VFALL:
- /*
- * Now, we has exited A_HOST. So set b_conn = 0
- * here. In fact, it need host driver to notify us.
- */
- mvotg->otg_ctrl.b_conn = 0;
- mv_otg_set_vbus(otg, 0);
- break;
- case OTG_STATE_A_VBUS_ERR:
- break;
- default:
- break;
- }
- goto run;
- }
-}
-
-static irqreturn_t mv_otg_irq(int irq, void *dev)
-{
- struct mv_otg *mvotg = dev;
- u32 otgsc;
-
- otgsc = readl(&mvotg->op_regs->otgsc);
- writel(otgsc, &mvotg->op_regs->otgsc);
-
- /*
- * if we have vbus, then the vbus detection for B-device
- * will be done by mv_otg_inputs_irq().
- */
- if (mvotg->pdata->vbus)
- if ((otgsc & OTGSC_STS_USB_ID) &&
- !(otgsc & OTGSC_INTSTS_USB_ID))
- return IRQ_NONE;
-
- if ((otgsc & mvotg->irq_status) == 0)
- return IRQ_NONE;
-
- mv_otg_run_state_machine(mvotg, 0);
-
- return IRQ_HANDLED;
-}
-
-static irqreturn_t mv_otg_inputs_irq(int irq, void *dev)
-{
- struct mv_otg *mvotg = dev;
-
- /* The clock may disabled at this time */
- if (!mvotg->active) {
- mv_otg_enable(mvotg);
- mv_otg_init_irq(mvotg);
- }
-
- mv_otg_run_state_machine(mvotg, 0);
-
- return IRQ_HANDLED;
-}
-
-static ssize_t
-a_bus_req_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct mv_otg *mvotg = dev_get_drvdata(dev);
- return scnprintf(buf, PAGE_SIZE, "%d\n",
- mvotg->otg_ctrl.a_bus_req);
-}
-
-static ssize_t
-a_bus_req_store(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct mv_otg *mvotg = dev_get_drvdata(dev);
-
- if (count > 2)
- return -1;
-
- /* We will use this interface to change to A device */
- if (mvotg->phy.otg->state != OTG_STATE_B_IDLE
- && mvotg->phy.otg->state != OTG_STATE_A_IDLE)
- return -1;
-
- /* The clock may disabled and we need to set irq for ID detected */
- mv_otg_enable(mvotg);
- mv_otg_init_irq(mvotg);
-
- if (buf[0] == '1') {
- mvotg->otg_ctrl.a_bus_req = 1;
- mvotg->otg_ctrl.a_bus_drop = 0;
- dev_dbg(&mvotg->pdev->dev,
- "User request: a_bus_req = 1\n");
-
- if (spin_trylock(&mvotg->wq_lock)) {
- mv_otg_run_state_machine(mvotg, 0);
- spin_unlock(&mvotg->wq_lock);
- }
- }
-
- return count;
-}
-
-static DEVICE_ATTR_RW(a_bus_req);
-
-static ssize_t
-a_clr_err_store(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct mv_otg *mvotg = dev_get_drvdata(dev);
- if (!mvotg->phy.otg->default_a)
- return -1;
-
- if (count > 2)
- return -1;
-
- if (buf[0] == '1') {
- mvotg->otg_ctrl.a_clr_err = 1;
- dev_dbg(&mvotg->pdev->dev,
- "User request: a_clr_err = 1\n");
- }
-
- if (spin_trylock(&mvotg->wq_lock)) {
- mv_otg_run_state_machine(mvotg, 0);
- spin_unlock(&mvotg->wq_lock);
- }
-
- return count;
-}
-
-static DEVICE_ATTR_WO(a_clr_err);
-
-static ssize_t
-a_bus_drop_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct mv_otg *mvotg = dev_get_drvdata(dev);
- return scnprintf(buf, PAGE_SIZE, "%d\n",
- mvotg->otg_ctrl.a_bus_drop);
-}
-
-static ssize_t
-a_bus_drop_store(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct mv_otg *mvotg = dev_get_drvdata(dev);
- if (!mvotg->phy.otg->default_a)
- return -1;
-
- if (count > 2)
- return -1;
-
- if (buf[0] == '0') {
- mvotg->otg_ctrl.a_bus_drop = 0;
- dev_dbg(&mvotg->pdev->dev,
- "User request: a_bus_drop = 0\n");
- } else if (buf[0] == '1') {
- mvotg->otg_ctrl.a_bus_drop = 1;
- mvotg->otg_ctrl.a_bus_req = 0;
- dev_dbg(&mvotg->pdev->dev,
- "User request: a_bus_drop = 1\n");
- dev_dbg(&mvotg->pdev->dev,
- "User request: and a_bus_req = 0\n");
- }
-
- if (spin_trylock(&mvotg->wq_lock)) {
- mv_otg_run_state_machine(mvotg, 0);
- spin_unlock(&mvotg->wq_lock);
- }
-
- return count;
-}
-
-static DEVICE_ATTR_RW(a_bus_drop);
-
-static struct attribute *inputs_attrs[] = {
- &dev_attr_a_bus_req.attr,
- &dev_attr_a_clr_err.attr,
- &dev_attr_a_bus_drop.attr,
- NULL,
-};
-
-static const struct attribute_group inputs_attr_group = {
- .name = "inputs",
- .attrs = inputs_attrs,
-};
-
-static const struct attribute_group *mv_otg_groups[] = {
- &inputs_attr_group,
- NULL,
-};
-
-static void mv_otg_remove(struct platform_device *pdev)
-{
- struct mv_otg *mvotg = platform_get_drvdata(pdev);
-
- if (mvotg->qwork)
- destroy_workqueue(mvotg->qwork);
-
- mv_otg_disable(mvotg);
-
- usb_remove_phy(&mvotg->phy);
-}
-
-static int mv_otg_probe(struct platform_device *pdev)
-{
- struct mv_usb_platform_data *pdata = dev_get_platdata(&pdev->dev);
- struct mv_otg *mvotg;
- struct usb_otg *otg;
- struct resource *r;
- int retval = 0, i;
-
- if (pdata == NULL) {
- dev_err(&pdev->dev, "failed to get platform data\n");
- return -ENODEV;
- }
-
- mvotg = devm_kzalloc(&pdev->dev, sizeof(*mvotg), GFP_KERNEL);
- if (!mvotg)
- return -ENOMEM;
-
- otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL);
- if (!otg)
- return -ENOMEM;
-
- platform_set_drvdata(pdev, mvotg);
-
- mvotg->pdev = pdev;
- mvotg->pdata = pdata;
-
- mvotg->clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(mvotg->clk))
- return PTR_ERR(mvotg->clk);
-
- mvotg->qwork = create_singlethread_workqueue("mv_otg_queue");
- if (!mvotg->qwork) {
- dev_dbg(&pdev->dev, "cannot create workqueue for OTG\n");
- return -ENOMEM;
- }
-
- INIT_DELAYED_WORK(&mvotg->work, mv_otg_work);
-
- /* OTG common part */
- mvotg->pdev = pdev;
- mvotg->phy.dev = &pdev->dev;
- mvotg->phy.otg = otg;
- mvotg->phy.label = driver_name;
-
- otg->state = OTG_STATE_UNDEFINED;
- otg->usb_phy = &mvotg->phy;
- otg->set_host = mv_otg_set_host;
- otg->set_peripheral = mv_otg_set_peripheral;
- otg->set_vbus = mv_otg_set_vbus;
-
- for (i = 0; i < OTG_TIMER_NUM; i++)
- timer_setup(&mvotg->otg_ctrl.timer[i],
- mv_otg_timer_await_bcon, 0);
-
- r = platform_get_resource_byname(mvotg->pdev,
- IORESOURCE_MEM, "phyregs");
- if (r == NULL) {
- dev_err(&pdev->dev, "no phy I/O memory resource defined\n");
- retval = -ENODEV;
- goto err_destroy_workqueue;
- }
-
- mvotg->phy_regs = devm_ioremap(&pdev->dev, r->start, resource_size(r));
- if (mvotg->phy_regs == NULL) {
- dev_err(&pdev->dev, "failed to map phy I/O memory\n");
- retval = -EFAULT;
- goto err_destroy_workqueue;
- }
-
- r = platform_get_resource_byname(mvotg->pdev,
- IORESOURCE_MEM, "capregs");
- if (r == NULL) {
- dev_err(&pdev->dev, "no I/O memory resource defined\n");
- retval = -ENODEV;
- goto err_destroy_workqueue;
- }
-
- mvotg->cap_regs = devm_ioremap(&pdev->dev, r->start, resource_size(r));
- if (mvotg->cap_regs == NULL) {
- dev_err(&pdev->dev, "failed to map I/O memory\n");
- retval = -EFAULT;
- goto err_destroy_workqueue;
- }
-
- /* we will acces controller register, so enable the udc controller */
- retval = mv_otg_enable_internal(mvotg);
- if (retval) {
- dev_err(&pdev->dev, "mv otg enable error %d\n", retval);
- goto err_destroy_workqueue;
- }
-
- mvotg->op_regs =
- (struct mv_otg_regs __iomem *) ((unsigned long) mvotg->cap_regs
- + (readl(mvotg->cap_regs) & CAPLENGTH_MASK));
-
- if (pdata->id) {
- retval = devm_request_threaded_irq(&pdev->dev, pdata->id->irq,
- NULL, mv_otg_inputs_irq,
- IRQF_ONESHOT, "id", mvotg);
- if (retval) {
- dev_info(&pdev->dev,
- "Failed to request irq for ID\n");
- pdata->id = NULL;
- }
- }
-
- if (pdata->vbus) {
- mvotg->clock_gating = 1;
- retval = devm_request_threaded_irq(&pdev->dev, pdata->vbus->irq,
- NULL, mv_otg_inputs_irq,
- IRQF_ONESHOT, "vbus", mvotg);
- if (retval) {
- dev_info(&pdev->dev,
- "Failed to request irq for VBUS, "
- "disable clock gating\n");
- mvotg->clock_gating = 0;
- pdata->vbus = NULL;
- }
- }
-
- if (pdata->disable_otg_clock_gating)
- mvotg->clock_gating = 0;
-
- mv_otg_reset(mvotg);
- mv_otg_init_irq(mvotg);
-
- r = platform_get_resource(mvotg->pdev, IORESOURCE_IRQ, 0);
- if (r == NULL) {
- dev_err(&pdev->dev, "no IRQ resource defined\n");
- retval = -ENODEV;
- goto err_disable_clk;
- }
-
- mvotg->irq = r->start;
- if (devm_request_irq(&pdev->dev, mvotg->irq, mv_otg_irq, IRQF_SHARED,
- driver_name, mvotg)) {
- dev_err(&pdev->dev, "Request irq %d for OTG failed\n",
- mvotg->irq);
- mvotg->irq = 0;
- retval = -ENODEV;
- goto err_disable_clk;
- }
-
- retval = usb_add_phy(&mvotg->phy, USB_PHY_TYPE_USB2);
- if (retval < 0) {
- dev_err(&pdev->dev, "can't register transceiver, %d\n",
- retval);
- goto err_disable_clk;
- }
-
- spin_lock_init(&mvotg->wq_lock);
- if (spin_trylock(&mvotg->wq_lock)) {
- mv_otg_run_state_machine(mvotg, 2 * HZ);
- spin_unlock(&mvotg->wq_lock);
- }
-
- dev_info(&pdev->dev,
- "successful probe OTG device %s clock gating.\n",
- mvotg->clock_gating ? "with" : "without");
-
- return 0;
-
-err_disable_clk:
- mv_otg_disable_internal(mvotg);
-err_destroy_workqueue:
- destroy_workqueue(mvotg->qwork);
-
- return retval;
-}
-
-#ifdef CONFIG_PM
-static int mv_otg_suspend(struct platform_device *pdev, pm_message_t state)
-{
- struct mv_otg *mvotg = platform_get_drvdata(pdev);
-
- if (mvotg->phy.otg->state != OTG_STATE_B_IDLE) {
- dev_info(&pdev->dev,
- "OTG state is not B_IDLE, it is %d!\n",
- mvotg->phy.otg->state);
- return -EAGAIN;
- }
-
- if (!mvotg->clock_gating)
- mv_otg_disable_internal(mvotg);
-
- return 0;
-}
-
-static int mv_otg_resume(struct platform_device *pdev)
-{
- struct mv_otg *mvotg = platform_get_drvdata(pdev);
- u32 otgsc;
-
- if (!mvotg->clock_gating) {
- mv_otg_enable_internal(mvotg);
-
- otgsc = readl(&mvotg->op_regs->otgsc);
- otgsc |= mvotg->irq_en;
- writel(otgsc, &mvotg->op_regs->otgsc);
-
- if (spin_trylock(&mvotg->wq_lock)) {
- mv_otg_run_state_machine(mvotg, 0);
- spin_unlock(&mvotg->wq_lock);
- }
- }
- return 0;
-}
-#endif
-
-static struct platform_driver mv_otg_driver = {
- .probe = mv_otg_probe,
- .remove = mv_otg_remove,
- .driver = {
- .name = driver_name,
- .dev_groups = mv_otg_groups,
- },
-#ifdef CONFIG_PM
- .suspend = mv_otg_suspend,
- .resume = mv_otg_resume,
-#endif
-};
-module_platform_driver(mv_otg_driver);
diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c
index 7490f1798b46..7069dd3f4d0d 100644
--- a/drivers/usb/phy/phy-mxs-usb.c
+++ b/drivers/usb/phy/phy-mxs-usb.c
@@ -769,11 +769,9 @@ static int mxs_phy_probe(struct platform_device *pdev)
return PTR_ERR(base);
clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(clk)) {
- dev_err(&pdev->dev,
- "can't get the clock, err=%ld", PTR_ERR(clk));
- return PTR_ERR(clk);
- }
+ if (IS_ERR(clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(clk),
+ "can't get the clock\n");
mxs_phy = devm_kzalloc(&pdev->dev, sizeof(*mxs_phy), GFP_KERNEL);
if (!mxs_phy)
diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c
index bee222967f6b..00443a7beaeb 100644
--- a/drivers/usb/phy/phy-tegra-usb.c
+++ b/drivers/usb/phy/phy-tegra-usb.c
@@ -29,17 +29,26 @@
#include <linux/usb/tegra_usb_phy.h>
#include <linux/usb/ulpi.h>
+#define USB_TXFILLTUNING 0x154
+#define USB_FIFO_TXFILL_THRES(x) (((x) & 0x1f) << 16)
+#define USB_FIFO_TXFILL_MASK 0x1f0000
+
#define ULPI_VIEWPORT 0x170
/* PORTSC PTS/PHCD bits, Tegra20 only */
#define TEGRA_USB_PORTSC1 0x184
-#define TEGRA_USB_PORTSC1_PTS(x) (((x) & 0x3) << 30)
-#define TEGRA_USB_PORTSC1_PHCD BIT(23)
+#define TEGRA_USB_PORTSC1_PTS(x) (((x) & 0x3) << 30)
+#define TEGRA_USB_PORTSC1_PHCD BIT(23)
+#define TEGRA_USB_PORTSC1_WKOC BIT(22)
+#define TEGRA_USB_PORTSC1_WKDS BIT(21)
+#define TEGRA_USB_PORTSC1_WKCN BIT(20)
/* HOSTPC1 PTS/PHCD bits, Tegra30 and above */
+#define TEGRA30_USB_PORTSC1 0x174
#define TEGRA_USB_HOSTPC1_DEVLC 0x1b4
-#define TEGRA_USB_HOSTPC1_DEVLC_PTS(x) (((x) & 0x7) << 29)
-#define TEGRA_USB_HOSTPC1_DEVLC_PHCD BIT(22)
+#define TEGRA_USB_HOSTPC1_DEVLC_PTS(x) (((x) & 0x7) << 29)
+#define TEGRA_USB_HOSTPC1_DEVLC_PHCD BIT(22)
+#define TEGRA_USB_HOSTPC1_DEVLC_PTS_HSIC 4
/* Bits of PORTSC1, which will get cleared by writing 1 into them */
#define TEGRA_PORTSC1_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC)
@@ -51,11 +60,12 @@
#define USB_SUSP_CLR BIT(5)
#define USB_PHY_CLK_VALID BIT(7)
#define UTMIP_RESET BIT(11)
-#define UHSIC_RESET BIT(11)
#define UTMIP_PHY_ENABLE BIT(12)
#define ULPI_PHY_ENABLE BIT(13)
#define USB_SUSP_SET BIT(14)
+#define UHSIC_RESET BIT(14)
#define USB_WAKEUP_DEBOUNCE_COUNT(x) (((x) & 0x7) << 16)
+#define UHSIC_PHY_ENABLE BIT(19)
#define USB_PHY_VBUS_SENSORS 0x404
#define B_SESS_VLD_WAKEUP_EN BIT(14)
@@ -156,6 +166,58 @@
#define UTMIP_BIAS_CFG1 0x83c
#define UTMIP_BIAS_PDTRK_COUNT(x) (((x) & 0x1f) << 3)
+/*
+ * Tegra20 has no UTMIP registers on PHY2 and UHSIC registers start from 0x800
+ * just where UTMIP registers should have been. This is the case only with Tegra20
+ * Tegra30+ have UTMIP registers at 0x800 and UHSIC registers are shifted by 0x400
+ * to 0xc00, but register layout is preserved.
+ */
+#define UHSIC_PLL_CFG1 0x804
+#define UHSIC_XTAL_FREQ_COUNT(x) (((x) & 0xfff) << 0)
+#define UHSIC_PLLU_ENABLE_DLY_COUNT(x) (((x) & 0x1f) << 14)
+
+#define UHSIC_HSRX_CFG0 0x808
+#define UHSIC_ELASTIC_UNDERRUN_LIMIT(x) (((x) & 0x1f) << 2)
+#define UHSIC_ELASTIC_OVERRUN_LIMIT(x) (((x) & 0x1f) << 8)
+#define UHSIC_IDLE_WAIT(x) (((x) & 0x1f) << 13)
+
+#define UHSIC_HSRX_CFG1 0x80c
+#define UHSIC_HS_SYNC_START_DLY(x) (((x) & 0x1f) << 1)
+
+#define UHSIC_TX_CFG0 0x810
+#define UHSIC_HS_READY_WAIT_FOR_VALID BIT(9)
+
+#define UHSIC_MISC_CFG0 0x814
+#define UHSIC_SUSPEND_EXIT_ON_EDGE BIT(7)
+#define UHSIC_DETECT_SHORT_CONNECT BIT(8)
+#define UHSIC_FORCE_XCVR_MODE BIT(15)
+#define UHSIC_DISABLE_BUSRESET BIT(20)
+
+#define UHSIC_MISC_CFG1 0x818
+#define UHSIC_PLLU_STABLE_COUNT(x) (((x) & 0xfff) << 2)
+
+#define UHSIC_PADS_CFG0 0x81c
+#define UHSIC_TX_RTUNEN 0xf000
+#define UHSIC_TX_RTUNE(x) (((x) & 0xf) << 12)
+
+#define UHSIC_PADS_CFG1 0x820
+#define UHSIC_PD_BG BIT(2)
+#define UHSIC_PD_TX BIT(3)
+#define UHSIC_PD_TRK BIT(4)
+#define UHSIC_PD_RX BIT(5)
+#define UHSIC_PD_ZI BIT(6)
+#define UHSIC_RX_SEL BIT(7)
+#define UHSIC_RPD_DATA BIT(9)
+#define UHSIC_RPD_STROBE BIT(10)
+#define UHSIC_RPU_DATA BIT(11)
+#define UHSIC_RPU_STROBE BIT(12)
+
+#define UHSIC_CMD_CFG0 0x824
+#define UHSIC_PRETEND_CONNECT_DETECT BIT(5)
+
+#define UHSIC_STAT_CFG0 0x828
+#define UHSIC_CONNECT_DETECT BIT(0)
+
/* For Tegra30 and above only, the address is different in Tegra20 */
#define USB_USBMODE 0x1f8
#define USB_USBMODE_MASK (3 << 0)
@@ -174,7 +236,8 @@ struct tegra_xtal_freq {
u8 enable_delay;
u8 stable_count;
u8 active_delay;
- u8 xtal_freq_count;
+ u8 utmi_xtal_freq_count;
+ u16 hsic_xtal_freq_count;
u16 debounce;
};
@@ -184,7 +247,8 @@ static const struct tegra_xtal_freq tegra_freq_table[] = {
.enable_delay = 0x02,
.stable_count = 0x2F,
.active_delay = 0x04,
- .xtal_freq_count = 0x76,
+ .utmi_xtal_freq_count = 0x76,
+ .hsic_xtal_freq_count = 0x1CA,
.debounce = 0x7530,
},
{
@@ -192,7 +256,8 @@ static const struct tegra_xtal_freq tegra_freq_table[] = {
.enable_delay = 0x02,
.stable_count = 0x33,
.active_delay = 0x05,
- .xtal_freq_count = 0x7F,
+ .utmi_xtal_freq_count = 0x7F,
+ .hsic_xtal_freq_count = 0x1F0,
.debounce = 0x7EF4,
},
{
@@ -200,7 +265,8 @@ static const struct tegra_xtal_freq tegra_freq_table[] = {
.enable_delay = 0x03,
.stable_count = 0x4B,
.active_delay = 0x06,
- .xtal_freq_count = 0xBB,
+ .utmi_xtal_freq_count = 0xBB,
+ .hsic_xtal_freq_count = 0x2DD,
.debounce = 0xBB80,
},
{
@@ -208,7 +274,8 @@ static const struct tegra_xtal_freq tegra_freq_table[] = {
.enable_delay = 0x04,
.stable_count = 0x66,
.active_delay = 0x09,
- .xtal_freq_count = 0xFE,
+ .utmi_xtal_freq_count = 0xFE,
+ .hsic_xtal_freq_count = 0x3E0,
.debounce = 0xFDE8,
},
};
@@ -532,7 +599,7 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy)
val = readl_relaxed(base + UTMIP_PLL_CFG1);
val &= ~(UTMIP_XTAL_FREQ_COUNT(~0) |
UTMIP_PLLU_ENABLE_DLY_COUNT(~0));
- val |= UTMIP_XTAL_FREQ_COUNT(phy->freq->xtal_freq_count) |
+ val |= UTMIP_XTAL_FREQ_COUNT(phy->freq->utmi_xtal_freq_count) |
UTMIP_PLLU_ENABLE_DLY_COUNT(phy->freq->enable_delay);
writel_relaxed(val, base + UTMIP_PLL_CFG1);
}
@@ -711,58 +778,6 @@ static int utmi_phy_power_off(struct tegra_usb_phy *phy)
return utmip_pad_power_off(phy);
}
-static void utmi_phy_preresume(struct tegra_usb_phy *phy)
-{
- void __iomem *base = phy->regs;
- u32 val;
-
- val = readl_relaxed(base + UTMIP_TX_CFG0);
- val |= UTMIP_HS_DISCON_DISABLE;
- writel_relaxed(val, base + UTMIP_TX_CFG0);
-}
-
-static void utmi_phy_postresume(struct tegra_usb_phy *phy)
-{
- void __iomem *base = phy->regs;
- u32 val;
-
- val = readl_relaxed(base + UTMIP_TX_CFG0);
- val &= ~UTMIP_HS_DISCON_DISABLE;
- writel_relaxed(val, base + UTMIP_TX_CFG0);
-}
-
-static void utmi_phy_restore_start(struct tegra_usb_phy *phy,
- enum tegra_usb_phy_port_speed port_speed)
-{
- void __iomem *base = phy->regs;
- u32 val;
-
- val = readl_relaxed(base + UTMIP_MISC_CFG0);
- val &= ~UTMIP_DPDM_OBSERVE_SEL(~0);
- if (port_speed == TEGRA_USB_PHY_PORT_SPEED_LOW)
- val |= UTMIP_DPDM_OBSERVE_SEL_FS_K;
- else
- val |= UTMIP_DPDM_OBSERVE_SEL_FS_J;
- writel_relaxed(val, base + UTMIP_MISC_CFG0);
- usleep_range(1, 10);
-
- val = readl_relaxed(base + UTMIP_MISC_CFG0);
- val |= UTMIP_DPDM_OBSERVE;
- writel_relaxed(val, base + UTMIP_MISC_CFG0);
- usleep_range(10, 100);
-}
-
-static void utmi_phy_restore_end(struct tegra_usb_phy *phy)
-{
- void __iomem *base = phy->regs;
- u32 val;
-
- val = readl_relaxed(base + UTMIP_MISC_CFG0);
- val &= ~UTMIP_DPDM_OBSERVE;
- writel_relaxed(val, base + UTMIP_MISC_CFG0);
- usleep_range(10, 100);
-}
-
static int ulpi_phy_power_on(struct tegra_usb_phy *phy)
{
void __iomem *base = phy->regs;
@@ -855,17 +870,170 @@ static int ulpi_phy_power_off(struct tegra_usb_phy *phy)
return 0;
}
+static u32 tegra_hsic_readl(struct tegra_usb_phy *phy, u32 reg)
+{
+ void __iomem *base = phy->regs;
+ u32 shift = phy->soc_config->uhsic_registers_offset;
+
+ return readl_relaxed(base + shift + reg);
+}
+
+static void tegra_hsic_writel(struct tegra_usb_phy *phy, u32 reg, u32 value)
+{
+ void __iomem *base = phy->regs;
+ u32 shift = phy->soc_config->uhsic_registers_offset;
+
+ writel_relaxed(value, base + shift + reg);
+}
+
+static int uhsic_phy_power_on(struct tegra_usb_phy *phy)
+{
+ struct tegra_utmip_config *config = phy->config;
+ void __iomem *base = phy->regs;
+ u32 val;
+ int err = 0;
+
+ val = tegra_hsic_readl(phy, UHSIC_PADS_CFG1);
+ val &= ~(UHSIC_PD_BG | UHSIC_PD_TX | UHSIC_PD_TRK | UHSIC_PD_RX |
+ UHSIC_PD_ZI | UHSIC_RPD_DATA | UHSIC_RPD_STROBE);
+ val |= UHSIC_RX_SEL;
+ tegra_hsic_writel(phy, UHSIC_PADS_CFG1, val);
+
+ udelay(2);
+
+ val = readl_relaxed(base + USB_SUSP_CTRL);
+ val |= UHSIC_RESET;
+ writel_relaxed(val, base + USB_SUSP_CTRL);
+
+ udelay(30);
+
+ val = readl_relaxed(base + USB_SUSP_CTRL);
+ val |= UHSIC_PHY_ENABLE;
+ writel_relaxed(val, base + USB_SUSP_CTRL);
+
+ val = tegra_hsic_readl(phy, UHSIC_HSRX_CFG0);
+ val &= ~(UHSIC_IDLE_WAIT(~0) |
+ UHSIC_ELASTIC_UNDERRUN_LIMIT(~0) |
+ UHSIC_ELASTIC_OVERRUN_LIMIT(~0));
+ val |= UHSIC_IDLE_WAIT(config->idle_wait_delay) |
+ UHSIC_ELASTIC_UNDERRUN_LIMIT(config->elastic_limit) |
+ UHSIC_ELASTIC_OVERRUN_LIMIT(config->elastic_limit);
+ tegra_hsic_writel(phy, UHSIC_HSRX_CFG0, val);
+
+ val = tegra_hsic_readl(phy, UHSIC_HSRX_CFG1);
+ val &= ~UHSIC_HS_SYNC_START_DLY(~0);
+ val |= UHSIC_HS_SYNC_START_DLY(config->hssync_start_delay);
+ tegra_hsic_writel(phy, UHSIC_HSRX_CFG1, val);
+
+ val = tegra_hsic_readl(phy, UHSIC_MISC_CFG0);
+ val |= UHSIC_SUSPEND_EXIT_ON_EDGE;
+ tegra_hsic_writel(phy, UHSIC_MISC_CFG0, val);
+
+ val = tegra_hsic_readl(phy, UHSIC_MISC_CFG1);
+ val &= ~UHSIC_PLLU_STABLE_COUNT(~0);
+ val |= UHSIC_PLLU_STABLE_COUNT(phy->freq->stable_count);
+ tegra_hsic_writel(phy, UHSIC_MISC_CFG1, val);
+
+ val = tegra_hsic_readl(phy, UHSIC_PLL_CFG1);
+ val &= ~(UHSIC_XTAL_FREQ_COUNT(~0) |
+ UHSIC_PLLU_ENABLE_DLY_COUNT(~0));
+ val |= UHSIC_XTAL_FREQ_COUNT(phy->freq->hsic_xtal_freq_count) |
+ UHSIC_PLLU_ENABLE_DLY_COUNT(phy->freq->enable_delay);
+ tegra_hsic_writel(phy, UHSIC_PLL_CFG1, val);
+
+ val = readl_relaxed(base + USB_SUSP_CTRL);
+ val &= ~UHSIC_RESET;
+ writel_relaxed(val, base + USB_SUSP_CTRL);
+
+ udelay(2);
+
+ if (phy->soc_config->requires_usbmode_setup) {
+ val = readl_relaxed(base + USB_USBMODE);
+ val &= ~USB_USBMODE_MASK;
+ if (phy->mode == USB_DR_MODE_HOST)
+ val |= USB_USBMODE_HOST;
+ else
+ val |= USB_USBMODE_DEVICE;
+ writel_relaxed(val, base + USB_USBMODE);
+ }
+
+ set_pts(phy, phy->soc_config->uhsic_pts_value);
+
+ val = readl_relaxed(base + USB_TXFILLTUNING);
+ if ((val & USB_FIFO_TXFILL_MASK) != USB_FIFO_TXFILL_THRES(0x10)) {
+ val = USB_FIFO_TXFILL_THRES(0x10);
+ writel_relaxed(val, base + USB_TXFILLTUNING);
+ }
+
+ val = readl_relaxed(base + phy->soc_config->portsc1_offset);
+ val &= ~(TEGRA_USB_PORTSC1_WKOC | TEGRA_USB_PORTSC1_WKDS |
+ TEGRA_USB_PORTSC1_WKCN);
+ writel_relaxed(val, base + phy->soc_config->portsc1_offset);
+
+ val = tegra_hsic_readl(phy, UHSIC_PADS_CFG0);
+ val &= ~UHSIC_TX_RTUNEN;
+ val |= UHSIC_TX_RTUNE(phy->soc_config->uhsic_tx_rtune);
+ tegra_hsic_writel(phy, UHSIC_PADS_CFG0, val);
+
+ err = utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID,
+ USB_PHY_CLK_VALID);
+
+ if (err)
+ dev_err(phy->u_phy.dev,
+ "Timeout waiting for PHY to stabilize on enable (HSIC)\n");
+
+ return err;
+}
+
+static int uhsic_phy_power_off(struct tegra_usb_phy *phy)
+{
+ void __iomem *base = phy->regs;
+ u32 val;
+
+ set_phcd(phy, true);
+
+ val = tegra_hsic_readl(phy, UHSIC_PADS_CFG1);
+ val |= (UHSIC_PD_BG | UHSIC_PD_TX | UHSIC_PD_TRK | UHSIC_PD_RX |
+ UHSIC_PD_ZI | UHSIC_RPD_DATA | UHSIC_RPD_STROBE);
+ tegra_hsic_writel(phy, UHSIC_PADS_CFG1, val);
+
+ val = readl_relaxed(base + USB_SUSP_CTRL);
+ val |= UHSIC_RESET;
+ writel_relaxed(val, base + USB_SUSP_CTRL);
+
+ udelay(30);
+
+ val = readl_relaxed(base + USB_SUSP_CTRL);
+ val &= ~UHSIC_PHY_ENABLE;
+ writel_relaxed(val, base + USB_SUSP_CTRL);
+
+ return 0;
+}
+
static int tegra_usb_phy_power_on(struct tegra_usb_phy *phy)
{
- int err;
+ int err = 0;
if (phy->powered_on)
return 0;
- if (phy->is_ulpi_phy)
- err = ulpi_phy_power_on(phy);
- else
+ switch (phy->phy_type) {
+ case USBPHY_INTERFACE_MODE_UTMI:
err = utmi_phy_power_on(phy);
+ break;
+
+ case USBPHY_INTERFACE_MODE_ULPI:
+ err = ulpi_phy_power_on(phy);
+ break;
+
+ case USBPHY_INTERFACE_MODE_HSIC:
+ err = uhsic_phy_power_on(phy);
+ break;
+
+ default:
+ break;
+ }
+
if (err)
return err;
@@ -879,15 +1047,28 @@ static int tegra_usb_phy_power_on(struct tegra_usb_phy *phy)
static int tegra_usb_phy_power_off(struct tegra_usb_phy *phy)
{
- int err;
+ int err = 0;
if (!phy->powered_on)
return 0;
- if (phy->is_ulpi_phy)
- err = ulpi_phy_power_off(phy);
- else
+ switch (phy->phy_type) {
+ case USBPHY_INTERFACE_MODE_UTMI:
err = utmi_phy_power_off(phy);
+ break;
+
+ case USBPHY_INTERFACE_MODE_ULPI:
+ err = ulpi_phy_power_off(phy);
+ break;
+
+ case USBPHY_INTERFACE_MODE_HSIC:
+ err = uhsic_phy_power_off(phy);
+ break;
+
+ default:
+ break;
+ }
+
if (err)
return err;
@@ -906,7 +1087,7 @@ static void tegra_usb_phy_shutdown(struct usb_phy *u_phy)
usb_phy_set_wakeup(u_phy, false);
tegra_usb_phy_power_off(phy);
- if (!phy->is_ulpi_phy)
+ if (phy->phy_type == USBPHY_INTERFACE_MODE_UTMI)
utmip_pad_close(phy);
regulator_disable(phy->vbus);
@@ -1092,7 +1273,7 @@ static int tegra_usb_phy_init(struct usb_phy *u_phy)
goto disable_clk;
}
- if (!phy->is_ulpi_phy) {
+ if (phy->phy_type == USBPHY_INTERFACE_MODE_UTMI) {
err = utmip_pad_open(phy);
if (err)
goto disable_vbus;
@@ -1109,7 +1290,7 @@ static int tegra_usb_phy_init(struct usb_phy *u_phy)
return 0;
close_phy:
- if (!phy->is_ulpi_phy)
+ if (phy->phy_type == USBPHY_INTERFACE_MODE_UTMI)
utmip_pad_close(phy);
disable_vbus:
@@ -1123,43 +1304,6 @@ disable_clk:
return err;
}
-void tegra_usb_phy_preresume(struct usb_phy *u_phy)
-{
- struct tegra_usb_phy *phy = to_tegra_usb_phy(u_phy);
-
- if (!phy->is_ulpi_phy)
- utmi_phy_preresume(phy);
-}
-EXPORT_SYMBOL_GPL(tegra_usb_phy_preresume);
-
-void tegra_usb_phy_postresume(struct usb_phy *u_phy)
-{
- struct tegra_usb_phy *phy = to_tegra_usb_phy(u_phy);
-
- if (!phy->is_ulpi_phy)
- utmi_phy_postresume(phy);
-}
-EXPORT_SYMBOL_GPL(tegra_usb_phy_postresume);
-
-void tegra_ehci_phy_restore_start(struct usb_phy *u_phy,
- enum tegra_usb_phy_port_speed port_speed)
-{
- struct tegra_usb_phy *phy = to_tegra_usb_phy(u_phy);
-
- if (!phy->is_ulpi_phy)
- utmi_phy_restore_start(phy, port_speed);
-}
-EXPORT_SYMBOL_GPL(tegra_ehci_phy_restore_start);
-
-void tegra_ehci_phy_restore_end(struct usb_phy *u_phy)
-{
- struct tegra_usb_phy *phy = to_tegra_usb_phy(u_phy);
-
- if (!phy->is_ulpi_phy)
- utmi_phy_restore_end(phy);
-}
-EXPORT_SYMBOL_GPL(tegra_ehci_phy_restore_end);
-
static int read_utmi_param(struct platform_device *pdev, const char *param,
u8 *dest)
{
@@ -1184,8 +1328,6 @@ static int utmi_phy_probe(struct tegra_usb_phy *tegra_phy,
struct resource *res;
int err;
- tegra_phy->is_ulpi_phy = false;
-
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (!res) {
dev_err(&pdev->dev, "Failed to get UTMI pad regs\n");
@@ -1320,6 +1462,10 @@ static const struct tegra_phy_soc_config tegra20_soc_config = {
.requires_usbmode_setup = false,
.requires_extra_tuning_parameters = false,
.requires_pmc_ao_power_up = false,
+ .uhsic_registers_offset = 0,
+ .uhsic_tx_rtune = 0, /* 40 ohm */
+ .uhsic_pts_value = 0, /* UTMI */
+ .portsc1_offset = TEGRA_USB_PORTSC1,
};
static const struct tegra_phy_soc_config tegra30_soc_config = {
@@ -1328,6 +1474,10 @@ static const struct tegra_phy_soc_config tegra30_soc_config = {
.requires_usbmode_setup = true,
.requires_extra_tuning_parameters = true,
.requires_pmc_ao_power_up = true,
+ .uhsic_registers_offset = 0x400,
+ .uhsic_tx_rtune = 8, /* 50 ohm */
+ .uhsic_pts_value = TEGRA_USB_HOSTPC1_DEVLC_PTS_HSIC,
+ .portsc1_offset = TEGRA30_USB_PORTSC1,
};
static const struct of_device_id tegra_usb_phy_id_table[] = {
@@ -1341,7 +1491,6 @@ static int tegra_usb_phy_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct tegra_usb_phy *tegra_phy;
- enum usb_phy_interface phy_type;
struct reset_control *reset;
struct gpio_desc *gpiod;
struct resource *res;
@@ -1403,9 +1552,10 @@ static int tegra_usb_phy_probe(struct platform_device *pdev)
return err;
}
- phy_type = of_usb_get_phy_mode(np);
- switch (phy_type) {
+ tegra_phy->phy_type = of_usb_get_phy_mode(np);
+ switch (tegra_phy->phy_type) {
case USBPHY_INTERFACE_MODE_UTMI:
+ case USBPHY_INTERFACE_MODE_HSIC:
err = utmi_phy_probe(tegra_phy, pdev);
if (err)
return err;
@@ -1430,8 +1580,6 @@ static int tegra_usb_phy_probe(struct platform_device *pdev)
break;
case USBPHY_INTERFACE_MODE_ULPI:
- tegra_phy->is_ulpi_phy = true;
-
tegra_phy->clk = devm_clk_get(&pdev->dev, "ulpi-link");
err = PTR_ERR_OR_ZERO(tegra_phy->clk);
if (err) {
@@ -1471,7 +1619,7 @@ static int tegra_usb_phy_probe(struct platform_device *pdev)
default:
dev_err(&pdev->dev, "phy_type %u is invalid or unsupported\n",
- phy_type);
+ tegra_phy->phy_type);
return -EINVAL;
}
diff --git a/drivers/usb/phy/phy-twl6030-usb.c b/drivers/usb/phy/phy-twl6030-usb.c
index 49d79c1257f3..8c09db750bfd 100644
--- a/drivers/usb/phy/phy-twl6030-usb.c
+++ b/drivers/usb/phy/phy-twl6030-usb.c
@@ -328,9 +328,8 @@ static int twl6030_set_vbus(struct phy_companion *comparator, bool enabled)
static int twl6030_usb_probe(struct platform_device *pdev)
{
- u32 ret;
struct twl6030_usb *twl;
- int status, err;
+ int status, err, ret;
struct device_node *np = pdev->dev.of_node;
struct device *dev = &pdev->dev;
diff --git a/drivers/usb/phy/phy-ulpi.c b/drivers/usb/phy/phy-ulpi.c
index e683a37e3a7a..4df63e67bb37 100644
--- a/drivers/usb/phy/phy-ulpi.c
+++ b/drivers/usb/phy/phy-ulpi.c
@@ -256,29 +256,6 @@ static void otg_ulpi_init(struct usb_phy *phy, struct usb_otg *otg,
}
struct usb_phy *
-otg_ulpi_create(struct usb_phy_io_ops *ops,
- unsigned int flags)
-{
- struct usb_phy *phy;
- struct usb_otg *otg;
-
- phy = kzalloc(sizeof(*phy), GFP_KERNEL);
- if (!phy)
- return NULL;
-
- otg = kzalloc(sizeof(*otg), GFP_KERNEL);
- if (!otg) {
- kfree(phy);
- return NULL;
- }
-
- otg_ulpi_init(phy, otg, ops, flags);
-
- return phy;
-}
-EXPORT_SYMBOL_GPL(otg_ulpi_create);
-
-struct usb_phy *
devm_otg_ulpi_create(struct device *dev,
struct usb_phy_io_ops *ops,
unsigned int flags)
diff --git a/drivers/usb/phy/phy.c b/drivers/usb/phy/phy.c
index e1435bc59662..5a9b9353f343 100644
--- a/drivers/usb/phy/phy.c
+++ b/drivers/usb/phy/phy.c
@@ -646,6 +646,8 @@ int usb_add_phy(struct usb_phy *x, enum usb_phy_type type)
return -EINVAL;
}
+ INIT_LIST_HEAD(&x->head);
+
usb_charger_init(x);
ret = usb_add_extcon(x);
if (ret)
@@ -696,6 +698,8 @@ int usb_add_phy_dev(struct usb_phy *x)
return -EINVAL;
}
+ INIT_LIST_HEAD(&x->head);
+
usb_charger_init(x);
ret = usb_add_extcon(x);
if (ret)
diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c
index 4b35ef216125..8c93bde4b816 100644
--- a/drivers/usb/renesas_usbhs/common.c
+++ b/drivers/usb/renesas_usbhs/common.c
@@ -580,6 +580,10 @@ static const struct of_device_id usbhs_of_match[] = {
.data = &usbhs_rzg2l_plat_info,
},
{
+ .compatible = "renesas,usbhs-r9a09g077",
+ .data = &usbhs_rzg2l_plat_info,
+ },
+ {
.compatible = "renesas,rcar-gen2-usbhs",
.data = &usbhs_rcar_gen2_plat_info,
},
@@ -685,10 +689,29 @@ static int usbhs_probe(struct platform_device *pdev)
INIT_DELAYED_WORK(&priv->notify_hotplug_work, usbhsc_notify_hotplug);
spin_lock_init(usbhs_priv_to_lock(priv));
+ /*
+ * Acquire clocks and enable power management (PM) early in the
+ * probe process, as the driver accesses registers during
+ * initialization. Ensure the device is active before proceeding.
+ */
+ pm_runtime_enable(dev);
+
+ ret = usbhsc_clk_get(dev, priv);
+ if (ret)
+ goto probe_pm_disable;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ goto probe_clk_put;
+
+ ret = usbhsc_clk_prepare_enable(priv);
+ if (ret)
+ goto probe_pm_put;
+
/* call pipe and module init */
ret = usbhs_pipe_probe(priv);
if (ret < 0)
- return ret;
+ goto probe_clk_dis_unprepare;
ret = usbhs_fifo_probe(priv);
if (ret < 0)
@@ -698,19 +721,15 @@ static int usbhs_probe(struct platform_device *pdev)
if (ret < 0)
goto probe_end_fifo_exit;
- /* dev_set_drvdata should be called after usbhs_mod_init */
+ /* platform_set_drvdata() should be called after usbhs_mod_probe() */
platform_set_drvdata(pdev, priv);
ret = reset_control_deassert(priv->rsts);
if (ret)
goto probe_fail_rst;
- ret = usbhsc_clk_get(dev, priv);
- if (ret)
- goto probe_fail_clks;
-
/*
- * deviece reset here because
+ * device reset here because
* USB device might be used in boot loader.
*/
usbhs_sys_clock_ctrl(priv, 0);
@@ -721,7 +740,7 @@ static int usbhs_probe(struct platform_device *pdev)
if (ret) {
dev_warn(dev, "USB function not selected (GPIO)\n");
ret = -ENOTSUPP;
- goto probe_end_mod_exit;
+ goto probe_assert_rest;
}
}
@@ -735,14 +754,19 @@ static int usbhs_probe(struct platform_device *pdev)
ret = usbhs_platform_call(priv, hardware_init, pdev);
if (ret < 0) {
dev_err(dev, "platform init failed.\n");
- goto probe_end_mod_exit;
+ goto probe_assert_rest;
}
/* reset phy for connection */
usbhs_platform_call(priv, phy_reset, pdev);
- /* power control */
- pm_runtime_enable(dev);
+ /*
+ * Disable the clocks that were enabled earlier in the probe path,
+ * and let the driver handle the clocks beyond this point.
+ */
+ usbhsc_clk_disable_unprepare(priv);
+ pm_runtime_put(dev);
+
if (!usbhs_get_dparam(priv, runtime_pwctrl)) {
usbhsc_power_ctrl(priv, 1);
usbhs_mod_autonomy_mode(priv);
@@ -759,9 +783,7 @@ static int usbhs_probe(struct platform_device *pdev)
return ret;
-probe_end_mod_exit:
- usbhsc_clk_put(priv);
-probe_fail_clks:
+probe_assert_rest:
reset_control_assert(priv->rsts);
probe_fail_rst:
usbhs_mod_remove(priv);
@@ -769,6 +791,14 @@ probe_end_fifo_exit:
usbhs_fifo_remove(priv);
probe_end_pipe_exit:
usbhs_pipe_remove(priv);
+probe_clk_dis_unprepare:
+ usbhsc_clk_disable_unprepare(priv);
+probe_pm_put:
+ pm_runtime_put(dev);
+probe_clk_put:
+ usbhsc_clk_put(priv);
+probe_pm_disable:
+ pm_runtime_disable(dev);
dev_info(dev, "probe failed (%d)\n", ret);
@@ -783,24 +813,49 @@ static void usbhs_remove(struct platform_device *pdev)
flush_delayed_work(&priv->notify_hotplug_work);
+ usbhs_platform_call(priv, hardware_exit, pdev);
+ reset_control_assert(priv->rsts);
+
+ /*
+ * Explicitly free the IRQ to ensure the interrupt handler is
+ * disabled and synchronized before freeing resources.
+ * devm_free_irq() calls free_irq() which waits for any running
+ * ISR to complete, preventing UAF.
+ */
+ devm_free_irq(&pdev->dev, priv->irq, priv);
+
+ usbhs_mod_remove(priv);
+ usbhs_fifo_remove(priv);
+ usbhs_pipe_remove(priv);
+
/* power off */
if (!usbhs_get_dparam(priv, runtime_pwctrl))
usbhsc_power_ctrl(priv, 0);
+ usbhsc_clk_put(priv);
pm_runtime_disable(&pdev->dev);
+}
- usbhs_platform_call(priv, hardware_exit, pdev);
- usbhsc_clk_put(priv);
- reset_control_assert(priv->rsts);
- usbhs_mod_remove(priv);
- usbhs_fifo_remove(priv);
- usbhs_pipe_remove(priv);
+static void usbhsc_restore(struct device *dev)
+{
+ struct usbhs_priv *priv = dev_get_drvdata(dev);
+ struct platform_device *pdev = usbhs_priv_to_pdev(priv);
+
+ if (!usbhs_get_dparam(priv, runtime_pwctrl)) {
+ usbhsc_power_ctrl(priv, 1);
+ usbhs_mod_autonomy_mode(priv);
+ }
+
+ usbhs_platform_call(priv, phy_reset, pdev);
+
+ usbhsc_schedule_notify_hotplug(pdev);
}
-static __maybe_unused int usbhsc_suspend(struct device *dev)
+static int usbhsc_suspend(struct device *dev)
{
struct usbhs_priv *priv = dev_get_drvdata(dev);
struct usbhs_mod *mod = usbhs_mod_get_current(priv);
+ int ret;
if (mod) {
usbhs_mod_call(priv, stop, priv);
@@ -810,32 +865,33 @@ static __maybe_unused int usbhsc_suspend(struct device *dev)
if (mod || !usbhs_get_dparam(priv, runtime_pwctrl))
usbhsc_power_ctrl(priv, 0);
- return 0;
+ ret = reset_control_assert(priv->rsts);
+ if (ret)
+ usbhsc_restore(dev);
+
+ return ret;
}
-static __maybe_unused int usbhsc_resume(struct device *dev)
+static int usbhsc_resume(struct device *dev)
{
struct usbhs_priv *priv = dev_get_drvdata(dev);
- struct platform_device *pdev = usbhs_priv_to_pdev(priv);
-
- if (!usbhs_get_dparam(priv, runtime_pwctrl)) {
- usbhsc_power_ctrl(priv, 1);
- usbhs_mod_autonomy_mode(priv);
- }
+ int ret;
- usbhs_platform_call(priv, phy_reset, pdev);
+ ret = reset_control_deassert(priv->rsts);
+ if (ret)
+ return ret;
- usbhsc_schedule_notify_hotplug(pdev);
+ usbhsc_restore(dev);
return 0;
}
-static SIMPLE_DEV_PM_OPS(usbhsc_pm_ops, usbhsc_suspend, usbhsc_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(usbhsc_pm_ops, usbhsc_suspend, usbhsc_resume);
static struct platform_driver renesas_usbhs_driver = {
.driver = {
.name = "renesas_usbhs",
- .pm = &usbhsc_pm_ops,
+ .pm = pm_sleep_ptr(&usbhsc_pm_ops),
.of_match_table = usbhs_of_match,
},
.probe = usbhs_probe,
diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c
index 10607e273879..bac6f8fd0055 100644
--- a/drivers/usb/renesas_usbhs/fifo.c
+++ b/drivers/usb/renesas_usbhs/fifo.c
@@ -123,7 +123,7 @@ struct usbhs_pkt *usbhs_pkt_pop(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt)
if (fifo)
chan = usbhsf_dma_chan_get(fifo, pkt);
if (chan) {
- dmaengine_terminate_all(chan);
+ dmaengine_terminate_sync(chan);
usbhsf_dma_unmap(pkt);
} else {
if (usbhs_pipe_is_dir_in(pipe))
diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c
index e8e5723f5412..0c7fe109d5c7 100644
--- a/drivers/usb/renesas_usbhs/mod_gadget.c
+++ b/drivers/usb/renesas_usbhs/mod_gadget.c
@@ -40,7 +40,6 @@ struct usbhsg_gpriv {
struct usb_gadget gadget;
struct usbhs_mod mod;
- struct usbhsg_uep *uep;
int uep_size;
struct usb_gadget_driver *driver;
@@ -53,6 +52,7 @@ struct usbhsg_gpriv {
#define USBHSG_STATUS_WEDGE (1 << 2)
#define USBHSG_STATUS_SELF_POWERED (1 << 3)
#define USBHSG_STATUS_SOFT_CONNECT (1 << 4)
+ struct usbhsg_uep uep[] __counted_by(uep_size);
};
struct usbhsg_recip_handle {
@@ -325,7 +325,7 @@ static void __usbhsg_recip_send_status(struct usbhsg_gpriv *gpriv,
}
/* alloc recip data buffer */
- buf = kmalloc(sizeof(*buf), GFP_ATOMIC);
+ buf = kmalloc_obj(*buf, GFP_ATOMIC);
if (!buf) {
usb_ep_free_request(&dcp->ep, req);
return;
@@ -661,7 +661,7 @@ static struct usb_request *usbhsg_ep_alloc_request(struct usb_ep *ep,
{
struct usbhsg_request *ureq;
- ureq = kzalloc(sizeof *ureq, gfp_flags);
+ ureq = kzalloc_obj(*ureq, gfp_flags);
if (!ureq)
return NULL;
@@ -1084,15 +1084,11 @@ int usbhs_mod_gadget_probe(struct usbhs_priv *priv)
int i;
int ret;
- gpriv = kzalloc(sizeof(struct usbhsg_gpriv), GFP_KERNEL);
+ gpriv = kzalloc_flex(*gpriv, uep, pipe_size);
if (!gpriv)
return -ENOMEM;
- uep = kcalloc(pipe_size, sizeof(struct usbhsg_uep), GFP_KERNEL);
- if (!uep) {
- ret = -ENOMEM;
- goto usbhs_mod_gadget_probe_err_gpriv;
- }
+ gpriv->uep_size = pipe_size;
gpriv->transceiver = devm_usb_get_phy(dev, USB_PHY_TYPE_UNDEFINED);
dev_info(dev, "%stransceiver found\n",
@@ -1115,8 +1111,6 @@ int usbhs_mod_gadget_probe(struct usbhs_priv *priv)
gpriv->mod.name = "gadget";
gpriv->mod.start = usbhsg_start;
gpriv->mod.stop = usbhsg_stop;
- gpriv->uep = uep;
- gpriv->uep_size = pipe_size;
usbhsg_status_init(gpriv);
/*
@@ -1175,9 +1169,6 @@ int usbhs_mod_gadget_probe(struct usbhs_priv *priv)
return 0;
err_add_udc:
- kfree(gpriv->uep);
-
-usbhs_mod_gadget_probe_err_gpriv:
kfree(gpriv);
return ret;
@@ -1189,6 +1180,5 @@ void usbhs_mod_gadget_remove(struct usbhs_priv *priv)
usb_del_gadget_udc(&gpriv->gadget);
- kfree(gpriv->uep);
kfree(gpriv);
}
diff --git a/drivers/usb/renesas_usbhs/mod_host.c b/drivers/usb/renesas_usbhs/mod_host.c
index ae54221011c3..f7ef3a9f82a4 100644
--- a/drivers/usb/renesas_usbhs/mod_host.c
+++ b/drivers/usb/renesas_usbhs/mod_host.c
@@ -158,7 +158,7 @@ static struct usbhsh_request *usbhsh_ureq_alloc(struct usbhsh_hpriv *hpriv,
{
struct usbhsh_request *ureq;
- ureq = kzalloc(sizeof(struct usbhsh_request), mem_flags);
+ ureq = kzalloc_obj(struct usbhsh_request, mem_flags);
if (!ureq)
return NULL;
@@ -374,7 +374,7 @@ static int usbhsh_endpoint_attach(struct usbhsh_hpriv *hpriv,
struct usb_endpoint_descriptor *desc = &ep->desc;
unsigned long flags;
- uep = kzalloc(sizeof(struct usbhsh_ep), mem_flags);
+ uep = kzalloc_obj(struct usbhsh_ep, mem_flags);
if (!uep)
return -ENOMEM;
diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c
index 75fff2e4cbc6..e4e897e8256f 100644
--- a/drivers/usb/renesas_usbhs/pipe.c
+++ b/drivers/usb/renesas_usbhs/pipe.c
@@ -713,11 +713,13 @@ struct usbhs_pipe *usbhs_pipe_malloc(struct usbhs_priv *priv,
/* make sure pipe is not busy */
ret = usbhsp_pipe_barrier(pipe);
if (ret < 0) {
+ usbhsp_put_pipe(pipe);
dev_err(dev, "pipe setup failed %d\n", usbhs_pipe_number(pipe));
return NULL;
}
if (usbhsp_setup_pipecfg(pipe, is_host, dir_in, &pipecfg)) {
+ usbhsp_put_pipe(pipe);
dev_err(dev, "can't setup pipe\n");
return NULL;
}
@@ -820,8 +822,7 @@ int usbhs_pipe_probe(struct usbhs_priv *priv)
return -EINVAL;
}
- info->pipe = kcalloc(pipe_size, sizeof(struct usbhs_pipe),
- GFP_KERNEL);
+ info->pipe = kzalloc_objs(struct usbhs_pipe, pipe_size);
if (!info->pipe)
return -ENOMEM;
diff --git a/drivers/usb/roles/class.c b/drivers/usb/roles/class.c
index 30482d4cf826..edec139b68b5 100644
--- a/drivers/usb/roles/class.c
+++ b/drivers/usb/roles/class.c
@@ -139,9 +139,14 @@ static void *usb_role_switch_match(const struct fwnode_handle *fwnode, const cha
static struct usb_role_switch *
usb_role_switch_is_parent(struct fwnode_handle *fwnode)
{
- struct fwnode_handle *parent = fwnode_get_parent(fwnode);
+ struct fwnode_handle *parent;
struct device *dev;
+ if (!fwnode_device_is_compatible(fwnode, "usb-b-connector"))
+ return NULL;
+
+ parent = fwnode_get_parent(fwnode);
+
if (!fwnode_property_present(parent, "usb-role-switch")) {
fwnode_handle_put(parent);
return NULL;
@@ -364,7 +369,7 @@ usb_role_switch_register(struct device *parent,
if (!desc || !desc->set)
return ERR_PTR(-EINVAL);
- sw = kzalloc(sizeof(*sw), GFP_KERNEL);
+ sw = kzalloc_obj(*sw);
if (!sw)
return ERR_PTR(-ENOMEM);
diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c
index 800b04fe37fa..d974da43fba3 100644
--- a/drivers/usb/serial/ark3116.c
+++ b/drivers/usb/serial/ark3116.c
@@ -126,7 +126,7 @@ static int ark3116_port_probe(struct usb_serial_port *port)
struct usb_serial *serial = port->serial;
struct ark3116_private *priv;
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ priv = kzalloc_obj(*priv);
if (!priv)
return -ENOMEM;
diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c
index 44f5b58beec9..38ac910b1082 100644
--- a/drivers/usb/serial/belkin_sa.c
+++ b/drivers/usb/serial/belkin_sa.c
@@ -114,7 +114,7 @@ static int belkin_sa_port_probe(struct usb_serial_port *port)
struct usb_device *dev = port->serial->dev;
struct belkin_sa_private *priv;
- priv = kmalloc(sizeof(struct belkin_sa_private), GFP_KERNEL);
+ priv = kmalloc_obj(struct belkin_sa_private);
if (!priv)
return -ENOMEM;
@@ -435,43 +435,39 @@ static int belkin_sa_tiocmset(struct tty_struct *tty,
struct belkin_sa_private *priv = usb_get_serial_port_data(port);
unsigned long control_state;
unsigned long flags;
- int retval;
- int rts = 0;
- int dtr = 0;
+ int retval = 0;
spin_lock_irqsave(&priv->lock, flags);
control_state = priv->control_state;
- if (set & TIOCM_RTS) {
+ if (set & TIOCM_RTS)
control_state |= TIOCM_RTS;
- rts = 1;
- }
- if (set & TIOCM_DTR) {
+ if (set & TIOCM_DTR)
control_state |= TIOCM_DTR;
- dtr = 1;
- }
- if (clear & TIOCM_RTS) {
+ if (clear & TIOCM_RTS)
control_state &= ~TIOCM_RTS;
- rts = 0;
- }
- if (clear & TIOCM_DTR) {
+ if (clear & TIOCM_DTR)
control_state &= ~TIOCM_DTR;
- dtr = 0;
- }
priv->control_state = control_state;
spin_unlock_irqrestore(&priv->lock, flags);
- retval = BSA_USB_CMD(BELKIN_SA_SET_RTS_REQUEST, rts);
- if (retval < 0) {
- dev_err(&port->dev, "Set RTS error %d\n", retval);
- goto exit;
+ if ((set | clear) & TIOCM_RTS) {
+ retval = BSA_USB_CMD(BELKIN_SA_SET_RTS_REQUEST,
+ !!(control_state & TIOCM_RTS));
+ if (retval < 0) {
+ dev_err(&port->dev, "Set RTS error %d\n", retval);
+ goto exit;
+ }
}
- retval = BSA_USB_CMD(BELKIN_SA_SET_DTR_REQUEST, dtr);
- if (retval < 0) {
- dev_err(&port->dev, "Set DTR error %d\n", retval);
- goto exit;
+ if ((set | clear) & TIOCM_DTR) {
+ retval = BSA_USB_CMD(BELKIN_SA_SET_DTR_REQUEST,
+ !!(control_state & TIOCM_DTR));
+ if (retval < 0) {
+ dev_err(&port->dev, "Set DTR error %d\n", retval);
+ goto exit;
+ }
}
exit:
return retval;
diff --git a/drivers/usb/serial/bus.c b/drivers/usb/serial/bus.c
index 2fea1b1db4a2..9e2a18c0b218 100644
--- a/drivers/usb/serial/bus.c
+++ b/drivers/usb/serial/bus.c
@@ -17,7 +17,7 @@ static int usb_serial_device_match(struct device *dev,
const struct device_driver *drv)
{
const struct usb_serial_port *port = to_usb_serial_port(dev);
- struct usb_serial_driver *driver = to_usb_serial_driver(drv);
+ const struct usb_serial_driver *driver = to_usb_serial_driver(drv);
/*
* drivers are already assigned to ports in serial_probe so it's
diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c
index 7cc36f84821f..569f4aede215 100644
--- a/drivers/usb/serial/ch341.c
+++ b/drivers/usb/serial/ch341.c
@@ -381,7 +381,7 @@ static int ch341_port_probe(struct usb_serial_port *port)
struct ch341_private *priv;
int r;
- priv = kzalloc(sizeof(struct ch341_private), GFP_KERNEL);
+ priv = kzalloc_obj(struct ch341_private);
if (!priv)
return -ENOMEM;
diff --git a/drivers/usb/serial/console.c b/drivers/usb/serial/console.c
index c3ea3a46ed76..29f09190846e 100644
--- a/drivers/usb/serial/console.c
+++ b/drivers/usb/serial/console.c
@@ -133,7 +133,7 @@ static int usb_console_setup(struct console *co, char *options)
* the termios structure, then later call set_termios to
* configure according to command line arguments
*/
- tty = kzalloc(sizeof(*tty), GFP_KERNEL);
+ tty = kzalloc_obj(*tty);
if (!tty) {
retval = -ENOMEM;
goto reset_open_count;
diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c
index 9960ac2b10b7..333b8b463704 100644
--- a/drivers/usb/serial/cp210x.c
+++ b/drivers/usb/serial/cp210x.c
@@ -1504,7 +1504,7 @@ static int cp210x_gpio_get(struct gpio_chip *gc, unsigned int gpio)
return !!(mask & BIT(gpio));
}
-static void cp210x_gpio_set(struct gpio_chip *gc, unsigned int gpio, int value)
+static int cp210x_gpio_set(struct gpio_chip *gc, unsigned int gpio, int value)
{
struct usb_serial *serial = gpiochip_get_data(gc);
struct cp210x_serial_private *priv = usb_get_serial_data(serial);
@@ -1559,7 +1559,10 @@ out:
if (result < 0) {
dev_err(&serial->interface->dev, "failed to set GPIO value: %d\n",
result);
+ return result;
}
+
+ return 0;
}
static int cp210x_gpio_direction_get(struct gpio_chip *gc, unsigned int gpio)
@@ -1599,9 +1602,8 @@ static int cp210x_gpio_direction_output(struct gpio_chip *gc, unsigned int gpio,
struct cp210x_serial_private *priv = usb_get_serial_data(serial);
priv->gpio_input &= ~BIT(gpio);
- cp210x_gpio_set(gc, gpio, value);
- return 0;
+ return cp210x_gpio_set(gc, gpio, value);
}
static int cp210x_gpio_set_config(struct gpio_chip *gc, unsigned int gpio,
@@ -2004,7 +2006,7 @@ static int cp210x_port_probe(struct usb_serial_port *port)
struct usb_serial *serial = port->serial;
struct cp210x_port_private *port_priv;
- port_priv = kzalloc(sizeof(*port_priv), GFP_KERNEL);
+ port_priv = kzalloc_obj(*port_priv);
if (!port_priv)
return -ENOMEM;
@@ -2161,7 +2163,7 @@ static int cp210x_attach(struct usb_serial *serial)
int result;
struct cp210x_serial_private *priv;
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ priv = kzalloc_obj(*priv);
if (!priv)
return -ENOMEM;
diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c
index 76dd8e7453b5..4e8ceb23c27d 100644
--- a/drivers/usb/serial/cyberjack.c
+++ b/drivers/usb/serial/cyberjack.c
@@ -101,7 +101,7 @@ static int cyberjack_port_probe(struct usb_serial_port *port)
struct cyberjack_private *priv;
int result;
- priv = kmalloc(sizeof(struct cyberjack_private), GFP_KERNEL);
+ priv = kmalloc_obj(struct cyberjack_private);
if (!priv)
return -ENOMEM;
diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c
index e29569d65991..afff1a0f4298 100644
--- a/drivers/usb/serial/cypress_m8.c
+++ b/drivers/usb/serial/cypress_m8.c
@@ -445,7 +445,7 @@ static int cypress_generic_port_probe(struct usb_serial_port *port)
return -ENODEV;
}
- priv = kzalloc(sizeof(struct cypress_private), GFP_KERNEL);
+ priv = kzalloc_obj(struct cypress_private);
if (!priv)
return -ENOMEM;
diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c
index a06485965412..d515df045c4c 100644
--- a/drivers/usb/serial/digi_acceleport.c
+++ b/drivers/usb/serial/digi_acceleport.c
@@ -1209,7 +1209,7 @@ static int digi_port_init(struct usb_serial_port *port, unsigned port_num)
{
struct digi_port *priv;
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ priv = kzalloc_obj(*priv);
if (!priv)
return -ENOMEM;
@@ -1231,7 +1231,7 @@ static int digi_startup(struct usb_serial *serial)
struct digi_serial *serial_priv;
int ret;
- serial_priv = kzalloc(sizeof(*serial_priv), GFP_KERNEL);
+ serial_priv = kzalloc_obj(*serial_priv);
if (!serial_priv)
return -ENOMEM;
diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c
index 530b77fc2f78..9262a2ac97f5 100644
--- a/drivers/usb/serial/f81232.c
+++ b/drivers/usb/serial/f81232.c
@@ -70,7 +70,6 @@ MODULE_DEVICE_TABLE(usb, combined_id_table);
#define F81232_REGISTER_REQUEST 0xa0
#define F81232_GET_REGISTER 0xc0
#define F81232_SET_REGISTER 0x40
-#define F81534A_ACCESS_REG_RETRY 2
#define SERIAL_BASE_ADDRESS 0x0120
#define RECEIVE_BUFFER_REGISTER (0x00 + SERIAL_BASE_ADDRESS)
@@ -824,36 +823,31 @@ static void f81232_lsr_worker(struct work_struct *work)
static int f81534a_ctrl_set_register(struct usb_interface *intf, u16 reg,
u16 size, void *val)
{
- struct usb_device *dev = interface_to_usbdev(intf);
- int retry = F81534A_ACCESS_REG_RETRY;
- int status;
-
- while (retry--) {
- status = usb_control_msg_send(dev,
- 0,
- F81232_REGISTER_REQUEST,
- F81232_SET_REGISTER,
- reg,
- 0,
- val,
- size,
- USB_CTRL_SET_TIMEOUT,
- GFP_KERNEL);
- if (status) {
- status = usb_translate_errors(status);
- if (status == -EIO)
- continue;
- }
-
- break;
- }
-
- if (status) {
- dev_err(&intf->dev, "failed to set register 0x%x: %d\n",
- reg, status);
- }
+ return usb_control_msg_send(interface_to_usbdev(intf),
+ 0,
+ F81232_REGISTER_REQUEST,
+ F81232_SET_REGISTER,
+ reg,
+ 0,
+ val,
+ size,
+ USB_CTRL_SET_TIMEOUT,
+ GFP_KERNEL);
+}
- return status;
+static int f81534a_ctrl_get_register(struct usb_interface *intf, u16 reg,
+ u16 size, void *val)
+{
+ return usb_control_msg_recv(interface_to_usbdev(intf),
+ 0,
+ F81232_REGISTER_REQUEST,
+ F81232_GET_REGISTER,
+ reg,
+ 0,
+ val,
+ size,
+ USB_CTRL_GET_TIMEOUT,
+ GFP_KERNEL);
}
static int f81534a_ctrl_enable_all_ports(struct usb_interface *intf, bool en)
@@ -869,6 +863,29 @@ static int f81534a_ctrl_enable_all_ports(struct usb_interface *intf, bool en)
* bit 0~11 : Serial port enable bit.
*/
if (en) {
+ /*
+ * The Fintek F81532A/534A/535/536 family relies on the
+ * F81534A_CTRL_CMD_ENABLE_PORT (116h) register during
+ * initialization to both determine serial port status and
+ * control port creation.
+ *
+ * If the driver experiences fast load/unload cycles, the
+ * device state may becomes unstable, resulting in the
+ * incomplete generation of serial ports.
+ *
+ * Performing a dummy read operation on the register prior
+ * to the initial write command resolves the issue.
+ *
+ * This clears the device's stale internal state. Subsequent
+ * write operations will correctly generate all serial ports.
+ */
+ status = f81534a_ctrl_get_register(intf,
+ F81534A_CTRL_CMD_ENABLE_PORT,
+ sizeof(enable),
+ enable);
+ if (status)
+ return status;
+
enable[0] = 0xff;
enable[1] = 0x8f;
}
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index 9b34e23b7091..af14548fa03d 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -107,26 +107,24 @@ struct ftdi_quirk {
};
static int ftdi_jtag_probe(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);
-static void ftdi_USB_UIRT_setup(struct ftdi_private *priv);
-static void ftdi_HE_TIRA1_setup(struct ftdi_private *priv);
+static void ftdi_usb_uirt_setup(struct ftdi_private *priv);
+static void ftdi_he_tira1_setup(struct ftdi_private *priv);
static const struct ftdi_quirk ftdi_jtag_quirk = {
.probe = ftdi_jtag_probe,
};
-static const struct ftdi_quirk ftdi_NDI_device_quirk = {
- .probe = ftdi_NDI_device_setup,
+static const struct ftdi_quirk ftdi_ndi_quirk = {
};
-static const struct ftdi_quirk ftdi_USB_UIRT_quirk = {
- .port_probe = ftdi_USB_UIRT_setup,
+static const struct ftdi_quirk ftdi_usb_uirt_quirk = {
+ .port_probe = ftdi_usb_uirt_setup,
};
-static const struct ftdi_quirk ftdi_HE_TIRA1_quirk = {
- .port_probe = ftdi_HE_TIRA1_setup,
+static const struct ftdi_quirk ftdi_he_tira1_quirk = {
+ .port_probe = ftdi_he_tira1_setup,
};
static const struct ftdi_quirk ftdi_stmclite_quirk = {
@@ -590,9 +588,9 @@ static const struct usb_device_id id_table_combined[] = {
{ USB_DEVICE(OCT_VID, OCT_US101_PID) },
{ USB_DEVICE(OCT_VID, OCT_DK201_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_HE_TIRA1_PID),
- .driver_info = (kernel_ulong_t)&ftdi_HE_TIRA1_quirk },
+ .driver_info = (kernel_ulong_t)&ftdi_he_tira1_quirk },
{ USB_DEVICE(FTDI_VID, FTDI_USB_UIRT_PID),
- .driver_info = (kernel_ulong_t)&ftdi_USB_UIRT_quirk },
+ .driver_info = (kernel_ulong_t)&ftdi_usb_uirt_quirk },
{ USB_DEVICE(FTDI_VID, PROTEGO_SPECIAL_1) },
{ USB_DEVICE(FTDI_VID, PROTEGO_R2X0) },
{ USB_DEVICE(FTDI_VID, PROTEGO_SPECIAL_3) },
@@ -628,10 +626,8 @@ static const struct usb_device_id id_table_combined[] = {
{ USB_DEVICE(FTDI_VID, FTDI_IBS_PEDO_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_IBS_PROD_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_TAVIR_STK500_PID) },
- { USB_DEVICE(FTDI_VID, FTDI_TIAO_UMPA_PID),
- .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
- { USB_DEVICE(FTDI_VID, FTDI_NT_ORIONLXM_PID),
- .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+ { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, FTDI_TIAO_UMPA_PID, 1) },
+ { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, FTDI_NT_ORIONLXM_PID, 1) },
{ USB_DEVICE(FTDI_VID, FTDI_NT_ORIONLX_PLUS_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_NT_ORION_IO_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_NT_ORIONMX_PID) },
@@ -794,15 +790,17 @@ static const struct usb_device_id id_table_combined[] = {
{ USB_DEVICE(FTDI_VID, FTDI_TACTRIX_OPENPORT_13U_PID) },
{ USB_DEVICE(ELEKTOR_VID, ELEKTOR_FT323R_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_NDI_HUC_PID),
- .driver_info = (kernel_ulong_t)&ftdi_NDI_device_quirk },
+ .driver_info = (kernel_ulong_t)&ftdi_ndi_quirk },
{ USB_DEVICE(FTDI_VID, FTDI_NDI_SPECTRA_SCU_PID),
- .driver_info = (kernel_ulong_t)&ftdi_NDI_device_quirk },
+ .driver_info = (kernel_ulong_t)&ftdi_ndi_quirk },
{ USB_DEVICE(FTDI_VID, FTDI_NDI_FUTURE_2_PID),
- .driver_info = (kernel_ulong_t)&ftdi_NDI_device_quirk },
+ .driver_info = (kernel_ulong_t)&ftdi_ndi_quirk },
{ USB_DEVICE(FTDI_VID, FTDI_NDI_FUTURE_3_PID),
- .driver_info = (kernel_ulong_t)&ftdi_NDI_device_quirk },
+ .driver_info = (kernel_ulong_t)&ftdi_ndi_quirk },
{ USB_DEVICE(FTDI_VID, FTDI_NDI_AURORA_SCU_PID),
- .driver_info = (kernel_ulong_t)&ftdi_NDI_device_quirk },
+ .driver_info = (kernel_ulong_t)&ftdi_ndi_quirk },
+ { USB_DEVICE(FTDI_NDI_VID, FTDI_NDI_EMGUIDE_GEMINI_PID),
+ .driver_info = (kernel_ulong_t)&ftdi_ndi_quirk },
{ USB_DEVICE(TELLDUS_VID, TELLDUS_TELLSTICK_PID) },
{ USB_DEVICE(NOVITUS_VID, NOVITUS_BONO_E_PID) },
{ USB_DEVICE(FTDI_VID, RTSYSTEMS_USB_VX8_PID) },
@@ -840,24 +838,18 @@ static const struct usb_device_id id_table_combined[] = {
{ USB_DEVICE(FTDI_VID, FTDI_ELSTER_UNICOM_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_PROPOX_JTAGCABLEII_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_PROPOX_ISPCABLEIII_PID) },
- { USB_DEVICE(FTDI_VID, CYBER_CORTEX_AV_PID),
- .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+ { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, CYBER_CORTEX_AV_PID, 1) },
{ USB_DEVICE_INTERFACE_NUMBER(OLIMEX_VID, OLIMEX_ARM_USB_OCD_PID, 1) },
{ USB_DEVICE_INTERFACE_NUMBER(OLIMEX_VID, OLIMEX_ARM_USB_OCD_H_PID, 1) },
{ USB_DEVICE_INTERFACE_NUMBER(OLIMEX_VID, OLIMEX_ARM_USB_TINY_PID, 1) },
{ USB_DEVICE_INTERFACE_NUMBER(OLIMEX_VID, OLIMEX_ARM_USB_TINY_H_PID, 1) },
- { USB_DEVICE(FIC_VID, FIC_NEO1973_DEBUG_PID),
- .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
- { USB_DEVICE(FTDI_VID, FTDI_OOCDLINK_PID),
- .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
- { USB_DEVICE(FTDI_VID, LMI_LM3S_DEVEL_BOARD_PID),
- .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
- { USB_DEVICE(FTDI_VID, LMI_LM3S_EVAL_BOARD_PID),
- .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
- { USB_DEVICE(FTDI_VID, LMI_LM3S_ICDI_BOARD_PID),
- .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
- { USB_DEVICE(FTDI_VID, FTDI_TURTELIZER_PID),
- .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+ { USB_DEVICE_INTERFACE_NUMBER(FIC_VID, FIC_NEO1973_DEBUG_PID, 1) },
+ { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, FTDI_OOCDLINK_PID, 1) },
+ { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, LMI_LM3S_DEVEL_BOARD_PID, 1) },
+ { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, LMI_LM3S_EVAL_BOARD_PID, 1) },
+ { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, LMI_LM3S_ICDI_BOARD_PID, 1) },
+ { USB_DEVICE(FTDI_VID, FTDI_AXE027_PID) },
+ { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, FTDI_TURTELIZER_PID, 1) },
{ USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID_USB60F) },
{ USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID_SCU18) },
{ USB_DEVICE(FTDI_VID, FTDI_REU_TINY_PID) },
@@ -899,17 +891,14 @@ static const struct usb_device_id id_table_combined[] = {
{ USB_DEVICE(ATMEL_VID, STK541_PID) },
{ USB_DEVICE(DE_VID, STB_PID) },
{ USB_DEVICE(DE_VID, WHT_PID) },
- { USB_DEVICE(ADI_VID, ADI_GNICE_PID),
- .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
- { USB_DEVICE(ADI_VID, ADI_GNICEPLUS_PID),
- .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+ { USB_DEVICE_INTERFACE_NUMBER(ADI_VID, ADI_GNICE_PID, 1) },
+ { USB_DEVICE_INTERFACE_NUMBER(ADI_VID, ADI_GNICEPLUS_PID, 1) },
{ USB_DEVICE_AND_INTERFACE_INFO(MICROCHIP_VID, MICROCHIP_USB_BOARD_PID,
USB_CLASS_VENDOR_SPEC,
USB_SUBCLASS_VENDOR_SPEC, 0x00) },
{ USB_DEVICE_INTERFACE_NUMBER(ACTEL_VID, MICROSEMI_ARROW_SF2PLUS_BOARD_PID, 2) },
{ USB_DEVICE(JETI_VID, JETI_SPC1201_PID) },
- { USB_DEVICE(MARVELL_VID, MARVELL_SHEEVAPLUG_PID),
- .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+ { USB_DEVICE_INTERFACE_NUMBER(MARVELL_VID, MARVELL_SHEEVAPLUG_PID, 1) },
{ USB_DEVICE(LARSENBRUSGAARD_VID, LB_ALTITRACK_PID) },
{ USB_DEVICE(GN_OTOMETRICS_VID, AURICAL_USB_PID) },
{ USB_DEVICE(FTDI_VID, PI_C865_PID) },
@@ -932,10 +921,8 @@ static const struct usb_device_id id_table_combined[] = {
{ USB_DEVICE(PI_VID, PI_1016_PID) },
{ USB_DEVICE(KONDO_VID, KONDO_USB_SERIAL_PID) },
{ USB_DEVICE(BAYER_VID, BAYER_CONTOUR_CABLE_PID) },
- { USB_DEVICE(FTDI_VID, MARVELL_OPENRD_PID),
- .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
- { USB_DEVICE(FTDI_VID, TI_XDS100V2_PID),
- .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+ { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, MARVELL_OPENRD_PID, 1) },
+ { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, TI_XDS100V2_PID, 1) },
{ USB_DEVICE(FTDI_VID, HAMEG_HO820_PID) },
{ USB_DEVICE(FTDI_VID, HAMEG_HO720_PID) },
{ USB_DEVICE(FTDI_VID, HAMEG_HO730_PID) },
@@ -944,18 +931,14 @@ static const struct usb_device_id id_table_combined[] = {
{ USB_DEVICE(FTDI_VID, MJSG_SR_RADIO_PID) },
{ USB_DEVICE(FTDI_VID, MJSG_HD_RADIO_PID) },
{ USB_DEVICE(FTDI_VID, MJSG_XM_RADIO_PID) },
- { USB_DEVICE(FTDI_VID, XVERVE_SIGNALYZER_ST_PID),
- .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
- { USB_DEVICE(FTDI_VID, XVERVE_SIGNALYZER_SLITE_PID),
- .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
- { USB_DEVICE(FTDI_VID, XVERVE_SIGNALYZER_SH2_PID),
- .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+ { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, XVERVE_SIGNALYZER_ST_PID, 1) },
+ { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, XVERVE_SIGNALYZER_SLITE_PID, 1) },
+ { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, XVERVE_SIGNALYZER_SH2_PID, 1) },
{ USB_DEVICE(FTDI_VID, XVERVE_SIGNALYZER_SH4_PID),
.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
{ USB_DEVICE(FTDI_VID, SEGWAY_RMP200_PID) },
{ USB_DEVICE(FTDI_VID, ACCESIO_COM4SM_PID) },
- { USB_DEVICE(IONICS_VID, IONICS_PLUGCOMPUTER_PID),
- .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+ { USB_DEVICE_INTERFACE_NUMBER(IONICS_VID, IONICS_PLUGCOMPUTER_PID, 1) },
{ USB_DEVICE(FTDI_VID, FTDI_CHAMSYS_24_MASTER_WING_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_CHAMSYS_PC_WING_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_CHAMSYS_USB_DMX_PID) },
@@ -970,15 +953,12 @@ static const struct usb_device_id id_table_combined[] = {
{ USB_DEVICE(FTDI_VID, FTDI_CINTERION_MC55I_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_FHE_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_DOTEC_PID) },
- { USB_DEVICE(QIHARDWARE_VID, MILKYMISTONE_JTAGSERIAL_PID),
- .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
- { USB_DEVICE(ST_VID, ST_STMCLT_2232_PID),
- .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+ { USB_DEVICE_INTERFACE_NUMBER(QIHARDWARE_VID, MILKYMISTONE_JTAGSERIAL_PID, 1) },
+ { USB_DEVICE_INTERFACE_NUMBER(ST_VID, ST_STMCLT_2232_PID, 1) },
{ USB_DEVICE(ST_VID, ST_STMCLT_4232_PID),
.driver_info = (kernel_ulong_t)&ftdi_stmclite_quirk },
{ USB_DEVICE(FTDI_VID, FTDI_RF_R106) },
- { USB_DEVICE(FTDI_VID, FTDI_DISTORTEC_JTAG_LOCK_PICK_PID),
- .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+ { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, FTDI_DISTORTEC_JTAG_LOCK_PICK_PID, 1) },
{ USB_DEVICE(FTDI_VID, FTDI_LUMEL_PD12_PID) },
/* Crucible Devices */
{ USB_DEVICE(FTDI_VID, FTDI_CT_COMET_PID) },
@@ -1053,8 +1033,7 @@ static const struct usb_device_id id_table_combined[] = {
{ USB_DEVICE(ICPDAS_VID, ICPDAS_I7561U_PID) },
{ USB_DEVICE(ICPDAS_VID, ICPDAS_I7563U_PID) },
{ USB_DEVICE(WICED_VID, WICED_USB20706V2_PID) },
- { USB_DEVICE(TI_VID, TI_CC3200_LAUNCHPAD_PID),
- .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+ { USB_DEVICE_INTERFACE_NUMBER(TI_VID, TI_CC3200_LAUNCHPAD_PID, 1) },
{ USB_DEVICE(CYPRESS_VID, CYPRESS_WICED_BT_USB_PID) },
{ USB_DEVICE(CYPRESS_VID, CYPRESS_WICED_WL_USB_PID) },
{ USB_DEVICE(AIRBUS_DS_VID, AIRBUS_DS_P8GR) },
@@ -1072,11 +1051,10 @@ static const struct usb_device_id id_table_combined[] = {
/* U-Blox devices */
{ USB_DEVICE(UBLOX_VID, UBLOX_C099F9P_ZED_PID) },
{ USB_DEVICE(UBLOX_VID, UBLOX_C099F9P_ODIN_PID) },
+ { USB_DEVICE_INTERFACE_NUMBER(UBLOX_VID, UBLOX_EVK_M101_PID, 2) },
/* FreeCalypso USB adapters */
- { USB_DEVICE(FTDI_VID, FTDI_FALCONIA_JTAG_BUF_PID),
- .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
- { USB_DEVICE(FTDI_VID, FTDI_FALCONIA_JTAG_UNBUF_PID),
- .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+ { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, FTDI_FALCONIA_JTAG_BUF_PID, 1) },
+ { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, FTDI_FALCONIA_JTAG_UNBUF_PID, 1) },
/* GMC devices */
{ USB_DEVICE(GMC_VID, GMC_Z216C_PID) },
/* Altera USB Blaster 3 */
@@ -1093,6 +1071,8 @@ static const struct usb_device_id id_table_combined[] = {
{ USB_DEVICE_INTERFACE_NUMBER(ALTERA_VID, ALTERA_UB3_602E_PID, 1) },
{ USB_DEVICE_INTERFACE_NUMBER(ALTERA_VID, ALTERA_UB3_602E_PID, 2) },
{ USB_DEVICE_INTERFACE_NUMBER(ALTERA_VID, ALTERA_UB3_602E_PID, 3) },
+ /* Abacus Electrics */
+ { USB_DEVICE(FTDI_VID, ABACUS_OPTICAL_PROBE_PID) },
{ } /* Terminating entry */
};
@@ -1266,6 +1246,7 @@ static int update_mctrl(struct usb_serial_port *port, unsigned int set,
static u32 get_ftdi_divisor(struct tty_struct *tty,
struct usb_serial_port *port)
{
+ const struct ftdi_quirk *quirk = usb_get_serial_data(port->serial);
struct ftdi_private *priv = usb_get_serial_port_data(port);
struct device *dev = &port->dev;
u32 div_value = 0;
@@ -1325,16 +1306,8 @@ static u32 get_ftdi_divisor(struct tty_struct *tty,
case FT232R:
case FTX:
if (baud <= 3000000) {
- u16 product_id = le16_to_cpu(
- port->serial->dev->descriptor.idProduct);
- if (((product_id == FTDI_NDI_HUC_PID) ||
- (product_id == FTDI_NDI_SPECTRA_SCU_PID) ||
- (product_id == FTDI_NDI_FUTURE_2_PID) ||
- (product_id == FTDI_NDI_FUTURE_3_PID) ||
- (product_id == FTDI_NDI_AURORA_SCU_PID)) &&
- (baud == 19200)) {
+ if (quirk == &ftdi_ndi_quirk && baud == 19200)
baud = 1200000;
- }
div_value = ftdi_232bm_baud_to_divisor(baud);
} else {
dev_dbg(dev, "%s - Baud rate too high!\n", __func__);
@@ -1855,10 +1828,11 @@ static int ftdi_gpio_get(struct gpio_chip *gc, unsigned int gpio)
return !!(result & BIT(gpio));
}
-static void ftdi_gpio_set(struct gpio_chip *gc, unsigned int gpio, int value)
+static int ftdi_gpio_set(struct gpio_chip *gc, unsigned int gpio, int value)
{
struct usb_serial_port *port = gpiochip_get_data(gc);
struct ftdi_private *priv = usb_get_serial_port_data(port);
+ int result;
mutex_lock(&priv->gpio_lock);
@@ -1867,9 +1841,11 @@ static void ftdi_gpio_set(struct gpio_chip *gc, unsigned int gpio, int value)
else
priv->gpio_value &= ~BIT(gpio);
- ftdi_set_cbus_pins(port);
+ result = ftdi_set_cbus_pins(port);
mutex_unlock(&priv->gpio_lock);
+
+ return result;
}
static int ftdi_gpio_get_multiple(struct gpio_chip *gc, unsigned long *mask,
@@ -1887,19 +1863,22 @@ static int ftdi_gpio_get_multiple(struct gpio_chip *gc, unsigned long *mask,
return 0;
}
-static void ftdi_gpio_set_multiple(struct gpio_chip *gc, unsigned long *mask,
+static int ftdi_gpio_set_multiple(struct gpio_chip *gc, unsigned long *mask,
unsigned long *bits)
{
struct usb_serial_port *port = gpiochip_get_data(gc);
struct ftdi_private *priv = usb_get_serial_port_data(port);
+ int result;
mutex_lock(&priv->gpio_lock);
priv->gpio_value &= ~(*mask);
priv->gpio_value |= *bits & *mask;
- ftdi_set_cbus_pins(port);
+ result = ftdi_set_cbus_pins(port);
mutex_unlock(&priv->gpio_lock);
+
+ return result;
}
static int ftdi_gpio_direction_get(struct gpio_chip *gc, unsigned int gpio)
@@ -2209,7 +2188,7 @@ static int ftdi_port_probe(struct usb_serial_port *port)
struct ftdi_private *priv;
int result;
- priv = kzalloc(sizeof(struct ftdi_private), GFP_KERNEL);
+ priv = kzalloc_obj(struct ftdi_private);
if (!priv)
return -ENOMEM;
@@ -2225,7 +2204,9 @@ static int ftdi_port_probe(struct usb_serial_port *port)
goto err_free;
ftdi_set_max_packet_size(port);
- if (read_latency_timer(port) < 0)
+ if (quirk == &ftdi_ndi_quirk)
+ priv->latency = 1;
+ else if (read_latency_timer(port) < 0)
priv->latency = 16;
write_latency_timer(port);
@@ -2244,20 +2225,22 @@ err_free:
return result;
}
-/* Setup for the USB-UIRT device, which requires hardwired
- * baudrate (38400 gets mapped to 312500) */
-/* Called from usbserial:serial_probe */
-static void ftdi_USB_UIRT_setup(struct ftdi_private *priv)
+/*
+ * Setup for the USB-UIRT device, which requires hardwired baudrate
+ * (38400 gets mapped to 312500).
+ */
+static void ftdi_usb_uirt_setup(struct ftdi_private *priv)
{
priv->flags |= ASYNC_SPD_CUST;
priv->custom_divisor = 77;
priv->force_baud = 38400;
}
-/* Setup for the HE-TIRA1 device, which requires hardwired
- * baudrate (38400 gets mapped to 100000) and RTS-CTS enabled. */
-
-static void ftdi_HE_TIRA1_setup(struct ftdi_private *priv)
+/*
+ * Setup for the HE-TIRA1 device, which requires hardwired baudrate
+ * (38400 gets mapped to 100000) and RTS-CTS enabled.
+ */
+static void ftdi_he_tira1_setup(struct ftdi_private *priv)
{
priv->flags |= ASYNC_SPD_CUST;
priv->custom_divisor = 240;
@@ -2266,39 +2249,6 @@ static void ftdi_HE_TIRA1_setup(struct ftdi_private *priv)
}
/*
- * Module parameter to control latency timer for NDI FTDI-based USB devices.
- * If this value is not set in /etc/modprobe.d/ its value will be set
- * to 1ms.
- */
-static int ndi_latency_timer = 1;
-
-/* Setup for the NDI FTDI-based USB devices, which requires hardwired
- * baudrate (19200 gets mapped to 1200000).
- *
- * Called from usbserial:serial_probe.
- */
-static int ftdi_NDI_device_setup(struct usb_serial *serial)
-{
- struct usb_device *udev = serial->dev;
- int latency = ndi_latency_timer;
-
- if (latency == 0)
- latency = 1;
- if (latency > 99)
- latency = 99;
-
- dev_dbg(&udev->dev, "%s setting NDI device latency to %d\n", __func__, latency);
- dev_info(&udev->dev, "NDI device with a latency value of %d\n", latency);
-
- /* FIXME: errors are not returned */
- usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
- FTDI_SIO_SET_LATENCY_TIMER_REQUEST,
- FTDI_SIO_SET_LATENCY_TIMER_REQUEST_TYPE,
- latency, 0, NULL, 0, WDR_TIMEOUT);
- return 0;
-}
-
-/*
* First port on JTAG adaptors such as Olimex arm-usb-ocd or the FIC/OpenMoko
* Neo1973 Debug Board is reserved for JTAG interface and can be accessed from
* userspace using openocd.
@@ -2308,26 +2258,29 @@ static int ftdi_jtag_probe(struct usb_serial *serial)
struct usb_interface *intf = serial->interface;
int ifnum = intf->cur_altsetting->desc.bInterfaceNumber;
- if (ifnum == 0) {
- dev_info(&intf->dev, "Ignoring interface reserved for JTAG\n");
+ if (ifnum == 0)
return -ENODEV;
- }
return 0;
}
static int ftdi_8u2232c_probe(struct usb_serial *serial)
{
+ struct usb_interface *intf = serial->interface;
struct usb_device *udev = serial->dev;
+ int ifnum = intf->cur_altsetting->desc.bInterfaceNumber;
- if (udev->manufacturer && !strcmp(udev->manufacturer, "CALAO Systems"))
- return ftdi_jtag_probe(serial);
+ if (ifnum == 0) {
+ if (udev->manufacturer &&
+ !strcmp(udev->manufacturer, "CALAO Systems"))
+ return -ENODEV;
- if (udev->product &&
- (!strcmp(udev->product, "Arrow USB Blaster") ||
- !strcmp(udev->product, "BeagleBone/XDS100V2") ||
- !strcmp(udev->product, "SNAP Connect E10")))
- return ftdi_jtag_probe(serial);
+ if (udev->product &&
+ (!strcmp(udev->product, "Arrow USB Blaster") ||
+ !strcmp(udev->product, "BeagleBone/XDS100V2") ||
+ !strcmp(udev->product, "SNAP Connect E10")))
+ return -ENODEV;
+ }
return 0;
}
@@ -2344,10 +2297,8 @@ static int ftdi_stmclite_probe(struct usb_serial *serial)
struct usb_interface *intf = serial->interface;
int ifnum = intf->cur_altsetting->desc.bInterfaceNumber;
- if (ifnum < 2) {
- dev_info(&intf->dev, "Ignoring interface reserved for JTAG\n");
+ if (ifnum < 2)
return -ENODEV;
- }
return 0;
}
@@ -2924,6 +2875,3 @@ module_usb_serial_driver(serial_drivers, id_table_combined);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
-
-module_param(ndi_latency_timer, int, 0644);
-MODULE_PARM_DESC(ndi_latency_timer, "NDI device latency timer override");
diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h
index 52be47d684ea..6c76cfebfd0e 100644
--- a/drivers/usb/serial/ftdi_sio_ids.h
+++ b/drivers/usb/serial/ftdi_sio_ids.h
@@ -96,6 +96,8 @@
#define LMI_LM3S_EVAL_BOARD_PID 0xbcd9
#define LMI_LM3S_ICDI_BOARD_PID 0xbcda
+#define FTDI_AXE027_PID 0xBD90 /* PICAXE AXE027 USB download cable */
+
#define FTDI_TURTELIZER_PID 0xBDC8 /* JTAG/RS-232 adapter by egnite GmbH */
/* OpenDCC (www.opendcc.de) product id */
@@ -204,6 +206,9 @@
#define FTDI_NDI_FUTURE_3_PID 0xDA73 /* NDI future device #3 */
#define FTDI_NDI_AURORA_SCU_PID 0xDA74 /* NDI Aurora SCU */
+#define FTDI_NDI_VID 0x23F2
+#define FTDI_NDI_EMGUIDE_GEMINI_PID 0x0003 /* NDI Emguide Gemini */
+
/*
* ChamSys Limited (www.chamsys.co.uk) USB wing/interface product IDs
*/
@@ -443,6 +448,11 @@
#define LINX_FUTURE_2_PID 0xF44C /* Linx future device */
/*
+ * Abacus Electrics
+ */
+#define ABACUS_OPTICAL_PROBE_PID 0xf458 /* ABACUS ELECTRICS Optical Probe */
+
+/*
* Oceanic product ids
*/
#define FTDI_OCEANIC_PID 0xF460 /* Oceanic dive instrument */
@@ -1606,6 +1616,7 @@
#define UBLOX_VID 0x1546
#define UBLOX_C099F9P_ZED_PID 0x0502
#define UBLOX_C099F9P_ODIN_PID 0x0503
+#define UBLOX_EVK_M101_PID 0x0506
/*
* GMC devices
diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c
index b97ba8ed6801..7205483a0115 100644
--- a/drivers/usb/serial/garmin_gps.c
+++ b/drivers/usb/serial/garmin_gps.c
@@ -267,7 +267,7 @@ static int pkt_add(struct garmin_data *garmin_data_p,
/* process only packets containing data ... */
if (data_length) {
- pkt = kmalloc(struct_size(pkt, data, data_length), GFP_ATOMIC);
+ pkt = kmalloc_flex(*pkt, data, data_length, GFP_ATOMIC);
if (!pkt)
return 0;
@@ -1357,7 +1357,8 @@ static void garmin_unthrottle(struct tty_struct *tty)
*/
static void timeout_handler(struct timer_list *t)
{
- struct garmin_data *garmin_data_p = from_timer(garmin_data_p, t, timer);
+ struct garmin_data *garmin_data_p = timer_container_of(garmin_data_p,
+ t, timer);
/* send the next queued packet to the tty port */
if (garmin_data_p->mode == MODE_NATIVE)
@@ -1372,7 +1373,7 @@ static int garmin_port_probe(struct usb_serial_port *port)
int status;
struct garmin_data *garmin_data_p;
- garmin_data_p = kzalloc(sizeof(struct garmin_data), GFP_KERNEL);
+ garmin_data_p = kzalloc_obj(struct garmin_data);
if (!garmin_data_p)
return -ENOMEM;
diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c
index 1fffda7647f9..3f5889145e51 100644
--- a/drivers/usb/serial/io_edgeport.c
+++ b/drivers/usb/serial/io_edgeport.c
@@ -73,6 +73,7 @@ static const struct usb_device_id edgeport_4port_id_table[] = {
{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_22I) },
{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_412_4) },
{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_COMPATIBLE) },
+ { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BLACKBOX_IC135A) },
{ }
};
@@ -121,6 +122,7 @@ static const struct usb_device_id id_table_combined[] = {
{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8R) },
{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8RR) },
{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_412_8) },
+ { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BLACKBOX_IC135A) },
{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0202) },
{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0203) },
{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0310) },
@@ -470,6 +472,7 @@ static void get_product_info(struct edgeport_serial *edge_serial)
case ION_DEVICE_ID_EDGEPORT_2_DIN:
case ION_DEVICE_ID_EDGEPORT_4_DIN:
case ION_DEVICE_ID_EDGEPORT_16_DUAL_CPU:
+ case ION_DEVICE_ID_BLACKBOX_IC135A:
product_info->IsRS232 = 1;
break;
@@ -498,7 +501,7 @@ static int get_epic_descriptor(struct edgeport_serial *ep)
ep->is_epic = 0;
- epic = kmalloc(sizeof(*epic), GFP_KERNEL);
+ epic = kmalloc_obj(*epic);
if (!epic)
return -ENOMEM;
@@ -2714,7 +2717,7 @@ static int edge_startup(struct usb_serial *serial)
dev = serial->dev;
/* create our private serial structure */
- edge_serial = kzalloc(sizeof(struct edgeport_serial), GFP_KERNEL);
+ edge_serial = kzalloc_obj(struct edgeport_serial);
if (!edge_serial)
return -ENOMEM;
@@ -2956,7 +2959,7 @@ static int edge_port_probe(struct usb_serial_port *port)
{
struct edgeport_port *edge_port;
- edge_port = kzalloc(sizeof(*edge_port), GFP_KERNEL);
+ edge_port = kzalloc_obj(*edge_port);
if (!edge_port)
return -ENOMEM;
diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c
index 7d0584b2a234..cb55370e036f 100644
--- a/drivers/usb/serial/io_ti.c
+++ b/drivers/usb/serial/io_ti.c
@@ -528,7 +528,7 @@ static int tx_active(struct edgeport_port *port)
u8 *lsr;
int bytes_left = 0;
- oedb = kmalloc(sizeof(*oedb), GFP_KERNEL);
+ oedb = kmalloc_obj(*oedb);
if (!oedb)
return -ENOMEM;
@@ -680,7 +680,7 @@ static int check_i2c_image(struct edgeport_serial *serial)
u8 *buffer;
u16 ttype;
- rom_desc = kmalloc(sizeof(*rom_desc), GFP_KERNEL);
+ rom_desc = kmalloc_obj(*rom_desc);
if (!rom_desc)
return -ENOMEM;
@@ -759,7 +759,7 @@ static int get_manuf_info(struct edgeport_serial *serial, u8 *buffer)
struct edge_ti_manuf_descriptor *desc;
struct device *dev = &serial->serial->dev->dev;
- rom_desc = kmalloc(sizeof(*rom_desc), GFP_KERNEL);
+ rom_desc = kmalloc_obj(*rom_desc);
if (!rom_desc)
return -ENOMEM;
@@ -1089,7 +1089,7 @@ static int do_download_mode(struct edgeport_serial *serial,
* Validate Hardware version number
* Read Manufacturing Descriptor from TI Based Edgeport
*/
- ti_manuf_desc = kmalloc(sizeof(*ti_manuf_desc), GFP_KERNEL);
+ ti_manuf_desc = kmalloc_obj(*ti_manuf_desc);
if (!ti_manuf_desc)
return -ENOMEM;
@@ -1107,7 +1107,7 @@ static int do_download_mode(struct edgeport_serial *serial,
return -EINVAL;
}
- rom_desc = kmalloc(sizeof(*rom_desc), GFP_KERNEL);
+ rom_desc = kmalloc_obj(*rom_desc);
if (!rom_desc) {
kfree(ti_manuf_desc);
return -ENOMEM;
@@ -1123,8 +1123,7 @@ static int do_download_mode(struct edgeport_serial *serial,
dev_dbg(dev, "%s - Found Type FIRMWARE (Type 2) record\n",
__func__);
- firmware_version = kmalloc(sizeof(*firmware_version),
- GFP_KERNEL);
+ firmware_version = kmalloc_obj(*firmware_version);
if (!firmware_version) {
kfree(rom_desc);
kfree(ti_manuf_desc);
@@ -1419,7 +1418,7 @@ static int do_boot_mode(struct edgeport_serial *serial,
* Validate Hardware version number
* Read Manufacturing Descriptor from TI Based Edgeport
*/
- ti_manuf_desc = kmalloc(sizeof(*ti_manuf_desc), GFP_KERNEL);
+ ti_manuf_desc = kmalloc_obj(*ti_manuf_desc);
if (!ti_manuf_desc)
return -ENOMEM;
@@ -2219,7 +2218,7 @@ static void change_port_settings(struct tty_struct *tty,
unsigned cflag;
int status;
- config = kmalloc (sizeof (*config), GFP_KERNEL);
+ config = kmalloc_obj(*config);
if (!config) {
tty->termios = *old_termios;
return;
@@ -2458,7 +2457,7 @@ static void edge_heartbeat_work(struct work_struct *work)
serial = container_of(work, struct edgeport_serial,
heartbeat_work.work);
- rom_desc = kmalloc(sizeof(*rom_desc), GFP_KERNEL);
+ rom_desc = kmalloc_obj(*rom_desc);
/* Descriptor address request is enough to reset the firmware timer */
if (!rom_desc || !get_descriptor_addr(serial, I2C_DESC_TYPE_ION,
@@ -2497,7 +2496,7 @@ static int edge_startup(struct usb_serial *serial)
u16 product_id;
/* create our private serial structure */
- edge_serial = kzalloc(sizeof(struct edgeport_serial), GFP_KERNEL);
+ edge_serial = kzalloc_obj(struct edgeport_serial);
if (!edge_serial)
return -ENOMEM;
@@ -2551,7 +2550,7 @@ static int edge_port_probe(struct usb_serial_port *port)
struct edgeport_port *edge_port;
int ret;
- edge_port = kzalloc(sizeof(*edge_port), GFP_KERNEL);
+ edge_port = kzalloc_obj(*edge_port);
if (!edge_port)
return -ENOMEM;
diff --git a/drivers/usb/serial/io_usbvend.h b/drivers/usb/serial/io_usbvend.h
index 9a6f742ad3ab..c82a275e8e76 100644
--- a/drivers/usb/serial/io_usbvend.h
+++ b/drivers/usb/serial/io_usbvend.h
@@ -211,6 +211,7 @@
//
// Definitions for other product IDs
+#define ION_DEVICE_ID_BLACKBOX_IC135A 0x0801 // OEM device (rebranded Edgeport/4)
#define ION_DEVICE_ID_MT4X56USB 0x1403 // OEM device
#define ION_DEVICE_ID_E5805A 0x1A01 // OEM device (rebranded Edgeport/4)
diff --git a/drivers/usb/serial/ipw.c b/drivers/usb/serial/ipw.c
index b1e672c2f423..83709d678b3a 100644
--- a/drivers/usb/serial/ipw.c
+++ b/drivers/usb/serial/ipw.c
@@ -198,7 +198,7 @@ static int ipw_attach(struct usb_serial *serial)
{
struct usb_wwan_intf_private *data;
- data = kzalloc(sizeof(struct usb_wwan_intf_private), GFP_KERNEL);
+ data = kzalloc_obj(struct usb_wwan_intf_private);
if (!data)
return -ENOMEM;
diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c
index a106b71e698f..12e928d25ba1 100644
--- a/drivers/usb/serial/ir-usb.c
+++ b/drivers/usb/serial/ir-usb.c
@@ -126,7 +126,7 @@ irda_usb_find_class_desc(struct usb_serial *serial, unsigned int ifnum)
struct usb_irda_cs_descriptor *desc;
int ret;
- desc = kzalloc(sizeof(*desc), GFP_KERNEL);
+ desc = kzalloc_obj(*desc);
if (!desc)
return NULL;
diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c
index c21dcc9b6f05..0ca111b111c7 100644
--- a/drivers/usb/serial/iuu_phoenix.c
+++ b/drivers/usb/serial/iuu_phoenix.c
@@ -6,7 +6,7 @@
* Copyright (C) 2007 Alain Degreffe (eczema@ecze.com)
*
- * Original code taken from iuutool (Copyright (C) 2006 Juan Carlos Borrás)
+ * Original code taken from iuutool (Copyright (C) 2006 Juan Carlos Borrás)
*
* And tested with help of WB Electronics
*/
@@ -67,7 +67,7 @@ static int iuu_port_probe(struct usb_serial_port *port)
struct iuu_private *priv;
int ret;
- priv = kzalloc(sizeof(struct iuu_private), GFP_KERNEL);
+ priv = kzalloc_obj(struct iuu_private);
if (!priv)
return -ENOMEM;
diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c
index 9129e0282c24..46448843541a 100644
--- a/drivers/usb/serial/keyspan.c
+++ b/drivers/usb/serial/keyspan.c
@@ -2789,7 +2789,7 @@ static int keyspan_startup(struct usb_serial *serial)
}
/* Setup private data for serial driver */
- s_priv = kzalloc(sizeof(struct keyspan_serial_private), GFP_KERNEL);
+ s_priv = kzalloc_obj(struct keyspan_serial_private);
if (!s_priv)
return -ENOMEM;
@@ -2886,7 +2886,7 @@ static int keyspan_port_probe(struct usb_serial_port *port)
s_priv = usb_get_serial_data(serial);
d_details = s_priv->device_details;
- p_priv = kzalloc(sizeof(*p_priv), GFP_KERNEL);
+ p_priv = kzalloc_obj(*p_priv);
if (!p_priv)
return -ENOMEM;
diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c
index e98b479593d3..5036600dd334 100644
--- a/drivers/usb/serial/keyspan_pda.c
+++ b/drivers/usb/serial/keyspan_pda.c
@@ -654,7 +654,7 @@ static int keyspan_pda_port_probe(struct usb_serial_port *port)
struct keyspan_pda_private *priv;
- priv = kmalloc(sizeof(struct keyspan_pda_private), GFP_KERNEL);
+ priv = kmalloc_obj(struct keyspan_pda_private);
if (!priv)
return -ENOMEM;
diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c
index d36155b6d2bf..ed8531a64768 100644
--- a/drivers/usb/serial/kl5kusb105.c
+++ b/drivers/usb/serial/kl5kusb105.c
@@ -189,7 +189,7 @@ static int klsi_105_port_probe(struct usb_serial_port *port)
{
struct klsi_105_private *priv;
- priv = kmalloc(sizeof(*priv), GFP_KERNEL);
+ priv = kmalloc_obj(*priv);
if (!priv)
return -ENOMEM;
@@ -378,7 +378,7 @@ static void klsi_105_set_termios(struct tty_struct *tty,
unsigned long flags;
speed_t baud;
- cfg = kmalloc(sizeof(*cfg), GFP_KERNEL);
+ cfg = kmalloc_obj(*cfg);
if (!cfg)
return;
diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c
index 464433be2034..6126afd67a7b 100644
--- a/drivers/usb/serial/kobil_sct.c
+++ b/drivers/usb/serial/kobil_sct.c
@@ -109,13 +109,28 @@ struct kobil_private {
__u16 device_type;
};
+static int kobil_ctrl_send(struct usb_serial_port *port, u8 req, u16 val)
+{
+ return usb_control_msg(port->serial->dev,
+ usb_sndctrlpipe(port->serial->dev, 0),
+ req, USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT,
+ val, 0, NULL, 0, KOBIL_TIMEOUT);
+}
+
+static int kobil_ctrl_recv(struct usb_serial_port *port, u8 req, u16 val, void *buf, u16 size)
+{
+ return usb_control_msg(port->serial->dev,
+ usb_rcvctrlpipe(port->serial->dev, 0),
+ req, USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_IN,
+ val, 0, buf, size, KOBIL_TIMEOUT);
+}
static int kobil_port_probe(struct usb_serial_port *port)
{
struct usb_serial *serial = port->serial;
struct kobil_private *priv;
- priv = kmalloc(sizeof(struct kobil_private), GFP_KERNEL);
+ priv = kmalloc_obj(struct kobil_private);
if (!priv)
return -ENOMEM;
@@ -163,10 +178,10 @@ static void kobil_init_termios(struct tty_struct *tty)
static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port)
{
struct device *dev = &port->dev;
- int result = 0;
struct kobil_private *priv;
unsigned char *transfer_buffer;
int transfer_buffer_length = 8;
+ int result;
priv = usb_get_serial_port_data(port);
@@ -176,16 +191,8 @@ static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port)
return -ENOMEM;
/* get hardware version */
- result = usb_control_msg(port->serial->dev,
- usb_rcvctrlpipe(port->serial->dev, 0),
- SUSBCRequest_GetMisc,
- USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_IN,
- SUSBCR_MSC_GetHWVersion,
- 0,
- transfer_buffer,
- transfer_buffer_length,
- KOBIL_TIMEOUT
- );
+ result = kobil_ctrl_recv(port, SUSBCRequest_GetMisc, SUSBCR_MSC_GetHWVersion,
+ transfer_buffer, transfer_buffer_length);
dev_dbg(dev, "%s - Send get_HW_version URB returns: %i\n", __func__, result);
if (result >= 3) {
dev_dbg(dev, "Hardware version: %i.%i.%i\n", transfer_buffer[0],
@@ -193,16 +200,8 @@ static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port)
}
/* get firmware version */
- result = usb_control_msg(port->serial->dev,
- usb_rcvctrlpipe(port->serial->dev, 0),
- SUSBCRequest_GetMisc,
- USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_IN,
- SUSBCR_MSC_GetFWVersion,
- 0,
- transfer_buffer,
- transfer_buffer_length,
- KOBIL_TIMEOUT
- );
+ result = kobil_ctrl_recv(port, SUSBCRequest_GetMisc, SUSBCR_MSC_GetFWVersion,
+ transfer_buffer, transfer_buffer_length);
dev_dbg(dev, "%s - Send get_FW_version URB returns: %i\n", __func__, result);
if (result >= 3) {
dev_dbg(dev, "Firmware version: %i.%i.%i\n", transfer_buffer[0],
@@ -212,35 +211,17 @@ static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port)
if (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID ||
priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID) {
/* Setting Baudrate, Parity and Stopbits */
- result = usb_control_msg(port->serial->dev,
- usb_sndctrlpipe(port->serial->dev, 0),
- SUSBCRequest_SetBaudRateParityAndStopBits,
- USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT,
- SUSBCR_SBR_9600 | SUSBCR_SPASB_EvenParity |
- SUSBCR_SPASB_1StopBit,
- 0,
- NULL,
- 0,
- KOBIL_TIMEOUT
- );
+ result = kobil_ctrl_send(port, SUSBCRequest_SetBaudRateParityAndStopBits,
+ SUSBCR_SBR_9600 | SUSBCR_SPASB_EvenParity | SUSBCR_SPASB_1StopBit);
dev_dbg(dev, "%s - Send set_baudrate URB returns: %i\n", __func__, result);
/* reset all queues */
- result = usb_control_msg(port->serial->dev,
- usb_sndctrlpipe(port->serial->dev, 0),
- SUSBCRequest_Misc,
- USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT,
- SUSBCR_MSC_ResetAllQueues,
- 0,
- NULL,
- 0,
- KOBIL_TIMEOUT
- );
+ result = kobil_ctrl_send(port, SUSBCRequest_Misc, SUSBCR_MSC_ResetAllQueues);
dev_dbg(dev, "%s - Send reset_all_queues URB returns: %i\n", __func__, result);
}
if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID ||
- priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID ||
- priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) {
+ priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID ||
+ priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) {
/* start reading (Adapter B 'cause PNP string) */
result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
dev_dbg(dev, "%s - Send read URB returns: %i\n", __func__, result);
@@ -291,10 +272,8 @@ static void kobil_write_int_callback(struct urb *urb)
static int kobil_write(struct tty_struct *tty, struct usb_serial_port *port,
const unsigned char *buf, int count)
{
- int length = 0;
- int result = 0;
- int todo = 0;
struct kobil_private *priv;
+ int length, todo, result;
if (count == 0) {
dev_dbg(&port->dev, "%s - write request of 0 bytes\n", __func__);
@@ -318,9 +297,10 @@ static int kobil_write(struct tty_struct *tty, struct usb_serial_port *port,
if (((priv->device_type != KOBIL_ADAPTER_B_PRODUCT_ID) && (priv->filled > 2) && (priv->filled >= (priv->buf[1] + 3))) ||
((priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID) && (priv->filled > 3) && (priv->filled >= (priv->buf[2] + 4)))) {
/* stop reading (except TWIN and KAAN SIM) */
- if ((priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID)
- || (priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID))
+ if (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID ||
+ priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID) {
usb_kill_urb(port->interrupt_in_urb);
+ }
todo = priv->filled - priv->cur_pos;
@@ -347,7 +327,7 @@ static int kobil_write(struct tty_struct *tty, struct usb_serial_port *port,
/* start reading (except TWIN and KAAN SIM) */
if (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID ||
- priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID) {
+ priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID) {
result = usb_submit_urb(port->interrupt_in_urb,
GFP_ATOMIC);
dev_dbg(&port->dev, "%s - Send read URB returns: %i\n", __func__, result);
@@ -373,8 +353,8 @@ static int kobil_tiocmget(struct tty_struct *tty)
int transfer_buffer_length = 8;
priv = usb_get_serial_port_data(port);
- if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID
- || priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) {
+ if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID ||
+ priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) {
/* This device doesn't support ioctl calls */
return -EINVAL;
}
@@ -384,16 +364,8 @@ static int kobil_tiocmget(struct tty_struct *tty)
if (!transfer_buffer)
return -ENOMEM;
- result = usb_control_msg(port->serial->dev,
- usb_rcvctrlpipe(port->serial->dev, 0),
- SUSBCRequest_GetStatusLineState,
- USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_IN,
- 0,
- 0,
- transfer_buffer,
- transfer_buffer_length,
- KOBIL_TIMEOUT);
-
+ result = kobil_ctrl_recv(port, SUSBCRequest_GetStatusLineState, 0,
+ transfer_buffer, transfer_buffer_length);
dev_dbg(&port->dev, "Send get_status_line_state URB returns: %i\n",
result);
if (result < 1) {
@@ -418,58 +390,41 @@ static int kobil_tiocmset(struct tty_struct *tty,
struct usb_serial_port *port = tty->driver_data;
struct device *dev = &port->dev;
struct kobil_private *priv;
+ int dtr, rts;
int result;
- int dtr = 0;
- int rts = 0;
+ u16 val = 0;
- /* FIXME: locking ? */
priv = usb_get_serial_port_data(port);
- if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID
- || priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) {
+ if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID ||
+ priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) {
/* This device doesn't support ioctl calls */
return -EINVAL;
}
- if (set & TIOCM_RTS)
- rts = 1;
- if (set & TIOCM_DTR)
- dtr = 1;
- if (clear & TIOCM_RTS)
- rts = 0;
- if (clear & TIOCM_DTR)
- dtr = 0;
-
- if (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID) {
- if (dtr != 0)
- dev_dbg(dev, "%s - Setting DTR\n", __func__);
+ dtr = (set | clear) & TIOCM_DTR;
+ rts = (set | clear) & TIOCM_RTS;
+
+ if (dtr && priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID) {
+ if (set & TIOCM_DTR)
+ val = SUSBCR_SSL_SETDTR;
else
- dev_dbg(dev, "%s - Clearing DTR\n", __func__);
- result = usb_control_msg(port->serial->dev,
- usb_sndctrlpipe(port->serial->dev, 0),
- SUSBCRequest_SetStatusLinesOrQueues,
- USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT,
- ((dtr != 0) ? SUSBCR_SSL_SETDTR : SUSBCR_SSL_CLRDTR),
- 0,
- NULL,
- 0,
- KOBIL_TIMEOUT);
- } else {
- if (rts != 0)
- dev_dbg(dev, "%s - Setting RTS\n", __func__);
+ val = SUSBCR_SSL_CLRDTR;
+ } else if (rts) {
+ if (set & TIOCM_RTS)
+ val = SUSBCR_SSL_SETRTS;
else
- dev_dbg(dev, "%s - Clearing RTS\n", __func__);
- result = usb_control_msg(port->serial->dev,
- usb_sndctrlpipe(port->serial->dev, 0),
- SUSBCRequest_SetStatusLinesOrQueues,
- USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT,
- ((rts != 0) ? SUSBCR_SSL_SETRTS : SUSBCR_SSL_CLRRTS),
- 0,
- NULL,
- 0,
- KOBIL_TIMEOUT);
+ val = SUSBCR_SSL_CLRRTS;
}
- dev_dbg(dev, "%s - Send set_status_line URB returns: %i\n", __func__, result);
- return (result < 0) ? result : 0;
+
+ if (val) {
+ result = kobil_ctrl_send(port, SUSBCRequest_SetStatusLinesOrQueues, val);
+ if (result < 0) {
+ dev_err(dev, "failed to set status lines: %d\n", result);
+ return result;
+ }
+ }
+
+ return 0;
}
static void kobil_set_termios(struct tty_struct *tty,
@@ -478,9 +433,9 @@ static void kobil_set_termios(struct tty_struct *tty,
{
struct kobil_private *priv;
int result;
- unsigned short urb_val = 0;
int c_cflag = tty->termios.c_cflag;
speed_t speed;
+ u16 val;
priv = usb_get_serial_port_data(port);
if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID ||
@@ -493,37 +448,34 @@ static void kobil_set_termios(struct tty_struct *tty,
speed = tty_get_baud_rate(tty);
switch (speed) {
case 1200:
- urb_val = SUSBCR_SBR_1200;
+ val = SUSBCR_SBR_1200;
break;
default:
speed = 9600;
fallthrough;
case 9600:
- urb_val = SUSBCR_SBR_9600;
+ val = SUSBCR_SBR_9600;
break;
}
- urb_val |= (c_cflag & CSTOPB) ? SUSBCR_SPASB_2StopBits :
- SUSBCR_SPASB_1StopBit;
+
+ if (c_cflag & CSTOPB)
+ val |= SUSBCR_SPASB_2StopBits;
+ else
+ val |= SUSBCR_SPASB_1StopBit;
+
if (c_cflag & PARENB) {
if (c_cflag & PARODD)
- urb_val |= SUSBCR_SPASB_OddParity;
+ val |= SUSBCR_SPASB_OddParity;
else
- urb_val |= SUSBCR_SPASB_EvenParity;
- } else
- urb_val |= SUSBCR_SPASB_NoParity;
+ val |= SUSBCR_SPASB_EvenParity;
+ } else {
+ val |= SUSBCR_SPASB_NoParity;
+ }
+
tty->termios.c_cflag &= ~CMSPAR;
tty_encode_baud_rate(tty, speed, speed);
- result = usb_control_msg(port->serial->dev,
- usb_sndctrlpipe(port->serial->dev, 0),
- SUSBCRequest_SetBaudRateParityAndStopBits,
- USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT,
- urb_val,
- 0,
- NULL,
- 0,
- KOBIL_TIMEOUT
- );
+ result = kobil_ctrl_send(port, SUSBCRequest_SetBaudRateParityAndStopBits, val);
if (result) {
dev_err(&port->dev, "failed to update line settings: %d\n",
result);
@@ -544,17 +496,7 @@ static int kobil_ioctl(struct tty_struct *tty,
switch (cmd) {
case TCFLSH:
- result = usb_control_msg(port->serial->dev,
- usb_sndctrlpipe(port->serial->dev, 0),
- SUSBCRequest_Misc,
- USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT,
- SUSBCR_MSC_ResetAllQueues,
- 0,
- NULL,
- 0,
- KOBIL_TIMEOUT
- );
-
+ result = kobil_ctrl_send(port, SUSBCRequest_Misc, SUSBCR_MSC_ResetAllQueues);
dev_dbg(&port->dev,
"%s - Send reset_all_queues (FLUSH) URB returns: %i\n",
__func__, result);
diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c
index 2bce8cc03aca..18844b92bd08 100644
--- a/drivers/usb/serial/mct_u232.c
+++ b/drivers/usb/serial/mct_u232.c
@@ -385,7 +385,7 @@ static int mct_u232_port_probe(struct usb_serial_port *port)
return -ENODEV;
}
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ priv = kzalloc_obj(*priv);
if (!priv)
return -ENOMEM;
diff --git a/drivers/usb/serial/metro-usb.c b/drivers/usb/serial/metro-usb.c
index 028878292901..bc834cc48550 100644
--- a/drivers/usb/serial/metro-usb.c
+++ b/drivers/usb/serial/metro-usb.c
@@ -245,7 +245,7 @@ static int metrousb_port_probe(struct usb_serial_port *port)
{
struct metrousb_private *metro_priv;
- metro_priv = kzalloc(sizeof(*metro_priv), GFP_KERNEL);
+ metro_priv = kzalloc_obj(*metro_priv);
if (!metro_priv)
return -ENOMEM;
diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c
index e59bfa7c8030..94459408e7fb 100644
--- a/drivers/usb/serial/mos7720.c
+++ b/drivers/usb/serial/mos7720.c
@@ -555,7 +555,7 @@ static int mos7715_parport_init(struct usb_serial *serial)
struct mos7715_parport *mos_parport;
/* allocate and initialize parallel port control struct */
- mos_parport = kzalloc(sizeof(struct mos7715_parport), GFP_KERNEL);
+ mos_parport = kzalloc_obj(struct mos7715_parport);
if (!mos_parport)
return -ENOMEM;
@@ -1703,7 +1703,7 @@ static int mos7720_port_probe(struct usb_serial_port *port)
{
struct moschip_port *mos7720_port;
- mos7720_port = kzalloc(sizeof(*mos7720_port), GFP_KERNEL);
+ mos7720_port = kzalloc_obj(*mos7720_port);
if (!mos7720_port)
return -ENOMEM;
diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c
index ca3da79afd23..c0d0c24b074b 100644
--- a/drivers/usb/serial/mos7840.c
+++ b/drivers/usb/serial/mos7840.c
@@ -66,29 +66,16 @@
#define MOS_WDR_TIMEOUT 5000 /* default urb timeout */
-#define MOS_PORT1 0x0200
-#define MOS_PORT2 0x0300
-#define MOS_VENREG 0x0000
-#define MOS_MAX_PORT 0x02
-#define MOS_WRITE 0x0E
-#define MOS_READ 0x0D
-
/* Requests */
#define MCS_RD_RTYPE 0xC0
#define MCS_WR_RTYPE 0x40
#define MCS_RDREQ 0x0D
#define MCS_WRREQ 0x0E
-#define MCS_CTRL_TIMEOUT 500
#define VENDOR_READ_LENGTH (0x01)
-#define MAX_NAME_LEN 64
-
#define ZLP_REG1 0x3A /* Zero_Flag_Reg1 58 */
#define ZLP_REG5 0x3E /* Zero_Flag_Reg5 62 */
-/* For higher baud Rates use TIOCEXBAUD */
-#define TIOCEXBAUD 0x5462
-
/*
* Vendor id and device id defines
*
@@ -396,7 +383,7 @@ static void mos7840_set_led_sync(struct usb_serial_port *port, __u16 reg,
static void mos7840_led_off(struct timer_list *t)
{
- struct moschip_port *mcs = from_timer(mcs, t, led_timer1);
+ struct moschip_port *mcs = timer_container_of(mcs, t, led_timer1);
/* Turn off LED */
mos7840_set_led_async(mcs, 0x0300, MODEM_CONTROL_REGISTER);
@@ -406,7 +393,7 @@ static void mos7840_led_off(struct timer_list *t)
static void mos7840_led_flag_off(struct timer_list *t)
{
- struct moschip_port *mcs = from_timer(mcs, t, led_timer2);
+ struct moschip_port *mcs = timer_container_of(mcs, t, led_timer2);
clear_bit_unlock(MOS7840_FLAG_LED_BUSY, &mcs->flags);
}
@@ -1545,7 +1532,7 @@ static int mos7840_port_probe(struct usb_serial_port *port)
pnum = port->port_number;
dev_dbg(&port->dev, "mos7840_startup: configuring port %d\n", pnum);
- mos7840_port = kzalloc(sizeof(struct moschip_port), GFP_KERNEL);
+ mos7840_port = kzalloc_obj(struct moschip_port);
if (!mos7840_port)
return -ENOMEM;
@@ -1690,8 +1677,7 @@ static int mos7840_port_probe(struct usb_serial_port *port)
/* Initialize LED timers */
if (mos7840_port->has_led) {
mos7840_port->led_urb = usb_alloc_urb(0, GFP_KERNEL);
- mos7840_port->led_dr = kmalloc(sizeof(*mos7840_port->led_dr),
- GFP_KERNEL);
+ mos7840_port->led_dr = kmalloc_obj(*mos7840_port->led_dr);
if (!mos7840_port->led_urb || !mos7840_port->led_dr) {
status = -ENOMEM;
goto error;
diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c
index 397ebd5a3e74..aa1e9745f967 100644
--- a/drivers/usb/serial/omninet.c
+++ b/drivers/usb/serial/omninet.c
@@ -113,7 +113,7 @@ static int omninet_port_probe(struct usb_serial_port *port)
{
struct omninet_data *od;
- od = kzalloc(sizeof(*od), GFP_KERNEL);
+ od = kzalloc_obj(*od);
if (!od)
return -ENOMEM;
diff --git a/drivers/usb/serial/opticon.c b/drivers/usb/serial/opticon.c
index 1ee84ccc4bbd..e2bed477ad57 100644
--- a/drivers/usb/serial/opticon.c
+++ b/drivers/usb/serial/opticon.c
@@ -220,7 +220,7 @@ static int opticon_write(struct tty_struct *tty, struct usb_serial_port *port,
/* The connected devices do not have a bulk write endpoint,
* to transmit data to de barcode device the control endpoint is used */
- dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC);
+ dr = kmalloc_obj(struct usb_ctrlrequest, GFP_ATOMIC);
if (!dr)
goto error_no_dr;
@@ -354,7 +354,7 @@ static int opticon_port_probe(struct usb_serial_port *port)
{
struct opticon_private *priv;
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ priv = kzalloc_obj(*priv);
if (!priv)
return -ENOMEM;
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index 5cd26dac2069..42e4cecd28ac 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -273,6 +273,7 @@ static void option_instat_callback(struct urb *urb);
#define QUECTEL_PRODUCT_EM05CN 0x0312
#define QUECTEL_PRODUCT_EM05G_GR 0x0313
#define QUECTEL_PRODUCT_EM05G_RS 0x0314
+#define QUECTEL_PRODUCT_RG255C 0x0316
#define QUECTEL_PRODUCT_EM12 0x0512
#define QUECTEL_PRODUCT_RM500Q 0x0800
#define QUECTEL_PRODUCT_RM520N 0x0801
@@ -611,11 +612,13 @@ static void option_instat_callback(struct urb *urb);
/* Sierra Wireless products */
#define SIERRA_VENDOR_ID 0x1199
#define SIERRA_PRODUCT_EM9191 0x90d3
+#define SIERRA_PRODUCT_EM9291 0x90e3
/* UNISOC (Spreadtrum) products */
#define UNISOC_VENDOR_ID 0x1782
/* TOZED LT70-C based on UNISOC SL8563 uses UNISOC's vendor ID */
#define TOZED_PRODUCT_LT70C 0x4055
+#define UNISOC_PRODUCT_UIS7720 0x4064
/* Luat Air72*U series based on UNISOC UIS8910 uses UNISOC's vendor ID */
#define LUAT_PRODUCT_AIR720U 0x4e00
@@ -1269,6 +1272,9 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RM500K, 0xff, 0x00, 0x00) },
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG650V, 0xff, 0xff, 0x30) },
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG650V, 0xff, 0, 0) },
+ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG255C, 0xff, 0xff, 0x30) },
+ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG255C, 0xff, 0, 0) },
+ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG255C, 0xff, 0xff, 0x40) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_6001) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CMU_300) },
@@ -1321,7 +1327,18 @@ static const struct usb_device_id option_ids[] = {
.driver_info = NCTRL(0) | RSVD(3) },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1033, 0xff), /* Telit LE910C1-EUX (ECM) */
.driver_info = NCTRL(0) },
+ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1034, 0xff), /* Telit LE910C4-WWX (rmnet) */
+ .driver_info = RSVD(2) },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1035, 0xff) }, /* Telit LE910C4-WWX (ECM) */
+ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1036, 0xff) }, /* Telit LE910C4-WWX */
+ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1037, 0xff), /* Telit LE910C4-WWX (rmnet) */
+ .driver_info = NCTRL(0) | NCTRL(1) | RSVD(4) },
+ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1038, 0xff), /* Telit LE910C4-WWX (rmnet) */
+ .driver_info = NCTRL(0) | RSVD(3) },
+ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x103b, 0xff), /* Telit LE910C4-WWX */
+ .driver_info = NCTRL(0) | NCTRL(1) },
+ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x103c, 0xff), /* Telit LE910C4-WWX */
+ .driver_info = NCTRL(0) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG0),
.driver_info = RSVD(0) | RSVD(1) | NCTRL(2) | RSVD(3) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG1),
@@ -1366,8 +1383,16 @@ static const struct usb_device_id option_ids[] = {
.driver_info = NCTRL(2) | RSVD(3) },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1073, 0xff), /* Telit FN990A (ECM) */
.driver_info = NCTRL(0) | RSVD(1) },
+ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1074, 0xff), /* Telit FN990A (MBIM) */
+ .driver_info = NCTRL(5) | RSVD(6) | RSVD(7) },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1075, 0xff), /* Telit FN990A (PCIe) */
.driver_info = RSVD(0) },
+ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1077, 0xff), /* Telit FN990A (rmnet + audio) */
+ .driver_info = NCTRL(0) | RSVD(1) | RSVD(2) },
+ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1078, 0xff), /* Telit FN990A (MBIM + audio) */
+ .driver_info = NCTRL(0) | RSVD(1) },
+ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1079, 0xff), /* Telit FN990A (RNDIS + audio) */
+ .driver_info = NCTRL(2) | RSVD(3) },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1080, 0xff), /* Telit FE990A (rmnet) */
.driver_info = NCTRL(0) | RSVD(1) | RSVD(2) },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1081, 0xff), /* Telit FE990A (MBIM) */
@@ -1378,16 +1403,26 @@ static const struct usb_device_id option_ids[] = {
.driver_info = NCTRL(0) | RSVD(1) },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a0, 0xff), /* Telit FN20C04 (rmnet) */
.driver_info = RSVD(0) | NCTRL(3) },
+ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a1, 0xff), /* Telit FN20C04 (RNDIS) */
+ .driver_info = NCTRL(4) },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a2, 0xff), /* Telit FN920C04 (MBIM) */
.driver_info = NCTRL(4) },
+ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a3, 0xff), /* Telit FN920C04 (ECM) */
+ .driver_info = NCTRL(4) },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a4, 0xff), /* Telit FN20C04 (rmnet) */
.driver_info = RSVD(0) | NCTRL(3) },
+ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a6, 0xff), /* Telit FN920C04 (RNDIS) */
+ .driver_info = NCTRL(4) },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a7, 0xff), /* Telit FN920C04 (MBIM) */
.driver_info = NCTRL(4) },
+ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a8, 0xff), /* Telit FN920C04 (ECM) */
+ .driver_info = NCTRL(4) },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a9, 0xff), /* Telit FN20C04 (rmnet) */
.driver_info = RSVD(0) | NCTRL(2) | RSVD(3) | RSVD(4) },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10aa, 0xff), /* Telit FN920C04 (MBIM) */
.driver_info = NCTRL(3) | RSVD(4) | RSVD(5) },
+ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10ab, 0xff), /* Telit FN920C04 (RNDIS) */
+ .driver_info = NCTRL(3) | RSVD(4) | RSVD(5) },
{ USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10b0, 0xff, 0xff, 0x30), /* Telit FE990B (rmnet) */
.driver_info = NCTRL(5) },
{ USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10b0, 0xff, 0xff, 0x40) },
@@ -1406,10 +1441,27 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10b3, 0xff, 0xff, 0x60) },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10c0, 0xff), /* Telit FE910C04 (rmnet) */
.driver_info = RSVD(0) | NCTRL(3) },
+ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10c1, 0xff), /* Telit FE910C04 (RNDIS) */
+ .driver_info = NCTRL(4) },
+ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10c2, 0xff), /* Telit FE910C04 (MBIM) */
+ .driver_info = NCTRL(4) },
+ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10c3, 0xff), /* Telit FE910C04 (ECM) */
+ .driver_info = NCTRL(4) },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10c4, 0xff), /* Telit FE910C04 (rmnet) */
.driver_info = RSVD(0) | NCTRL(3) },
+ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10c5, 0xff), /* Telit FE910C04 (RNDIS) */
+ .driver_info = NCTRL(4) },
+ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10c6, 0xff), /* Telit FE910C04 (MBIM) */
+ .driver_info = NCTRL(4) },
+ { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10c7, 0xff, 0xff, 0x30), /* Telit FE910C04 (ECM) */
+ .driver_info = NCTRL(4) },
+ { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10c7, 0xff, 0xff, 0x40) },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10c8, 0xff), /* Telit FE910C04 (rmnet) */
.driver_info = RSVD(0) | NCTRL(2) | RSVD(3) | RSVD(4) },
+ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10c9, 0xff), /* Telit FE910C04 (MBIM) */
+ .driver_info = NCTRL(3) | RSVD(4) | RSVD(5) },
+ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10cb, 0xff), /* Telit FE910C04 (RNDIS) */
+ .driver_info = NCTRL(3) | RSVD(4) | RSVD(5) },
{ USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10d0, 0xff, 0xff, 0x30), /* Telit FN990B (rmnet) */
.driver_info = NCTRL(5) },
{ USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10d0, 0xff, 0xff, 0x40) },
@@ -1461,6 +1513,11 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1231, 0xff), /* Telit LE910Cx (RNDIS) */
.driver_info = NCTRL(2) | RSVD(3) },
{ USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x1250, 0xff, 0x00, 0x00) }, /* Telit LE910Cx (rmnet) */
+ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1251, 0xff) }, /* Telit LE910Cx (RNDIS) */
+ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1252, 0xff) }, /* Telit LE910Cx (MBIM) */
+ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1253, 0xff) }, /* Telit LE910Cx (ECM) */
+ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1254, 0xff) }, /* Telit LE910Cx */
+ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1255, 0xff) }, /* Telit LE910Cx */
{ USB_DEVICE(TELIT_VENDOR_ID, 0x1260),
.driver_info = NCTRL(0) | RSVD(1) | RSVD(2) },
{ USB_DEVICE(TELIT_VENDOR_ID, 0x1261),
@@ -2093,6 +2150,12 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_INTERFACE_CLASS(0x1e0e, 0x9003, 0xff) }, /* Simcom SIM7500/SIM7600 MBIM mode */
{ USB_DEVICE_INTERFACE_CLASS(0x1e0e, 0x9011, 0xff), /* Simcom SIM7500/SIM7600 RNDIS mode */
.driver_info = RSVD(7) },
+ { USB_DEVICE(0x1e0e, 0x9071), /* Simcom SIM8230 RMNET mode */
+ .driver_info = RSVD(3) | RSVD(4) },
+ { USB_DEVICE_INTERFACE_CLASS(0x1e0e, 0x9078, 0xff), /* Simcom SIM8230 ECM mode */
+ .driver_info = RSVD(5) },
+ { USB_DEVICE_INTERFACE_CLASS(0x1e0e, 0x907b, 0xff), /* Simcom SIM8230 RNDIS mode */
+ .driver_info = RSVD(5) },
{ USB_DEVICE_INTERFACE_CLASS(0x1e0e, 0x9205, 0xff) }, /* Simcom SIM7070/SIM7080/SIM7090 AT+ECM mode */
{ USB_DEVICE_INTERFACE_CLASS(0x1e0e, 0x9206, 0xff) }, /* Simcom SIM7070/SIM7080/SIM7090 AT-only mode */
{ USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_X060S_X200),
@@ -2340,8 +2403,14 @@ static const struct usb_device_id option_ids[] = {
.driver_info = RSVD(3) },
{ USB_DEVICE_INTERFACE_CLASS(0x0489, 0xe0f0, 0xff), /* Foxconn T99W373 MBIM */
.driver_info = RSVD(3) },
+ { USB_DEVICE_INTERFACE_CLASS(0x0489, 0xe123, 0xff), /* Foxconn T99W760 MBIM */
+ .driver_info = RSVD(3) },
{ USB_DEVICE_INTERFACE_CLASS(0x0489, 0xe145, 0xff), /* Foxconn T99W651 RNDIS */
.driver_info = RSVD(5) | RSVD(6) },
+ { USB_DEVICE_INTERFACE_CLASS(0x0489, 0xe15f, 0xff), /* Foxconn T99W709 */
+ .driver_info = RSVD(5) },
+ { USB_DEVICE_INTERFACE_CLASS(0x0489, 0xe167, 0xff), /* Foxconn T99W640 MBIM */
+ .driver_info = RSVD(3) },
{ USB_DEVICE(0x1508, 0x1001), /* Fibocom NL668 (IOT version) */
.driver_info = RSVD(4) | RSVD(5) | RSVD(6) },
{ USB_DEVICE(0x1782, 0x4d10) }, /* Fibocom L610 (AT mode) */
@@ -2378,20 +2447,30 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d22, 0xff, 0xff, 0x30) }, /* MeiG Smart SRM815 and SRM825L */
{ USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d22, 0xff, 0xff, 0x40) }, /* MeiG Smart SRM825L */
{ USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d22, 0xff, 0xff, 0x60) }, /* MeiG Smart SRM825L */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d38, 0xff, 0xff, 0x30) }, /* MeiG Smart SRM825WN (Diag) */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d38, 0xff, 0xff, 0x40) }, /* MeiG Smart SRM825WN (AT) */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d38, 0xff, 0xff, 0x60) }, /* MeiG Smart SRM825WN (NMEA) */
{ USB_DEVICE_INTERFACE_CLASS(0x2df3, 0x9d03, 0xff) }, /* LongSung M5710 */
{ USB_DEVICE_INTERFACE_CLASS(0x305a, 0x1404, 0xff) }, /* GosunCn GM500 RNDIS */
{ USB_DEVICE_INTERFACE_CLASS(0x305a, 0x1405, 0xff) }, /* GosunCn GM500 MBIM */
{ USB_DEVICE_INTERFACE_CLASS(0x305a, 0x1406, 0xff) }, /* GosunCn GM500 ECM/NCM */
{ USB_DEVICE(0x33f8, 0x0104), /* Rolling RW101-GL (laptop RMNET) */
.driver_info = RSVD(4) | RSVD(5) },
+ { USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x0115, 0xff), /* Rolling RW135-GL (laptop MBIM) */
+ .driver_info = RSVD(5) },
{ USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x01a2, 0xff) }, /* Rolling RW101-GL (laptop MBIM) */
{ USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x01a3, 0xff) }, /* Rolling RW101-GL (laptop MBIM) */
{ USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x01a4, 0xff), /* Rolling RW101-GL (laptop MBIM) */
.driver_info = RSVD(4) },
- { USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x0115, 0xff), /* Rolling RW135-GL (laptop MBIM) */
- .driver_info = RSVD(5) },
+ { USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x01a8, 0xff), /* Rolling RW101R-GL (laptop MBIM) */
+ .driver_info = RSVD(4) },
+ { USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x01a9, 0xff), /* Rolling RW101R-GL (laptop MBIM) */
+ .driver_info = RSVD(4) },
+ { USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x0301, 0xff) }, /* Rolling RW101R-GL (laptop MBIM) */
+ { USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x0302, 0xff) }, /* Rolling RW101R-GL (laptop MBIM) */
{ USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x0802, 0xff), /* Rolling RW350-GL (laptop MBIM) */
.driver_info = RSVD(5) },
+ { USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x1003, 0xff) }, /* Rolling RW135R-GL (laptop MBIM) */
{ USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0100, 0xff, 0xff, 0x30) }, /* NetPrisma LCUK54-WWD for Global */
{ USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0100, 0xff, 0x00, 0x40) },
{ USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0100, 0xff, 0xff, 0x40) },
@@ -2432,7 +2511,10 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(SIERRA_VENDOR_ID, SIERRA_PRODUCT_EM9191, 0xff, 0xff, 0x30) },
{ USB_DEVICE_AND_INTERFACE_INFO(SIERRA_VENDOR_ID, SIERRA_PRODUCT_EM9191, 0xff, 0xff, 0x40) },
{ USB_DEVICE_AND_INTERFACE_INFO(SIERRA_VENDOR_ID, SIERRA_PRODUCT_EM9191, 0xff, 0, 0) },
+ { USB_DEVICE_AND_INTERFACE_INFO(SIERRA_VENDOR_ID, SIERRA_PRODUCT_EM9291, 0xff, 0xff, 0x30) },
+ { USB_DEVICE_AND_INTERFACE_INFO(SIERRA_VENDOR_ID, SIERRA_PRODUCT_EM9291, 0xff, 0xff, 0x40) },
{ USB_DEVICE_AND_INTERFACE_INFO(UNISOC_VENDOR_ID, TOZED_PRODUCT_LT70C, 0xff, 0, 0) },
+ { USB_DEVICE_AND_INTERFACE_INFO(UNISOC_VENDOR_ID, UNISOC_PRODUCT_UIS7720, 0xff, 0, 0) },
{ USB_DEVICE_AND_INTERFACE_INFO(UNISOC_VENDOR_ID, LUAT_PRODUCT_AIR720U, 0xff, 0, 0) },
{ USB_DEVICE_INTERFACE_CLASS(0x1bbb, 0x0530, 0xff), /* TCL IK512 MBIM */
.driver_info = NCTRL(1) },
@@ -2534,7 +2616,7 @@ static int option_attach(struct usb_serial *serial)
struct usb_wwan_intf_private *data;
unsigned long device_flags;
- data = kzalloc(sizeof(struct usb_wwan_intf_private), GFP_KERNEL);
+ data = kzalloc_obj(struct usb_wwan_intf_private);
if (!data)
return -ENOMEM;
diff --git a/drivers/usb/serial/oti6858.c b/drivers/usb/serial/oti6858.c
index 24068368892c..3ef5b5d8ce1a 100644
--- a/drivers/usb/serial/oti6858.c
+++ b/drivers/usb/serial/oti6858.c
@@ -106,7 +106,7 @@ struct oti6858_control_pkt {
#define PIN_DTR 0x04 /* output pin */
#define PIN_RI 0x02 /* input pin, active low */
#define PIN_DCD 0x01 /* input pin, active low */
- u8 rx_bytes_avail; /* number of bytes in rx buffer */;
+ u8 rx_bytes_avail; /* number of bytes in rx buffer */
};
#define OTI6858_CTRL_PKT_SIZE sizeof(struct oti6858_control_pkt)
@@ -328,7 +328,7 @@ static int oti6858_port_probe(struct usb_serial_port *port)
{
struct oti6858_private *priv;
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ priv = kzalloc_obj(*priv);
if (!priv)
return -ENOMEM;
diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c
index 010688dd9e49..50dc838e8115 100644
--- a/drivers/usb/serial/pl2303.c
+++ b/drivers/usb/serial/pl2303.c
@@ -458,6 +458,8 @@ static int pl2303_detect_type(struct usb_serial *serial)
case 0x605:
case 0x700: /* GR */
case 0x705:
+ case 0x905: /* GT-2AB */
+ case 0x1005: /* GC-Q20 */
return TYPE_HXN;
}
break;
@@ -501,7 +503,7 @@ static int pl2303_startup(struct usb_serial *serial)
type = ret;
dev_dbg(&serial->interface->dev, "device type: %s\n", pl2303_type_data[type].name);
- spriv = kzalloc(sizeof(*spriv), GFP_KERNEL);
+ spriv = kzalloc_obj(*spriv);
if (!spriv)
return -ENOMEM;
@@ -553,7 +555,7 @@ static int pl2303_port_probe(struct usb_serial_port *port)
{
struct pl2303_private *priv;
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ priv = kzalloc_obj(*priv);
if (!priv)
return -ENOMEM;
diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c
index 13c664317a05..1a930dc668e4 100644
--- a/drivers/usb/serial/qcserial.c
+++ b/drivers/usb/serial/qcserial.c
@@ -431,7 +431,7 @@ static int qc_attach(struct usb_serial *serial)
struct usb_wwan_intf_private *data;
bool sendsetup;
- data = kzalloc(sizeof(*data), GFP_KERNEL);
+ data = kzalloc_obj(*data);
if (!data)
return -ENOMEM;
diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c
index 72fe83a6c978..b05c655b3cc4 100644
--- a/drivers/usb/serial/quatech2.c
+++ b/drivers/usb/serial/quatech2.c
@@ -626,7 +626,7 @@ static int qt2_attach(struct usb_serial *serial)
return status;
}
- serial_priv = kzalloc(sizeof(*serial_priv), GFP_KERNEL);
+ serial_priv = kzalloc_obj(*serial_priv);
if (!serial_priv)
return -ENOMEM;
@@ -657,7 +657,7 @@ static int qt2_port_probe(struct usb_serial_port *port)
struct qt2_port_private *port_priv;
u8 bEndpointAddress;
- port_priv = kzalloc(sizeof(*port_priv), GFP_KERNEL);
+ port_priv = kzalloc_obj(*port_priv);
if (!port_priv)
return -ENOMEM;
diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c
index 741e68e46139..6e443aacae07 100644
--- a/drivers/usb/serial/sierra.c
+++ b/drivers/usb/serial/sierra.c
@@ -828,7 +828,7 @@ static int sierra_startup(struct usb_serial *serial)
{
struct sierra_intf_private *intfdata;
- intfdata = kzalloc(sizeof(*intfdata), GFP_KERNEL);
+ intfdata = kzalloc_obj(*intfdata);
if (!intfdata)
return -ENOMEM;
@@ -861,7 +861,7 @@ static int sierra_port_probe(struct usb_serial_port *port)
const struct sierra_iface_list *himemory_list;
u8 ifnum;
- portdata = kzalloc(sizeof(*portdata), GFP_KERNEL);
+ portdata = kzalloc_obj(*portdata);
if (!portdata)
return -ENOMEM;
diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c
index 11077beb7232..2fbb48f19efd 100644
--- a/drivers/usb/serial/spcp8x5.c
+++ b/drivers/usb/serial/spcp8x5.c
@@ -145,7 +145,7 @@ static int spcp8x5_port_probe(struct usb_serial_port *port)
const struct usb_device_id *id = usb_get_serial_data(port->serial);
struct spcp8x5_private *priv;
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ priv = kzalloc_obj(*priv);
if (!priv)
return -ENOMEM;
diff --git a/drivers/usb/serial/ssu100.c b/drivers/usb/serial/ssu100.c
index df21009bdf42..da6316410b77 100644
--- a/drivers/usb/serial/ssu100.c
+++ b/drivers/usb/serial/ssu100.c
@@ -326,7 +326,7 @@ static int ssu100_port_probe(struct usb_serial_port *port)
{
struct ssu100_port_private *priv;
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ priv = kzalloc_obj(*priv);
if (!priv)
return -ENOMEM;
diff --git a/drivers/usb/serial/symbolserial.c b/drivers/usb/serial/symbolserial.c
index 58962bcbf9ba..7a5aa39172a8 100644
--- a/drivers/usb/serial/symbolserial.c
+++ b/drivers/usb/serial/symbolserial.c
@@ -149,7 +149,7 @@ static int symbol_port_probe(struct usb_serial_port *port)
{
struct symbol_private *priv;
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ priv = kzalloc_obj(*priv);
if (!priv)
return -ENOMEM;
diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c
index a0c244bc77c0..b3591d6d7645 100644
--- a/drivers/usb/serial/ti_usb_3410_5052.c
+++ b/drivers/usb/serial/ti_usb_3410_5052.c
@@ -519,7 +519,7 @@ static int ti_startup(struct usb_serial *serial)
dev->descriptor.bNumConfigurations,
dev->actconfig->desc.bConfigurationValue);
- tdev = kzalloc(sizeof(struct ti_device), GFP_KERNEL);
+ tdev = kzalloc_obj(struct ti_device);
if (!tdev)
return -ENOMEM;
@@ -598,7 +598,7 @@ static int ti_port_probe(struct usb_serial_port *port)
{
struct ti_port *tport;
- tport = kzalloc(sizeof(*tport), GFP_KERNEL);
+ tport = kzalloc_obj(*tport);
if (!tport)
return -ENOMEM;
@@ -729,11 +729,6 @@ static int ti_open(struct tty_struct *tty, struct usb_serial_port *port)
/* start read urb */
urb = port->read_urb;
- if (!urb) {
- dev_err(&port->dev, "%s - no read urb\n", __func__);
- status = -EINVAL;
- goto unlink_int_urb;
- }
tport->tp_read_urb_state = TI_READ_URB_RUNNING;
urb->context = tport;
status = usb_submit_urb(urb, GFP_KERNEL);
@@ -902,7 +897,7 @@ static void ti_set_termios(struct tty_struct *tty,
u16 wbaudrate;
u16 wflags = 0;
- config = kmalloc(sizeof(*config), GFP_KERNEL);
+ config = kmalloc_obj(*config);
if (!config)
return;
@@ -1605,29 +1600,29 @@ static int ti_download_firmware(struct ti_device *tdev)
if (le16_to_cpu(dev->descriptor.idVendor) == MTS_VENDOR_ID) {
switch (le16_to_cpu(dev->descriptor.idProduct)) {
case MTS_CDMA_PRODUCT_ID:
- strcpy(buf, "mts_cdma.fw");
+ strscpy(buf, "mts_cdma.fw");
break;
case MTS_GSM_PRODUCT_ID:
- strcpy(buf, "mts_gsm.fw");
+ strscpy(buf, "mts_gsm.fw");
break;
case MTS_EDGE_PRODUCT_ID:
- strcpy(buf, "mts_edge.fw");
+ strscpy(buf, "mts_edge.fw");
break;
case MTS_MT9234MU_PRODUCT_ID:
- strcpy(buf, "mts_mt9234mu.fw");
+ strscpy(buf, "mts_mt9234mu.fw");
break;
case MTS_MT9234ZBA_PRODUCT_ID:
- strcpy(buf, "mts_mt9234zba.fw");
+ strscpy(buf, "mts_mt9234zba.fw");
break;
case MTS_MT9234ZBAOLD_PRODUCT_ID:
- strcpy(buf, "mts_mt9234zba.fw");
+ strscpy(buf, "mts_mt9234zba.fw");
break; }
}
if (buf[0] == '\0') {
if (tdev->td_is_3410)
- strcpy(buf, "ti_3410.fw");
+ strscpy(buf, "ti_3410.fw");
else
- strcpy(buf, "ti_5052.fw");
+ strscpy(buf, "ti_5052.fw");
}
status = request_firmware(&fw_p, buf, &dev->dev);
}
diff --git a/drivers/usb/serial/upd78f0730.c b/drivers/usb/serial/upd78f0730.c
index 15a17bf111f1..78c4d3c19a8e 100644
--- a/drivers/usb/serial/upd78f0730.c
+++ b/drivers/usb/serial/upd78f0730.c
@@ -161,7 +161,7 @@ static int upd78f0730_port_probe(struct usb_serial_port *port)
{
struct upd78f0730_port_private *private;
- private = kzalloc(sizeof(*private), GFP_KERNEL);
+ private = kzalloc_obj(*private);
if (!private)
return -ENOMEM;
diff --git a/drivers/usb/serial/usb-serial-simple.c b/drivers/usb/serial/usb-serial-simple.c
index 2c12449ff60c..a0afaf254d12 100644
--- a/drivers/usb/serial/usb-serial-simple.c
+++ b/drivers/usb/serial/usb-serial-simple.c
@@ -100,6 +100,11 @@ DEVICE(nokia, NOKIA_IDS);
{ USB_DEVICE(0x09d7, 0x0100) } /* NovAtel FlexPack GPS */
DEVICE_N(novatel_gps, NOVATEL_IDS, 3);
+/* OWON electronic test and measurement equipment driver */
+#define OWON_IDS() \
+ { USB_DEVICE(0x5345, 0x1234) } /* HDS200 oscilloscopes and others */
+DEVICE(owon, OWON_IDS);
+
/* Siemens USB/MPI adapter */
#define SIEMENS_IDS() \
{ USB_DEVICE(0x908, 0x0004) }
@@ -134,6 +139,7 @@ static struct usb_serial_driver * const serial_drivers[] = {
&motorola_tetra_device,
&nokia_device,
&novatel_gps_device,
+ &owon_device,
&siemens_mpi_device,
&suunto_device,
&vivopay_device,
@@ -153,6 +159,7 @@ static const struct usb_device_id id_table[] = {
MOTOROLA_TETRA_IDS(),
NOKIA_IDS(),
NOVATEL_IDS(),
+ OWON_IDS(),
SIEMENS_IDS(),
SUUNTO_IDS(),
VIVOPAY_IDS(),
diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c
index 7266558d823a..0e072fd87c3d 100644
--- a/drivers/usb/serial/usb-serial.c
+++ b/drivers/usb/serial/usb-serial.c
@@ -688,7 +688,7 @@ static struct usb_serial *create_serial(struct usb_device *dev,
{
struct usb_serial *serial;
- serial = kzalloc(sizeof(*serial), GFP_KERNEL);
+ serial = kzalloc_obj(*serial);
if (!serial)
return NULL;
serial->dev = usb_get_dev(dev);
@@ -1005,7 +1005,7 @@ static int usb_serial_probe(struct usb_interface *interface,
}
/* descriptor matches, let's find the endpoints needed */
- epds = kzalloc(sizeof(*epds), GFP_KERNEL);
+ epds = kzalloc_obj(*epds);
if (!epds) {
retval = -ENOMEM;
goto err_release_sibling;
@@ -1059,7 +1059,7 @@ static int usb_serial_probe(struct usb_interface *interface,
dev_dbg(ddev, "setting up %d port structure(s)\n", max_endpoints);
for (i = 0; i < max_endpoints; ++i) {
- port = kzalloc(sizeof(struct usb_serial_port), GFP_KERNEL);
+ port = kzalloc_obj(struct usb_serial_port);
if (!port) {
retval = -ENOMEM;
goto err_free_epds;
@@ -1176,7 +1176,6 @@ static void usb_serial_disconnect(struct usb_interface *interface)
struct usb_serial *serial = usb_get_intfdata(interface);
struct device *dev = &interface->dev;
struct usb_serial_port *port;
- struct tty_struct *tty;
/* sibling interface is cleaning up */
if (!serial)
@@ -1191,11 +1190,7 @@ static void usb_serial_disconnect(struct usb_interface *interface)
for (i = 0; i < serial->num_ports; ++i) {
port = serial->port[i];
- tty = tty_port_tty_get(&port->port);
- if (tty) {
- tty_vhangup(tty);
- tty_kref_put(tty);
- }
+ tty_port_tty_vhangup(&port->port);
usb_serial_port_poison_urbs(port);
wake_up_interruptible(&port->port.delta_msr_wait);
cancel_work_sync(&port->work);
@@ -1487,7 +1482,7 @@ int __usb_serial_register_drivers(struct usb_serial_driver *const serial_drivers
* Suspend/resume support is implemented in the usb-serial core,
* so fill in the PM-related fields in udriver.
*/
- udriver = kzalloc(sizeof(*udriver), GFP_KERNEL);
+ udriver = kzalloc_obj(*udriver);
if (!udriver)
return -ENOMEM;
diff --git a/drivers/usb/serial/usb_wwan.c b/drivers/usb/serial/usb_wwan.c
index 0017f6e969e1..e752ffa4dc62 100644
--- a/drivers/usb/serial/usb_wwan.c
+++ b/drivers/usb/serial/usb_wwan.c
@@ -449,7 +449,7 @@ int usb_wwan_port_probe(struct usb_serial_port *port)
if (!port->bulk_in_size || !port->bulk_out_size)
return -ENODEV;
- portdata = kzalloc(sizeof(*portdata), GFP_KERNEL);
+ portdata = kzalloc_obj(*portdata);
if (!portdata)
return -ENOMEM;
diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c
index 009faeb2ef55..6e2b52b2b5f8 100644
--- a/drivers/usb/serial/whiteheat.c
+++ b/drivers/usb/serial/whiteheat.c
@@ -278,8 +278,7 @@ static int whiteheat_attach(struct usb_serial *serial)
serial->type->description,
hw_info->sw_major_rev, hw_info->sw_minor_rev);
- command_info = kmalloc(sizeof(struct whiteheat_command_private),
- GFP_KERNEL);
+ command_info = kmalloc_obj(struct whiteheat_command_private);
if (!command_info)
goto no_command_private;
@@ -330,7 +329,7 @@ static int whiteheat_port_probe(struct usb_serial_port *port)
{
struct whiteheat_private *info;
- info = kzalloc(sizeof(*info), GFP_KERNEL);
+ info = kzalloc_obj(*info);
if (!info)
return -ENOMEM;
diff --git a/drivers/usb/serial/xr_serial.c b/drivers/usb/serial/xr_serial.c
index 4186e791b384..9fc4082de770 100644
--- a/drivers/usb/serial/xr_serial.c
+++ b/drivers/usb/serial/xr_serial.c
@@ -756,7 +756,7 @@ static void xr_cdc_set_line_coding(struct tty_struct *tty,
struct usb_cdc_line_coding *lc;
int ret;
- lc = kzalloc(sizeof(*lc), GFP_KERNEL);
+ lc = kzalloc_obj(*lc);
if (!lc)
return;
@@ -1020,7 +1020,7 @@ static int xr_port_probe(struct usb_serial_port *port)
type_id = (int)(unsigned long)usb_get_serial_data(port->serial);
type = &xr_types[type_id];
- data = kzalloc(sizeof(*data), GFP_KERNEL);
+ data = kzalloc_obj(*data);
if (!data)
return -ENOMEM;
diff --git a/drivers/usb/storage/alauda.c b/drivers/usb/storage/alauda.c
index 6263c4e61678..691fe47009cf 100644
--- a/drivers/usb/storage/alauda.c
+++ b/drivers/usb/storage/alauda.c
@@ -174,7 +174,7 @@ struct alauda_card_info {
unsigned char zoneshift; /* 1<<zs blocks per zone */
};
-static struct alauda_card_info alauda_card_ids[] = {
+static const struct alauda_card_info alauda_card_ids[] = {
/* NAND flash */
{ 0x6e, 20, 8, 4, 8}, /* 1 MB */
{ 0xe8, 20, 8, 4, 8}, /* 1 MB */
@@ -200,7 +200,7 @@ static struct alauda_card_info alauda_card_ids[] = {
{ 0,}
};
-static struct alauda_card_info *alauda_card_find_id(unsigned char id)
+static const struct alauda_card_info *alauda_card_find_id(unsigned char id)
{
int i;
@@ -383,7 +383,7 @@ static int alauda_init_media(struct us_data *us)
{
unsigned char *data = us->iobuf;
int ready = 0;
- struct alauda_card_info *media_info;
+ const struct alauda_card_info *media_info;
unsigned int num_zones;
while (ready == 0) {
@@ -1113,7 +1113,7 @@ static int init_alauda(struct us_data *us)
struct usb_host_interface *altsetting = us->pusb_intf->cur_altsetting;
nand_init_ecc();
- us->extra = kzalloc(sizeof(struct alauda_info), GFP_NOIO);
+ us->extra = kzalloc_obj(struct alauda_info, GFP_NOIO);
if (!us->extra)
return -ENOMEM;
@@ -1132,7 +1132,7 @@ static int alauda_transport(struct scsi_cmnd *srb, struct us_data *us)
int rc;
struct alauda_info *info = (struct alauda_info *) us->extra;
unsigned char *ptr = us->iobuf;
- static unsigned char inquiry_response[36] = {
+ static const unsigned char inquiry_response[36] = {
0x00, 0x80, 0x00, 0x01, 0x1F, 0x00, 0x00, 0x00
};
diff --git a/drivers/usb/storage/datafab.c b/drivers/usb/storage/datafab.c
index bbfa2398b170..0ded8ef72efa 100644
--- a/drivers/usb/storage/datafab.c
+++ b/drivers/usb/storage/datafab.c
@@ -319,7 +319,7 @@ static int datafab_determine_lun(struct us_data *us,
//
// There might be a better way of doing this?
- static unsigned char scommand[8] = { 0, 1, 0, 0, 0, 0xa0, 0xec, 1 };
+ static const unsigned char scommand[8] = { 0, 1, 0, 0, 0, 0xa0, 0xec, 1 };
unsigned char *command = us->iobuf;
unsigned char *buf;
int count = 0, rc;
@@ -384,7 +384,7 @@ static int datafab_id_device(struct us_data *us,
// to the ATA spec, 'Sector Count' isn't used but the Windows driver
// sets this bit so we do too...
//
- static unsigned char scommand[8] = { 0, 1, 0, 0, 0, 0xa0, 0xec, 1 };
+ static const unsigned char scommand[8] = { 0, 1, 0, 0, 0, 0xa0, 0xec, 1 };
unsigned char *command = us->iobuf;
unsigned char *reply;
int rc;
@@ -437,16 +437,16 @@ static int datafab_handle_mode_sense(struct us_data *us,
struct scsi_cmnd * srb,
int sense_6)
{
- static unsigned char rw_err_page[12] = {
+ static const unsigned char rw_err_page[12] = {
0x1, 0xA, 0x21, 1, 0, 0, 0, 0, 1, 0, 0, 0
};
- static unsigned char cache_page[12] = {
+ static const unsigned char cache_page[12] = {
0x8, 0xA, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
- static unsigned char rbac_page[12] = {
+ static const unsigned char rbac_page[12] = {
0x1B, 0xA, 0, 0x81, 0, 0, 0, 0, 0, 0, 0, 0
};
- static unsigned char timer_page[8] = {
+ static const unsigned char timer_page[8] = {
0x1C, 0x6, 0, 0, 0, 0
};
unsigned char pc, page_code;
@@ -550,12 +550,12 @@ static int datafab_transport(struct scsi_cmnd *srb, struct us_data *us)
int rc;
unsigned long block, blocks;
unsigned char *ptr = us->iobuf;
- static unsigned char inquiry_reply[8] = {
+ static const unsigned char inquiry_reply[8] = {
0x00, 0x80, 0x00, 0x01, 0x1F, 0x00, 0x00, 0x00
};
if (!us->extra) {
- us->extra = kzalloc(sizeof(struct datafab_info), GFP_NOIO);
+ us->extra = kzalloc_obj(struct datafab_info, GFP_NOIO);
if (!us->extra)
return USB_STOR_TRANSPORT_ERROR;
diff --git a/drivers/usb/storage/debug.c b/drivers/usb/storage/debug.c
index 576be66ad962..dda610f689b7 100644
--- a/drivers/usb/storage/debug.c
+++ b/drivers/usb/storage/debug.c
@@ -58,8 +58,8 @@ void usb_stor_show_command(const struct us_data *us, struct scsi_cmnd *srb)
case INQUIRY: what = "INQUIRY"; break;
case RECOVER_BUFFERED_DATA: what = "RECOVER_BUFFERED_DATA"; break;
case MODE_SELECT: what = "MODE_SELECT"; break;
- case RESERVE: what = "RESERVE"; break;
- case RELEASE: what = "RELEASE"; break;
+ case RESERVE_6: what = "RESERVE"; break;
+ case RELEASE_6: what = "RELEASE"; break;
case COPY: what = "COPY"; break;
case ERASE: what = "ERASE"; break;
case MODE_SENSE: what = "MODE_SENSE"; break;
diff --git a/drivers/usb/storage/ene_ub6250.c b/drivers/usb/storage/ene_ub6250.c
index ce91fb105975..8770de01a384 100644
--- a/drivers/usb/storage/ene_ub6250.c
+++ b/drivers/usb/storage/ene_ub6250.c
@@ -1119,9 +1119,8 @@ static int ms_lib_alloc_writebuf(struct us_data *us)
info->MS_Lib.blkpag = kmalloc_array(info->MS_Lib.PagesPerBlock,
info->MS_Lib.BytesPerSector,
GFP_KERNEL);
- info->MS_Lib.blkext = kmalloc_array(info->MS_Lib.PagesPerBlock,
- sizeof(struct ms_lib_type_extdat),
- GFP_KERNEL);
+ info->MS_Lib.blkext = kmalloc_objs(struct ms_lib_type_extdat,
+ info->MS_Lib.PagesPerBlock);
if ((info->MS_Lib.blkpag == NULL) || (info->MS_Lib.blkext == NULL)) {
ms_lib_free_writebuf(us);
@@ -2336,7 +2335,7 @@ static int ene_ub6250_probe(struct usb_interface *intf,
return result;
/* FIXME: where should the code alloc extra buf ? */
- us->extra = kzalloc(sizeof(struct ene_ub6250_info), GFP_KERNEL);
+ us->extra = kzalloc_obj(struct ene_ub6250_info);
if (!us->extra)
return -ENOMEM;
us->extra_destructor = ene_ub6250_info_destructor;
diff --git a/drivers/usb/storage/initializers.c b/drivers/usb/storage/initializers.c
index f8f9ce8dc710..b243bd5521a6 100644
--- a/drivers/usb/storage/initializers.c
+++ b/drivers/usb/storage/initializers.c
@@ -54,7 +54,7 @@ int usb_stor_ucr61s2b_init(struct us_data *us)
struct bulk_cs_wrap *bcs = (struct bulk_cs_wrap*) us->iobuf;
int res;
unsigned int partial;
- static char init_string[] = "\xec\x0a\x06\x00$PCCHIPS";
+ static const char init_string[] = "\xec\x0a\x06\x00$PCCHIPS";
usb_stor_dbg(us, "Sending UCR-61S2B initialization packet...\n");
diff --git a/drivers/usb/storage/isd200.c b/drivers/usb/storage/isd200.c
index a1669c35bad5..a4fd5dda3396 100644
--- a/drivers/usb/storage/isd200.c
+++ b/drivers/usb/storage/isd200.c
@@ -1463,7 +1463,7 @@ static int isd200_init_info(struct us_data *us)
{
struct isd200_info *info;
- info = kzalloc(sizeof(struct isd200_info), GFP_KERNEL);
+ info = kzalloc_obj(struct isd200_info);
if (!info)
return -ENOMEM;
diff --git a/drivers/usb/storage/jumpshot.c b/drivers/usb/storage/jumpshot.c
index 39ca84d68591..a9c48091491e 100644
--- a/drivers/usb/storage/jumpshot.c
+++ b/drivers/usb/storage/jumpshot.c
@@ -367,16 +367,16 @@ static int jumpshot_handle_mode_sense(struct us_data *us,
struct scsi_cmnd * srb,
int sense_6)
{
- static unsigned char rw_err_page[12] = {
+ static const unsigned char rw_err_page[12] = {
0x1, 0xA, 0x21, 1, 0, 0, 0, 0, 1, 0, 0, 0
};
- static unsigned char cache_page[12] = {
+ static const unsigned char cache_page[12] = {
0x8, 0xA, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
- static unsigned char rbac_page[12] = {
+ static const unsigned char rbac_page[12] = {
0x1B, 0xA, 0, 0x81, 0, 0, 0, 0, 0, 0, 0, 0
};
- static unsigned char timer_page[8] = {
+ static const unsigned char timer_page[8] = {
0x1C, 0x6, 0, 0, 0, 0
};
unsigned char pc, page_code;
@@ -477,12 +477,12 @@ static int jumpshot_transport(struct scsi_cmnd *srb, struct us_data *us)
int rc;
unsigned long block, blocks;
unsigned char *ptr = us->iobuf;
- static unsigned char inquiry_response[8] = {
+ static const unsigned char inquiry_response[8] = {
0x00, 0x80, 0x00, 0x01, 0x1F, 0x00, 0x00, 0x00
};
if (!us->extra) {
- us->extra = kzalloc(sizeof(struct jumpshot_info), GFP_NOIO);
+ us->extra = kzalloc_obj(struct jumpshot_info, GFP_NOIO);
if (!us->extra)
return USB_STOR_TRANSPORT_ERROR;
diff --git a/drivers/usb/storage/karma.c b/drivers/usb/storage/karma.c
index 341d6839548a..5f199c21bb0c 100644
--- a/drivers/usb/storage/karma.c
+++ b/drivers/usb/storage/karma.c
@@ -174,7 +174,7 @@ static void rio_karma_destructor(void *extra)
static int rio_karma_init(struct us_data *us)
{
- struct karma_data *data = kzalloc(sizeof(struct karma_data), GFP_NOIO);
+ struct karma_data *data = kzalloc_obj(struct karma_data, GFP_NOIO);
if (!data)
return -ENOMEM;
diff --git a/drivers/usb/storage/onetouch.c b/drivers/usb/storage/onetouch.c
index 5a8a1ffda0ec..49d281f7bef6 100644
--- a/drivers/usb/storage/onetouch.c
+++ b/drivers/usb/storage/onetouch.c
@@ -183,7 +183,7 @@ static int onetouch_connect_input(struct us_data *ss)
maxp = usb_maxpacket(udev, pipe);
maxp = min(maxp, ONETOUCH_PKT_LEN);
- onetouch = kzalloc(sizeof(struct usb_onetouch), GFP_KERNEL);
+ onetouch = kzalloc_obj(struct usb_onetouch);
input_dev = input_allocate_device();
if (!onetouch || !input_dev)
goto fail1;
diff --git a/drivers/usb/storage/protocol.c b/drivers/usb/storage/protocol.c
index 9033e505db7f..0cff54ad90fa 100644
--- a/drivers/usb/storage/protocol.c
+++ b/drivers/usb/storage/protocol.c
@@ -139,8 +139,7 @@ unsigned int usb_stor_access_xfer_buf(unsigned char *buffer,
return cnt;
while (sg_miter_next(&miter) && cnt < buflen) {
- unsigned int len = min_t(unsigned int, miter.length,
- buflen - cnt);
+ unsigned int len = min(miter.length, buflen - cnt);
if (dir == FROM_XFER_BUF)
memcpy(buffer + cnt, miter.addr, len);
diff --git a/drivers/usb/storage/realtek_cr.c b/drivers/usb/storage/realtek_cr.c
index 2a82ed7b68ea..af038b897c6b 100644
--- a/drivers/usb/storage/realtek_cr.c
+++ b/drivers/usb/storage/realtek_cr.c
@@ -191,7 +191,7 @@ MODULE_DEVICE_TABLE(usb, realtek_cr_ids);
.initFunction = init_function, \
}
-static struct us_unusual_dev realtek_cr_unusual_dev_list[] = {
+static const struct us_unusual_dev realtek_cr_unusual_dev_list[] = {
# include "unusual_realtek.h"
{} /* Terminating entry */
};
@@ -252,7 +252,7 @@ static int rts51x_bulk_transport(struct us_data *us, u8 lun,
return USB_STOR_TRANSPORT_ERROR;
}
- residue = bcs->Residue;
+ residue = le32_to_cpu(bcs->Residue);
if (bcs->Tag != us->tag)
return USB_STOR_TRANSPORT_ERROR;
@@ -260,8 +260,8 @@ static int rts51x_bulk_transport(struct us_data *us, u8 lun,
* try to compute the actual residue, based on how much data
* was really transferred and what the device tells us
*/
- if (residue)
- residue = residue < buf_len ? residue : buf_len;
+ if (residue > buf_len)
+ residue = buf_len;
if (act_len)
*act_len = buf_len - residue;
@@ -748,13 +748,14 @@ static void rts51x_modi_suspend_timer(struct rts51x_chip *chip)
usb_stor_dbg(us, "state:%d\n", rts51x_get_stat(chip));
- chip->timer_expires = jiffies + msecs_to_jiffies(1000*ss_delay);
+ chip->timer_expires = jiffies + secs_to_jiffies(ss_delay);
mod_timer(&chip->rts51x_suspend_timer, chip->timer_expires);
}
static void rts51x_suspend_timer_fn(struct timer_list *t)
{
- struct rts51x_chip *chip = from_timer(chip, t, rts51x_suspend_timer);
+ struct rts51x_chip *chip = timer_container_of(chip, t,
+ rts51x_suspend_timer);
struct us_data *us = chip->us;
switch (rts51x_get_stat(chip)) {
@@ -797,10 +798,10 @@ static void rts51x_invoke_transport(struct scsi_cmnd *srb, struct us_data *us)
{
struct rts51x_chip *chip = (struct rts51x_chip *)(us->extra);
static int card_first_show = 1;
- static u8 media_not_present[] = { 0x70, 0, 0x02, 0, 0, 0, 0,
+ static const u8 media_not_present[] = { 0x70, 0, 0x02, 0, 0, 0, 0,
10, 0, 0, 0, 0, 0x3A, 0, 0, 0, 0, 0
};
- static u8 invalid_cmd_field[] = { 0x70, 0, 0x05, 0, 0, 0, 0,
+ static const u8 invalid_cmd_field[] = { 0x70, 0, 0x05, 0, 0, 0, 0,
10, 0, 0, 0, 0, 0x24, 0, 0, 0, 0, 0
};
int ret;
@@ -934,7 +935,7 @@ static void realtek_cr_destructor(void *extra)
#ifdef CONFIG_REALTEK_AUTOPM
if (ss_en) {
- del_timer(&chip->rts51x_suspend_timer);
+ timer_delete(&chip->rts51x_suspend_timer);
chip->timer_expires = 0;
}
#endif
@@ -975,7 +976,7 @@ static int init_realtek_cr(struct us_data *us)
struct rts51x_chip *chip;
int size, i, retval;
- chip = kzalloc(sizeof(struct rts51x_chip), GFP_KERNEL);
+ chip = kzalloc_obj(struct rts51x_chip);
if (!chip)
return -ENOMEM;
diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c
index d2f476e48d0c..97de28e85562 100644
--- a/drivers/usb/storage/scsiglue.c
+++ b/drivers/usb/storage/scsiglue.c
@@ -357,7 +357,7 @@ static int target_alloc(struct scsi_target *starget)
/* queue a command */
/* This is always called with scsi_lock(host) held */
-static int queuecommand_lck(struct scsi_cmnd *srb)
+static enum scsi_qc_status queuecommand_lck(struct scsi_cmnd *srb)
{
void (*done)(struct scsi_cmnd *) = scsi_done;
struct us_data *us = host_to_us(srb->device->host);
diff --git a/drivers/usb/storage/sddr09.c b/drivers/usb/storage/sddr09.c
index d21ce3466e25..3d45e1b54c66 100644
--- a/drivers/usb/storage/sddr09.c
+++ b/drivers/usb/storage/sddr09.c
@@ -144,7 +144,7 @@ static inline char *nand_flash_manufacturer(int manuf_id) {
* 256 MB NAND flash has a 5-byte ID with 2nd byte 0xaa, 0xba, 0xca or 0xda.
*/
-static struct nand_flash_dev nand_flash_ids[] = {
+static const struct nand_flash_dev nand_flash_ids[] = {
/* NAND flash */
{ 0x6e, 20, 8, 4, 8, 2}, /* 1 MB */
{ 0xe8, 20, 8, 4, 8, 2}, /* 1 MB */
@@ -169,7 +169,7 @@ static struct nand_flash_dev nand_flash_ids[] = {
{ 0,}
};
-static struct nand_flash_dev *
+static const struct nand_flash_dev *
nand_find_id(unsigned char id) {
int i;
@@ -1133,9 +1133,9 @@ sddr09_reset(struct us_data *us) {
}
#endif
-static struct nand_flash_dev *
+static const struct nand_flash_dev *
sddr09_get_cardinfo(struct us_data *us, unsigned char flags) {
- struct nand_flash_dev *cardinfo;
+ const struct nand_flash_dev *cardinfo;
unsigned char deviceID[4];
char blurbtxt[256];
int result;
@@ -1232,8 +1232,8 @@ sddr09_read_map(struct us_data *us) {
kfree(info->lba_to_pba);
kfree(info->pba_to_lba);
- info->lba_to_pba = kmalloc_array(numblocks, sizeof(int), GFP_NOIO);
- info->pba_to_lba = kmalloc_array(numblocks, sizeof(int), GFP_NOIO);
+ info->lba_to_pba = kmalloc_objs(int, numblocks, GFP_NOIO);
+ info->pba_to_lba = kmalloc_objs(int, numblocks, GFP_NOIO);
if (info->lba_to_pba == NULL || info->pba_to_lba == NULL) {
printk(KERN_WARNING "sddr09_read_map: out of memory\n");
@@ -1428,7 +1428,7 @@ sddr09_common_init(struct us_data *us) {
return -EINVAL;
}
- us->extra = kzalloc(sizeof(struct sddr09_card_info), GFP_NOIO);
+ us->extra = kzalloc_obj(struct sddr09_card_info, GFP_NOIO);
if (!us->extra)
return -ENOMEM;
us->extra_destructor = sddr09_card_info_destructor;
@@ -1545,12 +1545,12 @@ static int sddr09_transport(struct scsi_cmnd *srb, struct us_data *us)
struct sddr09_card_info *info;
- static unsigned char inquiry_response[8] = {
+ static const unsigned char inquiry_response[8] = {
0x00, 0x80, 0x00, 0x02, 0x1F, 0x00, 0x00, 0x00
};
/* note: no block descriptor support */
- static unsigned char mode_page_01[19] = {
+ static const unsigned char mode_page_01[19] = {
0x00, 0x0F, 0x00, 0x0, 0x0, 0x0, 0x00,
0x01, 0x0A,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
@@ -1584,7 +1584,7 @@ static int sddr09_transport(struct scsi_cmnd *srb, struct us_data *us)
}
if (srb->cmnd[0] == READ_CAPACITY) {
- struct nand_flash_dev *cardinfo;
+ const struct nand_flash_dev *cardinfo;
sddr09_get_wp(us, info); /* read WP bit */
diff --git a/drivers/usb/storage/sddr55.c b/drivers/usb/storage/sddr55.c
index d5cdff30f6f3..e05e824cd4d1 100644
--- a/drivers/usb/storage/sddr55.c
+++ b/drivers/usb/storage/sddr55.c
@@ -469,6 +469,12 @@ static int sddr55_write_data(struct us_data *us,
new_pba = (status[3] + (status[4] << 8) + (status[5] << 16))
>> info->blockshift;
+ /* check if device-reported new_pba is out of range */
+ if (new_pba >= (info->capacity >> (info->blockshift + info->pageshift))) {
+ result = USB_STOR_TRANSPORT_FAILED;
+ goto leave;
+ }
+
/* check status for error */
if (status[0] == 0xff && status[1] == 0x4) {
info->pba_to_lba[new_pba] = BAD_BLOCK;
@@ -685,8 +691,8 @@ static int sddr55_read_map(struct us_data *us) {
kfree(info->lba_to_pba);
kfree(info->pba_to_lba);
- info->lba_to_pba = kmalloc_array(numblocks, sizeof(int), GFP_NOIO);
- info->pba_to_lba = kmalloc_array(numblocks, sizeof(int), GFP_NOIO);
+ info->lba_to_pba = kmalloc_objs(int, numblocks, GFP_NOIO);
+ info->pba_to_lba = kmalloc_objs(int, numblocks, GFP_NOIO);
if (info->lba_to_pba == NULL || info->pba_to_lba == NULL) {
kfree(info->lba_to_pba);
@@ -775,11 +781,11 @@ static void sddr55_card_info_destructor(void *extra) {
static int sddr55_transport(struct scsi_cmnd *srb, struct us_data *us)
{
int result;
- static unsigned char inquiry_response[8] = {
+ static const unsigned char inquiry_response[8] = {
0x00, 0x80, 0x00, 0x02, 0x1F, 0x00, 0x00, 0x00
};
// write-protected for now, no block descriptor support
- static unsigned char mode_page_01[20] = {
+ static const unsigned char mode_page_01[20] = {
0x0, 0x12, 0x00, 0x80, 0x0, 0x0, 0x0, 0x0,
0x01, 0x0A,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
@@ -793,8 +799,7 @@ static int sddr55_transport(struct scsi_cmnd *srb, struct us_data *us)
struct sddr55_card_info *info;
if (!us->extra) {
- us->extra = kzalloc(
- sizeof(struct sddr55_card_info), GFP_NOIO);
+ us->extra = kzalloc_obj(struct sddr55_card_info, GFP_NOIO);
if (!us->extra)
return USB_STOR_TRANSPORT_ERROR;
us->extra_destructor = sddr55_card_info_destructor;
diff --git a/drivers/usb/storage/shuttle_usbat.c b/drivers/usb/storage/shuttle_usbat.c
index c33cbf177e6f..7e5424268c73 100644
--- a/drivers/usb/storage/shuttle_usbat.c
+++ b/drivers/usb/storage/shuttle_usbat.c
@@ -1454,7 +1454,7 @@ static int init_usbat(struct us_data *us, int devicetype)
unsigned char subcountL = USBAT_ATA_LBA_ME;
unsigned char *status = us->iobuf;
- us->extra = kzalloc(sizeof(struct usbat_info), GFP_NOIO);
+ us->extra = kzalloc_obj(struct usbat_info, GFP_NOIO);
if (!us->extra)
return -ENOMEM;
@@ -1683,7 +1683,7 @@ static int usbat_flash_transport(struct scsi_cmnd * srb, struct us_data *us)
struct usbat_info *info = (struct usbat_info *) (us->extra);
unsigned long block, blocks;
unsigned char *ptr = us->iobuf;
- static unsigned char inquiry_response[36] = {
+ static const unsigned char inquiry_response[36] = {
0x00, 0x80, 0x00, 0x01, 0x1F, 0x00, 0x00, 0x00
};
diff --git a/drivers/usb/storage/sierra_ms.c b/drivers/usb/storage/sierra_ms.c
index 177fa6cd143a..c5e3eeeb2144 100644
--- a/drivers/usb/storage/sierra_ms.c
+++ b/drivers/usb/storage/sierra_ms.c
@@ -100,7 +100,7 @@ static ssize_t truinst_show(struct device *dev, struct device_attribute *attr,
if (swi_tru_install == TRU_FORCE_MS) {
result = sysfs_emit(buf, "Forced Mass Storage\n");
} else {
- swocInfo = kmalloc(sizeof(struct swoc_info), GFP_KERNEL);
+ swocInfo = kmalloc_obj(struct swoc_info);
if (!swocInfo) {
sysfs_emit(buf, "Error\n");
return -ENOMEM;
@@ -149,8 +149,7 @@ int sierra_ms_init(struct us_data *us)
else {
usb_stor_dbg(us, "SWIMS: Normal SWoC Logic\n");
- swocInfo = kmalloc(sizeof(struct swoc_info),
- GFP_KERNEL);
+ swocInfo = kmalloc_obj(struct swoc_info);
if (!swocInfo)
return -ENOMEM;
diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c
index e6bc8ecaecbb..9a4bf86e7b6a 100644
--- a/drivers/usb/storage/transport.c
+++ b/drivers/usb/storage/transport.c
@@ -528,7 +528,7 @@ static void last_sector_hacks(struct us_data *us, struct scsi_cmnd *srb)
u32 sector;
/* To Report "Medium Error: Record Not Found */
- static unsigned char record_not_found[18] = {
+ static const unsigned char record_not_found[18] = {
[0] = 0x70, /* current error */
[2] = MEDIUM_ERROR, /* = 0x03 */
[7] = 0x0a, /* additional length */
@@ -1200,7 +1200,23 @@ int usb_stor_Bulk_transport(struct scsi_cmnd *srb, struct us_data *us)
US_BULK_CS_WRAP_LEN &&
bcs->Signature ==
cpu_to_le32(US_BULK_CS_SIGN)) {
+ unsigned char buf[US_BULK_CS_WRAP_LEN];
+
usb_stor_dbg(us, "Device skipped data phase\n");
+
+ /*
+ * Devices skipping data phase might leave CSW data in srb's
+ * transfer buffer. Zero it to prevent USB protocol leakage.
+ */
+ sg = NULL;
+ offset = 0;
+ memset(buf, 0, sizeof(buf));
+ if (usb_stor_access_xfer_buf(buf,
+ US_BULK_CS_WRAP_LEN, srb, &sg,
+ &offset, TO_XFER_BUF) !=
+ US_BULK_CS_WRAP_LEN)
+ usb_stor_dbg(us, "Failed to clear CSW data\n");
+
scsi_set_resid(srb, transfer_length);
goto skipped_data_phase;
}
diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c
index 4ed0dc19afe0..265162981269 100644
--- a/drivers/usb/storage/uas.c
+++ b/drivers/usb/storage/uas.c
@@ -309,18 +309,18 @@ static void uas_stat_cmplt(struct urb *urb)
int status = urb->status;
bool success;
- spin_lock_irqsave(&devinfo->lock, flags);
-
- if (devinfo->resetting)
- goto out;
-
if (status) {
if (status != -ENOENT && status != -ECONNRESET && status != -ESHUTDOWN)
dev_err(&urb->dev->dev, "stat urb: status %d\n", status);
- goto out;
+ goto bail;
}
idx = be16_to_cpup(&iu->tag) - 1;
+
+ spin_lock_irqsave(&devinfo->lock, flags);
+
+ if (devinfo->resetting)
+ goto out;
if (idx >= MAX_CMNDS || !devinfo->cmnd[idx]) {
dev_err(&urb->dev->dev,
"stat urb: no pending cmd for uas-tag %d\n", idx + 1);
@@ -375,9 +375,8 @@ static void uas_stat_cmplt(struct urb *urb)
default:
uas_log_cmd_state(cmnd, "bogus IU", iu->iu_id);
}
-out:
- usb_free_urb(urb);
spin_unlock_irqrestore(&devinfo->lock, flags);
+ usb_free_urb(urb);
/* Unlinking of data urbs must be done without holding the lock */
if (data_in_urb) {
@@ -388,6 +387,12 @@ out:
usb_unlink_urb(data_out_urb);
usb_put_urb(data_out_urb);
}
+ return;
+
+out:
+ spin_unlock_irqrestore(&devinfo->lock, flags);
+bail:
+ usb_free_urb(urb);
}
static void uas_data_cmplt(struct urb *urb)
@@ -429,8 +434,8 @@ static void uas_data_cmplt(struct urb *urb)
}
uas_try_complete(cmnd, __func__);
out:
- usb_free_urb(urb);
spin_unlock_irqrestore(&devinfo->lock, flags);
+ usb_free_urb(urb);
}
static void uas_cmd_cmplt(struct urb *urb)
@@ -475,7 +480,7 @@ static struct urb *uas_alloc_sense_urb(struct uas_dev_info *devinfo, gfp_t gfp,
if (!urb)
goto out;
- iu = kzalloc(sizeof(*iu), gfp);
+ iu = kzalloc_obj(*iu, gfp);
if (!iu)
goto free;
@@ -631,7 +636,7 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd,
return 0;
}
-static int uas_queuecommand_lck(struct scsi_cmnd *cmnd)
+static enum scsi_qc_status uas_queuecommand_lck(struct scsi_cmnd *cmnd)
{
struct scsi_device *sdev = cmnd->device;
struct uas_dev_info *devinfo = sdev->hostdata;
@@ -698,6 +703,10 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd)
* of queueing, no matter how fatal the error
*/
if (err == -ENODEV) {
+ if (cmdinfo->state & (COMMAND_INFLIGHT | DATA_IN_URB_INFLIGHT |
+ DATA_OUT_URB_INFLIGHT))
+ goto out;
+
set_host_byte(cmnd, DID_NO_CONNECT);
scsi_done(cmnd);
goto zombie;
@@ -711,6 +720,7 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd)
uas_add_work(cmnd);
}
+out:
devinfo->cmnd[idx] = cmnd;
zombie:
spin_unlock_irqrestore(&devinfo->lock, flags);
@@ -762,7 +772,7 @@ static int uas_eh_abort_handler(struct scsi_cmnd *cmnd)
return FAILED;
}
-static int uas_eh_device_reset_handler(struct scsi_cmnd *cmnd)
+static int uas_eh_host_reset_handler(struct scsi_cmnd *cmnd)
{
struct scsi_device *sdev = cmnd->device;
struct uas_dev_info *devinfo = sdev->hostdata;
@@ -908,7 +918,7 @@ static const struct scsi_host_template uas_host_template = {
.sdev_init = uas_sdev_init,
.sdev_configure = uas_sdev_configure,
.eh_abort_handler = uas_eh_abort_handler,
- .eh_device_reset_handler = uas_eh_device_reset_handler,
+ .eh_host_reset_handler = uas_eh_host_reset_handler,
.this_id = -1,
.skip_settle_delay = 1,
/*
@@ -1265,7 +1275,7 @@ static int __init uas_init(void)
{
int rv;
- workqueue = alloc_workqueue("uas", WQ_MEM_RECLAIM, 0);
+ workqueue = alloc_workqueue("uas", WQ_MEM_RECLAIM | WQ_PERCPU, 0);
if (!workqueue)
return -ENOMEM;
diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h
index 54f0b1c83317..255968f9ca42 100644
--- a/drivers/usb/storage/unusual_devs.h
+++ b/drivers/usb/storage/unusual_devs.h
@@ -934,6 +934,13 @@ UNUSUAL_DEV( 0x05e3, 0x0723, 0x9451, 0x9451,
USB_SC_DEVICE, USB_PR_DEVICE, NULL,
US_FL_SANE_SENSE ),
+/* Added by Maël GUERIN <mael.guerin@murena.io> */
+UNUSUAL_DEV( 0x0603, 0x8611, 0x0000, 0xffff,
+ "Novatek",
+ "NTK96550-based camera",
+ USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+ US_FL_BULK_IGNORE_TAG ),
+
/*
* Reported by Hanno Boeck <hanno@gmx.de>
* Taken from the Lycoris Kernel
@@ -1494,6 +1501,28 @@ UNUSUAL_DEV( 0x0bc2, 0x3332, 0x0000, 0x9999,
USB_SC_DEVICE, USB_PR_DEVICE, NULL,
US_FL_NO_WP_DETECT ),
+/*
+ * Reported by Zenm Chen <zenmchen@gmail.com>
+ * Ignore driver CD mode, otherwise usb_modeswitch may fail to switch
+ * the device into Wi-Fi mode.
+ */
+UNUSUAL_DEV( 0x0bda, 0x1a2b, 0x0000, 0xffff,
+ "Realtek",
+ "DISK",
+ USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+ US_FL_IGNORE_DEVICE ),
+
+/*
+ * Reported by Zenm Chen <zenmchen@gmail.com>
+ * Ignore driver CD mode, otherwise usb_modeswitch may fail to switch
+ * the device into Wi-Fi mode.
+ */
+UNUSUAL_DEV( 0x0bda, 0xa192, 0x0000, 0xffff,
+ "Realtek",
+ "DISK",
+ USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+ US_FL_IGNORE_DEVICE ),
+
UNUSUAL_DEV( 0x0d49, 0x7310, 0x0000, 0x9999,
"Maxtor",
"USB to SATA",
@@ -2321,10 +2350,11 @@ UNUSUAL_DEV( 0x2027, 0xa001, 0x0000, 0x9999,
US_FL_SCM_MULT_TARG ),
/*
- * Reported by DocMAX <mail@vacharakis.de>
- * and Thomas Weißschuh <linux@weissschuh.net>
+ * Reported by DocMAX <mail@vacharakis.de>,
+ * Thomas Weißschuh <linux@weissschuh.net>
+ * and Daniel Brát <danek.brat@gmail.com>
*/
-UNUSUAL_DEV( 0x2109, 0x0715, 0x9999, 0x9999,
+UNUSUAL_DEV( 0x2109, 0x0715, 0x0000, 0x9999,
"VIA Labs, Inc.",
"VL817 SATA Bridge",
USB_SC_DEVICE, USB_PR_DEVICE, NULL,
diff --git a/drivers/usb/storage/unusual_uas.h b/drivers/usb/storage/unusual_uas.h
index 1f8c9b16a0fb..939a98c2d3f7 100644
--- a/drivers/usb/storage/unusual_uas.h
+++ b/drivers/usb/storage/unusual_uas.h
@@ -52,6 +52,13 @@ UNUSUAL_DEV(0x059f, 0x1061, 0x0000, 0x9999,
USB_SC_DEVICE, USB_PR_DEVICE, NULL,
US_FL_NO_REPORT_OPCODES | US_FL_NO_SAME),
+/* Reported-by: Zhihong Zhou <zhouzhihong@greatwall.com.cn> */
+UNUSUAL_DEV(0x0781, 0x55e8, 0x0000, 0x9999,
+ "SanDisk",
+ "",
+ USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+ US_FL_IGNORE_UAS),
+
/* Reported-by: Hongling Zeng <zenghongling@kylinos.cn> */
UNUSUAL_DEV(0x090c, 0x2000, 0x0000, 0x9999,
"Hiksemi",
@@ -83,8 +90,15 @@ UNUSUAL_DEV(0x0bc2, 0x331a, 0x0000, 0x9999,
USB_SC_DEVICE, USB_PR_DEVICE, NULL,
US_FL_NO_REPORT_LUNS),
+/* Reported-by: Oliver Neukum <oneukum@suse.com> */
+UNUSUAL_DEV(0x125f, 0xa94a, 0x0160, 0x0160,
+ "ADATA",
+ "Portable HDD CH94",
+ USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+ US_FL_NO_ATA_1X),
+
/* Reported-by: Benjamin Tissoires <benjamin.tissoires@redhat.com> */
-UNUSUAL_DEV(0x13fd, 0x3940, 0x0000, 0x9999,
+UNUSUAL_DEV(0x13fd, 0x3940, 0x0000, 0x0309,
"Initio Corporation",
"INIC-3069",
USB_SC_DEVICE, USB_PR_DEVICE, NULL,
diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c
index d36f3b6992bb..fa83fe0defe2 100644
--- a/drivers/usb/storage/usb.c
+++ b/drivers/usb/storage/usb.c
@@ -537,7 +537,7 @@ static int associate_dev(struct us_data *us, struct usb_interface *intf)
usb_set_intfdata(intf, us);
/* Allocate the control/setup and DMA-mapped buffers */
- us->cr = kmalloc(sizeof(*us->cr), GFP_KERNEL);
+ us->cr = kmalloc_obj(*us->cr);
if (!us->cr)
return -ENOMEM;
@@ -1056,13 +1056,20 @@ int usb_stor_probe1(struct us_data **pus,
goto BadDevice;
/*
- * Some USB host controllers can't do DMA; they have to use PIO.
- * For such controllers we need to make sure the block layer sets
- * up bounce buffers in addressable memory.
+ * Some USB host controllers can't do DMA: They have to use PIO, or they
+ * have to use a small dedicated local memory area, or they have other
+ * restrictions on addressable memory.
+ *
+ * We can't support these controllers on highmem systems as we don't
+ * kmap or bounce buffer.
*/
- if (!hcd_uses_dma(bus_to_hcd(us->pusb_dev->bus)) ||
- bus_to_hcd(us->pusb_dev->bus)->localmem_pool)
- host->no_highmem = true;
+ if (IS_ENABLED(CONFIG_HIGHMEM) &&
+ (!hcd_uses_dma(bus_to_hcd(us->pusb_dev->bus)) ||
+ bus_to_hcd(us->pusb_dev->bus)->localmem_pool)) {
+ dev_warn(&intf->dev, "USB Mass Storage not supported on this host controller\n");
+ result = -EINVAL;
+ goto release;
+ }
/* Get the unusual_devs entries and the descriptors */
result = get_device_info(us, id, unusual_dev);
@@ -1081,6 +1088,7 @@ int usb_stor_probe1(struct us_data **pus,
BadDevice:
usb_stor_dbg(us, "storage_probe() failed\n");
+release:
release_everything(us);
return result;
}
diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile
index 7a368fea61bc..8a6a1c663eb6 100644
--- a/drivers/usb/typec/Makefile
+++ b/drivers/usb/typec/Makefile
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_TYPEC) += typec.o
-typec-y := class.o mux.o bus.o pd.o retimer.o
+typec-y := class.o mux.o bus.o pd.o retimer.o mode_selection.o
typec-$(CONFIG_ACPI) += port-mapper.o
obj-$(CONFIG_TYPEC) += altmodes/
obj-$(CONFIG_TYPEC_TCPM) += tcpm/
diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c
index ac84a6d64c2f..35d9c3086990 100644
--- a/drivers/usb/typec/altmodes/displayport.c
+++ b/drivers/usb/typec/altmodes/displayport.c
@@ -65,6 +65,13 @@ struct dp_altmode {
enum dp_state state;
bool hpd;
bool pending_hpd;
+ u32 irq_hpd_count;
+ /*
+ * hpd is mandatory for irq_hpd assertion, so irq_hpd also needs its own pending flag if
+ * both hpd and irq_hpd are asserted in the first Status Update before the pin assignment
+ * is configured.
+ */
+ bool pending_irq_hpd;
struct mutex lock; /* device lock */
struct work_struct work;
@@ -93,9 +100,14 @@ static int dp_altmode_configure(struct dp_altmode *dp, u8 con)
{
u8 pin_assign = 0;
u32 conf;
+ u32 signal;
/* DP Signalling */
- conf = (dp->data.conf & DP_CONF_SIGNALLING_MASK) >> DP_CONF_SIGNALLING_SHIFT;
+ signal = DP_CAP_DP_SIGNALLING(dp->port->vdo) & DP_CAP_DP_SIGNALLING(dp->alt->vdo);
+ if (dp->plug_prime)
+ signal &= DP_CAP_DP_SIGNALLING(dp->plug_prime->vdo);
+
+ conf = signal << DP_CONF_SIGNALLING_SHIFT;
switch (con) {
case DP_STATUS_CON_DISABLED:
@@ -151,6 +163,7 @@ static int dp_altmode_status_update(struct dp_altmode *dp)
{
bool configured = !!DP_CONF_GET_PIN_ASSIGN(dp->data.conf);
bool hpd = !!(dp->data.status & DP_STATUS_HPD_STATE);
+ bool irq_hpd = !!(dp->data.status & DP_STATUS_IRQ_HPD);
u8 con = DP_STATUS_CONNECTION(dp->data.status);
int ret = 0;
@@ -170,6 +183,8 @@ static int dp_altmode_status_update(struct dp_altmode *dp)
dp->hpd = hpd;
dp->pending_hpd = true;
}
+ if (dp->hpd && dp->pending_hpd && irq_hpd)
+ dp->pending_irq_hpd = true;
}
} else {
drm_connector_oob_hotplug_event(dp->connector_fwnode,
@@ -177,6 +192,10 @@ static int dp_altmode_status_update(struct dp_altmode *dp)
connector_status_disconnected);
dp->hpd = hpd;
sysfs_notify(&dp->alt->dev.kobj, "displayport", "hpd");
+ if (hpd && irq_hpd) {
+ dp->irq_hpd_count++;
+ sysfs_notify(&dp->alt->dev.kobj, "displayport", "irq_hpd");
+ }
}
return ret;
@@ -196,6 +215,11 @@ static int dp_altmode_configured(struct dp_altmode *dp)
connector_status_connected);
sysfs_notify(&dp->alt->dev.kobj, "displayport", "hpd");
dp->pending_hpd = false;
+ if (dp->pending_irq_hpd) {
+ dp->irq_hpd_count++;
+ sysfs_notify(&dp->alt->dev.kobj, "displayport", "irq_hpd");
+ dp->pending_irq_hpd = false;
+ }
}
return dp_altmode_notify(dp);
@@ -393,6 +417,9 @@ static int dp_altmode_vdm(struct typec_altmode *alt,
break;
case CMDT_RSP_NAK:
switch (cmd) {
+ case DP_CMD_STATUS_UPDATE:
+ dp->state = DP_STATE_EXIT;
+ break;
case DP_CMD_CONFIGURE:
dp->data.conf = 0;
ret = dp_altmode_configured(dp);
@@ -673,7 +700,7 @@ static ssize_t pin_assignment_show(struct device *dev,
assignments = get_current_pin_assignments(dp);
- for (i = 0; assignments; assignments >>= 1, i++) {
+ for (i = 0; assignments && i < DP_PIN_ASSIGN_MAX; assignments >>= 1, i++) {
if (assignments & 1) {
if (i == cur)
len += sprintf(buf + len, "[%s] ",
@@ -703,10 +730,19 @@ static ssize_t hpd_show(struct device *dev, struct device_attribute *attr, char
}
static DEVICE_ATTR_RO(hpd);
+static ssize_t irq_hpd_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct dp_altmode *dp = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%d\n", dp->irq_hpd_count);
+}
+static DEVICE_ATTR_RO(irq_hpd);
+
static struct attribute *displayport_attrs[] = {
&dev_attr_configuration.attr,
&dev_attr_pin_assignment.attr,
&dev_attr_hpd.attr,
+ &dev_attr_irq_hpd.attr,
NULL
};
@@ -727,18 +763,24 @@ int dp_altmode_probe(struct typec_altmode *alt)
struct fwnode_handle *fwnode;
struct dp_altmode *dp;
- /* FIXME: Port can only be DFP_U. */
+ /* Port can only be DFP_U. */
+ if (typec_altmode_get_data_role(alt) != TYPEC_HOST)
+ return -EPROTO;
/* Make sure we have compatible pin configurations */
if (!(DP_CAP_PIN_ASSIGN_DFP_D(port->vdo) &
DP_CAP_PIN_ASSIGN_UFP_D(alt->vdo)) &&
!(DP_CAP_PIN_ASSIGN_UFP_D(port->vdo) &
- DP_CAP_PIN_ASSIGN_DFP_D(alt->vdo)))
+ DP_CAP_PIN_ASSIGN_DFP_D(alt->vdo))) {
+ typec_altmode_put_plug(plug);
return -ENODEV;
+ }
dp = devm_kzalloc(&alt->dev, sizeof(*dp), GFP_KERNEL);
- if (!dp)
+ if (!dp) {
+ typec_altmode_put_plug(plug);
return -ENOMEM;
+ }
INIT_WORK(&dp->work, dp_altmode_work);
mutex_init(&dp->lock);
@@ -767,8 +809,10 @@ int dp_altmode_probe(struct typec_altmode *alt)
if (plug)
typec_altmode_set_drvdata(plug, dp);
- dp->state = plug ? DP_STATE_ENTER_PRIME : DP_STATE_ENTER;
- schedule_work(&dp->work);
+ if (!alt->mode_selection) {
+ dp->state = plug ? DP_STATE_ENTER_PRIME : DP_STATE_ENTER;
+ schedule_work(&dp->work);
+ }
return 0;
}
diff --git a/drivers/usb/typec/altmodes/thunderbolt.c b/drivers/usb/typec/altmodes/thunderbolt.c
index 1b475b1d98e7..32250b94262a 100644
--- a/drivers/usb/typec/altmodes/thunderbolt.c
+++ b/drivers/usb/typec/altmodes/thunderbolt.c
@@ -39,28 +39,7 @@ static bool tbt_ready(struct typec_altmode *alt);
static int tbt_enter_mode(struct tbt_altmode *tbt)
{
- struct typec_altmode *plug = tbt->plug[TYPEC_PLUG_SOP_P];
- u32 vdo;
-
- vdo = tbt->alt->vdo & (TBT_VENDOR_SPECIFIC_B0 | TBT_VENDOR_SPECIFIC_B1);
- vdo |= tbt->alt->vdo & TBT_INTEL_SPECIFIC_B0;
- vdo |= TBT_MODE;
-
- if (plug) {
- if (typec_cable_is_active(tbt->cable))
- vdo |= TBT_ENTER_MODE_ACTIVE_CABLE;
-
- vdo |= TBT_ENTER_MODE_CABLE_SPEED(TBT_CABLE_SPEED(plug->vdo));
- vdo |= plug->vdo & TBT_CABLE_ROUNDED;
- vdo |= plug->vdo & TBT_CABLE_OPTICAL;
- vdo |= plug->vdo & TBT_CABLE_RETIMER;
- vdo |= plug->vdo & TBT_CABLE_LINK_TRAINING;
- } else {
- vdo |= TBT_ENTER_MODE_CABLE_SPEED(TBT_CABLE_USB3_PASSIVE);
- }
-
- tbt->enter_vdo = vdo;
- return typec_altmode_enter(tbt->alt, &vdo);
+ return typec_altmode_enter(tbt->alt, &tbt->enter_vdo);
}
static void tbt_altmode_work(struct work_struct *work)
@@ -112,7 +91,7 @@ static void tbt_altmode_work(struct work_struct *work)
return;
disable_plugs:
- for (int i = TYPEC_PLUG_SOP_PP; i > 0; --i) {
+ for (int i = TYPEC_PLUG_SOP_PP; i >= 0; --i) {
if (tbt->plug[i])
typec_altmode_put_plug(tbt->plug[i]);
@@ -143,7 +122,7 @@ static int tbt_enter_modes_ordered(struct typec_altmode *alt)
if (tbt->plug[TYPEC_PLUG_SOP_P]) {
ret = typec_cable_altmode_enter(alt, TYPEC_PLUG_SOP_P, NULL);
if (ret < 0) {
- for (int i = TYPEC_PLUG_SOP_PP; i > 0; --i) {
+ for (int i = TYPEC_PLUG_SOP_PP; i >= 0; --i) {
if (tbt->plug[i])
typec_altmode_put_plug(tbt->plug[i]);
@@ -307,7 +286,7 @@ static int tbt_altmode_probe(struct typec_altmode *alt)
typec_altmode_set_drvdata(alt, tbt);
typec_altmode_set_ops(alt, &tbt_altmode_ops);
- if (tbt_ready(alt)) {
+ if (!alt->mode_selection && tbt_ready(alt)) {
if (tbt->plug[TYPEC_PLUG_SOP_P])
tbt->state = TBT_STATE_SOP_P_ENTER;
else if (tbt->plug[TYPEC_PLUG_SOP_PP])
@@ -324,7 +303,7 @@ static void tbt_altmode_remove(struct typec_altmode *alt)
{
struct tbt_altmode *tbt = typec_altmode_get_drvdata(alt);
- for (int i = TYPEC_PLUG_SOP_PP; i > 0; --i) {
+ for (int i = TYPEC_PLUG_SOP_PP; i >= 0; --i) {
if (tbt->plug[i])
typec_altmode_put_plug(tbt->plug[i]);
}
@@ -337,6 +316,7 @@ static bool tbt_ready(struct typec_altmode *alt)
{
struct tbt_altmode *tbt = typec_altmode_get_drvdata(alt);
struct typec_altmode *plug;
+ u32 vdo;
if (tbt->cable)
return true;
@@ -351,10 +331,10 @@ static bool tbt_ready(struct typec_altmode *alt)
*/
for (int i = 0; i < TYPEC_PLUG_SOP_PP + 1; i++) {
plug = typec_altmode_get_plug(tbt->alt, i);
- if (IS_ERR(plug))
+ if (!plug)
continue;
- if (!plug || plug->svid != USB_TYPEC_TBT_SID)
+ if (plug->svid != USB_TYPEC_TBT_SID)
break;
plug->desc = "Thunderbolt3";
@@ -364,6 +344,26 @@ static bool tbt_ready(struct typec_altmode *alt)
tbt->plug[i] = plug;
}
+ vdo = tbt->alt->vdo & (TBT_VENDOR_SPECIFIC_B0 | TBT_VENDOR_SPECIFIC_B1);
+ vdo |= tbt->alt->vdo & TBT_INTEL_SPECIFIC_B0;
+ vdo |= TBT_MODE;
+ plug = tbt->plug[TYPEC_PLUG_SOP_P];
+
+ if (plug) {
+ if (typec_cable_is_active(tbt->cable))
+ vdo |= TBT_ENTER_MODE_ACTIVE_CABLE;
+
+ vdo |= TBT_ENTER_MODE_CABLE_SPEED(TBT_CABLE_SPEED(plug->vdo));
+ vdo |= plug->vdo & TBT_CABLE_ROUNDED;
+ vdo |= plug->vdo & TBT_CABLE_OPTICAL;
+ vdo |= plug->vdo & TBT_CABLE_RETIMER;
+ vdo |= plug->vdo & TBT_CABLE_LINK_TRAINING;
+ } else {
+ vdo |= TBT_ENTER_MODE_CABLE_SPEED(TBT_CABLE_USB3_PASSIVE);
+ }
+
+ tbt->enter_vdo = vdo;
+
return true;
}
diff --git a/drivers/usb/typec/anx7411.c b/drivers/usb/typec/anx7411.c
index 0ae0a5ee3fae..2e8ae1d2faf9 100644
--- a/drivers/usb/typec/anx7411.c
+++ b/drivers/usb/typec/anx7411.c
@@ -1516,8 +1516,7 @@ static int anx7411_i2c_probe(struct i2c_client *client)
INIT_WORK(&plat->work, anx7411_work_func);
plat->workqueue = alloc_workqueue("anx7411_work",
- WQ_FREEZABLE |
- WQ_MEM_RECLAIM,
+ WQ_FREEZABLE | WQ_MEM_RECLAIM | WQ_PERCPU,
1);
if (!plat->workqueue) {
dev_err(dev, "fail to create work queue\n");
diff --git a/drivers/usb/typec/bus.c b/drivers/usb/typec/bus.c
index ae90688d23e4..e84b134a3381 100644
--- a/drivers/usb/typec/bus.c
+++ b/drivers/usb/typec/bus.c
@@ -445,14 +445,33 @@ static struct attribute *typec_attrs[] = {
&dev_attr_description.attr,
NULL
};
-ATTRIBUTE_GROUPS(typec);
+
+static umode_t typec_is_visible(struct kobject *kobj, struct attribute *attr, int n)
+{
+ if (is_typec_partner_altmode(kobj_to_dev(kobj)))
+ return attr->mode;
+ return 0;
+}
+
+static const struct attribute_group typec_group = {
+ .is_visible = typec_is_visible,
+ .attrs = typec_attrs,
+};
+
+static const struct attribute_group *typec_groups[] = {
+ &typec_group,
+ NULL
+};
static int typec_match(struct device *dev, const struct device_driver *driver)
{
- struct typec_altmode_driver *drv = to_altmode_driver(driver);
+ const struct typec_altmode_driver *drv = to_altmode_driver(driver);
struct typec_altmode *altmode = to_typec_altmode(dev);
const struct typec_device_id *id;
+ if (!is_typec_partner_altmode(dev))
+ return 0;
+
for (id = drv->id_table; id->svid; id++)
if (id->svid == altmode->svid)
return 1;
@@ -463,6 +482,9 @@ static int typec_uevent(const struct device *dev, struct kobj_uevent_env *env)
{
const struct typec_altmode *altmode = to_typec_altmode(dev);
+ if (!is_typec_partner_altmode(dev))
+ return 0;
+
if (add_uevent_var(env, "SVID=%04X", altmode->svid))
return -ENOMEM;
@@ -547,3 +569,4 @@ const struct bus_type typec_bus = {
.probe = typec_probe,
.remove = typec_remove,
};
+EXPORT_SYMBOL_GPL(typec_bus);
diff --git a/drivers/usb/typec/bus.h b/drivers/usb/typec/bus.h
index 643b8c81786d..7df5deb1dd3a 100644
--- a/drivers/usb/typec/bus.h
+++ b/drivers/usb/typec/bus.h
@@ -5,7 +5,6 @@
#include <linux/usb/typec_altmode.h>
-struct bus_type;
struct typec_mux;
struct typec_retimer;
@@ -28,9 +27,4 @@ struct altmode {
#define to_altmode(d) container_of(d, struct altmode, adev)
-extern const struct bus_type typec_bus;
-extern const struct device_type typec_altmode_dev_type;
-
-#define is_typec_altmode(_dev_) (_dev_->type == &typec_altmode_dev_type)
-
#endif /* __USB_TYPEC_ALTMODE_H__ */
diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c
index 9c76c3d0c6cf..0977581ad1b6 100644
--- a/drivers/usb/typec/class.c
+++ b/drivers/usb/typec/class.c
@@ -235,7 +235,7 @@ static int altmode_match(struct device *dev, const void *data)
struct typec_altmode *adev = to_typec_altmode(dev);
const struct typec_device_id *id = data;
- if (!is_typec_altmode(dev))
+ if (!is_typec_port_altmode(dev))
return 0;
return (adev->svid == id->svid);
@@ -445,11 +445,88 @@ svid_show(struct device *dev, struct device_attribute *attr, char *buf)
}
static DEVICE_ATTR_RO(svid);
+static int increment_duplicated_priority(struct device *dev, void *data)
+{
+ if (is_typec_port_altmode(dev)) {
+ struct typec_altmode **alt_target = (struct typec_altmode **)data;
+ struct typec_altmode *alt = to_typec_altmode(dev);
+
+ if (alt != *alt_target && alt->priority == (*alt_target)->priority) {
+ alt->priority++;
+ *alt_target = alt;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int find_duplicated_priority(struct device *dev, void *data)
+{
+ if (is_typec_port_altmode(dev)) {
+ struct typec_altmode **alt_target = (struct typec_altmode **)data;
+ struct typec_altmode *alt = to_typec_altmode(dev);
+
+ if (alt != *alt_target && alt->priority == (*alt_target)->priority)
+ return 1;
+ }
+ return 0;
+}
+
+static int typec_mode_set_priority(struct typec_altmode *alt, const u8 priority)
+{
+ struct typec_port *port = to_typec_port(alt->dev.parent);
+ const u8 old_priority = alt->priority;
+ int res = 1;
+
+ alt->priority = priority;
+ while (res) {
+ res = device_for_each_child(&port->dev, &alt, find_duplicated_priority);
+ if (res) {
+ alt->priority++;
+ if (alt->priority == 0) {
+ alt->priority = old_priority;
+ return -EOVERFLOW;
+ }
+ }
+ }
+
+ res = 1;
+ alt->priority = priority;
+ while (res)
+ res = device_for_each_child(&port->dev, &alt,
+ increment_duplicated_priority);
+
+ return 0;
+}
+
+static ssize_t priority_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ u8 val;
+ int err = kstrtou8(buf, 10, &val);
+
+ if (!err)
+ err = typec_mode_set_priority(to_typec_altmode(dev), val);
+
+ if (!err)
+ return size;
+ return err;
+}
+
+static ssize_t priority_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sysfs_emit(buf, "%u\n", to_typec_altmode(dev)->priority);
+}
+static DEVICE_ATTR_RW(priority);
+
static struct attribute *typec_altmode_attrs[] = {
&dev_attr_active.attr,
&dev_attr_mode.attr,
&dev_attr_svid.attr,
&dev_attr_vdo.attr,
+ &dev_attr_priority.attr,
NULL
};
@@ -457,11 +534,17 @@ static umode_t typec_altmode_attr_is_visible(struct kobject *kobj,
struct attribute *attr, int n)
{
struct typec_altmode *adev = to_typec_altmode(kobj_to_dev(kobj));
+ struct typec_port *port = typec_altmode2port(adev);
- if (attr == &dev_attr_active.attr)
- if (!is_typec_port(adev->dev.parent) &&
- (!adev->ops || !adev->ops->activate))
- return 0444;
+ if (attr == &dev_attr_active.attr) {
+ if (!is_typec_port(adev->dev.parent)) {
+ if (!port->mode_control || !adev->ops || !adev->ops->activate)
+ return 0444;
+ }
+ } else if (attr == &dev_attr_priority.attr) {
+ if (!is_typec_port(adev->dev.parent) || !port->mode_control)
+ return 0;
+ }
return attr->mode;
}
@@ -532,22 +615,38 @@ static void typec_altmode_release(struct device *dev)
kfree(alt);
}
-const struct device_type typec_altmode_dev_type = {
- .name = "typec_alternate_mode",
+const struct device_type typec_port_altmode_dev_type = {
+ .name = "typec_port_alternate_mode",
.groups = typec_altmode_groups,
.release = typec_altmode_release,
};
+EXPORT_SYMBOL_GPL(typec_port_altmode_dev_type);
+
+const struct device_type typec_plug_altmode_dev_type = {
+ .name = "typec_plug_alternate_mode",
+ .groups = typec_altmode_groups,
+ .release = typec_altmode_release,
+};
+EXPORT_SYMBOL_GPL(typec_plug_altmode_dev_type);
+
+const struct device_type typec_partner_altmode_dev_type = {
+ .name = "typec_partner_alternate_mode",
+ .groups = typec_altmode_groups,
+ .release = typec_altmode_release,
+};
+EXPORT_SYMBOL_GPL(typec_partner_altmode_dev_type);
static struct typec_altmode *
typec_register_altmode(struct device *parent,
- const struct typec_altmode_desc *desc)
+ const struct typec_altmode_desc *desc,
+ const struct device_type *type)
{
unsigned int id = altmode_id_get(parent);
bool is_port = is_typec_port(parent);
struct altmode *alt;
int ret;
- alt = kzalloc(sizeof(*alt), GFP_KERNEL);
+ alt = kzalloc_obj(*alt);
if (!alt) {
altmode_id_remove(parent, id);
return ERR_PTR(-ENOMEM);
@@ -556,6 +655,7 @@ typec_register_altmode(struct device *parent,
alt->adev.svid = desc->svid;
alt->adev.mode = desc->mode;
alt->adev.vdo = desc->vdo;
+ alt->adev.mode_selection = desc->mode_selection;
alt->roles = desc->roles;
alt->id = id;
@@ -575,7 +675,7 @@ typec_register_altmode(struct device *parent,
alt->adev.dev.parent = parent;
alt->adev.dev.groups = alt->groups;
- alt->adev.dev.type = &typec_altmode_dev_type;
+ alt->adev.dev.type = type;
dev_set_name(&alt->adev.dev, "%s.%u", dev_name(parent), id);
get_device(alt->adev.dev.parent);
@@ -584,13 +684,7 @@ typec_register_altmode(struct device *parent,
if (!is_port)
typec_altmode_set_partner(alt);
- /* The partners are bind to drivers */
- if (is_typec_partner(parent))
- alt->adev.dev.bus = &typec_bus;
-
- /* Plug alt modes need a class to generate udev events. */
- if (is_typec_plug(parent))
- alt->adev.dev.class = &typec_class;
+ alt->adev.dev.bus = &typec_bus;
ret = device_register(&alt->adev.dev);
if (ret) {
@@ -963,7 +1057,7 @@ struct typec_altmode *
typec_partner_register_altmode(struct typec_partner *partner,
const struct typec_altmode_desc *desc)
{
- return typec_register_altmode(&partner->dev, desc);
+ return typec_register_altmode(&partner->dev, desc, &typec_partner_altmode_dev_type);
}
EXPORT_SYMBOL_GPL(typec_partner_register_altmode);
@@ -1015,7 +1109,7 @@ struct typec_partner *typec_register_partner(struct typec_port *port,
struct typec_partner *partner;
int ret;
- partner = kzalloc(sizeof(*partner), GFP_KERNEL);
+ partner = kzalloc_obj(*partner);
if (!partner)
return ERR_PTR(-ENOMEM);
@@ -1052,9 +1146,11 @@ struct typec_partner *typec_register_partner(struct typec_port *port,
partner->usb_mode = USB_MODE_USB3;
}
+ mutex_lock(&port->partner_link_lock);
ret = device_register(&partner->dev);
if (ret) {
dev_err(&port->dev, "failed to register partner (%d)\n", ret);
+ mutex_unlock(&port->partner_link_lock);
put_device(&partner->dev);
return ERR_PTR(ret);
}
@@ -1063,6 +1159,7 @@ struct typec_partner *typec_register_partner(struct typec_port *port,
typec_partner_link_device(partner, port->usb2_dev);
if (port->usb3_dev)
typec_partner_link_device(partner, port->usb3_dev);
+ mutex_unlock(&port->partner_link_lock);
return partner;
}
@@ -1083,12 +1180,18 @@ void typec_unregister_partner(struct typec_partner *partner)
port = to_typec_port(partner->dev.parent);
- if (port->usb2_dev)
+ mutex_lock(&port->partner_link_lock);
+ if (port->usb2_dev) {
typec_partner_unlink_device(partner, port->usb2_dev);
- if (port->usb3_dev)
+ port->usb2_dev = NULL;
+ }
+ if (port->usb3_dev) {
typec_partner_unlink_device(partner, port->usb3_dev);
+ port->usb3_dev = NULL;
+ }
device_unregister(&partner->dev);
+ mutex_unlock(&port->partner_link_lock);
}
EXPORT_SYMBOL_GPL(typec_unregister_partner);
@@ -1184,7 +1287,7 @@ struct typec_altmode *
typec_plug_register_altmode(struct typec_plug *plug,
const struct typec_altmode_desc *desc)
{
- return typec_register_altmode(&plug->dev, desc);
+ return typec_register_altmode(&plug->dev, desc, &typec_plug_altmode_dev_type);
}
EXPORT_SYMBOL_GPL(typec_plug_register_altmode);
@@ -1206,7 +1309,7 @@ struct typec_plug *typec_register_plug(struct typec_cable *cable,
char name[8];
int ret;
- plug = kzalloc(sizeof(*plug), GFP_KERNEL);
+ plug = kzalloc_obj(*plug);
if (!plug)
return ERR_PTR(-ENOMEM);
@@ -1359,7 +1462,7 @@ struct typec_cable *typec_register_cable(struct typec_port *port,
struct typec_cable *cable;
int ret;
- cable = kzalloc(sizeof(*cable), GFP_KERNEL);
+ cable = kzalloc_obj(*cable);
if (!cable)
return ERR_PTR(-ENOMEM);
@@ -2041,10 +2144,11 @@ static struct typec_partner *typec_get_partner(struct typec_port *port)
static void typec_partner_attach(struct typec_connector *con, struct device *dev)
{
struct typec_port *port = container_of(con, struct typec_port, con);
- struct typec_partner *partner = typec_get_partner(port);
+ struct typec_partner *partner;
struct usb_device *udev = to_usb_device(dev);
enum usb_mode usb_mode;
+ mutex_lock(&port->partner_link_lock);
if (udev->speed < USB_SPEED_SUPER) {
usb_mode = USB_MODE_USB2;
port->usb2_dev = dev;
@@ -2053,18 +2157,22 @@ static void typec_partner_attach(struct typec_connector *con, struct device *dev
port->usb3_dev = dev;
}
+ partner = typec_get_partner(port);
if (partner) {
typec_partner_set_usb_mode(partner, usb_mode);
typec_partner_link_device(partner, dev);
put_device(&partner->dev);
}
+ mutex_unlock(&port->partner_link_lock);
}
static void typec_partner_deattach(struct typec_connector *con, struct device *dev)
{
struct typec_port *port = container_of(con, struct typec_port, con);
- struct typec_partner *partner = typec_get_partner(port);
+ struct typec_partner *partner;
+ mutex_lock(&port->partner_link_lock);
+ partner = typec_get_partner(port);
if (partner) {
typec_partner_unlink_device(partner, dev);
put_device(&partner->dev);
@@ -2074,6 +2182,7 @@ static void typec_partner_deattach(struct typec_connector *con, struct device *d
port->usb2_dev = NULL;
else if (port->usb3_dev == dev)
port->usb3_dev = NULL;
+ mutex_unlock(&port->partner_link_lock);
}
/**
@@ -2106,6 +2215,19 @@ void typec_set_data_role(struct typec_port *port, enum typec_data_role role)
EXPORT_SYMBOL_GPL(typec_set_data_role);
/**
+ * typec_get_data_role - Get port data role
+ * @port: The USB Type-C Port to query
+ *
+ * This routine is used by the altmode drivers to determine if the port is the
+ * DFP before issuing Enter Mode
+ */
+enum typec_data_role typec_get_data_role(struct typec_port *port)
+{
+ return port->data_role;
+}
+EXPORT_SYMBOL_GPL(typec_get_data_role);
+
+/**
* typec_set_pwr_role - Report power role change
* @port: The USB Type-C Port where the role was changed
* @role: The new data role
@@ -2454,6 +2576,7 @@ typec_port_register_altmode(struct typec_port *port,
struct typec_altmode *adev;
struct typec_mux *mux;
struct typec_retimer *retimer;
+ int ret;
mux = typec_mux_get(&port->dev);
if (IS_ERR(mux))
@@ -2465,13 +2588,19 @@ typec_port_register_altmode(struct typec_port *port,
return ERR_CAST(retimer);
}
- adev = typec_register_altmode(&port->dev, desc);
+ adev = typec_register_altmode(&port->dev, desc, &typec_port_altmode_dev_type);
if (IS_ERR(adev)) {
typec_retimer_put(retimer);
typec_mux_put(mux);
} else {
to_altmode(adev)->mux = mux;
to_altmode(adev)->retimer = retimer;
+
+ ret = typec_mode_set_priority(adev, 0);
+ if (ret) {
+ typec_unregister_altmode(adev);
+ return ERR_PTR(ret);
+ }
}
return adev;
@@ -2570,7 +2699,7 @@ struct typec_port *typec_register_port(struct device *parent,
int ret;
int id;
- port = kzalloc(sizeof(*port), GFP_KERNEL);
+ port = kzalloc_obj(*port);
if (!port)
return ERR_PTR(-ENOMEM);
@@ -2614,6 +2743,7 @@ struct typec_port *typec_register_port(struct device *parent,
ida_init(&port->mode_ids);
mutex_init(&port->port_type_lock);
+ mutex_init(&port->partner_link_lock);
port->id = id;
port->ops = cap->ops;
@@ -2665,6 +2795,7 @@ struct typec_port *typec_register_port(struct device *parent,
}
port->pd = cap->pd;
+ port->mode_control = !cap->no_mode_control;
ret = device_add(&port->dev);
if (ret) {
diff --git a/drivers/usb/typec/class.h b/drivers/usb/typec/class.h
index b3076a24ad2e..d3435936ee7c 100644
--- a/drivers/usb/typec/class.h
+++ b/drivers/usb/typec/class.h
@@ -9,6 +9,7 @@
struct typec_mux;
struct typec_switch;
struct usb_device;
+struct mode_selection;
struct typec_plug {
struct device dev;
@@ -39,6 +40,7 @@ struct typec_partner {
u8 usb_capability;
struct usb_power_delivery *pd;
+ struct mode_selection *sel;
void (*attach)(struct typec_partner *partner, struct device *dev);
void (*deattach)(struct typec_partner *partner, struct device *dev);
@@ -59,8 +61,10 @@ struct typec_port {
enum typec_port_type port_type;
enum usb_mode usb_mode;
struct mutex port_type_lock;
+ struct mutex partner_link_lock;
enum typec_orientation orientation;
+ bool mode_control;
struct typec_switch *sw;
struct typec_mux *mux;
struct typec_retimer *retimer;
diff --git a/drivers/usb/typec/hd3ss3220.c b/drivers/usb/typec/hd3ss3220.c
index 3ecc688dda82..3e39b800e6b5 100644
--- a/drivers/usb/typec/hd3ss3220.c
+++ b/drivers/usb/typec/hd3ss3220.c
@@ -15,6 +15,9 @@
#include <linux/usb/typec.h>
#include <linux/delay.h>
#include <linux/workqueue.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+#include <linux/of_graph.h>
#define HD3SS3220_REG_CN_STAT 0x08
#define HD3SS3220_REG_CN_STAT_CTRL 0x09
@@ -54,6 +57,11 @@ struct hd3ss3220 {
struct delayed_work output_poll_work;
enum usb_role role_state;
bool poll;
+
+ struct gpio_desc *id_gpiod;
+ int id_irq;
+
+ struct regulator *vbus;
};
static int hd3ss3220_set_power_opmode(struct hd3ss3220 *hd3ss3220, int power_opmode)
@@ -196,6 +204,23 @@ static const struct typec_operations hd3ss3220_ops = {
.port_type_set = hd3ss3220_port_type_set,
};
+static void hd3ss3220_regulator_control(struct hd3ss3220 *hd3ss3220, bool on)
+{
+ int ret;
+
+ if (regulator_is_enabled(hd3ss3220->vbus) == on)
+ return;
+
+ if (on)
+ ret = regulator_enable(hd3ss3220->vbus);
+ else
+ ret = regulator_disable(hd3ss3220->vbus);
+
+ if (ret)
+ dev_err(hd3ss3220->dev,
+ "vbus regulator %s failed: %d\n", on ? "disable" : "enable", ret);
+}
+
static void hd3ss3220_set_role(struct hd3ss3220 *hd3ss3220)
{
enum usb_role role_state = hd3ss3220_get_attached_state(hd3ss3220);
@@ -213,6 +238,9 @@ static void hd3ss3220_set_role(struct hd3ss3220 *hd3ss3220)
break;
}
+ if (hd3ss3220->vbus && !hd3ss3220->id_gpiod)
+ hd3ss3220_regulator_control(hd3ss3220, role_state == USB_ROLE_HOST);
+
hd3ss3220->role_state = role_state;
}
@@ -319,13 +347,25 @@ static const struct regmap_config config = {
.max_register = 0x0A,
};
+static irqreturn_t hd3ss3220_id_isr(int irq, void *dev_id)
+{
+ struct hd3ss3220 *hd3ss3220 = dev_id;
+ int id;
+
+ id = gpiod_get_value_cansleep(hd3ss3220->id_gpiod);
+ hd3ss3220_regulator_control(hd3ss3220, !id);
+
+ return IRQ_HANDLED;
+}
+
static int hd3ss3220_probe(struct i2c_client *client)
{
struct typec_capability typec_cap = { };
- struct hd3ss3220 *hd3ss3220;
struct fwnode_handle *connector, *ep;
- int ret;
+ struct hd3ss3220 *hd3ss3220;
+ struct regulator *vbus;
unsigned int data;
+ int ret;
hd3ss3220 = devm_kzalloc(&client->dev, sizeof(struct hd3ss3220),
GFP_KERNEL);
@@ -359,6 +399,49 @@ static int hd3ss3220_probe(struct i2c_client *client)
goto err_put_fwnode;
}
+ vbus = devm_of_regulator_get_optional(hd3ss3220->dev,
+ to_of_node(connector),
+ "vbus");
+ if (IS_ERR(vbus) && vbus != ERR_PTR(-ENODEV)) {
+ ret = PTR_ERR(vbus);
+ dev_err(hd3ss3220->dev, "failed to get vbus: %d", ret);
+ goto err_put_fwnode;
+ }
+
+ hd3ss3220->vbus = (vbus == ERR_PTR(-ENODEV) ? NULL : vbus);
+
+ if (hd3ss3220->vbus) {
+ hd3ss3220->id_gpiod = devm_gpiod_get_optional(hd3ss3220->dev,
+ "id",
+ GPIOD_IN);
+ if (IS_ERR(hd3ss3220->id_gpiod)) {
+ ret = PTR_ERR(hd3ss3220->id_gpiod);
+ goto err_put_fwnode;
+ }
+ }
+
+ if (hd3ss3220->id_gpiod) {
+ hd3ss3220->id_irq = gpiod_to_irq(hd3ss3220->id_gpiod);
+ if (hd3ss3220->id_irq < 0) {
+ ret = hd3ss3220->id_irq;
+ dev_err(hd3ss3220->dev,
+ "failed to get ID gpio: %d\n",
+ hd3ss3220->id_irq);
+ goto err_put_fwnode;
+ }
+
+ ret = devm_request_threaded_irq(hd3ss3220->dev,
+ hd3ss3220->id_irq, NULL,
+ hd3ss3220_id_isr,
+ IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ dev_name(hd3ss3220->dev), hd3ss3220);
+ if (ret < 0) {
+ dev_err(hd3ss3220->dev, "failed to get ID irq: %d\n", ret);
+ goto err_put_fwnode;
+ }
+ }
+
typec_cap.prefer_role = TYPEC_NO_PREFERRED_ROLE;
typec_cap.driver_data = hd3ss3220;
typec_cap.type = TYPEC_PORT_DRP;
diff --git a/drivers/usb/typec/mode_selection.c b/drivers/usb/typec/mode_selection.c
new file mode 100644
index 000000000000..cd376f67e445
--- /dev/null
+++ b/drivers/usb/typec/mode_selection.c
@@ -0,0 +1,283 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2025 Google LLC.
+ */
+
+#include <linux/types.h>
+#include <linux/list_sort.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <linux/usb/typec_altmode.h>
+
+#include "class.h"
+
+/**
+ * struct mode_state - State tracking for a specific Type-C alternate mode
+ * @svid: Standard or Vendor ID of the Alternate Mode
+ * @priority: Mode priority
+ * @error: Outcome of the last attempt to enter the mode
+ * @list: List head to link this mode state into a prioritized list
+ */
+struct mode_state {
+ u16 svid;
+ u8 priority;
+ int error;
+ struct list_head list;
+};
+
+/**
+ * struct mode_selection - Manages the selection and state of Alternate Modes
+ * @mode_list: Prioritized list of available Alternate Modes
+ * @lock: Mutex to protect mode_list
+ * @work: Work structure
+ * @partner: Handle to the Type-C partner device
+ * @active_svid: svid of currently active mode
+ * @timeout: Timeout for a mode entry attempt, ms
+ * @delay: Delay between mode entry/exit attempts, ms
+ */
+struct mode_selection {
+ struct list_head mode_list;
+ /* Protects the mode_list*/
+ struct mutex lock;
+ struct delayed_work work;
+ struct typec_partner *partner;
+ u16 active_svid;
+ unsigned int timeout;
+ unsigned int delay;
+};
+
+/**
+ * struct mode_order - Mode activation tracking
+ * @svid: Standard or Vendor ID of the Alternate Mode
+ * @enter: Flag indicating if the driver is currently attempting to enter or
+ * exit the mode
+ * @result: Outcome of the attempt to activate the mode
+ */
+struct mode_order {
+ u16 svid;
+ int enter;
+ int result;
+};
+
+static int activate_altmode(struct device *dev, void *data)
+{
+ if (is_typec_partner_altmode(dev)) {
+ struct typec_altmode *alt = to_typec_altmode(dev);
+ struct mode_order *order = (struct mode_order *)data;
+
+ if (order->svid == alt->svid) {
+ if (alt->ops && alt->ops->activate)
+ order->result = alt->ops->activate(alt, order->enter);
+ else
+ order->result = -EOPNOTSUPP;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int mode_selection_activate(struct mode_selection *sel,
+ const u16 svid, const int enter)
+
+ __must_hold(&sel->lock)
+{
+ struct mode_order order = {.svid = svid, .enter = enter, .result = -ENODEV};
+
+ /*
+ * The port driver may acquire its internal mutex during alternate mode
+ * activation. Since this is the same mutex that may be held during the
+ * execution of typec_altmode_state_update(), it is crucial to release
+ * sel->mutex before activation to avoid potential deadlock.
+ * Note that sel->mode_list must remain invariant throughout this unlocked
+ * interval.
+ */
+ mutex_unlock(&sel->lock);
+ device_for_each_child(&sel->partner->dev, &order, activate_altmode);
+ mutex_lock(&sel->lock);
+
+ return order.result;
+}
+
+static void mode_list_clean(struct mode_selection *sel)
+{
+ struct mode_state *ms, *tmp;
+
+ list_for_each_entry_safe(ms, tmp, &sel->mode_list, list) {
+ list_del(&ms->list);
+ kfree(ms);
+ }
+}
+
+/**
+ * mode_selection_work_fn() - Alternate mode activation task
+ * @work: work structure
+ *
+ * - If the Alternate Mode currently prioritized at the top of the list is already
+ * active, the entire selection process is considered finished.
+ * - If a different Alternate Mode is currently active, the system must exit that
+ * active mode first before attempting any new entry.
+ *
+ * The function then checks the result of the attempt to entre the current mode,
+ * stored in the `ms->error` field:
+ * - if the attempt FAILED, the mode is deactivated and removed from the list.
+ * - `ms->error` value of 0 signifies that the mode has not yet been activated.
+ *
+ * Once successfully activated, the task is scheduled for subsequent entry after
+ * a timeout period. The alternate mode driver is expected to call back with the
+ * actual mode entry result via `typec_altmode_state_update()`.
+ */
+static void mode_selection_work_fn(struct work_struct *work)
+{
+ struct mode_selection *sel = container_of(work,
+ struct mode_selection, work.work);
+ struct mode_state *ms;
+ unsigned int delay = sel->delay;
+ int result;
+
+ guard(mutex)(&sel->lock);
+
+ ms = list_first_entry_or_null(&sel->mode_list, struct mode_state, list);
+ if (!ms)
+ return;
+
+ if (sel->active_svid == ms->svid) {
+ dev_dbg(&sel->partner->dev, "%x altmode is active\n", ms->svid);
+ mode_list_clean(sel);
+ } else if (sel->active_svid != 0) {
+ result = mode_selection_activate(sel, sel->active_svid, 0);
+ if (result)
+ mode_list_clean(sel);
+ else
+ sel->active_svid = 0;
+ } else if (ms->error) {
+ dev_err(&sel->partner->dev, "%x: entry error %pe\n",
+ ms->svid, ERR_PTR(ms->error));
+ mode_selection_activate(sel, ms->svid, 0);
+ list_del(&ms->list);
+ kfree(ms);
+ } else {
+ result = mode_selection_activate(sel, ms->svid, 1);
+ if (result) {
+ dev_err(&sel->partner->dev, "%x: activation error %pe\n",
+ ms->svid, ERR_PTR(result));
+ list_del(&ms->list);
+ kfree(ms);
+ } else {
+ delay = sel->timeout;
+ ms->error = -ETIMEDOUT;
+ }
+ }
+
+ if (!list_empty(&sel->mode_list))
+ schedule_delayed_work(&sel->work, msecs_to_jiffies(delay));
+}
+
+void typec_altmode_state_update(struct typec_partner *partner, const u16 svid,
+ const int error)
+{
+ struct mode_selection *sel = partner->sel;
+ struct mode_state *ms;
+
+ if (sel) {
+ mutex_lock(&sel->lock);
+ ms = list_first_entry_or_null(&sel->mode_list, struct mode_state, list);
+ if (ms && ms->svid == svid) {
+ ms->error = error;
+ if (cancel_delayed_work(&sel->work))
+ schedule_delayed_work(&sel->work, 0);
+ }
+ if (!error)
+ sel->active_svid = svid;
+ else
+ sel->active_svid = 0;
+ mutex_unlock(&sel->lock);
+ }
+}
+EXPORT_SYMBOL_GPL(typec_altmode_state_update);
+
+static int compare_priorities(void *priv,
+ const struct list_head *a, const struct list_head *b)
+{
+ const struct mode_state *msa = container_of(a, struct mode_state, list);
+ const struct mode_state *msb = container_of(b, struct mode_state, list);
+
+ if (msa->priority < msb->priority)
+ return -1;
+ return 1;
+}
+
+static int altmode_add_to_list(struct device *dev, void *data)
+{
+ if (is_typec_partner_altmode(dev)) {
+ struct list_head *list = (struct list_head *)data;
+ struct typec_altmode *altmode = to_typec_altmode(dev);
+ const struct typec_altmode *pdev = typec_altmode_get_partner(altmode);
+ struct mode_state *ms;
+
+ if (pdev && altmode->ops && altmode->ops->activate) {
+ ms = kzalloc_obj(*ms);
+ if (!ms)
+ return -ENOMEM;
+ ms->svid = pdev->svid;
+ ms->priority = pdev->priority;
+ INIT_LIST_HEAD(&ms->list);
+ list_add_tail(&ms->list, list);
+ }
+ }
+ return 0;
+}
+
+int typec_mode_selection_start(struct typec_partner *partner,
+ const unsigned int delay, const unsigned int timeout)
+{
+ struct mode_selection *sel;
+ int ret;
+
+ if (partner->usb_mode == USB_MODE_USB4)
+ return -EBUSY;
+
+ if (partner->sel)
+ return -EALREADY;
+
+ sel = kzalloc_obj(*sel);
+ if (!sel)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&sel->mode_list);
+
+ ret = device_for_each_child(&partner->dev, &sel->mode_list,
+ altmode_add_to_list);
+
+ if (ret || list_empty(&sel->mode_list)) {
+ mode_list_clean(sel);
+ kfree(sel);
+ return ret;
+ }
+
+ list_sort(NULL, &sel->mode_list, compare_priorities);
+ sel->partner = partner;
+ sel->delay = delay;
+ sel->timeout = timeout;
+ mutex_init(&sel->lock);
+ INIT_DELAYED_WORK(&sel->work, mode_selection_work_fn);
+ schedule_delayed_work(&sel->work, msecs_to_jiffies(delay));
+ partner->sel = sel;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(typec_mode_selection_start);
+
+void typec_mode_selection_delete(struct typec_partner *partner)
+{
+ struct mode_selection *sel = partner->sel;
+
+ if (sel) {
+ partner->sel = NULL;
+ cancel_delayed_work_sync(&sel->work);
+ mode_list_clean(sel);
+ mutex_destroy(&sel->lock);
+ kfree(sel);
+ }
+}
+EXPORT_SYMBOL_GPL(typec_mode_selection_delete);
diff --git a/drivers/usb/typec/mux.c b/drivers/usb/typec/mux.c
index 49926d6e72c7..db5e4a4c0a99 100644
--- a/drivers/usb/typec/mux.c
+++ b/drivers/usb/typec/mux.c
@@ -35,7 +35,9 @@ static int switch_fwnode_match(struct device *dev, const void *fwnode)
static void *typec_switch_match(const struct fwnode_handle *fwnode,
const char *id, void *data)
{
+ struct typec_switch_dev **sw_devs = data;
struct device *dev;
+ int i;
/*
* Device graph (OF graph) does not give any means to identify the
@@ -56,6 +58,13 @@ static void *typec_switch_match(const struct fwnode_handle *fwnode,
dev = class_find_device(&typec_mux_class, NULL, fwnode,
switch_fwnode_match);
+ /* Skip duplicates */
+ for (i = 0; i < TYPEC_MUX_MAX_DEVS; i++)
+ if (to_typec_switch_dev(dev) == sw_devs[i]) {
+ put_device(dev);
+ return NULL;
+ }
+
return dev ? to_typec_switch_dev(dev) : ERR_PTR(-EPROBE_DEFER);
}
@@ -76,11 +85,12 @@ struct typec_switch *fwnode_typec_switch_get(struct fwnode_handle *fwnode)
int err;
int i;
- sw = kzalloc(sizeof(*sw), GFP_KERNEL);
+ sw = kzalloc_obj(*sw);
if (!sw)
return ERR_PTR(-ENOMEM);
- count = fwnode_connection_find_matches(fwnode, "orientation-switch", NULL,
+ count = fwnode_connection_find_matches(fwnode, "orientation-switch",
+ (void **)sw_devs,
typec_switch_match,
(void **)sw_devs,
ARRAY_SIZE(sw_devs));
@@ -171,7 +181,7 @@ typec_switch_register(struct device *parent,
if (!desc || !desc->set)
return ERR_PTR(-EINVAL);
- sw_dev = kzalloc(sizeof(*sw_dev), GFP_KERNEL);
+ sw_dev = kzalloc_obj(*sw_dev);
if (!sw_dev)
return ERR_PTR(-ENOMEM);
@@ -214,7 +224,7 @@ int typec_switch_set(struct typec_switch *sw,
sw_dev = sw->sw_devs[i];
ret = sw_dev->set(sw_dev, orientation);
- if (ret)
+ if (ret && ret != -EOPNOTSUPP)
return ret;
}
@@ -265,7 +275,9 @@ static int mux_fwnode_match(struct device *dev, const void *fwnode)
static void *typec_mux_match(const struct fwnode_handle *fwnode,
const char *id, void *data)
{
+ struct typec_mux_dev **mux_devs = data;
struct device *dev;
+ int i;
/*
* Device graph (OF graph) does not give any means to identify the
@@ -281,6 +293,14 @@ static void *typec_mux_match(const struct fwnode_handle *fwnode,
dev = class_find_device(&typec_mux_class, NULL, fwnode,
mux_fwnode_match);
+ /* Skip duplicates */
+ for (i = 0; i < TYPEC_MUX_MAX_DEVS; i++)
+ if (to_typec_mux_dev(dev) == mux_devs[i]) {
+ put_device(dev);
+ return NULL;
+ }
+
+
return dev ? to_typec_mux_dev(dev) : ERR_PTR(-EPROBE_DEFER);
}
@@ -301,12 +321,13 @@ struct typec_mux *fwnode_typec_mux_get(struct fwnode_handle *fwnode)
int err;
int i;
- mux = kzalloc(sizeof(*mux), GFP_KERNEL);
+ mux = kzalloc_obj(*mux);
if (!mux)
return ERR_PTR(-ENOMEM);
count = fwnode_connection_find_matches(fwnode, "mode-switch",
- NULL, typec_mux_match,
+ (void **)mux_devs,
+ typec_mux_match,
(void **)mux_devs,
ARRAY_SIZE(mux_devs));
if (count <= 0) {
@@ -378,7 +399,7 @@ int typec_mux_set(struct typec_mux *mux, struct typec_mux_state *state)
mux_dev = mux->mux_devs[i];
ret = mux_dev->set(mux_dev, state);
- if (ret)
+ if (ret && ret != -EOPNOTSUPP)
return ret;
}
@@ -415,7 +436,7 @@ typec_mux_register(struct device *parent, const struct typec_mux_desc *desc)
if (!desc || !desc->set)
return ERR_PTR(-EINVAL);
- mux_dev = kzalloc(sizeof(*mux_dev), GFP_KERNEL);
+ mux_dev = kzalloc_obj(*mux_dev);
if (!mux_dev)
return ERR_PTR(-ENOMEM);
diff --git a/drivers/usb/typec/mux/Kconfig b/drivers/usb/typec/mux/Kconfig
index 67381b4ef4f6..6dd8f961b593 100644
--- a/drivers/usb/typec/mux/Kconfig
+++ b/drivers/usb/typec/mux/Kconfig
@@ -56,6 +56,16 @@ config TYPEC_MUX_NB7VPQ904M
Say Y or M if your system has a On Semiconductor NB7VPQ904M Type-C
redriver chip found on some devices with a Type-C port.
+config TYPEC_MUX_PS883X
+ tristate "Parade PS883x Type-C retimer driver"
+ depends on I2C
+ depends on DRM || DRM=n
+ select DRM_AUX_BRIDGE if DRM_BRIDGE && OF
+ select REGMAP_I2C
+ help
+ Say Y or M if your system has a Parade PS883x Type-C retimer chip
+ found on some devices with a Type-C port.
+
config TYPEC_MUX_PTN36502
tristate "NXP PTN36502 Type-C redriver driver"
depends on I2C
diff --git a/drivers/usb/typec/mux/Makefile b/drivers/usb/typec/mux/Makefile
index 60879446da93..b4f599eb5053 100644
--- a/drivers/usb/typec/mux/Makefile
+++ b/drivers/usb/typec/mux/Makefile
@@ -6,6 +6,7 @@ obj-$(CONFIG_TYPEC_MUX_PI3USB30532) += pi3usb30532.o
obj-$(CONFIG_TYPEC_MUX_INTEL_PMC) += intel_pmc_mux.o
obj-$(CONFIG_TYPEC_MUX_IT5205) += it5205.o
obj-$(CONFIG_TYPEC_MUX_NB7VPQ904M) += nb7vpq904m.o
+obj-$(CONFIG_TYPEC_MUX_PS883X) += ps883x.o
obj-$(CONFIG_TYPEC_MUX_PTN36502) += ptn36502.o
obj-$(CONFIG_TYPEC_MUX_TUSB1046) += tusb1046.o
obj-$(CONFIG_TYPEC_MUX_WCD939X_USBSS) += wcd939x-usbss.o
diff --git a/drivers/usb/typec/mux/fsa4480.c b/drivers/usb/typec/mux/fsa4480.c
index f71dba8bf07c..c54e42c7e6a1 100644
--- a/drivers/usb/typec/mux/fsa4480.c
+++ b/drivers/usb/typec/mux/fsa4480.c
@@ -12,6 +12,7 @@
#include <linux/regmap.h>
#include <linux/usb/typec_dp.h>
#include <linux/usb/typec_mux.h>
+#include <linux/regulator/consumer.h>
#define FSA4480_DEVICE_ID 0x00
#define FSA4480_DEVICE_ID_VENDOR_ID GENMASK(7, 6)
@@ -273,6 +274,10 @@ static int fsa4480_probe(struct i2c_client *client)
if (IS_ERR(fsa->regmap))
return dev_err_probe(dev, PTR_ERR(fsa->regmap), "failed to initialize regmap\n");
+ ret = devm_regulator_get_enable_optional(dev, "vcc");
+ if (ret && ret != -ENODEV)
+ return dev_err_probe(dev, ret, "Failed to get regulator\n");
+
ret = regmap_read(fsa->regmap, FSA4480_DEVICE_ID, &val);
if (ret)
return dev_err_probe(dev, -ENODEV, "FSA4480 not found\n");
diff --git a/drivers/usb/typec/mux/intel_pmc_mux.c b/drivers/usb/typec/mux/intel_pmc_mux.c
index 65dda9183e6f..1698428654ab 100644
--- a/drivers/usb/typec/mux/intel_pmc_mux.c
+++ b/drivers/usb/typec/mux/intel_pmc_mux.c
@@ -754,7 +754,7 @@ static int pmc_usb_probe(struct platform_device *pdev)
pmc->ipc = devm_intel_scu_ipc_dev_get(&pdev->dev);
if (!pmc->ipc)
- return -ENODEV;
+ return -EPROBE_DEFER;
pmc->dev = &pdev->dev;
diff --git a/drivers/usb/typec/mux/ps883x.c b/drivers/usb/typec/mux/ps883x.c
new file mode 100644
index 000000000000..1256252eceed
--- /dev/null
+++ b/drivers/usb/typec/mux/ps883x.c
@@ -0,0 +1,500 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Parade ps883x usb retimer driver
+ *
+ * Copyright (C) 2024 Linaro Ltd.
+ */
+
+#include <drm/bridge/aux-bridge.h>
+#include <linux/clk.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/usb/pd.h>
+#include <linux/usb/typec_altmode.h>
+#include <linux/usb/typec_dp.h>
+#include <linux/usb/typec_mux.h>
+#include <linux/usb/typec_retimer.h>
+#include <linux/usb/typec_tbt.h>
+
+#define REG_USB_PORT_CONN_STATUS_0 0x00
+
+#define CONN_STATUS_0_CONNECTION_PRESENT BIT(0)
+#define CONN_STATUS_0_ORIENTATION_REVERSED BIT(1)
+#define CONN_STATUS_0_ACTIVE_CABLE BIT(2)
+#define CONN_STATUS_0_USB_3_1_CONNECTED BIT(5)
+
+#define REG_USB_PORT_CONN_STATUS_1 0x01
+
+#define CONN_STATUS_1_DP_CONNECTED BIT(0)
+#define CONN_STATUS_1_DP_SINK_REQUESTED BIT(1)
+#define CONN_STATUS_1_DP_PIN_ASSIGNMENT_C_D BIT(2)
+#define CONN_STATUS_1_DP_HPD_LEVEL BIT(7)
+
+#define REG_USB_PORT_CONN_STATUS_2 0x02
+
+#define CONN_STATUS_2_TBT_CONNECTED BIT(0)
+#define CONN_STATUS_2_TBT_UNIDIR_LSRX_ACT_LT BIT(4)
+#define CONN_STATUS_2_USB4_CONNECTED BIT(7)
+
+struct ps883x_retimer {
+ struct i2c_client *client;
+ struct gpio_desc *reset_gpio;
+ struct regmap *regmap;
+ struct typec_switch_dev *sw;
+ struct typec_retimer *retimer;
+ struct clk *xo_clk;
+ struct regulator *vdd_supply;
+ struct regulator *vdd33_supply;
+ struct regulator *vdd33_cap_supply;
+ struct regulator *vddat_supply;
+ struct regulator *vddar_supply;
+ struct regulator *vddio_supply;
+
+ struct typec_switch *typec_switch;
+ struct typec_mux *typec_mux;
+
+ struct mutex lock; /* protect non-concurrent retimer & switch */
+
+ enum typec_orientation orientation;
+ u8 cfg0;
+ u8 cfg1;
+ u8 cfg2;
+};
+
+static int ps883x_configure(struct ps883x_retimer *retimer, int cfg0,
+ int cfg1, int cfg2)
+{
+ struct device *dev = &retimer->client->dev;
+ int ret;
+
+ if (retimer->cfg0 == cfg0 && retimer->cfg1 == cfg1 && retimer->cfg2 == cfg2)
+ return 0;
+
+ ret = regmap_write(retimer->regmap, REG_USB_PORT_CONN_STATUS_0, cfg0);
+ if (ret) {
+ dev_err(dev, "failed to write conn_status_0: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_write(retimer->regmap, REG_USB_PORT_CONN_STATUS_1, cfg1);
+ if (ret) {
+ dev_err(dev, "failed to write conn_status_1: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_write(retimer->regmap, REG_USB_PORT_CONN_STATUS_2, cfg2);
+ if (ret) {
+ dev_err(dev, "failed to write conn_status_2: %d\n", ret);
+ return ret;
+ }
+
+ retimer->cfg0 = cfg0;
+ retimer->cfg1 = cfg1;
+ retimer->cfg2 = cfg2;
+
+ return 0;
+}
+
+static int ps883x_set(struct ps883x_retimer *retimer, struct typec_retimer_state *state)
+{
+ struct typec_thunderbolt_data *tb_data;
+ const struct enter_usb_data *eudo_data;
+ int cfg0 = CONN_STATUS_0_CONNECTION_PRESENT;
+ int cfg1 = 0x00;
+ int cfg2 = 0x00;
+
+ if (retimer->orientation == TYPEC_ORIENTATION_REVERSE)
+ cfg0 |= CONN_STATUS_0_ORIENTATION_REVERSED;
+
+ if (state->alt) {
+ switch (state->alt->svid) {
+ case USB_TYPEC_DP_SID:
+ cfg1 |= CONN_STATUS_1_DP_CONNECTED |
+ CONN_STATUS_1_DP_HPD_LEVEL;
+
+ switch (state->mode) {
+ case TYPEC_DP_STATE_C:
+ cfg1 |= CONN_STATUS_1_DP_SINK_REQUESTED |
+ CONN_STATUS_1_DP_PIN_ASSIGNMENT_C_D;
+ fallthrough;
+ case TYPEC_DP_STATE_D:
+ cfg1 |= CONN_STATUS_0_USB_3_1_CONNECTED;
+ break;
+ default: /* MODE_E */
+ break;
+ }
+ break;
+ case USB_TYPEC_TBT_SID:
+ tb_data = state->data;
+
+ /* Unconditional */
+ cfg2 |= CONN_STATUS_2_TBT_CONNECTED;
+
+ if (tb_data->cable_mode & TBT_CABLE_ACTIVE_PASSIVE)
+ cfg0 |= CONN_STATUS_0_ACTIVE_CABLE;
+
+ if (tb_data->enter_vdo & TBT_ENTER_MODE_UNI_DIR_LSRX)
+ cfg2 |= CONN_STATUS_2_TBT_UNIDIR_LSRX_ACT_LT;
+ break;
+ default:
+ dev_err(&retimer->client->dev, "Got unsupported SID: 0x%x\n",
+ state->alt->svid);
+ return -EOPNOTSUPP;
+ }
+ } else {
+ switch (state->mode) {
+ case TYPEC_STATE_SAFE:
+ /* USB2 pins don't even go through this chip */
+ case TYPEC_MODE_USB2:
+ break;
+ case TYPEC_STATE_USB:
+ case TYPEC_MODE_USB3:
+ cfg0 |= CONN_STATUS_0_USB_3_1_CONNECTED;
+ break;
+ case TYPEC_MODE_USB4:
+ eudo_data = state->data;
+
+ cfg2 |= CONN_STATUS_2_USB4_CONNECTED;
+
+ if (FIELD_GET(EUDO_CABLE_TYPE_MASK, eudo_data->eudo) != EUDO_CABLE_TYPE_PASSIVE)
+ cfg0 |= CONN_STATUS_0_ACTIVE_CABLE;
+ break;
+ default:
+ dev_err(&retimer->client->dev, "Got unsupported mode: %lu\n",
+ state->mode);
+ return -EOPNOTSUPP;
+ }
+ }
+
+ return ps883x_configure(retimer, cfg0, cfg1, cfg2);
+}
+
+static int ps883x_sw_set(struct typec_switch_dev *sw,
+ enum typec_orientation orientation)
+{
+ struct ps883x_retimer *retimer = typec_switch_get_drvdata(sw);
+ int ret = 0;
+
+ ret = typec_switch_set(retimer->typec_switch, orientation);
+ if (ret)
+ return ret;
+
+ mutex_lock(&retimer->lock);
+
+ if (retimer->orientation != orientation) {
+ retimer->orientation = orientation;
+
+ ret = regmap_assign_bits(retimer->regmap, REG_USB_PORT_CONN_STATUS_0,
+ CONN_STATUS_0_ORIENTATION_REVERSED,
+ orientation == TYPEC_ORIENTATION_REVERSE);
+ if (ret)
+ dev_err(&retimer->client->dev, "failed to set orientation: %d\n", ret);
+ }
+
+ mutex_unlock(&retimer->lock);
+
+ return ret;
+}
+
+static int ps883x_retimer_set(struct typec_retimer *rtmr,
+ struct typec_retimer_state *state)
+{
+ struct ps883x_retimer *retimer = typec_retimer_get_drvdata(rtmr);
+ struct typec_mux_state mux_state;
+ int ret = 0;
+
+ mutex_lock(&retimer->lock);
+ ret = ps883x_set(retimer, state);
+ mutex_unlock(&retimer->lock);
+
+ if (ret)
+ return ret;
+
+ mux_state.alt = state->alt;
+ mux_state.data = state->data;
+ mux_state.mode = state->mode;
+
+ return typec_mux_set(retimer->typec_mux, &mux_state);
+}
+
+static int ps883x_enable_vregs(struct ps883x_retimer *retimer)
+{
+ struct device *dev = &retimer->client->dev;
+ int ret;
+
+ ret = regulator_enable(retimer->vdd33_supply);
+ if (ret) {
+ dev_err(dev, "cannot enable VDD 3.3V regulator: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_enable(retimer->vdd33_cap_supply);
+ if (ret) {
+ dev_err(dev, "cannot enable VDD 3.3V CAP regulator: %d\n", ret);
+ goto err_vdd33_disable;
+ }
+
+ usleep_range(4000, 10000);
+
+ ret = regulator_enable(retimer->vdd_supply);
+ if (ret) {
+ dev_err(dev, "cannot enable VDD regulator: %d\n", ret);
+ goto err_vdd33_cap_disable;
+ }
+
+ ret = regulator_enable(retimer->vddar_supply);
+ if (ret) {
+ dev_err(dev, "cannot enable VDD AR regulator: %d\n", ret);
+ goto err_vdd_disable;
+ }
+
+ ret = regulator_enable(retimer->vddat_supply);
+ if (ret) {
+ dev_err(dev, "cannot enable VDD AT regulator: %d\n", ret);
+ goto err_vddar_disable;
+ }
+
+ ret = regulator_enable(retimer->vddio_supply);
+ if (ret) {
+ dev_err(dev, "cannot enable VDD IO regulator: %d\n", ret);
+ goto err_vddat_disable;
+ }
+
+ return 0;
+
+err_vddat_disable:
+ regulator_disable(retimer->vddat_supply);
+err_vddar_disable:
+ regulator_disable(retimer->vddar_supply);
+err_vdd_disable:
+ regulator_disable(retimer->vdd_supply);
+err_vdd33_cap_disable:
+ regulator_disable(retimer->vdd33_cap_supply);
+err_vdd33_disable:
+ regulator_disable(retimer->vdd33_supply);
+
+ return ret;
+}
+
+static void ps883x_disable_vregs(struct ps883x_retimer *retimer)
+{
+ regulator_disable(retimer->vddio_supply);
+ regulator_disable(retimer->vddat_supply);
+ regulator_disable(retimer->vddar_supply);
+ regulator_disable(retimer->vdd_supply);
+ regulator_disable(retimer->vdd33_cap_supply);
+ regulator_disable(retimer->vdd33_supply);
+}
+
+static int ps883x_get_vregs(struct ps883x_retimer *retimer)
+{
+ struct device *dev = &retimer->client->dev;
+
+ retimer->vdd_supply = devm_regulator_get(dev, "vdd");
+ if (IS_ERR(retimer->vdd_supply))
+ return dev_err_probe(dev, PTR_ERR(retimer->vdd_supply),
+ "failed to get VDD\n");
+
+ retimer->vdd33_supply = devm_regulator_get(dev, "vdd33");
+ if (IS_ERR(retimer->vdd33_supply))
+ return dev_err_probe(dev, PTR_ERR(retimer->vdd33_supply),
+ "failed to get VDD 3.3V\n");
+
+ retimer->vdd33_cap_supply = devm_regulator_get(dev, "vdd33-cap");
+ if (IS_ERR(retimer->vdd33_cap_supply))
+ return dev_err_probe(dev, PTR_ERR(retimer->vdd33_cap_supply),
+ "failed to get VDD CAP 3.3V\n");
+
+ retimer->vddat_supply = devm_regulator_get(dev, "vddat");
+ if (IS_ERR(retimer->vddat_supply))
+ return dev_err_probe(dev, PTR_ERR(retimer->vddat_supply),
+ "failed to get VDD AT\n");
+
+ retimer->vddar_supply = devm_regulator_get(dev, "vddar");
+ if (IS_ERR(retimer->vddar_supply))
+ return dev_err_probe(dev, PTR_ERR(retimer->vddar_supply),
+ "failed to get VDD AR\n");
+
+ retimer->vddio_supply = devm_regulator_get(dev, "vddio");
+ if (IS_ERR(retimer->vddio_supply))
+ return dev_err_probe(dev, PTR_ERR(retimer->vddio_supply),
+ "failed to get VDD IO\n");
+
+ return 0;
+}
+
+static const struct regmap_config ps883x_retimer_regmap = {
+ .max_register = 0x1f,
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+static int ps883x_retimer_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct typec_switch_desc sw_desc = { };
+ struct typec_retimer_desc rtmr_desc = { };
+ struct ps883x_retimer *retimer;
+ unsigned int val;
+ int ret;
+
+ retimer = devm_kzalloc(dev, sizeof(*retimer), GFP_KERNEL);
+ if (!retimer)
+ return -ENOMEM;
+
+ retimer->client = client;
+
+ mutex_init(&retimer->lock);
+
+ retimer->regmap = devm_regmap_init_i2c(client, &ps883x_retimer_regmap);
+ if (IS_ERR(retimer->regmap))
+ return dev_err_probe(dev, PTR_ERR(retimer->regmap),
+ "failed to allocate register map\n");
+
+ ret = ps883x_get_vregs(retimer);
+ if (ret)
+ return ret;
+
+ retimer->xo_clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(retimer->xo_clk))
+ return dev_err_probe(dev, PTR_ERR(retimer->xo_clk),
+ "failed to get xo clock\n");
+
+ retimer->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_ASIS);
+ if (IS_ERR(retimer->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(retimer->reset_gpio),
+ "failed to get reset gpio\n");
+
+ retimer->typec_switch = typec_switch_get(dev);
+ if (IS_ERR(retimer->typec_switch))
+ return dev_err_probe(dev, PTR_ERR(retimer->typec_switch),
+ "failed to acquire orientation-switch\n");
+
+ retimer->typec_mux = typec_mux_get(dev);
+ if (IS_ERR(retimer->typec_mux)) {
+ ret = dev_err_probe(dev, PTR_ERR(retimer->typec_mux),
+ "failed to acquire mode-mux\n");
+ goto err_switch_put;
+ }
+
+ ret = drm_aux_bridge_register(dev);
+ if (ret)
+ goto err_mux_put;
+
+ ret = ps883x_enable_vregs(retimer);
+ if (ret)
+ goto err_mux_put;
+
+ ret = clk_prepare_enable(retimer->xo_clk);
+ if (ret) {
+ dev_err(dev, "failed to enable XO: %d\n", ret);
+ goto err_vregs_disable;
+ }
+
+ /* skip resetting if already configured */
+ if (regmap_test_bits(retimer->regmap, REG_USB_PORT_CONN_STATUS_0,
+ CONN_STATUS_0_CONNECTION_PRESENT) == 1) {
+ gpiod_direction_output(retimer->reset_gpio, 0);
+ } else {
+ gpiod_direction_output(retimer->reset_gpio, 1);
+
+ /* VDD IO supply enable to reset release delay */
+ usleep_range(4000, 14000);
+
+ gpiod_set_value(retimer->reset_gpio, 0);
+
+ /* firmware initialization delay */
+ msleep(60);
+
+ /* make sure device is accessible */
+ ret = regmap_read(retimer->regmap, REG_USB_PORT_CONN_STATUS_0,
+ &val);
+ if (ret) {
+ dev_err(dev, "failed to read conn_status_0: %d\n", ret);
+ if (ret == -ENXIO)
+ ret = -EIO;
+ goto err_clk_disable;
+ }
+ }
+
+ sw_desc.drvdata = retimer;
+ sw_desc.fwnode = dev_fwnode(dev);
+ sw_desc.set = ps883x_sw_set;
+
+ retimer->sw = typec_switch_register(dev, &sw_desc);
+ if (IS_ERR(retimer->sw)) {
+ ret = PTR_ERR(retimer->sw);
+ dev_err(dev, "failed to register typec switch: %d\n", ret);
+ goto err_clk_disable;
+ }
+
+ rtmr_desc.drvdata = retimer;
+ rtmr_desc.fwnode = dev_fwnode(dev);
+ rtmr_desc.set = ps883x_retimer_set;
+
+ retimer->retimer = typec_retimer_register(dev, &rtmr_desc);
+ if (IS_ERR(retimer->retimer)) {
+ ret = PTR_ERR(retimer->retimer);
+ dev_err(dev, "failed to register typec retimer: %d\n", ret);
+ goto err_switch_unregister;
+ }
+
+ i2c_set_clientdata(client, retimer);
+ return 0;
+
+err_switch_unregister:
+ typec_switch_unregister(retimer->sw);
+err_clk_disable:
+ clk_disable_unprepare(retimer->xo_clk);
+err_vregs_disable:
+ gpiod_set_value(retimer->reset_gpio, 1);
+ ps883x_disable_vregs(retimer);
+err_mux_put:
+ typec_mux_put(retimer->typec_mux);
+err_switch_put:
+ typec_switch_put(retimer->typec_switch);
+
+ return ret;
+}
+
+static void ps883x_retimer_remove(struct i2c_client *client)
+{
+ struct ps883x_retimer *retimer = i2c_get_clientdata(client);
+
+ typec_retimer_unregister(retimer->retimer);
+ typec_switch_unregister(retimer->sw);
+
+ gpiod_set_value(retimer->reset_gpio, 1);
+
+ clk_disable_unprepare(retimer->xo_clk);
+
+ ps883x_disable_vregs(retimer);
+
+ typec_mux_put(retimer->typec_mux);
+ typec_switch_put(retimer->typec_switch);
+}
+
+static const struct of_device_id ps883x_retimer_of_table[] = {
+ { .compatible = "parade,ps8830" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ps883x_retimer_of_table);
+
+static struct i2c_driver ps883x_retimer_driver = {
+ .driver = {
+ .name = "ps883x_retimer",
+ .of_match_table = ps883x_retimer_of_table,
+ },
+ .probe = ps883x_retimer_probe,
+ .remove = ps883x_retimer_remove,
+};
+
+module_i2c_driver(ps883x_retimer_driver);
+
+MODULE_DESCRIPTION("Parade ps883x Type-C Retimer driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/typec/mux/tusb1046.c b/drivers/usb/typec/mux/tusb1046.c
index b4f45c217b59..3c1a4551c2fb 100644
--- a/drivers/usb/typec/mux/tusb1046.c
+++ b/drivers/usb/typec/mux/tusb1046.c
@@ -129,7 +129,7 @@ static int tusb1046_i2c_probe(struct i2c_client *client)
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
- return dev_err_probe(dev, -ENOMEM, "failed to allocate driver data\n");
+ return -ENOMEM;
priv->client = client;
diff --git a/drivers/usb/typec/pd.c b/drivers/usb/typec/pd.c
index d78c04a421bc..766c76d63328 100644
--- a/drivers/usb/typec/pd.c
+++ b/drivers/usb/typec/pd.c
@@ -360,6 +360,84 @@ static const struct device_type sink_pps_type = {
};
/* -------------------------------------------------------------------------- */
+/* Standard Power Range (SPR) Adjustable Voltage Supply (AVS) */
+
+static ssize_t
+spr_avs_9v_to_15v_max_current_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sysfs_emit(buf, "%umA\n",
+ pdo_spr_avs_apdo_9v_to_15v_max_current_ma(to_pdo(dev)->pdo));
+}
+
+static ssize_t
+spr_avs_15v_to_20v_max_current_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sysfs_emit(buf, "%umA\n",
+ pdo_spr_avs_apdo_15v_to_20v_max_current_ma(to_pdo(dev)->pdo));
+}
+
+static ssize_t
+spr_avs_src_peak_current_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sysfs_emit(buf, "%u\n",
+ pdo_spr_avs_apdo_src_peak_current(to_pdo(dev)->pdo));
+}
+
+static struct device_attribute spr_avs_9v_to_15v_max_current_attr = {
+ .attr = {
+ .name = "maximum_current_9V_to_15V",
+ .mode = 0444,
+ },
+ .show = spr_avs_9v_to_15v_max_current_show,
+};
+
+static struct device_attribute spr_avs_15v_to_20v_max_current_attr = {
+ .attr = {
+ .name = "maximum_current_15V_to_20V",
+ .mode = 0444,
+ },
+ .show = spr_avs_15v_to_20v_max_current_show,
+};
+
+static struct device_attribute spr_avs_src_peak_current_attr = {
+ .attr = {
+ .name = "peak_current",
+ .mode = 0444,
+ },
+ .show = spr_avs_src_peak_current_show,
+};
+
+static struct attribute *source_spr_avs_attrs[] = {
+ &spr_avs_9v_to_15v_max_current_attr.attr,
+ &spr_avs_15v_to_20v_max_current_attr.attr,
+ &spr_avs_src_peak_current_attr.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(source_spr_avs);
+
+static const struct device_type source_spr_avs_type = {
+ .name = "pdo",
+ .release = pdo_release,
+ .groups = source_spr_avs_groups,
+};
+
+static struct attribute *sink_spr_avs_attrs[] = {
+ &spr_avs_9v_to_15v_max_current_attr.attr,
+ &spr_avs_15v_to_20v_max_current_attr.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(sink_spr_avs);
+
+static const struct device_type sink_spr_avs_type = {
+ .name = "pdo",
+ .release = pdo_release,
+ .groups = sink_spr_avs_groups,
+};
+
+/* -------------------------------------------------------------------------- */
static const char * const supply_name[] = {
[PDO_TYPE_FIXED] = "fixed_supply",
@@ -368,7 +446,8 @@ static const char * const supply_name[] = {
};
static const char * const apdo_supply_name[] = {
- [APDO_TYPE_PPS] = "programmable_supply",
+ [APDO_TYPE_PPS] = "programmable_supply",
+ [APDO_TYPE_SPR_AVS] = "spr_adjustable_voltage_supply",
};
static const struct device_type *source_type[] = {
@@ -378,7 +457,8 @@ static const struct device_type *source_type[] = {
};
static const struct device_type *source_apdo_type[] = {
- [APDO_TYPE_PPS] = &source_pps_type,
+ [APDO_TYPE_PPS] = &source_pps_type,
+ [APDO_TYPE_SPR_AVS] = &source_spr_avs_type,
};
static const struct device_type *sink_type[] = {
@@ -388,7 +468,8 @@ static const struct device_type *sink_type[] = {
};
static const struct device_type *sink_apdo_type[] = {
- [APDO_TYPE_PPS] = &sink_pps_type,
+ [APDO_TYPE_PPS] = &sink_pps_type,
+ [APDO_TYPE_SPR_AVS] = &sink_spr_avs_type,
};
/* REVISIT: Export when EPR_*_Capabilities need to be supported. */
@@ -399,7 +480,7 @@ static int add_pdo(struct usb_power_delivery_capabilities *cap, u32 pdo, int pos
struct pdo *p;
int ret;
- p = kzalloc(sizeof(*p), GFP_KERNEL);
+ p = kzalloc_obj(*p);
if (!p)
return -ENOMEM;
@@ -407,8 +488,12 @@ static int add_pdo(struct usb_power_delivery_capabilities *cap, u32 pdo, int pos
p->object_position = position;
if (pdo_type(pdo) == PDO_TYPE_APDO) {
- /* FIXME: Only PPS supported for now! Skipping others. */
- if (pdo_apdo_type(pdo) > APDO_TYPE_PPS) {
+ /*
+ * FIXME: Only PPS, SPR_AVS supported for now!
+ * Skipping others.
+ */
+ if (pdo_apdo_type(pdo) != APDO_TYPE_PPS &&
+ pdo_apdo_type(pdo) != APDO_TYPE_SPR_AVS) {
dev_warn(&cap->dev, "Unknown APDO type. PDO 0x%08x\n", pdo);
kfree(p);
return 0;
@@ -484,7 +569,7 @@ usb_power_delivery_register_capabilities(struct usb_power_delivery *pd,
int ret;
int i;
- cap = kzalloc(sizeof(*cap), GFP_KERNEL);
+ cap = kzalloc_obj(*cap);
if (!cap)
return ERR_PTR(-ENOMEM);
@@ -612,7 +697,7 @@ usb_power_delivery_register(struct device *parent, struct usb_power_delivery_des
struct usb_power_delivery *pd;
int ret;
- pd = kzalloc(sizeof(*pd), GFP_KERNEL);
+ pd = kzalloc_obj(*pd);
if (!pd)
return ERR_PTR(-ENOMEM);
diff --git a/drivers/usb/typec/port-mapper.c b/drivers/usb/typec/port-mapper.c
index d42da5720a25..cdbb7c11d714 100644
--- a/drivers/usb/typec/port-mapper.c
+++ b/drivers/usb/typec/port-mapper.c
@@ -8,6 +8,7 @@
#include <linux/acpi.h>
#include <linux/component.h>
+#include <linux/thunderbolt.h>
#include <linux/usb.h>
#include "class.h"
@@ -36,6 +37,11 @@ struct each_port_arg {
struct component_match *match;
};
+static int usb4_port_compare(struct device *dev, void *fwnode)
+{
+ return usb4_usb3_port_match(dev, fwnode);
+}
+
static int typec_port_compare(struct device *dev, void *fwnode)
{
return device_match_fwnode(dev, fwnode);
@@ -51,9 +57,22 @@ static int typec_port_match(struct device *dev, void *data)
if (con_adev == adev)
return 0;
- if (con_adev->pld_crc == adev->pld_crc)
+ if (con_adev->pld_crc == adev->pld_crc) {
+ struct fwnode_handle *adev_fwnode = acpi_fwnode_handle(adev);
+
component_match_add(&arg->port->dev, &arg->match, typec_port_compare,
- acpi_fwnode_handle(adev));
+ adev_fwnode);
+
+ /*
+ * If dev is USB 3.x port, it may have reference to the
+ * USB4 host interface in which case we can also link the
+ * Type-C port with the USB4 port.
+ */
+ if (fwnode_property_present(adev_fwnode, "usb4-host-interface"))
+ component_match_add(&arg->port->dev, &arg->match,
+ usb4_port_compare, adev_fwnode);
+ }
+
return 0;
}
diff --git a/drivers/usb/typec/retimer.c b/drivers/usb/typec/retimer.c
index b519fcf358ca..bfd80292b22a 100644
--- a/drivers/usb/typec/retimer.c
+++ b/drivers/usb/typec/retimer.c
@@ -110,7 +110,7 @@ typec_retimer_register(struct device *parent, const struct typec_retimer_desc *d
if (!desc || !desc->set)
return ERR_PTR(-EINVAL);
- retimer = kzalloc(sizeof(*retimer), GFP_KERNEL);
+ retimer = kzalloc_obj(*retimer);
if (!retimer)
return ERR_PTR(-ENOMEM);
diff --git a/drivers/usb/typec/tcpm/Kconfig b/drivers/usb/typec/tcpm/Kconfig
index 8cdd84ca5d6f..00baa7503d45 100644
--- a/drivers/usb/typec/tcpm/Kconfig
+++ b/drivers/usb/typec/tcpm/Kconfig
@@ -58,6 +58,8 @@ config TYPEC_FUSB302
tristate "Fairchild FUSB302 Type-C chip driver"
depends on I2C
depends on EXTCON || !EXTCON
+ depends on DRM || DRM=n
+ select DRM_AUX_HPD_BRIDGE if DRM_BRIDGE && OF
help
The Fairchild FUSB302 Type-C chip driver that works with
Type-C Port Controller Manager to provide USB PD and USB
diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c
index f15c63d3a8f4..889c4c29c1b8 100644
--- a/drivers/usb/typec/tcpm/fusb302.c
+++ b/drivers/usb/typec/tcpm/fusb302.c
@@ -5,6 +5,7 @@
* Fairchild FUSB302 Type-C Chip Driver
*/
+#include <drm/bridge/aux-bridge.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/errno.h>
@@ -104,6 +105,7 @@ struct fusb302_chip {
bool vconn_on;
bool vbus_on;
bool charge_on;
+ bool pd_rx_on;
bool vbus_present;
enum typec_cc_polarity cc_polarity;
enum typec_cc_status cc1;
@@ -841,6 +843,11 @@ static int tcpm_set_pd_rx(struct tcpc_dev *dev, bool on)
int ret = 0;
mutex_lock(&chip->lock);
+ if (chip->pd_rx_on == on) {
+ fusb302_log(chip, "pd is already %s", str_on_off(on));
+ goto done;
+ }
+
ret = fusb302_pd_rx_flush(chip);
if (ret < 0) {
fusb302_log(chip, "cannot flush pd rx buffer, ret=%d", ret);
@@ -863,6 +870,8 @@ static int tcpm_set_pd_rx(struct tcpc_dev *dev, bool on)
str_on_off(on), ret);
goto done;
}
+
+ chip->pd_rx_on = on;
fusb302_log(chip, "pd := %s", str_on_off(on));
done:
mutex_unlock(&chip->lock);
@@ -1681,6 +1690,7 @@ static int fusb302_probe(struct i2c_client *client)
{
struct fusb302_chip *chip;
struct i2c_adapter *adapter = client->adapter;
+ struct auxiliary_device *bridge_dev;
struct device *dev = &client->dev;
const char *name;
int ret = 0;
@@ -1739,6 +1749,13 @@ static int fusb302_probe(struct i2c_client *client)
goto destroy_workqueue;
}
+ bridge_dev = devm_drm_dp_hpd_bridge_alloc(chip->dev, to_of_node(chip->tcpc_dev.fwnode));
+ if (IS_ERR(bridge_dev)) {
+ ret = PTR_ERR(bridge_dev);
+ dev_err_probe(chip->dev, ret, "failed to alloc bridge\n");
+ goto destroy_workqueue;
+ }
+
chip->tcpm_port = tcpm_register_port(&client->dev, &chip->tcpc_dev);
if (IS_ERR(chip->tcpm_port)) {
fwnode_handle_put(chip->tcpc_dev.fwnode);
@@ -1747,9 +1764,9 @@ static int fusb302_probe(struct i2c_client *client)
goto destroy_workqueue;
}
- ret = request_irq(chip->gpio_int_n_irq, fusb302_irq_intn,
- IRQF_ONESHOT | IRQF_TRIGGER_LOW,
- "fsc_interrupt_int_n", chip);
+ ret = request_threaded_irq(chip->gpio_int_n_irq, NULL, fusb302_irq_intn,
+ IRQF_ONESHOT | IRQF_TRIGGER_LOW,
+ "fsc_interrupt_int_n", chip);
if (ret < 0) {
dev_err(dev, "cannot request IRQ for GPIO Int_N, ret=%d", ret);
goto tcpm_unregister_port;
@@ -1757,6 +1774,10 @@ static int fusb302_probe(struct i2c_client *client)
enable_irq_wake(chip->gpio_int_n_irq);
i2c_set_clientdata(client, chip);
+ ret = devm_drm_dp_hpd_bridge_add(chip->dev, bridge_dev);
+ if (ret)
+ return ret;
+
return ret;
tcpm_unregister_port:
diff --git a/drivers/usb/typec/tcpm/maxim_contaminant.c b/drivers/usb/typec/tcpm/maxim_contaminant.c
index 0cdda06592fd..af8da6dc60ae 100644
--- a/drivers/usb/typec/tcpm/maxim_contaminant.c
+++ b/drivers/usb/typec/tcpm/maxim_contaminant.c
@@ -188,6 +188,11 @@ static int max_contaminant_read_comparators(struct max_tcpci_chip *chip, u8 *ven
if (ret < 0)
return ret;
+ /* Disable low power mode */
+ ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, CCLPMODESEL,
+ FIELD_PREP(CCLPMODESEL,
+ LOW_POWER_MODE_DISABLE));
+
/* Sleep to allow comparators settle */
usleep_range(5000, 6000);
ret = regmap_update_bits(regmap, TCPC_TCPC_CTRL, TCPC_TCPC_CTRL_ORIENTATION, PLUG_ORNT_CC1);
@@ -324,6 +329,39 @@ static int max_contaminant_enable_dry_detection(struct max_tcpci_chip *chip)
return 0;
}
+static int max_contaminant_enable_toggling(struct max_tcpci_chip *chip)
+{
+ struct regmap *regmap = chip->data.regmap;
+ int ret;
+
+ /* Disable dry detection if enabled. */
+ ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, CCLPMODESEL,
+ FIELD_PREP(CCLPMODESEL,
+ LOW_POWER_MODE_DISABLE));
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL1, CCCONNDRY, 0);
+ if (ret)
+ return ret;
+
+ ret = max_tcpci_write8(chip, TCPC_ROLE_CTRL, TCPC_ROLE_CTRL_DRP |
+ FIELD_PREP(TCPC_ROLE_CTRL_CC1,
+ TCPC_ROLE_CTRL_CC_RD) |
+ FIELD_PREP(TCPC_ROLE_CTRL_CC2,
+ TCPC_ROLE_CTRL_CC_RD));
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(regmap, TCPC_TCPC_CTRL,
+ TCPC_TCPC_CTRL_EN_LK4CONN_ALRT,
+ TCPC_TCPC_CTRL_EN_LK4CONN_ALRT);
+ if (ret)
+ return ret;
+
+ return max_tcpci_write8(chip, TCPC_COMMAND, TCPC_CMD_LOOK4CONNECTION);
+}
+
bool max_contaminant_is_contaminant(struct max_tcpci_chip *chip, bool disconnect_while_debounce,
bool *cc_handled)
{
@@ -340,6 +378,12 @@ bool max_contaminant_is_contaminant(struct max_tcpci_chip *chip, bool disconnect
if (ret < 0)
return false;
+ if (cc_status & TCPC_CC_STATUS_TOGGLING) {
+ if (chip->contaminant_state == DETECTED)
+ return true;
+ return false;
+ }
+
if (chip->contaminant_state == NOT_DETECTED || chip->contaminant_state == SINK) {
if (!disconnect_while_debounce)
msleep(100);
@@ -372,6 +416,12 @@ bool max_contaminant_is_contaminant(struct max_tcpci_chip *chip, bool disconnect
max_contaminant_enable_dry_detection(chip);
return true;
}
+
+ ret = max_contaminant_enable_toggling(chip);
+ if (ret)
+ dev_err(chip->dev,
+ "Failed to enable toggling, ret=%d",
+ ret);
}
} else if (chip->contaminant_state == DETECTED) {
if (!(cc_status & TCPC_CC_STATUS_TOGGLING)) {
@@ -379,6 +429,14 @@ bool max_contaminant_is_contaminant(struct max_tcpci_chip *chip, bool disconnect
if (chip->contaminant_state == DETECTED) {
max_contaminant_enable_dry_detection(chip);
return true;
+ } else {
+ ret = max_contaminant_enable_toggling(chip);
+ if (ret) {
+ dev_err(chip->dev,
+ "Failed to enable toggling, ret=%d",
+ ret);
+ return true;
+ }
}
}
}
diff --git a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c
index 18303b34594b..c8b1463e6e8b 100644
--- a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c
+++ b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c
@@ -567,7 +567,7 @@ int qcom_pmic_typec_pdphy_probe(struct platform_device *pdev,
if (!res->nr_irqs || res->nr_irqs > PMIC_PDPHY_MAX_IRQS)
return -EINVAL;
- irq_data = devm_kzalloc(dev, sizeof(*irq_data) * res->nr_irqs,
+ irq_data = devm_kcalloc(dev, res->nr_irqs, sizeof(*irq_data),
GFP_KERNEL);
if (!irq_data)
return -ENOMEM;
diff --git a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c
index 4fc83dcfae64..8051eaa46991 100644
--- a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c
+++ b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c
@@ -713,7 +713,7 @@ int qcom_pmic_typec_port_probe(struct platform_device *pdev,
if (!res->nr_irqs || res->nr_irqs > PMIC_TYPEC_MAX_IRQS)
return -EINVAL;
- irq_data = devm_kzalloc(dev, sizeof(*irq_data) * res->nr_irqs,
+ irq_data = devm_kcalloc(dev, res->nr_irqs, sizeof(*irq_data),
GFP_KERNEL);
if (!irq_data)
return -ENOMEM;
diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c
index 19ab6647af70..0148b8f50412 100644
--- a/drivers/usb/typec/tcpm/tcpci.c
+++ b/drivers/usb/typec/tcpm/tcpci.c
@@ -7,6 +7,7 @@
#include <linux/bitfield.h>
#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/i2c.h>
@@ -17,6 +18,7 @@
#include <linux/usb/tcpci.h>
#include <linux/usb/tcpm.h>
#include <linux/usb/typec.h>
+#include <linux/regulator/consumer.h>
#define PD_RETRY_COUNT_DEFAULT 3
#define PD_RETRY_COUNT_3_0_OR_HIGHER 2
@@ -41,6 +43,7 @@ struct tcpci {
struct tcpc_dev tcpc;
struct tcpci_data *data;
+ struct gpio_desc *orientation_gpio;
};
struct tcpci_chip {
@@ -315,6 +318,10 @@ static int tcpci_set_orientation(struct tcpc_dev *tcpc,
struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
unsigned int reg;
+ if (tcpci->orientation_gpio)
+ return gpiod_set_value_cansleep(tcpci->orientation_gpio,
+ orientation != TYPEC_ORIENTATION_NORMAL);
+
switch (orientation) {
case TYPEC_ORIENTATION_NONE:
/* We can't put a single output into high impedance */
@@ -902,9 +909,14 @@ EXPORT_SYMBOL_GPL(tcpci_unregister_port);
static int tcpci_probe(struct i2c_client *client)
{
struct tcpci_chip *chip;
+ struct gpio_desc *orient_gpio = NULL;
int err;
u16 val = 0;
+ err = devm_regulator_get_enable_optional(&client->dev, "vdd");
+ if (err && err != -ENODEV)
+ return dev_err_probe(&client->dev, err, "Failed to get regulator\n");
+
chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
return -ENOMEM;
@@ -926,12 +938,23 @@ static int tcpci_probe(struct i2c_client *client)
if (err < 0)
return err;
+ if (err == 0) {
+ orient_gpio = devm_gpiod_get_optional(&client->dev, "orientation",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(orient_gpio))
+ return dev_err_probe(&client->dev, PTR_ERR(orient_gpio),
+ "unable to acquire orientation gpio\n");
+ err = !!orient_gpio;
+ }
+
chip->data.set_orientation = err;
chip->tcpci = tcpci_register_port(&client->dev, &chip->data);
if (IS_ERR(chip->tcpci))
return PTR_ERR(chip->tcpci);
+ chip->tcpci->orientation_gpio = orient_gpio;
+
err = devm_request_threaded_irq(&client->dev, client->irq, NULL,
_tcpci_irq,
IRQF_SHARED | IRQF_ONESHOT,
@@ -944,6 +967,8 @@ static int tcpci_probe(struct i2c_client *client)
if (err < 0)
goto unregister_port;
+ device_set_wakeup_capable(chip->tcpci->dev, true);
+
return 0;
unregister_port:
@@ -964,6 +989,36 @@ static void tcpci_remove(struct i2c_client *client)
tcpci_unregister_port(chip->tcpci);
}
+static int tcpci_suspend(struct device *dev)
+{
+ struct i2c_client *i2c = to_i2c_client(dev);
+ struct tcpci_chip *chip = i2c_get_clientdata(i2c);
+ int ret;
+
+ if (device_may_wakeup(dev))
+ ret = enable_irq_wake(i2c->irq);
+ else
+ ret = tcpci_write16(chip->tcpci, TCPC_ALERT_MASK, 0);
+
+ return ret;
+}
+
+static int tcpci_resume(struct device *dev)
+{
+ struct i2c_client *i2c = to_i2c_client(dev);
+ struct tcpci_chip *chip = i2c_get_clientdata(i2c);
+ int ret;
+
+ if (device_may_wakeup(dev))
+ ret = disable_irq_wake(i2c->irq);
+ else
+ ret = tcpci_write16(chip->tcpci, TCPC_ALERT_MASK, chip->tcpci->alert_mask);
+
+ return ret;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(tcpci_pm_ops, tcpci_suspend, tcpci_resume);
+
static const struct i2c_device_id tcpci_id[] = {
{ "tcpci" },
{ }
@@ -982,6 +1037,7 @@ MODULE_DEVICE_TABLE(of, tcpci_of_match);
static struct i2c_driver tcpci_i2c_driver = {
.driver = {
.name = "tcpci",
+ .pm = pm_sleep_ptr(&tcpci_pm_ops),
.of_match_table = of_match_ptr(tcpci_of_match),
},
.probe = tcpci_probe,
diff --git a/drivers/usb/typec/tcpm/tcpci_maxim.h b/drivers/usb/typec/tcpm/tcpci_maxim.h
index 76270d5c2838..b314606eb0f6 100644
--- a/drivers/usb/typec/tcpm/tcpci_maxim.h
+++ b/drivers/usb/typec/tcpm/tcpci_maxim.h
@@ -21,6 +21,7 @@
#define CCOVPDIS BIT(6)
#define SBURPCTRL BIT(5)
#define CCLPMODESEL GENMASK(4, 3)
+#define LOW_POWER_MODE_DISABLE 0
#define ULTRA_LOW_POWER_MODE 1
#define CCRPCTRL GENMASK(2, 0)
#define UA_1_SRC 1
@@ -59,6 +60,7 @@ struct max_tcpci_chip {
struct tcpm_port *port;
enum contamiant_state contaminant_state;
bool veto_vconn_swap;
+ struct regulator *vbus_reg;
};
static inline int max_tcpci_read16(struct max_tcpci_chip *chip, unsigned int reg, u16 *val)
diff --git a/drivers/usb/typec/tcpm/tcpci_maxim_core.c b/drivers/usb/typec/tcpm/tcpci_maxim_core.c
index fd1b80593367..c0ee7e6959ed 100644
--- a/drivers/usb/typec/tcpm/tcpci_maxim_core.c
+++ b/drivers/usb/typec/tcpm/tcpci_maxim_core.c
@@ -10,6 +10,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
#include <linux/usb/pd.h>
#include <linux/usb/tcpci.h>
#include <linux/usb/tcpm.h>
@@ -35,12 +36,6 @@
*/
#define TCPC_RECEIVE_BUFFER_LEN 32
-#define MAX_BUCK_BOOST_SID 0x69
-#define MAX_BUCK_BOOST_OP 0xb9
-#define MAX_BUCK_BOOST_OFF 0
-#define MAX_BUCK_BOOST_SOURCE 0xa
-#define MAX_BUCK_BOOST_SINK 0x5
-
static const struct regmap_range max_tcpci_tcpci_range[] = {
regmap_reg_range(0x00, 0x95)
};
@@ -166,7 +161,8 @@ static void process_rx(struct max_tcpci_chip *chip, u16 status)
return;
}
- if (count > sizeof(struct pd_message) || count + 1 > TCPC_RECEIVE_BUFFER_LEN) {
+ if (count > sizeof(struct pd_message) + 1 ||
+ count + 1 > TCPC_RECEIVE_BUFFER_LEN) {
dev_err(chip->dev, "Invalid TCPC_RX_BYTE_CNT %d\n", count);
return;
}
@@ -201,32 +197,49 @@ static void process_rx(struct max_tcpci_chip *chip, u16 status)
tcpm_pd_receive(chip->port, &msg, rx_type);
}
+static int get_vbus_regulator_handle(struct max_tcpci_chip *chip)
+{
+ if (IS_ERR_OR_NULL(chip->vbus_reg)) {
+ chip->vbus_reg = devm_regulator_get_exclusive(chip->dev,
+ "vbus");
+ if (IS_ERR_OR_NULL(chip->vbus_reg)) {
+ dev_err(chip->dev,
+ "Failed to get vbus regulator handle\n");
+ return -ENODEV;
+ }
+ }
+
+ return 0;
+}
+
static int max_tcpci_set_vbus(struct tcpci *tcpci, struct tcpci_data *tdata, bool source, bool sink)
{
struct max_tcpci_chip *chip = tdata_to_max_tcpci(tdata);
- u8 buffer_source[2] = {MAX_BUCK_BOOST_OP, MAX_BUCK_BOOST_SOURCE};
- u8 buffer_sink[2] = {MAX_BUCK_BOOST_OP, MAX_BUCK_BOOST_SINK};
- u8 buffer_none[2] = {MAX_BUCK_BOOST_OP, MAX_BUCK_BOOST_OFF};
- struct i2c_client *i2c = chip->client;
int ret;
- struct i2c_msg msgs[] = {
- {
- .addr = MAX_BUCK_BOOST_SID,
- .flags = i2c->flags & I2C_M_TEN,
- .len = 2,
- .buf = source ? buffer_source : sink ? buffer_sink : buffer_none,
- },
- };
-
if (source && sink) {
dev_err(chip->dev, "Both source and sink set\n");
return -EINVAL;
}
- ret = i2c_transfer(i2c->adapter, msgs, 1);
+ ret = get_vbus_regulator_handle(chip);
+ if (ret) {
+ /*
+ * Regulator is not necessary for sink only applications. Return
+ * success in cases where sink mode is being modified.
+ */
+ return source ? ret : 1;
+ }
- return ret < 0 ? ret : 1;
+ if (source) {
+ if (!regulator_is_enabled(chip->vbus_reg))
+ ret = regulator_enable(chip->vbus_reg);
+ } else {
+ if (regulator_is_enabled(chip->vbus_reg))
+ ret = regulator_disable(chip->vbus_reg);
+ }
+
+ return ret < 0 ? ret : 1;
}
static void process_power_status(struct max_tcpci_chip *chip)
@@ -420,21 +433,6 @@ static irqreturn_t max_tcpci_isr(int irq, void *dev_id)
return IRQ_WAKE_THREAD;
}
-static int max_tcpci_init_alert(struct max_tcpci_chip *chip, struct i2c_client *client)
-{
- int ret;
-
- ret = devm_request_threaded_irq(chip->dev, client->irq, max_tcpci_isr, max_tcpci_irq,
- (IRQF_TRIGGER_LOW | IRQF_ONESHOT), dev_name(chip->dev),
- chip);
-
- if (ret < 0)
- return ret;
-
- enable_irq_wake(client->irq);
- return 0;
-}
-
static int max_tcpci_start_toggling(struct tcpci *tcpci, struct tcpci_data *tdata,
enum typec_cc_status cc)
{
@@ -531,33 +529,64 @@ static int max_tcpci_probe(struct i2c_client *client)
chip->port = tcpci_get_tcpm_port(chip->tcpci);
- ret = max_tcpci_init_alert(chip, client);
+ ret = devm_request_threaded_irq(&client->dev, client->irq, max_tcpci_isr, max_tcpci_irq,
+ (IRQF_TRIGGER_LOW | IRQF_ONESHOT), dev_name(chip->dev),
+ chip);
if (ret < 0)
return dev_err_probe(&client->dev, ret,
"IRQ initialization failed\n");
- device_init_wakeup(chip->dev, true);
+ ret = devm_device_init_wakeup(chip->dev);
+ if (ret)
+ return dev_err_probe(chip->dev, ret, "Failed to init wakeup\n");
+
return 0;
}
+#ifdef CONFIG_PM_SLEEP
+static int max_tcpci_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ int ret = 0;
+
+ if (client->irq && device_may_wakeup(dev))
+ ret = disable_irq_wake(client->irq);
+
+ return ret;
+}
+
+static int max_tcpci_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ int ret = 0;
+
+ if (client->irq && device_may_wakeup(dev))
+ ret = enable_irq_wake(client->irq);
+
+ return ret;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static SIMPLE_DEV_PM_OPS(max_tcpci_pm_ops, max_tcpci_suspend, max_tcpci_resume);
+
static const struct i2c_device_id max_tcpci_id[] = {
{ "maxtcpc" },
{ }
};
MODULE_DEVICE_TABLE(i2c, max_tcpci_id);
-#ifdef CONFIG_OF
static const struct of_device_id max_tcpci_of_match[] = {
{ .compatible = "maxim,max33359", },
{},
};
MODULE_DEVICE_TABLE(of, max_tcpci_of_match);
-#endif
static struct i2c_driver max_tcpci_i2c_driver = {
.driver = {
.name = "maxtcpc",
- .of_match_table = of_match_ptr(max_tcpci_of_match),
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+ .of_match_table = max_tcpci_of_match,
+ .pm = &max_tcpci_pm_ops,
},
.probe = max_tcpci_probe,
.id_table = max_tcpci_id,
diff --git a/drivers/usb/typec/tcpm/tcpci_rt1711h.c b/drivers/usb/typec/tcpm/tcpci_rt1711h.c
index 88c50b984e8a..4b3e4e22a82e 100644
--- a/drivers/usb/typec/tcpm/tcpci_rt1711h.c
+++ b/drivers/usb/typec/tcpm/tcpci_rt1711h.c
@@ -18,11 +18,6 @@
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
-#define RT1711H_VID 0x29CF
-#define RT1711H_PID 0x1711
-#define RT1711H_DID 0x2171
-#define RT1715_DID 0x2173
-
#define RT1711H_PHYCTRL1 0x80
#define RT1711H_PHYCTRL2 0x81
@@ -55,7 +50,6 @@
struct rt1711h_chip_info {
u32 rxdz_sel;
- u16 did;
bool enable_pd30_extended_message;
};
@@ -301,35 +295,6 @@ static int rt1711h_sw_reset(struct rt1711h_chip *chip)
return 0;
}
-static int rt1711h_check_revision(struct i2c_client *i2c, struct rt1711h_chip *chip)
-{
- int ret;
-
- ret = i2c_smbus_read_word_data(i2c, TCPC_VENDOR_ID);
- if (ret < 0)
- return ret;
- if (ret != RT1711H_VID) {
- dev_err(&i2c->dev, "vid is not correct, 0x%04x\n", ret);
- return -ENODEV;
- }
- ret = i2c_smbus_read_word_data(i2c, TCPC_PRODUCT_ID);
- if (ret < 0)
- return ret;
- if (ret != RT1711H_PID) {
- dev_err(&i2c->dev, "pid is not correct, 0x%04x\n", ret);
- return -ENODEV;
- }
- ret = i2c_smbus_read_word_data(i2c, TCPC_BCD_DEV);
- if (ret < 0)
- return ret;
- if (ret != chip->info->did) {
- dev_err(&i2c->dev, "did is not correct, 0x%04x\n", ret);
- return -ENODEV;
- }
- dev_dbg(&i2c->dev, "did is 0x%04x\n", ret);
- return ret;
-}
-
static int rt1711h_probe(struct i2c_client *client)
{
int ret;
@@ -346,12 +311,6 @@ static int rt1711h_probe(struct i2c_client *client)
chip->info = i2c_get_match_data(client);
- ret = rt1711h_check_revision(client, chip);
- if (ret < 0) {
- dev_err(&client->dev, "check vid/pid fail\n");
- return ret;
- }
-
chip->data.regmap = devm_regmap_init_i2c(client,
&rt1711h_regmap_config);
if (IS_ERR(chip->data.regmap))
@@ -406,16 +365,15 @@ static void rt1711h_remove(struct i2c_client *client)
}
static const struct rt1711h_chip_info rt1711h = {
- .did = RT1711H_DID,
};
static const struct rt1711h_chip_info rt1715 = {
.rxdz_sel = RT1711H_BMCIO_RXDZSEL,
- .did = RT1715_DID,
.enable_pd30_extended_message = true,
};
static const struct i2c_device_id rt1711h_id[] = {
+ { "et7304", (kernel_ulong_t)&rt1715 },
{ "rt1711h", (kernel_ulong_t)&rt1711h },
{ "rt1715", (kernel_ulong_t)&rt1715 },
{}
@@ -423,6 +381,7 @@ static const struct i2c_device_id rt1711h_id[] = {
MODULE_DEVICE_TABLE(i2c, rt1711h_id);
static const struct of_device_id rt1711h_of_match[] = {
+ { .compatible = "etekmicro,et7304", .data = &rt1715 },
{ .compatible = "richtek,rt1711h", .data = &rt1711h },
{ .compatible = "richtek,rt1715", .data = &rt1715 },
{}
diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
index 62ca4a0ec55b..55fee96d3342 100644
--- a/drivers/usb/typec/tcpm/tcpm.c
+++ b/drivers/usb/typec/tcpm/tcpm.c
@@ -12,6 +12,7 @@
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/kthread.h>
+#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/power_supply.h>
@@ -61,12 +62,14 @@
S(SNK_WAIT_CAPABILITIES_TIMEOUT), \
S(SNK_NEGOTIATE_CAPABILITIES), \
S(SNK_NEGOTIATE_PPS_CAPABILITIES), \
+ S(SNK_NEGOTIATE_SPR_AVS_CAPABILITIES), \
S(SNK_TRANSITION_SINK), \
S(SNK_TRANSITION_SINK_VBUS), \
S(SNK_READY), \
\
S(ACC_UNATTACHED), \
S(DEBUG_ACC_ATTACHED), \
+ S(DEBUG_ACC_DEBOUNCE), \
S(AUDIO_ACC_ATTACHED), \
S(AUDIO_ACC_DEBOUNCE), \
\
@@ -187,7 +190,8 @@
S(STRUCTURED_VDMS), \
S(COUNTRY_INFO), \
S(COUNTRY_CODES), \
- S(REVISION_INFORMATION)
+ S(REVISION_INFORMATION), \
+ S(GETTING_SINK_EXTENDED_CAPABILITIES)
#define GENERATE_ENUM(e) e
#define GENERATE_STRING(s) #s
@@ -228,6 +232,7 @@ enum pd_msg_request {
PD_MSG_DATA_SINK_CAP,
PD_MSG_DATA_SOURCE_CAP,
PD_MSG_DATA_REV,
+ PD_MSG_EXT_SINK_CAP_EXT
};
enum adev_actions {
@@ -304,6 +309,51 @@ struct pd_pps_data {
bool active;
};
+enum spr_avs_status {
+ SPR_AVS_UNKNOWN,
+ SPR_AVS_NOT_SUPPORTED,
+ SPR_AVS_SUPPORTED
+};
+
+static const char * const spr_avs_status_strings[] = {
+ [SPR_AVS_UNKNOWN] = "Unknown",
+ [SPR_AVS_SUPPORTED] = "Supported",
+ [SPR_AVS_NOT_SUPPORTED] = "Not Supported",
+};
+
+/*
+ * Standard Power Range Adjustable Voltage Supply (SPR - AVS) data
+ * @max_current_ma_9v_to_15v: Max current for 9V to 15V range derived from
+ * source cap & sink cap
+ * @max_current_ma_15v_to_20v: Max current for 15V to 20V range derived from
+ * source cap & sink cap
+ * @req_op_curr_ma: Requested operating current to the port partner acting as source
+ * @req_out_volt_mv: Requested output voltage to the port partner acting as source
+ * @max_out_volt_mv: Max SPR voltage supported by the port and the port partner
+ * @max_current_ma; MAX SPR current supported by the port and the port partner
+ * @port_partner_src_status: SPR AVS status of port partner acting as source
+ * @port_partner_src_pdo_index: PDO index of SPR AVS cap of the port partner
+ * acting as source. Valid only when
+ * port_partner_src_status is SPR_AVS_SUPPORTED.
+ * @port_snk_status: SPR AVS status of the local port acting as sink.
+ * @port_snk_pdo_index: PDO index of SPR AVS cap of local port acting as sink
+ * @active: True when the local port acting as the sink has negotiated SPR AVS
+ * with the partner acting as source.
+ */
+struct pd_spr_avs_data {
+ u32 max_current_ma_9v_to_15v;
+ u32 max_current_ma_15v_to_20v;
+ u32 req_op_curr_ma;
+ u32 req_out_volt_mv;
+ u32 max_out_volt_mv;
+ u32 max_current_ma;
+ enum spr_avs_status port_partner_src_status;
+ unsigned int port_partner_src_pdo_index;
+ enum spr_avs_status port_snk_status;
+ unsigned int port_snk_pdo_index;
+ bool active;
+};
+
struct pd_data {
struct usb_power_delivery *pd;
struct usb_power_delivery_capabilities *source_cap;
@@ -313,6 +363,10 @@ struct pd_data {
unsigned int operating_snk_mw;
};
+#define PD_CAP_REV10 0x1
+#define PD_CAP_REV20 0x2
+#define PD_CAP_REV30 0x3
+
struct pd_revision_info {
u8 rev_major;
u8 rev_minor;
@@ -332,6 +386,47 @@ struct pd_timings {
u32 snk_bc12_cmpletion_time;
};
+/* Convert microwatt to watt */
+#define UW_TO_W(pow) ((pow) / 1000000)
+
+/*
+ * struct pd_identifier - Contains info about PD identifiers
+ * @vid: Vendor ID (assigned by USB-IF)
+ * @pid: Product ID (assigned by manufacturer)
+ * @xid: Value assigned by USB-IF for product
+ */
+struct pd_identifier {
+ u16 vid;
+ u16 pid;
+ u32 xid;
+};
+
+/*
+ * struct sink_caps_ext_data - Sink extended capability data
+ * @load_step: Indicates the load step slew rate. Value of 0 indicates 150mA/us
+ * & 1 indicates 500 mA/us
+ * @load_char: Snk overload characteristics
+ * @compliance: Types of sources the sink has been tested & certified on
+ * @modes: Charging caps & power sources supported
+ * @spr_min_pdp: Sink Minimum PDP for SPR mode (in Watts)
+ * @spr_op_pdp: Sink Operational PDP for SPR mode (in Watts)
+ * @spr_max_pdp: Sink Maximum PDP for SPR mode (in Watts)
+ */
+struct sink_caps_ext_data {
+ u8 load_step;
+ u16 load_char;
+ u8 compliance;
+ u8 modes;
+ u8 spr_min_pdp;
+ u8 spr_op_pdp;
+ u8 spr_max_pdp;
+};
+
+enum aug_req_type {
+ PD_PPS,
+ PD_SPR_AVS,
+};
+
struct tcpm_port {
struct device *dev;
@@ -494,9 +589,14 @@ struct tcpm_port {
/* PPS */
struct pd_pps_data pps_data;
- struct completion pps_complete;
- bool pps_pending;
- int pps_status;
+
+ /* SPR AVS */
+ struct pd_spr_avs_data spr_avs_data;
+
+ /* Augmented supply request - PPS; SPR_AVS */
+ struct completion aug_supply_req_complete;
+ bool aug_supply_req_pending;
+ int aug_supply_req_status;
/* Alternate mode data */
struct pd_mode_data mode_data;
@@ -580,6 +680,9 @@ struct tcpm_port {
/* Indicates maximum (revision, version) supported */
struct pd_revision_info pd_rev;
+
+ struct pd_identifier pd_ident;
+ struct sink_caps_ext_data sink_caps_ext;
#ifdef CONFIG_DEBUG_FS
struct dentry *dentry;
struct mutex logbuffer_lock; /* log buffer access lock */
@@ -596,6 +699,15 @@ struct pd_rx_event {
enum tcpm_transmit_type rx_sop_type;
};
+struct altmode_vdm_event {
+ struct kthread_work work;
+ struct tcpm_port *port;
+ u32 header;
+ int cnt;
+ enum tcpm_transmit_type tx_sop_type;
+ u32 data[] __counted_by(cnt);
+};
+
static const char * const pd_rev[] = {
[PD_REV10] = "rev1",
[PD_REV20] = "rev2",
@@ -620,9 +732,15 @@ static const char * const pd_rev[] = {
(tcpm_cc_is_source((port)->cc2) && \
!tcpm_cc_is_source((port)->cc1)))
-#define tcpm_port_is_debug(port) \
+#define tcpm_port_is_debug_source(port) \
(tcpm_cc_is_source((port)->cc1) && tcpm_cc_is_source((port)->cc2))
+#define tcpm_port_is_debug_sink(port) \
+ (tcpm_cc_is_sink((port)->cc1) && tcpm_cc_is_sink((port)->cc2))
+
+#define tcpm_port_is_debug(port) \
+ (tcpm_port_is_debug_source(port) || tcpm_port_is_debug_sink(port))
+
#define tcpm_port_is_audio(port) \
(tcpm_cc_is_audio((port)->cc1) && tcpm_cc_is_audio((port)->cc2))
@@ -710,7 +828,7 @@ static void _tcpm_log(struct tcpm_port *port, const char *fmt, va_list args)
if (tcpm_log_full(port)) {
port->logbuffer_head = max(port->logbuffer_head - 1, 0);
- strcpy(tmpbuffer, "overflow");
+ strscpy(tmpbuffer, "overflow");
}
if (port->logbuffer_head < 0 ||
@@ -808,15 +926,28 @@ static void tcpm_log_source_caps(struct tcpm_port *port)
case PDO_TYPE_APDO:
if (pdo_apdo_type(pdo) == APDO_TYPE_PPS)
scnprintf(msg, sizeof(msg),
- "%u-%u mV, %u mA",
+ "PPS %u-%u mV, %u mA",
pdo_pps_apdo_min_voltage(pdo),
pdo_pps_apdo_max_voltage(pdo),
pdo_pps_apdo_max_current(pdo));
+ else if (pdo_apdo_type(pdo) == APDO_TYPE_EPR_AVS)
+ scnprintf(msg, sizeof(msg),
+ "EPR AVS %u-%u mV %u W peak_current: %u",
+ pdo_epr_avs_apdo_min_voltage_mv(pdo),
+ pdo_epr_avs_apdo_max_voltage_mv(pdo),
+ pdo_epr_avs_apdo_pdp_w(pdo),
+ pdo_epr_avs_apdo_src_peak_current(pdo));
+ else if (pdo_apdo_type(pdo) == APDO_TYPE_SPR_AVS)
+ scnprintf(msg, sizeof(msg),
+ "SPR AVS 9-15 V: %u mA 15-20 V: %u mA peak_current: %u",
+ pdo_spr_avs_apdo_9v_to_15v_max_current_ma(pdo),
+ pdo_spr_avs_apdo_15v_to_20v_max_current_ma(pdo),
+ pdo_spr_avs_apdo_src_peak_current(pdo));
else
- strcpy(msg, "undefined APDO");
+ strscpy(msg, "undefined APDO");
break;
default:
- strcpy(msg, "undefined");
+ strscpy(msg, "undefined");
break;
}
tcpm_log(port, " PDO %d: type %d, %s",
@@ -1151,7 +1282,7 @@ static int tcpm_set_attached_state(struct tcpm_port *port, bool attached)
port->data_role);
}
-static int tcpm_set_roles(struct tcpm_port *port, bool attached,
+static int tcpm_set_roles(struct tcpm_port *port, bool attached, int state,
enum typec_role role, enum typec_data_role data)
{
enum typec_orientation orientation;
@@ -1188,7 +1319,7 @@ static int tcpm_set_roles(struct tcpm_port *port, bool attached,
}
}
- ret = tcpm_mux_set(port, TYPEC_STATE_USB, usb_role, orientation);
+ ret = tcpm_mux_set(port, state, usb_role, orientation);
if (ret < 0)
return ret;
@@ -1339,6 +1470,64 @@ static int tcpm_pd_send_sink_caps(struct tcpm_port *port)
return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg);
}
+static int tcpm_pd_send_sink_cap_ext(struct tcpm_port *port)
+{
+ u16 operating_snk_watt = port->operating_snk_mw / 1000;
+ struct sink_caps_ext_data *data = &port->sink_caps_ext;
+ struct pd_identifier *pd_ident = &port->pd_ident;
+ struct sink_caps_ext_msg skedb = {0};
+ struct pd_message msg;
+ u8 data_obj_cnt;
+
+ if (!port->self_powered)
+ data->spr_op_pdp = operating_snk_watt;
+
+ /*
+ * SPR Sink Minimum PDP indicates the minimum power required to operate
+ * a sink device in its lowest level of functionality without requiring
+ * power from the battery. We can use the operating_snk_watt value to
+ * populate it, as operating_snk_watt indicates device's min operating
+ * power.
+ */
+ data->spr_min_pdp = operating_snk_watt;
+
+ if (data->spr_op_pdp < data->spr_min_pdp ||
+ data->spr_max_pdp < data->spr_op_pdp) {
+ tcpm_log(port,
+ "Invalid PDP values, Min PDP:%u, Op PDP:%u, Max PDP:%u",
+ data->spr_min_pdp, data->spr_op_pdp, data->spr_max_pdp);
+ return -EOPNOTSUPP;
+ }
+
+ memset(&msg, 0, sizeof(msg));
+ skedb.vid = cpu_to_le16(pd_ident->vid);
+ skedb.pid = cpu_to_le16(pd_ident->pid);
+ skedb.xid = cpu_to_le32(pd_ident->xid);
+ skedb.skedb_ver = SKEDB_VER_1_0;
+ skedb.load_step = data->load_step;
+ skedb.load_char = cpu_to_le16(data->load_char);
+ skedb.compliance = data->compliance;
+ skedb.modes = data->modes;
+ skedb.spr_min_pdp = data->spr_min_pdp;
+ skedb.spr_op_pdp = data->spr_op_pdp;
+ skedb.spr_max_pdp = data->spr_max_pdp;
+ memcpy(msg.ext_msg.data, &skedb, sizeof(skedb));
+ msg.ext_msg.header = PD_EXT_HDR_LE(sizeof(skedb),
+ 0, /* Denotes if request chunk */
+ 0, /* Chunk Number */
+ 1 /* Chunked */);
+
+ data_obj_cnt = count_chunked_data_objs(sizeof(skedb));
+ msg.header = cpu_to_le16(PD_HEADER(PD_EXT_SINK_CAP_EXT,
+ port->pwr_role,
+ port->data_role,
+ port->negotiated_rev,
+ port->message_id,
+ data_obj_cnt,
+ 1 /* Denotes if ext header */));
+ return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg);
+}
+
static void mod_tcpm_delayed_work(struct tcpm_port *port, unsigned int delay_ms)
{
if (delay_ms) {
@@ -1608,18 +1797,59 @@ static void tcpm_queue_vdm(struct tcpm_port *port, const u32 header,
mod_vdm_delayed_work(port, 0);
}
-static void tcpm_queue_vdm_unlocked(struct tcpm_port *port, const u32 header,
- const u32 *data, int cnt, enum tcpm_transmit_type tx_sop_type)
+static void tcpm_queue_vdm_work(struct kthread_work *work)
{
- if (port->state != SRC_READY && port->state != SNK_READY &&
- port->state != SRC_VDM_IDENTITY_REQUEST)
- return;
+ struct altmode_vdm_event *event = container_of(work,
+ struct altmode_vdm_event,
+ work);
+ struct tcpm_port *port = event->port;
mutex_lock(&port->lock);
- tcpm_queue_vdm(port, header, data, cnt, tx_sop_type);
+ if (port->state != SRC_READY && port->state != SNK_READY &&
+ port->state != SRC_VDM_IDENTITY_REQUEST) {
+ tcpm_log_force(port, "dropping altmode_vdm_event");
+ goto port_unlock;
+ }
+
+ tcpm_queue_vdm(port, event->header, event->data, event->cnt, event->tx_sop_type);
+
+port_unlock:
+ kfree(event);
mutex_unlock(&port->lock);
}
+static int tcpm_queue_vdm_unlocked(struct tcpm_port *port, const u32 header,
+ const u32 *data, int cnt, enum tcpm_transmit_type tx_sop_type)
+{
+ struct altmode_vdm_event *event;
+ int ret = -ENOMEM;
+
+ event = kzalloc_flex(*event, data, cnt);
+ if (!event)
+ goto err_event;
+
+ kthread_init_work(&event->work, tcpm_queue_vdm_work);
+ event->cnt = cnt;
+ event->port = port;
+ event->header = header;
+ memcpy(event->data, data, sizeof(u32) * cnt);
+ event->tx_sop_type = tx_sop_type;
+
+ ret = kthread_queue_work(port->wq, &event->work);
+ if (!ret) {
+ ret = -EBUSY;
+ goto err_data;
+ }
+
+ return 0;
+
+err_data:
+ kfree(event);
+err_event:
+ tcpm_log_force(port, "failed to queue altmode vdm, err:%d", ret);
+ return ret;
+}
+
static void svdm_consume_identity(struct tcpm_port *port, const u32 *p, int cnt)
{
u32 vdo = p[VDO_INDEX_IDH];
@@ -1730,7 +1960,7 @@ static bool svdm_consume_svids(struct tcpm_port *port, const u32 *p, int cnt,
/*
* PD3.0 Spec 6.4.4.3.2: The SVIDs are returned 2 per VDO (see Table
* 6-43), and can be returned maximum 6 VDOs per response (see Figure
- * 6-19). If the Respondersupports 12 or more SVID then the Discover
+ * 6-19). If the Responder supports 12 or more SVID then the Discover
* SVIDs Command Shall be executed multiple times until a Discover
* SVIDs VDO is returned ending either with a SVID value of 0x0000 in
* the last part of the last VDO or with a VDO containing two SVIDs
@@ -2361,17 +2591,21 @@ static void tcpm_handle_vdm_request(struct tcpm_port *port,
case ADEV_NONE:
break;
case ADEV_NOTIFY_USB_AND_QUEUE_VDM:
- WARN_ON(typec_altmode_notify(adev, TYPEC_STATE_USB, NULL));
- typec_altmode_vdm(adev, p[0], &p[1], cnt);
+ if (rx_sop_type == TCPC_TX_SOP_PRIME) {
+ typec_cable_altmode_vdm(adev, TYPEC_PLUG_SOP_P, p[0], &p[1], cnt);
+ } else {
+ WARN_ON(typec_altmode_notify(adev, TYPEC_STATE_USB, NULL));
+ typec_altmode_vdm(adev, p[0], &p[1], cnt);
+ }
break;
case ADEV_QUEUE_VDM:
- if (response_tx_sop_type == TCPC_TX_SOP_PRIME)
+ if (rx_sop_type == TCPC_TX_SOP_PRIME)
typec_cable_altmode_vdm(adev, TYPEC_PLUG_SOP_P, p[0], &p[1], cnt);
else
typec_altmode_vdm(adev, p[0], &p[1], cnt);
break;
case ADEV_QUEUE_VDM_SEND_EXIT_MODE_ON_FAIL:
- if (response_tx_sop_type == TCPC_TX_SOP_PRIME) {
+ if (rx_sop_type == TCPC_TX_SOP_PRIME) {
if (typec_cable_altmode_vdm(adev, TYPEC_PLUG_SOP_P,
p[0], &p[1], cnt)) {
int svdm_version = typec_get_cable_svdm_version(
@@ -2830,8 +3064,7 @@ static int tcpm_altmode_enter(struct typec_altmode *altmode, u32 *vdo)
header = VDO(altmode->svid, vdo ? 2 : 1, svdm_version, CMD_ENTER_MODE);
header |= VDO_OPOS(altmode->mode);
- tcpm_queue_vdm_unlocked(port, header, vdo, vdo ? 1 : 0, TCPC_TX_SOP);
- return 0;
+ return tcpm_queue_vdm_unlocked(port, header, vdo, vdo ? 1 : 0, TCPC_TX_SOP);
}
static int tcpm_altmode_exit(struct typec_altmode *altmode)
@@ -2847,8 +3080,7 @@ static int tcpm_altmode_exit(struct typec_altmode *altmode)
header = VDO(altmode->svid, 1, svdm_version, CMD_EXIT_MODE);
header |= VDO_OPOS(altmode->mode);
- tcpm_queue_vdm_unlocked(port, header, NULL, 0, TCPC_TX_SOP);
- return 0;
+ return tcpm_queue_vdm_unlocked(port, header, NULL, 0, TCPC_TX_SOP);
}
static int tcpm_altmode_vdm(struct typec_altmode *altmode,
@@ -2856,9 +3088,7 @@ static int tcpm_altmode_vdm(struct typec_altmode *altmode,
{
struct tcpm_port *port = typec_altmode_get_drvdata(altmode);
- tcpm_queue_vdm_unlocked(port, header, data, count - 1, TCPC_TX_SOP);
-
- return 0;
+ return tcpm_queue_vdm_unlocked(port, header, data, count - 1, TCPC_TX_SOP);
}
static const struct typec_altmode_ops tcpm_altmode_ops = {
@@ -2882,8 +3112,7 @@ static int tcpm_cable_altmode_enter(struct typec_altmode *altmode, enum typec_pl
header = VDO(altmode->svid, vdo ? 2 : 1, svdm_version, CMD_ENTER_MODE);
header |= VDO_OPOS(altmode->mode);
- tcpm_queue_vdm_unlocked(port, header, vdo, vdo ? 1 : 0, TCPC_TX_SOP_PRIME);
- return 0;
+ return tcpm_queue_vdm_unlocked(port, header, vdo, vdo ? 1 : 0, TCPC_TX_SOP_PRIME);
}
static int tcpm_cable_altmode_exit(struct typec_altmode *altmode, enum typec_plug_index sop)
@@ -2899,8 +3128,7 @@ static int tcpm_cable_altmode_exit(struct typec_altmode *altmode, enum typec_plu
header = VDO(altmode->svid, 1, svdm_version, CMD_EXIT_MODE);
header |= VDO_OPOS(altmode->mode);
- tcpm_queue_vdm_unlocked(port, header, NULL, 0, TCPC_TX_SOP_PRIME);
- return 0;
+ return tcpm_queue_vdm_unlocked(port, header, NULL, 0, TCPC_TX_SOP_PRIME);
}
static int tcpm_cable_altmode_vdm(struct typec_altmode *altmode, enum typec_plug_index sop,
@@ -2908,9 +3136,7 @@ static int tcpm_cable_altmode_vdm(struct typec_altmode *altmode, enum typec_plug
{
struct tcpm_port *port = typec_altmode_get_drvdata(altmode);
- tcpm_queue_vdm_unlocked(port, header, data, count - 1, TCPC_TX_SOP_PRIME);
-
- return 0;
+ return tcpm_queue_vdm_unlocked(port, header, data, count - 1, TCPC_TX_SOP_PRIME);
}
static const struct typec_cable_ops tcpm_cable_ops = {
@@ -3120,6 +3346,7 @@ static void tcpm_pd_data_request(struct tcpm_port *port,
switch (type) {
case PD_DATA_SOURCE_CAP:
+ port->spr_avs_data.port_partner_src_status = SPR_AVS_UNKNOWN;
for (i = 0; i < cnt; i++)
port->source_caps[i] = le32_to_cpu(msg->payload[i]);
@@ -3291,12 +3518,12 @@ static void tcpm_pd_data_request(struct tcpm_port *port,
}
}
-static void tcpm_pps_complete(struct tcpm_port *port, int result)
+static void tcpm_aug_supply_req_complete(struct tcpm_port *port, int result)
{
- if (port->pps_pending) {
- port->pps_status = result;
- port->pps_pending = false;
- complete(&port->pps_complete);
+ if (port->aug_supply_req_pending) {
+ port->aug_supply_req_status = result;
+ port->aug_supply_req_pending = false;
+ complete(&port->aug_supply_req_complete);
}
}
@@ -3394,7 +3621,7 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port,
/* Revert data back from any requested PPS updates */
port->pps_data.req_out_volt = port->supply_voltage;
port->pps_data.req_op_curr = port->current_limit;
- port->pps_status = (type == PD_CTRL_WAIT ?
+ port->aug_supply_req_status = (type == PD_CTRL_WAIT ?
-EAGAIN : -EOPNOTSUPP);
/* Threshold was relaxed before sending Request. Restore it back. */
@@ -3404,6 +3631,20 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port,
tcpm_set_state(port, SNK_READY, 0);
break;
+ case SNK_NEGOTIATE_SPR_AVS_CAPABILITIES:
+ /* Revert data back from any requested SPR AVS updates */
+ port->spr_avs_data.req_out_volt_mv = port->supply_voltage;
+ port->spr_avs_data.req_op_curr_ma = port->current_limit;
+ port->aug_supply_req_status = (type == PD_CTRL_WAIT ?
+ -EAGAIN : -EOPNOTSUPP);
+
+ /* Threshold was relaxed before sending Request. Restore it back. */
+ tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_PWR_MODE_PD,
+ port->spr_avs_data.active,
+ port->supply_voltage);
+
+ tcpm_set_state(port, SNK_READY, 0);
+ break;
case DR_SWAP_SEND:
port->swap_status = (type == PD_CTRL_WAIT ?
-EAGAIN : -EOPNOTSUPP);
@@ -3456,6 +3697,7 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port,
switch (port->state) {
case SNK_NEGOTIATE_CAPABILITIES:
port->pps_data.active = false;
+ port->spr_avs_data.active = false;
tcpm_set_state(port, SNK_TRANSITION_SINK, 0);
break;
case SNK_NEGOTIATE_PPS_CAPABILITIES:
@@ -3468,6 +3710,13 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port,
power_supply_changed(port->psy);
tcpm_set_state(port, SNK_TRANSITION_SINK, 0);
break;
+ case SNK_NEGOTIATE_SPR_AVS_CAPABILITIES:
+ port->spr_avs_data.active = true;
+ port->req_supply_voltage = port->spr_avs_data.req_out_volt_mv;
+ port->req_current_limit = port->spr_avs_data.req_op_curr_ma;
+ power_supply_changed(port->psy);
+ tcpm_set_state(port, SNK_TRANSITION_SINK, 0);
+ break;
case SOFT_RESET_SEND:
if (port->ams == SOFT_RESET_AMS)
tcpm_ams_finish(port);
@@ -3581,6 +3830,19 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port,
PD_MSG_CTRL_NOT_SUPP,
NONE_AMS);
break;
+ case PD_CTRL_GET_SINK_CAP_EXT:
+ /* This is an unsupported message if port type is SRC */
+ if (port->negotiated_rev >= PD_REV30 &&
+ port->port_type != TYPEC_PORT_SRC)
+ tcpm_pd_handle_msg(port, PD_MSG_EXT_SINK_CAP_EXT,
+ GETTING_SINK_EXTENDED_CAPABILITIES);
+ else
+ tcpm_pd_handle_msg(port,
+ port->negotiated_rev < PD_REV30 ?
+ PD_MSG_CTRL_REJECT :
+ PD_MSG_CTRL_NOT_SUPP,
+ NONE_AMS);
+ break;
default:
tcpm_pd_handle_msg(port,
port->negotiated_rev < PD_REV30 ?
@@ -3729,7 +3991,7 @@ void tcpm_pd_receive(struct tcpm_port *port, const struct pd_message *msg,
{
struct pd_rx_event *event;
- event = kzalloc(sizeof(*event), GFP_ATOMIC);
+ event = kzalloc_obj(*event, GFP_ATOMIC);
if (!event)
return;
@@ -3833,6 +4095,16 @@ static bool tcpm_send_queued_message(struct tcpm_port *port)
ret);
tcpm_ams_finish(port);
break;
+ case PD_MSG_EXT_SINK_CAP_EXT:
+ ret = tcpm_pd_send_sink_cap_ext(port);
+ if (ret == -EOPNOTSUPP)
+ tcpm_pd_send_control(port, PD_CTRL_NOT_SUPP, TCPC_TX_SOP);
+ else if (ret < 0)
+ tcpm_log(port,
+ "Unable to transmit sink cap extended, ret=%d",
+ ret);
+ tcpm_ams_finish(port);
+ break;
default:
break;
}
@@ -3942,9 +4214,9 @@ static int tcpm_pd_select_pdo(struct tcpm_port *port, int *sink_pdo,
case PDO_TYPE_APDO:
if (pdo_apdo_type(pdo) == APDO_TYPE_PPS) {
port->pps_data.supported = true;
- port->usb_type =
- POWER_SUPPLY_USB_TYPE_PD_PPS;
- power_supply_changed(port->psy);
+ } else if (pdo_apdo_type(pdo) == APDO_TYPE_SPR_AVS) {
+ port->spr_avs_data.port_partner_src_status = SPR_AVS_SUPPORTED;
+ port->spr_avs_data.port_partner_src_pdo_index = i;
}
continue;
default:
@@ -3982,6 +4254,10 @@ static int tcpm_pd_select_pdo(struct tcpm_port *port, int *sink_pdo,
min_snk_mv = pdo_min_voltage(pdo);
break;
case PDO_TYPE_APDO:
+ if (pdo_apdo_type(pdo) == APDO_TYPE_SPR_AVS) {
+ port->spr_avs_data.port_snk_status = SPR_AVS_SUPPORTED;
+ port->spr_avs_data.port_snk_pdo_index = j;
+ }
continue;
default:
tcpm_log(port, "Invalid sink PDO type, ignoring");
@@ -4003,6 +4279,23 @@ static int tcpm_pd_select_pdo(struct tcpm_port *port, int *sink_pdo,
}
}
+ if (port->spr_avs_data.port_snk_status == SPR_AVS_UNKNOWN)
+ port->spr_avs_data.port_snk_status = SPR_AVS_NOT_SUPPORTED;
+
+ if (port->spr_avs_data.port_partner_src_status == SPR_AVS_UNKNOWN)
+ port->spr_avs_data.port_partner_src_status = SPR_AVS_NOT_SUPPORTED;
+
+ if (port->pps_data.supported &&
+ port->spr_avs_data.port_partner_src_status == SPR_AVS_SUPPORTED)
+ port->usb_type = POWER_SUPPLY_USB_TYPE_PD_PPS_SPR_AVS;
+ else if (port->pps_data.supported)
+ port->usb_type = POWER_SUPPLY_USB_TYPE_PD_PPS;
+ else if (port->spr_avs_data.port_partner_src_status == SPR_AVS_SUPPORTED)
+ port->usb_type = POWER_SUPPLY_USB_TYPE_PD_SPR_AVS;
+
+ if (port->usb_type != POWER_SUPPLY_USB_TYPE_PD)
+ power_supply_changed(port->psy);
+
return ret;
}
@@ -4053,6 +4346,88 @@ static unsigned int tcpm_pd_select_pps_apdo(struct tcpm_port *port)
return src_pdo;
}
+static int tcpm_pd_select_spr_avs_apdo(struct tcpm_port *port)
+{
+ u32 req_out_volt_mv, req_op_curr_ma, src_max_curr_ma = 0, source_cap;
+ u32 snk_max_curr_ma = 0, src_pdo_index, snk_pdo_index, snk_pdo;
+
+ if (port->spr_avs_data.port_snk_status != SPR_AVS_SUPPORTED ||
+ port->spr_avs_data.port_partner_src_status !=
+ SPR_AVS_SUPPORTED) {
+ tcpm_log(port, "SPR AVS not supported. port:%s partner:%s",
+ spr_avs_status_strings[port->spr_avs_data.port_snk_status],
+ spr_avs_status_strings[port->spr_avs_data.port_partner_src_status]);
+ return -EOPNOTSUPP;
+ }
+
+ /* Round up to SPR_AVS_VOLT_MV_STEP */
+ req_out_volt_mv = port->spr_avs_data.req_out_volt_mv;
+ if (req_out_volt_mv % SPR_AVS_VOLT_MV_STEP) {
+ req_out_volt_mv += SPR_AVS_VOLT_MV_STEP -
+ (req_out_volt_mv % SPR_AVS_VOLT_MV_STEP);
+ port->spr_avs_data.req_out_volt_mv = req_out_volt_mv;
+ }
+
+ /* Round up to RDO_SPR_AVS_CURR_MA_STEP */
+ req_op_curr_ma = port->spr_avs_data.req_op_curr_ma;
+ if (req_op_curr_ma % RDO_SPR_AVS_CURR_MA_STEP) {
+ req_op_curr_ma += RDO_SPR_AVS_CURR_MA_STEP -
+ (req_op_curr_ma % RDO_SPR_AVS_CURR_MA_STEP);
+ port->spr_avs_data.req_op_curr_ma = req_op_curr_ma;
+ }
+
+ src_pdo_index = port->spr_avs_data.port_partner_src_pdo_index;
+ snk_pdo_index = port->spr_avs_data.port_snk_pdo_index;
+ source_cap = port->source_caps[src_pdo_index];
+ snk_pdo = port->snk_pdo[snk_pdo_index];
+ tcpm_log(port,
+ "SPR AVS src_pdo_index:%d snk_pdo_index:%d req_op_curr_ma roundup:%u req_out_volt_mv roundup:%u",
+ src_pdo_index, snk_pdo_index, req_op_curr_ma, req_out_volt_mv);
+
+ if (req_out_volt_mv >= SPR_AVS_TIER1_MIN_VOLT_MV &&
+ req_out_volt_mv <= SPR_AVS_TIER1_MAX_VOLT_MV) {
+ src_max_curr_ma =
+ pdo_spr_avs_apdo_9v_to_15v_max_current_ma(source_cap);
+ snk_max_curr_ma =
+ pdo_spr_avs_apdo_9v_to_15v_max_current_ma(snk_pdo);
+ } else if (req_out_volt_mv > SPR_AVS_TIER1_MAX_VOLT_MV &&
+ req_out_volt_mv <= SPR_AVS_TIER2_MAX_VOLT_MV) {
+ src_max_curr_ma =
+ pdo_spr_avs_apdo_15v_to_20v_max_current_ma(source_cap);
+ snk_max_curr_ma =
+ pdo_spr_avs_apdo_15v_to_20v_max_current_ma(snk_pdo);
+ } else {
+ tcpm_log(port, "Invalid SPR AVS req_volt:%umV", req_out_volt_mv);
+ return -EINVAL;
+ }
+
+ if (req_op_curr_ma > src_max_curr_ma ||
+ req_op_curr_ma > snk_max_curr_ma) {
+ tcpm_log(port,
+ "Invalid SPR AVS request. req_volt:%umV req_curr:%umA src_max_cur:%umA snk_max_cur:%umA",
+ req_out_volt_mv, req_op_curr_ma, src_max_curr_ma,
+ snk_max_curr_ma);
+ return -EINVAL;
+ }
+
+ /* Max SPR voltage based on both the port and the partner caps */
+ if (pdo_spr_avs_apdo_15v_to_20v_max_current_ma(snk_pdo) &&
+ pdo_spr_avs_apdo_15v_to_20v_max_current_ma(source_cap))
+ port->spr_avs_data.max_out_volt_mv = SPR_AVS_TIER2_MAX_VOLT_MV;
+ else
+ port->spr_avs_data.max_out_volt_mv = SPR_AVS_TIER1_MAX_VOLT_MV;
+
+ /*
+ * Max SPR AVS curr based on 9V to 15V. This should be higher than or
+ * equal to 15V to 20V range.
+ */
+ port->spr_avs_data.max_current_ma =
+ min(pdo_spr_avs_apdo_9v_to_15v_max_current_ma(source_cap),
+ pdo_spr_avs_apdo_9v_to_15v_max_current_ma(snk_pdo));
+
+ return src_pdo_index;
+}
+
static int tcpm_pd_build_request(struct tcpm_port *port, u32 *rdo)
{
unsigned int mv, ma, mw, flags;
@@ -4220,13 +4595,74 @@ static int tcpm_pd_build_pps_request(struct tcpm_port *port, u32 *rdo)
return 0;
}
-static int tcpm_pd_send_pps_request(struct tcpm_port *port)
+static int tcpm_pd_build_spr_avs_request(struct tcpm_port *port, u32 *rdo)
+{
+ u32 out_mv, op_ma, flags, snk_pdo_index, source_cap;
+ unsigned int src_power_mw, snk_power_mw;
+ int src_pdo_index;
+ u32 snk_pdo;
+
+ src_pdo_index = tcpm_pd_select_spr_avs_apdo(port);
+ if (src_pdo_index < 0)
+ return src_pdo_index;
+ snk_pdo_index = port->spr_avs_data.port_snk_pdo_index;
+ source_cap = port->source_caps[src_pdo_index];
+ snk_pdo = port->snk_pdo[snk_pdo_index];
+ out_mv = port->spr_avs_data.req_out_volt_mv;
+ op_ma = port->spr_avs_data.req_op_curr_ma;
+
+ flags = RDO_USB_COMM | RDO_NO_SUSPEND;
+
+ /*
+ * Set capability mismatch when the maximum power needs in the current
+ * requested AVS voltage tier range is greater than
+ * port->operating_snk_mw, however, the maximum power offered by the
+ * source at the current requested AVS voltage tier is less than
+ * port->operating_sink_mw.
+ */
+ if (out_mv > SPR_AVS_TIER1_MAX_VOLT_MV) {
+ src_power_mw =
+ pdo_spr_avs_apdo_15v_to_20v_max_current_ma(source_cap) *
+ SPR_AVS_TIER2_MAX_VOLT_MV / 1000;
+ snk_power_mw =
+ pdo_spr_avs_apdo_15v_to_20v_max_current_ma(snk_pdo) *
+ SPR_AVS_TIER2_MAX_VOLT_MV / 1000;
+ } else {
+ src_power_mw =
+ pdo_spr_avs_apdo_9v_to_15v_max_current_ma(source_cap) *
+ SPR_AVS_TIER1_MAX_VOLT_MV / 1000;
+ snk_power_mw =
+ pdo_spr_avs_apdo_9v_to_15v_max_current_ma(snk_pdo) *
+ SPR_AVS_TIER1_MAX_VOLT_MV / 1000;
+ }
+
+ if (snk_power_mw >= port->operating_snk_mw &&
+ src_power_mw < port->operating_snk_mw)
+ flags |= RDO_CAP_MISMATCH;
+
+ *rdo = RDO_AVS(src_pdo_index + 1, out_mv, op_ma, flags);
+
+ tcpm_log(port, "Requesting APDO SPR AVS %d: %u mV, %u mA",
+ src_pdo_index, out_mv, op_ma);
+
+ return 0;
+}
+
+static int tcpm_pd_send_aug_supply_request(struct tcpm_port *port,
+ enum aug_req_type type)
{
struct pd_message msg;
int ret;
u32 rdo;
- ret = tcpm_pd_build_pps_request(port, &rdo);
+ if (type == PD_PPS) {
+ ret = tcpm_pd_build_pps_request(port, &rdo);
+ } else if (type == PD_SPR_AVS) {
+ ret = tcpm_pd_build_spr_avs_request(port, &rdo);
+ } else {
+ tcpm_log(port, "Invalid aug_req_type %d", type);
+ ret = -EOPNOTSUPP;
+ }
if (ret < 0)
return ret;
@@ -4353,16 +4789,6 @@ static int tcpm_src_attach(struct tcpm_port *port)
tcpm_enable_auto_vbus_discharge(port, true);
- ret = tcpm_set_roles(port, true, TYPEC_SOURCE, tcpm_data_role_for_source(port));
- if (ret < 0)
- return ret;
-
- if (port->pd_supported) {
- ret = port->tcpc->set_pd_rx(port->tcpc, true);
- if (ret < 0)
- goto out_disable_mux;
- }
-
/*
* USB Type-C specification, version 1.2,
* chapter 4.5.2.2.8.1 (Attached.SRC Requirements)
@@ -4372,13 +4798,24 @@ static int tcpm_src_attach(struct tcpm_port *port)
(polarity == TYPEC_POLARITY_CC2 && port->cc1 == TYPEC_CC_RA)) {
ret = tcpm_set_vconn(port, true);
if (ret < 0)
- goto out_disable_pd;
+ return ret;
}
ret = tcpm_set_vbus(port, true);
if (ret < 0)
goto out_disable_vconn;
+ ret = tcpm_set_roles(port, true, TYPEC_STATE_USB, TYPEC_SOURCE,
+ tcpm_data_role_for_source(port));
+ if (ret < 0)
+ goto out_disable_vbus;
+
+ if (port->pd_supported) {
+ ret = port->tcpc->set_pd_rx(port->tcpc, true);
+ if (ret < 0)
+ goto out_disable_mux;
+ }
+
port->pd_capable = false;
port->partner = NULL;
@@ -4389,14 +4826,14 @@ static int tcpm_src_attach(struct tcpm_port *port)
return 0;
-out_disable_vconn:
- tcpm_set_vconn(port, false);
-out_disable_pd:
- if (port->pd_supported)
- port->tcpc->set_pd_rx(port->tcpc, false);
out_disable_mux:
tcpm_mux_set(port, TYPEC_STATE_SAFE, USB_ROLE_NONE,
TYPEC_ORIENTATION_NONE);
+out_disable_vbus:
+ tcpm_set_vbus(port, false);
+out_disable_vconn:
+ tcpm_set_vconn(port, false);
+
return ret;
}
@@ -4448,6 +4885,14 @@ static void tcpm_set_partner_usb_comm_capable(struct tcpm_port *port, bool capab
port->tcpc->set_partner_usb_comm_capable(port->tcpc, capable);
}
+static void tcpm_partner_source_caps_reset(struct tcpm_port *port)
+{
+ usb_power_delivery_unregister_capabilities(port->partner_source_caps);
+ port->partner_source_caps = NULL;
+ port->spr_avs_data.port_partner_src_status = SPR_AVS_UNKNOWN;
+ port->spr_avs_data.active = false;
+}
+
static void tcpm_reset_port(struct tcpm_port *port)
{
tcpm_enable_auto_vbus_discharge(port, false);
@@ -4487,8 +4932,7 @@ static void tcpm_reset_port(struct tcpm_port *port)
usb_power_delivery_unregister_capabilities(port->partner_sink_caps);
port->partner_sink_caps = NULL;
- usb_power_delivery_unregister_capabilities(port->partner_source_caps);
- port->partner_source_caps = NULL;
+ tcpm_partner_source_caps_reset(port);
usb_power_delivery_unregister(port->partner_pd);
port->partner_pd = NULL;
}
@@ -4528,7 +4972,8 @@ static int tcpm_snk_attach(struct tcpm_port *port)
tcpm_enable_auto_vbus_discharge(port, true);
- ret = tcpm_set_roles(port, true, TYPEC_SINK, tcpm_data_role_for_sink(port));
+ ret = tcpm_set_roles(port, true, TYPEC_STATE_USB,
+ TYPEC_SINK, tcpm_data_role_for_sink(port));
if (ret < 0)
return ret;
@@ -4551,12 +4996,24 @@ static void tcpm_snk_detach(struct tcpm_port *port)
static int tcpm_acc_attach(struct tcpm_port *port)
{
int ret;
+ enum typec_role role;
+ enum typec_data_role data;
+ int state = TYPEC_STATE_USB;
if (port->attached)
return 0;
- ret = tcpm_set_roles(port, true, TYPEC_SOURCE,
- tcpm_data_role_for_source(port));
+ role = tcpm_port_is_sink(port) ? TYPEC_SINK : TYPEC_SOURCE;
+ data = tcpm_port_is_sink(port) ? tcpm_data_role_for_sink(port)
+ : tcpm_data_role_for_source(port);
+
+ if (tcpm_port_is_audio(port))
+ state = TYPEC_MODE_AUDIO;
+
+ if (tcpm_port_is_debug(port))
+ state = TYPEC_MODE_DEBUG;
+
+ ret = tcpm_set_roles(port, true, state, role, data);
if (ret < 0)
return ret;
@@ -4665,6 +5122,25 @@ static void tcpm_set_initial_svdm_version(struct tcpm_port *port)
}
}
+static void tcpm_set_initial_negotiated_rev(struct tcpm_port *port)
+{
+ switch (port->pd_rev.rev_major) {
+ case PD_CAP_REV10:
+ port->negotiated_rev = PD_REV10;
+ break;
+ case PD_CAP_REV20:
+ port->negotiated_rev = PD_REV20;
+ break;
+ case PD_CAP_REV30:
+ port->negotiated_rev = PD_REV30;
+ break;
+ default:
+ port->negotiated_rev = PD_MAX_REV;
+ break;
+ }
+ port->negotiated_rev_prime = port->negotiated_rev;
+}
+
static void run_state_machine(struct tcpm_port *port)
{
int ret;
@@ -4705,7 +5181,7 @@ static void run_state_machine(struct tcpm_port *port)
tcpm_set_state(port, SNK_UNATTACHED, PD_T_DRP_SNK);
break;
case SRC_ATTACH_WAIT:
- if (tcpm_port_is_debug(port))
+ if (tcpm_port_is_debug_source(port))
tcpm_set_state(port, DEBUG_ACC_ATTACHED,
port->timings.cc_debounce_time);
else if (tcpm_port_is_audio(port))
@@ -4782,8 +5258,7 @@ static void run_state_machine(struct tcpm_port *port)
typec_set_pwr_opmode(port->typec_port, opmode);
port->pwr_opmode = TYPEC_PWR_MODE_USB;
port->caps_count = 0;
- port->negotiated_rev = PD_MAX_REV;
- port->negotiated_rev_prime = PD_MAX_REV;
+ tcpm_set_initial_negotiated_rev(port);
port->message_id = 0;
port->message_id_prime = 0;
port->rx_msgid = -1;
@@ -4949,7 +5424,7 @@ static void run_state_machine(struct tcpm_port *port)
case SNK_UNATTACHED:
if (!port->non_pd_role_swap)
tcpm_swap_complete(port, -ENOTCONN);
- tcpm_pps_complete(port, -ENOTCONN);
+ tcpm_aug_supply_req_complete(port, -ENOTCONN);
tcpm_snk_detach(port);
if (port->potential_contaminant) {
tcpm_set_state(port, CHECK_CONTAMINANT, 0);
@@ -4964,7 +5439,13 @@ static void run_state_machine(struct tcpm_port *port)
tcpm_set_state(port, SRC_UNATTACHED, PD_T_DRP_SRC);
break;
case SNK_ATTACH_WAIT:
- if ((port->cc1 == TYPEC_CC_OPEN &&
+ if (tcpm_port_is_debug_sink(port))
+ tcpm_set_state(port, DEBUG_ACC_ATTACHED,
+ PD_T_CC_DEBOUNCE);
+ else if (tcpm_port_is_audio(port))
+ tcpm_set_state(port, AUDIO_ACC_ATTACHED,
+ PD_T_CC_DEBOUNCE);
+ else if ((port->cc1 == TYPEC_CC_OPEN &&
port->cc2 != TYPEC_CC_OPEN) ||
(port->cc1 != TYPEC_CC_OPEN &&
port->cc2 == TYPEC_CC_OPEN))
@@ -4978,6 +5459,12 @@ static void run_state_machine(struct tcpm_port *port)
if (tcpm_port_is_disconnected(port))
tcpm_set_state(port, SNK_UNATTACHED,
PD_T_PD_DEBOUNCE);
+ else if (tcpm_port_is_debug_sink(port))
+ tcpm_set_state(port, DEBUG_ACC_ATTACHED,
+ PD_T_CC_DEBOUNCE);
+ else if (tcpm_port_is_audio(port))
+ tcpm_set_state(port, AUDIO_ACC_ATTACHED,
+ PD_T_CC_DEBOUNCE);
else if (port->vbus_present)
tcpm_set_state(port,
tcpm_try_src(port) ? SRC_TRY
@@ -5048,8 +5535,7 @@ static void run_state_machine(struct tcpm_port *port)
port->cc2 : port->cc1);
typec_set_pwr_opmode(port->typec_port, opmode);
port->pwr_opmode = TYPEC_PWR_MODE_USB;
- port->negotiated_rev = PD_MAX_REV;
- port->negotiated_rev_prime = PD_MAX_REV;
+ tcpm_set_initial_negotiated_rev(port);
port->message_id = 0;
port->message_id_prime = 0;
port->rx_msgid = -1;
@@ -5169,13 +5655,16 @@ static void run_state_machine(struct tcpm_port *port)
}
break;
case SNK_NEGOTIATE_PPS_CAPABILITIES:
- ret = tcpm_pd_send_pps_request(port);
+ case SNK_NEGOTIATE_SPR_AVS_CAPABILITIES:
+ ret = tcpm_pd_send_aug_supply_request(port, port->state ==
+ SNK_NEGOTIATE_PPS_CAPABILITIES ?
+ PD_PPS : PD_SPR_AVS);
if (ret < 0) {
/* Restore back to the original state */
tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_PWR_MODE_PD,
port->pps_data.active,
port->supply_voltage);
- port->pps_status = ret;
+ port->aug_supply_req_status = ret;
/*
* If this was called due to updates to sink
* capabilities, and pps is no longer valid, we should
@@ -5191,23 +5680,58 @@ static void run_state_machine(struct tcpm_port *port)
}
break;
case SNK_TRANSITION_SINK:
- /* From the USB PD spec:
- * "The Sink Shall transition to Sink Standby before a positive or
- * negative voltage transition of VBUS. During Sink Standby
- * the Sink Shall reduce its power draw to pSnkStdby."
- *
- * This is not applicable to PPS though as the port can continue
- * to draw negotiated power without switching to standby.
- */
- if (port->supply_voltage != port->req_supply_voltage && !port->pps_data.active &&
- port->current_limit * port->supply_voltage / 1000 > PD_P_SNK_STDBY_MW) {
- u32 stdby_ma = PD_P_SNK_STDBY_MW * 1000 / port->supply_voltage;
+ if (port->spr_avs_data.active) {
+ if (abs(port->req_supply_voltage - port->supply_voltage) >
+ SPR_AVS_AVS_SMALL_STEP_V * 1000) {
+ /*
+ * The Sink Shall reduce its current draw to
+ * iSnkStdby within tSnkStdby. The reduction to
+ * iSnkStdby is not required if the voltage
+ * increase is less than or equal to
+ * vAvsSmallStep.
+ */
+ tcpm_log(port,
+ "SPR AVS Setting iSnkstandby. Req vol: %u mV Curr vol: %u mV",
+ port->req_supply_voltage,
+ port->supply_voltage);
+ tcpm_set_current_limit(port, PD_I_SNK_STBY_MA,
+ port->supply_voltage);
+ }
+ /*
+ * Although tAvsSrcTransSmall is expected to be used
+ * for voltage transistions smaller than 1V, using
+ * tAvsSrcTransLarge to be resilient against chargers
+ * which strictly cannot honor tAvsSrcTransSmall to
+ * improve interoperability.
+ */
+ tcpm_set_state(port, hard_reset_state(port),
+ PD_T_AVS_SRC_TRANS_LARGE);
+ /*
+ * From the USB PD spec:
+ * "The Sink Shall transition to Sink Standby before a
+ * positive ornegative voltage transition of VBUS.
+ * During Sink Standby the Sink Shall reduce its power
+ * draw to pSnkStdby."
+ *
+ * This is not applicable to PPS though as the port can
+ * continue to draw negotiated power without switching
+ * to standby.
+ */
+ } else if (port->supply_voltage != port->req_supply_voltage &&
+ !port->pps_data.active &&
+ (port->current_limit * port->supply_voltage / 1000 >
+ PD_P_SNK_STDBY_MW)) {
+ u32 stdby_ma = PD_P_SNK_STDBY_MW * 1000 /
+ port->supply_voltage;
tcpm_log(port, "Setting standby current %u mV @ %u mA",
port->supply_voltage, stdby_ma);
- tcpm_set_current_limit(port, stdby_ma, port->supply_voltage);
+ tcpm_set_current_limit(port, stdby_ma,
+ port->supply_voltage);
+ tcpm_set_state(port, hard_reset_state(port),
+ PD_T_PS_TRANSITION);
}
- fallthrough;
+ break;
case SNK_TRANSITION_SINK_VBUS:
tcpm_set_state(port, hard_reset_state(port),
PD_T_PS_TRANSITION);
@@ -5227,7 +5751,7 @@ static void run_state_machine(struct tcpm_port *port)
tcpm_typec_connect(port);
if (port->pd_capable && port->source_caps[0] & PDO_FIXED_DUAL_ROLE)
mod_enable_frs_delayed_work(port, 0);
- tcpm_pps_complete(port, port->pps_status);
+ tcpm_aug_supply_req_complete(port, port->aug_supply_req_status);
if (port->ams != NONE_AMS)
tcpm_ams_finish(port);
@@ -5276,7 +5800,10 @@ static void run_state_machine(struct tcpm_port *port)
/* Accessory states */
case ACC_UNATTACHED:
tcpm_acc_detach(port);
- tcpm_set_state(port, SRC_UNATTACHED, 0);
+ if (port->port_type == TYPEC_PORT_SRC)
+ tcpm_set_state(port, SRC_UNATTACHED, 0);
+ else
+ tcpm_set_state(port, SNK_UNATTACHED, 0);
break;
case DEBUG_ACC_ATTACHED:
case AUDIO_ACC_ATTACHED:
@@ -5284,6 +5811,7 @@ static void run_state_machine(struct tcpm_port *port)
if (ret < 0)
tcpm_set_state(port, ACC_UNATTACHED, 0);
break;
+ case DEBUG_ACC_DEBOUNCE:
case AUDIO_ACC_DEBOUNCE:
tcpm_set_state(port, ACC_UNATTACHED, port->timings.cc_debounce_time);
break;
@@ -5326,7 +5854,7 @@ static void run_state_machine(struct tcpm_port *port)
*/
tcpm_set_vconn(port, false);
tcpm_set_vbus(port, false);
- tcpm_set_roles(port, port->self_powered, TYPEC_SOURCE,
+ tcpm_set_roles(port, port->self_powered, TYPEC_STATE_USB, TYPEC_SOURCE,
tcpm_data_role_for_source(port));
/*
* If tcpc fails to notify vbus off, TCPM will wait for PD_T_SAFE_0V +
@@ -5358,7 +5886,7 @@ static void run_state_machine(struct tcpm_port *port)
tcpm_set_vconn(port, false);
if (port->pd_capable)
tcpm_set_charge(port, false);
- tcpm_set_roles(port, port->self_powered, TYPEC_SINK,
+ tcpm_set_roles(port, port->self_powered, TYPEC_STATE_USB, TYPEC_SINK,
tcpm_data_role_for_sink(port));
/*
* VBUS may or may not toggle, depending on the adapter.
@@ -5410,9 +5938,10 @@ static void run_state_machine(struct tcpm_port *port)
port->message_id = 0;
port->rx_msgid = -1;
/* remove existing capabilities */
- usb_power_delivery_unregister_capabilities(port->partner_source_caps);
- port->partner_source_caps = NULL;
+ tcpm_partner_source_caps_reset(port);
tcpm_pd_send_control(port, PD_CTRL_ACCEPT, TCPC_TX_SOP);
+ port->vdm_sm_running = false;
+ port->explicit_contract = false;
tcpm_ams_finish(port);
if (port->pwr_role == TYPEC_SOURCE) {
port->upcoming_state = SRC_SEND_CAPABILITIES;
@@ -5444,8 +5973,7 @@ static void run_state_machine(struct tcpm_port *port)
port->message_id = 0;
port->rx_msgid = -1;
/* remove existing capabilities */
- usb_power_delivery_unregister_capabilities(port->partner_source_caps);
- port->partner_source_caps = NULL;
+ tcpm_partner_source_caps_reset(port);
if (tcpm_pd_send_control(port, PD_CTRL_SOFT_RESET, TCPC_TX_SOP))
tcpm_set_state_cond(port, hard_reset_state(port), 0);
else
@@ -5482,10 +6010,10 @@ static void run_state_machine(struct tcpm_port *port)
case DR_SWAP_CHANGE_DR:
tcpm_unregister_altmodes(port);
if (port->data_role == TYPEC_HOST)
- tcpm_set_roles(port, true, port->pwr_role,
+ tcpm_set_roles(port, true, TYPEC_STATE_USB, port->pwr_role,
TYPEC_DEVICE);
else
- tcpm_set_roles(port, true, port->pwr_role,
+ tcpm_set_roles(port, true, TYPEC_STATE_USB, port->pwr_role,
TYPEC_HOST);
tcpm_ams_finish(port);
tcpm_set_state(port, ready_state(port), 0);
@@ -5582,8 +6110,7 @@ static void run_state_machine(struct tcpm_port *port)
break;
case PR_SWAP_SNK_SRC_SINK_OFF:
/* will be source, remove existing capabilities */
- usb_power_delivery_unregister_capabilities(port->partner_source_caps);
- port->partner_source_caps = NULL;
+ tcpm_partner_source_caps_reset(port);
/*
* Prevent vbus discharge circuit from turning on during PR_SWAP
* as this is not a disconnect.
@@ -5731,7 +6258,7 @@ static void run_state_machine(struct tcpm_port *port)
break;
case ERROR_RECOVERY:
tcpm_swap_complete(port, -EPROTO);
- tcpm_pps_complete(port, -EPROTO);
+ tcpm_aug_supply_req_complete(port, -EPROTO);
tcpm_set_state(port, PORT_RESET, 0);
break;
case PORT_RESET:
@@ -5840,10 +6367,10 @@ static void _tcpm_cc_change(struct tcpm_port *port, enum typec_cc_status cc1,
switch (port->state) {
case TOGGLING:
- if (tcpm_port_is_debug(port) || tcpm_port_is_audio(port) ||
+ if (tcpm_port_is_debug_source(port) || tcpm_port_is_audio(port) ||
tcpm_port_is_source(port))
tcpm_set_state(port, SRC_ATTACH_WAIT, 0);
- else if (tcpm_port_is_sink(port))
+ else if (tcpm_port_is_debug_sink(port) || tcpm_port_is_sink(port))
tcpm_set_state(port, SNK_ATTACH_WAIT, 0);
break;
case CHECK_CONTAMINANT:
@@ -5851,9 +6378,11 @@ static void _tcpm_cc_change(struct tcpm_port *port, enum typec_cc_status cc1,
break;
case SRC_UNATTACHED:
case ACC_UNATTACHED:
- if (tcpm_port_is_debug(port) || tcpm_port_is_audio(port) ||
+ if (tcpm_port_is_debug_source(port) || tcpm_port_is_audio(port) ||
tcpm_port_is_source(port))
tcpm_set_state(port, SRC_ATTACH_WAIT, 0);
+ else if (tcpm_port_is_debug_sink(port))
+ tcpm_set_state(port, SNK_ATTACH_WAIT, 0);
break;
case SRC_ATTACH_WAIT:
if (tcpm_port_is_disconnected(port) ||
@@ -5875,7 +6404,8 @@ static void _tcpm_cc_change(struct tcpm_port *port, enum typec_cc_status cc1,
}
break;
case SNK_UNATTACHED:
- if (tcpm_port_is_sink(port))
+ if (tcpm_port_is_debug_sink(port) || tcpm_port_is_audio(port) ||
+ tcpm_port_is_sink(port))
tcpm_set_state(port, SNK_ATTACH_WAIT, 0);
break;
case SNK_ATTACH_WAIT:
@@ -5938,7 +6468,12 @@ static void _tcpm_cc_change(struct tcpm_port *port, enum typec_cc_status cc1,
case DEBUG_ACC_ATTACHED:
if (cc1 == TYPEC_CC_OPEN || cc2 == TYPEC_CC_OPEN)
- tcpm_set_state(port, ACC_UNATTACHED, 0);
+ tcpm_set_state(port, DEBUG_ACC_DEBOUNCE, 0);
+ break;
+
+ case DEBUG_ACC_DEBOUNCE:
+ if (tcpm_port_is_debug(port))
+ tcpm_set_state(port, DEBUG_ACC_ATTACHED, 0);
break;
case SNK_TRY:
@@ -5965,7 +6500,7 @@ static void _tcpm_cc_change(struct tcpm_port *port, enum typec_cc_status cc1,
case SNK_TRY_WAIT_DEBOUNCE:
if (!tcpm_port_is_sink(port)) {
port->max_wait = 0;
- tcpm_set_state(port, SRC_TRYWAIT, 0);
+ tcpm_set_state(port, SRC_TRYWAIT, PD_T_PD_DEBOUNCE);
}
break;
case SRC_TRY_WAIT:
@@ -6699,7 +7234,7 @@ static int tcpm_try_role(struct typec_port *p, int role)
return ret;
}
-static int tcpm_pps_set_op_curr(struct tcpm_port *port, u16 req_op_curr)
+static int tcpm_aug_set_op_curr(struct tcpm_port *port, u16 req_op_curr_ma)
{
unsigned int target_mw;
int ret;
@@ -6707,7 +7242,19 @@ static int tcpm_pps_set_op_curr(struct tcpm_port *port, u16 req_op_curr)
mutex_lock(&port->swap_lock);
mutex_lock(&port->lock);
- if (!port->pps_data.active) {
+ if (port->pps_data.active) {
+ req_op_curr_ma = req_op_curr_ma -
+ (req_op_curr_ma % RDO_PROG_CURR_MA_STEP);
+ if (req_op_curr_ma > port->pps_data.max_curr) {
+ ret = -EINVAL;
+ goto port_unlock;
+ }
+ target_mw = (req_op_curr_ma * port->supply_voltage) / 1000;
+ if (target_mw < port->operating_snk_mw) {
+ ret = -EINVAL;
+ goto port_unlock;
+ }
+ } else if (!port->spr_avs_data.active) {
ret = -EOPNOTSUPP;
goto port_unlock;
}
@@ -6717,38 +7264,31 @@ static int tcpm_pps_set_op_curr(struct tcpm_port *port, u16 req_op_curr)
goto port_unlock;
}
- if (req_op_curr > port->pps_data.max_curr) {
- ret = -EINVAL;
- goto port_unlock;
- }
-
- target_mw = (req_op_curr * port->supply_voltage) / 1000;
- if (target_mw < port->operating_snk_mw) {
- ret = -EINVAL;
- goto port_unlock;
- }
+ if (port->pps_data.active)
+ port->upcoming_state = SNK_NEGOTIATE_PPS_CAPABILITIES;
+ else
+ port->upcoming_state = SNK_NEGOTIATE_SPR_AVS_CAPABILITIES;
- port->upcoming_state = SNK_NEGOTIATE_PPS_CAPABILITIES;
ret = tcpm_ams_start(port, POWER_NEGOTIATION);
if (ret == -EAGAIN) {
port->upcoming_state = INVALID_STATE;
goto port_unlock;
}
- /* Round down operating current to align with PPS valid steps */
- req_op_curr = req_op_curr - (req_op_curr % RDO_PROG_CURR_MA_STEP);
-
- reinit_completion(&port->pps_complete);
- port->pps_data.req_op_curr = req_op_curr;
- port->pps_status = 0;
- port->pps_pending = true;
+ reinit_completion(&port->aug_supply_req_complete);
+ if (port->pps_data.active)
+ port->pps_data.req_op_curr = req_op_curr_ma;
+ else
+ port->spr_avs_data.req_op_curr_ma = req_op_curr_ma;
+ port->aug_supply_req_status = 0;
+ port->aug_supply_req_pending = true;
mutex_unlock(&port->lock);
- if (!wait_for_completion_timeout(&port->pps_complete,
- msecs_to_jiffies(PD_PPS_CTRL_TIMEOUT)))
+ if (!wait_for_completion_timeout(&port->aug_supply_req_complete,
+ msecs_to_jiffies(PD_AUG_PSY_CTRL_TIMEOUT)))
ret = -ETIMEDOUT;
else
- ret = port->pps_status;
+ ret = port->aug_supply_req_status;
goto swap_unlock;
@@ -6760,7 +7300,7 @@ swap_unlock:
return ret;
}
-static int tcpm_pps_set_out_volt(struct tcpm_port *port, u16 req_out_volt)
+static int tcpm_aug_set_out_volt(struct tcpm_port *port, u16 req_out_volt_mv)
{
unsigned int target_mw;
int ret;
@@ -6768,7 +7308,16 @@ static int tcpm_pps_set_out_volt(struct tcpm_port *port, u16 req_out_volt)
mutex_lock(&port->swap_lock);
mutex_lock(&port->lock);
- if (!port->pps_data.active) {
+ if (port->pps_data.active) {
+ req_out_volt_mv = req_out_volt_mv - (req_out_volt_mv %
+ RDO_PROG_VOLT_MV_STEP);
+ /* Round down output voltage to align with PPS valid steps */
+ target_mw = (port->current_limit * req_out_volt_mv) / 1000;
+ if (target_mw < port->operating_snk_mw) {
+ ret = -EINVAL;
+ goto port_unlock;
+ }
+ } else if (!port->spr_avs_data.active) {
ret = -EOPNOTSUPP;
goto port_unlock;
}
@@ -6778,33 +7327,31 @@ static int tcpm_pps_set_out_volt(struct tcpm_port *port, u16 req_out_volt)
goto port_unlock;
}
- target_mw = (port->current_limit * req_out_volt) / 1000;
- if (target_mw < port->operating_snk_mw) {
- ret = -EINVAL;
- goto port_unlock;
- }
+ if (port->pps_data.active)
+ port->upcoming_state = SNK_NEGOTIATE_PPS_CAPABILITIES;
+ else
+ port->upcoming_state = SNK_NEGOTIATE_SPR_AVS_CAPABILITIES;
- port->upcoming_state = SNK_NEGOTIATE_PPS_CAPABILITIES;
ret = tcpm_ams_start(port, POWER_NEGOTIATION);
if (ret == -EAGAIN) {
port->upcoming_state = INVALID_STATE;
goto port_unlock;
}
- /* Round down output voltage to align with PPS valid steps */
- req_out_volt = req_out_volt - (req_out_volt % RDO_PROG_VOLT_MV_STEP);
-
- reinit_completion(&port->pps_complete);
- port->pps_data.req_out_volt = req_out_volt;
- port->pps_status = 0;
- port->pps_pending = true;
+ reinit_completion(&port->aug_supply_req_complete);
+ if (port->pps_data.active)
+ port->pps_data.req_out_volt = req_out_volt_mv;
+ else
+ port->spr_avs_data.req_out_volt_mv = req_out_volt_mv;
+ port->aug_supply_req_status = 0;
+ port->aug_supply_req_pending = true;
mutex_unlock(&port->lock);
- if (!wait_for_completion_timeout(&port->pps_complete,
- msecs_to_jiffies(PD_PPS_CTRL_TIMEOUT)))
+ if (!wait_for_completion_timeout(&port->aug_supply_req_complete,
+ msecs_to_jiffies(PD_AUG_PSY_CTRL_TIMEOUT)))
ret = -ETIMEDOUT;
else
- ret = port->pps_status;
+ ret = port->aug_supply_req_status;
goto swap_unlock;
@@ -6847,9 +7394,9 @@ static int tcpm_pps_activate(struct tcpm_port *port, bool activate)
goto port_unlock;
}
- reinit_completion(&port->pps_complete);
- port->pps_status = 0;
- port->pps_pending = true;
+ reinit_completion(&port->aug_supply_req_complete);
+ port->aug_supply_req_status = 0;
+ port->aug_supply_req_pending = true;
/* Trigger PPS request or move back to standard PDO contract */
if (activate) {
@@ -6858,11 +7405,75 @@ static int tcpm_pps_activate(struct tcpm_port *port, bool activate)
}
mutex_unlock(&port->lock);
- if (!wait_for_completion_timeout(&port->pps_complete,
- msecs_to_jiffies(PD_PPS_CTRL_TIMEOUT)))
+ if (!wait_for_completion_timeout(&port->aug_supply_req_complete,
+ msecs_to_jiffies(PD_AUG_PSY_CTRL_TIMEOUT)))
+ ret = -ETIMEDOUT;
+ else
+ ret = port->aug_supply_req_status;
+
+ goto swap_unlock;
+
+port_unlock:
+ mutex_unlock(&port->lock);
+swap_unlock:
+ mutex_unlock(&port->swap_lock);
+
+ return ret;
+}
+
+static int tcpm_spr_avs_activate(struct tcpm_port *port, bool activate)
+{
+ int ret = 0;
+
+ mutex_lock(&port->swap_lock);
+ mutex_lock(&port->lock);
+
+ if (port->spr_avs_data.port_snk_status == SPR_AVS_NOT_SUPPORTED ||
+ port->spr_avs_data.port_partner_src_status == SPR_AVS_NOT_SUPPORTED) {
+ tcpm_log(port, "SPR_AVS not supported");
+ ret = -EOPNOTSUPP;
+ goto port_unlock;
+ }
+
+ /* Trying to deactivate SPR AVS when already deactivated so just bail */
+ if (!port->spr_avs_data.active && !activate)
+ goto port_unlock;
+
+ if (port->state != SNK_READY) {
+ tcpm_log(port,
+ "SPR_AVS cannot be activated. Port not in SNK_READY");
+ ret = -EAGAIN;
+ goto port_unlock;
+ }
+
+ if (activate)
+ port->upcoming_state = SNK_NEGOTIATE_SPR_AVS_CAPABILITIES;
+ else
+ port->upcoming_state = SNK_NEGOTIATE_CAPABILITIES;
+ ret = tcpm_ams_start(port, POWER_NEGOTIATION);
+ if (ret == -EAGAIN) {
+ tcpm_log(port, "SPR_AVS cannot be %s. AMS start failed",
+ activate ? "activated" : "deactivated");
+ port->upcoming_state = INVALID_STATE;
+ goto port_unlock;
+ }
+
+ reinit_completion(&port->aug_supply_req_complete);
+ port->aug_supply_req_status = 0;
+ port->aug_supply_req_pending = true;
+
+ /* Trigger AVS request or move back to standard PDO contract */
+ if (activate) {
+ port->spr_avs_data.req_out_volt_mv = port->supply_voltage;
+ port->spr_avs_data.req_op_curr_ma = port->current_limit;
+ }
+ mutex_unlock(&port->lock);
+
+ if (!wait_for_completion_timeout(&port->aug_supply_req_complete,
+ msecs_to_jiffies(PD_AUG_PSY_CTRL_TIMEOUT)))
ret = -ETIMEDOUT;
else
- ret = port->pps_status;
+ ret = port->aug_supply_req_status;
goto swap_unlock;
@@ -7018,16 +7629,26 @@ static int tcpm_pd_set(struct typec_port *p, struct usb_power_delivery *pd)
break;
case SNK_NEGOTIATE_CAPABILITIES:
case SNK_NEGOTIATE_PPS_CAPABILITIES:
+ case SNK_NEGOTIATE_SPR_AVS_CAPABILITIES:
case SNK_READY:
case SNK_TRANSITION_SINK:
case SNK_TRANSITION_SINK_VBUS:
- if (port->pps_data.active)
+ if (port->pps_data.active) {
port->upcoming_state = SNK_NEGOTIATE_PPS_CAPABILITIES;
- else if (port->pd_capable)
+ } else if (port->pd_capable) {
port->upcoming_state = SNK_NEGOTIATE_CAPABILITIES;
- else
+ if (port->spr_avs_data.active) {
+ /*
+ * De-activate AVS and fallback to PD to
+ * re-evaluate whether AVS is supported in the
+ * current sink cap set.
+ */
+ port->spr_avs_data.active = false;
+ port->spr_avs_data.port_snk_status = SPR_AVS_UNKNOWN;
+ }
+ } else {
break;
-
+ }
port->update_sink_caps = true;
ret = tcpm_ams_start(port, POWER_NEGOTIATION);
@@ -7164,9 +7785,132 @@ static void tcpm_fw_get_timings(struct tcpm_port *port, struct fwnode_handle *fw
port->timings.snk_bc12_cmpletion_time = val;
}
+static void tcpm_fw_get_pd_ident(struct tcpm_port *port)
+{
+ struct pd_identifier *pd_ident = &port->pd_ident;
+ u32 *vdo;
+
+ /* First 3 vdo values contain info regarding USB PID, VID & XID */
+ if (port->nr_snk_vdo >= 3)
+ vdo = port->snk_vdo;
+ else if (port->nr_snk_vdo_v1 >= 3)
+ vdo = port->snk_vdo_v1;
+ else
+ return;
+
+ pd_ident->vid = PD_IDH_VID(vdo[0]);
+ pd_ident->pid = PD_PRODUCT_PID(vdo[2]);
+ pd_ident->xid = PD_CSTAT_XID(vdo[1]);
+ tcpm_log(port, "vid:%#x pid:%#x xid:%#x",
+ pd_ident->vid, pd_ident->pid, pd_ident->xid);
+}
+
+static void tcpm_parse_snk_pdos(struct tcpm_port *port)
+{
+ struct sink_caps_ext_data *caps = &port->sink_caps_ext;
+ u32 max_mv, max_ma;
+ u8 avs_tier1_pdp, avs_tier2_pdp;
+ int i, pdo_itr;
+ u32 *snk_pdos;
+
+ for (i = 0; i < port->pd_count; ++i) {
+ snk_pdos = port->pd_list[i]->sink_desc.pdo;
+ for (pdo_itr = 0; pdo_itr < PDO_MAX_OBJECTS && snk_pdos[pdo_itr];
+ ++pdo_itr) {
+ u32 pdo = snk_pdos[pdo_itr];
+ u8 curr_snk_pdp = 0;
+
+ switch (pdo_type(pdo)) {
+ case PDO_TYPE_FIXED:
+ max_mv = pdo_fixed_voltage(pdo);
+ max_ma = pdo_fixed_current(pdo);
+ curr_snk_pdp = UW_TO_W(max_mv * max_ma);
+ break;
+ case PDO_TYPE_BATT:
+ curr_snk_pdp = UW_TO_W(pdo_max_power(pdo));
+ break;
+ case PDO_TYPE_VAR:
+ max_mv = pdo_max_voltage(pdo);
+ max_ma = pdo_max_current(pdo);
+ curr_snk_pdp = UW_TO_W(max_mv * max_ma);
+ break;
+ case PDO_TYPE_APDO:
+ if (pdo_apdo_type(pdo) == APDO_TYPE_PPS) {
+ max_mv = pdo_pps_apdo_max_voltage(pdo);
+ max_ma = pdo_pps_apdo_max_current(pdo);
+ curr_snk_pdp = UW_TO_W(max_mv * max_ma);
+ caps->modes |= SINK_MODE_PPS;
+ } else if (pdo_apdo_type(pdo) ==
+ APDO_TYPE_SPR_AVS) {
+ avs_tier1_pdp = UW_TO_W(SPR_AVS_TIER1_MAX_VOLT_MV
+ * pdo_spr_avs_apdo_9v_to_15v_max_current_ma(pdo));
+ avs_tier2_pdp = UW_TO_W(SPR_AVS_TIER2_MAX_VOLT_MV
+ * pdo_spr_avs_apdo_15v_to_20v_max_current_ma(pdo));
+ curr_snk_pdp = max(avs_tier1_pdp, avs_tier2_pdp);
+ caps->modes |= SINK_MODE_AVS;
+ }
+ break;
+ default:
+ tcpm_log(port, "Invalid source PDO type, ignoring");
+ continue;
+ }
+
+ caps->spr_max_pdp = max(caps->spr_max_pdp,
+ curr_snk_pdp);
+ }
+ }
+}
+
+static void tcpm_fw_get_sink_caps_ext(struct tcpm_port *port,
+ struct fwnode_handle *fwnode)
+{
+ struct sink_caps_ext_data *caps = &port->sink_caps_ext;
+ int ret;
+ u32 val;
+
+ /*
+ * Load step represents the change in current per usec that a given
+ * source can tolerate while maintaining Vbus within the vSrcValid
+ * range. For a sink this represents the "preferred" load-step value. It
+ * can only have 2 values (150 mA/usec or 500 mA/usec) with 150 mA/usec
+ * being the default.
+ */
+ ret = fwnode_property_read_u32(fwnode, "sink-load-step", &val);
+ if (!ret)
+ caps->load_step = val == 500 ? 1 : 0;
+
+ fwnode_property_read_u16(fwnode, "sink-load-characteristics",
+ &caps->load_char);
+ fwnode_property_read_u8(fwnode, "sink-compliance", &caps->compliance);
+ caps->modes = SINK_MODE_VBUS;
+
+ /*
+ * As per "6.5.13.14" SPR Sink Operational PDP definition, for battery
+ * powered devices, this value will correspond to the PDP of the
+ * charging adapter either shipped or recommended for use with it. For
+ * batteryless sink devices SPR Operational PDP indicates the power
+ * required to operate all the device's functional modes. Hence, this
+ * value may be considered equal to port's operating_snk_mw. As
+ * operating_sink_mw can change as per the pd set used thus, OP PDP
+ * is determined when populating Sink Caps Extended Data Block.
+ */
+ if (port->self_powered) {
+ fwnode_property_read_u32(fwnode, "charging-adapter-pdp-milliwatt",
+ &val);
+ caps->spr_op_pdp = (u8)(val / 1000);
+ caps->modes |= SINK_MODE_BATT;
+ }
+
+ tcpm_parse_snk_pdos(port);
+ tcpm_log(port,
+ "load-step:%#x load-char:%#x compl:%#x op-pdp:%#x max-pdp:%#x",
+ caps->load_step, caps->load_char, caps->compliance,
+ caps->spr_op_pdp, caps->spr_max_pdp);
+}
+
static int tcpm_fw_get_caps(struct tcpm_port *port, struct fwnode_handle *fwnode)
{
- struct fwnode_handle *capabilities, *child, *caps = NULL;
+ struct fwnode_handle *capabilities, *caps = NULL;
unsigned int nr_src_pdo, nr_snk_pdo;
const char *opmode_str;
u32 *src_pdo, *snk_pdo;
@@ -7232,9 +7976,7 @@ static int tcpm_fw_get_caps(struct tcpm_port *port, struct fwnode_handle *fwnode
if (!capabilities) {
port->pd_count = 1;
} else {
- fwnode_for_each_child_node(capabilities, child)
- port->pd_count++;
-
+ port->pd_count = fwnode_get_child_node_count(capabilities);
if (!port->pd_count) {
ret = -ENODATA;
goto put_capabilities;
@@ -7339,6 +8081,9 @@ static int tcpm_fw_get_caps(struct tcpm_port *port, struct fwnode_handle *fwnode
}
}
+ if (port->port_type != TYPEC_PORT_SRC)
+ tcpm_fw_get_sink_caps_ext(port, fwnode);
+
put_caps:
if (caps != fwnode)
fwnode_handle_put(caps);
@@ -7381,6 +8126,8 @@ static int tcpm_fw_get_snk_vdos(struct tcpm_port *port, struct fwnode_handle *fw
return ret;
}
+ tcpm_fw_get_pd_ident(port);
+
return 0;
}
@@ -7411,7 +8158,8 @@ static void tcpm_fw_get_pd_revision(struct tcpm_port *port, struct fwnode_handle
enum tcpm_psy_online_states {
TCPM_PSY_OFFLINE = 0,
TCPM_PSY_FIXED_ONLINE,
- TCPM_PSY_PROG_ONLINE,
+ TCPM_PSY_PPS_ONLINE,
+ TCPM_PSY_SPR_AVS_ONLINE,
};
static enum power_supply_property tcpm_psy_props[] = {
@@ -7429,7 +8177,9 @@ static int tcpm_psy_get_online(struct tcpm_port *port,
{
if (port->vbus_charge) {
if (port->pps_data.active)
- val->intval = TCPM_PSY_PROG_ONLINE;
+ val->intval = TCPM_PSY_PPS_ONLINE;
+ else if (port->spr_avs_data.active)
+ val->intval = TCPM_PSY_SPR_AVS_ONLINE;
else
val->intval = TCPM_PSY_FIXED_ONLINE;
} else {
@@ -7444,6 +8194,8 @@ static int tcpm_psy_get_voltage_min(struct tcpm_port *port,
{
if (port->pps_data.active)
val->intval = port->pps_data.min_volt * 1000;
+ else if (port->spr_avs_data.active)
+ val->intval = SPR_AVS_TIER1_MIN_VOLT_MV * 1000;
else
val->intval = port->supply_voltage * 1000;
@@ -7455,6 +8207,8 @@ static int tcpm_psy_get_voltage_max(struct tcpm_port *port,
{
if (port->pps_data.active)
val->intval = port->pps_data.max_volt * 1000;
+ else if (port->spr_avs_data.active)
+ val->intval = port->spr_avs_data.max_out_volt_mv * 1000;
else
val->intval = port->supply_voltage * 1000;
@@ -7474,6 +8228,8 @@ static int tcpm_psy_get_current_max(struct tcpm_port *port,
{
if (port->pps_data.active)
val->intval = port->pps_data.max_curr * 1000;
+ else if (port->spr_avs_data.active)
+ val->intval = port->spr_avs_data.max_current_ma * 1000;
else
val->intval = port->current_limit * 1000;
@@ -7549,17 +8305,41 @@ static int tcpm_psy_get_prop(struct power_supply *psy,
return ret;
}
+static int tcpm_disable_pps_avs(struct tcpm_port *port)
+{
+ int ret = 0;
+
+ if (port->pps_data.active)
+ ret = tcpm_pps_activate(port, false);
+ else if (port->spr_avs_data.active)
+ ret = tcpm_spr_avs_activate(port, false);
+
+ return ret;
+}
+
static int tcpm_psy_set_online(struct tcpm_port *port,
const union power_supply_propval *val)
{
- int ret;
+ int ret = 0;
switch (val->intval) {
case TCPM_PSY_FIXED_ONLINE:
- ret = tcpm_pps_activate(port, false);
+ ret = tcpm_disable_pps_avs(port);
+ break;
+ case TCPM_PSY_PPS_ONLINE:
+ if (port->spr_avs_data.active)
+ ret = tcpm_spr_avs_activate(port, false);
+ if (!ret)
+ ret = tcpm_pps_activate(port, true);
break;
- case TCPM_PSY_PROG_ONLINE:
- ret = tcpm_pps_activate(port, true);
+ case TCPM_PSY_SPR_AVS_ONLINE:
+ tcpm_log(port, "request to set AVS online");
+ if (port->spr_avs_data.active)
+ return 0;
+ ret = tcpm_disable_pps_avs(port);
+ if (ret)
+ break;
+ ret = tcpm_spr_avs_activate(port, true);
break;
default:
ret = -EINVAL;
@@ -7588,13 +8368,10 @@ static int tcpm_psy_set_prop(struct power_supply *psy,
ret = tcpm_psy_set_online(port, val);
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
- ret = tcpm_pps_set_out_volt(port, val->intval / 1000);
+ ret = tcpm_aug_set_out_volt(port, val->intval / 1000);
break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
- if (val->intval > port->pps_data.max_curr * 1000)
- ret = -EINVAL;
- else
- ret = tcpm_pps_set_op_curr(port, val->intval / 1000);
+ ret = tcpm_aug_set_op_curr(port, val->intval / 1000);
break;
default:
ret = -EINVAL;
@@ -7639,7 +8416,9 @@ static int devm_tcpm_psy_register(struct tcpm_port *port)
port->psy_desc.type = POWER_SUPPLY_TYPE_USB;
port->psy_desc.usb_types = BIT(POWER_SUPPLY_USB_TYPE_C) |
BIT(POWER_SUPPLY_USB_TYPE_PD) |
- BIT(POWER_SUPPLY_USB_TYPE_PD_PPS);
+ BIT(POWER_SUPPLY_USB_TYPE_PD_PPS) |
+ BIT(POWER_SUPPLY_USB_TYPE_PD_PPS_SPR_AVS) |
+ BIT(POWER_SUPPLY_USB_TYPE_PD_SPR_AVS);
port->psy_desc.properties = tcpm_psy_props;
port->psy_desc.num_properties = ARRAY_SIZE(tcpm_psy_props);
port->psy_desc.get_property = tcpm_psy_get_prop;
@@ -7721,20 +8500,20 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
kthread_init_work(&port->event_work, tcpm_pd_event_handler);
kthread_init_work(&port->enable_frs, tcpm_enable_frs_work);
kthread_init_work(&port->send_discover_work, tcpm_send_discover_work);
- hrtimer_init(&port->state_machine_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- port->state_machine_timer.function = state_machine_timer_handler;
- hrtimer_init(&port->vdm_state_machine_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- port->vdm_state_machine_timer.function = vdm_state_machine_timer_handler;
- hrtimer_init(&port->enable_frs_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- port->enable_frs_timer.function = enable_frs_timer_handler;
- hrtimer_init(&port->send_discover_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- port->send_discover_timer.function = send_discover_timer_handler;
+ hrtimer_setup(&port->state_machine_timer, state_machine_timer_handler, CLOCK_MONOTONIC,
+ HRTIMER_MODE_REL);
+ hrtimer_setup(&port->vdm_state_machine_timer, vdm_state_machine_timer_handler,
+ CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ hrtimer_setup(&port->enable_frs_timer, enable_frs_timer_handler, CLOCK_MONOTONIC,
+ HRTIMER_MODE_REL);
+ hrtimer_setup(&port->send_discover_timer, send_discover_timer_handler, CLOCK_MONOTONIC,
+ HRTIMER_MODE_REL);
spin_lock_init(&port->pd_event_lock);
init_completion(&port->tx_complete);
init_completion(&port->swap_complete);
- init_completion(&port->pps_complete);
+ init_completion(&port->aug_supply_req_complete);
tcpm_debugfs_init(port);
err = tcpm_fw_get_caps(port, tcpc->fwnode);
@@ -7764,9 +8543,9 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
port->partner_desc.identity = &port->partner_ident;
- port->role_sw = usb_role_switch_get(port->dev);
+ port->role_sw = fwnode_usb_role_switch_get(tcpc->fwnode);
if (!port->role_sw)
- port->role_sw = fwnode_usb_role_switch_get(tcpc->fwnode);
+ port->role_sw = usb_role_switch_get(port->dev);
if (IS_ERR(port->role_sw)) {
err = PTR_ERR(port->role_sw);
goto out_destroy_wq;
diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c
index 7ee721a877c1..43faec794b95 100644
--- a/drivers/usb/typec/tipd/core.c
+++ b/drivers/usb/typec/tipd/core.c
@@ -16,6 +16,9 @@
#include <linux/interrupt.h>
#include <linux/usb/typec.h>
#include <linux/usb/typec_altmode.h>
+#include <linux/usb/typec_dp.h>
+#include <linux/usb/typec_mux.h>
+#include <linux/usb/typec_tbt.h>
#include <linux/usb/role.h>
#include <linux/workqueue.h>
#include <linux/firmware.h>
@@ -35,14 +38,18 @@
#define TPS_REG_INT_MASK2 0x17
#define TPS_REG_INT_CLEAR1 0x18
#define TPS_REG_INT_CLEAR2 0x19
-#define TPS_REG_SYSTEM_POWER_STATE 0x20
#define TPS_REG_STATUS 0x1a
+#define TPS_REG_SYSTEM_POWER_STATE 0x20
+#define TPS_REG_USB4_STATUS 0x24
#define TPS_REG_SYSTEM_CONF 0x28
#define TPS_REG_CTRL_CONF 0x29
#define TPS_REG_BOOT_STATUS 0x2D
#define TPS_REG_POWER_STATUS 0x3f
#define TPS_REG_PD_STATUS 0x40
#define TPS_REG_RX_IDENTITY_SOP 0x48
+#define TPS_REG_CF_VID_STATUS 0x5e
+#define TPS_REG_DP_SID_STATUS 0x58
+#define TPS_REG_INTEL_VID_STATUS 0x59
#define TPS_REG_DATA_STATUS 0x5f
#define TPS_REG_SLEEP_CONF 0x70
@@ -85,10 +92,38 @@ struct tps6598x_rx_identity_reg {
struct usb_pd_identity identity;
} __packed;
+/* TPS_REG_USB4_STATUS */
+struct tps6598x_usb4_status_reg {
+ u8 mode_status;
+ __le32 eudo;
+ __le32 unknown;
+} __packed;
+
+/* TPS_REG_DP_SID_STATUS */
+struct tps6598x_dp_sid_status_reg {
+ u8 mode_status;
+ __le32 status_tx;
+ __le32 status_rx;
+ __le32 configure;
+ __le32 mode_data;
+} __packed;
+
+/* TPS_REG_INTEL_VID_STATUS */
+struct tps6598x_intel_vid_status_reg {
+ u8 mode_status;
+ __le32 attention_vdo;
+ __le16 enter_vdo;
+ __le16 device_mode;
+ __le16 cable_mode;
+} __packed;
+
/* Standard Task return codes */
#define TPS_TASK_TIMEOUT 1
#define TPS_TASK_REJECTED 3
+/* Debounce delay for mode changes, in milliseconds */
+#define CD321X_DEBOUNCE_DELAY_MS 500
+
enum {
TPS_MODE_APP,
TPS_MODE_BOOT,
@@ -112,12 +147,20 @@ struct tps6598x;
struct tipd_data {
irq_handler_t irq_handler;
+ u64 irq_mask1;
+ size_t tps_struct_size;
+ void (*remove)(struct tps6598x *tps);
int (*register_port)(struct tps6598x *tps, struct fwnode_handle *node);
+ void (*unregister_port)(struct tps6598x *tps);
+ void (*trace_data_status)(u32 status);
void (*trace_power_status)(u16 status);
void (*trace_status)(u32 status);
int (*apply_patch)(struct tps6598x *tps);
int (*init)(struct tps6598x *tps);
+ int (*switch_power_state)(struct tps6598x *tps, u8 target_state);
+ bool (*read_data_status)(struct tps6598x *tps);
int (*reset)(struct tps6598x *tps);
+ int (*connect)(struct tps6598x *tps, u32 status);
};
struct tps6598x {
@@ -139,12 +182,42 @@ struct tps6598x {
int wakeup;
u32 status; /* status reg */
+ u32 data_status;
u16 pwr_status;
struct delayed_work wq_poll;
const struct tipd_data *data;
};
+struct cd321x_status {
+ u32 status;
+ u32 pwr_status;
+ u32 data_status;
+ u32 status_changed;
+ struct usb_pd_identity partner_identity;
+ struct tps6598x_dp_sid_status_reg dp_sid_status;
+ struct tps6598x_intel_vid_status_reg intel_vid_status;
+ struct tps6598x_usb4_status_reg usb4_status;
+};
+
+struct cd321x {
+ struct tps6598x tps;
+
+ struct tps6598x_dp_sid_status_reg dp_sid_status;
+ struct tps6598x_intel_vid_status_reg intel_vid_status;
+ struct tps6598x_usb4_status_reg usb4_status;
+
+ struct typec_altmode *port_altmode_dp;
+ struct typec_altmode *port_altmode_tbt;
+
+ struct typec_mux *mux;
+ struct typec_mux_state state;
+
+ struct cd321x_status update_status;
+ struct delayed_work update_work;
+ struct usb_pd_identity cur_partner_identity;
+};
+
static enum power_supply_property tps6598x_psy_props[] = {
POWER_SUPPLY_PROP_USB_TYPE,
POWER_SUPPLY_PROP_ONLINE,
@@ -490,7 +563,51 @@ static bool tps6598x_read_data_status(struct tps6598x *tps)
dev_err(tps->dev, "failed to read data status: %d\n", ret);
return false;
}
- trace_tps6598x_data_status(data_status);
+ tps->data_status = data_status;
+
+ if (tps->data->trace_data_status)
+ tps->data->trace_data_status(data_status);
+
+ return true;
+}
+
+static bool cd321x_read_data_status(struct tps6598x *tps)
+{
+ struct cd321x *cd321x = container_of(tps, struct cd321x, tps);
+ int ret;
+
+ ret = tps6598x_read_data_status(tps);
+ if (!ret)
+ return false;
+
+ if (tps->data_status & TPS_DATA_STATUS_DP_CONNECTION) {
+ ret = tps6598x_block_read(tps, TPS_REG_DP_SID_STATUS,
+ &cd321x->dp_sid_status, sizeof(cd321x->dp_sid_status));
+ if (ret) {
+ dev_err(tps->dev, "Failed to read DP SID Status: %d\n",
+ ret);
+ return false;
+ }
+ }
+
+ if (tps->data_status & TPS_DATA_STATUS_TBT_CONNECTION) {
+ ret = tps6598x_block_read(tps, TPS_REG_INTEL_VID_STATUS,
+ &cd321x->intel_vid_status, sizeof(cd321x->intel_vid_status));
+ if (ret) {
+ dev_err(tps->dev, "Failed to read Intel VID Status: %d\n", ret);
+ return false;
+ }
+ }
+
+ if (tps->data_status & CD321X_DATA_STATUS_USB4_CONNECTION) {
+ ret = tps6598x_block_read(tps, TPS_REG_USB4_STATUS,
+ &cd321x->usb4_status, sizeof(cd321x->usb4_status));
+ if (ret) {
+ dev_err(tps->dev,
+ "Failed to read USB4 Status: %d\n", ret);
+ return false;
+ }
+ }
return true;
}
@@ -526,6 +643,235 @@ static void tps6598x_handle_plug_event(struct tps6598x *tps, u32 status)
}
}
+static void cd321x_typec_update_mode(struct tps6598x *tps, struct cd321x_status *st)
+{
+ struct cd321x *cd321x = container_of(tps, struct cd321x, tps);
+
+ if (!(st->data_status & TPS_DATA_STATUS_DATA_CONNECTION)) {
+ if (cd321x->state.mode == TYPEC_STATE_SAFE)
+ return;
+ cd321x->state.alt = NULL;
+ cd321x->state.mode = TYPEC_STATE_SAFE;
+ cd321x->state.data = NULL;
+ typec_mux_set(cd321x->mux, &cd321x->state);
+ } else if (st->data_status & TPS_DATA_STATUS_DP_CONNECTION) {
+ struct typec_displayport_data dp_data;
+ unsigned long mode;
+
+ switch (TPS_DATA_STATUS_DP_SPEC_PIN_ASSIGNMENT(st->data_status)) {
+ case TPS_DATA_STATUS_DP_SPEC_PIN_ASSIGNMENT_A:
+ mode = TYPEC_DP_STATE_A;
+ break;
+ case TPS_DATA_STATUS_DP_SPEC_PIN_ASSIGNMENT_B:
+ mode = TYPEC_DP_STATE_B;
+ break;
+ case TPS_DATA_STATUS_DP_SPEC_PIN_ASSIGNMENT_C:
+ mode = TYPEC_DP_STATE_C;
+ break;
+ case TPS_DATA_STATUS_DP_SPEC_PIN_ASSIGNMENT_D:
+ mode = TYPEC_DP_STATE_D;
+ break;
+ case TPS_DATA_STATUS_DP_SPEC_PIN_ASSIGNMENT_E:
+ mode = TYPEC_DP_STATE_E;
+ break;
+ case TPS_DATA_STATUS_DP_SPEC_PIN_ASSIGNMENT_F:
+ mode = TYPEC_DP_STATE_F;
+ break;
+ default:
+ dev_err(tps->dev, "Invalid DP pin assignment\n");
+ return;
+ }
+
+ if (cd321x->state.alt == cd321x->port_altmode_dp &&
+ cd321x->state.mode == mode) {
+ return;
+ }
+
+ dp_data.status = le32_to_cpu(st->dp_sid_status.status_rx);
+ dp_data.conf = le32_to_cpu(st->dp_sid_status.configure);
+ cd321x->state.alt = cd321x->port_altmode_dp;
+ cd321x->state.data = &dp_data;
+ cd321x->state.mode = mode;
+ typec_mux_set(cd321x->mux, &cd321x->state);
+ } else if (st->data_status & TPS_DATA_STATUS_TBT_CONNECTION) {
+ struct typec_thunderbolt_data tbt_data;
+
+ if (cd321x->state.alt == cd321x->port_altmode_tbt &&
+ cd321x->state.mode == TYPEC_TBT_MODE)
+ return;
+
+ tbt_data.cable_mode = le16_to_cpu(st->intel_vid_status.cable_mode);
+ tbt_data.device_mode = le16_to_cpu(st->intel_vid_status.device_mode);
+ tbt_data.enter_vdo = le16_to_cpu(st->intel_vid_status.enter_vdo);
+ cd321x->state.alt = cd321x->port_altmode_tbt;
+ cd321x->state.mode = TYPEC_TBT_MODE;
+ cd321x->state.data = &tbt_data;
+ typec_mux_set(cd321x->mux, &cd321x->state);
+ } else if (st->data_status & CD321X_DATA_STATUS_USB4_CONNECTION) {
+ struct enter_usb_data eusb_data;
+
+ if (cd321x->state.alt == NULL && cd321x->state.mode == TYPEC_MODE_USB4)
+ return;
+
+ eusb_data.eudo = le32_to_cpu(st->usb4_status.eudo);
+ eusb_data.active_link_training =
+ !!(st->data_status & TPS_DATA_STATUS_ACTIVE_LINK_TRAIN);
+
+ cd321x->state.alt = NULL;
+ cd321x->state.data = &eusb_data;
+ cd321x->state.mode = TYPEC_MODE_USB4;
+ typec_mux_set(cd321x->mux, &cd321x->state);
+ } else {
+ if (cd321x->state.alt == NULL && cd321x->state.mode == TYPEC_STATE_USB)
+ return;
+ cd321x->state.alt = NULL;
+ cd321x->state.mode = TYPEC_STATE_USB;
+ cd321x->state.data = NULL;
+ typec_mux_set(cd321x->mux, &cd321x->state);
+ }
+
+ /* Clear data since it's no longer used after typec_mux_set and points to the stack */
+ cd321x->state.data = NULL;
+}
+
+static void cd321x_update_work(struct work_struct *work)
+{
+ struct cd321x *cd321x = container_of(to_delayed_work(work),
+ struct cd321x, update_work);
+ struct tps6598x *tps = &cd321x->tps;
+ struct cd321x_status st;
+
+ guard(mutex)(&tps->lock);
+
+ st = cd321x->update_status;
+ cd321x->update_status.status_changed = 0;
+
+ bool old_connected = !!tps->partner;
+ bool new_connected = st.status & TPS_STATUS_PLUG_PRESENT;
+ bool was_disconnected = st.status_changed & TPS_STATUS_PLUG_PRESENT;
+
+ bool usb_connection = st.data_status &
+ (TPS_DATA_STATUS_USB2_CONNECTION | TPS_DATA_STATUS_USB3_CONNECTION);
+
+ enum usb_role old_role = usb_role_switch_get_role(tps->role_sw);
+ enum usb_role new_role = USB_ROLE_NONE;
+ enum typec_pwr_opmode pwr_opmode = TYPEC_PWR_MODE_USB;
+ enum typec_orientation orientation = TYPEC_ORIENTATION_NONE;
+
+ if (usb_connection) {
+ if (tps->data_status & TPS_DATA_STATUS_USB_DATA_ROLE)
+ new_role = USB_ROLE_DEVICE;
+ else
+ new_role = USB_ROLE_HOST;
+ }
+
+ if (new_connected) {
+ pwr_opmode = TPS_POWER_STATUS_PWROPMODE(st.pwr_status);
+ orientation = TPS_STATUS_TO_UPSIDE_DOWN(st.status) ?
+ TYPEC_ORIENTATION_REVERSE : TYPEC_ORIENTATION_NORMAL;
+ }
+
+ bool is_pd = pwr_opmode == TYPEC_PWR_MODE_PD;
+ bool partner_changed = old_connected && new_connected &&
+ (was_disconnected ||
+ (is_pd && memcmp(&st.partner_identity,
+ &cd321x->cur_partner_identity, sizeof(struct usb_pd_identity))));
+
+ /* If we are switching from an active role, transition to USB_ROLE_NONE first */
+ if (old_role != USB_ROLE_NONE && (new_role != old_role || was_disconnected))
+ usb_role_switch_set_role(tps->role_sw, USB_ROLE_NONE);
+
+ /* Process partner disconnection or change */
+ if (!new_connected || partner_changed) {
+ if (!IS_ERR(tps->partner))
+ typec_unregister_partner(tps->partner);
+ tps->partner = NULL;
+ }
+
+ /* If there was a disconnection, set PHY to off */
+ if (!new_connected || was_disconnected) {
+ cd321x->state.alt = NULL;
+ cd321x->state.mode = TYPEC_STATE_SAFE;
+ cd321x->state.data = NULL;
+ typec_set_mode(tps->port, TYPEC_STATE_SAFE);
+ }
+
+ /* Update Type-C properties */
+ typec_set_pwr_opmode(tps->port, pwr_opmode);
+ typec_set_pwr_role(tps->port, TPS_STATUS_TO_TYPEC_PORTROLE(st.status));
+ typec_set_vconn_role(tps->port, TPS_STATUS_TO_TYPEC_VCONN(st.status));
+ typec_set_orientation(tps->port, orientation);
+ typec_set_data_role(tps->port, TPS_STATUS_TO_TYPEC_DATAROLE(st.status));
+ power_supply_changed(tps->psy);
+
+ /* If the plug is disconnected, we are done */
+ if (!new_connected)
+ return;
+
+ /* Set up partner if we were previously disconnected (or changed). */
+ if (!tps->partner) {
+ struct typec_partner_desc desc;
+
+ desc.usb_pd = is_pd;
+ desc.accessory = TYPEC_ACCESSORY_NONE; /* XXX: handle accessories */
+ desc.identity = NULL;
+
+ if (desc.usb_pd)
+ desc.identity = &st.partner_identity;
+
+ tps->partner = typec_register_partner(tps->port, &desc);
+ if (IS_ERR(tps->partner)) {
+ dev_warn(tps->dev, "%s: failed to register partner\n", __func__);
+ return;
+ }
+
+ if (desc.identity) {
+ typec_partner_set_identity(tps->partner);
+ cd321x->cur_partner_identity = st.partner_identity;
+ }
+ }
+
+ /* Update the TypeC MUX/PHY state */
+ cd321x_typec_update_mode(tps, &st);
+
+ /* Launch the USB role switch */
+ usb_role_switch_set_role(tps->role_sw, new_role);
+
+ power_supply_changed(tps->psy);
+}
+
+static void cd321x_queue_status(struct cd321x *cd321x)
+{
+ cd321x->update_status.status_changed |= cd321x->update_status.status ^ cd321x->tps.status;
+
+ cd321x->update_status.status = cd321x->tps.status;
+ cd321x->update_status.pwr_status = cd321x->tps.pwr_status;
+ cd321x->update_status.data_status = cd321x->tps.data_status;
+
+ cd321x->update_status.partner_identity = cd321x->tps.partner_identity;
+ cd321x->update_status.dp_sid_status = cd321x->dp_sid_status;
+ cd321x->update_status.intel_vid_status = cd321x->intel_vid_status;
+ cd321x->update_status.usb4_status = cd321x->usb4_status;
+}
+
+static int cd321x_connect(struct tps6598x *tps, u32 status)
+{
+ struct cd321x *cd321x = container_of(tps, struct cd321x, tps);
+
+ tps->status = status;
+ cd321x_queue_status(cd321x);
+
+ /*
+ * Cancel pending work if not already running, then requeue after CD321X_DEBOUNCE_DELAY_MS
+ * regardless since the work function will check for any plug or altmodes changes since
+ * its last run anyway.
+ */
+ cancel_delayed_work(&cd321x->update_work);
+ schedule_delayed_work(&cd321x->update_work, msecs_to_jiffies(CD321X_DEBOUNCE_DELAY_MS));
+
+ return 0;
+}
+
static irqreturn_t cd321x_interrupt(int irq, void *data)
{
struct tps6598x *tps = data;
@@ -545,23 +891,28 @@ static irqreturn_t cd321x_interrupt(int irq, void *data)
if (!event)
goto err_unlock;
+ tps6598x_write64(tps, TPS_REG_INT_CLEAR1, event);
+
if (!tps6598x_read_status(tps, &status))
- goto err_clear_ints;
+ goto err_unlock;
- if (event & APPLE_CD_REG_INT_POWER_STATUS_UPDATE)
+ if (event & APPLE_CD_REG_INT_POWER_STATUS_UPDATE) {
if (!tps6598x_read_power_status(tps))
- goto err_clear_ints;
+ goto err_unlock;
+ if (TPS_POWER_STATUS_PWROPMODE(tps->pwr_status) == TYPEC_PWR_MODE_PD) {
+ if (tps6598x_read_partner_identity(tps)) {
+ dev_err(tps->dev, "failed to read partner identity\n");
+ tps->partner_identity = (struct usb_pd_identity) {0};
+ }
+ }
+ }
if (event & APPLE_CD_REG_INT_DATA_STATUS_UPDATE)
- if (!tps6598x_read_data_status(tps))
- goto err_clear_ints;
+ if (!tps->data->read_data_status(tps))
+ goto err_unlock;
- /* Handle plug insert or removal */
- if (event & APPLE_CD_REG_INT_PLUG_EVENT)
- tps6598x_handle_plug_event(tps, status);
-
-err_clear_ints:
- tps6598x_write64(tps, TPS_REG_INT_CLEAR1, event);
+ /* Can be called uncondtionally since it will check for any changes itself */
+ cd321x_connect(tps, status);
err_unlock:
mutex_unlock(&tps->lock);
@@ -605,7 +956,7 @@ static irqreturn_t tps25750_interrupt(int irq, void *data)
goto err_clear_ints;
if (event[0] & TPS_REG_INT_DATA_STATUS_UPDATE)
- if (!tps6598x_read_data_status(tps))
+ if (!tps->data->read_data_status(tps))
goto err_clear_ints;
/*
@@ -668,25 +1019,24 @@ static irqreturn_t tps6598x_interrupt(int irq, void *data)
if (!(event1[0] | event1[1] | event2[0] | event2[1]))
goto err_unlock;
+ tps6598x_block_write(tps, TPS_REG_INT_CLEAR1, event1, intev_len);
+ tps6598x_block_write(tps, TPS_REG_INT_CLEAR2, event2, intev_len);
+
if (!tps6598x_read_status(tps, &status))
- goto err_clear_ints;
+ goto err_unlock;
if ((event1[0] | event2[0]) & TPS_REG_INT_POWER_STATUS_UPDATE)
if (!tps6598x_read_power_status(tps))
- goto err_clear_ints;
+ goto err_unlock;
if ((event1[0] | event2[0]) & TPS_REG_INT_DATA_STATUS_UPDATE)
- if (!tps6598x_read_data_status(tps))
- goto err_clear_ints;
+ if (!tps->data->read_data_status(tps))
+ goto err_unlock;
/* Handle plug insert or removal */
if ((event1[0] | event2[0]) & TPS_REG_INT_PLUG_EVENT)
tps6598x_handle_plug_event(tps, status);
-err_clear_ints:
- tps6598x_block_write(tps, TPS_REG_INT_CLEAR1, event1, intev_len);
- tps6598x_block_write(tps, TPS_REG_INT_CLEAR2, event2, intev_len);
-
err_unlock:
mutex_unlock(&tps->lock);
@@ -887,6 +1237,94 @@ tps6598x_register_port(struct tps6598x *tps, struct fwnode_handle *fwnode)
return 0;
}
+static int cd321x_register_port_altmodes(struct cd321x *cd321x)
+{
+ struct typec_altmode_desc desc;
+ struct typec_altmode *amode;
+
+ memset(&desc, 0, sizeof(desc));
+ desc.svid = USB_TYPEC_DP_SID;
+ desc.mode = USB_TYPEC_DP_MODE;
+ desc.vdo = DP_CONF_SET_PIN_ASSIGN(BIT(DP_PIN_ASSIGN_C) | BIT(DP_PIN_ASSIGN_D));
+ desc.vdo |= DP_CAP_DFP_D;
+ amode = typec_port_register_altmode(cd321x->tps.port, &desc);
+ if (IS_ERR(amode))
+ return PTR_ERR(amode);
+ cd321x->port_altmode_dp = amode;
+
+ memset(&desc, 0, sizeof(desc));
+ desc.svid = USB_TYPEC_TBT_SID;
+ desc.mode = TYPEC_ANY_MODE;
+ amode = typec_port_register_altmode(cd321x->tps.port, &desc);
+ if (IS_ERR(amode)) {
+ typec_unregister_altmode(cd321x->port_altmode_dp);
+ cd321x->port_altmode_dp = NULL;
+ return PTR_ERR(amode);
+ }
+ cd321x->port_altmode_tbt = amode;
+
+ return 0;
+}
+
+static int
+cd321x_register_port(struct tps6598x *tps, struct fwnode_handle *fwnode)
+{
+ struct cd321x *cd321x = container_of(tps, struct cd321x, tps);
+ int ret;
+
+ INIT_DELAYED_WORK(&cd321x->update_work, cd321x_update_work);
+
+ ret = tps6598x_register_port(tps, fwnode);
+ if (ret)
+ return ret;
+
+ ret = cd321x_register_port_altmodes(cd321x);
+ if (ret)
+ goto err_unregister_port;
+
+ cd321x->mux = fwnode_typec_mux_get(fwnode);
+ if (IS_ERR(cd321x->mux)) {
+ ret = PTR_ERR(cd321x->mux);
+ goto err_unregister_altmodes;
+ }
+
+ cd321x->state.alt = NULL;
+ cd321x->state.mode = TYPEC_STATE_SAFE;
+ cd321x->state.data = NULL;
+ typec_set_mode(tps->port, TYPEC_STATE_SAFE);
+
+ return 0;
+
+err_unregister_altmodes:
+ typec_unregister_altmode(cd321x->port_altmode_dp);
+ typec_unregister_altmode(cd321x->port_altmode_tbt);
+ cd321x->port_altmode_dp = NULL;
+ cd321x->port_altmode_tbt = NULL;
+err_unregister_port:
+ typec_unregister_port(tps->port);
+ return ret;
+}
+
+static void
+tps6598x_unregister_port(struct tps6598x *tps)
+{
+ typec_unregister_port(tps->port);
+}
+
+static void
+cd321x_unregister_port(struct tps6598x *tps)
+{
+ struct cd321x *cd321x = container_of(tps, struct cd321x, tps);
+
+ typec_mux_put(cd321x->mux);
+ cd321x->mux = NULL;
+ typec_unregister_altmode(cd321x->port_altmode_dp);
+ cd321x->port_altmode_dp = NULL;
+ typec_unregister_altmode(cd321x->port_altmode_tbt);
+ cd321x->port_altmode_tbt = NULL;
+ typec_unregister_port(tps->port);
+}
+
static int tps_request_firmware(struct tps6598x *tps, const struct firmware **fw,
const char **firmware_name)
{
@@ -1265,6 +1703,7 @@ tps25750_register_port(struct tps6598x *tps, struct fwnode_handle *fwnode)
typec_cap.data = ret;
typec_cap.revision = USB_TYPEC_REV_1_3;
typec_cap.pd_revision = 0x300;
+ typec_cap.orientation_aware = true;
typec_cap.driver_data = tps;
typec_cap.ops = &tps6598x_ops;
typec_cap.fwnode = fwnode;
@@ -1292,22 +1731,33 @@ tps25750_register_port(struct tps6598x *tps, struct fwnode_handle *fwnode)
return 0;
}
+static void cd321x_remove(struct tps6598x *tps)
+{
+ struct cd321x *cd321x = container_of(tps, struct cd321x, tps);
+
+ cancel_delayed_work_sync(&cd321x->update_work);
+}
+
static int tps6598x_probe(struct i2c_client *client)
{
- struct device_node *np = client->dev.of_node;
+ const struct tipd_data *data;
struct tps6598x *tps;
struct fwnode_handle *fwnode;
u32 status;
u32 vid;
int ret;
- u64 mask1;
- tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL);
+ data = i2c_get_match_data(client);
+ if (!data)
+ return -EINVAL;
+
+ tps = devm_kzalloc(&client->dev, data->tps_struct_size, GFP_KERNEL);
if (!tps)
return -ENOMEM;
mutex_init(&tps->lock);
tps->dev = &client->dev;
+ tps->data = data;
tps->reset = devm_gpiod_get_optional(tps->dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(tps->reset))
@@ -1333,28 +1783,12 @@ static int tps6598x_probe(struct i2c_client *client)
if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
tps->i2c_protocol = true;
- if (np && of_device_is_compatible(np, "apple,cd321x")) {
- /* Switch CD321X chips to the correct system power state */
- ret = cd321x_switch_power_state(tps, TPS_SYSTEM_POWER_STATE_S0);
+ if (tps->data->switch_power_state) {
+ ret = tps->data->switch_power_state(tps, TPS_SYSTEM_POWER_STATE_S0);
if (ret)
return ret;
-
- /* CD321X chips have all interrupts masked initially */
- mask1 = APPLE_CD_REG_INT_POWER_STATUS_UPDATE |
- APPLE_CD_REG_INT_DATA_STATUS_UPDATE |
- APPLE_CD_REG_INT_PLUG_EVENT;
-
- } else {
- /* Enable power status, data status and plug event interrupts */
- mask1 = TPS_REG_INT_POWER_STATUS_UPDATE |
- TPS_REG_INT_DATA_STATUS_UPDATE |
- TPS_REG_INT_PLUG_EVENT;
}
- tps->data = i2c_get_match_data(client);
- if (!tps->data)
- return -EINVAL;
-
/* Make sure the controller has application firmware running */
ret = tps6598x_check_mode(tps);
if (ret < 0)
@@ -1366,7 +1800,7 @@ static int tps6598x_probe(struct i2c_client *client)
return ret;
}
- ret = tps6598x_write64(tps, TPS_REG_INT_MASK1, mask1);
+ ret = tps6598x_write64(tps, TPS_REG_INT_MASK1, tps->data->irq_mask1);
if (ret)
goto err_reset_controller;
@@ -1401,12 +1835,11 @@ static int tps6598x_probe(struct i2c_client *client)
goto err_role_put;
if (status & TPS_STATUS_PLUG_PRESENT) {
- ret = tps6598x_read16(tps, TPS_REG_POWER_STATUS, &tps->pwr_status);
- if (ret < 0) {
- dev_err(tps->dev, "failed to read power status: %d\n", ret);
+ if (!tps6598x_read_power_status(tps))
goto err_unregister_port;
- }
- ret = tps6598x_connect(tps, status);
+ if (!tps->data->read_data_status(tps))
+ goto err_unregister_port;
+ ret = tps->data->connect(tps, status);
if (ret)
dev_err(&client->dev, "failed to register partner\n");
}
@@ -1431,7 +1864,7 @@ static int tps6598x_probe(struct i2c_client *client)
tps->wakeup = device_property_read_bool(tps->dev, "wakeup-source");
if (tps->wakeup && client->irq) {
- device_init_wakeup(&client->dev, true);
+ devm_device_init_wakeup(&client->dev);
enable_irq_wake(client->irq);
}
@@ -1440,7 +1873,7 @@ static int tps6598x_probe(struct i2c_client *client)
err_disconnect:
tps6598x_disconnect(tps, 0);
err_unregister_port:
- typec_unregister_port(tps->port);
+ tps->data->unregister_port(tps);
err_role_put:
usb_role_switch_put(tps->role_sw);
err_fwnode_put:
@@ -1463,8 +1896,11 @@ static void tps6598x_remove(struct i2c_client *client)
else
devm_free_irq(tps->dev, client->irq, tps);
+ if (tps->data->remove)
+ tps->data->remove(tps);
+
tps6598x_disconnect(tps, 0);
- typec_unregister_port(tps->port);
+ tps->data->unregister_port(tps);
usb_role_switch_put(tps->role_sw);
/* Reset PD controller to remove any applied patch */
@@ -1529,31 +1965,57 @@ static const struct dev_pm_ops tps6598x_pm_ops = {
static const struct tipd_data cd321x_data = {
.irq_handler = cd321x_interrupt,
- .register_port = tps6598x_register_port,
+ .irq_mask1 = APPLE_CD_REG_INT_POWER_STATUS_UPDATE |
+ APPLE_CD_REG_INT_DATA_STATUS_UPDATE |
+ APPLE_CD_REG_INT_PLUG_EVENT,
+ .tps_struct_size = sizeof(struct cd321x),
+ .remove = cd321x_remove,
+ .register_port = cd321x_register_port,
+ .unregister_port = cd321x_unregister_port,
+ .trace_data_status = trace_cd321x_data_status,
.trace_power_status = trace_tps6598x_power_status,
.trace_status = trace_tps6598x_status,
.init = cd321x_init,
+ .read_data_status = cd321x_read_data_status,
.reset = cd321x_reset,
+ .switch_power_state = cd321x_switch_power_state,
+ .connect = cd321x_connect,
};
static const struct tipd_data tps6598x_data = {
.irq_handler = tps6598x_interrupt,
+ .irq_mask1 = TPS_REG_INT_POWER_STATUS_UPDATE |
+ TPS_REG_INT_DATA_STATUS_UPDATE |
+ TPS_REG_INT_PLUG_EVENT,
+ .tps_struct_size = sizeof(struct tps6598x),
.register_port = tps6598x_register_port,
+ .unregister_port = tps6598x_unregister_port,
+ .trace_data_status = trace_tps6598x_data_status,
.trace_power_status = trace_tps6598x_power_status,
.trace_status = trace_tps6598x_status,
.apply_patch = tps6598x_apply_patch,
.init = tps6598x_init,
+ .read_data_status = tps6598x_read_data_status,
.reset = tps6598x_reset,
+ .connect = tps6598x_connect,
};
static const struct tipd_data tps25750_data = {
.irq_handler = tps25750_interrupt,
+ .irq_mask1 = TPS_REG_INT_POWER_STATUS_UPDATE |
+ TPS_REG_INT_DATA_STATUS_UPDATE |
+ TPS_REG_INT_PLUG_EVENT,
+ .tps_struct_size = sizeof(struct tps6598x),
.register_port = tps25750_register_port,
+ .unregister_port = tps6598x_unregister_port,
+ .trace_data_status = trace_tps6598x_data_status,
.trace_power_status = trace_tps25750_power_status,
.trace_status = trace_tps25750_status,
.apply_patch = tps25750_apply_patch,
.init = tps25750_init,
+ .read_data_status = tps6598x_read_data_status,
.reset = tps25750_reset,
+ .connect = tps6598x_connect,
};
static const struct of_device_id tps6598x_of_match[] = {
diff --git a/drivers/usb/typec/tipd/tps6598x.h b/drivers/usb/typec/tipd/tps6598x.h
index 9b23e9017452..03edbb77bbd6 100644
--- a/drivers/usb/typec/tipd/tps6598x.h
+++ b/drivers/usb/typec/tipd/tps6598x.h
@@ -27,7 +27,7 @@
#define TPS_STATUS_OVERCURRENT BIT(16)
#define TPS_STATUS_GOTO_MIN_ACTIVE BIT(26)
#define TPS_STATUS_BIST BIT(27)
-#define TPS_STATUS_HIGH_VOLAGE_WARNING BIT(28)
+#define TPS_STATUS_HIGH_VOLTAGE_WARNING BIT(28)
#define TPS_STATUS_HIGH_LOW_VOLTAGE_WARNING BIT(29)
#define TPS_STATUS_CONN_STATE_MASK GENMASK(3, 1)
@@ -197,6 +197,11 @@
#define TPS_DATA_STATUS_FORCE_LSX BIT(23)
#define TPS_DATA_STATUS_POWER_MISMATCH BIT(24)
+/* modified TPS_REG_DATA_STATUS bits for CD321x (and likely also TPS65987DDK) */
+#define CD321X_DATA_STATUS_HPD_IRQ BIT(14)
+#define CD321X_DATA_STATUS_HPD_LEVEL BIT(15)
+#define CD321X_DATA_STATUS_USB4_CONNECTION BIT(23)
+
#define TPS_DATA_STATUS_DP_PIN_ASSIGNMENT_MASK GENMASK(11, 10)
#define TPS_DATA_STATUS_DP_PIN_ASSIGNMENT(x) \
TPS_FIELD_GET(TPS_DATA_STATUS_DP_PIN_ASSIGNMENT_MASK, (x))
diff --git a/drivers/usb/typec/tipd/trace.h b/drivers/usb/typec/tipd/trace.h
index 0669cca12ea1..e9e40425138a 100644
--- a/drivers/usb/typec/tipd/trace.h
+++ b/drivers/usb/typec/tipd/trace.h
@@ -153,7 +153,7 @@
{ TPS_STATUS_OVERCURRENT, "OVERCURRENT" }, \
{ TPS_STATUS_GOTO_MIN_ACTIVE, "GOTO_MIN_ACTIVE" }, \
{ TPS_STATUS_BIST, "BIST" }, \
- { TPS_STATUS_HIGH_VOLAGE_WARNING, "HIGH_VOLAGE_WARNING" }, \
+ { TPS_STATUS_HIGH_VOLTAGE_WARNING, "HIGH_VOLTAGE_WARNING" }, \
{ TPS_STATUS_HIGH_LOW_VOLTAGE_WARNING, "HIGH_LOW_VOLTAGE_WARNING" })
#define show_tps25750_status_flags(flags) \
@@ -217,6 +217,26 @@
{ TPS_DATA_STATUS_FORCE_LSX, "FORCE_LSX" }, \
{ TPS_DATA_STATUS_POWER_MISMATCH, "POWER_MISMATCH" })
+#define show_cd321x_data_status_flags(data_status) \
+ __print_flags(data_status & TPS_DATA_STATUS_FLAGS_MASK, "|", \
+ { TPS_DATA_STATUS_DATA_CONNECTION, "DATA_CONNECTION" }, \
+ { TPS_DATA_STATUS_UPSIDE_DOWN, "DATA_UPSIDE_DOWN" }, \
+ { TPS_DATA_STATUS_ACTIVE_CABLE, "ACTIVE_CABLE" }, \
+ { TPS_DATA_STATUS_USB2_CONNECTION, "USB2_CONNECTION" }, \
+ { TPS_DATA_STATUS_USB3_CONNECTION, "USB3_CONNECTION" }, \
+ { TPS_DATA_STATUS_USB3_GEN2, "USB3_GEN2" }, \
+ { TPS_DATA_STATUS_USB_DATA_ROLE, "USB_DATA_ROLE" }, \
+ { TPS_DATA_STATUS_DP_CONNECTION, "DP_CONNECTION" }, \
+ { TPS_DATA_STATUS_DP_SINK, "DP_SINK" }, \
+ { CD321X_DATA_STATUS_HPD_IRQ, "HPD_IRQ" }, \
+ { CD321X_DATA_STATUS_HPD_LEVEL, "HPD_LEVEL" }, \
+ { TPS_DATA_STATUS_TBT_CONNECTION, "TBT_CONNECTION" }, \
+ { TPS_DATA_STATUS_TBT_TYPE, "TBT_TYPE" }, \
+ { TPS_DATA_STATUS_OPTICAL_CABLE, "OPTICAL_CABLE" }, \
+ { TPS_DATA_STATUS_ACTIVE_LINK_TRAIN, "ACTIVE_LINK_TRAIN" }, \
+ { CD321X_DATA_STATUS_USB4_CONNECTION, "USB4" }, \
+ { TPS_DATA_STATUS_POWER_MISMATCH, "POWER_MISMATCH" })
+
#define show_data_status_dp_pin_assignment(data_status) \
__print_symbolic(TPS_DATA_STATUS_DP_SPEC_PIN_ASSIGNMENT(data_status), \
{ TPS_DATA_STATUS_DP_SPEC_PIN_ASSIGNMENT_E, "E" }, \
@@ -388,6 +408,25 @@ TRACE_EVENT(tps6598x_data_status,
)
);
+TRACE_EVENT(cd321x_data_status,
+ TP_PROTO(u32 data_status),
+ TP_ARGS(data_status),
+
+ TP_STRUCT__entry(
+ __field(u32, data_status)
+ ),
+
+ TP_fast_assign(
+ __entry->data_status = data_status;
+ ),
+
+ TP_printk("%s%s%s",
+ show_cd321x_data_status_flags(__entry->data_status),
+ __entry->data_status & TPS_DATA_STATUS_DP_CONNECTION ? ", DP pinout " : "",
+ maybe_show_data_status_dp_pin_assignment(__entry->data_status)
+ )
+);
+
#endif /* _TPS6598X_TRACE_H_ */
/* This part must be outside protection */
diff --git a/drivers/usb/typec/ucsi/Kconfig b/drivers/usb/typec/ucsi/Kconfig
index 75559601fe8f..87dd992a4b9e 100644
--- a/drivers/usb/typec/ucsi/Kconfig
+++ b/drivers/usb/typec/ucsi/Kconfig
@@ -73,7 +73,6 @@ config CROS_EC_UCSI
tristate "UCSI Driver for ChromeOS EC"
depends on MFD_CROS_EC_DEV
depends on CROS_USBPD_NOTIFY
- depends on !EXTCON_TCSS_CROS_EC
default MFD_CROS_EC_DEV
help
This driver enables UCSI support for a ChromeOS EC. The EC is
@@ -85,10 +84,24 @@ config CROS_EC_UCSI
config UCSI_LENOVO_YOGA_C630
tristate "UCSI Interface Driver for Lenovo Yoga C630"
depends on EC_LENOVO_YOGA_C630
+ depends on DRM || !DRM
+ select DRM_AUX_HPD_BRIDGE if DRM_BRIDGE && OF
help
This driver enables UCSI support on the Lenovo Yoga C630 laptop.
To compile the driver as a module, choose M here: the module will be
called ucsi_yoga_c630.
+config UCSI_HUAWEI_GAOKUN
+ tristate "UCSI Interface Driver for Huawei Matebook E Go"
+ depends on EC_HUAWEI_GAOKUN
+ depends on DRM || !DRM
+ select DRM_AUX_HPD_BRIDGE if DRM_BRIDGE && OF
+ help
+ This driver enables UCSI support on the Huawei Matebook E Go tablet,
+ which is a sc8280xp-based 2-in-1 tablet.
+
+ To compile the driver as a module, choose M here: the module will be
+ called ucsi_huawei_gaokun.
+
endif
diff --git a/drivers/usb/typec/ucsi/Makefile b/drivers/usb/typec/ucsi/Makefile
index be98a879104d..c7e38bf01350 100644
--- a/drivers/usb/typec/ucsi/Makefile
+++ b/drivers/usb/typec/ucsi/Makefile
@@ -17,9 +17,14 @@ ifneq ($(CONFIG_TYPEC_DP_ALTMODE),)
typec_ucsi-y += displayport.o
endif
+ifneq ($(CONFIG_TYPEC_TBT_ALTMODE),)
+ typec_ucsi-y += thunderbolt.o
+endif
+
obj-$(CONFIG_UCSI_ACPI) += ucsi_acpi.o
obj-$(CONFIG_UCSI_CCG) += ucsi_ccg.o
obj-$(CONFIG_UCSI_STM32G0) += ucsi_stm32g0.o
obj-$(CONFIG_UCSI_PMIC_GLINK) += ucsi_glink.o
obj-$(CONFIG_CROS_EC_UCSI) += cros_ec_ucsi.o
obj-$(CONFIG_UCSI_LENOVO_YOGA_C630) += ucsi_yoga_c630.o
+obj-$(CONFIG_UCSI_HUAWEI_GAOKUN) += ucsi_huawei_gaokun.o
diff --git a/drivers/usb/typec/ucsi/cros_ec_ucsi.c b/drivers/usb/typec/ucsi/cros_ec_ucsi.c
index c605c8616726..251aa7251ce6 100644
--- a/drivers/usb/typec/ucsi/cros_ec_ucsi.c
+++ b/drivers/usb/typec/ucsi/cros_ec_ucsi.c
@@ -5,17 +5,20 @@
* Copyright 2024 Google LLC.
*/
+#include <linux/acpi.h>
#include <linux/container_of.h>
#include <linux/dev_printk.h>
#include <linux/jiffies.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/platform_data/cros_ec_commands.h>
#include <linux/platform_data/cros_usbpd_notify.h>
#include <linux/platform_data/cros_ec_proto.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/wait.h>
+#include <linux/usb/typec_altmode.h>
#include "ucsi.h"
@@ -33,6 +36,11 @@
/* Number of times to attempt recovery from a write timeout before giving up. */
#define WRITE_TMO_CTR_MAX 5
+/* Delay between mode entry/exit attempts, ms */
+static const unsigned int mode_selection_delay = 1000;
+/* Timeout for a mode entry attempt, ms */
+static const unsigned int mode_selection_timeout = 4000;
+
struct cros_ucsi_data {
struct device *dev;
struct ucsi *ucsi;
@@ -105,12 +113,13 @@ static int cros_ucsi_async_control(struct ucsi *ucsi, u64 cmd)
return 0;
}
-static int cros_ucsi_sync_control(struct ucsi *ucsi, u64 cmd)
+static int cros_ucsi_sync_control(struct ucsi *ucsi, u64 cmd, u32 *cci,
+ void *data, size_t size)
{
struct cros_ucsi_data *udata = ucsi_get_drvdata(ucsi);
int ret;
- ret = ucsi_sync_control_common(ucsi, cmd);
+ ret = ucsi_sync_control_common(ucsi, cmd, cci, data, size);
switch (ret) {
case -EBUSY:
/* EC may return -EBUSY if CCI.busy is set.
@@ -133,12 +142,29 @@ static int cros_ucsi_sync_control(struct ucsi *ucsi, u64 cmd)
return ret;
}
+static void cros_ucsi_add_partner_altmodes(struct ucsi_connector *con)
+{
+ if (!con->typec_cap.no_mode_control)
+ typec_mode_selection_start(con->partner,
+ mode_selection_delay,
+ mode_selection_timeout);
+}
+
+static void cros_ucsi_remove_partner_altmodes(struct ucsi_connector *con)
+{
+ if (!con->typec_cap.no_mode_control)
+ typec_mode_selection_delete(con->partner);
+}
+
static const struct ucsi_operations cros_ucsi_ops = {
.read_version = cros_ucsi_read_version,
.read_cci = cros_ucsi_read_cci,
+ .poll_cci = cros_ucsi_read_cci,
.read_message_in = cros_ucsi_read_message_in,
.async_control = cros_ucsi_async_control,
.sync_control = cros_ucsi_sync_control,
+ .add_partner_altmodes = cros_ucsi_add_partner_altmodes,
+ .remove_partner_altmodes = cros_ucsi_remove_partner_altmodes,
};
static void cros_ucsi_work(struct work_struct *work)
@@ -205,12 +231,19 @@ static int cros_ucsi_event(struct notifier_block *nb,
{
struct cros_ucsi_data *udata = container_of(nb, struct cros_ucsi_data, nb);
- if (!(host_event & PD_EVENT_PPM))
- return NOTIFY_OK;
+ if (host_event & PD_EVENT_INIT) {
+ /* Late init event received from ChromeOS EC. Treat this as a
+ * system resume to re-enable communication with the PPM.
+ */
+ dev_dbg(udata->dev, "Late PD init received\n");
+ ucsi_resume(udata->ucsi);
+ }
- dev_dbg(udata->dev, "UCSI notification received\n");
- flush_work(&udata->work);
- schedule_work(&udata->work);
+ if (host_event & PD_EVENT_PPM) {
+ dev_dbg(udata->dev, "UCSI notification received\n");
+ flush_work(&udata->work);
+ schedule_work(&udata->work);
+ }
return NOTIFY_OK;
}
@@ -226,7 +259,6 @@ static void cros_ucsi_destroy(struct cros_ucsi_data *udata)
static int cros_ucsi_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct cros_ec_dev *ec_data = dev_get_drvdata(dev->parent);
struct cros_ucsi_data *udata;
int ret;
@@ -234,9 +266,16 @@ static int cros_ucsi_probe(struct platform_device *pdev)
if (!udata)
return -ENOMEM;
+ /* ACPI and OF FW nodes for cros_ec_ucsi are children of the ChromeOS EC. If the
+ * cros_ec_ucsi device has an ACPI or OF FW node, its parent is the ChromeOS EC device.
+ * Platforms without a FW node for cros_ec_ucsi may add it as a subdevice of cros_ec_dev.
+ */
udata->dev = dev;
+ if (is_acpi_device_node(dev->fwnode) || is_of_node(dev->fwnode))
+ udata->ec = dev_get_drvdata(dev->parent);
+ else
+ udata->ec = ((struct cros_ec_dev *)dev_get_drvdata(dev->parent))->ec_dev;
- udata->ec = ec_data->ec_dev;
if (!udata->ec)
return dev_err_probe(dev, -ENODEV, "couldn't find parent EC device\n");
@@ -317,10 +356,24 @@ static const struct platform_device_id cros_ucsi_id[] = {
};
MODULE_DEVICE_TABLE(platform, cros_ucsi_id);
+static const struct acpi_device_id cros_ec_ucsi_acpi_device_ids[] = {
+ { "GOOG0021", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, cros_ec_ucsi_acpi_device_ids);
+
+static const struct of_device_id cros_ucsi_of_match[] = {
+ { .compatible = "google,cros-ec-ucsi", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, cros_ucsi_of_match);
+
static struct platform_driver cros_ucsi_driver = {
.driver = {
.name = KBUILD_MODNAME,
.pm = &cros_ucsi_pm_ops,
+ .acpi_match_table = cros_ec_ucsi_acpi_device_ids,
+ .of_match_table = cros_ucsi_of_match,
},
.id_table = cros_ucsi_id,
.probe = cros_ucsi_probe,
diff --git a/drivers/usb/typec/ucsi/debugfs.c b/drivers/usb/typec/ucsi/debugfs.c
index 83ff23086d79..a4b9a6b51649 100644
--- a/drivers/usb/typec/ucsi/debugfs.c
+++ b/drivers/usb/typec/ucsi/debugfs.c
@@ -8,6 +8,7 @@
* Gopal Saranya <saranya.gopal@intel.com>
*/
#include <linux/debugfs.h>
+#include <linux/hex.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/types.h>
@@ -28,20 +29,30 @@ static int ucsi_cmd(void *data, u64 val)
ucsi->debugfs->status = 0;
switch (UCSI_COMMAND(val)) {
- case UCSI_SET_UOM:
+ case UCSI_SET_CCOM:
case UCSI_SET_UOR:
case UCSI_SET_PDR:
case UCSI_CONNECTOR_RESET:
case UCSI_SET_SINK_PATH:
+ case UCSI_SET_NEW_CAM:
+ case UCSI_SET_USB:
+ case UCSI_SET_POWER_LEVEL:
+ case UCSI_READ_POWER_LEVEL:
ret = ucsi_send_command(ucsi, val, NULL, 0);
break;
case UCSI_GET_CAPABILITY:
case UCSI_GET_CONNECTOR_CAPABILITY:
case UCSI_GET_ALTERNATE_MODES:
+ case UCSI_GET_CAM_SUPPORTED:
case UCSI_GET_CURRENT_CAM:
case UCSI_GET_PDOS:
case UCSI_GET_CABLE_PROPERTY:
case UCSI_GET_CONNECTOR_STATUS:
+ case UCSI_GET_ERROR_STATUS:
+ case UCSI_GET_PD_MESSAGE:
+ case UCSI_GET_ATTENTION_VDO:
+ case UCSI_GET_CAM_CS:
+ case UCSI_GET_LPM_PPM_INFO:
ret = ucsi_send_command(ucsi, val,
&ucsi->debugfs->response,
sizeof(ucsi->debugfs->response));
@@ -72,15 +83,45 @@ static int ucsi_resp_show(struct seq_file *s, void *not_used)
}
DEFINE_SHOW_ATTRIBUTE(ucsi_resp);
+static int ucsi_peak_curr_show(struct seq_file *m, void *v)
+{
+ struct ucsi *ucsi = m->private;
+
+ seq_printf(m, "%u mA\n", ucsi->connector->peak_current);
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(ucsi_peak_curr);
+
+static int ucsi_avg_curr_show(struct seq_file *m, void *v)
+{
+ struct ucsi *ucsi = m->private;
+
+ seq_printf(m, "%u mA\n", ucsi->connector->avg_current);
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(ucsi_avg_curr);
+
+static int ucsi_vbus_volt_show(struct seq_file *m, void *v)
+{
+ struct ucsi *ucsi = m->private;
+
+ seq_printf(m, "%u mV\n", ucsi->connector->vbus_voltage);
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(ucsi_vbus_volt);
+
void ucsi_debugfs_register(struct ucsi *ucsi)
{
- ucsi->debugfs = kzalloc(sizeof(*ucsi->debugfs), GFP_KERNEL);
+ ucsi->debugfs = kzalloc_obj(*ucsi->debugfs);
if (!ucsi->debugfs)
return;
ucsi->debugfs->dentry = debugfs_create_dir(dev_name(ucsi->dev), ucsi_debugfs_root);
debugfs_create_file("command", 0200, ucsi->debugfs->dentry, ucsi, &ucsi_cmd_fops);
debugfs_create_file("response", 0400, ucsi->debugfs->dentry, ucsi, &ucsi_resp_fops);
+ debugfs_create_file("peak_current", 0400, ucsi->debugfs->dentry, ucsi, &ucsi_peak_curr_fops);
+ debugfs_create_file("avg_current", 0400, ucsi->debugfs->dentry, ucsi, &ucsi_avg_curr_fops);
+ debugfs_create_file("vbus_voltage", 0400, ucsi->debugfs->dentry, ucsi, &ucsi_vbus_volt_fops);
}
void ucsi_debugfs_unregister(struct ucsi *ucsi)
diff --git a/drivers/usb/typec/ucsi/displayport.c b/drivers/usb/typec/ucsi/displayport.c
index 420af5139c70..8aae80b457d7 100644
--- a/drivers/usb/typec/ucsi/displayport.c
+++ b/drivers/usb/typec/ucsi/displayport.c
@@ -54,7 +54,8 @@ static int ucsi_displayport_enter(struct typec_altmode *alt, u32 *vdo)
u8 cur = 0;
int ret;
- mutex_lock(&dp->con->lock);
+ if (!ucsi_con_mutex_lock(dp->con))
+ return -ENOTCONN;
if (!dp->override && dp->initialized) {
const struct typec_altmode *p = typec_altmode_get_partner(alt);
@@ -100,7 +101,7 @@ static int ucsi_displayport_enter(struct typec_altmode *alt, u32 *vdo)
schedule_work(&dp->work);
ret = 0;
err_unlock:
- mutex_unlock(&dp->con->lock);
+ ucsi_con_mutex_unlock(dp->con);
return ret;
}
@@ -112,7 +113,8 @@ static int ucsi_displayport_exit(struct typec_altmode *alt)
u64 command;
int ret = 0;
- mutex_lock(&dp->con->lock);
+ if (!ucsi_con_mutex_lock(dp->con))
+ return -ENOTCONN;
if (!dp->override) {
const struct typec_altmode *p = typec_altmode_get_partner(alt);
@@ -144,7 +146,7 @@ static int ucsi_displayport_exit(struct typec_altmode *alt)
schedule_work(&dp->work);
out_unlock:
- mutex_unlock(&dp->con->lock);
+ ucsi_con_mutex_unlock(dp->con);
return ret;
}
@@ -202,20 +204,21 @@ static int ucsi_displayport_vdm(struct typec_altmode *alt,
int cmd = PD_VDO_CMD(header);
int svdm_version;
- mutex_lock(&dp->con->lock);
+ if (!ucsi_con_mutex_lock(dp->con))
+ return -ENOTCONN;
if (!dp->override && dp->initialized) {
const struct typec_altmode *p = typec_altmode_get_partner(alt);
dev_warn(&p->dev,
"firmware doesn't support alternate mode overriding\n");
- mutex_unlock(&dp->con->lock);
+ ucsi_con_mutex_unlock(dp->con);
return -EOPNOTSUPP;
}
svdm_version = typec_altmode_get_svdm_version(alt);
if (svdm_version < 0) {
- mutex_unlock(&dp->con->lock);
+ ucsi_con_mutex_unlock(dp->con);
return svdm_version;
}
@@ -259,7 +262,7 @@ static int ucsi_displayport_vdm(struct typec_altmode *alt,
break;
}
- mutex_unlock(&dp->con->lock);
+ ucsi_con_mutex_unlock(dp->con);
return 0;
}
@@ -296,6 +299,8 @@ void ucsi_displayport_remove_partner(struct typec_altmode *alt)
if (!dp)
return;
+ cancel_work_sync(&dp->work);
+
dp->data.conf = 0;
dp->data.status = 0;
dp->initialized = false;
diff --git a/drivers/usb/typec/ucsi/psy.c b/drivers/usb/typec/ucsi/psy.c
index 62ac69730405..c8ebab8ba7e7 100644
--- a/drivers/usb/typec/ucsi/psy.c
+++ b/drivers/usb/typec/ucsi/psy.c
@@ -29,6 +29,7 @@ static enum power_supply_property ucsi_psy_props[] = {
POWER_SUPPLY_PROP_CURRENT_MAX,
POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_SCOPE,
+ POWER_SUPPLY_PROP_STATUS,
};
static int ucsi_psy_get_scope(struct ucsi_connector *con,
@@ -51,6 +52,29 @@ static int ucsi_psy_get_scope(struct ucsi_connector *con,
return 0;
}
+static int ucsi_psy_get_status(struct ucsi_connector *con,
+ union power_supply_propval *val)
+{
+ bool is_sink = UCSI_CONSTAT(con, PWR_DIR) == TYPEC_SINK;
+ bool sink_path_enabled = true;
+
+ val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+
+ if (con->ucsi->version >= UCSI_VERSION_2_0)
+ sink_path_enabled =
+ UCSI_CONSTAT(con, SINK_PATH_STATUS_V2_0) ==
+ UCSI_CONSTAT_SINK_PATH_ENABLED;
+
+ if (UCSI_CONSTAT(con, CONNECTED)) {
+ if (is_sink && sink_path_enabled)
+ val->intval = POWER_SUPPLY_STATUS_CHARGING;
+ else if (!is_sink)
+ val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+ }
+
+ return 0;
+}
+
static int ucsi_psy_get_online(struct ucsi_connector *con,
union power_supply_propval *val)
{
@@ -88,15 +112,20 @@ static int ucsi_psy_get_voltage_max(struct ucsi_connector *con,
union power_supply_propval *val)
{
u32 pdo;
+ int max_voltage = 0;
switch (UCSI_CONSTAT(con, PWR_OPMODE)) {
case UCSI_CONSTAT_PWR_OPMODE_PD:
- if (con->num_pdos > 0) {
- pdo = con->src_pdos[con->num_pdos - 1];
- val->intval = pdo_fixed_voltage(pdo) * 1000;
- } else {
- val->intval = 0;
+ for (int i = 0; i < con->num_pdos; i++) {
+ int pdo_voltage = 0;
+
+ pdo = con->src_pdos[i];
+ if (pdo_type(pdo) == PDO_TYPE_FIXED)
+ pdo_voltage = pdo_fixed_voltage(pdo) * 1000;
+ max_voltage = (pdo_voltage > max_voltage) ? pdo_voltage
+ : max_voltage;
}
+ val->intval = max_voltage;
break;
case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0:
case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
@@ -144,15 +173,25 @@ static int ucsi_psy_get_current_max(struct ucsi_connector *con,
union power_supply_propval *val)
{
u32 pdo;
+ int max_current = 0;
+
+ if (!UCSI_CONSTAT(con, CONNECTED)) {
+ val->intval = 0;
+ return 0;
+ }
switch (UCSI_CONSTAT(con, PWR_OPMODE)) {
case UCSI_CONSTAT_PWR_OPMODE_PD:
- if (con->num_pdos > 0) {
- pdo = con->src_pdos[con->num_pdos - 1];
- val->intval = pdo_max_current(pdo) * 1000;
- } else {
- val->intval = 0;
+ for (int i = 0; i < con->num_pdos; i++) {
+ int pdo_current = 0;
+
+ pdo = con->src_pdos[i];
+ if (pdo_type(pdo) == PDO_TYPE_FIXED)
+ pdo_current = pdo_max_current(pdo) * 1000;
+ max_current = (pdo_current > max_current) ? pdo_current
+ : max_current;
}
+ val->intval = max_current;
break;
case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
val->intval = UCSI_TYPEC_1_5_CURRENT * 1000;
@@ -164,7 +203,7 @@ static int ucsi_psy_get_current_max(struct ucsi_connector *con,
case UCSI_CONSTAT_PWR_OPMODE_DEFAULT:
/* UCSI can't tell b/w DCP/CDP or USB2/3x1/3x2 SDP chargers */
default:
- val->intval = 0;
+ val->intval = UCSI_TYPEC_DEFAULT_CURRENT * 1000;
break;
}
return 0;
@@ -173,10 +212,28 @@ static int ucsi_psy_get_current_max(struct ucsi_connector *con,
static int ucsi_psy_get_current_now(struct ucsi_connector *con,
union power_supply_propval *val)
{
- if (UCSI_CONSTAT(con, PWR_OPMODE) == UCSI_CONSTAT_PWR_OPMODE_PD)
- val->intval = rdo_op_current(con->rdo) * 1000;
- else
+ if (!UCSI_CONSTAT(con, CONNECTED)) {
val->intval = 0;
+ return 0;
+ }
+
+ switch (UCSI_CONSTAT(con, PWR_OPMODE)) {
+ case UCSI_CONSTAT_PWR_OPMODE_PD:
+ val->intval = rdo_op_current(con->rdo) * 1000;
+ break;
+ case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
+ val->intval = UCSI_TYPEC_1_5_CURRENT * 1000;
+ break;
+ case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0:
+ val->intval = UCSI_TYPEC_3_0_CURRENT * 1000;
+ break;
+ case UCSI_CONSTAT_PWR_OPMODE_BC:
+ case UCSI_CONSTAT_PWR_OPMODE_DEFAULT:
+ /* UCSI can't tell b/w DCP/CDP or USB2/3x1/3x2 SDP chargers */
+ default:
+ val->intval = UCSI_TYPEC_DEFAULT_CURRENT * 1000;
+ break;
+ }
return 0;
}
@@ -245,6 +302,8 @@ static int ucsi_psy_get_prop(struct power_supply *psy,
return ucsi_psy_get_current_now(con, val);
case POWER_SUPPLY_PROP_SCOPE:
return ucsi_psy_get_scope(con, val);
+ case POWER_SUPPLY_PROP_STATUS:
+ return ucsi_psy_get_status(con, val);
default:
return -EINVAL;
}
diff --git a/drivers/usb/typec/ucsi/thunderbolt.c b/drivers/usb/typec/ucsi/thunderbolt.c
new file mode 100644
index 000000000000..434d3d8ea5b6
--- /dev/null
+++ b/drivers/usb/typec/ucsi/thunderbolt.c
@@ -0,0 +1,212 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * UCSI Thunderbolt Alternate Mode Support
+ *
+ * Copyright 2026 Google LLC
+ */
+
+#include <linux/usb/typec_tbt.h>
+#include <linux/usb/pd_vdo.h>
+#include <linux/err.h>
+#include <linux/dev_printk.h>
+#include <linux/device/devres.h>
+#include <linux/gfp_types.h>
+#include <linux/types.h>
+#include <linux/usb/typec_altmode.h>
+#include <linux/workqueue.h>
+
+#include "ucsi.h"
+
+/**
+ * struct ucsi_tbt - Thunderbolt Alternate Mode private data structure
+ * @con: Pointer to UCSI connector structure
+ * @alt: Pointer to typec altmode structure
+ * @work: Work structure
+ * @cam: An offset into the list of alternate modes supported by the PPM
+ * @header: VDO header
+ */
+struct ucsi_tbt {
+ struct ucsi_connector *con;
+ struct typec_altmode *alt;
+ struct work_struct work;
+ int cam;
+ u32 header;
+};
+
+static void ucsi_thunderbolt_work(struct work_struct *work)
+{
+ struct ucsi_tbt *tbt = container_of(work, struct ucsi_tbt, work);
+
+ if (typec_altmode_vdm(tbt->alt, tbt->header, NULL, 0))
+ dev_err(&tbt->alt->dev, "VDM 0x%x failed\n", tbt->header);
+
+ tbt->header = 0;
+}
+
+static int ucsi_thunderbolt_set_altmode(struct ucsi_tbt *tbt,
+ bool enter, u32 vdo)
+{
+ int svdm_version;
+ int cmd;
+ int ret;
+ u64 command = UCSI_SET_NEW_CAM |
+ UCSI_CONNECTOR_NUMBER(tbt->con->num) |
+ UCSI_SET_NEW_CAM_SET_AM(tbt->cam) |
+ ((u64)vdo << 32);
+
+ if (enter)
+ command |= (1 << 23);
+
+ ret = ucsi_send_command(tbt->con->ucsi, command, NULL, 0);
+ if (ret < 0)
+ return ret;
+
+ svdm_version = typec_altmode_get_svdm_version(tbt->alt);
+ if (svdm_version < 0)
+ return svdm_version;
+
+ if (enter)
+ cmd = CMD_ENTER_MODE;
+ else
+ cmd = CMD_EXIT_MODE;
+ tbt->header = VDO(USB_TYPEC_TBT_SID, 1, svdm_version, cmd);
+ tbt->header |= VDO_OPOS(TYPEC_TBT_MODE);
+ tbt->header |= VDO_CMDT(CMDT_RSP_ACK);
+
+ schedule_work(&tbt->work);
+
+ return 0;
+}
+
+static int ucsi_thunderbolt_enter(struct typec_altmode *alt, u32 *vdo)
+{
+ struct ucsi_tbt *tbt = typec_altmode_get_drvdata(alt);
+ struct ucsi_connector *con = tbt->con;
+ u64 command;
+ u8 cur = 0;
+ int ret;
+
+ if (!ucsi_con_mutex_lock(con))
+ return -ENOTCONN;
+
+ command = UCSI_GET_CURRENT_CAM | UCSI_CONNECTOR_NUMBER(con->num);
+ ret = ucsi_send_command(con->ucsi, command, &cur, sizeof(cur));
+ if (ret < 0) {
+ if (con->ucsi->version > 0x0100)
+ goto err_unlock;
+ cur = 0xff;
+ }
+
+ if (cur != 0xff) {
+ if (cur >= UCSI_MAX_ALTMODES || con->port_altmode[cur] != alt)
+ ret = -EBUSY;
+ else
+ ret = 0;
+ goto err_unlock;
+ }
+
+ ret = ucsi_thunderbolt_set_altmode(tbt, true, *vdo);
+ ucsi_altmode_update_active(tbt->con);
+
+err_unlock:
+ ucsi_con_mutex_unlock(con);
+
+ return ret;
+}
+
+static int ucsi_thunderbolt_exit(struct typec_altmode *alt)
+{
+ struct ucsi_tbt *tbt = typec_altmode_get_drvdata(alt);
+ int ret;
+
+ if (!ucsi_con_mutex_lock(tbt->con))
+ return -ENOTCONN;
+
+ ret = ucsi_thunderbolt_set_altmode(tbt, false, 0);
+
+ ucsi_con_mutex_unlock(tbt->con);
+
+ return ret;
+}
+
+static int ucsi_thunderbolt_vdm(struct typec_altmode *alt,
+ u32 header, const u32 *data, int count)
+{
+ struct ucsi_tbt *tbt = typec_altmode_get_drvdata(alt);
+ int cmd_type = PD_VDO_CMDT(header);
+ int cmd = PD_VDO_CMD(header);
+ int svdm_version;
+
+ if (!ucsi_con_mutex_lock(tbt->con))
+ return -ENOTCONN;
+
+ svdm_version = typec_altmode_get_svdm_version(alt);
+ if (svdm_version < 0) {
+ ucsi_con_mutex_unlock(tbt->con);
+ return svdm_version;
+ }
+
+ switch (cmd_type) {
+ case CMDT_INIT:
+ if (PD_VDO_SVDM_VER(header) < svdm_version) {
+ svdm_version = PD_VDO_SVDM_VER(header);
+ typec_partner_set_svdm_version(tbt->con->partner, svdm_version);
+ }
+ tbt->header = VDO(USB_TYPEC_TBT_SID, 1, svdm_version, cmd);
+ tbt->header |= VDO_OPOS(TYPEC_TBT_MODE);
+ tbt->header |= VDO_CMDT(CMDT_RSP_ACK);
+
+ schedule_work(&tbt->work);
+ break;
+ default:
+ break;
+ }
+
+ ucsi_con_mutex_unlock(tbt->con);
+
+ return 0;
+}
+
+static const struct typec_altmode_ops ucsi_thunderbolt_ops = {
+ .enter = ucsi_thunderbolt_enter,
+ .exit = ucsi_thunderbolt_exit,
+ .vdm = ucsi_thunderbolt_vdm,
+};
+
+struct typec_altmode *ucsi_register_thunderbolt(struct ucsi_connector *con,
+ bool override, int offset,
+ struct typec_altmode_desc *desc)
+{
+ struct typec_altmode *alt;
+ struct ucsi_tbt *tbt;
+
+ alt = typec_port_register_altmode(con->port, desc);
+ if (IS_ERR(alt) || !override)
+ return alt;
+
+ tbt = devm_kzalloc(&alt->dev, sizeof(*tbt), GFP_KERNEL);
+ if (!tbt) {
+ typec_unregister_altmode(alt);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ tbt->cam = offset;
+ tbt->con = con;
+ tbt->alt = alt;
+ INIT_WORK(&tbt->work, ucsi_thunderbolt_work);
+ typec_altmode_set_drvdata(alt, tbt);
+ typec_altmode_set_ops(alt, &ucsi_thunderbolt_ops);
+
+ return alt;
+}
+
+void ucsi_thunderbolt_remove_partner(struct typec_altmode *alt)
+{
+ struct ucsi_tbt *tbt;
+
+ if (alt) {
+ tbt = typec_altmode_get_drvdata(alt);
+ if (tbt)
+ cancel_work_sync(&tbt->work);
+ }
+}
diff --git a/drivers/usb/typec/ucsi/trace.c b/drivers/usb/typec/ucsi/trace.c
index cb62ad835761..13a38422743a 100644
--- a/drivers/usb/typec/ucsi/trace.c
+++ b/drivers/usb/typec/ucsi/trace.c
@@ -12,7 +12,7 @@ static const char * const ucsi_cmd_strs[] = {
[UCSI_SET_NOTIFICATION_ENABLE] = "SET_NOTIFICATION_ENABLE",
[UCSI_GET_CAPABILITY] = "GET_CAPABILITY",
[UCSI_GET_CONNECTOR_CAPABILITY] = "GET_CONNECTOR_CAPABILITY",
- [UCSI_SET_UOM] = "SET_UOM",
+ [UCSI_SET_CCOM] = "SET_CCOM",
[UCSI_SET_UOR] = "SET_UOR",
[UCSI_SET_PDM] = "SET_PDM",
[UCSI_SET_PDR] = "SET_PDR",
@@ -33,23 +33,6 @@ const char *ucsi_cmd_str(u64 raw_cmd)
return ucsi_cmd_strs[(cmd >= ARRAY_SIZE(ucsi_cmd_strs)) ? 0 : cmd];
}
-const char *ucsi_cci_str(u32 cci)
-{
- if (UCSI_CCI_CONNECTOR(cci)) {
- if (cci & UCSI_CCI_ACK_COMPLETE)
- return "Event pending (ACK completed)";
- if (cci & UCSI_CCI_COMMAND_COMPLETE)
- return "Event pending (command completed)";
- return "Connector Change";
- }
- if (cci & UCSI_CCI_ACK_COMPLETE)
- return "ACK completed";
- if (cci & UCSI_CCI_COMMAND_COMPLETE)
- return "Command completed";
-
- return "";
-}
-
static const char * const ucsi_recipient_strs[] = {
[UCSI_RECIPIENT_CON] = "port",
[UCSI_RECIPIENT_SOP] = "partner",
diff --git a/drivers/usb/typec/ucsi/trace.h b/drivers/usb/typec/ucsi/trace.h
index 41701dee7056..9554a0207276 100644
--- a/drivers/usb/typec/ucsi/trace.h
+++ b/drivers/usb/typec/ucsi/trace.h
@@ -10,7 +10,6 @@
#include <linux/usb/typec_altmode.h>
const char *ucsi_cmd_str(u64 raw_cmd);
-const char *ucsi_cci_str(u32 cci);
const char *ucsi_recipient_str(u8 recipient);
DECLARE_EVENT_CLASS(ucsi_log_command,
diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c
index 2a2915b0a645..5b7ad9e99cb9 100644
--- a/drivers/usb/typec/ucsi/ucsi.c
+++ b/drivers/usb/typec/ucsi/ucsi.c
@@ -13,6 +13,7 @@
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/usb/typec_dp.h>
+#include <linux/usb/typec_tbt.h>
#include "ucsi.h"
#include "trace.h"
@@ -42,8 +43,14 @@ void ucsi_notify_common(struct ucsi *ucsi, u32 cci)
if (cci & UCSI_CCI_BUSY)
return;
- if (UCSI_CCI_CONNECTOR(cci))
- ucsi_connector_change(ucsi, UCSI_CCI_CONNECTOR(cci));
+ if (UCSI_CCI_CONNECTOR(cci)) {
+ if (!ucsi->cap.num_connectors ||
+ UCSI_CCI_CONNECTOR(cci) <= ucsi->cap.num_connectors)
+ ucsi_connector_change(ucsi, UCSI_CCI_CONNECTOR(cci));
+ else
+ dev_err(ucsi->dev, "bogus connector number in CCI: %lu\n",
+ UCSI_CCI_CONNECTOR(cci));
+ }
if (cci & UCSI_CCI_ACK_COMPLETE &&
test_and_clear_bit(ACK_PENDING, &ucsi->flags))
@@ -55,7 +62,8 @@ void ucsi_notify_common(struct ucsi *ucsi, u32 cci)
}
EXPORT_SYMBOL_GPL(ucsi_notify_common);
-int ucsi_sync_control_common(struct ucsi *ucsi, u64 command)
+int ucsi_sync_control_common(struct ucsi *ucsi, u64 command, u32 *cci,
+ void *data, size_t size)
{
bool ack = UCSI_COMMAND(command) == UCSI_ACK_CC_CI;
int ret;
@@ -80,6 +88,13 @@ out_clear_bit:
else
clear_bit(COMMAND_PENDING, &ucsi->flags);
+ if (!ret && cci)
+ ret = ucsi->ops->read_cci(ucsi, cci);
+
+ if (!ret && data &&
+ (*cci & UCSI_CCI_COMMAND_COMPLETE))
+ ret = ucsi->ops->read_message_in(ucsi, data, size);
+
return ret;
}
EXPORT_SYMBOL_GPL(ucsi_sync_control_common);
@@ -95,7 +110,7 @@ static int ucsi_acknowledge(struct ucsi *ucsi, bool conn_ack)
ctrl |= UCSI_ACK_CONNECTOR_CHANGE;
}
- return ucsi->ops->sync_control(ucsi, ctrl);
+ return ucsi->ops->sync_control(ucsi, ctrl, NULL, NULL, 0);
}
static int ucsi_run_command(struct ucsi *ucsi, u64 command, u32 *cci,
@@ -108,9 +123,7 @@ static int ucsi_run_command(struct ucsi *ucsi, u64 command, u32 *cci,
if (size > UCSI_MAX_DATA_LENGTH(ucsi))
return -EINVAL;
- ret = ucsi->ops->sync_control(ucsi, command);
- if (ucsi->ops->read_cci(ucsi, cci))
- return -EIO;
+ ret = ucsi->ops->sync_control(ucsi, command, cci, data, size);
if (*cci & UCSI_CCI_BUSY)
return ucsi_run_command(ucsi, UCSI_CANCEL, cci, NULL, 0, false) ?: -EBUSY;
@@ -127,9 +140,6 @@ static int ucsi_run_command(struct ucsi *ucsi, u64 command, u32 *cci,
else
err = 0;
- if (!err && data && UCSI_CCI_LENGTH(*cci))
- err = ucsi->ops->read_message_in(ucsi, data, size);
-
/*
* Don't ACK connection change if there was an error.
*/
@@ -231,6 +241,8 @@ static int ucsi_send_command_common(struct ucsi *ucsi, u64 cmd,
if (cci & UCSI_CCI_ERROR)
ret = ucsi_read_error(ucsi, connector_num);
+ trace_ucsi_run_command(cmd, ret);
+
mutex_unlock(&ucsi->ppm_lock);
return ret;
}
@@ -289,7 +301,7 @@ static int ucsi_partner_task(struct ucsi_connector *con,
if (!con->partner)
return 0;
- uwork = kzalloc(sizeof(*uwork), GFP_KERNEL);
+ uwork = kzalloc_obj(*uwork);
if (!uwork)
return -ENOMEM;
@@ -311,6 +323,7 @@ void ucsi_altmode_update_active(struct ucsi_connector *con)
{
const struct typec_altmode *altmode = NULL;
u64 command;
+ u16 svid = 0;
int ret;
u8 cur;
int i;
@@ -332,6 +345,10 @@ void ucsi_altmode_update_active(struct ucsi_connector *con)
for (i = 0; con->partner_altmode[i]; i++)
typec_altmode_update_active(con->partner_altmode[i],
con->partner_altmode[i] == altmode);
+
+ if (altmode)
+ svid = altmode->svid;
+ typec_altmode_state_update(con->partner, svid, 0);
}
static int ucsi_altmode_next_mode(struct typec_altmode **alt, u16 svid)
@@ -409,6 +426,9 @@ static int ucsi_register_altmode(struct ucsi_connector *con,
alt = ucsi_register_displayport(con, override,
i, desc);
break;
+ case USB_TYPEC_TBT_SID:
+ alt = ucsi_register_thunderbolt(con, override, i, desc);
+ break;
default:
alt = typec_port_register_altmode(con->port, desc);
break;
@@ -528,13 +548,12 @@ ucsi_register_altmodes_nvidia(struct ucsi_connector *con, u8 recipient)
* Update the original altmode table as some ppms may report
* multiple DP altmodes.
*/
- if (recipient == UCSI_RECIPIENT_CON)
- multi_dp = ucsi->ops->update_altmodes(ucsi, orig, updated);
+ multi_dp = ucsi->ops->update_altmodes(ucsi, recipient, orig, updated);
/* now register altmodes */
for (i = 0; i < max_altmodes; i++) {
memset(&desc, 0, sizeof(desc));
- if (multi_dp && recipient == UCSI_RECIPIENT_CON) {
+ if (multi_dp) {
desc.svid = updated[i].svid;
desc.vdo = updated[i].mid;
} else {
@@ -607,6 +626,8 @@ static int ucsi_register_altmodes(struct ucsi_connector *con, u8 recipient)
desc.vdo = alt[j].mid;
desc.svid = alt[j].svid;
desc.roles = TYPEC_PORT_DRD;
+ desc.mode_selection = con->ucsi->ops->add_partner_altmodes &&
+ !con->typec_cap.no_mode_control;
ret = ucsi_register_altmode(con, &desc, recipient);
if (ret)
@@ -638,12 +659,15 @@ static void ucsi_unregister_altmodes(struct ucsi_connector *con, u8 recipient)
}
while (adev[i]) {
- if (recipient == UCSI_RECIPIENT_SOP &&
- (adev[i]->svid == USB_TYPEC_DP_SID ||
- (adev[i]->svid == USB_TYPEC_NVIDIA_VLINK_SID &&
- adev[i]->vdo != USB_TYPEC_NVIDIA_VLINK_DBG_VDO))) {
+ if (recipient == UCSI_RECIPIENT_SOP) {
pdev = typec_altmode_get_partner(adev[i]);
- ucsi_displayport_remove_partner((void *)pdev);
+
+ if (adev[i]->svid == USB_TYPEC_DP_SID ||
+ (adev[i]->svid == USB_TYPEC_NVIDIA_VLINK_SID &&
+ adev[i]->vdo != USB_TYPEC_NVIDIA_VLINK_DBG_VDO))
+ ucsi_displayport_remove_partner((void *)pdev);
+ else if (adev[i]->svid == USB_TYPEC_TBT_SID)
+ ucsi_thunderbolt_remove_partner((void *)pdev);
}
typec_unregister_altmode(adev[i]);
adev[i++] = NULL;
@@ -829,6 +853,8 @@ static int ucsi_check_altmodes(struct ucsi_connector *con)
if (con->partner_altmode[0]) {
num_partner_am = ucsi_get_num_altmode(con->partner_altmode);
typec_partner_set_num_altmodes(con->partner, num_partner_am);
+ if (con->ucsi->ops->add_partner_altmodes)
+ con->ucsi->ops->add_partner_altmodes(con);
ucsi_altmode_update_active(con);
return 0;
} else {
@@ -1006,6 +1032,28 @@ static int ucsi_check_connector_capability(struct ucsi_connector *con)
return ret;
}
+static void ucsi_orientation(struct ucsi_connector *con)
+{
+ if (con->ucsi->version < UCSI_VERSION_2_0)
+ return;
+
+ if (!UCSI_CONSTAT(con, CONNECTED)) {
+ typec_set_orientation(con->port, TYPEC_ORIENTATION_NONE);
+ return;
+ }
+
+ switch (UCSI_CONSTAT(con, ORIENTATION)) {
+ case UCSI_CONSTAT_ORIENTATION_NORMAL:
+ typec_set_orientation(con->port, TYPEC_ORIENTATION_NORMAL);
+ break;
+ case UCSI_CONSTAT_ORIENTATION_REVERSE:
+ typec_set_orientation(con->port, TYPEC_ORIENTATION_REVERSE);
+ break;
+ default:
+ break;
+ }
+}
+
static void ucsi_pwr_opmode_change(struct ucsi_connector *con)
{
switch (UCSI_CONSTAT(con, PWR_OPMODE)) {
@@ -1020,14 +1068,17 @@ static void ucsi_pwr_opmode_change(struct ucsi_connector *con)
case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
con->rdo = 0;
typec_set_pwr_opmode(con->port, TYPEC_PWR_MODE_1_5A);
+ ucsi_port_psy_changed(con);
break;
case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0:
con->rdo = 0;
typec_set_pwr_opmode(con->port, TYPEC_PWR_MODE_3_0A);
+ ucsi_port_psy_changed(con);
break;
default:
con->rdo = 0;
typec_set_pwr_opmode(con->port, TYPEC_PWR_MODE_USB);
+ ucsi_port_psy_changed(con);
break;
}
}
@@ -1092,6 +1143,8 @@ static void ucsi_unregister_partner(struct ucsi_connector *con)
return;
typec_set_mode(con->port, TYPEC_STATE_SAFE);
+ if (con->ucsi->ops->remove_partner_altmodes)
+ con->ucsi->ops->remove_partner_altmodes(con);
typec_partner_set_usb_power_delivery(con->partner, NULL);
ucsi_unregister_partner_pdos(con);
@@ -1135,10 +1188,18 @@ static void ucsi_partner_change(struct ucsi_connector *con)
if (UCSI_CONSTAT(con, PARTNER_FLAG_USB))
typec_set_mode(con->port, TYPEC_STATE_USB);
}
+
+ if (((con->ucsi->version >= UCSI_VERSION_3_0 &&
+ UCSI_CONSTAT(con, PARTNER_FLAG_USB4_GEN4)) ||
+ (con->ucsi->version >= UCSI_VERSION_2_0 &&
+ UCSI_CONSTAT(con, PARTNER_FLAG_USB4_GEN3))) && con->partner)
+ typec_partner_set_usb_mode(con->partner, USB_MODE_USB4);
}
- /* Only notify USB controller if partner supports USB data */
- if (!(UCSI_CONSTAT(con, PARTNER_FLAG_USB)))
+ if ((!UCSI_CONSTAT(con, PARTNER_FLAG_USB)) &&
+ ((con->ucsi->quirks & UCSI_USB4_IMPLIES_USB) &&
+ (!(UCSI_CONSTAT(con, PARTNER_FLAG_USB4_GEN3) ||
+ UCSI_CONSTAT(con, PARTNER_FLAG_USB4_GEN4)))))
u_role = USB_ROLE_NONE;
ret = usb_role_switch_set_role(con->usb_role_sw, u_role);
@@ -1215,9 +1276,11 @@ static void ucsi_handle_connector_change(struct work_struct *work)
struct ucsi_connector *con = container_of(work, struct ucsi_connector,
work);
struct ucsi *ucsi = con->ucsi;
+ u8 curr_scale, volt_scale;
enum typec_role role;
u16 change;
int ret;
+ u32 val;
mutex_lock(&con->lock);
@@ -1243,6 +1306,7 @@ static void ucsi_handle_connector_change(struct work_struct *work)
if (change & UCSI_CONSTAT_POWER_DIR_CHANGE) {
typec_set_pwr_role(con->port, role);
+ ucsi_port_psy_changed(con);
/* Complete pending power role swap */
if (!completion_done(&con->complete))
@@ -1253,6 +1317,7 @@ static void ucsi_handle_connector_change(struct work_struct *work)
typec_set_pwr_role(con->port, role);
ucsi_port_psy_changed(con);
ucsi_partner_change(con);
+ ucsi_orientation(con);
if (UCSI_CONSTAT(con, CONNECTED)) {
ucsi_register_partner(con);
@@ -1276,6 +1341,7 @@ static void ucsi_handle_connector_change(struct work_struct *work)
if (con->partner && (change & UCSI_CONSTAT_PARTNER_CHANGE)) {
ucsi_partner_change(con);
+ ucsi_altmode_update_active(con);
/* Complete pending data role swap */
if (!completion_done(&con->complete))
@@ -1285,9 +1351,24 @@ static void ucsi_handle_connector_change(struct work_struct *work)
if (change & UCSI_CONSTAT_CAM_CHANGE)
ucsi_partner_task(con, ucsi_check_altmodes, 1, HZ);
- if (change & UCSI_CONSTAT_BC_CHANGE)
+ if (change & (UCSI_CONSTAT_BC_CHANGE | UCSI_CONSTAT_SINK_PATH_CHANGE))
ucsi_port_psy_changed(con);
+ if (con->ucsi->version >= UCSI_VERSION_2_1 &&
+ UCSI_CONSTAT(con, PWR_READING_READY_V2_1)) {
+ curr_scale = UCSI_CONSTAT(con, CURRENT_SCALE_V2_1);
+ volt_scale = UCSI_CONSTAT(con, VOLTAGE_SCALE_V2_1);
+
+ val = UCSI_CONSTAT(con, PEAK_CURRENT_V2_1);
+ con->peak_current = UCSI_CONSTAT_CURR_SCALE_MULT * curr_scale * val;
+
+ val = UCSI_CONSTAT(con, AVG_CURRENT_V2_1);
+ con->avg_current = UCSI_CONSTAT_CURR_SCALE_MULT * curr_scale * val;
+
+ val = UCSI_CONSTAT(con, VBUS_VOLTAGE_V2_1);
+ con->vbus_voltage = UCSI_CONSTAT_VOLT_SCALE_MULT * volt_scale * val;
+ }
+
out_unlock:
mutex_unlock(&con->lock);
}
@@ -1613,6 +1694,10 @@ static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con)
cap->driver_data = con;
cap->ops = &ucsi_ops;
+ cap->no_mode_control = !(con->ucsi->cap.features & UCSI_CAP_ALT_MODE_OVERRIDE);
+
+ if (ucsi->version >= UCSI_VERSION_2_0)
+ con->typec_cap.orientation_aware = true;
if (ucsi->ops->update_connector)
ucsi->ops->update_connector(con);
@@ -1670,6 +1755,7 @@ static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con)
typec_set_pwr_role(con->port, UCSI_CONSTAT(con, PWR_DIR));
ucsi_register_partner(con);
ucsi_pwr_opmode_change(con);
+ ucsi_orientation(con);
ucsi_port_psy_changed(con);
if (con->ucsi->cap.features & UCSI_CAP_GET_PD_MESSAGE)
ucsi_get_partner_identity(con);
@@ -1787,9 +1873,15 @@ static int ucsi_init(struct ucsi *ucsi)
ret = -ENODEV;
goto err_reset;
}
+ /* Check if reserved bit set. This is out of spec but happens in buggy FW */
+ if (ucsi->cap.num_connectors & 0x80) {
+ dev_warn(ucsi->dev, "UCSI: Invalid num_connectors %d. Likely buggy FW\n",
+ ucsi->cap.num_connectors);
+ ucsi->cap.num_connectors &= 0x7f; // clear bit and carry on
+ }
/* Allocate the connectors. Released in ucsi_unregister() */
- connector = kcalloc(ucsi->cap.num_connectors + 1, sizeof(*connector), GFP_KERNEL);
+ connector = kzalloc_objs(*connector, ucsi->cap.num_connectors + 1);
if (!connector) {
ret = -ENOMEM;
goto err_reset;
@@ -1920,6 +2012,40 @@ void ucsi_set_drvdata(struct ucsi *ucsi, void *data)
EXPORT_SYMBOL_GPL(ucsi_set_drvdata);
/**
+ * ucsi_con_mutex_lock - Acquire the connector mutex
+ * @con: The connector interface to lock
+ *
+ * Returns true on success, false if the connector is disconnected
+ */
+bool ucsi_con_mutex_lock(struct ucsi_connector *con)
+{
+ bool mutex_locked = false;
+ bool connected = true;
+
+ while (connected && !mutex_locked) {
+ mutex_locked = mutex_trylock(&con->lock) != 0;
+ connected = UCSI_CONSTAT(con, CONNECTED);
+ if (connected && !mutex_locked)
+ msleep(20);
+ }
+
+ connected = connected && con->partner;
+ if (!connected && mutex_locked)
+ mutex_unlock(&con->lock);
+
+ return connected;
+}
+
+/**
+ * ucsi_con_mutex_unlock - Release the connector mutex
+ * @con: The connector interface to unlock
+ */
+void ucsi_con_mutex_unlock(struct ucsi_connector *con)
+{
+ mutex_unlock(&con->lock);
+}
+
+/**
* ucsi_create - Allocate UCSI instance
* @dev: Device interface to the PPM (Platform Policy Manager)
* @ops: I/O routines
@@ -1933,7 +2059,7 @@ struct ucsi *ucsi_create(struct device *dev, const struct ucsi_operations *ops)
!ops->read_message_in || !ops->sync_control || !ops->async_control)
return ERR_PTR(-EINVAL);
- ucsi = kzalloc(sizeof(*ucsi), GFP_KERNEL);
+ ucsi = kzalloc_obj(*ucsi);
if (!ucsi)
return ERR_PTR(-ENOMEM);
diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h
index 28780acc4af2..cff9ddc2ae21 100644
--- a/drivers/usb/typec/ucsi/ucsi.h
+++ b/drivers/usb/typec/ucsi/ucsi.h
@@ -50,6 +50,7 @@ struct dentry;
/* Command Status and Connector Change Indication (CCI) bits */
#define UCSI_CCI_CONNECTOR(_c_) (((_c_) & GENMASK(7, 1)) >> 1)
#define UCSI_CCI_LENGTH(_c_) (((_c_) & GENMASK(15, 8)) >> 8)
+#define UCSI_SET_CCI_LENGTH(_c_) ((_c_) << 8)
#define UCSI_CCI_NOT_SUPPORTED BIT(25)
#define UCSI_CCI_CANCEL_COMPLETE BIT(26)
#define UCSI_CCI_RESET_COMPLETE BIT(27)
@@ -69,6 +70,8 @@ struct dentry;
* @update_altmodes: Squashes duplicate DP altmodes
* @update_connector: Update connector capabilities before registering
* @connector_status: Updates connector status, called holding connector lock
+ * @add_partner_altmodes: Start mode selection
+ * @remove_partner_altmodes: Clean mode selection
*
* Read and write routines for UCSI interface. @sync_write must wait for the
* Command Completion Event from the PPM before returning, and @async_write must
@@ -79,12 +82,16 @@ struct ucsi_operations {
int (*read_cci)(struct ucsi *ucsi, u32 *cci);
int (*poll_cci)(struct ucsi *ucsi, u32 *cci);
int (*read_message_in)(struct ucsi *ucsi, void *val, size_t val_len);
- int (*sync_control)(struct ucsi *ucsi, u64 command);
+ int (*sync_control)(struct ucsi *ucsi, u64 command, u32 *cci,
+ void *data, size_t size);
int (*async_control)(struct ucsi *ucsi, u64 command);
- bool (*update_altmodes)(struct ucsi *ucsi, struct ucsi_altmode *orig,
+ bool (*update_altmodes)(struct ucsi *ucsi, u8 recipient,
+ struct ucsi_altmode *orig,
struct ucsi_altmode *updated);
void (*update_connector)(struct ucsi_connector *con);
void (*connector_status)(struct ucsi_connector *con);
+ void (*add_partner_altmodes)(struct ucsi_connector *con);
+ void (*remove_partner_altmodes)(struct ucsi_connector *con);
};
struct ucsi *ucsi_create(struct device *dev, const struct ucsi_operations *ops);
@@ -93,6 +100,8 @@ int ucsi_register(struct ucsi *ucsi);
void ucsi_unregister(struct ucsi *ucsi);
void *ucsi_get_drvdata(struct ucsi *ucsi);
void ucsi_set_drvdata(struct ucsi *ucsi, void *data);
+bool ucsi_con_mutex_lock(struct ucsi_connector *con);
+void ucsi_con_mutex_unlock(struct ucsi_connector *con);
void ucsi_connector_change(struct ucsi *ucsi, u8 num);
@@ -108,7 +117,7 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num);
#define UCSI_GET_CAPABILITY_SIZE 128
#define UCSI_GET_CONNECTOR_CAPABILITY 0x07
#define UCSI_GET_CONNECTOR_CAPABILITY_SIZE 32
-#define UCSI_SET_UOM 0x08
+#define UCSI_SET_CCOM 0x08
#define UCSI_SET_UOR 0x09
#define UCSI_SET_PDM 0x0a
#define UCSI_SET_PDR 0x0b
@@ -122,8 +131,14 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num);
#define UCSI_GET_CONNECTOR_STATUS 0x12
#define UCSI_GET_CONNECTOR_STATUS_SIZE 152
#define UCSI_GET_ERROR_STATUS 0x13
+#define UCSI_SET_POWER_LEVEL 0x14
+#define UCSI_GET_ATTENTION_VDO 0x16
#define UCSI_GET_PD_MESSAGE 0x15
+#define UCSI_GET_CAM_CS 0x18
#define UCSI_SET_SINK_PATH 0x1c
+#define UCSI_READ_POWER_LEVEL 0x1e
+#define UCSI_SET_USB 0x21
+#define UCSI_GET_LPM_PPM_INFO 0x22
#define UCSI_CONNECTOR_NUMBER(_num_) ((u64)(_num_) << 16)
#define UCSI_COMMAND(_cmd_) ((_cmd_) & 0xff)
@@ -350,6 +365,20 @@ struct ucsi_cable_property {
#define UCSI_CONSTAT_BC_SLOW_CHARGING 2
#define UCSI_CONSTAT_BC_TRICKLE_CHARGING 3
#define UCSI_CONSTAT_PD_VERSION_V1_2 UCSI_DECLARE_BITFIELD_V1_2(70, 16)
+#define UCSI_CONSTAT_ORIENTATION UCSI_DECLARE_BITFIELD_V2_0(86, 1)
+#define UCSI_CONSTAT_ORIENTATION_NORMAL 0
+#define UCSI_CONSTAT_ORIENTATION_REVERSE 1
+#define UCSI_CONSTAT_SINK_PATH_STATUS_V2_0 UCSI_DECLARE_BITFIELD_V2_0(87, 1)
+#define UCSI_CONSTAT_SINK_PATH_DISABLED 0
+#define UCSI_CONSTAT_SINK_PATH_ENABLED 1
+#define UCSI_CONSTAT_PWR_READING_READY_V2_1 UCSI_DECLARE_BITFIELD_V2_1(89, 1)
+#define UCSI_CONSTAT_CURRENT_SCALE_V2_1 UCSI_DECLARE_BITFIELD_V2_1(90, 3)
+#define UCSI_CONSTAT_PEAK_CURRENT_V2_1 UCSI_DECLARE_BITFIELD_V2_1(93, 16)
+#define UCSI_CONSTAT_AVG_CURRENT_V2_1 UCSI_DECLARE_BITFIELD_V2_1(109, 16)
+#define UCSI_CONSTAT_VOLTAGE_SCALE_V2_1 UCSI_DECLARE_BITFIELD_V2_1(125, 4)
+#define UCSI_CONSTAT_VBUS_VOLTAGE_V2_1 UCSI_DECLARE_BITFIELD_V2_1(129, 16)
+#define UCSI_CONSTAT_CURR_SCALE_MULT 5
+#define UCSI_CONSTAT_VOLT_SCALE_MULT 5
/* Connector Status Change Bits. */
#define UCSI_CONSTAT_EXT_SUPPLY_CHANGE BIT(1)
@@ -361,6 +390,7 @@ struct ucsi_cable_property {
#define UCSI_CONSTAT_BC_CHANGE BIT(9)
#define UCSI_CONSTAT_PARTNER_CHANGE BIT(11)
#define UCSI_CONSTAT_POWER_DIR_CHANGE BIT(12)
+#define UCSI_CONSTAT_SINK_PATH_CHANGE BIT(13)
#define UCSI_CONSTAT_CONNECT_CHANGE BIT(14)
#define UCSI_CONSTAT_ERROR BIT(15)
@@ -429,7 +459,7 @@ struct ucsi_debugfs_entry {
u64 low;
u64 high;
} response;
- u32 status;
+ int status;
struct dentry *dentry;
};
@@ -467,6 +497,9 @@ struct ucsi {
unsigned long quirks;
#define UCSI_NO_PARTNER_PDOS BIT(0) /* Don't read partner's PDOs */
#define UCSI_DELAY_DEVICE_PDOS BIT(1) /* Reading PDOs fails until the parter is in PD mode */
+
+/* USB4 connection can imply that USB communcation is supported */
+#define UCSI_USB4_IMPLIES_USB BIT(2)
};
#define UCSI_MAX_DATA_LENGTH(u) (((u)->version < UCSI_VERSION_2_0) ? 0x10 : 0xff)
@@ -474,9 +507,10 @@ struct ucsi {
#define UCSI_MAX_SVID 5
#define UCSI_MAX_ALTMODES (UCSI_MAX_SVID * 6)
-#define UCSI_TYPEC_VSAFE5V 5000
-#define UCSI_TYPEC_1_5_CURRENT 1500
-#define UCSI_TYPEC_3_0_CURRENT 3000
+#define UCSI_TYPEC_VSAFE5V 5000
+#define UCSI_TYPEC_DEFAULT_CURRENT 100
+#define UCSI_TYPEC_1_5_CURRENT 1500
+#define UCSI_TYPEC_3_0_CURRENT 3000
struct ucsi_connector {
int num;
@@ -509,6 +543,10 @@ struct ucsi_connector {
u32 src_pdos[PDO_MAX_OBJECTS];
int num_pdos;
+ u32 peak_current;
+ u32 avg_current;
+ u32 vbus_voltage;
+
/* USB PD objects */
struct usb_power_delivery *pd;
struct usb_power_delivery_capabilities *port_source_caps;
@@ -531,7 +569,8 @@ void ucsi_altmode_update_active(struct ucsi_connector *con);
int ucsi_resume(struct ucsi *ucsi);
void ucsi_notify_common(struct ucsi *ucsi, u32 cci);
-int ucsi_sync_control_common(struct ucsi *ucsi, u64 command);
+int ucsi_sync_control_common(struct ucsi *ucsi, u64 command, u32 *cci,
+ void *data, size_t size);
#if IS_ENABLED(CONFIG_POWER_SUPPLY)
int ucsi_register_port_psy(struct ucsi_connector *con);
@@ -564,6 +603,26 @@ static inline void
ucsi_displayport_remove_partner(struct typec_altmode *adev) { }
#endif /* CONFIG_TYPEC_DP_ALTMODE */
+#if IS_ENABLED(CONFIG_TYPEC_TBT_ALTMODE)
+struct typec_altmode *
+ucsi_register_thunderbolt(struct ucsi_connector *con,
+ bool override, int offset,
+ struct typec_altmode_desc *desc);
+
+void ucsi_thunderbolt_remove_partner(struct typec_altmode *adev);
+#else
+static inline struct typec_altmode *
+ucsi_register_thunderbolt(struct ucsi_connector *con,
+ bool override, int offset,
+ struct typec_altmode_desc *desc)
+{
+ return typec_port_register_altmode(con->port, desc);
+}
+
+static inline void
+ucsi_thunderbolt_remove_partner(struct typec_altmode *adev) { }
+#endif /* CONFIG_TYPEC_TBT_ALTMODE */
+
#ifdef CONFIG_DEBUG_FS
void ucsi_debugfs_init(void);
void ucsi_debugfs_exit(void);
diff --git a/drivers/usb/typec/ucsi/ucsi_acpi.c b/drivers/usb/typec/ucsi/ucsi_acpi.c
index ac1ebb5d9527..6b92f296e985 100644
--- a/drivers/usb/typec/ucsi/ucsi_acpi.c
+++ b/drivers/usb/typec/ucsi/ucsi_acpi.c
@@ -105,17 +105,23 @@ static const struct ucsi_operations ucsi_acpi_ops = {
.async_control = ucsi_acpi_async_control
};
-static int ucsi_gram_read_message_in(struct ucsi *ucsi, void *val, size_t val_len)
+static int ucsi_gram_sync_control(struct ucsi *ucsi, u64 command, u32 *cci,
+ void *val, size_t len)
{
u16 bogus_change = UCSI_CONSTAT_POWER_LEVEL_CHANGE |
UCSI_CONSTAT_PDOS_CHANGE;
struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
int ret;
- ret = ucsi_acpi_read_message_in(ucsi, val, val_len);
+ ret = ucsi_sync_control_common(ucsi, command, cci, val, len);
if (ret < 0)
return ret;
+ if (UCSI_COMMAND(ua->cmd) == UCSI_GET_PDOS &&
+ ua->cmd & UCSI_GET_PDOS_PARTNER_PDO(1) &&
+ ua->cmd & UCSI_GET_PDOS_SRC_PDOS)
+ ua->check_bogus_event = true;
+
if (UCSI_COMMAND(ua->cmd) == UCSI_GET_CONNECTOR_STATUS &&
ua->check_bogus_event) {
/* Clear the bogus change */
@@ -128,28 +134,11 @@ static int ucsi_gram_read_message_in(struct ucsi *ucsi, void *val, size_t val_le
return ret;
}
-static int ucsi_gram_sync_control(struct ucsi *ucsi, u64 command)
-{
- struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
- int ret;
-
- ret = ucsi_sync_control_common(ucsi, command);
- if (ret < 0)
- return ret;
-
- if (UCSI_COMMAND(ua->cmd) == UCSI_GET_PDOS &&
- ua->cmd & UCSI_GET_PDOS_PARTNER_PDO(1) &&
- ua->cmd & UCSI_GET_PDOS_SRC_PDOS)
- ua->check_bogus_event = true;
-
- return ret;
-}
-
static const struct ucsi_operations ucsi_gram_ops = {
.read_version = ucsi_acpi_read_version,
.read_cci = ucsi_acpi_read_cci,
.poll_cci = ucsi_acpi_poll_cci,
- .read_message_in = ucsi_gram_read_message_in,
+ .read_message_in = ucsi_acpi_read_message_in,
.sync_control = ucsi_gram_sync_control,
.async_control = ucsi_acpi_async_control
};
diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c
index 4b1668733a4b..199799b319c2 100644
--- a/drivers/usb/typec/ucsi/ucsi_ccg.c
+++ b/drivers/usb/typec/ucsi/ucsi_ccg.c
@@ -10,6 +10,7 @@
#include <linux/acpi.h>
#include <linux/delay.h>
#include <linux/firmware.h>
+#include <linux/hex.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/pci.h>
@@ -222,7 +223,6 @@ struct ucsi_ccg {
u16 fw_build;
struct work_struct pm_work;
- u64 last_cmd_sent;
bool has_multiple_dp;
struct ucsi_ccg_altmode orig[UCSI_MAX_ALTMODES];
struct ucsi_ccg_altmode updated[UCSI_MAX_ALTMODES];
@@ -395,6 +395,7 @@ static void ucsi_ccg_update_get_current_cam_cmd(struct ucsi_ccg *uc, u8 *data)
}
static bool ucsi_ccg_update_altmodes(struct ucsi *ucsi,
+ u8 recipient,
struct ucsi_altmode *orig,
struct ucsi_altmode *updated)
{
@@ -403,6 +404,9 @@ static bool ucsi_ccg_update_altmodes(struct ucsi *ucsi,
int i, j, k = 0;
bool found = false;
+ if (recipient != UCSI_RECIPIENT_CON)
+ return false;
+
alt = uc->orig;
new_alt = uc->updated;
memset(uc->updated, 0, sizeof(uc->updated));
@@ -538,9 +542,10 @@ static void ucsi_ccg_update_set_new_cam_cmd(struct ucsi_ccg *uc,
* first and then vdo=0x3
*/
static void ucsi_ccg_nvidia_altmode(struct ucsi_ccg *uc,
- struct ucsi_altmode *alt)
+ struct ucsi_altmode *alt,
+ u64 command)
{
- switch (UCSI_ALTMODE_OFFSET(uc->last_cmd_sent)) {
+ switch (UCSI_ALTMODE_OFFSET(command)) {
case NVIDIA_FTB_DP_OFFSET:
if (alt[0].mid == USB_TYPEC_NVIDIA_VLINK_DBG_VDO)
alt[0].mid = USB_TYPEC_NVIDIA_VLINK_DP_VDO |
@@ -578,37 +583,11 @@ static int ucsi_ccg_read_cci(struct ucsi *ucsi, u32 *cci)
static int ucsi_ccg_read_message_in(struct ucsi *ucsi, void *val, size_t val_len)
{
struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi);
- struct ucsi_capability *cap;
- struct ucsi_altmode *alt;
spin_lock(&uc->op_lock);
memcpy(val, uc->op_data.message_in, val_len);
spin_unlock(&uc->op_lock);
- switch (UCSI_COMMAND(uc->last_cmd_sent)) {
- case UCSI_GET_CURRENT_CAM:
- if (uc->has_multiple_dp)
- ucsi_ccg_update_get_current_cam_cmd(uc, (u8 *)val);
- break;
- case UCSI_GET_ALTERNATE_MODES:
- if (UCSI_ALTMODE_RECIPIENT(uc->last_cmd_sent) ==
- UCSI_RECIPIENT_SOP) {
- alt = val;
- if (alt[0].svid == USB_TYPEC_NVIDIA_VLINK_SID)
- ucsi_ccg_nvidia_altmode(uc, alt);
- }
- break;
- case UCSI_GET_CAPABILITY:
- if (uc->fw_build == CCG_FW_BUILD_NVIDIA_TEGRA) {
- cap = val;
- cap->features &= ~UCSI_CAP_ALT_MODE_DETAILS;
- }
- break;
- default:
- break;
- }
- uc->last_cmd_sent = 0;
-
return 0;
}
@@ -628,7 +607,8 @@ static int ucsi_ccg_async_control(struct ucsi *ucsi, u64 command)
return ccg_write(uc, reg, (u8 *)&command, sizeof(command));
}
-static int ucsi_ccg_sync_control(struct ucsi *ucsi, u64 command)
+static int ucsi_ccg_sync_control(struct ucsi *ucsi, u64 command, u32 *cci,
+ void *data, size_t size)
{
struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi);
struct ucsi_connector *con;
@@ -638,11 +618,9 @@ static int ucsi_ccg_sync_control(struct ucsi *ucsi, u64 command)
mutex_lock(&uc->lock);
pm_runtime_get_sync(uc->dev);
- uc->last_cmd_sent = command;
-
- if (UCSI_COMMAND(uc->last_cmd_sent) == UCSI_SET_NEW_CAM &&
+ if (UCSI_COMMAND(command) == UCSI_SET_NEW_CAM &&
uc->has_multiple_dp) {
- con_index = (uc->last_cmd_sent >> 16) &
+ con_index = (command >> 16) &
UCSI_CMD_CONNECTOR_MASK;
if (con_index == 0) {
ret = -EINVAL;
@@ -652,7 +630,31 @@ static int ucsi_ccg_sync_control(struct ucsi *ucsi, u64 command)
ucsi_ccg_update_set_new_cam_cmd(uc, con, &command);
}
- ret = ucsi_sync_control_common(ucsi, command);
+ ret = ucsi_sync_control_common(ucsi, command, cci, data, size);
+
+ switch (UCSI_COMMAND(command)) {
+ case UCSI_GET_CURRENT_CAM:
+ if (uc->has_multiple_dp)
+ ucsi_ccg_update_get_current_cam_cmd(uc, (u8 *)data);
+ break;
+ case UCSI_GET_ALTERNATE_MODES:
+ if (UCSI_ALTMODE_RECIPIENT(command) == UCSI_RECIPIENT_SOP) {
+ struct ucsi_altmode *alt = data;
+
+ if (alt[0].svid == USB_TYPEC_NVIDIA_VLINK_SID)
+ ucsi_ccg_nvidia_altmode(uc, alt, command);
+ }
+ break;
+ case UCSI_GET_CAPABILITY:
+ if (uc->fw_build == CCG_FW_BUILD_NVIDIA_TEGRA) {
+ struct ucsi_capability *cap = data;
+
+ cap->features &= ~UCSI_CAP_ALT_MODE_DETAILS;
+ }
+ break;
+ default:
+ break;
+ }
err_put:
pm_runtime_put_sync(uc->dev);
@@ -1391,22 +1393,35 @@ static ssize_t do_flash_store(struct device *dev,
if (!flash)
return n;
- if (uc->fw_build == 0x0) {
- dev_err(dev, "fail to flash FW due to missing FW build info\n");
- return -EINVAL;
- }
-
schedule_work(&uc->work);
return n;
}
+static umode_t ucsi_ccg_attrs_is_visible(struct kobject *kobj, struct attribute *attr, int idx)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct ucsi_ccg *uc = i2c_get_clientdata(to_i2c_client(dev));
+
+ if (!uc->fw_build)
+ return 0;
+
+ return attr->mode;
+}
+
static DEVICE_ATTR_WO(do_flash);
static struct attribute *ucsi_ccg_attrs[] = {
&dev_attr_do_flash.attr,
NULL,
};
-ATTRIBUTE_GROUPS(ucsi_ccg);
+static struct attribute_group ucsi_ccg_attr_group = {
+ .attrs = ucsi_ccg_attrs,
+ .is_visible = ucsi_ccg_attrs_is_visible,
+};
+static const struct attribute_group *ucsi_ccg_groups[] = {
+ &ucsi_ccg_attr_group,
+ NULL,
+};
static int ucsi_ccg_probe(struct i2c_client *client)
{
@@ -1433,11 +1448,10 @@ static int ucsi_ccg_probe(struct i2c_client *client)
uc->fw_build = CCG_FW_BUILD_NVIDIA_TEGRA;
else if (!strcmp(fw_name, "nvidia,gpu"))
uc->fw_build = CCG_FW_BUILD_NVIDIA;
+ if (!uc->fw_build)
+ dev_err(uc->dev, "failed to get FW build information\n");
}
- if (!uc->fw_build)
- dev_err(uc->dev, "failed to get FW build information\n");
-
/* reset ccg device and initialize ucsi */
status = ucsi_ccg_init(uc);
if (status < 0) {
@@ -1474,6 +1488,8 @@ static int ucsi_ccg_probe(struct i2c_client *client)
i2c_set_clientdata(client, uc);
+ device_disable_async_suspend(uc->dev);
+
pm_runtime_set_active(uc->dev);
pm_runtime_enable(uc->dev);
pm_runtime_use_autosuspend(uc->dev);
diff --git a/drivers/usb/typec/ucsi/ucsi_glink.c b/drivers/usb/typec/ucsi/ucsi_glink.c
index 8af79101a2fc..12e07b9fe622 100644
--- a/drivers/usb/typec/ucsi/ucsi_glink.c
+++ b/drivers/usb/typec/ucsi/ucsi_glink.c
@@ -16,10 +16,10 @@
#define PMIC_GLINK_MAX_PORTS 3
-#define UCSI_BUF_SIZE 48
+#define UCSI_BUF_V1_SIZE (UCSI_MESSAGE_OUT + (UCSI_MESSAGE_OUT - UCSI_MESSAGE_IN))
+#define UCSI_BUF_V2_SIZE (UCSIv2_MESSAGE_OUT + (UCSIv2_MESSAGE_OUT - UCSI_MESSAGE_IN))
#define MSG_TYPE_REQ_RESP 1
-#define UCSI_BUF_SIZE 48
#define UC_NOTIFY_RECEIVER_UCSI 0x0
#define UC_UCSI_READ_BUF_REQ 0x11
@@ -30,24 +30,30 @@ struct ucsi_read_buf_req_msg {
struct pmic_glink_hdr hdr;
};
-struct ucsi_read_buf_resp_msg {
+struct __packed ucsi_read_buf_resp_msg {
struct pmic_glink_hdr hdr;
- u8 buf[UCSI_BUF_SIZE];
+ union {
+ u8 v2_buf[UCSI_BUF_V2_SIZE];
+ u8 v1_buf[UCSI_BUF_V1_SIZE];
+ } buf;
u32 ret_code;
};
-struct ucsi_write_buf_req_msg {
+struct __packed ucsi_write_buf_req_msg {
struct pmic_glink_hdr hdr;
- u8 buf[UCSI_BUF_SIZE];
+ union {
+ u8 v2_buf[UCSI_BUF_V2_SIZE];
+ u8 v1_buf[UCSI_BUF_V1_SIZE];
+ } buf;
u32 reserved;
};
-struct ucsi_write_buf_resp_msg {
+struct __packed ucsi_write_buf_resp_msg {
struct pmic_glink_hdr hdr;
u32 ret_code;
};
-struct ucsi_notify_ind_msg {
+struct __packed ucsi_notify_ind_msg {
struct pmic_glink_hdr hdr;
u32 notification;
u32 receiver;
@@ -72,7 +78,7 @@ struct pmic_glink_ucsi {
bool ucsi_registered;
bool pd_running;
- u8 read_buf[UCSI_BUF_SIZE];
+ u8 read_buf[UCSI_BUF_V2_SIZE];
};
static int pmic_glink_ucsi_read(struct ucsi *__ucsi, unsigned int offset,
@@ -132,17 +138,35 @@ static int pmic_glink_ucsi_locked_write(struct pmic_glink_ucsi *ucsi, unsigned i
const void *val, size_t val_len)
{
struct ucsi_write_buf_req_msg req = {};
+ size_t req_len, buf_len;
unsigned long left;
int ret;
+ u8 *buf;
req.hdr.owner = PMIC_GLINK_OWNER_USBC;
req.hdr.type = MSG_TYPE_REQ_RESP;
req.hdr.opcode = UC_UCSI_WRITE_BUF_REQ;
- memcpy(&req.buf[offset], val, val_len);
+
+ if (ucsi->ucsi->version >= UCSI_VERSION_2_0) {
+ buf_len = UCSI_BUF_V2_SIZE;
+ buf = req.buf.v2_buf;
+ } else if (ucsi->ucsi->version) {
+ buf_len = UCSI_BUF_V1_SIZE;
+ buf = req.buf.v1_buf;
+ } else {
+ dev_err(ucsi->dev, "UCSI version unknown\n");
+ return -EINVAL;
+ }
+ req_len = sizeof(struct pmic_glink_hdr) + buf_len + sizeof(u32);
+
+ if (offset + val_len > buf_len)
+ return -EINVAL;
+
+ memcpy(&buf[offset], val, val_len);
reinit_completion(&ucsi->write_ack);
- ret = pmic_glink_send(ucsi->client, &req, sizeof(req));
+ ret = pmic_glink_send(ucsi->client, &req, req_len);
if (ret < 0) {
dev_err(ucsi->dev, "failed to send UCSI write request: %d\n", ret);
return ret;
@@ -216,12 +240,48 @@ static const struct ucsi_operations pmic_glink_ucsi_ops = {
static void pmic_glink_ucsi_read_ack(struct pmic_glink_ucsi *ucsi, const void *data, int len)
{
- const struct ucsi_read_buf_resp_msg *resp = data;
+ u32 ret_code, resp_len, buf_len = 0;
+ u8 *buf;
+
+ if (ucsi->ucsi->version) {
+ if (ucsi->ucsi->version >= UCSI_VERSION_2_0) {
+ buf = ((struct ucsi_read_buf_resp_msg *)data)->buf.v2_buf;
+ buf_len = UCSI_BUF_V2_SIZE;
+ } else {
+ buf = ((struct ucsi_read_buf_resp_msg *)data)->buf.v1_buf;
+ buf_len = UCSI_BUF_V1_SIZE;
+ }
+ } else if (!ucsi->ucsi_registered) {
+ /*
+ * If UCSI version is not known yet because device is not registered, choose buffer
+ * size which best fits incoming data
+ */
+ if (len > sizeof(struct pmic_glink_hdr) + UCSI_BUF_V2_SIZE) {
+ buf = ((struct ucsi_read_buf_resp_msg *)data)->buf.v2_buf;
+ buf_len = UCSI_BUF_V2_SIZE;
+ } else {
+ buf = ((struct ucsi_read_buf_resp_msg *)data)->buf.v1_buf;
+ buf_len = UCSI_BUF_V1_SIZE;
+ }
+ } else {
+ dev_err(ucsi->dev, "Device has been registered but UCSI version is still unknown\n");
+ return;
+ }
- if (resp->ret_code)
+ resp_len = sizeof(struct pmic_glink_hdr) + buf_len + sizeof(u32);
+
+ if (len > resp_len)
+ return;
+
+ /* Ensure that buffer_len leaves space for ret_code to be read back from memory */
+ if (buf_len > len - sizeof(struct pmic_glink_hdr) - sizeof(u32))
+ buf_len = len - sizeof(struct pmic_glink_hdr) - sizeof(u32);
+
+ memcpy(&ret_code, buf + buf_len, sizeof(u32));
+ if (ret_code)
return;
- memcpy(ucsi->read_buf, resp->buf, UCSI_BUF_SIZE);
+ memcpy(ucsi->read_buf, buf, buf_len);
complete(&ucsi->read_ack);
}
@@ -311,14 +371,18 @@ static void pmic_glink_ucsi_destroy(void *data)
static unsigned long quirk_sc8180x = UCSI_NO_PARTNER_PDOS;
static unsigned long quirk_sc8280xp = UCSI_NO_PARTNER_PDOS | UCSI_DELAY_DEVICE_PDOS;
static unsigned long quirk_sm8450 = UCSI_DELAY_DEVICE_PDOS;
+static unsigned long quirk_x1e80100 = UCSI_DELAY_DEVICE_PDOS | UCSI_USB4_IMPLIES_USB;
static const struct of_device_id pmic_glink_ucsi_of_quirks[] = {
+ { .compatible = "qcom,glymur-pmic-glink", .data = &quirk_sm8450, },
+ { .compatible = "qcom,kaanapali-pmic-glink", .data = &quirk_sm8450, },
{ .compatible = "qcom,qcm6490-pmic-glink", .data = &quirk_sc8280xp, },
{ .compatible = "qcom,sc8180x-pmic-glink", .data = &quirk_sc8180x, },
{ .compatible = "qcom,sc8280xp-pmic-glink", .data = &quirk_sc8280xp, },
{ .compatible = "qcom,sm8350-pmic-glink", .data = &quirk_sc8180x, },
{ .compatible = "qcom,sm8450-pmic-glink", .data = &quirk_sm8450, },
{ .compatible = "qcom,sm8550-pmic-glink", .data = &quirk_sm8450, },
+ { .compatible = "qcom,x1e80100-pmic-glink", .data = &quirk_x1e80100, },
{}
};
diff --git a/drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c b/drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c
new file mode 100644
index 000000000000..ca749fde49bd
--- /dev/null
+++ b/drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c
@@ -0,0 +1,528 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ucsi-huawei-gaokun - A UCSI driver for HUAWEI Matebook E Go
+ *
+ * Copyright (C) 2024-2025 Pengyu Luo <mitltlatltl@gmail.com>
+ */
+
+#include <drm/bridge/aux-bridge.h>
+#include <linux/auxiliary_bus.h>
+#include <linux/bitops.h>
+#include <linux/completion.h>
+#include <linux/container_of.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/of.h>
+#include <linux/platform_data/huawei-gaokun-ec.h>
+#include <linux/string.h>
+#include <linux/usb/pd_vdo.h>
+#include <linux/usb/typec_altmode.h>
+#include <linux/usb/typec_dp.h>
+#include <linux/workqueue_types.h>
+
+#include "ucsi.h"
+
+#define EC_EVENT_UCSI 0x21
+#define EC_EVENT_USB 0x22
+
+#define GAOKUN_CCX_MASK GENMASK(1, 0)
+#define GAOKUN_MUX_MASK GENMASK(3, 2)
+
+#define GAOKUN_DPAM_MASK GENMASK(3, 0)
+#define GAOKUN_HPD_STATE_MASK BIT(4)
+#define GAOKUN_HPD_IRQ_MASK BIT(5)
+
+#define GET_IDX(updt) (ffs(updt) - 1)
+
+#define CCX_TO_ORI(ccx) (++(ccx) % 3) /* convert ccx to enum typec_orientation */
+
+/* Configuration Channel Extension */
+enum gaokun_ucsi_ccx {
+ USBC_CCX_NORMAL,
+ USBC_CCX_REVERSE,
+ USBC_CCX_NONE,
+};
+
+enum gaokun_ucsi_mux {
+ USBC_MUX_NONE,
+ USBC_MUX_USB_2L,
+ USBC_MUX_DP_4L,
+ USBC_MUX_USB_DP,
+};
+
+/* based on pmic_glink_altmode_pin_assignment */
+enum gaokun_ucsi_dpam_pan { /* DP Alt Mode Pin Assignments */
+ USBC_DPAM_PAN_NONE,
+ USBC_DPAM_PAN_A, /* Not supported after USB Type-C Standard v1.0b */
+ USBC_DPAM_PAN_B, /* Not supported after USB Type-C Standard v1.0b */
+ USBC_DPAM_PAN_C, /* USBC_DPAM_PAN_C_REVERSE - 6 */
+ USBC_DPAM_PAN_D,
+ USBC_DPAM_PAN_E,
+ USBC_DPAM_PAN_F, /* Not supported after USB Type-C Standard v1.0b */
+ USBC_DPAM_PAN_A_REVERSE,/* Not supported after USB Type-C Standard v1.0b */
+ USBC_DPAM_PAN_B_REVERSE,/* Not supported after USB Type-C Standard v1.0b */
+ USBC_DPAM_PAN_C_REVERSE,
+ USBC_DPAM_PAN_D_REVERSE,
+ USBC_DPAM_PAN_E_REVERSE,
+ USBC_DPAM_PAN_F_REVERSE,/* Not supported after USB Type-C Standard v1.0b */
+};
+
+struct gaokun_ucsi_reg {
+ u8 num_ports;
+ u8 port_updt;
+ u8 port_data[4];
+ u8 checksum;
+ u8 reserved;
+} __packed;
+
+struct gaokun_ucsi_port {
+ struct completion usb_ack;
+ spinlock_t lock; /* serializing port resource access */
+
+ struct gaokun_ucsi *ucsi;
+ struct auxiliary_device *bridge;
+
+ int idx;
+ enum gaokun_ucsi_ccx ccx;
+ enum gaokun_ucsi_mux mux;
+ u8 mode;
+ u16 svid;
+ u8 hpd_state;
+ u8 hpd_irq;
+};
+
+struct gaokun_ucsi {
+ struct gaokun_ec *ec;
+ struct ucsi *ucsi;
+ struct gaokun_ucsi_port *ports;
+ struct device *dev;
+ struct delayed_work work;
+ struct notifier_block nb;
+ u16 version;
+ u8 num_ports;
+};
+
+/* -------------------------------------------------------------------------- */
+/* For UCSI */
+
+static int gaokun_ucsi_read_version(struct ucsi *ucsi, u16 *version)
+{
+ struct gaokun_ucsi *uec = ucsi_get_drvdata(ucsi);
+
+ *version = uec->version;
+
+ return 0;
+}
+
+static int gaokun_ucsi_read_cci(struct ucsi *ucsi, u32 *cci)
+{
+ struct gaokun_ucsi *uec = ucsi_get_drvdata(ucsi);
+ u8 buf[GAOKUN_UCSI_READ_SIZE];
+ int ret;
+
+ ret = gaokun_ec_ucsi_read(uec->ec, buf);
+ if (ret)
+ return ret;
+
+ memcpy(cci, buf, sizeof(*cci));
+
+ return 0;
+}
+
+static int gaokun_ucsi_read_message_in(struct ucsi *ucsi,
+ void *val, size_t val_len)
+{
+ struct gaokun_ucsi *uec = ucsi_get_drvdata(ucsi);
+ u8 buf[GAOKUN_UCSI_READ_SIZE];
+ int ret;
+
+ ret = gaokun_ec_ucsi_read(uec->ec, buf);
+ if (ret)
+ return ret;
+
+ memcpy(val, buf + GAOKUN_UCSI_CCI_SIZE,
+ min(val_len, GAOKUN_UCSI_MSGI_SIZE));
+
+ return 0;
+}
+
+static int gaokun_ucsi_async_control(struct ucsi *ucsi, u64 command)
+{
+ struct gaokun_ucsi *uec = ucsi_get_drvdata(ucsi);
+ u8 buf[GAOKUN_UCSI_WRITE_SIZE] = {};
+
+ memcpy(buf, &command, sizeof(command));
+
+ return gaokun_ec_ucsi_write(uec->ec, buf);
+}
+
+static void gaokun_ucsi_update_connector(struct ucsi_connector *con)
+{
+ struct gaokun_ucsi *uec = ucsi_get_drvdata(con->ucsi);
+
+ if (con->num > uec->num_ports)
+ return;
+
+ con->typec_cap.orientation_aware = true;
+}
+
+static void gaokun_set_orientation(struct ucsi_connector *con,
+ struct gaokun_ucsi_port *port)
+{
+ enum gaokun_ucsi_ccx ccx;
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->lock, flags);
+ ccx = port->ccx;
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ typec_set_orientation(con->port, CCX_TO_ORI(ccx));
+}
+
+static void gaokun_ucsi_connector_status(struct ucsi_connector *con)
+{
+ struct gaokun_ucsi *uec = ucsi_get_drvdata(con->ucsi);
+ int idx;
+
+ idx = con->num - 1;
+ if (con->num > uec->num_ports) {
+ dev_warn(uec->dev, "set orientation out of range: con%d\n", idx);
+ return;
+ }
+
+ gaokun_set_orientation(con, &uec->ports[idx]);
+}
+
+static const struct ucsi_operations gaokun_ucsi_ops = {
+ .read_version = gaokun_ucsi_read_version,
+ .read_cci = gaokun_ucsi_read_cci,
+ .poll_cci = gaokun_ucsi_read_cci,
+ .read_message_in = gaokun_ucsi_read_message_in,
+ .sync_control = ucsi_sync_control_common,
+ .async_control = gaokun_ucsi_async_control,
+ .update_connector = gaokun_ucsi_update_connector,
+ .connector_status = gaokun_ucsi_connector_status,
+};
+
+/* -------------------------------------------------------------------------- */
+/* For Altmode */
+
+static void gaokun_ucsi_port_update(struct gaokun_ucsi_port *port,
+ const u8 *port_data)
+{
+ struct gaokun_ucsi *uec = port->ucsi;
+ int offset = port->idx * 2; /* every port has 2 Bytes data */
+ unsigned long flags;
+ u8 dcc, ddi;
+
+ dcc = port_data[offset];
+ ddi = port_data[offset + 1];
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ port->ccx = FIELD_GET(GAOKUN_CCX_MASK, dcc);
+ port->mux = FIELD_GET(GAOKUN_MUX_MASK, dcc);
+ port->mode = FIELD_GET(GAOKUN_DPAM_MASK, ddi);
+ port->hpd_state = FIELD_GET(GAOKUN_HPD_STATE_MASK, ddi);
+ port->hpd_irq = FIELD_GET(GAOKUN_HPD_IRQ_MASK, ddi);
+
+ /* Mode and SVID are unused; keeping them to make things clearer */
+ switch (port->mode) {
+ case USBC_DPAM_PAN_C:
+ case USBC_DPAM_PAN_C_REVERSE:
+ port->mode = DP_PIN_ASSIGN_C; /* correct it for usb later */
+ break;
+ case USBC_DPAM_PAN_D:
+ case USBC_DPAM_PAN_D_REVERSE:
+ port->mode = DP_PIN_ASSIGN_D;
+ break;
+ case USBC_DPAM_PAN_E:
+ case USBC_DPAM_PAN_E_REVERSE:
+ port->mode = DP_PIN_ASSIGN_E;
+ break;
+ case USBC_DPAM_PAN_NONE:
+ port->mode = TYPEC_STATE_SAFE;
+ break;
+ default:
+ dev_warn(uec->dev, "unknown mode %d\n", port->mode);
+ break;
+ }
+
+ switch (port->mux) {
+ case USBC_MUX_NONE:
+ port->svid = 0;
+ break;
+ case USBC_MUX_USB_2L:
+ port->svid = USB_SID_PD;
+ port->mode = TYPEC_STATE_USB; /* same as PAN_C, correct it */
+ break;
+ case USBC_MUX_DP_4L:
+ case USBC_MUX_USB_DP:
+ port->svid = USB_SID_DISPLAYPORT;
+ break;
+ default:
+ dev_warn(uec->dev, "unknown mux state %d\n", port->mux);
+ break;
+ }
+
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static int gaokun_ucsi_refresh(struct gaokun_ucsi *uec)
+{
+ struct gaokun_ucsi_reg ureg;
+ int ret, idx;
+
+ ret = gaokun_ec_ucsi_get_reg(uec->ec, &ureg);
+ if (ret)
+ return GAOKUN_UCSI_NO_PORT_UPDATE;
+
+ uec->num_ports = ureg.num_ports;
+ idx = GET_IDX(ureg.port_updt);
+
+ if (idx < 0 || idx >= ureg.num_ports)
+ return GAOKUN_UCSI_NO_PORT_UPDATE;
+
+ gaokun_ucsi_port_update(&uec->ports[idx], ureg.port_data);
+ return idx;
+}
+
+static void gaokun_ucsi_handle_altmode(struct gaokun_ucsi_port *port)
+{
+ struct gaokun_ucsi *uec = port->ucsi;
+ int idx = port->idx;
+
+ if (idx >= uec->ucsi->cap.num_connectors) {
+ dev_warn(uec->dev, "altmode port out of range: %d\n", idx);
+ return;
+ }
+
+ /* UCSI callback .connector_status() have set orientation */
+ if (port->bridge)
+ drm_aux_hpd_bridge_notify(&port->bridge->dev,
+ port->hpd_state ?
+ connector_status_connected :
+ connector_status_disconnected);
+
+ gaokun_ec_ucsi_pan_ack(uec->ec, port->idx);
+}
+
+static void gaokun_ucsi_altmode_notify_ind(struct gaokun_ucsi *uec)
+{
+ int idx;
+
+ if (!uec->ucsi->connector) { /* slow to register */
+ dev_err_ratelimited(uec->dev, "ucsi connector is not initialized yet\n");
+ return;
+ }
+
+ idx = gaokun_ucsi_refresh(uec);
+ if (idx == GAOKUN_UCSI_NO_PORT_UPDATE)
+ gaokun_ec_ucsi_pan_ack(uec->ec, idx); /* ack directly if no update */
+ else
+ gaokun_ucsi_handle_altmode(&uec->ports[idx]);
+}
+
+/*
+ * When inserting, 2 UCSI events(connector change) are followed by USB events.
+ * If we received one USB event, that means USB events are not blocked, so we
+ * can complelte for all ports, and we should signal all events.
+ */
+static void gaokun_ucsi_complete_usb_ack(struct gaokun_ucsi *uec)
+{
+ struct gaokun_ucsi_port *port;
+ int idx = 0;
+
+ while (idx < uec->num_ports) {
+ port = &uec->ports[idx++];
+ if (!completion_done(&port->usb_ack))
+ complete_all(&port->usb_ack);
+ }
+}
+
+/*
+ * USB event is necessary for enabling altmode, the event should follow
+ * UCSI event, if not after timeout(this notify may be disabled somehow),
+ * then force to enable altmode.
+ */
+static void gaokun_ucsi_handle_no_usb_event(struct gaokun_ucsi *uec, int idx)
+{
+ struct gaokun_ucsi_port *port;
+
+ port = &uec->ports[idx];
+ if (!wait_for_completion_timeout(&port->usb_ack, 2 * HZ)) {
+ dev_warn(uec->dev, "No USB EVENT, triggered by UCSI EVENT");
+ gaokun_ucsi_altmode_notify_ind(uec);
+ }
+}
+
+static int gaokun_ucsi_notify(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ u32 cci;
+ int ret;
+ struct gaokun_ucsi *uec = container_of(nb, struct gaokun_ucsi, nb);
+
+ switch (action) {
+ case EC_EVENT_USB:
+ gaokun_ucsi_complete_usb_ack(uec);
+ gaokun_ucsi_altmode_notify_ind(uec);
+ return NOTIFY_OK;
+
+ case EC_EVENT_UCSI:
+ ret = gaokun_ucsi_read_cci(uec->ucsi, &cci);
+ if (ret)
+ return NOTIFY_DONE;
+
+ ucsi_notify_common(uec->ucsi, cci);
+ if (UCSI_CCI_CONNECTOR(cci))
+ gaokun_ucsi_handle_no_usb_event(uec, UCSI_CCI_CONNECTOR(cci) - 1);
+
+ return NOTIFY_OK;
+
+ default:
+ return NOTIFY_DONE;
+ }
+}
+
+static int gaokun_ucsi_ports_init(struct gaokun_ucsi *uec)
+{
+ struct gaokun_ucsi_port *ucsi_port;
+ struct gaokun_ucsi_reg ureg = {};
+ struct device *dev = uec->dev;
+ struct fwnode_handle *fwnode;
+ int i, ret, num_ports;
+ u32 port;
+
+ gaokun_ec_ucsi_get_reg(uec->ec, &ureg);
+ num_ports = ureg.num_ports;
+ uec->ports = devm_kcalloc(dev, num_ports, sizeof(*uec->ports),
+ GFP_KERNEL);
+ if (!uec->ports)
+ return -ENOMEM;
+
+ for (i = 0; i < num_ports; ++i) {
+ ucsi_port = &uec->ports[i];
+ ucsi_port->ccx = USBC_CCX_NONE;
+ ucsi_port->idx = i;
+ ucsi_port->ucsi = uec;
+ init_completion(&ucsi_port->usb_ack);
+ spin_lock_init(&ucsi_port->lock);
+ }
+
+ device_for_each_child_node(dev, fwnode) {
+ ret = fwnode_property_read_u32(fwnode, "reg", &port);
+ if (ret < 0) {
+ dev_err(dev, "missing reg property of %pOFn\n", fwnode);
+ fwnode_handle_put(fwnode);
+ return ret;
+ }
+
+ if (port >= num_ports) {
+ dev_warn(dev, "invalid connector number %d, ignoring\n", port);
+ continue;
+ }
+
+ ucsi_port = &uec->ports[port];
+ ucsi_port->bridge = devm_drm_dp_hpd_bridge_alloc(dev, to_of_node(fwnode));
+ if (IS_ERR(ucsi_port->bridge)) {
+ fwnode_handle_put(fwnode);
+ return PTR_ERR(ucsi_port->bridge);
+ }
+ }
+
+ for (i = 0; i < num_ports; i++) {
+ if (!uec->ports[i].bridge)
+ continue;
+
+ ret = devm_drm_dp_hpd_bridge_add(dev, uec->ports[i].bridge);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void gaokun_ucsi_register_worker(struct work_struct *work)
+{
+ struct gaokun_ucsi *uec;
+ struct ucsi *ucsi;
+ int ret;
+
+ uec = container_of(work, struct gaokun_ucsi, work.work);
+ ucsi = uec->ucsi;
+
+ ret = gaokun_ec_register_notify(uec->ec, &uec->nb);
+ if (ret) {
+ dev_err_probe(ucsi->dev, ret, "notifier register failed\n");
+ return;
+ }
+
+ ret = ucsi_register(ucsi);
+ if (ret)
+ dev_err_probe(ucsi->dev, ret, "ucsi register failed\n");
+}
+
+static int gaokun_ucsi_probe(struct auxiliary_device *adev,
+ const struct auxiliary_device_id *id)
+{
+ struct gaokun_ec *ec = adev->dev.platform_data;
+ struct device *dev = &adev->dev;
+ struct gaokun_ucsi *uec;
+ int ret;
+
+ uec = devm_kzalloc(dev, sizeof(*uec), GFP_KERNEL);
+ if (!uec)
+ return -ENOMEM;
+
+ uec->ec = ec;
+ uec->dev = dev;
+ uec->version = UCSI_VERSION_1_0;
+ uec->nb.notifier_call = gaokun_ucsi_notify;
+
+ INIT_DELAYED_WORK(&uec->work, gaokun_ucsi_register_worker);
+
+ ret = gaokun_ucsi_ports_init(uec);
+ if (ret)
+ return ret;
+
+ uec->ucsi = ucsi_create(dev, &gaokun_ucsi_ops);
+ if (IS_ERR(uec->ucsi))
+ return PTR_ERR(uec->ucsi);
+
+ ucsi_set_drvdata(uec->ucsi, uec);
+ auxiliary_set_drvdata(adev, uec);
+
+ /* EC can't handle UCSI properly in the early stage */
+ schedule_delayed_work(&uec->work, 3 * HZ);
+
+ return 0;
+}
+
+static void gaokun_ucsi_remove(struct auxiliary_device *adev)
+{
+ struct gaokun_ucsi *uec = auxiliary_get_drvdata(adev);
+
+ disable_delayed_work_sync(&uec->work);
+ gaokun_ec_unregister_notify(uec->ec, &uec->nb);
+ ucsi_unregister(uec->ucsi);
+ ucsi_destroy(uec->ucsi);
+}
+
+static const struct auxiliary_device_id gaokun_ucsi_id_table[] = {
+ { .name = GAOKUN_MOD_NAME "." GAOKUN_DEV_UCSI, },
+ {}
+};
+MODULE_DEVICE_TABLE(auxiliary, gaokun_ucsi_id_table);
+
+static struct auxiliary_driver gaokun_ucsi_driver = {
+ .name = GAOKUN_DEV_UCSI,
+ .id_table = gaokun_ucsi_id_table,
+ .probe = gaokun_ucsi_probe,
+ .remove = gaokun_ucsi_remove,
+};
+
+module_auxiliary_driver(gaokun_ucsi_driver);
+
+MODULE_DESCRIPTION("HUAWEI Matebook E Go UCSI driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/typec/ucsi/ucsi_stm32g0.c b/drivers/usb/typec/ucsi/ucsi_stm32g0.c
index 57ef7d83a412..838ac0185082 100644
--- a/drivers/usb/typec/ucsi/ucsi_stm32g0.c
+++ b/drivers/usb/typec/ucsi/ucsi_stm32g0.c
@@ -10,6 +10,7 @@
#include <linux/firmware.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
+#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/unaligned.h>
@@ -523,11 +524,7 @@ static void ucsi_stm32g0_fw_cb(const struct firmware *fw, void *context)
data = fw->data;
end = fw->data + fw->size;
while (data < end) {
- if ((end - data) < STM32G0_I2C_BL_SZ)
- size = end - data;
- else
- size = STM32G0_I2C_BL_SZ;
-
+ size = min(end - data, STM32G0_I2C_BL_SZ);
ret = ucsi_stm32g0_bl_write(g0->ucsi, addr, data, size);
if (ret) {
dev_err(g0->dev, "Write failed %d\n", ret);
diff --git a/drivers/usb/typec/ucsi/ucsi_yoga_c630.c b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
index d33e3f2dd1d8..0187c1c4b21a 100644
--- a/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
+++ b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
@@ -7,18 +7,33 @@
*/
#include <linux/auxiliary_bus.h>
#include <linux/bitops.h>
+#include <linux/bitfield.h>
#include <linux/completion.h>
#include <linux/container_of.h>
#include <linux/module.h>
#include <linux/notifier.h>
+#include <linux/of.h>
+#include <linux/property.h>
#include <linux/string.h>
#include <linux/platform_data/lenovo-yoga-c630.h>
+#include <linux/usb/typec_dp.h>
+
+#include <drm/bridge/aux-bridge.h>
#include "ucsi.h"
+#define LENOVO_EC_USB_MUX 0x08
+
+#define USB_MUX_MUXC GENMASK(1, 0)
+#define USB_MUX_CCST GENMASK(3, 2)
+#define USB_MUX_DPPN GENMASK(7, 4)
+#define USB_MUX_HPDS BIT(8)
+#define USB_MUX_HSFL GENMASK(11, 9)
+
struct yoga_c630_ucsi {
struct yoga_c630_ec *ec;
struct ucsi *ucsi;
+ struct auxiliary_device *bridge;
struct notifier_block nb;
u16 version;
};
@@ -71,15 +86,127 @@ static int yoga_c630_ucsi_async_control(struct ucsi *ucsi, u64 command)
return yoga_c630_ec_ucsi_write(uec->ec, (u8*)&command);
}
+static int yoga_c630_ucsi_sync_control(struct ucsi *ucsi,
+ u64 command,
+ u32 *cci,
+ void *data, size_t size)
+{
+ int ret;
+
+ /*
+ * EC doesn't return connector's DP mode even though it is supported.
+ * Fake it.
+ */
+ if (UCSI_COMMAND(command) == UCSI_GET_ALTERNATE_MODES &&
+ UCSI_GET_ALTMODE_GET_CONNECTOR_NUMBER(command) == 1 &&
+ UCSI_ALTMODE_RECIPIENT(command) == UCSI_RECIPIENT_CON &&
+ UCSI_ALTMODE_OFFSET(command) == 0) {
+ static const struct ucsi_altmode alt = {
+ .svid = USB_TYPEC_DP_SID,
+ .mid = USB_TYPEC_DP_MODE,
+ };
+
+ dev_dbg(ucsi->dev, "faking DP altmode for con1\n");
+ memset(data, 0, size);
+ memcpy(data, &alt, min(sizeof(alt), size));
+ *cci = UCSI_CCI_COMMAND_COMPLETE | UCSI_SET_CCI_LENGTH(sizeof(alt));
+ return 0;
+ }
+
+ /*
+ * EC can return AltModes present on CON1 (port0, right) for CON2
+ * (port1, left) too. Ignore all requests going to CON2 (it doesn't
+ * support DP anyway).
+ */
+ if (UCSI_COMMAND(command) == UCSI_GET_ALTERNATE_MODES &&
+ UCSI_GET_ALTMODE_GET_CONNECTOR_NUMBER(command) == 2) {
+ dev_dbg(ucsi->dev, "ignoring altmodes for con2\n");
+ memset(data, 0, size);
+ *cci = UCSI_CCI_COMMAND_COMPLETE;
+ return 0;
+ }
+
+ ret = ucsi_sync_control_common(ucsi, command, cci, data, size);
+ if (ret < 0)
+ return ret;
+
+ /* UCSI_GET_CURRENT_CAM is off-by-one on all ports */
+ if (UCSI_COMMAND(command) == UCSI_GET_CURRENT_CAM && data)
+ ((u8 *)data)[0]--;
+
+ return ret;
+}
+
+static bool yoga_c630_ucsi_update_altmodes(struct ucsi *ucsi,
+ u8 recipient,
+ struct ucsi_altmode *orig,
+ struct ucsi_altmode *updated)
+{
+ int i;
+
+ if (orig[0].svid == 0 || recipient != UCSI_RECIPIENT_SOP)
+ return false;
+
+ /* EC is nice and repeats altmodes again and again. Ignore copies. */
+ for (i = 1; i < UCSI_MAX_ALTMODES; i++) {
+ if (orig[i].svid == orig[0].svid) {
+ dev_dbg(ucsi->dev, "Found duplicate altmodes, starting from %d\n", i);
+ memset(&orig[i], 0, (UCSI_MAX_ALTMODES - i) * sizeof(*orig));
+ break;
+ }
+ }
+
+ return false;
+}
+
+static void yoga_c630_ucsi_update_connector(struct ucsi_connector *con)
+{
+ if (con->num == 1)
+ con->typec_cap.orientation_aware = true;
+}
+
static const struct ucsi_operations yoga_c630_ucsi_ops = {
.read_version = yoga_c630_ucsi_read_version,
.read_cci = yoga_c630_ucsi_read_cci,
.poll_cci = yoga_c630_ucsi_read_cci,
.read_message_in = yoga_c630_ucsi_read_message_in,
- .sync_control = ucsi_sync_control_common,
+ .sync_control = yoga_c630_ucsi_sync_control,
.async_control = yoga_c630_ucsi_async_control,
+ .update_altmodes = yoga_c630_ucsi_update_altmodes,
+ .update_connector = yoga_c630_ucsi_update_connector,
};
+static void yoga_c630_ucsi_read_port0_status(struct yoga_c630_ucsi *uec)
+{
+ int val;
+ unsigned int muxc, ccst, dppn, hpds, hsfl;
+
+ val = yoga_c630_ec_read16(uec->ec, LENOVO_EC_USB_MUX);
+
+ muxc = FIELD_GET(USB_MUX_MUXC, val);
+ ccst = FIELD_GET(USB_MUX_CCST, val);
+ dppn = FIELD_GET(USB_MUX_DPPN, val);
+ hpds = FIELD_GET(USB_MUX_HPDS, val);
+ hsfl = FIELD_GET(USB_MUX_HSFL, val);
+
+ dev_dbg(uec->ucsi->dev, " mux %04x (muxc %d ccst %d dppn %d hpds %d hsfl %d)\n",
+ val,
+ muxc, ccst, dppn, hpds, hsfl);
+
+ if (uec->ucsi->connector && uec->ucsi->connector[0].port)
+ typec_set_orientation(uec->ucsi->connector[0].port,
+ ccst == 1 ?
+ TYPEC_ORIENTATION_REVERSE :
+ TYPEC_ORIENTATION_NORMAL);
+
+ if (uec->bridge)
+ drm_aux_hpd_bridge_notify(&uec->bridge->dev,
+ dppn != 0 ?
+ connector_status_connected :
+ connector_status_disconnected);
+
+}
+
static int yoga_c630_ucsi_notify(struct notifier_block *nb,
unsigned long action, void *data)
{
@@ -90,6 +217,7 @@ static int yoga_c630_ucsi_notify(struct notifier_block *nb,
switch (action) {
case LENOVO_EC_EVENT_USB:
case LENOVO_EC_EVENT_HPD:
+ yoga_c630_ucsi_read_port0_status(uec);
ucsi_connector_change(uec->ucsi, 1);
return NOTIFY_OK;
@@ -121,6 +249,24 @@ static int yoga_c630_ucsi_probe(struct auxiliary_device *adev,
uec->ec = ec;
uec->nb.notifier_call = yoga_c630_ucsi_notify;
+ device_for_each_child_node_scoped(&adev->dev, fwnode) {
+ u32 port;
+
+ ret = fwnode_property_read_u32(fwnode, "reg", &port);
+ if (ret < 0) {
+ dev_err(&adev->dev, "missing reg property of %pfwP\n", fwnode);
+ return ret;
+ }
+
+ /* DP is only on port0 */
+ if (port != 0)
+ continue;
+
+ uec->bridge = devm_drm_dp_hpd_bridge_alloc(&adev->dev, to_of_node(fwnode));
+ if (IS_ERR(uec->bridge))
+ return PTR_ERR(uec->bridge);
+ }
+
uec->ucsi = ucsi_create(&adev->dev, &yoga_c630_ucsi_ops);
if (IS_ERR(uec->ucsi))
return PTR_ERR(uec->ucsi);
@@ -133,17 +279,39 @@ static int yoga_c630_ucsi_probe(struct auxiliary_device *adev,
ret = yoga_c630_ec_register_notify(ec, &uec->nb);
if (ret)
- return ret;
+ goto err_destroy;
+
+ ret = ucsi_register(uec->ucsi);
+ if (ret)
+ goto err_unregister;
+
+ if (uec->bridge) {
+ ret = devm_drm_dp_hpd_bridge_add(&adev->dev, uec->bridge);
+ if (ret)
+ goto err_ucsi_unregister;
+ }
- return ucsi_register(uec->ucsi);
+ return 0;
+
+err_ucsi_unregister:
+ ucsi_unregister(uec->ucsi);
+
+err_unregister:
+ yoga_c630_ec_unregister_notify(uec->ec, &uec->nb);
+
+err_destroy:
+ ucsi_destroy(uec->ucsi);
+
+ return ret;
}
static void yoga_c630_ucsi_remove(struct auxiliary_device *adev)
{
struct yoga_c630_ucsi *uec = auxiliary_get_drvdata(adev);
- yoga_c630_ec_unregister_notify(uec->ec, &uec->nb);
ucsi_unregister(uec->ucsi);
+ yoga_c630_ec_unregister_notify(uec->ec, &uec->nb);
+ ucsi_destroy(uec->ucsi);
}
static const struct auxiliary_device_id yoga_c630_ucsi_id_table[] = {
diff --git a/drivers/usb/usb-skeleton.c b/drivers/usb/usb-skeleton.c
index 900a64ad25e4..709a24092104 100644
--- a/drivers/usb/usb-skeleton.c
+++ b/drivers/usb/usb-skeleton.c
@@ -493,7 +493,7 @@ static int skel_probe(struct usb_interface *interface,
int retval;
/* allocate memory for our device state and initialize it */
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ dev = kzalloc_obj(*dev);
if (!dev)
return -ENOMEM;
diff --git a/drivers/usb/usbip/stub_dev.c b/drivers/usb/usbip/stub_dev.c
index ce625b1ce9a5..abfa11d6bde7 100644
--- a/drivers/usb/usbip/stub_dev.c
+++ b/drivers/usb/usbip/stub_dev.c
@@ -263,11 +263,11 @@ static struct stub_device *stub_device_alloc(struct usb_device *udev)
dev_dbg(&udev->dev, "allocating stub device");
/* yes, it's a new device */
- sdev = kzalloc(sizeof(struct stub_device), GFP_KERNEL);
+ sdev = kzalloc_obj(struct stub_device);
if (!sdev)
return NULL;
- sdev->udev = usb_get_dev(udev);
+ sdev->udev = udev;
/*
* devid is defined with devnum when this driver is first allocated.
@@ -409,7 +409,6 @@ call_put_busid_priv:
put_busid_priv(busid_priv);
sdev_free:
- usb_put_dev(udev);
stub_device_free(sdev);
return rc;
@@ -488,8 +487,6 @@ static void stub_disconnect(struct usb_device *udev)
/* shutdown the current connection */
shutdown_busid(busid_priv);
- usb_put_dev(sdev->udev);
-
/* we already have busid_priv, just lock busid_lock */
spin_lock(&busid_priv->busid_lock);
/* free sdev */
diff --git a/drivers/usb/usbip/stub_rx.c b/drivers/usb/usbip/stub_rx.c
index 9aa30ef76f3b..1e9ae578810d 100644
--- a/drivers/usb/usbip/stub_rx.c
+++ b/drivers/usb/usbip/stub_rx.c
@@ -535,7 +535,7 @@ static void stub_recv_cmd_submit(struct stub_device *sdev,
/* allocate urb array */
priv->num_urbs = num_urbs;
- priv->urbs = kmalloc_array(num_urbs, sizeof(*priv->urbs), GFP_KERNEL);
+ priv->urbs = kmalloc_objs(*priv->urbs, num_urbs);
if (!priv->urbs)
goto err_urbs;
diff --git a/drivers/usb/usbip/stub_tx.c b/drivers/usb/usbip/stub_tx.c
index 7eb2e074012a..4ffd342250b5 100644
--- a/drivers/usb/usbip/stub_tx.c
+++ b/drivers/usb/usbip/stub_tx.c
@@ -4,6 +4,7 @@
*/
#include <linux/kthread.h>
+#include <linux/minmax.h>
#include <linux/socket.h>
#include <linux/scatterlist.h>
@@ -16,7 +17,7 @@ void stub_enqueue_ret_unlink(struct stub_device *sdev, __u32 seqnum,
{
struct stub_unlink *unlink;
- unlink = kzalloc(sizeof(struct stub_unlink), GFP_ATOMIC);
+ unlink = kzalloc_obj(struct stub_unlink, GFP_ATOMIC);
if (!unlink) {
usbip_event_add(&sdev->ud, VDEV_EVENT_ERROR_MALLOC);
return;
@@ -54,8 +55,8 @@ void stub_complete(struct urb *urb)
"stopped by a call to usb_kill_urb() because of cleaning up a virtual connection\n");
return;
case -ECONNRESET:
- dev_info(&urb->dev->dev,
- "unlinked by a call to usb_unlink_urb()\n");
+ dev_dbg(&urb->dev->dev,
+ "unlinked by a call to usb_unlink_urb()\n");
break;
case -EPIPE:
dev_info(&urb->dev->dev, "endpoint %d is stalled\n",
@@ -190,7 +191,7 @@ static int stub_send_ret_submit(struct stub_device *sdev)
else
iovnum = 2;
- iov = kcalloc(iovnum, sizeof(struct kvec), GFP_KERNEL);
+ iov = kzalloc_objs(struct kvec, iovnum);
if (!iov) {
usbip_event_add(&sdev->ud, SDEV_EVENT_ERROR_MALLOC);
@@ -239,17 +240,13 @@ static int stub_send_ret_submit(struct stub_device *sdev)
urb->actual_length > 0) {
if (urb->num_sgs) {
unsigned int copy = urb->actual_length;
- int size;
+ unsigned int size;
for_each_sg(urb->sg, sg, urb->num_sgs, i) {
if (copy == 0)
break;
- if (copy < sg->length)
- size = copy;
- else
- size = sg->length;
-
+ size = min(copy, sg->length);
iov[iovnum].iov_base = sg_virt(sg);
iov[iovnum].iov_len = size;
diff --git a/drivers/usb/usbip/usbip_common.c b/drivers/usb/usbip/usbip_common.c
index a2b2da1255dd..a5837c0feb05 100644
--- a/drivers/usb/usbip/usbip_common.c
+++ b/drivers/usb/usbip/usbip_common.c
@@ -470,6 +470,18 @@ static void usbip_pack_ret_submit(struct usbip_header *pdu, struct urb *urb,
urb->status = rpdu->status;
urb->actual_length = rpdu->actual_length;
urb->start_frame = rpdu->start_frame;
+ /*
+ * The number_of_packets field determines the length of
+ * iso_frame_desc[], which is a flexible array allocated
+ * at URB creation time. A response must never claim more
+ * packets than originally submitted; doing so would cause
+ * an out-of-bounds write in usbip_recv_iso() and
+ * usbip_pad_iso(). Clamp to zero on violation so both
+ * functions safely return early.
+ */
+ if (rpdu->number_of_packets < 0 ||
+ rpdu->number_of_packets > urb->number_of_packets)
+ rpdu->number_of_packets = 0;
urb->number_of_packets = rpdu->number_of_packets;
urb->error_count = rpdu->error_count;
}
@@ -662,19 +674,29 @@ int usbip_recv_iso(struct usbip_device *ud, struct urb *urb)
void *buff;
struct usbip_iso_packet_descriptor *iso;
int np = urb->number_of_packets;
- int size = np * sizeof(*iso);
+ int size;
int i;
int ret;
- int total_length = 0;
+ u32 total_length = 0;
if (!usb_pipeisoc(urb->pipe))
return 0;
- /* my Bluetooth dongle gets ISO URBs which are np = 0 */
- if (np == 0)
- return 0;
+ if (np <= 0 || np > USBIP_MAX_ISO_PACKETS) {
+ dev_err(&urb->dev->dev,
+ "recv iso: invalid number_of_packets %d\n", np);
+ /*
+ * usbip_pack_ret_submit() already set urb->number_of_packets
+ * from the wire. Zero it so processcompl() does not iterate
+ * OOB descriptors on the way out.
+ */
+ urb->number_of_packets = 0;
+ return -EPROTO;
+ }
+
+ size = np * sizeof(*iso);
- buff = kzalloc(size, GFP_KERNEL);
+ buff = kcalloc(np, sizeof(*iso), GFP_KERNEL);
if (!buff)
return -ENOMEM;
@@ -696,14 +718,23 @@ int usbip_recv_iso(struct usbip_device *ud, struct urb *urb)
for (i = 0; i < np; i++) {
usbip_iso_packet_correct_endian(&iso[i], 0);
usbip_pack_iso(&iso[i], &urb->iso_frame_desc[i], 0);
+ if (urb->iso_frame_desc[i].actual_length >
+ (unsigned int)urb->transfer_buffer_length) {
+ dev_err(&urb->dev->dev,
+ "recv iso: frame actual_length %u exceeds buffer %d\n",
+ urb->iso_frame_desc[i].actual_length,
+ urb->transfer_buffer_length);
+ kfree(buff);
+ return -EPROTO;
+ }
total_length += urb->iso_frame_desc[i].actual_length;
}
kfree(buff);
- if (total_length != urb->actual_length) {
+ if (total_length != (u32)urb->actual_length) {
dev_err(&urb->dev->dev,
- "total length of iso packets %d not equal to actual length of buffer %d\n",
+ "total length of iso packets %u not equal to actual length of buffer %d\n",
total_length, urb->actual_length);
if (ud->side == USBIP_STUB || ud->side == USBIP_VUDC)
@@ -751,6 +782,42 @@ void usbip_pad_iso(struct usbip_device *ud, struct urb *urb)
*/
for (i = np-1; i > 0; i--) {
actualoffset -= urb->iso_frame_desc[i].actual_length;
+
+ /*
+ * Validate source range: actualoffset can go negative
+ * via crafted actual_length values from the wire.
+ */
+ if (actualoffset < 0 ||
+ (unsigned int)actualoffset >
+ (unsigned int)urb->transfer_buffer_length ||
+ urb->iso_frame_desc[i].actual_length >
+ (unsigned int)urb->transfer_buffer_length -
+ (unsigned int)actualoffset) {
+ dev_err(&urb->dev->dev,
+ "pad_iso: bad src off=%d len=%u bufsz=%d\n",
+ actualoffset,
+ urb->iso_frame_desc[i].actual_length,
+ urb->transfer_buffer_length);
+ return;
+ }
+
+ /*
+ * Validate destination range: iso_frame_desc[i].offset
+ * is wire-supplied and must not exceed the buffer.
+ */
+ if (urb->iso_frame_desc[i].offset >
+ (unsigned int)urb->transfer_buffer_length ||
+ urb->iso_frame_desc[i].actual_length >
+ (unsigned int)urb->transfer_buffer_length -
+ urb->iso_frame_desc[i].offset) {
+ dev_err(&urb->dev->dev,
+ "pad_iso: bad dst off=%u len=%u bufsz=%d\n",
+ urb->iso_frame_desc[i].offset,
+ urb->iso_frame_desc[i].actual_length,
+ urb->transfer_buffer_length);
+ return;
+ }
+
memmove(urb->transfer_buffer + urb->iso_frame_desc[i].offset,
urb->transfer_buffer + actualoffset,
urb->iso_frame_desc[i].actual_length);
diff --git a/drivers/usb/usbip/usbip_event.c b/drivers/usb/usbip/usbip_event.c
index 26513540bcdb..0e00c2d000f8 100644
--- a/drivers/usb/usbip/usbip_event.c
+++ b/drivers/usb/usbip/usbip_event.c
@@ -158,7 +158,7 @@ void usbip_event_add(struct usbip_device *ud, unsigned long event)
goto out;
}
- ue = kmalloc(sizeof(struct usbip_event), GFP_ATOMIC);
+ ue = kmalloc_obj(struct usbip_event, GFP_ATOMIC);
if (ue == NULL)
goto out;
diff --git a/drivers/usb/usbip/vhci_hcd.c b/drivers/usb/usbip/vhci_hcd.c
index e70fba9f55d6..39e8faf4c18c 100644
--- a/drivers/usb/usbip/vhci_hcd.c
+++ b/drivers/usb/usbip/vhci_hcd.c
@@ -346,9 +346,10 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
if (wIndex < 1 || wIndex > VHCI_HC_PORTS) {
invalid_rhport = true;
if (wIndex > VHCI_HC_PORTS)
- pr_err("invalid port number %d\n", wIndex);
- } else
+ dev_err(hcd_dev(hcd), "invalid port number %d\n", wIndex);
+ } else {
rhport = wIndex - 1;
+ }
vhci_hcd = hcd_to_vhci_hcd(hcd);
vhci = vhci_hcd->vhci;
@@ -368,14 +369,14 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
break;
case ClearPortFeature:
if (invalid_rhport) {
- pr_err("invalid port number %d\n", wIndex);
+ dev_err(hcd_dev(hcd), "invalid port number %d\n", wIndex);
goto error;
}
switch (wValue) {
case USB_PORT_FEAT_SUSPEND:
if (hcd->speed >= HCD_USB3) {
- pr_err(" ClearPortFeature: USB_PORT_FEAT_SUSPEND req not "
- "supported for USB 3.0 roothub\n");
+ dev_err(hcd_dev(hcd),
+ "ClearPortFeature: USB_PORT_FEAT_SUSPEND req not supported for USB 3.0 roothub\n");
goto error;
}
usbip_dbg_vhci_rh(
@@ -408,7 +409,8 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
if (hcd->speed >= HCD_USB3 &&
(wLength < USB_DT_SS_HUB_SIZE ||
wValue != (USB_DT_SS_HUB << 8))) {
- pr_err("Wrong hub descriptor type for USB 3.0 roothub.\n");
+ dev_err(hcd_dev(hcd),
+ "Wrong hub descriptor type for USB 3.0 roothub.\n");
goto error;
}
if (hcd->speed >= HCD_USB3)
@@ -433,7 +435,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
case GetPortStatus:
usbip_dbg_vhci_rh(" GetPortStatus port %x\n", wIndex);
if (invalid_rhport) {
- pr_err("invalid port number %d\n", wIndex);
+ dev_err(hcd_dev(hcd), "invalid port number %d\n", wIndex);
retval = -EPIPE;
goto error;
}
@@ -483,7 +485,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
USB_PORT_STAT_LOW_SPEED;
break;
default:
- pr_err("vhci_device speed not set\n");
+ dev_err(hcd_dev(hcd), "vhci_device speed not set\n");
break;
}
}
@@ -505,8 +507,8 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
usbip_dbg_vhci_rh(
" SetPortFeature: USB_PORT_FEAT_LINK_STATE\n");
if (hcd->speed < HCD_USB3) {
- pr_err("USB_PORT_FEAT_LINK_STATE req not "
- "supported for USB 2.0 roothub\n");
+ dev_err(hcd_dev(hcd),
+ "USB_PORT_FEAT_LINK_STATE req not supported for USB 2.0 roothub\n");
goto error;
}
/*
@@ -523,8 +525,8 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
" SetPortFeature: USB_PORT_FEAT_U2_TIMEOUT\n");
/* TODO: add suspend/resume support! */
if (hcd->speed < HCD_USB3) {
- pr_err("USB_PORT_FEAT_U1/2_TIMEOUT req not "
- "supported for USB 2.0 roothub\n");
+ dev_err(hcd_dev(hcd),
+ "USB_PORT_FEAT_U1/2_TIMEOUT req not supported for USB 2.0 roothub\n");
goto error;
}
break;
@@ -533,13 +535,13 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
" SetPortFeature: USB_PORT_FEAT_SUSPEND\n");
/* Applicable only for USB2.0 hub */
if (hcd->speed >= HCD_USB3) {
- pr_err("USB_PORT_FEAT_SUSPEND req not "
- "supported for USB 3.0 roothub\n");
+ dev_err(hcd_dev(hcd),
+ "USB_PORT_FEAT_SUSPEND req not supported for USB 3.0 roothub\n");
goto error;
}
if (invalid_rhport) {
- pr_err("invalid port number %d\n", wIndex);
+ dev_err(hcd_dev(hcd), "invalid port number %d\n", wIndex);
goto error;
}
@@ -549,7 +551,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
usbip_dbg_vhci_rh(
" SetPortFeature: USB_PORT_FEAT_POWER\n");
if (invalid_rhport) {
- pr_err("invalid port number %d\n", wIndex);
+ dev_err(hcd_dev(hcd), "invalid port number %d\n", wIndex);
goto error;
}
if (hcd->speed >= HCD_USB3)
@@ -561,13 +563,13 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
usbip_dbg_vhci_rh(
" SetPortFeature: USB_PORT_FEAT_BH_PORT_RESET\n");
if (invalid_rhport) {
- pr_err("invalid port number %d\n", wIndex);
+ dev_err(hcd_dev(hcd), "invalid port number %d\n", wIndex);
goto error;
}
/* Applicable only for USB3.0 hub */
if (hcd->speed < HCD_USB3) {
- pr_err("USB_PORT_FEAT_BH_PORT_RESET req not "
- "supported for USB 2.0 roothub\n");
+ dev_err(hcd_dev(hcd),
+ "USB_PORT_FEAT_BH_PORT_RESET req not supported for USB 2.0 roothub\n");
goto error;
}
fallthrough;
@@ -575,7 +577,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
usbip_dbg_vhci_rh(
" SetPortFeature: USB_PORT_FEAT_RESET\n");
if (invalid_rhport) {
- pr_err("invalid port number %d\n", wIndex);
+ dev_err(hcd_dev(hcd), "invalid port number %d\n", wIndex);
goto error;
}
/* if it's already enabled, disable */
@@ -598,7 +600,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
usbip_dbg_vhci_rh(" SetPortFeature: default %d\n",
wValue);
if (invalid_rhport) {
- pr_err("invalid port number %d\n", wIndex);
+ dev_err(hcd_dev(hcd), "invalid port number %d\n", wIndex);
goto error;
}
if (wValue >= 32)
@@ -618,8 +620,8 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
case GetPortErrorCount:
usbip_dbg_vhci_rh(" GetPortErrorCount\n");
if (hcd->speed < HCD_USB3) {
- pr_err("GetPortErrorCount req not "
- "supported for USB 2.0 roothub\n");
+ dev_err(hcd_dev(hcd),
+ "GetPortErrorCount req not supported for USB 2.0 roothub\n");
goto error;
}
/* We'll always return 0 since this is a dummy hub */
@@ -628,13 +630,14 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
case SetHubDepth:
usbip_dbg_vhci_rh(" SetHubDepth\n");
if (hcd->speed < HCD_USB3) {
- pr_err("SetHubDepth req not supported for "
- "USB 2.0 roothub\n");
+ dev_err(hcd_dev(hcd),
+ "SetHubDepth req not supported for USB 2.0 roothub\n");
goto error;
}
break;
default:
- pr_err("default hub control req: %04x v%04x i%04x l%d\n",
+ dev_err(hcd_dev(hcd),
+ "default hub control req: %04x v%04x i%04x l%d\n",
typeReq, wValue, wIndex, wLength);
error:
/* "protocol stall" on error */
@@ -642,7 +645,7 @@ error:
}
if (usbip_dbg_flag_vhci_rh) {
- pr_debug("port %d\n", rhport);
+ dev_dbg(hcd_dev(hcd), "%s port %d\n", __func__, rhport);
/* Only dump valid port status */
if (!invalid_rhport) {
dump_port_status_diff(prev_port_status[rhport],
@@ -668,7 +671,7 @@ static void vhci_tx_urb(struct urb *urb, struct vhci_device *vdev)
struct vhci_hcd *vhci_hcd = vdev_to_vhci_hcd(vdev);
unsigned long flags;
- priv = kzalloc(sizeof(struct vhci_priv), GFP_ATOMIC);
+ priv = kzalloc_obj(struct vhci_priv, GFP_ATOMIC);
if (!priv) {
usbip_event_add(&vdev->ud, VDEV_EVENT_ERROR_MALLOC);
return;
@@ -702,7 +705,7 @@ static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag
unsigned long flags;
if (portnum > VHCI_HC_PORTS) {
- pr_err("invalid port number %d\n", portnum);
+ dev_err(hcd_dev(hcd), "invalid port number %d\n", portnum);
return -ENODEV;
}
vdev = &vhci_hcd->vdev[portnum-1];
@@ -765,6 +768,17 @@ static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag
ctrlreq->wValue, vdev->rhport);
vdev->udev = usb_get_dev(urb->dev);
+ /*
+ * NOTE: A similar operation has been done via
+ * USB_REQ_GET_DESCRIPTOR handler below, which is
+ * supposed to always precede USB_REQ_SET_ADDRESS.
+ *
+ * It's not entirely clear if operating on a different
+ * usb_device instance here is a real possibility,
+ * otherwise this call and vdev->udev assignment above
+ * should be dropped.
+ */
+ dev_pm_syscore_device(&vdev->udev->dev, true);
usb_put_dev(old);
spin_lock(&vdev->ud.lock);
@@ -785,6 +799,17 @@ static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag
"Not yet?:Get_Descriptor to device 0 (get max pipe size)\n");
vdev->udev = usb_get_dev(urb->dev);
+ /*
+ * Set syscore PM flag for the virtually attached
+ * devices to ensure they will not enter suspend on
+ * the client side.
+ *
+ * Note this doesn't have any impact on the physical
+ * devices attached to the host system on the server
+ * side, hence there is no need to undo the operation
+ * on disconnect.
+ */
+ dev_pm_syscore_device(&vdev->udev->dev, true);
usb_put_dev(old);
goto out;
@@ -809,15 +834,15 @@ out:
no_need_xmit:
usb_hcd_unlink_urb_from_ep(hcd, urb);
no_need_unlink:
- spin_unlock_irqrestore(&vhci->lock, flags);
if (!ret) {
/* usb_hcd_giveback_urb() should be called with
* irqs disabled
*/
- local_irq_disable();
+ spin_unlock(&vhci->lock);
usb_hcd_giveback_urb(hcd, urb, urb->status);
- local_irq_enable();
+ spin_lock(&vhci->lock);
}
+ spin_unlock_irqrestore(&vhci->lock, flags);
return ret;
}
@@ -926,7 +951,7 @@ static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
spin_lock(&vdev->priv_lock);
/* setup CMD_UNLINK pdu */
- unlink = kzalloc(sizeof(struct vhci_unlink), GFP_ATOMIC);
+ unlink = kzalloc_obj(struct vhci_unlink, GFP_ATOMIC);
if (!unlink) {
spin_unlock(&vdev->priv_lock);
spin_unlock_irqrestore(&vhci->lock, flags);
@@ -936,7 +961,7 @@ static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
unlink->seqnum = atomic_inc_return(&vhci_hcd->seqnum);
if (unlink->seqnum == 0xffff)
- pr_info("seqnum max\n");
+ dev_info(hcd_dev(hcd), "seqnum max\n");
unlink->unlink_seqnum = priv->seqnum;
@@ -1014,10 +1039,11 @@ static void vhci_device_unlink_cleanup(struct vhci_device *vdev)
static void vhci_shutdown_connection(struct usbip_device *ud)
{
struct vhci_device *vdev = container_of(ud, struct vhci_device, ud);
+ struct usb_hcd *hcd = vhci_hcd_to_hcd(vdev_to_vhci_hcd(vdev));
/* need this? see stub_dev.c */
if (ud->tcp_socket) {
- pr_debug("shutdown tcp_socket %d\n", ud->sockfd);
+ dev_dbg(hcd_dev(hcd), "shutdown tcp_socket %d\n", ud->sockfd);
kernel_sock_shutdown(ud->tcp_socket, SHUT_RDWR);
}
@@ -1030,7 +1056,7 @@ static void vhci_shutdown_connection(struct usbip_device *ud)
kthread_stop_put(vdev->ud.tcp_tx);
vdev->ud.tcp_tx = NULL;
}
- pr_info("stop threads\n");
+ dev_info(hcd_dev(hcd), "stop threads\n");
/* active connection is closed */
if (vdev->ud.tcp_socket) {
@@ -1038,7 +1064,7 @@ static void vhci_shutdown_connection(struct usbip_device *ud)
vdev->ud.tcp_socket = NULL;
vdev->ud.sockfd = -1;
}
- pr_info("release socket\n");
+ dev_info(hcd_dev(hcd), "release socket\n");
vhci_device_unlink_cleanup(vdev);
@@ -1064,7 +1090,7 @@ static void vhci_shutdown_connection(struct usbip_device *ud)
*/
rh_port_disconnect(vdev);
- pr_info("disconnect device\n");
+ dev_info(hcd_dev(hcd), "disconnect device\n");
}
static void vhci_device_reset(struct usbip_device *ud)
@@ -1200,7 +1226,7 @@ static int vhci_start(struct usb_hcd *hcd)
id = hcd_name_to_id(hcd_name(hcd));
if (id < 0) {
- pr_err("invalid vhci name %s\n", hcd_name(hcd));
+ dev_err(hcd_dev(hcd), "invalid vhci name %s\n", hcd_name(hcd));
return -EINVAL;
}
@@ -1217,7 +1243,7 @@ static int vhci_start(struct usb_hcd *hcd)
vhci_finish_attr_group();
return err;
}
- pr_info("created sysfs %s\n", hcd_name(hcd));
+ dev_info(hcd_dev(hcd), "created sysfs %s\n", hcd_name(hcd));
}
return 0;
@@ -1350,10 +1376,9 @@ static int vhci_hcd_probe(struct platform_device *pdev)
* Our private data is also allocated automatically.
*/
hcd_hs = usb_create_hcd(&vhci_hc_driver, &pdev->dev, dev_name(&pdev->dev));
- if (!hcd_hs) {
- pr_err("create primary hcd failed\n");
- return -ENOMEM;
- }
+ if (!hcd_hs)
+ return dev_err_probe(&pdev->dev, -ENOMEM, "create primary hcd failed\n");
+
hcd_hs->has_tt = 1;
/*
@@ -1361,22 +1386,21 @@ static int vhci_hcd_probe(struct platform_device *pdev)
* Call the driver's reset() and start() routines.
*/
ret = usb_add_hcd(hcd_hs, 0, 0);
- if (ret != 0) {
- pr_err("usb_add_hcd hs failed %d\n", ret);
+ if (ret) {
+ dev_err_probe(&pdev->dev, ret, "usb_add_hcd hs failed\n");
goto put_usb2_hcd;
}
hcd_ss = usb_create_shared_hcd(&vhci_hc_driver, &pdev->dev,
dev_name(&pdev->dev), hcd_hs);
if (!hcd_ss) {
- ret = -ENOMEM;
- pr_err("create shared hcd failed\n");
+ ret = dev_err_probe(&pdev->dev, -ENOMEM, "create shared hcd failed\n");
goto remove_usb2_hcd;
}
ret = usb_add_hcd(hcd_ss, 0, 0);
if (ret) {
- pr_err("usb_add_hcd ss failed %d\n", ret);
+ dev_err_probe(&pdev->dev, ret, "usb_add_hcd ss failed\n");
goto put_usb3_hcd;
}
@@ -1513,7 +1537,7 @@ static int __init vhci_hcd_init(void)
if (vhci_num_controllers < 1)
vhci_num_controllers = 1;
- vhcis = kcalloc(vhci_num_controllers, sizeof(struct vhci), GFP_KERNEL);
+ vhcis = kzalloc_objs(struct vhci, vhci_num_controllers);
if (vhcis == NULL)
return -ENOMEM;
diff --git a/drivers/usb/usbip/vhci_sysfs.c b/drivers/usb/usbip/vhci_sysfs.c
index d5865460e82d..5bc8c47788d4 100644
--- a/drivers/usb/usbip/vhci_sysfs.c
+++ b/drivers/usb/usbip/vhci_sysfs.c
@@ -463,7 +463,7 @@ static void set_status_attr(int id)
status = status_attrs + id;
if (id == 0)
- strcpy(status->name, "status");
+ strscpy(status->name, "status");
else
snprintf(status->name, MAX_STATUS_NAME+1, "status.%d", id);
status->attr.attr.name = status->name;
@@ -476,8 +476,7 @@ static int init_status_attrs(void)
{
int id;
- status_attrs = kcalloc(vhci_num_controllers, sizeof(struct status_attr),
- GFP_KERNEL);
+ status_attrs = kzalloc_objs(struct status_attr, vhci_num_controllers);
if (status_attrs == NULL)
return -ENOMEM;
@@ -501,8 +500,7 @@ int vhci_init_attr_group(void)
struct attribute **attrs;
int ret, i;
- attrs = kcalloc((vhci_num_controllers + 5), sizeof(struct attribute *),
- GFP_KERNEL);
+ attrs = kzalloc_objs(struct attribute *, (vhci_num_controllers + 5));
if (attrs == NULL)
return -ENOMEM;
diff --git a/drivers/usb/usbip/vhci_tx.c b/drivers/usb/usbip/vhci_tx.c
index 0ae40a13a9fe..32e6fabccf72 100644
--- a/drivers/usb/usbip/vhci_tx.c
+++ b/drivers/usb/usbip/vhci_tx.c
@@ -82,7 +82,7 @@ static int vhci_send_cmd_submit(struct vhci_device *vdev)
else
iovnum = 3;
- iov = kcalloc(iovnum, sizeof(*iov), GFP_KERNEL);
+ iov = kzalloc_objs(*iov, iovnum);
if (!iov) {
usbip_event_add(&vdev->ud, SDEV_EVENT_ERROR_MALLOC);
return -ENOMEM;
diff --git a/drivers/usb/usbip/vudc_dev.c b/drivers/usb/usbip/vudc_dev.c
index f11535020e35..90383107b660 100644
--- a/drivers/usb/usbip/vudc_dev.c
+++ b/drivers/usb/usbip/vudc_dev.c
@@ -43,7 +43,7 @@ struct urbp *alloc_urbp(void)
{
struct urbp *urb_p;
- urb_p = kzalloc(sizeof(*urb_p), GFP_KERNEL);
+ urb_p = kzalloc_obj(*urb_p);
if (!urb_p)
return urb_p;
@@ -284,7 +284,7 @@ static struct usb_request *vep_alloc_request(struct usb_ep *_ep,
if (!_ep)
return NULL;
- req = kzalloc(sizeof(*req), mem_flags);
+ req = kzalloc_obj(*req, mem_flags);
if (!req)
return NULL;
@@ -491,7 +491,7 @@ struct vudc_device *alloc_vudc_device(int devid)
{
struct vudc_device *udc_dev;
- udc_dev = kzalloc(sizeof(*udc_dev), GFP_KERNEL);
+ udc_dev = kzalloc_obj(*udc_dev);
if (!udc_dev)
return NULL;
@@ -518,7 +518,7 @@ static int init_vudc_hw(struct vudc *udc)
struct usbip_device *ud = &udc->ud;
struct vep *ep;
- udc->ep = kcalloc(VIRTUAL_ENDPOINTS, sizeof(*udc->ep), GFP_KERNEL);
+ udc->ep = kzalloc_objs(*udc->ep, VIRTUAL_ENDPOINTS);
if (!udc->ep)
goto nomem_ep;
@@ -598,7 +598,7 @@ int vudc_probe(struct platform_device *pdev)
struct vudc *udc;
int ret = -ENOMEM;
- udc = kzalloc(sizeof(*udc), GFP_KERNEL);
+ udc = kzalloc_obj(*udc);
if (!udc)
goto out;
diff --git a/drivers/usb/usbip/vudc_sysfs.c b/drivers/usb/usbip/vudc_sysfs.c
index 2aae3edfc813..47f3a7d51736 100644
--- a/drivers/usb/usbip/vudc_sysfs.c
+++ b/drivers/usb/usbip/vudc_sysfs.c
@@ -259,7 +259,7 @@ static const struct bin_attribute *const dev_bin_attrs[] = {
static const struct attribute_group vudc_attr_group = {
.attrs = dev_attrs,
- .bin_attrs_new = dev_bin_attrs,
+ .bin_attrs = dev_bin_attrs,
};
const struct attribute_group *vudc_groups[] = {
diff --git a/drivers/usb/usbip/vudc_transfer.c b/drivers/usb/usbip/vudc_transfer.c
index 7e801fee33bf..a4f02ea3e3ef 100644
--- a/drivers/usb/usbip/vudc_transfer.c
+++ b/drivers/usb/usbip/vudc_transfer.c
@@ -301,7 +301,7 @@ top:
static void v_timer(struct timer_list *t)
{
- struct vudc *udc = from_timer(udc, t, tr_timer.timer);
+ struct vudc *udc = timer_container_of(udc, t, tr_timer.timer);
struct transfer_timer *timer = &udc->tr_timer;
struct urbp *urb_p, *tmp;
unsigned long flags;
diff --git a/drivers/usb/usbip/vudc_tx.c b/drivers/usb/usbip/vudc_tx.c
index 30c11bf9f4e7..88edfbcc0035 100644
--- a/drivers/usb/usbip/vudc_tx.c
+++ b/drivers/usb/usbip/vudc_tx.c
@@ -97,7 +97,7 @@ static int v_send_ret_submit(struct vudc *udc, struct urbp *urb_p)
else
iovnum = 2;
- iov = kcalloc(iovnum, sizeof(*iov), GFP_KERNEL);
+ iov = kzalloc_objs(*iov, iovnum);
if (!iov) {
usbip_event_add(&udc->ud, VUDC_EVENT_ERROR_MALLOC);
ret = -ENOMEM;
@@ -246,12 +246,12 @@ void v_enqueue_ret_unlink(struct vudc *udc, __u32 seqnum, __u32 status)
struct tx_item *txi;
struct v_unlink *unlink;
- txi = kzalloc(sizeof(*txi), GFP_ATOMIC);
+ txi = kzalloc_obj(*txi, GFP_ATOMIC);
if (!txi) {
usbip_event_add(&udc->ud, VDEV_EVENT_ERROR_MALLOC);
return;
}
- unlink = kzalloc(sizeof(*unlink), GFP_ATOMIC);
+ unlink = kzalloc_obj(*unlink, GFP_ATOMIC);
if (!unlink) {
kfree(txi);
usbip_event_add(&udc->ud, VDEV_EVENT_ERROR_MALLOC);
@@ -271,7 +271,7 @@ void v_enqueue_ret_submit(struct vudc *udc, struct urbp *urb_p)
{
struct tx_item *txi;
- txi = kzalloc(sizeof(*txi), GFP_ATOMIC);
+ txi = kzalloc_obj(*txi, GFP_ATOMIC);
if (!txi) {
usbip_event_add(&udc->ud, VDEV_EVENT_ERROR_MALLOC);
return;