summaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/chipidea/Kconfig9
-rw-r--r--drivers/usb/chipidea/Makefile3
-rw-r--r--drivers/usb/chipidea/ci.h10
-rw-r--r--drivers/usb/chipidea/ci_hdrc_imx.c22
-rw-r--r--drivers/usb/chipidea/ci_hdrc_tegra.c4
-rw-r--r--drivers/usb/chipidea/core.c23
-rw-r--r--drivers/usb/chipidea/debug.c56
-rw-r--r--drivers/usb/chipidea/host.c14
-rw-r--r--drivers/usb/chipidea/otg.c9
-rw-r--r--drivers/usb/chipidea/otg.h3
-rw-r--r--drivers/usb/chipidea/udc.c9
-rw-r--r--drivers/usb/chipidea/ulpi.c3
-rw-r--r--drivers/usb/chipidea/usbmisc_imx.c4
-rw-r--r--drivers/usb/class/cdc-acm.c185
-rw-r--r--drivers/usb/class/cdc-acm.h1
-rw-r--r--drivers/usb/class/cdc-wdm.c47
-rw-r--r--drivers/usb/class/usblp.c10
-rw-r--r--drivers/usb/class/usbtmc.c1944
-rw-r--r--drivers/usb/common/common.c25
-rw-r--r--drivers/usb/common/roles.c15
-rw-r--r--drivers/usb/core/buffer.c8
-rw-r--r--drivers/usb/core/devio.c40
-rw-r--r--drivers/usb/core/driver.c32
-rw-r--r--drivers/usb/core/generic.c27
-rw-r--r--drivers/usb/core/hcd-pci.c2
-rw-r--r--drivers/usb/core/hcd.c63
-rw-r--r--drivers/usb/core/hub.c151
-rw-r--r--drivers/usb/core/hub.h1
-rw-r--r--drivers/usb/core/ledtrig-usbport.c34
-rw-r--r--drivers/usb/core/message.c28
-rw-r--r--drivers/usb/core/of.c26
-rw-r--r--drivers/usb/core/phy.c7
-rw-r--r--drivers/usb/core/port.c33
-rw-r--r--drivers/usb/core/quirks.c35
-rw-r--r--drivers/usb/core/sysfs.c22
-rw-r--r--drivers/usb/core/usb.c34
-rw-r--r--drivers/usb/dwc2/core.c248
-rw-r--r--drivers/usb/dwc2/core.h329
-rw-r--r--drivers/usb/dwc2/core_intr.c122
-rw-r--r--drivers/usb/dwc2/debug.h2
-rw-r--r--drivers/usb/dwc2/debugfs.c156
-rw-r--r--drivers/usb/dwc2/gadget.c994
-rw-r--r--drivers/usb/dwc2/hcd.c666
-rw-r--r--drivers/usb/dwc2/hcd.h32
-rw-r--r--drivers/usb/dwc2/hcd_ddma.c11
-rw-r--r--drivers/usb/dwc2/hcd_intr.c128
-rw-r--r--drivers/usb/dwc2/hcd_queue.c20
-rw-r--r--drivers/usb/dwc2/hw.h17
-rw-r--r--drivers/usb/dwc2/params.c45
-rw-r--r--drivers/usb/dwc2/pci.c7
-rw-r--r--drivers/usb/dwc2/platform.c27
-rw-r--r--drivers/usb/dwc3/Kconfig25
-rw-r--r--drivers/usb/dwc3/Makefile2
-rw-r--r--drivers/usb/dwc3/core.c278
-rw-r--r--drivers/usb/dwc3/core.h42
-rw-r--r--drivers/usb/dwc3/debug.h26
-rw-r--r--drivers/usb/dwc3/debugfs.c43
-rw-r--r--drivers/usb/dwc3/drd.c34
-rw-r--r--drivers/usb/dwc3/dwc3-exynos.c212
-rw-r--r--drivers/usb/dwc3/dwc3-haps.c137
-rw-r--r--drivers/usb/dwc3/dwc3-of-simple.c40
-rw-r--r--drivers/usb/dwc3/dwc3-pci.c225
-rw-r--r--drivers/usb/dwc3/dwc3-qcom.c616
-rw-r--r--drivers/usb/dwc3/ep0.c9
-rw-r--r--drivers/usb/dwc3/gadget.c818
-rw-r--r--drivers/usb/dwc3/gadget.h7
-rw-r--r--drivers/usb/dwc3/trace.h12
-rw-r--r--drivers/usb/early/xhci-dbc.c17
-rw-r--r--drivers/usb/gadget/composite.c6
-rw-r--r--drivers/usb/gadget/configfs.c4
-rw-r--r--drivers/usb/gadget/function/f_ecm.c3
-rw-r--r--drivers/usb/gadget/function/f_fs.c15
-rw-r--r--drivers/usb/gadget/function/f_mass_storage.c38
-rw-r--r--drivers/usb/gadget/function/f_mass_storage.h4
-rw-r--r--drivers/usb/gadget/function/f_midi.c31
-rw-r--r--drivers/usb/gadget/function/f_printer.c6
-rw-r--r--drivers/usb/gadget/function/f_tcm.c19
-rw-r--r--drivers/usb/gadget/function/f_uac2.c236
-rw-r--r--drivers/usb/gadget/function/f_uvc.c88
-rw-r--r--drivers/usb/gadget/function/f_uvc.h6
-rw-r--r--drivers/usb/gadget/function/rndis.c3
-rw-r--r--drivers/usb/gadget/function/u_audio.c88
-rw-r--r--drivers/usb/gadget/function/u_ether.c15
-rw-r--r--drivers/usb/gadget/function/u_uvc.h8
-rw-r--r--drivers/usb/gadget/function/uvc.h69
-rw-r--r--drivers/usb/gadget/function/uvc_configfs.c1176
-rw-r--r--drivers/usb/gadget/function/uvc_queue.c2
-rw-r--r--drivers/usb/gadget/function/uvc_queue.h12
-rw-r--r--drivers/usb/gadget/function/uvc_v4l2.c7
-rw-r--r--drivers/usb/gadget/function/uvc_video.c48
-rw-r--r--drivers/usb/gadget/function/uvc_video.h4
-rw-r--r--drivers/usb/gadget/legacy/tcm_usb_gadget.c2
-rw-r--r--drivers/usb/gadget/legacy/webcam.c4
-rw-r--r--drivers/usb/gadget/udc/Kconfig7
-rw-r--r--drivers/usb/gadget/udc/Makefile1
-rw-r--r--drivers/usb/gadget/udc/aspeed-vhub/Kconfig8
-rw-r--r--drivers/usb/gadget/udc/aspeed-vhub/Makefile4
-rw-r--r--drivers/usb/gadget/udc/aspeed-vhub/core.c425
-rw-r--r--drivers/usb/gadget/udc/aspeed-vhub/dev.c589
-rw-r--r--drivers/usb/gadget/udc/aspeed-vhub/ep0.c495
-rw-r--r--drivers/usb/gadget/udc/aspeed-vhub/epn.c851
-rw-r--r--drivers/usb/gadget/udc/aspeed-vhub/hub.c829
-rw-r--r--drivers/usb/gadget/udc/aspeed-vhub/vhub.h547
-rw-r--r--drivers/usb/gadget/udc/at91_udc.c16
-rw-r--r--drivers/usb/gadget/udc/atmel_usba_udc.c103
-rw-r--r--drivers/usb/gadget/udc/atmel_usba_udc.h5
-rw-r--r--drivers/usb/gadget/udc/bcm63xx_udc.c37
-rw-r--r--drivers/usb/gadget/udc/bdc/bdc_ep.c6
-rw-r--r--drivers/usb/gadget/udc/core.c33
-rw-r--r--drivers/usb/gadget/udc/fotg210-udc.c17
-rw-r--r--drivers/usb/gadget/udc/fsl_mxc_udc.c1
-rw-r--r--drivers/usb/gadget/udc/fsl_udc_core.c57
-rw-r--r--drivers/usb/gadget/udc/goku_udc.c18
-rw-r--r--drivers/usb/gadget/udc/gr_udc.c7
-rw-r--r--drivers/usb/gadget/udc/gr_udc.h1
-rw-r--r--drivers/usb/gadget/udc/mv_udc_core.c2
-rw-r--r--drivers/usb/gadget/udc/net2280.c13
-rw-r--r--drivers/usb/gadget/udc/omap_udc.c103
-rw-r--r--drivers/usb/gadget/udc/pxa27x_udc.c43
-rw-r--r--drivers/usb/gadget/udc/pxa27x_udc.h3
-rw-r--r--drivers/usb/gadget/udc/r8a66597-udc.c6
-rw-r--r--drivers/usb/gadget/udc/renesas_usb3.c149
-rw-r--r--drivers/usb/gadget/udc/s3c2410_udc.c17
-rw-r--r--drivers/usb/gadget/udc/udc-xilinx.c2
-rw-r--r--drivers/usb/gadget/usbstring.c2
-rw-r--r--drivers/usb/host/Kconfig21
-rw-r--r--drivers/usb/host/Makefile3
-rw-r--r--drivers/usb/host/ehci-dbg.c30
-rw-r--r--drivers/usb/host/ehci-exynos.c7
-rw-r--r--drivers/usb/host/ehci-hcd.c17
-rw-r--r--drivers/usb/host/ehci-hub.c8
-rw-r--r--drivers/usb/host/ehci-mv.c181
-rw-r--r--drivers/usb/host/ehci-npcm7xx.c212
-rw-r--r--drivers/usb/host/ehci-omap.c5
-rw-r--r--drivers/usb/host/ehci-ps3.c6
-rw-r--r--drivers/usb/host/ehci-q.c4
-rw-r--r--drivers/usb/host/ehci-sched.c9
-rw-r--r--drivers/usb/host/ehci-tegra.c87
-rw-r--r--drivers/usb/host/ehci-timer.c2
-rw-r--r--drivers/usb/host/ehci.h4
-rw-r--r--drivers/usb/host/fhci-dbg.c23
-rw-r--r--drivers/usb/host/fhci-tds.c2
-rw-r--r--drivers/usb/host/fhci.h2
-rw-r--r--drivers/usb/host/fotg210-hcd.c84
-rw-r--r--drivers/usb/host/fotg210.h7
-rw-r--r--drivers/usb/host/hwa-hc.c2
-rw-r--r--drivers/usb/host/imx21-dbg.c44
-rw-r--r--drivers/usb/host/imx21-hcd.c4
-rw-r--r--drivers/usb/host/isp116x-hcd.c17
-rw-r--r--drivers/usb/host/ohci-at91.c4
-rw-r--r--drivers/usb/host/ohci-dbg.c47
-rw-r--r--drivers/usb/host/ohci-exynos.c6
-rw-r--r--drivers/usb/host/ohci-hcd.c5
-rw-r--r--drivers/usb/host/ohci-ps3.c6
-rw-r--r--drivers/usb/host/ohci.h3
-rw-r--r--drivers/usb/host/pci-quirks.c32
-rw-r--r--drivers/usb/host/pci-quirks.h1
-rw-r--r--drivers/usb/host/u132-hcd.c4
-rw-r--r--drivers/usb/host/uhci-hcd.c15
-rw-r--r--drivers/usb/host/whci/pzl.c2
-rw-r--r--drivers/usb/host/xhci-dbgcap.c15
-rw-r--r--drivers/usb/host/xhci-dbgcap.h2
-rw-r--r--drivers/usb/host/xhci-dbgtty.c1
-rw-r--r--drivers/usb/host/xhci-debugfs.c85
-rw-r--r--drivers/usb/host/xhci-histb.c412
-rw-r--r--drivers/usb/host/xhci-hub.c327
-rw-r--r--drivers/usb/host/xhci-mem.c203
-rw-r--r--drivers/usb/host/xhci-mtk-sch.c433
-rw-r--r--drivers/usb/host/xhci-mtk.c10
-rw-r--r--drivers/usb/host/xhci-mtk.h23
-rw-r--r--drivers/usb/host/xhci-pci.c57
-rw-r--r--drivers/usb/host/xhci-plat.c37
-rw-r--r--drivers/usb/host/xhci-rcar.c43
-rw-r--r--drivers/usb/host/xhci-ring.c185
-rw-r--r--drivers/usb/host/xhci-tegra.c301
-rw-r--r--drivers/usb/host/xhci-trace.h36
-rw-r--r--drivers/usb/host/xhci.c246
-rw-r--r--drivers/usb/host/xhci.h121
-rw-r--r--drivers/usb/isp1760/isp1760-core.c2
-rw-r--r--drivers/usb/isp1760/isp1760-hcd.c4
-rw-r--r--drivers/usb/misc/adutux.c10
-rw-r--r--drivers/usb/misc/appledisplay.c10
-rw-r--r--drivers/usb/misc/iowarrior.c9
-rw-r--r--drivers/usb/misc/ldusb.c16
-rw-r--r--drivers/usb/misc/legousbtower.c5
-rw-r--r--drivers/usb/misc/sisusbvga/sisusb.c6
-rw-r--r--drivers/usb/misc/sisusbvga/sisusb_con.c2
-rw-r--r--drivers/usb/misc/trancevibrator.c4
-rw-r--r--drivers/usb/misc/usbtest.c10
-rw-r--r--drivers/usb/misc/uss720.c6
-rw-r--r--drivers/usb/misc/yurex.c29
-rw-r--r--drivers/usb/mon/mon_bin.c13
-rw-r--r--drivers/usb/mon/mon_text.c37
-rw-r--r--drivers/usb/mtu3/Kconfig3
-rw-r--r--drivers/usb/mtu3/mtu3.h15
-rw-r--r--drivers/usb/mtu3/mtu3_core.c36
-rw-r--r--drivers/usb/mtu3/mtu3_dr.c73
-rw-r--r--drivers/usb/mtu3/mtu3_gadget.c29
-rw-r--r--drivers/usb/mtu3/mtu3_gadget_ep0.c12
-rw-r--r--drivers/usb/mtu3/mtu3_hw_regs.h1
-rw-r--r--drivers/usb/mtu3/mtu3_plat.c6
-rw-r--r--drivers/usb/musb/am35x.c3
-rw-r--r--drivers/usb/musb/da8xx.c3
-rw-r--r--drivers/usb/musb/davinci.c49
-rw-r--r--drivers/usb/musb/musb_core.c838
-rw-r--r--drivers/usb/musb/musb_core.h16
-rw-r--r--drivers/usb/musb/musb_cppi41.c4
-rw-r--r--drivers/usb/musb/musb_debug.h5
-rw-r--r--drivers/usb/musb/musb_debugfs.c44
-rw-r--r--drivers/usb/musb/musb_dma.h10
-rw-r--r--drivers/usb/musb/musb_dsps.c53
-rw-r--r--drivers/usb/musb/musb_gadget.c8
-rw-r--r--drivers/usb/musb/musb_gadget.h10
-rw-r--r--drivers/usb/musb/musb_host.c9
-rw-r--r--drivers/usb/musb/musb_io.h6
-rw-r--r--drivers/usb/musb/musb_regs.h55
-rw-r--r--drivers/usb/musb/musb_virthub.c2
-rw-r--r--drivers/usb/musb/musbhsdma.c74
-rw-r--r--drivers/usb/musb/musbhsdma.h72
-rw-r--r--drivers/usb/musb/omap2430.c100
-rw-r--r--drivers/usb/musb/sunxi.c14
-rw-r--r--drivers/usb/musb/ux500.c2
-rw-r--r--drivers/usb/phy/Kconfig10
-rw-r--r--drivers/usb/phy/Makefile2
-rw-r--r--drivers/usb/phy/phy-ab8500-usb.c8
-rw-r--r--drivers/usb/phy/phy-am335x.c6
-rw-r--r--drivers/usb/phy/phy-fsl-usb.c4
-rw-r--r--drivers/usb/phy/phy-mxs-usb.c2
-rw-r--r--drivers/usb/phy/phy-tegra-usb.c140
-rw-r--r--drivers/usb/phy/phy.c133
-rw-r--r--drivers/usb/renesas_usbhs/Kconfig1
-rw-r--r--drivers/usb/renesas_usbhs/common.c113
-rw-r--r--drivers/usb/renesas_usbhs/common.h6
-rw-r--r--drivers/usb/renesas_usbhs/mod_gadget.c3
-rw-r--r--drivers/usb/renesas_usbhs/pipe.c3
-rw-r--r--drivers/usb/renesas_usbhs/rcar2.c29
-rw-r--r--drivers/usb/renesas_usbhs/rcar3.c27
-rw-r--r--drivers/usb/roles/intel-xhci-usb-role-switch.c41
-rw-r--r--drivers/usb/serial/ark3116.c38
-rw-r--r--drivers/usb/serial/bus.c3
-rw-r--r--drivers/usb/serial/ch341.c2
-rw-r--r--drivers/usb/serial/console.c2
-rw-r--r--drivers/usb/serial/cp210x.c436
-rw-r--r--drivers/usb/serial/cyberjack.c17
-rw-r--r--drivers/usb/serial/cypress_m8.c7
-rw-r--r--drivers/usb/serial/digi_acceleport.c35
-rw-r--r--drivers/usb/serial/f81232.c36
-rw-r--r--drivers/usb/serial/f81534.c38
-rw-r--r--drivers/usb/serial/ftdi_sio.c633
-rw-r--r--drivers/usb/serial/ftdi_sio.h28
-rw-r--r--drivers/usb/serial/io_edgeport.c54
-rw-r--r--drivers/usb/serial/io_ti.c52
-rw-r--r--drivers/usb/serial/io_ti.h2
-rw-r--r--drivers/usb/serial/ir-usb.c2
-rw-r--r--drivers/usb/serial/iuu_phoenix.c9
-rw-r--r--drivers/usb/serial/keyspan_pda.c4
-rw-r--r--drivers/usb/serial/kl5kusb105.c1
-rw-r--r--drivers/usb/serial/kl5kusb105.h3
-rw-r--r--drivers/usb/serial/kobil_sct.c24
-rw-r--r--drivers/usb/serial/mos7720.c100
-rw-r--r--drivers/usb/serial/mos7840.c47
-rw-r--r--drivers/usb/serial/opticon.c43
-rw-r--r--drivers/usb/serial/option.c41
-rw-r--r--drivers/usb/serial/pl2303.c47
-rw-r--r--drivers/usb/serial/pl2303.h1
-rw-r--r--drivers/usb/serial/quatech2.c49
-rw-r--r--drivers/usb/serial/sierra.c13
-rw-r--r--drivers/usb/serial/ssu100.c44
-rw-r--r--drivers/usb/serial/symbolserial.c5
-rw-r--r--drivers/usb/serial/ti_usb_3410_5052.c85
-rw-r--r--drivers/usb/serial/usb-serial-simple.c3
-rw-r--r--drivers/usb/serial/usb-serial.c37
-rw-r--r--drivers/usb/serial/usb-wwan.h6
-rw-r--r--drivers/usb/serial/usb_wwan.c68
-rw-r--r--drivers/usb/serial/whiteheat.c42
-rw-r--r--drivers/usb/storage/Kconfig23
-rw-r--r--drivers/usb/storage/alauda.c2
-rw-r--r--drivers/usb/storage/ene_ub6250.c16
-rw-r--r--drivers/usb/storage/freecom.c4
-rw-r--r--drivers/usb/storage/isd200.c2
-rw-r--r--drivers/usb/storage/scsiglue.c9
-rw-r--r--drivers/usb/storage/sddr09.c4
-rw-r--r--drivers/usb/storage/sddr55.c6
-rw-r--r--drivers/usb/storage/uas.c27
-rw-r--r--drivers/usb/storage/unusual_devs.h16
-rw-r--r--drivers/usb/storage/unusual_realtek.h10
-rw-r--r--drivers/usb/storage/unusual_uas.h9
-rw-r--r--drivers/usb/typec/Kconfig30
-rw-r--r--drivers/usb/typec/Makefile7
-rw-r--r--drivers/usb/typec/altmodes/Kconfig14
-rw-r--r--drivers/usb/typec/altmodes/Makefile2
-rw-r--r--drivers/usb/typec/altmodes/displayport.c580
-rw-r--r--drivers/usb/typec/bus.c403
-rw-r--r--drivers/usb/typec/bus.h38
-rw-r--r--drivers/usb/typec/class.c579
-rw-r--r--drivers/usb/typec/fusb302/Kconfig7
-rw-r--r--drivers/usb/typec/fusb302/Makefile2
-rw-r--r--drivers/usb/typec/mux.c25
-rw-r--r--drivers/usb/typec/mux/pi3usb30532.c13
-rw-r--r--drivers/usb/typec/tcpm/Kconfig52
-rw-r--r--drivers/usb/typec/tcpm/Makefile7
-rw-r--r--drivers/usb/typec/tcpm/fusb302.c (renamed from drivers/usb/typec/fusb302/fusb302.c)204
-rw-r--r--drivers/usb/typec/tcpm/fusb302_reg.h (renamed from drivers/usb/typec/fusb302/fusb302_reg.h)0
-rw-r--r--drivers/usb/typec/tcpm/tcpci.c612
-rw-r--r--drivers/usb/typec/tcpm/tcpci.h139
-rw-r--r--drivers/usb/typec/tcpm/tcpci_rt1711h.c312
-rw-r--r--drivers/usb/typec/tcpm/tcpm.c (renamed from drivers/usb/typec/tcpm.c)1416
-rw-r--r--drivers/usb/typec/tcpm/wcove.c (renamed from drivers/usb/typec/typec_wcove.c)34
-rw-r--r--drivers/usb/typec/tps6598x.c11
-rw-r--r--drivers/usb/typec/ucsi/Kconfig10
-rw-r--r--drivers/usb/typec/ucsi/Makefile2
-rw-r--r--drivers/usb/typec/ucsi/ucsi.c13
-rw-r--r--drivers/usb/typec/ucsi/ucsi_acpi.c5
-rw-r--r--drivers/usb/typec/ucsi/ucsi_ccg.c307
-rw-r--r--drivers/usb/usb-skeleton.c10
-rw-r--r--drivers/usb/usbip/usbip_common.c2
-rw-r--r--drivers/usb/usbip/vhci_hcd.c57
-rw-r--r--drivers/usb/usbip/vhci_sysfs.c24
-rw-r--r--drivers/usb/usbip/vudc_dev.c2
-rw-r--r--drivers/usb/usbip/vudc_main.c10
-rw-r--r--drivers/usb/wusbcore/crypto.c16
-rw-r--r--drivers/usb/wusbcore/security.c2
-rw-r--r--drivers/usb/wusbcore/wa-rpipe.c5
-rw-r--r--drivers/usb/wusbcore/wa-xfer.c8
324 files changed, 19769 insertions, 7639 deletions
diff --git a/drivers/usb/chipidea/Kconfig b/drivers/usb/chipidea/Kconfig
index 785f0ed037f7..ee34e9046f7e 100644
--- a/drivers/usb/chipidea/Kconfig
+++ b/drivers/usb/chipidea/Kconfig
@@ -3,6 +3,7 @@ config USB_CHIPIDEA
depends on ((USB_EHCI_HCD && USB_GADGET) || (USB_EHCI_HCD && !USB_GADGET) || (!USB_EHCI_HCD && USB_GADGET)) && HAS_DMA
select EXTCON
select RESET_CONTROLLER
+ select USB_ULPI_BUS
help
Say Y here if your system has a dual role high speed USB
controller based on ChipIdea silicon IP. It supports:
@@ -38,12 +39,4 @@ config USB_CHIPIDEA_HOST
help
Say Y here to enable host controller functionality of the
ChipIdea driver.
-
-config USB_CHIPIDEA_ULPI
- bool "ChipIdea ULPI PHY support"
- depends on USB_ULPI_BUS=y || USB_ULPI_BUS=USB_CHIPIDEA
- help
- Say Y here if you have a ULPI PHY attached to your ChipIdea
- controller.
-
endif
diff --git a/drivers/usb/chipidea/Makefile b/drivers/usb/chipidea/Makefile
index e3d5e728fa53..12df94f78f72 100644
--- a/drivers/usb/chipidea/Makefile
+++ b/drivers/usb/chipidea/Makefile
@@ -1,11 +1,10 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc.o
-ci_hdrc-y := core.o otg.o debug.o
+ci_hdrc-y := core.o otg.o debug.o ulpi.o
ci_hdrc-$(CONFIG_USB_CHIPIDEA_UDC) += udc.o
ci_hdrc-$(CONFIG_USB_CHIPIDEA_HOST) += host.o
ci_hdrc-$(CONFIG_USB_OTG_FSM) += otg_fsm.o
-ci_hdrc-$(CONFIG_USB_CHIPIDEA_ULPI) += ulpi.o
# Glue/Bridge layers go here
diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h
index 98b7cb3d0064..6a2cc5cd0281 100644
--- a/drivers/usb/chipidea/ci.h
+++ b/drivers/usb/chipidea/ci.h
@@ -240,10 +240,8 @@ struct ci_hdrc {
struct ci_hdrc_platform_data *platdata;
int vbus_active;
-#ifdef CONFIG_USB_CHIPIDEA_ULPI
struct ulpi *ulpi;
struct ulpi_ops ulpi_ops;
-#endif
struct phy *phy;
/* old usb_phy interface */
struct usb_phy *usb_phy;
@@ -426,15 +424,9 @@ static inline bool ci_otg_is_fsm_mode(struct ci_hdrc *ci)
#endif
}
-#if IS_ENABLED(CONFIG_USB_CHIPIDEA_ULPI)
int ci_ulpi_init(struct ci_hdrc *ci);
void ci_ulpi_exit(struct ci_hdrc *ci);
int ci_ulpi_resume(struct ci_hdrc *ci);
-#else
-static inline int ci_ulpi_init(struct ci_hdrc *ci) { return 0; }
-static inline void ci_ulpi_exit(struct ci_hdrc *ci) { }
-static inline int ci_ulpi_resume(struct ci_hdrc *ci) { return 0; }
-#endif
u32 hw_read_intr_enable(struct ci_hdrc *ci);
@@ -450,7 +442,7 @@ void hw_phymode_configure(struct ci_hdrc *ci);
void ci_platform_configure(struct ci_hdrc *ci);
-int dbg_create_files(struct ci_hdrc *ci);
+void dbg_create_files(struct ci_hdrc *ci);
void dbg_remove_files(struct ci_hdrc *ci);
#endif /* __DRIVERS_USB_CHIPIDEA_CI_H */
diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c
index e431c5aafe35..09b37c0d075d 100644
--- a/drivers/usb/chipidea/ci_hdrc_imx.c
+++ b/drivers/usb/chipidea/ci_hdrc_imx.c
@@ -291,7 +291,8 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
pdata.usb_phy = data->phy;
- if (of_device_is_compatible(np, "fsl,imx53-usb") && pdata.usb_phy &&
+ if ((of_device_is_compatible(np, "fsl,imx53-usb") ||
+ of_device_is_compatible(np, "fsl,imx51-usb")) && pdata.usb_phy &&
of_usb_get_phy_mode(np) == USBPHY_INTERFACE_MODE_ULPI) {
pdata.flags |= CI_HDRC_OVERRIDE_PHY_CONTROL;
data->override_phy_control = true;
@@ -363,8 +364,7 @@ static void ci_hdrc_imx_shutdown(struct platform_device *pdev)
ci_hdrc_imx_remove(pdev);
}
-#ifdef CONFIG_PM
-static int imx_controller_suspend(struct device *dev)
+static int __maybe_unused imx_controller_suspend(struct device *dev)
{
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
@@ -376,7 +376,7 @@ static int imx_controller_suspend(struct device *dev)
return 0;
}
-static int imx_controller_resume(struct device *dev)
+static int __maybe_unused imx_controller_resume(struct device *dev)
{
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
int ret = 0;
@@ -407,8 +407,7 @@ clk_disable:
return ret;
}
-#ifdef CONFIG_PM_SLEEP
-static int ci_hdrc_imx_suspend(struct device *dev)
+static int __maybe_unused ci_hdrc_imx_suspend(struct device *dev)
{
int ret;
@@ -430,7 +429,7 @@ static int ci_hdrc_imx_suspend(struct device *dev)
return imx_controller_suspend(dev);
}
-static int ci_hdrc_imx_resume(struct device *dev)
+static int __maybe_unused ci_hdrc_imx_resume(struct device *dev)
{
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
int ret;
@@ -444,9 +443,8 @@ static int ci_hdrc_imx_resume(struct device *dev)
return ret;
}
-#endif /* CONFIG_PM_SLEEP */
-static int ci_hdrc_imx_runtime_suspend(struct device *dev)
+static int __maybe_unused ci_hdrc_imx_runtime_suspend(struct device *dev)
{
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
int ret;
@@ -465,13 +463,11 @@ static int ci_hdrc_imx_runtime_suspend(struct device *dev)
return imx_controller_suspend(dev);
}
-static int ci_hdrc_imx_runtime_resume(struct device *dev)
+static int __maybe_unused ci_hdrc_imx_runtime_resume(struct device *dev)
{
return imx_controller_resume(dev);
}
-#endif /* CONFIG_PM */
-
static const struct dev_pm_ops ci_hdrc_imx_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(ci_hdrc_imx_suspend, ci_hdrc_imx_resume)
SET_RUNTIME_PM_OPS(ci_hdrc_imx_runtime_suspend,
@@ -491,7 +487,7 @@ static struct platform_driver ci_hdrc_imx_driver = {
module_platform_driver(ci_hdrc_imx_driver);
MODULE_ALIAS("platform:imx-usb");
-MODULE_LICENSE("GPL v2");
+MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("CI HDRC i.MX USB binding");
MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>");
diff --git a/drivers/usb/chipidea/ci_hdrc_tegra.c b/drivers/usb/chipidea/ci_hdrc_tegra.c
index 7f4d2b6af37a..772851bee99b 100644
--- a/drivers/usb/chipidea/ci_hdrc_tegra.c
+++ b/drivers/usb/chipidea/ci_hdrc_tegra.c
@@ -33,11 +33,11 @@ static const struct tegra_udc_soc_info tegra30_udc_soc_info = {
};
static const struct tegra_udc_soc_info tegra114_udc_soc_info = {
- .flags = 0,
+ .flags = CI_HDRC_REQUIRES_ALIGNED_DMA,
};
static const struct tegra_udc_soc_info tegra124_udc_soc_info = {
- .flags = 0,
+ .flags = CI_HDRC_REQUIRES_ALIGNED_DMA,
};
static const struct of_device_id tegra_udc_of_match[] = {
diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c
index 33ae87fa3ff3..7bfcbb23c2a4 100644
--- a/drivers/usb/chipidea/core.c
+++ b/drivers/usb/chipidea/core.c
@@ -53,6 +53,7 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/pm_runtime.h>
+#include <linux/pinctrl/consumer.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/usb/otg.h>
@@ -723,6 +724,24 @@ static int ci_get_platdata(struct device *dev,
else
cable->connected = false;
}
+
+ platdata->pctl = devm_pinctrl_get(dev);
+ if (!IS_ERR(platdata->pctl)) {
+ struct pinctrl_state *p;
+
+ p = pinctrl_lookup_state(platdata->pctl, "default");
+ if (!IS_ERR(p))
+ platdata->pins_default = p;
+
+ p = pinctrl_lookup_state(platdata->pctl, "host");
+ if (!IS_ERR(p))
+ platdata->pins_host = p;
+
+ p = pinctrl_lookup_state(platdata->pctl, "device");
+ if (!IS_ERR(p))
+ platdata->pins_device = p;
+ }
+
return 0;
}
@@ -1062,9 +1081,7 @@ static int ci_hdrc_probe(struct platform_device *pdev)
ci_hdrc_otg_fsm_start(ci);
device_set_wakeup_capable(&pdev->dev, true);
- ret = dbg_create_files(ci);
- if (ret)
- goto stop;
+ dbg_create_files(ci);
ret = sysfs_create_group(&dev->kobj, &ci_attr_group);
if (ret)
diff --git a/drivers/usb/chipidea/debug.c b/drivers/usb/chipidea/debug.c
index ce648cb3ed94..fcc91a338875 100644
--- a/drivers/usb/chipidea/debug.c
+++ b/drivers/usb/chipidea/debug.c
@@ -340,54 +340,28 @@ DEFINE_SHOW_ATTRIBUTE(ci_registers);
*
* This function returns an error code
*/
-int dbg_create_files(struct ci_hdrc *ci)
+void dbg_create_files(struct ci_hdrc *ci)
{
- struct dentry *dent;
-
ci->debugfs = debugfs_create_dir(dev_name(ci->dev), NULL);
- if (!ci->debugfs)
- return -ENOMEM;
-
- dent = debugfs_create_file("device", S_IRUGO, ci->debugfs, ci,
- &ci_device_fops);
- if (!dent)
- goto err;
-
- dent = debugfs_create_file("port_test", S_IRUGO | S_IWUSR, ci->debugfs,
- ci, &ci_port_test_fops);
- if (!dent)
- goto err;
-
- dent = debugfs_create_file("qheads", S_IRUGO, ci->debugfs, ci,
- &ci_qheads_fops);
- if (!dent)
- goto err;
- dent = debugfs_create_file("requests", S_IRUGO, ci->debugfs, ci,
- &ci_requests_fops);
- if (!dent)
- goto err;
+ debugfs_create_file("device", S_IRUGO, ci->debugfs, ci,
+ &ci_device_fops);
+ debugfs_create_file("port_test", S_IRUGO | S_IWUSR, ci->debugfs, ci,
+ &ci_port_test_fops);
+ debugfs_create_file("qheads", S_IRUGO, ci->debugfs, ci,
+ &ci_qheads_fops);
+ debugfs_create_file("requests", S_IRUGO, ci->debugfs, ci,
+ &ci_requests_fops);
if (ci_otg_is_fsm_mode(ci)) {
- dent = debugfs_create_file("otg", S_IRUGO, ci->debugfs, ci,
- &ci_otg_fops);
- if (!dent)
- goto err;
+ debugfs_create_file("otg", S_IRUGO, ci->debugfs, ci,
+ &ci_otg_fops);
}
- dent = debugfs_create_file("role", S_IRUGO | S_IWUSR, ci->debugfs, ci,
- &ci_role_fops);
- if (!dent)
- goto err;
-
- dent = debugfs_create_file("registers", S_IRUGO, ci->debugfs, ci,
- &ci_registers_fops);
-
- if (dent)
- return 0;
-err:
- debugfs_remove_recursive(ci->debugfs);
- return -ENOMEM;
+ debugfs_create_file("role", S_IRUGO | S_IWUSR, ci->debugfs, ci,
+ &ci_role_fops);
+ debugfs_create_file("registers", S_IRUGO, ci->debugfs, ci,
+ &ci_registers_fops);
}
/**
diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c
index af45aa3222b5..d858a82c4f44 100644
--- a/drivers/usb/chipidea/host.c
+++ b/drivers/usb/chipidea/host.c
@@ -13,6 +13,7 @@
#include <linux/usb/hcd.h>
#include <linux/usb/chipidea.h>
#include <linux/regulator/consumer.h>
+#include <linux/pinctrl/consumer.h>
#include "../host/ehci.h"
@@ -124,8 +125,11 @@ static int host_start(struct ci_hdrc *ci)
hcd->power_budget = ci->platdata->power_budget;
hcd->tpl_support = ci->platdata->tpl_support;
- if (ci->phy || ci->usb_phy)
+ if (ci->phy || ci->usb_phy) {
hcd->skip_phy_initialization = 1;
+ if (ci->usb_phy)
+ hcd->usb_phy = ci->usb_phy;
+ }
ehci = hcd_to_ehci(hcd);
ehci->caps = ci->hw_bank.cap;
@@ -150,6 +154,10 @@ static int host_start(struct ci_hdrc *ci)
}
}
+ if (ci->platdata->pins_host)
+ pinctrl_select_state(ci->platdata->pctl,
+ ci->platdata->pins_host);
+
ret = usb_add_hcd(hcd, 0, 0);
if (ret) {
goto disable_reg;
@@ -194,6 +202,10 @@ static void host_stop(struct ci_hdrc *ci)
}
ci->hcd = NULL;
ci->otg.host = NULL;
+
+ if (ci->platdata->pins_host && ci->platdata->pins_default)
+ pinctrl_select_state(ci->platdata->pctl,
+ ci->platdata->pins_default);
}
diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c
index db4ceffcf2a6..f25d4827fd49 100644
--- a/drivers/usb/chipidea/otg.c
+++ b/drivers/usb/chipidea/otg.c
@@ -203,14 +203,17 @@ static void ci_otg_work(struct work_struct *work)
}
pm_runtime_get_sync(ci->dev);
+
if (ci->id_event) {
ci->id_event = false;
ci_handle_id_switch(ci);
- } else if (ci->b_sess_valid_event) {
+ }
+
+ if (ci->b_sess_valid_event) {
ci->b_sess_valid_event = false;
ci_handle_vbus_change(ci);
- } else
- dev_err(ci->dev, "unexpected event occurs at %s\n", __func__);
+ }
+
pm_runtime_put_sync(ci->dev);
enable_irq(ci->irq);
diff --git a/drivers/usb/chipidea/otg.h b/drivers/usb/chipidea/otg.h
index 7e7428e48bfa..4f8b8179ec96 100644
--- a/drivers/usb/chipidea/otg.h
+++ b/drivers/usb/chipidea/otg.h
@@ -17,7 +17,8 @@ void ci_handle_vbus_change(struct ci_hdrc *ci);
static inline void ci_otg_queue_work(struct ci_hdrc *ci)
{
disable_irq_nosync(ci->irq);
- queue_work(ci->wq, &ci->work);
+ if (queue_work(ci->wq, &ci->work) == false)
+ enable_irq(ci->irq);
}
#endif /* __DRIVERS_USB_CHIPIDEA_OTG_H */
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
index 9852ec5e6e01..829e947cabf5 100644
--- a/drivers/usb/chipidea/udc.c
+++ b/drivers/usb/chipidea/udc.c
@@ -15,6 +15,7 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/pm_runtime.h>
+#include <linux/pinctrl/consumer.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/usb/otg-fsm.h>
@@ -1965,6 +1966,10 @@ void ci_hdrc_gadget_destroy(struct ci_hdrc *ci)
static int udc_id_switch_for_device(struct ci_hdrc *ci)
{
+ if (ci->platdata->pins_device)
+ pinctrl_select_state(ci->platdata->pctl,
+ ci->platdata->pins_device);
+
if (ci->is_otg)
/* Clear and enable BSV irq */
hw_write_otgsc(ci, OTGSC_BSVIS | OTGSC_BSVIE,
@@ -1983,6 +1988,10 @@ static void udc_id_switch_for_host(struct ci_hdrc *ci)
hw_write_otgsc(ci, OTGSC_BSVIE | OTGSC_BSVIS, OTGSC_BSVIS);
ci->vbus_active = 0;
+
+ if (ci->platdata->pins_device && ci->platdata->pins_default)
+ pinctrl_select_state(ci->platdata->pctl,
+ ci->platdata->pins_default);
}
/**
diff --git a/drivers/usb/chipidea/ulpi.c b/drivers/usb/chipidea/ulpi.c
index 6da42dcd2888..dfec07e8ae1d 100644
--- a/drivers/usb/chipidea/ulpi.c
+++ b/drivers/usb/chipidea/ulpi.c
@@ -95,6 +95,9 @@ int ci_ulpi_resume(struct ci_hdrc *ci)
{
int cnt = 100000;
+ if (ci->platdata->phy_mode != USBPHY_INTERFACE_MODE_ULPI)
+ return 0;
+
while (cnt-- > 0) {
if (hw_read(ci, OP_ULPI_VIEWPORT, ULPI_SYNC_STATE))
return 0;
diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c
index 34ad5bf8acd8..def80ff547e4 100644
--- a/drivers/usb/chipidea/usbmisc_imx.c
+++ b/drivers/usb/chipidea/usbmisc_imx.c
@@ -343,6 +343,8 @@ static int usbmisc_imx6q_init(struct imx_usbmisc_data *data)
} else if (data->oc_polarity == 1) {
/* High active */
reg &= ~(MX6_BM_OVER_CUR_DIS | MX6_BM_OVER_CUR_POLARITY);
+ } else {
+ reg &= ~(MX6_BM_OVER_CUR_DIS);
}
writel(reg, usbmisc->base + data->index * 4);
@@ -633,6 +635,6 @@ static struct platform_driver usbmisc_imx_driver = {
module_platform_driver(usbmisc_imx_driver);
MODULE_ALIAS("platform:usbmisc-imx");
-MODULE_LICENSE("GPL v2");
+MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("driver for imx usb non-core registers");
MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>");
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index 7b366a6c0b49..1b68fed464cb 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -276,6 +276,7 @@ static void acm_process_notification(struct acm *acm, unsigned char *buf)
{
int newctrl;
int difference;
+ unsigned long flags;
struct usb_cdc_notification *dr = (struct usb_cdc_notification *)buf;
unsigned char *data = buf + sizeof(struct usb_cdc_notification);
@@ -303,25 +304,25 @@ static void acm_process_notification(struct acm *acm, unsigned char *buf)
}
difference = acm->ctrlin ^ newctrl;
- spin_lock(&acm->read_lock);
+ spin_lock_irqsave(&acm->read_lock, flags);
acm->ctrlin = newctrl;
acm->oldcount = acm->iocount;
if (difference & ACM_CTRL_DSR)
acm->iocount.dsr++;
- if (difference & ACM_CTRL_BRK)
- acm->iocount.brk++;
- if (difference & ACM_CTRL_RI)
- acm->iocount.rng++;
if (difference & ACM_CTRL_DCD)
acm->iocount.dcd++;
- if (difference & ACM_CTRL_FRAMING)
+ if (newctrl & ACM_CTRL_BRK)
+ acm->iocount.brk++;
+ if (newctrl & ACM_CTRL_RI)
+ acm->iocount.rng++;
+ if (newctrl & ACM_CTRL_FRAMING)
acm->iocount.frame++;
- if (difference & ACM_CTRL_PARITY)
+ if (newctrl & ACM_CTRL_PARITY)
acm->iocount.parity++;
- if (difference & ACM_CTRL_OVERRUN)
+ if (newctrl & ACM_CTRL_OVERRUN)
acm->iocount.overrun++;
- spin_unlock(&acm->read_lock);
+ spin_unlock_irqrestore(&acm->read_lock, flags);
if (difference)
wake_up_all(&acm->wioctl);
@@ -354,7 +355,6 @@ static void acm_ctrl_irq(struct urb *urb)
case -ENOENT:
case -ESHUTDOWN:
/* this urb is terminated, clean up */
- acm->nb_index = 0;
dev_dbg(&acm->control->dev,
"%s - urb shutting down with status: %d\n",
__func__, status);
@@ -779,20 +779,9 @@ static int acm_tty_write(struct tty_struct *tty,
}
if (acm->susp_count) {
- if (acm->putbuffer) {
- /* now to preserve order */
- usb_anchor_urb(acm->putbuffer->urb, &acm->delayed);
- acm->putbuffer = NULL;
- }
usb_anchor_urb(wb->urb, &acm->delayed);
spin_unlock_irqrestore(&acm->write_lock, flags);
return count;
- } else {
- if (acm->putbuffer) {
- /* at this point there is no good way to handle errors */
- acm_start_wb(acm, acm->putbuffer);
- acm->putbuffer = NULL;
- }
}
stat = acm_start_wb(acm, wb);
@@ -803,66 +792,6 @@ static int acm_tty_write(struct tty_struct *tty,
return count;
}
-static void acm_tty_flush_chars(struct tty_struct *tty)
-{
- struct acm *acm = tty->driver_data;
- struct acm_wb *cur;
- int err;
- unsigned long flags;
-
- spin_lock_irqsave(&acm->write_lock, flags);
-
- cur = acm->putbuffer;
- if (!cur) /* nothing to do */
- goto out;
-
- acm->putbuffer = NULL;
- err = usb_autopm_get_interface_async(acm->control);
- if (err < 0) {
- cur->use = 0;
- acm->putbuffer = cur;
- goto out;
- }
-
- if (acm->susp_count)
- usb_anchor_urb(cur->urb, &acm->delayed);
- else
- acm_start_wb(acm, cur);
-out:
- spin_unlock_irqrestore(&acm->write_lock, flags);
- return;
-}
-
-static int acm_tty_put_char(struct tty_struct *tty, unsigned char ch)
-{
- struct acm *acm = tty->driver_data;
- struct acm_wb *cur;
- int wbn;
- unsigned long flags;
-
-overflow:
- cur = acm->putbuffer;
- if (!cur) {
- spin_lock_irqsave(&acm->write_lock, flags);
- wbn = acm_wb_alloc(acm);
- if (wbn >= 0) {
- cur = &acm->wb[wbn];
- acm->putbuffer = cur;
- }
- spin_unlock_irqrestore(&acm->write_lock, flags);
- if (!cur)
- return 0;
- }
-
- if (cur->len == acm->writesize) {
- acm_tty_flush_chars(tty);
- goto overflow;
- }
-
- cur->buf[cur->len++] = ch;
- return 1;
-}
-
static int acm_tty_write_room(struct tty_struct *tty)
{
struct acm *acm = tty->driver_data;
@@ -955,37 +884,28 @@ static int acm_tty_tiocmset(struct tty_struct *tty,
return acm_set_control(acm, acm->ctrlout = newctrl);
}
-static int get_serial_info(struct acm *acm, struct serial_struct __user *info)
+static int get_serial_info(struct tty_struct *tty, struct serial_struct *ss)
{
- struct serial_struct tmp;
+ struct acm *acm = tty->driver_data;
- memset(&tmp, 0, sizeof(tmp));
- tmp.xmit_fifo_size = acm->writesize;
- tmp.baud_base = le32_to_cpu(acm->line.dwDTERate);
- tmp.close_delay = acm->port.close_delay / 10;
- tmp.closing_wait = acm->port.closing_wait == ASYNC_CLOSING_WAIT_NONE ?
+ ss->xmit_fifo_size = acm->writesize;
+ ss->baud_base = le32_to_cpu(acm->line.dwDTERate);
+ ss->close_delay = acm->port.close_delay / 10;
+ ss->closing_wait = acm->port.closing_wait == ASYNC_CLOSING_WAIT_NONE ?
ASYNC_CLOSING_WAIT_NONE :
acm->port.closing_wait / 10;
-
- if (copy_to_user(info, &tmp, sizeof(tmp)))
- return -EFAULT;
- else
- return 0;
+ return 0;
}
-static int set_serial_info(struct acm *acm,
- struct serial_struct __user *newinfo)
+static int set_serial_info(struct tty_struct *tty, struct serial_struct *ss)
{
- struct serial_struct new_serial;
+ struct acm *acm = tty->driver_data;
unsigned int closing_wait, close_delay;
int retval = 0;
- if (copy_from_user(&new_serial, newinfo, sizeof(new_serial)))
- return -EFAULT;
-
- close_delay = new_serial.close_delay * 10;
- closing_wait = new_serial.closing_wait == ASYNC_CLOSING_WAIT_NONE ?
- ASYNC_CLOSING_WAIT_NONE : new_serial.closing_wait * 10;
+ close_delay = ss->close_delay * 10;
+ closing_wait = ss->closing_wait == ASYNC_CLOSING_WAIT_NONE ?
+ ASYNC_CLOSING_WAIT_NONE : ss->closing_wait * 10;
mutex_lock(&acm->port.mutex);
@@ -1070,12 +990,6 @@ static int acm_tty_ioctl(struct tty_struct *tty,
int rv = -ENOIOCTLCMD;
switch (cmd) {
- case TIOCGSERIAL: /* gets serial port data */
- rv = get_serial_info(acm, (struct serial_struct __user *) arg);
- break;
- case TIOCSSERIAL:
- rv = set_serial_info(acm, (struct serial_struct __user *) arg);
- break;
case TIOCMIWAIT:
rv = usb_autopm_get_interface(acm->control);
if (rv < 0) {
@@ -1378,6 +1292,9 @@ made_compressed_probe:
if (acm == NULL)
goto alloc_fail;
+ tty_port_init(&acm->port);
+ acm->port.ops = &acm_port_ops;
+
minor = acm_alloc_minor(acm);
if (minor < 0)
goto alloc_fail1;
@@ -1413,22 +1330,20 @@ made_compressed_probe:
acm->out = usb_sndintpipe(usb_dev, epwrite->bEndpointAddress);
else
acm->out = usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress);
- tty_port_init(&acm->port);
- acm->port.ops = &acm_port_ops;
init_usb_anchor(&acm->delayed);
acm->quirks = quirks;
buf = usb_alloc_coherent(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
if (!buf)
- goto alloc_fail2;
+ goto alloc_fail1;
acm->ctrl_buffer = buf;
if (acm_write_buffers_alloc(acm) < 0)
- goto alloc_fail4;
+ goto alloc_fail2;
acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL);
if (!acm->ctrlurb)
- goto alloc_fail5;
+ goto alloc_fail3;
for (i = 0; i < num_rx_buf; i++) {
struct acm_rb *rb = &(acm->read_buffers[i]);
@@ -1437,13 +1352,13 @@ made_compressed_probe:
rb->base = usb_alloc_coherent(acm->dev, readsize, GFP_KERNEL,
&rb->dma);
if (!rb->base)
- goto alloc_fail6;
+ goto alloc_fail4;
rb->index = i;
rb->instance = acm;
urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb)
- goto alloc_fail6;
+ goto alloc_fail4;
urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
urb->transfer_dma = rb->dma;
@@ -1465,7 +1380,7 @@ made_compressed_probe:
snd->urb = usb_alloc_urb(0, GFP_KERNEL);
if (snd->urb == NULL)
- goto alloc_fail7;
+ goto alloc_fail5;
if (usb_endpoint_xfer_int(epwrite))
usb_fill_int_urb(snd->urb, usb_dev, acm->out,
@@ -1483,7 +1398,7 @@ made_compressed_probe:
i = device_create_file(&intf->dev, &dev_attr_bmCapabilities);
if (i < 0)
- goto alloc_fail7;
+ goto alloc_fail5;
if (h.usb_cdc_country_functional_desc) { /* export the country data */
struct usb_cdc_country_functional_desc * cfd =
@@ -1542,7 +1457,7 @@ skip_countries:
&control_interface->dev);
if (IS_ERR(tty_dev)) {
rv = PTR_ERR(tty_dev);
- goto alloc_fail8;
+ goto alloc_fail6;
}
if (quirks & CLEAR_HALT_CONDITIONS) {
@@ -1551,7 +1466,7 @@ skip_countries:
}
return 0;
-alloc_fail8:
+alloc_fail6:
if (acm->country_codes) {
device_remove_file(&acm->control->dev,
&dev_attr_wCountryCodes);
@@ -1560,23 +1475,21 @@ alloc_fail8:
kfree(acm->country_codes);
}
device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities);
-alloc_fail7:
+alloc_fail5:
usb_set_intfdata(intf, NULL);
for (i = 0; i < ACM_NW; i++)
usb_free_urb(acm->wb[i].urb);
-alloc_fail6:
+alloc_fail4:
for (i = 0; i < num_rx_buf; i++)
usb_free_urb(acm->read_urbs[i]);
acm_read_buffers_free(acm);
usb_free_urb(acm->ctrlurb);
-alloc_fail5:
+alloc_fail3:
acm_write_buffers_free(acm);
-alloc_fail4:
- usb_free_coherent(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
alloc_fail2:
- acm_release_minor(acm);
+ usb_free_coherent(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
alloc_fail1:
- kfree(acm);
+ tty_port_put(&acm->port);
alloc_fail:
return rv;
}
@@ -1585,6 +1498,7 @@ 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 */
if (!acm)
@@ -1615,6 +1529,11 @@ static void acm_disconnect(struct usb_interface *intf)
tty_unregister_device(acm_tty_driver, acm->minor);
+ usb_free_urb(acm->ctrlurb);
+ for (i = 0; i < ACM_NW; i++)
+ usb_free_urb(acm->wb[i].urb);
+ for (i = 0; i < acm->rx_buflimit; i++)
+ usb_free_urb(acm->read_urbs[i]);
acm_write_buffers_free(acm);
usb_free_coherent(acm->dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
acm_read_buffers_free(acm);
@@ -1707,6 +1626,7 @@ static int acm_pre_reset(struct usb_interface *intf)
struct acm *acm = usb_get_intfdata(intf);
clear_bit(EVENT_RX_STALL, &acm->flags);
+ acm->nb_index = 0; /* pending control transfers are lost */
return 0;
}
@@ -1758,6 +1678,9 @@ static const struct usb_device_id acm_ids[] = {
{ USB_DEVICE(0x11ca, 0x0201), /* VeriFone Mx870 Gadget Serial */
.driver_info = SINGLE_RX_URB,
},
+ { USB_DEVICE(0x1965, 0x0018), /* Uniden UBC125XLT */
+ .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
+ },
{ USB_DEVICE(0x22b8, 0x7000), /* Motorola Q Phone */
.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
},
@@ -1773,6 +1696,9 @@ static const struct usb_device_id acm_ids[] = {
{ USB_DEVICE(0x0572, 0x1328), /* Shiro / Aztech USB MODEM UM-3100 */
.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
},
+ { USB_DEVICE(0x0572, 0x1349), /* Hiro (Conexant) USB MODEM H50228 */
+ .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
+ },
{ USB_DEVICE(0x20df, 0x0001), /* Simtec Electronics Entropy Key */
.driver_info = QUIRK_CONTROL_LINE_STATE, },
{ USB_DEVICE(0x2184, 0x001c) }, /* GW Instek AFG-2225 */
@@ -1828,6 +1754,9 @@ static const struct usb_device_id acm_ids[] = {
{ USB_DEVICE(0x09d8, 0x0320), /* Elatec GmbH TWN3 */
.driver_info = NO_UNION_NORMAL, /* has misplaced union descriptor */
},
+ { USB_DEVICE(0x0ca6, 0xa050), /* Castles VEGA3000 */
+ .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
+ },
{ USB_DEVICE(0x2912, 0x0001), /* ATOL FPrint */
.driver_info = CLEAR_HALT_CONDITIONS,
@@ -1981,8 +1910,6 @@ static const struct tty_operations acm_ops = {
.cleanup = acm_tty_cleanup,
.hangup = acm_tty_hangup,
.write = acm_tty_write,
- .put_char = acm_tty_put_char,
- .flush_chars = acm_tty_flush_chars,
.write_room = acm_tty_write_room,
.ioctl = acm_tty_ioctl,
.throttle = acm_tty_throttle,
@@ -1992,6 +1919,8 @@ static const struct tty_operations acm_ops = {
.set_termios = acm_tty_set_termios,
.tiocmget = acm_tty_tiocmget,
.tiocmset = acm_tty_tiocmset,
+ .get_serial = get_serial_info,
+ .set_serial = set_serial_info,
.get_icount = acm_tty_get_icount,
};
diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h
index eacc116e83da..ca06b20d7af9 100644
--- a/drivers/usb/class/cdc-acm.h
+++ b/drivers/usb/class/cdc-acm.h
@@ -96,7 +96,6 @@ struct acm {
unsigned long read_urbs_free;
struct urb *read_urbs[ACM_NR];
struct acm_rb read_buffers[ACM_NR];
- struct acm_wb *putbuffer; /* for acm_tty_put_char() */
int rx_buflimit;
spinlock_t read_lock;
u8 *notification_buffer; /* to reassemble fragmented notifications */
diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c
index a0d284ef3f40..bec581fb7c63 100644
--- a/drivers/usb/class/cdc-wdm.c
+++ b/drivers/usb/class/cdc-wdm.c
@@ -96,6 +96,7 @@ struct wdm_device {
struct mutex rlock;
wait_queue_head_t wait;
struct work_struct rxwork;
+ struct work_struct service_outs_intr;
int werr;
int rerr;
int resp_count;
@@ -141,26 +142,26 @@ found:
static void wdm_out_callback(struct urb *urb)
{
struct wdm_device *desc;
+ unsigned long flags;
+
desc = urb->context;
- spin_lock(&desc->iuspin);
+ spin_lock_irqsave(&desc->iuspin, flags);
desc->werr = urb->status;
- spin_unlock(&desc->iuspin);
+ spin_unlock_irqrestore(&desc->iuspin, flags);
kfree(desc->outbuf);
desc->outbuf = NULL;
clear_bit(WDM_IN_USE, &desc->flags);
wake_up(&desc->wait);
}
-/* forward declaration */
-static int service_outstanding_interrupt(struct wdm_device *desc);
-
static void wdm_in_callback(struct urb *urb)
{
+ unsigned long flags;
struct wdm_device *desc = urb->context;
int status = urb->status;
int length = urb->actual_length;
- spin_lock(&desc->iuspin);
+ spin_lock_irqsave(&desc->iuspin, flags);
clear_bit(WDM_RESPONDING, &desc->flags);
if (status) {
@@ -209,8 +210,6 @@ static void wdm_in_callback(struct urb *urb)
}
}
skip_error:
- set_bit(WDM_READ, &desc->flags);
- wake_up(&desc->wait);
if (desc->rerr) {
/*
@@ -219,14 +218,17 @@ skip_error:
* We should respond to further attempts from the device to send
* data, so that we can get unstuck.
*/
- service_outstanding_interrupt(desc);
+ schedule_work(&desc->service_outs_intr);
+ } else {
+ set_bit(WDM_READ, &desc->flags);
+ wake_up(&desc->wait);
}
-
- spin_unlock(&desc->iuspin);
+ spin_unlock_irqrestore(&desc->iuspin, flags);
}
static void wdm_int_callback(struct urb *urb)
{
+ unsigned long flags;
int rv = 0;
int responding;
int status = urb->status;
@@ -286,7 +288,7 @@ static void wdm_int_callback(struct urb *urb)
goto exit;
}
- spin_lock(&desc->iuspin);
+ spin_lock_irqsave(&desc->iuspin, flags);
responding = test_and_set_bit(WDM_RESPONDING, &desc->flags);
if (!desc->resp_count++ && !responding
&& !test_bit(WDM_DISCONNECTING, &desc->flags)
@@ -294,7 +296,7 @@ static void wdm_int_callback(struct urb *urb)
rv = usb_submit_urb(desc->response, GFP_ATOMIC);
dev_dbg(&desc->intf->dev, "submit response URB %d\n", rv);
}
- spin_unlock(&desc->iuspin);
+ spin_unlock_irqrestore(&desc->iuspin, flags);
if (rv < 0) {
clear_bit(WDM_RESPONDING, &desc->flags);
if (rv == -EPERM)
@@ -758,6 +760,21 @@ static void wdm_rxwork(struct work_struct *work)
}
}
+static void service_interrupt_work(struct work_struct *work)
+{
+ struct wdm_device *desc;
+
+ desc = container_of(work, struct wdm_device, service_outs_intr);
+
+ spin_lock_irq(&desc->iuspin);
+ service_outstanding_interrupt(desc);
+ if (!desc->resp_count) {
+ set_bit(WDM_READ, &desc->flags);
+ wake_up(&desc->wait);
+ }
+ spin_unlock_irq(&desc->iuspin);
+}
+
/* --- hotplug --- */
static int wdm_create(struct usb_interface *intf, struct usb_endpoint_descriptor *ep,
@@ -779,6 +796,7 @@ static int wdm_create(struct usb_interface *intf, struct usb_endpoint_descriptor
desc->inum = cpu_to_le16((u16)intf->cur_altsetting->desc.bInterfaceNumber);
desc->intf = intf;
INIT_WORK(&desc->rxwork, wdm_rxwork);
+ INIT_WORK(&desc->service_outs_intr, service_interrupt_work);
rv = -EINVAL;
if (!usb_endpoint_is_int_in(ep))
@@ -964,6 +982,7 @@ static void wdm_disconnect(struct usb_interface *intf)
mutex_lock(&desc->wlock);
kill_urbs(desc);
cancel_work_sync(&desc->rxwork);
+ cancel_work_sync(&desc->service_outs_intr);
mutex_unlock(&desc->wlock);
mutex_unlock(&desc->rlock);
@@ -1006,6 +1025,7 @@ static int wdm_suspend(struct usb_interface *intf, pm_message_t message)
/* callback submits work - order is essential */
kill_urbs(desc);
cancel_work_sync(&desc->rxwork);
+ cancel_work_sync(&desc->service_outs_intr);
}
if (!PMSG_IS_AUTO(message)) {
mutex_unlock(&desc->wlock);
@@ -1065,6 +1085,7 @@ static int wdm_pre_reset(struct usb_interface *intf)
mutex_lock(&desc->wlock);
kill_urbs(desc);
cancel_work_sync(&desc->rxwork);
+ cancel_work_sync(&desc->service_outs_intr);
return 0;
}
diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c
index d058d7a31e7c..407a7a6198a2 100644
--- a/drivers/usb/class/usblp.c
+++ b/drivers/usb/class/usblp.c
@@ -292,6 +292,7 @@ static void usblp_bulk_read(struct urb *urb)
{
struct usblp *usblp = urb->context;
int status = urb->status;
+ unsigned long flags;
if (usblp->present && usblp->used) {
if (status)
@@ -299,14 +300,14 @@ static void usblp_bulk_read(struct urb *urb)
"nonzero read bulk status received: %d\n",
usblp->minor, status);
}
- spin_lock(&usblp->lock);
+ spin_lock_irqsave(&usblp->lock, flags);
if (status < 0)
usblp->rstatus = status;
else
usblp->rstatus = urb->actual_length;
usblp->rcomplete = 1;
wake_up(&usblp->rwait);
- spin_unlock(&usblp->lock);
+ spin_unlock_irqrestore(&usblp->lock, flags);
usb_free_urb(urb);
}
@@ -315,6 +316,7 @@ static void usblp_bulk_write(struct urb *urb)
{
struct usblp *usblp = urb->context;
int status = urb->status;
+ unsigned long flags;
if (usblp->present && usblp->used) {
if (status)
@@ -322,7 +324,7 @@ static void usblp_bulk_write(struct urb *urb)
"nonzero write bulk status received: %d\n",
usblp->minor, status);
}
- spin_lock(&usblp->lock);
+ spin_lock_irqsave(&usblp->lock, flags);
if (status < 0)
usblp->wstatus = status;
else
@@ -330,7 +332,7 @@ static void usblp_bulk_write(struct urb *urb)
usblp->no_paper = 0;
usblp->wcomplete = 1;
wake_up(&usblp->wwait);
- spin_unlock(&usblp->lock);
+ spin_unlock_irqrestore(&usblp->lock, flags);
usb_free_urb(urb);
}
diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c
index bdb1de0c0cef..4942122b2346 100644
--- a/drivers/usb/class/usbtmc.c
+++ b/drivers/usb/class/usbtmc.c
@@ -5,6 +5,7 @@
* Copyright (C) 2007 Stefan Kopp, Gechingen, Germany
* Copyright (C) 2008 Novell, Inc.
* Copyright (C) 2008 Greg Kroah-Hartman <gregkh@suse.de>
+ * Copyright (C) 2018 IVI Foundation, Inc.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -18,22 +19,27 @@
#include <linux/poll.h>
#include <linux/mutex.h>
#include <linux/usb.h>
+#include <linux/compat.h>
#include <linux/usb/tmc.h>
+/* Increment API VERSION when changing tmc.h with new flags or ioctls
+ * or when changing a significant behavior of the driver.
+ */
+#define USBTMC_API_VERSION (2)
-#define RIGOL 1
#define USBTMC_HEADER_SIZE 12
#define USBTMC_MINOR_BASE 176
-/*
- * Size of driver internal IO buffer. Must be multiple of 4 and at least as
- * large as wMaxPacketSize (which is usually 512 bytes).
- */
-#define USBTMC_SIZE_IOBUFFER 2048
-
+/* Minimum USB timeout (in milliseconds) */
+#define USBTMC_MIN_TIMEOUT 100
/* Default USB timeout (in milliseconds) */
#define USBTMC_TIMEOUT 5000
+/* Max number of urbs used in write transfers */
+#define MAX_URBS_IN_FLIGHT 16
+/* I/O buffer size used in generic read/write functions */
+#define USBTMC_BUFSIZE (4096)
+
/*
* Maximum number of read cycles to empty bulk in endpoint during CLEAR and
* ABORT_BULK_IN requests. Ends the loop if (for whatever reason) a short
@@ -68,6 +74,7 @@ struct usbtmc_device_data {
const struct usb_device_id *id;
struct usb_device *usb_dev;
struct usb_interface *intf;
+ struct list_head file_list;
unsigned int bulk_in;
unsigned int bulk_out;
@@ -76,6 +83,9 @@ struct usbtmc_device_data {
u8 bTag_last_write; /* needed for abort */
u8 bTag_last_read; /* needed for abort */
+ /* packet size of IN bulk */
+ u16 wMaxPacketSize;
+
/* data for interrupt in endpoint handling */
u8 bNotify1;
u8 bNotify2;
@@ -88,18 +98,10 @@ struct usbtmc_device_data {
int iin_interval;
struct urb *iin_urb;
u16 iin_wMaxPacketSize;
- atomic_t srq_asserted;
/* coalesced usb488_caps from usbtmc_dev_capabilities */
__u8 usb488_caps;
- u8 rigol_quirk;
-
- /* attributes from the USB TMC spec for this device */
- u8 TermChar;
- bool TermCharEnabled;
- bool auto_abort;
-
bool zombie; /* fd of disconnected device */
struct usbtmc_dev_capabilities capabilities;
@@ -107,22 +109,48 @@ struct usbtmc_device_data {
struct mutex io_mutex; /* only one i/o function running at a time */
wait_queue_head_t waitq;
struct fasync_struct *fasync;
+ spinlock_t dev_lock; /* lock for file_list */
};
#define to_usbtmc_data(d) container_of(d, struct usbtmc_device_data, kref)
-struct usbtmc_ID_rigol_quirk {
- __u16 idVendor;
- __u16 idProduct;
-};
+/*
+ * This structure holds private data for each USBTMC file handle.
+ */
+struct usbtmc_file_data {
+ struct usbtmc_device_data *data;
+ struct list_head file_elem;
+
+ u32 timeout;
+ u8 srq_byte;
+ atomic_t srq_asserted;
+ atomic_t closing;
+ u8 bmTransferAttributes; /* member of DEV_DEP_MSG_IN */
+
+ u8 eom_val;
+ u8 term_char;
+ bool term_char_enabled;
+ bool auto_abort;
+
+ spinlock_t err_lock; /* lock for errors */
-static const struct usbtmc_ID_rigol_quirk usbtmc_id_quirk[] = {
- { 0x1ab1, 0x0588 },
- { 0x1ab1, 0x04b0 },
- { 0, 0 }
+ struct usb_anchor submitted;
+
+ /* data for generic_write */
+ struct semaphore limit_write_sem;
+ u32 out_transfer_size;
+ int out_status;
+
+ /* data for generic_read */
+ u32 in_transfer_size;
+ int in_status;
+ int in_urbs_used;
+ struct usb_anchor in_anchor;
+ wait_queue_head_t wait_bulk_in;
};
/* Forward declarations */
static struct usb_driver usbtmc_driver;
+static void usbtmc_draw_down(struct usbtmc_file_data *file_data);
static void usbtmc_delete(struct kref *kref)
{
@@ -136,7 +164,7 @@ static int usbtmc_open(struct inode *inode, struct file *filp)
{
struct usb_interface *intf;
struct usbtmc_device_data *data;
- int retval = 0;
+ struct usbtmc_file_data *file_data;
intf = usb_find_interface(&usbtmc_driver, iminor(inode));
if (!intf) {
@@ -144,36 +172,107 @@ static int usbtmc_open(struct inode *inode, struct file *filp)
return -ENODEV;
}
+ file_data = kzalloc(sizeof(*file_data), GFP_KERNEL);
+ if (!file_data)
+ return -ENOMEM;
+
+ spin_lock_init(&file_data->err_lock);
+ sema_init(&file_data->limit_write_sem, MAX_URBS_IN_FLIGHT);
+ init_usb_anchor(&file_data->submitted);
+ init_usb_anchor(&file_data->in_anchor);
+ init_waitqueue_head(&file_data->wait_bulk_in);
+
data = usb_get_intfdata(intf);
/* Protect reference to data from file structure until release */
kref_get(&data->kref);
+ mutex_lock(&data->io_mutex);
+ file_data->data = data;
+
+ atomic_set(&file_data->closing, 0);
+
+ file_data->timeout = USBTMC_TIMEOUT;
+ file_data->term_char = '\n';
+ file_data->term_char_enabled = 0;
+ file_data->auto_abort = 0;
+ file_data->eom_val = 1;
+
+ INIT_LIST_HEAD(&file_data->file_elem);
+ spin_lock_irq(&data->dev_lock);
+ list_add_tail(&file_data->file_elem, &data->file_list);
+ spin_unlock_irq(&data->dev_lock);
+ mutex_unlock(&data->io_mutex);
+
/* Store pointer in file structure's private data field */
- filp->private_data = data;
+ filp->private_data = file_data;
- return retval;
+ return 0;
+}
+
+/*
+ * usbtmc_flush - called before file handle is closed
+ */
+static int usbtmc_flush(struct file *file, fl_owner_t id)
+{
+ struct usbtmc_file_data *file_data;
+ struct usbtmc_device_data *data;
+
+ file_data = file->private_data;
+ if (file_data == NULL)
+ return -ENODEV;
+
+ atomic_set(&file_data->closing, 1);
+ data = file_data->data;
+
+ /* wait for io to stop */
+ mutex_lock(&data->io_mutex);
+
+ usbtmc_draw_down(file_data);
+
+ spin_lock_irq(&file_data->err_lock);
+ file_data->in_status = 0;
+ file_data->in_transfer_size = 0;
+ file_data->in_urbs_used = 0;
+ file_data->out_status = 0;
+ file_data->out_transfer_size = 0;
+ spin_unlock_irq(&file_data->err_lock);
+
+ wake_up_interruptible_all(&data->waitq);
+ mutex_unlock(&data->io_mutex);
+
+ return 0;
}
static int usbtmc_release(struct inode *inode, struct file *file)
{
- struct usbtmc_device_data *data = file->private_data;
+ struct usbtmc_file_data *file_data = file->private_data;
- kref_put(&data->kref, usbtmc_delete);
+ /* prevent IO _AND_ usbtmc_interrupt */
+ mutex_lock(&file_data->data->io_mutex);
+ spin_lock_irq(&file_data->data->dev_lock);
+
+ list_del(&file_data->file_elem);
+
+ spin_unlock_irq(&file_data->data->dev_lock);
+ mutex_unlock(&file_data->data->io_mutex);
+
+ kref_put(&file_data->data->kref, usbtmc_delete);
+ file_data->data = NULL;
+ kfree(file_data);
return 0;
}
-static int usbtmc_ioctl_abort_bulk_in(struct usbtmc_device_data *data)
+static int usbtmc_ioctl_abort_bulk_in_tag(struct usbtmc_device_data *data,
+ u8 tag)
{
u8 *buffer;
struct device *dev;
int rv;
int n;
int actual;
- struct usb_host_interface *current_setting;
- int max_size;
dev = &data->intf->dev;
- buffer = kmalloc(USBTMC_SIZE_IOBUFFER, GFP_KERNEL);
+ buffer = kmalloc(USBTMC_BUFSIZE, GFP_KERNEL);
if (!buffer)
return -ENOMEM;
@@ -181,86 +280,88 @@ static int usbtmc_ioctl_abort_bulk_in(struct usbtmc_device_data *data)
usb_rcvctrlpipe(data->usb_dev, 0),
USBTMC_REQUEST_INITIATE_ABORT_BULK_IN,
USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT,
- data->bTag_last_read, data->bulk_in,
- buffer, 2, USBTMC_TIMEOUT);
+ tag, data->bulk_in,
+ buffer, 2, USB_CTRL_GET_TIMEOUT);
if (rv < 0) {
dev_err(dev, "usb_control_msg returned %d\n", rv);
goto exit;
}
- dev_dbg(dev, "INITIATE_ABORT_BULK_IN returned %x\n", buffer[0]);
+ dev_dbg(dev, "INITIATE_ABORT_BULK_IN returned %x with tag %02x\n",
+ buffer[0], buffer[1]);
if (buffer[0] == USBTMC_STATUS_FAILED) {
+ /* No transfer in progress and the Bulk-OUT FIFO is empty. */
rv = 0;
goto exit;
}
- if (buffer[0] != USBTMC_STATUS_SUCCESS) {
- dev_err(dev, "INITIATE_ABORT_BULK_IN returned %x\n",
- buffer[0]);
- rv = -EPERM;
+ if (buffer[0] == USBTMC_STATUS_TRANSFER_NOT_IN_PROGRESS) {
+ /* The device returns this status if either:
+ * - There is a transfer in progress, but the specified bTag
+ * does not match.
+ * - There is no transfer in progress, but the Bulk-OUT FIFO
+ * is not empty.
+ */
+ rv = -ENOMSG;
goto exit;
}
- max_size = 0;
- current_setting = data->intf->cur_altsetting;
- for (n = 0; n < current_setting->desc.bNumEndpoints; n++)
- if (current_setting->endpoint[n].desc.bEndpointAddress ==
- data->bulk_in)
- max_size = usb_endpoint_maxp(&current_setting->endpoint[n].desc);
-
- if (max_size == 0) {
- dev_err(dev, "Couldn't get wMaxPacketSize\n");
+ if (buffer[0] != USBTMC_STATUS_SUCCESS) {
+ dev_err(dev, "INITIATE_ABORT_BULK_IN returned %x\n",
+ buffer[0]);
rv = -EPERM;
goto exit;
}
- dev_dbg(&data->intf->dev, "wMaxPacketSize is %d\n", max_size);
-
n = 0;
- do {
- dev_dbg(dev, "Reading from bulk in EP\n");
+usbtmc_abort_bulk_in_status:
+ dev_dbg(dev, "Reading from bulk in EP\n");
- rv = usb_bulk_msg(data->usb_dev,
- usb_rcvbulkpipe(data->usb_dev,
- data->bulk_in),
- buffer, USBTMC_SIZE_IOBUFFER,
- &actual, USBTMC_TIMEOUT);
+ /* Data must be present. So use low timeout 300 ms */
+ actual = 0;
+ rv = usb_bulk_msg(data->usb_dev,
+ usb_rcvbulkpipe(data->usb_dev,
+ data->bulk_in),
+ buffer, USBTMC_BUFSIZE,
+ &actual, 300);
- n++;
+ print_hex_dump_debug("usbtmc ", DUMP_PREFIX_NONE, 16, 1,
+ buffer, actual, true);
- if (rv < 0) {
- dev_err(dev, "usb_bulk_msg returned %d\n", rv);
+ n++;
+
+ if (rv < 0) {
+ dev_err(dev, "usb_bulk_msg returned %d\n", rv);
+ if (rv != -ETIMEDOUT)
goto exit;
- }
- } while ((actual == max_size) &&
- (n < USBTMC_MAX_READS_TO_CLEAR_BULK_IN));
+ }
+
+ if (actual == USBTMC_BUFSIZE)
+ goto usbtmc_abort_bulk_in_status;
- if (actual == max_size) {
+ if (n >= USBTMC_MAX_READS_TO_CLEAR_BULK_IN) {
dev_err(dev, "Couldn't clear device buffer within %d cycles\n",
USBTMC_MAX_READS_TO_CLEAR_BULK_IN);
rv = -EPERM;
goto exit;
}
- n = 0;
-
-usbtmc_abort_bulk_in_status:
rv = usb_control_msg(data->usb_dev,
usb_rcvctrlpipe(data->usb_dev, 0),
USBTMC_REQUEST_CHECK_ABORT_BULK_IN_STATUS,
USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT,
0, data->bulk_in, buffer, 0x08,
- USBTMC_TIMEOUT);
+ USB_CTRL_GET_TIMEOUT);
if (rv < 0) {
dev_err(dev, "usb_control_msg returned %d\n", rv);
goto exit;
}
- dev_dbg(dev, "INITIATE_ABORT_BULK_IN returned %x\n", buffer[0]);
+ dev_dbg(dev, "CHECK_ABORT_BULK_IN returned %x\n", buffer[0]);
if (buffer[0] == USBTMC_STATUS_SUCCESS) {
rv = 0;
@@ -268,46 +369,30 @@ usbtmc_abort_bulk_in_status:
}
if (buffer[0] != USBTMC_STATUS_PENDING) {
- dev_err(dev, "INITIATE_ABORT_BULK_IN returned %x\n", buffer[0]);
+ dev_err(dev, "CHECK_ABORT_BULK_IN returned %x\n", buffer[0]);
rv = -EPERM;
goto exit;
}
- if (buffer[1] == 1)
- do {
- dev_dbg(dev, "Reading from bulk in EP\n");
-
- rv = usb_bulk_msg(data->usb_dev,
- usb_rcvbulkpipe(data->usb_dev,
- data->bulk_in),
- buffer, USBTMC_SIZE_IOBUFFER,
- &actual, USBTMC_TIMEOUT);
-
- n++;
-
- if (rv < 0) {
- dev_err(dev, "usb_bulk_msg returned %d\n", rv);
- goto exit;
- }
- } while ((actual == max_size) &&
- (n < USBTMC_MAX_READS_TO_CLEAR_BULK_IN));
-
- if (actual == max_size) {
- dev_err(dev, "Couldn't clear device buffer within %d cycles\n",
- USBTMC_MAX_READS_TO_CLEAR_BULK_IN);
- rv = -EPERM;
- goto exit;
+ if ((buffer[1] & 1) > 0) {
+ /* The device has 1 or more queued packets the Host can read */
+ goto usbtmc_abort_bulk_in_status;
}
- goto usbtmc_abort_bulk_in_status;
-
+ /* The Host must send CHECK_ABORT_BULK_IN_STATUS at a later time. */
+ rv = -EAGAIN;
exit:
kfree(buffer);
return rv;
+}
+static int usbtmc_ioctl_abort_bulk_in(struct usbtmc_device_data *data)
+{
+ return usbtmc_ioctl_abort_bulk_in_tag(data, data->bTag_last_read);
}
-static int usbtmc_ioctl_abort_bulk_out(struct usbtmc_device_data *data)
+static int usbtmc_ioctl_abort_bulk_out_tag(struct usbtmc_device_data *data,
+ u8 tag)
{
struct device *dev;
u8 *buffer;
@@ -324,8 +409,8 @@ static int usbtmc_ioctl_abort_bulk_out(struct usbtmc_device_data *data)
usb_rcvctrlpipe(data->usb_dev, 0),
USBTMC_REQUEST_INITIATE_ABORT_BULK_OUT,
USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT,
- data->bTag_last_write, data->bulk_out,
- buffer, 2, USBTMC_TIMEOUT);
+ tag, data->bulk_out,
+ buffer, 2, USB_CTRL_GET_TIMEOUT);
if (rv < 0) {
dev_err(dev, "usb_control_msg returned %d\n", rv);
@@ -344,12 +429,14 @@ static int usbtmc_ioctl_abort_bulk_out(struct usbtmc_device_data *data)
n = 0;
usbtmc_abort_bulk_out_check_status:
+ /* do not stress device with subsequent requests */
+ msleep(50);
rv = usb_control_msg(data->usb_dev,
usb_rcvctrlpipe(data->usb_dev, 0),
USBTMC_REQUEST_CHECK_ABORT_BULK_OUT_STATUS,
USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT,
0, data->bulk_out, buffer, 0x08,
- USBTMC_TIMEOUT);
+ USB_CTRL_GET_TIMEOUT);
n++;
if (rv < 0) {
dev_err(dev, "usb_control_msg returned %d\n", rv);
@@ -383,10 +470,17 @@ exit:
return rv;
}
-static int usbtmc488_ioctl_read_stb(struct usbtmc_device_data *data,
+static int usbtmc_ioctl_abort_bulk_out(struct usbtmc_device_data *data)
+{
+ return usbtmc_ioctl_abort_bulk_out_tag(data, data->bTag_last_write);
+}
+
+static int usbtmc488_ioctl_read_stb(struct usbtmc_file_data *file_data,
void __user *arg)
{
+ struct usbtmc_device_data *data = file_data->data;
struct device *dev = &data->intf->dev;
+ int srq_asserted = 0;
u8 *buffer;
u8 tag;
__u8 stb;
@@ -395,22 +489,32 @@ static int usbtmc488_ioctl_read_stb(struct usbtmc_device_data *data,
dev_dbg(dev, "Enter ioctl_read_stb iin_ep_present: %d\n",
data->iin_ep_present);
+ spin_lock_irq(&data->dev_lock);
+ srq_asserted = atomic_xchg(&file_data->srq_asserted, srq_asserted);
+ if (srq_asserted) {
+ /* a STB with SRQ is already received */
+ stb = file_data->srq_byte;
+ spin_unlock_irq(&data->dev_lock);
+ rv = put_user(stb, (__u8 __user *)arg);
+ dev_dbg(dev, "stb:0x%02x with srq received %d\n",
+ (unsigned int)stb, rv);
+ return rv;
+ }
+ spin_unlock_irq(&data->dev_lock);
+
buffer = kmalloc(8, GFP_KERNEL);
if (!buffer)
return -ENOMEM;
atomic_set(&data->iin_data_valid, 0);
- /* must issue read_stb before using poll or select */
- atomic_set(&data->srq_asserted, 0);
-
rv = usb_control_msg(data->usb_dev,
usb_rcvctrlpipe(data->usb_dev, 0),
USBTMC488_REQUEST_READ_STATUS_BYTE,
USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
data->iin_bTag,
data->ifnum,
- buffer, 0x03, USBTMC_TIMEOUT);
+ buffer, 0x03, USB_CTRL_GET_TIMEOUT);
if (rv < 0) {
dev_err(dev, "stb usb_control_msg returned %d\n", rv);
goto exit;
@@ -426,7 +530,7 @@ static int usbtmc488_ioctl_read_stb(struct usbtmc_device_data *data,
rv = wait_event_interruptible_timeout(
data->waitq,
atomic_read(&data->iin_data_valid) != 0,
- USBTMC_TIMEOUT);
+ file_data->timeout);
if (rv < 0) {
dev_dbg(dev, "wait interrupted %d\n", rv);
goto exit;
@@ -434,7 +538,7 @@ static int usbtmc488_ioctl_read_stb(struct usbtmc_device_data *data,
if (rv == 0) {
dev_dbg(dev, "wait timed out\n");
- rv = -ETIME;
+ rv = -ETIMEDOUT;
goto exit;
}
@@ -449,9 +553,8 @@ static int usbtmc488_ioctl_read_stb(struct usbtmc_device_data *data,
stb = buffer[2];
}
- rv = copy_to_user(arg, &stb, sizeof(stb));
- if (rv)
- rv = -EFAULT;
+ rv = put_user(stb, (__u8 __user *)arg);
+ dev_dbg(dev, "stb:0x%02x received %d\n", (unsigned int)stb, rv);
exit:
/* bump interrupt bTag */
@@ -464,6 +567,54 @@ static int usbtmc488_ioctl_read_stb(struct usbtmc_device_data *data,
return rv;
}
+static int usbtmc488_ioctl_wait_srq(struct usbtmc_file_data *file_data,
+ __u32 __user *arg)
+{
+ struct usbtmc_device_data *data = file_data->data;
+ struct device *dev = &data->intf->dev;
+ int rv;
+ u32 timeout;
+ unsigned long expire;
+
+ if (!data->iin_ep_present) {
+ dev_dbg(dev, "no interrupt endpoint present\n");
+ return -EFAULT;
+ }
+
+ if (get_user(timeout, arg))
+ return -EFAULT;
+
+ expire = msecs_to_jiffies(timeout);
+
+ 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);
+
+ 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;
+
+ if (rv < 0) {
+ /* dev can be invalid now! */
+ pr_debug("%s - wait interrupted %d\n", __func__, rv);
+ return rv;
+ }
+
+ if (rv == 0) {
+ dev_dbg(dev, "%s - wait timed out\n", __func__);
+ return -ETIMEDOUT;
+ }
+
+ dev_dbg(dev, "%s - srq asserted\n", __func__);
+ return 0;
+}
+
static int usbtmc488_ioctl_simple(struct usbtmc_device_data *data,
void __user *arg, unsigned int cmd)
{
@@ -497,7 +648,7 @@ static int usbtmc488_ioctl_simple(struct usbtmc_device_data *data,
USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
wValue,
data->ifnum,
- buffer, 0x01, USBTMC_TIMEOUT);
+ buffer, 0x01, USB_CTRL_GET_TIMEOUT);
if (rv < 0) {
dev_err(dev, "simple usb_control_msg failed %d\n", rv);
goto exit;
@@ -520,6 +671,604 @@ static int usbtmc488_ioctl_simple(struct usbtmc_device_data *data,
}
/*
+ * Sends a TRIGGER Bulk-OUT command message
+ * See the USBTMC-USB488 specification, Table 2.
+ *
+ * Also updates bTag_last_write.
+ */
+static int usbtmc488_ioctl_trigger(struct usbtmc_file_data *file_data)
+{
+ struct usbtmc_device_data *data = file_data->data;
+ int retval;
+ u8 *buffer;
+ int actual;
+
+ buffer = kzalloc(USBTMC_HEADER_SIZE, GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+
+ buffer[0] = 128;
+ buffer[1] = data->bTag;
+ buffer[2] = ~data->bTag;
+
+ retval = usb_bulk_msg(data->usb_dev,
+ usb_sndbulkpipe(data->usb_dev,
+ data->bulk_out),
+ buffer, USBTMC_HEADER_SIZE,
+ &actual, file_data->timeout);
+
+ /* Store bTag (in case we need to abort) */
+ data->bTag_last_write = data->bTag;
+
+ /* Increment bTag -- and increment again if zero */
+ data->bTag++;
+ if (!data->bTag)
+ data->bTag++;
+
+ kfree(buffer);
+ if (retval < 0) {
+ dev_err(&data->intf->dev, "%s returned %d\n",
+ __func__, retval);
+ return retval;
+ }
+
+ return 0;
+}
+
+static struct urb *usbtmc_create_urb(void)
+{
+ const size_t bufsize = USBTMC_BUFSIZE;
+ u8 *dmabuf = NULL;
+ struct urb *urb = usb_alloc_urb(0, GFP_KERNEL);
+
+ if (!urb)
+ return NULL;
+
+ dmabuf = kmalloc(bufsize, GFP_KERNEL);
+ if (!dmabuf) {
+ usb_free_urb(urb);
+ return NULL;
+ }
+
+ urb->transfer_buffer = dmabuf;
+ urb->transfer_buffer_length = bufsize;
+ urb->transfer_flags |= URB_FREE_BUFFER;
+ return urb;
+}
+
+static void usbtmc_read_bulk_cb(struct urb *urb)
+{
+ struct usbtmc_file_data *file_data = urb->context;
+ int status = urb->status;
+ unsigned long flags;
+
+ /* sync/async unlink faults aren't errors */
+ if (status) {
+ if (!(/* status == -ENOENT || */
+ status == -ECONNRESET ||
+ status == -EREMOTEIO || /* Short packet */
+ status == -ESHUTDOWN))
+ dev_err(&file_data->data->intf->dev,
+ "%s - nonzero read bulk status received: %d\n",
+ __func__, status);
+
+ spin_lock_irqsave(&file_data->err_lock, flags);
+ if (!file_data->in_status)
+ file_data->in_status = status;
+ spin_unlock_irqrestore(&file_data->err_lock, flags);
+ }
+
+ spin_lock_irqsave(&file_data->err_lock, flags);
+ file_data->in_transfer_size += urb->actual_length;
+ dev_dbg(&file_data->data->intf->dev,
+ "%s - total size: %u current: %d status: %d\n",
+ __func__, file_data->in_transfer_size,
+ urb->actual_length, status);
+ spin_unlock_irqrestore(&file_data->err_lock, flags);
+ usb_anchor_urb(urb, &file_data->in_anchor);
+
+ wake_up_interruptible(&file_data->wait_bulk_in);
+ wake_up_interruptible(&file_data->data->waitq);
+}
+
+static inline bool usbtmc_do_transfer(struct usbtmc_file_data *file_data)
+{
+ bool data_or_error;
+
+ spin_lock_irq(&file_data->err_lock);
+ data_or_error = !usb_anchor_empty(&file_data->in_anchor)
+ || file_data->in_status;
+ spin_unlock_irq(&file_data->err_lock);
+ dev_dbg(&file_data->data->intf->dev, "%s: returns %d\n", __func__,
+ data_or_error);
+ return data_or_error;
+}
+
+static ssize_t usbtmc_generic_read(struct usbtmc_file_data *file_data,
+ void __user *user_buffer,
+ u32 transfer_size,
+ u32 *transferred,
+ u32 flags)
+{
+ struct usbtmc_device_data *data = file_data->data;
+ struct device *dev = &data->intf->dev;
+ u32 done = 0;
+ u32 remaining;
+ const u32 bufsize = USBTMC_BUFSIZE;
+ int retval = 0;
+ u32 max_transfer_size;
+ unsigned long expire;
+ int bufcount = 1;
+ int again = 0;
+
+ /* mutex already locked */
+
+ *transferred = done;
+
+ max_transfer_size = transfer_size;
+
+ if (flags & USBTMC_FLAG_IGNORE_TRAILER) {
+ /* The device may send extra alignment bytes (up to
+ * wMaxPacketSize – 1) to avoid sending a zero-length
+ * packet
+ */
+ remaining = transfer_size;
+ if ((max_transfer_size % data->wMaxPacketSize) == 0)
+ max_transfer_size += (data->wMaxPacketSize - 1);
+ } else {
+ /* round down to bufsize to avoid truncated data left */
+ if (max_transfer_size > bufsize) {
+ max_transfer_size =
+ roundup(max_transfer_size + 1 - bufsize,
+ bufsize);
+ }
+ remaining = max_transfer_size;
+ }
+
+ spin_lock_irq(&file_data->err_lock);
+
+ if (file_data->in_status) {
+ /* return the very first error */
+ retval = file_data->in_status;
+ spin_unlock_irq(&file_data->err_lock);
+ goto error;
+ }
+
+ if (flags & USBTMC_FLAG_ASYNC) {
+ if (usb_anchor_empty(&file_data->in_anchor))
+ again = 1;
+
+ if (file_data->in_urbs_used == 0) {
+ file_data->in_transfer_size = 0;
+ file_data->in_status = 0;
+ }
+ } else {
+ file_data->in_transfer_size = 0;
+ file_data->in_status = 0;
+ }
+
+ if (max_transfer_size == 0) {
+ bufcount = 0;
+ } else {
+ bufcount = roundup(max_transfer_size, bufsize) / bufsize;
+ if (bufcount > file_data->in_urbs_used)
+ bufcount -= file_data->in_urbs_used;
+ else
+ bufcount = 0;
+
+ if (bufcount + file_data->in_urbs_used > MAX_URBS_IN_FLIGHT) {
+ bufcount = MAX_URBS_IN_FLIGHT -
+ file_data->in_urbs_used;
+ }
+ }
+ spin_unlock_irq(&file_data->err_lock);
+
+ dev_dbg(dev, "%s: requested=%u flags=0x%X size=%u bufs=%d used=%d\n",
+ __func__, transfer_size, flags,
+ max_transfer_size, bufcount, file_data->in_urbs_used);
+
+ while (bufcount > 0) {
+ u8 *dmabuf = NULL;
+ struct urb *urb = usbtmc_create_urb();
+
+ if (!urb) {
+ retval = -ENOMEM;
+ goto error;
+ }
+
+ dmabuf = urb->transfer_buffer;
+
+ usb_fill_bulk_urb(urb, data->usb_dev,
+ usb_rcvbulkpipe(data->usb_dev, data->bulk_in),
+ dmabuf, bufsize,
+ usbtmc_read_bulk_cb, file_data);
+
+ usb_anchor_urb(urb, &file_data->submitted);
+ retval = usb_submit_urb(urb, GFP_KERNEL);
+ /* urb is anchored. We can release our reference. */
+ usb_free_urb(urb);
+ if (unlikely(retval)) {
+ usb_unanchor_urb(urb);
+ goto error;
+ }
+ file_data->in_urbs_used++;
+ bufcount--;
+ }
+
+ if (again) {
+ dev_dbg(dev, "%s: ret=again\n", __func__);
+ return -EAGAIN;
+ }
+
+ if (user_buffer == NULL)
+ return -EINVAL;
+
+ expire = msecs_to_jiffies(file_data->timeout);
+
+ while (max_transfer_size > 0) {
+ u32 this_part;
+ struct urb *urb = NULL;
+
+ if (!(flags & USBTMC_FLAG_ASYNC)) {
+ dev_dbg(dev, "%s: before wait time %lu\n",
+ __func__, expire);
+ retval = 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);
+
+ if (retval <= 0) {
+ if (retval == 0)
+ retval = -ETIMEDOUT;
+ goto error;
+ }
+ }
+
+ urb = usb_get_from_anchor(&file_data->in_anchor);
+ if (!urb) {
+ if (!(flags & USBTMC_FLAG_ASYNC)) {
+ /* synchronous case: must not happen */
+ retval = -EFAULT;
+ goto error;
+ }
+
+ /* asynchronous case: ready, do not block or wait */
+ *transferred = done;
+ dev_dbg(dev, "%s: (async) done=%u ret=0\n",
+ __func__, done);
+ return 0;
+ }
+
+ file_data->in_urbs_used--;
+
+ if (max_transfer_size > urb->actual_length)
+ max_transfer_size -= urb->actual_length;
+ else
+ max_transfer_size = 0;
+
+ if (remaining > urb->actual_length)
+ this_part = urb->actual_length;
+ else
+ this_part = remaining;
+
+ print_hex_dump_debug("usbtmc ", DUMP_PREFIX_NONE, 16, 1,
+ urb->transfer_buffer, urb->actual_length, true);
+
+ if (copy_to_user(user_buffer + done,
+ urb->transfer_buffer, this_part)) {
+ usb_free_urb(urb);
+ retval = -EFAULT;
+ goto error;
+ }
+
+ remaining -= this_part;
+ done += this_part;
+
+ spin_lock_irq(&file_data->err_lock);
+ if (urb->status) {
+ /* return the very first error */
+ retval = file_data->in_status;
+ spin_unlock_irq(&file_data->err_lock);
+ usb_free_urb(urb);
+ goto error;
+ }
+ spin_unlock_irq(&file_data->err_lock);
+
+ if (urb->actual_length < bufsize) {
+ /* short packet or ZLP received => ready */
+ usb_free_urb(urb);
+ retval = 1;
+ break;
+ }
+
+ if (!(flags & USBTMC_FLAG_ASYNC) &&
+ max_transfer_size > (bufsize * file_data->in_urbs_used)) {
+ /* resubmit, since other buffers still not enough */
+ usb_anchor_urb(urb, &file_data->submitted);
+ retval = usb_submit_urb(urb, GFP_KERNEL);
+ if (unlikely(retval)) {
+ usb_unanchor_urb(urb);
+ usb_free_urb(urb);
+ goto error;
+ }
+ file_data->in_urbs_used++;
+ }
+ usb_free_urb(urb);
+ retval = 0;
+ }
+
+error:
+ *transferred = done;
+
+ dev_dbg(dev, "%s: before kill\n", __func__);
+ /* Attention: killing urbs can take long time (2 ms) */
+ usb_kill_anchored_urbs(&file_data->submitted);
+ dev_dbg(dev, "%s: after kill\n", __func__);
+ usb_scuttle_anchored_urbs(&file_data->in_anchor);
+ file_data->in_urbs_used = 0;
+ file_data->in_status = 0; /* no spinlock needed here */
+ dev_dbg(dev, "%s: done=%u ret=%d\n", __func__, done, retval);
+
+ return retval;
+}
+
+static ssize_t usbtmc_ioctl_generic_read(struct usbtmc_file_data *file_data,
+ void __user *arg)
+{
+ struct usbtmc_message msg;
+ ssize_t retval = 0;
+
+ /* mutex already locked */
+
+ if (copy_from_user(&msg, arg, sizeof(struct usbtmc_message)))
+ return -EFAULT;
+
+ retval = usbtmc_generic_read(file_data, msg.message,
+ msg.transfer_size, &msg.transferred,
+ msg.flags);
+
+ if (put_user(msg.transferred,
+ &((struct usbtmc_message __user *)arg)->transferred))
+ return -EFAULT;
+
+ return retval;
+}
+
+static void usbtmc_write_bulk_cb(struct urb *urb)
+{
+ struct usbtmc_file_data *file_data = urb->context;
+ int wakeup = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&file_data->err_lock, flags);
+ file_data->out_transfer_size += urb->actual_length;
+
+ /* sync/async unlink faults aren't errors */
+ if (urb->status) {
+ if (!(urb->status == -ENOENT ||
+ urb->status == -ECONNRESET ||
+ urb->status == -ESHUTDOWN))
+ dev_err(&file_data->data->intf->dev,
+ "%s - nonzero write bulk status received: %d\n",
+ __func__, urb->status);
+
+ if (!file_data->out_status) {
+ file_data->out_status = urb->status;
+ wakeup = 1;
+ }
+ }
+ spin_unlock_irqrestore(&file_data->err_lock, flags);
+
+ dev_dbg(&file_data->data->intf->dev,
+ "%s - write bulk total size: %u\n",
+ __func__, file_data->out_transfer_size);
+
+ up(&file_data->limit_write_sem);
+ if (usb_anchor_empty(&file_data->submitted) || wakeup)
+ wake_up_interruptible(&file_data->data->waitq);
+}
+
+static ssize_t usbtmc_generic_write(struct usbtmc_file_data *file_data,
+ const void __user *user_buffer,
+ u32 transfer_size,
+ u32 *transferred,
+ u32 flags)
+{
+ struct usbtmc_device_data *data = file_data->data;
+ struct device *dev;
+ u32 done = 0;
+ u32 remaining;
+ unsigned long expire;
+ const u32 bufsize = USBTMC_BUFSIZE;
+ struct urb *urb = NULL;
+ int retval = 0;
+ u32 timeout;
+
+ *transferred = 0;
+
+ /* Get pointer to private data structure */
+ dev = &data->intf->dev;
+
+ dev_dbg(dev, "%s: size=%u flags=0x%X sema=%u\n",
+ __func__, transfer_size, flags,
+ file_data->limit_write_sem.count);
+
+ if (flags & USBTMC_FLAG_APPEND) {
+ spin_lock_irq(&file_data->err_lock);
+ retval = file_data->out_status;
+ spin_unlock_irq(&file_data->err_lock);
+ if (retval < 0)
+ return retval;
+ } else {
+ spin_lock_irq(&file_data->err_lock);
+ file_data->out_transfer_size = 0;
+ file_data->out_status = 0;
+ spin_unlock_irq(&file_data->err_lock);
+ }
+
+ remaining = transfer_size;
+ if (remaining > INT_MAX)
+ remaining = INT_MAX;
+
+ timeout = file_data->timeout;
+ expire = msecs_to_jiffies(timeout);
+
+ while (remaining > 0) {
+ u32 this_part, aligned;
+ u8 *buffer = NULL;
+
+ if (flags & USBTMC_FLAG_ASYNC) {
+ if (down_trylock(&file_data->limit_write_sem)) {
+ retval = (done)?(0):(-EAGAIN);
+ goto exit;
+ }
+ } else {
+ retval = down_timeout(&file_data->limit_write_sem,
+ expire);
+ if (retval < 0) {
+ retval = -ETIMEDOUT;
+ goto error;
+ }
+ }
+
+ spin_lock_irq(&file_data->err_lock);
+ retval = file_data->out_status;
+ spin_unlock_irq(&file_data->err_lock);
+ if (retval < 0) {
+ up(&file_data->limit_write_sem);
+ goto error;
+ }
+
+ /* prepare next urb to send */
+ urb = usbtmc_create_urb();
+ if (!urb) {
+ retval = -ENOMEM;
+ up(&file_data->limit_write_sem);
+ goto error;
+ }
+ buffer = urb->transfer_buffer;
+
+ if (remaining > bufsize)
+ this_part = bufsize;
+ else
+ this_part = remaining;
+
+ if (copy_from_user(buffer, user_buffer + done, this_part)) {
+ retval = -EFAULT;
+ up(&file_data->limit_write_sem);
+ goto error;
+ }
+
+ print_hex_dump_debug("usbtmc ", DUMP_PREFIX_NONE,
+ 16, 1, buffer, this_part, true);
+
+ /* fill bulk with 32 bit alignment to meet USBTMC specification
+ * (size + 3 & ~3) rounds up and simplifies user code
+ */
+ aligned = (this_part + 3) & ~3;
+ dev_dbg(dev, "write(size:%u align:%u done:%u)\n",
+ (unsigned int)this_part,
+ (unsigned int)aligned,
+ (unsigned int)done);
+
+ usb_fill_bulk_urb(urb, data->usb_dev,
+ usb_sndbulkpipe(data->usb_dev, data->bulk_out),
+ urb->transfer_buffer, aligned,
+ usbtmc_write_bulk_cb, file_data);
+
+ usb_anchor_urb(urb, &file_data->submitted);
+ retval = usb_submit_urb(urb, GFP_KERNEL);
+ if (unlikely(retval)) {
+ usb_unanchor_urb(urb);
+ up(&file_data->limit_write_sem);
+ goto error;
+ }
+
+ usb_free_urb(urb);
+ urb = NULL; /* urb will be finally released by usb driver */
+
+ remaining -= this_part;
+ done += this_part;
+ }
+
+ /* All urbs are on the fly */
+ if (!(flags & USBTMC_FLAG_ASYNC)) {
+ if (!usb_wait_anchor_empty_timeout(&file_data->submitted,
+ timeout)) {
+ retval = -ETIMEDOUT;
+ goto error;
+ }
+ }
+
+ retval = 0;
+ goto exit;
+
+error:
+ usb_kill_anchored_urbs(&file_data->submitted);
+exit:
+ usb_free_urb(urb);
+
+ spin_lock_irq(&file_data->err_lock);
+ if (!(flags & USBTMC_FLAG_ASYNC))
+ done = file_data->out_transfer_size;
+ if (!retval && file_data->out_status)
+ retval = file_data->out_status;
+ spin_unlock_irq(&file_data->err_lock);
+
+ *transferred = done;
+
+ dev_dbg(dev, "%s: done=%u, retval=%d, urbstat=%d\n",
+ __func__, done, retval, file_data->out_status);
+
+ return retval;
+}
+
+static ssize_t usbtmc_ioctl_generic_write(struct usbtmc_file_data *file_data,
+ void __user *arg)
+{
+ struct usbtmc_message msg;
+ ssize_t retval = 0;
+
+ /* mutex already locked */
+
+ if (copy_from_user(&msg, arg, sizeof(struct usbtmc_message)))
+ return -EFAULT;
+
+ retval = usbtmc_generic_write(file_data, msg.message,
+ msg.transfer_size, &msg.transferred,
+ msg.flags);
+
+ if (put_user(msg.transferred,
+ &((struct usbtmc_message __user *)arg)->transferred))
+ return -EFAULT;
+
+ return retval;
+}
+
+/*
+ * Get the generic write result
+ */
+static ssize_t usbtmc_ioctl_write_result(struct usbtmc_file_data *file_data,
+ void __user *arg)
+{
+ u32 transferred;
+ int retval;
+
+ spin_lock_irq(&file_data->err_lock);
+ transferred = file_data->out_transfer_size;
+ retval = file_data->out_status;
+ spin_unlock_irq(&file_data->err_lock);
+
+ if (put_user(transferred, (__u32 __user *)arg))
+ return -EFAULT;
+
+ return retval;
+}
+
+/*
* Sends a REQUEST_DEV_DEP_MSG_IN message on the Bulk-OUT endpoint.
* @transfer_size: number of bytes to request from the device.
*
@@ -527,8 +1276,10 @@ static int usbtmc488_ioctl_simple(struct usbtmc_device_data *data,
*
* Also updates bTag_last_write.
*/
-static int send_request_dev_dep_msg_in(struct usbtmc_device_data *data, size_t transfer_size)
+static int send_request_dev_dep_msg_in(struct usbtmc_file_data *file_data,
+ u32 transfer_size)
{
+ struct usbtmc_device_data *data = file_data->data;
int retval;
u8 *buffer;
int actual;
@@ -547,9 +1298,9 @@ static int send_request_dev_dep_msg_in(struct usbtmc_device_data *data, size_t t
buffer[5] = transfer_size >> 8;
buffer[6] = transfer_size >> 16;
buffer[7] = transfer_size >> 24;
- buffer[8] = data->TermCharEnabled * 2;
+ buffer[8] = file_data->term_char_enabled * 2;
/* Use term character? */
- buffer[9] = data->TermChar;
+ buffer[9] = file_data->term_char;
buffer[10] = 0; /* Reserved */
buffer[11] = 0; /* Reserved */
@@ -557,7 +1308,8 @@ static int send_request_dev_dep_msg_in(struct usbtmc_device_data *data, size_t t
retval = usb_bulk_msg(data->usb_dev,
usb_sndbulkpipe(data->usb_dev,
data->bulk_out),
- buffer, USBTMC_HEADER_SIZE, &actual, USBTMC_TIMEOUT);
+ buffer, USBTMC_HEADER_SIZE,
+ &actual, file_data->timeout);
/* Store bTag (in case we need to abort) */
data->bTag_last_write = data->bTag;
@@ -568,32 +1320,33 @@ static int send_request_dev_dep_msg_in(struct usbtmc_device_data *data, size_t t
data->bTag++;
kfree(buffer);
- if (retval < 0) {
- dev_err(&data->intf->dev, "usb_bulk_msg in send_request_dev_dep_msg_in() returned %d\n", retval);
- return retval;
- }
+ if (retval < 0)
+ dev_err(&data->intf->dev, "%s returned %d\n",
+ __func__, retval);
- return 0;
+ return retval;
}
static ssize_t usbtmc_read(struct file *filp, char __user *buf,
size_t count, loff_t *f_pos)
{
+ struct usbtmc_file_data *file_data;
struct usbtmc_device_data *data;
struct device *dev;
+ const u32 bufsize = USBTMC_BUFSIZE;
u32 n_characters;
u8 *buffer;
int actual;
- size_t done;
- size_t remaining;
+ u32 done = 0;
+ u32 remaining;
int retval;
- size_t this_part;
/* Get pointer to private data structure */
- data = filp->private_data;
+ file_data = filp->private_data;
+ data = file_data->data;
dev = &data->intf->dev;
- buffer = kmalloc(USBTMC_SIZE_IOBUFFER, GFP_KERNEL);
+ buffer = kmalloc(bufsize, GFP_KERNEL);
if (!buffer)
return -ENOMEM;
@@ -603,152 +1356,116 @@ static ssize_t usbtmc_read(struct file *filp, char __user *buf,
goto exit;
}
- if (data->rigol_quirk) {
- dev_dbg(dev, "usb_bulk_msg_in: count(%zu)\n", count);
+ if (count > INT_MAX)
+ count = INT_MAX;
- retval = send_request_dev_dep_msg_in(data, count);
+ dev_dbg(dev, "%s(count:%zu)\n", __func__, count);
- if (retval < 0) {
- if (data->auto_abort)
- usbtmc_ioctl_abort_bulk_out(data);
- goto exit;
- }
+ retval = send_request_dev_dep_msg_in(file_data, count);
+
+ if (retval < 0) {
+ if (file_data->auto_abort)
+ usbtmc_ioctl_abort_bulk_out(data);
+ goto exit;
}
/* Loop until we have fetched everything we requested */
remaining = count;
- this_part = remaining;
- done = 0;
-
- while (remaining > 0) {
- if (!data->rigol_quirk) {
- dev_dbg(dev, "usb_bulk_msg_in: remaining(%zu), count(%zu)\n", remaining, count);
-
- if (remaining > USBTMC_SIZE_IOBUFFER - USBTMC_HEADER_SIZE - 3)
- this_part = USBTMC_SIZE_IOBUFFER - USBTMC_HEADER_SIZE - 3;
- else
- this_part = remaining;
-
- retval = send_request_dev_dep_msg_in(data, this_part);
- if (retval < 0) {
- dev_err(dev, "usb_bulk_msg returned %d\n", retval);
- if (data->auto_abort)
- usbtmc_ioctl_abort_bulk_out(data);
- goto exit;
- }
- }
-
- /* Send bulk URB */
- retval = usb_bulk_msg(data->usb_dev,
- usb_rcvbulkpipe(data->usb_dev,
- data->bulk_in),
- buffer, USBTMC_SIZE_IOBUFFER, &actual,
- USBTMC_TIMEOUT);
+ actual = 0;
- dev_dbg(dev, "usb_bulk_msg: retval(%u), done(%zu), remaining(%zu), actual(%d)\n", retval, done, remaining, actual);
-
- /* Store bTag (in case we need to abort) */
- data->bTag_last_read = data->bTag;
+ /* Send bulk URB */
+ retval = usb_bulk_msg(data->usb_dev,
+ usb_rcvbulkpipe(data->usb_dev,
+ data->bulk_in),
+ buffer, bufsize, &actual,
+ file_data->timeout);
- if (retval < 0) {
- dev_dbg(dev, "Unable to read data, error %d\n", retval);
- if (data->auto_abort)
- usbtmc_ioctl_abort_bulk_in(data);
- goto exit;
- }
+ dev_dbg(dev, "%s: bulk_msg retval(%u), actual(%d)\n",
+ __func__, retval, actual);
- /* Parse header in first packet */
- if ((done == 0) || !data->rigol_quirk) {
- /* Sanity checks for the header */
- if (actual < USBTMC_HEADER_SIZE) {
- dev_err(dev, "Device sent too small first packet: %u < %u\n", actual, USBTMC_HEADER_SIZE);
- if (data->auto_abort)
- usbtmc_ioctl_abort_bulk_in(data);
- goto exit;
- }
+ /* Store bTag (in case we need to abort) */
+ data->bTag_last_read = data->bTag;
- if (buffer[0] != 2) {
- dev_err(dev, "Device sent reply with wrong MsgID: %u != 2\n", buffer[0]);
- if (data->auto_abort)
- usbtmc_ioctl_abort_bulk_in(data);
- goto exit;
- }
+ if (retval < 0) {
+ if (file_data->auto_abort)
+ usbtmc_ioctl_abort_bulk_in(data);
+ goto exit;
+ }
- if (buffer[1] != data->bTag_last_write) {
- dev_err(dev, "Device sent reply with wrong bTag: %u != %u\n", buffer[1], data->bTag_last_write);
- if (data->auto_abort)
- usbtmc_ioctl_abort_bulk_in(data);
- goto exit;
- }
+ /* Sanity checks for the header */
+ if (actual < USBTMC_HEADER_SIZE) {
+ dev_err(dev, "Device sent too small first packet: %u < %u\n",
+ actual, USBTMC_HEADER_SIZE);
+ if (file_data->auto_abort)
+ usbtmc_ioctl_abort_bulk_in(data);
+ goto exit;
+ }
- /* How many characters did the instrument send? */
- n_characters = buffer[4] +
- (buffer[5] << 8) +
- (buffer[6] << 16) +
- (buffer[7] << 24);
+ if (buffer[0] != 2) {
+ dev_err(dev, "Device sent reply with wrong MsgID: %u != 2\n",
+ buffer[0]);
+ if (file_data->auto_abort)
+ usbtmc_ioctl_abort_bulk_in(data);
+ goto exit;
+ }
- if (n_characters > this_part) {
- dev_err(dev, "Device wants to return more data than requested: %u > %zu\n", n_characters, count);
- if (data->auto_abort)
- usbtmc_ioctl_abort_bulk_in(data);
- goto exit;
- }
+ if (buffer[1] != data->bTag_last_write) {
+ dev_err(dev, "Device sent reply with wrong bTag: %u != %u\n",
+ buffer[1], data->bTag_last_write);
+ if (file_data->auto_abort)
+ usbtmc_ioctl_abort_bulk_in(data);
+ goto exit;
+ }
- /* Remove the USBTMC header */
- actual -= USBTMC_HEADER_SIZE;
+ /* How many characters did the instrument send? */
+ n_characters = buffer[4] +
+ (buffer[5] << 8) +
+ (buffer[6] << 16) +
+ (buffer[7] << 24);
- /* Check if the message is smaller than requested */
- if (data->rigol_quirk) {
- if (remaining > n_characters)
- remaining = n_characters;
- /* Remove padding if it exists */
- if (actual > remaining)
- actual = remaining;
- }
- else {
- if (this_part > n_characters)
- this_part = n_characters;
- /* Remove padding if it exists */
- if (actual > this_part)
- actual = this_part;
- }
+ file_data->bmTransferAttributes = buffer[8];
- dev_dbg(dev, "Bulk-IN header: N_characters(%u), bTransAttr(%u)\n", n_characters, buffer[8]);
+ dev_dbg(dev, "Bulk-IN header: N_characters(%u), bTransAttr(%u)\n",
+ n_characters, buffer[8]);
- remaining -= actual;
+ if (n_characters > remaining) {
+ dev_err(dev, "Device wants to return more data than requested: %u > %zu\n",
+ n_characters, count);
+ if (file_data->auto_abort)
+ usbtmc_ioctl_abort_bulk_in(data);
+ goto exit;
+ }
- /* Terminate if end-of-message bit received from device */
- if ((buffer[8] & 0x01) && (actual >= n_characters))
- remaining = 0;
+ print_hex_dump_debug("usbtmc ", DUMP_PREFIX_NONE,
+ 16, 1, buffer, actual, true);
- dev_dbg(dev, "Bulk-IN header: remaining(%zu), buf(%p), buffer(%p) done(%zu)\n", remaining,buf,buffer,done);
+ remaining = n_characters;
+ /* Remove the USBTMC header */
+ actual -= USBTMC_HEADER_SIZE;
- /* Copy buffer to user space */
- if (copy_to_user(buf + done, &buffer[USBTMC_HEADER_SIZE], actual)) {
- /* There must have been an addressing problem */
- retval = -EFAULT;
- goto exit;
- }
- done += actual;
- }
- else {
- if (actual > remaining)
- actual = remaining;
+ /* Remove padding if it exists */
+ if (actual > remaining)
+ actual = remaining;
- remaining -= actual;
+ remaining -= actual;
- dev_dbg(dev, "Bulk-IN header cont: actual(%u), done(%zu), remaining(%zu), buf(%p), buffer(%p)\n", actual, done, remaining,buf,buffer);
+ /* Copy buffer to user space */
+ if (copy_to_user(buf, &buffer[USBTMC_HEADER_SIZE], actual)) {
+ /* There must have been an addressing problem */
+ retval = -EFAULT;
+ goto exit;
+ }
- /* Copy buffer to user space */
- if (copy_to_user(buf + done, buffer, actual)) {
- /* There must have been an addressing problem */
- retval = -EFAULT;
- goto exit;
- }
- done += actual;
- }
+ if ((actual + USBTMC_HEADER_SIZE) == bufsize) {
+ retval = usbtmc_generic_read(file_data, buf + actual,
+ remaining,
+ &done,
+ USBTMC_FLAG_IGNORE_TRAILER);
+ if (retval < 0)
+ goto exit;
}
+ done += actual;
/* Update file position value */
*f_pos = *f_pos + done;
@@ -763,113 +1480,154 @@ exit:
static ssize_t usbtmc_write(struct file *filp, const char __user *buf,
size_t count, loff_t *f_pos)
{
+ struct usbtmc_file_data *file_data;
struct usbtmc_device_data *data;
+ struct urb *urb = NULL;
+ ssize_t retval = 0;
u8 *buffer;
- int retval;
- int actual;
- unsigned long int n_bytes;
- int remaining;
- int done;
- int this_part;
+ u32 remaining, done;
+ u32 transfersize, aligned, buflen;
- data = filp->private_data;
-
- buffer = kmalloc(USBTMC_SIZE_IOBUFFER, GFP_KERNEL);
- if (!buffer)
- return -ENOMEM;
+ file_data = filp->private_data;
+ data = file_data->data;
mutex_lock(&data->io_mutex);
+
if (data->zombie) {
retval = -ENODEV;
goto exit;
}
- remaining = count;
done = 0;
- while (remaining > 0) {
- if (remaining > USBTMC_SIZE_IOBUFFER - USBTMC_HEADER_SIZE) {
- this_part = USBTMC_SIZE_IOBUFFER - USBTMC_HEADER_SIZE;
- buffer[8] = 0;
- } else {
- this_part = remaining;
- buffer[8] = 1;
- }
+ spin_lock_irq(&file_data->err_lock);
+ file_data->out_transfer_size = 0;
+ file_data->out_status = 0;
+ spin_unlock_irq(&file_data->err_lock);
- /* Setup IO buffer for DEV_DEP_MSG_OUT message */
- buffer[0] = 1;
- buffer[1] = data->bTag;
- buffer[2] = ~data->bTag;
- buffer[3] = 0; /* Reserved */
- buffer[4] = this_part >> 0;
- buffer[5] = this_part >> 8;
- buffer[6] = this_part >> 16;
- buffer[7] = this_part >> 24;
- /* buffer[8] is set above... */
- buffer[9] = 0; /* Reserved */
- buffer[10] = 0; /* Reserved */
- buffer[11] = 0; /* Reserved */
-
- if (copy_from_user(&buffer[USBTMC_HEADER_SIZE], buf + done, this_part)) {
- retval = -EFAULT;
- goto exit;
- }
+ if (!count)
+ goto exit;
- n_bytes = roundup(USBTMC_HEADER_SIZE + this_part, 4);
- memset(buffer + USBTMC_HEADER_SIZE + this_part, 0, n_bytes - (USBTMC_HEADER_SIZE + this_part));
+ if (down_trylock(&file_data->limit_write_sem)) {
+ /* previous calls were async */
+ retval = -EBUSY;
+ goto exit;
+ }
- do {
- retval = usb_bulk_msg(data->usb_dev,
- usb_sndbulkpipe(data->usb_dev,
- data->bulk_out),
- buffer, n_bytes,
- &actual, USBTMC_TIMEOUT);
- if (retval != 0)
- break;
- n_bytes -= actual;
- } while (n_bytes);
-
- data->bTag_last_write = data->bTag;
+ urb = usbtmc_create_urb();
+ if (!urb) {
+ retval = -ENOMEM;
+ up(&file_data->limit_write_sem);
+ goto exit;
+ }
+
+ buffer = urb->transfer_buffer;
+ buflen = urb->transfer_buffer_length;
+
+ if (count > INT_MAX) {
+ transfersize = INT_MAX;
+ buffer[8] = 0;
+ } else {
+ transfersize = count;
+ buffer[8] = file_data->eom_val;
+ }
+
+ /* Setup IO buffer for DEV_DEP_MSG_OUT message */
+ buffer[0] = 1;
+ buffer[1] = data->bTag;
+ buffer[2] = ~data->bTag;
+ buffer[3] = 0; /* Reserved */
+ buffer[4] = transfersize >> 0;
+ buffer[5] = transfersize >> 8;
+ buffer[6] = transfersize >> 16;
+ buffer[7] = transfersize >> 24;
+ /* buffer[8] is set above... */
+ buffer[9] = 0; /* Reserved */
+ buffer[10] = 0; /* Reserved */
+ buffer[11] = 0; /* Reserved */
+
+ remaining = transfersize;
+
+ if (transfersize + USBTMC_HEADER_SIZE > buflen) {
+ transfersize = buflen - USBTMC_HEADER_SIZE;
+ aligned = buflen;
+ } else {
+ aligned = (transfersize + (USBTMC_HEADER_SIZE + 3)) & ~3;
+ }
+
+ if (copy_from_user(&buffer[USBTMC_HEADER_SIZE], buf, transfersize)) {
+ retval = -EFAULT;
+ up(&file_data->limit_write_sem);
+ goto exit;
+ }
+
+ dev_dbg(&data->intf->dev, "%s(size:%u align:%u)\n", __func__,
+ (unsigned int)transfersize, (unsigned int)aligned);
+
+ print_hex_dump_debug("usbtmc ", DUMP_PREFIX_NONE,
+ 16, 1, buffer, aligned, true);
+
+ usb_fill_bulk_urb(urb, data->usb_dev,
+ usb_sndbulkpipe(data->usb_dev, data->bulk_out),
+ urb->transfer_buffer, aligned,
+ usbtmc_write_bulk_cb, file_data);
+
+ usb_anchor_urb(urb, &file_data->submitted);
+ retval = usb_submit_urb(urb, GFP_KERNEL);
+ if (unlikely(retval)) {
+ usb_unanchor_urb(urb);
+ up(&file_data->limit_write_sem);
+ goto exit;
+ }
+
+ remaining -= transfersize;
+
+ data->bTag_last_write = data->bTag;
+ data->bTag++;
+
+ if (!data->bTag)
data->bTag++;
- if (!data->bTag)
- data->bTag++;
+ /* call generic_write even when remaining = 0 */
+ retval = usbtmc_generic_write(file_data, buf + transfersize, remaining,
+ &done, USBTMC_FLAG_APPEND);
+ /* truncate alignment bytes */
+ if (done > remaining)
+ done = remaining;
- if (retval < 0) {
- dev_err(&data->intf->dev,
- "Unable to send data, error %d\n", retval);
- if (data->auto_abort)
- usbtmc_ioctl_abort_bulk_out(data);
- goto exit;
- }
+ /*add size of first urb*/
+ done += transfersize;
- remaining -= this_part;
- done += this_part;
+ if (retval < 0) {
+ usb_kill_anchored_urbs(&file_data->submitted);
+
+ dev_err(&data->intf->dev,
+ "Unable to send data, error %d\n", (int)retval);
+ if (file_data->auto_abort)
+ usbtmc_ioctl_abort_bulk_out(data);
+ goto exit;
}
- retval = count;
+ retval = done;
exit:
+ usb_free_urb(urb);
mutex_unlock(&data->io_mutex);
- kfree(buffer);
return retval;
}
static int usbtmc_ioctl_clear(struct usbtmc_device_data *data)
{
- struct usb_host_interface *current_setting;
- struct usb_endpoint_descriptor *desc;
struct device *dev;
u8 *buffer;
int rv;
int n;
int actual = 0;
- int max_size;
dev = &data->intf->dev;
dev_dbg(dev, "Sending INITIATE_CLEAR request\n");
- buffer = kmalloc(USBTMC_SIZE_IOBUFFER, GFP_KERNEL);
+ buffer = kmalloc(USBTMC_BUFSIZE, GFP_KERNEL);
if (!buffer)
return -ENOMEM;
@@ -877,7 +1635,7 @@ static int usbtmc_ioctl_clear(struct usbtmc_device_data *data)
usb_rcvctrlpipe(data->usb_dev, 0),
USBTMC_REQUEST_INITIATE_CLEAR,
USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
- 0, 0, buffer, 1, USBTMC_TIMEOUT);
+ 0, 0, buffer, 1, USB_CTRL_GET_TIMEOUT);
if (rv < 0) {
dev_err(dev, "usb_control_msg returned %d\n", rv);
goto exit;
@@ -891,22 +1649,6 @@ static int usbtmc_ioctl_clear(struct usbtmc_device_data *data)
goto exit;
}
- max_size = 0;
- current_setting = data->intf->cur_altsetting;
- for (n = 0; n < current_setting->desc.bNumEndpoints; n++) {
- desc = &current_setting->endpoint[n].desc;
- if (desc->bEndpointAddress == data->bulk_in)
- max_size = usb_endpoint_maxp(desc);
- }
-
- if (max_size == 0) {
- dev_err(dev, "Couldn't get wMaxPacketSize\n");
- rv = -EPERM;
- goto exit;
- }
-
- dev_dbg(dev, "wMaxPacketSize is %d\n", max_size);
-
n = 0;
usbtmc_clear_check_status:
@@ -917,7 +1659,7 @@ usbtmc_clear_check_status:
usb_rcvctrlpipe(data->usb_dev, 0),
USBTMC_REQUEST_CHECK_CLEAR_STATUS,
USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
- 0, 0, buffer, 2, USBTMC_TIMEOUT);
+ 0, 0, buffer, 2, USB_CTRL_GET_TIMEOUT);
if (rv < 0) {
dev_err(dev, "usb_control_msg returned %d\n", rv);
goto exit;
@@ -934,15 +1676,20 @@ usbtmc_clear_check_status:
goto exit;
}
- if (buffer[1] == 1)
+ if ((buffer[1] & 1) != 0) {
do {
dev_dbg(dev, "Reading from bulk in EP\n");
+ actual = 0;
rv = usb_bulk_msg(data->usb_dev,
usb_rcvbulkpipe(data->usb_dev,
data->bulk_in),
- buffer, USBTMC_SIZE_IOBUFFER,
- &actual, USBTMC_TIMEOUT);
+ buffer, USBTMC_BUFSIZE,
+ &actual, USB_CTRL_GET_TIMEOUT);
+
+ print_hex_dump_debug("usbtmc ", DUMP_PREFIX_NONE,
+ 16, 1, buffer, actual, true);
+
n++;
if (rv < 0) {
@@ -950,10 +1697,15 @@ usbtmc_clear_check_status:
rv);
goto exit;
}
- } while ((actual == max_size) &&
+ } while ((actual == USBTMC_BUFSIZE) &&
(n < USBTMC_MAX_READS_TO_CLEAR_BULK_IN));
+ } else {
+ /* do not stress device with subsequent requests */
+ msleep(50);
+ n++;
+ }
- if (actual == max_size) {
+ if (n >= USBTMC_MAX_READS_TO_CLEAR_BULK_IN) {
dev_err(dev, "Couldn't clear device buffer within %d cycles\n",
USBTMC_MAX_READS_TO_CLEAR_BULK_IN);
rv = -EPERM;
@@ -967,7 +1719,7 @@ usbtmc_clear_bulk_out_halt:
rv = usb_clear_halt(data->usb_dev,
usb_sndbulkpipe(data->usb_dev, data->bulk_out));
if (rv < 0) {
- dev_err(dev, "usb_control_msg returned %d\n", rv);
+ dev_err(dev, "usb_clear_halt returned %d\n", rv);
goto exit;
}
rv = 0;
@@ -984,12 +1736,9 @@ static int usbtmc_ioctl_clear_out_halt(struct usbtmc_device_data *data)
rv = usb_clear_halt(data->usb_dev,
usb_sndbulkpipe(data->usb_dev, data->bulk_out));
- if (rv < 0) {
- dev_err(&data->usb_dev->dev, "usb_control_msg returned %d\n",
- rv);
- return rv;
- }
- return 0;
+ if (rv < 0)
+ dev_err(&data->usb_dev->dev, "%s returned %d\n", __func__, rv);
+ return rv;
}
static int usbtmc_ioctl_clear_in_halt(struct usbtmc_device_data *data)
@@ -999,11 +1748,33 @@ static int usbtmc_ioctl_clear_in_halt(struct usbtmc_device_data *data)
rv = usb_clear_halt(data->usb_dev,
usb_rcvbulkpipe(data->usb_dev, data->bulk_in));
- if (rv < 0) {
- dev_err(&data->usb_dev->dev, "usb_control_msg returned %d\n",
- rv);
- return rv;
- }
+ if (rv < 0)
+ dev_err(&data->usb_dev->dev, "%s returned %d\n", __func__, rv);
+ return rv;
+}
+
+static int usbtmc_ioctl_cancel_io(struct usbtmc_file_data *file_data)
+{
+ spin_lock_irq(&file_data->err_lock);
+ file_data->in_status = -ECANCELED;
+ file_data->out_status = -ECANCELED;
+ spin_unlock_irq(&file_data->err_lock);
+ usb_kill_anchored_urbs(&file_data->submitted);
+ return 0;
+}
+
+static int usbtmc_ioctl_cleanup_io(struct usbtmc_file_data *file_data)
+{
+ usb_kill_anchored_urbs(&file_data->submitted);
+ usb_scuttle_anchored_urbs(&file_data->in_anchor);
+ spin_lock_irq(&file_data->err_lock);
+ file_data->in_status = 0;
+ file_data->in_transfer_size = 0;
+ file_data->out_status = 0;
+ file_data->out_transfer_size = 0;
+ spin_unlock_irq(&file_data->err_lock);
+
+ file_data->in_urbs_used = 0;
return 0;
}
@@ -1020,7 +1791,7 @@ static int get_capabilities(struct usbtmc_device_data *data)
rv = usb_control_msg(data->usb_dev, usb_rcvctrlpipe(data->usb_dev, 0),
USBTMC_REQUEST_GET_CAPABILITIES,
USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
- 0, 0, buffer, 0x18, USBTMC_TIMEOUT);
+ 0, 0, buffer, 0x18, USB_CTRL_GET_TIMEOUT);
if (rv < 0) {
dev_err(dev, "usb_control_msg returned %d\n", rv);
goto err_out;
@@ -1077,72 +1848,6 @@ static const struct attribute_group capability_attr_grp = {
.attrs = capability_attrs,
};
-static ssize_t TermChar_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct usb_interface *intf = to_usb_interface(dev);
- struct usbtmc_device_data *data = usb_get_intfdata(intf);
-
- return sprintf(buf, "%c\n", data->TermChar);
-}
-
-static ssize_t TermChar_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct usb_interface *intf = to_usb_interface(dev);
- struct usbtmc_device_data *data = usb_get_intfdata(intf);
-
- if (count < 1)
- return -EINVAL;
- data->TermChar = buf[0];
- return count;
-}
-static DEVICE_ATTR_RW(TermChar);
-
-#define data_attribute(name) \
-static ssize_t name##_show(struct device *dev, \
- struct device_attribute *attr, char *buf) \
-{ \
- struct usb_interface *intf = to_usb_interface(dev); \
- struct usbtmc_device_data *data = usb_get_intfdata(intf); \
- \
- return sprintf(buf, "%d\n", data->name); \
-} \
-static ssize_t name##_store(struct device *dev, \
- struct device_attribute *attr, \
- const char *buf, size_t count) \
-{ \
- struct usb_interface *intf = to_usb_interface(dev); \
- struct usbtmc_device_data *data = usb_get_intfdata(intf); \
- ssize_t result; \
- unsigned val; \
- \
- result = sscanf(buf, "%u\n", &val); \
- if (result != 1) \
- result = -EINVAL; \
- data->name = val; \
- if (result < 0) \
- return result; \
- else \
- return count; \
-} \
-static DEVICE_ATTR_RW(name)
-
-data_attribute(TermCharEnabled);
-data_attribute(auto_abort);
-
-static struct attribute *data_attrs[] = {
- &dev_attr_TermChar.attr,
- &dev_attr_TermCharEnabled.attr,
- &dev_attr_auto_abort.attr,
- NULL,
-};
-
-static const struct attribute_group data_attr_grp = {
- .attrs = data_attrs,
-};
-
static int usbtmc_ioctl_indicator_pulse(struct usbtmc_device_data *data)
{
struct device *dev;
@@ -1159,7 +1864,7 @@ static int usbtmc_ioctl_indicator_pulse(struct usbtmc_device_data *data)
usb_rcvctrlpipe(data->usb_dev, 0),
USBTMC_REQUEST_INDICATOR_PULSE,
USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
- 0, 0, buffer, 0x01, USBTMC_TIMEOUT);
+ 0, 0, buffer, 0x01, USB_CTRL_GET_TIMEOUT);
if (rv < 0) {
dev_err(dev, "usb_control_msg returned %d\n", rv);
@@ -1180,12 +1885,149 @@ exit:
return rv;
}
+static int usbtmc_ioctl_request(struct usbtmc_device_data *data,
+ void __user *arg)
+{
+ struct device *dev = &data->intf->dev;
+ struct usbtmc_ctrlrequest request;
+ u8 *buffer = NULL;
+ int rv;
+ unsigned long res;
+
+ res = copy_from_user(&request, arg, sizeof(struct usbtmc_ctrlrequest));
+ if (res)
+ return -EFAULT;
+
+ if (request.req.wLength > USBTMC_BUFSIZE)
+ return -EMSGSIZE;
+
+ if (request.req.wLength) {
+ buffer = kmalloc(request.req.wLength, GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+
+ if ((request.req.bRequestType & USB_DIR_IN) == 0) {
+ /* Send control data to device */
+ res = copy_from_user(buffer, request.data,
+ request.req.wLength);
+ if (res) {
+ rv = -EFAULT;
+ goto exit;
+ }
+ }
+ }
+
+ rv = usb_control_msg(data->usb_dev,
+ usb_rcvctrlpipe(data->usb_dev, 0),
+ request.req.bRequest,
+ request.req.bRequestType,
+ request.req.wValue,
+ request.req.wIndex,
+ buffer, request.req.wLength, USB_CTRL_GET_TIMEOUT);
+
+ if (rv < 0) {
+ dev_err(dev, "%s failed %d\n", __func__, rv);
+ goto exit;
+ }
+
+ if (rv && (request.req.bRequestType & USB_DIR_IN)) {
+ /* Read control data from device */
+ res = copy_to_user(request.data, buffer, rv);
+ if (res)
+ rv = -EFAULT;
+ }
+
+ exit:
+ kfree(buffer);
+ return rv;
+}
+
+/*
+ * Get the usb timeout value
+ */
+static int usbtmc_ioctl_get_timeout(struct usbtmc_file_data *file_data,
+ void __user *arg)
+{
+ u32 timeout;
+
+ timeout = file_data->timeout;
+
+ return put_user(timeout, (__u32 __user *)arg);
+}
+
+/*
+ * Set the usb timeout value
+ */
+static int usbtmc_ioctl_set_timeout(struct usbtmc_file_data *file_data,
+ void __user *arg)
+{
+ u32 timeout;
+
+ if (get_user(timeout, (__u32 __user *)arg))
+ return -EFAULT;
+
+ /* Note that timeout = 0 means
+ * MAX_SCHEDULE_TIMEOUT in usb_control_msg
+ */
+ if (timeout < USBTMC_MIN_TIMEOUT)
+ return -EINVAL;
+
+ file_data->timeout = timeout;
+
+ return 0;
+}
+
+/*
+ * enables/disables sending EOM on write
+ */
+static int usbtmc_ioctl_eom_enable(struct usbtmc_file_data *file_data,
+ void __user *arg)
+{
+ u8 eom_enable;
+
+ if (copy_from_user(&eom_enable, arg, sizeof(eom_enable)))
+ return -EFAULT;
+
+ if (eom_enable > 1)
+ return -EINVAL;
+
+ file_data->eom_val = eom_enable;
+
+ return 0;
+}
+
+/*
+ * Configure termination character for read()
+ */
+static int usbtmc_ioctl_config_termc(struct usbtmc_file_data *file_data,
+ void __user *arg)
+{
+ struct usbtmc_termchar termc;
+
+ if (copy_from_user(&termc, arg, sizeof(termc)))
+ return -EFAULT;
+
+ if ((termc.term_char_enabled > 1) ||
+ (termc.term_char_enabled &&
+ !(file_data->data->capabilities.device_capabilities & 1)))
+ return -EINVAL;
+
+ file_data->term_char = termc.term_char;
+ file_data->term_char_enabled = termc.term_char_enabled;
+
+ return 0;
+}
+
static long usbtmc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
+ struct usbtmc_file_data *file_data;
struct usbtmc_device_data *data;
int retval = -EBADRQC;
+ __u8 tmp_byte;
+
+ file_data = file->private_data;
+ data = file_data->data;
- data = file->private_data;
mutex_lock(&data->io_mutex);
if (data->zombie) {
retval = -ENODEV;
@@ -1217,16 +2059,58 @@ static long usbtmc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
retval = usbtmc_ioctl_abort_bulk_in(data);
break;
+ case USBTMC_IOCTL_CTRL_REQUEST:
+ retval = usbtmc_ioctl_request(data, (void __user *)arg);
+ break;
+
+ case USBTMC_IOCTL_GET_TIMEOUT:
+ retval = usbtmc_ioctl_get_timeout(file_data,
+ (void __user *)arg);
+ break;
+
+ case USBTMC_IOCTL_SET_TIMEOUT:
+ retval = usbtmc_ioctl_set_timeout(file_data,
+ (void __user *)arg);
+ break;
+
+ case USBTMC_IOCTL_EOM_ENABLE:
+ retval = usbtmc_ioctl_eom_enable(file_data,
+ (void __user *)arg);
+ break;
+
+ case USBTMC_IOCTL_CONFIG_TERMCHAR:
+ retval = usbtmc_ioctl_config_termc(file_data,
+ (void __user *)arg);
+ break;
+
+ case USBTMC_IOCTL_WRITE:
+ retval = usbtmc_ioctl_generic_write(file_data,
+ (void __user *)arg);
+ break;
+
+ case USBTMC_IOCTL_READ:
+ retval = usbtmc_ioctl_generic_read(file_data,
+ (void __user *)arg);
+ break;
+
+ case USBTMC_IOCTL_WRITE_RESULT:
+ retval = usbtmc_ioctl_write_result(file_data,
+ (void __user *)arg);
+ break;
+
+ case USBTMC_IOCTL_API_VERSION:
+ retval = put_user(USBTMC_API_VERSION,
+ (__u32 __user *)arg);
+ break;
+
case USBTMC488_IOCTL_GET_CAPS:
- retval = copy_to_user((void __user *)arg,
- &data->usb488_caps,
- sizeof(data->usb488_caps));
- if (retval)
- retval = -EFAULT;
+ retval = put_user(data->usb488_caps,
+ (unsigned char __user *)arg);
break;
case USBTMC488_IOCTL_READ_STB:
- retval = usbtmc488_ioctl_read_stb(data, (void __user *)arg);
+ retval = usbtmc488_ioctl_read_stb(file_data,
+ (void __user *)arg);
break;
case USBTMC488_IOCTL_REN_CONTROL:
@@ -1243,6 +2127,34 @@ static long usbtmc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
retval = usbtmc488_ioctl_simple(data, (void __user *)arg,
USBTMC488_REQUEST_LOCAL_LOCKOUT);
break;
+
+ case USBTMC488_IOCTL_TRIGGER:
+ retval = usbtmc488_ioctl_trigger(file_data);
+ break;
+
+ case USBTMC488_IOCTL_WAIT_SRQ:
+ retval = usbtmc488_ioctl_wait_srq(file_data,
+ (__u32 __user *)arg);
+ break;
+
+ case USBTMC_IOCTL_MSG_IN_ATTR:
+ retval = put_user(file_data->bmTransferAttributes,
+ (__u8 __user *)arg);
+ break;
+
+ case USBTMC_IOCTL_AUTO_ABORT:
+ retval = get_user(tmp_byte, (unsigned char __user *)arg);
+ if (retval == 0)
+ file_data->auto_abort = !!tmp_byte;
+ break;
+
+ case USBTMC_IOCTL_CANCEL_IO:
+ retval = usbtmc_ioctl_cancel_io(file_data);
+ break;
+
+ case USBTMC_IOCTL_CLEANUP_IO:
+ retval = usbtmc_ioctl_cleanup_io(file_data);
+ break;
}
skip_io_on_zombie:
@@ -1252,14 +2164,15 @@ skip_io_on_zombie:
static int usbtmc_fasync(int fd, struct file *file, int on)
{
- struct usbtmc_device_data *data = file->private_data;
+ struct usbtmc_file_data *file_data = file->private_data;
- return fasync_helper(fd, file, on, &data->fasync);
+ return fasync_helper(fd, file, on, &file_data->data->fasync);
}
static __poll_t usbtmc_poll(struct file *file, poll_table *wait)
{
- struct usbtmc_device_data *data = file->private_data;
+ struct usbtmc_file_data *file_data = file->private_data;
+ struct usbtmc_device_data *data = file_data->data;
__poll_t mask;
mutex_lock(&data->io_mutex);
@@ -1271,7 +2184,28 @@ static __poll_t usbtmc_poll(struct file *file, poll_table *wait)
poll_wait(file, &data->waitq, wait);
- mask = (atomic_read(&data->srq_asserted)) ? EPOLLIN | EPOLLRDNORM : 0;
+ /* Note that EPOLLPRI is now assigned to SRQ, and
+ * EPOLLIN|EPOLLRDNORM to normal read data.
+ */
+ mask = 0;
+ if (atomic_read(&file_data->srq_asserted))
+ mask |= EPOLLPRI;
+
+ /* Note that the anchor submitted includes all urbs for BULK IN
+ * and OUT. So EPOLLOUT is signaled when BULK OUT is empty and
+ * all BULK IN urbs are completed and moved to in_anchor.
+ */
+ if (usb_anchor_empty(&file_data->submitted))
+ mask |= (EPOLLOUT | EPOLLWRNORM);
+ if (!usb_anchor_empty(&file_data->in_anchor))
+ mask |= (EPOLLIN | EPOLLRDNORM);
+
+ spin_lock_irq(&file_data->err_lock);
+ if (file_data->in_status || file_data->out_status)
+ mask |= EPOLLERR;
+ spin_unlock_irq(&file_data->err_lock);
+
+ dev_dbg(&data->intf->dev, "poll mask = %x\n", mask);
no_poll:
mutex_unlock(&data->io_mutex);
@@ -1284,7 +2218,11 @@ static const struct file_operations fops = {
.write = usbtmc_write,
.open = usbtmc_open,
.release = usbtmc_release,
+ .flush = usbtmc_flush,
.unlocked_ioctl = usbtmc_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = usbtmc_ioctl,
+#endif
.fasync = usbtmc_fasync,
.poll = usbtmc_poll,
.llseek = default_llseek,
@@ -1318,15 +2256,33 @@ static void usbtmc_interrupt(struct urb *urb)
}
/* check for SRQ notification */
if (data->iin_buffer[0] == 0x81) {
+ unsigned long flags;
+ struct list_head *elem;
+
if (data->fasync)
kill_fasync(&data->fasync,
- SIGIO, POLL_IN);
+ SIGIO, POLL_PRI);
- atomic_set(&data->srq_asserted, 1);
- wake_up_interruptible(&data->waitq);
+ spin_lock_irqsave(&data->dev_lock, flags);
+ list_for_each(elem, &data->file_list) {
+ struct usbtmc_file_data *file_data;
+
+ file_data = list_entry(elem,
+ struct usbtmc_file_data,
+ file_elem);
+ file_data->srq_byte = data->iin_buffer[1];
+ atomic_set(&file_data->srq_asserted, 1);
+ }
+ spin_unlock_irqrestore(&data->dev_lock, flags);
+
+ dev_dbg(dev, "srq received bTag %x stb %x\n",
+ (unsigned int)data->iin_buffer[0],
+ (unsigned int)data->iin_buffer[1]);
+ wake_up_interruptible_all(&data->waitq);
goto exit;
}
- dev_warn(dev, "invalid notification: %x\n", data->iin_buffer[0]);
+ dev_warn(dev, "invalid notification: %x\n",
+ data->iin_buffer[0]);
break;
case -EOVERFLOW:
dev_err(dev, "overflow with length %d, actual length is %d\n",
@@ -1337,6 +2293,7 @@ static void usbtmc_interrupt(struct urb *urb)
case -ESHUTDOWN:
case -EILSEQ:
case -ETIME:
+ case -EPIPE:
/* urb terminated, clean up */
dev_dbg(dev, "urb terminated, status: %d\n", status);
return;
@@ -1355,7 +2312,9 @@ static void usbtmc_free_int(struct usbtmc_device_data *data)
return;
usb_kill_urb(data->iin_urb);
kfree(data->iin_buffer);
+ data->iin_buffer = NULL;
usb_free_urb(data->iin_urb);
+ data->iin_urb = NULL;
kref_put(&data->kref, usbtmc_delete);
}
@@ -1365,7 +2324,6 @@ static int usbtmc_probe(struct usb_interface *intf,
struct usbtmc_device_data *data;
struct usb_host_interface *iface_desc;
struct usb_endpoint_descriptor *bulk_in, *bulk_out, *int_in;
- int n;
int retcode;
dev_dbg(&intf->dev, "%s called\n", __func__);
@@ -1382,27 +2340,13 @@ static int usbtmc_probe(struct usb_interface *intf,
mutex_init(&data->io_mutex);
init_waitqueue_head(&data->waitq);
atomic_set(&data->iin_data_valid, 0);
- atomic_set(&data->srq_asserted, 0);
- data->zombie = 0;
+ INIT_LIST_HEAD(&data->file_list);
+ spin_lock_init(&data->dev_lock);
- /* Determine if it is a Rigol or not */
- data->rigol_quirk = 0;
- dev_dbg(&intf->dev, "Trying to find if device Vendor 0x%04X Product 0x%04X has the RIGOL quirk\n",
- le16_to_cpu(data->usb_dev->descriptor.idVendor),
- le16_to_cpu(data->usb_dev->descriptor.idProduct));
- for(n = 0; usbtmc_id_quirk[n].idVendor > 0; n++) {
- if ((usbtmc_id_quirk[n].idVendor == le16_to_cpu(data->usb_dev->descriptor.idVendor)) &&
- (usbtmc_id_quirk[n].idProduct == le16_to_cpu(data->usb_dev->descriptor.idProduct))) {
- dev_dbg(&intf->dev, "Setting this device as having the RIGOL quirk\n");
- data->rigol_quirk = 1;
- break;
- }
- }
+ data->zombie = 0;
/* Initialize USBTMC bTag and other fields */
data->bTag = 1;
- data->TermCharEnabled = 0;
- data->TermChar = '\n';
/* 2 <= bTag <= 127 USBTMC-USB488 subclass specification 4.3.1 */
data->iin_bTag = 2;
@@ -1419,6 +2363,7 @@ static int usbtmc_probe(struct usb_interface *intf,
}
data->bulk_in = bulk_in->bEndpointAddress;
+ data->wMaxPacketSize = usb_endpoint_maxp(bulk_in);
dev_dbg(&intf->dev, "Found bulk in endpoint at %u\n", data->bulk_in);
data->bulk_out = bulk_out->bEndpointAddress;
@@ -1475,12 +2420,10 @@ static int usbtmc_probe(struct usb_interface *intf,
}
}
- retcode = sysfs_create_group(&intf->dev.kobj, &data_attr_grp);
-
retcode = usb_register_dev(intf, &usbtmc_class);
if (retcode) {
- dev_err(&intf->dev, "Not able to get a minor"
- " (base %u, slice default): %d\n", USBTMC_MINOR_BASE,
+ dev_err(&intf->dev, "Not able to get a minor (base %u, slice default): %d\n",
+ USBTMC_MINOR_BASE,
retcode);
goto error_register;
}
@@ -1490,7 +2433,6 @@ static int usbtmc_probe(struct usb_interface *intf,
error_register:
sysfs_remove_group(&intf->dev.kobj, &capability_attr_grp);
- sysfs_remove_group(&intf->dev.kobj, &data_attr_grp);
usbtmc_free_int(data);
err_put:
kref_put(&data->kref, usbtmc_delete);
@@ -1499,30 +2441,104 @@ err_put:
static void usbtmc_disconnect(struct usb_interface *intf)
{
- struct usbtmc_device_data *data;
-
- dev_dbg(&intf->dev, "usbtmc_disconnect called\n");
+ struct usbtmc_device_data *data = usb_get_intfdata(intf);
+ struct list_head *elem;
- data = usb_get_intfdata(intf);
usb_deregister_dev(intf, &usbtmc_class);
sysfs_remove_group(&intf->dev.kobj, &capability_attr_grp);
- sysfs_remove_group(&intf->dev.kobj, &data_attr_grp);
mutex_lock(&data->io_mutex);
data->zombie = 1;
- wake_up_all(&data->waitq);
+ wake_up_interruptible_all(&data->waitq);
+ list_for_each(elem, &data->file_list) {
+ struct usbtmc_file_data *file_data;
+
+ file_data = list_entry(elem,
+ struct usbtmc_file_data,
+ file_elem);
+ usb_kill_anchored_urbs(&file_data->submitted);
+ usb_scuttle_anchored_urbs(&file_data->in_anchor);
+ }
mutex_unlock(&data->io_mutex);
usbtmc_free_int(data);
kref_put(&data->kref, usbtmc_delete);
}
+static void usbtmc_draw_down(struct usbtmc_file_data *file_data)
+{
+ int time;
+
+ time = usb_wait_anchor_empty_timeout(&file_data->submitted, 1000);
+ if (!time)
+ usb_kill_anchored_urbs(&file_data->submitted);
+ usb_scuttle_anchored_urbs(&file_data->in_anchor);
+}
+
static int usbtmc_suspend(struct usb_interface *intf, pm_message_t message)
{
- /* this driver does not have pending URBs */
+ struct usbtmc_device_data *data = usb_get_intfdata(intf);
+ struct list_head *elem;
+
+ if (!data)
+ return 0;
+
+ mutex_lock(&data->io_mutex);
+ list_for_each(elem, &data->file_list) {
+ struct usbtmc_file_data *file_data;
+
+ file_data = list_entry(elem,
+ struct usbtmc_file_data,
+ file_elem);
+ usbtmc_draw_down(file_data);
+ }
+
+ if (data->iin_ep_present && data->iin_urb)
+ usb_kill_urb(data->iin_urb);
+
+ mutex_unlock(&data->io_mutex);
return 0;
}
static int usbtmc_resume(struct usb_interface *intf)
{
+ struct usbtmc_device_data *data = usb_get_intfdata(intf);
+ int retcode = 0;
+
+ if (data->iin_ep_present && data->iin_urb)
+ retcode = usb_submit_urb(data->iin_urb, GFP_KERNEL);
+ if (retcode)
+ dev_err(&intf->dev, "Failed to submit iin_urb\n");
+
+ return retcode;
+}
+
+static int usbtmc_pre_reset(struct usb_interface *intf)
+{
+ struct usbtmc_device_data *data = usb_get_intfdata(intf);
+ struct list_head *elem;
+
+ if (!data)
+ return 0;
+
+ mutex_lock(&data->io_mutex);
+
+ list_for_each(elem, &data->file_list) {
+ struct usbtmc_file_data *file_data;
+
+ file_data = list_entry(elem,
+ struct usbtmc_file_data,
+ file_elem);
+ usbtmc_ioctl_cancel_io(file_data);
+ }
+
+ return 0;
+}
+
+static int usbtmc_post_reset(struct usb_interface *intf)
+{
+ struct usbtmc_device_data *data = usb_get_intfdata(intf);
+
+ mutex_unlock(&data->io_mutex);
+
return 0;
}
@@ -1533,6 +2549,8 @@ static struct usb_driver usbtmc_driver = {
.disconnect = usbtmc_disconnect,
.suspend = usbtmc_suspend,
.resume = usbtmc_resume,
+ .pre_reset = usbtmc_pre_reset,
+ .post_reset = usbtmc_post_reset,
};
module_usb_driver(usbtmc_driver);
diff --git a/drivers/usb/common/common.c b/drivers/usb/common/common.c
index 50a2362ed3ea..48277bbc15e4 100644
--- a/drivers/usb/common/common.c
+++ b/drivers/usb/common/common.c
@@ -246,6 +246,31 @@ int of_usb_update_otg_caps(struct device_node *np,
}
EXPORT_SYMBOL_GPL(of_usb_update_otg_caps);
+/**
+ * usb_of_get_companion_dev - Find the companion device
+ * @dev: the device pointer to find a companion
+ *
+ * Find the companion device from platform bus.
+ *
+ * Takes a reference to the returned struct device which needs to be dropped
+ * after use.
+ *
+ * Return: On success, a pointer to the companion device, %NULL on failure.
+ */
+struct device *usb_of_get_companion_dev(struct device *dev)
+{
+ struct device_node *node;
+ struct platform_device *pdev = NULL;
+
+ node = of_parse_phandle(dev->of_node, "companion", 0);
+ if (node)
+ pdev = of_find_device_by_node(node);
+
+ of_node_put(node);
+
+ return pdev ? &pdev->dev : NULL;
+}
+EXPORT_SYMBOL_GPL(usb_of_get_companion_dev);
#endif
MODULE_LICENSE("GPL");
diff --git a/drivers/usb/common/roles.c b/drivers/usb/common/roles.c
index 15cc76e22123..99116af07f1d 100644
--- a/drivers/usb/common/roles.c
+++ b/drivers/usb/common/roles.c
@@ -109,8 +109,15 @@ static void *usb_role_switch_match(struct device_connection *con, int ep,
*/
struct usb_role_switch *usb_role_switch_get(struct device *dev)
{
- return device_connection_find_match(dev, "usb-role-switch", NULL,
- usb_role_switch_match);
+ struct usb_role_switch *sw;
+
+ sw = device_connection_find_match(dev, "usb-role-switch", NULL,
+ usb_role_switch_match);
+
+ if (!IS_ERR_OR_NULL(sw))
+ WARN_ON(!try_module_get(sw->dev.parent->driver->owner));
+
+ return sw;
}
EXPORT_SYMBOL_GPL(usb_role_switch_get);
@@ -122,8 +129,10 @@ EXPORT_SYMBOL_GPL(usb_role_switch_get);
*/
void usb_role_switch_put(struct usb_role_switch *sw)
{
- if (!IS_ERR_OR_NULL(sw))
+ if (!IS_ERR_OR_NULL(sw)) {
put_device(&sw->dev);
+ module_put(sw->dev.parent->driver->owner);
+ }
}
EXPORT_SYMBOL_GPL(usb_role_switch_put);
diff --git a/drivers/usb/core/buffer.c b/drivers/usb/core/buffer.c
index 77eef8acff94..f641342cdec0 100644
--- a/drivers/usb/core/buffer.c
+++ b/drivers/usb/core/buffer.c
@@ -101,12 +101,8 @@ void hcd_buffer_destroy(struct usb_hcd *hcd)
return;
for (i = 0; i < HCD_BUFFER_POOLS; i++) {
- struct dma_pool *pool = hcd->pool[i];
-
- if (pool) {
- dma_pool_destroy(pool);
- hcd->pool[i] = NULL;
- }
+ dma_pool_destroy(hcd->pool[i]);
+ hcd->pool[i] = NULL;
}
}
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index 76e16c5251b9..a75bc0b8a50f 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -582,12 +582,13 @@ static void async_completed(struct urb *urb)
{
struct async *as = urb->context;
struct usb_dev_state *ps = as->ps;
- struct siginfo sinfo;
+ struct kernel_siginfo sinfo;
struct pid *pid = NULL;
const struct cred *cred = NULL;
+ unsigned long flags;
int signr;
- spin_lock(&ps->lock);
+ spin_lock_irqsave(&ps->lock, flags);
list_move_tail(&as->asynclist, &ps->async_completed);
as->status = urb->status;
signr = as->signr;
@@ -611,7 +612,7 @@ static void async_completed(struct urb *urb)
cancel_bulk_urbs(ps, as->bulk_addr);
wake_up(&ps->wait);
- spin_unlock(&ps->lock);
+ spin_unlock_irqrestore(&ps->lock, flags);
if (signr) {
kill_pid_info_as_cred(sinfo.si_signo, &sinfo, pid, cred);
@@ -897,7 +898,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(num_eps * sizeof(*eps), GFP_KERNEL);
+ eps = kmalloc_array(num_eps, sizeof(*eps), GFP_KERNEL);
if (!eps)
return -ENOMEM;
@@ -1433,10 +1434,13 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb
struct async *as = NULL;
struct usb_ctrlrequest *dr = NULL;
unsigned int u, totlen, isofrmlen;
- int i, ret, is_in, num_sgs = 0, ifnum = -1;
+ int i, ret, num_sgs = 0, ifnum = -1;
int number_of_packets = 0;
unsigned int stream_id = 0;
void *buf;
+ bool is_in;
+ bool allow_short = false;
+ bool allow_zero = false;
unsigned long mask = USBDEVFS_URB_SHORT_NOT_OK |
USBDEVFS_URB_BULK_CONTINUATION |
USBDEVFS_URB_NO_FSBR |
@@ -1499,6 +1503,8 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb
is_in = 0;
uurb->endpoint &= ~USB_DIR_IN;
}
+ if (is_in)
+ allow_short = true;
snoop(&ps->dev->dev, "control urb: bRequestType=%02x "
"bRequest=%02x wValue=%04x "
"wIndex=%04x wLength=%04x\n",
@@ -1510,6 +1516,10 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb
break;
case USBDEVFS_URB_TYPE_BULK:
+ if (!is_in)
+ allow_zero = true;
+ else
+ allow_short = true;
switch (usb_endpoint_type(&ep->desc)) {
case USB_ENDPOINT_XFER_CONTROL:
case USB_ENDPOINT_XFER_ISOC:
@@ -1530,6 +1540,10 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb
if (!usb_endpoint_xfer_int(&ep->desc))
return -EINVAL;
interrupt_urb:
+ if (!is_in)
+ allow_zero = true;
+ else
+ allow_short = true;
break;
case USBDEVFS_URB_TYPE_ISO:
@@ -1602,8 +1616,9 @@ 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(num_sgs * sizeof(struct scatterlist),
- GFP_KERNEL);
+ as->urb->sg = kmalloc_array(num_sgs,
+ sizeof(struct scatterlist),
+ GFP_KERNEL);
if (!as->urb->sg) {
ret = -ENOMEM;
goto error;
@@ -1674,14 +1689,19 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb
u = (is_in ? URB_DIR_IN : URB_DIR_OUT);
if (uurb->flags & USBDEVFS_URB_ISO_ASAP)
u |= URB_ISO_ASAP;
- if (uurb->flags & USBDEVFS_URB_SHORT_NOT_OK && is_in)
+ if (allow_short && uurb->flags & USBDEVFS_URB_SHORT_NOT_OK)
u |= URB_SHORT_NOT_OK;
- if (uurb->flags & USBDEVFS_URB_ZERO_PACKET)
+ if (allow_zero && uurb->flags & USBDEVFS_URB_ZERO_PACKET)
u |= URB_ZERO_PACKET;
if (uurb->flags & USBDEVFS_URB_NO_INTERRUPT)
u |= URB_NO_INTERRUPT;
as->urb->transfer_flags = u;
+ if (!allow_short && uurb->flags & USBDEVFS_URB_SHORT_NOT_OK)
+ dev_warn(&ps->dev->dev, "Requested nonsensical USBDEVFS_URB_SHORT_NOT_OK.\n");
+ if (!allow_zero && uurb->flags & USBDEVFS_URB_ZERO_PACKET)
+ dev_warn(&ps->dev->dev, "Requested nonsensical USBDEVFS_URB_ZERO_PACKET.\n");
+
as->urb->transfer_buffer_length = uurb->buffer_length;
as->urb->setup_packet = (unsigned char *)dr;
dr = NULL;
@@ -2597,7 +2617,7 @@ const struct file_operations usbdev_file_operations = {
static void usbdev_remove(struct usb_device *udev)
{
struct usb_dev_state *ps;
- struct siginfo sinfo;
+ struct kernel_siginfo sinfo;
while (!list_empty(&udev->filelist)) {
ps = list_entry(udev->filelist.next, struct usb_dev_state, list);
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 9792cedfc351..53564386ed57 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -510,9 +510,7 @@ int usb_driver_claim_interface(struct usb_driver *driver,
struct usb_interface *iface, void *priv)
{
struct device *dev;
- struct usb_device *udev;
int retval = 0;
- int lpm_disable_error = -ENODEV;
if (!iface)
return -ENODEV;
@@ -525,24 +523,12 @@ int usb_driver_claim_interface(struct usb_driver *driver,
if (!iface->authorized)
return -ENODEV;
- udev = interface_to_usbdev(iface);
-
dev->driver = &driver->drvwrap.driver;
usb_set_intfdata(iface, priv);
iface->needs_binding = 0;
iface->condition = USB_INTERFACE_BOUND;
- /* See the comment about disabling LPM in usb_probe_interface(). */
- if (driver->disable_hub_initiated_lpm) {
- lpm_disable_error = usb_unlocked_disable_lpm(udev);
- if (lpm_disable_error) {
- dev_err(&iface->dev, "%s Failed to disable LPM for driver %s\n",
- __func__, driver->name);
- return -ENOMEM;
- }
- }
-
/* Claimed interfaces are initially inactive (suspended) and
* runtime-PM-enabled, but only if the driver has autosuspend
* support. Otherwise they are marked active, to prevent the
@@ -561,9 +547,20 @@ int usb_driver_claim_interface(struct usb_driver *driver,
if (device_is_registered(dev))
retval = device_bind_driver(dev);
- /* Attempt to re-enable USB3 LPM, if the disable was successful. */
- if (!lpm_disable_error)
- usb_unlocked_enable_lpm(udev);
+ if (retval) {
+ dev->driver = NULL;
+ usb_set_intfdata(iface, NULL);
+ iface->needs_remote_wakeup = 0;
+ iface->condition = USB_INTERFACE_UNBOUND;
+
+ /*
+ * Unbound interfaces are always runtime-PM-disabled
+ * and runtime-PM-suspended
+ */
+ if (driver->supports_autosuspend)
+ pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
+ }
return retval;
}
@@ -1922,4 +1919,5 @@ struct bus_type usb_bus_type = {
.name = "usb",
.match = usb_device_match,
.uevent = usb_uevent,
+ .need_parent_lock = true,
};
diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c
index bc8242bc4564..356b05c82dbc 100644
--- a/drivers/usb/core/generic.c
+++ b/drivers/usb/core/generic.c
@@ -21,6 +21,7 @@
#include <linux/usb.h>
#include <linux/usb/hcd.h>
+#include <uapi/linux/usb/audio.h>
#include "usb.h"
static inline const char *plural(int n)
@@ -42,6 +43,16 @@ static int is_activesync(struct usb_interface_descriptor *desc)
&& desc->bInterfaceProtocol == 1;
}
+static bool is_audio(struct usb_interface_descriptor *desc)
+{
+ return desc->bInterfaceClass == USB_CLASS_AUDIO;
+}
+
+static bool is_uac3_config(struct usb_interface_descriptor *desc)
+{
+ return desc->bInterfaceProtocol == UAC_VERSION_3;
+}
+
int usb_choose_configuration(struct usb_device *udev)
{
int i;
@@ -121,6 +132,22 @@ int usb_choose_configuration(struct usb_device *udev)
#endif
}
+ /*
+ * Select first configuration as default for audio so that
+ * devices that don't comply with UAC3 protocol are supported.
+ * But, still iterate through other configurations and
+ * select UAC3 compliant config if present.
+ */
+ if (i == 0 && num_configs > 1 && desc && is_audio(desc)) {
+ best = c;
+ continue;
+ }
+
+ if (i > 0 && desc && is_audio(desc) && is_uac3_config(desc)) {
+ best = c;
+ break;
+ }
+
/* From the remaining configs, choose the first one whose
* first interface is for a non-vendor-specific class.
* Reason: Linux is more likely to have a class driver
diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c
index 66fe1b78d952..03432467b05f 100644
--- a/drivers/usb/core/hcd-pci.c
+++ b/drivers/usb/core/hcd-pci.c
@@ -515,8 +515,6 @@ static int resume_common(struct device *dev, int event)
event == PM_EVENT_RESTORE);
if (retval) {
dev_err(dev, "PCI post-resume error %d!\n", retval);
- if (hcd->shared_hcd)
- usb_hc_died(hcd->shared_hcd);
usb_hc_died(hcd);
}
}
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 0a42c5df3c0f..487025d31d44 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -33,7 +33,6 @@
#include <linux/phy/phy.h>
#include <linux/usb.h>
#include <linux/usb/hcd.h>
-#include <linux/usb/phy.h>
#include <linux/usb/otg.h>
#include "usb.h"
@@ -568,6 +567,7 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
switch (wValue & 0xff00) {
case USB_DT_DEVICE << 8:
switch (hcd->speed) {
+ case HCD_USB32:
case HCD_USB31:
bufp = usb31_rh_dev_descriptor;
break;
@@ -592,6 +592,7 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
break;
case USB_DT_CONFIG << 8:
switch (hcd->speed) {
+ case HCD_USB32:
case HCD_USB31:
case HCD_USB3:
bufp = ss_rh_config_descriptor;
@@ -1737,7 +1738,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) &&
@@ -1754,20 +1754,7 @@ static void __usb_hcd_giveback_urb(struct urb *urb)
/* pass ownership to the completion handler */
urb->status = status;
-
- /*
- * We disable local IRQs here avoid possible deadlock because
- * drivers may call spin_lock() to hold lock which might be
- * acquired in one hard interrupt handler.
- *
- * The local_irq_save()/local_irq_restore() around complete()
- * will be removed if current USB drivers have been cleaned up
- * and no one may trigger the above deadlock situation when
- * running complete() in tasklet.
- */
- local_irq_save(flags);
urb->complete(urb);
- local_irq_restore(flags);
usb_anchor_resume_wakeups(anchor);
atomic_dec(&urb->use_count);
@@ -2742,34 +2729,14 @@ int usb_add_hcd(struct usb_hcd *hcd,
int retval;
struct usb_device *rhdev;
- if (IS_ENABLED(CONFIG_USB_PHY) && !hcd->skip_phy_initialization) {
- struct usb_phy *phy = usb_get_phy_dev(hcd->self.sysdev, 0);
-
- if (IS_ERR(phy)) {
- retval = PTR_ERR(phy);
- if (retval == -EPROBE_DEFER)
- return retval;
- } else {
- retval = usb_phy_init(phy);
- if (retval) {
- usb_put_phy(phy);
- return retval;
- }
- hcd->usb_phy = phy;
- hcd->remove_phy = 1;
- }
- }
-
if (!hcd->skip_phy_initialization && usb_hcd_is_primary_hcd(hcd)) {
hcd->phy_roothub = usb_phy_roothub_alloc(hcd->self.sysdev);
- if (IS_ERR(hcd->phy_roothub)) {
- retval = PTR_ERR(hcd->phy_roothub);
- goto err_phy_roothub_alloc;
- }
+ if (IS_ERR(hcd->phy_roothub))
+ return PTR_ERR(hcd->phy_roothub);
retval = usb_phy_roothub_init(hcd->phy_roothub);
if (retval)
- goto err_phy_roothub_alloc;
+ return retval;
retval = usb_phy_roothub_power_on(hcd->phy_roothub);
if (retval)
@@ -2819,6 +2786,9 @@ int usb_add_hcd(struct usb_hcd *hcd,
hcd->self.root_hub = rhdev;
mutex_unlock(&usb_port_peer_mutex);
+ rhdev->rx_lanes = 1;
+ rhdev->tx_lanes = 1;
+
switch (hcd->speed) {
case HCD_USB11:
rhdev->speed = USB_SPEED_FULL;
@@ -2832,6 +2802,10 @@ int usb_add_hcd(struct usb_hcd *hcd,
case HCD_USB3:
rhdev->speed = USB_SPEED_SUPER;
break;
+ case HCD_USB32:
+ rhdev->rx_lanes = 2;
+ rhdev->tx_lanes = 2;
+ /* fall through */
case HCD_USB31:
rhdev->speed = USB_SPEED_SUPER_PLUS;
break;
@@ -2943,12 +2917,7 @@ err_create_buf:
usb_phy_roothub_power_off(hcd->phy_roothub);
err_usb_phy_roothub_power_on:
usb_phy_roothub_exit(hcd->phy_roothub);
-err_phy_roothub_alloc:
- if (hcd->remove_phy && hcd->usb_phy) {
- usb_phy_shutdown(hcd->usb_phy);
- usb_put_phy(hcd->usb_phy);
- hcd->usb_phy = NULL;
- }
+
return retval;
}
EXPORT_SYMBOL_GPL(usb_add_hcd);
@@ -3024,12 +2993,6 @@ void usb_remove_hcd(struct usb_hcd *hcd)
usb_phy_roothub_power_off(hcd->phy_roothub);
usb_phy_roothub_exit(hcd->phy_roothub);
- if (hcd->remove_phy && hcd->usb_phy) {
- usb_phy_shutdown(hcd->usb_phy);
- usb_put_phy(hcd->usb_phy);
- hcd->usb_phy = NULL;
- }
-
usb_put_invalidate_rhdev(hcd);
hcd->flags = 0;
}
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index aa9968d90a48..f76b2e0aba9d 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -28,6 +28,7 @@
#include <linux/mutex.h>
#include <linux/random.h>
#include <linux/pm_qos.h>
+#include <linux/kobject.h>
#include <linux/uaccess.h>
#include <asm/byteorder.h>
@@ -1142,10 +1143,14 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
if (!udev || udev->state == USB_STATE_NOTATTACHED) {
/* Tell hub_wq to disconnect the device or
- * check for a new connection
+ * check for a new connection or over current condition.
+ * Based on USB2.0 Spec Section 11.12.5,
+ * C_PORT_OVER_CURRENT could be set while
+ * PORT_OVER_CURRENT is not. So check for any of them.
*/
if (udev || (portstatus & USB_PORT_STAT_CONNECTION) ||
- (portstatus & USB_PORT_STAT_OVERCURRENT))
+ (portstatus & USB_PORT_STAT_OVERCURRENT) ||
+ (portchange & USB_PORT_STAT_C_OVERCURRENT))
set_bit(port1, hub->change_bits);
} else if (portstatus & USB_PORT_STAT_ENABLE) {
@@ -1376,7 +1381,7 @@ static int hub_configure(struct usb_hub *hub,
dev_info(hub_dev, "%d port%s detected\n", maxchild,
(maxchild == 1) ? "" : "s");
- hub->ports = kzalloc(maxchild * sizeof(struct usb_port *), GFP_KERNEL);
+ hub->ports = kcalloc(maxchild, sizeof(struct usb_port *), GFP_KERNEL);
if (!hub->ports) {
ret = -ENOMEM;
goto fail;
@@ -2246,7 +2251,7 @@ static int usb_enumerate_device_otg(struct usb_device *udev)
/* descriptor may appear anywhere in config */
err = __usb_get_extra_descriptor(udev->rawdescriptors[0],
le16_to_cpu(udev->config[0].desc.wTotalLength),
- USB_DT_OTG, (void **) &desc);
+ USB_DT_OTG, (void **) &desc, sizeof(*desc));
if (err || !(desc->bmAttributes & USB_OTG_HNP))
return 0;
@@ -2636,7 +2641,7 @@ static unsigned hub_is_wusb(struct usb_hub *hub)
#define SET_ADDRESS_TRIES 2
#define GET_DESCRIPTOR_TRIES 2
#define SET_CONFIG_TRIES (2 * (use_both_schemes + 1))
-#define USE_NEW_SCHEME(i) ((i) / 2 == (int)old_scheme_first)
+#define USE_NEW_SCHEME(i, scheme) ((i) / 2 == (int)scheme)
#define HUB_ROOT_RESET_TIME 60 /* times are in msec */
#define HUB_SHORT_RESET_TIME 10
@@ -2651,12 +2656,18 @@ static unsigned hub_is_wusb(struct usb_hub *hub)
* enumeration failures, so disable this enumeration scheme for USB3
* devices.
*/
-static bool use_new_scheme(struct usb_device *udev, int retry)
+static bool use_new_scheme(struct usb_device *udev, int retry,
+ struct usb_port *port_dev)
{
+ int old_scheme_first_port =
+ port_dev->quirks & USB_PORT_QUIRK_OLD_SCHEME;
+ int quick_enumeration = (udev->speed == USB_SPEED_HIGH);
+
if (udev->speed >= USB_SPEED_SUPER)
return false;
- return USE_NEW_SCHEME(retry);
+ return USE_NEW_SCHEME(retry, old_scheme_first_port || old_scheme_first
+ || quick_enumeration);
}
/* Is a USB 3.0 port in the Inactive or Compliance Mode state?
@@ -2751,6 +2762,14 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
if (!udev)
return 0;
+ if (hub_is_superspeedplus(hub->hdev)) {
+ /* extended portstatus Rx and Tx lane count are zero based */
+ udev->rx_lanes = USB_EXT_PORT_RX_LANES(ext_portstatus) + 1;
+ udev->tx_lanes = USB_EXT_PORT_TX_LANES(ext_portstatus) + 1;
+ } else {
+ udev->rx_lanes = 1;
+ udev->tx_lanes = 1;
+ }
if (hub_is_wusb(hub))
udev->speed = USB_SPEED_WIRELESS;
else if (hub_is_superspeedplus(hub->hdev) &&
@@ -2775,6 +2794,7 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
int i, status;
u16 portchange, portstatus;
struct usb_port *port_dev = hub->ports[port1 - 1];
+ int reset_recovery_time;
if (!hub_is_superspeed(hub->hdev)) {
if (warm) {
@@ -2830,7 +2850,9 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
USB_PORT_FEAT_C_BH_PORT_RESET);
usb_clear_port_feature(hub->hdev, port1,
USB_PORT_FEAT_C_PORT_LINK_STATE);
- usb_clear_port_feature(hub->hdev, port1,
+
+ if (udev)
+ usb_clear_port_feature(hub->hdev, port1,
USB_PORT_FEAT_C_CONNECTION);
/*
@@ -2866,8 +2888,19 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
done:
if (status == 0) {
- /* TRSTRCY = 10 ms; plus some extra */
- msleep(10 + 40);
+ if (port_dev->quirks & USB_PORT_QUIRK_FAST_ENUM)
+ usleep_range(10000, 12000);
+ else {
+ /* TRSTRCY = 10 ms; plus some extra */
+ reset_recovery_time = 10 + 40;
+
+ /* Hub needs extra delay after resetting its port. */
+ if (hub->hdev->quirks & USB_QUIRK_HUB_SLOW_RESET)
+ reset_recovery_time += 100;
+
+ msleep(reset_recovery_time);
+ }
+
if (udev) {
struct usb_hcd *hcd = bus_to_hcd(udev->bus);
@@ -3376,6 +3409,10 @@ static int wait_for_connected(struct usb_device *udev,
while (delay_ms < 2000) {
if (status || *portstatus & USB_PORT_STAT_CONNECTION)
break;
+ if (!port_is_power_on(hub, *portstatus)) {
+ status = -ENODEV;
+ break;
+ }
msleep(20);
delay_ms += 20;
status = hub_port_status(hub, *port1, portstatus, portchange);
@@ -3636,12 +3673,54 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
return 0;
}
+/* Report wakeup requests from the ports of a resuming root hub */
+static void report_wakeup_requests(struct usb_hub *hub)
+{
+ struct usb_device *hdev = hub->hdev;
+ struct usb_device *udev;
+ struct usb_hcd *hcd;
+ unsigned long resuming_ports;
+ int i;
+
+ if (hdev->parent)
+ return; /* Not a root hub */
+
+ hcd = bus_to_hcd(hdev->bus);
+ if (hcd->driver->get_resuming_ports) {
+
+ /*
+ * The get_resuming_ports() method returns a bitmap (origin 0)
+ * of ports which have started wakeup signaling but have not
+ * yet finished resuming. During system resume we will
+ * resume all the enabled ports, regardless of any wakeup
+ * signals, which means the wakeup requests would be lost.
+ * To prevent this, report them to the PM core here.
+ */
+ resuming_ports = hcd->driver->get_resuming_ports(hcd);
+ for (i = 0; i < hdev->maxchild; ++i) {
+ if (test_bit(i, &resuming_ports)) {
+ udev = hub->ports[i]->child;
+ if (udev)
+ pm_wakeup_event(&udev->dev, 0);
+ }
+ }
+ }
+}
+
static int hub_resume(struct usb_interface *intf)
{
struct usb_hub *hub = usb_get_intfdata(intf);
dev_dbg(&intf->dev, "%s\n", __func__);
hub_activate(hub, HUB_RESUME);
+
+ /*
+ * This should be called only for system resume, not runtime resume.
+ * We can't tell the difference here, so some wakeup requests will be
+ * reported at the wrong time or more than once. This shouldn't
+ * matter much, so long as they do get reported.
+ */
+ report_wakeup_requests(hub);
return 0;
}
@@ -4380,6 +4459,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
{
struct usb_device *hdev = hub->hdev;
struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
+ struct usb_port *port_dev = hub->ports[port1 - 1];
int retries, operations, retval, i;
unsigned delay = HUB_SHORT_RESET_TIME;
enum usb_device_speed oldspeed = udev->speed;
@@ -4501,7 +4581,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
for (retries = 0; retries < GET_DESCRIPTOR_TRIES; (++retries, msleep(100))) {
bool did_new_scheme = false;
- if (use_new_scheme(udev, retry_counter)) {
+ if (use_new_scheme(udev, retry_counter, port_dev)) {
struct usb_device_descriptor *buf;
int r = 0;
@@ -4551,7 +4631,9 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
* reset. But only on the first attempt,
* lest we get into a time out/reset loop
*/
- if (r == 0 || (r == -ETIMEDOUT && retries == 0))
+ if (r == 0 || (r == -ETIMEDOUT &&
+ retries == 0 &&
+ udev->speed > USB_SPEED_FULL))
break;
}
udev->descriptor.bMaxPacketSize0 =
@@ -4598,9 +4680,12 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
if (udev->speed >= USB_SPEED_SUPER) {
devnum = udev->devnum;
dev_info(&udev->dev,
- "%s SuperSpeed%s USB device number %d using %s\n",
+ "%s SuperSpeed%s%s USB device number %d using %s\n",
(udev->config) ? "reset" : "new",
- (udev->speed == USB_SPEED_SUPER_PLUS) ? "Plus" : "",
+ (udev->speed == USB_SPEED_SUPER_PLUS) ?
+ "Plus Gen 2" : " Gen 1",
+ (udev->rx_lanes == 2 && udev->tx_lanes == 2) ?
+ "x2" : "",
devnum, driver_name);
}
@@ -5075,6 +5160,43 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
usb_lock_port(port_dev);
}
+/* Handle notifying userspace about hub over-current events */
+static void port_over_current_notify(struct usb_port *port_dev)
+{
+ char *envp[3];
+ struct device *hub_dev;
+ char *port_dev_path;
+
+ sysfs_notify(&port_dev->dev.kobj, NULL, "over_current_count");
+
+ hub_dev = port_dev->dev.parent;
+
+ if (!hub_dev)
+ return;
+
+ port_dev_path = kobject_get_path(&port_dev->dev.kobj, GFP_KERNEL);
+ if (!port_dev_path)
+ return;
+
+ envp[0] = kasprintf(GFP_KERNEL, "OVER_CURRENT_PORT=%s", port_dev_path);
+ if (!envp[0])
+ goto exit_path;
+
+ envp[1] = kasprintf(GFP_KERNEL, "OVER_CURRENT_COUNT=%u",
+ port_dev->over_current_count);
+ if (!envp[1])
+ goto exit;
+
+ envp[2] = NULL;
+ kobject_uevent_env(&hub_dev->kobj, KOBJ_CHANGE, envp);
+
+ kfree(envp[1]);
+exit:
+ kfree(envp[0]);
+exit_path:
+ kfree(port_dev_path);
+}
+
static void port_event(struct usb_hub *hub, int port1)
__must_hold(&port_dev->status_lock)
{
@@ -5117,6 +5239,7 @@ static void port_event(struct usb_hub *hub, int port1)
if (portchange & USB_PORT_STAT_C_OVERCURRENT) {
u16 status = 0, unused;
port_dev->over_current_count++;
+ port_over_current_notify(port_dev);
dev_dbg(&port_dev->dev, "over-current change #%u\n",
port_dev->over_current_count);
diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h
index 4dc769ee9c74..4accfb63f7dc 100644
--- a/drivers/usb/core/hub.h
+++ b/drivers/usb/core/hub.h
@@ -98,6 +98,7 @@ struct usb_port {
struct mutex status_lock;
u32 over_current_count;
u8 portnum;
+ u32 quirks;
unsigned int is_superspeed:1;
unsigned int usb3_lpm_u1_permit:1;
unsigned int usb3_lpm_u2_permit:1;
diff --git a/drivers/usb/core/ledtrig-usbport.c b/drivers/usb/core/ledtrig-usbport.c
index d775ffea20c3..dc7f7fd71684 100644
--- a/drivers/usb/core/ledtrig-usbport.c
+++ b/drivers/usb/core/ledtrig-usbport.c
@@ -113,11 +113,17 @@ static ssize_t usbport_trig_port_store(struct device *dev,
static struct attribute *ports_attrs[] = {
NULL,
};
+
static const struct attribute_group ports_group = {
.name = "ports",
.attrs = ports_attrs,
};
+static const struct attribute_group *ports_groups[] = {
+ &ports_group,
+ NULL
+};
+
/***************************************
* Adding & removing ports
***************************************/
@@ -298,61 +304,47 @@ static int usbport_trig_notify(struct notifier_block *nb, unsigned long action,
return NOTIFY_DONE;
}
-static void usbport_trig_activate(struct led_classdev *led_cdev)
+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);
if (!usbport_data)
- return;
+ return -ENOMEM;
usbport_data->led_cdev = led_cdev;
/* List of ports */
INIT_LIST_HEAD(&usbport_data->ports);
- err = sysfs_create_group(&led_cdev->dev->kobj, &ports_group);
- if (err)
- goto err_free;
usb_for_each_dev(usbport_data, usbport_trig_add_usb_dev_ports);
usbport_trig_update_count(usbport_data);
/* Notifications */
- usbport_data->nb.notifier_call = usbport_trig_notify,
- led_cdev->trigger_data = usbport_data;
+ usbport_data->nb.notifier_call = usbport_trig_notify;
+ led_set_trigger_data(led_cdev, usbport_data);
usb_register_notify(&usbport_data->nb);
- led_cdev->activated = true;
- return;
-
-err_free:
- kfree(usbport_data);
+ return 0;
}
static void usbport_trig_deactivate(struct led_classdev *led_cdev)
{
- struct usbport_trig_data *usbport_data = led_cdev->trigger_data;
+ struct usbport_trig_data *usbport_data = led_get_trigger_data(led_cdev);
struct usbport_trig_port *port, *tmp;
- if (!led_cdev->activated)
- return;
-
list_for_each_entry_safe(port, tmp, &usbport_data->ports, list) {
usbport_trig_remove_port(usbport_data, port);
}
usb_unregister_notify(&usbport_data->nb);
- sysfs_remove_group(&led_cdev->dev->kobj, &ports_group);
-
kfree(usbport_data);
-
- led_cdev->activated = false;
}
static struct led_trigger usbport_led_trigger = {
.name = "usbport",
.activate = usbport_trig_activate,
.deactivate = usbport_trig_deactivate,
+ .groups = ports_groups,
};
static int __init usbport_trig_init(void)
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index 0c11d40a12bc..bfa5eda0cc26 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -269,10 +269,11 @@ static void sg_clean(struct usb_sg_request *io)
static void sg_complete(struct urb *urb)
{
+ unsigned long flags;
struct usb_sg_request *io = urb->context;
int status = urb->status;
- spin_lock(&io->lock);
+ spin_lock_irqsave(&io->lock, flags);
/* In 2.5 we require hcds' endpoint queues not to progress after fault
* reports, until the completion callback (this!) returns. That lets
@@ -306,7 +307,7 @@ static void sg_complete(struct urb *urb)
* unlink pending urbs so they won't rx/tx bad data.
* careful: unlink can sometimes be synchronous...
*/
- spin_unlock(&io->lock);
+ spin_unlock_irqrestore(&io->lock, flags);
for (i = 0, found = 0; i < io->entries; i++) {
if (!io->urbs[i])
continue;
@@ -323,7 +324,7 @@ static void sg_complete(struct urb *urb)
} else if (urb == io->urbs[i])
found = 1;
}
- spin_lock(&io->lock);
+ spin_lock_irqsave(&io->lock, flags);
}
/* on the last completion, signal usb_sg_wait() */
@@ -332,7 +333,7 @@ static void sg_complete(struct urb *urb)
if (!io->count)
complete(&io->complete);
- spin_unlock(&io->lock);
+ spin_unlock_irqrestore(&io->lock, flags);
}
@@ -390,7 +391,7 @@ int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev,
}
/* initialize all the urbs we'll use */
- io->urbs = kmalloc(io->entries * sizeof(*io->urbs), mem_flags);
+ io->urbs = kmalloc_array(io->entries, sizeof(*io->urbs), mem_flags);
if (!io->urbs)
goto nomem;
@@ -940,7 +941,7 @@ int usb_set_isoch_delay(struct usb_device *dev)
return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
USB_REQ_SET_ISOCH_DELAY,
USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE,
- cpu_to_le16(dev->hub_delay), 0, NULL, 0,
+ dev->hub_delay, 0, NULL, 0,
USB_CTRL_SET_TIMEOUT);
}
@@ -1340,6 +1341,11 @@ void usb_enable_interface(struct usb_device *dev,
* is submitted that needs that bandwidth. Some other operating systems
* allocate bandwidth early, when a configuration is chosen.
*
+ * xHCI reserves bandwidth and configures the alternate setting in
+ * usb_hcd_alloc_bandwidth(). If it fails the original interface altsetting
+ * may be disabled. Drivers cannot rely on any particular alternate
+ * setting being in effect after a failure.
+ *
* This call is synchronous, and may not be used in an interrupt context.
* Also, drivers must not change altsettings while urbs are scheduled for
* endpoints in that interface; all such urbs must first be completed
@@ -1375,6 +1381,12 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
alternate);
return -EINVAL;
}
+ /*
+ * usb3 hosts configure the interface in usb_hcd_alloc_bandwidth,
+ * including freeing dropped endpoint ring buffers.
+ * Make sure the interface endpoints are flushed before that
+ */
+ usb_disable_interface(dev, iface, false);
/* Make sure we have enough bandwidth for this alternate interface.
* Remove the current alt setting and add the new alt setting.
@@ -1824,8 +1836,8 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
n = nintf = 0;
if (cp) {
nintf = cp->desc.bNumInterfaces;
- new_interfaces = kmalloc(nintf * sizeof(*new_interfaces),
- GFP_NOIO);
+ new_interfaces = kmalloc_array(nintf, sizeof(*new_interfaces),
+ GFP_NOIO);
if (!new_interfaces)
return -ENOMEM;
diff --git a/drivers/usb/core/of.c b/drivers/usb/core/of.c
index fd77442c2d12..651708d8c908 100644
--- a/drivers/usb/core/of.c
+++ b/drivers/usb/core/of.c
@@ -105,29 +105,3 @@ usb_of_get_interface_node(struct usb_device *udev, u8 config, u8 ifnum)
return NULL;
}
EXPORT_SYMBOL_GPL(usb_of_get_interface_node);
-
-/**
- * usb_of_get_companion_dev - Find the companion device
- * @dev: the device pointer to find a companion
- *
- * Find the companion device from platform bus.
- *
- * Takes a reference to the returned struct device which needs to be dropped
- * after use.
- *
- * Return: On success, a pointer to the companion device, %NULL on failure.
- */
-struct device *usb_of_get_companion_dev(struct device *dev)
-{
- struct device_node *node;
- struct platform_device *pdev = NULL;
-
- node = of_parse_phandle(dev->of_node, "companion", 0);
- if (node)
- pdev = of_find_device_by_node(node);
-
- of_node_put(node);
-
- return pdev ? &pdev->dev : NULL;
-}
-EXPORT_SYMBOL_GPL(usb_of_get_companion_dev);
diff --git a/drivers/usb/core/phy.c b/drivers/usb/core/phy.c
index 9879767452a2..38b2c776c4b4 100644
--- a/drivers/usb/core/phy.c
+++ b/drivers/usb/core/phy.c
@@ -23,10 +23,11 @@ static int usb_phy_roothub_add_phy(struct device *dev, int index,
struct list_head *list)
{
struct usb_phy_roothub *roothub_entry;
- struct phy *phy = devm_of_phy_get_by_index(dev, dev->of_node, index);
+ struct phy *phy;
- if (IS_ERR_OR_NULL(phy)) {
- if (!phy || PTR_ERR(phy) == -ENODEV)
+ phy = devm_of_phy_get_by_index(dev, dev->of_node, index);
+ if (IS_ERR(phy)) {
+ if (PTR_ERR(phy) == -ENODEV)
return 0;
else
return PTR_ERR(phy);
diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c
index 6979bde87d31..1a06a4b5fbb1 100644
--- a/drivers/usb/core/port.c
+++ b/drivers/usb/core/port.c
@@ -16,6 +16,15 @@ static int usb_port_block_power_off;
static const struct attribute_group *port_dev_group[];
+static ssize_t location_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct usb_port *port_dev = to_usb_port(dev);
+
+ return sprintf(buf, "0x%08x\n", port_dev->location);
+}
+static DEVICE_ATTR_RO(location);
+
static ssize_t connect_type_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -50,6 +59,28 @@ static ssize_t over_current_count_show(struct device *dev,
}
static DEVICE_ATTR_RO(over_current_count);
+static ssize_t quirks_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct usb_port *port_dev = to_usb_port(dev);
+
+ return sprintf(buf, "%08x\n", port_dev->quirks);
+}
+
+static ssize_t quirks_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct usb_port *port_dev = to_usb_port(dev);
+ u32 value;
+
+ if (kstrtou32(buf, 16, &value))
+ return -EINVAL;
+
+ port_dev->quirks = value;
+ return count;
+}
+static DEVICE_ATTR_RW(quirks);
+
static ssize_t usb3_lpm_permit_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -118,6 +149,8 @@ static DEVICE_ATTR_RW(usb3_lpm_permit);
static struct attribute *port_dev_attrs[] = {
&dev_attr_connect_type.attr,
+ &dev_attr_location.attr,
+ &dev_attr_quirks.attr,
&dev_attr_over_current_count.attr,
NULL,
};
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index c55def2f1320..514c5214ddb2 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -58,6 +58,7 @@ static int quirks_param_set(const char *val, const struct kernel_param *kp)
quirk_list = kcalloc(quirk_count, sizeof(struct quirk_entry),
GFP_KERNEL);
if (!quirk_list) {
+ quirk_count = 0;
mutex_unlock(&quirk_mutex);
return -ENOMEM;
}
@@ -127,6 +128,9 @@ static int quirks_param_set(const char *val, const struct kernel_param *kp)
case 'n':
flags |= USB_QUIRK_DELAY_CTRL_MSG;
break;
+ case 'o':
+ flags |= USB_QUIRK_HUB_SLOW_RESET;
+ break;
/* Ignore unrecognized flag characters */
}
}
@@ -154,7 +158,7 @@ static struct kparam_string quirks_param_string = {
.string = quirks_param,
};
-module_param_cb(quirks, &quirks_param_ops, &quirks_param_string, 0644);
+device_param_cb(quirks, &quirks_param_ops, &quirks_param_string, 0644);
MODULE_PARM_DESC(quirks, "Add/modify USB quirks by specifying quirks=vendorID:productID:quirks");
/* Lists of quirky USB devices, split in device quirks and interface quirks.
@@ -178,6 +182,10 @@ static const struct usb_device_id usb_quirk_list[] = {
/* CBM - Flash disk */
{ USB_DEVICE(0x0204, 0x6025), .driver_info = USB_QUIRK_RESET_RESUME },
+ /* WORLDE Controller KS49 or Prodipe MIDI 49C USB controller */
+ { USB_DEVICE(0x0218, 0x0201), .driver_info =
+ USB_QUIRK_CONFIG_INTF_STRINGS },
+
/* WORLDE easy key (easykey.25) MIDI controller */
{ USB_DEVICE(0x0218, 0x0401), .driver_info =
USB_QUIRK_CONFIG_INTF_STRINGS },
@@ -201,6 +209,9 @@ static const struct usb_device_id usb_quirk_list[] = {
/* Microsoft LifeCam-VX700 v2.0 */
{ USB_DEVICE(0x045e, 0x0770), .driver_info = USB_QUIRK_RESET_RESUME },
+ /* Cherry Stream G230 2.0 (G85-231) and 3.0 (G85-232) */
+ { USB_DEVICE(0x046a, 0x0023), .driver_info = USB_QUIRK_RESET_RESUME },
+
/* Logitech HD Pro Webcams C920, C920-C, C925e and C930e */
{ USB_DEVICE(0x046d, 0x082d), .driver_info = USB_QUIRK_DELAY_INIT },
{ USB_DEVICE(0x046d, 0x0841), .driver_info = USB_QUIRK_DELAY_INIT },
@@ -322,6 +333,10 @@ static const struct usb_device_id usb_quirk_list[] = {
/* Midiman M-Audio Keystation 88es */
{ USB_DEVICE(0x0763, 0x0192), .driver_info = USB_QUIRK_RESET_RESUME },
+ /* SanDisk Ultra Fit and Ultra Flair */
+ { USB_DEVICE(0x0781, 0x5583), .driver_info = USB_QUIRK_NO_LPM },
+ { USB_DEVICE(0x0781, 0x5591), .driver_info = USB_QUIRK_NO_LPM },
+
/* M-Systems Flash Disk Pioneers */
{ USB_DEVICE(0x08ec, 0x1000), .driver_info = USB_QUIRK_RESET_RESUME },
@@ -375,13 +390,23 @@ static const struct usb_device_id usb_quirk_list[] = {
{ USB_DEVICE(0x1a0a, 0x0200), .driver_info =
USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL },
+ /* Terminus Technology Inc. Hub */
+ { USB_DEVICE(0x1a40, 0x0101), .driver_info = USB_QUIRK_HUB_SLOW_RESET },
+
/* Corsair K70 RGB */
{ USB_DEVICE(0x1b1c, 0x1b13), .driver_info = USB_QUIRK_DELAY_INIT },
+ /* Corsair Strafe */
+ { USB_DEVICE(0x1b1c, 0x1b15), .driver_info = USB_QUIRK_DELAY_INIT |
+ USB_QUIRK_DELAY_CTRL_MSG },
+
/* Corsair Strafe RGB */
{ USB_DEVICE(0x1b1c, 0x1b20), .driver_info = USB_QUIRK_DELAY_INIT |
USB_QUIRK_DELAY_CTRL_MSG },
+ /* Corsair K70 LUX RGB */
+ { USB_DEVICE(0x1b1c, 0x1b33), .driver_info = USB_QUIRK_DELAY_INIT },
+
/* Corsair K70 LUX */
{ USB_DEVICE(0x1b1c, 0x1b36), .driver_info = USB_QUIRK_DELAY_INIT },
@@ -402,6 +427,14 @@ static const struct usb_device_id usb_quirk_list[] = {
{ USB_DEVICE(0x2040, 0x7200), .driver_info =
USB_QUIRK_CONFIG_INTF_STRINGS },
+ /* Raydium Touchscreen */
+ { USB_DEVICE(0x2386, 0x3114), .driver_info = USB_QUIRK_NO_LPM },
+
+ { USB_DEVICE(0x2386, 0x3119), .driver_info = USB_QUIRK_NO_LPM },
+
+ /* DJI CineSSD */
+ { USB_DEVICE(0x2ca3, 0x0031), .driver_info = USB_QUIRK_NO_LPM },
+
/* INTEL VALUE SSD */
{ USB_DEVICE(0x8086, 0xf1a5), .driver_info = USB_QUIRK_RESET_RESUME },
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c
index 27bb34043053..ea18284dfa9a 100644
--- a/drivers/usb/core/sysfs.c
+++ b/drivers/usb/core/sysfs.c
@@ -175,6 +175,26 @@ static ssize_t speed_show(struct device *dev, struct device_attribute *attr,
}
static DEVICE_ATTR_RO(speed);
+static ssize_t rx_lanes_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct usb_device *udev;
+
+ udev = to_usb_device(dev);
+ return sprintf(buf, "%d\n", udev->rx_lanes);
+}
+static DEVICE_ATTR_RO(rx_lanes);
+
+static ssize_t tx_lanes_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct usb_device *udev;
+
+ udev = to_usb_device(dev);
+ return sprintf(buf, "%d\n", udev->tx_lanes);
+}
+static DEVICE_ATTR_RO(tx_lanes);
+
static ssize_t busnum_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
@@ -790,6 +810,8 @@ static struct attribute *dev_attrs[] = {
&dev_attr_bNumConfigurations.attr,
&dev_attr_bMaxPacketSize0.attr,
&dev_attr_speed.attr,
+ &dev_attr_rx_lanes.attr,
+ &dev_attr_tx_lanes.attr,
&dev_attr_busnum.attr,
&dev_attr_devnum.attr,
&dev_attr_devpath.attr,
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 0adb6345ff2e..4ebfbd737905 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -228,6 +228,8 @@ struct usb_host_interface *usb_find_alt_setting(
struct usb_interface_cache *intf_cache = NULL;
int i;
+ if (!config)
+ return NULL;
for (i = 0; i < config->desc.bNumInterfaces; i++) {
if (config->intf_cache[i]->altsetting[0].desc.bInterfaceNumber
== iface_num) {
@@ -830,14 +832,14 @@ EXPORT_SYMBOL_GPL(usb_get_current_frame_number);
*/
int __usb_get_extra_descriptor(char *buffer, unsigned size,
- unsigned char type, void **ptr)
+ unsigned char type, void **ptr, size_t minsize)
{
struct usb_descriptor_header *header;
while (size >= sizeof(struct usb_descriptor_header)) {
header = (struct usb_descriptor_header *)buffer;
- if (header->bLength < 2) {
+ if (header->bLength < 2 || header->bLength > size) {
printk(KERN_ERR
"%s: bogus descriptor, type %d length %d\n",
usbcore_name,
@@ -846,7 +848,7 @@ int __usb_get_extra_descriptor(char *buffer, unsigned size,
return -1;
}
- if (header->bDescriptorType == type) {
+ if (header->bDescriptorType == type && header->bLength >= minsize) {
*ptr = header;
return 0;
}
@@ -1167,30 +1169,16 @@ static struct notifier_block usb_bus_nb = {
struct dentry *usb_debug_root;
EXPORT_SYMBOL_GPL(usb_debug_root);
-static struct dentry *usb_debug_devices;
-
-static int usb_debugfs_init(void)
+static void usb_debugfs_init(void)
{
usb_debug_root = debugfs_create_dir("usb", NULL);
- if (!usb_debug_root)
- return -ENOENT;
-
- usb_debug_devices = debugfs_create_file("devices", 0444,
- usb_debug_root, NULL,
- &usbfs_devices_fops);
- if (!usb_debug_devices) {
- debugfs_remove(usb_debug_root);
- usb_debug_root = NULL;
- return -ENOENT;
- }
-
- return 0;
+ debugfs_create_file("devices", 0444, usb_debug_root, NULL,
+ &usbfs_devices_fops);
}
static void usb_debugfs_cleanup(void)
{
- debugfs_remove(usb_debug_devices);
- debugfs_remove(usb_debug_root);
+ debugfs_remove_recursive(usb_debug_root);
}
/*
@@ -1205,9 +1193,7 @@ static int __init usb_init(void)
}
usb_init_pool_max();
- retval = usb_debugfs_init();
- if (retval)
- goto out;
+ usb_debugfs_init();
usb_acpi_register();
retval = bus_register(&usb_bus_type);
diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c
index 18a0a1771289..55d5ae2a7ec7 100644
--- a/drivers/usb/dwc2/core.c
+++ b/drivers/usb/dwc2/core.c
@@ -73,17 +73,17 @@ int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg)
/* Backup global regs */
gr = &hsotg->gr_backup;
- gr->gotgctl = dwc2_readl(hsotg->regs + GOTGCTL);
- gr->gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
- gr->gahbcfg = dwc2_readl(hsotg->regs + GAHBCFG);
- gr->gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
- gr->grxfsiz = dwc2_readl(hsotg->regs + GRXFSIZ);
- gr->gnptxfsiz = dwc2_readl(hsotg->regs + GNPTXFSIZ);
- gr->gdfifocfg = dwc2_readl(hsotg->regs + GDFIFOCFG);
- gr->pcgcctl1 = dwc2_readl(hsotg->regs + PCGCCTL1);
- gr->glpmcfg = dwc2_readl(hsotg->regs + GLPMCFG);
- gr->gi2cctl = dwc2_readl(hsotg->regs + GI2CCTL);
- gr->pcgcctl = dwc2_readl(hsotg->regs + PCGCTL);
+ gr->gotgctl = dwc2_readl(hsotg, GOTGCTL);
+ gr->gintmsk = dwc2_readl(hsotg, GINTMSK);
+ gr->gahbcfg = dwc2_readl(hsotg, GAHBCFG);
+ gr->gusbcfg = dwc2_readl(hsotg, GUSBCFG);
+ gr->grxfsiz = dwc2_readl(hsotg, GRXFSIZ);
+ gr->gnptxfsiz = dwc2_readl(hsotg, GNPTXFSIZ);
+ gr->gdfifocfg = dwc2_readl(hsotg, GDFIFOCFG);
+ gr->pcgcctl1 = dwc2_readl(hsotg, PCGCCTL1);
+ gr->glpmcfg = dwc2_readl(hsotg, GLPMCFG);
+ gr->gi2cctl = dwc2_readl(hsotg, GI2CCTL);
+ gr->pcgcctl = dwc2_readl(hsotg, PCGCTL);
gr->valid = true;
return 0;
@@ -111,18 +111,18 @@ int dwc2_restore_global_registers(struct dwc2_hsotg *hsotg)
}
gr->valid = false;
- dwc2_writel(0xffffffff, hsotg->regs + GINTSTS);
- dwc2_writel(gr->gotgctl, hsotg->regs + GOTGCTL);
- dwc2_writel(gr->gintmsk, hsotg->regs + GINTMSK);
- dwc2_writel(gr->gusbcfg, hsotg->regs + GUSBCFG);
- dwc2_writel(gr->gahbcfg, hsotg->regs + GAHBCFG);
- dwc2_writel(gr->grxfsiz, hsotg->regs + GRXFSIZ);
- dwc2_writel(gr->gnptxfsiz, hsotg->regs + GNPTXFSIZ);
- dwc2_writel(gr->gdfifocfg, hsotg->regs + GDFIFOCFG);
- dwc2_writel(gr->pcgcctl1, hsotg->regs + PCGCCTL1);
- dwc2_writel(gr->glpmcfg, hsotg->regs + GLPMCFG);
- dwc2_writel(gr->pcgcctl, hsotg->regs + PCGCTL);
- dwc2_writel(gr->gi2cctl, hsotg->regs + GI2CCTL);
+ dwc2_writel(hsotg, 0xffffffff, GINTSTS);
+ dwc2_writel(hsotg, gr->gotgctl, GOTGCTL);
+ dwc2_writel(hsotg, gr->gintmsk, GINTMSK);
+ dwc2_writel(hsotg, gr->gusbcfg, GUSBCFG);
+ dwc2_writel(hsotg, gr->gahbcfg, GAHBCFG);
+ dwc2_writel(hsotg, gr->grxfsiz, GRXFSIZ);
+ dwc2_writel(hsotg, gr->gnptxfsiz, GNPTXFSIZ);
+ dwc2_writel(hsotg, gr->gdfifocfg, GDFIFOCFG);
+ dwc2_writel(hsotg, gr->pcgcctl1, PCGCCTL1);
+ dwc2_writel(hsotg, gr->glpmcfg, GLPMCFG);
+ dwc2_writel(hsotg, gr->pcgcctl, PCGCTL);
+ dwc2_writel(hsotg, gr->gi2cctl, GI2CCTL);
return 0;
}
@@ -141,17 +141,17 @@ int dwc2_exit_partial_power_down(struct dwc2_hsotg *hsotg, bool restore)
if (hsotg->params.power_down != DWC2_POWER_DOWN_PARAM_PARTIAL)
return -ENOTSUPP;
- pcgcctl = dwc2_readl(hsotg->regs + PCGCTL);
+ pcgcctl = dwc2_readl(hsotg, PCGCTL);
pcgcctl &= ~PCGCTL_STOPPCLK;
- dwc2_writel(pcgcctl, hsotg->regs + PCGCTL);
+ dwc2_writel(hsotg, pcgcctl, PCGCTL);
- pcgcctl = dwc2_readl(hsotg->regs + PCGCTL);
+ pcgcctl = dwc2_readl(hsotg, PCGCTL);
pcgcctl &= ~PCGCTL_PWRCLMP;
- dwc2_writel(pcgcctl, hsotg->regs + PCGCTL);
+ dwc2_writel(hsotg, pcgcctl, PCGCTL);
- pcgcctl = dwc2_readl(hsotg->regs + PCGCTL);
+ pcgcctl = dwc2_readl(hsotg, PCGCTL);
pcgcctl &= ~PCGCTL_RSTPDWNMODULE;
- dwc2_writel(pcgcctl, hsotg->regs + PCGCTL);
+ dwc2_writel(hsotg, pcgcctl, PCGCTL);
udelay(100);
if (restore) {
@@ -222,21 +222,21 @@ int dwc2_enter_partial_power_down(struct dwc2_hsotg *hsotg)
* Clear any pending interrupts since dwc2 will not be able to
* clear them after entering partial_power_down.
*/
- dwc2_writel(0xffffffff, hsotg->regs + GINTSTS);
+ dwc2_writel(hsotg, 0xffffffff, GINTSTS);
/* Put the controller in low power state */
- pcgcctl = dwc2_readl(hsotg->regs + PCGCTL);
+ pcgcctl = dwc2_readl(hsotg, PCGCTL);
pcgcctl |= PCGCTL_PWRCLMP;
- dwc2_writel(pcgcctl, hsotg->regs + PCGCTL);
+ dwc2_writel(hsotg, pcgcctl, PCGCTL);
ndelay(20);
pcgcctl |= PCGCTL_RSTPDWNMODULE;
- dwc2_writel(pcgcctl, hsotg->regs + PCGCTL);
+ dwc2_writel(hsotg, pcgcctl, PCGCTL);
ndelay(20);
pcgcctl |= PCGCTL_STOPPCLK;
- dwc2_writel(pcgcctl, hsotg->regs + PCGCTL);
+ dwc2_writel(hsotg, pcgcctl, PCGCTL);
return ret;
}
@@ -272,39 +272,39 @@ static void dwc2_restore_essential_regs(struct dwc2_hsotg *hsotg, int rmode,
if (!(pcgcctl & PCGCTL_P2HD_DEV_ENUM_SPD_MASK))
pcgcctl |= BIT(17);
}
- dwc2_writel(pcgcctl, hsotg->regs + PCGCTL);
+ dwc2_writel(hsotg, pcgcctl, PCGCTL);
/* Umnask global Interrupt in GAHBCFG and restore it */
- dwc2_writel(gr->gahbcfg | GAHBCFG_GLBL_INTR_EN, hsotg->regs + GAHBCFG);
+ dwc2_writel(hsotg, gr->gahbcfg | GAHBCFG_GLBL_INTR_EN, GAHBCFG);
/* Clear all pending interupts */
- dwc2_writel(0xffffffff, hsotg->regs + GINTSTS);
+ dwc2_writel(hsotg, 0xffffffff, GINTSTS);
/* Unmask restore done interrupt */
- dwc2_writel(GINTSTS_RESTOREDONE, hsotg->regs + GINTMSK);
+ dwc2_writel(hsotg, GINTSTS_RESTOREDONE, GINTMSK);
/* Restore GUSBCFG and HCFG/DCFG */
- dwc2_writel(gr->gusbcfg, hsotg->regs + GUSBCFG);
+ dwc2_writel(hsotg, gr->gusbcfg, GUSBCFG);
if (is_host) {
- dwc2_writel(hr->hcfg, hsotg->regs + HCFG);
+ dwc2_writel(hsotg, hr->hcfg, HCFG);
if (rmode)
pcgcctl |= PCGCTL_RESTOREMODE;
- dwc2_writel(pcgcctl, hsotg->regs + PCGCTL);
+ dwc2_writel(hsotg, pcgcctl, PCGCTL);
udelay(10);
pcgcctl |= PCGCTL_ESS_REG_RESTORED;
- dwc2_writel(pcgcctl, hsotg->regs + PCGCTL);
+ dwc2_writel(hsotg, pcgcctl, PCGCTL);
udelay(10);
} else {
- dwc2_writel(dr->dcfg, hsotg->regs + DCFG);
+ dwc2_writel(hsotg, dr->dcfg, DCFG);
if (!rmode)
pcgcctl |= PCGCTL_RESTOREMODE | PCGCTL_RSTPDWNMODULE;
- dwc2_writel(pcgcctl, hsotg->regs + PCGCTL);
+ dwc2_writel(hsotg, pcgcctl, PCGCTL);
udelay(10);
pcgcctl |= PCGCTL_ESS_REG_RESTORED;
- dwc2_writel(pcgcctl, hsotg->regs + PCGCTL);
+ dwc2_writel(hsotg, pcgcctl, PCGCTL);
udelay(10);
}
}
@@ -322,42 +322,42 @@ void dwc2_hib_restore_common(struct dwc2_hsotg *hsotg, int rem_wakeup,
u32 gpwrdn;
/* Switch-on voltage to the core */
- gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn = dwc2_readl(hsotg, GPWRDN);
gpwrdn &= ~GPWRDN_PWRDNSWTCH;
- dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ dwc2_writel(hsotg, gpwrdn, GPWRDN);
udelay(10);
/* Reset core */
- gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn = dwc2_readl(hsotg, GPWRDN);
gpwrdn &= ~GPWRDN_PWRDNRSTN;
- dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ dwc2_writel(hsotg, gpwrdn, GPWRDN);
udelay(10);
/* Enable restore from PMU */
- gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn = dwc2_readl(hsotg, GPWRDN);
gpwrdn |= GPWRDN_RESTORE;
- dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ dwc2_writel(hsotg, gpwrdn, GPWRDN);
udelay(10);
/* Disable Power Down Clamp */
- gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn = dwc2_readl(hsotg, GPWRDN);
gpwrdn &= ~GPWRDN_PWRDNCLMP;
- dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ dwc2_writel(hsotg, gpwrdn, GPWRDN);
udelay(50);
if (!is_host && rem_wakeup)
udelay(70);
/* Deassert reset core */
- gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn = dwc2_readl(hsotg, GPWRDN);
gpwrdn |= GPWRDN_PWRDNRSTN;
- dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ dwc2_writel(hsotg, gpwrdn, GPWRDN);
udelay(10);
/* Disable PMU interrupt */
- gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn = dwc2_readl(hsotg, GPWRDN);
gpwrdn &= ~GPWRDN_PMUINTSEL;
- dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ dwc2_writel(hsotg, gpwrdn, GPWRDN);
udelay(10);
/* Set Restore Essential Regs bit in PCGCCTL register */
@@ -419,6 +419,8 @@ static void dwc2_wait_for_mode(struct dwc2_hsotg *hsotg,
/**
* dwc2_iddig_filter_enabled() - Returns true if the IDDIG debounce
* filter is enabled.
+ *
+ * @hsotg: Programming view of DWC_otg controller
*/
static bool dwc2_iddig_filter_enabled(struct dwc2_hsotg *hsotg)
{
@@ -429,7 +431,7 @@ static bool dwc2_iddig_filter_enabled(struct dwc2_hsotg *hsotg)
return false;
/* Check if core configuration includes the IDDIG filter. */
- ghwcfg4 = dwc2_readl(hsotg->regs + GHWCFG4);
+ ghwcfg4 = dwc2_readl(hsotg, GHWCFG4);
if (!(ghwcfg4 & GHWCFG4_IDDIG_FILT_EN))
return false;
@@ -437,9 +439,9 @@ static bool dwc2_iddig_filter_enabled(struct dwc2_hsotg *hsotg)
* Check if the IDDIG debounce filter is bypassed. Available
* in core version >= 3.10a.
*/
- gsnpsid = dwc2_readl(hsotg->regs + GSNPSID);
+ gsnpsid = dwc2_readl(hsotg, GSNPSID);
if (gsnpsid >= DWC2_CORE_REV_3_10a) {
- u32 gotgctl = dwc2_readl(hsotg->regs + GOTGCTL);
+ u32 gotgctl = dwc2_readl(hsotg, GOTGCTL);
if (gotgctl & GOTGCTL_DBNCE_FLTR_BYPASS)
return false;
@@ -508,8 +510,8 @@ int dwc2_core_reset(struct dwc2_hsotg *hsotg, bool skip_wait)
* reset and account for this delay after the reset.
*/
if (dwc2_iddig_filter_enabled(hsotg)) {
- u32 gotgctl = dwc2_readl(hsotg->regs + GOTGCTL);
- u32 gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
+ u32 gotgctl = dwc2_readl(hsotg, GOTGCTL);
+ u32 gusbcfg = dwc2_readl(hsotg, GUSBCFG);
if (!(gotgctl & GOTGCTL_CONID_B) ||
(gusbcfg & GUSBCFG_FORCEHOSTMODE)) {
@@ -518,9 +520,9 @@ int dwc2_core_reset(struct dwc2_hsotg *hsotg, bool skip_wait)
}
/* Core Soft Reset */
- greset = dwc2_readl(hsotg->regs + GRSTCTL);
+ greset = dwc2_readl(hsotg, GRSTCTL);
greset |= GRSTCTL_CSFTRST;
- dwc2_writel(greset, hsotg->regs + GRSTCTL);
+ dwc2_writel(hsotg, greset, GRSTCTL);
if (dwc2_hsotg_wait_bit_clear(hsotg, GRSTCTL, GRSTCTL_CSFTRST, 50)) {
dev_warn(hsotg->dev, "%s: HANG! Soft Reset timeout GRSTCTL GRSTCTL_CSFTRST\n",
@@ -564,6 +566,9 @@ int dwc2_core_reset(struct dwc2_hsotg *hsotg, bool skip_wait)
* If a force is done, it requires a IDDIG debounce filter delay if
* the filter is configured and enabled. We poll the current mode of
* the controller to account for this delay.
+ *
+ * @hsotg: Programming view of DWC_otg controller
+ * @host: Host mode flag
*/
void dwc2_force_mode(struct dwc2_hsotg *hsotg, bool host)
{
@@ -589,14 +594,14 @@ void dwc2_force_mode(struct dwc2_hsotg *hsotg, bool host)
if (WARN_ON(!host && hsotg->dr_mode == USB_DR_MODE_HOST))
return;
- gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
+ gusbcfg = dwc2_readl(hsotg, GUSBCFG);
set = host ? GUSBCFG_FORCEHOSTMODE : GUSBCFG_FORCEDEVMODE;
clear = host ? GUSBCFG_FORCEDEVMODE : GUSBCFG_FORCEHOSTMODE;
gusbcfg &= ~clear;
gusbcfg |= set;
- dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG);
+ dwc2_writel(hsotg, gusbcfg, GUSBCFG);
dwc2_wait_for_mode(hsotg, host);
return;
@@ -610,6 +615,8 @@ void dwc2_force_mode(struct dwc2_hsotg *hsotg, bool host)
* or not because the value of the connector ID status is affected by
* the force mode. We only need to call this once during probe if
* dr_mode == OTG.
+ *
+ * @hsotg: Programming view of DWC_otg controller
*/
static void dwc2_clear_force_mode(struct dwc2_hsotg *hsotg)
{
@@ -620,10 +627,10 @@ static void dwc2_clear_force_mode(struct dwc2_hsotg *hsotg)
dev_dbg(hsotg->dev, "Clearing force mode bits\n");
- gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
+ gusbcfg = dwc2_readl(hsotg, GUSBCFG);
gusbcfg &= ~GUSBCFG_FORCEHOSTMODE;
gusbcfg &= ~GUSBCFG_FORCEDEVMODE;
- dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG);
+ dwc2_writel(hsotg, gusbcfg, GUSBCFG);
if (dwc2_iddig_filter_enabled(hsotg))
msleep(100);
@@ -663,11 +670,11 @@ void dwc2_force_dr_mode(struct dwc2_hsotg *hsotg)
void dwc2_enable_acg(struct dwc2_hsotg *hsotg)
{
if (hsotg->params.acg_enable) {
- u32 pcgcctl1 = dwc2_readl(hsotg->regs + PCGCCTL1);
+ u32 pcgcctl1 = dwc2_readl(hsotg, PCGCCTL1);
dev_dbg(hsotg->dev, "Enabling Active Clock Gating\n");
pcgcctl1 |= PCGCCTL1_GATEEN;
- dwc2_writel(pcgcctl1, hsotg->regs + PCGCCTL1);
+ dwc2_writel(hsotg, pcgcctl1, PCGCCTL1);
}
}
@@ -688,56 +695,57 @@ void dwc2_dump_host_registers(struct dwc2_hsotg *hsotg)
dev_dbg(hsotg->dev, "Host Global Registers\n");
addr = hsotg->regs + HCFG;
dev_dbg(hsotg->dev, "HCFG @0x%08lX : 0x%08X\n",
- (unsigned long)addr, dwc2_readl(addr));
+ (unsigned long)addr, dwc2_readl(hsotg, HCFG));
addr = hsotg->regs + HFIR;
dev_dbg(hsotg->dev, "HFIR @0x%08lX : 0x%08X\n",
- (unsigned long)addr, dwc2_readl(addr));
+ (unsigned long)addr, dwc2_readl(hsotg, HFIR));
addr = hsotg->regs + HFNUM;
dev_dbg(hsotg->dev, "HFNUM @0x%08lX : 0x%08X\n",
- (unsigned long)addr, dwc2_readl(addr));
+ (unsigned long)addr, dwc2_readl(hsotg, HFNUM));
addr = hsotg->regs + HPTXSTS;
dev_dbg(hsotg->dev, "HPTXSTS @0x%08lX : 0x%08X\n",
- (unsigned long)addr, dwc2_readl(addr));
+ (unsigned long)addr, dwc2_readl(hsotg, HPTXSTS));
addr = hsotg->regs + HAINT;
dev_dbg(hsotg->dev, "HAINT @0x%08lX : 0x%08X\n",
- (unsigned long)addr, dwc2_readl(addr));
+ (unsigned long)addr, dwc2_readl(hsotg, HAINT));
addr = hsotg->regs + HAINTMSK;
dev_dbg(hsotg->dev, "HAINTMSK @0x%08lX : 0x%08X\n",
- (unsigned long)addr, dwc2_readl(addr));
+ (unsigned long)addr, dwc2_readl(hsotg, HAINTMSK));
if (hsotg->params.dma_desc_enable) {
addr = hsotg->regs + HFLBADDR;
dev_dbg(hsotg->dev, "HFLBADDR @0x%08lX : 0x%08X\n",
- (unsigned long)addr, dwc2_readl(addr));
+ (unsigned long)addr, dwc2_readl(hsotg, HFLBADDR));
}
addr = hsotg->regs + HPRT0;
dev_dbg(hsotg->dev, "HPRT0 @0x%08lX : 0x%08X\n",
- (unsigned long)addr, dwc2_readl(addr));
+ (unsigned long)addr, dwc2_readl(hsotg, HPRT0));
for (i = 0; i < hsotg->params.host_channels; i++) {
dev_dbg(hsotg->dev, "Host Channel %d Specific Registers\n", i);
addr = hsotg->regs + HCCHAR(i);
dev_dbg(hsotg->dev, "HCCHAR @0x%08lX : 0x%08X\n",
- (unsigned long)addr, dwc2_readl(addr));
+ (unsigned long)addr, dwc2_readl(hsotg, HCCHAR(i)));
addr = hsotg->regs + HCSPLT(i);
dev_dbg(hsotg->dev, "HCSPLT @0x%08lX : 0x%08X\n",
- (unsigned long)addr, dwc2_readl(addr));
+ (unsigned long)addr, dwc2_readl(hsotg, HCSPLT(i)));
addr = hsotg->regs + HCINT(i);
dev_dbg(hsotg->dev, "HCINT @0x%08lX : 0x%08X\n",
- (unsigned long)addr, dwc2_readl(addr));
+ (unsigned long)addr, dwc2_readl(hsotg, HCINT(i)));
addr = hsotg->regs + HCINTMSK(i);
dev_dbg(hsotg->dev, "HCINTMSK @0x%08lX : 0x%08X\n",
- (unsigned long)addr, dwc2_readl(addr));
+ (unsigned long)addr, dwc2_readl(hsotg, HCINTMSK(i)));
addr = hsotg->regs + HCTSIZ(i);
dev_dbg(hsotg->dev, "HCTSIZ @0x%08lX : 0x%08X\n",
- (unsigned long)addr, dwc2_readl(addr));
+ (unsigned long)addr, dwc2_readl(hsotg, HCTSIZ(i)));
addr = hsotg->regs + HCDMA(i);
dev_dbg(hsotg->dev, "HCDMA @0x%08lX : 0x%08X\n",
- (unsigned long)addr, dwc2_readl(addr));
+ (unsigned long)addr, dwc2_readl(hsotg, HCDMA(i)));
if (hsotg->params.dma_desc_enable) {
addr = hsotg->regs + HCDMAB(i);
dev_dbg(hsotg->dev, "HCDMAB @0x%08lX : 0x%08X\n",
- (unsigned long)addr, dwc2_readl(addr));
+ (unsigned long)addr, dwc2_readl(hsotg,
+ HCDMAB(i)));
}
}
#endif
@@ -759,80 +767,80 @@ void dwc2_dump_global_registers(struct dwc2_hsotg *hsotg)
dev_dbg(hsotg->dev, "Core Global Registers\n");
addr = hsotg->regs + GOTGCTL;
dev_dbg(hsotg->dev, "GOTGCTL @0x%08lX : 0x%08X\n",
- (unsigned long)addr, dwc2_readl(addr));
+ (unsigned long)addr, dwc2_readl(hsotg, GOTGCTL));
addr = hsotg->regs + GOTGINT;
dev_dbg(hsotg->dev, "GOTGINT @0x%08lX : 0x%08X\n",
- (unsigned long)addr, dwc2_readl(addr));
+ (unsigned long)addr, dwc2_readl(hsotg, GOTGINT));
addr = hsotg->regs + GAHBCFG;
dev_dbg(hsotg->dev, "GAHBCFG @0x%08lX : 0x%08X\n",
- (unsigned long)addr, dwc2_readl(addr));
+ (unsigned long)addr, dwc2_readl(hsotg, GAHBCFG));
addr = hsotg->regs + GUSBCFG;
dev_dbg(hsotg->dev, "GUSBCFG @0x%08lX : 0x%08X\n",
- (unsigned long)addr, dwc2_readl(addr));
+ (unsigned long)addr, dwc2_readl(hsotg, GUSBCFG));
addr = hsotg->regs + GRSTCTL;
dev_dbg(hsotg->dev, "GRSTCTL @0x%08lX : 0x%08X\n",
- (unsigned long)addr, dwc2_readl(addr));
+ (unsigned long)addr, dwc2_readl(hsotg, GRSTCTL));
addr = hsotg->regs + GINTSTS;
dev_dbg(hsotg->dev, "GINTSTS @0x%08lX : 0x%08X\n",
- (unsigned long)addr, dwc2_readl(addr));
+ (unsigned long)addr, dwc2_readl(hsotg, GINTSTS));
addr = hsotg->regs + GINTMSK;
dev_dbg(hsotg->dev, "GINTMSK @0x%08lX : 0x%08X\n",
- (unsigned long)addr, dwc2_readl(addr));
+ (unsigned long)addr, dwc2_readl(hsotg, GINTMSK));
addr = hsotg->regs + GRXSTSR;
dev_dbg(hsotg->dev, "GRXSTSR @0x%08lX : 0x%08X\n",
- (unsigned long)addr, dwc2_readl(addr));
+ (unsigned long)addr, dwc2_readl(hsotg, GRXSTSR));
addr = hsotg->regs + GRXFSIZ;
dev_dbg(hsotg->dev, "GRXFSIZ @0x%08lX : 0x%08X\n",
- (unsigned long)addr, dwc2_readl(addr));
+ (unsigned long)addr, dwc2_readl(hsotg, GRXFSIZ));
addr = hsotg->regs + GNPTXFSIZ;
dev_dbg(hsotg->dev, "GNPTXFSIZ @0x%08lX : 0x%08X\n",
- (unsigned long)addr, dwc2_readl(addr));
+ (unsigned long)addr, dwc2_readl(hsotg, GNPTXFSIZ));
addr = hsotg->regs + GNPTXSTS;
dev_dbg(hsotg->dev, "GNPTXSTS @0x%08lX : 0x%08X\n",
- (unsigned long)addr, dwc2_readl(addr));
+ (unsigned long)addr, dwc2_readl(hsotg, GNPTXSTS));
addr = hsotg->regs + GI2CCTL;
dev_dbg(hsotg->dev, "GI2CCTL @0x%08lX : 0x%08X\n",
- (unsigned long)addr, dwc2_readl(addr));
+ (unsigned long)addr, dwc2_readl(hsotg, GI2CCTL));
addr = hsotg->regs + GPVNDCTL;
dev_dbg(hsotg->dev, "GPVNDCTL @0x%08lX : 0x%08X\n",
- (unsigned long)addr, dwc2_readl(addr));
+ (unsigned long)addr, dwc2_readl(hsotg, GPVNDCTL));
addr = hsotg->regs + GGPIO;
dev_dbg(hsotg->dev, "GGPIO @0x%08lX : 0x%08X\n",
- (unsigned long)addr, dwc2_readl(addr));
+ (unsigned long)addr, dwc2_readl(hsotg, GGPIO));
addr = hsotg->regs + GUID;
dev_dbg(hsotg->dev, "GUID @0x%08lX : 0x%08X\n",
- (unsigned long)addr, dwc2_readl(addr));
+ (unsigned long)addr, dwc2_readl(hsotg, GUID));
addr = hsotg->regs + GSNPSID;
dev_dbg(hsotg->dev, "GSNPSID @0x%08lX : 0x%08X\n",
- (unsigned long)addr, dwc2_readl(addr));
+ (unsigned long)addr, dwc2_readl(hsotg, GSNPSID));
addr = hsotg->regs + GHWCFG1;
dev_dbg(hsotg->dev, "GHWCFG1 @0x%08lX : 0x%08X\n",
- (unsigned long)addr, dwc2_readl(addr));
+ (unsigned long)addr, dwc2_readl(hsotg, GHWCFG1));
addr = hsotg->regs + GHWCFG2;
dev_dbg(hsotg->dev, "GHWCFG2 @0x%08lX : 0x%08X\n",
- (unsigned long)addr, dwc2_readl(addr));
+ (unsigned long)addr, dwc2_readl(hsotg, GHWCFG2));
addr = hsotg->regs + GHWCFG3;
dev_dbg(hsotg->dev, "GHWCFG3 @0x%08lX : 0x%08X\n",
- (unsigned long)addr, dwc2_readl(addr));
+ (unsigned long)addr, dwc2_readl(hsotg, GHWCFG3));
addr = hsotg->regs + GHWCFG4;
dev_dbg(hsotg->dev, "GHWCFG4 @0x%08lX : 0x%08X\n",
- (unsigned long)addr, dwc2_readl(addr));
+ (unsigned long)addr, dwc2_readl(hsotg, GHWCFG4));
addr = hsotg->regs + GLPMCFG;
dev_dbg(hsotg->dev, "GLPMCFG @0x%08lX : 0x%08X\n",
- (unsigned long)addr, dwc2_readl(addr));
+ (unsigned long)addr, dwc2_readl(hsotg, GLPMCFG));
addr = hsotg->regs + GPWRDN;
dev_dbg(hsotg->dev, "GPWRDN @0x%08lX : 0x%08X\n",
- (unsigned long)addr, dwc2_readl(addr));
+ (unsigned long)addr, dwc2_readl(hsotg, GPWRDN));
addr = hsotg->regs + GDFIFOCFG;
dev_dbg(hsotg->dev, "GDFIFOCFG @0x%08lX : 0x%08X\n",
- (unsigned long)addr, dwc2_readl(addr));
+ (unsigned long)addr, dwc2_readl(hsotg, GDFIFOCFG));
addr = hsotg->regs + HPTXFSIZ;
dev_dbg(hsotg->dev, "HPTXFSIZ @0x%08lX : 0x%08X\n",
- (unsigned long)addr, dwc2_readl(addr));
+ (unsigned long)addr, dwc2_readl(hsotg, HPTXFSIZ));
addr = hsotg->regs + PCGCTL;
dev_dbg(hsotg->dev, "PCGCTL @0x%08lX : 0x%08X\n",
- (unsigned long)addr, dwc2_readl(addr));
+ (unsigned long)addr, dwc2_readl(hsotg, PCGCTL));
#endif
}
@@ -855,7 +863,7 @@ void dwc2_flush_tx_fifo(struct dwc2_hsotg *hsotg, const int num)
greset = GRSTCTL_TXFFLSH;
greset |= num << GRSTCTL_TXFNUM_SHIFT & GRSTCTL_TXFNUM_MASK;
- dwc2_writel(greset, hsotg->regs + GRSTCTL);
+ dwc2_writel(hsotg, greset, GRSTCTL);
if (dwc2_hsotg_wait_bit_clear(hsotg, GRSTCTL, GRSTCTL_TXFFLSH, 10000))
dev_warn(hsotg->dev, "%s: HANG! timeout GRSTCTL GRSTCTL_TXFFLSH\n",
@@ -882,7 +890,7 @@ void dwc2_flush_rx_fifo(struct dwc2_hsotg *hsotg)
__func__);
greset = GRSTCTL_RXFFLSH;
- dwc2_writel(greset, hsotg->regs + GRSTCTL);
+ dwc2_writel(hsotg, greset, GRSTCTL);
/* Wait for RxFIFO flush done */
if (dwc2_hsotg_wait_bit_clear(hsotg, GRSTCTL, GRSTCTL_RXFFLSH, 10000))
@@ -895,7 +903,7 @@ void dwc2_flush_rx_fifo(struct dwc2_hsotg *hsotg)
bool dwc2_is_controller_alive(struct dwc2_hsotg *hsotg)
{
- if (dwc2_readl(hsotg->regs + GSNPSID) == 0xffffffff)
+ if (dwc2_readl(hsotg, GSNPSID) == 0xffffffff)
return false;
else
return true;
@@ -909,10 +917,10 @@ bool dwc2_is_controller_alive(struct dwc2_hsotg *hsotg)
*/
void dwc2_enable_global_interrupts(struct dwc2_hsotg *hsotg)
{
- u32 ahbcfg = dwc2_readl(hsotg->regs + GAHBCFG);
+ u32 ahbcfg = dwc2_readl(hsotg, GAHBCFG);
ahbcfg |= GAHBCFG_GLBL_INTR_EN;
- dwc2_writel(ahbcfg, hsotg->regs + GAHBCFG);
+ dwc2_writel(hsotg, ahbcfg, GAHBCFG);
}
/**
@@ -923,16 +931,16 @@ void dwc2_enable_global_interrupts(struct dwc2_hsotg *hsotg)
*/
void dwc2_disable_global_interrupts(struct dwc2_hsotg *hsotg)
{
- u32 ahbcfg = dwc2_readl(hsotg->regs + GAHBCFG);
+ u32 ahbcfg = dwc2_readl(hsotg, GAHBCFG);
ahbcfg &= ~GAHBCFG_GLBL_INTR_EN;
- dwc2_writel(ahbcfg, hsotg->regs + GAHBCFG);
+ dwc2_writel(hsotg, ahbcfg, GAHBCFG);
}
/* Returns the controller's GHWCFG2.OTG_MODE. */
unsigned int dwc2_op_mode(struct dwc2_hsotg *hsotg)
{
- u32 ghwcfg2 = dwc2_readl(hsotg->regs + GHWCFG2);
+ u32 ghwcfg2 = dwc2_readl(hsotg, GHWCFG2);
return (ghwcfg2 & GHWCFG2_OP_MODE_MASK) >>
GHWCFG2_OP_MODE_SHIFT;
@@ -981,7 +989,7 @@ int dwc2_hsotg_wait_bit_set(struct dwc2_hsotg *hsotg, u32 offset, u32 mask,
u32 i;
for (i = 0; i < timeout; i++) {
- if (dwc2_readl(hsotg->regs + offset) & mask)
+ if (dwc2_readl(hsotg, offset) & mask)
return 0;
udelay(1);
}
@@ -1004,7 +1012,7 @@ int dwc2_hsotg_wait_bit_clear(struct dwc2_hsotg *hsotg, u32 offset, u32 mask,
u32 i;
for (i = 0; i < timeout; i++) {
- if (!(dwc2_readl(hsotg->regs + offset) & mask))
+ if (!(dwc2_readl(hsotg, offset) & mask))
return 0;
udelay(1);
}
diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h
index a666e0758a99..30bab8463c96 100644
--- a/drivers/usb/dwc2/core.h
+++ b/drivers/usb/dwc2/core.h
@@ -65,60 +65,6 @@
DWC2_TRACE_SCHEDULER_VB(pr_fmt("%s: SCH: " fmt), \
dev_name(hsotg->dev), ##__VA_ARGS__)
-#ifdef CONFIG_MIPS
-/*
- * There are some MIPS machines that can run in either big-endian
- * or little-endian mode and that use the dwc2 register without
- * a byteswap in both ways.
- * Unlike other architectures, MIPS apparently does not require a
- * barrier before the __raw_writel() to synchronize with DMA but does
- * require the barrier after the __raw_writel() to serialize a set of
- * writes. This set of operations was added specifically for MIPS and
- * should only be used there.
- */
-static inline u32 dwc2_readl(const void __iomem *addr)
-{
- u32 value = __raw_readl(addr);
-
- /* In order to preserve endianness __raw_* operation is used. Therefore
- * a barrier is needed to ensure IO access is not re-ordered across
- * reads or writes
- */
- mb();
- return value;
-}
-
-static inline void dwc2_writel(u32 value, void __iomem *addr)
-{
- __raw_writel(value, addr);
-
- /*
- * In order to preserve endianness __raw_* operation is used. Therefore
- * a barrier is needed to ensure IO access is not re-ordered across
- * reads or writes
- */
- mb();
-#ifdef DWC2_LOG_WRITES
- pr_info("INFO:: wrote %08x to %p\n", value, addr);
-#endif
-}
-#else
-/* Normal architectures just use readl/write */
-static inline u32 dwc2_readl(const void __iomem *addr)
-{
- return readl(addr);
-}
-
-static inline void dwc2_writel(u32 value, void __iomem *addr)
-{
- writel(value, addr);
-
-#ifdef DWC2_LOG_WRITES
- pr_info("info:: wrote %08x to %p\n", value, addr);
-#endif
-}
-#endif
-
/* Maximum number of Endpoints/HostChannels */
#define MAX_EPS_CHANNELS 16
@@ -164,12 +110,11 @@ struct dwc2_hsotg_req;
* and has yet to be completed (maybe due to data move, or simply
* awaiting an ack from the core all the data has been completed).
* @debugfs: File entry for debugfs file for this endpoint.
- * @lock: State lock to protect contents of endpoint.
* @dir_in: Set to true if this endpoint is of the IN direction, which
* means that it is sending data to the Host.
* @index: The index for the endpoint registers.
* @mc: Multi Count - number of transactions per microframe
- * @interval - Interval for periodic endpoints, in frames or microframes.
+ * @interval: Interval for periodic endpoints, in frames or microframes.
* @name: The name array passed to the USB core.
* @halted: Set if the endpoint has been halted.
* @periodic: Set if this is a periodic ep, such as Interrupt
@@ -178,10 +123,11 @@ struct dwc2_hsotg_req;
* @desc_list_dma: The DMA address of descriptor chain currently in use.
* @desc_list: Pointer to descriptor DMA chain head currently in use.
* @desc_count: Count of entries within the DMA descriptor chain of EP.
- * @isoc_chain_num: Number of ISOC chain currently in use - either 0 or 1.
* @next_desc: index of next free descriptor in the ISOC chain under SW control.
+ * @compl_desc: index of next descriptor to be completed by xFerComplete
* @total_data: The total number of data bytes done.
* @fifo_size: The size of the FIFO (for periodic IN endpoints)
+ * @fifo_index: For Dedicated FIFO operation, only FIFO0 can be used for EP0.
* @fifo_load: The amount of data loaded into the FIFO (periodic IN)
* @last_load: The offset of data for the last start of request.
* @size_loaded: The last loaded size for DxEPTSIZE for periodic IN
@@ -231,8 +177,8 @@ struct dwc2_hsotg_ep {
struct dwc2_dma_desc *desc_list;
u8 desc_count;
- unsigned char isoc_chain_num;
unsigned int next_desc;
+ unsigned int compl_desc;
char name[10];
};
@@ -380,6 +326,12 @@ enum dwc2_ep0_state {
* is FS.
* 0 - No (default)
* 1 - Yes
+ * @ipg_isoc_en: Indicates the IPG supports is enabled or disabled.
+ * 0 - Disable (default)
+ * 1 - Enable
+ * @acg_enable: For enabling Active Clock Gating in the controller
+ * 0 - No
+ * 1 - Yes
* @ulpi_fs_ls: Make ULPI phy operate in FS/LS mode only
* 0 - No (default)
* 1 - Yes
@@ -441,6 +393,20 @@ enum dwc2_ep0_state {
* 0 - No
* 1 - Yes
* @hird_threshold: Value of BESL or HIRD Threshold.
+ * @ref_clk_per: Indicates in terms of pico seconds the period
+ * of ref_clk.
+ * 62500 - 16MHz
+ * 58823 - 17MHz
+ * 52083 - 19.2MHz
+ * 50000 - 20MHz
+ * 41666 - 24MHz
+ * 33333 - 30MHz (default)
+ * 25000 - 40MHz
+ * @sof_cnt_wkup_alert: Indicates in term of number of SOF's after which
+ * the controller should generate an interrupt if the
+ * device had been in L1 state until that period.
+ * This is used by SW to initiate Remote WakeUp in the
+ * controller so as to sync to the uF number from the host.
* @activate_stm_fs_transceiver: Activate internal transceiver using GGPIO
* register.
* 0 - Deactivate the transceiver (default)
@@ -464,6 +430,9 @@ enum dwc2_ep0_state {
* back to DWC2_SPEED_PARAM_HIGH while device is gone.
* 0 - No (default)
* 1 - Yes
+ * @service_interval: Enable service interval based scheduling.
+ * 0 - No
+ * 1 - Yes
*
* The following parameters may be specified when starting the module. These
* parameters define how the DWC_otg controller should be configured. A
@@ -509,12 +478,18 @@ struct dwc2_core_params {
bool lpm_clock_gating;
bool besl;
bool hird_threshold_en;
+ bool service_interval;
u8 hird_threshold;
bool activate_stm_fs_transceiver;
+ bool ipg_isoc_en;
u16 max_packet_count;
u32 max_transfer_size;
u32 ahbcfg;
+ /* GREFCLK parameters */
+ u32 ref_clk_per;
+ u16 sof_cnt_wkup_alert;
+
/* Host parameters */
bool host_dma;
bool dma_desc_enable;
@@ -548,7 +523,7 @@ struct dwc2_core_params {
*
* The values that are not in dwc2_core_params are documented below.
*
- * @op_mode Mode of Operation
+ * @op_mode: Mode of Operation
* 0 - HNP- and SRP-Capable OTG (Host & Device)
* 1 - SRP-Capable OTG (Host & Device)
* 2 - Non-HNP and Non-SRP Capable OTG (Host & Device)
@@ -556,43 +531,106 @@ struct dwc2_core_params {
* 4 - Non-OTG Device
* 5 - SRP-Capable Host
* 6 - Non-OTG Host
- * @arch Architecture
+ * @arch: Architecture
* 0 - Slave only
* 1 - External DMA
* 2 - Internal DMA
- * @power_optimized Are power optimizations enabled?
- * @num_dev_ep Number of device endpoints available
- * @num_dev_in_eps Number of device IN endpoints available
- * @num_dev_perio_in_ep Number of device periodic IN endpoints
- * available
- * @dev_token_q_depth Device Mode IN Token Sequence Learning Queue
+ * @ipg_isoc_en: This feature indicates that the controller supports
+ * the worst-case scenario of Rx followed by Rx
+ * Interpacket Gap (IPG) (32 bitTimes) as per the utmi
+ * specification for any token following ISOC OUT token.
+ * 0 - Don't support
+ * 1 - Support
+ * @power_optimized: Are power optimizations enabled?
+ * @num_dev_ep: Number of device endpoints available
+ * @num_dev_in_eps: Number of device IN endpoints available
+ * @num_dev_perio_in_ep: Number of device periodic IN endpoints
+ * available
+ * @dev_token_q_depth: Device Mode IN Token Sequence Learning Queue
* Depth
* 0 to 30
- * @host_perio_tx_q_depth
+ * @host_perio_tx_q_depth:
* Host Mode Periodic Request Queue Depth
* 2, 4 or 8
- * @nperio_tx_q_depth
+ * @nperio_tx_q_depth:
* Non-Periodic Request Queue Depth
* 2, 4 or 8
- * @hs_phy_type High-speed PHY interface type
+ * @hs_phy_type: High-speed PHY interface type
* 0 - High-speed interface not supported
* 1 - UTMI+
* 2 - ULPI
* 3 - UTMI+ and ULPI
- * @fs_phy_type Full-speed PHY interface type
+ * @fs_phy_type: Full-speed PHY interface type
* 0 - Full speed interface not supported
* 1 - Dedicated full speed interface
* 2 - FS pins shared with UTMI+ pins
* 3 - FS pins shared with ULPI pins
* @total_fifo_size: Total internal RAM for FIFOs (bytes)
- * @hibernation Is hibernation enabled?
- * @utmi_phy_data_width UTMI+ PHY data width
+ * @hibernation: Is hibernation enabled?
+ * @utmi_phy_data_width: UTMI+ PHY data width
* 0 - 8 bits
* 1 - 16 bits
* 2 - 8 or 16 bits
* @snpsid: Value from SNPSID register
* @dev_ep_dirs: Direction of device endpoints (GHWCFG1)
- * @g_tx_fifo_size[] Power-on values of TxFIFO sizes
+ * @g_tx_fifo_size: Power-on values of TxFIFO sizes
+ * @dma_desc_enable: When DMA mode is enabled, specifies whether to use
+ * address DMA mode or descriptor DMA mode for accessing
+ * the data FIFOs. The driver will automatically detect the
+ * value for this if none is specified.
+ * 0 - Address DMA
+ * 1 - Descriptor DMA (default, if available)
+ * @enable_dynamic_fifo: 0 - Use coreConsultant-specified FIFO size parameters
+ * 1 - Allow dynamic FIFO sizing (default, if available)
+ * @en_multiple_tx_fifo: Specifies whether dedicated per-endpoint transmit FIFOs
+ * are enabled for non-periodic IN endpoints in device
+ * mode.
+ * @host_nperio_tx_fifo_size: Number of 4-byte words in the non-periodic Tx FIFO
+ * in host mode when dynamic FIFO sizing is enabled
+ * 16 to 32768
+ * Actual maximum value is autodetected and also
+ * the default.
+ * @host_perio_tx_fifo_size: Number of 4-byte words in the periodic Tx FIFO in
+ * host mode when dynamic FIFO sizing is enabled
+ * 16 to 32768
+ * Actual maximum value is autodetected and also
+ * the default.
+ * @max_transfer_size: The maximum transfer size supported, in bytes
+ * 2047 to 65,535
+ * Actual maximum value is autodetected and also
+ * the default.
+ * @max_packet_count: The maximum number of packets in a transfer
+ * 15 to 511
+ * Actual maximum value is autodetected and also
+ * the default.
+ * @host_channels: The number of host channel registers to use
+ * 1 to 16
+ * Actual maximum value is autodetected and also
+ * the default.
+ * @dev_nperio_tx_fifo_size: Number of 4-byte words in the non-periodic Tx FIFO
+ * in device mode when dynamic FIFO sizing is enabled
+ * 16 to 32768
+ * Actual maximum value is autodetected and also
+ * the default.
+ * @i2c_enable: Specifies whether to use the I2Cinterface for a full
+ * speed PHY. This parameter is only applicable if phy_type
+ * is FS.
+ * 0 - No (default)
+ * 1 - Yes
+ * @acg_enable: For enabling Active Clock Gating in the controller
+ * 0 - Disable
+ * 1 - Enable
+ * @lpm_mode: For enabling Link Power Management in the controller
+ * 0 - Disable
+ * 1 - Enable
+ * @rx_fifo_size: Number of 4-byte words in the Rx FIFO when dynamic
+ * FIFO sizing is enabled 16 to 32768
+ * Actual maximum value is autodetected and also
+ * the default.
+ * @service_interval_mode: For enabling service interval based scheduling in the
+ * controller.
+ * 0 - Disable
+ * 1 - Enable
*/
struct dwc2_hw_params {
unsigned op_mode:3;
@@ -622,6 +660,8 @@ struct dwc2_hw_params {
unsigned hibernation:1;
unsigned utmi_phy_data_width:2;
unsigned lpm_mode:1;
+ unsigned ipg_isoc_en:1;
+ unsigned service_interval_mode:1;
u32 snpsid;
u32 dev_ep_dirs;
u32 g_tx_fifo_size[MAX_EPS_CHANNELS];
@@ -642,7 +682,11 @@ struct dwc2_hw_params {
* @gi2cctl: Backup of GI2CCTL register
* @glpmcfg: Backup of GLPMCFG register
* @gdfifocfg: Backup of GDFIFOCFG register
+ * @pcgcctl: Backup of PCGCCTL register
+ * @pcgcctl1: Backup of PCGCCTL1 register
+ * @dtxfsiz: Backup of DTXFSIZ registers for each endpoint
* @gpwrdn: Backup of GPWRDN register
+ * @valid: True if registers values backuped.
*/
struct dwc2_gregs_backup {
u32 gotgctl;
@@ -675,6 +719,7 @@ struct dwc2_gregs_backup {
* @doeptsiz: Backup of DOEPTSIZ register
* @doepdma: Backup of DOEPDMA register
* @dtxfsiz: Backup of DTXFSIZ registers for each endpoint
+ * @valid: True if registers values backuped.
*/
struct dwc2_dregs_backup {
u32 dcfg;
@@ -698,9 +743,10 @@ struct dwc2_dregs_backup {
* @hcfg: Backup of HCFG register
* @haintmsk: Backup of HAINTMSK register
* @hcintmsk: Backup of HCINTMSK register
- * @hptr0: Backup of HPTR0 register
+ * @hprt0: Backup of HPTR0 register
* @hfir: Backup of HFIR register
* @hptxfsiz: Backup of HPTXFSIZ register
+ * @valid: True if registers values backuped.
*/
struct dwc2_hregs_backup {
u32 hcfg;
@@ -800,7 +846,7 @@ struct dwc2_hregs_backup {
* @regs: Pointer to controller regs
* @hw_params: Parameters that were autodetected from the
* hardware registers
- * @core_params: Parameters that define how the core should be configured
+ * @params: Parameters that define how the core should be configured
* @op_state: The operational State, during transitions (a_host=>
* a_peripheral and b_device=>b_host) this may not match
* the core, but allows the software to determine
@@ -809,10 +855,13 @@ struct dwc2_hregs_backup {
* - USB_DR_MODE_PERIPHERAL
* - USB_DR_MODE_HOST
* - USB_DR_MODE_OTG
- * @hcd_enabled Host mode sub-driver initialization indicator.
- * @gadget_enabled Peripheral mode sub-driver initialization indicator.
- * @ll_hw_enabled Status of low-level hardware resources.
+ * @hcd_enabled: Host mode sub-driver initialization indicator.
+ * @gadget_enabled: Peripheral mode sub-driver initialization indicator.
+ * @ll_hw_enabled: Status of low-level hardware resources.
* @hibernated: True if core is hibernated
+ * @frame_number: Frame number read from the core. For both device
+ * and host modes. The value ranges are from 0
+ * to HFNUM_MAX_FRNUM.
* @phy: The otg phy transceiver structure for phy control.
* @uphy: The otg phy transceiver structure for old USB phy
* control.
@@ -832,13 +881,26 @@ struct dwc2_hregs_backup {
* interrupt
* @wkp_timer: Timer object for handling Wakeup Detected interrupt
* @lx_state: Lx state of connected device
- * @gregs_backup: Backup of global registers during suspend
- * @dregs_backup: Backup of device registers during suspend
- * @hregs_backup: Backup of host registers during suspend
+ * @gr_backup: Backup of global registers during suspend
+ * @dr_backup: Backup of device registers during suspend
+ * @hr_backup: Backup of host registers during suspend
+ * @needs_byte_swap: Specifies whether the opposite endianness.
*
* These are for host mode:
*
* @flags: Flags for handling root port state changes
+ * @flags.d32: Contain all root port flags
+ * @flags.b: Separate root port flags from each other
+ * @flags.b.port_connect_status_change: True if root port connect status
+ * changed
+ * @flags.b.port_connect_status: True if device connected to root port
+ * @flags.b.port_reset_change: True if root port reset status changed
+ * @flags.b.port_enable_change: True if root port enable status changed
+ * @flags.b.port_suspend_change: True if root port suspend status changed
+ * @flags.b.port_over_current_change: True if root port over current state
+ * changed.
+ * @flags.b.port_l1_change: True if root port l1 status changed
+ * @flags.b.reserved: Reserved bits of root port register
* @non_periodic_sched_inactive: Inactive QHs in the non-periodic schedule.
* Transfers associated with these QHs are not currently
* assigned to a host channel.
@@ -847,6 +909,9 @@ struct dwc2_hregs_backup {
* assigned to a host channel.
* @non_periodic_qh_ptr: Pointer to next QH to process in the active
* non-periodic schedule
+ * @non_periodic_sched_waiting: Waiting QHs in the non-periodic schedule.
+ * Transfers associated with these QHs are not currently
+ * assigned to a host channel.
* @periodic_sched_inactive: Inactive QHs in the periodic schedule. This is a
* list of QHs for periodic transfers that are _not_
* scheduled for the next frame. Each QH in the list has an
@@ -886,8 +951,6 @@ struct dwc2_hregs_backup {
* @hs_periodic_bitmap: Bitmap used by the microframe scheduler any time the
* host is in high speed mode; low speed schedules are
* stored elsewhere since we need one per TT.
- * @frame_number: Frame number read from the core at SOF. The value ranges
- * from 0 to HFNUM_MAX_FRNUM.
* @periodic_qh_count: Count of periodic QHs, if using several eps. Used for
* SOF enable/disable.
* @free_hc_list: Free host channels in the controller. This is a list of
@@ -898,8 +961,8 @@ struct dwc2_hregs_backup {
* host channel is available for non-periodic transactions.
* @non_periodic_channels: Number of host channels assigned to non-periodic
* transfers
- * @available_host_channels Number of host channels available for the microframe
- * scheduler to use
+ * @available_host_channels: Number of host channels available for the
+ * microframe scheduler to use
* @hc_ptr_array: Array of pointers to the host channel descriptors.
* Allows accessing a host channel descriptor given the
* host channel number. This is useful in interrupt
@@ -915,6 +978,7 @@ struct dwc2_hregs_backup {
* @frame_list_sz: Frame list size
* @desc_gen_cache: Kmem cache for generic descriptors
* @desc_hsisoc_cache: Kmem cache for hs isochronous descriptors
+ * @unaligned_cache: Kmem cache for DMA mode to handle non-aligned buf
*
* These are for peripheral mode:
*
@@ -922,9 +986,6 @@ struct dwc2_hregs_backup {
* @dedicated_fifos: Set if the hardware has dedicated IN-EP fifos.
* @num_of_eps: Number of available EPs (excluding EP0)
* @debug_root: Root directrory for debugfs.
- * @debug_file: Main status file for debugfs.
- * @debug_testmode: Testmode status file for debugfs.
- * @debug_fifo: FIFO status file for debugfs.
* @ep0_reply: Request used for ep0 reply.
* @ep0_buff: Buffer for EP0 reply data, if needed.
* @ctrl_buff: Buffer for EP0 control requests.
@@ -939,7 +1000,37 @@ struct dwc2_hregs_backup {
* @ctrl_in_desc: EP0 IN data phase desc chain pointer
* @ctrl_out_desc_dma: EP0 OUT data phase desc chain DMA address
* @ctrl_out_desc: EP0 OUT data phase desc chain pointer
- * @eps: The endpoints being supplied to the gadget framework
+ * @irq: Interrupt request line number
+ * @clk: Pointer to otg clock
+ * @reset: Pointer to dwc2 reset controller
+ * @reset_ecc: Pointer to dwc2 optional reset controller in Stratix10.
+ * @regset: A pointer to a struct debugfs_regset32, which contains
+ * a pointer to an array of register definitions, the
+ * array size and the base address where the register bank
+ * is to be found.
+ * @bus_suspended: True if bus is suspended
+ * @last_frame_num: Number of last frame. Range from 0 to 32768
+ * @frame_num_array: Used only if CONFIG_USB_DWC2_TRACK_MISSED_SOFS is
+ * defined, for missed SOFs tracking. Array holds that
+ * frame numbers, which not equal to last_frame_num +1
+ * @last_frame_num_array: Used only if CONFIG_USB_DWC2_TRACK_MISSED_SOFS is
+ * defined, for missed SOFs tracking.
+ * If current_frame_number != last_frame_num+1
+ * then last_frame_num added to this array
+ * @frame_num_idx: Actual size of frame_num_array and last_frame_num_array
+ * @dumped_frame_num_array: 1 - if missed SOFs frame numbers dumbed
+ * 0 - if missed SOFs frame numbers not dumbed
+ * @fifo_mem: Total internal RAM for FIFOs (bytes)
+ * @fifo_map: Each bit intend for concrete fifo. If that bit is set,
+ * then that fifo is used
+ * @gadget: Represents a usb slave device
+ * @connected: Used in slave mode. True if device connected with host
+ * @eps_in: The IN endpoints being supplied to the gadget framework
+ * @eps_out: The OUT endpoints being supplied to the gadget framework
+ * @new_connection: Used in host mode. True if there are new connected
+ * device
+ * @enabled: Indicates the enabling state of controller
+ *
*/
struct dwc2_hsotg {
struct device *dev;
@@ -954,6 +1045,7 @@ struct dwc2_hsotg {
unsigned int gadget_enabled:1;
unsigned int ll_hw_enabled:1;
unsigned int hibernated:1;
+ u16 frame_number;
struct phy *phy;
struct usb_phy *uphy;
@@ -982,6 +1074,7 @@ struct dwc2_hsotg {
struct dentry *debug_root;
struct debugfs_regset32 *regset;
+ bool needs_byte_swap;
/* DWC OTG HW Release versions */
#define DWC2_CORE_REV_2_71a 0x4f54271a
@@ -1029,7 +1122,6 @@ struct dwc2_hsotg {
u16 periodic_usecs;
unsigned long hs_periodic_bitmap[
DIV_ROUND_UP(DWC2_HS_SCHEDULE_US, BITS_PER_LONG)];
- u16 frame_number;
u16 periodic_qh_count;
bool bus_suspended;
bool new_connection;
@@ -1061,6 +1153,8 @@ struct dwc2_hsotg {
u32 frame_list_sz;
struct kmem_cache *desc_gen_cache;
struct kmem_cache *desc_hsisoc_cache;
+ struct kmem_cache *unaligned_cache;
+#define DWC2_KMEM_UNALIGNED_BUF_SIZE 1024
#endif /* CONFIG_USB_DWC2_HOST || CONFIG_USB_DWC2_DUAL_ROLE */
@@ -1096,6 +1190,55 @@ struct dwc2_hsotg {
#endif /* CONFIG_USB_DWC2_PERIPHERAL || CONFIG_USB_DWC2_DUAL_ROLE */
};
+/* Normal architectures just use readl/write */
+static inline u32 dwc2_readl(struct dwc2_hsotg *hsotg, u32 offset)
+{
+ u32 val;
+
+ val = readl(hsotg->regs + offset);
+ if (hsotg->needs_byte_swap)
+ return swab32(val);
+ else
+ return val;
+}
+
+static inline void dwc2_writel(struct dwc2_hsotg *hsotg, u32 value, u32 offset)
+{
+ if (hsotg->needs_byte_swap)
+ writel(swab32(value), hsotg->regs + offset);
+ else
+ writel(value, hsotg->regs + offset);
+
+#ifdef DWC2_LOG_WRITES
+ pr_info("info:: wrote %08x to %p\n", value, hsotg->regs + offset);
+#endif
+}
+
+static inline void dwc2_readl_rep(struct dwc2_hsotg *hsotg, u32 offset,
+ void *buffer, unsigned int count)
+{
+ if (count) {
+ u32 *buf = buffer;
+
+ do {
+ u32 x = dwc2_readl(hsotg, offset);
+ *buf++ = x;
+ } while (--count);
+ }
+}
+
+static inline void dwc2_writel_rep(struct dwc2_hsotg *hsotg, u32 offset,
+ const void *buffer, unsigned int count)
+{
+ if (count) {
+ const u32 *buf = buffer;
+
+ do {
+ dwc2_writel(hsotg, *buf++, offset);
+ } while (--count);
+ }
+}
+
/* Reasons for halting a host channel */
enum dwc2_halt_status {
DWC2_HC_XFER_NO_HALT_STATUS,
@@ -1201,12 +1344,12 @@ bool dwc2_hw_is_device(struct dwc2_hsotg *hsotg);
*/
static inline int dwc2_is_host_mode(struct dwc2_hsotg *hsotg)
{
- return (dwc2_readl(hsotg->regs + GINTSTS) & GINTSTS_CURMODE_HOST) != 0;
+ return (dwc2_readl(hsotg, GINTSTS) & GINTSTS_CURMODE_HOST) != 0;
}
static inline int dwc2_is_device_mode(struct dwc2_hsotg *hsotg)
{
- return (dwc2_readl(hsotg->regs + GINTSTS) & GINTSTS_CURMODE_HOST) == 0;
+ return (dwc2_readl(hsotg, GINTSTS) & GINTSTS_CURMODE_HOST) == 0;
}
/*
@@ -1238,6 +1381,7 @@ int dwc2_hsotg_tx_fifo_count(struct dwc2_hsotg *hsotg);
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);
#else
static inline int dwc2_hsotg_remove(struct dwc2_hsotg *dwc2)
{ return 0; }
@@ -1272,6 +1416,7 @@ static inline int dwc2_hsotg_tx_fifo_total_depth(struct dwc2_hsotg *hsotg)
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) {}
#endif
#if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c
index 2982a155734d..19ae2595f1c3 100644
--- a/drivers/usb/dwc2/core_intr.c
+++ b/drivers/usb/dwc2/core_intr.c
@@ -81,11 +81,11 @@ static const char *dwc2_op_state_str(struct dwc2_hsotg *hsotg)
*/
static void dwc2_handle_usb_port_intr(struct dwc2_hsotg *hsotg)
{
- u32 hprt0 = dwc2_readl(hsotg->regs + HPRT0);
+ u32 hprt0 = dwc2_readl(hsotg, HPRT0);
if (hprt0 & HPRT0_ENACHG) {
hprt0 &= ~HPRT0_ENA;
- dwc2_writel(hprt0, hsotg->regs + HPRT0);
+ dwc2_writel(hsotg, hprt0, HPRT0);
}
}
@@ -97,7 +97,7 @@ static void dwc2_handle_usb_port_intr(struct dwc2_hsotg *hsotg)
static void dwc2_handle_mode_mismatch_intr(struct dwc2_hsotg *hsotg)
{
/* Clear interrupt */
- dwc2_writel(GINTSTS_MODEMIS, hsotg->regs + GINTSTS);
+ dwc2_writel(hsotg, GINTSTS_MODEMIS, GINTSTS);
dev_warn(hsotg->dev, "Mode Mismatch Interrupt: currently in %s mode\n",
dwc2_is_host_mode(hsotg) ? "Host" : "Device");
@@ -115,8 +115,8 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
u32 gotgctl;
u32 gintmsk;
- gotgint = dwc2_readl(hsotg->regs + GOTGINT);
- gotgctl = dwc2_readl(hsotg->regs + GOTGCTL);
+ gotgint = dwc2_readl(hsotg, GOTGINT);
+ gotgctl = dwc2_readl(hsotg, GOTGCTL);
dev_dbg(hsotg->dev, "++OTG Interrupt gotgint=%0x [%s]\n", gotgint,
dwc2_op_state_str(hsotg));
@@ -124,7 +124,7 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
dev_dbg(hsotg->dev,
" ++OTG Interrupt: Session End Detected++ (%s)\n",
dwc2_op_state_str(hsotg));
- gotgctl = dwc2_readl(hsotg->regs + GOTGCTL);
+ gotgctl = dwc2_readl(hsotg, GOTGCTL);
if (dwc2_is_device_mode(hsotg))
dwc2_hsotg_disconnect(hsotg);
@@ -150,24 +150,24 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
hsotg->lx_state = DWC2_L0;
}
- gotgctl = dwc2_readl(hsotg->regs + GOTGCTL);
+ gotgctl = dwc2_readl(hsotg, GOTGCTL);
gotgctl &= ~GOTGCTL_DEVHNPEN;
- dwc2_writel(gotgctl, hsotg->regs + GOTGCTL);
+ dwc2_writel(hsotg, gotgctl, GOTGCTL);
}
if (gotgint & GOTGINT_SES_REQ_SUC_STS_CHNG) {
dev_dbg(hsotg->dev,
" ++OTG Interrupt: Session Request Success Status Change++\n");
- gotgctl = dwc2_readl(hsotg->regs + GOTGCTL);
+ gotgctl = dwc2_readl(hsotg, GOTGCTL);
if (gotgctl & GOTGCTL_SESREQSCS) {
if (hsotg->params.phy_type == DWC2_PHY_TYPE_PARAM_FS &&
hsotg->params.i2c_enable) {
hsotg->srp_success = 1;
} else {
/* Clear Session Request */
- gotgctl = dwc2_readl(hsotg->regs + GOTGCTL);
+ gotgctl = dwc2_readl(hsotg, GOTGCTL);
gotgctl &= ~GOTGCTL_SESREQ;
- dwc2_writel(gotgctl, hsotg->regs + GOTGCTL);
+ dwc2_writel(hsotg, gotgctl, GOTGCTL);
}
}
}
@@ -177,7 +177,7 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
* Print statements during the HNP interrupt handling
* can cause it to fail
*/
- gotgctl = dwc2_readl(hsotg->regs + GOTGCTL);
+ gotgctl = dwc2_readl(hsotg, GOTGCTL);
/*
* WA for 3.00a- HW is not setting cur_mode, even sometimes
* this does not help
@@ -197,9 +197,9 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
* interrupt does not get handled and Linux
* complains loudly.
*/
- gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
+ gintmsk = dwc2_readl(hsotg, GINTMSK);
gintmsk &= ~GINTSTS_SOF;
- dwc2_writel(gintmsk, hsotg->regs + GINTMSK);
+ dwc2_writel(hsotg, gintmsk, GINTMSK);
/*
* Call callback function with spin lock
@@ -213,9 +213,9 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
hsotg->op_state = OTG_STATE_B_HOST;
}
} else {
- gotgctl = dwc2_readl(hsotg->regs + GOTGCTL);
+ gotgctl = dwc2_readl(hsotg, GOTGCTL);
gotgctl &= ~(GOTGCTL_HNPREQ | GOTGCTL_DEVHNPEN);
- dwc2_writel(gotgctl, hsotg->regs + GOTGCTL);
+ dwc2_writel(hsotg, gotgctl, GOTGCTL);
dev_dbg(hsotg->dev, "HNP Failed\n");
dev_err(hsotg->dev,
"Device Not Connected/Responding\n");
@@ -241,9 +241,9 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
hsotg->op_state = OTG_STATE_A_PERIPHERAL;
} else {
/* Need to disable SOF interrupt immediately */
- gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
+ gintmsk = dwc2_readl(hsotg, GINTMSK);
gintmsk &= ~GINTSTS_SOF;
- dwc2_writel(gintmsk, hsotg->regs + GINTMSK);
+ dwc2_writel(hsotg, gintmsk, GINTMSK);
spin_unlock(&hsotg->lock);
dwc2_hcd_start(hsotg);
spin_lock(&hsotg->lock);
@@ -258,7 +258,7 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
dev_dbg(hsotg->dev, " ++OTG Interrupt: Debounce Done++\n");
/* Clear GOTGINT */
- dwc2_writel(gotgint, hsotg->regs + GOTGINT);
+ dwc2_writel(hsotg, gotgint, GOTGINT);
}
/**
@@ -276,12 +276,12 @@ static void dwc2_handle_conn_id_status_change_intr(struct dwc2_hsotg *hsotg)
u32 gintmsk;
/* Clear interrupt */
- dwc2_writel(GINTSTS_CONIDSTSCHNG, hsotg->regs + GINTSTS);
+ dwc2_writel(hsotg, GINTSTS_CONIDSTSCHNG, GINTSTS);
/* Need to disable SOF interrupt immediately */
- gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
+ gintmsk = dwc2_readl(hsotg, GINTMSK);
gintmsk &= ~GINTSTS_SOF;
- dwc2_writel(gintmsk, hsotg->regs + GINTMSK);
+ dwc2_writel(hsotg, gintmsk, GINTMSK);
dev_dbg(hsotg->dev, " ++Connector ID Status Change Interrupt++ (%s)\n",
dwc2_is_host_mode(hsotg) ? "Host" : "Device");
@@ -314,7 +314,7 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg)
int ret;
/* Clear interrupt */
- dwc2_writel(GINTSTS_SESSREQINT, hsotg->regs + GINTSTS);
+ dwc2_writel(hsotg, GINTSTS_SESSREQINT, GINTSTS);
dev_dbg(hsotg->dev, "Session request interrupt - lx_state=%d\n",
hsotg->lx_state);
@@ -351,15 +351,15 @@ static void dwc2_wakeup_from_lpm_l1(struct dwc2_hsotg *hsotg)
return;
}
- glpmcfg = dwc2_readl(hsotg->regs + GLPMCFG);
+ glpmcfg = dwc2_readl(hsotg, GLPMCFG);
if (dwc2_is_device_mode(hsotg)) {
dev_dbg(hsotg->dev, "Exit from L1 state\n");
glpmcfg &= ~GLPMCFG_ENBLSLPM;
glpmcfg &= ~GLPMCFG_HIRD_THRES_EN;
- dwc2_writel(glpmcfg, hsotg->regs + GLPMCFG);
+ dwc2_writel(hsotg, glpmcfg, GLPMCFG);
do {
- glpmcfg = dwc2_readl(hsotg->regs + GLPMCFG);
+ glpmcfg = dwc2_readl(hsotg, GLPMCFG);
if (!(glpmcfg & (GLPMCFG_COREL1RES_MASK |
GLPMCFG_L1RESUMEOK | GLPMCFG_SLPSTS)))
@@ -398,7 +398,7 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
int ret;
/* Clear interrupt */
- dwc2_writel(GINTSTS_WKUPINT, hsotg->regs + GINTSTS);
+ dwc2_writel(hsotg, GINTSTS_WKUPINT, GINTSTS);
dev_dbg(hsotg->dev, "++Resume or Remote Wakeup Detected Interrupt++\n");
dev_dbg(hsotg->dev, "%s lxstate = %d\n", __func__, hsotg->lx_state);
@@ -410,13 +410,13 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
if (dwc2_is_device_mode(hsotg)) {
dev_dbg(hsotg->dev, "DSTS=0x%0x\n",
- dwc2_readl(hsotg->regs + DSTS));
+ dwc2_readl(hsotg, DSTS));
if (hsotg->lx_state == DWC2_L2) {
- u32 dctl = dwc2_readl(hsotg->regs + DCTL);
+ u32 dctl = dwc2_readl(hsotg, DCTL);
/* Clear Remote Wakeup Signaling */
dctl &= ~DCTL_RMTWKUPSIG;
- dwc2_writel(dctl, hsotg->regs + DCTL);
+ dwc2_writel(hsotg, dctl, DCTL);
ret = dwc2_exit_partial_power_down(hsotg, true);
if (ret && (ret != -ENOTSUPP))
dev_err(hsotg->dev, "exit power_down failed\n");
@@ -430,11 +430,11 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
return;
if (hsotg->lx_state != DWC2_L1) {
- u32 pcgcctl = dwc2_readl(hsotg->regs + PCGCTL);
+ u32 pcgcctl = dwc2_readl(hsotg, PCGCTL);
/* Restart the Phy Clock */
pcgcctl &= ~PCGCTL_STOPPCLK;
- dwc2_writel(pcgcctl, hsotg->regs + PCGCTL);
+ dwc2_writel(hsotg, pcgcctl, PCGCTL);
mod_timer(&hsotg->wkp_timer,
jiffies + msecs_to_jiffies(71));
} else {
@@ -450,7 +450,7 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
*/
static void dwc2_handle_disconnect_intr(struct dwc2_hsotg *hsotg)
{
- dwc2_writel(GINTSTS_DISCONNINT, hsotg->regs + GINTSTS);
+ dwc2_writel(hsotg, GINTSTS_DISCONNINT, GINTSTS);
dev_dbg(hsotg->dev, "++Disconnect Detected Interrupt++ (%s) %s\n",
dwc2_is_host_mode(hsotg) ? "Host" : "Device",
@@ -474,7 +474,7 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg)
int ret;
/* Clear interrupt */
- dwc2_writel(GINTSTS_USBSUSP, hsotg->regs + GINTSTS);
+ dwc2_writel(hsotg, GINTSTS_USBSUSP, GINTSTS);
dev_dbg(hsotg->dev, "USB SUSPEND\n");
@@ -483,7 +483,7 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg)
* Check the Device status register to determine if the Suspend
* state is active
*/
- dsts = dwc2_readl(hsotg->regs + DSTS);
+ dsts = dwc2_readl(hsotg, DSTS);
dev_dbg(hsotg->dev, "%s: DSTS=0x%0x\n", __func__, dsts);
dev_dbg(hsotg->dev,
"DSTS.Suspend Status=%d HWCFG4.Power Optimize=%d HWCFG4.Hibernation=%d\n",
@@ -563,9 +563,9 @@ static void dwc2_handle_lpm_intr(struct dwc2_hsotg *hsotg)
u32 enslpm;
/* Clear interrupt */
- dwc2_writel(GINTSTS_LPMTRANRCVD, hsotg->regs + GINTSTS);
+ dwc2_writel(hsotg, GINTSTS_LPMTRANRCVD, GINTSTS);
- glpmcfg = dwc2_readl(hsotg->regs + GLPMCFG);
+ glpmcfg = dwc2_readl(hsotg, GLPMCFG);
if (!(glpmcfg & GLPMCFG_LPMCAP)) {
dev_err(hsotg->dev, "Unexpected LPM interrupt\n");
@@ -588,16 +588,16 @@ static void dwc2_handle_lpm_intr(struct dwc2_hsotg *hsotg)
} else {
dev_dbg(hsotg->dev, "Entering Sleep with L1 Gating\n");
- pcgcctl = dwc2_readl(hsotg->regs + PCGCTL);
+ pcgcctl = dwc2_readl(hsotg, PCGCTL);
pcgcctl |= PCGCTL_ENBL_SLEEP_GATING;
- dwc2_writel(pcgcctl, hsotg->regs + PCGCTL);
+ dwc2_writel(hsotg, pcgcctl, PCGCTL);
}
/**
* Examine prt_sleep_sts after TL1TokenTetry period max (10 us)
*/
udelay(10);
- glpmcfg = dwc2_readl(hsotg->regs + GLPMCFG);
+ glpmcfg = dwc2_readl(hsotg, GLPMCFG);
if (glpmcfg & GLPMCFG_SLPSTS) {
/* Save the current state */
@@ -627,9 +627,9 @@ static u32 dwc2_read_common_intr(struct dwc2_hsotg *hsotg)
u32 gahbcfg;
u32 gintmsk_common = GINTMSK_COMMON;
- gintsts = dwc2_readl(hsotg->regs + GINTSTS);
- gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
- gahbcfg = dwc2_readl(hsotg->regs + GAHBCFG);
+ gintsts = dwc2_readl(hsotg, GINTSTS);
+ gintmsk = dwc2_readl(hsotg, GINTMSK);
+ gahbcfg = dwc2_readl(hsotg, GAHBCFG);
/* If any common interrupts set */
if (gintsts & gintmsk_common)
@@ -653,9 +653,9 @@ static void dwc2_handle_gpwrdn_intr(struct dwc2_hsotg *hsotg)
u32 gpwrdn;
int linestate;
- gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn = dwc2_readl(hsotg, GPWRDN);
/* clear all interrupt */
- dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ dwc2_writel(hsotg, gpwrdn, GPWRDN);
linestate = (gpwrdn & GPWRDN_LINESTATE_MASK) >> GPWRDN_LINESTATE_SHIFT;
dev_dbg(hsotg->dev,
"%s: dwc2_handle_gpwrdwn_intr called gpwrdn= %08x\n", __func__,
@@ -668,38 +668,38 @@ static void dwc2_handle_gpwrdn_intr(struct dwc2_hsotg *hsotg)
dev_dbg(hsotg->dev, "%s: GPWRDN_DISCONN_DET\n", __func__);
/* Switch-on voltage to the core */
- gpwrdn_tmp = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN);
gpwrdn_tmp &= ~GPWRDN_PWRDNSWTCH;
- dwc2_writel(gpwrdn_tmp, hsotg->regs + GPWRDN);
+ dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN);
udelay(10);
/* Reset core */
- gpwrdn_tmp = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN);
gpwrdn_tmp &= ~GPWRDN_PWRDNRSTN;
- dwc2_writel(gpwrdn_tmp, hsotg->regs + GPWRDN);
+ dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN);
udelay(10);
/* Disable Power Down Clamp */
- gpwrdn_tmp = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN);
gpwrdn_tmp &= ~GPWRDN_PWRDNCLMP;
- dwc2_writel(gpwrdn_tmp, hsotg->regs + GPWRDN);
+ dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN);
udelay(10);
/* Deassert reset core */
- gpwrdn_tmp = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN);
gpwrdn_tmp |= GPWRDN_PWRDNRSTN;
- dwc2_writel(gpwrdn_tmp, hsotg->regs + GPWRDN);
+ dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN);
udelay(10);
/* Disable PMU interrupt */
- gpwrdn_tmp = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN);
gpwrdn_tmp &= ~GPWRDN_PMUINTSEL;
- dwc2_writel(gpwrdn_tmp, hsotg->regs + GPWRDN);
+ dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN);
/* De-assert Wakeup Logic */
- gpwrdn_tmp = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN);
gpwrdn_tmp &= ~GPWRDN_PMUACTV;
- dwc2_writel(gpwrdn_tmp, hsotg->regs + GPWRDN);
+ dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN);
hsotg->hibernated = 0;
@@ -778,6 +778,14 @@ irqreturn_t dwc2_handle_common_intr(int irq, void *dev)
goto out;
}
+ /* Reading current frame number value in device or host modes. */
+ if (dwc2_is_device_mode(hsotg))
+ hsotg->frame_number = (dwc2_readl(hsotg, DSTS)
+ & DSTS_SOFFN_MASK) >> DSTS_SOFFN_SHIFT;
+ else
+ hsotg->frame_number = (dwc2_readl(hsotg, HFNUM)
+ & HFNUM_FRNUM_MASK) >> HFNUM_FRNUM_SHIFT;
+
gintsts = dwc2_read_common_intr(hsotg);
if (gintsts & ~GINTSTS_PRTINT)
retval = IRQ_HANDLED;
diff --git a/drivers/usb/dwc2/debug.h b/drivers/usb/dwc2/debug.h
index 6f23219c13cb..a8c565b6bc34 100644
--- a/drivers/usb/dwc2/debug.h
+++ b/drivers/usb/dwc2/debug.h
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
-/**
+/*
* debug.h - Designware USB2 DRD controller debug header
*
* Copyright (C) 2015 Intel Corporation
diff --git a/drivers/usb/dwc2/debugfs.c b/drivers/usb/dwc2/debugfs.c
index 58c691f882a8..7f62f4cdc265 100644
--- a/drivers/usb/dwc2/debugfs.c
+++ b/drivers/usb/dwc2/debugfs.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
-/**
+/*
* debugfs.c - Designware USB2 DRD controller debugfs
*
* Copyright (C) 2015 Intel Corporation
@@ -16,12 +16,13 @@
#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || \
IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
+
/**
- * testmode_write - debugfs: change usb test mode
- * @seq: The seq file to write to.
- * @v: Unused parameter.
- *
- * This debugfs entry modify the current usb test mode.
+ * testmode_write() - change usb test mode state.
+ * @file: The file to write to.
+ * @ubuf: The buffer where user wrote.
+ * @count: The ubuf size.
+ * @ppos: Unused parameter.
*/
static ssize_t testmode_write(struct file *file, const char __user *ubuf, size_t
count, loff_t *ppos)
@@ -55,9 +56,9 @@ static ssize_t testmode_write(struct file *file, const char __user *ubuf, size_t
}
/**
- * testmode_show - debugfs: show usb test mode state
- * @seq: The seq file to write to.
- * @v: Unused parameter.
+ * testmode_show() - debugfs: show usb test mode state
+ * @s: The seq file to write to.
+ * @unused: Unused parameter.
*
* This debugfs entry shows which usb test mode is currently enabled.
*/
@@ -68,7 +69,7 @@ static int testmode_show(struct seq_file *s, void *unused)
int dctl;
spin_lock_irqsave(&hsotg->lock, flags);
- dctl = dwc2_readl(hsotg->regs + DCTL);
+ dctl = dwc2_readl(hsotg, DCTL);
dctl &= DCTL_TSTCTL_MASK;
dctl >>= DCTL_TSTCTL_SHIFT;
spin_unlock_irqrestore(&hsotg->lock, flags);
@@ -125,42 +126,41 @@ static const struct file_operations testmode_fops = {
static int state_show(struct seq_file *seq, void *v)
{
struct dwc2_hsotg *hsotg = seq->private;
- void __iomem *regs = hsotg->regs;
int idx;
seq_printf(seq, "DCFG=0x%08x, DCTL=0x%08x, DSTS=0x%08x\n",
- dwc2_readl(regs + DCFG),
- dwc2_readl(regs + DCTL),
- dwc2_readl(regs + DSTS));
+ dwc2_readl(hsotg, DCFG),
+ dwc2_readl(hsotg, DCTL),
+ dwc2_readl(hsotg, DSTS));
seq_printf(seq, "DIEPMSK=0x%08x, DOEPMASK=0x%08x\n",
- dwc2_readl(regs + DIEPMSK), dwc2_readl(regs + DOEPMSK));
+ dwc2_readl(hsotg, DIEPMSK), dwc2_readl(hsotg, DOEPMSK));
seq_printf(seq, "GINTMSK=0x%08x, GINTSTS=0x%08x\n",
- dwc2_readl(regs + GINTMSK),
- dwc2_readl(regs + GINTSTS));
+ dwc2_readl(hsotg, GINTMSK),
+ dwc2_readl(hsotg, GINTSTS));
seq_printf(seq, "DAINTMSK=0x%08x, DAINT=0x%08x\n",
- dwc2_readl(regs + DAINTMSK),
- dwc2_readl(regs + DAINT));
+ dwc2_readl(hsotg, DAINTMSK),
+ dwc2_readl(hsotg, DAINT));
seq_printf(seq, "GNPTXSTS=0x%08x, GRXSTSR=%08x\n",
- dwc2_readl(regs + GNPTXSTS),
- dwc2_readl(regs + GRXSTSR));
+ dwc2_readl(hsotg, GNPTXSTS),
+ dwc2_readl(hsotg, GRXSTSR));
seq_puts(seq, "\nEndpoint status:\n");
for (idx = 0; idx < hsotg->num_of_eps; idx++) {
u32 in, out;
- in = dwc2_readl(regs + DIEPCTL(idx));
- out = dwc2_readl(regs + DOEPCTL(idx));
+ in = dwc2_readl(hsotg, DIEPCTL(idx));
+ out = dwc2_readl(hsotg, DOEPCTL(idx));
seq_printf(seq, "ep%d: DIEPCTL=0x%08x, DOEPCTL=0x%08x",
idx, in, out);
- in = dwc2_readl(regs + DIEPTSIZ(idx));
- out = dwc2_readl(regs + DOEPTSIZ(idx));
+ in = dwc2_readl(hsotg, DIEPTSIZ(idx));
+ out = dwc2_readl(hsotg, DOEPTSIZ(idx));
seq_printf(seq, ", DIEPTSIZ=0x%08x, DOEPTSIZ=0x%08x",
in, out);
@@ -183,14 +183,13 @@ DEFINE_SHOW_ATTRIBUTE(state);
static int fifo_show(struct seq_file *seq, void *v)
{
struct dwc2_hsotg *hsotg = seq->private;
- void __iomem *regs = hsotg->regs;
u32 val;
int idx;
seq_puts(seq, "Non-periodic FIFOs:\n");
- seq_printf(seq, "RXFIFO: Size %d\n", dwc2_readl(regs + GRXFSIZ));
+ seq_printf(seq, "RXFIFO: Size %d\n", dwc2_readl(hsotg, GRXFSIZ));
- val = dwc2_readl(regs + GNPTXFSIZ);
+ val = dwc2_readl(hsotg, GNPTXFSIZ);
seq_printf(seq, "NPTXFIFO: Size %d, Start 0x%08x\n",
val >> FIFOSIZE_DEPTH_SHIFT,
val & FIFOSIZE_STARTADDR_MASK);
@@ -198,7 +197,7 @@ static int fifo_show(struct seq_file *seq, void *v)
seq_puts(seq, "\nPeriodic TXFIFOs:\n");
for (idx = 1; idx < hsotg->num_of_eps; idx++) {
- val = dwc2_readl(regs + DPTXFSIZN(idx));
+ val = dwc2_readl(hsotg, DPTXFSIZN(idx));
seq_printf(seq, "\tDPTXFIFO%2d: Size %d, Start 0x%08x\n", idx,
val >> FIFOSIZE_DEPTH_SHIFT,
@@ -227,7 +226,6 @@ static int ep_show(struct seq_file *seq, void *v)
struct dwc2_hsotg_ep *ep = seq->private;
struct dwc2_hsotg *hsotg = ep->parent;
struct dwc2_hsotg_req *req;
- void __iomem *regs = hsotg->regs;
int index = ep->index;
int show_limit = 15;
unsigned long flags;
@@ -238,20 +236,20 @@ static int ep_show(struct seq_file *seq, void *v)
/* first show the register state */
seq_printf(seq, "\tDIEPCTL=0x%08x, DOEPCTL=0x%08x\n",
- dwc2_readl(regs + DIEPCTL(index)),
- dwc2_readl(regs + DOEPCTL(index)));
+ dwc2_readl(hsotg, DIEPCTL(index)),
+ dwc2_readl(hsotg, DOEPCTL(index)));
seq_printf(seq, "\tDIEPDMA=0x%08x, DOEPDMA=0x%08x\n",
- dwc2_readl(regs + DIEPDMA(index)),
- dwc2_readl(regs + DOEPDMA(index)));
+ dwc2_readl(hsotg, DIEPDMA(index)),
+ dwc2_readl(hsotg, DOEPDMA(index)));
seq_printf(seq, "\tDIEPINT=0x%08x, DOEPINT=0x%08x\n",
- dwc2_readl(regs + DIEPINT(index)),
- dwc2_readl(regs + DOEPINT(index)));
+ dwc2_readl(hsotg, DIEPINT(index)),
+ dwc2_readl(hsotg, DOEPINT(index)));
seq_printf(seq, "\tDIEPTSIZ=0x%08x, DOEPTSIZ=0x%08x\n",
- dwc2_readl(regs + DIEPTSIZ(index)),
- dwc2_readl(regs + DOEPTSIZ(index)));
+ dwc2_readl(hsotg, DIEPTSIZ(index)),
+ dwc2_readl(hsotg, DOEPTSIZ(index)));
seq_puts(seq, "\n");
seq_printf(seq, "mps %d\n", ep->ep.maxpacket);
@@ -293,52 +291,30 @@ DEFINE_SHOW_ATTRIBUTE(ep);
static void dwc2_hsotg_create_debug(struct dwc2_hsotg *hsotg)
{
struct dentry *root;
- struct dentry *file;
unsigned int epidx;
root = hsotg->debug_root;
/* create general state file */
-
- file = debugfs_create_file("state", 0444, root, hsotg, &state_fops);
- if (IS_ERR(file))
- dev_err(hsotg->dev, "%s: failed to create state\n", __func__);
-
- file = debugfs_create_file("testmode", 0644, root, hsotg,
- &testmode_fops);
- if (IS_ERR(file))
- dev_err(hsotg->dev, "%s: failed to create testmode\n",
- __func__);
-
- file = debugfs_create_file("fifo", 0444, root, hsotg, &fifo_fops);
- if (IS_ERR(file))
- dev_err(hsotg->dev, "%s: failed to create fifo\n", __func__);
+ debugfs_create_file("state", 0444, root, hsotg, &state_fops);
+ debugfs_create_file("testmode", 0644, root, hsotg, &testmode_fops);
+ debugfs_create_file("fifo", 0444, root, hsotg, &fifo_fops);
/* Create one file for each out endpoint */
for (epidx = 0; epidx < hsotg->num_of_eps; epidx++) {
struct dwc2_hsotg_ep *ep;
ep = hsotg->eps_out[epidx];
- if (ep) {
- file = debugfs_create_file(ep->name, 0444,
- root, ep, &ep_fops);
- if (IS_ERR(file))
- dev_err(hsotg->dev, "failed to create %s debug file\n",
- ep->name);
- }
+ if (ep)
+ debugfs_create_file(ep->name, 0444, root, ep, &ep_fops);
}
/* Create one file for each in endpoint. EP0 is handled with out eps */
for (epidx = 1; epidx < hsotg->num_of_eps; epidx++) {
struct dwc2_hsotg_ep *ep;
ep = hsotg->eps_in[epidx];
- if (ep) {
- file = debugfs_create_file(ep->name, 0444,
- root, ep, &ep_fops);
- if (IS_ERR(file))
- dev_err(hsotg->dev, "failed to create %s debug file\n",
- ep->name);
- }
+ if (ep)
+ debugfs_create_file(ep->name, 0444, root, ep, &ep_fops);
}
}
#else
@@ -368,7 +344,7 @@ static const struct debugfs_reg32 dwc2_regs[] = {
dump_register(GINTSTS),
dump_register(GINTMSK),
dump_register(GRXSTSR),
- dump_register(GRXSTSP),
+ /* Omit GRXSTSP */
dump_register(GRXFSIZ),
dump_register(GNPTXFSIZ),
dump_register(GNPTXSTS),
@@ -710,6 +686,7 @@ static int params_show(struct seq_file *seq, void *v)
print_param(seq, p, phy_ulpi_ddr);
print_param(seq, p, phy_ulpi_ext_vbus);
print_param(seq, p, i2c_enable);
+ print_param(seq, p, ipg_isoc_en);
print_param(seq, p, ulpi_fs_ls);
print_param(seq, p, host_support_fs_ls_low_power);
print_param(seq, p, host_ls_low_power_phy_clk);
@@ -724,6 +701,7 @@ static int params_show(struct seq_file *seq, void *v)
print_param(seq, p, besl);
print_param(seq, p, hird_threshold_en);
print_param(seq, p, hird_threshold);
+ print_param(seq, p, service_interval);
print_param(seq, p, host_dma);
print_param(seq, p, g_dma);
print_param(seq, p, g_dma_desc);
@@ -790,32 +768,14 @@ DEFINE_SHOW_ATTRIBUTE(dr_mode);
int dwc2_debugfs_init(struct dwc2_hsotg *hsotg)
{
int ret;
- struct dentry *file;
+ struct dentry *root;
- hsotg->debug_root = debugfs_create_dir(dev_name(hsotg->dev), NULL);
- if (!hsotg->debug_root) {
- ret = -ENOMEM;
- goto err0;
- }
+ root = debugfs_create_dir(dev_name(hsotg->dev), NULL);
+ hsotg->debug_root = root;
- file = debugfs_create_file("params", 0444,
- hsotg->debug_root,
- hsotg, &params_fops);
- if (IS_ERR(file))
- dev_err(hsotg->dev, "%s: failed to create params\n", __func__);
-
- file = debugfs_create_file("hw_params", 0444,
- hsotg->debug_root,
- hsotg, &hw_params_fops);
- if (IS_ERR(file))
- dev_err(hsotg->dev, "%s: failed to create hw_params\n",
- __func__);
-
- file = debugfs_create_file("dr_mode", 0444,
- hsotg->debug_root,
- hsotg, &dr_mode_fops);
- if (IS_ERR(file))
- dev_err(hsotg->dev, "%s: failed to create dr_mode\n", __func__);
+ debugfs_create_file("params", 0444, root, hsotg, &params_fops);
+ debugfs_create_file("hw_params", 0444, root, hsotg, &hw_params_fops);
+ debugfs_create_file("dr_mode", 0444, root, hsotg, &dr_mode_fops);
/* Add gadget debugfs nodes */
dwc2_hsotg_create_debug(hsotg);
@@ -824,24 +784,18 @@ int dwc2_debugfs_init(struct dwc2_hsotg *hsotg)
GFP_KERNEL);
if (!hsotg->regset) {
ret = -ENOMEM;
- goto err1;
+ goto err;
}
hsotg->regset->regs = dwc2_regs;
hsotg->regset->nregs = ARRAY_SIZE(dwc2_regs);
hsotg->regset->base = hsotg->regs;
- file = debugfs_create_regset32("regdump", 0444, hsotg->debug_root,
- hsotg->regset);
- if (!file) {
- ret = -ENOMEM;
- goto err1;
- }
+ debugfs_create_regset32("regdump", 0444, root, hsotg->regset);
return 0;
-err1:
+err:
debugfs_remove_recursive(hsotg->debug_root);
-err0:
return ret;
}
diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c
index 83cb5577a52f..2d6d2c8244de 100644
--- a/drivers/usb/dwc2/gadget.c
+++ b/drivers/usb/dwc2/gadget.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
-/**
+/*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
@@ -47,14 +47,14 @@ static inline struct dwc2_hsotg *to_hsotg(struct usb_gadget *gadget)
return container_of(gadget, struct dwc2_hsotg, gadget);
}
-static inline void dwc2_set_bit(void __iomem *ptr, u32 val)
+static inline void dwc2_set_bit(struct dwc2_hsotg *hsotg, u32 offset, u32 val)
{
- dwc2_writel(dwc2_readl(ptr) | val, ptr);
+ dwc2_writel(hsotg, dwc2_readl(hsotg, offset) | val, offset);
}
-static inline void dwc2_clear_bit(void __iomem *ptr, u32 val)
+static inline void dwc2_clear_bit(struct dwc2_hsotg *hsotg, u32 offset, u32 val)
{
- dwc2_writel(dwc2_readl(ptr) & ~val, ptr);
+ dwc2_writel(hsotg, dwc2_readl(hsotg, offset) & ~val, offset);
}
static inline struct dwc2_hsotg_ep *index_to_ep(struct dwc2_hsotg *hsotg,
@@ -107,7 +107,6 @@ static inline bool using_desc_dma(struct dwc2_hsotg *hsotg)
/**
* dwc2_gadget_incr_frame_num - Increments the targeted frame number.
* @hs_ep: The endpoint
- * @increment: The value to increment by
*
* This function will also check if the frame number overruns DSTS_SOFFN_LIMIT.
* If an overrun occurs it will wrap the value and set the frame_overrun flag.
@@ -124,20 +123,38 @@ static inline void dwc2_gadget_incr_frame_num(struct dwc2_hsotg_ep *hs_ep)
}
/**
+ * dwc2_gadget_dec_frame_num_by_one - Decrements the targeted frame number
+ * by one.
+ * @hs_ep: The endpoint.
+ *
+ * This function used in service interval based scheduling flow to calculate
+ * descriptor frame number filed value. For service interval mode frame
+ * number in descriptor should point to last (u)frame in the interval.
+ *
+ */
+static inline void dwc2_gadget_dec_frame_num_by_one(struct dwc2_hsotg_ep *hs_ep)
+{
+ if (hs_ep->target_frame)
+ hs_ep->target_frame -= 1;
+ else
+ hs_ep->target_frame = DSTS_SOFFN_LIMIT;
+}
+
+/**
* dwc2_hsotg_en_gsint - enable one or more of the general interrupt
* @hsotg: The device state
* @ints: A bitmask of the interrupts to enable
*/
static void dwc2_hsotg_en_gsint(struct dwc2_hsotg *hsotg, u32 ints)
{
- u32 gsintmsk = dwc2_readl(hsotg->regs + GINTMSK);
+ u32 gsintmsk = dwc2_readl(hsotg, GINTMSK);
u32 new_gsintmsk;
new_gsintmsk = gsintmsk | ints;
if (new_gsintmsk != gsintmsk) {
dev_dbg(hsotg->dev, "gsintmsk now 0x%08x\n", new_gsintmsk);
- dwc2_writel(new_gsintmsk, hsotg->regs + GINTMSK);
+ dwc2_writel(hsotg, new_gsintmsk, GINTMSK);
}
}
@@ -148,13 +165,13 @@ static void dwc2_hsotg_en_gsint(struct dwc2_hsotg *hsotg, u32 ints)
*/
static void dwc2_hsotg_disable_gsint(struct dwc2_hsotg *hsotg, u32 ints)
{
- u32 gsintmsk = dwc2_readl(hsotg->regs + GINTMSK);
+ u32 gsintmsk = dwc2_readl(hsotg, GINTMSK);
u32 new_gsintmsk;
new_gsintmsk = gsintmsk & ~ints;
if (new_gsintmsk != gsintmsk)
- dwc2_writel(new_gsintmsk, hsotg->regs + GINTMSK);
+ dwc2_writel(hsotg, new_gsintmsk, GINTMSK);
}
/**
@@ -179,17 +196,19 @@ static void dwc2_hsotg_ctrl_epint(struct dwc2_hsotg *hsotg,
bit <<= 16;
local_irq_save(flags);
- daint = dwc2_readl(hsotg->regs + DAINTMSK);
+ daint = dwc2_readl(hsotg, DAINTMSK);
if (en)
daint |= bit;
else
daint &= ~bit;
- dwc2_writel(daint, hsotg->regs + DAINTMSK);
+ dwc2_writel(hsotg, daint, DAINTMSK);
local_irq_restore(flags);
}
/**
* dwc2_hsotg_tx_fifo_count - return count of TX FIFOs in device mode
+ *
+ * @hsotg: Programming view of the DWC_otg controller
*/
int dwc2_hsotg_tx_fifo_count(struct dwc2_hsotg *hsotg)
{
@@ -204,6 +223,8 @@ int dwc2_hsotg_tx_fifo_count(struct dwc2_hsotg *hsotg)
/**
* dwc2_hsotg_tx_fifo_total_depth - return total FIFO depth available for
* device mode TX FIFOs
+ *
+ * @hsotg: Programming view of the DWC_otg controller
*/
int dwc2_hsotg_tx_fifo_total_depth(struct dwc2_hsotg *hsotg)
{
@@ -225,8 +246,31 @@ int dwc2_hsotg_tx_fifo_total_depth(struct dwc2_hsotg *hsotg)
}
/**
+ * dwc2_gadget_wkup_alert_handler - Handler for WKUP_ALERT interrupt
+ *
+ * @hsotg: Programming view of the DWC_otg controller
+ *
+ */
+static void dwc2_gadget_wkup_alert_handler(struct dwc2_hsotg *hsotg)
+{
+ u32 gintsts2;
+ u32 gintmsk2;
+
+ gintsts2 = dwc2_readl(hsotg, GINTSTS2);
+ gintmsk2 = dwc2_readl(hsotg, GINTMSK2);
+
+ if (gintsts2 & GINTSTS2_WKUP_ALERT_INT) {
+ dev_dbg(hsotg->dev, "%s: Wkup_Alert_Int\n", __func__);
+ dwc2_clear_bit(hsotg, GINTSTS2, GINTSTS2_WKUP_ALERT_INT);
+ dwc2_set_bit(hsotg, DCFG, DCTL_RMTWKUPSIG);
+ }
+}
+
+/**
* dwc2_hsotg_tx_fifo_average_depth - returns average depth of device mode
* TX FIFOs
+ *
+ * @hsotg: Programming view of the DWC_otg controller
*/
int dwc2_hsotg_tx_fifo_average_depth(struct dwc2_hsotg *hsotg)
{
@@ -261,10 +305,11 @@ static void dwc2_hsotg_init_fifo(struct dwc2_hsotg *hsotg)
hsotg->fifo_map = 0;
/* set RX/NPTX FIFO sizes */
- dwc2_writel(hsotg->params.g_rx_fifo_size, hsotg->regs + GRXFSIZ);
- dwc2_writel((hsotg->params.g_rx_fifo_size << FIFOSIZE_STARTADDR_SHIFT) |
+ dwc2_writel(hsotg, hsotg->params.g_rx_fifo_size, GRXFSIZ);
+ dwc2_writel(hsotg, (hsotg->params.g_rx_fifo_size <<
+ FIFOSIZE_STARTADDR_SHIFT) |
(hsotg->params.g_np_tx_fifo_size << FIFOSIZE_DEPTH_SHIFT),
- hsotg->regs + GNPTXFSIZ);
+ GNPTXFSIZ);
/*
* arange all the rest of the TX FIFOs, as some versions of this
@@ -290,25 +335,25 @@ static void dwc2_hsotg_init_fifo(struct dwc2_hsotg *hsotg)
"insufficient fifo memory");
addr += txfsz[ep];
- dwc2_writel(val, hsotg->regs + DPTXFSIZN(ep));
- val = dwc2_readl(hsotg->regs + DPTXFSIZN(ep));
+ dwc2_writel(hsotg, val, DPTXFSIZN(ep));
+ val = dwc2_readl(hsotg, DPTXFSIZN(ep));
}
- dwc2_writel(hsotg->hw_params.total_fifo_size |
+ dwc2_writel(hsotg, hsotg->hw_params.total_fifo_size |
addr << GDFIFOCFG_EPINFOBASE_SHIFT,
- hsotg->regs + GDFIFOCFG);
+ GDFIFOCFG);
/*
* according to p428 of the design guide, we need to ensure that
* all fifos are flushed before continuing
*/
- dwc2_writel(GRSTCTL_TXFNUM(0x10) | GRSTCTL_TXFFLSH |
- GRSTCTL_RXFFLSH, hsotg->regs + GRSTCTL);
+ dwc2_writel(hsotg, GRSTCTL_TXFNUM(0x10) | GRSTCTL_TXFFLSH |
+ GRSTCTL_RXFFLSH, GRSTCTL);
/* wait until the fifos are both flushed */
timeout = 100;
while (1) {
- val = dwc2_readl(hsotg->regs + GRSTCTL);
+ val = dwc2_readl(hsotg, GRSTCTL);
if ((val & (GRSTCTL_TXFFLSH | GRSTCTL_RXFFLSH)) == 0)
break;
@@ -327,6 +372,7 @@ static void dwc2_hsotg_init_fifo(struct dwc2_hsotg *hsotg)
}
/**
+ * dwc2_hsotg_ep_alloc_request - allocate USB rerequest structure
* @ep: USB endpoint to allocate request for.
* @flags: Allocation flags
*
@@ -445,7 +491,7 @@ static int dwc2_hsotg_write_fifo(struct dwc2_hsotg *hsotg,
struct dwc2_hsotg_req *hs_req)
{
bool periodic = is_ep_periodic(hs_ep);
- u32 gnptxsts = dwc2_readl(hsotg->regs + GNPTXSTS);
+ u32 gnptxsts = dwc2_readl(hsotg, GNPTXSTS);
int buf_pos = hs_req->req.actual;
int to_write = hs_ep->size_loaded;
void *data;
@@ -460,7 +506,7 @@ static int dwc2_hsotg_write_fifo(struct dwc2_hsotg *hsotg,
return 0;
if (periodic && !hsotg->dedicated_fifos) {
- u32 epsize = dwc2_readl(hsotg->regs + DIEPTSIZ(hs_ep->index));
+ u32 epsize = dwc2_readl(hsotg, DIEPTSIZ(hs_ep->index));
int size_left;
int size_done;
@@ -501,8 +547,8 @@ static int dwc2_hsotg_write_fifo(struct dwc2_hsotg *hsotg,
return -ENOSPC;
}
} else if (hsotg->dedicated_fifos && hs_ep->index != 0) {
- can_write = dwc2_readl(hsotg->regs +
- DTXFSTS(hs_ep->fifo_index));
+ can_write = dwc2_readl(hsotg,
+ DTXFSTS(hs_ep->fifo_index));
can_write &= 0xffff;
can_write *= 4;
@@ -592,7 +638,7 @@ static int dwc2_hsotg_write_fifo(struct dwc2_hsotg *hsotg,
to_write = DIV_ROUND_UP(to_write, 4);
data = hs_req->req.buf + buf_pos;
- iowrite32_rep(hsotg->regs + EPFIFO(hs_ep->index), data, to_write);
+ dwc2_writel_rep(hsotg, EPFIFO(hs_ep->index), data, to_write);
return (to_write >= can_write) ? -ENOSPC : 0;
}
@@ -646,7 +692,7 @@ static u32 dwc2_hsotg_read_frameno(struct dwc2_hsotg *hsotg)
{
u32 dsts;
- dsts = dwc2_readl(hsotg->regs + DSTS);
+ dsts = dwc2_readl(hsotg, DSTS);
dsts &= DSTS_SOFFN_MASK;
dsts >>= DSTS_SOFFN_SHIFT;
@@ -793,9 +839,7 @@ static void dwc2_gadget_config_nonisoc_xfer_ddma(struct dwc2_hsotg_ep *hs_ep,
* @dma_buff: usb requests dma buffer.
* @len: usb request transfer length.
*
- * Finds out index of first free entry either in the bottom or up half of
- * descriptor chain depend on which is under SW control and not processed
- * by HW. Then fills that descriptor with the data of the arrived usb request,
+ * Fills next free descriptor with the data of the arrived usb request,
* frame info, sets Last and IOC bits increments next_desc. If filled
* descriptor is not the first one, removes L bit from the previous descriptor
* status.
@@ -808,36 +852,20 @@ static int dwc2_gadget_fill_isoc_desc(struct dwc2_hsotg_ep *hs_ep,
u32 index;
u32 maxsize = 0;
u32 mask = 0;
+ u8 pid = 0;
maxsize = dwc2_gadget_get_desc_params(hs_ep, &mask);
- if (len > maxsize) {
- dev_err(hsotg->dev, "wrong len %d\n", len);
- return -EINVAL;
- }
-
- /*
- * If SW has already filled half of chain, then return and wait for
- * the other chain to be processed by HW.
- */
- if (hs_ep->next_desc == MAX_DMA_DESC_NUM_GENERIC / 2)
- return -EBUSY;
-
- /* Increment frame number by interval for IN */
- if (hs_ep->dir_in)
- dwc2_gadget_incr_frame_num(hs_ep);
- index = (MAX_DMA_DESC_NUM_GENERIC / 2) * hs_ep->isoc_chain_num +
- hs_ep->next_desc;
+ index = hs_ep->next_desc;
+ desc = &hs_ep->desc_list[index];
- /* Sanity check of calculated index */
- if ((hs_ep->isoc_chain_num && index > MAX_DMA_DESC_NUM_GENERIC) ||
- (!hs_ep->isoc_chain_num && index > MAX_DMA_DESC_NUM_GENERIC / 2)) {
- dev_err(hsotg->dev, "wrong index %d for iso chain\n", index);
- return -EINVAL;
+ /* Check if descriptor chain full */
+ if ((desc->status >> DEV_DMA_BUFF_STS_SHIFT) ==
+ DEV_DMA_BUFF_STS_HREADY) {
+ dev_dbg(hsotg->dev, "%s: desc chain full\n", __func__);
+ return 1;
}
- desc = &hs_ep->desc_list[index];
-
/* Clear L bit of previous desc if more than one entries in the chain */
if (hs_ep->next_desc)
hs_ep->desc_list[index - 1].status &= ~DEV_DMA_L;
@@ -853,7 +881,11 @@ static int dwc2_gadget_fill_isoc_desc(struct dwc2_hsotg_ep *hs_ep,
((len << DEV_DMA_NBYTES_SHIFT) & mask));
if (hs_ep->dir_in) {
- desc->status |= ((hs_ep->mc << DEV_DMA_ISOC_PID_SHIFT) &
+ if (len)
+ pid = DIV_ROUND_UP(len, hs_ep->ep.maxpacket);
+ else
+ pid = 1;
+ desc->status |= ((pid << DEV_DMA_ISOC_PID_SHIFT) &
DEV_DMA_ISOC_PID_MASK) |
((len % hs_ep->ep.maxpacket) ?
DEV_DMA_SHORT : 0) |
@@ -865,8 +897,14 @@ static int dwc2_gadget_fill_isoc_desc(struct dwc2_hsotg_ep *hs_ep,
desc->status &= ~DEV_DMA_BUFF_STS_MASK;
desc->status |= (DEV_DMA_BUFF_STS_HREADY << DEV_DMA_BUFF_STS_SHIFT);
+ /* Increment frame number by interval for IN */
+ if (hs_ep->dir_in)
+ dwc2_gadget_incr_frame_num(hs_ep);
+
/* Update index of last configured entry in the chain */
hs_ep->next_desc++;
+ if (hs_ep->next_desc >= MAX_DMA_DESC_NUM_GENERIC)
+ hs_ep->next_desc = 0;
return 0;
}
@@ -875,11 +913,8 @@ static int dwc2_gadget_fill_isoc_desc(struct dwc2_hsotg_ep *hs_ep,
* dwc2_gadget_start_isoc_ddma - start isochronous transfer in DDMA
* @hs_ep: The isochronous endpoint.
*
- * Prepare first descriptor chain for isochronous endpoints. Afterwards
+ * Prepare descriptor chain for isochronous endpoints. Afterwards
* write DMA address to HW and enable the endpoint.
- *
- * Switch between descriptor chains via isoc_chain_num to give SW opportunity
- * to prepare second descriptor chain while first one is being processed by HW.
*/
static void dwc2_gadget_start_isoc_ddma(struct dwc2_hsotg_ep *hs_ep)
{
@@ -887,37 +922,44 @@ static void dwc2_gadget_start_isoc_ddma(struct dwc2_hsotg_ep *hs_ep)
struct dwc2_hsotg_req *hs_req, *treq;
int index = hs_ep->index;
int ret;
+ int i;
u32 dma_reg;
u32 depctl;
u32 ctrl;
+ struct dwc2_dma_desc *desc;
if (list_empty(&hs_ep->queue)) {
+ hs_ep->target_frame = TARGET_FRAME_INITIAL;
dev_dbg(hsotg->dev, "%s: No requests in queue\n", __func__);
return;
}
+ /* Initialize descriptor chain by Host Busy status */
+ for (i = 0; i < MAX_DMA_DESC_NUM_GENERIC; i++) {
+ desc = &hs_ep->desc_list[i];
+ desc->status = 0;
+ desc->status |= (DEV_DMA_BUFF_STS_HBUSY
+ << DEV_DMA_BUFF_STS_SHIFT);
+ }
+
+ hs_ep->next_desc = 0;
list_for_each_entry_safe(hs_req, treq, &hs_ep->queue, queue) {
ret = dwc2_gadget_fill_isoc_desc(hs_ep, hs_req->req.dma,
hs_req->req.length);
- if (ret) {
- dev_dbg(hsotg->dev, "%s: desc chain full\n", __func__);
+ if (ret)
break;
- }
}
+ hs_ep->compl_desc = 0;
depctl = hs_ep->dir_in ? DIEPCTL(index) : DOEPCTL(index);
dma_reg = hs_ep->dir_in ? DIEPDMA(index) : DOEPDMA(index);
/* write descriptor chain address to control register */
- dwc2_writel(hs_ep->desc_list_dma, hsotg->regs + dma_reg);
+ dwc2_writel(hsotg, hs_ep->desc_list_dma, dma_reg);
- ctrl = dwc2_readl(hsotg->regs + depctl);
+ ctrl = dwc2_readl(hsotg, depctl);
ctrl |= DXEPCTL_EPENA | DXEPCTL_CNAK;
- dwc2_writel(ctrl, hsotg->regs + depctl);
-
- /* Switch ISOC descriptor chain number being processed by SW*/
- hs_ep->isoc_chain_num = (hs_ep->isoc_chain_num ^ 1) & 0x1;
- hs_ep->next_desc = 0;
+ dwc2_writel(hsotg, ctrl, depctl);
}
/**
@@ -965,11 +1007,11 @@ static void dwc2_hsotg_start_req(struct dwc2_hsotg *hsotg,
epsize_reg = dir_in ? DIEPTSIZ(index) : DOEPTSIZ(index);
dev_dbg(hsotg->dev, "%s: DxEPCTL=0x%08x, ep %d, dir %s\n",
- __func__, dwc2_readl(hsotg->regs + epctrl_reg), index,
+ __func__, dwc2_readl(hsotg, epctrl_reg), index,
hs_ep->dir_in ? "in" : "out");
/* If endpoint is stalled, we will restart request later */
- ctrl = dwc2_readl(hsotg->regs + epctrl_reg);
+ ctrl = dwc2_readl(hsotg, epctrl_reg);
if (index && ctrl & DXEPCTL_STALL) {
dev_warn(hsotg->dev, "%s: ep%d is stalled\n", __func__, index);
@@ -1062,13 +1104,13 @@ static void dwc2_hsotg_start_req(struct dwc2_hsotg *hsotg,
length);
/* write descriptor chain address to control register */
- dwc2_writel(hs_ep->desc_list_dma, hsotg->regs + dma_reg);
+ dwc2_writel(hsotg, hs_ep->desc_list_dma, dma_reg);
dev_dbg(hsotg->dev, "%s: %08x pad => 0x%08x\n",
__func__, (u32)hs_ep->desc_list_dma, dma_reg);
} else {
/* write size / packets */
- dwc2_writel(epsize, hsotg->regs + epsize_reg);
+ dwc2_writel(hsotg, epsize, epsize_reg);
if (using_dma(hsotg) && !continuing && (length != 0)) {
/*
@@ -1076,7 +1118,7 @@ static void dwc2_hsotg_start_req(struct dwc2_hsotg *hsotg,
* already synced by dwc2_hsotg_ep_queue().
*/
- dwc2_writel(ureq->dma, hsotg->regs + dma_reg);
+ dwc2_writel(hsotg, ureq->dma, dma_reg);
dev_dbg(hsotg->dev, "%s: %pad => 0x%08x\n",
__func__, &ureq->dma, dma_reg);
@@ -1102,7 +1144,7 @@ static void dwc2_hsotg_start_req(struct dwc2_hsotg *hsotg,
ctrl |= DXEPCTL_CNAK; /* clear NAK set by core */
dev_dbg(hsotg->dev, "%s: DxEPCTL=0x%08x\n", __func__, ctrl);
- dwc2_writel(ctrl, hsotg->regs + epctrl_reg);
+ dwc2_writel(hsotg, ctrl, epctrl_reg);
/*
* set these, it seems that DMA support increments past the end
@@ -1125,13 +1167,13 @@ static void dwc2_hsotg_start_req(struct dwc2_hsotg *hsotg,
*/
/* check ep is enabled */
- if (!(dwc2_readl(hsotg->regs + epctrl_reg) & DXEPCTL_EPENA))
+ if (!(dwc2_readl(hsotg, epctrl_reg) & DXEPCTL_EPENA))
dev_dbg(hsotg->dev,
"ep%d: failed to become enabled (DXEPCTL=0x%08x)?\n",
- index, dwc2_readl(hsotg->regs + epctrl_reg));
+ index, dwc2_readl(hsotg, epctrl_reg));
dev_dbg(hsotg->dev, "%s: DXEPCTL=0x%08x\n",
- __func__, dwc2_readl(hsotg->regs + epctrl_reg));
+ __func__, dwc2_readl(hsotg, epctrl_reg));
/* enable ep interrupts */
dwc2_hsotg_ctrl_epint(hsotg, hs_ep->index, hs_ep->dir_in, 1);
@@ -1235,7 +1277,7 @@ static bool dwc2_gadget_target_frame_elapsed(struct dwc2_hsotg_ep *hs_ep)
{
struct dwc2_hsotg *hsotg = hs_ep->parent;
u32 target_frame = hs_ep->target_frame;
- u32 current_frame = dwc2_hsotg_read_frameno(hsotg);
+ u32 current_frame = hsotg->frame_number;
bool frame_overrun = hs_ep->frame_overrun;
if (!frame_overrun && current_frame >= target_frame)
@@ -1291,6 +1333,9 @@ static int dwc2_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req,
struct dwc2_hsotg *hs = hs_ep->parent;
bool first;
int ret;
+ u32 maxsize = 0;
+ u32 mask = 0;
+
dev_dbg(hs->dev, "%s: req %p: %d@%p, noi=%d, zero=%d, snok=%d\n",
ep->name, req, req->length, req->buf, req->no_interrupt,
@@ -1308,6 +1353,24 @@ static int dwc2_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req,
req->actual = 0;
req->status = -EINPROGRESS;
+ /* In DDMA mode for ISOC's don't queue request if length greater
+ * than descriptor limits.
+ */
+ if (using_desc_dma(hs) && hs_ep->isochronous) {
+ maxsize = dwc2_gadget_get_desc_params(hs_ep, &mask);
+ if (hs_ep->dir_in && req->length > maxsize) {
+ dev_err(hs->dev, "wrong length %d (maxsize=%d)\n",
+ req->length, maxsize);
+ return -EINVAL;
+ }
+
+ if (!hs_ep->dir_in && req->length > hs_ep->ep.maxpacket) {
+ dev_err(hs->dev, "ISOC OUT: wrong length %d (mps=%d)\n",
+ req->length, hs_ep->ep.maxpacket);
+ return -EINVAL;
+ }
+ }
+
ret = dwc2_hsotg_handle_unaligned_buf_start(hs, hs_ep, hs_req);
if (ret)
return ret;
@@ -1330,17 +1393,15 @@ static int dwc2_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req,
/*
* Handle DDMA isochronous transfers separately - just add new entry
- * to the half of descriptor chain that is not processed by HW.
+ * to the descriptor chain.
* Transfer will be started once SW gets either one of NAK or
* OutTknEpDis interrupts.
*/
- if (using_desc_dma(hs) && hs_ep->isochronous &&
- hs_ep->target_frame != TARGET_FRAME_INITIAL) {
- ret = dwc2_gadget_fill_isoc_desc(hs_ep, hs_req->req.dma,
- hs_req->req.length);
- if (ret)
- dev_dbg(hs->dev, "%s: ISO desc chain full\n", __func__);
-
+ if (using_desc_dma(hs) && hs_ep->isochronous) {
+ if (hs_ep->target_frame != TARGET_FRAME_INITIAL) {
+ dwc2_gadget_fill_isoc_desc(hs_ep, hs_req->req.dma,
+ hs_req->req.length);
+ }
return 0;
}
@@ -1350,8 +1411,15 @@ static int dwc2_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req,
return 0;
}
- while (dwc2_gadget_target_frame_elapsed(hs_ep))
+ /* Update current frame number value. */
+ hs->frame_number = dwc2_hsotg_read_frameno(hs);
+ while (dwc2_gadget_target_frame_elapsed(hs_ep)) {
dwc2_gadget_incr_frame_num(hs_ep);
+ /* Update current frame number value once more as it
+ * changes here.
+ */
+ hs->frame_number = dwc2_hsotg_read_frameno(hs);
+ }
if (hs_ep->target_frame != TARGET_FRAME_INITIAL)
dwc2_hsotg_start_req(hs, hs_ep, hs_req, false);
@@ -1438,7 +1506,7 @@ static struct dwc2_hsotg_ep *ep_from_windex(struct dwc2_hsotg *hsotg,
*/
int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode)
{
- int dctl = dwc2_readl(hsotg->regs + DCTL);
+ int dctl = dwc2_readl(hsotg, DCTL);
dctl &= ~DCTL_TSTCTL_MASK;
switch (testmode) {
@@ -1452,7 +1520,7 @@ int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode)
default:
return -EINVAL;
}
- dwc2_writel(dctl, hsotg->regs + DCTL);
+ dwc2_writel(hsotg, dctl, DCTL);
return 0;
}
@@ -1606,9 +1674,9 @@ static void dwc2_gadget_start_next_request(struct dwc2_hsotg_ep *hs_ep)
} else {
dev_dbg(hsotg->dev, "%s: No more ISOC-OUT requests\n",
__func__);
- mask = dwc2_readl(hsotg->regs + epmsk_reg);
+ mask = dwc2_readl(hsotg, epmsk_reg);
mask |= DOEPMSK_OUTTKNEPDISMSK;
- dwc2_writel(mask, hsotg->regs + epmsk_reg);
+ dwc2_writel(hsotg, mask, epmsk_reg);
}
}
@@ -1745,14 +1813,14 @@ static void dwc2_hsotg_stall_ep0(struct dwc2_hsotg *hsotg)
* taken effect, so no need to clear later.
*/
- ctrl = dwc2_readl(hsotg->regs + reg);
+ ctrl = dwc2_readl(hsotg, reg);
ctrl |= DXEPCTL_STALL;
ctrl |= DXEPCTL_CNAK;
- dwc2_writel(ctrl, hsotg->regs + reg);
+ dwc2_writel(hsotg, ctrl, reg);
dev_dbg(hsotg->dev,
"written DXEPCTL=0x%08x to %08x (DXEPCTL=0x%08x)\n",
- ctrl, reg, dwc2_readl(hsotg->regs + reg));
+ ctrl, reg, dwc2_readl(hsotg, reg));
/*
* complete won't be called, so we enqueue
@@ -1797,11 +1865,11 @@ static void dwc2_hsotg_process_control(struct dwc2_hsotg *hsotg,
switch (ctrl->bRequest) {
case USB_REQ_SET_ADDRESS:
hsotg->connected = 1;
- dcfg = dwc2_readl(hsotg->regs + DCFG);
+ dcfg = dwc2_readl(hsotg, DCFG);
dcfg &= ~DCFG_DEVADDR_MASK;
dcfg |= (le16_to_cpu(ctrl->wValue) <<
DCFG_DEVADDR_SHIFT) & DCFG_DEVADDR_MASK;
- dwc2_writel(dcfg, hsotg->regs + DCFG);
+ dwc2_writel(hsotg, dcfg, DCFG);
dev_info(hsotg->dev, "new address %d\n", ctrl->wValue);
@@ -1927,16 +1995,16 @@ static void dwc2_hsotg_program_zlp(struct dwc2_hsotg *hsotg,
dwc2_gadget_config_nonisoc_xfer_ddma(hs_ep, dma, 0);
} else {
- dwc2_writel(DXEPTSIZ_MC(1) | DXEPTSIZ_PKTCNT(1) |
- DXEPTSIZ_XFERSIZE(0), hsotg->regs +
+ dwc2_writel(hsotg, DXEPTSIZ_MC(1) | DXEPTSIZ_PKTCNT(1) |
+ DXEPTSIZ_XFERSIZE(0),
epsiz_reg);
}
- ctrl = dwc2_readl(hsotg->regs + epctl_reg);
+ ctrl = dwc2_readl(hsotg, epctl_reg);
ctrl |= DXEPCTL_CNAK; /* clear NAK set by core */
ctrl |= DXEPCTL_EPENA; /* ensure ep enabled */
ctrl |= DXEPCTL_USBACTEP;
- dwc2_writel(ctrl, hsotg->regs + epctl_reg);
+ dwc2_writel(hsotg, ctrl, epctl_reg);
}
/**
@@ -2011,108 +2079,75 @@ static void dwc2_hsotg_complete_request(struct dwc2_hsotg *hsotg,
* @hs_ep: The endpoint the request was on.
*
* Get first request from the ep queue, determine descriptor on which complete
- * happened. SW based on isoc_chain_num discovers which half of the descriptor
- * chain is currently in use by HW, adjusts dma_address and calculates index
- * of completed descriptor based on the value of DEPDMA register. Update actual
- * length of request, giveback to gadget.
+ * happened. SW discovers which descriptor currently in use by HW, adjusts
+ * dma_address and calculates index of completed descriptor based on the value
+ * of DEPDMA register. Update actual length of request, giveback to gadget.
*/
static void dwc2_gadget_complete_isoc_request_ddma(struct dwc2_hsotg_ep *hs_ep)
{
struct dwc2_hsotg *hsotg = hs_ep->parent;
struct dwc2_hsotg_req *hs_req;
struct usb_request *ureq;
- int index;
- dma_addr_t dma_addr;
- u32 dma_reg;
- u32 depdma;
u32 desc_sts;
u32 mask;
- hs_req = get_ep_head(hs_ep);
- if (!hs_req) {
- dev_warn(hsotg->dev, "%s: ISOC EP queue empty\n", __func__);
- return;
- }
- ureq = &hs_req->req;
-
- dma_addr = hs_ep->desc_list_dma;
-
- /*
- * If lower half of descriptor chain is currently use by SW,
- * that means higher half is being processed by HW, so shift
- * DMA address to higher half of descriptor chain.
- */
- if (!hs_ep->isoc_chain_num)
- dma_addr += sizeof(struct dwc2_dma_desc) *
- (MAX_DMA_DESC_NUM_GENERIC / 2);
-
- dma_reg = hs_ep->dir_in ? DIEPDMA(hs_ep->index) : DOEPDMA(hs_ep->index);
- depdma = dwc2_readl(hsotg->regs + dma_reg);
+ desc_sts = hs_ep->desc_list[hs_ep->compl_desc].status;
- index = (depdma - dma_addr) / sizeof(struct dwc2_dma_desc) - 1;
- desc_sts = hs_ep->desc_list[index].status;
+ /* Process only descriptors with buffer status set to DMA done */
+ while ((desc_sts & DEV_DMA_BUFF_STS_MASK) >>
+ DEV_DMA_BUFF_STS_SHIFT == DEV_DMA_BUFF_STS_DMADONE) {
- mask = hs_ep->dir_in ? DEV_DMA_ISOC_TX_NBYTES_MASK :
- DEV_DMA_ISOC_RX_NBYTES_MASK;
- ureq->actual = ureq->length -
- ((desc_sts & mask) >> DEV_DMA_ISOC_NBYTES_SHIFT);
+ hs_req = get_ep_head(hs_ep);
+ if (!hs_req) {
+ dev_warn(hsotg->dev, "%s: ISOC EP queue empty\n", __func__);
+ return;
+ }
+ ureq = &hs_req->req;
+
+ /* Check completion status */
+ if ((desc_sts & DEV_DMA_STS_MASK) >> DEV_DMA_STS_SHIFT ==
+ DEV_DMA_STS_SUCC) {
+ mask = hs_ep->dir_in ? DEV_DMA_ISOC_TX_NBYTES_MASK :
+ DEV_DMA_ISOC_RX_NBYTES_MASK;
+ ureq->actual = ureq->length - ((desc_sts & mask) >>
+ DEV_DMA_ISOC_NBYTES_SHIFT);
+
+ /* Adjust actual len for ISOC Out if len is
+ * not align of 4
+ */
+ if (!hs_ep->dir_in && ureq->length & 0x3)
+ ureq->actual += 4 - (ureq->length & 0x3);
+ }
- /* Adjust actual length for ISOC Out if length is not align of 4 */
- if (!hs_ep->dir_in && ureq->length & 0x3)
- ureq->actual += 4 - (ureq->length & 0x3);
+ dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req, 0);
- dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req, 0);
+ hs_ep->compl_desc++;
+ if (hs_ep->compl_desc > (MAX_DMA_DESC_NUM_GENERIC - 1))
+ hs_ep->compl_desc = 0;
+ desc_sts = hs_ep->desc_list[hs_ep->compl_desc].status;
+ }
}
/*
- * dwc2_gadget_start_next_isoc_ddma - start next isoc request, if any.
- * @hs_ep: The isochronous endpoint to be re-enabled.
+ * dwc2_gadget_handle_isoc_bna - handle BNA interrupt for ISOC.
+ * @hs_ep: The isochronous endpoint.
*
- * If ep has been disabled due to last descriptor servicing (IN endpoint) or
- * BNA (OUT endpoint) check the status of other half of descriptor chain that
- * was under SW control till HW was busy and restart the endpoint if needed.
+ * If EP ISOC OUT then need to flush RX FIFO to remove source of BNA
+ * interrupt. Reset target frame and next_desc to allow to start
+ * ISOC's on NAK interrupt for IN direction or on OUTTKNEPDIS
+ * interrupt for OUT direction.
*/
-static void dwc2_gadget_start_next_isoc_ddma(struct dwc2_hsotg_ep *hs_ep)
+static void dwc2_gadget_handle_isoc_bna(struct dwc2_hsotg_ep *hs_ep)
{
struct dwc2_hsotg *hsotg = hs_ep->parent;
- u32 depctl;
- u32 dma_reg;
- u32 ctrl;
- u32 dma_addr = hs_ep->desc_list_dma;
- unsigned char index = hs_ep->index;
- dma_reg = hs_ep->dir_in ? DIEPDMA(index) : DOEPDMA(index);
- depctl = hs_ep->dir_in ? DIEPCTL(index) : DOEPCTL(index);
-
- ctrl = dwc2_readl(hsotg->regs + depctl);
-
- /*
- * EP was disabled if HW has processed last descriptor or BNA was set.
- * So restart ep if SW has prepared new descriptor chain in ep_queue
- * routine while HW was busy.
- */
- if (!(ctrl & DXEPCTL_EPENA)) {
- if (!hs_ep->next_desc) {
- dev_dbg(hsotg->dev, "%s: No more ISOC requests\n",
- __func__);
- return;
- }
-
- dma_addr += sizeof(struct dwc2_dma_desc) *
- (MAX_DMA_DESC_NUM_GENERIC / 2) *
- hs_ep->isoc_chain_num;
- dwc2_writel(dma_addr, hsotg->regs + dma_reg);
-
- ctrl |= DXEPCTL_EPENA | DXEPCTL_CNAK;
- dwc2_writel(ctrl, hsotg->regs + depctl);
-
- /* Switch ISOC descriptor chain number being processed by SW*/
- hs_ep->isoc_chain_num = (hs_ep->isoc_chain_num ^ 1) & 0x1;
- hs_ep->next_desc = 0;
+ if (!hs_ep->dir_in)
+ dwc2_flush_rx_fifo(hsotg);
+ dwc2_hsotg_complete_request(hsotg, hs_ep, get_ep_head(hs_ep), 0);
- dev_dbg(hsotg->dev, "%s: Restarted isochronous endpoint\n",
- __func__);
- }
+ hs_ep->target_frame = TARGET_FRAME_INITIAL;
+ hs_ep->next_desc = 0;
+ hs_ep->compl_desc = 0;
}
/**
@@ -2129,13 +2164,12 @@ static void dwc2_hsotg_rx_data(struct dwc2_hsotg *hsotg, int ep_idx, int size)
{
struct dwc2_hsotg_ep *hs_ep = hsotg->eps_out[ep_idx];
struct dwc2_hsotg_req *hs_req = hs_ep->req;
- void __iomem *fifo = hsotg->regs + EPFIFO(ep_idx);
int to_read;
int max_req;
int read_ptr;
if (!hs_req) {
- u32 epctl = dwc2_readl(hsotg->regs + DOEPCTL(ep_idx));
+ u32 epctl = dwc2_readl(hsotg, DOEPCTL(ep_idx));
int ptr;
dev_dbg(hsotg->dev,
@@ -2144,7 +2178,7 @@ static void dwc2_hsotg_rx_data(struct dwc2_hsotg *hsotg, int ep_idx, int size)
/* dump the data from the FIFO, we've nothing we can do */
for (ptr = 0; ptr < size; ptr += 4)
- (void)dwc2_readl(fifo);
+ (void)dwc2_readl(hsotg, EPFIFO(ep_idx));
return;
}
@@ -2174,7 +2208,8 @@ static void dwc2_hsotg_rx_data(struct dwc2_hsotg *hsotg, int ep_idx, int size)
* note, we might over-write the buffer end by 3 bytes depending on
* alignment of the data.
*/
- ioread32_rep(fifo, hs_req->req.buf + read_ptr, to_read);
+ dwc2_readl_rep(hsotg, EPFIFO(ep_idx),
+ hs_req->req.buf + read_ptr, to_read);
}
/**
@@ -2203,12 +2238,12 @@ static void dwc2_hsotg_change_ep_iso_parity(struct dwc2_hsotg *hsotg,
{
u32 ctrl;
- ctrl = dwc2_readl(hsotg->regs + epctl_reg);
+ ctrl = dwc2_readl(hsotg, epctl_reg);
if (ctrl & DXEPCTL_EOFRNUM)
ctrl |= DXEPCTL_SETEVENFR;
else
ctrl |= DXEPCTL_SETODDFR;
- dwc2_writel(ctrl, hsotg->regs + epctl_reg);
+ dwc2_writel(hsotg, ctrl, epctl_reg);
}
/*
@@ -2252,7 +2287,7 @@ static unsigned int dwc2_gadget_get_xfersize_ddma(struct dwc2_hsotg_ep *hs_ep)
*/
static void dwc2_hsotg_handle_outdone(struct dwc2_hsotg *hsotg, int epnum)
{
- u32 epsize = dwc2_readl(hsotg->regs + DOEPTSIZ(epnum));
+ u32 epsize = dwc2_readl(hsotg, DOEPTSIZ(epnum));
struct dwc2_hsotg_ep *hs_ep = hsotg->eps_out[epnum];
struct dwc2_hsotg_req *hs_req = hs_ep->req;
struct usb_request *req = &hs_req->req;
@@ -2348,7 +2383,7 @@ static void dwc2_hsotg_handle_outdone(struct dwc2_hsotg *hsotg, int epnum)
*/
static void dwc2_hsotg_handle_rx(struct dwc2_hsotg *hsotg)
{
- u32 grxstsr = dwc2_readl(hsotg->regs + GRXSTSP);
+ u32 grxstsr = dwc2_readl(hsotg, GRXSTSP);
u32 epnum, status, size;
WARN_ON(using_dma(hsotg));
@@ -2379,7 +2414,7 @@ static void dwc2_hsotg_handle_rx(struct dwc2_hsotg *hsotg)
dev_dbg(hsotg->dev,
"SetupDone (Frame=0x%08x, DOPEPCTL=0x%08x)\n",
dwc2_hsotg_read_frameno(hsotg),
- dwc2_readl(hsotg->regs + DOEPCTL(0)));
+ dwc2_readl(hsotg, DOEPCTL(0)));
/*
* Call dwc2_hsotg_handle_outdone here if it was not called from
* GRXSTS_PKTSTS_OUTDONE. That is, if the core didn't
@@ -2397,7 +2432,7 @@ static void dwc2_hsotg_handle_rx(struct dwc2_hsotg *hsotg)
dev_dbg(hsotg->dev,
"SetupRX (Frame=0x%08x, DOPEPCTL=0x%08x)\n",
dwc2_hsotg_read_frameno(hsotg),
- dwc2_readl(hsotg->regs + DOEPCTL(0)));
+ dwc2_readl(hsotg, DOEPCTL(0)));
WARN_ON(hsotg->ep0_state != DWC2_EP0_SETUP);
@@ -2441,6 +2476,7 @@ static u32 dwc2_hsotg_ep0_mps(unsigned int mps)
* @ep: The index number of the endpoint
* @mps: The maximum packet size in bytes
* @mc: The multicount value
+ * @dir_in: True if direction is in.
*
* Configure the maximum packet size for the given endpoint, updating
* the hardware control registers to reflect this.
@@ -2450,7 +2486,6 @@ static void dwc2_hsotg_set_ep_maxpacket(struct dwc2_hsotg *hsotg,
unsigned int mc, unsigned int dir_in)
{
struct dwc2_hsotg_ep *hs_ep;
- void __iomem *regs = hsotg->regs;
u32 reg;
hs_ep = index_to_ep(hsotg, ep, dir_in);
@@ -2476,15 +2511,15 @@ static void dwc2_hsotg_set_ep_maxpacket(struct dwc2_hsotg *hsotg,
}
if (dir_in) {
- reg = dwc2_readl(regs + DIEPCTL(ep));
+ reg = dwc2_readl(hsotg, DIEPCTL(ep));
reg &= ~DXEPCTL_MPS_MASK;
reg |= mps;
- dwc2_writel(reg, regs + DIEPCTL(ep));
+ dwc2_writel(hsotg, reg, DIEPCTL(ep));
} else {
- reg = dwc2_readl(regs + DOEPCTL(ep));
+ reg = dwc2_readl(hsotg, DOEPCTL(ep));
reg &= ~DXEPCTL_MPS_MASK;
reg |= mps;
- dwc2_writel(reg, regs + DOEPCTL(ep));
+ dwc2_writel(hsotg, reg, DOEPCTL(ep));
}
return;
@@ -2500,8 +2535,8 @@ bad_mps:
*/
static void dwc2_hsotg_txfifo_flush(struct dwc2_hsotg *hsotg, unsigned int idx)
{
- dwc2_writel(GRSTCTL_TXFNUM(idx) | GRSTCTL_TXFFLSH,
- hsotg->regs + GRSTCTL);
+ dwc2_writel(hsotg, GRSTCTL_TXFNUM(idx) | GRSTCTL_TXFFLSH,
+ GRSTCTL);
/* wait until the fifo is flushed */
if (dwc2_hsotg_wait_bit_clear(hsotg, GRSTCTL, GRSTCTL_TXFFLSH, 100))
@@ -2554,7 +2589,7 @@ static void dwc2_hsotg_complete_in(struct dwc2_hsotg *hsotg,
struct dwc2_hsotg_ep *hs_ep)
{
struct dwc2_hsotg_req *hs_req = hs_ep->req;
- u32 epsize = dwc2_readl(hsotg->regs + DIEPTSIZ(hs_ep->index));
+ u32 epsize = dwc2_readl(hsotg, DIEPTSIZ(hs_ep->index));
int size_left, size_done;
if (!hs_req) {
@@ -2658,12 +2693,12 @@ static u32 dwc2_gadget_read_ep_interrupts(struct dwc2_hsotg *hsotg,
u32 mask;
u32 diepempmsk;
- mask = dwc2_readl(hsotg->regs + epmsk_reg);
- diepempmsk = dwc2_readl(hsotg->regs + DIEPEMPMSK);
+ mask = dwc2_readl(hsotg, epmsk_reg);
+ diepempmsk = dwc2_readl(hsotg, DIEPEMPMSK);
mask |= ((diepempmsk >> idx) & 0x1) ? DIEPMSK_TXFIFOEMPTY : 0;
mask |= DXEPINT_SETUP_RCVD;
- ints = dwc2_readl(hsotg->regs + epint_reg);
+ ints = dwc2_readl(hsotg, epint_reg);
ints &= mask;
return ints;
}
@@ -2688,12 +2723,12 @@ static void dwc2_gadget_handle_ep_disabled(struct dwc2_hsotg_ep *hs_ep)
unsigned char idx = hs_ep->index;
int dir_in = hs_ep->dir_in;
u32 epctl_reg = dir_in ? DIEPCTL(idx) : DOEPCTL(idx);
- int dctl = dwc2_readl(hsotg->regs + DCTL);
+ int dctl = dwc2_readl(hsotg, DCTL);
dev_dbg(hsotg->dev, "%s: EPDisbld\n", __func__);
if (dir_in) {
- int epctl = dwc2_readl(hsotg->regs + epctl_reg);
+ int epctl = dwc2_readl(hsotg, epctl_reg);
dwc2_hsotg_txfifo_flush(hsotg, hs_ep->fifo_index);
@@ -2703,17 +2738,17 @@ static void dwc2_gadget_handle_ep_disabled(struct dwc2_hsotg_ep *hs_ep)
}
if ((epctl & DXEPCTL_STALL) && (epctl & DXEPCTL_EPTYPE_BULK)) {
- int dctl = dwc2_readl(hsotg->regs + DCTL);
+ int dctl = dwc2_readl(hsotg, DCTL);
dctl |= DCTL_CGNPINNAK;
- dwc2_writel(dctl, hsotg->regs + DCTL);
+ dwc2_writel(hsotg, dctl, DCTL);
}
return;
}
if (dctl & DCTL_GOUTNAKSTS) {
dctl |= DCTL_CGOUTNAK;
- dwc2_writel(dctl, hsotg->regs + DCTL);
+ dwc2_writel(hsotg, dctl, DCTL);
}
if (!hs_ep->isochronous)
@@ -2731,6 +2766,8 @@ static void dwc2_gadget_handle_ep_disabled(struct dwc2_hsotg_ep *hs_ep)
dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req,
-ENODATA);
dwc2_gadget_incr_frame_num(hs_ep);
+ /* Update current frame number value. */
+ hsotg->frame_number = dwc2_hsotg_read_frameno(hsotg);
} while (dwc2_gadget_target_frame_elapsed(hs_ep));
dwc2_gadget_start_next_request(hs_ep);
@@ -2738,7 +2775,7 @@ static void dwc2_gadget_handle_ep_disabled(struct dwc2_hsotg_ep *hs_ep)
/**
* dwc2_gadget_handle_out_token_ep_disabled - handle DXEPINT_OUTTKNEPDIS
- * @hs_ep: The endpoint on which interrupt is asserted.
+ * @ep: The endpoint on which interrupt is asserted.
*
* This is starting point for ISOC-OUT transfer, synchronization done with
* first out token received from host while corresponding EP is disabled.
@@ -2752,23 +2789,14 @@ static void dwc2_gadget_handle_out_token_ep_disabled(struct dwc2_hsotg_ep *ep)
struct dwc2_hsotg *hsotg = ep->parent;
int dir_in = ep->dir_in;
u32 doepmsk;
- u32 tmp;
if (dir_in || !ep->isochronous)
return;
- /*
- * Store frame in which irq was asserted here, as
- * it can change while completing request below.
- */
- tmp = dwc2_hsotg_read_frameno(hsotg);
-
- dwc2_hsotg_complete_request(hsotg, ep, get_ep_head(ep), -ENODATA);
-
if (using_desc_dma(hsotg)) {
if (ep->target_frame == TARGET_FRAME_INITIAL) {
/* Start first ISO Out */
- ep->target_frame = tmp;
+ ep->target_frame = hsotg->frame_number;
dwc2_gadget_start_isoc_ddma(ep);
}
return;
@@ -2776,26 +2804,24 @@ static void dwc2_gadget_handle_out_token_ep_disabled(struct dwc2_hsotg_ep *ep)
if (ep->interval > 1 &&
ep->target_frame == TARGET_FRAME_INITIAL) {
- u32 dsts;
u32 ctrl;
- dsts = dwc2_readl(hsotg->regs + DSTS);
- ep->target_frame = dwc2_hsotg_read_frameno(hsotg);
+ ep->target_frame = hsotg->frame_number;
dwc2_gadget_incr_frame_num(ep);
- ctrl = dwc2_readl(hsotg->regs + DOEPCTL(ep->index));
+ ctrl = dwc2_readl(hsotg, DOEPCTL(ep->index));
if (ep->target_frame & 0x1)
ctrl |= DXEPCTL_SETODDFR;
else
ctrl |= DXEPCTL_SETEVENFR;
- dwc2_writel(ctrl, hsotg->regs + DOEPCTL(ep->index));
+ dwc2_writel(hsotg, ctrl, DOEPCTL(ep->index));
}
dwc2_gadget_start_next_request(ep);
- doepmsk = dwc2_readl(hsotg->regs + DOEPMSK);
+ doepmsk = dwc2_readl(hsotg, DOEPMSK);
doepmsk &= ~DOEPMSK_OUTTKNEPDISMSK;
- dwc2_writel(doepmsk, hsotg->regs + DOEPMSK);
+ dwc2_writel(hsotg, doepmsk, DOEPMSK);
}
/**
@@ -2821,29 +2847,49 @@ static void dwc2_gadget_handle_nak(struct dwc2_hsotg_ep *hs_ep)
return;
if (hs_ep->target_frame == TARGET_FRAME_INITIAL) {
- hs_ep->target_frame = dwc2_hsotg_read_frameno(hsotg);
if (using_desc_dma(hsotg)) {
+ hs_ep->target_frame = hsotg->frame_number;
+ dwc2_gadget_incr_frame_num(hs_ep);
+
+ /* In service interval mode target_frame must
+ * be set to last (u)frame of the service interval.
+ */
+ if (hsotg->params.service_interval) {
+ /* Set target_frame to the first (u)frame of
+ * the service interval
+ */
+ hs_ep->target_frame &= ~hs_ep->interval + 1;
+
+ /* Set target_frame to the last (u)frame of
+ * the service interval
+ */
+ dwc2_gadget_incr_frame_num(hs_ep);
+ dwc2_gadget_dec_frame_num_by_one(hs_ep);
+ }
+
dwc2_gadget_start_isoc_ddma(hs_ep);
return;
}
+ hs_ep->target_frame = hsotg->frame_number;
if (hs_ep->interval > 1) {
- u32 ctrl = dwc2_readl(hsotg->regs +
+ u32 ctrl = dwc2_readl(hsotg,
DIEPCTL(hs_ep->index));
if (hs_ep->target_frame & 0x1)
ctrl |= DXEPCTL_SETODDFR;
else
ctrl |= DXEPCTL_SETEVENFR;
- dwc2_writel(ctrl, hsotg->regs + DIEPCTL(hs_ep->index));
+ dwc2_writel(hsotg, ctrl, DIEPCTL(hs_ep->index));
}
dwc2_hsotg_complete_request(hsotg, hs_ep,
get_ep_head(hs_ep), 0);
}
- dwc2_gadget_incr_frame_num(hs_ep);
+ if (!using_desc_dma(hsotg))
+ dwc2_gadget_incr_frame_num(hs_ep);
}
/**
@@ -2865,10 +2911,10 @@ static void dwc2_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx,
u32 ctrl;
ints = dwc2_gadget_read_ep_interrupts(hsotg, idx, dir_in);
- ctrl = dwc2_readl(hsotg->regs + epctl_reg);
+ ctrl = dwc2_readl(hsotg, epctl_reg);
/* Clear endpoint interrupts */
- dwc2_writel(ints, hsotg->regs + epint_reg);
+ dwc2_writel(hsotg, ints, epint_reg);
if (!hs_ep) {
dev_err(hsotg->dev, "%s:Interrupt for unconfigured ep%d(%s)\n",
@@ -2896,14 +2942,14 @@ static void dwc2_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx,
if (ints & DXEPINT_XFERCOMPL) {
dev_dbg(hsotg->dev,
"%s: XferCompl: DxEPCTL=0x%08x, DXEPTSIZ=%08x\n",
- __func__, dwc2_readl(hsotg->regs + epctl_reg),
- dwc2_readl(hsotg->regs + epsiz_reg));
+ __func__, dwc2_readl(hsotg, epctl_reg),
+ dwc2_readl(hsotg, epsiz_reg));
/* In DDMA handle isochronous requests separately */
if (using_desc_dma(hsotg) && hs_ep->isochronous) {
- dwc2_gadget_complete_isoc_request_ddma(hs_ep);
- /* Try to start next isoc request */
- dwc2_gadget_start_next_isoc_ddma(hs_ep);
+ /* XferCompl set along with BNA */
+ if (!(ints & DXEPINT_BNAINTR))
+ dwc2_gadget_complete_isoc_request_ddma(hs_ep);
} else if (dir_in) {
/*
* We get OutDone from the FIFO, so we only
@@ -2978,15 +3024,8 @@ static void dwc2_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx,
if (ints & DXEPINT_BNAINTR) {
dev_dbg(hsotg->dev, "%s: BNA interrupt\n", __func__);
-
- /*
- * Try to start next isoc request, if any.
- * Sometimes the endpoint remains enabled after BNA interrupt
- * assertion, which is not expected, hence we can enter here
- * couple of times.
- */
if (hs_ep->isochronous)
- dwc2_gadget_start_next_isoc_ddma(hs_ep);
+ dwc2_gadget_handle_isoc_bna(hs_ep);
}
if (dir_in && !hs_ep->isochronous) {
@@ -3022,7 +3061,7 @@ static void dwc2_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx,
*/
static void dwc2_hsotg_irq_enumdone(struct dwc2_hsotg *hsotg)
{
- u32 dsts = dwc2_readl(hsotg->regs + DSTS);
+ u32 dsts = dwc2_readl(hsotg, DSTS);
int ep0_mps = 0, ep_mps = 8;
/*
@@ -3093,8 +3132,8 @@ static void dwc2_hsotg_irq_enumdone(struct dwc2_hsotg *hsotg)
dwc2_hsotg_enqueue_setup(hsotg);
dev_dbg(hsotg->dev, "EP0: DIEPCTL0=0x%08x, DOEPCTL0=0x%08x\n",
- dwc2_readl(hsotg->regs + DIEPCTL0),
- dwc2_readl(hsotg->regs + DOEPCTL0));
+ dwc2_readl(hsotg, DIEPCTL0),
+ dwc2_readl(hsotg, DOEPCTL0));
}
/**
@@ -3121,11 +3160,13 @@ static void kill_all_requests(struct dwc2_hsotg *hsotg,
if (!hsotg->dedicated_fifos)
return;
- size = (dwc2_readl(hsotg->regs + DTXFSTS(ep->fifo_index)) & 0xffff) * 4;
+ size = (dwc2_readl(hsotg, DTXFSTS(ep->fifo_index)) & 0xffff) * 4;
if (size < ep->fifo_size)
dwc2_hsotg_txfifo_flush(hsotg, ep->fifo_index);
}
+static int dwc2_hsotg_ep_disable(struct usb_ep *ep);
+
/**
* dwc2_hsotg_disconnect - disconnect service
* @hsotg: The device state.
@@ -3144,13 +3185,12 @@ void dwc2_hsotg_disconnect(struct dwc2_hsotg *hsotg)
hsotg->connected = 0;
hsotg->test_mode = 0;
+ /* all endpoints should be shutdown */
for (ep = 0; ep < hsotg->num_of_eps; ep++) {
if (hsotg->eps_in[ep])
- kill_all_requests(hsotg, hsotg->eps_in[ep],
- -ESHUTDOWN);
+ dwc2_hsotg_ep_disable(&hsotg->eps_in[ep]->ep);
if (hsotg->eps_out[ep])
- kill_all_requests(hsotg, hsotg->eps_out[ep],
- -ESHUTDOWN);
+ dwc2_hsotg_ep_disable(&hsotg->eps_out[ep]->ep);
}
call_gadget(hsotg, disconnect);
@@ -3197,6 +3237,7 @@ static void dwc2_hsotg_irq_fifoempty(struct dwc2_hsotg *hsotg, bool periodic)
/**
* dwc2_hsotg_core_init - issue softreset to the core
* @hsotg: The device state
+ * @is_usb_reset: Usb resetting flag
*
* Issue a soft reset to the core, and await the core finishing it.
*/
@@ -3207,13 +3248,23 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg,
u32 val;
u32 usbcfg;
u32 dcfg = 0;
+ int ep;
/* Kill any ep0 requests as controller will be reinitialized */
kill_all_requests(hsotg, hsotg->eps_out[0], -ECONNRESET);
- if (!is_usb_reset)
+ if (!is_usb_reset) {
if (dwc2_core_reset(hsotg, true))
return;
+ } else {
+ /* all endpoints should be shutdown */
+ for (ep = 1; ep < hsotg->num_of_eps; ep++) {
+ if (hsotg->eps_in[ep])
+ dwc2_hsotg_ep_disable(&hsotg->eps_in[ep]->ep);
+ if (hsotg->eps_out[ep])
+ dwc2_hsotg_ep_disable(&hsotg->eps_out[ep]->ep);
+ }
+ }
/*
* we must now enable ep0 ready for host detection and then
@@ -3221,7 +3272,7 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg,
*/
/* keep other bits untouched (so e.g. forced modes are not lost) */
- usbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
+ usbcfg = dwc2_readl(hsotg, GUSBCFG);
usbcfg &= ~(GUSBCFG_TOUTCAL_MASK | GUSBCFG_PHYIF16 | GUSBCFG_SRPCAP |
GUSBCFG_HNPCAP | GUSBCFG_USBTRDTIM_MASK);
@@ -3236,12 +3287,12 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg,
usbcfg |= hsotg->phyif | GUSBCFG_TOUTCAL(7) |
(val << GUSBCFG_USBTRDTIM_SHIFT);
}
- dwc2_writel(usbcfg, hsotg->regs + GUSBCFG);
+ dwc2_writel(hsotg, usbcfg, GUSBCFG);
dwc2_hsotg_init_fifo(hsotg);
if (!is_usb_reset)
- dwc2_set_bit(hsotg->regs + DCTL, DCTL_SFTDISCON);
+ dwc2_set_bit(hsotg, DCTL, DCTL_SFTDISCON);
dcfg |= DCFG_EPMISCNT(1);
@@ -3259,13 +3310,16 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg,
dcfg |= DCFG_DEVSPD_HS;
}
- dwc2_writel(dcfg, hsotg->regs + DCFG);
+ if (hsotg->params.ipg_isoc_en)
+ dcfg |= DCFG_IPG_ISOC_SUPPORDED;
+
+ dwc2_writel(hsotg, dcfg, DCFG);
/* Clear any pending OTG interrupts */
- dwc2_writel(0xffffffff, hsotg->regs + GOTGINT);
+ dwc2_writel(hsotg, 0xffffffff, GOTGINT);
/* Clear any pending interrupts */
- dwc2_writel(0xffffffff, hsotg->regs + GINTSTS);
+ dwc2_writel(hsotg, 0xffffffff, GINTSTS);
intmsk = GINTSTS_ERLYSUSP | GINTSTS_SESSREQINT |
GINTSTS_GOUTNAKEFF | GINTSTS_GINNAKEFF |
GINTSTS_USBRST | GINTSTS_RESETDET |
@@ -3279,22 +3333,22 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg,
if (!hsotg->params.external_id_pin_ctl)
intmsk |= GINTSTS_CONIDSTSCHNG;
- dwc2_writel(intmsk, hsotg->regs + GINTMSK);
+ dwc2_writel(hsotg, intmsk, GINTMSK);
if (using_dma(hsotg)) {
- dwc2_writel(GAHBCFG_GLBL_INTR_EN | GAHBCFG_DMA_EN |
+ dwc2_writel(hsotg, GAHBCFG_GLBL_INTR_EN | GAHBCFG_DMA_EN |
hsotg->params.ahbcfg,
- hsotg->regs + GAHBCFG);
+ GAHBCFG);
/* Set DDMA mode support in the core if needed */
if (using_desc_dma(hsotg))
- dwc2_set_bit(hsotg->regs + DCFG, DCFG_DESCDMA_EN);
+ dwc2_set_bit(hsotg, DCFG, DCFG_DESCDMA_EN);
} else {
- dwc2_writel(((hsotg->dedicated_fifos) ?
+ dwc2_writel(hsotg, ((hsotg->dedicated_fifos) ?
(GAHBCFG_NP_TXF_EMP_LVL |
GAHBCFG_P_TXF_EMP_LVL) : 0) |
- GAHBCFG_GLBL_INTR_EN, hsotg->regs + GAHBCFG);
+ GAHBCFG_GLBL_INTR_EN, GAHBCFG);
}
/*
@@ -3303,31 +3357,37 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg,
* interrupts.
*/
- dwc2_writel(((hsotg->dedicated_fifos && !using_dma(hsotg)) ?
+ dwc2_writel(hsotg, ((hsotg->dedicated_fifos && !using_dma(hsotg)) ?
DIEPMSK_TXFIFOEMPTY | DIEPMSK_INTKNTXFEMPMSK : 0) |
DIEPMSK_EPDISBLDMSK | DIEPMSK_XFERCOMPLMSK |
DIEPMSK_TIMEOUTMSK | DIEPMSK_AHBERRMSK,
- hsotg->regs + DIEPMSK);
+ DIEPMSK);
/*
* don't need XferCompl, we get that from RXFIFO in slave mode. In
* DMA mode we may need this and StsPhseRcvd.
*/
- dwc2_writel((using_dma(hsotg) ? (DIEPMSK_XFERCOMPLMSK |
+ dwc2_writel(hsotg, (using_dma(hsotg) ? (DIEPMSK_XFERCOMPLMSK |
DOEPMSK_STSPHSERCVDMSK) : 0) |
DOEPMSK_EPDISBLDMSK | DOEPMSK_AHBERRMSK |
DOEPMSK_SETUPMSK,
- hsotg->regs + DOEPMSK);
+ DOEPMSK);
/* Enable BNA interrupt for DDMA */
- if (using_desc_dma(hsotg))
- dwc2_set_bit(hsotg->regs + DOEPMSK, DOEPMSK_BNAMSK);
+ if (using_desc_dma(hsotg)) {
+ dwc2_set_bit(hsotg, DOEPMSK, DOEPMSK_BNAMSK);
+ dwc2_set_bit(hsotg, DIEPMSK, DIEPMSK_BNAININTRMSK);
+ }
- dwc2_writel(0, hsotg->regs + DAINTMSK);
+ /* Enable Service Interval mode if supported */
+ if (using_desc_dma(hsotg) && hsotg->params.service_interval)
+ dwc2_set_bit(hsotg, DCTL, DCTL_SERVICE_INTERVAL_SUPPORTED);
+
+ dwc2_writel(hsotg, 0, DAINTMSK);
dev_dbg(hsotg->dev, "EP0: DIEPCTL0=0x%08x, DOEPCTL0=0x%08x\n",
- dwc2_readl(hsotg->regs + DIEPCTL0),
- dwc2_readl(hsotg->regs + DOEPCTL0));
+ dwc2_readl(hsotg, DIEPCTL0),
+ dwc2_readl(hsotg, DOEPCTL0));
/* enable in and out endpoint interrupts */
dwc2_hsotg_en_gsint(hsotg, GINTSTS_OEPINT | GINTSTS_IEPINT);
@@ -3345,12 +3405,12 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg,
dwc2_hsotg_ctrl_epint(hsotg, 0, 1, 1);
if (!is_usb_reset) {
- dwc2_set_bit(hsotg->regs + DCTL, DCTL_PWRONPRGDONE);
+ dwc2_set_bit(hsotg, DCTL, DCTL_PWRONPRGDONE);
udelay(10); /* see openiboot */
- dwc2_clear_bit(hsotg->regs + DCTL, DCTL_PWRONPRGDONE);
+ dwc2_clear_bit(hsotg, DCTL, DCTL_PWRONPRGDONE);
}
- dev_dbg(hsotg->dev, "DCTL=0x%08x\n", dwc2_readl(hsotg->regs + DCTL));
+ dev_dbg(hsotg->dev, "DCTL=0x%08x\n", dwc2_readl(hsotg, DCTL));
/*
* DxEPCTL_USBActEp says RO in manual, but seems to be set by
@@ -3358,27 +3418,31 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg,
*/
/* set to read 1 8byte packet */
- dwc2_writel(DXEPTSIZ_MC(1) | DXEPTSIZ_PKTCNT(1) |
- DXEPTSIZ_XFERSIZE(8), hsotg->regs + DOEPTSIZ0);
+ dwc2_writel(hsotg, DXEPTSIZ_MC(1) | DXEPTSIZ_PKTCNT(1) |
+ DXEPTSIZ_XFERSIZE(8), DOEPTSIZ0);
- dwc2_writel(dwc2_hsotg_ep0_mps(hsotg->eps_out[0]->ep.maxpacket) |
+ dwc2_writel(hsotg, dwc2_hsotg_ep0_mps(hsotg->eps_out[0]->ep.maxpacket) |
DXEPCTL_CNAK | DXEPCTL_EPENA |
DXEPCTL_USBACTEP,
- hsotg->regs + DOEPCTL0);
+ DOEPCTL0);
/* enable, but don't activate EP0in */
- dwc2_writel(dwc2_hsotg_ep0_mps(hsotg->eps_out[0]->ep.maxpacket) |
- DXEPCTL_USBACTEP, hsotg->regs + DIEPCTL0);
+ dwc2_writel(hsotg, dwc2_hsotg_ep0_mps(hsotg->eps_out[0]->ep.maxpacket) |
+ DXEPCTL_USBACTEP, DIEPCTL0);
/* clear global NAKs */
val = DCTL_CGOUTNAK | DCTL_CGNPINNAK;
if (!is_usb_reset)
val |= DCTL_SFTDISCON;
- dwc2_set_bit(hsotg->regs + DCTL, val);
+ dwc2_set_bit(hsotg, DCTL, val);
/* configure the core to support LPM */
dwc2_gadget_init_lpm(hsotg);
+ /* program GREFCLK register if needed */
+ if (using_desc_dma(hsotg) && hsotg->params.service_interval)
+ dwc2_gadget_program_ref_clk(hsotg);
+
/* must be at-least 3ms to allow bus to see disconnect */
mdelay(3);
@@ -3387,20 +3451,20 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg,
dwc2_hsotg_enqueue_setup(hsotg);
dev_dbg(hsotg->dev, "EP0: DIEPCTL0=0x%08x, DOEPCTL0=0x%08x\n",
- dwc2_readl(hsotg->regs + DIEPCTL0),
- dwc2_readl(hsotg->regs + DOEPCTL0));
+ dwc2_readl(hsotg, DIEPCTL0),
+ dwc2_readl(hsotg, DOEPCTL0));
}
static void dwc2_hsotg_core_disconnect(struct dwc2_hsotg *hsotg)
{
/* set the soft-disconnect bit */
- dwc2_set_bit(hsotg->regs + DCTL, DCTL_SFTDISCON);
+ dwc2_set_bit(hsotg, DCTL, DCTL_SFTDISCON);
}
void dwc2_hsotg_core_connect(struct dwc2_hsotg *hsotg)
{
/* remove the soft-disconnect and let's go */
- dwc2_clear_bit(hsotg->regs + DCTL, DCTL_SFTDISCON);
+ dwc2_clear_bit(hsotg, DCTL, DCTL_SFTDISCON);
}
/**
@@ -3425,25 +3489,25 @@ static void dwc2_gadget_handle_incomplete_isoc_in(struct dwc2_hsotg *hsotg)
dev_dbg(hsotg->dev, "Incomplete isoc in interrupt received:\n");
- daintmsk = dwc2_readl(hsotg->regs + DAINTMSK);
+ daintmsk = dwc2_readl(hsotg, DAINTMSK);
- for (idx = 1; idx <= hsotg->num_of_eps; idx++) {
+ for (idx = 1; idx < hsotg->num_of_eps; idx++) {
hs_ep = hsotg->eps_in[idx];
/* Proceed only unmasked ISOC EPs */
- if (!hs_ep->isochronous || (BIT(idx) & ~daintmsk))
+ if ((BIT(idx) & ~daintmsk) || !hs_ep->isochronous)
continue;
- epctrl = dwc2_readl(hsotg->regs + DIEPCTL(idx));
+ epctrl = dwc2_readl(hsotg, DIEPCTL(idx));
if ((epctrl & DXEPCTL_EPENA) &&
dwc2_gadget_target_frame_elapsed(hs_ep)) {
epctrl |= DXEPCTL_SNAK;
epctrl |= DXEPCTL_EPDIS;
- dwc2_writel(epctrl, hsotg->regs + DIEPCTL(idx));
+ dwc2_writel(hsotg, epctrl, DIEPCTL(idx));
}
}
/* Clear interrupt */
- dwc2_writel(GINTSTS_INCOMPL_SOIN, hsotg->regs + GINTSTS);
+ dwc2_writel(hsotg, GINTSTS_INCOMPL_SOIN, GINTSTS);
}
/**
@@ -3470,33 +3534,33 @@ static void dwc2_gadget_handle_incomplete_isoc_out(struct dwc2_hsotg *hsotg)
dev_dbg(hsotg->dev, "%s: GINTSTS_INCOMPL_SOOUT\n", __func__);
- daintmsk = dwc2_readl(hsotg->regs + DAINTMSK);
+ daintmsk = dwc2_readl(hsotg, DAINTMSK);
daintmsk >>= DAINT_OUTEP_SHIFT;
- for (idx = 1; idx <= hsotg->num_of_eps; idx++) {
+ for (idx = 1; idx < hsotg->num_of_eps; idx++) {
hs_ep = hsotg->eps_out[idx];
/* Proceed only unmasked ISOC EPs */
- if (!hs_ep->isochronous || (BIT(idx) & ~daintmsk))
+ if ((BIT(idx) & ~daintmsk) || !hs_ep->isochronous)
continue;
- epctrl = dwc2_readl(hsotg->regs + DOEPCTL(idx));
+ epctrl = dwc2_readl(hsotg, DOEPCTL(idx));
if ((epctrl & DXEPCTL_EPENA) &&
dwc2_gadget_target_frame_elapsed(hs_ep)) {
/* Unmask GOUTNAKEFF interrupt */
- gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
+ gintmsk = dwc2_readl(hsotg, GINTMSK);
gintmsk |= GINTSTS_GOUTNAKEFF;
- dwc2_writel(gintmsk, hsotg->regs + GINTMSK);
+ dwc2_writel(hsotg, gintmsk, GINTMSK);
- gintsts = dwc2_readl(hsotg->regs + GINTSTS);
+ gintsts = dwc2_readl(hsotg, GINTSTS);
if (!(gintsts & GINTSTS_GOUTNAKEFF)) {
- dwc2_set_bit(hsotg->regs + DCTL, DCTL_SGOUTNAK);
+ dwc2_set_bit(hsotg, DCTL, DCTL_SGOUTNAK);
break;
}
}
}
/* Clear interrupt */
- dwc2_writel(GINTSTS_INCOMPL_SOOUT, hsotg->regs + GINTSTS);
+ dwc2_writel(hsotg, GINTSTS_INCOMPL_SOOUT, GINTSTS);
}
/**
@@ -3516,8 +3580,8 @@ static irqreturn_t dwc2_hsotg_irq(int irq, void *pw)
spin_lock(&hsotg->lock);
irq_retry:
- gintsts = dwc2_readl(hsotg->regs + GINTSTS);
- gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
+ gintsts = dwc2_readl(hsotg, GINTSTS);
+ gintmsk = dwc2_readl(hsotg, GINTMSK);
dev_dbg(hsotg->dev, "%s: %08x %08x (%08x) retry %d\n",
__func__, gintsts, gintsts & gintmsk, gintmsk, retry_count);
@@ -3527,7 +3591,7 @@ irq_retry:
if (gintsts & GINTSTS_RESETDET) {
dev_dbg(hsotg->dev, "%s: USBRstDet\n", __func__);
- dwc2_writel(GINTSTS_RESETDET, hsotg->regs + GINTSTS);
+ dwc2_writel(hsotg, GINTSTS_RESETDET, GINTSTS);
/* This event must be used only if controller is suspended */
if (hsotg->lx_state == DWC2_L2) {
@@ -3537,34 +3601,34 @@ irq_retry:
}
if (gintsts & (GINTSTS_USBRST | GINTSTS_RESETDET)) {
- u32 usb_status = dwc2_readl(hsotg->regs + GOTGCTL);
+ u32 usb_status = dwc2_readl(hsotg, GOTGCTL);
u32 connected = hsotg->connected;
dev_dbg(hsotg->dev, "%s: USBRst\n", __func__);
dev_dbg(hsotg->dev, "GNPTXSTS=%08x\n",
- dwc2_readl(hsotg->regs + GNPTXSTS));
+ dwc2_readl(hsotg, GNPTXSTS));
- dwc2_writel(GINTSTS_USBRST, hsotg->regs + GINTSTS);
+ dwc2_writel(hsotg, GINTSTS_USBRST, GINTSTS);
/* Report disconnection if it is not already done. */
dwc2_hsotg_disconnect(hsotg);
/* Reset device address to zero */
- dwc2_clear_bit(hsotg->regs + DCFG, DCFG_DEVADDR_MASK);
+ dwc2_clear_bit(hsotg, DCFG, DCFG_DEVADDR_MASK);
if (usb_status & GOTGCTL_BSESVLD && connected)
dwc2_hsotg_core_init_disconnected(hsotg, true);
}
if (gintsts & GINTSTS_ENUMDONE) {
- dwc2_writel(GINTSTS_ENUMDONE, hsotg->regs + GINTSTS);
+ dwc2_writel(hsotg, GINTSTS_ENUMDONE, GINTSTS);
dwc2_hsotg_irq_enumdone(hsotg);
}
if (gintsts & (GINTSTS_OEPINT | GINTSTS_IEPINT)) {
- u32 daint = dwc2_readl(hsotg->regs + DAINT);
- u32 daintmsk = dwc2_readl(hsotg->regs + DAINTMSK);
+ u32 daint = dwc2_readl(hsotg, DAINT);
+ u32 daintmsk = dwc2_readl(hsotg, DAINTMSK);
u32 daint_out, daint_in;
int ep;
@@ -3623,7 +3687,7 @@ irq_retry:
if (gintsts & GINTSTS_ERLYSUSP) {
dev_dbg(hsotg->dev, "GINTSTS_ErlySusp\n");
- dwc2_writel(GINTSTS_ERLYSUSP, hsotg->regs + GINTSTS);
+ dwc2_writel(hsotg, GINTSTS_ERLYSUSP, GINTSTS);
}
/*
@@ -3639,26 +3703,26 @@ irq_retry:
u32 daintmsk;
struct dwc2_hsotg_ep *hs_ep;
- daintmsk = dwc2_readl(hsotg->regs + DAINTMSK);
+ daintmsk = dwc2_readl(hsotg, DAINTMSK);
daintmsk >>= DAINT_OUTEP_SHIFT;
/* Mask this interrupt */
- gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
+ gintmsk = dwc2_readl(hsotg, GINTMSK);
gintmsk &= ~GINTSTS_GOUTNAKEFF;
- dwc2_writel(gintmsk, hsotg->regs + GINTMSK);
+ dwc2_writel(hsotg, gintmsk, GINTMSK);
dev_dbg(hsotg->dev, "GOUTNakEff triggered\n");
- for (idx = 1; idx <= hsotg->num_of_eps; idx++) {
+ for (idx = 1; idx < hsotg->num_of_eps; idx++) {
hs_ep = hsotg->eps_out[idx];
/* Proceed only unmasked ISOC EPs */
- if (!hs_ep->isochronous || (BIT(idx) & ~daintmsk))
+ if ((BIT(idx) & ~daintmsk) || !hs_ep->isochronous)
continue;
- epctrl = dwc2_readl(hsotg->regs + DOEPCTL(idx));
+ epctrl = dwc2_readl(hsotg, DOEPCTL(idx));
if (epctrl & DXEPCTL_EPENA) {
epctrl |= DXEPCTL_SNAK;
epctrl |= DXEPCTL_EPDIS;
- dwc2_writel(epctrl, hsotg->regs + DOEPCTL(idx));
+ dwc2_writel(hsotg, epctrl, DOEPCTL(idx));
}
}
@@ -3668,7 +3732,7 @@ irq_retry:
if (gintsts & GINTSTS_GINNAKEFF) {
dev_info(hsotg->dev, "GINNakEff triggered\n");
- dwc2_set_bit(hsotg->regs + DCTL, DCTL_CGNPINNAK);
+ dwc2_set_bit(hsotg, DCTL, DCTL_CGNPINNAK);
dwc2_hsotg_dump(hsotg);
}
@@ -3687,6 +3751,10 @@ irq_retry:
if (gintsts & IRQ_RETRY_MASK && --retry_count > 0)
goto irq_retry;
+ /* Check WKUP_ALERT interrupt*/
+ if (hsotg->params.service_interval)
+ dwc2_gadget_wkup_alert_handler(hsotg);
+
spin_unlock(&hsotg->lock);
return IRQ_HANDLED;
@@ -3708,7 +3776,7 @@ static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg,
if (hs_ep->dir_in) {
if (hsotg->dedicated_fifos || hs_ep->periodic) {
- dwc2_set_bit(hsotg->regs + epctrl_reg, DXEPCTL_SNAK);
+ dwc2_set_bit(hsotg, epctrl_reg, DXEPCTL_SNAK);
/* Wait for Nak effect */
if (dwc2_hsotg_wait_bit_set(hsotg, epint_reg,
DXEPINT_INEPNAKEFF, 100))
@@ -3716,7 +3784,7 @@ static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg,
"%s: timeout DIEPINT.NAKEFF\n",
__func__);
} else {
- dwc2_set_bit(hsotg->regs + DCTL, DCTL_SGNPINNAK);
+ dwc2_set_bit(hsotg, DCTL, DCTL_SGNPINNAK);
/* Wait for Nak effect */
if (dwc2_hsotg_wait_bit_set(hsotg, GINTSTS,
GINTSTS_GINNAKEFF, 100))
@@ -3725,8 +3793,8 @@ static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg,
__func__);
}
} else {
- if (!(dwc2_readl(hsotg->regs + GINTSTS) & GINTSTS_GOUTNAKEFF))
- dwc2_set_bit(hsotg->regs + DCTL, DCTL_SGOUTNAK);
+ if (!(dwc2_readl(hsotg, GINTSTS) & GINTSTS_GOUTNAKEFF))
+ dwc2_set_bit(hsotg, DCTL, DCTL_SGOUTNAK);
/* Wait for global nak to take effect */
if (dwc2_hsotg_wait_bit_set(hsotg, GINTSTS,
@@ -3736,7 +3804,7 @@ static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg,
}
/* Disable ep */
- dwc2_set_bit(hsotg->regs + epctrl_reg, DXEPCTL_EPDIS | DXEPCTL_SNAK);
+ dwc2_set_bit(hsotg, epctrl_reg, DXEPCTL_EPDIS | DXEPCTL_SNAK);
/* Wait for ep to be disabled */
if (dwc2_hsotg_wait_bit_set(hsotg, epint_reg, DXEPINT_EPDISBLD, 100))
@@ -3744,7 +3812,7 @@ static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg,
"%s: timeout DOEPCTL.EPDisable\n", __func__);
/* Clear EPDISBLD interrupt */
- dwc2_set_bit(hsotg->regs + epint_reg, DXEPINT_EPDISBLD);
+ dwc2_set_bit(hsotg, epint_reg, DXEPINT_EPDISBLD);
if (hs_ep->dir_in) {
unsigned short fifo_index;
@@ -3759,11 +3827,11 @@ static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg,
/* Clear Global In NP NAK in Shared FIFO for non periodic ep */
if (!hsotg->dedicated_fifos && !hs_ep->periodic)
- dwc2_set_bit(hsotg->regs + DCTL, DCTL_CGNPINNAK);
+ dwc2_set_bit(hsotg, DCTL, DCTL_CGNPINNAK);
} else {
/* Remove global NAKs */
- dwc2_set_bit(hsotg->regs + DCTL, DCTL_CGOUTNAK);
+ dwc2_set_bit(hsotg, DCTL, DCTL_CGOUTNAK);
}
}
@@ -3789,6 +3857,7 @@ static int dwc2_hsotg_ep_enable(struct usb_ep *ep,
unsigned int dir_in;
unsigned int i, val, size;
int ret = 0;
+ unsigned char ep_type;
dev_dbg(hsotg->dev,
"%s: ep %s: a 0x%02x, attr 0x%02x, mps 0x%04x, intr %d\n",
@@ -3807,13 +3876,30 @@ static int dwc2_hsotg_ep_enable(struct usb_ep *ep,
return -EINVAL;
}
+ ep_type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
mps = usb_endpoint_maxp(desc);
mc = usb_endpoint_maxp_mult(desc);
+ /* ISOC IN in DDMA supported bInterval up to 10 */
+ if (using_desc_dma(hsotg) && ep_type == USB_ENDPOINT_XFER_ISOC &&
+ dir_in && desc->bInterval > 10) {
+ dev_err(hsotg->dev,
+ "%s: ISOC IN, DDMA: bInterval>10 not supported!\n", __func__);
+ return -EINVAL;
+ }
+
+ /* High bandwidth ISOC OUT in DDMA not supported */
+ if (using_desc_dma(hsotg) && ep_type == USB_ENDPOINT_XFER_ISOC &&
+ !dir_in && mc > 1) {
+ dev_err(hsotg->dev,
+ "%s: ISOC OUT, DDMA: HB not supported!\n", __func__);
+ return -EINVAL;
+ }
+
/* note, we handle this here instead of dwc2_hsotg_set_ep_maxpacket */
epctrl_reg = dir_in ? DIEPCTL(index) : DOEPCTL(index);
- epctrl = dwc2_readl(hsotg->regs + epctrl_reg);
+ epctrl = dwc2_readl(hsotg, epctrl_reg);
dev_dbg(hsotg->dev, "%s: read DxEPCTL=0x%08x from 0x%08x\n",
__func__, epctrl, epctrl_reg);
@@ -3850,24 +3936,24 @@ static int dwc2_hsotg_ep_enable(struct usb_ep *ep,
hs_ep->halted = 0;
hs_ep->interval = desc->bInterval;
- switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
+ switch (ep_type) {
case USB_ENDPOINT_XFER_ISOC:
epctrl |= DXEPCTL_EPTYPE_ISO;
epctrl |= DXEPCTL_SETEVENFR;
hs_ep->isochronous = 1;
hs_ep->interval = 1 << (desc->bInterval - 1);
hs_ep->target_frame = TARGET_FRAME_INITIAL;
- hs_ep->isoc_chain_num = 0;
hs_ep->next_desc = 0;
+ hs_ep->compl_desc = 0;
if (dir_in) {
hs_ep->periodic = 1;
- mask = dwc2_readl(hsotg->regs + DIEPMSK);
+ mask = dwc2_readl(hsotg, DIEPMSK);
mask |= DIEPMSK_NAKMSK;
- dwc2_writel(mask, hsotg->regs + DIEPMSK);
+ dwc2_writel(hsotg, mask, DIEPMSK);
} else {
- mask = dwc2_readl(hsotg->regs + DOEPMSK);
+ mask = dwc2_readl(hsotg, DOEPMSK);
mask |= DOEPMSK_OUTTKNEPDISMSK;
- dwc2_writel(mask, hsotg->regs + DOEPMSK);
+ dwc2_writel(hsotg, mask, DOEPMSK);
}
break;
@@ -3902,7 +3988,7 @@ static int dwc2_hsotg_ep_enable(struct usb_ep *ep,
for (i = 1; i < hsotg->num_of_eps; ++i) {
if (hsotg->fifo_map & (1 << i))
continue;
- val = dwc2_readl(hsotg->regs + DPTXFSIZN(i));
+ val = dwc2_readl(hsotg, DPTXFSIZN(i));
val = (val >> FIFOSIZE_DEPTH_SHIFT) * 4;
if (val < size)
continue;
@@ -3940,7 +4026,7 @@ static int dwc2_hsotg_ep_enable(struct usb_ep *ep,
* to 4.00a (including both). Also for FS_IOT_1.00a
* and HS_IOT_1.00a.
*/
- u32 gsnpsid = dwc2_readl(hsotg->regs + GSNPSID);
+ u32 gsnpsid = dwc2_readl(hsotg, GSNPSID);
if ((gsnpsid >= DWC2_CORE_REV_2_72a &&
gsnpsid <= DWC2_CORE_REV_4_00a) ||
@@ -3952,9 +4038,9 @@ static int dwc2_hsotg_ep_enable(struct usb_ep *ep,
dev_dbg(hsotg->dev, "%s: write DxEPCTL=0x%08x\n",
__func__, epctrl);
- dwc2_writel(epctrl, hsotg->regs + epctrl_reg);
+ dwc2_writel(hsotg, epctrl, epctrl_reg);
dev_dbg(hsotg->dev, "%s: read DxEPCTL=0x%08x\n",
- __func__, dwc2_readl(hsotg->regs + epctrl_reg));
+ __func__, dwc2_readl(hsotg, epctrl_reg));
/* enable the endpoint interrupt */
dwc2_hsotg_ctrl_epint(hsotg, index, dir_in, 1);
@@ -3986,6 +4072,7 @@ static int dwc2_hsotg_ep_disable(struct usb_ep *ep)
unsigned long flags;
u32 epctrl_reg;
u32 ctrl;
+ int locked;
dev_dbg(hsotg->dev, "%s(ep %p)\n", __func__, ep);
@@ -4001,9 +4088,11 @@ static int dwc2_hsotg_ep_disable(struct usb_ep *ep)
epctrl_reg = dir_in ? DIEPCTL(index) : DOEPCTL(index);
- spin_lock_irqsave(&hsotg->lock, flags);
+ locked = spin_is_locked(&hsotg->lock);
+ if (!locked)
+ spin_lock_irqsave(&hsotg->lock, flags);
- ctrl = dwc2_readl(hsotg->regs + epctrl_reg);
+ ctrl = dwc2_readl(hsotg, epctrl_reg);
if (ctrl & DXEPCTL_EPENA)
dwc2_hsotg_ep_stop_xfr(hsotg, hs_ep);
@@ -4013,7 +4102,7 @@ static int dwc2_hsotg_ep_disable(struct usb_ep *ep)
ctrl |= DXEPCTL_SNAK;
dev_dbg(hsotg->dev, "%s: DxEPCTL=0x%08x\n", __func__, ctrl);
- dwc2_writel(ctrl, hsotg->regs + epctrl_reg);
+ dwc2_writel(hsotg, ctrl, epctrl_reg);
/* disable endpoint interrupts */
dwc2_hsotg_ctrl_epint(hsotg, hs_ep->index, hs_ep->dir_in, 0);
@@ -4025,7 +4114,9 @@ static int dwc2_hsotg_ep_disable(struct usb_ep *ep)
hs_ep->fifo_index = 0;
hs_ep->fifo_size = 0;
- spin_unlock_irqrestore(&hsotg->lock, flags);
+ if (!locked)
+ spin_unlock_irqrestore(&hsotg->lock, flags);
+
return 0;
}
@@ -4120,7 +4211,7 @@ static int dwc2_hsotg_ep_sethalt(struct usb_ep *ep, int value, bool now)
if (hs_ep->dir_in) {
epreg = DIEPCTL(index);
- epctl = dwc2_readl(hs->regs + epreg);
+ epctl = dwc2_readl(hs, epreg);
if (value) {
epctl |= DXEPCTL_STALL | DXEPCTL_SNAK;
@@ -4133,10 +4224,10 @@ static int dwc2_hsotg_ep_sethalt(struct usb_ep *ep, int value, bool now)
xfertype == DXEPCTL_EPTYPE_INTERRUPT)
epctl |= DXEPCTL_SETD0PID;
}
- dwc2_writel(epctl, hs->regs + epreg);
+ dwc2_writel(hs, epctl, epreg);
} else {
epreg = DOEPCTL(index);
- epctl = dwc2_readl(hs->regs + epreg);
+ epctl = dwc2_readl(hs, epreg);
if (value) {
epctl |= DXEPCTL_STALL;
@@ -4147,7 +4238,7 @@ static int dwc2_hsotg_ep_sethalt(struct usb_ep *ep, int value, bool now)
xfertype == DXEPCTL_EPTYPE_INTERRUPT)
epctl |= DXEPCTL_SETD0PID;
}
- dwc2_writel(epctl, hs->regs + epreg);
+ dwc2_writel(hs, epctl, epreg);
}
hs_ep->halted = value;
@@ -4195,29 +4286,29 @@ static void dwc2_hsotg_init(struct dwc2_hsotg *hsotg)
u32 usbcfg;
/* unmask subset of endpoint interrupts */
- dwc2_writel(DIEPMSK_TIMEOUTMSK | DIEPMSK_AHBERRMSK |
+ dwc2_writel(hsotg, DIEPMSK_TIMEOUTMSK | DIEPMSK_AHBERRMSK |
DIEPMSK_EPDISBLDMSK | DIEPMSK_XFERCOMPLMSK,
- hsotg->regs + DIEPMSK);
+ DIEPMSK);
- dwc2_writel(DOEPMSK_SETUPMSK | DOEPMSK_AHBERRMSK |
+ dwc2_writel(hsotg, DOEPMSK_SETUPMSK | DOEPMSK_AHBERRMSK |
DOEPMSK_EPDISBLDMSK | DOEPMSK_XFERCOMPLMSK,
- hsotg->regs + DOEPMSK);
+ DOEPMSK);
- dwc2_writel(0, hsotg->regs + DAINTMSK);
+ dwc2_writel(hsotg, 0, DAINTMSK);
/* Be in disconnected state until gadget is registered */
- dwc2_set_bit(hsotg->regs + DCTL, DCTL_SFTDISCON);
+ dwc2_set_bit(hsotg, DCTL, DCTL_SFTDISCON);
/* setup fifos */
dev_dbg(hsotg->dev, "GRXFSIZ=0x%08x, GNPTXFSIZ=0x%08x\n",
- dwc2_readl(hsotg->regs + GRXFSIZ),
- dwc2_readl(hsotg->regs + GNPTXFSIZ));
+ dwc2_readl(hsotg, GRXFSIZ),
+ dwc2_readl(hsotg, GNPTXFSIZ));
dwc2_hsotg_init_fifo(hsotg);
/* keep other bits untouched (so e.g. forced modes are not lost) */
- usbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
+ usbcfg = dwc2_readl(hsotg, GUSBCFG);
usbcfg &= ~(GUSBCFG_TOUTCAL_MASK | GUSBCFG_PHYIF16 | GUSBCFG_SRPCAP |
GUSBCFG_HNPCAP | GUSBCFG_USBTRDTIM_MASK);
@@ -4225,10 +4316,10 @@ static void dwc2_hsotg_init(struct dwc2_hsotg *hsotg)
trdtim = (hsotg->phyif == GUSBCFG_PHYIF8) ? 9 : 5;
usbcfg |= hsotg->phyif | GUSBCFG_TOUTCAL(7) |
(trdtim << GUSBCFG_USBTRDTIM_SHIFT);
- dwc2_writel(usbcfg, hsotg->regs + GUSBCFG);
+ dwc2_writel(hsotg, usbcfg, GUSBCFG);
if (using_dma(hsotg))
- dwc2_set_bit(hsotg->regs + GAHBCFG, GAHBCFG_DMA_EN);
+ dwc2_set_bit(hsotg, GAHBCFG, GAHBCFG_DMA_EN);
}
/**
@@ -4301,7 +4392,6 @@ err:
/**
* dwc2_hsotg_udc_stop - stop the udc
* @gadget: The usb gadget state
- * @driver: The usb gadget driver
*
* Stop udc hw block and stay tunned for future transmissions
*/
@@ -4453,6 +4543,7 @@ static const struct usb_gadget_ops dwc2_hsotg_gadget_ops = {
* @hsotg: The device state.
* @hs_ep: The endpoint to be initialised.
* @epnum: The endpoint number
+ * @dir_in: True if direction is in.
*
* Initialise the given endpoint (as part of the probe and device state
* creation) to give to the gadget driver. Setup the endpoint name, any
@@ -4518,15 +4609,15 @@ static void dwc2_hsotg_initep(struct dwc2_hsotg *hsotg,
u32 next = DXEPCTL_NEXTEP((epnum + 1) % 15);
if (dir_in)
- dwc2_writel(next, hsotg->regs + DIEPCTL(epnum));
+ dwc2_writel(hsotg, next, DIEPCTL(epnum));
else
- dwc2_writel(next, hsotg->regs + DOEPCTL(epnum));
+ dwc2_writel(hsotg, next, DOEPCTL(epnum));
}
}
/**
* dwc2_hsotg_hw_cfg - read HW configuration registers
- * @param: The device state
+ * @hsotg: Programming view of the DWC_otg controller
*
* Read the USB core HW configuration registers
*/
@@ -4582,30 +4673,30 @@ static int dwc2_hsotg_hw_cfg(struct dwc2_hsotg *hsotg)
/**
* dwc2_hsotg_dump - dump state of the udc
- * @param: The device state
+ * @hsotg: Programming view of the DWC_otg controller
+ *
*/
static void dwc2_hsotg_dump(struct dwc2_hsotg *hsotg)
{
#ifdef DEBUG
struct device *dev = hsotg->dev;
- void __iomem *regs = hsotg->regs;
u32 val;
int idx;
dev_info(dev, "DCFG=0x%08x, DCTL=0x%08x, DIEPMSK=%08x\n",
- dwc2_readl(regs + DCFG), dwc2_readl(regs + DCTL),
- dwc2_readl(regs + DIEPMSK));
+ dwc2_readl(hsotg, DCFG), dwc2_readl(hsotg, DCTL),
+ dwc2_readl(hsotg, DIEPMSK));
dev_info(dev, "GAHBCFG=0x%08x, GHWCFG1=0x%08x\n",
- dwc2_readl(regs + GAHBCFG), dwc2_readl(regs + GHWCFG1));
+ dwc2_readl(hsotg, GAHBCFG), dwc2_readl(hsotg, GHWCFG1));
dev_info(dev, "GRXFSIZ=0x%08x, GNPTXFSIZ=0x%08x\n",
- dwc2_readl(regs + GRXFSIZ), dwc2_readl(regs + GNPTXFSIZ));
+ dwc2_readl(hsotg, GRXFSIZ), dwc2_readl(hsotg, GNPTXFSIZ));
/* show periodic fifo settings */
for (idx = 1; idx < hsotg->num_of_eps; idx++) {
- val = dwc2_readl(regs + DPTXFSIZN(idx));
+ val = dwc2_readl(hsotg, DPTXFSIZN(idx));
dev_info(dev, "DPTx[%d] FSize=%d, StAddr=0x%08x\n", idx,
val >> FIFOSIZE_DEPTH_SHIFT,
val & FIFOSIZE_STARTADDR_MASK);
@@ -4614,26 +4705,27 @@ static void dwc2_hsotg_dump(struct dwc2_hsotg *hsotg)
for (idx = 0; idx < hsotg->num_of_eps; idx++) {
dev_info(dev,
"ep%d-in: EPCTL=0x%08x, SIZ=0x%08x, DMA=0x%08x\n", idx,
- dwc2_readl(regs + DIEPCTL(idx)),
- dwc2_readl(regs + DIEPTSIZ(idx)),
- dwc2_readl(regs + DIEPDMA(idx)));
+ dwc2_readl(hsotg, DIEPCTL(idx)),
+ dwc2_readl(hsotg, DIEPTSIZ(idx)),
+ dwc2_readl(hsotg, DIEPDMA(idx)));
- val = dwc2_readl(regs + DOEPCTL(idx));
+ val = dwc2_readl(hsotg, DOEPCTL(idx));
dev_info(dev,
"ep%d-out: EPCTL=0x%08x, SIZ=0x%08x, DMA=0x%08x\n",
- idx, dwc2_readl(regs + DOEPCTL(idx)),
- dwc2_readl(regs + DOEPTSIZ(idx)),
- dwc2_readl(regs + DOEPDMA(idx)));
+ idx, dwc2_readl(hsotg, DOEPCTL(idx)),
+ dwc2_readl(hsotg, DOEPTSIZ(idx)),
+ dwc2_readl(hsotg, DOEPDMA(idx)));
}
dev_info(dev, "DVBUSDIS=0x%08x, DVBUSPULSE=%08x\n",
- dwc2_readl(regs + DVBUSDIS), dwc2_readl(regs + DVBUSPULSE));
+ dwc2_readl(hsotg, DVBUSDIS), dwc2_readl(hsotg, DVBUSPULSE));
#endif
}
/**
* dwc2_gadget_init - init function for gadget
- * @dwc2: The data structure for the DWC2 driver.
+ * @hsotg: Programming view of the DWC_otg controller
+ *
*/
int dwc2_gadget_init(struct dwc2_hsotg *hsotg)
{
@@ -4720,9 +4812,11 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg)
}
ret = usb_add_gadget_udc(dev, &hsotg->gadget);
- if (ret)
+ if (ret) {
+ dwc2_hsotg_ep_free_request(&hsotg->eps_out[0]->ep,
+ hsotg->ctrl_req);
return ret;
-
+ }
dwc2_hsotg_dump(hsotg);
return 0;
@@ -4730,11 +4824,13 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg)
/**
* dwc2_hsotg_remove - remove function for hsotg driver
- * @pdev: The platform information for the driver
+ * @hsotg: Programming view of the DWC_otg controller
+ *
*/
int dwc2_hsotg_remove(struct dwc2_hsotg *hsotg)
{
usb_del_gadget_udc(&hsotg->gadget);
+ dwc2_hsotg_ep_free_request(&hsotg->eps_out[0]->ep, hsotg->ctrl_req);
return 0;
}
@@ -4811,15 +4907,15 @@ int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg)
/* Backup dev regs */
dr = &hsotg->dr_backup;
- dr->dcfg = dwc2_readl(hsotg->regs + DCFG);
- dr->dctl = dwc2_readl(hsotg->regs + DCTL);
- dr->daintmsk = dwc2_readl(hsotg->regs + DAINTMSK);
- dr->diepmsk = dwc2_readl(hsotg->regs + DIEPMSK);
- dr->doepmsk = dwc2_readl(hsotg->regs + DOEPMSK);
+ dr->dcfg = dwc2_readl(hsotg, DCFG);
+ dr->dctl = dwc2_readl(hsotg, DCTL);
+ dr->daintmsk = dwc2_readl(hsotg, DAINTMSK);
+ dr->diepmsk = dwc2_readl(hsotg, DIEPMSK);
+ dr->doepmsk = dwc2_readl(hsotg, DOEPMSK);
for (i = 0; i < hsotg->num_of_eps; i++) {
/* Backup IN EPs */
- dr->diepctl[i] = dwc2_readl(hsotg->regs + DIEPCTL(i));
+ dr->diepctl[i] = dwc2_readl(hsotg, DIEPCTL(i));
/* Ensure DATA PID is correctly configured */
if (dr->diepctl[i] & DXEPCTL_DPID)
@@ -4827,11 +4923,11 @@ int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg)
else
dr->diepctl[i] |= DXEPCTL_SETD0PID;
- dr->dieptsiz[i] = dwc2_readl(hsotg->regs + DIEPTSIZ(i));
- dr->diepdma[i] = dwc2_readl(hsotg->regs + DIEPDMA(i));
+ dr->dieptsiz[i] = dwc2_readl(hsotg, DIEPTSIZ(i));
+ dr->diepdma[i] = dwc2_readl(hsotg, DIEPDMA(i));
/* Backup OUT EPs */
- dr->doepctl[i] = dwc2_readl(hsotg->regs + DOEPCTL(i));
+ dr->doepctl[i] = dwc2_readl(hsotg, DOEPCTL(i));
/* Ensure DATA PID is correctly configured */
if (dr->doepctl[i] & DXEPCTL_DPID)
@@ -4839,9 +4935,9 @@ int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg)
else
dr->doepctl[i] |= DXEPCTL_SETD0PID;
- dr->doeptsiz[i] = dwc2_readl(hsotg->regs + DOEPTSIZ(i));
- dr->doepdma[i] = dwc2_readl(hsotg->regs + DOEPDMA(i));
- dr->dtxfsiz[i] = dwc2_readl(hsotg->regs + DPTXFSIZN(i));
+ dr->doeptsiz[i] = dwc2_readl(hsotg, DOEPTSIZ(i));
+ dr->doepdma[i] = dwc2_readl(hsotg, DOEPDMA(i));
+ dr->dtxfsiz[i] = dwc2_readl(hsotg, DPTXFSIZN(i));
}
dr->valid = true;
return 0;
@@ -4874,17 +4970,17 @@ int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg, int remote_wakeup)
dr->valid = false;
if (!remote_wakeup)
- dwc2_writel(dr->dctl, hsotg->regs + DCTL);
+ dwc2_writel(hsotg, dr->dctl, DCTL);
- dwc2_writel(dr->daintmsk, hsotg->regs + DAINTMSK);
- dwc2_writel(dr->diepmsk, hsotg->regs + DIEPMSK);
- dwc2_writel(dr->doepmsk, hsotg->regs + DOEPMSK);
+ dwc2_writel(hsotg, dr->daintmsk, DAINTMSK);
+ dwc2_writel(hsotg, dr->diepmsk, DIEPMSK);
+ dwc2_writel(hsotg, dr->doepmsk, DOEPMSK);
for (i = 0; i < hsotg->num_of_eps; i++) {
/* Restore IN EPs */
- dwc2_writel(dr->dieptsiz[i], hsotg->regs + DIEPTSIZ(i));
- dwc2_writel(dr->diepdma[i], hsotg->regs + DIEPDMA(i));
- dwc2_writel(dr->doeptsiz[i], hsotg->regs + DOEPTSIZ(i));
+ dwc2_writel(hsotg, dr->dieptsiz[i], DIEPTSIZ(i));
+ dwc2_writel(hsotg, dr->diepdma[i], DIEPDMA(i));
+ dwc2_writel(hsotg, dr->doeptsiz[i], DOEPTSIZ(i));
/** WA for enabled EPx's IN in DDMA mode. On entering to
* hibernation wrong value read and saved from DIEPDMAx,
* as result BNA interrupt asserted on hibernation exit
@@ -4893,10 +4989,10 @@ int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg, int remote_wakeup)
if (hsotg->params.g_dma_desc &&
(dr->diepctl[i] & DXEPCTL_EPENA))
dr->diepdma[i] = hsotg->eps_in[i]->desc_list_dma;
- dwc2_writel(dr->dtxfsiz[i], hsotg->regs + DPTXFSIZN(i));
- dwc2_writel(dr->diepctl[i], hsotg->regs + DIEPCTL(i));
+ dwc2_writel(hsotg, dr->dtxfsiz[i], DPTXFSIZN(i));
+ dwc2_writel(hsotg, dr->diepctl[i], DIEPCTL(i));
/* Restore OUT EPs */
- dwc2_writel(dr->doeptsiz[i], hsotg->regs + DOEPTSIZ(i));
+ dwc2_writel(hsotg, dr->doeptsiz[i], DOEPTSIZ(i));
/* WA for enabled EPx's OUT in DDMA mode. On entering to
* hibernation wrong value read and saved from DOEPDMAx,
* as result BNA interrupt asserted on hibernation exit
@@ -4905,8 +5001,8 @@ int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg, int remote_wakeup)
if (hsotg->params.g_dma_desc &&
(dr->doepctl[i] & DXEPCTL_EPENA))
dr->doepdma[i] = hsotg->eps_out[i]->desc_list_dma;
- dwc2_writel(dr->doepdma[i], hsotg->regs + DOEPDMA(i));
- dwc2_writel(dr->doepctl[i], hsotg->regs + DOEPCTL(i));
+ dwc2_writel(hsotg, dr->doepdma[i], DOEPDMA(i));
+ dwc2_writel(hsotg, dr->doepctl[i], DOEPCTL(i));
}
return 0;
@@ -4930,9 +5026,31 @@ void dwc2_gadget_init_lpm(struct dwc2_hsotg *hsotg)
val |= hsotg->params.lpm_clock_gating ? GLPMCFG_ENBLSLPM : 0;
val |= hsotg->params.hird_threshold << GLPMCFG_HIRD_THRES_SHIFT;
val |= hsotg->params.besl ? GLPMCFG_ENBESL : 0;
- dwc2_writel(val, hsotg->regs + GLPMCFG);
- dev_dbg(hsotg->dev, "GLPMCFG=0x%08x\n", dwc2_readl(hsotg->regs
- + GLPMCFG));
+ dwc2_writel(hsotg, val, GLPMCFG);
+ dev_dbg(hsotg->dev, "GLPMCFG=0x%08x\n", dwc2_readl(hsotg, GLPMCFG));
+
+ /* Unmask WKUP_ALERT Interrupt */
+ if (hsotg->params.service_interval)
+ dwc2_set_bit(hsotg, GINTMSK2, GINTMSK2_WKUP_ALERT_INT_MSK);
+}
+
+/**
+ * dwc2_gadget_program_ref_clk - Program GREFCLK register in device mode
+ *
+ * @hsotg: Programming view of DWC_otg controller
+ *
+ */
+void dwc2_gadget_program_ref_clk(struct dwc2_hsotg *hsotg)
+{
+ u32 val = 0;
+
+ val |= GREFCLK_REF_CLK_MODE;
+ val |= hsotg->params.ref_clk_per << GREFCLK_REFCLKPER_SHIFT;
+ val |= hsotg->params.sof_cnt_wkup_alert <<
+ GREFCLK_SOF_CNT_WKUP_ALERT_SHIFT;
+
+ dwc2_writel(hsotg, val, GREFCLK);
+ dev_dbg(hsotg->dev, "GREFCLK=0x%08x\n", dwc2_readl(hsotg, GREFCLK));
}
/**
@@ -4965,40 +5083,40 @@ int dwc2_gadget_enter_hibernation(struct dwc2_hsotg *hsotg)
gpwrdn = GPWRDN_PWRDNRSTN;
gpwrdn |= GPWRDN_PMUACTV;
- dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ dwc2_writel(hsotg, gpwrdn, GPWRDN);
udelay(10);
/* Set flag to indicate that we are in hibernation */
hsotg->hibernated = 1;
/* Enable interrupts from wake up logic */
- gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn = dwc2_readl(hsotg, GPWRDN);
gpwrdn |= GPWRDN_PMUINTSEL;
- dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ dwc2_writel(hsotg, gpwrdn, GPWRDN);
udelay(10);
/* Unmask device mode interrupts in GPWRDN */
- gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn = dwc2_readl(hsotg, GPWRDN);
gpwrdn |= GPWRDN_RST_DET_MSK;
gpwrdn |= GPWRDN_LNSTSCHG_MSK;
gpwrdn |= GPWRDN_STS_CHGINT_MSK;
- dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ dwc2_writel(hsotg, gpwrdn, GPWRDN);
udelay(10);
/* Enable Power Down Clamp */
- gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn = dwc2_readl(hsotg, GPWRDN);
gpwrdn |= GPWRDN_PWRDNCLMP;
- dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ dwc2_writel(hsotg, gpwrdn, GPWRDN);
udelay(10);
/* Switch off VDD */
- gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn = dwc2_readl(hsotg, GPWRDN);
gpwrdn |= GPWRDN_PWRDNSWTCH;
- dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ dwc2_writel(hsotg, gpwrdn, GPWRDN);
udelay(10);
/* Save gpwrdn register for further usage if stschng interrupt */
- hsotg->gr_backup.gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ hsotg->gr_backup.gpwrdn = dwc2_readl(hsotg, GPWRDN);
dev_dbg(hsotg->dev, "Hibernation completed\n");
return ret;
@@ -5011,7 +5129,7 @@ int dwc2_gadget_enter_hibernation(struct dwc2_hsotg *hsotg)
*
* @hsotg: Programming view of the DWC_otg controller
* @rem_wakeup: indicates whether resume is initiated by Device or Host.
- * @param reset: indicates whether resume is initiated by Reset.
+ * @reset: indicates whether resume is initiated by Reset.
*
* Return non-zero if failed to exit from hibernation.
*/
@@ -5040,46 +5158,46 @@ int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg,
if (!reset) {
/* Clear all pending interupts */
- dwc2_writel(0xffffffff, hsotg->regs + GINTSTS);
+ dwc2_writel(hsotg, 0xffffffff, GINTSTS);
}
/* De-assert Restore */
- gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn = dwc2_readl(hsotg, GPWRDN);
gpwrdn &= ~GPWRDN_RESTORE;
- dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ dwc2_writel(hsotg, gpwrdn, GPWRDN);
udelay(10);
if (!rem_wakeup) {
- pcgcctl = dwc2_readl(hsotg->regs + PCGCTL);
+ pcgcctl = dwc2_readl(hsotg, PCGCTL);
pcgcctl &= ~PCGCTL_RSTPDWNMODULE;
- dwc2_writel(pcgcctl, hsotg->regs + PCGCTL);
+ dwc2_writel(hsotg, pcgcctl, PCGCTL);
}
/* Restore GUSBCFG, DCFG and DCTL */
- dwc2_writel(gr->gusbcfg, hsotg->regs + GUSBCFG);
- dwc2_writel(dr->dcfg, hsotg->regs + DCFG);
- dwc2_writel(dr->dctl, hsotg->regs + DCTL);
+ dwc2_writel(hsotg, gr->gusbcfg, GUSBCFG);
+ dwc2_writel(hsotg, dr->dcfg, DCFG);
+ dwc2_writel(hsotg, dr->dctl, DCTL);
/* De-assert Wakeup Logic */
- gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn = dwc2_readl(hsotg, GPWRDN);
gpwrdn &= ~GPWRDN_PMUACTV;
- dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ dwc2_writel(hsotg, gpwrdn, GPWRDN);
if (rem_wakeup) {
udelay(10);
/* Start Remote Wakeup Signaling */
- dwc2_writel(dr->dctl | DCTL_RMTWKUPSIG, hsotg->regs + DCTL);
+ dwc2_writel(hsotg, dr->dctl | DCTL_RMTWKUPSIG, DCTL);
} else {
udelay(50);
/* Set Device programming done bit */
- dctl = dwc2_readl(hsotg->regs + DCTL);
+ dctl = dwc2_readl(hsotg, DCTL);
dctl |= DCTL_PWRONPRGDONE;
- dwc2_writel(dctl, hsotg->regs + DCTL);
+ dwc2_writel(hsotg, dctl, DCTL);
}
/* Wait for interrupts which must be cleared */
mdelay(2);
/* Clear all pending interupts */
- dwc2_writel(0xffffffff, hsotg->regs + GINTSTS);
+ dwc2_writel(hsotg, 0xffffffff, GINTSTS);
/* Restore global registers */
ret = dwc2_restore_global_registers(hsotg);
@@ -5099,9 +5217,9 @@ int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg,
if (rem_wakeup) {
mdelay(10);
- dctl = dwc2_readl(hsotg->regs + DCTL);
+ dctl = dwc2_readl(hsotg, DCTL);
dctl &= ~DCTL_RMTWKUPSIG;
- dwc2_writel(dctl, hsotg->regs + DCTL);
+ dwc2_writel(hsotg, dctl, DCTL);
}
hsotg->hibernated = 0;
diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index c51b73b3e048..dd82fa516f3f 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -75,10 +75,10 @@ static void dwc2_enable_common_interrupts(struct dwc2_hsotg *hsotg)
u32 intmsk;
/* Clear any pending OTG Interrupts */
- dwc2_writel(0xffffffff, hsotg->regs + GOTGINT);
+ dwc2_writel(hsotg, 0xffffffff, GOTGINT);
/* Clear any pending interrupts */
- dwc2_writel(0xffffffff, hsotg->regs + GINTSTS);
+ dwc2_writel(hsotg, 0xffffffff, GINTSTS);
/* Enable the interrupts in the GINTMSK */
intmsk = GINTSTS_MODEMIS | GINTSTS_OTGINT;
@@ -94,7 +94,7 @@ static void dwc2_enable_common_interrupts(struct dwc2_hsotg *hsotg)
if (dwc2_is_device_mode(hsotg) && hsotg->params.lpm)
intmsk |= GINTSTS_LPMTRANRCVD;
- dwc2_writel(intmsk, hsotg->regs + GINTMSK);
+ dwc2_writel(hsotg, intmsk, GINTMSK);
}
/*
@@ -117,10 +117,10 @@ static void dwc2_init_fs_ls_pclk_sel(struct dwc2_hsotg *hsotg)
}
dev_dbg(hsotg->dev, "Initializing HCFG.FSLSPClkSel to %08x\n", val);
- hcfg = dwc2_readl(hsotg->regs + HCFG);
+ hcfg = dwc2_readl(hsotg, HCFG);
hcfg &= ~HCFG_FSLSPCLKSEL_MASK;
hcfg |= val << HCFG_FSLSPCLKSEL_SHIFT;
- dwc2_writel(hcfg, hsotg->regs + HCFG);
+ dwc2_writel(hsotg, hcfg, HCFG);
}
static int dwc2_fs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
@@ -135,10 +135,10 @@ static int dwc2_fs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
if (select_phy) {
dev_dbg(hsotg->dev, "FS PHY selected\n");
- usbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
+ usbcfg = dwc2_readl(hsotg, GUSBCFG);
if (!(usbcfg & GUSBCFG_PHYSEL)) {
usbcfg |= GUSBCFG_PHYSEL;
- dwc2_writel(usbcfg, hsotg->regs + GUSBCFG);
+ dwc2_writel(hsotg, usbcfg, GUSBCFG);
/* Reset after a PHY select */
retval = dwc2_core_reset(hsotg, false);
@@ -151,7 +151,7 @@ static int dwc2_fs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
}
if (hsotg->params.activate_stm_fs_transceiver) {
- ggpio = dwc2_readl(hsotg->regs + GGPIO);
+ ggpio = dwc2_readl(hsotg, GGPIO);
if (!(ggpio & GGPIO_STM32_OTG_GCCFG_PWRDWN)) {
dev_dbg(hsotg->dev, "Activating transceiver\n");
/*
@@ -159,7 +159,7 @@ static int dwc2_fs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
* core configuration register.
*/
ggpio |= GGPIO_STM32_OTG_GCCFG_PWRDWN;
- dwc2_writel(ggpio, hsotg->regs + GGPIO);
+ dwc2_writel(hsotg, ggpio, GGPIO);
}
}
}
@@ -176,18 +176,18 @@ static int dwc2_fs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
dev_dbg(hsotg->dev, "FS PHY enabling I2C\n");
/* Program GUSBCFG.OtgUtmiFsSel to I2C */
- usbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
+ usbcfg = dwc2_readl(hsotg, GUSBCFG);
usbcfg |= GUSBCFG_OTG_UTMI_FS_SEL;
- dwc2_writel(usbcfg, hsotg->regs + GUSBCFG);
+ dwc2_writel(hsotg, usbcfg, GUSBCFG);
/* Program GI2CCTL.I2CEn */
- i2cctl = dwc2_readl(hsotg->regs + GI2CCTL);
+ i2cctl = dwc2_readl(hsotg, GI2CCTL);
i2cctl &= ~GI2CCTL_I2CDEVADDR_MASK;
i2cctl |= 1 << GI2CCTL_I2CDEVADDR_SHIFT;
i2cctl &= ~GI2CCTL_I2CEN;
- dwc2_writel(i2cctl, hsotg->regs + GI2CCTL);
+ dwc2_writel(hsotg, i2cctl, GI2CCTL);
i2cctl |= GI2CCTL_I2CEN;
- dwc2_writel(i2cctl, hsotg->regs + GI2CCTL);
+ dwc2_writel(hsotg, i2cctl, GI2CCTL);
}
return retval;
@@ -201,7 +201,7 @@ static int dwc2_hs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
if (!select_phy)
return 0;
- usbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
+ usbcfg = dwc2_readl(hsotg, GUSBCFG);
usbcfg_old = usbcfg;
/*
@@ -236,7 +236,7 @@ static int dwc2_hs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
}
if (usbcfg != usbcfg_old) {
- dwc2_writel(usbcfg, hsotg->regs + GUSBCFG);
+ dwc2_writel(hsotg, usbcfg, GUSBCFG);
/* Reset after setting the PHY parameters */
retval = dwc2_core_reset(hsotg, false);
@@ -273,15 +273,15 @@ static int dwc2_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED &&
hsotg->params.ulpi_fs_ls) {
dev_dbg(hsotg->dev, "Setting ULPI FSLS\n");
- usbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
+ usbcfg = dwc2_readl(hsotg, GUSBCFG);
usbcfg |= GUSBCFG_ULPI_FS_LS;
usbcfg |= GUSBCFG_ULPI_CLK_SUSP_M;
- dwc2_writel(usbcfg, hsotg->regs + GUSBCFG);
+ dwc2_writel(hsotg, usbcfg, GUSBCFG);
} else {
- usbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
+ usbcfg = dwc2_readl(hsotg, GUSBCFG);
usbcfg &= ~GUSBCFG_ULPI_FS_LS;
usbcfg &= ~GUSBCFG_ULPI_CLK_SUSP_M;
- dwc2_writel(usbcfg, hsotg->regs + GUSBCFG);
+ dwc2_writel(hsotg, usbcfg, GUSBCFG);
}
return retval;
@@ -289,7 +289,7 @@ static int dwc2_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
static int dwc2_gahbcfg_init(struct dwc2_hsotg *hsotg)
{
- u32 ahbcfg = dwc2_readl(hsotg->regs + GAHBCFG);
+ u32 ahbcfg = dwc2_readl(hsotg, GAHBCFG);
switch (hsotg->hw_params.arch) {
case GHWCFG2_EXT_DMA_ARCH:
@@ -316,7 +316,7 @@ static int dwc2_gahbcfg_init(struct dwc2_hsotg *hsotg)
else
hsotg->params.dma_desc_enable = false;
- dwc2_writel(ahbcfg, hsotg->regs + GAHBCFG);
+ dwc2_writel(hsotg, ahbcfg, GAHBCFG);
return 0;
}
@@ -325,7 +325,7 @@ static void dwc2_gusbcfg_init(struct dwc2_hsotg *hsotg)
{
u32 usbcfg;
- usbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
+ usbcfg = dwc2_readl(hsotg, GUSBCFG);
usbcfg &= ~(GUSBCFG_HNPCAP | GUSBCFG_SRPCAP);
switch (hsotg->hw_params.op_mode) {
@@ -353,21 +353,15 @@ static void dwc2_gusbcfg_init(struct dwc2_hsotg *hsotg)
break;
}
- dwc2_writel(usbcfg, hsotg->regs + GUSBCFG);
+ dwc2_writel(hsotg, usbcfg, GUSBCFG);
}
static int dwc2_vbus_supply_init(struct dwc2_hsotg *hsotg)
{
- int ret;
-
- hsotg->vbus_supply = devm_regulator_get_optional(hsotg->dev, "vbus");
- if (IS_ERR(hsotg->vbus_supply)) {
- ret = PTR_ERR(hsotg->vbus_supply);
- hsotg->vbus_supply = NULL;
- return ret == -ENODEV ? 0 : ret;
- }
+ if (hsotg->vbus_supply)
+ return regulator_enable(hsotg->vbus_supply);
- return regulator_enable(hsotg->vbus_supply);
+ return 0;
}
static int dwc2_vbus_supply_exit(struct dwc2_hsotg *hsotg)
@@ -390,16 +384,16 @@ static void dwc2_enable_host_interrupts(struct dwc2_hsotg *hsotg)
dev_dbg(hsotg->dev, "%s()\n", __func__);
/* Disable all interrupts */
- dwc2_writel(0, hsotg->regs + GINTMSK);
- dwc2_writel(0, hsotg->regs + HAINTMSK);
+ dwc2_writel(hsotg, 0, GINTMSK);
+ dwc2_writel(hsotg, 0, HAINTMSK);
/* Enable the common interrupts */
dwc2_enable_common_interrupts(hsotg);
/* Enable host mode interrupts without disturbing common interrupts */
- intmsk = dwc2_readl(hsotg->regs + GINTMSK);
+ intmsk = dwc2_readl(hsotg, GINTMSK);
intmsk |= GINTSTS_DISCONNINT | GINTSTS_PRTINT | GINTSTS_HCHINT;
- dwc2_writel(intmsk, hsotg->regs + GINTMSK);
+ dwc2_writel(hsotg, intmsk, GINTMSK);
}
/**
@@ -409,12 +403,12 @@ static void dwc2_enable_host_interrupts(struct dwc2_hsotg *hsotg)
*/
static void dwc2_disable_host_interrupts(struct dwc2_hsotg *hsotg)
{
- u32 intmsk = dwc2_readl(hsotg->regs + GINTMSK);
+ u32 intmsk = dwc2_readl(hsotg, GINTMSK);
/* Disable host mode interrupts without disturbing common interrupts */
intmsk &= ~(GINTSTS_SOF | GINTSTS_PRTINT | GINTSTS_HCHINT |
GINTSTS_PTXFEMP | GINTSTS_NPTXFEMP | GINTSTS_DISCONNINT);
- dwc2_writel(intmsk, hsotg->regs + GINTMSK);
+ dwc2_writel(hsotg, intmsk, GINTMSK);
}
/*
@@ -494,37 +488,37 @@ static void dwc2_config_fifos(struct dwc2_hsotg *hsotg)
dwc2_calculate_dynamic_fifo(hsotg);
/* Rx FIFO */
- grxfsiz = dwc2_readl(hsotg->regs + GRXFSIZ);
+ grxfsiz = dwc2_readl(hsotg, GRXFSIZ);
dev_dbg(hsotg->dev, "initial grxfsiz=%08x\n", grxfsiz);
grxfsiz &= ~GRXFSIZ_DEPTH_MASK;
grxfsiz |= params->host_rx_fifo_size <<
GRXFSIZ_DEPTH_SHIFT & GRXFSIZ_DEPTH_MASK;
- dwc2_writel(grxfsiz, hsotg->regs + GRXFSIZ);
+ dwc2_writel(hsotg, grxfsiz, GRXFSIZ);
dev_dbg(hsotg->dev, "new grxfsiz=%08x\n",
- dwc2_readl(hsotg->regs + GRXFSIZ));
+ dwc2_readl(hsotg, GRXFSIZ));
/* Non-periodic Tx FIFO */
dev_dbg(hsotg->dev, "initial gnptxfsiz=%08x\n",
- dwc2_readl(hsotg->regs + GNPTXFSIZ));
+ dwc2_readl(hsotg, GNPTXFSIZ));
nptxfsiz = params->host_nperio_tx_fifo_size <<
FIFOSIZE_DEPTH_SHIFT & FIFOSIZE_DEPTH_MASK;
nptxfsiz |= params->host_rx_fifo_size <<
FIFOSIZE_STARTADDR_SHIFT & FIFOSIZE_STARTADDR_MASK;
- dwc2_writel(nptxfsiz, hsotg->regs + GNPTXFSIZ);
+ dwc2_writel(hsotg, nptxfsiz, GNPTXFSIZ);
dev_dbg(hsotg->dev, "new gnptxfsiz=%08x\n",
- dwc2_readl(hsotg->regs + GNPTXFSIZ));
+ dwc2_readl(hsotg, GNPTXFSIZ));
/* Periodic Tx FIFO */
dev_dbg(hsotg->dev, "initial hptxfsiz=%08x\n",
- dwc2_readl(hsotg->regs + HPTXFSIZ));
+ dwc2_readl(hsotg, HPTXFSIZ));
hptxfsiz = params->host_perio_tx_fifo_size <<
FIFOSIZE_DEPTH_SHIFT & FIFOSIZE_DEPTH_MASK;
hptxfsiz |= (params->host_rx_fifo_size +
params->host_nperio_tx_fifo_size) <<
FIFOSIZE_STARTADDR_SHIFT & FIFOSIZE_STARTADDR_MASK;
- dwc2_writel(hptxfsiz, hsotg->regs + HPTXFSIZ);
+ dwc2_writel(hsotg, hptxfsiz, HPTXFSIZ);
dev_dbg(hsotg->dev, "new hptxfsiz=%08x\n",
- dwc2_readl(hsotg->regs + HPTXFSIZ));
+ dwc2_readl(hsotg, HPTXFSIZ));
if (hsotg->params.en_multiple_tx_fifo &&
hsotg->hw_params.snpsid >= DWC2_CORE_REV_2_91a) {
@@ -533,14 +527,14 @@ static void dwc2_config_fifos(struct dwc2_hsotg *hsotg)
* Global DFIFOCFG calculation for Host mode -
* include RxFIFO, NPTXFIFO and HPTXFIFO
*/
- dfifocfg = dwc2_readl(hsotg->regs + GDFIFOCFG);
+ dfifocfg = dwc2_readl(hsotg, GDFIFOCFG);
dfifocfg &= ~GDFIFOCFG_EPINFOBASE_MASK;
dfifocfg |= (params->host_rx_fifo_size +
params->host_nperio_tx_fifo_size +
params->host_perio_tx_fifo_size) <<
GDFIFOCFG_EPINFOBASE_SHIFT &
GDFIFOCFG_EPINFOBASE_MASK;
- dwc2_writel(dfifocfg, hsotg->regs + GDFIFOCFG);
+ dwc2_writel(hsotg, dfifocfg, GDFIFOCFG);
}
}
@@ -560,8 +554,8 @@ u32 dwc2_calc_frame_interval(struct dwc2_hsotg *hsotg)
u32 hprt0;
int clock = 60; /* default value */
- usbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
- hprt0 = dwc2_readl(hsotg->regs + HPRT0);
+ usbcfg = dwc2_readl(hsotg, GUSBCFG);
+ hprt0 = dwc2_readl(hsotg, HPRT0);
if (!(usbcfg & GUSBCFG_PHYSEL) && (usbcfg & GUSBCFG_ULPI_UTMI_SEL) &&
!(usbcfg & GUSBCFG_PHYIF16))
@@ -597,13 +591,12 @@ u32 dwc2_calc_frame_interval(struct dwc2_hsotg *hsotg)
* dwc2_read_packet() - Reads a packet from the Rx FIFO into the destination
* buffer
*
- * @core_if: Programming view of DWC_otg controller
+ * @hsotg: Programming view of DWC_otg controller
* @dest: Destination buffer for the packet
* @bytes: Number of bytes to copy to the destination
*/
void dwc2_read_packet(struct dwc2_hsotg *hsotg, u8 *dest, u16 bytes)
{
- u32 __iomem *fifo = hsotg->regs + HCFIFO(0);
u32 *data_buf = (u32 *)dest;
int word_count = (bytes + 3) / 4;
int i;
@@ -617,7 +610,7 @@ void dwc2_read_packet(struct dwc2_hsotg *hsotg, u8 *dest, u16 bytes)
dev_vdbg(hsotg->dev, "%s(%p,%p,%d)\n", __func__, hsotg, dest, bytes);
for (i = 0; i < word_count; i++, data_buf++)
- *data_buf = dwc2_readl(fifo);
+ *data_buf = dwc2_readl(hsotg, HCFIFO(0));
}
/**
@@ -646,10 +639,10 @@ static void dwc2_dump_channel_info(struct dwc2_hsotg *hsotg,
if (!chan)
return;
- hcchar = dwc2_readl(hsotg->regs + HCCHAR(chan->hc_num));
- hcsplt = dwc2_readl(hsotg->regs + HCSPLT(chan->hc_num));
- hctsiz = dwc2_readl(hsotg->regs + HCTSIZ(chan->hc_num));
- hc_dma = dwc2_readl(hsotg->regs + HCDMA(chan->hc_num));
+ hcchar = dwc2_readl(hsotg, HCCHAR(chan->hc_num));
+ hcsplt = dwc2_readl(hsotg, HCSPLT(chan->hc_num));
+ hctsiz = dwc2_readl(hsotg, HCTSIZ(chan->hc_num));
+ hc_dma = dwc2_readl(hsotg, HCDMA(chan->hc_num));
dev_dbg(hsotg->dev, " Assigned to channel %p:\n", chan);
dev_dbg(hsotg->dev, " hcchar 0x%08x, hcsplt 0x%08x\n",
@@ -797,7 +790,7 @@ static void dwc2_hc_enable_slave_ints(struct dwc2_hsotg *hsotg,
break;
}
- dwc2_writel(hcintmsk, hsotg->regs + HCINTMSK(chan->hc_num));
+ dwc2_writel(hsotg, hcintmsk, HCINTMSK(chan->hc_num));
if (dbg_hc(chan))
dev_vdbg(hsotg->dev, "set HCINTMSK to %08x\n", hcintmsk);
}
@@ -834,7 +827,7 @@ static void dwc2_hc_enable_dma_ints(struct dwc2_hsotg *hsotg,
}
}
- dwc2_writel(hcintmsk, hsotg->regs + HCINTMSK(chan->hc_num));
+ dwc2_writel(hsotg, hcintmsk, HCINTMSK(chan->hc_num));
if (dbg_hc(chan))
dev_vdbg(hsotg->dev, "set HCINTMSK to %08x\n", hcintmsk);
}
@@ -855,16 +848,16 @@ static void dwc2_hc_enable_ints(struct dwc2_hsotg *hsotg,
}
/* Enable the top level host channel interrupt */
- intmsk = dwc2_readl(hsotg->regs + HAINTMSK);
+ intmsk = dwc2_readl(hsotg, HAINTMSK);
intmsk |= 1 << chan->hc_num;
- dwc2_writel(intmsk, hsotg->regs + HAINTMSK);
+ dwc2_writel(hsotg, intmsk, HAINTMSK);
if (dbg_hc(chan))
dev_vdbg(hsotg->dev, "set HAINTMSK to %08x\n", intmsk);
/* Make sure host channel interrupts are enabled */
- intmsk = dwc2_readl(hsotg->regs + GINTMSK);
+ intmsk = dwc2_readl(hsotg, GINTMSK);
intmsk |= GINTSTS_HCHINT;
- dwc2_writel(intmsk, hsotg->regs + GINTMSK);
+ dwc2_writel(hsotg, intmsk, GINTMSK);
if (dbg_hc(chan))
dev_vdbg(hsotg->dev, "set GINTMSK to %08x\n", intmsk);
}
@@ -893,7 +886,7 @@ static void dwc2_hc_init(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan)
/* Clear old interrupt conditions for this host channel */
hcintmsk = 0xffffffff;
hcintmsk &= ~HCINTMSK_RESERVED14_31;
- dwc2_writel(hcintmsk, hsotg->regs + HCINT(hc_num));
+ dwc2_writel(hsotg, hcintmsk, HCINT(hc_num));
/* Enable channel interrupts required for this transfer */
dwc2_hc_enable_ints(hsotg, chan);
@@ -910,7 +903,7 @@ static void dwc2_hc_init(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan)
hcchar |= HCCHAR_LSPDDEV;
hcchar |= chan->ep_type << HCCHAR_EPTYPE_SHIFT & HCCHAR_EPTYPE_MASK;
hcchar |= chan->max_packet << HCCHAR_MPS_SHIFT & HCCHAR_MPS_MASK;
- dwc2_writel(hcchar, hsotg->regs + HCCHAR(hc_num));
+ dwc2_writel(hsotg, hcchar, HCCHAR(hc_num));
if (dbg_hc(chan)) {
dev_vdbg(hsotg->dev, "set HCCHAR(%d) to %08x\n",
hc_num, hcchar);
@@ -964,7 +957,7 @@ static void dwc2_hc_init(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan)
}
}
- dwc2_writel(hcsplt, hsotg->regs + HCSPLT(hc_num));
+ dwc2_writel(hsotg, hcsplt, HCSPLT(hc_num));
}
/**
@@ -1034,14 +1027,14 @@ void dwc2_hc_halt(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan,
u32 hcintmsk = HCINTMSK_CHHLTD;
dev_vdbg(hsotg->dev, "dequeue/error\n");
- dwc2_writel(hcintmsk, hsotg->regs + HCINTMSK(chan->hc_num));
+ dwc2_writel(hsotg, hcintmsk, HCINTMSK(chan->hc_num));
/*
* Make sure no other interrupts besides halt are currently
* pending. Handling another interrupt could cause a crash due
* to the QTD and QH state.
*/
- dwc2_writel(~hcintmsk, hsotg->regs + HCINT(chan->hc_num));
+ dwc2_writel(hsotg, ~hcintmsk, HCINT(chan->hc_num));
/*
* Make sure the halt status is set to URB_DEQUEUE or AHB_ERR
@@ -1050,7 +1043,7 @@ void dwc2_hc_halt(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan,
*/
chan->halt_status = halt_status;
- hcchar = dwc2_readl(hsotg->regs + HCCHAR(chan->hc_num));
+ hcchar = dwc2_readl(hsotg, HCCHAR(chan->hc_num));
if (!(hcchar & HCCHAR_CHENA)) {
/*
* The channel is either already halted or it hasn't
@@ -1078,7 +1071,7 @@ void dwc2_hc_halt(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan,
return;
}
- hcchar = dwc2_readl(hsotg->regs + HCCHAR(chan->hc_num));
+ hcchar = dwc2_readl(hsotg, HCCHAR(chan->hc_num));
/* No need to set the bit in DDMA for disabling the channel */
/* TODO check it everywhere channel is disabled */
@@ -1101,7 +1094,7 @@ void dwc2_hc_halt(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan,
if (chan->ep_type == USB_ENDPOINT_XFER_CONTROL ||
chan->ep_type == USB_ENDPOINT_XFER_BULK) {
dev_vdbg(hsotg->dev, "control/bulk\n");
- nptxsts = dwc2_readl(hsotg->regs + GNPTXSTS);
+ nptxsts = dwc2_readl(hsotg, GNPTXSTS);
if ((nptxsts & TXSTS_QSPCAVAIL_MASK) == 0) {
dev_vdbg(hsotg->dev, "Disabling channel\n");
hcchar &= ~HCCHAR_CHENA;
@@ -1109,7 +1102,7 @@ void dwc2_hc_halt(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan,
} else {
if (dbg_perio())
dev_vdbg(hsotg->dev, "isoc/intr\n");
- hptxsts = dwc2_readl(hsotg->regs + HPTXSTS);
+ hptxsts = dwc2_readl(hsotg, HPTXSTS);
if ((hptxsts & TXSTS_QSPCAVAIL_MASK) == 0 ||
hsotg->queuing_high_bandwidth) {
if (dbg_perio())
@@ -1122,7 +1115,7 @@ void dwc2_hc_halt(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan,
dev_vdbg(hsotg->dev, "DMA enabled\n");
}
- dwc2_writel(hcchar, hsotg->regs + HCCHAR(chan->hc_num));
+ dwc2_writel(hsotg, hcchar, HCCHAR(chan->hc_num));
chan->halt_status = halt_status;
if (hcchar & HCCHAR_CHENA) {
@@ -1171,10 +1164,10 @@ void dwc2_hc_cleanup(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan)
* Clear channel interrupt enables and any unhandled channel interrupt
* conditions
*/
- dwc2_writel(0, hsotg->regs + HCINTMSK(chan->hc_num));
+ dwc2_writel(hsotg, 0, HCINTMSK(chan->hc_num));
hcintmsk = 0xffffffff;
hcintmsk &= ~HCINTMSK_RESERVED14_31;
- dwc2_writel(hcintmsk, hsotg->regs + HCINT(chan->hc_num));
+ dwc2_writel(hsotg, hcintmsk, HCINT(chan->hc_num));
}
/**
@@ -1228,7 +1221,7 @@ static void dwc2_hc_set_even_odd_frame(struct dwc2_hsotg *hsotg,
!chan->do_split) ? chan->speed : USB_SPEED_HIGH;
/* See how many bytes are in the periodic FIFO right now */
- fifo_space = (dwc2_readl(hsotg->regs + HPTXSTS) &
+ fifo_space = (dwc2_readl(hsotg, HPTXSTS) &
TXSTS_FSPCAVAIL_MASK) >> TXSTS_FSPCAVAIL_SHIFT;
bytes_in_fifo = sizeof(u32) *
(hsotg->params.host_perio_tx_fifo_size -
@@ -1329,14 +1322,11 @@ static void dwc2_hc_write_packet(struct dwc2_hsotg *hsotg,
u32 remaining_count;
u32 byte_count;
u32 dword_count;
- u32 __iomem *data_fifo;
u32 *data_buf = (u32 *)chan->xfer_buf;
if (dbg_hc(chan))
dev_vdbg(hsotg->dev, "%s()\n", __func__);
- data_fifo = (u32 __iomem *)(hsotg->regs + HCFIFO(chan->hc_num));
-
remaining_count = chan->xfer_len - chan->xfer_count;
if (remaining_count > chan->max_packet)
byte_count = chan->max_packet;
@@ -1348,13 +1338,13 @@ static void dwc2_hc_write_packet(struct dwc2_hsotg *hsotg,
if (((unsigned long)data_buf & 0x3) == 0) {
/* xfer_buf is DWORD aligned */
for (i = 0; i < dword_count; i++, data_buf++)
- dwc2_writel(*data_buf, data_fifo);
+ dwc2_writel(hsotg, *data_buf, HCFIFO(chan->hc_num));
} else {
/* xfer_buf is not DWORD aligned */
for (i = 0; i < dword_count; i++, data_buf++) {
u32 data = data_buf[0] | data_buf[1] << 8 |
data_buf[2] << 16 | data_buf[3] << 24;
- dwc2_writel(data, data_fifo);
+ dwc2_writel(hsotg, data, HCFIFO(chan->hc_num));
}
}
@@ -1383,12 +1373,12 @@ static void dwc2_hc_do_ping(struct dwc2_hsotg *hsotg,
hctsiz = TSIZ_DOPNG;
hctsiz |= 1 << TSIZ_PKTCNT_SHIFT;
- dwc2_writel(hctsiz, hsotg->regs + HCTSIZ(chan->hc_num));
+ dwc2_writel(hsotg, hctsiz, HCTSIZ(chan->hc_num));
- hcchar = dwc2_readl(hsotg->regs + HCCHAR(chan->hc_num));
+ hcchar = dwc2_readl(hsotg, HCCHAR(chan->hc_num));
hcchar |= HCCHAR_CHENA;
hcchar &= ~HCCHAR_CHDIS;
- dwc2_writel(hcchar, hsotg->regs + HCCHAR(chan->hc_num));
+ dwc2_writel(hsotg, hcchar, HCCHAR(chan->hc_num));
}
/**
@@ -1548,7 +1538,7 @@ static void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg,
hctsiz |= num_packets << TSIZ_PKTCNT_SHIFT & TSIZ_PKTCNT_MASK;
hctsiz |= chan->data_pid_start << TSIZ_SC_MC_PID_SHIFT &
TSIZ_SC_MC_PID_MASK;
- dwc2_writel(hctsiz, hsotg->regs + HCTSIZ(chan->hc_num));
+ dwc2_writel(hsotg, hctsiz, HCTSIZ(chan->hc_num));
if (dbg_hc(chan)) {
dev_vdbg(hsotg->dev, "Wrote %08x to HCTSIZ(%d)\n",
hctsiz, chan->hc_num);
@@ -1567,22 +1557,31 @@ static void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg,
}
if (hsotg->params.host_dma) {
- dwc2_writel((u32)chan->xfer_dma,
- hsotg->regs + HCDMA(chan->hc_num));
+ dma_addr_t dma_addr;
+
+ if (chan->align_buf) {
+ if (dbg_hc(chan))
+ dev_vdbg(hsotg->dev, "align_buf\n");
+ dma_addr = chan->align_buf;
+ } else {
+ dma_addr = chan->xfer_dma;
+ }
+ dwc2_writel(hsotg, (u32)dma_addr, HCDMA(chan->hc_num));
+
if (dbg_hc(chan))
dev_vdbg(hsotg->dev, "Wrote %08lx to HCDMA(%d)\n",
- (unsigned long)chan->xfer_dma, chan->hc_num);
+ (unsigned long)dma_addr, chan->hc_num);
}
/* Start the split */
if (chan->do_split) {
- u32 hcsplt = dwc2_readl(hsotg->regs + HCSPLT(chan->hc_num));
+ u32 hcsplt = dwc2_readl(hsotg, HCSPLT(chan->hc_num));
hcsplt |= HCSPLT_SPLTENA;
- dwc2_writel(hcsplt, hsotg->regs + HCSPLT(chan->hc_num));
+ dwc2_writel(hsotg, hcsplt, HCSPLT(chan->hc_num));
}
- hcchar = dwc2_readl(hsotg->regs + HCCHAR(chan->hc_num));
+ hcchar = dwc2_readl(hsotg, HCCHAR(chan->hc_num));
hcchar &= ~HCCHAR_MULTICNT_MASK;
hcchar |= (ec_mc << HCCHAR_MULTICNT_SHIFT) & HCCHAR_MULTICNT_MASK;
dwc2_hc_set_even_odd_frame(hsotg, chan, &hcchar);
@@ -1601,7 +1600,7 @@ static void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg,
(hcchar & HCCHAR_MULTICNT_MASK) >>
HCCHAR_MULTICNT_SHIFT);
- dwc2_writel(hcchar, hsotg->regs + HCCHAR(chan->hc_num));
+ dwc2_writel(hsotg, hcchar, HCCHAR(chan->hc_num));
if (dbg_hc(chan))
dev_vdbg(hsotg->dev, "Wrote %08x to HCCHAR(%d)\n", hcchar,
chan->hc_num);
@@ -1659,18 +1658,18 @@ void dwc2_hc_start_transfer_ddma(struct dwc2_hsotg *hsotg,
dev_vdbg(hsotg->dev, " NTD: %d\n", chan->ntd - 1);
}
- dwc2_writel(hctsiz, hsotg->regs + HCTSIZ(chan->hc_num));
+ dwc2_writel(hsotg, hctsiz, HCTSIZ(chan->hc_num));
dma_sync_single_for_device(hsotg->dev, chan->desc_list_addr,
chan->desc_list_sz, DMA_TO_DEVICE);
- dwc2_writel(chan->desc_list_addr, hsotg->regs + HCDMA(chan->hc_num));
+ dwc2_writel(hsotg, chan->desc_list_addr, HCDMA(chan->hc_num));
if (dbg_hc(chan))
dev_vdbg(hsotg->dev, "Wrote %pad to HCDMA(%d)\n",
&chan->desc_list_addr, chan->hc_num);
- hcchar = dwc2_readl(hsotg->regs + HCCHAR(chan->hc_num));
+ hcchar = dwc2_readl(hsotg, HCCHAR(chan->hc_num));
hcchar &= ~HCCHAR_MULTICNT_MASK;
hcchar |= chan->multi_count << HCCHAR_MULTICNT_SHIFT &
HCCHAR_MULTICNT_MASK;
@@ -1689,7 +1688,7 @@ void dwc2_hc_start_transfer_ddma(struct dwc2_hsotg *hsotg,
(hcchar & HCCHAR_MULTICNT_MASK) >>
HCCHAR_MULTICNT_SHIFT);
- dwc2_writel(hcchar, hsotg->regs + HCCHAR(chan->hc_num));
+ dwc2_writel(hsotg, hcchar, HCCHAR(chan->hc_num));
if (dbg_hc(chan))
dev_vdbg(hsotg->dev, "Wrote %08x to HCCHAR(%d)\n", hcchar,
chan->hc_num);
@@ -1746,7 +1745,7 @@ static int dwc2_hc_continue_transfer(struct dwc2_hsotg *hsotg,
* transfer completes, the extra requests for the channel will
* be flushed.
*/
- u32 hcchar = dwc2_readl(hsotg->regs + HCCHAR(chan->hc_num));
+ u32 hcchar = dwc2_readl(hsotg, HCCHAR(chan->hc_num));
dwc2_hc_set_even_odd_frame(hsotg, chan, &hcchar);
hcchar |= HCCHAR_CHENA;
@@ -1754,7 +1753,7 @@ static int dwc2_hc_continue_transfer(struct dwc2_hsotg *hsotg,
if (dbg_hc(chan))
dev_vdbg(hsotg->dev, " IN xfer: hcchar = 0x%08x\n",
hcchar);
- dwc2_writel(hcchar, hsotg->regs + HCCHAR(chan->hc_num));
+ dwc2_writel(hsotg, hcchar, HCCHAR(chan->hc_num));
chan->requests++;
return 1;
}
@@ -1764,7 +1763,7 @@ static int dwc2_hc_continue_transfer(struct dwc2_hsotg *hsotg,
if (chan->xfer_count < chan->xfer_len) {
if (chan->ep_type == USB_ENDPOINT_XFER_INT ||
chan->ep_type == USB_ENDPOINT_XFER_ISOC) {
- u32 hcchar = dwc2_readl(hsotg->regs +
+ u32 hcchar = dwc2_readl(hsotg,
HCCHAR(chan->hc_num));
dwc2_hc_set_even_odd_frame(hsotg, chan,
@@ -1878,7 +1877,7 @@ void dwc2_hcd_start(struct dwc2_hsotg *hsotg)
*/
hprt0 = dwc2_read_hprt0(hsotg);
hprt0 |= HPRT0_RST;
- dwc2_writel(hprt0, hsotg->regs + HPRT0);
+ dwc2_writel(hsotg, hprt0, HPRT0);
}
queue_delayed_work(hsotg->wq_otg, &hsotg->start_work,
@@ -1899,11 +1898,11 @@ static void dwc2_hcd_cleanup_channels(struct dwc2_hsotg *hsotg)
channel = hsotg->hc_ptr_array[i];
if (!list_empty(&channel->hc_list_entry))
continue;
- hcchar = dwc2_readl(hsotg->regs + HCCHAR(i));
+ hcchar = dwc2_readl(hsotg, HCCHAR(i));
if (hcchar & HCCHAR_CHENA) {
hcchar &= ~(HCCHAR_CHENA | HCCHAR_EPDIR);
hcchar |= HCCHAR_CHDIS;
- dwc2_writel(hcchar, hsotg->regs + HCCHAR(i));
+ dwc2_writel(hsotg, hcchar, HCCHAR(i));
}
}
}
@@ -1912,11 +1911,11 @@ static void dwc2_hcd_cleanup_channels(struct dwc2_hsotg *hsotg)
channel = hsotg->hc_ptr_array[i];
if (!list_empty(&channel->hc_list_entry))
continue;
- hcchar = dwc2_readl(hsotg->regs + HCCHAR(i));
+ hcchar = dwc2_readl(hsotg, HCCHAR(i));
if (hcchar & HCCHAR_CHENA) {
/* Halt the channel */
hcchar |= HCCHAR_CHDIS;
- dwc2_writel(hcchar, hsotg->regs + HCCHAR(i));
+ dwc2_writel(hsotg, hcchar, HCCHAR(i));
}
dwc2_hc_cleanup(hsotg, channel);
@@ -1976,11 +1975,11 @@ void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force)
* interrupt mask and status bits and disabling subsequent host
* channel interrupts.
*/
- intr = dwc2_readl(hsotg->regs + GINTMSK);
+ intr = dwc2_readl(hsotg, GINTMSK);
intr &= ~(GINTSTS_NPTXFEMP | GINTSTS_PTXFEMP | GINTSTS_HCHINT);
- dwc2_writel(intr, hsotg->regs + GINTMSK);
+ dwc2_writel(hsotg, intr, GINTMSK);
intr = GINTSTS_NPTXFEMP | GINTSTS_PTXFEMP | GINTSTS_HCHINT;
- dwc2_writel(intr, hsotg->regs + GINTSTS);
+ dwc2_writel(hsotg, intr, GINTSTS);
/*
* Turn off the vbus power only if the core has transitioned to device
@@ -1990,7 +1989,7 @@ void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force)
if (dwc2_is_device_mode(hsotg)) {
if (hsotg->op_state != OTG_STATE_A_SUSPEND) {
dev_dbg(hsotg->dev, "Disconnect: PortPower off\n");
- dwc2_writel(0, hsotg->regs + HPRT0);
+ dwc2_writel(hsotg, 0, HPRT0);
}
dwc2_disable_host_interrupts(hsotg);
@@ -2018,7 +2017,7 @@ void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force)
* and won't get any future interrupts to handle the connect.
*/
if (!force) {
- hprt0 = dwc2_readl(hsotg->regs + HPRT0);
+ hprt0 = dwc2_readl(hsotg, HPRT0);
if (!(hprt0 & HPRT0_CONNDET) && (hprt0 & HPRT0_CONNSTS))
dwc2_hcd_connect(hsotg);
}
@@ -2062,7 +2061,7 @@ void dwc2_hcd_stop(struct dwc2_hsotg *hsotg)
/* Turn off the vbus power */
dev_dbg(hsotg->dev, "PortPower off\n");
- dwc2_writel(0, hsotg->regs + HPRT0);
+ dwc2_writel(hsotg, 0, HPRT0);
}
/* Caller must hold driver lock */
@@ -2086,7 +2085,7 @@ static int dwc2_hcd_urb_enqueue(struct dwc2_hsotg *hsotg,
if ((dev_speed == USB_SPEED_LOW) &&
(hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED) &&
(hsotg->hw_params.hs_phy_type == GHWCFG2_HS_PHY_TYPE_UTMI)) {
- u32 hprt0 = dwc2_readl(hsotg->regs + HPRT0);
+ u32 hprt0 = dwc2_readl(hsotg, HPRT0);
u32 prtspd = (hprt0 & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT;
if (prtspd == HPRT0_SPD_FULL_SPEED)
@@ -2105,7 +2104,7 @@ static int dwc2_hcd_urb_enqueue(struct dwc2_hsotg *hsotg,
return retval;
}
- intr_mask = dwc2_readl(hsotg->regs + GINTMSK);
+ intr_mask = dwc2_readl(hsotg, GINTMSK);
if (!(intr_mask & GINTSTS_SOF)) {
enum dwc2_transaction_type tr_type;
@@ -2270,7 +2269,7 @@ int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup)
dev_dbg(hsotg->dev, "%s(%p)\n", __func__, hsotg);
- usbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
+ usbcfg = dwc2_readl(hsotg, GUSBCFG);
/* Set ULPI External VBUS bit if needed */
usbcfg &= ~GUSBCFG_ULPI_EXT_VBUS_DRV;
@@ -2282,7 +2281,7 @@ int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup)
if (hsotg->params.ts_dline)
usbcfg |= GUSBCFG_TERMSELDLPULSE;
- dwc2_writel(usbcfg, hsotg->regs + GUSBCFG);
+ dwc2_writel(hsotg, usbcfg, GUSBCFG);
/*
* Reset the Controller
@@ -2316,9 +2315,9 @@ int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup)
dwc2_gusbcfg_init(hsotg);
/* Program the GOTGCTL register */
- otgctl = dwc2_readl(hsotg->regs + GOTGCTL);
+ otgctl = dwc2_readl(hsotg, GOTGCTL);
otgctl &= ~GOTGCTL_OTGVER;
- dwc2_writel(otgctl, hsotg->regs + GOTGCTL);
+ dwc2_writel(hsotg, otgctl, GOTGCTL);
/* Clear the SRP success bit for FS-I2c */
hsotg->srp_success = 0;
@@ -2365,20 +2364,20 @@ static void dwc2_core_host_init(struct dwc2_hsotg *hsotg)
* introduced by the PHY in generating the linestate condition
* can vary from one PHY to another.
*/
- usbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
+ usbcfg = dwc2_readl(hsotg, GUSBCFG);
usbcfg |= GUSBCFG_TOUTCAL(7);
- dwc2_writel(usbcfg, hsotg->regs + GUSBCFG);
+ dwc2_writel(hsotg, usbcfg, GUSBCFG);
/* Restart the Phy Clock */
- dwc2_writel(0, hsotg->regs + PCGCTL);
+ dwc2_writel(hsotg, 0, PCGCTL);
/* Initialize Host Configuration Register */
dwc2_init_fs_ls_pclk_sel(hsotg);
if (hsotg->params.speed == DWC2_SPEED_PARAM_FULL ||
hsotg->params.speed == DWC2_SPEED_PARAM_LOW) {
- hcfg = dwc2_readl(hsotg->regs + HCFG);
+ hcfg = dwc2_readl(hsotg, HCFG);
hcfg |= HCFG_FSLSSUPP;
- dwc2_writel(hcfg, hsotg->regs + HCFG);
+ dwc2_writel(hsotg, hcfg, HCFG);
}
/*
@@ -2387,9 +2386,9 @@ static void dwc2_core_host_init(struct dwc2_hsotg *hsotg)
* and its value must not be changed during runtime.
*/
if (hsotg->params.reload_ctl) {
- hfir = dwc2_readl(hsotg->regs + HFIR);
+ hfir = dwc2_readl(hsotg, HFIR);
hfir |= HFIR_RLDCTRL;
- dwc2_writel(hfir, hsotg->regs + HFIR);
+ dwc2_writel(hsotg, hfir, HFIR);
}
if (hsotg->params.dma_desc_enable) {
@@ -2406,9 +2405,9 @@ static void dwc2_core_host_init(struct dwc2_hsotg *hsotg)
"falling back to buffer DMA mode.\n");
hsotg->params.dma_desc_enable = false;
} else {
- hcfg = dwc2_readl(hsotg->regs + HCFG);
+ hcfg = dwc2_readl(hsotg, HCFG);
hcfg |= HCFG_DESCDMA;
- dwc2_writel(hcfg, hsotg->regs + HCFG);
+ dwc2_writel(hsotg, hcfg, HCFG);
}
}
@@ -2417,18 +2416,18 @@ static void dwc2_core_host_init(struct dwc2_hsotg *hsotg)
/* TODO - check this */
/* Clear Host Set HNP Enable in the OTG Control Register */
- otgctl = dwc2_readl(hsotg->regs + GOTGCTL);
+ otgctl = dwc2_readl(hsotg, GOTGCTL);
otgctl &= ~GOTGCTL_HSTSETHNPEN;
- dwc2_writel(otgctl, hsotg->regs + GOTGCTL);
+ dwc2_writel(hsotg, otgctl, GOTGCTL);
/* Make sure the FIFOs are flushed */
dwc2_flush_tx_fifo(hsotg, 0x10 /* all TX FIFOs */);
dwc2_flush_rx_fifo(hsotg);
/* Clear Host Set HNP Enable in the OTG Control Register */
- otgctl = dwc2_readl(hsotg->regs + GOTGCTL);
+ otgctl = dwc2_readl(hsotg, GOTGCTL);
otgctl &= ~GOTGCTL_HSTSETHNPEN;
- dwc2_writel(otgctl, hsotg->regs + GOTGCTL);
+ dwc2_writel(hsotg, otgctl, GOTGCTL);
if (!hsotg->params.dma_desc_enable) {
int num_channels, i;
@@ -2437,19 +2436,19 @@ static void dwc2_core_host_init(struct dwc2_hsotg *hsotg)
/* Flush out any leftover queued requests */
num_channels = hsotg->params.host_channels;
for (i = 0; i < num_channels; i++) {
- hcchar = dwc2_readl(hsotg->regs + HCCHAR(i));
+ hcchar = dwc2_readl(hsotg, HCCHAR(i));
hcchar &= ~HCCHAR_CHENA;
hcchar |= HCCHAR_CHDIS;
hcchar &= ~HCCHAR_EPDIR;
- dwc2_writel(hcchar, hsotg->regs + HCCHAR(i));
+ dwc2_writel(hsotg, hcchar, HCCHAR(i));
}
/* Halt all channels to put them into a known state */
for (i = 0; i < num_channels; i++) {
- hcchar = dwc2_readl(hsotg->regs + HCCHAR(i));
+ hcchar = dwc2_readl(hsotg, HCCHAR(i));
hcchar |= HCCHAR_CHENA | HCCHAR_CHDIS;
hcchar &= ~HCCHAR_EPDIR;
- dwc2_writel(hcchar, hsotg->regs + HCCHAR(i));
+ dwc2_writel(hsotg, hcchar, HCCHAR(i));
dev_dbg(hsotg->dev, "%s: Halt channel %d\n",
__func__, i);
@@ -2473,7 +2472,7 @@ static void dwc2_core_host_init(struct dwc2_hsotg *hsotg)
!!(hprt0 & HPRT0_PWR));
if (!(hprt0 & HPRT0_PWR)) {
hprt0 |= HPRT0_PWR;
- dwc2_writel(hprt0, hsotg->regs + HPRT0);
+ dwc2_writel(hsotg, hprt0, HPRT0);
}
}
@@ -2625,36 +2624,66 @@ static void dwc2_hc_init_xfer(struct dwc2_hsotg *hsotg,
}
}
-#define DWC2_USB_DMA_ALIGN 4
+static int dwc2_alloc_split_dma_aligned_buf(struct dwc2_hsotg *hsotg,
+ struct dwc2_qh *qh,
+ struct dwc2_host_chan *chan)
+{
+ if (!hsotg->unaligned_cache ||
+ chan->max_packet > DWC2_KMEM_UNALIGNED_BUF_SIZE)
+ return -ENOMEM;
-struct dma_aligned_buffer {
- void *kmalloc_ptr;
- void *old_xfer_buffer;
- u8 data[0];
-};
+ if (!qh->dw_align_buf) {
+ qh->dw_align_buf = kmem_cache_alloc(hsotg->unaligned_cache,
+ GFP_ATOMIC | GFP_DMA);
+ if (!qh->dw_align_buf)
+ return -ENOMEM;
+ }
+
+ qh->dw_align_buf_dma = dma_map_single(hsotg->dev, qh->dw_align_buf,
+ DWC2_KMEM_UNALIGNED_BUF_SIZE,
+ DMA_FROM_DEVICE);
+
+ if (dma_mapping_error(hsotg->dev, qh->dw_align_buf_dma)) {
+ dev_err(hsotg->dev, "can't map align_buf\n");
+ chan->align_buf = 0;
+ return -EINVAL;
+ }
+
+ chan->align_buf = qh->dw_align_buf_dma;
+ return 0;
+}
+
+#define DWC2_USB_DMA_ALIGN 4
static void dwc2_free_dma_aligned_buffer(struct urb *urb)
{
- struct dma_aligned_buffer *temp;
+ void *stored_xfer_buffer;
+ size_t length;
if (!(urb->transfer_flags & URB_ALIGNED_TEMP_BUFFER))
return;
- temp = container_of(urb->transfer_buffer,
- struct dma_aligned_buffer, data);
+ /* Restore urb->transfer_buffer from the end of the allocated area */
+ memcpy(&stored_xfer_buffer, urb->transfer_buffer +
+ urb->transfer_buffer_length, sizeof(urb->transfer_buffer));
- if (usb_urb_dir_in(urb))
- memcpy(temp->old_xfer_buffer, temp->data,
- urb->transfer_buffer_length);
- urb->transfer_buffer = temp->old_xfer_buffer;
- kfree(temp->kmalloc_ptr);
+ if (usb_urb_dir_in(urb)) {
+ if (usb_pipeisoc(urb->pipe))
+ length = urb->transfer_buffer_length;
+ else
+ length = urb->actual_length;
+
+ memcpy(stored_xfer_buffer, urb->transfer_buffer, length);
+ }
+ kfree(urb->transfer_buffer);
+ urb->transfer_buffer = stored_xfer_buffer;
urb->transfer_flags &= ~URB_ALIGNED_TEMP_BUFFER;
}
static int dwc2_alloc_dma_aligned_buffer(struct urb *urb, gfp_t mem_flags)
{
- struct dma_aligned_buffer *temp, *kmalloc_ptr;
+ void *kmalloc_ptr;
size_t kmalloc_size;
if (urb->num_sgs || urb->sg ||
@@ -2662,22 +2691,29 @@ static int dwc2_alloc_dma_aligned_buffer(struct urb *urb, gfp_t mem_flags)
!((uintptr_t)urb->transfer_buffer & (DWC2_USB_DMA_ALIGN - 1)))
return 0;
- /* Allocate a buffer with enough padding for alignment */
+ /*
+ * Allocate a buffer with enough padding for original transfer_buffer
+ * pointer. This allocation is guaranteed to be aligned properly for
+ * DMA
+ */
kmalloc_size = urb->transfer_buffer_length +
- sizeof(struct dma_aligned_buffer) + DWC2_USB_DMA_ALIGN - 1;
+ sizeof(urb->transfer_buffer);
kmalloc_ptr = kmalloc(kmalloc_size, mem_flags);
if (!kmalloc_ptr)
return -ENOMEM;
- /* Position our struct dma_aligned_buffer such that data is aligned */
- temp = PTR_ALIGN(kmalloc_ptr + 1, DWC2_USB_DMA_ALIGN) - 1;
- temp->kmalloc_ptr = kmalloc_ptr;
- temp->old_xfer_buffer = urb->transfer_buffer;
+ /*
+ * Position value of original urb->transfer_buffer pointer to the end
+ * of allocation for later referencing
+ */
+ memcpy(kmalloc_ptr + urb->transfer_buffer_length,
+ &urb->transfer_buffer, sizeof(urb->transfer_buffer));
+
if (usb_urb_dir_out(urb))
- memcpy(temp->data, urb->transfer_buffer,
+ memcpy(kmalloc_ptr, urb->transfer_buffer,
urb->transfer_buffer_length);
- urb->transfer_buffer = temp->data;
+ urb->transfer_buffer = kmalloc_ptr;
urb->transfer_flags |= URB_ALIGNED_TEMP_BUFFER;
@@ -2802,6 +2838,32 @@ static int dwc2_assign_and_init_hc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
/* Set the transfer attributes */
dwc2_hc_init_xfer(hsotg, chan, qtd);
+ /* For non-dword aligned buffers */
+ if (hsotg->params.host_dma && qh->do_split &&
+ chan->ep_is_in && (chan->xfer_dma & 0x3)) {
+ dev_vdbg(hsotg->dev, "Non-aligned buffer\n");
+ if (dwc2_alloc_split_dma_aligned_buf(hsotg, qh, chan)) {
+ dev_err(hsotg->dev,
+ "Failed to allocate memory to handle non-aligned buffer\n");
+ /* Add channel back to free list */
+ chan->align_buf = 0;
+ chan->multi_count = 0;
+ list_add_tail(&chan->hc_list_entry,
+ &hsotg->free_hc_list);
+ qtd->in_process = 0;
+ qh->channel = NULL;
+ return -ENOMEM;
+ }
+ } else {
+ /*
+ * We assume that DMA is always aligned in non-split
+ * case or split out case. Warn if not.
+ */
+ WARN_ON_ONCE(hsotg->params.host_dma &&
+ (chan->xfer_dma & 0x3));
+ chan->align_buf = 0;
+ }
+
if (chan->ep_type == USB_ENDPOINT_XFER_INT ||
chan->ep_type == USB_ENDPOINT_XFER_ISOC)
/*
@@ -3012,7 +3074,7 @@ static void dwc2_process_periodic_channels(struct dwc2_hsotg *hsotg)
if (dbg_perio())
dev_vdbg(hsotg->dev, "Queue periodic transactions\n");
- tx_status = dwc2_readl(hsotg->regs + HPTXSTS);
+ tx_status = dwc2_readl(hsotg, HPTXSTS);
qspcavail = (tx_status & TXSTS_QSPCAVAIL_MASK) >>
TXSTS_QSPCAVAIL_SHIFT;
fspcavail = (tx_status & TXSTS_FSPCAVAIL_MASK) >>
@@ -3027,7 +3089,7 @@ static void dwc2_process_periodic_channels(struct dwc2_hsotg *hsotg)
qh_ptr = hsotg->periodic_sched_assigned.next;
while (qh_ptr != &hsotg->periodic_sched_assigned) {
- tx_status = dwc2_readl(hsotg->regs + HPTXSTS);
+ tx_status = dwc2_readl(hsotg, HPTXSTS);
qspcavail = (tx_status & TXSTS_QSPCAVAIL_MASK) >>
TXSTS_QSPCAVAIL_SHIFT;
if (qspcavail == 0) {
@@ -3097,10 +3159,10 @@ exit:
* level to ensure that new requests are loaded as
* soon as possible.)
*/
- gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
+ gintmsk = dwc2_readl(hsotg, GINTMSK);
if (!(gintmsk & GINTSTS_PTXFEMP)) {
gintmsk |= GINTSTS_PTXFEMP;
- dwc2_writel(gintmsk, hsotg->regs + GINTMSK);
+ dwc2_writel(hsotg, gintmsk, GINTMSK);
}
} else {
/*
@@ -3110,10 +3172,10 @@ exit:
* handlers to queue more transactions as transfer
* states change.
*/
- gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
+ gintmsk = dwc2_readl(hsotg, GINTMSK);
if (gintmsk & GINTSTS_PTXFEMP) {
gintmsk &= ~GINTSTS_PTXFEMP;
- dwc2_writel(gintmsk, hsotg->regs + GINTMSK);
+ dwc2_writel(hsotg, gintmsk, GINTMSK);
}
}
}
@@ -3142,7 +3204,7 @@ static void dwc2_process_non_periodic_channels(struct dwc2_hsotg *hsotg)
dev_vdbg(hsotg->dev, "Queue non-periodic transactions\n");
- tx_status = dwc2_readl(hsotg->regs + GNPTXSTS);
+ tx_status = dwc2_readl(hsotg, GNPTXSTS);
qspcavail = (tx_status & TXSTS_QSPCAVAIL_MASK) >>
TXSTS_QSPCAVAIL_SHIFT;
fspcavail = (tx_status & TXSTS_FSPCAVAIL_MASK) >>
@@ -3165,7 +3227,7 @@ static void dwc2_process_non_periodic_channels(struct dwc2_hsotg *hsotg)
* available in the request queue or the Tx FIFO
*/
do {
- tx_status = dwc2_readl(hsotg->regs + GNPTXSTS);
+ tx_status = dwc2_readl(hsotg, GNPTXSTS);
qspcavail = (tx_status & TXSTS_QSPCAVAIL_MASK) >>
TXSTS_QSPCAVAIL_SHIFT;
if (!hsotg->params.host_dma && qspcavail == 0) {
@@ -3202,7 +3264,7 @@ next:
} while (hsotg->non_periodic_qh_ptr != orig_qh_ptr);
if (!hsotg->params.host_dma) {
- tx_status = dwc2_readl(hsotg->regs + GNPTXSTS);
+ tx_status = dwc2_readl(hsotg, GNPTXSTS);
qspcavail = (tx_status & TXSTS_QSPCAVAIL_MASK) >>
TXSTS_QSPCAVAIL_SHIFT;
fspcavail = (tx_status & TXSTS_FSPCAVAIL_MASK) >>
@@ -3222,9 +3284,9 @@ next:
* level to ensure that new requests are loaded as
* soon as possible.)
*/
- gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
+ gintmsk = dwc2_readl(hsotg, GINTMSK);
gintmsk |= GINTSTS_NPTXFEMP;
- dwc2_writel(gintmsk, hsotg->regs + GINTMSK);
+ dwc2_writel(hsotg, gintmsk, GINTMSK);
} else {
/*
* Disable the Tx FIFO empty interrupt since there are
@@ -3233,9 +3295,9 @@ next:
* handlers to queue more transactions as transfer
* states change.
*/
- gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
+ gintmsk = dwc2_readl(hsotg, GINTMSK);
gintmsk &= ~GINTSTS_NPTXFEMP;
- dwc2_writel(gintmsk, hsotg->regs + GINTMSK);
+ dwc2_writel(hsotg, gintmsk, GINTMSK);
}
}
}
@@ -3272,10 +3334,10 @@ void dwc2_hcd_queue_transactions(struct dwc2_hsotg *hsotg,
* Ensure NP Tx FIFO empty interrupt is disabled when
* there are no non-periodic transfers to process
*/
- u32 gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
+ u32 gintmsk = dwc2_readl(hsotg, GINTMSK);
gintmsk &= ~GINTSTS_NPTXFEMP;
- dwc2_writel(gintmsk, hsotg->regs + GINTMSK);
+ dwc2_writel(hsotg, gintmsk, GINTMSK);
}
}
}
@@ -3290,7 +3352,7 @@ static void dwc2_conn_id_status_change(struct work_struct *work)
dev_dbg(hsotg->dev, "%s()\n", __func__);
- gotgctl = dwc2_readl(hsotg->regs + GOTGCTL);
+ gotgctl = dwc2_readl(hsotg, GOTGCTL);
dev_dbg(hsotg->dev, "gotgctl=%0x\n", gotgctl);
dev_dbg(hsotg->dev, "gotgctl.b.conidsts=%d\n",
!!(gotgctl & GOTGCTL_CONID_B));
@@ -3316,7 +3378,7 @@ static void dwc2_conn_id_status_change(struct work_struct *work)
* check it again and jump to host mode if that was
* the case.
*/
- gotgctl = dwc2_readl(hsotg->regs + GOTGCTL);
+ gotgctl = dwc2_readl(hsotg, GOTGCTL);
if (!(gotgctl & GOTGCTL_CONID_B))
goto host;
if (++count > 250)
@@ -3376,9 +3438,9 @@ static void dwc2_wakeup_detected(struct timer_list *t)
hprt0 = dwc2_read_hprt0(hsotg);
dev_dbg(hsotg->dev, "Resume: HPRT0=%0x\n", hprt0);
hprt0 &= ~HPRT0_RES;
- dwc2_writel(hprt0, hsotg->regs + HPRT0);
+ dwc2_writel(hsotg, hprt0, HPRT0);
dev_dbg(hsotg->dev, "Clear Resume: HPRT0=%0x\n",
- dwc2_readl(hsotg->regs + HPRT0));
+ dwc2_readl(hsotg, HPRT0));
dwc2_hcd_rem_wakeup(hsotg);
hsotg->bus_suspended = false;
@@ -3407,15 +3469,15 @@ static void dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex)
spin_lock_irqsave(&hsotg->lock, flags);
if (windex == hsotg->otg_port && dwc2_host_is_b_hnp_enabled(hsotg)) {
- gotgctl = dwc2_readl(hsotg->regs + GOTGCTL);
+ gotgctl = dwc2_readl(hsotg, GOTGCTL);
gotgctl |= GOTGCTL_HSTSETHNPEN;
- dwc2_writel(gotgctl, hsotg->regs + GOTGCTL);
+ dwc2_writel(hsotg, gotgctl, GOTGCTL);
hsotg->op_state = OTG_STATE_A_SUSPEND;
}
hprt0 = dwc2_read_hprt0(hsotg);
hprt0 |= HPRT0_SUSP;
- dwc2_writel(hprt0, hsotg->regs + HPRT0);
+ dwc2_writel(hsotg, hprt0, HPRT0);
hsotg->bus_suspended = true;
@@ -3425,17 +3487,17 @@ static void dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex)
*/
if (!hsotg->params.power_down) {
/* Suspend the Phy Clock */
- pcgctl = dwc2_readl(hsotg->regs + PCGCTL);
+ pcgctl = dwc2_readl(hsotg, PCGCTL);
pcgctl |= PCGCTL_STOPPCLK;
- dwc2_writel(pcgctl, hsotg->regs + PCGCTL);
+ dwc2_writel(hsotg, pcgctl, PCGCTL);
udelay(10);
}
/* For HNP the bus must be suspended for at least 200ms */
if (dwc2_host_is_b_hnp_enabled(hsotg)) {
- pcgctl = dwc2_readl(hsotg->regs + PCGCTL);
+ pcgctl = dwc2_readl(hsotg, PCGCTL);
pcgctl &= ~PCGCTL_STOPPCLK;
- dwc2_writel(pcgctl, hsotg->regs + PCGCTL);
+ dwc2_writel(hsotg, pcgctl, PCGCTL);
spin_unlock_irqrestore(&hsotg->lock, flags);
@@ -3459,9 +3521,9 @@ static void dwc2_port_resume(struct dwc2_hsotg *hsotg)
* after registers restore.
*/
if (!hsotg->params.power_down) {
- pcgctl = dwc2_readl(hsotg->regs + PCGCTL);
+ pcgctl = dwc2_readl(hsotg, PCGCTL);
pcgctl &= ~PCGCTL_STOPPCLK;
- dwc2_writel(pcgctl, hsotg->regs + PCGCTL);
+ dwc2_writel(hsotg, pcgctl, PCGCTL);
spin_unlock_irqrestore(&hsotg->lock, flags);
msleep(20);
spin_lock_irqsave(&hsotg->lock, flags);
@@ -3470,7 +3532,7 @@ static void dwc2_port_resume(struct dwc2_hsotg *hsotg)
hprt0 = dwc2_read_hprt0(hsotg);
hprt0 |= HPRT0_RES;
hprt0 &= ~HPRT0_SUSP;
- dwc2_writel(hprt0, hsotg->regs + HPRT0);
+ dwc2_writel(hsotg, hprt0, HPRT0);
spin_unlock_irqrestore(&hsotg->lock, flags);
msleep(USB_RESUME_TIMEOUT);
@@ -3478,7 +3540,7 @@ static void dwc2_port_resume(struct dwc2_hsotg *hsotg)
spin_lock_irqsave(&hsotg->lock, flags);
hprt0 = dwc2_read_hprt0(hsotg);
hprt0 &= ~(HPRT0_RES | HPRT0_SUSP);
- dwc2_writel(hprt0, hsotg->regs + HPRT0);
+ dwc2_writel(hsotg, hprt0, HPRT0);
hsotg->bus_suspended = false;
spin_unlock_irqrestore(&hsotg->lock, flags);
}
@@ -3493,6 +3555,7 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
u32 port_status;
u32 speed;
u32 pcgctl;
+ u32 pwr;
switch (typereq) {
case ClearHubFeature:
@@ -3522,7 +3585,7 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
"ClearPortFeature USB_PORT_FEAT_ENABLE\n");
hprt0 = dwc2_read_hprt0(hsotg);
hprt0 |= HPRT0_ENA;
- dwc2_writel(hprt0, hsotg->regs + HPRT0);
+ dwc2_writel(hsotg, hprt0, HPRT0);
break;
case USB_PORT_FEAT_SUSPEND:
@@ -3541,8 +3604,11 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
dev_dbg(hsotg->dev,
"ClearPortFeature USB_PORT_FEAT_POWER\n");
hprt0 = dwc2_read_hprt0(hsotg);
+ pwr = hprt0 & HPRT0_PWR;
hprt0 &= ~HPRT0_PWR;
- dwc2_writel(hprt0, hsotg->regs + HPRT0);
+ dwc2_writel(hsotg, hprt0, HPRT0);
+ if (pwr)
+ dwc2_vbus_supply_exit(hsotg);
break;
case USB_PORT_FEAT_INDICATOR:
@@ -3663,7 +3729,7 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
break;
}
- hprt0 = dwc2_readl(hsotg->regs + HPRT0);
+ hprt0 = dwc2_readl(hsotg, HPRT0);
dev_vdbg(hsotg->dev, " HPRT0: 0x%08x\n", hprt0);
if (hprt0 & HPRT0_CONNSTS)
@@ -3704,9 +3770,9 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
dev_info(hsotg->dev, "Enabling descriptor DMA mode\n");
hsotg->params.dma_desc_enable = true;
- hcfg = dwc2_readl(hsotg->regs + HCFG);
+ hcfg = dwc2_readl(hsotg, HCFG);
hcfg |= HCFG_DESCDMA;
- dwc2_writel(hcfg, hsotg->regs + HCFG);
+ dwc2_writel(hsotg, hcfg, HCFG);
hsotg->new_connection = false;
}
}
@@ -3752,8 +3818,11 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
dev_dbg(hsotg->dev,
"SetPortFeature - USB_PORT_FEAT_POWER\n");
hprt0 = dwc2_read_hprt0(hsotg);
+ pwr = hprt0 & HPRT0_PWR;
hprt0 |= HPRT0_PWR;
- dwc2_writel(hprt0, hsotg->regs + HPRT0);
+ dwc2_writel(hsotg, hprt0, HPRT0);
+ if (!pwr)
+ dwc2_vbus_supply_init(hsotg);
break;
case USB_PORT_FEAT_RESET:
@@ -3763,13 +3832,14 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
hprt0 = dwc2_read_hprt0(hsotg);
dev_dbg(hsotg->dev,
"SetPortFeature - USB_PORT_FEAT_RESET\n");
- pcgctl = dwc2_readl(hsotg->regs + PCGCTL);
+ pcgctl = dwc2_readl(hsotg, PCGCTL);
pcgctl &= ~(PCGCTL_ENBL_SLEEP_GATING | PCGCTL_STOPPCLK);
- dwc2_writel(pcgctl, hsotg->regs + PCGCTL);
+ dwc2_writel(hsotg, pcgctl, PCGCTL);
/* ??? Original driver does this */
- dwc2_writel(0, hsotg->regs + PCGCTL);
+ dwc2_writel(hsotg, 0, PCGCTL);
hprt0 = dwc2_read_hprt0(hsotg);
+ pwr = hprt0 & HPRT0_PWR;
/* Clear suspend bit if resetting from suspend state */
hprt0 &= ~HPRT0_SUSP;
@@ -3782,13 +3852,15 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
hprt0 |= HPRT0_PWR | HPRT0_RST;
dev_dbg(hsotg->dev,
"In host mode, hprt0=%08x\n", hprt0);
- dwc2_writel(hprt0, hsotg->regs + HPRT0);
+ dwc2_writel(hsotg, hprt0, HPRT0);
+ if (!pwr)
+ dwc2_vbus_supply_init(hsotg);
}
/* Clear reset bit in 10ms (FS/LS) or 50ms (HS) */
msleep(50);
hprt0 &= ~HPRT0_RST;
- dwc2_writel(hprt0, hsotg->regs + HPRT0);
+ dwc2_writel(hsotg, hprt0, HPRT0);
hsotg->lx_state = DWC2_L0; /* Now back to On state */
break;
@@ -3804,7 +3876,7 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
"SetPortFeature - USB_PORT_FEAT_TEST\n");
hprt0 &= ~HPRT0_TSTCTL_MASK;
hprt0 |= (windex >> 8) << HPRT0_TSTCTL_SHIFT;
- dwc2_writel(hprt0, hsotg->regs + HPRT0);
+ dwc2_writel(hsotg, hprt0, HPRT0);
break;
default:
@@ -3861,7 +3933,7 @@ static int dwc2_hcd_is_status_changed(struct dwc2_hsotg *hsotg, int port)
int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg)
{
- u32 hfnum = dwc2_readl(hsotg->regs + HFNUM);
+ u32 hfnum = dwc2_readl(hsotg, HFNUM);
#ifdef DWC2_DEBUG_SOF
dev_vdbg(hsotg->dev, "DWC OTG HCD GET FRAME NUMBER %d\n",
@@ -3872,9 +3944,9 @@ int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg)
int dwc2_hcd_get_future_frame_number(struct dwc2_hsotg *hsotg, int us)
{
- u32 hprt = dwc2_readl(hsotg->regs + HPRT0);
- u32 hfir = dwc2_readl(hsotg->regs + HFIR);
- u32 hfnum = dwc2_readl(hsotg->regs + HFNUM);
+ u32 hprt = dwc2_readl(hsotg, HPRT0);
+ u32 hfir = dwc2_readl(hsotg, HFIR);
+ u32 hfnum = dwc2_readl(hsotg, HFNUM);
unsigned int us_per_frame;
unsigned int frame_number;
unsigned int remaining;
@@ -3993,11 +4065,11 @@ void dwc2_hcd_dump_state(struct dwc2_hsotg *hsotg)
if (chan->xfer_started) {
u32 hfnum, hcchar, hctsiz, hcint, hcintmsk;
- hfnum = dwc2_readl(hsotg->regs + HFNUM);
- hcchar = dwc2_readl(hsotg->regs + HCCHAR(i));
- hctsiz = dwc2_readl(hsotg->regs + HCTSIZ(i));
- hcint = dwc2_readl(hsotg->regs + HCINT(i));
- hcintmsk = dwc2_readl(hsotg->regs + HCINTMSK(i));
+ hfnum = dwc2_readl(hsotg, HFNUM);
+ hcchar = dwc2_readl(hsotg, HCCHAR(i));
+ hctsiz = dwc2_readl(hsotg, HCTSIZ(i));
+ hcint = dwc2_readl(hsotg, HCINT(i));
+ hcintmsk = dwc2_readl(hsotg, HCINTMSK(i));
dev_dbg(hsotg->dev, " hfnum: 0x%08x\n", hfnum);
dev_dbg(hsotg->dev, " hcchar: 0x%08x\n", hcchar);
dev_dbg(hsotg->dev, " hctsiz: 0x%08x\n", hctsiz);
@@ -4045,12 +4117,12 @@ void dwc2_hcd_dump_state(struct dwc2_hsotg *hsotg)
dev_dbg(hsotg->dev, " periodic_channels: %d\n",
hsotg->periodic_channels);
dev_dbg(hsotg->dev, " periodic_usecs: %d\n", hsotg->periodic_usecs);
- np_tx_status = dwc2_readl(hsotg->regs + GNPTXSTS);
+ np_tx_status = dwc2_readl(hsotg, GNPTXSTS);
dev_dbg(hsotg->dev, " NP Tx Req Queue Space Avail: %d\n",
(np_tx_status & TXSTS_QSPCAVAIL_MASK) >> TXSTS_QSPCAVAIL_SHIFT);
dev_dbg(hsotg->dev, " NP Tx FIFO Space Avail: %d\n",
(np_tx_status & TXSTS_FSPCAVAIL_MASK) >> TXSTS_FSPCAVAIL_SHIFT);
- p_tx_status = dwc2_readl(hsotg->regs + HPTXSTS);
+ p_tx_status = dwc2_readl(hsotg, HPTXSTS);
dev_dbg(hsotg->dev, " P Tx Req Queue Space Avail: %d\n",
(p_tx_status & TXSTS_QSPCAVAIL_MASK) >> TXSTS_QSPCAVAIL_SHIFT);
dev_dbg(hsotg->dev, " P Tx FIFO Space Avail: %d\n",
@@ -4087,7 +4159,6 @@ static struct dwc2_hsotg *dwc2_hcd_to_hsotg(struct usb_hcd *hcd)
* then the refcount for the structure will go to 0 and we'll free it.
*
* @hsotg: The HCD state structure for the DWC OTG controller.
- * @qh: The QH structure.
* @context: The priv pointer from a struct dwc2_hcd_urb.
* @mem_flags: Flags for allocating memory.
* @ttport: We'll return this device's port number here. That's used to
@@ -4301,7 +4372,7 @@ static void dwc2_hcd_reset_func(struct work_struct *work)
hprt0 = dwc2_read_hprt0(hsotg);
hprt0 &= ~HPRT0_RST;
- dwc2_writel(hprt0, hsotg->regs + HPRT0);
+ dwc2_writel(hsotg, hprt0, HPRT0);
hsotg->flags.b.port_reset_change = 1;
spin_unlock_irqrestore(&hsotg->lock, flags);
@@ -4323,6 +4394,8 @@ static int _dwc2_hcd_start(struct usb_hcd *hcd)
struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
struct usb_bus *bus = hcd_to_bus(hcd);
unsigned long flags;
+ u32 hprt0;
+ int ret;
dev_dbg(hsotg->dev, "DWC OTG HCD START\n");
@@ -4338,6 +4411,17 @@ static int _dwc2_hcd_start(struct usb_hcd *hcd)
dwc2_hcd_reinit(hsotg);
+ hprt0 = dwc2_read_hprt0(hsotg);
+ /* Has vbus power been turned on in dwc2_core_host_init ? */
+ if (hprt0 & HPRT0_PWR) {
+ /* Enable external vbus supply before resuming root hub */
+ spin_unlock_irqrestore(&hsotg->lock, flags);
+ ret = dwc2_vbus_supply_init(hsotg);
+ if (ret)
+ return ret;
+ spin_lock_irqsave(&hsotg->lock, flags);
+ }
+
/* Initialize and connect root hub if one is not already attached */
if (bus->root_hub) {
dev_dbg(hsotg->dev, "DWC OTG HCD Has Root Hub\n");
@@ -4347,7 +4431,7 @@ static int _dwc2_hcd_start(struct usb_hcd *hcd)
spin_unlock_irqrestore(&hsotg->lock, flags);
- return dwc2_vbus_supply_init(hsotg);
+ return 0;
}
/*
@@ -4358,6 +4442,7 @@ static void _dwc2_hcd_stop(struct usb_hcd *hcd)
{
struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
unsigned long flags;
+ u32 hprt0;
/* Turn off all host-specific interrupts */
dwc2_disable_host_interrupts(hsotg);
@@ -4366,6 +4451,7 @@ static void _dwc2_hcd_stop(struct usb_hcd *hcd)
synchronize_irq(hcd->irq);
spin_lock_irqsave(&hsotg->lock, flags);
+ hprt0 = dwc2_read_hprt0(hsotg);
/* Ensure hcd is disconnected */
dwc2_hcd_disconnect(hsotg, true);
dwc2_hcd_stop(hsotg);
@@ -4374,7 +4460,9 @@ static void _dwc2_hcd_stop(struct usb_hcd *hcd)
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
spin_unlock_irqrestore(&hsotg->lock, flags);
- dwc2_vbus_supply_exit(hsotg);
+ /* keep balanced supply init/exit by checking HPRT0_PWR */
+ if (hprt0 & HPRT0_PWR)
+ dwc2_vbus_supply_exit(hsotg);
usleep_range(1000, 3000);
}
@@ -4411,8 +4499,10 @@ static int _dwc2_hcd_suspend(struct usb_hcd *hcd)
hprt0 = dwc2_read_hprt0(hsotg);
hprt0 |= HPRT0_SUSP;
hprt0 &= ~HPRT0_PWR;
- dwc2_writel(hprt0, hsotg->regs + HPRT0);
+ dwc2_writel(hsotg, hprt0, HPRT0);
+ spin_unlock_irqrestore(&hsotg->lock, flags);
dwc2_vbus_supply_exit(hsotg);
+ spin_lock_irqsave(&hsotg->lock, flags);
}
/* Enter partial_power_down */
@@ -4502,8 +4592,8 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd)
* Clear Port Enable and Port Status changes.
* Enable Port Power.
*/
- dwc2_writel(HPRT0_PWR | HPRT0_CONNDET |
- HPRT0_ENACHG, hsotg->regs + HPRT0);
+ dwc2_writel(hsotg, HPRT0_PWR | HPRT0_CONNDET |
+ HPRT0_ENACHG, HPRT0);
/* Wait for controller to detect Port Connect */
usleep_range(5000, 7000);
}
@@ -5023,17 +5113,17 @@ static void dwc2_hcd_free(struct dwc2_hsotg *hsotg)
hsotg->status_buf = NULL;
}
- ahbcfg = dwc2_readl(hsotg->regs + GAHBCFG);
+ ahbcfg = dwc2_readl(hsotg, GAHBCFG);
/* Disable all interrupts */
ahbcfg &= ~GAHBCFG_GLBL_INTR_EN;
- dwc2_writel(ahbcfg, hsotg->regs + GAHBCFG);
- dwc2_writel(0, hsotg->regs + GINTMSK);
+ dwc2_writel(hsotg, ahbcfg, GAHBCFG);
+ dwc2_writel(hsotg, 0, GINTMSK);
if (hsotg->hw_params.snpsid >= DWC2_CORE_REV_3_00a) {
- dctl = dwc2_readl(hsotg->regs + DCTL);
+ dctl = dwc2_readl(hsotg, DCTL);
dctl |= DCTL_SFTDISCON;
- dwc2_writel(dctl, hsotg->regs + DCTL);
+ dwc2_writel(hsotg, dctl, DCTL);
}
if (hsotg->wq_otg) {
@@ -5076,17 +5166,18 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg)
retval = -ENOMEM;
- hcfg = dwc2_readl(hsotg->regs + HCFG);
+ hcfg = dwc2_readl(hsotg, HCFG);
dev_dbg(hsotg->dev, "hcfg=%08x\n", hcfg);
#ifdef CONFIG_USB_DWC2_TRACK_MISSED_SOFS
- hsotg->frame_num_array = kzalloc(sizeof(*hsotg->frame_num_array) *
- FRAME_NUM_ARRAY_SIZE, GFP_KERNEL);
+ hsotg->frame_num_array = kcalloc(FRAME_NUM_ARRAY_SIZE,
+ sizeof(*hsotg->frame_num_array),
+ GFP_KERNEL);
if (!hsotg->frame_num_array)
goto error1;
- hsotg->last_frame_num_array = kzalloc(
- sizeof(*hsotg->last_frame_num_array) *
- FRAME_NUM_ARRAY_SIZE, GFP_KERNEL);
+ hsotg->last_frame_num_array =
+ kcalloc(FRAME_NUM_ARRAY_SIZE,
+ sizeof(*hsotg->last_frame_num_array), GFP_KERNEL);
if (!hsotg->last_frame_num_array)
goto error1;
#endif
@@ -5246,6 +5337,19 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg)
}
}
+ if (hsotg->params.host_dma) {
+ /*
+ * Create kmem caches to handle non-aligned buffer
+ * in Buffer DMA mode.
+ */
+ hsotg->unaligned_cache = kmem_cache_create("dwc2-unaligned-dma",
+ DWC2_KMEM_UNALIGNED_BUF_SIZE, 4,
+ SLAB_CACHE_DMA, NULL);
+ if (!hsotg->unaligned_cache)
+ dev_err(hsotg->dev,
+ "unable to create dwc2 unaligned cache\n");
+ }
+
hsotg->otg_port = 1;
hsotg->frame_list = NULL;
hsotg->frame_list_dma = 0;
@@ -5280,8 +5384,9 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg)
return 0;
error4:
- kmem_cache_destroy(hsotg->desc_gen_cache);
+ kmem_cache_destroy(hsotg->unaligned_cache);
kmem_cache_destroy(hsotg->desc_hsisoc_cache);
+ kmem_cache_destroy(hsotg->desc_gen_cache);
error3:
dwc2_hcd_release(hsotg);
error2:
@@ -5322,8 +5427,9 @@ void dwc2_hcd_remove(struct dwc2_hsotg *hsotg)
usb_remove_hcd(hcd);
hsotg->priv = NULL;
- kmem_cache_destroy(hsotg->desc_gen_cache);
+ kmem_cache_destroy(hsotg->unaligned_cache);
kmem_cache_destroy(hsotg->desc_hsisoc_cache);
+ kmem_cache_destroy(hsotg->desc_gen_cache);
dwc2_hcd_release(hsotg);
usb_put_hcd(hcd);
@@ -5350,14 +5456,14 @@ int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg)
/* Backup Host regs */
hr = &hsotg->hr_backup;
- hr->hcfg = dwc2_readl(hsotg->regs + HCFG);
- hr->haintmsk = dwc2_readl(hsotg->regs + HAINTMSK);
+ hr->hcfg = dwc2_readl(hsotg, HCFG);
+ hr->haintmsk = dwc2_readl(hsotg, HAINTMSK);
for (i = 0; i < hsotg->params.host_channels; ++i)
- hr->hcintmsk[i] = dwc2_readl(hsotg->regs + HCINTMSK(i));
+ hr->hcintmsk[i] = dwc2_readl(hsotg, HCINTMSK(i));
hr->hprt0 = dwc2_read_hprt0(hsotg);
- hr->hfir = dwc2_readl(hsotg->regs + HFIR);
- hr->hptxfsiz = dwc2_readl(hsotg->regs + HPTXFSIZ);
+ hr->hfir = dwc2_readl(hsotg, HFIR);
+ hr->hptxfsiz = dwc2_readl(hsotg, HPTXFSIZ);
hr->valid = true;
return 0;
@@ -5386,15 +5492,15 @@ int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg)
}
hr->valid = false;
- dwc2_writel(hr->hcfg, hsotg->regs + HCFG);
- dwc2_writel(hr->haintmsk, hsotg->regs + HAINTMSK);
+ dwc2_writel(hsotg, hr->hcfg, HCFG);
+ dwc2_writel(hsotg, hr->haintmsk, HAINTMSK);
for (i = 0; i < hsotg->params.host_channels; ++i)
- dwc2_writel(hr->hcintmsk[i], hsotg->regs + HCINTMSK(i));
+ dwc2_writel(hsotg, hr->hcintmsk[i], HCINTMSK(i));
- dwc2_writel(hr->hprt0, hsotg->regs + HPRT0);
- dwc2_writel(hr->hfir, hsotg->regs + HFIR);
- dwc2_writel(hr->hptxfsiz, hsotg->regs + HPTXFSIZ);
+ dwc2_writel(hsotg, hr->hprt0, HPRT0);
+ dwc2_writel(hsotg, hr->hfir, HFIR);
+ dwc2_writel(hsotg, hr->hptxfsiz, HPTXFSIZ);
hsotg->frame_number = 0;
return 0;
@@ -5429,13 +5535,13 @@ int dwc2_host_enter_hibernation(struct dwc2_hsotg *hsotg)
}
/* Enter USB Suspend Mode */
- hprt0 = dwc2_readl(hsotg->regs + HPRT0);
+ hprt0 = dwc2_readl(hsotg, HPRT0);
hprt0 |= HPRT0_SUSP;
hprt0 &= ~HPRT0_ENA;
- dwc2_writel(hprt0, hsotg->regs + HPRT0);
+ dwc2_writel(hsotg, hprt0, HPRT0);
/* Wait for the HPRT0.PrtSusp register field to be set */
- if (dwc2_hsotg_wait_bit_set(hsotg, HPRT0, HPRT0_SUSP, 300))
+ if (dwc2_hsotg_wait_bit_set(hsotg, HPRT0, HPRT0_SUSP, 3000))
dev_warn(hsotg->dev, "Suspend wasn't generated\n");
/*
@@ -5445,56 +5551,56 @@ int dwc2_host_enter_hibernation(struct dwc2_hsotg *hsotg)
spin_lock_irqsave(&hsotg->lock, flags);
hsotg->lx_state = DWC2_L2;
- gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
+ gusbcfg = dwc2_readl(hsotg, GUSBCFG);
if (gusbcfg & GUSBCFG_ULPI_UTMI_SEL) {
/* ULPI interface */
/* Suspend the Phy Clock */
- pcgcctl = dwc2_readl(hsotg->regs + PCGCTL);
+ pcgcctl = dwc2_readl(hsotg, PCGCTL);
pcgcctl |= PCGCTL_STOPPCLK;
- dwc2_writel(pcgcctl, hsotg->regs + PCGCTL);
+ dwc2_writel(hsotg, pcgcctl, PCGCTL);
udelay(10);
- gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn = dwc2_readl(hsotg, GPWRDN);
gpwrdn |= GPWRDN_PMUACTV;
- dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ dwc2_writel(hsotg, gpwrdn, GPWRDN);
udelay(10);
} else {
/* UTMI+ Interface */
- gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn = dwc2_readl(hsotg, GPWRDN);
gpwrdn |= GPWRDN_PMUACTV;
- dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ dwc2_writel(hsotg, gpwrdn, GPWRDN);
udelay(10);
- pcgcctl = dwc2_readl(hsotg->regs + PCGCTL);
+ pcgcctl = dwc2_readl(hsotg, PCGCTL);
pcgcctl |= PCGCTL_STOPPCLK;
- dwc2_writel(pcgcctl, hsotg->regs + PCGCTL);
+ dwc2_writel(hsotg, pcgcctl, PCGCTL);
udelay(10);
}
/* Enable interrupts from wake up logic */
- gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn = dwc2_readl(hsotg, GPWRDN);
gpwrdn |= GPWRDN_PMUINTSEL;
- dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ dwc2_writel(hsotg, gpwrdn, GPWRDN);
udelay(10);
/* Unmask host mode interrupts in GPWRDN */
- gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn = dwc2_readl(hsotg, GPWRDN);
gpwrdn |= GPWRDN_DISCONN_DET_MSK;
gpwrdn |= GPWRDN_LNSTSCHG_MSK;
gpwrdn |= GPWRDN_STS_CHGINT_MSK;
- dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ dwc2_writel(hsotg, gpwrdn, GPWRDN);
udelay(10);
/* Enable Power Down Clamp */
- gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn = dwc2_readl(hsotg, GPWRDN);
gpwrdn |= GPWRDN_PWRDNCLMP;
- dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ dwc2_writel(hsotg, gpwrdn, GPWRDN);
udelay(10);
/* Switch off VDD */
- gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn = dwc2_readl(hsotg, GPWRDN);
gpwrdn |= GPWRDN_PWRDNSWTCH;
- dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ dwc2_writel(hsotg, gpwrdn, GPWRDN);
hsotg->hibernated = 1;
hsotg->bus_suspended = 1;
@@ -5542,29 +5648,29 @@ int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup,
mdelay(100);
/* Clear all pending interupts */
- dwc2_writel(0xffffffff, hsotg->regs + GINTSTS);
+ dwc2_writel(hsotg, 0xffffffff, GINTSTS);
/* De-assert Restore */
- gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn = dwc2_readl(hsotg, GPWRDN);
gpwrdn &= ~GPWRDN_RESTORE;
- dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ dwc2_writel(hsotg, gpwrdn, GPWRDN);
udelay(10);
/* Restore GUSBCFG, HCFG */
- dwc2_writel(gr->gusbcfg, hsotg->regs + GUSBCFG);
- dwc2_writel(hr->hcfg, hsotg->regs + HCFG);
+ dwc2_writel(hsotg, gr->gusbcfg, GUSBCFG);
+ dwc2_writel(hsotg, hr->hcfg, HCFG);
/* De-assert Wakeup Logic */
- gpwrdn = dwc2_readl(hsotg->regs + GPWRDN);
+ gpwrdn = dwc2_readl(hsotg, GPWRDN);
gpwrdn &= ~GPWRDN_PMUACTV;
- dwc2_writel(gpwrdn, hsotg->regs + GPWRDN);
+ dwc2_writel(hsotg, gpwrdn, GPWRDN);
udelay(10);
hprt0 = hr->hprt0;
hprt0 |= HPRT0_PWR;
hprt0 &= ~HPRT0_ENA;
hprt0 &= ~HPRT0_SUSP;
- dwc2_writel(hprt0, hsotg->regs + HPRT0);
+ dwc2_writel(hsotg, hprt0, HPRT0);
hprt0 = hr->hprt0;
hprt0 |= HPRT0_PWR;
@@ -5573,32 +5679,32 @@ int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup,
if (reset) {
hprt0 |= HPRT0_RST;
- dwc2_writel(hprt0, hsotg->regs + HPRT0);
+ dwc2_writel(hsotg, hprt0, HPRT0);
/* Wait for Resume time and then program HPRT again */
mdelay(60);
hprt0 &= ~HPRT0_RST;
- dwc2_writel(hprt0, hsotg->regs + HPRT0);
+ dwc2_writel(hsotg, hprt0, HPRT0);
} else {
hprt0 |= HPRT0_RES;
- dwc2_writel(hprt0, hsotg->regs + HPRT0);
+ dwc2_writel(hsotg, hprt0, HPRT0);
/* Wait for Resume time and then program HPRT again */
mdelay(100);
hprt0 &= ~HPRT0_RES;
- dwc2_writel(hprt0, hsotg->regs + HPRT0);
+ dwc2_writel(hsotg, hprt0, HPRT0);
}
/* Clear all interrupt status */
- hprt0 = dwc2_readl(hsotg->regs + HPRT0);
+ hprt0 = dwc2_readl(hsotg, HPRT0);
hprt0 |= HPRT0_CONNDET;
hprt0 |= HPRT0_ENACHG;
hprt0 &= ~HPRT0_ENA;
- dwc2_writel(hprt0, hsotg->regs + HPRT0);
+ dwc2_writel(hsotg, hprt0, HPRT0);
- hprt0 = dwc2_readl(hsotg->regs + HPRT0);
+ hprt0 = dwc2_readl(hsotg, HPRT0);
/* Clear all pending interupts */
- dwc2_writel(0xffffffff, hsotg->regs + GINTSTS);
+ dwc2_writel(hsotg, 0xffffffff, GINTSTS);
/* Restore global registers */
ret = dwc2_restore_global_registers(hsotg);
@@ -5616,6 +5722,8 @@ int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup,
return ret;
}
+ dwc2_hcd_rem_wakeup(hsotg);
+
hsotg->hibernated = 0;
hsotg->bus_suspended = 0;
hsotg->lx_state = DWC2_L0;
diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h
index 96a9da5fb202..3f9bccc95add 100644
--- a/drivers/usb/dwc2/hcd.h
+++ b/drivers/usb/dwc2/hcd.h
@@ -76,11 +76,13 @@ struct dwc2_qh;
* (micro)frame
* @xfer_buf: Pointer to current transfer buffer position
* @xfer_dma: DMA address of xfer_buf
+ * @align_buf: In Buffer DMA mode this will be used if xfer_buf is not
+ * DWORD aligned
* @xfer_len: Total number of bytes to transfer
* @xfer_count: Number of bytes transferred so far
* @start_pkt_count: Packet count at start of transfer
* @xfer_started: True if the transfer has been started
- * @ping: True if a PING request should be issued on this channel
+ * @do_ping: True if a PING request should be issued on this channel
* @error_state: True if the error count for this transaction is non-zero
* @halt_on_queue: True if this channel should be halted the next time a
* request is queued for the channel. This is necessary in
@@ -102,7 +104,7 @@ struct dwc2_qh;
* @schinfo: Scheduling micro-frame bitmap
* @ntd: Number of transfer descriptors for the transfer
* @halt_status: Reason for halting the host channel
- * @hcint Contents of the HCINT register when the interrupt came
+ * @hcint: Contents of the HCINT register when the interrupt came
* @qh: QH for the transfer being processed by this channel
* @hc_list_entry: For linking to list of host channels
* @desc_list_addr: Current QH's descriptor list DMA address
@@ -133,6 +135,7 @@ struct dwc2_host_chan {
u8 *xfer_buf;
dma_addr_t xfer_dma;
+ dma_addr_t align_buf;
u32 xfer_len;
u32 xfer_count;
u16 start_pkt_count;
@@ -237,7 +240,7 @@ struct dwc2_tt {
/**
* struct dwc2_hs_transfer_time - Info about a transfer on the high speed bus.
*
- * @start_schedule_usecs: The start time on the main bus schedule. Note that
+ * @start_schedule_us: The start time on the main bus schedule. Note that
* the main bus schedule is tightly packed and this
* time should be interpreted as tightly packed (so
* uFrame 0 starts at 0 us, uFrame 1 starts at 100 us
@@ -301,8 +304,10 @@ struct dwc2_hs_transfer_time {
* "struct dwc2_tt". Not used if this device is high
* speed. Note that this is in "schedule slice" which
* is tightly packed.
- * @ls_duration_us: Duration on the low speed bus schedule.
* @ntd: Actual number of transfer descriptors in a list
+ * @dw_align_buf: Used instead of original buffer if its physical address
+ * is not dword-aligned
+ * @dw_align_buf_dma: DMA address for dw_align_buf
* @qtd_list: List of QTDs for this QH
* @channel: Host channel currently processing transfers for this QH
* @qh_list_entry: Entry for QH in either the periodic or non-periodic
@@ -315,7 +320,7 @@ struct dwc2_hs_transfer_time {
* descriptor
* @unreserve_timer: Timer for releasing periodic reservation.
* @wait_timer: Timer used to wait before re-queuing.
- * @dwc2_tt: Pointer to our tt info (or NULL if no tt).
+ * @dwc_tt: Pointer to our tt info (or NULL if no tt).
* @ttport: Port number within our tt.
* @tt_buffer_dirty True if clear_tt_buffer_complete is pending
* @unreserve_pending: True if we planned to unreserve but haven't yet.
@@ -325,6 +330,7 @@ struct dwc2_hs_transfer_time {
* periodic transfers and is ignored for periodic ones.
* @wait_timer_cancel: Set to true to cancel the wait_timer.
*
+ * @tt_buffer_dirty: True if EP's TT buffer is not clean.
* A Queue Head (QH) holds the static characteristics of an endpoint and
* maintains a list of transfers (QTDs) for that endpoint. A QH structure may
* be entered in either the non-periodic or periodic schedule.
@@ -350,6 +356,8 @@ struct dwc2_qh {
struct dwc2_hs_transfer_time hs_transfers[DWC2_HS_SCHEDULE_UFRAMES];
u32 ls_start_schedule_slice;
u16 ntd;
+ u8 *dw_align_buf;
+ dma_addr_t dw_align_buf_dma;
struct list_head qtd_list;
struct dwc2_host_chan *channel;
struct list_head qh_list_entry;
@@ -400,6 +408,10 @@ struct dwc2_qh {
* @urb: URB for this transfer
* @qh: Queue head for this QTD
* @qtd_list_entry: For linking to the QH's list of QTDs
+ * @isoc_td_first: Index of first activated isochronous transfer
+ * descriptor in Descriptor DMA mode
+ * @isoc_td_last: Index of last activated isochronous transfer
+ * descriptor in Descriptor DMA mode
*
* A Queue Transfer Descriptor (QTD) holds the state of a bulk, control,
* interrupt, or isochronous transfer. A single QTD is created for each URB
@@ -457,10 +469,10 @@ static inline struct usb_hcd *dwc2_hsotg_to_hcd(struct dwc2_hsotg *hsotg)
*/
static inline void disable_hc_int(struct dwc2_hsotg *hsotg, int chnum, u32 intr)
{
- u32 mask = dwc2_readl(hsotg->regs + HCINTMSK(chnum));
+ u32 mask = dwc2_readl(hsotg, HCINTMSK(chnum));
mask &= ~intr;
- dwc2_writel(mask, hsotg->regs + HCINTMSK(chnum));
+ dwc2_writel(hsotg, mask, HCINTMSK(chnum));
}
void dwc2_hc_cleanup(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan);
@@ -475,7 +487,7 @@ void dwc2_hc_start_transfer_ddma(struct dwc2_hsotg *hsotg,
*/
static inline u32 dwc2_read_hprt0(struct dwc2_hsotg *hsotg)
{
- u32 hprt0 = dwc2_readl(hsotg->regs + HPRT0);
+ u32 hprt0 = dwc2_readl(hsotg, HPRT0);
hprt0 &= ~(HPRT0_ENA | HPRT0_CONNDET | HPRT0_ENACHG | HPRT0_OVRCURRCHG);
return hprt0;
@@ -678,8 +690,8 @@ static inline u16 dwc2_micro_frame_num(u16 frame)
*/
static inline u32 dwc2_read_core_intr(struct dwc2_hsotg *hsotg)
{
- return dwc2_readl(hsotg->regs + GINTSTS) &
- dwc2_readl(hsotg->regs + GINTMSK);
+ return dwc2_readl(hsotg, GINTSTS) &
+ dwc2_readl(hsotg, GINTMSK);
}
static inline u32 dwc2_hcd_urb_get_status(struct dwc2_hcd_urb *dwc2_urb)
diff --git a/drivers/usb/dwc2/hcd_ddma.c b/drivers/usb/dwc2/hcd_ddma.c
index 28c8898b3b66..a858b5f9c1d6 100644
--- a/drivers/usb/dwc2/hcd_ddma.c
+++ b/drivers/usb/dwc2/hcd_ddma.c
@@ -185,19 +185,19 @@ static void dwc2_per_sched_enable(struct dwc2_hsotg *hsotg, u32 fr_list_en)
spin_lock_irqsave(&hsotg->lock, flags);
- hcfg = dwc2_readl(hsotg->regs + HCFG);
+ hcfg = dwc2_readl(hsotg, HCFG);
if (hcfg & HCFG_PERSCHEDENA) {
/* already enabled */
spin_unlock_irqrestore(&hsotg->lock, flags);
return;
}
- dwc2_writel(hsotg->frame_list_dma, hsotg->regs + HFLBADDR);
+ dwc2_writel(hsotg, hsotg->frame_list_dma, HFLBADDR);
hcfg &= ~HCFG_FRLISTEN_MASK;
hcfg |= fr_list_en | HCFG_PERSCHEDENA;
dev_vdbg(hsotg->dev, "Enabling Periodic schedule\n");
- dwc2_writel(hcfg, hsotg->regs + HCFG);
+ dwc2_writel(hsotg, hcfg, HCFG);
spin_unlock_irqrestore(&hsotg->lock, flags);
}
@@ -209,7 +209,7 @@ static void dwc2_per_sched_disable(struct dwc2_hsotg *hsotg)
spin_lock_irqsave(&hsotg->lock, flags);
- hcfg = dwc2_readl(hsotg->regs + HCFG);
+ hcfg = dwc2_readl(hsotg, HCFG);
if (!(hcfg & HCFG_PERSCHEDENA)) {
/* already disabled */
spin_unlock_irqrestore(&hsotg->lock, flags);
@@ -218,7 +218,7 @@ static void dwc2_per_sched_disable(struct dwc2_hsotg *hsotg)
hcfg &= ~HCFG_PERSCHEDENA;
dev_vdbg(hsotg->dev, "Disabling Periodic schedule\n");
- dwc2_writel(hcfg, hsotg->regs + HCFG);
+ dwc2_writel(hsotg, hcfg, HCFG);
spin_unlock_irqrestore(&hsotg->lock, flags);
}
@@ -332,6 +332,7 @@ static void dwc2_release_channel_ddma(struct dwc2_hsotg *hsotg,
*
* @hsotg: The HCD state structure for the DWC OTG controller
* @qh: The QH to init
+ * @mem_flags: Indicates the type of memory allocation
*
* Return: 0 if successful, negative error code otherwise
*
diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c
index a5dfd9d8bd9a..88b5dcf3aefc 100644
--- a/drivers/usb/dwc2/hcd_intr.c
+++ b/drivers/usb/dwc2/hcd_intr.c
@@ -144,7 +144,7 @@ static void dwc2_sof_intr(struct dwc2_hsotg *hsotg)
enum dwc2_transaction_type tr_type;
/* Clear interrupt */
- dwc2_writel(GINTSTS_SOF, hsotg->regs + GINTSTS);
+ dwc2_writel(hsotg, GINTSTS_SOF, GINTSTS);
#ifdef DEBUG_SOF
dev_vdbg(hsotg->dev, "--Start of Frame Interrupt--\n");
@@ -191,7 +191,7 @@ static void dwc2_rx_fifo_level_intr(struct dwc2_hsotg *hsotg)
if (dbg_perio())
dev_vdbg(hsotg->dev, "--RxFIFO Level Interrupt--\n");
- grxsts = dwc2_readl(hsotg->regs + GRXSTSP);
+ grxsts = dwc2_readl(hsotg, GRXSTSP);
chnum = (grxsts & GRXSTS_HCHNUM_MASK) >> GRXSTS_HCHNUM_SHIFT;
chan = hsotg->hc_ptr_array[chnum];
if (!chan) {
@@ -274,11 +274,11 @@ static void dwc2_hprt0_enable(struct dwc2_hsotg *hsotg, u32 hprt0,
dev_vdbg(hsotg->dev, "%s(%p)\n", __func__, hsotg);
/* Every time when port enables calculate HFIR.FrInterval */
- hfir = dwc2_readl(hsotg->regs + HFIR);
+ hfir = dwc2_readl(hsotg, HFIR);
hfir &= ~HFIR_FRINT_MASK;
hfir |= dwc2_calc_frame_interval(hsotg) << HFIR_FRINT_SHIFT &
HFIR_FRINT_MASK;
- dwc2_writel(hfir, hsotg->regs + HFIR);
+ dwc2_writel(hsotg, hfir, HFIR);
/* Check if we need to adjust the PHY clock speed for low power */
if (!params->host_support_fs_ls_low_power) {
@@ -287,7 +287,7 @@ static void dwc2_hprt0_enable(struct dwc2_hsotg *hsotg, u32 hprt0,
return;
}
- usbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
+ usbcfg = dwc2_readl(hsotg, GUSBCFG);
prtspd = (hprt0 & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT;
if (prtspd == HPRT0_SPD_LOW_SPEED || prtspd == HPRT0_SPD_FULL_SPEED) {
@@ -295,11 +295,11 @@ static void dwc2_hprt0_enable(struct dwc2_hsotg *hsotg, u32 hprt0,
if (!(usbcfg & GUSBCFG_PHY_LP_CLK_SEL)) {
/* Set PHY low power clock select for FS/LS devices */
usbcfg |= GUSBCFG_PHY_LP_CLK_SEL;
- dwc2_writel(usbcfg, hsotg->regs + GUSBCFG);
+ dwc2_writel(hsotg, usbcfg, GUSBCFG);
do_reset = 1;
}
- hcfg = dwc2_readl(hsotg->regs + HCFG);
+ hcfg = dwc2_readl(hsotg, HCFG);
fslspclksel = (hcfg & HCFG_FSLSPCLKSEL_MASK) >>
HCFG_FSLSPCLKSEL_SHIFT;
@@ -312,7 +312,7 @@ static void dwc2_hprt0_enable(struct dwc2_hsotg *hsotg, u32 hprt0,
fslspclksel = HCFG_FSLSPCLKSEL_6_MHZ;
hcfg &= ~HCFG_FSLSPCLKSEL_MASK;
hcfg |= fslspclksel << HCFG_FSLSPCLKSEL_SHIFT;
- dwc2_writel(hcfg, hsotg->regs + HCFG);
+ dwc2_writel(hsotg, hcfg, HCFG);
do_reset = 1;
}
} else {
@@ -323,7 +323,7 @@ static void dwc2_hprt0_enable(struct dwc2_hsotg *hsotg, u32 hprt0,
fslspclksel = HCFG_FSLSPCLKSEL_48_MHZ;
hcfg &= ~HCFG_FSLSPCLKSEL_MASK;
hcfg |= fslspclksel << HCFG_FSLSPCLKSEL_SHIFT;
- dwc2_writel(hcfg, hsotg->regs + HCFG);
+ dwc2_writel(hsotg, hcfg, HCFG);
do_reset = 1;
}
}
@@ -331,14 +331,14 @@ static void dwc2_hprt0_enable(struct dwc2_hsotg *hsotg, u32 hprt0,
/* Not low power */
if (usbcfg & GUSBCFG_PHY_LP_CLK_SEL) {
usbcfg &= ~GUSBCFG_PHY_LP_CLK_SEL;
- dwc2_writel(usbcfg, hsotg->regs + GUSBCFG);
+ dwc2_writel(hsotg, usbcfg, GUSBCFG);
do_reset = 1;
}
}
if (do_reset) {
*hprt0_modify |= HPRT0_RST;
- dwc2_writel(*hprt0_modify, hsotg->regs + HPRT0);
+ dwc2_writel(hsotg, *hprt0_modify, HPRT0);
queue_delayed_work(hsotg->wq_otg, &hsotg->reset_work,
msecs_to_jiffies(60));
} else {
@@ -359,7 +359,7 @@ static void dwc2_port_intr(struct dwc2_hsotg *hsotg)
dev_vdbg(hsotg->dev, "--Port Interrupt--\n");
- hprt0 = dwc2_readl(hsotg->regs + HPRT0);
+ hprt0 = dwc2_readl(hsotg, HPRT0);
hprt0_modify = hprt0;
/*
@@ -374,7 +374,7 @@ static void dwc2_port_intr(struct dwc2_hsotg *hsotg)
* Set flag and clear if detected
*/
if (hprt0 & HPRT0_CONNDET) {
- dwc2_writel(hprt0_modify | HPRT0_CONNDET, hsotg->regs + HPRT0);
+ dwc2_writel(hsotg, hprt0_modify | HPRT0_CONNDET, HPRT0);
dev_vdbg(hsotg->dev,
"--Port Interrupt HPRT0=0x%08x Port Connect Detected--\n",
@@ -392,7 +392,7 @@ static void dwc2_port_intr(struct dwc2_hsotg *hsotg)
* Clear if detected - Set internal flag if disabled
*/
if (hprt0 & HPRT0_ENACHG) {
- dwc2_writel(hprt0_modify | HPRT0_ENACHG, hsotg->regs + HPRT0);
+ dwc2_writel(hsotg, hprt0_modify | HPRT0_ENACHG, HPRT0);
dev_vdbg(hsotg->dev,
" --Port Interrupt HPRT0=0x%08x Port Enable Changed (now %d)--\n",
hprt0, !!(hprt0 & HPRT0_ENA));
@@ -406,17 +406,17 @@ static void dwc2_port_intr(struct dwc2_hsotg *hsotg)
hsotg->params.dma_desc_enable = false;
hsotg->new_connection = false;
- hcfg = dwc2_readl(hsotg->regs + HCFG);
+ hcfg = dwc2_readl(hsotg, HCFG);
hcfg &= ~HCFG_DESCDMA;
- dwc2_writel(hcfg, hsotg->regs + HCFG);
+ dwc2_writel(hsotg, hcfg, HCFG);
}
}
}
/* Overcurrent Change Interrupt */
if (hprt0 & HPRT0_OVRCURRCHG) {
- dwc2_writel(hprt0_modify | HPRT0_OVRCURRCHG,
- hsotg->regs + HPRT0);
+ dwc2_writel(hsotg, hprt0_modify | HPRT0_OVRCURRCHG,
+ HPRT0);
dev_vdbg(hsotg->dev,
" --Port Interrupt HPRT0=0x%08x Port Overcurrent Changed--\n",
hprt0);
@@ -441,7 +441,7 @@ static u32 dwc2_get_actual_xfer_length(struct dwc2_hsotg *hsotg,
{
u32 hctsiz, count, length;
- hctsiz = dwc2_readl(hsotg->regs + HCTSIZ(chnum));
+ hctsiz = dwc2_readl(hsotg, HCTSIZ(chnum));
if (halt_status == DWC2_HC_XFER_COMPLETE) {
if (chan->ep_is_in) {
@@ -478,6 +478,12 @@ static u32 dwc2_get_actual_xfer_length(struct dwc2_hsotg *hsotg,
* of the URB based on the number of bytes transferred via the host channel.
* Sets the URB status if the data transfer is finished.
*
+ * @hsotg: Programming view of the DWC_otg controller
+ * @chan: Programming view of host channel
+ * @chnum: Channel number
+ * @urb: Processing URB
+ * @qtd: Queue transfer descriptor
+ *
* Return: 1 if the data transfer specified by the URB is completely finished,
* 0 otherwise
*/
@@ -512,7 +518,7 @@ static int dwc2_update_urb_state(struct dwc2_hsotg *hsotg,
urb->status = 0;
}
- hctsiz = dwc2_readl(hsotg->regs + HCTSIZ(chnum));
+ hctsiz = dwc2_readl(hsotg, HCTSIZ(chnum));
dev_vdbg(hsotg->dev, "DWC_otg: %s: %s, channel %d\n",
__func__, (chan->ep_is_in ? "IN" : "OUT"), chnum);
dev_vdbg(hsotg->dev, " chan->xfer_len %d\n", chan->xfer_len);
@@ -535,7 +541,7 @@ void dwc2_hcd_save_data_toggle(struct dwc2_hsotg *hsotg,
struct dwc2_host_chan *chan, int chnum,
struct dwc2_qtd *qtd)
{
- u32 hctsiz = dwc2_readl(hsotg->regs + HCTSIZ(chnum));
+ u32 hctsiz = dwc2_readl(hsotg, HCTSIZ(chnum));
u32 pid = (hctsiz & TSIZ_SC_MC_PID_MASK) >> TSIZ_SC_MC_PID_SHIFT;
if (chan->ep_type != USB_ENDPOINT_XFER_CONTROL) {
@@ -566,6 +572,12 @@ void dwc2_hcd_save_data_toggle(struct dwc2_hsotg *hsotg,
* halt_status. Completes the Isochronous URB if all the URB frames have been
* completed.
*
+ * @hsotg: Programming view of the DWC_otg controller
+ * @chan: Programming view of host channel
+ * @chnum: Channel number
+ * @halt_status: Reason for halting a host channel
+ * @qtd: Queue transfer descriptor
+ *
* Return: DWC2_HC_XFER_COMPLETE if there are more frames remaining to be
* transferred in the URB. Otherwise return DWC2_HC_XFER_URB_COMPLETE.
*/
@@ -768,9 +780,9 @@ cleanup:
}
}
- haintmsk = dwc2_readl(hsotg->regs + HAINTMSK);
+ haintmsk = dwc2_readl(hsotg, HAINTMSK);
haintmsk &= ~(1 << chan->hc_num);
- dwc2_writel(haintmsk, hsotg->regs + HAINTMSK);
+ dwc2_writel(hsotg, haintmsk, HAINTMSK);
/* Try to queue more transfers now that there's a free channel */
tr_type = dwc2_hcd_select_transactions(hsotg);
@@ -817,9 +829,9 @@ static void dwc2_halt_channel(struct dwc2_hsotg *hsotg,
* is enabled so that the non-periodic schedule will
* be processed
*/
- gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
+ gintmsk = dwc2_readl(hsotg, GINTMSK);
gintmsk |= GINTSTS_NPTXFEMP;
- dwc2_writel(gintmsk, hsotg->regs + GINTMSK);
+ dwc2_writel(hsotg, gintmsk, GINTMSK);
} else {
dev_vdbg(hsotg->dev, "isoc/intr\n");
/*
@@ -836,9 +848,9 @@ static void dwc2_halt_channel(struct dwc2_hsotg *hsotg,
* enabled so that the periodic schedule will be
* processed
*/
- gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
+ gintmsk = dwc2_readl(hsotg, GINTMSK);
gintmsk |= GINTSTS_PTXFEMP;
- dwc2_writel(gintmsk, hsotg->regs + GINTMSK);
+ dwc2_writel(hsotg, gintmsk, GINTMSK);
}
}
}
@@ -903,7 +915,7 @@ static void dwc2_complete_periodic_xfer(struct dwc2_hsotg *hsotg,
struct dwc2_qtd *qtd,
enum dwc2_halt_status halt_status)
{
- u32 hctsiz = dwc2_readl(hsotg->regs + HCTSIZ(chnum));
+ u32 hctsiz = dwc2_readl(hsotg, HCTSIZ(chnum));
qtd->error_count = 0;
@@ -930,17 +942,24 @@ static int dwc2_xfercomp_isoc_split_in(struct dwc2_hsotg *hsotg,
frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index];
len = dwc2_get_actual_xfer_length(hsotg, chan, chnum, qtd,
DWC2_HC_XFER_COMPLETE, NULL);
- if (!len) {
+ if (!len && !qtd->isoc_split_offset) {
qtd->complete_split = 0;
- qtd->isoc_split_offset = 0;
return 0;
}
frame_desc->actual_length += len;
+ if (chan->align_buf) {
+ dev_vdbg(hsotg->dev, "non-aligned buffer\n");
+ dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma,
+ DWC2_KMEM_UNALIGNED_BUF_SIZE, DMA_FROM_DEVICE);
+ memcpy(qtd->urb->buf + (chan->xfer_dma - qtd->urb->dma),
+ chan->qh->dw_align_buf, len);
+ }
+
qtd->isoc_split_offset += len;
- hctsiz = dwc2_readl(hsotg->regs + HCTSIZ(chnum));
+ hctsiz = dwc2_readl(hsotg, HCTSIZ(chnum));
pid = (hctsiz & TSIZ_SC_MC_PID_MASK) >> TSIZ_SC_MC_PID_SHIFT;
if (frame_desc->actual_length >= frame_desc->length || pid == 0) {
@@ -1166,7 +1185,7 @@ static void dwc2_update_urb_state_abn(struct dwc2_hsotg *hsotg,
urb->actual_length += xfer_length;
- hctsiz = dwc2_readl(hsotg->regs + HCTSIZ(chnum));
+ hctsiz = dwc2_readl(hsotg, HCTSIZ(chnum));
dev_vdbg(hsotg->dev, "DWC_otg: %s: %s, channel %d\n",
__func__, (chan->ep_is_in ? "IN" : "OUT"), chnum);
dev_vdbg(hsotg->dev, " chan->start_pkt_count %d\n",
@@ -1212,7 +1231,10 @@ static void dwc2_hc_nak_intr(struct dwc2_hsotg *hsotg,
* avoid interrupt storms we'll wait before retrying if we've got
* several NAKs. If we didn't do this we'd retry directly from the
* interrupt handler and could end up quickly getting another
- * interrupt (another NAK), which we'd retry.
+ * interrupt (another NAK), which we'd retry. Note that we do not
+ * delay retries for IN parts of control requests, as those are expected
+ * to complete fairly quickly, and if we delay them we risk confusing
+ * the device and cause it issue STALL.
*
* Note that in DMA mode software only gets involved to re-send NAKed
* transfers for split transactions, so we only need to apply this
@@ -1225,7 +1247,9 @@ static void dwc2_hc_nak_intr(struct dwc2_hsotg *hsotg,
qtd->error_count = 0;
qtd->complete_split = 0;
qtd->num_naks++;
- qtd->qh->want_wait = qtd->num_naks >= DWC2_NAKS_BEFORE_DELAY;
+ qtd->qh->want_wait = qtd->num_naks >= DWC2_NAKS_BEFORE_DELAY &&
+ !(chan->ep_type == USB_ENDPOINT_XFER_CONTROL &&
+ chan->ep_is_in);
dwc2_halt_channel(hsotg, chan, qtd, DWC2_HC_XFER_NAK);
goto handle_nak_done;
}
@@ -1542,10 +1566,10 @@ static void dwc2_hc_ahberr_intr(struct dwc2_hsotg *hsotg,
dwc2_hc_handle_tt_clear(hsotg, chan, qtd);
- hcchar = dwc2_readl(hsotg->regs + HCCHAR(chnum));
- hcsplt = dwc2_readl(hsotg->regs + HCSPLT(chnum));
- hctsiz = dwc2_readl(hsotg->regs + HCTSIZ(chnum));
- hc_dma = dwc2_readl(hsotg->regs + HCDMA(chnum));
+ hcchar = dwc2_readl(hsotg, HCCHAR(chnum));
+ hcsplt = dwc2_readl(hsotg, HCSPLT(chnum));
+ hctsiz = dwc2_readl(hsotg, HCTSIZ(chnum));
+ hc_dma = dwc2_readl(hsotg, HCDMA(chnum));
dev_err(hsotg->dev, "AHB ERROR, Channel %d\n", chnum);
dev_err(hsotg->dev, " hcchar 0x%08x, hcsplt 0x%08x\n", hcchar, hcsplt);
@@ -1757,10 +1781,10 @@ static bool dwc2_halt_status_ok(struct dwc2_hsotg *hsotg,
* This code is here only as a check. This condition should
* never happen. Ignore the halt if it does occur.
*/
- hcchar = dwc2_readl(hsotg->regs + HCCHAR(chnum));
- hctsiz = dwc2_readl(hsotg->regs + HCTSIZ(chnum));
- hcintmsk = dwc2_readl(hsotg->regs + HCINTMSK(chnum));
- hcsplt = dwc2_readl(hsotg->regs + HCSPLT(chnum));
+ hcchar = dwc2_readl(hsotg, HCCHAR(chnum));
+ hctsiz = dwc2_readl(hsotg, HCTSIZ(chnum));
+ hcintmsk = dwc2_readl(hsotg, HCINTMSK(chnum));
+ hcsplt = dwc2_readl(hsotg, HCSPLT(chnum));
dev_dbg(hsotg->dev,
"%s: chan->halt_status DWC2_HC_XFER_NO_HALT_STATUS,\n",
__func__);
@@ -1784,7 +1808,7 @@ static bool dwc2_halt_status_ok(struct dwc2_hsotg *hsotg,
* when the halt interrupt occurs. Halt the channel again if it does
* occur.
*/
- hcchar = dwc2_readl(hsotg->regs + HCCHAR(chnum));
+ hcchar = dwc2_readl(hsotg, HCCHAR(chnum));
if (hcchar & HCCHAR_CHDIS) {
dev_warn(hsotg->dev,
"%s: hcchar.chdis set unexpectedly, hcchar 0x%08x, trying to halt again\n",
@@ -1844,7 +1868,7 @@ static void dwc2_hc_chhltd_intr_dma(struct dwc2_hsotg *hsotg,
return;
}
- hcintmsk = dwc2_readl(hsotg->regs + HCINTMSK(chnum));
+ hcintmsk = dwc2_readl(hsotg, HCINTMSK(chnum));
if (chan->hcint & HCINTMSK_XFERCOMPL) {
/*
@@ -1939,7 +1963,7 @@ static void dwc2_hc_chhltd_intr_dma(struct dwc2_hsotg *hsotg,
dev_err(hsotg->dev,
"hcint 0x%08x, intsts 0x%08x\n",
chan->hcint,
- dwc2_readl(hsotg->regs + GINTSTS));
+ dwc2_readl(hsotg, GINTSTS));
goto error;
}
}
@@ -2012,11 +2036,11 @@ static void dwc2_hc_n_intr(struct dwc2_hsotg *hsotg, int chnum)
chan = hsotg->hc_ptr_array[chnum];
- hcint = dwc2_readl(hsotg->regs + HCINT(chnum));
- hcintmsk = dwc2_readl(hsotg->regs + HCINTMSK(chnum));
+ hcint = dwc2_readl(hsotg, HCINT(chnum));
+ hcintmsk = dwc2_readl(hsotg, HCINTMSK(chnum));
if (!chan) {
dev_err(hsotg->dev, "## hc_ptr_array for channel is NULL ##\n");
- dwc2_writel(hcint, hsotg->regs + HCINT(chnum));
+ dwc2_writel(hsotg, hcint, HCINT(chnum));
return;
}
@@ -2028,7 +2052,7 @@ static void dwc2_hc_n_intr(struct dwc2_hsotg *hsotg, int chnum)
hcint, hcintmsk, hcint & hcintmsk);
}
- dwc2_writel(hcint, hsotg->regs + HCINT(chnum));
+ dwc2_writel(hsotg, hcint, HCINT(chnum));
/*
* If we got an interrupt after someone called
@@ -2163,7 +2187,7 @@ static void dwc2_hc_intr(struct dwc2_hsotg *hsotg)
int i;
struct dwc2_host_chan *chan, *chan_tmp;
- haint = dwc2_readl(hsotg->regs + HAINT);
+ haint = dwc2_readl(hsotg, HAINT);
if (dbg_perio()) {
dev_vdbg(hsotg->dev, "%s()\n", __func__);
@@ -2247,8 +2271,8 @@ irqreturn_t dwc2_handle_hcd_intr(struct dwc2_hsotg *hsotg)
"DWC OTG HCD Finished Servicing Interrupts\n");
dev_vdbg(hsotg->dev,
"DWC OTG HCD gintsts=0x%08x gintmsk=0x%08x\n",
- dwc2_readl(hsotg->regs + GINTSTS),
- dwc2_readl(hsotg->regs + GINTMSK));
+ dwc2_readl(hsotg, GINTSTS),
+ dwc2_readl(hsotg, GINTMSK));
}
}
diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c
index e34ad5e65350..40839591d2ec 100644
--- a/drivers/usb/dwc2/hcd_queue.c
+++ b/drivers/usb/dwc2/hcd_queue.c
@@ -383,7 +383,7 @@ static unsigned long *dwc2_get_ls_map(struct dwc2_hsotg *hsotg,
/* Get the map and adjust if this is a multi_tt hub */
map = qh->dwc_tt->periodic_bitmaps;
if (qh->dwc_tt->usb_tt->multi)
- map += DWC2_ELEMENTS_PER_LS_BITMAP * qh->ttport;
+ map += DWC2_ELEMENTS_PER_LS_BITMAP * (qh->ttport - 1);
return map;
}
@@ -679,6 +679,7 @@ static int dwc2_hs_pmap_schedule(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
*
* @hsotg: The HCD state structure for the DWC OTG controller.
* @qh: QH for the periodic transfer.
+ * @index: Transfer index
*/
static void dwc2_hs_pmap_unschedule(struct dwc2_hsotg *hsotg,
struct dwc2_qh *qh, int index)
@@ -1276,7 +1277,7 @@ static void dwc2_do_unreserve(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
* release the reservation. This worker is called after the appropriate
* delay.
*
- * @work: Pointer to a qh unreserve_work.
+ * @t: Address to a qh unreserve_work.
*/
static void dwc2_unreserve_timer_fn(struct timer_list *t)
{
@@ -1509,7 +1510,7 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
bool ep_is_in = !!dwc2_hcd_is_pipe_in(&urb->pipe_info);
bool ep_is_isoc = (ep_type == USB_ENDPOINT_XFER_ISOC);
bool ep_is_int = (ep_type == USB_ENDPOINT_XFER_INT);
- u32 hprt = dwc2_readl(hsotg->regs + HPRT0);
+ u32 hprt = dwc2_readl(hsotg, HPRT0);
u32 prtspd = (hprt & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT;
bool do_split = (prtspd == HPRT0_SPD_HIGH_SPEED &&
dev_speed != USB_SPEED_HIGH);
@@ -1631,7 +1632,7 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
* @hsotg: The HCD state structure for the DWC OTG controller
* @urb: Holds the information about the device/endpoint needed
* to initialize the QH
- * @atomic_alloc: Flag to do atomic allocation if needed
+ * @mem_flags: Flags for allocating memory.
*
* Return: Pointer to the newly allocated QH, or NULL on error
*/
@@ -1695,6 +1696,9 @@ void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
if (qh->desc_list)
dwc2_hcd_qh_free_ddma(hsotg, qh);
+ else if (hsotg->unaligned_cache && qh->dw_align_buf)
+ kmem_cache_free(hsotg->unaligned_cache, qh->dw_align_buf);
+
kfree(qh);
}
@@ -1743,9 +1747,9 @@ int dwc2_hcd_qh_add(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
if (status)
return status;
if (!hsotg->periodic_qh_count) {
- intr_mask = dwc2_readl(hsotg->regs + GINTMSK);
+ intr_mask = dwc2_readl(hsotg, GINTMSK);
intr_mask |= GINTSTS_SOF;
- dwc2_writel(intr_mask, hsotg->regs + GINTMSK);
+ dwc2_writel(hsotg, intr_mask, GINTMSK);
}
hsotg->periodic_qh_count++;
@@ -1784,9 +1788,9 @@ void dwc2_hcd_qh_unlink(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
hsotg->periodic_qh_count--;
if (!hsotg->periodic_qh_count &&
!hsotg->params.dma_desc_enable) {
- intr_mask = dwc2_readl(hsotg->regs + GINTMSK);
+ intr_mask = dwc2_readl(hsotg, GINTMSK);
intr_mask &= ~GINTSTS_SOF;
- dwc2_writel(intr_mask, hsotg->regs + GINTMSK);
+ dwc2_writel(hsotg, intr_mask, GINTMSK);
}
}
diff --git a/drivers/usb/dwc2/hw.h b/drivers/usb/dwc2/hw.h
index 38391e48351f..2b1ea441b7d4 100644
--- a/drivers/usb/dwc2/hw.h
+++ b/drivers/usb/dwc2/hw.h
@@ -311,6 +311,8 @@
#define GHWCFG4_UTMI_PHY_DATA_WIDTH_MASK (0x3 << 14)
#define GHWCFG4_UTMI_PHY_DATA_WIDTH_SHIFT 14
#define GHWCFG4_ACG_SUPPORTED BIT(12)
+#define GHWCFG4_IPG_ISOC_SUPPORTED BIT(11)
+#define GHWCFG4_SERVICE_INTERVAL_SUPPORTED BIT(10)
#define GHWCFG4_UTMI_PHY_DATA_WIDTH_8 0
#define GHWCFG4_UTMI_PHY_DATA_WIDTH_16 1
#define GHWCFG4_UTMI_PHY_DATA_WIDTH_8_OR_16 2
@@ -403,6 +405,19 @@
#define ADPCTL_PRB_DSCHRG_MASK (0x3 << 0)
#define ADPCTL_PRB_DSCHRG_SHIFT 0
+#define GREFCLK HSOTG_REG(0x0064)
+#define GREFCLK_REFCLKPER_MASK (0x1ffff << 15)
+#define GREFCLK_REFCLKPER_SHIFT 15
+#define GREFCLK_REF_CLK_MODE BIT(14)
+#define GREFCLK_SOF_CNT_WKUP_ALERT_MASK (0x3ff)
+#define GREFCLK_SOF_CNT_WKUP_ALERT_SHIFT 0
+
+#define GINTMSK2 HSOTG_REG(0x0068)
+#define GINTMSK2_WKUP_ALERT_INT_MSK BIT(0)
+
+#define GINTSTS2 HSOTG_REG(0x006c)
+#define GINTSTS2_WKUP_ALERT_INT BIT(0)
+
#define HPTXFSIZ HSOTG_REG(0x100)
/* Use FIFOSIZE_* constants to access this register */
@@ -424,6 +439,7 @@
#define DCFG_EPMISCNT_SHIFT 18
#define DCFG_EPMISCNT_LIMIT 0x1f
#define DCFG_EPMISCNT(_x) ((_x) << 18)
+#define DCFG_IPG_ISOC_SUPPORDED BIT(17)
#define DCFG_PERFRINT_MASK (0x3 << 11)
#define DCFG_PERFRINT_SHIFT 11
#define DCFG_PERFRINT_LIMIT 0x3
@@ -441,6 +457,7 @@
#define DCFG_DEVSPD_FS48 3
#define DCTL HSOTG_REG(0x804)
+#define DCTL_SERVICE_INTERVAL_SUPPORTED BIT(19)
#define DCTL_PWRONPRGDONE BIT(11)
#define DCTL_CGOUTNAK BIT(10)
#define DCTL_SGOUTNAK BIT(9)
diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c
index f03e41879224..7c1b6938f212 100644
--- a/drivers/usb/dwc2/params.c
+++ b/drivers/usb/dwc2/params.c
@@ -47,7 +47,6 @@ static void dwc2_set_bcm_params(struct dwc2_hsotg *hsotg)
p->max_transfer_size = 65535;
p->max_packet_count = 511;
p->ahbcfg = 0x10;
- p->uframe_sched = false;
}
static void dwc2_set_his_params(struct dwc2_hsotg *hsotg)
@@ -68,8 +67,8 @@ static void dwc2_set_his_params(struct dwc2_hsotg *hsotg)
p->reload_ctl = false;
p->ahbcfg = GAHBCFG_HBSTLEN_INCR16 <<
GAHBCFG_HBSTLEN_SHIFT;
- p->uframe_sched = false;
p->change_speed_quirk = true;
+ p->power_down = false;
}
static void dwc2_set_rk_params(struct dwc2_hsotg *hsotg)
@@ -82,6 +81,7 @@ static void dwc2_set_rk_params(struct dwc2_hsotg *hsotg)
p->host_perio_tx_fifo_size = 256;
p->ahbcfg = GAHBCFG_HBSTLEN_INCR16 <<
GAHBCFG_HBSTLEN_SHIFT;
+ p->power_down = 0;
}
static void dwc2_set_ltq_params(struct dwc2_hsotg *hsotg)
@@ -111,7 +111,6 @@ static void dwc2_set_amlogic_params(struct dwc2_hsotg *hsotg)
p->phy_type = DWC2_PHY_TYPE_PARAM_UTMI;
p->ahbcfg = GAHBCFG_HBSTLEN_INCR8 <<
GAHBCFG_HBSTLEN_SHIFT;
- p->uframe_sched = false;
}
static void dwc2_set_amcc_params(struct dwc2_hsotg *hsotg)
@@ -133,7 +132,6 @@ static void dwc2_set_stm32f4x9_fsotg_params(struct dwc2_hsotg *hsotg)
p->max_packet_count = 256;
p->phy_type = DWC2_PHY_TYPE_PARAM_FS;
p->i2c_enable = false;
- p->uframe_sched = false;
p->activate_stm_fs_transceiver = true;
}
@@ -269,6 +267,9 @@ static void dwc2_set_param_power_down(struct dwc2_hsotg *hsotg)
/**
* dwc2_set_default_params() - Set all core parameters to their
* auto-detected default values.
+ *
+ * @hsotg: Programming view of the DWC_otg controller
+ *
*/
static void dwc2_set_default_params(struct dwc2_hsotg *hsotg)
{
@@ -298,9 +299,13 @@ static void dwc2_set_default_params(struct dwc2_hsotg *hsotg)
p->besl = true;
p->hird_threshold_en = true;
p->hird_threshold = 4;
+ p->ipg_isoc_en = false;
+ p->service_interval = false;
p->max_packet_count = hw->max_packet_count;
p->max_transfer_size = hw->max_transfer_size;
p->ahbcfg = GAHBCFG_HBSTLEN_INCR << GAHBCFG_HBSTLEN_SHIFT;
+ p->ref_clk_per = 33333;
+ p->sof_cnt_wkup_alert = 100;
if ((hsotg->dr_mode == USB_DR_MODE_HOST) ||
(hsotg->dr_mode == USB_DR_MODE_OTG)) {
@@ -338,6 +343,8 @@ static void dwc2_set_default_params(struct dwc2_hsotg *hsotg)
/**
* dwc2_get_device_properties() - Read in device properties.
*
+ * @hsotg: Programming view of the DWC_otg controller
+ *
* Read in the device properties and adjust core parameters if needed.
*/
static void dwc2_get_device_properties(struct dwc2_hsotg *hsotg)
@@ -549,7 +556,7 @@ static void dwc2_check_param_tx_fifo_sizes(struct dwc2_hsotg *hsotg)
}
#define CHECK_RANGE(_param, _min, _max, _def) do { \
- if ((hsotg->params._param) < (_min) || \
+ if ((int)(hsotg->params._param) < (_min) || \
(hsotg->params._param) > (_max)) { \
dev_warn(hsotg->dev, "%s: Invalid parameter %s=%d\n", \
__func__, #_param, hsotg->params._param); \
@@ -579,6 +586,7 @@ static void dwc2_check_params(struct dwc2_hsotg *hsotg)
CHECK_BOOL(enable_dynamic_fifo, hw->enable_dynamic_fifo);
CHECK_BOOL(en_multiple_tx_fifo, hw->en_multiple_tx_fifo);
CHECK_BOOL(i2c_enable, hw->i2c_enable);
+ CHECK_BOOL(ipg_isoc_en, hw->ipg_isoc_en);
CHECK_BOOL(acg_enable, hw->acg_enable);
CHECK_BOOL(reload_ctl, (hsotg->hw_params.snpsid > DWC2_CORE_REV_2_92a));
CHECK_BOOL(lpm, (hsotg->hw_params.snpsid >= DWC2_CORE_REV_2_80a));
@@ -588,6 +596,7 @@ static void dwc2_check_params(struct dwc2_hsotg *hsotg)
CHECK_BOOL(besl, (hsotg->hw_params.snpsid >= DWC2_CORE_REV_3_00a));
CHECK_BOOL(hird_threshold_en, hsotg->params.lpm);
CHECK_RANGE(hird_threshold, 0, hsotg->params.besl ? 12 : 7, 0);
+ CHECK_BOOL(service_interval, hw->service_interval_mode);
CHECK_RANGE(max_packet_count,
15, hw->max_packet_count,
hw->max_packet_count);
@@ -646,8 +655,8 @@ static void dwc2_get_host_hwparams(struct dwc2_hsotg *hsotg)
dwc2_force_mode(hsotg, true);
- gnptxfsiz = dwc2_readl(hsotg->regs + GNPTXFSIZ);
- hptxfsiz = dwc2_readl(hsotg->regs + HPTXFSIZ);
+ gnptxfsiz = dwc2_readl(hsotg, GNPTXFSIZ);
+ hptxfsiz = dwc2_readl(hsotg, HPTXFSIZ);
hw->host_nperio_tx_fifo_size = (gnptxfsiz & FIFOSIZE_DEPTH_MASK) >>
FIFOSIZE_DEPTH_SHIFT;
@@ -671,13 +680,13 @@ static void dwc2_get_dev_hwparams(struct dwc2_hsotg *hsotg)
dwc2_force_mode(hsotg, false);
- gnptxfsiz = dwc2_readl(hsotg->regs + GNPTXFSIZ);
+ gnptxfsiz = dwc2_readl(hsotg, GNPTXFSIZ);
fifo_count = dwc2_hsotg_tx_fifo_count(hsotg);
for (fifo = 1; fifo <= fifo_count; fifo++) {
hw->g_tx_fifo_size[fifo] =
- (dwc2_readl(hsotg->regs + DPTXFSIZN(fifo)) &
+ (dwc2_readl(hsotg, DPTXFSIZN(fifo)) &
FIFOSIZE_DEPTH_MASK) >> FIFOSIZE_DEPTH_SHIFT;
}
@@ -688,6 +697,9 @@ static void dwc2_get_dev_hwparams(struct dwc2_hsotg *hsotg)
/**
* During device initialization, read various hardware configuration
* registers and interpret the contents.
+ *
+ * @hsotg: Programming view of the DWC_otg controller
+ *
*/
int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
{
@@ -702,7 +714,7 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
* 0x45f4xxxx, 0x5531xxxx or 0x5532xxxx
*/
- hw->snpsid = dwc2_readl(hsotg->regs + GSNPSID);
+ hw->snpsid = dwc2_readl(hsotg, GSNPSID);
if ((hw->snpsid & GSNPSID_ID_MASK) != DWC2_OTG_ID &&
(hw->snpsid & GSNPSID_ID_MASK) != DWC2_FS_IOT_ID &&
(hw->snpsid & GSNPSID_ID_MASK) != DWC2_HS_IOT_ID) {
@@ -715,11 +727,11 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
hw->snpsid >> 12 & 0xf, hw->snpsid >> 8 & 0xf,
hw->snpsid >> 4 & 0xf, hw->snpsid & 0xf, hw->snpsid);
- hwcfg1 = dwc2_readl(hsotg->regs + GHWCFG1);
- hwcfg2 = dwc2_readl(hsotg->regs + GHWCFG2);
- hwcfg3 = dwc2_readl(hsotg->regs + GHWCFG3);
- hwcfg4 = dwc2_readl(hsotg->regs + GHWCFG4);
- grxfsiz = dwc2_readl(hsotg->regs + GRXFSIZ);
+ hwcfg1 = dwc2_readl(hsotg, GHWCFG1);
+ hwcfg2 = dwc2_readl(hsotg, GHWCFG2);
+ hwcfg3 = dwc2_readl(hsotg, GHWCFG3);
+ hwcfg4 = dwc2_readl(hsotg, GHWCFG4);
+ grxfsiz = dwc2_readl(hsotg, GRXFSIZ);
/* hwcfg1 */
hw->dev_ep_dirs = hwcfg1;
@@ -772,6 +784,9 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
hw->utmi_phy_data_width = (hwcfg4 & GHWCFG4_UTMI_PHY_DATA_WIDTH_MASK) >>
GHWCFG4_UTMI_PHY_DATA_WIDTH_SHIFT;
hw->acg_enable = !!(hwcfg4 & GHWCFG4_ACG_SUPPORTED);
+ hw->ipg_isoc_en = !!(hwcfg4 & GHWCFG4_IPG_ISOC_SUPPORTED);
+ hw->service_interval_mode = !!(hwcfg4 &
+ GHWCFG4_SERVICE_INTERVAL_SUPPORTED);
/* fifo sizes */
hw->rx_fifo_size = (grxfsiz & GRXFSIZ_DEPTH_MASK) >>
diff --git a/drivers/usb/dwc2/pci.c b/drivers/usb/dwc2/pci.c
index bea2e8ec0369..7afc10872f1f 100644
--- a/drivers/usb/dwc2/pci.c
+++ b/drivers/usb/dwc2/pci.c
@@ -77,6 +77,12 @@ static int dwc2_pci_quirks(struct pci_dev *pdev, struct platform_device *dwc2)
return 0;
}
+/**
+ * dwc2_pci_probe() - Provides the cleanup entry points for the DWC_otg PCI
+ * driver
+ *
+ * @pci: The programming view of DWC_otg PCI
+ */
static void dwc2_pci_remove(struct pci_dev *pci)
{
struct dwc2_pci_glue *glue = pci_get_drvdata(pci);
@@ -114,6 +120,7 @@ static int dwc2_pci_probe(struct pci_dev *pci,
dwc2 = platform_device_alloc("dwc2", PLATFORM_DEVID_AUTO);
if (!dwc2) {
dev_err(dev, "couldn't allocate dwc2 device\n");
+ ret = -ENOMEM;
goto err;
}
diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c
index 4c0819554bcd..c0b64d483552 100644
--- a/drivers/usb/dwc2/platform.c
+++ b/drivers/usb/dwc2/platform.c
@@ -353,6 +353,23 @@ static void dwc2_driver_shutdown(struct platform_device *dev)
}
/**
+ * dwc2_check_core_endianness() - Returns true if core and AHB have
+ * opposite endianness.
+ * @hsotg: Programming view of the DWC_otg controller.
+ */
+static bool dwc2_check_core_endianness(struct dwc2_hsotg *hsotg)
+{
+ u32 snpsid;
+
+ snpsid = ioread32(hsotg->regs + GSNPSID);
+ if ((snpsid & GSNPSID_ID_MASK) == DWC2_OTG_ID ||
+ (snpsid & GSNPSID_ID_MASK) == DWC2_FS_IOT_ID ||
+ (snpsid & GSNPSID_ID_MASK) == DWC2_HS_IOT_ID)
+ return false;
+ return true;
+}
+
+/**
* dwc2_driver_probe() - Called when the DWC_otg core is bound to the DWC_otg
* driver
*
@@ -415,10 +432,20 @@ static int dwc2_driver_probe(struct platform_device *dev)
if (retval)
return retval;
+ hsotg->vbus_supply = devm_regulator_get_optional(hsotg->dev, "vbus");
+ if (IS_ERR(hsotg->vbus_supply)) {
+ retval = PTR_ERR(hsotg->vbus_supply);
+ hsotg->vbus_supply = NULL;
+ if (retval != -ENODEV)
+ return retval;
+ }
+
retval = dwc2_lowlevel_hw_enable(hsotg);
if (retval)
return retval;
+ hsotg->needs_byte_swap = dwc2_check_core_endianness(hsotg);
+
retval = dwc2_get_dr_mode(hsotg);
if (retval)
goto error;
diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig
index ab8c0e0d3b60..1a0404fda596 100644
--- a/drivers/usb/dwc3/Kconfig
+++ b/drivers/usb/dwc3/Kconfig
@@ -74,11 +74,16 @@ config USB_DWC3_PCI
depends on USB_PCI && ACPI
default USB_DWC3
help
- If you're using the DesignWare Core IP with a PCIe, please say
- 'Y' or 'M' here.
+ If you're using the DesignWare Core IP with a PCIe (but not HAPS
+ platform), please say 'Y' or 'M' here.
- One such PCIe-based platform is Synopsys' PCIe HAPS model of
- this IP.
+config USB_DWC3_HAPS
+ tristate "Synopsys PCIe-based HAPS Platforms"
+ depends on USB_PCI
+ default USB_DWC3
+ help
+ If you're using the DesignWare Core IP with a Synopsys PCIe HAPS
+ platform, please say 'Y' or 'M' here.
config USB_DWC3_KEYSTONE
tristate "Texas Instruments Keystone2 Platforms"
@@ -106,4 +111,16 @@ config USB_DWC3_ST
inside (i.e. STiH407).
Say 'Y' or 'M' if you have one such device.
+config USB_DWC3_QCOM
+ tristate "Qualcomm Platform"
+ depends on EXTCON && (ARCH_QCOM || COMPILE_TEST)
+ depends on OF
+ default USB_DWC3
+ help
+ Some Qualcomm SoCs use DesignWare Core IP for USB2/3
+ functionality.
+ This driver also handles Qscratch wrapper which is needed
+ for peripheral mode support.
+ Say 'Y' or 'M' if you have one such device.
+
endif
diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile
index 025bc68094fc..6e3ef6144e5d 100644
--- a/drivers/usb/dwc3/Makefile
+++ b/drivers/usb/dwc3/Makefile
@@ -45,6 +45,8 @@ endif
obj-$(CONFIG_USB_DWC3_OMAP) += dwc3-omap.o
obj-$(CONFIG_USB_DWC3_EXYNOS) += dwc3-exynos.o
obj-$(CONFIG_USB_DWC3_PCI) += dwc3-pci.o
+obj-$(CONFIG_USB_DWC3_HAPS) += dwc3-haps.o
obj-$(CONFIG_USB_DWC3_KEYSTONE) += dwc3-keystone.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
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index a15648d25e30..2f2048aa5fde 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -8,6 +8,7 @@
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
*/
+#include <linux/clk.h>
#include <linux/version.h>
#include <linux/module.h>
#include <linux/kernel.h>
@@ -24,6 +25,7 @@
#include <linux/of.h>
#include <linux/acpi.h>
#include <linux/pinctrl/consumer.h>
+#include <linux/reset.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
@@ -76,6 +78,14 @@ static int dwc3_get_dr_mode(struct dwc3 *dwc)
mode = USB_DR_MODE_HOST;
else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET))
mode = USB_DR_MODE_PERIPHERAL;
+
+ /*
+ * dwc_usb31 does not support OTG mode. If the controller
+ * supports DRD but the dr_mode is not specified or set to OTG,
+ * then set the mode to peripheral.
+ */
+ if (mode == USB_DR_MODE_OTG && dwc3_is_usb31(dwc))
+ mode = USB_DR_MODE_PERIPHERAL;
}
if (mode != dwc->dr_mode) {
@@ -266,6 +276,12 @@ done:
return 0;
}
+static const struct clk_bulk_data dwc3_core_clks[] = {
+ { .id = "ref" },
+ { .id = "bus_early" },
+ { .id = "suspend" },
+};
+
/*
* dwc3_frame_length_adjustment - Adjusts frame length if required
* @dwc3: Pointer to our controller context structure
@@ -667,6 +683,9 @@ static void dwc3_core_exit(struct dwc3 *dwc)
usb_phy_set_suspend(dwc->usb3_phy, 1);
phy_power_off(dwc->usb2_generic_phy);
phy_power_off(dwc->usb3_generic_phy);
+ clk_bulk_disable(dwc->num_clks, dwc->clks);
+ clk_bulk_unprepare(dwc->num_clks, dwc->clks);
+ reset_control_assert(dwc->reset);
}
static bool dwc3_core_is_valid(struct dwc3 *dwc)
@@ -737,7 +756,7 @@ static void dwc3_core_setup_global_control(struct dwc3 *dwc)
/* check if current dwc3 is on simulation board */
if (dwc->hwparams.hwparams6 & DWC3_GHWPARAMS6_EN_FPGA) {
- dev_info(dwc->dev, "Running with FPGA optmizations\n");
+ dev_info(dwc->dev, "Running with FPGA optimizations\n");
dwc->is_fpga = true;
}
@@ -767,6 +786,98 @@ static void dwc3_core_setup_global_control(struct dwc3 *dwc)
static int dwc3_core_get_phy(struct dwc3 *dwc);
static int dwc3_core_ulpi_init(struct dwc3 *dwc);
+/* set global incr burst type configuration registers */
+static void dwc3_set_incr_burst_type(struct dwc3 *dwc)
+{
+ struct device *dev = dwc->dev;
+ /* incrx_mode : for INCR burst type. */
+ bool incrx_mode;
+ /* incrx_size : for size of INCRX burst. */
+ u32 incrx_size;
+ u32 *vals;
+ u32 cfg;
+ int ntype;
+ int ret;
+ int i;
+
+ cfg = dwc3_readl(dwc->regs, DWC3_GSBUSCFG0);
+
+ /*
+ * Handle property "snps,incr-burst-type-adjustment".
+ * Get the number of value from this property:
+ * result <= 0, means this property is not supported.
+ * result = 1, means INCRx burst mode supported.
+ * result > 1, means undefined length burst mode supported.
+ */
+ ntype = device_property_read_u32_array(dev,
+ "snps,incr-burst-type-adjustment", NULL, 0);
+ if (ntype <= 0)
+ return;
+
+ vals = kcalloc(ntype, sizeof(u32), GFP_KERNEL);
+ if (!vals) {
+ dev_err(dev, "Error to get memory\n");
+ return;
+ }
+
+ /* Get INCR burst type, and parse it */
+ ret = device_property_read_u32_array(dev,
+ "snps,incr-burst-type-adjustment", vals, ntype);
+ if (ret) {
+ dev_err(dev, "Error to get property\n");
+ return;
+ }
+
+ incrx_size = *vals;
+
+ if (ntype > 1) {
+ /* INCRX (undefined length) burst mode */
+ incrx_mode = INCRX_UNDEF_LENGTH_BURST_MODE;
+ for (i = 1; i < ntype; i++) {
+ if (vals[i] > incrx_size)
+ incrx_size = vals[i];
+ }
+ } else {
+ /* INCRX burst mode */
+ incrx_mode = INCRX_BURST_MODE;
+ }
+
+ /* Enable Undefined Length INCR Burst and Enable INCRx Burst */
+ cfg &= ~DWC3_GSBUSCFG0_INCRBRST_MASK;
+ if (incrx_mode)
+ cfg |= DWC3_GSBUSCFG0_INCRBRSTENA;
+ switch (incrx_size) {
+ case 256:
+ cfg |= DWC3_GSBUSCFG0_INCR256BRSTENA;
+ break;
+ case 128:
+ cfg |= DWC3_GSBUSCFG0_INCR128BRSTENA;
+ break;
+ case 64:
+ cfg |= DWC3_GSBUSCFG0_INCR64BRSTENA;
+ break;
+ case 32:
+ cfg |= DWC3_GSBUSCFG0_INCR32BRSTENA;
+ break;
+ case 16:
+ cfg |= DWC3_GSBUSCFG0_INCR16BRSTENA;
+ break;
+ case 8:
+ cfg |= DWC3_GSBUSCFG0_INCR8BRSTENA;
+ break;
+ case 4:
+ cfg |= DWC3_GSBUSCFG0_INCR4BRSTENA;
+ break;
+ case 1:
+ break;
+ default:
+ dev_err(dev, "Invalid property\n");
+ break;
+ }
+
+ dwc3_writel(dwc->regs, DWC3_GSBUSCFG0, cfg);
+}
+
/**
* dwc3_core_init - Low-level initialization of DWC3 Core
* @dwc: Pointer to our controller context structure
@@ -829,6 +940,8 @@ static int dwc3_core_init(struct dwc3 *dwc)
/* Adjust Frame Length */
dwc3_frame_length_adjustment(dwc);
+ dwc3_set_incr_burst_type(dwc);
+
usb_phy_set_suspend(dwc->usb2_phy, 0);
usb_phy_set_suspend(dwc->usb3_phy, 0);
ret = phy_power_on(dwc->usb2_generic_phy);
@@ -872,6 +985,22 @@ static int dwc3_core_init(struct dwc3 *dwc)
dwc3_writel(dwc->regs, DWC3_GUCTL1, reg);
}
+ if (dwc->dr_mode == USB_DR_MODE_HOST ||
+ dwc->dr_mode == USB_DR_MODE_OTG) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUCTL);
+
+ /*
+ * Enable Auto retry Feature to make the controller operating in
+ * Host mode on seeing transaction errors(CRC errors or internal
+ * overrun scenerios) on IN transfers to reply to the device
+ * with a non-terminating retry ACK (i.e, an ACK transcation
+ * packet with Retry=1 & Nump != 0)
+ */
+ reg |= DWC3_GUCTL_HSTINAUTORETRY;
+
+ dwc3_writel(dwc->regs, DWC3_GUCTL, reg);
+ }
+
/*
* Must config both number of packets and max burst settings to enable
* RX and/or TX threshold.
@@ -1245,7 +1374,7 @@ static void dwc3_check_params(struct dwc3 *dwc)
static int dwc3_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct resource *res;
+ struct resource *res, dwc_res;
struct dwc3 *dwc;
int ret;
@@ -1256,6 +1385,11 @@ static int dwc3_probe(struct platform_device *pdev)
if (!dwc)
return -ENOMEM;
+ dwc->clks = devm_kmemdup(dev, dwc3_core_clks, sizeof(dwc3_core_clks),
+ GFP_KERNEL);
+ if (!dwc->clks)
+ return -ENOMEM;
+
dwc->dev = dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -1270,23 +1404,52 @@ static int dwc3_probe(struct platform_device *pdev)
dwc->xhci_resources[0].flags = res->flags;
dwc->xhci_resources[0].name = res->name;
- res->start += DWC3_GLOBALS_REGS_START;
-
/*
* Request memory region but exclude xHCI regs,
* since it will be requested by the xhci-plat driver.
*/
- regs = devm_ioremap_resource(dev, res);
- if (IS_ERR(regs)) {
- ret = PTR_ERR(regs);
- goto err0;
- }
+ dwc_res = *res;
+ dwc_res.start += DWC3_GLOBALS_REGS_START;
+
+ regs = devm_ioremap_resource(dev, &dwc_res);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
dwc->regs = regs;
- dwc->regs_size = resource_size(res);
+ dwc->regs_size = resource_size(&dwc_res);
dwc3_get_properties(dwc);
+ dwc->reset = devm_reset_control_get_optional_shared(dev, NULL);
+ if (IS_ERR(dwc->reset))
+ return PTR_ERR(dwc->reset);
+
+ if (dev->of_node) {
+ dwc->num_clks = ARRAY_SIZE(dwc3_core_clks);
+
+ ret = clk_bulk_get(dev, dwc->num_clks, dwc->clks);
+ if (ret == -EPROBE_DEFER)
+ return ret;
+ /*
+ * Clocks are optional, but new DT platforms should support all
+ * clocks as required by the DT-binding.
+ */
+ if (ret)
+ dwc->num_clks = 0;
+ }
+
+ ret = reset_control_deassert(dwc->reset);
+ if (ret)
+ goto put_clks;
+
+ ret = clk_bulk_prepare(dwc->num_clks, dwc->clks);
+ if (ret)
+ goto assert_reset;
+
+ ret = clk_bulk_enable(dwc->num_clks, dwc->clks);
+ if (ret)
+ goto unprepare_clks;
+
platform_set_drvdata(pdev, dwc);
dwc3_cache_hwparams(dwc);
@@ -1336,6 +1499,7 @@ static int dwc3_probe(struct platform_device *pdev)
err5:
dwc3_event_buffers_cleanup(dwc);
+ dwc3_ulpi_exit(dwc);
err4:
dwc3_free_scratch_buffers(dwc);
@@ -1350,13 +1514,13 @@ err1:
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
-err0:
- /*
- * restore res->start back to its original value so that, in case the
- * probe is deferred, we don't end up getting error in request the
- * memory region the next time probe is called.
- */
- res->start -= DWC3_GLOBALS_REGS_START;
+ clk_bulk_disable(dwc->num_clks, dwc->clks);
+unprepare_clks:
+ clk_bulk_unprepare(dwc->num_clks, dwc->clks);
+assert_reset:
+ reset_control_assert(dwc->reset);
+put_clks:
+ clk_bulk_put(dwc->num_clks, dwc->clks);
return ret;
}
@@ -1364,15 +1528,8 @@ err0:
static int dwc3_remove(struct platform_device *pdev)
{
struct dwc3 *dwc = platform_get_drvdata(pdev);
- struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
pm_runtime_get_sync(&pdev->dev);
- /*
- * restore res->start back to its original value so that, in case the
- * probe is deferred, we don't end up getting error in request the
- * memory region the next time probe is called.
- */
- res->start -= DWC3_GLOBALS_REGS_START;
dwc3_debugfs_exit(dwc);
dwc3_core_exit_mode(dwc);
@@ -1386,14 +1543,48 @@ static int dwc3_remove(struct platform_device *pdev)
dwc3_free_event_buffers(dwc);
dwc3_free_scratch_buffers(dwc);
+ clk_bulk_put(dwc->num_clks, dwc->clks);
return 0;
}
#ifdef CONFIG_PM
+static int dwc3_core_init_for_resume(struct dwc3 *dwc)
+{
+ int ret;
+
+ ret = reset_control_deassert(dwc->reset);
+ if (ret)
+ return ret;
+
+ ret = clk_bulk_prepare(dwc->num_clks, dwc->clks);
+ if (ret)
+ goto assert_reset;
+
+ ret = clk_bulk_enable(dwc->num_clks, dwc->clks);
+ if (ret)
+ goto unprepare_clks;
+
+ ret = dwc3_core_init(dwc);
+ if (ret)
+ goto disable_clks;
+
+ return 0;
+
+disable_clks:
+ clk_bulk_disable(dwc->num_clks, dwc->clks);
+unprepare_clks:
+ clk_bulk_unprepare(dwc->num_clks, dwc->clks);
+assert_reset:
+ reset_control_assert(dwc->reset);
+
+ return ret;
+}
+
static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
{
unsigned long flags;
+ u32 reg;
switch (dwc->current_dr_role) {
case DWC3_GCTL_PRTCAP_DEVICE:
@@ -1403,9 +1594,25 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
dwc3_core_exit(dwc);
break;
case DWC3_GCTL_PRTCAP_HOST:
- /* do nothing during host runtime_suspend */
- if (!PMSG_IS_AUTO(msg))
+ if (!PMSG_IS_AUTO(msg)) {
dwc3_core_exit(dwc);
+ break;
+ }
+
+ /* Let controller to suspend HSPHY before PHY driver suspends */
+ if (dwc->dis_u2_susphy_quirk ||
+ dwc->dis_enblslpm_quirk) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
+ reg |= DWC3_GUSB2PHYCFG_ENBLSLPM |
+ DWC3_GUSB2PHYCFG_SUSPHY;
+ dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+
+ /* Give some time for USB2 PHY to suspend */
+ usleep_range(5000, 6000);
+ }
+
+ phy_pm_runtime_put_sync(dwc->usb2_generic_phy);
+ phy_pm_runtime_put_sync(dwc->usb3_generic_phy);
break;
case DWC3_GCTL_PRTCAP_OTG:
/* do nothing during runtime_suspend */
@@ -1433,10 +1640,11 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg)
{
unsigned long flags;
int ret;
+ u32 reg;
switch (dwc->current_dr_role) {
case DWC3_GCTL_PRTCAP_DEVICE:
- ret = dwc3_core_init(dwc);
+ ret = dwc3_core_init_for_resume(dwc);
if (ret)
return ret;
@@ -1446,13 +1654,25 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg)
spin_unlock_irqrestore(&dwc->lock, flags);
break;
case DWC3_GCTL_PRTCAP_HOST:
- /* nothing to do on host runtime_resume */
if (!PMSG_IS_AUTO(msg)) {
- ret = dwc3_core_init(dwc);
+ ret = dwc3_core_init_for_resume(dwc);
if (ret)
return ret;
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST);
+ break;
}
+ /* Restore GUSB2PHYCFG bits that were modified in suspend */
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
+ 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(0), reg);
+
+ phy_pm_runtime_get_sync(dwc->usb2_generic_phy);
+ phy_pm_runtime_get_sync(dwc->usb3_generic_phy);
break;
case DWC3_GCTL_PRTCAP_OTG:
/* nothing to do on runtime_resume */
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 4f3b43809917..5bfb62533e0f 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -163,6 +163,17 @@
/* Bit fields */
+/* Global SoC Bus Configuration INCRx Register 0 */
+#define DWC3_GSBUSCFG0_INCR256BRSTENA (1 << 7) /* INCR256 burst */
+#define DWC3_GSBUSCFG0_INCR128BRSTENA (1 << 6) /* INCR128 burst */
+#define DWC3_GSBUSCFG0_INCR64BRSTENA (1 << 5) /* INCR64 burst */
+#define DWC3_GSBUSCFG0_INCR32BRSTENA (1 << 4) /* INCR32 burst */
+#define DWC3_GSBUSCFG0_INCR16BRSTENA (1 << 3) /* INCR16 burst */
+#define DWC3_GSBUSCFG0_INCR8BRSTENA (1 << 2) /* INCR8 burst */
+#define DWC3_GSBUSCFG0_INCR4BRSTENA (1 << 1) /* INCR4 burst */
+#define DWC3_GSBUSCFG0_INCRBRSTENA (1 << 0) /* undefined length enable */
+#define DWC3_GSBUSCFG0_INCRBRST_MASK 0xff
+
/* Global Debug Queue/FIFO Space Available Register */
#define DWC3_GDBGFIFOSPACE_NUM(n) ((n) & 0x1f)
#define DWC3_GDBGFIFOSPACE_TYPE(n) (((n) << 5) & 0x1e0)
@@ -227,6 +238,9 @@
#define DWC3_GCTL_GBLHIBERNATIONEN BIT(1)
#define DWC3_GCTL_DSBLCLKGTNG BIT(0)
+/* Global User Control Register */
+#define DWC3_GUCTL_HSTINAUTORETRY BIT(14)
+
/* Global User Control 1 Register */
#define DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS BIT(28)
#define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW BIT(24)
@@ -639,8 +653,6 @@ struct dwc3_event_buffer {
* @resource_index: Resource transfer index
* @frame_number: set to the frame number we want this transfer to start (ISOC)
* @interval: the interval on which the ISOC transfer is started
- * @allocated_requests: number of requests allocated
- * @queued_requests: number of requests queued for transfer
* @name: a human readable name e.g. ep1out-bulk
* @direction: true for TX, false for RX
* @stream_capable: true when streams are enabled
@@ -664,11 +676,9 @@ struct dwc3_ep {
#define DWC3_EP_ENABLED BIT(0)
#define DWC3_EP_STALL BIT(1)
#define DWC3_EP_WEDGE BIT(2)
-#define DWC3_EP_BUSY BIT(4)
+#define DWC3_EP_TRANSFER_STARTED BIT(3)
#define DWC3_EP_PENDING_REQUEST BIT(5)
-#define DWC3_EP_MISSED_ISOC BIT(6)
#define DWC3_EP_END_TRANSFER_PENDING BIT(7)
-#define DWC3_EP_TRANSFER_STARTED BIT(8)
/* This last one is specific to EP0 */
#define DWC3_EP0_DIR_IN BIT(31)
@@ -688,8 +698,6 @@ struct dwc3_ep {
u8 number;
u8 type;
u8 resource_index;
- u32 allocated_requests;
- u32 queued_requests;
u32 frame_number;
u32 interval;
@@ -832,7 +840,9 @@ struct dwc3_hwparams {
* @list: a list_head used for request queueing
* @dep: struct dwc3_ep owning this request
* @sg: pointer to first incomplete sg
+ * @start_sg: pointer to the sg which should be queued next
* @num_pending_sgs: counter to pending sgs
+ * @num_queued_sgs: counter to the number of sgs which already got queued
* @remaining: amount of data remaining
* @epnum: endpoint number to which this request refers
* @trb: pointer to struct dwc3_trb
@@ -848,8 +858,10 @@ struct dwc3_request {
struct list_head list;
struct dwc3_ep *dep;
struct scatterlist *sg;
+ struct scatterlist *start_sg;
unsigned num_pending_sgs;
+ unsigned int num_queued_sgs;
unsigned remaining;
u8 epnum;
struct dwc3_trb *trb;
@@ -891,6 +903,9 @@ struct dwc3_scratchpad_array {
* @eps: endpoint array
* @gadget: device side representation of the peripheral controller
* @gadget_driver: pointer to the gadget driver
+ * @clks: array of clocks
+ * @num_clks: number of clocks
+ * @reset: reset control
* @regs: base address for our registers
* @regs_size: address space size
* @fladj: frame length adjustment
@@ -1013,6 +1028,11 @@ struct dwc3 {
struct usb_gadget gadget;
struct usb_gadget_driver *gadget_driver;
+ struct clk_bulk_data *clks;
+ int num_clks;
+
+ struct reset_control *reset;
+
struct usb_phy *usb2_phy;
struct usb_phy *usb3_phy;
@@ -1151,6 +1171,9 @@ struct dwc3 {
u16 imod_interval;
};
+#define INCRX_BURST_MODE 0
+#define INCRX_UNDEF_LENGTH_BURST_MODE 1
+
#define work_to_dwc(w) (container_of((w), struct dwc3, drd_work))
/* -------------------------------------------------------------------------- */
@@ -1197,11 +1220,12 @@ struct dwc3_event_depevt {
/* Within XferNotReady */
#define DEPEVT_STATUS_TRANSFER_ACTIVE BIT(3)
-/* Within XferComplete */
+/* Within XferComplete or XferInProgress */
#define DEPEVT_STATUS_BUSERR BIT(0)
#define DEPEVT_STATUS_SHORT BIT(1)
#define DEPEVT_STATUS_IOC BIT(2)
-#define DEPEVT_STATUS_LST BIT(3)
+#define DEPEVT_STATUS_LST BIT(3) /* XferComplete */
+#define DEPEVT_STATUS_MISSED_ISOC BIT(3) /* XferInProgress */
/* Stream event only */
#define DEPEVT_STREAMEVT_FOUND 1
diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h
index bfb90c52d8fc..c66d216dcc30 100644
--- a/drivers/usb/dwc3/debug.h
+++ b/drivers/usb/dwc3/debug.h
@@ -475,21 +475,37 @@ dwc3_ep_event_string(char *str, const struct dwc3_event_depevt *event,
if (ret < 0)
return "UNKNOWN";
+ status = event->status;
+
switch (event->endpoint_event) {
case DWC3_DEPEVT_XFERCOMPLETE:
- strcat(str, "Transfer Complete");
+ len = strlen(str);
+ sprintf(str + len, "Transfer Complete (%c%c%c)",
+ status & DEPEVT_STATUS_SHORT ? 'S' : 's',
+ status & DEPEVT_STATUS_IOC ? 'I' : 'i',
+ status & DEPEVT_STATUS_LST ? 'L' : 'l');
+
len = strlen(str);
if (epnum <= 1)
sprintf(str + len, " [%s]", dwc3_ep0_state_string(ep0state));
break;
case DWC3_DEPEVT_XFERINPROGRESS:
- strcat(str, "Transfer In-Progress");
+ len = strlen(str);
+
+ sprintf(str + len, "Transfer In Progress [%d] (%c%c%c)",
+ event->parameters,
+ status & DEPEVT_STATUS_SHORT ? 'S' : 's',
+ status & DEPEVT_STATUS_IOC ? 'I' : 'i',
+ status & DEPEVT_STATUS_LST ? 'M' : 'm');
break;
case DWC3_DEPEVT_XFERNOTREADY:
- strcat(str, "Transfer Not Ready");
- status = event->status & DEPEVT_STATUS_TRANSFER_ACTIVE;
- strcat(str, status ? " (Active)" : " (Not Active)");
+ len = strlen(str);
+
+ sprintf(str + len, "Transfer Not Ready [%d]%s",
+ event->parameters,
+ status & DEPEVT_STATUS_TRANSFER_ACTIVE ?
+ " (Active)" : " (Not Active)");
/* Control Endpoints */
if (epnum <= 1) {
diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c
index 2f07be1e1f31..df8e73ec3342 100644
--- a/drivers/usb/dwc3/debugfs.c
+++ b/drivers/usb/dwc3/debugfs.c
@@ -716,9 +716,6 @@ static void dwc3_debugfs_create_endpoint_dir(struct dwc3_ep *dep,
struct dentry *dir;
dir = debugfs_create_dir(dep->name, parent);
- if (IS_ERR_OR_NULL(dir))
- return;
-
dwc3_debugfs_create_endpoint_files(dep, dir);
}
@@ -740,49 +737,31 @@ static void dwc3_debugfs_create_endpoint_dirs(struct dwc3 *dwc,
void dwc3_debugfs_init(struct dwc3 *dwc)
{
struct dentry *root;
- struct dentry *file;
-
- root = debugfs_create_dir(dev_name(dwc->dev), NULL);
- if (IS_ERR_OR_NULL(root)) {
- if (!root)
- dev_err(dwc->dev, "Can't create debugfs root\n");
- return;
- }
- dwc->root = root;
dwc->regset = kzalloc(sizeof(*dwc->regset), GFP_KERNEL);
- if (!dwc->regset) {
- debugfs_remove_recursive(root);
+ if (!dwc->regset)
return;
- }
dwc->regset->regs = dwc3_regs;
dwc->regset->nregs = ARRAY_SIZE(dwc3_regs);
dwc->regset->base = dwc->regs - DWC3_GLOBALS_REGS_START;
- file = debugfs_create_regset32("regdump", S_IRUGO, root, dwc->regset);
- if (!file)
- dev_dbg(dwc->dev, "Can't create debugfs regdump\n");
+ root = debugfs_create_dir(dev_name(dwc->dev), NULL);
+ dwc->root = root;
+
+ debugfs_create_regset32("regdump", S_IRUGO, root, dwc->regset);
if (IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)) {
- file = debugfs_create_file("mode", S_IRUGO | S_IWUSR, root,
- dwc, &dwc3_mode_fops);
- if (!file)
- dev_dbg(dwc->dev, "Can't create debugfs mode\n");
+ debugfs_create_file("mode", S_IRUGO | S_IWUSR, root, dwc,
+ &dwc3_mode_fops);
}
if (IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE) ||
IS_ENABLED(CONFIG_USB_DWC3_GADGET)) {
- file = debugfs_create_file("testmode", S_IRUGO | S_IWUSR, root,
- dwc, &dwc3_testmode_fops);
- if (!file)
- dev_dbg(dwc->dev, "Can't create debugfs testmode\n");
-
- file = debugfs_create_file("link_state", S_IRUGO | S_IWUSR,
- root, dwc, &dwc3_link_state_fops);
- if (!file)
- dev_dbg(dwc->dev, "Can't create debugfs link_state\n");
-
+ debugfs_create_file("testmode", S_IRUGO | S_IWUSR, root, dwc,
+ &dwc3_testmode_fops);
+ debugfs_create_file("link_state", S_IRUGO | S_IWUSR, root, dwc,
+ &dwc3_link_state_fops);
dwc3_debugfs_create_endpoint_dirs(dwc, root);
}
}
diff --git a/drivers/usb/dwc3/drd.c b/drivers/usb/dwc3/drd.c
index 1d8c557e97e0..218371f985ca 100644
--- a/drivers/usb/dwc3/drd.c
+++ b/drivers/usb/dwc3/drd.c
@@ -8,6 +8,7 @@
*/
#include <linux/extcon.h>
+#include <linux/of_graph.h>
#include <linux/platform_device.h>
#include "debug.h"
@@ -439,17 +440,38 @@ static int dwc3_drd_notifier(struct notifier_block *nb,
return NOTIFY_DONE;
}
+static struct extcon_dev *dwc3_get_extcon(struct dwc3 *dwc)
+{
+ struct device *dev = dwc->dev;
+ struct device_node *np_phy, *np_conn;
+ struct extcon_dev *edev;
+
+ if (of_property_read_bool(dev->of_node, "extcon"))
+ return extcon_get_edev_by_phandle(dwc->dev, 0);
+
+ np_phy = of_parse_phandle(dev->of_node, "phys", 0);
+ np_conn = of_graph_get_remote_node(np_phy, -1, -1);
+
+ if (np_conn)
+ edev = extcon_find_edev_by_node(np_conn);
+ else
+ edev = NULL;
+
+ of_node_put(np_conn);
+ of_node_put(np_phy);
+
+ return edev;
+}
+
int dwc3_drd_init(struct dwc3 *dwc)
{
int ret, irq;
- if (dwc->dev->of_node &&
- of_property_read_bool(dwc->dev->of_node, "extcon")) {
- dwc->edev = extcon_get_edev_by_phandle(dwc->dev, 0);
-
- if (IS_ERR(dwc->edev))
- return PTR_ERR(dwc->edev);
+ dwc->edev = dwc3_get_extcon(dwc);
+ if (IS_ERR(dwc->edev))
+ return PTR_ERR(dwc->edev);
+ if (dwc->edev) {
dwc->edev_nb.notifier_call = dwc3_drd_notifier;
ret = extcon_register_notifier(dwc->edev, EXTCON_USB_HOST,
&dwc->edev_nb);
diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c
index a94fb1ba8f2c..cb7fcd7c0ad8 100644
--- a/drivers/usb/dwc3/dwc3-exynos.c
+++ b/drivers/usb/dwc3/dwc3-exynos.c
@@ -13,80 +13,30 @@
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
-#include <linux/usb/otg.h>
-#include <linux/usb/usb_phy_generic.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/regulator/consumer.h>
+#define DWC3_EXYNOS_MAX_CLOCKS 4
+
+struct dwc3_exynos_driverdata {
+ const char *clk_names[DWC3_EXYNOS_MAX_CLOCKS];
+ int num_clks;
+ int suspend_clk_idx;
+};
+
struct dwc3_exynos {
- struct platform_device *usb2_phy;
- struct platform_device *usb3_phy;
struct device *dev;
- struct clk *clk;
- struct clk *susp_clk;
- struct clk *axius_clk;
+ const char **clk_names;
+ struct clk *clks[DWC3_EXYNOS_MAX_CLOCKS];
+ int num_clks;
+ int suspend_clk_idx;
struct regulator *vdd33;
struct regulator *vdd10;
};
-static int dwc3_exynos_register_phys(struct dwc3_exynos *exynos)
-{
- struct usb_phy_generic_platform_data pdata;
- struct platform_device *pdev;
- int ret;
-
- memset(&pdata, 0x00, sizeof(pdata));
-
- pdev = platform_device_alloc("usb_phy_generic", PLATFORM_DEVID_AUTO);
- if (!pdev)
- return -ENOMEM;
-
- exynos->usb2_phy = pdev;
- pdata.type = USB_PHY_TYPE_USB2;
- pdata.gpio_reset = -1;
-
- ret = platform_device_add_data(exynos->usb2_phy, &pdata, sizeof(pdata));
- if (ret)
- goto err1;
-
- pdev = platform_device_alloc("usb_phy_generic", PLATFORM_DEVID_AUTO);
- if (!pdev) {
- ret = -ENOMEM;
- goto err1;
- }
-
- exynos->usb3_phy = pdev;
- pdata.type = USB_PHY_TYPE_USB3;
-
- ret = platform_device_add_data(exynos->usb3_phy, &pdata, sizeof(pdata));
- if (ret)
- goto err2;
-
- ret = platform_device_add(exynos->usb2_phy);
- if (ret)
- goto err2;
-
- ret = platform_device_add(exynos->usb3_phy);
- if (ret)
- goto err3;
-
- return 0;
-
-err3:
- platform_device_del(exynos->usb2_phy);
-
-err2:
- platform_device_put(exynos->usb3_phy);
-
-err1:
- platform_device_put(exynos->usb2_phy);
-
- return ret;
-}
-
static int dwc3_exynos_remove_child(struct device *dev, void *unused)
{
struct platform_device *pdev = to_platform_device(dev);
@@ -101,47 +51,42 @@ static int dwc3_exynos_probe(struct platform_device *pdev)
struct dwc3_exynos *exynos;
struct device *dev = &pdev->dev;
struct device_node *node = dev->of_node;
-
- int ret;
+ const struct dwc3_exynos_driverdata *driver_data;
+ int i, ret;
exynos = devm_kzalloc(dev, sizeof(*exynos), GFP_KERNEL);
if (!exynos)
return -ENOMEM;
- platform_set_drvdata(pdev, exynos);
+ driver_data = of_device_get_match_data(dev);
+ exynos->dev = dev;
+ exynos->num_clks = driver_data->num_clks;
+ exynos->clk_names = (const char **)driver_data->clk_names;
+ exynos->suspend_clk_idx = driver_data->suspend_clk_idx;
- exynos->dev = dev;
+ platform_set_drvdata(pdev, exynos);
- exynos->clk = devm_clk_get(dev, "usbdrd30");
- if (IS_ERR(exynos->clk)) {
- dev_err(dev, "couldn't get clock\n");
- return -EINVAL;
+ for (i = 0; i < exynos->num_clks; i++) {
+ exynos->clks[i] = devm_clk_get(dev, exynos->clk_names[i]);
+ if (IS_ERR(exynos->clks[i])) {
+ dev_err(dev, "failed to get clock: %s\n",
+ exynos->clk_names[i]);
+ return PTR_ERR(exynos->clks[i]);
+ }
}
- ret = clk_prepare_enable(exynos->clk);
- if (ret)
- return ret;
- exynos->susp_clk = devm_clk_get(dev, "usbdrd30_susp_clk");
- if (IS_ERR(exynos->susp_clk))
- exynos->susp_clk = NULL;
- ret = clk_prepare_enable(exynos->susp_clk);
- if (ret)
- goto susp_clk_err;
-
- if (of_device_is_compatible(node, "samsung,exynos7-dwusb3")) {
- exynos->axius_clk = devm_clk_get(dev, "usbdrd30_axius_clk");
- if (IS_ERR(exynos->axius_clk)) {
- dev_err(dev, "no AXI UpScaler clk specified\n");
- ret = -ENODEV;
- goto axius_clk_err;
+ for (i = 0; i < exynos->num_clks; i++) {
+ ret = clk_prepare_enable(exynos->clks[i]);
+ if (ret) {
+ while (--i > 0)
+ clk_disable_unprepare(exynos->clks[i]);
+ return ret;
}
- ret = clk_prepare_enable(exynos->axius_clk);
- if (ret)
- goto axius_clk_err;
- } else {
- exynos->axius_clk = NULL;
}
+ if (exynos->suspend_clk_idx >= 0)
+ clk_prepare_enable(exynos->clks[exynos->suspend_clk_idx]);
+
exynos->vdd33 = devm_regulator_get(dev, "vdd33");
if (IS_ERR(exynos->vdd33)) {
ret = PTR_ERR(exynos->vdd33);
@@ -164,12 +109,6 @@ static int dwc3_exynos_probe(struct platform_device *pdev)
goto vdd10_err;
}
- ret = dwc3_exynos_register_phys(exynos);
- if (ret) {
- dev_err(dev, "couldn't register PHYs\n");
- goto phys_err;
- }
-
if (node) {
ret = of_platform_populate(node, NULL, NULL, dev);
if (ret) {
@@ -185,32 +124,31 @@ static int dwc3_exynos_probe(struct platform_device *pdev)
return 0;
populate_err:
- platform_device_unregister(exynos->usb2_phy);
- platform_device_unregister(exynos->usb3_phy);
-phys_err:
regulator_disable(exynos->vdd10);
vdd10_err:
regulator_disable(exynos->vdd33);
vdd33_err:
- clk_disable_unprepare(exynos->axius_clk);
-axius_clk_err:
- clk_disable_unprepare(exynos->susp_clk);
-susp_clk_err:
- clk_disable_unprepare(exynos->clk);
+ for (i = exynos->num_clks - 1; i >= 0; i--)
+ clk_disable_unprepare(exynos->clks[i]);
+
+ if (exynos->suspend_clk_idx >= 0)
+ clk_disable_unprepare(exynos->clks[exynos->suspend_clk_idx]);
+
return ret;
}
static int dwc3_exynos_remove(struct platform_device *pdev)
{
struct dwc3_exynos *exynos = platform_get_drvdata(pdev);
+ int i;
device_for_each_child(&pdev->dev, NULL, dwc3_exynos_remove_child);
- platform_device_unregister(exynos->usb2_phy);
- platform_device_unregister(exynos->usb3_phy);
- clk_disable_unprepare(exynos->axius_clk);
- clk_disable_unprepare(exynos->susp_clk);
- clk_disable_unprepare(exynos->clk);
+ for (i = exynos->num_clks - 1; i >= 0; i--)
+ clk_disable_unprepare(exynos->clks[i]);
+
+ if (exynos->suspend_clk_idx >= 0)
+ clk_disable_unprepare(exynos->clks[exynos->suspend_clk_idx]);
regulator_disable(exynos->vdd33);
regulator_disable(exynos->vdd10);
@@ -218,10 +156,36 @@ static int dwc3_exynos_remove(struct platform_device *pdev)
return 0;
}
+static const struct dwc3_exynos_driverdata exynos5250_drvdata = {
+ .clk_names = { "usbdrd30" },
+ .num_clks = 1,
+ .suspend_clk_idx = -1,
+};
+
+static const struct dwc3_exynos_driverdata exynos5433_drvdata = {
+ .clk_names = { "aclk", "susp_clk", "pipe_pclk", "phyclk" },
+ .num_clks = 4,
+ .suspend_clk_idx = 1,
+};
+
+static const struct dwc3_exynos_driverdata exynos7_drvdata = {
+ .clk_names = { "usbdrd30", "usbdrd30_susp_clk", "usbdrd30_axius_clk" },
+ .num_clks = 3,
+ .suspend_clk_idx = 1,
+};
+
static const struct of_device_id exynos_dwc3_match[] = {
- { .compatible = "samsung,exynos5250-dwusb3" },
- { .compatible = "samsung,exynos7-dwusb3" },
- {},
+ {
+ .compatible = "samsung,exynos5250-dwusb3",
+ .data = &exynos5250_drvdata,
+ }, {
+ .compatible = "samsung,exynos5433-dwusb3",
+ .data = &exynos5433_drvdata,
+ }, {
+ .compatible = "samsung,exynos7-dwusb3",
+ .data = &exynos7_drvdata,
+ }, {
+ }
};
MODULE_DEVICE_TABLE(of, exynos_dwc3_match);
@@ -229,9 +193,10 @@ MODULE_DEVICE_TABLE(of, exynos_dwc3_match);
static int dwc3_exynos_suspend(struct device *dev)
{
struct dwc3_exynos *exynos = dev_get_drvdata(dev);
+ int i;
- clk_disable(exynos->axius_clk);
- clk_disable(exynos->clk);
+ for (i = exynos->num_clks - 1; i >= 0; i--)
+ clk_disable_unprepare(exynos->clks[i]);
regulator_disable(exynos->vdd33);
regulator_disable(exynos->vdd10);
@@ -242,7 +207,7 @@ static int dwc3_exynos_suspend(struct device *dev)
static int dwc3_exynos_resume(struct device *dev)
{
struct dwc3_exynos *exynos = dev_get_drvdata(dev);
- int ret;
+ int i, ret;
ret = regulator_enable(exynos->vdd33);
if (ret) {
@@ -255,13 +220,14 @@ static int dwc3_exynos_resume(struct device *dev)
return ret;
}
- clk_enable(exynos->clk);
- clk_enable(exynos->axius_clk);
-
- /* runtime set active to reflect active state. */
- pm_runtime_disable(dev);
- pm_runtime_set_active(dev);
- pm_runtime_enable(dev);
+ for (i = 0; i < exynos->num_clks; i++) {
+ ret = clk_prepare_enable(exynos->clks[i]);
+ if (ret) {
+ while (--i > 0)
+ clk_disable_unprepare(exynos->clks[i]);
+ return ret;
+ }
+ }
return 0;
}
diff --git a/drivers/usb/dwc3/dwc3-haps.c b/drivers/usb/dwc3/dwc3-haps.c
new file mode 100644
index 000000000000..c9cc33881bef
--- /dev/null
+++ b/drivers/usb/dwc3/dwc3-haps.c
@@ -0,0 +1,137 @@
+// SPDX-License-Identifier: GPL-2.0
+/**
+ * dwc3-haps.c - Synopsys HAPS PCI Specific glue layer
+ *
+ * Copyright (C) 2018 Synopsys, Inc.
+ *
+ * Authors: Thinh Nguyen <thinhn@synopsys.com>,
+ * John Youn <johnyoun@synopsys.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+
+#define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3 0xabcd
+#define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3_AXI 0xabce
+#define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB31 0xabcf
+
+/**
+ * struct dwc3_haps - Driver private structure
+ * @dwc3: child dwc3 platform_device
+ * @pci: our link to PCI bus
+ */
+struct dwc3_haps {
+ struct platform_device *dwc3;
+ struct pci_dev *pci;
+};
+
+static const struct property_entry initial_properties[] = {
+ PROPERTY_ENTRY_BOOL("snps,usb3_lpm_capable"),
+ PROPERTY_ENTRY_BOOL("snps,has-lpm-erratum"),
+ PROPERTY_ENTRY_BOOL("snps,dis_enblslpm_quirk"),
+ PROPERTY_ENTRY_BOOL("linux,sysdev_is_parent"),
+ { },
+};
+
+static int dwc3_haps_probe(struct pci_dev *pci,
+ const struct pci_device_id *id)
+{
+ struct dwc3_haps *dwc;
+ struct device *dev = &pci->dev;
+ struct resource res[2];
+ int ret;
+
+ ret = pcim_enable_device(pci);
+ if (ret) {
+ dev_err(dev, "failed to enable pci device\n");
+ return -ENODEV;
+ }
+
+ pci_set_master(pci);
+
+ dwc = devm_kzalloc(dev, sizeof(*dwc), GFP_KERNEL);
+ if (!dwc)
+ return -ENOMEM;
+
+ dwc->dwc3 = platform_device_alloc("dwc3", PLATFORM_DEVID_AUTO);
+ if (!dwc->dwc3)
+ return -ENOMEM;
+
+ memset(res, 0x00, sizeof(struct resource) * ARRAY_SIZE(res));
+
+ res[0].start = pci_resource_start(pci, 0);
+ res[0].end = pci_resource_end(pci, 0);
+ res[0].name = "dwc_usb3";
+ res[0].flags = IORESOURCE_MEM;
+
+ res[1].start = pci->irq;
+ res[1].name = "dwc_usb3";
+ res[1].flags = IORESOURCE_IRQ;
+
+ ret = platform_device_add_resources(dwc->dwc3, res, ARRAY_SIZE(res));
+ if (ret) {
+ dev_err(dev, "couldn't add resources to dwc3 device\n");
+ goto err;
+ }
+
+ dwc->pci = pci;
+ dwc->dwc3->dev.parent = dev;
+
+ ret = platform_device_add_properties(dwc->dwc3, initial_properties);
+ if (ret)
+ goto err;
+
+ ret = platform_device_add(dwc->dwc3);
+ if (ret) {
+ dev_err(dev, "failed to register dwc3 device\n");
+ goto err;
+ }
+
+ pci_set_drvdata(pci, dwc);
+
+ return 0;
+err:
+ platform_device_put(dwc->dwc3);
+ return ret;
+}
+
+static void dwc3_haps_remove(struct pci_dev *pci)
+{
+ struct dwc3_haps *dwc = pci_get_drvdata(pci);
+
+ platform_device_unregister(dwc->dwc3);
+}
+
+static const struct pci_device_id dwc3_haps_id_table[] = {
+ {
+ PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS,
+ PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3),
+ },
+ {
+ PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS,
+ PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3_AXI),
+ },
+ {
+ PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS,
+ PCI_DEVICE_ID_SYNOPSYS_HAPSUSB31),
+ },
+ { } /* Terminating Entry */
+};
+MODULE_DEVICE_TABLE(pci, dwc3_haps_id_table);
+
+static struct pci_driver dwc3_haps_driver = {
+ .name = "dwc3-haps",
+ .id_table = dwc3_haps_id_table,
+ .probe = dwc3_haps_probe,
+ .remove = dwc3_haps_remove,
+};
+
+MODULE_AUTHOR("Thinh Nguyen <thinhn@synopsys.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Synopsys HAPS PCI Glue Layer");
+
+module_pci_driver(dwc3_haps_driver);
diff --git a/drivers/usb/dwc3/dwc3-of-simple.c b/drivers/usb/dwc3/dwc3-of-simple.c
index cb2ee96fd3e8..4c2771c5e727 100644
--- a/drivers/usb/dwc3/dwc3-of-simple.c
+++ b/drivers/usb/dwc3/dwc3-of-simple.c
@@ -28,6 +28,7 @@ struct dwc3_of_simple {
int num_clocks;
struct reset_control *resets;
bool pulse_resets;
+ bool need_reset;
};
static int dwc3_of_simple_clk_init(struct dwc3_of_simple *simple, int count)
@@ -93,6 +94,13 @@ static int dwc3_of_simple_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, simple);
simple->dev = dev;
+ /*
+ * Some controllers need to toggle the usb3-otg reset before trying to
+ * initialize the PHY, otherwise the PHY times out.
+ */
+ if (of_device_is_compatible(np, "rockchip,rk3399-dwc3"))
+ simple->need_reset = true;
+
if (of_device_is_compatible(np, "amlogic,meson-axg-dwc3") ||
of_device_is_compatible(np, "amlogic,meson-gxl-dwc3")) {
shared_resets = true;
@@ -165,14 +173,14 @@ static int dwc3_of_simple_remove(struct platform_device *pdev)
reset_control_put(simple->resets);
- pm_runtime_put_sync(dev);
pm_runtime_disable(dev);
+ pm_runtime_put_noidle(dev);
+ pm_runtime_set_suspended(dev);
return 0;
}
-#ifdef CONFIG_PM
-static int dwc3_of_simple_runtime_suspend(struct device *dev)
+static int __maybe_unused dwc3_of_simple_runtime_suspend(struct device *dev)
{
struct dwc3_of_simple *simple = dev_get_drvdata(dev);
int i;
@@ -183,7 +191,7 @@ static int dwc3_of_simple_runtime_suspend(struct device *dev)
return 0;
}
-static int dwc3_of_simple_runtime_resume(struct device *dev)
+static int __maybe_unused dwc3_of_simple_runtime_resume(struct device *dev)
{
struct dwc3_of_simple *simple = dev_get_drvdata(dev);
int ret;
@@ -200,21 +208,41 @@ static int dwc3_of_simple_runtime_resume(struct device *dev)
return 0;
}
-#endif
+
+static int __maybe_unused dwc3_of_simple_suspend(struct device *dev)
+{
+ struct dwc3_of_simple *simple = dev_get_drvdata(dev);
+
+ if (simple->need_reset)
+ reset_control_assert(simple->resets);
+
+ return 0;
+}
+
+static int __maybe_unused dwc3_of_simple_resume(struct device *dev)
+{
+ struct dwc3_of_simple *simple = dev_get_drvdata(dev);
+
+ if (simple->need_reset)
+ reset_control_deassert(simple->resets);
+
+ return 0;
+}
static const struct dev_pm_ops dwc3_of_simple_dev_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(dwc3_of_simple_suspend, dwc3_of_simple_resume)
SET_RUNTIME_PM_OPS(dwc3_of_simple_runtime_suspend,
dwc3_of_simple_runtime_resume, NULL)
};
static const struct of_device_id of_dwc3_simple_match[] = {
- { .compatible = "qcom,dwc3" },
{ .compatible = "rockchip,rk3399-dwc3" },
{ .compatible = "xlnx,zynqmp-dwc3" },
{ .compatible = "cavium,octeon-7130-usb-uctl" },
{ .compatible = "sprd,sc9860-dwc3" },
{ .compatible = "amlogic,meson-axg-dwc3" },
{ .compatible = "amlogic,meson-gxl-dwc3" },
+ { .compatible = "allwinner,sun50i-h6-dwc3" },
{ /* Sentinel */ }
};
MODULE_DEVICE_TABLE(of, of_dwc3_simple_match);
diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c
index c961a94d136b..842795856bf4 100644
--- a/drivers/usb/dwc3/dwc3-pci.c
+++ b/drivers/usb/dwc3/dwc3-pci.c
@@ -16,12 +16,10 @@
#include <linux/pm_runtime.h>
#include <linux/platform_device.h>
#include <linux/gpio/consumer.h>
+#include <linux/gpio/machine.h>
#include <linux/acpi.h>
#include <linux/delay.h>
-#define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3 0xabcd
-#define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3_AXI 0xabce
-#define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB31 0xabcf
#define PCI_DEVICE_ID_INTEL_BYT 0x0f37
#define PCI_DEVICE_ID_INTEL_MRFLD 0x119e
#define PCI_DEVICE_ID_INTEL_BSW 0x22b7
@@ -34,18 +32,24 @@
#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_ICLLP 0x34ee
#define PCI_INTEL_BXT_DSM_GUID "732b85d5-b7a7-4a1b-9ba0-4bbd00ffd511"
#define PCI_INTEL_BXT_FUNC_PMU_PWR 4
#define PCI_INTEL_BXT_STATE_D0 0
#define PCI_INTEL_BXT_STATE_D3 3
+#define GP_RWBAR 1
+#define GP_RWREG1 0xa0
+#define GP_RWREG1_ULPI_REFCLK_DISABLE (1 << 17)
+
/**
* struct dwc3_pci - Driver private structure
* @dwc3: child dwc3 platform_device
* @pci: our link to PCI bus
* @guid: _DSM GUID
* @has_dsm_for_pm: true for devices which need to run _DSM on runtime PM
+ * @wakeup_work: work for asynchronous resume
*/
struct dwc3_pci {
struct platform_device *dwc3;
@@ -66,52 +70,74 @@ static const struct acpi_gpio_mapping acpi_dwc3_byt_gpios[] = {
{ },
};
-static int dwc3_pci_quirks(struct dwc3_pci *dwc)
+static struct gpiod_lookup_table platform_bytcr_gpios = {
+ .dev_id = "0000:00:16.0",
+ .table = {
+ GPIO_LOOKUP("INT33FC:00", 54, "reset", GPIO_ACTIVE_HIGH),
+ GPIO_LOOKUP("INT33FC:02", 14, "cs", GPIO_ACTIVE_HIGH),
+ {}
+ },
+};
+
+static int dwc3_byt_enable_ulpi_refclock(struct pci_dev *pci)
{
- struct platform_device *dwc3 = dwc->dwc3;
- struct pci_dev *pdev = dwc->pci;
+ void __iomem *reg;
+ u32 value;
- if (pdev->vendor == PCI_VENDOR_ID_AMD &&
- pdev->device == PCI_DEVICE_ID_AMD_NL_USB) {
- struct property_entry properties[] = {
- PROPERTY_ENTRY_BOOL("snps,has-lpm-erratum"),
- PROPERTY_ENTRY_U8("snps,lpm-nyet-threshold", 0xf),
- PROPERTY_ENTRY_BOOL("snps,u2exit_lfps_quirk"),
- PROPERTY_ENTRY_BOOL("snps,u2ss_inp3_quirk"),
- PROPERTY_ENTRY_BOOL("snps,req_p1p2p3_quirk"),
- PROPERTY_ENTRY_BOOL("snps,del_p1p2p3_quirk"),
- PROPERTY_ENTRY_BOOL("snps,del_phy_power_chg_quirk"),
- PROPERTY_ENTRY_BOOL("snps,lfps_filter_quirk"),
- PROPERTY_ENTRY_BOOL("snps,rx_detect_poll_quirk"),
- PROPERTY_ENTRY_BOOL("snps,tx_de_emphasis_quirk"),
- PROPERTY_ENTRY_U8("snps,tx_de_emphasis", 1),
- /*
- * FIXME these quirks should be removed when AMD NL
- * tapes out
- */
- PROPERTY_ENTRY_BOOL("snps,disable_scramble_quirk"),
- PROPERTY_ENTRY_BOOL("snps,dis_u3_susphy_quirk"),
- PROPERTY_ENTRY_BOOL("snps,dis_u2_susphy_quirk"),
- PROPERTY_ENTRY_BOOL("linux,sysdev_is_parent"),
- { },
- };
-
- return platform_device_add_properties(dwc3, properties);
- }
+ reg = pcim_iomap(pci, GP_RWBAR, 0);
+ if (!reg)
+ return -ENOMEM;
- if (pdev->vendor == PCI_VENDOR_ID_INTEL) {
- int ret;
+ value = readl(reg + GP_RWREG1);
+ if (!(value & GP_RWREG1_ULPI_REFCLK_DISABLE))
+ goto unmap; /* ULPI refclk already enabled */
+
+ value &= ~GP_RWREG1_ULPI_REFCLK_DISABLE;
+ writel(value, reg + GP_RWREG1);
+ /* This comes from the Intel Android x86 tree w/o any explanation */
+ msleep(100);
+unmap:
+ pcim_iounmap(pci, reg);
+ return 0;
+}
+
+static const struct property_entry dwc3_pci_intel_properties[] = {
+ PROPERTY_ENTRY_STRING("dr_mode", "peripheral"),
+ PROPERTY_ENTRY_BOOL("linux,sysdev_is_parent"),
+ {}
+};
+
+static const struct property_entry dwc3_pci_mrfld_properties[] = {
+ PROPERTY_ENTRY_STRING("dr_mode", "otg"),
+ PROPERTY_ENTRY_BOOL("linux,sysdev_is_parent"),
+ {}
+};
- struct property_entry properties[] = {
- PROPERTY_ENTRY_STRING("dr_mode", "peripheral"),
- PROPERTY_ENTRY_BOOL("linux,sysdev_is_parent"),
- { }
- };
+static const struct property_entry dwc3_pci_amd_properties[] = {
+ PROPERTY_ENTRY_BOOL("snps,has-lpm-erratum"),
+ PROPERTY_ENTRY_U8("snps,lpm-nyet-threshold", 0xf),
+ PROPERTY_ENTRY_BOOL("snps,u2exit_lfps_quirk"),
+ PROPERTY_ENTRY_BOOL("snps,u2ss_inp3_quirk"),
+ PROPERTY_ENTRY_BOOL("snps,req_p1p2p3_quirk"),
+ PROPERTY_ENTRY_BOOL("snps,del_p1p2p3_quirk"),
+ PROPERTY_ENTRY_BOOL("snps,del_phy_power_chg_quirk"),
+ PROPERTY_ENTRY_BOOL("snps,lfps_filter_quirk"),
+ PROPERTY_ENTRY_BOOL("snps,rx_detect_poll_quirk"),
+ PROPERTY_ENTRY_BOOL("snps,tx_de_emphasis_quirk"),
+ PROPERTY_ENTRY_U8("snps,tx_de_emphasis", 1),
+ /* FIXME these quirks should be removed when AMD NL tapes out */
+ PROPERTY_ENTRY_BOOL("snps,disable_scramble_quirk"),
+ PROPERTY_ENTRY_BOOL("snps,dis_u3_susphy_quirk"),
+ PROPERTY_ENTRY_BOOL("snps,dis_u2_susphy_quirk"),
+ PROPERTY_ENTRY_BOOL("linux,sysdev_is_parent"),
+ {}
+};
- ret = platform_device_add_properties(dwc3, properties);
- if (ret < 0)
- return ret;
+static int dwc3_pci_quirks(struct dwc3_pci *dwc)
+{
+ struct pci_dev *pdev = dwc->pci;
+ if (pdev->vendor == PCI_VENDOR_ID_INTEL) {
if (pdev->device == PCI_DEVICE_ID_INTEL_BXT ||
pdev->device == PCI_DEVICE_ID_INTEL_BXT_M) {
guid_parse(PCI_INTEL_BXT_DSM_GUID, &dwc->guid);
@@ -120,6 +146,12 @@ static int dwc3_pci_quirks(struct dwc3_pci *dwc)
if (pdev->device == PCI_DEVICE_ID_INTEL_BYT) {
struct gpio_desc *gpio;
+ int ret;
+
+ /* On BYT the FW does not always enable the refclock */
+ ret = dwc3_byt_enable_ulpi_refclock(pdev);
+ if (ret)
+ return ret;
ret = devm_acpi_dev_add_driver_gpios(&pdev->dev,
acpi_dwc3_byt_gpios);
@@ -127,44 +159,36 @@ static int dwc3_pci_quirks(struct dwc3_pci *dwc)
dev_dbg(&pdev->dev, "failed to add mapping table\n");
/*
+ * A lot of BYT devices lack ACPI resource entries for
+ * the GPIOs, add a fallback mapping to the reference
+ * design GPIOs which all boards seem to use.
+ */
+ gpiod_add_lookup_table(&platform_bytcr_gpios);
+
+ /*
* These GPIOs will turn on the USB2 PHY. Note that we have to
* put the gpio descriptors again here because the phy driver
* might want to grab them, too.
*/
- gpio = gpiod_get_optional(&pdev->dev, "cs", GPIOD_OUT_LOW);
+ gpio = devm_gpiod_get_optional(&pdev->dev, "cs",
+ GPIOD_OUT_LOW);
if (IS_ERR(gpio))
return PTR_ERR(gpio);
gpiod_set_value_cansleep(gpio, 1);
- gpiod_put(gpio);
- gpio = gpiod_get_optional(&pdev->dev, "reset", GPIOD_OUT_LOW);
+ gpio = devm_gpiod_get_optional(&pdev->dev, "reset",
+ GPIOD_OUT_LOW);
if (IS_ERR(gpio))
return PTR_ERR(gpio);
if (gpio) {
gpiod_set_value_cansleep(gpio, 1);
- gpiod_put(gpio);
usleep_range(10000, 11000);
}
}
}
- if (pdev->vendor == PCI_VENDOR_ID_SYNOPSYS &&
- (pdev->device == PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3 ||
- pdev->device == PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3_AXI ||
- pdev->device == PCI_DEVICE_ID_SYNOPSYS_HAPSUSB31)) {
- struct property_entry properties[] = {
- PROPERTY_ENTRY_BOOL("snps,usb3_lpm_capable"),
- PROPERTY_ENTRY_BOOL("snps,has-lpm-erratum"),
- PROPERTY_ENTRY_BOOL("snps,dis_enblslpm_quirk"),
- PROPERTY_ENTRY_BOOL("linux,sysdev_is_parent"),
- { },
- };
-
- return platform_device_add_properties(dwc3, properties);
- }
-
return 0;
}
@@ -184,9 +208,9 @@ static void dwc3_pci_resume_work(struct work_struct *work)
}
#endif
-static int dwc3_pci_probe(struct pci_dev *pci,
- const struct pci_device_id *id)
+static int dwc3_pci_probe(struct pci_dev *pci, const struct pci_device_id *id)
{
+ struct property_entry *p = (struct property_entry *)id->driver_data;
struct dwc3_pci *dwc;
struct resource res[2];
int ret;
@@ -229,6 +253,10 @@ static int dwc3_pci_probe(struct pci_dev *pci,
dwc->dwc3->dev.parent = dev;
ACPI_COMPANION_SET(&dwc->dwc3->dev, ACPI_COMPANION(dev));
+ ret = platform_device_add_properties(dwc->dwc3, p);
+ if (ret < 0)
+ return ret;
+
ret = dwc3_pci_quirks(dwc);
if (ret)
goto err;
@@ -255,7 +283,10 @@ err:
static void dwc3_pci_remove(struct pci_dev *pci)
{
struct dwc3_pci *dwc = pci_get_drvdata(pci);
+ struct pci_dev *pdev = dwc->pci;
+ if (pdev->device == PCI_DEVICE_ID_INTEL_BYT)
+ gpiod_remove_lookup_table(&platform_bytcr_gpios);
#ifdef CONFIG_PM
cancel_work_sync(&dwc->wakeup_work);
#endif
@@ -265,31 +296,47 @@ static void dwc3_pci_remove(struct pci_dev *pci)
}
static const struct pci_device_id dwc3_pci_id_table[] = {
- {
- PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS,
- PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3),
- },
- {
- PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS,
- PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3_AXI),
- },
- {
- PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS,
- PCI_DEVICE_ID_SYNOPSYS_HAPSUSB31),
- },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BSW), },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BYT), },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_MRFLD), },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SPTLP), },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SPTH), },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BXT), },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BXT_M), },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_APL), },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_KBP), },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_GLK), },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CNPLP), },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CNPH), },
- { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_NL_USB), },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_BSW),
+ (kernel_ulong_t) &dwc3_pci_intel_properties },
+
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_BYT),
+ (kernel_ulong_t) &dwc3_pci_intel_properties, },
+
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_MRFLD),
+ (kernel_ulong_t) &dwc3_pci_mrfld_properties, },
+
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_SPTLP),
+ (kernel_ulong_t) &dwc3_pci_intel_properties, },
+
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_SPTH),
+ (kernel_ulong_t) &dwc3_pci_intel_properties, },
+
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_BXT),
+ (kernel_ulong_t) &dwc3_pci_intel_properties, },
+
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_BXT_M),
+ (kernel_ulong_t) &dwc3_pci_intel_properties, },
+
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_APL),
+ (kernel_ulong_t) &dwc3_pci_intel_properties, },
+
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_KBP),
+ (kernel_ulong_t) &dwc3_pci_intel_properties, },
+
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_GLK),
+ (kernel_ulong_t) &dwc3_pci_intel_properties, },
+
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_CNPLP),
+ (kernel_ulong_t) &dwc3_pci_intel_properties, },
+
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_CNPH),
+ (kernel_ulong_t) &dwc3_pci_intel_properties, },
+
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ICLLP),
+ (kernel_ulong_t) &dwc3_pci_intel_properties, },
+
+ { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_NL_USB),
+ (kernel_ulong_t) &dwc3_pci_amd_properties, },
{ } /* Terminating Entry */
};
MODULE_DEVICE_TABLE(pci, dwc3_pci_id_table);
diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c
new file mode 100644
index 000000000000..a6d0203e40b6
--- /dev/null
+++ b/drivers/usb/dwc3/dwc3-qcom.c
@@ -0,0 +1,616 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * Inspired by dwc3-of-simple.c
+ */
+
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/clk.h>
+#include <linux/irq.h>
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/extcon.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 "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_IRQ_STAT_REG 0x58
+#define PWR_EVNT_LPM_IN_L2_MASK BIT(4)
+#define PWR_EVNT_LPM_OUT_L2_MASK BIT(5)
+
+struct dwc3_qcom {
+ struct device *dev;
+ void __iomem *qscratch_base;
+ struct platform_device *dwc3;
+ struct clk **clks;
+ int num_clocks;
+ struct reset_control *resets;
+
+ int hs_phy_irq;
+ int dp_hs_phy_irq;
+ int dm_hs_phy_irq;
+ int ss_phy_irq;
+
+ 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;
+};
+
+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_overrride_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_overrride_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_overrride_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_read_bool(dev->of_node, "extcon"))
+ return 0;
+
+ qcom->edev = extcon_get_edev_by_phandle(dev, 0);
+ if (IS_ERR(qcom->edev))
+ return PTR_ERR(qcom->edev);
+
+ 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 void dwc3_qcom_disable_interrupts(struct dwc3_qcom *qcom)
+{
+ if (qcom->hs_phy_irq) {
+ disable_irq_wake(qcom->hs_phy_irq);
+ disable_irq_nosync(qcom->hs_phy_irq);
+ }
+
+ if (qcom->dp_hs_phy_irq) {
+ disable_irq_wake(qcom->dp_hs_phy_irq);
+ disable_irq_nosync(qcom->dp_hs_phy_irq);
+ }
+
+ if (qcom->dm_hs_phy_irq) {
+ disable_irq_wake(qcom->dm_hs_phy_irq);
+ disable_irq_nosync(qcom->dm_hs_phy_irq);
+ }
+
+ if (qcom->ss_phy_irq) {
+ disable_irq_wake(qcom->ss_phy_irq);
+ disable_irq_nosync(qcom->ss_phy_irq);
+ }
+}
+
+static void dwc3_qcom_enable_interrupts(struct dwc3_qcom *qcom)
+{
+ if (qcom->hs_phy_irq) {
+ enable_irq(qcom->hs_phy_irq);
+ enable_irq_wake(qcom->hs_phy_irq);
+ }
+
+ if (qcom->dp_hs_phy_irq) {
+ enable_irq(qcom->dp_hs_phy_irq);
+ enable_irq_wake(qcom->dp_hs_phy_irq);
+ }
+
+ if (qcom->dm_hs_phy_irq) {
+ enable_irq(qcom->dm_hs_phy_irq);
+ enable_irq_wake(qcom->dm_hs_phy_irq);
+ }
+
+ if (qcom->ss_phy_irq) {
+ enable_irq(qcom->ss_phy_irq);
+ enable_irq_wake(qcom->ss_phy_irq);
+ }
+}
+
+static int dwc3_qcom_suspend(struct dwc3_qcom *qcom)
+{
+ u32 val;
+ int i;
+
+ if (qcom->is_suspended)
+ return 0;
+
+ val = readl(qcom->qscratch_base + PWR_EVNT_IRQ_STAT_REG);
+ if (!(val & PWR_EVNT_LPM_IN_L2_MASK))
+ dev_err(qcom->dev, "HS-PHY not in L2\n");
+
+ for (i = qcom->num_clocks - 1; i >= 0; i--)
+ clk_disable_unprepare(qcom->clks[i]);
+
+ qcom->is_suspended = true;
+ dwc3_qcom_enable_interrupts(qcom);
+
+ return 0;
+}
+
+static int dwc3_qcom_resume(struct dwc3_qcom *qcom)
+{
+ int ret;
+ int i;
+
+ if (!qcom->is_suspended)
+ return 0;
+
+ 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;
+ }
+ }
+
+ /* Clear existing events from PHY related to L2 in/out */
+ dwc3_qcom_setbits(qcom->qscratch_base, PWR_EVNT_IRQ_STAT_REG,
+ 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;
+
+ if (dwc->xhci)
+ 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_setup_irq(struct platform_device *pdev)
+{
+ struct dwc3_qcom *qcom = platform_get_drvdata(pdev);
+ int irq, ret;
+
+ irq = platform_get_irq_byname(pdev, "hs_phy_irq");
+ if (irq > 0) {
+ /* Keep wakeup interrupts disabled until suspend */
+ irq_set_status_flags(irq, IRQ_NOAUTOEN);
+ ret = devm_request_threaded_irq(qcom->dev, irq, NULL,
+ qcom_dwc3_resume_irq,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+ "qcom_dwc3 HS", qcom);
+ if (ret) {
+ dev_err(qcom->dev, "hs_phy_irq failed: %d\n", ret);
+ return ret;
+ }
+ qcom->hs_phy_irq = irq;
+ }
+
+ irq = platform_get_irq_byname(pdev, "dp_hs_phy_irq");
+ if (irq > 0) {
+ irq_set_status_flags(irq, IRQ_NOAUTOEN);
+ ret = devm_request_threaded_irq(qcom->dev, irq, NULL,
+ qcom_dwc3_resume_irq,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+ "qcom_dwc3 DP_HS", qcom);
+ if (ret) {
+ dev_err(qcom->dev, "dp_hs_phy_irq failed: %d\n", ret);
+ return ret;
+ }
+ qcom->dp_hs_phy_irq = irq;
+ }
+
+ irq = platform_get_irq_byname(pdev, "dm_hs_phy_irq");
+ if (irq > 0) {
+ irq_set_status_flags(irq, IRQ_NOAUTOEN);
+ ret = devm_request_threaded_irq(qcom->dev, irq, NULL,
+ qcom_dwc3_resume_irq,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+ "qcom_dwc3 DM_HS", qcom);
+ if (ret) {
+ dev_err(qcom->dev, "dm_hs_phy_irq failed: %d\n", ret);
+ return ret;
+ }
+ qcom->dm_hs_phy_irq = irq;
+ }
+
+ irq = platform_get_irq_byname(pdev, "ss_phy_irq");
+ if (irq > 0) {
+ irq_set_status_flags(irq, IRQ_NOAUTOEN);
+ ret = devm_request_threaded_irq(qcom->dev, irq, NULL,
+ qcom_dwc3_resume_irq,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+ "qcom_dwc3 SS", qcom);
+ if (ret) {
+ dev_err(qcom->dev, "ss_phy_irq failed: %d\n", ret);
+ return ret;
+ }
+ qcom->ss_phy_irq = irq;
+ }
+
+ 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;
+
+ qcom->num_clocks = count;
+
+ if (!count)
+ return 0;
+
+ 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_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node, *dwc3_np;
+ struct device *dev = &pdev->dev;
+ struct dwc3_qcom *qcom;
+ struct resource *res;
+ int ret, i;
+ bool ignore_pipe_clk;
+
+ 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)) {
+ ret = PTR_ERR(qcom->resets);
+ dev_err(&pdev->dev, "failed to get resets, err=%d\n", ret);
+ return ret;
+ }
+
+ 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_count_phandle_with_args(np,
+ "clocks", "#clock-cells"));
+ if (ret) {
+ dev_err(dev, "failed to get clocks\n");
+ goto reset_assert;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ qcom->qscratch_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(qcom->qscratch_base)) {
+ dev_err(dev, "failed to map qscratch, err=%d\n", ret);
+ ret = PTR_ERR(qcom->qscratch_base);
+ goto clk_disable;
+ }
+
+ ret = dwc3_qcom_setup_irq(pdev);
+ if (ret)
+ goto clk_disable;
+
+ dwc3_np = of_get_child_by_name(np, "dwc3");
+ if (!dwc3_np) {
+ dev_err(dev, "failed to find dwc3 core child\n");
+ ret = -ENODEV;
+ 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 = of_platform_populate(np, NULL, NULL, dev);
+ if (ret) {
+ dev_err(dev, "failed to register dwc3 core - %d\n", ret);
+ goto clk_disable;
+ }
+
+ qcom->dwc3 = of_find_device_by_node(dwc3_np);
+ if (!qcom->dwc3) {
+ dev_err(&pdev->dev, "failed to get dwc3 platform device\n");
+ ret = -ENODEV;
+ goto depopulate;
+ }
+
+ qcom->mode = usb_get_dr_mode(&qcom->dwc3->dev);
+
+ /* enable vbus override for device mode */
+ if (qcom->mode == USB_DR_MODE_PERIPHERAL)
+ dwc3_qcom_vbus_overrride_enable(qcom, true);
+
+ /* register extcon to override sw_vbus on Vbus change later */
+ ret = dwc3_qcom_register_extcon(qcom);
+ if (ret)
+ goto depopulate;
+
+ device_init_wakeup(&pdev->dev, 1);
+ qcom->is_suspended = false;
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ pm_runtime_forbid(dev);
+
+ return 0;
+
+depopulate:
+ of_platform_depopulate(&pdev->dev);
+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 int 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(dev);
+
+ for (i = qcom->num_clocks - 1; i >= 0; i--) {
+ clk_disable_unprepare(qcom->clks[i]);
+ clk_put(qcom->clks[i]);
+ }
+ qcom->num_clocks = 0;
+
+ reset_control_assert(qcom->resets);
+
+ pm_runtime_allow(dev);
+ pm_runtime_disable(dev);
+
+ return 0;
+}
+
+static int __maybe_unused dwc3_qcom_pm_suspend(struct device *dev)
+{
+ struct dwc3_qcom *qcom = dev_get_drvdata(dev);
+ int ret = 0;
+
+ ret = dwc3_qcom_suspend(qcom);
+ if (!ret)
+ qcom->pm_suspended = true;
+
+ return ret;
+}
+
+static int __maybe_unused dwc3_qcom_pm_resume(struct device *dev)
+{
+ struct dwc3_qcom *qcom = dev_get_drvdata(dev);
+ int ret;
+
+ ret = dwc3_qcom_resume(qcom);
+ if (!ret)
+ qcom->pm_suspended = false;
+
+ return ret;
+}
+
+static int __maybe_unused dwc3_qcom_runtime_suspend(struct device *dev)
+{
+ struct dwc3_qcom *qcom = dev_get_drvdata(dev);
+
+ return dwc3_qcom_suspend(qcom);
+}
+
+static int __maybe_unused dwc3_qcom_runtime_resume(struct device *dev)
+{
+ struct dwc3_qcom *qcom = dev_get_drvdata(dev);
+
+ return dwc3_qcom_resume(qcom);
+}
+
+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" },
+ { .compatible = "qcom,msm8996-dwc3" },
+ { .compatible = "qcom,sdm845-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",
+ .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 Glue Driver");
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 5a991bca8ed7..8efde178eef4 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -66,7 +66,7 @@ static int dwc3_ep0_start_trans(struct dwc3_ep *dep)
struct dwc3 *dwc;
int ret;
- if (dep->flags & DWC3_EP_BUSY)
+ if (dep->flags & DWC3_EP_TRANSFER_STARTED)
return 0;
dwc = dep->dwc;
@@ -79,8 +79,6 @@ static int dwc3_ep0_start_trans(struct dwc3_ep *dep)
if (ret < 0)
return ret;
- dep->flags |= DWC3_EP_BUSY;
- dep->resource_index = dwc3_gadget_ep_get_transfer_index(dep);
dwc->ep0_next_event = DWC3_EP0_COMPLETE;
return 0;
@@ -913,7 +911,7 @@ static void dwc3_ep0_xfer_complete(struct dwc3 *dwc,
{
struct dwc3_ep *dep = dwc->eps[event->endpoint_number];
- dep->flags &= ~DWC3_EP_BUSY;
+ dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
dep->resource_index = 0;
dwc->setup_packet_pending = false;
@@ -975,15 +973,12 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
ret = dwc3_ep0_start_trans(dep);
} else if (IS_ALIGNED(req->request.length, dep->endpoint.maxpacket) &&
req->request.length && req->request.zero) {
- u32 maxpacket;
ret = usb_gadget_map_request_by_dev(dwc->sysdev,
&req->request, dep->number);
if (ret)
return;
- maxpacket = dep->endpoint.maxpacket;
-
/* prepare normal TRB */
dwc3_ep0_prepare_one_trb(dep, req->request.dma,
req->request.length,
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 0dedf8a799f4..9f92ee03dde7 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -27,6 +27,9 @@
#include "gadget.h"
#include "io.h"
+#define DWC3_ALIGN_FRAME(d) (((d)->frame_number + (d)->interval) \
+ & ~((d)->interval - 1))
+
/**
* dwc3_gadget_set_test_mode - enables usb2 test modes
* @dwc: pointer to our context structure
@@ -267,27 +270,36 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
const struct usb_endpoint_descriptor *desc = dep->endpoint.desc;
struct dwc3 *dwc = dep->dwc;
u32 timeout = 1000;
+ u32 saved_config = 0;
u32 reg;
int cmd_status = 0;
- int susphy = false;
int ret = -EINVAL;
/*
- * Synopsys Databook 2.60a states, on section 6.3.2.5.[1-8], that if
- * we're issuing an endpoint command, we must check if
- * GUSB2PHYCFG.SUSPHY bit is set. If it is, then we need to clear it.
+ * When operating in USB 2.0 speeds (HS/FS), if GUSB2PHYCFG.ENBLSLPM or
+ * GUSB2PHYCFG.SUSPHY is set, it must be cleared before issuing an
+ * endpoint command.
+ *
+ * Save and clear both GUSB2PHYCFG.ENBLSLPM and GUSB2PHYCFG.SUSPHY
+ * settings. Restore them after the command is completed.
*
- * We will also set SUSPHY bit to what it was before returning as stated
- * by the same section on Synopsys databook.
+ * DWC_usb3 3.30a and DWC_usb31 1.90a programming guide section 3.2.2
*/
if (dwc->gadget.speed <= USB_SPEED_HIGH) {
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
if (unlikely(reg & DWC3_GUSB2PHYCFG_SUSPHY)) {
- susphy = true;
+ saved_config |= DWC3_GUSB2PHYCFG_SUSPHY;
reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
}
+
+ if (reg & DWC3_GUSB2PHYCFG_ENBLSLPM) {
+ saved_config |= DWC3_GUSB2PHYCFG_ENBLSLPM;
+ reg &= ~DWC3_GUSB2PHYCFG_ENBLSLPM;
+ }
+
+ if (saved_config)
+ dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
}
if (DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_STARTTRANSFER) {
@@ -375,6 +387,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
switch (DWC3_DEPCMD_CMD(cmd)) {
case DWC3_DEPCMD_STARTTRANSFER:
dep->flags |= DWC3_EP_TRANSFER_STARTED;
+ dwc3_gadget_ep_get_transfer_index(dep);
break;
case DWC3_DEPCMD_ENDTRANSFER:
dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
@@ -385,9 +398,9 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
}
}
- if (unlikely(susphy)) {
+ if (saved_config) {
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
- reg |= DWC3_GUSB2PHYCFG_SUSPHY;
+ reg |= saved_config;
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
}
@@ -455,11 +468,20 @@ static void dwc3_free_trb_pool(struct dwc3_ep *dep)
dep->trb_pool_dma = 0;
}
-static int dwc3_gadget_set_xfer_resource(struct dwc3 *dwc, struct dwc3_ep *dep);
+static int dwc3_gadget_set_xfer_resource(struct dwc3_ep *dep)
+{
+ struct dwc3_gadget_ep_cmd_params params;
+
+ memset(&params, 0x00, sizeof(params));
+
+ params.param0 = DWC3_DEPXFERCFG_NUM_XFER_RES(1);
+
+ return dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETTRANSFRESOURCE,
+ &params);
+}
/**
* dwc3_gadget_start_config - configure ep resources
- * @dwc: pointer to our controller context structure
* @dep: endpoint that is being enabled
*
* Issue a %DWC3_DEPCMD_DEPSTARTCFG command to @dep. After the command's
@@ -491,9 +513,10 @@ static int dwc3_gadget_set_xfer_resource(struct dwc3 *dwc, struct dwc3_ep *dep);
* triggered only when called for EP0-out, which always happens first, and which
* should only happen in one of the above conditions.
*/
-static int dwc3_gadget_start_config(struct dwc3 *dwc, struct dwc3_ep *dep)
+static int dwc3_gadget_start_config(struct dwc3_ep *dep)
{
struct dwc3_gadget_ep_cmd_params params;
+ struct dwc3 *dwc;
u32 cmd;
int i;
int ret;
@@ -503,6 +526,7 @@ static int dwc3_gadget_start_config(struct dwc3 *dwc, struct dwc3_ep *dep)
memset(&params, 0x00, sizeof(params));
cmd = DWC3_DEPCMD_DEPSTARTCFG;
+ dwc = dep->dwc;
ret = dwc3_send_gadget_ep_cmd(dep, cmd, &params);
if (ret)
@@ -514,7 +538,7 @@ static int dwc3_gadget_start_config(struct dwc3 *dwc, struct dwc3_ep *dep)
if (!dep)
continue;
- ret = dwc3_gadget_set_xfer_resource(dwc, dep);
+ ret = dwc3_gadget_set_xfer_resource(dep);
if (ret)
return ret;
}
@@ -522,16 +546,12 @@ static int dwc3_gadget_start_config(struct dwc3 *dwc, struct dwc3_ep *dep)
return 0;
}
-static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep,
- bool modify, bool restore)
+static int dwc3_gadget_set_ep_config(struct dwc3_ep *dep, unsigned int action)
{
const struct usb_ss_ep_comp_descriptor *comp_desc;
const struct usb_endpoint_descriptor *desc;
struct dwc3_gadget_ep_cmd_params params;
-
- if (dev_WARN_ONCE(dwc->dev, modify && restore,
- "Can't modify and restore\n"))
- return -EINVAL;
+ struct dwc3 *dwc = dep->dwc;
comp_desc = dep->endpoint.comp_desc;
desc = dep->endpoint.desc;
@@ -547,14 +567,9 @@ static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep,
params.param0 |= DWC3_DEPCFG_BURST_SIZE(burst - 1);
}
- if (modify) {
- params.param0 |= DWC3_DEPCFG_ACTION_MODIFY;
- } else if (restore) {
- params.param0 |= DWC3_DEPCFG_ACTION_RESTORE;
+ params.param0 |= action;
+ if (action == DWC3_DEPCFG_ACTION_RESTORE)
params.param2 |= dep->saved_state;
- } else {
- params.param0 |= DWC3_DEPCFG_ACTION_INIT;
- }
if (usb_endpoint_xfer_control(desc))
params.param1 = DWC3_DEPCFG_XFER_COMPLETE_EN;
@@ -594,29 +609,15 @@ static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep,
return dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETEPCONFIG, &params);
}
-static int dwc3_gadget_set_xfer_resource(struct dwc3 *dwc, struct dwc3_ep *dep)
-{
- struct dwc3_gadget_ep_cmd_params params;
-
- memset(&params, 0x00, sizeof(params));
-
- params.param0 = DWC3_DEPXFERCFG_NUM_XFER_RES(1);
-
- return dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETTRANSFRESOURCE,
- &params);
-}
-
/**
* __dwc3_gadget_ep_enable - initializes a hw endpoint
* @dep: endpoint to be initialized
- * @modify: if true, modify existing endpoint configuration
- * @restore: if true, restore endpoint configuration from scratch buffer
+ * @action: one of INIT, MODIFY or RESTORE
*
* Caller should take care of locking. Execute all necessary commands to
* initialize a HW endpoint so it can be used by a gadget driver.
*/
-static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
- bool modify, bool restore)
+static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
{
const struct usb_endpoint_descriptor *desc = dep->endpoint.desc;
struct dwc3 *dwc = dep->dwc;
@@ -625,12 +626,12 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
int ret;
if (!(dep->flags & DWC3_EP_ENABLED)) {
- ret = dwc3_gadget_start_config(dwc, dep);
+ ret = dwc3_gadget_start_config(dep);
if (ret)
return ret;
}
- ret = dwc3_gadget_set_ep_config(dwc, dep, modify, restore);
+ ret = dwc3_gadget_set_ep_config(dep, action);
if (ret)
return ret;
@@ -671,7 +672,8 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
* Issue StartTransfer here with no-op TRB so we can always rely on No
* Response Update Transfer command.
*/
- if (usb_endpoint_xfer_bulk(desc)) {
+ if (usb_endpoint_xfer_bulk(desc) ||
+ usb_endpoint_xfer_int(desc)) {
struct dwc3_gadget_ep_cmd_params params;
struct dwc3_trb *trb;
dma_addr_t trb_dma;
@@ -689,26 +691,20 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
ret = dwc3_send_gadget_ep_cmd(dep, cmd, &params);
if (ret < 0)
return ret;
-
- dep->flags |= DWC3_EP_BUSY;
-
- dep->resource_index = dwc3_gadget_ep_get_transfer_index(dep);
- WARN_ON_ONCE(!dep->resource_index);
}
-
out:
trace_dwc3_gadget_ep_enable(dep);
return 0;
}
-static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force);
+static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force);
static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep)
{
struct dwc3_request *req;
- dwc3_stop_active_transfer(dwc, dep->number, true);
+ dwc3_stop_active_transfer(dep, true);
/* - giveback all requests to gadget driver */
while (!list_empty(&dep->started_list)) {
@@ -806,7 +802,7 @@ static int dwc3_gadget_ep_enable(struct usb_ep *ep,
return 0;
spin_lock_irqsave(&dwc->lock, flags);
- ret = __dwc3_gadget_ep_enable(dep, false, false);
+ ret = __dwc3_gadget_ep_enable(dep, DWC3_DEPCFG_ACTION_INIT);
spin_unlock_irqrestore(&dwc->lock, flags);
return ret;
@@ -840,7 +836,7 @@ static int dwc3_gadget_ep_disable(struct usb_ep *ep)
}
static struct usb_request *dwc3_gadget_ep_alloc_request(struct usb_ep *ep,
- gfp_t gfp_flags)
+ gfp_t gfp_flags)
{
struct dwc3_request *req;
struct dwc3_ep *dep = to_dwc3_ep(ep);
@@ -849,11 +845,10 @@ static struct usb_request *dwc3_gadget_ep_alloc_request(struct usb_ep *ep,
if (!req)
return NULL;
+ req->direction = dep->direction;
req->epnum = dep->number;
req->dep = dep;
- dep->allocated_requests++;
-
trace_dwc3_alloc_request(req);
return &req->request;
@@ -863,14 +858,58 @@ static void dwc3_gadget_ep_free_request(struct usb_ep *ep,
struct usb_request *request)
{
struct dwc3_request *req = to_dwc3_request(request);
- struct dwc3_ep *dep = to_dwc3_ep(ep);
- dep->allocated_requests--;
trace_dwc3_free_request(req);
kfree(req);
}
-static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep);
+/**
+ * dwc3_ep_prev_trb - returns the previous TRB in the ring
+ * @dep: The endpoint with the TRB ring
+ * @index: The index of the current TRB in the ring
+ *
+ * Returns the TRB prior to the one pointed to by the index. If the
+ * index is 0, we will wrap backwards, skip the link TRB, and return
+ * the one just before that.
+ */
+static struct dwc3_trb *dwc3_ep_prev_trb(struct dwc3_ep *dep, u8 index)
+{
+ u8 tmp = index;
+
+ if (!tmp)
+ tmp = DWC3_TRB_NUM - 1;
+
+ return &dep->trb_pool[tmp - 1];
+}
+
+static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep)
+{
+ struct dwc3_trb *tmp;
+ u8 trbs_left;
+
+ /*
+ * If enqueue & dequeue are equal than it is either full or empty.
+ *
+ * One way to know for sure is if the TRB right before us has HWO bit
+ * set or not. If it has, then we're definitely full and can't fit any
+ * more transfers in our ring.
+ */
+ if (dep->trb_enqueue == dep->trb_dequeue) {
+ tmp = dwc3_ep_prev_trb(dep, dep->trb_enqueue);
+ if (tmp->ctrl & DWC3_TRB_CTRL_HWO)
+ return 0;
+
+ return DWC3_TRB_NUM - 1;
+ }
+
+ trbs_left = dep->trb_dequeue - dep->trb_enqueue;
+ trbs_left &= (DWC3_TRB_NUM - 1);
+
+ if (dep->trb_dequeue < dep->trb_enqueue)
+ trbs_left--;
+
+ return trbs_left;
+}
static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb,
dma_addr_t dma, unsigned length, unsigned chain, unsigned node,
@@ -985,11 +1024,19 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
struct dwc3_request *req, unsigned chain, unsigned node)
{
struct dwc3_trb *trb;
- unsigned length = req->request.length;
+ unsigned int length;
+ dma_addr_t dma;
unsigned stream_id = req->request.stream_id;
unsigned short_not_ok = req->request.short_not_ok;
unsigned no_interrupt = req->request.no_interrupt;
- dma_addr_t dma = req->request.dma;
+
+ if (req->request.num_sgs > 0) {
+ length = sg_dma_len(req->start_sg);
+ dma = sg_dma_address(req->start_sg);
+ } else {
+ length = req->request.length;
+ dma = req->request.dma;
+ }
trb = &dep->trb_pool[dep->trb_enqueue];
@@ -997,69 +1044,23 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
dwc3_gadget_move_started_request(req);
req->trb = trb;
req->trb_dma = dwc3_trb_dma_offset(dep, trb);
- dep->queued_requests++;
}
__dwc3_prepare_one_trb(dep, trb, dma, length, chain, node,
stream_id, short_not_ok, no_interrupt);
}
-/**
- * dwc3_ep_prev_trb - returns the previous TRB in the ring
- * @dep: The endpoint with the TRB ring
- * @index: The index of the current TRB in the ring
- *
- * Returns the TRB prior to the one pointed to by the index. If the
- * index is 0, we will wrap backwards, skip the link TRB, and return
- * the one just before that.
- */
-static struct dwc3_trb *dwc3_ep_prev_trb(struct dwc3_ep *dep, u8 index)
-{
- u8 tmp = index;
-
- if (!tmp)
- tmp = DWC3_TRB_NUM - 1;
-
- return &dep->trb_pool[tmp - 1];
-}
-
-static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep)
-{
- struct dwc3_trb *tmp;
- u8 trbs_left;
-
- /*
- * If enqueue & dequeue are equal than it is either full or empty.
- *
- * One way to know for sure is if the TRB right before us has HWO bit
- * set or not. If it has, then we're definitely full and can't fit any
- * more transfers in our ring.
- */
- if (dep->trb_enqueue == dep->trb_dequeue) {
- tmp = dwc3_ep_prev_trb(dep, dep->trb_enqueue);
- if (tmp->ctrl & DWC3_TRB_CTRL_HWO)
- return 0;
-
- return DWC3_TRB_NUM - 1;
- }
-
- trbs_left = dep->trb_dequeue - dep->trb_enqueue;
- trbs_left &= (DWC3_TRB_NUM - 1);
-
- if (dep->trb_dequeue < dep->trb_enqueue)
- trbs_left--;
-
- return trbs_left;
-}
-
static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
struct dwc3_request *req)
{
- struct scatterlist *sg = req->sg;
+ struct scatterlist *sg = req->start_sg;
struct scatterlist *s;
int i;
- for_each_sg(sg, s, req->num_pending_sgs, i) {
+ unsigned int remaining = req->request.num_mapped_sgs
+ - req->num_queued_sgs;
+
+ for_each_sg(sg, s, remaining, i) {
unsigned int length = req->request.length;
unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc);
unsigned int rem = length % maxp;
@@ -1080,7 +1081,7 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
/* Now prepare one extra TRB to align transfer size */
trb = &dep->trb_pool[dep->trb_enqueue];
__dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr,
- maxp - rem, false, 0,
+ maxp - rem, false, 1,
req->request.stream_id,
req->request.short_not_ok,
req->request.no_interrupt);
@@ -1088,6 +1089,18 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
dwc3_prepare_one_trb(dep, req, chain, i);
}
+ /*
+ * There can be a situation where all sgs in sglist are not
+ * queued because of insufficient trb number. To handle this
+ * case, update start_sg to next sg to be queued, so that
+ * we have free trbs we can continue queuing from where we
+ * previously stopped
+ */
+ if (chain)
+ req->start_sg = sg_next(s);
+
+ req->num_queued_sgs++;
+
if (!dwc3_calc_trbs_left(dep))
break;
}
@@ -1112,11 +1125,11 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
/* Now prepare one extra TRB to align transfer size */
trb = &dep->trb_pool[dep->trb_enqueue];
__dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, maxp - rem,
- false, 0, req->request.stream_id,
+ false, 1, req->request.stream_id,
req->request.short_not_ok,
req->request.no_interrupt);
} else if (req->request.zero && req->request.length &&
- (IS_ALIGNED(req->request.length,dep->endpoint.maxpacket))) {
+ (IS_ALIGNED(req->request.length, maxp))) {
struct dwc3 *dwc = dep->dwc;
struct dwc3_trb *trb;
@@ -1128,7 +1141,7 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
/* Now prepare one extra TRB to handle ZLP */
trb = &dep->trb_pool[dep->trb_enqueue];
__dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, 0,
- false, 0, req->request.stream_id,
+ false, 1, req->request.stream_id,
req->request.short_not_ok,
req->request.no_interrupt);
} else {
@@ -1178,6 +1191,8 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep)
return;
req->sg = req->request.sg;
+ req->start_sg = req->sg;
+ req->num_queued_sgs = 0;
req->num_pending_sgs = req->request.num_mapped_sgs;
if (req->num_pending_sgs > 0)
@@ -1201,7 +1216,7 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep)
if (!dwc3_calc_trbs_left(dep))
return 0;
- starting = !(dep->flags & DWC3_EP_BUSY);
+ starting = !(dep->flags & DWC3_EP_TRANSFER_STARTED);
dwc3_prepare_trbs(dep);
req = next_request(&dep->started_list);
@@ -1233,18 +1248,10 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep)
*/
if (req->trb)
memset(req->trb, 0, sizeof(struct dwc3_trb));
- dep->queued_requests--;
dwc3_gadget_del_and_unmap_request(dep, req, ret);
return ret;
}
- dep->flags |= DWC3_EP_BUSY;
-
- if (starting) {
- dep->resource_index = dwc3_gadget_ep_get_transfer_index(dep);
- WARN_ON_ONCE(!dep->resource_index);
- }
-
return 0;
}
@@ -1256,35 +1263,19 @@ static int __dwc3_gadget_get_frame(struct dwc3 *dwc)
return DWC3_DSTS_SOFFN(reg);
}
-static void __dwc3_gadget_start_isoc(struct dwc3 *dwc,
- struct dwc3_ep *dep, u32 cur_uf)
+static void __dwc3_gadget_start_isoc(struct dwc3_ep *dep)
{
if (list_empty(&dep->pending_list)) {
- dev_info(dwc->dev, "%s: ran out of requests\n",
+ dev_info(dep->dwc->dev, "%s: ran out of requests\n",
dep->name);
dep->flags |= DWC3_EP_PENDING_REQUEST;
return;
}
- /*
- * Schedule the first trb for one interval in the future or at
- * least 4 microframes.
- */
- dep->frame_number = cur_uf + max_t(u32, 4, dep->interval);
+ dep->frame_number = DWC3_ALIGN_FRAME(dep);
__dwc3_gadget_kick_transfer(dep);
}
-static void dwc3_gadget_start_isoc(struct dwc3 *dwc,
- struct dwc3_ep *dep, const struct dwc3_event_depevt *event)
-{
- u32 cur_uf, mask;
-
- mask = ~(dep->interval - 1);
- cur_uf = event->parameters & mask;
-
- __dwc3_gadget_start_isoc(dwc, dep, cur_uf);
-}
-
static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
{
struct dwc3 *dwc = dep->dwc;
@@ -1303,8 +1294,6 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
req->request.actual = 0;
req->request.status = -EINPROGRESS;
- req->direction = dep->direction;
- req->epnum = dep->number;
trace_dwc3_ep_queue(req);
@@ -1319,28 +1308,18 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
* errors which will force us issue EndTransfer command.
*/
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
- if ((dep->flags & DWC3_EP_PENDING_REQUEST)) {
- if (dep->flags & DWC3_EP_TRANSFER_STARTED) {
- dwc3_stop_active_transfer(dwc, dep->number, true);
- dep->flags = DWC3_EP_ENABLED;
- } else {
- u32 cur_uf;
+ if (!(dep->flags & DWC3_EP_PENDING_REQUEST) &&
+ !(dep->flags & DWC3_EP_TRANSFER_STARTED))
+ return 0;
- cur_uf = __dwc3_gadget_get_frame(dwc);
- __dwc3_gadget_start_isoc(dwc, dep, cur_uf);
- dep->flags &= ~DWC3_EP_PENDING_REQUEST;
+ if ((dep->flags & DWC3_EP_PENDING_REQUEST)) {
+ if (!(dep->flags & DWC3_EP_TRANSFER_STARTED)) {
+ __dwc3_gadget_start_isoc(dep);
+ return 0;
}
- return 0;
}
-
- if ((dep->flags & DWC3_EP_BUSY) &&
- !(dep->flags & DWC3_EP_MISSED_ISOC))
- goto out;
-
- return 0;
}
-out:
return __dwc3_gadget_kick_transfer(dep);
}
@@ -1390,7 +1369,7 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
}
if (r == req) {
/* wait until it is processed */
- dwc3_stop_active_transfer(dwc, dep->number, true);
+ dwc3_stop_active_transfer(dep, true);
/*
* If request was already started, this means we had to
@@ -1463,7 +1442,7 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
out1:
/* giveback the request */
- dep->queued_requests--;
+
dwc3_gadget_giveback(dep, req, -ECONNRESET);
out0:
@@ -1491,9 +1470,6 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
unsigned transfer_in_flight;
unsigned started;
- if (dep->flags & DWC3_EP_STALL)
- return 0;
-
if (dep->number > 1)
trb = dwc3_ep_prev_trb(dep, dep->trb_enqueue);
else
@@ -1515,8 +1491,6 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
else
dep->flags |= DWC3_EP_STALL;
} else {
- if (!(dep->flags & DWC3_EP_STALL))
- return 0;
ret = dwc3_send_clear_stall_ep_cmd(dep);
if (ret)
@@ -1878,14 +1852,14 @@ static int __dwc3_gadget_start(struct dwc3 *dwc)
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
dep = dwc->eps[0];
- ret = __dwc3_gadget_ep_enable(dep, false, false);
+ ret = __dwc3_gadget_ep_enable(dep, DWC3_DEPCFG_ACTION_INIT);
if (ret) {
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
goto err0;
}
dep = dwc->eps[1];
- ret = __dwc3_gadget_ep_enable(dep, false, false);
+ ret = __dwc3_gadget_ep_enable(dep, DWC3_DEPCFG_ACTION_INIT);
if (ret) {
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
goto err1;
@@ -2082,113 +2056,142 @@ static const struct usb_gadget_ops dwc3_gadget_ops = {
/* -------------------------------------------------------------------------- */
-static int dwc3_gadget_init_endpoints(struct dwc3 *dwc, u8 total)
+static int dwc3_gadget_init_control_endpoint(struct dwc3_ep *dep)
{
- struct dwc3_ep *dep;
- u8 epnum;
+ struct dwc3 *dwc = dep->dwc;
- INIT_LIST_HEAD(&dwc->gadget.ep_list);
+ usb_ep_set_maxpacket_limit(&dep->endpoint, 512);
+ dep->endpoint.maxburst = 1;
+ dep->endpoint.ops = &dwc3_gadget_ep0_ops;
+ if (!dep->direction)
+ dwc->gadget.ep0 = &dep->endpoint;
- for (epnum = 0; epnum < total; epnum++) {
- bool direction = epnum & 1;
- u8 num = epnum >> 1;
+ dep->endpoint.caps.type_control = true;
- dep = kzalloc(sizeof(*dep), GFP_KERNEL);
- if (!dep)
- return -ENOMEM;
+ return 0;
+}
- dep->dwc = dwc;
- dep->number = epnum;
- dep->direction = direction;
- dep->regs = dwc->regs + DWC3_DEP_BASE(epnum);
- dwc->eps[epnum] = dep;
+static int dwc3_gadget_init_in_endpoint(struct dwc3_ep *dep)
+{
+ struct dwc3 *dwc = dep->dwc;
+ int mdwidth;
+ int kbytes;
+ int size;
- snprintf(dep->name, sizeof(dep->name), "ep%u%s", num,
- direction ? "in" : "out");
+ mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0);
+ /* MDWIDTH is represented in bits, we need it in bytes */
+ mdwidth /= 8;
- dep->endpoint.name = dep->name;
+ size = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(dep->number >> 1));
+ if (dwc3_is_usb31(dwc))
+ size = DWC31_GTXFIFOSIZ_TXFDEF(size);
+ else
+ size = DWC3_GTXFIFOSIZ_TXFDEF(size);
- if (!(dep->number > 1)) {
- dep->endpoint.desc = &dwc3_gadget_ep0_desc;
- dep->endpoint.comp_desc = NULL;
- }
+ /* FIFO Depth is in MDWDITH bytes. Multiply */
+ size *= mdwidth;
- spin_lock_init(&dep->lock);
-
- if (num == 0) {
- usb_ep_set_maxpacket_limit(&dep->endpoint, 512);
- dep->endpoint.maxburst = 1;
- dep->endpoint.ops = &dwc3_gadget_ep0_ops;
- if (!direction)
- dwc->gadget.ep0 = &dep->endpoint;
- } else if (direction) {
- int mdwidth;
- int kbytes;
- int size;
- int ret;
-
- mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0);
- /* MDWIDTH is represented in bits, we need it in bytes */
- mdwidth /= 8;
-
- size = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(num));
- if (dwc3_is_usb31(dwc))
- size = DWC31_GTXFIFOSIZ_TXFDEF(size);
- else
- size = DWC3_GTXFIFOSIZ_TXFDEF(size);
+ kbytes = size / 1024;
+ if (kbytes == 0)
+ kbytes = 1;
- /* FIFO Depth is in MDWDITH bytes. Multiply */
- size *= mdwidth;
+ /*
+ * FIFO sizes account an extra MDWIDTH * (kbytes + 1) bytes for
+ * internal overhead. We don't really know how these are used,
+ * but documentation say it exists.
+ */
+ size -= mdwidth * (kbytes + 1);
+ size /= kbytes;
- kbytes = size / 1024;
- if (kbytes == 0)
- kbytes = 1;
+ usb_ep_set_maxpacket_limit(&dep->endpoint, size);
- /*
- * FIFO sizes account an extra MDWIDTH * (kbytes + 1) bytes for
- * internal overhead. We don't really know how these are used,
- * but documentation say it exists.
- */
- size -= mdwidth * (kbytes + 1);
- size /= kbytes;
+ dep->endpoint.max_streams = 15;
+ dep->endpoint.ops = &dwc3_gadget_ep_ops;
+ list_add_tail(&dep->endpoint.ep_list,
+ &dwc->gadget.ep_list);
+ dep->endpoint.caps.type_iso = true;
+ dep->endpoint.caps.type_bulk = true;
+ dep->endpoint.caps.type_int = true;
- usb_ep_set_maxpacket_limit(&dep->endpoint, size);
+ return dwc3_alloc_trb_pool(dep);
+}
- dep->endpoint.max_streams = 15;
- dep->endpoint.ops = &dwc3_gadget_ep_ops;
- list_add_tail(&dep->endpoint.ep_list,
- &dwc->gadget.ep_list);
+static int dwc3_gadget_init_out_endpoint(struct dwc3_ep *dep)
+{
+ struct dwc3 *dwc = dep->dwc;
- ret = dwc3_alloc_trb_pool(dep);
- if (ret)
- return ret;
- } else {
- int ret;
+ usb_ep_set_maxpacket_limit(&dep->endpoint, 1024);
+ dep->endpoint.max_streams = 15;
+ dep->endpoint.ops = &dwc3_gadget_ep_ops;
+ list_add_tail(&dep->endpoint.ep_list,
+ &dwc->gadget.ep_list);
+ dep->endpoint.caps.type_iso = true;
+ dep->endpoint.caps.type_bulk = true;
+ dep->endpoint.caps.type_int = true;
- usb_ep_set_maxpacket_limit(&dep->endpoint, 1024);
- dep->endpoint.max_streams = 15;
- dep->endpoint.ops = &dwc3_gadget_ep_ops;
- list_add_tail(&dep->endpoint.ep_list,
- &dwc->gadget.ep_list);
+ return dwc3_alloc_trb_pool(dep);
+}
- ret = dwc3_alloc_trb_pool(dep);
- if (ret)
- return ret;
- }
+static int dwc3_gadget_init_endpoint(struct dwc3 *dwc, u8 epnum)
+{
+ struct dwc3_ep *dep;
+ bool direction = epnum & 1;
+ int ret;
+ u8 num = epnum >> 1;
- if (num == 0) {
- dep->endpoint.caps.type_control = true;
- } else {
- dep->endpoint.caps.type_iso = true;
- dep->endpoint.caps.type_bulk = true;
- dep->endpoint.caps.type_int = true;
- }
+ dep = kzalloc(sizeof(*dep), GFP_KERNEL);
+ 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;
+
+ snprintf(dep->name, sizeof(dep->name), "ep%u%s", num,
+ direction ? "in" : "out");
+
+ dep->endpoint.name = dep->name;
+
+ if (!(dep->number > 1)) {
+ dep->endpoint.desc = &dwc3_gadget_ep0_desc;
+ dep->endpoint.comp_desc = NULL;
+ }
+
+ spin_lock_init(&dep->lock);
+
+ if (num == 0)
+ ret = dwc3_gadget_init_control_endpoint(dep);
+ else if (direction)
+ ret = dwc3_gadget_init_in_endpoint(dep);
+ else
+ ret = dwc3_gadget_init_out_endpoint(dep);
- dep->endpoint.caps.dir_in = direction;
- dep->endpoint.caps.dir_out = !direction;
+ if (ret)
+ return ret;
+
+ dep->endpoint.caps.dir_in = direction;
+ dep->endpoint.caps.dir_out = !direction;
+
+ INIT_LIST_HEAD(&dep->pending_list);
+ INIT_LIST_HEAD(&dep->started_list);
+
+ return 0;
+}
- INIT_LIST_HEAD(&dep->pending_list);
- INIT_LIST_HEAD(&dep->started_list);
+static int dwc3_gadget_init_endpoints(struct dwc3 *dwc, u8 total)
+{
+ u8 epnum;
+
+ INIT_LIST_HEAD(&dwc->gadget.ep_list);
+
+ for (epnum = 0; epnum < total; epnum++) {
+ int ret;
+
+ ret = dwc3_gadget_init_endpoint(dwc, epnum);
+ if (ret)
+ return ret;
}
return 0;
@@ -2223,20 +2226,14 @@ static void dwc3_gadget_free_endpoints(struct dwc3 *dwc)
/* -------------------------------------------------------------------------- */
-static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep,
+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, int chain)
{
unsigned int count;
- unsigned int s_pkt = 0;
- unsigned int trb_status;
dwc3_ep_inc_deq(dep);
- if (req->trb == trb)
- dep->queued_requests--;
-
trace_dwc3_complete_trb(dep, trb);
/*
@@ -2257,7 +2254,7 @@ static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep,
* with one TRB pending in the ring. We need to manually clear HWO bit
* from that TRB.
*/
- if ((req->zero || req->unaligned) && (trb->ctrl & DWC3_TRB_CTRL_HWO)) {
+ if ((req->zero || req->unaligned) && !(trb->ctrl & DWC3_TRB_CTRL_CHN)) {
trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
return 1;
}
@@ -2268,159 +2265,140 @@ static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep,
if ((trb->ctrl & DWC3_TRB_CTRL_HWO) && status != -ESHUTDOWN)
return 1;
- if (dep->direction) {
- if (count) {
- trb_status = DWC3_TRB_SIZE_TRBSTS(trb->size);
- if (trb_status == DWC3_TRBSTS_MISSED_ISOC) {
- /*
- * If missed isoc occurred and there is
- * no request queued then issue END
- * TRANSFER, so that core generates
- * next xfernotready and we will issue
- * a fresh START TRANSFER.
- * If there are still queued request
- * then wait, do not issue either END
- * or UPDATE TRANSFER, just attach next
- * request in pending_list during
- * giveback.If any future queued request
- * is successfully transferred then we
- * will issue UPDATE TRANSFER for all
- * request in the pending_list.
- */
- dep->flags |= DWC3_EP_MISSED_ISOC;
- } else {
- dev_err(dwc->dev, "incomplete IN transfer %s\n",
- dep->name);
- status = -ECONNRESET;
- }
- } else {
- dep->flags &= ~DWC3_EP_MISSED_ISOC;
- }
- } else {
- if (count && (event->status & DEPEVT_STATUS_SHORT))
- s_pkt = 1;
- }
-
- if (s_pkt && !chain)
+ if (event->status & DEPEVT_STATUS_SHORT && !chain)
return 1;
- if ((event->status & DEPEVT_STATUS_IOC) &&
- (trb->ctrl & DWC3_TRB_CTRL_IOC))
+ if (event->status & DEPEVT_STATUS_IOC)
return 1;
return 0;
}
-static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
- const struct dwc3_event_depevt *event, int status)
+static int dwc3_gadget_ep_reclaim_trb_sg(struct dwc3_ep *dep,
+ struct dwc3_request *req, const struct dwc3_event_depevt *event,
+ int status)
{
- struct dwc3_request *req, *n;
- struct dwc3_trb *trb;
- bool ioc = false;
- int ret = 0;
+ struct dwc3_trb *trb = &dep->trb_pool[dep->trb_dequeue];
+ struct scatterlist *sg = req->sg;
+ struct scatterlist *s;
+ unsigned int pending = req->num_pending_sgs;
+ unsigned int i;
+ int ret = 0;
- list_for_each_entry_safe(req, n, &dep->started_list, list) {
- unsigned length;
- int chain;
+ for_each_sg(sg, s, pending, i) {
+ trb = &dep->trb_pool[dep->trb_dequeue];
- length = req->request.length;
- chain = req->num_pending_sgs > 0;
- if (chain) {
- struct scatterlist *sg = req->sg;
- struct scatterlist *s;
- unsigned int pending = req->num_pending_sgs;
- unsigned int i;
-
- for_each_sg(sg, s, pending, i) {
- trb = &dep->trb_pool[dep->trb_dequeue];
-
- if (trb->ctrl & DWC3_TRB_CTRL_HWO)
- break;
-
- req->sg = sg_next(s);
- req->num_pending_sgs--;
-
- ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb,
- event, status, chain);
- if (ret)
- break;
- }
- } else {
- trb = &dep->trb_pool[dep->trb_dequeue];
- ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb,
- event, status, chain);
- }
+ if (trb->ctrl & DWC3_TRB_CTRL_HWO)
+ break;
- if (req->unaligned || req->zero) {
- trb = &dep->trb_pool[dep->trb_dequeue];
- ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb,
- event, status, false);
- req->unaligned = false;
- req->zero = false;
- }
+ req->sg = sg_next(s);
+ req->num_pending_sgs--;
- req->request.actual = length - req->remaining;
+ ret = dwc3_gadget_ep_reclaim_completed_trb(dep, req,
+ trb, event, status, true);
+ if (ret)
+ break;
+ }
- if ((req->request.actual < length) && req->num_pending_sgs)
- return __dwc3_gadget_kick_transfer(dep);
+ return ret;
+}
- dwc3_gadget_giveback(dep, req, status);
+static int dwc3_gadget_ep_reclaim_trb_linear(struct dwc3_ep *dep,
+ struct dwc3_request *req, const struct dwc3_event_depevt *event,
+ int status)
+{
+ struct dwc3_trb *trb = &dep->trb_pool[dep->trb_dequeue];
- if (ret) {
- if ((event->status & DEPEVT_STATUS_IOC) &&
- (trb->ctrl & DWC3_TRB_CTRL_IOC))
- ioc = true;
- break;
- }
+ return dwc3_gadget_ep_reclaim_completed_trb(dep, req, trb,
+ event, status, false);
+}
+
+static bool dwc3_gadget_ep_request_completed(struct dwc3_request *req)
+{
+ return req->request.actual == req->request.length;
+}
+
+static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep,
+ const struct dwc3_event_depevt *event,
+ struct dwc3_request *req, int status)
+{
+ int ret;
+
+ if (req->num_pending_sgs)
+ ret = dwc3_gadget_ep_reclaim_trb_sg(dep, req, event,
+ status);
+ else
+ ret = dwc3_gadget_ep_reclaim_trb_linear(dep, req, event,
+ status);
+
+ if (req->unaligned || req->zero) {
+ ret = dwc3_gadget_ep_reclaim_trb_linear(dep, req, event,
+ status);
+ req->unaligned = false;
+ req->zero = false;
}
- /*
- * Our endpoint might get disabled by another thread during
- * dwc3_gadget_giveback(). If that happens, we're just gonna return 1
- * early on so DWC3_EP_BUSY flag gets cleared
- */
- if (!dep->endpoint.desc)
- return 1;
+ req->request.actual = req->request.length - req->remaining;
- if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
- list_empty(&dep->started_list)) {
- if (list_empty(&dep->pending_list)) {
- /*
- * If there is no entry in request list then do
- * not issue END TRANSFER now. Just set PENDING
- * flag, so that END TRANSFER is issued when an
- * entry is added into request list.
- */
- dep->flags = DWC3_EP_PENDING_REQUEST;
- } else {
- dwc3_stop_active_transfer(dwc, dep->number, true);
- dep->flags = DWC3_EP_ENABLED;
- }
- return 1;
+ if (!dwc3_gadget_ep_request_completed(req) &&
+ req->num_pending_sgs) {
+ __dwc3_gadget_kick_transfer(dep);
+ goto out;
}
- if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && ioc)
- return 0;
+ dwc3_gadget_giveback(dep, req, status);
- return 1;
+out:
+ return ret;
}
-static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc,
- struct dwc3_ep *dep, const struct dwc3_event_depevt *event)
+static void dwc3_gadget_ep_cleanup_completed_requests(struct dwc3_ep *dep,
+ const struct dwc3_event_depevt *event, int status)
{
+ struct dwc3_request *req;
+ struct dwc3_request *tmp;
+
+ list_for_each_entry_safe(req, tmp, &dep->started_list, list) {
+ int ret;
+
+ ret = dwc3_gadget_ep_cleanup_completed_request(dep, event,
+ req, status);
+ if (ret)
+ break;
+ }
+}
+
+static void dwc3_gadget_endpoint_frame_from_event(struct dwc3_ep *dep,
+ const struct dwc3_event_depevt *event)
+{
+ dep->frame_number = event->parameters;
+}
+
+static void dwc3_gadget_endpoint_transfer_in_progress(struct dwc3_ep *dep,
+ const struct dwc3_event_depevt *event)
+{
+ struct dwc3 *dwc = dep->dwc;
unsigned status = 0;
- int clean_busy;
- u32 is_xfer_complete;
+ bool stop = false;
- is_xfer_complete = (event->endpoint_event == DWC3_DEPEVT_XFERCOMPLETE);
+ dwc3_gadget_endpoint_frame_from_event(dep, event);
if (event->status & DEPEVT_STATUS_BUSERR)
status = -ECONNRESET;
- clean_busy = dwc3_cleanup_done_reqs(dwc, dep, event, status);
- if (clean_busy && (!dep->endpoint.desc || is_xfer_complete ||
- usb_endpoint_xfer_isoc(dep->endpoint.desc)))
- dep->flags &= ~DWC3_EP_BUSY;
+ if (event->status & DEPEVT_STATUS_MISSED_ISOC) {
+ status = -EXDEV;
+
+ if (list_empty(&dep->started_list))
+ stop = true;
+ }
+
+ dwc3_gadget_ep_cleanup_completed_requests(dep, event, status);
+
+ if (stop) {
+ dwc3_stop_active_transfer(dep, true);
+ dep->flags = DWC3_EP_ENABLED;
+ }
/*
* WORKAROUND: This is the 2nd half of U1/U2 -> U0 workaround.
@@ -2446,17 +2424,13 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc,
dwc->u1u2 = 0;
}
+}
- /*
- * Our endpoint might get disabled by another thread during
- * dwc3_gadget_giveback(). If that happens, we're just gonna return 1
- * early on so DWC3_EP_BUSY flag gets cleared
- */
- if (!dep->endpoint.desc)
- return;
-
- if (!usb_endpoint_xfer_isoc(dep->endpoint.desc))
- __dwc3_gadget_kick_transfer(dep);
+static void dwc3_gadget_endpoint_transfer_not_ready(struct dwc3_ep *dep,
+ const struct dwc3_event_depevt *event)
+{
+ dwc3_gadget_endpoint_frame_from_event(dep, event);
+ __dwc3_gadget_start_isoc(dep);
}
static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
@@ -2483,32 +2457,11 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
}
switch (event->endpoint_event) {
- case DWC3_DEPEVT_XFERCOMPLETE:
- dep->resource_index = 0;
-
- if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
- dev_err(dwc->dev, "XferComplete for Isochronous endpoint\n");
- return;
- }
-
- dwc3_endpoint_transfer_complete(dwc, dep, event);
- break;
case DWC3_DEPEVT_XFERINPROGRESS:
- dwc3_endpoint_transfer_complete(dwc, dep, event);
+ dwc3_gadget_endpoint_transfer_in_progress(dep, event);
break;
case DWC3_DEPEVT_XFERNOTREADY:
- if (usb_endpoint_xfer_isoc(dep->endpoint.desc))
- dwc3_gadget_start_isoc(dwc, dep, event);
- else
- __dwc3_gadget_kick_transfer(dep);
-
- break;
- case DWC3_DEPEVT_STREAMEVT:
- if (!usb_endpoint_xfer_bulk(dep->endpoint.desc)) {
- dev_err(dwc->dev, "Stream event for non-Bulk %s\n",
- dep->name);
- return;
- }
+ dwc3_gadget_endpoint_transfer_not_ready(dep, event);
break;
case DWC3_DEPEVT_EPCMDCMPLT:
cmd = DEPEVT_PARAMETER_CMD(event->parameters);
@@ -2518,6 +2471,8 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
wake_up(&dep->wait_end_transfer);
}
break;
+ case DWC3_DEPEVT_STREAMEVT:
+ case DWC3_DEPEVT_XFERCOMPLETE:
case DWC3_DEPEVT_RXTXFIFOEVT:
break;
}
@@ -2562,15 +2517,13 @@ static void dwc3_reset_gadget(struct dwc3 *dwc)
}
}
-static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force)
+static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force)
{
- struct dwc3_ep *dep;
+ struct dwc3 *dwc = dep->dwc;
struct dwc3_gadget_ep_cmd_params params;
u32 cmd;
int ret;
- dep = dwc->eps[epnum];
-
if ((dep->flags & DWC3_EP_END_TRANSFER_PENDING) ||
!dep->resource_index)
return;
@@ -2614,7 +2567,6 @@ static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force)
ret = dwc3_send_gadget_ep_cmd(dep, cmd, &params);
WARN_ON_ONCE(ret);
dep->resource_index = 0;
- dep->flags &= ~DWC3_EP_BUSY;
if (dwc3_is_usb31(dwc) || dwc->revision < DWC3_REVISION_310A) {
dep->flags |= DWC3_EP_END_TRANSFER_PENDING;
@@ -2816,14 +2768,14 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
}
dep = dwc->eps[0];
- ret = __dwc3_gadget_ep_enable(dep, true, false);
+ ret = __dwc3_gadget_ep_enable(dep, DWC3_DEPCFG_ACTION_MODIFY);
if (ret) {
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
return;
}
dep = dwc->eps[1];
- ret = __dwc3_gadget_ep_enable(dep, true, false);
+ ret = __dwc3_gadget_ep_enable(dep, DWC3_DEPCFG_ACTION_MODIFY);
if (ret) {
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
return;
diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h
index 578aa856f986..2aacd1afd9ff 100644
--- a/drivers/usb/dwc3/gadget.h
+++ b/drivers/usb/dwc3/gadget.h
@@ -25,7 +25,7 @@ struct dwc3;
#define DWC3_DEPCFG_XFER_IN_PROGRESS_EN BIT(9)
#define DWC3_DEPCFG_XFER_NOT_READY_EN BIT(10)
#define DWC3_DEPCFG_FIFO_ERROR_EN BIT(11)
-#define DWC3_DEPCFG_STREAM_EVENT_EN BIT(12)
+#define DWC3_DEPCFG_STREAM_EVENT_EN BIT(13)
#define DWC3_DEPCFG_BINTERVAL_M1(n) (((n) & 0xff) << 16)
#define DWC3_DEPCFG_STREAM_CAPABLE BIT(24)
#define DWC3_DEPCFG_EP_NUMBER(n) (((n) & 0x1f) << 25)
@@ -98,13 +98,12 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol);
* Caller should take care of locking. Returns the transfer resource
* index for a given endpoint.
*/
-static inline u32 dwc3_gadget_ep_get_transfer_index(struct dwc3_ep *dep)
+static inline void dwc3_gadget_ep_get_transfer_index(struct dwc3_ep *dep)
{
u32 res_id;
res_id = dwc3_readl(dep->regs, DWC3_DEPCMD);
-
- return DWC3_DEPCMD_GET_RSC_IDX(res_id);
+ dep->resource_index = DWC3_DEPCMD_GET_RSC_IDX(res_id);
}
#endif /* __DRIVERS_USB_DWC3_GADGET_H */
diff --git a/drivers/usb/dwc3/trace.h b/drivers/usb/dwc3/trace.h
index babaee981aa7..f22714cce070 100644
--- a/drivers/usb/dwc3/trace.h
+++ b/drivers/usb/dwc3/trace.h
@@ -230,17 +230,14 @@ DECLARE_EVENT_CLASS(dwc3_log_trb,
TP_fast_assign(
__assign_str(name, dep->name);
__entry->trb = trb;
- __entry->allocated = dep->allocated_requests;
- __entry->queued = dep->queued_requests;
__entry->bpl = trb->bpl;
__entry->bph = trb->bph;
__entry->size = trb->size;
__entry->ctrl = trb->ctrl;
__entry->type = usb_endpoint_type(dep->endpoint.desc);
),
- TP_printk("%s: %d/%d trb %p buf %08x%08x size %s%d ctrl %08x (%c%c%c%c:%c%c:%s)",
- __get_str(name), __entry->queued, __entry->allocated,
- __entry->trb, __entry->bph, __entry->bpl,
+ TP_printk("%s: trb %p buf %08x%08x size %s%d ctrl %08x (%c%c%c%c:%c%c:%s)",
+ __get_str(name), __entry->trb, __entry->bph, __entry->bpl,
({char *s;
int pcm = ((__entry->size >> 24) & 3) + 1;
switch (__entry->type) {
@@ -306,7 +303,7 @@ 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:%c:%c",
+ TP_printk("%s: mps %d/%d streams %d burst %d ring %d/%d flags %c:%c%c%c%c:%c:%c",
__get_str(name), __entry->maxpacket,
__entry->maxpacket_limit, __entry->max_streams,
__entry->maxburst, __entry->trb_enqueue,
@@ -314,9 +311,8 @@ DECLARE_EVENT_CLASS(dwc3_log_ep,
__entry->flags & DWC3_EP_ENABLED ? 'E' : 'e',
__entry->flags & DWC3_EP_STALL ? 'S' : 's',
__entry->flags & DWC3_EP_WEDGE ? 'W' : 'w',
- __entry->flags & DWC3_EP_BUSY ? 'B' : 'b',
+ __entry->flags & DWC3_EP_TRANSFER_STARTED ? 'B' : 'b',
__entry->flags & DWC3_EP_PENDING_REQUEST ? 'P' : 'p',
- __entry->flags & DWC3_EP_MISSED_ISOC ? 'M' : 'm',
__entry->flags & DWC3_EP_END_TRANSFER_PENDING ? 'E' : 'e',
__entry->direction ? '<' : '>'
)
diff --git a/drivers/usb/early/xhci-dbc.c b/drivers/usb/early/xhci-dbc.c
index e15e896f356c..d2652dccc699 100644
--- a/drivers/usb/early/xhci-dbc.c
+++ b/drivers/usb/early/xhci-dbc.c
@@ -12,7 +12,7 @@
#include <linux/console.h>
#include <linux/pci_regs.h>
#include <linux/pci_ids.h>
-#include <linux/bootmem.h>
+#include <linux/memblock.h>
#include <linux/io.h>
#include <asm/pci-direct.h>
#include <asm/fixmap.h>
@@ -94,7 +94,7 @@ static void * __init xdbc_get_page(dma_addr_t *dma_addr)
{
void *virt;
- virt = alloc_bootmem_pages_nopanic(PAGE_SIZE);
+ virt = memblock_alloc_nopanic(PAGE_SIZE, PAGE_SIZE);
if (!virt)
return NULL;
@@ -191,7 +191,7 @@ static void __init xdbc_free_ring(struct xdbc_ring *ring)
if (!seg)
return;
- free_bootmem(seg->dma, PAGE_SIZE);
+ memblock_free(seg->dma, PAGE_SIZE);
ring->segment = NULL;
}
@@ -675,10 +675,10 @@ int __init early_xdbc_setup_hardware(void)
xdbc_free_ring(&xdbc.in_ring);
if (xdbc.table_dma)
- free_bootmem(xdbc.table_dma, PAGE_SIZE);
+ memblock_free(xdbc.table_dma, PAGE_SIZE);
if (xdbc.out_dma)
- free_bootmem(xdbc.out_dma, PAGE_SIZE);
+ memblock_free(xdbc.out_dma, PAGE_SIZE);
xdbc.table_base = NULL;
xdbc.out_buf = NULL;
@@ -717,17 +717,14 @@ static void xdbc_handle_port_status(struct xdbc_trb *evt_trb)
static void xdbc_handle_tx_event(struct xdbc_trb *evt_trb)
{
- size_t remain_length;
u32 comp_code;
int ep_id;
comp_code = GET_COMP_CODE(le32_to_cpu(evt_trb->field[2]));
- remain_length = EVENT_TRB_LEN(le32_to_cpu(evt_trb->field[2]));
ep_id = TRB_TO_EP_ID(le32_to_cpu(evt_trb->field[3]));
switch (comp_code) {
case COMP_SUCCESS:
- remain_length = 0;
case COMP_SHORT_PACKET:
break;
case COMP_TRB_ERROR:
@@ -1000,8 +997,8 @@ free_and_quit:
xdbc_free_ring(&xdbc.evt_ring);
xdbc_free_ring(&xdbc.out_ring);
xdbc_free_ring(&xdbc.in_ring);
- free_bootmem(xdbc.table_dma, PAGE_SIZE);
- free_bootmem(xdbc.out_dma, PAGE_SIZE);
+ memblock_free(xdbc.table_dma, PAGE_SIZE);
+ memblock_free(xdbc.out_dma, PAGE_SIZE);
writel(0, &xdbc.xdbc_reg->control);
early_iounmap(xdbc.xhci_base, xdbc.xhci_length);
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 63a7cb87514a..b8a15840b4ff 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -1601,7 +1601,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
cdev->gadget->ep0->maxpacket;
if (gadget_is_superspeed(gadget)) {
if (gadget->speed >= USB_SPEED_SUPER) {
- cdev->desc.bcdUSB = cpu_to_le16(0x0310);
+ cdev->desc.bcdUSB = cpu_to_le16(0x0320);
cdev->desc.bMaxPacketSize0 = 9;
} else {
cdev->desc.bcdUSB = cpu_to_le16(0x0210);
@@ -1719,6 +1719,8 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
*/
if (w_value && !f->get_alt)
break;
+
+ spin_lock(&cdev->lock);
value = f->set_alt(f, w_index, w_value);
if (value == USB_GADGET_DELAYED_STATUS) {
DBG(cdev,
@@ -1728,6 +1730,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
DBG(cdev, "delayed_status count %d\n",
cdev->delayed_status);
}
+ spin_unlock(&cdev->lock);
break;
case USB_REQ_GET_INTERFACE:
if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE))
@@ -1816,7 +1819,6 @@ unknown:
if (cdev->use_os_string && cdev->os_desc_config &&
(ctrl->bRequestType & USB_TYPE_VENDOR) &&
ctrl->bRequest == cdev->b_vendor_code) {
- struct usb_request *req;
struct usb_configuration *os_desc_cfg;
u8 *buf;
int interface;
diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c
index efba66ca0719..025129942894 100644
--- a/drivers/usb/gadget/configfs.c
+++ b/drivers/usb/gadget/configfs.c
@@ -1217,8 +1217,8 @@ static void purge_configs_funcs(struct gadget_info *gi)
list_move_tail(&f->list, &cfg->func_list);
if (f->unbind) {
dev_dbg(&gi->cdev.gadget->dev,
- "unbind function '%s'/%p\n",
- f->name, f);
+ "unbind function '%s'/%p\n",
+ f->name, f);
f->unbind(c, f);
}
}
diff --git a/drivers/usb/gadget/function/f_ecm.c b/drivers/usb/gadget/function/f_ecm.c
index b104ed0c1ab5..6ce044008cf6 100644
--- a/drivers/usb/gadget/function/f_ecm.c
+++ b/drivers/usb/gadget/function/f_ecm.c
@@ -705,6 +705,8 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f)
ecm_opts->bound = true;
}
+ ecm_string_defs[1].s = ecm->ethaddr;
+
us = usb_gstrings_attach(cdev, ecm_strings,
ARRAY_SIZE(ecm_string_defs));
if (IS_ERR(us))
@@ -928,7 +930,6 @@ static struct usb_function *ecm_alloc(struct usb_function_instance *fi)
mutex_unlock(&opts->lock);
return ERR_PTR(-EINVAL);
}
- ecm_string_defs[1].s = ecm->ethaddr;
ecm->port.ioport = netdev_priv(opts->net);
mutex_unlock(&opts->lock);
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index 0294e4f18873..31e8bf3578c8 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -1266,6 +1266,14 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code,
return ret;
}
+#ifdef CONFIG_COMPAT
+static long ffs_epfile_compat_ioctl(struct file *file, unsigned code,
+ unsigned long value)
+{
+ return ffs_epfile_ioctl(file, code, value);
+}
+#endif
+
static const struct file_operations ffs_epfile_operations = {
.llseek = no_llseek,
@@ -1274,6 +1282,9 @@ static const struct file_operations ffs_epfile_operations = {
.read_iter = ffs_epfile_read_iter,
.release = ffs_epfile_release,
.unlocked_ioctl = ffs_epfile_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = ffs_epfile_compat_ioctl,
+#endif
};
@@ -1297,7 +1308,7 @@ ffs_sb_make_inode(struct super_block *sb, void *data,
inode = new_inode(sb);
if (likely(inode)) {
- struct timespec ts = current_time(inode);
+ struct timespec64 ts = current_time(inode);
inode->i_ino = get_next_ino();
inode->i_mode = perms->mode;
@@ -3242,7 +3253,7 @@ static int ffs_func_setup(struct usb_function *f,
__ffs_event_add(ffs, FUNCTIONFS_SETUP);
spin_unlock_irqrestore(&ffs->ev.waitq.lock, flags);
- return USB_GADGET_DELAYED_STATUS;
+ return creq->wLength == 0 ? USB_GADGET_DELAYED_STATUS : 0;
}
static bool ffs_func_req_match(struct usb_function *f,
diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c
index acecd13dcbd9..043f97ad8f22 100644
--- a/drivers/usb/gadget/function/f_mass_storage.c
+++ b/drivers/usb/gadget/function/f_mass_storage.c
@@ -206,7 +206,6 @@
#include <linux/fcntl.h>
#include <linux/file.h>
#include <linux/fs.h>
-#include <linux/kref.h>
#include <linux/kthread.h>
#include <linux/sched/signal.h>
#include <linux/limits.h>
@@ -222,6 +221,8 @@
#include <linux/usb/gadget.h>
#include <linux/usb/composite.h>
+#include <linux/nospec.h>
+
#include "configfs.h"
@@ -312,8 +313,6 @@ struct fsg_common {
void *private_data;
char inquiry_string[INQUIRY_STRING_LEN];
-
- struct kref ref;
};
struct fsg_dev {
@@ -406,7 +405,7 @@ static void raise_exception(struct fsg_common *common, enum fsg_state new_state)
common->exception_req_tag = common->ep0_req_tag;
common->state = new_state;
if (common->thread_task)
- send_sig_info(SIGUSR1, SEND_SIG_FORCED,
+ send_sig_info(SIGUSR1, SEND_SIG_PRIV,
common->thread_task);
}
spin_unlock_irqrestore(&common->lock, flags);
@@ -2314,7 +2313,7 @@ static void handle_exception(struct fsg_common *common)
* into a high-priority EXIT exception.
*/
for (;;) {
- int sig = kernel_dequeue_signal(NULL);
+ int sig = kernel_dequeue_signal();
if (!sig)
break;
if (sig != SIGUSR1) {
@@ -2551,25 +2550,11 @@ static DEVICE_ATTR(file, 0, file_show, file_store);
/****************************** FSG COMMON ******************************/
-static void fsg_common_release(struct kref *ref);
-
static void fsg_lun_release(struct device *dev)
{
/* Nothing needs to be done */
}
-void fsg_common_get(struct fsg_common *common)
-{
- kref_get(&common->ref);
-}
-EXPORT_SYMBOL_GPL(fsg_common_get);
-
-void fsg_common_put(struct fsg_common *common)
-{
- kref_put(&common->ref, fsg_common_release);
-}
-EXPORT_SYMBOL_GPL(fsg_common_put);
-
static struct fsg_common *fsg_common_setup(struct fsg_common *common)
{
if (!common) {
@@ -2582,7 +2567,6 @@ static struct fsg_common *fsg_common_setup(struct fsg_common *common)
}
init_rwsem(&common->filesem);
spin_lock_init(&common->lock);
- kref_init(&common->ref);
init_completion(&common->thread_notifier);
init_waitqueue_head(&common->io_wait);
init_waitqueue_head(&common->fsg_wait);
@@ -2870,9 +2854,8 @@ void fsg_common_set_inquiry_string(struct fsg_common *common, const char *vn,
}
EXPORT_SYMBOL_GPL(fsg_common_set_inquiry_string);
-static void fsg_common_release(struct kref *ref)
+static void fsg_common_release(struct fsg_common *common)
{
- struct fsg_common *common = container_of(ref, struct fsg_common, ref);
int i;
/* If the thread isn't already dead, tell it to exit now */
@@ -3171,6 +3154,7 @@ static struct config_group *fsg_lun_make(struct config_group *group,
fsg_opts = to_fsg_opts(&group->cg_item);
if (num >= FSG_MAX_LUNS)
return ERR_PTR(-ERANGE);
+ num = array_index_nospec(num, FSG_MAX_LUNS);
mutex_lock(&fsg_opts->lock);
if (fsg_opts->refcnt || fsg_opts->common->luns[num]) {
@@ -3308,7 +3292,9 @@ static ssize_t fsg_opts_num_buffers_store(struct config_item *item,
if (ret)
goto end;
- fsg_common_set_num_buffers(opts->common, num);
+ ret = fsg_common_set_num_buffers(opts->common, num);
+ if (ret)
+ goto end;
ret = len;
end:
@@ -3344,7 +3330,7 @@ static void fsg_free_inst(struct usb_function_instance *fi)
struct fsg_opts *opts;
opts = fsg_opts_from_func_inst(fi);
- fsg_common_put(opts->common);
+ fsg_common_release(opts->common);
kfree(opts);
}
@@ -3368,7 +3354,7 @@ static struct usb_function_instance *fsg_alloc_inst(void)
rc = fsg_common_set_num_buffers(opts->common,
CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS);
if (rc)
- goto release_opts;
+ goto release_common;
pr_info(FSG_DRIVER_DESC ", version: " FSG_DRIVER_VERSION "\n");
@@ -3391,6 +3377,8 @@ static struct usb_function_instance *fsg_alloc_inst(void)
release_buffers:
fsg_common_free_buffers(opts->common);
+release_common:
+ kfree(opts->common);
release_opts:
kfree(opts);
return ERR_PTR(rc);
diff --git a/drivers/usb/gadget/function/f_mass_storage.h b/drivers/usb/gadget/function/f_mass_storage.h
index 58857fcf199f..3b8c4ce2a40a 100644
--- a/drivers/usb/gadget/function/f_mass_storage.h
+++ b/drivers/usb/gadget/function/f_mass_storage.h
@@ -115,10 +115,6 @@ fsg_opts_from_func_inst(const struct usb_function_instance *fi)
return container_of(fi, struct fsg_opts, func_inst);
}
-void fsg_common_get(struct fsg_common *common);
-
-void fsg_common_put(struct fsg_common *common);
-
void fsg_common_set_sysfs(struct fsg_common *common, bool sysfs);
int fsg_common_set_num_buffers(struct fsg_common *common, unsigned int n);
diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c
index e8f35db42394..46af0aa07e2e 100644
--- a/drivers/usb/gadget/function/f_midi.c
+++ b/drivers/usb/gadget/function/f_midi.c
@@ -109,6 +109,7 @@ static inline struct f_midi *func_to_midi(struct usb_function *f)
static void f_midi_transmit(struct f_midi *midi);
static void f_midi_rmidi_free(struct snd_rawmidi *rmidi);
+static void f_midi_free_inst(struct usb_function_instance *f);
DECLARE_UAC_AC_HEADER_DESCRIPTOR(1);
DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(1);
@@ -1102,7 +1103,7 @@ static ssize_t f_midi_opts_##name##_store(struct config_item *item, \
u32 num; \
\
mutex_lock(&opts->lock); \
- if (opts->refcnt) { \
+ if (opts->refcnt > 1) { \
ret = -EBUSY; \
goto end; \
} \
@@ -1157,7 +1158,7 @@ static ssize_t f_midi_opts_id_store(struct config_item *item,
char *c;
mutex_lock(&opts->lock);
- if (opts->refcnt) {
+ if (opts->refcnt > 1) {
ret = -EBUSY;
goto end;
}
@@ -1198,13 +1199,21 @@ static const struct config_item_type midi_func_type = {
static void f_midi_free_inst(struct usb_function_instance *f)
{
struct f_midi_opts *opts;
+ bool free = false;
opts = container_of(f, struct f_midi_opts, func_inst);
- if (opts->id_allocated)
- kfree(opts->id);
+ mutex_lock(&opts->lock);
+ if (!--opts->refcnt) {
+ free = true;
+ }
+ mutex_unlock(&opts->lock);
- kfree(opts);
+ if (free) {
+ if (opts->id_allocated)
+ kfree(opts->id);
+ kfree(opts);
+ }
}
static struct usb_function_instance *f_midi_alloc_inst(void)
@@ -1223,6 +1232,7 @@ static struct usb_function_instance *f_midi_alloc_inst(void)
opts->qlen = 32;
opts->in_ports = 1;
opts->out_ports = 1;
+ opts->refcnt = 1;
config_group_init_type_name(&opts->func_inst.group, "",
&midi_func_type);
@@ -1234,6 +1244,7 @@ static void f_midi_free(struct usb_function *f)
{
struct f_midi *midi;
struct f_midi_opts *opts;
+ bool free = false;
midi = func_to_midi(f);
opts = container_of(f->fi, struct f_midi_opts, func_inst);
@@ -1242,9 +1253,12 @@ static void f_midi_free(struct usb_function *f)
kfree(midi->id);
kfifo_free(&midi->in_req_fifo);
kfree(midi);
- --opts->refcnt;
+ free = true;
}
mutex_unlock(&opts->lock);
+
+ if (free)
+ f_midi_free_inst(&opts->func_inst);
}
static void f_midi_rmidi_free(struct snd_rawmidi *rmidi)
@@ -1287,9 +1301,8 @@ static struct usb_function *f_midi_alloc(struct usb_function_instance *fi)
}
/* allocate and initialize one new instance */
- midi = kzalloc(
- sizeof(*midi) + opts->in_ports * sizeof(*midi->in_ports_array),
- GFP_KERNEL);
+ midi = kzalloc(struct_size(midi, in_ports_array, opts->in_ports),
+ GFP_KERNEL);
if (!midi) {
status = -ENOMEM;
goto setup_fail;
diff --git a/drivers/usb/gadget/function/f_printer.c b/drivers/usb/gadget/function/f_printer.c
index d359efe06c76..9c7ed2539ff7 100644
--- a/drivers/usb/gadget/function/f_printer.c
+++ b/drivers/usb/gadget/function/f_printer.c
@@ -631,19 +631,19 @@ printer_write(struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
return -EAGAIN;
}
+ list_add(&req->list, &dev->tx_reqs_active);
+
/* here, we unlock, and only unlock, to avoid deadlock. */
spin_unlock(&dev->lock);
value = usb_ep_queue(dev->in_ep, req, GFP_ATOMIC);
spin_lock(&dev->lock);
if (value) {
+ list_del(&req->list);
list_add(&req->list, &dev->tx_reqs);
spin_unlock_irqrestore(&dev->lock, flags);
mutex_unlock(&dev->lock_printer_io);
return -EAGAIN;
}
-
- list_add(&req->list, &dev->tx_reqs_active);
-
}
spin_unlock_irqrestore(&dev->lock, flags);
diff --git a/drivers/usb/gadget/function/f_tcm.c b/drivers/usb/gadget/function/f_tcm.c
index d78dbb73bde8..106988a6661a 100644
--- a/drivers/usb/gadget/function/f_tcm.c
+++ b/drivers/usb/gadget/function/f_tcm.c
@@ -1071,15 +1071,16 @@ static struct usbg_cmd *usbg_get_cmd(struct f_uas *fu,
{
struct se_session *se_sess = tv_nexus->tvn_se_sess;
struct usbg_cmd *cmd;
- int tag;
+ int tag, cpu;
- tag = percpu_ida_alloc(&se_sess->sess_tag_pool, TASK_RUNNING);
+ tag = sbitmap_queue_get(&se_sess->sess_tag_pool, &cpu);
if (tag < 0)
return ERR_PTR(-ENOMEM);
cmd = &((struct usbg_cmd *)se_sess->sess_cmd_map)[tag];
memset(cmd, 0, sizeof(*cmd));
cmd->se_cmd.map_tag = tag;
+ cmd->se_cmd.map_cpu = cpu;
cmd->se_cmd.tag = cmd->tag = scsi_tag;
cmd->fu = fu;
@@ -1288,7 +1289,7 @@ static void usbg_release_cmd(struct se_cmd *se_cmd)
struct se_session *se_sess = se_cmd->se_sess;
kfree(cmd->data_buf);
- percpu_ida_free(&se_sess->sess_tag_pool, se_cmd->map_tag);
+ target_free_tag(se_sess, se_cmd);
}
static u32 usbg_sess_get_index(struct se_session *se_sess)
@@ -1343,10 +1344,8 @@ static int usbg_init_nodeacl(struct se_node_acl *se_nacl, const char *name)
return 0;
}
-static struct se_portal_group *usbg_make_tpg(
- struct se_wwn *wwn,
- struct config_group *group,
- const char *name)
+static struct se_portal_group *usbg_make_tpg(struct se_wwn *wwn,
+ const char *name)
{
struct usbg_tport *tport = container_of(wwn, struct usbg_tport,
tport_wwn);
@@ -1379,7 +1378,7 @@ static struct se_portal_group *usbg_make_tpg(
goto unlock_dep;
} else {
ret = configfs_depend_item_unlocked(
- group->cg_subsys,
+ wwn->wwn_group.cg_subsys,
&opts->func_inst.group.cg_item);
if (ret)
goto unlock_dep;
@@ -1593,7 +1592,7 @@ static int tcm_usbg_make_nexus(struct usbg_tpg *tpg, char *name)
goto out_unlock;
}
- tv_nexus->tvn_se_sess = target_alloc_session(&tpg->se_tpg,
+ tv_nexus->tvn_se_sess = target_setup_session(&tpg->se_tpg,
USB_G_DEFAULT_SESSION_TAGS,
sizeof(struct usbg_cmd),
TARGET_PROT_NORMAL, name,
@@ -1639,7 +1638,7 @@ static int tcm_usbg_drop_nexus(struct usbg_tpg *tpg)
/*
* Release the SCSI I_T Nexus to the emulated vHost Target Port
*/
- transport_deregister_session(tv_nexus->tvn_se_sess);
+ target_remove_session(se_sess);
tpg->tpg_nexus = NULL;
kfree(tv_nexus);
diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
index d2dc1f00180b..db2d4980cb35 100644
--- a/drivers/usb/gadget/function/f_uac2.c
+++ b/drivers/usb/gadget/function/f_uac2.c
@@ -22,12 +22,8 @@
* controlled by two clock sources :
* CLK_5 := c_srate, and CLK_6 := p_srate
*/
-#define USB_OUT_IT_ID 1
-#define IO_IN_IT_ID 2
-#define IO_OUT_OT_ID 3
-#define USB_IN_OT_ID 4
-#define USB_OUT_CLK_ID 5
-#define USB_IN_CLK_ID 6
+#define USB_OUT_CLK_ID (out_clk_src_desc.bClockID)
+#define USB_IN_CLK_ID (in_clk_src_desc.bClockID)
#define CONTROL_ABSENT 0
#define CONTROL_RDONLY 1
@@ -43,6 +39,9 @@
#define UNFLW_CTRL 8
#define OVFLW_CTRL 10
+#define EPIN_EN(_opts) ((_opts)->p_chmask != 0)
+#define EPOUT_EN(_opts) ((_opts)->c_chmask != 0)
+
struct f_uac2 {
struct g_audio g_audio;
u8 ac_intf, as_in_intf, as_out_intf;
@@ -135,7 +134,7 @@ static struct uac_clock_source_descriptor in_clk_src_desc = {
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = UAC2_CLOCK_SOURCE,
- .bClockID = USB_IN_CLK_ID,
+ /* .bClockID = DYNAMIC */
.bmAttributes = UAC_CLOCK_SOURCE_TYPE_INT_FIXED,
.bmControls = (CONTROL_RDONLY << CLK_FREQ_CTRL),
.bAssocTerminal = 0,
@@ -147,7 +146,7 @@ static struct uac_clock_source_descriptor out_clk_src_desc = {
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = UAC2_CLOCK_SOURCE,
- .bClockID = USB_OUT_CLK_ID,
+ /* .bClockID = DYNAMIC */
.bmAttributes = UAC_CLOCK_SOURCE_TYPE_INT_FIXED,
.bmControls = (CONTROL_RDONLY << CLK_FREQ_CTRL),
.bAssocTerminal = 0,
@@ -159,10 +158,10 @@ static struct uac2_input_terminal_descriptor usb_out_it_desc = {
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = UAC_INPUT_TERMINAL,
- .bTerminalID = USB_OUT_IT_ID,
+ /* .bTerminalID = DYNAMIC */
.wTerminalType = cpu_to_le16(UAC_TERMINAL_STREAMING),
.bAssocTerminal = 0,
- .bCSourceID = USB_OUT_CLK_ID,
+ /* .bCSourceID = DYNAMIC */
.iChannelNames = 0,
.bmControls = cpu_to_le16(CONTROL_RDWR << COPY_CTRL),
};
@@ -173,10 +172,10 @@ static struct uac2_input_terminal_descriptor io_in_it_desc = {
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = UAC_INPUT_TERMINAL,
- .bTerminalID = IO_IN_IT_ID,
+ /* .bTerminalID = DYNAMIC */
.wTerminalType = cpu_to_le16(UAC_INPUT_TERMINAL_UNDEFINED),
.bAssocTerminal = 0,
- .bCSourceID = USB_IN_CLK_ID,
+ /* .bCSourceID = DYNAMIC */
.iChannelNames = 0,
.bmControls = cpu_to_le16(CONTROL_RDWR << COPY_CTRL),
};
@@ -187,11 +186,11 @@ static struct uac2_output_terminal_descriptor usb_in_ot_desc = {
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = UAC_OUTPUT_TERMINAL,
- .bTerminalID = USB_IN_OT_ID,
+ /* .bTerminalID = DYNAMIC */
.wTerminalType = cpu_to_le16(UAC_TERMINAL_STREAMING),
.bAssocTerminal = 0,
- .bSourceID = IO_IN_IT_ID,
- .bCSourceID = USB_IN_CLK_ID,
+ /* .bSourceID = DYNAMIC */
+ /* .bCSourceID = DYNAMIC */
.bmControls = cpu_to_le16(CONTROL_RDWR << COPY_CTRL),
};
@@ -201,11 +200,11 @@ static struct uac2_output_terminal_descriptor io_out_ot_desc = {
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = UAC_OUTPUT_TERMINAL,
- .bTerminalID = IO_OUT_OT_ID,
+ /* .bTerminalID = DYNAMIC */
.wTerminalType = cpu_to_le16(UAC_OUTPUT_TERMINAL_UNDEFINED),
.bAssocTerminal = 0,
- .bSourceID = USB_OUT_IT_ID,
- .bCSourceID = USB_OUT_CLK_ID,
+ /* .bSourceID = DYNAMIC */
+ /* .bCSourceID = DYNAMIC */
.bmControls = cpu_to_le16(CONTROL_RDWR << COPY_CTRL),
};
@@ -253,7 +252,7 @@ static struct uac2_as_header_descriptor as_out_hdr_desc = {
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = UAC_AS_GENERAL,
- .bTerminalLink = USB_OUT_IT_ID,
+ /* .bTerminalLink = DYNAMIC */
.bmControls = 0,
.bFormatType = UAC_FORMAT_TYPE_I,
.bmFormats = cpu_to_le32(UAC_FORMAT_TYPE_I_PCM),
@@ -330,7 +329,7 @@ static struct uac2_as_header_descriptor as_in_hdr_desc = {
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = UAC_AS_GENERAL,
- .bTerminalLink = USB_IN_OT_ID,
+ /* .bTerminalLink = DYNAMIC */
.bmControls = 0,
.bFormatType = UAC_FORMAT_TYPE_I,
.bmFormats = cpu_to_le32(UAC_FORMAT_TYPE_I_PCM),
@@ -438,14 +437,14 @@ static struct usb_descriptor_header *hs_audio_desc[] = {
};
struct cntrl_cur_lay3 {
- __u32 dCUR;
+ __le32 dCUR;
};
struct cntrl_range_lay3 {
- __u16 wNumSubRanges;
- __u32 dMIN;
- __u32 dMAX;
- __u32 dRES;
+ __le16 wNumSubRanges;
+ __le32 dMIN;
+ __le32 dMAX;
+ __le32 dRES;
} __packed;
static void set_ep_max_packet_size(const struct f_uac2_opts *uac2_opts,
@@ -471,6 +470,125 @@ static void set_ep_max_packet_size(const struct f_uac2_opts *uac2_opts,
le16_to_cpu(ep_desc->wMaxPacketSize)));
}
+/* Use macro to overcome line length limitation */
+#define USBDHDR(p) (struct usb_descriptor_header *)(p)
+
+static void setup_descriptor(struct f_uac2_opts *opts)
+{
+ /* patch descriptors */
+ int i = 1; /* ID's start with 1 */
+
+ if (EPOUT_EN(opts))
+ usb_out_it_desc.bTerminalID = i++;
+ if (EPIN_EN(opts))
+ io_in_it_desc.bTerminalID = i++;
+ if (EPOUT_EN(opts))
+ io_out_ot_desc.bTerminalID = i++;
+ if (EPIN_EN(opts))
+ usb_in_ot_desc.bTerminalID = i++;
+ if (EPOUT_EN(opts))
+ out_clk_src_desc.bClockID = i++;
+ if (EPIN_EN(opts))
+ in_clk_src_desc.bClockID = i++;
+
+ usb_out_it_desc.bCSourceID = out_clk_src_desc.bClockID;
+ usb_in_ot_desc.bSourceID = io_in_it_desc.bTerminalID;
+ usb_in_ot_desc.bCSourceID = in_clk_src_desc.bClockID;
+ io_in_it_desc.bCSourceID = in_clk_src_desc.bClockID;
+ io_out_ot_desc.bCSourceID = out_clk_src_desc.bClockID;
+ io_out_ot_desc.bSourceID = usb_out_it_desc.bTerminalID;
+ as_out_hdr_desc.bTerminalLink = usb_out_it_desc.bTerminalID;
+ as_in_hdr_desc.bTerminalLink = usb_in_ot_desc.bTerminalID;
+
+ iad_desc.bInterfaceCount = 1;
+ ac_hdr_desc.wTotalLength = 0;
+
+ if (EPIN_EN(opts)) {
+ u16 len = le16_to_cpu(ac_hdr_desc.wTotalLength);
+
+ len += sizeof(in_clk_src_desc);
+ len += sizeof(usb_in_ot_desc);
+ len += sizeof(io_in_it_desc);
+ ac_hdr_desc.wTotalLength = cpu_to_le16(len);
+ iad_desc.bInterfaceCount++;
+ }
+ if (EPOUT_EN(opts)) {
+ u16 len = le16_to_cpu(ac_hdr_desc.wTotalLength);
+
+ len += sizeof(out_clk_src_desc);
+ len += sizeof(usb_out_it_desc);
+ len += sizeof(io_out_ot_desc);
+ ac_hdr_desc.wTotalLength = cpu_to_le16(len);
+ iad_desc.bInterfaceCount++;
+ }
+
+ i = 0;
+ fs_audio_desc[i++] = USBDHDR(&iad_desc);
+ fs_audio_desc[i++] = USBDHDR(&std_ac_if_desc);
+ fs_audio_desc[i++] = USBDHDR(&ac_hdr_desc);
+ if (EPIN_EN(opts))
+ fs_audio_desc[i++] = USBDHDR(&in_clk_src_desc);
+ if (EPOUT_EN(opts)) {
+ fs_audio_desc[i++] = USBDHDR(&out_clk_src_desc);
+ fs_audio_desc[i++] = USBDHDR(&usb_out_it_desc);
+ }
+ if (EPIN_EN(opts)) {
+ fs_audio_desc[i++] = USBDHDR(&io_in_it_desc);
+ fs_audio_desc[i++] = USBDHDR(&usb_in_ot_desc);
+ }
+ if (EPOUT_EN(opts)) {
+ fs_audio_desc[i++] = USBDHDR(&io_out_ot_desc);
+ fs_audio_desc[i++] = USBDHDR(&std_as_out_if0_desc);
+ fs_audio_desc[i++] = USBDHDR(&std_as_out_if1_desc);
+ fs_audio_desc[i++] = USBDHDR(&as_out_hdr_desc);
+ fs_audio_desc[i++] = USBDHDR(&as_out_fmt1_desc);
+ fs_audio_desc[i++] = USBDHDR(&fs_epout_desc);
+ fs_audio_desc[i++] = USBDHDR(&as_iso_out_desc);
+ }
+ if (EPIN_EN(opts)) {
+ fs_audio_desc[i++] = USBDHDR(&std_as_in_if0_desc);
+ fs_audio_desc[i++] = USBDHDR(&std_as_in_if1_desc);
+ fs_audio_desc[i++] = USBDHDR(&as_in_hdr_desc);
+ fs_audio_desc[i++] = USBDHDR(&as_in_fmt1_desc);
+ fs_audio_desc[i++] = USBDHDR(&fs_epin_desc);
+ fs_audio_desc[i++] = USBDHDR(&as_iso_in_desc);
+ }
+ fs_audio_desc[i] = NULL;
+
+ i = 0;
+ hs_audio_desc[i++] = USBDHDR(&iad_desc);
+ hs_audio_desc[i++] = USBDHDR(&std_ac_if_desc);
+ hs_audio_desc[i++] = USBDHDR(&ac_hdr_desc);
+ if (EPIN_EN(opts))
+ hs_audio_desc[i++] = USBDHDR(&in_clk_src_desc);
+ if (EPOUT_EN(opts)) {
+ hs_audio_desc[i++] = USBDHDR(&out_clk_src_desc);
+ hs_audio_desc[i++] = USBDHDR(&usb_out_it_desc);
+ }
+ if (EPIN_EN(opts)) {
+ hs_audio_desc[i++] = USBDHDR(&io_in_it_desc);
+ hs_audio_desc[i++] = USBDHDR(&usb_in_ot_desc);
+ }
+ if (EPOUT_EN(opts)) {
+ hs_audio_desc[i++] = USBDHDR(&io_out_ot_desc);
+ hs_audio_desc[i++] = USBDHDR(&std_as_out_if0_desc);
+ hs_audio_desc[i++] = USBDHDR(&std_as_out_if1_desc);
+ hs_audio_desc[i++] = USBDHDR(&as_out_hdr_desc);
+ hs_audio_desc[i++] = USBDHDR(&as_out_fmt1_desc);
+ hs_audio_desc[i++] = USBDHDR(&hs_epout_desc);
+ hs_audio_desc[i++] = USBDHDR(&as_iso_out_desc);
+ }
+ if (EPIN_EN(opts)) {
+ hs_audio_desc[i++] = USBDHDR(&std_as_in_if0_desc);
+ hs_audio_desc[i++] = USBDHDR(&std_as_in_if1_desc);
+ hs_audio_desc[i++] = USBDHDR(&as_in_hdr_desc);
+ hs_audio_desc[i++] = USBDHDR(&as_in_fmt1_desc);
+ hs_audio_desc[i++] = USBDHDR(&hs_epin_desc);
+ hs_audio_desc[i++] = USBDHDR(&as_iso_in_desc);
+ }
+ hs_audio_desc[i] = NULL;
+}
+
static int
afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
{
@@ -530,25 +648,29 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
uac2->ac_intf = ret;
uac2->ac_alt = 0;
- ret = usb_interface_id(cfg, fn);
- if (ret < 0) {
- dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
- return ret;
+ if (EPOUT_EN(uac2_opts)) {
+ ret = usb_interface_id(cfg, fn);
+ if (ret < 0) {
+ dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
+ return ret;
+ }
+ std_as_out_if0_desc.bInterfaceNumber = ret;
+ std_as_out_if1_desc.bInterfaceNumber = ret;
+ uac2->as_out_intf = ret;
+ uac2->as_out_alt = 0;
}
- std_as_out_if0_desc.bInterfaceNumber = ret;
- std_as_out_if1_desc.bInterfaceNumber = ret;
- uac2->as_out_intf = ret;
- uac2->as_out_alt = 0;
- ret = usb_interface_id(cfg, fn);
- if (ret < 0) {
- dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
- return ret;
+ if (EPIN_EN(uac2_opts)) {
+ ret = usb_interface_id(cfg, fn);
+ if (ret < 0) {
+ dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
+ return ret;
+ }
+ std_as_in_if0_desc.bInterfaceNumber = ret;
+ std_as_in_if1_desc.bInterfaceNumber = ret;
+ uac2->as_in_intf = ret;
+ uac2->as_in_alt = 0;
}
- std_as_in_if0_desc.bInterfaceNumber = ret;
- std_as_in_if1_desc.bInterfaceNumber = ret;
- uac2->as_in_intf = ret;
- uac2->as_in_alt = 0;
/* Calculate wMaxPacketSize according to audio bandwidth */
set_ep_max_packet_size(uac2_opts, &fs_epin_desc, 1000, true);
@@ -556,16 +678,20 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
set_ep_max_packet_size(uac2_opts, &hs_epin_desc, 8000, true);
set_ep_max_packet_size(uac2_opts, &hs_epout_desc, 8000, false);
- agdev->out_ep = usb_ep_autoconfig(gadget, &fs_epout_desc);
- if (!agdev->out_ep) {
- dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
- return ret;
+ if (EPOUT_EN(uac2_opts)) {
+ agdev->out_ep = usb_ep_autoconfig(gadget, &fs_epout_desc);
+ if (!agdev->out_ep) {
+ dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
+ return -ENODEV;
+ }
}
- agdev->in_ep = usb_ep_autoconfig(gadget, &fs_epin_desc);
- if (!agdev->in_ep) {
- dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
- return ret;
+ if (EPIN_EN(uac2_opts)) {
+ agdev->in_ep = usb_ep_autoconfig(gadget, &fs_epin_desc);
+ if (!agdev->in_ep) {
+ dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
+ return -ENODEV;
+ }
}
agdev->in_ep_maxpsize = max_t(u16,
@@ -578,6 +704,8 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
hs_epout_desc.bEndpointAddress = fs_epout_desc.bEndpointAddress;
hs_epin_desc.bEndpointAddress = fs_epin_desc.bEndpointAddress;
+ setup_descriptor(uac2_opts);
+
ret = usb_assign_descriptors(fn, fs_audio_desc, hs_audio_desc, NULL,
NULL);
if (ret)
@@ -703,9 +831,9 @@ in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
memset(&c, 0, sizeof(struct cntrl_cur_lay3));
if (entity_id == USB_IN_CLK_ID)
- c.dCUR = p_srate;
+ c.dCUR = cpu_to_le32(p_srate);
else if (entity_id == USB_OUT_CLK_ID)
- c.dCUR = c_srate;
+ c.dCUR = cpu_to_le32(c_srate);
value = min_t(unsigned, w_length, sizeof c);
memcpy(req->buf, &c, value);
@@ -742,15 +870,15 @@ in_rq_range(struct usb_function *fn, const struct usb_ctrlrequest *cr)
if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) {
if (entity_id == USB_IN_CLK_ID)
- r.dMIN = p_srate;
+ r.dMIN = cpu_to_le32(p_srate);
else if (entity_id == USB_OUT_CLK_ID)
- r.dMIN = c_srate;
+ r.dMIN = cpu_to_le32(c_srate);
else
return -EOPNOTSUPP;
r.dMAX = r.dMIN;
r.dRES = 0;
- r.wNumSubRanges = 1;
+ r.wNumSubRanges = cpu_to_le16(1);
value = min_t(unsigned, w_length, sizeof r);
memcpy(req->buf, &r, value);
diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c
index 439eba660e95..8c99392df593 100644
--- a/drivers/usb/gadget/function/f_uvc.c
+++ b/drivers/usb/gadget/function/f_uvc.c
@@ -6,16 +6,17 @@
* Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*/
-#include <linux/kernel.h>
-#include <linux/module.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/fs.h>
+#include <linux/kernel.h>
#include <linux/list.h>
+#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/string.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
+#include <linux/usb/g_uvc.h>
#include <linux/usb/video.h>
#include <linux/vmalloc.h>
#include <linux/wait.h>
@@ -30,6 +31,8 @@
#include "uvc_video.h"
unsigned int uvc_gadget_trace_param;
+module_param_named(trace, uvc_gadget_trace_param, uint, 0644);
+MODULE_PARM_DESC(trace, "Trace level bitmask");
/* --------------------------------------------------------------------------
* Function descriptors
@@ -194,12 +197,6 @@ static const struct usb_descriptor_header * const uvc_ss_streaming[] = {
NULL,
};
-void uvc_set_trace_param(unsigned int trace)
-{
- uvc_gadget_trace_param = trace;
-}
-EXPORT_SYMBOL(uvc_set_trace_param);
-
/* --------------------------------------------------------------------------
* Control requests
*/
@@ -229,13 +226,8 @@ uvc_function_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
struct v4l2_event v4l2_event;
struct uvc_event *uvc_event = (void *)&v4l2_event.u.data;
- /* printk(KERN_INFO "setup request %02x %02x value %04x index %04x %04x\n",
- * ctrl->bRequestType, ctrl->bRequest, le16_to_cpu(ctrl->wValue),
- * le16_to_cpu(ctrl->wIndex), le16_to_cpu(ctrl->wLength));
- */
-
if ((ctrl->bRequestType & USB_TYPE_MASK) != USB_TYPE_CLASS) {
- INFO(f->config->cdev, "invalid request type\n");
+ uvcg_info(f, "invalid request type\n");
return -EINVAL;
}
@@ -269,7 +261,7 @@ uvc_function_get_alt(struct usb_function *f, unsigned interface)
{
struct uvc_device *uvc = to_uvc(f);
- INFO(f->config->cdev, "uvc_function_get_alt(%u)\n", interface);
+ uvcg_info(f, "%s(%u)\n", __func__, interface);
if (interface == uvc->control_intf)
return 0;
@@ -288,13 +280,13 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt)
struct uvc_event *uvc_event = (void *)&v4l2_event.u.data;
int ret;
- INFO(cdev, "uvc_function_set_alt(%u, %u)\n", interface, alt);
+ uvcg_info(f, "%s(%u, %u)\n", __func__, interface, alt);
if (interface == uvc->control_intf) {
if (alt)
return -EINVAL;
- INFO(cdev, "reset UVC Control\n");
+ uvcg_info(f, "reset UVC Control\n");
usb_ep_disable(uvc->control_ep);
if (!uvc->control_ep->desc)
@@ -345,7 +337,7 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt)
if (!uvc->video.ep)
return -EINVAL;
- INFO(cdev, "reset UVC\n");
+ uvcg_info(f, "reset UVC\n");
usb_ep_disable(uvc->video.ep);
ret = config_ep_by_speed(f->config->cdev->gadget,
@@ -370,7 +362,7 @@ uvc_function_disable(struct usb_function *f)
struct uvc_device *uvc = to_uvc(f);
struct v4l2_event v4l2_event;
- INFO(f->config->cdev, "uvc_function_disable\n");
+ uvcg_info(f, "%s()\n", __func__);
memset(&v4l2_event, 0, sizeof(v4l2_event));
v4l2_event.type = UVC_EVENT_DISCONNECT;
@@ -389,31 +381,40 @@ uvc_function_disable(struct usb_function *f)
void
uvc_function_connect(struct uvc_device *uvc)
{
- struct usb_composite_dev *cdev = uvc->func.config->cdev;
int ret;
if ((ret = usb_function_activate(&uvc->func)) < 0)
- INFO(cdev, "UVC connect failed with %d\n", ret);
+ uvcg_info(&uvc->func, "UVC connect failed with %d\n", ret);
}
void
uvc_function_disconnect(struct uvc_device *uvc)
{
- struct usb_composite_dev *cdev = uvc->func.config->cdev;
int ret;
if ((ret = usb_function_deactivate(&uvc->func)) < 0)
- INFO(cdev, "UVC disconnect failed with %d\n", ret);
+ uvcg_info(&uvc->func, "UVC disconnect failed with %d\n", ret);
}
/* --------------------------------------------------------------------------
* USB probe and disconnect
*/
+static ssize_t function_name_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct uvc_device *uvc = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s\n", uvc->func.fi->group.cg_item.ci_name);
+}
+
+static DEVICE_ATTR_RO(function_name);
+
static int
uvc_register_video(struct uvc_device *uvc)
{
struct usb_composite_dev *cdev = uvc->func.config->cdev;
+ int ret;
/* TODO reference counting. */
uvc->vdev.v4l2_dev = &uvc->v4l2_dev;
@@ -426,7 +427,17 @@ uvc_register_video(struct uvc_device *uvc)
video_set_drvdata(&uvc->vdev, uvc);
- return video_register_device(&uvc->vdev, VFL_TYPE_GRABBER, -1);
+ ret = video_register_device(&uvc->vdev, VFL_TYPE_GRABBER, -1);
+ if (ret < 0)
+ return ret;
+
+ ret = device_create_file(&uvc->vdev.dev, &dev_attr_function_name);
+ if (ret < 0) {
+ video_unregister_device(&uvc->vdev);
+ return ret;
+ }
+
+ return 0;
}
#define UVC_COPY_DESCRIPTOR(mem, dst, desc) \
@@ -581,7 +592,7 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
struct f_uvc_opts *opts;
int ret = -EINVAL;
- INFO(cdev, "uvc_function_bind\n");
+ uvcg_info(f, "%s()\n", __func__);
opts = fi_to_f_uvc_opts(f->fi);
/* Sanity check the streaming endpoint module parameters.
@@ -594,8 +605,8 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
if (opts->streaming_maxburst &&
(opts->streaming_maxpacket % 1024) != 0) {
opts->streaming_maxpacket = roundup(opts->streaming_maxpacket, 1024);
- INFO(cdev, "overriding streaming_maxpacket to %d\n",
- opts->streaming_maxpacket);
+ uvcg_info(f, "overriding streaming_maxpacket to %d\n",
+ opts->streaming_maxpacket);
}
/* Fill in the FS/HS/SS Video Streaming specific descriptors from the
@@ -634,7 +645,7 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
/* Allocate endpoints. */
ep = usb_ep_autoconfig(cdev->gadget, &uvc_control_ep);
if (!ep) {
- INFO(cdev, "Unable to allocate control EP\n");
+ uvcg_info(f, "Unable to allocate control EP\n");
goto error;
}
uvc->control_ep = ep;
@@ -648,7 +659,7 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
ep = usb_ep_autoconfig(cdev->gadget, &uvc_fs_streaming_ep);
if (!ep) {
- INFO(cdev, "Unable to allocate streaming EP\n");
+ uvcg_info(f, "Unable to allocate streaming EP\n");
goto error;
}
uvc->video.ep = ep;
@@ -675,12 +686,14 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
uvc_iad.bFirstInterface = ret;
uvc_control_intf.bInterfaceNumber = ret;
uvc->control_intf = ret;
+ opts->control_interface = ret;
if ((ret = usb_interface_id(c, f)) < 0)
goto error;
uvc_streaming_intf_alt0.bInterfaceNumber = ret;
uvc_streaming_intf_alt1.bInterfaceNumber = ret;
uvc->streaming_intf = ret;
+ opts->streaming_interface = ret;
/* Copy descriptors */
f->fs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_FULL);
@@ -719,19 +732,19 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
uvc->control_req->context = uvc;
if (v4l2_device_register(&cdev->gadget->dev, &uvc->v4l2_dev)) {
- printk(KERN_INFO "v4l2_device_register failed\n");
+ uvcg_err(f, "failed to register V4L2 device\n");
goto error;
}
/* Initialise video. */
- ret = uvcg_video_init(&uvc->video);
+ ret = uvcg_video_init(&uvc->video, uvc);
if (ret < 0)
goto error;
/* Register a V4L2 device. */
ret = uvc_register_video(uvc);
if (ret < 0) {
- printk(KERN_INFO "Unable to register video device\n");
+ uvcg_err(f, "failed to register video device\n");
goto error;
}
@@ -768,6 +781,7 @@ static struct usb_function_instance *uvc_alloc_inst(void)
struct uvc_output_terminal_descriptor *od;
struct uvc_color_matching_descriptor *md;
struct uvc_descriptor_header **ctl_cls;
+ int ret;
opts = kzalloc(sizeof(*opts), GFP_KERNEL);
if (!opts)
@@ -844,7 +858,12 @@ static struct usb_function_instance *uvc_alloc_inst(void)
opts->streaming_interval = 1;
opts->streaming_maxpacket = 1024;
- uvcg_attach_configfs(opts);
+ ret = uvcg_attach_configfs(opts);
+ if (ret < 0) {
+ kfree(opts);
+ return ERR_PTR(ret);
+ }
+
return &opts->func_inst;
}
@@ -862,8 +881,9 @@ static void uvc_unbind(struct usb_configuration *c, struct usb_function *f)
struct usb_composite_dev *cdev = c->cdev;
struct uvc_device *uvc = to_uvc(f);
- INFO(cdev, "%s\n", __func__);
+ uvcg_info(f, "%s\n", __func__);
+ device_remove_file(&uvc->vdev.dev, &dev_attr_function_name);
video_unregister_device(&uvc->vdev);
v4l2_device_unregister(&uvc->v4l2_dev);
diff --git a/drivers/usb/gadget/function/f_uvc.h b/drivers/usb/gadget/function/f_uvc.h
index 81defe4557fe..a81a17765558 100644
--- a/drivers/usb/gadget/function/f_uvc.h
+++ b/drivers/usb/gadget/function/f_uvc.h
@@ -9,10 +9,7 @@
#ifndef _F_UVC_H_
#define _F_UVC_H_
-#include <linux/usb/composite.h>
-#include <linux/usb/video.h>
-
-#include "uvc.h"
+struct uvc_device;
void uvc_function_setup_continue(struct uvc_device *uvc);
@@ -21,4 +18,3 @@ void uvc_function_connect(struct uvc_device *uvc);
void uvc_function_disconnect(struct uvc_device *uvc);
#endif /* _F_UVC_H_ */
-
diff --git a/drivers/usb/gadget/function/rndis.c b/drivers/usb/gadget/function/rndis.c
index 51dd3e90b06c..04c142c13075 100644
--- a/drivers/usb/gadget/function/rndis.c
+++ b/drivers/usb/gadget/function/rndis.c
@@ -851,6 +851,9 @@ int rndis_msg_parser(struct rndis_params *params, u8 *buf)
*/
pr_warn("%s: unknown RNDIS message 0x%08X len %d\n",
__func__, MsgType, MsgLength);
+ /* Garbled message can be huge, so limit what we display */
+ if (MsgLength > 16)
+ MsgLength = 16;
print_hex_dump_bytes(__func__, DUMP_PREFIX_OFFSET,
buf, MsgLength);
break;
diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c
index a72295c953bb..fb5ed97572e5 100644
--- a/drivers/usb/gadget/function/u_audio.c
+++ b/drivers/usb/gadget/function/u_audio.c
@@ -32,9 +32,6 @@ struct uac_req {
struct uac_rtd_params {
struct snd_uac_chip *uac; /* parent chip */
bool ep_enabled; /* if the ep is enabled */
- /* Size of the ring buffer */
- size_t dma_bytes;
- unsigned char *dma_area;
struct snd_pcm_substream *ss;
@@ -43,8 +40,6 @@ struct uac_rtd_params {
void *rbuf;
- size_t period_size;
-
unsigned max_psize; /* MaxPacketSize of endpoint */
struct uac_req *ureq;
@@ -84,12 +79,12 @@ static const struct snd_pcm_hardware uac_pcm_hardware = {
static void u_audio_iso_complete(struct usb_ep *ep, struct usb_request *req)
{
unsigned pending;
- unsigned long flags;
+ unsigned long flags, flags2;
unsigned int hw_ptr;
- bool update_alsa = false;
int status = req->status;
struct uac_req *ur = req->context;
struct snd_pcm_substream *substream;
+ struct snd_pcm_runtime *runtime;
struct uac_rtd_params *prm = ur->pp;
struct snd_uac_chip *uac = prm->uac;
@@ -111,6 +106,14 @@ static void u_audio_iso_complete(struct usb_ep *ep, struct usb_request *req)
if (!substream)
goto exit;
+ snd_pcm_stream_lock_irqsave(substream, flags2);
+
+ runtime = substream->runtime;
+ if (!runtime || !snd_pcm_running(substream)) {
+ snd_pcm_stream_unlock_irqrestore(substream, flags2);
+ goto exit;
+ }
+
spin_lock_irqsave(&prm->lock, flags);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
@@ -137,43 +140,46 @@ static void u_audio_iso_complete(struct usb_ep *ep, struct usb_request *req)
req->actual = req->length;
}
- pending = prm->hw_ptr % prm->period_size;
- pending += req->actual;
- if (pending >= prm->period_size)
- update_alsa = true;
-
hw_ptr = prm->hw_ptr;
- prm->hw_ptr = (prm->hw_ptr + req->actual) % prm->dma_bytes;
spin_unlock_irqrestore(&prm->lock, flags);
/* Pack USB load in ALSA ring buffer */
- pending = prm->dma_bytes - hw_ptr;
+ pending = runtime->dma_bytes - hw_ptr;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
if (unlikely(pending < req->actual)) {
- memcpy(req->buf, prm->dma_area + hw_ptr, pending);
- memcpy(req->buf + pending, prm->dma_area,
+ memcpy(req->buf, runtime->dma_area + hw_ptr, pending);
+ memcpy(req->buf + pending, runtime->dma_area,
req->actual - pending);
} else {
- memcpy(req->buf, prm->dma_area + hw_ptr, req->actual);
+ memcpy(req->buf, runtime->dma_area + hw_ptr,
+ req->actual);
}
} else {
if (unlikely(pending < req->actual)) {
- memcpy(prm->dma_area + hw_ptr, req->buf, pending);
- memcpy(prm->dma_area, req->buf + pending,
+ memcpy(runtime->dma_area + hw_ptr, req->buf, pending);
+ memcpy(runtime->dma_area, req->buf + pending,
req->actual - pending);
} else {
- memcpy(prm->dma_area + hw_ptr, req->buf, req->actual);
+ memcpy(runtime->dma_area + hw_ptr, req->buf,
+ req->actual);
}
}
+ spin_lock_irqsave(&prm->lock, flags);
+ /* update hw_ptr after data is copied to memory */
+ prm->hw_ptr = (hw_ptr + req->actual) % runtime->dma_bytes;
+ hw_ptr = prm->hw_ptr;
+ spin_unlock_irqrestore(&prm->lock, flags);
+ snd_pcm_stream_unlock_irqrestore(substream, flags2);
+
+ if ((hw_ptr % snd_pcm_lib_period_bytes(substream)) < req->actual)
+ snd_pcm_period_elapsed(substream);
+
exit:
if (usb_ep_queue(ep, req, GFP_ATOMIC))
dev_err(uac->card->dev, "%d Error!\n", __LINE__);
-
- if (update_alsa)
- snd_pcm_period_elapsed(substream);
}
static int uac_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
@@ -236,40 +242,12 @@ static snd_pcm_uframes_t uac_pcm_pointer(struct snd_pcm_substream *substream)
static int uac_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
- struct snd_uac_chip *uac = snd_pcm_substream_chip(substream);
- struct uac_rtd_params *prm;
- int err;
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- prm = &uac->p_prm;
- else
- prm = &uac->c_prm;
-
- err = snd_pcm_lib_malloc_pages(substream,
+ return snd_pcm_lib_malloc_pages(substream,
params_buffer_bytes(hw_params));
- if (err >= 0) {
- prm->dma_bytes = substream->runtime->dma_bytes;
- prm->dma_area = substream->runtime->dma_area;
- prm->period_size = params_period_bytes(hw_params);
- }
-
- return err;
}
static int uac_pcm_hw_free(struct snd_pcm_substream *substream)
{
- struct snd_uac_chip *uac = snd_pcm_substream_chip(substream);
- struct uac_rtd_params *prm;
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- prm = &uac->p_prm;
- else
- prm = &uac->c_prm;
-
- prm->dma_area = NULL;
- prm->dma_bytes = 0;
- prm->period_size = 0;
-
return snd_pcm_lib_free_pages(substream);
}
@@ -595,15 +573,15 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
if (err < 0)
goto snd_fail;
- strcpy(pcm->name, pcm_name);
+ strlcpy(pcm->name, pcm_name, sizeof(pcm->name));
pcm->private_data = uac;
uac->pcm = pcm;
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &uac_pcm_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &uac_pcm_ops);
- strcpy(card->driver, card_name);
- strcpy(card->shortname, card_name);
+ strlcpy(card->driver, card_name, sizeof(card->driver));
+ strlcpy(card->shortname, card_name, sizeof(card->shortname));
sprintf(card->longname, "%s %i", card_name, card->dev->id);
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c
index 6fcda62f55ea..0f026d445e31 100644
--- a/drivers/usb/gadget/function/u_ether.c
+++ b/drivers/usb/gadget/function/u_ether.c
@@ -401,12 +401,12 @@ done:
static void rx_fill(struct eth_dev *dev, gfp_t gfp_flags)
{
struct usb_request *req;
- struct usb_request *tmp;
unsigned long flags;
/* fill unused rxq slots with some skb */
spin_lock_irqsave(&dev->req_lock, flags);
- list_for_each_entry_safe(req, tmp, &dev->rx_reqs, list) {
+ while (!list_empty(&dev->rx_reqs)) {
+ req = list_first_entry(&dev->rx_reqs, struct usb_request, list);
list_del_init(&req->list);
spin_unlock_irqrestore(&dev->req_lock, flags);
@@ -844,6 +844,10 @@ struct net_device *gether_setup_name_default(const char *netname)
net->ethtool_ops = &ops;
SET_NETDEV_DEVTYPE(net, &gadget_type);
+ /* MTU range: 14 - 15412 */
+ net->min_mtu = ETH_HLEN;
+ net->max_mtu = GETHER_MAX_ETH_FRAME_LEN;
+
return net;
}
EXPORT_SYMBOL_GPL(gether_setup_name_default);
@@ -1121,7 +1125,6 @@ void gether_disconnect(struct gether *link)
{
struct eth_dev *dev = link->ioport;
struct usb_request *req;
- struct usb_request *tmp;
WARN_ON(!dev);
if (!dev)
@@ -1138,7 +1141,8 @@ void gether_disconnect(struct gether *link)
*/
usb_ep_disable(link->in_ep);
spin_lock(&dev->req_lock);
- list_for_each_entry_safe(req, tmp, &dev->tx_reqs, list) {
+ while (!list_empty(&dev->tx_reqs)) {
+ req = list_first_entry(&dev->tx_reqs, struct usb_request, list);
list_del(&req->list);
spin_unlock(&dev->req_lock);
@@ -1150,7 +1154,8 @@ void gether_disconnect(struct gether *link)
usb_ep_disable(link->out_ep);
spin_lock(&dev->req_lock);
- list_for_each_entry_safe(req, tmp, &dev->rx_reqs, list) {
+ while (!list_empty(&dev->rx_reqs)) {
+ req = list_first_entry(&dev->rx_reqs, struct usb_request, list);
list_del(&req->list);
spin_unlock(&dev->req_lock);
diff --git a/drivers/usb/gadget/function/u_uvc.h b/drivers/usb/gadget/function/u_uvc.h
index d00d3ded71c0..5242d489e20a 100644
--- a/drivers/usb/gadget/function/u_uvc.h
+++ b/drivers/usb/gadget/function/u_uvc.h
@@ -13,6 +13,7 @@
#ifndef U_UVC_H
#define U_UVC_H
+#include <linux/mutex.h>
#include <linux/usb/composite.h>
#include <linux/usb/video.h>
@@ -20,11 +21,13 @@
struct f_uvc_opts {
struct usb_function_instance func_inst;
- unsigned int uvc_gadget_trace_param;
unsigned int streaming_interval;
unsigned int streaming_maxpacket;
unsigned int streaming_maxburst;
+ unsigned int control_interface;
+ unsigned int streaming_interface;
+
/*
* Control descriptors array pointers for full-/high-speed and
* super-speed. They point by default to the uvc_fs_control_cls and
@@ -80,7 +83,4 @@ struct f_uvc_opts {
int refcnt;
};
-void uvc_set_trace_param(unsigned int trace);
-
#endif /* U_UVC_H */
-
diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h
index a64e07e61f8c..099d650082e5 100644
--- a/drivers/usb/gadget/function/uvc.h
+++ b/drivers/usb/gadget/function/uvc.h
@@ -9,52 +9,27 @@
#ifndef _UVC_GADGET_H_
#define _UVC_GADGET_H_
-#include <linux/ioctl.h>
-#include <linux/types.h>
-#include <linux/usb/ch9.h>
-
-#define UVC_EVENT_FIRST (V4L2_EVENT_PRIVATE_START + 0)
-#define UVC_EVENT_CONNECT (V4L2_EVENT_PRIVATE_START + 0)
-#define UVC_EVENT_DISCONNECT (V4L2_EVENT_PRIVATE_START + 1)
-#define UVC_EVENT_STREAMON (V4L2_EVENT_PRIVATE_START + 2)
-#define UVC_EVENT_STREAMOFF (V4L2_EVENT_PRIVATE_START + 3)
-#define UVC_EVENT_SETUP (V4L2_EVENT_PRIVATE_START + 4)
-#define UVC_EVENT_DATA (V4L2_EVENT_PRIVATE_START + 5)
-#define UVC_EVENT_LAST (V4L2_EVENT_PRIVATE_START + 5)
-
-struct uvc_request_data {
- __s32 length;
- __u8 data[60];
-};
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/usb/composite.h>
+#include <linux/videodev2.h>
-struct uvc_event {
- union {
- enum usb_device_speed speed;
- struct usb_ctrlrequest req;
- struct uvc_request_data data;
- };
-};
+#include <media/v4l2-device.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-fh.h>
-#define UVCIOC_SEND_RESPONSE _IOW('U', 1, struct uvc_request_data)
+#include "uvc_queue.h"
-#define UVC_INTF_CONTROL 0
-#define UVC_INTF_STREAMING 1
+struct usb_ep;
+struct usb_request;
+struct uvc_descriptor_header;
+struct uvc_device;
/* ------------------------------------------------------------------------
* Debugging, printing and logging
*/
-#ifdef __KERNEL__
-
-#include <linux/usb.h> /* For usb_endpoint_* */
-#include <linux/usb/composite.h>
-#include <linux/usb/gadget.h>
-#include <linux/videodev2.h>
-#include <media/v4l2-fh.h>
-#include <media/v4l2-device.h>
-
-#include "uvc_queue.h"
-
#define UVC_TRACE_PROBE (1 << 0)
#define UVC_TRACE_DESCR (1 << 1)
#define UVC_TRACE_CONTROL (1 << 2)
@@ -77,14 +52,12 @@ extern unsigned int uvc_gadget_trace_param;
printk(KERN_DEBUG "uvcvideo: " msg); \
} while (0)
-#define uvc_warn_once(dev, warn, msg...) \
- do { \
- if (!test_and_set_bit(warn, &dev->warnings)) \
- printk(KERN_INFO "uvcvideo: " msg); \
- } while (0)
-
-#define uvc_printk(level, msg...) \
- printk(level "uvcvideo: " msg)
+#define uvcg_dbg(f, fmt, args...) \
+ dev_dbg(&(f)->config->cdev->gadget->dev, "%s: " fmt, (f)->name, ##args)
+#define uvcg_info(f, fmt, args...) \
+ dev_info(&(f)->config->cdev->gadget->dev, "%s: " fmt, (f)->name, ##args)
+#define uvcg_err(f, fmt, args...) \
+ dev_err(&(f)->config->cdev->gadget->dev, "%s: " fmt, (f)->name, ##args)
/* ------------------------------------------------------------------------
* Driver specific constants
@@ -99,6 +72,7 @@ extern unsigned int uvc_gadget_trace_param;
*/
struct uvc_video {
+ struct uvc_device *uvc;
struct usb_ep *ep;
/* Frame parameters */
@@ -184,7 +158,4 @@ extern void uvc_endpoint_stream(struct uvc_device *dev);
extern void uvc_function_connect(struct uvc_device *uvc);
extern void uvc_function_disconnect(struct uvc_device *uvc);
-#endif /* __KERNEL__ */
-
#endif /* _UVC_GADGET_H_ */
-
diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c
index c9b8cc4aae5a..bc1e2af566c3 100644
--- a/drivers/usb/gadget/function/uvc_configfs.c
+++ b/drivers/usb/gadget/function/uvc_configfs.c
@@ -9,9 +9,16 @@
*
* Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
*/
+
+#include <linux/sort.h>
+
#include "u_uvc.h"
#include "uvc_configfs.h"
+/* -----------------------------------------------------------------------------
+ * Global Utility Structures and Macros
+ */
+
#define UVCG_STREAMING_CONTROL_SIZE 1
#define UVC_ATTR(prefix, cname, aname) \
@@ -31,9 +38,93 @@ static struct configfs_attribute prefix##attr_##cname = { \
.show = prefix##cname##_show, \
}
-static inline struct f_uvc_opts *to_f_uvc_opts(struct config_item *item);
+#define le8_to_cpu(x) (x)
+#define cpu_to_le8(x) (x)
+
+static int uvcg_config_compare_u32(const void *l, const void *r)
+{
+ u32 li = *(const u32 *)l;
+ u32 ri = *(const u32 *)r;
+
+ return li < ri ? -1 : li == ri ? 0 : 1;
+}
+
+static inline struct f_uvc_opts *to_f_uvc_opts(struct config_item *item)
+{
+ return container_of(to_config_group(item), struct f_uvc_opts,
+ func_inst.group);
+}
+
+struct uvcg_config_group_type {
+ struct config_item_type type;
+ const char *name;
+ const struct uvcg_config_group_type **children;
+ int (*create_children)(struct config_group *group);
+};
+
+static void uvcg_config_item_release(struct config_item *item)
+{
+ struct config_group *group = to_config_group(item);
+
+ kfree(group);
+}
+
+static struct configfs_item_operations uvcg_config_item_ops = {
+ .release = uvcg_config_item_release,
+};
+
+static int uvcg_config_create_group(struct config_group *parent,
+ const struct uvcg_config_group_type *type);
+
+static int uvcg_config_create_children(struct config_group *group,
+ const struct uvcg_config_group_type *type)
+{
+ const struct uvcg_config_group_type **child;
+ int ret;
+
+ if (type->create_children)
+ return type->create_children(group);
+
+ for (child = type->children; child && *child; ++child) {
+ ret = uvcg_config_create_group(group, *child);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int uvcg_config_create_group(struct config_group *parent,
+ const struct uvcg_config_group_type *type)
+{
+ struct config_group *group;
+
+ group = kzalloc(sizeof(*group), GFP_KERNEL);
+ if (!group)
+ return -ENOMEM;
+
+ config_group_init_type_name(group, type->name, &type->type);
+ configfs_add_default_group(group, parent);
+
+ return uvcg_config_create_children(group, type);
+}
+
+static void uvcg_config_remove_children(struct config_group *group)
+{
+ struct config_group *child, *n;
+
+ list_for_each_entry_safe(child, n, &group->default_groups, group_entry) {
+ list_del(&child->group_entry);
+ uvcg_config_remove_children(child);
+ config_item_put(&child->cg_item);
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * control/header/<NAME>
+ * control/header
+ */
-/* control/header/<NAME> */
DECLARE_UVC_HEADER_DESCRIPTOR(1);
struct uvcg_control_header {
@@ -47,9 +138,9 @@ static struct uvcg_control_header *to_uvcg_control_header(struct config_item *it
return container_of(item, struct uvcg_control_header, item);
}
-#define UVCG_CTRL_HDR_ATTR(cname, aname, conv, str2u, uxx, vnoc, limit) \
+#define UVCG_CTRL_HDR_ATTR(cname, aname, bits, limit) \
static ssize_t uvcg_control_header_##cname##_show( \
- struct config_item *item, char *page) \
+ struct config_item *item, char *page) \
{ \
struct uvcg_control_header *ch = to_uvcg_control_header(item); \
struct f_uvc_opts *opts; \
@@ -63,7 +154,7 @@ static ssize_t uvcg_control_header_##cname##_show( \
opts = to_f_uvc_opts(opts_item); \
\
mutex_lock(&opts->lock); \
- result = sprintf(page, "%d\n", conv(ch->desc.aname)); \
+ result = sprintf(page, "%u\n", le##bits##_to_cpu(ch->desc.aname));\
mutex_unlock(&opts->lock); \
\
mutex_unlock(su_mutex); \
@@ -79,7 +170,7 @@ uvcg_control_header_##cname##_store(struct config_item *item, \
struct config_item *opts_item; \
struct mutex *su_mutex = &ch->item.ci_group->cg_subsys->su_mutex;\
int ret; \
- uxx num; \
+ u##bits num; \
\
mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \
\
@@ -92,7 +183,7 @@ uvcg_control_header_##cname##_store(struct config_item *item, \
goto end; \
} \
\
- ret = str2u(page, 0, &num); \
+ ret = kstrtou##bits(page, 0, &num); \
if (ret) \
goto end; \
\
@@ -100,7 +191,7 @@ uvcg_control_header_##cname##_store(struct config_item *item, \
ret = -EINVAL; \
goto end; \
} \
- ch->desc.aname = vnoc(num); \
+ ch->desc.aname = cpu_to_le##bits(num); \
ret = len; \
end: \
mutex_unlock(&opts->lock); \
@@ -110,11 +201,9 @@ end: \
\
UVC_ATTR(uvcg_control_header_, cname, aname)
-UVCG_CTRL_HDR_ATTR(bcd_uvc, bcdUVC, le16_to_cpu, kstrtou16, u16, cpu_to_le16,
- 0xffff);
+UVCG_CTRL_HDR_ATTR(bcd_uvc, bcdUVC, 16, 0xffff);
-UVCG_CTRL_HDR_ATTR(dw_clock_frequency, dwClockFrequency, le32_to_cpu, kstrtou32,
- u32, cpu_to_le32, 0x7fffffff);
+UVCG_CTRL_HDR_ATTR(dw_clock_frequency, dwClockFrequency, 32, 0x7fffffff);
#undef UVCG_CTRL_HDR_ATTR
@@ -125,6 +214,7 @@ static struct configfs_attribute *uvcg_control_header_attrs[] = {
};
static const struct config_item_type uvcg_control_header_type = {
+ .ct_item_ops = &uvcg_config_item_ops,
.ct_attrs = uvcg_control_header_attrs,
.ct_owner = THIS_MODULE,
};
@@ -149,60 +239,42 @@ static struct config_item *uvcg_control_header_make(struct config_group *group,
return &h->item;
}
-static void uvcg_control_header_drop(struct config_group *group,
- struct config_item *item)
-{
- struct uvcg_control_header *h = to_uvcg_control_header(item);
-
- kfree(h);
-}
-
-/* control/header */
-static struct uvcg_control_header_grp {
- struct config_group group;
-} uvcg_control_header_grp;
-
static struct configfs_group_operations uvcg_control_header_grp_ops = {
.make_item = uvcg_control_header_make,
- .drop_item = uvcg_control_header_drop,
};
-static const struct config_item_type uvcg_control_header_grp_type = {
- .ct_group_ops = &uvcg_control_header_grp_ops,
- .ct_owner = THIS_MODULE,
+static const struct uvcg_config_group_type uvcg_control_header_grp_type = {
+ .type = {
+ .ct_item_ops = &uvcg_config_item_ops,
+ .ct_group_ops = &uvcg_control_header_grp_ops,
+ .ct_owner = THIS_MODULE,
+ },
+ .name = "header",
};
-/* control/processing/default */
-static struct uvcg_default_processing {
- struct config_group group;
-} uvcg_default_processing;
-
-static inline struct uvcg_default_processing
-*to_uvcg_default_processing(struct config_item *item)
-{
- return container_of(to_config_group(item),
- struct uvcg_default_processing, group);
-}
+/* -----------------------------------------------------------------------------
+ * control/processing/default
+ */
-#define UVCG_DEFAULT_PROCESSING_ATTR(cname, aname, conv) \
+#define UVCG_DEFAULT_PROCESSING_ATTR(cname, aname, bits) \
static ssize_t uvcg_default_processing_##cname##_show( \
struct config_item *item, char *page) \
{ \
- struct uvcg_default_processing *dp = to_uvcg_default_processing(item); \
+ struct config_group *group = to_config_group(item); \
struct f_uvc_opts *opts; \
struct config_item *opts_item; \
- struct mutex *su_mutex = &dp->group.cg_subsys->su_mutex; \
+ struct mutex *su_mutex = &group->cg_subsys->su_mutex; \
struct uvc_processing_unit_descriptor *pd; \
int result; \
\
mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \
\
- opts_item = dp->group.cg_item.ci_parent->ci_parent->ci_parent; \
+ opts_item = group->cg_item.ci_parent->ci_parent->ci_parent; \
opts = to_f_uvc_opts(opts_item); \
pd = &opts->uvc_processing; \
\
mutex_lock(&opts->lock); \
- result = sprintf(page, "%d\n", conv(pd->aname)); \
+ result = sprintf(page, "%u\n", le##bits##_to_cpu(pd->aname)); \
mutex_unlock(&opts->lock); \
\
mutex_unlock(su_mutex); \
@@ -211,37 +283,33 @@ static ssize_t uvcg_default_processing_##cname##_show( \
\
UVC_ATTR_RO(uvcg_default_processing_, cname, aname)
-#define identity_conv(x) (x)
-
-UVCG_DEFAULT_PROCESSING_ATTR(b_unit_id, bUnitID, identity_conv);
-UVCG_DEFAULT_PROCESSING_ATTR(b_source_id, bSourceID, identity_conv);
-UVCG_DEFAULT_PROCESSING_ATTR(w_max_multiplier, wMaxMultiplier, le16_to_cpu);
-UVCG_DEFAULT_PROCESSING_ATTR(i_processing, iProcessing, identity_conv);
-
-#undef identity_conv
+UVCG_DEFAULT_PROCESSING_ATTR(b_unit_id, bUnitID, 8);
+UVCG_DEFAULT_PROCESSING_ATTR(b_source_id, bSourceID, 8);
+UVCG_DEFAULT_PROCESSING_ATTR(w_max_multiplier, wMaxMultiplier, 16);
+UVCG_DEFAULT_PROCESSING_ATTR(i_processing, iProcessing, 8);
#undef UVCG_DEFAULT_PROCESSING_ATTR
static ssize_t uvcg_default_processing_bm_controls_show(
struct config_item *item, char *page)
{
- struct uvcg_default_processing *dp = to_uvcg_default_processing(item);
+ struct config_group *group = to_config_group(item);
struct f_uvc_opts *opts;
struct config_item *opts_item;
- struct mutex *su_mutex = &dp->group.cg_subsys->su_mutex;
+ struct mutex *su_mutex = &group->cg_subsys->su_mutex;
struct uvc_processing_unit_descriptor *pd;
int result, i;
char *pg = page;
mutex_lock(su_mutex); /* for navigating configfs hierarchy */
- opts_item = dp->group.cg_item.ci_parent->ci_parent->ci_parent;
+ opts_item = group->cg_item.ci_parent->ci_parent->ci_parent;
opts = to_f_uvc_opts(opts_item);
pd = &opts->uvc_processing;
mutex_lock(&opts->lock);
for (result = 0, i = 0; i < pd->bControlSize; ++i) {
- result += sprintf(pg, "%d\n", pd->bmControls[i]);
+ result += sprintf(pg, "%u\n", pd->bmControls[i]);
pg = page + result;
}
mutex_unlock(&opts->lock);
@@ -262,54 +330,55 @@ static struct configfs_attribute *uvcg_default_processing_attrs[] = {
NULL,
};
-static const struct config_item_type uvcg_default_processing_type = {
- .ct_attrs = uvcg_default_processing_attrs,
- .ct_owner = THIS_MODULE,
+static const struct uvcg_config_group_type uvcg_default_processing_type = {
+ .type = {
+ .ct_item_ops = &uvcg_config_item_ops,
+ .ct_attrs = uvcg_default_processing_attrs,
+ .ct_owner = THIS_MODULE,
+ },
+ .name = "default",
};
-/* struct uvcg_processing {}; */
-
-/* control/processing */
-static struct uvcg_processing_grp {
- struct config_group group;
-} uvcg_processing_grp;
+/* -----------------------------------------------------------------------------
+ * control/processing
+ */
-static const struct config_item_type uvcg_processing_grp_type = {
- .ct_owner = THIS_MODULE,
+static const struct uvcg_config_group_type uvcg_processing_grp_type = {
+ .type = {
+ .ct_item_ops = &uvcg_config_item_ops,
+ .ct_owner = THIS_MODULE,
+ },
+ .name = "processing",
+ .children = (const struct uvcg_config_group_type*[]) {
+ &uvcg_default_processing_type,
+ NULL,
+ },
};
-/* control/terminal/camera/default */
-static struct uvcg_default_camera {
- struct config_group group;
-} uvcg_default_camera;
-
-static inline struct uvcg_default_camera
-*to_uvcg_default_camera(struct config_item *item)
-{
- return container_of(to_config_group(item),
- struct uvcg_default_camera, group);
-}
+/* -----------------------------------------------------------------------------
+ * control/terminal/camera/default
+ */
-#define UVCG_DEFAULT_CAMERA_ATTR(cname, aname, conv) \
+#define UVCG_DEFAULT_CAMERA_ATTR(cname, aname, bits) \
static ssize_t uvcg_default_camera_##cname##_show( \
struct config_item *item, char *page) \
{ \
- struct uvcg_default_camera *dc = to_uvcg_default_camera(item); \
+ struct config_group *group = to_config_group(item); \
struct f_uvc_opts *opts; \
struct config_item *opts_item; \
- struct mutex *su_mutex = &dc->group.cg_subsys->su_mutex; \
+ struct mutex *su_mutex = &group->cg_subsys->su_mutex; \
struct uvc_camera_terminal_descriptor *cd; \
int result; \
\
mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \
\
- opts_item = dc->group.cg_item.ci_parent->ci_parent->ci_parent-> \
+ opts_item = group->cg_item.ci_parent->ci_parent->ci_parent-> \
ci_parent; \
opts = to_f_uvc_opts(opts_item); \
cd = &opts->uvc_camera_terminal; \
\
mutex_lock(&opts->lock); \
- result = sprintf(page, "%d\n", conv(cd->aname)); \
+ result = sprintf(page, "%u\n", le##bits##_to_cpu(cd->aname)); \
mutex_unlock(&opts->lock); \
\
mutex_unlock(su_mutex); \
@@ -319,44 +388,40 @@ static ssize_t uvcg_default_camera_##cname##_show( \
\
UVC_ATTR_RO(uvcg_default_camera_, cname, aname)
-#define identity_conv(x) (x)
-
-UVCG_DEFAULT_CAMERA_ATTR(b_terminal_id, bTerminalID, identity_conv);
-UVCG_DEFAULT_CAMERA_ATTR(w_terminal_type, wTerminalType, le16_to_cpu);
-UVCG_DEFAULT_CAMERA_ATTR(b_assoc_terminal, bAssocTerminal, identity_conv);
-UVCG_DEFAULT_CAMERA_ATTR(i_terminal, iTerminal, identity_conv);
+UVCG_DEFAULT_CAMERA_ATTR(b_terminal_id, bTerminalID, 8);
+UVCG_DEFAULT_CAMERA_ATTR(w_terminal_type, wTerminalType, 16);
+UVCG_DEFAULT_CAMERA_ATTR(b_assoc_terminal, bAssocTerminal, 8);
+UVCG_DEFAULT_CAMERA_ATTR(i_terminal, iTerminal, 8);
UVCG_DEFAULT_CAMERA_ATTR(w_objective_focal_length_min, wObjectiveFocalLengthMin,
- le16_to_cpu);
+ 16);
UVCG_DEFAULT_CAMERA_ATTR(w_objective_focal_length_max, wObjectiveFocalLengthMax,
- le16_to_cpu);
+ 16);
UVCG_DEFAULT_CAMERA_ATTR(w_ocular_focal_length, wOcularFocalLength,
- le16_to_cpu);
-
-#undef identity_conv
+ 16);
#undef UVCG_DEFAULT_CAMERA_ATTR
static ssize_t uvcg_default_camera_bm_controls_show(
struct config_item *item, char *page)
{
- struct uvcg_default_camera *dc = to_uvcg_default_camera(item);
+ struct config_group *group = to_config_group(item);
struct f_uvc_opts *opts;
struct config_item *opts_item;
- struct mutex *su_mutex = &dc->group.cg_subsys->su_mutex;
+ struct mutex *su_mutex = &group->cg_subsys->su_mutex;
struct uvc_camera_terminal_descriptor *cd;
int result, i;
char *pg = page;
mutex_lock(su_mutex); /* for navigating configfs hierarchy */
- opts_item = dc->group.cg_item.ci_parent->ci_parent->ci_parent->
+ opts_item = group->cg_item.ci_parent->ci_parent->ci_parent->
ci_parent;
opts = to_f_uvc_opts(opts_item);
cd = &opts->uvc_camera_terminal;
mutex_lock(&opts->lock);
for (result = 0, i = 0; i < cd->bControlSize; ++i) {
- result += sprintf(pg, "%d\n", cd->bmControls[i]);
+ result += sprintf(pg, "%u\n", cd->bmControls[i]);
pg = page + result;
}
mutex_unlock(&opts->lock);
@@ -379,54 +444,55 @@ static struct configfs_attribute *uvcg_default_camera_attrs[] = {
NULL,
};
-static const struct config_item_type uvcg_default_camera_type = {
- .ct_attrs = uvcg_default_camera_attrs,
- .ct_owner = THIS_MODULE,
+static const struct uvcg_config_group_type uvcg_default_camera_type = {
+ .type = {
+ .ct_item_ops = &uvcg_config_item_ops,
+ .ct_attrs = uvcg_default_camera_attrs,
+ .ct_owner = THIS_MODULE,
+ },
+ .name = "default",
};
-/* struct uvcg_camera {}; */
-
-/* control/terminal/camera */
-static struct uvcg_camera_grp {
- struct config_group group;
-} uvcg_camera_grp;
+/* -----------------------------------------------------------------------------
+ * control/terminal/camera
+ */
-static const struct config_item_type uvcg_camera_grp_type = {
- .ct_owner = THIS_MODULE,
+static const struct uvcg_config_group_type uvcg_camera_grp_type = {
+ .type = {
+ .ct_item_ops = &uvcg_config_item_ops,
+ .ct_owner = THIS_MODULE,
+ },
+ .name = "camera",
+ .children = (const struct uvcg_config_group_type*[]) {
+ &uvcg_default_camera_type,
+ NULL,
+ },
};
-/* control/terminal/output/default */
-static struct uvcg_default_output {
- struct config_group group;
-} uvcg_default_output;
-
-static inline struct uvcg_default_output
-*to_uvcg_default_output(struct config_item *item)
-{
- return container_of(to_config_group(item),
- struct uvcg_default_output, group);
-}
+/* -----------------------------------------------------------------------------
+ * control/terminal/output/default
+ */
-#define UVCG_DEFAULT_OUTPUT_ATTR(cname, aname, conv) \
+#define UVCG_DEFAULT_OUTPUT_ATTR(cname, aname, bits) \
static ssize_t uvcg_default_output_##cname##_show( \
- struct config_item *item, char *page) \
+ struct config_item *item, char *page) \
{ \
- struct uvcg_default_output *dout = to_uvcg_default_output(item); \
+ struct config_group *group = to_config_group(item); \
struct f_uvc_opts *opts; \
struct config_item *opts_item; \
- struct mutex *su_mutex = &dout->group.cg_subsys->su_mutex; \
+ struct mutex *su_mutex = &group->cg_subsys->su_mutex; \
struct uvc_output_terminal_descriptor *cd; \
int result; \
\
mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \
\
- opts_item = dout->group.cg_item.ci_parent->ci_parent-> \
+ opts_item = group->cg_item.ci_parent->ci_parent-> \
ci_parent->ci_parent; \
opts = to_f_uvc_opts(opts_item); \
cd = &opts->uvc_output_terminal; \
\
mutex_lock(&opts->lock); \
- result = sprintf(page, "%d\n", conv(cd->aname)); \
+ result = sprintf(page, "%u\n", le##bits##_to_cpu(cd->aname)); \
mutex_unlock(&opts->lock); \
\
mutex_unlock(su_mutex); \
@@ -436,15 +502,11 @@ static ssize_t uvcg_default_output_##cname##_show( \
\
UVC_ATTR_RO(uvcg_default_output_, cname, aname)
-#define identity_conv(x) (x)
-
-UVCG_DEFAULT_OUTPUT_ATTR(b_terminal_id, bTerminalID, identity_conv);
-UVCG_DEFAULT_OUTPUT_ATTR(w_terminal_type, wTerminalType, le16_to_cpu);
-UVCG_DEFAULT_OUTPUT_ATTR(b_assoc_terminal, bAssocTerminal, identity_conv);
-UVCG_DEFAULT_OUTPUT_ATTR(b_source_id, bSourceID, identity_conv);
-UVCG_DEFAULT_OUTPUT_ATTR(i_terminal, iTerminal, identity_conv);
-
-#undef identity_conv
+UVCG_DEFAULT_OUTPUT_ATTR(b_terminal_id, bTerminalID, 8);
+UVCG_DEFAULT_OUTPUT_ATTR(w_terminal_type, wTerminalType, 16);
+UVCG_DEFAULT_OUTPUT_ATTR(b_assoc_terminal, bAssocTerminal, 8);
+UVCG_DEFAULT_OUTPUT_ATTR(b_source_id, bSourceID, 8);
+UVCG_DEFAULT_OUTPUT_ATTR(i_terminal, iTerminal, 8);
#undef UVCG_DEFAULT_OUTPUT_ATTR
@@ -457,47 +519,68 @@ static struct configfs_attribute *uvcg_default_output_attrs[] = {
NULL,
};
-static const struct config_item_type uvcg_default_output_type = {
- .ct_attrs = uvcg_default_output_attrs,
- .ct_owner = THIS_MODULE,
+static const struct uvcg_config_group_type uvcg_default_output_type = {
+ .type = {
+ .ct_item_ops = &uvcg_config_item_ops,
+ .ct_attrs = uvcg_default_output_attrs,
+ .ct_owner = THIS_MODULE,
+ },
+ .name = "default",
};
-/* struct uvcg_output {}; */
-
-/* control/terminal/output */
-static struct uvcg_output_grp {
- struct config_group group;
-} uvcg_output_grp;
+/* -----------------------------------------------------------------------------
+ * control/terminal/output
+ */
-static const struct config_item_type uvcg_output_grp_type = {
- .ct_owner = THIS_MODULE,
+static const struct uvcg_config_group_type uvcg_output_grp_type = {
+ .type = {
+ .ct_item_ops = &uvcg_config_item_ops,
+ .ct_owner = THIS_MODULE,
+ },
+ .name = "output",
+ .children = (const struct uvcg_config_group_type*[]) {
+ &uvcg_default_output_type,
+ NULL,
+ },
};
-/* control/terminal */
-static struct uvcg_terminal_grp {
- struct config_group group;
-} uvcg_terminal_grp;
+/* -----------------------------------------------------------------------------
+ * control/terminal
+ */
-static const struct config_item_type uvcg_terminal_grp_type = {
- .ct_owner = THIS_MODULE,
+static const struct uvcg_config_group_type uvcg_terminal_grp_type = {
+ .type = {
+ .ct_item_ops = &uvcg_config_item_ops,
+ .ct_owner = THIS_MODULE,
+ },
+ .name = "terminal",
+ .children = (const struct uvcg_config_group_type*[]) {
+ &uvcg_camera_grp_type,
+ &uvcg_output_grp_type,
+ NULL,
+ },
};
-/* control/class/{fs} */
-static struct uvcg_control_class {
- struct config_group group;
-} uvcg_control_class_fs, uvcg_control_class_ss;
+/* -----------------------------------------------------------------------------
+ * control/class/{fs|ss}
+ */
+struct uvcg_control_class_group {
+ struct config_group group;
+ const char *name;
+};
static inline struct uvc_descriptor_header
**uvcg_get_ctl_class_arr(struct config_item *i, struct f_uvc_opts *o)
{
- struct uvcg_control_class *cl = container_of(to_config_group(i),
- struct uvcg_control_class, group);
+ struct uvcg_control_class_group *group =
+ container_of(i, struct uvcg_control_class_group,
+ group.cg_item);
- if (cl == &uvcg_control_class_fs)
+ if (!strcmp(group->name, "fs"))
return o->uvc_fs_control_cls;
- if (cl == &uvcg_control_class_ss)
+ if (!strcmp(group->name, "ss"))
return o->uvc_ss_control_cls;
return NULL;
@@ -540,6 +623,7 @@ static int uvcg_control_class_allow_link(struct config_item *src,
unlock:
mutex_unlock(&opts->lock);
out:
+ config_item_put(header);
mutex_unlock(su_mutex);
return ret;
}
@@ -575,10 +659,12 @@ static void uvcg_control_class_drop_link(struct config_item *src,
unlock:
mutex_unlock(&opts->lock);
out:
+ config_item_put(header);
mutex_unlock(su_mutex);
}
static 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,
};
@@ -588,37 +674,99 @@ static const struct config_item_type uvcg_control_class_type = {
.ct_owner = THIS_MODULE,
};
-/* control/class */
-static struct uvcg_control_class_grp {
- struct config_group group;
-} uvcg_control_class_grp;
+/* -----------------------------------------------------------------------------
+ * control/class
+ */
+
+static int uvcg_control_class_create_children(struct config_group *parent)
+{
+ static const char * const names[] = { "fs", "ss" };
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(names); ++i) {
+ struct uvcg_control_class_group *group;
-static const struct config_item_type uvcg_control_class_grp_type = {
- .ct_owner = THIS_MODULE,
+ group = kzalloc(sizeof(*group), GFP_KERNEL);
+ if (!group)
+ return -ENOMEM;
+
+ group->name = names[i];
+
+ config_group_init_type_name(&group->group, group->name,
+ &uvcg_control_class_type);
+ configfs_add_default_group(&group->group, parent);
+ }
+
+ return 0;
+}
+
+static const struct uvcg_config_group_type uvcg_control_class_grp_type = {
+ .type = {
+ .ct_item_ops = &uvcg_config_item_ops,
+ .ct_owner = THIS_MODULE,
+ },
+ .name = "class",
+ .create_children = uvcg_control_class_create_children,
};
-/* control */
-static struct uvcg_control_grp {
- struct config_group group;
-} uvcg_control_grp;
+/* -----------------------------------------------------------------------------
+ * control
+ */
-static const struct config_item_type uvcg_control_grp_type = {
- .ct_owner = THIS_MODULE,
+static ssize_t uvcg_default_control_b_interface_number_show(
+ struct config_item *item, char *page)
+{
+ struct config_group *group = to_config_group(item);
+ struct mutex *su_mutex = &group->cg_subsys->su_mutex;
+ struct config_item *opts_item;
+ struct f_uvc_opts *opts;
+ int result = 0;
+
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+ opts_item = item->ci_parent;
+ opts = to_f_uvc_opts(opts_item);
+
+ mutex_lock(&opts->lock);
+ result += sprintf(page, "%u\n", opts->control_interface);
+ mutex_unlock(&opts->lock);
+
+ mutex_unlock(su_mutex);
+
+ return result;
+}
+
+UVC_ATTR_RO(uvcg_default_control_, b_interface_number, bInterfaceNumber);
+
+static struct configfs_attribute *uvcg_default_control_attrs[] = {
+ &uvcg_default_control_attr_b_interface_number,
+ NULL,
};
-/* streaming/uncompressed */
-static struct uvcg_uncompressed_grp {
- struct config_group group;
-} uvcg_uncompressed_grp;
+static const struct uvcg_config_group_type uvcg_control_grp_type = {
+ .type = {
+ .ct_item_ops = &uvcg_config_item_ops,
+ .ct_attrs = uvcg_default_control_attrs,
+ .ct_owner = THIS_MODULE,
+ },
+ .name = "control",
+ .children = (const struct uvcg_config_group_type*[]) {
+ &uvcg_control_header_grp_type,
+ &uvcg_processing_grp_type,
+ &uvcg_terminal_grp_type,
+ &uvcg_control_class_grp_type,
+ NULL,
+ },
+};
-/* streaming/mjpeg */
-static struct uvcg_mjpeg_grp {
- struct config_group group;
-} uvcg_mjpeg_grp;
+/* -----------------------------------------------------------------------------
+ * streaming/uncompressed
+ * streaming/mjpeg
+ */
-static struct config_item *fmt_parent[] = {
- &uvcg_uncompressed_grp.group.cg_item,
- &uvcg_mjpeg_grp.group.cg_item,
+static const char * const uvcg_format_names[] = {
+ "uncompressed",
+ "mjpeg",
};
enum uvcg_format_type {
@@ -702,7 +850,11 @@ struct uvcg_format_ptr {
struct list_head entry;
};
-/* streaming/header/<NAME> */
+/* -----------------------------------------------------------------------------
+ * streaming/header/<NAME>
+ * streaming/header
+ */
+
struct uvcg_streaming_header {
struct config_item item;
struct uvc_input_header_descriptor desc;
@@ -716,6 +868,8 @@ static struct uvcg_streaming_header *to_uvcg_streaming_header(struct config_item
return container_of(item, struct uvcg_streaming_header, item);
}
+static void uvcg_format_set_indices(struct config_group *fmt);
+
static int uvcg_streaming_header_allow_link(struct config_item *src,
struct config_item *target)
{
@@ -740,10 +894,22 @@ static int uvcg_streaming_header_allow_link(struct config_item *src,
goto out;
}
- for (i = 0; i < ARRAY_SIZE(fmt_parent); ++i)
- if (target->ci_parent == fmt_parent[i])
+ /*
+ * Linking is only allowed to direct children of the format nodes
+ * (streaming/uncompressed or streaming/mjpeg nodes). First check that
+ * the grand-parent of the target matches the grand-parent of the source
+ * (the streaming node), and then verify that the target parent is a
+ * format node.
+ */
+ if (src->ci_parent->ci_parent != target->ci_parent->ci_parent)
+ goto out;
+
+ for (i = 0; i < ARRAY_SIZE(uvcg_format_names); ++i) {
+ if (!strcmp(target->ci_parent->ci_name, uvcg_format_names[i]))
break;
- if (i == ARRAY_SIZE(fmt_parent))
+ }
+
+ if (i == ARRAY_SIZE(uvcg_format_names))
goto out;
target_fmt = container_of(to_config_group(target), struct uvcg_format,
@@ -751,6 +917,8 @@ static int uvcg_streaming_header_allow_link(struct config_item *src,
if (!target_fmt)
goto out;
+ uvcg_format_set_indices(to_config_group(target));
+
format_ptr = kzalloc(sizeof(*format_ptr), GFP_KERNEL);
if (!format_ptr) {
ret = -ENOMEM;
@@ -760,6 +928,7 @@ static int uvcg_streaming_header_allow_link(struct config_item *src,
format_ptr->fmt = target_fmt;
list_add_tail(&format_ptr->entry, &src_hdr->formats);
++src_hdr->num_fmt;
+ ++target_fmt->linked;
out:
mutex_unlock(&opts->lock);
@@ -797,19 +966,22 @@ static void uvcg_streaming_header_drop_link(struct config_item *src,
break;
}
+ --target_fmt->linked;
+
out:
mutex_unlock(&opts->lock);
mutex_unlock(su_mutex);
}
static struct configfs_item_operations uvcg_streaming_header_item_ops = {
- .allow_link = uvcg_streaming_header_allow_link,
- .drop_link = uvcg_streaming_header_drop_link,
+ .release = uvcg_config_item_release,
+ .allow_link = uvcg_streaming_header_allow_link,
+ .drop_link = uvcg_streaming_header_drop_link,
};
-#define UVCG_STREAMING_HEADER_ATTR(cname, aname, conv) \
+#define UVCG_STREAMING_HEADER_ATTR(cname, aname, bits) \
static ssize_t uvcg_streaming_header_##cname##_show( \
- struct config_item *item, char *page) \
+ struct config_item *item, char *page) \
{ \
struct uvcg_streaming_header *sh = to_uvcg_streaming_header(item); \
struct f_uvc_opts *opts; \
@@ -823,7 +995,7 @@ static ssize_t uvcg_streaming_header_##cname##_show( \
opts = to_f_uvc_opts(opts_item); \
\
mutex_lock(&opts->lock); \
- result = sprintf(page, "%d\n", conv(sh->desc.aname)); \
+ result = sprintf(page, "%u\n", le##bits##_to_cpu(sh->desc.aname));\
mutex_unlock(&opts->lock); \
\
mutex_unlock(su_mutex); \
@@ -832,16 +1004,11 @@ static ssize_t uvcg_streaming_header_##cname##_show( \
\
UVC_ATTR_RO(uvcg_streaming_header_, cname, aname)
-#define identity_conv(x) (x)
-
-UVCG_STREAMING_HEADER_ATTR(bm_info, bmInfo, identity_conv);
-UVCG_STREAMING_HEADER_ATTR(b_terminal_link, bTerminalLink, identity_conv);
-UVCG_STREAMING_HEADER_ATTR(b_still_capture_method, bStillCaptureMethod,
- identity_conv);
-UVCG_STREAMING_HEADER_ATTR(b_trigger_support, bTriggerSupport, identity_conv);
-UVCG_STREAMING_HEADER_ATTR(b_trigger_usage, bTriggerUsage, identity_conv);
-
-#undef identity_conv
+UVCG_STREAMING_HEADER_ATTR(bm_info, bmInfo, 8);
+UVCG_STREAMING_HEADER_ATTR(b_terminal_link, bTerminalLink, 8);
+UVCG_STREAMING_HEADER_ATTR(b_still_capture_method, bStillCaptureMethod, 8);
+UVCG_STREAMING_HEADER_ATTR(b_trigger_support, bTriggerSupport, 8);
+UVCG_STREAMING_HEADER_ATTR(b_trigger_usage, bTriggerUsage, 8);
#undef UVCG_STREAMING_HEADER_ATTR
@@ -880,31 +1047,26 @@ static struct config_item
return &h->item;
}
-static void uvcg_streaming_header_drop(struct config_group *group,
- struct config_item *item)
-{
- struct uvcg_streaming_header *h = to_uvcg_streaming_header(item);
-
- kfree(h);
-}
-
-/* streaming/header */
-static struct uvcg_streaming_header_grp {
- struct config_group group;
-} uvcg_streaming_header_grp;
-
static struct configfs_group_operations uvcg_streaming_header_grp_ops = {
.make_item = uvcg_streaming_header_make,
- .drop_item = uvcg_streaming_header_drop,
};
-static const struct config_item_type uvcg_streaming_header_grp_type = {
- .ct_group_ops = &uvcg_streaming_header_grp_ops,
- .ct_owner = THIS_MODULE,
+static const struct uvcg_config_group_type uvcg_streaming_header_grp_type = {
+ .type = {
+ .ct_item_ops = &uvcg_config_item_ops,
+ .ct_group_ops = &uvcg_streaming_header_grp_ops,
+ .ct_owner = THIS_MODULE,
+ },
+ .name = "header",
};
-/* streaming/<mode>/<format>/<NAME> */
+/* -----------------------------------------------------------------------------
+ * streaming/<mode>/<format>/<NAME>
+ */
+
struct uvcg_frame {
+ struct config_item item;
+ enum uvcg_format_type fmt_type;
struct {
u8 b_length;
u8 b_descriptor_type;
@@ -920,8 +1082,6 @@ struct uvcg_frame {
u8 b_frame_interval_type;
} __attribute__((packed)) frame;
u32 *dw_frame_interval;
- enum uvcg_format_type fmt_type;
- struct config_item item;
};
static struct uvcg_frame *to_uvcg_frame(struct config_item *item)
@@ -929,7 +1089,7 @@ static struct uvcg_frame *to_uvcg_frame(struct config_item *item)
return container_of(item, struct uvcg_frame, item);
}
-#define UVCG_FRAME_ATTR(cname, aname, to_cpu_endian, to_little_endian, bits) \
+#define UVCG_FRAME_ATTR(cname, aname, bits) \
static ssize_t uvcg_frame_##cname##_show(struct config_item *item, char *page)\
{ \
struct uvcg_frame *f = to_uvcg_frame(item); \
@@ -944,7 +1104,7 @@ static ssize_t uvcg_frame_##cname##_show(struct config_item *item, char *page)\
opts = to_f_uvc_opts(opts_item); \
\
mutex_lock(&opts->lock); \
- result = sprintf(page, "%d\n", to_cpu_endian(f->frame.cname)); \
+ result = sprintf(page, "%u\n", f->frame.cname); \
mutex_unlock(&opts->lock); \
\
mutex_unlock(su_mutex); \
@@ -959,8 +1119,8 @@ static ssize_t uvcg_frame_##cname##_store(struct config_item *item, \
struct config_item *opts_item; \
struct uvcg_format *fmt; \
struct mutex *su_mutex = &f->item.ci_group->cg_subsys->su_mutex;\
+ typeof(f->frame.cname) num; \
int ret; \
- u##bits num; \
\
ret = kstrtou##bits(page, 0, &num); \
if (ret) \
@@ -978,7 +1138,7 @@ static ssize_t uvcg_frame_##cname##_store(struct config_item *item, \
goto end; \
} \
\
- f->frame.cname = to_little_endian(num); \
+ f->frame.cname = num; \
ret = len; \
end: \
mutex_unlock(&opts->lock); \
@@ -988,20 +1148,48 @@ end: \
\
UVC_ATTR(uvcg_frame_, cname, aname);
-#define noop_conversion(x) (x)
+static ssize_t uvcg_frame_b_frame_index_show(struct config_item *item,
+ char *page)
+{
+ struct uvcg_frame *f = to_uvcg_frame(item);
+ struct uvcg_format *fmt;
+ struct f_uvc_opts *opts;
+ struct config_item *opts_item;
+ struct config_item *fmt_item;
+ struct mutex *su_mutex = &f->item.ci_group->cg_subsys->su_mutex;
+ int result;
+
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */
-UVCG_FRAME_ATTR(bm_capabilities, bmCapabilities, noop_conversion,
- noop_conversion, 8);
-UVCG_FRAME_ATTR(w_width, wWidth, le16_to_cpu, cpu_to_le16, 16);
-UVCG_FRAME_ATTR(w_height, wHeight, le16_to_cpu, cpu_to_le16, 16);
-UVCG_FRAME_ATTR(dw_min_bit_rate, dwMinBitRate, le32_to_cpu, cpu_to_le32, 32);
-UVCG_FRAME_ATTR(dw_max_bit_rate, dwMaxBitRate, le32_to_cpu, cpu_to_le32, 32);
-UVCG_FRAME_ATTR(dw_max_video_frame_buffer_size, dwMaxVideoFrameBufferSize,
- le32_to_cpu, cpu_to_le32, 32);
-UVCG_FRAME_ATTR(dw_default_frame_interval, dwDefaultFrameInterval,
- le32_to_cpu, cpu_to_le32, 32);
+ fmt_item = f->item.ci_parent;
+ fmt = to_uvcg_format(fmt_item);
-#undef noop_conversion
+ if (!fmt->linked) {
+ result = -EBUSY;
+ goto out;
+ }
+
+ opts_item = fmt_item->ci_parent->ci_parent->ci_parent;
+ opts = to_f_uvc_opts(opts_item);
+
+ mutex_lock(&opts->lock);
+ result = sprintf(page, "%u\n", f->frame.b_frame_index);
+ mutex_unlock(&opts->lock);
+
+out:
+ mutex_unlock(su_mutex);
+ return result;
+}
+
+UVC_ATTR_RO(uvcg_frame_, b_frame_index, bFrameIndex);
+
+UVCG_FRAME_ATTR(bm_capabilities, bmCapabilities, 8);
+UVCG_FRAME_ATTR(w_width, wWidth, 16);
+UVCG_FRAME_ATTR(w_height, wHeight, 16);
+UVCG_FRAME_ATTR(dw_min_bit_rate, dwMinBitRate, 32);
+UVCG_FRAME_ATTR(dw_max_bit_rate, dwMaxBitRate, 32);
+UVCG_FRAME_ATTR(dw_max_video_frame_buffer_size, dwMaxVideoFrameBufferSize, 32);
+UVCG_FRAME_ATTR(dw_default_frame_interval, dwDefaultFrameInterval, 32);
#undef UVCG_FRAME_ATTR
@@ -1022,8 +1210,7 @@ static ssize_t uvcg_frame_dw_frame_interval_show(struct config_item *item,
mutex_lock(&opts->lock);
for (result = 0, i = 0; i < frm->frame.b_frame_interval_type; ++i) {
- result += sprintf(pg, "%d\n",
- le32_to_cpu(frm->dw_frame_interval[i]));
+ result += sprintf(pg, "%u\n", frm->dw_frame_interval[i]);
pg = page + result;
}
mutex_unlock(&opts->lock);
@@ -1048,7 +1235,7 @@ static inline int __uvcg_fill_frm_intrv(char *buf, void *priv)
return ret;
interv = priv;
- **interv = cpu_to_le32(num);
+ **interv = num;
++*interv;
return 0;
@@ -1125,6 +1312,8 @@ static ssize_t uvcg_frame_dw_frame_interval_store(struct config_item *item,
kfree(ch->dw_frame_interval);
ch->dw_frame_interval = frm_intrv;
ch->frame.b_frame_interval_type = n;
+ sort(ch->dw_frame_interval, n, sizeof(*ch->dw_frame_interval),
+ uvcg_config_compare_u32, NULL);
ret = len;
end:
@@ -1136,6 +1325,7 @@ end:
UVC_ATTR(uvcg_frame_, dw_frame_interval, dwFrameInterval);
static struct configfs_attribute *uvcg_frame_attrs[] = {
+ &uvcg_frame_attr_b_frame_index,
&uvcg_frame_attr_bm_capabilities,
&uvcg_frame_attr_w_width,
&uvcg_frame_attr_w_height,
@@ -1148,6 +1338,7 @@ static struct configfs_attribute *uvcg_frame_attrs[] = {
};
static const struct config_item_type uvcg_frame_type = {
+ .ct_item_ops = &uvcg_config_item_ops,
.ct_attrs = uvcg_frame_attrs,
.ct_owner = THIS_MODULE,
};
@@ -1166,12 +1357,12 @@ static struct config_item *uvcg_frame_make(struct config_group *group,
h->frame.b_descriptor_type = USB_DT_CS_INTERFACE;
h->frame.b_frame_index = 1;
- h->frame.w_width = cpu_to_le16(640);
- h->frame.w_height = cpu_to_le16(360);
- h->frame.dw_min_bit_rate = cpu_to_le32(18432000);
- h->frame.dw_max_bit_rate = cpu_to_le32(55296000);
- h->frame.dw_max_video_frame_buffer_size = cpu_to_le32(460800);
- h->frame.dw_default_frame_interval = cpu_to_le32(666666);
+ h->frame.w_width = 640;
+ h->frame.w_height = 360;
+ h->frame.dw_min_bit_rate = 18432000;
+ h->frame.dw_max_bit_rate = 55296000;
+ h->frame.dw_max_video_frame_buffer_size = 460800;
+ h->frame.dw_default_frame_interval = 666666;
opts_item = group->cg_item.ci_parent->ci_parent->ci_parent;
opts = to_f_uvc_opts(opts_item);
@@ -1199,7 +1390,6 @@ static struct config_item *uvcg_frame_make(struct config_group *group,
static void uvcg_frame_drop(struct config_group *group, struct config_item *item)
{
- struct uvcg_frame *h = to_uvcg_frame(item);
struct uvcg_format *fmt;
struct f_uvc_opts *opts;
struct config_item *opts_item;
@@ -1210,11 +1400,31 @@ static void uvcg_frame_drop(struct config_group *group, struct config_item *item
mutex_lock(&opts->lock);
fmt = to_uvcg_format(&group->cg_item);
--fmt->num_frames;
- kfree(h);
mutex_unlock(&opts->lock);
+
+ config_item_put(item);
}
-/* streaming/uncompressed/<NAME> */
+static void uvcg_format_set_indices(struct config_group *fmt)
+{
+ struct config_item *ci;
+ unsigned int i = 1;
+
+ list_for_each_entry(ci, &fmt->cg_children, ci_entry) {
+ struct uvcg_frame *frm;
+
+ if (ci->ci_type != &uvcg_frame_type)
+ continue;
+
+ frm = to_uvcg_frame(ci);
+ frm->frame.b_frame_index = i++;
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * streaming/uncompressed/<NAME>
+ */
+
struct uvcg_uncompressed {
struct uvcg_format fmt;
struct uvc_format_uncompressed desc;
@@ -1286,7 +1496,7 @@ end:
UVC_ATTR(uvcg_uncompressed_, guid_format, guidFormat);
-#define UVCG_UNCOMPRESSED_ATTR_RO(cname, aname, conv) \
+#define UVCG_UNCOMPRESSED_ATTR_RO(cname, aname, bits) \
static ssize_t uvcg_uncompressed_##cname##_show( \
struct config_item *item, char *page) \
{ \
@@ -1302,7 +1512,7 @@ static ssize_t uvcg_uncompressed_##cname##_show( \
opts = to_f_uvc_opts(opts_item); \
\
mutex_lock(&opts->lock); \
- result = sprintf(page, "%d\n", conv(u->desc.aname)); \
+ result = sprintf(page, "%u\n", le##bits##_to_cpu(u->desc.aname));\
mutex_unlock(&opts->lock); \
\
mutex_unlock(su_mutex); \
@@ -1311,7 +1521,7 @@ static ssize_t uvcg_uncompressed_##cname##_show( \
\
UVC_ATTR_RO(uvcg_uncompressed_, cname, aname);
-#define UVCG_UNCOMPRESSED_ATTR(cname, aname, conv) \
+#define UVCG_UNCOMPRESSED_ATTR(cname, aname, bits) \
static ssize_t uvcg_uncompressed_##cname##_show( \
struct config_item *item, char *page) \
{ \
@@ -1327,7 +1537,7 @@ static ssize_t uvcg_uncompressed_##cname##_show( \
opts = to_f_uvc_opts(opts_item); \
\
mutex_lock(&opts->lock); \
- result = sprintf(page, "%d\n", conv(u->desc.aname)); \
+ result = sprintf(page, "%u\n", le##bits##_to_cpu(u->desc.aname));\
mutex_unlock(&opts->lock); \
\
mutex_unlock(su_mutex); \
@@ -1374,16 +1584,12 @@ end: \
\
UVC_ATTR(uvcg_uncompressed_, cname, aname);
-#define identity_conv(x) (x)
-
-UVCG_UNCOMPRESSED_ATTR(b_bits_per_pixel, bBitsPerPixel, identity_conv);
-UVCG_UNCOMPRESSED_ATTR(b_default_frame_index, bDefaultFrameIndex,
- identity_conv);
-UVCG_UNCOMPRESSED_ATTR_RO(b_aspect_ratio_x, bAspectRatioX, identity_conv);
-UVCG_UNCOMPRESSED_ATTR_RO(b_aspect_ratio_y, bAspectRatioY, identity_conv);
-UVCG_UNCOMPRESSED_ATTR_RO(bm_interface_flags, bmInterfaceFlags, identity_conv);
-
-#undef identity_conv
+UVCG_UNCOMPRESSED_ATTR_RO(b_format_index, bFormatIndex, 8);
+UVCG_UNCOMPRESSED_ATTR(b_bits_per_pixel, bBitsPerPixel, 8);
+UVCG_UNCOMPRESSED_ATTR(b_default_frame_index, bDefaultFrameIndex, 8);
+UVCG_UNCOMPRESSED_ATTR_RO(b_aspect_ratio_x, bAspectRatioX, 8);
+UVCG_UNCOMPRESSED_ATTR_RO(b_aspect_ratio_y, bAspectRatioY, 8);
+UVCG_UNCOMPRESSED_ATTR_RO(bm_interface_flags, bmInterfaceFlags, 8);
#undef UVCG_UNCOMPRESSED_ATTR
#undef UVCG_UNCOMPRESSED_ATTR_RO
@@ -1406,6 +1612,7 @@ uvcg_uncompressed_bma_controls_store(struct config_item *item,
UVC_ATTR(uvcg_uncompressed_, bma_controls, bmaControls);
static struct configfs_attribute *uvcg_uncompressed_attrs[] = {
+ &uvcg_uncompressed_attr_b_format_index,
&uvcg_uncompressed_attr_guid_format,
&uvcg_uncompressed_attr_b_bits_per_pixel,
&uvcg_uncompressed_attr_b_default_frame_index,
@@ -1417,6 +1624,7 @@ static struct configfs_attribute *uvcg_uncompressed_attrs[] = {
};
static const struct config_item_type uvcg_uncompressed_type = {
+ .ct_item_ops = &uvcg_config_item_ops,
.ct_group_ops = &uvcg_uncompressed_group_ops,
.ct_attrs = uvcg_uncompressed_attrs,
.ct_owner = THIS_MODULE,
@@ -1453,25 +1661,23 @@ static struct config_group *uvcg_uncompressed_make(struct config_group *group,
return &h->fmt.group;
}
-static void uvcg_uncompressed_drop(struct config_group *group,
- struct config_item *item)
-{
- struct uvcg_uncompressed *h = to_uvcg_uncompressed(item);
-
- kfree(h);
-}
-
static struct configfs_group_operations uvcg_uncompressed_grp_ops = {
.make_group = uvcg_uncompressed_make,
- .drop_item = uvcg_uncompressed_drop,
};
-static const struct config_item_type uvcg_uncompressed_grp_type = {
- .ct_group_ops = &uvcg_uncompressed_grp_ops,
- .ct_owner = THIS_MODULE,
+static const struct uvcg_config_group_type uvcg_uncompressed_grp_type = {
+ .type = {
+ .ct_item_ops = &uvcg_config_item_ops,
+ .ct_group_ops = &uvcg_uncompressed_grp_ops,
+ .ct_owner = THIS_MODULE,
+ },
+ .name = "uncompressed",
};
-/* streaming/mjpeg/<NAME> */
+/* -----------------------------------------------------------------------------
+ * streaming/mjpeg/<NAME>
+ */
+
struct uvcg_mjpeg {
struct uvcg_format fmt;
struct uvc_format_mjpeg desc;
@@ -1489,7 +1695,7 @@ static struct configfs_group_operations uvcg_mjpeg_group_ops = {
.drop_item = uvcg_frame_drop,
};
-#define UVCG_MJPEG_ATTR_RO(cname, aname, conv) \
+#define UVCG_MJPEG_ATTR_RO(cname, aname, bits) \
static ssize_t uvcg_mjpeg_##cname##_show(struct config_item *item, char *page)\
{ \
struct uvcg_mjpeg *u = to_uvcg_mjpeg(item); \
@@ -1504,7 +1710,7 @@ static ssize_t uvcg_mjpeg_##cname##_show(struct config_item *item, char *page)\
opts = to_f_uvc_opts(opts_item); \
\
mutex_lock(&opts->lock); \
- result = sprintf(page, "%d\n", conv(u->desc.aname)); \
+ result = sprintf(page, "%u\n", le##bits##_to_cpu(u->desc.aname));\
mutex_unlock(&opts->lock); \
\
mutex_unlock(su_mutex); \
@@ -1513,7 +1719,7 @@ static ssize_t uvcg_mjpeg_##cname##_show(struct config_item *item, char *page)\
\
UVC_ATTR_RO(uvcg_mjpeg_, cname, aname)
-#define UVCG_MJPEG_ATTR(cname, aname, conv) \
+#define UVCG_MJPEG_ATTR(cname, aname, bits) \
static ssize_t uvcg_mjpeg_##cname##_show(struct config_item *item, char *page)\
{ \
struct uvcg_mjpeg *u = to_uvcg_mjpeg(item); \
@@ -1528,7 +1734,7 @@ static ssize_t uvcg_mjpeg_##cname##_show(struct config_item *item, char *page)\
opts = to_f_uvc_opts(opts_item); \
\
mutex_lock(&opts->lock); \
- result = sprintf(page, "%d\n", conv(u->desc.aname)); \
+ result = sprintf(page, "%u\n", le##bits##_to_cpu(u->desc.aname));\
mutex_unlock(&opts->lock); \
\
mutex_unlock(su_mutex); \
@@ -1575,16 +1781,12 @@ end: \
\
UVC_ATTR(uvcg_mjpeg_, cname, aname)
-#define identity_conv(x) (x)
-
-UVCG_MJPEG_ATTR(b_default_frame_index, bDefaultFrameIndex,
- identity_conv);
-UVCG_MJPEG_ATTR_RO(bm_flags, bmFlags, identity_conv);
-UVCG_MJPEG_ATTR_RO(b_aspect_ratio_x, bAspectRatioX, identity_conv);
-UVCG_MJPEG_ATTR_RO(b_aspect_ratio_y, bAspectRatioY, identity_conv);
-UVCG_MJPEG_ATTR_RO(bm_interface_flags, bmInterfaceFlags, identity_conv);
-
-#undef identity_conv
+UVCG_MJPEG_ATTR_RO(b_format_index, bFormatIndex, 8);
+UVCG_MJPEG_ATTR(b_default_frame_index, bDefaultFrameIndex, 8);
+UVCG_MJPEG_ATTR_RO(bm_flags, bmFlags, 8);
+UVCG_MJPEG_ATTR_RO(b_aspect_ratio_x, bAspectRatioX, 8);
+UVCG_MJPEG_ATTR_RO(b_aspect_ratio_y, bAspectRatioY, 8);
+UVCG_MJPEG_ATTR_RO(bm_interface_flags, bmInterfaceFlags, 8);
#undef UVCG_MJPEG_ATTR
#undef UVCG_MJPEG_ATTR_RO
@@ -1607,6 +1809,7 @@ uvcg_mjpeg_bma_controls_store(struct config_item *item,
UVC_ATTR(uvcg_mjpeg_, bma_controls, bmaControls);
static struct configfs_attribute *uvcg_mjpeg_attrs[] = {
+ &uvcg_mjpeg_attr_b_format_index,
&uvcg_mjpeg_attr_b_default_frame_index,
&uvcg_mjpeg_attr_bm_flags,
&uvcg_mjpeg_attr_b_aspect_ratio_x,
@@ -1617,6 +1820,7 @@ static struct configfs_attribute *uvcg_mjpeg_attrs[] = {
};
static const struct config_item_type uvcg_mjpeg_type = {
+ .ct_item_ops = &uvcg_config_item_ops,
.ct_group_ops = &uvcg_mjpeg_group_ops,
.ct_attrs = uvcg_mjpeg_attrs,
.ct_owner = THIS_MODULE,
@@ -1647,56 +1851,42 @@ static struct config_group *uvcg_mjpeg_make(struct config_group *group,
return &h->fmt.group;
}
-static void uvcg_mjpeg_drop(struct config_group *group,
- struct config_item *item)
-{
- struct uvcg_mjpeg *h = to_uvcg_mjpeg(item);
-
- kfree(h);
-}
-
static struct configfs_group_operations uvcg_mjpeg_grp_ops = {
.make_group = uvcg_mjpeg_make,
- .drop_item = uvcg_mjpeg_drop,
};
-static const struct config_item_type uvcg_mjpeg_grp_type = {
- .ct_group_ops = &uvcg_mjpeg_grp_ops,
- .ct_owner = THIS_MODULE,
+static const struct uvcg_config_group_type uvcg_mjpeg_grp_type = {
+ .type = {
+ .ct_item_ops = &uvcg_config_item_ops,
+ .ct_group_ops = &uvcg_mjpeg_grp_ops,
+ .ct_owner = THIS_MODULE,
+ },
+ .name = "mjpeg",
};
-/* streaming/color_matching/default */
-static struct uvcg_default_color_matching {
- struct config_group group;
-} uvcg_default_color_matching;
-
-static inline struct uvcg_default_color_matching
-*to_uvcg_default_color_matching(struct config_item *item)
-{
- return container_of(to_config_group(item),
- struct uvcg_default_color_matching, group);
-}
+/* -----------------------------------------------------------------------------
+ * streaming/color_matching/default
+ */
-#define UVCG_DEFAULT_COLOR_MATCHING_ATTR(cname, aname, conv) \
+#define UVCG_DEFAULT_COLOR_MATCHING_ATTR(cname, aname, bits) \
static ssize_t uvcg_default_color_matching_##cname##_show( \
- struct config_item *item, char *page) \
+ struct config_item *item, char *page) \
{ \
- struct uvcg_default_color_matching *dc = \
- to_uvcg_default_color_matching(item); \
+ struct config_group *group = to_config_group(item); \
struct f_uvc_opts *opts; \
struct config_item *opts_item; \
- struct mutex *su_mutex = &dc->group.cg_subsys->su_mutex; \
+ struct mutex *su_mutex = &group->cg_subsys->su_mutex; \
struct uvc_color_matching_descriptor *cd; \
int result; \
\
mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \
\
- opts_item = dc->group.cg_item.ci_parent->ci_parent->ci_parent; \
+ opts_item = group->cg_item.ci_parent->ci_parent->ci_parent; \
opts = to_f_uvc_opts(opts_item); \
cd = &opts->uvc_color_matching; \
\
mutex_lock(&opts->lock); \
- result = sprintf(page, "%d\n", conv(cd->aname)); \
+ result = sprintf(page, "%u\n", le##bits##_to_cpu(cd->aname)); \
mutex_unlock(&opts->lock); \
\
mutex_unlock(su_mutex); \
@@ -1705,16 +1895,10 @@ static ssize_t uvcg_default_color_matching_##cname##_show( \
\
UVC_ATTR_RO(uvcg_default_color_matching_, cname, aname)
-#define identity_conv(x) (x)
-
-UVCG_DEFAULT_COLOR_MATCHING_ATTR(b_color_primaries, bColorPrimaries,
- identity_conv);
+UVCG_DEFAULT_COLOR_MATCHING_ATTR(b_color_primaries, bColorPrimaries, 8);
UVCG_DEFAULT_COLOR_MATCHING_ATTR(b_transfer_characteristics,
- bTransferCharacteristics, identity_conv);
-UVCG_DEFAULT_COLOR_MATCHING_ATTR(b_matrix_coefficients, bMatrixCoefficients,
- identity_conv);
-
-#undef identity_conv
+ bTransferCharacteristics, 8);
+UVCG_DEFAULT_COLOR_MATCHING_ATTR(b_matrix_coefficients, bMatrixCoefficients, 8);
#undef UVCG_DEFAULT_COLOR_MATCHING_ATTR
@@ -1725,41 +1909,54 @@ static struct configfs_attribute *uvcg_default_color_matching_attrs[] = {
NULL,
};
-static const struct config_item_type uvcg_default_color_matching_type = {
- .ct_attrs = uvcg_default_color_matching_attrs,
- .ct_owner = THIS_MODULE,
+static const struct uvcg_config_group_type uvcg_default_color_matching_type = {
+ .type = {
+ .ct_item_ops = &uvcg_config_item_ops,
+ .ct_attrs = uvcg_default_color_matching_attrs,
+ .ct_owner = THIS_MODULE,
+ },
+ .name = "default",
};
-/* struct uvcg_color_matching {}; */
-
-/* streaming/color_matching */
-static struct uvcg_color_matching_grp {
- struct config_group group;
-} uvcg_color_matching_grp;
+/* -----------------------------------------------------------------------------
+ * streaming/color_matching
+ */
-static const struct config_item_type uvcg_color_matching_grp_type = {
- .ct_owner = THIS_MODULE,
+static const struct uvcg_config_group_type uvcg_color_matching_grp_type = {
+ .type = {
+ .ct_item_ops = &uvcg_config_item_ops,
+ .ct_owner = THIS_MODULE,
+ },
+ .name = "color_matching",
+ .children = (const struct uvcg_config_group_type*[]) {
+ &uvcg_default_color_matching_type,
+ NULL,
+ },
};
-/* streaming/class/{fs|hs|ss} */
-static struct uvcg_streaming_class {
- struct config_group group;
-} uvcg_streaming_class_fs, uvcg_streaming_class_hs, uvcg_streaming_class_ss;
+/* -----------------------------------------------------------------------------
+ * streaming/class/{fs|hs|ss}
+ */
+struct uvcg_streaming_class_group {
+ struct config_group group;
+ const char *name;
+};
static inline struct uvc_descriptor_header
***__uvcg_get_stream_class_arr(struct config_item *i, struct f_uvc_opts *o)
{
- struct uvcg_streaming_class *cl = container_of(to_config_group(i),
- struct uvcg_streaming_class, group);
+ struct uvcg_streaming_class_group *group =
+ container_of(i, struct uvcg_streaming_class_group,
+ group.cg_item);
- if (cl == &uvcg_streaming_class_fs)
+ if (!strcmp(group->name, "fs"))
return &o->uvc_fs_streaming_cls;
- if (cl == &uvcg_streaming_class_hs)
+ if (!strcmp(group->name, "hs"))
return &o->uvc_hs_streaming_cls;
- if (cl == &uvcg_streaming_class_ss)
+ if (!strcmp(group->name, "ss"))
return &o->uvc_ss_streaming_cls;
return NULL;
@@ -1918,24 +2115,22 @@ static int __uvcg_fill_strm(void *priv1, void *priv2, void *priv3, int n,
struct uvcg_format *fmt = priv1;
if (fmt->type == UVCG_UNCOMPRESSED) {
- struct uvc_format_uncompressed *unc = *dest;
struct uvcg_uncompressed *u =
container_of(fmt, struct uvcg_uncompressed,
fmt);
+ u->desc.bFormatIndex = n + 1;
+ u->desc.bNumFrameDescriptors = fmt->num_frames;
memcpy(*dest, &u->desc, sizeof(u->desc));
*dest += sizeof(u->desc);
- unc->bNumFrameDescriptors = fmt->num_frames;
- unc->bFormatIndex = n + 1;
} else if (fmt->type == UVCG_MJPEG) {
- struct uvc_format_mjpeg *mjp = *dest;
struct uvcg_mjpeg *m =
container_of(fmt, struct uvcg_mjpeg, fmt);
+ m->desc.bFormatIndex = n + 1;
+ m->desc.bNumFrameDescriptors = fmt->num_frames;
memcpy(*dest, &m->desc, sizeof(m->desc));
*dest += sizeof(m->desc);
- mjp->bNumFrameDescriptors = fmt->num_frames;
- mjp->bFormatIndex = n + 1;
} else {
return -EINVAL;
}
@@ -2034,6 +2229,7 @@ static int uvcg_streaming_class_allow_link(struct config_item *src,
unlock:
mutex_unlock(&opts->lock);
out:
+ config_item_put(header);
mutex_unlock(su_mutex);
return ret;
}
@@ -2074,10 +2270,12 @@ static void uvcg_streaming_class_drop_link(struct config_item *src,
unlock:
mutex_unlock(&opts->lock);
out:
+ config_item_put(header);
mutex_unlock(su_mutex);
}
static 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,
};
@@ -2087,42 +2285,109 @@ static const struct config_item_type uvcg_streaming_class_type = {
.ct_owner = THIS_MODULE,
};
-/* streaming/class */
-static struct uvcg_streaming_class_grp {
- struct config_group group;
-} uvcg_streaming_class_grp;
+/* -----------------------------------------------------------------------------
+ * streaming/class
+ */
-static const struct config_item_type uvcg_streaming_class_grp_type = {
- .ct_owner = THIS_MODULE,
-};
+static int uvcg_streaming_class_create_children(struct config_group *parent)
+{
+ static const char * const names[] = { "fs", "hs", "ss" };
+ unsigned int i;
-/* streaming */
-static struct uvcg_streaming_grp {
- struct config_group group;
-} uvcg_streaming_grp;
+ for (i = 0; i < ARRAY_SIZE(names); ++i) {
+ struct uvcg_streaming_class_group *group;
+
+ group = kzalloc(sizeof(*group), GFP_KERNEL);
+ if (!group)
+ return -ENOMEM;
+
+ group->name = names[i];
-static const struct config_item_type uvcg_streaming_grp_type = {
- .ct_owner = THIS_MODULE,
+ config_group_init_type_name(&group->group, group->name,
+ &uvcg_streaming_class_type);
+ configfs_add_default_group(&group->group, parent);
+ }
+
+ return 0;
+}
+
+static const struct uvcg_config_group_type uvcg_streaming_class_grp_type = {
+ .type = {
+ .ct_item_ops = &uvcg_config_item_ops,
+ .ct_owner = THIS_MODULE,
+ },
+ .name = "class",
+ .create_children = uvcg_streaming_class_create_children,
};
-static inline struct f_uvc_opts *to_f_uvc_opts(struct config_item *item)
+/* -----------------------------------------------------------------------------
+ * streaming
+ */
+
+static ssize_t uvcg_default_streaming_b_interface_number_show(
+ struct config_item *item, char *page)
{
- return container_of(to_config_group(item), struct f_uvc_opts,
- func_inst.group);
+ struct config_group *group = to_config_group(item);
+ struct mutex *su_mutex = &group->cg_subsys->su_mutex;
+ struct config_item *opts_item;
+ struct f_uvc_opts *opts;
+ int result = 0;
+
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+ opts_item = item->ci_parent;
+ opts = to_f_uvc_opts(opts_item);
+
+ mutex_lock(&opts->lock);
+ result += sprintf(page, "%u\n", opts->streaming_interface);
+ mutex_unlock(&opts->lock);
+
+ mutex_unlock(su_mutex);
+
+ return result;
}
-static void uvc_attr_release(struct config_item *item)
+UVC_ATTR_RO(uvcg_default_streaming_, b_interface_number, bInterfaceNumber);
+
+static struct configfs_attribute *uvcg_default_streaming_attrs[] = {
+ &uvcg_default_streaming_attr_b_interface_number,
+ NULL,
+};
+
+static const struct uvcg_config_group_type uvcg_streaming_grp_type = {
+ .type = {
+ .ct_item_ops = &uvcg_config_item_ops,
+ .ct_attrs = uvcg_default_streaming_attrs,
+ .ct_owner = THIS_MODULE,
+ },
+ .name = "streaming",
+ .children = (const struct uvcg_config_group_type*[]) {
+ &uvcg_streaming_header_grp_type,
+ &uvcg_uncompressed_grp_type,
+ &uvcg_mjpeg_grp_type,
+ &uvcg_color_matching_grp_type,
+ &uvcg_streaming_class_grp_type,
+ NULL,
+ },
+};
+
+/* -----------------------------------------------------------------------------
+ * UVC function
+ */
+
+static void uvc_func_item_release(struct config_item *item)
{
struct f_uvc_opts *opts = to_f_uvc_opts(item);
+ uvcg_config_remove_children(to_config_group(item));
usb_put_function_instance(&opts->func_inst);
}
-static struct configfs_item_operations uvc_item_ops = {
- .release = uvc_attr_release,
+static struct configfs_item_operations uvc_func_item_ops = {
+ .release = uvc_func_item_release,
};
-#define UVCG_OPTS_ATTR(cname, aname, conv, str2u, uxx, vnoc, limit) \
+#define UVCG_OPTS_ATTR(cname, aname, limit) \
static ssize_t f_uvc_opts_##cname##_show( \
struct config_item *item, char *page) \
{ \
@@ -2130,7 +2395,7 @@ static ssize_t f_uvc_opts_##cname##_show( \
int result; \
\
mutex_lock(&opts->lock); \
- result = sprintf(page, "%d\n", conv(opts->cname)); \
+ result = sprintf(page, "%u\n", opts->cname); \
mutex_unlock(&opts->lock); \
\
return result; \
@@ -2141,8 +2406,8 @@ f_uvc_opts_##cname##_store(struct config_item *item, \
const char *page, size_t len) \
{ \
struct f_uvc_opts *opts = to_f_uvc_opts(item); \
+ unsigned int num; \
int ret; \
- uxx num; \
\
mutex_lock(&opts->lock); \
if (opts->refcnt) { \
@@ -2150,7 +2415,7 @@ f_uvc_opts_##cname##_store(struct config_item *item, \
goto end; \
} \
\
- ret = str2u(page, 0, &num); \
+ ret = kstrtouint(page, 0, &num); \
if (ret) \
goto end; \
\
@@ -2158,7 +2423,7 @@ f_uvc_opts_##cname##_store(struct config_item *item, \
ret = -EINVAL; \
goto end; \
} \
- opts->cname = vnoc(num); \
+ opts->cname = num; \
ret = len; \
end: \
mutex_unlock(&opts->lock); \
@@ -2167,16 +2432,9 @@ end: \
\
UVC_ATTR(f_uvc_opts_, cname, cname)
-#define identity_conv(x) (x)
-
-UVCG_OPTS_ATTR(streaming_interval, streaming_interval, identity_conv,
- kstrtou8, u8, identity_conv, 16);
-UVCG_OPTS_ATTR(streaming_maxpacket, streaming_maxpacket, le16_to_cpu,
- kstrtou16, u16, le16_to_cpu, 3072);
-UVCG_OPTS_ATTR(streaming_maxburst, streaming_maxburst, identity_conv,
- kstrtou8, u8, identity_conv, 15);
-
-#undef identity_conv
+UVCG_OPTS_ATTR(streaming_interval, streaming_interval, 16);
+UVCG_OPTS_ATTR(streaming_maxpacket, streaming_maxpacket, 3072);
+UVCG_OPTS_ATTR(streaming_maxburst, streaming_maxburst, 15);
#undef UVCG_OPTS_ATTR
@@ -2187,123 +2445,31 @@ static struct configfs_attribute *uvc_attrs[] = {
NULL,
};
-static const struct config_item_type uvc_func_type = {
- .ct_item_ops = &uvc_item_ops,
- .ct_attrs = uvc_attrs,
- .ct_owner = THIS_MODULE,
+static const struct uvcg_config_group_type uvc_func_type = {
+ .type = {
+ .ct_item_ops = &uvc_func_item_ops,
+ .ct_attrs = uvc_attrs,
+ .ct_owner = THIS_MODULE,
+ },
+ .name = "",
+ .children = (const struct uvcg_config_group_type*[]) {
+ &uvcg_control_grp_type,
+ &uvcg_streaming_grp_type,
+ NULL,
+ },
};
int uvcg_attach_configfs(struct f_uvc_opts *opts)
{
- config_group_init_type_name(&uvcg_control_header_grp.group,
- "header",
- &uvcg_control_header_grp_type);
-
- config_group_init_type_name(&uvcg_default_processing.group,
- "default", &uvcg_default_processing_type);
- config_group_init_type_name(&uvcg_processing_grp.group,
- "processing", &uvcg_processing_grp_type);
- configfs_add_default_group(&uvcg_default_processing.group,
- &uvcg_processing_grp.group);
-
- config_group_init_type_name(&uvcg_default_camera.group,
- "default", &uvcg_default_camera_type);
- config_group_init_type_name(&uvcg_camera_grp.group,
- "camera", &uvcg_camera_grp_type);
- configfs_add_default_group(&uvcg_default_camera.group,
- &uvcg_camera_grp.group);
-
- config_group_init_type_name(&uvcg_default_output.group,
- "default", &uvcg_default_output_type);
- config_group_init_type_name(&uvcg_output_grp.group,
- "output", &uvcg_output_grp_type);
- configfs_add_default_group(&uvcg_default_output.group,
- &uvcg_output_grp.group);
-
- config_group_init_type_name(&uvcg_terminal_grp.group,
- "terminal", &uvcg_terminal_grp_type);
- configfs_add_default_group(&uvcg_camera_grp.group,
- &uvcg_terminal_grp.group);
- configfs_add_default_group(&uvcg_output_grp.group,
- &uvcg_terminal_grp.group);
-
- config_group_init_type_name(&uvcg_control_class_fs.group,
- "fs", &uvcg_control_class_type);
- config_group_init_type_name(&uvcg_control_class_ss.group,
- "ss", &uvcg_control_class_type);
- config_group_init_type_name(&uvcg_control_class_grp.group,
- "class",
- &uvcg_control_class_grp_type);
- configfs_add_default_group(&uvcg_control_class_fs.group,
- &uvcg_control_class_grp.group);
- configfs_add_default_group(&uvcg_control_class_ss.group,
- &uvcg_control_class_grp.group);
-
- config_group_init_type_name(&uvcg_control_grp.group,
- "control",
- &uvcg_control_grp_type);
- configfs_add_default_group(&uvcg_control_header_grp.group,
- &uvcg_control_grp.group);
- configfs_add_default_group(&uvcg_processing_grp.group,
- &uvcg_control_grp.group);
- configfs_add_default_group(&uvcg_terminal_grp.group,
- &uvcg_control_grp.group);
- configfs_add_default_group(&uvcg_control_class_grp.group,
- &uvcg_control_grp.group);
-
- config_group_init_type_name(&uvcg_streaming_header_grp.group,
- "header",
- &uvcg_streaming_header_grp_type);
- config_group_init_type_name(&uvcg_uncompressed_grp.group,
- "uncompressed",
- &uvcg_uncompressed_grp_type);
- config_group_init_type_name(&uvcg_mjpeg_grp.group,
- "mjpeg",
- &uvcg_mjpeg_grp_type);
- config_group_init_type_name(&uvcg_default_color_matching.group,
- "default",
- &uvcg_default_color_matching_type);
- config_group_init_type_name(&uvcg_color_matching_grp.group,
- "color_matching",
- &uvcg_color_matching_grp_type);
- configfs_add_default_group(&uvcg_default_color_matching.group,
- &uvcg_color_matching_grp.group);
-
- config_group_init_type_name(&uvcg_streaming_class_fs.group,
- "fs", &uvcg_streaming_class_type);
- config_group_init_type_name(&uvcg_streaming_class_hs.group,
- "hs", &uvcg_streaming_class_type);
- config_group_init_type_name(&uvcg_streaming_class_ss.group,
- "ss", &uvcg_streaming_class_type);
- config_group_init_type_name(&uvcg_streaming_class_grp.group,
- "class", &uvcg_streaming_class_grp_type);
- configfs_add_default_group(&uvcg_streaming_class_fs.group,
- &uvcg_streaming_class_grp.group);
- configfs_add_default_group(&uvcg_streaming_class_hs.group,
- &uvcg_streaming_class_grp.group);
- configfs_add_default_group(&uvcg_streaming_class_ss.group,
- &uvcg_streaming_class_grp.group);
-
- config_group_init_type_name(&uvcg_streaming_grp.group,
- "streaming", &uvcg_streaming_grp_type);
- configfs_add_default_group(&uvcg_streaming_header_grp.group,
- &uvcg_streaming_grp.group);
- configfs_add_default_group(&uvcg_uncompressed_grp.group,
- &uvcg_streaming_grp.group);
- configfs_add_default_group(&uvcg_mjpeg_grp.group,
- &uvcg_streaming_grp.group);
- configfs_add_default_group(&uvcg_color_matching_grp.group,
- &uvcg_streaming_grp.group);
- configfs_add_default_group(&uvcg_streaming_class_grp.group,
- &uvcg_streaming_grp.group);
-
- config_group_init_type_name(&opts->func_inst.group,
- "",
- &uvc_func_type);
- configfs_add_default_group(&uvcg_control_grp.group,
- &opts->func_inst.group);
- configfs_add_default_group(&uvcg_streaming_grp.group,
- &opts->func_inst.group);
+ int ret;
- return 0;
+ config_group_init_type_name(&opts->func_inst.group, uvc_func_type.name,
+ &uvc_func_type.type);
+
+ ret = uvcg_config_create_children(&opts->func_inst.group,
+ &uvc_func_type);
+ if (ret < 0)
+ config_group_put(&opts->func_inst.group);
+
+ return ret;
}
diff --git a/drivers/usb/gadget/function/uvc_queue.c b/drivers/usb/gadget/function/uvc_queue.c
index 9e33d5206d54..f2497cb96abb 100644
--- a/drivers/usb/gadget/function/uvc_queue.c
+++ b/drivers/usb/gadget/function/uvc_queue.c
@@ -166,7 +166,7 @@ int uvcg_queue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf)
unsigned long flags;
int ret;
- ret = vb2_qbuf(&queue->queue, buf);
+ ret = vb2_qbuf(&queue->queue, NULL, buf);
if (ret < 0)
return ret;
diff --git a/drivers/usb/gadget/function/uvc_queue.h b/drivers/usb/gadget/function/uvc_queue.h
index f9f65b5c1062..2f0fff769843 100644
--- a/drivers/usb/gadget/function/uvc_queue.h
+++ b/drivers/usb/gadget/function/uvc_queue.h
@@ -2,13 +2,15 @@
#ifndef _UVC_QUEUE_H_
#define _UVC_QUEUE_H_
-#ifdef __KERNEL__
-
-#include <linux/kernel.h>
+#include <linux/list.h>
#include <linux/poll.h>
-#include <linux/videodev2.h>
+#include <linux/spinlock.h>
+
#include <media/videobuf2-v4l2.h>
+struct file;
+struct mutex;
+
/* Maximum frame size in bytes, for sanity checking. */
#define UVC_MAX_FRAME_SIZE (16*1024*1024)
/* Maximum number of video buffers. */
@@ -91,7 +93,5 @@ struct uvc_buffer *uvcg_queue_next_buffer(struct uvc_video_queue *queue,
struct uvc_buffer *uvcg_queue_head(struct uvc_video_queue *queue);
-#endif /* __KERNEL__ */
-
#endif /* _UVC_QUEUE_H_ */
diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c
index 9a9019625496..a1183eccee22 100644
--- a/drivers/usb/gadget/function/uvc_v4l2.c
+++ b/drivers/usb/gadget/function/uvc_v4l2.c
@@ -6,10 +6,11 @@
* Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*/
-#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/errno.h>
+#include <linux/kernel.h>
#include <linux/list.h>
+#include <linux/usb/g_uvc.h>
#include <linux/videodev2.h>
#include <linux/vmalloc.h>
#include <linux/wait.h>
@@ -114,8 +115,8 @@ uvc_v4l2_set_format(struct file *file, void *fh, struct v4l2_format *fmt)
}
if (i == ARRAY_SIZE(uvc_formats)) {
- printk(KERN_INFO "Unsupported format 0x%08x.\n",
- fmt->fmt.pix.pixelformat);
+ uvcg_info(&uvc->func, "Unsupported format 0x%08x.\n",
+ fmt->fmt.pix.pixelformat);
return -EINVAL;
}
diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c
index d3567b90343a..5c042f380708 100644
--- a/drivers/usb/gadget/function/uvc_video.c
+++ b/drivers/usb/gadget/function/uvc_video.c
@@ -125,6 +125,23 @@ uvc_video_encode_isoc(struct usb_request *req, struct uvc_video *video,
* Request handling
*/
+static int uvcg_video_ep_queue(struct uvc_video *video, struct usb_request *req)
+{
+ int ret;
+
+ ret = usb_ep_queue(video->ep, req, GFP_ATOMIC);
+ if (ret < 0) {
+ uvcg_err(&video->uvc->func, "Failed to queue request (%d).\n",
+ ret);
+
+ /* Isochronous endpoints can't be halted. */
+ if (usb_endpoint_xfer_bulk(video->ep->desc))
+ usb_ep_set_halt(video->ep);
+ }
+
+ return ret;
+}
+
/*
* I somehow feel that synchronisation won't be easy to achieve here. We have
* three events that control USB requests submission:
@@ -169,13 +186,14 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req)
break;
case -ESHUTDOWN: /* disconnect from host. */
- printk(KERN_DEBUG "VS request cancelled.\n");
+ uvcg_dbg(&video->uvc->func, "VS request cancelled.\n");
uvcg_queue_cancel(queue, 1);
goto requeue;
default:
- printk(KERN_INFO "VS request completed with status %d.\n",
- req->status);
+ uvcg_info(&video->uvc->func,
+ "VS request completed with status %d.\n",
+ req->status);
uvcg_queue_cancel(queue, 0);
goto requeue;
}
@@ -189,14 +207,13 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req)
video->encode(req, video, buf);
- if ((ret = usb_ep_queue(ep, req, GFP_ATOMIC)) < 0) {
- printk(KERN_INFO "Failed to queue request (%d).\n", ret);
- usb_ep_set_halt(ep);
- spin_unlock_irqrestore(&video->queue.irqlock, flags);
+ ret = uvcg_video_ep_queue(video, req);
+ spin_unlock_irqrestore(&video->queue.irqlock, flags);
+
+ if (ret < 0) {
uvcg_queue_cancel(queue, 0);
goto requeue;
}
- spin_unlock_irqrestore(&video->queue.irqlock, flags);
return;
@@ -316,15 +333,13 @@ int uvcg_video_pump(struct uvc_video *video)
video->encode(req, video, buf);
/* Queue the USB request */
- ret = usb_ep_queue(video->ep, req, GFP_ATOMIC);
+ ret = uvcg_video_ep_queue(video, req);
+ spin_unlock_irqrestore(&queue->irqlock, flags);
+
if (ret < 0) {
- printk(KERN_INFO "Failed to queue request (%d)\n", ret);
- usb_ep_set_halt(video->ep);
- spin_unlock_irqrestore(&queue->irqlock, flags);
uvcg_queue_cancel(queue, 0);
break;
}
- spin_unlock_irqrestore(&queue->irqlock, flags);
}
spin_lock_irqsave(&video->req_lock, flags);
@@ -342,8 +357,8 @@ int uvcg_video_enable(struct uvc_video *video, int enable)
int ret;
if (video->ep == NULL) {
- printk(KERN_INFO "Video enable failed, device is "
- "uninitialized.\n");
+ uvcg_info(&video->uvc->func,
+ "Video enable failed, device is uninitialized.\n");
return -ENODEV;
}
@@ -375,11 +390,12 @@ int uvcg_video_enable(struct uvc_video *video, int enable)
/*
* Initialize the UVC video stream.
*/
-int uvcg_video_init(struct uvc_video *video)
+int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc)
{
INIT_LIST_HEAD(&video->req_free);
spin_lock_init(&video->req_lock);
+ video->uvc = uvc;
video->fcc = V4L2_PIX_FMT_YUYV;
video->bpp = 16;
video->width = 320;
diff --git a/drivers/usb/gadget/function/uvc_video.h b/drivers/usb/gadget/function/uvc_video.h
index 6c20aa75f966..278dc52c7604 100644
--- a/drivers/usb/gadget/function/uvc_video.h
+++ b/drivers/usb/gadget/function/uvc_video.h
@@ -12,10 +12,12 @@
#ifndef __UVC_VIDEO_H__
#define __UVC_VIDEO_H__
+struct uvc_video;
+
int uvcg_video_pump(struct uvc_video *video);
int uvcg_video_enable(struct uvc_video *video, int enable);
-int uvcg_video_init(struct uvc_video *video);
+int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc);
#endif /* __UVC_VIDEO_H__ */
diff --git a/drivers/usb/gadget/legacy/tcm_usb_gadget.c b/drivers/usb/gadget/legacy/tcm_usb_gadget.c
index 682bf99dcf76..40870227999a 100644
--- a/drivers/usb/gadget/legacy/tcm_usb_gadget.c
+++ b/drivers/usb/gadget/legacy/tcm_usb_gadget.c
@@ -41,7 +41,7 @@ static struct usb_device_descriptor usbg_device_desc = {
#define USB_G_STR_CONFIG USB_GADGET_FIRST_AVAIL_IDX
static struct usb_string usbg_us_strings[] = {
- [USB_GADGET_MANUFACTURER_IDX].s = "Target Manufactor",
+ [USB_GADGET_MANUFACTURER_IDX].s = "Target Manufacturer",
[USB_GADGET_PRODUCT_IDX].s = "Target Product",
[USB_GADGET_SERIAL_IDX].s = "000000000001",
[USB_G_STR_CONFIG].s = "default config",
diff --git a/drivers/usb/gadget/legacy/webcam.c b/drivers/usb/gadget/legacy/webcam.c
index 6b86568c9157..a9f8eb8e1c76 100644
--- a/drivers/usb/gadget/legacy/webcam.c
+++ b/drivers/usb/gadget/legacy/webcam.c
@@ -30,9 +30,6 @@ static unsigned int streaming_maxburst;
module_param(streaming_maxburst, uint, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(streaming_maxburst, "0 - 15 (ss only)");
-static unsigned int trace;
-module_param(trace, uint, S_IRUGO|S_IWUSR);
-MODULE_PARM_DESC(trace, "Trace level bitmask");
/* --------------------------------------------------------------------------
* Device descriptor
*/
@@ -379,7 +376,6 @@ webcam_bind(struct usb_composite_dev *cdev)
uvc_opts->streaming_interval = streaming_interval;
uvc_opts->streaming_maxpacket = streaming_maxpacket;
uvc_opts->streaming_maxburst = streaming_maxburst;
- uvc_set_trace_param(trace);
uvc_opts->fs_control = uvc_fs_control_cls;
uvc_opts->ss_control = uvc_ss_control_cls;
diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig
index 0875d38476ee..0a16cbd4e528 100644
--- a/drivers/usb/gadget/udc/Kconfig
+++ b/drivers/usb/gadget/udc/Kconfig
@@ -179,7 +179,7 @@ config USB_R8A66597
config USB_RENESAS_USBHS_UDC
tristate 'Renesas USBHS controller'
- depends on USB_RENESAS_USBHS && HAS_DMA
+ depends on USB_RENESAS_USBHS
help
Renesas USBHS is a discrete USB host and peripheral controller chip
that supports both full and high speed USB 2.0 data transfers.
@@ -192,7 +192,8 @@ config USB_RENESAS_USBHS_UDC
config USB_RENESAS_USB3
tristate 'Renesas USB3.0 Peripheral controller'
depends on ARCH_RENESAS || COMPILE_TEST
- depends on EXTCON && HAS_DMA
+ depends on EXTCON
+ select USB_ROLE_SWITCH
help
Renesas USB3.0 Peripheral controller is a USB peripheral controller
that supports super, high, and full speed USB 3.0 data transfers.
@@ -438,6 +439,8 @@ config USB_GADGET_XILINX
dynamically linked module called "udc-xilinx" and force all
gadget drivers to also be dynamically linked.
+source "drivers/usb/gadget/udc/aspeed-vhub/Kconfig"
+
#
# LAST -- dummy/emulated controller
#
diff --git a/drivers/usb/gadget/udc/Makefile b/drivers/usb/gadget/udc/Makefile
index ce865b129fd6..897f648f3cf1 100644
--- a/drivers/usb/gadget/udc/Makefile
+++ b/drivers/usb/gadget/udc/Makefile
@@ -39,4 +39,5 @@ 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
+obj-$(CONFIG_USB_ASPEED_VHUB) += aspeed-vhub/
obj-$(CONFIG_USB_BDC_UDC) += bdc/
diff --git a/drivers/usb/gadget/udc/aspeed-vhub/Kconfig b/drivers/usb/gadget/udc/aspeed-vhub/Kconfig
new file mode 100644
index 000000000000..83ba8a2eb6af
--- /dev/null
+++ b/drivers/usb/gadget/udc/aspeed-vhub/Kconfig
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0+
+config USB_ASPEED_VHUB
+ tristate "Aspeed vHub UDC driver"
+ depends on ARCH_ASPEED || COMPILE_TEST
+ depends on USB_LIBCOMPOSITE
+ help
+ USB peripheral controller for the Aspeed AST2500 family
+ SoCs supporting the "vHub" functionality and USB2.0
diff --git a/drivers/usb/gadget/udc/aspeed-vhub/Makefile b/drivers/usb/gadget/udc/aspeed-vhub/Makefile
new file mode 100644
index 000000000000..9f3add605f8e
--- /dev/null
+++ b/drivers/usb/gadget/udc/aspeed-vhub/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0+
+obj-$(CONFIG_USB_ASPEED_VHUB) += aspeed-vhub.o
+aspeed-vhub-y := core.o ep0.o epn.o dev.o hub.o
+
diff --git a/drivers/usb/gadget/udc/aspeed-vhub/core.c b/drivers/usb/gadget/udc/aspeed-vhub/core.c
new file mode 100644
index 000000000000..db3628be38c0
--- /dev/null
+++ b/drivers/usb/gadget/udc/aspeed-vhub/core.c
@@ -0,0 +1,425 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * aspeed-vhub -- Driver for Aspeed SoC "vHub" USB gadget
+ *
+ * core.c - Top level support
+ *
+ * Copyright 2017 IBM Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/proc_fs.h>
+#include <linux/prefetch.h>
+#include <linux/clk.h>
+#include <linux/usb/gadget.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/regmap.h>
+#include <linux/dma-mapping.h>
+
+#include "vhub.h"
+
+void ast_vhub_done(struct ast_vhub_ep *ep, struct ast_vhub_req *req,
+ int status)
+{
+ bool internal = req->internal;
+
+ EPVDBG(ep, "completing request @%p, status %d\n", req, status);
+
+ list_del_init(&req->queue);
+
+ if (req->req.status == -EINPROGRESS)
+ req->req.status = status;
+
+ if (req->req.dma) {
+ if (!WARN_ON(!ep->dev))
+ usb_gadget_unmap_request(&ep->dev->gadget,
+ &req->req, ep->epn.is_in);
+ req->req.dma = 0;
+ }
+
+ /*
+ * If this isn't an internal EP0 request, call the core
+ * to call the gadget completion.
+ */
+ if (!internal) {
+ spin_unlock(&ep->vhub->lock);
+ usb_gadget_giveback_request(&ep->ep, &req->req);
+ spin_lock(&ep->vhub->lock);
+ }
+}
+
+void ast_vhub_nuke(struct ast_vhub_ep *ep, int status)
+{
+ struct ast_vhub_req *req;
+
+ EPDBG(ep, "Nuking\n");
+
+ /* Beware, lock will be dropped & req-acquired by done() */
+ while (!list_empty(&ep->queue)) {
+ req = list_first_entry(&ep->queue, struct ast_vhub_req, queue);
+ ast_vhub_done(ep, req, status);
+ }
+}
+
+struct usb_request *ast_vhub_alloc_request(struct usb_ep *u_ep,
+ gfp_t gfp_flags)
+{
+ struct ast_vhub_req *req;
+
+ req = kzalloc(sizeof(*req), gfp_flags);
+ if (!req)
+ return NULL;
+ return &req->req;
+}
+
+void ast_vhub_free_request(struct usb_ep *u_ep, struct usb_request *u_req)
+{
+ struct ast_vhub_req *req = to_ast_req(u_req);
+
+ kfree(req);
+}
+
+static irqreturn_t ast_vhub_irq(int irq, void *data)
+{
+ struct ast_vhub *vhub = data;
+ irqreturn_t iret = IRQ_NONE;
+ u32 istat;
+
+ /* Stale interrupt while tearing down */
+ if (!vhub->ep0_bufs)
+ return IRQ_NONE;
+
+ spin_lock(&vhub->lock);
+
+ /* Read and ACK interrupts */
+ istat = readl(vhub->regs + AST_VHUB_ISR);
+ if (!istat)
+ goto bail;
+ writel(istat, vhub->regs + AST_VHUB_ISR);
+ iret = IRQ_HANDLED;
+
+ UDCVDBG(vhub, "irq status=%08x, ep_acks=%08x ep_nacks=%08x\n",
+ istat,
+ readl(vhub->regs + AST_VHUB_EP_ACK_ISR),
+ readl(vhub->regs + AST_VHUB_EP_NACK_ISR));
+
+ /* Handle generic EPs first */
+ if (istat & VHUB_IRQ_EP_POOL_ACK_STALL) {
+ u32 i, ep_acks = readl(vhub->regs + AST_VHUB_EP_ACK_ISR);
+ writel(ep_acks, vhub->regs + AST_VHUB_EP_ACK_ISR);
+
+ for (i = 0; ep_acks && i < AST_VHUB_NUM_GEN_EPs; i++) {
+ u32 mask = VHUB_EP_IRQ(i);
+ if (ep_acks & mask) {
+ ast_vhub_epn_ack_irq(&vhub->epns[i]);
+ ep_acks &= ~mask;
+ }
+ }
+ }
+
+ /* Handle device interrupts */
+ if (istat & (VHUB_IRQ_DEVICE1 |
+ VHUB_IRQ_DEVICE2 |
+ VHUB_IRQ_DEVICE3 |
+ VHUB_IRQ_DEVICE4 |
+ VHUB_IRQ_DEVICE5)) {
+ if (istat & VHUB_IRQ_DEVICE1)
+ ast_vhub_dev_irq(&vhub->ports[0].dev);
+ if (istat & VHUB_IRQ_DEVICE2)
+ ast_vhub_dev_irq(&vhub->ports[1].dev);
+ if (istat & VHUB_IRQ_DEVICE3)
+ ast_vhub_dev_irq(&vhub->ports[2].dev);
+ if (istat & VHUB_IRQ_DEVICE4)
+ ast_vhub_dev_irq(&vhub->ports[3].dev);
+ if (istat & VHUB_IRQ_DEVICE5)
+ ast_vhub_dev_irq(&vhub->ports[4].dev);
+ }
+
+ /* Handle top-level vHub EP0 interrupts */
+ if (istat & (VHUB_IRQ_HUB_EP0_OUT_ACK_STALL |
+ VHUB_IRQ_HUB_EP0_IN_ACK_STALL |
+ VHUB_IRQ_HUB_EP0_SETUP)) {
+ if (istat & VHUB_IRQ_HUB_EP0_IN_ACK_STALL)
+ ast_vhub_ep0_handle_ack(&vhub->ep0, true);
+ if (istat & VHUB_IRQ_HUB_EP0_OUT_ACK_STALL)
+ ast_vhub_ep0_handle_ack(&vhub->ep0, false);
+ if (istat & VHUB_IRQ_HUB_EP0_SETUP)
+ ast_vhub_ep0_handle_setup(&vhub->ep0);
+ }
+
+ /* Various top level bus events */
+ if (istat & (VHUB_IRQ_BUS_RESUME |
+ VHUB_IRQ_BUS_SUSPEND |
+ VHUB_IRQ_BUS_RESET)) {
+ if (istat & VHUB_IRQ_BUS_RESUME)
+ ast_vhub_hub_resume(vhub);
+ if (istat & VHUB_IRQ_BUS_SUSPEND)
+ ast_vhub_hub_suspend(vhub);
+ if (istat & VHUB_IRQ_BUS_RESET)
+ ast_vhub_hub_reset(vhub);
+ }
+
+ bail:
+ spin_unlock(&vhub->lock);
+ return iret;
+}
+
+void ast_vhub_init_hw(struct ast_vhub *vhub)
+{
+ u32 ctrl;
+
+ UDCDBG(vhub,"(Re)Starting HW ...\n");
+
+ /* Enable PHY */
+ ctrl = VHUB_CTRL_PHY_CLK |
+ VHUB_CTRL_PHY_RESET_DIS;
+
+ /*
+ * We do *NOT* set the VHUB_CTRL_CLK_STOP_SUSPEND bit
+ * to stop the logic clock during suspend because
+ * it causes the registers to become inaccessible and
+ * we haven't yet figured out a good wayt to bring the
+ * controller back into life to issue a wakeup.
+ */
+
+ /*
+ * Set some ISO & split control bits according to Aspeed
+ * recommendation
+ *
+ * VHUB_CTRL_ISO_RSP_CTRL: When set tells the HW to respond
+ * with 0 bytes data packet to ISO IN endpoints when no data
+ * is available.
+ *
+ * VHUB_CTRL_SPLIT_IN: This makes a SOF complete a split IN
+ * transaction.
+ */
+ ctrl |= VHUB_CTRL_ISO_RSP_CTRL | VHUB_CTRL_SPLIT_IN;
+ writel(ctrl, vhub->regs + AST_VHUB_CTRL);
+ udelay(1);
+
+ /* Set descriptor ring size */
+ if (AST_VHUB_DESCS_COUNT == 256) {
+ ctrl |= VHUB_CTRL_LONG_DESC;
+ writel(ctrl, vhub->regs + AST_VHUB_CTRL);
+ } else {
+ BUILD_BUG_ON(AST_VHUB_DESCS_COUNT != 32);
+ }
+
+ /* Reset all devices */
+ writel(VHUB_SW_RESET_ALL, vhub->regs + AST_VHUB_SW_RESET);
+ udelay(1);
+ writel(0, vhub->regs + AST_VHUB_SW_RESET);
+
+ /* Disable and cleanup EP ACK/NACK interrupts */
+ writel(0, vhub->regs + AST_VHUB_EP_ACK_IER);
+ writel(0, vhub->regs + AST_VHUB_EP_NACK_IER);
+ writel(VHUB_EP_IRQ_ALL, vhub->regs + AST_VHUB_EP_ACK_ISR);
+ writel(VHUB_EP_IRQ_ALL, vhub->regs + AST_VHUB_EP_NACK_ISR);
+
+ /* Default settings for EP0, enable HW hub EP1 */
+ writel(0, vhub->regs + AST_VHUB_EP0_CTRL);
+ writel(VHUB_EP1_CTRL_RESET_TOGGLE |
+ VHUB_EP1_CTRL_ENABLE,
+ vhub->regs + AST_VHUB_EP1_CTRL);
+ writel(0, vhub->regs + AST_VHUB_EP1_STS_CHG);
+
+ /* Configure EP0 DMA buffer */
+ writel(vhub->ep0.buf_dma, vhub->regs + AST_VHUB_EP0_DATA);
+
+ /* Clear address */
+ writel(0, vhub->regs + AST_VHUB_CONF);
+
+ /* Pullup hub (activate on host) */
+ if (vhub->force_usb1)
+ ctrl |= VHUB_CTRL_FULL_SPEED_ONLY;
+
+ ctrl |= VHUB_CTRL_UPSTREAM_CONNECT;
+ writel(ctrl, vhub->regs + AST_VHUB_CTRL);
+
+ /* Enable some interrupts */
+ writel(VHUB_IRQ_HUB_EP0_IN_ACK_STALL |
+ VHUB_IRQ_HUB_EP0_OUT_ACK_STALL |
+ VHUB_IRQ_HUB_EP0_SETUP |
+ VHUB_IRQ_EP_POOL_ACK_STALL |
+ VHUB_IRQ_BUS_RESUME |
+ VHUB_IRQ_BUS_SUSPEND |
+ VHUB_IRQ_BUS_RESET,
+ vhub->regs + AST_VHUB_IER);
+}
+
+static int ast_vhub_remove(struct platform_device *pdev)
+{
+ struct ast_vhub *vhub = platform_get_drvdata(pdev);
+ unsigned long flags;
+ int i;
+
+ if (!vhub || !vhub->regs)
+ return 0;
+
+ /* Remove devices */
+ for (i = 0; i < AST_VHUB_NUM_PORTS; i++)
+ ast_vhub_del_dev(&vhub->ports[i].dev);
+
+ spin_lock_irqsave(&vhub->lock, flags);
+
+ /* Mask & ack all interrupts */
+ writel(0, vhub->regs + AST_VHUB_IER);
+ writel(VHUB_IRQ_ACK_ALL, vhub->regs + AST_VHUB_ISR);
+
+ /* Pull device, leave PHY enabled */
+ writel(VHUB_CTRL_PHY_CLK |
+ VHUB_CTRL_PHY_RESET_DIS,
+ vhub->regs + AST_VHUB_CTRL);
+
+ if (vhub->clk)
+ clk_disable_unprepare(vhub->clk);
+
+ spin_unlock_irqrestore(&vhub->lock, flags);
+
+ if (vhub->ep0_bufs)
+ dma_free_coherent(&pdev->dev,
+ AST_VHUB_EP0_MAX_PACKET *
+ (AST_VHUB_NUM_PORTS + 1),
+ vhub->ep0_bufs,
+ vhub->ep0_bufs_dma);
+ vhub->ep0_bufs = NULL;
+
+ return 0;
+}
+
+static int ast_vhub_probe(struct platform_device *pdev)
+{
+ enum usb_device_speed max_speed;
+ struct ast_vhub *vhub;
+ struct resource *res;
+ int i, rc = 0;
+
+ vhub = devm_kzalloc(&pdev->dev, sizeof(*vhub), GFP_KERNEL);
+ if (!vhub)
+ return -ENOMEM;
+
+ spin_lock_init(&vhub->lock);
+ vhub->pdev = pdev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ vhub->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(vhub->regs)) {
+ dev_err(&pdev->dev, "Failed to map resources\n");
+ return PTR_ERR(vhub->regs);
+ }
+ UDCDBG(vhub, "vHub@%pR mapped @%p\n", res, vhub->regs);
+
+ platform_set_drvdata(pdev, vhub);
+
+ vhub->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(vhub->clk)) {
+ rc = PTR_ERR(vhub->clk);
+ goto err;
+ }
+ rc = clk_prepare_enable(vhub->clk);
+ if (rc) {
+ dev_err(&pdev->dev, "Error couldn't enable clock (%d)\n", 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)
+ vhub->force_usb1 = true;
+
+ /* Mask & ack all interrupts before installing the handler */
+ writel(0, vhub->regs + AST_VHUB_IER);
+ writel(VHUB_IRQ_ACK_ALL, vhub->regs + AST_VHUB_ISR);
+
+ /* Find interrupt and install handler */
+ vhub->irq = platform_get_irq(pdev, 0);
+ if (vhub->irq < 0) {
+ dev_err(&pdev->dev, "Failed to get interrupt\n");
+ rc = vhub->irq;
+ goto err;
+ }
+ rc = devm_request_irq(&pdev->dev, vhub->irq, ast_vhub_irq, 0,
+ KBUILD_MODNAME, vhub);
+ if (rc) {
+ dev_err(&pdev->dev, "Failed to request interrupt\n");
+ goto err;
+ }
+
+ /*
+ * Allocate DMA buffers for all EP0s in one chunk,
+ * one per port and one for the vHub itself
+ */
+ vhub->ep0_bufs = dma_alloc_coherent(&pdev->dev,
+ AST_VHUB_EP0_MAX_PACKET *
+ (AST_VHUB_NUM_PORTS + 1),
+ &vhub->ep0_bufs_dma, GFP_KERNEL);
+ if (!vhub->ep0_bufs) {
+ dev_err(&pdev->dev, "Failed to allocate EP0 DMA buffers\n");
+ rc = -ENOMEM;
+ goto err;
+ }
+ UDCVDBG(vhub, "EP0 DMA buffers @%p (DMA 0x%08x)\n",
+ vhub->ep0_bufs, (u32)vhub->ep0_bufs_dma);
+
+ /* Init vHub EP0 */
+ ast_vhub_init_ep0(vhub, &vhub->ep0, NULL);
+
+ /* Init devices */
+ for (i = 0; i < AST_VHUB_NUM_PORTS && rc == 0; i++)
+ rc = ast_vhub_init_dev(vhub, i);
+ if (rc)
+ goto err;
+
+ /* Init hub emulation */
+ ast_vhub_init_hub(vhub);
+
+ /* Initialize HW */
+ ast_vhub_init_hw(vhub);
+
+ dev_info(&pdev->dev, "Initialized virtual hub in USB%d mode\n",
+ vhub->force_usb1 ? 1 : 2);
+
+ return 0;
+ err:
+ ast_vhub_remove(pdev);
+ return rc;
+}
+
+static const struct of_device_id ast_vhub_dt_ids[] = {
+ {
+ .compatible = "aspeed,ast2400-usb-vhub",
+ },
+ {
+ .compatible = "aspeed,ast2500-usb-vhub",
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ast_vhub_dt_ids);
+
+static struct platform_driver ast_vhub_driver = {
+ .probe = ast_vhub_probe,
+ .remove = ast_vhub_remove,
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .of_match_table = ast_vhub_dt_ids,
+ },
+};
+module_platform_driver(ast_vhub_driver);
+
+MODULE_DESCRIPTION("Aspeed vHub udc driver");
+MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/udc/aspeed-vhub/dev.c b/drivers/usb/gadget/udc/aspeed-vhub/dev.c
new file mode 100644
index 000000000000..f0233912bace
--- /dev/null
+++ b/drivers/usb/gadget/udc/aspeed-vhub/dev.c
@@ -0,0 +1,589 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * aspeed-vhub -- Driver for Aspeed SoC "vHub" USB gadget
+ *
+ * dev.c - Individual device/gadget management (ie, a port = a gadget)
+ *
+ * Copyright 2017 IBM Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/proc_fs.h>
+#include <linux/prefetch.h>
+#include <linux/clk.h>
+#include <linux/usb/gadget.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/regmap.h>
+#include <linux/dma-mapping.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+
+#include "vhub.h"
+
+void ast_vhub_dev_irq(struct ast_vhub_dev *d)
+{
+ u32 istat = readl(d->regs + AST_VHUB_DEV_ISR);
+
+ writel(istat, d->regs + AST_VHUB_DEV_ISR);
+
+ if (istat & VHUV_DEV_IRQ_EP0_IN_ACK_STALL)
+ ast_vhub_ep0_handle_ack(&d->ep0, true);
+ if (istat & VHUV_DEV_IRQ_EP0_OUT_ACK_STALL)
+ ast_vhub_ep0_handle_ack(&d->ep0, false);
+ if (istat & VHUV_DEV_IRQ_EP0_SETUP)
+ ast_vhub_ep0_handle_setup(&d->ep0);
+}
+
+static void ast_vhub_dev_enable(struct ast_vhub_dev *d)
+{
+ u32 reg, hmsk;
+
+ if (d->enabled)
+ return;
+
+ /* Enable device and its EP0 interrupts */
+ reg = VHUB_DEV_EN_ENABLE_PORT |
+ VHUB_DEV_EN_EP0_IN_ACK_IRQEN |
+ VHUB_DEV_EN_EP0_OUT_ACK_IRQEN |
+ VHUB_DEV_EN_EP0_SETUP_IRQEN;
+ if (d->gadget.speed == USB_SPEED_HIGH)
+ reg |= VHUB_DEV_EN_SPEED_SEL_HIGH;
+ writel(reg, d->regs + AST_VHUB_DEV_EN_CTRL);
+
+ /* Enable device interrupt in the hub as well */
+ hmsk = VHUB_IRQ_DEVICE1 << d->index;
+ reg = readl(d->vhub->regs + AST_VHUB_IER);
+ reg |= hmsk;
+ writel(reg, d->vhub->regs + AST_VHUB_IER);
+
+ /* Set EP0 DMA buffer address */
+ writel(d->ep0.buf_dma, d->regs + AST_VHUB_DEV_EP0_DATA);
+
+ d->enabled = true;
+}
+
+static void ast_vhub_dev_disable(struct ast_vhub_dev *d)
+{
+ u32 reg, hmsk;
+
+ if (!d->enabled)
+ return;
+
+ /* Disable device interrupt in the hub */
+ hmsk = VHUB_IRQ_DEVICE1 << d->index;
+ reg = readl(d->vhub->regs + AST_VHUB_IER);
+ reg &= ~hmsk;
+ writel(reg, d->vhub->regs + AST_VHUB_IER);
+
+ /* Then disable device */
+ writel(0, d->regs + AST_VHUB_DEV_EN_CTRL);
+ d->gadget.speed = USB_SPEED_UNKNOWN;
+ d->enabled = false;
+ d->suspended = false;
+}
+
+static int ast_vhub_dev_feature(struct ast_vhub_dev *d,
+ u16 wIndex, u16 wValue,
+ bool is_set)
+{
+ DDBG(d, "%s_FEATURE(dev val=%02x)\n",
+ is_set ? "SET" : "CLEAR", wValue);
+
+ if (wValue != USB_DEVICE_REMOTE_WAKEUP)
+ return std_req_driver;
+
+ d->wakeup_en = is_set;
+
+ return std_req_complete;
+}
+
+static int ast_vhub_ep_feature(struct ast_vhub_dev *d,
+ u16 wIndex, u16 wValue, bool is_set)
+{
+ struct ast_vhub_ep *ep;
+ int ep_num;
+
+ ep_num = wIndex & USB_ENDPOINT_NUMBER_MASK;
+ DDBG(d, "%s_FEATURE(ep%d val=%02x)\n",
+ is_set ? "SET" : "CLEAR", ep_num, wValue);
+ if (ep_num == 0)
+ return std_req_complete;
+ if (ep_num >= AST_VHUB_NUM_GEN_EPs || !d->epns[ep_num - 1])
+ return std_req_stall;
+ if (wValue != USB_ENDPOINT_HALT)
+ return std_req_driver;
+
+ ep = d->epns[ep_num - 1];
+ if (WARN_ON(!ep))
+ return std_req_stall;
+
+ if (!ep->epn.enabled || !ep->ep.desc || ep->epn.is_iso ||
+ ep->epn.is_in != !!(wIndex & USB_DIR_IN))
+ return std_req_stall;
+
+ DDBG(d, "%s stall on EP %d\n",
+ is_set ? "setting" : "clearing", ep_num);
+ ep->epn.stalled = is_set;
+ ast_vhub_update_epn_stall(ep);
+
+ return std_req_complete;
+}
+
+static int ast_vhub_dev_status(struct ast_vhub_dev *d,
+ u16 wIndex, u16 wValue)
+{
+ u8 st0;
+
+ DDBG(d, "GET_STATUS(dev)\n");
+
+ st0 = d->gadget.is_selfpowered << USB_DEVICE_SELF_POWERED;
+ if (d->wakeup_en)
+ st0 |= 1 << USB_DEVICE_REMOTE_WAKEUP;
+
+ return ast_vhub_simple_reply(&d->ep0, st0, 0);
+}
+
+static int ast_vhub_ep_status(struct ast_vhub_dev *d,
+ u16 wIndex, u16 wValue)
+{
+ int ep_num = wIndex & USB_ENDPOINT_NUMBER_MASK;
+ struct ast_vhub_ep *ep;
+ u8 st0 = 0;
+
+ DDBG(d, "GET_STATUS(ep%d)\n", ep_num);
+
+ if (ep_num >= AST_VHUB_NUM_GEN_EPs)
+ return std_req_stall;
+ if (ep_num != 0) {
+ ep = d->epns[ep_num - 1];
+ if (!ep)
+ return std_req_stall;
+ if (!ep->epn.enabled || !ep->ep.desc || ep->epn.is_iso ||
+ ep->epn.is_in != !!(wIndex & USB_DIR_IN))
+ return std_req_stall;
+ if (ep->epn.stalled)
+ st0 |= 1 << USB_ENDPOINT_HALT;
+ }
+
+ return ast_vhub_simple_reply(&d->ep0, st0, 0);
+}
+
+static void ast_vhub_dev_set_address(struct ast_vhub_dev *d, u8 addr)
+{
+ u32 reg;
+
+ DDBG(d, "SET_ADDRESS: Got address %x\n", addr);
+
+ reg = readl(d->regs + AST_VHUB_DEV_EN_CTRL);
+ reg &= ~VHUB_DEV_EN_ADDR_MASK;
+ reg |= VHUB_DEV_EN_SET_ADDR(addr);
+ writel(reg, d->regs + AST_VHUB_DEV_EN_CTRL);
+}
+
+int ast_vhub_std_dev_request(struct ast_vhub_ep *ep,
+ struct usb_ctrlrequest *crq)
+{
+ struct ast_vhub_dev *d = ep->dev;
+ u16 wValue, wIndex;
+
+ /* No driver, we shouldn't be enabled ... */
+ if (!d->driver || !d->enabled || d->suspended) {
+ EPDBG(ep,
+ "Device is wrong state driver=%p enabled=%d"
+ " suspended=%d\n",
+ d->driver, d->enabled, d->suspended);
+ return std_req_stall;
+ }
+
+ /* First packet, grab speed */
+ if (d->gadget.speed == USB_SPEED_UNKNOWN) {
+ d->gadget.speed = ep->vhub->speed;
+ if (d->gadget.speed > d->driver->max_speed)
+ d->gadget.speed = d->driver->max_speed;
+ DDBG(d, "fist packet, captured speed %d\n",
+ d->gadget.speed);
+ }
+
+ wValue = le16_to_cpu(crq->wValue);
+ wIndex = le16_to_cpu(crq->wIndex);
+
+ switch ((crq->bRequestType << 8) | crq->bRequest) {
+ /* SET_ADDRESS */
+ case DeviceOutRequest | USB_REQ_SET_ADDRESS:
+ ast_vhub_dev_set_address(d, wValue);
+ return std_req_complete;
+
+ /* GET_STATUS */
+ case DeviceRequest | USB_REQ_GET_STATUS:
+ return ast_vhub_dev_status(d, wIndex, wValue);
+ case InterfaceRequest | USB_REQ_GET_STATUS:
+ return ast_vhub_simple_reply(ep, 0, 0);
+ case EndpointRequest | USB_REQ_GET_STATUS:
+ return ast_vhub_ep_status(d, wIndex, wValue);
+
+ /* SET/CLEAR_FEATURE */
+ case DeviceOutRequest | USB_REQ_SET_FEATURE:
+ return ast_vhub_dev_feature(d, wIndex, wValue, true);
+ case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
+ return ast_vhub_dev_feature(d, wIndex, wValue, false);
+ case EndpointOutRequest | USB_REQ_SET_FEATURE:
+ return ast_vhub_ep_feature(d, wIndex, wValue, true);
+ case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
+ return ast_vhub_ep_feature(d, wIndex, wValue, false);
+ }
+ return std_req_driver;
+}
+
+static int ast_vhub_udc_wakeup(struct usb_gadget* gadget)
+{
+ struct ast_vhub_dev *d = to_ast_dev(gadget);
+ unsigned long flags;
+ int rc = -EINVAL;
+
+ spin_lock_irqsave(&d->vhub->lock, flags);
+ if (!d->wakeup_en)
+ goto err;
+
+ DDBG(d, "Device initiated wakeup\n");
+
+ /* Wakeup the host */
+ ast_vhub_hub_wake_all(d->vhub);
+ rc = 0;
+ err:
+ spin_unlock_irqrestore(&d->vhub->lock, flags);
+ return rc;
+}
+
+static int ast_vhub_udc_get_frame(struct usb_gadget* gadget)
+{
+ struct ast_vhub_dev *d = to_ast_dev(gadget);
+
+ return (readl(d->vhub->regs + AST_VHUB_USBSTS) >> 16) & 0x7ff;
+}
+
+static void ast_vhub_dev_nuke(struct ast_vhub_dev *d)
+{
+ unsigned int i;
+
+ for (i = 0; i < AST_VHUB_NUM_GEN_EPs; i++) {
+ if (!d->epns[i])
+ continue;
+ ast_vhub_nuke(d->epns[i], -ESHUTDOWN);
+ }
+}
+
+static int ast_vhub_udc_pullup(struct usb_gadget* gadget, int on)
+{
+ struct ast_vhub_dev *d = to_ast_dev(gadget);
+ unsigned long flags;
+
+ spin_lock_irqsave(&d->vhub->lock, flags);
+
+ DDBG(d, "pullup(%d)\n", on);
+
+ /* Mark disconnected in the hub */
+ ast_vhub_device_connect(d->vhub, d->index, on);
+
+ /*
+ * If enabled, nuke all requests if any (there shouldn't be)
+ * and disable the port. This will clear the address too.
+ */
+ if (d->enabled) {
+ ast_vhub_dev_nuke(d);
+ ast_vhub_dev_disable(d);
+ }
+
+ spin_unlock_irqrestore(&d->vhub->lock, flags);
+
+ return 0;
+}
+
+static int ast_vhub_udc_start(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
+{
+ struct ast_vhub_dev *d = to_ast_dev(gadget);
+ unsigned long flags;
+
+ spin_lock_irqsave(&d->vhub->lock, flags);
+
+ DDBG(d, "start\n");
+
+ /* We don't do much more until the hub enables us */
+ d->driver = driver;
+ d->gadget.is_selfpowered = 1;
+
+ spin_unlock_irqrestore(&d->vhub->lock, flags);
+
+ return 0;
+}
+
+static struct usb_ep *ast_vhub_udc_match_ep(struct usb_gadget *gadget,
+ struct usb_endpoint_descriptor *desc,
+ struct usb_ss_ep_comp_descriptor *ss)
+{
+ struct ast_vhub_dev *d = to_ast_dev(gadget);
+ struct ast_vhub_ep *ep;
+ struct usb_ep *u_ep;
+ unsigned int max, addr, i;
+
+ DDBG(d, "Match EP type %d\n", usb_endpoint_type(desc));
+
+ /*
+ * First we need to look for an existing unclaimed EP as another
+ * configuration may have already associated a bunch of EPs with
+ * this gadget. This duplicates the code in usb_ep_autoconfig_ss()
+ * unfortunately.
+ */
+ list_for_each_entry(u_ep, &gadget->ep_list, ep_list) {
+ if (usb_gadget_ep_match_desc(gadget, u_ep, desc, ss)) {
+ DDBG(d, " -> using existing EP%d\n",
+ to_ast_ep(u_ep)->d_idx);
+ return u_ep;
+ }
+ }
+
+ /*
+ * We didn't find one, we need to grab one from the pool.
+ *
+ * First let's do some sanity checking
+ */
+ switch(usb_endpoint_type(desc)) {
+ case USB_ENDPOINT_XFER_CONTROL:
+ /* Only EP0 can be a control endpoint */
+ return NULL;
+ case USB_ENDPOINT_XFER_ISOC:
+ /* ISO: limit 1023 bytes full speed, 1024 high/super speed */
+ if (gadget_is_dualspeed(gadget))
+ max = 1024;
+ else
+ max = 1023;
+ break;
+ case USB_ENDPOINT_XFER_BULK:
+ if (gadget_is_dualspeed(gadget))
+ max = 512;
+ else
+ max = 64;
+ break;
+ case USB_ENDPOINT_XFER_INT:
+ if (gadget_is_dualspeed(gadget))
+ max = 1024;
+ else
+ max = 64;
+ break;
+ }
+ if (usb_endpoint_maxp(desc) > max)
+ return NULL;
+
+ /*
+ * Find a free EP address for that device. We can't
+ * let the generic code assign these as it would
+ * create overlapping numbers for IN and OUT which
+ * we don't support, so also create a suitable name
+ * that will allow the generic code to use our
+ * assigned address.
+ */
+ for (i = 0; i < AST_VHUB_NUM_GEN_EPs; i++)
+ if (d->epns[i] == NULL)
+ break;
+ if (i >= AST_VHUB_NUM_GEN_EPs)
+ return NULL;
+ addr = i + 1;
+
+ /*
+ * Now grab an EP from the shared pool and associate
+ * it with our device
+ */
+ ep = ast_vhub_alloc_epn(d, addr);
+ if (!ep)
+ return NULL;
+ DDBG(d, "Allocated epn#%d for port EP%d\n",
+ ep->epn.g_idx, addr);
+
+ return &ep->ep;
+}
+
+static int ast_vhub_udc_stop(struct usb_gadget *gadget)
+{
+ struct ast_vhub_dev *d = to_ast_dev(gadget);
+ unsigned long flags;
+
+ spin_lock_irqsave(&d->vhub->lock, flags);
+
+ DDBG(d, "stop\n");
+
+ d->driver = NULL;
+ d->gadget.speed = USB_SPEED_UNKNOWN;
+
+ ast_vhub_dev_nuke(d);
+
+ if (d->enabled)
+ ast_vhub_dev_disable(d);
+
+ spin_unlock_irqrestore(&d->vhub->lock, flags);
+
+ return 0;
+}
+
+static struct usb_gadget_ops ast_vhub_udc_ops = {
+ .get_frame = ast_vhub_udc_get_frame,
+ .wakeup = ast_vhub_udc_wakeup,
+ .pullup = ast_vhub_udc_pullup,
+ .udc_start = ast_vhub_udc_start,
+ .udc_stop = ast_vhub_udc_stop,
+ .match_ep = ast_vhub_udc_match_ep,
+};
+
+void ast_vhub_dev_suspend(struct ast_vhub_dev *d)
+{
+ d->suspended = true;
+ if (d->driver) {
+ spin_unlock(&d->vhub->lock);
+ d->driver->suspend(&d->gadget);
+ spin_lock(&d->vhub->lock);
+ }
+}
+
+void ast_vhub_dev_resume(struct ast_vhub_dev *d)
+{
+ d->suspended = false;
+ if (d->driver) {
+ spin_unlock(&d->vhub->lock);
+ d->driver->resume(&d->gadget);
+ spin_lock(&d->vhub->lock);
+ }
+}
+
+void ast_vhub_dev_reset(struct ast_vhub_dev *d)
+{
+ /*
+ * If speed is not set, we enable the port. If it is,
+ * send reset to the gadget and reset "speed".
+ *
+ * Speed is an indication that we have got the first
+ * setup packet to the device.
+ */
+ if (d->gadget.speed == USB_SPEED_UNKNOWN && !d->enabled) {
+ DDBG(d, "Reset at unknown speed of disabled device, enabling...\n");
+ ast_vhub_dev_enable(d);
+ d->suspended = false;
+ }
+ if (d->gadget.speed != USB_SPEED_UNKNOWN && d->driver) {
+ unsigned int i;
+
+ DDBG(d, "Reset at known speed of bound device, resetting...\n");
+ spin_unlock(&d->vhub->lock);
+ d->driver->reset(&d->gadget);
+ spin_lock(&d->vhub->lock);
+
+ /*
+ * Disable/re-enable HW, this will clear the address
+ * and speed setting.
+ */
+ ast_vhub_dev_disable(d);
+ ast_vhub_dev_enable(d);
+
+ /* Clear stall on all EPs */
+ for (i = 0; i < AST_VHUB_NUM_GEN_EPs; i++) {
+ struct ast_vhub_ep *ep = d->epns[i];
+
+ if (ep && ep->epn.stalled) {
+ ep->epn.stalled = false;
+ ast_vhub_update_epn_stall(ep);
+ }
+ }
+
+ /* Additional cleanups */
+ d->wakeup_en = false;
+ d->suspended = false;
+ }
+}
+
+void ast_vhub_del_dev(struct ast_vhub_dev *d)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&d->vhub->lock, flags);
+ if (!d->registered) {
+ spin_unlock_irqrestore(&d->vhub->lock, flags);
+ return;
+ }
+ d->registered = false;
+ spin_unlock_irqrestore(&d->vhub->lock, flags);
+
+ usb_del_gadget_udc(&d->gadget);
+ device_unregister(d->port_dev);
+}
+
+static void ast_vhub_dev_release(struct device *dev)
+{
+ kfree(dev);
+}
+
+int ast_vhub_init_dev(struct ast_vhub *vhub, unsigned int idx)
+{
+ struct ast_vhub_dev *d = &vhub->ports[idx].dev;
+ struct device *parent = &vhub->pdev->dev;
+ int rc;
+
+ d->vhub = vhub;
+ d->index = idx;
+ d->name = devm_kasprintf(parent, GFP_KERNEL, "port%d", idx+1);
+ d->regs = vhub->regs + 0x100 + 0x10 * idx;
+
+ ast_vhub_init_ep0(vhub, &d->ep0, d);
+
+ /*
+ * The UDC core really needs us to have separate and uniquely
+ * 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);
+ if (!d->port_dev)
+ return -ENOMEM;
+ device_initialize(d->port_dev);
+ d->port_dev->release = ast_vhub_dev_release;
+ d->port_dev->parent = parent;
+ dev_set_name(d->port_dev, "%s:p%d", dev_name(parent), idx + 1);
+ rc = device_add(d->port_dev);
+ if (rc)
+ goto fail_add;
+
+ /* Populate gadget */
+ INIT_LIST_HEAD(&d->gadget.ep_list);
+ d->gadget.ops = &ast_vhub_udc_ops;
+ d->gadget.ep0 = &d->ep0.ep;
+ d->gadget.name = KBUILD_MODNAME;
+ if (vhub->force_usb1)
+ d->gadget.max_speed = USB_SPEED_FULL;
+ else
+ d->gadget.max_speed = USB_SPEED_HIGH;
+ d->gadget.speed = USB_SPEED_UNKNOWN;
+ d->gadget.dev.of_node = vhub->pdev->dev.of_node;
+
+ rc = usb_add_gadget_udc(d->port_dev, &d->gadget);
+ if (rc != 0)
+ goto fail_udc;
+ d->registered = true;
+
+ return 0;
+ fail_udc:
+ device_del(d->port_dev);
+ fail_add:
+ put_device(d->port_dev);
+
+ return rc;
+}
diff --git a/drivers/usb/gadget/udc/aspeed-vhub/ep0.c b/drivers/usb/gadget/udc/aspeed-vhub/ep0.c
new file mode 100644
index 000000000000..e2927fb083cf
--- /dev/null
+++ b/drivers/usb/gadget/udc/aspeed-vhub/ep0.c
@@ -0,0 +1,495 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * aspeed-vhub -- Driver for Aspeed SoC "vHub" USB gadget
+ *
+ * ep0.c - Endpoint 0 handling
+ *
+ * Copyright 2017 IBM Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/proc_fs.h>
+#include <linux/prefetch.h>
+#include <linux/clk.h>
+#include <linux/usb/gadget.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/regmap.h>
+#include <linux/dma-mapping.h>
+
+#include "vhub.h"
+
+int ast_vhub_reply(struct ast_vhub_ep *ep, char *ptr, int len)
+{
+ struct usb_request *req = &ep->ep0.req.req;
+ int rc;
+
+ if (WARN_ON(ep->d_idx != 0))
+ return std_req_stall;
+ if (WARN_ON(!ep->ep0.dir_in))
+ return std_req_stall;
+ if (WARN_ON(len > AST_VHUB_EP0_MAX_PACKET))
+ return std_req_stall;
+ if (WARN_ON(req->status == -EINPROGRESS))
+ return std_req_stall;
+
+ req->buf = ptr;
+ req->length = len;
+ req->complete = NULL;
+ req->zero = true;
+
+ /*
+ * Call internal queue directly after dropping the lock. This is
+ * safe to do as the reply is always the last thing done when
+ * processing a SETUP packet, usually as a tail call
+ */
+ spin_unlock(&ep->vhub->lock);
+ if (ep->ep.ops->queue(&ep->ep, req, GFP_ATOMIC))
+ rc = std_req_stall;
+ else
+ rc = std_req_data;
+ spin_lock(&ep->vhub->lock);
+ return rc;
+}
+
+int __ast_vhub_simple_reply(struct ast_vhub_ep *ep, int len, ...)
+{
+ u8 *buffer = ep->buf;
+ unsigned int i;
+ va_list args;
+
+ va_start(args, len);
+
+ /* Copy data directly into EP buffer */
+ for (i = 0; i < len; i++)
+ buffer[i] = va_arg(args, int);
+ va_end(args);
+
+ /* req->buf NULL means data is already there */
+ return ast_vhub_reply(ep, NULL, len);
+}
+
+void ast_vhub_ep0_handle_setup(struct ast_vhub_ep *ep)
+{
+ struct usb_ctrlrequest crq;
+ enum std_req_rc std_req_rc;
+ int rc = -ENODEV;
+
+ if (WARN_ON(ep->d_idx != 0))
+ return;
+
+ /*
+ * Grab the setup packet from the chip and byteswap
+ * interesting fields
+ */
+ memcpy_fromio(&crq, ep->ep0.setup, sizeof(crq));
+
+ EPDBG(ep, "SETUP packet %02x/%02x/%04x/%04x/%04x [%s] st=%d\n",
+ crq.bRequestType, crq.bRequest,
+ le16_to_cpu(crq.wValue),
+ le16_to_cpu(crq.wIndex),
+ le16_to_cpu(crq.wLength),
+ (crq.bRequestType & USB_DIR_IN) ? "in" : "out",
+ ep->ep0.state);
+
+ /* Check our state, cancel pending requests if needed */
+ if (ep->ep0.state != ep0_state_token) {
+ EPDBG(ep, "wrong state\n");
+ ast_vhub_nuke(ep, -EIO);
+
+ /*
+ * Accept the packet regardless, this seems to happen
+ * when stalling a SETUP packet that has an OUT data
+ * phase.
+ */
+ ast_vhub_nuke(ep, 0);
+ goto stall;
+ }
+
+ /* Calculate next state for EP0 */
+ ep->ep0.state = ep0_state_data;
+ ep->ep0.dir_in = !!(crq.bRequestType & USB_DIR_IN);
+
+ /* If this is the vHub, we handle requests differently */
+ std_req_rc = std_req_driver;
+ if (ep->dev == NULL) {
+ if ((crq.bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)
+ std_req_rc = ast_vhub_std_hub_request(ep, &crq);
+ else if ((crq.bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS)
+ std_req_rc = ast_vhub_class_hub_request(ep, &crq);
+ else
+ std_req_rc = std_req_stall;
+ } else if ((crq.bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)
+ std_req_rc = ast_vhub_std_dev_request(ep, &crq);
+
+ /* Act upon result */
+ switch(std_req_rc) {
+ case std_req_complete:
+ goto complete;
+ case std_req_stall:
+ goto stall;
+ case std_req_driver:
+ break;
+ case std_req_data:
+ return;
+ }
+
+ /* Pass request up to the gadget driver */
+ if (WARN_ON(!ep->dev))
+ goto stall;
+ if (ep->dev->driver) {
+ EPDBG(ep, "forwarding to gadget...\n");
+ spin_unlock(&ep->vhub->lock);
+ rc = ep->dev->driver->setup(&ep->dev->gadget, &crq);
+ spin_lock(&ep->vhub->lock);
+ EPDBG(ep, "driver returned %d\n", rc);
+ } else {
+ EPDBG(ep, "no gadget for request !\n");
+ }
+ if (rc >= 0)
+ return;
+
+ stall:
+ EPDBG(ep, "stalling\n");
+ writel(VHUB_EP0_CTRL_STALL, ep->ep0.ctlstat);
+ ep->ep0.state = ep0_state_status;
+ ep->ep0.dir_in = false;
+ return;
+
+ complete:
+ EPVDBG(ep, "sending [in] status with no data\n");
+ writel(VHUB_EP0_TX_BUFF_RDY, ep->ep0.ctlstat);
+ ep->ep0.state = ep0_state_status;
+ ep->ep0.dir_in = false;
+}
+
+
+static void ast_vhub_ep0_do_send(struct ast_vhub_ep *ep,
+ struct ast_vhub_req *req)
+{
+ unsigned int chunk;
+ u32 reg;
+
+ /* If this is a 0-length request, it's the gadget trying to
+ * send a status on our behalf. We take it from here.
+ */
+ if (req->req.length == 0)
+ req->last_desc = 1;
+
+ /* Are we done ? Complete request, otherwise wait for next interrupt */
+ if (req->last_desc >= 0) {
+ EPVDBG(ep, "complete send %d/%d\n",
+ req->req.actual, req->req.length);
+ ep->ep0.state = ep0_state_status;
+ writel(VHUB_EP0_RX_BUFF_RDY, ep->ep0.ctlstat);
+ ast_vhub_done(ep, req, 0);
+ return;
+ }
+
+ /*
+ * Next chunk cropped to max packet size. Also check if this
+ * is the last packet
+ */
+ chunk = req->req.length - req->req.actual;
+ if (chunk > ep->ep.maxpacket)
+ chunk = ep->ep.maxpacket;
+ else if ((chunk < ep->ep.maxpacket) || !req->req.zero)
+ req->last_desc = 1;
+
+ EPVDBG(ep, "send chunk=%d last=%d, req->act=%d mp=%d\n",
+ chunk, req->last_desc, req->req.actual, ep->ep.maxpacket);
+
+ /*
+ * Copy data if any (internal requests already have data
+ * in the EP buffer)
+ */
+ if (chunk && req->req.buf)
+ memcpy(ep->buf, req->req.buf + req->req.actual, chunk);
+
+ vhub_dma_workaround(ep->buf);
+
+ /* Remember chunk size and trigger send */
+ reg = VHUB_EP0_SET_TX_LEN(chunk);
+ writel(reg, ep->ep0.ctlstat);
+ writel(reg | VHUB_EP0_TX_BUFF_RDY, ep->ep0.ctlstat);
+ req->req.actual += chunk;
+}
+
+static void ast_vhub_ep0_rx_prime(struct ast_vhub_ep *ep)
+{
+ EPVDBG(ep, "rx prime\n");
+
+ /* Prime endpoint for receiving data */
+ writel(VHUB_EP0_RX_BUFF_RDY, ep->ep0.ctlstat);
+}
+
+static void ast_vhub_ep0_do_receive(struct ast_vhub_ep *ep, struct ast_vhub_req *req,
+ unsigned int len)
+{
+ unsigned int remain;
+ int rc = 0;
+
+ /* We are receiving... grab request */
+ remain = req->req.length - req->req.actual;
+
+ EPVDBG(ep, "receive got=%d remain=%d\n", len, remain);
+
+ /* Are we getting more than asked ? */
+ if (len > remain) {
+ EPDBG(ep, "receiving too much (ovf: %d) !\n",
+ len - remain);
+ len = remain;
+ rc = -EOVERFLOW;
+ }
+ if (len && req->req.buf)
+ memcpy(req->req.buf + req->req.actual, ep->buf, len);
+ req->req.actual += len;
+
+ /* Done ? */
+ if (len < ep->ep.maxpacket || len == remain) {
+ ep->ep0.state = ep0_state_status;
+ writel(VHUB_EP0_TX_BUFF_RDY, ep->ep0.ctlstat);
+ ast_vhub_done(ep, req, rc);
+ } else
+ ast_vhub_ep0_rx_prime(ep);
+}
+
+void ast_vhub_ep0_handle_ack(struct ast_vhub_ep *ep, bool in_ack)
+{
+ struct ast_vhub_req *req;
+ struct ast_vhub *vhub = ep->vhub;
+ struct device *dev = &vhub->pdev->dev;
+ bool stall = false;
+ u32 stat;
+
+ /* Read EP0 status */
+ stat = readl(ep->ep0.ctlstat);
+
+ /* Grab current request if any */
+ req = list_first_entry_or_null(&ep->queue, struct ast_vhub_req, queue);
+
+ EPVDBG(ep, "ACK status=%08x,state=%d is_in=%d in_ack=%d req=%p\n",
+ stat, ep->ep0.state, ep->ep0.dir_in, in_ack, req);
+
+ switch(ep->ep0.state) {
+ case ep0_state_token:
+ /* There should be no request queued in that state... */
+ if (req) {
+ dev_warn(dev, "request present while in TOKEN state\n");
+ ast_vhub_nuke(ep, -EINVAL);
+ }
+ dev_warn(dev, "ack while in TOKEN state\n");
+ stall = true;
+ break;
+ case ep0_state_data:
+ /* Check the state bits corresponding to our direction */
+ if ((ep->ep0.dir_in && (stat & VHUB_EP0_TX_BUFF_RDY)) ||
+ (!ep->ep0.dir_in && (stat & VHUB_EP0_RX_BUFF_RDY)) ||
+ (ep->ep0.dir_in != in_ack)) {
+ dev_warn(dev, "irq state mismatch");
+ stall = true;
+ break;
+ }
+ /*
+ * We are in data phase and there's no request, something is
+ * wrong, stall
+ */
+ if (!req) {
+ dev_warn(dev, "data phase, no request\n");
+ stall = true;
+ break;
+ }
+
+ /* We have a request, handle data transfers */
+ if (ep->ep0.dir_in)
+ ast_vhub_ep0_do_send(ep, req);
+ else
+ ast_vhub_ep0_do_receive(ep, req, VHUB_EP0_RX_LEN(stat));
+ return;
+ case ep0_state_status:
+ /* Nuke stale requests */
+ if (req) {
+ dev_warn(dev, "request present while in STATUS state\n");
+ ast_vhub_nuke(ep, -EINVAL);
+ }
+
+ /*
+ * If the status phase completes with the wrong ack, stall
+ * the endpoint just in case, to abort whatever the host
+ * was doing.
+ */
+ if (ep->ep0.dir_in == in_ack) {
+ dev_warn(dev, "status direction mismatch\n");
+ stall = true;
+ }
+ }
+
+ /* Reset to token state */
+ ep->ep0.state = ep0_state_token;
+ if (stall)
+ writel(VHUB_EP0_CTRL_STALL, ep->ep0.ctlstat);
+}
+
+static int ast_vhub_ep0_queue(struct usb_ep* u_ep, struct usb_request *u_req,
+ gfp_t gfp_flags)
+{
+ struct ast_vhub_req *req = to_ast_req(u_req);
+ struct ast_vhub_ep *ep = to_ast_ep(u_ep);
+ struct ast_vhub *vhub = ep->vhub;
+ struct device *dev = &vhub->pdev->dev;
+ unsigned long flags;
+
+ /* Paranoid cheks */
+ if (!u_req || (!u_req->complete && !req->internal)) {
+ dev_warn(dev, "Bogus EP0 request ! u_req=%p\n", u_req);
+ if (u_req) {
+ dev_warn(dev, "complete=%p internal=%d\n",
+ u_req->complete, req->internal);
+ }
+ return -EINVAL;
+ }
+
+ /* Not endpoint 0 ? */
+ if (WARN_ON(ep->d_idx != 0))
+ return -EINVAL;
+
+ /* Disabled device */
+ if (ep->dev && (!ep->dev->enabled || ep->dev->suspended))
+ return -ESHUTDOWN;
+
+ /* Data, no buffer and not internal ? */
+ if (u_req->length && !u_req->buf && !req->internal) {
+ dev_warn(dev, "Request with no buffer !\n");
+ return -EINVAL;
+ }
+
+ EPVDBG(ep, "enqueue req @%p\n", req);
+ EPVDBG(ep, " l=%d zero=%d noshort=%d is_in=%d\n",
+ u_req->length, u_req->zero,
+ u_req->short_not_ok, ep->ep0.dir_in);
+
+ /* Initialize request progress fields */
+ u_req->status = -EINPROGRESS;
+ u_req->actual = 0;
+ req->last_desc = -1;
+ req->active = false;
+
+ spin_lock_irqsave(&vhub->lock, flags);
+
+ /* EP0 can only support a single request at a time */
+ if (!list_empty(&ep->queue) || ep->ep0.state == ep0_state_token) {
+ dev_warn(dev, "EP0: Request in wrong state\n");
+ spin_unlock_irqrestore(&vhub->lock, flags);
+ return -EBUSY;
+ }
+
+ /* Add request to list and kick processing if empty */
+ list_add_tail(&req->queue, &ep->queue);
+
+ if (ep->ep0.dir_in) {
+ /* IN request, send data */
+ ast_vhub_ep0_do_send(ep, req);
+ } else if (u_req->length == 0) {
+ /* 0-len request, send completion as rx */
+ EPVDBG(ep, "0-length rx completion\n");
+ ep->ep0.state = ep0_state_status;
+ writel(VHUB_EP0_TX_BUFF_RDY, ep->ep0.ctlstat);
+ ast_vhub_done(ep, req, 0);
+ } else {
+ /* OUT request, start receiver */
+ ast_vhub_ep0_rx_prime(ep);
+ }
+
+ spin_unlock_irqrestore(&vhub->lock, flags);
+
+ return 0;
+}
+
+static int ast_vhub_ep0_dequeue(struct usb_ep* u_ep, struct usb_request *u_req)
+{
+ struct ast_vhub_ep *ep = to_ast_ep(u_ep);
+ struct ast_vhub *vhub = ep->vhub;
+ struct ast_vhub_req *req;
+ unsigned long flags;
+ int rc = -EINVAL;
+
+ spin_lock_irqsave(&vhub->lock, flags);
+
+ /* Only one request can be in the queue */
+ req = list_first_entry_or_null(&ep->queue, struct ast_vhub_req, queue);
+
+ /* Is it ours ? */
+ if (req && u_req == &req->req) {
+ EPVDBG(ep, "dequeue req @%p\n", req);
+
+ /*
+ * We don't have to deal with "active" as all
+ * DMAs go to the EP buffers, not the request.
+ */
+ ast_vhub_done(ep, req, -ECONNRESET);
+
+ /* We do stall the EP to clean things up in HW */
+ writel(VHUB_EP0_CTRL_STALL, ep->ep0.ctlstat);
+ ep->ep0.state = ep0_state_status;
+ ep->ep0.dir_in = false;
+ rc = 0;
+ }
+ spin_unlock_irqrestore(&vhub->lock, flags);
+ return rc;
+}
+
+
+static const struct usb_ep_ops ast_vhub_ep0_ops = {
+ .queue = ast_vhub_ep0_queue,
+ .dequeue = ast_vhub_ep0_dequeue,
+ .alloc_request = ast_vhub_alloc_request,
+ .free_request = ast_vhub_free_request,
+};
+
+void ast_vhub_init_ep0(struct ast_vhub *vhub, struct ast_vhub_ep *ep,
+ struct ast_vhub_dev *dev)
+{
+ memset(ep, 0, sizeof(*ep));
+
+ INIT_LIST_HEAD(&ep->ep.ep_list);
+ INIT_LIST_HEAD(&ep->queue);
+ ep->ep.ops = &ast_vhub_ep0_ops;
+ ep->ep.name = "ep0";
+ ep->ep.caps.type_control = true;
+ usb_ep_set_maxpacket_limit(&ep->ep, AST_VHUB_EP0_MAX_PACKET);
+ ep->d_idx = 0;
+ ep->dev = dev;
+ ep->vhub = vhub;
+ ep->ep0.state = ep0_state_token;
+ INIT_LIST_HEAD(&ep->ep0.req.queue);
+ ep->ep0.req.internal = true;
+
+ /* Small difference between vHub and devices */
+ if (dev) {
+ ep->ep0.ctlstat = dev->regs + AST_VHUB_DEV_EP0_CTRL;
+ ep->ep0.setup = vhub->regs +
+ AST_VHUB_SETUP0 + 8 * (dev->index + 1);
+ ep->buf = vhub->ep0_bufs +
+ AST_VHUB_EP0_MAX_PACKET * (dev->index + 1);
+ ep->buf_dma = vhub->ep0_bufs_dma +
+ AST_VHUB_EP0_MAX_PACKET * (dev->index + 1);
+ } else {
+ ep->ep0.ctlstat = vhub->regs + AST_VHUB_EP0_CTRL;
+ ep->ep0.setup = vhub->regs + AST_VHUB_SETUP0;
+ ep->buf = vhub->ep0_bufs;
+ ep->buf_dma = vhub->ep0_bufs_dma;
+ }
+}
diff --git a/drivers/usb/gadget/udc/aspeed-vhub/epn.c b/drivers/usb/gadget/udc/aspeed-vhub/epn.c
new file mode 100644
index 000000000000..4a28e3fbeb0b
--- /dev/null
+++ b/drivers/usb/gadget/udc/aspeed-vhub/epn.c
@@ -0,0 +1,851 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * aspeed-vhub -- Driver for Aspeed SoC "vHub" USB gadget
+ *
+ * epn.c - Generic endpoints management
+ *
+ * Copyright 2017 IBM Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/proc_fs.h>
+#include <linux/prefetch.h>
+#include <linux/clk.h>
+#include <linux/usb/gadget.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/regmap.h>
+#include <linux/dma-mapping.h>
+
+#include "vhub.h"
+
+#define EXTRA_CHECKS
+
+#ifdef EXTRA_CHECKS
+#define CHECK(ep, expr, fmt...) \
+ do { \
+ if (!(expr)) EPDBG(ep, "CHECK:" fmt); \
+ } while(0)
+#else
+#define CHECK(ep, expr, fmt...) do { } while(0)
+#endif
+
+static void ast_vhub_epn_kick(struct ast_vhub_ep *ep, struct ast_vhub_req *req)
+{
+ unsigned int act = req->req.actual;
+ unsigned int len = req->req.length;
+ unsigned int chunk;
+
+ /* There should be no DMA ongoing */
+ WARN_ON(req->active);
+
+ /* Calculate next chunk size */
+ chunk = len - act;
+ if (chunk > ep->ep.maxpacket)
+ chunk = ep->ep.maxpacket;
+ else if ((chunk < ep->ep.maxpacket) || !req->req.zero)
+ req->last_desc = 1;
+
+ EPVDBG(ep, "kick req %p act=%d/%d chunk=%d last=%d\n",
+ req, act, len, chunk, req->last_desc);
+
+ /* If DMA unavailable, using staging EP buffer */
+ if (!req->req.dma) {
+
+ /* For IN transfers, copy data over first */
+ if (ep->epn.is_in) {
+ memcpy(ep->buf, req->req.buf + act, chunk);
+ vhub_dma_workaround(ep->buf);
+ }
+ writel(ep->buf_dma, ep->epn.regs + AST_VHUB_EP_DESC_BASE);
+ } else {
+ if (ep->epn.is_in)
+ vhub_dma_workaround(req->req.buf);
+ writel(req->req.dma + act, ep->epn.regs + AST_VHUB_EP_DESC_BASE);
+ }
+
+ /* Start DMA */
+ req->active = true;
+ writel(VHUB_EP_DMA_SET_TX_SIZE(chunk),
+ ep->epn.regs + AST_VHUB_EP_DESC_STATUS);
+ writel(VHUB_EP_DMA_SET_TX_SIZE(chunk) | VHUB_EP_DMA_SINGLE_KICK,
+ ep->epn.regs + AST_VHUB_EP_DESC_STATUS);
+}
+
+static void ast_vhub_epn_handle_ack(struct ast_vhub_ep *ep)
+{
+ struct ast_vhub_req *req;
+ unsigned int len;
+ u32 stat;
+
+ /* Read EP status */
+ stat = readl(ep->epn.regs + AST_VHUB_EP_DESC_STATUS);
+
+ /* Grab current request if any */
+ req = list_first_entry_or_null(&ep->queue, struct ast_vhub_req, queue);
+
+ EPVDBG(ep, "ACK status=%08x is_in=%d, req=%p (active=%d)\n",
+ stat, ep->epn.is_in, req, req ? req->active : 0);
+
+ /* In absence of a request, bail out, must have been dequeued */
+ if (!req)
+ return;
+
+ /*
+ * Request not active, move on to processing queue, active request
+ * was probably dequeued
+ */
+ if (!req->active)
+ goto next_chunk;
+
+ /* Check if HW has moved on */
+ if (VHUB_EP_DMA_RPTR(stat) != 0) {
+ EPDBG(ep, "DMA read pointer not 0 !\n");
+ return;
+ }
+
+ /* No current DMA ongoing */
+ req->active = false;
+
+ /* Grab lenght out of HW */
+ len = VHUB_EP_DMA_TX_SIZE(stat);
+
+ /* If not using DMA, copy data out if needed */
+ if (!req->req.dma && !ep->epn.is_in && len)
+ memcpy(req->req.buf + req->req.actual, ep->buf, len);
+
+ /* Adjust size */
+ req->req.actual += len;
+
+ /* Check for short packet */
+ if (len < ep->ep.maxpacket)
+ req->last_desc = 1;
+
+ /* That's it ? complete the request and pick a new one */
+ if (req->last_desc >= 0) {
+ ast_vhub_done(ep, req, 0);
+ req = list_first_entry_or_null(&ep->queue, struct ast_vhub_req,
+ queue);
+
+ /*
+ * Due to lock dropping inside "done" the next request could
+ * already be active, so check for that and bail if needed.
+ */
+ if (!req || req->active)
+ return;
+ }
+
+ next_chunk:
+ ast_vhub_epn_kick(ep, req);
+}
+
+static inline unsigned int ast_vhub_count_free_descs(struct ast_vhub_ep *ep)
+{
+ /*
+ * d_next == d_last means descriptor list empty to HW,
+ * thus we can only have AST_VHUB_DESCS_COUNT-1 descriptors
+ * in the list
+ */
+ return (ep->epn.d_last + AST_VHUB_DESCS_COUNT - ep->epn.d_next - 1) &
+ (AST_VHUB_DESCS_COUNT - 1);
+}
+
+static void ast_vhub_epn_kick_desc(struct ast_vhub_ep *ep,
+ struct ast_vhub_req *req)
+{
+ struct ast_vhub_desc *desc = NULL;
+ unsigned int act = req->act_count;
+ unsigned int len = req->req.length;
+ unsigned int chunk;
+
+ /* Mark request active if not already */
+ req->active = true;
+
+ /* If the request was already completely written, do nothing */
+ if (req->last_desc >= 0)
+ return;
+
+ EPVDBG(ep, "kick act=%d/%d chunk_max=%d free_descs=%d\n",
+ act, len, ep->epn.chunk_max, ast_vhub_count_free_descs(ep));
+
+ /* While we can create descriptors */
+ while (ast_vhub_count_free_descs(ep) && req->last_desc < 0) {
+ unsigned int d_num;
+
+ /* Grab next free descriptor */
+ d_num = ep->epn.d_next;
+ desc = &ep->epn.descs[d_num];
+ ep->epn.d_next = (d_num + 1) & (AST_VHUB_DESCS_COUNT - 1);
+
+ /* Calculate next chunk size */
+ chunk = len - act;
+ if (chunk <= ep->epn.chunk_max) {
+ /*
+ * Is this the last packet ? Because of having up to 8
+ * packets in a descriptor we can't just compare "chunk"
+ * with ep.maxpacket. We have to see if it's a multiple
+ * of it to know if we have to send a zero packet.
+ * Sadly that involves a modulo which is a bit expensive
+ * but probably still better than not doing it.
+ */
+ if (!chunk || !req->req.zero || (chunk % ep->ep.maxpacket) != 0)
+ req->last_desc = d_num;
+ } else {
+ chunk = ep->epn.chunk_max;
+ }
+
+ EPVDBG(ep, " chunk: act=%d/%d chunk=%d last=%d desc=%d free=%d\n",
+ act, len, chunk, req->last_desc, d_num,
+ ast_vhub_count_free_descs(ep));
+
+ /* Populate descriptor */
+ desc->w0 = cpu_to_le32(req->req.dma + act);
+
+ /* Interrupt if end of request or no more descriptors */
+
+ /*
+ * TODO: Be smarter about it, if we don't have enough
+ * descriptors request an interrupt before queue empty
+ * or so in order to be able to populate more before
+ * the HW runs out. This isn't a problem at the moment
+ * as we use 256 descriptors and only put at most one
+ * request in the ring.
+ */
+ desc->w1 = cpu_to_le32(VHUB_DSC1_IN_SET_LEN(chunk));
+ if (req->last_desc >= 0 || !ast_vhub_count_free_descs(ep))
+ desc->w1 |= cpu_to_le32(VHUB_DSC1_IN_INTERRUPT);
+
+ /* Account packet */
+ req->act_count = act = act + chunk;
+ }
+
+ if (likely(desc))
+ vhub_dma_workaround(desc);
+
+ /* Tell HW about new descriptors */
+ writel(VHUB_EP_DMA_SET_CPU_WPTR(ep->epn.d_next),
+ ep->epn.regs + AST_VHUB_EP_DESC_STATUS);
+
+ EPVDBG(ep, "HW kicked, d_next=%d dstat=%08x\n",
+ ep->epn.d_next, readl(ep->epn.regs + AST_VHUB_EP_DESC_STATUS));
+}
+
+static void ast_vhub_epn_handle_ack_desc(struct ast_vhub_ep *ep)
+{
+ struct ast_vhub_req *req;
+ unsigned int len, d_last;
+ u32 stat, stat1;
+
+ /* Read EP status, workaround HW race */
+ do {
+ stat = readl(ep->epn.regs + AST_VHUB_EP_DESC_STATUS);
+ stat1 = readl(ep->epn.regs + AST_VHUB_EP_DESC_STATUS);
+ } while(stat != stat1);
+
+ /* Extract RPTR */
+ d_last = VHUB_EP_DMA_RPTR(stat);
+
+ /* Grab current request if any */
+ req = list_first_entry_or_null(&ep->queue, struct ast_vhub_req, queue);
+
+ EPVDBG(ep, "ACK status=%08x is_in=%d ep->d_last=%d..%d\n",
+ stat, ep->epn.is_in, ep->epn.d_last, d_last);
+
+ /* Check all completed descriptors */
+ while (ep->epn.d_last != d_last) {
+ struct ast_vhub_desc *desc;
+ unsigned int d_num;
+ bool is_last_desc;
+
+ /* Grab next completed descriptor */
+ d_num = ep->epn.d_last;
+ desc = &ep->epn.descs[d_num];
+ ep->epn.d_last = (d_num + 1) & (AST_VHUB_DESCS_COUNT - 1);
+
+ /* Grab len out of descriptor */
+ len = VHUB_DSC1_IN_LEN(le32_to_cpu(desc->w1));
+
+ EPVDBG(ep, " desc %d len=%d req=%p (act=%d)\n",
+ d_num, len, req, req ? req->active : 0);
+
+ /* If no active request pending, move on */
+ if (!req || !req->active)
+ continue;
+
+ /* Adjust size */
+ req->req.actual += len;
+
+ /* Is that the last chunk ? */
+ is_last_desc = req->last_desc == d_num;
+ CHECK(ep, is_last_desc == (len < ep->ep.maxpacket ||
+ (req->req.actual >= req->req.length &&
+ !req->req.zero)),
+ "Last packet discrepancy: last_desc=%d len=%d r.act=%d "
+ "r.len=%d r.zero=%d mp=%d\n",
+ is_last_desc, len, req->req.actual, req->req.length,
+ req->req.zero, ep->ep.maxpacket);
+
+ if (is_last_desc) {
+ /*
+ * Because we can only have one request at a time
+ * in our descriptor list in this implementation,
+ * d_last and ep->d_last should now be equal
+ */
+ CHECK(ep, d_last == ep->epn.d_last,
+ "DMA read ptr mismatch %d vs %d\n",
+ d_last, ep->epn.d_last);
+
+ /* Note: done will drop and re-acquire the lock */
+ ast_vhub_done(ep, req, 0);
+ req = list_first_entry_or_null(&ep->queue,
+ struct ast_vhub_req,
+ queue);
+ break;
+ }
+ }
+
+ /* More work ? */
+ if (req)
+ ast_vhub_epn_kick_desc(ep, req);
+}
+
+void ast_vhub_epn_ack_irq(struct ast_vhub_ep *ep)
+{
+ if (ep->epn.desc_mode)
+ ast_vhub_epn_handle_ack_desc(ep);
+ else
+ ast_vhub_epn_handle_ack(ep);
+}
+
+static int ast_vhub_epn_queue(struct usb_ep* u_ep, struct usb_request *u_req,
+ gfp_t gfp_flags)
+{
+ struct ast_vhub_req *req = to_ast_req(u_req);
+ struct ast_vhub_ep *ep = to_ast_ep(u_ep);
+ struct ast_vhub *vhub = ep->vhub;
+ unsigned long flags;
+ bool empty;
+ int rc;
+
+ /* Paranoid checks */
+ if (!u_req || !u_req->complete || !u_req->buf) {
+ dev_warn(&vhub->pdev->dev, "Bogus EPn request ! u_req=%p\n", u_req);
+ if (u_req) {
+ dev_warn(&vhub->pdev->dev, "complete=%p internal=%d\n",
+ u_req->complete, req->internal);
+ }
+ return -EINVAL;
+ }
+
+ /* Endpoint enabled ? */
+ if (!ep->epn.enabled || !u_ep->desc || !ep->dev || !ep->d_idx ||
+ !ep->dev->enabled || ep->dev->suspended) {
+ EPDBG(ep, "Enqueuing request on wrong or disabled EP\n");
+ return -ESHUTDOWN;
+ }
+
+ /* Map request for DMA if possible. For now, the rule for DMA is
+ * that:
+ *
+ * * For single stage mode (no descriptors):
+ *
+ * - The buffer is aligned to a 8 bytes boundary (HW requirement)
+ * - For a OUT endpoint, the request size is a multiple of the EP
+ * packet size (otherwise the controller will DMA past the end
+ * of the buffer if the host is sending a too long packet).
+ *
+ * * For descriptor mode (tx only for now), always.
+ *
+ * We could relax the latter by making the decision to use the bounce
+ * buffer based on the size of a given *segment* of the request rather
+ * than the whole request.
+ */
+ if (ep->epn.desc_mode ||
+ ((((unsigned long)u_req->buf & 7) == 0) &&
+ (ep->epn.is_in || !(u_req->length & (u_ep->maxpacket - 1))))) {
+ rc = usb_gadget_map_request(&ep->dev->gadget, u_req,
+ ep->epn.is_in);
+ if (rc) {
+ dev_warn(&vhub->pdev->dev,
+ "Request mapping failure %d\n", rc);
+ return rc;
+ }
+ } else
+ u_req->dma = 0;
+
+ EPVDBG(ep, "enqueue req @%p\n", req);
+ EPVDBG(ep, " l=%d dma=0x%x zero=%d noshort=%d noirq=%d is_in=%d\n",
+ u_req->length, (u32)u_req->dma, u_req->zero,
+ u_req->short_not_ok, u_req->no_interrupt,
+ ep->epn.is_in);
+
+ /* Initialize request progress fields */
+ u_req->status = -EINPROGRESS;
+ u_req->actual = 0;
+ req->act_count = 0;
+ req->active = false;
+ req->last_desc = -1;
+ spin_lock_irqsave(&vhub->lock, flags);
+ empty = list_empty(&ep->queue);
+
+ /* Add request to list and kick processing if empty */
+ list_add_tail(&req->queue, &ep->queue);
+ if (empty) {
+ if (ep->epn.desc_mode)
+ ast_vhub_epn_kick_desc(ep, req);
+ else
+ ast_vhub_epn_kick(ep, req);
+ }
+ spin_unlock_irqrestore(&vhub->lock, flags);
+
+ return 0;
+}
+
+static void ast_vhub_stop_active_req(struct ast_vhub_ep *ep,
+ bool restart_ep)
+{
+ u32 state, reg, loops;
+
+ /* Stop DMA activity */
+ writel(0, ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT);
+
+ /* Wait for it to complete */
+ for (loops = 0; loops < 1000; loops++) {
+ state = readl(ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT);
+ state = VHUB_EP_DMA_PROC_STATUS(state);
+ if (state == EP_DMA_PROC_RX_IDLE ||
+ state == EP_DMA_PROC_TX_IDLE)
+ break;
+ udelay(1);
+ }
+ if (loops >= 1000)
+ dev_warn(&ep->vhub->pdev->dev, "Timeout waiting for DMA\n");
+
+ /* If we don't have to restart the endpoint, that's it */
+ if (!restart_ep)
+ return;
+
+ /* Restart the endpoint */
+ if (ep->epn.desc_mode) {
+ /*
+ * Take out descriptors by resetting the DMA read
+ * pointer to be equal to the CPU write pointer.
+ *
+ * Note: If we ever support creating descriptors for
+ * requests that aren't the head of the queue, we
+ * may have to do something more complex here,
+ * especially if the request being taken out is
+ * not the current head descriptors.
+ */
+ reg = VHUB_EP_DMA_SET_RPTR(ep->epn.d_next) |
+ VHUB_EP_DMA_SET_CPU_WPTR(ep->epn.d_next);
+ writel(reg, ep->epn.regs + AST_VHUB_EP_DESC_STATUS);
+
+ /* Then turn it back on */
+ writel(ep->epn.dma_conf,
+ ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT);
+ } else {
+ /* Single mode: just turn it back on */
+ writel(ep->epn.dma_conf,
+ ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT);
+ }
+}
+
+static int ast_vhub_epn_dequeue(struct usb_ep* u_ep, struct usb_request *u_req)
+{
+ struct ast_vhub_ep *ep = to_ast_ep(u_ep);
+ struct ast_vhub *vhub = ep->vhub;
+ struct ast_vhub_req *req;
+ unsigned long flags;
+ int rc = -EINVAL;
+
+ spin_lock_irqsave(&vhub->lock, flags);
+
+ /* Make sure it's actually queued on this endpoint */
+ list_for_each_entry (req, &ep->queue, queue) {
+ if (&req->req == u_req)
+ break;
+ }
+
+ if (&req->req == u_req) {
+ EPVDBG(ep, "dequeue req @%p active=%d\n",
+ req, req->active);
+ if (req->active)
+ ast_vhub_stop_active_req(ep, true);
+ ast_vhub_done(ep, req, -ECONNRESET);
+ rc = 0;
+ }
+
+ spin_unlock_irqrestore(&vhub->lock, flags);
+ return rc;
+}
+
+void ast_vhub_update_epn_stall(struct ast_vhub_ep *ep)
+{
+ u32 reg;
+
+ if (WARN_ON(ep->d_idx == 0))
+ return;
+ reg = readl(ep->epn.regs + AST_VHUB_EP_CONFIG);
+ if (ep->epn.stalled || ep->epn.wedged)
+ reg |= VHUB_EP_CFG_STALL_CTRL;
+ else
+ reg &= ~VHUB_EP_CFG_STALL_CTRL;
+ writel(reg, ep->epn.regs + AST_VHUB_EP_CONFIG);
+
+ if (!ep->epn.stalled && !ep->epn.wedged)
+ writel(VHUB_EP_TOGGLE_SET_EPNUM(ep->epn.g_idx),
+ ep->vhub->regs + AST_VHUB_EP_TOGGLE);
+}
+
+static int ast_vhub_set_halt_and_wedge(struct usb_ep* u_ep, bool halt,
+ bool wedge)
+{
+ struct ast_vhub_ep *ep = to_ast_ep(u_ep);
+ struct ast_vhub *vhub = ep->vhub;
+ unsigned long flags;
+
+ EPDBG(ep, "Set halt (%d) & wedge (%d)\n", halt, wedge);
+
+ if (!u_ep || !u_ep->desc)
+ return -EINVAL;
+ if (ep->d_idx == 0)
+ return 0;
+ if (ep->epn.is_iso)
+ return -EOPNOTSUPP;
+
+ spin_lock_irqsave(&vhub->lock, flags);
+
+ /* Fail with still-busy IN endpoints */
+ if (halt && ep->epn.is_in && !list_empty(&ep->queue)) {
+ spin_unlock_irqrestore(&vhub->lock, flags);
+ return -EAGAIN;
+ }
+ ep->epn.stalled = halt;
+ ep->epn.wedged = wedge;
+ ast_vhub_update_epn_stall(ep);
+
+ spin_unlock_irqrestore(&vhub->lock, flags);
+
+ return 0;
+}
+
+static int ast_vhub_epn_set_halt(struct usb_ep *u_ep, int value)
+{
+ return ast_vhub_set_halt_and_wedge(u_ep, value != 0, false);
+}
+
+static int ast_vhub_epn_set_wedge(struct usb_ep *u_ep)
+{
+ return ast_vhub_set_halt_and_wedge(u_ep, true, true);
+}
+
+static int ast_vhub_epn_disable(struct usb_ep* u_ep)
+{
+ struct ast_vhub_ep *ep = to_ast_ep(u_ep);
+ struct ast_vhub *vhub = ep->vhub;
+ unsigned long flags;
+ u32 imask, ep_ier;
+
+ EPDBG(ep, "Disabling !\n");
+
+ spin_lock_irqsave(&vhub->lock, flags);
+
+ ep->epn.enabled = false;
+
+ /* Stop active DMA if any */
+ ast_vhub_stop_active_req(ep, false);
+
+ /* Disable endpoint */
+ writel(0, ep->epn.regs + AST_VHUB_EP_CONFIG);
+
+ /* Disable ACK interrupt */
+ imask = VHUB_EP_IRQ(ep->epn.g_idx);
+ ep_ier = readl(vhub->regs + AST_VHUB_EP_ACK_IER);
+ ep_ier &= ~imask;
+ writel(ep_ier, vhub->regs + AST_VHUB_EP_ACK_IER);
+ writel(imask, vhub->regs + AST_VHUB_EP_ACK_ISR);
+
+ /* Nuke all pending requests */
+ ast_vhub_nuke(ep, -ESHUTDOWN);
+
+ /* No more descriptor associated with request */
+ ep->ep.desc = NULL;
+
+ spin_unlock_irqrestore(&vhub->lock, flags);
+
+ return 0;
+}
+
+static int ast_vhub_epn_enable(struct usb_ep* u_ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ static const char *ep_type_string[] __maybe_unused = { "ctrl",
+ "isoc",
+ "bulk",
+ "intr" };
+ struct ast_vhub_ep *ep = to_ast_ep(u_ep);
+ struct ast_vhub_dev *dev;
+ struct ast_vhub *vhub;
+ u16 maxpacket, type;
+ unsigned long flags;
+ u32 ep_conf, ep_ier, imask;
+
+ /* Check arguments */
+ if (!u_ep || !desc)
+ return -EINVAL;
+
+ maxpacket = usb_endpoint_maxp(desc);
+ if (!ep->d_idx || !ep->dev ||
+ desc->bDescriptorType != USB_DT_ENDPOINT ||
+ maxpacket == 0 || maxpacket > ep->ep.maxpacket) {
+ EPDBG(ep, "Invalid EP enable,d_idx=%d,dev=%p,type=%d,mp=%d/%d\n",
+ ep->d_idx, ep->dev, desc->bDescriptorType,
+ maxpacket, ep->ep.maxpacket);
+ return -EINVAL;
+ }
+ if (ep->d_idx != usb_endpoint_num(desc)) {
+ EPDBG(ep, "EP number mismatch !\n");
+ return -EINVAL;
+ }
+
+ if (ep->epn.enabled) {
+ EPDBG(ep, "Already enabled\n");
+ return -EBUSY;
+ }
+ dev = ep->dev;
+ vhub = ep->vhub;
+
+ /* Check device state */
+ if (!dev->driver) {
+ EPDBG(ep, "Bogus device state: driver=%p speed=%d\n",
+ dev->driver, dev->gadget.speed);
+ return -ESHUTDOWN;
+ }
+
+ /* Grab some info from the descriptor */
+ ep->epn.is_in = usb_endpoint_dir_in(desc);
+ ep->ep.maxpacket = maxpacket;
+ type = usb_endpoint_type(desc);
+ ep->epn.d_next = ep->epn.d_last = 0;
+ ep->epn.is_iso = false;
+ ep->epn.stalled = false;
+ ep->epn.wedged = false;
+
+ EPDBG(ep, "Enabling [%s] %s num %d maxpacket=%d\n",
+ ep->epn.is_in ? "in" : "out", ep_type_string[type],
+ usb_endpoint_num(desc), maxpacket);
+
+ /* Can we use DMA descriptor mode ? */
+ ep->epn.desc_mode = ep->epn.descs && ep->epn.is_in;
+ if (ep->epn.desc_mode)
+ memset(ep->epn.descs, 0, 8 * AST_VHUB_DESCS_COUNT);
+
+ /*
+ * Large send function can send up to 8 packets from
+ * one descriptor with a limit of 4095 bytes.
+ */
+ ep->epn.chunk_max = ep->ep.maxpacket;
+ if (ep->epn.is_in) {
+ ep->epn.chunk_max <<= 3;
+ while (ep->epn.chunk_max > 4095)
+ ep->epn.chunk_max -= ep->ep.maxpacket;
+ }
+
+ switch(type) {
+ case USB_ENDPOINT_XFER_CONTROL:
+ EPDBG(ep, "Only one control endpoint\n");
+ return -EINVAL;
+ case USB_ENDPOINT_XFER_INT:
+ ep_conf = VHUB_EP_CFG_SET_TYPE(EP_TYPE_INT);
+ break;
+ case USB_ENDPOINT_XFER_BULK:
+ ep_conf = VHUB_EP_CFG_SET_TYPE(EP_TYPE_BULK);
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
+ ep_conf = VHUB_EP_CFG_SET_TYPE(EP_TYPE_ISO);
+ ep->epn.is_iso = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Encode the rest of the EP config register */
+ if (maxpacket < 1024)
+ ep_conf |= VHUB_EP_CFG_SET_MAX_PKT(maxpacket);
+ if (!ep->epn.is_in)
+ ep_conf |= VHUB_EP_CFG_DIR_OUT;
+ ep_conf |= VHUB_EP_CFG_SET_EP_NUM(usb_endpoint_num(desc));
+ ep_conf |= VHUB_EP_CFG_ENABLE;
+ ep_conf |= VHUB_EP_CFG_SET_DEV(dev->index + 1);
+ EPVDBG(ep, "config=%08x\n", ep_conf);
+
+ spin_lock_irqsave(&vhub->lock, flags);
+
+ /* Disable HW and reset DMA */
+ writel(0, ep->epn.regs + AST_VHUB_EP_CONFIG);
+ writel(VHUB_EP_DMA_CTRL_RESET,
+ ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT);
+
+ /* Configure and enable */
+ writel(ep_conf, ep->epn.regs + AST_VHUB_EP_CONFIG);
+
+ if (ep->epn.desc_mode) {
+ /* Clear DMA status, including the DMA read ptr */
+ writel(0, ep->epn.regs + AST_VHUB_EP_DESC_STATUS);
+
+ /* Set descriptor base */
+ writel(ep->epn.descs_dma,
+ ep->epn.regs + AST_VHUB_EP_DESC_BASE);
+
+ /* Set base DMA config value */
+ ep->epn.dma_conf = VHUB_EP_DMA_DESC_MODE;
+ if (ep->epn.is_in)
+ ep->epn.dma_conf |= VHUB_EP_DMA_IN_LONG_MODE;
+
+ /* First reset and disable all operations */
+ writel(ep->epn.dma_conf | VHUB_EP_DMA_CTRL_RESET,
+ ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT);
+
+ /* Enable descriptor mode */
+ writel(ep->epn.dma_conf,
+ ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT);
+ } else {
+ /* Set base DMA config value */
+ ep->epn.dma_conf = VHUB_EP_DMA_SINGLE_STAGE;
+
+ /* Reset and switch to single stage mode */
+ writel(ep->epn.dma_conf | VHUB_EP_DMA_CTRL_RESET,
+ ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT);
+ writel(ep->epn.dma_conf,
+ ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT);
+ writel(0, ep->epn.regs + AST_VHUB_EP_DESC_STATUS);
+ }
+
+ /* Cleanup data toggle just in case */
+ writel(VHUB_EP_TOGGLE_SET_EPNUM(ep->epn.g_idx),
+ vhub->regs + AST_VHUB_EP_TOGGLE);
+
+ /* Cleanup and enable ACK interrupt */
+ imask = VHUB_EP_IRQ(ep->epn.g_idx);
+ writel(imask, vhub->regs + AST_VHUB_EP_ACK_ISR);
+ ep_ier = readl(vhub->regs + AST_VHUB_EP_ACK_IER);
+ ep_ier |= imask;
+ writel(ep_ier, vhub->regs + AST_VHUB_EP_ACK_IER);
+
+ /* Woot, we are online ! */
+ ep->epn.enabled = true;
+
+ spin_unlock_irqrestore(&vhub->lock, flags);
+
+ return 0;
+}
+
+static void ast_vhub_epn_dispose(struct usb_ep *u_ep)
+{
+ struct ast_vhub_ep *ep = to_ast_ep(u_ep);
+
+ if (WARN_ON(!ep->dev || !ep->d_idx))
+ return;
+
+ EPDBG(ep, "Releasing endpoint\n");
+
+ /* Take it out of the EP list */
+ list_del_init(&ep->ep.ep_list);
+
+ /* Mark the address free in the device */
+ ep->dev->epns[ep->d_idx - 1] = NULL;
+
+ /* Free name & DMA buffers */
+ kfree(ep->ep.name);
+ ep->ep.name = NULL;
+ dma_free_coherent(&ep->vhub->pdev->dev,
+ AST_VHUB_EPn_MAX_PACKET +
+ 8 * AST_VHUB_DESCS_COUNT,
+ ep->buf, ep->buf_dma);
+ ep->buf = NULL;
+ ep->epn.descs = NULL;
+
+ /* Mark free */
+ ep->dev = NULL;
+}
+
+static const struct usb_ep_ops ast_vhub_epn_ops = {
+ .enable = ast_vhub_epn_enable,
+ .disable = ast_vhub_epn_disable,
+ .dispose = ast_vhub_epn_dispose,
+ .queue = ast_vhub_epn_queue,
+ .dequeue = ast_vhub_epn_dequeue,
+ .set_halt = ast_vhub_epn_set_halt,
+ .set_wedge = ast_vhub_epn_set_wedge,
+ .alloc_request = ast_vhub_alloc_request,
+ .free_request = ast_vhub_free_request,
+};
+
+struct ast_vhub_ep *ast_vhub_alloc_epn(struct ast_vhub_dev *d, u8 addr)
+{
+ struct ast_vhub *vhub = d->vhub;
+ struct ast_vhub_ep *ep;
+ unsigned long flags;
+ int i;
+
+ /* Find a free one (no device) */
+ spin_lock_irqsave(&vhub->lock, flags);
+ for (i = 0; i < AST_VHUB_NUM_GEN_EPs; i++)
+ if (vhub->epns[i].dev == NULL)
+ break;
+ if (i >= AST_VHUB_NUM_GEN_EPs) {
+ spin_unlock_irqrestore(&vhub->lock, flags);
+ return NULL;
+ }
+
+ /* Set it up */
+ ep = &vhub->epns[i];
+ ep->dev = d;
+ spin_unlock_irqrestore(&vhub->lock, flags);
+
+ DDBG(d, "Allocating gen EP %d for addr %d\n", i, addr);
+ INIT_LIST_HEAD(&ep->queue);
+ ep->d_idx = addr;
+ ep->vhub = vhub;
+ ep->ep.ops = &ast_vhub_epn_ops;
+ ep->ep.name = kasprintf(GFP_KERNEL, "ep%d", addr);
+ d->epns[addr-1] = ep;
+ ep->epn.g_idx = i;
+ ep->epn.regs = vhub->regs + 0x200 + (i * 0x10);
+
+ ep->buf = dma_alloc_coherent(&vhub->pdev->dev,
+ AST_VHUB_EPn_MAX_PACKET +
+ 8 * AST_VHUB_DESCS_COUNT,
+ &ep->buf_dma, GFP_KERNEL);
+ if (!ep->buf) {
+ kfree(ep->ep.name);
+ ep->ep.name = NULL;
+ return NULL;
+ }
+ ep->epn.descs = ep->buf + AST_VHUB_EPn_MAX_PACKET;
+ ep->epn.descs_dma = ep->buf_dma + AST_VHUB_EPn_MAX_PACKET;
+
+ usb_ep_set_maxpacket_limit(&ep->ep, AST_VHUB_EPn_MAX_PACKET);
+ list_add_tail(&ep->ep.ep_list, &d->gadget.ep_list);
+ 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;
+
+ return ep;
+}
diff --git a/drivers/usb/gadget/udc/aspeed-vhub/hub.c b/drivers/usb/gadget/udc/aspeed-vhub/hub.c
new file mode 100644
index 000000000000..35ba0e55a2e9
--- /dev/null
+++ b/drivers/usb/gadget/udc/aspeed-vhub/hub.c
@@ -0,0 +1,829 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * aspeed-vhub -- Driver for Aspeed SoC "vHub" USB gadget
+ *
+ * hub.c - virtual hub handling
+ *
+ * Copyright 2017 IBM Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/proc_fs.h>
+#include <linux/prefetch.h>
+#include <linux/clk.h>
+#include <linux/usb/gadget.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/regmap.h>
+#include <linux/dma-mapping.h>
+#include <linux/bcd.h>
+#include <linux/version.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+
+#include "vhub.h"
+
+/* usb 2.0 hub device descriptor
+ *
+ * A few things we may want to improve here:
+ *
+ * - We may need to indicate TT support
+ * - We may need a device qualifier descriptor
+ * as devices can pretend to be usb1 or 2
+ * - Make vid/did overridable
+ * - make it look like usb1 if usb1 mode forced
+ */
+#define KERNEL_REL bin2bcd(((LINUX_VERSION_CODE >> 16) & 0x0ff))
+#define KERNEL_VER bin2bcd(((LINUX_VERSION_CODE >> 8) & 0x0ff))
+
+enum {
+ AST_VHUB_STR_MANUF = 3,
+ AST_VHUB_STR_PRODUCT = 2,
+ AST_VHUB_STR_SERIAL = 1,
+};
+
+static const struct usb_device_descriptor ast_vhub_dev_desc = {
+ .bLength = USB_DT_DEVICE_SIZE,
+ .bDescriptorType = USB_DT_DEVICE,
+ .bcdUSB = cpu_to_le16(0x0200),
+ .bDeviceClass = USB_CLASS_HUB,
+ .bDeviceSubClass = 0,
+ .bDeviceProtocol = 1,
+ .bMaxPacketSize0 = 64,
+ .idVendor = cpu_to_le16(0x1d6b),
+ .idProduct = cpu_to_le16(0x0107),
+ .bcdDevice = cpu_to_le16(0x0100),
+ .iManufacturer = AST_VHUB_STR_MANUF,
+ .iProduct = AST_VHUB_STR_PRODUCT,
+ .iSerialNumber = AST_VHUB_STR_SERIAL,
+ .bNumConfigurations = 1,
+};
+
+/* Patches to the above when forcing USB1 mode */
+static void ast_vhub_patch_dev_desc_usb1(struct usb_device_descriptor *desc)
+{
+ desc->bcdUSB = cpu_to_le16(0x0100);
+ desc->bDeviceProtocol = 0;
+}
+
+/*
+ * Configuration descriptor: same comments as above
+ * regarding handling USB1 mode.
+ */
+
+/*
+ * We don't use sizeof() as Linux definition of
+ * struct usb_endpoint_descriptor contains 2
+ * extra bytes
+ */
+#define AST_VHUB_CONF_DESC_SIZE (USB_DT_CONFIG_SIZE + \
+ USB_DT_INTERFACE_SIZE + \
+ USB_DT_ENDPOINT_SIZE)
+
+static const struct ast_vhub_full_cdesc {
+ struct usb_config_descriptor cfg;
+ struct usb_interface_descriptor intf;
+ struct usb_endpoint_descriptor ep;
+} __attribute__ ((packed)) ast_vhub_conf_desc = {
+ .cfg = {
+ .bLength = USB_DT_CONFIG_SIZE,
+ .bDescriptorType = USB_DT_CONFIG,
+ .wTotalLength = cpu_to_le16(AST_VHUB_CONF_DESC_SIZE),
+ .bNumInterfaces = 1,
+ .bConfigurationValue = 1,
+ .iConfiguration = 0,
+ .bmAttributes = USB_CONFIG_ATT_ONE |
+ USB_CONFIG_ATT_SELFPOWER |
+ USB_CONFIG_ATT_WAKEUP,
+ .bMaxPower = 0,
+ },
+ .intf = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 1,
+ .bInterfaceClass = USB_CLASS_HUB,
+ .bInterfaceSubClass = 0,
+ .bInterfaceProtocol = 0,
+ .iInterface = 0,
+ },
+ .ep = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 0x81,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = cpu_to_le16(1),
+ .bInterval = 0x0c,
+ },
+};
+
+#define AST_VHUB_HUB_DESC_SIZE (USB_DT_HUB_NONVAR_SIZE + 2)
+
+static const struct usb_hub_descriptor ast_vhub_hub_desc = {
+ .bDescLength = AST_VHUB_HUB_DESC_SIZE,
+ .bDescriptorType = USB_DT_HUB,
+ .bNbrPorts = AST_VHUB_NUM_PORTS,
+ .wHubCharacteristics = cpu_to_le16(HUB_CHAR_NO_LPSM),
+ .bPwrOn2PwrGood = 10,
+ .bHubContrCurrent = 0,
+ .u.hs.DeviceRemovable[0] = 0,
+ .u.hs.DeviceRemovable[1] = 0xff,
+};
+
+/*
+ * These strings converted to UTF-16 must be smaller than
+ * our EP0 buffer.
+ */
+static const struct usb_string ast_vhub_str_array[] = {
+ {
+ .id = AST_VHUB_STR_SERIAL,
+ .s = "00000000"
+ },
+ {
+ .id = AST_VHUB_STR_PRODUCT,
+ .s = "USB Virtual Hub"
+ },
+ {
+ .id = AST_VHUB_STR_MANUF,
+ .s = "Aspeed"
+ },
+ { }
+};
+
+static const struct usb_gadget_strings ast_vhub_strings = {
+ .language = 0x0409,
+ .strings = (struct usb_string *)ast_vhub_str_array
+};
+
+static int ast_vhub_hub_dev_status(struct ast_vhub_ep *ep,
+ u16 wIndex, u16 wValue)
+{
+ u8 st0;
+
+ EPDBG(ep, "GET_STATUS(dev)\n");
+
+ /*
+ * Mark it as self-powered, I doubt the BMC is powered off
+ * the USB bus ...
+ */
+ st0 = 1 << USB_DEVICE_SELF_POWERED;
+
+ /*
+ * Need to double check how remote wakeup actually works
+ * on that chip and what triggers it.
+ */
+ if (ep->vhub->wakeup_en)
+ st0 |= 1 << USB_DEVICE_REMOTE_WAKEUP;
+
+ return ast_vhub_simple_reply(ep, st0, 0);
+}
+
+static int ast_vhub_hub_ep_status(struct ast_vhub_ep *ep,
+ u16 wIndex, u16 wValue)
+{
+ int ep_num;
+ u8 st0 = 0;
+
+ ep_num = wIndex & USB_ENDPOINT_NUMBER_MASK;
+ EPDBG(ep, "GET_STATUS(ep%d)\n", ep_num);
+
+ /* On the hub we have only EP 0 and 1 */
+ if (ep_num == 1) {
+ if (ep->vhub->ep1_stalled)
+ st0 |= 1 << USB_ENDPOINT_HALT;
+ } else if (ep_num != 0)
+ return std_req_stall;
+
+ return ast_vhub_simple_reply(ep, st0, 0);
+}
+
+static int ast_vhub_hub_dev_feature(struct ast_vhub_ep *ep,
+ u16 wIndex, u16 wValue,
+ bool is_set)
+{
+ EPDBG(ep, "%s_FEATURE(dev val=%02x)\n",
+ is_set ? "SET" : "CLEAR", wValue);
+
+ if (wValue != USB_DEVICE_REMOTE_WAKEUP)
+ return std_req_stall;
+
+ ep->vhub->wakeup_en = is_set;
+ EPDBG(ep, "Hub remote wakeup %s\n",
+ is_set ? "enabled" : "disabled");
+
+ return std_req_complete;
+}
+
+static int ast_vhub_hub_ep_feature(struct ast_vhub_ep *ep,
+ u16 wIndex, u16 wValue,
+ bool is_set)
+{
+ int ep_num;
+ u32 reg;
+
+ ep_num = wIndex & USB_ENDPOINT_NUMBER_MASK;
+ EPDBG(ep, "%s_FEATURE(ep%d val=%02x)\n",
+ is_set ? "SET" : "CLEAR", ep_num, wValue);
+
+ if (ep_num > 1)
+ return std_req_stall;
+ if (wValue != USB_ENDPOINT_HALT)
+ return std_req_stall;
+ if (ep_num == 0)
+ return std_req_complete;
+
+ EPDBG(ep, "%s stall on EP 1\n",
+ is_set ? "setting" : "clearing");
+
+ ep->vhub->ep1_stalled = is_set;
+ reg = readl(ep->vhub->regs + AST_VHUB_EP1_CTRL);
+ if (is_set) {
+ reg |= VHUB_EP1_CTRL_STALL;
+ } else {
+ reg &= ~VHUB_EP1_CTRL_STALL;
+ reg |= VHUB_EP1_CTRL_RESET_TOGGLE;
+ }
+ writel(reg, ep->vhub->regs + AST_VHUB_EP1_CTRL);
+
+ return std_req_complete;
+}
+
+static int ast_vhub_rep_desc(struct ast_vhub_ep *ep,
+ u8 desc_type, u16 len)
+{
+ size_t dsize;
+
+ EPDBG(ep, "GET_DESCRIPTOR(type:%d)\n", desc_type);
+
+ /*
+ * Copy first to EP buffer and send from there, so
+ * we can do some in-place patching if needed. We know
+ * the EP buffer is big enough but ensure that doesn't
+ * change. We do that now rather than later after we
+ * have checked sizes etc... to avoid a gcc bug where
+ * it thinks len is constant and barfs about read
+ * overflows in memcpy.
+ */
+ switch(desc_type) {
+ case USB_DT_DEVICE:
+ dsize = USB_DT_DEVICE_SIZE;
+ memcpy(ep->buf, &ast_vhub_dev_desc, dsize);
+ BUILD_BUG_ON(dsize > sizeof(ast_vhub_dev_desc));
+ BUILD_BUG_ON(USB_DT_DEVICE_SIZE >= AST_VHUB_EP0_MAX_PACKET);
+ break;
+ case USB_DT_CONFIG:
+ dsize = AST_VHUB_CONF_DESC_SIZE;
+ memcpy(ep->buf, &ast_vhub_conf_desc, dsize);
+ BUILD_BUG_ON(dsize > sizeof(ast_vhub_conf_desc));
+ BUILD_BUG_ON(AST_VHUB_CONF_DESC_SIZE >= AST_VHUB_EP0_MAX_PACKET);
+ break;
+ case USB_DT_HUB:
+ dsize = AST_VHUB_HUB_DESC_SIZE;
+ memcpy(ep->buf, &ast_vhub_hub_desc, dsize);
+ BUILD_BUG_ON(dsize > sizeof(ast_vhub_hub_desc));
+ BUILD_BUG_ON(AST_VHUB_HUB_DESC_SIZE >= AST_VHUB_EP0_MAX_PACKET);
+ break;
+ default:
+ return std_req_stall;
+ }
+
+ /* Crop requested length */
+ if (len > dsize)
+ len = dsize;
+
+ /* Patch it if forcing USB1 */
+ if (desc_type == USB_DT_DEVICE && ep->vhub->force_usb1)
+ ast_vhub_patch_dev_desc_usb1(ep->buf);
+
+ /* Shoot it from the EP buffer */
+ return ast_vhub_reply(ep, NULL, len);
+}
+
+static int ast_vhub_rep_string(struct ast_vhub_ep *ep,
+ u8 string_id, u16 lang_id,
+ u16 len)
+{
+ int rc = usb_gadget_get_string (&ast_vhub_strings, string_id, ep->buf);
+
+ /*
+ * This should never happen unless we put too big strings in
+ * the array above
+ */
+ BUG_ON(rc >= AST_VHUB_EP0_MAX_PACKET);
+
+ if (rc < 0)
+ return std_req_stall;
+
+ /* Shoot it from the EP buffer */
+ return ast_vhub_reply(ep, NULL, min_t(u16, rc, len));
+}
+
+enum std_req_rc ast_vhub_std_hub_request(struct ast_vhub_ep *ep,
+ struct usb_ctrlrequest *crq)
+{
+ struct ast_vhub *vhub = ep->vhub;
+ u16 wValue, wIndex, wLength;
+
+ wValue = le16_to_cpu(crq->wValue);
+ wIndex = le16_to_cpu(crq->wIndex);
+ wLength = le16_to_cpu(crq->wLength);
+
+ /* First packet, grab speed */
+ if (vhub->speed == USB_SPEED_UNKNOWN) {
+ u32 ustat = readl(vhub->regs + AST_VHUB_USBSTS);
+ if (ustat & VHUB_USBSTS_HISPEED)
+ vhub->speed = USB_SPEED_HIGH;
+ else
+ vhub->speed = USB_SPEED_FULL;
+ UDCDBG(vhub, "USB status=%08x speed=%s\n", ustat,
+ vhub->speed == USB_SPEED_HIGH ? "high" : "full");
+ }
+
+ switch ((crq->bRequestType << 8) | crq->bRequest) {
+ /* SET_ADDRESS */
+ case DeviceOutRequest | USB_REQ_SET_ADDRESS:
+ EPDBG(ep, "SET_ADDRESS: Got address %x\n", wValue);
+ writel(wValue, vhub->regs + AST_VHUB_CONF);
+ return std_req_complete;
+
+ /* GET_STATUS */
+ case DeviceRequest | USB_REQ_GET_STATUS:
+ return ast_vhub_hub_dev_status(ep, wIndex, wValue);
+ case InterfaceRequest | USB_REQ_GET_STATUS:
+ return ast_vhub_simple_reply(ep, 0, 0);
+ case EndpointRequest | USB_REQ_GET_STATUS:
+ return ast_vhub_hub_ep_status(ep, wIndex, wValue);
+
+ /* SET/CLEAR_FEATURE */
+ case DeviceOutRequest | USB_REQ_SET_FEATURE:
+ return ast_vhub_hub_dev_feature(ep, wIndex, wValue, true);
+ case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
+ return ast_vhub_hub_dev_feature(ep, wIndex, wValue, false);
+ case EndpointOutRequest | USB_REQ_SET_FEATURE:
+ return ast_vhub_hub_ep_feature(ep, wIndex, wValue, true);
+ case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
+ return ast_vhub_hub_ep_feature(ep, wIndex, wValue, false);
+
+ /* GET/SET_CONFIGURATION */
+ case DeviceRequest | USB_REQ_GET_CONFIGURATION:
+ return ast_vhub_simple_reply(ep, 1);
+ case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
+ if (wValue != 1)
+ return std_req_stall;
+ return std_req_complete;
+
+ /* GET_DESCRIPTOR */
+ case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
+ switch (wValue >> 8) {
+ case USB_DT_DEVICE:
+ case USB_DT_CONFIG:
+ return ast_vhub_rep_desc(ep, wValue >> 8,
+ wLength);
+ case USB_DT_STRING:
+ return ast_vhub_rep_string(ep, wValue & 0xff,
+ wIndex, wLength);
+ }
+ return std_req_stall;
+
+ /* GET/SET_INTERFACE */
+ case DeviceRequest | USB_REQ_GET_INTERFACE:
+ return ast_vhub_simple_reply(ep, 0);
+ case DeviceOutRequest | USB_REQ_SET_INTERFACE:
+ if (wValue != 0 || wIndex != 0)
+ return std_req_stall;
+ return std_req_complete;
+ }
+ return std_req_stall;
+}
+
+static void ast_vhub_update_hub_ep1(struct ast_vhub *vhub,
+ unsigned int port)
+{
+ /* Update HW EP1 response */
+ u32 reg = readl(vhub->regs + AST_VHUB_EP1_STS_CHG);
+ u32 pmask = (1 << (port + 1));
+ if (vhub->ports[port].change)
+ reg |= pmask;
+ else
+ reg &= ~pmask;
+ writel(reg, vhub->regs + AST_VHUB_EP1_STS_CHG);
+}
+
+static void ast_vhub_change_port_stat(struct ast_vhub *vhub,
+ unsigned int port,
+ u16 clr_flags,
+ u16 set_flags,
+ bool set_c)
+{
+ struct ast_vhub_port *p = &vhub->ports[port];
+ u16 prev;
+
+ /* Update port status */
+ prev = p->status;
+ p->status = (prev & ~clr_flags) | set_flags;
+ DDBG(&p->dev, "port %d status %04x -> %04x (C=%d)\n",
+ port + 1, prev, p->status, set_c);
+
+ /* Update change bits if needed */
+ if (set_c) {
+ u16 chg = p->status ^ prev;
+
+ /* Only these are relevant for change */
+ chg &= USB_PORT_STAT_C_CONNECTION |
+ USB_PORT_STAT_C_ENABLE |
+ USB_PORT_STAT_C_SUSPEND |
+ USB_PORT_STAT_C_OVERCURRENT |
+ USB_PORT_STAT_C_RESET |
+ USB_PORT_STAT_C_L1;
+ p->change |= chg;
+
+ ast_vhub_update_hub_ep1(vhub, port);
+ }
+}
+
+static void ast_vhub_send_host_wakeup(struct ast_vhub *vhub)
+{
+ u32 reg = readl(vhub->regs + AST_VHUB_CTRL);
+ UDCDBG(vhub, "Waking up host !\n");
+ reg |= VHUB_CTRL_MANUAL_REMOTE_WAKEUP;
+ writel(reg, vhub->regs + AST_VHUB_CTRL);
+}
+
+void ast_vhub_device_connect(struct ast_vhub *vhub,
+ unsigned int port, bool on)
+{
+ if (on)
+ ast_vhub_change_port_stat(vhub, port, 0,
+ USB_PORT_STAT_CONNECTION, true);
+ else
+ ast_vhub_change_port_stat(vhub, port,
+ USB_PORT_STAT_CONNECTION |
+ USB_PORT_STAT_ENABLE,
+ 0, true);
+
+ /*
+ * If the hub is set to wakup the host on connection events
+ * then send a wakeup.
+ */
+ if (vhub->wakeup_en)
+ ast_vhub_send_host_wakeup(vhub);
+}
+
+static void ast_vhub_wake_work(struct work_struct *work)
+{
+ struct ast_vhub *vhub = container_of(work,
+ struct ast_vhub,
+ wake_work);
+ unsigned long flags;
+ unsigned int i;
+
+ /*
+ * Wake all sleeping ports. If a port is suspended by
+ * the host suspend (without explicit state suspend),
+ * we let the normal host wake path deal with it later.
+ */
+ spin_lock_irqsave(&vhub->lock, flags);
+ for (i = 0; i < AST_VHUB_NUM_PORTS; i++) {
+ struct ast_vhub_port *p = &vhub->ports[i];
+
+ if (!(p->status & USB_PORT_STAT_SUSPEND))
+ continue;
+ ast_vhub_change_port_stat(vhub, i,
+ USB_PORT_STAT_SUSPEND,
+ 0, true);
+ ast_vhub_dev_resume(&p->dev);
+ }
+ ast_vhub_send_host_wakeup(vhub);
+ spin_unlock_irqrestore(&vhub->lock, flags);
+}
+
+void ast_vhub_hub_wake_all(struct ast_vhub *vhub)
+{
+ /*
+ * A device is trying to wake the world, because this
+ * can recurse into the device, we break the call chain
+ * using a work queue
+ */
+ schedule_work(&vhub->wake_work);
+}
+
+static void ast_vhub_port_reset(struct ast_vhub *vhub, u8 port)
+{
+ struct ast_vhub_port *p = &vhub->ports[port];
+ u16 set, clr, speed;
+
+ /* First mark disabled */
+ ast_vhub_change_port_stat(vhub, port,
+ USB_PORT_STAT_ENABLE |
+ USB_PORT_STAT_SUSPEND,
+ USB_PORT_STAT_RESET,
+ false);
+
+ if (!p->dev.driver)
+ return;
+
+ /*
+ * This will either "start" the port or reset the
+ * device if already started...
+ */
+ ast_vhub_dev_reset(&p->dev);
+
+ /* Grab the right speed */
+ speed = p->dev.driver->max_speed;
+ if (speed == USB_SPEED_UNKNOWN || speed > vhub->speed)
+ speed = vhub->speed;
+
+ switch (speed) {
+ case USB_SPEED_LOW:
+ set = USB_PORT_STAT_LOW_SPEED;
+ clr = USB_PORT_STAT_HIGH_SPEED;
+ break;
+ case USB_SPEED_FULL:
+ set = 0;
+ clr = USB_PORT_STAT_LOW_SPEED |
+ USB_PORT_STAT_HIGH_SPEED;
+ break;
+ case USB_SPEED_HIGH:
+ set = USB_PORT_STAT_HIGH_SPEED;
+ clr = USB_PORT_STAT_LOW_SPEED;
+ break;
+ default:
+ UDCDBG(vhub, "Unsupported speed %d when"
+ " connecting device\n",
+ speed);
+ return;
+ }
+ clr |= USB_PORT_STAT_RESET;
+ set |= USB_PORT_STAT_ENABLE;
+
+ /* This should ideally be delayed ... */
+ ast_vhub_change_port_stat(vhub, port, clr, set, true);
+}
+
+static enum std_req_rc ast_vhub_set_port_feature(struct ast_vhub_ep *ep,
+ u8 port, u16 feat)
+{
+ struct ast_vhub *vhub = ep->vhub;
+ struct ast_vhub_port *p;
+
+ if (port == 0 || port > AST_VHUB_NUM_PORTS)
+ return std_req_stall;
+ port--;
+ p = &vhub->ports[port];
+
+ switch(feat) {
+ case USB_PORT_FEAT_SUSPEND:
+ if (!(p->status & USB_PORT_STAT_ENABLE))
+ return std_req_complete;
+ ast_vhub_change_port_stat(vhub, port,
+ 0, USB_PORT_STAT_SUSPEND,
+ false);
+ ast_vhub_dev_suspend(&p->dev);
+ return std_req_complete;
+ case USB_PORT_FEAT_RESET:
+ EPDBG(ep, "Port reset !\n");
+ ast_vhub_port_reset(vhub, port);
+ return std_req_complete;
+ case USB_PORT_FEAT_POWER:
+ /*
+ * On Power-on, we mark the connected flag changed,
+ * if there's a connected device, some hosts will
+ * otherwise fail to detect it.
+ */
+ if (p->status & USB_PORT_STAT_CONNECTION) {
+ p->change |= USB_PORT_STAT_C_CONNECTION;
+ ast_vhub_update_hub_ep1(vhub, port);
+ }
+ return std_req_complete;
+ case USB_PORT_FEAT_TEST:
+ case USB_PORT_FEAT_INDICATOR:
+ /* We don't do anything with these */
+ return std_req_complete;
+ }
+ return std_req_stall;
+}
+
+static enum std_req_rc ast_vhub_clr_port_feature(struct ast_vhub_ep *ep,
+ u8 port, u16 feat)
+{
+ struct ast_vhub *vhub = ep->vhub;
+ struct ast_vhub_port *p;
+
+ if (port == 0 || port > AST_VHUB_NUM_PORTS)
+ return std_req_stall;
+ port--;
+ p = &vhub->ports[port];
+
+ switch(feat) {
+ case USB_PORT_FEAT_ENABLE:
+ ast_vhub_change_port_stat(vhub, port,
+ USB_PORT_STAT_ENABLE |
+ USB_PORT_STAT_SUSPEND, 0,
+ false);
+ ast_vhub_dev_suspend(&p->dev);
+ return std_req_complete;
+ case USB_PORT_FEAT_SUSPEND:
+ if (!(p->status & USB_PORT_STAT_SUSPEND))
+ return std_req_complete;
+ ast_vhub_change_port_stat(vhub, port,
+ USB_PORT_STAT_SUSPEND, 0,
+ false);
+ ast_vhub_dev_resume(&p->dev);
+ return std_req_complete;
+ case USB_PORT_FEAT_POWER:
+ /* We don't do power control */
+ return std_req_complete;
+ case USB_PORT_FEAT_INDICATOR:
+ /* We don't have indicators */
+ return std_req_complete;
+ case USB_PORT_FEAT_C_CONNECTION:
+ case USB_PORT_FEAT_C_ENABLE:
+ case USB_PORT_FEAT_C_SUSPEND:
+ case USB_PORT_FEAT_C_OVER_CURRENT:
+ case USB_PORT_FEAT_C_RESET:
+ /* Clear state-change feature */
+ p->change &= ~(1u << (feat - 16));
+ ast_vhub_update_hub_ep1(vhub, port);
+ return std_req_complete;
+ }
+ return std_req_stall;
+}
+
+static enum std_req_rc ast_vhub_get_port_stat(struct ast_vhub_ep *ep,
+ u8 port)
+{
+ struct ast_vhub *vhub = ep->vhub;
+ u16 stat, chg;
+
+ if (port == 0 || port > AST_VHUB_NUM_PORTS)
+ return std_req_stall;
+ port--;
+
+ stat = vhub->ports[port].status;
+ chg = vhub->ports[port].change;
+
+ /* We always have power */
+ stat |= USB_PORT_STAT_POWER;
+
+ EPDBG(ep, " port status=%04x change=%04x\n", stat, chg);
+
+ return ast_vhub_simple_reply(ep,
+ stat & 0xff,
+ stat >> 8,
+ chg & 0xff,
+ chg >> 8);
+}
+
+enum std_req_rc ast_vhub_class_hub_request(struct ast_vhub_ep *ep,
+ struct usb_ctrlrequest *crq)
+{
+ u16 wValue, wIndex, wLength;
+
+ wValue = le16_to_cpu(crq->wValue);
+ wIndex = le16_to_cpu(crq->wIndex);
+ wLength = le16_to_cpu(crq->wLength);
+
+ switch ((crq->bRequestType << 8) | crq->bRequest) {
+ case GetHubStatus:
+ EPDBG(ep, "GetHubStatus\n");
+ return ast_vhub_simple_reply(ep, 0, 0, 0, 0);
+ case GetPortStatus:
+ EPDBG(ep, "GetPortStatus(%d)\n", wIndex & 0xff);
+ return ast_vhub_get_port_stat(ep, wIndex & 0xf);
+ case GetHubDescriptor:
+ if (wValue != (USB_DT_HUB << 8))
+ return std_req_stall;
+ EPDBG(ep, "GetHubDescriptor(%d)\n", wIndex & 0xff);
+ return ast_vhub_rep_desc(ep, USB_DT_HUB, wLength);
+ case SetHubFeature:
+ case ClearHubFeature:
+ EPDBG(ep, "Get/SetHubFeature(%d)\n", wValue);
+ /* No feature, just complete the requests */
+ if (wValue == C_HUB_LOCAL_POWER ||
+ wValue == C_HUB_OVER_CURRENT)
+ return std_req_complete;
+ return std_req_stall;
+ case SetPortFeature:
+ EPDBG(ep, "SetPortFeature(%d,%d)\n", wIndex & 0xf, wValue);
+ return ast_vhub_set_port_feature(ep, wIndex & 0xf, wValue);
+ case ClearPortFeature:
+ EPDBG(ep, "ClearPortFeature(%d,%d)\n", wIndex & 0xf, wValue);
+ return ast_vhub_clr_port_feature(ep, wIndex & 0xf, wValue);
+ default:
+ EPDBG(ep, "Unknown class request\n");
+ }
+ return std_req_stall;
+}
+
+void ast_vhub_hub_suspend(struct ast_vhub *vhub)
+{
+ unsigned int i;
+
+ UDCDBG(vhub, "USB bus suspend\n");
+
+ if (vhub->suspended)
+ return;
+
+ vhub->suspended = true;
+
+ /*
+ * Forward to unsuspended ports without changing
+ * their connection status.
+ */
+ for (i = 0; i < AST_VHUB_NUM_PORTS; i++) {
+ struct ast_vhub_port *p = &vhub->ports[i];
+
+ if (!(p->status & USB_PORT_STAT_SUSPEND))
+ ast_vhub_dev_suspend(&p->dev);
+ }
+}
+
+void ast_vhub_hub_resume(struct ast_vhub *vhub)
+{
+ unsigned int i;
+
+ UDCDBG(vhub, "USB bus resume\n");
+
+ if (!vhub->suspended)
+ return;
+
+ vhub->suspended = false;
+
+ /*
+ * Forward to unsuspended ports without changing
+ * their connection status.
+ */
+ for (i = 0; i < AST_VHUB_NUM_PORTS; i++) {
+ struct ast_vhub_port *p = &vhub->ports[i];
+
+ if (!(p->status & USB_PORT_STAT_SUSPEND))
+ ast_vhub_dev_resume(&p->dev);
+ }
+}
+
+void ast_vhub_hub_reset(struct ast_vhub *vhub)
+{
+ unsigned int i;
+
+ UDCDBG(vhub, "USB bus reset\n");
+
+ /*
+ * Is the speed known ? If not we don't care, we aren't
+ * initialized yet and ports haven't been enabled.
+ */
+ if (vhub->speed == USB_SPEED_UNKNOWN)
+ return;
+
+ /* We aren't suspended anymore obviously */
+ vhub->suspended = false;
+
+ /* No speed set */
+ vhub->speed = USB_SPEED_UNKNOWN;
+
+ /* Wakeup not enabled anymore */
+ vhub->wakeup_en = false;
+
+ /*
+ * Clear all port status, disable gadgets and "suspend"
+ * them. They will be woken up by a port reset.
+ */
+ for (i = 0; i < AST_VHUB_NUM_PORTS; i++) {
+ struct ast_vhub_port *p = &vhub->ports[i];
+
+ /* Only keep the connected flag */
+ p->status &= USB_PORT_STAT_CONNECTION;
+ p->change = 0;
+
+ /* Suspend the gadget if any */
+ ast_vhub_dev_suspend(&p->dev);
+ }
+
+ /* Cleanup HW */
+ writel(0, vhub->regs + AST_VHUB_CONF);
+ writel(0, vhub->regs + AST_VHUB_EP0_CTRL);
+ writel(VHUB_EP1_CTRL_RESET_TOGGLE |
+ VHUB_EP1_CTRL_ENABLE,
+ vhub->regs + AST_VHUB_EP1_CTRL);
+ writel(0, vhub->regs + AST_VHUB_EP1_STS_CHG);
+}
+
+void ast_vhub_init_hub(struct ast_vhub *vhub)
+{
+ vhub->speed = USB_SPEED_UNKNOWN;
+ INIT_WORK(&vhub->wake_work, ast_vhub_wake_work);
+}
+
diff --git a/drivers/usb/gadget/udc/aspeed-vhub/vhub.h b/drivers/usb/gadget/udc/aspeed-vhub/vhub.h
new file mode 100644
index 000000000000..4ed03d33a5a9
--- /dev/null
+++ b/drivers/usb/gadget/udc/aspeed-vhub/vhub.h
@@ -0,0 +1,547 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+#ifndef __ASPEED_VHUB_H
+#define __ASPEED_VHUB_H
+
+/*****************************
+ * *
+ * VHUB register definitions *
+ * *
+ *****************************/
+
+#define AST_VHUB_CTRL 0x00 /* Root Function Control & Status Register */
+#define AST_VHUB_CONF 0x04 /* Root Configuration Setting Register */
+#define AST_VHUB_IER 0x08 /* Interrupt Ctrl Register */
+#define AST_VHUB_ISR 0x0C /* Interrupt Status Register */
+#define AST_VHUB_EP_ACK_IER 0x10 /* Programmable Endpoint Pool ACK Interrupt Enable Register */
+#define AST_VHUB_EP_NACK_IER 0x14 /* Programmable Endpoint Pool NACK Interrupt Enable Register */
+#define AST_VHUB_EP_ACK_ISR 0x18 /* Programmable Endpoint Pool ACK Interrupt Status Register */
+#define AST_VHUB_EP_NACK_ISR 0x1C /* Programmable Endpoint Pool NACK Interrupt Status Register */
+#define AST_VHUB_SW_RESET 0x20 /* Device Controller Soft Reset Enable Register */
+#define AST_VHUB_USBSTS 0x24 /* USB Status Register */
+#define AST_VHUB_EP_TOGGLE 0x28 /* Programmable Endpoint Pool Data Toggle Value Set */
+#define AST_VHUB_ISO_FAIL_ACC 0x2C /* Isochronous Transaction Fail Accumulator */
+#define AST_VHUB_EP0_CTRL 0x30 /* Endpoint 0 Contrl/Status Register */
+#define AST_VHUB_EP0_DATA 0x34 /* Base Address of Endpoint 0 In/OUT Data Buffer Register */
+#define AST_VHUB_EP1_CTRL 0x38 /* Endpoint 1 Contrl/Status Register */
+#define AST_VHUB_EP1_STS_CHG 0x3C /* Endpoint 1 Status Change Bitmap Data */
+#define AST_VHUB_SETUP0 0x80 /* Root Device Setup Data Buffer0 */
+#define AST_VHUB_SETUP1 0x84 /* Root Device Setup Data Buffer1 */
+
+/* Main control reg */
+#define VHUB_CTRL_PHY_CLK (1 << 31)
+#define VHUB_CTRL_PHY_LOOP_TEST (1 << 25)
+#define VHUB_CTRL_DN_PWN (1 << 24)
+#define VHUB_CTRL_DP_PWN (1 << 23)
+#define VHUB_CTRL_LONG_DESC (1 << 18)
+#define VHUB_CTRL_ISO_RSP_CTRL (1 << 17)
+#define VHUB_CTRL_SPLIT_IN (1 << 16)
+#define VHUB_CTRL_LOOP_T_RESULT (1 << 15)
+#define VHUB_CTRL_LOOP_T_STS (1 << 14)
+#define VHUB_CTRL_PHY_BIST_RESULT (1 << 13)
+#define VHUB_CTRL_PHY_BIST_CTRL (1 << 12)
+#define VHUB_CTRL_PHY_RESET_DIS (1 << 11)
+#define VHUB_CTRL_SET_TEST_MODE(x) ((x) << 8)
+#define VHUB_CTRL_MANUAL_REMOTE_WAKEUP (1 << 4)
+#define VHUB_CTRL_AUTO_REMOTE_WAKEUP (1 << 3)
+#define VHUB_CTRL_CLK_STOP_SUSPEND (1 << 2)
+#define VHUB_CTRL_FULL_SPEED_ONLY (1 << 1)
+#define VHUB_CTRL_UPSTREAM_CONNECT (1 << 0)
+
+/* IER & ISR */
+#define VHUB_IRQ_USB_CMD_DEADLOCK (1 << 18)
+#define VHUB_IRQ_EP_POOL_NAK (1 << 17)
+#define VHUB_IRQ_EP_POOL_ACK_STALL (1 << 16)
+#define VHUB_IRQ_DEVICE5 (1 << 13)
+#define VHUB_IRQ_DEVICE4 (1 << 12)
+#define VHUB_IRQ_DEVICE3 (1 << 11)
+#define VHUB_IRQ_DEVICE2 (1 << 10)
+#define VHUB_IRQ_DEVICE1 (1 << 9)
+#define VHUB_IRQ_BUS_RESUME (1 << 8)
+#define VHUB_IRQ_BUS_SUSPEND (1 << 7)
+#define VHUB_IRQ_BUS_RESET (1 << 6)
+#define VHUB_IRQ_HUB_EP1_IN_DATA_ACK (1 << 5)
+#define VHUB_IRQ_HUB_EP0_IN_DATA_NAK (1 << 4)
+#define VHUB_IRQ_HUB_EP0_IN_ACK_STALL (1 << 3)
+#define VHUB_IRQ_HUB_EP0_OUT_NAK (1 << 2)
+#define VHUB_IRQ_HUB_EP0_OUT_ACK_STALL (1 << 1)
+#define VHUB_IRQ_HUB_EP0_SETUP (1 << 0)
+#define VHUB_IRQ_ACK_ALL 0x1ff
+
+/* SW reset reg */
+#define VHUB_SW_RESET_EP_POOL (1 << 9)
+#define VHUB_SW_RESET_DMA_CONTROLLER (1 << 8)
+#define VHUB_SW_RESET_DEVICE5 (1 << 5)
+#define VHUB_SW_RESET_DEVICE4 (1 << 4)
+#define VHUB_SW_RESET_DEVICE3 (1 << 3)
+#define VHUB_SW_RESET_DEVICE2 (1 << 2)
+#define VHUB_SW_RESET_DEVICE1 (1 << 1)
+#define VHUB_SW_RESET_ROOT_HUB (1 << 0)
+#define VHUB_SW_RESET_ALL (VHUB_SW_RESET_EP_POOL | \
+ VHUB_SW_RESET_DMA_CONTROLLER | \
+ VHUB_SW_RESET_DEVICE5 | \
+ VHUB_SW_RESET_DEVICE4 | \
+ VHUB_SW_RESET_DEVICE3 | \
+ VHUB_SW_RESET_DEVICE2 | \
+ VHUB_SW_RESET_DEVICE1 | \
+ VHUB_SW_RESET_ROOT_HUB)
+/* EP ACK/NACK IRQ masks */
+#define VHUB_EP_IRQ(n) (1 << (n))
+#define VHUB_EP_IRQ_ALL 0x7fff /* 15 EPs */
+
+/* USB status reg */
+#define VHUB_USBSTS_HISPEED (1 << 27)
+
+/* EP toggle */
+#define VHUB_EP_TOGGLE_VALUE (1 << 8)
+#define VHUB_EP_TOGGLE_SET_EPNUM(x) ((x) & 0x1f)
+
+/* HUB EP0 control */
+#define VHUB_EP0_CTRL_STALL (1 << 0)
+#define VHUB_EP0_TX_BUFF_RDY (1 << 1)
+#define VHUB_EP0_RX_BUFF_RDY (1 << 2)
+#define VHUB_EP0_RX_LEN(x) (((x) >> 16) & 0x7f)
+#define VHUB_EP0_SET_TX_LEN(x) (((x) & 0x7f) << 8)
+
+/* HUB EP1 control */
+#define VHUB_EP1_CTRL_RESET_TOGGLE (1 << 2)
+#define VHUB_EP1_CTRL_STALL (1 << 1)
+#define VHUB_EP1_CTRL_ENABLE (1 << 0)
+
+/***********************************
+ * *
+ * per-device register definitions *
+ * *
+ ***********************************/
+#define AST_VHUB_DEV_EN_CTRL 0x00
+#define AST_VHUB_DEV_ISR 0x04
+#define AST_VHUB_DEV_EP0_CTRL 0x08
+#define AST_VHUB_DEV_EP0_DATA 0x0c
+
+/* Device enable control */
+#define VHUB_DEV_EN_SET_ADDR(x) ((x) << 8)
+#define VHUB_DEV_EN_ADDR_MASK ((0xff) << 8)
+#define VHUB_DEV_EN_EP0_NAK_IRQEN (1 << 6)
+#define VHUB_DEV_EN_EP0_IN_ACK_IRQEN (1 << 5)
+#define VHUB_DEV_EN_EP0_OUT_NAK_IRQEN (1 << 4)
+#define VHUB_DEV_EN_EP0_OUT_ACK_IRQEN (1 << 3)
+#define VHUB_DEV_EN_EP0_SETUP_IRQEN (1 << 2)
+#define VHUB_DEV_EN_SPEED_SEL_HIGH (1 << 1)
+#define VHUB_DEV_EN_ENABLE_PORT (1 << 0)
+
+/* Interrupt status */
+#define VHUV_DEV_IRQ_EP0_IN_DATA_NACK (1 << 4)
+#define VHUV_DEV_IRQ_EP0_IN_ACK_STALL (1 << 3)
+#define VHUV_DEV_IRQ_EP0_OUT_DATA_NACK (1 << 2)
+#define VHUV_DEV_IRQ_EP0_OUT_ACK_STALL (1 << 1)
+#define VHUV_DEV_IRQ_EP0_SETUP (1 << 0)
+
+/* Control bits.
+ *
+ * Note: The driver relies on the bulk of those bits
+ * matching corresponding vHub EP0 control bits
+ */
+#define VHUB_DEV_EP0_CTRL_STALL VHUB_EP0_CTRL_STALL
+#define VHUB_DEV_EP0_TX_BUFF_RDY VHUB_EP0_TX_BUFF_RDY
+#define VHUB_DEV_EP0_RX_BUFF_RDY VHUB_EP0_RX_BUFF_RDY
+#define VHUB_DEV_EP0_RX_LEN(x) VHUB_EP0_RX_LEN(x)
+#define VHUB_DEV_EP0_SET_TX_LEN(x) VHUB_EP0_SET_TX_LEN(x)
+
+/*************************************
+ * *
+ * per-endpoint register definitions *
+ * *
+ *************************************/
+
+#define AST_VHUB_EP_CONFIG 0x00
+#define AST_VHUB_EP_DMA_CTLSTAT 0x04
+#define AST_VHUB_EP_DESC_BASE 0x08
+#define AST_VHUB_EP_DESC_STATUS 0x0C
+
+/* EP config reg */
+#define VHUB_EP_CFG_SET_MAX_PKT(x) (((x) & 0x3ff) << 16)
+#define VHUB_EP_CFG_AUTO_DATA_DISABLE (1 << 13)
+#define VHUB_EP_CFG_STALL_CTRL (1 << 12)
+#define VHUB_EP_CFG_SET_EP_NUM(x) (((x) & 0xf) << 8)
+#define VHUB_EP_CFG_SET_TYPE(x) ((x) << 5)
+#define EP_TYPE_OFF 0
+#define EP_TYPE_BULK 1
+#define EP_TYPE_INT 2
+#define EP_TYPE_ISO 3
+#define VHUB_EP_CFG_DIR_OUT (1 << 4)
+#define VHUB_EP_CFG_SET_DEV(x) ((x) << 1)
+#define VHUB_EP_CFG_ENABLE (1 << 0)
+
+/* EP DMA control */
+#define VHUB_EP_DMA_PROC_STATUS(x) (((x) >> 4) & 0xf)
+#define EP_DMA_PROC_RX_IDLE 0
+#define EP_DMA_PROC_TX_IDLE 8
+#define VHUB_EP_DMA_IN_LONG_MODE (1 << 3)
+#define VHUB_EP_DMA_OUT_CONTIG_MODE (1 << 3)
+#define VHUB_EP_DMA_CTRL_RESET (1 << 2)
+#define VHUB_EP_DMA_SINGLE_STAGE (1 << 1)
+#define VHUB_EP_DMA_DESC_MODE (1 << 0)
+
+/* EP DMA status */
+#define VHUB_EP_DMA_SET_TX_SIZE(x) ((x) << 16)
+#define VHUB_EP_DMA_TX_SIZE(x) (((x) >> 16) & 0x7ff)
+#define VHUB_EP_DMA_RPTR(x) (((x) >> 8) & 0xff)
+#define VHUB_EP_DMA_SET_RPTR(x) (((x) & 0xff) << 8)
+#define VHUB_EP_DMA_SET_CPU_WPTR(x) (x)
+#define VHUB_EP_DMA_SINGLE_KICK (1 << 0) /* WPTR = 1 for single mode */
+
+/*******************************
+ * *
+ * DMA descriptors definitions *
+ * *
+ *******************************/
+
+/* Desc W1 IN */
+#define VHUB_DSC1_IN_INTERRUPT (1 << 31)
+#define VHUB_DSC1_IN_SPID_DATA0 (0 << 14)
+#define VHUB_DSC1_IN_SPID_DATA2 (1 << 14)
+#define VHUB_DSC1_IN_SPID_DATA1 (2 << 14)
+#define VHUB_DSC1_IN_SPID_MDATA (3 << 14)
+#define VHUB_DSC1_IN_SET_LEN(x) ((x) & 0xfff)
+#define VHUB_DSC1_IN_LEN(x) ((x) & 0xfff)
+
+/****************************************
+ * *
+ * Data structures and misc definitions *
+ * *
+ ****************************************/
+
+#define AST_VHUB_NUM_GEN_EPs 15 /* Generic non-0 EPs */
+#define AST_VHUB_NUM_PORTS 5 /* vHub ports */
+#define AST_VHUB_EP0_MAX_PACKET 64 /* EP0's max packet size */
+#define AST_VHUB_EPn_MAX_PACKET 1024 /* Generic EPs max packet size */
+#define AST_VHUB_DESCS_COUNT 256 /* Use 256 descriptor mode (valid
+ * values are 256 and 32)
+ */
+
+struct ast_vhub;
+struct ast_vhub_dev;
+
+/*
+ * DMA descriptor (generic EPs only, currently only used
+ * for IN endpoints
+ */
+struct ast_vhub_desc {
+ __le32 w0;
+ __le32 w1;
+};
+
+/* A transfer request, either core-originated or internal */
+struct ast_vhub_req {
+ struct usb_request req;
+ struct list_head queue;
+
+ /* Actual count written to descriptors (desc mode only) */
+ unsigned int act_count;
+
+ /*
+ * Desc number of the final packet or -1. For non-desc
+ * mode (or ep0), any >= 0 value means "last packet"
+ */
+ int last_desc;
+
+ /* Request active (pending DMAs) */
+ bool active : 1;
+
+ /* Internal request (don't call back core) */
+ bool internal : 1;
+};
+#define to_ast_req(__ureq) container_of(__ureq, struct ast_vhub_req, req)
+
+/* Current state of an EP0 */
+enum ep0_state {
+ ep0_state_token,
+ ep0_state_data,
+ ep0_state_status,
+};
+
+/*
+ * An endpoint, either generic, ep0, actual gadget EP
+ * or internal use vhub EP0. vhub EP1 doesn't have an
+ * associated structure as it's mostly HW managed.
+ */
+struct ast_vhub_ep {
+ struct usb_ep ep;
+
+ /* Request queue */
+ struct list_head queue;
+
+ /* EP index in the device, 0 means this is an EP0 */
+ unsigned int d_idx;
+
+ /* Dev pointer or NULL for vHub EP0 */
+ struct ast_vhub_dev *dev;
+
+ /* vHub itself */
+ struct ast_vhub *vhub;
+
+ /*
+ * DMA buffer for EP0, fallback DMA buffer for misaligned
+ * OUT transfers for generic EPs
+ */
+ void *buf;
+ dma_addr_t buf_dma;
+
+ /* The rest depends on the EP type */
+ union {
+ /* EP0 (either device or vhub) */
+ struct {
+ /*
+ * EP0 registers are "similar" for
+ * vHub and devices but located in
+ * different places.
+ */
+ void __iomem *ctlstat;
+ void __iomem *setup;
+
+ /* Current state & direction */
+ enum ep0_state state;
+ bool dir_in;
+
+ /* Internal use request */
+ struct ast_vhub_req req;
+ } ep0;
+
+ /* Generic endpoint (aka EPn) */
+ struct {
+ /* Registers */
+ void __iomem *regs;
+
+ /* Index in global pool (0..14) */
+ unsigned int g_idx;
+
+ /* DMA Descriptors */
+ struct ast_vhub_desc *descs;
+ dma_addr_t descs_dma;
+ unsigned int d_next;
+ unsigned int d_last;
+ unsigned int dma_conf;
+
+ /* Max chunk size for IN EPs */
+ unsigned int chunk_max;
+
+ /* State flags */
+ bool is_in : 1;
+ bool is_iso : 1;
+ bool stalled : 1;
+ bool wedged : 1;
+ bool enabled : 1;
+ bool desc_mode : 1;
+ } epn;
+ };
+};
+#define to_ast_ep(__uep) container_of(__uep, struct ast_vhub_ep, ep)
+
+/* A device attached to a vHub port */
+struct ast_vhub_dev {
+ struct ast_vhub *vhub;
+ void __iomem *regs;
+
+ /* Device index (0...4) and name string */
+ unsigned int index;
+ const char *name;
+
+ /* sysfs enclosure for the gadget gunk */
+ struct device *port_dev;
+
+ /* Link to gadget core */
+ struct usb_gadget gadget;
+ struct usb_gadget_driver *driver;
+ bool registered : 1;
+ bool wakeup_en : 1;
+ bool suspended : 1;
+ bool enabled : 1;
+
+ /* Endpoint structures */
+ struct ast_vhub_ep ep0;
+ struct ast_vhub_ep *epns[AST_VHUB_NUM_GEN_EPs];
+
+};
+#define to_ast_dev(__g) container_of(__g, struct ast_vhub_dev, gadget)
+
+/* Per vhub port stateinfo structure */
+struct ast_vhub_port {
+ /* Port status & status change registers */
+ u16 status;
+ u16 change;
+
+ /* Associated device slot */
+ struct ast_vhub_dev dev;
+};
+
+/* Global vhub structure */
+struct ast_vhub {
+ struct platform_device *pdev;
+ void __iomem *regs;
+ int irq;
+ spinlock_t lock;
+ struct work_struct wake_work;
+ struct clk *clk;
+
+ /* EP0 DMA buffers allocated in one chunk */
+ void *ep0_bufs;
+ dma_addr_t ep0_bufs_dma;
+
+ /* EP0 of the vhub itself */
+ struct ast_vhub_ep ep0;
+
+ /* State of vhub ep1 */
+ bool ep1_stalled : 1;
+
+ /* Per-port info */
+ struct ast_vhub_port ports[AST_VHUB_NUM_PORTS];
+
+ /* Generic EP data structures */
+ struct ast_vhub_ep epns[AST_VHUB_NUM_GEN_EPs];
+
+ /* Upstream bus is suspended ? */
+ bool suspended : 1;
+
+ /* Hub itself can signal remote wakeup */
+ bool wakeup_en : 1;
+
+ /* Force full speed only */
+ bool force_usb1 : 1;
+
+ /* Upstream bus speed captured at bus reset */
+ unsigned int speed;
+};
+
+/* Standard request handlers result codes */
+enum std_req_rc {
+ std_req_stall = -1, /* Stall requested */
+ std_req_complete = 0, /* Request completed with no data */
+ std_req_data = 1, /* Request completed with data */
+ std_req_driver = 2, /* Pass to driver pls */
+};
+
+#ifdef CONFIG_USB_GADGET_VERBOSE
+#define UDCVDBG(u, fmt...) dev_dbg(&(u)->pdev->dev, fmt)
+
+#define EPVDBG(ep, fmt, ...) do { \
+ dev_dbg(&(ep)->vhub->pdev->dev, \
+ "%s:EP%d " fmt, \
+ (ep)->dev ? (ep)->dev->name : "hub", \
+ (ep)->d_idx, ##__VA_ARGS__); \
+ } while(0)
+
+#define DVDBG(d, fmt, ...) do { \
+ dev_dbg(&(d)->vhub->pdev->dev, \
+ "%s " fmt, (d)->name, \
+ ##__VA_ARGS__); \
+ } while(0)
+
+#else
+#define UDCVDBG(u, fmt...) do { } while(0)
+#define EPVDBG(ep, fmt, ...) do { } while(0)
+#define DVDBG(d, fmt, ...) do { } while(0)
+#endif
+
+#ifdef CONFIG_USB_GADGET_DEBUG
+#define UDCDBG(u, fmt...) dev_dbg(&(u)->pdev->dev, fmt)
+
+#define EPDBG(ep, fmt, ...) do { \
+ dev_dbg(&(ep)->vhub->pdev->dev, \
+ "%s:EP%d " fmt, \
+ (ep)->dev ? (ep)->dev->name : "hub", \
+ (ep)->d_idx, ##__VA_ARGS__); \
+ } while(0)
+
+#define DDBG(d, fmt, ...) do { \
+ dev_dbg(&(d)->vhub->pdev->dev, \
+ "%s " fmt, (d)->name, \
+ ##__VA_ARGS__); \
+ } while(0)
+#else
+#define UDCDBG(u, fmt...) do { } while(0)
+#define EPDBG(ep, fmt, ...) do { } while(0)
+#define DDBG(d, fmt, ...) do { } while(0)
+#endif
+
+static inline void vhub_dma_workaround(void *addr)
+{
+ /*
+ * This works around a confirmed HW issue with the Aspeed chip.
+ *
+ * The core uses a different bus to memory than the AHB going to
+ * the USB device controller. Due to the latter having a higher
+ * priority than the core for arbitration on that bus, it's
+ * possible for an MMIO to the device, followed by a DMA by the
+ * device from memory to all be performed and services before
+ * a previous store to memory gets completed.
+ *
+ * This the following scenario can happen:
+ *
+ * - Driver writes to a DMA descriptor (Mbus)
+ * - Driver writes to the MMIO register to start the DMA (AHB)
+ * - The gadget sees the second write and sends a read of the
+ * descriptor to the memory controller (Mbus)
+ * - The gadget hits memory before the descriptor write
+ * causing it to read an obsolete value.
+ *
+ * Thankfully the problem is limited to the USB gadget device, other
+ * masters in the SoC all have a lower priority than the core, thus
+ * ensuring that the store by the core arrives first.
+ *
+ * The workaround consists of using a dummy read of the memory before
+ * doing the MMIO writes. This will ensure that the previous writes
+ * have been "pushed out".
+ */
+ mb();
+ (void)__raw_readl((void __iomem *)addr);
+}
+
+/* core.c */
+void ast_vhub_done(struct ast_vhub_ep *ep, struct ast_vhub_req *req,
+ int status);
+void ast_vhub_nuke(struct ast_vhub_ep *ep, int status);
+struct usb_request *ast_vhub_alloc_request(struct usb_ep *u_ep,
+ gfp_t gfp_flags);
+void ast_vhub_free_request(struct usb_ep *u_ep, struct usb_request *u_req);
+void ast_vhub_init_hw(struct ast_vhub *vhub);
+
+/* ep0.c */
+void ast_vhub_ep0_handle_ack(struct ast_vhub_ep *ep, bool in_ack);
+void ast_vhub_ep0_handle_setup(struct ast_vhub_ep *ep);
+void ast_vhub_init_ep0(struct ast_vhub *vhub, struct ast_vhub_ep *ep,
+ struct ast_vhub_dev *dev);
+int ast_vhub_reply(struct ast_vhub_ep *ep, char *ptr, int len);
+int __ast_vhub_simple_reply(struct ast_vhub_ep *ep, int len, ...);
+#define ast_vhub_simple_reply(udc, ...) \
+ __ast_vhub_simple_reply((udc), \
+ sizeof((u8[]) { __VA_ARGS__ })/sizeof(u8), \
+ __VA_ARGS__)
+
+/* hub.c */
+void ast_vhub_init_hub(struct ast_vhub *vhub);
+enum std_req_rc ast_vhub_std_hub_request(struct ast_vhub_ep *ep,
+ struct usb_ctrlrequest *crq);
+enum std_req_rc ast_vhub_class_hub_request(struct ast_vhub_ep *ep,
+ struct usb_ctrlrequest *crq);
+void ast_vhub_device_connect(struct ast_vhub *vhub, unsigned int port,
+ bool on);
+void ast_vhub_hub_suspend(struct ast_vhub *vhub);
+void ast_vhub_hub_resume(struct ast_vhub *vhub);
+void ast_vhub_hub_reset(struct ast_vhub *vhub);
+void ast_vhub_hub_wake_all(struct ast_vhub *vhub);
+
+/* dev.c */
+int ast_vhub_init_dev(struct ast_vhub *vhub, unsigned int idx);
+void ast_vhub_del_dev(struct ast_vhub_dev *d);
+void ast_vhub_dev_irq(struct ast_vhub_dev *d);
+int ast_vhub_std_dev_request(struct ast_vhub_ep *ep,
+ struct usb_ctrlrequest *crq);
+
+/* epn.c */
+void ast_vhub_epn_ack_irq(struct ast_vhub_ep *ep);
+void ast_vhub_update_epn_stall(struct ast_vhub_ep *ep);
+struct ast_vhub_ep *ast_vhub_alloc_epn(struct ast_vhub_dev *d, u8 addr);
+void ast_vhub_dev_suspend(struct ast_vhub_dev *d);
+void ast_vhub_dev_resume(struct ast_vhub_dev *d);
+void ast_vhub_dev_reset(struct ast_vhub_dev *d);
+
+#endif /* __ASPEED_VHUB_H */
diff --git a/drivers/usb/gadget/udc/at91_udc.c b/drivers/usb/gadget/udc/at91_udc.c
index ad743a8493be..03959dc86cfd 100644
--- a/drivers/usb/gadget/udc/at91_udc.c
+++ b/drivers/usb/gadget/udc/at91_udc.c
@@ -234,22 +234,10 @@ static int proc_udc_show(struct seq_file *s, void *unused)
return 0;
}
-static int proc_udc_open(struct inode *inode, struct file *file)
-{
- return single_open(file, proc_udc_show, PDE_DATA(inode));
-}
-
-static const struct file_operations proc_ops = {
- .owner = THIS_MODULE,
- .open = proc_udc_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
static void create_debug_file(struct at91_udc *udc)
{
- udc->pde = proc_create_data(debug_filename, 0, NULL, &proc_ops, udc);
+ udc->pde = proc_create_single_data(debug_filename, 0, NULL,
+ proc_udc_show, udc);
}
static void remove_debug_file(struct at91_udc *udc)
diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c
index 27c16399c7e8..11247322d587 100644
--- a/drivers/usb/gadget/udc/atmel_usba_udc.c
+++ b/drivers/usb/gadget/udc/atmel_usba_udc.c
@@ -20,7 +20,6 @@
#include <linux/ctype.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
-#include <linux/usb/atmel_usba_udc.h>
#include <linux/delay.h>
#include <linux/of.h>
#include <linux/irq.h>
@@ -207,94 +206,45 @@ static void usba_ep_init_debugfs(struct usba_udc *udc,
struct dentry *ep_root;
ep_root = debugfs_create_dir(ep->ep.name, udc->debugfs_root);
- if (!ep_root)
- goto err_root;
ep->debugfs_dir = ep_root;
- ep->debugfs_queue = debugfs_create_file("queue", 0400, ep_root,
- ep, &queue_dbg_fops);
- if (!ep->debugfs_queue)
- goto err_queue;
-
- if (ep->can_dma) {
- ep->debugfs_dma_status
- = debugfs_create_u32("dma_status", 0400, ep_root,
- &ep->last_dma_status);
- if (!ep->debugfs_dma_status)
- goto err_dma_status;
- }
- if (ep_is_control(ep)) {
- ep->debugfs_state
- = debugfs_create_u32("state", 0400, ep_root,
- &ep->state);
- if (!ep->debugfs_state)
- goto err_state;
- }
-
- return;
-
-err_state:
+ debugfs_create_file("queue", 0400, ep_root, ep, &queue_dbg_fops);
if (ep->can_dma)
- debugfs_remove(ep->debugfs_dma_status);
-err_dma_status:
- debugfs_remove(ep->debugfs_queue);
-err_queue:
- debugfs_remove(ep_root);
-err_root:
- dev_err(&ep->udc->pdev->dev,
- "failed to create debugfs directory for %s\n", ep->ep.name);
+ debugfs_create_u32("dma_status", 0400, ep_root,
+ &ep->last_dma_status);
+ if (ep_is_control(ep))
+ debugfs_create_u32("state", 0400, ep_root, &ep->state);
}
static void usba_ep_cleanup_debugfs(struct usba_ep *ep)
{
- debugfs_remove(ep->debugfs_queue);
- debugfs_remove(ep->debugfs_dma_status);
- debugfs_remove(ep->debugfs_state);
- debugfs_remove(ep->debugfs_dir);
- ep->debugfs_dma_status = NULL;
- ep->debugfs_dir = NULL;
+ debugfs_remove_recursive(ep->debugfs_dir);
}
static void usba_init_debugfs(struct usba_udc *udc)
{
- struct dentry *root, *regs;
+ struct dentry *root;
struct resource *regs_resource;
root = debugfs_create_dir(udc->gadget.name, NULL);
- if (IS_ERR(root) || !root)
- goto err_root;
udc->debugfs_root = root;
regs_resource = platform_get_resource(udc->pdev, IORESOURCE_MEM,
CTRL_IOMEM_ID);
if (regs_resource) {
- regs = debugfs_create_file_size("regs", 0400, root, udc,
- &regs_dbg_fops,
- resource_size(regs_resource));
- if (!regs)
- goto err_regs;
- udc->debugfs_regs = regs;
+ debugfs_create_file_size("regs", 0400, root, udc,
+ &regs_dbg_fops,
+ resource_size(regs_resource));
}
usba_ep_init_debugfs(udc, to_usba_ep(udc->gadget.ep0));
-
- return;
-
-err_regs:
- debugfs_remove(root);
-err_root:
- udc->debugfs_root = NULL;
- dev_err(&udc->pdev->dev, "debugfs is not available\n");
}
static void usba_cleanup_debugfs(struct usba_udc *udc)
{
usba_ep_cleanup_debugfs(to_usba_ep(udc->gadget.ep0));
- debugfs_remove(udc->debugfs_regs);
- debugfs_remove(udc->debugfs_root);
- udc->debugfs_regs = NULL;
- udc->debugfs_root = NULL;
+ debugfs_remove_recursive(udc->debugfs_root);
}
#else
static inline void usba_ep_init_debugfs(struct usba_udc *udc,
@@ -417,7 +367,7 @@ static inline void usba_int_enb_set(struct usba_udc *udc, u32 val)
static int vbus_is_present(struct usba_udc *udc)
{
if (udc->vbus_pin)
- return gpiod_get_value(udc->vbus_pin) ^ udc->vbus_pin_inverted;
+ return gpiod_get_value(udc->vbus_pin);
/* No Vbus detection: Assume always present */
return 1;
@@ -2054,7 +2004,6 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
struct usba_udc *udc)
{
u32 val;
- const char *name;
struct device_node *np = pdev->dev.of_node;
const struct of_device_id *match;
struct device_node *pp;
@@ -2068,6 +2017,8 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
udc->errata = match->data;
udc->pmc = syscon_regmap_lookup_by_compatible("atmel,at91sam9g45-pmc");
if (IS_ERR(udc->pmc))
+ udc->pmc = syscon_regmap_lookup_by_compatible("atmel,at91sam9rl-pmc");
+ if (IS_ERR(udc->pmc))
udc->pmc = syscon_regmap_lookup_by_compatible("atmel,at91sam9x5-pmc");
if (udc->errata && IS_ERR(udc->pmc))
return ERR_CAST(udc->pmc);
@@ -2076,7 +2027,6 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
udc->vbus_pin = devm_gpiod_get_optional(&pdev->dev, "atmel,vbus",
GPIOD_IN);
- udc->vbus_pin_inverted = gpiod_is_active_low(udc->vbus_pin);
if (fifo_mode == 0) {
pp = NULL;
@@ -2087,7 +2037,7 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
udc->num_ep = usba_config_fifo_table(udc);
}
- eps = devm_kzalloc(&pdev->dev, sizeof(struct usba_ep) * udc->num_ep,
+ eps = devm_kcalloc(&pdev->dev, udc->num_ep, sizeof(struct usba_ep),
GFP_KERNEL);
if (!eps)
return ERR_PTR(-ENOMEM);
@@ -2145,11 +2095,6 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
ep->can_dma = of_property_read_bool(pp, "atmel,can-dma");
ep->can_isoc = of_property_read_bool(pp, "atmel,can-isoc");
- ret = of_property_read_string(pp, "name", &name);
- if (ret) {
- dev_err(&pdev->dev, "of_probe: name error(%d)\n", ret);
- goto err;
- }
sprintf(ep->name, "ep%d", ep->index);
ep->ep.name = ep->name;
@@ -2279,15 +2224,15 @@ static int usba_udc_probe(struct platform_device *pdev)
if (udc->vbus_pin) {
irq_set_status_flags(gpiod_to_irq(udc->vbus_pin), IRQ_NOAUTOEN);
ret = devm_request_threaded_irq(&pdev->dev,
- gpiod_to_irq(udc->vbus_pin), NULL,
- usba_vbus_irq_thread, USBA_VBUS_IRQFLAGS,
- "atmel_usba_udc", udc);
- if (ret) {
- udc->vbus_pin = NULL;
- dev_warn(&udc->pdev->dev,
- "failed to request vbus irq; "
- "assuming always on\n");
- }
+ gpiod_to_irq(udc->vbus_pin), NULL,
+ usba_vbus_irq_thread, USBA_VBUS_IRQFLAGS,
+ "atmel_usba_udc", udc);
+ if (ret) {
+ udc->vbus_pin = NULL;
+ dev_warn(&udc->pdev->dev,
+ "failed to request vbus irq; "
+ "assuming always on\n");
+ }
}
ret = usb_add_gadget_udc(&pdev->dev, &udc->gadget);
diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.h b/drivers/usb/gadget/udc/atmel_usba_udc.h
index 969ce8f3c3e2..030bf797cd25 100644
--- a/drivers/usb/gadget/udc/atmel_usba_udc.h
+++ b/drivers/usb/gadget/udc/atmel_usba_udc.h
@@ -287,9 +287,6 @@ struct usba_ep {
#ifdef CONFIG_USB_GADGET_DEBUG_FS
u32 last_dma_status;
struct dentry *debugfs_dir;
- struct dentry *debugfs_queue;
- struct dentry *debugfs_dma_status;
- struct dentry *debugfs_state;
#endif
};
@@ -326,7 +323,6 @@ struct usba_udc {
const struct usba_udc_errata *errata;
int irq;
struct gpio_desc *vbus_pin;
- int vbus_pin_inverted;
int num_ep;
int configured_ep;
struct usba_fifo_cfg *fifo_cfg;
@@ -345,7 +341,6 @@ struct usba_udc {
#ifdef CONFIG_USB_GADGET_DEBUG_FS
struct dentry *debugfs_root;
- struct dentry *debugfs_regs;
#endif
struct regmap *pmc;
diff --git a/drivers/usb/gadget/udc/bcm63xx_udc.c b/drivers/usb/gadget/udc/bcm63xx_udc.c
index 3a8df8601074..c1fcc77403ea 100644
--- a/drivers/usb/gadget/udc/bcm63xx_udc.c
+++ b/drivers/usb/gadget/udc/bcm63xx_udc.c
@@ -288,8 +288,6 @@ struct bcm63xx_req {
* @ep0_reply: Pending reply from gadget driver.
* @ep0_request: Outstanding ep0 request.
* @debugfs_root: debugfs directory: /sys/kernel/debug/<DRV_MODULE_NAME>.
- * @debugfs_usbd: debugfs file "usbd" for controller state.
- * @debugfs_iudma: debugfs file "usbd" for IUDMA state.
*/
struct bcm63xx_udc {
spinlock_t lock;
@@ -330,8 +328,6 @@ struct bcm63xx_udc {
struct usb_request *ep0_request;
struct dentry *debugfs_root;
- struct dentry *debugfs_usbd;
- struct dentry *debugfs_iudma;
};
static const struct usb_ep_ops bcm63xx_udc_ep_ops;
@@ -2247,34 +2243,16 @@ DEFINE_SHOW_ATTRIBUTE(bcm63xx_iudma_dbg);
*/
static void bcm63xx_udc_init_debugfs(struct bcm63xx_udc *udc)
{
- struct dentry *root, *usbd, *iudma;
+ struct dentry *root;
if (!IS_ENABLED(CONFIG_USB_GADGET_DEBUG_FS))
return;
root = debugfs_create_dir(udc->gadget.name, NULL);
- if (IS_ERR(root) || !root)
- goto err_root;
-
- usbd = debugfs_create_file("usbd", 0400, root, udc,
- &bcm63xx_usbd_dbg_fops);
- if (!usbd)
- goto err_usbd;
- iudma = debugfs_create_file("iudma", 0400, root, udc,
- &bcm63xx_iudma_dbg_fops);
- if (!iudma)
- goto err_iudma;
-
udc->debugfs_root = root;
- udc->debugfs_usbd = usbd;
- udc->debugfs_iudma = iudma;
- return;
-err_iudma:
- debugfs_remove(usbd);
-err_usbd:
- debugfs_remove(root);
-err_root:
- dev_err(udc->dev, "debugfs is not available\n");
+
+ debugfs_create_file("usbd", 0400, root, udc, &bcm63xx_usbd_dbg_fops);
+ debugfs_create_file("iudma", 0400, root, udc, &bcm63xx_iudma_dbg_fops);
}
/**
@@ -2285,12 +2263,7 @@ err_root:
*/
static void bcm63xx_udc_cleanup_debugfs(struct bcm63xx_udc *udc)
{
- debugfs_remove(udc->debugfs_iudma);
- debugfs_remove(udc->debugfs_usbd);
- debugfs_remove(udc->debugfs_root);
- udc->debugfs_iudma = NULL;
- udc->debugfs_usbd = NULL;
- udc->debugfs_root = NULL;
+ debugfs_remove_recursive(udc->debugfs_root);
}
/***********************************************************************
diff --git a/drivers/usb/gadget/udc/bdc/bdc_ep.c b/drivers/usb/gadget/udc/bdc/bdc_ep.c
index 03149b9d7ea7..a4d9b5e1e50e 100644
--- a/drivers/usb/gadget/udc/bdc/bdc_ep.c
+++ b/drivers/usb/gadget/udc/bdc/bdc_ep.c
@@ -138,9 +138,9 @@ 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 = kzalloc(
- num_tabs * sizeof(struct bd_table *),
- GFP_ATOMIC);
+ ep->bd_list.bd_table_array = kcalloc(num_tabs,
+ sizeof(struct bd_table *),
+ GFP_ATOMIC);
if (!ep->bd_list.bd_table_array)
return -ENOMEM;
diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c
index 842814bc0e4f..87d6b12779f2 100644
--- a/drivers/usb/gadget/udc/core.c
+++ b/drivers/usb/gadget/udc/core.c
@@ -87,6 +87,8 @@ EXPORT_SYMBOL_GPL(usb_ep_set_maxpacket_limit);
* configurable, with more generic names like "ep-a". (remember that for
* USB, "in" means "towards the USB master".)
*
+ * This routine must be called in process context.
+ *
* returns zero, or a negative error code.
*/
int usb_ep_enable(struct usb_ep *ep)
@@ -119,6 +121,8 @@ EXPORT_SYMBOL_GPL(usb_ep_enable);
* gadget drivers must call usb_ep_enable() again before queueing
* requests to the endpoint.
*
+ * This routine must be called in process context.
+ *
* returns zero, or a negative error code.
*/
int usb_ep_disable(struct usb_ep *ep)
@@ -241,9 +245,17 @@ EXPORT_SYMBOL_GPL(usb_ep_free_request);
* Note that @req's ->complete() callback must never be called from
* within usb_ep_queue() as that can create deadlock situations.
*
+ * This routine may be called in interrupt context.
+ *
* Returns zero, or a negative error code. Endpoints that are not enabled
* report errors; errors will also be
* reported when the usb peripheral is disconnected.
+ *
+ * If and only if @req is successfully queued (the return value is zero),
+ * @req->complete() will be called exactly once, when the Gadget core and
+ * UDC are finished with the request. When the completion function is called,
+ * control of the request is returned to the device driver which submitted it.
+ * The completion handler may then immediately free or reuse @req.
*/
int usb_ep_queue(struct usb_ep *ep,
struct usb_request *req, gfp_t gfp_flags)
@@ -278,6 +290,8 @@ EXPORT_SYMBOL_GPL(usb_ep_queue);
* at the head of the queue) except as part of disconnecting from usb. Such
* restrictions prevent drivers from supporting configuration changes,
* even to configuration zero (a "chapter 9" requirement).
+ *
+ * This routine may be called in interrupt context.
*/
int usb_ep_dequeue(struct usb_ep *ep, struct usb_request *req)
{
@@ -305,6 +319,8 @@ EXPORT_SYMBOL_GPL(usb_ep_dequeue);
* current altsetting, see usb_ep_clear_halt(). When switching altsettings,
* it's simplest to use usb_ep_enable() or usb_ep_disable() for the endpoints.
*
+ * This routine may be called in interrupt context.
+ *
* Returns zero, or a negative error code. On success, this call sets
* underlying hardware state that blocks data transfers.
* Attempts to halt IN endpoints will fail (returning -EAGAIN) if any
@@ -330,6 +346,8 @@ EXPORT_SYMBOL_GPL(usb_ep_set_halt);
* for endpoints that aren't reconfigured, after clearing any other state
* in the endpoint's i/o queue.
*
+ * This routine may be called in interrupt context.
+ *
* Returns zero, or a negative error code. On success, this call clears
* the underlying hardware state reflecting endpoint halt and data toggle.
* Note that some hardware can't support this request (like pxa2xx_udc),
@@ -354,6 +372,8 @@ EXPORT_SYMBOL_GPL(usb_ep_clear_halt);
* requests. If the gadget driver clears the halt status, it will
* automatically unwedge the endpoint.
*
+ * This routine may be called in interrupt context.
+ *
* Returns zero on success, else negative errno.
*/
int usb_ep_set_wedge(struct usb_ep *ep)
@@ -382,6 +402,8 @@ EXPORT_SYMBOL_GPL(usb_ep_set_wedge);
* written OUT to it by the host. Drivers that need precise handling for
* fault reporting or recovery may need to use this call.
*
+ * This routine may be called in interrupt context.
+ *
* This returns the number of such bytes in the fifo, or a negative
* errno if the endpoint doesn't use a FIFO or doesn't support such
* precise handling.
@@ -409,6 +431,8 @@ EXPORT_SYMBOL_GPL(usb_ep_fifo_status);
* an endpoint fifo after abnormal transaction terminations. The call
* must never be used except when endpoint is not being used for any
* protocol translation.
+ *
+ * This routine may be called in interrupt context.
*/
void usb_ep_fifo_flush(struct usb_ep *ep)
{
@@ -666,6 +690,9 @@ EXPORT_SYMBOL_GPL(usb_gadget_connect);
* as a disconnect (when a VBUS session is active). Not all systems
* support software pullup controls.
*
+ * Following a successful disconnect, invoke the ->disconnect() callback
+ * for the current gadget driver so that UDC drivers don't need to.
+ *
* Returns zero on success, else negative errno.
*/
int usb_gadget_disconnect(struct usb_gadget *gadget)
@@ -687,8 +714,10 @@ int usb_gadget_disconnect(struct usb_gadget *gadget)
}
ret = gadget->ops->pullup(gadget, 0);
- if (!ret)
+ if (!ret) {
gadget->connected = 0;
+ gadget->udc->driver->disconnect(gadget);
+ }
out:
trace_usb_gadget_disconnect(gadget, ret);
@@ -1257,7 +1286,6 @@ static void usb_gadget_remove_driver(struct usb_udc *udc)
kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
usb_gadget_disconnect(udc->gadget);
- udc->driver->disconnect(udc->gadget);
udc->driver->unbind(udc->gadget);
usb_gadget_udc_stop(udc);
@@ -1447,7 +1475,6 @@ static ssize_t soft_connect_store(struct device *dev,
usb_gadget_connect(udc->gadget);
} else if (sysfs_streq(buf, "disconnect")) {
usb_gadget_disconnect(udc->gadget);
- udc->driver->disconnect(udc->gadget);
usb_gadget_udc_stop(udc);
} else {
dev_err(dev, "unsupported command '%s'\n", buf);
diff --git a/drivers/usb/gadget/udc/fotg210-udc.c b/drivers/usb/gadget/udc/fotg210-udc.c
index 53a48f561458..bc6abaea907d 100644
--- a/drivers/usb/gadget/udc/fotg210-udc.c
+++ b/drivers/usb/gadget/udc/fotg210-udc.c
@@ -741,7 +741,7 @@ static void fotg210_get_status(struct fotg210_udc *fotg210,
fotg210->ep0_req->length = 2;
spin_unlock(&fotg210->lock);
- fotg210_ep_queue(fotg210->gadget.ep0, fotg210->ep0_req, GFP_KERNEL);
+ fotg210_ep_queue(fotg210->gadget.ep0, fotg210->ep0_req, GFP_ATOMIC);
spin_lock(&fotg210->lock);
}
@@ -1063,12 +1063,15 @@ static const struct usb_gadget_ops fotg210_gadget_ops = {
static int fotg210_udc_remove(struct platform_device *pdev)
{
struct fotg210_udc *fotg210 = platform_get_drvdata(pdev);
+ int i;
usb_del_gadget_udc(&fotg210->gadget);
iounmap(fotg210->reg);
free_irq(platform_get_irq(pdev, 0), fotg210);
fotg210_ep_free_request(&fotg210->ep[0]->ep, fotg210->ep0_req);
+ for (i = 0; i < FOTG210_MAX_NUM_EP; i++)
+ kfree(fotg210->ep[i]);
kfree(fotg210);
return 0;
@@ -1099,7 +1102,7 @@ static int fotg210_udc_probe(struct platform_device *pdev)
/* initialize udc */
fotg210 = kzalloc(sizeof(struct fotg210_udc), GFP_KERNEL);
if (fotg210 == NULL)
- goto err_alloc;
+ goto err;
for (i = 0; i < FOTG210_MAX_NUM_EP; i++) {
_ep[i] = kzalloc(sizeof(struct fotg210_ep), GFP_KERNEL);
@@ -1111,7 +1114,7 @@ static int fotg210_udc_probe(struct platform_device *pdev)
fotg210->reg = ioremap(res->start, resource_size(res));
if (fotg210->reg == NULL) {
pr_err("ioremap error.\n");
- goto err_map;
+ goto err_alloc;
}
spin_lock_init(&fotg210->lock);
@@ -1159,7 +1162,7 @@ static int fotg210_udc_probe(struct platform_device *pdev)
fotg210->ep0_req = fotg210_ep_alloc_request(&fotg210->ep[0]->ep,
GFP_KERNEL);
if (fotg210->ep0_req == NULL)
- goto err_req;
+ goto err_map;
fotg210_init(fotg210);
@@ -1187,12 +1190,14 @@ err_req:
fotg210_ep_free_request(&fotg210->ep[0]->ep, fotg210->ep0_req);
err_map:
- if (fotg210->reg)
- iounmap(fotg210->reg);
+ iounmap(fotg210->reg);
err_alloc:
+ for (i = 0; i < FOTG210_MAX_NUM_EP; i++)
+ kfree(fotg210->ep[i]);
kfree(fotg210);
+err:
return ret;
}
diff --git a/drivers/usb/gadget/udc/fsl_mxc_udc.c b/drivers/usb/gadget/udc/fsl_mxc_udc.c
index f29cf5c6160c..5a321992decc 100644
--- a/drivers/usb/gadget/udc/fsl_mxc_udc.c
+++ b/drivers/usb/gadget/udc/fsl_mxc_udc.c
@@ -11,6 +11,7 @@
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/fsl_devices.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include <linux/io.h>
diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c
index 56b517a38865..20141c3096f6 100644
--- a/drivers/usb/gadget/udc/fsl_udc_core.c
+++ b/drivers/usb/gadget/udc/fsl_udc_core.c
@@ -253,6 +253,7 @@ static int dr_controller_setup(struct fsl_udc *udc)
portctrl |= PORTSCX_PTW_16BIT;
/* fall through */
case FSL_USB2_PHY_UTMI:
+ case FSL_USB2_PHY_UTMI_DUAL:
if (udc->pdata->have_sysif_regs) {
if (udc->pdata->controller_ver) {
/* controller version 1.6 or above */
@@ -2207,22 +2208,8 @@ static int fsl_proc_read(struct seq_file *m, void *v)
return 0;
}
-/*
- * seq_file wrappers for procfile show routines.
- */
-static int fsl_proc_open(struct inode *inode, struct file *file)
-{
- return single_open(file, fsl_proc_read, NULL);
-}
-
-static const struct file_operations fsl_proc_fops = {
- .open = fsl_proc_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-#define create_proc_file() proc_create(proc_filename, 0, NULL, &fsl_proc_fops)
+#define create_proc_file() \
+ proc_create_single(proc_filename, 0, NULL, fsl_proc_read)
#define remove_proc_file() remove_proc_entry(proc_filename, NULL)
#else /* !CONFIG_USB_GADGET_DEBUG_FILES */
@@ -2247,8 +2234,10 @@ static void fsl_udc_release(struct device *dev)
Internal structure setup functions
*******************************************************************/
/*------------------------------------------------------------------
- * init resource for globle controller
- * Return the udc handle on success or NULL on failure
+ * init resource for global controller called by fsl_udc_probe()
+ * On success the udc handle is initialized, on failure it is
+ * unchanged (reset).
+ * Return 0 on success and -1 on allocation failure
------------------------------------------------------------------*/
static int struct_udc_setup(struct fsl_udc *udc,
struct platform_device *pdev)
@@ -2259,9 +2248,11 @@ static int struct_udc_setup(struct fsl_udc *udc,
pdata = dev_get_platdata(&pdev->dev);
udc->phy_mode = pdata->phy_mode;
- udc->eps = kzalloc(sizeof(struct fsl_ep) * udc->max_ep, GFP_KERNEL);
- if (!udc->eps)
- return -1;
+ udc->eps = kcalloc(udc->max_ep, sizeof(struct fsl_ep), GFP_KERNEL);
+ if (!udc->eps) {
+ ERR("kmalloc udc endpoint status failed\n");
+ goto eps_alloc_failed;
+ }
/* initialized QHs, take care of alignment */
size = udc->max_ep * sizeof(struct ep_queue_head);
@@ -2275,8 +2266,7 @@ static int struct_udc_setup(struct fsl_udc *udc,
&udc->ep_qh_dma, GFP_KERNEL);
if (!udc->ep_qh) {
ERR("malloc QHs for udc failed\n");
- kfree(udc->eps);
- return -1;
+ goto ep_queue_alloc_failed;
}
udc->ep_qh_size = size;
@@ -2285,8 +2275,17 @@ static int struct_udc_setup(struct fsl_udc *udc,
/* FIXME: fsl_alloc_request() ignores ep argument */
udc->status_req = container_of(fsl_alloc_request(NULL, GFP_KERNEL),
struct fsl_req, req);
+ if (!udc->status_req) {
+ ERR("kzalloc for udc status request failed\n");
+ goto udc_status_alloc_failed;
+ }
+
/* allocate a small amount of memory to get valid address */
udc->status_req->req.buf = kmalloc(8, GFP_KERNEL);
+ if (!udc->status_req->req.buf) {
+ ERR("kzalloc for udc request buffer failed\n");
+ goto udc_req_buf_alloc_failed;
+ }
udc->resume_state = USB_STATE_NOTATTACHED;
udc->usb_state = USB_STATE_POWERED;
@@ -2294,6 +2293,18 @@ static int struct_udc_setup(struct fsl_udc *udc,
udc->remote_wakeup = 0; /* default to 0 on reset */
return 0;
+
+udc_req_buf_alloc_failed:
+ kfree(udc->status_req);
+udc_status_alloc_failed:
+ kfree(udc->ep_qh);
+ udc->ep_qh_size = 0;
+ep_queue_alloc_failed:
+ kfree(udc->eps);
+eps_alloc_failed:
+ udc->phy_mode = 0;
+ return -1;
+
}
/*----------------------------------------------------------------
diff --git a/drivers/usb/gadget/udc/goku_udc.c b/drivers/usb/gadget/udc/goku_udc.c
index 4504d0b202db..c3721225b61e 100644
--- a/drivers/usb/gadget/udc/goku_udc.c
+++ b/drivers/usb/gadget/udc/goku_udc.c
@@ -1241,22 +1241,6 @@ done:
local_irq_restore(flags);
return 0;
}
-
-/*
- * seq_file wrappers for procfile show routines.
- */
-static int udc_proc_open(struct inode *inode, struct file *file)
-{
- return single_open(file, udc_proc_read, PDE_DATA(file_inode(file)));
-}
-
-static const struct file_operations udc_proc_fops = {
- .open = udc_proc_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
/*-------------------------------------------------------------------------*/
@@ -1826,7 +1810,7 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id)
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
- proc_create_data(proc_node_name, 0, NULL, &udc_proc_fops, dev);
+ proc_create_single_data(proc_node_name, 0, NULL, udc_proc_read, dev);
#endif
retval = usb_add_gadget_udc_release(&pdev->dev, &dev->gadget,
diff --git a/drivers/usb/gadget/udc/gr_udc.c b/drivers/usb/gadget/udc/gr_udc.c
index ca83c15d8ea4..729e60e49564 100644
--- a/drivers/usb/gadget/udc/gr_udc.c
+++ b/drivers/usb/gadget/udc/gr_udc.c
@@ -209,15 +209,12 @@ static void gr_dfs_create(struct gr_udc *dev)
const char *name = "gr_udc_state";
dev->dfs_root = debugfs_create_dir(dev_name(dev->dev), NULL);
- dev->dfs_state = debugfs_create_file(name, 0444, dev->dfs_root, dev,
- &gr_dfs_fops);
+ debugfs_create_file(name, 0444, dev->dfs_root, dev, &gr_dfs_fops);
}
static void gr_dfs_delete(struct gr_udc *dev)
{
- /* Handles NULL and ERR pointers internally */
- debugfs_remove(dev->dfs_state);
- debugfs_remove(dev->dfs_root);
+ debugfs_remove_recursive(dev->dfs_root);
}
#else /* !CONFIG_USB_GADGET_DEBUG_FS */
diff --git a/drivers/usb/gadget/udc/gr_udc.h b/drivers/usb/gadget/udc/gr_udc.h
index 3e913268c8c5..417ad2aa2cc7 100644
--- a/drivers/usb/gadget/udc/gr_udc.h
+++ b/drivers/usb/gadget/udc/gr_udc.h
@@ -217,7 +217,6 @@ struct gr_udc {
spinlock_t lock; /* General lock, a.k.a. "dev->lock" in comments */
struct dentry *dfs_root;
- struct dentry *dfs_state;
};
#define to_gr_udc(gadget) (container_of((gadget), struct gr_udc, gadget))
diff --git a/drivers/usb/gadget/udc/mv_udc_core.c b/drivers/usb/gadget/udc/mv_udc_core.c
index 95f52232493b..cafde053788b 100644
--- a/drivers/usb/gadget/udc/mv_udc_core.c
+++ b/drivers/usb/gadget/udc/mv_udc_core.c
@@ -185,7 +185,7 @@ static int process_ep_req(struct mv_udc *udc, int index,
else
bit_pos = 1 << (16 + curr_req->ep->ep_num);
- while ((curr_dqh->curr_dtd_ptr == curr_dtd->td_dma)) {
+ 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);
diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c
index 318246d8b2e2..e7dae5379e04 100644
--- a/drivers/usb/gadget/udc/net2280.c
+++ b/drivers/usb/gadget/udc/net2280.c
@@ -1545,7 +1545,7 @@ static int net2280_pullup(struct usb_gadget *_gadget, int is_on)
writel(tmp | BIT(USB_DETECT_ENABLE), &dev->usb->usbctl);
} else {
writel(tmp & ~BIT(USB_DETECT_ENABLE), &dev->usb->usbctl);
- stop_activity(dev, dev->driver);
+ stop_activity(dev, NULL);
}
spin_unlock_irqrestore(&dev->lock, flags);
@@ -2466,8 +2466,11 @@ static void stop_activity(struct net2280 *dev, struct usb_gadget_driver *driver)
nuke(&dev->ep[i]);
/* report disconnect; the driver is already quiesced */
- if (driver)
+ if (driver) {
+ spin_unlock(&dev->lock);
driver->disconnect(&dev->gadget);
+ spin_lock(&dev->lock);
+ }
usb_reinit(dev);
}
@@ -3341,6 +3344,8 @@ next_endpoints:
BIT(PCI_RETRY_ABORT_INTERRUPT))
static void handle_stat1_irqs(struct net2280 *dev, u32 stat)
+__releases(dev->lock)
+__acquires(dev->lock)
{
struct net2280_ep *ep;
u32 tmp, num, mask, scratch;
@@ -3381,12 +3386,14 @@ static void handle_stat1_irqs(struct net2280 *dev, u32 stat)
if (disconnect || reset) {
stop_activity(dev, dev->driver);
ep0_start(dev);
+ 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;
}
}
@@ -3405,6 +3412,7 @@ static void handle_stat1_irqs(struct net2280 *dev, u32 stat)
tmp = BIT(SUSPEND_REQUEST_CHANGE_INTERRUPT);
if (stat & tmp) {
writel(tmp, &dev->regs->irqstat1);
+ spin_unlock(&dev->lock);
if (stat & BIT(SUSPEND_REQUEST_INTERRUPT)) {
if (dev->driver->suspend)
dev->driver->suspend(&dev->gadget);
@@ -3415,6 +3423,7 @@ static void handle_stat1_irqs(struct net2280 *dev, u32 stat)
dev->driver->resume(&dev->gadget);
/* at high speed, note erratum 0133 */
}
+ spin_lock(&dev->lock);
stat &= ~tmp;
}
diff --git a/drivers/usb/gadget/udc/omap_udc.c b/drivers/usb/gadget/udc/omap_udc.c
index dc35a54bad90..fcf13ef33b31 100644
--- a/drivers/usb/gadget/udc/omap_udc.c
+++ b/drivers/usb/gadget/udc/omap_udc.c
@@ -2033,6 +2033,7 @@ static inline int machine_without_vbus_sense(void)
{
return machine_is_omap_innovator()
|| machine_is_omap_osk()
+ || machine_is_omap_palmte()
|| machine_is_sx1()
/* No known omap7xx boards with vbus sense */
|| cpu_is_omap7xx();
@@ -2041,7 +2042,7 @@ static inline int machine_without_vbus_sense(void)
static int omap_udc_start(struct usb_gadget *g,
struct usb_gadget_driver *driver)
{
- int status = -ENODEV;
+ int status;
struct omap_ep *ep;
unsigned long flags;
@@ -2079,6 +2080,7 @@ static int omap_udc_start(struct usb_gadget *g,
goto done;
}
} else {
+ status = 0;
if (can_pullup(udc))
pullup_enable(udc);
else
@@ -2432,22 +2434,9 @@ static int proc_udc_show(struct seq_file *s, void *_)
return 0;
}
-static int proc_udc_open(struct inode *inode, struct file *file)
-{
- return single_open(file, proc_udc_show, NULL);
-}
-
-static const struct file_operations proc_ops = {
- .owner = THIS_MODULE,
- .open = proc_udc_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
static void create_proc_file(void)
{
- proc_create(proc_filename, 0, NULL, &proc_ops);
+ proc_create_single(proc_filename, 0, NULL, proc_udc_show);
}
static void remove_proc_file(void)
@@ -2606,9 +2595,22 @@ omap_ep_setup(char *name, u8 addr, u8 type,
static void omap_udc_release(struct device *dev)
{
- complete(udc->done);
+ pullup_disable(udc);
+ if (!IS_ERR_OR_NULL(udc->transceiver)) {
+ usb_put_phy(udc->transceiver);
+ udc->transceiver = NULL;
+ }
+ omap_writew(0, UDC_SYSCON1);
+ remove_proc_file();
+ if (udc->dc_clk) {
+ if (udc->clk_requested)
+ omap_udc_enable_clock(0);
+ clk_put(udc->hhc_clk);
+ clk_put(udc->dc_clk);
+ }
+ if (udc->done)
+ complete(udc->done);
kfree(udc);
- udc = NULL;
}
static int
@@ -2640,6 +2642,7 @@ omap_udc_setup(struct platform_device *odev, struct usb_phy *xceiv)
udc->gadget.speed = USB_SPEED_UNKNOWN;
udc->gadget.max_speed = USB_SPEED_FULL;
udc->gadget.name = driver_name;
+ udc->gadget.quirk_ep_out_aligned_size = 1;
udc->transceiver = xceiv;
/* ep0 is special; put it right after the SETUP buffer */
@@ -2880,8 +2883,8 @@ bad_on_1710:
udc->clr_halt = UDC_RESET_EP;
/* USB general purpose IRQ: ep0, state changes, dma, etc */
- status = request_irq(pdev->resource[1].start, omap_udc_irq,
- 0, driver_name, udc);
+ status = devm_request_irq(&pdev->dev, pdev->resource[1].start,
+ omap_udc_irq, 0, driver_name, udc);
if (status != 0) {
ERR("can't get irq %d, err %d\n",
(int) pdev->resource[1].start, status);
@@ -2889,20 +2892,20 @@ bad_on_1710:
}
/* USB "non-iso" IRQ (PIO for all but ep0) */
- status = request_irq(pdev->resource[2].start, omap_udc_pio_irq,
- 0, "omap_udc pio", udc);
+ status = devm_request_irq(&pdev->dev, pdev->resource[2].start,
+ omap_udc_pio_irq, 0, "omap_udc pio", udc);
if (status != 0) {
ERR("can't get irq %d, err %d\n",
(int) pdev->resource[2].start, status);
- goto cleanup2;
+ goto cleanup1;
}
#ifdef USE_ISO
- status = request_irq(pdev->resource[3].start, omap_udc_iso_irq,
- 0, "omap_udc iso", udc);
+ status = devm_request_irq(&pdev->dev, pdev->resource[3].start,
+ omap_udc_iso_irq, 0, "omap_udc iso", udc);
if (status != 0) {
ERR("can't get irq %d, err %d\n",
(int) pdev->resource[3].start, status);
- goto cleanup3;
+ goto cleanup1;
}
#endif
if (cpu_is_omap16xx() || cpu_is_omap7xx()) {
@@ -2913,23 +2916,8 @@ bad_on_1710:
}
create_proc_file();
- status = usb_add_gadget_udc_release(&pdev->dev, &udc->gadget,
- omap_udc_release);
- if (status)
- goto cleanup4;
-
- return 0;
-
-cleanup4:
- remove_proc_file();
-
-#ifdef USE_ISO
-cleanup3:
- free_irq(pdev->resource[2].start, udc);
-#endif
-
-cleanup2:
- free_irq(pdev->resource[1].start, udc);
+ return usb_add_gadget_udc_release(&pdev->dev, &udc->gadget,
+ omap_udc_release);
cleanup1:
kfree(udc);
@@ -2956,42 +2944,15 @@ static int omap_udc_remove(struct platform_device *pdev)
{
DECLARE_COMPLETION_ONSTACK(done);
- if (!udc)
- return -ENODEV;
-
- usb_del_gadget_udc(&udc->gadget);
- if (udc->driver)
- return -EBUSY;
-
udc->done = &done;
- pullup_disable(udc);
- if (!IS_ERR_OR_NULL(udc->transceiver)) {
- usb_put_phy(udc->transceiver);
- udc->transceiver = NULL;
- }
- omap_writew(0, UDC_SYSCON1);
-
- remove_proc_file();
-
-#ifdef USE_ISO
- free_irq(pdev->resource[3].start, udc);
-#endif
- free_irq(pdev->resource[2].start, udc);
- free_irq(pdev->resource[1].start, udc);
+ usb_del_gadget_udc(&udc->gadget);
- if (udc->dc_clk) {
- if (udc->clk_requested)
- omap_udc_enable_clock(0);
- clk_put(udc->hhc_clk);
- clk_put(udc->dc_clk);
- }
+ wait_for_completion(&done);
release_mem_region(pdev->resource[0].start,
pdev->resource[0].end - pdev->resource[0].start + 1);
- wait_for_completion(&done);
-
return 0;
}
diff --git a/drivers/usb/gadget/udc/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c
index a58242e901df..014233252299 100644
--- a/drivers/usb/gadget/udc/pxa27x_udc.c
+++ b/drivers/usb/gadget/udc/pxa27x_udc.c
@@ -205,50 +205,19 @@ DEFINE_SHOW_ATTRIBUTE(eps_dbg);
static void pxa_init_debugfs(struct pxa_udc *udc)
{
- struct dentry *root, *state, *queues, *eps;
+ struct dentry *root;
root = debugfs_create_dir(udc->gadget.name, NULL);
- if (IS_ERR(root) || !root)
- goto err_root;
-
- state = debugfs_create_file("udcstate", 0400, root, udc,
- &state_dbg_fops);
- if (!state)
- goto err_state;
- queues = debugfs_create_file("queues", 0400, root, udc,
- &queues_dbg_fops);
- if (!queues)
- goto err_queues;
- eps = debugfs_create_file("epstate", 0400, root, udc,
- &eps_dbg_fops);
- if (!eps)
- goto err_eps;
-
udc->debugfs_root = root;
- udc->debugfs_state = state;
- udc->debugfs_queues = queues;
- udc->debugfs_eps = eps;
- return;
-err_eps:
- debugfs_remove(eps);
-err_queues:
- debugfs_remove(queues);
-err_state:
- debugfs_remove(root);
-err_root:
- dev_err(udc->dev, "debugfs is not available\n");
+
+ debugfs_create_file("udcstate", 0400, root, udc, &state_dbg_fops);
+ debugfs_create_file("queues", 0400, root, udc, &queues_dbg_fops);
+ debugfs_create_file("epstate", 0400, root, udc, &eps_dbg_fops);
}
static void pxa_cleanup_debugfs(struct pxa_udc *udc)
{
- debugfs_remove(udc->debugfs_eps);
- debugfs_remove(udc->debugfs_queues);
- debugfs_remove(udc->debugfs_state);
- debugfs_remove(udc->debugfs_root);
- udc->debugfs_eps = NULL;
- udc->debugfs_queues = NULL;
- udc->debugfs_state = NULL;
- udc->debugfs_root = NULL;
+ debugfs_remove_recursive(udc->debugfs_root);
}
#else
diff --git a/drivers/usb/gadget/udc/pxa27x_udc.h b/drivers/usb/gadget/udc/pxa27x_udc.h
index 1128d39a4255..13b2977399ab 100644
--- a/drivers/usb/gadget/udc/pxa27x_udc.h
+++ b/drivers/usb/gadget/udc/pxa27x_udc.h
@@ -476,9 +476,6 @@ struct pxa_udc {
#endif
#ifdef CONFIG_USB_GADGET_DEBUG_FS
struct dentry *debugfs_root;
- struct dentry *debugfs_state;
- struct dentry *debugfs_queues;
- struct dentry *debugfs_eps;
#endif
};
#define to_pxa(g) (container_of((g), struct pxa_udc, gadget))
diff --git a/drivers/usb/gadget/udc/r8a66597-udc.c b/drivers/usb/gadget/udc/r8a66597-udc.c
index a3ecce62662b..11e25a3f4f1f 100644
--- a/drivers/usb/gadget/udc/r8a66597-udc.c
+++ b/drivers/usb/gadget/udc/r8a66597-udc.c
@@ -832,11 +832,11 @@ static void init_controller(struct r8a66597 *r8a66597)
r8a66597_bset(r8a66597, XCKE, SYSCFG0);
- msleep(3);
+ mdelay(3);
r8a66597_bset(r8a66597, PLLC, SYSCFG0);
- msleep(1);
+ mdelay(1);
r8a66597_bset(r8a66597, SCKE, SYSCFG0);
@@ -1190,7 +1190,7 @@ __acquires(r8a66597->lock)
r8a66597->ep0_req->length = 2;
/* AV: what happens if we get called again before that gets through? */
spin_unlock(&r8a66597->lock);
- r8a66597_queue(r8a66597->gadget.ep0, r8a66597->ep0_req, GFP_KERNEL);
+ r8a66597_queue(r8a66597->gadget.ep0, r8a66597->ep0_req, GFP_ATOMIC);
spin_lock(&r8a66597->lock);
}
diff --git a/drivers/usb/gadget/udc/renesas_usb3.c b/drivers/usb/gadget/udc/renesas_usb3.c
index 409cde4e6a51..cdffbd1e0316 100644
--- a/drivers/usb/gadget/udc/renesas_usb3.c
+++ b/drivers/usb/gadget/udc/renesas_usb3.c
@@ -23,6 +23,8 @@
#include <linux/uaccess.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
+#include <linux/usb/of.h>
+#include <linux/usb/role.h>
/* register definitions */
#define USB3_AXI_INT_STA 0x008
@@ -333,6 +335,12 @@ struct renesas_usb3 {
struct extcon_dev *extcon;
struct work_struct extcon_work;
struct phy *phy;
+ struct dentry *dentry;
+
+ struct usb_role_switch *role_sw;
+ struct device *host_dev;
+ struct work_struct role_work;
+ enum usb_role role;
struct renesas_usb3_ep *usb3_ep;
int num_usb3_eps;
@@ -622,6 +630,13 @@ static void usb3_disconnect(struct renesas_usb3 *usb3)
usb3_usb2_pullup(usb3, 0);
usb3_clear_bit(usb3, USB30_CON_B3_CONNECT, USB3_USB30_CON);
usb3_reset_epc(usb3);
+ usb3_disable_irq_1(usb3, USB_INT_1_B2_RSUM | USB_INT_1_B3_PLLWKUP |
+ USB_INT_1_B3_LUPSUCS | USB_INT_1_B3_DISABLE |
+ USB_INT_1_SPEED | USB_INT_1_B3_WRMRST |
+ USB_INT_1_B3_HOTRST | USB_INT_1_B2_SPND |
+ USB_INT_1_B2_L1SPND | USB_INT_1_B2_USBRST);
+ usb3_clear_bit(usb3, USB_COM_CON_SPD_MODE, USB3_USB_COM_CON);
+ usb3_init_epc_registers(usb3);
if (usb3->driver)
usb3->driver->disconnect(&usb3->gadget);
@@ -643,6 +658,14 @@ static void usb3_check_vbus(struct renesas_usb3 *usb3)
}
}
+static void renesas_usb3_role_work(struct work_struct *work)
+{
+ struct renesas_usb3 *usb3 =
+ container_of(work, struct renesas_usb3, role_work);
+
+ usb_role_switch_set_role(usb3->role_sw, usb3->role);
+}
+
static void usb3_set_mode(struct renesas_usb3 *usb3, bool host)
{
if (host)
@@ -651,6 +674,16 @@ static void usb3_set_mode(struct renesas_usb3 *usb3, bool host)
usb3_set_bit(usb3, DRD_CON_PERI_CON, USB3_DRD_CON);
}
+static void usb3_set_mode_by_role_sw(struct renesas_usb3 *usb3, bool host)
+{
+ if (usb3->role_sw) {
+ usb3->role = host ? USB_ROLE_HOST : USB_ROLE_DEVICE;
+ schedule_work(&usb3->role_work);
+ } else {
+ usb3_set_mode(usb3, host);
+ }
+}
+
static void usb3_vbus_out(struct renesas_usb3 *usb3, bool enable)
{
if (enable)
@@ -664,7 +697,7 @@ static void usb3_mode_config(struct renesas_usb3 *usb3, bool host, bool a_dev)
unsigned long flags;
spin_lock_irqsave(&usb3->lock, flags);
- usb3_set_mode(usb3, host);
+ usb3_set_mode_by_role_sw(usb3, host);
usb3_vbus_out(usb3, a_dev);
/* for A-Peripheral or forced B-device mode */
if ((!host && a_dev) ||
@@ -779,12 +812,15 @@ static void usb3_irq_epc_int_1_speed(struct renesas_usb3 *usb3)
switch (speed) {
case USB_STA_SPEED_SS:
usb3->gadget.speed = USB_SPEED_SUPER;
+ usb3->gadget.ep0->maxpacket = USB3_EP0_SS_MAX_PACKET_SIZE;
break;
case USB_STA_SPEED_HS:
usb3->gadget.speed = USB_SPEED_HIGH;
+ usb3->gadget.ep0->maxpacket = USB3_EP0_HSFS_MAX_PACKET_SIZE;
break;
case USB_STA_SPEED_FS:
usb3->gadget.speed = USB_SPEED_FULL;
+ usb3->gadget.ep0->maxpacket = USB3_EP0_HSFS_MAX_PACKET_SIZE;
break;
default:
usb3->gadget.speed = USB_SPEED_UNKNOWN;
@@ -2294,6 +2330,41 @@ static const struct usb_gadget_ops renesas_usb3_gadget_ops = {
.set_selfpowered = renesas_usb3_set_selfpowered,
};
+static enum usb_role renesas_usb3_role_switch_get(struct device *dev)
+{
+ struct renesas_usb3 *usb3 = dev_get_drvdata(dev);
+ enum usb_role cur_role;
+
+ pm_runtime_get_sync(dev);
+ cur_role = usb3_is_host(usb3) ? USB_ROLE_HOST : USB_ROLE_DEVICE;
+ pm_runtime_put(dev);
+
+ return cur_role;
+}
+
+static int renesas_usb3_role_switch_set(struct device *dev,
+ enum usb_role role)
+{
+ struct renesas_usb3 *usb3 = dev_get_drvdata(dev);
+ struct device *host = usb3->host_dev;
+ enum usb_role cur_role = renesas_usb3_role_switch_get(dev);
+
+ pm_runtime_get_sync(dev);
+ if (cur_role == USB_ROLE_HOST && role == USB_ROLE_DEVICE) {
+ device_release_driver(host);
+ usb3_set_mode(usb3, false);
+ } else if (cur_role == USB_ROLE_DEVICE && role == USB_ROLE_HOST) {
+ /* Must set the mode before device_attach of the host */
+ usb3_set_mode(usb3, true);
+ /* This device_attach() might sleep */
+ if (device_attach(host) < 0)
+ dev_err(dev, "device_attach(host) failed\n");
+ }
+ pm_runtime_put(dev);
+
+ return 0;
+}
+
static ssize_t role_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
@@ -2366,6 +2437,9 @@ static ssize_t renesas_usb3_b_device_write(struct file *file,
else
usb3->forced_b_device = false;
+ if (usb3->workaround_for_vbus)
+ usb3_disconnect(usb3);
+
/* Let this driver call usb3_connect() anyway */
usb3_check_id(usb3);
@@ -2383,18 +2457,10 @@ static const struct file_operations renesas_usb3_b_device_fops = {
static void renesas_usb3_debugfs_init(struct renesas_usb3 *usb3,
struct device *dev)
{
- struct dentry *root, *file;
-
- root = debugfs_create_dir(dev_name(dev), NULL);
- if (IS_ERR_OR_NULL(root)) {
- dev_info(dev, "%s: Can't create the root\n", __func__);
- return;
- }
+ usb3->dentry = debugfs_create_dir(dev_name(dev), NULL);
- file = debugfs_create_file("b_device", 0644, root, usb3,
- &renesas_usb3_b_device_fops);
- if (!file)
- dev_info(dev, "%s: Can't create debugfs mode\n", __func__);
+ debugfs_create_file("b_device", 0644, usb3->dentry, usb3,
+ &renesas_usb3_b_device_fops);
}
/*------- platform_driver ------------------------------------------------*/
@@ -2402,14 +2468,15 @@ static int renesas_usb3_remove(struct platform_device *pdev)
{
struct renesas_usb3 *usb3 = platform_get_drvdata(pdev);
+ debugfs_remove_recursive(usb3->dentry);
device_remove_file(&pdev->dev, &dev_attr_role);
+ usb_role_switch_unregister(usb3->role_sw);
+
usb_del_gadget_udc(&usb3->gadget);
renesas_usb3_dma_free_prd(usb3, &pdev->dev);
__renesas_usb3_ep_free_request(usb3->ep0_req);
- if (usb3->phy)
- phy_put(usb3->phy);
pm_runtime_disable(&pdev->dev);
return 0;
@@ -2428,7 +2495,8 @@ static int renesas_usb3_init_ep(struct renesas_usb3 *usb3, struct device *dev,
if (usb3->num_usb3_eps > USB3_MAX_NUM_PIPES)
usb3->num_usb3_eps = USB3_MAX_NUM_PIPES;
- usb3->usb3_ep = devm_kzalloc(dev, sizeof(*usb3_ep) * usb3->num_usb3_eps,
+ usb3->usb3_ep = devm_kcalloc(dev,
+ usb3->num_usb3_eps, sizeof(*usb3_ep),
GFP_KERNEL);
if (!usb3->usb3_ep)
return -ENOMEM;
@@ -2451,7 +2519,7 @@ static int renesas_usb3_init_ep(struct renesas_usb3 *usb3, struct device *dev,
/* for control pipe */
usb3->gadget.ep0 = &usb3_ep->ep;
usb_ep_set_maxpacket_limit(&usb3_ep->ep,
- USB3_EP0_HSFS_MAX_PACKET_SIZE);
+ USB3_EP0_SS_MAX_PACKET_SIZE);
usb3_ep->ep.caps.type_control = true;
usb3_ep->ep.caps.dir_in = true;
usb3_ep->ep.caps.dir_out = true;
@@ -2535,6 +2603,13 @@ static const struct renesas_usb3_priv renesas_usb3_priv_gen3 = {
.ramsize_per_pipe = SZ_4K,
};
+static const struct renesas_usb3_priv renesas_usb3_priv_r8a77990 = {
+ .ramsize_per_ramif = SZ_16K,
+ .num_ramif = 4,
+ .ramsize_per_pipe = SZ_4K,
+ .workaround_for_vbus = true,
+};
+
static const struct of_device_id usb3_of_match[] = {
{
.compatible = "renesas,r8a7795-usb3-peri",
@@ -2553,6 +2628,10 @@ static const struct soc_device_attribute renesas_usb3_quirks_match[] = {
.soc_id = "r8a7795", .revision = "ES1.*",
.data = &renesas_usb3_priv_r8a7795_es1,
},
+ {
+ .soc_id = "r8a77990",
+ .data = &renesas_usb3_priv_r8a77990,
+ },
{ /* sentinel */ },
};
@@ -2562,6 +2641,12 @@ static const unsigned int renesas_usb3_cable[] = {
EXTCON_NONE,
};
+static const struct usb_role_switch_desc renesas_usb3_role_switch_desc = {
+ .set = renesas_usb3_role_switch_set,
+ .get = renesas_usb3_role_switch_get,
+ .allow_userspace_control = true,
+};
+
static int renesas_usb3_probe(struct platform_device *pdev)
{
struct renesas_usb3 *usb3;
@@ -2628,6 +2713,17 @@ static int renesas_usb3_probe(struct platform_device *pdev)
if (ret < 0)
goto err_alloc_prd;
+ /*
+ * This is optional. So, if this driver cannot get a phy,
+ * this driver will not handle a phy anymore.
+ */
+ usb3->phy = devm_phy_optional_get(&pdev->dev, "usb");
+ if (IS_ERR(usb3->phy)) {
+ ret = PTR_ERR(usb3->phy);
+ goto err_add_udc;
+ }
+
+ pm_runtime_enable(&pdev->dev);
ret = usb_add_gadget_udc(&pdev->dev, &usb3->gadget);
if (ret < 0)
goto err_add_udc;
@@ -2636,20 +2732,25 @@ static int renesas_usb3_probe(struct platform_device *pdev)
if (ret < 0)
goto err_dev_create;
- /*
- * This is an optional. So, if this driver cannot get a phy,
- * this driver will not handle a phy anymore.
- */
- usb3->phy = devm_phy_get(&pdev->dev, "usb");
- if (IS_ERR(usb3->phy))
- usb3->phy = NULL;
+ INIT_WORK(&usb3->role_work, renesas_usb3_role_work);
+ usb3->role_sw = usb_role_switch_register(&pdev->dev,
+ &renesas_usb3_role_switch_desc);
+ if (!IS_ERR(usb3->role_sw)) {
+ usb3->host_dev = usb_of_get_companion_dev(&pdev->dev);
+ if (!usb3->host_dev) {
+ /* If not found, this driver will not use a role sw */
+ usb_role_switch_unregister(usb3->role_sw);
+ usb3->role_sw = NULL;
+ }
+ } else {
+ usb3->role_sw = NULL;
+ }
usb3->workaround_for_vbus = priv->workaround_for_vbus;
renesas_usb3_debugfs_init(usb3, &pdev->dev);
dev_info(&pdev->dev, "probed%s\n", usb3->phy ? " with phy" : "");
- pm_runtime_enable(usb3_to_dev(usb3));
return 0;
diff --git a/drivers/usb/gadget/udc/s3c2410_udc.c b/drivers/usb/gadget/udc/s3c2410_udc.c
index f154f49e98c8..8bf5ad7a59ad 100644
--- a/drivers/usb/gadget/udc/s3c2410_udc.c
+++ b/drivers/usb/gadget/udc/s3c2410_udc.c
@@ -1871,13 +1871,9 @@ static int s3c2410_udc_probe(struct platform_device *pdev)
if (retval)
goto err_add_udc;
- if (s3c2410_udc_debugfs_root) {
- udc->regs_info = debugfs_create_file("registers", S_IRUGO,
- s3c2410_udc_debugfs_root,
- udc, &s3c2410_udc_debugfs_fops);
- if (!udc->regs_info)
- dev_warn(dev, "debugfs file creation failed\n");
- }
+ udc->regs_info = debugfs_create_file("registers", S_IRUGO,
+ s3c2410_udc_debugfs_root, udc,
+ &s3c2410_udc_debugfs_fops);
dev_dbg(dev, "probe ok\n");
@@ -1994,11 +1990,6 @@ static int __init udc_init(void)
dprintk(DEBUG_NORMAL, "%s\n", gadget_name);
s3c2410_udc_debugfs_root = debugfs_create_dir(gadget_name, NULL);
- if (IS_ERR(s3c2410_udc_debugfs_root)) {
- pr_err("%s: debugfs dir creation failed %ld\n",
- gadget_name, PTR_ERR(s3c2410_udc_debugfs_root));
- s3c2410_udc_debugfs_root = NULL;
- }
retval = platform_driver_register(&udc_driver_24x0);
if (retval)
@@ -2014,7 +2005,7 @@ err:
static void __exit udc_exit(void)
{
platform_driver_unregister(&udc_driver_24x0);
- debugfs_remove(s3c2410_udc_debugfs_root);
+ debugfs_remove_recursive(s3c2410_udc_debugfs_root);
}
module_init(udc_init);
diff --git a/drivers/usb/gadget/udc/udc-xilinx.c b/drivers/usb/gadget/udc/udc-xilinx.c
index 6407e433bc78..b1f4104d1283 100644
--- a/drivers/usb/gadget/udc/udc-xilinx.c
+++ b/drivers/usb/gadget/udc/udc-xilinx.c
@@ -1078,7 +1078,7 @@ static int xudc_ep_queue(struct usb_ep *_ep, struct usb_request *_req,
unsigned long flags;
if (!ep->desc) {
- dev_dbg(udc->dev, "%s:queing request to disabled %s\n",
+ dev_dbg(udc->dev, "%s: queuing request to disabled %s\n",
__func__, ep->name);
return -ESHUTDOWN;
}
diff --git a/drivers/usb/gadget/usbstring.c b/drivers/usb/gadget/usbstring.c
index 566ab261e8b7..7c24d1ce1088 100644
--- a/drivers/usb/gadget/usbstring.c
+++ b/drivers/usb/gadget/usbstring.c
@@ -33,7 +33,7 @@
* characters (which are also widely used in C strings).
*/
int
-usb_gadget_get_string (struct usb_gadget_strings *table, int id, u8 *buf)
+usb_gadget_get_string (const struct usb_gadget_strings *table, int id, u8 *buf)
{
struct usb_string *s;
int len;
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 5d958da8e1bc..16758b12a5e9 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -52,6 +52,13 @@ config USB_XHCI_PLATFORM
If unsure, say N.
+config USB_XHCI_HISTB
+ tristate "xHCI support for HiSilicon STB SoCs"
+ depends on USB_XHCI_PLATFORM && (ARCH_HISI || COMPILE_TEST)
+ help
+ Say 'Y' to enable the support for the xHCI host controller
+ found in HiSilicon STB SoCs.
+
config USB_XHCI_MTK
tristate "xHCI support for MediaTek SoCs"
select MFD_SYSCON
@@ -185,6 +192,14 @@ config USB_EHCI_MXC
---help---
Variation of ARC USB block used in some Freescale chips.
+config USB_EHCI_HCD_NPCM7XX
+ tristate "Support for Nuvoton NPCM7XX on-chip EHCI USB controller"
+ depends on (USB_EHCI_HCD && ARCH_NPCM7XX) || COMPILE_TEST
+ default y if (USB_EHCI_HCD && ARCH_NPCM7XX)
+ help
+ Enables support for the on-chip EHCI controller on
+ Nuvoton NPCM7XX chips.
+
config USB_EHCI_HCD_OMAP
tristate "EHCI support for OMAP3 and later chips"
depends on ARCH_OMAP
@@ -234,9 +249,7 @@ config USB_EHCI_TEGRA
tristate "NVIDIA Tegra HCD support"
depends on ARCH_TEGRA
select USB_EHCI_ROOT_HUB_TT
- select USB_PHY
- select USB_ULPI
- select USB_ULPI_VIEWPORT
+ select USB_TEGRA_PHY
help
This driver enables support for the internal USB Host Controllers
found in NVIDIA Tegra SoCs. The controllers are EHCI compliant.
@@ -263,7 +276,7 @@ config USB_EHCI_EXYNOS
Enable support for the Samsung Exynos SOC's on-chip EHCI controller.
config USB_EHCI_MV
- bool "EHCI support for Marvell PXA/MMP USB controller"
+ tristate "EHCI support for Marvell PXA/MMP USB controller"
depends on (ARCH_PXA || ARCH_MMP)
select USB_EHCI_ROOT_HUB_TT
---help---
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index 8a8cffe0b445..84514f71ae44 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -43,6 +43,7 @@ obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o
obj-$(CONFIG_USB_EHCI_PCI) += ehci-pci.o
obj-$(CONFIG_USB_EHCI_HCD_PLATFORM) += ehci-platform.o
obj-$(CONFIG_USB_EHCI_MXC) += ehci-mxc.o
+obj-$(CONFIG_USB_EHCI_HCD_NPCM7XX) += ehci-npcm7xx.o
obj-$(CONFIG_USB_EHCI_HCD_OMAP) += ehci-omap.o
obj-$(CONFIG_USB_EHCI_HCD_ORION) += ehci-orion.o
obj-$(CONFIG_USB_EHCI_HCD_SPEAR) += ehci-spear.o
@@ -74,6 +75,7 @@ obj-$(CONFIG_USB_FHCI_HCD) += fhci.o
obj-$(CONFIG_USB_XHCI_HCD) += xhci-hcd.o
obj-$(CONFIG_USB_XHCI_PCI) += xhci-pci.o
obj-$(CONFIG_USB_XHCI_PLATFORM) += xhci-plat-hcd.o
+obj-$(CONFIG_USB_XHCI_HISTB) += xhci-histb.o
obj-$(CONFIG_USB_XHCI_MTK) += xhci-mtk.o
obj-$(CONFIG_USB_XHCI_TEGRA) += xhci-tegra.o
obj-$(CONFIG_USB_SL811_HCD) += sl811-hcd.o
@@ -85,6 +87,7 @@ obj-$(CONFIG_USB_IMX21_HCD) += imx21-hcd.o
obj-$(CONFIG_USB_FSL_USB2) += fsl-mph-dr-of.o
obj-$(CONFIG_USB_EHCI_FSL) += fsl-mph-dr-of.o
obj-$(CONFIG_USB_EHCI_FSL) += ehci-fsl.o
+obj-$(CONFIG_USB_EHCI_MV) += ehci-mv.o
obj-$(CONFIG_USB_HCD_BCMA) += bcma-hcd.o
obj-$(CONFIG_USB_HCD_SSB) += ssb-hcd.o
obj-$(CONFIG_USB_FOTG210_HCD) += fotg210-hcd.o
diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c
index 3ed75aaa09d9..7619cfb06883 100644
--- a/drivers/usb/host/ehci-dbg.c
+++ b/drivers/usb/host/ehci-dbg.c
@@ -1028,29 +1028,15 @@ static inline void create_debug_files(struct ehci_hcd *ehci)
struct usb_bus *bus = &ehci_to_hcd(ehci)->self;
ehci->debug_dir = debugfs_create_dir(bus->bus_name, ehci_debug_root);
- if (!ehci->debug_dir)
- return;
- if (!debugfs_create_file("async", S_IRUGO, ehci->debug_dir, bus,
- &debug_async_fops))
- goto file_error;
-
- if (!debugfs_create_file("bandwidth", S_IRUGO, ehci->debug_dir, bus,
- &debug_bandwidth_fops))
- goto file_error;
-
- if (!debugfs_create_file("periodic", S_IRUGO, ehci->debug_dir, bus,
- &debug_periodic_fops))
- goto file_error;
-
- if (!debugfs_create_file("registers", S_IRUGO, ehci->debug_dir, bus,
- &debug_registers_fops))
- goto file_error;
-
- return;
-
-file_error:
- debugfs_remove_recursive(ehci->debug_dir);
+ debugfs_create_file("async", S_IRUGO, ehci->debug_dir, bus,
+ &debug_async_fops);
+ debugfs_create_file("bandwidth", S_IRUGO, ehci->debug_dir, bus,
+ &debug_bandwidth_fops);
+ debugfs_create_file("periodic", S_IRUGO, ehci->debug_dir, bus,
+ &debug_periodic_fops);
+ debugfs_create_file("registers", S_IRUGO, ehci->debug_dir, bus,
+ &debug_registers_fops);
}
static inline void remove_debug_files(struct ehci_hcd *ehci)
diff --git a/drivers/usb/host/ehci-exynos.c b/drivers/usb/host/ehci-exynos.c
index d9145a8f35d2..8e3bab1e0c1f 100644
--- a/drivers/usb/host/ehci-exynos.c
+++ b/drivers/usb/host/ehci-exynos.c
@@ -161,16 +161,10 @@ static int exynos_ehci_probe(struct platform_device *pdev)
}
exynos_ehci = to_exynos_ehci(hcd);
- if (of_device_is_compatible(pdev->dev.of_node,
- "samsung,exynos5440-ehci"))
- goto skip_phy;
-
err = exynos_ehci_get_phy(&pdev->dev, exynos_ehci);
if (err)
goto fail_clk;
-skip_phy:
-
exynos_ehci->clk = devm_clk_get(&pdev->dev, "usbhost");
if (IS_ERR(exynos_ehci->clk)) {
@@ -304,7 +298,6 @@ static const struct dev_pm_ops exynos_ehci_pm_ops = {
#ifdef CONFIG_OF
static const struct of_device_id exynos_ehci_match[] = {
{ .compatible = "samsung,exynos4210-ehci" },
- { .compatible = "samsung,exynos5440-ehci" },
{},
};
MODULE_DEVICE_TABLE(of, exynos_ehci_match);
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index d927adf3afcd..cdafa97f632d 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -730,9 +730,9 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)
/* normal [4.15.1.2] or error [4.15.1.1] completion */
if (likely ((status & (STS_INT|STS_ERR)) != 0)) {
if (likely ((status & STS_ERR) == 0))
- COUNT (ehci->stats.normal);
+ INCR(ehci->stats.normal);
else
- COUNT (ehci->stats.error);
+ INCR(ehci->stats.error);
bh = 1;
}
@@ -756,7 +756,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)
if (cmd & CMD_IAAD)
ehci_dbg(ehci, "IAA with IAAD still set?\n");
if (ehci->iaa_in_progress)
- COUNT(ehci->stats.iaa);
+ INCR(ehci->stats.iaa);
end_iaa_cycle(ehci);
}
@@ -1226,6 +1226,7 @@ static const struct hc_driver ehci_hc_driver = {
.bus_resume = ehci_bus_resume,
.relinquish_port = ehci_relinquish_port,
.port_handed_over = ehci_port_handed_over,
+ .get_resuming_ports = ehci_get_resuming_ports,
/*
* device support
@@ -1285,11 +1286,6 @@ MODULE_LICENSE ("GPL");
#define PLATFORM_DRIVER ehci_grlib_driver
#endif
-#ifdef CONFIG_USB_EHCI_MV
-#include "ehci-mv.c"
-#define PLATFORM_DRIVER ehci_mv_driver
-#endif
-
static int __init ehci_hcd_init(void)
{
int retval = 0;
@@ -1311,10 +1307,6 @@ static int __init ehci_hcd_init(void)
#ifdef CONFIG_DYNAMIC_DEBUG
ehci_debug_root = debugfs_create_dir("ehci", usb_debug_root);
- if (!ehci_debug_root) {
- retval = -ENOENT;
- goto err_debug;
- }
#endif
#ifdef PLATFORM_DRIVER
@@ -1361,7 +1353,6 @@ clean0:
#ifdef CONFIG_DYNAMIC_DEBUG
debugfs_remove(ehci_debug_root);
ehci_debug_root = NULL;
-err_debug:
#endif
clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded);
return retval;
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index d7641cbdee43..ce0eaf7d7c12 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -512,10 +512,18 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
return -ESHUTDOWN;
}
+static unsigned long ehci_get_resuming_ports(struct usb_hcd *hcd)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+
+ return ehci->resuming_ports;
+}
+
#else
#define ehci_bus_suspend NULL
#define ehci_bus_resume NULL
+#define ehci_get_resuming_ports NULL
#endif /* CONFIG_PM */
diff --git a/drivers/usb/host/ehci-mv.c b/drivers/usb/host/ehci-mv.c
index de764459e05a..f26109eafdbf 100644
--- a/drivers/usb/host/ehci-mv.c
+++ b/drivers/usb/host/ehci-mv.c
@@ -12,24 +12,33 @@
#include <linux/err.h>
#include <linux/usb/otg.h>
#include <linux/platform_data/mv_usb.h>
+#include <linux/io.h>
+
+#include <linux/usb/hcd.h>
+
+#include "ehci.h"
+
+/* registers */
+#define U2x_CAPREGS_OFFSET 0x100
#define CAPLENGTH_MASK (0xff)
-struct ehci_hcd_mv {
- struct usb_hcd *hcd;
+#define hcd_to_ehci_hcd_mv(h) ((struct ehci_hcd_mv *)hcd_to_ehci(h)->priv)
+struct ehci_hcd_mv {
/* Which mode does this ehci running OTG/Host ? */
int mode;
- void __iomem *phy_regs;
+ void __iomem *base;
void __iomem *cap_regs;
void __iomem *op_regs;
struct usb_phy *otg;
+ struct clk *clk;
- struct mv_usb_platform_data *pdata;
+ struct phy *phy;
- struct clk *clk;
+ int (*set_vbus)(unsigned int vbus);
};
static void ehci_clock_enable(struct ehci_hcd_mv *ehci_mv)
@@ -44,29 +53,20 @@ static void ehci_clock_disable(struct ehci_hcd_mv *ehci_mv)
static int mv_ehci_enable(struct ehci_hcd_mv *ehci_mv)
{
- int retval;
-
ehci_clock_enable(ehci_mv);
- if (ehci_mv->pdata->phy_init) {
- retval = ehci_mv->pdata->phy_init(ehci_mv->phy_regs);
- if (retval)
- return retval;
- }
-
- return 0;
+ return phy_init(ehci_mv->phy);
}
static void mv_ehci_disable(struct ehci_hcd_mv *ehci_mv)
{
- if (ehci_mv->pdata->phy_deinit)
- ehci_mv->pdata->phy_deinit(ehci_mv->phy_regs);
+ phy_exit(ehci_mv->phy);
ehci_clock_disable(ehci_mv);
}
static int mv_ehci_reset(struct usb_hcd *hcd)
{
struct device *dev = hcd->self.controller;
- struct ehci_hcd_mv *ehci_mv = dev_get_drvdata(dev);
+ struct ehci_hcd_mv *ehci_mv = hcd_to_ehci_hcd_mv(hcd);
int retval;
if (ehci_mv == NULL) {
@@ -83,46 +83,11 @@ static int mv_ehci_reset(struct usb_hcd *hcd)
return retval;
}
-static const struct hc_driver mv_ehci_hc_driver = {
- .description = hcd_name,
- .product_desc = "Marvell EHCI",
- .hcd_priv_size = sizeof(struct ehci_hcd),
-
- /*
- * generic hardware linkage
- */
- .irq = ehci_irq,
- .flags = HCD_MEMORY | HCD_USB2 | HCD_BH,
-
- /*
- * basic lifecycle operations
- */
- .reset = mv_ehci_reset,
- .start = ehci_run,
- .stop = ehci_stop,
- .shutdown = ehci_shutdown,
-
- /*
- * managing i/o requests and associated device resources
- */
- .urb_enqueue = ehci_urb_enqueue,
- .urb_dequeue = ehci_urb_dequeue,
- .endpoint_disable = ehci_endpoint_disable,
- .endpoint_reset = ehci_endpoint_reset,
- .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
-
- /*
- * scheduling support
- */
- .get_frame_number = ehci_get_frame,
-
- /*
- * root hub support
- */
- .hub_status_data = ehci_hub_status_data,
- .hub_control = ehci_hub_control,
- .bus_suspend = ehci_bus_suspend,
- .bus_resume = ehci_bus_resume,
+static struct hc_driver __read_mostly ehci_platform_hc_driver;
+
+static const struct ehci_driver_overrides platform_overrides __initconst = {
+ .reset = mv_ehci_reset,
+ .extra_priv_size = sizeof(struct ehci_hcd_mv),
};
static int mv_ehci_probe(struct platform_device *pdev)
@@ -135,27 +100,29 @@ static int mv_ehci_probe(struct platform_device *pdev)
int retval = -ENODEV;
u32 offset;
- if (!pdata) {
- dev_err(&pdev->dev, "missing platform_data\n");
- return -ENODEV;
- }
-
if (usb_disabled())
return -ENODEV;
- hcd = usb_create_hcd(&mv_ehci_hc_driver, &pdev->dev, "mv ehci");
+ hcd = usb_create_hcd(&ehci_platform_hc_driver, &pdev->dev, "mv ehci");
if (!hcd)
return -ENOMEM;
- ehci_mv = devm_kzalloc(&pdev->dev, sizeof(*ehci_mv), GFP_KERNEL);
- if (ehci_mv == NULL) {
- retval = -ENOMEM;
- goto err_put_hcd;
+ platform_set_drvdata(pdev, hcd);
+ ehci_mv = hcd_to_ehci_hcd_mv(hcd);
+
+ ehci_mv->mode = MV_USB_MODE_HOST;
+ if (pdata) {
+ ehci_mv->mode = pdata->mode;
+ ehci_mv->set_vbus = pdata->set_vbus;
}
- platform_set_drvdata(pdev, ehci_mv);
- ehci_mv->pdata = pdata;
- ehci_mv->hcd = hcd;
+ ehci_mv->phy = devm_phy_get(&pdev->dev, "usb");
+ if (IS_ERR(ehci_mv->phy)) {
+ retval = PTR_ERR(ehci_mv->phy);
+ if (retval != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "Failed to get phy.\n");
+ goto err_put_hcd;
+ }
ehci_mv->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(ehci_mv->clk)) {
@@ -164,17 +131,12 @@ static int mv_ehci_probe(struct platform_device *pdev)
goto err_put_hcd;
}
- r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phyregs");
- ehci_mv->phy_regs = devm_ioremap_resource(&pdev->dev, r);
- if (IS_ERR(ehci_mv->phy_regs)) {
- retval = PTR_ERR(ehci_mv->phy_regs);
- goto err_put_hcd;
- }
- r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "capregs");
- ehci_mv->cap_regs = devm_ioremap_resource(&pdev->dev, r);
- if (IS_ERR(ehci_mv->cap_regs)) {
- retval = PTR_ERR(ehci_mv->cap_regs);
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ ehci_mv->base = devm_ioremap_resource(&pdev->dev, r);
+ if (IS_ERR(ehci_mv->base)) {
+ retval = PTR_ERR(ehci_mv->base);
goto err_put_hcd;
}
@@ -184,6 +146,8 @@ static int mv_ehci_probe(struct platform_device *pdev)
goto err_put_hcd;
}
+ ehci_mv->cap_regs =
+ (void __iomem *) ((unsigned long) ehci_mv->base + U2x_CAPREGS_OFFSET);
offset = readl(ehci_mv->cap_regs) & CAPLENGTH_MASK;
ehci_mv->op_regs =
(void __iomem *) ((unsigned long) ehci_mv->cap_regs + offset);
@@ -202,7 +166,6 @@ static int mv_ehci_probe(struct platform_device *pdev)
ehci = hcd_to_ehci(hcd);
ehci->caps = (struct ehci_caps *) ehci_mv->cap_regs;
- ehci_mv->mode = pdata->mode;
if (ehci_mv->mode == MV_USB_MODE_OTG) {
ehci_mv->otg = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
if (IS_ERR(ehci_mv->otg)) {
@@ -227,8 +190,8 @@ static int mv_ehci_probe(struct platform_device *pdev)
/* otg will enable clock before use as host */
mv_ehci_disable(ehci_mv);
} else {
- if (pdata->set_vbus)
- pdata->set_vbus(1);
+ if (ehci_mv->set_vbus)
+ ehci_mv->set_vbus(1);
retval = usb_add_hcd(hcd, hcd->irq, IRQF_SHARED);
if (retval) {
@@ -239,9 +202,6 @@ static int mv_ehci_probe(struct platform_device *pdev)
device_wakeup_enable(hcd->self.controller);
}
- if (pdata->private_init)
- pdata->private_init(ehci_mv->op_regs, ehci_mv->phy_regs);
-
dev_info(&pdev->dev,
"successful find EHCI device with regs 0x%p irq %d"
" working in %s mode\n", hcd->regs, hcd->irq,
@@ -250,8 +210,8 @@ static int mv_ehci_probe(struct platform_device *pdev)
return 0;
err_set_vbus:
- if (pdata->set_vbus)
- pdata->set_vbus(0);
+ if (ehci_mv->set_vbus)
+ ehci_mv->set_vbus(0);
err_disable_clk:
mv_ehci_disable(ehci_mv);
err_put_hcd:
@@ -262,8 +222,8 @@ err_put_hcd:
static int mv_ehci_remove(struct platform_device *pdev)
{
- struct ehci_hcd_mv *ehci_mv = platform_get_drvdata(pdev);
- struct usb_hcd *hcd = ehci_mv->hcd;
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct ehci_hcd_mv *ehci_mv = hcd_to_ehci_hcd_mv(hcd);
if (hcd->rh_registered)
usb_remove_hcd(hcd);
@@ -272,8 +232,8 @@ static int mv_ehci_remove(struct platform_device *pdev)
otg_set_host(ehci_mv->otg->otg, NULL);
if (ehci_mv->mode == MV_USB_MODE_HOST) {
- if (ehci_mv->pdata->set_vbus)
- ehci_mv->pdata->set_vbus(0);
+ if (ehci_mv->set_vbus)
+ ehci_mv->set_vbus(0);
mv_ehci_disable(ehci_mv);
}
@@ -295,8 +255,7 @@ static const struct platform_device_id ehci_id_table[] = {
static void mv_ehci_shutdown(struct platform_device *pdev)
{
- struct ehci_hcd_mv *ehci_mv = platform_get_drvdata(pdev);
- struct usb_hcd *hcd = ehci_mv->hcd;
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
if (!hcd->rh_registered)
return;
@@ -305,13 +264,41 @@ static void mv_ehci_shutdown(struct platform_device *pdev)
hcd->driver->shutdown(hcd);
}
+static const struct of_device_id ehci_mv_dt_ids[] = {
+ { .compatible = "marvell,pxau2o-ehci", },
+ {},
+};
+
static struct platform_driver ehci_mv_driver = {
.probe = mv_ehci_probe,
.remove = mv_ehci_remove,
.shutdown = mv_ehci_shutdown,
.driver = {
- .name = "mv-ehci",
- .bus = &platform_bus_type,
- },
+ .name = "mv-ehci",
+ .bus = &platform_bus_type,
+ .of_match_table = ehci_mv_dt_ids,
+ },
.id_table = ehci_id_table,
};
+
+static int __init ehci_platform_init(void)
+{
+ if (usb_disabled())
+ return -ENODEV;
+
+ ehci_init_driver(&ehci_platform_hc_driver, &platform_overrides);
+ return platform_driver_register(&ehci_mv_driver);
+}
+module_init(ehci_platform_init);
+
+static void __exit ehci_platform_cleanup(void)
+{
+ platform_driver_unregister(&ehci_mv_driver);
+}
+module_exit(ehci_platform_cleanup);
+
+MODULE_DESCRIPTION("Marvell EHCI driver");
+MODULE_AUTHOR("Chao Xie <chao.xie@marvell.com>");
+MODULE_AUTHOR("Neil Zhang <zhangwm@marvell.com>");
+MODULE_ALIAS("mv-ehci");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/host/ehci-npcm7xx.c b/drivers/usb/host/ehci-npcm7xx.c
new file mode 100644
index 000000000000..adaf8fb4b459
--- /dev/null
+++ b/drivers/usb/host/ehci-npcm7xx.c
@@ -0,0 +1,212 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Nuvoton NPCM7xx driver for EHCI HCD
+ *
+ * Copyright (C) 2018 Nuvoton Technologies,
+ * Avi Fishman <avi.fishman@nuvoton.com> <avifishman70@gmail.com>
+ * Tomer Maimon <tomer.maimon@nuvoton.com> <tmaimon77@gmail.com>
+ *
+ * Based on various ehci-spear.c driver
+ */
+
+
+#include <linux/dma-mapping.h>
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+
+#include "ehci.h"
+
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+
+#define DRIVER_DESC "EHCI npcm7xx driver"
+
+static const char hcd_name[] = "npcm7xx-ehci";
+
+#define USB2PHYCTL_OFFSET 0x144
+
+#define IPSRST2_OFFSET 0x24
+#define IPSRST3_OFFSET 0x34
+
+
+static struct hc_driver __read_mostly ehci_npcm7xx_hc_driver;
+
+#ifdef CONFIG_PM_SLEEP
+static int ehci_npcm7xx_drv_suspend(struct device *dev)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ bool do_wakeup = device_may_wakeup(dev);
+
+ return ehci_suspend(hcd, do_wakeup);
+}
+
+static int ehci_npcm7xx_drv_resume(struct device *dev)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+
+ ehci_resume(hcd, false);
+ return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static SIMPLE_DEV_PM_OPS(ehci_npcm7xx_pm_ops, ehci_npcm7xx_drv_suspend,
+ ehci_npcm7xx_drv_resume);
+
+static int npcm7xx_ehci_hcd_drv_probe(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd;
+ struct resource *res;
+ struct regmap *gcr_regmap;
+ struct regmap *rst_regmap;
+ const struct hc_driver *driver = &ehci_npcm7xx_hc_driver;
+ int irq;
+ int retval;
+
+ dev_dbg(&pdev->dev, "initializing npcm7xx ehci USB Controller\n");
+
+ gcr_regmap = syscon_regmap_lookup_by_compatible("nuvoton,npcm750-gcr");
+ if (IS_ERR(gcr_regmap)) {
+ dev_err(&pdev->dev, "%s: failed to find nuvoton,npcm750-gcr\n",
+ __func__);
+ return PTR_ERR(gcr_regmap);
+ }
+
+ rst_regmap = syscon_regmap_lookup_by_compatible("nuvoton,npcm750-rst");
+ if (IS_ERR(rst_regmap)) {
+ dev_err(&pdev->dev, "%s: failed to find nuvoton,npcm750-rst\n",
+ __func__);
+ return PTR_ERR(rst_regmap);
+ }
+
+ /********* phy init ******/
+ // reset usb host
+ regmap_update_bits(rst_regmap, IPSRST2_OFFSET,
+ (0x1 << 26), (0x1 << 26));
+ regmap_update_bits(rst_regmap, IPSRST3_OFFSET,
+ (0x1 << 25), (0x1 << 25));
+ regmap_update_bits(gcr_regmap, USB2PHYCTL_OFFSET,
+ (0x1 << 28), 0);
+
+ udelay(1);
+
+ // enable phy
+ regmap_update_bits(rst_regmap, IPSRST3_OFFSET,
+ (0x1 << 25), 0);
+
+ udelay(50); // enable phy
+
+ regmap_update_bits(gcr_regmap, USB2PHYCTL_OFFSET,
+ (0x1 << 28), (0x1 << 28));
+
+ // enable host
+ regmap_update_bits(rst_regmap, IPSRST2_OFFSET,
+ (0x1 << 26), 0);
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ retval = irq;
+ goto fail;
+ }
+
+ /*
+ * Right now device-tree probed devices don't get dma_mask set.
+ * Since shared usb code relies on it, set it here for now.
+ * Once we have dma capability bindings this can go away.
+ */
+ retval = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+ if (retval)
+ goto fail;
+
+ hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
+ if (!hcd) {
+ retval = -ENOMEM;
+ goto fail;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(hcd->regs)) {
+ retval = PTR_ERR(hcd->regs);
+ goto err_put_hcd;
+ }
+ hcd->rsrc_start = res->start;
+ hcd->rsrc_len = resource_size(res);
+
+ /* registers start at offset 0x0 */
+ hcd_to_ehci(hcd)->caps = hcd->regs;
+
+ retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
+ if (retval)
+ goto err_put_hcd;
+
+ device_wakeup_enable(hcd->self.controller);
+ return retval;
+
+err_put_hcd:
+ usb_put_hcd(hcd);
+fail:
+ dev_err(&pdev->dev, "init fail, %d\n", retval);
+
+ return retval;
+}
+
+static int npcm7xx_ehci_hcd_drv_remove(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+
+ usb_remove_hcd(hcd);
+
+ usb_put_hcd(hcd);
+
+ return 0;
+}
+
+static const struct of_device_id npcm7xx_ehci_id_table[] = {
+ { .compatible = "nuvoton,npcm750-ehci" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, npcm7xx_ehci_id_table);
+
+static struct platform_driver npcm7xx_ehci_hcd_driver = {
+ .probe = npcm7xx_ehci_hcd_drv_probe,
+ .remove = npcm7xx_ehci_hcd_drv_remove,
+ .shutdown = usb_hcd_platform_shutdown,
+ .driver = {
+ .name = "npcm7xx-ehci",
+ .bus = &platform_bus_type,
+ .pm = &ehci_npcm7xx_pm_ops,
+ .of_match_table = npcm7xx_ehci_id_table,
+ }
+};
+
+static int __init ehci_npcm7xx_init(void)
+{
+ if (usb_disabled())
+ return -ENODEV;
+
+ pr_info("%s: " DRIVER_DESC "\n", hcd_name);
+
+ ehci_init_driver(&ehci_npcm7xx_hc_driver, NULL);
+ return platform_driver_register(&npcm7xx_ehci_hcd_driver);
+}
+module_init(ehci_npcm7xx_init);
+
+static void __exit ehci_npcm7xx_cleanup(void)
+{
+ platform_driver_unregister(&npcm7xx_ehci_hcd_driver);
+}
+module_exit(ehci_npcm7xx_cleanup);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_ALIAS("platform:npcm7xx-ehci");
+MODULE_AUTHOR("Avi Fishman");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c
index 8d8bafc70c1f..7e4c13346a1e 100644
--- a/drivers/usb/host/ehci-omap.c
+++ b/drivers/usb/host/ehci-omap.c
@@ -157,10 +157,7 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev)
struct usb_phy *phy;
/* get the PHY device */
- if (dev->of_node)
- phy = devm_usb_get_phy_by_phandle(dev, "phys", i);
- else
- phy = devm_usb_get_phy_dev(dev, i);
+ phy = devm_usb_get_phy_by_phandle(dev, "phys", i);
if (IS_ERR(phy)) {
/* Don't bail out if PHY is not absolutely necessary */
if (pdata->port_mode[i] != OMAP_EHCI_PORT_MODE_PHY)
diff --git a/drivers/usb/host/ehci-ps3.c b/drivers/usb/host/ehci-ps3.c
index 8c733492d8fe..454d8c624a3f 100644
--- a/drivers/usb/host/ehci-ps3.c
+++ b/drivers/usb/host/ehci-ps3.c
@@ -86,7 +86,7 @@ static int ps3_ehci_probe(struct ps3_system_bus_device *dev)
int result;
struct usb_hcd *hcd;
unsigned int virq;
- static u64 dummy_mask = DMA_BIT_MASK(32);
+ static u64 dummy_mask;
if (usb_disabled()) {
result = -ENODEV;
@@ -131,7 +131,9 @@ static int ps3_ehci_probe(struct ps3_system_bus_device *dev)
goto fail_irq;
}
- dev->core.dma_mask = &dummy_mask; /* FIXME: for improper usb code */
+ dummy_mask = DMA_BIT_MASK(32);
+ dev->core.dma_mask = &dummy_mask;
+ dma_set_coherent_mask(&dev->core, dummy_mask);
hcd = usb_create_hcd(&ps3_ehci_hc_driver, &dev->core, dev_name(&dev->core));
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
index 327630405695..aa2f77f1506d 100644
--- a/drivers/usb/host/ehci-q.c
+++ b/drivers/usb/host/ehci-q.c
@@ -245,12 +245,12 @@ ehci_urb_done(struct ehci_hcd *ehci, struct urb *urb, int status)
}
if (unlikely(urb->unlinked)) {
- COUNT(ehci->stats.unlink);
+ INCR(ehci->stats.unlink);
} else {
/* report non-error and short read status as zero */
if (status == -EINPROGRESS || status == -EREMOTEIO)
status = 0;
- COUNT(ehci->stats.complete);
+ INCR(ehci->stats.complete);
}
#ifdef EHCI_URB_TRACE
diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c
index e56db44708bc..da7b00a6110b 100644
--- a/drivers/usb/host/ehci-sched.c
+++ b/drivers/usb/host/ehci-sched.c
@@ -117,8 +117,9 @@ 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 = kzalloc(utt->hub->maxchild *
- sizeof(*tt_index), GFP_ATOMIC);
+ tt_index = kcalloc(utt->hub->maxchild,
+ sizeof(*tt_index),
+ GFP_ATOMIC);
if (!tt_index)
return ERR_PTR(-ENOMEM);
utt->hcpriv = tt_index;
@@ -1834,7 +1835,6 @@ static bool itd_complete(struct ehci_hcd *ehci, struct ehci_itd *itd)
unsigned uframe;
int urb_index = -1;
struct ehci_iso_stream *stream = itd->stream;
- struct usb_device *dev;
bool retval = false;
/* for each uframe with a packet */
@@ -1885,7 +1885,6 @@ static bool itd_complete(struct ehci_hcd *ehci, struct ehci_itd *itd)
*/
/* give urb back to the driver; completion often (re)submits */
- dev = urb->dev;
ehci_urb_done(ehci, urb, 0);
retval = true;
urb = NULL;
@@ -2229,7 +2228,6 @@ static bool sitd_complete(struct ehci_hcd *ehci, struct ehci_sitd *sitd)
u32 t;
int urb_index;
struct ehci_iso_stream *stream = sitd->stream;
- struct usb_device *dev;
bool retval = false;
urb_index = sitd->index;
@@ -2267,7 +2265,6 @@ static bool sitd_complete(struct ehci_hcd *ehci, struct ehci_sitd *sitd)
*/
/* give urb back to the driver; completion often (re)submits */
- dev = urb->dev;
ehci_urb_done(ehci, urb, 0);
retval = true;
urb = NULL;
diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c
index a6f4389f7e88..4d2cdec4cb78 100644
--- a/drivers/usb/host/ehci-tegra.c
+++ b/drivers/usb/host/ehci-tegra.c
@@ -36,7 +36,6 @@
#define DRV_NAME "tegra-ehci"
static struct hc_driver __read_mostly tegra_ehci_hc_driver;
-static bool usb1_reset_attempted;
struct tegra_ehci_soc_config {
bool has_hostpc;
@@ -51,67 +50,54 @@ struct tegra_ehci_hcd {
enum tegra_usb_phy_port_speed port_speed;
};
-/*
- * The 1st USB controller contains some UTMI pad registers that are global for
- * all the controllers on the chip. Those registers are also cleared when
- * reset is asserted to the 1st controller. This means that the 1st controller
- * can only be reset when no other controlled has finished probing. So we'll
- * reset the 1st controller before doing any other setup on any of the
- * controllers, and then never again.
- *
- * Since this is a PHY issue, the Tegra PHY driver should probably be doing
- * the resetting of the USB controllers. But to keep compatibility with old
- * device trees that don't have reset phandles in the PHYs, do it here.
- * Those old DTs will be vulnerable to total USB breakage if the 1st EHCI
- * device isn't the first one to finish probing, so warn them.
- */
static int tegra_reset_usb_controller(struct platform_device *pdev)
{
struct device_node *phy_np;
struct usb_hcd *hcd = platform_get_drvdata(pdev);
struct tegra_ehci_hcd *tegra =
(struct tegra_ehci_hcd *)hcd_to_ehci(hcd)->priv;
- bool has_utmi_pad_registers = false;
+ struct reset_control *rst;
+ int err;
phy_np = of_parse_phandle(pdev->dev.of_node, "nvidia,phy", 0);
if (!phy_np)
return -ENOENT;
- if (of_property_read_bool(phy_np, "nvidia,has-utmi-pad-registers"))
- has_utmi_pad_registers = true;
+ /*
+ * The 1st USB controller contains some UTMI pad registers that are
+ * global for all the controllers on the chip. Those registers are
+ * also cleared when reset is asserted to the 1st controller.
+ */
+ rst = of_reset_control_get_shared(phy_np, "utmi-pads");
+ if (IS_ERR(rst)) {
+ dev_warn(&pdev->dev,
+ "can't get utmi-pads reset from the PHY\n");
+ dev_warn(&pdev->dev,
+ "continuing, but please update your DT\n");
+ } else {
+ /*
+ * PHY driver performs UTMI-pads reset in a case of
+ * non-legacy DT.
+ */
+ reset_control_put(rst);
+ }
- if (!usb1_reset_attempted) {
- struct reset_control *usb1_reset;
+ of_node_put(phy_np);
- if (!has_utmi_pad_registers)
- usb1_reset = of_reset_control_get(phy_np, "utmi-pads");
- else
- usb1_reset = tegra->rst;
-
- if (IS_ERR(usb1_reset)) {
- dev_warn(&pdev->dev,
- "can't get utmi-pads reset from the PHY\n");
- dev_warn(&pdev->dev,
- "continuing, but please update your DT\n");
- } else {
- reset_control_assert(usb1_reset);
- udelay(1);
- reset_control_deassert(usb1_reset);
-
- if (!has_utmi_pad_registers)
- reset_control_put(usb1_reset);
- }
+ /* reset control is shared, hence initialize it first */
+ err = reset_control_deassert(tegra->rst);
+ if (err)
+ return err;
- usb1_reset_attempted = true;
- }
+ err = reset_control_assert(tegra->rst);
+ if (err)
+ return err;
- if (!has_utmi_pad_registers) {
- reset_control_assert(tegra->rst);
- udelay(1);
- reset_control_deassert(tegra->rst);
- }
+ udelay(1);
- of_node_put(phy_np);
+ err = reset_control_deassert(tegra->rst);
+ if (err)
+ return err;
return 0;
}
@@ -440,7 +426,7 @@ static int tegra_ehci_probe(struct platform_device *pdev)
goto cleanup_hcd_create;
}
- tegra->rst = devm_reset_control_get(&pdev->dev, "usb");
+ tegra->rst = devm_reset_control_get_shared(&pdev->dev, "usb");
if (IS_ERR(tegra->rst)) {
dev_err(&pdev->dev, "Can't get ehci reset\n");
err = PTR_ERR(tegra->rst);
@@ -452,8 +438,10 @@ static int tegra_ehci_probe(struct platform_device *pdev)
goto cleanup_hcd_create;
err = tegra_reset_usb_controller(pdev);
- if (err)
+ if (err) {
+ dev_err(&pdev->dev, "Failed to reset controller\n");
goto cleanup_clk_en;
+ }
u_phy = devm_usb_get_phy_by_phandle(&pdev->dev, "nvidia,phy", 0);
if (IS_ERR(u_phy)) {
@@ -538,6 +526,9 @@ static int tegra_ehci_remove(struct platform_device *pdev)
usb_phy_shutdown(hcd->usb_phy);
usb_remove_hcd(hcd);
+ reset_control_assert(tegra->rst);
+ udelay(1);
+
clk_disable_unprepare(tegra->clk);
usb_put_hcd(hcd);
diff --git a/drivers/usb/host/ehci-timer.c b/drivers/usb/host/ehci-timer.c
index 4fcebda4b79d..a79c8ac0a55f 100644
--- a/drivers/usb/host/ehci-timer.c
+++ b/drivers/usb/host/ehci-timer.c
@@ -347,7 +347,7 @@ static void ehci_iaa_watchdog(struct ehci_hcd *ehci)
*/
status = ehci_readl(ehci, &ehci->regs->status);
if ((status & STS_IAA) || !(cmd & CMD_IAAD)) {
- COUNT(ehci->stats.lost_iaa);
+ INCR(ehci->stats.lost_iaa);
ehci_writel(ehci, STS_IAA, &ehci->regs->status);
}
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index c8e9a48e1d51..ac5e967907d1 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -235,9 +235,9 @@ struct ehci_hcd { /* one per controller */
/* irq statistics */
#ifdef EHCI_STATS
struct ehci_stats stats;
-# define COUNT(x) ((x)++)
+# define INCR(x) ((x)++)
#else
-# define COUNT(x)
+# define INCR(x) do {} while (0)
#endif
/* debug files */
diff --git a/drivers/usb/host/fhci-dbg.c b/drivers/usb/host/fhci-dbg.c
index ebf9bb219f75..100048b3bd17 100644
--- a/drivers/usb/host/fhci-dbg.c
+++ b/drivers/usb/host/fhci-dbg.c
@@ -83,27 +83,14 @@ void fhci_dfs_create(struct fhci_hcd *fhci)
struct device *dev = fhci_to_hcd(fhci)->self.controller;
fhci->dfs_root = debugfs_create_dir(dev_name(dev), usb_debug_root);
- if (!fhci->dfs_root) {
- WARN_ON(1);
- return;
- }
-
- fhci->dfs_regs = debugfs_create_file("regs", S_IFREG | S_IRUGO,
- fhci->dfs_root, fhci, &fhci_dfs_regs_fops);
- fhci->dfs_irq_stat = debugfs_create_file("irq_stat",
- S_IFREG | S_IRUGO, fhci->dfs_root, fhci,
- &fhci_dfs_irq_stat_fops);
-
- WARN_ON(!fhci->dfs_regs || !fhci->dfs_irq_stat);
+ debugfs_create_file("regs", S_IFREG | S_IRUGO, fhci->dfs_root, fhci,
+ &fhci_dfs_regs_fops);
+ debugfs_create_file("irq_stat", S_IFREG | S_IRUGO, fhci->dfs_root, fhci,
+ &fhci_dfs_irq_stat_fops);
}
void fhci_dfs_destroy(struct fhci_hcd *fhci)
{
- if (!fhci->dfs_root)
- return;
-
- debugfs_remove(fhci->dfs_irq_stat);
- debugfs_remove(fhci->dfs_regs);
- debugfs_remove(fhci->dfs_root);
+ debugfs_remove_recursive(fhci->dfs_root);
}
diff --git a/drivers/usb/host/fhci-tds.c b/drivers/usb/host/fhci-tds.c
index 3a4e8f616751..f3308ce25043 100644
--- a/drivers/usb/host/fhci-tds.c
+++ b/drivers/usb/host/fhci-tds.c
@@ -189,7 +189,7 @@ u32 fhci_create_ep(struct fhci_usb *usb, enum fhci_mem_alloc data_mem,
goto err;
}
- buff = kmalloc(1028 * sizeof(*buff), GFP_KERNEL);
+ buff = kmalloc_array(1028, sizeof(*buff), GFP_KERNEL);
if (!buff) {
kfree(pkt);
err_for = "buffer";
diff --git a/drivers/usb/host/fhci.h b/drivers/usb/host/fhci.h
index e7ec41d62410..2ce5031d866d 100644
--- a/drivers/usb/host/fhci.h
+++ b/drivers/usb/host/fhci.h
@@ -262,8 +262,6 @@ struct fhci_hcd {
#ifdef CONFIG_FHCI_DEBUG
int usb_irq_stat[13];
struct dentry *dfs_root;
- struct dentry *dfs_regs;
- struct dentry *dfs_irq_stat;
#endif
};
diff --git a/drivers/usb/host/fotg210-hcd.c b/drivers/usb/host/fotg210-hcd.c
index d8abf401918a..0da68df259c8 100644
--- a/drivers/usb/host/fotg210-hcd.c
+++ b/drivers/usb/host/fotg210-hcd.c
@@ -31,6 +31,7 @@
#include <linux/uaccess.h>
#include <linux/platform_device.h>
#include <linux/io.h>
+#include <linux/clk.h>
#include <asm/byteorder.h>
#include <asm/irq.h>
@@ -844,28 +845,16 @@ static int debug_registers_open(struct inode *inode, struct file *file)
static inline void create_debug_files(struct fotg210_hcd *fotg210)
{
struct usb_bus *bus = &fotg210_to_hcd(fotg210)->self;
+ struct dentry *root;
- fotg210->debug_dir = debugfs_create_dir(bus->bus_name,
- fotg210_debug_root);
- if (!fotg210->debug_dir)
- return;
-
- if (!debugfs_create_file("async", S_IRUGO, fotg210->debug_dir, bus,
- &debug_async_fops))
- goto file_error;
-
- if (!debugfs_create_file("periodic", S_IRUGO, fotg210->debug_dir, bus,
- &debug_periodic_fops))
- goto file_error;
+ root = debugfs_create_dir(bus->bus_name, fotg210_debug_root);
+ fotg210->debug_dir = root;
- if (!debugfs_create_file("registers", S_IRUGO, fotg210->debug_dir, bus,
- &debug_registers_fops))
- goto file_error;
-
- return;
-
-file_error:
- debugfs_remove_recursive(fotg210->debug_dir);
+ debugfs_create_file("async", S_IRUGO, root, bus, &debug_async_fops);
+ debugfs_create_file("periodic", S_IRUGO, root, bus,
+ &debug_periodic_fops);
+ debugfs_create_file("registers", S_IRUGO, root, bus,
+ &debug_registers_fops);
}
static inline void remove_debug_files(struct fotg210_hcd *fotg210)
@@ -1297,7 +1286,7 @@ static void fotg210_iaa_watchdog(struct fotg210_hcd *fotg210)
*/
status = fotg210_readl(fotg210, &fotg210->regs->status);
if ((status & STS_IAA) || !(cmd & CMD_IAAD)) {
- COUNT(fotg210->stats.lost_iaa);
+ INCR(fotg210->stats.lost_iaa);
fotg210_writel(fotg210, STS_IAA,
&fotg210->regs->status);
}
@@ -2216,12 +2205,12 @@ __acquires(fotg210->lock)
}
if (unlikely(urb->unlinked)) {
- COUNT(fotg210->stats.unlink);
+ INCR(fotg210->stats.unlink);
} else {
/* report non-error and short read status as zero */
if (status == -EINPROGRESS || status == -EREMOTEIO)
status = 0;
- COUNT(fotg210->stats.complete);
+ INCR(fotg210->stats.complete);
}
#ifdef FOTG210_URB_TRACE
@@ -5165,9 +5154,9 @@ static irqreturn_t fotg210_irq(struct usb_hcd *hcd)
/* normal [4.15.1.2] or error [4.15.1.1] completion */
if (likely((status & (STS_INT|STS_ERR)) != 0)) {
if (likely((status & STS_ERR) == 0))
- COUNT(fotg210->stats.normal);
+ INCR(fotg210->stats.normal);
else
- COUNT(fotg210->stats.error);
+ INCR(fotg210->stats.error);
bh = 1;
}
@@ -5192,7 +5181,7 @@ static irqreturn_t fotg210_irq(struct usb_hcd *hcd)
if (cmd & CMD_IAAD)
fotg210_dbg(fotg210, "IAA with IAAD still set?\n");
if (fotg210->async_iaa) {
- COUNT(fotg210->stats.iaa);
+ INCR(fotg210->stats.iaa);
end_unlink_async(fotg210);
} else
fotg210_dbg(fotg210, "IAA with nothing unlinked?\n");
@@ -5608,7 +5597,7 @@ static int fotg210_hcd_probe(struct platform_device *pdev)
hcd->regs = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(hcd->regs)) {
retval = PTR_ERR(hcd->regs);
- goto failed;
+ goto failed_put_hcd;
}
hcd->rsrc_start = res->start;
@@ -5618,22 +5607,43 @@ static int fotg210_hcd_probe(struct platform_device *pdev)
fotg210->caps = hcd->regs;
+ /* It's OK not to supply this clock */
+ fotg210->pclk = clk_get(dev, "PCLK");
+ if (!IS_ERR(fotg210->pclk)) {
+ retval = clk_prepare_enable(fotg210->pclk);
+ if (retval) {
+ dev_err(dev, "failed to enable PCLK\n");
+ goto failed_put_hcd;
+ }
+ } else if (PTR_ERR(fotg210->pclk) == -EPROBE_DEFER) {
+ /*
+ * Percolate deferrals, for anything else,
+ * just live without the clocking.
+ */
+ retval = PTR_ERR(fotg210->pclk);
+ goto failed_dis_clk;
+ }
+
retval = fotg210_setup(hcd);
if (retval)
- goto failed;
+ goto failed_dis_clk;
fotg210_init(fotg210);
retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
if (retval) {
dev_err(dev, "failed to add hcd with err %d\n", retval);
- goto failed;
+ goto failed_dis_clk;
}
device_wakeup_enable(hcd->self.controller);
+ platform_set_drvdata(pdev, hcd);
return retval;
-failed:
+failed_dis_clk:
+ if (!IS_ERR(fotg210->pclk))
+ clk_disable_unprepare(fotg210->pclk);
+failed_put_hcd:
usb_put_hcd(hcd);
fail_create_hcd:
dev_err(dev, "init %s fail, %d\n", dev_name(dev), retval);
@@ -5647,11 +5657,11 @@ fail_create_hcd:
*/
static int fotg210_hcd_remove(struct platform_device *pdev)
{
- struct device *dev = &pdev->dev;
- struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd);
- if (!hcd)
- return 0;
+ if (!IS_ERR(fotg210->pclk))
+ clk_disable_unprepare(fotg210->pclk);
usb_remove_hcd(hcd);
usb_put_hcd(hcd);
@@ -5686,10 +5696,6 @@ static int __init fotg210_hcd_init(void)
sizeof(struct fotg210_itd));
fotg210_debug_root = debugfs_create_dir("fotg210", usb_debug_root);
- if (!fotg210_debug_root) {
- retval = -ENOENT;
- goto err_debug;
- }
retval = platform_driver_register(&fotg210_hcd_driver);
if (retval < 0)
@@ -5699,7 +5705,7 @@ static int __init fotg210_hcd_init(void)
clean:
debugfs_remove(fotg210_debug_root);
fotg210_debug_root = NULL;
-err_debug:
+
clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded);
return retval;
}
diff --git a/drivers/usb/host/fotg210.h b/drivers/usb/host/fotg210.h
index 7fcd785c7bc8..1b4db95e5c43 100644
--- a/drivers/usb/host/fotg210.h
+++ b/drivers/usb/host/fotg210.h
@@ -177,11 +177,14 @@ struct fotg210_hcd { /* one per controller */
/* irq statistics */
#ifdef FOTG210_STATS
struct fotg210_stats stats;
-# define COUNT(x) ((x)++)
+# define INCR(x) ((x)++)
#else
-# define COUNT(x)
+# define INCR(x) do {} while (0)
#endif
+ /* silicon clock */
+ struct clk *pclk;
+
/* debug files */
struct dentry *debug_dir;
};
diff --git a/drivers/usb/host/hwa-hc.c b/drivers/usb/host/hwa-hc.c
index 684d6f074c3a..09a8ebd95588 100644
--- a/drivers/usb/host/hwa-hc.c
+++ b/drivers/usb/host/hwa-hc.c
@@ -640,7 +640,7 @@ static int hwahc_security_create(struct hwahc *hwahc)
top = itr + itr_size;
result = __usb_get_extra_descriptor(usb_dev->rawdescriptors[index],
le16_to_cpu(usb_dev->actconfig->desc.wTotalLength),
- USB_DT_SECURITY, (void **) &secd);
+ USB_DT_SECURITY, (void **) &secd, sizeof(*secd));
if (result == -1) {
dev_warn(dev, "BUG? WUSB host has no security descriptors\n");
return 0;
diff --git a/drivers/usb/host/imx21-dbg.c b/drivers/usb/host/imx21-dbg.c
index a213ed6f07b5..7fcf1d9dd7f3 100644
--- a/drivers/usb/host/imx21-dbg.c
+++ b/drivers/usb/host/imx21-dbg.c
@@ -417,46 +417,22 @@ DEFINE_SHOW_ATTRIBUTE(debug_isoc);
static void create_debug_files(struct imx21 *imx21)
{
- imx21->debug_root = debugfs_create_dir(dev_name(imx21->dev), NULL);
- if (!imx21->debug_root)
- goto failed_create_rootdir;
+ struct dentry *root;
- if (!debugfs_create_file("status", S_IRUGO,
- imx21->debug_root, imx21, &debug_status_fops))
- goto failed_create;
+ root = debugfs_create_dir(dev_name(imx21->dev), NULL);
+ imx21->debug_root = root;
- if (!debugfs_create_file("dmem", S_IRUGO,
- imx21->debug_root, imx21, &debug_dmem_fops))
- goto failed_create;
-
- if (!debugfs_create_file("etd", S_IRUGO,
- imx21->debug_root, imx21, &debug_etd_fops))
- goto failed_create;
-
- if (!debugfs_create_file("statistics", S_IRUGO,
- imx21->debug_root, imx21, &debug_statistics_fops))
- goto failed_create;
-
- if (!debugfs_create_file("isoc", S_IRUGO,
- imx21->debug_root, imx21, &debug_isoc_fops))
- goto failed_create;
-
- return;
-
-failed_create:
- debugfs_remove_recursive(imx21->debug_root);
-
-failed_create_rootdir:
- imx21->debug_root = NULL;
+ debugfs_create_file("status", S_IRUGO, root, imx21, &debug_status_fops);
+ debugfs_create_file("dmem", S_IRUGO, root, imx21, &debug_dmem_fops);
+ debugfs_create_file("etd", S_IRUGO, root, imx21, &debug_etd_fops);
+ debugfs_create_file("statistics", S_IRUGO, root, imx21,
+ &debug_statistics_fops);
+ debugfs_create_file("isoc", S_IRUGO, root, imx21, &debug_isoc_fops);
}
-
static void remove_debug_files(struct imx21 *imx21)
{
- if (imx21->debug_root) {
- debugfs_remove_recursive(imx21->debug_root);
- imx21->debug_root = NULL;
- }
+ debugfs_remove_recursive(imx21->debug_root);
}
#endif
diff --git a/drivers/usb/host/imx21-hcd.c b/drivers/usb/host/imx21-hcd.c
index 3a8bbfe43a8e..6e3dad19d369 100644
--- a/drivers/usb/host/imx21-hcd.c
+++ b/drivers/usb/host/imx21-hcd.c
@@ -741,8 +741,8 @@ static int imx21_hc_urb_enqueue_isoc(struct usb_hcd *hcd,
if (urb_priv == NULL)
return -ENOMEM;
- urb_priv->isoc_td = kzalloc(
- sizeof(struct td) * urb->number_of_packets, mem_flags);
+ urb_priv->isoc_td = kcalloc(urb->number_of_packets, sizeof(struct td),
+ mem_flags);
if (urb_priv->isoc_td == NULL) {
ret = -ENOMEM;
goto alloc_td_failed;
diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c
index 4602ed801f0a..74da136d322a 100644
--- a/drivers/usb/host/isp116x-hcd.c
+++ b/drivers/usb/host/isp116x-hcd.c
@@ -1198,14 +1198,11 @@ static int isp116x_debug_show(struct seq_file *s, void *unused)
}
DEFINE_SHOW_ATTRIBUTE(isp116x_debug);
-static int create_debug_file(struct isp116x *isp116x)
+static void create_debug_file(struct isp116x *isp116x)
{
isp116x->dentry = debugfs_create_file(hcd_name,
S_IRUGO, NULL, isp116x,
&isp116x_debug_fops);
- if (!isp116x->dentry)
- return -ENOMEM;
- return 0;
}
static void remove_debug_file(struct isp116x *isp116x)
@@ -1215,8 +1212,8 @@ static void remove_debug_file(struct isp116x *isp116x)
#else
-#define create_debug_file(d) 0
-#define remove_debug_file(d) do{}while(0)
+static inline void create_debug_file(struct isp116x *isp116x) { }
+static inline void remove_debug_file(struct isp116x *isp116x) { }
#endif /* CONFIG_DEBUG_FS */
@@ -1643,16 +1640,10 @@ static int isp116x_probe(struct platform_device *pdev)
device_wakeup_enable(hcd->self.controller);
- ret = create_debug_file(isp116x);
- if (ret) {
- ERR("Couldn't create debugfs entry\n");
- goto err7;
- }
+ create_debug_file(isp116x);
return 0;
- err7:
- usb_remove_hcd(hcd);
err6:
usb_put_hcd(hcd);
err5:
diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c
index 5ad9e9bdc8ee..ec6739ef3129 100644
--- a/drivers/usb/host/ohci-at91.c
+++ b/drivers/usb/host/ohci-at91.c
@@ -212,7 +212,7 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver,
ohci_at91->sfr_regmap = at91_dt_syscon_sfr();
if (!ohci_at91->sfr_regmap)
- dev_warn(dev, "failed to find sfr node\n");
+ dev_dbg(dev, "failed to find sfr node\n");
board = hcd->self.controller->platform_data;
ohci = hcd_to_ohci(hcd);
@@ -551,6 +551,8 @@ static int ohci_hcd_at91_drv_probe(struct platform_device *pdev)
pdata->overcurrent_pin[i] =
devm_gpiod_get_index_optional(&pdev->dev, "atmel,oc",
i, GPIOD_IN);
+ if (!pdata->overcurrent_pin[i])
+ continue;
if (IS_ERR(pdata->overcurrent_pin[i])) {
err = PTR_ERR(pdata->overcurrent_pin[i]);
dev_err(&pdev->dev, "unable to claim gpio \"overcurrent\": %d\n", err);
diff --git a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c
index ac7d4ac34b02..4f267dc93882 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(DBG_SCHED_LIMIT * sizeof *seen, GFP_ATOMIC);
+ seen = kmalloc_array(DBG_SCHED_LIMIT, sizeof(*seen), GFP_ATOMIC);
if (!seen)
return 0;
seen_count = 0;
@@ -762,50 +762,23 @@ static int debug_registers_open(struct inode *inode, struct file *file)
static inline void create_debug_files (struct ohci_hcd *ohci)
{
struct usb_bus *bus = &ohci_to_hcd(ohci)->self;
+ struct dentry *root;
- ohci->debug_dir = debugfs_create_dir(bus->bus_name, ohci_debug_root);
- if (!ohci->debug_dir)
- goto dir_error;
+ root = debugfs_create_dir(bus->bus_name, ohci_debug_root);
+ ohci->debug_dir = root;
- ohci->debug_async = debugfs_create_file("async", S_IRUGO,
- ohci->debug_dir, ohci,
- &debug_async_fops);
- if (!ohci->debug_async)
- goto async_error;
-
- ohci->debug_periodic = debugfs_create_file("periodic", S_IRUGO,
- ohci->debug_dir, ohci,
- &debug_periodic_fops);
- if (!ohci->debug_periodic)
- goto periodic_error;
-
- ohci->debug_registers = debugfs_create_file("registers", S_IRUGO,
- ohci->debug_dir, ohci,
- &debug_registers_fops);
- if (!ohci->debug_registers)
- goto registers_error;
+ debugfs_create_file("async", S_IRUGO, root, ohci, &debug_async_fops);
+ debugfs_create_file("periodic", S_IRUGO, root, ohci,
+ &debug_periodic_fops);
+ debugfs_create_file("registers", S_IRUGO, root, ohci,
+ &debug_registers_fops);
ohci_dbg (ohci, "created debug files\n");
- return;
-
-registers_error:
- debugfs_remove(ohci->debug_periodic);
-periodic_error:
- debugfs_remove(ohci->debug_async);
-async_error:
- debugfs_remove(ohci->debug_dir);
-dir_error:
- ohci->debug_periodic = NULL;
- ohci->debug_async = NULL;
- ohci->debug_dir = NULL;
}
static inline void remove_debug_files (struct ohci_hcd *ohci)
{
- debugfs_remove(ohci->debug_registers);
- debugfs_remove(ohci->debug_periodic);
- debugfs_remove(ohci->debug_async);
- debugfs_remove(ohci->debug_dir);
+ debugfs_remove_recursive(ohci->debug_dir);
}
/*-------------------------------------------------------------------------*/
diff --git a/drivers/usb/host/ohci-exynos.c b/drivers/usb/host/ohci-exynos.c
index a39fae41bc70..c0c4dcca6f3c 100644
--- a/drivers/usb/host/ohci-exynos.c
+++ b/drivers/usb/host/ohci-exynos.c
@@ -130,15 +130,10 @@ static int exynos_ohci_probe(struct platform_device *pdev)
exynos_ohci = to_exynos_ohci(hcd);
- if (of_device_is_compatible(pdev->dev.of_node,
- "samsung,exynos5440-ohci"))
- goto skip_phy;
-
err = exynos_ohci_get_phy(&pdev->dev, exynos_ohci);
if (err)
goto fail_clk;
-skip_phy:
exynos_ohci->clk = devm_clk_get(&pdev->dev, "usbhost");
if (IS_ERR(exynos_ohci->clk)) {
@@ -270,7 +265,6 @@ static const struct dev_pm_ops exynos_ohci_pm_ops = {
#ifdef CONFIG_OF
static const struct of_device_id exynos_ohci_match[] = {
{ .compatible = "samsung,exynos4210-ohci" },
- { .compatible = "samsung,exynos5440-ohci" },
{},
};
MODULE_DEVICE_TABLE(of, exynos_ohci_match);
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index 4806e0f9e8d4..210181fd98d2 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -1258,10 +1258,6 @@ static int __init ohci_hcd_mod_init(void)
set_bit(USB_OHCI_LOADED, &usb_hcds_loaded);
ohci_debug_root = debugfs_create_dir("ohci", usb_debug_root);
- if (!ohci_debug_root) {
- retval = -ENOENT;
- goto error_debug;
- }
#ifdef PS3_SYSTEM_BUS_DRIVER
retval = ps3_ohci_driver_register(&PS3_SYSTEM_BUS_DRIVER);
@@ -1318,7 +1314,6 @@ static int __init ohci_hcd_mod_init(void)
#endif
debugfs_remove(ohci_debug_root);
ohci_debug_root = NULL;
- error_debug:
clear_bit(USB_OHCI_LOADED, &usb_hcds_loaded);
return retval;
diff --git a/drivers/usb/host/ohci-ps3.c b/drivers/usb/host/ohci-ps3.c
index 20a23d795adf..395f9d3bc849 100644
--- a/drivers/usb/host/ohci-ps3.c
+++ b/drivers/usb/host/ohci-ps3.c
@@ -69,7 +69,7 @@ static int ps3_ohci_probe(struct ps3_system_bus_device *dev)
int result;
struct usb_hcd *hcd;
unsigned int virq;
- static u64 dummy_mask = DMA_BIT_MASK(32);
+ static u64 dummy_mask;
if (usb_disabled()) {
result = -ENODEV;
@@ -115,7 +115,9 @@ static int ps3_ohci_probe(struct ps3_system_bus_device *dev)
goto fail_irq;
}
- dev->core.dma_mask = &dummy_mask; /* FIXME: for improper usb code */
+ dummy_mask = DMA_BIT_MASK(32);
+ dev->core.dma_mask = &dummy_mask;
+ dma_set_coherent_mask(&dev->core, dummy_mask);
hcd = usb_create_hcd(&ps3_ohci_hc_driver, &dev->core, dev_name(&dev->core));
diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h
index 508a803139dd..ef4813bfc5bf 100644
--- a/drivers/usb/host/ohci.h
+++ b/drivers/usb/host/ohci.h
@@ -431,9 +431,6 @@ struct ohci_hcd {
struct work_struct nec_work; /* Worker for NEC quirk */
struct dentry *debug_dir;
- struct dentry *debug_async;
- struct dentry *debug_periodic;
- struct dentry *debug_registers;
/* platform-specific data -- must come last */
unsigned long priv[0] __aligned(sizeof(s64));
diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c
index 67ad4bb6919a..3ce71cbfbb58 100644
--- a/drivers/usb/host/pci-quirks.c
+++ b/drivers/usb/host/pci-quirks.c
@@ -783,15 +783,9 @@ static void quirk_usb_handoff_ohci(struct pci_dev *pdev)
/* disable interrupts */
writel((u32) ~0, base + OHCI_INTRDISABLE);
- /* Reset the USB bus, if the controller isn't already in RESET */
- if (control & OHCI_HCFS) {
- /* Go into RESET, preserving RWC (and possibly IR) */
- writel(control & OHCI_CTRL_MASK, base + OHCI_CONTROL);
- readl(base + OHCI_CONTROL);
-
- /* drive bus reset for at least 50 ms (7.1.7.5) */
- msleep(50);
- }
+ /* Go into the USB_RESET state, preserving RWC (and possibly IR) */
+ writel(control & OHCI_CTRL_MASK, base + OHCI_CONTROL);
+ readl(base + OHCI_CONTROL);
/* software reset of the controller, preserving HcFmInterval */
if (!no_fminterval)
@@ -1268,23 +1262,3 @@ static void quirk_usb_early_handoff(struct pci_dev *pdev)
}
DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_ANY_ID, PCI_ANY_ID,
PCI_CLASS_SERIAL_USB, 8, quirk_usb_early_handoff);
-
-bool usb_xhci_needs_pci_reset(struct pci_dev *pdev)
-{
- /*
- * Our dear uPD72020{1,2} friend only partially resets when
- * asked to via the XHCI interface, and may end up doing DMA
- * at the wrong addresses, as it keeps the top 32bit of some
- * addresses from its previous programming under obscure
- * circumstances.
- * Give it a good wack at probe time. Unfortunately, this
- * needs to happen before we've had a chance to discover any
- * quirk, or the system will be in a rather bad state.
- */
- if (pdev->vendor == PCI_VENDOR_ID_RENESAS &&
- (pdev->device == 0x0014 || pdev->device == 0x0015))
- return true;
-
- return false;
-}
-EXPORT_SYMBOL_GPL(usb_xhci_needs_pci_reset);
diff --git a/drivers/usb/host/pci-quirks.h b/drivers/usb/host/pci-quirks.h
index 4ca0d9b7e463..63c633077d9e 100644
--- a/drivers/usb/host/pci-quirks.h
+++ b/drivers/usb/host/pci-quirks.h
@@ -16,7 +16,6 @@ void usb_asmedia_modifyflowcontrol(struct pci_dev *pdev);
void usb_enable_intel_xhci_ports(struct pci_dev *xhci_pdev);
void usb_disable_xhci_ports(struct pci_dev *xhci_pdev);
void sb800_prefetch(struct device *dev, int on);
-bool usb_xhci_needs_pci_reset(struct pci_dev *pdev);
bool usb_amd_pt_check_port(struct device *device, int port);
#else
struct pci_dev;
diff --git a/drivers/usb/host/u132-hcd.c b/drivers/usb/host/u132-hcd.c
index 032b8652910a..5b8a3d9530c4 100644
--- a/drivers/usb/host/u132-hcd.c
+++ b/drivers/usb/host/u132-hcd.c
@@ -2555,7 +2555,7 @@ static int u132_get_frame(struct usb_hcd *hcd)
} else {
int frame = 0;
dev_err(&u132->platform_dev->dev, "TODO: u132_get_frame\n");
- msleep(100);
+ mdelay(100);
return frame;
}
}
@@ -3062,7 +3062,6 @@ static int u132_probe(struct platform_device *pdev)
int retval;
u32 control;
u32 rh_a = -1;
- u32 num_ports;
msleep(100);
if (u132_exiting > 0)
@@ -3077,7 +3076,6 @@ static int u132_probe(struct platform_device *pdev)
retval = ftdi_read_pcimem(pdev, roothub.a, &rh_a);
if (retval)
return retval;
- num_ports = rh_a & RH_A_NDP; /* refuse to confuse usbcore */
if (pdev->dev.dma_mask)
return -EINVAL;
diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c
index f9c3947577fc..6218bfe54f52 100644
--- a/drivers/usb/host/uhci-hcd.c
+++ b/drivers/usb/host/uhci-hcd.c
@@ -590,14 +590,10 @@ static int uhci_start(struct usb_hcd *hcd)
init_waitqueue_head(&uhci->waitqh);
#ifdef UHCI_DEBUG_OPS
- dentry = debugfs_create_file(hcd->self.bus_name,
- S_IFREG|S_IRUGO|S_IWUSR, uhci_debugfs_root,
- uhci, &uhci_debug_operations);
- if (!dentry) {
- dev_err(uhci_dev(uhci), "couldn't create uhci debugfs entry\n");
- return -ENOMEM;
- }
- uhci->dentry = dentry;
+ uhci->dentry = debugfs_create_file(hcd->self.bus_name,
+ S_IFREG|S_IRUGO|S_IWUSR,
+ uhci_debugfs_root, uhci,
+ &uhci_debug_operations);
#endif
uhci->frame = dma_zalloc_coherent(uhci_dev(uhci),
@@ -882,8 +878,6 @@ static int __init uhci_hcd_init(void)
if (!errbuf)
goto errbuf_failed;
uhci_debugfs_root = debugfs_create_dir("uhci", usb_debug_root);
- if (!uhci_debugfs_root)
- goto debug_failed;
#endif
uhci_up_cachep = kmem_cache_create("uhci_urb_priv",
@@ -918,7 +912,6 @@ up_failed:
#if defined(DEBUG) || defined(CONFIG_DYNAMIC_DEBUG)
debugfs_remove(uhci_debugfs_root);
-debug_failed:
kfree(errbuf);
errbuf_failed:
diff --git a/drivers/usb/host/whci/pzl.c b/drivers/usb/host/whci/pzl.c
index bb84366f7bd3..ef52aeb02fde 100644
--- a/drivers/usb/host/whci/pzl.c
+++ b/drivers/usb/host/whci/pzl.c
@@ -96,9 +96,7 @@ static enum whc_update pzl_process_qset(struct whc *whc, struct whc_qset *qset)
while (qset->ntds) {
struct whc_qtd *td;
- int t;
- t = qset->td_start;
td = &qset->qtd[qset->td_start];
status = le32_to_cpu(td->status);
diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c
index c359bae7b754..86cff5c28eff 100644
--- a/drivers/usb/host/xhci-dbgcap.c
+++ b/drivers/usb/host/xhci-dbgcap.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/**
* xhci-dbgcap.c - xHCI debug capability support
*
@@ -507,16 +508,18 @@ static int xhci_do_dbc_start(struct xhci_hcd *xhci)
return 0;
}
-static void xhci_do_dbc_stop(struct xhci_hcd *xhci)
+static int xhci_do_dbc_stop(struct xhci_hcd *xhci)
{
struct xhci_dbc *dbc = xhci->dbc;
if (dbc->state == DS_DISABLED)
- return;
+ return -1;
writel(0, &dbc->regs->control);
xhci_dbc_mem_cleanup(xhci);
dbc->state = DS_DISABLED;
+
+ return 0;
}
static int xhci_dbc_start(struct xhci_hcd *xhci)
@@ -543,6 +546,7 @@ static int xhci_dbc_start(struct xhci_hcd *xhci)
static void xhci_dbc_stop(struct xhci_hcd *xhci)
{
+ int ret;
unsigned long flags;
struct xhci_dbc *dbc = xhci->dbc;
struct dbc_port *port = &dbc->port;
@@ -555,10 +559,11 @@ static void xhci_dbc_stop(struct xhci_hcd *xhci)
xhci_dbc_tty_unregister_device(xhci);
spin_lock_irqsave(&dbc->lock, flags);
- xhci_do_dbc_stop(xhci);
+ ret = xhci_do_dbc_stop(xhci);
spin_unlock_irqrestore(&dbc->lock, flags);
- pm_runtime_put_sync(xhci_to_hcd(xhci)->self.controller);
+ if (!ret)
+ pm_runtime_put_sync(xhci_to_hcd(xhci)->self.controller);
}
static void
@@ -908,11 +913,9 @@ static ssize_t dbc_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
- struct xhci_dbc *dbc;
struct xhci_hcd *xhci;
xhci = hcd_to_xhci(dev_get_drvdata(dev));
- dbc = xhci->dbc;
if (!strncmp(buf, "enable", 6))
xhci_dbc_start(xhci);
diff --git a/drivers/usb/host/xhci-dbgcap.h b/drivers/usb/host/xhci-dbgcap.h
index e66ea0748ba3..ce0c6072bd48 100644
--- a/drivers/usb/host/xhci-dbgcap.h
+++ b/drivers/usb/host/xhci-dbgcap.h
@@ -1,4 +1,4 @@
-
+/* SPDX-License-Identifier: GPL-2.0 */
/**
* xhci-dbgcap.h - xHCI debug capability support
*
diff --git a/drivers/usb/host/xhci-dbgtty.c b/drivers/usb/host/xhci-dbgtty.c
index eb494ec547e8..aff79ff5aba4 100644
--- a/drivers/usb/host/xhci-dbgtty.c
+++ b/drivers/usb/host/xhci-dbgtty.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/**
* xhci-dbgtty.c - tty glue for xHCI debug capability
*
diff --git a/drivers/usb/host/xhci-debugfs.c b/drivers/usb/host/xhci-debugfs.c
index 5851052d4668..cadc01336bf8 100644
--- a/drivers/usb/host/xhci-debugfs.c
+++ b/drivers/usb/host/xhci-debugfs.c
@@ -8,6 +8,7 @@
*/
#include <linux/slab.h>
+#include <linux/uaccess.h>
#include "xhci.h"
#include "xhci-debugfs.h"
@@ -333,6 +334,67 @@ static const struct file_operations xhci_context_fops = {
.release = single_release,
};
+
+
+static int xhci_portsc_show(struct seq_file *s, void *unused)
+{
+ struct xhci_port *port = s->private;
+ u32 portsc;
+
+ portsc = readl(port->addr);
+ seq_printf(s, "%s\n", xhci_decode_portsc(portsc));
+
+ return 0;
+}
+
+static int xhci_port_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, xhci_portsc_show, inode->i_private);
+}
+
+static ssize_t xhci_port_write(struct file *file, const char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct seq_file *s = file->private_data;
+ struct xhci_port *port = s->private;
+ struct xhci_hcd *xhci = hcd_to_xhci(port->rhub->hcd);
+ char buf[32];
+ u32 portsc;
+ unsigned long flags;
+
+ if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+ return -EFAULT;
+
+ if (!strncmp(buf, "compliance", 10)) {
+ /* If CTC is clear, compliance is enabled by default */
+ if (!HCC2_CTC(xhci->hcc_params2))
+ return count;
+ spin_lock_irqsave(&xhci->lock, flags);
+ /* compliance mode can only be enabled on ports in RxDetect */
+ portsc = readl(port->addr);
+ if ((portsc & PORT_PLS_MASK) != XDEV_RXDETECT) {
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ return -EPERM;
+ }
+ portsc = xhci_port_state_to_neutral(portsc);
+ portsc &= ~PORT_PLS_MASK;
+ portsc |= PORT_LINK_STROBE | XDEV_COMP_MODE;
+ writel(portsc, port->addr);
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ } else {
+ return -EINVAL;
+ }
+ return count;
+}
+
+static const struct file_operations port_fops = {
+ .open = xhci_port_open,
+ .write = xhci_port_write,
+ .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,
@@ -449,6 +511,27 @@ void xhci_debugfs_remove_slot(struct xhci_hcd *xhci, int slot_id)
dev->debugfs_private = NULL;
}
+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);
+ dir = debugfs_create_dir(port_name, parent);
+ port = &xhci->hw_ports[num_ports];
+ debugfs_create_file("portsc", 0644, dir, port, &port_fops);
+ }
+}
+
void xhci_debugfs_init(struct xhci_hcd *xhci)
{
struct device *dev = xhci_to_hcd(xhci)->self.controller;
@@ -497,6 +580,8 @@ void xhci_debugfs_init(struct xhci_hcd *xhci)
xhci->debugfs_root);
xhci->debugfs_slots = debugfs_create_dir("devices", xhci->debugfs_root);
+
+ xhci_debugfs_create_ports(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
new file mode 100644
index 000000000000..3c4abb5a1c3f
--- /dev/null
+++ b/drivers/usb/host/xhci-histb.c
@@ -0,0 +1,412 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * xHCI host controller driver for HiSilicon STB SoCs
+ *
+ * Copyright (C) 2017-2018 HiSilicon Co., Ltd. http://www.hisilicon.com
+ *
+ * Authors: Jianguo Sun <sunjianguo1@huawei.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+
+#include "xhci.h"
+
+#define GTXTHRCFG 0xc108
+#define GRXTHRCFG 0xc10c
+#define REG_GUSB2PHYCFG0 0xc200
+#define BIT_UTMI_8_16 BIT(3)
+#define BIT_UTMI_ULPI BIT(4)
+#define BIT_FREECLK_EXIST BIT(30)
+
+#define REG_GUSB3PIPECTL0 0xc2c0
+#define USB3_DEEMPHASIS_MASK GENMASK(2, 1)
+#define USB3_DEEMPHASIS0 BIT(1)
+#define USB3_TX_MARGIN1 BIT(4)
+
+struct xhci_hcd_histb {
+ struct device *dev;
+ struct usb_hcd *hcd;
+ void __iomem *ctrl;
+ struct clk *bus_clk;
+ struct clk *utmi_clk;
+ struct clk *pipe_clk;
+ struct clk *suspend_clk;
+ struct reset_control *soft_reset;
+};
+
+static inline struct xhci_hcd_histb *hcd_to_histb(struct usb_hcd *hcd)
+{
+ return dev_get_drvdata(hcd->self.controller);
+}
+
+static int xhci_histb_config(struct xhci_hcd_histb *histb)
+{
+ struct device_node *np = histb->dev->of_node;
+ u32 regval;
+
+ if (of_property_match_string(np, "phys-names", "inno") >= 0) {
+ /* USB2 PHY chose ulpi 8bit interface */
+ regval = readl(histb->ctrl + REG_GUSB2PHYCFG0);
+ regval &= ~BIT_UTMI_ULPI;
+ regval &= ~(BIT_UTMI_8_16);
+ regval &= ~BIT_FREECLK_EXIST;
+ writel(regval, histb->ctrl + REG_GUSB2PHYCFG0);
+ }
+
+ if (of_property_match_string(np, "phys-names", "combo") >= 0) {
+ /*
+ * write 0x010c0012 to GUSB3PIPECTL0
+ * GUSB3PIPECTL0[5:3] = 010 : Tx Margin = 900mV ,
+ * decrease TX voltage
+ * GUSB3PIPECTL0[2:1] = 01 : Tx Deemphasis = -3.5dB,
+ * refer to xHCI spec
+ */
+ regval = readl(histb->ctrl + REG_GUSB3PIPECTL0);
+ regval &= ~USB3_DEEMPHASIS_MASK;
+ regval |= USB3_DEEMPHASIS0;
+ regval |= USB3_TX_MARGIN1;
+ writel(regval, histb->ctrl + REG_GUSB3PIPECTL0);
+ }
+
+ writel(0x23100000, histb->ctrl + GTXTHRCFG);
+ writel(0x23100000, histb->ctrl + GRXTHRCFG);
+
+ return 0;
+}
+
+static int xhci_histb_clks_get(struct xhci_hcd_histb *histb)
+{
+ struct device *dev = histb->dev;
+
+ histb->bus_clk = devm_clk_get(dev, "bus");
+ if (IS_ERR(histb->bus_clk)) {
+ dev_err(dev, "fail to get bus clk\n");
+ return PTR_ERR(histb->bus_clk);
+ }
+
+ histb->utmi_clk = devm_clk_get(dev, "utmi");
+ if (IS_ERR(histb->utmi_clk)) {
+ dev_err(dev, "fail to get utmi clk\n");
+ return PTR_ERR(histb->utmi_clk);
+ }
+
+ histb->pipe_clk = devm_clk_get(dev, "pipe");
+ if (IS_ERR(histb->pipe_clk)) {
+ dev_err(dev, "fail to get pipe clk\n");
+ return PTR_ERR(histb->pipe_clk);
+ }
+
+ histb->suspend_clk = devm_clk_get(dev, "suspend");
+ if (IS_ERR(histb->suspend_clk)) {
+ dev_err(dev, "fail to get suspend clk\n");
+ return PTR_ERR(histb->suspend_clk);
+ }
+
+ return 0;
+}
+
+static int xhci_histb_host_enable(struct xhci_hcd_histb *histb)
+{
+ int ret;
+
+ ret = clk_prepare_enable(histb->bus_clk);
+ if (ret) {
+ dev_err(histb->dev, "failed to enable bus clk\n");
+ return ret;
+ }
+
+ ret = clk_prepare_enable(histb->utmi_clk);
+ if (ret) {
+ dev_err(histb->dev, "failed to enable utmi clk\n");
+ goto err_utmi_clk;
+ }
+
+ ret = clk_prepare_enable(histb->pipe_clk);
+ if (ret) {
+ dev_err(histb->dev, "failed to enable pipe clk\n");
+ goto err_pipe_clk;
+ }
+
+ ret = clk_prepare_enable(histb->suspend_clk);
+ if (ret) {
+ dev_err(histb->dev, "failed to enable suspend clk\n");
+ goto err_suspend_clk;
+ }
+
+ reset_control_deassert(histb->soft_reset);
+
+ return 0;
+
+err_suspend_clk:
+ clk_disable_unprepare(histb->pipe_clk);
+err_pipe_clk:
+ clk_disable_unprepare(histb->utmi_clk);
+err_utmi_clk:
+ clk_disable_unprepare(histb->bus_clk);
+
+ return ret;
+}
+
+static void xhci_histb_host_disable(struct xhci_hcd_histb *histb)
+{
+ reset_control_assert(histb->soft_reset);
+
+ clk_disable_unprepare(histb->suspend_clk);
+ clk_disable_unprepare(histb->pipe_clk);
+ clk_disable_unprepare(histb->utmi_clk);
+ clk_disable_unprepare(histb->bus_clk);
+}
+
+static void xhci_histb_quirks(struct device *dev, struct xhci_hcd *xhci)
+{
+ /*
+ * As of now platform drivers don't provide MSI support so we ensure
+ * here that the generic code does not try to make a pci_dev from our
+ * dev struct in order to setup MSI
+ */
+ xhci->quirks |= XHCI_PLAT;
+}
+
+/* called during probe() after chip reset completes */
+static int xhci_histb_setup(struct usb_hcd *hcd)
+{
+ struct xhci_hcd_histb *histb = hcd_to_histb(hcd);
+ int ret;
+
+ if (usb_hcd_is_primary_hcd(hcd)) {
+ ret = xhci_histb_config(histb);
+ if (ret)
+ return ret;
+ }
+
+ return xhci_gen_setup(hcd, xhci_histb_quirks);
+}
+
+static const struct xhci_driver_overrides xhci_histb_overrides __initconst = {
+ .reset = xhci_histb_setup,
+};
+
+static struct hc_driver __read_mostly xhci_histb_hc_driver;
+static int xhci_histb_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct xhci_hcd_histb *histb;
+ const struct hc_driver *driver;
+ struct usb_hcd *hcd;
+ struct xhci_hcd *xhci;
+ struct resource *res;
+ int irq;
+ int ret = -ENODEV;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ driver = &xhci_histb_hc_driver;
+ histb = devm_kzalloc(dev, sizeof(*histb), GFP_KERNEL);
+ if (!histb)
+ return -ENOMEM;
+
+ histb->dev = dev;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ histb->ctrl = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(histb->ctrl))
+ return PTR_ERR(histb->ctrl);
+
+ ret = xhci_histb_clks_get(histb);
+ if (ret)
+ return ret;
+
+ histb->soft_reset = devm_reset_control_get(dev, "soft");
+ if (IS_ERR(histb->soft_reset)) {
+ dev_err(dev, "failed to get soft reset\n");
+ return PTR_ERR(histb->soft_reset);
+ }
+
+ pm_runtime_enable(dev);
+ pm_runtime_get_sync(dev);
+ device_enable_async_suspend(dev);
+
+ /* Initialize dma_mask and coherent_dma_mask to 32-bits */
+ ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
+ if (ret)
+ return ret;
+
+ hcd = usb_create_hcd(driver, dev, dev_name(dev));
+ if (!hcd) {
+ ret = -ENOMEM;
+ goto disable_pm;
+ }
+
+ hcd->regs = histb->ctrl;
+ hcd->rsrc_start = res->start;
+ hcd->rsrc_len = resource_size(res);
+
+ histb->hcd = hcd;
+ dev_set_drvdata(hcd->self.controller, histb);
+
+ ret = xhci_histb_host_enable(histb);
+ if (ret)
+ goto put_hcd;
+
+ xhci = hcd_to_xhci(hcd);
+
+ device_wakeup_enable(hcd->self.controller);
+
+ xhci->main_hcd = hcd;
+ xhci->shared_hcd = usb_create_shared_hcd(driver, dev, dev_name(dev),
+ hcd);
+ if (!xhci->shared_hcd) {
+ ret = -ENOMEM;
+ goto disable_host;
+ }
+
+ if (device_property_read_bool(dev, "usb2-lpm-disable"))
+ xhci->quirks |= XHCI_HW_LPM_DISABLE;
+
+ if (device_property_read_bool(dev, "usb3-lpm-capable"))
+ xhci->quirks |= XHCI_LPM_SUPPORT;
+
+ /* imod_interval is the interrupt moderation value in nanoseconds. */
+ xhci->imod_interval = 40000;
+ device_property_read_u32(dev, "imod-interval-ns",
+ &xhci->imod_interval);
+
+ ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
+ if (ret)
+ goto put_usb3_hcd;
+
+ if (HCC_MAX_PSA(xhci->hcc_params) >= 4)
+ xhci->shared_hcd->can_do_streams = 1;
+
+ ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED);
+ if (ret)
+ goto dealloc_usb2_hcd;
+
+ device_enable_async_suspend(dev);
+ pm_runtime_put_noidle(dev);
+
+ /*
+ * Prevent runtime pm from being on as default, users should enable
+ * runtime pm using power/control in sysfs.
+ */
+ pm_runtime_forbid(dev);
+
+ return 0;
+
+dealloc_usb2_hcd:
+ usb_remove_hcd(hcd);
+put_usb3_hcd:
+ usb_put_hcd(xhci->shared_hcd);
+disable_host:
+ xhci_histb_host_disable(histb);
+put_hcd:
+ usb_put_hcd(hcd);
+disable_pm:
+ pm_runtime_put_sync(dev);
+ pm_runtime_disable(dev);
+
+ return ret;
+}
+
+static int xhci_histb_remove(struct platform_device *dev)
+{
+ struct xhci_hcd_histb *histb = platform_get_drvdata(dev);
+ struct usb_hcd *hcd = histb->hcd;
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ struct usb_hcd *shared_hcd = xhci->shared_hcd;
+
+ xhci->xhc_state |= XHCI_STATE_REMOVING;
+
+ usb_remove_hcd(shared_hcd);
+ xhci->shared_hcd = NULL;
+ device_wakeup_disable(&dev->dev);
+
+ usb_remove_hcd(hcd);
+ usb_put_hcd(shared_hcd);
+
+ xhci_histb_host_disable(histb);
+ usb_put_hcd(hcd);
+ pm_runtime_put_sync(&dev->dev);
+ pm_runtime_disable(&dev->dev);
+
+ return 0;
+}
+
+static int __maybe_unused xhci_histb_suspend(struct device *dev)
+{
+ struct xhci_hcd_histb *histb = dev_get_drvdata(dev);
+ struct usb_hcd *hcd = histb->hcd;
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ int ret;
+
+ ret = xhci_suspend(xhci, device_may_wakeup(dev));
+
+ if (!device_may_wakeup(dev))
+ xhci_histb_host_disable(histb);
+
+ return ret;
+}
+
+static int __maybe_unused xhci_histb_resume(struct device *dev)
+{
+ struct xhci_hcd_histb *histb = dev_get_drvdata(dev);
+ struct usb_hcd *hcd = histb->hcd;
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+
+ if (!device_may_wakeup(dev))
+ xhci_histb_host_enable(histb);
+
+ return xhci_resume(xhci, 0);
+}
+
+static const struct dev_pm_ops xhci_histb_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(xhci_histb_suspend, xhci_histb_resume)
+};
+#define DEV_PM_OPS (IS_ENABLED(CONFIG_PM) ? &xhci_histb_pm_ops : NULL)
+
+#ifdef CONFIG_OF
+static const struct of_device_id histb_xhci_of_match[] = {
+ { .compatible = "hisilicon,hi3798cv200-xhci"},
+ { },
+};
+MODULE_DEVICE_TABLE(of, histb_xhci_of_match);
+#endif
+
+static struct platform_driver histb_xhci_driver = {
+ .probe = xhci_histb_probe,
+ .remove = xhci_histb_remove,
+ .driver = {
+ .name = "xhci-histb",
+ .pm = DEV_PM_OPS,
+ .of_match_table = of_match_ptr(histb_xhci_of_match),
+ },
+};
+MODULE_ALIAS("platform:xhci-histb");
+
+static int __init xhci_histb_init(void)
+{
+ xhci_init_driver(&xhci_histb_hc_driver, &xhci_histb_overrides);
+ return platform_driver_register(&histb_xhci_driver);
+}
+module_init(xhci_histb_init);
+
+static void __exit xhci_histb_exit(void)
+{
+ platform_driver_unregister(&histb_xhci_driver);
+}
+module_exit(xhci_histb_exit);
+
+MODULE_DESCRIPTION("HiSilicon STB xHCI Host Controller Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index 32cd52ca8318..01b5818a4be5 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -189,9 +189,10 @@ static void xhci_usb2_hub_descriptor(struct usb_hcd *hcd, struct xhci_hcd *xhci,
__u8 port_removable[(USB_MAXCHILDREN + 1 + 7) / 8];
u32 portsc;
unsigned int i;
+ struct xhci_hub *rhub;
- ports = xhci->num_usb2_ports;
-
+ rhub = &xhci->usb2_rhub;
+ ports = rhub->num_ports;
xhci_common_hub_descriptor(xhci, desc, ports);
desc->bDescriptorType = USB_DT_HUB;
temp = 1 + (ports / 8);
@@ -202,7 +203,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(xhci->usb2_ports[i]);
+ portsc = readl(rhub->ports[i]->addr);
/* If a device is removable, PORTSC reports a 0, same as in the
* hub descriptor DeviceRemovable bits.
*/
@@ -241,8 +242,10 @@ static void xhci_usb3_hub_descriptor(struct usb_hcd *hcd, struct xhci_hcd *xhci,
u16 port_removable;
u32 portsc;
unsigned int i;
+ struct xhci_hub *rhub;
- ports = xhci->num_usb3_ports;
+ rhub = &xhci->usb3_rhub;
+ ports = rhub->num_ports;
xhci_common_hub_descriptor(xhci, desc, ports);
desc->bDescriptorType = USB_DT_SS_HUB;
desc->bDescLength = USB_DT_SS_HUB_SIZE;
@@ -256,7 +259,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(xhci->usb3_ports[i]);
+ portsc = readl(rhub->ports[i]->addr);
if (portsc & PORT_DEV_REMOVE)
port_removable |= 1 << (i + 1);
}
@@ -538,28 +541,13 @@ static void xhci_clear_port_change_bit(struct xhci_hcd *xhci, u16 wValue,
port_change_bit, wIndex, port_status);
}
-static int xhci_get_ports(struct usb_hcd *hcd, __le32 __iomem ***port_array)
+struct xhci_hub *xhci_get_rhub(struct usb_hcd *hcd)
{
- int max_ports;
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
- if (hcd->speed >= HCD_USB3) {
- max_ports = xhci->num_usb3_ports;
- *port_array = xhci->usb3_ports;
- } else {
- max_ports = xhci->num_usb2_ports;
- *port_array = xhci->usb2_ports;
- }
-
- return max_ports;
-}
-
-static __le32 __iomem *xhci_get_port_io_addr(struct usb_hcd *hcd, int index)
-{
- __le32 __iomem **port_array;
-
- xhci_get_ports(hcd, &port_array);
- return port_array[index];
+ if (hcd->speed >= HCD_USB3)
+ return &xhci->usb3_rhub;
+ return &xhci->usb2_rhub;
}
/*
@@ -570,21 +558,23 @@ static __le32 __iomem *xhci_get_port_io_addr(struct usb_hcd *hcd, int index)
static void xhci_set_port_power(struct xhci_hcd *xhci, struct usb_hcd *hcd,
u16 index, bool on, unsigned long *flags)
{
- __le32 __iomem *addr;
+ struct xhci_hub *rhub;
+ struct xhci_port *port;
u32 temp;
- addr = xhci_get_port_io_addr(hcd, index);
- temp = readl(addr);
+ rhub = xhci_get_rhub(hcd);
+ port = rhub->ports[index];
+ temp = readl(port->addr);
temp = xhci_port_state_to_neutral(temp);
if (on) {
/* Power on */
- writel(temp | PORT_POWER, addr);
- temp = readl(addr);
+ writel(temp | PORT_POWER, port->addr);
+ temp = readl(port->addr);
xhci_dbg(xhci, "set port power, actual port %d status = 0x%x\n",
index, temp);
} else {
/* Power off */
- writel(temp & ~PORT_POWER, addr);
+ writel(temp & ~PORT_POWER, port->addr);
}
spin_unlock_irqrestore(&xhci->lock, *flags);
@@ -600,13 +590,13 @@ static void xhci_port_set_test_mode(struct xhci_hcd *xhci,
u16 test_mode, u16 wIndex)
{
u32 temp;
- __le32 __iomem *addr;
+ struct xhci_port *port;
- /* xhci only supports test mode for usb2 ports, i.e. xhci->main_hcd */
- addr = xhci_get_port_io_addr(xhci->main_hcd, wIndex);
- temp = readl(addr + PORTPMSC);
+ /* xhci only supports test mode for usb2 ports */
+ port = xhci->usb2_rhub.ports[wIndex];
+ temp = readl(port->addr + PORTPMSC);
temp |= test_mode << PORT_TEST_MODE_SHIFT;
- writel(temp, addr + PORTPMSC);
+ writel(temp, port->addr + PORTPMSC);
xhci->test_mode = test_mode;
if (test_mode == TEST_FORCE_EN)
xhci_start(xhci);
@@ -633,10 +623,10 @@ static int xhci_enter_test_mode(struct xhci_hcd *xhci,
/* Put all ports to the Disable state by clear PP */
xhci_dbg(xhci, "Disable all port (PP = 0)\n");
/* Power off USB3 ports*/
- for (i = 0; i < xhci->num_usb3_ports; i++)
+ for (i = 0; i < xhci->usb3_rhub.num_ports; i++)
xhci_set_port_power(xhci, xhci->shared_hcd, i, false, flags);
/* Power off USB2 ports*/
- for (i = 0; i < xhci->num_usb2_ports; i++)
+ for (i = 0; i < xhci->usb2_rhub.num_ports; i++)
xhci_set_port_power(xhci, xhci->main_hcd, i, false, flags);
/* Stop the controller */
xhci_dbg(xhci, "Stop controller\n");
@@ -672,24 +662,24 @@ static int xhci_exit_test_mode(struct xhci_hcd *xhci)
return xhci_reset(xhci);
}
-void xhci_set_link_state(struct xhci_hcd *xhci, __le32 __iomem **port_array,
- int port_id, u32 link_state)
+void xhci_set_link_state(struct xhci_hcd *xhci, struct xhci_port *port,
+ u32 link_state)
{
u32 temp;
- temp = readl(port_array[port_id]);
+ temp = readl(port->addr);
temp = xhci_port_state_to_neutral(temp);
temp &= ~PORT_PLS_MASK;
temp |= PORT_LINK_STROBE | link_state;
- writel(temp, port_array[port_id]);
+ writel(temp, port->addr);
}
static void xhci_set_remote_wake_mask(struct xhci_hcd *xhci,
- __le32 __iomem **port_array, int port_id, u16 wake_mask)
+ struct xhci_port *port, u16 wake_mask)
{
u32 temp;
- temp = readl(port_array[port_id]);
+ temp = readl(port->addr);
temp = xhci_port_state_to_neutral(temp);
if (wake_mask & USB_PORT_FEAT_REMOTE_WAKE_CONNECT)
@@ -707,20 +697,20 @@ static void xhci_set_remote_wake_mask(struct xhci_hcd *xhci,
else
temp &= ~PORT_WKOC_E;
- writel(temp, port_array[port_id]);
+ writel(temp, port->addr);
}
/* Test and clear port RWC bit */
-void xhci_test_and_clear_bit(struct xhci_hcd *xhci, __le32 __iomem **port_array,
- int port_id, u32 port_bit)
+void xhci_test_and_clear_bit(struct xhci_hcd *xhci, struct xhci_port *port,
+ u32 port_bit)
{
u32 temp;
- temp = readl(port_array[port_id]);
+ temp = readl(port->addr);
if (temp & port_bit) {
temp = xhci_port_state_to_neutral(temp);
temp |= port_bit;
- writel(temp, port_array[port_id]);
+ writel(temp, port->addr);
}
}
@@ -794,7 +784,7 @@ static void xhci_hub_report_usb3_link_state(struct xhci_hcd *xhci,
static void xhci_del_comp_mod_timer(struct xhci_hcd *xhci, u32 status,
u16 wIndex)
{
- u32 all_ports_seen_u0 = ((1 << xhci->num_usb3_ports)-1);
+ u32 all_ports_seen_u0 = ((1 << xhci->usb3_rhub.num_ports) - 1);
bool port_in_u0 = ((status & PORT_PLS_MASK) == XDEV_U0);
if (!(xhci->quirks & XHCI_COMP_MODE_QUIRK))
@@ -840,8 +830,7 @@ static u32 xhci_get_ext_port_status(u32 raw_port_status, u32 port_li)
*/
static u32 xhci_get_port_status(struct usb_hcd *hcd,
struct xhci_bus_state *bus_state,
- __le32 __iomem **port_array,
- u16 wIndex, u32 raw_port_status,
+ u16 wIndex, u32 raw_port_status,
unsigned long flags)
__releases(&xhci->lock)
__acquires(&xhci->lock)
@@ -849,6 +838,11 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd,
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
u32 status = 0;
int slot_id;
+ struct xhci_hub *rhub;
+ struct xhci_port *port;
+
+ rhub = xhci_get_rhub(hcd);
+ port = rhub->ports[wIndex];
/* wPortChange bits */
if (raw_port_status & PORT_CSC)
@@ -882,7 +876,7 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd,
status |= USB_PORT_STAT_SUSPEND;
}
if ((raw_port_status & PORT_PLS_MASK) == XDEV_RESUME &&
- !DEV_SUPERSPEED_ANY(raw_port_status)) {
+ !DEV_SUPERSPEED_ANY(raw_port_status) && hcd->speed < HCD_USB3) {
if ((raw_port_status & PORT_RESET) ||
!(raw_port_status & PORT_PE))
return 0xffffffff;
@@ -906,6 +900,7 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd,
set_bit(wIndex, &bus_state->resuming_ports);
bus_state->resume_done[wIndex] = timeout;
mod_timer(&hcd->rh_timer, timeout);
+ usb_hcd_start_port_resume(&hcd->self, wIndex);
}
/* Has resume been signalled for USB_RESUME_TIME yet? */
} else if (time_after_eq(jiffies,
@@ -919,16 +914,14 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd,
set_bit(wIndex, &bus_state->rexit_ports);
- xhci_test_and_clear_bit(xhci, port_array, wIndex,
- PORT_PLC);
- xhci_set_link_state(xhci, port_array, wIndex,
- XDEV_U0);
+ xhci_test_and_clear_bit(xhci, port, PORT_PLC);
+ xhci_set_link_state(xhci, port, XDEV_U0);
spin_unlock_irqrestore(&xhci->lock, flags);
time_left = wait_for_completion_timeout(
&bus_state->rexit_done[wIndex],
msecs_to_jiffies(
- XHCI_MAX_REXIT_TIMEOUT));
+ XHCI_MAX_REXIT_TIMEOUT_MS));
spin_lock_irqsave(&xhci->lock, flags);
if (time_left) {
@@ -940,14 +933,15 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd,
}
xhci_ring_device(xhci, slot_id);
} else {
- int port_status = readl(port_array[wIndex]);
+ int port_status = readl(port->addr);
xhci_warn(xhci, "Port resume took longer than %i msec, port status = 0x%x\n",
- XHCI_MAX_REXIT_TIMEOUT,
+ XHCI_MAX_REXIT_TIMEOUT_MS,
port_status);
status |= USB_PORT_STAT_SUSPEND;
clear_bit(wIndex, &bus_state->rexit_ports);
}
+ usb_hcd_end_port_resume(&hcd->self, wIndex);
bus_state->port_c_suspend |= 1 << wIndex;
bus_state->suspended_ports &= ~(1 << wIndex);
} else {
@@ -970,6 +964,7 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd,
(raw_port_status & PORT_PLS_MASK) != XDEV_RESUME) {
bus_state->resume_done[wIndex] = 0;
clear_bit(wIndex, &bus_state->resuming_ports);
+ usb_hcd_end_port_resume(&hcd->self, wIndex);
}
@@ -1024,15 +1019,18 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
unsigned long flags;
u32 temp, status;
int retval = 0;
- __le32 __iomem **port_array;
int slot_id;
struct xhci_bus_state *bus_state;
u16 link_state = 0;
u16 wake_mask = 0;
u16 timeout = 0;
u16 test_mode = 0;
+ struct xhci_hub *rhub;
+ struct xhci_port **ports;
- max_ports = xhci_get_ports(hcd, &port_array);
+ rhub = xhci_get_rhub(hcd);
+ ports = rhub->ports;
+ max_ports = rhub->num_ports;
bus_state = &xhci->bus_state[hcd_index(hcd)];
spin_lock_irqsave(&xhci->lock, flags);
@@ -1070,15 +1068,15 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
if (!wIndex || wIndex > max_ports)
goto error;
wIndex--;
- temp = readl(port_array[wIndex]);
+ temp = readl(ports[wIndex]->addr);
if (temp == ~(u32)0) {
xhci_hc_died(xhci);
retval = -ENODEV;
break;
}
trace_xhci_get_port_status(wIndex, temp);
- status = xhci_get_port_status(hcd, bus_state, port_array,
- wIndex, temp, flags);
+ status = xhci_get_port_status(hcd, bus_state, wIndex, temp,
+ flags);
if (status == 0xffffffff)
goto error;
@@ -1096,7 +1094,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
retval = -EINVAL;
break;
}
- port_li = readl(port_array[wIndex] + PORTLI);
+ port_li = readl(ports[wIndex]->addr + PORTLI);
status = xhci_get_ext_port_status(temp, port_li);
put_unaligned_le32(cpu_to_le32(status), &buf[4]);
}
@@ -1114,7 +1112,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
if (!wIndex || wIndex > max_ports)
goto error;
wIndex--;
- temp = readl(port_array[wIndex]);
+ temp = readl(ports[wIndex]->addr);
if (temp == ~(u32)0) {
xhci_hc_died(xhci);
retval = -ENODEV;
@@ -1124,10 +1122,10 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
/* FIXME: What new port features do we need to support? */
switch (wValue) {
case USB_PORT_FEAT_SUSPEND:
- temp = readl(port_array[wIndex]);
+ temp = readl(ports[wIndex]->addr);
if ((temp & PORT_PLS_MASK) != XDEV_U0) {
/* Resume the port to U0 first */
- xhci_set_link_state(xhci, port_array, wIndex,
+ xhci_set_link_state(xhci, ports[wIndex],
XDEV_U0);
spin_unlock_irqrestore(&xhci->lock, flags);
msleep(10);
@@ -1137,7 +1135,7 @@ 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_array[wIndex]);
+ temp = readl(ports[wIndex]->addr);
if ((temp & PORT_PE) == 0 || (temp & PORT_RESET)
|| (temp & PORT_PLS_MASK) >= XDEV_U3) {
xhci_warn(xhci, "USB core suspending device not in U0/U1/U2.\n");
@@ -1155,18 +1153,17 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
xhci_stop_device(xhci, slot_id, 1);
spin_lock_irqsave(&xhci->lock, flags);
- xhci_set_link_state(xhci, port_array, wIndex, XDEV_U3);
+ xhci_set_link_state(xhci, ports[wIndex], XDEV_U3);
spin_unlock_irqrestore(&xhci->lock, flags);
msleep(10); /* wait device to enter */
spin_lock_irqsave(&xhci->lock, flags);
- temp = readl(port_array[wIndex]);
+ temp = readl(ports[wIndex]->addr);
bus_state->suspended_ports |= 1 << wIndex;
break;
case USB_PORT_FEAT_LINK_STATE:
- temp = readl(port_array[wIndex]);
-
+ temp = readl(ports[wIndex]->addr);
/* Disable port */
if (link_state == USB_SS_PORT_LS_SS_DISABLED) {
xhci_dbg(xhci, "Disable port %d\n", wIndex);
@@ -1178,17 +1175,17 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
temp |= PORT_CSC | PORT_PEC | PORT_WRC |
PORT_OCC | PORT_RC | PORT_PLC |
PORT_CEC;
- writel(temp | PORT_PE, port_array[wIndex]);
- temp = readl(port_array[wIndex]);
+ writel(temp | PORT_PE, ports[wIndex]->addr);
+ temp = readl(ports[wIndex]->addr);
break;
}
/* Put link in RxDetect (enable port) */
if (link_state == USB_SS_PORT_LS_RX_DETECT) {
xhci_dbg(xhci, "Enable port %d\n", wIndex);
- xhci_set_link_state(xhci, port_array, wIndex,
- link_state);
- temp = readl(port_array[wIndex]);
+ xhci_set_link_state(xhci, ports[wIndex],
+ link_state);
+ temp = readl(ports[wIndex]->addr);
break;
}
@@ -1219,9 +1216,10 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
xhci_dbg(xhci, "Enable compliance mode transition for port %d\n",
wIndex);
- xhci_set_link_state(xhci, port_array, wIndex,
+ xhci_set_link_state(xhci, ports[wIndex],
link_state);
- temp = readl(port_array[wIndex]);
+
+ temp = readl(ports[wIndex]->addr);
break;
}
/* Port must be enabled */
@@ -1248,14 +1246,13 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
}
}
- xhci_set_link_state(xhci, port_array, wIndex,
- link_state);
+ xhci_set_link_state(xhci, ports[wIndex], link_state);
spin_unlock_irqrestore(&xhci->lock, flags);
msleep(20); /* wait device to enter */
spin_lock_irqsave(&xhci->lock, flags);
- temp = readl(port_array[wIndex]);
+ temp = readl(ports[wIndex]->addr);
if (link_state == USB_SS_PORT_LS_U3)
bus_state->suspended_ports |= 1 << wIndex;
break;
@@ -1270,40 +1267,39 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
break;
case USB_PORT_FEAT_RESET:
temp = (temp | PORT_RESET);
- writel(temp, port_array[wIndex]);
+ writel(temp, ports[wIndex]->addr);
- temp = readl(port_array[wIndex]);
+ temp = readl(ports[wIndex]->addr);
xhci_dbg(xhci, "set port reset, actual port %d status = 0x%x\n", wIndex, temp);
break;
case USB_PORT_FEAT_REMOTE_WAKE_MASK:
- xhci_set_remote_wake_mask(xhci, port_array,
- wIndex, wake_mask);
- temp = readl(port_array[wIndex]);
+ xhci_set_remote_wake_mask(xhci, ports[wIndex],
+ wake_mask);
+ temp = readl(ports[wIndex]->addr);
xhci_dbg(xhci, "set port remote wake mask, "
"actual port %d status = 0x%x\n",
wIndex, temp);
break;
case USB_PORT_FEAT_BH_PORT_RESET:
temp |= PORT_WR;
- writel(temp, port_array[wIndex]);
-
- temp = readl(port_array[wIndex]);
+ writel(temp, ports[wIndex]->addr);
+ temp = readl(ports[wIndex]->addr);
break;
case USB_PORT_FEAT_U1_TIMEOUT:
if (hcd->speed < HCD_USB3)
goto error;
- temp = readl(port_array[wIndex] + PORTPMSC);
+ temp = readl(ports[wIndex]->addr + PORTPMSC);
temp &= ~PORT_U1_TIMEOUT_MASK;
temp |= PORT_U1_TIMEOUT(timeout);
- writel(temp, port_array[wIndex] + PORTPMSC);
+ writel(temp, ports[wIndex]->addr + PORTPMSC);
break;
case USB_PORT_FEAT_U2_TIMEOUT:
if (hcd->speed < HCD_USB3)
goto error;
- temp = readl(port_array[wIndex] + PORTPMSC);
+ temp = readl(ports[wIndex]->addr + PORTPMSC);
temp &= ~PORT_U2_TIMEOUT_MASK;
temp |= PORT_U2_TIMEOUT(timeout);
- writel(temp, port_array[wIndex] + PORTPMSC);
+ writel(temp, ports[wIndex]->addr + PORTPMSC);
break;
case USB_PORT_FEAT_TEST:
/* 4.19.6 Port Test Modes (USB2 Test Mode) */
@@ -1318,13 +1314,13 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
goto error;
}
/* unblock any posted writes */
- temp = readl(port_array[wIndex]);
+ temp = readl(ports[wIndex]->addr);
break;
case ClearPortFeature:
if (!wIndex || wIndex > max_ports)
goto error;
wIndex--;
- temp = readl(port_array[wIndex]);
+ temp = readl(ports[wIndex]->addr);
if (temp == ~(u32)0) {
xhci_hc_died(xhci);
retval = -ENODEV;
@@ -1334,7 +1330,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
temp = xhci_port_state_to_neutral(temp);
switch (wValue) {
case USB_PORT_FEAT_SUSPEND:
- temp = readl(port_array[wIndex]);
+ temp = readl(ports[wIndex]->addr);
xhci_dbg(xhci, "clear USB_PORT_FEAT_SUSPEND\n");
xhci_dbg(xhci, "PORTSC %04x\n", temp);
if (temp & PORT_RESET)
@@ -1344,14 +1340,16 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
goto error;
set_bit(wIndex, &bus_state->resuming_ports);
- xhci_set_link_state(xhci, port_array, wIndex,
- XDEV_RESUME);
+ usb_hcd_start_port_resume(&hcd->self, wIndex);
+ xhci_set_link_state(xhci, ports[wIndex],
+ XDEV_RESUME);
spin_unlock_irqrestore(&xhci->lock, flags);
msleep(USB_RESUME_TIMEOUT);
spin_lock_irqsave(&xhci->lock, flags);
- xhci_set_link_state(xhci, port_array, wIndex,
+ xhci_set_link_state(xhci, ports[wIndex],
XDEV_U0);
clear_bit(wIndex, &bus_state->resuming_ports);
+ usb_hcd_end_port_resume(&hcd->self, wIndex);
}
bus_state->port_c_suspend |= 1 << wIndex;
@@ -1374,11 +1372,11 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
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_array[wIndex], temp);
+ ports[wIndex]->addr, temp);
break;
case USB_PORT_FEAT_ENABLE:
xhci_disable_port(hcd, xhci, wIndex,
- port_array[wIndex], temp);
+ ports[wIndex]->addr, temp);
break;
case USB_PORT_FEAT_POWER:
xhci_set_port_power(xhci, hcd, wIndex, false, &flags);
@@ -1415,11 +1413,14 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf)
int i, retval;
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
int max_ports;
- __le32 __iomem **port_array;
struct xhci_bus_state *bus_state;
bool reset_change = false;
+ struct xhci_hub *rhub;
+ struct xhci_port **ports;
- max_ports = xhci_get_ports(hcd, &port_array);
+ rhub = xhci_get_rhub(hcd);
+ ports = rhub->ports;
+ max_ports = rhub->num_ports;
bus_state = &xhci->bus_state[hcd_index(hcd)];
/* Initial status is no changes */
@@ -1437,7 +1438,7 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf)
spin_lock_irqsave(&xhci->lock, flags);
/* For each port, did anything change? If so, set that bit in buf. */
for (i = 0; i < max_ports; i++) {
- temp = readl(port_array[i]);
+ temp = readl(ports[i]->addr);
if (temp == ~(u32)0) {
xhci_hc_died(xhci);
retval = -ENODEV;
@@ -1469,16 +1470,22 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
{
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
int max_ports, port_index;
- __le32 __iomem **port_array;
struct xhci_bus_state *bus_state;
unsigned long flags;
-
- max_ports = xhci_get_ports(hcd, &port_array);
+ struct xhci_hub *rhub;
+ struct xhci_port **ports;
+ u32 portsc_buf[USB_MAXCHILDREN];
+ bool wake_enabled;
+
+ rhub = xhci_get_rhub(hcd);
+ ports = rhub->ports;
+ max_ports = rhub->num_ports;
bus_state = &xhci->bus_state[hcd_index(hcd)];
+ wake_enabled = hcd->self.root_hub->do_remote_wakeup;
spin_lock_irqsave(&xhci->lock, flags);
- if (hcd->self.root_hub->do_remote_wakeup) {
+ if (wake_enabled) {
if (bus_state->resuming_ports || /* USB2 */
bus_state->port_remote_wakeup) { /* USB3 */
spin_unlock_irqrestore(&xhci->lock, flags);
@@ -1486,26 +1493,37 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
return -EBUSY;
}
}
-
- port_index = max_ports;
+ /*
+ * Prepare ports for suspend, but don't write anything before all ports
+ * are checked and we know bus suspend can proceed
+ */
bus_state->bus_suspended = 0;
+ port_index = max_ports;
while (port_index--) {
- /* suspend the port if the port is not suspended */
u32 t1, t2;
- int slot_id;
- t1 = readl(port_array[port_index]);
+ t1 = readl(ports[port_index]->addr);
t2 = xhci_port_state_to_neutral(t1);
+ portsc_buf[port_index] = 0;
- if ((t1 & PORT_PE) && !(t1 & PORT_PLS_MASK)) {
- xhci_dbg(xhci, "port %d not suspended\n", port_index);
- slot_id = xhci_find_slot_id_by_port(hcd, xhci,
- port_index + 1);
- if (slot_id) {
+ /* Bail out if a USB3 port has a new device in link training */
+ if ((hcd->speed >= HCD_USB3) &&
+ (t1 & PORT_PLS_MASK) == XDEV_POLLING) {
+ bus_state->bus_suspended = 0;
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ xhci_dbg(xhci, "Bus suspend bailout, port in polling\n");
+ return -EBUSY;
+ }
+
+ /* suspend ports in U0, or bail out for new connect changes */
+ if ((t1 & PORT_PE) && (t1 & PORT_PLS_MASK) == XDEV_U0) {
+ if ((t1 & PORT_CSC) && wake_enabled) {
+ bus_state->bus_suspended = 0;
spin_unlock_irqrestore(&xhci->lock, flags);
- xhci_stop_device(xhci, slot_id, 1);
- spin_lock_irqsave(&xhci->lock, flags);
+ xhci_dbg(xhci, "Bus suspend bailout, port connect change\n");
+ return -EBUSY;
}
+ xhci_dbg(xhci, "port %d not suspended\n", port_index);
t2 &= ~PORT_PLS_MASK;
t2 |= PORT_LINK_STROBE | XDEV_U3;
set_bit(port_index, &bus_state->bus_suspended);
@@ -1514,7 +1532,7 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
* including the USB 3.0 roothub, but only if CONFIG_PM
* is enabled, so also enable remote wake here.
*/
- if (hcd->self.root_hub->do_remote_wakeup) {
+ if (wake_enabled) {
if (t1 & PORT_CONNECT) {
t2 |= PORT_WKOC_E | PORT_WKDISC_E;
t2 &= ~PORT_WKCONN_E;
@@ -1534,7 +1552,26 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
t1 = xhci_port_state_to_neutral(t1);
if (t1 != t2)
- writel(t2, port_array[port_index]);
+ portsc_buf[port_index] = t2;
+ }
+
+ /* write port settings, stopping and suspending ports if needed */
+ port_index = max_ports;
+ while (port_index--) {
+ if (!portsc_buf[port_index])
+ continue;
+ if (test_bit(port_index, &bus_state->bus_suspended)) {
+ int slot_id;
+
+ slot_id = xhci_find_slot_id_by_port(hcd, xhci,
+ port_index + 1);
+ if (slot_id) {
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ xhci_stop_device(xhci, slot_id, 1);
+ spin_lock_irqsave(&xhci->lock, flags);
+ }
+ }
+ writel(portsc_buf[port_index], ports[port_index]->addr);
}
hcd->state = HC_STATE_SUSPENDED;
bus_state->next_statechange = jiffies + msecs_to_jiffies(10);
@@ -1547,12 +1584,11 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
* warm reset a USB3 device stuck in polling or compliance mode after resume.
* See Intel 100/c230 series PCH specification update Doc #332692-006 Errata #8
*/
-static bool xhci_port_missing_cas_quirk(int port_index,
- __le32 __iomem **port_array)
+static bool xhci_port_missing_cas_quirk(struct xhci_port *port)
{
u32 portsc;
- portsc = readl(port_array[port_index]);
+ portsc = readl(port->addr);
/* if any of these are set we are not stuck */
if (portsc & (PORT_CONNECT | PORT_CAS))
@@ -1565,9 +1601,9 @@ static bool xhci_port_missing_cas_quirk(int port_index,
/* 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_array[port_index]);
+ writel(portsc, port->addr);
/* flush write */
- readl(port_array[port_index]);
+ readl(port->addr);
return true;
}
@@ -1575,15 +1611,18 @@ int xhci_bus_resume(struct usb_hcd *hcd)
{
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
struct xhci_bus_state *bus_state;
- __le32 __iomem **port_array;
unsigned long flags;
int max_ports, port_index;
int slot_id;
int sret;
u32 next_state;
u32 temp, portsc;
+ struct xhci_hub *rhub;
+ struct xhci_port **ports;
- max_ports = xhci_get_ports(hcd, &port_array);
+ rhub = xhci_get_rhub(hcd);
+ ports = rhub->ports;
+ max_ports = rhub->num_ports;
bus_state = &xhci->bus_state[hcd_index(hcd)];
if (time_before(jiffies, bus_state->next_statechange))
@@ -1608,12 +1647,12 @@ int xhci_bus_resume(struct usb_hcd *hcd)
port_index = max_ports;
while (port_index--) {
- portsc = readl(port_array[port_index]);
+ portsc = readl(ports[port_index]->addr);
/* warm reset CAS limited ports stuck in polling/compliance */
if ((xhci->quirks & XHCI_MISSING_CAS) &&
(hcd->speed >= HCD_USB3) &&
- xhci_port_missing_cas_quirk(port_index, port_array)) {
+ xhci_port_missing_cas_quirk(ports[port_index])) {
xhci_dbg(xhci, "reset stuck port %d\n", port_index);
clear_bit(port_index, &bus_state->bus_suspended);
continue;
@@ -1637,7 +1676,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, port_array[port_index]);
+ writel(portsc, ports[port_index]->addr);
}
/* USB2 specific resume signaling delay and U0 link state transition */
@@ -1650,23 +1689,22 @@ int xhci_bus_resume(struct usb_hcd *hcd)
for_each_set_bit(port_index, &bus_state->bus_suspended,
BITS_PER_LONG) {
/* Clear PLC to poll it later for U0 transition */
- xhci_test_and_clear_bit(xhci, port_array, port_index,
+ xhci_test_and_clear_bit(xhci, ports[port_index],
PORT_PLC);
- xhci_set_link_state(xhci, port_array, port_index,
- XDEV_U0);
+ xhci_set_link_state(xhci, ports[port_index], XDEV_U0);
}
}
/* 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(port_array[port_index], PORT_PLC,
+ sret = xhci_handshake(ports[port_index]->addr, PORT_PLC,
PORT_PLC, 10 * 1000);
if (sret) {
xhci_warn(xhci, "port %d resume PLC timeout\n",
port_index);
continue;
}
- xhci_test_and_clear_bit(xhci, port_array, port_index, PORT_PLC);
+ xhci_test_and_clear_bit(xhci, ports[port_index], PORT_PLC);
slot_id = xhci_find_slot_id_by_port(hcd, xhci, port_index + 1);
if (slot_id)
xhci_ring_device(xhci, slot_id);
@@ -1684,4 +1722,15 @@ int xhci_bus_resume(struct usb_hcd *hcd)
return 0;
}
+unsigned long xhci_get_resuming_ports(struct usb_hcd *hcd)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ struct xhci_bus_state *bus_state;
+
+ bus_state = &xhci->bus_state[hcd_index(hcd)];
+
+ /* USB3 port wakeups are reported via usb_wakeup_notification() */
+ return bus_state->resuming_ports; /* USB2 ports only */
+}
+
#endif /* CONFIG_PM */
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index e5ace8995b3b..b1f27aa38b10 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -33,8 +33,9 @@ static struct xhci_segment *xhci_segment_alloc(struct xhci_hcd *xhci,
struct xhci_segment *seg;
dma_addr_t dma;
int i;
+ struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
- seg = kzalloc(sizeof *seg, flags);
+ seg = kzalloc_node(sizeof(*seg), flags, dev_to_node(dev));
if (!seg)
return NULL;
@@ -45,7 +46,8 @@ static struct xhci_segment *xhci_segment_alloc(struct xhci_hcd *xhci,
}
if (max_packet) {
- seg->bounce_buf = kzalloc(max_packet, flags);
+ seg->bounce_buf = kzalloc_node(max_packet, flags,
+ dev_to_node(dev));
if (!seg->bounce_buf) {
dma_pool_free(xhci->segment_pool, seg->trbs, dma);
kfree(seg);
@@ -363,8 +365,9 @@ struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci,
{
struct xhci_ring *ring;
int ret;
+ struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
- ring = kzalloc(sizeof *(ring), flags);
+ ring = kzalloc_node(sizeof(*ring), flags, dev_to_node(dev));
if (!ring)
return NULL;
@@ -458,11 +461,12 @@ struct xhci_container_ctx *xhci_alloc_container_ctx(struct xhci_hcd *xhci,
int type, gfp_t flags)
{
struct xhci_container_ctx *ctx;
+ struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
if ((type != XHCI_CTX_TYPE_DEVICE) && (type != XHCI_CTX_TYPE_INPUT))
return NULL;
- ctx = kzalloc(sizeof(*ctx), flags);
+ ctx = kzalloc_node(sizeof(*ctx), flags, dev_to_node(dev));
if (!ctx)
return NULL;
@@ -591,7 +595,7 @@ struct xhci_ring *xhci_stream_id_to_ring(
if (!ep->stream_info)
return NULL;
- if (stream_id > ep->stream_info->num_streams)
+ if (stream_id >= ep->stream_info->num_streams)
return NULL;
return ep->stream_info->stream_rings[stream_id];
}
@@ -615,6 +619,7 @@ struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci,
struct xhci_ring *cur_ring;
u64 addr;
int ret;
+ struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
xhci_dbg(xhci, "Allocating %u streams and %u "
"stream context array entries.\n",
@@ -625,7 +630,8 @@ struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci,
}
xhci->cmd_ring_reserved_trbs++;
- stream_info = kzalloc(sizeof(struct xhci_stream_info), mem_flags);
+ stream_info = kzalloc_node(sizeof(*stream_info), mem_flags,
+ dev_to_node(dev));
if (!stream_info)
goto cleanup_trbs;
@@ -633,9 +639,9 @@ struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci,
stream_info->num_stream_ctxs = num_stream_ctxs;
/* Initialize the array of virtual pointers to stream rings. */
- stream_info->stream_rings = kzalloc(
- sizeof(struct xhci_ring *)*num_streams,
- mem_flags);
+ stream_info->stream_rings = kcalloc_node(
+ num_streams, sizeof(struct xhci_ring *), mem_flags,
+ dev_to_node(dev));
if (!stream_info->stream_rings)
goto cleanup_info;
@@ -831,6 +837,7 @@ int xhci_alloc_tt_info(struct xhci_hcd *xhci,
struct xhci_tt_bw_info *tt_info;
unsigned int num_ports;
int i, j;
+ struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
if (!tt->multi)
num_ports = 1;
@@ -840,7 +847,8 @@ int xhci_alloc_tt_info(struct xhci_hcd *xhci,
for (i = 0; i < num_ports; i++, tt_info++) {
struct xhci_interval_bw_table *bw_table;
- tt_info = kzalloc(sizeof(*tt_info), mem_flags);
+ tt_info = kzalloc_node(sizeof(*tt_info), mem_flags,
+ dev_to_node(dev));
if (!tt_info)
goto free_tts;
INIT_LIST_HEAD(&tt_info->tt_list);
@@ -878,12 +886,12 @@ void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id)
dev = xhci->devs[slot_id];
- trace_xhci_free_virt_device(dev);
-
xhci->dcbaa->dev_context_ptrs[slot_id] = 0;
if (!dev)
return;
+ trace_xhci_free_virt_device(dev);
+
if (dev->tt_info)
old_active_eps = dev->tt_info->active_eps;
@@ -1054,8 +1062,7 @@ void xhci_copy_ep0_dequeue_into_input_ctx(struct xhci_hcd *xhci,
/*
* The xHCI roothub may have ports of differing speeds in any order in the port
- * status registers. xhci->port_array provides an array of the port speed for
- * each offset into the port status registers.
+ * status registers.
*
* The xHCI hardware wants to know the roothub port number that the USB device
* is attached to (or the roothub port its ancestor hub is attached to). All we
@@ -1606,6 +1613,10 @@ void xhci_endpoint_copy(struct xhci_hcd *xhci,
in_ep_ctx->ep_info2 = out_ep_ctx->ep_info2;
in_ep_ctx->deq = out_ep_ctx->deq;
in_ep_ctx->tx_info = out_ep_ctx->tx_info;
+ if (xhci->quirks & XHCI_MTK_HOST) {
+ in_ep_ctx->reserved[0] = out_ep_ctx->reserved[0];
+ in_ep_ctx->reserved[1] = out_ep_ctx->reserved[1];
+ }
}
/* Copy output xhci_slot_ctx to the input xhci_slot_ctx.
@@ -1642,7 +1653,8 @@ static int scratchpad_alloc(struct xhci_hcd *xhci, gfp_t flags)
if (!num_sp)
return 0;
- xhci->scratchpad = kzalloc(sizeof(*xhci->scratchpad), flags);
+ xhci->scratchpad = kzalloc_node(sizeof(*xhci->scratchpad), flags,
+ dev_to_node(dev));
if (!xhci->scratchpad)
goto fail_sp;
@@ -1652,7 +1664,8 @@ static int scratchpad_alloc(struct xhci_hcd *xhci, gfp_t flags)
if (!xhci->scratchpad->sp_array)
goto fail_sp2;
- xhci->scratchpad->sp_buffers = kzalloc(sizeof(void *) * num_sp, flags);
+ xhci->scratchpad->sp_buffers = kcalloc_node(num_sp, sizeof(void *),
+ flags, dev_to_node(dev));
if (!xhci->scratchpad->sp_buffers)
goto fail_sp3;
@@ -1720,14 +1733,16 @@ struct xhci_command *xhci_alloc_command(struct xhci_hcd *xhci,
bool allocate_completion, gfp_t mem_flags)
{
struct xhci_command *command;
+ struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
- command = kzalloc(sizeof(*command), mem_flags);
+ command = kzalloc_node(sizeof(*command), mem_flags, dev_to_node(dev));
if (!command)
return NULL;
if (allocate_completion) {
command->completion =
- kzalloc(sizeof(struct completion), mem_flags);
+ kzalloc_node(sizeof(struct completion), mem_flags,
+ dev_to_node(dev));
if (!command->completion) {
kfree(command);
return NULL;
@@ -1890,18 +1905,18 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
no_bw:
xhci->cmd_ring_reserved_trbs = 0;
- xhci->num_usb2_ports = 0;
- xhci->num_usb3_ports = 0;
+ xhci->usb2_rhub.num_ports = 0;
+ xhci->usb3_rhub.num_ports = 0;
xhci->num_active_eps = 0;
- kfree(xhci->usb2_ports);
- kfree(xhci->usb3_ports);
- kfree(xhci->port_array);
+ kfree(xhci->usb2_rhub.ports);
+ kfree(xhci->usb3_rhub.ports);
+ kfree(xhci->hw_ports);
kfree(xhci->rh_bw);
kfree(xhci->ext_caps);
- xhci->usb2_ports = NULL;
- xhci->usb3_ports = NULL;
- xhci->port_array = NULL;
+ xhci->usb2_rhub.ports = NULL;
+ xhci->usb3_rhub.ports = NULL;
+ xhci->hw_ports = NULL;
xhci->rh_bw = NULL;
xhci->ext_caps = NULL;
@@ -2100,6 +2115,7 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports,
int i;
u8 major_revision, minor_revision;
struct xhci_hub *rhub;
+ struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
temp = readl(addr);
major_revision = XHCI_EXT_PORT_MAJOR(temp);
@@ -2136,8 +2152,8 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports,
rhub->psi_count = XHCI_EXT_PORT_PSIC(temp);
if (rhub->psi_count) {
- rhub->psi = kcalloc(rhub->psi_count, sizeof(*rhub->psi),
- GFP_KERNEL);
+ rhub->psi = kcalloc_node(rhub->psi_count, sizeof(*rhub->psi),
+ GFP_KERNEL, dev_to_node(dev));
if (!rhub->psi)
rhub->psi_count = 0;
@@ -2186,36 +2202,53 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports,
port_offset--;
for (i = port_offset; i < (port_offset + port_count); i++) {
+ struct xhci_port *hw_port = &xhci->hw_ports[i];
/* Duplicate entry. Ignore the port if the revisions differ. */
- if (xhci->port_array[i] != 0) {
+ if (hw_port->rhub) {
xhci_warn(xhci, "Duplicate port entry, Ext Cap %p,"
" port %u\n", addr, i);
xhci_warn(xhci, "Port was marked as USB %u, "
"duplicated as USB %u\n",
- xhci->port_array[i], major_revision);
+ hw_port->rhub->maj_rev, major_revision);
/* Only adjust the roothub port counts if we haven't
* found a similar duplicate.
*/
- if (xhci->port_array[i] != major_revision &&
- xhci->port_array[i] != DUPLICATE_ENTRY) {
- if (xhci->port_array[i] == 0x03)
- xhci->num_usb3_ports--;
- else
- xhci->num_usb2_ports--;
- xhci->port_array[i] = DUPLICATE_ENTRY;
+ if (hw_port->rhub != rhub &&
+ hw_port->hcd_portnum != DUPLICATE_ENTRY) {
+ hw_port->rhub->num_ports--;
+ hw_port->hcd_portnum = DUPLICATE_ENTRY;
}
- /* FIXME: Should we disable the port? */
continue;
}
- xhci->port_array[i] = major_revision;
- if (major_revision == 0x03)
- xhci->num_usb3_ports++;
- else
- xhci->num_usb2_ports++;
+ hw_port->rhub = rhub;
+ rhub->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)
+{
+ int port_index = 0;
+ int i;
+ struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
+
+ if (!rhub->num_ports)
+ return;
+ rhub->ports = kcalloc_node(rhub->num_ports, sizeof(rhub->ports), flags,
+ dev_to_node(dev));
+ for (i = 0; i < HCS_MAX_PORTS(xhci->hcs_params1); i++) {
+ if (xhci->hw_ports[i].rhub != rhub ||
+ xhci->hw_ports[i].hcd_portnum == DUPLICATE_ENTRY)
+ continue;
+ xhci->hw_ports[i].hcd_portnum = port_index;
+ rhub->ports[port_index] = &xhci->hw_ports[i];
+ port_index++;
+ if (port_index == rhub->num_ports)
+ break;
+ }
+}
+
/*
* Scan the Extended Capabilities for the "Supported Protocol Capabilities" that
* specify what speeds each port is supposed to be. We can't count on the port
@@ -2228,16 +2261,25 @@ 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, port_index;
+ 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->port_array = kzalloc(sizeof(*xhci->port_array)*num_ports, flags);
- if (!xhci->port_array)
+ xhci->hw_ports = kcalloc_node(num_ports, sizeof(*xhci->hw_ports),
+ flags, dev_to_node(dev));
+ if (!xhci->hw_ports)
return -ENOMEM;
- xhci->rh_bw = kzalloc(sizeof(*xhci->rh_bw)*num_ports, flags);
+ for (i = 0; i < num_ports; i++) {
+ xhci->hw_ports[i].addr = &xhci->op_regs->port_status_base +
+ NUM_PORT_REGS * i;
+ xhci->hw_ports[i].hw_portnum = i;
+ }
+
+ xhci->rh_bw = kcalloc_node(num_ports, sizeof(*xhci->rh_bw), flags,
+ dev_to_node(dev));
if (!xhci->rh_bw)
return -ENOMEM;
for (i = 0; i < num_ports; i++) {
@@ -2264,7 +2306,8 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags)
XHCI_EXT_CAPS_PROTOCOL);
}
- xhci->ext_caps = kzalloc(sizeof(*xhci->ext_caps) * cap_count, flags);
+ xhci->ext_caps = kcalloc_node(cap_count, sizeof(*xhci->ext_caps),
+ flags, dev_to_node(dev));
if (!xhci->ext_caps)
return -ENOMEM;
@@ -2272,86 +2315,44 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags)
while (offset) {
xhci_add_in_port(xhci, num_ports, base + offset, cap_count);
- if (xhci->num_usb2_ports + xhci->num_usb3_ports == num_ports)
+ if (xhci->usb2_rhub.num_ports + xhci->usb3_rhub.num_ports ==
+ num_ports)
break;
offset = xhci_find_next_ext_cap(base, offset,
XHCI_EXT_CAPS_PROTOCOL);
}
-
- if (xhci->num_usb2_ports == 0 && xhci->num_usb3_ports == 0) {
+ if (xhci->usb2_rhub.num_ports == 0 && xhci->usb3_rhub.num_ports == 0) {
xhci_warn(xhci, "No ports on the roothubs?\n");
return -ENODEV;
}
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
- "Found %u USB 2.0 ports and %u USB 3.0 ports.",
- xhci->num_usb2_ports, xhci->num_usb3_ports);
+ "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->num_usb3_ports > USB_SS_MAXPORTS) {
+ 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->num_usb3_ports = USB_SS_MAXPORTS;
+ xhci->usb3_rhub.num_ports = USB_SS_MAXPORTS;
}
- if (xhci->num_usb2_ports > USB_MAXCHILDREN) {
+ 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->num_usb2_ports = USB_MAXCHILDREN;
+ xhci->usb2_rhub.num_ports = USB_MAXCHILDREN;
}
/*
* Note we could have all USB 3.0 ports, or all USB 2.0 ports.
* Not sure how the USB core will handle a hub with no ports...
*/
- if (xhci->num_usb2_ports) {
- xhci->usb2_ports = kmalloc(sizeof(*xhci->usb2_ports)*
- xhci->num_usb2_ports, flags);
- if (!xhci->usb2_ports)
- return -ENOMEM;
- port_index = 0;
- for (i = 0; i < num_ports; i++) {
- if (xhci->port_array[i] == 0x03 ||
- xhci->port_array[i] == 0 ||
- xhci->port_array[i] == DUPLICATE_ENTRY)
- continue;
+ xhci_create_rhub_port_array(xhci, &xhci->usb2_rhub, flags);
+ xhci_create_rhub_port_array(xhci, &xhci->usb3_rhub, flags);
- xhci->usb2_ports[port_index] =
- &xhci->op_regs->port_status_base +
- NUM_PORT_REGS*i;
- xhci_dbg_trace(xhci, trace_xhci_dbg_init,
- "USB 2.0 port at index %u, "
- "addr = %p", i,
- xhci->usb2_ports[port_index]);
- port_index++;
- if (port_index == xhci->num_usb2_ports)
- break;
- }
- }
- if (xhci->num_usb3_ports) {
- xhci->usb3_ports = kmalloc(sizeof(*xhci->usb3_ports)*
- xhci->num_usb3_ports, flags);
- if (!xhci->usb3_ports)
- return -ENOMEM;
-
- port_index = 0;
- for (i = 0; i < num_ports; i++)
- if (xhci->port_array[i] == 0x03) {
- xhci->usb3_ports[port_index] =
- &xhci->op_regs->port_status_base +
- NUM_PORT_REGS*i;
- xhci_dbg_trace(xhci, trace_xhci_dbg_init,
- "USB 3.0 port at index %u, "
- "addr = %p", i,
- xhci->usb3_ports[port_index]);
- port_index++;
- if (port_index == xhci->num_usb3_ports)
- break;
- }
- }
return 0;
}
diff --git a/drivers/usb/host/xhci-mtk-sch.c b/drivers/usb/host/xhci-mtk-sch.c
index eea7360a18fc..fea555570ad4 100644
--- a/drivers/usb/host/xhci-mtk-sch.c
+++ b/drivers/usb/host/xhci-mtk-sch.c
@@ -13,14 +13,20 @@
#include "xhci.h"
#include "xhci-mtk.h"
+#define SSP_BW_BOUNDARY 130000
#define SS_BW_BOUNDARY 51000
/* table 5-5. High-speed Isoc Transaction Limits in usb_20 spec */
#define HS_BW_BOUNDARY 6144
/* usb2 spec section11.18.1: at most 188 FS bytes per microframe */
#define FS_PAYLOAD_MAX 188
+/*
+ * max number of microframes for split transfer,
+ * for fs isoc in : 1 ss + 1 idle + 7 cs
+ */
+#define TT_MICROFRAMES_MAX 9
/* mtk scheduler bitmasks */
-#define EP_BPKTS(p) ((p) & 0x3f)
+#define EP_BPKTS(p) ((p) & 0x7f)
#define EP_BCSCOUNT(p) (((p) & 0x7) << 8)
#define EP_BBM(p) ((p) << 11)
#define EP_BOFFSET(p) ((p) & 0x3fff)
@@ -51,38 +57,180 @@ static int get_bw_index(struct xhci_hcd *xhci, struct usb_device *udev,
virt_dev = xhci->devs[udev->slot_id];
- if (udev->speed == USB_SPEED_SUPER) {
+ if (udev->speed >= USB_SPEED_SUPER) {
if (usb_endpoint_dir_out(&ep->desc))
bw_index = (virt_dev->real_port - 1) * 2;
else
bw_index = (virt_dev->real_port - 1) * 2 + 1;
} else {
/* add one more for each SS port */
- bw_index = virt_dev->real_port + xhci->num_usb3_ports - 1;
+ bw_index = virt_dev->real_port + xhci->usb3_rhub.num_ports - 1;
}
return bw_index;
}
+static u32 get_esit(struct xhci_ep_ctx *ep_ctx)
+{
+ u32 esit;
+
+ esit = 1 << CTX_TO_EP_INTERVAL(le32_to_cpu(ep_ctx->ep_info));
+ if (esit > XHCI_MTK_MAX_ESIT)
+ esit = XHCI_MTK_MAX_ESIT;
+
+ return esit;
+}
+
+static struct mu3h_sch_tt *find_tt(struct usb_device *udev)
+{
+ struct usb_tt *utt = udev->tt;
+ struct mu3h_sch_tt *tt, **tt_index, **ptt;
+ unsigned int port;
+ bool allocated_index = false;
+
+ if (!utt)
+ return NULL; /* Not below a TT */
+
+ /*
+ * Find/create our data structure.
+ * For hubs with a single TT, we get it directly.
+ * For hubs with multiple TTs, there's an extra level of pointers.
+ */
+ tt_index = NULL;
+ 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);
+ if (!tt_index)
+ return ERR_PTR(-ENOMEM);
+ utt->hcpriv = tt_index;
+ allocated_index = true;
+ }
+ port = udev->ttport - 1;
+ ptt = &tt_index[port];
+ } else {
+ port = 0;
+ ptt = (struct mu3h_sch_tt **) &utt->hcpriv;
+ }
+
+ tt = *ptt;
+ if (!tt) { /* Create the mu3h_sch_tt */
+ tt = kzalloc(sizeof(*tt), GFP_KERNEL);
+ if (!tt) {
+ if (allocated_index) {
+ utt->hcpriv = NULL;
+ kfree(tt_index);
+ }
+ return ERR_PTR(-ENOMEM);
+ }
+ INIT_LIST_HEAD(&tt->ep_list);
+ tt->usb_tt = utt;
+ tt->tt_port = port;
+ *ptt = tt;
+ }
+
+ return tt;
+}
+
+/* Release the TT above udev, if it's not in use */
+static void drop_tt(struct usb_device *udev)
+{
+ struct usb_tt *utt = udev->tt;
+ struct mu3h_sch_tt *tt, **tt_index, **ptt;
+ int i, cnt;
+
+ if (!utt || !utt->hcpriv)
+ return; /* Not below a TT, or never allocated */
+
+ cnt = 0;
+ if (utt->multi) {
+ tt_index = utt->hcpriv;
+ ptt = &tt_index[udev->ttport - 1];
+ /* How many entries are left in tt_index? */
+ for (i = 0; i < utt->hub->maxchild; ++i)
+ cnt += !!tt_index[i];
+ } else {
+ tt_index = NULL;
+ ptt = (struct mu3h_sch_tt **)&utt->hcpriv;
+ }
+
+ tt = *ptt;
+ if (!tt || !list_empty(&tt->ep_list))
+ return; /* never allocated , or still in use*/
+
+ *ptt = NULL;
+ kfree(tt);
+
+ if (cnt == 1) {
+ utt->hcpriv = NULL;
+ kfree(tt_index);
+ }
+}
+
+static struct mu3h_sch_ep_info *create_sch_ep(struct usb_device *udev,
+ struct usb_host_endpoint *ep, struct xhci_ep_ctx *ep_ctx)
+{
+ struct mu3h_sch_ep_info *sch_ep;
+ struct mu3h_sch_tt *tt = NULL;
+ u32 len_bw_budget_table;
+ size_t mem_size;
+
+ if (is_fs_or_ls(udev->speed))
+ len_bw_budget_table = TT_MICROFRAMES_MAX;
+ else if ((udev->speed >= USB_SPEED_SUPER)
+ && usb_endpoint_xfer_isoc(&ep->desc))
+ len_bw_budget_table = get_esit(ep_ctx);
+ else
+ len_bw_budget_table = 1;
+
+ mem_size = sizeof(struct mu3h_sch_ep_info) +
+ len_bw_budget_table * sizeof(u32);
+ sch_ep = kzalloc(mem_size, GFP_KERNEL);
+ if (!sch_ep)
+ return ERR_PTR(-ENOMEM);
+
+ if (is_fs_or_ls(udev->speed)) {
+ tt = find_tt(udev);
+ if (IS_ERR(tt)) {
+ kfree(sch_ep);
+ return ERR_PTR(-ENOMEM);
+ }
+ }
+
+ sch_ep->sch_tt = tt;
+ sch_ep->ep = ep;
+
+ return sch_ep;
+}
+
static void setup_sch_info(struct usb_device *udev,
struct xhci_ep_ctx *ep_ctx, struct mu3h_sch_ep_info *sch_ep)
{
u32 ep_type;
- u32 ep_interval;
- u32 max_packet_size;
+ u32 maxpkt;
u32 max_burst;
u32 mult;
u32 esit_pkts;
+ u32 max_esit_payload;
+ u32 *bwb_table = sch_ep->bw_budget_table;
+ int i;
ep_type = CTX_TO_EP_TYPE(le32_to_cpu(ep_ctx->ep_info2));
- ep_interval = CTX_TO_EP_INTERVAL(le32_to_cpu(ep_ctx->ep_info));
- max_packet_size = MAX_PACKET_DECODED(le32_to_cpu(ep_ctx->ep_info2));
+ maxpkt = MAX_PACKET_DECODED(le32_to_cpu(ep_ctx->ep_info2));
max_burst = CTX_TO_MAX_BURST(le32_to_cpu(ep_ctx->ep_info2));
mult = CTX_TO_EP_MULT(le32_to_cpu(ep_ctx->ep_info));
-
- sch_ep->esit = 1 << ep_interval;
+ max_esit_payload =
+ (CTX_TO_MAX_ESIT_PAYLOAD_HI(
+ le32_to_cpu(ep_ctx->ep_info)) << 16) |
+ CTX_TO_MAX_ESIT_PAYLOAD(le32_to_cpu(ep_ctx->tx_info));
+
+ sch_ep->esit = get_esit(ep_ctx);
+ sch_ep->ep_type = ep_type;
+ sch_ep->maxpkt = maxpkt;
sch_ep->offset = 0;
sch_ep->burst_mode = 0;
+ sch_ep->repeat = 0;
if (udev->speed == USB_SPEED_HIGH) {
sch_ep->cs_count = 0;
@@ -93,7 +241,6 @@ static void setup_sch_info(struct usb_device *udev,
* in a interval
*/
sch_ep->num_budget_microframes = 1;
- sch_ep->repeat = 0;
/*
* xHCI spec section6.2.3.4
@@ -101,19 +248,33 @@ static void setup_sch_info(struct usb_device *udev,
* opportunities per microframe
*/
sch_ep->pkts = max_burst + 1;
- sch_ep->bw_cost_per_microframe = max_packet_size * sch_ep->pkts;
- } else if (udev->speed == USB_SPEED_SUPER) {
+ sch_ep->bw_cost_per_microframe = maxpkt * sch_ep->pkts;
+ bwb_table[0] = sch_ep->bw_cost_per_microframe;
+ } else if (udev->speed >= USB_SPEED_SUPER) {
/* usb3_r1 spec section4.4.7 & 4.4.8 */
sch_ep->cs_count = 0;
- esit_pkts = (mult + 1) * (max_burst + 1);
+ sch_ep->burst_mode = 1;
+ /*
+ * some device's (d)wBytesPerInterval is set as 0,
+ * then max_esit_payload is 0, so evaluate esit_pkts from
+ * mult and burst
+ */
+ esit_pkts = DIV_ROUND_UP(max_esit_payload, maxpkt);
+ if (esit_pkts == 0)
+ esit_pkts = (mult + 1) * (max_burst + 1);
+
if (ep_type == INT_IN_EP || ep_type == INT_OUT_EP) {
sch_ep->pkts = esit_pkts;
sch_ep->num_budget_microframes = 1;
- sch_ep->repeat = 0;
+ bwb_table[0] = maxpkt * sch_ep->pkts;
}
if (ep_type == ISOC_IN_EP || ep_type == ISOC_OUT_EP) {
- if (esit_pkts <= sch_ep->esit)
+ u32 remainder;
+
+ if (sch_ep->esit == 1)
+ sch_ep->pkts = esit_pkts;
+ else if (esit_pkts <= sch_ep->esit)
sch_ep->pkts = 1;
else
sch_ep->pkts = roundup_pow_of_two(esit_pkts)
@@ -122,43 +283,48 @@ static void setup_sch_info(struct usb_device *udev,
sch_ep->num_budget_microframes =
DIV_ROUND_UP(esit_pkts, sch_ep->pkts);
- if (sch_ep->num_budget_microframes > 1)
- sch_ep->repeat = 1;
- else
- sch_ep->repeat = 0;
+ sch_ep->repeat = !!(sch_ep->num_budget_microframes > 1);
+ sch_ep->bw_cost_per_microframe = maxpkt * sch_ep->pkts;
+
+ remainder = sch_ep->bw_cost_per_microframe;
+ remainder *= sch_ep->num_budget_microframes;
+ remainder -= (maxpkt * esit_pkts);
+ for (i = 0; i < sch_ep->num_budget_microframes - 1; i++)
+ bwb_table[i] = sch_ep->bw_cost_per_microframe;
+
+ /* last one <= bw_cost_per_microframe */
+ bwb_table[i] = remainder;
}
- sch_ep->bw_cost_per_microframe = max_packet_size * sch_ep->pkts;
} else if (is_fs_or_ls(udev->speed)) {
+ sch_ep->pkts = 1; /* at most one packet for each microframe */
/*
- * usb_20 spec section11.18.4
- * assume worst cases
+ * num_budget_microframes and cs_count will be updated when
+ * check TT for INT_OUT_EP, ISOC/INT_IN_EP type
*/
- sch_ep->repeat = 0;
- sch_ep->pkts = 1; /* at most one packet for each microframe */
- if (ep_type == INT_IN_EP || ep_type == INT_OUT_EP) {
- sch_ep->cs_count = 3; /* at most need 3 CS*/
- /* one for SS and one for budgeted transaction */
- sch_ep->num_budget_microframes = sch_ep->cs_count + 2;
- sch_ep->bw_cost_per_microframe = max_packet_size;
- }
- if (ep_type == ISOC_OUT_EP) {
+ sch_ep->cs_count = DIV_ROUND_UP(maxpkt, FS_PAYLOAD_MAX);
+ sch_ep->num_budget_microframes = sch_ep->cs_count;
+ sch_ep->bw_cost_per_microframe =
+ (maxpkt < FS_PAYLOAD_MAX) ? maxpkt : FS_PAYLOAD_MAX;
+ /* init budget table */
+ if (ep_type == ISOC_OUT_EP) {
+ for (i = 0; i < sch_ep->num_budget_microframes; i++)
+ bwb_table[i] = sch_ep->bw_cost_per_microframe;
+ } else if (ep_type == INT_OUT_EP) {
+ /* only first one consumes bandwidth, others as zero */
+ bwb_table[0] = sch_ep->bw_cost_per_microframe;
+ } else { /* INT_IN_EP or ISOC_IN_EP */
+ bwb_table[0] = 0; /* start split */
+ bwb_table[1] = 0; /* idle */
/*
- * the best case FS budget assumes that 188 FS bytes
- * occur in each microframe
+ * due to cs_count will be updated according to cs
+ * position, assign all remainder budget array
+ * elements as @bw_cost_per_microframe, but only first
+ * @num_budget_microframes elements will be used later
*/
- sch_ep->num_budget_microframes = DIV_ROUND_UP(
- max_packet_size, FS_PAYLOAD_MAX);
- sch_ep->bw_cost_per_microframe = FS_PAYLOAD_MAX;
- sch_ep->cs_count = sch_ep->num_budget_microframes;
- }
- if (ep_type == ISOC_IN_EP) {
- /* at most need additional two CS. */
- sch_ep->cs_count = DIV_ROUND_UP(
- max_packet_size, FS_PAYLOAD_MAX) + 2;
- sch_ep->num_budget_microframes = sch_ep->cs_count + 2;
- sch_ep->bw_cost_per_microframe = FS_PAYLOAD_MAX;
+ for (i = 2; i < TT_MICROFRAMES_MAX; i++)
+ bwb_table[i] = sch_ep->bw_cost_per_microframe;
}
}
}
@@ -169,6 +335,7 @@ static u32 get_max_bw(struct mu3h_sch_bw_info *sch_bw,
{
u32 num_esit;
u32 max_bw = 0;
+ u32 bw;
int i;
int j;
@@ -177,15 +344,17 @@ static u32 get_max_bw(struct mu3h_sch_bw_info *sch_bw,
u32 base = offset + i * sch_ep->esit;
for (j = 0; j < sch_ep->num_budget_microframes; j++) {
- if (sch_bw->bus_bw[base + j] > max_bw)
- max_bw = sch_bw->bus_bw[base + j];
+ bw = sch_bw->bus_bw[base + j] +
+ sch_ep->bw_budget_table[j];
+ if (bw > max_bw)
+ max_bw = bw;
}
}
return max_bw;
}
static void update_bus_bw(struct mu3h_sch_bw_info *sch_bw,
- struct mu3h_sch_ep_info *sch_ep, int bw_cost)
+ struct mu3h_sch_ep_info *sch_ep, bool used)
{
u32 num_esit;
u32 base;
@@ -195,9 +364,105 @@ static void update_bus_bw(struct mu3h_sch_bw_info *sch_bw,
num_esit = XHCI_MTK_MAX_ESIT / sch_ep->esit;
for (i = 0; i < num_esit; i++) {
base = sch_ep->offset + i * sch_ep->esit;
+ for (j = 0; j < sch_ep->num_budget_microframes; j++) {
+ if (used)
+ sch_bw->bus_bw[base + j] +=
+ sch_ep->bw_budget_table[j];
+ else
+ sch_bw->bus_bw[base + j] -=
+ sch_ep->bw_budget_table[j];
+ }
+ }
+}
+
+static int check_sch_tt(struct usb_device *udev,
+ struct mu3h_sch_ep_info *sch_ep, u32 offset)
+{
+ struct mu3h_sch_tt *tt = sch_ep->sch_tt;
+ u32 extra_cs_count;
+ u32 fs_budget_start;
+ u32 start_ss, last_ss;
+ u32 start_cs, last_cs;
+ int i;
+
+ start_ss = offset % 8;
+ fs_budget_start = (start_ss + 1) % 8;
+
+ if (sch_ep->ep_type == ISOC_OUT_EP) {
+ last_ss = start_ss + sch_ep->cs_count - 1;
+
+ /*
+ * usb_20 spec section11.18:
+ * must never schedule Start-Split in Y6
+ */
+ if (!(start_ss == 7 || last_ss < 6))
+ return -ERANGE;
+
+ for (i = 0; i < sch_ep->cs_count; i++)
+ if (test_bit(offset + i, tt->split_bit_map))
+ return -ERANGE;
+
+ } else {
+ u32 cs_count = DIV_ROUND_UP(sch_ep->maxpkt, FS_PAYLOAD_MAX);
+
+ /*
+ * usb_20 spec section11.18:
+ * must never schedule Start-Split in Y6
+ */
+ if (start_ss == 6)
+ return -ERANGE;
+
+ /* one uframe for ss + one uframe for idle */
+ start_cs = (start_ss + 2) % 8;
+ last_cs = start_cs + cs_count - 1;
+
+ if (last_cs > 7)
+ return -ERANGE;
+
+ if (sch_ep->ep_type == ISOC_IN_EP)
+ extra_cs_count = (last_cs == 7) ? 1 : 2;
+ else /* ep_type : INTR IN / INTR OUT */
+ extra_cs_count = (fs_budget_start == 6) ? 1 : 2;
+
+ cs_count += extra_cs_count;
+ if (cs_count > 7)
+ cs_count = 7; /* HW limit */
+
+ for (i = 0; i < cs_count + 2; i++) {
+ if (test_bit(offset + i, tt->split_bit_map))
+ return -ERANGE;
+ }
+
+ sch_ep->cs_count = cs_count;
+ /* one for ss, the other for idle */
+ sch_ep->num_budget_microframes = cs_count + 2;
+
+ /*
+ * if interval=1, maxp >752, num_budge_micoframe is larger
+ * than sch_ep->esit, will overstep boundary
+ */
+ if (sch_ep->num_budget_microframes > sch_ep->esit)
+ sch_ep->num_budget_microframes = sch_ep->esit;
+ }
+
+ return 0;
+}
+
+static void update_sch_tt(struct usb_device *udev,
+ struct mu3h_sch_ep_info *sch_ep)
+{
+ struct mu3h_sch_tt *tt = sch_ep->sch_tt;
+ u32 base, num_esit;
+ int i, j;
+
+ num_esit = XHCI_MTK_MAX_ESIT / sch_ep->esit;
+ for (i = 0; i < num_esit; i++) {
+ base = sch_ep->offset + i * sch_ep->esit;
for (j = 0; j < sch_ep->num_budget_microframes; j++)
- sch_bw->bus_bw[base + j] += bw_cost;
+ set_bit(base + j, tt->split_bit_map);
}
+
+ list_add_tail(&sch_ep->tt_endpoint, &tt->ep_list);
}
static int check_sch_bw(struct usb_device *udev,
@@ -205,17 +470,16 @@ static int check_sch_bw(struct usb_device *udev,
{
u32 offset;
u32 esit;
- u32 num_budget_microframes;
u32 min_bw;
u32 min_index;
u32 worst_bw;
u32 bw_boundary;
-
- if (sch_ep->esit > XHCI_MTK_MAX_ESIT)
- sch_ep->esit = XHCI_MTK_MAX_ESIT;
+ u32 min_num_budget;
+ u32 min_cs_count;
+ bool tt_offset_ok = false;
+ int ret;
esit = sch_ep->esit;
- num_budget_microframes = sch_ep->num_budget_microframes;
/*
* Search through all possible schedule microframes.
@@ -223,36 +487,56 @@ static int check_sch_bw(struct usb_device *udev,
*/
min_bw = ~0;
min_index = 0;
+ min_cs_count = sch_ep->cs_count;
+ min_num_budget = sch_ep->num_budget_microframes;
for (offset = 0; offset < esit; offset++) {
- if ((offset + num_budget_microframes) > sch_ep->esit)
- break;
+ if (is_fs_or_ls(udev->speed)) {
+ ret = check_sch_tt(udev, sch_ep, offset);
+ if (ret)
+ continue;
+ else
+ tt_offset_ok = true;
+ }
- /*
- * usb_20 spec section11.18:
- * must never schedule Start-Split in Y6
- */
- if (is_fs_or_ls(udev->speed) && (offset % 8 == 6))
- continue;
+ if ((offset + sch_ep->num_budget_microframes) > sch_ep->esit)
+ break;
worst_bw = get_max_bw(sch_bw, sch_ep, offset);
if (min_bw > worst_bw) {
min_bw = worst_bw;
min_index = offset;
+ min_cs_count = sch_ep->cs_count;
+ min_num_budget = sch_ep->num_budget_microframes;
}
if (min_bw == 0)
break;
}
- sch_ep->offset = min_index;
- bw_boundary = (udev->speed == USB_SPEED_SUPER)
- ? SS_BW_BOUNDARY : HS_BW_BOUNDARY;
+ if (udev->speed == USB_SPEED_SUPER_PLUS)
+ bw_boundary = SSP_BW_BOUNDARY;
+ else if (udev->speed == USB_SPEED_SUPER)
+ bw_boundary = SS_BW_BOUNDARY;
+ else
+ bw_boundary = HS_BW_BOUNDARY;
/* check bandwidth */
- if (min_bw + sch_ep->bw_cost_per_microframe > bw_boundary)
+ if (min_bw > bw_boundary)
return -ERANGE;
+ sch_ep->offset = min_index;
+ sch_ep->cs_count = min_cs_count;
+ sch_ep->num_budget_microframes = min_num_budget;
+
+ if (is_fs_or_ls(udev->speed)) {
+ /* all offset for tt is not ok*/
+ if (!tt_offset_ok)
+ return -ERANGE;
+
+ update_sch_tt(udev, sch_ep);
+ }
+
/* update bus bandwidth info */
- update_bus_bw(sch_bw, sch_ep, sch_ep->bw_cost_per_microframe);
+ update_bus_bw(sch_bw, sch_ep, 1);
return 0;
}
@@ -284,7 +568,7 @@ int xhci_mtk_sch_init(struct xhci_hcd_mtk *mtk)
int i;
/* ss IN and OUT are separated */
- num_usb_bus = xhci->num_usb3_ports * 2 + xhci->num_usb2_ports;
+ 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);
if (sch_array == NULL)
@@ -347,8 +631,8 @@ int xhci_mtk_add_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev,
bw_index = get_bw_index(xhci, udev, ep);
sch_bw = &sch_array[bw_index];
- sch_ep = kzalloc(sizeof(struct mu3h_sch_ep_info), GFP_NOIO);
- if (!sch_ep)
+ sch_ep = create_sch_ep(udev, ep, ep_ctx);
+ if (IS_ERR_OR_NULL(sch_ep))
return -ENOMEM;
setup_sch_info(udev, ep_ctx, sch_ep);
@@ -356,12 +640,14 @@ int xhci_mtk_add_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev,
ret = check_sch_bw(udev, sch_bw, sch_ep);
if (ret) {
xhci_err(xhci, "Not enough bandwidth!\n");
+ if (is_fs_or_ls(udev->speed))
+ drop_tt(udev);
+
kfree(sch_ep);
return -ENOSPC;
}
list_add_tail(&sch_ep->endpoint, &sch_bw->bw_ep_list);
- sch_ep->ep = ep;
ep_ctx->reserved[0] |= cpu_to_le32(EP_BPKTS(sch_ep->pkts)
| EP_BCSCOUNT(sch_ep->cs_count) | EP_BBM(sch_ep->burst_mode));
@@ -406,9 +692,12 @@ void xhci_mtk_drop_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev,
list_for_each_entry(sch_ep, &sch_bw->bw_ep_list, endpoint) {
if (sch_ep->ep == ep) {
- update_bus_bw(sch_bw, sch_ep,
- -sch_ep->bw_cost_per_microframe);
+ update_bus_bw(sch_bw, sch_ep, 0);
list_del(&sch_ep->endpoint);
+ if (is_fs_or_ls(udev->speed)) {
+ list_del(&sch_ep->tt_endpoint);
+ drop_tt(udev);
+ }
kfree(sch_ep);
break;
}
diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c
index 7334da9e9779..60987c787e44 100644
--- a/drivers/usb/host/xhci-mtk.c
+++ b/drivers/usb/host/xhci-mtk.c
@@ -590,12 +590,14 @@ static int xhci_mtk_remove(struct platform_device *dev)
struct xhci_hcd_mtk *mtk = platform_get_drvdata(dev);
struct usb_hcd *hcd = mtk->hcd;
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ struct usb_hcd *shared_hcd = xhci->shared_hcd;
- usb_remove_hcd(xhci->shared_hcd);
+ usb_remove_hcd(shared_hcd);
+ xhci->shared_hcd = NULL;
device_init_wakeup(&dev->dev, false);
usb_remove_hcd(hcd);
- usb_put_hcd(xhci->shared_hcd);
+ usb_put_hcd(shared_hcd);
usb_put_hcd(hcd);
xhci_mtk_sch_exit(mtk);
xhci_mtk_clks_disable(mtk);
@@ -642,10 +644,10 @@ static int __maybe_unused xhci_mtk_resume(struct device *dev)
xhci_mtk_host_enable(mtk);
xhci_dbg(xhci, "%s: restart port polling\n", __func__);
- set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
- usb_hcd_poll_rh_status(hcd);
set_bit(HCD_FLAG_POLL_RH, &xhci->shared_hcd->flags);
usb_hcd_poll_rh_status(xhci->shared_hcd);
+ set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
+ usb_hcd_poll_rh_status(hcd);
return 0;
}
diff --git a/drivers/usb/host/xhci-mtk.h b/drivers/usb/host/xhci-mtk.h
index cc59d80b663b..8be8c5f7ff62 100644
--- a/drivers/usb/host/xhci-mtk.h
+++ b/drivers/usb/host/xhci-mtk.h
@@ -20,6 +20,19 @@
#define XHCI_MTK_MAX_ESIT 64
/**
+ * @split_bit_map: used to avoid split microframes overlay
+ * @ep_list: Endpoints using this TT
+ * @usb_tt: usb TT related
+ * @tt_port: TT port number
+ */
+struct mu3h_sch_tt {
+ DECLARE_BITMAP(split_bit_map, XHCI_MTK_MAX_ESIT);
+ struct list_head ep_list;
+ struct usb_tt *usb_tt;
+ int tt_port;
+};
+
+/**
* struct mu3h_sch_bw_info: schedule information for bandwidth domain
*
* @bus_bw: array to keep track of bandwidth already used at each uframes
@@ -41,6 +54,10 @@ struct mu3h_sch_bw_info {
* (@repeat==1) scheduled within the interval
* @bw_cost_per_microframe: bandwidth cost per microframe
* @endpoint: linked into bandwidth domain which it belongs to
+ * @tt_endpoint: linked into mu3h_sch_tt's list which it belongs to
+ * @sch_tt: mu3h_sch_tt linked into
+ * @ep_type: endpoint type
+ * @maxpkt: max packet size of endpoint
* @ep: address of usb_host_endpoint struct
* @offset: which uframe of the interval that transfer should be
* scheduled first time within the interval
@@ -57,12 +74,17 @@ struct mu3h_sch_bw_info {
* times; 1: distribute the (bMaxBurst+1)*(Mult+1) packets
* according to @pkts and @repeat. normal mode is used by
* default
+ * @bw_budget_table: table to record bandwidth budget per microframe
*/
struct mu3h_sch_ep_info {
u32 esit;
u32 num_budget_microframes;
u32 bw_cost_per_microframe;
struct list_head endpoint;
+ struct list_head tt_endpoint;
+ struct mu3h_sch_tt *sch_tt;
+ u32 ep_type;
+ u32 maxpkt;
void *ep;
/*
* mtk xHCI scheduling information put into reserved DWs
@@ -73,6 +95,7 @@ struct mu3h_sch_ep_info {
u32 pkts;
u32 cs_count;
u32 burst_mode;
+ u32 bw_budget_table[0];
};
#define MU3C_U3_PORT_MAX 4
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index 85ffda85f8ab..a9ec7051f286 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -41,6 +41,13 @@
#define PCI_DEVICE_ID_INTEL_BROXTON_B_XHCI 0x1aa8
#define PCI_DEVICE_ID_INTEL_APL_XHCI 0x5aa8
#define PCI_DEVICE_ID_INTEL_DNV_XHCI 0x19d0
+#define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_2C_XHCI 0x15b5
+#define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_4C_XHCI 0x15b6
+#define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_2C_XHCI 0x15db
+#define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_4C_XHCI 0x15d4
+#define PCI_DEVICE_ID_INTEL_TITAN_RIDGE_2C_XHCI 0x15e9
+#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_PROMONTORYA_4 0x43b9
#define PCI_DEVICE_ID_AMD_PROMONTORYA_3 0x43ba
@@ -132,6 +139,10 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
pdev->device == 0x43bb))
xhci->quirks |= XHCI_SUSPEND_DELAY;
+ if (pdev->vendor == PCI_VENDOR_ID_AMD &&
+ (pdev->device == 0x15e0 || pdev->device == 0x15e1))
+ xhci->quirks |= XHCI_SNPS_BROKEN_SUSPEND;
+
if (pdev->vendor == PCI_VENDOR_ID_AMD)
xhci->quirks |= XHCI_TRUST_TX_LENGTH;
@@ -179,16 +190,30 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
xhci->quirks |= XHCI_PME_STUCK_QUIRK;
}
if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
- pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI) {
+ pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI)
xhci->quirks |= XHCI_SSIC_PORT_UNUSED;
+ if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
+ (pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI ||
+ pdev->device == PCI_DEVICE_ID_INTEL_APL_XHCI))
xhci->quirks |= XHCI_INTEL_USB_ROLE_SW;
- }
if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
(pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI ||
+ pdev->device == PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_XHCI ||
+ pdev->device == PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_XHCI ||
pdev->device == PCI_DEVICE_ID_INTEL_APL_XHCI ||
pdev->device == PCI_DEVICE_ID_INTEL_DNV_XHCI))
xhci->quirks |= XHCI_MISSING_CAS;
+ if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
+ (pdev->device == PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_2C_XHCI ||
+ pdev->device == PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_4C_XHCI ||
+ pdev->device == PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_2C_XHCI ||
+ pdev->device == PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_4C_XHCI ||
+ pdev->device == PCI_DEVICE_ID_INTEL_TITAN_RIDGE_2C_XHCI ||
+ pdev->device == PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_XHCI ||
+ pdev->device == PCI_DEVICE_ID_INTEL_TITAN_RIDGE_DD_XHCI))
+ xhci->quirks |= XHCI_DEFAULT_PM_RUNTIME_ALLOW;
+
if (pdev->vendor == PCI_VENDOR_ID_ETRON &&
pdev->device == PCI_DEVICE_ID_EJ168) {
xhci->quirks |= XHCI_RESET_ON_RESUME;
@@ -196,11 +221,15 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
xhci->quirks |= XHCI_BROKEN_STREAMS;
}
if (pdev->vendor == PCI_VENDOR_ID_RENESAS &&
- pdev->device == 0x0014)
+ pdev->device == 0x0014) {
xhci->quirks |= XHCI_TRUST_TX_LENGTH;
+ xhci->quirks |= XHCI_ZERO_64B_REGS;
+ }
if (pdev->vendor == PCI_VENDOR_ID_RENESAS &&
- pdev->device == 0x0015)
+ pdev->device == 0x0015) {
xhci->quirks |= XHCI_RESET_ON_RESUME;
+ xhci->quirks |= XHCI_ZERO_64B_REGS;
+ }
if (pdev->vendor == PCI_VENDOR_ID_VIA)
xhci->quirks |= XHCI_RESET_ON_RESUME;
@@ -223,6 +252,11 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
if (pdev->vendor == PCI_VENDOR_ID_TI && pdev->device == 0x8241)
xhci->quirks |= XHCI_LIMIT_ENDPOINT_INTERVAL_7;
+ if ((pdev->vendor == PCI_VENDOR_ID_BROADCOM ||
+ pdev->vendor == PCI_VENDOR_ID_CAVIUM) &&
+ pdev->device == 0x9026)
+ xhci->quirks |= XHCI_RESET_PLL_ON_DISCONNECT;
+
if (xhci->quirks & XHCI_RESET_ON_RESUME)
xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
"QUIRK: Resetting on resume");
@@ -284,13 +318,6 @@ static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
driver = (struct hc_driver *)id->driver_data;
- /* For some HW implementation, a XHCI reset is just not enough... */
- if (usb_xhci_needs_pci_reset(dev)) {
- dev_info(&dev->dev, "Resetting\n");
- if (pci_reset_function_locked(dev))
- dev_warn(&dev->dev, "Reset failed");
- }
-
/* Prevent runtime suspending between USB-2 and USB-3 initialization */
pm_runtime_get_noresume(&dev->dev);
@@ -335,6 +362,9 @@ static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
/* USB-2 and USB-3 roothubs initialized, allow runtime pm suspend */
pm_runtime_put_noidle(&dev->dev);
+ if (xhci->quirks & XHCI_DEFAULT_PM_RUNTIME_ALLOW)
+ pm_runtime_allow(&dev->dev);
+
return 0;
put_usb3_hcd:
@@ -352,9 +382,14 @@ static void xhci_pci_remove(struct pci_dev *dev)
xhci = hcd_to_xhci(pci_get_drvdata(dev));
xhci->xhc_state |= XHCI_STATE_REMOVING;
+
+ if (xhci->quirks & XHCI_DEFAULT_PM_RUNTIME_ALLOW)
+ pm_runtime_forbid(&dev->dev);
+
if (xhci->shared_hcd) {
usb_remove_hcd(xhci->shared_hcd);
usb_put_hcd(xhci->shared_hcd);
+ xhci->shared_hcd = NULL;
}
/* Workaround for spurious wakeups at shutdown with HSW */
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index c1b22fc64e38..ef09cb06212f 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -18,6 +18,7 @@
#include <linux/usb/phy.h>
#include <linux/slab.h>
#include <linux/acpi.h>
+#include <linux/usb/of.h>
#include "xhci.h"
#include "xhci-plat.h"
@@ -105,6 +106,7 @@ static const struct xhci_plat_priv xhci_plat_renesas_rcar_gen2 = {
};
static const struct xhci_plat_priv xhci_plat_renesas_rcar_gen3 = {
+ .firmware_name = XHCI_RCAR_FIRMWARE_NAME_V3,
.init_quirk = xhci_rcar_init_quirk,
.plat_start = xhci_rcar_start,
.resume_quirk = xhci_rcar_resume_quirk,
@@ -152,7 +154,7 @@ static int xhci_plat_probe(struct platform_device *pdev)
{
const struct xhci_plat_priv *priv_match;
const struct hc_driver *driver;
- struct device *sysdev;
+ struct device *sysdev, *tmpdev;
struct xhci_hcd *xhci;
struct resource *res;
struct usb_hcd *hcd;
@@ -272,19 +274,24 @@ static int xhci_plat_probe(struct platform_device *pdev)
goto disable_clk;
}
- if (device_property_read_bool(sysdev, "usb2-lpm-disable"))
- xhci->quirks |= XHCI_HW_LPM_DISABLE;
+ /* imod_interval is the interrupt moderation value in nanoseconds. */
+ xhci->imod_interval = 40000;
- if (device_property_read_bool(sysdev, "usb3-lpm-capable"))
- xhci->quirks |= XHCI_LPM_SUPPORT;
+ /* Iterate over all parent nodes for finding quirks */
+ for (tmpdev = &pdev->dev; tmpdev; tmpdev = tmpdev->parent) {
- if (device_property_read_bool(&pdev->dev, "quirk-broken-port-ped"))
- xhci->quirks |= XHCI_BROKEN_PORT_PED;
+ if (device_property_read_bool(tmpdev, "usb2-lpm-disable"))
+ xhci->quirks |= XHCI_HW_LPM_DISABLE;
- /* imod_interval is the interrupt moderation value in nanoseconds. */
- xhci->imod_interval = 40000;
- device_property_read_u32(sysdev, "imod-interval-ns",
- &xhci->imod_interval);
+ if (device_property_read_bool(tmpdev, "usb3-lpm-capable"))
+ xhci->quirks |= XHCI_LPM_SUPPORT;
+
+ if (device_property_read_bool(tmpdev, "quirk-broken-port-ped"))
+ xhci->quirks |= XHCI_BROKEN_PORT_PED;
+
+ device_property_read_u32(tmpdev, "imod-interval-ns",
+ &xhci->imod_interval);
+ }
hcd->usb_phy = devm_usb_get_phy_by_phandle(sysdev, "usb-phy", 0);
if (IS_ERR(hcd->usb_phy)) {
@@ -299,6 +306,8 @@ static int xhci_plat_probe(struct platform_device *pdev)
hcd->skip_phy_initialization = 1;
}
+ hcd->tpl_support = of_usb_host_tpl_support(sysdev->of_node);
+ xhci->shared_hcd->tpl_support = hcd->tpl_support;
ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
if (ret)
goto disable_usb_phy;
@@ -353,14 +362,16 @@ static int xhci_plat_remove(struct platform_device *dev)
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
struct clk *clk = xhci->clk;
struct clk *reg_clk = xhci->reg_clk;
+ struct usb_hcd *shared_hcd = xhci->shared_hcd;
xhci->xhc_state |= XHCI_STATE_REMOVING;
- usb_remove_hcd(xhci->shared_hcd);
+ usb_remove_hcd(shared_hcd);
+ xhci->shared_hcd = NULL;
usb_phy_shutdown(hcd->usb_phy);
usb_remove_hcd(hcd);
- usb_put_hcd(xhci->shared_hcd);
+ usb_put_hcd(shared_hcd);
clk_disable_unprepare(clk);
clk_disable_unprepare(reg_clk);
diff --git a/drivers/usb/host/xhci-rcar.c b/drivers/usb/host/xhci-rcar.c
index f33ffc2bc4ed..a6e463715779 100644
--- a/drivers/usb/host/xhci-rcar.c
+++ b/drivers/usb/host/xhci-rcar.c
@@ -17,9 +17,8 @@
#include "xhci-rcar.h"
/*
-* - The V3 firmware is for r8a7796 (with good performance) and r8a7795 es2.0
-* or later.
-* - The V2 firmware can be used on both r8a7795 (es1.x) and r8a7796.
+* - The V3 firmware is for almost all R-Car Gen3 (except r8a7795 ES1.x)
+* - The V2 firmware is for r8a7795 ES1.x.
* - The V2 firmware is possible to use on R-Car Gen2. However, the V2 causes
* performance degradation. So, this driver continues to use the V1 if R-Car
* Gen2.
@@ -30,6 +29,7 @@ MODULE_FIRMWARE(XHCI_RCAR_FIRMWARE_NAME_V2);
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 */
@@ -42,6 +42,12 @@ MODULE_FIRMWARE(XHCI_RCAR_FIRMWARE_NAME_V3);
#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
@@ -75,18 +81,6 @@ static const struct soc_device_attribute rcar_quirks_match[] = {
.soc_id = "r8a7795", .revision = "ES1.*",
.data = (void *)RCAR_XHCI_FIRMWARE_V2,
},
- {
- .soc_id = "r8a7795",
- .data = (void *)RCAR_XHCI_FIRMWARE_V3,
- },
- {
- .soc_id = "r8a7796",
- .data = (void *)RCAR_XHCI_FIRMWARE_V3,
- },
- {
- .soc_id = "r8a77965",
- .data = (void *)RCAR_XHCI_FIRMWARE_V3,
- },
{ /* sentinel */ },
};
@@ -213,6 +207,22 @@ static int xhci_rcar_download_firmware(struct usb_hcd *hcd)
return retval;
}
+static bool xhci_rcar_wait_for_pll_active(struct usb_hcd *hcd)
+{
+ int timeout = 1000;
+ u32 val, mask = RCAR_USB3_AXH_STA_PLL_ACTIVE_MASK;
+
+ while (timeout > 0) {
+ val = readl(hcd->regs + RCAR_USB3_AXH_STA);
+ if ((val & mask) == mask)
+ return true;
+ udelay(1);
+ timeout--;
+ }
+
+ return false;
+}
+
/* This function needs to initialize a "phy" of usb before */
int xhci_rcar_init_quirk(struct usb_hcd *hcd)
{
@@ -233,6 +243,9 @@ int xhci_rcar_init_quirk(struct usb_hcd *hcd)
xhci_rcar_is_gen3(hcd->self.controller))
xhci->quirks |= XHCI_NO_64BIT_SUPPORT;
+ if (!xhci_rcar_wait_for_pll_active(hcd))
+ return -ETIMEDOUT;
+
return xhci_rcar_download_firmware(hcd);
}
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 91a1a824673d..65750582133f 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -1155,6 +1155,10 @@ static void xhci_handle_cmd_reset_ep(struct xhci_hcd *xhci, int slot_id,
/* Clear our internal halted state */
xhci->devs[slot_id]->eps[ep_index].ep_state &= ~EP_HALTED;
}
+
+ /* if this was a soft reset, then restart */
+ if ((le32_to_cpu(trb->generic.field[3])) & TRB_TSP)
+ ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
}
static void xhci_handle_cmd_enable_slot(struct xhci_hcd *xhci, int slot_id,
@@ -1497,44 +1501,6 @@ static void handle_vendor_event(struct xhci_hcd *xhci,
handle_cmd_completion(xhci, &event->event_cmd);
}
-/* @port_id: the one-based port ID from the hardware (indexed from array of all
- * port registers -- USB 3.0 and USB 2.0).
- *
- * Returns a zero-based port number, which is suitable for indexing into each of
- * the split roothubs' port arrays and bus state arrays.
- * Add one to it in order to call xhci_find_slot_id_by_port.
- */
-static unsigned int find_faked_portnum_from_hw_portnum(struct usb_hcd *hcd,
- struct xhci_hcd *xhci, u32 port_id)
-{
- unsigned int i;
- unsigned int num_similar_speed_ports = 0;
-
- /* port_id from the hardware is 1-based, but port_array[], usb3_ports[],
- * and usb2_ports are 0-based indexes. Count the number of similar
- * speed ports, up to 1 port before this port.
- */
- for (i = 0; i < (port_id - 1); i++) {
- u8 port_speed = xhci->port_array[i];
-
- /*
- * Skip ports that don't have known speeds, or have duplicate
- * Extended Capabilities port speed entries.
- */
- if (port_speed == 0 || port_speed == DUPLICATE_ENTRY)
- continue;
-
- /*
- * USB 3.0 ports are always under a USB 3.0 hub. USB 2.0 and
- * 1.1 ports are under the USB 2.0 hub. If the port speed
- * matches the device speed, it's a similar speed port.
- */
- if ((port_speed == 0x03) == (hcd->speed >= HCD_USB3))
- num_similar_speed_ports++;
- }
- return num_similar_speed_ports;
-}
-
static void handle_device_notification(struct xhci_hcd *xhci,
union xhci_trb *event)
{
@@ -1555,6 +1521,35 @@ static void handle_device_notification(struct xhci_hcd *xhci,
usb_wakeup_notification(udev->parent, udev->portnum);
}
+/*
+ * Quirk hanlder for errata seen on Cavium ThunderX2 processor XHCI
+ * Controller.
+ * As per ThunderX2errata-129 USB 2 device may come up as USB 1
+ * If a connection to a USB 1 device is followed by another connection
+ * to a USB 2 device.
+ *
+ * Reset the PHY after the USB device is disconnected if device speed
+ * is less than HCD_USB3.
+ * Retry the reset sequence max of 4 times checking the PLL lock status.
+ *
+ */
+static void xhci_cavium_reset_phy_quirk(struct xhci_hcd *xhci)
+{
+ struct usb_hcd *hcd = xhci_to_hcd(xhci);
+ u32 pll_lock_check;
+ u32 retry_count = 4;
+
+ do {
+ /* Assert PHY reset */
+ writel(0x6F, hcd->regs + 0x1048);
+ udelay(10);
+ /* De-assert the PHY reset */
+ writel(0x7F, hcd->regs + 0x1048);
+ udelay(200);
+ pll_lock_check = readl(hcd->regs + 0x1070);
+ } while (!(pll_lock_check & 0x1) && --retry_count);
+}
+
static void handle_port_status(struct xhci_hcd *xhci,
union xhci_trb *event)
{
@@ -1563,11 +1558,10 @@ static void handle_port_status(struct xhci_hcd *xhci,
u32 portsc, cmd_reg;
int max_ports;
int slot_id;
- unsigned int faked_port_index;
- u8 major_revision;
+ unsigned int hcd_portnum;
struct xhci_bus_state *bus_state;
- __le32 __iomem **port_array;
bool bogus_port_status = false;
+ struct xhci_port *port;
/* Port status change events always have a successful completion code */
if (GET_COMP_CODE(le32_to_cpu(event->generic.field[2])) != COMP_SUCCESS)
@@ -1584,49 +1578,26 @@ static void handle_port_status(struct xhci_hcd *xhci,
return;
}
- /* Figure out which usb_hcd this port is attached to:
- * is it a USB 3.0 port or a USB 2.0/1.1 port?
- */
- major_revision = xhci->port_array[port_id - 1];
-
- /* Find the right roothub. */
- hcd = xhci_to_hcd(xhci);
- if ((major_revision == 0x03) != (hcd->speed >= HCD_USB3))
- hcd = xhci->shared_hcd;
-
- if (major_revision == 0) {
- xhci_warn(xhci, "Event for port %u not in "
- "Extended Capabilities, ignoring.\n",
- port_id);
+ port = &xhci->hw_ports[port_id - 1];
+ if (!port || !port->rhub || port->hcd_portnum == DUPLICATE_ENTRY) {
+ xhci_warn(xhci, "Event for invalid port %u\n", port_id);
bogus_port_status = true;
goto cleanup;
}
- if (major_revision == DUPLICATE_ENTRY) {
- xhci_warn(xhci, "Event for port %u duplicated in"
- "Extended Capabilities, ignoring.\n",
- port_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");
bogus_port_status = true;
goto cleanup;
}
- /*
- * Hardware port IDs reported by a Port Status Change Event include USB
- * 3.0 and USB 2.0 ports. We want to check if the port has reported a
- * resume event, but we first need to translate the hardware port ID
- * into the index into the ports on the correct split roothub, and the
- * correct bus_state structure.
- */
+ hcd = port->rhub->hcd;
bus_state = &xhci->bus_state[hcd_index(hcd)];
- if (hcd->speed >= HCD_USB3)
- port_array = xhci->usb3_ports;
- else
- port_array = xhci->usb2_ports;
- /* Find the faked port hub number */
- faked_port_index = find_faked_portnum_from_hw_portnum(hcd, xhci,
- port_id);
- portsc = readl(port_array[faked_port_index]);
+ hcd_portnum = port->hcd_portnum;
+ portsc = readl(port->addr);
- trace_xhci_handle_port_status(faked_port_index, portsc);
+ trace_xhci_handle_port_status(hcd_portnum, portsc);
if (hcd->state == HC_STATE_SUSPENDED) {
xhci_dbg(xhci, "resume root hub\n");
@@ -1634,7 +1605,7 @@ static void handle_port_status(struct xhci_hcd *xhci,
}
if (hcd->speed >= HCD_USB3 && (portsc & PORT_PLS_MASK) == XDEV_INACTIVE)
- bus_state->port_remote_wakeup &= ~(1 << faked_port_index);
+ bus_state->port_remote_wakeup &= ~(1 << hcd_portnum);
if ((portsc & PORT_PLC) && (portsc & PORT_PLS_MASK) == XDEV_RESUME) {
xhci_dbg(xhci, "port resume event for port %d\n", port_id);
@@ -1651,29 +1622,27 @@ static void handle_port_status(struct xhci_hcd *xhci,
* so we can tell the difference between the end of
* device and host initiated resume.
*/
- bus_state->port_remote_wakeup |= 1 << faked_port_index;
- xhci_test_and_clear_bit(xhci, port_array,
- faked_port_index, PORT_PLC);
- xhci_set_link_state(xhci, port_array, faked_port_index,
- XDEV_U0);
+ bus_state->port_remote_wakeup |= 1 << hcd_portnum;
+ xhci_test_and_clear_bit(xhci, port, PORT_PLC);
+ xhci_set_link_state(xhci, port, XDEV_U0);
/* Need to wait until the next link state change
* indicates the device is actually in U0.
*/
bogus_port_status = true;
goto cleanup;
- } else if (!test_bit(faked_port_index,
- &bus_state->resuming_ports)) {
+ } else if (!test_bit(hcd_portnum, &bus_state->resuming_ports)) {
xhci_dbg(xhci, "resume HS port %d\n", port_id);
- bus_state->resume_done[faked_port_index] = jiffies +
+ bus_state->resume_done[hcd_portnum] = jiffies +
msecs_to_jiffies(USB_RESUME_TIMEOUT);
- set_bit(faked_port_index, &bus_state->resuming_ports);
+ set_bit(hcd_portnum, &bus_state->resuming_ports);
/* Do the rest in GetPortStatus after resume time delay.
* Avoid polling roothub status before that so that a
* usb device auto-resume latency around ~40ms.
*/
set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
mod_timer(&hcd->rh_timer,
- bus_state->resume_done[faked_port_index]);
+ bus_state->resume_done[hcd_portnum]);
+ usb_hcd_start_port_resume(&hcd->self, hcd_portnum);
bogus_port_status = true;
}
}
@@ -1688,17 +1657,14 @@ static void handle_port_status(struct xhci_hcd *xhci,
* so the roothub behavior is consistent with external
* USB 3.0 hub behavior.
*/
- slot_id = xhci_find_slot_id_by_port(hcd, xhci,
- faked_port_index + 1);
+ slot_id = xhci_find_slot_id_by_port(hcd, xhci, hcd_portnum + 1);
if (slot_id && xhci->devs[slot_id])
xhci_ring_device(xhci, slot_id);
- if (bus_state->port_remote_wakeup & (1 << faked_port_index)) {
- bus_state->port_remote_wakeup &=
- ~(1 << faked_port_index);
- xhci_test_and_clear_bit(xhci, port_array,
- faked_port_index, PORT_PLC);
+ if (bus_state->port_remote_wakeup & (1 << hcd_portnum)) {
+ bus_state->port_remote_wakeup &= ~(1 << hcd_portnum);
+ xhci_test_and_clear_bit(xhci, port, PORT_PLC);
usb_wakeup_notification(hcd->self.root_hub,
- faked_port_index + 1);
+ hcd_portnum + 1);
bogus_port_status = true;
goto cleanup;
}
@@ -1709,17 +1675,20 @@ static void handle_port_status(struct xhci_hcd *xhci,
* RExit to a disconnect state). If so, let the the driver know it's
* out of the RExit state.
*/
- if (!DEV_SUPERSPEED_ANY(portsc) &&
- test_and_clear_bit(faked_port_index,
+ if (!DEV_SUPERSPEED_ANY(portsc) && hcd->speed < HCD_USB3 &&
+ test_and_clear_bit(hcd_portnum,
&bus_state->rexit_ports)) {
- complete(&bus_state->rexit_done[faked_port_index]);
+ complete(&bus_state->rexit_done[hcd_portnum]);
bogus_port_status = true;
goto cleanup;
}
- if (hcd->speed < HCD_USB3)
- xhci_test_and_clear_bit(xhci, port_array, faked_port_index,
- PORT_PLC);
+ if (hcd->speed < HCD_USB3) {
+ xhci_test_and_clear_bit(xhci, port, PORT_PLC);
+ if ((xhci->quirks & XHCI_RESET_PLL_ON_DISCONNECT) &&
+ (portsc & PORT_CSC) && !(portsc & PORT_CONNECT))
+ xhci_cavium_reset_phy_quirk(xhci);
+ }
cleanup:
/* Update event ring dequeue pointer before dropping the lock */
@@ -2208,10 +2177,16 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td,
union xhci_trb *ep_trb, struct xhci_transfer_event *event,
struct xhci_virt_ep *ep, int *status)
{
+ struct xhci_slot_ctx *slot_ctx;
struct xhci_ring *ep_ring;
u32 trb_comp_code;
u32 remaining, requested, ep_trb_len;
+ unsigned int slot_id;
+ int ep_index;
+ slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags));
+ slot_ctx = xhci_get_slot_ctx(xhci, xhci->devs[slot_id]->out_ctx);
+ ep_index = TRB_TO_EP_ID(le32_to_cpu(event->flags)) - 1;
ep_ring = xhci_dma_to_transfer_ring(ep, le64_to_cpu(event->buffer));
trb_comp_code = GET_COMP_CODE(le32_to_cpu(event->transfer_len));
remaining = EVENT_TRB_LEN(le32_to_cpu(event->transfer_len));
@@ -2220,6 +2195,7 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td,
switch (trb_comp_code) {
case COMP_SUCCESS:
+ ep_ring->err_count = 0;
/* handle success with untransferred data as short packet */
if (ep_trb != td->last_trb || remaining) {
xhci_warn(xhci, "WARN Successful completion on short TX\n");
@@ -2243,6 +2219,14 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td,
ep_trb_len = 0;
remaining = 0;
break;
+ case COMP_USB_TRANSACTION_ERROR:
+ if ((ep_ring->err_count++ > MAX_SOFT_RETRY) ||
+ le32_to_cpu(slot_ctx->tt_info) & TT_SLOT)
+ break;
+ *status = 0;
+ xhci_cleanup_halted_endpoint(xhci, slot_id, ep_index,
+ ep_ring->stream_id, td, EP_SOFT_RESET);
+ return 0;
default:
/* do nothing */
break;
@@ -2322,6 +2306,7 @@ static int handle_tx_event(struct xhci_hcd *xhci,
goto cleanup;
case COMP_RING_UNDERRUN:
case COMP_RING_OVERRUN:
+ case COMP_STOPPED_LENGTH_INVALID:
goto cleanup;
default:
xhci_err(xhci, "ERROR Transfer event for unknown stream ring slot %u ep %u\n",
diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c
index 2c076ea80522..938ff06c0349 100644
--- a/drivers/usb/host/xhci-tegra.c
+++ b/drivers/usb/host/xhci-tegra.c
@@ -18,9 +18,12 @@
#include <linux/phy/tegra/xusb.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
#include <linux/slab.h>
+#include <soc/tegra/pmc.h>
#include "xhci.h"
@@ -105,35 +108,35 @@
#define IMEM_BLOCK_SIZE 256
struct tegra_xusb_fw_header {
- u32 boot_loadaddr_in_imem;
- u32 boot_codedfi_offset;
- u32 boot_codetag;
- u32 boot_codesize;
- u32 phys_memaddr;
- u16 reqphys_memsize;
- u16 alloc_phys_memsize;
- u32 rodata_img_offset;
- u32 rodata_section_start;
- u32 rodata_section_end;
- u32 main_fnaddr;
- u32 fwimg_cksum;
- u32 fwimg_created_time;
- u32 imem_resident_start;
- u32 imem_resident_end;
- u32 idirect_start;
- u32 idirect_end;
- u32 l2_imem_start;
- u32 l2_imem_end;
- u32 version_id;
+ __le32 boot_loadaddr_in_imem;
+ __le32 boot_codedfi_offset;
+ __le32 boot_codetag;
+ __le32 boot_codesize;
+ __le32 phys_memaddr;
+ __le16 reqphys_memsize;
+ __le16 alloc_phys_memsize;
+ __le32 rodata_img_offset;
+ __le32 rodata_section_start;
+ __le32 rodata_section_end;
+ __le32 main_fnaddr;
+ __le32 fwimg_cksum;
+ __le32 fwimg_created_time;
+ __le32 imem_resident_start;
+ __le32 imem_resident_end;
+ __le32 idirect_start;
+ __le32 idirect_end;
+ __le32 l2_imem_start;
+ __le32 l2_imem_end;
+ __le32 version_id;
u8 init_ddirect;
u8 reserved[3];
- u32 phys_addr_log_buffer;
- u32 total_log_entries;
- u32 dequeue_ptr;
- u32 dummy_var[2];
- u32 fwimg_len;
+ __le32 phys_addr_log_buffer;
+ __le32 total_log_entries;
+ __le32 dequeue_ptr;
+ __le32 dummy_var[2];
+ __le32 fwimg_len;
u8 magic[8];
- u32 ss_low_power_entry_timeout;
+ __le32 ss_low_power_entry_timeout;
u8 num_hsic_port;
u8 padding[139]; /* Pad to 256 bytes */
};
@@ -192,6 +195,11 @@ struct tegra_xusb {
struct reset_control *host_rst;
struct reset_control *ss_rst;
+ struct device *genpd_dev_host;
+ struct device *genpd_dev_ss;
+ struct device_link *genpd_dl_host;
+ struct device_link *genpd_dl_ss;
+
struct phy **phys;
unsigned int num_phys;
@@ -479,7 +487,7 @@ static void tegra_xusb_mbox_handle(struct tegra_xusb *tegra,
unsigned long mask;
unsigned int port;
bool idle, enable;
- int err;
+ int err = 0;
memset(&rsp, 0, sizeof(rsp));
@@ -761,6 +769,49 @@ static void tegra_xusb_phy_disable(struct tegra_xusb *tegra)
}
}
+static int tegra_xusb_runtime_suspend(struct device *dev)
+{
+ struct tegra_xusb *tegra = dev_get_drvdata(dev);
+
+ tegra_xusb_phy_disable(tegra);
+ regulator_bulk_disable(tegra->soc->num_supplies, tegra->supplies);
+ tegra_xusb_clk_disable(tegra);
+
+ return 0;
+}
+
+static int tegra_xusb_runtime_resume(struct device *dev)
+{
+ struct tegra_xusb *tegra = dev_get_drvdata(dev);
+ int err;
+
+ err = tegra_xusb_clk_enable(tegra);
+ if (err) {
+ dev_err(dev, "failed to enable clocks: %d\n", err);
+ return err;
+ }
+
+ err = regulator_bulk_enable(tegra->soc->num_supplies, tegra->supplies);
+ if (err) {
+ dev_err(dev, "failed to enable regulators: %d\n", err);
+ goto disable_clk;
+ }
+
+ err = tegra_xusb_phy_enable(tegra);
+ if (err < 0) {
+ dev_err(dev, "failed to enable PHYs: %d\n", err);
+ goto disable_regulator;
+ }
+
+ return 0;
+
+disable_regulator:
+ regulator_bulk_disable(tegra->soc->num_supplies, tegra->supplies);
+disable_clk:
+ tegra_xusb_clk_disable(tegra);
+ return err;
+}
+
static int tegra_xusb_load_firmware(struct tegra_xusb *tegra)
{
unsigned int code_tag_blocks, code_size_blocks, code_blocks;
@@ -883,6 +934,57 @@ static int tegra_xusb_load_firmware(struct tegra_xusb *tegra)
return 0;
}
+static void tegra_xusb_powerdomain_remove(struct device *dev,
+ struct tegra_xusb *tegra)
+{
+ if (tegra->genpd_dl_ss)
+ device_link_del(tegra->genpd_dl_ss);
+ if (tegra->genpd_dl_host)
+ device_link_del(tegra->genpd_dl_host);
+ if (tegra->genpd_dev_ss)
+ dev_pm_domain_detach(tegra->genpd_dev_ss, true);
+ if (tegra->genpd_dev_host)
+ dev_pm_domain_detach(tegra->genpd_dev_host, true);
+}
+
+static int tegra_xusb_powerdomain_init(struct device *dev,
+ struct tegra_xusb *tegra)
+{
+ int err;
+
+ tegra->genpd_dev_host = dev_pm_domain_attach_by_name(dev, "xusb_host");
+ if (IS_ERR(tegra->genpd_dev_host)) {
+ err = PTR_ERR(tegra->genpd_dev_host);
+ dev_err(dev, "failed to get host pm-domain: %d\n", err);
+ return err;
+ }
+
+ tegra->genpd_dev_ss = dev_pm_domain_attach_by_name(dev, "xusb_ss");
+ if (IS_ERR(tegra->genpd_dev_ss)) {
+ err = PTR_ERR(tegra->genpd_dev_ss);
+ dev_err(dev, "failed to get superspeed pm-domain: %d\n", err);
+ return err;
+ }
+
+ tegra->genpd_dl_host = device_link_add(dev, tegra->genpd_dev_host,
+ DL_FLAG_PM_RUNTIME |
+ DL_FLAG_STATELESS);
+ if (!tegra->genpd_dl_host) {
+ dev_err(dev, "adding host device link failed!\n");
+ return -ENODEV;
+ }
+
+ tegra->genpd_dl_ss = device_link_add(dev, tegra->genpd_dev_ss,
+ DL_FLAG_PM_RUNTIME |
+ DL_FLAG_STATELESS);
+ if (!tegra->genpd_dl_ss) {
+ dev_err(dev, "adding superspeed device link failed!\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
static int tegra_xusb_probe(struct platform_device *pdev)
{
struct tegra_xusb_mbox_msg msg;
@@ -930,20 +1032,6 @@ static int tegra_xusb_probe(struct platform_device *pdev)
if (IS_ERR(tegra->padctl))
return PTR_ERR(tegra->padctl);
- tegra->host_rst = devm_reset_control_get(&pdev->dev, "xusb_host");
- if (IS_ERR(tegra->host_rst)) {
- err = PTR_ERR(tegra->host_rst);
- dev_err(&pdev->dev, "failed to get xusb_host reset: %d\n", err);
- goto put_padctl;
- }
-
- tegra->ss_rst = devm_reset_control_get(&pdev->dev, "xusb_ss");
- if (IS_ERR(tegra->ss_rst)) {
- err = PTR_ERR(tegra->ss_rst);
- dev_err(&pdev->dev, "failed to get xusb_ss reset: %d\n", err);
- goto put_padctl;
- }
-
tegra->host_clk = devm_clk_get(&pdev->dev, "xusb_host");
if (IS_ERR(tegra->host_clk)) {
err = PTR_ERR(tegra->host_clk);
@@ -1007,11 +1095,53 @@ static int tegra_xusb_probe(struct platform_device *pdev)
goto put_padctl;
}
+ if (!of_property_read_bool(pdev->dev.of_node, "power-domains")) {
+ tegra->host_rst = devm_reset_control_get(&pdev->dev,
+ "xusb_host");
+ if (IS_ERR(tegra->host_rst)) {
+ err = PTR_ERR(tegra->host_rst);
+ dev_err(&pdev->dev,
+ "failed to get xusb_host reset: %d\n", err);
+ goto put_padctl;
+ }
+
+ tegra->ss_rst = devm_reset_control_get(&pdev->dev, "xusb_ss");
+ if (IS_ERR(tegra->ss_rst)) {
+ err = PTR_ERR(tegra->ss_rst);
+ dev_err(&pdev->dev, "failed to get xusb_ss reset: %d\n",
+ err);
+ goto put_padctl;
+ }
+
+ err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_XUSBA,
+ tegra->ss_clk,
+ tegra->ss_rst);
+ if (err) {
+ dev_err(&pdev->dev,
+ "failed to enable XUSBA domain: %d\n", err);
+ goto put_padctl;
+ }
+
+ err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_XUSBC,
+ tegra->host_clk,
+ tegra->host_rst);
+ if (err) {
+ tegra_powergate_power_off(TEGRA_POWERGATE_XUSBA);
+ dev_err(&pdev->dev,
+ "failed to enable XUSBC domain: %d\n", err);
+ goto put_padctl;
+ }
+ } else {
+ err = tegra_xusb_powerdomain_init(&pdev->dev, tegra);
+ if (err)
+ goto put_powerdomains;
+ }
+
tegra->supplies = devm_kcalloc(&pdev->dev, tegra->soc->num_supplies,
sizeof(*tegra->supplies), GFP_KERNEL);
if (!tegra->supplies) {
err = -ENOMEM;
- goto put_padctl;
+ goto put_powerdomains;
}
for (i = 0; i < tegra->soc->num_supplies; i++)
@@ -1021,7 +1151,7 @@ static int tegra_xusb_probe(struct platform_device *pdev)
tegra->supplies);
if (err) {
dev_err(&pdev->dev, "failed to get regulators: %d\n", err);
- goto put_padctl;
+ goto put_powerdomains;
}
for (i = 0; i < tegra->soc->num_types; i++)
@@ -1031,7 +1161,7 @@ static int tegra_xusb_probe(struct platform_device *pdev)
sizeof(*tegra->phys), GFP_KERNEL);
if (!tegra->phys) {
err = -ENOMEM;
- goto put_padctl;
+ goto put_powerdomains;
}
for (i = 0, k = 0; i < tegra->soc->num_types; i++) {
@@ -1047,29 +1177,35 @@ static int tegra_xusb_probe(struct platform_device *pdev)
"failed to get PHY %s: %ld\n", prop,
PTR_ERR(phy));
err = PTR_ERR(phy);
- goto put_padctl;
+ goto put_powerdomains;
}
tegra->phys[k++] = phy;
}
}
- err = tegra_xusb_clk_enable(tegra);
- if (err) {
- dev_err(&pdev->dev, "failed to enable clocks: %d\n", err);
- goto put_padctl;
+ tegra->hcd = usb_create_hcd(&tegra_xhci_hc_driver, &pdev->dev,
+ dev_name(&pdev->dev));
+ if (!tegra->hcd) {
+ err = -ENOMEM;
+ goto put_powerdomains;
}
- err = regulator_bulk_enable(tegra->soc->num_supplies, tegra->supplies);
- if (err) {
- dev_err(&pdev->dev, "failed to enable regulators: %d\n", err);
- goto disable_clk;
- }
+ /*
+ * This must happen after usb_create_hcd(), because usb_create_hcd()
+ * will overwrite the drvdata of the device with the hcd it creates.
+ */
+ platform_set_drvdata(pdev, tegra);
+
+ pm_runtime_enable(&pdev->dev);
+ if (pm_runtime_enabled(&pdev->dev))
+ err = pm_runtime_get_sync(&pdev->dev);
+ else
+ err = tegra_xusb_runtime_resume(&pdev->dev);
- err = tegra_xusb_phy_enable(tegra);
if (err < 0) {
- dev_err(&pdev->dev, "failed to enable PHYs: %d\n", err);
- goto disable_regulator;
+ dev_err(&pdev->dev, "failed to enable device: %d\n", err);
+ goto disable_rpm;
}
tegra_xusb_ipfs_config(tegra, regs);
@@ -1077,22 +1213,9 @@ static int tegra_xusb_probe(struct platform_device *pdev)
err = tegra_xusb_load_firmware(tegra);
if (err < 0) {
dev_err(&pdev->dev, "failed to load firmware: %d\n", err);
- goto disable_phy;
+ goto put_rpm;
}
- tegra->hcd = usb_create_hcd(&tegra_xhci_hc_driver, &pdev->dev,
- dev_name(&pdev->dev));
- if (!tegra->hcd) {
- err = -ENOMEM;
- goto disable_phy;
- }
-
- /*
- * This must happen after usb_create_hcd(), because usb_create_hcd()
- * will overwrite the drvdata of the device with the hcd it creates.
- */
- platform_set_drvdata(pdev, tegra);
-
tegra->hcd->regs = tegra->regs;
tegra->hcd->rsrc_start = regs->start;
tegra->hcd->rsrc_len = resource_size(regs);
@@ -1100,7 +1223,7 @@ static int tegra_xusb_probe(struct platform_device *pdev)
err = usb_add_hcd(tegra->hcd, tegra->xhci_irq, IRQF_SHARED);
if (err < 0) {
dev_err(&pdev->dev, "failed to add USB HCD: %d\n", err);
- goto put_usb2;
+ goto put_rpm;
}
device_wakeup_enable(tegra->hcd->self.controller);
@@ -1155,14 +1278,19 @@ put_usb3:
usb_put_hcd(xhci->shared_hcd);
remove_usb2:
usb_remove_hcd(tegra->hcd);
-put_usb2:
+put_rpm:
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ tegra_xusb_runtime_suspend(&pdev->dev);
+disable_rpm:
+ pm_runtime_disable(&pdev->dev);
usb_put_hcd(tegra->hcd);
-disable_phy:
- tegra_xusb_phy_disable(tegra);
-disable_regulator:
- regulator_bulk_disable(tegra->soc->num_supplies, tegra->supplies);
-disable_clk:
- tegra_xusb_clk_disable(tegra);
+put_powerdomains:
+ if (!of_property_read_bool(pdev->dev.of_node, "power-domains")) {
+ tegra_powergate_power_off(TEGRA_POWERGATE_XUSBC);
+ tegra_powergate_power_off(TEGRA_POWERGATE_XUSBA);
+ } else {
+ tegra_xusb_powerdomain_remove(&pdev->dev, tegra);
+ }
put_padctl:
tegra_xusb_padctl_put(tegra->padctl);
return err;
@@ -1175,15 +1303,22 @@ static int tegra_xusb_remove(struct platform_device *pdev)
usb_remove_hcd(xhci->shared_hcd);
usb_put_hcd(xhci->shared_hcd);
+ xhci->shared_hcd = NULL;
usb_remove_hcd(tegra->hcd);
usb_put_hcd(tegra->hcd);
dma_free_coherent(&pdev->dev, tegra->fw.size, tegra->fw.virt,
tegra->fw.phys);
- tegra_xusb_phy_disable(tegra);
- regulator_bulk_disable(tegra->soc->num_supplies, tegra->supplies);
- tegra_xusb_clk_disable(tegra);
+ pm_runtime_put_sync(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+
+ if (!of_property_read_bool(pdev->dev.of_node, "power-domains")) {
+ tegra_powergate_power_off(TEGRA_POWERGATE_XUSBC);
+ tegra_powergate_power_off(TEGRA_POWERGATE_XUSBA);
+ } else {
+ tegra_xusb_powerdomain_remove(&pdev->dev, tegra);
+ }
tegra_xusb_padctl_put(tegra->padctl);
@@ -1211,6 +1346,8 @@ static int tegra_xusb_resume(struct device *dev)
#endif
static const struct dev_pm_ops tegra_xusb_pm_ops = {
+ SET_RUNTIME_PM_OPS(tegra_xusb_runtime_suspend,
+ tegra_xusb_runtime_resume, NULL)
SET_SYSTEM_SLEEP_PM_OPS(tegra_xusb_suspend, tegra_xusb_resume)
};
diff --git a/drivers/usb/host/xhci-trace.h b/drivers/usb/host/xhci-trace.h
index 410544ffe78f..88b427434bd8 100644
--- a/drivers/usb/host/xhci-trace.h
+++ b/drivers/usb/host/xhci-trace.h
@@ -171,6 +171,37 @@ DEFINE_EVENT(xhci_log_trb, xhci_dbc_gadget_ep_queue,
TP_ARGS(ring, trb)
);
+DECLARE_EVENT_CLASS(xhci_log_free_virt_dev,
+ TP_PROTO(struct xhci_virt_device *vdev),
+ TP_ARGS(vdev),
+ TP_STRUCT__entry(
+ __field(void *, vdev)
+ __field(unsigned long long, out_ctx)
+ __field(unsigned long long, in_ctx)
+ __field(u8, fake_port)
+ __field(u8, real_port)
+ __field(u16, current_mel)
+
+ ),
+ TP_fast_assign(
+ __entry->vdev = vdev;
+ __entry->in_ctx = (unsigned long long) vdev->in_ctx->dma;
+ __entry->out_ctx = (unsigned long long) vdev->out_ctx->dma;
+ __entry->fake_port = (u8) vdev->fake_port;
+ __entry->real_port = (u8) vdev->real_port;
+ __entry->current_mel = (u16) vdev->current_mel;
+ ),
+ TP_printk("vdev %p ctx %llx | %llx fake_port %d real_port %d current_mel %d",
+ __entry->vdev, __entry->in_ctx, __entry->out_ctx,
+ __entry->fake_port, __entry->real_port, __entry->current_mel
+ )
+);
+
+DEFINE_EVENT(xhci_log_free_virt_dev, xhci_free_virt_device,
+ TP_PROTO(struct xhci_virt_device *vdev),
+ TP_ARGS(vdev)
+);
+
DECLARE_EVENT_CLASS(xhci_log_virt_dev,
TP_PROTO(struct xhci_virt_device *vdev),
TP_ARGS(vdev),
@@ -208,11 +239,6 @@ DEFINE_EVENT(xhci_log_virt_dev, xhci_alloc_virt_device,
TP_ARGS(vdev)
);
-DEFINE_EVENT(xhci_log_virt_dev, xhci_free_virt_device,
- TP_PROTO(struct xhci_virt_device *vdev),
- TP_ARGS(vdev)
-);
-
DEFINE_EVENT(xhci_log_virt_dev, xhci_setup_device,
TP_PROTO(struct xhci_virt_device *vdev),
TP_ARGS(vdev)
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 711da3306b14..dae3be1b9c8f 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -33,10 +33,25 @@ static int link_quirk;
module_param(link_quirk, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(link_quirk, "Don't clear the chain bit on a link TRB");
-static unsigned int quirks;
-module_param(quirks, uint, S_IRUGO);
+static unsigned long long quirks;
+module_param(quirks, ullong, S_IRUGO);
MODULE_PARM_DESC(quirks, "Bit flags for quirks to be enabled as default");
+static bool td_on_ring(struct xhci_td *td, struct xhci_ring *ring)
+{
+ struct xhci_segment *seg = ring->first_seg;
+
+ if (!td || !td->start_seg)
+ return false;
+ do {
+ if (seg == td->start_seg)
+ return true;
+ seg = seg->next;
+ } while (seg && seg != ring->first_seg);
+
+ return false;
+}
+
/* TODO: copied from ehci-hcd.c - can this be refactored? */
/*
* xhci_handshake - spin reading hc until handshake completes or fails
@@ -209,6 +224,68 @@ int xhci_reset(struct xhci_hcd *xhci)
return ret;
}
+static void xhci_zero_64b_regs(struct xhci_hcd *xhci)
+{
+ struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
+ int err, i;
+ u64 val;
+
+ /*
+ * Some Renesas controllers get into a weird state if they are
+ * reset while programmed with 64bit addresses (they will preserve
+ * the top half of the address in internal, non visible
+ * registers). You end up with half the address coming from the
+ * kernel, and the other half coming from the firmware. Also,
+ * changing the programming leads to extra accesses even if the
+ * controller is supposed to be halted. The controller ends up with
+ * a fatal fault, and is then ripe for being properly reset.
+ *
+ * Special care is taken to only apply this if the device is behind
+ * an iommu. Doing anything when there is no iommu is definitely
+ * unsafe...
+ */
+ if (!(xhci->quirks & XHCI_ZERO_64B_REGS) || !dev->iommu_group)
+ return;
+
+ xhci_info(xhci, "Zeroing 64bit base registers, expecting fault\n");
+
+ /* Clear HSEIE so that faults do not get signaled */
+ val = readl(&xhci->op_regs->command);
+ val &= ~CMD_HSEIE;
+ writel(val, &xhci->op_regs->command);
+
+ /* Clear HSE (aka FATAL) */
+ val = readl(&xhci->op_regs->status);
+ val |= STS_FATAL;
+ writel(val, &xhci->op_regs->status);
+
+ /* Now zero the registers, and brace for impact */
+ val = xhci_read_64(xhci, &xhci->op_regs->dcbaa_ptr);
+ if (upper_32_bits(val))
+ xhci_write_64(xhci, 0, &xhci->op_regs->dcbaa_ptr);
+ val = xhci_read_64(xhci, &xhci->op_regs->cmd_ring);
+ if (upper_32_bits(val))
+ xhci_write_64(xhci, 0, &xhci->op_regs->cmd_ring);
+
+ for (i = 0; i < HCS_MAX_INTRS(xhci->hcs_params1); i++) {
+ struct xhci_intr_reg __iomem *ir;
+
+ ir = &xhci->run_regs->ir_set[i];
+ val = xhci_read_64(xhci, &ir->erst_base);
+ if (upper_32_bits(val))
+ xhci_write_64(xhci, 0, &ir->erst_base);
+ val= xhci_read_64(xhci, &ir->erst_dequeue);
+ if (upper_32_bits(val))
+ xhci_write_64(xhci, 0, &ir->erst_dequeue);
+ }
+
+ /* Wait for the fault to appear. It will be cleared on reset */
+ err = xhci_handshake(&xhci->op_regs->status,
+ STS_FATAL, STS_FATAL,
+ XHCI_MAX_HALT_USEC);
+ if (!err)
+ xhci_info(xhci, "Fault detected\n");
+}
#ifdef CONFIG_USB_PCI
/*
@@ -400,13 +477,15 @@ static void compliance_mode_recovery(struct timer_list *t)
{
struct xhci_hcd *xhci;
struct usb_hcd *hcd;
+ struct xhci_hub *rhub;
u32 temp;
int i;
xhci = from_timer(xhci, t, comp_mode_recovery_timer);
+ rhub = &xhci->usb3_rhub;
- for (i = 0; i < xhci->num_usb3_ports; i++) {
- temp = readl(xhci->usb3_ports[i]);
+ for (i = 0; i < rhub->num_ports; i++) {
+ temp = readl(rhub->ports[i]->addr);
if ((temp & PORT_PLS_MASK) == USB_SS_PORT_LS_COMP_MOD) {
/*
* Compliance Mode Detected. Letting USB Core
@@ -426,7 +505,7 @@ static void compliance_mode_recovery(struct timer_list *t)
}
}
- if (xhci->port_status_u0 != ((1 << xhci->num_usb3_ports)-1))
+ if (xhci->port_status_u0 != ((1 << rhub->num_ports) - 1))
mod_timer(&xhci->comp_mode_recovery_timer,
jiffies + msecs_to_jiffies(COMP_MODE_RCVRY_MSECS));
}
@@ -483,7 +562,7 @@ static bool xhci_compliance_mode_recovery_timer_quirk_check(void)
static int xhci_all_ports_seen_u0(struct xhci_hcd *xhci)
{
- return (xhci->port_status_u0 == ((1 << xhci->num_usb3_ports)-1));
+ return (xhci->port_status_u0 == ((1 << xhci->usb3_rhub.num_ports) - 1));
}
@@ -640,8 +719,6 @@ static void xhci_stop(struct usb_hcd *hcd)
/* Only halt host and free memory after both hcds are removed */
if (!usb_hcd_is_primary_hcd(hcd)) {
- /* usb core will free this hcd shortly, unset pointer */
- xhci->shared_hcd = NULL;
mutex_unlock(&xhci->mutex);
return;
}
@@ -812,38 +889,73 @@ static void xhci_clear_command_ring(struct xhci_hcd *xhci)
static void xhci_disable_port_wake_on_bits(struct xhci_hcd *xhci)
{
+ struct xhci_port **ports;
int port_index;
- __le32 __iomem **port_array;
unsigned long flags;
u32 t1, t2;
spin_lock_irqsave(&xhci->lock, flags);
/* disable usb3 ports Wake bits */
- port_index = xhci->num_usb3_ports;
- port_array = xhci->usb3_ports;
+ port_index = xhci->usb3_rhub.num_ports;
+ ports = xhci->usb3_rhub.ports;
while (port_index--) {
- t1 = readl(port_array[port_index]);
+ t1 = readl(ports[port_index]->addr);
t1 = xhci_port_state_to_neutral(t1);
t2 = t1 & ~PORT_WAKE_BITS;
if (t1 != t2)
- writel(t2, port_array[port_index]);
+ writel(t2, ports[port_index]->addr);
}
/* disable usb2 ports Wake bits */
- port_index = xhci->num_usb2_ports;
- port_array = xhci->usb2_ports;
+ port_index = xhci->usb2_rhub.num_ports;
+ ports = xhci->usb2_rhub.ports;
while (port_index--) {
- t1 = readl(port_array[port_index]);
+ t1 = readl(ports[port_index]->addr);
t1 = xhci_port_state_to_neutral(t1);
t2 = t1 & ~PORT_WAKE_BITS;
if (t1 != t2)
- writel(t2, port_array[port_index]);
+ writel(t2, ports[port_index]->addr);
}
spin_unlock_irqrestore(&xhci->lock, flags);
}
+static bool xhci_pending_portevent(struct xhci_hcd *xhci)
+{
+ struct xhci_port **ports;
+ int port_index;
+ u32 status;
+ u32 portsc;
+
+ status = readl(&xhci->op_regs->status);
+ if (status & STS_EINT)
+ return true;
+ /*
+ * Checking STS_EINT is not enough as there is a lag between a change
+ * bit being set and the Port Status Change Event that it generated
+ * being written to the Event Ring. See note in xhci 1.1 section 4.19.2.
+ */
+
+ port_index = xhci->usb2_rhub.num_ports;
+ ports = xhci->usb2_rhub.ports;
+ while (port_index--) {
+ portsc = readl(ports[port_index]->addr);
+ if (portsc & PORT_CHANGE_MASK ||
+ (portsc & PORT_PLS_MASK) == XDEV_RESUME)
+ return true;
+ }
+ port_index = xhci->usb3_rhub.num_ports;
+ ports = xhci->usb3_rhub.ports;
+ while (port_index--) {
+ portsc = readl(ports[port_index]->addr);
+ if (portsc & PORT_CHANGE_MASK ||
+ (portsc & PORT_PLS_MASK) == XDEV_RESUME)
+ return true;
+ }
+ return false;
+}
+
/*
* Stop HC (not bus-specific)
*
@@ -856,6 +968,7 @@ int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup)
unsigned int delay = XHCI_MAX_HALT_USEC;
struct usb_hcd *hcd = xhci_to_hcd(xhci);
u32 command;
+ u32 res;
if (!hcd->state)
return 0;
@@ -909,11 +1022,28 @@ 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);
+ xhci->broken_suspend = 0;
if (xhci_handshake(&xhci->op_regs->status,
STS_SAVE, 0, 10 * 1000)) {
- xhci_warn(xhci, "WARN: xHC save state timeout\n");
- spin_unlock_irq(&xhci->lock);
- return -ETIMEDOUT;
+ /*
+ * 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;
+ }
}
spin_unlock_irq(&xhci->lock);
@@ -945,7 +1075,7 @@ EXPORT_SYMBOL_GPL(xhci_suspend);
*/
int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
{
- u32 command, temp = 0, status;
+ u32 command, temp = 0;
struct usb_hcd *hcd = xhci_to_hcd(xhci);
struct usb_hcd *secondary_hcd;
int retval = 0;
@@ -966,7 +1096,7 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
set_bit(HCD_FLAG_HW_ACCESSIBLE, &xhci->shared_hcd->flags);
spin_lock_irq(&xhci->lock);
- if (xhci->quirks & XHCI_RESET_ON_RESUME)
+ if ((xhci->quirks & XHCI_RESET_ON_RESUME) || xhci->broken_suspend)
hibernated = true;
if (!hibernated) {
@@ -979,8 +1109,13 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
command = readl(&xhci->op_regs->command);
command |= CMD_CRS;
writel(command, &xhci->op_regs->command);
+ /*
+ * Some controllers take up to 55+ ms to complete the controller
+ * restore so setting the timeout to 100ms. Xhci specification
+ * doesn't mention any timeout value.
+ */
if (xhci_handshake(&xhci->op_regs->status,
- STS_RESTORE, 0, 10 * 1000)) {
+ STS_RESTORE, 0, 100 * 1000)) {
xhci_warn(xhci, "WARN: xHC restore state timeout\n");
spin_unlock_irq(&xhci->lock);
return -ETIMEDOUT;
@@ -1004,6 +1139,7 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
xhci_dbg(xhci, "Stop HCD\n");
xhci_halt(xhci);
+ xhci_zero_64b_regs(xhci);
xhci_reset(xhci);
spin_unlock_irq(&xhci->lock);
xhci_cleanup_msix(xhci);
@@ -1069,8 +1205,7 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
done:
if (retval == 0) {
/* Resume root hubs only when have pending events. */
- status = readl(&xhci->op_regs->status);
- if (status & STS_EINT) {
+ if (xhci_pending_portevent(xhci)) {
usb_hcd_resume_root_hub(xhci->shared_hcd);
usb_hcd_resume_root_hub(hcd);
}
@@ -1467,6 +1602,21 @@ static int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
goto done;
}
+ /*
+ * check ring is not re-allocated since URB was enqueued. If it is, then
+ * make sure none of the ring related pointers in this URB private data
+ * are touched, such as td_list, otherwise we overwrite freed data
+ */
+ if (!td_on_ring(&urb_priv->td[0], ep_ring)) {
+ xhci_err(xhci, "Canceled URB td not found on endpoint ring");
+ for (i = urb_priv->num_tds_done; i < urb_priv->num_tds; i++) {
+ td = &urb_priv->td[i];
+ if (!list_empty(&td->cancelled_td_list))
+ list_del_init(&td->cancelled_td_list);
+ }
+ goto err_giveback;
+ }
+
if (xhci->xhc_state & XHCI_STATE_HALTED) {
xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
"HC halted, freeing TD manually.");
@@ -2947,6 +3097,7 @@ static void xhci_endpoint_reset(struct usb_hcd *hcd,
if (!list_empty(&ep->ring->td_list)) {
dev_err(&udev->dev, "EP not empty, refuse reset\n");
spin_unlock_irqrestore(&xhci->lock, flags);
+ xhci_free_command(xhci, cfg_cmd);
goto cleanup;
}
xhci_queue_stop_endpoint(xhci, stop_cmd, udev->slot_id, ep_index, 0);
@@ -3976,18 +4127,10 @@ static int xhci_enable_device(struct usb_hcd *hcd, struct usb_device *udev)
*/
int xhci_find_raw_port_number(struct usb_hcd *hcd, int port1)
{
- struct xhci_hcd *xhci = hcd_to_xhci(hcd);
- __le32 __iomem *base_addr = &xhci->op_regs->port_status_base;
- __le32 __iomem *addr;
- int raw_port;
-
- if (hcd->speed < HCD_USB3)
- addr = xhci->usb2_ports[port1 - 1];
- else
- addr = xhci->usb3_ports[port1 - 1];
+ struct xhci_hub *rhub;
- raw_port = (addr - base_addr)/NUM_PORT_REGS + 1;
- return raw_port;
+ rhub = xhci_get_rhub(hcd);
+ return rhub->ports[port1 - 1]->hw_portnum + 1;
}
/*
@@ -4120,7 +4263,7 @@ static int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd,
struct usb_device *udev, int enable)
{
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
- __le32 __iomem **port_array;
+ struct xhci_port **ports;
__le32 __iomem *pm_addr, *hlpm_addr;
u32 pm_val, hlpm_val, field;
unsigned int port_num;
@@ -4141,11 +4284,11 @@ static int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd,
spin_lock_irqsave(&xhci->lock, flags);
- port_array = xhci->usb2_ports;
+ ports = xhci->usb2_rhub.ports;
port_num = udev->portnum - 1;
- pm_addr = port_array[port_num] + PORTPMSC;
+ pm_addr = ports[port_num]->addr + PORTPMSC;
pm_val = readl(pm_addr);
- hlpm_addr = port_array[port_num] + PORTHLPMC;
+ hlpm_addr = ports[port_num]->addr + PORTHLPMC;
field = le32_to_cpu(udev->bos->ext_cap->bmAttributes);
xhci_dbg(xhci, "%s port %d USB2 hardware LPM\n",
@@ -4371,6 +4514,14 @@ static u16 xhci_calculate_u1_timeout(struct xhci_hcd *xhci,
{
unsigned long long timeout_ns;
+ /* Prevent U1 if service interval is shorter than U1 exit latency */
+ if (usb_endpoint_xfer_int(desc) || usb_endpoint_xfer_isoc(desc)) {
+ if (xhci_service_interval_to_ns(desc) <= udev->u1_params.mel) {
+ dev_dbg(&udev->dev, "Disable U1, ESIT shorter than exit latency\n");
+ return USB3_LPM_DISABLED;
+ }
+ }
+
if (xhci->quirks & XHCI_INTEL_HOST)
timeout_ns = xhci_calculate_intel_u1_timeout(udev, desc);
else
@@ -4427,6 +4578,14 @@ static u16 xhci_calculate_u2_timeout(struct xhci_hcd *xhci,
{
unsigned long long timeout_ns;
+ /* Prevent U2 if service interval is shorter than U2 exit latency */
+ if (usb_endpoint_xfer_int(desc) || usb_endpoint_xfer_isoc(desc)) {
+ if (xhci_service_interval_to_ns(desc) <= udev->u2_params.mel) {
+ dev_dbg(&udev->dev, "Disable U2, ESIT shorter than exit latency\n");
+ return USB3_LPM_DISABLED;
+ }
+ }
+
if (xhci->quirks & XHCI_INTEL_HOST)
timeout_ns = xhci_calculate_intel_u2_timeout(udev, desc);
else
@@ -4858,6 +5017,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
if (usb_hcd_is_primary_hcd(hcd)) {
xhci->main_hcd = hcd;
+ xhci->usb2_rhub.hcd = hcd;
/* Mark the first roothub as being USB 2.0.
* The xHCI driver will register the USB 3.0 roothub.
*/
@@ -4883,6 +5043,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
minor_rev,
minor_rev ? "Enhanced" : "");
+ xhci->usb3_rhub.hcd = hcd;
/* xHCI private pointer was set in xhci_pci_probe for the second
* registered roothub.
*/
@@ -4921,6 +5082,8 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
if (retval)
return retval;
+ xhci_zero_64b_regs(xhci);
+
xhci_dbg(xhci, "Resetting HCD\n");
/* Reset the internal HC memory state and registers. */
retval = xhci_reset(xhci);
@@ -4963,7 +5126,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
return retval;
xhci_dbg(xhci, "Called HCD init\n");
- xhci_info(xhci, "hcc params 0x%08x hci version 0x%x quirks 0x%08x\n",
+ xhci_info(xhci, "hcc params 0x%08x hci version 0x%x quirks 0x%016llx\n",
xhci->hcc_params, xhci->hci_version, xhci->quirks);
return 0;
@@ -5020,6 +5183,7 @@ static const struct hc_driver xhci_hc_driver = {
.hub_status_data = xhci_hub_status_data,
.bus_suspend = xhci_bus_suspend,
.bus_resume = xhci_bus_resume,
+ .get_resuming_ports = xhci_get_resuming_ports,
/*
* call back when device connected and addressed
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 6dfc4867dbcf..011dd45f8718 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -382,6 +382,10 @@ struct xhci_op_regs {
#define PORT_PLC (1 << 22)
/* port configure error change - port failed to configure its link partner */
#define PORT_CEC (1 << 23)
+#define PORT_CHANGE_MASK (PORT_CSC | PORT_PEC | PORT_WRC | PORT_OCC | \
+ PORT_RC | PORT_PLC | PORT_CEC)
+
+
/* Cold Attach Status - xHC can set this bit to report device attached during
* Sx state. Warm port reset should be perfomed to clear this bit and move port
* to connected state.
@@ -1492,6 +1496,7 @@ static inline const char *xhci_trb_type_string(u8 type)
/* How much data is left before the 64KB boundary? */
#define TRB_BUFF_LEN_UP_TO_BOUNDARY(addr) (TRB_MAX_BUFF_SIZE - \
(addr & (TRB_MAX_BUFF_SIZE - 1)))
+#define MAX_SOFT_RETRY 3
struct xhci_segment {
union xhci_trb *trbs;
@@ -1579,6 +1584,7 @@ struct xhci_ring {
* if we own the TRB (if we are the consumer). See section 4.9.1.
*/
u32 cycle_state;
+ unsigned int err_count;
unsigned int stream_id;
unsigned int num_segs;
unsigned int num_trbs_free;
@@ -1674,7 +1680,7 @@ struct xhci_bus_state {
* It can take up to 20 ms to transition from RExit to U0 on the
* Intel Lynx Point LP xHCI host.
*/
-#define XHCI_MAX_REXIT_TIMEOUT (20 * 1000)
+#define XHCI_MAX_REXIT_TIMEOUT_MS 20
static inline unsigned int hcd_index(struct usb_hcd *hcd)
{
@@ -1683,13 +1689,23 @@ static inline unsigned int hcd_index(struct usb_hcd *hcd)
else
return 1;
}
+struct xhci_port {
+ __le32 __iomem *addr;
+ int hw_portnum;
+ int hcd_portnum;
+ struct xhci_hub *rhub;
+};
struct xhci_hub {
- u8 maj_rev;
- u8 min_rev;
- u32 *psi; /* array of protocol speed ID entries */
- u8 psi_count;
- u8 psi_uid_count;
+ struct xhci_port **ports;
+ unsigned int num_ports;
+ struct usb_hcd *hcd;
+ /* supported prococol extended capabiliy values */
+ u8 maj_rev;
+ u8 min_rev;
+ u32 *psi; /* array of protocol speed ID entries */
+ u8 psi_count;
+ u8 psi_uid_count;
};
/* There is one xhci_hcd structure per controller */
@@ -1787,12 +1803,12 @@ struct xhci_hcd {
#define XHCI_STATE_DYING (1 << 0)
#define XHCI_STATE_HALTED (1 << 1)
#define XHCI_STATE_REMOVING (1 << 2)
- unsigned int quirks;
-#define XHCI_LINK_TRB_QUIRK (1 << 0)
-#define XHCI_RESET_EP_QUIRK (1 << 1)
-#define XHCI_NEC_HOST (1 << 2)
-#define XHCI_AMD_PLL_FIX (1 << 3)
-#define XHCI_SPURIOUS_SUCCESS (1 << 4)
+ unsigned long long quirks;
+#define XHCI_LINK_TRB_QUIRK BIT_ULL(0)
+#define XHCI_RESET_EP_QUIRK BIT_ULL(1)
+#define XHCI_NEC_HOST BIT_ULL(2)
+#define XHCI_AMD_PLL_FIX BIT_ULL(3)
+#define XHCI_SPURIOUS_SUCCESS BIT_ULL(4)
/*
* Certain Intel host controllers have a limit to the number of endpoint
* contexts they can handle. Ideally, they would signal that they can't handle
@@ -1802,54 +1818,53 @@ struct xhci_hcd {
* commands, reset device commands, disable slot commands, and address device
* commands.
*/
-#define XHCI_EP_LIMIT_QUIRK (1 << 5)
-#define XHCI_BROKEN_MSI (1 << 6)
-#define XHCI_RESET_ON_RESUME (1 << 7)
-#define XHCI_SW_BW_CHECKING (1 << 8)
-#define XHCI_AMD_0x96_HOST (1 << 9)
-#define XHCI_TRUST_TX_LENGTH (1 << 10)
-#define XHCI_LPM_SUPPORT (1 << 11)
-#define XHCI_INTEL_HOST (1 << 12)
-#define XHCI_SPURIOUS_REBOOT (1 << 13)
-#define XHCI_COMP_MODE_QUIRK (1 << 14)
-#define XHCI_AVOID_BEI (1 << 15)
-#define XHCI_PLAT (1 << 16)
-#define XHCI_SLOW_SUSPEND (1 << 17)
-#define XHCI_SPURIOUS_WAKEUP (1 << 18)
+#define XHCI_EP_LIMIT_QUIRK BIT_ULL(5)
+#define XHCI_BROKEN_MSI BIT_ULL(6)
+#define XHCI_RESET_ON_RESUME BIT_ULL(7)
+#define XHCI_SW_BW_CHECKING BIT_ULL(8)
+#define XHCI_AMD_0x96_HOST BIT_ULL(9)
+#define XHCI_TRUST_TX_LENGTH BIT_ULL(10)
+#define XHCI_LPM_SUPPORT BIT_ULL(11)
+#define XHCI_INTEL_HOST BIT_ULL(12)
+#define XHCI_SPURIOUS_REBOOT BIT_ULL(13)
+#define XHCI_COMP_MODE_QUIRK BIT_ULL(14)
+#define XHCI_AVOID_BEI BIT_ULL(15)
+#define XHCI_PLAT BIT_ULL(16)
+#define XHCI_SLOW_SUSPEND BIT_ULL(17)
+#define XHCI_SPURIOUS_WAKEUP BIT_ULL(18)
/* For controllers with a broken beyond repair streams implementation */
-#define XHCI_BROKEN_STREAMS (1 << 19)
-#define XHCI_PME_STUCK_QUIRK (1 << 20)
-#define XHCI_MTK_HOST (1 << 21)
-#define XHCI_SSIC_PORT_UNUSED (1 << 22)
-#define XHCI_NO_64BIT_SUPPORT (1 << 23)
-#define XHCI_MISSING_CAS (1 << 24)
+#define XHCI_BROKEN_STREAMS BIT_ULL(19)
+#define XHCI_PME_STUCK_QUIRK BIT_ULL(20)
+#define XHCI_MTK_HOST BIT_ULL(21)
+#define XHCI_SSIC_PORT_UNUSED BIT_ULL(22)
+#define XHCI_NO_64BIT_SUPPORT BIT_ULL(23)
+#define XHCI_MISSING_CAS BIT_ULL(24)
/* For controller with a broken Port Disable implementation */
-#define XHCI_BROKEN_PORT_PED (1 << 25)
-#define XHCI_LIMIT_ENDPOINT_INTERVAL_7 (1 << 26)
-#define XHCI_U2_DISABLE_WAKE (1 << 27)
-#define XHCI_ASMEDIA_MODIFY_FLOWCONTROL (1 << 28)
-#define XHCI_HW_LPM_DISABLE (1 << 29)
-#define XHCI_SUSPEND_DELAY (1 << 30)
-#define XHCI_INTEL_USB_ROLE_SW (1 << 31)
+#define XHCI_BROKEN_PORT_PED BIT_ULL(25)
+#define XHCI_LIMIT_ENDPOINT_INTERVAL_7 BIT_ULL(26)
+#define XHCI_U2_DISABLE_WAKE BIT_ULL(27)
+#define XHCI_ASMEDIA_MODIFY_FLOWCONTROL BIT_ULL(28)
+#define XHCI_HW_LPM_DISABLE BIT_ULL(29)
+#define XHCI_SUSPEND_DELAY BIT_ULL(30)
+#define XHCI_INTEL_USB_ROLE_SW BIT_ULL(31)
+#define XHCI_ZERO_64B_REGS BIT_ULL(32)
+#define XHCI_DEFAULT_PM_RUNTIME_ALLOW BIT_ULL(33)
+#define XHCI_RESET_PLL_ON_DISCONNECT BIT_ULL(34)
+#define XHCI_SNPS_BROKEN_SUSPEND BIT_ULL(35)
unsigned int num_active_eps;
unsigned int limit_active_eps;
/* There are two roothubs to keep track of bus suspend info for */
struct xhci_bus_state bus_state[2];
- /* Is each xHCI roothub port a USB 3.0, USB 2.0, or USB 1.1 port? */
- u8 *port_array;
- /* Array of pointers to USB 3.0 PORTSC registers */
- __le32 __iomem **usb3_ports;
- unsigned int num_usb3_ports;
- /* Array of pointers to USB 2.0 PORTSC registers */
- __le32 __iomem **usb2_ports;
+ struct xhci_port *hw_ports;
struct xhci_hub usb2_rhub;
struct xhci_hub usb3_rhub;
- unsigned int num_usb2_ports;
/* support xHCI 0.96 spec USB2 software LPM */
unsigned sw_lpm_support:1;
/* support xHCI 1.0 spec USB2 hardware LPM */
unsigned hw_lpm_support:1;
+ /* Broken Suspend flag for SNPS Suspend resume issue */
+ unsigned broken_suspend:1;
/* cached usb2 extened protocol capabilites */
u32 *ext_caps;
unsigned int num_ext_caps;
@@ -2091,22 +2106,26 @@ void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring);
unsigned int count_trbs(u64 addr, u64 len);
/* xHCI roothub code */
-void xhci_set_link_state(struct xhci_hcd *xhci, __le32 __iomem **port_array,
- int port_id, u32 link_state);
-void xhci_test_and_clear_bit(struct xhci_hcd *xhci, __le32 __iomem **port_array,
- int port_id, u32 port_bit);
+void xhci_set_link_state(struct xhci_hcd *xhci, struct xhci_port *port,
+ u32 link_state);
+void xhci_test_and_clear_bit(struct xhci_hcd *xhci, struct xhci_port *port,
+ u32 port_bit);
int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex,
char *buf, u16 wLength);
int xhci_hub_status_data(struct usb_hcd *hcd, char *buf);
int xhci_find_raw_port_number(struct usb_hcd *hcd, int port1);
+struct xhci_hub *xhci_get_rhub(struct usb_hcd *hcd);
+
void xhci_hc_died(struct xhci_hcd *xhci);
#ifdef CONFIG_PM
int xhci_bus_suspend(struct usb_hcd *hcd);
int xhci_bus_resume(struct usb_hcd *hcd);
+unsigned long xhci_get_resuming_ports(struct usb_hcd *hcd);
#else
#define xhci_bus_suspend NULL
#define xhci_bus_resume NULL
+#define xhci_get_resuming_ports NULL
#endif /* CONFIG_PM */
u32 xhci_port_state_to_neutral(u32 state);
diff --git a/drivers/usb/isp1760/isp1760-core.c b/drivers/usb/isp1760/isp1760-core.c
index 05d22589b5cc..55b94fd10331 100644
--- a/drivers/usb/isp1760/isp1760-core.c
+++ b/drivers/usb/isp1760/isp1760-core.c
@@ -31,7 +31,7 @@ static void isp1760_init_core(struct isp1760_device *isp)
/* Low-level chip reset */
if (isp->rst_gpio) {
gpiod_set_value_cansleep(isp->rst_gpio, 1);
- mdelay(50);
+ msleep(50);
gpiod_set_value_cansleep(isp->rst_gpio, 0);
}
diff --git a/drivers/usb/isp1760/isp1760-hcd.c b/drivers/usb/isp1760/isp1760-hcd.c
index 42672d6ec525..8142c6b4c4cf 100644
--- a/drivers/usb/isp1760/isp1760-hcd.c
+++ b/drivers/usb/isp1760/isp1760-hcd.c
@@ -1817,7 +1817,6 @@ static int isp1760_hub_control(struct usb_hcd *hcd, u16 typeReq,
u32 temp, status;
unsigned long flags;
int retval = 0;
- unsigned selector;
/*
* FIXME: support SetPortFeatures USB_PORT_FEAT_INDICATOR.
@@ -2010,7 +2009,6 @@ static int isp1760_hub_control(struct usb_hcd *hcd, u16 typeReq,
}
break;
case SetPortFeature:
- selector = wIndex >> 8;
wIndex &= 0xff;
if (!wIndex || wIndex > ports)
goto error;
@@ -2093,7 +2091,7 @@ static void isp1760_stop(struct usb_hcd *hcd)
isp1760_hub_control(hcd, ClearPortFeature, USB_PORT_FEAT_POWER, 1,
NULL, 0);
- mdelay(20);
+ msleep(20);
spin_lock_irq(&priv->lock);
ehci_reset(hcd);
diff --git a/drivers/usb/misc/adutux.c b/drivers/usb/misc/adutux.c
index b3160afe0458..9465fb95d70a 100644
--- a/drivers/usb/misc/adutux.c
+++ b/drivers/usb/misc/adutux.c
@@ -155,11 +155,12 @@ static void adu_interrupt_in_callback(struct urb *urb)
{
struct adu_device *dev = urb->context;
int status = urb->status;
+ unsigned long flags;
adu_debug_data(&dev->udev->dev, __func__,
urb->actual_length, urb->transfer_buffer);
- spin_lock(&dev->buflock);
+ spin_lock_irqsave(&dev->buflock, flags);
if (status != 0) {
if ((status != -ENOENT) && (status != -ECONNRESET) &&
@@ -190,7 +191,7 @@ static void adu_interrupt_in_callback(struct urb *urb)
exit:
dev->read_urb_finished = 1;
- spin_unlock(&dev->buflock);
+ spin_unlock_irqrestore(&dev->buflock, flags);
/* always wake up so we recover from errors */
wake_up_interruptible(&dev->read_wait);
}
@@ -199,6 +200,7 @@ static void adu_interrupt_out_callback(struct urb *urb)
{
struct adu_device *dev = urb->context;
int status = urb->status;
+ unsigned long flags;
adu_debug_data(&dev->udev->dev, __func__,
urb->actual_length, urb->transfer_buffer);
@@ -213,10 +215,10 @@ static void adu_interrupt_out_callback(struct urb *urb)
return;
}
- spin_lock(&dev->buflock);
+ spin_lock_irqsave(&dev->buflock, flags);
dev->out_urb_finished = 1;
wake_up(&dev->write_wait);
- spin_unlock(&dev->buflock);
+ spin_unlock_irqrestore(&dev->buflock, flags);
}
static int adu_open(struct inode *inode, struct file *file)
diff --git a/drivers/usb/misc/appledisplay.c b/drivers/usb/misc/appledisplay.c
index b3eb8b989bd4..39ca31b4de46 100644
--- a/drivers/usb/misc/appledisplay.c
+++ b/drivers/usb/misc/appledisplay.c
@@ -50,6 +50,8 @@ static const struct usb_device_id appledisplay_table[] = {
{ APPLEDISPLAY_DEVICE(0x9219) },
{ APPLEDISPLAY_DEVICE(0x921c) },
{ APPLEDISPLAY_DEVICE(0x921d) },
+ { APPLEDISPLAY_DEVICE(0x9222) },
+ { APPLEDISPLAY_DEVICE(0x9226) },
{ APPLEDISPLAY_DEVICE(0x9236) },
/* Terminating entry */
@@ -89,6 +91,7 @@ static void appledisplay_complete(struct urb *urb)
dev_err(dev,
"OVERFLOW with data length %d, actual length is %d\n",
ACD_URB_BUFFER_LEN, pdata->urb->actual_length);
+ /* fall through */
case -ECONNRESET:
case -ENOENT:
case -ESHUTDOWN:
@@ -145,8 +148,11 @@ static int appledisplay_bl_update_status(struct backlight_device *bd)
pdata->msgdata, 2,
ACD_USB_TIMEOUT);
mutex_unlock(&pdata->sysfslock);
-
- return retval;
+
+ if (retval < 0)
+ return retval;
+ else
+ return 0;
}
static int appledisplay_bl_get_brightness(struct backlight_device *bd)
diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c
index 8d33187ce2af..ba05dd80a020 100644
--- a/drivers/usb/misc/iowarrior.c
+++ b/drivers/usb/misc/iowarrior.c
@@ -81,7 +81,6 @@ struct iowarrior {
atomic_t write_busy; /* number of write-urbs submitted */
atomic_t read_idx;
atomic_t intr_idx;
- spinlock_t intr_idx_lock; /* protects intr_idx */
atomic_t overflow_flag; /* signals an index 'rollover' */
int present; /* this is 1 as long as the device is connected */
int opened; /* this is 1 if the device is currently open */
@@ -166,7 +165,6 @@ static void iowarrior_callback(struct urb *urb)
goto exit;
}
- spin_lock(&dev->intr_idx_lock);
intr_idx = atomic_read(&dev->intr_idx);
/* aux_idx become previous intr_idx */
aux_idx = (intr_idx == 0) ? (MAX_INTERRUPT_BUFFER - 1) : (intr_idx - 1);
@@ -181,7 +179,6 @@ static void iowarrior_callback(struct urb *urb)
(dev->read_queue + offset, urb->transfer_buffer,
dev->report_size)) {
/* equal values on interface 0 will be ignored */
- spin_unlock(&dev->intr_idx_lock);
goto exit;
}
}
@@ -202,7 +199,6 @@ static void iowarrior_callback(struct urb *urb)
*(dev->read_queue + offset + (dev->report_size)) = dev->serial_number++;
atomic_set(&dev->intr_idx, aux_idx);
- spin_unlock(&dev->intr_idx_lock);
/* tell the blocking read about the new data */
wake_up_interruptible(&dev->read_wait);
@@ -762,7 +758,6 @@ static int iowarrior_probe(struct usb_interface *interface,
atomic_set(&dev->intr_idx, 0);
atomic_set(&dev->read_idx, 0);
- spin_lock_init(&dev->intr_idx_lock);
atomic_set(&dev->overflow_flag, 0);
init_waitqueue_head(&dev->read_wait);
atomic_set(&dev->write_busy, 0);
@@ -813,8 +808,8 @@ static int iowarrior_probe(struct usb_interface *interface,
dev->int_in_endpoint->bInterval);
/* create an internal buffer for interrupt data from the device */
dev->read_queue =
- kmalloc(((dev->report_size + 1) * MAX_INTERRUPT_BUFFER),
- GFP_KERNEL);
+ kmalloc_array(dev->report_size + 1, MAX_INTERRUPT_BUFFER,
+ GFP_KERNEL);
if (!dev->read_queue)
goto error;
/* Get the serial-number of the chip */
diff --git a/drivers/usb/misc/ldusb.c b/drivers/usb/misc/ldusb.c
index 236a60f53099..006762b72ff5 100644
--- a/drivers/usb/misc/ldusb.c
+++ b/drivers/usb/misc/ldusb.c
@@ -225,6 +225,7 @@ static void ld_usb_interrupt_in_callback(struct urb *urb)
size_t *actual_buffer;
unsigned int next_ring_head;
int status = urb->status;
+ unsigned long flags;
int retval;
if (status) {
@@ -236,12 +237,12 @@ static void ld_usb_interrupt_in_callback(struct urb *urb)
dev_dbg(&dev->intf->dev,
"%s: nonzero status received: %d\n", __func__,
status);
- spin_lock(&dev->rbsl);
+ spin_lock_irqsave(&dev->rbsl, flags);
goto resubmit; /* maybe we can recover */
}
}
- spin_lock(&dev->rbsl);
+ spin_lock_irqsave(&dev->rbsl, flags);
if (urb->actual_length > 0) {
next_ring_head = (dev->ring_head+1) % ring_buffer_size;
if (next_ring_head != dev->ring_tail) {
@@ -270,7 +271,7 @@ resubmit:
dev->buffer_overflow = 1;
}
}
- spin_unlock(&dev->rbsl);
+ spin_unlock_irqrestore(&dev->rbsl, flags);
exit:
dev->interrupt_in_done = 1;
wake_up_interruptible(&dev->read_wait);
@@ -695,7 +696,10 @@ static int ld_usb_probe(struct usb_interface *intf, const struct usb_device_id *
dev_warn(&intf->dev, "Interrupt out endpoint not found (using control endpoint instead)\n");
dev->interrupt_in_endpoint_size = usb_endpoint_maxp(dev->interrupt_in_endpoint);
- dev->ring_buffer = kmalloc(ring_buffer_size*(sizeof(size_t)+dev->interrupt_in_endpoint_size), GFP_KERNEL);
+ dev->ring_buffer =
+ kmalloc_array(ring_buffer_size,
+ sizeof(size_t) + dev->interrupt_in_endpoint_size,
+ GFP_KERNEL);
if (!dev->ring_buffer)
goto error;
dev->interrupt_in_buffer = kmalloc(dev->interrupt_in_endpoint_size, GFP_KERNEL);
@@ -706,7 +710,9 @@ static int ld_usb_probe(struct usb_interface *intf, const struct usb_device_id *
goto error;
dev->interrupt_out_endpoint_size = dev->interrupt_out_endpoint ? usb_endpoint_maxp(dev->interrupt_out_endpoint) :
udev->descriptor.bMaxPacketSize0;
- dev->interrupt_out_buffer = kmalloc(write_buffer_size*dev->interrupt_out_endpoint_size, GFP_KERNEL);
+ dev->interrupt_out_buffer =
+ kmalloc_array(write_buffer_size,
+ dev->interrupt_out_endpoint_size, GFP_KERNEL);
if (!dev->interrupt_out_buffer)
goto error;
dev->interrupt_out_urb = usb_alloc_urb(0, GFP_KERNEL);
diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c
index bf47bd8bc76f..006cf13b2199 100644
--- a/drivers/usb/misc/legousbtower.c
+++ b/drivers/usb/misc/legousbtower.c
@@ -722,6 +722,7 @@ static void tower_interrupt_in_callback (struct urb *urb)
struct lego_usb_tower *dev = urb->context;
int status = urb->status;
int retval;
+ unsigned long flags;
lego_usb_tower_debug_data(&dev->udev->dev, __func__,
urb->actual_length, urb->transfer_buffer);
@@ -740,7 +741,7 @@ static void tower_interrupt_in_callback (struct urb *urb)
}
if (urb->actual_length > 0) {
- spin_lock (&dev->read_buffer_lock);
+ spin_lock_irqsave(&dev->read_buffer_lock, flags);
if (dev->read_buffer_length + urb->actual_length < read_buffer_size) {
memcpy (dev->read_buffer + dev->read_buffer_length,
dev->interrupt_in_buffer,
@@ -753,7 +754,7 @@ static void tower_interrupt_in_callback (struct urb *urb)
pr_warn("read_buffer overflow, %d bytes dropped\n",
urb->actual_length);
}
- spin_unlock (&dev->read_buffer_lock);
+ spin_unlock_irqrestore(&dev->read_buffer_lock, flags);
}
resubmit:
diff --git a/drivers/usb/misc/sisusbvga/sisusb.c b/drivers/usb/misc/sisusbvga/sisusb.c
index 3e65bdc2615c..3198d0477cf8 100644
--- a/drivers/usb/misc/sisusbvga/sisusb.c
+++ b/drivers/usb/misc/sisusbvga/sisusb.c
@@ -1750,7 +1750,7 @@ static int sisusb_setup_screen(struct sisusb_usb_data *sisusb,
static int sisusb_set_default_mode(struct sisusb_usb_data *sisusb,
int touchengines)
{
- int ret = 0, i, j, modex, modey, bpp, du;
+ int ret = 0, i, j, modex, bpp, du;
u8 sr31, cr63, tmp8;
static const char attrdata[] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
@@ -1773,7 +1773,7 @@ static int sisusb_set_default_mode(struct sisusb_usb_data *sisusb,
0x00
};
- modex = 640; modey = 480; bpp = 2;
+ modex = 640; bpp = 2;
GETIREG(SISSR, 0x31, &sr31);
GETIREG(SISCR, 0x63, &cr63);
@@ -2107,7 +2107,7 @@ static void sisusb_get_ramconfig(struct sisusb_usb_data *sisusb)
bw = busSDR[(tmp8 & 0x03)];
break;
case 2:
- ramtypetext1 = "asymmeric";
+ ramtypetext1 = "asymmetric";
sisusb->vramsize += sisusb->vramsize/2;
bw = busDDRA[(tmp8 & 0x03)];
break;
diff --git a/drivers/usb/misc/sisusbvga/sisusb_con.c b/drivers/usb/misc/sisusbvga/sisusb_con.c
index a0d6e0af957c..c4f017e1d17a 100644
--- a/drivers/usb/misc/sisusbvga/sisusb_con.c
+++ b/drivers/usb/misc/sisusbvga/sisusb_con.c
@@ -1243,7 +1243,7 @@ sisusbcon_font_set(struct vc_data *c, struct console_font *font,
}
if (!sisusb->font_backup)
- sisusb->font_backup = vmalloc(charcount * 32);
+ sisusb->font_backup = vmalloc(array_size(charcount, 32));
if (sisusb->font_backup) {
memcpy(sisusb->font_backup, font->data, charcount * 32);
diff --git a/drivers/usb/misc/trancevibrator.c b/drivers/usb/misc/trancevibrator.c
index b3e1f553954a..ac357ce2d1a6 100644
--- a/drivers/usb/misc/trancevibrator.c
+++ b/drivers/usb/misc/trancevibrator.c
@@ -46,7 +46,9 @@ static ssize_t speed_store(struct device *dev, struct device_attribute *attr,
struct trancevibrator *tv = usb_get_intfdata(intf);
int temp, retval, old;
- temp = simple_strtoul(buf, NULL, 10);
+ retval = kstrtoint(buf, 10, &temp);
+ if (retval)
+ return retval;
if (temp > 255)
temp = 255;
else if (temp < 0)
diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c
index 9e1142b8b91b..c7f82310e73e 100644
--- a/drivers/usb/misc/usbtest.c
+++ b/drivers/usb/misc/usbtest.c
@@ -1082,11 +1082,12 @@ static void ctrl_complete(struct urb *urb)
struct usb_ctrlrequest *reqp;
struct subcase *subcase;
int status = urb->status;
+ unsigned long flags;
reqp = (struct usb_ctrlrequest *)urb->setup_packet;
subcase = container_of(reqp, struct subcase, setup);
- spin_lock(&ctx->lock);
+ spin_lock_irqsave(&ctx->lock, flags);
ctx->count--;
ctx->pending--;
@@ -1185,7 +1186,7 @@ error:
/* signal completion when nothing's queued */
if (ctx->pending == 0)
complete(&ctx->complete);
- spin_unlock(&ctx->lock);
+ spin_unlock_irqrestore(&ctx->lock, flags);
}
static int
@@ -1917,8 +1918,9 @@ struct transfer_context {
static void complicated_callback(struct urb *urb)
{
struct transfer_context *ctx = urb->context;
+ unsigned long flags;
- spin_lock(&ctx->lock);
+ spin_lock_irqsave(&ctx->lock, flags);
ctx->count--;
ctx->packet_count += urb->number_of_packets;
@@ -1958,7 +1960,7 @@ static void complicated_callback(struct urb *urb)
complete(&ctx->done);
}
done:
- spin_unlock(&ctx->lock);
+ spin_unlock_irqrestore(&ctx->lock, flags);
}
static struct urb *iso_alloc_urb(
diff --git a/drivers/usb/misc/uss720.c b/drivers/usb/misc/uss720.c
index de9a502491c2..b5d661644263 100644
--- a/drivers/usb/misc/uss720.c
+++ b/drivers/usb/misc/uss720.c
@@ -369,7 +369,7 @@ static unsigned char parport_uss720_frob_control(struct parport *pp, unsigned ch
mask &= 0x0f;
val &= 0x0f;
d = (priv->reg[1] & (~mask)) ^ val;
- if (set_1284_register(pp, 2, d, GFP_KERNEL))
+ if (set_1284_register(pp, 2, d, GFP_ATOMIC))
return 0;
priv->reg[1] = d;
return d & 0xf;
@@ -379,7 +379,7 @@ static unsigned char parport_uss720_read_status(struct parport *pp)
{
unsigned char ret;
- if (get_1284_register(pp, 1, &ret, GFP_KERNEL))
+ if (get_1284_register(pp, 1, &ret, GFP_ATOMIC))
return 0;
return ret & 0xf8;
}
@@ -748,13 +748,11 @@ static void uss720_disconnect(struct usb_interface *intf)
{
struct parport *pp = usb_get_intfdata(intf);
struct parport_uss720_private *priv;
- struct usb_device *usbdev;
dev_dbg(&intf->dev, "disconnect\n");
usb_set_intfdata(intf, NULL);
if (pp) {
priv = pp->private_data;
- usbdev = priv->usbdev;
priv->usbdev = NULL;
priv->pp = NULL;
dev_dbg(&intf->dev, "parport_remove_port\n");
diff --git a/drivers/usb/misc/yurex.c b/drivers/usb/misc/yurex.c
index 8abb6cbbd98a..6d9fd5f64903 100644
--- a/drivers/usb/misc/yurex.c
+++ b/drivers/usb/misc/yurex.c
@@ -396,8 +396,7 @@ static ssize_t yurex_read(struct file *file, char __user *buffer, size_t count,
loff_t *ppos)
{
struct usb_yurex *dev;
- int retval = 0;
- int bytes_read = 0;
+ int len = 0;
char in_buffer[20];
unsigned long flags;
@@ -405,26 +404,19 @@ static ssize_t yurex_read(struct file *file, char __user *buffer, size_t count,
mutex_lock(&dev->io_mutex);
if (!dev->interface) { /* already disconnected */
- retval = -ENODEV;
- goto exit;
+ mutex_unlock(&dev->io_mutex);
+ return -ENODEV;
}
spin_lock_irqsave(&dev->lock, flags);
- bytes_read = snprintf(in_buffer, 20, "%lld\n", dev->bbu);
+ len = snprintf(in_buffer, 20, "%lld\n", dev->bbu);
spin_unlock_irqrestore(&dev->lock, flags);
+ mutex_unlock(&dev->io_mutex);
- if (*ppos < bytes_read) {
- if (copy_to_user(buffer, in_buffer + *ppos, bytes_read - *ppos))
- retval = -EFAULT;
- else {
- retval = bytes_read - *ppos;
- *ppos += bytes_read;
- }
- }
+ if (WARN_ON_ONCE(len >= sizeof(in_buffer)))
+ return -EIO;
-exit:
- mutex_unlock(&dev->io_mutex);
- return retval;
+ return simple_read_from_buffer(buffer, count, ppos, in_buffer, len);
}
static ssize_t yurex_write(struct file *file, const char __user *user_buffer,
@@ -432,13 +424,13 @@ static ssize_t yurex_write(struct file *file, const char __user *user_buffer,
{
struct usb_yurex *dev;
int i, set = 0, retval = 0;
- char buffer[16];
+ char buffer[16 + 1];
char *data = buffer;
unsigned long long c, c2 = 0;
signed long timeout = 0;
DEFINE_WAIT(wait);
- count = min(sizeof(buffer), count);
+ count = min(sizeof(buffer) - 1, count);
dev = file->private_data;
/* verify that we actually have some data to write */
@@ -457,6 +449,7 @@ static ssize_t yurex_write(struct file *file, const char __user *user_buffer,
retval = -EFAULT;
goto error;
}
+ buffer[count] = 0;
memset(dev->cntl_buffer, CMD_PADDING, YUREX_BUF_SIZE);
switch (buffer[0]) {
diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c
index 2761fad66b95..ac2b4fcc265f 100644
--- a/drivers/usb/mon/mon_bin.c
+++ b/drivers/usb/mon/mon_bin.c
@@ -95,8 +95,8 @@ struct mon_bin_hdr {
unsigned short busnum; /* Bus number */
char flag_setup;
char flag_data;
- s64 ts_sec; /* getnstimeofday64 */
- s32 ts_usec; /* getnstimeofday64 */
+ s64 ts_sec; /* ktime_get_real_ts64 */
+ s32 ts_usec; /* ktime_get_real_ts64 */
int status;
unsigned int len_urb; /* Length of data (submitted or actual) */
unsigned int len_cap; /* Delivered length */
@@ -497,7 +497,7 @@ static void mon_bin_event(struct mon_reader_bin *rp, struct urb *urb,
struct mon_bin_hdr *ep;
char data_tag = 0;
- getnstimeofday64(&ts);
+ ktime_get_real_ts64(&ts);
spin_lock_irqsave(&rp->b_lock, flags);
@@ -637,7 +637,7 @@ static void mon_bin_error(void *data, struct urb *urb, int error)
unsigned int offset;
struct mon_bin_hdr *ep;
- getnstimeofday64(&ts);
+ ktime_get_real_ts64(&ts);
spin_lock_irqsave(&rp->b_lock, flags);
@@ -1024,7 +1024,8 @@ static long mon_bin_ioctl(struct file *file, unsigned int cmd, unsigned long arg
return -EINVAL;
size = CHUNK_ALIGN(arg);
- vec = kzalloc(sizeof(struct mon_pgmap) * (size / CHUNK_SIZE), GFP_KERNEL);
+ vec = kcalloc(size / CHUNK_SIZE, sizeof(struct mon_pgmap),
+ GFP_KERNEL);
if (vec == NULL) {
ret = -ENOMEM;
break;
@@ -1227,7 +1228,7 @@ static void mon_bin_vma_close(struct vm_area_struct *vma)
/*
* Map ring pages to user space.
*/
-static int mon_bin_vma_fault(struct vm_fault *vmf)
+static vm_fault_t mon_bin_vma_fault(struct vm_fault *vmf)
{
struct mon_reader_bin *rp = vmf->vma->vm_private_data;
unsigned long offset, chunk_idx;
diff --git a/drivers/usb/mon/mon_text.c b/drivers/usb/mon/mon_text.c
index 984f7e12a6a5..bc5ecd5ff565 100644
--- a/drivers/usb/mon/mon_text.c
+++ b/drivers/usb/mon/mon_text.c
@@ -700,7 +700,6 @@ static const struct file_operations mon_fops_text_u = {
int mon_text_add(struct mon_bus *mbus, const struct usb_bus *ubus)
{
- struct dentry *d;
enum { NAMESZ = 10 };
char name[NAMESZ];
int busnum = ubus? ubus->busnum: 0;
@@ -713,42 +712,32 @@ int mon_text_add(struct mon_bus *mbus, const struct usb_bus *ubus)
rc = snprintf(name, NAMESZ, "%dt", busnum);
if (rc <= 0 || rc >= NAMESZ)
goto err_print_t;
- d = debugfs_create_file(name, 0600, mon_dir, mbus,
+ mbus->dent_t = debugfs_create_file(name, 0600, mon_dir, mbus,
&mon_fops_text_t);
- if (d == NULL)
- goto err_create_t;
- mbus->dent_t = d;
}
rc = snprintf(name, NAMESZ, "%du", busnum);
if (rc <= 0 || rc >= NAMESZ)
goto err_print_u;
- d = debugfs_create_file(name, 0600, mon_dir, mbus, &mon_fops_text_u);
- if (d == NULL)
- goto err_create_u;
- mbus->dent_u = d;
+ mbus->dent_u = debugfs_create_file(name, 0600, mon_dir, mbus,
+ &mon_fops_text_u);
rc = snprintf(name, NAMESZ, "%ds", busnum);
if (rc <= 0 || rc >= NAMESZ)
goto err_print_s;
- d = debugfs_create_file(name, 0600, mon_dir, mbus, &mon_fops_stat);
- if (d == NULL)
- goto err_create_s;
- mbus->dent_s = d;
+ mbus->dent_s = debugfs_create_file(name, 0600, mon_dir, mbus,
+ &mon_fops_stat);
return 1;
-err_create_s:
err_print_s:
debugfs_remove(mbus->dent_u);
mbus->dent_u = NULL;
-err_create_u:
err_print_u:
if (ubus != NULL) {
debugfs_remove(mbus->dent_t);
mbus->dent_t = NULL;
}
-err_create_t:
err_print_t:
return 0;
}
@@ -756,8 +745,7 @@ err_print_t:
void mon_text_del(struct mon_bus *mbus)
{
debugfs_remove(mbus->dent_u);
- if (mbus->dent_t != NULL)
- debugfs_remove(mbus->dent_t);
+ debugfs_remove(mbus->dent_t);
debugfs_remove(mbus->dent_s);
}
@@ -775,18 +763,7 @@ static void mon_text_ctor(void *mem)
int __init mon_text_init(void)
{
- struct dentry *mondir;
-
- mondir = debugfs_create_dir("usbmon", usb_debug_root);
- if (IS_ERR(mondir)) {
- /* debugfs not available, but we can use usbmon without it */
- return 0;
- }
- if (mondir == NULL) {
- printk(KERN_NOTICE TAG ": unable to create usbmon directory\n");
- return -ENOMEM;
- }
- mon_dir = mondir;
+ mon_dir = debugfs_create_dir("usbmon", usb_debug_root);
return 0;
}
diff --git a/drivers/usb/mtu3/Kconfig b/drivers/usb/mtu3/Kconfig
index 25cd61947bee..40bbf1f53337 100644
--- a/drivers/usb/mtu3/Kconfig
+++ b/drivers/usb/mtu3/Kconfig
@@ -2,7 +2,7 @@
config USB_MTU3
tristate "MediaTek USB3 Dual Role controller"
- depends on EXTCON && (USB || USB_GADGET) && HAS_DMA
+ depends on USB || USB_GADGET
depends on ARCH_MEDIATEK || COMPILE_TEST
select USB_XHCI_MTK if USB_SUPPORT && USB_XHCI_HCD
help
@@ -40,6 +40,7 @@ config USB_MTU3_GADGET
config USB_MTU3_DUAL_ROLE
bool "Dual Role mode"
depends on ((USB=y || USB=USB_MTU3) && (USB_GADGET=y || USB_GADGET=USB_MTU3))
+ depends on (EXTCON=y || EXTCON=USB_MTU3)
help
This is the default mode of working of MTU3 controller where
both host and gadget features are enabled.
diff --git a/drivers/usb/mtu3/mtu3.h b/drivers/usb/mtu3/mtu3.h
index 2cd00a24afd9..87823ac0d120 100644
--- a/drivers/usb/mtu3/mtu3.h
+++ b/drivers/usb/mtu3/mtu3.h
@@ -196,10 +196,12 @@ struct mtu3_gpd_ring {
* @vbus: vbus 5V used by host mode
* @edev: external connector used to detect vbus and iddig changes
* @vbus_nb: notifier for vbus detection
-* @vbus_nb: notifier for iddig(idpin) detection
-* @extcon_reg_dwork: delay work for extcon notifier register, waiting for
-* xHCI driver initialization, it's necessary for system bootup
-* as device.
+* @vbus_work : work of vbus detection notifier, used to avoid sleep in
+* notifier callback which is atomic context
+* @vbus_event : event of vbus detecion notifier
+* @id_nb : notifier for iddig(idpin) detection
+* @id_work : work of iddig detection notifier
+* @id_event : event of iddig detecion notifier
* @is_u3_drd: whether port0 supports usb3.0 dual-role device or not
* @manual_drd_enabled: it's true when supports dual-role device by debugfs
* to switch host/device modes depending on user input.
@@ -208,8 +210,11 @@ struct otg_switch_mtk {
struct regulator *vbus;
struct extcon_dev *edev;
struct notifier_block vbus_nb;
+ struct work_struct vbus_work;
+ unsigned long vbus_event;
struct notifier_block id_nb;
- struct delayed_work extcon_reg_dwork;
+ struct work_struct id_work;
+ unsigned long id_event;
bool is_u3_drd;
bool manual_drd_enabled;
};
diff --git a/drivers/usb/mtu3/mtu3_core.c b/drivers/usb/mtu3/mtu3_core.c
index b1b99a8f6a7a..ae70b9bfd797 100644
--- a/drivers/usb/mtu3/mtu3_core.c
+++ b/drivers/usb/mtu3/mtu3_core.c
@@ -107,8 +107,12 @@ static int mtu3_device_enable(struct mtu3 *mtu)
(SSUSB_U2_PORT_DIS | SSUSB_U2_PORT_PDN |
SSUSB_U2_PORT_HOST_SEL));
- if (mtu->ssusb->dr_mode == USB_DR_MODE_OTG)
+ if (mtu->ssusb->dr_mode == USB_DR_MODE_OTG) {
mtu3_setbits(ibase, SSUSB_U2_CTRL(0), SSUSB_U2_PORT_OTG_SEL);
+ if (mtu->is_u3_ip)
+ mtu3_setbits(ibase, SSUSB_U3_CTRL(0),
+ SSUSB_U3_PORT_DUAL_MODE);
+ }
return ssusb_check_clocks(mtu->ssusb, check_clk);
}
@@ -176,13 +180,13 @@ static void mtu3_intr_enable(struct mtu3 *mtu)
mtu3_writel(mbase, U3D_LV1IESR, value);
/* Enable U2 common USB interrupts */
- value = SUSPEND_INTR | RESUME_INTR | RESET_INTR;
+ value = SUSPEND_INTR | RESUME_INTR | RESET_INTR | LPM_RESUME_INTR;
mtu3_writel(mbase, U3D_COMMON_USB_INTR_ENABLE, value);
if (mtu->is_u3_ip) {
/* Enable U3 LTSSM interrupts */
- value = HOT_RST_INTR | WARM_RST_INTR | VBUS_RISE_INTR |
- VBUS_FALL_INTR | ENTER_U3_INTR | EXIT_U3_INTR;
+ value = HOT_RST_INTR | WARM_RST_INTR |
+ ENTER_U3_INTR | EXIT_U3_INTR;
mtu3_writel(mbase, U3D_LTSSM_INTR_ENABLE, value);
}
@@ -195,6 +199,16 @@ static void mtu3_intr_enable(struct mtu3 *mtu)
mtu3_writel(mbase, U3D_DEV_LINK_INTR_ENABLE, SSUSB_DEV_SPEED_CHG_INTR);
}
+/* reset: u2 - data toggle, u3 - SeqN, flow control status etc */
+static void mtu3_ep_reset(struct mtu3_ep *mep)
+{
+ struct mtu3 *mtu = mep->mtu;
+ u32 rst_bit = EP_RST(mep->is_in, mep->epnum);
+
+ mtu3_setbits(mtu->mac_base, U3D_EP_RST, rst_bit);
+ mtu3_clrbits(mtu->mac_base, U3D_EP_RST, rst_bit);
+}
+
/* set/clear the stall and toggle bits for non-ep0 */
void mtu3_ep_stall_set(struct mtu3_ep *mep, bool set)
{
@@ -220,8 +234,7 @@ void mtu3_ep_stall_set(struct mtu3_ep *mep, bool set)
}
if (!set) {
- mtu3_setbits(mbase, U3D_EP_RST, EP_RST(mep->is_in, epnum));
- mtu3_clrbits(mbase, U3D_EP_RST, EP_RST(mep->is_in, epnum));
+ mtu3_ep_reset(mep);
mep->flags &= ~MTU3_EP_STALL;
} else {
mep->flags |= MTU3_EP_STALL;
@@ -400,6 +413,7 @@ void mtu3_deconfig_ep(struct mtu3 *mtu, struct mtu3_ep *mep)
mtu3_setbits(mbase, U3D_QIECR0, QMU_RX_DONE_INT(epnum));
}
+ mtu3_ep_reset(mep);
ep_fifo_free(mep);
dev_dbg(mtu->dev, "%s: %s\n", __func__, mep->name);
@@ -658,8 +672,10 @@ static irqreturn_t mtu3_u3_ltssm_isr(struct mtu3 *mtu)
if (ltssm & (HOT_RST_INTR | WARM_RST_INTR))
mtu3_gadget_reset(mtu);
- if (ltssm & VBUS_FALL_INTR)
+ if (ltssm & VBUS_FALL_INTR) {
mtu3_ss_func_set(mtu, false);
+ mtu3_gadget_reset(mtu);
+ }
if (ltssm & VBUS_RISE_INTR)
mtu3_ss_func_set(mtu, true);
@@ -692,6 +708,12 @@ static irqreturn_t mtu3_u2_common_isr(struct mtu3 *mtu)
if (u2comm & RESET_INTR)
mtu3_gadget_reset(mtu);
+ if (u2comm & LPM_RESUME_INTR) {
+ if (!(mtu3_readl(mbase, U3D_POWER_MANAGEMENT) & LPM_HRWE))
+ mtu3_setbits(mbase, U3D_USB20_MISC_CONTROL,
+ LPM_U3_ACK_EN);
+ }
+
return IRQ_HANDLED;
}
diff --git a/drivers/usb/mtu3/mtu3_dr.c b/drivers/usb/mtu3/mtu3_dr.c
index db7562d99b95..ac60e9c8564e 100644
--- a/drivers/usb/mtu3/mtu3_dr.c
+++ b/drivers/usb/mtu3/mtu3_dr.c
@@ -174,16 +174,40 @@ static void ssusb_set_mailbox(struct otg_switch_mtk *otg_sx,
}
}
-static int ssusb_id_notifier(struct notifier_block *nb,
- unsigned long event, void *ptr)
+static void ssusb_id_work(struct work_struct *work)
{
struct otg_switch_mtk *otg_sx =
- container_of(nb, struct otg_switch_mtk, id_nb);
+ container_of(work, struct otg_switch_mtk, id_work);
- if (event)
+ if (otg_sx->id_event)
ssusb_set_mailbox(otg_sx, MTU3_ID_GROUND);
else
ssusb_set_mailbox(otg_sx, MTU3_ID_FLOAT);
+}
+
+static void ssusb_vbus_work(struct work_struct *work)
+{
+ struct otg_switch_mtk *otg_sx =
+ container_of(work, struct otg_switch_mtk, vbus_work);
+
+ if (otg_sx->vbus_event)
+ ssusb_set_mailbox(otg_sx, MTU3_VBUS_VALID);
+ else
+ ssusb_set_mailbox(otg_sx, MTU3_VBUS_OFF);
+}
+
+/*
+ * @ssusb_id_notifier is called in atomic context, but @ssusb_set_mailbox
+ * may sleep, so use work queue here
+ */
+static int ssusb_id_notifier(struct notifier_block *nb,
+ unsigned long event, void *ptr)
+{
+ struct otg_switch_mtk *otg_sx =
+ container_of(nb, struct otg_switch_mtk, id_nb);
+
+ otg_sx->id_event = event;
+ schedule_work(&otg_sx->id_work);
return NOTIFY_DONE;
}
@@ -194,10 +218,8 @@ static int ssusb_vbus_notifier(struct notifier_block *nb,
struct otg_switch_mtk *otg_sx =
container_of(nb, struct otg_switch_mtk, vbus_nb);
- if (event)
- ssusb_set_mailbox(otg_sx, MTU3_VBUS_VALID);
- else
- ssusb_set_mailbox(otg_sx, MTU3_VBUS_OFF);
+ otg_sx->vbus_event = event;
+ schedule_work(&otg_sx->vbus_work);
return NOTIFY_DONE;
}
@@ -238,15 +260,6 @@ static int ssusb_extcon_register(struct otg_switch_mtk *otg_sx)
return 0;
}
-static void extcon_register_dwork(struct work_struct *work)
-{
- struct delayed_work *dwork = to_delayed_work(work);
- struct otg_switch_mtk *otg_sx =
- container_of(dwork, struct otg_switch_mtk, extcon_reg_dwork);
-
- ssusb_extcon_register(otg_sx);
-}
-
/*
* We provide an interface via debugfs to switch between host and device modes
* depending on user input.
@@ -365,10 +378,6 @@ static void ssusb_debugfs_init(struct ssusb_mtk *ssusb)
struct dentry *root;
root = debugfs_create_dir(dev_name(ssusb->dev), usb_debug_root);
- if (!root) {
- dev_err(ssusb->dev, "create debugfs root failed\n");
- return;
- }
ssusb->dbgfs_root = root;
debugfs_create_file("mode", 0644, root, ssusb, &ssusb_mode_fops);
@@ -407,18 +416,13 @@ int ssusb_otg_switch_init(struct ssusb_mtk *ssusb)
{
struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
- if (otg_sx->manual_drd_enabled) {
+ INIT_WORK(&otg_sx->id_work, ssusb_id_work);
+ INIT_WORK(&otg_sx->vbus_work, ssusb_vbus_work);
+
+ if (otg_sx->manual_drd_enabled)
ssusb_debugfs_init(ssusb);
- } else {
- INIT_DELAYED_WORK(&otg_sx->extcon_reg_dwork,
- extcon_register_dwork);
-
- /*
- * It is enough to delay 1s for waiting for
- * host initialization
- */
- schedule_delayed_work(&otg_sx->extcon_reg_dwork, HZ);
- }
+ else
+ ssusb_extcon_register(otg_sx);
return 0;
}
@@ -429,6 +433,7 @@ void ssusb_otg_switch_exit(struct ssusb_mtk *ssusb)
if (otg_sx->manual_drd_enabled)
ssusb_debugfs_exit(ssusb);
- else
- cancel_delayed_work(&otg_sx->extcon_reg_dwork);
+
+ cancel_work_sync(&otg_sx->id_work);
+ cancel_work_sync(&otg_sx->vbus_work);
}
diff --git a/drivers/usb/mtu3/mtu3_gadget.c b/drivers/usb/mtu3/mtu3_gadget.c
index f05f10f5c171..bbcd3332471d 100644
--- a/drivers/usb/mtu3/mtu3_gadget.c
+++ b/drivers/usb/mtu3/mtu3_gadget.c
@@ -585,6 +585,17 @@ static const struct usb_gadget_ops mtu3_gadget_ops = {
.udc_stop = mtu3_gadget_stop,
};
+static void mtu3_state_reset(struct mtu3 *mtu)
+{
+ mtu->address = 0;
+ mtu->ep0_state = MU3D_EP0_STATE_SETUP;
+ mtu->may_wakeup = 0;
+ mtu->u1_enable = 0;
+ mtu->u2_enable = 0;
+ mtu->delayed_status = false;
+ mtu->test_mode = false;
+}
+
static void init_hw_ep(struct mtu3 *mtu, struct mtu3_ep *mep,
u32 epnum, u32 is_in)
{
@@ -660,14 +671,10 @@ int mtu3_gadget_setup(struct mtu3 *mtu)
mtu3_gadget_init_eps(mtu);
ret = usb_add_gadget_udc(mtu->dev, &mtu->g);
- if (ret) {
+ if (ret)
dev_err(mtu->dev, "failed to register udc\n");
- return ret;
- }
- usb_gadget_set_state(&mtu->g, USB_STATE_NOTATTACHED);
-
- return 0;
+ return ret;
}
void mtu3_gadget_cleanup(struct mtu3 *mtu)
@@ -706,6 +713,7 @@ void mtu3_gadget_disconnect(struct mtu3 *mtu)
spin_lock(&mtu->lock);
}
+ mtu3_state_reset(mtu);
usb_gadget_set_state(&mtu->g, USB_STATE_NOTATTACHED);
}
@@ -716,11 +724,6 @@ void mtu3_gadget_reset(struct mtu3 *mtu)
/* report disconnect, if we didn't flush EP state */
if (mtu->g.speed != USB_SPEED_UNKNOWN)
mtu3_gadget_disconnect(mtu);
-
- mtu->address = 0;
- mtu->ep0_state = MU3D_EP0_STATE_SETUP;
- mtu->may_wakeup = 0;
- mtu->u1_enable = 0;
- mtu->u2_enable = 0;
- mtu->delayed_status = false;
+ else
+ mtu3_state_reset(mtu);
}
diff --git a/drivers/usb/mtu3/mtu3_gadget_ep0.c b/drivers/usb/mtu3/mtu3_gadget_ep0.c
index ebdcf7a38c29..25216e79cd6e 100644
--- a/drivers/usb/mtu3/mtu3_gadget_ep0.c
+++ b/drivers/usb/mtu3/mtu3_gadget_ep0.c
@@ -7,6 +7,7 @@
* Author: Chunfeng.Yun <chunfeng.yun@mediatek.com>
*/
+#include <linux/iopoll.h>
#include <linux/usb/composite.h>
#include "mtu3.h"
@@ -263,6 +264,7 @@ static int handle_test_mode(struct mtu3 *mtu, struct usb_ctrlrequest *setup)
{
void __iomem *mbase = mtu->mac_base;
int handled = 1;
+ u32 value;
switch (le16_to_cpu(setup->wIndex) >> 8) {
case TEST_J:
@@ -292,6 +294,14 @@ static int handle_test_mode(struct mtu3 *mtu, struct usb_ctrlrequest *setup)
if (mtu->test_mode_nr == TEST_PACKET_MODE)
ep0_load_test_packet(mtu);
+ /* send status before entering test mode. */
+ value = mtu3_readl(mbase, U3D_EP0CSR) & EP0_W1C_BITS;
+ mtu3_writel(mbase, U3D_EP0CSR, value | EP0_SETUPPKTRDY | EP0_DATAEND);
+
+ /* wait for ACK status sent by host */
+ readl_poll_timeout_atomic(mbase + U3D_EP0CSR, value,
+ !(value & EP0_DATAEND), 100, 5000);
+
mtu3_writel(mbase, U3D_USB2_TEST_MODE, mtu->test_mode_nr);
mtu->ep0_state = MU3D_EP0_STATE_SETUP;
@@ -546,7 +556,7 @@ static void ep0_tx_state(struct mtu3 *mtu)
struct usb_request *req;
u32 csr;
u8 *src;
- u8 count;
+ u32 count;
u32 maxp;
dev_dbg(mtu->dev, "%s\n", __func__);
diff --git a/drivers/usb/mtu3/mtu3_hw_regs.h b/drivers/usb/mtu3/mtu3_hw_regs.h
index 6ee371478d89..a45bb253939f 100644
--- a/drivers/usb/mtu3/mtu3_hw_regs.h
+++ b/drivers/usb/mtu3/mtu3_hw_regs.h
@@ -459,6 +459,7 @@
/* U3D_SSUSB_U3_CTRL_0P */
#define SSUSB_U3_PORT_SSP_SPEED BIT(9)
+#define SSUSB_U3_PORT_DUAL_MODE BIT(7)
#define SSUSB_U3_PORT_HOST_SEL BIT(2)
#define SSUSB_U3_PORT_PDN BIT(1)
#define SSUSB_U3_PORT_DIS BIT(0)
diff --git a/drivers/usb/mtu3/mtu3_plat.c b/drivers/usb/mtu3/mtu3_plat.c
index 628d5ce356ca..46551f6d16fd 100644
--- a/drivers/usb/mtu3/mtu3_plat.c
+++ b/drivers/usb/mtu3/mtu3_plat.c
@@ -447,8 +447,7 @@ static int mtu3_remove(struct platform_device *pdev)
*/
static int __maybe_unused mtu3_suspend(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct ssusb_mtk *ssusb = platform_get_drvdata(pdev);
+ struct ssusb_mtk *ssusb = dev_get_drvdata(dev);
dev_dbg(dev, "%s\n", __func__);
@@ -466,8 +465,7 @@ static int __maybe_unused mtu3_suspend(struct device *dev)
static int __maybe_unused mtu3_resume(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct ssusb_mtk *ssusb = platform_get_drvdata(pdev);
+ struct ssusb_mtk *ssusb = dev_get_drvdata(dev);
int ret;
dev_dbg(dev, "%s\n", __func__);
diff --git a/drivers/usb/musb/am35x.c b/drivers/usb/musb/am35x.c
index 0ad664efda6b..660641ab1545 100644
--- a/drivers/usb/musb/am35x.c
+++ b/drivers/usb/musb/am35x.c
@@ -201,7 +201,6 @@ static irqreturn_t am35x_musb_interrupt(int irq, void *hci)
struct device *dev = musb->controller;
struct musb_hdrc_platform_data *plat = dev_get_platdata(dev);
struct omap_musb_board_data *data = plat->board_data;
- struct usb_otg *otg = musb->xceiv->otg;
unsigned long flags;
irqreturn_t ret = IRQ_NONE;
u32 epintr, usbintr;
@@ -264,14 +263,12 @@ static irqreturn_t am35x_musb_interrupt(int irq, void *hci)
WARNING("VBUS error workaround (delay coming)\n");
} else if (drvvbus) {
MUSB_HST_MODE(musb);
- otg->default_a = 1;
musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE;
portstate(musb->port1_status |= USB_PORT_STAT_POWER);
del_timer(&musb->dev_timer);
} else {
musb->is_active = 0;
MUSB_DEV_MODE(musb);
- otg->default_a = 0;
musb->xceiv->otg->state = OTG_STATE_B_IDLE;
portstate(musb->port1_status &= ~USB_PORT_STAT_POWER);
}
diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c
index b8295ce7c4fe..1c023c0091c4 100644
--- a/drivers/usb/musb/da8xx.c
+++ b/drivers/usb/musb/da8xx.c
@@ -223,7 +223,6 @@ static irqreturn_t da8xx_musb_interrupt(int irq, void *hci)
{
struct musb *musb = hci;
void __iomem *reg_base = musb->ctrl_base;
- struct usb_otg *otg = musb->xceiv->otg;
unsigned long flags;
irqreturn_t ret = IRQ_NONE;
u32 status;
@@ -280,7 +279,6 @@ static irqreturn_t da8xx_musb_interrupt(int irq, void *hci)
WARNING("VBUS error workaround (delay coming)\n");
} else if (drvvbus) {
MUSB_HST_MODE(musb);
- otg->default_a = 1;
musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE;
portstate(musb->port1_status |= USB_PORT_STAT_POWER);
del_timer(&musb->dev_timer);
@@ -295,7 +293,6 @@ static irqreturn_t da8xx_musb_interrupt(int irq, void *hci)
*/
musb->is_active = 0;
MUSB_DEV_MODE(musb);
- otg->default_a = 0;
musb->xceiv->otg->state = OTG_STATE_B_IDLE;
portstate(musb->port1_status &= ~USB_PORT_STAT_POWER);
}
diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c
index 2ad39dcd2f4c..fb6bbd254ab7 100644
--- a/drivers/usb/musb/davinci.c
+++ b/drivers/usb/musb/davinci.c
@@ -311,14 +311,12 @@ static irqreturn_t davinci_musb_interrupt(int irq, void *__hci)
WARNING("VBUS error workaround (delay coming)\n");
} else if (drvvbus) {
MUSB_HST_MODE(musb);
- otg->default_a = 1;
musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE;
portstate(musb->port1_status |= USB_PORT_STAT_POWER);
del_timer(&musb->dev_timer);
} else {
musb->is_active = 0;
MUSB_DEV_MODE(musb);
- otg->default_a = 0;
musb->xceiv->otg->state = OTG_STATE_B_IDLE;
portstate(musb->port1_status &= ~USB_PORT_STAT_POWER);
}
@@ -425,6 +423,9 @@ unregister:
static int davinci_musb_exit(struct musb *musb)
{
+ int maxdelay = 30;
+ u8 devctl, warn = 0;
+
del_timer_sync(&musb->dev_timer);
/* force VBUS off */
@@ -438,31 +439,27 @@ static int davinci_musb_exit(struct musb *musb)
davinci_musb_source_power(musb, 0 /*off*/, 1);
- /* delay, to avoid problems with module reload */
- if (musb->xceiv->otg->default_a) {
- int maxdelay = 30;
- u8 devctl, warn = 0;
+ /*
+ * delay, to avoid problems with module reload.
+ * if there's no peripheral connected, this can take a
+ * long time to fall, especially on EVM with huge C133.
+ */
+ do {
+ devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
+ if (!(devctl & MUSB_DEVCTL_VBUS))
+ break;
+ if ((devctl & MUSB_DEVCTL_VBUS) != warn) {
+ warn = devctl & MUSB_DEVCTL_VBUS;
+ dev_dbg(musb->controller, "VBUS %d\n",
+ warn >> MUSB_DEVCTL_VBUS_SHIFT);
+ }
+ msleep(1000);
+ maxdelay--;
+ } while (maxdelay > 0);
- /* if there's no peripheral connected, this can take a
- * long time to fall, especially on EVM with huge C133.
- */
- do {
- devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
- if (!(devctl & MUSB_DEVCTL_VBUS))
- break;
- if ((devctl & MUSB_DEVCTL_VBUS) != warn) {
- warn = devctl & MUSB_DEVCTL_VBUS;
- dev_dbg(musb->controller, "VBUS %d\n",
- warn >> MUSB_DEVCTL_VBUS_SHIFT);
- }
- msleep(1000);
- maxdelay--;
- } while (maxdelay > 0);
-
- /* in OTG mode, another host might be connected */
- if (devctl & MUSB_DEVCTL_VBUS)
- dev_dbg(musb->controller, "VBUS off timeout (devctl %02x)\n", devctl);
- }
+ /* in OTG mode, another host might be connected */
+ if (devctl & MUSB_DEVCTL_VBUS)
+ dev_dbg(musb->controller, "VBUS off timeout (devctl %02x)\n", devctl);
phy_off();
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
index fb5e4523dc28..b7d56272f9d1 100644
--- a/drivers/usb/musb/musb_core.c
+++ b/drivers/usb/musb/musb_core.c
@@ -274,20 +274,6 @@ static void musb_default_writew(void __iomem *addr, unsigned offset, u16 data)
__raw_writew(data, addr + offset);
}
-static u32 musb_default_readl(const void __iomem *addr, unsigned offset)
-{
- u32 data = __raw_readl(addr + offset);
-
- trace_musb_readl(__builtin_return_address(0), addr, offset, data);
- return data;
-}
-
-static void musb_default_writel(void __iomem *addr, unsigned offset, u32 data)
-{
- trace_musb_writel(__builtin_return_address(0), addr, offset, data);
- __raw_writel(data, addr + offset);
-}
-
/*
* Load an endpoint's FIFO
*/
@@ -390,10 +376,20 @@ EXPORT_SYMBOL_GPL(musb_readw);
void (*musb_writew)(void __iomem *addr, unsigned offset, u16 data);
EXPORT_SYMBOL_GPL(musb_writew);
-u32 (*musb_readl)(const void __iomem *addr, unsigned offset);
+u32 musb_readl(const void __iomem *addr, unsigned offset)
+{
+ u32 data = __raw_readl(addr + offset);
+
+ trace_musb_readl(__builtin_return_address(0), addr, offset, data);
+ return data;
+}
EXPORT_SYMBOL_GPL(musb_readl);
-void (*musb_writel)(void __iomem *addr, unsigned offset, u32 data);
+void musb_writel(void __iomem *addr, unsigned offset, u32 data)
+{
+ trace_musb_writel(__builtin_return_address(0), addr, offset, data);
+ __raw_writel(data, addr + offset);
+}
EXPORT_SYMBOL_GPL(musb_writel);
#ifndef CONFIG_MUSB_PIO_ONLY
@@ -527,6 +523,383 @@ void musb_hnp_stop(struct musb *musb)
static void musb_recover_from_babble(struct musb *musb);
+static void musb_handle_intr_resume(struct musb *musb, u8 devctl)
+{
+ musb_dbg(musb, "RESUME (%s)",
+ usb_otg_state_string(musb->xceiv->otg->state));
+
+ if (devctl & MUSB_DEVCTL_HM) {
+ switch (musb->xceiv->otg->state) {
+ case OTG_STATE_A_SUSPEND:
+ /* remote wakeup? */
+ musb->port1_status |=
+ (USB_PORT_STAT_C_SUSPEND << 16)
+ | MUSB_PORT_STAT_RESUME;
+ musb->rh_timer = jiffies
+ + msecs_to_jiffies(USB_RESUME_TIMEOUT);
+ musb->xceiv->otg->state = OTG_STATE_A_HOST;
+ musb->is_active = 1;
+ musb_host_resume_root_hub(musb);
+ schedule_delayed_work(&musb->finish_resume_work,
+ msecs_to_jiffies(USB_RESUME_TIMEOUT));
+ break;
+ case OTG_STATE_B_WAIT_ACON:
+ musb->xceiv->otg->state = OTG_STATE_B_PERIPHERAL;
+ musb->is_active = 1;
+ MUSB_DEV_MODE(musb);
+ break;
+ default:
+ WARNING("bogus %s RESUME (%s)\n",
+ "host",
+ usb_otg_state_string(musb->xceiv->otg->state));
+ }
+ } else {
+ switch (musb->xceiv->otg->state) {
+ case OTG_STATE_A_SUSPEND:
+ /* possibly DISCONNECT is upcoming */
+ musb->xceiv->otg->state = OTG_STATE_A_HOST;
+ musb_host_resume_root_hub(musb);
+ break;
+ case OTG_STATE_B_WAIT_ACON:
+ case OTG_STATE_B_PERIPHERAL:
+ /* disconnect while suspended? we may
+ * not get a disconnect irq...
+ */
+ if ((devctl & MUSB_DEVCTL_VBUS)
+ != (3 << MUSB_DEVCTL_VBUS_SHIFT)
+ ) {
+ musb->int_usb |= MUSB_INTR_DISCONNECT;
+ musb->int_usb &= ~MUSB_INTR_SUSPEND;
+ break;
+ }
+ musb_g_resume(musb);
+ break;
+ case OTG_STATE_B_IDLE:
+ musb->int_usb &= ~MUSB_INTR_SUSPEND;
+ break;
+ default:
+ WARNING("bogus %s RESUME (%s)\n",
+ "peripheral",
+ usb_otg_state_string(musb->xceiv->otg->state));
+ }
+ }
+}
+
+/* return IRQ_HANDLED to tell the caller to return immediately */
+static irqreturn_t musb_handle_intr_sessreq(struct musb *musb, u8 devctl)
+{
+ void __iomem *mbase = musb->mregs;
+
+ if ((devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS
+ && (devctl & MUSB_DEVCTL_BDEVICE)) {
+ musb_dbg(musb, "SessReq while on B state");
+ return IRQ_HANDLED;
+ }
+
+ musb_dbg(musb, "SESSION_REQUEST (%s)",
+ usb_otg_state_string(musb->xceiv->otg->state));
+
+ /* IRQ arrives from ID pin sense or (later, if VBUS power
+ * is removed) SRP. responses are time critical:
+ * - turn on VBUS (with silicon-specific mechanism)
+ * - go through A_WAIT_VRISE
+ * - ... to A_WAIT_BCON.
+ * a_wait_vrise_tmout triggers VBUS_ERROR transitions
+ */
+ musb_writeb(mbase, MUSB_DEVCTL, MUSB_DEVCTL_SESSION);
+ musb->ep0_stage = MUSB_EP0_START;
+ musb->xceiv->otg->state = OTG_STATE_A_IDLE;
+ MUSB_HST_MODE(musb);
+ musb_platform_set_vbus(musb, 1);
+
+ return IRQ_NONE;
+}
+
+static void musb_handle_intr_vbuserr(struct musb *musb, u8 devctl)
+{
+ int ignore = 0;
+
+ /* During connection as an A-Device, we may see a short
+ * current spikes causing voltage drop, because of cable
+ * and peripheral capacitance combined with vbus draw.
+ * (So: less common with truly self-powered devices, where
+ * vbus doesn't act like a power supply.)
+ *
+ * Such spikes are short; usually less than ~500 usec, max
+ * of ~2 msec. That is, they're not sustained overcurrent
+ * errors, though they're reported using VBUSERROR irqs.
+ *
+ * Workarounds: (a) hardware: use self powered devices.
+ * (b) software: ignore non-repeated VBUS errors.
+ *
+ * REVISIT: do delays from lots of DEBUG_KERNEL checks
+ * make trouble here, keeping VBUS < 4.4V ?
+ */
+ switch (musb->xceiv->otg->state) {
+ case OTG_STATE_A_HOST:
+ /* recovery is dicey once we've gotten past the
+ * initial stages of enumeration, but if VBUS
+ * stayed ok at the other end of the link, and
+ * another reset is due (at least for high speed,
+ * to redo the chirp etc), it might work OK...
+ */
+ case OTG_STATE_A_WAIT_BCON:
+ case OTG_STATE_A_WAIT_VRISE:
+ if (musb->vbuserr_retry) {
+ void __iomem *mbase = musb->mregs;
+
+ musb->vbuserr_retry--;
+ ignore = 1;
+ devctl |= MUSB_DEVCTL_SESSION;
+ musb_writeb(mbase, MUSB_DEVCTL, devctl);
+ } else {
+ musb->port1_status |=
+ USB_PORT_STAT_OVERCURRENT
+ | (USB_PORT_STAT_C_OVERCURRENT << 16);
+ }
+ break;
+ default:
+ break;
+ }
+
+ dev_printk(ignore ? KERN_DEBUG : KERN_ERR, musb->controller,
+ "VBUS_ERROR in %s (%02x, %s), retry #%d, port1 %08x\n",
+ usb_otg_state_string(musb->xceiv->otg->state),
+ devctl,
+ ({ char *s;
+ switch (devctl & MUSB_DEVCTL_VBUS) {
+ case 0 << MUSB_DEVCTL_VBUS_SHIFT:
+ s = "<SessEnd"; break;
+ case 1 << MUSB_DEVCTL_VBUS_SHIFT:
+ s = "<AValid"; break;
+ case 2 << MUSB_DEVCTL_VBUS_SHIFT:
+ s = "<VBusValid"; break;
+ /* case 3 << MUSB_DEVCTL_VBUS_SHIFT: */
+ default:
+ s = "VALID"; break;
+ } s; }),
+ VBUSERR_RETRY_COUNT - musb->vbuserr_retry,
+ musb->port1_status);
+
+ /* go through A_WAIT_VFALL then start a new session */
+ if (!ignore)
+ musb_platform_set_vbus(musb, 0);
+}
+
+static void musb_handle_intr_suspend(struct musb *musb, u8 devctl)
+{
+ musb_dbg(musb, "SUSPEND (%s) devctl %02x",
+ usb_otg_state_string(musb->xceiv->otg->state), devctl);
+
+ switch (musb->xceiv->otg->state) {
+ case OTG_STATE_A_PERIPHERAL:
+ /* We also come here if the cable is removed, since
+ * this silicon doesn't report ID-no-longer-grounded.
+ *
+ * We depend on T(a_wait_bcon) to shut us down, and
+ * hope users don't do anything dicey during this
+ * undesired detour through A_WAIT_BCON.
+ */
+ musb_hnp_stop(musb);
+ musb_host_resume_root_hub(musb);
+ musb_root_disconnect(musb);
+ musb_platform_try_idle(musb, jiffies
+ + msecs_to_jiffies(musb->a_wait_bcon
+ ? : OTG_TIME_A_WAIT_BCON));
+
+ break;
+ case OTG_STATE_B_IDLE:
+ if (!musb->is_active)
+ break;
+ /* fall through */
+ case OTG_STATE_B_PERIPHERAL:
+ musb_g_suspend(musb);
+ musb->is_active = musb->g.b_hnp_enable;
+ if (musb->is_active) {
+ musb->xceiv->otg->state = OTG_STATE_B_WAIT_ACON;
+ musb_dbg(musb, "HNP: Setting timer for b_ase0_brst");
+ mod_timer(&musb->otg_timer, jiffies
+ + msecs_to_jiffies(
+ OTG_TIME_B_ASE0_BRST));
+ }
+ break;
+ case OTG_STATE_A_WAIT_BCON:
+ if (musb->a_wait_bcon != 0)
+ musb_platform_try_idle(musb, jiffies
+ + msecs_to_jiffies(musb->a_wait_bcon));
+ break;
+ case OTG_STATE_A_HOST:
+ musb->xceiv->otg->state = OTG_STATE_A_SUSPEND;
+ musb->is_active = musb->hcd->self.b_hnp_enable;
+ break;
+ case OTG_STATE_B_HOST:
+ /* Transition to B_PERIPHERAL, see 6.8.2.6 p 44 */
+ musb_dbg(musb, "REVISIT: SUSPEND as B_HOST");
+ break;
+ default:
+ /* "should not happen" */
+ musb->is_active = 0;
+ break;
+ }
+}
+
+static void musb_handle_intr_connect(struct musb *musb, u8 devctl, u8 int_usb)
+{
+ struct usb_hcd *hcd = musb->hcd;
+
+ musb->is_active = 1;
+ musb->ep0_stage = MUSB_EP0_START;
+
+ musb->intrtxe = musb->epmask;
+ musb_writew(musb->mregs, MUSB_INTRTXE, musb->intrtxe);
+ musb->intrrxe = musb->epmask & 0xfffe;
+ musb_writew(musb->mregs, MUSB_INTRRXE, musb->intrrxe);
+ musb_writeb(musb->mregs, MUSB_INTRUSBE, 0xf7);
+ musb->port1_status &= ~(USB_PORT_STAT_LOW_SPEED
+ |USB_PORT_STAT_HIGH_SPEED
+ |USB_PORT_STAT_ENABLE
+ );
+ musb->port1_status |= USB_PORT_STAT_CONNECTION
+ |(USB_PORT_STAT_C_CONNECTION << 16);
+
+ /* high vs full speed is just a guess until after reset */
+ if (devctl & MUSB_DEVCTL_LSDEV)
+ musb->port1_status |= USB_PORT_STAT_LOW_SPEED;
+
+ /* indicate new connection to OTG machine */
+ switch (musb->xceiv->otg->state) {
+ case OTG_STATE_B_PERIPHERAL:
+ if (int_usb & MUSB_INTR_SUSPEND) {
+ musb_dbg(musb, "HNP: SUSPEND+CONNECT, now b_host");
+ int_usb &= ~MUSB_INTR_SUSPEND;
+ goto b_host;
+ } else
+ musb_dbg(musb, "CONNECT as b_peripheral???");
+ break;
+ case OTG_STATE_B_WAIT_ACON:
+ musb_dbg(musb, "HNP: CONNECT, now b_host");
+b_host:
+ musb->xceiv->otg->state = OTG_STATE_B_HOST;
+ if (musb->hcd)
+ musb->hcd->self.is_b_host = 1;
+ del_timer(&musb->otg_timer);
+ break;
+ default:
+ if ((devctl & MUSB_DEVCTL_VBUS)
+ == (3 << MUSB_DEVCTL_VBUS_SHIFT)) {
+ musb->xceiv->otg->state = OTG_STATE_A_HOST;
+ if (hcd)
+ hcd->self.is_b_host = 0;
+ }
+ break;
+ }
+
+ musb_host_poke_root_hub(musb);
+
+ musb_dbg(musb, "CONNECT (%s) devctl %02x",
+ usb_otg_state_string(musb->xceiv->otg->state), devctl);
+}
+
+static void musb_handle_intr_disconnect(struct musb *musb, u8 devctl)
+{
+ musb_dbg(musb, "DISCONNECT (%s) as %s, devctl %02x",
+ usb_otg_state_string(musb->xceiv->otg->state),
+ MUSB_MODE(musb), devctl);
+
+ switch (musb->xceiv->otg->state) {
+ case OTG_STATE_A_HOST:
+ case OTG_STATE_A_SUSPEND:
+ musb_host_resume_root_hub(musb);
+ musb_root_disconnect(musb);
+ if (musb->a_wait_bcon != 0)
+ musb_platform_try_idle(musb, jiffies
+ + msecs_to_jiffies(musb->a_wait_bcon));
+ break;
+ case OTG_STATE_B_HOST:
+ /* REVISIT this behaves for "real disconnect"
+ * cases; make sure the other transitions from
+ * from B_HOST act right too. The B_HOST code
+ * in hnp_stop() is currently not used...
+ */
+ musb_root_disconnect(musb);
+ if (musb->hcd)
+ musb->hcd->self.is_b_host = 0;
+ musb->xceiv->otg->state = OTG_STATE_B_PERIPHERAL;
+ MUSB_DEV_MODE(musb);
+ musb_g_disconnect(musb);
+ break;
+ case OTG_STATE_A_PERIPHERAL:
+ musb_hnp_stop(musb);
+ musb_root_disconnect(musb);
+ /* FALLTHROUGH */
+ case OTG_STATE_B_WAIT_ACON:
+ /* FALLTHROUGH */
+ case OTG_STATE_B_PERIPHERAL:
+ case OTG_STATE_B_IDLE:
+ musb_g_disconnect(musb);
+ break;
+ default:
+ WARNING("unhandled DISCONNECT transition (%s)\n",
+ usb_otg_state_string(musb->xceiv->otg->state));
+ break;
+ }
+}
+
+/*
+ * mentor saves a bit: bus reset and babble share the same irq.
+ * only host sees babble; only peripheral sees bus reset.
+ */
+static void musb_handle_intr_reset(struct musb *musb)
+{
+ if (is_host_active(musb)) {
+ /*
+ * When BABBLE happens what we can depends on which
+ * platform MUSB is running, because some platforms
+ * implemented proprietary means for 'recovering' from
+ * Babble conditions. One such platform is AM335x. In
+ * most cases, however, the only thing we can do is
+ * drop the session.
+ */
+ dev_err(musb->controller, "Babble\n");
+ musb_recover_from_babble(musb);
+ } else {
+ musb_dbg(musb, "BUS RESET as %s",
+ usb_otg_state_string(musb->xceiv->otg->state));
+ switch (musb->xceiv->otg->state) {
+ case OTG_STATE_A_SUSPEND:
+ musb_g_reset(musb);
+ /* FALLTHROUGH */
+ case OTG_STATE_A_WAIT_BCON: /* OPT TD.4.7-900ms */
+ /* never use invalid T(a_wait_bcon) */
+ musb_dbg(musb, "HNP: in %s, %d msec timeout",
+ usb_otg_state_string(musb->xceiv->otg->state),
+ TA_WAIT_BCON(musb));
+ mod_timer(&musb->otg_timer, jiffies
+ + msecs_to_jiffies(TA_WAIT_BCON(musb)));
+ break;
+ case OTG_STATE_A_PERIPHERAL:
+ del_timer(&musb->otg_timer);
+ musb_g_reset(musb);
+ break;
+ case OTG_STATE_B_WAIT_ACON:
+ musb_dbg(musb, "HNP: RESET (%s), to b_peripheral",
+ usb_otg_state_string(musb->xceiv->otg->state));
+ musb->xceiv->otg->state = OTG_STATE_B_PERIPHERAL;
+ musb_g_reset(musb);
+ break;
+ case OTG_STATE_B_IDLE:
+ musb->xceiv->otg->state = OTG_STATE_B_PERIPHERAL;
+ /* FALLTHROUGH */
+ case OTG_STATE_B_PERIPHERAL:
+ musb_g_reset(musb);
+ break;
+ default:
+ musb_dbg(musb, "Unhandled BUS RESET as %s",
+ usb_otg_state_string(musb->xceiv->otg->state));
+ }
+ }
+}
+
/*
* Interrupt Service Routine to record USB "global" interrupts.
* Since these do not happen often and signify things of
@@ -551,379 +924,40 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
* spurious RESUME irqs happen too, paired with SUSPEND.
*/
if (int_usb & MUSB_INTR_RESUME) {
+ musb_handle_intr_resume(musb, devctl);
handled = IRQ_HANDLED;
- musb_dbg(musb, "RESUME (%s)",
- usb_otg_state_string(musb->xceiv->otg->state));
-
- if (devctl & MUSB_DEVCTL_HM) {
- switch (musb->xceiv->otg->state) {
- case OTG_STATE_A_SUSPEND:
- /* remote wakeup? */
- musb->port1_status |=
- (USB_PORT_STAT_C_SUSPEND << 16)
- | MUSB_PORT_STAT_RESUME;
- musb->rh_timer = jiffies
- + msecs_to_jiffies(USB_RESUME_TIMEOUT);
- musb->xceiv->otg->state = OTG_STATE_A_HOST;
- musb->is_active = 1;
- musb_host_resume_root_hub(musb);
- schedule_delayed_work(&musb->finish_resume_work,
- msecs_to_jiffies(USB_RESUME_TIMEOUT));
- break;
- case OTG_STATE_B_WAIT_ACON:
- musb->xceiv->otg->state = OTG_STATE_B_PERIPHERAL;
- musb->is_active = 1;
- MUSB_DEV_MODE(musb);
- break;
- default:
- WARNING("bogus %s RESUME (%s)\n",
- "host",
- usb_otg_state_string(musb->xceiv->otg->state));
- }
- } else {
- switch (musb->xceiv->otg->state) {
- case OTG_STATE_A_SUSPEND:
- /* possibly DISCONNECT is upcoming */
- musb->xceiv->otg->state = OTG_STATE_A_HOST;
- musb_host_resume_root_hub(musb);
- break;
- case OTG_STATE_B_WAIT_ACON:
- case OTG_STATE_B_PERIPHERAL:
- /* disconnect while suspended? we may
- * not get a disconnect irq...
- */
- if ((devctl & MUSB_DEVCTL_VBUS)
- != (3 << MUSB_DEVCTL_VBUS_SHIFT)
- ) {
- musb->int_usb |= MUSB_INTR_DISCONNECT;
- musb->int_usb &= ~MUSB_INTR_SUSPEND;
- break;
- }
- musb_g_resume(musb);
- break;
- case OTG_STATE_B_IDLE:
- musb->int_usb &= ~MUSB_INTR_SUSPEND;
- break;
- default:
- WARNING("bogus %s RESUME (%s)\n",
- "peripheral",
- usb_otg_state_string(musb->xceiv->otg->state));
- }
- }
}
/* see manual for the order of the tests */
if (int_usb & MUSB_INTR_SESSREQ) {
- void __iomem *mbase = musb->mregs;
-
- if ((devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS
- && (devctl & MUSB_DEVCTL_BDEVICE)) {
- musb_dbg(musb, "SessReq while on B state");
+ if (musb_handle_intr_sessreq(musb, devctl))
return IRQ_HANDLED;
- }
-
- musb_dbg(musb, "SESSION_REQUEST (%s)",
- usb_otg_state_string(musb->xceiv->otg->state));
-
- /* IRQ arrives from ID pin sense or (later, if VBUS power
- * is removed) SRP. responses are time critical:
- * - turn on VBUS (with silicon-specific mechanism)
- * - go through A_WAIT_VRISE
- * - ... to A_WAIT_BCON.
- * a_wait_vrise_tmout triggers VBUS_ERROR transitions
- */
- musb_writeb(mbase, MUSB_DEVCTL, MUSB_DEVCTL_SESSION);
- musb->ep0_stage = MUSB_EP0_START;
- musb->xceiv->otg->state = OTG_STATE_A_IDLE;
- MUSB_HST_MODE(musb);
- musb_platform_set_vbus(musb, 1);
-
handled = IRQ_HANDLED;
}
if (int_usb & MUSB_INTR_VBUSERROR) {
- int ignore = 0;
-
- /* During connection as an A-Device, we may see a short
- * current spikes causing voltage drop, because of cable
- * and peripheral capacitance combined with vbus draw.
- * (So: less common with truly self-powered devices, where
- * vbus doesn't act like a power supply.)
- *
- * Such spikes are short; usually less than ~500 usec, max
- * of ~2 msec. That is, they're not sustained overcurrent
- * errors, though they're reported using VBUSERROR irqs.
- *
- * Workarounds: (a) hardware: use self powered devices.
- * (b) software: ignore non-repeated VBUS errors.
- *
- * REVISIT: do delays from lots of DEBUG_KERNEL checks
- * make trouble here, keeping VBUS < 4.4V ?
- */
- switch (musb->xceiv->otg->state) {
- case OTG_STATE_A_HOST:
- /* recovery is dicey once we've gotten past the
- * initial stages of enumeration, but if VBUS
- * stayed ok at the other end of the link, and
- * another reset is due (at least for high speed,
- * to redo the chirp etc), it might work OK...
- */
- case OTG_STATE_A_WAIT_BCON:
- case OTG_STATE_A_WAIT_VRISE:
- if (musb->vbuserr_retry) {
- void __iomem *mbase = musb->mregs;
-
- musb->vbuserr_retry--;
- ignore = 1;
- devctl |= MUSB_DEVCTL_SESSION;
- musb_writeb(mbase, MUSB_DEVCTL, devctl);
- } else {
- musb->port1_status |=
- USB_PORT_STAT_OVERCURRENT
- | (USB_PORT_STAT_C_OVERCURRENT << 16);
- }
- break;
- default:
- break;
- }
-
- dev_printk(ignore ? KERN_DEBUG : KERN_ERR, musb->controller,
- "VBUS_ERROR in %s (%02x, %s), retry #%d, port1 %08x\n",
- usb_otg_state_string(musb->xceiv->otg->state),
- devctl,
- ({ char *s;
- switch (devctl & MUSB_DEVCTL_VBUS) {
- case 0 << MUSB_DEVCTL_VBUS_SHIFT:
- s = "<SessEnd"; break;
- case 1 << MUSB_DEVCTL_VBUS_SHIFT:
- s = "<AValid"; break;
- case 2 << MUSB_DEVCTL_VBUS_SHIFT:
- s = "<VBusValid"; break;
- /* case 3 << MUSB_DEVCTL_VBUS_SHIFT: */
- default:
- s = "VALID"; break;
- } s; }),
- VBUSERR_RETRY_COUNT - musb->vbuserr_retry,
- musb->port1_status);
-
- /* go through A_WAIT_VFALL then start a new session */
- if (!ignore)
- musb_platform_set_vbus(musb, 0);
+ musb_handle_intr_vbuserr(musb, devctl);
handled = IRQ_HANDLED;
}
if (int_usb & MUSB_INTR_SUSPEND) {
- musb_dbg(musb, "SUSPEND (%s) devctl %02x",
- usb_otg_state_string(musb->xceiv->otg->state), devctl);
+ musb_handle_intr_suspend(musb, devctl);
handled = IRQ_HANDLED;
-
- switch (musb->xceiv->otg->state) {
- case OTG_STATE_A_PERIPHERAL:
- /* We also come here if the cable is removed, since
- * this silicon doesn't report ID-no-longer-grounded.
- *
- * We depend on T(a_wait_bcon) to shut us down, and
- * hope users don't do anything dicey during this
- * undesired detour through A_WAIT_BCON.
- */
- musb_hnp_stop(musb);
- musb_host_resume_root_hub(musb);
- musb_root_disconnect(musb);
- musb_platform_try_idle(musb, jiffies
- + msecs_to_jiffies(musb->a_wait_bcon
- ? : OTG_TIME_A_WAIT_BCON));
-
- break;
- case OTG_STATE_B_IDLE:
- if (!musb->is_active)
- break;
- /* fall through */
- case OTG_STATE_B_PERIPHERAL:
- musb_g_suspend(musb);
- musb->is_active = musb->g.b_hnp_enable;
- if (musb->is_active) {
- musb->xceiv->otg->state = OTG_STATE_B_WAIT_ACON;
- musb_dbg(musb, "HNP: Setting timer for b_ase0_brst");
- mod_timer(&musb->otg_timer, jiffies
- + msecs_to_jiffies(
- OTG_TIME_B_ASE0_BRST));
- }
- break;
- case OTG_STATE_A_WAIT_BCON:
- if (musb->a_wait_bcon != 0)
- musb_platform_try_idle(musb, jiffies
- + msecs_to_jiffies(musb->a_wait_bcon));
- break;
- case OTG_STATE_A_HOST:
- musb->xceiv->otg->state = OTG_STATE_A_SUSPEND;
- musb->is_active = musb->hcd->self.b_hnp_enable;
- break;
- case OTG_STATE_B_HOST:
- /* Transition to B_PERIPHERAL, see 6.8.2.6 p 44 */
- musb_dbg(musb, "REVISIT: SUSPEND as B_HOST");
- break;
- default:
- /* "should not happen" */
- musb->is_active = 0;
- break;
- }
}
if (int_usb & MUSB_INTR_CONNECT) {
- struct usb_hcd *hcd = musb->hcd;
-
+ musb_handle_intr_connect(musb, devctl, int_usb);
handled = IRQ_HANDLED;
- musb->is_active = 1;
-
- musb->ep0_stage = MUSB_EP0_START;
-
- musb->intrtxe = musb->epmask;
- musb_writew(musb->mregs, MUSB_INTRTXE, musb->intrtxe);
- musb->intrrxe = musb->epmask & 0xfffe;
- musb_writew(musb->mregs, MUSB_INTRRXE, musb->intrrxe);
- musb_writeb(musb->mregs, MUSB_INTRUSBE, 0xf7);
- musb->port1_status &= ~(USB_PORT_STAT_LOW_SPEED
- |USB_PORT_STAT_HIGH_SPEED
- |USB_PORT_STAT_ENABLE
- );
- musb->port1_status |= USB_PORT_STAT_CONNECTION
- |(USB_PORT_STAT_C_CONNECTION << 16);
-
- /* high vs full speed is just a guess until after reset */
- if (devctl & MUSB_DEVCTL_LSDEV)
- musb->port1_status |= USB_PORT_STAT_LOW_SPEED;
-
- /* indicate new connection to OTG machine */
- switch (musb->xceiv->otg->state) {
- case OTG_STATE_B_PERIPHERAL:
- if (int_usb & MUSB_INTR_SUSPEND) {
- musb_dbg(musb, "HNP: SUSPEND+CONNECT, now b_host");
- int_usb &= ~MUSB_INTR_SUSPEND;
- goto b_host;
- } else
- musb_dbg(musb, "CONNECT as b_peripheral???");
- break;
- case OTG_STATE_B_WAIT_ACON:
- musb_dbg(musb, "HNP: CONNECT, now b_host");
-b_host:
- musb->xceiv->otg->state = OTG_STATE_B_HOST;
- if (musb->hcd)
- musb->hcd->self.is_b_host = 1;
- del_timer(&musb->otg_timer);
- break;
- default:
- if ((devctl & MUSB_DEVCTL_VBUS)
- == (3 << MUSB_DEVCTL_VBUS_SHIFT)) {
- musb->xceiv->otg->state = OTG_STATE_A_HOST;
- if (hcd)
- hcd->self.is_b_host = 0;
- }
- break;
- }
-
- musb_host_poke_root_hub(musb);
-
- musb_dbg(musb, "CONNECT (%s) devctl %02x",
- usb_otg_state_string(musb->xceiv->otg->state), devctl);
}
if (int_usb & MUSB_INTR_DISCONNECT) {
- musb_dbg(musb, "DISCONNECT (%s) as %s, devctl %02x",
- usb_otg_state_string(musb->xceiv->otg->state),
- MUSB_MODE(musb), devctl);
+ musb_handle_intr_disconnect(musb, devctl);
handled = IRQ_HANDLED;
-
- switch (musb->xceiv->otg->state) {
- case OTG_STATE_A_HOST:
- case OTG_STATE_A_SUSPEND:
- musb_host_resume_root_hub(musb);
- musb_root_disconnect(musb);
- if (musb->a_wait_bcon != 0)
- musb_platform_try_idle(musb, jiffies
- + msecs_to_jiffies(musb->a_wait_bcon));
- break;
- case OTG_STATE_B_HOST:
- /* REVISIT this behaves for "real disconnect"
- * cases; make sure the other transitions from
- * from B_HOST act right too. The B_HOST code
- * in hnp_stop() is currently not used...
- */
- musb_root_disconnect(musb);
- if (musb->hcd)
- musb->hcd->self.is_b_host = 0;
- musb->xceiv->otg->state = OTG_STATE_B_PERIPHERAL;
- MUSB_DEV_MODE(musb);
- musb_g_disconnect(musb);
- break;
- case OTG_STATE_A_PERIPHERAL:
- musb_hnp_stop(musb);
- musb_root_disconnect(musb);
- /* FALLTHROUGH */
- case OTG_STATE_B_WAIT_ACON:
- /* FALLTHROUGH */
- case OTG_STATE_B_PERIPHERAL:
- case OTG_STATE_B_IDLE:
- musb_g_disconnect(musb);
- break;
- default:
- WARNING("unhandled DISCONNECT transition (%s)\n",
- usb_otg_state_string(musb->xceiv->otg->state));
- break;
- }
}
- /* mentor saves a bit: bus reset and babble share the same irq.
- * only host sees babble; only peripheral sees bus reset.
- */
if (int_usb & MUSB_INTR_RESET) {
+ musb_handle_intr_reset(musb);
handled = IRQ_HANDLED;
- if (is_host_active(musb)) {
- /*
- * When BABBLE happens what we can depends on which
- * platform MUSB is running, because some platforms
- * implemented proprietary means for 'recovering' from
- * Babble conditions. One such platform is AM335x. In
- * most cases, however, the only thing we can do is
- * drop the session.
- */
- dev_err(musb->controller, "Babble\n");
- musb_recover_from_babble(musb);
- } else {
- musb_dbg(musb, "BUS RESET as %s",
- usb_otg_state_string(musb->xceiv->otg->state));
- switch (musb->xceiv->otg->state) {
- case OTG_STATE_A_SUSPEND:
- musb_g_reset(musb);
- /* FALLTHROUGH */
- case OTG_STATE_A_WAIT_BCON: /* OPT TD.4.7-900ms */
- /* never use invalid T(a_wait_bcon) */
- musb_dbg(musb, "HNP: in %s, %d msec timeout",
- usb_otg_state_string(musb->xceiv->otg->state),
- TA_WAIT_BCON(musb));
- mod_timer(&musb->otg_timer, jiffies
- + msecs_to_jiffies(TA_WAIT_BCON(musb)));
- break;
- case OTG_STATE_A_PERIPHERAL:
- del_timer(&musb->otg_timer);
- musb_g_reset(musb);
- break;
- case OTG_STATE_B_WAIT_ACON:
- musb_dbg(musb, "HNP: RESET (%s), to b_peripheral",
- usb_otg_state_string(musb->xceiv->otg->state));
- musb->xceiv->otg->state = OTG_STATE_B_PERIPHERAL;
- musb_g_reset(musb);
- break;
- case OTG_STATE_B_IDLE:
- musb->xceiv->otg->state = OTG_STATE_B_PERIPHERAL;
- /* FALLTHROUGH */
- case OTG_STATE_B_PERIPHERAL:
- musb_g_reset(musb);
- break;
- default:
- musb_dbg(musb, "Unhandled BUS RESET as %s",
- usb_otg_state_string(musb->xceiv->otg->state));
- }
- }
}
#if 0
@@ -1042,7 +1076,7 @@ void musb_start(struct musb *musb)
* (b) vbus present/connect IRQ, peripheral mode;
* (c) peripheral initiates, using SRP
*/
- if (musb->port_mode != MUSB_PORT_MODE_HOST &&
+ if (musb->port_mode != MUSB_HOST &&
musb->xceiv->otg->state != OTG_STATE_A_WAIT_BCON &&
(devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS) {
musb->is_active = 1;
@@ -1244,25 +1278,25 @@ fifo_setup(struct musb *musb, struct musb_hw_ep *hw_ep,
/* REVISIT error check: be sure ep0 can both rx and tx ... */
switch (cfg->style) {
case FIFO_TX:
- musb_write_txfifosz(mbase, c_size);
- musb_write_txfifoadd(mbase, c_off);
+ musb_writeb(mbase, MUSB_TXFIFOSZ, c_size);
+ musb_writew(mbase, MUSB_TXFIFOADD, c_off);
hw_ep->tx_double_buffered = !!(c_size & MUSB_FIFOSZ_DPB);
hw_ep->max_packet_sz_tx = maxpacket;
break;
case FIFO_RX:
- musb_write_rxfifosz(mbase, c_size);
- musb_write_rxfifoadd(mbase, c_off);
+ musb_writeb(mbase, MUSB_RXFIFOSZ, c_size);
+ musb_writew(mbase, MUSB_RXFIFOADD, c_off);
hw_ep->rx_double_buffered = !!(c_size & MUSB_FIFOSZ_DPB);
hw_ep->max_packet_sz_rx = maxpacket;
break;
case FIFO_RXTX:
- musb_write_txfifosz(mbase, c_size);
- musb_write_txfifoadd(mbase, c_off);
+ musb_writeb(mbase, MUSB_TXFIFOSZ, c_size);
+ musb_writew(mbase, MUSB_TXFIFOADD, c_off);
hw_ep->rx_double_buffered = !!(c_size & MUSB_FIFOSZ_DPB);
hw_ep->max_packet_sz_rx = maxpacket;
- musb_write_rxfifosz(mbase, c_size);
- musb_write_rxfifoadd(mbase, c_off);
+ musb_writeb(mbase, MUSB_RXFIFOSZ, c_size);
+ musb_writew(mbase, MUSB_RXFIFOADD, c_off);
hw_ep->tx_double_buffered = hw_ep->rx_double_buffered;
hw_ep->max_packet_sz_tx = maxpacket;
@@ -1470,7 +1504,7 @@ static int musb_core_init(u16 musb_type, struct musb *musb)
}
/* log release info */
- musb->hwvers = musb_read_hwvers(mbase);
+ musb->hwvers = musb_readw(mbase, MUSB_HWVERS);
pr_debug("%s: %sHDRC RTL version %d.%d%s\n",
musb_driver_name, type, MUSB_HWVERS_MAJOR(musb->hwvers),
MUSB_HWVERS_MINOR(musb->hwvers),
@@ -1497,7 +1531,7 @@ static int musb_core_init(u16 musb_type, struct musb *musb)
hw_ep->fifo = musb->io.fifo_offset(i) + mbase;
#if IS_ENABLED(CONFIG_USB_MUSB_TUSB6010)
- if (musb->io.quirks & MUSB_IN_TUSB) {
+ if (musb->ops->quirks & MUSB_IN_TUSB) {
hw_ep->fifo_async = musb->async + 0x400 +
musb->io.fifo_offset(i);
hw_ep->fifo_sync = musb->sync + 0x400 +
@@ -2158,8 +2192,6 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
musb_writeb = musb_default_writeb;
musb_readw = musb_default_readw;
musb_writew = musb_default_writew;
- musb_readl = musb_default_readl;
- musb_writel = musb_default_writel;
/* The musb_platform_init() call:
* - adjusts musb->mregs
@@ -2182,11 +2214,9 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
goto fail2;
}
- if (musb->ops->quirks)
- musb->io.quirks = musb->ops->quirks;
/* Most devices use indexed offset or flat offset */
- if (musb->io.quirks & MUSB_INDEXED_EP) {
+ if (musb->ops->quirks & MUSB_INDEXED_EP) {
musb->io.ep_offset = musb_indexed_ep_offset;
musb->io.ep_select = musb_indexed_ep_select;
} else {
@@ -2194,7 +2224,7 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
musb->io.ep_select = musb_flat_ep_select;
}
- if (musb->io.quirks & MUSB_G_NO_SKB_RESERVE)
+ if (musb->ops->quirks & MUSB_G_NO_SKB_RESERVE)
musb->g.quirk_avoids_skb_reserve = 1;
/* At least tusb6010 has its own offsets */
@@ -2226,10 +2256,6 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
musb_readw = musb->ops->readw;
if (musb->ops->writew)
musb_writew = musb->ops->writew;
- if (musb->ops->readl)
- musb_readl = musb->ops->readl;
- if (musb->ops->writel)
- musb_writel = musb->ops->writel;
#ifndef CONFIG_MUSB_PIO_ONLY
if (!musb->ops->dma_init || !musb->ops->dma_exit) {
@@ -2321,33 +2347,28 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
/* program PHY to use external vBus if required */
if (plat->extvbus) {
- u8 busctl = musb_read_ulpi_buscontrol(musb->mregs);
+ u8 busctl = musb_readb(musb->mregs, MUSB_ULPI_BUSCONTROL);
busctl |= MUSB_ULPI_USE_EXTVBUS;
- musb_write_ulpi_buscontrol(musb->mregs, busctl);
+ musb_writeb(musb->mregs, MUSB_ULPI_BUSCONTROL, busctl);
}
- if (musb->xceiv->otg->default_a) {
- MUSB_HST_MODE(musb);
- musb->xceiv->otg->state = OTG_STATE_A_IDLE;
- } else {
- MUSB_DEV_MODE(musb);
- musb->xceiv->otg->state = OTG_STATE_B_IDLE;
- }
+ MUSB_DEV_MODE(musb);
+ musb->xceiv->otg->state = OTG_STATE_B_IDLE;
switch (musb->port_mode) {
- case MUSB_PORT_MODE_HOST:
+ case MUSB_HOST:
status = musb_host_setup(musb, plat->power);
if (status < 0)
goto fail3;
status = musb_platform_set_mode(musb, MUSB_HOST);
break;
- case MUSB_PORT_MODE_GADGET:
+ case MUSB_PERIPHERAL:
status = musb_gadget_setup(musb);
if (status < 0)
goto fail3;
status = musb_platform_set_mode(musb, MUSB_PERIPHERAL);
break;
- case MUSB_PORT_MODE_DUAL_ROLE:
+ case MUSB_OTG:
status = musb_host_setup(musb, plat->power);
if (status < 0)
goto fail3;
@@ -2366,9 +2387,7 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
if (status < 0)
goto fail3;
- status = musb_init_debugfs(musb);
- if (status < 0)
- goto fail4;
+ musb_init_debugfs(musb);
status = sysfs_create_group(&musb->controller->kobj, &musb_attr_group);
if (status)
@@ -2383,7 +2402,6 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
fail5:
musb_exit_debugfs(musb);
-fail4:
musb_gadget_cleanup(musb);
musb_host_cleanup(musb);
@@ -2492,7 +2510,7 @@ static void musb_save_context(struct musb *musb)
musb->context.frame = musb_readw(musb_base, MUSB_FRAME);
musb->context.testmode = musb_readb(musb_base, MUSB_TESTMODE);
- musb->context.busctl = musb_read_ulpi_buscontrol(musb->mregs);
+ musb->context.busctl = musb_readb(musb_base, MUSB_ULPI_BUSCONTROL);
musb->context.power = musb_readb(musb_base, MUSB_POWER);
musb->context.intrusbe = musb_readb(musb_base, MUSB_INTRUSBE);
musb->context.index = musb_readb(musb_base, MUSB_INDEX);
@@ -2521,13 +2539,13 @@ static void musb_save_context(struct musb *musb)
if (musb->dyn_fifo) {
musb->context.index_regs[i].txfifoadd =
- musb_read_txfifoadd(musb_base);
+ musb_readw(musb_base, MUSB_TXFIFOADD);
musb->context.index_regs[i].rxfifoadd =
- musb_read_rxfifoadd(musb_base);
+ musb_readw(musb_base, MUSB_RXFIFOADD);
musb->context.index_regs[i].txfifosz =
- musb_read_txfifosz(musb_base);
+ musb_readb(musb_base, MUSB_TXFIFOSZ);
musb->context.index_regs[i].rxfifosz =
- musb_read_rxfifosz(musb_base);
+ musb_readb(musb_base, MUSB_RXFIFOSZ);
}
musb->context.index_regs[i].txtype =
@@ -2564,7 +2582,7 @@ static void musb_restore_context(struct musb *musb)
musb_writew(musb_base, MUSB_FRAME, musb->context.frame);
musb_writeb(musb_base, MUSB_TESTMODE, musb->context.testmode);
- musb_write_ulpi_buscontrol(musb->mregs, musb->context.busctl);
+ musb_writeb(musb_base, MUSB_ULPI_BUSCONTROL, musb->context.busctl);
/* Don't affect SUSPENDM/RESUME bits in POWER reg */
power = musb_readb(musb_base, MUSB_POWER);
@@ -2601,13 +2619,13 @@ static void musb_restore_context(struct musb *musb)
musb->context.index_regs[i].rxcsr);
if (musb->dyn_fifo) {
- musb_write_txfifosz(musb_base,
+ musb_writeb(musb_base, MUSB_TXFIFOSZ,
musb->context.index_regs[i].txfifosz);
- musb_write_rxfifosz(musb_base,
+ musb_writeb(musb_base, MUSB_RXFIFOSZ,
musb->context.index_regs[i].rxfifosz);
- musb_write_txfifoadd(musb_base,
+ musb_writew(musb_base, MUSB_TXFIFOADD,
musb->context.index_regs[i].txfifoadd);
- musb_write_rxfifoadd(musb_base,
+ musb_writew(musb_base, MUSB_RXFIFOADD,
musb->context.index_regs[i].rxfifoadd);
}
@@ -2657,7 +2675,7 @@ static int musb_suspend(struct device *dev)
;
musb->flush_irq_work = false;
- if (!(musb->io.quirks & MUSB_PRESERVE_SESSION))
+ if (!(musb->ops->quirks & MUSB_PRESERVE_SESSION))
musb_writeb(musb->mregs, MUSB_DEVCTL, 0);
WARN_ON(!list_empty(&musb->pending_list));
diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h
index 8a74cb2907f8..04203b7126d5 100644
--- a/drivers/usb/musb/musb_core.h
+++ b/drivers/usb/musb/musb_core.h
@@ -53,12 +53,6 @@ struct musb_ep;
#define is_peripheral_active(m) (!(m)->is_host)
#define is_host_active(m) ((m)->is_host)
-enum {
- MUSB_PORT_MODE_HOST = 1,
- MUSB_PORT_MODE_GADGET,
- MUSB_PORT_MODE_DUAL_ROLE,
-};
-
/****************************** CONSTANTS ********************************/
#ifndef MUSB_C_NUM_EPS
@@ -127,8 +121,6 @@ struct musb_io;
* @writeb: write 8 bits
* @readw: read 16 bits
* @writew: write 16 bits
- * @readl: read 32 bits
- * @writel: write 32 bits
* @read_fifo: reads the fifo
* @write_fifo: writes to fifo
* @dma_init: platform specific dma init function
@@ -140,7 +132,6 @@ struct musb_io;
* @recover: platform-specific babble recovery
* @vbus_status: returns vbus status if possible
* @set_vbus: forces vbus status
- * @adjust_channel_params: pre check for standard dma channel_program func
* @pre_root_reset_end: called before the root usb port reset flag gets cleared
* @post_root_reset_end: called after the root usb port reset flag gets cleared
* @phy_callback: optional callback function for the phy to call
@@ -174,8 +165,6 @@ struct musb_platform_ops {
void (*writeb)(void __iomem *addr, unsigned offset, u8 data);
u16 (*readw)(const void __iomem *addr, unsigned offset);
void (*writew)(void __iomem *addr, unsigned offset, u16 data);
- u32 (*readl)(const void __iomem *addr, unsigned offset);
- void (*writel)(void __iomem *addr, unsigned offset, u32 data);
void (*read_fifo)(struct musb_hw_ep *hw_ep, u16 len, u8 *buf);
void (*write_fifo)(struct musb_hw_ep *hw_ep, u16 len, const u8 *buf);
struct dma_controller *
@@ -188,9 +177,6 @@ struct musb_platform_ops {
int (*vbus_status)(struct musb *musb);
void (*set_vbus)(struct musb *musb, int on);
- int (*adjust_channel_params)(struct dma_channel *channel,
- u16 packet_sz, u8 *mode,
- dma_addr_t *dma_addr, u32 *len);
void (*pre_root_reset_end)(struct musb *musb);
void (*post_root_reset_end)(struct musb *musb);
int (*phy_callback)(enum musb_vbus_id_status status);
@@ -359,7 +345,7 @@ struct musb {
u8 min_power; /* vbus for periph, in mA/2 */
- int port_mode; /* MUSB_PORT_MODE_* */
+ enum musb_mode port_mode;
bool session;
unsigned long quirk_retries;
bool is_host;
diff --git a/drivers/usb/musb/musb_cppi41.c b/drivers/usb/musb/musb_cppi41.c
index d0dd4f470bbe..7fbb8a307145 100644
--- a/drivers/usb/musb/musb_cppi41.c
+++ b/drivers/usb/musb/musb_cppi41.c
@@ -614,7 +614,7 @@ static int cppi41_dma_channel_abort(struct dma_channel *channel)
}
/* DA8xx Advisory 2.3.27: wait 250 ms before to start the teardown */
- if (musb->io.quirks & MUSB_DA8XX)
+ if (musb->ops->quirks & MUSB_DA8XX)
mdelay(250);
tdbit = 1 << cppi41_channel->port_num;
@@ -773,7 +773,7 @@ cppi41_dma_controller_create(struct musb *musb, void __iomem *base)
controller->controller.is_compatible = cppi41_is_compatible;
controller->controller.musb = musb;
- if (musb->io.quirks & MUSB_DA8XX) {
+ if (musb->ops->quirks & MUSB_DA8XX) {
controller->tdown_reg = DA8XX_USB_TEARDOWN;
controller->autoreq_reg = DA8XX_USB_AUTOREQ;
controller->set_dma_mode = da8xx_set_dma_mode;
diff --git a/drivers/usb/musb/musb_debug.h b/drivers/usb/musb/musb_debug.h
index 5e0f079dde21..c444a80fe1da 100644
--- a/drivers/usb/musb/musb_debug.h
+++ b/drivers/usb/musb/musb_debug.h
@@ -20,12 +20,11 @@
void musb_dbg(struct musb *musb, const char *fmt, ...);
#ifdef CONFIG_DEBUG_FS
-int musb_init_debugfs(struct musb *musb);
+void musb_init_debugfs(struct musb *musb);
void musb_exit_debugfs(struct musb *musb);
#else
-static inline int musb_init_debugfs(struct musb *musb)
+static inline void musb_init_debugfs(struct musb *musb)
{
- return 0;
}
static inline void musb_exit_debugfs(struct musb *musb)
{
diff --git a/drivers/usb/musb/musb_debugfs.c b/drivers/usb/musb/musb_debugfs.c
index e2050cac3eae..f42858e2b54c 100644
--- a/drivers/usb/musb/musb_debugfs.c
+++ b/drivers/usb/musb/musb_debugfs.c
@@ -321,48 +321,18 @@ static const struct file_operations musb_softconnect_fops = {
.release = single_release,
};
-int musb_init_debugfs(struct musb *musb)
+void musb_init_debugfs(struct musb *musb)
{
- struct dentry *root;
- struct dentry *file;
- int ret;
+ struct dentry *root;
root = debugfs_create_dir(dev_name(musb->controller), NULL);
- if (!root) {
- ret = -ENOMEM;
- goto err0;
- }
-
- file = debugfs_create_file("regdump", S_IRUGO, root, musb,
- &musb_regdump_fops);
- if (!file) {
- ret = -ENOMEM;
- goto err1;
- }
-
- file = debugfs_create_file("testmode", S_IRUGO | S_IWUSR,
- root, musb, &musb_test_mode_fops);
- if (!file) {
- ret = -ENOMEM;
- goto err1;
- }
-
- file = debugfs_create_file("softconnect", S_IRUGO | S_IWUSR,
- root, musb, &musb_softconnect_fops);
- if (!file) {
- ret = -ENOMEM;
- goto err1;
- }
-
musb->debugfs_root = root;
- return 0;
-
-err1:
- debugfs_remove_recursive(root);
-
-err0:
- return ret;
+ debugfs_create_file("regdump", S_IRUGO, root, musb, &musb_regdump_fops);
+ debugfs_create_file("testmode", S_IRUGO | S_IWUSR, root, musb,
+ &musb_test_mode_fops);
+ debugfs_create_file("softconnect", S_IRUGO | S_IWUSR, root, musb,
+ &musb_softconnect_fops);
}
void /* __init_or_exit */ musb_exit_debugfs(struct musb *musb)
diff --git a/drivers/usb/musb/musb_dma.h b/drivers/usb/musb/musb_dma.h
index 0fc8cd0c2a5c..8f60271c0a9d 100644
--- a/drivers/usb/musb/musb_dma.h
+++ b/drivers/usb/musb/musb_dma.h
@@ -44,31 +44,31 @@ struct musb_hw_ep;
#endif
#ifdef CONFIG_USB_UX500_DMA
-#define musb_dma_ux500(musb) (musb->io.quirks & MUSB_DMA_UX500)
+#define musb_dma_ux500(musb) (musb->ops->quirks & MUSB_DMA_UX500)
#else
#define musb_dma_ux500(musb) 0
#endif
#ifdef CONFIG_USB_TI_CPPI41_DMA
-#define musb_dma_cppi41(musb) (musb->io.quirks & MUSB_DMA_CPPI41)
+#define musb_dma_cppi41(musb) (musb->ops->quirks & MUSB_DMA_CPPI41)
#else
#define musb_dma_cppi41(musb) 0
#endif
#ifdef CONFIG_USB_TI_CPPI_DMA
-#define musb_dma_cppi(musb) (musb->io.quirks & MUSB_DMA_CPPI)
+#define musb_dma_cppi(musb) (musb->ops->quirks & MUSB_DMA_CPPI)
#else
#define musb_dma_cppi(musb) 0
#endif
#ifdef CONFIG_USB_TUSB_OMAP_DMA
-#define tusb_dma_omap(musb) (musb->io.quirks & MUSB_DMA_TUSB_OMAP)
+#define tusb_dma_omap(musb) (musb->ops->quirks & MUSB_DMA_TUSB_OMAP)
#else
#define tusb_dma_omap(musb) 0
#endif
#ifdef CONFIG_USB_INVENTRA_DMA
-#define musb_dma_inventra(musb) (musb->io.quirks & MUSB_DMA_INVENTRA)
+#define musb_dma_inventra(musb) (musb->ops->quirks & MUSB_DMA_INVENTRA)
#else
#define musb_dma_inventra(musb) 0
#endif
diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c
index 6a60bc0490c5..23a0df79ef21 100644
--- a/drivers/usb/musb/musb_dsps.c
+++ b/drivers/usb/musb/musb_dsps.c
@@ -183,7 +183,7 @@ static void dsps_musb_enable(struct musb *musb)
musb_writel(reg_base, wrp->coreintr_set, coremask);
/* start polling for ID change in dual-role idle mode */
if (musb->xceiv->otg->state == OTG_STATE_B_IDLE &&
- musb->port_mode == MUSB_PORT_MODE_DUAL_ROLE)
+ musb->port_mode == MUSB_OTG)
dsps_mod_timer(glue, -1);
}
@@ -231,13 +231,13 @@ static int dsps_check_status(struct musb *musb, void *unused)
break;
case OTG_STATE_A_WAIT_BCON:
/* keep VBUS on for host-only mode */
- if (musb->port_mode == MUSB_PORT_MODE_HOST) {
+ if (musb->port_mode == MUSB_HOST) {
dsps_mod_timer_optional(glue);
break;
}
musb_writeb(musb->mregs, MUSB_DEVCTL, 0);
skip_session = 1;
- /* fall */
+ /* fall through */
case OTG_STATE_A_IDLE:
case OTG_STATE_B_IDLE:
@@ -360,13 +360,11 @@ static irqreturn_t dsps_interrupt(int irq, void *hci)
WARNING("VBUS error workaround (delay coming)\n");
} else if (drvvbus) {
MUSB_HST_MODE(musb);
- musb->xceiv->otg->default_a = 1;
musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE;
dsps_mod_timer_optional(glue);
} else {
musb->is_active = 0;
MUSB_DEV_MODE(musb);
- musb->xceiv->otg->default_a = 0;
musb->xceiv->otg->state = OTG_STATE_B_IDLE;
}
@@ -401,24 +399,17 @@ out:
static int dsps_musb_dbg_init(struct musb *musb, struct dsps_glue *glue)
{
struct dentry *root;
- struct dentry *file;
char buf[128];
sprintf(buf, "%s.dsps", dev_name(musb->controller));
root = debugfs_create_dir(buf, NULL);
- if (!root)
- return -ENOMEM;
glue->dbgfs_root = root;
glue->regset.regs = dsps_musb_regs;
glue->regset.nregs = ARRAY_SIZE(dsps_musb_regs);
glue->regset.base = musb->ctrl_base;
- file = debugfs_create_regset32("regdump", S_IRUGO, root, &glue->regset);
- if (!file) {
- debugfs_remove_recursive(root);
- return -ENOMEM;
- }
+ debugfs_create_regset32("regdump", S_IRUGO, root, &glue->regset);
return 0;
}
@@ -667,16 +658,6 @@ dsps_dma_controller_create(struct musb *musb, void __iomem *base)
return controller;
}
-static void dsps_dma_controller_destroy(struct dma_controller *c)
-{
- struct musb *musb = c->musb;
- struct dsps_glue *glue = dev_get_drvdata(musb->controller->parent);
- void __iomem *usbss_base = glue->usbss_base;
-
- musb_writel(usbss_base, USBSS_IRQ_CLEARR, USBSS_IRQ_PD_COMP);
- cppi41_dma_controller_destroy(c);
-}
-
#ifdef CONFIG_PM_SLEEP
static void dsps_dma_controller_suspend(struct dsps_glue *glue)
{
@@ -706,7 +687,7 @@ static struct musb_platform_ops dsps_ops = {
#ifdef CONFIG_USB_TI_CPPI41_DMA
.dma_init = dsps_dma_controller_create,
- .dma_exit = dsps_dma_controller_destroy,
+ .dma_exit = cppi41_dma_controller_destroy,
#endif
.enable = dsps_musb_enable,
.disable = dsps_musb_disable,
@@ -729,25 +710,6 @@ static int get_int_prop(struct device_node *dn, const char *s)
return val;
}
-static int get_musb_port_mode(struct device *dev)
-{
- enum usb_dr_mode mode;
-
- mode = usb_get_dr_mode(dev);
- switch (mode) {
- case USB_DR_MODE_HOST:
- return MUSB_PORT_MODE_HOST;
-
- case USB_DR_MODE_PERIPHERAL:
- return MUSB_PORT_MODE_GADGET;
-
- case USB_DR_MODE_UNKNOWN:
- case USB_DR_MODE_OTG:
- default:
- return MUSB_PORT_MODE_DUAL_ROLE;
- }
-}
-
static int dsps_create_musb_pdev(struct dsps_glue *glue,
struct platform_device *parent)
{
@@ -786,6 +748,7 @@ static int dsps_create_musb_pdev(struct dsps_glue *glue,
musb->dev.parent = dev;
musb->dev.dma_mask = &musb_dmamask;
musb->dev.coherent_dma_mask = musb_dmamask;
+ device_set_of_node_from_dev(&musb->dev, &parent->dev);
glue->musb = musb;
@@ -807,7 +770,7 @@ static int dsps_create_musb_pdev(struct dsps_glue *glue,
config->num_eps = get_int_prop(dn, "mentor,num-eps");
config->ram_bits = get_int_prop(dn, "mentor,ram-bits");
config->host_port_deassert_reset_at_resume = 1;
- pdata.mode = get_musb_port_mode(dev);
+ pdata.mode = musb_get_mode(dev);
/* DT keeps this entry in mA, musb expects it as per USB spec */
pdata.power = get_int_prop(dn, "mentor,power") / 2;
@@ -1047,7 +1010,7 @@ static int dsps_resume(struct device *dev)
musb_writel(mbase, wrp->tx_mode, glue->context.tx_mode);
musb_writel(mbase, wrp->rx_mode, glue->context.rx_mode);
if (musb->xceiv->otg->state == OTG_STATE_B_IDLE &&
- musb->port_mode == MUSB_PORT_MODE_DUAL_ROLE)
+ musb->port_mode == MUSB_OTG)
dsps_mod_timer(glue, -1);
pm_runtime_put(dev);
diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c
index 71c5835ea9cd..eae8b1b1b45b 100644
--- a/drivers/usb/musb/musb_gadget.c
+++ b/drivers/usb/musb/musb_gadget.c
@@ -1794,16 +1794,12 @@ int musb_gadget_setup(struct musb *musb)
musb->g.speed = USB_SPEED_UNKNOWN;
MUSB_DEV_MODE(musb);
- musb->xceiv->otg->default_a = 0;
musb->xceiv->otg->state = OTG_STATE_B_IDLE;
/* this "gadget" abstracts/virtualizes the controller */
musb->g.name = musb_driver_name;
-#if IS_ENABLED(CONFIG_USB_MUSB_DUAL_ROLE)
- musb->g.is_otg = 1;
-#elif IS_ENABLED(CONFIG_USB_MUSB_GADGET)
+ /* don't support otg protocols */
musb->g.is_otg = 0;
-#endif
INIT_DELAYED_WORK(&musb->gadget_work, musb_gadget_work);
musb_g_init_endpoints(musb);
@@ -1823,7 +1819,7 @@ err:
void musb_gadget_cleanup(struct musb *musb)
{
- if (musb->port_mode == MUSB_PORT_MODE_HOST)
+ if (musb->port_mode == MUSB_HOST)
return;
cancel_delayed_work_sync(&musb->gadget_work);
diff --git a/drivers/usb/musb/musb_gadget.h b/drivers/usb/musb/musb_gadget.h
index 9c34aca06db6..d02663660813 100644
--- a/drivers/usb/musb/musb_gadget.h
+++ b/drivers/usb/musb/musb_gadget.h
@@ -60,10 +60,7 @@ struct musb_request {
enum buffer_map_state map_state;
};
-static inline struct musb_request *to_musb_request(struct usb_request *req)
-{
- return req ? container_of(req, struct musb_request, request) : NULL;
-}
+#define to_musb_request(r) container_of((r), struct musb_request, request)
extern struct usb_request *
musb_alloc_request(struct usb_ep *ep, gfp_t gfp_flags);
@@ -99,10 +96,7 @@ struct musb_ep {
u8 hb_mult;
};
-static inline struct musb_ep *to_musb_ep(struct usb_ep *ep)
-{
- return ep ? container_of(ep, struct musb_ep, end_point) : NULL;
-}
+#define to_musb_ep(ep) container_of((ep), struct musb_ep, end_point)
static inline struct musb_request *next_request(struct musb_ep *ep)
{
diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
index 15a42cee0a9c..b59ce9ad14ce 100644
--- a/drivers/usb/musb/musb_host.c
+++ b/drivers/usb/musb/musb_host.c
@@ -378,6 +378,7 @@ static void musb_advance_schedule(struct musb *musb, struct urb *urb,
qh = first_qh(head);
break;
}
+ /* else: fall through */
case USB_ENDPOINT_XFER_ISOC:
case USB_ENDPOINT_XFER_INT:
@@ -2735,7 +2736,7 @@ int musb_host_alloc(struct musb *musb)
void musb_host_cleanup(struct musb *musb)
{
- if (musb->port_mode == MUSB_PORT_MODE_GADGET)
+ if (musb->port_mode == MUSB_PERIPHERAL)
return;
usb_remove_hcd(musb->hcd);
}
@@ -2750,13 +2751,13 @@ int musb_host_setup(struct musb *musb, int power_budget)
int ret;
struct usb_hcd *hcd = musb->hcd;
- if (musb->port_mode == MUSB_PORT_MODE_HOST) {
+ if (musb->port_mode == MUSB_HOST) {
MUSB_HST_MODE(musb);
- musb->xceiv->otg->default_a = 1;
musb->xceiv->otg->state = OTG_STATE_A_IDLE;
}
otg_set_host(musb->xceiv->otg, &hcd->self);
- hcd->self.otg_port = 1;
+ /* don't support otg protocols */
+ hcd->self.otg_port = 0;
musb->xceiv->otg->host = &hcd->self;
hcd->power_budget = 2 * (power_budget ? : 250);
hcd->skip_phy_initialization = 1;
diff --git a/drivers/usb/musb/musb_io.h b/drivers/usb/musb/musb_io.h
index b7025b2e6e00..8058a58092cf 100644
--- a/drivers/usb/musb/musb_io.h
+++ b/drivers/usb/musb/musb_io.h
@@ -16,7 +16,6 @@
/**
* struct musb_io - IO functions for MUSB
- * @quirks: platform specific flags
* @ep_offset: platform specific function to get end point offset
* @ep_select: platform specific function to select end point
* @fifo_offset: platform specific function to get fifo offset
@@ -25,7 +24,6 @@
* @busctl_offset: platform specific function to get busctl offset
*/
struct musb_io {
- u32 quirks;
u32 (*ep_offset)(u8 epnum, u16 offset);
void (*ep_select)(void __iomem *mbase, u8 epnum);
u32 (*fifo_offset)(u8 epnum);
@@ -39,7 +37,7 @@ extern u8 (*musb_readb)(const void __iomem *addr, unsigned offset);
extern void (*musb_writeb)(void __iomem *addr, unsigned offset, u8 data);
extern u16 (*musb_readw)(const void __iomem *addr, unsigned offset);
extern void (*musb_writew)(void __iomem *addr, unsigned offset, u16 data);
-extern u32 (*musb_readl)(const void __iomem *addr, unsigned offset);
-extern void (*musb_writel)(void __iomem *addr, unsigned offset, u32 data);
+extern u32 musb_readl(const void __iomem *addr, unsigned offset);
+extern void musb_writel(void __iomem *addr, unsigned offset, u32 data);
#endif
diff --git a/drivers/usb/musb/musb_regs.h b/drivers/usb/musb/musb_regs.h
index 88466622c89f..5cd7264fc2cb 100644
--- a/drivers/usb/musb/musb_regs.h
+++ b/drivers/usb/musb/musb_regs.h
@@ -273,67 +273,12 @@
#define MUSB_RXHUBADDR 0x06
#define MUSB_RXHUBPORT 0x07
-static inline void musb_write_txfifosz(void __iomem *mbase, u8 c_size)
-{
- musb_writeb(mbase, MUSB_TXFIFOSZ, c_size);
-}
-
-static inline void musb_write_txfifoadd(void __iomem *mbase, u16 c_off)
-{
- musb_writew(mbase, MUSB_TXFIFOADD, c_off);
-}
-
-static inline void musb_write_rxfifosz(void __iomem *mbase, u8 c_size)
-{
- musb_writeb(mbase, MUSB_RXFIFOSZ, c_size);
-}
-
-static inline void musb_write_rxfifoadd(void __iomem *mbase, u16 c_off)
-{
- musb_writew(mbase, MUSB_RXFIFOADD, c_off);
-}
-
-static inline void musb_write_ulpi_buscontrol(void __iomem *mbase, u8 val)
-{
- musb_writeb(mbase, MUSB_ULPI_BUSCONTROL, val);
-}
-
-static inline u8 musb_read_txfifosz(void __iomem *mbase)
-{
- return musb_readb(mbase, MUSB_TXFIFOSZ);
-}
-
-static inline u16 musb_read_txfifoadd(void __iomem *mbase)
-{
- return musb_readw(mbase, MUSB_TXFIFOADD);
-}
-
-static inline u8 musb_read_rxfifosz(void __iomem *mbase)
-{
- return musb_readb(mbase, MUSB_RXFIFOSZ);
-}
-
-static inline u16 musb_read_rxfifoadd(void __iomem *mbase)
-{
- return musb_readw(mbase, MUSB_RXFIFOADD);
-}
-
-static inline u8 musb_read_ulpi_buscontrol(void __iomem *mbase)
-{
- return musb_readb(mbase, MUSB_ULPI_BUSCONTROL);
-}
-
static inline u8 musb_read_configdata(void __iomem *mbase)
{
musb_writeb(mbase, MUSB_INDEX, 0);
return musb_readb(mbase, 0x10 + MUSB_CONFIGDATA);
}
-static inline u16 musb_read_hwvers(void __iomem *mbase)
-{
- return musb_readw(mbase, MUSB_HWVERS);
-}
-
static inline void musb_write_rxfunaddr(struct musb *musb, u8 epnum,
u8 qh_addr_reg)
{
diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c
index 2f8dd9826e94..a84ec27c4c12 100644
--- a/drivers/usb/musb/musb_virthub.c
+++ b/drivers/usb/musb/musb_virthub.c
@@ -254,7 +254,7 @@ static int musb_has_gadget(struct musb *musb)
#ifdef CONFIG_USB_MUSB_HOST
return 1;
#else
- return musb->port_mode == MUSB_PORT_MODE_HOST;
+ return musb->port_mode == MUSB_HOST;
#endif
}
diff --git a/drivers/usb/musb/musbhsdma.c b/drivers/usb/musb/musbhsdma.c
index 4389fc3422bd..a688f7f87829 100644
--- a/drivers/usb/musb/musbhsdma.c
+++ b/drivers/usb/musb/musbhsdma.c
@@ -10,7 +10,71 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
#include "musb_core.h"
-#include "musbhsdma.h"
+
+#define MUSB_HSDMA_BASE 0x200
+#define MUSB_HSDMA_INTR (MUSB_HSDMA_BASE + 0)
+#define MUSB_HSDMA_CONTROL 0x4
+#define MUSB_HSDMA_ADDRESS 0x8
+#define MUSB_HSDMA_COUNT 0xc
+
+#define MUSB_HSDMA_CHANNEL_OFFSET(_bchannel, _offset) \
+ (MUSB_HSDMA_BASE + (_bchannel << 4) + _offset)
+
+#define musb_read_hsdma_addr(mbase, bchannel) \
+ musb_readl(mbase, \
+ MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_ADDRESS))
+
+#define musb_write_hsdma_addr(mbase, bchannel, addr) \
+ musb_writel(mbase, \
+ MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_ADDRESS), \
+ addr)
+
+#define musb_read_hsdma_count(mbase, bchannel) \
+ musb_readl(mbase, \
+ MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_COUNT))
+
+#define musb_write_hsdma_count(mbase, bchannel, len) \
+ musb_writel(mbase, \
+ MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_COUNT), \
+ len)
+/* control register (16-bit): */
+#define MUSB_HSDMA_ENABLE_SHIFT 0
+#define MUSB_HSDMA_TRANSMIT_SHIFT 1
+#define MUSB_HSDMA_MODE1_SHIFT 2
+#define MUSB_HSDMA_IRQENABLE_SHIFT 3
+#define MUSB_HSDMA_ENDPOINT_SHIFT 4
+#define MUSB_HSDMA_BUSERROR_SHIFT 8
+#define MUSB_HSDMA_BURSTMODE_SHIFT 9
+#define MUSB_HSDMA_BURSTMODE (3 << MUSB_HSDMA_BURSTMODE_SHIFT)
+#define MUSB_HSDMA_BURSTMODE_UNSPEC 0
+#define MUSB_HSDMA_BURSTMODE_INCR4 1
+#define MUSB_HSDMA_BURSTMODE_INCR8 2
+#define MUSB_HSDMA_BURSTMODE_INCR16 3
+
+#define MUSB_HSDMA_CHANNELS 8
+
+struct musb_dma_controller;
+
+struct musb_dma_channel {
+ struct dma_channel channel;
+ struct musb_dma_controller *controller;
+ u32 start_addr;
+ u32 len;
+ u16 max_packet_sz;
+ u8 idx;
+ u8 epnum;
+ u8 transmit;
+};
+
+struct musb_dma_controller {
+ struct dma_controller controller;
+ struct musb_dma_channel channel[MUSB_HSDMA_CHANNELS];
+ void *private_data;
+ void __iomem *base;
+ u8 channel_count;
+ u8 used_channels;
+ int irq;
+};
static void dma_channel_release(struct dma_channel *channel);
@@ -135,14 +199,6 @@ static int dma_channel_program(struct dma_channel *channel,
BUG_ON(channel->status == MUSB_DMA_STATUS_UNKNOWN ||
channel->status == MUSB_DMA_STATUS_BUSY);
- /* Let targets check/tweak the arguments */
- if (musb->ops->adjust_channel_params) {
- int ret = musb->ops->adjust_channel_params(channel,
- packet_sz, &mode, &dma_addr, &len);
- if (ret)
- return ret;
- }
-
/*
* The DMA engine in RTL1.8 and above cannot handle
* DMA addresses that are not aligned to a 4 byte boundary.
diff --git a/drivers/usb/musb/musbhsdma.h b/drivers/usb/musb/musbhsdma.h
deleted file mode 100644
index 93665135aff1..000000000000
--- a/drivers/usb/musb/musbhsdma.h
+++ /dev/null
@@ -1,72 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * MUSB OTG driver - support for Mentor's DMA controller
- *
- * Copyright 2005 Mentor Graphics Corporation
- * Copyright (C) 2005-2007 by Texas Instruments
- */
-
-#define MUSB_HSDMA_BASE 0x200
-#define MUSB_HSDMA_INTR (MUSB_HSDMA_BASE + 0)
-#define MUSB_HSDMA_CONTROL 0x4
-#define MUSB_HSDMA_ADDRESS 0x8
-#define MUSB_HSDMA_COUNT 0xc
-
-#define MUSB_HSDMA_CHANNEL_OFFSET(_bchannel, _offset) \
- (MUSB_HSDMA_BASE + (_bchannel << 4) + _offset)
-
-#define musb_read_hsdma_addr(mbase, bchannel) \
- musb_readl(mbase, \
- MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_ADDRESS))
-
-#define musb_write_hsdma_addr(mbase, bchannel, addr) \
- musb_writel(mbase, \
- MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_ADDRESS), \
- addr)
-
-#define musb_read_hsdma_count(mbase, bchannel) \
- musb_readl(mbase, \
- MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_COUNT))
-
-#define musb_write_hsdma_count(mbase, bchannel, len) \
- musb_writel(mbase, \
- MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_COUNT), \
- len)
-/* control register (16-bit): */
-#define MUSB_HSDMA_ENABLE_SHIFT 0
-#define MUSB_HSDMA_TRANSMIT_SHIFT 1
-#define MUSB_HSDMA_MODE1_SHIFT 2
-#define MUSB_HSDMA_IRQENABLE_SHIFT 3
-#define MUSB_HSDMA_ENDPOINT_SHIFT 4
-#define MUSB_HSDMA_BUSERROR_SHIFT 8
-#define MUSB_HSDMA_BURSTMODE_SHIFT 9
-#define MUSB_HSDMA_BURSTMODE (3 << MUSB_HSDMA_BURSTMODE_SHIFT)
-#define MUSB_HSDMA_BURSTMODE_UNSPEC 0
-#define MUSB_HSDMA_BURSTMODE_INCR4 1
-#define MUSB_HSDMA_BURSTMODE_INCR8 2
-#define MUSB_HSDMA_BURSTMODE_INCR16 3
-
-#define MUSB_HSDMA_CHANNELS 8
-
-struct musb_dma_controller;
-
-struct musb_dma_channel {
- struct dma_channel channel;
- struct musb_dma_controller *controller;
- u32 start_addr;
- u32 len;
- u16 max_packet_sz;
- u8 idx;
- u8 epnum;
- u8 transmit;
-};
-
-struct musb_dma_controller {
- struct dma_controller controller;
- struct musb_dma_channel channel[MUSB_HSDMA_CHANNELS];
- void *private_data;
- void __iomem *base;
- u8 channel_count;
- u8 used_channels;
- int irq;
-};
diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c
index 5d705930ef47..b1dd81fb5f55 100644
--- a/drivers/usb/musb/omap2430.c
+++ b/drivers/usb/musb/omap2430.c
@@ -77,7 +77,6 @@ static void omap2430_musb_set_vbus(struct musb *musb, int is_on)
otg_set_vbus(otg, 1);
} else {
musb->is_active = 1;
- otg->default_a = 1;
musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE;
devctl |= MUSB_DEVCTL_SESSION;
MUSB_HST_MODE(musb);
@@ -89,7 +88,6 @@ static void omap2430_musb_set_vbus(struct musb *musb, int is_on)
* jumping right to B_IDLE...
*/
- otg->default_a = 0;
musb->xceiv->otg->state = OTG_STATE_B_IDLE;
devctl &= ~MUSB_DEVCTL_SESSION;
@@ -148,14 +146,12 @@ static void omap_musb_set_mailbox(struct omap2430_glue *glue)
struct musb_hdrc_platform_data *pdata =
dev_get_platdata(musb->controller);
struct omap_musb_board_data *data = pdata->board_data;
- struct usb_otg *otg = musb->xceiv->otg;
pm_runtime_get_sync(musb->controller);
switch (glue->status) {
case MUSB_ID_GROUND:
dev_dbg(musb->controller, "ID GND\n");
- otg->default_a = true;
musb->xceiv->otg->state = OTG_STATE_A_IDLE;
musb->xceiv->last_event = USB_EVENT_ID;
if (musb->gadget_driver) {
@@ -168,7 +164,6 @@ static void omap_musb_set_mailbox(struct omap2430_glue *glue)
case MUSB_VBUS_VALID:
dev_dbg(musb->controller, "VBUS Connect\n");
- otg->default_a = false;
musb->xceiv->otg->state = OTG_STATE_B_IDLE;
musb->xceiv->last_event = USB_EVENT_VBUS;
omap_control_usb_set_mode(glue->control_otghs, USB_MODE_DEVICE);
@@ -239,21 +234,15 @@ static int omap2430_musb_init(struct musb *musb)
* up through ULPI. TWL4030-family PMICs include one,
* which needs a driver, drivers aren't always needed.
*/
- if (dev->parent->of_node) {
- musb->phy = devm_phy_get(dev->parent, "usb2-phy");
-
- /* We can't totally remove musb->xceiv as of now because
- * musb core uses xceiv.state and xceiv.otg. Once we have
- * a separate state machine to handle otg, these can be moved
- * out of xceiv and then we can start using the generic PHY
- * framework
- */
- musb->xceiv = devm_usb_get_phy_by_phandle(dev->parent,
- "usb-phy", 0);
- } else {
- musb->xceiv = devm_usb_get_phy_dev(dev, 0);
- musb->phy = devm_phy_get(dev, "usb");
- }
+ musb->phy = devm_phy_get(dev->parent, "usb2-phy");
+
+ /* We can't totally remove musb->xceiv as of now because
+ * musb core uses xceiv.state and xceiv.otg. Once we have
+ * a separate state machine to handle otg, these can be moved
+ * out of xceiv and then we can start using the generic PHY
+ * framework
+ */
+ musb->xceiv = devm_usb_get_phy_by_phandle(dev->parent, "usb-phy", 0);
if (IS_ERR(musb->xceiv)) {
status = PTR_ERR(musb->xceiv);
@@ -391,8 +380,13 @@ static int omap2430_probe(struct platform_device *pdev)
struct omap2430_glue *glue;
struct device_node *np = pdev->dev.of_node;
struct musb_hdrc_config *config;
+ struct device_node *control_node;
+ struct platform_device *control_pdev;
int ret = -ENOMEM, val;
+ if (!np)
+ return -ENODEV;
+
glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);
if (!glue)
goto err0;
@@ -412,47 +406,43 @@ static int omap2430_probe(struct platform_device *pdev)
glue->status = MUSB_UNKNOWN;
glue->control_otghs = ERR_PTR(-ENODEV);
- if (np) {
- struct device_node *control_node;
- struct platform_device *control_pdev;
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ goto err2;
- pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
- if (!pdata)
- goto err2;
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ goto err2;
- data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
- if (!data)
- goto err2;
+ config = devm_kzalloc(&pdev->dev, sizeof(*config), GFP_KERNEL);
+ if (!config)
+ goto err2;
- config = devm_kzalloc(&pdev->dev, sizeof(*config), GFP_KERNEL);
- if (!config)
+ of_property_read_u32(np, "mode", (u32 *)&pdata->mode);
+ of_property_read_u32(np, "interface-type",
+ (u32 *)&data->interface_type);
+ of_property_read_u32(np, "num-eps", (u32 *)&config->num_eps);
+ of_property_read_u32(np, "ram-bits", (u32 *)&config->ram_bits);
+ of_property_read_u32(np, "power", (u32 *)&pdata->power);
+
+ ret = of_property_read_u32(np, "multipoint", &val);
+ if (!ret && val)
+ config->multipoint = true;
+
+ pdata->board_data = data;
+ pdata->config = config;
+
+ control_node = of_parse_phandle(np, "ctrl-module", 0);
+ if (control_node) {
+ control_pdev = of_find_device_by_node(control_node);
+ if (!control_pdev) {
+ dev_err(&pdev->dev, "Failed to get control device\n");
+ ret = -EINVAL;
goto err2;
-
- of_property_read_u32(np, "mode", (u32 *)&pdata->mode);
- of_property_read_u32(np, "interface-type",
- (u32 *)&data->interface_type);
- of_property_read_u32(np, "num-eps", (u32 *)&config->num_eps);
- of_property_read_u32(np, "ram-bits", (u32 *)&config->ram_bits);
- of_property_read_u32(np, "power", (u32 *)&pdata->power);
-
- ret = of_property_read_u32(np, "multipoint", &val);
- if (!ret && val)
- config->multipoint = true;
-
- pdata->board_data = data;
- pdata->config = config;
-
- control_node = of_parse_phandle(np, "ctrl-module", 0);
- if (control_node) {
- control_pdev = of_find_device_by_node(control_node);
- if (!control_pdev) {
- dev_err(&pdev->dev, "Failed to get control device\n");
- ret = -EINVAL;
- goto err2;
- }
- glue->control_otghs = &control_pdev->dev;
}
+ glue->control_otghs = &control_pdev->dev;
}
+
pdata->platform_ops = &omap2430_ops;
platform_set_drvdata(pdev, glue);
diff --git a/drivers/usb/musb/sunxi.c b/drivers/usb/musb/sunxi.c
index 2d201219ecff..832a41f9ee7d 100644
--- a/drivers/usb/musb/sunxi.c
+++ b/drivers/usb/musb/sunxi.c
@@ -105,13 +105,11 @@ static void sunxi_musb_work(struct work_struct *work)
devctl = readb(musb->mregs + SUNXI_MUSB_DEVCTL);
if (test_bit(SUNXI_MUSB_FL_HOSTMODE, &glue->flags)) {
set_bit(SUNXI_MUSB_FL_VBUS_ON, &glue->flags);
- musb->xceiv->otg->default_a = 1;
musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE;
MUSB_HST_MODE(musb);
devctl |= MUSB_DEVCTL_SESSION;
} else {
clear_bit(SUNXI_MUSB_FL_VBUS_ON, &glue->flags);
- musb->xceiv->otg->default_a = 0;
musb->xceiv->otg->state = OTG_STATE_B_IDLE;
MUSB_DEV_MODE(musb);
devctl &= ~MUSB_DEVCTL_SESSION;
@@ -347,7 +345,7 @@ static int sunxi_musb_set_mode(struct musb *musb, u8 mode)
if (glue->phy_mode == new_mode)
return 0;
- if (musb->port_mode != MUSB_PORT_MODE_DUAL_ROLE) {
+ if (musb->port_mode != MUSB_OTG) {
dev_err(musb->controller->parent,
"Error changing modes is only supported in dual role mode\n");
return -EINVAL;
@@ -651,10 +649,8 @@ static const struct musb_hdrc_config sunxi_musb_hdrc_config = {
.fifo_cfg_size = ARRAY_SIZE(sunxi_musb_mode_cfg),
.multipoint = true,
.dyn_fifo = true,
- .soft_con = true,
.num_eps = SUNXI_MUSB_MAX_EP_NUM,
.ram_bits = SUNXI_MUSB_RAM_BITS,
- .dma = 0,
};
static struct musb_hdrc_config sunxi_musb_hdrc_config_h3 = {
@@ -662,10 +658,8 @@ static struct musb_hdrc_config sunxi_musb_hdrc_config_h3 = {
.fifo_cfg_size = ARRAY_SIZE(sunxi_musb_mode_cfg_h3),
.multipoint = true,
.dyn_fifo = true,
- .soft_con = true,
.num_eps = SUNXI_MUSB_MAX_EP_NUM_H3,
.ram_bits = SUNXI_MUSB_RAM_BITS,
- .dma = 0,
};
@@ -690,19 +684,19 @@ static int sunxi_musb_probe(struct platform_device *pdev)
switch (usb_get_dr_mode(&pdev->dev)) {
#if defined CONFIG_USB_MUSB_DUAL_ROLE || defined CONFIG_USB_MUSB_HOST
case USB_DR_MODE_HOST:
- pdata.mode = MUSB_PORT_MODE_HOST;
+ pdata.mode = MUSB_HOST;
glue->phy_mode = PHY_MODE_USB_HOST;
break;
#endif
#if defined CONFIG_USB_MUSB_DUAL_ROLE || defined CONFIG_USB_MUSB_GADGET
case USB_DR_MODE_PERIPHERAL:
- pdata.mode = MUSB_PORT_MODE_GADGET;
+ pdata.mode = MUSB_PERIPHERAL;
glue->phy_mode = PHY_MODE_USB_DEVICE;
break;
#endif
#ifdef CONFIG_USB_MUSB_DUAL_ROLE
case USB_DR_MODE_OTG:
- pdata.mode = MUSB_PORT_MODE_DUAL_ROLE;
+ pdata.mode = MUSB_OTG;
glue->phy_mode = PHY_MODE_USB_OTG;
break;
#endif
diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c
index 27b4a77a9e23..73538d1d0524 100644
--- a/drivers/usb/musb/ux500.c
+++ b/drivers/usb/musb/ux500.c
@@ -62,7 +62,6 @@ static void ux500_musb_set_vbus(struct musb *musb, int is_on)
} else {
musb->is_active = 1;
- musb->xceiv->otg->default_a = 1;
musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE;
devctl |= MUSB_DEVCTL_SESSION;
MUSB_HST_MODE(musb);
@@ -73,7 +72,6 @@ static void ux500_musb_set_vbus(struct musb *musb, int is_on)
/* NOTE: we're skipping A_WAIT_VFALL -> A_IDLE and jumping
* right to B_IDLE...
*/
- musb->xceiv->otg->default_a = 0;
devctl &= ~MUSB_DEVCTL_SESSION;
MUSB_DEV_MODE(musb);
}
diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig
index 0f8ab981d572..d7312eed6088 100644
--- a/drivers/usb/phy/Kconfig
+++ b/drivers/usb/phy/Kconfig
@@ -159,6 +159,16 @@ config USB_MXS_PHY
MXS Phy is used by some of the i.MX SoCs, for example imx23/28/6x.
+config USB_TEGRA_PHY
+ tristate "NVIDIA Tegra USB PHY Driver"
+ depends on ARCH_TEGRA
+ select USB_COMMON
+ select USB_PHY
+ select USB_ULPI
+ help
+ This driver provides PHY support for the USB controllers found
+ on NVIDIA Tegra SoC's.
+
config USB_ULPI
bool "Generic ULPI Transceiver Driver"
depends on ARM || ARM64
diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile
index 25e579fb92b8..df1d99010079 100644
--- a/drivers/usb/phy/Makefile
+++ b/drivers/usb/phy/Makefile
@@ -16,7 +16,7 @@ obj-$(CONFIG_AM335X_CONTROL_USB) += phy-am335x-control.o
obj-$(CONFIG_AM335X_PHY_USB) += phy-am335x.o
obj-$(CONFIG_OMAP_OTG) += phy-omap-otg.o
obj-$(CONFIG_TWL6030_USB) += phy-twl6030-usb.o
-obj-$(CONFIG_USB_EHCI_TEGRA) += phy-tegra-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
diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c
index 66143ab8c043..aaf363f19714 100644
--- a/drivers/usb/phy/phy-ab8500-usb.c
+++ b/drivers/usb/phy/phy-ab8500-usb.c
@@ -505,15 +505,19 @@ static int abx500_usb_link_status_update(struct ab8500_usb *ab)
if (is_ab8500(ab->ab8500)) {
enum ab8500_usb_link_status lsts;
- abx500_get_register_interruptible(ab->dev,
+ ret = abx500_get_register_interruptible(ab->dev,
AB8500_USB, AB8500_USB_LINE_STAT_REG, &reg);
+ if (ret < 0)
+ return ret;
lsts = (reg >> 3) & 0x0F;
ret = ab8500_usb_link_status_update(ab, lsts);
} else if (is_ab8505(ab->ab8500)) {
enum ab8505_usb_link_status lsts;
- abx500_get_register_interruptible(ab->dev,
+ ret = abx500_get_register_interruptible(ab->dev,
AB8500_USB, AB8505_USB_LINE_STAT_REG, &reg);
+ if (ret < 0)
+ return ret;
lsts = (reg >> 3) & 0x1F;
ret = ab8505_usb_link_status_update(ab, lsts);
}
diff --git a/drivers/usb/phy/phy-am335x.c b/drivers/usb/phy/phy-am335x.c
index b36fa8b953d0..27bdb7222527 100644
--- a/drivers/usb/phy/phy-am335x.c
+++ b/drivers/usb/phy/phy-am335x.c
@@ -96,8 +96,7 @@ static int am335x_phy_remove(struct platform_device *pdev)
#ifdef CONFIG_PM_SLEEP
static int am335x_phy_suspend(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct am335x_phy *am_phy = platform_get_drvdata(pdev);
+ struct am335x_phy *am_phy = dev_get_drvdata(dev);
/*
* Enable phy wakeup only if dev->power.can_wakeup is true.
@@ -117,8 +116,7 @@ static int am335x_phy_suspend(struct device *dev)
static int am335x_phy_resume(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct am335x_phy *am_phy = platform_get_drvdata(pdev);
+ struct am335x_phy *am_phy = dev_get_drvdata(dev);
phy_ctrl_power(am_phy->phy_ctrl, am_phy->id, am_phy->dr_mode, true);
diff --git a/drivers/usb/phy/phy-fsl-usb.c b/drivers/usb/phy/phy-fsl-usb.c
index 900875f326d7..f7c96d209eda 100644
--- a/drivers/usb/phy/phy-fsl-usb.c
+++ b/drivers/usb/phy/phy-fsl-usb.c
@@ -861,6 +861,7 @@ int usb_otg_start(struct platform_device *pdev)
if (pdata->init && pdata->init(pdev) != 0)
return -EINVAL;
+#ifdef CONFIG_PPC32
if (pdata->big_endian_mmio) {
_fsl_readl = _fsl_readl_be;
_fsl_writel = _fsl_writel_be;
@@ -868,6 +869,7 @@ int usb_otg_start(struct platform_device *pdev)
_fsl_readl = _fsl_readl_le;
_fsl_writel = _fsl_writel_le;
}
+#endif
/* request irq */
p_otg->irq = platform_get_irq(pdev, 0);
@@ -958,7 +960,7 @@ int usb_otg_start(struct platform_device *pdev)
/*
* state file in sysfs
*/
-static int show_fsl_usb2_otg_state(struct device *dev,
+static ssize_t show_fsl_usb2_otg_state(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct otg_fsm *fsm = &fsl_otg_dev->fsm;
diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c
index e5aa24c1e4fd..1b1bb0ad40c3 100644
--- a/drivers/usb/phy/phy-mxs-usb.c
+++ b/drivers/usb/phy/phy-mxs-usb.c
@@ -563,7 +563,7 @@ static enum usb_charger_type mxs_charger_primary_detection(struct mxs_phy *x)
regmap_read(regmap, ANADIG_USB1_CHRG_DET_STAT, &val);
if (!(val & ANADIG_USB1_CHRG_DET_STAT_CHRG_DETECTED)) {
chgr_type = SDP_TYPE;
- dev_dbg(x->phy.dev, "It is a stardard downstream port\n");
+ dev_dbg(x->phy.dev, "It is a standard downstream port\n");
}
/* Disable charger detector */
diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c
index 0e8d23e51732..ea7ef1dc0b42 100644
--- a/drivers/usb/phy/phy-tegra-usb.c
+++ b/drivers/usb/phy/phy-tegra-usb.c
@@ -236,13 +236,83 @@ static void set_phcd(struct tegra_usb_phy *phy, bool enable)
static int utmip_pad_open(struct tegra_usb_phy *phy)
{
+ int ret;
+
phy->pad_clk = devm_clk_get(phy->u_phy.dev, "utmi-pads");
if (IS_ERR(phy->pad_clk)) {
- pr_err("%s: can't get utmip pad clock\n", __func__);
- return PTR_ERR(phy->pad_clk);
+ ret = PTR_ERR(phy->pad_clk);
+ dev_err(phy->u_phy.dev,
+ "Failed to get UTMIP pad clock: %d\n", ret);
+ return ret;
}
- return 0;
+ phy->pad_rst = devm_reset_control_get_optional_shared(
+ phy->u_phy.dev, "utmi-pads");
+ if (IS_ERR(phy->pad_rst)) {
+ ret = PTR_ERR(phy->pad_rst);
+ dev_err(phy->u_phy.dev,
+ "Failed to get UTMI-pads reset: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(phy->pad_clk);
+ if (ret) {
+ dev_err(phy->u_phy.dev,
+ "Failed to enable UTMI-pads clock: %d\n", ret);
+ return ret;
+ }
+
+ spin_lock(&utmip_pad_lock);
+
+ ret = reset_control_deassert(phy->pad_rst);
+ if (ret) {
+ dev_err(phy->u_phy.dev,
+ "Failed to initialize UTMI-pads reset: %d\n", ret);
+ goto unlock;
+ }
+
+ ret = reset_control_assert(phy->pad_rst);
+ if (ret) {
+ dev_err(phy->u_phy.dev,
+ "Failed to assert UTMI-pads reset: %d\n", ret);
+ goto unlock;
+ }
+
+ udelay(1);
+
+ ret = reset_control_deassert(phy->pad_rst);
+ if (ret)
+ dev_err(phy->u_phy.dev,
+ "Failed to deassert UTMI-pads reset: %d\n", ret);
+unlock:
+ spin_unlock(&utmip_pad_lock);
+
+ clk_disable_unprepare(phy->pad_clk);
+
+ return ret;
+}
+
+static int utmip_pad_close(struct tegra_usb_phy *phy)
+{
+ int ret;
+
+ ret = clk_prepare_enable(phy->pad_clk);
+ if (ret) {
+ dev_err(phy->u_phy.dev,
+ "Failed to enable UTMI-pads clock: %d\n", ret);
+ return ret;
+ }
+
+ ret = reset_control_assert(phy->pad_rst);
+ if (ret)
+ dev_err(phy->u_phy.dev,
+ "Failed to assert UTMI-pads reset: %d\n", ret);
+
+ udelay(1);
+
+ clk_disable_unprepare(phy->pad_clk);
+
+ return ret;
}
static void utmip_pad_power_on(struct tegra_usb_phy *phy)
@@ -282,7 +352,7 @@ static int utmip_pad_power_off(struct tegra_usb_phy *phy)
void __iomem *base = phy->pad_regs;
if (!utmip_pad_count) {
- pr_err("%s: utmip pad already powered off\n", __func__);
+ dev_err(phy->u_phy.dev, "UTMIP pad already powered off\n");
return -EINVAL;
}
@@ -338,7 +408,8 @@ static void utmi_phy_clk_disable(struct tegra_usb_phy *phy)
set_phcd(phy, true);
if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID, 0) < 0)
- pr_err("%s: timeout waiting for phy to stabilize\n", __func__);
+ dev_err(phy->u_phy.dev,
+ "Timeout waiting for PHY to stabilize on disable\n");
}
static void utmi_phy_clk_enable(struct tegra_usb_phy *phy)
@@ -370,7 +441,8 @@ static void utmi_phy_clk_enable(struct tegra_usb_phy *phy)
if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID,
USB_PHY_CLK_VALID))
- pr_err("%s: timeout waiting for phy to stabilize\n", __func__);
+ dev_err(phy->u_phy.dev,
+ "Timeout waiting for PHY to stabilize on enable\n");
}
static int utmi_phy_power_on(struct tegra_usb_phy *phy)
@@ -617,15 +689,15 @@ static int ulpi_phy_power_on(struct tegra_usb_phy *phy)
ret = gpio_direction_output(phy->reset_gpio, 0);
if (ret < 0) {
- dev_err(phy->u_phy.dev, "gpio %d not set to 0\n",
- phy->reset_gpio);
+ dev_err(phy->u_phy.dev, "GPIO %d not set to 0: %d\n",
+ phy->reset_gpio, ret);
return ret;
}
msleep(5);
ret = gpio_direction_output(phy->reset_gpio, 1);
if (ret < 0) {
- dev_err(phy->u_phy.dev, "gpio %d not set to 1\n",
- phy->reset_gpio);
+ dev_err(phy->u_phy.dev, "GPIO %d not set to 1: %d\n",
+ phy->reset_gpio, ret);
return ret;
}
@@ -661,13 +733,13 @@ static int ulpi_phy_power_on(struct tegra_usb_phy *phy)
/* Fix VbusInvalid due to floating VBUS */
ret = usb_phy_io_write(phy->ulpi, 0x40, 0x08);
if (ret) {
- pr_err("%s: ulpi write failed\n", __func__);
+ dev_err(phy->u_phy.dev, "ULPI write failed: %d\n", ret);
return ret;
}
ret = usb_phy_io_write(phy->ulpi, 0x80, 0x0B);
if (ret) {
- pr_err("%s: ulpi write failed\n", __func__);
+ dev_err(phy->u_phy.dev, "ULPI write failed: %d\n", ret);
return ret;
}
@@ -694,6 +766,9 @@ static void tegra_usb_phy_close(struct tegra_usb_phy *phy)
if (!IS_ERR(phy->vbus))
regulator_disable(phy->vbus);
+ if (!phy->is_ulpi_phy)
+ utmip_pad_close(phy);
+
clk_disable_unprepare(phy->pll_u);
}
@@ -728,28 +803,30 @@ static int ulpi_open(struct tegra_usb_phy *phy)
phy->clk = devm_clk_get(phy->u_phy.dev, "ulpi-link");
if (IS_ERR(phy->clk)) {
- pr_err("%s: can't get ulpi clock\n", __func__);
- return PTR_ERR(phy->clk);
+ err = PTR_ERR(phy->clk);
+ dev_err(phy->u_phy.dev, "Failed to get ULPI clock: %d\n", err);
+ return err;
}
err = devm_gpio_request(phy->u_phy.dev, phy->reset_gpio,
"ulpi_phy_reset_b");
if (err < 0) {
- dev_err(phy->u_phy.dev, "request failed for gpio: %d\n",
- phy->reset_gpio);
+ dev_err(phy->u_phy.dev, "Request failed for GPIO %d: %d\n",
+ phy->reset_gpio, err);
return err;
}
err = gpio_direction_output(phy->reset_gpio, 0);
if (err < 0) {
- dev_err(phy->u_phy.dev, "gpio %d direction not set to output\n",
- phy->reset_gpio);
+ dev_err(phy->u_phy.dev,
+ "GPIO %d direction not set to output: %d\n",
+ phy->reset_gpio, err);
return err;
}
phy->ulpi = otg_ulpi_create(&ulpi_viewport_access_ops, 0);
if (!phy->ulpi) {
- dev_err(phy->u_phy.dev, "otg_ulpi_create returned NULL\n");
+ dev_err(phy->u_phy.dev, "Failed to create ULPI OTG\n");
err = -ENOMEM;
return err;
}
@@ -766,8 +843,10 @@ static int tegra_usb_phy_init(struct tegra_usb_phy *phy)
phy->pll_u = devm_clk_get(phy->u_phy.dev, "pll_u");
if (IS_ERR(phy->pll_u)) {
- pr_err("Can't get pll_u clock\n");
- return PTR_ERR(phy->pll_u);
+ err = PTR_ERR(phy->pll_u);
+ dev_err(phy->u_phy.dev,
+ "Failed to get pll_u clock: %d\n", err);
+ return err;
}
err = clk_prepare_enable(phy->pll_u);
@@ -782,7 +861,8 @@ static int tegra_usb_phy_init(struct tegra_usb_phy *phy)
}
}
if (!phy->freq) {
- pr_err("invalid pll_u parent rate %ld\n", parent_rate);
+ dev_err(phy->u_phy.dev, "Invalid pll_u parent rate %ld\n",
+ parent_rate);
err = -EINVAL;
goto fail;
}
@@ -791,7 +871,7 @@ static int tegra_usb_phy_init(struct tegra_usb_phy *phy)
err = regulator_enable(phy->vbus);
if (err) {
dev_err(phy->u_phy.dev,
- "failed to enable usb vbus regulator: %d\n",
+ "Failed to enable USB VBUS regulator: %d\n",
err);
goto fail;
}
@@ -855,7 +935,8 @@ static int read_utmi_param(struct platform_device *pdev, const char *param,
int err = of_property_read_u32(pdev->dev.of_node, param, &value);
*dest = (u8)value;
if (err < 0)
- dev_err(&pdev->dev, "Failed to read USB UTMI parameter %s: %d\n",
+ dev_err(&pdev->dev,
+ "Failed to read USB UTMI parameter %s: %d\n",
param, err);
return err;
}
@@ -871,14 +952,14 @@ static int utmi_phy_probe(struct tegra_usb_phy *tegra_phy,
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (!res) {
- dev_err(&pdev->dev, "Failed to get UTMI Pad regs\n");
+ dev_err(&pdev->dev, "Failed to get UTMI pad regs\n");
return -ENXIO;
}
tegra_phy->pad_regs = devm_ioremap(&pdev->dev, res->start,
resource_size(res));
if (!tegra_phy->pad_regs) {
- dev_err(&pdev->dev, "Failed to remap UTMI Pad regs\n");
+ dev_err(&pdev->dev, "Failed to remap UTMI pad regs\n");
return -ENOMEM;
}
@@ -1020,15 +1101,16 @@ static int tegra_usb_phy_probe(struct platform_device *pdev)
tegra_phy->reset_gpio =
of_get_named_gpio(np, "nvidia,phy-reset-gpio", 0);
if (!gpio_is_valid(tegra_phy->reset_gpio)) {
- dev_err(&pdev->dev, "invalid gpio: %d\n",
- tegra_phy->reset_gpio);
+ dev_err(&pdev->dev,
+ "Invalid GPIO: %d\n", tegra_phy->reset_gpio);
return tegra_phy->reset_gpio;
}
tegra_phy->config = NULL;
break;
default:
- dev_err(&pdev->dev, "phy_type is invalid or unsupported\n");
+ dev_err(&pdev->dev, "phy_type %u is invalid or unsupported\n",
+ phy_type);
return -EINVAL;
}
diff --git a/drivers/usb/phy/phy.c b/drivers/usb/phy/phy.c
index bceb2c9988dd..0277f62739a2 100644
--- a/drivers/usb/phy/phy.c
+++ b/drivers/usb/phy/phy.c
@@ -27,7 +27,6 @@
#define DEFAULT_ACA_CUR_MAX 5000
static LIST_HEAD(phy_list);
-static LIST_HEAD(phy_bind_list);
static DEFINE_SPINLOCK(phy_lock);
struct phy_devm {
@@ -50,24 +49,6 @@ static struct usb_phy *__usb_find_phy(struct list_head *list,
return ERR_PTR(-ENODEV);
}
-static struct usb_phy *__usb_find_phy_dev(struct device *dev,
- struct list_head *list, u8 index)
-{
- struct usb_phy_bind *phy_bind = NULL;
-
- list_for_each_entry(phy_bind, list, list) {
- if (!(strcmp(phy_bind->dev_name, dev_name(dev))) &&
- phy_bind->index == index) {
- if (phy_bind->phy)
- return phy_bind->phy;
- else
- return ERR_PTR(-EPROBE_DEFER);
- }
- }
-
- return ERR_PTR(-ENODEV);
-}
-
static struct usb_phy *__of_usb_find_phy(struct device_node *node)
{
struct usb_phy *phy;
@@ -585,72 +566,6 @@ struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev,
EXPORT_SYMBOL_GPL(devm_usb_get_phy_by_phandle);
/**
- * usb_get_phy_dev - find the USB PHY
- * @dev - device that requests this phy
- * @index - the index of the phy
- *
- * Returns the phy driver, after getting a refcount to it; or
- * -ENODEV if there is no such phy. The caller is responsible for
- * calling usb_put_phy() to release that count.
- *
- * For use by USB host and peripheral drivers.
- */
-struct usb_phy *usb_get_phy_dev(struct device *dev, u8 index)
-{
- struct usb_phy *phy = NULL;
- unsigned long flags;
-
- spin_lock_irqsave(&phy_lock, flags);
-
- phy = __usb_find_phy_dev(dev, &phy_bind_list, index);
- if (IS_ERR(phy) || !try_module_get(phy->dev->driver->owner)) {
- dev_dbg(dev, "unable to find transceiver\n");
- if (!IS_ERR(phy))
- phy = ERR_PTR(-ENODEV);
-
- goto err0;
- }
-
- get_device(phy->dev);
-
-err0:
- spin_unlock_irqrestore(&phy_lock, flags);
-
- return phy;
-}
-EXPORT_SYMBOL_GPL(usb_get_phy_dev);
-
-/**
- * devm_usb_get_phy_dev - find the USB PHY using device ptr and index
- * @dev - device that requests this phy
- * @index - the index of the phy
- *
- * Gets the phy using usb_get_phy_dev(), and associates a device with it using
- * devres. On driver detach, release function is invoked on the devres data,
- * then, devres data is freed.
- *
- * For use by USB host and peripheral drivers.
- */
-struct usb_phy *devm_usb_get_phy_dev(struct device *dev, u8 index)
-{
- struct usb_phy **ptr, *phy;
-
- ptr = devres_alloc(devm_usb_phy_release, sizeof(*ptr), GFP_KERNEL);
- if (!ptr)
- return NULL;
-
- phy = usb_get_phy_dev(dev, index);
- if (!IS_ERR(phy)) {
- *ptr = phy;
- devres_add(dev, ptr);
- } else
- devres_free(ptr);
-
- return phy;
-}
-EXPORT_SYMBOL_GPL(devm_usb_get_phy_dev);
-
-/**
* devm_usb_put_phy - release the USB PHY
* @dev - device that wants to release this phy
* @phy - the phy returned by devm_usb_get_phy()
@@ -745,7 +660,6 @@ EXPORT_SYMBOL_GPL(usb_add_phy);
*/
int usb_add_phy_dev(struct usb_phy *x)
{
- struct usb_phy_bind *phy_bind;
unsigned long flags;
int ret;
@@ -762,13 +676,9 @@ int usb_add_phy_dev(struct usb_phy *x)
ATOMIC_INIT_NOTIFIER_HEAD(&x->notifier);
spin_lock_irqsave(&phy_lock, flags);
- list_for_each_entry(phy_bind, &phy_bind_list, list)
- if (!(strcmp(phy_bind->phy_dev_name, dev_name(x->dev))))
- phy_bind->phy = x;
-
list_add_tail(&x->head, &phy_list);
-
spin_unlock_irqrestore(&phy_lock, flags);
+
return 0;
}
EXPORT_SYMBOL_GPL(usb_add_phy_dev);
@@ -782,54 +692,15 @@ EXPORT_SYMBOL_GPL(usb_add_phy_dev);
void usb_remove_phy(struct usb_phy *x)
{
unsigned long flags;
- struct usb_phy_bind *phy_bind;
spin_lock_irqsave(&phy_lock, flags);
- if (x) {
- list_for_each_entry(phy_bind, &phy_bind_list, list)
- if (phy_bind->phy == x)
- phy_bind->phy = NULL;
+ if (x)
list_del(&x->head);
- }
spin_unlock_irqrestore(&phy_lock, flags);
}
EXPORT_SYMBOL_GPL(usb_remove_phy);
/**
- * usb_bind_phy - bind the phy and the controller that uses the phy
- * @dev_name: the device name of the device that will bind to the phy
- * @index: index to specify the port number
- * @phy_dev_name: the device name of the phy
- *
- * Fills the phy_bind structure with the dev_name and phy_dev_name. This will
- * be used when the phy driver registers the phy and when the controller
- * requests this phy.
- *
- * To be used by platform specific initialization code.
- */
-int usb_bind_phy(const char *dev_name, u8 index,
- const char *phy_dev_name)
-{
- struct usb_phy_bind *phy_bind;
- unsigned long flags;
-
- phy_bind = kzalloc(sizeof(*phy_bind), GFP_KERNEL);
- if (!phy_bind)
- return -ENOMEM;
-
- phy_bind->dev_name = dev_name;
- phy_bind->phy_dev_name = phy_dev_name;
- phy_bind->index = index;
-
- spin_lock_irqsave(&phy_lock, flags);
- list_add_tail(&phy_bind->list, &phy_bind_list);
- spin_unlock_irqrestore(&phy_lock, flags);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(usb_bind_phy);
-
-/**
* usb_phy_set_event - set event to phy event
* @x: the phy returned by usb_get_phy();
*
diff --git a/drivers/usb/renesas_usbhs/Kconfig b/drivers/usb/renesas_usbhs/Kconfig
index b26d7c339c05..7fdbff23ae8b 100644
--- a/drivers/usb/renesas_usbhs/Kconfig
+++ b/drivers/usb/renesas_usbhs/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Renesas USBHS Controller Drivers
#
diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c
index 4310df46639d..a3e1290d682d 100644
--- a/drivers/usb/renesas_usbhs/common.c
+++ b/drivers/usb/renesas_usbhs/common.c
@@ -5,6 +5,7 @@
* Copyright (C) 2011 Renesas Solutions Corp.
* Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
*/
+#include <linux/clk.h>
#include <linux/err.h>
#include <linux/gpio.h>
#include <linux/io.h>
@@ -12,6 +13,7 @@
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/pm_runtime.h>
+#include <linux/reset.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include "common.h"
@@ -290,6 +292,79 @@ static void usbhsc_set_buswait(struct usbhs_priv *priv)
usbhs_bset(priv, BUSWAIT, 0x000F, wait);
}
+static bool usbhsc_is_multi_clks(struct usbhs_priv *priv)
+{
+ if (priv->dparam.type == USBHS_TYPE_RCAR_GEN3 ||
+ priv->dparam.type == USBHS_TYPE_RCAR_GEN3_WITH_PLL)
+ return true;
+
+ return false;
+}
+
+static int usbhsc_clk_get(struct device *dev, struct usbhs_priv *priv)
+{
+ if (!usbhsc_is_multi_clks(priv))
+ return 0;
+
+ /* The first clock should exist */
+ priv->clks[0] = of_clk_get(dev->of_node, 0);
+ if (IS_ERR(priv->clks[0]))
+ return PTR_ERR(priv->clks[0]);
+
+ /*
+ * To backward compatibility with old DT, this driver checks the return
+ * value if it's -ENOENT or not.
+ */
+ priv->clks[1] = of_clk_get(dev->of_node, 1);
+ if (PTR_ERR(priv->clks[1]) == -ENOENT)
+ priv->clks[1] = NULL;
+ else if (IS_ERR(priv->clks[1]))
+ return PTR_ERR(priv->clks[1]);
+
+ return 0;
+}
+
+static void usbhsc_clk_put(struct usbhs_priv *priv)
+{
+ int i;
+
+ if (!usbhsc_is_multi_clks(priv))
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(priv->clks); i++)
+ clk_put(priv->clks[i]);
+}
+
+static int usbhsc_clk_prepare_enable(struct usbhs_priv *priv)
+{
+ int i, ret;
+
+ if (!usbhsc_is_multi_clks(priv))
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(priv->clks); i++) {
+ ret = clk_prepare_enable(priv->clks[i]);
+ if (ret) {
+ while (--i >= 0)
+ clk_disable_unprepare(priv->clks[i]);
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static void usbhsc_clk_disable_unprepare(struct usbhs_priv *priv)
+{
+ int i;
+
+ if (!usbhsc_is_multi_clks(priv))
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(priv->clks); i++)
+ clk_disable_unprepare(priv->clks[i]);
+}
+
/*
* platform default param
*/
@@ -340,6 +415,10 @@ static void usbhsc_power_ctrl(struct usbhs_priv *priv, int enable)
/* enable PM */
pm_runtime_get_sync(dev);
+ /* enable clks */
+ if (usbhsc_clk_prepare_enable(priv))
+ return;
+
/* enable platform power */
usbhs_platform_call(priv, power_ctrl, pdev, priv->base, enable);
@@ -352,6 +431,9 @@ static void usbhsc_power_ctrl(struct usbhs_priv *priv, int enable)
/* disable platform power */
usbhs_platform_call(priv, power_ctrl, pdev, priv->base, enable);
+ /* disable clks */
+ usbhsc_clk_disable_unprepare(priv);
+
/* disable PM */
pm_runtime_put_sync(dev);
}
@@ -478,6 +560,10 @@ static const struct of_device_id usbhs_of_match[] = {
.data = (void *)USBHS_TYPE_RCAR_GEN3,
},
{
+ .compatible = "renesas,usbhs-r8a77990",
+ .data = (void *)USBHS_TYPE_RCAR_GEN3_WITH_PLL,
+ },
+ {
.compatible = "renesas,usbhs-r8a77995",
.data = (void *)USBHS_TYPE_RCAR_GEN3_WITH_PLL,
},
@@ -574,6 +660,10 @@ static int usbhs_probe(struct platform_device *pdev)
return PTR_ERR(priv->edev);
}
+ priv->rsts = devm_reset_control_array_get_optional_shared(&pdev->dev);
+ if (IS_ERR(priv->rsts))
+ return PTR_ERR(priv->rsts);
+
/*
* care platform info
*/
@@ -591,15 +681,6 @@ static int usbhs_probe(struct platform_device *pdev)
break;
case USBHS_TYPE_RCAR_GEN3_WITH_PLL:
priv->pfunc = usbhs_rcar3_with_pll_ops;
- if (!IS_ERR_OR_NULL(priv->edev)) {
- priv->nb.notifier_call = priv->pfunc.notifier;
- ret = devm_extcon_register_notifier(&pdev->dev,
- priv->edev,
- EXTCON_USB_HOST,
- &priv->nb);
- if (ret < 0)
- dev_err(&pdev->dev, "no notifier registered\n");
- }
break;
case USBHS_TYPE_RZA1:
priv->pfunc = usbhs_rza1_ops;
@@ -658,6 +739,14 @@ static int usbhs_probe(struct platform_device *pdev)
/* dev_set_drvdata should be called after usbhs_mod_init */
platform_set_drvdata(pdev, priv);
+ ret = reset_control_deassert(priv->rsts);
+ if (ret)
+ goto probe_fail_rst;
+
+ ret = usbhsc_clk_get(&pdev->dev, priv);
+ if (ret)
+ goto probe_fail_clks;
+
/*
* deviece reset here because
* USB device might be used in boot loader.
@@ -711,6 +800,10 @@ static int usbhs_probe(struct platform_device *pdev)
return ret;
probe_end_mod_exit:
+ usbhsc_clk_put(priv);
+probe_fail_clks:
+ reset_control_assert(priv->rsts);
+probe_fail_rst:
usbhs_mod_remove(priv);
probe_end_fifo_exit:
usbhs_fifo_remove(priv);
@@ -739,6 +832,8 @@ static int usbhs_remove(struct platform_device *pdev)
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);
diff --git a/drivers/usb/renesas_usbhs/common.h b/drivers/usb/renesas_usbhs/common.h
index f619afeae2b8..3777af848a35 100644
--- a/drivers/usb/renesas_usbhs/common.h
+++ b/drivers/usb/renesas_usbhs/common.h
@@ -8,8 +8,10 @@
#ifndef RENESAS_USB_DRIVER_H
#define RENESAS_USB_DRIVER_H
+#include <linux/clk.h>
#include <linux/extcon.h>
#include <linux/platform_device.h>
+#include <linux/reset.h>
#include <linux/usb/renesas_usbhs.h>
struct usbhs_priv;
@@ -255,7 +257,6 @@ struct usbhs_priv {
struct platform_device *pdev;
struct extcon_dev *edev;
- struct notifier_block nb;
spinlock_t lock;
@@ -276,8 +277,9 @@ struct usbhs_priv {
*/
struct usbhs_fifo_info fifo_info;
- struct usb_phy *usb_phy;
struct phy *phy;
+ struct reset_control *rsts;
+ struct clk *clks[2];
};
/*
diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c
index 34ee9ebe12a3..59cac40aafcc 100644
--- a/drivers/usb/renesas_usbhs/mod_gadget.c
+++ b/drivers/usb/renesas_usbhs/mod_gadget.c
@@ -502,6 +502,7 @@ static int usbhsg_irq_ctrl_stage(struct usbhs_priv *priv,
case READ_STATUS_STAGE:
case WRITE_STATUS_STAGE:
usbhs_dcp_control_transfer_done(pipe);
+ /* fall through */
default:
return ret;
}
@@ -1068,7 +1069,7 @@ int usbhs_mod_gadget_probe(struct usbhs_priv *priv)
if (!gpriv)
return -ENOMEM;
- uep = kzalloc(sizeof(struct usbhsg_uep) * pipe_size, GFP_KERNEL);
+ uep = kcalloc(pipe_size, sizeof(struct usbhsg_uep), GFP_KERNEL);
if (!uep) {
ret = -ENOMEM;
goto usbhs_mod_gadget_probe_err_gpriv;
diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c
index 9677e0e31475..c4922b96c93b 100644
--- a/drivers/usb/renesas_usbhs/pipe.c
+++ b/drivers/usb/renesas_usbhs/pipe.c
@@ -803,7 +803,8 @@ int usbhs_pipe_probe(struct usbhs_priv *priv)
return -EINVAL;
}
- info->pipe = kzalloc(sizeof(struct usbhs_pipe) * pipe_size, GFP_KERNEL);
+ info->pipe = kcalloc(pipe_size, sizeof(struct usbhs_pipe),
+ GFP_KERNEL);
if (!info->pipe)
return -ENOMEM;
diff --git a/drivers/usb/renesas_usbhs/rcar2.c b/drivers/usb/renesas_usbhs/rcar2.c
index 85a0e0933917..0027092b1118 100644
--- a/drivers/usb/renesas_usbhs/rcar2.c
+++ b/drivers/usb/renesas_usbhs/rcar2.c
@@ -8,7 +8,6 @@
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/phy/phy.h>
-#include <linux/usb/phy.h>
#include "common.h"
#include "rcar2.h"
@@ -26,16 +25,6 @@ static int usbhs_rcar2_hardware_init(struct platform_device *pdev)
return 0;
}
- if (IS_ENABLED(CONFIG_USB_PHY)) {
- struct usb_phy *usb_phy = usb_get_phy_dev(&pdev->dev, 0);
-
- if (IS_ERR(usb_phy))
- return PTR_ERR(usb_phy);
-
- priv->usb_phy = usb_phy;
- return 0;
- }
-
return -ENXIO;
}
@@ -48,11 +37,6 @@ static int usbhs_rcar2_hardware_exit(struct platform_device *pdev)
priv->phy = NULL;
}
- if (priv->usb_phy) {
- usb_put_phy(priv->usb_phy);
- priv->usb_phy = NULL;
- }
-
return 0;
}
@@ -75,19 +59,6 @@ static int usbhs_rcar2_power_ctrl(struct platform_device *pdev,
}
}
- if (priv->usb_phy) {
- if (enable) {
- retval = usb_phy_init(priv->usb_phy);
-
- if (!retval)
- retval = usb_phy_set_suspend(priv->usb_phy, 0);
- } else {
- usb_phy_set_suspend(priv->usb_phy, 1);
- usb_phy_shutdown(priv->usb_phy);
- retval = 0;
- }
- }
-
return retval;
}
diff --git a/drivers/usb/renesas_usbhs/rcar3.c b/drivers/usb/renesas_usbhs/rcar3.c
index d0ea4ff89622..aa3820448286 100644
--- a/drivers/usb/renesas_usbhs/rcar3.c
+++ b/drivers/usb/renesas_usbhs/rcar3.c
@@ -27,7 +27,6 @@
* Remarks: bit[31:11] and bit[9:6] should be 0
*/
#define UGCTRL2_RESERVED_3 0x00000001 /* bit[3:0] should be B'0001 */
-#define UGCTRL2_USB0SEL_EHCI 0x00000010
#define UGCTRL2_USB0SEL_HSUSB 0x00000020
#define UGCTRL2_USB0SEL_OTG 0x00000030
#define UGCTRL2_VBUSSEL 0x00000400
@@ -50,14 +49,6 @@ static void usbhs_rcar3_set_ugctrl2(struct usbhs_priv *priv, u32 val)
usbhs_write32(priv, UGCTRL2, val | UGCTRL2_RESERVED_3);
}
-static void usbhs_rcar3_set_usbsel(struct usbhs_priv *priv, bool ehci)
-{
- if (ehci)
- usbhs_rcar3_set_ugctrl2(priv, UGCTRL2_USB0SEL_EHCI);
- else
- usbhs_rcar3_set_ugctrl2(priv, UGCTRL2_USB0SEL_HSUSB);
-}
-
static int usbhs_rcar3_power_ctrl(struct platform_device *pdev,
void __iomem *base, int enable)
{
@@ -83,14 +74,11 @@ static int usbhs_rcar3_power_and_pll_ctrl(struct platform_device *pdev,
struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev);
u32 val;
int timeout = 1000;
- bool is_host = false;
if (enable) {
usbhs_write32(priv, UGCTRL, 0); /* release PLLRESET */
- if (priv->edev)
- is_host = extcon_get_state(priv->edev, EXTCON_USB_HOST);
-
- usbhs_rcar3_set_usbsel(priv, is_host);
+ usbhs_rcar3_set_ugctrl2(priv,
+ UGCTRL2_USB0SEL_OTG | UGCTRL2_VBUSSEL);
usbhs_bset(priv, LPSTS, LPSTS_SUSPM, LPSTS_SUSPM);
do {
@@ -112,16 +100,6 @@ static int usbhs_rcar3_get_id(struct platform_device *pdev)
return USBHS_GADGET;
}
-static int usbhs_rcar3_notifier(struct notifier_block *nb, unsigned long event,
- void *data)
-{
- struct usbhs_priv *priv = container_of(nb, struct usbhs_priv, nb);
-
- usbhs_rcar3_set_usbsel(priv, !!event);
-
- return NOTIFY_DONE;
-}
-
const struct renesas_usbhs_platform_callback usbhs_rcar3_ops = {
.power_ctrl = usbhs_rcar3_power_ctrl,
.get_id = usbhs_rcar3_get_id,
@@ -130,5 +108,4 @@ const struct renesas_usbhs_platform_callback usbhs_rcar3_ops = {
const struct renesas_usbhs_platform_callback usbhs_rcar3_with_pll_ops = {
.power_ctrl = usbhs_rcar3_power_and_pll_ctrl,
.get_id = usbhs_rcar3_get_id,
- .notifier = usbhs_rcar3_notifier,
};
diff --git a/drivers/usb/roles/intel-xhci-usb-role-switch.c b/drivers/usb/roles/intel-xhci-usb-role-switch.c
index de72eedb762e..277de96181f9 100644
--- a/drivers/usb/roles/intel-xhci-usb-role-switch.c
+++ b/drivers/usb/roles/intel-xhci-usb-role-switch.c
@@ -18,6 +18,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/usb/role.h>
/* register definition */
@@ -38,20 +39,6 @@ struct intel_xhci_usb_data {
void __iomem *base;
};
-struct intel_xhci_acpi_match {
- const char *hid;
- int hrv;
-};
-
-/*
- * ACPI IDs for PMICs which do not support separate data and power role
- * detection (USB ACA detection for micro USB OTG), we allow userspace to
- * change the role manually on these.
- */
-static const struct intel_xhci_acpi_match allow_userspace_ctrl_ids[] = {
- { "INT33F4", 3 }, /* X-Powers AXP288 PMIC */
-};
-
static int intel_xhci_usb_set_role(struct device *dev, enum usb_role role)
{
struct intel_xhci_usb_data *data = dev_get_drvdata(dev);
@@ -70,6 +57,8 @@ static int intel_xhci_usb_set_role(struct device *dev, enum usb_role role)
return -EIO;
}
+ pm_runtime_get_sync(dev);
+
/* Set idpin value as requested */
val = readl(data->base + DUAL_ROLE_CFG0);
switch (role) {
@@ -98,13 +87,17 @@ static int intel_xhci_usb_set_role(struct device *dev, enum usb_role role)
/* Polling on CFG1 register to confirm mode switch.*/
do {
val = readl(data->base + DUAL_ROLE_CFG1);
- if (!!(val & HOST_MODE) == (role == USB_ROLE_HOST))
+ if (!!(val & HOST_MODE) == (role == USB_ROLE_HOST)) {
+ pm_runtime_put(dev);
return 0;
+ }
/* Interval for polling is set to about 5 - 10 ms */
usleep_range(5000, 10000);
} while (time_before(jiffies, timeout));
+ pm_runtime_put(dev);
+
dev_warn(dev, "Timeout waiting for role-switch\n");
return -ETIMEDOUT;
}
@@ -115,7 +108,9 @@ static enum usb_role intel_xhci_usb_get_role(struct device *dev)
enum usb_role role;
u32 val;
+ pm_runtime_get_sync(dev);
val = readl(data->base + DUAL_ROLE_CFG0);
+ pm_runtime_put(dev);
if (!(val & SW_IDPIN))
role = USB_ROLE_HOST;
@@ -127,9 +122,10 @@ static enum usb_role intel_xhci_usb_get_role(struct device *dev)
return role;
}
-static struct usb_role_switch_desc sw_desc = {
+static const struct usb_role_switch_desc sw_desc = {
.set = intel_xhci_usb_set_role,
.get = intel_xhci_usb_get_role,
+ .allow_userspace_control = true,
};
static int intel_xhci_usb_probe(struct platform_device *pdev)
@@ -137,28 +133,27 @@ static int intel_xhci_usb_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct intel_xhci_usb_data *data;
struct resource *res;
- int i;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -EINVAL;
data->base = devm_ioremap_nocache(dev, res->start, resource_size(res));
if (!data->base)
return -ENOMEM;
- for (i = 0; i < ARRAY_SIZE(allow_userspace_ctrl_ids); i++)
- if (acpi_dev_present(allow_userspace_ctrl_ids[i].hid, "1",
- allow_userspace_ctrl_ids[i].hrv))
- sw_desc.allow_userspace_control = true;
-
platform_set_drvdata(pdev, data);
data->role_sw = usb_role_switch_register(dev, &sw_desc);
if (IS_ERR(data->role_sw))
return PTR_ERR(data->role_sw);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
return 0;
}
@@ -166,6 +161,8 @@ static int intel_xhci_usb_remove(struct platform_device *pdev)
{
struct intel_xhci_usb_data *data = platform_get_drvdata(pdev);
+ pm_runtime_disable(&pdev->dev);
+
usb_role_switch_unregister(data->role_sw);
return 0;
}
diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c
index 7796ad8e33c6..ff38aa8963cf 100644
--- a/drivers/usb/serial/ark3116.c
+++ b/drivers/usb/serial/ark3116.c
@@ -397,38 +397,16 @@ err_free:
return result;
}
-static int ark3116_get_serial_info(struct usb_serial_port *port,
- struct serial_struct __user *retinfo)
-{
- struct serial_struct tmp;
-
- memset(&tmp, 0, sizeof(tmp));
-
- tmp.type = PORT_16654;
- tmp.line = port->minor;
- tmp.port = port->port_number;
- tmp.baud_base = 460800;
-
- if (copy_to_user(retinfo, &tmp, sizeof(tmp)))
- return -EFAULT;
-
- return 0;
-}
-
-static int ark3116_ioctl(struct tty_struct *tty,
- unsigned int cmd, unsigned long arg)
+static int ark3116_get_serial_info(struct tty_struct *tty,
+ struct serial_struct *ss)
{
struct usb_serial_port *port = tty->driver_data;
- void __user *user_arg = (void __user *)arg;
-
- switch (cmd) {
- case TIOCGSERIAL:
- return ark3116_get_serial_info(port, user_arg);
- default:
- break;
- }
- return -ENOIOCTLCMD;
+ ss->type = PORT_16654;
+ ss->line = port->minor;
+ ss->port = port->port_number;
+ ss->baud_base = 460800;
+ return 0;
}
static int ark3116_tiocmget(struct tty_struct *tty)
@@ -668,7 +646,7 @@ static struct usb_serial_driver ark3116_device = {
.port_remove = ark3116_port_remove,
.set_termios = ark3116_set_termios,
.init_termios = ark3116_init_termios,
- .ioctl = ark3116_ioctl,
+ .get_serial = ark3116_get_serial_info,
.tiocmget = ark3116_tiocmget,
.tiocmset = ark3116_tiocmset,
.tiocmiwait = usb_serial_generic_tiocmiwait,
diff --git a/drivers/usb/serial/bus.c b/drivers/usb/serial/bus.c
index 9e265eb92611..eb0195cf37dd 100644
--- a/drivers/usb/serial/bus.c
+++ b/drivers/usb/serial/bus.c
@@ -60,7 +60,8 @@ static int usb_serial_device_probe(struct device *dev)
}
minor = port->minor;
- tty_dev = tty_register_device(usb_serial_tty_driver, minor, dev);
+ tty_dev = tty_port_register_device(&port->port, usb_serial_tty_driver,
+ minor, dev);
if (IS_ERR(tty_dev)) {
retval = PTR_ERR(tty_dev);
goto err_port_remove;
diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c
index bdd7a5ad3bf1..3bb1fff02bed 100644
--- a/drivers/usb/serial/ch341.c
+++ b/drivers/usb/serial/ch341.c
@@ -128,7 +128,7 @@ static int ch341_control_in(struct usb_device *dev,
r = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), request,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
value, index, buf, bufsize, DEFAULT_TIMEOUT);
- if (r < bufsize) {
+ if (r < (int)bufsize) {
if (r >= 0) {
dev_err(&dev->dev,
"short control message received (%d < %u)\n",
diff --git a/drivers/usb/serial/console.c b/drivers/usb/serial/console.c
index 17940589c647..7d289302ff6c 100644
--- a/drivers/usb/serial/console.c
+++ b/drivers/usb/serial/console.c
@@ -101,7 +101,6 @@ static int usb_console_setup(struct console *co, char *options)
cflag |= PARENB;
break;
}
- co->cflag = cflag;
/*
* no need to check the index here: if the index is wrong, console
@@ -164,6 +163,7 @@ static int usb_console_setup(struct console *co, char *options)
serial->type->set_termios(tty, port, &dummy);
tty_port_tty_set(&port->port, NULL);
+ tty_save_termios(tty);
tty_kref_put(tty);
}
tty_port_set_initialized(&port->port, 1);
diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c
index eb6c26cbe579..c0777a374a88 100644
--- a/drivers/usb/serial/cp210x.c
+++ b/drivers/usb/serial/cp210x.c
@@ -95,6 +95,9 @@ static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x10C4, 0x8156) }, /* B&G H3000 link cable */
{ USB_DEVICE(0x10C4, 0x815E) }, /* Helicomm IP-Link 1220-DVM */
{ USB_DEVICE(0x10C4, 0x815F) }, /* Timewave HamLinkUSB */
+ { USB_DEVICE(0x10C4, 0x817C) }, /* CESINEL MEDCAL N Power Quality Monitor */
+ { USB_DEVICE(0x10C4, 0x817D) }, /* CESINEL MEDCAL NT Power Quality Monitor */
+ { USB_DEVICE(0x10C4, 0x817E) }, /* CESINEL MEDCAL S Power Quality Monitor */
{ USB_DEVICE(0x10C4, 0x818B) }, /* AVIT Research USB to TTL */
{ USB_DEVICE(0x10C4, 0x819F) }, /* MJS USB Toslink Switcher */
{ USB_DEVICE(0x10C4, 0x81A6) }, /* ThinkOptics WavIt */
@@ -112,6 +115,9 @@ static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x10C4, 0x826B) }, /* Cygnal Integrated Products, Inc., Fasttrax GPS demonstration module */
{ USB_DEVICE(0x10C4, 0x8281) }, /* Nanotec Plug & Drive */
{ USB_DEVICE(0x10C4, 0x8293) }, /* Telegesis ETRX2USB */
+ { USB_DEVICE(0x10C4, 0x82EF) }, /* CESINEL FALCO 6105 AC Power Supply */
+ { USB_DEVICE(0x10C4, 0x82F1) }, /* CESINEL MEDCAL EFD Earth Fault Detector */
+ { USB_DEVICE(0x10C4, 0x82F2) }, /* CESINEL MEDCAL ST Network Analyzer */
{ USB_DEVICE(0x10C4, 0x82F4) }, /* Starizona MicroTouch */
{ USB_DEVICE(0x10C4, 0x82F9) }, /* Procyon AVS */
{ USB_DEVICE(0x10C4, 0x8341) }, /* Siemens MC35PU GPRS Modem */
@@ -124,7 +130,9 @@ static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x10C4, 0x8470) }, /* Juniper Networks BX Series System Console */
{ USB_DEVICE(0x10C4, 0x8477) }, /* Balluff RFID */
{ USB_DEVICE(0x10C4, 0x84B6) }, /* Starizona Hyperion */
+ { USB_DEVICE(0x10C4, 0x851E) }, /* CESINEL MEDCAL PT Network Analyzer */
{ USB_DEVICE(0x10C4, 0x85A7) }, /* LifeScan OneTouch Verio IQ */
+ { USB_DEVICE(0x10C4, 0x85B8) }, /* CESINEL ReCon T Energy Logger */
{ USB_DEVICE(0x10C4, 0x85EA) }, /* AC-Services IBUS-IF */
{ USB_DEVICE(0x10C4, 0x85EB) }, /* AC-Services CIS-IBUS */
{ USB_DEVICE(0x10C4, 0x85F8) }, /* Virtenio Preon32 */
@@ -134,17 +142,24 @@ static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x10C4, 0x8857) }, /* CEL EM357 ZigBee USB Stick */
{ USB_DEVICE(0x10C4, 0x88A4) }, /* MMB Networks ZigBee USB Device */
{ USB_DEVICE(0x10C4, 0x88A5) }, /* Planet Innovation Ingeni ZigBee USB Device */
+ { USB_DEVICE(0x10C4, 0x88FB) }, /* CESINEL MEDCAL STII Network Analyzer */
+ { USB_DEVICE(0x10C4, 0x8938) }, /* CESINEL MEDCAL S II Network Analyzer */
{ USB_DEVICE(0x10C4, 0x8946) }, /* Ketra N1 Wireless Interface */
{ USB_DEVICE(0x10C4, 0x8962) }, /* Brim Brothers charging dock */
{ USB_DEVICE(0x10C4, 0x8977) }, /* CEL MeshWorks DevKit Device */
{ USB_DEVICE(0x10C4, 0x8998) }, /* KCF Technologies PRN */
+ { USB_DEVICE(0x10C4, 0x89A4) }, /* CESINEL FTBC Flexible Thyristor Bridge Controller */
+ { USB_DEVICE(0x10C4, 0x89FB) }, /* Qivicon ZigBee USB Radio Stick */
{ USB_DEVICE(0x10C4, 0x8A2A) }, /* HubZ dual ZigBee and Z-Wave dongle */
{ USB_DEVICE(0x10C4, 0x8A5E) }, /* CEL EM3588 ZigBee USB Stick Long Range */
{ USB_DEVICE(0x10C4, 0x8B34) }, /* Qivicon ZigBee USB Radio Stick */
{ USB_DEVICE(0x10C4, 0xEA60) }, /* Silicon Labs factory default */
{ USB_DEVICE(0x10C4, 0xEA61) }, /* Silicon Labs factory default */
+ { USB_DEVICE(0x10C4, 0xEA63) }, /* Silicon Labs Windows Update (CP2101-4/CP2102N) */
{ USB_DEVICE(0x10C4, 0xEA70) }, /* Silicon Labs factory default */
{ USB_DEVICE(0x10C4, 0xEA71) }, /* Infinity GPS-MIC-1 Radio Monophone */
+ { USB_DEVICE(0x10C4, 0xEA7A) }, /* Silicon Labs Windows Update (CP2105) */
+ { USB_DEVICE(0x10C4, 0xEA7B) }, /* Silicon Labs Windows Update (CP2108) */
{ USB_DEVICE(0x10C4, 0xF001) }, /* Elan Digital Systems USBscope50 */
{ USB_DEVICE(0x10C4, 0xF002) }, /* Elan Digital Systems USBwave12 */
{ USB_DEVICE(0x10C4, 0xF003) }, /* Elan Digital Systems USBpulse100 */
@@ -224,11 +239,14 @@ MODULE_DEVICE_TABLE(usb, id_table);
struct cp210x_serial_private {
#ifdef CONFIG_GPIOLIB
struct gpio_chip gc;
- u8 config;
- u8 gpio_mode;
bool gpio_registered;
+ u8 gpio_pushpull;
+ u8 gpio_altfunc;
+ u8 gpio_input;
#endif
u8 partnum;
+ speed_t max_speed;
+ bool use_actual_rate;
};
struct cp210x_port_private {
@@ -341,6 +359,7 @@ static struct usb_serial_driver * const serial_drivers[] = {
#define CONTROL_WRITE_RTS 0x0200
/* CP210X_VENDOR_SPECIFIC values */
+#define CP210X_READ_2NCONFIG 0x000E
#define CP210X_READ_LATCH 0x00C2
#define CP210X_GET_PARTNUM 0x370B
#define CP210X_GET_PORTCONFIG 0x370C
@@ -354,6 +373,9 @@ static struct usb_serial_driver * const serial_drivers[] = {
#define CP210X_PARTNUM_CP2104 0x04
#define CP210X_PARTNUM_CP2105 0x05
#define CP210X_PARTNUM_CP2108 0x08
+#define CP210X_PARTNUM_CP2102N_QFN28 0x20
+#define CP210X_PARTNUM_CP2102N_QFN24 0x21
+#define CP210X_PARTNUM_CP2102N_QFN20 0x22
#define CP210X_PARTNUM_UNKNOWN 0xFF
/* CP210X_GET_COMM_STATUS returns these 0x13 bytes */
@@ -447,6 +469,12 @@ struct cp210x_config {
#define CP2105_GPIO1_RXLED_MODE BIT(1)
#define CP2105_GPIO1_RS485_MODE BIT(2)
+/* CP2102N configuration array indices */
+#define CP210X_2NCONFIG_CONFIG_VERSION_IDX 2
+#define CP210X_2NCONFIG_GPIO_MODE_IDX 581
+#define CP210X_2NCONFIG_GPIO_RSTLATCH_IDX 587
+#define CP210X_2NCONFIG_GPIO_CONTROL_IDX 600
+
/* CP210X_VENDOR_SPECIFIC, CP210X_WRITE_LATCH call writes these 0x2 bytes. */
struct cp210x_gpio_write {
u8 mask;
@@ -752,48 +780,6 @@ static int cp210x_get_line_ctl(struct usb_serial_port *port, u16 *ctl)
return 0;
}
-/*
- * cp210x_quantise_baudrate
- * Quantises the baud rate as per AN205 Table 1
- */
-static unsigned int cp210x_quantise_baudrate(unsigned int baud)
-{
- if (baud <= 300)
- baud = 300;
- else if (baud <= 600) baud = 600;
- else if (baud <= 1200) baud = 1200;
- else if (baud <= 1800) baud = 1800;
- else if (baud <= 2400) baud = 2400;
- else if (baud <= 4000) baud = 4000;
- else if (baud <= 4803) baud = 4800;
- else if (baud <= 7207) baud = 7200;
- else if (baud <= 9612) baud = 9600;
- else if (baud <= 14428) baud = 14400;
- else if (baud <= 16062) baud = 16000;
- else if (baud <= 19250) baud = 19200;
- else if (baud <= 28912) baud = 28800;
- else if (baud <= 38601) baud = 38400;
- else if (baud <= 51558) baud = 51200;
- else if (baud <= 56280) baud = 56000;
- else if (baud <= 58053) baud = 57600;
- else if (baud <= 64111) baud = 64000;
- else if (baud <= 77608) baud = 76800;
- else if (baud <= 117028) baud = 115200;
- else if (baud <= 129347) baud = 128000;
- else if (baud <= 156868) baud = 153600;
- else if (baud <= 237832) baud = 230400;
- else if (baud <= 254234) baud = 250000;
- else if (baud <= 273066) baud = 256000;
- else if (baud <= 491520) baud = 460800;
- else if (baud <= 567138) baud = 500000;
- else if (baud <= 670254) baud = 576000;
- else if (baud < 1000000)
- baud = 921600;
- else if (baud > 2000000)
- baud = 2000000;
- return baud;
-}
-
static int cp210x_open(struct tty_struct *tty, struct usb_serial_port *port)
{
int result;
@@ -1013,6 +999,75 @@ static void cp210x_get_termios_port(struct usb_serial_port *port,
*cflagp = cflag;
}
+struct cp210x_rate {
+ speed_t rate;
+ speed_t high;
+};
+
+static const struct cp210x_rate cp210x_an205_table1[] = {
+ { 300, 300 },
+ { 600, 600 },
+ { 1200, 1200 },
+ { 1800, 1800 },
+ { 2400, 2400 },
+ { 4000, 4000 },
+ { 4800, 4803 },
+ { 7200, 7207 },
+ { 9600, 9612 },
+ { 14400, 14428 },
+ { 16000, 16062 },
+ { 19200, 19250 },
+ { 28800, 28912 },
+ { 38400, 38601 },
+ { 51200, 51558 },
+ { 56000, 56280 },
+ { 57600, 58053 },
+ { 64000, 64111 },
+ { 76800, 77608 },
+ { 115200, 117028 },
+ { 128000, 129347 },
+ { 153600, 156868 },
+ { 230400, 237832 },
+ { 250000, 254234 },
+ { 256000, 273066 },
+ { 460800, 491520 },
+ { 500000, 567138 },
+ { 576000, 670254 },
+ { 921600, UINT_MAX }
+};
+
+/*
+ * Quantises the baud rate as per AN205 Table 1
+ */
+static speed_t cp210x_get_an205_rate(speed_t baud)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cp210x_an205_table1); ++i) {
+ if (baud <= cp210x_an205_table1[i].high)
+ break;
+ }
+
+ return cp210x_an205_table1[i].rate;
+}
+
+static speed_t cp210x_get_actual_rate(struct usb_serial *serial, speed_t baud)
+{
+ struct cp210x_serial_private *priv = usb_get_serial_data(serial);
+ unsigned int prescale = 1;
+ unsigned int div;
+
+ baud = clamp(baud, 300u, priv->max_speed);
+
+ if (baud <= 365)
+ prescale = 4;
+
+ div = DIV_ROUND_CLOSEST(48000000, 2 * prescale * baud);
+ baud = 48000000 / (2 * prescale * div);
+
+ return baud;
+}
+
/*
* CP2101 supports the following baud rates:
*
@@ -1042,16 +1097,24 @@ static void cp210x_get_termios_port(struct usb_serial_port *port,
static void cp210x_change_speed(struct tty_struct *tty,
struct usb_serial_port *port, struct ktermios *old_termios)
{
+ struct usb_serial *serial = port->serial;
+ struct cp210x_serial_private *priv = usb_get_serial_data(serial);
u32 baud;
baud = tty->termios.c_ospeed;
- /* This maps the requested rate to a rate valid on cp2102 or cp2103,
- * or to an arbitrary rate in [1M,2M].
+ /*
+ * This maps the requested rate to the actual rate, a valid rate on
+ * cp2102 or cp2103, or to an arbitrary rate in [1M, max_speed].
*
* NOTE: B0 is not implemented.
*/
- baud = cp210x_quantise_baudrate(baud);
+ if (priv->use_actual_rate)
+ baud = cp210x_get_actual_rate(serial, baud);
+ else if (baud < 1000000)
+ baud = cp210x_get_an205_rate(baud);
+ else if (baud > priv->max_speed)
+ baud = priv->max_speed;
dev_dbg(&port->dev, "%s - setting baud rate to %u\n", __func__, baud);
if (cp210x_write_u32_reg(port, CP210X_SET_BAUDRATE, baud)) {
@@ -1273,17 +1336,8 @@ static int cp210x_gpio_request(struct gpio_chip *gc, unsigned int offset)
struct usb_serial *serial = gpiochip_get_data(gc);
struct cp210x_serial_private *priv = usb_get_serial_data(serial);
- switch (offset) {
- case 0:
- if (priv->config & CP2105_GPIO0_TXLED_MODE)
- return -ENODEV;
- break;
- case 1:
- if (priv->config & (CP2105_GPIO1_RXLED_MODE |
- CP2105_GPIO1_RS485_MODE))
- return -ENODEV;
- break;
- }
+ if (priv->gpio_altfunc & BIT(offset))
+ return -ENODEV;
return 0;
}
@@ -1291,10 +1345,15 @@ static int cp210x_gpio_request(struct gpio_chip *gc, unsigned int offset)
static int cp210x_gpio_get(struct gpio_chip *gc, unsigned int gpio)
{
struct usb_serial *serial = gpiochip_get_data(gc);
+ struct cp210x_serial_private *priv = usb_get_serial_data(serial);
+ u8 req_type = REQTYPE_DEVICE_TO_HOST;
int result;
u8 buf;
- result = cp210x_read_vendor_block(serial, REQTYPE_INTERFACE_TO_HOST,
+ if (priv->partnum == CP210X_PARTNUM_CP2105)
+ req_type = REQTYPE_INTERFACE_TO_HOST;
+
+ result = cp210x_read_vendor_block(serial, req_type,
CP210X_READ_LATCH, &buf, sizeof(buf));
if (result < 0)
return result;
@@ -1305,7 +1364,9 @@ static int cp210x_gpio_get(struct gpio_chip *gc, unsigned int gpio)
static void 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);
struct cp210x_gpio_write buf;
+ int result;
if (value == 1)
buf.state = BIT(gpio);
@@ -1314,25 +1375,68 @@ static void cp210x_gpio_set(struct gpio_chip *gc, unsigned int gpio, int value)
buf.mask = BIT(gpio);
- cp210x_write_vendor_block(serial, REQTYPE_HOST_TO_INTERFACE,
- CP210X_WRITE_LATCH, &buf, sizeof(buf));
+ if (priv->partnum == CP210X_PARTNUM_CP2105) {
+ result = cp210x_write_vendor_block(serial,
+ REQTYPE_HOST_TO_INTERFACE,
+ CP210X_WRITE_LATCH, &buf,
+ sizeof(buf));
+ } else {
+ u16 wIndex = buf.state << 8 | buf.mask;
+
+ result = usb_control_msg(serial->dev,
+ usb_sndctrlpipe(serial->dev, 0),
+ CP210X_VENDOR_SPECIFIC,
+ REQTYPE_HOST_TO_DEVICE,
+ CP210X_WRITE_LATCH,
+ wIndex,
+ NULL, 0, USB_CTRL_SET_TIMEOUT);
+ }
+
+ if (result < 0) {
+ dev_err(&serial->interface->dev, "failed to set GPIO value: %d\n",
+ result);
+ }
}
static int cp210x_gpio_direction_get(struct gpio_chip *gc, unsigned int gpio)
{
- /* Hardware does not support an input mode */
- return 0;
+ struct usb_serial *serial = gpiochip_get_data(gc);
+ struct cp210x_serial_private *priv = usb_get_serial_data(serial);
+
+ return priv->gpio_input & BIT(gpio);
}
static int cp210x_gpio_direction_input(struct gpio_chip *gc, unsigned int gpio)
{
- /* Hardware does not support an input mode */
- return -ENOTSUPP;
+ struct usb_serial *serial = gpiochip_get_data(gc);
+ struct cp210x_serial_private *priv = usb_get_serial_data(serial);
+
+ if (priv->partnum == CP210X_PARTNUM_CP2105) {
+ /* hardware does not support an input mode */
+ return -ENOTSUPP;
+ }
+
+ /* push-pull pins cannot be changed to be inputs */
+ if (priv->gpio_pushpull & BIT(gpio))
+ return -EINVAL;
+
+ /* make sure to release pin if it is being driven low */
+ cp210x_gpio_set(gc, gpio, 1);
+
+ priv->gpio_input |= BIT(gpio);
+
+ return 0;
}
static int cp210x_gpio_direction_output(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);
+
+ priv->gpio_input &= ~BIT(gpio);
+ cp210x_gpio_set(gc, gpio, value);
+
return 0;
}
@@ -1345,11 +1449,11 @@ static int cp210x_gpio_set_config(struct gpio_chip *gc, unsigned int gpio,
/* Succeed only if in correct mode (this can't be set at runtime) */
if ((param == PIN_CONFIG_DRIVE_PUSH_PULL) &&
- (priv->gpio_mode & BIT(gpio)))
+ (priv->gpio_pushpull & BIT(gpio)))
return 0;
if ((param == PIN_CONFIG_DRIVE_OPEN_DRAIN) &&
- !(priv->gpio_mode & BIT(gpio)))
+ !(priv->gpio_pushpull & BIT(gpio)))
return 0;
return -ENOTSUPP;
@@ -1362,12 +1466,13 @@ static int cp210x_gpio_set_config(struct gpio_chip *gc, unsigned int gpio,
* this driver that provide GPIO do so in a way that does not impact other
* signals and are thus expected to have very different initialisation.
*/
-static int cp2105_shared_gpio_init(struct usb_serial *serial)
+static int cp2105_gpioconf_init(struct usb_serial *serial)
{
struct cp210x_serial_private *priv = usb_get_serial_data(serial);
struct cp210x_pin_mode mode;
struct cp210x_config config;
u8 intf_num = cp210x_interface_num(serial);
+ u8 iface_config;
int result;
result = cp210x_read_vendor_block(serial, REQTYPE_DEVICE_TO_HOST,
@@ -1384,20 +1489,26 @@ static int cp2105_shared_gpio_init(struct usb_serial *serial)
/* 2 banks of GPIO - One for the pins taken from each serial port */
if (intf_num == 0) {
- if (mode.eci == CP210X_PIN_MODE_MODEM)
+ if (mode.eci == CP210X_PIN_MODE_MODEM) {
+ /* mark all GPIOs of this interface as reserved */
+ priv->gpio_altfunc = 0xff;
return 0;
+ }
- priv->config = config.eci_cfg;
- priv->gpio_mode = (u8)((le16_to_cpu(config.gpio_mode) &
+ iface_config = config.eci_cfg;
+ priv->gpio_pushpull = (u8)((le16_to_cpu(config.gpio_mode) &
CP210X_ECI_GPIO_MODE_MASK) >>
CP210X_ECI_GPIO_MODE_OFFSET);
priv->gc.ngpio = 2;
} else if (intf_num == 1) {
- if (mode.sci == CP210X_PIN_MODE_MODEM)
+ if (mode.sci == CP210X_PIN_MODE_MODEM) {
+ /* mark all GPIOs of this interface as reserved */
+ priv->gpio_altfunc = 0xff;
return 0;
+ }
- priv->config = config.sci_cfg;
- priv->gpio_mode = (u8)((le16_to_cpu(config.gpio_mode) &
+ iface_config = config.sci_cfg;
+ priv->gpio_pushpull = (u8)((le16_to_cpu(config.gpio_mode) &
CP210X_SCI_GPIO_MODE_MASK) >>
CP210X_SCI_GPIO_MODE_OFFSET);
priv->gc.ngpio = 3;
@@ -1405,6 +1516,125 @@ static int cp2105_shared_gpio_init(struct usb_serial *serial)
return -ENODEV;
}
+ /* mark all pins which are not in GPIO mode */
+ if (iface_config & CP2105_GPIO0_TXLED_MODE) /* GPIO 0 */
+ priv->gpio_altfunc |= BIT(0);
+ if (iface_config & (CP2105_GPIO1_RXLED_MODE | /* GPIO 1 */
+ CP2105_GPIO1_RS485_MODE))
+ priv->gpio_altfunc |= BIT(1);
+
+ /* driver implementation for CP2105 only supports outputs */
+ priv->gpio_input = 0;
+
+ return 0;
+}
+
+static int cp2102n_gpioconf_init(struct usb_serial *serial)
+{
+ struct cp210x_serial_private *priv = usb_get_serial_data(serial);
+ const u16 config_size = 0x02a6;
+ u8 gpio_rst_latch;
+ u8 config_version;
+ u8 gpio_pushpull;
+ u8 *config_buf;
+ u8 gpio_latch;
+ u8 gpio_ctrl;
+ int result;
+ u8 i;
+
+ /*
+ * Retrieve device configuration from the device.
+ * The array received contains all customization settings done at the
+ * factory/manufacturer. Format of the array is documented at the
+ * time of writing at:
+ * https://www.silabs.com/community/interface/knowledge-base.entry.html/2017/03/31/cp2102n_setconfig-xsfa
+ */
+ config_buf = kmalloc(config_size, GFP_KERNEL);
+ if (!config_buf)
+ return -ENOMEM;
+
+ result = cp210x_read_vendor_block(serial,
+ REQTYPE_DEVICE_TO_HOST,
+ CP210X_READ_2NCONFIG,
+ config_buf,
+ config_size);
+ if (result < 0) {
+ kfree(config_buf);
+ return result;
+ }
+
+ config_version = config_buf[CP210X_2NCONFIG_CONFIG_VERSION_IDX];
+ gpio_pushpull = config_buf[CP210X_2NCONFIG_GPIO_MODE_IDX];
+ gpio_ctrl = config_buf[CP210X_2NCONFIG_GPIO_CONTROL_IDX];
+ gpio_rst_latch = config_buf[CP210X_2NCONFIG_GPIO_RSTLATCH_IDX];
+
+ kfree(config_buf);
+
+ /* Make sure this is a config format we understand. */
+ if (config_version != 0x01)
+ return -ENOTSUPP;
+
+ /*
+ * We only support 4 GPIOs even on the QFN28 package, because
+ * config locations of GPIOs 4-6 determined using reverse
+ * engineering revealed conflicting offsets with other
+ * documented functions. So we'll just play it safe for now.
+ */
+ priv->gc.ngpio = 4;
+
+ /*
+ * Get default pin states after reset. Needed so we can determine
+ * the direction of an open-drain pin.
+ */
+ gpio_latch = (gpio_rst_latch >> 3) & 0x0f;
+
+ /* 0 indicates open-drain mode, 1 is push-pull */
+ priv->gpio_pushpull = (gpio_pushpull >> 3) & 0x0f;
+
+ /* 0 indicates GPIO mode, 1 is alternate function */
+ priv->gpio_altfunc = (gpio_ctrl >> 2) & 0x0f;
+
+ /*
+ * The CP2102N does not strictly has input and output pin modes,
+ * it only knows open-drain and push-pull modes which is set at
+ * factory. An open-drain pin can function both as an
+ * input or an output. We emulate input mode for open-drain pins
+ * by making sure they are not driven low, and we do not allow
+ * push-pull pins to be set as an input.
+ */
+ for (i = 0; i < priv->gc.ngpio; ++i) {
+ /*
+ * Set direction to "input" iff pin is open-drain and reset
+ * value is 1.
+ */
+ if (!(priv->gpio_pushpull & BIT(i)) && (gpio_latch & BIT(i)))
+ priv->gpio_input |= BIT(i);
+ }
+
+ return 0;
+}
+
+static int cp210x_gpio_init(struct usb_serial *serial)
+{
+ struct cp210x_serial_private *priv = usb_get_serial_data(serial);
+ int result;
+
+ switch (priv->partnum) {
+ case CP210X_PARTNUM_CP2105:
+ result = cp2105_gpioconf_init(serial);
+ break;
+ case CP210X_PARTNUM_CP2102N_QFN28:
+ case CP210X_PARTNUM_CP2102N_QFN24:
+ case CP210X_PARTNUM_CP2102N_QFN20:
+ result = cp2102n_gpioconf_init(serial);
+ break;
+ default:
+ return 0;
+ }
+
+ if (result < 0)
+ return result;
+
priv->gc.label = "cp210x";
priv->gc.request = cp210x_gpio_request;
priv->gc.get_direction = cp210x_gpio_direction_get;
@@ -1437,7 +1667,7 @@ static void cp210x_gpio_remove(struct usb_serial *serial)
#else
-static int cp2105_shared_gpio_init(struct usb_serial *serial)
+static int cp210x_gpio_init(struct usb_serial *serial)
{
return 0;
}
@@ -1482,6 +1712,50 @@ static int cp210x_port_remove(struct usb_serial_port *port)
return 0;
}
+static void cp210x_init_max_speed(struct usb_serial *serial)
+{
+ struct cp210x_serial_private *priv = usb_get_serial_data(serial);
+ bool use_actual_rate = false;
+ speed_t max;
+
+ switch (priv->partnum) {
+ case CP210X_PARTNUM_CP2101:
+ max = 921600;
+ break;
+ case CP210X_PARTNUM_CP2102:
+ case CP210X_PARTNUM_CP2103:
+ max = 1000000;
+ break;
+ case CP210X_PARTNUM_CP2104:
+ use_actual_rate = true;
+ max = 2000000;
+ break;
+ case CP210X_PARTNUM_CP2108:
+ max = 2000000;
+ break;
+ case CP210X_PARTNUM_CP2105:
+ if (cp210x_interface_num(serial) == 0) {
+ use_actual_rate = true;
+ max = 2000000; /* ECI */
+ } else {
+ max = 921600; /* SCI */
+ }
+ break;
+ case CP210X_PARTNUM_CP2102N_QFN28:
+ case CP210X_PARTNUM_CP2102N_QFN24:
+ case CP210X_PARTNUM_CP2102N_QFN20:
+ use_actual_rate = true;
+ max = 3000000;
+ break;
+ default:
+ max = 2000000;
+ break;
+ }
+
+ priv->max_speed = max;
+ priv->use_actual_rate = use_actual_rate;
+}
+
static int cp210x_attach(struct usb_serial *serial)
{
int result;
@@ -1502,12 +1776,12 @@ static int cp210x_attach(struct usb_serial *serial)
usb_set_serial_data(serial, priv);
- if (priv->partnum == CP210X_PARTNUM_CP2105) {
- result = cp2105_shared_gpio_init(serial);
- if (result < 0) {
- dev_err(&serial->interface->dev,
- "GPIO initialisation failed, continuing without GPIO support\n");
- }
+ cp210x_init_max_speed(serial);
+
+ result = cp210x_gpio_init(serial);
+ if (result < 0) {
+ dev_err(&serial->interface->dev, "GPIO initialisation failed: %d\n",
+ result);
}
return 0;
diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c
index dc67a2eb98d7..ebd76ab07b72 100644
--- a/drivers/usb/serial/cyberjack.c
+++ b/drivers/usb/serial/cyberjack.c
@@ -255,6 +255,7 @@ static void cyberjack_read_int_callback(struct urb *urb)
struct device *dev = &port->dev;
unsigned char *data = urb->transfer_buffer;
int status = urb->status;
+ unsigned long flags;
int result;
/* the urb might have been killed. */
@@ -270,13 +271,13 @@ static void cyberjack_read_int_callback(struct urb *urb)
/* This is a announcement of coming bulk_ins. */
unsigned short size = ((unsigned short)data[3]<<8)+data[2]+3;
- spin_lock(&priv->lock);
+ spin_lock_irqsave(&priv->lock, flags);
old_rdtodo = priv->rdtodo;
if (old_rdtodo > SHRT_MAX - size) {
dev_dbg(dev, "To many bulk_in urbs to do.\n");
- spin_unlock(&priv->lock);
+ spin_unlock_irqrestore(&priv->lock, flags);
goto resubmit;
}
@@ -285,7 +286,7 @@ static void cyberjack_read_int_callback(struct urb *urb)
dev_dbg(dev, "%s - rdtodo: %d\n", __func__, priv->rdtodo);
- spin_unlock(&priv->lock);
+ spin_unlock_irqrestore(&priv->lock, flags);
if (!old_rdtodo) {
result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
@@ -309,6 +310,7 @@ static void cyberjack_read_bulk_callback(struct urb *urb)
struct cyberjack_private *priv = usb_get_serial_port_data(port);
struct device *dev = &port->dev;
unsigned char *data = urb->transfer_buffer;
+ unsigned long flags;
short todo;
int result;
int status = urb->status;
@@ -325,7 +327,7 @@ static void cyberjack_read_bulk_callback(struct urb *urb)
tty_flip_buffer_push(&port->port);
}
- spin_lock(&priv->lock);
+ spin_lock_irqsave(&priv->lock, flags);
/* Reduce urbs to do by one. */
priv->rdtodo -= urb->actual_length;
@@ -334,7 +336,7 @@ static void cyberjack_read_bulk_callback(struct urb *urb)
priv->rdtodo = 0;
todo = priv->rdtodo;
- spin_unlock(&priv->lock);
+ spin_unlock_irqrestore(&priv->lock, flags);
dev_dbg(dev, "%s - rdtodo: %d\n", __func__, todo);
@@ -354,6 +356,7 @@ static void cyberjack_write_bulk_callback(struct urb *urb)
struct cyberjack_private *priv = usb_get_serial_port_data(port);
struct device *dev = &port->dev;
int status = urb->status;
+ unsigned long flags;
set_bit(0, &port->write_urbs_free);
if (status) {
@@ -362,7 +365,7 @@ static void cyberjack_write_bulk_callback(struct urb *urb)
return;
}
- spin_lock(&priv->lock);
+ spin_lock_irqsave(&priv->lock, flags);
/* only do something if we have more data to send */
if (priv->wrfilled) {
@@ -406,7 +409,7 @@ static void cyberjack_write_bulk_callback(struct urb *urb)
}
exit:
- spin_unlock(&priv->lock);
+ spin_unlock_irqrestore(&priv->lock, flags);
usb_serial_port_softint(port);
}
diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c
index e0035c023120..ed51bc48eea6 100644
--- a/drivers/usb/serial/cypress_m8.c
+++ b/drivers/usb/serial/cypress_m8.c
@@ -378,7 +378,7 @@ static int cypress_serial_control(struct tty_struct *tty,
retval = -ENOTTY;
goto out;
}
- dev_dbg(dev, "%s - retreiving serial line settings\n", __func__);
+ dev_dbg(dev, "%s - retrieving serial line settings\n", __func__);
do {
retval = usb_control_msg(port->serial->dev,
usb_rcvctrlpipe(port->serial->dev, 0),
@@ -769,7 +769,7 @@ send:
usb_fill_int_urb(port->interrupt_out_urb, port->serial->dev,
usb_sndintpipe(port->serial->dev, port->interrupt_out_endpointAddress),
- port->interrupt_out_buffer, port->interrupt_out_size,
+ port->interrupt_out_buffer, actual_size,
cypress_write_int_callback, port, priv->write_urb_interval);
result = usb_submit_urb(port->interrupt_out_urb, GFP_ATOMIC);
if (result) {
@@ -863,7 +863,7 @@ static void cypress_set_termios(struct tty_struct *tty,
struct cypress_private *priv = usb_get_serial_port_data(port);
struct device *dev = &port->dev;
int data_bits, stop_bits, parity_type, parity_enable;
- unsigned cflag, iflag;
+ unsigned int cflag;
unsigned long flags;
__u8 oldlines;
int linechange = 0;
@@ -899,7 +899,6 @@ static void cypress_set_termios(struct tty_struct *tty,
tty->termios.c_cflag &= ~(CMSPAR|CRTSCTS);
cflag = tty->termios.c_cflag;
- iflag = tty->termios.c_iflag;
/* check if there are new settings */
if (old_termios) {
diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c
index b0526786fb02..e7f244cf2c07 100644
--- a/drivers/usb/serial/digi_acceleport.c
+++ b/drivers/usb/serial/digi_acceleport.c
@@ -984,6 +984,7 @@ static void digi_write_bulk_callback(struct urb *urb)
struct usb_serial *serial;
struct digi_port *priv;
struct digi_serial *serial_priv;
+ unsigned long flags;
int ret = 0;
int status = urb->status;
@@ -1004,15 +1005,15 @@ static void digi_write_bulk_callback(struct urb *urb)
/* handle oob callback */
if (priv->dp_port_num == serial_priv->ds_oob_port_num) {
dev_dbg(&port->dev, "digi_write_bulk_callback: oob callback\n");
- spin_lock(&priv->dp_port_lock);
+ spin_lock_irqsave(&priv->dp_port_lock, flags);
priv->dp_write_urb_in_use = 0;
wake_up_interruptible(&port->write_wait);
- spin_unlock(&priv->dp_port_lock);
+ spin_unlock_irqrestore(&priv->dp_port_lock, flags);
return;
}
/* try to send any buffered data on this port */
- spin_lock(&priv->dp_port_lock);
+ spin_lock_irqsave(&priv->dp_port_lock, flags);
priv->dp_write_urb_in_use = 0;
if (priv->dp_out_buf_len > 0) {
*((unsigned char *)(port->write_urb->transfer_buffer))
@@ -1035,7 +1036,7 @@ static void digi_write_bulk_callback(struct urb *urb)
/* lost the race in write_chan(). */
schedule_work(&priv->dp_wakeup_work);
- spin_unlock(&priv->dp_port_lock);
+ spin_unlock_irqrestore(&priv->dp_port_lock, flags);
if (ret && ret != -EPERM)
dev_err_console(port,
"%s: usb_submit_urb failed, ret=%d, port=%d\n",
@@ -1381,11 +1382,12 @@ static int digi_read_inb_callback(struct urb *urb)
struct usb_serial_port *port = urb->context;
struct digi_port *priv = usb_get_serial_port_data(port);
unsigned char *buf = urb->transfer_buffer;
+ unsigned long flags;
int opcode;
int len;
int port_status;
unsigned char *data;
- int flag, throttled;
+ int tty_flag, throttled;
/* short/multiple packet check */
if (urb->actual_length < 2) {
@@ -1407,7 +1409,7 @@ static int digi_read_inb_callback(struct urb *urb)
return -1;
}
- spin_lock(&priv->dp_port_lock);
+ spin_lock_irqsave(&priv->dp_port_lock, flags);
/* check for throttle; if set, do not resubmit read urb */
/* indicate the read chain needs to be restarted on unthrottle */
@@ -1421,7 +1423,7 @@ static int digi_read_inb_callback(struct urb *urb)
data = &buf[3];
/* get flag from port_status */
- flag = 0;
+ tty_flag = 0;
/* overrun is special, not associated with a char */
if (port_status & DIGI_OVERRUN_ERROR)
@@ -1430,21 +1432,21 @@ static int digi_read_inb_callback(struct urb *urb)
/* break takes precedence over parity, */
/* which takes precedence over framing errors */
if (port_status & DIGI_BREAK_ERROR)
- flag = TTY_BREAK;
+ tty_flag = TTY_BREAK;
else if (port_status & DIGI_PARITY_ERROR)
- flag = TTY_PARITY;
+ tty_flag = TTY_PARITY;
else if (port_status & DIGI_FRAMING_ERROR)
- flag = TTY_FRAME;
+ tty_flag = TTY_FRAME;
/* data length is len-1 (one byte of len is port_status) */
--len;
if (len > 0) {
tty_insert_flip_string_fixed_flag(&port->port, data,
- flag, len);
+ tty_flag, len);
tty_flip_buffer_push(&port->port);
}
}
- spin_unlock(&priv->dp_port_lock);
+ spin_unlock_irqrestore(&priv->dp_port_lock, flags);
if (opcode == DIGI_CMD_RECEIVE_DISABLE)
dev_dbg(&port->dev, "%s: got RECEIVE_DISABLE\n", __func__);
@@ -1474,6 +1476,7 @@ static int digi_read_oob_callback(struct urb *urb)
struct digi_port *priv = usb_get_serial_port_data(port);
unsigned char *buf = urb->transfer_buffer;
int opcode, line, status, val;
+ unsigned long flags;
int i;
unsigned int rts;
@@ -1506,7 +1509,7 @@ static int digi_read_oob_callback(struct urb *urb)
rts = C_CRTSCTS(tty);
if (tty && opcode == DIGI_CMD_READ_INPUT_SIGNALS) {
- spin_lock(&priv->dp_port_lock);
+ spin_lock_irqsave(&priv->dp_port_lock, flags);
/* convert from digi flags to termiox flags */
if (val & DIGI_READ_INPUT_SIGNALS_CTS) {
priv->dp_modem_signals |= TIOCM_CTS;
@@ -1530,12 +1533,12 @@ static int digi_read_oob_callback(struct urb *urb)
else
priv->dp_modem_signals &= ~TIOCM_CD;
- spin_unlock(&priv->dp_port_lock);
+ spin_unlock_irqrestore(&priv->dp_port_lock, flags);
} else if (opcode == DIGI_CMD_TRANSMIT_IDLE) {
- spin_lock(&priv->dp_port_lock);
+ spin_lock_irqsave(&priv->dp_port_lock, flags);
priv->dp_transmit_idle = 1;
wake_up_interruptible(&priv->dp_transmit_idle_wait);
- spin_unlock(&priv->dp_port_lock);
+ spin_unlock_irqrestore(&priv->dp_port_lock, flags);
} else if (opcode == DIGI_CMD_IFLUSH_FIFO) {
wake_up_interruptible(&priv->dp_flush_wait);
}
diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c
index 96036f87b1de..0dcdcb4b2cde 100644
--- a/drivers/usb/serial/f81232.c
+++ b/drivers/usb/serial/f81232.c
@@ -583,36 +583,16 @@ static int f81232_carrier_raised(struct usb_serial_port *port)
return 0;
}
-static int f81232_get_serial_info(struct usb_serial_port *port,
- unsigned long arg)
-{
- struct serial_struct ser;
-
- memset(&ser, 0, sizeof(ser));
-
- ser.type = PORT_16550A;
- ser.line = port->minor;
- ser.port = port->port_number;
- ser.baud_base = F81232_MAX_BAUDRATE;
-
- if (copy_to_user((void __user *)arg, &ser, sizeof(ser)))
- return -EFAULT;
-
- return 0;
-}
-
-static int f81232_ioctl(struct tty_struct *tty,
- unsigned int cmd, unsigned long arg)
+static int f81232_get_serial_info(struct tty_struct *tty,
+ struct serial_struct *ss)
{
struct usb_serial_port *port = tty->driver_data;
- switch (cmd) {
- case TIOCGSERIAL:
- return f81232_get_serial_info(port, arg);
- default:
- break;
- }
- return -ENOIOCTLCMD;
+ ss->type = PORT_16550A;
+ ss->line = port->minor;
+ ss->port = port->port_number;
+ ss->baud_base = F81232_MAX_BAUDRATE;
+ return 0;
}
static void f81232_interrupt_work(struct work_struct *work)
@@ -665,7 +645,7 @@ static struct usb_serial_driver f81232_device = {
.close = f81232_close,
.dtr_rts = f81232_dtr_rts,
.carrier_raised = f81232_carrier_raised,
- .ioctl = f81232_ioctl,
+ .get_serial = f81232_get_serial_info,
.break_ctl = f81232_break_ctl,
.set_termios = f81232_set_termios,
.tiocmget = f81232_tiocmget,
diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c
index 4dfbff20bda4..380933db34dd 100644
--- a/drivers/usb/serial/f81534.c
+++ b/drivers/usb/serial/f81534.c
@@ -1139,43 +1139,21 @@ static void f81534_close(struct usb_serial_port *port)
mutex_unlock(&serial_priv->urb_mutex);
}
-static int f81534_get_serial_info(struct usb_serial_port *port,
- struct serial_struct __user *retinfo)
+static int f81534_get_serial_info(struct tty_struct *tty,
+ struct serial_struct *ss)
{
+ struct usb_serial_port *port = tty->driver_data;
struct f81534_port_private *port_priv;
- struct serial_struct tmp;
port_priv = usb_get_serial_port_data(port);
- memset(&tmp, 0, sizeof(tmp));
-
- tmp.type = PORT_16550A;
- tmp.port = port->port_number;
- tmp.line = port->minor;
- tmp.baud_base = port_priv->baud_base;
-
- if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
- return -EFAULT;
-
+ ss->type = PORT_16550A;
+ ss->port = port->port_number;
+ ss->line = port->minor;
+ ss->baud_base = port_priv->baud_base;
return 0;
}
-static int f81534_ioctl(struct tty_struct *tty, unsigned int cmd,
- unsigned long arg)
-{
- struct usb_serial_port *port = tty->driver_data;
- struct serial_struct __user *buf = (struct serial_struct __user *)arg;
-
- switch (cmd) {
- case TIOCGSERIAL:
- return f81534_get_serial_info(port, buf);
- default:
- break;
- }
-
- return -ENOIOCTLCMD;
-}
-
static void f81534_process_per_serial_block(struct usb_serial_port *port,
u8 *data)
{
@@ -1581,7 +1559,7 @@ static struct usb_serial_driver f81534_device = {
.break_ctl = f81534_break_ctl,
.dtr_rts = f81534_dtr_rts,
.process_read_urb = f81534_process_read_urb,
- .ioctl = f81534_ioctl,
+ .get_serial = f81534_get_serial_info,
.tiocmget = f81534_tiocmget,
.tiocmset = f81534_tiocmset,
.write_bulk_callback = f81534_write_usb_callback,
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index 7ea221d42dba..609198d9594c 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -39,6 +39,7 @@
#include <linux/uaccess.h>
#include <linux/usb.h>
#include <linux/serial.h>
+#include <linux/gpio/driver.h>
#include <linux/usb/serial.h>
#include "ftdi_sio.h"
#include "ftdi_sio_ids.h"
@@ -54,15 +55,14 @@ struct ftdi_private {
int custom_divisor; /* custom_divisor kludge, this is for
baud_base (different from what goes to the
chip!) */
- __u16 last_set_data_urb_value ;
- /* the last data state set - needed for doing
- * a break
- */
+ u16 last_set_data_value; /* the last data state set - needed for doing
+ * a break
+ */
int flags; /* some ASYNC_xxxx flags are supported */
unsigned long last_dtr_rts; /* saved modem control outputs */
char prev_status; /* Used for TIOCMIWAIT */
char transmit_empty; /* If transmitter is empty or not */
- __u16 interface; /* FT2232C, FT2232H or FT4232H port interface
+ u16 interface; /* FT2232C, FT2232H or FT4232H port interface
(0 for FT232/245) */
speed_t force_baud; /* if non-zero, force the baud rate to
@@ -73,6 +73,15 @@ struct ftdi_private {
unsigned int latency; /* latency setting in use */
unsigned short max_packet_size;
struct mutex cfg_lock; /* Avoid mess by parallel calls of config ioctl() and change_speed() */
+#ifdef CONFIG_GPIOLIB
+ struct gpio_chip gc;
+ struct mutex gpio_lock; /* protects GPIO state */
+ bool gpio_registered; /* is the gpiochip in kernel registered */
+ bool gpio_used; /* true if the user requested a gpio */
+ u8 gpio_altfunc; /* which pins are in gpio mode */
+ u8 gpio_output; /* pin directions cache */
+ u8 gpio_value; /* pin value for outputs */
+#endif
};
/* struct ftdi_sio_quirk is used by devices requiring special attention. */
@@ -1056,6 +1065,10 @@ static int ftdi_tiocmset(struct tty_struct *tty,
unsigned int set, unsigned int clear);
static int ftdi_ioctl(struct tty_struct *tty,
unsigned int cmd, unsigned long arg);
+static int get_serial_info(struct tty_struct *tty,
+ struct serial_struct *ss);
+static int set_serial_info(struct tty_struct *tty,
+ struct serial_struct *ss);
static void ftdi_break_ctl(struct tty_struct *tty, int break_state);
static bool ftdi_tx_empty(struct usb_serial_port *port);
static int ftdi_get_modem_status(struct usb_serial_port *port,
@@ -1063,10 +1076,10 @@ static int ftdi_get_modem_status(struct usb_serial_port *port,
static unsigned short int ftdi_232am_baud_base_to_divisor(int baud, int base);
static unsigned short int ftdi_232am_baud_to_divisor(int baud);
-static __u32 ftdi_232bm_baud_base_to_divisor(int baud, int base);
-static __u32 ftdi_232bm_baud_to_divisor(int baud);
-static __u32 ftdi_2232h_baud_base_to_divisor(int baud, int base);
-static __u32 ftdi_2232h_baud_to_divisor(int baud);
+static u32 ftdi_232bm_baud_base_to_divisor(int baud, int base);
+static u32 ftdi_232bm_baud_to_divisor(int baud);
+static u32 ftdi_2232h_baud_base_to_divisor(int baud, int base);
+static u32 ftdi_2232h_baud_to_divisor(int baud);
static struct usb_serial_driver ftdi_sio_device = {
.driver = {
@@ -1092,6 +1105,8 @@ static struct usb_serial_driver ftdi_sio_device = {
.tiocmiwait = usb_serial_generic_tiocmiwait,
.get_icount = usb_serial_generic_get_icount,
.ioctl = ftdi_ioctl,
+ .get_serial = get_serial_info,
+ .set_serial = set_serial_info,
.set_termios = ftdi_set_termios,
.break_ctl = ftdi_break_ctl,
.tx_empty = ftdi_tx_empty,
@@ -1136,14 +1151,14 @@ static unsigned short int ftdi_232am_baud_to_divisor(int baud)
return ftdi_232am_baud_base_to_divisor(baud, 48000000);
}
-static __u32 ftdi_232bm_baud_base_to_divisor(int baud, int base)
+static u32 ftdi_232bm_baud_base_to_divisor(int baud, int base)
{
static const unsigned char divfrac[8] = { 0, 3, 2, 4, 1, 5, 6, 7 };
- __u32 divisor;
+ u32 divisor;
/* divisor shifted 3 bits to the left */
int divisor3 = base / 2 / baud;
divisor = divisor3 >> 3;
- divisor |= (__u32)divfrac[divisor3 & 0x7] << 14;
+ divisor |= (u32)divfrac[divisor3 & 0x7] << 14;
/* Deal with special cases for highest baud rates. */
if (divisor == 1)
divisor = 0;
@@ -1152,22 +1167,22 @@ static __u32 ftdi_232bm_baud_base_to_divisor(int baud, int base)
return divisor;
}
-static __u32 ftdi_232bm_baud_to_divisor(int baud)
+static u32 ftdi_232bm_baud_to_divisor(int baud)
{
return ftdi_232bm_baud_base_to_divisor(baud, 48000000);
}
-static __u32 ftdi_2232h_baud_base_to_divisor(int baud, int base)
+static u32 ftdi_2232h_baud_base_to_divisor(int baud, int base)
{
static const unsigned char divfrac[8] = { 0, 3, 2, 4, 1, 5, 6, 7 };
- __u32 divisor;
+ u32 divisor;
int divisor3;
/* hi-speed baud rate is 10-bit sampling instead of 16-bit */
divisor3 = base * 8 / (baud * 10);
divisor = divisor3 >> 3;
- divisor |= (__u32)divfrac[divisor3 & 0x7] << 14;
+ divisor |= (u32)divfrac[divisor3 & 0x7] << 14;
/* Deal with special cases for highest baud rates. */
if (divisor == 1)
divisor = 0;
@@ -1182,7 +1197,7 @@ static __u32 ftdi_2232h_baud_base_to_divisor(int baud, int base)
return divisor;
}
-static __u32 ftdi_2232h_baud_to_divisor(int baud)
+static u32 ftdi_2232h_baud_to_divisor(int baud)
{
return ftdi_2232h_baud_base_to_divisor(baud, 120000000);
}
@@ -1195,7 +1210,7 @@ static int update_mctrl(struct usb_serial_port *port, unsigned int set,
{
struct ftdi_private *priv = usb_get_serial_port_data(port);
struct device *dev = &port->dev;
- unsigned urb_value;
+ unsigned value;
int rv;
if (((set | clear) & (TIOCM_DTR | TIOCM_RTS)) == 0) {
@@ -1204,20 +1219,20 @@ static int update_mctrl(struct usb_serial_port *port, unsigned int set,
}
clear &= ~set; /* 'set' takes precedence over 'clear' */
- urb_value = 0;
+ value = 0;
if (clear & TIOCM_DTR)
- urb_value |= FTDI_SIO_SET_DTR_LOW;
+ value |= FTDI_SIO_SET_DTR_LOW;
if (clear & TIOCM_RTS)
- urb_value |= FTDI_SIO_SET_RTS_LOW;
+ value |= FTDI_SIO_SET_RTS_LOW;
if (set & TIOCM_DTR)
- urb_value |= FTDI_SIO_SET_DTR_HIGH;
+ value |= FTDI_SIO_SET_DTR_HIGH;
if (set & TIOCM_RTS)
- urb_value |= FTDI_SIO_SET_RTS_HIGH;
+ value |= FTDI_SIO_SET_RTS_HIGH;
rv = usb_control_msg(port->serial->dev,
usb_sndctrlpipe(port->serial->dev, 0),
FTDI_SIO_SET_MODEM_CTRL_REQUEST,
FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE,
- urb_value, priv->interface,
+ value, priv->interface,
NULL, 0, WDR_TIMEOUT);
if (rv < 0) {
dev_dbg(dev, "%s Error from MODEM_CTRL urb: DTR %s, RTS %s\n",
@@ -1236,12 +1251,12 @@ static int update_mctrl(struct usb_serial_port *port, unsigned int set,
}
-static __u32 get_ftdi_divisor(struct tty_struct *tty,
+static u32 get_ftdi_divisor(struct tty_struct *tty,
struct usb_serial_port *port)
{
struct ftdi_private *priv = usb_get_serial_port_data(port);
struct device *dev = &port->dev;
- __u32 div_value = 0;
+ u32 div_value = 0;
int div_okay = 1;
int baud;
@@ -1299,7 +1314,7 @@ static __u32 get_ftdi_divisor(struct tty_struct *tty,
case FT232RL: /* FT232RL chip */
case FTX: /* FT-X series */
if (baud <= 3000000) {
- __u16 product_id = le16_to_cpu(
+ 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) ||
@@ -1346,26 +1361,26 @@ static __u32 get_ftdi_divisor(struct tty_struct *tty,
static int change_speed(struct tty_struct *tty, struct usb_serial_port *port)
{
struct ftdi_private *priv = usb_get_serial_port_data(port);
- __u16 urb_value;
- __u16 urb_index;
- __u32 urb_index_value;
+ u16 value;
+ u16 index;
+ u32 index_value;
int rv;
- urb_index_value = get_ftdi_divisor(tty, port);
- urb_value = (__u16)urb_index_value;
- urb_index = (__u16)(urb_index_value >> 16);
+ index_value = get_ftdi_divisor(tty, port);
+ value = (u16)index_value;
+ index = (u16)(index_value >> 16);
if ((priv->chip_type == FT2232C) || (priv->chip_type == FT2232H) ||
(priv->chip_type == FT4232H) || (priv->chip_type == FT232H)) {
/* Probably the BM type needs the MSB of the encoded fractional
* divider also moved like for the chips above. Any infos? */
- urb_index = (__u16)((urb_index << 8) | priv->interface);
+ index = (u16)((index << 8) | priv->interface);
}
rv = usb_control_msg(port->serial->dev,
usb_sndctrlpipe(port->serial->dev, 0),
FTDI_SIO_SET_BAUDRATE_REQUEST,
FTDI_SIO_SET_BAUDRATE_REQUEST_TYPE,
- urb_value, urb_index,
+ value, index,
NULL, 0, WDR_SHORT_TIMEOUT);
return rv;
}
@@ -1444,48 +1459,42 @@ static int read_latency_timer(struct usb_serial_port *port)
return 0;
}
-static int get_serial_info(struct usb_serial_port *port,
- struct serial_struct __user *retinfo)
+static int get_serial_info(struct tty_struct *tty,
+ struct serial_struct *ss)
{
+ struct usb_serial_port *port = tty->driver_data;
struct ftdi_private *priv = usb_get_serial_port_data(port);
- struct serial_struct tmp;
- memset(&tmp, 0, sizeof(tmp));
- tmp.flags = priv->flags;
- tmp.baud_base = priv->baud_base;
- tmp.custom_divisor = priv->custom_divisor;
- if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
- return -EFAULT;
+ ss->flags = priv->flags;
+ ss->baud_base = priv->baud_base;
+ ss->custom_divisor = priv->custom_divisor;
return 0;
}
static int set_serial_info(struct tty_struct *tty,
- struct usb_serial_port *port, struct serial_struct __user *newinfo)
+ struct serial_struct *ss)
{
+ struct usb_serial_port *port = tty->driver_data;
struct ftdi_private *priv = usb_get_serial_port_data(port);
- struct serial_struct new_serial;
struct ftdi_private old_priv;
- if (copy_from_user(&new_serial, newinfo, sizeof(new_serial)))
- return -EFAULT;
-
mutex_lock(&priv->cfg_lock);
old_priv = *priv;
/* Do error checking and permission checking */
if (!capable(CAP_SYS_ADMIN)) {
- if ((new_serial.flags ^ priv->flags) & ~ASYNC_USR_MASK) {
+ if ((ss->flags ^ priv->flags) & ~ASYNC_USR_MASK) {
mutex_unlock(&priv->cfg_lock);
return -EPERM;
}
priv->flags = ((priv->flags & ~ASYNC_USR_MASK) |
- (new_serial.flags & ASYNC_USR_MASK));
- priv->custom_divisor = new_serial.custom_divisor;
+ (ss->flags & ASYNC_USR_MASK));
+ priv->custom_divisor = ss->custom_divisor;
goto check_and_exit;
}
- if (new_serial.baud_base != priv->baud_base) {
+ if (ss->baud_base != priv->baud_base) {
mutex_unlock(&priv->cfg_lock);
return -EINVAL;
}
@@ -1493,8 +1502,8 @@ static int set_serial_info(struct tty_struct *tty,
/* Make the changes - these are privileged changes! */
priv->flags = ((priv->flags & ~ASYNC_FLAGS) |
- (new_serial.flags & ASYNC_FLAGS));
- priv->custom_divisor = new_serial.custom_divisor;
+ (ss->flags & ASYNC_FLAGS));
+ priv->custom_divisor = ss->custom_divisor;
check_and_exit:
write_latency_timer(port);
@@ -1508,10 +1517,8 @@ check_and_exit:
dev_warn_ratelimited(&port->dev, "use of SPD flags is deprecated\n");
change_speed(tty, port);
- mutex_unlock(&priv->cfg_lock);
}
- else
- mutex_unlock(&priv->cfg_lock);
+ mutex_unlock(&priv->cfg_lock);
return 0;
}
@@ -1767,6 +1774,375 @@ static void remove_sysfs_attrs(struct usb_serial_port *port)
}
+#ifdef CONFIG_GPIOLIB
+
+static int ftdi_set_bitmode(struct usb_serial_port *port, u8 mode)
+{
+ struct ftdi_private *priv = usb_get_serial_port_data(port);
+ struct usb_serial *serial = port->serial;
+ int result;
+ u16 val;
+
+ val = (mode << 8) | (priv->gpio_output << 4) | priv->gpio_value;
+ result = usb_control_msg(serial->dev,
+ usb_sndctrlpipe(serial->dev, 0),
+ FTDI_SIO_SET_BITMODE_REQUEST,
+ FTDI_SIO_SET_BITMODE_REQUEST_TYPE, val,
+ priv->interface, NULL, 0, WDR_TIMEOUT);
+ if (result < 0) {
+ dev_err(&serial->interface->dev,
+ "bitmode request failed for value 0x%04x: %d\n",
+ val, result);
+ }
+
+ return result;
+}
+
+static int ftdi_set_cbus_pins(struct usb_serial_port *port)
+{
+ return ftdi_set_bitmode(port, FTDI_SIO_BITMODE_CBUS);
+}
+
+static int ftdi_exit_cbus_mode(struct usb_serial_port *port)
+{
+ struct ftdi_private *priv = usb_get_serial_port_data(port);
+
+ priv->gpio_output = 0;
+ priv->gpio_value = 0;
+ return ftdi_set_bitmode(port, FTDI_SIO_BITMODE_RESET);
+}
+
+static int ftdi_gpio_request(struct gpio_chip *gc, unsigned int offset)
+{
+ struct usb_serial_port *port = gpiochip_get_data(gc);
+ struct ftdi_private *priv = usb_get_serial_port_data(port);
+ int result;
+
+ if (priv->gpio_altfunc & BIT(offset))
+ return -ENODEV;
+
+ mutex_lock(&priv->gpio_lock);
+ if (!priv->gpio_used) {
+ /* Set default pin states, as we cannot get them from device */
+ priv->gpio_output = 0x00;
+ priv->gpio_value = 0x00;
+ result = ftdi_set_cbus_pins(port);
+ if (result) {
+ mutex_unlock(&priv->gpio_lock);
+ return result;
+ }
+
+ priv->gpio_used = true;
+ }
+ mutex_unlock(&priv->gpio_lock);
+
+ return 0;
+}
+
+static int ftdi_read_cbus_pins(struct usb_serial_port *port)
+{
+ struct ftdi_private *priv = usb_get_serial_port_data(port);
+ struct usb_serial *serial = port->serial;
+ unsigned char *buf;
+ int result;
+
+ buf = kmalloc(1, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ result = usb_control_msg(serial->dev,
+ usb_rcvctrlpipe(serial->dev, 0),
+ FTDI_SIO_READ_PINS_REQUEST,
+ FTDI_SIO_READ_PINS_REQUEST_TYPE, 0,
+ priv->interface, buf, 1, WDR_TIMEOUT);
+ if (result < 1) {
+ if (result >= 0)
+ result = -EIO;
+ } else {
+ result = buf[0];
+ }
+
+ kfree(buf);
+
+ return result;
+}
+
+static int ftdi_gpio_get(struct gpio_chip *gc, unsigned int gpio)
+{
+ struct usb_serial_port *port = gpiochip_get_data(gc);
+ int result;
+
+ result = ftdi_read_cbus_pins(port);
+ if (result < 0)
+ return result;
+
+ return !!(result & BIT(gpio));
+}
+
+static void 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);
+
+ mutex_lock(&priv->gpio_lock);
+
+ if (value)
+ priv->gpio_value |= BIT(gpio);
+ else
+ priv->gpio_value &= ~BIT(gpio);
+
+ ftdi_set_cbus_pins(port);
+
+ mutex_unlock(&priv->gpio_lock);
+}
+
+static int ftdi_gpio_get_multiple(struct gpio_chip *gc, unsigned long *mask,
+ unsigned long *bits)
+{
+ struct usb_serial_port *port = gpiochip_get_data(gc);
+ int result;
+
+ result = ftdi_read_cbus_pins(port);
+ if (result < 0)
+ return result;
+
+ *bits = result & *mask;
+
+ return 0;
+}
+
+static void 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);
+
+ mutex_lock(&priv->gpio_lock);
+
+ priv->gpio_value &= ~(*mask);
+ priv->gpio_value |= *bits & *mask;
+ ftdi_set_cbus_pins(port);
+
+ mutex_unlock(&priv->gpio_lock);
+}
+
+static int ftdi_gpio_direction_get(struct gpio_chip *gc, unsigned int gpio)
+{
+ struct usb_serial_port *port = gpiochip_get_data(gc);
+ struct ftdi_private *priv = usb_get_serial_port_data(port);
+
+ return !(priv->gpio_output & BIT(gpio));
+}
+
+static int ftdi_gpio_direction_input(struct gpio_chip *gc, unsigned int gpio)
+{
+ 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_output &= ~BIT(gpio);
+ result = ftdi_set_cbus_pins(port);
+
+ mutex_unlock(&priv->gpio_lock);
+
+ return result;
+}
+
+static int ftdi_gpio_direction_output(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);
+
+ priv->gpio_output |= BIT(gpio);
+ if (value)
+ priv->gpio_value |= BIT(gpio);
+ else
+ priv->gpio_value &= ~BIT(gpio);
+
+ result = ftdi_set_cbus_pins(port);
+
+ mutex_unlock(&priv->gpio_lock);
+
+ return result;
+}
+
+static int ftdi_read_eeprom(struct usb_serial *serial, void *dst, u16 addr,
+ u16 nbytes)
+{
+ int read = 0;
+
+ if (addr % 2 != 0)
+ return -EINVAL;
+ if (nbytes % 2 != 0)
+ return -EINVAL;
+
+ /* Read EEPROM two bytes at a time */
+ while (read < nbytes) {
+ int rv;
+
+ rv = usb_control_msg(serial->dev,
+ usb_rcvctrlpipe(serial->dev, 0),
+ FTDI_SIO_READ_EEPROM_REQUEST,
+ FTDI_SIO_READ_EEPROM_REQUEST_TYPE,
+ 0, (addr + read) / 2, dst + read, 2,
+ WDR_TIMEOUT);
+ if (rv < 2) {
+ if (rv >= 0)
+ return -EIO;
+ else
+ return rv;
+ }
+
+ read += rv;
+ }
+
+ return 0;
+}
+
+static int ftdi_gpio_init_ft232r(struct usb_serial_port *port)
+{
+ struct ftdi_private *priv = usb_get_serial_port_data(port);
+ u16 cbus_config;
+ u8 *buf;
+ int ret;
+ int i;
+
+ buf = kmalloc(2, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ ret = ftdi_read_eeprom(port->serial, buf, 0x14, 2);
+ if (ret < 0)
+ goto out_free;
+
+ cbus_config = le16_to_cpup((__le16 *)buf);
+ dev_dbg(&port->dev, "cbus_config = 0x%04x\n", cbus_config);
+
+ priv->gc.ngpio = 4;
+
+ priv->gpio_altfunc = 0xff;
+ for (i = 0; i < priv->gc.ngpio; ++i) {
+ if ((cbus_config & 0xf) == FTDI_FT232R_CBUS_MUX_GPIO)
+ priv->gpio_altfunc &= ~BIT(i);
+ cbus_config >>= 4;
+ }
+out_free:
+ kfree(buf);
+
+ return ret;
+}
+
+static int ftdi_gpio_init_ftx(struct usb_serial_port *port)
+{
+ struct ftdi_private *priv = usb_get_serial_port_data(port);
+ struct usb_serial *serial = port->serial;
+ const u16 cbus_cfg_addr = 0x1a;
+ const u16 cbus_cfg_size = 4;
+ u8 *cbus_cfg_buf;
+ int result;
+ u8 i;
+
+ cbus_cfg_buf = kmalloc(cbus_cfg_size, GFP_KERNEL);
+ if (!cbus_cfg_buf)
+ return -ENOMEM;
+
+ result = ftdi_read_eeprom(serial, cbus_cfg_buf,
+ cbus_cfg_addr, cbus_cfg_size);
+ if (result < 0)
+ goto out_free;
+
+ /* FIXME: FT234XD alone has 1 GPIO, but how to recognize this IC? */
+ priv->gc.ngpio = 4;
+
+ /* Determine which pins are configured for CBUS bitbanging */
+ priv->gpio_altfunc = 0xff;
+ for (i = 0; i < priv->gc.ngpio; ++i) {
+ if (cbus_cfg_buf[i] == FTDI_FTX_CBUS_MUX_GPIO)
+ priv->gpio_altfunc &= ~BIT(i);
+ }
+
+out_free:
+ kfree(cbus_cfg_buf);
+
+ return result;
+}
+
+static int ftdi_gpio_init(struct usb_serial_port *port)
+{
+ struct ftdi_private *priv = usb_get_serial_port_data(port);
+ struct usb_serial *serial = port->serial;
+ int result;
+
+ switch (priv->chip_type) {
+ case FT232RL:
+ result = ftdi_gpio_init_ft232r(port);
+ break;
+ case FTX:
+ result = ftdi_gpio_init_ftx(port);
+ break;
+ default:
+ return 0;
+ }
+
+ if (result < 0)
+ return result;
+
+ mutex_init(&priv->gpio_lock);
+
+ priv->gc.label = "ftdi-cbus";
+ priv->gc.request = ftdi_gpio_request;
+ priv->gc.get_direction = ftdi_gpio_direction_get;
+ priv->gc.direction_input = ftdi_gpio_direction_input;
+ priv->gc.direction_output = ftdi_gpio_direction_output;
+ priv->gc.get = ftdi_gpio_get;
+ priv->gc.set = ftdi_gpio_set;
+ priv->gc.get_multiple = ftdi_gpio_get_multiple;
+ priv->gc.set_multiple = ftdi_gpio_set_multiple;
+ priv->gc.owner = THIS_MODULE;
+ priv->gc.parent = &serial->interface->dev;
+ priv->gc.base = -1;
+ priv->gc.can_sleep = true;
+
+ result = gpiochip_add_data(&priv->gc, port);
+ if (!result)
+ priv->gpio_registered = true;
+
+ return result;
+}
+
+static void ftdi_gpio_remove(struct usb_serial_port *port)
+{
+ struct ftdi_private *priv = usb_get_serial_port_data(port);
+
+ if (priv->gpio_registered) {
+ gpiochip_remove(&priv->gc);
+ priv->gpio_registered = false;
+ }
+
+ if (priv->gpio_used) {
+ /* Exiting CBUS-mode does not reset pin states. */
+ ftdi_exit_cbus_mode(port);
+ priv->gpio_used = false;
+ }
+}
+
+#else
+
+static int ftdi_gpio_init(struct usb_serial_port *port)
+{
+ return 0;
+}
+
+static void ftdi_gpio_remove(struct usb_serial_port *port) { }
+
+#endif /* CONFIG_GPIOLIB */
+
/*
* ***************************************************************************
* FTDI driver specific functions
@@ -1795,7 +2171,7 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port)
{
struct ftdi_private *priv;
const struct ftdi_sio_quirk *quirk = usb_get_serial_data(port->serial);
-
+ int result;
priv = kzalloc(sizeof(struct ftdi_private), GFP_KERNEL);
if (!priv)
@@ -1814,6 +2190,14 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port)
priv->latency = 16;
write_latency_timer(port);
create_sysfs_attrs(port);
+
+ result = ftdi_gpio_init(port);
+ if (result < 0) {
+ dev_err(&port->serial->interface->dev,
+ "GPIO initialisation failed: %d\n",
+ result);
+ }
+
return 0;
}
@@ -1931,6 +2315,8 @@ static int ftdi_sio_port_remove(struct usb_serial_port *port)
{
struct ftdi_private *priv = usb_get_serial_port_data(port);
+ ftdi_gpio_remove(port);
+
remove_sysfs_attrs(port);
kfree(priv);
@@ -2140,29 +2526,29 @@ static void ftdi_break_ctl(struct tty_struct *tty, int break_state)
{
struct usb_serial_port *port = tty->driver_data;
struct ftdi_private *priv = usb_get_serial_port_data(port);
- __u16 urb_value;
+ u16 value;
/* break_state = -1 to turn on break, and 0 to turn off break */
/* see drivers/char/tty_io.c to see it used */
- /* last_set_data_urb_value NEVER has the break bit set in it */
+ /* last_set_data_value NEVER has the break bit set in it */
if (break_state)
- urb_value = priv->last_set_data_urb_value | FTDI_SIO_SET_BREAK;
+ value = priv->last_set_data_value | FTDI_SIO_SET_BREAK;
else
- urb_value = priv->last_set_data_urb_value;
+ value = priv->last_set_data_value;
if (usb_control_msg(port->serial->dev,
usb_sndctrlpipe(port->serial->dev, 0),
FTDI_SIO_SET_DATA_REQUEST,
FTDI_SIO_SET_DATA_REQUEST_TYPE,
- urb_value , priv->interface,
+ value , priv->interface,
NULL, 0, WDR_TIMEOUT) < 0) {
dev_err(&port->dev, "%s FAILED to enable/disable break state (state was %d)\n",
__func__, break_state);
}
dev_dbg(&port->dev, "%s break state is %d - urb is %d\n", __func__,
- break_state, urb_value);
+ break_state, value);
}
@@ -2192,12 +2578,8 @@ static void ftdi_set_termios(struct tty_struct *tty,
struct ftdi_private *priv = usb_get_serial_port_data(port);
struct ktermios *termios = &tty->termios;
unsigned int cflag = termios->c_cflag;
- __u16 urb_value; /* will hold the new flags */
-
- /* Added for xon/xoff support */
- unsigned int iflag = termios->c_iflag;
- unsigned char vstop;
- unsigned char vstart;
+ u16 value, index;
+ int ret;
/* Force baud rate if this device requires it, unless it is set to
B0. */
@@ -2258,44 +2640,44 @@ static void ftdi_set_termios(struct tty_struct *tty,
no_skip:
/* Set number of data bits, parity, stop bits */
- urb_value = 0;
- urb_value |= (cflag & CSTOPB ? FTDI_SIO_SET_DATA_STOP_BITS_2 :
- FTDI_SIO_SET_DATA_STOP_BITS_1);
+ value = 0;
+ value |= (cflag & CSTOPB ? FTDI_SIO_SET_DATA_STOP_BITS_2 :
+ FTDI_SIO_SET_DATA_STOP_BITS_1);
if (cflag & PARENB) {
if (cflag & CMSPAR)
- urb_value |= cflag & PARODD ?
- FTDI_SIO_SET_DATA_PARITY_MARK :
- FTDI_SIO_SET_DATA_PARITY_SPACE;
+ value |= cflag & PARODD ?
+ FTDI_SIO_SET_DATA_PARITY_MARK :
+ FTDI_SIO_SET_DATA_PARITY_SPACE;
else
- urb_value |= cflag & PARODD ?
- FTDI_SIO_SET_DATA_PARITY_ODD :
- FTDI_SIO_SET_DATA_PARITY_EVEN;
+ value |= cflag & PARODD ?
+ FTDI_SIO_SET_DATA_PARITY_ODD :
+ FTDI_SIO_SET_DATA_PARITY_EVEN;
} else {
- urb_value |= FTDI_SIO_SET_DATA_PARITY_NONE;
+ value |= FTDI_SIO_SET_DATA_PARITY_NONE;
}
switch (cflag & CSIZE) {
case CS5:
dev_dbg(ddev, "Setting CS5 quirk\n");
break;
case CS7:
- urb_value |= 7;
+ value |= 7;
dev_dbg(ddev, "Setting CS7\n");
break;
default:
case CS8:
- urb_value |= 8;
+ value |= 8;
dev_dbg(ddev, "Setting CS8\n");
break;
}
/* This is needed by the break command since it uses the same command
- but is or'ed with this value */
- priv->last_set_data_urb_value = urb_value;
+ priv->last_set_data_value = value;
if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
FTDI_SIO_SET_DATA_REQUEST,
FTDI_SIO_SET_DATA_REQUEST_TYPE,
- urb_value , priv->interface,
+ value , priv->interface,
NULL, 0, WDR_SHORT_TIMEOUT) < 0) {
dev_err(ddev, "%s FAILED to set databits/stopbits/parity\n",
__func__);
@@ -2326,65 +2708,30 @@ no_data_parity_stop_changes:
set_mctrl(port, TIOCM_DTR | TIOCM_RTS);
}
- /* Set flow control */
- /* Note device also supports DTR/CD (ugh) and Xon/Xoff in hardware */
no_c_cflag_changes:
- if (cflag & CRTSCTS) {
- dev_dbg(ddev, "%s Setting to CRTSCTS flow control\n", __func__);
- if (usb_control_msg(dev,
- usb_sndctrlpipe(dev, 0),
- FTDI_SIO_SET_FLOW_CTRL_REQUEST,
- FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
- 0 , (FTDI_SIO_RTS_CTS_HS | priv->interface),
- NULL, 0, WDR_TIMEOUT) < 0) {
- dev_err(ddev, "urb failed to set to rts/cts flow control\n");
- }
+ /* Set hardware-assisted flow control */
+ value = 0;
+
+ if (C_CRTSCTS(tty)) {
+ dev_dbg(&port->dev, "enabling rts/cts flow control\n");
+ index = FTDI_SIO_RTS_CTS_HS;
+ } else if (I_IXON(tty)) {
+ dev_dbg(&port->dev, "enabling xon/xoff flow control\n");
+ index = FTDI_SIO_XON_XOFF_HS;
+ value = STOP_CHAR(tty) << 8 | START_CHAR(tty);
} else {
- /*
- * Xon/Xoff code
- *
- * Check the IXOFF status in the iflag component of the
- * termios structure. If IXOFF is not set, the pre-xon/xoff
- * code is executed.
- */
- if (iflag & IXOFF) {
- dev_dbg(ddev, "%s request to enable xonxoff iflag=%04x\n",
- __func__, iflag);
- /* Try to enable the XON/XOFF on the ftdi_sio
- * Set the vstart and vstop -- could have been done up
- * above where a lot of other dereferencing is done but
- * that would be very inefficient as vstart and vstop
- * are not always needed.
- */
- vstart = termios->c_cc[VSTART];
- vstop = termios->c_cc[VSTOP];
- urb_value = (vstop << 8) | (vstart);
-
- if (usb_control_msg(dev,
- usb_sndctrlpipe(dev, 0),
- FTDI_SIO_SET_FLOW_CTRL_REQUEST,
- FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
- urb_value , (FTDI_SIO_XON_XOFF_HS
- | priv->interface),
- NULL, 0, WDR_TIMEOUT) < 0) {
- dev_err(&port->dev, "urb failed to set to "
- "xon/xoff flow control\n");
- }
- } else {
- /* else clause to only run if cflag ! CRTSCTS and iflag
- * ! XOFF. CHECKME Assuming XON/XOFF handled by tty
- * stack - not by device */
- dev_dbg(ddev, "%s Turning off hardware flow control\n", __func__);
- if (usb_control_msg(dev,
- usb_sndctrlpipe(dev, 0),
- FTDI_SIO_SET_FLOW_CTRL_REQUEST,
- FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
- 0, priv->interface,
- NULL, 0, WDR_TIMEOUT) < 0) {
- dev_err(ddev, "urb failed to clear flow control\n");
- }
- }
+ dev_dbg(&port->dev, "disabling flow control\n");
+ index = FTDI_SIO_DISABLE_FLOW_CTRL;
}
+
+ index |= priv->interface;
+
+ ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ FTDI_SIO_SET_FLOW_CTRL_REQUEST,
+ FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
+ value, index, NULL, 0, WDR_TIMEOUT);
+ if (ret < 0)
+ dev_err(&port->dev, "failed to set flow control: %d\n", ret);
}
/*
@@ -2492,10 +2839,6 @@ static int ftdi_ioctl(struct tty_struct *tty,
void __user *argp = (void __user *)arg;
switch (cmd) {
- case TIOCGSERIAL:
- return get_serial_info(port, argp);
- case TIOCSSERIAL:
- return set_serial_info(tty, port, argp);
case TIOCSERGETLSR:
return get_lsr_info(port, argp);
default:
diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h
index dcd0b6e05baf..a79a1325b4d9 100644
--- a/drivers/usb/serial/ftdi_sio.h
+++ b/drivers/usb/serial/ftdi_sio.h
@@ -35,7 +35,10 @@
#define FTDI_SIO_SET_EVENT_CHAR 6 /* Set the event character */
#define FTDI_SIO_SET_ERROR_CHAR 7 /* Set the error character */
#define FTDI_SIO_SET_LATENCY_TIMER 9 /* Set the latency timer */
-#define FTDI_SIO_GET_LATENCY_TIMER 10 /* Get the latency timer */
+#define FTDI_SIO_GET_LATENCY_TIMER 0x0a /* Get the latency timer */
+#define FTDI_SIO_SET_BITMODE 0x0b /* Set bitbang mode */
+#define FTDI_SIO_READ_PINS 0x0c /* Read immediate value of pins */
+#define FTDI_SIO_READ_EEPROM 0x90 /* Read EEPROM */
/* Interface indices for FT2232, FT2232H and FT4232H devices */
#define INTERFACE_A 1
@@ -433,6 +436,29 @@ enum ftdi_sio_baudrate {
* 1 = active
*/
+/* FTDI_SIO_SET_BITMODE */
+#define FTDI_SIO_SET_BITMODE_REQUEST_TYPE 0x40
+#define FTDI_SIO_SET_BITMODE_REQUEST FTDI_SIO_SET_BITMODE
+
+/* Possible bitmodes for FTDI_SIO_SET_BITMODE_REQUEST */
+#define FTDI_SIO_BITMODE_RESET 0x00
+#define FTDI_SIO_BITMODE_CBUS 0x20
+
+/* FTDI_SIO_READ_PINS */
+#define FTDI_SIO_READ_PINS_REQUEST_TYPE 0xc0
+#define FTDI_SIO_READ_PINS_REQUEST FTDI_SIO_READ_PINS
+
+/*
+ * FTDI_SIO_READ_EEPROM
+ *
+ * EEPROM format found in FTDI AN_201, "FT-X MTP memory Configuration",
+ * http://www.ftdichip.com/Support/Documents/AppNotes/AN_201_FT-X%20MTP%20Memory%20Configuration.pdf
+ */
+#define FTDI_SIO_READ_EEPROM_REQUEST_TYPE 0xc0
+#define FTDI_SIO_READ_EEPROM_REQUEST FTDI_SIO_READ_EEPROM
+
+#define FTDI_FTX_CBUS_MUX_GPIO 0x8
+#define FTDI_FT232R_CBUS_MUX_GPIO 0xa
/* Descriptors returned by the device
diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c
index 17283f4b4779..4ca31c0e4174 100644
--- a/drivers/usb/serial/io_edgeport.c
+++ b/drivers/usb/serial/io_edgeport.c
@@ -648,6 +648,7 @@ static void edge_interrupt_callback(struct urb *urb)
struct usb_serial_port *port;
unsigned char *data = urb->transfer_buffer;
int length = urb->actual_length;
+ unsigned long flags;
int bytes_avail;
int position;
int txCredits;
@@ -679,7 +680,7 @@ static void edge_interrupt_callback(struct urb *urb)
if (length > 1) {
bytes_avail = data[0] | (data[1] << 8);
if (bytes_avail) {
- spin_lock(&edge_serial->es_lock);
+ spin_lock_irqsave(&edge_serial->es_lock, flags);
edge_serial->rxBytesAvail += bytes_avail;
dev_dbg(dev,
"%s - bytes_avail=%d, rxBytesAvail=%d, read_in_progress=%d\n",
@@ -702,7 +703,8 @@ static void edge_interrupt_callback(struct urb *urb)
edge_serial->read_in_progress = false;
}
}
- spin_unlock(&edge_serial->es_lock);
+ spin_unlock_irqrestore(&edge_serial->es_lock,
+ flags);
}
}
/* grab the txcredits for the ports if available */
@@ -715,9 +717,11 @@ static void edge_interrupt_callback(struct urb *urb)
port = edge_serial->serial->port[portNumber];
edge_port = usb_get_serial_port_data(port);
if (edge_port->open) {
- spin_lock(&edge_port->ep_lock);
+ spin_lock_irqsave(&edge_port->ep_lock,
+ flags);
edge_port->txCredits += txCredits;
- spin_unlock(&edge_port->ep_lock);
+ spin_unlock_irqrestore(&edge_port->ep_lock,
+ flags);
dev_dbg(dev, "%s - txcredits for port%d = %d\n",
__func__, portNumber,
edge_port->txCredits);
@@ -758,6 +762,7 @@ static void edge_bulk_in_callback(struct urb *urb)
int retval;
__u16 raw_data_length;
int status = urb->status;
+ unsigned long flags;
if (status) {
dev_dbg(&urb->dev->dev, "%s - nonzero read bulk status received: %d\n",
@@ -777,7 +782,7 @@ static void edge_bulk_in_callback(struct urb *urb)
usb_serial_debug_data(dev, __func__, raw_data_length, data);
- spin_lock(&edge_serial->es_lock);
+ spin_lock_irqsave(&edge_serial->es_lock, flags);
/* decrement our rxBytes available by the number that we just got */
edge_serial->rxBytesAvail -= raw_data_length;
@@ -801,7 +806,7 @@ static void edge_bulk_in_callback(struct urb *urb)
edge_serial->read_in_progress = false;
}
- spin_unlock(&edge_serial->es_lock);
+ spin_unlock_irqrestore(&edge_serial->es_lock, flags);
}
@@ -1632,24 +1637,20 @@ static int edge_tiocmget(struct tty_struct *tty)
return result;
}
-static int get_serial_info(struct edgeport_port *edge_port,
- struct serial_struct __user *retinfo)
+static int get_serial_info(struct tty_struct *tty,
+ struct serial_struct *ss)
{
- struct serial_struct tmp;
-
- memset(&tmp, 0, sizeof(tmp));
-
- tmp.type = PORT_16550A;
- tmp.line = edge_port->port->minor;
- tmp.port = edge_port->port->port_number;
- tmp.irq = 0;
- tmp.xmit_fifo_size = edge_port->maxTxCredits;
- tmp.baud_base = 9600;
- tmp.close_delay = 5*HZ;
- tmp.closing_wait = 30*HZ;
+ struct usb_serial_port *port = tty->driver_data;
+ struct edgeport_port *edge_port = usb_get_serial_port_data(port);
- if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
- return -EFAULT;
+ ss->type = PORT_16550A;
+ ss->line = edge_port->port->minor;
+ ss->port = edge_port->port->port_number;
+ ss->irq = 0;
+ ss->xmit_fifo_size = edge_port->maxTxCredits;
+ ss->baud_base = 9600;
+ ss->close_delay = 5*HZ;
+ ss->closing_wait = 30*HZ;
return 0;
}
@@ -1662,17 +1663,12 @@ static int edge_ioctl(struct tty_struct *tty,
unsigned int cmd, unsigned long arg)
{
struct usb_serial_port *port = tty->driver_data;
- DEFINE_WAIT(wait);
struct edgeport_port *edge_port = usb_get_serial_port_data(port);
switch (cmd) {
case TIOCSERGETLSR:
dev_dbg(&port->dev, "%s TIOCSERGETLSR\n", __func__);
return get_lsr_info(edge_port, (unsigned int __user *) arg);
-
- case TIOCGSERIAL:
- dev_dbg(&port->dev, "%s TIOCGSERIAL\n", __func__);
- return get_serial_info(edge_port, (struct serial_struct __user *) arg);
}
return -ENOIOCTLCMD;
}
@@ -3121,6 +3117,7 @@ static struct usb_serial_driver edgeport_2port_device = {
.set_termios = edge_set_termios,
.tiocmget = edge_tiocmget,
.tiocmset = edge_tiocmset,
+ .get_serial = get_serial_info,
.tiocmiwait = usb_serial_generic_tiocmiwait,
.get_icount = usb_serial_generic_get_icount,
.write = edge_write,
@@ -3156,6 +3153,7 @@ static struct usb_serial_driver edgeport_4port_device = {
.set_termios = edge_set_termios,
.tiocmget = edge_tiocmget,
.tiocmset = edge_tiocmset,
+ .get_serial = get_serial_info,
.tiocmiwait = usb_serial_generic_tiocmiwait,
.get_icount = usb_serial_generic_get_icount,
.write = edge_write,
@@ -3191,6 +3189,7 @@ static struct usb_serial_driver edgeport_8port_device = {
.set_termios = edge_set_termios,
.tiocmget = edge_tiocmget,
.tiocmset = edge_tiocmset,
+ .get_serial = get_serial_info,
.tiocmiwait = usb_serial_generic_tiocmiwait,
.get_icount = usb_serial_generic_get_icount,
.write = edge_write,
@@ -3226,6 +3225,7 @@ static struct usb_serial_driver epic_device = {
.set_termios = edge_set_termios,
.tiocmget = edge_tiocmget,
.tiocmset = edge_tiocmset,
+ .get_serial = get_serial_info,
.tiocmiwait = usb_serial_generic_tiocmiwait,
.get_icount = usb_serial_generic_get_icount,
.write = edge_write,
diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c
index 0fbadb37c104..c327d4cf7928 100644
--- a/drivers/usb/serial/io_ti.c
+++ b/drivers/usb/serial/io_ti.c
@@ -1729,6 +1729,7 @@ static void edge_bulk_in_callback(struct urb *urb)
struct edgeport_port *edge_port = urb->context;
struct device *dev = &edge_port->port->dev;
unsigned char *data = urb->transfer_buffer;
+ unsigned long flags;
int retval = 0;
int port_number;
int status = urb->status;
@@ -1780,13 +1781,13 @@ static void edge_bulk_in_callback(struct urb *urb)
exit:
/* continue read unless stopped */
- spin_lock(&edge_port->ep_lock);
+ spin_lock_irqsave(&edge_port->ep_lock, flags);
if (edge_port->ep_read_urb_state == EDGE_READ_URB_RUNNING)
retval = usb_submit_urb(urb, GFP_ATOMIC);
else if (edge_port->ep_read_urb_state == EDGE_READ_URB_STOPPING)
edge_port->ep_read_urb_state = EDGE_READ_URB_STOPPED;
- spin_unlock(&edge_port->ep_lock);
+ spin_unlock_irqrestore(&edge_port->ep_lock, flags);
if (retval)
dev_err(dev, "%s - usb_submit_urb failed with result %d\n", __func__, retval);
}
@@ -2436,47 +2437,28 @@ static int edge_tiocmget(struct tty_struct *tty)
return result;
}
-static int get_serial_info(struct edgeport_port *edge_port,
- struct serial_struct __user *retinfo)
+static int get_serial_info(struct tty_struct *tty,
+ struct serial_struct *ss)
{
- struct serial_struct tmp;
+ struct usb_serial_port *port = tty->driver_data;
+ struct edgeport_port *edge_port = usb_get_serial_port_data(port);
unsigned cwait;
cwait = edge_port->port->port.closing_wait;
if (cwait != ASYNC_CLOSING_WAIT_NONE)
cwait = jiffies_to_msecs(cwait) / 10;
- memset(&tmp, 0, sizeof(tmp));
-
- tmp.type = PORT_16550A;
- tmp.line = edge_port->port->minor;
- tmp.port = edge_port->port->port_number;
- tmp.irq = 0;
- tmp.xmit_fifo_size = edge_port->port->bulk_out_size;
- tmp.baud_base = 9600;
- tmp.close_delay = 5*HZ;
- tmp.closing_wait = cwait;
-
- if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
- return -EFAULT;
+ ss->type = PORT_16550A;
+ ss->line = edge_port->port->minor;
+ ss->port = edge_port->port->port_number;
+ ss->irq = 0;
+ ss->xmit_fifo_size = edge_port->port->bulk_out_size;
+ ss->baud_base = 9600;
+ ss->close_delay = 5*HZ;
+ ss->closing_wait = cwait;
return 0;
}
-static int edge_ioctl(struct tty_struct *tty,
- unsigned int cmd, unsigned long arg)
-{
- struct usb_serial_port *port = tty->driver_data;
- struct edgeport_port *edge_port = usb_get_serial_port_data(port);
-
- switch (cmd) {
- case TIOCGSERIAL:
- dev_dbg(&port->dev, "%s - TIOCGSERIAL\n", __func__);
- return get_serial_info(edge_port,
- (struct serial_struct __user *) arg);
- }
- return -ENOIOCTLCMD;
-}
-
static void edge_break(struct tty_struct *tty, int break_state)
{
struct usb_serial_port *port = tty->driver_data;
@@ -2737,7 +2719,7 @@ static struct usb_serial_driver edgeport_1port_device = {
.release = edge_release,
.port_probe = edge_port_probe,
.port_remove = edge_port_remove,
- .ioctl = edge_ioctl,
+ .get_serial = get_serial_info,
.set_termios = edge_set_termios,
.tiocmget = edge_tiocmget,
.tiocmset = edge_tiocmset,
@@ -2776,7 +2758,7 @@ static struct usb_serial_driver edgeport_2port_device = {
.release = edge_release,
.port_probe = edge_port_probe,
.port_remove = edge_port_remove,
- .ioctl = edge_ioctl,
+ .get_serial = get_serial_info,
.set_termios = edge_set_termios,
.tiocmget = edge_tiocmget,
.tiocmset = edge_tiocmset,
diff --git a/drivers/usb/serial/io_ti.h b/drivers/usb/serial/io_ti.h
index e53c68261017..9bbcee37524e 100644
--- a/drivers/usb/serial/io_ti.h
+++ b/drivers/usb/serial/io_ti.h
@@ -173,7 +173,7 @@ struct ump_interrupt {
} __attribute__((packed));
-#define TIUMP_GET_PORT_FROM_CODE(c) (((c) >> 4) - 3)
+#define TIUMP_GET_PORT_FROM_CODE(c) (((c) >> 6) & 0x01)
#define TIUMP_GET_FUNC_FROM_CODE(c) ((c) & 0x0f)
#define TIUMP_INTERRUPT_CODE_LSR 0x03
#define TIUMP_INTERRUPT_CODE_MSR 0x04
diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c
index 24b06c7e5e2d..7643716b5299 100644
--- a/drivers/usb/serial/ir-usb.c
+++ b/drivers/usb/serial/ir-usb.c
@@ -132,7 +132,7 @@ irda_usb_find_class_desc(struct usb_serial *serial, unsigned int ifnum)
0, ifnum, desc, sizeof(*desc), 1000);
dev_dbg(&serial->dev->dev, "%s - ret=%d\n", __func__, ret);
- if (ret < sizeof(*desc)) {
+ if (ret < (int)sizeof(*desc)) {
dev_dbg(&serial->dev->dev,
"%s - class descriptor read %s (%d)\n", __func__,
(ret < 0) ? "failed" : "too short", ret);
diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c
index 62c91e360baf..449e89db9cea 100644
--- a/drivers/usb/serial/iuu_phoenix.c
+++ b/drivers/usb/serial/iuu_phoenix.c
@@ -58,7 +58,6 @@ struct iuu_private {
u8 *buf; /* used for initialize speed */
u8 len;
int vcc; /* vcc (either 3 or 5 V) */
- u32 baud;
u32 boost;
u32 clk;
};
@@ -736,7 +735,7 @@ static int iuu_uart_on(struct usb_serial_port *port)
int status;
u8 *buf;
- buf = kmalloc(sizeof(u8) * 4, GFP_KERNEL);
+ buf = kmalloc(4, GFP_KERNEL);
if (!buf)
return -ENOMEM;
@@ -790,7 +789,7 @@ static int iuu_uart_baud(struct usb_serial_port *port, u32 baud_base,
unsigned int T1FrekvensHZ = 0;
dev_dbg(&port->dev, "%s - enter baud_base=%d\n", __func__, baud_base);
- dataout = kmalloc(sizeof(u8) * 5, GFP_KERNEL);
+ dataout = kmalloc(5, GFP_KERNEL);
if (!dataout)
return -ENOMEM;
@@ -963,9 +962,6 @@ static int iuu_open(struct tty_struct *tty, struct usb_serial_port *port)
struct iuu_private *priv = usb_get_serial_port_data(port);
baud = tty->termios.c_ospeed;
- tty->termios.c_ispeed = baud;
- /* Re-encode speed */
- tty_encode_baud_rate(tty, baud, baud);
dev_dbg(dev, "%s - baud %d\n", __func__, baud);
usb_clear_halt(serial->dev, port->write_urb->pipe);
@@ -991,7 +987,6 @@ static int iuu_open(struct tty_struct *tty, struct usb_serial_port *port)
if (boost < 100)
boost = 100;
priv->boost = boost;
- priv->baud = baud;
switch (clockmode) {
case 2: /* 3.680 Mhz */
priv->clk = IUU_CLK_3680000;
diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c
index 5169624d8b11..38d43c4b7ce5 100644
--- a/drivers/usb/serial/keyspan_pda.c
+++ b/drivers/usb/serial/keyspan_pda.c
@@ -369,8 +369,10 @@ static int keyspan_pda_get_modem_info(struct usb_serial *serial,
3, /* get pins */
USB_TYPE_VENDOR|USB_RECIP_INTERFACE|USB_DIR_IN,
0, 0, data, 1, 2000);
- if (rc >= 0)
+ if (rc == 1)
*value = *data;
+ else if (rc >= 0)
+ rc = -EIO;
kfree(data);
return rc;
diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c
index 5046ffd53cde..5ee48b0650c4 100644
--- a/drivers/usb/serial/kl5kusb105.c
+++ b/drivers/usb/serial/kl5kusb105.c
@@ -67,7 +67,6 @@ static int klsi_105_prepare_write_buffer(struct usb_serial_port *port,
*/
static const struct usb_device_id id_table[] = {
{ USB_DEVICE(PALMCONNECT_VID, PALMCONNECT_PID) },
- { USB_DEVICE(KLSI_VID, KLSI_KL5KUSB105D_PID) },
{ } /* Terminating entry */
};
diff --git a/drivers/usb/serial/kl5kusb105.h b/drivers/usb/serial/kl5kusb105.h
index 41c9bf60fbf0..dbe98d85ca8e 100644
--- a/drivers/usb/serial/kl5kusb105.h
+++ b/drivers/usb/serial/kl5kusb105.h
@@ -7,9 +7,6 @@
#define PALMCONNECT_VID 0x0830
#define PALMCONNECT_PID 0x0080
-#define KLSI_VID 0x05e9
-#define KLSI_KL5KUSB105D_PID 0x00c0
-
/* Vendor commands: */
diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c
index a31ea7e194dd..e9882ba20933 100644
--- a/drivers/usb/serial/kobil_sct.c
+++ b/drivers/usb/serial/kobil_sct.c
@@ -190,8 +190,10 @@ static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port)
KOBIL_TIMEOUT
);
dev_dbg(dev, "%s - Send get_HW_version URB returns: %i\n", __func__, result);
- dev_dbg(dev, "Hardware version: %i.%i.%i\n", transfer_buffer[0],
- transfer_buffer[1], transfer_buffer[2]);
+ if (result >= 3) {
+ dev_dbg(dev, "Hardware version: %i.%i.%i\n", transfer_buffer[0],
+ transfer_buffer[1], transfer_buffer[2]);
+ }
/* get firmware version */
result = usb_control_msg(port->serial->dev,
@@ -205,8 +207,10 @@ static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port)
KOBIL_TIMEOUT
);
dev_dbg(dev, "%s - Send get_FW_version URB returns: %i\n", __func__, result);
- dev_dbg(dev, "Firmware version: %i.%i.%i\n", transfer_buffer[0],
- transfer_buffer[1], transfer_buffer[2]);
+ if (result >= 3) {
+ dev_dbg(dev, "Firmware version: %i.%i.%i\n", transfer_buffer[0],
+ transfer_buffer[1], transfer_buffer[2]);
+ }
if (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID ||
priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID) {
@@ -393,12 +397,20 @@ static int kobil_tiocmget(struct tty_struct *tty)
transfer_buffer_length,
KOBIL_TIMEOUT);
- dev_dbg(&port->dev, "%s - Send get_status_line_state URB returns: %i. Statusline: %02x\n",
- __func__, result, transfer_buffer[0]);
+ dev_dbg(&port->dev, "Send get_status_line_state URB returns: %i\n",
+ result);
+ if (result < 1) {
+ if (result >= 0)
+ result = -EIO;
+ goto out_free;
+ }
+
+ dev_dbg(&port->dev, "Statusline: %02x\n", transfer_buffer[0]);
result = 0;
if ((transfer_buffer[0] & SUSBCR_GSL_DSR) != 0)
result = TIOCM_DSR;
+out_free:
kfree(transfer_buffer);
return result;
}
diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c
index bd57630e67e2..fc52ac75fbf6 100644
--- a/drivers/usb/serial/mos7720.c
+++ b/drivers/usb/serial/mos7720.c
@@ -340,14 +340,15 @@ static void async_complete(struct urb *urb)
{
struct urbtracker *urbtrack = urb->context;
int status = urb->status;
+ unsigned long flags;
if (unlikely(status))
dev_dbg(&urb->dev->dev, "%s - nonzero urb status received: %d\n", __func__, status);
/* remove the urbtracker from the active_urbs list */
- spin_lock(&urbtrack->mos_parport->listlock);
+ spin_lock_irqsave(&urbtrack->mos_parport->listlock, flags);
list_del(&urbtrack->urblist_entry);
- spin_unlock(&urbtrack->mos_parport->listlock);
+ spin_unlock_irqrestore(&urbtrack->mos_parport->listlock, flags);
kref_put(&urbtrack->ref_count, destroy_urbtracker);
}
@@ -1526,8 +1527,6 @@ static void change_port_settings(struct tty_struct *tty,
struct usb_serial *serial;
int baud;
unsigned cflag;
- unsigned iflag;
- __u8 mask = 0xff;
__u8 lData;
__u8 lParity;
__u8 lStop;
@@ -1551,23 +1550,19 @@ static void change_port_settings(struct tty_struct *tty,
lParity = 0x00; /* No parity */
cflag = tty->termios.c_cflag;
- iflag = tty->termios.c_iflag;
/* Change the number of bits */
switch (cflag & CSIZE) {
case CS5:
lData = UART_LCR_WLEN5;
- mask = 0x1f;
break;
case CS6:
lData = UART_LCR_WLEN6;
- mask = 0x3f;
break;
case CS7:
lData = UART_LCR_WLEN7;
- mask = 0x7f;
break;
default:
case CS8:
@@ -1685,11 +1680,8 @@ static void mos7720_set_termios(struct tty_struct *tty,
struct usb_serial_port *port, struct ktermios *old_termios)
{
int status;
- struct usb_serial *serial;
struct moschip_port *mos7720_port;
- serial = port->serial;
-
mos7720_port = usb_get_serial_port_data(port);
if (mos7720_port == NULL)
@@ -1794,69 +1786,20 @@ static int mos7720_tiocmset(struct tty_struct *tty,
return 0;
}
-static int set_modem_info(struct moschip_port *mos7720_port, unsigned int cmd,
- unsigned int __user *value)
+static int get_serial_info(struct tty_struct *tty,
+ struct serial_struct *ss)
{
- unsigned int mcr;
- unsigned int arg;
-
- struct usb_serial_port *port;
-
- if (mos7720_port == NULL)
- return -1;
-
- port = (struct usb_serial_port *)mos7720_port->port;
- mcr = mos7720_port->shadowMCR;
-
- if (copy_from_user(&arg, value, sizeof(int)))
- return -EFAULT;
-
- switch (cmd) {
- case TIOCMBIS:
- if (arg & TIOCM_RTS)
- mcr |= UART_MCR_RTS;
- if (arg & TIOCM_DTR)
- mcr |= UART_MCR_RTS;
- if (arg & TIOCM_LOOP)
- mcr |= UART_MCR_LOOP;
- break;
-
- case TIOCMBIC:
- if (arg & TIOCM_RTS)
- mcr &= ~UART_MCR_RTS;
- if (arg & TIOCM_DTR)
- mcr &= ~UART_MCR_RTS;
- if (arg & TIOCM_LOOP)
- mcr &= ~UART_MCR_LOOP;
- break;
-
- }
-
- mos7720_port->shadowMCR = mcr;
- write_mos_reg(port->serial, port->port_number, MOS7720_MCR,
- mos7720_port->shadowMCR);
-
- return 0;
-}
-
-static int get_serial_info(struct moschip_port *mos7720_port,
- struct serial_struct __user *retinfo)
-{
- struct serial_struct tmp;
-
- memset(&tmp, 0, sizeof(tmp));
-
- tmp.type = PORT_16550A;
- tmp.line = mos7720_port->port->minor;
- tmp.port = mos7720_port->port->port_number;
- tmp.irq = 0;
- tmp.xmit_fifo_size = NUM_URBS * URB_TRANSFER_BUFFER_SIZE;
- tmp.baud_base = 9600;
- tmp.close_delay = 5*HZ;
- tmp.closing_wait = 30*HZ;
+ struct usb_serial_port *port = tty->driver_data;
+ struct moschip_port *mos7720_port = usb_get_serial_port_data(port);
- if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
- return -EFAULT;
+ ss->type = PORT_16550A;
+ ss->line = mos7720_port->port->minor;
+ ss->port = mos7720_port->port->port_number;
+ ss->irq = 0;
+ ss->xmit_fifo_size = NUM_URBS * URB_TRANSFER_BUFFER_SIZE;
+ ss->baud_base = 9600;
+ ss->close_delay = 5*HZ;
+ ss->closing_wait = 30*HZ;
return 0;
}
@@ -1875,18 +1818,6 @@ static int mos7720_ioctl(struct tty_struct *tty,
dev_dbg(&port->dev, "%s TIOCSERGETLSR\n", __func__);
return get_lsr_info(tty, mos7720_port,
(unsigned int __user *)arg);
-
- /* FIXME: These should be using the mode methods */
- case TIOCMBIS:
- case TIOCMBIC:
- dev_dbg(&port->dev, "%s TIOCMSET/TIOCMBIC/TIOCMSET\n", __func__);
- return set_modem_info(mos7720_port, cmd,
- (unsigned int __user *)arg);
-
- case TIOCGSERIAL:
- dev_dbg(&port->dev, "%s TIOCGSERIAL\n", __func__);
- return get_serial_info(mos7720_port,
- (struct serial_struct __user *)arg);
}
return -ENOIOCTLCMD;
@@ -2023,6 +1954,7 @@ static struct usb_serial_driver moschip7720_2port_driver = {
.ioctl = mos7720_ioctl,
.tiocmget = mos7720_tiocmget,
.tiocmset = mos7720_tiocmset,
+ .get_serial = get_serial_info,
.set_termios = mos7720_set_termios,
.write = mos7720_write,
.write_room = mos7720_write_room,
diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c
index fdceb46d9fc6..88828b4b8c44 100644
--- a/drivers/usb/serial/mos7840.c
+++ b/drivers/usb/serial/mos7840.c
@@ -468,6 +468,9 @@ static void mos7840_control_callback(struct urb *urb)
}
dev_dbg(dev, "%s urb buffer size is %d\n", __func__, urb->actual_length);
+ if (urb->actual_length < 1)
+ goto out;
+
dev_dbg(dev, "%s mos7840_port->MsrLsr is %d port %d\n", __func__,
mos7840_port->MsrLsr, mos7840_port->port_num);
data = urb->transfer_buffer;
@@ -802,18 +805,19 @@ static void mos7840_bulk_out_data_callback(struct urb *urb)
struct moschip_port *mos7840_port;
struct usb_serial_port *port;
int status = urb->status;
+ unsigned long flags;
int i;
mos7840_port = urb->context;
port = mos7840_port->port;
- spin_lock(&mos7840_port->pool_lock);
+ spin_lock_irqsave(&mos7840_port->pool_lock, flags);
for (i = 0; i < NUM_URBS; i++) {
if (urb == mos7840_port->write_urb_pool[i]) {
mos7840_port->busy[i] = 0;
break;
}
}
- spin_unlock(&mos7840_port->pool_lock);
+ spin_unlock_irqrestore(&mos7840_port->pool_lock, flags);
if (status) {
dev_dbg(&port->dev, "nonzero write bulk status received:%d\n", status);
@@ -1927,27 +1931,20 @@ static int mos7840_get_lsr_info(struct tty_struct *tty,
* function to get information about serial port
*****************************************************************************/
-static int mos7840_get_serial_info(struct moschip_port *mos7840_port,
- struct serial_struct __user *retinfo)
+static int mos7840_get_serial_info(struct tty_struct *tty,
+ struct serial_struct *ss)
{
- struct serial_struct tmp;
-
- if (mos7840_port == NULL)
- return -1;
-
- memset(&tmp, 0, sizeof(tmp));
-
- tmp.type = PORT_16550A;
- tmp.line = mos7840_port->port->minor;
- tmp.port = mos7840_port->port->port_number;
- tmp.irq = 0;
- tmp.xmit_fifo_size = NUM_URBS * URB_TRANSFER_BUFFER_SIZE;
- tmp.baud_base = 9600;
- tmp.close_delay = 5 * HZ;
- tmp.closing_wait = 30 * HZ;
+ struct usb_serial_port *port = tty->driver_data;
+ struct moschip_port *mos7840_port = mos7840_get_port_private(port);
- if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
- return -EFAULT;
+ ss->type = PORT_16550A;
+ ss->line = mos7840_port->port->minor;
+ ss->port = mos7840_port->port->port_number;
+ ss->irq = 0;
+ ss->xmit_fifo_size = NUM_URBS * URB_TRANSFER_BUFFER_SIZE;
+ ss->baud_base = 9600;
+ ss->close_delay = 5 * HZ;
+ ss->closing_wait = 30 * HZ;
return 0;
}
@@ -1978,13 +1975,6 @@ static int mos7840_ioctl(struct tty_struct *tty,
dev_dbg(&port->dev, "%s TIOCSERGETLSR\n", __func__);
return mos7840_get_lsr_info(tty, argp);
- case TIOCGSERIAL:
- dev_dbg(&port->dev, "%s TIOCGSERIAL\n", __func__);
- return mos7840_get_serial_info(mos7840_port, argp);
-
- case TIOCSSERIAL:
- dev_dbg(&port->dev, "%s TIOCSSERIAL\n", __func__);
- break;
default:
break;
}
@@ -2372,6 +2362,7 @@ static struct usb_serial_driver moschip7840_4port_device = {
.calc_num_ports = mos7840_calc_num_ports,
.probe = mos7840_probe,
.ioctl = mos7840_ioctl,
+ .get_serial = mos7840_get_serial_info,
.set_termios = mos7840_set_termios,
.break_ctl = mos7840_break,
.tiocmget = mos7840_tiocmget,
diff --git a/drivers/usb/serial/opticon.c b/drivers/usb/serial/opticon.c
index caa0746326fd..cb7aac9cd9e7 100644
--- a/drivers/usb/serial/opticon.c
+++ b/drivers/usb/serial/opticon.c
@@ -328,42 +328,23 @@ static int opticon_tiocmset(struct tty_struct *tty,
return 0;
}
-static int get_serial_info(struct usb_serial_port *port,
- struct serial_struct __user *serial)
+static int get_serial_info(struct tty_struct *tty,
+ struct serial_struct *ss)
{
- struct serial_struct tmp;
-
- memset(&tmp, 0x00, sizeof(tmp));
+ struct usb_serial_port *port = tty->driver_data;
/* fake emulate a 16550 uart to make userspace code happy */
- tmp.type = PORT_16550A;
- tmp.line = port->minor;
- tmp.port = 0;
- tmp.irq = 0;
- tmp.xmit_fifo_size = 1024;
- tmp.baud_base = 9600;
- tmp.close_delay = 5*HZ;
- tmp.closing_wait = 30*HZ;
-
- if (copy_to_user(serial, &tmp, sizeof(*serial)))
- return -EFAULT;
+ ss->type = PORT_16550A;
+ ss->line = port->minor;
+ ss->port = 0;
+ ss->irq = 0;
+ ss->xmit_fifo_size = 1024;
+ ss->baud_base = 9600;
+ ss->close_delay = 5*HZ;
+ ss->closing_wait = 30*HZ;
return 0;
}
-static int opticon_ioctl(struct tty_struct *tty,
- unsigned int cmd, unsigned long arg)
-{
- struct usb_serial_port *port = tty->driver_data;
-
- switch (cmd) {
- case TIOCGSERIAL:
- return get_serial_info(port,
- (struct serial_struct __user *)arg);
- }
-
- return -ENOIOCTLCMD;
-}
-
static int opticon_port_probe(struct usb_serial_port *port)
{
struct opticon_private *priv;
@@ -404,7 +385,7 @@ static struct usb_serial_driver opticon_device = {
.write_room = opticon_write_room,
.throttle = usb_serial_generic_throttle,
.unthrottle = usb_serial_generic_unthrottle,
- .ioctl = opticon_ioctl,
+ .get_serial = get_serial_info,
.tiocmget = opticon_tiocmget,
.tiocmset = opticon_tiocmset,
.process_read_urb = opticon_process_read_urb,
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index 2058852a87fa..1ce27f3ff7a7 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -196,6 +196,8 @@ static void option_instat_callback(struct urb *urb);
#define DELL_PRODUCT_5800_V2_MINICARD_VZW 0x8196 /* Novatel E362 */
#define DELL_PRODUCT_5804_MINICARD_ATT 0x819b /* Novatel E371 */
+#define DELL_PRODUCT_5821E 0x81d7
+
#define KYOCERA_VENDOR_ID 0x0c88
#define KYOCERA_PRODUCT_KPC650 0x17da
#define KYOCERA_PRODUCT_KPC680 0x180a
@@ -559,6 +561,9 @@ static void option_instat_callback(struct urb *urb);
/* Interface is reserved */
#define RSVD(ifnum) ((BIT(ifnum) & 0xff) << 0)
+/* Interface must have two endpoints */
+#define NUMEP2 BIT(16)
+
static const struct usb_device_id option_ids[] = {
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) },
@@ -1030,6 +1035,8 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(DELL_VENDOR_ID, DELL_PRODUCT_5800_MINICARD_VZW, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(DELL_VENDOR_ID, DELL_PRODUCT_5800_V2_MINICARD_VZW, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(DELL_VENDOR_ID, DELL_PRODUCT_5804_MINICARD_ATT, 0xff, 0xff, 0xff) },
+ { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5821E),
+ .driver_info = RSVD(0) | RSVD(1) | RSVD(6) },
{ USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ADU_E100A) }, /* ADU-E100, ADU-310 */
{ USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ADU_500A) },
{ USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ADU_620UW) },
@@ -1077,8 +1084,9 @@ static const struct usb_device_id option_ids[] = {
.driver_info = RSVD(4) },
{ USB_DEVICE(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_BG96),
.driver_info = RSVD(4) },
- { USB_DEVICE(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EP06),
- .driver_info = RSVD(4) | RSVD(5) },
+ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EP06, 0xff, 0xff, 0xff),
+ .driver_info = RSVD(1) | RSVD(2) | RSVD(3) | RSVD(4) | NUMEP2 },
+ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EP06, 0xff, 0, 0) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_6001) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CMU_300) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_6003),
@@ -1156,6 +1164,10 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, TELIT_PRODUCT_LE920A4_1213, 0xff) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE920A4_1214),
.driver_info = NCTRL(0) | RSVD(1) | RSVD(2) | RSVD(3) },
+ { USB_DEVICE(TELIT_VENDOR_ID, 0x1900), /* Telit LN940 (QMI) */
+ .driver_info = NCTRL(0) | RSVD(1) },
+ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1901, 0xff), /* Telit LN940 (MBIM) */
+ .driver_info = NCTRL(0) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MF622, 0xff, 0xff, 0xff) }, /* ZTE WCDMA products */
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0002, 0xff, 0xff, 0xff),
.driver_info = RSVD(1) },
@@ -1320,6 +1332,7 @@ static const struct usb_device_id option_ids[] = {
.driver_info = RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0414, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0417, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_INTERFACE_CLASS(ZTE_VENDOR_ID, 0x0602, 0xff) }, /* GosunCn ZTE WeLink ME3630 (MBIM mode) */
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1008, 0xff, 0xff, 0xff),
.driver_info = RSVD(4) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1010, 0xff, 0xff, 0xff),
@@ -1523,6 +1536,7 @@ static const struct usb_device_id option_ids[] = {
.driver_info = RSVD(2) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1428, 0xff, 0xff, 0xff), /* Telewell TW-LTE 4G v2 */
.driver_info = RSVD(2) },
+ { USB_DEVICE_INTERFACE_CLASS(ZTE_VENDOR_ID, 0x1476, 0xff) }, /* GosunCn ZTE WeLink ME3630 (ECM/NCM mode) */
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1533, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1534, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1535, 0xff, 0xff, 0xff) },
@@ -1750,6 +1764,7 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(ALINK_VENDOR_ID, ALINK_PRODUCT_3GU, 0xff, 0xff, 0xff) },
{ USB_DEVICE(ALINK_VENDOR_ID, SIMCOM_PRODUCT_SIM7100E),
.driver_info = RSVD(5) | RSVD(6) },
+ { USB_DEVICE_INTERFACE_CLASS(0x1e0e, 0x9003, 0xff) }, /* Simcom SIM7500/SIM7600 MBIM mode */
{ USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_X060S_X200),
.driver_info = NCTRL(0) | NCTRL(1) | RSVD(4) },
{ USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_X220_X500D),
@@ -1916,7 +1931,8 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7d01, 0xff) }, /* D-Link DWM-156 (variant) */
{ USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7d02, 0xff) },
{ USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7d03, 0xff) },
- { USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7d04, 0xff) }, /* D-Link DWM-158 */
+ { USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7d04, 0xff), /* D-Link DWM-158 */
+ .driver_info = RSVD(4) | RSVD(5) },
{ USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7d0e, 0xff) }, /* D-Link DWM-157 C1 */
{ USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7e19, 0xff), /* D-Link DWM-221 B1 */
.driver_info = RSVD(4) },
@@ -1931,7 +1947,14 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(WETELECOM_VENDOR_ID, WETELECOM_PRODUCT_WMD200, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(WETELECOM_VENDOR_ID, WETELECOM_PRODUCT_6802, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(WETELECOM_VENDOR_ID, WETELECOM_PRODUCT_WMD300, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0x421d, 0xff, 0xff, 0xff) }, /* HP lt2523 (Novatel E371) */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0x421d, 0xff, 0xff, 0xff) }, /* HP lt2523 (Novatel E371) */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0xa31d, 0xff, 0x06, 0x10) }, /* HP lt4132 (Huawei ME906s-158) */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0xa31d, 0xff, 0x06, 0x12) },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0xa31d, 0xff, 0x06, 0x13) },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0xa31d, 0xff, 0x06, 0x14) },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0xa31d, 0xff, 0x06, 0x1b) },
+ { USB_DEVICE(0x1508, 0x1001), /* Fibocom NL668 */
+ .driver_info = RSVD(4) | RSVD(5) | RSVD(6) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, option_ids);
@@ -1957,7 +1980,8 @@ static struct usb_serial_driver option_1port_device = {
.chars_in_buffer = usb_wwan_chars_in_buffer,
.tiocmget = usb_wwan_tiocmget,
.tiocmset = usb_wwan_tiocmset,
- .ioctl = usb_wwan_ioctl,
+ .get_serial = usb_wwan_get_serial_info,
+ .set_serial = usb_wwan_set_serial_info,
.attach = option_attach,
.release = option_release,
.port_probe = usb_wwan_port_probe,
@@ -1994,6 +2018,13 @@ static int option_probe(struct usb_serial *serial,
if (device_flags & RSVD(iface_desc->bInterfaceNumber))
return -ENODEV;
+ /*
+ * Allow matching on bNumEndpoints for devices whose interface numbers
+ * can change (e.g. Quectel EP06).
+ */
+ if (device_flags & NUMEP2 && iface_desc->bNumEndpoints != 2)
+ return -ENODEV;
+
/* Store the device flags so we can use them during attach. */
usb_set_serial_data(serial, (void *)device_flags);
diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c
index 46dd09da2434..a4e0d13fc121 100644
--- a/drivers/usb/serial/pl2303.c
+++ b/drivers/usb/serial/pl2303.c
@@ -52,6 +52,8 @@ static const struct usb_device_id id_table[] = {
.driver_info = PL2303_QUIRK_ENDPOINT_HACK },
{ USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_UC485),
.driver_info = PL2303_QUIRK_ENDPOINT_HACK },
+ { USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_UC232B),
+ .driver_info = PL2303_QUIRK_ENDPOINT_HACK },
{ USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_ID2) },
{ USB_DEVICE(ATEN_VENDOR_ID2, ATEN_PRODUCT_ID) },
{ USB_DEVICE(ELCOM_VENDOR_ID, ELCOM_PRODUCT_ID) },
@@ -533,6 +535,17 @@ static int pl2303_set_line_request(struct usb_serial_port *port,
return 0;
}
+static bool pl2303_termios_change(const struct ktermios *a, const struct ktermios *b)
+{
+ bool ixon_change;
+
+ ixon_change = ((a->c_iflag ^ b->c_iflag) & (IXON | IXANY)) ||
+ a->c_cc[VSTART] != b->c_cc[VSTART] ||
+ a->c_cc[VSTOP] != b->c_cc[VSTOP];
+
+ return tty_termios_hw_change(a, b) || ixon_change;
+}
+
static void pl2303_set_termios(struct tty_struct *tty,
struct usb_serial_port *port, struct ktermios *old_termios)
{
@@ -544,7 +557,7 @@ static void pl2303_set_termios(struct tty_struct *tty,
int ret;
u8 control;
- if (old_termios && !tty_termios_hw_change(&tty->termios, old_termios))
+ if (old_termios && !pl2303_termios_change(&tty->termios, old_termios))
return;
buf = kzalloc(7, GFP_KERNEL);
@@ -662,6 +675,9 @@ static void pl2303_set_termios(struct tty_struct *tty,
pl2303_vendor_write(serial, 0x0, 0x41);
else
pl2303_vendor_write(serial, 0x0, 0x61);
+ } else if (I_IXON(tty) && !I_IXANY(tty) && START_CHAR(tty) == 0x11 &&
+ STOP_CHAR(tty) == 0x13) {
+ pl2303_vendor_write(serial, 0x0, 0xc0);
} else {
pl2303_vendor_write(serial, 0x0, 0x0);
}
@@ -792,29 +808,16 @@ static int pl2303_carrier_raised(struct usb_serial_port *port)
return 0;
}
-static int pl2303_ioctl(struct tty_struct *tty,
- unsigned int cmd, unsigned long arg)
+static int pl2303_get_serial(struct tty_struct *tty,
+ struct serial_struct *ss)
{
- struct serial_struct ser;
struct usb_serial_port *port = tty->driver_data;
- switch (cmd) {
- case TIOCGSERIAL:
- memset(&ser, 0, sizeof ser);
- ser.type = PORT_16654;
- ser.line = port->minor;
- ser.port = port->port_number;
- ser.baud_base = 460800;
-
- if (copy_to_user((void __user *)arg, &ser, sizeof ser))
- return -EFAULT;
-
- return 0;
- default:
- break;
- }
-
- return -ENOIOCTLCMD;
+ ss->type = PORT_16654;
+ ss->line = port->minor;
+ ss->port = port->port_number;
+ ss->baud_base = 460800;
+ return 0;
}
static void pl2303_set_break(struct usb_serial_port *port, bool enable)
@@ -1000,7 +1003,7 @@ static struct usb_serial_driver pl2303_device = {
.close = pl2303_close,
.dtr_rts = pl2303_dtr_rts,
.carrier_raised = pl2303_carrier_raised,
- .ioctl = pl2303_ioctl,
+ .get_serial = pl2303_get_serial,
.break_ctl = pl2303_break_ctl,
.set_termios = pl2303_set_termios,
.tiocmget = pl2303_tiocmget,
diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h
index fcd72396a7b6..26965cc23c17 100644
--- a/drivers/usb/serial/pl2303.h
+++ b/drivers/usb/serial/pl2303.h
@@ -24,6 +24,7 @@
#define ATEN_VENDOR_ID2 0x0547
#define ATEN_PRODUCT_ID 0x2008
#define ATEN_PRODUCT_UC485 0x2021
+#define ATEN_PRODUCT_UC232B 0x2022
#define ATEN_PRODUCT_ID2 0x2118
#define IODATA_VENDOR_ID 0x04bb
diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c
index 958e12e1e7c7..f2fbe1ec9701 100644
--- a/drivers/usb/serial/quatech2.c
+++ b/drivers/usb/serial/quatech2.c
@@ -194,7 +194,7 @@ static inline int qt2_getregister(struct usb_device *dev,
ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
QT_SET_GET_REGISTER, 0xc0, reg,
uart, data, sizeof(*data), QT2_USB_TIMEOUT);
- if (ret < sizeof(*data)) {
+ if (ret < (int)sizeof(*data)) {
if (ret >= 0)
ret = -EIO;
}
@@ -453,39 +453,19 @@ static void qt2_disconnect(struct usb_serial *serial)
usb_kill_urb(serial_priv->read_urb);
}
-static int get_serial_info(struct usb_serial_port *port,
- struct serial_struct __user *retinfo)
-{
- struct serial_struct tmp;
-
- memset(&tmp, 0, sizeof(tmp));
- tmp.line = port->minor;
- tmp.port = 0;
- tmp.irq = 0;
- tmp.xmit_fifo_size = port->bulk_out_size;
- tmp.baud_base = 9600;
- tmp.close_delay = 5*HZ;
- tmp.closing_wait = 30*HZ;
-
- if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
- return -EFAULT;
- return 0;
-}
-
-static int qt2_ioctl(struct tty_struct *tty,
- unsigned int cmd, unsigned long arg)
+static int get_serial_info(struct tty_struct *tty,
+ struct serial_struct *ss)
{
struct usb_serial_port *port = tty->driver_data;
- switch (cmd) {
- case TIOCGSERIAL:
- return get_serial_info(port,
- (struct serial_struct __user *)arg);
- default:
- break;
- }
-
- return -ENOIOCTLCMD;
+ ss->line = port->minor;
+ ss->port = 0;
+ ss->irq = 0;
+ ss->xmit_fifo_size = port->bulk_out_size;
+ ss->baud_base = 9600;
+ ss->close_delay = 5*HZ;
+ ss->closing_wait = 30*HZ;
+ return 0;
}
static void qt2_process_status(struct usb_serial_port *port, unsigned char *ch)
@@ -621,16 +601,17 @@ static void qt2_write_bulk_callback(struct urb *urb)
{
struct usb_serial_port *port;
struct qt2_port_private *port_priv;
+ unsigned long flags;
port = urb->context;
port_priv = usb_get_serial_port_data(port);
- spin_lock(&port_priv->urb_lock);
+ spin_lock_irqsave(&port_priv->urb_lock, flags);
port_priv->urb_in_use = false;
usb_serial_port_softint(port);
- spin_unlock(&port_priv->urb_lock);
+ spin_unlock_irqrestore(&port_priv->urb_lock, flags);
}
@@ -1012,7 +993,7 @@ static struct usb_serial_driver qt2_device = {
.tiocmset = qt2_tiocmset,
.tiocmiwait = usb_serial_generic_tiocmiwait,
.get_icount = usb_serial_generic_get_icount,
- .ioctl = qt2_ioctl,
+ .get_serial = get_serial_info,
.set_termios = qt2_set_termios,
};
diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c
index d189f953c891..a43263a0edd8 100644
--- a/drivers/usb/serial/sierra.c
+++ b/drivers/usb/serial/sierra.c
@@ -409,6 +409,7 @@ static void sierra_outdat_callback(struct urb *urb)
struct sierra_port_private *portdata = usb_get_serial_port_data(port);
struct sierra_intf_private *intfdata;
int status = urb->status;
+ unsigned long flags;
intfdata = usb_get_serial_data(port->serial);
@@ -419,12 +420,12 @@ static void sierra_outdat_callback(struct urb *urb)
dev_dbg(&port->dev, "%s - nonzero write bulk status "
"received: %d\n", __func__, status);
- spin_lock(&portdata->lock);
+ spin_lock_irqsave(&portdata->lock, flags);
--portdata->outstanding_urbs;
- spin_unlock(&portdata->lock);
- spin_lock(&intfdata->susp_lock);
+ spin_unlock_irqrestore(&portdata->lock, flags);
+ spin_lock_irqsave(&intfdata->susp_lock, flags);
--intfdata->in_flight;
- spin_unlock(&intfdata->susp_lock);
+ spin_unlock_irqrestore(&intfdata->susp_lock, flags);
usb_serial_port_softint(port);
}
@@ -770,9 +771,9 @@ static void sierra_close(struct usb_serial_port *port)
kfree(urb->transfer_buffer);
usb_free_urb(urb);
usb_autopm_put_interface_async(serial->interface);
- spin_lock(&portdata->lock);
+ spin_lock_irq(&portdata->lock);
portdata->outstanding_urbs--;
- spin_unlock(&portdata->lock);
+ spin_unlock_irq(&portdata->lock);
}
sierra_stop_rx_urbs(port);
diff --git a/drivers/usb/serial/ssu100.c b/drivers/usb/serial/ssu100.c
index 2083c267787b..f6aea9f1be1a 100644
--- a/drivers/usb/serial/ssu100.c
+++ b/drivers/usb/serial/ssu100.c
@@ -104,7 +104,7 @@ static inline int ssu100_getregister(struct usb_device *dev,
ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
QT_SET_GET_REGISTER, 0xc0, reg,
uart, data, sizeof(*data), 300);
- if (ret < sizeof(*data)) {
+ if (ret < (int)sizeof(*data)) {
if (ret >= 0)
ret = -EIO;
}
@@ -331,39 +331,19 @@ static int ssu100_open(struct tty_struct *tty, struct usb_serial_port *port)
return usb_serial_generic_open(tty, port);
}
-static int get_serial_info(struct usb_serial_port *port,
- struct serial_struct __user *retinfo)
-{
- struct serial_struct tmp;
-
- memset(&tmp, 0, sizeof(tmp));
- tmp.line = port->minor;
- tmp.port = 0;
- tmp.irq = 0;
- tmp.xmit_fifo_size = port->bulk_out_size;
- tmp.baud_base = 9600;
- tmp.close_delay = 5*HZ;
- tmp.closing_wait = 30*HZ;
-
- if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
- return -EFAULT;
- return 0;
-}
-
-static int ssu100_ioctl(struct tty_struct *tty,
- unsigned int cmd, unsigned long arg)
+static int get_serial_info(struct tty_struct *tty,
+ struct serial_struct *ss)
{
struct usb_serial_port *port = tty->driver_data;
- switch (cmd) {
- case TIOCGSERIAL:
- return get_serial_info(port,
- (struct serial_struct __user *) arg);
- default:
- break;
- }
-
- return -ENOIOCTLCMD;
+ ss->line = port->minor;
+ ss->port = 0;
+ ss->irq = 0;
+ ss->xmit_fifo_size = port->bulk_out_size;
+ ss->baud_base = 9600;
+ ss->close_delay = 5*HZ;
+ ss->closing_wait = 30*HZ;
+ return 0;
}
static int ssu100_attach(struct usb_serial *serial)
@@ -566,7 +546,7 @@ static struct usb_serial_driver ssu100_device = {
.tiocmset = ssu100_tiocmset,
.tiocmiwait = usb_serial_generic_tiocmiwait,
.get_icount = usb_serial_generic_get_icount,
- .ioctl = ssu100_ioctl,
+ .get_serial = get_serial_info,
.set_termios = ssu100_set_termios,
};
diff --git a/drivers/usb/serial/symbolserial.c b/drivers/usb/serial/symbolserial.c
index cd2f8dc8b58c..6ca24e86f686 100644
--- a/drivers/usb/serial/symbolserial.c
+++ b/drivers/usb/serial/symbolserial.c
@@ -35,6 +35,7 @@ static void symbol_int_callback(struct urb *urb)
struct symbol_private *priv = usb_get_serial_port_data(port);
unsigned char *data = urb->transfer_buffer;
int status = urb->status;
+ unsigned long flags;
int result;
int data_length;
@@ -73,7 +74,7 @@ static void symbol_int_callback(struct urb *urb)
}
exit:
- spin_lock(&priv->lock);
+ spin_lock_irqsave(&priv->lock, flags);
/* Continue trying to always read if we should */
if (!priv->throttled) {
@@ -84,7 +85,7 @@ exit:
__func__, result);
} else
priv->actually_throttled = true;
- spin_unlock(&priv->lock);
+ spin_unlock_irqrestore(&priv->lock, flags);
}
static int symbol_open(struct tty_struct *tty, struct usb_serial_port *port)
diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c
index 6b22857f6e52..dd0ad67aa71e 100644
--- a/drivers/usb/serial/ti_usb_3410_5052.c
+++ b/drivers/usb/serial/ti_usb_3410_5052.c
@@ -313,8 +313,6 @@ static int ti_chars_in_buffer(struct tty_struct *tty);
static bool ti_tx_empty(struct usb_serial_port *port);
static void ti_throttle(struct tty_struct *tty);
static void ti_unthrottle(struct tty_struct *tty);
-static int ti_ioctl(struct tty_struct *tty,
- unsigned int cmd, unsigned long arg);
static void ti_set_termios(struct tty_struct *tty,
struct usb_serial_port *port, struct ktermios *old_termios);
static int ti_tiocmget(struct tty_struct *tty);
@@ -330,10 +328,10 @@ static void ti_recv(struct usb_serial_port *port, unsigned char *data,
static void ti_send(struct ti_port *tport);
static int ti_set_mcr(struct ti_port *tport, unsigned int mcr);
static int ti_get_lsr(struct ti_port *tport, u8 *lsr);
-static int ti_get_serial_info(struct ti_port *tport,
- struct serial_struct __user *ret_arg);
-static int ti_set_serial_info(struct tty_struct *tty, struct ti_port *tport,
- struct serial_struct __user *new_arg);
+static int ti_get_serial_info(struct tty_struct *tty,
+ struct serial_struct *ss);
+static int ti_set_serial_info(struct tty_struct *tty,
+ struct serial_struct *ss);
static void ti_handle_new_msr(struct ti_port *tport, u8 msr);
static void ti_stop_read(struct ti_port *tport, struct tty_struct *tty);
@@ -436,7 +434,8 @@ static struct usb_serial_driver ti_1port_device = {
.tx_empty = ti_tx_empty,
.throttle = ti_throttle,
.unthrottle = ti_unthrottle,
- .ioctl = ti_ioctl,
+ .get_serial = ti_get_serial_info,
+ .set_serial = ti_set_serial_info,
.set_termios = ti_set_termios,
.tiocmget = ti_tiocmget,
.tiocmset = ti_tiocmset,
@@ -469,7 +468,8 @@ static struct usb_serial_driver ti_2port_device = {
.tx_empty = ti_tx_empty,
.throttle = ti_throttle,
.unthrottle = ti_unthrottle,
- .ioctl = ti_ioctl,
+ .get_serial = ti_get_serial_info,
+ .set_serial = ti_set_serial_info,
.set_termios = ti_set_termios,
.tiocmget = ti_tiocmget,
.tiocmset = ti_tiocmset,
@@ -902,24 +902,6 @@ static void ti_unthrottle(struct tty_struct *tty)
}
}
-static int ti_ioctl(struct tty_struct *tty,
- unsigned int cmd, unsigned long arg)
-{
- struct usb_serial_port *port = tty->driver_data;
- struct ti_port *tport = usb_get_serial_port_data(port);
-
- switch (cmd) {
- case TIOCGSERIAL:
- return ti_get_serial_info(tport,
- (struct serial_struct __user *)arg);
- case TIOCSSERIAL:
- return ti_set_serial_info(tty, tport,
- (struct serial_struct __user *)arg);
- }
- return -ENOIOCTLCMD;
-}
-
-
static void ti_set_termios(struct tty_struct *tty,
struct usb_serial_port *port, struct ktermios *old_termios)
{
@@ -1119,7 +1101,7 @@ static void ti_break(struct tty_struct *tty, int break_state)
static int ti_get_port_from_code(unsigned char code)
{
- return (code >> 4) - 3;
+ return (code >> 6) & 0x01;
}
static int ti_get_func_from_code(unsigned char code)
@@ -1215,6 +1197,7 @@ static void ti_bulk_in_callback(struct urb *urb)
struct usb_serial_port *port = tport->tp_port;
struct device *dev = &urb->dev->dev;
int status = urb->status;
+ unsigned long flags;
int retval = 0;
switch (status) {
@@ -1247,20 +1230,20 @@ static void ti_bulk_in_callback(struct urb *urb)
__func__);
else
ti_recv(port, urb->transfer_buffer, urb->actual_length);
- spin_lock(&tport->tp_lock);
+ spin_lock_irqsave(&tport->tp_lock, flags);
port->icount.rx += urb->actual_length;
- spin_unlock(&tport->tp_lock);
+ spin_unlock_irqrestore(&tport->tp_lock, flags);
}
exit:
/* continue to read unless stopping */
- spin_lock(&tport->tp_lock);
+ spin_lock_irqsave(&tport->tp_lock, flags);
if (tport->tp_read_urb_state == TI_READ_URB_RUNNING)
retval = usb_submit_urb(urb, GFP_ATOMIC);
else if (tport->tp_read_urb_state == TI_READ_URB_STOPPING)
tport->tp_read_urb_state = TI_READ_URB_STOPPED;
- spin_unlock(&tport->tp_lock);
+ spin_unlock_irqrestore(&tport->tp_lock, flags);
if (retval)
dev_err(dev, "%s - resubmit read urb failed, %d\n",
__func__, retval);
@@ -1416,45 +1399,37 @@ free_data:
}
-static int ti_get_serial_info(struct ti_port *tport,
- struct serial_struct __user *ret_arg)
+static int ti_get_serial_info(struct tty_struct *tty,
+ struct serial_struct *ss)
{
- struct usb_serial_port *port = tport->tp_port;
- struct serial_struct ret_serial;
+ struct usb_serial_port *port = tty->driver_data;
+ struct ti_port *tport = usb_get_serial_port_data(port);
unsigned cwait;
cwait = port->port.closing_wait;
if (cwait != ASYNC_CLOSING_WAIT_NONE)
cwait = jiffies_to_msecs(cwait) / 10;
- memset(&ret_serial, 0, sizeof(ret_serial));
-
- ret_serial.type = PORT_16550A;
- ret_serial.line = port->minor;
- ret_serial.port = port->port_number;
- ret_serial.xmit_fifo_size = kfifo_size(&port->write_fifo);
- ret_serial.baud_base = tport->tp_tdev->td_is_3410 ? 921600 : 460800;
- ret_serial.closing_wait = cwait;
-
- if (copy_to_user(ret_arg, &ret_serial, sizeof(*ret_arg)))
- return -EFAULT;
-
+ ss->type = PORT_16550A;
+ ss->line = port->minor;
+ ss->port = port->port_number;
+ ss->xmit_fifo_size = kfifo_size(&port->write_fifo);
+ ss->baud_base = tport->tp_tdev->td_is_3410 ? 921600 : 460800;
+ ss->closing_wait = cwait;
return 0;
}
-static int ti_set_serial_info(struct tty_struct *tty, struct ti_port *tport,
- struct serial_struct __user *new_arg)
+static int ti_set_serial_info(struct tty_struct *tty,
+ struct serial_struct *ss)
{
- struct serial_struct new_serial;
+ struct usb_serial_port *port = tty->driver_data;
+ struct ti_port *tport = usb_get_serial_port_data(port);
unsigned cwait;
- if (copy_from_user(&new_serial, new_arg, sizeof(new_serial)))
- return -EFAULT;
-
- cwait = new_serial.closing_wait;
+ cwait = ss->closing_wait;
if (cwait != ASYNC_CLOSING_WAIT_NONE)
- cwait = msecs_to_jiffies(10 * new_serial.closing_wait);
+ cwait = msecs_to_jiffies(10 * ss->closing_wait);
tport->tp_port->port.closing_wait = cwait;
diff --git a/drivers/usb/serial/usb-serial-simple.c b/drivers/usb/serial/usb-serial-simple.c
index 40864c2bd9dc..4d0273508043 100644
--- a/drivers/usb/serial/usb-serial-simple.c
+++ b/drivers/usb/serial/usb-serial-simple.c
@@ -84,7 +84,8 @@ DEVICE(moto_modem, MOTO_IDS);
/* Motorola Tetra driver */
#define MOTOROLA_TETRA_IDS() \
- { USB_DEVICE(0x0cad, 0x9011) } /* Motorola Solutions TETRA PEI */
+ { USB_DEVICE(0x0cad, 0x9011) }, /* Motorola Solutions TETRA PEI */ \
+ { USB_DEVICE(0x0cad, 0x9012) } /* MTP6550 */
DEVICE(motorola_tetra, MOTOROLA_TETRA_IDS);
/* Novatel Wireless GPS driver */
diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c
index 790e0cbe3da9..7e89efbf2c28 100644
--- a/drivers/usb/serial/usb-serial.c
+++ b/drivers/usb/serial/usb-serial.c
@@ -192,7 +192,7 @@ static int serial_install(struct tty_driver *driver, struct tty_struct *tty)
if (retval)
goto error_get_interface;
- retval = tty_port_install(&port->port, driver, tty);
+ retval = tty_standard_install(driver, tty);
if (retval)
goto error_init_termios;
@@ -396,6 +396,24 @@ static void serial_unthrottle(struct tty_struct *tty)
port->serial->type->unthrottle(tty);
}
+static int serial_get_serial(struct tty_struct *tty, struct serial_struct *ss)
+{
+ struct usb_serial_port *port = tty->driver_data;
+
+ if (port->serial->type->get_serial)
+ return port->serial->type->get_serial(tty, ss);
+ return -ENOTTY;
+}
+
+static int serial_set_serial(struct tty_struct *tty, struct serial_struct *ss)
+{
+ struct usb_serial_port *port = tty->driver_data;
+
+ if (port->serial->type->set_serial)
+ return port->serial->type->set_serial(tty, ss);
+ return -ENOTTY;
+}
+
static int serial_ioctl(struct tty_struct *tty,
unsigned int cmd, unsigned long arg)
{
@@ -476,19 +494,6 @@ static int serial_proc_show(struct seq_file *m, void *v)
return 0;
}
-static int serial_proc_open(struct inode *inode, struct file *file)
-{
- return single_open(file, serial_proc_show, NULL);
-}
-
-static const struct file_operations serial_proc_fops = {
- .owner = THIS_MODULE,
- .open = serial_proc_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
static int serial_tiocmget(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
@@ -1190,9 +1195,11 @@ static const struct tty_operations serial_ops = {
.tiocmget = serial_tiocmget,
.tiocmset = serial_tiocmset,
.get_icount = serial_get_icount,
+ .set_serial = serial_set_serial,
+ .get_serial = serial_get_serial,
.cleanup = serial_cleanup,
.install = serial_install,
- .proc_fops = &serial_proc_fops,
+ .proc_show = serial_proc_show,
};
diff --git a/drivers/usb/serial/usb-wwan.h b/drivers/usb/serial/usb-wwan.h
index d28dab4b9eff..1c120eaf4091 100644
--- a/drivers/usb/serial/usb-wwan.h
+++ b/drivers/usb/serial/usb-wwan.h
@@ -15,8 +15,10 @@ extern int usb_wwan_write_room(struct tty_struct *tty);
extern int usb_wwan_tiocmget(struct tty_struct *tty);
extern int usb_wwan_tiocmset(struct tty_struct *tty,
unsigned int set, unsigned int clear);
-extern int usb_wwan_ioctl(struct tty_struct *tty,
- unsigned int cmd, unsigned long arg);
+extern int usb_wwan_get_serial_info(struct tty_struct *tty,
+ struct serial_struct *ss);
+extern int usb_wwan_set_serial_info(struct tty_struct *tty,
+ struct serial_struct *ss);
extern int usb_wwan_write(struct tty_struct *tty, struct usb_serial_port *port,
const unsigned char *buf, int count);
extern int usb_wwan_chars_in_buffer(struct tty_struct *tty);
diff --git a/drivers/usb/serial/usb_wwan.c b/drivers/usb/serial/usb_wwan.c
index 107e64c42e94..7e855c87e4f7 100644
--- a/drivers/usb/serial/usb_wwan.c
+++ b/drivers/usb/serial/usb_wwan.c
@@ -132,38 +132,32 @@ int usb_wwan_tiocmset(struct tty_struct *tty,
}
EXPORT_SYMBOL(usb_wwan_tiocmset);
-static int get_serial_info(struct usb_serial_port *port,
- struct serial_struct __user *retinfo)
+int usb_wwan_get_serial_info(struct tty_struct *tty,
+ struct serial_struct *ss)
{
- struct serial_struct tmp;
-
- memset(&tmp, 0, sizeof(tmp));
- tmp.line = port->minor;
- tmp.port = port->port_number;
- tmp.baud_base = tty_get_baud_rate(port->port.tty);
- tmp.close_delay = port->port.close_delay / 10;
- tmp.closing_wait = port->port.closing_wait == ASYNC_CLOSING_WAIT_NONE ?
+ struct usb_serial_port *port = tty->driver_data;
+
+ ss->line = port->minor;
+ ss->port = port->port_number;
+ ss->baud_base = tty_get_baud_rate(port->port.tty);
+ ss->close_delay = port->port.close_delay / 10;
+ ss->closing_wait = port->port.closing_wait == ASYNC_CLOSING_WAIT_NONE ?
ASYNC_CLOSING_WAIT_NONE :
port->port.closing_wait / 10;
-
- if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
- return -EFAULT;
return 0;
}
+EXPORT_SYMBOL(usb_wwan_get_serial_info);
-static int set_serial_info(struct usb_serial_port *port,
- struct serial_struct __user *newinfo)
+int usb_wwan_set_serial_info(struct tty_struct *tty,
+ struct serial_struct *ss)
{
- struct serial_struct new_serial;
+ struct usb_serial_port *port = tty->driver_data;
unsigned int closing_wait, close_delay;
int retval = 0;
- if (copy_from_user(&new_serial, newinfo, sizeof(new_serial)))
- return -EFAULT;
-
- close_delay = new_serial.close_delay * 10;
- closing_wait = new_serial.closing_wait == ASYNC_CLOSING_WAIT_NONE ?
- ASYNC_CLOSING_WAIT_NONE : new_serial.closing_wait * 10;
+ close_delay = ss->close_delay * 10;
+ closing_wait = ss->closing_wait == ASYNC_CLOSING_WAIT_NONE ?
+ ASYNC_CLOSING_WAIT_NONE : ss->closing_wait * 10;
mutex_lock(&port->port.mutex);
@@ -181,30 +175,7 @@ static int set_serial_info(struct usb_serial_port *port,
mutex_unlock(&port->port.mutex);
return retval;
}
-
-int usb_wwan_ioctl(struct tty_struct *tty,
- unsigned int cmd, unsigned long arg)
-{
- struct usb_serial_port *port = tty->driver_data;
-
- dev_dbg(&port->dev, "%s cmd 0x%04x\n", __func__, cmd);
-
- switch (cmd) {
- case TIOCGSERIAL:
- return get_serial_info(port,
- (struct serial_struct __user *) arg);
- case TIOCSSERIAL:
- return set_serial_info(port,
- (struct serial_struct __user *) arg);
- default:
- break;
- }
-
- dev_dbg(&port->dev, "%s arg not supported\n", __func__);
-
- return -ENOIOCTLCMD;
-}
-EXPORT_SYMBOL(usb_wwan_ioctl);
+EXPORT_SYMBOL(usb_wwan_set_serial_info);
int usb_wwan_write(struct tty_struct *tty, struct usb_serial_port *port,
const unsigned char *buf, int count)
@@ -326,6 +297,7 @@ static void usb_wwan_outdat_callback(struct urb *urb)
struct usb_serial_port *port;
struct usb_wwan_port_private *portdata;
struct usb_wwan_intf_private *intfdata;
+ unsigned long flags;
int i;
port = urb->context;
@@ -334,9 +306,9 @@ static void usb_wwan_outdat_callback(struct urb *urb)
usb_serial_port_softint(port);
usb_autopm_put_interface_async(port->serial->interface);
portdata = usb_get_serial_port_data(port);
- spin_lock(&intfdata->susp_lock);
+ spin_lock_irqsave(&intfdata->susp_lock, flags);
intfdata->in_flight--;
- spin_unlock(&intfdata->susp_lock);
+ spin_unlock_irqrestore(&intfdata->susp_lock, flags);
for (i = 0; i < N_OUT_URB; ++i) {
if (portdata->out_urbs[i] == urb) {
diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c
index 1c7b46a8620c..aefd84f88b59 100644
--- a/drivers/usb/serial/whiteheat.c
+++ b/drivers/usb/serial/whiteheat.c
@@ -83,8 +83,8 @@ static int whiteheat_port_remove(struct usb_serial_port *port);
static int whiteheat_open(struct tty_struct *tty,
struct usb_serial_port *port);
static void whiteheat_close(struct usb_serial_port *port);
-static int whiteheat_ioctl(struct tty_struct *tty,
- unsigned int cmd, unsigned long arg);
+static int whiteheat_get_serial(struct tty_struct *tty,
+ struct serial_struct *ss);
static void whiteheat_set_termios(struct tty_struct *tty,
struct usb_serial_port *port, struct ktermios *old);
static int whiteheat_tiocmget(struct tty_struct *tty);
@@ -120,7 +120,7 @@ static struct usb_serial_driver whiteheat_device = {
.port_remove = whiteheat_port_remove,
.open = whiteheat_open,
.close = whiteheat_close,
- .ioctl = whiteheat_ioctl,
+ .get_serial = whiteheat_get_serial,
.set_termios = whiteheat_set_termios,
.break_ctl = whiteheat_break_ctl,
.tiocmget = whiteheat_tiocmget,
@@ -442,33 +442,21 @@ static int whiteheat_tiocmset(struct tty_struct *tty,
}
-static int whiteheat_ioctl(struct tty_struct *tty,
- unsigned int cmd, unsigned long arg)
+static int whiteheat_get_serial(struct tty_struct *tty,
+ struct serial_struct *ss)
{
struct usb_serial_port *port = tty->driver_data;
- struct serial_struct serstruct;
- void __user *user_arg = (void __user *)arg;
-
- switch (cmd) {
- case TIOCGSERIAL:
- memset(&serstruct, 0, sizeof(serstruct));
- serstruct.type = PORT_16654;
- serstruct.line = port->minor;
- serstruct.port = port->port_number;
- serstruct.xmit_fifo_size = kfifo_size(&port->write_fifo);
- serstruct.custom_divisor = 0;
- serstruct.baud_base = 460800;
- serstruct.close_delay = CLOSING_DELAY;
- serstruct.closing_wait = CLOSING_DELAY;
-
- if (copy_to_user(user_arg, &serstruct, sizeof(serstruct)))
- return -EFAULT;
- break;
- default:
- break;
- }
- return -ENOIOCTLCMD;
+ ss->type = PORT_16654;
+ ss->line = port->minor;
+ ss->port = port->port_number;
+ ss->xmit_fifo_size = kfifo_size(&port->write_fifo);
+ ss->custom_divisor = 0;
+ ss->baud_base = 460800;
+ ss->close_delay = CLOSING_DELAY;
+ ss->closing_wait = CLOSING_DELAY;
+
+ return 0;
}
diff --git a/drivers/usb/storage/Kconfig b/drivers/usb/storage/Kconfig
index ec84758f0e23..6fd427284b12 100644
--- a/drivers/usb/storage/Kconfig
+++ b/drivers/usb/storage/Kconfig
@@ -23,16 +23,16 @@ config USB_STORAGE
To compile this driver as a module, choose M here: the
module will be called usb-storage.
+if USB_STORAGE
+
config USB_STORAGE_DEBUG
bool "USB Mass Storage verbose debug"
- depends on USB_STORAGE
help
Say Y here in order to have the USB Mass Storage code generate
verbose debugging messages.
config USB_STORAGE_REALTEK
tristate "Realtek Card Reader support"
- depends on USB_STORAGE
help
Say Y here to include additional code to support the power-saving function
for Realtek RTS51xx USB card readers.
@@ -46,7 +46,6 @@ config REALTEK_AUTOPM
config USB_STORAGE_DATAFAB
tristate "Datafab Compact Flash Reader support"
- depends on USB_STORAGE
help
Support for certain Datafab CompactFlash readers.
Datafab has a web page at <http://www.datafab.com/>.
@@ -55,7 +54,6 @@ config USB_STORAGE_DATAFAB
config USB_STORAGE_FREECOM
tristate "Freecom USB/ATAPI Bridge support"
- depends on USB_STORAGE
help
Support for the Freecom USB to IDE/ATAPI adaptor.
Freecom has a web page at <http://www.freecom.de/>.
@@ -64,7 +62,6 @@ config USB_STORAGE_FREECOM
config USB_STORAGE_ISD200
tristate "ISD-200 USB/ATA Bridge support"
- depends on USB_STORAGE
---help---
Say Y here if you want to use USB Mass Store devices based
on the In-Systems Design ISD-200 USB/ATA bridge.
@@ -82,7 +79,6 @@ config USB_STORAGE_ISD200
config USB_STORAGE_USBAT
tristate "USBAT/USBAT02-based storage support"
- depends on USB_STORAGE
help
Say Y here to include additional code to support storage devices
based on the SCM/Shuttle USBAT/USBAT02 processors.
@@ -105,7 +101,6 @@ config USB_STORAGE_USBAT
config USB_STORAGE_SDDR09
tristate "SanDisk SDDR-09 (and other SmartMedia, including DPCM) support"
- depends on USB_STORAGE
help
Say Y here to include additional code to support the Sandisk SDDR-09
SmartMedia reader in the USB Mass Storage driver.
@@ -115,7 +110,6 @@ config USB_STORAGE_SDDR09
config USB_STORAGE_SDDR55
tristate "SanDisk SDDR-55 SmartMedia support"
- depends on USB_STORAGE
help
Say Y here to include additional code to support the Sandisk SDDR-55
SmartMedia reader in the USB Mass Storage driver.
@@ -124,7 +118,6 @@ config USB_STORAGE_SDDR55
config USB_STORAGE_JUMPSHOT
tristate "Lexar Jumpshot Compact Flash Reader"
- depends on USB_STORAGE
help
Say Y here to include additional code to support the Lexar Jumpshot
USB CompactFlash reader.
@@ -133,7 +126,6 @@ config USB_STORAGE_JUMPSHOT
config USB_STORAGE_ALAUDA
tristate "Olympus MAUSB-10/Fuji DPC-R1 support"
- depends on USB_STORAGE
help
Say Y here to include additional code to support the Olympus MAUSB-10
and Fujifilm DPC-R1 USB Card reader/writer devices.
@@ -145,7 +137,6 @@ config USB_STORAGE_ALAUDA
config USB_STORAGE_ONETOUCH
tristate "Support OneTouch Button on Maxtor Hard Drives"
- depends on USB_STORAGE
depends on INPUT=y || INPUT=USB_STORAGE
help
Say Y here to include additional code to support the Maxtor OneTouch
@@ -160,7 +151,6 @@ config USB_STORAGE_ONETOUCH
config USB_STORAGE_KARMA
tristate "Support for Rio Karma music player"
- depends on USB_STORAGE
help
Say Y here to include additional code to support the Rio Karma
USB interface.
@@ -174,7 +164,6 @@ config USB_STORAGE_KARMA
config USB_STORAGE_CYPRESS_ATACB
tristate "SAT emulation on Cypress USB/ATA Bridge with ATACB"
- depends on USB_STORAGE
---help---
Say Y here if you want to use SAT (ata pass through) on devices based
on the Cypress USB/ATA bridge supporting ATACB. This will allow you
@@ -187,19 +176,15 @@ config USB_STORAGE_CYPRESS_ATACB
config USB_STORAGE_ENE_UB6250
tristate "USB ENE card reader support"
- depends on SCSI
- depends on USB_STORAGE
---help---
Say Y here if you wish to control a ENE SD/MS Card reader.
Note that this driver does not support SM cards.
- This option depends on 'SCSI' support being enabled, but you
- probably also need 'SCSI device support: SCSI disk support'
- (BLK_DEV_SD) for most USB storage devices.
-
To compile this driver as a module, choose M here: the
module will be called ums-eneub6250.
+endif # USB_STORAGE
+
config USB_UAS
tristate "USB Attached SCSI"
depends on SCSI && USB_STORAGE
diff --git a/drivers/usb/storage/alauda.c b/drivers/usb/storage/alauda.c
index 900591df8bb2..6b8edf6178df 100644
--- a/drivers/usb/storage/alauda.c
+++ b/drivers/usb/storage/alauda.c
@@ -1025,7 +1025,7 @@ static int alauda_write_data(struct us_data *us, unsigned long address,
* We also need a temporary block buffer, where we read in the old data,
* overwrite parts with the new data, and manipulate the redundancy data
*/
- blockbuffer = kmalloc((pagesize + 64) * blocksize, GFP_NOIO);
+ blockbuffer = kmalloc_array(pagesize + 64, blocksize, GFP_NOIO);
if (!blockbuffer) {
kfree(buffer);
return USB_STOR_TRANSPORT_ERROR;
diff --git a/drivers/usb/storage/ene_ub6250.c b/drivers/usb/storage/ene_ub6250.c
index 93cf57ac47d6..4d261e4de9ad 100644
--- a/drivers/usb/storage/ene_ub6250.c
+++ b/drivers/usb/storage/ene_ub6250.c
@@ -807,8 +807,12 @@ static int ms_lib_alloc_logicalmap(struct us_data *us)
u32 i;
struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra;
- info->MS_Lib.Phy2LogMap = kmalloc(info->MS_Lib.NumberOfPhyBlock * sizeof(u16), GFP_KERNEL);
- info->MS_Lib.Log2PhyMap = kmalloc(info->MS_Lib.NumberOfLogBlock * sizeof(u16), GFP_KERNEL);
+ info->MS_Lib.Phy2LogMap = kmalloc_array(info->MS_Lib.NumberOfPhyBlock,
+ sizeof(u16),
+ GFP_KERNEL);
+ info->MS_Lib.Log2PhyMap = kmalloc_array(info->MS_Lib.NumberOfLogBlock,
+ sizeof(u16),
+ GFP_KERNEL);
if ((info->MS_Lib.Phy2LogMap == NULL) || (info->MS_Lib.Log2PhyMap == NULL)) {
ms_lib_free_logicalmap(us);
@@ -1113,8 +1117,12 @@ static int ms_lib_alloc_writebuf(struct us_data *us)
info->MS_Lib.wrtblk = (u16)-1;
- info->MS_Lib.blkpag = kmalloc(info->MS_Lib.PagesPerBlock * info->MS_Lib.BytesPerSector, GFP_KERNEL);
- info->MS_Lib.blkext = kmalloc(info->MS_Lib.PagesPerBlock * sizeof(struct ms_lib_type_extdat), GFP_KERNEL);
+ 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);
if ((info->MS_Lib.blkpag == NULL) || (info->MS_Lib.blkext == NULL)) {
ms_lib_free_writebuf(us);
diff --git a/drivers/usb/storage/freecom.c b/drivers/usb/storage/freecom.c
index ec4d92c92762..4f542df37a44 100644
--- a/drivers/usb/storage/freecom.c
+++ b/drivers/usb/storage/freecom.c
@@ -464,7 +464,7 @@ static int init_freecom(struct us_data *us)
usb_stor_dbg(us, "result from activate reset is %d\n", result);
/* wait 250ms */
- mdelay(250);
+ msleep(250);
/* clear reset */
result = usb_stor_control_msg(us, us->send_ctrl_pipe,
@@ -472,7 +472,7 @@ static int init_freecom(struct us_data *us)
usb_stor_dbg(us, "result from clear reset is %d\n", result);
/* wait 3 seconds */
- mdelay(3 * 1000);
+ msleep(3 * 1000);
return USB_STOR_TRANSPORT_GOOD;
}
diff --git a/drivers/usb/storage/isd200.c b/drivers/usb/storage/isd200.c
index f5e4500d9970..2b474d60b4db 100644
--- a/drivers/usb/storage/isd200.c
+++ b/drivers/usb/storage/isd200.c
@@ -1153,7 +1153,7 @@ static int isd200_get_inquiry_data( struct us_data *us )
/* Fill in vendor identification fields */
src = (__be16 *)&id[ATA_ID_PROD];
dest = (__u16*)info->InquiryData.VendorId;
- for (i=0;i<4;i++)
+ for (i = 0; i < 4; i++)
dest[i] = be16_to_cpu(src[i]);
src = (__be16 *)&id[ATA_ID_PROD + 8/2];
diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c
index c267f2812a04..e227bb5b794f 100644
--- a/drivers/usb/storage/scsiglue.c
+++ b/drivers/usb/storage/scsiglue.c
@@ -376,6 +376,15 @@ static int queuecommand_lck(struct scsi_cmnd *srb,
return 0;
}
+ if ((us->fflags & US_FL_NO_ATA_1X) &&
+ (srb->cmnd[0] == ATA_12 || srb->cmnd[0] == ATA_16)) {
+ memcpy(srb->sense_buffer, usb_stor_sense_invalidCDB,
+ sizeof(usb_stor_sense_invalidCDB));
+ srb->result = SAM_STAT_CHECK_CONDITION;
+ done(srb);
+ return 0;
+ }
+
/* enqueue the command and wake up the control thread */
srb->scsi_done = done;
us->srb = srb;
diff --git a/drivers/usb/storage/sddr09.c b/drivers/usb/storage/sddr09.c
index 1cf7dbfe277c..bc9da736bdfc 100644
--- a/drivers/usb/storage/sddr09.c
+++ b/drivers/usb/storage/sddr09.c
@@ -1231,8 +1231,8 @@ sddr09_read_map(struct us_data *us) {
kfree(info->lba_to_pba);
kfree(info->pba_to_lba);
- info->lba_to_pba = kmalloc(numblocks*sizeof(int), GFP_NOIO);
- info->pba_to_lba = kmalloc(numblocks*sizeof(int), GFP_NOIO);
+ info->lba_to_pba = kmalloc_array(numblocks, sizeof(int), GFP_NOIO);
+ info->pba_to_lba = kmalloc_array(numblocks, sizeof(int), GFP_NOIO);
if (info->lba_to_pba == NULL || info->pba_to_lba == NULL) {
printk(KERN_WARNING "sddr09_read_map: out of memory\n");
diff --git a/drivers/usb/storage/sddr55.c b/drivers/usb/storage/sddr55.c
index 8c814b2ec9b2..b8527c55335b 100644
--- a/drivers/usb/storage/sddr55.c
+++ b/drivers/usb/storage/sddr55.c
@@ -651,7 +651,7 @@ static int sddr55_read_map(struct us_data *us) {
numblocks = info->capacity >> (info->blockshift + info->pageshift);
- buffer = kmalloc( numblocks * 2, GFP_NOIO );
+ buffer = kmalloc_array(numblocks, 2, GFP_NOIO );
if (!buffer)
return -1;
@@ -684,8 +684,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(numblocks*sizeof(int), GFP_NOIO);
- info->pba_to_lba = kmalloc(numblocks*sizeof(int), GFP_NOIO);
+ info->lba_to_pba = kmalloc_array(numblocks, sizeof(int), GFP_NOIO);
+ info->pba_to_lba = kmalloc_array(numblocks, sizeof(int), GFP_NOIO);
if (info->lba_to_pba == NULL || info->pba_to_lba == NULL) {
kfree(info->lba_to_pba);
diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c
index 6034c39b67d1..1f7b401c4d04 100644
--- a/drivers/usb/storage/uas.c
+++ b/drivers/usb/storage/uas.c
@@ -836,6 +836,33 @@ static int uas_slave_configure(struct scsi_device *sdev)
if (devinfo->flags & US_FL_BROKEN_FUA)
sdev->broken_fua = 1;
+ /* UAS also needs to support FL_ALWAYS_SYNC */
+ if (devinfo->flags & US_FL_ALWAYS_SYNC) {
+ sdev->skip_ms_page_3f = 1;
+ sdev->skip_ms_page_8 = 1;
+ sdev->wce_default_on = 1;
+ }
+
+ /*
+ * Some disks return the total number of blocks in response
+ * to READ CAPACITY rather than the highest block number.
+ * If this device makes that mistake, tell the sd driver.
+ */
+ if (devinfo->flags & US_FL_FIX_CAPACITY)
+ sdev->fix_capacity = 1;
+
+ /*
+ * Some devices don't like MODE SENSE with page=0x3f,
+ * which is the command used for checking if a device
+ * is write-protected. Now that we tell the sd driver
+ * to do a 192-byte transfer with this command the
+ * majority of devices work fine, but a few still can't
+ * handle it. The sd driver will simply assume those
+ * devices are write-enabled.
+ */
+ if (devinfo->flags & US_FL_NO_WP_DETECT)
+ sdev->skip_ms_page_3f = 1;
+
scsi_change_queue_depth(sdev, devinfo->qdepth - 2);
return 0;
}
diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h
index 747d3a9596d9..f7f83b21dc74 100644
--- a/drivers/usb/storage/unusual_devs.h
+++ b/drivers/usb/storage/unusual_devs.h
@@ -2288,6 +2288,13 @@ UNUSUAL_DEV( 0x2735, 0x100b, 0x0000, 0x9999,
USB_SC_DEVICE, USB_PR_DEVICE, NULL,
US_FL_GO_SLOW ),
+/* Reported-by: Tim Anderson <tsa@biglakesoftware.com> */
+UNUSUAL_DEV( 0x2ca3, 0x0031, 0x0000, 0x9999,
+ "DJI",
+ "CineSSD",
+ USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+ US_FL_NO_ATA_1X),
+
/*
* Reported by Frederic Marchal <frederic.marchal@wowcompany.com>
* Mio Moov 330
@@ -2321,6 +2328,15 @@ UNUSUAL_DEV( 0x4146, 0xba01, 0x0100, 0x0100,
"Micro Mini 1GB",
USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_NOT_LOCKABLE ),
+/* "G-DRIVE" external HDD hangs on write without these.
+ * Patch submitted by Alexander Kappner <agk@godking.net>
+ */
+UNUSUAL_DEV(0x4971, 0x8024, 0x0000, 0x9999,
+ "SimpleTech",
+ "External HDD",
+ USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+ US_FL_ALWAYS_SYNC),
+
/*
* Nick Bowler <nbowler@elliptictech.com>
* SCSI stack spams (otherwise harmless) error messages.
diff --git a/drivers/usb/storage/unusual_realtek.h b/drivers/usb/storage/unusual_realtek.h
index d17cd95b55bb..6b2140f966ef 100644
--- a/drivers/usb/storage/unusual_realtek.h
+++ b/drivers/usb/storage/unusual_realtek.h
@@ -27,4 +27,14 @@ UNUSUAL_DEV(0x0bda, 0x0159, 0x0000, 0x9999,
"USB Card Reader",
USB_SC_DEVICE, USB_PR_DEVICE, init_realtek_cr, 0),
+UNUSUAL_DEV(0x0bda, 0x0177, 0x0000, 0x9999,
+ "Realtek",
+ "USB Card Reader",
+ USB_SC_DEVICE, USB_PR_DEVICE, init_realtek_cr, 0),
+
+UNUSUAL_DEV(0x0bda, 0x0184, 0x0000, 0x9999,
+ "Realtek",
+ "USB Card Reader",
+ USB_SC_DEVICE, USB_PR_DEVICE, init_realtek_cr, 0),
+
#endif /* defined(CONFIG_USB_STORAGE_REALTEK) || ... */
diff --git a/drivers/usb/storage/unusual_uas.h b/drivers/usb/storage/unusual_uas.h
index 38434d88954a..d0bdebd87ce3 100644
--- a/drivers/usb/storage/unusual_uas.h
+++ b/drivers/usb/storage/unusual_uas.h
@@ -107,3 +107,12 @@ UNUSUAL_DEV(0x4971, 0x8017, 0x0000, 0x9999,
"External HDD",
USB_SC_DEVICE, USB_PR_DEVICE, NULL,
US_FL_NO_REPORT_OPCODES),
+
+/* "G-DRIVE" external HDD hangs on write without these.
+ * Patch submitted by Alexander Kappner <agk@godking.net>
+ */
+UNUSUAL_DEV(0x4971, 0x8024, 0x0000, 0x9999,
+ "SimpleTech",
+ "External HDD",
+ USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+ US_FL_ALWAYS_SYNC),
diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig
index 030f88cb0c3f..30a847c2089d 100644
--- a/drivers/usb/typec/Kconfig
+++ b/drivers/usb/typec/Kconfig
@@ -45,33 +45,7 @@ menuconfig TYPEC
if TYPEC
-config TYPEC_TCPM
- tristate "USB Type-C Port Controller Manager"
- depends on USB
- select USB_ROLE_SWITCH
- help
- The Type-C Port Controller Manager provides a USB PD and USB Type-C
- state machine for use with Type-C Port Controllers.
-
-if TYPEC_TCPM
-
-source "drivers/usb/typec/fusb302/Kconfig"
-
-config TYPEC_WCOVE
- tristate "Intel WhiskeyCove PMIC USB Type-C PHY driver"
- depends on ACPI
- depends on INTEL_SOC_PMIC
- depends on INTEL_PMC_IPC
- depends on BXT_WC_PMIC_OPREGION
- help
- This driver adds support for USB Type-C detection on Intel Broxton
- platforms that have Intel Whiskey Cove PMIC. The driver can detect the
- role and cable orientation.
-
- To compile this driver as module, choose M here: the module will be
- called typec_wcove
-
-endif # TYPEC_TCPM
+source "drivers/usb/typec/tcpm/Kconfig"
source "drivers/usb/typec/ucsi/Kconfig"
@@ -87,4 +61,6 @@ config TYPEC_TPS6598X
source "drivers/usb/typec/mux/Kconfig"
+source "drivers/usb/typec/altmodes/Kconfig"
+
endif # TYPEC
diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile
index 1f599a6c30cc..6696b7263d61 100644
--- a/drivers/usb/typec/Makefile
+++ b/drivers/usb/typec/Makefile
@@ -1,9 +1,8 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_TYPEC) += typec.o
-typec-y := class.o mux.o
-obj-$(CONFIG_TYPEC_TCPM) += tcpm.o
-obj-y += fusb302/
-obj-$(CONFIG_TYPEC_WCOVE) += typec_wcove.o
+typec-y := class.o mux.o bus.o
+obj-$(CONFIG_TYPEC) += altmodes/
+obj-$(CONFIG_TYPEC_TCPM) += tcpm/
obj-$(CONFIG_TYPEC_UCSI) += ucsi/
obj-$(CONFIG_TYPEC_TPS6598X) += tps6598x.o
obj-$(CONFIG_TYPEC) += mux/
diff --git a/drivers/usb/typec/altmodes/Kconfig b/drivers/usb/typec/altmodes/Kconfig
new file mode 100644
index 000000000000..efef2a64bc51
--- /dev/null
+++ b/drivers/usb/typec/altmodes/Kconfig
@@ -0,0 +1,14 @@
+
+menu "USB Type-C Alternate Mode drivers"
+
+config TYPEC_DP_ALTMODE
+ tristate "DisplayPort Alternate Mode driver"
+ help
+ DisplayPort USB Type-C Alternate Mode allows DisplayPort
+ displays and adapters to be attached to the USB Type-C
+ connectors on the system.
+
+ To compile this driver as a module, choose M here: the
+ module will be called typec_displayport.
+
+endmenu
diff --git a/drivers/usb/typec/altmodes/Makefile b/drivers/usb/typec/altmodes/Makefile
new file mode 100644
index 000000000000..5caf094ef71a
--- /dev/null
+++ b/drivers/usb/typec/altmodes/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_TYPEC_DP_ALTMODE) += typec_displayport.o
+typec_displayport-y := displayport.o
diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c
new file mode 100644
index 000000000000..3f06e94771a7
--- /dev/null
+++ b/drivers/usb/typec/altmodes/displayport.c
@@ -0,0 +1,580 @@
+// SPDX-License-Identifier: GPL-2.0
+/**
+ * USB Typec-C DisplayPort Alternate Mode driver
+ *
+ * Copyright (C) 2018 Intel Corporation
+ * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+ *
+ * DisplayPort is trademark of VESA (www.vesa.org)
+ */
+
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/usb/pd_vdo.h>
+#include <linux/usb/typec_dp.h>
+
+#define DP_HEADER(cmd) (VDO(USB_TYPEC_DP_SID, 1, cmd) | \
+ VDO_OPOS(USB_TYPEC_DP_MODE))
+
+enum {
+ DP_CONF_USB,
+ DP_CONF_DFP_D,
+ DP_CONF_UFP_D,
+ DP_CONF_DUAL_D,
+};
+
+/* Helper for setting/getting the pin assignement value to the configuration */
+#define DP_CONF_SET_PIN_ASSIGN(_a_) ((_a_) << 8)
+#define DP_CONF_GET_PIN_ASSIGN(_conf_) (((_conf_) & GENMASK(15, 8)) >> 8)
+
+/* Pin assignments that use USB3.1 Gen2 signaling to carry DP protocol */
+#define DP_PIN_ASSIGN_GEN2_BR_MASK (BIT(DP_PIN_ASSIGN_A) | \
+ BIT(DP_PIN_ASSIGN_B))
+
+/* Pin assignments that use DP v1.3 signaling to carry DP protocol */
+#define DP_PIN_ASSIGN_DP_BR_MASK (BIT(DP_PIN_ASSIGN_C) | \
+ BIT(DP_PIN_ASSIGN_D) | \
+ BIT(DP_PIN_ASSIGN_E) | \
+ BIT(DP_PIN_ASSIGN_F))
+
+/* DP only pin assignments */
+#define DP_PIN_ASSIGN_DP_ONLY_MASK (BIT(DP_PIN_ASSIGN_A) | \
+ BIT(DP_PIN_ASSIGN_C) | \
+ BIT(DP_PIN_ASSIGN_E))
+
+/* Pin assignments where one channel is for USB */
+#define DP_PIN_ASSIGN_MULTI_FUNC_MASK (BIT(DP_PIN_ASSIGN_B) | \
+ BIT(DP_PIN_ASSIGN_D) | \
+ BIT(DP_PIN_ASSIGN_F))
+
+enum dp_state {
+ DP_STATE_IDLE,
+ DP_STATE_ENTER,
+ DP_STATE_UPDATE,
+ DP_STATE_CONFIGURE,
+ DP_STATE_EXIT,
+};
+
+struct dp_altmode {
+ struct typec_displayport_data data;
+
+ enum dp_state state;
+
+ struct mutex lock; /* device lock */
+ struct work_struct work;
+ struct typec_altmode *alt;
+ const struct typec_altmode *port;
+};
+
+static int dp_altmode_notify(struct dp_altmode *dp)
+{
+ u8 state = get_count_order(DP_CONF_GET_PIN_ASSIGN(dp->data.conf));
+
+ return typec_altmode_notify(dp->alt, TYPEC_MODAL_STATE(state),
+ &dp->data);
+}
+
+static int dp_altmode_configure(struct dp_altmode *dp, u8 con)
+{
+ u32 conf = DP_CONF_SIGNALING_DP; /* Only DP signaling supported */
+ u8 pin_assign = 0;
+
+ switch (con) {
+ case DP_STATUS_CON_DISABLED:
+ return 0;
+ case DP_STATUS_CON_DFP_D:
+ conf |= DP_CONF_UFP_U_AS_DFP_D;
+ pin_assign = DP_CAP_UFP_D_PIN_ASSIGN(dp->alt->vdo) &
+ DP_CAP_DFP_D_PIN_ASSIGN(dp->port->vdo);
+ break;
+ case DP_STATUS_CON_UFP_D:
+ case DP_STATUS_CON_BOTH: /* NOTE: First acting as DP source */
+ conf |= DP_CONF_UFP_U_AS_UFP_D;
+ pin_assign = DP_CAP_DFP_D_PIN_ASSIGN(dp->alt->vdo) &
+ DP_CAP_UFP_D_PIN_ASSIGN(dp->port->vdo);
+ break;
+ default:
+ break;
+ }
+
+ /* Determining the initial pin assignment. */
+ if (!DP_CONF_GET_PIN_ASSIGN(dp->data.conf)) {
+ /* Is USB together with DP preferred */
+ if (dp->data.status & DP_STATUS_PREFER_MULTI_FUNC &&
+ pin_assign & DP_PIN_ASSIGN_MULTI_FUNC_MASK)
+ pin_assign &= DP_PIN_ASSIGN_MULTI_FUNC_MASK;
+ else
+ pin_assign &= DP_PIN_ASSIGN_DP_ONLY_MASK;
+
+ if (!pin_assign)
+ return -EINVAL;
+
+ conf |= DP_CONF_SET_PIN_ASSIGN(pin_assign);
+ }
+
+ dp->data.conf = conf;
+
+ return 0;
+}
+
+static int dp_altmode_status_update(struct dp_altmode *dp)
+{
+ bool configured = !!DP_CONF_GET_PIN_ASSIGN(dp->data.conf);
+ u8 con = DP_STATUS_CONNECTION(dp->data.status);
+ int ret = 0;
+
+ if (configured && (dp->data.status & DP_STATUS_SWITCH_TO_USB)) {
+ dp->data.conf = 0;
+ dp->state = DP_STATE_CONFIGURE;
+ } else if (dp->data.status & DP_STATUS_EXIT_DP_MODE) {
+ dp->state = DP_STATE_EXIT;
+ } else if (!(con & DP_CONF_CURRENTLY(dp->data.conf))) {
+ ret = dp_altmode_configure(dp, con);
+ if (!ret)
+ dp->state = DP_STATE_CONFIGURE;
+ }
+
+ return ret;
+}
+
+static int dp_altmode_configured(struct dp_altmode *dp)
+{
+ int ret;
+
+ sysfs_notify(&dp->alt->dev.kobj, "displayport", "configuration");
+
+ if (!dp->data.conf)
+ return typec_altmode_notify(dp->alt, TYPEC_STATE_USB,
+ &dp->data);
+
+ ret = dp_altmode_notify(dp);
+ if (ret)
+ return ret;
+
+ sysfs_notify(&dp->alt->dev.kobj, "displayport", "pin_assignment");
+
+ return 0;
+}
+
+static int dp_altmode_configure_vdm(struct dp_altmode *dp, u32 conf)
+{
+ u32 header = DP_HEADER(DP_CMD_CONFIGURE);
+ int ret;
+
+ ret = typec_altmode_notify(dp->alt, TYPEC_STATE_SAFE, &dp->data);
+ if (ret) {
+ dev_err(&dp->alt->dev,
+ "unable to put to connector to safe mode\n");
+ return ret;
+ }
+
+ ret = typec_altmode_vdm(dp->alt, header, &conf, 2);
+ if (ret) {
+ if (DP_CONF_GET_PIN_ASSIGN(dp->data.conf))
+ dp_altmode_notify(dp);
+ else
+ typec_altmode_notify(dp->alt, TYPEC_STATE_USB,
+ &dp->data);
+ }
+
+ return ret;
+}
+
+static void dp_altmode_work(struct work_struct *work)
+{
+ struct dp_altmode *dp = container_of(work, struct dp_altmode, work);
+ u32 header;
+ u32 vdo;
+ int ret;
+
+ mutex_lock(&dp->lock);
+
+ switch (dp->state) {
+ case DP_STATE_ENTER:
+ ret = typec_altmode_enter(dp->alt);
+ if (ret)
+ dev_err(&dp->alt->dev, "failed to enter mode\n");
+ break;
+ case DP_STATE_UPDATE:
+ header = DP_HEADER(DP_CMD_STATUS_UPDATE);
+ vdo = 1;
+ ret = typec_altmode_vdm(dp->alt, header, &vdo, 2);
+ if (ret)
+ dev_err(&dp->alt->dev,
+ "unable to send Status Update command (%d)\n",
+ ret);
+ break;
+ case DP_STATE_CONFIGURE:
+ ret = dp_altmode_configure_vdm(dp, dp->data.conf);
+ if (ret)
+ dev_err(&dp->alt->dev,
+ "unable to send Configure command (%d)\n", ret);
+ break;
+ case DP_STATE_EXIT:
+ if (typec_altmode_exit(dp->alt))
+ dev_err(&dp->alt->dev, "Exit Mode Failed!\n");
+ break;
+ default:
+ break;
+ }
+
+ dp->state = DP_STATE_IDLE;
+
+ mutex_unlock(&dp->lock);
+}
+
+static void dp_altmode_attention(struct typec_altmode *alt, const u32 vdo)
+{
+ struct dp_altmode *dp = typec_altmode_get_drvdata(alt);
+ u8 old_state;
+
+ mutex_lock(&dp->lock);
+
+ old_state = dp->state;
+ dp->data.status = vdo;
+
+ if (old_state != DP_STATE_IDLE)
+ dev_warn(&alt->dev, "ATTENTION while processing state %d\n",
+ old_state);
+
+ if (dp_altmode_status_update(dp))
+ dev_warn(&alt->dev, "%s: status update failed\n", __func__);
+
+ if (dp_altmode_notify(dp))
+ dev_err(&alt->dev, "%s: notification failed\n", __func__);
+
+ if (old_state == DP_STATE_IDLE && dp->state != DP_STATE_IDLE)
+ schedule_work(&dp->work);
+
+ mutex_unlock(&dp->lock);
+}
+
+static int dp_altmode_vdm(struct typec_altmode *alt,
+ const u32 hdr, const u32 *vdo, int count)
+{
+ struct dp_altmode *dp = typec_altmode_get_drvdata(alt);
+ int cmd_type = PD_VDO_CMDT(hdr);
+ int cmd = PD_VDO_CMD(hdr);
+ int ret = 0;
+
+ mutex_lock(&dp->lock);
+
+ if (dp->state != DP_STATE_IDLE) {
+ ret = -EBUSY;
+ goto err_unlock;
+ }
+
+ switch (cmd_type) {
+ case CMDT_RSP_ACK:
+ switch (cmd) {
+ case CMD_ENTER_MODE:
+ dp->state = DP_STATE_UPDATE;
+ break;
+ case CMD_EXIT_MODE:
+ dp->data.status = 0;
+ dp->data.conf = 0;
+ break;
+ case DP_CMD_STATUS_UPDATE:
+ dp->data.status = *vdo;
+ ret = dp_altmode_status_update(dp);
+ break;
+ case DP_CMD_CONFIGURE:
+ ret = dp_altmode_configured(dp);
+ break;
+ default:
+ break;
+ }
+ break;
+ case CMDT_RSP_NAK:
+ switch (cmd) {
+ case DP_CMD_CONFIGURE:
+ dp->data.conf = 0;
+ ret = dp_altmode_configured(dp);
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (dp->state != DP_STATE_IDLE)
+ schedule_work(&dp->work);
+
+err_unlock:
+ mutex_unlock(&dp->lock);
+ return ret;
+}
+
+static int dp_altmode_activate(struct typec_altmode *alt, int activate)
+{
+ return activate ? typec_altmode_enter(alt) : typec_altmode_exit(alt);
+}
+
+static const struct typec_altmode_ops dp_altmode_ops = {
+ .attention = dp_altmode_attention,
+ .vdm = dp_altmode_vdm,
+ .activate = dp_altmode_activate,
+};
+
+static const char * const configurations[] = {
+ [DP_CONF_USB] = "USB",
+ [DP_CONF_DFP_D] = "source",
+ [DP_CONF_UFP_D] = "sink",
+};
+
+static ssize_t
+configuration_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct dp_altmode *dp = dev_get_drvdata(dev);
+ u32 conf;
+ u32 cap;
+ int con;
+ int ret = 0;
+
+ con = sysfs_match_string(configurations, buf);
+ if (con < 0)
+ return con;
+
+ mutex_lock(&dp->lock);
+
+ if (dp->state != DP_STATE_IDLE) {
+ ret = -EBUSY;
+ goto err_unlock;
+ }
+
+ cap = DP_CAP_CAPABILITY(dp->alt->vdo);
+
+ if ((con == DP_CONF_DFP_D && !(cap & DP_CAP_DFP_D)) ||
+ (con == DP_CONF_UFP_D && !(cap & DP_CAP_UFP_D))) {
+ ret = -EINVAL;
+ goto err_unlock;
+ }
+
+ conf = dp->data.conf & ~DP_CONF_DUAL_D;
+ conf |= con;
+
+ if (dp->alt->active) {
+ ret = dp_altmode_configure_vdm(dp, conf);
+ if (ret)
+ goto err_unlock;
+ }
+
+ dp->data.conf = conf;
+
+err_unlock:
+ mutex_unlock(&dp->lock);
+
+ return ret ? ret : size;
+}
+
+static ssize_t configuration_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct dp_altmode *dp = dev_get_drvdata(dev);
+ int len;
+ u8 cap;
+ u8 cur;
+ int i;
+
+ mutex_lock(&dp->lock);
+
+ cap = DP_CAP_CAPABILITY(dp->alt->vdo);
+ cur = DP_CONF_CURRENTLY(dp->data.conf);
+
+ len = sprintf(buf, "%s ", cur ? "USB" : "[USB]");
+
+ for (i = 1; i < ARRAY_SIZE(configurations); i++) {
+ if (i == cur)
+ len += sprintf(buf + len, "[%s] ", configurations[i]);
+ else if ((i == DP_CONF_DFP_D && cap & DP_CAP_DFP_D) ||
+ (i == DP_CONF_UFP_D && cap & DP_CAP_UFP_D))
+ len += sprintf(buf + len, "%s ", configurations[i]);
+ }
+
+ mutex_unlock(&dp->lock);
+
+ buf[len - 1] = '\n';
+ return len;
+}
+static DEVICE_ATTR_RW(configuration);
+
+static const char * const pin_assignments[] = {
+ [DP_PIN_ASSIGN_A] = "A",
+ [DP_PIN_ASSIGN_B] = "B",
+ [DP_PIN_ASSIGN_C] = "C",
+ [DP_PIN_ASSIGN_D] = "D",
+ [DP_PIN_ASSIGN_E] = "E",
+ [DP_PIN_ASSIGN_F] = "F",
+};
+
+static ssize_t
+pin_assignment_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct dp_altmode *dp = dev_get_drvdata(dev);
+ u8 assignments;
+ u32 conf;
+ int ret;
+
+ ret = sysfs_match_string(pin_assignments, buf);
+ if (ret < 0)
+ return ret;
+
+ conf = DP_CONF_SET_PIN_ASSIGN(BIT(ret));
+ ret = 0;
+
+ mutex_lock(&dp->lock);
+
+ if (conf & dp->data.conf)
+ goto out_unlock;
+
+ if (dp->state != DP_STATE_IDLE) {
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+
+ if (DP_CONF_CURRENTLY(dp->data.conf) == DP_CONF_DFP_D)
+ assignments = DP_CAP_UFP_D_PIN_ASSIGN(dp->alt->vdo);
+ else
+ assignments = DP_CAP_DFP_D_PIN_ASSIGN(dp->alt->vdo);
+
+ if (!(DP_CONF_GET_PIN_ASSIGN(conf) & assignments)) {
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+
+ conf |= dp->data.conf & ~DP_CONF_PIN_ASSIGNEMENT_MASK;
+
+ /* Only send Configure command if a configuration has been set */
+ if (dp->alt->active && DP_CONF_CURRENTLY(dp->data.conf)) {
+ ret = dp_altmode_configure_vdm(dp, conf);
+ if (ret)
+ goto out_unlock;
+ }
+
+ dp->data.conf = conf;
+
+out_unlock:
+ mutex_unlock(&dp->lock);
+
+ return ret ? ret : size;
+}
+
+static ssize_t pin_assignment_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct dp_altmode *dp = dev_get_drvdata(dev);
+ u8 assignments;
+ int len = 0;
+ u8 cur;
+ int i;
+
+ mutex_lock(&dp->lock);
+
+ cur = get_count_order(DP_CONF_GET_PIN_ASSIGN(dp->data.conf));
+
+ if (DP_CONF_CURRENTLY(dp->data.conf) == DP_CONF_DFP_D)
+ assignments = DP_CAP_UFP_D_PIN_ASSIGN(dp->alt->vdo);
+ else
+ assignments = DP_CAP_DFP_D_PIN_ASSIGN(dp->alt->vdo);
+
+ for (i = 0; assignments; assignments >>= 1, i++) {
+ if (assignments & 1) {
+ if (i == cur)
+ len += sprintf(buf + len, "[%s] ",
+ pin_assignments[i]);
+ else
+ len += sprintf(buf + len, "%s ",
+ pin_assignments[i]);
+ }
+ }
+
+ mutex_unlock(&dp->lock);
+
+ buf[len - 1] = '\n';
+ return len;
+}
+static DEVICE_ATTR_RW(pin_assignment);
+
+static struct attribute *dp_altmode_attrs[] = {
+ &dev_attr_configuration.attr,
+ &dev_attr_pin_assignment.attr,
+ NULL
+};
+
+static const struct attribute_group dp_altmode_group = {
+ .name = "displayport",
+ .attrs = dp_altmode_attrs,
+};
+
+static int dp_altmode_probe(struct typec_altmode *alt)
+{
+ const struct typec_altmode *port = typec_altmode_get_partner(alt);
+ struct dp_altmode *dp;
+ int ret;
+
+ /* FIXME: Port can only be DFP_U. */
+
+ /* Make sure we have compatiple pin configurations */
+ if (!(DP_CAP_DFP_D_PIN_ASSIGN(port->vdo) &
+ DP_CAP_UFP_D_PIN_ASSIGN(alt->vdo)) &&
+ !(DP_CAP_UFP_D_PIN_ASSIGN(port->vdo) &
+ DP_CAP_DFP_D_PIN_ASSIGN(alt->vdo)))
+ return -ENODEV;
+
+ ret = sysfs_create_group(&alt->dev.kobj, &dp_altmode_group);
+ if (ret)
+ return ret;
+
+ dp = devm_kzalloc(&alt->dev, sizeof(*dp), GFP_KERNEL);
+ if (!dp)
+ return -ENOMEM;
+
+ INIT_WORK(&dp->work, dp_altmode_work);
+ mutex_init(&dp->lock);
+ dp->port = port;
+ dp->alt = alt;
+
+ alt->desc = "DisplayPort";
+ alt->ops = &dp_altmode_ops;
+
+ typec_altmode_set_drvdata(alt, dp);
+
+ dp->state = DP_STATE_ENTER;
+ schedule_work(&dp->work);
+
+ return 0;
+}
+
+static void dp_altmode_remove(struct typec_altmode *alt)
+{
+ struct dp_altmode *dp = typec_altmode_get_drvdata(alt);
+
+ sysfs_remove_group(&alt->dev.kobj, &dp_altmode_group);
+ cancel_work_sync(&dp->work);
+}
+
+static const struct typec_device_id dp_typec_id[] = {
+ { USB_TYPEC_DP_SID, USB_TYPEC_DP_MODE },
+ { },
+};
+MODULE_DEVICE_TABLE(typec, dp_typec_id);
+
+static struct typec_altmode_driver dp_altmode_driver = {
+ .id_table = dp_typec_id,
+ .probe = dp_altmode_probe,
+ .remove = dp_altmode_remove,
+ .driver = {
+ .name = "typec_displayport",
+ .owner = THIS_MODULE,
+ },
+};
+module_typec_altmode_driver(dp_altmode_driver);
+
+MODULE_AUTHOR("Heikki Krogerus <heikki.krogerus@linux.intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("DisplayPort Alternate Mode");
diff --git a/drivers/usb/typec/bus.c b/drivers/usb/typec/bus.c
new file mode 100644
index 000000000000..76299b6ff06d
--- /dev/null
+++ b/drivers/usb/typec/bus.c
@@ -0,0 +1,403 @@
+// SPDX-License-Identifier: GPL-2.0
+/**
+ * Bus for USB Type-C Alternate Modes
+ *
+ * Copyright (C) 2018 Intel Corporation
+ * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+ */
+
+#include <linux/usb/pd_vdo.h>
+
+#include "bus.h"
+
+static inline int typec_altmode_set_mux(struct altmode *alt, u8 state)
+{
+ return alt->mux ? alt->mux->set(alt->mux, state) : 0;
+}
+
+static int typec_altmode_set_state(struct typec_altmode *adev, int state)
+{
+ bool is_port = is_typec_port(adev->dev.parent);
+ struct altmode *port_altmode;
+ int ret;
+
+ port_altmode = is_port ? to_altmode(adev) : to_altmode(adev)->partner;
+
+ ret = typec_altmode_set_mux(port_altmode, state);
+ if (ret)
+ return ret;
+
+ blocking_notifier_call_chain(&port_altmode->nh, state, NULL);
+
+ return 0;
+}
+
+/* -------------------------------------------------------------------------- */
+/* Common API */
+
+/**
+ * typec_altmode_notify - Communication between the OS and alternate mode driver
+ * @adev: Handle to the alternate mode
+ * @conf: Alternate mode specific configuration value
+ * @data: Alternate mode specific data
+ *
+ * The primary purpose for this function is to allow the alternate mode drivers
+ * to tell which pin configuration has been negotiated with the partner. That
+ * information will then be used for example to configure the muxes.
+ * Communication to the other direction is also possible, and low level device
+ * drivers can also send notifications to the alternate mode drivers. The actual
+ * communication will be specific for every SVID.
+ */
+int typec_altmode_notify(struct typec_altmode *adev,
+ unsigned long conf, void *data)
+{
+ bool is_port;
+ struct altmode *altmode;
+ struct altmode *partner;
+ int ret;
+
+ if (!adev)
+ return 0;
+
+ altmode = to_altmode(adev);
+
+ if (!altmode->partner)
+ return -ENODEV;
+
+ is_port = is_typec_port(adev->dev.parent);
+ partner = altmode->partner;
+
+ ret = typec_altmode_set_mux(is_port ? altmode : partner, (u8)conf);
+ if (ret)
+ return ret;
+
+ blocking_notifier_call_chain(is_port ? &altmode->nh : &partner->nh,
+ conf, data);
+
+ if (partner->adev.ops && partner->adev.ops->notify)
+ return partner->adev.ops->notify(&partner->adev, conf, data);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(typec_altmode_notify);
+
+/**
+ * typec_altmode_enter - Enter Mode
+ * @adev: The alternate mode
+ *
+ * The alternate mode drivers use this function to enter mode. The port drivers
+ * use this to inform the alternate mode drivers that the partner has initiated
+ * Enter Mode command.
+ */
+int typec_altmode_enter(struct typec_altmode *adev)
+{
+ struct altmode *partner = to_altmode(adev)->partner;
+ struct typec_altmode *pdev = &partner->adev;
+ int ret;
+
+ if (!adev || adev->active)
+ return 0;
+
+ if (!pdev->ops || !pdev->ops->enter)
+ return -EOPNOTSUPP;
+
+ /* Moving to USB Safe State */
+ ret = typec_altmode_set_state(adev, TYPEC_STATE_SAFE);
+ if (ret)
+ return ret;
+
+ /* Enter Mode */
+ return pdev->ops->enter(pdev);
+}
+EXPORT_SYMBOL_GPL(typec_altmode_enter);
+
+/**
+ * typec_altmode_exit - Exit Mode
+ * @adev: The alternate mode
+ *
+ * The partner of @adev has initiated Exit Mode command.
+ */
+int typec_altmode_exit(struct typec_altmode *adev)
+{
+ struct altmode *partner = to_altmode(adev)->partner;
+ struct typec_altmode *pdev = &partner->adev;
+ int ret;
+
+ if (!adev || !adev->active)
+ return 0;
+
+ if (!pdev->ops || !pdev->ops->enter)
+ return -EOPNOTSUPP;
+
+ /* Moving to USB Safe State */
+ ret = typec_altmode_set_state(adev, TYPEC_STATE_SAFE);
+ if (ret)
+ return ret;
+
+ /* Exit Mode command */
+ return pdev->ops->exit(pdev);
+}
+EXPORT_SYMBOL_GPL(typec_altmode_exit);
+
+/**
+ * typec_altmode_attention - Attention command
+ * @adev: The alternate mode
+ * @vdo: VDO for the Attention command
+ *
+ * Notifies the partner of @adev about Attention command.
+ */
+void typec_altmode_attention(struct typec_altmode *adev, u32 vdo)
+{
+ struct typec_altmode *pdev = &to_altmode(adev)->partner->adev;
+
+ if (pdev->ops && pdev->ops->attention)
+ pdev->ops->attention(pdev, vdo);
+}
+EXPORT_SYMBOL_GPL(typec_altmode_attention);
+
+/**
+ * typec_altmode_vdm - Send Vendor Defined Messages (VDM) to the partner
+ * @adev: Alternate mode handle
+ * @header: VDM Header
+ * @vdo: Array of Vendor Defined Data Objects
+ * @count: Number of Data Objects
+ *
+ * The alternate mode drivers use this function for SVID specific communication
+ * with the partner. The port drivers use it to deliver the Structured VDMs
+ * received from the partners to the alternate mode drivers.
+ */
+int typec_altmode_vdm(struct typec_altmode *adev,
+ const u32 header, const u32 *vdo, int count)
+{
+ struct typec_altmode *pdev;
+ struct altmode *altmode;
+
+ if (!adev)
+ return 0;
+
+ altmode = to_altmode(adev);
+
+ if (!altmode->partner)
+ return -ENODEV;
+
+ pdev = &altmode->partner->adev;
+
+ if (!pdev->ops || !pdev->ops->vdm)
+ return -EOPNOTSUPP;
+
+ return pdev->ops->vdm(pdev, header, vdo, count);
+}
+EXPORT_SYMBOL_GPL(typec_altmode_vdm);
+
+const struct typec_altmode *
+typec_altmode_get_partner(struct typec_altmode *adev)
+{
+ return &to_altmode(adev)->partner->adev;
+}
+EXPORT_SYMBOL_GPL(typec_altmode_get_partner);
+
+/* -------------------------------------------------------------------------- */
+/* API for the alternate mode drivers */
+
+/**
+ * typec_altmode_get_plug - Find cable plug alternate mode
+ * @adev: Handle to partner alternate mode
+ * @index: Cable plug index
+ *
+ * Increment reference count for cable plug alternate mode device. Returns
+ * handle to the cable plug alternate mode, or NULL if none is found.
+ */
+struct typec_altmode *typec_altmode_get_plug(struct typec_altmode *adev,
+ enum typec_plug_index index)
+{
+ struct altmode *port = to_altmode(adev)->partner;
+
+ if (port->plug[index]) {
+ get_device(&port->plug[index]->adev.dev);
+ return &port->plug[index]->adev;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(typec_altmode_get_plug);
+
+/**
+ * typec_altmode_put_plug - Decrement cable plug alternate mode reference count
+ * @plug: Handle to the cable plug alternate mode
+ */
+void typec_altmode_put_plug(struct typec_altmode *plug)
+{
+ if (plug)
+ put_device(&plug->dev);
+}
+EXPORT_SYMBOL_GPL(typec_altmode_put_plug);
+
+int __typec_altmode_register_driver(struct typec_altmode_driver *drv,
+ struct module *module)
+{
+ if (!drv->probe)
+ return -EINVAL;
+
+ drv->driver.owner = module;
+ drv->driver.bus = &typec_bus;
+
+ return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(__typec_altmode_register_driver);
+
+void typec_altmode_unregister_driver(struct typec_altmode_driver *drv)
+{
+ driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(typec_altmode_unregister_driver);
+
+/* -------------------------------------------------------------------------- */
+/* API for the port drivers */
+
+/**
+ * typec_match_altmode - Match SVID and mode to an array of alternate modes
+ * @altmodes: Array of alternate modes
+ * @n: Number of elements in the array, or -1 for NULL terminated arrays
+ * @svid: Standard or Vendor ID to match with
+ * @mode: Mode to match with
+ *
+ * Return pointer to an alternate mode with SVID matching @svid, or NULL when no
+ * match is found.
+ */
+struct typec_altmode *typec_match_altmode(struct typec_altmode **altmodes,
+ size_t n, u16 svid, u8 mode)
+{
+ int i;
+
+ for (i = 0; i < n; i++) {
+ if (!altmodes[i])
+ break;
+ if (altmodes[i]->svid == svid && altmodes[i]->mode == mode)
+ return altmodes[i];
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(typec_match_altmode);
+
+/* -------------------------------------------------------------------------- */
+
+static ssize_t
+description_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct typec_altmode *alt = to_typec_altmode(dev);
+
+ return sprintf(buf, "%s\n", alt->desc ? alt->desc : "");
+}
+static DEVICE_ATTR_RO(description);
+
+static struct attribute *typec_attrs[] = {
+ &dev_attr_description.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(typec);
+
+static int typec_match(struct device *dev, struct device_driver *driver)
+{
+ struct typec_altmode_driver *drv = to_altmode_driver(driver);
+ struct typec_altmode *altmode = to_typec_altmode(dev);
+ const struct typec_device_id *id;
+
+ for (id = drv->id_table; id->svid; id++)
+ if (id->svid == altmode->svid &&
+ (id->mode == TYPEC_ANY_MODE || id->mode == altmode->mode))
+ return 1;
+ return 0;
+}
+
+static int typec_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ struct typec_altmode *altmode = to_typec_altmode(dev);
+
+ if (add_uevent_var(env, "SVID=%04X", altmode->svid))
+ return -ENOMEM;
+
+ if (add_uevent_var(env, "MODE=%u", altmode->mode))
+ return -ENOMEM;
+
+ return add_uevent_var(env, "MODALIAS=typec:id%04Xm%02X",
+ altmode->svid, altmode->mode);
+}
+
+static int typec_altmode_create_links(struct altmode *alt)
+{
+ struct device *port_dev = &alt->partner->adev.dev;
+ struct device *dev = &alt->adev.dev;
+ int err;
+
+ err = sysfs_create_link(&dev->kobj, &port_dev->kobj, "port");
+ if (err)
+ return err;
+
+ err = sysfs_create_link(&port_dev->kobj, &dev->kobj, "partner");
+ if (err)
+ sysfs_remove_link(&dev->kobj, "port");
+
+ return err;
+}
+
+static void typec_altmode_remove_links(struct altmode *alt)
+{
+ sysfs_remove_link(&alt->partner->adev.dev.kobj, "partner");
+ sysfs_remove_link(&alt->adev.dev.kobj, "port");
+}
+
+static int typec_probe(struct device *dev)
+{
+ struct typec_altmode_driver *drv = to_altmode_driver(dev->driver);
+ struct typec_altmode *adev = to_typec_altmode(dev);
+ struct altmode *altmode = to_altmode(adev);
+ int ret;
+
+ /* Fail if the port does not support the alternate mode */
+ if (!altmode->partner)
+ return -ENODEV;
+
+ ret = typec_altmode_create_links(altmode);
+ if (ret) {
+ dev_warn(dev, "failed to create symlinks\n");
+ return ret;
+ }
+
+ ret = drv->probe(adev);
+ if (ret)
+ typec_altmode_remove_links(altmode);
+
+ return ret;
+}
+
+static int typec_remove(struct device *dev)
+{
+ struct typec_altmode_driver *drv = to_altmode_driver(dev->driver);
+ struct typec_altmode *adev = to_typec_altmode(dev);
+ struct altmode *altmode = to_altmode(adev);
+
+ typec_altmode_remove_links(altmode);
+
+ if (drv->remove)
+ drv->remove(to_typec_altmode(dev));
+
+ if (adev->active) {
+ WARN_ON(typec_altmode_set_state(adev, TYPEC_STATE_SAFE));
+ typec_altmode_update_active(adev, false);
+ }
+
+ adev->desc = NULL;
+ adev->ops = NULL;
+
+ return 0;
+}
+
+struct bus_type typec_bus = {
+ .name = "typec",
+ .dev_groups = typec_groups,
+ .match = typec_match,
+ .uevent = typec_uevent,
+ .probe = typec_probe,
+ .remove = typec_remove,
+};
diff --git a/drivers/usb/typec/bus.h b/drivers/usb/typec/bus.h
new file mode 100644
index 000000000000..db40e61d8b72
--- /dev/null
+++ b/drivers/usb/typec/bus.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __USB_TYPEC_ALTMODE_H__
+#define __USB_TYPEC_ALTMODE_H__
+
+#include <linux/usb/typec_altmode.h>
+#include <linux/usb/typec_mux.h>
+
+struct bus_type;
+
+struct altmode {
+ unsigned int id;
+ struct typec_altmode adev;
+ struct typec_mux *mux;
+
+ enum typec_port_data roles;
+
+ struct attribute *attrs[5];
+ char group_name[8];
+ struct attribute_group group;
+ const struct attribute_group *groups[2];
+
+ struct altmode *partner;
+ struct altmode *plug[2];
+
+ struct blocking_notifier_head nh;
+};
+
+#define to_altmode(d) container_of(d, struct altmode, adev)
+
+extern struct bus_type typec_bus;
+extern const struct device_type typec_altmode_dev_type;
+extern const struct device_type typec_port_dev_type;
+
+#define is_typec_altmode(_dev_) (_dev_->type == &typec_altmode_dev_type)
+#define is_typec_port(_dev_) (_dev_->type == &typec_port_dev_type)
+
+#endif /* __USB_TYPEC_ALTMODE_H__ */
diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c
index 53df10df2f9d..5db0593ca0bd 100644
--- a/drivers/usb/typec/class.c
+++ b/drivers/usb/typec/class.c
@@ -10,39 +10,13 @@
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/slab.h>
-#include <linux/usb/typec.h>
-#include <linux/usb/typec_mux.h>
-struct typec_mode {
- int index;
- u32 vdo;
- char *desc;
- enum typec_port_type roles;
-
- struct typec_altmode *alt_mode;
-
- unsigned int active:1;
-
- char group_name[6];
- struct attribute_group group;
- struct attribute *attrs[5];
- struct device_attribute vdo_attr;
- struct device_attribute desc_attr;
- struct device_attribute active_attr;
- struct device_attribute roles_attr;
-};
-
-struct typec_altmode {
- struct device dev;
- u16 svid;
- int n_modes;
- struct typec_mode modes[ALTMODE_MAX_MODES];
- const struct attribute_group *mode_groups[ALTMODE_MAX_MODES];
-};
+#include "bus.h"
struct typec_plug {
struct device dev;
enum typec_plug_index index;
+ struct ida mode_ids;
};
struct typec_cable {
@@ -57,11 +31,13 @@ struct typec_partner {
unsigned int usb_pd:1;
struct usb_pd_identity *identity;
enum typec_accessory accessory;
+ struct ida mode_ids;
};
struct typec_port {
unsigned int id;
struct device dev;
+ struct ida mode_ids;
int prefer_role;
enum typec_data_role data_role;
@@ -82,17 +58,14 @@ struct typec_port {
#define to_typec_plug(_dev_) container_of(_dev_, struct typec_plug, dev)
#define to_typec_cable(_dev_) container_of(_dev_, struct typec_cable, dev)
#define to_typec_partner(_dev_) container_of(_dev_, struct typec_partner, dev)
-#define to_altmode(_dev_) container_of(_dev_, struct typec_altmode, dev)
static const struct device_type typec_partner_dev_type;
static const struct device_type typec_cable_dev_type;
static const struct device_type typec_plug_dev_type;
-static const struct device_type typec_port_dev_type;
#define is_typec_partner(_dev_) (_dev_->type == &typec_partner_dev_type)
#define is_typec_cable(_dev_) (_dev_->type == &typec_cable_dev_type)
#define is_typec_plug(_dev_) (_dev_->type == &typec_plug_dev_type)
-#define is_typec_port(_dev_) (_dev_->type == &typec_port_dev_type)
static DEFINE_IDA(typec_index_ida);
static struct class *typec_class;
@@ -174,28 +147,148 @@ static void typec_report_identity(struct device *dev)
/* ------------------------------------------------------------------------- */
/* Alternate Modes */
+static int altmode_match(struct device *dev, void *data)
+{
+ struct typec_altmode *adev = to_typec_altmode(dev);
+ struct typec_device_id *id = data;
+
+ if (!is_typec_altmode(dev))
+ return 0;
+
+ return ((adev->svid == id->svid) && (adev->mode == id->mode));
+}
+
+static void typec_altmode_set_partner(struct altmode *altmode)
+{
+ struct typec_altmode *adev = &altmode->adev;
+ struct typec_device_id id = { adev->svid, adev->mode, };
+ struct typec_port *port = typec_altmode2port(adev);
+ struct altmode *partner;
+ struct device *dev;
+
+ dev = device_find_child(&port->dev, &id, altmode_match);
+ if (!dev)
+ return;
+
+ /* Bind the port alt mode to the partner/plug alt mode. */
+ partner = to_altmode(to_typec_altmode(dev));
+ altmode->partner = partner;
+
+ /* Bind the partner/plug alt mode to the port alt mode. */
+ if (is_typec_plug(adev->dev.parent)) {
+ struct typec_plug *plug = to_typec_plug(adev->dev.parent);
+
+ partner->plug[plug->index] = altmode;
+ } else {
+ partner->partner = altmode;
+ }
+}
+
+static void typec_altmode_put_partner(struct altmode *altmode)
+{
+ struct altmode *partner = altmode->partner;
+ struct typec_altmode *adev;
+
+ if (!partner)
+ return;
+
+ adev = &partner->adev;
+
+ if (is_typec_plug(adev->dev.parent)) {
+ struct typec_plug *plug = to_typec_plug(adev->dev.parent);
+
+ partner->plug[plug->index] = NULL;
+ } else {
+ partner->partner = NULL;
+ }
+ put_device(&adev->dev);
+}
+
+static int __typec_port_match(struct device *dev, const void *name)
+{
+ return !strcmp((const char *)name, dev_name(dev));
+}
+
+static void *typec_port_match(struct device_connection *con, int ep, void *data)
+{
+ return class_find_device(typec_class, NULL, con->endpoint[ep],
+ __typec_port_match);
+}
+
+struct typec_altmode *
+typec_altmode_register_notifier(struct device *dev, u16 svid, u8 mode,
+ struct notifier_block *nb)
+{
+ struct typec_device_id id = { svid, mode, };
+ struct device *altmode_dev;
+ struct device *port_dev;
+ struct altmode *altmode;
+ int ret;
+
+ /* Find the port linked to the caller */
+ port_dev = device_connection_find_match(dev, NULL, NULL,
+ typec_port_match);
+ if (IS_ERR_OR_NULL(port_dev))
+ return port_dev ? ERR_CAST(port_dev) : ERR_PTR(-ENODEV);
+
+ /* Find the altmode with matching svid */
+ altmode_dev = device_find_child(port_dev, &id, altmode_match);
+
+ put_device(port_dev);
+
+ if (!altmode_dev)
+ return ERR_PTR(-ENODEV);
+
+ altmode = to_altmode(to_typec_altmode(altmode_dev));
+
+ /* Register notifier */
+ ret = blocking_notifier_chain_register(&altmode->nh, nb);
+ if (ret) {
+ put_device(altmode_dev);
+ return ERR_PTR(ret);
+ }
+
+ return &altmode->adev;
+}
+EXPORT_SYMBOL_GPL(typec_altmode_register_notifier);
+
+void typec_altmode_unregister_notifier(struct typec_altmode *adev,
+ struct notifier_block *nb)
+{
+ struct altmode *altmode = to_altmode(adev);
+
+ blocking_notifier_chain_unregister(&altmode->nh, nb);
+ put_device(&adev->dev);
+}
+EXPORT_SYMBOL_GPL(typec_altmode_unregister_notifier);
+
/**
* typec_altmode_update_active - Report Enter/Exit mode
- * @alt: Handle to the alternate mode
- * @mode: Mode index
+ * @adev: Handle to the alternate mode
* @active: True when the mode has been entered
*
* If a partner or cable plug executes Enter/Exit Mode command successfully, the
* drivers use this routine to report the updated state of the mode.
*/
-void typec_altmode_update_active(struct typec_altmode *alt, int mode,
- bool active)
+void typec_altmode_update_active(struct typec_altmode *adev, bool active)
{
- struct typec_mode *m = &alt->modes[mode];
char dir[6];
- if (m->active == active)
+ if (adev->active == active)
return;
- m->active = active;
- snprintf(dir, sizeof(dir), "mode%d", mode);
- sysfs_notify(&alt->dev.kobj, dir, "active");
- kobject_uevent(&alt->dev.kobj, KOBJ_CHANGE);
+ if (!is_typec_port(adev->dev.parent)) {
+ if (!active)
+ module_put(adev->dev.driver->owner);
+ else
+ WARN_ON(!try_module_get(adev->dev.driver->owner));
+ }
+
+ adev->active = active;
+ snprintf(dir, sizeof(dir), "mode%d", adev->mode);
+ sysfs_notify(&adev->dev.kobj, dir, "active");
+ sysfs_notify(&adev->dev.kobj, NULL, "active");
+ kobject_uevent(&adev->dev.kobj, KOBJ_CHANGE);
}
EXPORT_SYMBOL_GPL(typec_altmode_update_active);
@@ -220,68 +313,78 @@ struct typec_port *typec_altmode2port(struct typec_altmode *alt)
EXPORT_SYMBOL_GPL(typec_altmode2port);
static ssize_t
-typec_altmode_vdo_show(struct device *dev, struct device_attribute *attr,
- char *buf)
+vdo_show(struct device *dev, struct device_attribute *attr, char *buf)
{
- struct typec_mode *mode = container_of(attr, struct typec_mode,
- vdo_attr);
+ struct typec_altmode *alt = to_typec_altmode(dev);
- return sprintf(buf, "0x%08x\n", mode->vdo);
+ return sprintf(buf, "0x%08x\n", alt->vdo);
}
+static DEVICE_ATTR_RO(vdo);
static ssize_t
-typec_altmode_desc_show(struct device *dev, struct device_attribute *attr,
- char *buf)
+description_show(struct device *dev, struct device_attribute *attr, char *buf)
{
- struct typec_mode *mode = container_of(attr, struct typec_mode,
- desc_attr);
+ struct typec_altmode *alt = to_typec_altmode(dev);
- return sprintf(buf, "%s\n", mode->desc ? mode->desc : "");
+ return sprintf(buf, "%s\n", alt->desc ? alt->desc : "");
}
+static DEVICE_ATTR_RO(description);
static ssize_t
-typec_altmode_active_show(struct device *dev, struct device_attribute *attr,
- char *buf)
+active_show(struct device *dev, struct device_attribute *attr, char *buf)
{
- struct typec_mode *mode = container_of(attr, struct typec_mode,
- active_attr);
+ struct typec_altmode *alt = to_typec_altmode(dev);
- return sprintf(buf, "%s\n", mode->active ? "yes" : "no");
+ return sprintf(buf, "%s\n", alt->active ? "yes" : "no");
}
-static ssize_t
-typec_altmode_active_store(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t size)
+static ssize_t active_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t size)
{
- struct typec_mode *mode = container_of(attr, struct typec_mode,
- active_attr);
- struct typec_port *port = typec_altmode2port(mode->alt_mode);
- bool activate;
+ struct typec_altmode *adev = to_typec_altmode(dev);
+ struct altmode *altmode = to_altmode(adev);
+ bool enter;
int ret;
- if (!port->cap->activate_mode)
- return -EOPNOTSUPP;
-
- ret = kstrtobool(buf, &activate);
+ ret = kstrtobool(buf, &enter);
if (ret)
return ret;
- ret = port->cap->activate_mode(port->cap, mode->index, activate);
- if (ret)
- return ret;
+ if (adev->active == enter)
+ return size;
+
+ if (is_typec_port(adev->dev.parent)) {
+ typec_altmode_update_active(adev, enter);
+
+ /* Make sure that the partner exits the mode before disabling */
+ if (altmode->partner && !enter && altmode->partner->adev.active)
+ typec_altmode_exit(&altmode->partner->adev);
+ } else if (altmode->partner) {
+ if (enter && !altmode->partner->adev.active) {
+ dev_warn(dev, "port has the mode disabled\n");
+ return -EPERM;
+ }
+ }
+
+ /* Note: If there is no driver, the mode will not be entered */
+ if (adev->ops && adev->ops->activate) {
+ ret = adev->ops->activate(adev, enter);
+ if (ret)
+ return ret;
+ }
return size;
}
+static DEVICE_ATTR_RW(active);
static ssize_t
-typec_altmode_roles_show(struct device *dev, struct device_attribute *attr,
- char *buf)
+supported_roles_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
- struct typec_mode *mode = container_of(attr, struct typec_mode,
- roles_attr);
+ struct altmode *alt = to_altmode(to_typec_altmode(dev));
ssize_t ret;
- switch (mode->roles) {
+ switch (alt->roles) {
case TYPEC_PORT_SRC:
ret = sprintf(buf, "source\n");
break;
@@ -295,89 +398,74 @@ typec_altmode_roles_show(struct device *dev, struct device_attribute *attr,
}
return ret;
}
+static DEVICE_ATTR_RO(supported_roles);
-static void typec_init_modes(struct typec_altmode *alt,
- const struct typec_mode_desc *desc, bool is_port)
+static ssize_t
+mode_show(struct device *dev, struct device_attribute *attr, char *buf)
{
- int i;
-
- for (i = 0; i < alt->n_modes; i++, desc++) {
- struct typec_mode *mode = &alt->modes[i];
-
- /* Not considering the human readable description critical */
- mode->desc = kstrdup(desc->desc, GFP_KERNEL);
- if (desc->desc && !mode->desc)
- dev_err(&alt->dev, "failed to copy mode%d desc\n", i);
-
- mode->alt_mode = alt;
- mode->vdo = desc->vdo;
- mode->roles = desc->roles;
- mode->index = desc->index;
- sprintf(mode->group_name, "mode%d", desc->index);
-
- sysfs_attr_init(&mode->vdo_attr.attr);
- mode->vdo_attr.attr.name = "vdo";
- mode->vdo_attr.attr.mode = 0444;
- mode->vdo_attr.show = typec_altmode_vdo_show;
-
- sysfs_attr_init(&mode->desc_attr.attr);
- mode->desc_attr.attr.name = "description";
- mode->desc_attr.attr.mode = 0444;
- mode->desc_attr.show = typec_altmode_desc_show;
-
- sysfs_attr_init(&mode->active_attr.attr);
- mode->active_attr.attr.name = "active";
- mode->active_attr.attr.mode = 0644;
- mode->active_attr.show = typec_altmode_active_show;
- mode->active_attr.store = typec_altmode_active_store;
-
- mode->attrs[0] = &mode->vdo_attr.attr;
- mode->attrs[1] = &mode->desc_attr.attr;
- mode->attrs[2] = &mode->active_attr.attr;
-
- /* With ports, list the roles that the mode is supported with */
- if (is_port) {
- sysfs_attr_init(&mode->roles_attr.attr);
- mode->roles_attr.attr.name = "supported_roles";
- mode->roles_attr.attr.mode = 0444;
- mode->roles_attr.show = typec_altmode_roles_show;
-
- mode->attrs[3] = &mode->roles_attr.attr;
- }
+ struct typec_altmode *adev = to_typec_altmode(dev);
- mode->group.attrs = mode->attrs;
- mode->group.name = mode->group_name;
-
- alt->mode_groups[i] = &mode->group;
- }
+ return sprintf(buf, "%u\n", adev->mode);
}
+static DEVICE_ATTR_RO(mode);
-static ssize_t svid_show(struct device *dev, struct device_attribute *attr,
- char *buf)
+static ssize_t
+svid_show(struct device *dev, struct device_attribute *attr, char *buf)
{
- struct typec_altmode *alt = to_altmode(dev);
+ struct typec_altmode *adev = to_typec_altmode(dev);
- return sprintf(buf, "%04x\n", alt->svid);
+ return sprintf(buf, "%04x\n", adev->svid);
}
static DEVICE_ATTR_RO(svid);
static struct attribute *typec_altmode_attrs[] = {
+ &dev_attr_active.attr,
+ &dev_attr_mode.attr,
&dev_attr_svid.attr,
+ &dev_attr_vdo.attr,
NULL
};
ATTRIBUTE_GROUPS(typec_altmode);
+static int altmode_id_get(struct device *dev)
+{
+ struct ida *ids;
+
+ if (is_typec_partner(dev))
+ ids = &to_typec_partner(dev)->mode_ids;
+ else if (is_typec_plug(dev))
+ ids = &to_typec_plug(dev)->mode_ids;
+ else
+ ids = &to_typec_port(dev)->mode_ids;
+
+ return ida_simple_get(ids, 0, 0, GFP_KERNEL);
+}
+
+static void altmode_id_remove(struct device *dev, int id)
+{
+ struct ida *ids;
+
+ if (is_typec_partner(dev))
+ ids = &to_typec_partner(dev)->mode_ids;
+ else if (is_typec_plug(dev))
+ ids = &to_typec_plug(dev)->mode_ids;
+ else
+ ids = &to_typec_port(dev)->mode_ids;
+
+ ida_simple_remove(ids, id);
+}
+
static void typec_altmode_release(struct device *dev)
{
- struct typec_altmode *alt = to_altmode(dev);
- int i;
+ struct altmode *alt = to_altmode(to_typec_altmode(dev));
- for (i = 0; i < alt->n_modes; i++)
- kfree(alt->modes[i].desc);
+ typec_altmode_put_partner(alt);
+
+ altmode_id_remove(alt->adev.dev.parent, alt->id);
kfree(alt);
}
-static const struct device_type typec_altmode_dev_type = {
+const struct device_type typec_altmode_dev_type = {
.name = "typec_alternate_mode",
.groups = typec_altmode_groups,
.release = typec_altmode_release,
@@ -387,44 +475,74 @@ static struct typec_altmode *
typec_register_altmode(struct device *parent,
const struct typec_altmode_desc *desc)
{
- struct typec_altmode *alt;
+ 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);
if (!alt)
return ERR_PTR(-ENOMEM);
- alt->svid = desc->svid;
- alt->n_modes = desc->n_modes;
- typec_init_modes(alt, desc->modes, is_typec_port(parent));
+ alt->adev.svid = desc->svid;
+ alt->adev.mode = desc->mode;
+ alt->adev.vdo = desc->vdo;
+ alt->roles = desc->roles;
+ alt->id = id;
- alt->dev.parent = parent;
- alt->dev.groups = alt->mode_groups;
- alt->dev.type = &typec_altmode_dev_type;
- dev_set_name(&alt->dev, "svid-%04x", alt->svid);
+ alt->attrs[0] = &dev_attr_vdo.attr;
+ alt->attrs[1] = &dev_attr_description.attr;
+ alt->attrs[2] = &dev_attr_active.attr;
- ret = device_register(&alt->dev);
+ if (is_port) {
+ alt->attrs[3] = &dev_attr_supported_roles.attr;
+ alt->adev.active = true; /* Enabled by default */
+ }
+
+ sprintf(alt->group_name, "mode%d", desc->mode);
+ alt->group.name = alt->group_name;
+ alt->group.attrs = alt->attrs;
+ alt->groups[0] = &alt->group;
+
+ alt->adev.dev.parent = parent;
+ alt->adev.dev.groups = alt->groups;
+ alt->adev.dev.type = &typec_altmode_dev_type;
+ dev_set_name(&alt->adev.dev, "%s.%u", dev_name(parent), id);
+
+ /* Link partners and plugs with the ports */
+ if (is_port)
+ BLOCKING_INIT_NOTIFIER_HEAD(&alt->nh);
+ else
+ typec_altmode_set_partner(alt);
+
+ /* The partners are bind to drivers */
+ if (is_typec_partner(parent))
+ alt->adev.dev.bus = &typec_bus;
+
+ ret = device_register(&alt->adev.dev);
if (ret) {
dev_err(parent, "failed to register alternate mode (%d)\n",
ret);
- put_device(&alt->dev);
+ put_device(&alt->adev.dev);
return ERR_PTR(ret);
}
- return alt;
+ return &alt->adev;
}
/**
* typec_unregister_altmode - Unregister Alternate Mode
- * @alt: The alternate mode to be unregistered
+ * @adev: The alternate mode to be unregistered
*
* Unregister device created with typec_partner_register_altmode(),
* typec_plug_register_altmode() or typec_port_register_altmode().
*/
-void typec_unregister_altmode(struct typec_altmode *alt)
+void typec_unregister_altmode(struct typec_altmode *adev)
{
- if (!IS_ERR_OR_NULL(alt))
- device_unregister(&alt->dev);
+ if (IS_ERR_OR_NULL(adev))
+ return;
+ typec_mux_put(to_altmode(adev)->mux);
+ device_unregister(&adev->dev);
}
EXPORT_SYMBOL_GPL(typec_unregister_altmode);
@@ -462,6 +580,7 @@ static void typec_partner_release(struct device *dev)
{
struct typec_partner *partner = to_typec_partner(dev);
+ ida_destroy(&partner->mode_ids);
kfree(partner);
}
@@ -527,6 +646,7 @@ struct typec_partner *typec_register_partner(struct typec_port *port,
if (!partner)
return ERR_PTR(-ENOMEM);
+ ida_init(&partner->mode_ids);
partner->usb_pd = desc->usb_pd;
partner->accessory = desc->accessory;
@@ -575,6 +695,7 @@ static void typec_plug_release(struct device *dev)
{
struct typec_plug *plug = to_typec_plug(dev);
+ ida_destroy(&plug->mode_ids);
kfree(plug);
}
@@ -627,6 +748,7 @@ struct typec_plug *typec_register_plug(struct typec_cable *cable,
sprintf(name, "plug%d", desc->index);
+ ida_init(&plug->mode_ids);
plug->index = desc->index;
plug->dev.class = typec_class;
plug->dev.parent = &cable->dev;
@@ -796,12 +918,18 @@ static const char * const typec_data_roles[] = {
[TYPEC_HOST] = "host",
};
-static const char * const typec_port_types[] = {
+static const char * const typec_port_power_roles[] = {
[TYPEC_PORT_SRC] = "source",
[TYPEC_PORT_SNK] = "sink",
[TYPEC_PORT_DRP] = "dual",
};
+static const char * const typec_port_data_roles[] = {
+ [TYPEC_PORT_DFP] = "host",
+ [TYPEC_PORT_UFP] = "device",
+ [TYPEC_PORT_DRD] = "dual",
+};
+
static const char * const typec_port_types_drp[] = {
[TYPEC_PORT_SRC] = "dual [source] sink",
[TYPEC_PORT_SNK] = "dual source [sink]",
@@ -932,7 +1060,7 @@ static ssize_t power_role_store(struct device *dev,
mutex_lock(&port->port_type_lock);
if (port->port_type != TYPEC_PORT_DRP) {
dev_dbg(dev, "port type fixed at \"%s\"",
- typec_port_types[port->port_type]);
+ typec_port_power_roles[port->port_type]);
ret = -EOPNOTSUPP;
goto unlock_and_ret;
}
@@ -973,7 +1101,7 @@ port_type_store(struct device *dev, struct device_attribute *attr,
return -EOPNOTSUPP;
}
- ret = sysfs_match_string(typec_port_types, buf);
+ ret = sysfs_match_string(typec_port_power_roles, buf);
if (ret < 0)
return ret;
@@ -1007,7 +1135,7 @@ port_type_show(struct device *dev, struct device_attribute *attr,
return sprintf(buf, "%s\n",
typec_port_types_drp[port->port_type]);
- return sprintf(buf, "[%s]\n", typec_port_types[port->cap->type]);
+ return sprintf(buf, "[%s]\n", typec_port_power_roles[port->cap->type]);
}
static DEVICE_ATTR_RW(port_type);
@@ -1141,12 +1269,13 @@ static void typec_release(struct device *dev)
struct typec_port *port = to_typec_port(dev);
ida_simple_remove(&typec_index_ida, port->id);
+ ida_destroy(&port->mode_ids);
typec_switch_put(port->sw);
typec_mux_put(port->mux);
kfree(port);
}
-static const struct device_type typec_port_dev_type = {
+const struct device_type typec_port_dev_type = {
.name = "typec_port",
.groups = typec_groups,
.uevent = typec_uevent,
@@ -1193,7 +1322,7 @@ void typec_set_pwr_role(struct typec_port *port, enum typec_role role)
EXPORT_SYMBOL_GPL(typec_set_pwr_role);
/**
- * typec_set_pwr_role - Report VCONN source change
+ * typec_set_vconn_role - Report VCONN source change
* @port: The USB Type-C Port which VCONN role changed
* @role: Source when @port is sourcing VCONN, or Sink when it's not
*
@@ -1252,6 +1381,50 @@ void typec_set_pwr_opmode(struct typec_port *port,
}
EXPORT_SYMBOL_GPL(typec_set_pwr_opmode);
+/**
+ * typec_find_port_power_role - Get the typec port power capability
+ * @name: port power capability string
+ *
+ * This routine is used to find the typec_port_type by its string name.
+ *
+ * Returns typec_port_type if success, otherwise negative error code.
+ */
+int typec_find_port_power_role(const char *name)
+{
+ return match_string(typec_port_power_roles,
+ ARRAY_SIZE(typec_port_power_roles), name);
+}
+EXPORT_SYMBOL_GPL(typec_find_port_power_role);
+
+/**
+ * typec_find_power_role - Find the typec one specific power role
+ * @name: power role string
+ *
+ * This routine is used to find the typec_role by its string name.
+ *
+ * Returns typec_role if success, otherwise negative error code.
+ */
+int typec_find_power_role(const char *name)
+{
+ return match_string(typec_roles, ARRAY_SIZE(typec_roles), name);
+}
+EXPORT_SYMBOL_GPL(typec_find_power_role);
+
+/**
+ * typec_find_port_data_role - Get the typec port data capability
+ * @name: port data capability string
+ *
+ * This routine is used to find the typec_port_data by its string name.
+ *
+ * Returns typec_port_data if success, otherwise negative error code.
+ */
+int typec_find_port_data_role(const char *name)
+{
+ return match_string(typec_port_data_roles,
+ ARRAY_SIZE(typec_port_data_roles), name);
+}
+EXPORT_SYMBOL_GPL(typec_find_port_data_role);
+
/* ------------------------------------------ */
/* API for Multiplexer/DeMultiplexer Switches */
@@ -1280,12 +1453,24 @@ int typec_set_orientation(struct typec_port *port,
EXPORT_SYMBOL_GPL(typec_set_orientation);
/**
+ * typec_get_orientation - Get USB Type-C cable plug orientation
+ * @port: USB Type-C Port
+ *
+ * Get current cable plug orientation for @port.
+ */
+enum typec_orientation typec_get_orientation(struct typec_port *port)
+{
+ return port->orientation;
+}
+EXPORT_SYMBOL_GPL(typec_get_orientation);
+
+/**
* typec_set_mode - Set mode of operation for USB Type-C connector
- * @port: USB Type-C port for the connector
- * @mode: Operation mode for the connector
+ * @port: USB Type-C connector
+ * @mode: Accessory Mode, USB Operation or Safe State
*
- * Set mode @mode for @port. This function will configure the muxes needed to
- * enter @mode.
+ * Configure @port for Accessory Mode @mode. This function will configure the
+ * muxes needed for @mode.
*/
int typec_set_mode(struct typec_port *port, int mode)
{
@@ -1309,7 +1494,23 @@ struct typec_altmode *
typec_port_register_altmode(struct typec_port *port,
const struct typec_altmode_desc *desc)
{
- return typec_register_altmode(&port->dev, desc);
+ struct typec_altmode *adev;
+ struct typec_mux *mux;
+ char id[10];
+
+ sprintf(id, "id%04xm%02x", desc->svid, desc->mode);
+
+ mux = typec_mux_get(&port->dev, id);
+ if (IS_ERR(mux))
+ return ERR_CAST(mux);
+
+ adev = typec_register_altmode(&port->dev, desc);
+ if (IS_ERR(adev))
+ typec_mux_put(mux);
+ else
+ to_altmode(adev)->mux = mux;
+
+ return adev;
}
EXPORT_SYMBOL_GPL(typec_port_register_altmode);
@@ -1339,18 +1540,6 @@ struct typec_port *typec_register_port(struct device *parent,
return ERR_PTR(id);
}
- port->sw = typec_switch_get(cap->fwnode ? &port->dev : parent);
- if (IS_ERR(port->sw)) {
- ret = PTR_ERR(port->sw);
- goto err_switch;
- }
-
- port->mux = typec_mux_get(cap->fwnode ? &port->dev : parent);
- if (IS_ERR(port->mux)) {
- ret = PTR_ERR(port->mux);
- goto err_mux;
- }
-
switch (cap->type) {
case TYPEC_PORT_SRC:
port->pwr_role = TYPEC_SOURCE;
@@ -1383,19 +1572,34 @@ struct typec_port *typec_register_port(struct device *parent,
break;
}
+ ida_init(&port->mode_ids);
+ mutex_init(&port->port_type_lock);
+
port->id = id;
port->cap = cap;
port->port_type = cap->type;
- mutex_init(&port->port_type_lock);
port->prefer_role = cap->prefer_role;
+ device_initialize(&port->dev);
port->dev.class = typec_class;
port->dev.parent = parent;
port->dev.fwnode = cap->fwnode;
port->dev.type = &typec_port_dev_type;
dev_set_name(&port->dev, "port%d", id);
- ret = device_register(&port->dev);
+ port->sw = typec_switch_get(&port->dev);
+ if (IS_ERR(port->sw)) {
+ put_device(&port->dev);
+ return ERR_CAST(port->sw);
+ }
+
+ port->mux = typec_mux_get(&port->dev, "typec-mux");
+ if (IS_ERR(port->mux)) {
+ put_device(&port->dev);
+ return ERR_CAST(port->mux);
+ }
+
+ ret = device_add(&port->dev);
if (ret) {
dev_err(parent, "failed to register port (%d)\n", ret);
put_device(&port->dev);
@@ -1403,15 +1607,6 @@ struct typec_port *typec_register_port(struct device *parent,
}
return port;
-
-err_mux:
- typec_switch_put(port->sw);
-
-err_switch:
- ida_simple_remove(&typec_index_ida, port->id);
- kfree(port);
-
- return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(typec_register_port);
@@ -1430,8 +1625,19 @@ EXPORT_SYMBOL_GPL(typec_unregister_port);
static int __init typec_init(void)
{
+ int ret;
+
+ ret = bus_register(&typec_bus);
+ if (ret)
+ return ret;
+
typec_class = class_create(THIS_MODULE, "typec");
- return PTR_ERR_OR_ZERO(typec_class);
+ if (IS_ERR(typec_class)) {
+ bus_unregister(&typec_bus);
+ return PTR_ERR(typec_class);
+ }
+
+ return 0;
}
subsys_initcall(typec_init);
@@ -1439,6 +1645,7 @@ static void __exit typec_exit(void)
{
class_destroy(typec_class);
ida_destroy(&typec_index_ida);
+ bus_unregister(&typec_bus);
}
module_exit(typec_exit);
diff --git a/drivers/usb/typec/fusb302/Kconfig b/drivers/usb/typec/fusb302/Kconfig
deleted file mode 100644
index 48a4f2fcee03..000000000000
--- a/drivers/usb/typec/fusb302/Kconfig
+++ /dev/null
@@ -1,7 +0,0 @@
-config TYPEC_FUSB302
- tristate "Fairchild FUSB302 Type-C chip driver"
- depends on I2C && POWER_SUPPLY
- help
- The Fairchild FUSB302 Type-C chip driver that works with
- Type-C Port Controller Manager to provide USB PD and USB
- Type-C functionalities.
diff --git a/drivers/usb/typec/fusb302/Makefile b/drivers/usb/typec/fusb302/Makefile
deleted file mode 100644
index 3b51b33631a0..000000000000
--- a/drivers/usb/typec/fusb302/Makefile
+++ /dev/null
@@ -1,2 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0
-obj-$(CONFIG_TYPEC_FUSB302) += fusb302.o
diff --git a/drivers/usb/typec/mux.c b/drivers/usb/typec/mux.c
index f89093bd7185..d990aa510fab 100644
--- a/drivers/usb/typec/mux.c
+++ b/drivers/usb/typec/mux.c
@@ -9,6 +9,7 @@
#include <linux/device.h>
#include <linux/list.h>
+#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/usb/typec_mux.h>
@@ -49,8 +50,10 @@ struct typec_switch *typec_switch_get(struct device *dev)
mutex_lock(&switch_lock);
sw = device_connection_find_match(dev, "typec-switch", NULL,
typec_switch_match);
- if (!IS_ERR_OR_NULL(sw))
+ if (!IS_ERR_OR_NULL(sw)) {
+ WARN_ON(!try_module_get(sw->dev->driver->owner));
get_device(sw->dev);
+ }
mutex_unlock(&switch_lock);
return sw;
@@ -65,8 +68,10 @@ EXPORT_SYMBOL_GPL(typec_switch_get);
*/
void typec_switch_put(struct typec_switch *sw)
{
- if (!IS_ERR_OR_NULL(sw))
+ if (!IS_ERR_OR_NULL(sw)) {
+ module_put(sw->dev->driver->owner);
put_device(sw->dev);
+ }
}
EXPORT_SYMBOL_GPL(typec_switch_put);
@@ -123,21 +128,23 @@ static void *typec_mux_match(struct device_connection *con, int ep, void *data)
/**
* typec_mux_get - Find USB Type-C Multiplexer
* @dev: The caller device
+ * @name: Mux identifier
*
* Finds a mux linked to the caller. This function is primarily meant for the
* Type-C drivers. Returns a reference to the mux on success, NULL if no
* matching connection was found, or ERR_PTR(-EPROBE_DEFER) when a connection
* was found but the mux has not been enumerated yet.
*/
-struct typec_mux *typec_mux_get(struct device *dev)
+struct typec_mux *typec_mux_get(struct device *dev, const char *name)
{
struct typec_mux *mux;
mutex_lock(&mux_lock);
- mux = device_connection_find_match(dev, "typec-mux", NULL,
- typec_mux_match);
- if (!IS_ERR_OR_NULL(mux))
+ mux = device_connection_find_match(dev, name, NULL, typec_mux_match);
+ if (!IS_ERR_OR_NULL(mux)) {
+ WARN_ON(!try_module_get(mux->dev->driver->owner));
get_device(mux->dev);
+ }
mutex_unlock(&mux_lock);
return mux;
@@ -152,8 +159,10 @@ EXPORT_SYMBOL_GPL(typec_mux_get);
*/
void typec_mux_put(struct typec_mux *mux)
{
- if (!IS_ERR_OR_NULL(mux))
+ if (!IS_ERR_OR_NULL(mux)) {
+ module_put(mux->dev->driver->owner);
put_device(mux->dev);
+ }
}
EXPORT_SYMBOL_GPL(typec_mux_put);
@@ -178,7 +187,7 @@ EXPORT_SYMBOL_GPL(typec_mux_register);
/**
* typec_mux_unregister - Unregister Multiplexer Switch
- * @sw: USB Type-C Connector Multiplexer/DeMultiplexer
+ * @mux: USB Type-C Connector Multiplexer/DeMultiplexer
*
* Unregister mux that was registered with typec_mux_register().
*/
diff --git a/drivers/usb/typec/mux/pi3usb30532.c b/drivers/usb/typec/mux/pi3usb30532.c
index b0e88db60ecf..64eb5983e17a 100644
--- a/drivers/usb/typec/mux/pi3usb30532.c
+++ b/drivers/usb/typec/mux/pi3usb30532.c
@@ -9,7 +9,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
-#include <linux/usb/tcpm.h>
+#include <linux/usb/typec_dp.h>
#include <linux/usb/typec_mux.h>
#define PI3USB30532_CONF 0x00
@@ -83,21 +83,24 @@ static int pi3usb30532_mux_set(struct typec_mux *mux, int state)
new_conf = pi->conf;
switch (state) {
- case TYPEC_MUX_NONE:
+ case TYPEC_STATE_SAFE:
new_conf = PI3USB30532_CONF_OPEN;
break;
- case TYPEC_MUX_USB:
+ case TYPEC_STATE_USB:
new_conf = (new_conf & PI3USB30532_CONF_SWAP) |
PI3USB30532_CONF_USB3;
break;
- case TYPEC_MUX_DP:
+ case TYPEC_DP_STATE_C:
+ case TYPEC_DP_STATE_E:
new_conf = (new_conf & PI3USB30532_CONF_SWAP) |
PI3USB30532_CONF_4LANE_DP;
break;
- case TYPEC_MUX_DOCK:
+ case TYPEC_DP_STATE_D:
new_conf = (new_conf & PI3USB30532_CONF_SWAP) |
PI3USB30532_CONF_USB3_AND_2LANE_DP;
break;
+ default:
+ break;
}
ret = pi3usb30532_set_conf(pi, new_conf);
diff --git a/drivers/usb/typec/tcpm/Kconfig b/drivers/usb/typec/tcpm/Kconfig
new file mode 100644
index 000000000000..f03ea8a61768
--- /dev/null
+++ b/drivers/usb/typec/tcpm/Kconfig
@@ -0,0 +1,52 @@
+config TYPEC_TCPM
+ tristate "USB Type-C Port Controller Manager"
+ depends on USB
+ select USB_ROLE_SWITCH
+ select POWER_SUPPLY
+ help
+ The Type-C Port Controller Manager provides a USB PD and USB Type-C
+ state machine for use with Type-C Port Controllers.
+
+if TYPEC_TCPM
+
+config TYPEC_TCPCI
+ tristate "Type-C Port Controller Interface driver"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Type-C Port Controller driver for TCPCI-compliant controller.
+
+if TYPEC_TCPCI
+
+config TYPEC_RT1711H
+ tristate "Richtek RT1711H Type-C chip driver"
+ help
+ Richtek RT1711H Type-C chip driver that works with
+ Type-C Port Controller Manager to provide USB PD and USB
+ Type-C functionalities.
+
+endif # TYPEC_TCPCI
+
+config TYPEC_FUSB302
+ tristate "Fairchild FUSB302 Type-C chip driver"
+ depends on I2C
+ help
+ The Fairchild FUSB302 Type-C chip driver that works with
+ Type-C Port Controller Manager to provide USB PD and USB
+ Type-C functionalities.
+
+config TYPEC_WCOVE
+ tristate "Intel WhiskeyCove PMIC USB Type-C PHY driver"
+ depends on ACPI
+ depends on INTEL_SOC_PMIC
+ depends on INTEL_PMC_IPC
+ depends on BXT_WC_PMIC_OPREGION
+ help
+ This driver adds support for USB Type-C on Intel Broxton platforms
+ that have Intel Whiskey Cove PMIC. The driver works with USB Type-C
+ Port Controller Manager to provide USB PD and Type-C functionalities.
+
+ To compile this driver as module, choose M here: the module will be
+ called typec_wcove.ko
+
+endif # TYPEC_TCPM
diff --git a/drivers/usb/typec/tcpm/Makefile b/drivers/usb/typec/tcpm/Makefile
new file mode 100644
index 000000000000..a5ff6c8eb892
--- /dev/null
+++ b/drivers/usb/typec/tcpm/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_TYPEC_TCPM) += tcpm.o
+obj-$(CONFIG_TYPEC_FUSB302) += fusb302.o
+obj-$(CONFIG_TYPEC_WCOVE) += typec_wcove.o
+typec_wcove-y := wcove.o
+obj-$(CONFIG_TYPEC_TCPCI) += tcpci.o
+obj-$(CONFIG_TYPEC_RT1711H) += tcpci_rt1711h.o
diff --git a/drivers/usb/typec/fusb302/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c
index 703617129067..43b64d9309d0 100644
--- a/drivers/usb/typec/fusb302/fusb302.c
+++ b/drivers/usb/typec/tcpm/fusb302.c
@@ -18,7 +18,6 @@
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/pinctrl/consumer.h>
-#include <linux/power_supply.h>
#include <linux/proc_fs.h>
#include <linux/regulator/consumer.h>
#include <linux/sched/clock.h>
@@ -43,19 +42,12 @@
#define T_BC_LVL_DEBOUNCE_DELAY_MS 30
enum toggling_mode {
- TOGGLINE_MODE_OFF,
+ TOGGLING_MODE_OFF,
TOGGLING_MODE_DRP,
TOGGLING_MODE_SNK,
TOGGLING_MODE_SRC,
};
-static const char * const toggling_mode_name[] = {
- [TOGGLINE_MODE_OFF] = "toggling_OFF",
- [TOGGLING_MODE_DRP] = "toggling_DRP",
- [TOGGLING_MODE_SNK] = "toggling_SNK",
- [TOGGLING_MODE_SRC] = "toggling_SRC",
-};
-
enum src_current_status {
SRC_CURRENT_DEFAULT,
SRC_CURRENT_MEDIUM,
@@ -99,11 +91,6 @@ struct fusb302_chip {
/* lock for sharing chip states */
struct mutex lock;
- /* psy + psy status */
- struct power_supply *psy;
- u32 current_limit;
- u32 supply_voltage;
-
/* chip status */
enum toggling_mode toggling_mode;
enum src_current_status src_current_status;
@@ -120,6 +107,7 @@ struct fusb302_chip {
enum typec_cc_polarity cc_polarity;
enum typec_cc_status cc1;
enum typec_cc_status cc2;
+ u32 snk_pdo[PDO_MAX_OBJECTS];
#ifdef CONFIG_DEBUG_FS
struct dentry *dentry;
@@ -220,32 +208,28 @@ DEFINE_SHOW_ATTRIBUTE(fusb302_debug);
static struct dentry *rootdir;
-static int fusb302_debugfs_init(struct fusb302_chip *chip)
+static void fusb302_debugfs_init(struct fusb302_chip *chip)
{
mutex_init(&chip->logbuffer_lock);
- if (!rootdir) {
+ if (!rootdir)
rootdir = debugfs_create_dir("fusb302", NULL);
- if (!rootdir)
- return -ENOMEM;
- }
chip->dentry = debugfs_create_file(dev_name(chip->dev),
S_IFREG | 0444, rootdir,
chip, &fusb302_debug_fops);
-
- return 0;
}
static void fusb302_debugfs_exit(struct fusb302_chip *chip)
{
debugfs_remove(chip->dentry);
+ debugfs_remove(rootdir);
}
#else
static void fusb302_log(const struct fusb302_chip *chip,
const char *fmt, ...) { }
-static int fusb302_debugfs_init(const struct fusb302_chip *chip) { return 0; }
+static void fusb302_debugfs_init(const struct fusb302_chip *chip) { }
static void fusb302_debugfs_exit(const struct fusb302_chip *chip) { }
#endif
@@ -610,7 +594,7 @@ static int fusb302_set_toggling(struct fusb302_chip *chip,
chip->intr_comp_chng = false;
/* configure toggling mode: none/snk/src/drp */
switch (mode) {
- case TOGGLINE_MODE_OFF:
+ case TOGGLING_MODE_OFF:
ret = fusb302_i2c_mask_write(chip, FUSB_REG_CONTROL2,
FUSB_REG_CONTROL2_MODE_MASK,
FUSB_REG_CONTROL2_MODE_NONE);
@@ -642,7 +626,7 @@ static int fusb302_set_toggling(struct fusb302_chip *chip,
break;
}
- if (mode == TOGGLINE_MODE_OFF) {
+ if (mode == TOGGLING_MODE_OFF) {
/* mask TOGDONE interrupt */
ret = fusb302_i2c_set_bits(chip, FUSB_REG_MASKA,
FUSB_REG_MASKA_TOGDONE);
@@ -695,6 +679,7 @@ static int tcpm_set_cc(struct tcpc_dev *dev, enum typec_cc_status cc)
int ret = 0;
bool pull_up, pull_down;
u8 rd_mda;
+ enum toggling_mode mode;
mutex_lock(&chip->lock);
switch (cc) {
@@ -718,7 +703,7 @@ static int tcpm_set_cc(struct tcpc_dev *dev, enum typec_cc_status cc)
ret = -EINVAL;
goto done;
}
- ret = fusb302_set_toggling(chip, TOGGLINE_MODE_OFF);
+ ret = fusb302_set_toggling(chip, TOGGLING_MODE_OFF);
if (ret < 0) {
fusb302_log(chip, "cannot stop toggling, ret=%d", ret);
goto done;
@@ -780,6 +765,29 @@ static int tcpm_set_cc(struct tcpc_dev *dev, enum typec_cc_status cc)
chip->intr_comp_chng = false;
}
fusb302_log(chip, "cc := %s", typec_cc_status_name[cc]);
+
+ /* Enable detection for fixed SNK or SRC only roles */
+ switch (cc) {
+ case TYPEC_CC_RD:
+ mode = TOGGLING_MODE_SNK;
+ break;
+ case TYPEC_CC_RP_DEF:
+ case TYPEC_CC_RP_1_5:
+ case TYPEC_CC_RP_3_0:
+ mode = TOGGLING_MODE_SRC;
+ break;
+ default:
+ mode = TOGGLING_MODE_OFF;
+ break;
+ }
+
+ if (mode != TOGGLING_MODE_OFF) {
+ ret = fusb302_set_toggling(chip, mode);
+ if (ret < 0)
+ fusb302_log(chip,
+ "cannot set fixed role toggling mode, ret=%d",
+ ret);
+ }
done:
mutex_unlock(&chip->lock);
@@ -861,13 +869,11 @@ static int tcpm_set_vbus(struct tcpc_dev *dev, bool on, bool charge)
chip->vbus_on = on;
fusb302_log(chip, "vbus := %s", on ? "On" : "Off");
}
- if (chip->charge_on == charge) {
+ if (chip->charge_on == charge)
fusb302_log(chip, "charge is already %s",
charge ? "On" : "Off");
- } else {
+ else
chip->charge_on = charge;
- power_supply_changed(chip->psy);
- }
done:
mutex_unlock(&chip->lock);
@@ -875,22 +881,6 @@ done:
return ret;
}
-static int tcpm_set_current_limit(struct tcpc_dev *dev, u32 max_ma, u32 mv)
-{
- struct fusb302_chip *chip = container_of(dev, struct fusb302_chip,
- tcpc_dev);
-
- fusb302_log(chip, "current limit: %d ma, %d mv (not implemented)",
- max_ma, mv);
-
- chip->supply_voltage = mv;
- chip->current_limit = max_ma;
-
- power_supply_changed(chip->psy);
-
- return 0;
-}
-
static int fusb302_pd_tx_flush(struct fusb302_chip *chip)
{
return fusb302_i2c_set_bits(chip, FUSB_REG_CONTROL0,
@@ -1205,18 +1195,9 @@ static const u32 src_pdo[] = {
PDO_FIXED(5000, 400, PDO_FIXED_FLAGS),
};
-static const u32 snk_pdo[] = {
- PDO_FIXED(5000, 400, PDO_FIXED_FLAGS),
-};
-
static const struct tcpc_config fusb302_tcpc_config = {
.src_pdo = src_pdo,
.nr_src_pdo = ARRAY_SIZE(src_pdo),
- .snk_pdo = snk_pdo,
- .nr_snk_pdo = ARRAY_SIZE(snk_pdo),
- .max_snk_mv = 5000,
- .max_snk_ma = 3000,
- .max_snk_mw = 15000,
.operating_snk_mw = 2500,
.type = TYPEC_PORT_DRP,
.data = TYPEC_PORT_DRD,
@@ -1234,7 +1215,6 @@ static void init_tcpc_dev(struct tcpc_dev *fusb302_tcpc_dev)
fusb302_tcpc_dev->set_polarity = tcpm_set_polarity;
fusb302_tcpc_dev->set_vconn = tcpm_set_vconn;
fusb302_tcpc_dev->set_vbus = tcpm_set_vbus;
- fusb302_tcpc_dev->set_current_limit = tcpm_set_current_limit;
fusb302_tcpc_dev->set_pd_rx = tcpm_set_pd_rx;
fusb302_tcpc_dev->set_roles = tcpm_set_roles;
fusb302_tcpc_dev->start_drp_toggling = tcpm_start_drp_toggling;
@@ -1336,7 +1316,7 @@ static int fusb302_handle_togdone_snk(struct fusb302_chip *chip,
tcpm_cc_change(chip->tcpm_port);
}
/* turn off toggling */
- ret = fusb302_set_toggling(chip, TOGGLINE_MODE_OFF);
+ ret = fusb302_set_toggling(chip, TOGGLING_MODE_OFF);
if (ret < 0) {
fusb302_log(chip,
"cannot set toggling mode off, ret=%d", ret);
@@ -1432,7 +1412,7 @@ static int fusb302_handle_togdone_src(struct fusb302_chip *chip,
tcpm_cc_change(chip->tcpm_port);
}
/* turn off toggling */
- ret = fusb302_set_toggling(chip, TOGGLINE_MODE_OFF);
+ ret = fusb302_set_toggling(chip, TOGGLING_MODE_OFF);
if (ret < 0) {
fusb302_log(chip,
"cannot set toggling mode off, ret=%d", ret);
@@ -1686,43 +1666,6 @@ done:
return IRQ_HANDLED;
}
-static int fusb302_psy_get_property(struct power_supply *psy,
- enum power_supply_property psp,
- union power_supply_propval *val)
-{
- struct fusb302_chip *chip = power_supply_get_drvdata(psy);
-
- switch (psp) {
- case POWER_SUPPLY_PROP_ONLINE:
- val->intval = chip->charge_on;
- break;
- case POWER_SUPPLY_PROP_VOLTAGE_NOW:
- val->intval = chip->supply_voltage * 1000; /* mV -> µV */
- break;
- case POWER_SUPPLY_PROP_CURRENT_MAX:
- val->intval = chip->current_limit * 1000; /* mA -> µA */
- break;
- default:
- return -ENODATA;
- }
-
- return 0;
-}
-
-static enum power_supply_property fusb302_psy_properties[] = {
- POWER_SUPPLY_PROP_ONLINE,
- POWER_SUPPLY_PROP_VOLTAGE_NOW,
- POWER_SUPPLY_PROP_CURRENT_MAX,
-};
-
-static const struct power_supply_desc fusb302_psy_desc = {
- .name = "fusb302-typec-source",
- .type = POWER_SUPPLY_TYPE_USB_TYPE_C,
- .properties = fusb302_psy_properties,
- .num_properties = ARRAY_SIZE(fusb302_psy_properties),
- .get_property = fusb302_psy_get_property,
-};
-
static int init_gpio(struct fusb302_chip *chip)
{
struct device_node *node;
@@ -1756,13 +1699,35 @@ static int init_gpio(struct fusb302_chip *chip)
return 0;
}
+static int fusb302_composite_snk_pdo_array(struct fusb302_chip *chip)
+{
+ struct device *dev = chip->dev;
+ u32 max_uv, max_ua;
+
+ chip->snk_pdo[0] = PDO_FIXED(5000, 400, PDO_FIXED_FLAGS);
+
+ /*
+ * As max_snk_ma/mv/mw is not needed for tcpc_config,
+ * those settings should be passed in via sink PDO, so
+ * "fcs, max-sink-*" properties will be deprecated, to
+ * perserve compatibility with existing users of them,
+ * we read those properties to convert them to be a var
+ * PDO.
+ */
+ if (device_property_read_u32(dev, "fcs,max-sink-microvolt", &max_uv) ||
+ device_property_read_u32(dev, "fcs,max-sink-microamp", &max_ua))
+ return 1;
+
+ chip->snk_pdo[1] = PDO_VAR(5000, max_uv / 1000, max_ua / 1000);
+ return 2;
+}
+
static int fusb302_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct fusb302_chip *chip;
struct i2c_adapter *adapter;
struct device *dev = &client->dev;
- struct power_supply_config cfg = {};
const char *name;
int ret = 0;
u32 v;
@@ -1778,24 +1743,21 @@ static int fusb302_probe(struct i2c_client *client,
return -ENOMEM;
chip->i2c_client = client;
- i2c_set_clientdata(client, chip);
chip->dev = &client->dev;
chip->tcpc_config = fusb302_tcpc_config;
chip->tcpc_dev.config = &chip->tcpc_config;
mutex_init(&chip->lock);
- if (!device_property_read_u32(dev, "fcs,max-sink-microvolt", &v))
- chip->tcpc_config.max_snk_mv = v / 1000;
-
- if (!device_property_read_u32(dev, "fcs,max-sink-microamp", &v))
- chip->tcpc_config.max_snk_ma = v / 1000;
-
- if (!device_property_read_u32(dev, "fcs,max-sink-microwatt", &v))
- chip->tcpc_config.max_snk_mw = v / 1000;
+ chip->tcpc_dev.fwnode =
+ device_get_named_child_node(dev, "connector");
if (!device_property_read_u32(dev, "fcs,operating-sink-microwatt", &v))
chip->tcpc_config.operating_snk_mw = v / 1000;
+ /* Composite sink PDO */
+ chip->tcpc_config.nr_snk_pdo = fusb302_composite_snk_pdo_array(chip);
+ chip->tcpc_config.snk_pdo = chip->snk_pdo;
+
/*
* Devicetree platforms should get extcon via phandle (not yet
* supported). On ACPI platforms, we get the name from a device prop.
@@ -1809,32 +1771,17 @@ static int fusb302_probe(struct i2c_client *client,
return -EPROBE_DEFER;
}
- cfg.drv_data = chip;
- chip->psy = devm_power_supply_register(dev, &fusb302_psy_desc, &cfg);
- if (IS_ERR(chip->psy)) {
- ret = PTR_ERR(chip->psy);
- dev_err(chip->dev, "Error registering power-supply: %d\n", ret);
- return ret;
- }
-
- ret = fusb302_debugfs_init(chip);
- if (ret < 0)
- return ret;
+ chip->vbus = devm_regulator_get(chip->dev, "vbus");
+ if (IS_ERR(chip->vbus))
+ return PTR_ERR(chip->vbus);
chip->wq = create_singlethread_workqueue(dev_name(chip->dev));
- if (!chip->wq) {
- ret = -ENOMEM;
- goto clear_client_data;
- }
+ if (!chip->wq)
+ return -ENOMEM;
+
INIT_DELAYED_WORK(&chip->bc_lvl_handler, fusb302_bc_lvl_handler_work);
init_tcpc_dev(&chip->tcpc_dev);
- chip->vbus = devm_regulator_get(chip->dev, "vbus");
- if (IS_ERR(chip->vbus)) {
- ret = PTR_ERR(chip->vbus);
- goto destroy_workqueue;
- }
-
if (client->irq) {
chip->gpio_int_n_irq = client->irq;
} else {
@@ -1860,15 +1807,15 @@ static int fusb302_probe(struct i2c_client *client,
goto tcpm_unregister_port;
}
enable_irq_wake(chip->gpio_int_n_irq);
+ fusb302_debugfs_init(chip);
+ i2c_set_clientdata(client, chip);
+
return ret;
tcpm_unregister_port:
tcpm_unregister_port(chip->tcpm_port);
destroy_workqueue:
destroy_workqueue(chip->wq);
-clear_client_data:
- i2c_set_clientdata(client, NULL);
- fusb302_debugfs_exit(chip);
return ret;
}
@@ -1879,7 +1826,6 @@ static int fusb302_remove(struct i2c_client *client)
tcpm_unregister_port(chip->tcpm_port);
destroy_workqueue(chip->wq);
- i2c_set_clientdata(client, NULL);
fusb302_debugfs_exit(chip);
return 0;
diff --git a/drivers/usb/typec/fusb302/fusb302_reg.h b/drivers/usb/typec/tcpm/fusb302_reg.h
index 00b39d365478..00b39d365478 100644
--- a/drivers/usb/typec/fusb302/fusb302_reg.h
+++ b/drivers/usb/typec/tcpm/fusb302_reg.h
diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c
new file mode 100644
index 000000000000..ac6b418b15f1
--- /dev/null
+++ b/drivers/usb/typec/tcpm/tcpci.c
@@ -0,0 +1,612 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2015-2017 Google, Inc
+ *
+ * USB Type-C Port Controller Interface.
+ */
+
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/usb/pd.h>
+#include <linux/usb/tcpm.h>
+#include <linux/usb/typec.h>
+
+#include "tcpci.h"
+
+#define PD_RETRY_COUNT 3
+
+struct tcpci {
+ struct device *dev;
+
+ struct tcpm_port *port;
+
+ struct regmap *regmap;
+
+ bool controls_vbus;
+
+ struct tcpc_dev tcpc;
+ struct tcpci_data *data;
+};
+
+struct tcpci_chip {
+ struct tcpci *tcpci;
+ struct tcpci_data data;
+};
+
+static inline struct tcpci *tcpc_to_tcpci(struct tcpc_dev *tcpc)
+{
+ return container_of(tcpc, struct tcpci, tcpc);
+}
+
+static int tcpci_read16(struct tcpci *tcpci, unsigned int reg, u16 *val)
+{
+ return regmap_raw_read(tcpci->regmap, reg, val, sizeof(u16));
+}
+
+static int tcpci_write16(struct tcpci *tcpci, unsigned int reg, u16 val)
+{
+ return regmap_raw_write(tcpci->regmap, reg, &val, sizeof(u16));
+}
+
+static int tcpci_set_cc(struct tcpc_dev *tcpc, enum typec_cc_status cc)
+{
+ struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
+ unsigned int reg;
+ int ret;
+
+ switch (cc) {
+ case TYPEC_CC_RA:
+ reg = (TCPC_ROLE_CTRL_CC_RA << TCPC_ROLE_CTRL_CC1_SHIFT) |
+ (TCPC_ROLE_CTRL_CC_RA << TCPC_ROLE_CTRL_CC2_SHIFT);
+ break;
+ case TYPEC_CC_RD:
+ reg = (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC1_SHIFT) |
+ (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC2_SHIFT);
+ break;
+ case TYPEC_CC_RP_DEF:
+ reg = (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT) |
+ (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT) |
+ (TCPC_ROLE_CTRL_RP_VAL_DEF <<
+ TCPC_ROLE_CTRL_RP_VAL_SHIFT);
+ break;
+ case TYPEC_CC_RP_1_5:
+ reg = (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT) |
+ (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT) |
+ (TCPC_ROLE_CTRL_RP_VAL_1_5 <<
+ TCPC_ROLE_CTRL_RP_VAL_SHIFT);
+ break;
+ case TYPEC_CC_RP_3_0:
+ reg = (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT) |
+ (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT) |
+ (TCPC_ROLE_CTRL_RP_VAL_3_0 <<
+ TCPC_ROLE_CTRL_RP_VAL_SHIFT);
+ break;
+ case TYPEC_CC_OPEN:
+ default:
+ reg = (TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC1_SHIFT) |
+ (TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC2_SHIFT);
+ break;
+ }
+
+ ret = regmap_write(tcpci->regmap, TCPC_ROLE_CTRL, reg);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int tcpci_start_drp_toggling(struct tcpc_dev *tcpc,
+ enum typec_cc_status cc)
+{
+ int ret;
+ struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
+ unsigned int reg = TCPC_ROLE_CTRL_DRP;
+
+ /* Handle vendor drp toggling */
+ if (tcpci->data->start_drp_toggling) {
+ ret = tcpci->data->start_drp_toggling(tcpci, tcpci->data, cc);
+ if (ret < 0)
+ return ret;
+ }
+
+ switch (cc) {
+ default:
+ case TYPEC_CC_RP_DEF:
+ reg |= (TCPC_ROLE_CTRL_RP_VAL_DEF <<
+ TCPC_ROLE_CTRL_RP_VAL_SHIFT);
+ break;
+ case TYPEC_CC_RP_1_5:
+ reg |= (TCPC_ROLE_CTRL_RP_VAL_1_5 <<
+ TCPC_ROLE_CTRL_RP_VAL_SHIFT);
+ break;
+ case TYPEC_CC_RP_3_0:
+ reg |= (TCPC_ROLE_CTRL_RP_VAL_3_0 <<
+ TCPC_ROLE_CTRL_RP_VAL_SHIFT);
+ break;
+ }
+
+ if (cc == TYPEC_CC_RD)
+ reg |= (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC1_SHIFT) |
+ (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC2_SHIFT);
+ else
+ reg |= (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT) |
+ (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT);
+ ret = regmap_write(tcpci->regmap, TCPC_ROLE_CTRL, reg);
+ if (ret < 0)
+ return ret;
+ return regmap_write(tcpci->regmap, TCPC_COMMAND,
+ TCPC_CMD_LOOK4CONNECTION);
+}
+
+static enum typec_cc_status tcpci_to_typec_cc(unsigned int cc, bool sink)
+{
+ switch (cc) {
+ case 0x1:
+ return sink ? TYPEC_CC_RP_DEF : TYPEC_CC_RA;
+ case 0x2:
+ return sink ? TYPEC_CC_RP_1_5 : TYPEC_CC_RD;
+ case 0x3:
+ if (sink)
+ return TYPEC_CC_RP_3_0;
+ /* fall through */
+ case 0x0:
+ default:
+ return TYPEC_CC_OPEN;
+ }
+}
+
+static int tcpci_get_cc(struct tcpc_dev *tcpc,
+ enum typec_cc_status *cc1, enum typec_cc_status *cc2)
+{
+ struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
+ unsigned int reg;
+ int ret;
+
+ ret = regmap_read(tcpci->regmap, TCPC_CC_STATUS, &reg);
+ if (ret < 0)
+ return ret;
+
+ *cc1 = tcpci_to_typec_cc((reg >> TCPC_CC_STATUS_CC1_SHIFT) &
+ TCPC_CC_STATUS_CC1_MASK,
+ reg & TCPC_CC_STATUS_TERM);
+ *cc2 = tcpci_to_typec_cc((reg >> TCPC_CC_STATUS_CC2_SHIFT) &
+ TCPC_CC_STATUS_CC2_MASK,
+ reg & TCPC_CC_STATUS_TERM);
+
+ return 0;
+}
+
+static int tcpci_set_polarity(struct tcpc_dev *tcpc,
+ enum typec_cc_polarity polarity)
+{
+ struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
+ unsigned int reg;
+ int ret;
+
+ /* Keep the disconnect cc line open */
+ ret = regmap_read(tcpci->regmap, TCPC_ROLE_CTRL, &reg);
+ if (ret < 0)
+ return ret;
+
+ if (polarity == TYPEC_POLARITY_CC2)
+ reg |= TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC1_SHIFT;
+ else
+ reg |= TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC2_SHIFT;
+ ret = regmap_write(tcpci->regmap, TCPC_ROLE_CTRL, reg);
+ if (ret < 0)
+ return ret;
+
+ return regmap_write(tcpci->regmap, TCPC_TCPC_CTRL,
+ (polarity == TYPEC_POLARITY_CC2) ?
+ TCPC_TCPC_CTRL_ORIENTATION : 0);
+}
+
+static int tcpci_set_vconn(struct tcpc_dev *tcpc, bool enable)
+{
+ struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
+ int ret;
+
+ /* Handle vendor set vconn */
+ if (tcpci->data->set_vconn) {
+ ret = tcpci->data->set_vconn(tcpci, tcpci->data, enable);
+ if (ret < 0)
+ return ret;
+ }
+
+ return regmap_update_bits(tcpci->regmap, TCPC_POWER_CTRL,
+ TCPC_POWER_CTRL_VCONN_ENABLE,
+ enable ? TCPC_POWER_CTRL_VCONN_ENABLE : 0);
+}
+
+static int tcpci_set_roles(struct tcpc_dev *tcpc, bool attached,
+ enum typec_role role, enum typec_data_role data)
+{
+ struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
+ unsigned int reg;
+ int ret;
+
+ reg = PD_REV20 << TCPC_MSG_HDR_INFO_REV_SHIFT;
+ if (role == TYPEC_SOURCE)
+ reg |= TCPC_MSG_HDR_INFO_PWR_ROLE;
+ if (data == TYPEC_HOST)
+ reg |= TCPC_MSG_HDR_INFO_DATA_ROLE;
+ ret = regmap_write(tcpci->regmap, TCPC_MSG_HDR_INFO, reg);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int tcpci_set_pd_rx(struct tcpc_dev *tcpc, bool enable)
+{
+ struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
+ unsigned int reg = 0;
+ int ret;
+
+ if (enable)
+ reg = TCPC_RX_DETECT_SOP | TCPC_RX_DETECT_HARD_RESET;
+ ret = regmap_write(tcpci->regmap, TCPC_RX_DETECT, reg);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int tcpci_get_vbus(struct tcpc_dev *tcpc)
+{
+ struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
+ unsigned int reg;
+ int ret;
+
+ ret = regmap_read(tcpci->regmap, TCPC_POWER_STATUS, &reg);
+ if (ret < 0)
+ return ret;
+
+ return !!(reg & TCPC_POWER_STATUS_VBUS_PRES);
+}
+
+static int tcpci_set_vbus(struct tcpc_dev *tcpc, bool source, bool sink)
+{
+ struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
+ int ret;
+
+ /* Disable both source and sink first before enabling anything */
+
+ if (!source) {
+ ret = regmap_write(tcpci->regmap, TCPC_COMMAND,
+ TCPC_CMD_DISABLE_SRC_VBUS);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (!sink) {
+ ret = regmap_write(tcpci->regmap, TCPC_COMMAND,
+ TCPC_CMD_DISABLE_SINK_VBUS);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (source) {
+ ret = regmap_write(tcpci->regmap, TCPC_COMMAND,
+ TCPC_CMD_SRC_VBUS_DEFAULT);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (sink) {
+ ret = regmap_write(tcpci->regmap, TCPC_COMMAND,
+ TCPC_CMD_SINK_VBUS);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int tcpci_pd_transmit(struct tcpc_dev *tcpc,
+ enum tcpm_transmit_type type,
+ const struct pd_message *msg)
+{
+ struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
+ u16 header = msg ? le16_to_cpu(msg->header) : 0;
+ unsigned int reg, cnt;
+ int ret;
+
+ cnt = msg ? pd_header_cnt(header) * 4 : 0;
+ ret = regmap_write(tcpci->regmap, TCPC_TX_BYTE_CNT, cnt + 2);
+ if (ret < 0)
+ return ret;
+
+ ret = tcpci_write16(tcpci, TCPC_TX_HDR, header);
+ if (ret < 0)
+ return ret;
+
+ if (cnt > 0) {
+ ret = regmap_raw_write(tcpci->regmap, TCPC_TX_DATA,
+ &msg->payload, cnt);
+ if (ret < 0)
+ return ret;
+ }
+
+ reg = (PD_RETRY_COUNT << TCPC_TRANSMIT_RETRY_SHIFT) |
+ (type << TCPC_TRANSMIT_TYPE_SHIFT);
+ ret = regmap_write(tcpci->regmap, TCPC_TRANSMIT, reg);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int tcpci_init(struct tcpc_dev *tcpc)
+{
+ struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
+ unsigned long timeout = jiffies + msecs_to_jiffies(2000); /* XXX */
+ unsigned int reg;
+ int ret;
+
+ while (time_before_eq(jiffies, timeout)) {
+ ret = regmap_read(tcpci->regmap, TCPC_POWER_STATUS, &reg);
+ if (ret < 0)
+ return ret;
+ if (!(reg & TCPC_POWER_STATUS_UNINIT))
+ break;
+ usleep_range(10000, 20000);
+ }
+ if (time_after(jiffies, timeout))
+ return -ETIMEDOUT;
+
+ /* Handle vendor init */
+ if (tcpci->data->init) {
+ ret = tcpci->data->init(tcpci, tcpci->data);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Clear all events */
+ ret = tcpci_write16(tcpci, TCPC_ALERT, 0xffff);
+ if (ret < 0)
+ return ret;
+
+ if (tcpci->controls_vbus)
+ reg = TCPC_POWER_STATUS_VBUS_PRES;
+ else
+ reg = 0;
+ ret = regmap_write(tcpci->regmap, TCPC_POWER_STATUS_MASK, reg);
+ if (ret < 0)
+ return ret;
+
+ /* Enable Vbus detection */
+ ret = regmap_write(tcpci->regmap, TCPC_COMMAND,
+ TCPC_CMD_ENABLE_VBUS_DETECT);
+ if (ret < 0)
+ return ret;
+
+ reg = TCPC_ALERT_TX_SUCCESS | TCPC_ALERT_TX_FAILED |
+ TCPC_ALERT_TX_DISCARDED | TCPC_ALERT_RX_STATUS |
+ TCPC_ALERT_RX_HARD_RST | TCPC_ALERT_CC_STATUS;
+ if (tcpci->controls_vbus)
+ reg |= TCPC_ALERT_POWER_STATUS;
+ return tcpci_write16(tcpci, TCPC_ALERT_MASK, reg);
+}
+
+irqreturn_t tcpci_irq(struct tcpci *tcpci)
+{
+ u16 status;
+
+ tcpci_read16(tcpci, TCPC_ALERT, &status);
+
+ /*
+ * Clear alert status for everything except RX_STATUS, which shouldn't
+ * be cleared until we have successfully retrieved message.
+ */
+ if (status & ~TCPC_ALERT_RX_STATUS)
+ tcpci_write16(tcpci, TCPC_ALERT,
+ status & ~TCPC_ALERT_RX_STATUS);
+
+ if (status & TCPC_ALERT_CC_STATUS)
+ tcpm_cc_change(tcpci->port);
+
+ if (status & TCPC_ALERT_POWER_STATUS) {
+ unsigned int reg;
+
+ regmap_read(tcpci->regmap, TCPC_POWER_STATUS_MASK, &reg);
+
+ /*
+ * If power status mask has been reset, then the TCPC
+ * has reset.
+ */
+ if (reg == 0xff)
+ tcpm_tcpc_reset(tcpci->port);
+ else
+ tcpm_vbus_change(tcpci->port);
+ }
+
+ if (status & TCPC_ALERT_RX_STATUS) {
+ struct pd_message msg;
+ unsigned int cnt;
+ u16 header;
+
+ regmap_read(tcpci->regmap, TCPC_RX_BYTE_CNT, &cnt);
+
+ tcpci_read16(tcpci, TCPC_RX_HDR, &header);
+ msg.header = cpu_to_le16(header);
+
+ if (WARN_ON(cnt > sizeof(msg.payload)))
+ cnt = sizeof(msg.payload);
+
+ if (cnt > 0)
+ regmap_raw_read(tcpci->regmap, TCPC_RX_DATA,
+ &msg.payload, cnt);
+
+ /* Read complete, clear RX status alert bit */
+ tcpci_write16(tcpci, TCPC_ALERT, TCPC_ALERT_RX_STATUS);
+
+ tcpm_pd_receive(tcpci->port, &msg);
+ }
+
+ if (status & TCPC_ALERT_RX_HARD_RST)
+ tcpm_pd_hard_reset(tcpci->port);
+
+ if (status & TCPC_ALERT_TX_SUCCESS)
+ tcpm_pd_transmit_complete(tcpci->port, TCPC_TX_SUCCESS);
+ else if (status & TCPC_ALERT_TX_DISCARDED)
+ tcpm_pd_transmit_complete(tcpci->port, TCPC_TX_DISCARDED);
+ else if (status & TCPC_ALERT_TX_FAILED)
+ tcpm_pd_transmit_complete(tcpci->port, TCPC_TX_FAILED);
+
+ return IRQ_HANDLED;
+}
+EXPORT_SYMBOL_GPL(tcpci_irq);
+
+static irqreturn_t _tcpci_irq(int irq, void *dev_id)
+{
+ struct tcpci_chip *chip = dev_id;
+
+ return tcpci_irq(chip->tcpci);
+}
+
+static const struct regmap_config tcpci_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = 0x7F, /* 0x80 .. 0xFF are vendor defined */
+};
+
+static int tcpci_parse_config(struct tcpci *tcpci)
+{
+ tcpci->controls_vbus = true; /* XXX */
+
+ tcpci->tcpc.fwnode = device_get_named_child_node(tcpci->dev,
+ "connector");
+ if (!tcpci->tcpc.fwnode) {
+ dev_err(tcpci->dev, "Can't find connector node.\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+struct tcpci *tcpci_register_port(struct device *dev, struct tcpci_data *data)
+{
+ struct tcpci *tcpci;
+ int err;
+
+ tcpci = devm_kzalloc(dev, sizeof(*tcpci), GFP_KERNEL);
+ if (!tcpci)
+ return ERR_PTR(-ENOMEM);
+
+ tcpci->dev = dev;
+ tcpci->data = data;
+ tcpci->regmap = data->regmap;
+
+ tcpci->tcpc.init = tcpci_init;
+ tcpci->tcpc.get_vbus = tcpci_get_vbus;
+ tcpci->tcpc.set_vbus = tcpci_set_vbus;
+ tcpci->tcpc.set_cc = tcpci_set_cc;
+ tcpci->tcpc.get_cc = tcpci_get_cc;
+ tcpci->tcpc.set_polarity = tcpci_set_polarity;
+ tcpci->tcpc.set_vconn = tcpci_set_vconn;
+ tcpci->tcpc.start_drp_toggling = tcpci_start_drp_toggling;
+
+ tcpci->tcpc.set_pd_rx = tcpci_set_pd_rx;
+ tcpci->tcpc.set_roles = tcpci_set_roles;
+ tcpci->tcpc.pd_transmit = tcpci_pd_transmit;
+
+ err = tcpci_parse_config(tcpci);
+ if (err < 0)
+ return ERR_PTR(err);
+
+ tcpci->port = tcpm_register_port(tcpci->dev, &tcpci->tcpc);
+ if (IS_ERR(tcpci->port))
+ return ERR_CAST(tcpci->port);
+
+ return tcpci;
+}
+EXPORT_SYMBOL_GPL(tcpci_register_port);
+
+void tcpci_unregister_port(struct tcpci *tcpci)
+{
+ tcpm_unregister_port(tcpci->port);
+}
+EXPORT_SYMBOL_GPL(tcpci_unregister_port);
+
+static int tcpci_probe(struct i2c_client *client,
+ const struct i2c_device_id *i2c_id)
+{
+ struct tcpci_chip *chip;
+ int err;
+ u16 val = 0;
+
+ chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->data.regmap = devm_regmap_init_i2c(client, &tcpci_regmap_config);
+ if (IS_ERR(chip->data.regmap))
+ return PTR_ERR(chip->data.regmap);
+
+ i2c_set_clientdata(client, chip);
+
+ /* Disable chip interrupts before requesting irq */
+ err = regmap_raw_write(chip->data.regmap, TCPC_ALERT_MASK, &val,
+ sizeof(u16));
+ if (err < 0)
+ return err;
+
+ chip->tcpci = tcpci_register_port(&client->dev, &chip->data);
+ if (IS_ERR(chip->tcpci))
+ return PTR_ERR(chip->tcpci);
+
+ err = devm_request_threaded_irq(&client->dev, client->irq, NULL,
+ _tcpci_irq,
+ IRQF_ONESHOT | IRQF_TRIGGER_LOW,
+ dev_name(&client->dev), chip);
+ if (err < 0) {
+ tcpci_unregister_port(chip->tcpci);
+ return err;
+ }
+
+ return 0;
+}
+
+static int tcpci_remove(struct i2c_client *client)
+{
+ struct tcpci_chip *chip = i2c_get_clientdata(client);
+
+ tcpci_unregister_port(chip->tcpci);
+
+ return 0;
+}
+
+static const struct i2c_device_id tcpci_id[] = {
+ { "tcpci", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tcpci_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id tcpci_of_match[] = {
+ { .compatible = "nxp,ptn5110", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, tcpci_of_match);
+#endif
+
+static struct i2c_driver tcpci_i2c_driver = {
+ .driver = {
+ .name = "tcpci",
+ .of_match_table = of_match_ptr(tcpci_of_match),
+ },
+ .probe = tcpci_probe,
+ .remove = tcpci_remove,
+ .id_table = tcpci_id,
+};
+module_i2c_driver(tcpci_i2c_driver);
+
+MODULE_DESCRIPTION("USB Type-C Port Controller Interface driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/typec/tcpm/tcpci.h b/drivers/usb/typec/tcpm/tcpci.h
new file mode 100644
index 000000000000..303ebde26546
--- /dev/null
+++ b/drivers/usb/typec/tcpm/tcpci.h
@@ -0,0 +1,139 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2015-2017 Google, Inc
+ *
+ * USB Type-C Port Controller Interface.
+ */
+
+#ifndef __LINUX_USB_TCPCI_H
+#define __LINUX_USB_TCPCI_H
+
+#define TCPC_VENDOR_ID 0x0
+#define TCPC_PRODUCT_ID 0x2
+#define TCPC_BCD_DEV 0x4
+#define TCPC_TC_REV 0x6
+#define TCPC_PD_REV 0x8
+#define TCPC_PD_INT_REV 0xa
+
+#define TCPC_ALERT 0x10
+#define TCPC_ALERT_VBUS_DISCNCT BIT(11)
+#define TCPC_ALERT_RX_BUF_OVF BIT(10)
+#define TCPC_ALERT_FAULT BIT(9)
+#define TCPC_ALERT_V_ALARM_LO BIT(8)
+#define TCPC_ALERT_V_ALARM_HI BIT(7)
+#define TCPC_ALERT_TX_SUCCESS BIT(6)
+#define TCPC_ALERT_TX_DISCARDED BIT(5)
+#define TCPC_ALERT_TX_FAILED BIT(4)
+#define TCPC_ALERT_RX_HARD_RST BIT(3)
+#define TCPC_ALERT_RX_STATUS BIT(2)
+#define TCPC_ALERT_POWER_STATUS BIT(1)
+#define TCPC_ALERT_CC_STATUS BIT(0)
+
+#define TCPC_ALERT_MASK 0x12
+#define TCPC_POWER_STATUS_MASK 0x14
+#define TCPC_FAULT_STATUS_MASK 0x15
+#define TCPC_CONFIG_STD_OUTPUT 0x18
+
+#define TCPC_TCPC_CTRL 0x19
+#define TCPC_TCPC_CTRL_ORIENTATION BIT(0)
+
+#define TCPC_ROLE_CTRL 0x1a
+#define TCPC_ROLE_CTRL_DRP BIT(6)
+#define TCPC_ROLE_CTRL_RP_VAL_SHIFT 4
+#define TCPC_ROLE_CTRL_RP_VAL_MASK 0x3
+#define TCPC_ROLE_CTRL_RP_VAL_DEF 0x0
+#define TCPC_ROLE_CTRL_RP_VAL_1_5 0x1
+#define TCPC_ROLE_CTRL_RP_VAL_3_0 0x2
+#define TCPC_ROLE_CTRL_CC2_SHIFT 2
+#define TCPC_ROLE_CTRL_CC2_MASK 0x3
+#define TCPC_ROLE_CTRL_CC1_SHIFT 0
+#define TCPC_ROLE_CTRL_CC1_MASK 0x3
+#define TCPC_ROLE_CTRL_CC_RA 0x0
+#define TCPC_ROLE_CTRL_CC_RP 0x1
+#define TCPC_ROLE_CTRL_CC_RD 0x2
+#define TCPC_ROLE_CTRL_CC_OPEN 0x3
+
+#define TCPC_FAULT_CTRL 0x1b
+
+#define TCPC_POWER_CTRL 0x1c
+#define TCPC_POWER_CTRL_VCONN_ENABLE BIT(0)
+
+#define TCPC_CC_STATUS 0x1d
+#define TCPC_CC_STATUS_TOGGLING BIT(5)
+#define TCPC_CC_STATUS_TERM BIT(4)
+#define TCPC_CC_STATUS_CC2_SHIFT 2
+#define TCPC_CC_STATUS_CC2_MASK 0x3
+#define TCPC_CC_STATUS_CC1_SHIFT 0
+#define TCPC_CC_STATUS_CC1_MASK 0x3
+
+#define TCPC_POWER_STATUS 0x1e
+#define TCPC_POWER_STATUS_UNINIT BIT(6)
+#define TCPC_POWER_STATUS_VBUS_DET BIT(3)
+#define TCPC_POWER_STATUS_VBUS_PRES BIT(2)
+
+#define TCPC_FAULT_STATUS 0x1f
+
+#define TCPC_COMMAND 0x23
+#define TCPC_CMD_WAKE_I2C 0x11
+#define TCPC_CMD_DISABLE_VBUS_DETECT 0x22
+#define TCPC_CMD_ENABLE_VBUS_DETECT 0x33
+#define TCPC_CMD_DISABLE_SINK_VBUS 0x44
+#define TCPC_CMD_SINK_VBUS 0x55
+#define TCPC_CMD_DISABLE_SRC_VBUS 0x66
+#define TCPC_CMD_SRC_VBUS_DEFAULT 0x77
+#define TCPC_CMD_SRC_VBUS_HIGH 0x88
+#define TCPC_CMD_LOOK4CONNECTION 0x99
+#define TCPC_CMD_RXONEMORE 0xAA
+#define TCPC_CMD_I2C_IDLE 0xFF
+
+#define TCPC_DEV_CAP_1 0x24
+#define TCPC_DEV_CAP_2 0x26
+#define TCPC_STD_INPUT_CAP 0x28
+#define TCPC_STD_OUTPUT_CAP 0x29
+
+#define TCPC_MSG_HDR_INFO 0x2e
+#define TCPC_MSG_HDR_INFO_DATA_ROLE BIT(3)
+#define TCPC_MSG_HDR_INFO_PWR_ROLE BIT(0)
+#define TCPC_MSG_HDR_INFO_REV_SHIFT 1
+#define TCPC_MSG_HDR_INFO_REV_MASK 0x3
+
+#define TCPC_RX_DETECT 0x2f
+#define TCPC_RX_DETECT_HARD_RESET BIT(5)
+#define TCPC_RX_DETECT_SOP BIT(0)
+
+#define TCPC_RX_BYTE_CNT 0x30
+#define TCPC_RX_BUF_FRAME_TYPE 0x31
+#define TCPC_RX_HDR 0x32
+#define TCPC_RX_DATA 0x34 /* through 0x4f */
+
+#define TCPC_TRANSMIT 0x50
+#define TCPC_TRANSMIT_RETRY_SHIFT 4
+#define TCPC_TRANSMIT_RETRY_MASK 0x3
+#define TCPC_TRANSMIT_TYPE_SHIFT 0
+#define TCPC_TRANSMIT_TYPE_MASK 0x7
+
+#define TCPC_TX_BYTE_CNT 0x51
+#define TCPC_TX_HDR 0x52
+#define TCPC_TX_DATA 0x54 /* through 0x6f */
+
+#define TCPC_VBUS_VOLTAGE 0x70
+#define TCPC_VBUS_SINK_DISCONNECT_THRESH 0x72
+#define TCPC_VBUS_STOP_DISCHARGE_THRESH 0x74
+#define TCPC_VBUS_VOLTAGE_ALARM_HI_CFG 0x76
+#define TCPC_VBUS_VOLTAGE_ALARM_LO_CFG 0x78
+
+struct tcpci;
+struct tcpci_data {
+ struct regmap *regmap;
+ int (*init)(struct tcpci *tcpci, struct tcpci_data *data);
+ int (*set_vconn)(struct tcpci *tcpci, struct tcpci_data *data,
+ bool enable);
+ int (*start_drp_toggling)(struct tcpci *tcpci, struct tcpci_data *data,
+ enum typec_cc_status cc);
+};
+
+struct tcpci *tcpci_register_port(struct device *dev, struct tcpci_data *data);
+void tcpci_unregister_port(struct tcpci *tcpci);
+irqreturn_t tcpci_irq(struct tcpci *tcpci);
+
+#endif /* __LINUX_USB_TCPCI_H */
diff --git a/drivers/usb/typec/tcpm/tcpci_rt1711h.c b/drivers/usb/typec/tcpm/tcpci_rt1711h.c
new file mode 100644
index 000000000000..017389021b96
--- /dev/null
+++ b/drivers/usb/typec/tcpm/tcpci_rt1711h.c
@@ -0,0 +1,312 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018, Richtek Technology Corporation
+ *
+ * Richtek RT1711H Type-C Chip Driver
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/gpio/consumer.h>
+#include <linux/usb/tcpm.h>
+#include <linux/regmap.h>
+#include "tcpci.h"
+
+#define RT1711H_VID 0x29CF
+#define RT1711H_PID 0x1711
+
+#define RT1711H_RTCTRL8 0x9B
+
+/* Autoidle timeout = (tout * 2 + 1) * 6.4ms */
+#define RT1711H_RTCTRL8_SET(ck300, ship_off, auto_idle, tout) \
+ (((ck300) << 7) | ((ship_off) << 5) | \
+ ((auto_idle) << 3) | ((tout) & 0x07))
+
+#define RT1711H_RTCTRL11 0x9E
+
+/* I2C timeout = (tout + 1) * 12.5ms */
+#define RT1711H_RTCTRL11_SET(en, tout) \
+ (((en) << 7) | ((tout) & 0x0F))
+
+#define RT1711H_RTCTRL13 0xA0
+#define RT1711H_RTCTRL14 0xA1
+#define RT1711H_RTCTRL15 0xA2
+#define RT1711H_RTCTRL16 0xA3
+
+struct rt1711h_chip {
+ struct tcpci_data data;
+ struct tcpci *tcpci;
+ struct device *dev;
+};
+
+static int rt1711h_read16(struct rt1711h_chip *chip, unsigned int reg, u16 *val)
+{
+ return regmap_raw_read(chip->data.regmap, reg, val, sizeof(u16));
+}
+
+static int rt1711h_write16(struct rt1711h_chip *chip, unsigned int reg, u16 val)
+{
+ return regmap_raw_write(chip->data.regmap, reg, &val, sizeof(u16));
+}
+
+static int rt1711h_read8(struct rt1711h_chip *chip, unsigned int reg, u8 *val)
+{
+ return regmap_raw_read(chip->data.regmap, reg, val, sizeof(u8));
+}
+
+static int rt1711h_write8(struct rt1711h_chip *chip, unsigned int reg, u8 val)
+{
+ return regmap_raw_write(chip->data.regmap, reg, &val, sizeof(u8));
+}
+
+static const struct regmap_config rt1711h_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = 0xFF, /* 0x80 .. 0xFF are vendor defined */
+};
+
+static struct rt1711h_chip *tdata_to_rt1711h(struct tcpci_data *tdata)
+{
+ return container_of(tdata, struct rt1711h_chip, data);
+}
+
+static int rt1711h_init(struct tcpci *tcpci, struct tcpci_data *tdata)
+{
+ int ret;
+ struct rt1711h_chip *chip = tdata_to_rt1711h(tdata);
+
+ /* CK 300K from 320K, shipping off, auto_idle enable, tout = 32ms */
+ ret = rt1711h_write8(chip, RT1711H_RTCTRL8,
+ RT1711H_RTCTRL8_SET(0, 1, 1, 2));
+ if (ret < 0)
+ return ret;
+
+ /* I2C reset : (val + 1) * 12.5ms */
+ ret = rt1711h_write8(chip, RT1711H_RTCTRL11,
+ RT1711H_RTCTRL11_SET(1, 0x0F));
+ if (ret < 0)
+ return ret;
+
+ /* tTCPCfilter : (26.7 * val) us */
+ ret = rt1711h_write8(chip, RT1711H_RTCTRL14, 0x0F);
+ if (ret < 0)
+ return ret;
+
+ /* tDRP : (51.2 + 6.4 * val) ms */
+ ret = rt1711h_write8(chip, RT1711H_RTCTRL15, 0x04);
+ if (ret < 0)
+ return ret;
+
+ /* dcSRC.DRP : 33% */
+ return rt1711h_write16(chip, RT1711H_RTCTRL16, 330);
+}
+
+static int rt1711h_set_vconn(struct tcpci *tcpci, struct tcpci_data *tdata,
+ bool enable)
+{
+ struct rt1711h_chip *chip = tdata_to_rt1711h(tdata);
+
+ return rt1711h_write8(chip, RT1711H_RTCTRL8,
+ RT1711H_RTCTRL8_SET(0, 1, !enable, 2));
+}
+
+static int rt1711h_start_drp_toggling(struct tcpci *tcpci,
+ struct tcpci_data *tdata,
+ enum typec_cc_status cc)
+{
+ struct rt1711h_chip *chip = tdata_to_rt1711h(tdata);
+ int ret;
+ unsigned int reg = 0;
+
+ switch (cc) {
+ default:
+ case TYPEC_CC_RP_DEF:
+ reg |= (TCPC_ROLE_CTRL_RP_VAL_DEF <<
+ TCPC_ROLE_CTRL_RP_VAL_SHIFT);
+ break;
+ case TYPEC_CC_RP_1_5:
+ reg |= (TCPC_ROLE_CTRL_RP_VAL_1_5 <<
+ TCPC_ROLE_CTRL_RP_VAL_SHIFT);
+ break;
+ case TYPEC_CC_RP_3_0:
+ reg |= (TCPC_ROLE_CTRL_RP_VAL_3_0 <<
+ TCPC_ROLE_CTRL_RP_VAL_SHIFT);
+ break;
+ }
+
+ if (cc == TYPEC_CC_RD)
+ reg |= (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC1_SHIFT) |
+ (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC2_SHIFT);
+ else
+ reg |= (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT) |
+ (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT);
+
+ ret = rt1711h_write8(chip, TCPC_ROLE_CTRL, reg);
+ if (ret < 0)
+ return ret;
+ usleep_range(500, 1000);
+
+ return 0;
+}
+
+static irqreturn_t rt1711h_irq(int irq, void *dev_id)
+{
+ int ret;
+ u16 alert;
+ u8 status;
+ struct rt1711h_chip *chip = dev_id;
+
+ if (!chip->tcpci)
+ return IRQ_HANDLED;
+
+ ret = rt1711h_read16(chip, TCPC_ALERT, &alert);
+ if (ret < 0)
+ goto out;
+
+ if (alert & TCPC_ALERT_CC_STATUS) {
+ ret = rt1711h_read8(chip, TCPC_CC_STATUS, &status);
+ if (ret < 0)
+ goto out;
+ /* Clear cc change event triggered by starting toggling */
+ if (status & TCPC_CC_STATUS_TOGGLING)
+ rt1711h_write8(chip, TCPC_ALERT, TCPC_ALERT_CC_STATUS);
+ }
+
+out:
+ return tcpci_irq(chip->tcpci);
+}
+
+static int rt1711h_init_alert(struct rt1711h_chip *chip,
+ struct i2c_client *client)
+{
+ int ret;
+
+ /* Disable chip interrupts before requesting irq */
+ ret = rt1711h_write16(chip, TCPC_ALERT_MASK, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = devm_request_threaded_irq(chip->dev, client->irq, NULL,
+ rt1711h_irq,
+ IRQF_ONESHOT | IRQF_TRIGGER_LOW,
+ dev_name(chip->dev), chip);
+ if (ret < 0)
+ return ret;
+ enable_irq_wake(client->irq);
+ return 0;
+}
+
+static int rt1711h_sw_reset(struct rt1711h_chip *chip)
+{
+ int ret;
+
+ ret = rt1711h_write8(chip, RT1711H_RTCTRL13, 0x01);
+ if (ret < 0)
+ return ret;
+
+ usleep_range(1000, 2000);
+ return 0;
+}
+
+static int rt1711h_check_revision(struct i2c_client *i2c)
+{
+ 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;
+ }
+ return 0;
+}
+
+static int rt1711h_probe(struct i2c_client *client,
+ const struct i2c_device_id *i2c_id)
+{
+ int ret;
+ struct rt1711h_chip *chip;
+
+ ret = rt1711h_check_revision(client);
+ if (ret < 0) {
+ dev_err(&client->dev, "check vid/pid fail\n");
+ return ret;
+ }
+
+ chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->data.regmap = devm_regmap_init_i2c(client,
+ &rt1711h_regmap_config);
+ if (IS_ERR(chip->data.regmap))
+ return PTR_ERR(chip->data.regmap);
+
+ chip->dev = &client->dev;
+ i2c_set_clientdata(client, chip);
+
+ ret = rt1711h_sw_reset(chip);
+ if (ret < 0)
+ return ret;
+
+ ret = rt1711h_init_alert(chip, client);
+ if (ret < 0)
+ return ret;
+
+ chip->data.init = rt1711h_init;
+ chip->data.set_vconn = rt1711h_set_vconn;
+ chip->data.start_drp_toggling = rt1711h_start_drp_toggling;
+ chip->tcpci = tcpci_register_port(chip->dev, &chip->data);
+ if (IS_ERR_OR_NULL(chip->tcpci))
+ return PTR_ERR(chip->tcpci);
+
+ return 0;
+}
+
+static int rt1711h_remove(struct i2c_client *client)
+{
+ struct rt1711h_chip *chip = i2c_get_clientdata(client);
+
+ tcpci_unregister_port(chip->tcpci);
+ return 0;
+}
+
+static const struct i2c_device_id rt1711h_id[] = {
+ { "rt1711h", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, rt1711h_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id rt1711h_of_match[] = {
+ { .compatible = "richtek,rt1711h", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rt1711h_of_match);
+#endif
+
+static struct i2c_driver rt1711h_i2c_driver = {
+ .driver = {
+ .name = "rt1711h",
+ .of_match_table = of_match_ptr(rt1711h_of_match),
+ },
+ .probe = rt1711h_probe,
+ .remove = rt1711h_remove,
+ .id_table = rt1711h_id,
+};
+module_i2c_driver(rt1711h_i2c_driver);
+
+MODULE_AUTHOR("ShuFan Lee <shufan_lee@richtek.com>");
+MODULE_DESCRIPTION("RT1711H USB Type-C Port Controller Interface Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/typec/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
index ded49e3bf2b0..dbbd71f754d0 100644
--- a/drivers/usb/typec/tcpm.c
+++ b/drivers/usb/typec/tcpm/tcpm.c
@@ -12,17 +12,21 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
+#include <linux/power_supply.h>
#include <linux/proc_fs.h>
+#include <linux/property.h>
#include <linux/sched/clock.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/usb/pd.h>
+#include <linux/usb/pd_ado.h>
#include <linux/usb/pd_bdo.h>
+#include <linux/usb/pd_ext_sdb.h>
#include <linux/usb/pd_vdo.h>
#include <linux/usb/role.h>
#include <linux/usb/tcpm.h>
-#include <linux/usb/typec.h>
+#include <linux/usb/typec_altmode.h>
#include <linux/workqueue.h>
#define FOREACH_STATE(S) \
@@ -48,6 +52,7 @@
S(SNK_DISCOVERY_DEBOUNCE_DONE), \
S(SNK_WAIT_CAPABILITIES), \
S(SNK_NEGOTIATE_CAPABILITIES), \
+ S(SNK_NEGOTIATE_PPS_CAPABILITIES), \
S(SNK_TRANSITION_SINK), \
S(SNK_TRANSITION_SINK_VBUS), \
S(SNK_READY), \
@@ -112,6 +117,11 @@
S(SNK_TRYWAIT_VBUS), \
S(BIST_RX), \
\
+ S(GET_STATUS_SEND), \
+ S(GET_STATUS_SEND_TIMEOUT), \
+ S(GET_PPS_STATUS_SEND), \
+ S(GET_PPS_STATUS_SEND_TIMEOUT), \
+ \
S(ERROR_RECOVERY), \
S(PORT_RESET), \
S(PORT_RESET_WAIT_OFF)
@@ -142,6 +152,7 @@ enum pd_msg_request {
PD_MSG_NONE = 0,
PD_MSG_CTRL_REJECT,
PD_MSG_CTRL_WAIT,
+ PD_MSG_CTRL_NOT_SUPP,
PD_MSG_DATA_SINK_CAP,
PD_MSG_DATA_SOURCE_CAP,
};
@@ -158,13 +169,24 @@ enum pd_msg_request {
/* Alternate mode support */
#define SVID_DISCOVERY_MAX 16
+#define ALTMODE_DISCOVERY_MAX (SVID_DISCOVERY_MAX * MODE_DISCOVERY_MAX)
struct pd_mode_data {
int svid_index; /* current SVID index */
int nsvids;
u16 svids[SVID_DISCOVERY_MAX];
int altmodes; /* number of alternate modes */
- struct typec_altmode_desc altmode_desc[SVID_DISCOVERY_MAX];
+ struct typec_altmode_desc altmode_desc[ALTMODE_DISCOVERY_MAX];
+};
+
+struct pd_pps_data {
+ u32 min_volt;
+ u32 max_volt;
+ u32 max_curr;
+ u32 out_volt;
+ u32 op_curr;
+ bool supported;
+ bool active;
};
struct tcpm_port {
@@ -235,6 +257,7 @@ struct tcpm_port {
struct completion swap_complete;
int swap_status;
+ unsigned int negotiated_rev;
unsigned int message_id;
unsigned int caps_count;
unsigned int hard_reset_count;
@@ -257,15 +280,18 @@ struct tcpm_port {
u32 snk_vdo[VDO_MAX_OBJECTS];
unsigned int nr_snk_vdo;
- unsigned int max_snk_mv;
- unsigned int max_snk_ma;
- unsigned int max_snk_mw;
unsigned int operating_snk_mw;
+ bool update_sink_caps;
/* Requested current / voltage */
u32 current_limit;
u32 supply_voltage;
+ /* Used to export TA voltage and current */
+ struct power_supply *psy;
+ struct power_supply_desc psy_desc;
+ enum power_supply_usb_type usb_type;
+
u32 bist_request;
/* PD state for Vendor Defined Messages */
@@ -277,11 +303,16 @@ struct tcpm_port {
/* VDO to retry if UFP responder replied busy */
u32 vdo_retry;
- /* Alternate mode data */
+ /* PPS */
+ struct pd_pps_data pps_data;
+ struct completion pps_complete;
+ bool pps_pending;
+ int pps_status;
+ /* Alternate mode data */
struct pd_mode_data mode_data;
- struct typec_altmode *partner_altmode[SVID_DISCOVERY_MAX];
- struct typec_altmode *port_altmode[SVID_DISCOVERY_MAX];
+ struct typec_altmode *partner_altmode[ALTMODE_DISCOVERY_MAX];
+ struct typec_altmode *port_altmode[ALTMODE_DISCOVERY_MAX];
/* Deadline in jiffies to exit src_try_wait state */
unsigned long max_wait;
@@ -388,17 +419,18 @@ static void _tcpm_log(struct tcpm_port *port, const char *fmt, va_list args)
u64 ts_nsec = local_clock();
unsigned long rem_nsec;
+ mutex_lock(&port->logbuffer_lock);
if (!port->logbuffer[port->logbuffer_head]) {
port->logbuffer[port->logbuffer_head] =
kzalloc(LOG_BUFFER_ENTRY_SIZE, GFP_KERNEL);
- if (!port->logbuffer[port->logbuffer_head])
+ if (!port->logbuffer[port->logbuffer_head]) {
+ mutex_unlock(&port->logbuffer_lock);
return;
+ }
}
vsnprintf(tmpbuffer, sizeof(tmpbuffer), fmt, args);
- mutex_lock(&port->logbuffer_lock);
-
if (tcpm_log_full(port)) {
port->logbuffer_head = max(port->logbuffer_head - 1, 0);
strcpy(tmpbuffer, "overflow");
@@ -496,6 +528,16 @@ static void tcpm_log_source_caps(struct tcpm_port *port)
pdo_max_voltage(pdo),
pdo_max_power(pdo));
break;
+ case PDO_TYPE_APDO:
+ if (pdo_apdo_type(pdo) == APDO_TYPE_PPS)
+ scnprintf(msg, sizeof(msg),
+ "%u-%u mV, %u mA",
+ pdo_pps_apdo_min_voltage(pdo),
+ pdo_pps_apdo_max_voltage(pdo),
+ pdo_pps_apdo_max_current(pdo));
+ else
+ strcpy(msg, "undefined APDO");
+ break;
default:
strcpy(msg, "undefined");
break;
@@ -526,21 +568,16 @@ DEFINE_SHOW_ATTRIBUTE(tcpm_debug);
static struct dentry *rootdir;
-static int tcpm_debugfs_init(struct tcpm_port *port)
+static void tcpm_debugfs_init(struct tcpm_port *port)
{
mutex_init(&port->logbuffer_lock);
/* /sys/kernel/debug/tcpm/usbcX */
- if (!rootdir) {
+ if (!rootdir)
rootdir = debugfs_create_dir("tcpm", NULL);
- if (!rootdir)
- return -ENOMEM;
- }
port->dentry = debugfs_create_file(dev_name(port->dev),
S_IFREG | 0444, rootdir,
port, &tcpm_debug_fops);
-
- return 0;
}
static void tcpm_debugfs_exit(struct tcpm_port *port)
@@ -555,7 +592,7 @@ static void tcpm_log(const struct tcpm_port *port, const char *fmt, ...) { }
__printf(2, 3)
static void tcpm_log_force(struct tcpm_port *port, const char *fmt, ...) { }
static void tcpm_log_source_caps(struct tcpm_port *port) { }
-static int tcpm_debugfs_init(const struct tcpm_port *port) { return 0; }
+static void tcpm_debugfs_init(const struct tcpm_port *port) { }
static void tcpm_debugfs_exit(const struct tcpm_port *port) { }
#endif
@@ -605,14 +642,14 @@ void tcpm_pd_transmit_complete(struct tcpm_port *port,
}
EXPORT_SYMBOL_GPL(tcpm_pd_transmit_complete);
-static int tcpm_mux_set(struct tcpm_port *port, enum tcpc_mux_mode mode,
+static int tcpm_mux_set(struct tcpm_port *port, int state,
enum usb_role usb_role,
enum typec_orientation orientation)
{
int ret;
- tcpm_log(port, "Requesting mux mode %d, usb-role %d, orientation %d",
- mode, usb_role, orientation);
+ tcpm_log(port, "Requesting mux state %d, usb-role %d, orientation %d",
+ state, usb_role, orientation);
ret = typec_set_orientation(port->typec_port, orientation);
if (ret)
@@ -624,7 +661,7 @@ static int tcpm_mux_set(struct tcpm_port *port, enum tcpc_mux_mode mode,
return ret;
}
- return typec_set_mode(port->typec_port, mode);
+ return typec_set_mode(port->typec_port, state);
}
static int tcpm_set_polarity(struct tcpm_port *port,
@@ -689,6 +726,9 @@ static int tcpm_set_current_limit(struct tcpm_port *port, u32 max_ma, u32 mv)
tcpm_log(port, "Setting voltage/current limit %u mV %u mA", mv, max_ma);
+ port->supply_voltage = mv;
+ port->current_limit = max_ma;
+
if (port->tcpc->set_current_limit)
ret = port->tcpc->set_current_limit(port->tcpc, max_ma, mv);
@@ -751,7 +791,7 @@ static int tcpm_set_roles(struct tcpm_port *port, bool attached,
else
usb_role = USB_ROLE_DEVICE;
- ret = tcpm_mux_set(port, TYPEC_MUX_USB, usb_role, orientation);
+ ret = tcpm_mux_set(port, TYPEC_STATE_USB, usb_role, orientation);
if (ret < 0)
return ret;
@@ -793,11 +833,13 @@ static int tcpm_pd_send_source_caps(struct tcpm_port *port)
msg.header = PD_HEADER_LE(PD_CTRL_REJECT,
port->pwr_role,
port->data_role,
+ port->negotiated_rev,
port->message_id, 0);
} else {
msg.header = PD_HEADER_LE(PD_DATA_SOURCE_CAP,
port->pwr_role,
port->data_role,
+ port->negotiated_rev,
port->message_id,
port->nr_src_pdo);
}
@@ -818,11 +860,13 @@ static int tcpm_pd_send_sink_caps(struct tcpm_port *port)
msg.header = PD_HEADER_LE(PD_CTRL_REJECT,
port->pwr_role,
port->data_role,
+ port->negotiated_rev,
port->message_id, 0);
} else {
msg.header = PD_HEADER_LE(PD_DATA_SINK_CAP,
port->pwr_role,
port->data_role,
+ port->negotiated_rev,
port->message_id,
port->nr_snk_pdo);
}
@@ -955,7 +999,6 @@ static void svdm_consume_modes(struct tcpm_port *port, const __le32 *payload,
{
struct pd_mode_data *pmdata = &port->mode_data;
struct typec_altmode_desc *paltmode;
- struct typec_mode_desc *pmode;
int i;
if (pmdata->altmodes >= ARRAY_SIZE(port->partner_altmode)) {
@@ -963,32 +1006,36 @@ static void svdm_consume_modes(struct tcpm_port *port, const __le32 *payload,
return;
}
- paltmode = &pmdata->altmode_desc[pmdata->altmodes];
- memset(paltmode, 0, sizeof(*paltmode));
+ for (i = 1; i < cnt; i++) {
+ paltmode = &pmdata->altmode_desc[pmdata->altmodes];
+ memset(paltmode, 0, sizeof(*paltmode));
- paltmode->svid = pmdata->svids[pmdata->svid_index];
+ paltmode->svid = pmdata->svids[pmdata->svid_index];
+ paltmode->mode = i;
+ paltmode->vdo = le32_to_cpu(payload[i]);
- tcpm_log(port, " Alternate mode %d: SVID 0x%04x",
- pmdata->altmodes, paltmode->svid);
+ tcpm_log(port, " Alternate mode %d: SVID 0x%04x, VDO %d: 0x%08x",
+ pmdata->altmodes, paltmode->svid,
+ paltmode->mode, paltmode->vdo);
- for (i = 1; i < cnt && paltmode->n_modes < ALTMODE_MAX_MODES; i++) {
- pmode = &paltmode->modes[paltmode->n_modes];
- memset(pmode, 0, sizeof(*pmode));
- pmode->vdo = le32_to_cpu(payload[i]);
- pmode->index = i - 1;
- paltmode->n_modes++;
- tcpm_log(port, " VDO %d: 0x%08x",
- pmode->index, pmode->vdo);
+ pmdata->altmodes++;
}
- port->partner_altmode[pmdata->altmodes] =
- typec_partner_register_altmode(port->partner, paltmode);
- if (!port->partner_altmode[pmdata->altmodes]) {
- tcpm_log(port,
- "Failed to register alternate modes for SVID 0x%04x",
- paltmode->svid);
- return;
+}
+
+static void tcpm_register_partner_altmodes(struct tcpm_port *port)
+{
+ struct pd_mode_data *modep = &port->mode_data;
+ struct typec_altmode *altmode;
+ int i;
+
+ for (i = 0; i < modep->altmodes; i++) {
+ altmode = typec_partner_register_altmode(port->partner,
+ &modep->altmode_desc[i]);
+ if (!altmode)
+ tcpm_log(port, "Failed to register partner SVID 0x%04x",
+ modep->altmode_desc[i].svid);
+ port->partner_altmode[i] = altmode;
}
- pmdata->altmodes++;
}
#define supports_modal(port) PD_IDH_MODAL_SUPP((port)->partner_ident.id_header)
@@ -996,19 +1043,32 @@ static void svdm_consume_modes(struct tcpm_port *port, const __le32 *payload,
static int tcpm_pd_svdm(struct tcpm_port *port, const __le32 *payload, int cnt,
u32 *response)
{
- u32 p0 = le32_to_cpu(payload[0]);
- int cmd_type = PD_VDO_CMDT(p0);
- int cmd = PD_VDO_CMD(p0);
+ struct typec_altmode *adev;
+ struct typec_altmode *pdev;
struct pd_mode_data *modep;
+ u32 p[PD_MAX_PAYLOAD];
int rlen = 0;
- u16 svid;
+ int cmd_type;
+ int cmd;
int i;
+ for (i = 0; i < cnt; i++)
+ p[i] = le32_to_cpu(payload[i]);
+
+ cmd_type = PD_VDO_CMDT(p[0]);
+ cmd = PD_VDO_CMD(p[0]);
+
tcpm_log(port, "Rx VDM cmd 0x%x type %d cmd %d len %d",
- p0, cmd_type, cmd, cnt);
+ p[0], cmd_type, cmd, cnt);
modep = &port->mode_data;
+ adev = typec_match_altmode(port->port_altmode, ALTMODE_DISCOVERY_MAX,
+ PD_VDO_VID(p[0]), PD_VDO_OPOS(p[0]));
+
+ pdev = typec_match_altmode(port->partner_altmode, ALTMODE_DISCOVERY_MAX,
+ PD_VDO_VID(p[0]), PD_VDO_OPOS(p[0]));
+
switch (cmd_type) {
case CMDT_INIT:
switch (cmd) {
@@ -1030,17 +1090,19 @@ static int tcpm_pd_svdm(struct tcpm_port *port, const __le32 *payload, int cnt,
case CMD_EXIT_MODE:
break;
case CMD_ATTENTION:
- break;
+ /* Attention command does not have response */
+ typec_altmode_attention(adev, p[1]);
+ return 0;
default:
break;
}
if (rlen >= 1) {
- response[0] = p0 | VDO_CMDT(CMDT_RSP_ACK);
+ response[0] = p[0] | VDO_CMDT(CMDT_RSP_ACK);
} else if (rlen == 0) {
- response[0] = p0 | VDO_CMDT(CMDT_RSP_NAK);
+ response[0] = p[0] | VDO_CMDT(CMDT_RSP_NAK);
rlen = 1;
} else {
- response[0] = p0 | VDO_CMDT(CMDT_RSP_BUSY);
+ response[0] = p[0] | VDO_CMDT(CMDT_RSP_BUSY);
rlen = 1;
}
break;
@@ -1073,14 +1135,39 @@ static int tcpm_pd_svdm(struct tcpm_port *port, const __le32 *payload, int cnt,
svdm_consume_modes(port, payload, cnt);
modep->svid_index++;
if (modep->svid_index < modep->nsvids) {
- svid = modep->svids[modep->svid_index];
+ u16 svid = modep->svids[modep->svid_index];
response[0] = VDO(svid, 1, CMD_DISCOVER_MODES);
rlen = 1;
} else {
- /* enter alternate mode if/when implemented */
+ tcpm_register_partner_altmodes(port);
}
break;
case CMD_ENTER_MODE:
+ typec_altmode_update_active(pdev, true);
+
+ if (typec_altmode_vdm(adev, p[0], &p[1], cnt)) {
+ response[0] = VDO(adev->svid, 1, CMD_EXIT_MODE);
+ response[0] |= VDO_OPOS(adev->mode);
+ return 1;
+ }
+ return 0;
+ case CMD_EXIT_MODE:
+ typec_altmode_update_active(pdev, false);
+
+ /* Back to USB Operation */
+ WARN_ON(typec_altmode_notify(adev, TYPEC_STATE_USB,
+ NULL));
+ break;
+ default:
+ break;
+ }
+ break;
+ case CMDT_RSP_NAK:
+ switch (cmd) {
+ case CMD_ENTER_MODE:
+ /* Back to USB Operation */
+ WARN_ON(typec_altmode_notify(adev, TYPEC_STATE_USB,
+ NULL));
break;
default:
break;
@@ -1090,6 +1177,9 @@ static int tcpm_pd_svdm(struct tcpm_port *port, const __le32 *payload, int cnt,
break;
}
+ /* Informing the alternate mode drivers about everything */
+ typec_altmode_vdm(adev, p[0], &p[1], cnt);
+
return rlen;
}
@@ -1189,6 +1279,7 @@ static void vdm_run_state_machine(struct tcpm_port *port)
msg.header = PD_HEADER_LE(PD_DATA_VENDOR_DEF,
port->pwr_role,
port->data_role,
+ port->negotiated_rev,
port->message_id, port->vdo_count);
for (i = 0; i < port->vdo_count; i++)
msg.payload[i] = cpu_to_le32(port->vdo_data[i]);
@@ -1260,6 +1351,8 @@ enum pdo_err {
PDO_ERR_FIXED_NOT_SORTED,
PDO_ERR_VARIABLE_BATT_NOT_SORTED,
PDO_ERR_DUPE_PDO,
+ PDO_ERR_PPS_APDO_NOT_SORTED,
+ PDO_ERR_DUPE_PPS_APDO,
};
static const char * const pdo_err_msg[] = {
@@ -1275,6 +1368,10 @@ static const char * const pdo_err_msg[] = {
" err: Variable/Battery supply pdos should be in increasing order of their minimum voltage",
[PDO_ERR_DUPE_PDO] =
" err: Variable/Batt supply pdos cannot have same min/max voltage",
+ [PDO_ERR_PPS_APDO_NOT_SORTED] =
+ " err: Programmable power supply apdos should be in increasing order of their maximum voltage",
+ [PDO_ERR_DUPE_PPS_APDO] =
+ " err: Programmable power supply apdos cannot have same min/max voltage and max current",
};
static enum pdo_err tcpm_caps_err(struct tcpm_port *port, const u32 *pdo,
@@ -1324,6 +1421,26 @@ static enum pdo_err tcpm_caps_err(struct tcpm_port *port, const u32 *pdo,
pdo_min_voltage(pdo[i - 1])))
return PDO_ERR_DUPE_PDO;
break;
+ /*
+ * The Programmable Power Supply APDOs, if present,
+ * shall be sent in Maximum Voltage order;
+ * lowest to highest.
+ */
+ case PDO_TYPE_APDO:
+ if (pdo_apdo_type(pdo[i]) != APDO_TYPE_PPS)
+ break;
+
+ if (pdo_pps_apdo_max_voltage(pdo[i]) <
+ pdo_pps_apdo_max_voltage(pdo[i - 1]))
+ return PDO_ERR_PPS_APDO_NOT_SORTED;
+ else if (pdo_pps_apdo_min_voltage(pdo[i]) ==
+ pdo_pps_apdo_min_voltage(pdo[i - 1]) &&
+ pdo_pps_apdo_max_voltage(pdo[i]) ==
+ pdo_pps_apdo_max_voltage(pdo[i - 1]) &&
+ pdo_pps_apdo_max_current(pdo[i]) ==
+ pdo_pps_apdo_max_current(pdo[i - 1]))
+ return PDO_ERR_DUPE_PPS_APDO;
+ break;
default:
tcpm_log_force(port, " Unknown pdo type");
}
@@ -1346,14 +1463,102 @@ static int tcpm_validate_caps(struct tcpm_port *port, const u32 *pdo,
return 0;
}
+static int tcpm_altmode_enter(struct typec_altmode *altmode)
+{
+ struct tcpm_port *port = typec_altmode_get_drvdata(altmode);
+ u32 header;
+
+ mutex_lock(&port->lock);
+ header = VDO(altmode->svid, 1, CMD_ENTER_MODE);
+ header |= VDO_OPOS(altmode->mode);
+
+ tcpm_queue_vdm(port, header, NULL, 0);
+ mod_delayed_work(port->wq, &port->vdm_state_machine, 0);
+ mutex_unlock(&port->lock);
+
+ return 0;
+}
+
+static int tcpm_altmode_exit(struct typec_altmode *altmode)
+{
+ struct tcpm_port *port = typec_altmode_get_drvdata(altmode);
+ u32 header;
+
+ mutex_lock(&port->lock);
+ header = VDO(altmode->svid, 1, CMD_EXIT_MODE);
+ header |= VDO_OPOS(altmode->mode);
+
+ tcpm_queue_vdm(port, header, NULL, 0);
+ mod_delayed_work(port->wq, &port->vdm_state_machine, 0);
+ mutex_unlock(&port->lock);
+
+ return 0;
+}
+
+static int tcpm_altmode_vdm(struct typec_altmode *altmode,
+ u32 header, const u32 *data, int count)
+{
+ struct tcpm_port *port = typec_altmode_get_drvdata(altmode);
+
+ mutex_lock(&port->lock);
+ tcpm_queue_vdm(port, header, data, count - 1);
+ mod_delayed_work(port->wq, &port->vdm_state_machine, 0);
+ mutex_unlock(&port->lock);
+
+ return 0;
+}
+
+static const struct typec_altmode_ops tcpm_altmode_ops = {
+ .enter = tcpm_altmode_enter,
+ .exit = tcpm_altmode_exit,
+ .vdm = tcpm_altmode_vdm,
+};
+
/*
* PD (data, control) command handling functions
*/
+static inline enum tcpm_state ready_state(struct tcpm_port *port)
+{
+ if (port->pwr_role == TYPEC_SOURCE)
+ return SRC_READY;
+ else
+ return SNK_READY;
+}
+
+static int tcpm_pd_send_control(struct tcpm_port *port,
+ enum pd_ctrl_msg_type type);
+
+static void tcpm_handle_alert(struct tcpm_port *port, const __le32 *payload,
+ int cnt)
+{
+ u32 p0 = le32_to_cpu(payload[0]);
+ unsigned int type = usb_pd_ado_type(p0);
+
+ if (!type) {
+ tcpm_log(port, "Alert message received with no type");
+ return;
+ }
+
+ /* Just handling non-battery alerts for now */
+ if (!(type & USB_PD_ADO_TYPE_BATT_STATUS_CHANGE)) {
+ switch (port->state) {
+ case SRC_READY:
+ case SNK_READY:
+ tcpm_set_state(port, GET_STATUS_SEND, 0);
+ break;
+ default:
+ tcpm_queue_message(port, PD_MSG_CTRL_WAIT);
+ break;
+ }
+ }
+}
+
static void tcpm_pd_data_request(struct tcpm_port *port,
const struct pd_message *msg)
{
enum pd_data_msg_type type = pd_header_type_le(msg->header);
unsigned int cnt = pd_header_cnt_le(msg->header);
+ unsigned int rev = pd_header_rev_le(msg->header);
unsigned int i;
switch (type) {
@@ -1372,6 +1577,17 @@ static void tcpm_pd_data_request(struct tcpm_port *port,
port->nr_source_caps);
/*
+ * Adjust revision in subsequent message headers, as required,
+ * to comply with 6.2.1.1.5 of the USB PD 3.0 spec. We don't
+ * support Rev 1.0 so just do nothing in that scenario.
+ */
+ if (rev == PD_REV10)
+ break;
+
+ if (rev < PD_MAX_REV)
+ port->negotiated_rev = rev;
+
+ /*
* This message may be received even if VBUS is not
* present. This is quite unexpected; see USB PD
* specification, sections 8.3.3.6.3.1 and 8.3.3.6.3.2.
@@ -1392,6 +1608,20 @@ static void tcpm_pd_data_request(struct tcpm_port *port,
tcpm_queue_message(port, PD_MSG_CTRL_REJECT);
break;
}
+
+ /*
+ * Adjust revision in subsequent message headers, as required,
+ * to comply with 6.2.1.1.5 of the USB PD 3.0 spec. We don't
+ * support Rev 1.0 so just reject in that scenario.
+ */
+ if (rev == PD_REV10) {
+ tcpm_queue_message(port, PD_MSG_CTRL_REJECT);
+ break;
+ }
+
+ if (rev < PD_MAX_REV)
+ port->negotiated_rev = rev;
+
port->sink_request = le32_to_cpu(msg->payload[0]);
tcpm_set_state(port, SRC_NEGOTIATE_CAPABILITIES, 0);
break;
@@ -1410,12 +1640,29 @@ static void tcpm_pd_data_request(struct tcpm_port *port,
tcpm_set_state(port, BIST_RX, 0);
}
break;
+ case PD_DATA_ALERT:
+ tcpm_handle_alert(port, msg->payload, cnt);
+ break;
+ case PD_DATA_BATT_STATUS:
+ case PD_DATA_GET_COUNTRY_INFO:
+ /* Currently unsupported */
+ tcpm_queue_message(port, PD_MSG_CTRL_NOT_SUPP);
+ break;
default:
tcpm_log(port, "Unhandled data message type %#x", type);
break;
}
}
+static void tcpm_pps_complete(struct tcpm_port *port, int result)
+{
+ if (port->pps_pending) {
+ port->pps_status = result;
+ port->pps_pending = false;
+ complete(&port->pps_complete);
+ }
+}
+
static void tcpm_pd_ctrl_request(struct tcpm_port *port,
const struct pd_message *msg)
{
@@ -1483,6 +1730,7 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port,
break;
case PD_CTRL_REJECT:
case PD_CTRL_WAIT:
+ case PD_CTRL_NOT_SUPP:
switch (port->state) {
case SNK_NEGOTIATE_CAPABILITIES:
/* USB PD specification, Figure 8-43 */
@@ -1492,6 +1740,14 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port,
next_state = SNK_WAIT_CAPABILITIES;
tcpm_set_state(port, next_state, 0);
break;
+ case SNK_NEGOTIATE_PPS_CAPABILITIES:
+ /* Revert data back from any requested PPS updates */
+ port->pps_data.out_volt = port->supply_voltage;
+ port->pps_data.op_curr = port->current_limit;
+ port->pps_status = (type == PD_CTRL_WAIT ?
+ -EAGAIN : -EOPNOTSUPP);
+ tcpm_set_state(port, SNK_READY, 0);
+ break;
case DR_SWAP_SEND:
port->swap_status = (type == PD_CTRL_WAIT ?
-EAGAIN : -EOPNOTSUPP);
@@ -1514,6 +1770,13 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port,
case PD_CTRL_ACCEPT:
switch (port->state) {
case SNK_NEGOTIATE_CAPABILITIES:
+ port->pps_data.active = false;
+ tcpm_set_state(port, SNK_TRANSITION_SINK, 0);
+ break;
+ case SNK_NEGOTIATE_PPS_CAPABILITIES:
+ port->pps_data.active = true;
+ port->supply_voltage = port->pps_data.out_volt;
+ port->current_limit = port->pps_data.op_curr;
tcpm_set_state(port, SNK_TRANSITION_SINK, 0);
break;
case SOFT_RESET_SEND:
@@ -1587,12 +1850,75 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port,
break;
}
break;
+ case PD_CTRL_GET_SOURCE_CAP_EXT:
+ case PD_CTRL_GET_STATUS:
+ case PD_CTRL_FR_SWAP:
+ case PD_CTRL_GET_PPS_STATUS:
+ case PD_CTRL_GET_COUNTRY_CODES:
+ /* Currently not supported */
+ tcpm_queue_message(port, PD_MSG_CTRL_NOT_SUPP);
+ break;
default:
tcpm_log(port, "Unhandled ctrl message type %#x", type);
break;
}
}
+static void tcpm_pd_ext_msg_request(struct tcpm_port *port,
+ const struct pd_message *msg)
+{
+ enum pd_ext_msg_type type = pd_header_type_le(msg->header);
+ unsigned int data_size = pd_ext_header_data_size_le(msg->ext_msg.header);
+
+ if (!(msg->ext_msg.header & PD_EXT_HDR_CHUNKED)) {
+ tcpm_log(port, "Unchunked extended messages unsupported");
+ return;
+ }
+
+ if (data_size > PD_EXT_MAX_CHUNK_DATA) {
+ tcpm_log(port, "Chunk handling not yet supported");
+ return;
+ }
+
+ switch (type) {
+ case PD_EXT_STATUS:
+ /*
+ * If PPS related events raised then get PPS status to clear
+ * (see USB PD 3.0 Spec, 6.5.2.4)
+ */
+ if (msg->ext_msg.data[USB_PD_EXT_SDB_EVENT_FLAGS] &
+ USB_PD_EXT_SDB_PPS_EVENTS)
+ tcpm_set_state(port, GET_PPS_STATUS_SEND, 0);
+ else
+ tcpm_set_state(port, ready_state(port), 0);
+ break;
+ case PD_EXT_PPS_STATUS:
+ /*
+ * For now the PPS status message is used to clear events
+ * and nothing more.
+ */
+ tcpm_set_state(port, ready_state(port), 0);
+ break;
+ case PD_EXT_SOURCE_CAP_EXT:
+ case PD_EXT_GET_BATT_CAP:
+ case PD_EXT_GET_BATT_STATUS:
+ case PD_EXT_BATT_CAP:
+ case PD_EXT_GET_MANUFACTURER_INFO:
+ case PD_EXT_MANUFACTURER_INFO:
+ case PD_EXT_SECURITY_REQUEST:
+ case PD_EXT_SECURITY_RESPONSE:
+ case PD_EXT_FW_UPDATE_REQUEST:
+ case PD_EXT_FW_UPDATE_RESPONSE:
+ case PD_EXT_COUNTRY_INFO:
+ case PD_EXT_COUNTRY_CODES:
+ tcpm_queue_message(port, PD_MSG_CTRL_NOT_SUPP);
+ break;
+ default:
+ tcpm_log(port, "Unhandled extended message type %#x", type);
+ break;
+ }
+}
+
static void tcpm_pd_rx_handler(struct work_struct *work)
{
struct pd_rx_event *event = container_of(work,
@@ -1633,7 +1959,9 @@ static void tcpm_pd_rx_handler(struct work_struct *work)
"Data role mismatch, initiating error recovery");
tcpm_set_state(port, ERROR_RECOVERY, 0);
} else {
- if (cnt)
+ if (msg->header & PD_HEADER_EXT_HDR)
+ tcpm_pd_ext_msg_request(port, msg);
+ else if (cnt)
tcpm_pd_data_request(port, msg);
else
tcpm_pd_ctrl_request(port, msg);
@@ -1668,6 +1996,7 @@ static int tcpm_pd_send_control(struct tcpm_port *port,
memset(&msg, 0, sizeof(msg));
msg.header = PD_HEADER_LE(type, port->pwr_role,
port->data_role,
+ port->negotiated_rev,
port->message_id, 0);
return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg);
@@ -1693,6 +2022,9 @@ static bool tcpm_send_queued_message(struct tcpm_port *port)
case PD_MSG_CTRL_REJECT:
tcpm_pd_send_control(port, PD_CTRL_REJECT);
break;
+ case PD_MSG_CTRL_NOT_SUPP:
+ tcpm_pd_send_control(port, PD_CTRL_NOT_SUPP);
+ break;
case PD_MSG_DATA_SINK_CAP:
tcpm_pd_send_sink_caps(port);
break;
@@ -1772,84 +2104,252 @@ static int tcpm_pd_check_request(struct tcpm_port *port)
return 0;
}
-static int tcpm_pd_select_pdo(struct tcpm_port *port)
+#define min_power(x, y) min(pdo_max_power(x), pdo_max_power(y))
+#define min_current(x, y) min(pdo_max_current(x), pdo_max_current(y))
+
+static int tcpm_pd_select_pdo(struct tcpm_port *port, int *sink_pdo,
+ int *src_pdo)
{
- unsigned int i, max_mw = 0, max_mv = 0;
+ unsigned int i, j, max_src_mv = 0, min_src_mv = 0, max_mw = 0,
+ max_mv = 0, src_mw = 0, src_ma = 0, max_snk_mv = 0,
+ min_snk_mv = 0;
int ret = -EINVAL;
+ port->pps_data.supported = false;
+ port->usb_type = POWER_SUPPLY_USB_TYPE_PD;
+
/*
- * Select the source PDO providing the most power while staying within
- * the board's voltage limits. Prefer PDO providing exp
+ * Select the source PDO providing the most power which has a
+ * matchig sink cap.
*/
for (i = 0; i < port->nr_source_caps; i++) {
u32 pdo = port->source_caps[i];
enum pd_pdo_type type = pdo_type(pdo);
- unsigned int mv, ma, mw;
- if (type == PDO_TYPE_FIXED)
- mv = pdo_fixed_voltage(pdo);
- else
- mv = pdo_min_voltage(pdo);
+ switch (type) {
+ case PDO_TYPE_FIXED:
+ max_src_mv = pdo_fixed_voltage(pdo);
+ min_src_mv = max_src_mv;
+ break;
+ case PDO_TYPE_BATT:
+ case PDO_TYPE_VAR:
+ max_src_mv = pdo_max_voltage(pdo);
+ min_src_mv = pdo_min_voltage(pdo);
+ break;
+ 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;
+ }
+ continue;
+ default:
+ tcpm_log(port, "Invalid source PDO type, ignoring");
+ continue;
+ }
- if (type == PDO_TYPE_BATT) {
- mw = pdo_max_power(pdo);
- } else {
- ma = min(pdo_max_current(pdo),
- port->max_snk_ma);
- mw = ma * mv / 1000;
+ switch (type) {
+ case PDO_TYPE_FIXED:
+ case PDO_TYPE_VAR:
+ src_ma = pdo_max_current(pdo);
+ src_mw = src_ma * min_src_mv / 1000;
+ break;
+ case PDO_TYPE_BATT:
+ src_mw = pdo_max_power(pdo);
+ break;
+ case PDO_TYPE_APDO:
+ continue;
+ default:
+ tcpm_log(port, "Invalid source PDO type, ignoring");
+ continue;
}
- /* Perfer higher voltages if available */
- if ((mw > max_mw || (mw == max_mw && mv > max_mv)) &&
- mv <= port->max_snk_mv) {
- ret = i;
- max_mw = mw;
- max_mv = mv;
+ for (j = 0; j < port->nr_snk_pdo; j++) {
+ pdo = port->snk_pdo[j];
+
+ switch (pdo_type(pdo)) {
+ case PDO_TYPE_FIXED:
+ max_snk_mv = pdo_fixed_voltage(pdo);
+ min_snk_mv = max_snk_mv;
+ break;
+ case PDO_TYPE_BATT:
+ case PDO_TYPE_VAR:
+ max_snk_mv = pdo_max_voltage(pdo);
+ min_snk_mv = pdo_min_voltage(pdo);
+ break;
+ case PDO_TYPE_APDO:
+ continue;
+ default:
+ tcpm_log(port, "Invalid sink PDO type, ignoring");
+ continue;
+ }
+
+ if (max_src_mv <= max_snk_mv &&
+ min_src_mv >= min_snk_mv) {
+ /* Prefer higher voltages if available */
+ if ((src_mw == max_mw && min_src_mv > max_mv) ||
+ src_mw > max_mw) {
+ *src_pdo = i;
+ *sink_pdo = j;
+ max_mw = src_mw;
+ max_mv = min_src_mv;
+ ret = 0;
+ }
+ }
}
}
return ret;
}
+#define min_pps_apdo_current(x, y) \
+ min(pdo_pps_apdo_max_current(x), pdo_pps_apdo_max_current(y))
+
+static unsigned int tcpm_pd_select_pps_apdo(struct tcpm_port *port)
+{
+ unsigned int i, j, max_mw = 0, max_mv = 0;
+ unsigned int min_src_mv, max_src_mv, src_ma, src_mw;
+ unsigned int min_snk_mv, max_snk_mv;
+ u32 pdo;
+ unsigned int src_pdo = 0, snk_pdo = 0;
+
+ /*
+ * Select the source PPS APDO providing the most power while staying
+ * within the board's limits. We skip the first PDO as this is always
+ * 5V 3A.
+ */
+ for (i = 1; i < port->nr_source_caps; ++i) {
+ pdo = port->source_caps[i];
+
+ switch (pdo_type(pdo)) {
+ case PDO_TYPE_APDO:
+ if (pdo_apdo_type(pdo) != APDO_TYPE_PPS) {
+ tcpm_log(port, "Not PPS APDO (source), ignoring");
+ continue;
+ }
+
+ min_src_mv = pdo_pps_apdo_min_voltage(pdo);
+ max_src_mv = pdo_pps_apdo_max_voltage(pdo);
+ src_ma = pdo_pps_apdo_max_current(pdo);
+ src_mw = (src_ma * max_src_mv) / 1000;
+
+ /*
+ * Now search through the sink PDOs to find a matching
+ * PPS APDO. Again skip the first sink PDO as this will
+ * always be 5V 3A.
+ */
+ for (j = 1; j < port->nr_snk_pdo; j++) {
+ pdo = port->snk_pdo[j];
+
+ switch (pdo_type(pdo)) {
+ case PDO_TYPE_APDO:
+ if (pdo_apdo_type(pdo) != APDO_TYPE_PPS) {
+ tcpm_log(port,
+ "Not PPS APDO (sink), ignoring");
+ continue;
+ }
+
+ min_snk_mv =
+ pdo_pps_apdo_min_voltage(pdo);
+ max_snk_mv =
+ pdo_pps_apdo_max_voltage(pdo);
+ break;
+ default:
+ tcpm_log(port,
+ "Not APDO type (sink), ignoring");
+ continue;
+ }
+
+ if (max_src_mv <= max_snk_mv &&
+ min_src_mv >= min_snk_mv) {
+ /* Prefer higher voltages if available */
+ if ((src_mw == max_mw &&
+ min_src_mv > max_mv) ||
+ src_mw > max_mw) {
+ src_pdo = i;
+ snk_pdo = j;
+ max_mw = src_mw;
+ max_mv = max_src_mv;
+ }
+ }
+ }
+
+ break;
+ default:
+ tcpm_log(port, "Not APDO type (source), ignoring");
+ continue;
+ }
+ }
+
+ if (src_pdo) {
+ pdo = port->source_caps[src_pdo];
+
+ port->pps_data.min_volt = pdo_pps_apdo_min_voltage(pdo);
+ port->pps_data.max_volt = pdo_pps_apdo_max_voltage(pdo);
+ port->pps_data.max_curr =
+ min_pps_apdo_current(pdo, port->snk_pdo[snk_pdo]);
+ port->pps_data.out_volt =
+ min(pdo_pps_apdo_max_voltage(pdo), port->pps_data.out_volt);
+ port->pps_data.op_curr =
+ min(port->pps_data.max_curr, port->pps_data.op_curr);
+ }
+
+ return src_pdo;
+}
+
static int tcpm_pd_build_request(struct tcpm_port *port, u32 *rdo)
{
unsigned int mv, ma, mw, flags;
unsigned int max_ma, max_mw;
enum pd_pdo_type type;
- int index;
- u32 pdo;
+ u32 pdo, matching_snk_pdo;
+ int src_pdo_index = 0;
+ int snk_pdo_index = 0;
+ int ret;
- index = tcpm_pd_select_pdo(port);
- if (index < 0)
- return -EINVAL;
- pdo = port->source_caps[index];
+ ret = tcpm_pd_select_pdo(port, &snk_pdo_index, &src_pdo_index);
+ if (ret < 0)
+ return ret;
+
+ pdo = port->source_caps[src_pdo_index];
+ matching_snk_pdo = port->snk_pdo[snk_pdo_index];
type = pdo_type(pdo);
- if (type == PDO_TYPE_FIXED)
+ switch (type) {
+ case PDO_TYPE_FIXED:
mv = pdo_fixed_voltage(pdo);
- else
+ break;
+ case PDO_TYPE_BATT:
+ case PDO_TYPE_VAR:
mv = pdo_min_voltage(pdo);
+ break;
+ default:
+ tcpm_log(port, "Invalid PDO selected!");
+ return -EINVAL;
+ }
- /* Select maximum available current within the board's power limit */
+ /* Select maximum available current within the sink pdo's limit */
if (type == PDO_TYPE_BATT) {
- mw = pdo_max_power(pdo);
- ma = 1000 * min(mw, port->max_snk_mw) / mv;
+ mw = min_power(pdo, matching_snk_pdo);
+ ma = 1000 * mw / mv;
} else {
- ma = min(pdo_max_current(pdo),
- 1000 * port->max_snk_mw / mv);
+ ma = min_current(pdo, matching_snk_pdo);
+ mw = ma * mv / 1000;
}
- ma = min(ma, port->max_snk_ma);
flags = RDO_USB_COMM | RDO_NO_SUSPEND;
/* Set mismatch bit if offered power is less than operating power */
- mw = ma * mv / 1000;
max_ma = ma;
max_mw = mw;
if (mw < port->operating_snk_mw) {
flags |= RDO_CAP_MISMATCH;
- max_mw = port->operating_snk_mw;
- max_ma = max_mw * 1000 / mv;
+ if (type == PDO_TYPE_BATT &&
+ (pdo_max_power(matching_snk_pdo) > pdo_max_power(pdo)))
+ max_mw = pdo_max_power(matching_snk_pdo);
+ else if (pdo_max_current(matching_snk_pdo) >
+ pdo_max_current(pdo))
+ max_ma = pdo_max_current(matching_snk_pdo);
}
tcpm_log(port, "cc=%d cc1=%d cc2=%d vbus=%d vconn=%s polarity=%d",
@@ -1858,16 +2358,16 @@ static int tcpm_pd_build_request(struct tcpm_port *port, u32 *rdo)
port->polarity);
if (type == PDO_TYPE_BATT) {
- *rdo = RDO_BATT(index + 1, mw, max_mw, flags);
+ *rdo = RDO_BATT(src_pdo_index + 1, mw, max_mw, flags);
tcpm_log(port, "Requesting PDO %d: %u mV, %u mW%s",
- index, mv, mw,
+ src_pdo_index, mv, mw,
flags & RDO_CAP_MISMATCH ? " [mismatch]" : "");
} else {
- *rdo = RDO_FIXED(index + 1, ma, max_ma, flags);
+ *rdo = RDO_FIXED(src_pdo_index + 1, ma, max_ma, flags);
tcpm_log(port, "Requesting PDO %d: %u mV, %u mA%s",
- index, mv, ma,
+ src_pdo_index, mv, ma,
flags & RDO_CAP_MISMATCH ? " [mismatch]" : "");
}
@@ -1891,6 +2391,104 @@ static int tcpm_pd_send_request(struct tcpm_port *port)
msg.header = PD_HEADER_LE(PD_DATA_REQUEST,
port->pwr_role,
port->data_role,
+ port->negotiated_rev,
+ port->message_id, 1);
+ msg.payload[0] = cpu_to_le32(rdo);
+
+ return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg);
+}
+
+static int tcpm_pd_build_pps_request(struct tcpm_port *port, u32 *rdo)
+{
+ unsigned int out_mv, op_ma, op_mw, max_mv, max_ma, flags;
+ enum pd_pdo_type type;
+ unsigned int src_pdo_index;
+ u32 pdo;
+
+ src_pdo_index = tcpm_pd_select_pps_apdo(port);
+ if (!src_pdo_index)
+ return -EOPNOTSUPP;
+
+ pdo = port->source_caps[src_pdo_index];
+ type = pdo_type(pdo);
+
+ switch (type) {
+ case PDO_TYPE_APDO:
+ if (pdo_apdo_type(pdo) != APDO_TYPE_PPS) {
+ tcpm_log(port, "Invalid APDO selected!");
+ return -EINVAL;
+ }
+ max_mv = port->pps_data.max_volt;
+ max_ma = port->pps_data.max_curr;
+ out_mv = port->pps_data.out_volt;
+ op_ma = port->pps_data.op_curr;
+ break;
+ default:
+ tcpm_log(port, "Invalid PDO selected!");
+ return -EINVAL;
+ }
+
+ flags = RDO_USB_COMM | RDO_NO_SUSPEND;
+
+ op_mw = (op_ma * out_mv) / 1000;
+ if (op_mw < port->operating_snk_mw) {
+ /*
+ * Try raising current to meet power needs. If that's not enough
+ * then try upping the voltage. If that's still not enough
+ * then we've obviously chosen a PPS APDO which really isn't
+ * suitable so abandon ship.
+ */
+ op_ma = (port->operating_snk_mw * 1000) / out_mv;
+ if ((port->operating_snk_mw * 1000) % out_mv)
+ ++op_ma;
+ op_ma += RDO_PROG_CURR_MA_STEP - (op_ma % RDO_PROG_CURR_MA_STEP);
+
+ if (op_ma > max_ma) {
+ op_ma = max_ma;
+ out_mv = (port->operating_snk_mw * 1000) / op_ma;
+ if ((port->operating_snk_mw * 1000) % op_ma)
+ ++out_mv;
+ out_mv += RDO_PROG_VOLT_MV_STEP -
+ (out_mv % RDO_PROG_VOLT_MV_STEP);
+
+ if (out_mv > max_mv) {
+ tcpm_log(port, "Invalid PPS APDO selected!");
+ return -EINVAL;
+ }
+ }
+ }
+
+ tcpm_log(port, "cc=%d cc1=%d cc2=%d vbus=%d vconn=%s polarity=%d",
+ port->cc_req, port->cc1, port->cc2, port->vbus_source,
+ port->vconn_role == TYPEC_SOURCE ? "source" : "sink",
+ port->polarity);
+
+ *rdo = RDO_PROG(src_pdo_index + 1, out_mv, op_ma, flags);
+
+ tcpm_log(port, "Requesting APDO %d: %u mV, %u mA",
+ src_pdo_index, out_mv, op_ma);
+
+ port->pps_data.op_curr = op_ma;
+ port->pps_data.out_volt = out_mv;
+
+ return 0;
+}
+
+static int tcpm_pd_send_pps_request(struct tcpm_port *port)
+{
+ struct pd_message msg;
+ int ret;
+ u32 rdo;
+
+ ret = tcpm_pd_build_pps_request(port, &rdo);
+ if (ret < 0)
+ return ret;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.header = PD_HEADER_LE(PD_DATA_REQUEST,
+ port->pwr_role,
+ port->data_role,
+ port->negotiated_rev,
port->message_id, 1);
msg.payload[0] = cpu_to_le32(rdo);
@@ -1932,15 +2530,15 @@ static int tcpm_set_charge(struct tcpm_port *port, bool charge)
return 0;
}
-static bool tcpm_start_drp_toggling(struct tcpm_port *port)
+static bool tcpm_start_drp_toggling(struct tcpm_port *port,
+ enum typec_cc_status cc)
{
int ret;
if (port->tcpc->start_drp_toggling &&
port->port_type == TYPEC_PORT_DRP) {
tcpm_log_force(port, "Start DRP toggling");
- ret = port->tcpc->start_drp_toggling(port->tcpc,
- tcpm_rp_cc(port));
+ ret = port->tcpc->start_drp_toggling(port->tcpc, cc);
if (!ret)
return true;
}
@@ -2044,7 +2642,7 @@ out_disable_vconn:
out_disable_pd:
port->tcpc->set_pd_rx(port->tcpc, false);
out_disable_mux:
- tcpm_mux_set(port, TYPEC_MUX_NONE, USB_ROLE_NONE,
+ tcpm_mux_set(port, TYPEC_STATE_SAFE, USB_ROLE_NONE,
TYPEC_ORIENTATION_NONE);
return ret;
}
@@ -2077,6 +2675,7 @@ static void tcpm_reset_port(struct tcpm_port *port)
tcpm_typec_disconnect(port);
port->attached = false;
port->pd_capable = false;
+ port->pps_data.supported = false;
/*
* First Rx ID should be 0; set this to a sentinel of -1 so that
@@ -2089,11 +2688,14 @@ static void tcpm_reset_port(struct tcpm_port *port)
tcpm_init_vconn(port);
tcpm_set_current_limit(port, 0, 0);
tcpm_set_polarity(port, TYPEC_POLARITY_CC1);
- tcpm_mux_set(port, TYPEC_MUX_NONE, USB_ROLE_NONE,
+ tcpm_mux_set(port, TYPEC_STATE_SAFE, USB_ROLE_NONE,
TYPEC_ORIENTATION_NONE);
tcpm_set_attached_state(port, false);
port->try_src_count = 0;
port->try_snk_count = 0;
+ port->usb_type = POWER_SUPPLY_USB_TYPE_C;
+
+ power_supply_changed(port->psy);
}
static void tcpm_detach(struct tcpm_port *port)
@@ -2181,14 +2783,6 @@ static inline enum tcpm_state hard_reset_state(struct tcpm_port *port)
return SNK_UNATTACHED;
}
-static inline enum tcpm_state ready_state(struct tcpm_port *port)
-{
- if (port->pwr_role == TYPEC_SOURCE)
- return SRC_READY;
- else
- return SNK_READY;
-}
-
static inline enum tcpm_state unattached_state(struct tcpm_port *port)
{
if (port->port_type == TYPEC_PORT_DRP) {
@@ -2250,7 +2844,7 @@ static void run_state_machine(struct tcpm_port *port)
if (!port->non_pd_role_swap)
tcpm_swap_complete(port, -ENOTCONN);
tcpm_src_detach(port);
- if (tcpm_start_drp_toggling(port)) {
+ if (tcpm_start_drp_toggling(port, tcpm_rp_cc(port))) {
tcpm_set_state(port, DRP_TOGGLING, 0);
break;
}
@@ -2338,6 +2932,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->message_id = 0;
port->rx_msgid = -1;
port->explicit_contract = false;
@@ -2398,6 +2993,7 @@ static void run_state_machine(struct tcpm_port *port)
tcpm_swap_complete(port, 0);
tcpm_typec_connect(port);
+
tcpm_check_send_discover(port);
/*
* 6.3.5
@@ -2421,8 +3017,9 @@ 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_snk_detach(port);
- if (tcpm_start_drp_toggling(port)) {
+ if (tcpm_start_drp_toggling(port, TYPEC_CC_RD)) {
tcpm_set_state(port, DRP_TOGGLING, 0);
break;
}
@@ -2510,6 +3107,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->message_id = 0;
port->rx_msgid = -1;
port->explicit_contract = false;
@@ -2542,7 +3140,8 @@ static void run_state_machine(struct tcpm_port *port)
tcpm_port_is_sink(port) &&
time_is_after_jiffies(port->delayed_runtime)) {
tcpm_set_state(port, SNK_DISCOVERY,
- port->delayed_runtime - jiffies);
+ jiffies_to_msecs(port->delayed_runtime -
+ jiffies));
break;
}
tcpm_set_state(port, unattached_state(port), 0);
@@ -2580,6 +3179,24 @@ static void run_state_machine(struct tcpm_port *port)
PD_T_SENDER_RESPONSE);
}
break;
+ case SNK_NEGOTIATE_PPS_CAPABILITIES:
+ ret = tcpm_pd_send_pps_request(port);
+ if (ret < 0) {
+ port->pps_status = ret;
+ /*
+ * If this was called due to updates to sink
+ * capabilities, and pps is no longer valid, we should
+ * safely fall back to a standard PDO.
+ */
+ if (port->update_sink_caps)
+ tcpm_set_state(port, SNK_NEGOTIATE_CAPABILITIES, 0);
+ else
+ tcpm_set_state(port, SNK_READY, 0);
+ } else {
+ tcpm_set_state_cond(port, hard_reset_state(port),
+ PD_T_SENDER_RESPONSE);
+ }
+ break;
case SNK_TRANSITION_SINK:
case SNK_TRANSITION_SINK_VBUS:
tcpm_set_state(port, hard_reset_state(port),
@@ -2587,6 +3204,7 @@ static void run_state_machine(struct tcpm_port *port)
break;
case SNK_READY:
port->try_snk_count = 0;
+ port->update_sink_caps = false;
if (port->explicit_contract) {
typec_set_pwr_opmode(port->typec_port,
TYPEC_PWR_MODE_PD);
@@ -2596,6 +3214,10 @@ static void run_state_machine(struct tcpm_port *port)
tcpm_swap_complete(port, 0);
tcpm_typec_connect(port);
tcpm_check_send_discover(port);
+ tcpm_pps_complete(port, port->pps_status);
+
+ power_supply_changed(port->psy);
+
break;
/* Accessory states */
@@ -2642,6 +3264,7 @@ static void run_state_machine(struct tcpm_port *port)
tcpm_set_state(port, SRC_UNATTACHED, PD_T_PS_SOURCE_ON);
break;
case SNK_HARD_RESET_SINK_OFF:
+ memset(&port->pps_data, 0, sizeof(port->pps_data));
tcpm_set_vconn(port, false);
tcpm_set_charge(port, false);
tcpm_set_roles(port, false, TYPEC_SINK, TYPEC_DEVICE);
@@ -2860,8 +3483,25 @@ static void run_state_machine(struct tcpm_port *port)
/* Always switch to unattached state */
tcpm_set_state(port, unattached_state(port), 0);
break;
+ case GET_STATUS_SEND:
+ tcpm_pd_send_control(port, PD_CTRL_GET_STATUS);
+ tcpm_set_state(port, GET_STATUS_SEND_TIMEOUT,
+ PD_T_SENDER_RESPONSE);
+ break;
+ case GET_STATUS_SEND_TIMEOUT:
+ tcpm_set_state(port, ready_state(port), 0);
+ break;
+ case GET_PPS_STATUS_SEND:
+ tcpm_pd_send_control(port, PD_CTRL_GET_PPS_STATUS);
+ tcpm_set_state(port, GET_PPS_STATUS_SEND_TIMEOUT,
+ PD_T_SENDER_RESPONSE);
+ break;
+ case GET_PPS_STATUS_SEND_TIMEOUT:
+ tcpm_set_state(port, ready_state(port), 0);
+ break;
case ERROR_RECOVERY:
tcpm_swap_complete(port, -EPROTO);
+ tcpm_pps_complete(port, -EPROTO);
tcpm_set_state(port, PORT_RESET, 0);
break;
case PORT_RESET:
@@ -3444,6 +4084,168 @@ static int tcpm_try_role(const struct typec_capability *cap, int role)
return ret;
}
+static int tcpm_pps_set_op_curr(struct tcpm_port *port, u16 op_curr)
+{
+ unsigned int target_mw;
+ int ret;
+
+ mutex_lock(&port->swap_lock);
+ mutex_lock(&port->lock);
+
+ if (!port->pps_data.active) {
+ ret = -EOPNOTSUPP;
+ goto port_unlock;
+ }
+
+ if (port->state != SNK_READY) {
+ ret = -EAGAIN;
+ goto port_unlock;
+ }
+
+ if (op_curr > port->pps_data.max_curr) {
+ ret = -EINVAL;
+ goto port_unlock;
+ }
+
+ target_mw = (op_curr * port->pps_data.out_volt) / 1000;
+ if (target_mw < port->operating_snk_mw) {
+ ret = -EINVAL;
+ goto port_unlock;
+ }
+
+ /* Round down operating current to align with PPS valid steps */
+ op_curr = op_curr - (op_curr % RDO_PROG_CURR_MA_STEP);
+
+ reinit_completion(&port->pps_complete);
+ port->pps_data.op_curr = op_curr;
+ port->pps_status = 0;
+ port->pps_pending = true;
+ tcpm_set_state(port, SNK_NEGOTIATE_PPS_CAPABILITIES, 0);
+ mutex_unlock(&port->lock);
+
+ if (!wait_for_completion_timeout(&port->pps_complete,
+ msecs_to_jiffies(PD_PPS_CTRL_TIMEOUT)))
+ ret = -ETIMEDOUT;
+ else
+ ret = port->pps_status;
+
+ goto swap_unlock;
+
+port_unlock:
+ mutex_unlock(&port->lock);
+swap_unlock:
+ mutex_unlock(&port->swap_lock);
+
+ return ret;
+}
+
+static int tcpm_pps_set_out_volt(struct tcpm_port *port, u16 out_volt)
+{
+ unsigned int target_mw;
+ int ret;
+
+ mutex_lock(&port->swap_lock);
+ mutex_lock(&port->lock);
+
+ if (!port->pps_data.active) {
+ ret = -EOPNOTSUPP;
+ goto port_unlock;
+ }
+
+ if (port->state != SNK_READY) {
+ ret = -EAGAIN;
+ goto port_unlock;
+ }
+
+ if (out_volt < port->pps_data.min_volt ||
+ out_volt > port->pps_data.max_volt) {
+ ret = -EINVAL;
+ goto port_unlock;
+ }
+
+ target_mw = (port->pps_data.op_curr * out_volt) / 1000;
+ if (target_mw < port->operating_snk_mw) {
+ ret = -EINVAL;
+ goto port_unlock;
+ }
+
+ /* Round down output voltage to align with PPS valid steps */
+ out_volt = out_volt - (out_volt % RDO_PROG_VOLT_MV_STEP);
+
+ reinit_completion(&port->pps_complete);
+ port->pps_data.out_volt = out_volt;
+ port->pps_status = 0;
+ port->pps_pending = true;
+ tcpm_set_state(port, SNK_NEGOTIATE_PPS_CAPABILITIES, 0);
+ mutex_unlock(&port->lock);
+
+ if (!wait_for_completion_timeout(&port->pps_complete,
+ msecs_to_jiffies(PD_PPS_CTRL_TIMEOUT)))
+ ret = -ETIMEDOUT;
+ else
+ ret = port->pps_status;
+
+ goto swap_unlock;
+
+port_unlock:
+ mutex_unlock(&port->lock);
+swap_unlock:
+ mutex_unlock(&port->swap_lock);
+
+ return ret;
+}
+
+static int tcpm_pps_activate(struct tcpm_port *port, bool activate)
+{
+ int ret = 0;
+
+ mutex_lock(&port->swap_lock);
+ mutex_lock(&port->lock);
+
+ if (!port->pps_data.supported) {
+ ret = -EOPNOTSUPP;
+ goto port_unlock;
+ }
+
+ /* Trying to deactivate PPS when already deactivated so just bail */
+ if (!port->pps_data.active && !activate)
+ goto port_unlock;
+
+ if (port->state != SNK_READY) {
+ ret = -EAGAIN;
+ goto port_unlock;
+ }
+
+ reinit_completion(&port->pps_complete);
+ port->pps_status = 0;
+ port->pps_pending = true;
+
+ /* Trigger PPS request or move back to standard PDO contract */
+ if (activate) {
+ port->pps_data.out_volt = port->supply_voltage;
+ port->pps_data.op_curr = port->current_limit;
+ tcpm_set_state(port, SNK_NEGOTIATE_PPS_CAPABILITIES, 0);
+ } else {
+ tcpm_set_state(port, SNK_NEGOTIATE_CAPABILITIES, 0);
+ }
+ mutex_unlock(&port->lock);
+
+ if (!wait_for_completion_timeout(&port->pps_complete,
+ msecs_to_jiffies(PD_PPS_CTRL_TIMEOUT)))
+ ret = -ETIMEDOUT;
+ else
+ ret = port->pps_status;
+
+ goto swap_unlock;
+
+port_unlock:
+ mutex_unlock(&port->lock);
+swap_unlock:
+ mutex_unlock(&port->swap_lock);
+
+ return ret;
+}
+
static void tcpm_init(struct tcpm_port *port)
{
enum typec_cc_status cc1, cc2;
@@ -3538,6 +4340,81 @@ static int tcpm_copy_vdos(u32 *dest_vdo, const u32 *src_vdo,
return nr_vdo;
}
+static int tcpm_fw_get_caps(struct tcpm_port *port,
+ struct fwnode_handle *fwnode)
+{
+ const char *cap_str;
+ int ret;
+ u32 mw;
+
+ if (!fwnode)
+ return -EINVAL;
+
+ /* USB data support is optional */
+ ret = fwnode_property_read_string(fwnode, "data-role", &cap_str);
+ if (ret == 0) {
+ port->typec_caps.data = typec_find_port_data_role(cap_str);
+ if (port->typec_caps.data < 0)
+ return -EINVAL;
+ }
+
+ ret = fwnode_property_read_string(fwnode, "power-role", &cap_str);
+ if (ret < 0)
+ return ret;
+
+ port->typec_caps.type = typec_find_port_power_role(cap_str);
+ if (port->typec_caps.type < 0)
+ return -EINVAL;
+ port->port_type = port->typec_caps.type;
+
+ if (port->port_type == TYPEC_PORT_SNK)
+ goto sink;
+
+ /* Get source pdos */
+ ret = fwnode_property_read_u32_array(fwnode, "source-pdos",
+ NULL, 0);
+ if (ret <= 0)
+ return -EINVAL;
+
+ port->nr_src_pdo = min(ret, PDO_MAX_OBJECTS);
+ ret = fwnode_property_read_u32_array(fwnode, "source-pdos",
+ port->src_pdo, port->nr_src_pdo);
+ if ((ret < 0) || tcpm_validate_caps(port, port->src_pdo,
+ port->nr_src_pdo))
+ return -EINVAL;
+
+ if (port->port_type == TYPEC_PORT_SRC)
+ return 0;
+
+ /* Get the preferred power role for DRP */
+ ret = fwnode_property_read_string(fwnode, "try-power-role", &cap_str);
+ if (ret < 0)
+ return ret;
+
+ port->typec_caps.prefer_role = typec_find_power_role(cap_str);
+ if (port->typec_caps.prefer_role < 0)
+ return -EINVAL;
+sink:
+ /* Get sink pdos */
+ ret = fwnode_property_read_u32_array(fwnode, "sink-pdos",
+ NULL, 0);
+ if (ret <= 0)
+ return -EINVAL;
+
+ port->nr_snk_pdo = min(ret, PDO_MAX_OBJECTS);
+ ret = fwnode_property_read_u32_array(fwnode, "sink-pdos",
+ port->snk_pdo, port->nr_snk_pdo);
+ if ((ret < 0) || tcpm_validate_caps(port, port->snk_pdo,
+ port->nr_snk_pdo))
+ return -EINVAL;
+
+ if (fwnode_property_read_u32(fwnode, "op-sink-microwatt", &mw) < 0)
+ return -EINVAL;
+ port->operating_snk_mw = mw / 1000;
+
+ return 0;
+}
+
int tcpm_update_source_capabilities(struct tcpm_port *port, const u32 *pdo,
unsigned int nr_pdo)
{
@@ -3569,9 +4446,6 @@ EXPORT_SYMBOL_GPL(tcpm_update_source_capabilities);
int tcpm_update_sink_capabilities(struct tcpm_port *port, const u32 *pdo,
unsigned int nr_pdo,
- unsigned int max_snk_mv,
- unsigned int max_snk_ma,
- unsigned int max_snk_mw,
unsigned int operating_snk_mw)
{
if (tcpm_validate_caps(port, pdo, nr_pdo))
@@ -3579,17 +4453,19 @@ int tcpm_update_sink_capabilities(struct tcpm_port *port, const u32 *pdo,
mutex_lock(&port->lock);
port->nr_snk_pdo = tcpm_copy_pdos(port->snk_pdo, pdo, nr_pdo);
- port->max_snk_mv = max_snk_mv;
- port->max_snk_ma = max_snk_ma;
- port->max_snk_mw = max_snk_mw;
port->operating_snk_mw = operating_snk_mw;
+ port->update_sink_caps = true;
switch (port->state) {
case SNK_NEGOTIATE_CAPABILITIES:
+ case SNK_NEGOTIATE_PPS_CAPABILITIES:
case SNK_READY:
case SNK_TRANSITION_SINK:
case SNK_TRANSITION_SINK_VBUS:
- tcpm_set_state(port, SNK_NEGOTIATE_CAPABILITIES, 0);
+ if (port->pps_data.active)
+ tcpm_set_state(port, SNK_NEGOTIATE_PPS_CAPABILITIES, 0);
+ else
+ tcpm_set_state(port, SNK_NEGOTIATE_CAPABILITIES, 0);
break;
default:
break;
@@ -3599,12 +4475,261 @@ int tcpm_update_sink_capabilities(struct tcpm_port *port, const u32 *pdo,
}
EXPORT_SYMBOL_GPL(tcpm_update_sink_capabilities);
+/* Power Supply access to expose source power information */
+enum tcpm_psy_online_states {
+ TCPM_PSY_OFFLINE = 0,
+ TCPM_PSY_FIXED_ONLINE,
+ TCPM_PSY_PROG_ONLINE,
+};
+
+static enum power_supply_property tcpm_psy_props[] = {
+ POWER_SUPPLY_PROP_USB_TYPE,
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_VOLTAGE_MIN,
+ POWER_SUPPLY_PROP_VOLTAGE_MAX,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_MAX,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+};
+
+static int tcpm_psy_get_online(struct tcpm_port *port,
+ union power_supply_propval *val)
+{
+ if (port->vbus_charge) {
+ if (port->pps_data.active)
+ val->intval = TCPM_PSY_PROG_ONLINE;
+ else
+ val->intval = TCPM_PSY_FIXED_ONLINE;
+ } else {
+ val->intval = TCPM_PSY_OFFLINE;
+ }
+
+ return 0;
+}
+
+static int tcpm_psy_get_voltage_min(struct tcpm_port *port,
+ union power_supply_propval *val)
+{
+ if (port->pps_data.active)
+ val->intval = port->pps_data.min_volt * 1000;
+ else
+ val->intval = port->supply_voltage * 1000;
+
+ return 0;
+}
+
+static int tcpm_psy_get_voltage_max(struct tcpm_port *port,
+ union power_supply_propval *val)
+{
+ if (port->pps_data.active)
+ val->intval = port->pps_data.max_volt * 1000;
+ else
+ val->intval = port->supply_voltage * 1000;
+
+ return 0;
+}
+
+static int tcpm_psy_get_voltage_now(struct tcpm_port *port,
+ union power_supply_propval *val)
+{
+ val->intval = port->supply_voltage * 1000;
+
+ return 0;
+}
+
+static int tcpm_psy_get_current_max(struct tcpm_port *port,
+ union power_supply_propval *val)
+{
+ if (port->pps_data.active)
+ val->intval = port->pps_data.max_curr * 1000;
+ else
+ val->intval = port->current_limit * 1000;
+
+ return 0;
+}
+
+static int tcpm_psy_get_current_now(struct tcpm_port *port,
+ union power_supply_propval *val)
+{
+ val->intval = port->current_limit * 1000;
+
+ return 0;
+}
+
+static int tcpm_psy_get_prop(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct tcpm_port *port = power_supply_get_drvdata(psy);
+ int ret = 0;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_USB_TYPE:
+ val->intval = port->usb_type;
+ break;
+ case POWER_SUPPLY_PROP_ONLINE:
+ ret = tcpm_psy_get_online(port, val);
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MIN:
+ ret = tcpm_psy_get_voltage_min(port, val);
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+ ret = tcpm_psy_get_voltage_max(port, val);
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ ret = tcpm_psy_get_voltage_now(port, val);
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_MAX:
+ ret = tcpm_psy_get_current_max(port, val);
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ ret = tcpm_psy_get_current_now(port, val);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int tcpm_psy_set_online(struct tcpm_port *port,
+ const union power_supply_propval *val)
+{
+ int ret;
+
+ switch (val->intval) {
+ case TCPM_PSY_FIXED_ONLINE:
+ ret = tcpm_pps_activate(port, false);
+ break;
+ case TCPM_PSY_PROG_ONLINE:
+ ret = tcpm_pps_activate(port, true);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int tcpm_psy_set_prop(struct power_supply *psy,
+ enum power_supply_property psp,
+ const union power_supply_propval *val)
+{
+ struct tcpm_port *port = power_supply_get_drvdata(psy);
+ int ret;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_ONLINE:
+ ret = tcpm_psy_set_online(port, val);
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ if (val->intval < port->pps_data.min_volt * 1000 ||
+ val->intval > port->pps_data.max_volt * 1000)
+ ret = -EINVAL;
+ else
+ ret = tcpm_pps_set_out_volt(port, val->intval / 1000);
+ 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);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int tcpm_psy_prop_writeable(struct power_supply *psy,
+ enum power_supply_property psp)
+{
+ switch (psp) {
+ case POWER_SUPPLY_PROP_ONLINE:
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static enum power_supply_usb_type tcpm_psy_usb_types[] = {
+ POWER_SUPPLY_USB_TYPE_C,
+ POWER_SUPPLY_USB_TYPE_PD,
+ POWER_SUPPLY_USB_TYPE_PD_PPS,
+};
+
+static const char *tcpm_psy_name_prefix = "tcpm-source-psy-";
+
+static int devm_tcpm_psy_register(struct tcpm_port *port)
+{
+ struct power_supply_config psy_cfg = {};
+ const char *port_dev_name = dev_name(port->dev);
+ size_t psy_name_len = strlen(tcpm_psy_name_prefix) +
+ strlen(port_dev_name) + 1;
+ char *psy_name;
+
+ psy_cfg.drv_data = port;
+ psy_cfg.fwnode = dev_fwnode(port->dev);
+ psy_name = devm_kzalloc(port->dev, psy_name_len, GFP_KERNEL);
+ if (!psy_name)
+ return -ENOMEM;
+
+ snprintf(psy_name, psy_name_len, "%s%s", tcpm_psy_name_prefix,
+ port_dev_name);
+ port->psy_desc.name = psy_name;
+ port->psy_desc.type = POWER_SUPPLY_TYPE_USB,
+ port->psy_desc.usb_types = tcpm_psy_usb_types;
+ port->psy_desc.num_usb_types = ARRAY_SIZE(tcpm_psy_usb_types);
+ 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,
+ port->psy_desc.set_property = tcpm_psy_set_prop,
+ port->psy_desc.property_is_writeable = tcpm_psy_prop_writeable,
+
+ port->usb_type = POWER_SUPPLY_USB_TYPE_C;
+
+ port->psy = devm_power_supply_register(port->dev, &port->psy_desc,
+ &psy_cfg);
+
+ return PTR_ERR_OR_ZERO(port->psy);
+}
+
+static int tcpm_copy_caps(struct tcpm_port *port,
+ const struct tcpc_config *tcfg)
+{
+ if (tcpm_validate_caps(port, tcfg->src_pdo, tcfg->nr_src_pdo) ||
+ tcpm_validate_caps(port, tcfg->snk_pdo, tcfg->nr_snk_pdo))
+ return -EINVAL;
+
+ port->nr_src_pdo = tcpm_copy_pdos(port->src_pdo, tcfg->src_pdo,
+ tcfg->nr_src_pdo);
+ port->nr_snk_pdo = tcpm_copy_pdos(port->snk_pdo, tcfg->snk_pdo,
+ tcfg->nr_snk_pdo);
+
+ port->nr_snk_vdo = tcpm_copy_vdos(port->snk_vdo, tcfg->snk_vdo,
+ tcfg->nr_snk_vdo);
+
+ port->operating_snk_mw = tcfg->operating_snk_mw;
+
+ port->typec_caps.prefer_role = tcfg->default_role;
+ port->typec_caps.type = tcfg->type;
+ port->typec_caps.data = tcfg->data;
+
+ return 0;
+}
+
struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
{
struct tcpm_port *port;
int i, err;
- if (!dev || !tcpc || !tcpc->config ||
+ if (!dev || !tcpc ||
!tcpc->get_vbus || !tcpc->set_cc || !tcpc->get_cc ||
!tcpc->set_polarity || !tcpc->set_vconn || !tcpc->set_vbus ||
!tcpc->set_pd_rx || !tcpc->set_roles || !tcpc->pd_transmit)
@@ -3631,36 +4756,23 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
init_completion(&port->tx_complete);
init_completion(&port->swap_complete);
+ init_completion(&port->pps_complete);
tcpm_debugfs_init(port);
- if (tcpm_validate_caps(port, tcpc->config->src_pdo,
- tcpc->config->nr_src_pdo) ||
- tcpm_validate_caps(port, tcpc->config->snk_pdo,
- tcpc->config->nr_snk_pdo)) {
- err = -EINVAL;
+ err = tcpm_fw_get_caps(port, tcpc->fwnode);
+ if ((err < 0) && tcpc->config)
+ err = tcpm_copy_caps(port, tcpc->config);
+ if (err < 0)
goto out_destroy_wq;
- }
- port->nr_src_pdo = tcpm_copy_pdos(port->src_pdo, tcpc->config->src_pdo,
- tcpc->config->nr_src_pdo);
- port->nr_snk_pdo = tcpm_copy_pdos(port->snk_pdo, tcpc->config->snk_pdo,
- tcpc->config->nr_snk_pdo);
- port->nr_snk_vdo = tcpm_copy_vdos(port->snk_vdo, tcpc->config->snk_vdo,
- tcpc->config->nr_snk_vdo);
-
- port->max_snk_mv = tcpc->config->max_snk_mv;
- port->max_snk_ma = tcpc->config->max_snk_ma;
- port->max_snk_mw = tcpc->config->max_snk_mw;
- port->operating_snk_mw = tcpc->config->operating_snk_mw;
- if (!tcpc->config->try_role_hw)
- port->try_role = tcpc->config->default_role;
+
+ if (!tcpc->config || !tcpc->config->try_role_hw)
+ port->try_role = port->typec_caps.prefer_role;
else
port->try_role = TYPEC_NO_PREFERRED_ROLE;
- port->typec_caps.prefer_role = tcpc->config->default_role;
- port->typec_caps.type = tcpc->config->type;
- port->typec_caps.data = tcpc->config->data;
+ port->typec_caps.fwnode = tcpc->fwnode;
port->typec_caps.revision = 0x0120; /* Type-C spec release 1.2 */
- port->typec_caps.pd_revision = 0x0200; /* USB-PD spec release 2.0 */
+ port->typec_caps.pd_revision = 0x0300; /* USB-PD spec release 3.0 */
port->typec_caps.dr_set = tcpm_dr_set;
port->typec_caps.pr_set = tcpm_pr_set;
port->typec_caps.vconn_set = tcpm_vconn_set;
@@ -3668,7 +4780,7 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
port->typec_caps.port_type_set = tcpm_port_type_set;
port->partner_desc.identity = &port->partner_ident;
- port->port_type = tcpc->config->type;
+ port->port_type = port->typec_caps.type;
port->role_sw = usb_role_switch_get(port->dev);
if (IS_ERR(port->role_sw)) {
@@ -3676,13 +4788,17 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
goto out_destroy_wq;
}
+ err = devm_tcpm_psy_register(port);
+ if (err)
+ goto out_destroy_wq;
+
port->typec_port = typec_register_port(port->dev, &port->typec_caps);
if (IS_ERR(port->typec_port)) {
err = PTR_ERR(port->typec_port);
goto out_destroy_wq;
}
- if (tcpc->config->alt_modes) {
+ if (tcpc->config && tcpc->config->alt_modes) {
const struct typec_altmode_desc *paltmode = tcpc->config->alt_modes;
i = 0;
@@ -3697,6 +4813,8 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
dev_name(dev), paltmode->svid);
break;
}
+ typec_altmode_set_drvdata(alt, port);
+ alt->ops = &tcpm_altmode_ops;
port->port_altmode[i] = alt;
i++;
paltmode++;
diff --git a/drivers/usb/typec/typec_wcove.c b/drivers/usb/typec/tcpm/wcove.c
index 19cca7f1b2c5..423208e19383 100644
--- a/drivers/usb/typec/typec_wcove.c
+++ b/drivers/usb/typec/tcpm/wcove.c
@@ -202,6 +202,10 @@ static int wcove_init(struct tcpc_dev *tcpc)
struct wcove_typec *wcove = tcpc_to_wcove(tcpc);
int ret;
+ ret = regmap_write(wcove->regmap, USBC_CONTROL1, 0);
+ if (ret)
+ return ret;
+
/* Unmask everything */
ret = regmap_write(wcove->regmap, USBC_IRQMASK1, 0);
if (ret)
@@ -285,8 +289,30 @@ static int wcove_get_cc(struct tcpc_dev *tcpc, enum typec_cc_status *cc1,
static int wcove_set_cc(struct tcpc_dev *tcpc, enum typec_cc_status cc)
{
- /* XXX: Relying on the HW FSM to configure things correctly for now */
- return 0;
+ struct wcove_typec *wcove = tcpc_to_wcove(tcpc);
+ unsigned int ctrl;
+
+ switch (cc) {
+ case TYPEC_CC_RD:
+ ctrl = USBC_CONTROL1_MODE_SNK;
+ break;
+ case TYPEC_CC_RP_DEF:
+ ctrl = USBC_CONTROL1_CURSRC_UA_80 | USBC_CONTROL1_MODE_SRC;
+ break;
+ case TYPEC_CC_RP_1_5:
+ ctrl = USBC_CONTROL1_CURSRC_UA_180 | USBC_CONTROL1_MODE_SRC;
+ break;
+ case TYPEC_CC_RP_3_0:
+ ctrl = USBC_CONTROL1_CURSRC_UA_330 | USBC_CONTROL1_MODE_SRC;
+ break;
+ case TYPEC_CC_OPEN:
+ ctrl = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_write(wcove->regmap, USBC_CONTROL1, ctrl);
}
static int wcove_set_polarity(struct tcpc_dev *tcpc, enum typec_cc_polarity pol)
@@ -558,6 +584,7 @@ static const u32 src_pdo[] = {
static const u32 snk_pdo[] = {
PDO_FIXED(5000, 500, PDO_FIXED_DUAL_ROLE | PDO_FIXED_DATA_SWAP |
PDO_FIXED_USB_COMM),
+ PDO_VAR(5000, 12000, 3000),
};
static struct tcpc_config wcove_typec_config = {
@@ -566,9 +593,6 @@ static struct tcpc_config wcove_typec_config = {
.snk_pdo = snk_pdo,
.nr_snk_pdo = ARRAY_SIZE(snk_pdo),
- .max_snk_mv = 12000,
- .max_snk_ma = 3000,
- .max_snk_mw = 36000,
.operating_snk_mw = 15000,
.type = TYPEC_PORT_DRP,
diff --git a/drivers/usb/typec/tps6598x.c b/drivers/usb/typec/tps6598x.c
index 4b4c8d271b27..c84c8c189e90 100644
--- a/drivers/usb/typec/tps6598x.c
+++ b/drivers/usb/typec/tps6598x.c
@@ -81,12 +81,21 @@ struct tps6598x {
struct typec_capability typec_cap;
};
+/*
+ * Max data bytes for Data1, Data2, and other registers. See ch 1.3.2:
+ * http://www.ti.com/lit/ug/slvuan1a/slvuan1a.pdf
+ */
+#define TPS_MAX_LEN 64
+
static int
tps6598x_block_read(struct tps6598x *tps, u8 reg, void *val, size_t len)
{
- u8 data[len + 1];
+ u8 data[TPS_MAX_LEN + 1];
int ret;
+ if (WARN_ON(len + 1 > sizeof(data)))
+ return -EINVAL;
+
if (!tps->i2c_protocol)
return regmap_raw_read(tps->regmap, reg, val, len);
diff --git a/drivers/usb/typec/ucsi/Kconfig b/drivers/usb/typec/ucsi/Kconfig
index e36d6c73c4a4..78118883f96c 100644
--- a/drivers/usb/typec/ucsi/Kconfig
+++ b/drivers/usb/typec/ucsi/Kconfig
@@ -23,6 +23,16 @@ config TYPEC_UCSI
if TYPEC_UCSI
+config UCSI_CCG
+ tristate "UCSI Interface Driver for Cypress CCGx"
+ depends on I2C
+ help
+ This driver enables UCSI support on platforms that expose a
+ Cypress CCGx Type-C controller over I2C interface.
+
+ To compile the driver as a module, choose M here: the module will be
+ called ucsi_ccg.
+
config UCSI_ACPI
tristate "UCSI ACPI Interface Driver"
depends on ACPI
diff --git a/drivers/usb/typec/ucsi/Makefile b/drivers/usb/typec/ucsi/Makefile
index 7afbea512207..2f4900b26210 100644
--- a/drivers/usb/typec/ucsi/Makefile
+++ b/drivers/usb/typec/ucsi/Makefile
@@ -8,3 +8,5 @@ typec_ucsi-y := ucsi.o
typec_ucsi-$(CONFIG_TRACING) += trace.o
obj-$(CONFIG_UCSI_ACPI) += ucsi_acpi.o
+
+obj-$(CONFIG_UCSI_CCG) += ucsi_ccg.o
diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c
index bd5cca5632b3..8d0a6fe748bd 100644
--- a/drivers/usb/typec/ucsi/ucsi.c
+++ b/drivers/usb/typec/ucsi/ucsi.c
@@ -350,6 +350,19 @@ static void ucsi_connector_change(struct work_struct *work)
}
if (con->status.change & UCSI_CONSTAT_CONNECT_CHANGE) {
+ typec_set_pwr_role(con->port, con->status.pwr_dir);
+
+ switch (con->status.partner_type) {
+ case UCSI_CONSTAT_PARTNER_TYPE_UFP:
+ typec_set_data_role(con->port, TYPEC_HOST);
+ break;
+ case UCSI_CONSTAT_PARTNER_TYPE_DFP:
+ typec_set_data_role(con->port, TYPEC_DEVICE);
+ break;
+ default:
+ break;
+ }
+
if (con->status.connected)
ucsi_register_partner(con);
else
diff --git a/drivers/usb/typec/ucsi/ucsi_acpi.c b/drivers/usb/typec/ucsi/ucsi_acpi.c
index 44eb4e1ea817..a18112a83fae 100644
--- a/drivers/usb/typec/ucsi/ucsi_acpi.c
+++ b/drivers/usb/typec/ucsi/ucsi_acpi.c
@@ -79,6 +79,11 @@ static int ucsi_acpi_probe(struct platform_device *pdev)
return -ENODEV;
}
+ /* This will make sure we can use ioremap_nocache() */
+ status = acpi_release_memory(ACPI_HANDLE(&pdev->dev), res, 1);
+ if (ACPI_FAILURE(status))
+ return -ENOMEM;
+
/*
* NOTE: The memory region for the data structures is used also in an
* operation region, which means ACPI has already reserved it. Therefore
diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c
new file mode 100644
index 000000000000..de8a43bdff68
--- /dev/null
+++ b/drivers/usb/typec/ucsi/ucsi_ccg.c
@@ -0,0 +1,307 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * UCSI driver for Cypress CCGx Type-C controller
+ *
+ * Copyright (C) 2017-2018 NVIDIA Corporation. All rights reserved.
+ * Author: Ajay Gupta <ajayg@nvidia.com>
+ *
+ * Some code borrowed from drivers/usb/typec/ucsi/ucsi_acpi.c
+ */
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+
+#include <asm/unaligned.h>
+#include "ucsi.h"
+
+struct ucsi_ccg {
+ struct device *dev;
+ struct ucsi *ucsi;
+ struct ucsi_ppm ppm;
+ struct i2c_client *client;
+};
+
+#define CCGX_RAB_INTR_REG 0x06
+#define CCGX_RAB_UCSI_CONTROL 0x39
+#define CCGX_RAB_UCSI_CONTROL_START BIT(0)
+#define CCGX_RAB_UCSI_CONTROL_STOP BIT(1)
+#define CCGX_RAB_UCSI_DATA_BLOCK(offset) (0xf000 | ((offset) & 0xff))
+
+static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len)
+{
+ struct i2c_client *client = uc->client;
+ const struct i2c_adapter_quirks *quirks = client->adapter->quirks;
+ unsigned char buf[2];
+ struct i2c_msg msgs[] = {
+ {
+ .addr = client->addr,
+ .flags = 0x0,
+ .len = sizeof(buf),
+ .buf = buf,
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .buf = data,
+ },
+ };
+ u32 rlen, rem_len = len, max_read_len = len;
+ int status;
+
+ /* check any max_read_len limitation on i2c adapter */
+ if (quirks && quirks->max_read_len)
+ max_read_len = quirks->max_read_len;
+
+ while (rem_len > 0) {
+ msgs[1].buf = &data[len - rem_len];
+ rlen = min_t(u16, rem_len, max_read_len);
+ msgs[1].len = rlen;
+ put_unaligned_le16(rab, buf);
+ status = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (status < 0) {
+ dev_err(uc->dev, "i2c_transfer failed %d\n", status);
+ return status;
+ }
+ rab += rlen;
+ rem_len -= rlen;
+ }
+
+ return 0;
+}
+
+static int ccg_write(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len)
+{
+ struct i2c_client *client = uc->client;
+ unsigned char *buf;
+ struct i2c_msg msgs[] = {
+ {
+ .addr = client->addr,
+ .flags = 0x0,
+ }
+ };
+ int status;
+
+ buf = kzalloc(len + sizeof(rab), GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ put_unaligned_le16(rab, buf);
+ memcpy(buf + sizeof(rab), data, len);
+
+ msgs[0].len = len + sizeof(rab);
+ msgs[0].buf = buf;
+
+ status = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (status < 0) {
+ dev_err(uc->dev, "i2c_transfer failed %d\n", status);
+ kfree(buf);
+ return status;
+ }
+
+ kfree(buf);
+ return 0;
+}
+
+static int ucsi_ccg_init(struct ucsi_ccg *uc)
+{
+ unsigned int count = 10;
+ u8 data;
+ int status;
+
+ data = CCGX_RAB_UCSI_CONTROL_STOP;
+ status = ccg_write(uc, CCGX_RAB_UCSI_CONTROL, &data, sizeof(data));
+ if (status < 0)
+ return status;
+
+ data = CCGX_RAB_UCSI_CONTROL_START;
+ status = ccg_write(uc, CCGX_RAB_UCSI_CONTROL, &data, sizeof(data));
+ if (status < 0)
+ return status;
+
+ /*
+ * Flush CCGx RESPONSE queue by acking interrupts. Above ucsi control
+ * register write will push response which must be cleared.
+ */
+ do {
+ status = ccg_read(uc, CCGX_RAB_INTR_REG, &data, sizeof(data));
+ if (status < 0)
+ return status;
+
+ if (!data)
+ return 0;
+
+ status = ccg_write(uc, CCGX_RAB_INTR_REG, &data, sizeof(data));
+ if (status < 0)
+ return status;
+
+ usleep_range(10000, 11000);
+ } while (--count);
+
+ return -ETIMEDOUT;
+}
+
+static int ucsi_ccg_send_data(struct ucsi_ccg *uc)
+{
+ u8 *ppm = (u8 *)uc->ppm.data;
+ int status;
+ u16 rab;
+
+ rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, message_out));
+ status = ccg_write(uc, rab, ppm +
+ offsetof(struct ucsi_data, message_out),
+ sizeof(uc->ppm.data->message_out));
+ if (status < 0)
+ return status;
+
+ rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, ctrl));
+ return ccg_write(uc, rab, ppm + offsetof(struct ucsi_data, ctrl),
+ sizeof(uc->ppm.data->ctrl));
+}
+
+static int ucsi_ccg_recv_data(struct ucsi_ccg *uc)
+{
+ u8 *ppm = (u8 *)uc->ppm.data;
+ int status;
+ u16 rab;
+
+ rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, cci));
+ status = ccg_read(uc, rab, ppm + offsetof(struct ucsi_data, cci),
+ sizeof(uc->ppm.data->cci));
+ if (status < 0)
+ return status;
+
+ rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, message_in));
+ return ccg_read(uc, rab, ppm + offsetof(struct ucsi_data, message_in),
+ sizeof(uc->ppm.data->message_in));
+}
+
+static int ucsi_ccg_ack_interrupt(struct ucsi_ccg *uc)
+{
+ int status;
+ unsigned char data;
+
+ status = ccg_read(uc, CCGX_RAB_INTR_REG, &data, sizeof(data));
+ if (status < 0)
+ return status;
+
+ return ccg_write(uc, CCGX_RAB_INTR_REG, &data, sizeof(data));
+}
+
+static int ucsi_ccg_sync(struct ucsi_ppm *ppm)
+{
+ struct ucsi_ccg *uc = container_of(ppm, struct ucsi_ccg, ppm);
+ int status;
+
+ status = ucsi_ccg_recv_data(uc);
+ if (status < 0)
+ return status;
+
+ /* ack interrupt to allow next command to run */
+ return ucsi_ccg_ack_interrupt(uc);
+}
+
+static int ucsi_ccg_cmd(struct ucsi_ppm *ppm, struct ucsi_control *ctrl)
+{
+ struct ucsi_ccg *uc = container_of(ppm, struct ucsi_ccg, ppm);
+
+ ppm->data->ctrl.raw_cmd = ctrl->raw_cmd;
+ return ucsi_ccg_send_data(uc);
+}
+
+static irqreturn_t ccg_irq_handler(int irq, void *data)
+{
+ struct ucsi_ccg *uc = data;
+
+ ucsi_notify(uc->ucsi);
+
+ return IRQ_HANDLED;
+}
+
+static int ucsi_ccg_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ struct ucsi_ccg *uc;
+ int status;
+ u16 rab;
+
+ uc = devm_kzalloc(dev, sizeof(*uc), GFP_KERNEL);
+ if (!uc)
+ return -ENOMEM;
+
+ uc->ppm.data = devm_kzalloc(dev, sizeof(struct ucsi_data), GFP_KERNEL);
+ if (!uc->ppm.data)
+ return -ENOMEM;
+
+ uc->ppm.cmd = ucsi_ccg_cmd;
+ uc->ppm.sync = ucsi_ccg_sync;
+ uc->dev = dev;
+ uc->client = client;
+
+ /* reset ccg device and initialize ucsi */
+ status = ucsi_ccg_init(uc);
+ if (status < 0) {
+ dev_err(uc->dev, "ucsi_ccg_init failed - %d\n", status);
+ return status;
+ }
+
+ status = devm_request_threaded_irq(dev, client->irq, NULL,
+ ccg_irq_handler,
+ IRQF_ONESHOT | IRQF_TRIGGER_HIGH,
+ dev_name(dev), uc);
+ if (status < 0) {
+ dev_err(uc->dev, "request_threaded_irq failed - %d\n", status);
+ return status;
+ }
+
+ uc->ucsi = ucsi_register_ppm(dev, &uc->ppm);
+ if (IS_ERR(uc->ucsi)) {
+ dev_err(uc->dev, "ucsi_register_ppm failed\n");
+ return PTR_ERR(uc->ucsi);
+ }
+
+ rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, version));
+ status = ccg_read(uc, rab, (u8 *)(uc->ppm.data) +
+ offsetof(struct ucsi_data, version),
+ sizeof(uc->ppm.data->version));
+ if (status < 0) {
+ ucsi_unregister_ppm(uc->ucsi);
+ return status;
+ }
+
+ i2c_set_clientdata(client, uc);
+ return 0;
+}
+
+static int ucsi_ccg_remove(struct i2c_client *client)
+{
+ struct ucsi_ccg *uc = i2c_get_clientdata(client);
+
+ ucsi_unregister_ppm(uc->ucsi);
+
+ return 0;
+}
+
+static const struct i2c_device_id ucsi_ccg_device_id[] = {
+ {"ccgx-ucsi", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, ucsi_ccg_device_id);
+
+static struct i2c_driver ucsi_ccg_driver = {
+ .driver = {
+ .name = "ucsi_ccg",
+ },
+ .probe = ucsi_ccg_probe,
+ .remove = ucsi_ccg_remove,
+ .id_table = ucsi_ccg_device_id,
+};
+
+module_i2c_driver(ucsi_ccg_driver);
+
+MODULE_AUTHOR("Ajay Gupta <ajayg@nvidia.com>");
+MODULE_DESCRIPTION("UCSI driver for Cypress CCGx Type-C controller");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/usb-skeleton.c b/drivers/usb/usb-skeleton.c
index c3ddd0f1f449..f101347e3ea3 100644
--- a/drivers/usb/usb-skeleton.c
+++ b/drivers/usb/usb-skeleton.c
@@ -159,10 +159,11 @@ static int skel_flush(struct file *file, fl_owner_t id)
static void skel_read_bulk_callback(struct urb *urb)
{
struct usb_skel *dev;
+ unsigned long flags;
dev = urb->context;
- spin_lock(&dev->err_lock);
+ spin_lock_irqsave(&dev->err_lock, flags);
/* sync/async unlink faults aren't errors */
if (urb->status) {
if (!(urb->status == -ENOENT ||
@@ -177,7 +178,7 @@ static void skel_read_bulk_callback(struct urb *urb)
dev->bulk_in_filled = urb->actual_length;
}
dev->ongoing_read = 0;
- spin_unlock(&dev->err_lock);
+ spin_unlock_irqrestore(&dev->err_lock, flags);
wake_up_interruptible(&dev->bulk_in_wait);
}
@@ -331,6 +332,7 @@ exit:
static void skel_write_bulk_callback(struct urb *urb)
{
struct usb_skel *dev;
+ unsigned long flags;
dev = urb->context;
@@ -343,9 +345,9 @@ static void skel_write_bulk_callback(struct urb *urb)
"%s - nonzero write bulk status received: %d\n",
__func__, urb->status);
- spin_lock(&dev->err_lock);
+ spin_lock_irqsave(&dev->err_lock, flags);
dev->errors = urb->status;
- spin_unlock(&dev->err_lock);
+ spin_unlock_irqrestore(&dev->err_lock, flags);
}
/* free up our allocated buffer */
diff --git a/drivers/usb/usbip/usbip_common.c b/drivers/usb/usbip/usbip_common.c
index 9756752c0681..45da3e01c7b0 100644
--- a/drivers/usb/usbip/usbip_common.c
+++ b/drivers/usb/usbip/usbip_common.c
@@ -309,7 +309,7 @@ int usbip_recv(struct socket *sock, void *buf, int size)
if (!sock || !buf || !size)
return -EINVAL;
- iov_iter_kvec(&msg.msg_iter, READ|ITER_KVEC, &iov, 1, size);
+ iov_iter_kvec(&msg.msg_iter, READ, &iov, 1, size);
usbip_dbg_xmit("enter\n");
diff --git a/drivers/usb/usbip/vhci_hcd.c b/drivers/usb/usbip/vhci_hcd.c
index d11f3f8dad40..1e592ec94ba4 100644
--- a/drivers/usb/usbip/vhci_hcd.c
+++ b/drivers/usb/usbip/vhci_hcd.c
@@ -318,8 +318,9 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
struct vhci_hcd *vhci_hcd;
struct vhci *vhci;
int retval = 0;
- int rhport;
+ int rhport = -1;
unsigned long flags;
+ bool invalid_rhport = false;
u32 prev_port_status[VHCI_HC_PORTS];
@@ -334,9 +335,19 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
usbip_dbg_vhci_rh("typeReq %x wValue %x wIndex %x\n", typeReq, wValue,
wIndex);
- if (wIndex > VHCI_HC_PORTS)
- pr_err("invalid port number %d\n", wIndex);
- rhport = wIndex - 1;
+ /*
+ * wIndex can be 0 for some request types (typeReq). rhport is
+ * in valid range when wIndex >= 1 and < VHCI_HC_PORTS.
+ *
+ * Reference port_status[] only with valid rhport when
+ * invalid_rhport is false.
+ */
+ if (wIndex < 1 || wIndex > VHCI_HC_PORTS) {
+ invalid_rhport = true;
+ if (wIndex > VHCI_HC_PORTS)
+ pr_err("invalid port number %d\n", wIndex);
+ } else
+ rhport = wIndex - 1;
vhci_hcd = hcd_to_vhci_hcd(hcd);
vhci = vhci_hcd->vhci;
@@ -345,8 +356,9 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
/* store old status and compare now and old later */
if (usbip_dbg_flag_vhci_rh) {
- memcpy(prev_port_status, vhci_hcd->port_status,
- sizeof(prev_port_status));
+ if (!invalid_rhport)
+ memcpy(prev_port_status, vhci_hcd->port_status,
+ sizeof(prev_port_status));
}
switch (typeReq) {
@@ -354,8 +366,10 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
usbip_dbg_vhci_rh(" ClearHubFeature\n");
break;
case ClearPortFeature:
- if (rhport < 0)
+ if (invalid_rhport) {
+ pr_err("invalid port number %d\n", wIndex);
goto error;
+ }
switch (wValue) {
case USB_PORT_FEAT_SUSPEND:
if (hcd->speed == HCD_USB3) {
@@ -415,9 +429,10 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
break;
case GetPortStatus:
usbip_dbg_vhci_rh(" GetPortStatus port %x\n", wIndex);
- if (wIndex < 1) {
+ if (invalid_rhport) {
pr_err("invalid port number %d\n", wIndex);
retval = -EPIPE;
+ goto error;
}
/* we do not care about resume. */
@@ -513,16 +528,20 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
goto error;
}
- if (rhport < 0)
+ if (invalid_rhport) {
+ pr_err("invalid port number %d\n", wIndex);
goto error;
+ }
vhci_hcd->port_status[rhport] |= USB_PORT_STAT_SUSPEND;
break;
case USB_PORT_FEAT_POWER:
usbip_dbg_vhci_rh(
" SetPortFeature: USB_PORT_FEAT_POWER\n");
- if (rhport < 0)
+ if (invalid_rhport) {
+ pr_err("invalid port number %d\n", wIndex);
goto error;
+ }
if (hcd->speed == HCD_USB3)
vhci_hcd->port_status[rhport] |= USB_SS_PORT_STAT_POWER;
else
@@ -531,8 +550,10 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
case USB_PORT_FEAT_BH_PORT_RESET:
usbip_dbg_vhci_rh(
" SetPortFeature: USB_PORT_FEAT_BH_PORT_RESET\n");
- if (rhport < 0)
+ if (invalid_rhport) {
+ pr_err("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 "
@@ -543,8 +564,10 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
case USB_PORT_FEAT_RESET:
usbip_dbg_vhci_rh(
" SetPortFeature: USB_PORT_FEAT_RESET\n");
- if (rhport < 0)
+ if (invalid_rhport) {
+ pr_err("invalid port number %d\n", wIndex);
goto error;
+ }
/* if it's already enabled, disable */
if (hcd->speed == HCD_USB3) {
vhci_hcd->port_status[rhport] = 0;
@@ -565,8 +588,10 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
default:
usbip_dbg_vhci_rh(" SetPortFeature: default %d\n",
wValue);
- if (rhport < 0)
+ if (invalid_rhport) {
+ pr_err("invalid port number %d\n", wIndex);
goto error;
+ }
if (hcd->speed == HCD_USB3) {
if ((vhci_hcd->port_status[rhport] &
USB_SS_PORT_STAT_POWER) != 0) {
@@ -608,7 +633,7 @@ error:
if (usbip_dbg_flag_vhci_rh) {
pr_debug("port %d\n", rhport);
/* Only dump valid port status */
- if (rhport >= 0) {
+ if (!invalid_rhport) {
dump_port_status_diff(prev_port_status[rhport],
vhci_hcd->port_status[rhport],
hcd->speed == HCD_USB3);
@@ -618,8 +643,10 @@ error:
spin_unlock_irqrestore(&vhci->lock, flags);
- if ((vhci_hcd->port_status[rhport] & PORT_C_MASK) != 0)
+ if (!invalid_rhport &&
+ (vhci_hcd->port_status[rhport] & PORT_C_MASK) != 0) {
usb_hcd_poll_rh_status(hcd);
+ }
return retval;
}
diff --git a/drivers/usb/usbip/vhci_sysfs.c b/drivers/usb/usbip/vhci_sysfs.c
index 48808388ec33..be37aec250c2 100644
--- a/drivers/usb/usbip/vhci_sysfs.c
+++ b/drivers/usb/usbip/vhci_sysfs.c
@@ -10,6 +10,9 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
+/* Hardening for Spectre-v1 */
+#include <linux/nospec.h>
+
#include "usbip_common.h"
#include "vhci.h"
@@ -205,16 +208,20 @@ static int vhci_port_disconnect(struct vhci_hcd *vhci_hcd, __u32 rhport)
return 0;
}
-static int valid_port(__u32 pdev_nr, __u32 rhport)
+static int valid_port(__u32 *pdev_nr, __u32 *rhport)
{
- if (pdev_nr >= vhci_num_controllers) {
- pr_err("pdev %u\n", pdev_nr);
+ if (*pdev_nr >= vhci_num_controllers) {
+ pr_err("pdev %u\n", *pdev_nr);
return 0;
}
- if (rhport >= VHCI_HC_PORTS) {
- pr_err("rhport %u\n", rhport);
+ *pdev_nr = array_index_nospec(*pdev_nr, vhci_num_controllers);
+
+ if (*rhport >= VHCI_HC_PORTS) {
+ pr_err("rhport %u\n", *rhport);
return 0;
}
+ *rhport = array_index_nospec(*rhport, VHCI_HC_PORTS);
+
return 1;
}
@@ -232,7 +239,7 @@ static ssize_t detach_store(struct device *dev, struct device_attribute *attr,
pdev_nr = port_to_pdev_nr(port);
rhport = port_to_rhport(port);
- if (!valid_port(pdev_nr, rhport))
+ if (!valid_port(&pdev_nr, &rhport))
return -EINVAL;
hcd = platform_get_drvdata(vhcis[pdev_nr].pdev);
@@ -258,7 +265,8 @@ static ssize_t detach_store(struct device *dev, struct device_attribute *attr,
}
static DEVICE_ATTR_WO(detach);
-static int valid_args(__u32 pdev_nr, __u32 rhport, enum usb_device_speed speed)
+static int valid_args(__u32 *pdev_nr, __u32 *rhport,
+ enum usb_device_speed speed)
{
if (!valid_port(pdev_nr, rhport)) {
return 0;
@@ -322,7 +330,7 @@ static ssize_t attach_store(struct device *dev, struct device_attribute *attr,
sockfd, devid, speed);
/* check received parameters */
- if (!valid_args(pdev_nr, rhport, speed))
+ if (!valid_args(&pdev_nr, &rhport, speed))
return -EINVAL;
hcd = platform_get_drvdata(vhcis[pdev_nr].pdev);
diff --git a/drivers/usb/usbip/vudc_dev.c b/drivers/usb/usbip/vudc_dev.c
index 1b9a4f87db59..1634d8698e15 100644
--- a/drivers/usb/usbip/vudc_dev.c
+++ b/drivers/usb/usbip/vudc_dev.c
@@ -279,12 +279,10 @@ static int vep_disable(struct usb_ep *_ep)
static struct usb_request *vep_alloc_request(struct usb_ep *_ep,
gfp_t mem_flags)
{
- struct vep *ep;
struct vrequest *req;
if (!_ep)
return NULL;
- ep = to_vep(_ep);
req = kzalloc(sizeof(*req), mem_flags);
if (!req)
diff --git a/drivers/usb/usbip/vudc_main.c b/drivers/usb/usbip/vudc_main.c
index 3fc22037a82f..390733e6937e 100644
--- a/drivers/usb/usbip/vudc_main.c
+++ b/drivers/usb/usbip/vudc_main.c
@@ -73,6 +73,10 @@ static int __init init(void)
cleanup:
list_for_each_entry_safe(udc_dev, udc_dev2, &vudc_devices, dev_entry) {
list_del(&udc_dev->dev_entry);
+ /*
+ * Just do platform_device_del() here, put_vudc_device()
+ * calls the platform_device_put()
+ */
platform_device_del(udc_dev->pdev);
put_vudc_device(udc_dev);
}
@@ -89,7 +93,11 @@ static void __exit cleanup(void)
list_for_each_entry_safe(udc_dev, udc_dev2, &vudc_devices, dev_entry) {
list_del(&udc_dev->dev_entry);
- platform_device_unregister(udc_dev->pdev);
+ /*
+ * Just do platform_device_del() here, put_vudc_device()
+ * calls the platform_device_put()
+ */
+ platform_device_del(udc_dev->pdev);
put_vudc_device(udc_dev);
}
platform_driver_unregister(&vudc_driver);
diff --git a/drivers/usb/wusbcore/crypto.c b/drivers/usb/wusbcore/crypto.c
index aff50eb09ca9..68ddee86a886 100644
--- a/drivers/usb/wusbcore/crypto.c
+++ b/drivers/usb/wusbcore/crypto.c
@@ -189,7 +189,7 @@ struct wusb_mac_scratch {
* NOTE: blen is not aligned to a block size, we'll pad zeros, that's
* what sg[4] is for. Maybe there is a smarter way to do this.
*/
-static int wusb_ccm_mac(struct crypto_skcipher *tfm_cbc,
+static int wusb_ccm_mac(struct crypto_sync_skcipher *tfm_cbc,
struct crypto_cipher *tfm_aes,
struct wusb_mac_scratch *scratch,
void *mic,
@@ -198,7 +198,7 @@ static int wusb_ccm_mac(struct crypto_skcipher *tfm_cbc,
size_t blen)
{
int result = 0;
- SKCIPHER_REQUEST_ON_STACK(req, tfm_cbc);
+ SYNC_SKCIPHER_REQUEST_ON_STACK(req, tfm_cbc);
struct scatterlist sg[4], sg_dst;
void *dst_buf;
size_t dst_size;
@@ -224,7 +224,7 @@ static int wusb_ccm_mac(struct crypto_skcipher *tfm_cbc,
if (!dst_buf)
goto error_dst_buf;
- iv = kzalloc(crypto_skcipher_ivsize(tfm_cbc), GFP_KERNEL);
+ iv = kzalloc(crypto_sync_skcipher_ivsize(tfm_cbc), GFP_KERNEL);
if (!iv)
goto error_iv;
@@ -251,7 +251,7 @@ static int wusb_ccm_mac(struct crypto_skcipher *tfm_cbc,
sg_set_page(&sg[3], ZERO_PAGE(0), zero_padding, 0);
sg_init_one(&sg_dst, dst_buf, dst_size);
- skcipher_request_set_tfm(req, tfm_cbc);
+ skcipher_request_set_sync_tfm(req, tfm_cbc);
skcipher_request_set_callback(req, 0, NULL, NULL);
skcipher_request_set_crypt(req, sg, &sg_dst, dst_size, iv);
result = crypto_skcipher_encrypt(req);
@@ -298,19 +298,19 @@ ssize_t wusb_prf(void *out, size_t out_size,
{
ssize_t result, bytes = 0, bitr;
struct aes_ccm_nonce n = *_n;
- struct crypto_skcipher *tfm_cbc;
+ struct crypto_sync_skcipher *tfm_cbc;
struct crypto_cipher *tfm_aes;
struct wusb_mac_scratch *scratch;
u64 sfn = 0;
__le64 sfn_le;
- tfm_cbc = crypto_alloc_skcipher("cbc(aes)", 0, CRYPTO_ALG_ASYNC);
+ tfm_cbc = crypto_alloc_sync_skcipher("cbc(aes)", 0, 0);
if (IS_ERR(tfm_cbc)) {
result = PTR_ERR(tfm_cbc);
printk(KERN_ERR "E: can't load CBC(AES): %d\n", (int)result);
goto error_alloc_cbc;
}
- result = crypto_skcipher_setkey(tfm_cbc, key, 16);
+ result = crypto_sync_skcipher_setkey(tfm_cbc, key, 16);
if (result < 0) {
printk(KERN_ERR "E: can't set CBC key: %d\n", (int)result);
goto error_setkey_cbc;
@@ -351,7 +351,7 @@ error_setkey_aes:
crypto_free_cipher(tfm_aes);
error_alloc_aes:
error_setkey_cbc:
- crypto_free_skcipher(tfm_cbc);
+ crypto_free_sync_skcipher(tfm_cbc);
error_alloc_cbc:
return result;
}
diff --git a/drivers/usb/wusbcore/security.c b/drivers/usb/wusbcore/security.c
index 33d2f5d7f33b..14ac8c98ac9e 100644
--- a/drivers/usb/wusbcore/security.c
+++ b/drivers/usb/wusbcore/security.c
@@ -217,7 +217,7 @@ int wusb_dev_sec_add(struct wusbhc *wusbhc,
result = usb_get_descriptor(usb_dev, USB_DT_SECURITY,
0, secd, sizeof(*secd));
- if (result < sizeof(*secd)) {
+ if (result < (int)sizeof(*secd)) {
dev_err(dev, "Can't read security descriptor or "
"not enough data: %d\n", result);
goto out;
diff --git a/drivers/usb/wusbcore/wa-rpipe.c b/drivers/usb/wusbcore/wa-rpipe.c
index d0f1a6698460..a5734cbcd5ad 100644
--- a/drivers/usb/wusbcore/wa-rpipe.c
+++ b/drivers/usb/wusbcore/wa-rpipe.c
@@ -470,8 +470,7 @@ error:
int wa_rpipes_create(struct wahc *wa)
{
wa->rpipes = le16_to_cpu(wa->wa_descr->wNumRPipes);
- wa->rpipe_bm = kzalloc(BITS_TO_LONGS(wa->rpipes)*sizeof(unsigned long),
- GFP_KERNEL);
+ wa->rpipe_bm = bitmap_zalloc(wa->rpipes, GFP_KERNEL);
if (wa->rpipe_bm == NULL)
return -ENOMEM;
return 0;
@@ -486,7 +485,7 @@ void wa_rpipes_destroy(struct wahc *wa)
dev_err(dev, "BUG: pipes not released on exit: %*pb\n",
wa->rpipes, wa->rpipe_bm);
}
- kfree(wa->rpipe_bm);
+ bitmap_free(wa->rpipe_bm);
}
/*
diff --git a/drivers/usb/wusbcore/wa-xfer.c b/drivers/usb/wusbcore/wa-xfer.c
index 7fca4e7e556d..01f2f21830c0 100644
--- a/drivers/usb/wusbcore/wa-xfer.c
+++ b/drivers/usb/wusbcore/wa-xfer.c
@@ -1918,7 +1918,7 @@ EXPORT_SYMBOL_GPL(wa_urb_enqueue);
*/
int wa_urb_dequeue(struct wahc *wa, struct urb *urb, int status)
{
- unsigned long flags, flags2;
+ unsigned long flags;
struct wa_xfer *xfer;
struct wa_seg *seg;
struct wa_rpipe *rpipe;
@@ -1964,10 +1964,10 @@ int wa_urb_dequeue(struct wahc *wa, struct urb *urb, int status)
goto out_unlock;
}
/* Check the delayed list -> if there, release and complete */
- spin_lock_irqsave(&wa->xfer_list_lock, flags2);
+ spin_lock(&wa->xfer_list_lock);
if (!list_empty(&xfer->list_node) && xfer->seg == NULL)
goto dequeue_delayed;
- spin_unlock_irqrestore(&wa->xfer_list_lock, flags2);
+ spin_unlock(&wa->xfer_list_lock);
if (xfer->seg == NULL) /* still hasn't reached */
goto out_unlock; /* setup(), enqueue_b() completes */
/* Ok, the xfer is in flight already, it's been setup and submitted.*/
@@ -2054,7 +2054,7 @@ out_unlock:
dequeue_delayed:
list_del_init(&xfer->list_node);
- spin_unlock_irqrestore(&wa->xfer_list_lock, flags2);
+ spin_unlock(&wa->xfer_list_lock);
xfer->result = urb->status;
spin_unlock_irqrestore(&xfer->lock, flags);
wa_xfer_giveback(xfer);
OpenPOWER on IntegriCloud