summaryrefslogtreecommitdiff
path: root/drivers/usb
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/Kconfig2
-rw-r--r--drivers/usb/Makefile1
-rw-r--r--drivers/usb/atm/usbatm.c4
-rw-r--r--drivers/usb/chipidea/Kconfig8
-rw-r--r--drivers/usb/chipidea/Makefile1
-rw-r--r--drivers/usb/chipidea/ci.h22
-rw-r--r--drivers/usb/chipidea/ci_hdrc_imx.c4
-rw-r--r--drivers/usb/chipidea/ci_hdrc_imx.h1
-rw-r--r--drivers/usb/chipidea/ci_hdrc_msm.c280
-rw-r--r--drivers/usb/chipidea/ci_hdrc_usb2.c4
-rw-r--r--drivers/usb/chipidea/core.c173
-rw-r--r--drivers/usb/chipidea/host.c10
-rw-r--r--drivers/usb/chipidea/otg.c99
-rw-r--r--drivers/usb/chipidea/otg_fsm.c14
-rw-r--r--drivers/usb/chipidea/udc.c17
-rw-r--r--drivers/usb/chipidea/udc.h12
-rw-r--r--drivers/usb/chipidea/ulpi.c113
-rw-r--r--drivers/usb/chipidea/usbmisc_imx.c86
-rw-r--r--drivers/usb/class/cdc-acm.c218
-rw-r--r--drivers/usb/class/cdc-acm.h5
-rw-r--r--drivers/usb/class/cdc-wdm.c2
-rw-r--r--drivers/usb/class/usblp.c2
-rw-r--r--drivers/usb/class/usbtmc.c23
-rw-r--r--drivers/usb/common/ulpi.c79
-rw-r--r--drivers/usb/core/buffer.c3
-rw-r--r--drivers/usb/core/config.c25
-rw-r--r--drivers/usb/core/devices.c12
-rw-r--r--drivers/usb/core/devio.c46
-rw-r--r--drivers/usb/core/driver.c3
-rw-r--r--drivers/usb/core/endpoint.c7
-rw-r--r--drivers/usb/core/file.c2
-rw-r--r--drivers/usb/core/generic.c2
-rw-r--r--drivers/usb/core/hcd.c8
-rw-r--r--drivers/usb/core/hub.c153
-rw-r--r--drivers/usb/core/ledtrig-usbport.c7
-rw-r--r--drivers/usb/core/message.c36
-rw-r--r--drivers/usb/core/notify.c2
-rw-r--r--drivers/usb/core/quirks.c12
-rw-r--r--drivers/usb/core/sysfs.c17
-rw-r--r--drivers/usb/core/urb.c12
-rw-r--r--drivers/usb/core/usb.c3
-rw-r--r--drivers/usb/core/usb.h5
-rw-r--r--drivers/usb/dwc2/Makefile1
-rw-r--r--drivers/usb/dwc2/core.c963
-rw-r--r--drivers/usb/dwc2/core.h502
-rw-r--r--drivers/usb/dwc2/core_intr.c13
-rw-r--r--drivers/usb/dwc2/debug.h4
-rw-r--r--drivers/usb/dwc2/debugfs.c184
-rw-r--r--drivers/usb/dwc2/gadget.c1431
-rw-r--r--drivers/usb/dwc2/hcd.c416
-rw-r--r--drivers/usb/dwc2/hcd.h83
-rw-r--r--drivers/usb/dwc2/hcd_ddma.c75
-rw-r--r--drivers/usb/dwc2/hcd_intr.c111
-rw-r--r--drivers/usb/dwc2/hcd_queue.c111
-rw-r--r--drivers/usb/dwc2/hw.h630
-rw-r--r--drivers/usb/dwc2/params.c707
-rw-r--r--drivers/usb/dwc2/pci.c20
-rw-r--r--drivers/usb/dwc2/platform.c223
-rw-r--r--drivers/usb/dwc3/Kconfig6
-rw-r--r--drivers/usb/dwc3/Makefile6
-rw-r--r--drivers/usb/dwc3/core.c358
-rw-r--r--drivers/usb/dwc3/core.h68
-rw-r--r--drivers/usb/dwc3/debug.c32
-rw-r--r--drivers/usb/dwc3/debug.h41
-rw-r--r--drivers/usb/dwc3/dwc3-exynos.c19
-rw-r--r--drivers/usb/dwc3/dwc3-omap.c29
-rw-r--r--drivers/usb/dwc3/dwc3-pci.c131
-rw-r--r--drivers/usb/dwc3/dwc3-st.c1
-rw-r--r--drivers/usb/dwc3/ep0.c405
-rw-r--r--drivers/usb/dwc3/gadget.c871
-rw-r--r--drivers/usb/dwc3/gadget.h19
-rw-r--r--drivers/usb/dwc3/host.c109
-rw-r--r--drivers/usb/dwc3/io.h6
-rw-r--r--drivers/usb/dwc3/trace.h123
-rw-r--r--drivers/usb/early/ehci-dbgp.c1
-rw-r--r--drivers/usb/gadget/composite.c37
-rw-r--r--drivers/usb/gadget/configfs.c9
-rw-r--r--drivers/usb/gadget/function/f_acm.c4
-rw-r--r--drivers/usb/gadget/function/f_fs.c63
-rw-r--r--drivers/usb/gadget/function/f_hid.c267
-rw-r--r--drivers/usb/gadget/function/f_mass_storage.c1
-rw-r--r--drivers/usb/gadget/function/f_ncm.c14
-rw-r--r--drivers/usb/gadget/function/f_phonet.c11
-rw-r--r--drivers/usb/gadget/function/f_printer.c63
-rw-r--r--drivers/usb/gadget/function/f_tcm.c4
-rw-r--r--drivers/usb/gadget/function/f_uac2.c59
-rw-r--r--drivers/usb/gadget/function/f_uvc.c17
-rw-r--r--drivers/usb/gadget/function/rndis.c12
-rw-r--r--drivers/usb/gadget/function/rndis.h51
-rw-r--r--drivers/usb/gadget/function/u_ether.c43
-rw-r--r--drivers/usb/gadget/function/u_ether_configfs.h2
-rw-r--r--drivers/usb/gadget/function/u_fs.h3
-rw-r--r--drivers/usb/gadget/function/u_printer.h5
-rw-r--r--drivers/usb/gadget/function/u_serial.c7
-rw-r--r--drivers/usb/gadget/function/u_uac2.h2
-rw-r--r--drivers/usb/gadget/function/uvc.h18
-rw-r--r--drivers/usb/gadget/function/uvc_configfs.c25
-rw-r--r--drivers/usb/gadget/function/uvc_v4l2.c3
-rw-r--r--drivers/usb/gadget/function/uvc_video.c2
-rw-r--r--drivers/usb/gadget/legacy/audio.c1
-rw-r--r--drivers/usb/gadget/legacy/inode.c31
-rw-r--r--drivers/usb/gadget/legacy/printer.c28
-rw-r--r--drivers/usb/gadget/udc/Kconfig14
-rw-r--r--drivers/usb/gadget/udc/at91_udc.h2
-rw-r--r--drivers/usb/gadget/udc/atmel_usba_udc.c251
-rw-r--r--drivers/usb/gadget/udc/atmel_usba_udc.h11
-rw-r--r--drivers/usb/gadget/udc/bdc/bdc_cmd.c2
-rw-r--r--drivers/usb/gadget/udc/bdc/bdc_ep.c2
-rw-r--r--drivers/usb/gadget/udc/core.c51
-rw-r--r--drivers/usb/gadget/udc/dummy_hcd.c13
-rw-r--r--drivers/usb/gadget/udc/fotg210-udc.c4
-rw-r--r--drivers/usb/gadget/udc/fsl_qe_udc.c2
-rw-r--r--drivers/usb/gadget/udc/fsl_udc_core.c19
-rw-r--r--drivers/usb/gadget/udc/fsl_usb2_udc.h2
-rw-r--r--drivers/usb/gadget/udc/fusb300_udc.c4
-rw-r--r--drivers/usb/gadget/udc/goku_udc.c2
-rw-r--r--drivers/usb/gadget/udc/gr_udc.c4
-rw-r--r--drivers/usb/gadget/udc/m66592-udc.c6
-rw-r--r--drivers/usb/gadget/udc/mv_u3d_core.c36
-rw-r--r--drivers/usb/gadget/udc/mv_udc_core.c5
-rw-r--r--drivers/usb/gadget/udc/net2272.c8
-rw-r--r--drivers/usb/gadget/udc/net2280.c31
-rw-r--r--drivers/usb/gadget/udc/omap_udc.c2
-rw-r--r--drivers/usb/gadget/udc/omap_udc.h2
-rw-r--r--drivers/usb/gadget/udc/pch_udc.c1
-rw-r--r--drivers/usb/gadget/udc/pxa25x_udc.h2
-rw-r--r--drivers/usb/gadget/udc/pxa27x_udc.c7
-rw-r--r--drivers/usb/gadget/udc/r8a66597-udc.c2
-rw-r--r--drivers/usb/gadget/udc/renesas_usb3.c4
-rw-r--r--drivers/usb/gadget/udc/s3c-hsudc.c2
-rw-r--r--drivers/usb/gadget/udc/s3c2410_udc.c4
-rw-r--r--drivers/usb/host/Kconfig9
-rw-r--r--drivers/usb/host/Makefile1
-rw-r--r--drivers/usb/host/ehci-exynos.c2
-rw-r--r--drivers/usb/host/ehci-fsl.c5
-rw-r--r--drivers/usb/host/ehci-hcd.c2
-rw-r--r--drivers/usb/host/ehci-hub.c14
-rw-r--r--drivers/usb/host/ehci-pci.c3
-rw-r--r--drivers/usb/host/ehci-q.c30
-rw-r--r--drivers/usb/host/ehci-sched.c3
-rw-r--r--drivers/usb/host/ehci-timer.c5
-rw-r--r--drivers/usb/host/ehci-w90x900.c30
-rw-r--r--drivers/usb/host/ehci.h8
-rw-r--r--drivers/usb/host/fotg210-hcd.c7
-rw-r--r--drivers/usb/host/fsl-mph-dr-of.c2
-rw-r--r--drivers/usb/host/isp1362-hcd.c27
-rw-r--r--drivers/usb/host/ohci-at91.c129
-rw-r--r--drivers/usb/host/ohci-da8xx.c522
-rw-r--r--drivers/usb/host/ohci-exynos.c2
-rw-r--r--drivers/usb/host/ohci-hcd.c20
-rw-r--r--drivers/usb/host/ohci-hub.c26
-rw-r--r--drivers/usb/host/ohci-mem.c6
-rw-r--r--drivers/usb/host/ohci-nxp.c7
-rw-r--r--drivers/usb/host/ohci-omap.c42
-rw-r--r--drivers/usb/host/ohci-pxa27x.c36
-rw-r--r--drivers/usb/host/ohci-s3c2410.c47
-rw-r--r--drivers/usb/host/oxu210hp-hcd.c2
-rw-r--r--drivers/usb/host/uhci-hcd.c2
-rw-r--r--drivers/usb/host/uhci-pci.c4
-rw-r--r--drivers/usb/host/xhci-dbg.c24
-rw-r--r--drivers/usb/host/xhci-ext-caps.h2
-rw-r--r--drivers/usb/host/xhci-hub.c14
-rw-r--r--drivers/usb/host/xhci-mem.c92
-rw-r--r--drivers/usb/host/xhci-mtk-sch.c4
-rw-r--r--drivers/usb/host/xhci-mtk.c71
-rw-r--r--drivers/usb/host/xhci-mtk.h2
-rw-r--r--drivers/usb/host/xhci-pci.c9
-rw-r--r--drivers/usb/host/xhci-plat.c21
-rw-r--r--drivers/usb/host/xhci-rcar.c4
-rw-r--r--drivers/usb/host/xhci-rcar.h1
-rw-r--r--drivers/usb/host/xhci-ring.c1272
-rw-r--r--drivers/usb/host/xhci-tegra.c1
-rw-r--r--drivers/usb/host/xhci-trace.h184
-rw-r--r--drivers/usb/host/xhci.c318
-rw-r--r--drivers/usb/host/xhci.h546
-rw-r--r--drivers/usb/image/mdc800.c2
-rw-r--r--drivers/usb/isp1760/isp1760-if.c2
-rw-r--r--drivers/usb/isp1760/isp1760-udc.c2
-rw-r--r--drivers/usb/misc/Kconfig9
-rw-r--r--drivers/usb/misc/Makefile1
-rw-r--r--drivers/usb/misc/adutux.c11
-rw-r--r--drivers/usb/misc/chaoskey.c14
-rw-r--r--drivers/usb/misc/ftdi-elan.c2
-rw-r--r--drivers/usb/misc/idmouse.c6
-rw-r--r--drivers/usb/misc/iowarrior.c21
-rw-r--r--drivers/usb/misc/ldusb.c2
-rw-r--r--drivers/usb/misc/legousbtower.c4
-rw-r--r--drivers/usb/misc/lvstest.c4
-rw-r--r--drivers/usb/misc/rio500.c4
-rw-r--r--drivers/usb/misc/sisusbvga/sisusb.c6
-rw-r--r--drivers/usb/misc/sisusbvga/sisusb_con.c55
-rw-r--r--drivers/usb/misc/usb251xb.c594
-rw-r--r--drivers/usb/misc/usbtest.c8
-rw-r--r--drivers/usb/misc/uss720.c14
-rw-r--r--drivers/usb/mon/mon_bin.c7
-rw-r--r--drivers/usb/mon/mon_main.c2
-rw-r--r--drivers/usb/mon/mon_stat.c2
-rw-r--r--drivers/usb/mon/mon_text.c3
-rw-r--r--drivers/usb/mtu3/Kconfig54
-rw-r--r--drivers/usb/mtu3/Makefile18
-rw-r--r--drivers/usb/mtu3/mtu3.h418
-rw-r--r--drivers/usb/mtu3/mtu3_core.c863
-rw-r--r--drivers/usb/mtu3/mtu3_dr.c379
-rw-r--r--drivers/usb/mtu3/mtu3_dr.h108
-rw-r--r--drivers/usb/mtu3/mtu3_gadget.c730
-rw-r--r--drivers/usb/mtu3/mtu3_gadget_ep0.c881
-rw-r--r--drivers/usb/mtu3/mtu3_host.c294
-rw-r--r--drivers/usb/mtu3/mtu3_hw_regs.h473
-rw-r--r--drivers/usb/mtu3/mtu3_plat.c506
-rw-r--r--drivers/usb/mtu3/mtu3_qmu.c573
-rw-r--r--drivers/usb/mtu3/mtu3_qmu.h43
-rw-r--r--drivers/usb/musb/am35x.c1
-rw-r--r--drivers/usb/musb/blackfin.c7
-rw-r--r--drivers/usb/musb/cppi_dma.c26
-rw-r--r--drivers/usb/musb/cppi_dma.h1
-rw-r--r--drivers/usb/musb/da8xx.c106
-rw-r--r--drivers/usb/musb/davinci.c1
-rw-r--r--drivers/usb/musb/jz4740.c2
-rw-r--r--drivers/usb/musb/musb_core.c87
-rw-r--r--drivers/usb/musb/musb_core.h15
-rw-r--r--drivers/usb/musb/musb_cppi41.c81
-rw-r--r--drivers/usb/musb/musb_debugfs.c68
-rw-r--r--drivers/usb/musb/musb_dma.h5
-rw-r--r--drivers/usb/musb/musb_dsps.c217
-rw-r--r--drivers/usb/musb/musb_gadget.c8
-rw-r--r--drivers/usb/musb/musb_host.c12
-rw-r--r--drivers/usb/musb/musb_virthub.c1
-rw-r--r--drivers/usb/musb/musbhsdma.h2
-rw-r--r--drivers/usb/musb/omap2430.c8
-rw-r--r--drivers/usb/musb/sunxi.c74
-rw-r--r--drivers/usb/musb/tusb6010_omap.c7
-rw-r--r--drivers/usb/musb/ux500.c2
-rw-r--r--drivers/usb/phy/Kconfig1
-rw-r--r--drivers/usb/phy/phy-ab8500-usb.c33
-rw-r--r--drivers/usb/phy/phy-am335x-control.c2
-rw-r--r--drivers/usb/phy/phy-fsl-usb.c12
-rw-r--r--drivers/usb/phy/phy-generic.c9
-rw-r--r--drivers/usb/phy/phy-isp1301-omap.c2
-rw-r--r--drivers/usb/phy/phy-isp1301.c7
-rw-r--r--drivers/usb/phy/phy-msm-usb.c51
-rw-r--r--drivers/usb/phy/phy-omap-otg.c24
-rw-r--r--drivers/usb/phy/phy-qcom-8x16-usb.c13
-rw-r--r--drivers/usb/phy/phy-tahvo.c10
-rw-r--r--drivers/usb/phy/phy-twl6030-usb.c23
-rw-r--r--drivers/usb/renesas_usbhs/common.c2
-rw-r--r--drivers/usb/renesas_usbhs/fifo.c5
-rw-r--r--drivers/usb/renesas_usbhs/mod_host.c2
-rw-r--r--drivers/usb/serial/Kconfig19
-rw-r--r--drivers/usb/serial/Makefile2
-rw-r--r--drivers/usb/serial/ark3116.c74
-rw-r--r--drivers/usb/serial/ch341.c287
-rw-r--r--drivers/usb/serial/console.c4
-rw-r--r--drivers/usb/serial/cp210x.c416
-rw-r--r--drivers/usb/serial/cyberjack.c10
-rw-r--r--drivers/usb/serial/cypress_m8.c5
-rw-r--r--drivers/usb/serial/digi_acceleport.c53
-rw-r--r--drivers/usb/serial/f81534.c1411
-rw-r--r--drivers/usb/serial/ftdi_sio.c62
-rw-r--r--drivers/usb/serial/garmin_gps.c1
-rw-r--r--drivers/usb/serial/generic.c1
-rw-r--r--drivers/usb/serial/io_edgeport.c274
-rw-r--r--drivers/usb/serial/io_tables.h232
-rw-r--r--drivers/usb/serial/io_ti.c34
-rw-r--r--drivers/usb/serial/iuu_phoenix.c26
-rw-r--r--drivers/usb/serial/keyspan.c592
-rw-r--r--drivers/usb/serial/keyspan.h629
-rw-r--r--drivers/usb/serial/keyspan_pda.c33
-rw-r--r--drivers/usb/serial/kl5kusb105.c159
-rw-r--r--drivers/usb/serial/kobil_sct.c12
-rw-r--r--drivers/usb/serial/mct_u232.c6
-rw-r--r--drivers/usb/serial/metro-usb.c42
-rw-r--r--drivers/usb/serial/mos7720.c69
-rw-r--r--drivers/usb/serial/mos7840.c49
-rw-r--r--drivers/usb/serial/omninet.c26
-rw-r--r--drivers/usb/serial/opticon.c6
-rw-r--r--drivers/usb/serial/option.c25
-rw-r--r--drivers/usb/serial/oti6858.c16
-rw-r--r--drivers/usb/serial/pl2303.c17
-rw-r--r--drivers/usb/serial/pl2303.h1
-rw-r--r--drivers/usb/serial/qcserial.c3
-rw-r--r--drivers/usb/serial/quatech2.c32
-rw-r--r--drivers/usb/serial/safe_serial.c5
-rw-r--r--drivers/usb/serial/sierra.c28
-rw-r--r--drivers/usb/serial/spcp8x5.c22
-rw-r--r--drivers/usb/serial/ssu100.c35
-rw-r--r--drivers/usb/serial/ti_usb_3410_5052.c22
-rw-r--r--drivers/usb/serial/upd78f0730.c441
-rw-r--r--drivers/usb/serial/usb_wwan.c3
-rw-r--r--drivers/usb/serial/whiteheat.c1
-rw-r--r--drivers/usb/storage/ene_ub6250.c2
-rw-r--r--drivers/usb/storage/sddr09.c4
-rw-r--r--drivers/usb/storage/unusual_devs.h21
-rw-r--r--drivers/usb/storage/usb.c1
-rw-r--r--drivers/usb/usbip/usbip_common.c34
-rw-r--r--drivers/usb/usbip/usbip_common.h1
-rw-r--r--drivers/usb/usbip/vhci_hcd.c4
-rw-r--r--drivers/usb/usbip/vhci_sysfs.c1
-rw-r--r--drivers/usb/usbip/vudc_dev.c45
-rw-r--r--drivers/usb/usbip/vudc_transfer.c8
-rw-r--r--drivers/usb/wusbcore/crypto.c3
-rw-r--r--drivers/usb/wusbcore/dev-sysfs.c6
-rw-r--r--drivers/usb/wusbcore/security.c1
-rw-r--r--drivers/usb/wusbcore/wa-hc.c3
-rw-r--r--drivers/usb/wusbcore/wa-nep.c1
-rw-r--r--drivers/usb/wusbcore/wa-xfer.c1
-rw-r--r--drivers/usb/wusbcore/wusbhc.c13
306 files changed, 18801 insertions, 7644 deletions
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 644e978cbd3e..fbe493d44e81 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -95,6 +95,8 @@ source "drivers/usb/usbip/Kconfig"
endif
+source "drivers/usb/mtu3/Kconfig"
+
source "drivers/usb/musb/Kconfig"
source "drivers/usb/dwc3/Kconfig"
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index dca78565eb55..7791af6c102c 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_USB_DWC2) += dwc2/
obj-$(CONFIG_USB_ISP1760) += isp1760/
obj-$(CONFIG_USB_MON) += mon/
+obj-$(CONFIG_USB_MTU3) += mtu3/
obj-$(CONFIG_PCI) += host/
obj-$(CONFIG_USB_EHCI_HCD) += host/
diff --git a/drivers/usb/atm/usbatm.c b/drivers/usb/atm/usbatm.c
index 4dec9df8764b..3e80aa3b917a 100644
--- a/drivers/usb/atm/usbatm.c
+++ b/drivers/usb/atm/usbatm.c
@@ -64,7 +64,7 @@
#include "usbatm.h"
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <linux/crc32.h>
#include <linux/errno.h>
#include <linux/init.h>
@@ -74,7 +74,7 @@
#include <linux/moduleparam.h>
#include <linux/netdevice.h>
#include <linux/proc_fs.h>
-#include <linux/sched.h>
+#include <linux/sched/signal.h>
#include <linux/signal.h>
#include <linux/slab.h>
#include <linux/stat.h>
diff --git a/drivers/usb/chipidea/Kconfig b/drivers/usb/chipidea/Kconfig
index 5e5b9eb7ebf6..fc96f5cdcb5c 100644
--- a/drivers/usb/chipidea/Kconfig
+++ b/drivers/usb/chipidea/Kconfig
@@ -2,6 +2,7 @@ config USB_CHIPIDEA
tristate "ChipIdea Highspeed Dual Role Controller"
depends on ((USB_EHCI_HCD && USB_GADGET) || (USB_EHCI_HCD && !USB_GADGET) || (!USB_EHCI_HCD && USB_GADGET)) && HAS_DMA
select EXTCON
+ select RESET_CONTROLLER
help
Say Y here if your system has a dual role high speed USB
controller based on ChipIdea silicon IP. It supports:
@@ -38,4 +39,11 @@ config USB_CHIPIDEA_HOST
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 518e445476c3..39fca5715ed3 100644
--- a/drivers/usb/chipidea/Makefile
+++ b/drivers/usb/chipidea/Makefile
@@ -4,6 +4,7 @@ ci_hdrc-y := core.o otg.o debug.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 cd414559040f..59e22389c10b 100644
--- a/drivers/usb/chipidea/ci.h
+++ b/drivers/usb/chipidea/ci.h
@@ -18,6 +18,8 @@
#include <linux/usb.h>
#include <linux/usb/gadget.h>
#include <linux/usb/otg-fsm.h>
+#include <linux/usb/otg.h>
+#include <linux/ulpi/interface.h>
/******************************************************************************
* DEFINE
@@ -52,6 +54,7 @@ enum ci_hw_regs {
OP_ENDPTLISTADDR,
OP_TTCTRL,
OP_BURSTSIZE,
+ OP_ULPI_VIEWPORT,
OP_PORTSC,
OP_DEVLC,
OP_OTGSC,
@@ -187,6 +190,8 @@ struct hw_bank {
* @test_mode: the selected test mode
* @platdata: platform specific information supplied by parent device
* @vbus_active: is VBUS active
+ * @ulpi: pointer to ULPI device, if any
+ * @ulpi_ops: ULPI read/write ops for this device
* @phy: pointer to PHY, if any
* @usb_phy: pointer to USB PHY, if any and if using the USB PHY framework
* @hcd: pointer to usb_hcd for ehci host driver
@@ -236,6 +241,10 @@ 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;
@@ -418,6 +427,16 @@ 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);
u32 hw_read_intr_status(struct ci_hdrc *ci);
@@ -428,8 +447,7 @@ int hw_port_test_set(struct ci_hdrc *ci, u8 mode);
u8 hw_port_test_get(struct ci_hdrc *ci);
-int hw_wait_reg(struct ci_hdrc *ci, enum ci_hw_regs reg, u32 mask,
- u32 value, unsigned int timeout_ms);
+void hw_phymode_configure(struct ci_hdrc *ci);
void ci_platform_configure(struct ci_hdrc *ci);
diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c
index 099179457f60..5f4a8157fad8 100644
--- a/drivers/usb/chipidea/ci_hdrc_imx.c
+++ b/drivers/usb/chipidea/ci_hdrc_imx.c
@@ -18,6 +18,7 @@
#include <linux/pm_runtime.h>
#include <linux/dma-mapping.h>
#include <linux/usb/chipidea.h>
+#include <linux/usb/of.h>
#include <linux/clk.h>
#include "ci.h"
@@ -146,6 +147,9 @@ static struct imx_usbmisc_data *usbmisc_get_init_data(struct device *dev)
if (of_find_property(np, "external-vbus-divider", NULL))
data->evdo = 1;
+ if (of_usb_get_phy_mode(np) == USBPHY_INTERFACE_MODE_ULPI)
+ data->ulpi = 1;
+
return data;
}
diff --git a/drivers/usb/chipidea/ci_hdrc_imx.h b/drivers/usb/chipidea/ci_hdrc_imx.h
index 409aa5ca8dda..d666c9f036ba 100644
--- a/drivers/usb/chipidea/ci_hdrc_imx.h
+++ b/drivers/usb/chipidea/ci_hdrc_imx.h
@@ -19,6 +19,7 @@ struct imx_usbmisc_data {
unsigned int disable_oc:1; /* over current detect disabled */
unsigned int oc_polarity:1; /* over current polarity if oc enabled */
unsigned int evdo:1; /* set external vbus divider option */
+ unsigned int ulpi:1; /* connected to an ULPI phy */
};
int imx_usbmisc_init(struct imx_usbmisc_data *);
diff --git a/drivers/usb/chipidea/ci_hdrc_msm.c b/drivers/usb/chipidea/ci_hdrc_msm.c
index 3889809fd0c4..0bdfcdcbf7a5 100644
--- a/drivers/usb/chipidea/ci_hdrc_msm.c
+++ b/drivers/usb/chipidea/ci_hdrc_msm.c
@@ -8,90 +8,292 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
-#include <linux/usb/msm_hsusb_hw.h>
-#include <linux/usb/ulpi.h>
-#include <linux/usb/gadget.h>
#include <linux/usb/chipidea.h>
+#include <linux/clk.h>
+#include <linux/reset.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <linux/io.h>
+#include <linux/reset-controller.h>
+#include <linux/extcon.h>
+#include <linux/of.h>
#include "ci.h"
-#define MSM_USB_BASE (ci->hw_bank.abs)
+#define HS_PHY_AHB_MODE 0x0098
-static void ci_hdrc_msm_notify_event(struct ci_hdrc *ci, unsigned event)
+#define HS_PHY_GENCONFIG 0x009c
+#define HS_PHY_TXFIFO_IDLE_FORCE_DIS BIT(4)
+
+#define HS_PHY_GENCONFIG_2 0x00a0
+#define HS_PHY_SESS_VLD_CTRL_EN BIT(7)
+#define HS_PHY_ULPI_TX_PKT_EN_CLR_FIX BIT(19)
+
+#define HSPHY_SESS_VLD_CTRL BIT(25)
+
+/* Vendor base starts at 0x200 beyond CI base */
+#define HS_PHY_CTRL 0x0040
+#define HS_PHY_SEC_CTRL 0x0078
+#define HS_PHY_DIG_CLAMP_N BIT(16)
+#define HS_PHY_POR_ASSERT BIT(0)
+
+struct ci_hdrc_msm {
+ struct platform_device *ci;
+ struct clk *core_clk;
+ struct clk *iface_clk;
+ struct clk *fs_clk;
+ struct ci_hdrc_platform_data pdata;
+ struct reset_controller_dev rcdev;
+ bool secondary_phy;
+ bool hsic;
+ void __iomem *base;
+};
+
+static int
+ci_hdrc_msm_por_reset(struct reset_controller_dev *r, unsigned long id)
{
- struct device *dev = ci->gadget.dev.parent;
+ struct ci_hdrc_msm *ci_msm = container_of(r, struct ci_hdrc_msm, rcdev);
+ void __iomem *addr = ci_msm->base;
+ u32 val;
+
+ if (id)
+ addr += HS_PHY_SEC_CTRL;
+ else
+ addr += HS_PHY_CTRL;
+
+ val = readl_relaxed(addr);
+ val |= HS_PHY_POR_ASSERT;
+ writel(val, addr);
+ /*
+ * wait for minimum 10 microseconds as suggested by manual.
+ * Use a slightly larger value since the exact value didn't
+ * work 100% of the time.
+ */
+ udelay(12);
+ val &= ~HS_PHY_POR_ASSERT;
+ writel(val, addr);
+
+ return 0;
+}
+
+static const struct reset_control_ops ci_hdrc_msm_reset_ops = {
+ .reset = ci_hdrc_msm_por_reset,
+};
+
+static int ci_hdrc_msm_notify_event(struct ci_hdrc *ci, unsigned event)
+{
+ struct device *dev = ci->dev->parent;
+ struct ci_hdrc_msm *msm_ci = dev_get_drvdata(dev);
+ int ret;
switch (event) {
case CI_HDRC_CONTROLLER_RESET_EVENT:
dev_dbg(dev, "CI_HDRC_CONTROLLER_RESET_EVENT received\n");
- writel(0, USB_AHBBURST);
+
+ hw_phymode_configure(ci);
+ if (msm_ci->secondary_phy) {
+ u32 val = readl_relaxed(msm_ci->base + HS_PHY_SEC_CTRL);
+ val |= HS_PHY_DIG_CLAMP_N;
+ writel_relaxed(val, msm_ci->base + HS_PHY_SEC_CTRL);
+ }
+
+ ret = phy_init(ci->phy);
+ if (ret)
+ return ret;
+
+ ret = phy_power_on(ci->phy);
+ if (ret) {
+ phy_exit(ci->phy);
+ return ret;
+ }
+
/* use AHB transactor, allow posted data writes */
- writel(0x8, USB_AHBMODE);
- usb_phy_init(ci->usb_phy);
+ hw_write_id_reg(ci, HS_PHY_AHB_MODE, 0xffffffff, 0x8);
+
+ /* workaround for rx buffer collision issue */
+ hw_write_id_reg(ci, HS_PHY_GENCONFIG,
+ HS_PHY_TXFIFO_IDLE_FORCE_DIS, 0);
+
+ if (!msm_ci->hsic)
+ hw_write_id_reg(ci, HS_PHY_GENCONFIG_2,
+ HS_PHY_ULPI_TX_PKT_EN_CLR_FIX, 0);
+
+ if (!IS_ERR(ci->platdata->vbus_extcon.edev)) {
+ hw_write_id_reg(ci, HS_PHY_GENCONFIG_2,
+ HS_PHY_SESS_VLD_CTRL_EN,
+ HS_PHY_SESS_VLD_CTRL_EN);
+ hw_write(ci, OP_USBCMD, HSPHY_SESS_VLD_CTRL,
+ HSPHY_SESS_VLD_CTRL);
+
+ }
break;
case CI_HDRC_CONTROLLER_STOPPED_EVENT:
dev_dbg(dev, "CI_HDRC_CONTROLLER_STOPPED_EVENT received\n");
- /*
- * Put the phy in non-driving mode. Otherwise host
- * may not detect soft-disconnection.
- */
- usb_phy_notify_disconnect(ci->usb_phy, USB_SPEED_UNKNOWN);
+ phy_power_off(ci->phy);
+ phy_exit(ci->phy);
break;
default:
dev_dbg(dev, "unknown ci_hdrc event\n");
break;
}
+
+ return 0;
}
-static struct ci_hdrc_platform_data ci_hdrc_msm_platdata = {
- .name = "ci_hdrc_msm",
- .capoffset = DEF_CAPOFFSET,
- .flags = CI_HDRC_REGS_SHARED |
- CI_HDRC_DISABLE_STREAMING,
+static int ci_hdrc_msm_mux_phy(struct ci_hdrc_msm *ci,
+ struct platform_device *pdev)
+{
+ struct regmap *regmap;
+ struct device *dev = &pdev->dev;
+ struct of_phandle_args args;
+ u32 val;
+ int ret;
- .notify_event = ci_hdrc_msm_notify_event,
-};
+ ret = of_parse_phandle_with_fixed_args(dev->of_node, "phy-select", 2, 0,
+ &args);
+ if (ret)
+ return 0;
+
+ regmap = syscon_node_to_regmap(args.np);
+ of_node_put(args.np);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ ret = regmap_write(regmap, args.args[0], args.args[1]);
+ if (ret)
+ return ret;
+
+ ci->secondary_phy = !!args.args[1];
+ if (ci->secondary_phy) {
+ val = readl_relaxed(ci->base + HS_PHY_SEC_CTRL);
+ val |= HS_PHY_DIG_CLAMP_N;
+ writel_relaxed(val, ci->base + HS_PHY_SEC_CTRL);
+ }
+
+ return 0;
+}
static int ci_hdrc_msm_probe(struct platform_device *pdev)
{
+ struct ci_hdrc_msm *ci;
struct platform_device *plat_ci;
- struct usb_phy *phy;
+ struct clk *clk;
+ struct reset_control *reset;
+ struct resource *res;
+ int ret;
+ struct device_node *ulpi_node, *phy_node;
dev_dbg(&pdev->dev, "ci_hdrc_msm_probe\n");
- /*
- * OTG(PHY) driver takes care of PHY initialization, clock management,
- * powering up VBUS, mapping of registers address space and power
- * management.
- */
- phy = devm_usb_get_phy_by_phandle(&pdev->dev, "usb-phy", 0);
- if (IS_ERR(phy))
- return PTR_ERR(phy);
+ ci = devm_kzalloc(&pdev->dev, sizeof(*ci), GFP_KERNEL);
+ if (!ci)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, ci);
+
+ ci->pdata.name = "ci_hdrc_msm";
+ ci->pdata.capoffset = DEF_CAPOFFSET;
+ ci->pdata.flags = CI_HDRC_REGS_SHARED | CI_HDRC_DISABLE_STREAMING |
+ CI_HDRC_OVERRIDE_AHB_BURST |
+ CI_HDRC_OVERRIDE_PHY_CONTROL;
+ ci->pdata.notify_event = ci_hdrc_msm_notify_event;
+
+ reset = devm_reset_control_get(&pdev->dev, "core");
+ if (IS_ERR(reset))
+ return PTR_ERR(reset);
+
+ ci->core_clk = clk = devm_clk_get(&pdev->dev, "core");
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
- ci_hdrc_msm_platdata.usb_phy = phy;
+ ci->iface_clk = clk = devm_clk_get(&pdev->dev, "iface");
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
- plat_ci = ci_hdrc_add_device(&pdev->dev,
- pdev->resource, pdev->num_resources,
- &ci_hdrc_msm_platdata);
+ ci->fs_clk = clk = devm_clk_get(&pdev->dev, "fs");
+ if (IS_ERR(clk)) {
+ if (PTR_ERR(clk) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ ci->fs_clk = NULL;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ ci->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(ci->base))
+ return PTR_ERR(ci->base);
+
+ ci->rcdev.owner = THIS_MODULE;
+ ci->rcdev.ops = &ci_hdrc_msm_reset_ops;
+ ci->rcdev.of_node = pdev->dev.of_node;
+ ci->rcdev.nr_resets = 2;
+ ret = reset_controller_register(&ci->rcdev);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(ci->fs_clk);
+ if (ret)
+ goto err_fs;
+
+ reset_control_assert(reset);
+ usleep_range(10000, 12000);
+ reset_control_deassert(reset);
+
+ clk_disable_unprepare(ci->fs_clk);
+
+ ret = clk_prepare_enable(ci->core_clk);
+ if (ret)
+ goto err_fs;
+
+ ret = clk_prepare_enable(ci->iface_clk);
+ if (ret)
+ goto err_iface;
+
+ ret = ci_hdrc_msm_mux_phy(ci, pdev);
+ if (ret)
+ goto err_mux;
+
+ ulpi_node = of_find_node_by_name(pdev->dev.of_node, "ulpi");
+ if (ulpi_node) {
+ phy_node = of_get_next_available_child(ulpi_node, NULL);
+ ci->hsic = of_device_is_compatible(phy_node, "qcom,usb-hsic-phy");
+ of_node_put(phy_node);
+ }
+ of_node_put(ulpi_node);
+
+ plat_ci = ci_hdrc_add_device(&pdev->dev, pdev->resource,
+ pdev->num_resources, &ci->pdata);
if (IS_ERR(plat_ci)) {
- dev_err(&pdev->dev, "ci_hdrc_add_device failed!\n");
- return PTR_ERR(plat_ci);
+ ret = PTR_ERR(plat_ci);
+ if (ret != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "ci_hdrc_add_device failed!\n");
+ goto err_mux;
}
- platform_set_drvdata(pdev, plat_ci);
+ ci->ci = plat_ci;
+ pm_runtime_set_active(&pdev->dev);
pm_runtime_no_callbacks(&pdev->dev);
pm_runtime_enable(&pdev->dev);
return 0;
+
+err_mux:
+ clk_disable_unprepare(ci->iface_clk);
+err_iface:
+ clk_disable_unprepare(ci->core_clk);
+err_fs:
+ reset_controller_unregister(&ci->rcdev);
+ return ret;
}
static int ci_hdrc_msm_remove(struct platform_device *pdev)
{
- struct platform_device *plat_ci = platform_get_drvdata(pdev);
+ struct ci_hdrc_msm *ci = platform_get_drvdata(pdev);
pm_runtime_disable(&pdev->dev);
- ci_hdrc_remove_device(plat_ci);
+ ci_hdrc_remove_device(ci->ci);
+ clk_disable_unprepare(ci->iface_clk);
+ clk_disable_unprepare(ci->core_clk);
+ reset_controller_unregister(&ci->rcdev);
return 0;
}
diff --git a/drivers/usb/chipidea/ci_hdrc_usb2.c b/drivers/usb/chipidea/ci_hdrc_usb2.c
index 4456d2cf80ff..d162cc0bb8ce 100644
--- a/drivers/usb/chipidea/ci_hdrc_usb2.c
+++ b/drivers/usb/chipidea/ci_hdrc_usb2.c
@@ -74,10 +74,6 @@ static int ci_hdrc_usb2_probe(struct platform_device *pdev)
}
}
- ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
- if (ret)
- goto clk_err;
-
ci_pdata->name = dev_name(dev);
priv->ci_pdev = ci_hdrc_add_device(dev, pdev->resource,
diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c
index 3dbb4a21ab44..79ad8e91632e 100644
--- a/drivers/usb/chipidea/core.c
+++ b/drivers/usb/chipidea/core.c
@@ -62,7 +62,6 @@
#include <linux/usb/chipidea.h>
#include <linux/usb/of.h>
#include <linux/of.h>
-#include <linux/phy.h>
#include <linux/regulator/consumer.h>
#include <linux/usb/ehci_def.h>
@@ -86,6 +85,7 @@ static const u8 ci_regs_nolpm[] = {
[OP_ENDPTLISTADDR] = 0x18U,
[OP_TTCTRL] = 0x1CU,
[OP_BURSTSIZE] = 0x20U,
+ [OP_ULPI_VIEWPORT] = 0x30U,
[OP_PORTSC] = 0x44U,
[OP_DEVLC] = 0x84U,
[OP_OTGSC] = 0x64U,
@@ -110,6 +110,7 @@ static const u8 ci_regs_lpm[] = {
[OP_ENDPTLISTADDR] = 0x18U,
[OP_TTCTRL] = 0x1CU,
[OP_BURSTSIZE] = 0x20U,
+ [OP_ULPI_VIEWPORT] = 0x30U,
[OP_PORTSC] = 0x44U,
[OP_DEVLC] = 0x84U,
[OP_OTGSC] = 0xC4U,
@@ -285,7 +286,7 @@ static int hw_device_init(struct ci_hdrc *ci, void __iomem *base)
return 0;
}
-static void hw_phymode_configure(struct ci_hdrc *ci)
+void hw_phymode_configure(struct ci_hdrc *ci)
{
u32 portsc, lpm, sts = 0;
@@ -325,6 +326,7 @@ static void hw_phymode_configure(struct ci_hdrc *ci)
hw_write(ci, OP_PORTSC, PORTSC_STS, PORTSC_STS);
}
}
+EXPORT_SYMBOL_GPL(hw_phymode_configure);
/**
* _ci_usb_phy_init: initialize phy taking in account both phy and usb_phy
@@ -361,6 +363,9 @@ static int _ci_usb_phy_init(struct ci_hdrc *ci)
*/
static void ci_usb_phy_exit(struct ci_hdrc *ci)
{
+ if (ci->platdata->flags & CI_HDRC_OVERRIDE_PHY_CONTROL)
+ return;
+
if (ci->phy) {
phy_power_off(ci->phy);
phy_exit(ci->phy);
@@ -379,6 +384,9 @@ static int ci_usb_phy_init(struct ci_hdrc *ci)
{
int ret;
+ if (ci->platdata->flags & CI_HDRC_OVERRIDE_PHY_CONTROL)
+ return 0;
+
switch (ci->platdata->phy_mode) {
case USBPHY_INTERFACE_MODE_UTMI:
case USBPHY_INTERFACE_MODE_UTMIW:
@@ -419,13 +427,21 @@ void ci_platform_configure(struct ci_hdrc *ci)
is_device_mode = hw_read(ci, OP_USBMODE, USBMODE_CM) == USBMODE_CM_DC;
is_host_mode = hw_read(ci, OP_USBMODE, USBMODE_CM) == USBMODE_CM_HC;
- if (is_device_mode &&
- (ci->platdata->flags & CI_HDRC_DISABLE_DEVICE_STREAMING))
- hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, USBMODE_CI_SDIS);
+ if (is_device_mode) {
+ phy_set_mode(ci->phy, PHY_MODE_USB_DEVICE);
+
+ if (ci->platdata->flags & CI_HDRC_DISABLE_DEVICE_STREAMING)
+ hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS,
+ USBMODE_CI_SDIS);
+ }
+
+ if (is_host_mode) {
+ phy_set_mode(ci->phy, PHY_MODE_USB_HOST);
- if (is_host_mode &&
- (ci->platdata->flags & CI_HDRC_DISABLE_HOST_STREAMING))
- hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, USBMODE_CI_SDIS);
+ if (ci->platdata->flags & CI_HDRC_DISABLE_HOST_STREAMING)
+ hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS,
+ USBMODE_CI_SDIS);
+ }
if (ci->platdata->flags & CI_HDRC_FORCE_FULLSPEED) {
if (ci->hw_bank.lpm)
@@ -495,9 +511,12 @@ int hw_device_reset(struct ci_hdrc *ci)
return ret;
}
- if (ci->platdata->notify_event)
- ci->platdata->notify_event(ci,
+ if (ci->platdata->notify_event) {
+ ret = ci->platdata->notify_event(ci,
CI_HDRC_CONTROLLER_RESET_EVENT);
+ if (ret)
+ return ret;
+ }
/* USBMODE should be configured step by step */
hw_write(ci, OP_USBMODE, USBMODE_CM, USBMODE_CM_IDLE);
@@ -516,38 +535,6 @@ int hw_device_reset(struct ci_hdrc *ci)
return 0;
}
-/**
- * hw_wait_reg: wait the register value
- *
- * Sometimes, it needs to wait register value before going on.
- * Eg, when switch to device mode, the vbus value should be lower
- * than OTGSC_BSV before connects to host.
- *
- * @ci: the controller
- * @reg: register index
- * @mask: mast bit
- * @value: the bit value to wait
- * @timeout_ms: timeout in millisecond
- *
- * This function returns an error code if timeout
- */
-int hw_wait_reg(struct ci_hdrc *ci, enum ci_hw_regs reg, u32 mask,
- u32 value, unsigned int timeout_ms)
-{
- unsigned long elapse = jiffies + msecs_to_jiffies(timeout_ms);
-
- while (hw_read(ci, reg, mask) != value) {
- if (time_after(jiffies, elapse)) {
- dev_err(ci->dev, "timeout waiting for %08x in %d\n",
- mask, reg);
- return -ETIMEDOUT;
- }
- msleep(20);
- }
-
- return 0;
-}
-
static irqreturn_t ci_irq(int irq, void *data)
{
struct ci_hdrc *ci = data;
@@ -601,35 +588,14 @@ static irqreturn_t ci_irq(int irq, void *data)
return ret;
}
-static int ci_vbus_notifier(struct notifier_block *nb, unsigned long event,
- void *ptr)
-{
- struct ci_hdrc_cable *vbus = container_of(nb, struct ci_hdrc_cable, nb);
- struct ci_hdrc *ci = vbus->ci;
-
- if (event)
- vbus->state = true;
- else
- vbus->state = false;
-
- vbus->changed = true;
-
- ci_irq(ci->irq, ci);
- return NOTIFY_DONE;
-}
-
-static int ci_id_notifier(struct notifier_block *nb, unsigned long event,
- void *ptr)
+static int ci_cable_notifier(struct notifier_block *nb, unsigned long event,
+ void *ptr)
{
- struct ci_hdrc_cable *id = container_of(nb, struct ci_hdrc_cable, nb);
- struct ci_hdrc *ci = id->ci;
+ struct ci_hdrc_cable *cbl = container_of(nb, struct ci_hdrc_cable, nb);
+ struct ci_hdrc *ci = cbl->ci;
- if (event)
- id->state = false;
- else
- id->state = true;
-
- id->changed = true;
+ cbl->connected = event;
+ cbl->changed = true;
ci_irq(ci->irq, ci);
return NOTIFY_DONE;
@@ -738,27 +704,27 @@ static int ci_get_platdata(struct device *dev,
}
cable = &platdata->vbus_extcon;
- cable->nb.notifier_call = ci_vbus_notifier;
+ cable->nb.notifier_call = ci_cable_notifier;
cable->edev = ext_vbus;
if (!IS_ERR(ext_vbus)) {
- ret = extcon_get_cable_state_(cable->edev, EXTCON_USB);
+ ret = extcon_get_state(cable->edev, EXTCON_USB);
if (ret)
- cable->state = true;
+ cable->connected = true;
else
- cable->state = false;
+ cable->connected = false;
}
cable = &platdata->id_extcon;
- cable->nb.notifier_call = ci_id_notifier;
+ cable->nb.notifier_call = ci_cable_notifier;
cable->edev = ext_id;
if (!IS_ERR(ext_id)) {
- ret = extcon_get_cable_state_(cable->edev, EXTCON_USB_HOST);
+ ret = extcon_get_state(cable->edev, EXTCON_USB_HOST);
if (ret)
- cable->state = false;
+ cable->connected = true;
else
- cable->state = true;
+ cable->connected = false;
}
return 0;
}
@@ -771,8 +737,8 @@ static int ci_extcon_register(struct ci_hdrc *ci)
id = &ci->platdata->id_extcon;
id->ci = ci;
if (!IS_ERR(id->edev)) {
- ret = extcon_register_notifier(id->edev, EXTCON_USB_HOST,
- &id->nb);
+ ret = devm_extcon_register_notifier(ci->dev, id->edev,
+ EXTCON_USB_HOST, &id->nb);
if (ret < 0) {
dev_err(ci->dev, "register ID failed\n");
return ret;
@@ -782,11 +748,9 @@ static int ci_extcon_register(struct ci_hdrc *ci)
vbus = &ci->platdata->vbus_extcon;
vbus->ci = ci;
if (!IS_ERR(vbus->edev)) {
- ret = extcon_register_notifier(vbus->edev, EXTCON_USB,
- &vbus->nb);
+ ret = devm_extcon_register_notifier(ci->dev, vbus->edev,
+ EXTCON_USB, &vbus->nb);
if (ret < 0) {
- extcon_unregister_notifier(id->edev, EXTCON_USB_HOST,
- &id->nb);
dev_err(ci->dev, "register VBUS failed\n");
return ret;
}
@@ -795,20 +759,6 @@ static int ci_extcon_register(struct ci_hdrc *ci)
return 0;
}
-static void ci_extcon_unregister(struct ci_hdrc *ci)
-{
- struct ci_hdrc_cable *cable;
-
- cable = &ci->platdata->id_extcon;
- if (!IS_ERR(cable->edev))
- extcon_unregister_notifier(cable->edev, EXTCON_USB_HOST,
- &cable->nb);
-
- cable = &ci->platdata->vbus_extcon;
- if (!IS_ERR(cable->edev))
- extcon_unregister_notifier(cable->edev, EXTCON_USB, &cable->nb);
-}
-
static DEFINE_IDA(ci_ida);
struct platform_device *ci_hdrc_add_device(struct device *dev,
@@ -921,6 +871,7 @@ static int ci_hdrc_probe(struct platform_device *pdev)
CI_HDRC_IMX28_WRITE_FIX);
ci->supports_runtime_pm = !!(ci->platdata->flags &
CI_HDRC_SUPPORTS_RUNTIME_PM);
+ platform_set_drvdata(pdev, ci);
ret = hw_device_init(ci, base);
if (ret < 0) {
@@ -928,6 +879,10 @@ static int ci_hdrc_probe(struct platform_device *pdev)
return -ENODEV;
}
+ ret = ci_ulpi_init(ci);
+ if (ret)
+ return ret;
+
if (ci->platdata->phy) {
ci->phy = ci->platdata->phy;
} else if (ci->platdata->usb_phy) {
@@ -938,11 +893,15 @@ static int ci_hdrc_probe(struct platform_device *pdev)
/* if both generic PHY and USB PHY layers aren't enabled */
if (PTR_ERR(ci->phy) == -ENOSYS &&
- PTR_ERR(ci->usb_phy) == -ENXIO)
- return -ENXIO;
+ PTR_ERR(ci->usb_phy) == -ENXIO) {
+ ret = -ENXIO;
+ goto ulpi_exit;
+ }
- if (IS_ERR(ci->phy) && IS_ERR(ci->usb_phy))
- return -EPROBE_DEFER;
+ if (IS_ERR(ci->phy) && IS_ERR(ci->usb_phy)) {
+ ret = -EPROBE_DEFER;
+ goto ulpi_exit;
+ }
if (IS_ERR(ci->phy))
ci->phy = NULL;
@@ -1027,7 +986,6 @@ static int ci_hdrc_probe(struct platform_device *pdev)
}
}
- platform_set_drvdata(pdev, ci);
ret = devm_request_irq(dev, ci->irq, ci_irq, IRQF_SHARED,
ci->platdata->name, ci);
if (ret)
@@ -1054,11 +1012,12 @@ static int ci_hdrc_probe(struct platform_device *pdev)
if (!ret)
return 0;
- ci_extcon_unregister(ci);
stop:
ci_role_destroy(ci);
deinit_phy:
ci_usb_phy_exit(ci);
+ulpi_exit:
+ ci_ulpi_exit(ci);
return ret;
}
@@ -1074,10 +1033,10 @@ static int ci_hdrc_remove(struct platform_device *pdev)
}
dbg_remove_files(ci);
- ci_extcon_unregister(ci);
ci_role_destroy(ci);
ci_hdrc_enter_lpm(ci, true);
ci_usb_phy_exit(ci);
+ ci_ulpi_exit(ci);
return 0;
}
@@ -1125,6 +1084,7 @@ static void ci_controller_suspend(struct ci_hdrc *ci)
static int ci_controller_resume(struct device *dev)
{
struct ci_hdrc *ci = dev_get_drvdata(dev);
+ int ret;
dev_dbg(dev, "at %s\n", __func__);
@@ -1134,6 +1094,11 @@ static int ci_controller_resume(struct device *dev)
}
ci_hdrc_enter_lpm(ci, false);
+
+ ret = ci_ulpi_resume(ci);
+ if (ret)
+ return ret;
+
if (ci->usb_phy) {
usb_phy_set_suspend(ci->usb_phy, 0);
usb_phy_set_wakeup(ci->usb_phy, false);
diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c
index 111b0e0b8698..915f3e91586e 100644
--- a/drivers/usb/chipidea/host.c
+++ b/drivers/usb/chipidea/host.c
@@ -90,6 +90,13 @@ static int ehci_ci_reset(struct usb_hcd *hcd)
ehci->need_io_watchdog = 0;
+ if (ci->platdata->notify_event) {
+ ret = ci->platdata->notify_event(ci,
+ CI_HDRC_CONTROLLER_RESET_EVENT);
+ if (ret)
+ return ret;
+ }
+
ci_platform_configure(ci);
return ret;
@@ -187,6 +194,9 @@ static void host_stop(struct ci_hdrc *ci)
struct usb_hcd *hcd = ci->hcd;
if (hcd) {
+ if (ci->platdata->notify_event)
+ ci->platdata->notify_event(ci,
+ CI_HDRC_CONTROLLER_STOPPED_EVENT);
usb_remove_hcd(hcd);
ci->role = CI_ROLE_END;
synchronize_irq(ci->irq);
diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c
index 03b6743461d1..10236fe71522 100644
--- a/drivers/usb/chipidea/otg.c
+++ b/drivers/usb/chipidea/otg.c
@@ -44,12 +44,15 @@ u32 hw_read_otgsc(struct ci_hdrc *ci, u32 mask)
else
val &= ~OTGSC_BSVIS;
- cable->changed = false;
-
- if (cable->state)
+ if (cable->connected)
val |= OTGSC_BSV;
else
val &= ~OTGSC_BSV;
+
+ if (cable->enabled)
+ val |= OTGSC_BSVIE;
+ else
+ val &= ~OTGSC_BSVIE;
}
cable = &ci->platdata->id_extcon;
@@ -59,15 +62,18 @@ u32 hw_read_otgsc(struct ci_hdrc *ci, u32 mask)
else
val &= ~OTGSC_IDIS;
- cable->changed = false;
+ if (cable->connected)
+ val &= ~OTGSC_ID; /* host */
+ else
+ val |= OTGSC_ID; /* device */
- if (cable->state)
- val |= OTGSC_ID;
+ if (cable->enabled)
+ val |= OTGSC_IDIE;
else
- val &= ~OTGSC_ID;
+ val &= ~OTGSC_IDIE;
}
- return val;
+ return val & mask;
}
/**
@@ -77,6 +83,36 @@ u32 hw_read_otgsc(struct ci_hdrc *ci, u32 mask)
*/
void hw_write_otgsc(struct ci_hdrc *ci, u32 mask, u32 data)
{
+ struct ci_hdrc_cable *cable;
+
+ cable = &ci->platdata->vbus_extcon;
+ if (!IS_ERR(cable->edev)) {
+ if (data & mask & OTGSC_BSVIS)
+ cable->changed = false;
+
+ /* Don't enable vbus interrupt if using external notifier */
+ if (data & mask & OTGSC_BSVIE) {
+ cable->enabled = true;
+ data &= ~OTGSC_BSVIE;
+ } else if (mask & OTGSC_BSVIE) {
+ cable->enabled = false;
+ }
+ }
+
+ cable = &ci->platdata->id_extcon;
+ if (!IS_ERR(cable->edev)) {
+ if (data & mask & OTGSC_IDIS)
+ cable->changed = false;
+
+ /* Don't enable id interrupt if using external notifier */
+ if (data & mask & OTGSC_IDIE) {
+ cable->enabled = true;
+ data &= ~OTGSC_IDIE;
+ } else if (mask & OTGSC_IDIE) {
+ cable->enabled = false;
+ }
+ }
+
hw_write(ci, OP_OTGSC, mask | OTGSC_INT_STATUS_BITS, data);
}
@@ -98,13 +134,37 @@ void ci_handle_vbus_change(struct ci_hdrc *ci)
if (!ci->is_otg)
return;
- if (hw_read_otgsc(ci, OTGSC_BSV))
+ if (hw_read_otgsc(ci, OTGSC_BSV) && !ci->vbus_active)
usb_gadget_vbus_connect(&ci->gadget);
- else
+ else if (!hw_read_otgsc(ci, OTGSC_BSV) && ci->vbus_active)
usb_gadget_vbus_disconnect(&ci->gadget);
}
-#define CI_VBUS_STABLE_TIMEOUT_MS 5000
+/**
+ * When we switch to device mode, the vbus value should be lower
+ * than OTGSC_BSV before connecting to host.
+ *
+ * @ci: the controller
+ *
+ * This function returns an error code if timeout
+ */
+static int hw_wait_vbus_lower_bsv(struct ci_hdrc *ci)
+{
+ unsigned long elapse = jiffies + msecs_to_jiffies(5000);
+ u32 mask = OTGSC_BSV;
+
+ while (hw_read_otgsc(ci, mask)) {
+ if (time_after(jiffies, elapse)) {
+ dev_err(ci->dev, "timeout waiting for %08x in OTGSC\n",
+ mask);
+ return -ETIMEDOUT;
+ }
+ msleep(20);
+ }
+
+ return 0;
+}
+
static void ci_handle_id_switch(struct ci_hdrc *ci)
{
enum ci_role role = ci_otg_role(ci);
@@ -115,12 +175,21 @@ static void ci_handle_id_switch(struct ci_hdrc *ci)
ci_role_stop(ci);
- if (role == CI_ROLE_GADGET)
- /* wait vbus lower than OTGSC_BSV */
- hw_wait_reg(ci, OP_OTGSC, OTGSC_BSV, 0,
- CI_VBUS_STABLE_TIMEOUT_MS);
+ if (role == CI_ROLE_GADGET &&
+ IS_ERR(ci->platdata->vbus_extcon.edev))
+ /*
+ * Wait vbus lower than OTGSC_BSV before connecting
+ * to host. If connecting status is from an external
+ * connector instead of register, we don't need to
+ * care vbus on the board, since it will not affect
+ * external connector status.
+ */
+ hw_wait_vbus_lower_bsv(ci);
ci_role_start(ci, role);
+ /* vbus change may have already occurred */
+ if (role == CI_ROLE_GADGET)
+ ci_handle_vbus_change(ci);
}
}
/**
diff --git a/drivers/usb/chipidea/otg_fsm.c b/drivers/usb/chipidea/otg_fsm.c
index de8e22ec3902..93e24ce61a3a 100644
--- a/drivers/usb/chipidea/otg_fsm.c
+++ b/drivers/usb/chipidea/otg_fsm.c
@@ -234,8 +234,8 @@ static void ci_otg_add_timer(struct ci_hdrc *ci, enum otg_fsm_timer t)
ktime_set(timer_sec, timer_nsec));
ci->enabled_otg_timer_bits |= (1 << t);
if ((ci->next_otg_timer == NUM_OTG_FSM_TIMERS) ||
- (ci->hr_timeouts[ci->next_otg_timer].tv64 >
- ci->hr_timeouts[t].tv64)) {
+ (ci->hr_timeouts[ci->next_otg_timer] >
+ ci->hr_timeouts[t])) {
ci->next_otg_timer = t;
hrtimer_start_range_ns(&ci->otg_fsm_hrtimer,
ci->hr_timeouts[t], NSEC_PER_MSEC,
@@ -269,8 +269,8 @@ static void ci_otg_del_timer(struct ci_hdrc *ci, enum otg_fsm_timer t)
for_each_set_bit(cur_timer, &enabled_timer_bits,
NUM_OTG_FSM_TIMERS) {
if ((next_timer == NUM_OTG_FSM_TIMERS) ||
- (ci->hr_timeouts[next_timer].tv64 <
- ci->hr_timeouts[cur_timer].tv64))
+ (ci->hr_timeouts[next_timer] <
+ ci->hr_timeouts[cur_timer]))
next_timer = cur_timer;
}
}
@@ -397,14 +397,14 @@ static enum hrtimer_restart ci_otg_hrtimer_func(struct hrtimer *t)
now = ktime_get();
for_each_set_bit(cur_timer, &enabled_timer_bits, NUM_OTG_FSM_TIMERS) {
- if (now.tv64 >= ci->hr_timeouts[cur_timer].tv64) {
+ if (now >= ci->hr_timeouts[cur_timer]) {
ci->enabled_otg_timer_bits &= ~(1 << cur_timer);
if (otg_timer_handlers[cur_timer])
ret = otg_timer_handlers[cur_timer](ci);
} else {
if ((next_timer == NUM_OTG_FSM_TIMERS) ||
- (ci->hr_timeouts[cur_timer].tv64 <
- ci->hr_timeouts[next_timer].tv64))
+ (ci->hr_timeouts[cur_timer] <
+ ci->hr_timeouts[next_timer]))
next_timer = cur_timer;
}
}
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
index c9e80ad48fdc..f88e9157fad0 100644
--- a/drivers/usb/chipidea/udc.c
+++ b/drivers/usb/chipidea/udc.c
@@ -365,7 +365,7 @@ static int add_td_to_list(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq,
if (hwreq->req.length == 0
|| hwreq->req.length % hwep->ep.maxpacket)
mul++;
- node->ptr->token |= mul << __ffs(TD_MULTO);
+ node->ptr->token |= cpu_to_le32(mul << __ffs(TD_MULTO));
}
temp = (u32) (hwreq->req.dma + hwreq->req.actual);
@@ -504,7 +504,7 @@ static int _hardware_enqueue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq)
if (hwreq->req.length == 0
|| hwreq->req.length % hwep->ep.maxpacket)
mul++;
- hwep->qh.ptr->cap |= mul << __ffs(QH_MULT);
+ hwep->qh.ptr->cap |= cpu_to_le32(mul << __ffs(QH_MULT));
}
ret = hw_ep_prime(ci, hwep->num, hwep->dir,
@@ -529,7 +529,7 @@ static void free_pending_td(struct ci_hw_ep *hwep)
static int reprime_dtd(struct ci_hdrc *ci, struct ci_hw_ep *hwep,
struct td_node *node)
{
- hwep->qh.ptr->td.next = node->dma;
+ hwep->qh.ptr->td.next = cpu_to_le32(node->dma);
hwep->qh.ptr->td.token &=
cpu_to_le32(~(TD_STATUS_HALTED | TD_STATUS_ACTIVE));
@@ -821,7 +821,7 @@ static int _ep_queue(struct usb_ep *ep, struct usb_request *req,
}
if (usb_endpoint_xfer_isoc(hwep->ep.desc) &&
- hwreq->req.length > (1 + hwep->ep.mult) * hwep->ep.maxpacket) {
+ hwreq->req.length > hwep->ep.mult * hwep->ep.maxpacket) {
dev_err(hwep->ci->dev, "request length too big for isochronous\n");
return -EMSGSIZE;
}
@@ -1253,8 +1253,8 @@ static int ep_enable(struct usb_ep *ep,
hwep->num = usb_endpoint_num(desc);
hwep->type = usb_endpoint_type(desc);
- hwep->ep.maxpacket = usb_endpoint_maxp(desc) & 0x07ff;
- hwep->ep.mult = QH_ISO_MULT(usb_endpoint_maxp(desc));
+ hwep->ep.maxpacket = usb_endpoint_maxp(desc);
+ hwep->ep.mult = usb_endpoint_maxp_mult(desc);
if (hwep->type == USB_ENDPOINT_XFER_CONTROL)
cap |= QH_IOS;
@@ -1725,7 +1725,6 @@ static int ci_udc_start(struct usb_gadget *gadget,
struct usb_gadget_driver *driver)
{
struct ci_hdrc *ci = container_of(gadget, struct ci_hdrc, gadget);
- unsigned long flags;
int retval = -ENOMEM;
if (driver->disconnect == NULL)
@@ -1752,7 +1751,6 @@ static int ci_udc_start(struct usb_gadget *gadget,
pm_runtime_get_sync(&ci->gadget.dev);
if (ci->vbus_active) {
- spin_lock_irqsave(&ci->lock, flags);
hw_device_reset(ci);
} else {
usb_udc_vbus_handler(&ci->gadget, false);
@@ -1761,7 +1759,6 @@ static int ci_udc_start(struct usb_gadget *gadget,
}
retval = hw_device_state(ci, ci->ep0out->qh.dma);
- spin_unlock_irqrestore(&ci->lock, flags);
if (retval)
pm_runtime_put_sync(&ci->gadget.dev);
@@ -1796,10 +1793,10 @@ static int ci_udc_stop(struct usb_gadget *gadget)
if (ci->vbus_active) {
hw_device_state(ci, 0);
+ spin_unlock_irqrestore(&ci->lock, flags);
if (ci->platdata->notify_event)
ci->platdata->notify_event(ci,
CI_HDRC_CONTROLLER_STOPPED_EVENT);
- spin_unlock_irqrestore(&ci->lock, flags);
_gadget_stop_activity(&ci->gadget);
spin_lock_irqsave(&ci->lock, flags);
pm_runtime_put(&ci->gadget.dev);
diff --git a/drivers/usb/chipidea/udc.h b/drivers/usb/chipidea/udc.h
index e66df0020bd4..2ecd1174d66c 100644
--- a/drivers/usb/chipidea/udc.h
+++ b/drivers/usb/chipidea/udc.h
@@ -22,11 +22,11 @@
/* DMA layout of transfer descriptors */
struct ci_hw_td {
/* 0 */
- u32 next;
+ __le32 next;
#define TD_TERMINATE BIT(0)
#define TD_ADDR_MASK (0xFFFFFFEUL << 5)
/* 1 */
- u32 token;
+ __le32 token;
#define TD_STATUS (0x00FFUL << 0)
#define TD_STATUS_TR_ERR BIT(3)
#define TD_STATUS_DT_ERR BIT(5)
@@ -36,7 +36,7 @@ struct ci_hw_td {
#define TD_IOC BIT(15)
#define TD_TOTAL_BYTES (0x7FFFUL << 16)
/* 2 */
- u32 page[5];
+ __le32 page[5];
#define TD_CURR_OFFSET (0x0FFFUL << 0)
#define TD_FRAME_NUM (0x07FFUL << 0)
#define TD_RESERVED_MASK (0x0FFFUL << 0)
@@ -45,18 +45,18 @@ struct ci_hw_td {
/* DMA layout of queue heads */
struct ci_hw_qh {
/* 0 */
- u32 cap;
+ __le32 cap;
#define QH_IOS BIT(15)
#define QH_MAX_PKT (0x07FFUL << 16)
#define QH_ZLT BIT(29)
#define QH_MULT (0x0003UL << 30)
#define QH_ISO_MULT(x) ((x >> 11) & 0x03)
/* 1 */
- u32 curr;
+ __le32 curr;
/* 2 - 8 */
struct ci_hw_td td;
/* 9 */
- u32 RESERVED;
+ __le32 RESERVED;
struct usb_ctrlrequest setup;
} __attribute__ ((packed, aligned(4)));
diff --git a/drivers/usb/chipidea/ulpi.c b/drivers/usb/chipidea/ulpi.c
new file mode 100644
index 000000000000..1219583dc1b2
--- /dev/null
+++ b/drivers/usb/chipidea/ulpi.c
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2016 Linaro Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/usb/chipidea.h>
+#include <linux/ulpi/interface.h>
+
+#include "ci.h"
+
+#define ULPI_WAKEUP BIT(31)
+#define ULPI_RUN BIT(30)
+#define ULPI_WRITE BIT(29)
+#define ULPI_SYNC_STATE BIT(27)
+#define ULPI_ADDR(n) ((n) << 16)
+#define ULPI_DATA(n) (n)
+
+static int ci_ulpi_wait(struct ci_hdrc *ci, u32 mask)
+{
+ unsigned long usec = 10000;
+
+ while (usec--) {
+ if (!hw_read(ci, OP_ULPI_VIEWPORT, mask))
+ return 0;
+
+ udelay(1);
+ }
+
+ return -ETIMEDOUT;
+}
+
+static int ci_ulpi_read(struct device *dev, u8 addr)
+{
+ struct ci_hdrc *ci = dev_get_drvdata(dev);
+ int ret;
+
+ hw_write(ci, OP_ULPI_VIEWPORT, 0xffffffff, ULPI_WRITE | ULPI_WAKEUP);
+ ret = ci_ulpi_wait(ci, ULPI_WAKEUP);
+ if (ret)
+ return ret;
+
+ hw_write(ci, OP_ULPI_VIEWPORT, 0xffffffff, ULPI_RUN | ULPI_ADDR(addr));
+ ret = ci_ulpi_wait(ci, ULPI_RUN);
+ if (ret)
+ return ret;
+
+ return hw_read(ci, OP_ULPI_VIEWPORT, GENMASK(15, 8)) >> 8;
+}
+
+static int ci_ulpi_write(struct device *dev, u8 addr, u8 val)
+{
+ struct ci_hdrc *ci = dev_get_drvdata(dev);
+ int ret;
+
+ hw_write(ci, OP_ULPI_VIEWPORT, 0xffffffff, ULPI_WRITE | ULPI_WAKEUP);
+ ret = ci_ulpi_wait(ci, ULPI_WAKEUP);
+ if (ret)
+ return ret;
+
+ hw_write(ci, OP_ULPI_VIEWPORT, 0xffffffff,
+ ULPI_RUN | ULPI_WRITE | ULPI_ADDR(addr) | val);
+ return ci_ulpi_wait(ci, ULPI_RUN);
+}
+
+int ci_ulpi_init(struct ci_hdrc *ci)
+{
+ if (ci->platdata->phy_mode != USBPHY_INTERFACE_MODE_ULPI)
+ return 0;
+
+ /*
+ * Set PORTSC correctly so we can read/write ULPI registers for
+ * identification purposes
+ */
+ hw_phymode_configure(ci);
+
+ ci->ulpi_ops.read = ci_ulpi_read;
+ ci->ulpi_ops.write = ci_ulpi_write;
+ ci->ulpi = ulpi_register_interface(ci->dev, &ci->ulpi_ops);
+ if (IS_ERR(ci->ulpi))
+ dev_err(ci->dev, "failed to register ULPI interface");
+
+ return PTR_ERR_OR_ZERO(ci->ulpi);
+}
+
+void ci_ulpi_exit(struct ci_hdrc *ci)
+{
+ if (ci->ulpi) {
+ ulpi_unregister_interface(ci->ulpi);
+ ci->ulpi = NULL;
+ }
+}
+
+int ci_ulpi_resume(struct ci_hdrc *ci)
+{
+ int cnt = 100000;
+
+ while (cnt-- > 0) {
+ if (hw_read(ci, OP_ULPI_VIEWPORT, ULPI_SYNC_STATE))
+ return 0;
+ udelay(1);
+ }
+
+ return -ETIMEDOUT;
+}
diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c
index 20d02a5e418d..e77a4ed4f021 100644
--- a/drivers/usb/chipidea/usbmisc_imx.c
+++ b/drivers/usb/chipidea/usbmisc_imx.c
@@ -46,11 +46,23 @@
#define MX53_USB_OTG_PHY_CTRL_0_OFFSET 0x08
#define MX53_USB_OTG_PHY_CTRL_1_OFFSET 0x0c
+#define MX53_USB_CTRL_1_OFFSET 0x10
+#define MX53_USB_CTRL_1_H2_XCVR_CLK_SEL_MASK (0x11 << 2)
+#define MX53_USB_CTRL_1_H2_XCVR_CLK_SEL_ULPI BIT(2)
+#define MX53_USB_CTRL_1_H3_XCVR_CLK_SEL_MASK (0x11 << 6)
+#define MX53_USB_CTRL_1_H3_XCVR_CLK_SEL_ULPI BIT(6)
#define MX53_USB_UH2_CTRL_OFFSET 0x14
#define MX53_USB_UH3_CTRL_OFFSET 0x18
+#define MX53_USB_CLKONOFF_CTRL_OFFSET 0x24
+#define MX53_USB_CLKONOFF_CTRL_H2_INT60CKOFF BIT(21)
+#define MX53_USB_CLKONOFF_CTRL_H3_INT60CKOFF BIT(22)
#define MX53_BM_OVER_CUR_DIS_H1 BIT(5)
#define MX53_BM_OVER_CUR_DIS_OTG BIT(8)
#define MX53_BM_OVER_CUR_DIS_UHx BIT(30)
+#define MX53_USB_CTRL_1_UH2_ULPI_EN BIT(26)
+#define MX53_USB_CTRL_1_UH3_ULPI_EN BIT(27)
+#define MX53_USB_UHx_CTRL_WAKE_UP_EN BIT(7)
+#define MX53_USB_UHx_CTRL_ULPI_INT_EN BIT(8)
#define MX53_USB_PHYCTRL1_PLLDIV_MASK 0x3
#define MX53_USB_PLL_DIV_24_MHZ 0x01
@@ -199,31 +211,77 @@ static int usbmisc_imx53_init(struct imx_usbmisc_data *data)
val |= MX53_USB_PLL_DIV_24_MHZ;
writel(val, usbmisc->base + MX53_USB_OTG_PHY_CTRL_1_OFFSET);
- if (data->disable_oc) {
- spin_lock_irqsave(&usbmisc->lock, flags);
- switch (data->index) {
- case 0:
+ spin_lock_irqsave(&usbmisc->lock, flags);
+
+ switch (data->index) {
+ case 0:
+ if (data->disable_oc) {
reg = usbmisc->base + MX53_USB_OTG_PHY_CTRL_0_OFFSET;
val = readl(reg) | MX53_BM_OVER_CUR_DIS_OTG;
- break;
- case 1:
+ writel(val, reg);
+ }
+ break;
+ case 1:
+ if (data->disable_oc) {
reg = usbmisc->base + MX53_USB_OTG_PHY_CTRL_0_OFFSET;
val = readl(reg) | MX53_BM_OVER_CUR_DIS_H1;
- break;
- case 2:
+ writel(val, reg);
+ }
+ break;
+ case 2:
+ if (data->ulpi) {
+ /* set USBH2 into ULPI-mode. */
+ reg = usbmisc->base + MX53_USB_CTRL_1_OFFSET;
+ val = readl(reg) | MX53_USB_CTRL_1_UH2_ULPI_EN;
+ /* select ULPI clock */
+ val &= ~MX53_USB_CTRL_1_H2_XCVR_CLK_SEL_MASK;
+ val |= MX53_USB_CTRL_1_H2_XCVR_CLK_SEL_ULPI;
+ writel(val, reg);
+ /* Set interrupt wake up enable */
+ reg = usbmisc->base + MX53_USB_UH2_CTRL_OFFSET;
+ val = readl(reg) | MX53_USB_UHx_CTRL_WAKE_UP_EN
+ | MX53_USB_UHx_CTRL_ULPI_INT_EN;
+ writel(val, reg);
+ /* Disable internal 60Mhz clock */
+ reg = usbmisc->base + MX53_USB_CLKONOFF_CTRL_OFFSET;
+ val = readl(reg) | MX53_USB_CLKONOFF_CTRL_H2_INT60CKOFF;
+ writel(val, reg);
+ }
+ if (data->disable_oc) {
reg = usbmisc->base + MX53_USB_UH2_CTRL_OFFSET;
val = readl(reg) | MX53_BM_OVER_CUR_DIS_UHx;
- break;
- case 3:
+ writel(val, reg);
+ }
+ break;
+ case 3:
+ if (data->ulpi) {
+ /* set USBH3 into ULPI-mode. */
+ reg = usbmisc->base + MX53_USB_CTRL_1_OFFSET;
+ val = readl(reg) | MX53_USB_CTRL_1_UH3_ULPI_EN;
+ /* select ULPI clock */
+ val &= ~MX53_USB_CTRL_1_H3_XCVR_CLK_SEL_MASK;
+ val |= MX53_USB_CTRL_1_H3_XCVR_CLK_SEL_ULPI;
+ writel(val, reg);
+ /* Set interrupt wake up enable */
reg = usbmisc->base + MX53_USB_UH3_CTRL_OFFSET;
- val = readl(reg) | MX53_BM_OVER_CUR_DIS_UHx;
- break;
+ val = readl(reg) | MX53_USB_UHx_CTRL_WAKE_UP_EN
+ | MX53_USB_UHx_CTRL_ULPI_INT_EN;
+ writel(val, reg);
+ /* Disable internal 60Mhz clock */
+ reg = usbmisc->base + MX53_USB_CLKONOFF_CTRL_OFFSET;
+ val = readl(reg) | MX53_USB_CLKONOFF_CTRL_H3_INT60CKOFF;
+ writel(val, reg);
}
- if (reg && val)
+ if (data->disable_oc) {
+ reg = usbmisc->base + MX53_USB_UH3_CTRL_OFFSET;
+ val = readl(reg) | MX53_BM_OVER_CUR_DIS_UHx;
writel(val, reg);
- spin_unlock_irqrestore(&usbmisc->lock, flags);
+ }
+ break;
}
+ spin_unlock_irqrestore(&usbmisc->lock, flags);
+
return 0;
}
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index fada988512a1..d5388938bc7a 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -32,6 +32,7 @@
#undef VERBOSE_DEBUG
#include <linux/kernel.h>
+#include <linux/sched/signal.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
@@ -133,8 +134,8 @@ static int acm_ctrl_msg(struct acm *acm, int request, int value,
buf, len, 5000);
dev_dbg(&acm->control->dev,
- "%s - rq 0x%02x, val %#x, len %#x, result %d\n",
- __func__, request, value, len, retval);
+ "%s - rq 0x%02x, val %#x, len %#x, result %d\n",
+ __func__, request, value, len, retval);
usb_autopm_put_interface(acm->control);
@@ -158,6 +159,17 @@ static inline int acm_set_control(struct acm *acm, int control)
#define acm_send_break(acm, ms) \
acm_ctrl_msg(acm, USB_CDC_REQ_SEND_BREAK, ms, NULL, 0)
+static void acm_kill_urbs(struct acm *acm)
+{
+ int i;
+
+ usb_kill_urb(acm->ctrlurb);
+ for (i = 0; i < ACM_NW; i++)
+ usb_kill_urb(acm->wb[i].urb);
+ for (i = 0; i < acm->rx_buflimit; i++)
+ usb_kill_urb(acm->read_urbs[i]);
+}
+
/*
* Write buffer management.
* All of these assume proper locks taken by the caller.
@@ -291,13 +303,13 @@ static void acm_ctrl_irq(struct urb *urb)
case -ESHUTDOWN:
/* this urb is terminated, clean up */
dev_dbg(&acm->control->dev,
- "%s - urb shutting down with status: %d\n",
- __func__, status);
+ "%s - urb shutting down with status: %d\n",
+ __func__, status);
return;
default:
dev_dbg(&acm->control->dev,
- "%s - nonzero urb status received: %d\n",
- __func__, status);
+ "%s - nonzero urb status received: %d\n",
+ __func__, status);
goto exit;
}
@@ -306,16 +318,16 @@ static void acm_ctrl_irq(struct urb *urb)
data = (unsigned char *)(dr + 1);
switch (dr->bNotificationType) {
case USB_CDC_NOTIFY_NETWORK_CONNECTION:
- dev_dbg(&acm->control->dev, "%s - network connection: %d\n",
- __func__, dr->wValue);
+ dev_dbg(&acm->control->dev,
+ "%s - network connection: %d\n", __func__, dr->wValue);
break;
case USB_CDC_NOTIFY_SERIAL_STATE:
newctrl = get_unaligned_le16(data);
if (!acm->clocal && (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) {
- dev_dbg(&acm->control->dev, "%s - calling hangup\n",
- __func__);
+ dev_dbg(&acm->control->dev,
+ "%s - calling hangup\n", __func__);
tty_port_tty_hangup(&acm->port, false);
}
@@ -357,8 +369,8 @@ static void acm_ctrl_irq(struct urb *urb)
exit:
retval = usb_submit_urb(urb, GFP_ATOMIC);
if (retval && retval != -EPERM)
- dev_err(&acm->control->dev, "%s - usb_submit_urb failed: %d\n",
- __func__, retval);
+ dev_err(&acm->control->dev,
+ "%s - usb_submit_urb failed: %d\n", __func__, retval);
}
static int acm_submit_read_urb(struct acm *acm, int index, gfp_t mem_flags)
@@ -372,8 +384,8 @@ static int acm_submit_read_urb(struct acm *acm, int index, gfp_t mem_flags)
if (res) {
if (res != -EPERM) {
dev_err(&acm->data->dev,
- "urb %d failed submission with %d\n",
- index, res);
+ "urb %d failed submission with %d\n",
+ index, res);
}
set_bit(index, &acm->read_urbs_free);
return res;
@@ -416,30 +428,43 @@ static void acm_read_bulk_callback(struct urb *urb)
int status = urb->status;
dev_vdbg(&acm->data->dev, "got urb %d, len %d, status %d\n",
- rb->index, urb->actual_length,
- status);
+ rb->index, urb->actual_length, status);
+
+ set_bit(rb->index, &acm->read_urbs_free);
if (!acm->dev) {
- set_bit(rb->index, &acm->read_urbs_free);
dev_dbg(&acm->data->dev, "%s - disconnected\n", __func__);
return;
}
- if (status) {
- set_bit(rb->index, &acm->read_urbs_free);
- if ((status != -ENOENT) || (urb->actual_length == 0))
- return;
+ switch (status) {
+ case 0:
+ usb_mark_last_busy(acm->dev);
+ acm_process_read_urb(acm, urb);
+ break;
+ case -EPIPE:
+ set_bit(EVENT_RX_STALL, &acm->flags);
+ schedule_work(&acm->work);
+ return;
+ case -ENOENT:
+ case -ECONNRESET:
+ case -ESHUTDOWN:
+ dev_dbg(&acm->data->dev,
+ "%s - urb shutting down with status: %d\n",
+ __func__, status);
+ return;
+ default:
+ dev_dbg(&acm->data->dev,
+ "%s - nonzero urb status received: %d\n",
+ __func__, status);
+ break;
}
- usb_mark_last_busy(acm->dev);
-
- acm_process_read_urb(acm, urb);
/*
* Unthrottle may run on another CPU which needs to see events
* in the same order. Submission has an implict barrier
*/
smp_mb__before_atomic();
- set_bit(rb->index, &acm->read_urbs_free);
/* throttle device if requested by tty */
spin_lock_irqsave(&acm->read_lock, flags);
@@ -469,14 +494,30 @@ static void acm_write_bulk(struct urb *urb)
spin_lock_irqsave(&acm->write_lock, flags);
acm_write_done(acm, wb);
spin_unlock_irqrestore(&acm->write_lock, flags);
+ set_bit(EVENT_TTY_WAKEUP, &acm->flags);
schedule_work(&acm->work);
}
static void acm_softint(struct work_struct *work)
{
+ int i;
struct acm *acm = container_of(work, struct acm, work);
- tty_port_tty_wakeup(&acm->port);
+ if (test_bit(EVENT_RX_STALL, &acm->flags)) {
+ if (!(usb_autopm_get_interface(acm->data))) {
+ for (i = 0; i < acm->rx_buflimit; i++)
+ usb_kill_urb(acm->read_urbs[i]);
+ usb_clear_halt(acm->dev, acm->in);
+ acm_submit_read_urbs(acm, GFP_KERNEL);
+ usb_autopm_put_interface(acm->data);
+ }
+ clear_bit(EVENT_RX_STALL, &acm->flags);
+ }
+
+ if (test_bit(EVENT_TTY_WAKEUP, &acm->flags)) {
+ tty_port_tty_wakeup(&acm->port);
+ clear_bit(EVENT_TTY_WAKEUP, &acm->flags);
+ }
}
/*
@@ -608,7 +649,6 @@ static void acm_port_shutdown(struct tty_port *port)
struct acm *acm = container_of(port, struct acm, port);
struct urb *urb;
struct acm_wb *wb;
- int i;
/*
* Need to grab write_lock to prevent race with resume, but no need to
@@ -630,11 +670,7 @@ static void acm_port_shutdown(struct tty_port *port)
usb_autopm_put_interface_async(acm->control);
}
- usb_kill_urb(acm->ctrlurb);
- for (i = 0; i < ACM_NW; i++)
- usb_kill_urb(acm->wb[i].urb);
- for (i = 0; i < acm->rx_buflimit; i++)
- usb_kill_urb(acm->read_urbs[i]);
+ acm_kill_urbs(acm);
}
static void acm_tty_cleanup(struct tty_struct *tty)
@@ -837,8 +873,8 @@ static int acm_tty_break_ctl(struct tty_struct *tty, int state)
retval = acm_send_break(acm, state ? 0xffff : 0);
if (retval < 0)
- dev_dbg(&acm->control->dev, "%s - send break failed\n",
- __func__);
+ dev_dbg(&acm->control->dev,
+ "%s - send break failed\n", __func__);
return retval;
}
@@ -877,11 +913,7 @@ static int get_serial_info(struct acm *acm, struct serial_struct __user *info)
{
struct serial_struct tmp;
- if (!info)
- return -EINVAL;
-
memset(&tmp, 0, sizeof(tmp));
- tmp.flags = ASYNC_LOW_LATENCY;
tmp.xmit_fifo_size = acm->writesize;
tmp.baud_base = le32_to_cpu(acm->line.dwDTERate);
tmp.close_delay = acm->port.close_delay / 10;
@@ -969,25 +1001,20 @@ static int wait_serial_change(struct acm *acm, unsigned long arg)
return rv;
}
-static int get_serial_usage(struct acm *acm,
- struct serial_icounter_struct __user *count)
+static int acm_tty_get_icount(struct tty_struct *tty,
+ struct serial_icounter_struct *icount)
{
- struct serial_icounter_struct icount;
- int rv = 0;
-
- memset(&icount, 0, sizeof(icount));
- icount.dsr = acm->iocount.dsr;
- icount.rng = acm->iocount.rng;
- icount.dcd = acm->iocount.dcd;
- icount.frame = acm->iocount.frame;
- icount.overrun = acm->iocount.overrun;
- icount.parity = acm->iocount.parity;
- icount.brk = acm->iocount.brk;
+ struct acm *acm = tty->driver_data;
- if (copy_to_user(count, &icount, sizeof(icount)) > 0)
- rv = -EFAULT;
+ icount->dsr = acm->iocount.dsr;
+ icount->rng = acm->iocount.rng;
+ icount->dcd = acm->iocount.dcd;
+ icount->frame = acm->iocount.frame;
+ icount->overrun = acm->iocount.overrun;
+ icount->parity = acm->iocount.parity;
+ icount->brk = acm->iocount.brk;
- return rv;
+ return 0;
}
static int acm_tty_ioctl(struct tty_struct *tty,
@@ -1012,9 +1039,6 @@ static int acm_tty_ioctl(struct tty_struct *tty,
rv = wait_serial_change(acm, arg);
usb_autopm_put_interface(acm->control);
break;
- case TIOCGICOUNT:
- rv = get_serial_usage(acm, (struct serial_icounter_struct __user *) arg);
- break;
}
return rv;
@@ -1088,19 +1112,17 @@ static void acm_write_buffers_free(struct acm *acm)
{
int i;
struct acm_wb *wb;
- struct usb_device *usb_dev = interface_to_usbdev(acm->control);
for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++)
- usb_free_coherent(usb_dev, acm->writesize, wb->buf, wb->dmah);
+ usb_free_coherent(acm->dev, acm->writesize, wb->buf, wb->dmah);
}
static void acm_read_buffers_free(struct acm *acm)
{
- struct usb_device *usb_dev = interface_to_usbdev(acm->control);
int i;
for (i = 0; i < acm->rx_buflimit; i++)
- usb_free_coherent(usb_dev, acm->readsize,
+ usb_free_coherent(acm->dev, acm->readsize,
acm->read_buffers[i].base, acm->read_buffers[i].dma);
}
@@ -1345,9 +1367,16 @@ made_compressed_probe:
spin_lock_init(&acm->write_lock);
spin_lock_init(&acm->read_lock);
mutex_init(&acm->mutex);
- acm->is_int_ep = usb_endpoint_xfer_int(epread);
- if (acm->is_int_ep)
+ if (usb_endpoint_xfer_int(epread)) {
acm->bInterval = epread->bInterval;
+ acm->in = usb_rcvintpipe(usb_dev, epread->bEndpointAddress);
+ } else {
+ acm->in = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress);
+ }
+ if (usb_endpoint_xfer_int(epwrite))
+ 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);
@@ -1382,20 +1411,15 @@ made_compressed_probe:
urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
urb->transfer_dma = rb->dma;
- if (acm->is_int_ep) {
- usb_fill_int_urb(urb, acm->dev,
- usb_rcvintpipe(usb_dev, epread->bEndpointAddress),
- rb->base,
+ if (usb_endpoint_xfer_int(epread))
+ usb_fill_int_urb(urb, acm->dev, acm->in, rb->base,
acm->readsize,
acm_read_bulk_callback, rb,
acm->bInterval);
- } else {
- usb_fill_bulk_urb(urb, acm->dev,
- usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress),
- rb->base,
+ else
+ usb_fill_bulk_urb(urb, acm->dev, acm->in, rb->base,
acm->readsize,
acm_read_bulk_callback, rb);
- }
acm->read_urbs[i] = urb;
__set_bit(i, &acm->read_urbs_free);
@@ -1408,12 +1432,10 @@ made_compressed_probe:
goto alloc_fail7;
if (usb_endpoint_xfer_int(epwrite))
- usb_fill_int_urb(snd->urb, usb_dev,
- usb_sndintpipe(usb_dev, epwrite->bEndpointAddress),
+ usb_fill_int_urb(snd->urb, usb_dev, acm->out,
NULL, acm->writesize, acm_write_bulk, snd, epwrite->bInterval);
else
- usb_fill_bulk_urb(snd->urb, usb_dev,
- usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
+ usb_fill_bulk_urb(snd->urb, usb_dev, acm->out,
NULL, acm->writesize, acm_write_bulk, snd);
snd->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
if (quirks & SEND_ZERO_PACKET)
@@ -1485,8 +1507,8 @@ skip_countries:
}
if (quirks & CLEAR_HALT_CONDITIONS) {
- usb_clear_halt(usb_dev, usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress));
- usb_clear_halt(usb_dev, usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress));
+ usb_clear_halt(usb_dev, acm->in);
+ usb_clear_halt(usb_dev, acm->out);
}
return 0;
@@ -1520,25 +1542,10 @@ alloc_fail:
return rv;
}
-static void stop_data_traffic(struct acm *acm)
-{
- int i;
-
- usb_kill_urb(acm->ctrlurb);
- for (i = 0; i < ACM_NW; i++)
- usb_kill_urb(acm->wb[i].urb);
- for (i = 0; i < acm->rx_buflimit; i++)
- usb_kill_urb(acm->read_urbs[i]);
-
- cancel_work_sync(&acm->work);
-}
-
static void acm_disconnect(struct usb_interface *intf)
{
struct acm *acm = usb_get_intfdata(intf);
- struct usb_device *usb_dev = interface_to_usbdev(intf);
struct tty_struct *tty;
- int i;
/* sibling interface is already cleaning up */
if (!acm)
@@ -1564,17 +1571,13 @@ static void acm_disconnect(struct usb_interface *intf)
tty_kref_put(tty);
}
- stop_data_traffic(acm);
+ acm_kill_urbs(acm);
+ cancel_work_sync(&acm->work);
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(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
+ usb_free_coherent(acm->dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
acm_read_buffers_free(acm);
if (!acm->combined_interfaces)
@@ -1603,7 +1606,8 @@ static int acm_suspend(struct usb_interface *intf, pm_message_t message)
if (cnt)
return 0;
- stop_data_traffic(acm);
+ acm_kill_urbs(acm);
+ cancel_work_sync(&acm->work);
return 0;
}
@@ -1657,6 +1661,15 @@ static int acm_reset_resume(struct usb_interface *intf)
#endif /* CONFIG_PM */
+static int acm_pre_reset(struct usb_interface *intf)
+{
+ struct acm *acm = usb_get_intfdata(intf);
+
+ clear_bit(EVENT_RX_STALL, &acm->flags);
+
+ return 0;
+}
+
#define NOKIA_PCSUITE_ACM_INFO(x) \
USB_DEVICE_AND_INTERFACE_INFO(0x0421, x, \
USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, \
@@ -1719,6 +1732,7 @@ static const struct usb_device_id acm_ids[] = {
{ USB_DEVICE(0x20df, 0x0001), /* Simtec Electronics Entropy Key */
.driver_info = QUIRK_CONTROL_LINE_STATE, },
{ USB_DEVICE(0x2184, 0x001c) }, /* GW Instek AFG-2225 */
+ { USB_DEVICE(0x2184, 0x0036) }, /* GW Instek AFG-125 */
{ USB_DEVICE(0x22b8, 0x6425), /* Motorola MOTOMAGX phones */
},
/* Motorola H24 HSPA module: */
@@ -1898,6 +1912,7 @@ static struct usb_driver acm_driver = {
.resume = acm_resume,
.reset_resume = acm_reset_resume,
#endif
+ .pre_reset = acm_pre_reset,
.id_table = acm_ids,
#ifdef CONFIG_PM
.supports_autosuspend = 1,
@@ -1927,6 +1942,7 @@ static const struct tty_operations acm_ops = {
.set_termios = acm_tty_set_termios,
.tiocmget = acm_tty_tiocmget,
.tiocmset = acm_tty_tiocmset,
+ .get_icount = acm_tty_get_icount,
};
/*
diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h
index 1f1eabfd8462..c980f11cdf56 100644
--- a/drivers/usb/class/cdc-acm.h
+++ b/drivers/usb/class/cdc-acm.h
@@ -83,6 +83,7 @@ struct acm {
struct usb_device *dev; /* the corresponding usb device */
struct usb_interface *control; /* control interface */
struct usb_interface *data; /* data interface */
+ unsigned in, out; /* i/o pipes */
struct tty_port port; /* our tty port data */
struct urb *ctrlurb; /* urbs */
u8 *ctrl_buffer; /* buffers of urbs */
@@ -102,6 +103,9 @@ struct acm {
spinlock_t write_lock;
struct mutex mutex;
bool disconnected;
+ unsigned long flags;
+# define EVENT_TTY_WAKEUP 0
+# define EVENT_RX_STALL 1
struct usb_cdc_line_coding line; /* bits, stop, parity */
struct work_struct work; /* work queue entry for line discipline waking up */
unsigned int ctrlin; /* input control lines (DCD, DSR, RI, break, overruns) */
@@ -116,7 +120,6 @@ struct acm {
unsigned int ctrl_caps; /* control capabilities from the class specific header */
unsigned int susp_count; /* number of suspended interfaces */
unsigned int combined_interfaces:1; /* control and data collapsed */
- unsigned int is_int_ep:1; /* interrupt endpoints contrary to spec used */
unsigned int throttled:1; /* actually throttled */
unsigned int throttle_req:1; /* throttle requested */
u8 bInterval;
diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c
index 0a6369510f2d..8fda45a45bd3 100644
--- a/drivers/usb/class/cdc-wdm.c
+++ b/drivers/usb/class/cdc-wdm.c
@@ -531,7 +531,7 @@ retry:
i++;
if (file->f_flags & O_NONBLOCK) {
if (!test_bit(WDM_READ, &desc->flags)) {
- rv = cntr ? cntr : -EAGAIN;
+ rv = -EAGAIN;
goto err;
}
rv = 0;
diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c
index 071964c7847f..cc61055fb9be 100644
--- a/drivers/usb/class/usblp.c
+++ b/drivers/usb/class/usblp.c
@@ -49,7 +49,7 @@
#include <linux/module.h>
#include <linux/kernel.h>
-#include <linux/sched.h>
+#include <linux/sched/signal.h>
#include <linux/signal.h>
#include <linux/poll.h>
#include <linux/slab.h>
diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c
index a6c1fae7d52a..8fb309a0ff6b 100644
--- a/drivers/usb/class/usbtmc.c
+++ b/drivers/usb/class/usbtmc.c
@@ -157,6 +157,7 @@ static int usbtmc_open(struct inode *inode, struct file *filp)
}
data = usb_get_intfdata(intf);
+ /* Protect reference to data from file structure until release */
kref_get(&data->kref);
/* Store pointer in file structure's private data field */
@@ -531,7 +532,7 @@ static int usbtmc488_ioctl_simple(struct usbtmc_device_data *data,
}
/*
- * Sends a REQUEST_DEV_DEP_MSG_IN message on the Bulk-IN endpoint.
+ * Sends a REQUEST_DEV_DEP_MSG_IN message on the Bulk-OUT endpoint.
* @transfer_size: number of bytes to request from the device.
*
* See the USBTMC specification, Table 4.
@@ -1380,7 +1381,7 @@ static int usbtmc_probe(struct usb_interface *intf,
dev_dbg(&intf->dev, "%s called\n", __func__);
- data = kmalloc(sizeof(*data), GFP_KERNEL);
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
@@ -1443,6 +1444,13 @@ static int usbtmc_probe(struct usb_interface *intf,
break;
}
}
+
+ if (!data->bulk_out || !data->bulk_in) {
+ dev_err(&intf->dev, "bulk endpoints not found\n");
+ retcode = -ENODEV;
+ goto err_put;
+ }
+
/* Find int endpoint */
for (n = 0; n < iface_desc->desc.bNumEndpoints; n++) {
endpoint = &iface_desc->endpoint[n].desc;
@@ -1468,17 +1476,21 @@ static int usbtmc_probe(struct usb_interface *intf,
if (data->iin_ep_present) {
/* allocate int urb */
data->iin_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!data->iin_urb)
+ if (!data->iin_urb) {
+ retcode = -ENOMEM;
goto error_register;
+ }
- /* will reference data in int urb */
+ /* Protect interrupt in endpoint data until iin_urb is freed */
kref_get(&data->kref);
/* allocate buffer for interrupt in */
data->iin_buffer = kmalloc(data->iin_wMaxPacketSize,
GFP_KERNEL);
- if (!data->iin_buffer)
+ if (!data->iin_buffer) {
+ retcode = -ENOMEM;
goto error_register;
+ }
/* fill interrupt urb */
usb_fill_int_urb(data->iin_urb, data->usb_dev,
@@ -1511,6 +1523,7 @@ 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);
return retcode;
}
diff --git a/drivers/usb/common/ulpi.c b/drivers/usb/common/ulpi.c
index 8b317702d761..c9480d77810c 100644
--- a/drivers/usb/common/ulpi.c
+++ b/drivers/usb/common/ulpi.c
@@ -16,6 +16,9 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/acpi.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/clk/clk-conf.h>
/* -------------------------------------------------------------------------- */
@@ -39,6 +42,10 @@ static int ulpi_match(struct device *dev, struct device_driver *driver)
struct ulpi *ulpi = to_ulpi_dev(dev);
const struct ulpi_device_id *id;
+ /* Some ULPI devices don't have a vendor id so rely on OF match */
+ if (ulpi->id.vendor == 0)
+ return of_driver_match_device(dev, driver);
+
for (id = drv->id_table; id->vendor; id++)
if (id->vendor == ulpi->id.vendor &&
id->product == ulpi->id.product)
@@ -50,6 +57,11 @@ static int ulpi_match(struct device *dev, struct device_driver *driver)
static int ulpi_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct ulpi *ulpi = to_ulpi_dev(dev);
+ int ret;
+
+ ret = of_device_uevent_modalias(dev, env);
+ if (ret != -ENODEV)
+ return ret;
if (add_uevent_var(env, "MODALIAS=ulpi:v%04xp%04x",
ulpi->id.vendor, ulpi->id.product))
@@ -60,6 +72,11 @@ static int ulpi_uevent(struct device *dev, struct kobj_uevent_env *env)
static int ulpi_probe(struct device *dev)
{
struct ulpi_driver *drv = to_ulpi_driver(dev->driver);
+ int ret;
+
+ ret = of_clk_set_defaults(dev->of_node, false);
+ if (ret < 0)
+ return ret;
return drv->probe(to_ulpi_dev(dev));
}
@@ -87,8 +104,13 @@ static struct bus_type ulpi_bus = {
static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
+ int len;
struct ulpi *ulpi = to_ulpi_dev(dev);
+ len = of_device_get_modalias(dev, buf, PAGE_SIZE - 1);
+ if (len != -ENODEV)
+ return len;
+
return sprintf(buf, "ulpi:v%04xp%04x\n",
ulpi->id.vendor, ulpi->id.product);
}
@@ -153,23 +175,45 @@ EXPORT_SYMBOL_GPL(ulpi_unregister_driver);
/* -------------------------------------------------------------------------- */
-static int ulpi_register(struct device *dev, struct ulpi *ulpi)
+static int ulpi_of_register(struct ulpi *ulpi)
{
- int ret;
+ struct device_node *np = NULL, *child;
+ struct device *parent;
+
+ /* Find a ulpi bus underneath the parent or the grandparent */
+ parent = ulpi->dev.parent;
+ if (parent->of_node)
+ np = of_find_node_by_name(parent->of_node, "ulpi");
+ else if (parent->parent && parent->parent->of_node)
+ np = of_find_node_by_name(parent->parent->of_node, "ulpi");
+ if (!np)
+ return 0;
+
+ child = of_get_next_available_child(np, NULL);
+ of_node_put(np);
+ if (!child)
+ return -EINVAL;
- ulpi->dev.parent = dev; /* needed early for ops */
+ ulpi->dev.of_node = child;
+
+ return 0;
+}
+
+static int ulpi_read_id(struct ulpi *ulpi)
+{
+ int ret;
/* Test the interface */
ret = ulpi_write(ulpi, ULPI_SCRATCH, 0xaa);
if (ret < 0)
- return ret;
+ goto err;
ret = ulpi_read(ulpi, ULPI_SCRATCH);
if (ret < 0)
return ret;
if (ret != 0xaa)
- return -ENODEV;
+ goto err;
ulpi->id.vendor = ulpi_read(ulpi, ULPI_VENDOR_ID_LOW);
ulpi->id.vendor |= ulpi_read(ulpi, ULPI_VENDOR_ID_HIGH) << 8;
@@ -177,13 +221,35 @@ static int ulpi_register(struct device *dev, struct ulpi *ulpi)
ulpi->id.product = ulpi_read(ulpi, ULPI_PRODUCT_ID_LOW);
ulpi->id.product |= ulpi_read(ulpi, ULPI_PRODUCT_ID_HIGH) << 8;
+ /* Some ULPI devices don't have a vendor id so rely on OF match */
+ if (ulpi->id.vendor == 0)
+ goto err;
+
+ request_module("ulpi:v%04xp%04x", ulpi->id.vendor, ulpi->id.product);
+ return 0;
+err:
+ of_device_request_module(&ulpi->dev);
+ return 0;
+}
+
+static int ulpi_register(struct device *dev, struct ulpi *ulpi)
+{
+ int ret;
+
+ ulpi->dev.parent = dev; /* needed early for ops */
ulpi->dev.bus = &ulpi_bus;
ulpi->dev.type = &ulpi_dev_type;
dev_set_name(&ulpi->dev, "%s.ulpi", dev_name(dev));
ACPI_COMPANION_SET(&ulpi->dev, ACPI_COMPANION(dev));
- request_module("ulpi:v%04xp%04x", ulpi->id.vendor, ulpi->id.product);
+ ret = ulpi_of_register(ulpi);
+ if (ret)
+ return ret;
+
+ ret = ulpi_read_id(ulpi);
+ if (ret)
+ return ret;
ret = device_register(&ulpi->dev);
if (ret)
@@ -234,6 +300,7 @@ EXPORT_SYMBOL_GPL(ulpi_register_interface);
*/
void ulpi_unregister_interface(struct ulpi *ulpi)
{
+ of_node_put(ulpi->dev.of_node);
device_unregister(&ulpi->dev);
}
EXPORT_SYMBOL_GPL(ulpi_unregister_interface);
diff --git a/drivers/usb/core/buffer.c b/drivers/usb/core/buffer.c
index 98e39f91723a..b9bf6e2eb6fe 100644
--- a/drivers/usb/core/buffer.c
+++ b/drivers/usb/core/buffer.c
@@ -3,6 +3,9 @@
*
* This implementation plugs in through generic "usb_bus" level methods,
* and should work with all USB controllers, regardless of bus type.
+ *
+ * Released under the GPLv2 only.
+ * SPDX-License-Identifier: GPL-2.0
*/
#include <linux/module.h>
diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c
index a2d90aca779f..4be52c602e9b 100644
--- a/drivers/usb/core/config.c
+++ b/drivers/usb/core/config.c
@@ -1,3 +1,8 @@
+/*
+ * Released under the GPLv2 only.
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
#include <linux/usb.h>
#include <linux/usb/ch9.h>
#include <linux/usb/hcd.h>
@@ -234,6 +239,16 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
if (ifp->desc.bNumEndpoints >= num_ep)
goto skip_to_next_endpoint_or_interface_descriptor;
+ /* Check for duplicate endpoint addresses */
+ for (i = 0; i < ifp->desc.bNumEndpoints; ++i) {
+ if (ifp->endpoint[i].desc.bEndpointAddress ==
+ d->bEndpointAddress) {
+ dev_warn(ddev, "config %d interface %d altsetting %d has a duplicate endpoint with address 0x%X, skipping\n",
+ cfgno, inum, asnum, d->bEndpointAddress);
+ goto skip_to_next_endpoint_or_interface_descriptor;
+ }
+ }
+
endpoint = &ifp->endpoint[ifp->desc.bNumEndpoints];
++ifp->desc.bNumEndpoints;
@@ -265,6 +280,16 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
/*
* Adjust bInterval for quirked devices.
+ */
+ /*
+ * This quirk fixes bIntervals reported in ms.
+ */
+ if (to_usb_device(ddev)->quirks &
+ USB_QUIRK_LINEAR_FRAME_INTR_BINTERVAL) {
+ n = clamp(fls(d->bInterval) + 3, i, j);
+ i = j = n;
+ }
+ /*
* This quirk fixes bIntervals reported in
* linear microframes.
*/
diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c
index ef04b50e6bbb..f2987ddb1cde 100644
--- a/drivers/usb/core/devices.c
+++ b/drivers/usb/core/devices.c
@@ -182,14 +182,8 @@ static char *usb_dump_endpoint_descriptor(int speed, char *start, char *end,
dir = usb_endpoint_dir_in(desc) ? 'I' : 'O';
- if (speed == USB_SPEED_HIGH) {
- switch (usb_endpoint_maxp(desc) & (0x03 << 11)) {
- case 1 << 11:
- bandwidth = 2; break;
- case 2 << 11:
- bandwidth = 3; break;
- }
- }
+ if (speed == USB_SPEED_HIGH)
+ bandwidth = usb_endpoint_maxp_mult(desc);
/* this isn't checking for illegal values */
switch (usb_endpoint_type(desc)) {
@@ -233,7 +227,7 @@ static char *usb_dump_endpoint_descriptor(int speed, char *start, char *end,
start += sprintf(start, format_endpt, desc->bEndpointAddress, dir,
desc->bmAttributes, type,
- (usb_endpoint_maxp(desc) & 0x07ff) *
+ usb_endpoint_maxp(desc) *
bandwidth,
interval, unit);
return start;
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index 4016dae7433b..cfc3cff6e8d5 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -36,6 +36,7 @@
#include <linux/fs.h>
#include <linux/mm.h>
+#include <linux/sched/signal.h>
#include <linux/slab.h>
#include <linux/signal.h>
#include <linux/poll.h>
@@ -134,42 +135,35 @@ enum snoop_when {
#define USB_DEVICE_DEV MKDEV(USB_DEVICE_MAJOR, 0)
/* Limit on the total amount of memory we can allocate for transfers */
-static unsigned usbfs_memory_mb = 16;
+static u32 usbfs_memory_mb = 16;
module_param(usbfs_memory_mb, uint, 0644);
MODULE_PARM_DESC(usbfs_memory_mb,
"maximum MB allowed for usbfs buffers (0 = no limit)");
-/* Hard limit, necessary to avoid arithmetic overflow */
-#define USBFS_XFER_MAX (UINT_MAX / 2 - 1000000)
-
-static atomic_t usbfs_memory_usage; /* Total memory currently allocated */
+static atomic64_t usbfs_memory_usage; /* Total memory currently allocated */
/* Check whether it's okay to allocate more memory for a transfer */
-static int usbfs_increase_memory_usage(unsigned amount)
+static int usbfs_increase_memory_usage(u64 amount)
{
- unsigned lim;
+ u64 lim;
- /*
- * Convert usbfs_memory_mb to bytes, avoiding overflows.
- * 0 means use the hard limit (effectively unlimited).
- */
lim = ACCESS_ONCE(usbfs_memory_mb);
- if (lim == 0 || lim > (USBFS_XFER_MAX >> 20))
- lim = USBFS_XFER_MAX;
- else
- lim <<= 20;
+ lim <<= 20;
- atomic_add(amount, &usbfs_memory_usage);
- if (atomic_read(&usbfs_memory_usage) <= lim)
- return 0;
- atomic_sub(amount, &usbfs_memory_usage);
- return -ENOMEM;
+ atomic64_add(amount, &usbfs_memory_usage);
+
+ if (lim > 0 && atomic64_read(&usbfs_memory_usage) > lim) {
+ atomic64_sub(amount, &usbfs_memory_usage);
+ return -ENOMEM;
+ }
+
+ return 0;
}
/* Memory for a transfer is being deallocated */
-static void usbfs_decrease_memory_usage(unsigned amount)
+static void usbfs_decrease_memory_usage(u64 amount)
{
- atomic_sub(amount, &usbfs_memory_usage);
+ atomic64_sub(amount, &usbfs_memory_usage);
}
static int connected(struct usb_dev_state *ps)
@@ -1191,7 +1185,7 @@ static int proc_bulk(struct usb_dev_state *ps, void __user *arg)
if (!usb_maxpacket(dev, pipe, !(bulk.ep & USB_DIR_IN)))
return -EINVAL;
len1 = bulk.len;
- if (len1 >= USBFS_XFER_MAX)
+ if (len1 >= (INT_MAX - sizeof(struct urb)))
return -EINVAL;
ret = usbfs_increase_memory_usage(len1 + sizeof(struct urb));
if (ret)
@@ -1584,10 +1578,6 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb
return -EINVAL;
}
- if (uurb->buffer_length >= USBFS_XFER_MAX) {
- ret = -EINVAL;
- goto error;
- }
if (uurb->buffer_length > 0 &&
!access_ok(is_in ? VERIFY_WRITE : VERIFY_READ,
uurb->buffer, uurb->buffer_length)) {
@@ -2346,7 +2336,7 @@ static int proc_drop_privileges(struct usb_dev_state *ps, void __user *arg)
if (copy_from_user(&data, arg, sizeof(data)))
return -EFAULT;
- /* This is an one way operation. Once privileges are
+ /* This is a one way operation. Once privileges are
* dropped, you cannot regain them. You may however reissue
* this ioctl to shrink the allowed interfaces mask.
*/
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index dadd1e8dfe09..cdee5130638b 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -15,6 +15,9 @@
* (usb_device_id matching changes by Adam J. Richter)
* (C) Copyright Greg Kroah-Hartman 2002-2003
*
+ * Released under the GPLv2 only.
+ * SPDX-License-Identifier: GPL-2.0
+ *
* NOTE! This is not actually a driver at all, rather this is
* just a collection of helper routines that implement the
* matching, probing, releasing, suspending and resuming for
diff --git a/drivers/usb/core/endpoint.c b/drivers/usb/core/endpoint.c
index 101983b7e8d2..a60bc830a056 100644
--- a/drivers/usb/core/endpoint.c
+++ b/drivers/usb/core/endpoint.c
@@ -5,8 +5,10 @@
* (C) Copyright 2002,2004 IBM Corp.
* (C) Copyright 2006 Novell Inc.
*
- * Endpoint sysfs stuff
+ * Released under the GPLv2 only.
+ * SPDX-License-Identifier: GPL-2.0
*
+ * Endpoint sysfs stuff
*/
#include <linux/kernel.h>
@@ -50,8 +52,7 @@ static ssize_t wMaxPacketSize_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ep_device *ep = to_ep_device(dev);
- return sprintf(buf, "%04x\n",
- usb_endpoint_maxp(ep->desc) & 0x07ff);
+ return sprintf(buf, "%04x\n", usb_endpoint_maxp(ep->desc));
}
static DEVICE_ATTR_RO(wMaxPacketSize);
diff --git a/drivers/usb/core/file.c b/drivers/usb/core/file.c
index 822ced9639aa..e26bd5e773ad 100644
--- a/drivers/usb/core/file.c
+++ b/drivers/usb/core/file.c
@@ -13,6 +13,8 @@
* (usb_device_id matching changes by Adam J. Richter)
* (C) Copyright Greg Kroah-Hartman 2002-2003
*
+ * Released under the GPLv2 only.
+ * SPDX-License-Identifier: GPL-2.0
*/
#include <linux/module.h>
diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c
index 358ca8dd784f..bd3e0c5a6db2 100644
--- a/drivers/usb/core/generic.c
+++ b/drivers/usb/core/generic.c
@@ -15,6 +15,8 @@
* (usb_device_id matching changes by Adam J. Richter)
* (C) Copyright Greg Kroah-Hartman 2002-2003
*
+ * Released under the GPLv2 only.
+ * SPDX-License-Identifier: GPL-2.0
*/
#include <linux/usb.h>
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 479e223f9cff..79bdca5cb9c7 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -520,8 +520,10 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
*/
tbuf_size = max_t(u16, sizeof(struct usb_hub_descriptor), wLength);
tbuf = kzalloc(tbuf_size, GFP_KERNEL);
- if (!tbuf)
- return -ENOMEM;
+ if (!tbuf) {
+ status = -ENOMEM;
+ goto err_alloc;
+ }
bufp = tbuf;
@@ -734,6 +736,7 @@ error:
}
kfree(tbuf);
+ err_alloc:
/* any errors get returned through the urb completion */
spin_lock_irq(&hcd_root_hub_lock);
@@ -3017,6 +3020,7 @@ void usb_remove_hcd(struct usb_hcd *hcd)
}
usb_put_invalidate_rhdev(hcd);
+ hcd->flags = 0;
}
EXPORT_SYMBOL_GPL(usb_remove_hcd);
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index cbb146736f57..5286bf67869a 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -6,6 +6,8 @@
* (C) Copyright 1999 Gregory P. Smith
* (C) Copyright 2001 Brad Hards (bhards@bigpond.net.au)
*
+ * Released under the GPLv2 only.
+ * SPDX-License-Identifier: GPL-2.0
*/
#include <linux/kernel.h>
@@ -13,7 +15,7 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/completion.h>
-#include <linux/sched.h>
+#include <linux/sched/mm.h>
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/ioctl.h>
@@ -27,7 +29,7 @@
#include <linux/random.h>
#include <linux/pm_qos.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <asm/byteorder.h>
#include "hub.h"
@@ -101,6 +103,7 @@ EXPORT_SYMBOL_GPL(ehci_cf_port_reset_rwsem);
static void hub_release(struct kref *kref);
static int usb_reset_and_verify_device(struct usb_device *udev);
+static int hub_port_disable(struct usb_hub *hub, int port1, int set_state);
static inline char *portspeed(struct usb_hub *hub, int portstatus)
{
@@ -899,88 +902,6 @@ static int hub_set_port_link_state(struct usb_hub *hub, int port1,
}
/*
- * If USB 3.0 ports are placed into the Disabled state, they will no longer
- * detect any device connects or disconnects. This is generally not what the
- * USB core wants, since it expects a disabled port to produce a port status
- * change event when a new device connects.
- *
- * Instead, set the link state to Disabled, wait for the link to settle into
- * that state, clear any change bits, and then put the port into the RxDetect
- * state.
- */
-static int hub_usb3_port_disable(struct usb_hub *hub, int port1)
-{
- int ret;
- int total_time;
- u16 portchange, portstatus;
-
- if (!hub_is_superspeed(hub->hdev))
- return -EINVAL;
-
- ret = hub_port_status(hub, port1, &portstatus, &portchange);
- if (ret < 0)
- return ret;
-
- /*
- * USB controller Advanced Micro Devices, Inc. [AMD] FCH USB XHCI
- * Controller [1022:7814] will have spurious result making the following
- * usb 3.0 device hotplugging route to the 2.0 root hub and recognized
- * as high-speed device if we set the usb 3.0 port link state to
- * Disabled. Since it's already in USB_SS_PORT_LS_RX_DETECT state, we
- * check the state here to avoid the bug.
- */
- if ((portstatus & USB_PORT_STAT_LINK_STATE) ==
- USB_SS_PORT_LS_RX_DETECT) {
- dev_dbg(&hub->ports[port1 - 1]->dev,
- "Not disabling port; link state is RxDetect\n");
- return ret;
- }
-
- ret = hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_SS_DISABLED);
- if (ret)
- return ret;
-
- /* Wait for the link to enter the disabled state. */
- for (total_time = 0; ; total_time += HUB_DEBOUNCE_STEP) {
- ret = hub_port_status(hub, port1, &portstatus, &portchange);
- if (ret < 0)
- return ret;
-
- if ((portstatus & USB_PORT_STAT_LINK_STATE) ==
- USB_SS_PORT_LS_SS_DISABLED)
- break;
- if (total_time >= HUB_DEBOUNCE_TIMEOUT)
- break;
- msleep(HUB_DEBOUNCE_STEP);
- }
- if (total_time >= HUB_DEBOUNCE_TIMEOUT)
- dev_warn(&hub->ports[port1 - 1]->dev,
- "Could not disable after %d ms\n", total_time);
-
- return hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_RX_DETECT);
-}
-
-static int hub_port_disable(struct usb_hub *hub, int port1, int set_state)
-{
- struct usb_port *port_dev = hub->ports[port1 - 1];
- struct usb_device *hdev = hub->hdev;
- int ret = 0;
-
- if (port_dev->child && set_state)
- usb_set_device_state(port_dev->child, USB_STATE_NOTATTACHED);
- if (!hub->error) {
- if (hub_is_superspeed(hub->hdev))
- ret = hub_usb3_port_disable(hub, port1);
- else
- ret = usb_clear_port_feature(hdev, port1,
- USB_PORT_FEAT_ENABLE);
- }
- if (ret && ret != -ENODEV)
- dev_err(&port_dev->dev, "cannot disable (err = %d)\n", ret);
- return ret;
-}
-
-/*
* Disable a port and mark a logical connect-change event, so that some
* time later hub_wq will disconnect() any existing usb_device on the port
* and will re-enumerate if there actually is a device attached.
@@ -2731,8 +2652,15 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
if (ret < 0)
return ret;
- /* The port state is unknown until the reset completes. */
- if (!(portstatus & USB_PORT_STAT_RESET))
+ /*
+ * The port state is unknown until the reset completes.
+ *
+ * On top of that, some chips may require additional time
+ * to re-establish a connection after the reset is complete,
+ * so also wait for the connection to be re-established.
+ */
+ if (!(portstatus & USB_PORT_STAT_RESET) &&
+ (portstatus & USB_PORT_STAT_CONNECTION))
break;
/* switch to the long delay after two short delay failures */
@@ -4140,6 +4068,26 @@ void usb_unlocked_enable_lpm(struct usb_device *udev)
}
EXPORT_SYMBOL_GPL(usb_unlocked_enable_lpm);
+/* usb3 devices use U3 for disabled, make sure remote wakeup is disabled */
+static void hub_usb3_port_prepare_disable(struct usb_hub *hub,
+ struct usb_port *port_dev)
+{
+ struct usb_device *udev = port_dev->child;
+ int ret;
+
+ if (udev && udev->port_is_suspended && udev->do_remote_wakeup) {
+ ret = hub_set_port_link_state(hub, port_dev->portnum,
+ USB_SS_PORT_LS_U0);
+ if (!ret) {
+ msleep(USB_RESUME_TIMEOUT);
+ ret = usb_disable_remote_wakeup(udev);
+ }
+ if (ret)
+ dev_warn(&udev->dev,
+ "Port disable: can't disable remote wake\n");
+ udev->do_remote_wakeup = 0;
+ }
+}
#else /* CONFIG_PM */
@@ -4147,6 +4095,9 @@ EXPORT_SYMBOL_GPL(usb_unlocked_enable_lpm);
#define hub_resume NULL
#define hub_reset_resume NULL
+static inline void hub_usb3_port_prepare_disable(struct usb_hub *hub,
+ struct usb_port *port_dev) { }
+
int usb_disable_lpm(struct usb_device *udev)
{
return 0;
@@ -4182,6 +4133,34 @@ static int hub_handle_remote_wakeup(struct usb_hub *hub, unsigned int port,
#endif /* CONFIG_PM */
+/*
+ * USB-3 does not have a similar link state as USB-2 that will avoid negotiating
+ * a connection with a plugged-in cable but will signal the host when the cable
+ * is unplugged. Disable remote wake and set link state to U3 for USB-3 devices
+ */
+static int hub_port_disable(struct usb_hub *hub, int port1, int set_state)
+{
+ struct usb_port *port_dev = hub->ports[port1 - 1];
+ struct usb_device *hdev = hub->hdev;
+ int ret = 0;
+
+ if (!hub->error) {
+ if (hub_is_superspeed(hub->hdev)) {
+ hub_usb3_port_prepare_disable(hub, port_dev);
+ ret = hub_set_port_link_state(hub, port_dev->portnum,
+ USB_SS_PORT_LS_U3);
+ } else {
+ ret = usb_clear_port_feature(hdev, port1,
+ USB_PORT_FEAT_ENABLE);
+ }
+ }
+ if (port_dev->child && set_state)
+ usb_set_device_state(port_dev->child, USB_STATE_NOTATTACHED);
+ if (ret && ret != -ENODEV)
+ dev_err(&port_dev->dev, "cannot disable (err = %d)\n", ret);
+ return ret;
+}
+
/* USB 2.0 spec, 7.1.7.3 / fig 7-29:
*
@@ -4296,7 +4275,7 @@ static void hub_set_initial_usb2_lpm_policy(struct usb_device *udev)
struct usb_hub *hub = usb_hub_to_struct_hub(udev->parent);
int connect_type = USB_PORT_CONNECT_TYPE_UNKNOWN;
- if (!udev->usb2_hw_lpm_capable)
+ if (!udev->usb2_hw_lpm_capable || !udev->bos)
return;
if (hub)
diff --git a/drivers/usb/core/ledtrig-usbport.c b/drivers/usb/core/ledtrig-usbport.c
index 3ed5162677ad..1713248ab15a 100644
--- a/drivers/usb/core/ledtrig-usbport.c
+++ b/drivers/usb/core/ledtrig-usbport.c
@@ -74,8 +74,7 @@ static void usbport_trig_update_count(struct usbport_trig_data *usbport_data)
usbport_data->count = 0;
usb_for_each_dev(usbport_data, usbport_trig_usb_dev_check);
- led_cdev->brightness_set(led_cdev,
- usbport_data->count ? LED_FULL : LED_OFF);
+ led_set_brightness(led_cdev, usbport_data->count ? LED_FULL : LED_OFF);
}
/***************************************
@@ -228,12 +227,12 @@ static int usbport_trig_notify(struct notifier_block *nb, unsigned long action,
case USB_DEVICE_ADD:
usbport_trig_add_usb_dev_ports(usb_dev, usbport_data);
if (observed && usbport_data->count++ == 0)
- led_cdev->brightness_set(led_cdev, LED_FULL);
+ led_set_brightness(led_cdev, LED_FULL);
return NOTIFY_OK;
case USB_DEVICE_REMOVE:
usbport_trig_remove_usb_dev_ports(usbport_data, usb_dev);
if (observed && --usbport_data->count == 0)
- led_cdev->brightness_set(led_cdev, LED_OFF);
+ led_set_brightness(led_cdev, LED_OFF);
return NOTIFY_OK;
}
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index 3a4707746157..2184ef40a82a 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -1,5 +1,8 @@
/*
* message.c - synchronous message handling
+ *
+ * Released under the GPLv2 only.
+ * SPDX-License-Identifier: GPL-2.0
*/
#include <linux/pci.h> /* for scatterlist macros */
@@ -119,12 +122,11 @@ static int usb_internal_control_msg(struct usb_device *usb_dev,
* This function sends a simple control message to a specified endpoint and
* waits for the message to complete, or timeout.
*
- * Don't use this function from within an interrupt context, like a bottom half
- * handler. If you need an asynchronous message, or need to send a message
- * from within interrupt context, use usb_submit_urb().
- * If a thread in your driver uses this call, make sure your disconnect()
- * method can wait for it to complete. Since you don't have a handle on the
- * URB used, you can't cancel the request.
+ * Don't use this function from within an interrupt context. If you need
+ * an asynchronous message, or need to send a message from within interrupt
+ * context, use usb_submit_urb(). If a thread in your driver uses this call,
+ * make sure your disconnect() method can wait for it to complete. Since you
+ * don't have a handle on the URB used, you can't cancel the request.
*
* Return: If successful, the number of bytes transferred. Otherwise, a negative
* error number.
@@ -170,12 +172,11 @@ EXPORT_SYMBOL_GPL(usb_control_msg);
* This function sends a simple interrupt message to a specified endpoint and
* waits for the message to complete, or timeout.
*
- * Don't use this function from within an interrupt context, like a bottom half
- * handler. If you need an asynchronous message, or need to send a message
- * from within interrupt context, use usb_submit_urb() If a thread in your
- * driver uses this call, make sure your disconnect() method can wait for it to
- * complete. Since you don't have a handle on the URB used, you can't cancel
- * the request.
+ * Don't use this function from within an interrupt context. If you need
+ * an asynchronous message, or need to send a message from within interrupt
+ * context, use usb_submit_urb() If a thread in your driver uses this call,
+ * make sure your disconnect() method can wait for it to complete. Since you
+ * don't have a handle on the URB used, you can't cancel the request.
*
* Return:
* If successful, 0. Otherwise a negative error number. The number of actual
@@ -204,12 +205,11 @@ EXPORT_SYMBOL_GPL(usb_interrupt_msg);
* This function sends a simple bulk message to a specified endpoint
* and waits for the message to complete, or timeout.
*
- * Don't use this function from within an interrupt context, like a bottom half
- * handler. If you need an asynchronous message, or need to send a message
- * from within interrupt context, use usb_submit_urb() If a thread in your
- * driver uses this call, make sure your disconnect() method can wait for it to
- * complete. Since you don't have a handle on the URB used, you can't cancel
- * the request.
+ * Don't use this function from within an interrupt context. If you need
+ * an asynchronous message, or need to send a message from within interrupt
+ * context, use usb_submit_urb() If a thread in your driver uses this call,
+ * make sure your disconnect() method can wait for it to complete. Since you
+ * don't have a handle on the URB used, you can't cancel the request.
*
* Because there is no usb_interrupt_msg() and no USBDEVFS_INTERRUPT ioctl,
* users are forced to abuse this routine by using it to submit URBs for
diff --git a/drivers/usb/core/notify.c b/drivers/usb/core/notify.c
index 7728c91dfa2e..b12a463a3e22 100644
--- a/drivers/usb/core/notify.c
+++ b/drivers/usb/core/notify.c
@@ -6,6 +6,8 @@
* notifier functions originally based on those in kernel/sys.c
* but fixed up to not be so broken.
*
+ * Released under the GPLv2 only.
+ * SPDX-License-Identifier: GPL-2.0
*/
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index d2e50a27140c..96b21b0dac1e 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -37,6 +37,10 @@ static const struct usb_device_id usb_quirk_list[] = {
/* CBM - Flash disk */
{ USB_DEVICE(0x0204, 0x6025), .driver_info = USB_QUIRK_RESET_RESUME },
+ /* WORLDE easy key (easykey.25) MIDI controller */
+ { USB_DEVICE(0x0218, 0x0401), .driver_info =
+ USB_QUIRK_CONFIG_INTF_STRINGS },
+
/* HP 5300/5370C scanner */
{ USB_DEVICE(0x03f0, 0x0701), .driver_info =
USB_QUIRK_STRING_FETCH_255 },
@@ -166,6 +170,14 @@ static const struct usb_device_id usb_quirk_list[] = {
/* M-Systems Flash Disk Pioneers */
{ USB_DEVICE(0x08ec, 0x1000), .driver_info = USB_QUIRK_RESET_RESUME },
+ /* Baum Vario Ultra */
+ { USB_DEVICE(0x0904, 0x6101), .driver_info =
+ USB_QUIRK_LINEAR_FRAME_INTR_BINTERVAL },
+ { USB_DEVICE(0x0904, 0x6102), .driver_info =
+ USB_QUIRK_LINEAR_FRAME_INTR_BINTERVAL },
+ { USB_DEVICE(0x0904, 0x6103), .driver_info =
+ USB_QUIRK_LINEAR_FRAME_INTR_BINTERVAL },
+
/* Keytouch QWERTY Panel keyboard */
{ USB_DEVICE(0x0926, 0x3333), .driver_info =
USB_QUIRK_CONFIG_INTF_STRINGS },
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c
index c953a0f1c695..dfc68ed24db1 100644
--- a/drivers/usb/core/sysfs.c
+++ b/drivers/usb/core/sysfs.c
@@ -7,6 +7,8 @@
*
* All of the sysfs file attributes for usb devices and interfaces.
*
+ * Released under the GPLv2 only.
+ * SPDX-License-Identifier: GPL-2.0
*/
@@ -14,6 +16,7 @@
#include <linux/string.h>
#include <linux/usb.h>
#include <linux/usb/quirks.h>
+#include <linux/of.h>
#include "usb.h"
/* Active configuration fields */
@@ -104,6 +107,17 @@ static ssize_t bConfigurationValue_store(struct device *dev,
static DEVICE_ATTR_IGNORE_LOCKDEP(bConfigurationValue, S_IRUGO | S_IWUSR,
bConfigurationValue_show, bConfigurationValue_store);
+#ifdef CONFIG_OF
+static ssize_t devspec_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct device_node *of_node = dev->of_node;
+
+ return sprintf(buf, "%s\n", of_node_full_name(of_node));
+}
+static DEVICE_ATTR_RO(devspec);
+#endif
+
/* String fields */
#define usb_string_attr(name) \
static ssize_t name##_show(struct device *dev, \
@@ -786,6 +800,9 @@ static struct attribute *dev_attrs[] = {
&dev_attr_remove.attr,
&dev_attr_removable.attr,
&dev_attr_ltm_capable.attr,
+#ifdef CONFIG_OF
+ &dev_attr_devspec.attr,
+#endif
NULL,
};
static struct attribute_group dev_attr_grp = {
diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c
index a9039696476e..d75cb8c0f7df 100644
--- a/drivers/usb/core/urb.c
+++ b/drivers/usb/core/urb.c
@@ -1,3 +1,8 @@
+/*
+ * Released under the GPLv2 only.
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
#include <linux/module.h>
#include <linux/string.h>
#include <linux/bitops.h>
@@ -407,11 +412,8 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
}
/* "high bandwidth" mode, 1-3 packets/uframe? */
- if (dev->speed == USB_SPEED_HIGH) {
- int mult = 1 + ((max >> 11) & 0x03);
- max &= 0x07ff;
- max *= mult;
- }
+ if (dev->speed == USB_SPEED_HIGH)
+ max *= usb_endpoint_maxp_mult(&ep->desc);
if (urb->number_of_packets <= 0)
return -EINVAL;
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 592151461017..a2ccc69fb45c 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -12,6 +12,9 @@
* (usb_device_id matching changes by Adam J. Richter)
* (C) Copyright Greg Kroah-Hartman 2002-2003
*
+ * Released under the GPLv2 only.
+ * SPDX-License-Identifier: GPL-2.0
+ *
* NOTE! This is not actually a driver at all, rather this is
* just a collection of helper routines that implement the
* generic USB things that the real drivers can use..
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index 53318126ed91..dc6949248823 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -1,3 +1,8 @@
+/*
+ * Released under the GPLv2 only.
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
#include <linux/pm.h>
#include <linux/acpi.h>
diff --git a/drivers/usb/dwc2/Makefile b/drivers/usb/dwc2/Makefile
index 50fdaace1e73..b9237e1e45d0 100644
--- a/drivers/usb/dwc2/Makefile
+++ b/drivers/usb/dwc2/Makefile
@@ -3,6 +3,7 @@ ccflags-$(CONFIG_USB_DWC2_VERBOSE) += -DVERBOSE_DEBUG
obj-$(CONFIG_USB_DWC2) += dwc2.o
dwc2-y := core.o core_intr.o platform.o
+dwc2-y += params.o
ifneq ($(filter y,$(CONFIG_USB_DWC2_HOST) $(CONFIG_USB_DWC2_DUAL_ROLE)),)
dwc2-y += hcd.o hcd_intr.o
diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c
index 4c0fa0b17353..1b6612c2cdda 100644
--- a/drivers/usb/dwc2/core.c
+++ b/drivers/usb/dwc2/core.c
@@ -104,7 +104,7 @@ static int dwc2_restore_global_registers(struct dwc2_hsotg *hsotg)
gr = &hsotg->gr_backup;
if (!gr->valid) {
dev_err(hsotg->dev, "%s: no global registers to restore\n",
- __func__);
+ __func__);
return -EINVAL;
}
gr->valid = false;
@@ -135,7 +135,7 @@ int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, bool restore)
u32 pcgcctl;
int ret = 0;
- if (!hsotg->core_params->hibernation)
+ if (!hsotg->params.hibernation)
return -ENOTSUPP;
pcgcctl = dwc2_readl(hsotg->regs + PCGCTL);
@@ -155,21 +155,21 @@ int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, bool restore)
ret = dwc2_restore_global_registers(hsotg);
if (ret) {
dev_err(hsotg->dev, "%s: failed to restore registers\n",
- __func__);
+ __func__);
return ret;
}
if (dwc2_is_host_mode(hsotg)) {
ret = dwc2_restore_host_registers(hsotg);
if (ret) {
dev_err(hsotg->dev, "%s: failed to restore host registers\n",
- __func__);
+ __func__);
return ret;
}
} else {
ret = dwc2_restore_device_registers(hsotg);
if (ret) {
dev_err(hsotg->dev, "%s: failed to restore device registers\n",
- __func__);
+ __func__);
return ret;
}
}
@@ -188,14 +188,14 @@ int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg)
u32 pcgcctl;
int ret = 0;
- if (!hsotg->core_params->hibernation)
+ if (!hsotg->params.hibernation)
return -ENOTSUPP;
/* Backup all registers */
ret = dwc2_backup_global_registers(hsotg);
if (ret) {
dev_err(hsotg->dev, "%s: failed to backup global registers\n",
- __func__);
+ __func__);
return ret;
}
@@ -203,14 +203,14 @@ int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg)
ret = dwc2_backup_host_registers(hsotg);
if (ret) {
dev_err(hsotg->dev, "%s: failed to backup host registers\n",
- __func__);
+ __func__);
return ret;
}
} else {
ret = dwc2_backup_device_registers(hsotg);
if (ret) {
dev_err(hsotg->dev, "%s: failed to backup device registers\n",
- __func__);
+ __func__);
return ret;
}
}
@@ -313,7 +313,7 @@ static bool dwc2_iddig_filter_enabled(struct dwc2_hsotg *hsotg)
* Do core a soft reset of the core. Be careful with this because it
* resets all the internal state machines of the core.
*/
-int dwc2_core_reset(struct dwc2_hsotg *hsotg)
+int dwc2_core_reset(struct dwc2_hsotg *hsotg, bool skip_wait)
{
u32 greset;
int count = 0;
@@ -369,7 +369,7 @@ int dwc2_core_reset(struct dwc2_hsotg *hsotg)
}
} while (!(greset & GRSTCTL_AHBIDLE));
- if (wait_for_host_mode)
+ if (wait_for_host_mode && !skip_wait)
dwc2_wait_for_mode(hsotg, true);
return 0;
@@ -445,7 +445,7 @@ static bool dwc2_force_mode(struct dwc2_hsotg *hsotg, bool host)
* the force mode. We only need to call this once during probe if
* dr_mode == OTG.
*/
-static void dwc2_clear_force_mode(struct dwc2_hsotg *hsotg)
+void dwc2_clear_force_mode(struct dwc2_hsotg *hsotg)
{
u32 gusbcfg;
@@ -455,7 +455,7 @@ static void dwc2_clear_force_mode(struct dwc2_hsotg *hsotg)
dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG);
if (dwc2_iddig_filter_enabled(hsotg))
- usleep_range(100000, 110000);
+ msleep(100);
}
/*
@@ -500,7 +500,7 @@ int dwc2_core_reset_and_force_dr_mode(struct dwc2_hsotg *hsotg)
{
int retval;
- retval = dwc2_core_reset(hsotg);
+ retval = dwc2_core_reset(hsotg, false);
if (retval)
return retval;
@@ -541,7 +541,7 @@ void dwc2_dump_host_registers(struct dwc2_hsotg *hsotg)
addr = hsotg->regs + HAINTMSK;
dev_dbg(hsotg->dev, "HAINTMSK @0x%08lX : 0x%08X\n",
(unsigned long)addr, dwc2_readl(addr));
- if (hsotg->core_params->dma_desc_enable > 0) {
+ 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));
@@ -551,7 +551,7 @@ void dwc2_dump_host_registers(struct dwc2_hsotg *hsotg)
dev_dbg(hsotg->dev, "HPRT0 @0x%08lX : 0x%08X\n",
(unsigned long)addr, dwc2_readl(addr));
- for (i = 0; i < hsotg->core_params->host_channels; i++) {
+ 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",
@@ -571,7 +571,7 @@ void dwc2_dump_host_registers(struct dwc2_hsotg *hsotg)
addr = hsotg->regs + HCDMA(i);
dev_dbg(hsotg->dev, "HCDMA @0x%08lX : 0x%08X\n",
(unsigned long)addr, dwc2_readl(addr));
- if (hsotg->core_params->dma_desc_enable > 0) {
+ 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));
@@ -735,704 +735,13 @@ void dwc2_flush_rx_fifo(struct dwc2_hsotg *hsotg)
udelay(1);
}
-#define DWC2_OUT_OF_BOUNDS(a, b, c) ((a) < (b) || (a) > (c))
-
-/* Parameter access functions */
-void dwc2_set_param_otg_cap(struct dwc2_hsotg *hsotg, int val)
-{
- int valid = 1;
-
- switch (val) {
- case DWC2_CAP_PARAM_HNP_SRP_CAPABLE:
- if (hsotg->hw_params.op_mode != GHWCFG2_OP_MODE_HNP_SRP_CAPABLE)
- valid = 0;
- break;
- case DWC2_CAP_PARAM_SRP_ONLY_CAPABLE:
- switch (hsotg->hw_params.op_mode) {
- case GHWCFG2_OP_MODE_HNP_SRP_CAPABLE:
- case GHWCFG2_OP_MODE_SRP_ONLY_CAPABLE:
- case GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE:
- case GHWCFG2_OP_MODE_SRP_CAPABLE_HOST:
- break;
- default:
- valid = 0;
- break;
- }
- break;
- case DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE:
- /* always valid */
- break;
- default:
- valid = 0;
- break;
- }
-
- if (!valid) {
- if (val >= 0)
- dev_err(hsotg->dev,
- "%d invalid for otg_cap parameter. Check HW configuration.\n",
- val);
- switch (hsotg->hw_params.op_mode) {
- case GHWCFG2_OP_MODE_HNP_SRP_CAPABLE:
- val = DWC2_CAP_PARAM_HNP_SRP_CAPABLE;
- break;
- case GHWCFG2_OP_MODE_SRP_ONLY_CAPABLE:
- case GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE:
- case GHWCFG2_OP_MODE_SRP_CAPABLE_HOST:
- val = DWC2_CAP_PARAM_SRP_ONLY_CAPABLE;
- break;
- default:
- val = DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE;
- break;
- }
- dev_dbg(hsotg->dev, "Setting otg_cap to %d\n", val);
- }
-
- hsotg->core_params->otg_cap = val;
-}
-
-void dwc2_set_param_dma_enable(struct dwc2_hsotg *hsotg, int val)
-{
- int valid = 1;
-
- if (val > 0 && hsotg->hw_params.arch == GHWCFG2_SLAVE_ONLY_ARCH)
- valid = 0;
- if (val < 0)
- valid = 0;
-
- if (!valid) {
- if (val >= 0)
- dev_err(hsotg->dev,
- "%d invalid for dma_enable parameter. Check HW configuration.\n",
- val);
- val = hsotg->hw_params.arch != GHWCFG2_SLAVE_ONLY_ARCH;
- dev_dbg(hsotg->dev, "Setting dma_enable to %d\n", val);
- }
-
- hsotg->core_params->dma_enable = val;
-}
-
-void dwc2_set_param_dma_desc_enable(struct dwc2_hsotg *hsotg, int val)
-{
- int valid = 1;
-
- if (val > 0 && (hsotg->core_params->dma_enable <= 0 ||
- !hsotg->hw_params.dma_desc_enable))
- valid = 0;
- if (val < 0)
- valid = 0;
-
- if (!valid) {
- if (val >= 0)
- dev_err(hsotg->dev,
- "%d invalid for dma_desc_enable parameter. Check HW configuration.\n",
- val);
- val = (hsotg->core_params->dma_enable > 0 &&
- hsotg->hw_params.dma_desc_enable);
- dev_dbg(hsotg->dev, "Setting dma_desc_enable to %d\n", val);
- }
-
- hsotg->core_params->dma_desc_enable = val;
-}
-
-void dwc2_set_param_dma_desc_fs_enable(struct dwc2_hsotg *hsotg, int val)
-{
- int valid = 1;
-
- if (val > 0 && (hsotg->core_params->dma_enable <= 0 ||
- !hsotg->hw_params.dma_desc_enable))
- valid = 0;
- if (val < 0)
- valid = 0;
-
- if (!valid) {
- if (val >= 0)
- dev_err(hsotg->dev,
- "%d invalid for dma_desc_fs_enable parameter. Check HW configuration.\n",
- val);
- val = (hsotg->core_params->dma_enable > 0 &&
- hsotg->hw_params.dma_desc_enable);
- }
-
- hsotg->core_params->dma_desc_fs_enable = val;
- dev_dbg(hsotg->dev, "Setting dma_desc_fs_enable to %d\n", val);
-}
-
-void dwc2_set_param_host_support_fs_ls_low_power(struct dwc2_hsotg *hsotg,
- int val)
-{
- if (DWC2_OUT_OF_BOUNDS(val, 0, 1)) {
- if (val >= 0) {
- dev_err(hsotg->dev,
- "Wrong value for host_support_fs_low_power\n");
- dev_err(hsotg->dev,
- "host_support_fs_low_power must be 0 or 1\n");
- }
- val = 0;
- dev_dbg(hsotg->dev,
- "Setting host_support_fs_low_power to %d\n", val);
- }
-
- hsotg->core_params->host_support_fs_ls_low_power = val;
-}
-
-void dwc2_set_param_enable_dynamic_fifo(struct dwc2_hsotg *hsotg, int val)
-{
- int valid = 1;
-
- if (val > 0 && !hsotg->hw_params.enable_dynamic_fifo)
- valid = 0;
- if (val < 0)
- valid = 0;
-
- if (!valid) {
- if (val >= 0)
- dev_err(hsotg->dev,
- "%d invalid for enable_dynamic_fifo parameter. Check HW configuration.\n",
- val);
- val = hsotg->hw_params.enable_dynamic_fifo;
- dev_dbg(hsotg->dev, "Setting enable_dynamic_fifo to %d\n", val);
- }
-
- hsotg->core_params->enable_dynamic_fifo = val;
-}
-
-void dwc2_set_param_host_rx_fifo_size(struct dwc2_hsotg *hsotg, int val)
-{
- int valid = 1;
-
- if (val < 16 || val > hsotg->hw_params.host_rx_fifo_size)
- valid = 0;
-
- if (!valid) {
- if (val >= 0)
- dev_err(hsotg->dev,
- "%d invalid for host_rx_fifo_size. Check HW configuration.\n",
- val);
- val = hsotg->hw_params.host_rx_fifo_size;
- dev_dbg(hsotg->dev, "Setting host_rx_fifo_size to %d\n", val);
- }
-
- hsotg->core_params->host_rx_fifo_size = val;
-}
-
-void dwc2_set_param_host_nperio_tx_fifo_size(struct dwc2_hsotg *hsotg, int val)
-{
- int valid = 1;
-
- if (val < 16 || val > hsotg->hw_params.host_nperio_tx_fifo_size)
- valid = 0;
-
- if (!valid) {
- if (val >= 0)
- dev_err(hsotg->dev,
- "%d invalid for host_nperio_tx_fifo_size. Check HW configuration.\n",
- val);
- val = hsotg->hw_params.host_nperio_tx_fifo_size;
- dev_dbg(hsotg->dev, "Setting host_nperio_tx_fifo_size to %d\n",
- val);
- }
-
- hsotg->core_params->host_nperio_tx_fifo_size = val;
-}
-
-void dwc2_set_param_host_perio_tx_fifo_size(struct dwc2_hsotg *hsotg, int val)
-{
- int valid = 1;
-
- if (val < 16 || val > hsotg->hw_params.host_perio_tx_fifo_size)
- valid = 0;
-
- if (!valid) {
- if (val >= 0)
- dev_err(hsotg->dev,
- "%d invalid for host_perio_tx_fifo_size. Check HW configuration.\n",
- val);
- val = hsotg->hw_params.host_perio_tx_fifo_size;
- dev_dbg(hsotg->dev, "Setting host_perio_tx_fifo_size to %d\n",
- val);
- }
-
- hsotg->core_params->host_perio_tx_fifo_size = val;
-}
-
-void dwc2_set_param_max_transfer_size(struct dwc2_hsotg *hsotg, int val)
-{
- int valid = 1;
-
- if (val < 2047 || val > hsotg->hw_params.max_transfer_size)
- valid = 0;
-
- if (!valid) {
- if (val >= 0)
- dev_err(hsotg->dev,
- "%d invalid for max_transfer_size. Check HW configuration.\n",
- val);
- val = hsotg->hw_params.max_transfer_size;
- dev_dbg(hsotg->dev, "Setting max_transfer_size to %d\n", val);
- }
-
- hsotg->core_params->max_transfer_size = val;
-}
-
-void dwc2_set_param_max_packet_count(struct dwc2_hsotg *hsotg, int val)
-{
- int valid = 1;
-
- if (val < 15 || val > hsotg->hw_params.max_packet_count)
- valid = 0;
-
- if (!valid) {
- if (val >= 0)
- dev_err(hsotg->dev,
- "%d invalid for max_packet_count. Check HW configuration.\n",
- val);
- val = hsotg->hw_params.max_packet_count;
- dev_dbg(hsotg->dev, "Setting max_packet_count to %d\n", val);
- }
-
- hsotg->core_params->max_packet_count = val;
-}
-
-void dwc2_set_param_host_channels(struct dwc2_hsotg *hsotg, int val)
-{
- int valid = 1;
-
- if (val < 1 || val > hsotg->hw_params.host_channels)
- valid = 0;
-
- if (!valid) {
- if (val >= 0)
- dev_err(hsotg->dev,
- "%d invalid for host_channels. Check HW configuration.\n",
- val);
- val = hsotg->hw_params.host_channels;
- dev_dbg(hsotg->dev, "Setting host_channels to %d\n", val);
- }
-
- hsotg->core_params->host_channels = val;
-}
-
-void dwc2_set_param_phy_type(struct dwc2_hsotg *hsotg, int val)
-{
- int valid = 0;
- u32 hs_phy_type, fs_phy_type;
-
- if (DWC2_OUT_OF_BOUNDS(val, DWC2_PHY_TYPE_PARAM_FS,
- DWC2_PHY_TYPE_PARAM_ULPI)) {
- if (val >= 0) {
- dev_err(hsotg->dev, "Wrong value for phy_type\n");
- dev_err(hsotg->dev, "phy_type must be 0, 1 or 2\n");
- }
-
- valid = 0;
- }
-
- hs_phy_type = hsotg->hw_params.hs_phy_type;
- fs_phy_type = hsotg->hw_params.fs_phy_type;
- if (val == DWC2_PHY_TYPE_PARAM_UTMI &&
- (hs_phy_type == GHWCFG2_HS_PHY_TYPE_UTMI ||
- hs_phy_type == GHWCFG2_HS_PHY_TYPE_UTMI_ULPI))
- valid = 1;
- else if (val == DWC2_PHY_TYPE_PARAM_ULPI &&
- (hs_phy_type == GHWCFG2_HS_PHY_TYPE_ULPI ||
- hs_phy_type == GHWCFG2_HS_PHY_TYPE_UTMI_ULPI))
- valid = 1;
- else if (val == DWC2_PHY_TYPE_PARAM_FS &&
- fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED)
- valid = 1;
-
- if (!valid) {
- if (val >= 0)
- dev_err(hsotg->dev,
- "%d invalid for phy_type. Check HW configuration.\n",
- val);
- val = DWC2_PHY_TYPE_PARAM_FS;
- if (hs_phy_type != GHWCFG2_HS_PHY_TYPE_NOT_SUPPORTED) {
- if (hs_phy_type == GHWCFG2_HS_PHY_TYPE_UTMI ||
- hs_phy_type == GHWCFG2_HS_PHY_TYPE_UTMI_ULPI)
- val = DWC2_PHY_TYPE_PARAM_UTMI;
- else
- val = DWC2_PHY_TYPE_PARAM_ULPI;
- }
- dev_dbg(hsotg->dev, "Setting phy_type to %d\n", val);
- }
-
- hsotg->core_params->phy_type = val;
-}
-
-static int dwc2_get_param_phy_type(struct dwc2_hsotg *hsotg)
-{
- return hsotg->core_params->phy_type;
-}
-
-void dwc2_set_param_speed(struct dwc2_hsotg *hsotg, int val)
-{
- int valid = 1;
-
- if (DWC2_OUT_OF_BOUNDS(val, 0, 1)) {
- if (val >= 0) {
- dev_err(hsotg->dev, "Wrong value for speed parameter\n");
- dev_err(hsotg->dev, "max_speed parameter must be 0 or 1\n");
- }
- valid = 0;
- }
-
- if (val == DWC2_SPEED_PARAM_HIGH &&
- dwc2_get_param_phy_type(hsotg) == DWC2_PHY_TYPE_PARAM_FS)
- valid = 0;
-
- if (!valid) {
- if (val >= 0)
- dev_err(hsotg->dev,
- "%d invalid for speed parameter. Check HW configuration.\n",
- val);
- val = dwc2_get_param_phy_type(hsotg) == DWC2_PHY_TYPE_PARAM_FS ?
- DWC2_SPEED_PARAM_FULL : DWC2_SPEED_PARAM_HIGH;
- dev_dbg(hsotg->dev, "Setting speed to %d\n", val);
- }
-
- hsotg->core_params->speed = val;
-}
-
-void dwc2_set_param_host_ls_low_power_phy_clk(struct dwc2_hsotg *hsotg, int val)
-{
- int valid = 1;
-
- if (DWC2_OUT_OF_BOUNDS(val, DWC2_HOST_LS_LOW_POWER_PHY_CLK_PARAM_48MHZ,
- DWC2_HOST_LS_LOW_POWER_PHY_CLK_PARAM_6MHZ)) {
- if (val >= 0) {
- dev_err(hsotg->dev,
- "Wrong value for host_ls_low_power_phy_clk parameter\n");
- dev_err(hsotg->dev,
- "host_ls_low_power_phy_clk must be 0 or 1\n");
- }
- valid = 0;
- }
-
- if (val == DWC2_HOST_LS_LOW_POWER_PHY_CLK_PARAM_48MHZ &&
- dwc2_get_param_phy_type(hsotg) == DWC2_PHY_TYPE_PARAM_FS)
- valid = 0;
-
- if (!valid) {
- if (val >= 0)
- dev_err(hsotg->dev,
- "%d invalid for host_ls_low_power_phy_clk. Check HW configuration.\n",
- val);
- val = dwc2_get_param_phy_type(hsotg) == DWC2_PHY_TYPE_PARAM_FS
- ? DWC2_HOST_LS_LOW_POWER_PHY_CLK_PARAM_6MHZ
- : DWC2_HOST_LS_LOW_POWER_PHY_CLK_PARAM_48MHZ;
- dev_dbg(hsotg->dev, "Setting host_ls_low_power_phy_clk to %d\n",
- val);
- }
-
- hsotg->core_params->host_ls_low_power_phy_clk = val;
-}
-
-void dwc2_set_param_phy_ulpi_ddr(struct dwc2_hsotg *hsotg, int val)
-{
- if (DWC2_OUT_OF_BOUNDS(val, 0, 1)) {
- if (val >= 0) {
- dev_err(hsotg->dev, "Wrong value for phy_ulpi_ddr\n");
- dev_err(hsotg->dev, "phy_upli_ddr must be 0 or 1\n");
- }
- val = 0;
- dev_dbg(hsotg->dev, "Setting phy_upli_ddr to %d\n", val);
- }
-
- hsotg->core_params->phy_ulpi_ddr = val;
-}
-
-void dwc2_set_param_phy_ulpi_ext_vbus(struct dwc2_hsotg *hsotg, int val)
-{
- if (DWC2_OUT_OF_BOUNDS(val, 0, 1)) {
- if (val >= 0) {
- dev_err(hsotg->dev,
- "Wrong value for phy_ulpi_ext_vbus\n");
- dev_err(hsotg->dev,
- "phy_ulpi_ext_vbus must be 0 or 1\n");
- }
- val = 0;
- dev_dbg(hsotg->dev, "Setting phy_ulpi_ext_vbus to %d\n", val);
- }
-
- hsotg->core_params->phy_ulpi_ext_vbus = val;
-}
-
-void dwc2_set_param_phy_utmi_width(struct dwc2_hsotg *hsotg, int val)
-{
- int valid = 0;
-
- switch (hsotg->hw_params.utmi_phy_data_width) {
- case GHWCFG4_UTMI_PHY_DATA_WIDTH_8:
- valid = (val == 8);
- break;
- case GHWCFG4_UTMI_PHY_DATA_WIDTH_16:
- valid = (val == 16);
- break;
- case GHWCFG4_UTMI_PHY_DATA_WIDTH_8_OR_16:
- valid = (val == 8 || val == 16);
- break;
- }
-
- if (!valid) {
- if (val >= 0) {
- dev_err(hsotg->dev,
- "%d invalid for phy_utmi_width. Check HW configuration.\n",
- val);
- }
- val = (hsotg->hw_params.utmi_phy_data_width ==
- GHWCFG4_UTMI_PHY_DATA_WIDTH_8) ? 8 : 16;
- dev_dbg(hsotg->dev, "Setting phy_utmi_width to %d\n", val);
- }
-
- hsotg->core_params->phy_utmi_width = val;
-}
-
-void dwc2_set_param_ulpi_fs_ls(struct dwc2_hsotg *hsotg, int val)
-{
- if (DWC2_OUT_OF_BOUNDS(val, 0, 1)) {
- if (val >= 0) {
- dev_err(hsotg->dev, "Wrong value for ulpi_fs_ls\n");
- dev_err(hsotg->dev, "ulpi_fs_ls must be 0 or 1\n");
- }
- val = 0;
- dev_dbg(hsotg->dev, "Setting ulpi_fs_ls to %d\n", val);
- }
-
- hsotg->core_params->ulpi_fs_ls = val;
-}
-
-void dwc2_set_param_ts_dline(struct dwc2_hsotg *hsotg, int val)
-{
- if (DWC2_OUT_OF_BOUNDS(val, 0, 1)) {
- if (val >= 0) {
- dev_err(hsotg->dev, "Wrong value for ts_dline\n");
- dev_err(hsotg->dev, "ts_dline must be 0 or 1\n");
- }
- val = 0;
- dev_dbg(hsotg->dev, "Setting ts_dline to %d\n", val);
- }
-
- hsotg->core_params->ts_dline = val;
-}
-
-void dwc2_set_param_i2c_enable(struct dwc2_hsotg *hsotg, int val)
-{
- int valid = 1;
-
- if (DWC2_OUT_OF_BOUNDS(val, 0, 1)) {
- if (val >= 0) {
- dev_err(hsotg->dev, "Wrong value for i2c_enable\n");
- dev_err(hsotg->dev, "i2c_enable must be 0 or 1\n");
- }
-
- valid = 0;
- }
-
- if (val == 1 && !(hsotg->hw_params.i2c_enable))
- valid = 0;
-
- if (!valid) {
- if (val >= 0)
- dev_err(hsotg->dev,
- "%d invalid for i2c_enable. Check HW configuration.\n",
- val);
- val = hsotg->hw_params.i2c_enable;
- dev_dbg(hsotg->dev, "Setting i2c_enable to %d\n", val);
- }
-
- hsotg->core_params->i2c_enable = val;
-}
-
-void dwc2_set_param_en_multiple_tx_fifo(struct dwc2_hsotg *hsotg, int val)
-{
- int valid = 1;
-
- if (DWC2_OUT_OF_BOUNDS(val, 0, 1)) {
- if (val >= 0) {
- dev_err(hsotg->dev,
- "Wrong value for en_multiple_tx_fifo,\n");
- dev_err(hsotg->dev,
- "en_multiple_tx_fifo must be 0 or 1\n");
- }
- valid = 0;
- }
-
- if (val == 1 && !hsotg->hw_params.en_multiple_tx_fifo)
- valid = 0;
-
- if (!valid) {
- if (val >= 0)
- dev_err(hsotg->dev,
- "%d invalid for parameter en_multiple_tx_fifo. Check HW configuration.\n",
- val);
- val = hsotg->hw_params.en_multiple_tx_fifo;
- dev_dbg(hsotg->dev, "Setting en_multiple_tx_fifo to %d\n", val);
- }
-
- hsotg->core_params->en_multiple_tx_fifo = val;
-}
-
-void dwc2_set_param_reload_ctl(struct dwc2_hsotg *hsotg, int val)
-{
- int valid = 1;
-
- if (DWC2_OUT_OF_BOUNDS(val, 0, 1)) {
- if (val >= 0) {
- dev_err(hsotg->dev,
- "'%d' invalid for parameter reload_ctl\n", val);
- dev_err(hsotg->dev, "reload_ctl must be 0 or 1\n");
- }
- valid = 0;
- }
-
- if (val == 1 && hsotg->hw_params.snpsid < DWC2_CORE_REV_2_92a)
- valid = 0;
-
- if (!valid) {
- if (val >= 0)
- dev_err(hsotg->dev,
- "%d invalid for parameter reload_ctl. Check HW configuration.\n",
- val);
- val = hsotg->hw_params.snpsid >= DWC2_CORE_REV_2_92a;
- dev_dbg(hsotg->dev, "Setting reload_ctl to %d\n", val);
- }
-
- hsotg->core_params->reload_ctl = val;
-}
-
-void dwc2_set_param_ahbcfg(struct dwc2_hsotg *hsotg, int val)
-{
- if (val != -1)
- hsotg->core_params->ahbcfg = val;
- else
- hsotg->core_params->ahbcfg = GAHBCFG_HBSTLEN_INCR4 <<
- GAHBCFG_HBSTLEN_SHIFT;
-}
-
-void dwc2_set_param_otg_ver(struct dwc2_hsotg *hsotg, int val)
-{
- if (DWC2_OUT_OF_BOUNDS(val, 0, 1)) {
- if (val >= 0) {
- dev_err(hsotg->dev,
- "'%d' invalid for parameter otg_ver\n", val);
- dev_err(hsotg->dev,
- "otg_ver must be 0 (for OTG 1.3 support) or 1 (for OTG 2.0 support)\n");
- }
- val = 0;
- dev_dbg(hsotg->dev, "Setting otg_ver to %d\n", val);
- }
-
- hsotg->core_params->otg_ver = val;
-}
-
-static void dwc2_set_param_uframe_sched(struct dwc2_hsotg *hsotg, int val)
-{
- if (DWC2_OUT_OF_BOUNDS(val, 0, 1)) {
- if (val >= 0) {
- dev_err(hsotg->dev,
- "'%d' invalid for parameter uframe_sched\n",
- val);
- dev_err(hsotg->dev, "uframe_sched must be 0 or 1\n");
- }
- val = 1;
- dev_dbg(hsotg->dev, "Setting uframe_sched to %d\n", val);
- }
-
- hsotg->core_params->uframe_sched = val;
-}
-
-static void dwc2_set_param_external_id_pin_ctl(struct dwc2_hsotg *hsotg,
- int val)
-{
- if (DWC2_OUT_OF_BOUNDS(val, 0, 1)) {
- if (val >= 0) {
- dev_err(hsotg->dev,
- "'%d' invalid for parameter external_id_pin_ctl\n",
- val);
- dev_err(hsotg->dev, "external_id_pin_ctl must be 0 or 1\n");
- }
- val = 0;
- dev_dbg(hsotg->dev, "Setting external_id_pin_ctl to %d\n", val);
- }
-
- hsotg->core_params->external_id_pin_ctl = val;
-}
-
-static void dwc2_set_param_hibernation(struct dwc2_hsotg *hsotg,
- int val)
-{
- if (DWC2_OUT_OF_BOUNDS(val, 0, 1)) {
- if (val >= 0) {
- dev_err(hsotg->dev,
- "'%d' invalid for parameter hibernation\n",
- val);
- dev_err(hsotg->dev, "hibernation must be 0 or 1\n");
- }
- val = 0;
- dev_dbg(hsotg->dev, "Setting hibernation to %d\n", val);
- }
-
- hsotg->core_params->hibernation = val;
-}
-
-/*
- * This function is called during module intialization to pass module parameters
- * for the DWC_otg core.
- */
-void dwc2_set_parameters(struct dwc2_hsotg *hsotg,
- const struct dwc2_core_params *params)
-{
- dev_dbg(hsotg->dev, "%s()\n", __func__);
-
- dwc2_set_param_otg_cap(hsotg, params->otg_cap);
- dwc2_set_param_dma_enable(hsotg, params->dma_enable);
- dwc2_set_param_dma_desc_enable(hsotg, params->dma_desc_enable);
- dwc2_set_param_dma_desc_fs_enable(hsotg, params->dma_desc_fs_enable);
- dwc2_set_param_host_support_fs_ls_low_power(hsotg,
- params->host_support_fs_ls_low_power);
- dwc2_set_param_enable_dynamic_fifo(hsotg,
- params->enable_dynamic_fifo);
- dwc2_set_param_host_rx_fifo_size(hsotg,
- params->host_rx_fifo_size);
- dwc2_set_param_host_nperio_tx_fifo_size(hsotg,
- params->host_nperio_tx_fifo_size);
- dwc2_set_param_host_perio_tx_fifo_size(hsotg,
- params->host_perio_tx_fifo_size);
- dwc2_set_param_max_transfer_size(hsotg,
- params->max_transfer_size);
- dwc2_set_param_max_packet_count(hsotg,
- params->max_packet_count);
- dwc2_set_param_host_channels(hsotg, params->host_channels);
- dwc2_set_param_phy_type(hsotg, params->phy_type);
- dwc2_set_param_speed(hsotg, params->speed);
- dwc2_set_param_host_ls_low_power_phy_clk(hsotg,
- params->host_ls_low_power_phy_clk);
- dwc2_set_param_phy_ulpi_ddr(hsotg, params->phy_ulpi_ddr);
- dwc2_set_param_phy_ulpi_ext_vbus(hsotg,
- params->phy_ulpi_ext_vbus);
- dwc2_set_param_phy_utmi_width(hsotg, params->phy_utmi_width);
- dwc2_set_param_ulpi_fs_ls(hsotg, params->ulpi_fs_ls);
- dwc2_set_param_ts_dline(hsotg, params->ts_dline);
- dwc2_set_param_i2c_enable(hsotg, params->i2c_enable);
- dwc2_set_param_en_multiple_tx_fifo(hsotg,
- params->en_multiple_tx_fifo);
- dwc2_set_param_reload_ctl(hsotg, params->reload_ctl);
- dwc2_set_param_ahbcfg(hsotg, params->ahbcfg);
- dwc2_set_param_otg_ver(hsotg, params->otg_ver);
- dwc2_set_param_uframe_sched(hsotg, params->uframe_sched);
- dwc2_set_param_external_id_pin_ctl(hsotg, params->external_id_pin_ctl);
- dwc2_set_param_hibernation(hsotg, params->hibernation);
-}
-
/*
* Forces either host or device mode if the controller is not
* currently in that mode.
*
* Returns true if the mode was forced.
*/
-static bool dwc2_force_mode_if_needed(struct dwc2_hsotg *hsotg, bool host)
+bool dwc2_force_mode_if_needed(struct dwc2_hsotg *hsotg, bool host)
{
if (host && dwc2_is_host_mode(hsotg))
return false;
@@ -1442,234 +751,6 @@ static bool dwc2_force_mode_if_needed(struct dwc2_hsotg *hsotg, bool host)
return dwc2_force_mode(hsotg, host);
}
-/*
- * Gets host hardware parameters. Forces host mode if not currently in
- * host mode. Should be called immediately after a core soft reset in
- * order to get the reset values.
- */
-static void dwc2_get_host_hwparams(struct dwc2_hsotg *hsotg)
-{
- struct dwc2_hw_params *hw = &hsotg->hw_params;
- u32 gnptxfsiz;
- u32 hptxfsiz;
- bool forced;
-
- if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL)
- return;
-
- forced = dwc2_force_mode_if_needed(hsotg, true);
-
- gnptxfsiz = dwc2_readl(hsotg->regs + GNPTXFSIZ);
- hptxfsiz = dwc2_readl(hsotg->regs + HPTXFSIZ);
- dev_dbg(hsotg->dev, "gnptxfsiz=%08x\n", gnptxfsiz);
- dev_dbg(hsotg->dev, "hptxfsiz=%08x\n", hptxfsiz);
-
- if (forced)
- dwc2_clear_force_mode(hsotg);
-
- hw->host_nperio_tx_fifo_size = (gnptxfsiz & FIFOSIZE_DEPTH_MASK) >>
- FIFOSIZE_DEPTH_SHIFT;
- hw->host_perio_tx_fifo_size = (hptxfsiz & FIFOSIZE_DEPTH_MASK) >>
- FIFOSIZE_DEPTH_SHIFT;
-}
-
-/*
- * Gets device hardware parameters. Forces device mode if not
- * currently in device mode. Should be called immediately after a core
- * soft reset in order to get the reset values.
- */
-static void dwc2_get_dev_hwparams(struct dwc2_hsotg *hsotg)
-{
- struct dwc2_hw_params *hw = &hsotg->hw_params;
- bool forced;
- u32 gnptxfsiz;
-
- if (hsotg->dr_mode == USB_DR_MODE_HOST)
- return;
-
- forced = dwc2_force_mode_if_needed(hsotg, false);
-
- gnptxfsiz = dwc2_readl(hsotg->regs + GNPTXFSIZ);
- dev_dbg(hsotg->dev, "gnptxfsiz=%08x\n", gnptxfsiz);
-
- if (forced)
- dwc2_clear_force_mode(hsotg);
-
- hw->dev_nperio_tx_fifo_size = (gnptxfsiz & FIFOSIZE_DEPTH_MASK) >>
- FIFOSIZE_DEPTH_SHIFT;
-}
-
-/**
- * During device initialization, read various hardware configuration
- * registers and interpret the contents.
- */
-int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
-{
- struct dwc2_hw_params *hw = &hsotg->hw_params;
- unsigned width;
- u32 hwcfg1, hwcfg2, hwcfg3, hwcfg4;
- u32 grxfsiz;
-
- /*
- * Attempt to ensure this device is really a DWC_otg Controller.
- * Read and verify the GSNPSID register contents. The value should be
- * 0x45f42xxx or 0x45f43xxx, which corresponds to either "OT2" or "OT3",
- * as in "OTG version 2.xx" or "OTG version 3.xx".
- */
- hw->snpsid = dwc2_readl(hsotg->regs + GSNPSID);
- if ((hw->snpsid & 0xfffff000) != 0x4f542000 &&
- (hw->snpsid & 0xfffff000) != 0x4f543000) {
- dev_err(hsotg->dev, "Bad value for GSNPSID: 0x%08x\n",
- hw->snpsid);
- return -ENODEV;
- }
-
- dev_dbg(hsotg->dev, "Core Release: %1x.%1x%1x%1x (snpsid=%x)\n",
- 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);
-
- dev_dbg(hsotg->dev, "hwcfg1=%08x\n", hwcfg1);
- dev_dbg(hsotg->dev, "hwcfg2=%08x\n", hwcfg2);
- dev_dbg(hsotg->dev, "hwcfg3=%08x\n", hwcfg3);
- dev_dbg(hsotg->dev, "hwcfg4=%08x\n", hwcfg4);
- dev_dbg(hsotg->dev, "grxfsiz=%08x\n", grxfsiz);
-
- /*
- * Host specific hardware parameters. Reading these parameters
- * requires the controller to be in host mode. The mode will
- * be forced, if necessary, to read these values.
- */
- dwc2_get_host_hwparams(hsotg);
- dwc2_get_dev_hwparams(hsotg);
-
- /* hwcfg1 */
- hw->dev_ep_dirs = hwcfg1;
-
- /* hwcfg2 */
- hw->op_mode = (hwcfg2 & GHWCFG2_OP_MODE_MASK) >>
- GHWCFG2_OP_MODE_SHIFT;
- hw->arch = (hwcfg2 & GHWCFG2_ARCHITECTURE_MASK) >>
- GHWCFG2_ARCHITECTURE_SHIFT;
- hw->enable_dynamic_fifo = !!(hwcfg2 & GHWCFG2_DYNAMIC_FIFO);
- hw->host_channels = 1 + ((hwcfg2 & GHWCFG2_NUM_HOST_CHAN_MASK) >>
- GHWCFG2_NUM_HOST_CHAN_SHIFT);
- hw->hs_phy_type = (hwcfg2 & GHWCFG2_HS_PHY_TYPE_MASK) >>
- GHWCFG2_HS_PHY_TYPE_SHIFT;
- hw->fs_phy_type = (hwcfg2 & GHWCFG2_FS_PHY_TYPE_MASK) >>
- GHWCFG2_FS_PHY_TYPE_SHIFT;
- hw->num_dev_ep = (hwcfg2 & GHWCFG2_NUM_DEV_EP_MASK) >>
- GHWCFG2_NUM_DEV_EP_SHIFT;
- hw->nperio_tx_q_depth =
- (hwcfg2 & GHWCFG2_NONPERIO_TX_Q_DEPTH_MASK) >>
- GHWCFG2_NONPERIO_TX_Q_DEPTH_SHIFT << 1;
- hw->host_perio_tx_q_depth =
- (hwcfg2 & GHWCFG2_HOST_PERIO_TX_Q_DEPTH_MASK) >>
- GHWCFG2_HOST_PERIO_TX_Q_DEPTH_SHIFT << 1;
- hw->dev_token_q_depth =
- (hwcfg2 & GHWCFG2_DEV_TOKEN_Q_DEPTH_MASK) >>
- GHWCFG2_DEV_TOKEN_Q_DEPTH_SHIFT;
-
- /* hwcfg3 */
- width = (hwcfg3 & GHWCFG3_XFER_SIZE_CNTR_WIDTH_MASK) >>
- GHWCFG3_XFER_SIZE_CNTR_WIDTH_SHIFT;
- hw->max_transfer_size = (1 << (width + 11)) - 1;
- width = (hwcfg3 & GHWCFG3_PACKET_SIZE_CNTR_WIDTH_MASK) >>
- GHWCFG3_PACKET_SIZE_CNTR_WIDTH_SHIFT;
- hw->max_packet_count = (1 << (width + 4)) - 1;
- hw->i2c_enable = !!(hwcfg3 & GHWCFG3_I2C);
- hw->total_fifo_size = (hwcfg3 & GHWCFG3_DFIFO_DEPTH_MASK) >>
- GHWCFG3_DFIFO_DEPTH_SHIFT;
-
- /* hwcfg4 */
- hw->en_multiple_tx_fifo = !!(hwcfg4 & GHWCFG4_DED_FIFO_EN);
- hw->num_dev_perio_in_ep = (hwcfg4 & GHWCFG4_NUM_DEV_PERIO_IN_EP_MASK) >>
- GHWCFG4_NUM_DEV_PERIO_IN_EP_SHIFT;
- hw->dma_desc_enable = !!(hwcfg4 & GHWCFG4_DESC_DMA);
- hw->power_optimized = !!(hwcfg4 & GHWCFG4_POWER_OPTIMIZ);
- hw->utmi_phy_data_width = (hwcfg4 & GHWCFG4_UTMI_PHY_DATA_WIDTH_MASK) >>
- GHWCFG4_UTMI_PHY_DATA_WIDTH_SHIFT;
-
- /* fifo sizes */
- hw->host_rx_fifo_size = (grxfsiz & GRXFSIZ_DEPTH_MASK) >>
- GRXFSIZ_DEPTH_SHIFT;
-
- dev_dbg(hsotg->dev, "Detected values from hardware:\n");
- dev_dbg(hsotg->dev, " op_mode=%d\n",
- hw->op_mode);
- dev_dbg(hsotg->dev, " arch=%d\n",
- hw->arch);
- dev_dbg(hsotg->dev, " dma_desc_enable=%d\n",
- hw->dma_desc_enable);
- dev_dbg(hsotg->dev, " power_optimized=%d\n",
- hw->power_optimized);
- dev_dbg(hsotg->dev, " i2c_enable=%d\n",
- hw->i2c_enable);
- dev_dbg(hsotg->dev, " hs_phy_type=%d\n",
- hw->hs_phy_type);
- dev_dbg(hsotg->dev, " fs_phy_type=%d\n",
- hw->fs_phy_type);
- dev_dbg(hsotg->dev, " utmi_phy_data_width=%d\n",
- hw->utmi_phy_data_width);
- dev_dbg(hsotg->dev, " num_dev_ep=%d\n",
- hw->num_dev_ep);
- dev_dbg(hsotg->dev, " num_dev_perio_in_ep=%d\n",
- hw->num_dev_perio_in_ep);
- dev_dbg(hsotg->dev, " host_channels=%d\n",
- hw->host_channels);
- dev_dbg(hsotg->dev, " max_transfer_size=%d\n",
- hw->max_transfer_size);
- dev_dbg(hsotg->dev, " max_packet_count=%d\n",
- hw->max_packet_count);
- dev_dbg(hsotg->dev, " nperio_tx_q_depth=0x%0x\n",
- hw->nperio_tx_q_depth);
- dev_dbg(hsotg->dev, " host_perio_tx_q_depth=0x%0x\n",
- hw->host_perio_tx_q_depth);
- dev_dbg(hsotg->dev, " dev_token_q_depth=0x%0x\n",
- hw->dev_token_q_depth);
- dev_dbg(hsotg->dev, " enable_dynamic_fifo=%d\n",
- hw->enable_dynamic_fifo);
- dev_dbg(hsotg->dev, " en_multiple_tx_fifo=%d\n",
- hw->en_multiple_tx_fifo);
- dev_dbg(hsotg->dev, " total_fifo_size=%d\n",
- hw->total_fifo_size);
- dev_dbg(hsotg->dev, " host_rx_fifo_size=%d\n",
- hw->host_rx_fifo_size);
- dev_dbg(hsotg->dev, " host_nperio_tx_fifo_size=%d\n",
- hw->host_nperio_tx_fifo_size);
- dev_dbg(hsotg->dev, " host_perio_tx_fifo_size=%d\n",
- hw->host_perio_tx_fifo_size);
- dev_dbg(hsotg->dev, "\n");
-
- return 0;
-}
-
-/*
- * Sets all parameters to the given value.
- *
- * Assumes that the dwc2_core_params struct contains only integers.
- */
-void dwc2_set_all_params(struct dwc2_core_params *params, int value)
-{
- int *p = (int *)params;
- size_t size = sizeof(*params) / sizeof(*p);
- int i;
-
- for (i = 0; i < size; i++)
- p[i] = value;
-}
-
-
-u16 dwc2_get_otg_version(struct dwc2_hsotg *hsotg)
-{
- return hsotg->core_params->otg_ver == 1 ? 0x0200 : 0x0103;
-}
-
bool dwc2_is_controller_alive(struct dwc2_hsotg *hsotg)
{
if (dwc2_readl(hsotg->regs + GSNPSID) == 0xffffffff)
@@ -1707,7 +788,7 @@ void dwc2_disable_global_interrupts(struct dwc2_hsotg *hsotg)
}
/* Returns the controller's GHWCFG2.OTG_MODE. */
-unsigned dwc2_op_mode(struct dwc2_hsotg *hsotg)
+unsigned int dwc2_op_mode(struct dwc2_hsotg *hsotg)
{
u32 ghwcfg2 = dwc2_readl(hsotg->regs + GHWCFG2);
@@ -1718,7 +799,7 @@ unsigned dwc2_op_mode(struct dwc2_hsotg *hsotg)
/* Returns true if the controller is capable of DRD. */
bool dwc2_hw_is_otg(struct dwc2_hsotg *hsotg)
{
- unsigned op_mode = dwc2_op_mode(hsotg);
+ unsigned int op_mode = dwc2_op_mode(hsotg);
return (op_mode == GHWCFG2_OP_MODE_HNP_SRP_CAPABLE) ||
(op_mode == GHWCFG2_OP_MODE_SRP_ONLY_CAPABLE) ||
@@ -1728,7 +809,7 @@ bool dwc2_hw_is_otg(struct dwc2_hsotg *hsotg)
/* Returns true if the controller is host-only. */
bool dwc2_hw_is_host(struct dwc2_hsotg *hsotg)
{
- unsigned op_mode = dwc2_op_mode(hsotg);
+ unsigned int op_mode = dwc2_op_mode(hsotg);
return (op_mode == GHWCFG2_OP_MODE_SRP_CAPABLE_HOST) ||
(op_mode == GHWCFG2_OP_MODE_NO_SRP_CAPABLE_HOST);
@@ -1737,7 +818,7 @@ bool dwc2_hw_is_host(struct dwc2_hsotg *hsotg)
/* Returns true if the controller is device-only. */
bool dwc2_hw_is_device(struct dwc2_hsotg *hsotg)
{
- unsigned op_mode = dwc2_op_mode(hsotg);
+ unsigned int op_mode = dwc2_op_mode(hsotg);
return (op_mode == GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE) ||
(op_mode == GHWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE);
diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h
index 2a21a0414b1d..1a7e83005082 100644
--- a/drivers/usb/dwc2/core.h
+++ b/drivers/usb/dwc2/core.h
@@ -127,6 +127,8 @@ static const char * const dwc2_hsotg_supply_names[] = {
"vusb_a", /* analog USB supply, 1.1V */
};
+#define DWC2_NUM_SUPPLIES ARRAY_SIZE(dwc2_hsotg_supply_names)
+
/*
* EP0_MPS_LIMIT
*
@@ -172,6 +174,11 @@ struct dwc2_hsotg_req;
* @periodic: Set if this is a periodic ep, such as Interrupt
* @isochronous: Set if this is a isochronous ep
* @send_zlp: Set if we need to send a zero-length packet.
+ * @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.
* @total_data: The total number of data bytes done.
* @fifo_size: The size of the FIFO (for periodic IN endpoints)
* @fifo_load: The amount of data loaded into the FIFO (periodic IN)
@@ -219,6 +226,13 @@ struct dwc2_hsotg_ep {
#define TARGET_FRAME_INITIAL 0xFFFFFFFF
bool frame_overrun;
+ dma_addr_t desc_list_dma;
+ struct dwc2_dma_desc *desc_list;
+ u8 desc_count;
+
+ unsigned char isoc_chain_num;
+ unsigned int next_desc;
+
char name[10];
};
@@ -234,7 +248,8 @@ struct dwc2_hsotg_req {
void *saved_req_buf;
};
-#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
+#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || \
+ IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
#define call_gadget(_hs, _entry) \
do { \
if ((_hs)->gadget.speed != USB_SPEED_UNKNOWN && \
@@ -259,13 +274,6 @@ enum dwc2_lx_state {
DWC2_L3, /* Off state */
};
-/*
- * Gadget periodic tx fifo sizes as used by legacy driver
- * EP0 is not included
- */
-#define DWC2_G_P_LEGACY_TX_FIFO_SIZE {256, 256, 256, 256, 768, 768, 768, \
- 768, 0, 0, 0, 0, 0, 0, 0}
-
/* Gadget ep0 states */
enum dwc2_ep0_state {
DWC2_EP0_SETUP,
@@ -283,10 +291,7 @@ enum dwc2_ep0_state {
* 1 - SRP Only capable
* 2 - No HNP/SRP capable (always available)
* Defaults to best available option (0, 1, then 2)
- * @otg_ver: OTG version supported
- * 0 - 1.3 (default)
- * 1 - 2.0
- * @dma_enable: Specifies whether to use slave or DMA mode for accessing
+ * @host_dma: Specifies whether to use slave or DMA mode for accessing
* the data FIFOs. The driver will automatically detect the
* value for this parameter if none is specified.
* 0 - Slave (always available)
@@ -314,7 +319,8 @@ enum dwc2_ep0_state {
* @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
+ * are enabled for non-periodic IN endpoints in device
+ * mode.
* @host_rx_fifo_size: Number of 4-byte words in the Rx FIFO in host mode when
* dynamic FIFO sizing is enabled
* 16 to 32768
@@ -417,6 +423,25 @@ enum dwc2_ep0_state {
* needed.
* 0 - No (default)
* 1 - Yes
+ * @g_dma: Enables gadget dma usage (default: autodetect).
+ * @g_dma_desc: Enables gadget descriptor DMA (default: autodetect).
+ * @g_rx_fifo_size: The periodic rx fifo size for the device, in
+ * DWORDS from 16-32768 (default: 2048 if
+ * possible, otherwise autodetect).
+ * @g_np_tx_fifo_size: The non-periodic tx fifo size for the device in
+ * DWORDS from 16-32768 (default: 1024 if
+ * possible, otherwise autodetect).
+ * @g_tx_fifo_size: An array of TX fifo sizes in dedicated fifo
+ * mode. Each value corresponds to one EP
+ * starting from EP1 (max 15 values). Sizes are
+ * in DWORDS with possible values from from
+ * 16-32768 (default: 256, 256, 256, 256, 768,
+ * 768, 768, 768, 0, 0, 0, 0, 0, 0, 0).
+ * @change_speed_quirk: Change speed configuration to DWC2_SPEED_PARAM_FULL
+ * while full&low speed device connect. And change speed
+ * back to DWC2_SPEED_PARAM_HIGH while device is gone.
+ * 0 - No (default)
+ * 1 - Yes
*
* The following parameters may be specified when starting the module. These
* parameters define how the DWC_otg controller should be configured. A
@@ -425,38 +450,57 @@ enum dwc2_ep0_state {
* default described above.
*/
struct dwc2_core_params {
- /*
- * Don't add any non-int members here, this will break
- * dwc2_set_all_params!
- */
- int otg_cap;
- int otg_ver;
- int dma_enable;
- int dma_desc_enable;
- int dma_desc_fs_enable;
- int speed;
- int enable_dynamic_fifo;
- int en_multiple_tx_fifo;
- int host_rx_fifo_size;
- int host_nperio_tx_fifo_size;
- int host_perio_tx_fifo_size;
- int max_transfer_size;
- int max_packet_count;
- int host_channels;
- int phy_type;
- int phy_utmi_width;
- int phy_ulpi_ddr;
- int phy_ulpi_ext_vbus;
- int i2c_enable;
- int ulpi_fs_ls;
- int host_support_fs_ls_low_power;
- int host_ls_low_power_phy_clk;
- int ts_dline;
- int reload_ctl;
- int ahbcfg;
- int uframe_sched;
- int external_id_pin_ctl;
- int hibernation;
+ u8 otg_cap;
+#define DWC2_CAP_PARAM_HNP_SRP_CAPABLE 0
+#define DWC2_CAP_PARAM_SRP_ONLY_CAPABLE 1
+#define DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE 2
+
+ u8 phy_type;
+#define DWC2_PHY_TYPE_PARAM_FS 0
+#define DWC2_PHY_TYPE_PARAM_UTMI 1
+#define DWC2_PHY_TYPE_PARAM_ULPI 2
+
+ u8 speed;
+#define DWC2_SPEED_PARAM_HIGH 0
+#define DWC2_SPEED_PARAM_FULL 1
+#define DWC2_SPEED_PARAM_LOW 2
+
+ u8 phy_utmi_width;
+ bool phy_ulpi_ddr;
+ bool phy_ulpi_ext_vbus;
+ bool enable_dynamic_fifo;
+ bool en_multiple_tx_fifo;
+ bool i2c_enable;
+ bool ulpi_fs_ls;
+ bool ts_dline;
+ bool reload_ctl;
+ bool uframe_sched;
+ bool external_id_pin_ctl;
+ bool hibernation;
+ u16 max_packet_count;
+ u32 max_transfer_size;
+ u32 ahbcfg;
+
+ /* Host parameters */
+ bool host_dma;
+ bool dma_desc_enable;
+ bool dma_desc_fs_enable;
+ bool host_support_fs_ls_low_power;
+ bool host_ls_low_power_phy_clk;
+
+ u8 host_channels;
+ u16 host_rx_fifo_size;
+ u16 host_nperio_tx_fifo_size;
+ u16 host_perio_tx_fifo_size;
+
+ /* Gadget parameters */
+ bool g_dma;
+ bool g_dma_desc;
+ u32 g_rx_fifo_size;
+ u32 g_np_tx_fifo_size;
+ u32 g_tx_fifo_size[MAX_EPS_CHANNELS];
+
+ bool change_speed_quirk;
};
/**
@@ -516,10 +560,9 @@ struct dwc2_hw_params {
unsigned op_mode:3;
unsigned arch:2;
unsigned dma_desc_enable:1;
- unsigned dma_desc_fs_enable:1;
unsigned enable_dynamic_fifo:1;
unsigned en_multiple_tx_fifo:1;
- unsigned host_rx_fifo_size:16;
+ unsigned rx_fifo_size:16;
unsigned host_nperio_tx_fifo_size:16;
unsigned dev_nperio_tx_fifo_size:16;
unsigned host_perio_tx_fifo_size:16;
@@ -545,8 +588,8 @@ struct dwc2_hw_params {
#define DWC2_CTRL_BUFF_SIZE 8
/**
- * struct dwc2_gregs_backup - Holds global registers state before entering partial
- * power down
+ * struct dwc2_gregs_backup - Holds global registers state before
+ * entering partial power down
* @gotgctl: Backup of GOTGCTL register
* @gintmsk: Backup of GINTMSK register
* @gahbcfg: Backup of GAHBCFG register
@@ -576,8 +619,8 @@ struct dwc2_gregs_backup {
};
/**
- * struct dwc2_dregs_backup - Holds device registers state before entering partial
- * power down
+ * struct dwc2_dregs_backup - Holds device registers state before
+ * entering partial power down
* @dcfg: Backup of DCFG register
* @dctl: Backup of DCTL register
* @daintmsk: Backup of DAINTMSK register
@@ -606,8 +649,8 @@ struct dwc2_dregs_backup {
};
/**
- * struct dwc2_hregs_backup - Holds host registers state before entering partial
- * power down
+ * struct dwc2_hregs_backup - Holds host registers state before
+ * entering partial power down
* @hcfg: Backup of HCFG register
* @haintmsk: Backup of HAINTMSK register
* @hcintmsk: Backup of HCINTMSK register
@@ -724,9 +767,10 @@ struct dwc2_hregs_backup {
* @gadget_enabled Peripheral mode sub-driver initialization indicator.
* @ll_hw_enabled Status of low-level hardware resources.
* @phy: The otg phy transceiver structure for phy control.
- * @uphy: The otg phy transceiver structure for old USB phy control.
- * @plat: The platform specific configuration data. This can be removed once
- * all SoCs support usb transceiver.
+ * @uphy: The otg phy transceiver structure for old USB phy
+ * control.
+ * @plat: The platform specific configuration data. This can be
+ * removed once all SoCs support usb transceiver.
* @supplies: Definition of USB power supplies
* @phyif: PHY interface width
* @lock: Spinlock that protects all the driver data structures
@@ -839,11 +883,13 @@ struct dwc2_hregs_backup {
* @ctrl_req: Request for EP0 control packets.
* @ep0_state: EP0 control transfers state
* @test_mode: USB test mode requested by the host
+ * @setup_desc_dma: EP0 setup stage desc chain DMA address
+ * @setup_desc: EP0 setup stage desc chain pointer
+ * @ctrl_in_desc_dma: EP0 IN data phase desc chain DMA address
+ * @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
- * @g_using_dma: Indicate if dma usage is enabled
- * @g_rx_fifo_sz: Contains rx fifo size value
- * @g_np_g_tx_fifo_sz: Contains Non-Periodic tx fifo size value
- * @g_tx_fifo_sz: Contains tx fifo size value per endpoints
*/
struct dwc2_hsotg {
struct device *dev;
@@ -851,7 +897,7 @@ struct dwc2_hsotg {
/** Params detected from hardware */
struct dwc2_hw_params hw_params;
/** Params to actually use */
- struct dwc2_core_params *core_params;
+ struct dwc2_core_params params;
enum usb_otg_state op_state;
enum usb_dr_mode dr_mode;
unsigned int hcd_enabled:1;
@@ -861,7 +907,7 @@ struct dwc2_hsotg {
struct phy *phy;
struct usb_phy *uphy;
struct dwc2_hsotg_plat *plat;
- struct regulator_bulk_data supplies[ARRAY_SIZE(dwc2_hsotg_supply_names)];
+ struct regulator_bulk_data supplies[DWC2_NUM_SUPPLIES];
u32 phyif;
spinlock_t lock;
@@ -887,10 +933,13 @@ struct dwc2_hsotg {
/* DWC OTG HW Release versions */
#define DWC2_CORE_REV_2_71a 0x4f54271a
#define DWC2_CORE_REV_2_90a 0x4f54290a
+#define DWC2_CORE_REV_2_91a 0x4f54291a
#define DWC2_CORE_REV_2_92a 0x4f54292a
#define DWC2_CORE_REV_2_94a 0x4f54294a
#define DWC2_CORE_REV_3_00a 0x4f54300a
#define DWC2_CORE_REV_3_10a 0x4f54310a
+#define DWC2_FS_IOT_REV_1_00a 0x5531100a
+#define DWC2_HS_IOT_REV_1_00a 0x5532100a
#if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
union dwc2_hcd_internal_flags {
@@ -971,7 +1020,8 @@ struct dwc2_hsotg {
#endif
#endif /* CONFIG_USB_DWC2_HOST || CONFIG_USB_DWC2_DUAL_ROLE */
-#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
+#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || \
+ IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
/* Gadget structures */
struct usb_gadget_driver *driver;
int fifo_mem;
@@ -986,15 +1036,18 @@ struct dwc2_hsotg {
enum dwc2_ep0_state ep0_state;
u8 test_mode;
+ dma_addr_t setup_desc_dma[2];
+ struct dwc2_dma_desc *setup_desc[2];
+ dma_addr_t ctrl_in_desc_dma;
+ struct dwc2_dma_desc *ctrl_in_desc;
+ dma_addr_t ctrl_out_desc_dma;
+ struct dwc2_dma_desc *ctrl_out_desc;
+
struct usb_gadget gadget;
unsigned int enabled:1;
unsigned int connected:1;
struct dwc2_hsotg_ep *eps_in[MAX_EPS_CHANNELS];
struct dwc2_hsotg_ep *eps_out[MAX_EPS_CHANNELS];
- u32 g_using_dma;
- u32 g_rx_fifo_sz;
- u32 g_np_g_tx_fifo_sz;
- u32 g_tx_fifo_sz[MAX_EPS_CHANNELS];
#endif /* CONFIG_USB_DWC2_PERIPHERAL || CONFIG_USB_DWC2_DUAL_ROLE */
};
@@ -1016,244 +1069,61 @@ enum dwc2_halt_status {
DWC2_HC_XFER_URB_DEQUEUE,
};
+/* Core version information */
+static inline bool dwc2_is_iot(struct dwc2_hsotg *hsotg)
+{
+ return (hsotg->hw_params.snpsid & 0xfff00000) == 0x55300000;
+}
+
+static inline bool dwc2_is_fs_iot(struct dwc2_hsotg *hsotg)
+{
+ return (hsotg->hw_params.snpsid & 0xffff0000) == 0x55310000;
+}
+
+static inline bool dwc2_is_hs_iot(struct dwc2_hsotg *hsotg)
+{
+ return (hsotg->hw_params.snpsid & 0xffff0000) == 0x55320000;
+}
+
/*
* The following functions support initialization of the core driver component
* and the DWC_otg controller
*/
-extern int dwc2_core_reset(struct dwc2_hsotg *hsotg);
-extern int dwc2_core_reset_and_force_dr_mode(struct dwc2_hsotg *hsotg);
-extern int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg);
-extern int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, bool restore);
+int dwc2_core_reset(struct dwc2_hsotg *hsotg, bool skip_wait);
+int dwc2_core_reset_and_force_dr_mode(struct dwc2_hsotg *hsotg);
+int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg);
+int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, bool restore);
+bool dwc2_force_mode_if_needed(struct dwc2_hsotg *hsotg, bool host);
+void dwc2_clear_force_mode(struct dwc2_hsotg *hsotg);
void dwc2_force_dr_mode(struct dwc2_hsotg *hsotg);
-extern bool dwc2_is_controller_alive(struct dwc2_hsotg *hsotg);
+bool dwc2_is_controller_alive(struct dwc2_hsotg *hsotg);
/*
* Common core Functions.
* The following functions support managing the DWC_otg controller in either
* device or host mode.
*/
-extern void dwc2_read_packet(struct dwc2_hsotg *hsotg, u8 *dest, u16 bytes);
-extern void dwc2_flush_tx_fifo(struct dwc2_hsotg *hsotg, const int num);
-extern void dwc2_flush_rx_fifo(struct dwc2_hsotg *hsotg);
+void dwc2_read_packet(struct dwc2_hsotg *hsotg, u8 *dest, u16 bytes);
+void dwc2_flush_tx_fifo(struct dwc2_hsotg *hsotg, const int num);
+void dwc2_flush_rx_fifo(struct dwc2_hsotg *hsotg);
-extern void dwc2_enable_global_interrupts(struct dwc2_hsotg *hcd);
-extern void dwc2_disable_global_interrupts(struct dwc2_hsotg *hcd);
+void dwc2_enable_global_interrupts(struct dwc2_hsotg *hcd);
+void dwc2_disable_global_interrupts(struct dwc2_hsotg *hcd);
/* This function should be called on every hardware interrupt. */
-extern irqreturn_t dwc2_handle_common_intr(int irq, void *dev);
+irqreturn_t dwc2_handle_common_intr(int irq, void *dev);
-/* OTG Core Parameters */
+/* The device ID match table */
+extern const struct of_device_id dwc2_of_match_table[];
-/*
- * Specifies the OTG capabilities. The driver will automatically
- * detect the value for this parameter if none is specified.
- * 0 - HNP and SRP capable (default)
- * 1 - SRP Only capable
- * 2 - No HNP/SRP capable
- */
-extern void dwc2_set_param_otg_cap(struct dwc2_hsotg *hsotg, int val);
-#define DWC2_CAP_PARAM_HNP_SRP_CAPABLE 0
-#define DWC2_CAP_PARAM_SRP_ONLY_CAPABLE 1
-#define DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE 2
-
-/*
- * Specifies whether to use slave or DMA mode for accessing the data
- * FIFOs. The driver will automatically detect the value for this
- * parameter if none is specified.
- * 0 - Slave
- * 1 - DMA (default, if available)
- */
-extern void dwc2_set_param_dma_enable(struct dwc2_hsotg *hsotg, int val);
+int dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg);
+int dwc2_lowlevel_hw_disable(struct dwc2_hsotg *hsotg);
-/*
- * When DMA mode is enabled specifies whether to use
- * address DMA or DMA Descritor mode for accessing the data
- * FIFOs in device mode. The driver will automatically detect
- * the value for this parameter if none is specified.
- * 0 - address DMA
- * 1 - DMA Descriptor(default, if available)
- */
-extern void dwc2_set_param_dma_desc_enable(struct dwc2_hsotg *hsotg, int val);
-
-/*
- * When DMA mode is enabled specifies whether to use
- * address DMA or DMA Descritor mode with full speed devices
- * for accessing the data FIFOs in host mode.
- * 0 - address DMA
- * 1 - FS DMA Descriptor(default, if available)
- */
-extern void dwc2_set_param_dma_desc_fs_enable(struct dwc2_hsotg *hsotg,
- int val);
-
-/*
- * Specifies the maximum speed of operation in host and device mode.
- * The actual speed depends on the speed of the attached device and
- * the value of phy_type. The actual speed depends on the speed of the
- * attached device.
- * 0 - High Speed (default)
- * 1 - Full Speed
- */
-extern void dwc2_set_param_speed(struct dwc2_hsotg *hsotg, int val);
-#define DWC2_SPEED_PARAM_HIGH 0
-#define DWC2_SPEED_PARAM_FULL 1
-
-/*
- * Specifies whether low power mode is supported when attached
- * to a Full Speed or Low Speed device in host mode.
- *
- * 0 - Don't support low power mode (default)
- * 1 - Support low power mode
- */
-extern void dwc2_set_param_host_support_fs_ls_low_power(
- struct dwc2_hsotg *hsotg, int val);
-
-/*
- * Specifies the PHY clock rate in low power mode when connected to a
- * Low Speed device in host mode. This parameter is applicable only if
- * HOST_SUPPORT_FS_LS_LOW_POWER is enabled. If PHY_TYPE is set to FS
- * then defaults to 6 MHZ otherwise 48 MHZ.
- *
- * 0 - 48 MHz
- * 1 - 6 MHz
- */
-extern void dwc2_set_param_host_ls_low_power_phy_clk(struct dwc2_hsotg *hsotg,
- int val);
-#define DWC2_HOST_LS_LOW_POWER_PHY_CLK_PARAM_48MHZ 0
-#define DWC2_HOST_LS_LOW_POWER_PHY_CLK_PARAM_6MHZ 1
-
-/*
- * 0 - Use cC FIFO size parameters
- * 1 - Allow dynamic FIFO sizing (default)
- */
-extern void dwc2_set_param_enable_dynamic_fifo(struct dwc2_hsotg *hsotg,
- int val);
-
-/*
- * Number of 4-byte words in the Rx FIFO in host mode when dynamic
- * FIFO sizing is enabled.
- * 16 to 32768 (default 1024)
- */
-extern void dwc2_set_param_host_rx_fifo_size(struct dwc2_hsotg *hsotg, int val);
-
-/*
- * Number of 4-byte words in the non-periodic Tx FIFO in host mode
- * when Dynamic FIFO sizing is enabled in the core.
- * 16 to 32768 (default 256)
- */
-extern void dwc2_set_param_host_nperio_tx_fifo_size(struct dwc2_hsotg *hsotg,
- int val);
-
-/*
- * Number of 4-byte words in the host periodic Tx FIFO when dynamic
- * FIFO sizing is enabled.
- * 16 to 32768 (default 256)
- */
-extern void dwc2_set_param_host_perio_tx_fifo_size(struct dwc2_hsotg *hsotg,
- int val);
-
-/*
- * The maximum transfer size supported in bytes.
- * 2047 to 65,535 (default 65,535)
- */
-extern void dwc2_set_param_max_transfer_size(struct dwc2_hsotg *hsotg, int val);
-
-/*
- * The maximum number of packets in a transfer.
- * 15 to 511 (default 511)
- */
-extern void dwc2_set_param_max_packet_count(struct dwc2_hsotg *hsotg, int val);
-
-/*
- * The number of host channel registers to use.
- * 1 to 16 (default 11)
- * Note: The FPGA configuration supports a maximum of 11 host channels.
- */
-extern void dwc2_set_param_host_channels(struct dwc2_hsotg *hsotg, int val);
-
-/*
- * Specifies the type of PHY interface to use. By default, the driver
- * will automatically detect the phy_type.
- *
- * 0 - Full Speed PHY
- * 1 - UTMI+ (default)
- * 2 - ULPI
- */
-extern void dwc2_set_param_phy_type(struct dwc2_hsotg *hsotg, int val);
-#define DWC2_PHY_TYPE_PARAM_FS 0
-#define DWC2_PHY_TYPE_PARAM_UTMI 1
-#define DWC2_PHY_TYPE_PARAM_ULPI 2
-
-/*
- * Specifies the UTMI+ Data Width. This parameter is
- * applicable for a PHY_TYPE of UTMI+ or ULPI. (For a ULPI
- * PHY_TYPE, this parameter indicates the data width between
- * the MAC and the ULPI Wrapper.) Also, this parameter is
- * applicable only if the OTG_HSPHY_WIDTH cC parameter was set
- * to "8 and 16 bits", meaning that the core has been
- * configured to work at either data path width.
- *
- * 8 or 16 bits (default 16)
- */
-extern void dwc2_set_param_phy_utmi_width(struct dwc2_hsotg *hsotg, int val);
-
-/*
- * Specifies whether the ULPI operates at double or single
- * data rate. This parameter is only applicable if PHY_TYPE is
- * ULPI.
- *
- * 0 - single data rate ULPI interface with 8 bit wide data
- * bus (default)
- * 1 - double data rate ULPI interface with 4 bit wide data
- * bus
- */
-extern void dwc2_set_param_phy_ulpi_ddr(struct dwc2_hsotg *hsotg, int val);
-
-/*
- * Specifies whether to use the internal or external supply to
- * drive the vbus with a ULPI phy.
- */
-extern void dwc2_set_param_phy_ulpi_ext_vbus(struct dwc2_hsotg *hsotg, int val);
-#define DWC2_PHY_ULPI_INTERNAL_VBUS 0
-#define DWC2_PHY_ULPI_EXTERNAL_VBUS 1
-
-/*
- * Specifies whether to use the I2Cinterface for full speed PHY. This
- * parameter is only applicable if PHY_TYPE is FS.
- * 0 - No (default)
- * 1 - Yes
- */
-extern void dwc2_set_param_i2c_enable(struct dwc2_hsotg *hsotg, int val);
-
-extern void dwc2_set_param_ulpi_fs_ls(struct dwc2_hsotg *hsotg, int val);
-
-extern void dwc2_set_param_ts_dline(struct dwc2_hsotg *hsotg, int val);
-
-/*
- * Specifies whether dedicated transmit FIFOs are
- * enabled for non periodic IN endpoints in device mode
- * 0 - No
- * 1 - Yes
- */
-extern void dwc2_set_param_en_multiple_tx_fifo(struct dwc2_hsotg *hsotg,
- int val);
-
-extern void dwc2_set_param_reload_ctl(struct dwc2_hsotg *hsotg, int val);
-
-extern void dwc2_set_param_ahbcfg(struct dwc2_hsotg *hsotg, int val);
-
-extern void dwc2_set_param_otg_ver(struct dwc2_hsotg *hsotg, int val);
-
-extern void dwc2_set_parameters(struct dwc2_hsotg *hsotg,
- const struct dwc2_core_params *params);
-
-extern void dwc2_set_all_params(struct dwc2_core_params *params, int value);
-
-extern int dwc2_get_hwparams(struct dwc2_hsotg *hsotg);
-
-extern int dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg);
-extern int dwc2_lowlevel_hw_disable(struct dwc2_hsotg *hsotg);
+/* Parameters */
+int dwc2_get_hwparams(struct dwc2_hsotg *hsotg);
+int dwc2_init_params(struct dwc2_hsotg *hsotg);
/*
* The following functions check the controller's OTG operation mode
@@ -1263,7 +1133,7 @@ extern int dwc2_lowlevel_hw_disable(struct dwc2_hsotg *hsotg);
* are read in and cached so they always read directly from the
* GHWCFG2 register.
*/
-unsigned dwc2_op_mode(struct dwc2_hsotg *hsotg);
+unsigned int dwc2_op_mode(struct dwc2_hsotg *hsotg);
bool dwc2_hw_is_otg(struct dwc2_hsotg *hsotg);
bool dwc2_hw_is_host(struct dwc2_hsotg *hsotg);
bool dwc2_hw_is_device(struct dwc2_hsotg *hsotg);
@@ -1275,6 +1145,7 @@ static inline int dwc2_is_host_mode(struct dwc2_hsotg *hsotg)
{
return (dwc2_readl(hsotg->regs + 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;
@@ -1283,29 +1154,28 @@ static inline int dwc2_is_device_mode(struct dwc2_hsotg *hsotg)
/*
* Dump core registers and SPRAM
*/
-extern void dwc2_dump_dev_registers(struct dwc2_hsotg *hsotg);
-extern void dwc2_dump_host_registers(struct dwc2_hsotg *hsotg);
-extern void dwc2_dump_global_registers(struct dwc2_hsotg *hsotg);
-
-/*
- * Return OTG version - either 1.3 or 2.0
- */
-extern u16 dwc2_get_otg_version(struct dwc2_hsotg *hsotg);
+void dwc2_dump_dev_registers(struct dwc2_hsotg *hsotg);
+void dwc2_dump_host_registers(struct dwc2_hsotg *hsotg);
+void dwc2_dump_global_registers(struct dwc2_hsotg *hsotg);
/* Gadget defines */
-#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
-extern int dwc2_hsotg_remove(struct dwc2_hsotg *hsotg);
-extern int dwc2_hsotg_suspend(struct dwc2_hsotg *dwc2);
-extern int dwc2_hsotg_resume(struct dwc2_hsotg *dwc2);
-extern int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq);
-extern void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2,
- bool reset);
-extern void dwc2_hsotg_core_connect(struct dwc2_hsotg *hsotg);
-extern void dwc2_hsotg_disconnect(struct dwc2_hsotg *dwc2);
-extern int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode);
+#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || \
+ IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
+int dwc2_hsotg_remove(struct dwc2_hsotg *hsotg);
+int dwc2_hsotg_suspend(struct dwc2_hsotg *dwc2);
+int dwc2_hsotg_resume(struct dwc2_hsotg *dwc2);
+int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq);
+void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2,
+ bool reset);
+void dwc2_hsotg_core_connect(struct dwc2_hsotg *hsotg);
+void dwc2_hsotg_disconnect(struct dwc2_hsotg *dwc2);
+int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode);
#define dwc2_is_device_connected(hsotg) (hsotg->connected)
int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg);
int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg);
+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);
#else
static inline int dwc2_hsotg_remove(struct dwc2_hsotg *dwc2)
{ return 0; }
@@ -1316,25 +1186,31 @@ static inline int dwc2_hsotg_resume(struct dwc2_hsotg *dwc2)
static inline int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
{ return 0; }
static inline void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2,
- bool reset) {}
+ bool reset) {}
static inline void dwc2_hsotg_core_connect(struct dwc2_hsotg *hsotg) {}
static inline void dwc2_hsotg_disconnect(struct dwc2_hsotg *dwc2) {}
static inline int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg,
- int testmode)
+ int testmode)
{ return 0; }
#define dwc2_is_device_connected(hsotg) (0)
static inline int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg)
{ return 0; }
static inline int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg)
{ return 0; }
+static inline int dwc2_hsotg_tx_fifo_count(struct dwc2_hsotg *hsotg)
+{ return 0; }
+static inline int dwc2_hsotg_tx_fifo_total_depth(struct dwc2_hsotg *hsotg)
+{ return 0; }
+static inline int dwc2_hsotg_tx_fifo_average_depth(struct dwc2_hsotg *hsotg)
+{ return 0; }
#endif
#if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
-extern int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg);
-extern int dwc2_hcd_get_future_frame_number(struct dwc2_hsotg *hsotg, int us);
-extern void dwc2_hcd_connect(struct dwc2_hsotg *hsotg);
-extern void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force);
-extern void dwc2_hcd_start(struct dwc2_hsotg *hsotg);
+int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg);
+int dwc2_hcd_get_future_frame_number(struct dwc2_hsotg *hsotg, int us);
+void dwc2_hcd_connect(struct dwc2_hsotg *hsotg);
+void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force);
+void dwc2_hcd_start(struct dwc2_hsotg *hsotg);
int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg);
int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg);
#else
@@ -1347,7 +1223,7 @@ static inline void dwc2_hcd_connect(struct dwc2_hsotg *hsotg) {}
static inline void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force) {}
static inline void dwc2_hcd_start(struct dwc2_hsotg *hsotg) {}
static inline void dwc2_hcd_remove(struct dwc2_hsotg *hsotg) {}
-static inline int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq)
+static inline int dwc2_hcd_init(struct dwc2_hsotg *hsotg)
{ return 0; }
static inline int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg)
{ return 0; }
diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c
index d85c5c9f96c1..b8bcb007c92a 100644
--- a/drivers/usb/dwc2/core_intr.c
+++ b/drivers/usb/dwc2/core_intr.c
@@ -159,9 +159,8 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
" ++OTG Interrupt: Session Request Success Status Change++\n");
gotgctl = dwc2_readl(hsotg->regs + GOTGCTL);
if (gotgctl & GOTGCTL_SESREQSCS) {
- if (hsotg->core_params->phy_type ==
- DWC2_PHY_TYPE_PARAM_FS
- && hsotg->core_params->i2c_enable > 0) {
+ if (hsotg->params.phy_type == DWC2_PHY_TYPE_PARAM_FS &&
+ hsotg->params.i2c_enable) {
hsotg->srp_success = 1;
} else {
/* Clear Session Request */
@@ -317,7 +316,7 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg)
dwc2_writel(GINTSTS_SESSREQINT, hsotg->regs + GINTSTS);
dev_dbg(hsotg->dev, "Session request interrupt - lx_state=%d\n",
- hsotg->lx_state);
+ hsotg->lx_state);
if (dwc2_is_device_mode(hsotg)) {
if (hsotg->lx_state == DWC2_L2) {
@@ -370,7 +369,7 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
/* Change to L0 state */
hsotg->lx_state = DWC2_L0;
} else {
- if (hsotg->core_params->hibernation)
+ if (hsotg->params.hibernation)
return;
if (hsotg->lx_state != DWC2_L1) {
@@ -437,7 +436,7 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg)
/* Ignore suspend request before enumeration */
if (!dwc2_is_device_connected(hsotg)) {
dev_dbg(hsotg->dev,
- "ignore suspend request before enumeration\n");
+ "ignore suspend request before enumeration\n");
return;
}
@@ -445,7 +444,7 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg)
if (ret) {
if (ret != -ENOTSUPP)
dev_err(hsotg->dev,
- "enter hibernation failed\n");
+ "enter hibernation failed\n");
goto skip_power_saving;
}
diff --git a/drivers/usb/dwc2/debug.h b/drivers/usb/dwc2/debug.h
index 12dbd1daec87..8222783e6822 100644
--- a/drivers/usb/dwc2/debug.h
+++ b/drivers/usb/dwc2/debug.h
@@ -17,8 +17,8 @@
#include "core.h"
#ifdef CONFIG_DEBUG_FS
-extern int dwc2_debugfs_init(struct dwc2_hsotg *);
-extern void dwc2_debugfs_exit(struct dwc2_hsotg *);
+int dwc2_debugfs_init(struct dwc2_hsotg *hsotg);
+void dwc2_debugfs_exit(struct dwc2_hsotg *hsotg);
#else
static inline int dwc2_debugfs_init(struct dwc2_hsotg *hsotg)
{ return 0; }
diff --git a/drivers/usb/dwc2/debugfs.c b/drivers/usb/dwc2/debugfs.c
index 55d91f24f94a..794b959a7c8c 100644
--- a/drivers/usb/dwc2/debugfs.c
+++ b/drivers/usb/dwc2/debugfs.c
@@ -137,7 +137,7 @@ static int state_show(struct seq_file *seq, void *v)
int idx;
seq_printf(seq, "DCFG=0x%08x, DCTL=0x%08x, DSTS=0x%08x\n",
- dwc2_readl(regs + DCFG),
+ dwc2_readl(regs + DCFG),
dwc2_readl(regs + DCTL),
dwc2_readl(regs + DSTS));
@@ -213,7 +213,7 @@ static int fifo_show(struct seq_file *seq, void *v)
val = dwc2_readl(regs + GNPTXFSIZ);
seq_printf(seq, "NPTXFIFO: Size %d, Start 0x%08x\n",
val >> FIFOSIZE_DEPTH_SHIFT,
- val & FIFOSIZE_DEPTH_MASK);
+ val & FIFOSIZE_STARTADDR_MASK);
seq_puts(seq, "\nPeriodic TXFIFOs:\n");
@@ -338,23 +338,23 @@ static void dwc2_hsotg_create_debug(struct dwc2_hsotg *hsotg)
{
struct dentry *root;
struct dentry *file;
- unsigned epidx;
+ unsigned int epidx;
root = hsotg->debug_root;
/* create general state file */
- file = debugfs_create_file("state", S_IRUGO, root, hsotg, &state_fops);
+ 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", S_IRUGO | S_IWUSR, root, hsotg,
- &testmode_fops);
+ 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__);
+ __func__);
- file = debugfs_create_file("fifo", S_IRUGO, root, hsotg, &fifo_fops);
+ 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__);
@@ -364,8 +364,8 @@ static void dwc2_hsotg_create_debug(struct dwc2_hsotg *hsotg)
ep = hsotg->eps_out[epidx];
if (ep) {
- file = debugfs_create_file(ep->name, S_IRUGO,
- root, ep, &ep_fops);
+ 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);
@@ -377,8 +377,8 @@ static void dwc2_hsotg_create_debug(struct dwc2_hsotg *hsotg)
ep = hsotg->eps_in[epidx];
if (ep) {
- file = debugfs_create_file(ep->name, S_IRUGO,
- root, ep, &ep_fops);
+ 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);
@@ -725,6 +725,143 @@ static const struct debugfs_reg32 dwc2_regs[] = {
dump_register(HCDMAB(15)),
};
+#define print_param(_seq, _ptr, _param) \
+seq_printf((_seq), "%-30s: %d\n", #_param, (_ptr)->_param)
+
+#define print_param_hex(_seq, _ptr, _param) \
+seq_printf((_seq), "%-30s: 0x%x\n", #_param, (_ptr)->_param)
+
+static int params_show(struct seq_file *seq, void *v)
+{
+ struct dwc2_hsotg *hsotg = seq->private;
+ struct dwc2_core_params *p = &hsotg->params;
+ int i;
+
+ print_param(seq, p, otg_cap);
+ print_param(seq, p, dma_desc_enable);
+ print_param(seq, p, dma_desc_fs_enable);
+ print_param(seq, p, speed);
+ print_param(seq, p, enable_dynamic_fifo);
+ print_param(seq, p, en_multiple_tx_fifo);
+ print_param(seq, p, host_rx_fifo_size);
+ print_param(seq, p, host_nperio_tx_fifo_size);
+ print_param(seq, p, host_perio_tx_fifo_size);
+ print_param(seq, p, max_transfer_size);
+ print_param(seq, p, max_packet_count);
+ print_param(seq, p, host_channels);
+ print_param(seq, p, phy_type);
+ print_param(seq, p, phy_utmi_width);
+ 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, ulpi_fs_ls);
+ print_param(seq, p, host_support_fs_ls_low_power);
+ print_param(seq, p, host_ls_low_power_phy_clk);
+ print_param(seq, p, ts_dline);
+ print_param(seq, p, reload_ctl);
+ print_param_hex(seq, p, ahbcfg);
+ print_param(seq, p, uframe_sched);
+ print_param(seq, p, external_id_pin_ctl);
+ print_param(seq, p, hibernation);
+ print_param(seq, p, host_dma);
+ print_param(seq, p, g_dma);
+ print_param(seq, p, g_dma_desc);
+ print_param(seq, p, g_rx_fifo_size);
+ print_param(seq, p, g_np_tx_fifo_size);
+
+ for (i = 0; i < MAX_EPS_CHANNELS; i++) {
+ char str[32];
+
+ snprintf(str, 32, "g_tx_fifo_size[%d]", i);
+ seq_printf(seq, "%-30s: %d\n", str, p->g_tx_fifo_size[i]);
+ }
+
+ return 0;
+}
+
+static int params_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, params_show, inode->i_private);
+}
+
+static const struct file_operations params_fops = {
+ .owner = THIS_MODULE,
+ .open = params_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int hw_params_show(struct seq_file *seq, void *v)
+{
+ struct dwc2_hsotg *hsotg = seq->private;
+ struct dwc2_hw_params *hw = &hsotg->hw_params;
+
+ print_param(seq, hw, op_mode);
+ print_param(seq, hw, arch);
+ print_param(seq, hw, dma_desc_enable);
+ print_param(seq, hw, enable_dynamic_fifo);
+ print_param(seq, hw, en_multiple_tx_fifo);
+ print_param(seq, hw, rx_fifo_size);
+ print_param(seq, hw, host_nperio_tx_fifo_size);
+ print_param(seq, hw, dev_nperio_tx_fifo_size);
+ print_param(seq, hw, host_perio_tx_fifo_size);
+ print_param(seq, hw, nperio_tx_q_depth);
+ print_param(seq, hw, host_perio_tx_q_depth);
+ print_param(seq, hw, dev_token_q_depth);
+ print_param(seq, hw, max_transfer_size);
+ print_param(seq, hw, max_packet_count);
+ print_param(seq, hw, host_channels);
+ print_param(seq, hw, hs_phy_type);
+ print_param(seq, hw, fs_phy_type);
+ print_param(seq, hw, i2c_enable);
+ print_param(seq, hw, num_dev_ep);
+ print_param(seq, hw, num_dev_perio_in_ep);
+ print_param(seq, hw, total_fifo_size);
+ print_param(seq, hw, power_optimized);
+ print_param(seq, hw, utmi_phy_data_width);
+ print_param_hex(seq, hw, snpsid);
+ print_param_hex(seq, hw, dev_ep_dirs);
+
+ return 0;
+}
+
+static int hw_params_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, hw_params_show, inode->i_private);
+}
+
+static const struct file_operations hw_params_fops = {
+ .owner = THIS_MODULE,
+ .open = hw_params_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int dr_mode_show(struct seq_file *seq, void *v)
+{
+ struct dwc2_hsotg *hsotg = seq->private;
+ const char *dr_mode = "";
+
+ device_property_read_string(hsotg->dev, "dr_mode", &dr_mode);
+ seq_printf(seq, "%s\n", dr_mode);
+ return 0;
+}
+
+static int dr_mode_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, dr_mode_show, inode->i_private);
+}
+
+static const struct file_operations dr_mode_fops = {
+ .owner = THIS_MODULE,
+ .open = dr_mode_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
int dwc2_debugfs_init(struct dwc2_hsotg *hsotg)
{
int ret;
@@ -736,6 +873,25 @@ int dwc2_debugfs_init(struct dwc2_hsotg *hsotg)
goto err0;
}
+ 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__);
+
/* Add gadget debugfs nodes */
dwc2_hsotg_create_debug(hsotg);
@@ -750,8 +906,8 @@ int dwc2_debugfs_init(struct dwc2_hsotg *hsotg)
hsotg->regset->nregs = ARRAY_SIZE(dwc2_regs);
hsotg->regset->base = hsotg->regs;
- file = debugfs_create_regset32("regdump", S_IRUGO, hsotg->debug_root,
- hsotg->regset);
+ file = debugfs_create_regset32("regdump", 0444, hsotg->debug_root,
+ hsotg->regset);
if (!file) {
ret = -ENOMEM;
goto err1;
diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c
index 24fbebc9b409..bc3b3fda5000 100644
--- a/drivers/usb/dwc2/gadget.c
+++ b/drivers/usb/dwc2/gadget.c
@@ -93,7 +93,18 @@ static void dwc2_hsotg_dump(struct dwc2_hsotg *hsotg);
*/
static inline bool using_dma(struct dwc2_hsotg *hsotg)
{
- return hsotg->g_using_dma;
+ return hsotg->params.g_dma;
+}
+
+/*
+ * using_desc_dma - return the descriptor DMA status of the driver.
+ * @hsotg: The driver state.
+ *
+ * Return true if we're using descriptor DMA.
+ */
+static inline bool using_desc_dma(struct dwc2_hsotg *hsotg)
+{
+ return hsotg->params.g_dma_desc;
}
/**
@@ -160,7 +171,7 @@ static void dwc2_hsotg_disable_gsint(struct dwc2_hsotg *hsotg, u32 ints)
* request.
*/
static void dwc2_hsotg_ctrl_epint(struct dwc2_hsotg *hsotg,
- unsigned int ep, unsigned int dir_in,
+ unsigned int ep, unsigned int dir_in,
unsigned int en)
{
unsigned long flags;
@@ -181,6 +192,99 @@ static void dwc2_hsotg_ctrl_epint(struct dwc2_hsotg *hsotg,
}
/**
+ * dwc2_hsotg_tx_fifo_count - return count of TX FIFOs in device mode
+ */
+int dwc2_hsotg_tx_fifo_count(struct dwc2_hsotg *hsotg)
+{
+ if (hsotg->hw_params.en_multiple_tx_fifo)
+ /* In dedicated FIFO mode we need count of IN EPs */
+ return (dwc2_readl(hsotg->regs + GHWCFG4) &
+ GHWCFG4_NUM_IN_EPS_MASK) >> GHWCFG4_NUM_IN_EPS_SHIFT;
+ else
+ /* In shared FIFO mode we need count of Periodic IN EPs */
+ return hsotg->hw_params.num_dev_perio_in_ep;
+}
+
+/**
+ * dwc2_hsotg_ep_info_size - return Endpoint Info Control block size in DWORDs
+ */
+static int dwc2_hsotg_ep_info_size(struct dwc2_hsotg *hsotg)
+{
+ int val = 0;
+ int i;
+ u32 ep_dirs;
+
+ /*
+ * Don't need additional space for ep info control registers in
+ * slave mode.
+ */
+ if (!using_dma(hsotg)) {
+ dev_dbg(hsotg->dev, "Buffer DMA ep info size 0\n");
+ return 0;
+ }
+
+ /*
+ * Buffer DMA mode - 1 location per endpoit
+ * Descriptor DMA mode - 4 locations per endpoint
+ */
+ ep_dirs = hsotg->hw_params.dev_ep_dirs;
+
+ for (i = 0; i <= hsotg->hw_params.num_dev_ep; i++) {
+ val += ep_dirs & 3 ? 1 : 2;
+ ep_dirs >>= 2;
+ }
+
+ if (using_desc_dma(hsotg))
+ val = val * 4;
+
+ return val;
+}
+
+/**
+ * dwc2_hsotg_tx_fifo_total_depth - return total FIFO depth available for
+ * device mode TX FIFOs
+ */
+int dwc2_hsotg_tx_fifo_total_depth(struct dwc2_hsotg *hsotg)
+{
+ int ep_info_size;
+ int addr;
+ int tx_addr_max;
+ u32 np_tx_fifo_size;
+
+ np_tx_fifo_size = min_t(u32, hsotg->hw_params.dev_nperio_tx_fifo_size,
+ hsotg->params.g_np_tx_fifo_size);
+
+ /* Get Endpoint Info Control block size in DWORDs. */
+ ep_info_size = dwc2_hsotg_ep_info_size(hsotg);
+ tx_addr_max = hsotg->hw_params.total_fifo_size - ep_info_size;
+
+ addr = hsotg->params.g_rx_fifo_size + np_tx_fifo_size;
+ if (tx_addr_max <= addr)
+ return 0;
+
+ return tx_addr_max - addr;
+}
+
+/**
+ * dwc2_hsotg_tx_fifo_average_depth - returns average depth of device mode
+ * TX FIFOs
+ */
+int dwc2_hsotg_tx_fifo_average_depth(struct dwc2_hsotg *hsotg)
+{
+ int tx_fifo_count;
+ int tx_fifo_depth;
+
+ tx_fifo_depth = dwc2_hsotg_tx_fifo_total_depth(hsotg);
+
+ tx_fifo_count = dwc2_hsotg_tx_fifo_count(hsotg);
+
+ if (!tx_fifo_count)
+ return tx_fifo_depth;
+ else
+ return tx_fifo_depth / tx_fifo_count;
+}
+
+/**
* dwc2_hsotg_init_fifo - initialise non-periodic FIFOs
* @hsotg: The device instance.
*/
@@ -190,16 +294,17 @@ static void dwc2_hsotg_init_fifo(struct dwc2_hsotg *hsotg)
unsigned int addr;
int timeout;
u32 val;
+ u32 *txfsz = hsotg->params.g_tx_fifo_size;
/* Reset fifo map if not correctly cleared during previous session */
WARN_ON(hsotg->fifo_map);
hsotg->fifo_map = 0;
/* set RX/NPTX FIFO sizes */
- dwc2_writel(hsotg->g_rx_fifo_sz, hsotg->regs + GRXFSIZ);
- dwc2_writel((hsotg->g_rx_fifo_sz << FIFOSIZE_STARTADDR_SHIFT) |
- (hsotg->g_np_g_tx_fifo_sz << FIFOSIZE_DEPTH_SHIFT),
- hsotg->regs + GNPTXFSIZ);
+ dwc2_writel(hsotg->params.g_rx_fifo_size, hsotg->regs + GRXFSIZ);
+ dwc2_writel((hsotg->params.g_rx_fifo_size << FIFOSIZE_STARTADDR_SHIFT) |
+ (hsotg->params.g_np_tx_fifo_size << FIFOSIZE_DEPTH_SHIFT),
+ hsotg->regs + GNPTXFSIZ);
/*
* arange all the rest of the TX FIFOs, as some versions of this
@@ -209,7 +314,7 @@ static void dwc2_hsotg_init_fifo(struct dwc2_hsotg *hsotg)
*/
/* start at the end of the GNPTXFSIZ, rounded up */
- addr = hsotg->g_rx_fifo_sz + hsotg->g_np_g_tx_fifo_sz;
+ addr = hsotg->params.g_rx_fifo_size + hsotg->params.g_np_tx_fifo_size;
/*
* Configure fifos sizes from provided configuration and assign
@@ -217,17 +322,21 @@ static void dwc2_hsotg_init_fifo(struct dwc2_hsotg *hsotg)
* given endpoint.
*/
for (ep = 1; ep < MAX_EPS_CHANNELS; ep++) {
- if (!hsotg->g_tx_fifo_sz[ep])
+ if (!txfsz[ep])
continue;
val = addr;
- val |= hsotg->g_tx_fifo_sz[ep] << FIFOSIZE_DEPTH_SHIFT;
- WARN_ONCE(addr + hsotg->g_tx_fifo_sz[ep] > hsotg->fifo_mem,
+ val |= txfsz[ep] << FIFOSIZE_DEPTH_SHIFT;
+ WARN_ONCE(addr + txfsz[ep] > hsotg->fifo_mem,
"insufficient fifo memory");
- addr += hsotg->g_tx_fifo_sz[ep];
+ addr += txfsz[ep];
dwc2_writel(val, hsotg->regs + DPTXFSIZN(ep));
+ val = dwc2_readl(hsotg->regs + DPTXFSIZN(ep));
}
+ dwc2_writel(hsotg->hw_params.total_fifo_size |
+ addr << GDFIFOCFG_EPINFOBASE_SHIFT,
+ hsotg->regs + GDFIFOCFG);
/*
* according to p428 of the design guide, we need to ensure that
* all fifos are flushed before continuing
@@ -264,11 +373,11 @@ static void dwc2_hsotg_init_fifo(struct dwc2_hsotg *hsotg)
* Allocate a new USB request structure appropriate for the specified endpoint
*/
static struct usb_request *dwc2_hsotg_ep_alloc_request(struct usb_ep *ep,
- gfp_t flags)
+ gfp_t flags)
{
struct dwc2_hsotg_req *req;
- req = kzalloc(sizeof(struct dwc2_hsotg_req), flags);
+ req = kzalloc(sizeof(*req), flags);
if (!req)
return NULL;
@@ -299,18 +408,62 @@ static inline int is_ep_periodic(struct dwc2_hsotg_ep *hs_ep)
* of a request to ensure the buffer is ready for access by the caller.
*/
static void dwc2_hsotg_unmap_dma(struct dwc2_hsotg *hsotg,
- struct dwc2_hsotg_ep *hs_ep,
+ struct dwc2_hsotg_ep *hs_ep,
struct dwc2_hsotg_req *hs_req)
{
struct usb_request *req = &hs_req->req;
- /* ignore this if we're not moving any data */
- if (hs_req->req.length == 0)
- return;
-
usb_gadget_unmap_request(&hsotg->gadget, req, hs_ep->dir_in);
}
+/*
+ * dwc2_gadget_alloc_ctrl_desc_chains - allocate DMA descriptor chains
+ * for Control endpoint
+ * @hsotg: The device state.
+ *
+ * This function will allocate 4 descriptor chains for EP 0: 2 for
+ * Setup stage, per one for IN and OUT data/status transactions.
+ */
+static int dwc2_gadget_alloc_ctrl_desc_chains(struct dwc2_hsotg *hsotg)
+{
+ hsotg->setup_desc[0] =
+ dmam_alloc_coherent(hsotg->dev,
+ sizeof(struct dwc2_dma_desc),
+ &hsotg->setup_desc_dma[0],
+ GFP_KERNEL);
+ if (!hsotg->setup_desc[0])
+ goto fail;
+
+ hsotg->setup_desc[1] =
+ dmam_alloc_coherent(hsotg->dev,
+ sizeof(struct dwc2_dma_desc),
+ &hsotg->setup_desc_dma[1],
+ GFP_KERNEL);
+ if (!hsotg->setup_desc[1])
+ goto fail;
+
+ hsotg->ctrl_in_desc =
+ dmam_alloc_coherent(hsotg->dev,
+ sizeof(struct dwc2_dma_desc),
+ &hsotg->ctrl_in_desc_dma,
+ GFP_KERNEL);
+ if (!hsotg->ctrl_in_desc)
+ goto fail;
+
+ hsotg->ctrl_out_desc =
+ dmam_alloc_coherent(hsotg->dev,
+ sizeof(struct dwc2_dma_desc),
+ &hsotg->ctrl_out_desc_dma,
+ GFP_KERNEL);
+ if (!hsotg->ctrl_out_desc)
+ goto fail;
+
+ return 0;
+
+fail:
+ return -ENOMEM;
+}
+
/**
* dwc2_hsotg_write_fifo - write packet Data to the TxFIFO
* @hsotg: The controller state.
@@ -328,7 +481,7 @@ static void dwc2_hsotg_unmap_dma(struct dwc2_hsotg *hsotg,
* This routine is only needed for PIO
*/
static int dwc2_hsotg_write_fifo(struct dwc2_hsotg *hsotg,
- struct dwc2_hsotg_ep *hs_ep,
+ struct dwc2_hsotg_ep *hs_ep,
struct dwc2_hsotg_req *hs_req)
{
bool periodic = is_ep_periodic(hs_ep);
@@ -410,7 +563,7 @@ static int dwc2_hsotg_write_fifo(struct dwc2_hsotg *hsotg,
max_transfer = hs_ep->ep.maxpacket * hs_ep->mc;
dev_dbg(hsotg->dev, "%s: GNPTXSTS=%08x, can=%d, to=%d, max_transfer %d\n",
- __func__, gnptxsts, can_write, to_write, max_transfer);
+ __func__, gnptxsts, can_write, to_write, max_transfer);
/*
* limit to 512 bytes of data, it seems at least on the non-periodic
@@ -431,7 +584,7 @@ static int dwc2_hsotg_write_fifo(struct dwc2_hsotg *hsotg,
/* it's needed only when we do not use dedicated fifos */
if (!hsotg->dedicated_fifos)
dwc2_hsotg_en_gsint(hsotg,
- periodic ? GINTSTS_PTXFEMP :
+ periodic ? GINTSTS_PTXFEMP :
GINTSTS_NPTXFEMP);
}
@@ -460,12 +613,12 @@ static int dwc2_hsotg_write_fifo(struct dwc2_hsotg *hsotg,
/* it's needed only when we do not use dedicated fifos */
if (!hsotg->dedicated_fifos)
dwc2_hsotg_en_gsint(hsotg,
- periodic ? GINTSTS_PTXFEMP :
+ periodic ? GINTSTS_PTXFEMP :
GINTSTS_NPTXFEMP);
}
dev_dbg(hsotg->dev, "write %d/%d, can_write %d, done %d\n",
- to_write, hs_req->req.length, can_write, buf_pos);
+ to_write, hs_req->req.length, can_write, buf_pos);
if (to_write <= 0)
return -ENOSPC;
@@ -491,17 +644,17 @@ static int dwc2_hsotg_write_fifo(struct dwc2_hsotg *hsotg,
* Return the maximum data that can be queued in one go on a given endpoint
* so that transfers that are too long can be split.
*/
-static unsigned get_ep_limit(struct dwc2_hsotg_ep *hs_ep)
+static unsigned int get_ep_limit(struct dwc2_hsotg_ep *hs_ep)
{
int index = hs_ep->index;
- unsigned maxsize;
- unsigned maxpkt;
+ unsigned int maxsize;
+ unsigned int maxpkt;
if (index != 0) {
maxsize = DXEPTSIZ_XFERSIZE_LIMIT + 1;
maxpkt = DXEPTSIZ_PKTCNT_LIMIT + 1;
} else {
- maxsize = 64+64;
+ maxsize = 64 + 64;
if (hs_ep->dir_in)
maxpkt = DIEPTSIZ0_PKTCNT_LIMIT + 1;
else
@@ -524,11 +677,11 @@ static unsigned get_ep_limit(struct dwc2_hsotg_ep *hs_ep)
}
/**
-* dwc2_hsotg_read_frameno - read current frame number
-* @hsotg: The device instance
-*
-* Return the current frame number
-*/
+ * dwc2_hsotg_read_frameno - read current frame number
+ * @hsotg: The device instance
+ *
+ * Return the current frame number
+ */
static u32 dwc2_hsotg_read_frameno(struct dwc2_hsotg *hsotg)
{
u32 dsts;
@@ -541,6 +694,273 @@ static u32 dwc2_hsotg_read_frameno(struct dwc2_hsotg *hsotg)
}
/**
+ * dwc2_gadget_get_chain_limit - get the maximum data payload value of the
+ * DMA descriptor chain prepared for specific endpoint
+ * @hs_ep: The endpoint
+ *
+ * Return the maximum data that can be queued in one go on a given endpoint
+ * depending on its descriptor chain capacity so that transfers that
+ * are too long can be split.
+ */
+static unsigned int dwc2_gadget_get_chain_limit(struct dwc2_hsotg_ep *hs_ep)
+{
+ int is_isoc = hs_ep->isochronous;
+ unsigned int maxsize;
+
+ if (is_isoc)
+ maxsize = hs_ep->dir_in ? DEV_DMA_ISOC_TX_NBYTES_LIMIT :
+ DEV_DMA_ISOC_RX_NBYTES_LIMIT;
+ else
+ maxsize = DEV_DMA_NBYTES_LIMIT;
+
+ /* Above size of one descriptor was chosen, multiple it */
+ maxsize *= MAX_DMA_DESC_NUM_GENERIC;
+
+ return maxsize;
+}
+
+/*
+ * dwc2_gadget_get_desc_params - get DMA descriptor parameters.
+ * @hs_ep: The endpoint
+ * @mask: RX/TX bytes mask to be defined
+ *
+ * Returns maximum data payload for one descriptor after analyzing endpoint
+ * characteristics.
+ * DMA descriptor transfer bytes limit depends on EP type:
+ * Control out - MPS,
+ * Isochronous - descriptor rx/tx bytes bitfield limit,
+ * Control In/Bulk/Interrupt - multiple of mps. This will allow to not
+ * have concatenations from various descriptors within one packet.
+ *
+ * Selects corresponding mask for RX/TX bytes as well.
+ */
+static u32 dwc2_gadget_get_desc_params(struct dwc2_hsotg_ep *hs_ep, u32 *mask)
+{
+ u32 mps = hs_ep->ep.maxpacket;
+ int dir_in = hs_ep->dir_in;
+ u32 desc_size = 0;
+
+ if (!hs_ep->index && !dir_in) {
+ desc_size = mps;
+ *mask = DEV_DMA_NBYTES_MASK;
+ } else if (hs_ep->isochronous) {
+ if (dir_in) {
+ desc_size = DEV_DMA_ISOC_TX_NBYTES_LIMIT;
+ *mask = DEV_DMA_ISOC_TX_NBYTES_MASK;
+ } else {
+ desc_size = DEV_DMA_ISOC_RX_NBYTES_LIMIT;
+ *mask = DEV_DMA_ISOC_RX_NBYTES_MASK;
+ }
+ } else {
+ desc_size = DEV_DMA_NBYTES_LIMIT;
+ *mask = DEV_DMA_NBYTES_MASK;
+
+ /* Round down desc_size to be mps multiple */
+ desc_size -= desc_size % mps;
+ }
+
+ return desc_size;
+}
+
+/*
+ * dwc2_gadget_config_nonisoc_xfer_ddma - prepare non ISOC DMA desc chain.
+ * @hs_ep: The endpoint
+ * @dma_buff: DMA address to use
+ * @len: Length of the transfer
+ *
+ * This function will iterate over descriptor chain and fill its entries
+ * with corresponding information based on transfer data.
+ */
+static void dwc2_gadget_config_nonisoc_xfer_ddma(struct dwc2_hsotg_ep *hs_ep,
+ dma_addr_t dma_buff,
+ unsigned int len)
+{
+ struct dwc2_hsotg *hsotg = hs_ep->parent;
+ int dir_in = hs_ep->dir_in;
+ struct dwc2_dma_desc *desc = hs_ep->desc_list;
+ u32 mps = hs_ep->ep.maxpacket;
+ u32 maxsize = 0;
+ u32 offset = 0;
+ u32 mask = 0;
+ int i;
+
+ maxsize = dwc2_gadget_get_desc_params(hs_ep, &mask);
+
+ hs_ep->desc_count = (len / maxsize) +
+ ((len % maxsize) ? 1 : 0);
+ if (len == 0)
+ hs_ep->desc_count = 1;
+
+ for (i = 0; i < hs_ep->desc_count; ++i) {
+ desc->status = 0;
+ desc->status |= (DEV_DMA_BUFF_STS_HBUSY
+ << DEV_DMA_BUFF_STS_SHIFT);
+
+ if (len > maxsize) {
+ if (!hs_ep->index && !dir_in)
+ desc->status |= (DEV_DMA_L | DEV_DMA_IOC);
+
+ desc->status |= (maxsize <<
+ DEV_DMA_NBYTES_SHIFT & mask);
+ desc->buf = dma_buff + offset;
+
+ len -= maxsize;
+ offset += maxsize;
+ } else {
+ desc->status |= (DEV_DMA_L | DEV_DMA_IOC);
+
+ if (dir_in)
+ desc->status |= (len % mps) ? DEV_DMA_SHORT :
+ ((hs_ep->send_zlp) ? DEV_DMA_SHORT : 0);
+ if (len > maxsize)
+ dev_err(hsotg->dev, "wrong len %d\n", len);
+
+ desc->status |=
+ len << DEV_DMA_NBYTES_SHIFT & mask;
+ desc->buf = dma_buff + offset;
+ }
+
+ desc->status &= ~DEV_DMA_BUFF_STS_MASK;
+ desc->status |= (DEV_DMA_BUFF_STS_HREADY
+ << DEV_DMA_BUFF_STS_SHIFT);
+ desc++;
+ }
+}
+
+/*
+ * dwc2_gadget_fill_isoc_desc - fills next isochronous descriptor in chain.
+ * @hs_ep: The isochronous endpoint.
+ * @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,
+ * 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.
+ */
+static int dwc2_gadget_fill_isoc_desc(struct dwc2_hsotg_ep *hs_ep,
+ dma_addr_t dma_buff, unsigned int len)
+{
+ struct dwc2_dma_desc *desc;
+ struct dwc2_hsotg *hsotg = hs_ep->parent;
+ u32 index;
+ u32 maxsize = 0;
+ u32 mask = 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;
+
+ /* 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;
+ }
+
+ 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;
+
+ dev_dbg(hsotg->dev, "%s: Filling ep %d, dir %s isoc desc # %d\n",
+ __func__, hs_ep->index, hs_ep->dir_in ? "in" : "out", index);
+
+ desc->status = 0;
+ desc->status |= (DEV_DMA_BUFF_STS_HBUSY << DEV_DMA_BUFF_STS_SHIFT);
+
+ desc->buf = dma_buff;
+ desc->status |= (DEV_DMA_L | DEV_DMA_IOC |
+ ((len << DEV_DMA_NBYTES_SHIFT) & mask));
+
+ if (hs_ep->dir_in) {
+ desc->status |= ((hs_ep->mc << DEV_DMA_ISOC_PID_SHIFT) &
+ DEV_DMA_ISOC_PID_MASK) |
+ ((len % hs_ep->ep.maxpacket) ?
+ DEV_DMA_SHORT : 0) |
+ ((hs_ep->target_frame <<
+ DEV_DMA_ISOC_FRNUM_SHIFT) &
+ DEV_DMA_ISOC_FRNUM_MASK);
+ }
+
+ desc->status &= ~DEV_DMA_BUFF_STS_MASK;
+ desc->status |= (DEV_DMA_BUFF_STS_HREADY << DEV_DMA_BUFF_STS_SHIFT);
+
+ /* Update index of last configured entry in the chain */
+ hs_ep->next_desc++;
+
+ return 0;
+}
+
+/*
+ * dwc2_gadget_start_isoc_ddma - start isochronous transfer in DDMA
+ * @hs_ep: The isochronous endpoint.
+ *
+ * Prepare first 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)
+{
+ struct dwc2_hsotg *hsotg = hs_ep->parent;
+ struct dwc2_hsotg_req *hs_req, *treq;
+ int index = hs_ep->index;
+ int ret;
+ u32 dma_reg;
+ u32 depctl;
+ u32 ctrl;
+
+ if (list_empty(&hs_ep->queue)) {
+ dev_dbg(hsotg->dev, "%s: No requests in queue\n", __func__);
+ return;
+ }
+
+ 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__);
+ break;
+ }
+ }
+
+ 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);
+
+ ctrl = dwc2_readl(hsotg->regs + 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_hsotg_start_req - start a USB request from an endpoint's queue
* @hsotg: The controller state.
* @hs_ep: The endpoint to process a request for
@@ -551,7 +971,7 @@ static u32 dwc2_hsotg_read_frameno(struct dwc2_hsotg *hsotg)
* appropriately, and writing any data to the FIFOs.
*/
static void dwc2_hsotg_start_req(struct dwc2_hsotg *hsotg,
- struct dwc2_hsotg_ep *hs_ep,
+ struct dwc2_hsotg_ep *hs_ep,
struct dwc2_hsotg_req *hs_req,
bool continuing)
{
@@ -562,9 +982,10 @@ static void dwc2_hsotg_start_req(struct dwc2_hsotg *hsotg,
u32 epsize_reg;
u32 epsize;
u32 ctrl;
- unsigned length;
- unsigned packets;
- unsigned maxreq;
+ unsigned int length;
+ unsigned int packets;
+ unsigned int maxreq;
+ unsigned int dma_reg;
if (index != 0) {
if (hs_ep->req && !continuing) {
@@ -579,6 +1000,7 @@ static void dwc2_hsotg_start_req(struct dwc2_hsotg *hsotg,
}
}
+ dma_reg = dir_in ? DIEPDMA(index) : DOEPDMA(index);
epctrl_reg = dir_in ? DIEPCTL(index) : DOEPCTL(index);
epsize_reg = dir_in ? DIEPTSIZ(index) : DOEPTSIZ(index);
@@ -598,7 +1020,11 @@ static void dwc2_hsotg_start_req(struct dwc2_hsotg *hsotg,
dev_dbg(hsotg->dev, "ureq->length:%d ureq->actual:%d\n",
ureq->length, ureq->actual);
- maxreq = get_ep_limit(hs_ep);
+ if (!using_desc_dma(hsotg))
+ maxreq = get_ep_limit(hs_ep);
+ else
+ maxreq = dwc2_gadget_get_chain_limit(hs_ep);
+
if (length > maxreq) {
int round = maxreq % hs_ep->ep.maxpacket;
@@ -637,7 +1063,7 @@ static void dwc2_hsotg_start_req(struct dwc2_hsotg *hsotg,
if (dir_in && ureq->zero && !continuing) {
/* Test if zlp is actually required. */
if ((ureq->length >= hs_ep->ep.maxpacket) &&
- !(ureq->length % hs_ep->ep.maxpacket))
+ !(ureq->length % hs_ep->ep.maxpacket))
hs_ep->send_zlp = 1;
}
@@ -650,22 +1076,51 @@ static void dwc2_hsotg_start_req(struct dwc2_hsotg *hsotg,
/* store the request as the current one we're doing */
hs_ep->req = hs_req;
- /* write size / packets */
- dwc2_writel(epsize, hsotg->regs + epsize_reg);
+ if (using_desc_dma(hsotg)) {
+ u32 offset = 0;
+ u32 mps = hs_ep->ep.maxpacket;
- if (using_dma(hsotg) && !continuing) {
- unsigned int dma_reg;
+ /* Adjust length: EP0 - MPS, other OUT EPs - multiple of MPS */
+ if (!dir_in) {
+ if (!index)
+ length = mps;
+ else if (length % mps)
+ length += (mps - (length % mps));
+ }
/*
- * write DMA address to control register, buffer already
- * synced by dwc2_hsotg_ep_queue().
+ * If more data to send, adjust DMA for EP0 out data stage.
+ * ureq->dma stays unchanged, hence increment it by already
+ * passed passed data count before starting new transaction.
*/
+ if (!index && hsotg->ep0_state == DWC2_EP0_DATA_OUT &&
+ continuing)
+ offset = ureq->actual;
+
+ /* Fill DDMA chain entries */
+ dwc2_gadget_config_nonisoc_xfer_ddma(hs_ep, ureq->dma + offset,
+ length);
- dma_reg = dir_in ? DIEPDMA(index) : DOEPDMA(index);
- dwc2_writel(ureq->dma, hsotg->regs + dma_reg);
+ /* write descriptor chain address to control register */
+ dwc2_writel(hs_ep->desc_list_dma, hsotg->regs + dma_reg);
- dev_dbg(hsotg->dev, "%s: %pad => 0x%08x\n",
- __func__, &ureq->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);
+
+ if (using_dma(hsotg) && !continuing && (length != 0)) {
+ /*
+ * write DMA address to control register, buffer
+ * already synced by dwc2_hsotg_ep_queue().
+ */
+
+ dwc2_writel(ureq->dma, hsotg->regs + dma_reg);
+
+ dev_dbg(hsotg->dev, "%s: %pad => 0x%08x\n",
+ __func__, &ureq->dma, dma_reg);
+ }
}
if (hs_ep->isochronous && hs_ep->interval == 1) {
@@ -712,7 +1167,7 @@ static void dwc2_hsotg_start_req(struct dwc2_hsotg *hsotg,
/* check ep is enabled */
if (!(dwc2_readl(hsotg->regs + epctrl_reg) & DXEPCTL_EPENA))
dev_dbg(hsotg->dev,
- "ep%d: failed to become enabled (DXEPCTL=0x%08x)?\n",
+ "ep%d: failed to become enabled (DXEPCTL=0x%08x)?\n",
index, dwc2_readl(hsotg->regs + epctrl_reg));
dev_dbg(hsotg->dev, "%s: DXEPCTL=0x%08x\n",
@@ -735,16 +1190,11 @@ static void dwc2_hsotg_start_req(struct dwc2_hsotg *hsotg,
* cleanup on completion.
*/
static int dwc2_hsotg_map_dma(struct dwc2_hsotg *hsotg,
- struct dwc2_hsotg_ep *hs_ep,
+ struct dwc2_hsotg_ep *hs_ep,
struct usb_request *req)
{
- struct dwc2_hsotg_req *hs_req = our_req(req);
int ret;
- /* if the length is zero, ignore the DMA data */
- if (hs_req->req.length == 0)
- return 0;
-
ret = usb_gadget_map_request(&hsotg->gadget, req, hs_ep->dir_in);
if (ret)
goto dma_error;
@@ -759,7 +1209,8 @@ dma_error:
}
static int dwc2_hsotg_handle_unaligned_buf_start(struct dwc2_hsotg *hsotg,
- struct dwc2_hsotg_ep *hs_ep, struct dwc2_hsotg_req *hs_req)
+ struct dwc2_hsotg_ep *hs_ep,
+ struct dwc2_hsotg_req *hs_req)
{
void *req_buf = hs_req->req.buf;
@@ -770,7 +1221,7 @@ static int dwc2_hsotg_handle_unaligned_buf_start(struct dwc2_hsotg *hsotg,
WARN_ON(hs_req->saved_req_buf);
dev_dbg(hsotg->dev, "%s: %s: buf=%p length=%d\n", __func__,
- hs_ep->ep.name, req_buf, hs_req->req.length);
+ hs_ep->ep.name, req_buf, hs_req->req.length);
hs_req->req.buf = kmalloc(hs_req->req.length, GFP_ATOMIC);
if (!hs_req->req.buf) {
@@ -789,8 +1240,10 @@ static int dwc2_hsotg_handle_unaligned_buf_start(struct dwc2_hsotg *hsotg,
return 0;
}
-static void dwc2_hsotg_handle_unaligned_buf_complete(struct dwc2_hsotg *hsotg,
- struct dwc2_hsotg_ep *hs_ep, struct dwc2_hsotg_req *hs_req)
+static void
+dwc2_hsotg_handle_unaligned_buf_complete(struct dwc2_hsotg *hsotg,
+ struct dwc2_hsotg_ep *hs_ep,
+ struct dwc2_hsotg_req *hs_req)
{
/* If dma is not being used or buffer was aligned */
if (!using_dma(hsotg) || !hs_req->saved_req_buf)
@@ -802,7 +1255,7 @@ static void dwc2_hsotg_handle_unaligned_buf_complete(struct dwc2_hsotg *hsotg,
/* Copy data from bounce buffer on successful out transfer */
if (!hs_ep->dir_in && !hs_req->req.status)
memcpy(hs_req->saved_req_buf, hs_req->req.buf,
- hs_req->req.actual);
+ hs_req->req.actual);
/* Free bounce buffer */
kfree(hs_req->req.buf);
@@ -835,8 +1288,43 @@ static bool dwc2_gadget_target_frame_elapsed(struct dwc2_hsotg_ep *hs_ep)
return false;
}
+/*
+ * dwc2_gadget_set_ep0_desc_chain - Set EP's desc chain pointers
+ * @hsotg: The driver state
+ * @hs_ep: the ep descriptor chain is for
+ *
+ * Called to update EP0 structure's pointers depend on stage of
+ * control transfer.
+ */
+static int dwc2_gadget_set_ep0_desc_chain(struct dwc2_hsotg *hsotg,
+ struct dwc2_hsotg_ep *hs_ep)
+{
+ switch (hsotg->ep0_state) {
+ case DWC2_EP0_SETUP:
+ case DWC2_EP0_STATUS_OUT:
+ hs_ep->desc_list = hsotg->setup_desc[0];
+ hs_ep->desc_list_dma = hsotg->setup_desc_dma[0];
+ break;
+ case DWC2_EP0_DATA_IN:
+ case DWC2_EP0_STATUS_IN:
+ hs_ep->desc_list = hsotg->ctrl_in_desc;
+ hs_ep->desc_list_dma = hsotg->ctrl_in_desc_dma;
+ break;
+ case DWC2_EP0_DATA_OUT:
+ hs_ep->desc_list = hsotg->ctrl_out_desc;
+ hs_ep->desc_list_dma = hsotg->ctrl_out_desc_dma;
+ break;
+ default:
+ dev_err(hsotg->dev, "invalid EP 0 state in queue %d\n",
+ hsotg->ep0_state);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int dwc2_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req,
- gfp_t gfp_flags)
+ gfp_t gfp_flags)
{
struct dwc2_hsotg_req *hs_req = our_req(req);
struct dwc2_hsotg_ep *hs_ep = our_ep(ep);
@@ -851,7 +1339,7 @@ static int dwc2_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req,
/* Prevent new request submission when controller is suspended */
if (hs->lx_state == DWC2_L2) {
dev_dbg(hs->dev, "%s: don't submit request while suspended\n",
- __func__);
+ __func__);
return -EAGAIN;
}
@@ -870,10 +1358,32 @@ static int dwc2_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req,
if (ret)
return ret;
}
+ /* If using descriptor DMA configure EP0 descriptor chain pointers */
+ if (using_desc_dma(hs) && !hs_ep->index) {
+ ret = dwc2_gadget_set_ep0_desc_chain(hs, hs_ep);
+ if (ret)
+ return ret;
+ }
first = list_empty(&hs_ep->queue);
list_add_tail(&hs_req->queue, &hs_ep->queue);
+ /*
+ * Handle DDMA isochronous transfers separately - just add new entry
+ * to the half of descriptor chain that is not processed by HW.
+ * 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__);
+
+ return 0;
+ }
+
if (first) {
if (!hs_ep->isochronous) {
dwc2_hsotg_start_req(hs, hs_ep, hs_req, false);
@@ -890,7 +1400,7 @@ static int dwc2_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req,
}
static int dwc2_hsotg_ep_queue_lock(struct usb_ep *ep, struct usb_request *req,
- gfp_t gfp_flags)
+ gfp_t gfp_flags)
{
struct dwc2_hsotg_ep *hs_ep = our_ep(ep);
struct dwc2_hsotg *hs = hs_ep->parent;
@@ -905,7 +1415,7 @@ static int dwc2_hsotg_ep_queue_lock(struct usb_ep *ep, struct usb_request *req,
}
static void dwc2_hsotg_ep_free_request(struct usb_ep *ep,
- struct usb_request *req)
+ struct usb_request *req)
{
struct dwc2_hsotg_req *hs_req = our_req(req);
@@ -921,7 +1431,7 @@ static void dwc2_hsotg_ep_free_request(struct usb_ep *ep,
* submitted that need cleaning up.
*/
static void dwc2_hsotg_complete_oursetup(struct usb_ep *ep,
- struct usb_request *req)
+ struct usb_request *req)
{
struct dwc2_hsotg_ep *hs_ep = our_ep(ep);
struct dwc2_hsotg *hsotg = hs_ep->parent;
@@ -940,7 +1450,7 @@ static void dwc2_hsotg_complete_oursetup(struct usb_ep *ep,
* structure, or return NULL if it is not a valid endpoint.
*/
static struct dwc2_hsotg_ep *ep_from_windex(struct dwc2_hsotg *hsotg,
- u32 windex)
+ u32 windex)
{
struct dwc2_hsotg_ep *ep;
int dir = (windex & USB_DIR_IN) ? 1 : 0;
@@ -997,7 +1507,7 @@ int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode)
* an internal method of sending replies to certain control requests, etc.
*/
static int dwc2_hsotg_send_reply(struct dwc2_hsotg *hsotg,
- struct dwc2_hsotg_ep *ep,
+ struct dwc2_hsotg_ep *ep,
void *buff,
int length)
{
@@ -1040,7 +1550,7 @@ static int dwc2_hsotg_send_reply(struct dwc2_hsotg *hsotg,
* @ctrl: USB control request
*/
static int dwc2_hsotg_process_req_status(struct dwc2_hsotg *hsotg,
- struct usb_ctrlrequest *ctrl)
+ struct usb_ctrlrequest *ctrl)
{
struct dwc2_hsotg_ep *ep0 = hsotg->eps_out[0];
struct dwc2_hsotg_ep *ep;
@@ -1056,8 +1566,11 @@ static int dwc2_hsotg_process_req_status(struct dwc2_hsotg *hsotg,
switch (ctrl->bRequestType & USB_RECIP_MASK) {
case USB_RECIP_DEVICE:
- reply = cpu_to_le16(0); /* bit 0 => self powered,
- * bit 1 => remote wakeup */
+ /*
+ * bit 0 => self powered
+ * bit 1 => remote wakeup
+ */
+ reply = cpu_to_le16(0);
break;
case USB_RECIP_INTERFACE:
@@ -1099,10 +1612,8 @@ static int dwc2_hsotg_ep_sethalt(struct usb_ep *ep, int value, bool now);
*/
static struct dwc2_hsotg_req *get_ep_head(struct dwc2_hsotg_ep *hs_ep)
{
- if (list_empty(&hs_ep->queue))
- return NULL;
-
- return list_first_entry(&hs_ep->queue, struct dwc2_hsotg_req, queue);
+ return list_first_entry_or_null(&hs_ep->queue, struct dwc2_hsotg_req,
+ queue);
}
/**
@@ -1147,7 +1658,7 @@ static void dwc2_gadget_start_next_request(struct dwc2_hsotg_ep *hs_ep)
* @ctrl: USB control request
*/
static int dwc2_hsotg_process_req_feature(struct dwc2_hsotg *hsotg,
- struct usb_ctrlrequest *ctrl)
+ struct usb_ctrlrequest *ctrl)
{
struct dwc2_hsotg_ep *ep0 = hsotg->eps_out[0];
struct dwc2_hsotg_req *hs_req;
@@ -1232,9 +1743,8 @@ static int dwc2_hsotg_process_req_feature(struct dwc2_hsotg *hsotg,
}
/* If we have pending request, then start it */
- if (!ep->req) {
+ if (!ep->req)
dwc2_gadget_start_next_request(ep);
- }
}
break;
@@ -1297,7 +1807,7 @@ static void dwc2_hsotg_stall_ep0(struct dwc2_hsotg *hsotg)
* gadget driver).
*/
static void dwc2_hsotg_process_control(struct dwc2_hsotg *hsotg,
- struct usb_ctrlrequest *ctrl)
+ struct usb_ctrlrequest *ctrl)
{
struct dwc2_hsotg_ep *ep0 = hsotg->eps_out[0];
int ret = 0;
@@ -1373,7 +1883,7 @@ static void dwc2_hsotg_process_control(struct dwc2_hsotg *hsotg,
* EP0 setup packets
*/
static void dwc2_hsotg_complete_setup(struct usb_ep *ep,
- struct usb_request *req)
+ struct usb_request *req)
{
struct dwc2_hsotg_ep *hs_ep = our_ep(ep);
struct dwc2_hsotg *hsotg = hs_ep->parent;
@@ -1431,7 +1941,7 @@ static void dwc2_hsotg_enqueue_setup(struct dwc2_hsotg *hsotg)
}
static void dwc2_hsotg_program_zlp(struct dwc2_hsotg *hsotg,
- struct dwc2_hsotg_ep *hs_ep)
+ struct dwc2_hsotg_ep *hs_ep)
{
u32 ctrl;
u8 index = hs_ep->index;
@@ -1440,14 +1950,21 @@ static void dwc2_hsotg_program_zlp(struct dwc2_hsotg *hsotg,
if (hs_ep->dir_in)
dev_dbg(hsotg->dev, "Sending zero-length packet on ep%d\n",
- index);
+ index);
else
dev_dbg(hsotg->dev, "Receiving zero-length packet on ep%d\n",
- index);
+ index);
+ if (using_desc_dma(hsotg)) {
+ /* Not specific buffer needed for ep0 ZLP */
+ dma_addr_t dma = hs_ep->desc_list_dma;
- dwc2_writel(DXEPTSIZ_MC(1) | DXEPTSIZ_PKTCNT(1) |
- DXEPTSIZ_XFERSIZE(0), hsotg->regs +
- epsiz_reg);
+ dwc2_gadget_set_ep0_desc_chain(hsotg, hs_ep);
+ dwc2_gadget_config_nonisoc_xfer_ddma(hs_ep, dma, 0);
+ } else {
+ dwc2_writel(DXEPTSIZ_MC(1) | DXEPTSIZ_PKTCNT(1) |
+ DXEPTSIZ_XFERSIZE(0), hsotg->regs +
+ epsiz_reg);
+ }
ctrl = dwc2_readl(hsotg->regs + epctl_reg);
ctrl |= DXEPCTL_CNAK; /* clear NAK set by core */
@@ -1470,11 +1987,10 @@ static void dwc2_hsotg_program_zlp(struct dwc2_hsotg *hsotg,
* Note, expects the ep to already be locked as appropriate.
*/
static void dwc2_hsotg_complete_request(struct dwc2_hsotg *hsotg,
- struct dwc2_hsotg_ep *hs_ep,
+ struct dwc2_hsotg_ep *hs_ep,
struct dwc2_hsotg_req *hs_req,
int result)
{
-
if (!hs_req) {
dev_dbg(hsotg->dev, "%s: nothing to complete?\n", __func__);
return;
@@ -1510,14 +2026,126 @@ static void dwc2_hsotg_complete_request(struct dwc2_hsotg *hsotg,
spin_lock(&hsotg->lock);
}
+ /* In DDMA don't need to proceed to starting of next ISOC request */
+ if (using_desc_dma(hsotg) && hs_ep->isochronous)
+ return;
+
/*
* Look to see if there is anything else to do. Note, the completion
* of the previous request may have caused a new request to be started
* so be careful when doing this.
*/
- if (!hs_ep->req && result >= 0) {
+ if (!hs_ep->req && result >= 0)
dwc2_gadget_start_next_request(hs_ep);
+}
+
+/*
+ * dwc2_gadget_complete_isoc_request_ddma - complete an isoc request in DDMA
+ * @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.
+ */
+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);
+
+ index = (depdma - dma_addr) / sizeof(struct dwc2_dma_desc) - 1;
+ desc_sts = hs_ep->desc_list[index].status;
+
+ 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 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_gadget_start_next_isoc_ddma - start next isoc request, if any.
+ * @hs_ep: The isochronous endpoint to be re-enabled.
+ *
+ * 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.
+ */
+static void dwc2_gadget_start_next_isoc_ddma(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;
+
+ dev_dbg(hsotg->dev, "%s: Restarted isochronous endpoint\n",
+ __func__);
}
}
@@ -1540,13 +2168,12 @@ static void dwc2_hsotg_rx_data(struct dwc2_hsotg *hsotg, int ep_idx, int size)
int max_req;
int read_ptr;
-
if (!hs_req) {
u32 epctl = dwc2_readl(hsotg->regs + DOEPCTL(ep_idx));
int ptr;
dev_dbg(hsotg->dev,
- "%s: FIFO %d bytes on ep%d but no req (DXEPCTl=0x%08x)\n",
+ "%s: FIFO %d bytes on ep%d but no req (DXEPCTl=0x%08x)\n",
__func__, size, ep_idx, epctl);
/* dump the data from the FIFO, we've nothing we can do */
@@ -1606,7 +2233,7 @@ static void dwc2_hsotg_ep0_zlp(struct dwc2_hsotg *hsotg, bool dir_in)
}
static void dwc2_hsotg_change_ep_iso_parity(struct dwc2_hsotg *hsotg,
- u32 epctl_reg)
+ u32 epctl_reg)
{
u32 ctrl;
@@ -1618,6 +2245,36 @@ static void dwc2_hsotg_change_ep_iso_parity(struct dwc2_hsotg *hsotg,
dwc2_writel(ctrl, hsotg->regs + epctl_reg);
}
+/*
+ * dwc2_gadget_get_xfersize_ddma - get transferred bytes amount from desc
+ * @hs_ep - The endpoint on which transfer went
+ *
+ * Iterate over endpoints descriptor chain and get info on bytes remained
+ * in DMA descriptors after transfer has completed. Used for non isoc EPs.
+ */
+static unsigned int dwc2_gadget_get_xfersize_ddma(struct dwc2_hsotg_ep *hs_ep)
+{
+ struct dwc2_hsotg *hsotg = hs_ep->parent;
+ unsigned int bytes_rem = 0;
+ struct dwc2_dma_desc *desc = hs_ep->desc_list;
+ int i;
+ u32 status;
+
+ if (!desc)
+ return -EINVAL;
+
+ for (i = 0; i < hs_ep->desc_count; ++i) {
+ status = desc->status;
+ bytes_rem += status & DEV_DMA_NBYTES_MASK;
+
+ if (status & DEV_DMA_STS_MASK)
+ dev_err(hsotg->dev, "descriptor %d closed with %x\n",
+ i, status & DEV_DMA_STS_MASK);
+ }
+
+ return bytes_rem;
+}
+
/**
* dwc2_hsotg_handle_outdone - handle receiving OutDone/SetupDone from RXFIFO
* @hsotg: The device instance
@@ -1633,7 +2290,7 @@ static void dwc2_hsotg_handle_outdone(struct dwc2_hsotg *hsotg, int 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;
- unsigned size_left = DXEPTSIZ_XFERSIZE_GET(epsize);
+ unsigned int size_left = DXEPTSIZ_XFERSIZE_GET(epsize);
int result = 0;
if (!hs_req) {
@@ -1648,8 +2305,11 @@ static void dwc2_hsotg_handle_outdone(struct dwc2_hsotg *hsotg, int epnum)
return;
}
+ if (using_desc_dma(hsotg))
+ size_left = dwc2_gadget_get_xfersize_ddma(hs_ep);
+
if (using_dma(hsotg)) {
- unsigned size_done;
+ unsigned int size_done;
/*
* Calculate the size of the transfer by checking how much
@@ -1682,7 +2342,9 @@ static void dwc2_hsotg_handle_outdone(struct dwc2_hsotg *hsotg, int epnum)
*/
}
- if (epnum == 0 && hsotg->ep0_state == DWC2_EP0_DATA_OUT) {
+ /* DDMA IN status phase will start from StsPhseRcvd interrupt */
+ if (!using_desc_dma(hsotg) && epnum == 0 &&
+ hsotg->ep0_state == DWC2_EP0_DATA_OUT) {
/* Move to STATUS IN */
dwc2_hsotg_ep0_zlp(hsotg, true);
return;
@@ -1732,7 +2394,7 @@ static void dwc2_hsotg_handle_rx(struct dwc2_hsotg *hsotg)
size >>= GRXSTS_BYTECNT_SHIFT;
dev_dbg(hsotg->dev, "%s: GRXSTSP=0x%08x (%d@%d)\n",
- __func__, grxstsr, size, epnum);
+ __func__, grxstsr, size, epnum);
switch ((status & GRXSTS_PKTSTS_MASK) >> GRXSTS_PKTSTS_SHIFT) {
case GRXSTS_PKTSTS_GLOBALOUTNAK:
@@ -1812,17 +2474,17 @@ static u32 dwc2_hsotg_ep0_mps(unsigned int mps)
* @hsotg: The driver state.
* @ep: The index number of the endpoint
* @mps: The maximum packet size in bytes
+ * @mc: The multicount value
*
* Configure the maximum packet size for the given endpoint, updating
* the hardware control registers to reflect this.
*/
static void dwc2_hsotg_set_ep_maxpacket(struct dwc2_hsotg *hsotg,
- unsigned int ep, unsigned int mps, unsigned int dir_in)
+ unsigned int ep, unsigned int mps,
+ unsigned int mc, unsigned int dir_in)
{
struct dwc2_hsotg_ep *hs_ep;
void __iomem *regs = hsotg->regs;
- u32 mpsval;
- u32 mcval;
u32 reg;
hs_ep = index_to_ep(hsotg, ep, dir_in);
@@ -1830,32 +2492,32 @@ static void dwc2_hsotg_set_ep_maxpacket(struct dwc2_hsotg *hsotg,
return;
if (ep == 0) {
+ u32 mps_bytes = mps;
+
/* EP0 is a special case */
- mpsval = dwc2_hsotg_ep0_mps(mps);
- if (mpsval > 3)
+ mps = dwc2_hsotg_ep0_mps(mps_bytes);
+ if (mps > 3)
goto bad_mps;
- hs_ep->ep.maxpacket = mps;
+ hs_ep->ep.maxpacket = mps_bytes;
hs_ep->mc = 1;
} else {
- mpsval = mps & DXEPCTL_MPS_MASK;
- if (mpsval > 1024)
+ if (mps > 1024)
goto bad_mps;
- mcval = ((mps >> 11) & 0x3) + 1;
- hs_ep->mc = mcval;
- if (mcval > 3)
+ hs_ep->mc = mc;
+ if (mc > 3)
goto bad_mps;
- hs_ep->ep.maxpacket = mpsval;
+ hs_ep->ep.maxpacket = mps;
}
if (dir_in) {
reg = dwc2_readl(regs + DIEPCTL(ep));
reg &= ~DXEPCTL_MPS_MASK;
- reg |= mpsval;
+ reg |= mps;
dwc2_writel(reg, regs + DIEPCTL(ep));
} else {
reg = dwc2_readl(regs + DOEPCTL(ep));
reg &= ~DXEPCTL_MPS_MASK;
- reg |= mpsval;
+ reg |= mps;
dwc2_writel(reg, regs + DOEPCTL(ep));
}
@@ -1907,7 +2569,7 @@ static void dwc2_hsotg_txfifo_flush(struct dwc2_hsotg *hsotg, unsigned int idx)
* make an attempt to write data into the FIFO.
*/
static int dwc2_hsotg_trytx(struct dwc2_hsotg *hsotg,
- struct dwc2_hsotg_ep *hs_ep)
+ struct dwc2_hsotg_ep *hs_ep)
{
struct dwc2_hsotg_req *hs_req = hs_ep->req;
@@ -1918,7 +2580,7 @@ static int dwc2_hsotg_trytx(struct dwc2_hsotg *hsotg,
*/
if (hs_ep->index != 0)
dwc2_hsotg_ctrl_epint(hsotg, hs_ep->index,
- hs_ep->dir_in, 0);
+ hs_ep->dir_in, 0);
return 0;
}
@@ -1940,7 +2602,7 @@ static int dwc2_hsotg_trytx(struct dwc2_hsotg *hsotg,
* call the relevant completion routines.
*/
static void dwc2_hsotg_complete_in(struct dwc2_hsotg *hsotg,
- struct dwc2_hsotg_ep *hs_ep)
+ 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));
@@ -1954,6 +2616,13 @@ static void dwc2_hsotg_complete_in(struct dwc2_hsotg *hsotg,
/* Finish ZLP handling for IN EP0 transactions */
if (hs_ep->index == 0 && hsotg->ep0_state == DWC2_EP0_STATUS_IN) {
dev_dbg(hsotg->dev, "zlp packet sent\n");
+
+ /*
+ * While send zlp for DWC2_EP0_STATUS_IN EP direction was
+ * changed to IN. Change back to complete OUT transfer request
+ */
+ hs_ep->dir_in = 0;
+
dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req, 0);
if (hsotg->test_mode) {
int ret;
@@ -1961,7 +2630,7 @@ static void dwc2_hsotg_complete_in(struct dwc2_hsotg *hsotg,
ret = dwc2_hsotg_set_test_mode(hsotg, hsotg->test_mode);
if (ret < 0) {
dev_dbg(hsotg->dev, "Invalid Test #%d\n",
- hsotg->test_mode);
+ hsotg->test_mode);
dwc2_hsotg_stall_ep0(hsotg);
return;
}
@@ -1979,8 +2648,14 @@ static void dwc2_hsotg_complete_in(struct dwc2_hsotg *hsotg,
* past the end of the buffer (DMA transfers are always 32bit
* aligned).
*/
-
- size_left = DXEPTSIZ_XFERSIZE_GET(epsize);
+ if (using_desc_dma(hsotg)) {
+ size_left = dwc2_gadget_get_xfersize_ddma(hs_ep);
+ if (size_left < 0)
+ dev_err(hsotg->dev, "error parsing DDMA results %d\n",
+ size_left);
+ } else {
+ size_left = DXEPTSIZ_XFERSIZE_GET(epsize);
+ }
size_done = hs_ep->size_loaded - size_left;
size_done += hs_ep->last_load;
@@ -2128,12 +2803,28 @@ 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;
+ dwc2_gadget_start_isoc_ddma(ep);
+ }
+ return;
+ }
+
if (ep->interval > 1 &&
ep->target_frame == TARGET_FRAME_INITIAL) {
u32 dsts;
@@ -2159,19 +2850,19 @@ static void dwc2_gadget_handle_out_token_ep_disabled(struct dwc2_hsotg_ep *ep)
}
/**
-* dwc2_gadget_handle_nak - handle NAK interrupt
-* @hs_ep: The endpoint on which interrupt is asserted.
-*
-* This is starting point for ISOC-IN transfer, synchronization done with
-* first IN token received from host while corresponding EP is disabled.
-*
-* Device does not know when first one token will arrive from host. On first
-* token arrival HW generates 2 interrupts: 'in token received while FIFO empty'
-* and 'NAK'. NAK interrupt for ISOC-IN means that token has arrived and ZLP was
-* sent in response to that as there was no data in FIFO. SW is basing on this
-* interrupt to obtain frame in which token has come and then based on the
-* interval calculates next frame for transfer.
-*/
+ * dwc2_gadget_handle_nak - handle NAK interrupt
+ * @hs_ep: The endpoint on which interrupt is asserted.
+ *
+ * This is starting point for ISOC-IN transfer, synchronization done with
+ * first IN token received from host while corresponding EP is disabled.
+ *
+ * Device does not know when first one token will arrive from host. On first
+ * token arrival HW generates 2 interrupts: 'in token received while FIFO empty'
+ * and 'NAK'. NAK interrupt for ISOC-IN means that token has arrived and ZLP was
+ * sent in response to that as there was no data in FIFO. SW is basing on this
+ * interrupt to obtain frame in which token has come and then based on the
+ * interval calculates next frame for transfer.
+ */
static void dwc2_gadget_handle_nak(struct dwc2_hsotg_ep *hs_ep)
{
struct dwc2_hsotg *hsotg = hs_ep->parent;
@@ -2182,6 +2873,12 @@ static void dwc2_gadget_handle_nak(struct dwc2_hsotg_ep *hs_ep)
if (hs_ep->target_frame == TARGET_FRAME_INITIAL) {
hs_ep->target_frame = dwc2_hsotg_read_frameno(hsotg);
+
+ if (using_desc_dma(hsotg)) {
+ dwc2_gadget_start_isoc_ddma(hs_ep);
+ return;
+ }
+
if (hs_ep->interval > 1) {
u32 ctrl = dwc2_readl(hsotg->regs +
DIEPCTL(hs_ep->index));
@@ -2209,7 +2906,7 @@ static void dwc2_gadget_handle_nak(struct dwc2_hsotg_ep *hs_ep)
* Process and clear any interrupt pending for an individual endpoint
*/
static void dwc2_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx,
- int dir_in)
+ int dir_in)
{
struct dwc2_hsotg_ep *hs_ep = index_to_ep(hsotg, idx, dir_in);
u32 epint_reg = dir_in ? DIEPINT(idx) : DOEPINT(idx);
@@ -2226,7 +2923,7 @@ static void dwc2_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx,
if (!hs_ep) {
dev_err(hsotg->dev, "%s:Interrupt for unconfigured ep%d(%s)\n",
- __func__, idx, dir_in ? "in" : "out");
+ __func__, idx, dir_in ? "in" : "out");
return;
}
@@ -2237,8 +2934,15 @@ static void dwc2_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx,
if (idx == 0 && (ints & (DXEPINT_SETUP | DXEPINT_SETUP_RCVD)))
ints &= ~DXEPINT_XFERCOMPL;
- if (ints & DXEPINT_STSPHSERCVD)
- dev_dbg(hsotg->dev, "%s: StsPhseRcvd asserted\n", __func__);
+ /*
+ * Don't process XferCompl interrupt in DDMA if EP0 is still in SETUP
+ * stage and xfercomplete was generated without SETUP phase done
+ * interrupt. SW should parse received setup packet only after host's
+ * exit from setup phase of control transfer.
+ */
+ if (using_desc_dma(hsotg) && idx == 0 && !hs_ep->dir_in &&
+ hsotg->ep0_state == DWC2_EP0_SETUP && !(ints & DXEPINT_SETUP))
+ ints &= ~DXEPINT_XFERCOMPL;
if (ints & DXEPINT_XFERCOMPL) {
dev_dbg(hsotg->dev,
@@ -2246,11 +2950,17 @@ static void dwc2_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx,
__func__, dwc2_readl(hsotg->regs + epctl_reg),
dwc2_readl(hsotg->regs + epsiz_reg));
- /*
- * we get OutDone from the FIFO, so we only need to look
- * at completing IN requests here
- */
- if (dir_in) {
+ /* 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);
+ } else if (dir_in) {
+ /*
+ * We get OutDone from the FIFO, so we only
+ * need to look at completing IN requests here
+ * if operating slave mode
+ */
if (hs_ep->isochronous && hs_ep->interval > 1)
dwc2_gadget_incr_frame_num(hs_ep);
@@ -2302,9 +3012,30 @@ static void dwc2_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx,
}
}
+ if (ints & DXEPINT_STSPHSERCVD) {
+ dev_dbg(hsotg->dev, "%s: StsPhseRcvd\n", __func__);
+
+ /* Move to STATUS IN for DDMA */
+ if (using_desc_dma(hsotg))
+ dwc2_hsotg_ep0_zlp(hsotg, true);
+ }
+
if (ints & DXEPINT_BACK2BACKSETUP)
dev_dbg(hsotg->dev, "%s: B2BSetup/INEPNakEff\n", __func__);
+ 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);
+ }
+
if (dir_in && !hs_ep->isochronous) {
/* not sure if this is important, but we'll clear it anyway */
if (ints & DXEPINT_INTKNTXFEMP) {
@@ -2372,6 +3103,8 @@ static void dwc2_hsotg_irq_enumdone(struct dwc2_hsotg *hsotg)
case DSTS_ENUMSPD_LS:
hsotg->gadget.speed = USB_SPEED_LOW;
+ ep0_mps = 8;
+ ep_mps = 8;
/*
* note, we don't actually support LS in this driver at the
* moment, and the documentation seems to imply that it isn't
@@ -2390,13 +3123,15 @@ static void dwc2_hsotg_irq_enumdone(struct dwc2_hsotg *hsotg)
if (ep0_mps) {
int i;
/* Initialize ep0 for both in and out directions */
- dwc2_hsotg_set_ep_maxpacket(hsotg, 0, ep0_mps, 1);
- dwc2_hsotg_set_ep_maxpacket(hsotg, 0, ep0_mps, 0);
+ dwc2_hsotg_set_ep_maxpacket(hsotg, 0, ep0_mps, 0, 1);
+ dwc2_hsotg_set_ep_maxpacket(hsotg, 0, ep0_mps, 0, 0);
for (i = 1; i < hsotg->num_of_eps; i++) {
if (hsotg->eps_in[i])
- dwc2_hsotg_set_ep_maxpacket(hsotg, i, ep_mps, 1);
+ dwc2_hsotg_set_ep_maxpacket(hsotg, i, ep_mps,
+ 0, 1);
if (hsotg->eps_out[i])
- dwc2_hsotg_set_ep_maxpacket(hsotg, i, ep_mps, 0);
+ dwc2_hsotg_set_ep_maxpacket(hsotg, i, ep_mps,
+ 0, 0);
}
}
@@ -2423,13 +3158,13 @@ static void kill_all_requests(struct dwc2_hsotg *hsotg,
int result)
{
struct dwc2_hsotg_req *req, *treq;
- unsigned size;
+ unsigned int size;
ep->req = NULL;
list_for_each_entry_safe(req, treq, &ep->queue, queue)
dwc2_hsotg_complete_request(hsotg, ep, req,
- result);
+ result);
if (!hsotg->dedicated_fifos)
return;
@@ -2448,7 +3183,7 @@ static void kill_all_requests(struct dwc2_hsotg *hsotg,
*/
void dwc2_hsotg_disconnect(struct dwc2_hsotg *hsotg)
{
- unsigned ep;
+ unsigned int ep;
if (!hsotg->connected)
return;
@@ -2459,10 +3194,10 @@ void dwc2_hsotg_disconnect(struct dwc2_hsotg *hsotg)
for (ep = 0; ep < hsotg->num_of_eps; ep++) {
if (hsotg->eps_in[ep])
kill_all_requests(hsotg, hsotg->eps_in[ep],
- -ESHUTDOWN);
+ -ESHUTDOWN);
if (hsotg->eps_out[ep])
kill_all_requests(hsotg, hsotg->eps_out[ep],
- -ESHUTDOWN);
+ -ESHUTDOWN);
}
call_gadget(hsotg, disconnect);
@@ -2511,17 +3246,18 @@ static void dwc2_hsotg_irq_fifoempty(struct dwc2_hsotg *hsotg, bool periodic)
* Issue a soft reset to the core, and await the core finishing it.
*/
void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg,
- bool is_usb_reset)
+ bool is_usb_reset)
{
u32 intmsk;
u32 val;
u32 usbcfg;
+ u32 dcfg = 0;
/* Kill any ep0 requests as controller will be reinitialized */
kill_all_requests(hsotg, hsotg->eps_out[0], -ECONNRESET);
if (!is_usb_reset)
- if (dwc2_core_reset(hsotg))
+ if (dwc2_core_reset(hsotg, true))
return;
/*
@@ -2532,12 +3268,19 @@ 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 &= ~(GUSBCFG_TOUTCAL_MASK | GUSBCFG_PHYIF16 | GUSBCFG_SRPCAP |
- GUSBCFG_HNPCAP);
+ GUSBCFG_HNPCAP | GUSBCFG_USBTRDTIM_MASK);
- /* set the PLL on, remove the HNP/SRP and set the PHY */
- val = (hsotg->phyif == GUSBCFG_PHYIF8) ? 9 : 5;
- usbcfg |= hsotg->phyif | GUSBCFG_TOUTCAL(7) |
- (val << GUSBCFG_USBTRDTIM_SHIFT);
+ if (hsotg->params.phy_type == DWC2_PHY_TYPE_PARAM_FS &&
+ (hsotg->params.speed == DWC2_SPEED_PARAM_FULL ||
+ hsotg->params.speed == DWC2_SPEED_PARAM_LOW)) {
+ /* FS/LS Dedicated Transceiver Interface */
+ usbcfg |= GUSBCFG_PHYSEL;
+ } else {
+ /* set the PLL on, remove the HNP/SRP and set the PHY */
+ val = (hsotg->phyif == GUSBCFG_PHYIF8) ? 9 : 5;
+ usbcfg |= hsotg->phyif | GUSBCFG_TOUTCAL(7) |
+ (val << GUSBCFG_USBTRDTIM_SHIFT);
+ }
dwc2_writel(usbcfg, hsotg->regs + GUSBCFG);
dwc2_hsotg_init_fifo(hsotg);
@@ -2545,7 +3288,23 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg,
if (!is_usb_reset)
__orr32(hsotg->regs + DCTL, DCTL_SFTDISCON);
- dwc2_writel(DCFG_EPMISCNT(1) | DCFG_DEVSPD_HS, hsotg->regs + DCFG);
+ dcfg |= DCFG_EPMISCNT(1);
+
+ switch (hsotg->params.speed) {
+ case DWC2_SPEED_PARAM_LOW:
+ dcfg |= DCFG_DEVSPD_LS;
+ break;
+ case DWC2_SPEED_PARAM_FULL:
+ if (hsotg->params.phy_type == DWC2_PHY_TYPE_PARAM_FS)
+ dcfg |= DCFG_DEVSPD_FS48;
+ else
+ dcfg |= DCFG_DEVSPD_FS;
+ break;
+ default:
+ dcfg |= DCFG_DEVSPD_HS;
+ }
+
+ dwc2_writel(dcfg, hsotg->regs + DCFG);
/* Clear any pending OTG interrupts */
dwc2_writel(0xffffffff, hsotg->regs + GOTGINT);
@@ -2556,23 +3315,31 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg,
GINTSTS_GOUTNAKEFF | GINTSTS_GINNAKEFF |
GINTSTS_USBRST | GINTSTS_RESETDET |
GINTSTS_ENUMDONE | GINTSTS_OTGINT |
- GINTSTS_USBSUSP | GINTSTS_WKUPINT |
- GINTSTS_INCOMPL_SOIN | GINTSTS_INCOMPL_SOOUT;
+ GINTSTS_USBSUSP | GINTSTS_WKUPINT;
+
+ if (!using_desc_dma(hsotg))
+ intmsk |= GINTSTS_INCOMPL_SOIN | GINTSTS_INCOMPL_SOOUT;
- if (hsotg->core_params->external_id_pin_ctl <= 0)
+ if (!hsotg->params.external_id_pin_ctl)
intmsk |= GINTSTS_CONIDSTSCHNG;
dwc2_writel(intmsk, hsotg->regs + GINTMSK);
- if (using_dma(hsotg))
+ if (using_dma(hsotg)) {
dwc2_writel(GAHBCFG_GLBL_INTR_EN | GAHBCFG_DMA_EN |
(GAHBCFG_HBSTLEN_INCR4 << GAHBCFG_HBSTLEN_SHIFT),
hsotg->regs + GAHBCFG);
- else
+
+ /* Set DDMA mode support in the core if needed */
+ if (using_desc_dma(hsotg))
+ __orr32(hsotg->regs + DCFG, DCFG_DESCDMA_EN);
+
+ } else {
dwc2_writel(((hsotg->dedicated_fifos) ?
(GAHBCFG_NP_TXF_EMP_LVL |
GAHBCFG_P_TXF_EMP_LVL) : 0) |
GAHBCFG_GLBL_INTR_EN, hsotg->regs + GAHBCFG);
+ }
/*
* If INTknTXFEmpMsk is enabled, it's important to disable ep interrupts
@@ -2588,13 +3355,18 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg,
/*
* don't need XferCompl, we get that from RXFIFO in slave mode. In
- * DMA mode we may need this.
+ * DMA mode we may need this and StsPhseRcvd.
*/
- dwc2_writel((using_dma(hsotg) ? (DIEPMSK_XFERCOMPLMSK) : 0) |
+ dwc2_writel((using_dma(hsotg) ? (DIEPMSK_XFERCOMPLMSK |
+ DOEPMSK_STSPHSERCVDMSK) : 0) |
DOEPMSK_EPDISBLDMSK | DOEPMSK_AHBERRMSK |
- DOEPMSK_SETUPMSK | DOEPMSK_STSPHSERCVDMSK,
+ DOEPMSK_SETUPMSK,
hsotg->regs + DOEPMSK);
+ /* Enable BNA interrupt for DDMA */
+ if (using_desc_dma(hsotg))
+ __orr32(hsotg->regs + DOEPMSK, DOEPMSK_BNAMSK);
+
dwc2_writel(0, hsotg->regs + DAINTMSK);
dev_dbg(hsotg->dev, "EP0: DIEPCTL0=0x%08x, DOEPCTL0=0x%08x\n",
@@ -2789,7 +3561,6 @@ irq_retry:
}
if (gintsts & (GINTSTS_USBRST | GINTSTS_RESETDET)) {
-
u32 usb_status = dwc2_readl(hsotg->regs + GOTGCTL);
u32 connected = hsotg->connected;
@@ -2928,13 +3699,102 @@ irq_retry:
*/
if (gintsts & IRQ_RETRY_MASK && --retry_count > 0)
- goto irq_retry;
+ goto irq_retry;
spin_unlock(&hsotg->lock);
return IRQ_HANDLED;
}
+static int dwc2_hsotg_wait_bit_set(struct dwc2_hsotg *hs_otg, u32 reg,
+ u32 bit, u32 timeout)
+{
+ u32 i;
+
+ for (i = 0; i < timeout; i++) {
+ if (dwc2_readl(hs_otg->regs + reg) & bit)
+ return 0;
+ udelay(1);
+ }
+
+ return -ETIMEDOUT;
+}
+
+static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg,
+ struct dwc2_hsotg_ep *hs_ep)
+{
+ u32 epctrl_reg;
+ u32 epint_reg;
+
+ epctrl_reg = hs_ep->dir_in ? DIEPCTL(hs_ep->index) :
+ DOEPCTL(hs_ep->index);
+ epint_reg = hs_ep->dir_in ? DIEPINT(hs_ep->index) :
+ DOEPINT(hs_ep->index);
+
+ dev_dbg(hsotg->dev, "%s: stopping transfer on %s\n", __func__,
+ hs_ep->name);
+
+ if (hs_ep->dir_in) {
+ if (hsotg->dedicated_fifos || hs_ep->periodic) {
+ __orr32(hsotg->regs + epctrl_reg, DXEPCTL_SNAK);
+ /* Wait for Nak effect */
+ if (dwc2_hsotg_wait_bit_set(hsotg, epint_reg,
+ DXEPINT_INEPNAKEFF, 100))
+ dev_warn(hsotg->dev,
+ "%s: timeout DIEPINT.NAKEFF\n",
+ __func__);
+ } else {
+ __orr32(hsotg->regs + DCTL, DCTL_SGNPINNAK);
+ /* Wait for Nak effect */
+ if (dwc2_hsotg_wait_bit_set(hsotg, GINTSTS,
+ GINTSTS_GINNAKEFF, 100))
+ dev_warn(hsotg->dev,
+ "%s: timeout GINTSTS.GINNAKEFF\n",
+ __func__);
+ }
+ } else {
+ if (!(dwc2_readl(hsotg->regs + GINTSTS) & GINTSTS_GOUTNAKEFF))
+ __orr32(hsotg->regs + DCTL, DCTL_SGOUTNAK);
+
+ /* Wait for global nak to take effect */
+ if (dwc2_hsotg_wait_bit_set(hsotg, GINTSTS,
+ GINTSTS_GOUTNAKEFF, 100))
+ dev_warn(hsotg->dev, "%s: timeout GINTSTS.GOUTNAKEFF\n",
+ __func__);
+ }
+
+ /* Disable ep */
+ __orr32(hsotg->regs + epctrl_reg, DXEPCTL_EPDIS | DXEPCTL_SNAK);
+
+ /* Wait for ep to be disabled */
+ if (dwc2_hsotg_wait_bit_set(hsotg, epint_reg, DXEPINT_EPDISBLD, 100))
+ dev_warn(hsotg->dev,
+ "%s: timeout DOEPCTL.EPDisable\n", __func__);
+
+ /* Clear EPDISBLD interrupt */
+ __orr32(hsotg->regs + epint_reg, DXEPINT_EPDISBLD);
+
+ if (hs_ep->dir_in) {
+ unsigned short fifo_index;
+
+ if (hsotg->dedicated_fifos || hs_ep->periodic)
+ fifo_index = hs_ep->fifo_index;
+ else
+ fifo_index = 0;
+
+ /* Flush TX FIFO */
+ dwc2_flush_tx_fifo(hsotg, fifo_index);
+
+ /* Clear Global In NP NAK in Shared FIFO for non periodic ep */
+ if (!hsotg->dedicated_fifos && !hs_ep->periodic)
+ __orr32(hsotg->regs + DCTL, DCTL_CGNPINNAK);
+
+ } else {
+ /* Remove global NAKs */
+ __orr32(hsotg->regs + DCTL, DCTL_CGOUTNAK);
+ }
+}
+
/**
* dwc2_hsotg_ep_enable - enable the given endpoint
* @ep: The USB endpint to configure
@@ -2943,7 +3803,7 @@ irq_retry:
* This is called from the USB gadget code's usb_ep_enable().
*/
static int dwc2_hsotg_ep_enable(struct usb_ep *ep,
- const struct usb_endpoint_descriptor *desc)
+ const struct usb_endpoint_descriptor *desc)
{
struct dwc2_hsotg_ep *hs_ep = our_ep(ep);
struct dwc2_hsotg *hsotg = hs_ep->parent;
@@ -2952,6 +3812,7 @@ static int dwc2_hsotg_ep_enable(struct usb_ep *ep,
u32 epctrl_reg;
u32 epctrl;
u32 mps;
+ u32 mc;
u32 mask;
unsigned int dir_in;
unsigned int i, val, size;
@@ -2975,6 +3836,7 @@ static int dwc2_hsotg_ep_enable(struct usb_ep *ep,
}
mps = usb_endpoint_maxp(desc);
+ mc = usb_endpoint_maxp_mult(desc);
/* note, we handle this here instead of dwc2_hsotg_set_ep_maxpacket */
@@ -2984,6 +3846,18 @@ static int dwc2_hsotg_ep_enable(struct usb_ep *ep,
dev_dbg(hsotg->dev, "%s: read DxEPCTL=0x%08x from 0x%08x\n",
__func__, epctrl, epctrl_reg);
+ /* Allocate DMA descriptor chain for non-ctrl endpoints */
+ if (using_desc_dma(hsotg) && !hs_ep->desc_list) {
+ hs_ep->desc_list = dmam_alloc_coherent(hsotg->dev,
+ MAX_DMA_DESC_NUM_GENERIC *
+ sizeof(struct dwc2_dma_desc),
+ &hs_ep->desc_list_dma, GFP_ATOMIC);
+ if (!hs_ep->desc_list) {
+ ret = -ENOMEM;
+ goto error2;
+ }
+ }
+
spin_lock_irqsave(&hsotg->lock, flags);
epctrl &= ~(DXEPCTL_EPTYPE_MASK | DXEPCTL_MPS_MASK);
@@ -2996,7 +3870,7 @@ static int dwc2_hsotg_ep_enable(struct usb_ep *ep,
epctrl |= DXEPCTL_USBACTEP;
/* update the endpoint state */
- dwc2_hsotg_set_ep_maxpacket(hsotg, hs_ep->index, mps, dir_in);
+ dwc2_hsotg_set_ep_maxpacket(hsotg, hs_ep->index, mps, mc, dir_in);
/* default, set to non-periodic */
hs_ep->isochronous = 0;
@@ -3011,6 +3885,8 @@ static int dwc2_hsotg_ep_enable(struct usb_ep *ep,
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;
if (dir_in) {
hs_ep->periodic = 1;
mask = dwc2_readl(hsotg->regs + DIEPMSK);
@@ -3049,12 +3925,13 @@ static int dwc2_hsotg_ep_enable(struct usb_ep *ep,
if (dir_in && hsotg->dedicated_fifos) {
u32 fifo_index = 0;
u32 fifo_size = UINT_MAX;
- size = hs_ep->ep.maxpacket*hs_ep->mc;
+
+ size = hs_ep->ep.maxpacket * hs_ep->mc;
for (i = 1; i < hsotg->num_of_eps; ++i) {
- if (hsotg->fifo_map & (1<<i))
+ if (hsotg->fifo_map & (1 << i))
continue;
val = dwc2_readl(hsotg->regs + DPTXFSIZN(i));
- val = (val >> FIFOSIZE_DEPTH_SHIFT)*4;
+ val = (val >> FIFOSIZE_DEPTH_SHIFT) * 4;
if (val < size)
continue;
/* Search for smallest acceptable fifo */
@@ -3067,7 +3944,7 @@ static int dwc2_hsotg_ep_enable(struct usb_ep *ep,
dev_err(hsotg->dev,
"%s: No suitable fifo found\n", __func__);
ret = -ENOMEM;
- goto error;
+ goto error1;
}
hsotg->fifo_map |= 1 << fifo_index;
epctrl |= DXEPCTL_TXFNUM(fifo_index);
@@ -3089,8 +3966,17 @@ static int dwc2_hsotg_ep_enable(struct usb_ep *ep,
/* enable the endpoint interrupt */
dwc2_hsotg_ctrl_epint(hsotg, index, dir_in, 1);
-error:
+error1:
spin_unlock_irqrestore(&hsotg->lock, flags);
+
+error2:
+ if (ret && using_desc_dma(hsotg) && hs_ep->desc_list) {
+ dmam_free_coherent(hsotg->dev, MAX_DMA_DESC_NUM_GENERIC *
+ sizeof(struct dwc2_dma_desc),
+ hs_ep->desc_list, hs_ep->desc_list_dma);
+ hs_ep->desc_list = NULL;
+ }
+
return ret;
}
@@ -3120,6 +4006,10 @@ static int dwc2_hsotg_ep_disable(struct usb_ep *ep)
spin_lock_irqsave(&hsotg->lock, flags);
ctrl = dwc2_readl(hsotg->regs + epctrl_reg);
+
+ if (ctrl & DXEPCTL_EPENA)
+ dwc2_hsotg_ep_stop_xfr(hsotg, hs_ep);
+
ctrl &= ~DXEPCTL_EPENA;
ctrl &= ~DXEPCTL_USBACTEP;
ctrl |= DXEPCTL_SNAK;
@@ -3158,77 +4048,6 @@ static bool on_list(struct dwc2_hsotg_ep *ep, struct dwc2_hsotg_req *test)
return false;
}
-static int dwc2_hsotg_wait_bit_set(struct dwc2_hsotg *hs_otg, u32 reg,
- u32 bit, u32 timeout)
-{
- u32 i;
-
- for (i = 0; i < timeout; i++) {
- if (dwc2_readl(hs_otg->regs + reg) & bit)
- return 0;
- udelay(1);
- }
-
- return -ETIMEDOUT;
-}
-
-static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg,
- struct dwc2_hsotg_ep *hs_ep)
-{
- u32 epctrl_reg;
- u32 epint_reg;
-
- epctrl_reg = hs_ep->dir_in ? DIEPCTL(hs_ep->index) :
- DOEPCTL(hs_ep->index);
- epint_reg = hs_ep->dir_in ? DIEPINT(hs_ep->index) :
- DOEPINT(hs_ep->index);
-
- dev_dbg(hsotg->dev, "%s: stopping transfer on %s\n", __func__,
- hs_ep->name);
- if (hs_ep->dir_in) {
- __orr32(hsotg->regs + epctrl_reg, DXEPCTL_SNAK);
- /* Wait for Nak effect */
- if (dwc2_hsotg_wait_bit_set(hsotg, epint_reg,
- DXEPINT_INEPNAKEFF, 100))
- dev_warn(hsotg->dev,
- "%s: timeout DIEPINT.NAKEFF\n", __func__);
- } else {
- if (!(dwc2_readl(hsotg->regs + GINTSTS) & GINTSTS_GOUTNAKEFF))
- __orr32(hsotg->regs + DCTL, DCTL_SGOUTNAK);
-
- /* Wait for global nak to take effect */
- if (dwc2_hsotg_wait_bit_set(hsotg, GINTSTS,
- GINTSTS_GOUTNAKEFF, 100))
- dev_warn(hsotg->dev,
- "%s: timeout GINTSTS.GOUTNAKEFF\n", __func__);
- }
-
- /* Disable ep */
- __orr32(hsotg->regs + epctrl_reg, DXEPCTL_EPDIS | DXEPCTL_SNAK);
-
- /* Wait for ep to be disabled */
- if (dwc2_hsotg_wait_bit_set(hsotg, epint_reg, DXEPINT_EPDISBLD, 100))
- dev_warn(hsotg->dev,
- "%s: timeout DOEPCTL.EPDisable\n", __func__);
-
- if (hs_ep->dir_in) {
- if (hsotg->dedicated_fifos) {
- dwc2_writel(GRSTCTL_TXFNUM(hs_ep->fifo_index) |
- GRSTCTL_TXFFLSH, hsotg->regs + GRSTCTL);
- /* Wait for fifo flush */
- if (dwc2_hsotg_wait_bit_set(hsotg, GRSTCTL,
- GRSTCTL_TXFFLSH, 100))
- dev_warn(hsotg->dev,
- "%s: timeout flushing fifos\n",
- __func__);
- }
- /* TODO: Flush shared tx fifo */
- } else {
- /* Remove global NAKs */
- __bic32(hsotg->regs + DCTL, DCTL_SGOUTNAK);
- }
-}
-
/**
* dwc2_hsotg_ep_dequeue - dequeue given endpoint
* @ep: The endpoint to dequeue.
@@ -3313,23 +4132,22 @@ static int dwc2_hsotg_ep_sethalt(struct usb_ep *ep, int value, bool now)
epctl &= ~DXEPCTL_STALL;
xfertype = epctl & DXEPCTL_EPTYPE_MASK;
if (xfertype == DXEPCTL_EPTYPE_BULK ||
- xfertype == DXEPCTL_EPTYPE_INTERRUPT)
- epctl |= DXEPCTL_SETD0PID;
+ xfertype == DXEPCTL_EPTYPE_INTERRUPT)
+ epctl |= DXEPCTL_SETD0PID;
}
dwc2_writel(epctl, hs->regs + epreg);
} else {
-
epreg = DOEPCTL(index);
epctl = dwc2_readl(hs->regs + epreg);
- if (value)
+ if (value) {
epctl |= DXEPCTL_STALL;
- else {
+ } else {
epctl &= ~DXEPCTL_STALL;
xfertype = epctl & DXEPCTL_EPTYPE_MASK;
if (xfertype == DXEPCTL_EPTYPE_BULK ||
- xfertype == DXEPCTL_EPTYPE_INTERRUPT)
- epctl |= DXEPCTL_SETD0PID;
+ xfertype == DXEPCTL_EPTYPE_INTERRUPT)
+ epctl |= DXEPCTL_SETD0PID;
}
dwc2_writel(epctl, hs->regs + epreg);
}
@@ -3370,7 +4188,7 @@ static struct usb_ep_ops dwc2_hsotg_ep_ops = {
};
/**
- * dwc2_hsotg_init - initalize the usb core
+ * dwc2_hsotg_init - initialize the usb core
* @hsotg: The driver state
*/
static void dwc2_hsotg_init(struct dwc2_hsotg *hsotg)
@@ -3403,7 +4221,7 @@ static void dwc2_hsotg_init(struct dwc2_hsotg *hsotg)
/* keep other bits untouched (so e.g. forced modes are not lost) */
usbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
usbcfg &= ~(GUSBCFG_TOUTCAL_MASK | GUSBCFG_PHYIF16 | GUSBCFG_SRPCAP |
- GUSBCFG_HNPCAP);
+ GUSBCFG_HNPCAP | GUSBCFG_USBTRDTIM_MASK);
/* set the PLL on, remove the HNP/SRP and set the PHY */
trdtim = (hsotg->phyif == GUSBCFG_PHYIF8) ? 9 : 5;
@@ -3424,7 +4242,7 @@ static void dwc2_hsotg_init(struct dwc2_hsotg *hsotg)
* to work.
*/
static int dwc2_hsotg_udc_start(struct usb_gadget *gadget,
- struct usb_gadget_driver *driver)
+ struct usb_gadget_driver *driver)
{
struct dwc2_hsotg *hsotg = to_hsotg(gadget);
unsigned long flags;
@@ -3547,7 +4365,7 @@ static int dwc2_hsotg_pullup(struct usb_gadget *gadget, int is_on)
unsigned long flags = 0;
dev_dbg(hsotg->dev, "%s: is_on: %d op_state: %d\n", __func__, is_on,
- hsotg->op_state);
+ hsotg->op_state);
/* Don't modify pullup state while in host mode */
if (hsotg->op_state != OTG_STATE_B_PERIPHERAL) {
@@ -3609,7 +4427,7 @@ static int dwc2_hsotg_vbus_session(struct usb_gadget *gadget, int is_active)
*
* Report how much power the device may consume to the phy.
*/
-static int dwc2_hsotg_vbus_draw(struct usb_gadget *gadget, unsigned mA)
+static int dwc2_hsotg_vbus_draw(struct usb_gadget *gadget, unsigned int mA)
{
struct dwc2_hsotg *hsotg = to_hsotg(gadget);
@@ -3638,7 +4456,7 @@ static const struct usb_gadget_ops dwc2_hsotg_gadget_ops = {
* direction information and other state that may be required.
*/
static void dwc2_hsotg_initep(struct dwc2_hsotg *hsotg,
- struct dwc2_hsotg_ep *hs_ep,
+ struct dwc2_hsotg_ep *hs_ep,
int epnum,
bool dir_in)
{
@@ -3665,14 +4483,21 @@ static void dwc2_hsotg_initep(struct dwc2_hsotg *hsotg,
hs_ep->parent = hsotg;
hs_ep->ep.name = hs_ep->name;
- usb_ep_set_maxpacket_limit(&hs_ep->ep, epnum ? 1024 : EP0_MPS_LIMIT);
+
+ if (hsotg->params.speed == DWC2_SPEED_PARAM_LOW)
+ usb_ep_set_maxpacket_limit(&hs_ep->ep, 8);
+ else
+ usb_ep_set_maxpacket_limit(&hs_ep->ep,
+ epnum ? 1024 : EP0_MPS_LIMIT);
hs_ep->ep.ops = &dwc2_hsotg_ep_ops;
if (epnum == 0) {
hs_ep->ep.caps.type_control = true;
} else {
- hs_ep->ep.caps.type_iso = true;
- hs_ep->ep.caps.type_bulk = true;
+ if (hsotg->params.speed != DWC2_SPEED_PARAM_LOW) {
+ hs_ep->ep.caps.type_iso = true;
+ hs_ep->ep.caps.type_bulk = true;
+ }
hs_ep->ep.caps.type_int = true;
}
@@ -3688,6 +4513,7 @@ static void dwc2_hsotg_initep(struct dwc2_hsotg *hsotg,
if (using_dma(hsotg)) {
u32 next = DXEPCTL_NEXTEP((epnum + 1) % 15);
+
if (dir_in)
dwc2_writel(next, hsotg->regs + DIEPCTL(epnum));
else
@@ -3714,8 +4540,9 @@ static int dwc2_hsotg_hw_cfg(struct dwc2_hsotg *hsotg)
/* Add ep0 */
hsotg->num_of_eps++;
- hsotg->eps_in[0] = devm_kzalloc(hsotg->dev, sizeof(struct dwc2_hsotg_ep),
- GFP_KERNEL);
+ hsotg->eps_in[0] = devm_kzalloc(hsotg->dev,
+ sizeof(struct dwc2_hsotg_ep),
+ GFP_KERNEL);
if (!hsotg->eps_in[0])
return -ENOMEM;
/* Same dwc2_hsotg_ep is used in both directions for ep0 */
@@ -3794,7 +4621,6 @@ static void dwc2_hsotg_dump(struct dwc2_hsotg *hsotg)
idx, dwc2_readl(regs + DOEPCTL(idx)),
dwc2_readl(regs + DOEPTSIZ(idx)),
dwc2_readl(regs + DOEPDMA(idx)));
-
}
dev_info(dev, "DVBUSDIS=0x%08x, DVBUSPULSE=%08x\n",
@@ -3802,51 +4628,6 @@ static void dwc2_hsotg_dump(struct dwc2_hsotg *hsotg)
#endif
}
-#ifdef CONFIG_OF
-static void dwc2_hsotg_of_probe(struct dwc2_hsotg *hsotg)
-{
- struct device_node *np = hsotg->dev->of_node;
- u32 len = 0;
- u32 i = 0;
-
- /* Enable dma if requested in device tree */
- hsotg->g_using_dma = of_property_read_bool(np, "g-use-dma");
-
- /*
- * Register TX periodic fifo size per endpoint.
- * EP0 is excluded since it has no fifo configuration.
- */
- if (!of_find_property(np, "g-tx-fifo-size", &len))
- goto rx_fifo;
-
- len /= sizeof(u32);
-
- /* Read tx fifo sizes other than ep0 */
- if (of_property_read_u32_array(np, "g-tx-fifo-size",
- &hsotg->g_tx_fifo_sz[1], len))
- goto rx_fifo;
-
- /* Add ep0 */
- len++;
-
- /* Make remaining TX fifos unavailable */
- if (len < MAX_EPS_CHANNELS) {
- for (i = len; i < MAX_EPS_CHANNELS; i++)
- hsotg->g_tx_fifo_sz[i] = 0;
- }
-
-rx_fifo:
- /* Register RX fifo size */
- of_property_read_u32(np, "g-rx-fifo-size", &hsotg->g_rx_fifo_sz);
-
- /* Register NPTX fifo size */
- of_property_read_u32(np, "g-np-tx-fifo-size",
- &hsotg->g_np_g_tx_fifo_sz);
-}
-#else
-static inline void dwc2_hsotg_of_probe(struct dwc2_hsotg *hsotg) { }
-#endif
-
/**
* dwc2_gadget_init - init function for gadget
* @dwc2: The data structure for the DWC2 driver.
@@ -3857,33 +4638,11 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
struct device *dev = hsotg->dev;
int epnum;
int ret;
- int i;
- u32 p_tx_fifo[] = DWC2_G_P_LEGACY_TX_FIFO_SIZE;
-
- /* Initialize to legacy fifo configuration values */
- hsotg->g_rx_fifo_sz = 2048;
- hsotg->g_np_g_tx_fifo_sz = 1024;
- memcpy(&hsotg->g_tx_fifo_sz[1], p_tx_fifo, sizeof(p_tx_fifo));
- /* Device tree specific probe */
- dwc2_hsotg_of_probe(hsotg);
-
- /* Check against largest possible value. */
- if (hsotg->g_np_g_tx_fifo_sz >
- hsotg->hw_params.dev_nperio_tx_fifo_size) {
- dev_warn(dev, "Specified GNPTXFDEP=%d > %d\n",
- hsotg->g_np_g_tx_fifo_sz,
- hsotg->hw_params.dev_nperio_tx_fifo_size);
- hsotg->g_np_g_tx_fifo_sz =
- hsotg->hw_params.dev_nperio_tx_fifo_size;
- }
/* Dump fifo information */
dev_dbg(dev, "NonPeriodic TXFIFO size: %d\n",
- hsotg->g_np_g_tx_fifo_sz);
- dev_dbg(dev, "RXFIFO size: %d\n", hsotg->g_rx_fifo_sz);
- for (i = 0; i < MAX_EPS_CHANNELS; i++)
- dev_dbg(dev, "Periodic TXFIFO%2d size: %d\n", i,
- hsotg->g_tx_fifo_sz[i]);
+ hsotg->params.g_np_tx_fifo_size);
+ dev_dbg(dev, "RXFIFO size: %d\n", hsotg->params.g_rx_fifo_size);
hsotg->gadget.max_speed = USB_SPEED_HIGH;
hsotg->gadget.ops = &dwc2_hsotg_gadget_ops;
@@ -3909,8 +4668,14 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
if (!hsotg->ep0_buff)
return -ENOMEM;
+ if (using_desc_dma(hsotg)) {
+ ret = dwc2_gadget_alloc_ctrl_desc_chains(hsotg);
+ if (ret < 0)
+ return ret;
+ }
+
ret = devm_request_irq(hsotg->dev, irq, dwc2_hsotg_irq, IRQF_SHARED,
- dev_name(hsotg->dev), hsotg);
+ dev_name(hsotg->dev), hsotg);
if (ret < 0) {
dev_err(dev, "cannot claim IRQ for gadget\n");
return ret;
@@ -3941,10 +4706,10 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
for (epnum = 0; epnum < hsotg->num_of_eps; epnum++) {
if (hsotg->eps_in[epnum])
dwc2_hsotg_initep(hsotg, hsotg->eps_in[epnum],
- epnum, 1);
+ epnum, 1);
if (hsotg->eps_out[epnum])
dwc2_hsotg_initep(hsotg, hsotg->eps_out[epnum],
- epnum, 0);
+ epnum, 0);
}
ret = usb_add_gadget_udc(dev, &hsotg->gadget);
diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index df5a06578005..a73722e27d07 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -42,6 +42,7 @@
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
+#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/delay.h>
#include <linux/io.h>
@@ -54,6 +55,8 @@
#include "core.h"
#include "hcd.h"
+static void dwc2_port_resume(struct dwc2_hsotg *hsotg);
+
/*
* =========================================================================
* Host Core Layer Functions
@@ -79,9 +82,9 @@ static void dwc2_enable_common_interrupts(struct dwc2_hsotg *hsotg)
/* Enable the interrupts in the GINTMSK */
intmsk = GINTSTS_MODEMIS | GINTSTS_OTGINT;
- if (hsotg->core_params->dma_enable <= 0)
+ if (!hsotg->params.host_dma)
intmsk |= GINTSTS_RXFLVL;
- if (hsotg->core_params->external_id_pin_ctl <= 0)
+ if (!hsotg->params.external_id_pin_ctl)
intmsk |= GINTSTS_CONIDSTSCHNG;
intmsk |= GINTSTS_WKUPINT | GINTSTS_USBSUSP |
@@ -100,8 +103,8 @@ static void dwc2_init_fs_ls_pclk_sel(struct dwc2_hsotg *hsotg)
if ((hsotg->hw_params.hs_phy_type == GHWCFG2_HS_PHY_TYPE_ULPI &&
hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED &&
- hsotg->core_params->ulpi_fs_ls > 0) ||
- hsotg->core_params->phy_type == DWC2_PHY_TYPE_PARAM_FS) {
+ hsotg->params.ulpi_fs_ls) ||
+ hsotg->params.phy_type == DWC2_PHY_TYPE_PARAM_FS) {
/* Full speed PHY */
val = HCFG_FSLSPCLKSEL_48_MHZ;
} else {
@@ -152,7 +155,7 @@ static int dwc2_fs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
if (dwc2_is_host_mode(hsotg))
dwc2_init_fs_ls_pclk_sel(hsotg);
- if (hsotg->core_params->i2c_enable > 0) {
+ if (hsotg->params.i2c_enable) {
dev_dbg(hsotg->dev, "FS PHY enabling I2C\n");
/* Program GUSBCFG.OtgUtmiFsSel to I2C */
@@ -189,20 +192,20 @@ static int dwc2_hs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
* so only program the first time. Do a soft reset immediately after
* setting phyif.
*/
- switch (hsotg->core_params->phy_type) {
+ switch (hsotg->params.phy_type) {
case DWC2_PHY_TYPE_PARAM_ULPI:
/* ULPI interface */
dev_dbg(hsotg->dev, "HS ULPI PHY selected\n");
usbcfg |= GUSBCFG_ULPI_UTMI_SEL;
usbcfg &= ~(GUSBCFG_PHYIF16 | GUSBCFG_DDRSEL);
- if (hsotg->core_params->phy_ulpi_ddr > 0)
+ if (hsotg->params.phy_ulpi_ddr)
usbcfg |= GUSBCFG_DDRSEL;
break;
case DWC2_PHY_TYPE_PARAM_UTMI:
/* UTMI+ interface */
dev_dbg(hsotg->dev, "HS UTMI+ PHY selected\n");
usbcfg &= ~(GUSBCFG_ULPI_UTMI_SEL | GUSBCFG_PHYIF16);
- if (hsotg->core_params->phy_utmi_width == 16)
+ if (hsotg->params.phy_utmi_width == 16)
usbcfg |= GUSBCFG_PHYIF16;
break;
default:
@@ -230,9 +233,10 @@ static int dwc2_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
u32 usbcfg;
int retval = 0;
- if (hsotg->core_params->speed == DWC2_SPEED_PARAM_FULL &&
- hsotg->core_params->phy_type == DWC2_PHY_TYPE_PARAM_FS) {
- /* If FS mode with FS PHY */
+ if ((hsotg->params.speed == DWC2_SPEED_PARAM_FULL ||
+ hsotg->params.speed == DWC2_SPEED_PARAM_LOW) &&
+ hsotg->params.phy_type == DWC2_PHY_TYPE_PARAM_FS) {
+ /* If FS/LS mode with FS/LS PHY */
retval = dwc2_fs_phy_init(hsotg, select_phy);
if (retval)
return retval;
@@ -245,7 +249,7 @@ static int dwc2_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
if (hsotg->hw_params.hs_phy_type == GHWCFG2_HS_PHY_TYPE_ULPI &&
hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED &&
- hsotg->core_params->ulpi_fs_ls > 0) {
+ hsotg->params.ulpi_fs_ls) {
dev_dbg(hsotg->dev, "Setting ULPI FSLS\n");
usbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
usbcfg |= GUSBCFG_ULPI_FS_LS;
@@ -272,9 +276,9 @@ static int dwc2_gahbcfg_init(struct dwc2_hsotg *hsotg)
case GHWCFG2_INT_DMA_ARCH:
dev_dbg(hsotg->dev, "Internal DMA Mode\n");
- if (hsotg->core_params->ahbcfg != -1) {
+ if (hsotg->params.ahbcfg != -1) {
ahbcfg &= GAHBCFG_CTRL_MASK;
- ahbcfg |= hsotg->core_params->ahbcfg &
+ ahbcfg |= hsotg->params.ahbcfg &
~GAHBCFG_CTRL_MASK;
}
break;
@@ -285,21 +289,21 @@ static int dwc2_gahbcfg_init(struct dwc2_hsotg *hsotg)
break;
}
- dev_dbg(hsotg->dev, "dma_enable:%d dma_desc_enable:%d\n",
- hsotg->core_params->dma_enable,
- hsotg->core_params->dma_desc_enable);
+ dev_dbg(hsotg->dev, "host_dma:%d dma_desc_enable:%d\n",
+ hsotg->params.host_dma,
+ hsotg->params.dma_desc_enable);
- if (hsotg->core_params->dma_enable > 0) {
- if (hsotg->core_params->dma_desc_enable > 0)
+ if (hsotg->params.host_dma) {
+ if (hsotg->params.dma_desc_enable)
dev_dbg(hsotg->dev, "Using Descriptor DMA mode\n");
else
dev_dbg(hsotg->dev, "Using Buffer DMA mode\n");
} else {
dev_dbg(hsotg->dev, "Using Slave mode\n");
- hsotg->core_params->dma_desc_enable = 0;
+ hsotg->params.dma_desc_enable = false;
}
- if (hsotg->core_params->dma_enable > 0)
+ if (hsotg->params.host_dma)
ahbcfg |= GAHBCFG_DMA_EN;
dwc2_writel(ahbcfg, hsotg->regs + GAHBCFG);
@@ -316,10 +320,10 @@ static void dwc2_gusbcfg_init(struct dwc2_hsotg *hsotg)
switch (hsotg->hw_params.op_mode) {
case GHWCFG2_OP_MODE_HNP_SRP_CAPABLE:
- if (hsotg->core_params->otg_cap ==
+ if (hsotg->params.otg_cap ==
DWC2_CAP_PARAM_HNP_SRP_CAPABLE)
usbcfg |= GUSBCFG_HNPCAP;
- if (hsotg->core_params->otg_cap !=
+ if (hsotg->params.otg_cap !=
DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE)
usbcfg |= GUSBCFG_SRPCAP;
break;
@@ -327,7 +331,7 @@ static void dwc2_gusbcfg_init(struct dwc2_hsotg *hsotg)
case GHWCFG2_OP_MODE_SRP_ONLY_CAPABLE:
case GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE:
case GHWCFG2_OP_MODE_SRP_CAPABLE_HOST:
- if (hsotg->core_params->otg_cap !=
+ if (hsotg->params.otg_cap !=
DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE)
usbcfg |= GUSBCFG_SRPCAP;
break;
@@ -390,7 +394,7 @@ static void dwc2_disable_host_interrupts(struct dwc2_hsotg *hsotg)
*/
static void dwc2_calculate_dynamic_fifo(struct dwc2_hsotg *hsotg)
{
- struct dwc2_core_params *params = hsotg->core_params;
+ struct dwc2_core_params *params = &hsotg->params;
struct dwc2_hw_params *hw = &hsotg->hw_params;
u32 rxfsiz, nptxfsiz, ptxfsiz, total_fifo_size;
@@ -449,7 +453,7 @@ static void dwc2_calculate_dynamic_fifo(struct dwc2_hsotg *hsotg)
static void dwc2_config_fifos(struct dwc2_hsotg *hsotg)
{
- struct dwc2_core_params *params = hsotg->core_params;
+ struct dwc2_core_params *params = &hsotg->params;
u32 nptxfsiz, hptxfsiz, dfifocfg, grxfsiz;
if (!params->enable_dynamic_fifo)
@@ -490,9 +494,10 @@ static void dwc2_config_fifos(struct dwc2_hsotg *hsotg)
dev_dbg(hsotg->dev, "new hptxfsiz=%08x\n",
dwc2_readl(hsotg->regs + HPTXFSIZ));
- if (hsotg->core_params->en_multiple_tx_fifo > 0 &&
- hsotg->hw_params.snpsid <= DWC2_CORE_REV_2_94a) {
+ if (hsotg->params.en_multiple_tx_fifo &&
+ hsotg->hw_params.snpsid >= DWC2_CORE_REV_2_91a) {
/*
+ * This feature was implemented in 2.91a version
* Global DFIFOCFG calculation for Host mode -
* include RxFIFO, NPTXFIFO and HPTXFIFO
*/
@@ -598,7 +603,7 @@ static void dwc2_dump_channel_info(struct dwc2_hsotg *hsotg,
struct dwc2_host_chan *chan)
{
#ifdef VERBOSE_DEBUG
- int num_channels = hsotg->core_params->host_channels;
+ int num_channels = hsotg->params.host_channels;
struct dwc2_qh *qh;
u32 hcchar;
u32 hcsplt;
@@ -648,6 +653,35 @@ static void dwc2_dump_channel_info(struct dwc2_hsotg *hsotg,
#endif /* VERBOSE_DEBUG */
}
+static int _dwc2_hcd_start(struct usb_hcd *hcd);
+
+static void dwc2_host_start(struct dwc2_hsotg *hsotg)
+{
+ struct usb_hcd *hcd = dwc2_hsotg_to_hcd(hsotg);
+
+ hcd->self.is_b_host = dwc2_hcd_is_b_host(hsotg);
+ _dwc2_hcd_start(hcd);
+}
+
+static void dwc2_host_disconnect(struct dwc2_hsotg *hsotg)
+{
+ struct usb_hcd *hcd = dwc2_hsotg_to_hcd(hsotg);
+
+ hcd->self.is_b_host = 0;
+}
+
+static void dwc2_host_hub_info(struct dwc2_hsotg *hsotg, void *context,
+ int *hub_addr, int *hub_port)
+{
+ struct urb *urb = context;
+
+ if (urb->dev->tt)
+ *hub_addr = urb->dev->tt->hub->devnum;
+ else
+ *hub_addr = 0;
+ *hub_port = urb->dev->ttport;
+}
+
/*
* =========================================================================
* Low Level Host Channel Access Functions
@@ -741,7 +775,7 @@ static void dwc2_hc_enable_dma_ints(struct dwc2_hsotg *hsotg,
* For Descriptor DMA mode core halts the channel on AHB error.
* Interrupt is not required.
*/
- if (hsotg->core_params->dma_desc_enable <= 0) {
+ if (!hsotg->params.dma_desc_enable) {
if (dbg_hc(chan))
dev_vdbg(hsotg->dev, "desc DMA disabled\n");
hcintmsk |= HCINTMSK_AHBERR;
@@ -774,7 +808,7 @@ static void dwc2_hc_enable_ints(struct dwc2_hsotg *hsotg,
{
u32 intmsk;
- if (hsotg->core_params->dma_enable > 0) {
+ if (hsotg->params.host_dma) {
if (dbg_hc(chan))
dev_vdbg(hsotg->dev, "DMA enabled\n");
dwc2_hc_enable_dma_ints(hsotg, chan);
@@ -994,7 +1028,7 @@ void dwc2_hc_halt(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan,
/* No need to set the bit in DDMA for disabling the channel */
/* TODO check it everywhere channel is disabled */
- if (hsotg->core_params->dma_desc_enable <= 0) {
+ if (!hsotg->params.dma_desc_enable) {
if (dbg_hc(chan))
dev_vdbg(hsotg->dev, "desc DMA disabled\n");
hcchar |= HCCHAR_CHENA;
@@ -1004,7 +1038,7 @@ void dwc2_hc_halt(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan,
}
hcchar |= HCCHAR_CHDIS;
- if (hsotg->core_params->dma_enable <= 0) {
+ if (!hsotg->params.host_dma) {
if (dbg_hc(chan))
dev_vdbg(hsotg->dev, "DMA not enabled\n");
hcchar |= HCCHAR_CHENA;
@@ -1143,7 +1177,7 @@ static void dwc2_hc_set_even_odd_frame(struct dwc2_hsotg *hsotg,
fifo_space = (dwc2_readl(hsotg->regs + HPTXSTS) &
TXSTS_FSPCAVAIL_MASK) >> TXSTS_FSPCAVAIL_SHIFT;
bytes_in_fifo = sizeof(u32) *
- (hsotg->core_params->host_perio_tx_fifo_size -
+ (hsotg->params.host_perio_tx_fifo_size -
fifo_space);
/*
@@ -1339,8 +1373,8 @@ static void dwc2_hc_do_ping(struct dwc2_hsotg *hsotg,
static void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg,
struct dwc2_host_chan *chan)
{
- u32 max_hc_xfer_size = hsotg->core_params->max_transfer_size;
- u16 max_hc_pkt_count = hsotg->core_params->max_packet_count;
+ u32 max_hc_xfer_size = hsotg->params.max_transfer_size;
+ u16 max_hc_pkt_count = hsotg->params.max_packet_count;
u32 hcchar;
u32 hctsiz = 0;
u16 num_packets;
@@ -1350,7 +1384,7 @@ static void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg,
dev_vdbg(hsotg->dev, "%s()\n", __func__);
if (chan->do_ping) {
- if (hsotg->core_params->dma_enable <= 0) {
+ if (!hsotg->params.host_dma) {
if (dbg_hc(chan))
dev_vdbg(hsotg->dev, "ping, no DMA\n");
dwc2_hc_do_ping(hsotg, chan);
@@ -1478,7 +1512,7 @@ static void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg,
TSIZ_SC_MC_PID_SHIFT);
}
- if (hsotg->core_params->dma_enable > 0) {
+ if (hsotg->params.host_dma) {
dwc2_writel((u32)chan->xfer_dma,
hsotg->regs + HCDMA(chan->hc_num));
if (dbg_hc(chan))
@@ -1521,7 +1555,7 @@ static void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg,
chan->xfer_started = 1;
chan->requests++;
- if (hsotg->core_params->dma_enable <= 0 &&
+ if (!hsotg->params.host_dma &&
!chan->ep_is_in && chan->xfer_len > 0)
/* Load OUT packet into the appropriate Tx FIFO */
dwc2_hc_write_packet(hsotg, chan);
@@ -1799,12 +1833,12 @@ void dwc2_hcd_start(struct dwc2_hsotg *hsotg)
/* Must be called with interrupt disabled and spinlock held */
static void dwc2_hcd_cleanup_channels(struct dwc2_hsotg *hsotg)
{
- int num_channels = hsotg->core_params->host_channels;
+ int num_channels = hsotg->params.host_channels;
struct dwc2_host_chan *channel;
u32 hcchar;
int i;
- if (hsotg->core_params->dma_enable <= 0) {
+ if (!hsotg->params.host_dma) {
/* Flush out any channel requests in slave mode */
for (i = 0; i < num_channels; i++) {
channel = hsotg->hc_ptr_array[i];
@@ -1840,9 +1874,9 @@ static void dwc2_hcd_cleanup_channels(struct dwc2_hsotg *hsotg)
channel->qh = NULL;
}
/* All channels have been freed, mark them available */
- if (hsotg->core_params->uframe_sched > 0) {
+ if (hsotg->params.uframe_sched) {
hsotg->available_host_channels =
- hsotg->core_params->host_channels;
+ hsotg->params.host_channels;
} else {
hsotg->non_periodic_channels = 0;
hsotg->periodic_channels = 0;
@@ -2077,7 +2111,7 @@ static int dwc2_hcd_urb_dequeue(struct dwc2_hsotg *hsotg,
* Free the QTD and clean up the associated QH. Leave the QH in the
* schedule if it has any remaining QTDs.
*/
- if (hsotg->core_params->dma_desc_enable <= 0) {
+ if (!hsotg->params.dma_desc_enable) {
u8 in_process = urb_qtd->in_process;
dwc2_hcd_qtd_unlink_and_free(hsotg, urb_qtd, qh);
@@ -2120,7 +2154,7 @@ static int dwc2_hcd_endpoint_disable(struct dwc2_hsotg *hsotg,
}
spin_unlock_irqrestore(&hsotg->lock, flags);
- usleep_range(20000, 40000);
+ msleep(20);
spin_lock_irqsave(&hsotg->lock, flags);
qh = ep->hcpriv;
if (!qh) {
@@ -2185,13 +2219,12 @@ static int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup)
/* Set ULPI External VBUS bit if needed */
usbcfg &= ~GUSBCFG_ULPI_EXT_VBUS_DRV;
- if (hsotg->core_params->phy_ulpi_ext_vbus ==
- DWC2_PHY_ULPI_EXTERNAL_VBUS)
+ if (hsotg->params.phy_ulpi_ext_vbus)
usbcfg |= GUSBCFG_ULPI_EXT_VBUS_DRV;
/* Set external TS Dline pulsing bit if needed */
usbcfg &= ~GUSBCFG_TERMSELDLPULSE;
- if (hsotg->core_params->ts_dline > 0)
+ if (hsotg->params.ts_dline)
usbcfg |= GUSBCFG_TERMSELDLPULSE;
dwc2_writel(usbcfg, hsotg->regs + GUSBCFG);
@@ -2230,10 +2263,7 @@ static int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup)
/* Program the GOTGCTL register */
otgctl = dwc2_readl(hsotg->regs + GOTGCTL);
otgctl &= ~GOTGCTL_OTGVER;
- if (hsotg->core_params->otg_ver > 0)
- otgctl |= GOTGCTL_OTGVER;
dwc2_writel(otgctl, hsotg->regs + GOTGCTL);
- dev_dbg(hsotg->dev, "OTG VER PARAM: %d\n", hsotg->core_params->otg_ver);
/* Clear the SRP success bit for FS-I2c */
hsotg->srp_success = 0;
@@ -2277,7 +2307,8 @@ static void dwc2_core_host_init(struct dwc2_hsotg *hsotg)
/* Initialize Host Configuration Register */
dwc2_init_fs_ls_pclk_sel(hsotg);
- if (hsotg->core_params->speed == DWC2_SPEED_PARAM_FULL) {
+ if (hsotg->params.speed == DWC2_SPEED_PARAM_FULL ||
+ hsotg->params.speed == DWC2_SPEED_PARAM_LOW) {
hcfg = dwc2_readl(hsotg->regs + HCFG);
hcfg |= HCFG_FSLSSUPP;
dwc2_writel(hcfg, hsotg->regs + HCFG);
@@ -2288,13 +2319,13 @@ static void dwc2_core_host_init(struct dwc2_hsotg *hsotg)
* runtime. This bit needs to be programmed during initial configuration
* and its value must not be changed during runtime.
*/
- if (hsotg->core_params->reload_ctl > 0) {
+ if (hsotg->params.reload_ctl) {
hfir = dwc2_readl(hsotg->regs + HFIR);
hfir |= HFIR_RLDCTRL;
dwc2_writel(hfir, hsotg->regs + HFIR);
}
- if (hsotg->core_params->dma_desc_enable > 0) {
+ if (hsotg->params.dma_desc_enable) {
u32 op_mode = hsotg->hw_params.op_mode;
if (hsotg->hw_params.snpsid < DWC2_CORE_REV_2_90a ||
@@ -2306,7 +2337,7 @@ static void dwc2_core_host_init(struct dwc2_hsotg *hsotg)
"Hardware does not support descriptor DMA mode -\n");
dev_err(hsotg->dev,
"falling back to buffer DMA mode.\n");
- hsotg->core_params->dma_desc_enable = 0;
+ hsotg->params.dma_desc_enable = false;
} else {
hcfg = dwc2_readl(hsotg->regs + HCFG);
hcfg |= HCFG_DESCDMA;
@@ -2332,12 +2363,12 @@ static void dwc2_core_host_init(struct dwc2_hsotg *hsotg)
otgctl &= ~GOTGCTL_HSTSETHNPEN;
dwc2_writel(otgctl, hsotg->regs + GOTGCTL);
- if (hsotg->core_params->dma_desc_enable <= 0) {
+ if (!hsotg->params.dma_desc_enable) {
int num_channels, i;
u32 hcchar;
/* Flush out any leftover queued requests */
- num_channels = hsotg->core_params->host_channels;
+ num_channels = hsotg->params.host_channels;
for (i = 0; i < num_channels; i++) {
hcchar = dwc2_readl(hsotg->regs + HCCHAR(i));
hcchar &= ~HCCHAR_CHENA;
@@ -2399,9 +2430,9 @@ static void dwc2_hcd_reinit(struct dwc2_hsotg *hsotg)
hsotg->flags.d32 = 0;
hsotg->non_periodic_qh_ptr = &hsotg->non_periodic_sched_active;
- if (hsotg->core_params->uframe_sched > 0) {
+ if (hsotg->params.uframe_sched) {
hsotg->available_host_channels =
- hsotg->core_params->host_channels;
+ hsotg->params.host_channels;
} else {
hsotg->non_periodic_channels = 0;
hsotg->periodic_channels = 0;
@@ -2415,7 +2446,7 @@ static void dwc2_hcd_reinit(struct dwc2_hsotg *hsotg)
hc_list_entry)
list_del_init(&chan->hc_list_entry);
- num_channels = hsotg->core_params->host_channels;
+ num_channels = hsotg->params.host_channels;
for (i = 0; i < num_channels; i++) {
chan = hsotg->hc_ptr_array[i];
list_add_tail(&chan->hc_list_entry, &hsotg->free_hc_list);
@@ -2457,7 +2488,7 @@ static void dwc2_hc_init_xfer(struct dwc2_hsotg *hsotg,
chan->do_ping = 0;
chan->ep_is_in = 0;
chan->data_pid_start = DWC2_HC_PID_SETUP;
- if (hsotg->core_params->dma_enable > 0)
+ if (hsotg->params.host_dma)
chan->xfer_dma = urb->setup_dma;
else
chan->xfer_buf = urb->setup_packet;
@@ -2484,7 +2515,7 @@ static void dwc2_hc_init_xfer(struct dwc2_hsotg *hsotg,
chan->do_ping = 0;
chan->data_pid_start = DWC2_HC_PID_DATA1;
chan->xfer_len = 0;
- if (hsotg->core_params->dma_enable > 0)
+ if (hsotg->params.host_dma)
chan->xfer_dma = hsotg->status_buf_dma;
else
chan->xfer_buf = hsotg->status_buf;
@@ -2502,13 +2533,13 @@ static void dwc2_hc_init_xfer(struct dwc2_hsotg *hsotg,
case USB_ENDPOINT_XFER_ISOC:
chan->ep_type = USB_ENDPOINT_XFER_ISOC;
- if (hsotg->core_params->dma_desc_enable > 0)
+ if (hsotg->params.dma_desc_enable)
break;
frame_desc = &urb->iso_descs[qtd->isoc_frame_index];
frame_desc->status = 0;
- if (hsotg->core_params->dma_enable > 0) {
+ if (hsotg->params.host_dma) {
chan->xfer_dma = urb->dma;
chan->xfer_dma += frame_desc->offset +
qtd->isoc_split_offset;
@@ -2546,7 +2577,7 @@ static void dwc2_free_dma_aligned_buffer(struct urb *urb)
return;
temp = container_of(urb->transfer_buffer,
- struct dma_aligned_buffer, data);
+ struct dma_aligned_buffer, data);
if (usb_urb_dir_in(urb))
memcpy(temp->old_xfer_buffer, temp->data,
@@ -2590,7 +2621,7 @@ static int dwc2_alloc_dma_aligned_buffer(struct urb *urb, gfp_t mem_flags)
}
static int dwc2_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
- gfp_t mem_flags)
+ gfp_t mem_flags)
{
int ret;
@@ -2687,10 +2718,10 @@ static int dwc2_assign_and_init_hc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
chan->multi_count = 1;
if (urb->actual_length > urb->length &&
- !dwc2_hcd_is_pipe_in(&urb->pipe_info))
+ !dwc2_hcd_is_pipe_in(&urb->pipe_info))
urb->actual_length = urb->length;
- if (hsotg->core_params->dma_enable > 0)
+ if (hsotg->params.host_dma)
chan->xfer_dma = urb->dma + urb->actual_length;
else
chan->xfer_buf = (u8 *)urb->buf + urb->actual_length;
@@ -2715,7 +2746,7 @@ static int dwc2_assign_and_init_hc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
*/
chan->multi_count = dwc2_hb_mult(qh->maxp);
- if (hsotg->core_params->dma_desc_enable > 0) {
+ if (hsotg->params.dma_desc_enable) {
chan->desc_list_addr = qh->desc_list_dma;
chan->desc_list_sz = qh->desc_list_sz;
}
@@ -2752,7 +2783,7 @@ enum dwc2_transaction_type dwc2_hcd_select_transactions(
while (qh_ptr != &hsotg->periodic_sched_ready) {
if (list_empty(&hsotg->free_hc_list))
break;
- if (hsotg->core_params->uframe_sched > 0) {
+ if (hsotg->params.uframe_sched) {
if (hsotg->available_host_channels <= 1)
break;
hsotg->available_host_channels--;
@@ -2776,17 +2807,17 @@ enum dwc2_transaction_type dwc2_hcd_select_transactions(
* schedule. Some free host channels may not be used if they are
* reserved for periodic transfers.
*/
- num_channels = hsotg->core_params->host_channels;
+ num_channels = hsotg->params.host_channels;
qh_ptr = hsotg->non_periodic_sched_inactive.next;
while (qh_ptr != &hsotg->non_periodic_sched_inactive) {
- if (hsotg->core_params->uframe_sched <= 0 &&
+ if (!hsotg->params.uframe_sched &&
hsotg->non_periodic_channels >= num_channels -
hsotg->periodic_channels)
break;
if (list_empty(&hsotg->free_hc_list))
break;
qh = list_entry(qh_ptr, struct dwc2_qh, qh_list_entry);
- if (hsotg->core_params->uframe_sched > 0) {
+ if (hsotg->params.uframe_sched) {
if (hsotg->available_host_channels < 1)
break;
hsotg->available_host_channels--;
@@ -2808,7 +2839,7 @@ enum dwc2_transaction_type dwc2_hcd_select_transactions(
else
ret_val = DWC2_TRANSACTION_ALL;
- if (hsotg->core_params->uframe_sched <= 0)
+ if (!hsotg->params.uframe_sched)
hsotg->non_periodic_channels++;
}
@@ -2847,8 +2878,8 @@ static int dwc2_queue_transaction(struct dwc2_hsotg *hsotg,
list_move_tail(&chan->split_order_list_entry,
&hsotg->split_order);
- if (hsotg->core_params->dma_enable > 0) {
- if (hsotg->core_params->dma_desc_enable > 0) {
+ if (hsotg->params.host_dma) {
+ if (hsotg->params.dma_desc_enable) {
if (!chan->xfer_started ||
chan->ep_type == USB_ENDPOINT_XFER_ISOC) {
dwc2_hcd_start_xfer_ddma(hsotg, chan->qh);
@@ -2936,7 +2967,7 @@ static void dwc2_process_periodic_channels(struct dwc2_hsotg *hsotg)
qspcavail = (tx_status & TXSTS_QSPCAVAIL_MASK) >>
TXSTS_QSPCAVAIL_SHIFT;
if (qspcavail == 0) {
- no_queue_space = 1;
+ no_queue_space = true;
break;
}
@@ -2957,15 +2988,15 @@ static void dwc2_process_periodic_channels(struct dwc2_hsotg *hsotg)
* The flag prevents any halts to get into the request queue in
* the middle of multiple high-bandwidth packets getting queued.
*/
- if (hsotg->core_params->dma_enable <= 0 &&
- qh->channel->multi_count > 1)
+ if (!hsotg->params.host_dma &&
+ qh->channel->multi_count > 1)
hsotg->queuing_high_bandwidth = 1;
fspcavail = (tx_status & TXSTS_FSPCAVAIL_MASK) >>
TXSTS_FSPCAVAIL_SHIFT;
status = dwc2_queue_transaction(hsotg, qh->channel, fspcavail);
if (status < 0) {
- no_fifo_space = 1;
+ no_fifo_space = true;
break;
}
@@ -2976,7 +3007,7 @@ static void dwc2_process_periodic_channels(struct dwc2_hsotg *hsotg)
* controller automatically handles multiple packets for
* high-bandwidth transfers.
*/
- if (hsotg->core_params->dma_enable > 0 || status == 0 ||
+ if (hsotg->params.host_dma || status == 0 ||
qh->channel->requests == qh->channel->multi_count) {
qh_ptr = qh_ptr->next;
/*
@@ -2993,7 +3024,7 @@ static void dwc2_process_periodic_channels(struct dwc2_hsotg *hsotg)
exit:
if (no_queue_space || no_fifo_space ||
- (hsotg->core_params->dma_enable <= 0 &&
+ (!hsotg->params.host_dma &&
!list_empty(&hsotg->periodic_sched_assigned))) {
/*
* May need to queue more transactions as the request
@@ -3014,7 +3045,7 @@ exit:
* now. This function is called from interrupt
* handlers to queue more transactions as transfer
* states change.
- */
+ */
gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
if (gintmsk & GINTSTS_PTXFEMP) {
gintmsk &= ~GINTSTS_PTXFEMP;
@@ -3073,7 +3104,7 @@ static void dwc2_process_non_periodic_channels(struct dwc2_hsotg *hsotg)
tx_status = dwc2_readl(hsotg->regs + GNPTXSTS);
qspcavail = (tx_status & TXSTS_QSPCAVAIL_MASK) >>
TXSTS_QSPCAVAIL_SHIFT;
- if (hsotg->core_params->dma_enable <= 0 && qspcavail == 0) {
+ if (!hsotg->params.host_dma && qspcavail == 0) {
no_queue_space = 1;
break;
}
@@ -3106,7 +3137,7 @@ next:
hsotg->non_periodic_qh_ptr->next;
} while (hsotg->non_periodic_qh_ptr != orig_qh_ptr);
- if (hsotg->core_params->dma_enable <= 0) {
+ if (!hsotg->params.host_dma) {
tx_status = dwc2_readl(hsotg->regs + GNPTXSTS);
qspcavail = (tx_status & TXSTS_QSPCAVAIL_MASK) >>
TXSTS_QSPCAVAIL_SHIFT;
@@ -3204,12 +3235,25 @@ static void dwc2_conn_id_status_change(struct work_struct *work)
if (gotgctl & GOTGCTL_CONID_B) {
/* Wait for switch to device mode */
dev_dbg(hsotg->dev, "connId B\n");
+ if (hsotg->bus_suspended) {
+ dev_info(hsotg->dev,
+ "Do port resume before switching to device mode\n");
+ dwc2_port_resume(hsotg);
+ }
while (!dwc2_is_device_mode(hsotg)) {
dev_info(hsotg->dev,
"Waiting for Peripheral Mode, Mode=%s\n",
dwc2_is_host_mode(hsotg) ? "Host" :
"Peripheral");
- usleep_range(20000, 40000);
+ msleep(20);
+ /*
+ * Sometimes the initial GOTGCTRL read is wrong, so
+ * check it again and jump to host mode if that was
+ * the case.
+ */
+ gotgctl = dwc2_readl(hsotg->regs + GOTGCTL);
+ if (!(gotgctl & GOTGCTL_CONID_B))
+ goto host;
if (++count > 250)
break;
}
@@ -3224,13 +3268,14 @@ static void dwc2_conn_id_status_change(struct work_struct *work)
spin_unlock_irqrestore(&hsotg->lock, flags);
dwc2_hsotg_core_connect(hsotg);
} else {
+host:
/* A-Device connector (Host Mode) */
dev_dbg(hsotg->dev, "connId A\n");
while (!dwc2_is_host_mode(hsotg)) {
dev_info(hsotg->dev, "Waiting for Host Mode, Mode=%s\n",
dwc2_is_host_mode(hsotg) ?
"Host" : "Peripheral");
- usleep_range(20000, 40000);
+ msleep(20);
if (++count > 250)
break;
}
@@ -3265,7 +3310,7 @@ static void dwc2_wakeup_detected(unsigned long data)
dwc2_readl(hsotg->regs + HPRT0));
dwc2_hcd_rem_wakeup(hsotg);
- hsotg->bus_suspended = 0;
+ hsotg->bus_suspended = false;
/* Change to L0 state */
hsotg->lx_state = DWC2_L0;
@@ -3301,13 +3346,13 @@ static void dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex)
hprt0 |= HPRT0_SUSP;
dwc2_writel(hprt0, hsotg->regs + HPRT0);
- hsotg->bus_suspended = 1;
+ hsotg->bus_suspended = true;
/*
* If hibernation is supported, Phy clock will be suspended
* after registers are backuped.
*/
- if (!hsotg->core_params->hibernation) {
+ if (!hsotg->params.hibernation) {
/* Suspend the Phy Clock */
pcgctl = dwc2_readl(hsotg->regs + PCGCTL);
pcgctl |= PCGCTL_STOPPCLK;
@@ -3323,7 +3368,7 @@ static void dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex)
spin_unlock_irqrestore(&hsotg->lock, flags);
- usleep_range(200000, 250000);
+ msleep(200);
} else {
spin_unlock_irqrestore(&hsotg->lock, flags);
}
@@ -3342,12 +3387,12 @@ static void dwc2_port_resume(struct dwc2_hsotg *hsotg)
* If hibernation is supported, Phy clock is already resumed
* after registers restore.
*/
- if (!hsotg->core_params->hibernation) {
+ if (!hsotg->params.hibernation) {
pcgctl = dwc2_readl(hsotg->regs + PCGCTL);
pcgctl &= ~PCGCTL_STOPPCLK;
dwc2_writel(pcgctl, hsotg->regs + PCGCTL);
spin_unlock_irqrestore(&hsotg->lock, flags);
- usleep_range(20000, 40000);
+ msleep(20);
spin_lock_irqsave(&hsotg->lock, flags);
}
@@ -3363,7 +3408,7 @@ static void dwc2_port_resume(struct dwc2_hsotg *hsotg)
hprt0 = dwc2_read_hprt0(hsotg);
hprt0 &= ~(HPRT0_RES | HPRT0_SUSP);
dwc2_writel(hprt0, hsotg->regs + HPRT0);
- hsotg->bus_suspended = 0;
+ hsotg->bus_suspended = false;
spin_unlock_irqrestore(&hsotg->lock, flags);
}
@@ -3569,7 +3614,7 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
port_status |= USB_PORT_STAT_TEST;
/* USB_PORT_FEAT_INDICATOR unsupported always 0 */
- if (hsotg->core_params->dma_desc_fs_enable) {
+ if (hsotg->params.dma_desc_fs_enable) {
/*
* Enable descriptor DMA only if a full speed
* device is connected.
@@ -3583,7 +3628,7 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
u32 hcfg;
dev_info(hsotg->dev, "Enabling descriptor DMA mode\n");
- hsotg->core_params->dma_desc_enable = 1;
+ hsotg->params.dma_desc_enable = true;
hcfg = dwc2_readl(hsotg->regs + HCFG);
hcfg |= HCFG_DESCDMA;
dwc2_writel(hcfg, hsotg->regs + HCFG);
@@ -3660,7 +3705,7 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
}
/* Clear reset bit in 10ms (FS/LS) or 50ms (HS) */
- usleep_range(50000, 70000);
+ msleep(50);
hprt0 &= ~HPRT0_RST;
dwc2_writel(hprt0, hsotg->regs + HPRT0);
hsotg->lx_state = DWC2_L0; /* Now back to On state */
@@ -3824,7 +3869,7 @@ void dwc2_hcd_dump_state(struct dwc2_hsotg *hsotg)
u32 p_tx_status;
int i;
- num_channels = hsotg->core_params->host_channels;
+ num_channels = hsotg->params.host_channels;
dev_dbg(hsotg->dev, "\n");
dev_dbg(hsotg->dev,
"************************************************************\n");
@@ -4016,39 +4061,10 @@ static struct dwc2_hsotg *dwc2_hcd_to_hsotg(struct usb_hcd *hcd)
{
struct wrapper_priv_data *p;
- p = (struct wrapper_priv_data *) &hcd->hcd_priv;
+ p = (struct wrapper_priv_data *)&hcd->hcd_priv;
return p->hsotg;
}
-static int _dwc2_hcd_start(struct usb_hcd *hcd);
-
-void dwc2_host_start(struct dwc2_hsotg *hsotg)
-{
- struct usb_hcd *hcd = dwc2_hsotg_to_hcd(hsotg);
-
- hcd->self.is_b_host = dwc2_hcd_is_b_host(hsotg);
- _dwc2_hcd_start(hcd);
-}
-
-void dwc2_host_disconnect(struct dwc2_hsotg *hsotg)
-{
- struct usb_hcd *hcd = dwc2_hsotg_to_hcd(hsotg);
-
- hcd->self.is_b_host = 0;
-}
-
-void dwc2_host_hub_info(struct dwc2_hsotg *hsotg, void *context, int *hub_addr,
- int *hub_port)
-{
- struct urb *urb = context;
-
- if (urb->dev->tt)
- *hub_addr = urb->dev->tt->hub->devnum;
- else
- *hub_addr = 0;
- *hub_port = urb->dev->ttport;
-}
-
/**
* dwc2_host_get_tt_info() - Get the dwc2_tt associated with context
*
@@ -4080,7 +4096,7 @@ struct dwc2_tt *dwc2_host_get_tt_info(struct dwc2_hsotg *hsotg, void *context,
*ttport = urb->dev->ttport;
dwc_tt = urb->dev->tt->hcpriv;
- if (dwc_tt == NULL) {
+ if (!dwc_tt) {
size_t bitmap_size;
/*
@@ -4094,7 +4110,7 @@ struct dwc2_tt *dwc2_host_get_tt_info(struct dwc2_hsotg *hsotg, void *context,
dwc_tt = kzalloc(sizeof(*dwc_tt) + bitmap_size,
mem_flags);
- if (dwc_tt == NULL)
+ if (!dwc_tt)
return NULL;
dwc_tt->usb_tt = urb->dev->tt;
@@ -4121,7 +4137,7 @@ struct dwc2_tt *dwc2_host_get_tt_info(struct dwc2_hsotg *hsotg, void *context,
void dwc2_host_put_tt_info(struct dwc2_hsotg *hsotg, struct dwc2_tt *dwc_tt)
{
/* Model kfree and make put of NULL a no-op */
- if (dwc_tt == NULL)
+ if (!dwc_tt)
return;
WARN_ON(dwc_tt->refcount < 1);
@@ -4204,7 +4220,6 @@ void dwc2_host_complete(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd,
usb_pipein(urb->pipe) ? "IN" : "OUT", status,
urb->actual_length);
-
if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
urb->error_count = dwc2_hcd_urb_get_error_count(qtd->urb);
for (i = 0; i < urb->number_of_packets; ++i) {
@@ -4365,7 +4380,10 @@ static int _dwc2_hcd_suspend(struct usb_hcd *hcd)
if (!HCD_HW_ACCESSIBLE(hcd))
goto unlock;
- if (!hsotg->core_params->hibernation)
+ if (hsotg->op_state == OTG_STATE_B_PERIPHERAL)
+ goto unlock;
+
+ if (!hsotg->params.hibernation)
goto skip_power_saving;
/*
@@ -4417,7 +4435,7 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd)
if (hsotg->lx_state != DWC2_L2)
goto unlock;
- if (!hsotg->core_params->hibernation) {
+ if (!hsotg->params.hibernation) {
hsotg->lx_state = DWC2_L0;
goto unlock;
}
@@ -4487,8 +4505,8 @@ static void dwc2_dump_urb_info(struct usb_hcd *hcd, struct urb *urb,
{
#ifdef VERBOSE_DEBUG
struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
- char *pipetype;
- char *speed;
+ char *pipetype = NULL;
+ char *speed = NULL;
dev_vdbg(hsotg->dev, "%s, urb %p\n", fn_name, urb);
dev_vdbg(hsotg->dev, " Device address: %d\n",
@@ -4510,9 +4528,6 @@ static void dwc2_dump_urb_info(struct usb_hcd *hcd, struct urb *urb,
case PIPE_ISOCHRONOUS:
pipetype = "ISOCHRONOUS";
break;
- default:
- pipetype = "UNKNOWN";
- break;
}
dev_vdbg(hsotg->dev, " Endpoint type: %s %s (%s)\n", pipetype,
@@ -4585,7 +4600,7 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
dwc2_dump_urb_info(hcd, urb, "urb_enqueue");
}
- if (ep == NULL)
+ if (!ep)
return -EINVAL;
if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS ||
@@ -4609,8 +4624,6 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
case PIPE_INTERRUPT:
ep_type = USB_ENDPOINT_XFER_INT;
break;
- default:
- dev_warn(hsotg->dev, "Wrong ep type\n");
}
dwc2_urb = dwc2_hcd_urb_alloc(hsotg, urb->number_of_packets,
@@ -4657,7 +4670,7 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
urb->iso_frame_desc[i].length);
urb->hcpriv = dwc2_urb;
- qh = (struct dwc2_qh *) ep->hcpriv;
+ qh = (struct dwc2_qh *)ep->hcpriv;
/* Create QH for the endpoint if it doesn't exist */
if (!qh) {
qh = dwc2_hcd_qh_create(hsotg, dwc2_urb, mem_flags);
@@ -4712,7 +4725,7 @@ fail1:
dwc2_hcd_qh_unlink(hsotg, qh);
/* Free each QTD in the QH's QTD list */
list_for_each_entry_safe(qtd2, qtd2_tmp, &qh->qtd_list,
- qtd_list_entry)
+ qtd_list_entry)
dwc2_hcd_qtd_unlink_and_free(hsotg, qtd2, qh);
dwc2_hcd_qh_free(hsotg, qh);
}
@@ -4860,6 +4873,61 @@ static void _dwc2_hcd_clear_tt_buffer_complete(struct usb_hcd *hcd,
spin_unlock_irqrestore(&hsotg->lock, flags);
}
+/*
+ * HPRT0_SPD_HIGH_SPEED: high speed
+ * HPRT0_SPD_FULL_SPEED: full speed
+ */
+static void dwc2_change_bus_speed(struct usb_hcd *hcd, int speed)
+{
+ struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
+
+ if (hsotg->params.speed == speed)
+ return;
+
+ hsotg->params.speed = speed;
+ queue_work(hsotg->wq_otg, &hsotg->wf_otg);
+}
+
+static void dwc2_free_dev(struct usb_hcd *hcd, struct usb_device *udev)
+{
+ struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
+
+ if (!hsotg->params.change_speed_quirk)
+ return;
+
+ /*
+ * On removal, set speed to default high-speed.
+ */
+ if (udev->parent && udev->parent->speed > USB_SPEED_UNKNOWN &&
+ udev->parent->speed < USB_SPEED_HIGH) {
+ dev_info(hsotg->dev, "Set speed to default high-speed\n");
+ dwc2_change_bus_speed(hcd, HPRT0_SPD_HIGH_SPEED);
+ }
+}
+
+static int dwc2_reset_device(struct usb_hcd *hcd, struct usb_device *udev)
+{
+ struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
+
+ if (!hsotg->params.change_speed_quirk)
+ return 0;
+
+ if (udev->speed == USB_SPEED_HIGH) {
+ dev_info(hsotg->dev, "Set speed to high-speed\n");
+ dwc2_change_bus_speed(hcd, HPRT0_SPD_HIGH_SPEED);
+ } else if ((udev->speed == USB_SPEED_FULL ||
+ udev->speed == USB_SPEED_LOW)) {
+ /*
+ * Change speed setting to full-speed if there's
+ * a full-speed or low-speed device plugged in.
+ */
+ dev_info(hsotg->dev, "Set speed to full-speed\n");
+ dwc2_change_bus_speed(hcd, HPRT0_SPD_FULL_SPEED);
+ }
+
+ return 0;
+}
+
static struct hc_driver dwc2_hc_driver = {
.description = "dwc2_hsotg",
.product_desc = "DWC OTG Controller",
@@ -4911,7 +4979,7 @@ static void dwc2_hcd_free(struct dwc2_hsotg *hsotg)
for (i = 0; i < MAX_EPS_CHANNELS; i++) {
struct dwc2_host_chan *chan = hsotg->hc_ptr_array[i];
- if (chan != NULL) {
+ if (chan) {
dev_dbg(hsotg->dev, "HCD Free channel #%i, chan=%p\n",
i, chan);
hsotg->hc_ptr_array[i] = NULL;
@@ -4919,7 +4987,7 @@ static void dwc2_hcd_free(struct dwc2_hsotg *hsotg)
}
}
- if (hsotg->core_params->dma_enable > 0) {
+ if (hsotg->params.host_dma) {
if (hsotg->status_buf) {
dma_free_coherent(hsotg->dev, DWC2_HCD_STATUS_BUF_SIZE,
hsotg->status_buf,
@@ -4967,8 +5035,10 @@ static void dwc2_hcd_release(struct dwc2_hsotg *hsotg)
* USB bus with the core and calls the hc_driver->start() function. It returns
* a negative error on failure.
*/
-int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq)
+int dwc2_hcd_init(struct dwc2_hsotg *hsotg)
{
+ struct platform_device *pdev = to_platform_device(hsotg->dev);
+ struct resource *res;
struct usb_hcd *hcd;
struct dwc2_host_chan *channel;
u32 hcfg;
@@ -4999,32 +5069,41 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq)
hsotg->last_frame_num = HFNUM_MAX_FRNUM;
/* Check if the bus driver or platform code has setup a dma_mask */
- if (hsotg->core_params->dma_enable > 0 &&
- hsotg->dev->dma_mask == NULL) {
+ if (hsotg->params.host_dma &&
+ !hsotg->dev->dma_mask) {
dev_warn(hsotg->dev,
"dma_mask not set, disabling DMA\n");
- hsotg->core_params->dma_enable = 0;
- hsotg->core_params->dma_desc_enable = 0;
+ hsotg->params.host_dma = false;
+ hsotg->params.dma_desc_enable = false;
}
/* Set device flags indicating whether the HCD supports DMA */
- if (hsotg->core_params->dma_enable > 0) {
+ if (hsotg->params.host_dma) {
if (dma_set_mask(hsotg->dev, DMA_BIT_MASK(32)) < 0)
dev_warn(hsotg->dev, "can't set DMA mask\n");
if (dma_set_coherent_mask(hsotg->dev, DMA_BIT_MASK(32)) < 0)
dev_warn(hsotg->dev, "can't set coherent DMA mask\n");
}
+ if (hsotg->params.change_speed_quirk) {
+ dwc2_hc_driver.free_dev = dwc2_free_dev;
+ dwc2_hc_driver.reset_device = dwc2_reset_device;
+ }
+
hcd = usb_create_hcd(&dwc2_hc_driver, hsotg->dev, dev_name(hsotg->dev));
if (!hcd)
goto error1;
- if (hsotg->core_params->dma_enable <= 0)
+ if (!hsotg->params.host_dma)
hcd->self.uses_dma = 0;
hcd->has_tt = 1;
- ((struct wrapper_priv_data *) &hcd->hcd_priv)->hsotg = hsotg;
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ hcd->rsrc_start = res->start;
+ hcd->rsrc_len = resource_size(res);
+
+ ((struct wrapper_priv_data *)&hcd->hcd_priv)->hsotg = hsotg;
hsotg->priv = hcd;
/*
@@ -5067,12 +5146,12 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq)
* in the controller. Initialize the channel descriptor array.
*/
INIT_LIST_HEAD(&hsotg->free_hc_list);
- num_channels = hsotg->core_params->host_channels;
+ num_channels = hsotg->params.host_channels;
memset(&hsotg->hc_ptr_array[0], 0, sizeof(hsotg->hc_ptr_array));
for (i = 0; i < num_channels; i++) {
channel = kzalloc(sizeof(*channel), GFP_KERNEL);
- if (channel == NULL)
+ if (!channel)
goto error3;
channel->hc_num = i;
INIT_LIST_HEAD(&channel->split_order_list_entry);
@@ -5091,7 +5170,7 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq)
* done after usb_add_hcd since that function allocates the DMA buffer
* pool.
*/
- if (hsotg->core_params->dma_enable > 0)
+ if (hsotg->params.host_dma)
hsotg->status_buf = dma_alloc_coherent(hsotg->dev,
DWC2_HCD_STATUS_BUF_SIZE,
&hsotg->status_buf_dma, GFP_KERNEL);
@@ -5107,10 +5186,10 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq)
* DMA mode.
* Alignment must be set to 512 bytes.
*/
- if (hsotg->core_params->dma_desc_enable ||
- hsotg->core_params->dma_desc_fs_enable) {
+ if (hsotg->params.dma_desc_enable ||
+ hsotg->params.dma_desc_fs_enable) {
hsotg->desc_gen_cache = kmem_cache_create("dwc2-gen-desc",
- sizeof(struct dwc2_hcd_dma_desc) *
+ sizeof(struct dwc2_dma_desc) *
MAX_DMA_DESC_NUM_GENERIC, 512, SLAB_CACHE_DMA,
NULL);
if (!hsotg->desc_gen_cache) {
@@ -5121,12 +5200,12 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq)
* Disable descriptor dma mode since it will not be
* usable.
*/
- hsotg->core_params->dma_desc_enable = 0;
- hsotg->core_params->dma_desc_fs_enable = 0;
+ hsotg->params.dma_desc_enable = false;
+ hsotg->params.dma_desc_fs_enable = false;
}
hsotg->desc_hsisoc_cache = kmem_cache_create("dwc2-hsisoc-desc",
- sizeof(struct dwc2_hcd_dma_desc) *
+ sizeof(struct dwc2_dma_desc) *
MAX_DMA_DESC_NUM_HS_ISOC, 512, 0, NULL);
if (!hsotg->desc_hsisoc_cache) {
dev_err(hsotg->dev,
@@ -5138,8 +5217,8 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq)
* Disable descriptor dma mode since it will not be
* usable.
*/
- hsotg->core_params->dma_desc_enable = 0;
- hsotg->core_params->dma_desc_fs_enable = 0;
+ hsotg->params.dma_desc_enable = false;
+ hsotg->params.dma_desc_fs_enable = false;
}
}
@@ -5164,7 +5243,7 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq)
* allocates the DMA buffer pool, registers the USB bus, requests the
* IRQ line, and calls hcd_start method.
*/
- retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
+ retval = usb_add_hcd(hcd, hsotg->irq, IRQF_SHARED);
if (retval < 0)
goto error4;
@@ -5184,7 +5263,6 @@ error3:
error2:
usb_put_hcd(hcd);
error1:
- kfree(hsotg->core_params);
#ifdef CONFIG_USB_DWC2_TRACK_MISSED_SOFS
kfree(hsotg->last_frame_num_array);
@@ -5250,7 +5328,7 @@ int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg)
hr = &hsotg->hr_backup;
hr->hcfg = dwc2_readl(hsotg->regs + HCFG);
hr->haintmsk = dwc2_readl(hsotg->regs + HAINTMSK);
- for (i = 0; i < hsotg->core_params->host_channels; ++i)
+ for (i = 0; i < hsotg->params.host_channels; ++i)
hr->hcintmsk[i] = dwc2_readl(hsotg->regs + HCINTMSK(i));
hr->hprt0 = dwc2_read_hprt0(hsotg);
@@ -5286,7 +5364,7 @@ int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg)
dwc2_writel(hr->hcfg, hsotg->regs + HCFG);
dwc2_writel(hr->haintmsk, hsotg->regs + HAINTMSK);
- for (i = 0; i < hsotg->core_params->host_channels; ++i)
+ for (i = 0; i < hsotg->params.host_channels; ++i)
dwc2_writel(hr->hcintmsk[i], hsotg->regs + HCINTMSK(i));
dwc2_writel(hr->hprt0, hsotg->regs + HPRT0);
diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h
index 7758bfb644ff..11c3c145b793 100644
--- a/drivers/usb/dwc2/hcd.h
+++ b/drivers/usb/dwc2/hcd.h
@@ -348,7 +348,7 @@ struct dwc2_qh {
struct list_head qtd_list;
struct dwc2_host_chan *channel;
struct list_head qh_list_entry;
- struct dwc2_hcd_dma_desc *desc_list;
+ struct dwc2_dma_desc *desc_list;
dma_addr_t desc_list_dma;
u32 desc_list_sz;
u32 *n_bytes;
@@ -521,29 +521,29 @@ static inline u8 dwc2_hcd_is_pipe_out(struct dwc2_hcd_pipe_info *pipe)
return !dwc2_hcd_is_pipe_in(pipe);
}
-extern int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq);
-extern void dwc2_hcd_remove(struct dwc2_hsotg *hsotg);
+int dwc2_hcd_init(struct dwc2_hsotg *hsotg);
+void dwc2_hcd_remove(struct dwc2_hsotg *hsotg);
/* Transaction Execution Functions */
-extern enum dwc2_transaction_type dwc2_hcd_select_transactions(
+enum dwc2_transaction_type dwc2_hcd_select_transactions(
struct dwc2_hsotg *hsotg);
-extern void dwc2_hcd_queue_transactions(struct dwc2_hsotg *hsotg,
- enum dwc2_transaction_type tr_type);
+void dwc2_hcd_queue_transactions(struct dwc2_hsotg *hsotg,
+ enum dwc2_transaction_type tr_type);
/* Schedule Queue Functions */
/* Implemented in hcd_queue.c */
-extern struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
- struct dwc2_hcd_urb *urb,
+struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
+ struct dwc2_hcd_urb *urb,
gfp_t mem_flags);
-extern void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh);
-extern int dwc2_hcd_qh_add(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh);
-extern void dwc2_hcd_qh_unlink(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh);
-extern void dwc2_hcd_qh_deactivate(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
- int sched_csplit);
+void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh);
+int dwc2_hcd_qh_add(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh);
+void dwc2_hcd_qh_unlink(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh);
+void dwc2_hcd_qh_deactivate(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
+ int sched_csplit);
-extern void dwc2_hcd_qtd_init(struct dwc2_qtd *qtd, struct dwc2_hcd_urb *urb);
-extern int dwc2_hcd_qtd_add(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd,
- struct dwc2_qh *qh);
+void dwc2_hcd_qtd_init(struct dwc2_qtd *qtd, struct dwc2_hcd_urb *urb);
+int dwc2_hcd_qtd_add(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd,
+ struct dwc2_qh *qh);
/* Unlinks and frees a QTD */
static inline void dwc2_hcd_qtd_unlink_and_free(struct dwc2_hsotg *hsotg,
@@ -556,15 +556,15 @@ static inline void dwc2_hcd_qtd_unlink_and_free(struct dwc2_hsotg *hsotg,
}
/* Descriptor DMA support functions */
-extern void dwc2_hcd_start_xfer_ddma(struct dwc2_hsotg *hsotg,
- struct dwc2_qh *qh);
-extern void dwc2_hcd_complete_xfer_ddma(struct dwc2_hsotg *hsotg,
- struct dwc2_host_chan *chan, int chnum,
+void dwc2_hcd_start_xfer_ddma(struct dwc2_hsotg *hsotg,
+ struct dwc2_qh *qh);
+void dwc2_hcd_complete_xfer_ddma(struct dwc2_hsotg *hsotg,
+ struct dwc2_host_chan *chan, int chnum,
enum dwc2_halt_status halt_status);
-extern int dwc2_hcd_qh_init_ddma(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
- gfp_t mem_flags);
-extern void dwc2_hcd_qh_free_ddma(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh);
+int dwc2_hcd_qh_init_ddma(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
+ gfp_t mem_flags);
+void dwc2_hcd_qh_free_ddma(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh);
/* Check if QH is non-periodic */
#define dwc2_qh_is_non_per(_qh_ptr_) \
@@ -732,8 +732,8 @@ static inline u16 dwc2_hcd_get_ep_bandwidth(struct dwc2_hsotg *hsotg,
return qh->host_us;
}
-extern void dwc2_hcd_save_data_toggle(struct dwc2_hsotg *hsotg,
- struct dwc2_host_chan *chan, int chnum,
+void dwc2_hcd_save_data_toggle(struct dwc2_hsotg *hsotg,
+ struct dwc2_host_chan *chan, int chnum,
struct dwc2_qtd *qtd);
/* HCD Core API */
@@ -746,14 +746,14 @@ extern void dwc2_hcd_save_data_toggle(struct dwc2_hsotg *hsotg,
* Returns IRQ_HANDLED if interrupt is handled
* Return IRQ_NONE if interrupt is not handled
*/
-extern irqreturn_t dwc2_handle_hcd_intr(struct dwc2_hsotg *hsotg);
+irqreturn_t dwc2_handle_hcd_intr(struct dwc2_hsotg *hsotg);
/**
* dwc2_hcd_stop() - Halts the DWC_otg host mode operation
*
* @hsotg: The DWC2 HCD
*/
-extern void dwc2_hcd_stop(struct dwc2_hsotg *hsotg);
+void dwc2_hcd_stop(struct dwc2_hsotg *hsotg);
/**
* dwc2_hcd_is_b_host() - Returns 1 if core currently is acting as B host,
@@ -761,7 +761,7 @@ extern void dwc2_hcd_stop(struct dwc2_hsotg *hsotg);
*
* @hsotg: The DWC2 HCD
*/
-extern int dwc2_hcd_is_b_host(struct dwc2_hsotg *hsotg);
+int dwc2_hcd_is_b_host(struct dwc2_hsotg *hsotg);
/**
* dwc2_hcd_dump_state() - Dumps hsotg state
@@ -771,7 +771,7 @@ extern int dwc2_hcd_is_b_host(struct dwc2_hsotg *hsotg);
* NOTE: This function will be removed once the peripheral controller code
* is integrated and the driver is stable
*/
-extern void dwc2_hcd_dump_state(struct dwc2_hsotg *hsotg);
+void dwc2_hcd_dump_state(struct dwc2_hsotg *hsotg);
/**
* dwc2_hcd_dump_frrem() - Dumps the average frame remaining at SOF
@@ -784,7 +784,7 @@ extern void dwc2_hcd_dump_state(struct dwc2_hsotg *hsotg);
* NOTE: This function will be removed once the peripheral controller code
* is integrated and the driver is stable
*/
-extern void dwc2_hcd_dump_frrem(struct dwc2_hsotg *hsotg);
+void dwc2_hcd_dump_frrem(struct dwc2_hsotg *hsotg);
/* URB interface */
@@ -793,20 +793,15 @@ extern void dwc2_hcd_dump_frrem(struct dwc2_hsotg *hsotg);
#define URB_SEND_ZERO_PACKET 0x2
/* Host driver callbacks */
-
-extern void dwc2_host_start(struct dwc2_hsotg *hsotg);
-extern void dwc2_host_disconnect(struct dwc2_hsotg *hsotg);
-extern void dwc2_host_hub_info(struct dwc2_hsotg *hsotg, void *context,
- int *hub_addr, int *hub_port);
-extern struct dwc2_tt *dwc2_host_get_tt_info(struct dwc2_hsotg *hsotg,
- void *context, gfp_t mem_flags,
- int *ttport);
-
-extern void dwc2_host_put_tt_info(struct dwc2_hsotg *hsotg,
- struct dwc2_tt *dwc_tt);
-extern int dwc2_host_get_speed(struct dwc2_hsotg *hsotg, void *context);
-extern void dwc2_host_complete(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd,
- int status);
+struct dwc2_tt *dwc2_host_get_tt_info(struct dwc2_hsotg *hsotg,
+ void *context, gfp_t mem_flags,
+ int *ttport);
+
+void dwc2_host_put_tt_info(struct dwc2_hsotg *hsotg,
+ struct dwc2_tt *dwc_tt);
+int dwc2_host_get_speed(struct dwc2_hsotg *hsotg, void *context);
+void dwc2_host_complete(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd,
+ int status);
#ifdef DEBUG
/*
diff --git a/drivers/usb/dwc2/hcd_ddma.c b/drivers/usb/dwc2/hcd_ddma.c
index 0e1d42b5dec5..b8bdf545c3a7 100644
--- a/drivers/usb/dwc2/hcd_ddma.c
+++ b/drivers/usb/dwc2/hcd_ddma.c
@@ -89,13 +89,13 @@ static int dwc2_desc_list_alloc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
{
struct kmem_cache *desc_cache;
- if (qh->ep_type == USB_ENDPOINT_XFER_ISOC
- && qh->dev_speed == USB_SPEED_HIGH)
+ if (qh->ep_type == USB_ENDPOINT_XFER_ISOC &&
+ qh->dev_speed == USB_SPEED_HIGH)
desc_cache = hsotg->desc_hsisoc_cache;
else
desc_cache = hsotg->desc_gen_cache;
- qh->desc_list_sz = sizeof(struct dwc2_hcd_dma_desc) *
+ qh->desc_list_sz = sizeof(struct dwc2_dma_desc) *
dwc2_max_desc_num(qh);
qh->desc_list = kmem_cache_zalloc(desc_cache, flags | GFP_DMA);
@@ -106,7 +106,7 @@ static int dwc2_desc_list_alloc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
qh->desc_list_sz,
DMA_TO_DEVICE);
- qh->n_bytes = kzalloc(sizeof(u32) * dwc2_max_desc_num(qh), flags);
+ qh->n_bytes = kcalloc(dwc2_max_desc_num(qh), sizeof(u32), flags);
if (!qh->n_bytes) {
dma_unmap_single(hsotg->dev, qh->desc_list_dma,
qh->desc_list_sz,
@@ -123,8 +123,8 @@ static void dwc2_desc_list_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
{
struct kmem_cache *desc_cache;
- if (qh->ep_type == USB_ENDPOINT_XFER_ISOC
- && qh->dev_speed == USB_SPEED_HIGH)
+ if (qh->ep_type == USB_ENDPOINT_XFER_ISOC &&
+ qh->dev_speed == USB_SPEED_HIGH)
desc_cache = hsotg->desc_hsisoc_cache;
else
desc_cache = hsotg->desc_gen_cache;
@@ -175,7 +175,6 @@ static void dwc2_frame_list_free(struct dwc2_hsotg *hsotg)
hsotg->frame_list = NULL;
spin_unlock_irqrestore(&hsotg->lock, flags);
-
}
static void dwc2_per_sched_enable(struct dwc2_hsotg *hsotg, u32 fr_list_en)
@@ -297,7 +296,7 @@ static void dwc2_release_channel_ddma(struct dwc2_hsotg *hsotg,
struct dwc2_host_chan *chan = qh->channel;
if (dwc2_qh_is_non_per(qh)) {
- if (hsotg->core_params->uframe_sched > 0)
+ if (hsotg->params.uframe_sched)
hsotg->available_host_channels++;
else
hsotg->non_periodic_channels--;
@@ -322,7 +321,7 @@ static void dwc2_release_channel_ddma(struct dwc2_hsotg *hsotg,
qh->ntd = 0;
if (qh->desc_list)
- memset(qh->desc_list, 0, sizeof(struct dwc2_hcd_dma_desc) *
+ memset(qh->desc_list, 0, sizeof(struct dwc2_dma_desc) *
dwc2_max_desc_num(qh));
}
@@ -404,7 +403,7 @@ void dwc2_hcd_qh_free_ddma(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
if ((qh->ep_type == USB_ENDPOINT_XFER_ISOC ||
qh->ep_type == USB_ENDPOINT_XFER_INT) &&
- (hsotg->core_params->uframe_sched > 0 ||
+ (hsotg->params.uframe_sched ||
!hsotg->periodic_channels) && hsotg->frame_list) {
dwc2_per_sched_disable(hsotg);
dwc2_frame_list_free(hsotg);
@@ -542,7 +541,7 @@ static void dwc2_fill_host_isoc_dma_desc(struct dwc2_hsotg *hsotg,
struct dwc2_qh *qh, u32 max_xfer_size,
u16 idx)
{
- struct dwc2_hcd_dma_desc *dma_desc = &qh->desc_list[idx];
+ struct dwc2_dma_desc *dma_desc = &qh->desc_list[idx];
struct dwc2_hcd_iso_packet_desc *frame_desc;
memset(dma_desc, 0, sizeof(*dma_desc));
@@ -570,9 +569,9 @@ static void dwc2_fill_host_isoc_dma_desc(struct dwc2_hsotg *hsotg,
#endif
dma_sync_single_for_device(hsotg->dev,
- qh->desc_list_dma +
- (idx * sizeof(struct dwc2_hcd_dma_desc)),
- sizeof(struct dwc2_hcd_dma_desc),
+ qh->desc_list_dma +
+ (idx * sizeof(struct dwc2_dma_desc)),
+ sizeof(struct dwc2_dma_desc),
DMA_TO_DEVICE);
}
@@ -645,8 +644,8 @@ static void dwc2_init_isoc_dma_desc(struct dwc2_hsotg *hsotg,
qh->desc_list[idx].status |= HOST_DMA_IOC;
dma_sync_single_for_device(hsotg->dev,
qh->desc_list_dma + (idx *
- sizeof(struct dwc2_hcd_dma_desc)),
- sizeof(struct dwc2_hcd_dma_desc),
+ sizeof(struct dwc2_dma_desc)),
+ sizeof(struct dwc2_dma_desc),
DMA_TO_DEVICE);
}
#else
@@ -679,8 +678,8 @@ static void dwc2_init_isoc_dma_desc(struct dwc2_hsotg *hsotg,
qh->desc_list[idx].status |= HOST_DMA_IOC;
dma_sync_single_for_device(hsotg->dev,
qh->desc_list_dma +
- (idx * sizeof(struct dwc2_hcd_dma_desc)),
- sizeof(struct dwc2_hcd_dma_desc),
+ (idx * sizeof(struct dwc2_dma_desc)),
+ sizeof(struct dwc2_dma_desc),
DMA_TO_DEVICE);
#endif
}
@@ -690,11 +689,11 @@ static void dwc2_fill_host_dma_desc(struct dwc2_hsotg *hsotg,
struct dwc2_qtd *qtd, struct dwc2_qh *qh,
int n_desc)
{
- struct dwc2_hcd_dma_desc *dma_desc = &qh->desc_list[n_desc];
+ struct dwc2_dma_desc *dma_desc = &qh->desc_list[n_desc];
int len = chan->xfer_len;
- if (len > MAX_DMA_DESC_SIZE - (chan->max_packet - 1))
- len = MAX_DMA_DESC_SIZE - (chan->max_packet - 1);
+ if (len > HOST_DMA_NBYTES_LIMIT - (chan->max_packet - 1))
+ len = HOST_DMA_NBYTES_LIMIT - (chan->max_packet - 1);
if (chan->ep_is_in) {
int num_packets;
@@ -721,8 +720,8 @@ static void dwc2_fill_host_dma_desc(struct dwc2_hsotg *hsotg,
dma_sync_single_for_device(hsotg->dev,
qh->desc_list_dma +
- (n_desc * sizeof(struct dwc2_hcd_dma_desc)),
- sizeof(struct dwc2_hcd_dma_desc),
+ (n_desc * sizeof(struct dwc2_dma_desc)),
+ sizeof(struct dwc2_dma_desc),
DMA_TO_DEVICE);
/*
@@ -776,10 +775,10 @@ static void dwc2_init_non_isoc_dma_desc(struct dwc2_hsotg *hsotg,
n_desc - 1,
&qh->desc_list[n_desc - 1]);
dma_sync_single_for_device(hsotg->dev,
- qh->desc_list_dma +
+ qh->desc_list_dma +
((n_desc - 1) *
- sizeof(struct dwc2_hcd_dma_desc)),
- sizeof(struct dwc2_hcd_dma_desc),
+ sizeof(struct dwc2_dma_desc)),
+ sizeof(struct dwc2_dma_desc),
DMA_TO_DEVICE);
}
dwc2_fill_host_dma_desc(hsotg, chan, qtd, qh, n_desc);
@@ -808,16 +807,16 @@ static void dwc2_init_non_isoc_dma_desc(struct dwc2_hsotg *hsotg,
n_desc - 1, &qh->desc_list[n_desc - 1]);
dma_sync_single_for_device(hsotg->dev,
qh->desc_list_dma + (n_desc - 1) *
- sizeof(struct dwc2_hcd_dma_desc),
- sizeof(struct dwc2_hcd_dma_desc),
+ sizeof(struct dwc2_dma_desc),
+ sizeof(struct dwc2_dma_desc),
DMA_TO_DEVICE);
if (n_desc > 1) {
qh->desc_list[0].status |= HOST_DMA_A;
dev_vdbg(hsotg->dev, "set A bit in desc 0 (%p)\n",
&qh->desc_list[0]);
dma_sync_single_for_device(hsotg->dev,
- qh->desc_list_dma,
- sizeof(struct dwc2_hcd_dma_desc),
+ qh->desc_list_dma,
+ sizeof(struct dwc2_dma_desc),
DMA_TO_DEVICE);
}
chan->ntd = n_desc;
@@ -893,7 +892,7 @@ static int dwc2_cmpl_host_isoc_dma_desc(struct dwc2_hsotg *hsotg,
struct dwc2_qtd *qtd,
struct dwc2_qh *qh, u16 idx)
{
- struct dwc2_hcd_dma_desc *dma_desc;
+ struct dwc2_dma_desc *dma_desc;
struct dwc2_hcd_iso_packet_desc *frame_desc;
u16 remain = 0;
int rc = 0;
@@ -902,8 +901,8 @@ static int dwc2_cmpl_host_isoc_dma_desc(struct dwc2_hsotg *hsotg,
return -EINVAL;
dma_sync_single_for_cpu(hsotg->dev, qh->desc_list_dma + (idx *
- sizeof(struct dwc2_hcd_dma_desc)),
- sizeof(struct dwc2_hcd_dma_desc),
+ sizeof(struct dwc2_dma_desc)),
+ sizeof(struct dwc2_dma_desc),
DMA_FROM_DEVICE);
dma_desc = &qh->desc_list[idx];
@@ -1064,9 +1063,9 @@ stop_scan:
}
static int dwc2_update_non_isoc_urb_state_ddma(struct dwc2_hsotg *hsotg,
- struct dwc2_host_chan *chan,
+ struct dwc2_host_chan *chan,
struct dwc2_qtd *qtd,
- struct dwc2_hcd_dma_desc *dma_desc,
+ struct dwc2_dma_desc *dma_desc,
enum dwc2_halt_status halt_status,
u32 n_bytes, int *xfer_done)
{
@@ -1154,7 +1153,7 @@ static int dwc2_process_non_isoc_desc(struct dwc2_hsotg *hsotg,
{
struct dwc2_qh *qh = chan->qh;
struct dwc2_hcd_urb *urb = qtd->urb;
- struct dwc2_hcd_dma_desc *dma_desc;
+ struct dwc2_dma_desc *dma_desc;
u32 n_bytes;
int failed;
@@ -1165,8 +1164,8 @@ static int dwc2_process_non_isoc_desc(struct dwc2_hsotg *hsotg,
dma_sync_single_for_cpu(hsotg->dev,
qh->desc_list_dma + (desc_num *
- sizeof(struct dwc2_hcd_dma_desc)),
- sizeof(struct dwc2_hcd_dma_desc),
+ sizeof(struct dwc2_dma_desc)),
+ sizeof(struct dwc2_dma_desc),
DMA_FROM_DEVICE);
dma_desc = &qh->desc_list[desc_num];
diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c
index 906f223542ee..28a8210710b1 100644
--- a/drivers/usb/dwc2/hcd_intr.c
+++ b/drivers/usb/dwc2/hcd_intr.c
@@ -60,7 +60,7 @@ static void dwc2_track_missed_sofs(struct dwc2_hsotg *hsotg)
if (expected != curr_frame_number)
dwc2_sch_vdbg(hsotg, "MISSED SOF %04x != %04x\n",
- expected, curr_frame_number);
+ expected, curr_frame_number);
#ifdef CONFIG_USB_DWC2_TRACK_MISSED_SOFS
if (hsotg->frame_num_idx < FRAME_NUM_ARRAY_SIZE) {
@@ -163,7 +163,7 @@ static void dwc2_sof_intr(struct dwc2_hsotg *hsotg)
* (micro)frame
*/
list_move_tail(&qh->qh_list_entry,
- &hsotg->periodic_sched_ready);
+ &hsotg->periodic_sched_ready);
}
}
tr_type = dwc2_hcd_select_transactions(hsotg);
@@ -256,7 +256,7 @@ static void dwc2_perio_tx_fifo_empty_intr(struct dwc2_hsotg *hsotg)
static void dwc2_hprt0_enable(struct dwc2_hsotg *hsotg, u32 hprt0,
u32 *hprt0_modify)
{
- struct dwc2_core_params *params = hsotg->core_params;
+ struct dwc2_core_params *params = &hsotg->params;
int do_reset = 0;
u32 usbcfg;
u32 prtspd;
@@ -297,8 +297,7 @@ static void dwc2_hprt0_enable(struct dwc2_hsotg *hsotg, u32 hprt0,
HCFG_FSLSPCLKSEL_SHIFT;
if (prtspd == HPRT0_SPD_LOW_SPEED &&
- params->host_ls_low_power_phy_clk ==
- DWC2_HOST_LS_LOW_POWER_PHY_CLK_PARAM_6MHZ) {
+ params->host_ls_low_power_phy_clk) {
/* 6 MHZ */
dev_vdbg(hsotg->dev,
"FS_PHY programming HCFG to 6 MHz\n");
@@ -395,10 +394,10 @@ static void dwc2_port_intr(struct dwc2_hsotg *hsotg)
dwc2_hprt0_enable(hsotg, hprt0, &hprt0_modify);
} else {
hsotg->flags.b.port_enable_change = 1;
- if (hsotg->core_params->dma_desc_fs_enable) {
+ if (hsotg->params.dma_desc_fs_enable) {
u32 hcfg;
- hsotg->core_params->dma_desc_enable = 0;
+ hsotg->params.dma_desc_enable = false;
hsotg->new_connection = false;
hcfg = dwc2_readl(hsotg->regs + HCFG);
hcfg &= ~HCFG_DESCDMA;
@@ -442,7 +441,7 @@ static u32 dwc2_get_actual_xfer_length(struct dwc2_hsotg *hsotg,
count = (hctsiz & TSIZ_XFERSIZE_MASK) >>
TSIZ_XFERSIZE_SHIFT;
length = chan->xfer_len - count;
- if (short_read != NULL)
+ if (short_read)
*short_read = (count != 0);
} else if (chan->qh->do_split) {
length = qtd->ssplit_out_xfer_count;
@@ -604,7 +603,7 @@ static enum dwc2_halt_status dwc2_update_isoc_urb_state(
/* Skip whole frame */
if (chan->qh->do_split &&
chan->ep_type == USB_ENDPOINT_XFER_ISOC && chan->ep_is_in &&
- hsotg->core_params->dma_enable > 0) {
+ hsotg->params.host_dma) {
qtd->complete_split = 0;
qtd->isoc_split_offset = 0;
}
@@ -743,7 +742,7 @@ cleanup:
dwc2_hc_cleanup(hsotg, chan);
list_add_tail(&chan->hc_list_entry, &hsotg->free_hc_list);
- if (hsotg->core_params->uframe_sched > 0) {
+ if (hsotg->params.uframe_sched) {
hsotg->available_host_channels++;
} else {
switch (chan->ep_type) {
@@ -789,7 +788,7 @@ static void dwc2_halt_channel(struct dwc2_hsotg *hsotg,
if (dbg_hc(chan))
dev_vdbg(hsotg->dev, "%s()\n", __func__);
- if (hsotg->core_params->dma_enable > 0) {
+ if (hsotg->params.host_dma) {
if (dbg_hc(chan))
dev_vdbg(hsotg->dev, "DMA enabled\n");
dwc2_release_channel(hsotg, chan, qtd, halt_status);
@@ -823,7 +822,7 @@ static void dwc2_halt_channel(struct dwc2_hsotg *hsotg,
* processed.
*/
list_move_tail(&chan->qh->qh_list_entry,
- &hsotg->periodic_sched_assigned);
+ &hsotg->periodic_sched_assigned);
/*
* Make sure the Periodic Tx FIFO Empty interrupt is
@@ -915,6 +914,8 @@ static int dwc2_xfercomp_isoc_split_in(struct dwc2_hsotg *hsotg,
{
struct dwc2_hcd_iso_packet_desc *frame_desc;
u32 len;
+ u32 hctsiz;
+ u32 pid;
if (!qtd->urb)
return 0;
@@ -932,7 +933,10 @@ static int dwc2_xfercomp_isoc_split_in(struct dwc2_hsotg *hsotg,
qtd->isoc_split_offset += len;
- if (frame_desc->actual_length >= frame_desc->length) {
+ hctsiz = dwc2_readl(hsotg->regs + HCTSIZ(chnum));
+ pid = (hctsiz & TSIZ_SC_MC_PID_MASK) >> TSIZ_SC_MC_PID_SHIFT;
+
+ if (frame_desc->actual_length >= frame_desc->length || pid == 0) {
frame_desc->status = 0;
qtd->isoc_frame_index++;
qtd->complete_split = 0;
@@ -974,7 +978,7 @@ static void dwc2_hc_xfercomp_intr(struct dwc2_hsotg *hsotg,
pipe_type = dwc2_hcd_get_pipe_type(&urb->pipe_info);
- if (hsotg->core_params->dma_desc_enable > 0) {
+ if (hsotg->params.dma_desc_enable) {
dwc2_hcd_complete_xfer_ddma(hsotg, chan, chnum, halt_status);
if (pipe_type == USB_ENDPOINT_XFER_ISOC)
/* Do not disable the interrupt, just clear it */
@@ -985,7 +989,7 @@ static void dwc2_hc_xfercomp_intr(struct dwc2_hsotg *hsotg,
/* Handle xfer complete on CSPLIT */
if (chan->qh->do_split) {
if (chan->ep_type == USB_ENDPOINT_XFER_ISOC && chan->ep_is_in &&
- hsotg->core_params->dma_enable > 0) {
+ hsotg->params.host_dma) {
if (qtd->complete_split &&
dwc2_xfercomp_isoc_split_in(hsotg, chan, chnum,
qtd))
@@ -1073,7 +1077,8 @@ static void dwc2_hc_xfercomp_intr(struct dwc2_hsotg *hsotg,
dev_vdbg(hsotg->dev, " Isochronous transfer complete\n");
if (qtd->isoc_split_pos == DWC2_HCSPLT_XACTPOS_ALL)
halt_status = dwc2_update_isoc_urb_state(hsotg, chan,
- chnum, qtd, DWC2_HC_XFER_COMPLETE);
+ chnum, qtd,
+ DWC2_HC_XFER_COMPLETE);
dwc2_complete_periodic_xfer(hsotg, chan, chnum, qtd,
halt_status);
break;
@@ -1097,7 +1102,7 @@ static void dwc2_hc_stall_intr(struct dwc2_hsotg *hsotg,
dev_dbg(hsotg->dev, "--Host Channel %d Interrupt: STALL Received--\n",
chnum);
- if (hsotg->core_params->dma_desc_enable > 0) {
+ if (hsotg->params.dma_desc_enable) {
dwc2_hcd_complete_xfer_ddma(hsotg, chan, chnum,
DWC2_HC_XFER_STALL);
goto handle_stall_done;
@@ -1207,7 +1212,7 @@ static void dwc2_hc_nak_intr(struct dwc2_hsotg *hsotg,
switch (dwc2_hcd_get_pipe_type(&qtd->urb->pipe_info)) {
case USB_ENDPOINT_XFER_CONTROL:
case USB_ENDPOINT_XFER_BULK:
- if (hsotg->core_params->dma_enable > 0 && chan->ep_is_in) {
+ if (hsotg->params.host_dma && chan->ep_is_in) {
/*
* NAK interrupts are enabled on bulk/control IN
* transfers in DMA mode for the sole purpose of
@@ -1353,7 +1358,7 @@ static void dwc2_hc_nyet_intr(struct dwc2_hsotg *hsotg,
*/
if (chan->do_split && chan->complete_split) {
if (chan->ep_is_in && chan->ep_type == USB_ENDPOINT_XFER_ISOC &&
- hsotg->core_params->dma_enable > 0) {
+ hsotg->params.host_dma) {
qtd->complete_split = 0;
qtd->isoc_split_offset = 0;
qtd->isoc_frame_index++;
@@ -1374,7 +1379,7 @@ static void dwc2_hc_nyet_intr(struct dwc2_hsotg *hsotg,
struct dwc2_qh *qh = chan->qh;
bool past_end;
- if (hsotg->core_params->uframe_sched <= 0) {
+ if (!hsotg->params.uframe_sched) {
int frnum = dwc2_hcd_get_frame_number(hsotg);
/* Don't have num_hs_transfers; simple logic */
@@ -1384,22 +1389,27 @@ static void dwc2_hc_nyet_intr(struct dwc2_hsotg *hsotg,
int end_frnum;
/*
- * Figure out the end frame based on schedule.
- *
- * We don't want to go on trying again and again
- * forever. Let's stop when we've done all the
- * transfers that were scheduled.
- *
- * We're going to be comparing start_active_frame
- * and next_active_frame, both of which are 1
- * before the time the packet goes on the wire,
- * so that cancels out. Basically if had 1
- * transfer and we saw 1 NYET then we're done.
- * We're getting a NYET here so if next >=
- * (start + num_transfers) we're done. The
- * complexity is that for all but ISOC_OUT we
- * skip one slot.
- */
+ * Figure out the end frame based on
+ * schedule.
+ *
+ * We don't want to go on trying again
+ * and again forever. Let's stop when
+ * we've done all the transfers that
+ * were scheduled.
+ *
+ * We're going to be comparing
+ * start_active_frame and
+ * next_active_frame, both of which
+ * are 1 before the time the packet
+ * goes on the wire, so that cancels
+ * out. Basically if had 1 transfer
+ * and we saw 1 NYET then we're done.
+ * We're getting a NYET here so if
+ * next >= (start + num_transfers)
+ * we're done. The complexity is that
+ * for all but ISOC_OUT we skip one
+ * slot.
+ */
end_frnum = dwc2_frame_num_inc(
qh->start_active_frame,
qh->num_hs_transfers);
@@ -1467,7 +1477,7 @@ static void dwc2_hc_babble_intr(struct dwc2_hsotg *hsotg,
dwc2_hc_handle_tt_clear(hsotg, chan, qtd);
- if (hsotg->core_params->dma_desc_enable > 0) {
+ if (hsotg->params.dma_desc_enable) {
dwc2_hcd_complete_xfer_ddma(hsotg, chan, chnum,
DWC2_HC_XFER_BABBLE_ERR);
goto disable_int;
@@ -1572,7 +1582,7 @@ static void dwc2_hc_ahberr_intr(struct dwc2_hsotg *hsotg,
dev_err(hsotg->dev, " Interval: %d\n", urb->interval);
/* Core halts the channel for Descriptor DMA mode */
- if (hsotg->core_params->dma_desc_enable > 0) {
+ if (hsotg->params.dma_desc_enable) {
dwc2_hcd_complete_xfer_ddma(hsotg, chan, chnum,
DWC2_HC_XFER_AHB_ERR);
goto handle_ahberr_done;
@@ -1604,7 +1614,7 @@ static void dwc2_hc_xacterr_intr(struct dwc2_hsotg *hsotg,
dwc2_hc_handle_tt_clear(hsotg, chan, qtd);
- if (hsotg->core_params->dma_desc_enable > 0) {
+ if (hsotg->params.dma_desc_enable) {
dwc2_hcd_complete_xfer_ddma(hsotg, chan, chnum,
DWC2_HC_XFER_XACT_ERR);
goto handle_xacterr_done;
@@ -1615,7 +1625,6 @@ static void dwc2_hc_xacterr_intr(struct dwc2_hsotg *hsotg,
case USB_ENDPOINT_XFER_BULK:
qtd->error_count++;
if (!chan->qh->ping_state) {
-
dwc2_update_urb_state_abn(hsotg, chan, chnum, qtd->urb,
qtd, DWC2_HC_XFER_XACT_ERR);
dwc2_hcd_save_data_toggle(hsotg, chan, chnum, qtd);
@@ -1640,7 +1649,7 @@ static void dwc2_hc_xacterr_intr(struct dwc2_hsotg *hsotg,
enum dwc2_halt_status halt_status;
halt_status = dwc2_update_isoc_urb_state(hsotg, chan,
- chnum, qtd, DWC2_HC_XFER_XACT_ERR);
+ chnum, qtd, DWC2_HC_XFER_XACT_ERR);
dwc2_halt_channel(hsotg, chan, qtd, halt_status);
}
break;
@@ -1798,8 +1807,8 @@ static void dwc2_hc_chhltd_intr_dma(struct dwc2_hsotg *hsotg,
if (chan->halt_status == DWC2_HC_XFER_URB_DEQUEUE ||
(chan->halt_status == DWC2_HC_XFER_AHB_ERR &&
- hsotg->core_params->dma_desc_enable <= 0)) {
- if (hsotg->core_params->dma_desc_enable > 0)
+ !hsotg->params.dma_desc_enable)) {
+ if (hsotg->params.dma_desc_enable)
dwc2_hcd_complete_xfer_ddma(hsotg, chan, chnum,
chan->halt_status);
else
@@ -1830,7 +1839,7 @@ static void dwc2_hc_chhltd_intr_dma(struct dwc2_hsotg *hsotg,
} else if (chan->hcint & HCINTMSK_STALL) {
dwc2_hc_stall_intr(hsotg, chan, chnum, qtd);
} else if ((chan->hcint & HCINTMSK_XACTERR) &&
- hsotg->core_params->dma_desc_enable <= 0) {
+ !hsotg->params.dma_desc_enable) {
if (out_nak_enh) {
if (chan->hcint &
(HCINTMSK_NYET | HCINTMSK_NAK | HCINTMSK_ACK)) {
@@ -1850,10 +1859,10 @@ static void dwc2_hc_chhltd_intr_dma(struct dwc2_hsotg *hsotg,
*/
dwc2_hc_xacterr_intr(hsotg, chan, chnum, qtd);
} else if ((chan->hcint & HCINTMSK_XCS_XACT) &&
- hsotg->core_params->dma_desc_enable > 0) {
+ hsotg->params.dma_desc_enable) {
dwc2_hc_xacterr_intr(hsotg, chan, chnum, qtd);
} else if ((chan->hcint & HCINTMSK_AHBERR) &&
- hsotg->core_params->dma_desc_enable > 0) {
+ hsotg->params.dma_desc_enable) {
dwc2_hc_ahberr_intr(hsotg, chan, chnum, qtd);
} else if (chan->hcint & HCINTMSK_BBLERR) {
dwc2_hc_babble_intr(hsotg, chan, chnum, qtd);
@@ -1946,7 +1955,7 @@ static void dwc2_hc_chhltd_intr(struct dwc2_hsotg *hsotg,
dev_vdbg(hsotg->dev, "--Host Channel %d Interrupt: Channel Halted--\n",
chnum);
- if (hsotg->core_params->dma_enable > 0) {
+ if (hsotg->params.host_dma) {
dwc2_hc_chhltd_intr_dma(hsotg, chan, chnum, qtd);
} else {
if (!dwc2_halt_status_ok(hsotg, chan, chnum, qtd))
@@ -1965,7 +1974,7 @@ static bool dwc2_check_qtd_still_ok(struct dwc2_qtd *qtd, struct dwc2_qh *qh)
{
struct dwc2_qtd *cur_head;
- if (qh == NULL)
+ if (!qh)
return false;
cur_head = list_first_entry(&qh->qtd_list, struct dwc2_qtd,
@@ -2023,7 +2032,7 @@ static void dwc2_hc_n_intr(struct dwc2_hsotg *hsotg, int chnum)
* interrupt unmasked
*/
WARN_ON(hcint != HCINTMSK_CHHLTD);
- if (hsotg->core_params->dma_desc_enable > 0)
+ if (hsotg->params.dma_desc_enable)
dwc2_hcd_complete_xfer_ddma(hsotg, chan, chnum,
chan->halt_status);
else
@@ -2051,7 +2060,7 @@ static void dwc2_hc_n_intr(struct dwc2_hsotg *hsotg, int chnum)
qtd = list_first_entry(&chan->qh->qtd_list, struct dwc2_qtd,
qtd_list_entry);
- if (hsotg->core_params->dma_enable <= 0) {
+ if (!hsotg->params.host_dma) {
if ((hcint & HCINTMSK_CHHLTD) && hcint != HCINTMSK_CHHLTD)
hcint &= ~HCINTMSK_CHHLTD;
}
@@ -2156,7 +2165,7 @@ static void dwc2_hc_intr(struct dwc2_hsotg *hsotg)
}
}
- for (i = 0; i < hsotg->core_params->host_channels; i++) {
+ for (i = 0; i < hsotg->params.host_channels; i++) {
if (haint & (1 << i))
dwc2_hc_n_intr(hsotg, i);
}
diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c
index 13754353251f..3ae8b1bbaa55 100644
--- a/drivers/usb/dwc2/hcd_queue.c
+++ b/drivers/usb/dwc2/hcd_queue.c
@@ -75,15 +75,14 @@ static int dwc2_periodic_channel_available(struct dwc2_hsotg *hsotg)
int status;
int num_channels;
- num_channels = hsotg->core_params->host_channels;
- if (hsotg->periodic_channels + hsotg->non_periodic_channels <
- num_channels
- && hsotg->periodic_channels < num_channels - 1) {
+ num_channels = hsotg->params.host_channels;
+ if ((hsotg->periodic_channels + hsotg->non_periodic_channels <
+ num_channels) && (hsotg->periodic_channels < num_channels - 1)) {
status = 0;
} else {
dev_dbg(hsotg->dev,
- "%s: Total channels: %d, Periodic: %d, "
- "Non-periodic: %d\n", __func__, num_channels,
+ "%s: Total channels: %d, Periodic: %d, Non-periodic: %d\n",
+ __func__, num_channels,
hsotg->periodic_channels, hsotg->non_periodic_channels);
status = -ENOSPC;
}
@@ -355,6 +354,37 @@ static void pmap_unschedule(unsigned long *map, int bits_per_period,
}
}
+/**
+ * dwc2_get_ls_map() - Get the map used for the given qh
+ *
+ * @hsotg: The HCD state structure for the DWC OTG controller.
+ * @qh: QH for the periodic transfer.
+ *
+ * We'll always get the periodic map out of our TT. Note that even if we're
+ * running the host straight in low speed / full speed mode it appears as if
+ * a TT is allocated for us, so we'll use it. If that ever changes we can
+ * add logic here to get a map out of "hsotg" if !qh->do_split.
+ *
+ * Returns: the map or NULL if a map couldn't be found.
+ */
+static unsigned long *dwc2_get_ls_map(struct dwc2_hsotg *hsotg,
+ struct dwc2_qh *qh)
+{
+ unsigned long *map;
+
+ /* Don't expect to be missing a TT and be doing low speed scheduling */
+ if (WARN_ON(!qh->dwc_tt))
+ return NULL;
+
+ /* 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;
+
+ return map;
+}
+
+#ifdef DWC2_PRINT_SCHEDULE
/*
* cat_printf() - A printf() + strcat() helper
*
@@ -454,36 +484,6 @@ static void pmap_print(unsigned long *map, int bits_per_period,
}
}
-/**
- * dwc2_get_ls_map() - Get the map used for the given qh
- *
- * @hsotg: The HCD state structure for the DWC OTG controller.
- * @qh: QH for the periodic transfer.
- *
- * We'll always get the periodic map out of our TT. Note that even if we're
- * running the host straight in low speed / full speed mode it appears as if
- * a TT is allocated for us, so we'll use it. If that ever changes we can
- * add logic here to get a map out of "hsotg" if !qh->do_split.
- *
- * Returns: the map or NULL if a map couldn't be found.
- */
-static unsigned long *dwc2_get_ls_map(struct dwc2_hsotg *hsotg,
- struct dwc2_qh *qh)
-{
- unsigned long *map;
-
- /* Don't expect to be missing a TT and be doing low speed scheduling */
- if (WARN_ON(!qh->dwc_tt))
- return NULL;
-
- /* 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;
-
- return map;
-}
-
struct dwc2_qh_print_data {
struct dwc2_hsotg *hsotg;
struct dwc2_qh *qh;
@@ -519,9 +519,6 @@ static void dwc2_qh_schedule_print(struct dwc2_hsotg *hsotg,
* If we don't have tracing turned on, don't run unless the special
* define is turned on.
*/
-#ifndef DWC2_PRINT_SCHEDULE
- return;
-#endif
if (qh->schedule_low_speed) {
unsigned long *map = dwc2_get_ls_map(hsotg, qh);
@@ -559,8 +556,11 @@ static void dwc2_qh_schedule_print(struct dwc2_hsotg *hsotg,
DWC2_HS_SCHEDULE_UFRAMES, "uFrame", "us",
dwc2_qh_print, &print_data);
}
-
}
+#else
+static inline void dwc2_qh_schedule_print(struct dwc2_hsotg *hsotg,
+ struct dwc2_qh *qh) {};
+#endif
/**
* dwc2_ls_pmap_schedule() - Schedule a low speed QH
@@ -584,7 +584,7 @@ static int dwc2_ls_pmap_schedule(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
unsigned long *map = dwc2_get_ls_map(hsotg, qh);
int slice;
- if (map == NULL)
+ if (!map)
return -EINVAL;
/*
@@ -623,7 +623,7 @@ static void dwc2_ls_pmap_unschedule(struct dwc2_hsotg *hsotg,
unsigned long *map = dwc2_get_ls_map(hsotg, qh);
/* Schedule should have failed, so no worries about no error code */
- if (map == NULL)
+ if (!map)
return;
pmap_unschedule(map, DWC2_LS_PERIODIC_SLICES_PER_FRAME,
@@ -1104,7 +1104,7 @@ static void dwc2_pick_first_frame(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
next_active_frame = earliest_frame;
/* Get the "no microframe schduler" out of the way... */
- if (hsotg->core_params->uframe_sched <= 0) {
+ if (!hsotg->params.uframe_sched) {
if (qh->do_split)
/* Splits are active at microframe 0 minus 1 */
next_active_frame |= 0x7;
@@ -1179,7 +1179,7 @@ exit:
qh->start_active_frame = next_active_frame;
dwc2_sch_vdbg(hsotg, "QH=%p First fn=%04x nxt=%04x\n",
- qh, frame_number, qh->next_active_frame);
+ qh, frame_number, qh->next_active_frame);
}
/**
@@ -1197,7 +1197,7 @@ static int dwc2_do_reserve(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
{
int status;
- if (hsotg->core_params->uframe_sched > 0) {
+ if (hsotg->params.uframe_sched) {
status = dwc2_uframe_schedule(hsotg, qh);
} else {
status = dwc2_periodic_channel_available(hsotg);
@@ -1218,7 +1218,7 @@ static int dwc2_do_reserve(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
return status;
}
- if (hsotg->core_params->uframe_sched <= 0)
+ if (!hsotg->params.uframe_sched)
/* Reserve periodic channel */
hsotg->periodic_channels++;
@@ -1254,7 +1254,7 @@ static void dwc2_do_unreserve(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
/* Update claimed usecs per (micro)frame */
hsotg->periodic_usecs -= qh->host_us;
- if (hsotg->core_params->uframe_sched > 0) {
+ if (hsotg->params.uframe_sched) {
dwc2_uframe_unschedule(hsotg, qh);
} else {
/* Release periodic channel reservation */
@@ -1328,7 +1328,7 @@ static int dwc2_check_max_xfer_size(struct dwc2_hsotg *hsotg,
int status = 0;
max_xfer_size = dwc2_max_packet(qh->maxp) * dwc2_hb_mult(qh->maxp);
- max_channel_xfer_size = hsotg->core_params->max_transfer_size;
+ max_channel_xfer_size = hsotg->params.max_transfer_size;
if (max_xfer_size > max_channel_xfer_size) {
dev_err(hsotg->dev,
@@ -1391,7 +1391,7 @@ static int dwc2_schedule_periodic(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
qh->unreserve_pending = 0;
- if (hsotg->core_params->dma_desc_enable > 0)
+ if (hsotg->params.dma_desc_enable)
/* Don't rely on SOF and start in ready schedule */
list_add_tail(&qh->qh_list_entry, &hsotg->periodic_sched_ready);
else
@@ -1498,7 +1498,6 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
device_ns += dwc_tt->usb_tt->think_time;
qh->device_us = NS_TO_US(device_ns);
-
qh->device_interval = urb->interval;
qh->host_interval = urb->interval * (do_split ? 8 : 1);
@@ -1584,7 +1583,7 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
* Return: Pointer to the newly allocated QH, or NULL on error
*/
struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
- struct dwc2_hcd_urb *urb,
+ struct dwc2_hcd_urb *urb,
gfp_t mem_flags)
{
struct dwc2_qh *qh;
@@ -1599,7 +1598,7 @@ struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
dwc2_qh_init(hsotg, qh, urb, mem_flags);
- if (hsotg->core_params->dma_desc_enable > 0 &&
+ if (hsotg->params.dma_desc_enable &&
dwc2_hcd_qh_init_ddma(hsotg, qh, mem_flags) < 0) {
dwc2_hcd_qh_free(hsotg, qh);
return NULL;
@@ -1711,7 +1710,7 @@ void dwc2_hcd_qh_unlink(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
dwc2_deschedule_periodic(hsotg, qh);
hsotg->periodic_qh_count--;
if (!hsotg->periodic_qh_count &&
- hsotg->core_params->dma_desc_enable <= 0) {
+ !hsotg->params.dma_desc_enable) {
intr_mask = dwc2_readl(hsotg->regs + GINTMSK);
intr_mask &= ~GINTSTS_SOF;
dwc2_writel(intr_mask, hsotg->regs + GINTMSK);
@@ -1738,7 +1737,7 @@ void dwc2_hcd_qh_unlink(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
* Return: number missed by (or 0 if we didn't miss).
*/
static int dwc2_next_for_periodic_split(struct dwc2_hsotg *hsotg,
- struct dwc2_qh *qh, u16 frame_number)
+ struct dwc2_qh *qh, u16 frame_number)
{
u16 old_frame = qh->next_active_frame;
u16 prev_frame_number = dwc2_frame_num_dec(frame_number, 1);
@@ -1801,7 +1800,7 @@ static int dwc2_next_for_periodic_split(struct dwc2_hsotg *hsotg,
* Return: number missed by (or 0 if we didn't miss).
*/
static int dwc2_next_periodic_start(struct dwc2_hsotg *hsotg,
- struct dwc2_qh *qh, u16 frame_number)
+ struct dwc2_qh *qh, u16 frame_number)
{
int missed = 0;
u16 interval = qh->host_interval;
@@ -1923,7 +1922,7 @@ void dwc2_hcd_qh_deactivate(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
missed = dwc2_next_periodic_start(hsotg, qh, frame_number);
dwc2_sch_vdbg(hsotg,
- "QH=%p next(%d) fn=%04x, sch=%04x=>%04x (%+d) miss=%d %s\n",
+ "QH=%p next(%d) fn=%04x, sch=%04x=>%04x (%+d) miss=%d %s\n",
qh, sched_next_periodic_split, frame_number, old_frame,
qh->next_active_frame,
dwc2_frame_num_dec(qh->next_active_frame, old_frame),
diff --git a/drivers/usb/dwc2/hw.h b/drivers/usb/dwc2/hw.h
index 91058441e62a..bde72489ae66 100644
--- a/drivers/usb/dwc2/hw.h
+++ b/drivers/usb/dwc2/hw.h
@@ -40,37 +40,37 @@
#define HSOTG_REG(x) (x)
#define GOTGCTL HSOTG_REG(0x000)
-#define GOTGCTL_CHIRPEN (1 << 27)
+#define GOTGCTL_CHIRPEN BIT(27)
#define GOTGCTL_MULT_VALID_BC_MASK (0x1f << 22)
#define GOTGCTL_MULT_VALID_BC_SHIFT 22
-#define GOTGCTL_OTGVER (1 << 20)
-#define GOTGCTL_BSESVLD (1 << 19)
-#define GOTGCTL_ASESVLD (1 << 18)
-#define GOTGCTL_DBNC_SHORT (1 << 17)
-#define GOTGCTL_CONID_B (1 << 16)
-#define GOTGCTL_DBNCE_FLTR_BYPASS (1 << 15)
-#define GOTGCTL_DEVHNPEN (1 << 11)
-#define GOTGCTL_HSTSETHNPEN (1 << 10)
-#define GOTGCTL_HNPREQ (1 << 9)
-#define GOTGCTL_HSTNEGSCS (1 << 8)
-#define GOTGCTL_SESREQ (1 << 1)
-#define GOTGCTL_SESREQSCS (1 << 0)
+#define GOTGCTL_OTGVER BIT(20)
+#define GOTGCTL_BSESVLD BIT(19)
+#define GOTGCTL_ASESVLD BIT(18)
+#define GOTGCTL_DBNC_SHORT BIT(17)
+#define GOTGCTL_CONID_B BIT(16)
+#define GOTGCTL_DBNCE_FLTR_BYPASS BIT(15)
+#define GOTGCTL_DEVHNPEN BIT(11)
+#define GOTGCTL_HSTSETHNPEN BIT(10)
+#define GOTGCTL_HNPREQ BIT(9)
+#define GOTGCTL_HSTNEGSCS BIT(8)
+#define GOTGCTL_SESREQ BIT(1)
+#define GOTGCTL_SESREQSCS BIT(0)
#define GOTGINT HSOTG_REG(0x004)
-#define GOTGINT_DBNCE_DONE (1 << 19)
-#define GOTGINT_A_DEV_TOUT_CHG (1 << 18)
-#define GOTGINT_HST_NEG_DET (1 << 17)
-#define GOTGINT_HST_NEG_SUC_STS_CHNG (1 << 9)
-#define GOTGINT_SES_REQ_SUC_STS_CHNG (1 << 8)
-#define GOTGINT_SES_END_DET (1 << 2)
+#define GOTGINT_DBNCE_DONE BIT(19)
+#define GOTGINT_A_DEV_TOUT_CHG BIT(18)
+#define GOTGINT_HST_NEG_DET BIT(17)
+#define GOTGINT_HST_NEG_SUC_STS_CHNG BIT(9)
+#define GOTGINT_SES_REQ_SUC_STS_CHNG BIT(8)
+#define GOTGINT_SES_END_DET BIT(2)
#define GAHBCFG HSOTG_REG(0x008)
-#define GAHBCFG_AHB_SINGLE (1 << 23)
-#define GAHBCFG_NOTI_ALL_DMA_WRIT (1 << 22)
-#define GAHBCFG_REM_MEM_SUPP (1 << 21)
-#define GAHBCFG_P_TXF_EMP_LVL (1 << 8)
-#define GAHBCFG_NP_TXF_EMP_LVL (1 << 7)
-#define GAHBCFG_DMA_EN (1 << 5)
+#define GAHBCFG_AHB_SINGLE BIT(23)
+#define GAHBCFG_NOTI_ALL_DMA_WRIT BIT(22)
+#define GAHBCFG_REM_MEM_SUPP BIT(21)
+#define GAHBCFG_P_TXF_EMP_LVL BIT(8)
+#define GAHBCFG_NP_TXF_EMP_LVL BIT(7)
+#define GAHBCFG_DMA_EN BIT(5)
#define GAHBCFG_HBSTLEN_MASK (0xf << 1)
#define GAHBCFG_HBSTLEN_SHIFT 1
#define GAHBCFG_HBSTLEN_SINGLE 0
@@ -78,38 +78,38 @@
#define GAHBCFG_HBSTLEN_INCR4 3
#define GAHBCFG_HBSTLEN_INCR8 5
#define GAHBCFG_HBSTLEN_INCR16 7
-#define GAHBCFG_GLBL_INTR_EN (1 << 0)
+#define GAHBCFG_GLBL_INTR_EN BIT(0)
#define GAHBCFG_CTRL_MASK (GAHBCFG_P_TXF_EMP_LVL | \
GAHBCFG_NP_TXF_EMP_LVL | \
GAHBCFG_DMA_EN | \
GAHBCFG_GLBL_INTR_EN)
#define GUSBCFG HSOTG_REG(0x00C)
-#define GUSBCFG_FORCEDEVMODE (1 << 30)
-#define GUSBCFG_FORCEHOSTMODE (1 << 29)
-#define GUSBCFG_TXENDDELAY (1 << 28)
-#define GUSBCFG_ICTRAFFICPULLREMOVE (1 << 27)
-#define GUSBCFG_ICUSBCAP (1 << 26)
-#define GUSBCFG_ULPI_INT_PROT_DIS (1 << 25)
-#define GUSBCFG_INDICATORPASSTHROUGH (1 << 24)
-#define GUSBCFG_INDICATORCOMPLEMENT (1 << 23)
-#define GUSBCFG_TERMSELDLPULSE (1 << 22)
-#define GUSBCFG_ULPI_INT_VBUS_IND (1 << 21)
-#define GUSBCFG_ULPI_EXT_VBUS_DRV (1 << 20)
-#define GUSBCFG_ULPI_CLK_SUSP_M (1 << 19)
-#define GUSBCFG_ULPI_AUTO_RES (1 << 18)
-#define GUSBCFG_ULPI_FS_LS (1 << 17)
-#define GUSBCFG_OTG_UTMI_FS_SEL (1 << 16)
-#define GUSBCFG_PHY_LP_CLK_SEL (1 << 15)
+#define GUSBCFG_FORCEDEVMODE BIT(30)
+#define GUSBCFG_FORCEHOSTMODE BIT(29)
+#define GUSBCFG_TXENDDELAY BIT(28)
+#define GUSBCFG_ICTRAFFICPULLREMOVE BIT(27)
+#define GUSBCFG_ICUSBCAP BIT(26)
+#define GUSBCFG_ULPI_INT_PROT_DIS BIT(25)
+#define GUSBCFG_INDICATORPASSTHROUGH BIT(24)
+#define GUSBCFG_INDICATORCOMPLEMENT BIT(23)
+#define GUSBCFG_TERMSELDLPULSE BIT(22)
+#define GUSBCFG_ULPI_INT_VBUS_IND BIT(21)
+#define GUSBCFG_ULPI_EXT_VBUS_DRV BIT(20)
+#define GUSBCFG_ULPI_CLK_SUSP_M BIT(19)
+#define GUSBCFG_ULPI_AUTO_RES BIT(18)
+#define GUSBCFG_ULPI_FS_LS BIT(17)
+#define GUSBCFG_OTG_UTMI_FS_SEL BIT(16)
+#define GUSBCFG_PHY_LP_CLK_SEL BIT(15)
#define GUSBCFG_USBTRDTIM_MASK (0xf << 10)
#define GUSBCFG_USBTRDTIM_SHIFT 10
-#define GUSBCFG_HNPCAP (1 << 9)
-#define GUSBCFG_SRPCAP (1 << 8)
-#define GUSBCFG_DDRSEL (1 << 7)
-#define GUSBCFG_PHYSEL (1 << 6)
-#define GUSBCFG_FSINTF (1 << 5)
-#define GUSBCFG_ULPI_UTMI_SEL (1 << 4)
-#define GUSBCFG_PHYIF16 (1 << 3)
+#define GUSBCFG_HNPCAP BIT(9)
+#define GUSBCFG_SRPCAP BIT(8)
+#define GUSBCFG_DDRSEL BIT(7)
+#define GUSBCFG_PHYSEL BIT(6)
+#define GUSBCFG_FSINTF BIT(5)
+#define GUSBCFG_ULPI_UTMI_SEL BIT(4)
+#define GUSBCFG_PHYIF16 BIT(3)
#define GUSBCFG_PHYIF8 (0 << 3)
#define GUSBCFG_TOUTCAL_MASK (0x7 << 0)
#define GUSBCFG_TOUTCAL_SHIFT 0
@@ -117,54 +117,54 @@
#define GUSBCFG_TOUTCAL(_x) ((_x) << 0)
#define GRSTCTL HSOTG_REG(0x010)
-#define GRSTCTL_AHBIDLE (1 << 31)
-#define GRSTCTL_DMAREQ (1 << 30)
+#define GRSTCTL_AHBIDLE BIT(31)
+#define GRSTCTL_DMAREQ BIT(30)
#define GRSTCTL_TXFNUM_MASK (0x1f << 6)
#define GRSTCTL_TXFNUM_SHIFT 6
#define GRSTCTL_TXFNUM_LIMIT 0x1f
#define GRSTCTL_TXFNUM(_x) ((_x) << 6)
-#define GRSTCTL_TXFFLSH (1 << 5)
-#define GRSTCTL_RXFFLSH (1 << 4)
-#define GRSTCTL_IN_TKNQ_FLSH (1 << 3)
-#define GRSTCTL_FRMCNTRRST (1 << 2)
-#define GRSTCTL_HSFTRST (1 << 1)
-#define GRSTCTL_CSFTRST (1 << 0)
+#define GRSTCTL_TXFFLSH BIT(5)
+#define GRSTCTL_RXFFLSH BIT(4)
+#define GRSTCTL_IN_TKNQ_FLSH BIT(3)
+#define GRSTCTL_FRMCNTRRST BIT(2)
+#define GRSTCTL_HSFTRST BIT(1)
+#define GRSTCTL_CSFTRST BIT(0)
#define GINTSTS HSOTG_REG(0x014)
#define GINTMSK HSOTG_REG(0x018)
-#define GINTSTS_WKUPINT (1 << 31)
-#define GINTSTS_SESSREQINT (1 << 30)
-#define GINTSTS_DISCONNINT (1 << 29)
-#define GINTSTS_CONIDSTSCHNG (1 << 28)
-#define GINTSTS_LPMTRANRCVD (1 << 27)
-#define GINTSTS_PTXFEMP (1 << 26)
-#define GINTSTS_HCHINT (1 << 25)
-#define GINTSTS_PRTINT (1 << 24)
-#define GINTSTS_RESETDET (1 << 23)
-#define GINTSTS_FET_SUSP (1 << 22)
-#define GINTSTS_INCOMPL_IP (1 << 21)
-#define GINTSTS_INCOMPL_SOOUT (1 << 21)
-#define GINTSTS_INCOMPL_SOIN (1 << 20)
-#define GINTSTS_OEPINT (1 << 19)
-#define GINTSTS_IEPINT (1 << 18)
-#define GINTSTS_EPMIS (1 << 17)
-#define GINTSTS_RESTOREDONE (1 << 16)
-#define GINTSTS_EOPF (1 << 15)
-#define GINTSTS_ISOUTDROP (1 << 14)
-#define GINTSTS_ENUMDONE (1 << 13)
-#define GINTSTS_USBRST (1 << 12)
-#define GINTSTS_USBSUSP (1 << 11)
-#define GINTSTS_ERLYSUSP (1 << 10)
-#define GINTSTS_I2CINT (1 << 9)
-#define GINTSTS_ULPI_CK_INT (1 << 8)
-#define GINTSTS_GOUTNAKEFF (1 << 7)
-#define GINTSTS_GINNAKEFF (1 << 6)
-#define GINTSTS_NPTXFEMP (1 << 5)
-#define GINTSTS_RXFLVL (1 << 4)
-#define GINTSTS_SOF (1 << 3)
-#define GINTSTS_OTGINT (1 << 2)
-#define GINTSTS_MODEMIS (1 << 1)
-#define GINTSTS_CURMODE_HOST (1 << 0)
+#define GINTSTS_WKUPINT BIT(31)
+#define GINTSTS_SESSREQINT BIT(30)
+#define GINTSTS_DISCONNINT BIT(29)
+#define GINTSTS_CONIDSTSCHNG BIT(28)
+#define GINTSTS_LPMTRANRCVD BIT(27)
+#define GINTSTS_PTXFEMP BIT(26)
+#define GINTSTS_HCHINT BIT(25)
+#define GINTSTS_PRTINT BIT(24)
+#define GINTSTS_RESETDET BIT(23)
+#define GINTSTS_FET_SUSP BIT(22)
+#define GINTSTS_INCOMPL_IP BIT(21)
+#define GINTSTS_INCOMPL_SOOUT BIT(21)
+#define GINTSTS_INCOMPL_SOIN BIT(20)
+#define GINTSTS_OEPINT BIT(19)
+#define GINTSTS_IEPINT BIT(18)
+#define GINTSTS_EPMIS BIT(17)
+#define GINTSTS_RESTOREDONE BIT(16)
+#define GINTSTS_EOPF BIT(15)
+#define GINTSTS_ISOUTDROP BIT(14)
+#define GINTSTS_ENUMDONE BIT(13)
+#define GINTSTS_USBRST BIT(12)
+#define GINTSTS_USBSUSP BIT(11)
+#define GINTSTS_ERLYSUSP BIT(10)
+#define GINTSTS_I2CINT BIT(9)
+#define GINTSTS_ULPI_CK_INT BIT(8)
+#define GINTSTS_GOUTNAKEFF BIT(7)
+#define GINTSTS_GINNAKEFF BIT(6)
+#define GINTSTS_NPTXFEMP BIT(5)
+#define GINTSTS_RXFLVL BIT(4)
+#define GINTSTS_SOF BIT(3)
+#define GINTSTS_OTGINT BIT(2)
+#define GINTSTS_MODEMIS BIT(1)
+#define GINTSTS_CURMODE_HOST BIT(0)
#define GRXSTSR HSOTG_REG(0x01C)
#define GRXSTSP HSOTG_REG(0x020)
@@ -208,14 +208,14 @@
#define GNPTXSTS_NP_TXF_SPC_AVAIL_GET(_v) (((_v) >> 0) & 0xffff)
#define GI2CCTL HSOTG_REG(0x0030)
-#define GI2CCTL_BSYDNE (1 << 31)
-#define GI2CCTL_RW (1 << 30)
-#define GI2CCTL_I2CDATSE0 (1 << 28)
+#define GI2CCTL_BSYDNE BIT(31)
+#define GI2CCTL_RW BIT(30)
+#define GI2CCTL_I2CDATSE0 BIT(28)
#define GI2CCTL_I2CDEVADDR_MASK (0x3 << 26)
#define GI2CCTL_I2CDEVADDR_SHIFT 26
-#define GI2CCTL_I2CSUSPCTL (1 << 25)
-#define GI2CCTL_ACK (1 << 24)
-#define GI2CCTL_I2CEN (1 << 23)
+#define GI2CCTL_I2CSUSPCTL BIT(25)
+#define GI2CCTL_ACK BIT(24)
+#define GI2CCTL_I2CEN BIT(23)
#define GI2CCTL_ADDR_MASK (0x7f << 16)
#define GI2CCTL_ADDR_SHIFT 16
#define GI2CCTL_REGADDR_MASK (0xff << 8)
@@ -230,16 +230,16 @@
#define GHWCFG1 HSOTG_REG(0x0044)
#define GHWCFG2 HSOTG_REG(0x0048)
-#define GHWCFG2_OTG_ENABLE_IC_USB (1 << 31)
+#define GHWCFG2_OTG_ENABLE_IC_USB BIT(31)
#define GHWCFG2_DEV_TOKEN_Q_DEPTH_MASK (0x1f << 26)
#define GHWCFG2_DEV_TOKEN_Q_DEPTH_SHIFT 26
#define GHWCFG2_HOST_PERIO_TX_Q_DEPTH_MASK (0x3 << 24)
#define GHWCFG2_HOST_PERIO_TX_Q_DEPTH_SHIFT 24
#define GHWCFG2_NONPERIO_TX_Q_DEPTH_MASK (0x3 << 22)
#define GHWCFG2_NONPERIO_TX_Q_DEPTH_SHIFT 22
-#define GHWCFG2_MULTI_PROC_INT (1 << 20)
-#define GHWCFG2_DYNAMIC_FIFO (1 << 19)
-#define GHWCFG2_PERIO_EP_SUPPORTED (1 << 18)
+#define GHWCFG2_MULTI_PROC_INT BIT(20)
+#define GHWCFG2_DYNAMIC_FIFO BIT(19)
+#define GHWCFG2_PERIO_EP_SUPPORTED BIT(18)
#define GHWCFG2_NUM_HOST_CHAN_MASK (0xf << 14)
#define GHWCFG2_NUM_HOST_CHAN_SHIFT 14
#define GHWCFG2_NUM_DEV_EP_MASK (0xf << 10)
@@ -256,7 +256,7 @@
#define GHWCFG2_HS_PHY_TYPE_UTMI 1
#define GHWCFG2_HS_PHY_TYPE_ULPI 2
#define GHWCFG2_HS_PHY_TYPE_UTMI_ULPI 3
-#define GHWCFG2_POINT2POINT (1 << 5)
+#define GHWCFG2_POINT2POINT BIT(5)
#define GHWCFG2_ARCHITECTURE_MASK (0x3 << 3)
#define GHWCFG2_ARCHITECTURE_SHIFT 3
#define GHWCFG2_SLAVE_ONLY_ARCH 0
@@ -276,32 +276,32 @@
#define GHWCFG3 HSOTG_REG(0x004c)
#define GHWCFG3_DFIFO_DEPTH_MASK (0xffff << 16)
#define GHWCFG3_DFIFO_DEPTH_SHIFT 16
-#define GHWCFG3_OTG_LPM_EN (1 << 15)
-#define GHWCFG3_BC_SUPPORT (1 << 14)
-#define GHWCFG3_OTG_ENABLE_HSIC (1 << 13)
-#define GHWCFG3_ADP_SUPP (1 << 12)
-#define GHWCFG3_SYNCH_RESET_TYPE (1 << 11)
-#define GHWCFG3_OPTIONAL_FEATURES (1 << 10)
-#define GHWCFG3_VENDOR_CTRL_IF (1 << 9)
-#define GHWCFG3_I2C (1 << 8)
-#define GHWCFG3_OTG_FUNC (1 << 7)
+#define GHWCFG3_OTG_LPM_EN BIT(15)
+#define GHWCFG3_BC_SUPPORT BIT(14)
+#define GHWCFG3_OTG_ENABLE_HSIC BIT(13)
+#define GHWCFG3_ADP_SUPP BIT(12)
+#define GHWCFG3_SYNCH_RESET_TYPE BIT(11)
+#define GHWCFG3_OPTIONAL_FEATURES BIT(10)
+#define GHWCFG3_VENDOR_CTRL_IF BIT(9)
+#define GHWCFG3_I2C BIT(8)
+#define GHWCFG3_OTG_FUNC BIT(7)
#define GHWCFG3_PACKET_SIZE_CNTR_WIDTH_MASK (0x7 << 4)
#define GHWCFG3_PACKET_SIZE_CNTR_WIDTH_SHIFT 4
#define GHWCFG3_XFER_SIZE_CNTR_WIDTH_MASK (0xf << 0)
#define GHWCFG3_XFER_SIZE_CNTR_WIDTH_SHIFT 0
#define GHWCFG4 HSOTG_REG(0x0050)
-#define GHWCFG4_DESC_DMA_DYN (1 << 31)
-#define GHWCFG4_DESC_DMA (1 << 30)
+#define GHWCFG4_DESC_DMA_DYN BIT(31)
+#define GHWCFG4_DESC_DMA BIT(30)
#define GHWCFG4_NUM_IN_EPS_MASK (0xf << 26)
#define GHWCFG4_NUM_IN_EPS_SHIFT 26
-#define GHWCFG4_DED_FIFO_EN (1 << 25)
+#define GHWCFG4_DED_FIFO_EN BIT(25)
#define GHWCFG4_DED_FIFO_SHIFT 25
-#define GHWCFG4_SESSION_END_FILT_EN (1 << 24)
-#define GHWCFG4_B_VALID_FILT_EN (1 << 23)
-#define GHWCFG4_A_VALID_FILT_EN (1 << 22)
-#define GHWCFG4_VBUS_VALID_FILT_EN (1 << 21)
-#define GHWCFG4_IDDIG_FILT_EN (1 << 20)
+#define GHWCFG4_SESSION_END_FILT_EN BIT(24)
+#define GHWCFG4_B_VALID_FILT_EN BIT(23)
+#define GHWCFG4_A_VALID_FILT_EN BIT(22)
+#define GHWCFG4_VBUS_VALID_FILT_EN BIT(21)
+#define GHWCFG4_IDDIG_FILT_EN BIT(20)
#define GHWCFG4_NUM_DEV_MODE_CTRL_EP_MASK (0xf << 16)
#define GHWCFG4_NUM_DEV_MODE_CTRL_EP_SHIFT 16
#define GHWCFG4_UTMI_PHY_DATA_WIDTH_MASK (0x3 << 14)
@@ -309,64 +309,64 @@
#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
-#define GHWCFG4_XHIBER (1 << 7)
-#define GHWCFG4_HIBER (1 << 6)
-#define GHWCFG4_MIN_AHB_FREQ (1 << 5)
-#define GHWCFG4_POWER_OPTIMIZ (1 << 4)
+#define GHWCFG4_XHIBER BIT(7)
+#define GHWCFG4_HIBER BIT(6)
+#define GHWCFG4_MIN_AHB_FREQ BIT(5)
+#define GHWCFG4_POWER_OPTIMIZ BIT(4)
#define GHWCFG4_NUM_DEV_PERIO_IN_EP_MASK (0xf << 0)
#define GHWCFG4_NUM_DEV_PERIO_IN_EP_SHIFT 0
#define GLPMCFG HSOTG_REG(0x0054)
-#define GLPMCFG_INV_SEL_HSIC (1 << 31)
-#define GLPMCFG_HSIC_CONNECT (1 << 30)
+#define GLPMCFG_INV_SEL_HSIC BIT(31)
+#define GLPMCFG_HSIC_CONNECT BIT(30)
#define GLPMCFG_RETRY_COUNT_STS_MASK (0x7 << 25)
#define GLPMCFG_RETRY_COUNT_STS_SHIFT 25
-#define GLPMCFG_SEND_LPM (1 << 24)
+#define GLPMCFG_SEND_LPM BIT(24)
#define GLPMCFG_RETRY_COUNT_MASK (0x7 << 21)
#define GLPMCFG_RETRY_COUNT_SHIFT 21
#define GLPMCFG_LPM_CHAN_INDEX_MASK (0xf << 17)
#define GLPMCFG_LPM_CHAN_INDEX_SHIFT 17
-#define GLPMCFG_SLEEP_STATE_RESUMEOK (1 << 16)
-#define GLPMCFG_PRT_SLEEP_STS (1 << 15)
+#define GLPMCFG_SLEEP_STATE_RESUMEOK BIT(16)
+#define GLPMCFG_PRT_SLEEP_STS BIT(15)
#define GLPMCFG_LPM_RESP_MASK (0x3 << 13)
#define GLPMCFG_LPM_RESP_SHIFT 13
#define GLPMCFG_HIRD_THRES_MASK (0x1f << 8)
#define GLPMCFG_HIRD_THRES_SHIFT 8
#define GLPMCFG_HIRD_THRES_EN (0x10 << 8)
-#define GLPMCFG_EN_UTMI_SLEEP (1 << 7)
-#define GLPMCFG_REM_WKUP_EN (1 << 6)
+#define GLPMCFG_EN_UTMI_SLEEP BIT(7)
+#define GLPMCFG_REM_WKUP_EN BIT(6)
#define GLPMCFG_HIRD_MASK (0xf << 2)
#define GLPMCFG_HIRD_SHIFT 2
-#define GLPMCFG_APPL_RESP (1 << 1)
-#define GLPMCFG_LPM_CAP_EN (1 << 0)
+#define GLPMCFG_APPL_RESP BIT(1)
+#define GLPMCFG_LPM_CAP_EN BIT(0)
#define GPWRDN HSOTG_REG(0x0058)
#define GPWRDN_MULT_VAL_ID_BC_MASK (0x1f << 24)
#define GPWRDN_MULT_VAL_ID_BC_SHIFT 24
-#define GPWRDN_ADP_INT (1 << 23)
-#define GPWRDN_BSESSVLD (1 << 22)
-#define GPWRDN_IDSTS (1 << 21)
+#define GPWRDN_ADP_INT BIT(23)
+#define GPWRDN_BSESSVLD BIT(22)
+#define GPWRDN_IDSTS BIT(21)
#define GPWRDN_LINESTATE_MASK (0x3 << 19)
#define GPWRDN_LINESTATE_SHIFT 19
-#define GPWRDN_STS_CHGINT_MSK (1 << 18)
-#define GPWRDN_STS_CHGINT (1 << 17)
-#define GPWRDN_SRP_DET_MSK (1 << 16)
-#define GPWRDN_SRP_DET (1 << 15)
-#define GPWRDN_CONNECT_DET_MSK (1 << 14)
-#define GPWRDN_CONNECT_DET (1 << 13)
-#define GPWRDN_DISCONN_DET_MSK (1 << 12)
-#define GPWRDN_DISCONN_DET (1 << 11)
-#define GPWRDN_RST_DET_MSK (1 << 10)
-#define GPWRDN_RST_DET (1 << 9)
-#define GPWRDN_LNSTSCHG_MSK (1 << 8)
-#define GPWRDN_LNSTSCHG (1 << 7)
-#define GPWRDN_DIS_VBUS (1 << 6)
-#define GPWRDN_PWRDNSWTCH (1 << 5)
-#define GPWRDN_PWRDNRSTN (1 << 4)
-#define GPWRDN_PWRDNCLMP (1 << 3)
-#define GPWRDN_RESTORE (1 << 2)
-#define GPWRDN_PMUACTV (1 << 1)
-#define GPWRDN_PMUINTSEL (1 << 0)
+#define GPWRDN_STS_CHGINT_MSK BIT(18)
+#define GPWRDN_STS_CHGINT BIT(17)
+#define GPWRDN_SRP_DET_MSK BIT(16)
+#define GPWRDN_SRP_DET BIT(15)
+#define GPWRDN_CONNECT_DET_MSK BIT(14)
+#define GPWRDN_CONNECT_DET BIT(13)
+#define GPWRDN_DISCONN_DET_MSK BIT(12)
+#define GPWRDN_DISCONN_DET BIT(11)
+#define GPWRDN_RST_DET_MSK BIT(10)
+#define GPWRDN_RST_DET BIT(9)
+#define GPWRDN_LNSTSCHG_MSK BIT(8)
+#define GPWRDN_LNSTSCHG BIT(7)
+#define GPWRDN_DIS_VBUS BIT(6)
+#define GPWRDN_PWRDNSWTCH BIT(5)
+#define GPWRDN_PWRDNRSTN BIT(4)
+#define GPWRDN_PWRDNCLMP BIT(3)
+#define GPWRDN_RESTORE BIT(2)
+#define GPWRDN_PMUACTV BIT(1)
+#define GPWRDN_PMUINTSEL BIT(0)
#define GDFIFOCFG HSOTG_REG(0x005c)
#define GDFIFOCFG_EPINFOBASE_MASK (0xffff << 16)
@@ -377,16 +377,16 @@
#define ADPCTL HSOTG_REG(0x0060)
#define ADPCTL_AR_MASK (0x3 << 27)
#define ADPCTL_AR_SHIFT 27
-#define ADPCTL_ADP_TMOUT_INT_MSK (1 << 26)
-#define ADPCTL_ADP_SNS_INT_MSK (1 << 25)
-#define ADPCTL_ADP_PRB_INT_MSK (1 << 24)
-#define ADPCTL_ADP_TMOUT_INT (1 << 23)
-#define ADPCTL_ADP_SNS_INT (1 << 22)
-#define ADPCTL_ADP_PRB_INT (1 << 21)
-#define ADPCTL_ADPENA (1 << 20)
-#define ADPCTL_ADPRES (1 << 19)
-#define ADPCTL_ENASNS (1 << 18)
-#define ADPCTL_ENAPRB (1 << 17)
+#define ADPCTL_ADP_TMOUT_INT_MSK BIT(26)
+#define ADPCTL_ADP_SNS_INT_MSK BIT(25)
+#define ADPCTL_ADP_PRB_INT_MSK BIT(24)
+#define ADPCTL_ADP_TMOUT_INT BIT(23)
+#define ADPCTL_ADP_SNS_INT BIT(22)
+#define ADPCTL_ADP_PRB_INT BIT(21)
+#define ADPCTL_ADPENA BIT(20)
+#define ADPCTL_ADPRES BIT(19)
+#define ADPCTL_ENASNS BIT(18)
+#define ADPCTL_ENAPRB BIT(17)
#define ADPCTL_RTIM_MASK (0x7ff << 6)
#define ADPCTL_RTIM_SHIFT 6
#define ADPCTL_PRB_PER_MASK (0x3 << 4)
@@ -412,6 +412,7 @@
/* Device mode registers */
#define DCFG HSOTG_REG(0x800)
+#define DCFG_DESCDMA_EN BIT(23)
#define DCFG_EPMISCNT_MASK (0x1f << 18)
#define DCFG_EPMISCNT_SHIFT 18
#define DCFG_EPMISCNT_LIMIT 0x1f
@@ -424,7 +425,7 @@
#define DCFG_DEVADDR_SHIFT 4
#define DCFG_DEVADDR_LIMIT 0x7f
#define DCFG_DEVADDR(_x) ((_x) << 4)
-#define DCFG_NZ_STS_OUT_HSHK (1 << 2)
+#define DCFG_NZ_STS_OUT_HSHK BIT(2)
#define DCFG_DEVSPD_MASK (0x3 << 0)
#define DCFG_DEVSPD_SHIFT 0
#define DCFG_DEVSPD_HS 0
@@ -433,53 +434,54 @@
#define DCFG_DEVSPD_FS48 3
#define DCTL HSOTG_REG(0x804)
-#define DCTL_PWRONPRGDONE (1 << 11)
-#define DCTL_CGOUTNAK (1 << 10)
-#define DCTL_SGOUTNAK (1 << 9)
-#define DCTL_CGNPINNAK (1 << 8)
-#define DCTL_SGNPINNAK (1 << 7)
+#define DCTL_PWRONPRGDONE BIT(11)
+#define DCTL_CGOUTNAK BIT(10)
+#define DCTL_SGOUTNAK BIT(9)
+#define DCTL_CGNPINNAK BIT(8)
+#define DCTL_SGNPINNAK BIT(7)
#define DCTL_TSTCTL_MASK (0x7 << 4)
#define DCTL_TSTCTL_SHIFT 4
-#define DCTL_GOUTNAKSTS (1 << 3)
-#define DCTL_GNPINNAKSTS (1 << 2)
-#define DCTL_SFTDISCON (1 << 1)
-#define DCTL_RMTWKUPSIG (1 << 0)
+#define DCTL_GOUTNAKSTS BIT(3)
+#define DCTL_GNPINNAKSTS BIT(2)
+#define DCTL_SFTDISCON BIT(1)
+#define DCTL_RMTWKUPSIG BIT(0)
#define DSTS HSOTG_REG(0x808)
#define DSTS_SOFFN_MASK (0x3fff << 8)
#define DSTS_SOFFN_SHIFT 8
#define DSTS_SOFFN_LIMIT 0x3fff
#define DSTS_SOFFN(_x) ((_x) << 8)
-#define DSTS_ERRATICERR (1 << 3)
+#define DSTS_ERRATICERR BIT(3)
#define DSTS_ENUMSPD_MASK (0x3 << 1)
#define DSTS_ENUMSPD_SHIFT 1
#define DSTS_ENUMSPD_HS 0
#define DSTS_ENUMSPD_FS 1
#define DSTS_ENUMSPD_LS 2
#define DSTS_ENUMSPD_FS48 3
-#define DSTS_SUSPSTS (1 << 0)
+#define DSTS_SUSPSTS BIT(0)
#define DIEPMSK HSOTG_REG(0x810)
-#define DIEPMSK_NAKMSK (1 << 13)
-#define DIEPMSK_BNAININTRMSK (1 << 9)
-#define DIEPMSK_TXFIFOUNDRNMSK (1 << 8)
-#define DIEPMSK_TXFIFOEMPTY (1 << 7)
-#define DIEPMSK_INEPNAKEFFMSK (1 << 6)
-#define DIEPMSK_INTKNEPMISMSK (1 << 5)
-#define DIEPMSK_INTKNTXFEMPMSK (1 << 4)
-#define DIEPMSK_TIMEOUTMSK (1 << 3)
-#define DIEPMSK_AHBERRMSK (1 << 2)
-#define DIEPMSK_EPDISBLDMSK (1 << 1)
-#define DIEPMSK_XFERCOMPLMSK (1 << 0)
+#define DIEPMSK_NAKMSK BIT(13)
+#define DIEPMSK_BNAININTRMSK BIT(9)
+#define DIEPMSK_TXFIFOUNDRNMSK BIT(8)
+#define DIEPMSK_TXFIFOEMPTY BIT(7)
+#define DIEPMSK_INEPNAKEFFMSK BIT(6)
+#define DIEPMSK_INTKNEPMISMSK BIT(5)
+#define DIEPMSK_INTKNTXFEMPMSK BIT(4)
+#define DIEPMSK_TIMEOUTMSK BIT(3)
+#define DIEPMSK_AHBERRMSK BIT(2)
+#define DIEPMSK_EPDISBLDMSK BIT(1)
+#define DIEPMSK_XFERCOMPLMSK BIT(0)
#define DOEPMSK HSOTG_REG(0x814)
-#define DOEPMSK_BACK2BACKSETUP (1 << 6)
-#define DOEPMSK_STSPHSERCVDMSK (1 << 5)
-#define DOEPMSK_OUTTKNEPDISMSK (1 << 4)
-#define DOEPMSK_SETUPMSK (1 << 3)
-#define DOEPMSK_AHBERRMSK (1 << 2)
-#define DOEPMSK_EPDISBLDMSK (1 << 1)
-#define DOEPMSK_XFERCOMPLMSK (1 << 0)
+#define DOEPMSK_BNAMSK BIT(9)
+#define DOEPMSK_BACK2BACKSETUP BIT(6)
+#define DOEPMSK_STSPHSERCVDMSK BIT(5)
+#define DOEPMSK_OUTTKNEPDISMSK BIT(4)
+#define DOEPMSK_SETUPMSK BIT(3)
+#define DOEPMSK_AHBERRMSK BIT(2)
+#define DOEPMSK_EPDISBLDMSK BIT(1)
+#define DOEPMSK_XFERCOMPLMSK BIT(0)
#define DAINT HSOTG_REG(0x818)
#define DAINTMSK HSOTG_REG(0x81C)
@@ -514,30 +516,30 @@
#define D0EPCTL_MPS_16 2
#define D0EPCTL_MPS_8 3
-#define DXEPCTL_EPENA (1 << 31)
-#define DXEPCTL_EPDIS (1 << 30)
-#define DXEPCTL_SETD1PID (1 << 29)
-#define DXEPCTL_SETODDFR (1 << 29)
-#define DXEPCTL_SETD0PID (1 << 28)
-#define DXEPCTL_SETEVENFR (1 << 28)
-#define DXEPCTL_SNAK (1 << 27)
-#define DXEPCTL_CNAK (1 << 26)
+#define DXEPCTL_EPENA BIT(31)
+#define DXEPCTL_EPDIS BIT(30)
+#define DXEPCTL_SETD1PID BIT(29)
+#define DXEPCTL_SETODDFR BIT(29)
+#define DXEPCTL_SETD0PID BIT(28)
+#define DXEPCTL_SETEVENFR BIT(28)
+#define DXEPCTL_SNAK BIT(27)
+#define DXEPCTL_CNAK BIT(26)
#define DXEPCTL_TXFNUM_MASK (0xf << 22)
#define DXEPCTL_TXFNUM_SHIFT 22
#define DXEPCTL_TXFNUM_LIMIT 0xf
#define DXEPCTL_TXFNUM(_x) ((_x) << 22)
-#define DXEPCTL_STALL (1 << 21)
-#define DXEPCTL_SNP (1 << 20)
+#define DXEPCTL_STALL BIT(21)
+#define DXEPCTL_SNP BIT(20)
#define DXEPCTL_EPTYPE_MASK (0x3 << 18)
#define DXEPCTL_EPTYPE_CONTROL (0x0 << 18)
#define DXEPCTL_EPTYPE_ISO (0x1 << 18)
#define DXEPCTL_EPTYPE_BULK (0x2 << 18)
#define DXEPCTL_EPTYPE_INTERRUPT (0x3 << 18)
-#define DXEPCTL_NAKSTS (1 << 17)
-#define DXEPCTL_DPID (1 << 16)
-#define DXEPCTL_EOFRNUM (1 << 16)
-#define DXEPCTL_USBACTEP (1 << 15)
+#define DXEPCTL_NAKSTS BIT(17)
+#define DXEPCTL_DPID BIT(16)
+#define DXEPCTL_EOFRNUM BIT(16)
+#define DXEPCTL_USBACTEP BIT(15)
#define DXEPCTL_NEXTEP_MASK (0xf << 11)
#define DXEPCTL_NEXTEP_SHIFT 11
#define DXEPCTL_NEXTEP_LIMIT 0xf
@@ -549,26 +551,26 @@
#define DIEPINT(_a) HSOTG_REG(0x908 + ((_a) * 0x20))
#define DOEPINT(_a) HSOTG_REG(0xB08 + ((_a) * 0x20))
-#define DXEPINT_SETUP_RCVD (1 << 15)
-#define DXEPINT_NYETINTRPT (1 << 14)
-#define DXEPINT_NAKINTRPT (1 << 13)
-#define DXEPINT_BBLEERRINTRPT (1 << 12)
-#define DXEPINT_PKTDRPSTS (1 << 11)
-#define DXEPINT_BNAINTR (1 << 9)
-#define DXEPINT_TXFIFOUNDRN (1 << 8)
-#define DXEPINT_OUTPKTERR (1 << 8)
-#define DXEPINT_TXFEMP (1 << 7)
-#define DXEPINT_INEPNAKEFF (1 << 6)
-#define DXEPINT_BACK2BACKSETUP (1 << 6)
-#define DXEPINT_INTKNEPMIS (1 << 5)
-#define DXEPINT_STSPHSERCVD (1 << 5)
-#define DXEPINT_INTKNTXFEMP (1 << 4)
-#define DXEPINT_OUTTKNEPDIS (1 << 4)
-#define DXEPINT_TIMEOUT (1 << 3)
-#define DXEPINT_SETUP (1 << 3)
-#define DXEPINT_AHBERR (1 << 2)
-#define DXEPINT_EPDISBLD (1 << 1)
-#define DXEPINT_XFERCOMPL (1 << 0)
+#define DXEPINT_SETUP_RCVD BIT(15)
+#define DXEPINT_NYETINTRPT BIT(14)
+#define DXEPINT_NAKINTRPT BIT(13)
+#define DXEPINT_BBLEERRINTRPT BIT(12)
+#define DXEPINT_PKTDRPSTS BIT(11)
+#define DXEPINT_BNAINTR BIT(9)
+#define DXEPINT_TXFIFOUNDRN BIT(8)
+#define DXEPINT_OUTPKTERR BIT(8)
+#define DXEPINT_TXFEMP BIT(7)
+#define DXEPINT_INEPNAKEFF BIT(6)
+#define DXEPINT_BACK2BACKSETUP BIT(6)
+#define DXEPINT_INTKNEPMIS BIT(5)
+#define DXEPINT_STSPHSERCVD BIT(5)
+#define DXEPINT_INTKNTXFEMP BIT(4)
+#define DXEPINT_OUTTKNEPDIS BIT(4)
+#define DXEPINT_TIMEOUT BIT(3)
+#define DXEPINT_SETUP BIT(3)
+#define DXEPINT_AHBERR BIT(2)
+#define DXEPINT_EPDISBLD BIT(1)
+#define DXEPINT_XFERCOMPL BIT(0)
#define DIEPTSIZ0 HSOTG_REG(0x910)
#define DIEPTSIZ0_PKTCNT_MASK (0x3 << 19)
@@ -585,7 +587,7 @@
#define DOEPTSIZ0_SUPCNT_SHIFT 29
#define DOEPTSIZ0_SUPCNT_LIMIT 0x3
#define DOEPTSIZ0_SUPCNT(_x) ((_x) << 29)
-#define DOEPTSIZ0_PKTCNT (1 << 19)
+#define DOEPTSIZ0_PKTCNT BIT(19)
#define DOEPTSIZ0_XFERSIZE_MASK (0x7f << 0)
#define DOEPTSIZ0_XFERSIZE_SHIFT 0
@@ -612,55 +614,55 @@
#define DTXFSTS(_a) HSOTG_REG(0x918 + ((_a) * 0x20))
#define PCGCTL HSOTG_REG(0x0e00)
-#define PCGCTL_IF_DEV_MODE (1 << 31)
+#define PCGCTL_IF_DEV_MODE BIT(31)
#define PCGCTL_P2HD_PRT_SPD_MASK (0x3 << 29)
#define PCGCTL_P2HD_PRT_SPD_SHIFT 29
#define PCGCTL_P2HD_DEV_ENUM_SPD_MASK (0x3 << 27)
#define PCGCTL_P2HD_DEV_ENUM_SPD_SHIFT 27
#define PCGCTL_MAC_DEV_ADDR_MASK (0x7f << 20)
#define PCGCTL_MAC_DEV_ADDR_SHIFT 20
-#define PCGCTL_MAX_TERMSEL (1 << 19)
+#define PCGCTL_MAX_TERMSEL BIT(19)
#define PCGCTL_MAX_XCVRSELECT_MASK (0x3 << 17)
#define PCGCTL_MAX_XCVRSELECT_SHIFT 17
-#define PCGCTL_PORT_POWER (1 << 16)
+#define PCGCTL_PORT_POWER BIT(16)
#define PCGCTL_PRT_CLK_SEL_MASK (0x3 << 14)
#define PCGCTL_PRT_CLK_SEL_SHIFT 14
-#define PCGCTL_ESS_REG_RESTORED (1 << 13)
-#define PCGCTL_EXTND_HIBER_SWITCH (1 << 12)
-#define PCGCTL_EXTND_HIBER_PWRCLMP (1 << 11)
-#define PCGCTL_ENBL_EXTND_HIBER (1 << 10)
-#define PCGCTL_RESTOREMODE (1 << 9)
-#define PCGCTL_RESETAFTSUSP (1 << 8)
-#define PCGCTL_DEEP_SLEEP (1 << 7)
-#define PCGCTL_PHY_IN_SLEEP (1 << 6)
-#define PCGCTL_ENBL_SLEEP_GATING (1 << 5)
-#define PCGCTL_RSTPDWNMODULE (1 << 3)
-#define PCGCTL_PWRCLMP (1 << 2)
-#define PCGCTL_GATEHCLK (1 << 1)
-#define PCGCTL_STOPPCLK (1 << 0)
+#define PCGCTL_ESS_REG_RESTORED BIT(13)
+#define PCGCTL_EXTND_HIBER_SWITCH BIT(12)
+#define PCGCTL_EXTND_HIBER_PWRCLMP BIT(11)
+#define PCGCTL_ENBL_EXTND_HIBER BIT(10)
+#define PCGCTL_RESTOREMODE BIT(9)
+#define PCGCTL_RESETAFTSUSP BIT(8)
+#define PCGCTL_DEEP_SLEEP BIT(7)
+#define PCGCTL_PHY_IN_SLEEP BIT(6)
+#define PCGCTL_ENBL_SLEEP_GATING BIT(5)
+#define PCGCTL_RSTPDWNMODULE BIT(3)
+#define PCGCTL_PWRCLMP BIT(2)
+#define PCGCTL_GATEHCLK BIT(1)
+#define PCGCTL_STOPPCLK BIT(0)
#define EPFIFO(_a) HSOTG_REG(0x1000 + ((_a) * 0x1000))
/* Host Mode Registers */
#define HCFG HSOTG_REG(0x0400)
-#define HCFG_MODECHTIMEN (1 << 31)
-#define HCFG_PERSCHEDENA (1 << 26)
+#define HCFG_MODECHTIMEN BIT(31)
+#define HCFG_PERSCHEDENA BIT(26)
#define HCFG_FRLISTEN_MASK (0x3 << 24)
#define HCFG_FRLISTEN_SHIFT 24
#define HCFG_FRLISTEN_8 (0 << 24)
#define FRLISTEN_8_SIZE 8
-#define HCFG_FRLISTEN_16 (1 << 24)
+#define HCFG_FRLISTEN_16 BIT(24)
#define FRLISTEN_16_SIZE 16
#define HCFG_FRLISTEN_32 (2 << 24)
#define FRLISTEN_32_SIZE 32
#define HCFG_FRLISTEN_64 (3 << 24)
#define FRLISTEN_64_SIZE 64
-#define HCFG_DESCDMA (1 << 23)
+#define HCFG_DESCDMA BIT(23)
#define HCFG_RESVALID_MASK (0xff << 8)
#define HCFG_RESVALID_SHIFT 8
-#define HCFG_ENA32KHZ (1 << 7)
-#define HCFG_FSLSSUPP (1 << 2)
+#define HCFG_ENA32KHZ BIT(7)
+#define HCFG_FSLSSUPP BIT(2)
#define HCFG_FSLSPCLKSEL_MASK (0x3 << 0)
#define HCFG_FSLSPCLKSEL_SHIFT 0
#define HCFG_FSLSPCLKSEL_30_60_MHZ 0
@@ -670,7 +672,7 @@
#define HFIR HSOTG_REG(0x0404)
#define HFIR_FRINT_MASK (0xffff << 0)
#define HFIR_FRINT_SHIFT 0
-#define HFIR_RLDCTRL (1 << 16)
+#define HFIR_RLDCTRL BIT(16)
#define HFNUM HSOTG_REG(0x0408)
#define HFNUM_FRREM_MASK (0xffff << 16)
@@ -680,12 +682,12 @@
#define HFNUM_MAX_FRNUM 0x3fff
#define HPTXSTS HSOTG_REG(0x0410)
-#define TXSTS_QTOP_ODD (1 << 31)
+#define TXSTS_QTOP_ODD BIT(31)
#define TXSTS_QTOP_CHNEP_MASK (0xf << 27)
#define TXSTS_QTOP_CHNEP_SHIFT 27
#define TXSTS_QTOP_TOKEN_MASK (0x3 << 25)
#define TXSTS_QTOP_TOKEN_SHIFT 25
-#define TXSTS_QTOP_TERMINATE (1 << 24)
+#define TXSTS_QTOP_TERMINATE BIT(24)
#define TXSTS_QSPCAVAIL_MASK (0xff << 16)
#define TXSTS_QSPCAVAIL_SHIFT 16
#define TXSTS_FSPCAVAIL_MASK (0xffff << 0)
@@ -703,39 +705,39 @@
#define HPRT0_SPD_LOW_SPEED 2
#define HPRT0_TSTCTL_MASK (0xf << 13)
#define HPRT0_TSTCTL_SHIFT 13
-#define HPRT0_PWR (1 << 12)
+#define HPRT0_PWR BIT(12)
#define HPRT0_LNSTS_MASK (0x3 << 10)
#define HPRT0_LNSTS_SHIFT 10
-#define HPRT0_RST (1 << 8)
-#define HPRT0_SUSP (1 << 7)
-#define HPRT0_RES (1 << 6)
-#define HPRT0_OVRCURRCHG (1 << 5)
-#define HPRT0_OVRCURRACT (1 << 4)
-#define HPRT0_ENACHG (1 << 3)
-#define HPRT0_ENA (1 << 2)
-#define HPRT0_CONNDET (1 << 1)
-#define HPRT0_CONNSTS (1 << 0)
+#define HPRT0_RST BIT(8)
+#define HPRT0_SUSP BIT(7)
+#define HPRT0_RES BIT(6)
+#define HPRT0_OVRCURRCHG BIT(5)
+#define HPRT0_OVRCURRACT BIT(4)
+#define HPRT0_ENACHG BIT(3)
+#define HPRT0_ENA BIT(2)
+#define HPRT0_CONNDET BIT(1)
+#define HPRT0_CONNSTS BIT(0)
#define HCCHAR(_ch) HSOTG_REG(0x0500 + 0x20 * (_ch))
-#define HCCHAR_CHENA (1 << 31)
-#define HCCHAR_CHDIS (1 << 30)
-#define HCCHAR_ODDFRM (1 << 29)
+#define HCCHAR_CHENA BIT(31)
+#define HCCHAR_CHDIS BIT(30)
+#define HCCHAR_ODDFRM BIT(29)
#define HCCHAR_DEVADDR_MASK (0x7f << 22)
#define HCCHAR_DEVADDR_SHIFT 22
#define HCCHAR_MULTICNT_MASK (0x3 << 20)
#define HCCHAR_MULTICNT_SHIFT 20
#define HCCHAR_EPTYPE_MASK (0x3 << 18)
#define HCCHAR_EPTYPE_SHIFT 18
-#define HCCHAR_LSPDDEV (1 << 17)
-#define HCCHAR_EPDIR (1 << 15)
+#define HCCHAR_LSPDDEV BIT(17)
+#define HCCHAR_EPDIR BIT(15)
#define HCCHAR_EPNUM_MASK (0xf << 11)
#define HCCHAR_EPNUM_SHIFT 11
#define HCCHAR_MPS_MASK (0x7ff << 0)
#define HCCHAR_MPS_SHIFT 0
#define HCSPLT(_ch) HSOTG_REG(0x0504 + 0x20 * (_ch))
-#define HCSPLT_SPLTENA (1 << 31)
-#define HCSPLT_COMPSPLT (1 << 16)
+#define HCSPLT_SPLTENA BIT(31)
+#define HCSPLT_COMPSPLT BIT(16)
#define HCSPLT_XACTPOS_MASK (0x3 << 14)
#define HCSPLT_XACTPOS_SHIFT 14
#define HCSPLT_XACTPOS_MID 0
@@ -750,23 +752,23 @@
#define HCINT(_ch) HSOTG_REG(0x0508 + 0x20 * (_ch))
#define HCINTMSK(_ch) HSOTG_REG(0x050c + 0x20 * (_ch))
#define HCINTMSK_RESERVED14_31 (0x3ffff << 14)
-#define HCINTMSK_FRM_LIST_ROLL (1 << 13)
-#define HCINTMSK_XCS_XACT (1 << 12)
-#define HCINTMSK_BNA (1 << 11)
-#define HCINTMSK_DATATGLERR (1 << 10)
-#define HCINTMSK_FRMOVRUN (1 << 9)
-#define HCINTMSK_BBLERR (1 << 8)
-#define HCINTMSK_XACTERR (1 << 7)
-#define HCINTMSK_NYET (1 << 6)
-#define HCINTMSK_ACK (1 << 5)
-#define HCINTMSK_NAK (1 << 4)
-#define HCINTMSK_STALL (1 << 3)
-#define HCINTMSK_AHBERR (1 << 2)
-#define HCINTMSK_CHHLTD (1 << 1)
-#define HCINTMSK_XFERCOMPL (1 << 0)
+#define HCINTMSK_FRM_LIST_ROLL BIT(13)
+#define HCINTMSK_XCS_XACT BIT(12)
+#define HCINTMSK_BNA BIT(11)
+#define HCINTMSK_DATATGLERR BIT(10)
+#define HCINTMSK_FRMOVRUN BIT(9)
+#define HCINTMSK_BBLERR BIT(8)
+#define HCINTMSK_XACTERR BIT(7)
+#define HCINTMSK_NYET BIT(6)
+#define HCINTMSK_ACK BIT(5)
+#define HCINTMSK_NAK BIT(4)
+#define HCINTMSK_STALL BIT(3)
+#define HCINTMSK_AHBERR BIT(2)
+#define HCINTMSK_CHHLTD BIT(1)
+#define HCINTMSK_XFERCOMPL BIT(0)
#define HCTSIZ(_ch) HSOTG_REG(0x0510 + 0x20 * (_ch))
-#define TSIZ_DOPNG (1 << 31)
+#define TSIZ_DOPNG BIT(31)
#define TSIZ_SC_MC_PID_MASK (0x3 << 29)
#define TSIZ_SC_MC_PID_SHIFT 29
#define TSIZ_SC_MC_PID_DATA0 0
@@ -790,7 +792,8 @@
#define HCFIFO(_ch) HSOTG_REG(0x1000 + 0x1000 * (_ch))
/**
- * struct dwc2_hcd_dma_desc - Host-mode DMA descriptor structure
+ * struct dwc2_dma_desc - DMA descriptor structure,
+ * used for both host and gadget modes
*
* @status: DMA descriptor status quadlet
* @buf: DMA descriptor data buffer pointer
@@ -798,27 +801,64 @@
* DMA Descriptor structure contains two quadlets:
* Status quadlet and Data buffer pointer.
*/
-struct dwc2_hcd_dma_desc {
+struct dwc2_dma_desc {
u32 status;
u32 buf;
-};
+} __packed;
-#define HOST_DMA_A (1 << 31)
+/* Host Mode DMA descriptor status quadlet */
+
+#define HOST_DMA_A BIT(31)
#define HOST_DMA_STS_MASK (0x3 << 28)
#define HOST_DMA_STS_SHIFT 28
-#define HOST_DMA_STS_PKTERR (1 << 28)
-#define HOST_DMA_EOL (1 << 26)
-#define HOST_DMA_IOC (1 << 25)
-#define HOST_DMA_SUP (1 << 24)
-#define HOST_DMA_ALT_QTD (1 << 23)
+#define HOST_DMA_STS_PKTERR BIT(28)
+#define HOST_DMA_EOL BIT(26)
+#define HOST_DMA_IOC BIT(25)
+#define HOST_DMA_SUP BIT(24)
+#define HOST_DMA_ALT_QTD BIT(23)
#define HOST_DMA_QTD_OFFSET_MASK (0x3f << 17)
#define HOST_DMA_QTD_OFFSET_SHIFT 17
#define HOST_DMA_ISOC_NBYTES_MASK (0xfff << 0)
#define HOST_DMA_ISOC_NBYTES_SHIFT 0
#define HOST_DMA_NBYTES_MASK (0x1ffff << 0)
#define HOST_DMA_NBYTES_SHIFT 0
+#define HOST_DMA_NBYTES_LIMIT 131071
+
+/* Device Mode DMA descriptor status quadlet */
+
+#define DEV_DMA_BUFF_STS_MASK (0x3 << 30)
+#define DEV_DMA_BUFF_STS_SHIFT 30
+#define DEV_DMA_BUFF_STS_HREADY 0
+#define DEV_DMA_BUFF_STS_DMABUSY 1
+#define DEV_DMA_BUFF_STS_DMADONE 2
+#define DEV_DMA_BUFF_STS_HBUSY 3
+#define DEV_DMA_STS_MASK (0x3 << 28)
+#define DEV_DMA_STS_SHIFT 28
+#define DEV_DMA_STS_SUCC 0
+#define DEV_DMA_STS_BUFF_FLUSH 1
+#define DEV_DMA_STS_BUFF_ERR 3
+#define DEV_DMA_L BIT(27)
+#define DEV_DMA_SHORT BIT(26)
+#define DEV_DMA_IOC BIT(25)
+#define DEV_DMA_SR BIT(24)
+#define DEV_DMA_MTRF BIT(23)
+#define DEV_DMA_ISOC_PID_MASK (0x3 << 23)
+#define DEV_DMA_ISOC_PID_SHIFT 23
+#define DEV_DMA_ISOC_PID_DATA0 0
+#define DEV_DMA_ISOC_PID_DATA2 1
+#define DEV_DMA_ISOC_PID_DATA1 2
+#define DEV_DMA_ISOC_PID_MDATA 3
+#define DEV_DMA_ISOC_FRNUM_MASK (0x7ff << 12)
+#define DEV_DMA_ISOC_FRNUM_SHIFT 12
+#define DEV_DMA_ISOC_TX_NBYTES_MASK (0xfff << 0)
+#define DEV_DMA_ISOC_TX_NBYTES_LIMIT 0xfff
+#define DEV_DMA_ISOC_RX_NBYTES_MASK (0x7ff << 0)
+#define DEV_DMA_ISOC_RX_NBYTES_LIMIT 0x7ff
+#define DEV_DMA_ISOC_NBYTES_SHIFT 0
+#define DEV_DMA_NBYTES_MASK (0xffff << 0)
+#define DEV_DMA_NBYTES_SHIFT 0
+#define DEV_DMA_NBYTES_LIMIT 0xffff
-#define MAX_DMA_DESC_SIZE 131071
#define MAX_DMA_DESC_NUM_GENERIC 64
#define MAX_DMA_DESC_NUM_HS_ISOC 256
diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c
new file mode 100644
index 000000000000..2990c347289f
--- /dev/null
+++ b/drivers/usb/dwc2/params.c
@@ -0,0 +1,707 @@
+/*
+ * Copyright (C) 2004-2016 Synopsys, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The names of the above-listed copyright holders may not be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+
+#include "core.h"
+
+static void dwc2_set_bcm_params(struct dwc2_hsotg *hsotg)
+{
+ struct dwc2_core_params *p = &hsotg->params;
+
+ p->host_rx_fifo_size = 774;
+ 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)
+{
+ struct dwc2_core_params *p = &hsotg->params;
+
+ p->otg_cap = DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE;
+ p->speed = DWC2_SPEED_PARAM_HIGH;
+ p->host_rx_fifo_size = 512;
+ p->host_nperio_tx_fifo_size = 512;
+ p->host_perio_tx_fifo_size = 512;
+ p->max_transfer_size = 65535;
+ p->max_packet_count = 511;
+ p->host_channels = 16;
+ p->phy_type = DWC2_PHY_TYPE_PARAM_UTMI;
+ p->phy_utmi_width = 8;
+ p->i2c_enable = false;
+ p->reload_ctl = false;
+ p->ahbcfg = GAHBCFG_HBSTLEN_INCR16 <<
+ GAHBCFG_HBSTLEN_SHIFT;
+ p->uframe_sched = false;
+ p->change_speed_quirk = true;
+}
+
+static void dwc2_set_rk_params(struct dwc2_hsotg *hsotg)
+{
+ struct dwc2_core_params *p = &hsotg->params;
+
+ p->otg_cap = DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE;
+ p->host_rx_fifo_size = 525;
+ p->host_nperio_tx_fifo_size = 128;
+ p->host_perio_tx_fifo_size = 256;
+ p->ahbcfg = GAHBCFG_HBSTLEN_INCR16 <<
+ GAHBCFG_HBSTLEN_SHIFT;
+}
+
+static void dwc2_set_ltq_params(struct dwc2_hsotg *hsotg)
+{
+ struct dwc2_core_params *p = &hsotg->params;
+
+ p->otg_cap = 2;
+ p->host_rx_fifo_size = 288;
+ p->host_nperio_tx_fifo_size = 128;
+ p->host_perio_tx_fifo_size = 96;
+ p->max_transfer_size = 65535;
+ p->max_packet_count = 511;
+ p->ahbcfg = GAHBCFG_HBSTLEN_INCR16 <<
+ GAHBCFG_HBSTLEN_SHIFT;
+}
+
+static void dwc2_set_amlogic_params(struct dwc2_hsotg *hsotg)
+{
+ struct dwc2_core_params *p = &hsotg->params;
+
+ p->otg_cap = DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE;
+ p->speed = DWC2_SPEED_PARAM_HIGH;
+ p->host_rx_fifo_size = 512;
+ p->host_nperio_tx_fifo_size = 500;
+ p->host_perio_tx_fifo_size = 500;
+ p->host_channels = 16;
+ 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)
+{
+ struct dwc2_core_params *p = &hsotg->params;
+
+ p->ahbcfg = GAHBCFG_HBSTLEN_INCR16 << GAHBCFG_HBSTLEN_SHIFT;
+}
+
+const struct of_device_id dwc2_of_match_table[] = {
+ { .compatible = "brcm,bcm2835-usb", .data = dwc2_set_bcm_params },
+ { .compatible = "hisilicon,hi6220-usb", .data = dwc2_set_his_params },
+ { .compatible = "rockchip,rk3066-usb", .data = dwc2_set_rk_params },
+ { .compatible = "lantiq,arx100-usb", .data = dwc2_set_ltq_params },
+ { .compatible = "lantiq,xrx200-usb", .data = dwc2_set_ltq_params },
+ { .compatible = "snps,dwc2" },
+ { .compatible = "samsung,s3c6400-hsotg" },
+ { .compatible = "amlogic,meson8b-usb",
+ .data = dwc2_set_amlogic_params },
+ { .compatible = "amlogic,meson-gxbb-usb",
+ .data = dwc2_set_amlogic_params },
+ { .compatible = "amcc,dwc-otg", .data = dwc2_set_amcc_params },
+ {},
+};
+MODULE_DEVICE_TABLE(of, dwc2_of_match_table);
+
+static void dwc2_set_param_otg_cap(struct dwc2_hsotg *hsotg)
+{
+ u8 val;
+
+ switch (hsotg->hw_params.op_mode) {
+ case GHWCFG2_OP_MODE_HNP_SRP_CAPABLE:
+ val = DWC2_CAP_PARAM_HNP_SRP_CAPABLE;
+ break;
+ case GHWCFG2_OP_MODE_SRP_ONLY_CAPABLE:
+ case GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE:
+ case GHWCFG2_OP_MODE_SRP_CAPABLE_HOST:
+ val = DWC2_CAP_PARAM_SRP_ONLY_CAPABLE;
+ break;
+ default:
+ val = DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE;
+ break;
+ }
+
+ hsotg->params.otg_cap = val;
+}
+
+static void dwc2_set_param_phy_type(struct dwc2_hsotg *hsotg)
+{
+ int val;
+ u32 hs_phy_type = hsotg->hw_params.hs_phy_type;
+
+ val = DWC2_PHY_TYPE_PARAM_FS;
+ if (hs_phy_type != GHWCFG2_HS_PHY_TYPE_NOT_SUPPORTED) {
+ if (hs_phy_type == GHWCFG2_HS_PHY_TYPE_UTMI ||
+ hs_phy_type == GHWCFG2_HS_PHY_TYPE_UTMI_ULPI)
+ val = DWC2_PHY_TYPE_PARAM_UTMI;
+ else
+ val = DWC2_PHY_TYPE_PARAM_ULPI;
+ }
+
+ if (dwc2_is_fs_iot(hsotg))
+ hsotg->params.phy_type = DWC2_PHY_TYPE_PARAM_FS;
+
+ hsotg->params.phy_type = val;
+}
+
+static void dwc2_set_param_speed(struct dwc2_hsotg *hsotg)
+{
+ int val;
+
+ val = hsotg->params.phy_type == DWC2_PHY_TYPE_PARAM_FS ?
+ DWC2_SPEED_PARAM_FULL : DWC2_SPEED_PARAM_HIGH;
+
+ if (dwc2_is_fs_iot(hsotg))
+ val = DWC2_SPEED_PARAM_FULL;
+
+ if (dwc2_is_hs_iot(hsotg))
+ val = DWC2_SPEED_PARAM_HIGH;
+
+ hsotg->params.speed = val;
+}
+
+static void dwc2_set_param_phy_utmi_width(struct dwc2_hsotg *hsotg)
+{
+ int val;
+
+ val = (hsotg->hw_params.utmi_phy_data_width ==
+ GHWCFG4_UTMI_PHY_DATA_WIDTH_8) ? 8 : 16;
+
+ hsotg->params.phy_utmi_width = val;
+}
+
+static void dwc2_set_param_tx_fifo_sizes(struct dwc2_hsotg *hsotg)
+{
+ struct dwc2_core_params *p = &hsotg->params;
+ int depth_average;
+ int fifo_count;
+ int i;
+
+ fifo_count = dwc2_hsotg_tx_fifo_count(hsotg);
+
+ memset(p->g_tx_fifo_size, 0, sizeof(p->g_tx_fifo_size));
+ depth_average = dwc2_hsotg_tx_fifo_average_depth(hsotg);
+ for (i = 1; i <= fifo_count; i++)
+ p->g_tx_fifo_size[i] = depth_average;
+}
+
+/**
+ * dwc2_set_default_params() - Set all core parameters to their
+ * auto-detected default values.
+ */
+static void dwc2_set_default_params(struct dwc2_hsotg *hsotg)
+{
+ struct dwc2_hw_params *hw = &hsotg->hw_params;
+ struct dwc2_core_params *p = &hsotg->params;
+ bool dma_capable = !(hw->arch == GHWCFG2_SLAVE_ONLY_ARCH);
+
+ dwc2_set_param_otg_cap(hsotg);
+ dwc2_set_param_phy_type(hsotg);
+ dwc2_set_param_speed(hsotg);
+ dwc2_set_param_phy_utmi_width(hsotg);
+ p->phy_ulpi_ddr = false;
+ p->phy_ulpi_ext_vbus = false;
+
+ p->enable_dynamic_fifo = hw->enable_dynamic_fifo;
+ p->en_multiple_tx_fifo = hw->en_multiple_tx_fifo;
+ p->i2c_enable = hw->i2c_enable;
+ p->ulpi_fs_ls = false;
+ p->ts_dline = false;
+ p->reload_ctl = (hw->snpsid >= DWC2_CORE_REV_2_92a);
+ p->uframe_sched = true;
+ p->external_id_pin_ctl = false;
+ p->hibernation = false;
+ p->max_packet_count = hw->max_packet_count;
+ p->max_transfer_size = hw->max_transfer_size;
+ p->ahbcfg = GAHBCFG_HBSTLEN_INCR4 << GAHBCFG_HBSTLEN_SHIFT;
+
+ if ((hsotg->dr_mode == USB_DR_MODE_HOST) ||
+ (hsotg->dr_mode == USB_DR_MODE_OTG)) {
+ p->host_dma = dma_capable;
+ p->dma_desc_enable = false;
+ p->dma_desc_fs_enable = false;
+ p->host_support_fs_ls_low_power = false;
+ p->host_ls_low_power_phy_clk = false;
+ p->host_channels = hw->host_channels;
+ p->host_rx_fifo_size = hw->rx_fifo_size;
+ p->host_nperio_tx_fifo_size = hw->host_nperio_tx_fifo_size;
+ p->host_perio_tx_fifo_size = hw->host_perio_tx_fifo_size;
+ }
+
+ if ((hsotg->dr_mode == USB_DR_MODE_PERIPHERAL) ||
+ (hsotg->dr_mode == USB_DR_MODE_OTG)) {
+ p->g_dma = dma_capable;
+ p->g_dma_desc = hw->dma_desc_enable;
+
+ /*
+ * The values for g_rx_fifo_size (2048) and
+ * g_np_tx_fifo_size (1024) come from the legacy s3c
+ * gadget driver. These defaults have been hard-coded
+ * for some time so many platforms depend on these
+ * values. Leave them as defaults for now and only
+ * auto-detect if the hardware does not support the
+ * default.
+ */
+ p->g_rx_fifo_size = 2048;
+ p->g_np_tx_fifo_size = 1024;
+ dwc2_set_param_tx_fifo_sizes(hsotg);
+ }
+}
+
+/**
+ * dwc2_get_device_properties() - Read in device properties.
+ *
+ * Read in the device properties and adjust core parameters if needed.
+ */
+static void dwc2_get_device_properties(struct dwc2_hsotg *hsotg)
+{
+ struct dwc2_core_params *p = &hsotg->params;
+ int num;
+
+ if ((hsotg->dr_mode == USB_DR_MODE_PERIPHERAL) ||
+ (hsotg->dr_mode == USB_DR_MODE_OTG)) {
+ device_property_read_u32(hsotg->dev, "g-rx-fifo-size",
+ &p->g_rx_fifo_size);
+
+ device_property_read_u32(hsotg->dev, "g-np-tx-fifo-size",
+ &p->g_np_tx_fifo_size);
+
+ num = device_property_read_u32_array(hsotg->dev,
+ "g-tx-fifo-size",
+ NULL, 0);
+
+ if (num > 0) {
+ num = min(num, 15);
+ memset(p->g_tx_fifo_size, 0,
+ sizeof(p->g_tx_fifo_size));
+ device_property_read_u32_array(hsotg->dev,
+ "g-tx-fifo-size",
+ &p->g_tx_fifo_size[1],
+ num);
+ }
+ }
+}
+
+static void dwc2_check_param_otg_cap(struct dwc2_hsotg *hsotg)
+{
+ int valid = 1;
+
+ switch (hsotg->params.otg_cap) {
+ case DWC2_CAP_PARAM_HNP_SRP_CAPABLE:
+ if (hsotg->hw_params.op_mode != GHWCFG2_OP_MODE_HNP_SRP_CAPABLE)
+ valid = 0;
+ break;
+ case DWC2_CAP_PARAM_SRP_ONLY_CAPABLE:
+ switch (hsotg->hw_params.op_mode) {
+ case GHWCFG2_OP_MODE_HNP_SRP_CAPABLE:
+ case GHWCFG2_OP_MODE_SRP_ONLY_CAPABLE:
+ case GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE:
+ case GHWCFG2_OP_MODE_SRP_CAPABLE_HOST:
+ break;
+ default:
+ valid = 0;
+ break;
+ }
+ break;
+ case DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE:
+ /* always valid */
+ break;
+ default:
+ valid = 0;
+ break;
+ }
+
+ if (!valid)
+ dwc2_set_param_otg_cap(hsotg);
+}
+
+static void dwc2_check_param_phy_type(struct dwc2_hsotg *hsotg)
+{
+ int valid = 0;
+ u32 hs_phy_type;
+ u32 fs_phy_type;
+
+ hs_phy_type = hsotg->hw_params.hs_phy_type;
+ fs_phy_type = hsotg->hw_params.fs_phy_type;
+
+ switch (hsotg->params.phy_type) {
+ case DWC2_PHY_TYPE_PARAM_FS:
+ if (fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED)
+ valid = 1;
+ break;
+ case DWC2_PHY_TYPE_PARAM_UTMI:
+ if ((hs_phy_type == GHWCFG2_HS_PHY_TYPE_UTMI) ||
+ (hs_phy_type == GHWCFG2_HS_PHY_TYPE_UTMI_ULPI))
+ valid = 1;
+ break;
+ case DWC2_PHY_TYPE_PARAM_ULPI:
+ if ((hs_phy_type == GHWCFG2_HS_PHY_TYPE_UTMI) ||
+ (hs_phy_type == GHWCFG2_HS_PHY_TYPE_UTMI_ULPI))
+ valid = 1;
+ break;
+ default:
+ break;
+ }
+
+ if (!valid)
+ dwc2_set_param_phy_type(hsotg);
+}
+
+static void dwc2_check_param_speed(struct dwc2_hsotg *hsotg)
+{
+ int valid = 1;
+ int phy_type = hsotg->params.phy_type;
+ int speed = hsotg->params.speed;
+
+ switch (speed) {
+ case DWC2_SPEED_PARAM_HIGH:
+ if ((hsotg->params.speed == DWC2_SPEED_PARAM_HIGH) &&
+ (phy_type == DWC2_PHY_TYPE_PARAM_FS))
+ valid = 0;
+ break;
+ case DWC2_SPEED_PARAM_FULL:
+ case DWC2_SPEED_PARAM_LOW:
+ break;
+ default:
+ valid = 0;
+ break;
+ }
+
+ if (!valid)
+ dwc2_set_param_speed(hsotg);
+}
+
+static void dwc2_check_param_phy_utmi_width(struct dwc2_hsotg *hsotg)
+{
+ int valid = 0;
+ int param = hsotg->params.phy_utmi_width;
+ int width = hsotg->hw_params.utmi_phy_data_width;
+
+ switch (width) {
+ case GHWCFG4_UTMI_PHY_DATA_WIDTH_8:
+ valid = (param == 8);
+ break;
+ case GHWCFG4_UTMI_PHY_DATA_WIDTH_16:
+ valid = (param == 16);
+ break;
+ case GHWCFG4_UTMI_PHY_DATA_WIDTH_8_OR_16:
+ valid = (param == 8 || param == 16);
+ break;
+ }
+
+ if (!valid)
+ dwc2_set_param_phy_utmi_width(hsotg);
+}
+
+static void dwc2_check_param_tx_fifo_sizes(struct dwc2_hsotg *hsotg)
+{
+ int fifo_count;
+ int fifo;
+ int min;
+ u32 total = 0;
+ u32 dptxfszn;
+
+ fifo_count = dwc2_hsotg_tx_fifo_count(hsotg);
+ min = hsotg->hw_params.en_multiple_tx_fifo ? 16 : 4;
+
+ for (fifo = 1; fifo <= fifo_count; fifo++)
+ total += hsotg->params.g_tx_fifo_size[fifo];
+
+ if (total > dwc2_hsotg_tx_fifo_total_depth(hsotg) || !total) {
+ dev_warn(hsotg->dev, "%s: Invalid parameter g-tx-fifo-size, setting to default average\n",
+ __func__);
+ dwc2_set_param_tx_fifo_sizes(hsotg);
+ }
+
+ for (fifo = 1; fifo <= fifo_count; fifo++) {
+ dptxfszn = (dwc2_readl(hsotg->regs + DPTXFSIZN(fifo)) &
+ FIFOSIZE_DEPTH_MASK) >> FIFOSIZE_DEPTH_SHIFT;
+
+ if (hsotg->params.g_tx_fifo_size[fifo] < min ||
+ hsotg->params.g_tx_fifo_size[fifo] > dptxfszn) {
+ dev_warn(hsotg->dev, "%s: Invalid parameter g_tx_fifo_size[%d]=%d\n",
+ __func__, fifo,
+ hsotg->params.g_tx_fifo_size[fifo]);
+ hsotg->params.g_tx_fifo_size[fifo] = dptxfszn;
+ }
+ }
+}
+
+#define CHECK_RANGE(_param, _min, _max, _def) do { \
+ if ((hsotg->params._param) < (_min) || \
+ (hsotg->params._param) > (_max)) { \
+ dev_warn(hsotg->dev, "%s: Invalid parameter %s=%d\n", \
+ __func__, #_param, hsotg->params._param); \
+ hsotg->params._param = (_def); \
+ } \
+ } while (0)
+
+#define CHECK_BOOL(_param, _check) do { \
+ if (hsotg->params._param && !(_check)) { \
+ dev_warn(hsotg->dev, "%s: Invalid parameter %s=%d\n", \
+ __func__, #_param, hsotg->params._param); \
+ hsotg->params._param = false; \
+ } \
+ } while (0)
+
+static void dwc2_check_params(struct dwc2_hsotg *hsotg)
+{
+ struct dwc2_hw_params *hw = &hsotg->hw_params;
+ struct dwc2_core_params *p = &hsotg->params;
+ bool dma_capable = !(hw->arch == GHWCFG2_SLAVE_ONLY_ARCH);
+
+ dwc2_check_param_otg_cap(hsotg);
+ dwc2_check_param_phy_type(hsotg);
+ dwc2_check_param_speed(hsotg);
+ dwc2_check_param_phy_utmi_width(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(reload_ctl, (hsotg->hw_params.snpsid > DWC2_CORE_REV_2_92a));
+ CHECK_RANGE(max_packet_count,
+ 15, hw->max_packet_count,
+ hw->max_packet_count);
+ CHECK_RANGE(max_transfer_size,
+ 2047, hw->max_transfer_size,
+ hw->max_transfer_size);
+
+ if ((hsotg->dr_mode == USB_DR_MODE_HOST) ||
+ (hsotg->dr_mode == USB_DR_MODE_OTG)) {
+ CHECK_BOOL(host_dma, dma_capable);
+ CHECK_BOOL(dma_desc_enable, p->host_dma);
+ CHECK_BOOL(dma_desc_fs_enable, p->dma_desc_enable);
+ CHECK_BOOL(host_ls_low_power_phy_clk,
+ p->phy_type == DWC2_PHY_TYPE_PARAM_FS);
+ CHECK_RANGE(host_channels,
+ 1, hw->host_channels,
+ hw->host_channels);
+ CHECK_RANGE(host_rx_fifo_size,
+ 16, hw->rx_fifo_size,
+ hw->rx_fifo_size);
+ CHECK_RANGE(host_nperio_tx_fifo_size,
+ 16, hw->host_nperio_tx_fifo_size,
+ hw->host_nperio_tx_fifo_size);
+ CHECK_RANGE(host_perio_tx_fifo_size,
+ 16, hw->host_perio_tx_fifo_size,
+ hw->host_perio_tx_fifo_size);
+ }
+
+ if ((hsotg->dr_mode == USB_DR_MODE_PERIPHERAL) ||
+ (hsotg->dr_mode == USB_DR_MODE_OTG)) {
+ CHECK_BOOL(g_dma, dma_capable);
+ CHECK_BOOL(g_dma_desc, (p->g_dma && hw->dma_desc_enable));
+ CHECK_RANGE(g_rx_fifo_size,
+ 16, hw->rx_fifo_size,
+ hw->rx_fifo_size);
+ CHECK_RANGE(g_np_tx_fifo_size,
+ 16, hw->dev_nperio_tx_fifo_size,
+ hw->dev_nperio_tx_fifo_size);
+ dwc2_check_param_tx_fifo_sizes(hsotg);
+ }
+}
+
+/*
+ * Gets host hardware parameters. Forces host mode if not currently in
+ * host mode. Should be called immediately after a core soft reset in
+ * order to get the reset values.
+ */
+static void dwc2_get_host_hwparams(struct dwc2_hsotg *hsotg)
+{
+ struct dwc2_hw_params *hw = &hsotg->hw_params;
+ u32 gnptxfsiz;
+ u32 hptxfsiz;
+ bool forced;
+
+ if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL)
+ return;
+
+ forced = dwc2_force_mode_if_needed(hsotg, true);
+
+ gnptxfsiz = dwc2_readl(hsotg->regs + GNPTXFSIZ);
+ hptxfsiz = dwc2_readl(hsotg->regs + HPTXFSIZ);
+
+ if (forced)
+ dwc2_clear_force_mode(hsotg);
+
+ hw->host_nperio_tx_fifo_size = (gnptxfsiz & FIFOSIZE_DEPTH_MASK) >>
+ FIFOSIZE_DEPTH_SHIFT;
+ hw->host_perio_tx_fifo_size = (hptxfsiz & FIFOSIZE_DEPTH_MASK) >>
+ FIFOSIZE_DEPTH_SHIFT;
+}
+
+/*
+ * Gets device hardware parameters. Forces device mode if not
+ * currently in device mode. Should be called immediately after a core
+ * soft reset in order to get the reset values.
+ */
+static void dwc2_get_dev_hwparams(struct dwc2_hsotg *hsotg)
+{
+ struct dwc2_hw_params *hw = &hsotg->hw_params;
+ bool forced;
+ u32 gnptxfsiz;
+
+ if (hsotg->dr_mode == USB_DR_MODE_HOST)
+ return;
+
+ forced = dwc2_force_mode_if_needed(hsotg, false);
+
+ gnptxfsiz = dwc2_readl(hsotg->regs + GNPTXFSIZ);
+
+ if (forced)
+ dwc2_clear_force_mode(hsotg);
+
+ hw->dev_nperio_tx_fifo_size = (gnptxfsiz & FIFOSIZE_DEPTH_MASK) >>
+ FIFOSIZE_DEPTH_SHIFT;
+}
+
+/**
+ * During device initialization, read various hardware configuration
+ * registers and interpret the contents.
+ */
+int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
+{
+ struct dwc2_hw_params *hw = &hsotg->hw_params;
+ unsigned int width;
+ u32 hwcfg1, hwcfg2, hwcfg3, hwcfg4;
+ u32 grxfsiz;
+
+ /*
+ * Attempt to ensure this device is really a DWC_otg Controller.
+ * Read and verify the GSNPSID register contents. The value should be
+ * 0x45f42xxx or 0x45f43xxx, which corresponds to either "OT2" or "OT3",
+ * as in "OTG version 2.xx" or "OTG version 3.xx".
+ */
+ hw->snpsid = dwc2_readl(hsotg->regs + GSNPSID);
+ if ((hw->snpsid & 0xfffff000) != 0x4f542000 &&
+ (hw->snpsid & 0xfffff000) != 0x4f543000 &&
+ (hw->snpsid & 0xffff0000) != 0x55310000 &&
+ (hw->snpsid & 0xffff0000) != 0x55320000) {
+ dev_err(hsotg->dev, "Bad value for GSNPSID: 0x%08x\n",
+ hw->snpsid);
+ return -ENODEV;
+ }
+
+ dev_dbg(hsotg->dev, "Core Release: %1x.%1x%1x%1x (snpsid=%x)\n",
+ 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);
+
+ /*
+ * Host specific hardware parameters. Reading these parameters
+ * requires the controller to be in host mode. The mode will
+ * be forced, if necessary, to read these values.
+ */
+ dwc2_get_host_hwparams(hsotg);
+ dwc2_get_dev_hwparams(hsotg);
+
+ /* hwcfg1 */
+ hw->dev_ep_dirs = hwcfg1;
+
+ /* hwcfg2 */
+ hw->op_mode = (hwcfg2 & GHWCFG2_OP_MODE_MASK) >>
+ GHWCFG2_OP_MODE_SHIFT;
+ hw->arch = (hwcfg2 & GHWCFG2_ARCHITECTURE_MASK) >>
+ GHWCFG2_ARCHITECTURE_SHIFT;
+ hw->enable_dynamic_fifo = !!(hwcfg2 & GHWCFG2_DYNAMIC_FIFO);
+ hw->host_channels = 1 + ((hwcfg2 & GHWCFG2_NUM_HOST_CHAN_MASK) >>
+ GHWCFG2_NUM_HOST_CHAN_SHIFT);
+ hw->hs_phy_type = (hwcfg2 & GHWCFG2_HS_PHY_TYPE_MASK) >>
+ GHWCFG2_HS_PHY_TYPE_SHIFT;
+ hw->fs_phy_type = (hwcfg2 & GHWCFG2_FS_PHY_TYPE_MASK) >>
+ GHWCFG2_FS_PHY_TYPE_SHIFT;
+ hw->num_dev_ep = (hwcfg2 & GHWCFG2_NUM_DEV_EP_MASK) >>
+ GHWCFG2_NUM_DEV_EP_SHIFT;
+ hw->nperio_tx_q_depth =
+ (hwcfg2 & GHWCFG2_NONPERIO_TX_Q_DEPTH_MASK) >>
+ GHWCFG2_NONPERIO_TX_Q_DEPTH_SHIFT << 1;
+ hw->host_perio_tx_q_depth =
+ (hwcfg2 & GHWCFG2_HOST_PERIO_TX_Q_DEPTH_MASK) >>
+ GHWCFG2_HOST_PERIO_TX_Q_DEPTH_SHIFT << 1;
+ hw->dev_token_q_depth =
+ (hwcfg2 & GHWCFG2_DEV_TOKEN_Q_DEPTH_MASK) >>
+ GHWCFG2_DEV_TOKEN_Q_DEPTH_SHIFT;
+
+ /* hwcfg3 */
+ width = (hwcfg3 & GHWCFG3_XFER_SIZE_CNTR_WIDTH_MASK) >>
+ GHWCFG3_XFER_SIZE_CNTR_WIDTH_SHIFT;
+ hw->max_transfer_size = (1 << (width + 11)) - 1;
+ width = (hwcfg3 & GHWCFG3_PACKET_SIZE_CNTR_WIDTH_MASK) >>
+ GHWCFG3_PACKET_SIZE_CNTR_WIDTH_SHIFT;
+ hw->max_packet_count = (1 << (width + 4)) - 1;
+ hw->i2c_enable = !!(hwcfg3 & GHWCFG3_I2C);
+ hw->total_fifo_size = (hwcfg3 & GHWCFG3_DFIFO_DEPTH_MASK) >>
+ GHWCFG3_DFIFO_DEPTH_SHIFT;
+
+ /* hwcfg4 */
+ hw->en_multiple_tx_fifo = !!(hwcfg4 & GHWCFG4_DED_FIFO_EN);
+ hw->num_dev_perio_in_ep = (hwcfg4 & GHWCFG4_NUM_DEV_PERIO_IN_EP_MASK) >>
+ GHWCFG4_NUM_DEV_PERIO_IN_EP_SHIFT;
+ hw->dma_desc_enable = !!(hwcfg4 & GHWCFG4_DESC_DMA);
+ hw->power_optimized = !!(hwcfg4 & GHWCFG4_POWER_OPTIMIZ);
+ hw->utmi_phy_data_width = (hwcfg4 & GHWCFG4_UTMI_PHY_DATA_WIDTH_MASK) >>
+ GHWCFG4_UTMI_PHY_DATA_WIDTH_SHIFT;
+
+ /* fifo sizes */
+ hw->rx_fifo_size = (grxfsiz & GRXFSIZ_DEPTH_MASK) >>
+ GRXFSIZ_DEPTH_SHIFT;
+
+ return 0;
+}
+
+int dwc2_init_params(struct dwc2_hsotg *hsotg)
+{
+ const struct of_device_id *match;
+ void (*set_params)(void *data);
+
+ dwc2_set_default_params(hsotg);
+ dwc2_get_device_properties(hsotg);
+
+ match = of_match_device(dwc2_of_match_table, hsotg->dev);
+ if (match && match->data) {
+ set_params = match->data;
+ set_params(hsotg);
+ }
+
+ dwc2_check_params(hsotg);
+
+ return 0;
+}
diff --git a/drivers/usb/dwc2/pci.c b/drivers/usb/dwc2/pci.c
index ae419615a176..fdeb8c7bf30a 100644
--- a/drivers/usb/dwc2/pci.c
+++ b/drivers/usb/dwc2/pci.c
@@ -62,6 +62,20 @@ struct dwc2_pci_glue {
struct platform_device *phy;
};
+static int dwc2_pci_quirks(struct pci_dev *pdev, struct platform_device *dwc2)
+{
+ if (pdev->vendor == PCI_VENDOR_ID_SYNOPSYS &&
+ pdev->device == PCI_PRODUCT_ID_HAPS_HSOTG) {
+ struct property_entry properties[] = {
+ { },
+ };
+
+ return platform_device_add_properties(dwc2, properties);
+ }
+
+ return 0;
+}
+
static void dwc2_pci_remove(struct pci_dev *pci)
{
struct dwc2_pci_glue *glue = pci_get_drvdata(pci);
@@ -73,7 +87,7 @@ static void dwc2_pci_remove(struct pci_dev *pci)
}
static int dwc2_pci_probe(struct pci_dev *pci,
- const struct pci_device_id *id)
+ const struct pci_device_id *id)
{
struct resource res[2];
struct platform_device *dwc2;
@@ -122,6 +136,10 @@ static int dwc2_pci_probe(struct pci_dev *pci,
return PTR_ERR(phy);
}
+ ret = dwc2_pci_quirks(pci, dwc2);
+ if (ret)
+ goto err;
+
ret = platform_device_add(dwc2);
if (ret) {
dev_err(dev, "failed to register dwc2 device\n");
diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c
index 8e1728b39a49..9564bc76c56f 100644
--- a/drivers/usb/dwc2/platform.c
+++ b/drivers/usb/dwc2/platform.c
@@ -55,165 +55,6 @@
static const char dwc2_driver_name[] = "dwc2";
-static const struct dwc2_core_params params_hi6220 = {
- .otg_cap = 2, /* No HNP/SRP capable */
- .otg_ver = 0, /* 1.3 */
- .dma_enable = 1,
- .dma_desc_enable = 0,
- .dma_desc_fs_enable = 0,
- .speed = 0, /* High Speed */
- .enable_dynamic_fifo = 1,
- .en_multiple_tx_fifo = 1,
- .host_rx_fifo_size = 512,
- .host_nperio_tx_fifo_size = 512,
- .host_perio_tx_fifo_size = 512,
- .max_transfer_size = 65535,
- .max_packet_count = 511,
- .host_channels = 16,
- .phy_type = 1, /* UTMI */
- .phy_utmi_width = 8,
- .phy_ulpi_ddr = 0, /* Single */
- .phy_ulpi_ext_vbus = 0,
- .i2c_enable = 0,
- .ulpi_fs_ls = 0,
- .host_support_fs_ls_low_power = 0,
- .host_ls_low_power_phy_clk = 0, /* 48 MHz */
- .ts_dline = 0,
- .reload_ctl = 0,
- .ahbcfg = GAHBCFG_HBSTLEN_INCR16 <<
- GAHBCFG_HBSTLEN_SHIFT,
- .uframe_sched = 0,
- .external_id_pin_ctl = -1,
- .hibernation = -1,
-};
-
-static const struct dwc2_core_params params_bcm2835 = {
- .otg_cap = 0, /* HNP/SRP capable */
- .otg_ver = 0, /* 1.3 */
- .dma_enable = 1,
- .dma_desc_enable = 0,
- .dma_desc_fs_enable = 0,
- .speed = 0, /* High Speed */
- .enable_dynamic_fifo = 1,
- .en_multiple_tx_fifo = 1,
- .host_rx_fifo_size = 774, /* 774 DWORDs */
- .host_nperio_tx_fifo_size = 256, /* 256 DWORDs */
- .host_perio_tx_fifo_size = 512, /* 512 DWORDs */
- .max_transfer_size = 65535,
- .max_packet_count = 511,
- .host_channels = 8,
- .phy_type = 1, /* UTMI */
- .phy_utmi_width = 8, /* 8 bits */
- .phy_ulpi_ddr = 0, /* Single */
- .phy_ulpi_ext_vbus = 0,
- .i2c_enable = 0,
- .ulpi_fs_ls = 0,
- .host_support_fs_ls_low_power = 0,
- .host_ls_low_power_phy_clk = 0, /* 48 MHz */
- .ts_dline = 0,
- .reload_ctl = 0,
- .ahbcfg = 0x10,
- .uframe_sched = 0,
- .external_id_pin_ctl = -1,
- .hibernation = -1,
-};
-
-static const struct dwc2_core_params params_rk3066 = {
- .otg_cap = 2, /* non-HNP/non-SRP */
- .otg_ver = -1,
- .dma_enable = -1,
- .dma_desc_enable = 0,
- .dma_desc_fs_enable = 0,
- .speed = -1,
- .enable_dynamic_fifo = 1,
- .en_multiple_tx_fifo = -1,
- .host_rx_fifo_size = 525, /* 525 DWORDs */
- .host_nperio_tx_fifo_size = 128, /* 128 DWORDs */
- .host_perio_tx_fifo_size = 256, /* 256 DWORDs */
- .max_transfer_size = -1,
- .max_packet_count = -1,
- .host_channels = -1,
- .phy_type = -1,
- .phy_utmi_width = -1,
- .phy_ulpi_ddr = -1,
- .phy_ulpi_ext_vbus = -1,
- .i2c_enable = -1,
- .ulpi_fs_ls = -1,
- .host_support_fs_ls_low_power = -1,
- .host_ls_low_power_phy_clk = -1,
- .ts_dline = -1,
- .reload_ctl = -1,
- .ahbcfg = GAHBCFG_HBSTLEN_INCR16 <<
- GAHBCFG_HBSTLEN_SHIFT,
- .uframe_sched = -1,
- .external_id_pin_ctl = -1,
- .hibernation = -1,
-};
-
-static const struct dwc2_core_params params_ltq = {
- .otg_cap = 2, /* non-HNP/non-SRP */
- .otg_ver = -1,
- .dma_enable = -1,
- .dma_desc_enable = -1,
- .dma_desc_fs_enable = -1,
- .speed = -1,
- .enable_dynamic_fifo = -1,
- .en_multiple_tx_fifo = -1,
- .host_rx_fifo_size = 288, /* 288 DWORDs */
- .host_nperio_tx_fifo_size = 128, /* 128 DWORDs */
- .host_perio_tx_fifo_size = 96, /* 96 DWORDs */
- .max_transfer_size = 65535,
- .max_packet_count = 511,
- .host_channels = -1,
- .phy_type = -1,
- .phy_utmi_width = -1,
- .phy_ulpi_ddr = -1,
- .phy_ulpi_ext_vbus = -1,
- .i2c_enable = -1,
- .ulpi_fs_ls = -1,
- .host_support_fs_ls_low_power = -1,
- .host_ls_low_power_phy_clk = -1,
- .ts_dline = -1,
- .reload_ctl = -1,
- .ahbcfg = GAHBCFG_HBSTLEN_INCR16 <<
- GAHBCFG_HBSTLEN_SHIFT,
- .uframe_sched = -1,
- .external_id_pin_ctl = -1,
- .hibernation = -1,
-};
-
-static const struct dwc2_core_params params_amlogic = {
- .otg_cap = DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE,
- .otg_ver = -1,
- .dma_enable = 1,
- .dma_desc_enable = 0,
- .dma_desc_fs_enable = 0,
- .speed = DWC2_SPEED_PARAM_HIGH,
- .enable_dynamic_fifo = 1,
- .en_multiple_tx_fifo = -1,
- .host_rx_fifo_size = 512,
- .host_nperio_tx_fifo_size = 500,
- .host_perio_tx_fifo_size = 500,
- .max_transfer_size = -1,
- .max_packet_count = -1,
- .host_channels = 16,
- .phy_type = DWC2_PHY_TYPE_PARAM_UTMI,
- .phy_utmi_width = -1,
- .phy_ulpi_ddr = -1,
- .phy_ulpi_ext_vbus = -1,
- .i2c_enable = -1,
- .ulpi_fs_ls = -1,
- .host_support_fs_ls_low_power = -1,
- .host_ls_low_power_phy_clk = -1,
- .ts_dline = -1,
- .reload_ctl = 1,
- .ahbcfg = GAHBCFG_HBSTLEN_INCR8 <<
- GAHBCFG_HBSTLEN_SHIFT,
- .uframe_sched = 0,
- .external_id_pin_ctl = -1,
- .hibernation = -1,
-};
-
/*
* Check the dr_mode against the module configuration and hardware
* capabilities.
@@ -270,7 +111,7 @@ static int dwc2_get_dr_mode(struct dwc2_hsotg *hsotg)
if (mode != hsotg->dr_mode) {
dev_warn(hsotg->dev,
- "Configuration mismatch. dr_mode forced to %s\n",
+ "Configuration mismatch. dr_mode forced to %s\n",
mode == USB_DR_MODE_HOST ? "host" : "device");
hsotg->dr_mode = mode;
@@ -295,11 +136,11 @@ static int __dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg)
return ret;
}
- if (hsotg->uphy)
+ if (hsotg->uphy) {
ret = usb_phy_init(hsotg->uphy);
- else if (hsotg->plat && hsotg->plat->phy_init)
+ } else if (hsotg->plat && hsotg->plat->phy_init) {
ret = hsotg->plat->phy_init(pdev, hsotg->plat->phy_type);
- else {
+ } else {
ret = phy_power_on(hsotg->phy);
if (ret == 0)
ret = phy_init(hsotg->phy);
@@ -329,11 +170,11 @@ static int __dwc2_lowlevel_hw_disable(struct dwc2_hsotg *hsotg)
struct platform_device *pdev = to_platform_device(hsotg->dev);
int ret = 0;
- if (hsotg->uphy)
+ if (hsotg->uphy) {
usb_phy_shutdown(hsotg->uphy);
- else if (hsotg->plat && hsotg->plat->phy_exit)
+ } else if (hsotg->plat && hsotg->plat->phy_exit) {
ret = hsotg->plat->phy_exit(pdev, hsotg->plat->phy_type);
- else {
+ } else {
ret = phy_exit(hsotg->phy);
if (ret == 0)
ret = phy_power_off(hsotg->phy);
@@ -510,20 +351,6 @@ static void dwc2_driver_shutdown(struct platform_device *dev)
disable_irq(hsotg->irq);
}
-static const struct of_device_id dwc2_of_match_table[] = {
- { .compatible = "brcm,bcm2835-usb", .data = &params_bcm2835 },
- { .compatible = "hisilicon,hi6220-usb", .data = &params_hi6220 },
- { .compatible = "rockchip,rk3066-usb", .data = &params_rk3066 },
- { .compatible = "lantiq,arx100-usb", .data = &params_ltq },
- { .compatible = "lantiq,xrx200-usb", .data = &params_ltq },
- { .compatible = "snps,dwc2", .data = NULL },
- { .compatible = "samsung,s3c6400-hsotg", .data = NULL},
- { .compatible = "amlogic,meson8b-usb", .data = &params_amlogic },
- { .compatible = "amlogic,meson-gxbb-usb", .data = &params_amlogic },
- {},
-};
-MODULE_DEVICE_TABLE(of, dwc2_of_match_table);
-
/**
* dwc2_driver_probe() - Called when the DWC_otg core is bound to the DWC_otg
* driver
@@ -538,30 +365,10 @@ MODULE_DEVICE_TABLE(of, dwc2_of_match_table);
*/
static int dwc2_driver_probe(struct platform_device *dev)
{
- const struct of_device_id *match;
- const struct dwc2_core_params *params;
- struct dwc2_core_params defparams;
struct dwc2_hsotg *hsotg;
struct resource *res;
int retval;
- match = of_match_device(dwc2_of_match_table, &dev->dev);
- if (match && match->data) {
- params = match->data;
- } else {
- /* Default all params to autodetect */
- dwc2_set_all_params(&defparams, -1);
- params = &defparams;
-
- /*
- * Disable descriptor dma mode by default as the HW can support
- * it, but does not support it for SPLIT transactions.
- * Disable it for FS devices as well.
- */
- defparams.dma_desc_enable = 0;
- defparams.dma_desc_fs_enable = 0;
- }
-
hsotg = devm_kzalloc(&dev->dev, sizeof(*hsotg), GFP_KERNEL);
if (!hsotg)
return -ENOMEM;
@@ -591,13 +398,6 @@ static int dwc2_driver_probe(struct platform_device *dev)
spin_lock_init(&hsotg->lock);
- hsotg->core_params = devm_kzalloc(&dev->dev,
- sizeof(*hsotg->core_params), GFP_KERNEL);
- if (!hsotg->core_params)
- return -ENOMEM;
-
- dwc2_set_all_params(hsotg->core_params, -1);
-
hsotg->irq = platform_get_irq(dev, 0);
if (hsotg->irq < 0) {
dev_err(&dev->dev, "missing IRQ resource\n");
@@ -631,11 +431,12 @@ static int dwc2_driver_probe(struct platform_device *dev)
if (retval)
goto error;
- /* Validate parameter values */
- dwc2_set_parameters(hsotg, params);
-
dwc2_force_dr_mode(hsotg);
+ retval = dwc2_init_params(hsotg);
+ if (retval)
+ goto error;
+
if (hsotg->dr_mode != USB_DR_MODE_HOST) {
retval = dwc2_gadget_init(hsotg, hsotg->irq);
if (retval)
@@ -644,7 +445,7 @@ static int dwc2_driver_probe(struct platform_device *dev)
}
if (hsotg->dr_mode != USB_DR_MODE_PERIPHERAL) {
- retval = dwc2_hcd_init(hsotg, hsotg->irq);
+ retval = dwc2_hcd_init(hsotg);
if (retval) {
if (hsotg->gadget_enabled)
dwc2_hsotg_remove(hsotg);
diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig
index b97cde76914d..c5aa235863e8 100644
--- a/drivers/usb/dwc3/Kconfig
+++ b/drivers/usb/dwc3/Kconfig
@@ -62,7 +62,7 @@ config USB_DWC3_OMAP
config USB_DWC3_EXYNOS
tristate "Samsung Exynos Platform"
- depends on ARCH_EXYNOS && OF || COMPILE_TEST
+ depends on (ARCH_EXYNOS || COMPILE_TEST) && OF
default USB_DWC3
help
Recent Exynos5 SoCs ship with one DesignWare Core USB3 IP inside,
@@ -70,7 +70,7 @@ config USB_DWC3_EXYNOS
config USB_DWC3_PCI
tristate "PCIe-based Platforms"
- depends on PCI
+ depends on PCI && ACPI
default USB_DWC3
help
If you're using the DesignWare Core IP with a PCIe, please say
@@ -98,7 +98,7 @@ config USB_DWC3_OF_SIMPLE
config USB_DWC3_ST
tristate "STMicroelectronics Platforms"
- depends on ARCH_STI && OF
+ depends on (ARCH_STI || COMPILE_TEST) && OF
default USB_DWC3
help
STMicroelectronics SoCs with one DesignWare Core USB3 IP
diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile
index 22420e17d68b..ffca34029b21 100644
--- a/drivers/usb/dwc3/Makefile
+++ b/drivers/usb/dwc3/Makefile
@@ -3,7 +3,11 @@ CFLAGS_trace.o := -I$(src)
obj-$(CONFIG_USB_DWC3) += dwc3.o
-dwc3-y := core.o debug.o trace.o
+dwc3-y := core.o
+
+ifneq ($(CONFIG_FTRACE),)
+ dwc3-y += trace.o
+endif
ifneq ($(filter y,$(CONFIG_USB_DWC3_HOST) $(CONFIG_USB_DWC3_DUAL_ROLE)),)
dwc3-y += host.o
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index fea446900cad..369bab16a824 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -169,33 +169,6 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc)
return -ETIMEDOUT;
}
-/**
- * dwc3_soft_reset - Issue soft reset
- * @dwc: Pointer to our controller context structure
- */
-static int dwc3_soft_reset(struct dwc3 *dwc)
-{
- unsigned long timeout;
- u32 reg;
-
- timeout = jiffies + msecs_to_jiffies(500);
- dwc3_writel(dwc->regs, DWC3_DCTL, DWC3_DCTL_CSFTRST);
- do {
- reg = dwc3_readl(dwc->regs, DWC3_DCTL);
- if (!(reg & DWC3_DCTL_CSFTRST))
- break;
-
- if (time_after(jiffies, timeout)) {
- dev_err(dwc->dev, "Reset Timed Out\n");
- return -ETIMEDOUT;
- }
-
- cpu_relax();
- } while (true);
-
- return 0;
-}
-
/*
* dwc3_frame_length_adjustment - Adjusts frame length if required
* @dwc3: Pointer to our controller context structure
@@ -229,7 +202,7 @@ static void dwc3_frame_length_adjustment(struct dwc3 *dwc)
static void dwc3_free_one_event_buffer(struct dwc3 *dwc,
struct dwc3_event_buffer *evt)
{
- dma_free_coherent(dwc->dev, evt->length, evt->buf, evt->dma);
+ dma_free_coherent(dwc->sysdev, evt->length, evt->buf, evt->dma);
}
/**
@@ -251,7 +224,11 @@ static struct dwc3_event_buffer *dwc3_alloc_one_event_buffer(struct dwc3 *dwc,
evt->dwc = dwc;
evt->length = length;
- evt->buf = dma_alloc_coherent(dwc->dev, length,
+ evt->cache = devm_kzalloc(dwc->dev, length, GFP_KERNEL);
+ if (!evt->cache)
+ return ERR_PTR(-ENOMEM);
+
+ evt->buf = dma_alloc_coherent(dwc->sysdev, length,
&evt->dma, GFP_KERNEL);
if (!evt->buf)
return ERR_PTR(-ENOMEM);
@@ -305,13 +282,7 @@ static int dwc3_event_buffers_setup(struct dwc3 *dwc)
struct dwc3_event_buffer *evt;
evt = dwc->ev_buf;
- dwc3_trace(trace_dwc3_core,
- "Event buf %p dma %08llx length %d\n",
- evt->buf, (unsigned long long) evt->dma,
- evt->length);
-
evt->lpos = 0;
-
dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(0),
lower_32_bits(evt->dma));
dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(0),
@@ -370,11 +341,11 @@ static int dwc3_setup_scratch_buffers(struct dwc3 *dwc)
if (!WARN_ON(dwc->scratchbuf))
return 0;
- scratch_addr = dma_map_single(dwc->dev, dwc->scratchbuf,
+ scratch_addr = dma_map_single(dwc->sysdev, dwc->scratchbuf,
dwc->nr_scratch * DWC3_SCRATCHBUF_SIZE,
DMA_BIDIRECTIONAL);
- if (dma_mapping_error(dwc->dev, scratch_addr)) {
- dev_err(dwc->dev, "failed to map scratch buffer\n");
+ if (dma_mapping_error(dwc->sysdev, scratch_addr)) {
+ dev_err(dwc->sysdev, "failed to map scratch buffer\n");
ret = -EFAULT;
goto err0;
}
@@ -398,7 +369,7 @@ static int dwc3_setup_scratch_buffers(struct dwc3 *dwc)
return 0;
err1:
- dma_unmap_single(dwc->dev, dwc->scratch_addr, dwc->nr_scratch *
+ dma_unmap_single(dwc->sysdev, dwc->scratch_addr, dwc->nr_scratch *
DWC3_SCRATCHBUF_SIZE, DMA_BIDIRECTIONAL);
err0:
@@ -417,7 +388,7 @@ static void dwc3_free_scratch_buffers(struct dwc3 *dwc)
if (!WARN_ON(dwc->scratchbuf))
return;
- dma_unmap_single(dwc->dev, dwc->scratch_addr, dwc->nr_scratch *
+ dma_unmap_single(dwc->sysdev, dwc->scratch_addr, dwc->nr_scratch *
DWC3_SCRATCHBUF_SIZE, DMA_BIDIRECTIONAL);
kfree(dwc->scratchbuf);
}
@@ -428,9 +399,6 @@ static void dwc3_core_num_eps(struct dwc3 *dwc)
dwc->num_in_eps = DWC3_NUM_IN_EPS(parms);
dwc->num_out_eps = DWC3_NUM_EPS(parms) - dwc->num_in_eps;
-
- dwc3_trace(trace_dwc3_core, "found %d IN and %d OUT endpoints",
- dwc->num_in_eps, dwc->num_out_eps);
}
static void dwc3_cache_hwparams(struct dwc3 *dwc)
@@ -524,13 +492,6 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
}
/* FALLTHROUGH */
case DWC3_GHWPARAMS3_HSPHY_IFC_ULPI:
- /* Making sure the interface and PHY are operational */
- ret = dwc3_soft_reset(dwc);
- if (ret)
- return ret;
-
- udelay(1);
-
ret = dwc3_ulpi_init(dwc);
if (ret)
return ret;
@@ -594,19 +555,12 @@ static void dwc3_core_exit(struct dwc3 *dwc)
phy_power_off(dwc->usb3_generic_phy);
}
-/**
- * dwc3_core_init - Low-level initialization of DWC3 Core
- * @dwc: Pointer to our controller context structure
- *
- * Returns 0 on success otherwise negative errno.
- */
-static int dwc3_core_init(struct dwc3 *dwc)
+static bool dwc3_core_is_valid(struct dwc3 *dwc)
{
- u32 hwparams4 = dwc->hwparams.hwparams4;
- u32 reg;
- int ret;
+ u32 reg;
reg = dwc3_readl(dwc->regs, DWC3_GSNPSID);
+
/* This should read as U3 followed by revision number */
if ((reg & DWC3_GSNPSID_MASK) == 0x55330000) {
/* Detected DWC_usb3 IP */
@@ -616,36 +570,16 @@ static int dwc3_core_init(struct dwc3 *dwc)
dwc->revision = dwc3_readl(dwc->regs, DWC3_VER_NUMBER);
dwc->revision |= DWC3_REVISION_IS_DWC31;
} else {
- dev_err(dwc->dev, "this is not a DesignWare USB3 DRD Core\n");
- ret = -ENODEV;
- goto err0;
+ return false;
}
- /*
- * Write Linux Version Code to our GUID register so it's easy to figure
- * out which kernel version a bug was found.
- */
- dwc3_writel(dwc->regs, DWC3_GUID, LINUX_VERSION_CODE);
-
- /* Handle USB2.0-only core configuration */
- if (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) ==
- DWC3_GHWPARAMS3_SSPHY_IFC_DIS) {
- if (dwc->maximum_speed == USB_SPEED_SUPER)
- dwc->maximum_speed = USB_SPEED_HIGH;
- }
-
- /* issue device SoftReset too */
- ret = dwc3_soft_reset(dwc);
- if (ret)
- goto err0;
-
- ret = dwc3_core_soft_reset(dwc);
- if (ret)
- goto err0;
+ return true;
+}
- ret = dwc3_phy_setup(dwc);
- if (ret)
- goto err0;
+static void dwc3_core_setup_global_control(struct dwc3 *dwc)
+{
+ u32 hwparams4 = dwc->hwparams.hwparams4;
+ u32 reg;
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
reg &= ~DWC3_GCTL_SCALEDOWN_MASK;
@@ -683,13 +617,13 @@ static int dwc3_core_init(struct dwc3 *dwc)
reg |= DWC3_GCTL_GBLHIBERNATIONEN;
break;
default:
- dwc3_trace(trace_dwc3_core, "No power optimization available\n");
+ /* nothing */
+ break;
}
/* check if current dwc3 is on simulation board */
if (dwc->hwparams.hwparams6 & DWC3_GHWPARAMS6_EN_FPGA) {
- dwc3_trace(trace_dwc3_core,
- "running on FPGA platform\n");
+ dev_info(dwc->dev, "Running with FPGA optmizations\n");
dwc->is_fpga = true;
}
@@ -714,7 +648,47 @@ static int dwc3_core_init(struct dwc3 *dwc)
reg |= DWC3_GCTL_U2RSTECN;
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+}
+
+/**
+ * dwc3_core_init - Low-level initialization of DWC3 Core
+ * @dwc: Pointer to our controller context structure
+ *
+ * Returns 0 on success otherwise negative errno.
+ */
+static int dwc3_core_init(struct dwc3 *dwc)
+{
+ u32 reg;
+ int ret;
+
+ if (!dwc3_core_is_valid(dwc)) {
+ dev_err(dwc->dev, "this is not a DesignWare USB3 DRD Core\n");
+ ret = -ENODEV;
+ goto err0;
+ }
+
+ /*
+ * Write Linux Version Code to our GUID register so it's easy to figure
+ * out which kernel version a bug was found.
+ */
+ dwc3_writel(dwc->regs, DWC3_GUID, LINUX_VERSION_CODE);
+
+ /* Handle USB2.0-only core configuration */
+ if (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) ==
+ DWC3_GHWPARAMS3_SSPHY_IFC_DIS) {
+ if (dwc->maximum_speed == USB_SPEED_SUPER)
+ dwc->maximum_speed = USB_SPEED_HIGH;
+ }
+
+ ret = dwc3_core_soft_reset(dwc);
+ if (ret)
+ goto err0;
+ ret = dwc3_phy_setup(dwc);
+ if (ret)
+ goto err0;
+
+ dwc3_core_setup_global_control(dwc);
dwc3_core_num_eps(dwc);
ret = dwc3_setup_scratch_buffers(dwc);
@@ -766,6 +740,16 @@ static int dwc3_core_init(struct dwc3 *dwc)
dwc3_writel(dwc->regs, DWC3_GUCTL2, reg);
}
+ /*
+ * Enable hardware control of sending remote wakeup in HS when
+ * the device is in the L1 state.
+ */
+ if (dwc->revision >= DWC3_REVISION_290A) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUCTL1);
+ reg |= DWC3_GUCTL1_DEV_L1_EXIT_BY_HW;
+ dwc3_writel(dwc->regs, DWC3_GUCTL1, reg);
+ }
+
return 0;
err4:
@@ -919,57 +903,13 @@ static void dwc3_core_exit_mode(struct dwc3 *dwc)
}
}
-#define DWC3_ALIGN_MASK (16 - 1)
-
-static int dwc3_probe(struct platform_device *pdev)
+static void dwc3_get_properties(struct dwc3 *dwc)
{
- struct device *dev = &pdev->dev;
- struct resource *res;
- struct dwc3 *dwc;
+ struct device *dev = dwc->dev;
u8 lpm_nyet_threshold;
u8 tx_de_emphasis;
u8 hird_threshold;
- int ret;
-
- void __iomem *regs;
- void *mem;
-
- mem = devm_kzalloc(dev, sizeof(*dwc) + DWC3_ALIGN_MASK, GFP_KERNEL);
- if (!mem)
- return -ENOMEM;
-
- dwc = PTR_ALIGN(mem, DWC3_ALIGN_MASK + 1);
- dwc->mem = mem;
- dwc->dev = dev;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(dev, "missing memory resource\n");
- return -ENODEV;
- }
-
- dwc->xhci_resources[0].start = res->start;
- dwc->xhci_resources[0].end = dwc->xhci_resources[0].start +
- DWC3_XHCI_REGS_END;
- 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->regs = regs;
- dwc->regs_size = resource_size(res);
-
/* default to highest possible threshold */
lpm_nyet_threshold = 0xff;
@@ -986,6 +926,13 @@ static int dwc3_probe(struct platform_device *pdev)
dwc->dr_mode = usb_get_dr_mode(dev);
dwc->hsphy_mode = of_usb_get_phy_mode(dev->of_node);
+ dwc->sysdev_is_parent = device_property_read_bool(dev,
+ "linux,sysdev_is_parent");
+ if (dwc->sysdev_is_parent)
+ dwc->sysdev = dwc->dev->parent;
+ else
+ dwc->sysdev = dwc->dev;
+
dwc->has_lpm_erratum = device_property_read_bool(dev,
"snps,has-lpm-erratum");
device_property_read_u8(dev, "snps,lpm-nyet-threshold",
@@ -1041,6 +988,112 @@ static int dwc3_probe(struct platform_device *pdev)
dwc->hird_threshold = hird_threshold
| (dwc->is_utmi_l1_suspend << 4);
+ dwc->imod_interval = 0;
+}
+
+/* check whether the core supports IMOD */
+bool dwc3_has_imod(struct dwc3 *dwc)
+{
+ return ((dwc3_is_usb3(dwc) &&
+ dwc->revision >= DWC3_REVISION_300A) ||
+ (dwc3_is_usb31(dwc) &&
+ dwc->revision >= DWC3_USB31_REVISION_120A));
+}
+
+static void dwc3_check_params(struct dwc3 *dwc)
+{
+ struct device *dev = dwc->dev;
+
+ /* Check for proper value of imod_interval */
+ if (dwc->imod_interval && !dwc3_has_imod(dwc)) {
+ dev_warn(dwc->dev, "Interrupt moderation not supported\n");
+ dwc->imod_interval = 0;
+ }
+
+ /*
+ * Workaround for STAR 9000961433 which affects only version
+ * 3.00a of the DWC_usb3 core. This prevents the controller
+ * interrupt from being masked while handling events. IMOD
+ * allows us to work around this issue. Enable it for the
+ * affected version.
+ */
+ if (!dwc->imod_interval &&
+ (dwc->revision == DWC3_REVISION_300A))
+ dwc->imod_interval = 1;
+
+ /* Check the maximum_speed parameter */
+ switch (dwc->maximum_speed) {
+ case USB_SPEED_LOW:
+ case USB_SPEED_FULL:
+ case USB_SPEED_HIGH:
+ case USB_SPEED_SUPER:
+ case USB_SPEED_SUPER_PLUS:
+ break;
+ default:
+ dev_err(dev, "invalid maximum_speed parameter %d\n",
+ dwc->maximum_speed);
+ /* fall through */
+ case USB_SPEED_UNKNOWN:
+ /* default to superspeed */
+ dwc->maximum_speed = USB_SPEED_SUPER;
+
+ /*
+ * default to superspeed plus if we are capable.
+ */
+ if (dwc3_is_usb31(dwc) &&
+ (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) ==
+ DWC3_GHWPARAMS3_SSPHY_IFC_GEN2))
+ dwc->maximum_speed = USB_SPEED_SUPER_PLUS;
+
+ break;
+ }
+}
+
+static int dwc3_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ struct dwc3 *dwc;
+
+ int ret;
+
+ void __iomem *regs;
+
+ dwc = devm_kzalloc(dev, sizeof(*dwc), GFP_KERNEL);
+ if (!dwc)
+ return -ENOMEM;
+
+ dwc->dev = dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "missing memory resource\n");
+ return -ENODEV;
+ }
+
+ dwc->xhci_resources[0].start = res->start;
+ dwc->xhci_resources[0].end = dwc->xhci_resources[0].start +
+ DWC3_XHCI_REGS_END;
+ 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->regs = regs;
+ dwc->regs_size = resource_size(res);
+
+ dwc3_get_properties(dwc);
+
platform_set_drvdata(pdev, dwc);
dwc3_cache_hwparams(dwc);
@@ -1050,12 +1103,6 @@ static int dwc3_probe(struct platform_device *pdev)
spin_lock_init(&dwc->lock);
- if (!dev->dma_mask) {
- dev->dma_mask = dev->parent->dma_mask;
- dev->dma_parms = dev->parent->dma_parms;
- dma_set_coherent_mask(dev, dev->parent->coherent_dma_mask);
- }
-
pm_runtime_set_active(dev);
pm_runtime_use_autosuspend(dev);
pm_runtime_set_autosuspend_delay(dev, DWC3_DEFAULT_AUTOSUSPEND_DELAY);
@@ -1087,32 +1134,7 @@ static int dwc3_probe(struct platform_device *pdev)
goto err4;
}
- /* Check the maximum_speed parameter */
- switch (dwc->maximum_speed) {
- case USB_SPEED_LOW:
- case USB_SPEED_FULL:
- case USB_SPEED_HIGH:
- case USB_SPEED_SUPER:
- case USB_SPEED_SUPER_PLUS:
- break;
- default:
- dev_err(dev, "invalid maximum_speed parameter %d\n",
- dwc->maximum_speed);
- /* fall through */
- case USB_SPEED_UNKNOWN:
- /* default to superspeed */
- dwc->maximum_speed = USB_SPEED_SUPER;
-
- /*
- * default to superspeed plus if we are capable.
- */
- if (dwc3_is_usb31(dwc) &&
- (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) ==
- DWC3_GHWPARAMS3_SSPHY_IFC_GEN2))
- dwc->maximum_speed = USB_SPEED_SUPER_PLUS;
-
- break;
- }
+ dwc3_check_params(dwc);
ret = dwc3_core_init_mode(dwc);
if (ret)
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 6b60e42626a2..2b9e4ca3c932 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -26,6 +26,7 @@
#include <linux/dma-mapping.h>
#include <linux/mm.h>
#include <linux/debugfs.h>
+#include <linux/wait.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
@@ -37,15 +38,15 @@
#define DWC3_MSG_MAX 500
/* Global constants */
+#define DWC3_PULL_UP_TIMEOUT 500 /* ms */
#define DWC3_ZLP_BUF_SIZE 1024 /* size of a superspeed bulk */
+#define DWC3_BOUNCE_SIZE 1024 /* size of a superspeed bulk */
#define DWC3_EP0_BOUNCE_SIZE 512
#define DWC3_ENDPOINTS_NUM 32
#define DWC3_XHCI_RESOURCES_NUM 2
#define DWC3_SCRATCHBUF_SIZE 4096 /* each buffer is assumed to be 4KiB */
-#define DWC3_EVENT_SIZE 4 /* bytes */
-#define DWC3_EVENT_MAX_NUM 64 /* 2 events/endpoint */
-#define DWC3_EVENT_BUFFERS_SIZE (DWC3_EVENT_SIZE * DWC3_EVENT_MAX_NUM)
+#define DWC3_EVENT_BUFFERS_SIZE 4096
#define DWC3_EVENT_TYPE_MASK 0xfe
#define DWC3_EVENT_TYPE_DEV 0
@@ -65,6 +66,7 @@
#define DWC3_DEVICE_EVENT_OVERFLOW 11
#define DWC3_GEVNTCOUNT_MASK 0xfffc
+#define DWC3_GEVNTCOUNT_EHB (1 << 31)
#define DWC3_GSNPSID_MASK 0xffff0000
#define DWC3_GSNPSREV_MASK 0xffff
@@ -147,6 +149,8 @@
#define DWC3_DEPCMDPAR0 0x08
#define DWC3_DEPCMD 0x0c
+#define DWC3_DEV_IMOD(n) (0xca00 + (n * 0x4))
+
/* OTG Registers */
#define DWC3_OCFG 0xcc00
#define DWC3_OCTL 0xcc04
@@ -198,6 +202,9 @@
#define DWC3_GCTL_GBLHIBERNATIONEN (1 << 1)
#define DWC3_GCTL_DSBLCLKGTNG (1 << 0)
+/* Global User Control 1 Register */
+#define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW (1 << 24)
+
/* Global USB2 PHY Configuration Register */
#define DWC3_GUSB2PHYCFG_PHYSOFTRST (1 << 31)
#define DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS (1 << 30)
@@ -303,9 +310,8 @@
#define DWC3_DCFG_SUPERSPEED_PLUS (5 << 0) /* DWC_usb31 only */
#define DWC3_DCFG_SUPERSPEED (4 << 0)
#define DWC3_DCFG_HIGHSPEED (0 << 0)
-#define DWC3_DCFG_FULLSPEED2 (1 << 0)
+#define DWC3_DCFG_FULLSPEED (1 << 0)
#define DWC3_DCFG_LOWSPEED (2 << 0)
-#define DWC3_DCFG_FULLSPEED1 (3 << 0)
#define DWC3_DCFG_NUMP_SHIFT 17
#define DWC3_DCFG_NUMP(n) (((n) >> DWC3_DCFG_NUMP_SHIFT) & 0x1f)
@@ -397,9 +403,8 @@
#define DWC3_DSTS_SUPERSPEED_PLUS (5 << 0) /* DWC_usb31 only */
#define DWC3_DSTS_SUPERSPEED (4 << 0)
#define DWC3_DSTS_HIGHSPEED (0 << 0)
-#define DWC3_DSTS_FULLSPEED2 (1 << 0)
+#define DWC3_DSTS_FULLSPEED (1 << 0)
#define DWC3_DSTS_LOWSPEED (2 << 0)
-#define DWC3_DSTS_FULLSPEED1 (3 << 0)
/* Device Generic Command Register */
#define DWC3_DGCMD_SET_LMP 0x01
@@ -450,6 +455,8 @@
#define DWC3_DEPCMD_SETTRANSFRESOURCE (0x02 << 0)
#define DWC3_DEPCMD_SETEPCONFIG (0x01 << 0)
+#define DWC3_DEPCMD_CMD(x) ((x) & 0xf)
+
/* The EP number goes 0..31 so ep0 is always out and ep1 is always in */
#define DWC3_DALEPENA_EP(n) (1 << n)
@@ -458,6 +465,11 @@
#define DWC3_DEPCMD_TYPE_BULK 2
#define DWC3_DEPCMD_TYPE_INTR 3
+#define DWC3_DEV_IMOD_COUNT_SHIFT 16
+#define DWC3_DEV_IMOD_COUNT_MASK (0xffff << 16)
+#define DWC3_DEV_IMOD_INTERVAL_SHIFT 0
+#define DWC3_DEV_IMOD_INTERVAL_MASK (0xffff << 0)
+
/* Structures */
struct dwc3_trb;
@@ -465,6 +477,7 @@ struct dwc3_trb;
/**
* struct dwc3_event_buffer - Software event buffer representation
* @buf: _THE_ buffer
+ * @cache: The buffer cache used in the threaded interrupt
* @length: size of this buffer
* @lpos: event offset
* @count: cache of last read event count register
@@ -474,6 +487,7 @@ struct dwc3_trb;
*/
struct dwc3_event_buffer {
void *buf;
+ void *cache;
unsigned length;
unsigned int lpos;
unsigned int count;
@@ -499,6 +513,7 @@ struct dwc3_event_buffer {
* @endpoint: usb endpoint
* @pending_list: list of pending requests for this endpoint
* @started_list: list of started requests on this endpoint
+ * @wait_end_transfer: wait_queue_head_t for waiting on End Transfer complete
* @lock: spinlock for endpoint request queue traversal
* @regs: pointer to first endpoint register
* @trb_pool: array of transaction buffers
@@ -524,12 +539,13 @@ struct dwc3_ep {
struct list_head pending_list;
struct list_head started_list;
+ wait_queue_head_t wait_end_transfer;
+
spinlock_t lock;
void __iomem *regs;
struct dwc3_trb *trb_pool;
dma_addr_t trb_pool_dma;
- const struct usb_ss_ep_comp_descriptor *comp_desc;
struct dwc3 *dwc;
u32 saved_state;
@@ -540,6 +556,8 @@ struct dwc3_ep {
#define DWC3_EP_BUSY (1 << 4)
#define DWC3_EP_PENDING_REQUEST (1 << 5)
#define DWC3_EP_MISSED_ISOC (1 << 6)
+#define DWC3_EP_END_TRANSFER_PENDING (1 << 7)
+#define DWC3_EP_TRANSFER_STARTED (1 << 8)
/* This last one is specific to EP0 */
#define DWC3_EP0_DIR_IN (1 << 31)
@@ -703,10 +721,11 @@ struct dwc3_hwparams {
* @dep: struct dwc3_ep owning this request
* @sg: pointer to first incomplete sg
* @num_pending_sgs: counter to pending sgs
- * @first_trb_index: index to first trb used by this request
+ * @remaining: amount of data remaining
* @epnum: endpoint number to which this request refers
* @trb: pointer to struct dwc3_trb
* @trb_dma: DMA address of @trb
+ * @unaligned: true for OUT endpoints with length not divisible by maxp
* @direction: IN or OUT direction flag
* @mapped: true when request has been dma-mapped
* @queued: true when request has been queued to HW
@@ -718,11 +737,12 @@ struct dwc3_request {
struct scatterlist *sg;
unsigned num_pending_sgs;
- u8 first_trb_index;
+ unsigned remaining;
u8 epnum;
struct dwc3_trb *trb;
dma_addr_t trb_dma;
+ unsigned unaligned:1;
unsigned direction:1;
unsigned mapped:1;
unsigned started:1;
@@ -748,6 +768,7 @@ struct dwc3_scratchpad_array {
* @ep0_usb_req: dummy req used while handling STD USB requests
* @ep0_bounce_addr: dma address of ep0_bounce
* @scratch_addr: dma address of scratchbuf
+ * @ep0_in_setup: one control transfer is completed and enter setup phase
* @lock: for synchronizing
* @dev: pointer to our struct device
* @xhci: pointer to our xHCI child
@@ -784,7 +805,6 @@ struct dwc3_scratchpad_array {
* @ep0state: state of endpoint zero
* @link_state: link state
* @speed: device speed (super, high, full, low)
- * @mem: points to start of memory which is used for this struct.
* @hwparams: copy of hwparams registers
* @root: debugfs root folder pointer
* @regset: debugfs pointer to regdump file
@@ -798,6 +818,7 @@ struct dwc3_scratchpad_array {
* @ep0_bounced: true when we used bounce buffer
* @ep0_expect_in: true when we expect a DATA IN transfer
* @has_hibernation: true when dwc3 was configured with Hibernation
+ * @sysdev_is_parent: true when dwc3 device has a parent driver
* @has_lpm_erratum: true when core was configured with LPM Erratum. Note that
* there's now way for software to detect this in runtime.
* @is_utmi_l1_suspend: the core asserts output signal
@@ -833,24 +854,30 @@ struct dwc3_scratchpad_array {
* 1 - -3.5dB de-emphasis
* 2 - No de-emphasis
* 3 - Reserved
+ * @imod_interval: set the interrupt moderation interval in 250ns
+ * increments or 0 to disable.
*/
struct dwc3 {
struct usb_ctrlrequest *ctrl_req;
struct dwc3_trb *ep0_trb;
+ void *bounce;
void *ep0_bounce;
void *zlp_buf;
void *scratchbuf;
u8 *setup_buf;
dma_addr_t ctrl_req_addr;
dma_addr_t ep0_trb_addr;
+ dma_addr_t bounce_addr;
dma_addr_t ep0_bounce_addr;
dma_addr_t scratch_addr;
struct dwc3_request ep0_usb_req;
+ struct completion ep0_in_setup;
/* device lock */
spinlock_t lock;
struct device *dev;
+ struct device *sysdev;
struct platform_device *xhci;
struct resource xhci_resources[DWC3_XHCI_RESOURCES_NUM];
@@ -909,6 +936,7 @@ struct dwc3 {
#define DWC3_REVISION_260A 0x5533260a
#define DWC3_REVISION_270A 0x5533270a
#define DWC3_REVISION_280A 0x5533280a
+#define DWC3_REVISION_290A 0x5533290a
#define DWC3_REVISION_300A 0x5533300a
#define DWC3_REVISION_310A 0x5533310a
@@ -918,6 +946,7 @@ struct dwc3 {
*/
#define DWC3_REVISION_IS_DWC31 0x80000000
#define DWC3_USB31_REVISION_110A (0x3131302a | DWC3_REVISION_IS_DWC31)
+#define DWC3_USB31_REVISION_120A (0x3132302a | DWC3_REVISION_IS_DWC31)
enum dwc3_ep0_next ep0_next_event;
enum dwc3_ep0_state ep0state;
@@ -934,8 +963,6 @@ struct dwc3 {
u8 num_out_eps;
u8 num_in_eps;
- void *mem;
-
struct dwc3_hwparams hwparams;
struct dentry *root;
struct debugfs_regset32 *regset;
@@ -952,6 +979,7 @@ struct dwc3 {
unsigned ep0_bounced:1;
unsigned ep0_expect_in:1;
unsigned has_hibernation:1;
+ unsigned sysdev_is_parent:1;
unsigned has_lpm_erratum:1;
unsigned is_utmi_l1_suspend:1;
unsigned is_fpga:1;
@@ -978,6 +1006,8 @@ struct dwc3 {
unsigned tx_de_emphasis_quirk:1;
unsigned tx_de_emphasis:2;
+
+ u16 imod_interval;
};
/* -------------------------------------------------------------------------- */
@@ -1039,12 +1069,16 @@ struct dwc3_event_depevt {
/* Control-only Status */
#define DEPEVT_STATUS_CONTROL_DATA 1
#define DEPEVT_STATUS_CONTROL_STATUS 2
+#define DEPEVT_STATUS_CONTROL_PHASE(n) ((n) & 3)
/* In response to Start Transfer */
#define DEPEVT_TRANSFER_NO_RESOURCE 1
#define DEPEVT_TRANSFER_BUS_EXPIRY 2
u32 parameters:16;
+
+/* For Command Complete Events */
+#define DEPEVT_PARAMETER_CMD(n) (((n) & (0xf << 8)) >> 8)
} __packed;
/**
@@ -1133,12 +1167,20 @@ struct dwc3_gadget_ep_cmd_params {
void dwc3_set_mode(struct dwc3 *dwc, u32 mode);
u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type);
+/* check whether we are on the DWC_usb3 core */
+static inline bool dwc3_is_usb3(struct dwc3 *dwc)
+{
+ return !(dwc->revision & DWC3_REVISION_IS_DWC31);
+}
+
/* check whether we are on the DWC_usb31 core */
static inline bool dwc3_is_usb31(struct dwc3 *dwc)
{
return !!(dwc->revision & DWC3_REVISION_IS_DWC31);
}
+bool dwc3_has_imod(struct dwc3 *dwc);
+
#if IS_ENABLED(CONFIG_USB_DWC3_HOST) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
int dwc3_host_init(struct dwc3 *dwc);
void dwc3_host_exit(struct dwc3 *dwc);
diff --git a/drivers/usb/dwc3/debug.c b/drivers/usb/dwc3/debug.c
deleted file mode 100644
index 0be6885bc370..000000000000
--- a/drivers/usb/dwc3/debug.c
+++ /dev/null
@@ -1,32 +0,0 @@
-/**
- * debug.c - DesignWare USB3 DRD Controller Debug/Trace Support
- *
- * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com
- *
- * Author: Felipe Balbi <balbi@ti.com>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 of
- * the License as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#include "debug.h"
-
-void dwc3_trace(void (*trace)(struct va_format *), const char *fmt, ...)
-{
- struct va_format vaf;
- va_list args;
-
- va_start(args, fmt);
- vaf.fmt = fmt;
- vaf.va = &args;
-
- trace(&vaf);
-
- va_end(args);
-}
diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h
index 33ab2a203c1b..eeed4ffd8131 100644
--- a/drivers/usb/dwc3/debug.h
+++ b/drivers/usb/dwc3/debug.h
@@ -124,6 +124,22 @@ dwc3_gadget_link_string(enum dwc3_link_state link_state)
}
}
+static inline const char *dwc3_ep0_state_string(enum dwc3_ep0_state state)
+{
+ switch (state) {
+ case EP0_UNCONNECTED:
+ return "Unconnected";
+ case EP0_SETUP_PHASE:
+ return "Setup Phase";
+ case EP0_DATA_PHASE:
+ return "Data Phase";
+ case EP0_STATUS_PHASE:
+ return "Status Phase";
+ default:
+ return "UNKNOWN";
+ }
+}
+
/**
* dwc3_gadget_event_string - returns event name
* @event: the event code
@@ -184,10 +200,11 @@ dwc3_gadget_event_string(const struct dwc3_event_devt *event)
* @event: then event code
*/
static inline const char *
-dwc3_ep_event_string(const struct dwc3_event_depevt *event)
+dwc3_ep_event_string(const struct dwc3_event_depevt *event, u32 ep0state)
{
u8 epnum = event->endpoint_number;
static char str[256];
+ size_t len;
int status;
int ret;
@@ -199,6 +216,10 @@ dwc3_ep_event_string(const struct dwc3_event_depevt *event)
switch (event->endpoint_event) {
case DWC3_DEPEVT_XFERCOMPLETE:
strcat(str, "Transfer Complete");
+ 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");
@@ -207,6 +228,19 @@ dwc3_ep_event_string(const struct dwc3_event_depevt *event)
strcat(str, "Transfer Not Ready");
status = event->status & DEPEVT_STATUS_TRANSFER_ACTIVE;
strcat(str, status ? " (Active)" : " (Not Active)");
+
+ /* Control Endpoints */
+ if (epnum <= 1) {
+ int phase = DEPEVT_STATUS_CONTROL_PHASE(event->status);
+
+ switch (phase) {
+ case DEPEVT_STATUS_CONTROL_DATA:
+ strcat(str, " [Data Phase]");
+ break;
+ case DEPEVT_STATUS_CONTROL_STATUS:
+ strcat(str, " [Status Phase]");
+ }
+ }
break;
case DWC3_DEPEVT_RXTXFIFOEVT:
strcat(str, "FIFO");
@@ -270,14 +304,14 @@ static inline const char *dwc3_gadget_event_type_string(u8 event)
}
}
-static inline const char *dwc3_decode_event(u32 event)
+static inline const char *dwc3_decode_event(u32 event, u32 ep0state)
{
const union dwc3_event evt = (union dwc3_event) event;
if (evt.type.is_devspec)
return dwc3_gadget_event_string(&evt.devt);
else
- return dwc3_ep_event_string(&evt.depevt);
+ return dwc3_ep_event_string(&evt.depevt, ep0state);
}
static inline const char *dwc3_ep_cmd_status_string(int status)
@@ -310,7 +344,6 @@ static inline const char *dwc3_gadget_generic_cmd_status_string(int status)
}
}
-void dwc3_trace(void (*trace)(struct va_format *), const char *fmt, ...);
#ifdef CONFIG_DEBUG_FS
extern void dwc3_debugfs_init(struct dwc3 *);
diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c
index 2f1fb7e7aa54..1515d45ebcec 100644
--- a/drivers/usb/dwc3/dwc3-exynos.c
+++ b/drivers/usb/dwc3/dwc3-exynos.c
@@ -20,7 +20,6 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
-#include <linux/dma-mapping.h>
#include <linux/clk.h>
#include <linux/usb/otg.h>
#include <linux/usb/usb_phy_generic.h>
@@ -117,15 +116,6 @@ static int dwc3_exynos_probe(struct platform_device *pdev)
if (!exynos)
return -ENOMEM;
- /*
- * 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 move to full device tree support this will vanish off.
- */
- ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32));
- if (ret)
- return ret;
-
platform_set_drvdata(pdev, exynos);
exynos->dev = dev;
@@ -138,17 +128,16 @@ static int dwc3_exynos_probe(struct platform_device *pdev)
clk_prepare_enable(exynos->clk);
exynos->susp_clk = devm_clk_get(dev, "usbdrd30_susp_clk");
- if (IS_ERR(exynos->susp_clk)) {
- dev_info(dev, "no suspend clk specified\n");
+ if (IS_ERR(exynos->susp_clk))
exynos->susp_clk = NULL;
- }
clk_prepare_enable(exynos->susp_clk);
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");
- return -ENODEV;
+ ret = -ENODEV;
+ goto axius_clk_err;
}
clk_prepare_enable(exynos->axius_clk);
} else {
@@ -206,6 +195,7 @@ err3:
regulator_disable(exynos->vdd33);
err2:
clk_disable_unprepare(exynos->axius_clk);
+axius_clk_err:
clk_disable_unprepare(exynos->susp_clk);
clk_disable_unprepare(exynos->clk);
return ret;
@@ -298,7 +288,6 @@ static struct platform_driver dwc3_exynos_driver = {
module_platform_driver(dwc3_exynos_driver);
-MODULE_ALIAS("platform:exynos-dwc3");
MODULE_AUTHOR("Anton Tikhomirov <av.tikhomirov@samsung.com>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("DesignWare USB3 EXYNOS Glue Layer");
diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c
index 29e80cc9b634..f8d0747810e7 100644
--- a/drivers/usb/dwc3/dwc3-omap.c
+++ b/drivers/usb/dwc3/dwc3-omap.c
@@ -19,6 +19,7 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
+#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/platform_data/dwc3-omap.h>
@@ -249,6 +250,7 @@ static void dwc3_omap_set_mailbox(struct dwc3_omap *omap,
val = dwc3_omap_read_utmi_ctrl(omap);
val |= USBOTGSS_UTMI_OTG_CTRL_IDDIG;
dwc3_omap_write_utmi_ctrl(omap, val);
+ break;
case OMAP_DWC3_VBUS_OFF:
val = dwc3_omap_read_utmi_ctrl(omap);
@@ -391,7 +393,7 @@ static void dwc3_omap_set_utmi_mode(struct dwc3_omap *omap)
{
u32 reg;
struct device_node *node = omap->dev->of_node;
- int utmi_mode = 0;
+ u32 utmi_mode = 0;
reg = dwc3_omap_read_utmi_ctrl(omap);
@@ -425,20 +427,20 @@ static int dwc3_omap_extcon_register(struct dwc3_omap *omap)
}
omap->vbus_nb.notifier_call = dwc3_omap_vbus_notifier;
- ret = extcon_register_notifier(edev, EXTCON_USB,
- &omap->vbus_nb);
+ ret = devm_extcon_register_notifier(omap->dev, edev,
+ EXTCON_USB, &omap->vbus_nb);
if (ret < 0)
dev_vdbg(omap->dev, "failed to register notifier for USB\n");
omap->id_nb.notifier_call = dwc3_omap_id_notifier;
- ret = extcon_register_notifier(edev, EXTCON_USB_HOST,
- &omap->id_nb);
+ ret = devm_extcon_register_notifier(omap->dev, edev,
+ EXTCON_USB_HOST, &omap->id_nb);
if (ret < 0)
dev_vdbg(omap->dev, "failed to register notifier for USB-HOST\n");
- if (extcon_get_cable_state_(edev, EXTCON_USB) == true)
+ if (extcon_get_state(edev, EXTCON_USB) == true)
dwc3_omap_set_mailbox(omap, OMAP_DWC3_VBUS_VALID);
- if (extcon_get_cable_state_(edev, EXTCON_USB_HOST) == true)
+ if (extcon_get_state(edev, EXTCON_USB_HOST) == true)
dwc3_omap_set_mailbox(omap, OMAP_DWC3_ID_GROUND);
omap->edev = edev;
@@ -510,7 +512,7 @@ static int dwc3_omap_probe(struct platform_device *pdev)
/* check the DMA Status */
reg = dwc3_omap_readl(omap->base, USBOTGSS_SYSCONFIG);
-
+ irq_set_status_flags(omap->irq, IRQ_NOAUTOEN);
ret = devm_request_threaded_irq(dev, omap->irq, dwc3_omap_interrupt,
dwc3_omap_interrupt_thread, IRQF_SHARED,
"dwc3-omap", omap);
@@ -527,17 +529,13 @@ static int dwc3_omap_probe(struct platform_device *pdev)
ret = of_platform_populate(node, NULL, NULL, dev);
if (ret) {
dev_err(&pdev->dev, "failed to create dwc3 core\n");
- goto err2;
+ goto err1;
}
dwc3_omap_enable_irqs(omap);
-
+ enable_irq(omap->irq);
return 0;
-err2:
- extcon_unregister_notifier(omap->edev, EXTCON_USB, &omap->vbus_nb);
- extcon_unregister_notifier(omap->edev, EXTCON_USB_HOST, &omap->id_nb);
-
err1:
pm_runtime_put_sync(dev);
pm_runtime_disable(dev);
@@ -549,9 +547,8 @@ static int dwc3_omap_remove(struct platform_device *pdev)
{
struct dwc3_omap *omap = platform_get_drvdata(pdev);
- extcon_unregister_notifier(omap->edev, EXTCON_USB, &omap->vbus_nb);
- extcon_unregister_notifier(omap->edev, EXTCON_USB_HOST, &omap->id_nb);
dwc3_omap_disable_irqs(omap);
+ disable_irq(omap->irq);
of_platform_depopulate(omap->dev);
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c
index 6df0f5dad9a4..cce0a220b6b0 100644
--- a/drivers/usb/dwc3/dwc3-pci.c
+++ b/drivers/usb/dwc3/dwc3-pci.c
@@ -38,6 +38,28 @@
#define PCI_DEVICE_ID_INTEL_BXT_M 0x1aaa
#define PCI_DEVICE_ID_INTEL_APL 0x5aaa
#define PCI_DEVICE_ID_INTEL_KBP 0xa2b0
+#define PCI_DEVICE_ID_INTEL_GLK 0x31aa
+
+#define PCI_INTEL_BXT_DSM_UUID "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
+
+/**
+ * struct dwc3_pci - Driver private structure
+ * @dwc3: child dwc3 platform_device
+ * @pci: our link to PCI bus
+ * @uuid: _DSM UUID
+ * @has_dsm_for_pm: true for devices which need to run _DSM on runtime PM
+ */
+struct dwc3_pci {
+ struct platform_device *dwc3;
+ struct pci_dev *pci;
+
+ u8 uuid[16];
+
+ unsigned int has_dsm_for_pm:1;
+};
static const struct acpi_gpio_params reset_gpios = { 0, 0, false };
static const struct acpi_gpio_params cs_gpios = { 1, 0, false };
@@ -48,8 +70,11 @@ static const struct acpi_gpio_mapping acpi_dwc3_byt_gpios[] = {
{ },
};
-static int dwc3_pci_quirks(struct pci_dev *pdev, struct platform_device *dwc3)
+static int dwc3_pci_quirks(struct dwc3_pci *dwc)
{
+ struct platform_device *dwc3 = dwc->dwc3;
+ struct pci_dev *pdev = dwc->pci;
+
if (pdev->vendor == PCI_VENDOR_ID_AMD &&
pdev->device == PCI_DEVICE_ID_AMD_NL_USB) {
struct property_entry properties[] = {
@@ -71,6 +96,7 @@ static int dwc3_pci_quirks(struct pci_dev *pdev, struct platform_device *dwc3)
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"),
{ },
};
@@ -81,7 +107,8 @@ static int dwc3_pci_quirks(struct pci_dev *pdev, struct platform_device *dwc3)
int ret;
struct property_entry properties[] = {
- PROPERTY_ENTRY_STRING("dr-mode", "peripheral"),
+ PROPERTY_ENTRY_STRING("dr_mode", "peripheral"),
+ PROPERTY_ENTRY_BOOL("linux,sysdev_is_parent"),
{ }
};
@@ -89,6 +116,12 @@ static int dwc3_pci_quirks(struct pci_dev *pdev, struct platform_device *dwc3)
if (ret < 0)
return ret;
+ if (pdev->device == PCI_DEVICE_ID_INTEL_BXT ||
+ pdev->device == PCI_DEVICE_ID_INTEL_BXT_M) {
+ acpi_str_to_uuid(PCI_INTEL_BXT_DSM_UUID, dwc->uuid);
+ dwc->has_dsm_for_pm = true;
+ }
+
if (pdev->device == PCI_DEVICE_ID_INTEL_BYT) {
struct gpio_desc *gpio;
@@ -127,6 +160,7 @@ static int dwc3_pci_quirks(struct pci_dev *pdev, struct platform_device *dwc3)
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"),
{ },
};
@@ -139,8 +173,8 @@ static int dwc3_pci_quirks(struct pci_dev *pdev, struct platform_device *dwc3)
static int dwc3_pci_probe(struct pci_dev *pci,
const struct pci_device_id *id)
{
+ struct dwc3_pci *dwc;
struct resource res[2];
- struct platform_device *dwc3;
int ret;
struct device *dev = &pci->dev;
@@ -152,11 +186,13 @@ static int dwc3_pci_probe(struct pci_dev *pci,
pci_set_master(pci);
- dwc3 = platform_device_alloc("dwc3", PLATFORM_DEVID_AUTO);
- if (!dwc3) {
- dev_err(dev, "couldn't allocate dwc3 device\n");
+ 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));
@@ -169,20 +205,21 @@ static int dwc3_pci_probe(struct pci_dev *pci,
res[1].name = "dwc_usb3";
res[1].flags = IORESOURCE_IRQ;
- ret = platform_device_add_resources(dwc3, res, ARRAY_SIZE(res));
+ ret = platform_device_add_resources(dwc->dwc3, res, ARRAY_SIZE(res));
if (ret) {
dev_err(dev, "couldn't add resources to dwc3 device\n");
return ret;
}
- dwc3->dev.parent = dev;
- ACPI_COMPANION_SET(&dwc3->dev, ACPI_COMPANION(dev));
+ dwc->pci = pci;
+ dwc->dwc3->dev.parent = dev;
+ ACPI_COMPANION_SET(&dwc->dwc3->dev, ACPI_COMPANION(dev));
- ret = dwc3_pci_quirks(pci, dwc3);
+ ret = dwc3_pci_quirks(dwc);
if (ret)
goto err;
- ret = platform_device_add(dwc3);
+ ret = platform_device_add(dwc->dwc3);
if (ret) {
dev_err(dev, "failed to register dwc3 device\n");
goto err;
@@ -190,21 +227,23 @@ static int dwc3_pci_probe(struct pci_dev *pci,
device_init_wakeup(dev, true);
device_set_run_wake(dev, true);
- pci_set_drvdata(pci, dwc3);
+ pci_set_drvdata(pci, dwc);
pm_runtime_put(dev);
return 0;
err:
- platform_device_put(dwc3);
+ platform_device_put(dwc->dwc3);
return ret;
}
static void dwc3_pci_remove(struct pci_dev *pci)
{
+ struct dwc3_pci *dwc = pci_get_drvdata(pci);
+
device_init_wakeup(&pci->dev, false);
pm_runtime_get(&pci->dev);
acpi_dev_remove_driver_gpios(ACPI_COMPANION(&pci->dev));
- platform_device_unregister(pci_get_drvdata(pci));
+ platform_device_unregister(dwc->dwc3);
}
static const struct pci_device_id dwc3_pci_id_table[] = {
@@ -229,45 +268,81 @@ static const struct pci_device_id dwc3_pci_id_table[] = {
{ 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_AMD, PCI_DEVICE_ID_AMD_NL_USB), },
{ } /* Terminating Entry */
};
MODULE_DEVICE_TABLE(pci, dwc3_pci_id_table);
+#if defined(CONFIG_PM) || defined(CONFIG_PM_SLEEP)
+static int dwc3_pci_dsm(struct dwc3_pci *dwc, int param)
+{
+ union acpi_object *obj;
+ union acpi_object tmp;
+ union acpi_object argv4 = ACPI_INIT_DSM_ARGV4(1, &tmp);
+
+ if (!dwc->has_dsm_for_pm)
+ return 0;
+
+ tmp.type = ACPI_TYPE_INTEGER;
+ tmp.integer.value = param;
+
+ obj = acpi_evaluate_dsm(ACPI_HANDLE(&dwc->pci->dev), dwc->uuid,
+ 1, PCI_INTEL_BXT_FUNC_PMU_PWR, &argv4);
+ if (!obj) {
+ dev_err(&dwc->pci->dev, "failed to evaluate _DSM\n");
+ return -EIO;
+ }
+
+ ACPI_FREE(obj);
+
+ return 0;
+}
+#endif /* CONFIG_PM || CONFIG_PM_SLEEP */
+
#ifdef CONFIG_PM
static int dwc3_pci_runtime_suspend(struct device *dev)
{
+ struct dwc3_pci *dwc = dev_get_drvdata(dev);
+
if (device_run_wake(dev))
- return 0;
+ return dwc3_pci_dsm(dwc, PCI_INTEL_BXT_STATE_D3);
return -EBUSY;
}
static int dwc3_pci_runtime_resume(struct device *dev)
{
- struct platform_device *dwc3 = dev_get_drvdata(dev);
+ struct dwc3_pci *dwc = dev_get_drvdata(dev);
+ struct platform_device *dwc3 = dwc->dwc3;
+ int ret;
+
+ ret = dwc3_pci_dsm(dwc, PCI_INTEL_BXT_STATE_D0);
+ if (ret)
+ return ret;
return pm_runtime_get(&dwc3->dev);
}
#endif /* CONFIG_PM */
#ifdef CONFIG_PM_SLEEP
-static int dwc3_pci_pm_dummy(struct device *dev)
+static int dwc3_pci_suspend(struct device *dev)
{
- /*
- * There's nothing to do here. No, seriously. Everything is either taken
- * care either by PCI subsystem or dwc3/core.c, so we have nothing
- * missing here.
- *
- * So you'd think we didn't need this at all, but PCI subsystem will
- * bail out if we don't have a valid callback :-s
- */
- return 0;
+ struct dwc3_pci *dwc = dev_get_drvdata(dev);
+
+ return dwc3_pci_dsm(dwc, PCI_INTEL_BXT_STATE_D3);
+}
+
+static int dwc3_pci_resume(struct device *dev)
+{
+ struct dwc3_pci *dwc = dev_get_drvdata(dev);
+
+ return dwc3_pci_dsm(dwc, PCI_INTEL_BXT_STATE_D0);
}
#endif /* CONFIG_PM_SLEEP */
static struct dev_pm_ops dwc3_pci_dev_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(dwc3_pci_pm_dummy, dwc3_pci_pm_dummy)
+ SET_SYSTEM_SLEEP_PM_OPS(dwc3_pci_suspend, dwc3_pci_resume)
SET_RUNTIME_PM_OPS(dwc3_pci_runtime_suspend, dwc3_pci_runtime_resume,
NULL)
};
diff --git a/drivers/usb/dwc3/dwc3-st.c b/drivers/usb/dwc3/dwc3-st.c
index aaaf256f71dd..dfbf464eb88c 100644
--- a/drivers/usb/dwc3/dwc3-st.c
+++ b/drivers/usb/dwc3/dwc3-st.c
@@ -219,7 +219,6 @@ static int st_dwc3_probe(struct platform_device *pdev)
if (IS_ERR(regmap))
return PTR_ERR(regmap);
- dma_set_coherent_mask(dev, dev->coherent_dma_mask);
dwc3_data->dev = dev;
dwc3_data->regmap = regmap;
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index fe79d771dee4..e689cede9b0e 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -39,36 +39,13 @@ static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep);
static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
struct dwc3_ep *dep, struct dwc3_request *req);
-static const char *dwc3_ep0_state_string(enum dwc3_ep0_state state)
+static void dwc3_ep0_prepare_one_trb(struct dwc3 *dwc, u8 epnum,
+ dma_addr_t buf_dma, u32 len, u32 type, bool chain)
{
- switch (state) {
- case EP0_UNCONNECTED:
- return "Unconnected";
- case EP0_SETUP_PHASE:
- return "Setup Phase";
- case EP0_DATA_PHASE:
- return "Data Phase";
- case EP0_STATUS_PHASE:
- return "Status Phase";
- default:
- return "UNKNOWN";
- }
-}
-
-static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma,
- u32 len, u32 type, bool chain)
-{
- struct dwc3_gadget_ep_cmd_params params;
struct dwc3_trb *trb;
struct dwc3_ep *dep;
- int ret;
-
dep = dwc->eps[epnum];
- if (dep->flags & DWC3_EP_BUSY) {
- dwc3_trace(trace_dwc3_ep0, "%s still busy", dep->name);
- return 0;
- }
trb = &dwc->ep0_trb[dep->trb_enqueue];
@@ -89,21 +66,26 @@ static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma,
trb->ctrl |= (DWC3_TRB_CTRL_IOC
| DWC3_TRB_CTRL_LST);
- if (chain)
+ trace_dwc3_prepare_trb(dep, trb);
+}
+
+static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum)
+{
+ struct dwc3_gadget_ep_cmd_params params;
+ struct dwc3_ep *dep;
+ int ret;
+
+ dep = dwc->eps[epnum];
+ if (dep->flags & DWC3_EP_BUSY)
return 0;
memset(&params, 0, sizeof(params));
params.param0 = upper_32_bits(dwc->ep0_trb_addr);
params.param1 = lower_32_bits(dwc->ep0_trb_addr);
- trace_dwc3_prepare_trb(dep, trb);
-
ret = dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_STARTTRANSFER, &params);
- if (ret < 0) {
- dwc3_trace(trace_dwc3_ep0, "%s STARTTRANSFER failed",
- dep->name);
+ if (ret < 0)
return ret;
- }
dep->flags |= DWC3_EP_BUSY;
dep->resource_index = dwc3_gadget_ep_get_transfer_index(dep);
@@ -163,9 +145,6 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep,
if (dwc->ep0state == EP0_STATUS_PHASE)
__dwc3_ep0_do_control_status(dwc, dwc->eps[direction]);
- else
- dwc3_trace(trace_dwc3_ep0,
- "too early for delayed status");
return 0;
}
@@ -229,9 +208,8 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
spin_lock_irqsave(&dwc->lock, flags);
if (!dep->endpoint.desc) {
- dwc3_trace(trace_dwc3_ep0,
- "trying to queue request %p to disabled %s",
- request, dep->name);
+ dev_err(dwc->dev, "%s: can't queue to disabled endpoint\n",
+ dep->name);
ret = -ESHUTDOWN;
goto out;
}
@@ -242,11 +220,6 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
goto out;
}
- dwc3_trace(trace_dwc3_ep0,
- "queueing request %p to %s length %d state '%s'",
- request, dep->name, request->length,
- dwc3_ep0_state_string(dwc->ep0state));
-
ret = __dwc3_gadget_ep0_queue(dep, req);
out:
@@ -308,8 +281,11 @@ void dwc3_ep0_out_start(struct dwc3 *dwc)
{
int ret;
- ret = dwc3_ep0_start_trans(dwc, 0, dwc->ctrl_req_addr, 8,
+ complete(&dwc->ep0_in_setup);
+
+ dwc3_ep0_prepare_one_trb(dwc, 0, dwc->ctrl_req_addr, 8,
DWC3_TRBCTL_CONTROL_SETUP, false);
+ ret = dwc3_ep0_start_trans(dwc, 0);
WARN_ON(ret < 0);
}
@@ -395,126 +371,198 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc,
return __dwc3_gadget_ep0_queue(dep, &dwc->ep0_usb_req);
}
-static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
+static int dwc3_ep0_handle_u1(struct dwc3 *dwc, enum usb_device_state state,
+ int set)
+{
+ u32 reg;
+
+ if (state != USB_STATE_CONFIGURED)
+ return -EINVAL;
+ if ((dwc->speed != DWC3_DSTS_SUPERSPEED) &&
+ (dwc->speed != DWC3_DSTS_SUPERSPEED_PLUS))
+ return -EINVAL;
+
+ reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ if (set)
+ reg |= DWC3_DCTL_INITU1ENA;
+ else
+ reg &= ~DWC3_DCTL_INITU1ENA;
+ dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+
+ return 0;
+}
+
+static int dwc3_ep0_handle_u2(struct dwc3 *dwc, enum usb_device_state state,
+ int set)
+{
+ u32 reg;
+
+
+ if (state != USB_STATE_CONFIGURED)
+ return -EINVAL;
+ if ((dwc->speed != DWC3_DSTS_SUPERSPEED) &&
+ (dwc->speed != DWC3_DSTS_SUPERSPEED_PLUS))
+ return -EINVAL;
+
+ reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ if (set)
+ reg |= DWC3_DCTL_INITU2ENA;
+ else
+ reg &= ~DWC3_DCTL_INITU2ENA;
+ dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+
+ return 0;
+}
+
+static int dwc3_ep0_handle_test(struct dwc3 *dwc, enum usb_device_state state,
+ u32 wIndex, int set)
+{
+ if ((wIndex & 0xff) != 0)
+ return -EINVAL;
+ if (!set)
+ return -EINVAL;
+
+ switch (wIndex >> 8) {
+ case TEST_J:
+ case TEST_K:
+ case TEST_SE0_NAK:
+ case TEST_PACKET:
+ case TEST_FORCE_EN:
+ dwc->test_mode_nr = wIndex >> 8;
+ dwc->test_mode = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int dwc3_ep0_handle_device(struct dwc3 *dwc,
struct usb_ctrlrequest *ctrl, int set)
{
- struct dwc3_ep *dep;
- u32 recip;
+ enum usb_device_state state;
u32 wValue;
u32 wIndex;
- u32 reg;
- int ret;
- enum usb_device_state state;
+ int ret = 0;
wValue = le16_to_cpu(ctrl->wValue);
wIndex = le16_to_cpu(ctrl->wIndex);
- recip = ctrl->bRequestType & USB_RECIP_MASK;
state = dwc->gadget.state;
- switch (recip) {
- case USB_RECIP_DEVICE:
+ switch (wValue) {
+ case USB_DEVICE_REMOTE_WAKEUP:
+ break;
+ /*
+ * 9.4.1 says only only for SS, in AddressState only for
+ * default control pipe
+ */
+ case USB_DEVICE_U1_ENABLE:
+ ret = dwc3_ep0_handle_u1(dwc, state, set);
+ break;
+ case USB_DEVICE_U2_ENABLE:
+ ret = dwc3_ep0_handle_u2(dwc, state, set);
+ break;
+ case USB_DEVICE_LTM_ENABLE:
+ ret = -EINVAL;
+ break;
+ case USB_DEVICE_TEST_MODE:
+ ret = dwc3_ep0_handle_test(dwc, state, wIndex, set);
+ break;
+ default:
+ ret = -EINVAL;
+ }
- switch (wValue) {
- case USB_DEVICE_REMOTE_WAKEUP:
- break;
+ return ret;
+}
+
+static int dwc3_ep0_handle_intf(struct dwc3 *dwc,
+ struct usb_ctrlrequest *ctrl, int set)
+{
+ enum usb_device_state state;
+ u32 wValue;
+ u32 wIndex;
+ int ret = 0;
+
+ wValue = le16_to_cpu(ctrl->wValue);
+ wIndex = le16_to_cpu(ctrl->wIndex);
+ state = dwc->gadget.state;
+
+ switch (wValue) {
+ case USB_INTRF_FUNC_SUSPEND:
/*
- * 9.4.1 says only only for SS, in AddressState only for
- * default control pipe
+ * REVISIT: Ideally we would enable some low power mode here,
+ * however it's unclear what we should be doing here.
+ *
+ * For now, we're not doing anything, just making sure we return
+ * 0 so USB Command Verifier tests pass without any errors.
*/
- case USB_DEVICE_U1_ENABLE:
- if (state != USB_STATE_CONFIGURED)
- return -EINVAL;
- if ((dwc->speed != DWC3_DSTS_SUPERSPEED) &&
- (dwc->speed != DWC3_DSTS_SUPERSPEED_PLUS))
- return -EINVAL;
+ break;
+ default:
+ ret = -EINVAL;
+ }
- reg = dwc3_readl(dwc->regs, DWC3_DCTL);
- if (set)
- reg |= DWC3_DCTL_INITU1ENA;
- else
- reg &= ~DWC3_DCTL_INITU1ENA;
- dwc3_writel(dwc->regs, DWC3_DCTL, reg);
- break;
+ return ret;
+}
- case USB_DEVICE_U2_ENABLE:
- if (state != USB_STATE_CONFIGURED)
- return -EINVAL;
- if ((dwc->speed != DWC3_DSTS_SUPERSPEED) &&
- (dwc->speed != DWC3_DSTS_SUPERSPEED_PLUS))
- return -EINVAL;
+static int dwc3_ep0_handle_endpoint(struct dwc3 *dwc,
+ struct usb_ctrlrequest *ctrl, int set)
+{
+ struct dwc3_ep *dep;
+ enum usb_device_state state;
+ u32 wValue;
+ u32 wIndex;
+ int ret;
- reg = dwc3_readl(dwc->regs, DWC3_DCTL);
- if (set)
- reg |= DWC3_DCTL_INITU2ENA;
- else
- reg &= ~DWC3_DCTL_INITU2ENA;
- dwc3_writel(dwc->regs, DWC3_DCTL, reg);
- break;
+ wValue = le16_to_cpu(ctrl->wValue);
+ wIndex = le16_to_cpu(ctrl->wIndex);
+ state = dwc->gadget.state;
- case USB_DEVICE_LTM_ENABLE:
+ switch (wValue) {
+ case USB_ENDPOINT_HALT:
+ dep = dwc3_wIndex_to_dep(dwc, ctrl->wIndex);
+ if (!dep)
return -EINVAL;
- case USB_DEVICE_TEST_MODE:
- if ((wIndex & 0xff) != 0)
- return -EINVAL;
- if (!set)
- return -EINVAL;
-
- switch (wIndex >> 8) {
- case TEST_J:
- case TEST_K:
- case TEST_SE0_NAK:
- case TEST_PACKET:
- case TEST_FORCE_EN:
- dwc->test_mode_nr = wIndex >> 8;
- dwc->test_mode = true;
- break;
- default:
- return -EINVAL;
- }
+ if (set == 0 && (dep->flags & DWC3_EP_WEDGE))
break;
- default:
+
+ ret = __dwc3_gadget_ep_set_halt(dep, set, true);
+ if (ret)
return -EINVAL;
- }
break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
+ struct usb_ctrlrequest *ctrl, int set)
+{
+ u32 recip;
+ int ret;
+ enum usb_device_state state;
+ recip = ctrl->bRequestType & USB_RECIP_MASK;
+ state = dwc->gadget.state;
+
+ switch (recip) {
+ case USB_RECIP_DEVICE:
+ ret = dwc3_ep0_handle_device(dwc, ctrl, set);
+ break;
case USB_RECIP_INTERFACE:
- switch (wValue) {
- case USB_INTRF_FUNC_SUSPEND:
- if (wIndex & USB_INTRF_FUNC_SUSPEND_LP)
- /* XXX enable Low power suspend */
- ;
- if (wIndex & USB_INTRF_FUNC_SUSPEND_RW)
- /* XXX enable remote wakeup */
- ;
- break;
- default:
- return -EINVAL;
- }
+ ret = dwc3_ep0_handle_intf(dwc, ctrl, set);
break;
-
case USB_RECIP_ENDPOINT:
- switch (wValue) {
- case USB_ENDPOINT_HALT:
- dep = dwc3_wIndex_to_dep(dwc, ctrl->wIndex);
- if (!dep)
- return -EINVAL;
- if (set == 0 && (dep->flags & DWC3_EP_WEDGE))
- break;
- ret = __dwc3_gadget_ep_set_halt(dep, set, true);
- if (ret)
- return -EINVAL;
- break;
- default:
- return -EINVAL;
- }
+ ret = dwc3_ep0_handle_endpoint(dwc, ctrl, set);
break;
-
default:
- return -EINVAL;
+ ret = -EINVAL;
}
- return 0;
+ return ret;
}
static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
@@ -525,13 +573,12 @@ static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
addr = le16_to_cpu(ctrl->wValue);
if (addr > 127) {
- dwc3_trace(trace_dwc3_ep0, "invalid device address %d", addr);
+ dev_err(dwc->dev, "invalid device address %d\n", addr);
return -EINVAL;
}
if (state == USB_STATE_CONFIGURED) {
- dwc3_trace(trace_dwc3_ep0,
- "trying to set address when configured");
+ dev_err(dwc->dev, "can't SetAddress() from Configured State\n");
return -EINVAL;
}
@@ -716,35 +763,27 @@ static int dwc3_ep0_std_request(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
switch (ctrl->bRequest) {
case USB_REQ_GET_STATUS:
- dwc3_trace(trace_dwc3_ep0, "USB_REQ_GET_STATUS");
ret = dwc3_ep0_handle_status(dwc, ctrl);
break;
case USB_REQ_CLEAR_FEATURE:
- dwc3_trace(trace_dwc3_ep0, "USB_REQ_CLEAR_FEATURE");
ret = dwc3_ep0_handle_feature(dwc, ctrl, 0);
break;
case USB_REQ_SET_FEATURE:
- dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_FEATURE");
ret = dwc3_ep0_handle_feature(dwc, ctrl, 1);
break;
case USB_REQ_SET_ADDRESS:
- dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_ADDRESS");
ret = dwc3_ep0_set_address(dwc, ctrl);
break;
case USB_REQ_SET_CONFIGURATION:
- dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_CONFIGURATION");
ret = dwc3_ep0_set_config(dwc, ctrl);
break;
case USB_REQ_SET_SEL:
- dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_SEL");
ret = dwc3_ep0_set_sel(dwc, ctrl);
break;
case USB_REQ_SET_ISOCH_DELAY:
- dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_ISOCH_DELAY");
ret = dwc3_ep0_set_isoch_delay(dwc, ctrl);
break;
default:
- dwc3_trace(trace_dwc3_ep0, "Forwarding to gadget driver");
ret = dwc3_ep0_delegate_req(dwc, ctrl);
break;
}
@@ -820,9 +859,6 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
status = DWC3_TRB_SIZE_TRBSTS(trb->size);
if (status == DWC3_TRBSTS_SETUP_PENDING) {
dwc->setup_packet_pending = true;
-
- dwc3_trace(trace_dwc3_ep0, "Setup Pending received");
-
if (r)
dwc3_gadget_giveback(ep0, r, -ECONNRESET);
@@ -880,9 +916,9 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
dwc->ep0_next_event = DWC3_EP0_COMPLETE;
- ret = dwc3_ep0_start_trans(dwc, epnum,
- dwc->ctrl_req_addr, 0,
- DWC3_TRBCTL_CONTROL_DATA, false);
+ dwc3_ep0_prepare_one_trb(dwc, epnum, dwc->ctrl_req_addr,
+ 0, DWC3_TRBCTL_CONTROL_DATA, false);
+ ret = dwc3_ep0_start_trans(dwc, epnum);
WARN_ON(ret < 0);
}
}
@@ -912,7 +948,7 @@ static void dwc3_ep0_complete_status(struct dwc3 *dwc,
ret = dwc3_gadget_set_test_mode(dwc, dwc->test_mode_nr);
if (ret < 0) {
- dwc3_trace(trace_dwc3_ep0, "Invalid Test #%d",
+ dev_err(dwc->dev, "invalid test #%d\n",
dwc->test_mode_nr);
dwc3_ep0_stall_and_restart(dwc);
return;
@@ -920,10 +956,8 @@ static void dwc3_ep0_complete_status(struct dwc3 *dwc,
}
status = DWC3_TRB_SIZE_TRBSTS(trb->size);
- if (status == DWC3_TRBSTS_SETUP_PENDING) {
+ if (status == DWC3_TRBSTS_SETUP_PENDING)
dwc->setup_packet_pending = true;
- dwc3_trace(trace_dwc3_ep0, "Setup Pending received");
- }
dwc->ep0state = EP0_SETUP_PHASE;
dwc3_ep0_out_start(dwc);
@@ -940,17 +974,14 @@ static void dwc3_ep0_xfer_complete(struct dwc3 *dwc,
switch (dwc->ep0state) {
case EP0_SETUP_PHASE:
- dwc3_trace(trace_dwc3_ep0, "Setup Phase");
dwc3_ep0_inspect_setup(dwc, event);
break;
case EP0_DATA_PHASE:
- dwc3_trace(trace_dwc3_ep0, "Data Phase");
dwc3_ep0_complete_data(dwc, event);
break;
case EP0_STATUS_PHASE:
- dwc3_trace(trace_dwc3_ep0, "Status Phase");
dwc3_ep0_complete_status(dwc, event);
break;
default:
@@ -966,27 +997,26 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
req->direction = !!dep->number;
if (req->request.length == 0) {
- ret = dwc3_ep0_start_trans(dwc, dep->number,
+ dwc3_ep0_prepare_one_trb(dwc, dep->number,
dwc->ctrl_req_addr, 0,
DWC3_TRBCTL_CONTROL_DATA, false);
+ ret = dwc3_ep0_start_trans(dwc, dep->number);
} else if (!IS_ALIGNED(req->request.length, dep->endpoint.maxpacket)
&& (dep->number == 0)) {
u32 transfer_size = 0;
u32 maxpacket;
- ret = usb_gadget_map_request(&dwc->gadget, &req->request,
- dep->number);
- if (ret) {
- dwc3_trace(trace_dwc3_ep0, "failed to map request");
+ ret = usb_gadget_map_request_by_dev(dwc->sysdev,
+ &req->request, dep->number);
+ if (ret)
return;
- }
maxpacket = dep->endpoint.maxpacket;
if (req->request.length > DWC3_EP0_BOUNCE_SIZE) {
transfer_size = ALIGN(req->request.length - maxpacket,
maxpacket);
- ret = dwc3_ep0_start_trans(dwc, dep->number,
+ dwc3_ep0_prepare_one_trb(dwc, dep->number,
req->request.dma,
transfer_size,
DWC3_TRBCTL_CONTROL_DATA,
@@ -998,20 +1028,20 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
dwc->ep0_bounced = true;
- ret = dwc3_ep0_start_trans(dwc, dep->number,
+ dwc3_ep0_prepare_one_trb(dwc, dep->number,
dwc->ep0_bounce_addr, transfer_size,
DWC3_TRBCTL_CONTROL_DATA, false);
+ ret = dwc3_ep0_start_trans(dwc, dep->number);
} else {
- ret = usb_gadget_map_request(&dwc->gadget, &req->request,
- dep->number);
- if (ret) {
- dwc3_trace(trace_dwc3_ep0, "failed to map request");
+ ret = usb_gadget_map_request_by_dev(dwc->sysdev,
+ &req->request, dep->number);
+ if (ret)
return;
- }
- ret = dwc3_ep0_start_trans(dwc, dep->number, req->request.dma,
+ dwc3_ep0_prepare_one_trb(dwc, dep->number, req->request.dma,
req->request.length, DWC3_TRBCTL_CONTROL_DATA,
false);
+ ret = dwc3_ep0_start_trans(dwc, dep->number);
}
WARN_ON(ret < 0);
@@ -1025,8 +1055,9 @@ static int dwc3_ep0_start_control_status(struct dwc3_ep *dep)
type = dwc->three_stage_setup ? DWC3_TRBCTL_CONTROL_STATUS3
: DWC3_TRBCTL_CONTROL_STATUS2;
- return dwc3_ep0_start_trans(dwc, dep->number,
+ dwc3_ep0_prepare_one_trb(dwc, dep->number,
dwc->ctrl_req_addr, 0, type, false);
+ return dwc3_ep0_start_trans(dwc, dep->number);
}
static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep)
@@ -1065,8 +1096,6 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
{
switch (event->status) {
case DEPEVT_STATUS_CONTROL_DATA:
- dwc3_trace(trace_dwc3_ep0, "Control Data");
-
/*
* We already have a DATA transfer in the controller's cache,
* if we receive a XferNotReady(DATA) we will ignore it, unless
@@ -1079,8 +1108,7 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
if (dwc->ep0_expect_in != event->endpoint_number) {
struct dwc3_ep *dep = dwc->eps[dwc->ep0_expect_in];
- dwc3_trace(trace_dwc3_ep0,
- "Wrong direction for Data phase");
+ dev_err(dwc->dev, "unexpected direction for Data Phase\n");
dwc3_ep0_end_control_data(dwc, dep);
dwc3_ep0_stall_and_restart(dwc);
return;
@@ -1092,13 +1120,24 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
if (dwc->ep0_next_event != DWC3_EP0_NRDY_STATUS)
return;
- dwc3_trace(trace_dwc3_ep0, "Control Status");
-
dwc->ep0state = EP0_STATUS_PHASE;
if (dwc->delayed_status) {
+ struct dwc3_ep *dep = dwc->eps[0];
+
WARN_ON_ONCE(event->endpoint_number != 1);
- dwc3_trace(trace_dwc3_ep0, "Delayed Status");
+ /*
+ * We should handle the delay STATUS phase here if the
+ * request for handling delay STATUS has been queued
+ * into the list.
+ */
+ if (!list_empty(&dep->pending_list)) {
+ dwc->delayed_status = false;
+ usb_gadget_set_state(&dwc->gadget,
+ USB_STATE_CONFIGURED);
+ dwc3_ep0_do_control_status(dwc, event);
+ }
+
return;
}
@@ -1109,10 +1148,6 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
void dwc3_ep0_interrupt(struct dwc3 *dwc,
const struct dwc3_event_depevt *event)
{
- dwc3_trace(trace_dwc3_ep0, "%s: state '%s'",
- dwc3_ep_event_string(event),
- dwc3_ep0_state_string(dwc->ep0state));
-
switch (event->endpoint_event) {
case DWC3_DEPEVT_XFERCOMPLETE:
dwc3_ep0_xfer_complete(dwc, event);
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 1dfa56a5f1c5..79e7a3480d51 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -139,9 +139,6 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)
udelay(5);
}
- dwc3_trace(trace_dwc3_gadget,
- "link state change request timed out");
-
return -ETIMEDOUT;
}
@@ -174,19 +171,29 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
int status)
{
struct dwc3 *dwc = dep->dwc;
+ unsigned int unmap_after_complete = false;
req->started = false;
list_del(&req->list);
req->trb = NULL;
+ req->remaining = 0;
if (req->request.status == -EINPROGRESS)
req->request.status = status;
- if (dwc->ep0_bounced && dep->number == 0)
+ /*
+ * NOTICE we don't want to unmap before calling ->complete() if we're
+ * dealing with a bounced ep0 request. If we unmap it here, we would end
+ * up overwritting the contents of req->buf and this could confuse the
+ * gadget driver.
+ */
+ if (dwc->ep0_bounced && dep->number <= 1) {
dwc->ep0_bounced = false;
- else
- usb_gadget_unmap_request(&dwc->gadget, &req->request,
- req->direction);
+ unmap_after_complete = true;
+ } else {
+ usb_gadget_unmap_request_by_dev(dwc->sysdev,
+ &req->request, req->direction);
+ }
trace_dwc3_gadget_giveback(req);
@@ -194,6 +201,10 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
usb_gadget_giveback_request(&dep->endpoint, &req->request);
spin_lock(&dwc->lock);
+ if (unmap_after_complete)
+ usb_gadget_unmap_request_by_dev(dwc->sysdev,
+ &req->request, req->direction);
+
if (dep->number > 1)
pm_runtime_put(dwc->dev);
}
@@ -216,7 +227,7 @@ int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param)
ret = -EINVAL;
break;
}
- } while (timeout--);
+ } while (--timeout);
if (!timeout) {
ret = -ETIMEDOUT;
@@ -233,6 +244,7 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc);
int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
struct dwc3_gadget_ep_cmd_params *params)
{
+ const struct usb_endpoint_descriptor *desc = dep->endpoint.desc;
struct dwc3 *dwc = dep->dwc;
u32 timeout = 500;
u32 reg;
@@ -258,7 +270,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
}
}
- if (cmd == DWC3_DEPCMD_STARTTRANSFER) {
+ if (DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_STARTTRANSFER) {
int needs_wakeup;
needs_wakeup = (dwc->link_state == DWC3_LINK_STATE_U1 ||
@@ -276,7 +288,28 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
dwc3_writel(dep->regs, DWC3_DEPCMDPAR1, params->param1);
dwc3_writel(dep->regs, DWC3_DEPCMDPAR2, params->param2);
- dwc3_writel(dep->regs, DWC3_DEPCMD, cmd | DWC3_DEPCMD_CMDACT);
+ /*
+ * Synopsys Databook 2.60a states in section 6.3.2.5.6 of that if we're
+ * not relying on XferNotReady, we can make use of a special "No
+ * Response Update Transfer" command where we should clear both CmdAct
+ * and CmdIOC bits.
+ *
+ * With this, we don't need to wait for command completion and can
+ * straight away issue further commands to the endpoint.
+ *
+ * NOTICE: We're making an assumption that control endpoints will never
+ * make use of Update Transfer command. This is a safe assumption
+ * because we can never have more than one request at a time with
+ * Control Endpoints. If anybody changes that assumption, this chunk
+ * needs to be updated accordingly.
+ */
+ if (DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_UPDATETRANSFER &&
+ !usb_endpoint_xfer_isoc(desc))
+ cmd &= ~(DWC3_DEPCMD_CMDIOC | DWC3_DEPCMD_CMDACT);
+ else
+ cmd |= DWC3_DEPCMD_CMDACT;
+
+ dwc3_writel(dep->regs, DWC3_DEPCMD, cmd);
do {
reg = dwc3_readl(dep->regs, DWC3_DEPCMD);
if (!(reg & DWC3_DEPCMD_CMDACT)) {
@@ -318,6 +351,20 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
trace_dwc3_gadget_ep_cmd(dep, cmd, params, cmd_status);
+ if (ret == 0) {
+ switch (DWC3_DEPCMD_CMD(cmd)) {
+ case DWC3_DEPCMD_STARTTRANSFER:
+ dep->flags |= DWC3_EP_TRANSFER_STARTED;
+ break;
+ case DWC3_DEPCMD_ENDTRANSFER:
+ dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
+ break;
+ default:
+ /* nothing */
+ break;
+ }
+ }
+
if (unlikely(susphy)) {
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
reg |= DWC3_GUSB2PHYCFG_SUSPHY;
@@ -365,7 +412,7 @@ static int dwc3_alloc_trb_pool(struct dwc3_ep *dep)
if (dep->trb_pool)
return 0;
- dep->trb_pool = dma_alloc_coherent(dwc->dev,
+ dep->trb_pool = dma_alloc_coherent(dwc->sysdev,
sizeof(struct dwc3_trb) * DWC3_TRB_NUM,
&dep->trb_pool_dma, GFP_KERNEL);
if (!dep->trb_pool) {
@@ -381,7 +428,7 @@ static void dwc3_free_trb_pool(struct dwc3_ep *dep)
{
struct dwc3 *dwc = dep->dwc;
- dma_free_coherent(dwc->dev, sizeof(struct dwc3_trb) * DWC3_TRB_NUM,
+ dma_free_coherent(dwc->sysdev, sizeof(struct dwc3_trb) * DWC3_TRB_NUM,
dep->trb_pool, dep->trb_pool_dma);
dep->trb_pool = NULL;
@@ -454,16 +501,19 @@ static int dwc3_gadget_start_config(struct dwc3 *dwc, struct dwc3_ep *dep)
}
static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep,
- const struct usb_endpoint_descriptor *desc,
- const struct usb_ss_ep_comp_descriptor *comp_desc,
bool modify, bool restore)
{
+ 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;
+ comp_desc = dep->endpoint.comp_desc;
+ desc = dep->endpoint.desc;
+
memset(&params, 0x00, sizeof(params));
params.param0 = DWC3_DEPCFG_EP_TYPE(usb_endpoint_type(desc))
@@ -542,24 +592,21 @@ static int dwc3_gadget_set_xfer_resource(struct dwc3 *dwc, struct dwc3_ep *dep)
* Caller should take care of locking
*/
static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
- const struct usb_endpoint_descriptor *desc,
- const struct usb_ss_ep_comp_descriptor *comp_desc,
bool modify, bool restore)
{
+ const struct usb_endpoint_descriptor *desc = dep->endpoint.desc;
struct dwc3 *dwc = dep->dwc;
+
u32 reg;
int ret;
- dwc3_trace(trace_dwc3_gadget, "Enabling %s", dep->name);
-
if (!(dep->flags & DWC3_EP_ENABLED)) {
ret = dwc3_gadget_start_config(dwc, dep);
if (ret)
return ret;
}
- ret = dwc3_gadget_set_ep_config(dwc, dep, desc, comp_desc, modify,
- restore);
+ ret = dwc3_gadget_set_ep_config(dwc, dep, modify, restore);
if (ret)
return ret;
@@ -567,17 +614,18 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
struct dwc3_trb *trb_st_hw;
struct dwc3_trb *trb_link;
- dep->endpoint.desc = desc;
- dep->comp_desc = comp_desc;
dep->type = usb_endpoint_type(desc);
dep->flags |= DWC3_EP_ENABLED;
+ dep->flags &= ~DWC3_EP_END_TRANSFER_PENDING;
reg = dwc3_readl(dwc->regs, DWC3_DALEPENA);
reg |= DWC3_DALEPENA_EP(dep->number);
dwc3_writel(dwc->regs, DWC3_DALEPENA, reg);
+ init_waitqueue_head(&dep->wait_end_transfer);
+
if (usb_endpoint_xfer_control(desc))
- return 0;
+ goto out;
/* Initialize the TRB ring */
dep->trb_dequeue = 0;
@@ -595,6 +643,39 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
trb_link->ctrl |= DWC3_TRB_CTRL_HWO;
}
+ /*
+ * Issue StartTransfer here with no-op TRB so we can always rely on No
+ * Response Update Transfer command.
+ */
+ if (usb_endpoint_xfer_bulk(desc)) {
+ struct dwc3_gadget_ep_cmd_params params;
+ struct dwc3_trb *trb;
+ dma_addr_t trb_dma;
+ u32 cmd;
+
+ memset(&params, 0, sizeof(params));
+ trb = &dep->trb_pool[0];
+ trb_dma = dwc3_trb_dma_offset(dep, trb);
+
+ params.param0 = upper_32_bits(trb_dma);
+ params.param1 = lower_32_bits(trb_dma);
+
+ cmd = DWC3_DEPCMD_STARTTRANSFER;
+
+ 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;
}
@@ -632,7 +713,7 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
struct dwc3 *dwc = dep->dwc;
u32 reg;
- dwc3_trace(trace_dwc3_gadget, "Disabling %s", dep->name);
+ trace_dwc3_gadget_ep_disable(dep);
dwc3_remove_requests(dwc, dep);
@@ -645,10 +726,14 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
dwc3_writel(dwc->regs, DWC3_DALEPENA, reg);
dep->stream_capable = false;
- dep->endpoint.desc = NULL;
- dep->comp_desc = NULL;
dep->type = 0;
- dep->flags = 0;
+ dep->flags &= DWC3_EP_END_TRANSFER_PENDING;
+
+ /* Clear out the ep descriptors for non-ep0 */
+ if (dep->number > 1) {
+ dep->endpoint.comp_desc = NULL;
+ dep->endpoint.desc = NULL;
+ }
return 0;
}
@@ -695,7 +780,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, desc, ep->comp_desc, false, false);
+ ret = __dwc3_gadget_ep_enable(dep, false, false);
spin_unlock_irqrestore(&dwc->lock, flags);
return ret;
@@ -761,30 +846,13 @@ static void dwc3_gadget_ep_free_request(struct usb_ep *ep,
static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep);
-/**
- * dwc3_prepare_one_trb - setup one TRB from one request
- * @dep: endpoint for which this request is prepared
- * @req: dwc3_request pointer
- */
-static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
- struct dwc3_request *req, dma_addr_t dma,
- unsigned length, unsigned chain, unsigned node)
+static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb,
+ dma_addr_t dma, unsigned length, unsigned chain, unsigned node,
+ unsigned stream_id, unsigned short_not_ok, unsigned no_interrupt)
{
- struct dwc3_trb *trb;
-
- dwc3_trace(trace_dwc3_gadget, "%s: req %p dma %08llx length %d%s",
- dep->name, req, (unsigned long long) dma,
- length, chain ? " chain" : "");
-
- trb = &dep->trb_pool[dep->trb_enqueue];
-
- if (!req->trb) {
- dwc3_gadget_move_started_request(req);
- req->trb = trb;
- req->trb_dma = dwc3_trb_dma_offset(dep, trb);
- req->first_trb_index = dep->trb_enqueue;
- dep->queued_requests++;
- }
+ struct dwc3 *dwc = dep->dwc;
+ struct usb_gadget *gadget = &dwc->gadget;
+ enum usb_device_speed speed = gadget->speed;
dwc3_ep_inc_enq(dep);
@@ -798,10 +866,16 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
break;
case USB_ENDPOINT_XFER_ISOC:
- if (!node)
+ if (!node) {
trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS_FIRST;
- else
+
+ if (speed == USB_SPEED_HIGH) {
+ struct usb_ep *ep = &dep->endpoint;
+ trb->size |= DWC3_TRB_SIZE_PCM1(ep->mult - 1);
+ }
+ } else {
trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS;
+ }
/* always enable Interrupt on Missed ISOC */
trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI;
@@ -816,21 +890,27 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
* This is only possible with faulty memory because we
* checked it already :)
*/
- BUG();
+ dev_WARN(dwc->dev, "Unknown endpoint type %d\n",
+ usb_endpoint_type(dep->endpoint.desc));
}
/* always enable Continue on Short Packet */
- trb->ctrl |= DWC3_TRB_CTRL_CSP;
+ if (usb_endpoint_dir_out(dep->endpoint.desc)) {
+ trb->ctrl |= DWC3_TRB_CTRL_CSP;
- if ((!req->request.no_interrupt && !chain) ||
+ if (short_not_ok)
+ trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI;
+ }
+
+ if ((!no_interrupt && !chain) ||
(dwc3_calc_trbs_left(dep) == 0))
- trb->ctrl |= DWC3_TRB_CTRL_IOC | DWC3_TRB_CTRL_ISP_IMI;
+ trb->ctrl |= DWC3_TRB_CTRL_IOC;
if (chain)
trb->ctrl |= DWC3_TRB_CTRL_CHN;
if (usb_endpoint_xfer_bulk(dep->endpoint.desc) && dep->stream_capable)
- trb->ctrl |= DWC3_TRB_CTRL_SID_SOFN(req->request.stream_id);
+ trb->ctrl |= DWC3_TRB_CTRL_SID_SOFN(stream_id);
trb->ctrl |= DWC3_TRB_CTRL_HWO;
@@ -838,6 +918,36 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
}
/**
+ * dwc3_prepare_one_trb - setup one TRB from one request
+ * @dep: endpoint for which this request is prepared
+ * @req: dwc3_request pointer
+ * @chain: should this TRB be chained to the next?
+ * @node: only for isochronous endpoints. First TRB needs different type.
+ */
+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 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;
+
+ trb = &dep->trb_pool[dep->trb_enqueue];
+
+ if (!req->trb) {
+ 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
@@ -859,6 +969,7 @@ static struct dwc3_trb *dwc3_ep_prev_trb(struct dwc3_ep *dep, u8 index)
static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep)
{
struct dwc3_trb *tmp;
+ struct dwc3 *dwc = dep->dwc;
u8 trbs_left;
/*
@@ -870,7 +981,8 @@ static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep)
*/
if (dep->trb_enqueue == dep->trb_dequeue) {
tmp = dwc3_ep_prev_trb(dep, dep->trb_enqueue);
- if (tmp->ctrl & DWC3_TRB_CTRL_HWO)
+ if (dev_WARN_ONCE(dwc->dev, tmp->ctrl & DWC3_TRB_CTRL_HWO,
+ "%s No TRBS left\n", dep->name))
return 0;
return DWC3_TRB_NUM - 1;
@@ -890,21 +1002,36 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
{
struct scatterlist *sg = req->sg;
struct scatterlist *s;
- unsigned int length;
- dma_addr_t dma;
int i;
for_each_sg(sg, s, req->num_pending_sgs, i) {
+ unsigned int length = req->request.length;
+ unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc);
+ unsigned int rem = length % maxp;
unsigned chain = true;
- length = sg_dma_len(s);
- dma = sg_dma_address(s);
-
if (sg_is_last(s))
chain = false;
- dwc3_prepare_one_trb(dep, req, dma, length,
- chain, i);
+ if (rem && usb_endpoint_dir_out(dep->endpoint.desc) && !chain) {
+ struct dwc3 *dwc = dep->dwc;
+ struct dwc3_trb *trb;
+
+ req->unaligned = true;
+
+ /* prepare normal TRB */
+ dwc3_prepare_one_trb(dep, req, true, i);
+
+ /* 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,
+ req->request.short_not_ok,
+ req->request.no_interrupt);
+ } else {
+ dwc3_prepare_one_trb(dep, req, chain, i);
+ }
if (!dwc3_calc_trbs_left(dep))
break;
@@ -914,14 +1041,28 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
struct dwc3_request *req)
{
- unsigned int length;
- dma_addr_t dma;
+ unsigned int length = req->request.length;
+ unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc);
+ unsigned int rem = length % maxp;
+
+ if (rem && usb_endpoint_dir_out(dep->endpoint.desc)) {
+ struct dwc3 *dwc = dep->dwc;
+ struct dwc3_trb *trb;
- dma = req->request.dma;
- length = req->request.length;
+ req->unaligned = true;
- dwc3_prepare_one_trb(dep, req, dma, length,
- false, 0);
+ /* prepare normal TRB */
+ dwc3_prepare_one_trb(dep, req, true, 0);
+
+ /* 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,
+ req->request.short_not_ok,
+ req->request.no_interrupt);
+ } else {
+ dwc3_prepare_one_trb(dep, req, false, 0);
+ }
}
/*
@@ -941,6 +1082,24 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep)
if (!dwc3_calc_trbs_left(dep))
return;
+ /*
+ * We can get in a situation where there's a request in the started list
+ * but there weren't enough TRBs to fully kick it in the first time
+ * around, so it has been waiting for more TRBs to be freed up.
+ *
+ * In that case, we should check if we have a request with pending_sgs
+ * in the started list and prepare TRBs for that request first,
+ * otherwise we will prepare TRBs completely out of order and that will
+ * break things.
+ */
+ list_for_each_entry(req, &dep->started_list, list) {
+ if (req->num_pending_sgs > 0)
+ dwc3_prepare_one_trb_sg(dep, req);
+
+ if (!dwc3_calc_trbs_left(dep))
+ return;
+ }
+
list_for_each_entry_safe(req, n, &dep->pending_list, list) {
if (req->num_pending_sgs > 0)
dwc3_prepare_one_trb_sg(dep, req);
@@ -956,7 +1115,6 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param)
{
struct dwc3_gadget_ep_cmd_params params;
struct dwc3_request *req;
- struct dwc3 *dwc = dep->dwc;
int starting;
int ret;
u32 cmd;
@@ -989,9 +1147,10 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param)
* here and stop, unmap, free and del each of the linked
* requests instead of what we do now.
*/
- usb_gadget_unmap_request(&dwc->gadget, &req->request,
- req->direction);
- list_del(&req->list);
+ if (req->trb)
+ memset(req->trb, 0, sizeof(struct dwc3_trb));
+ dep->queued_requests--;
+ dwc3_gadget_giveback(dep, req, ret);
return ret;
}
@@ -1005,14 +1164,21 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param)
return 0;
}
+static int __dwc3_gadget_get_frame(struct dwc3 *dwc)
+{
+ u32 reg;
+
+ reg = dwc3_readl(dwc->regs, DWC3_DSTS);
+ return DWC3_DSTS_SOFFN(reg);
+}
+
static void __dwc3_gadget_start_isoc(struct dwc3 *dwc,
struct dwc3_ep *dep, u32 cur_uf)
{
u32 uf;
if (list_empty(&dep->pending_list)) {
- dwc3_trace(trace_dwc3_gadget,
- "ISOC ep %s run out for requests",
+ dev_info(dwc->dev, "%s: ran out of requests\n",
dep->name);
dep->flags |= DWC3_EP_PENDING_REQUEST;
return;
@@ -1041,16 +1207,15 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
int ret;
if (!dep->endpoint.desc) {
- dwc3_trace(trace_dwc3_gadget,
- "trying to queue request %p to disabled %s",
- &req->request, dep->endpoint.name);
+ dev_err(dwc->dev, "%s: can't queue to disabled endpoint\n",
+ dep->name);
return -ESHUTDOWN;
}
if (WARN(req->dep != dep, "request %p belongs to '%s'\n",
&req->request, req->dep->name)) {
- dwc3_trace(trace_dwc3_gadget, "request %p belongs to '%s'",
- &req->request, req->dep->name);
+ dev_err(dwc->dev, "%s: request %p belongs to '%s'\n",
+ dep->name, &req->request, req->dep->name);
return -EINVAL;
}
@@ -1063,8 +1228,8 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
trace_dwc3_ep_queue(req);
- ret = usb_gadget_map_request(&dwc->gadget, &req->request,
- dep->direction);
+ ret = usb_gadget_map_request_by_dev(dwc->sysdev, &req->request,
+ dep->direction);
if (ret)
return ret;
@@ -1082,10 +1247,17 @@ 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) &&
- list_empty(&dep->started_list)) {
- dwc3_stop_active_transfer(dwc, dep->number, true);
- dep->flags = DWC3_EP_ENABLED;
+ 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;
+
+ cur_uf = __dwc3_gadget_get_frame(dwc);
+ __dwc3_gadget_start_isoc(dwc, dep, cur_uf);
+ dep->flags &= ~DWC3_EP_PENDING_REQUEST;
+ }
}
return 0;
}
@@ -1094,10 +1266,6 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
return 0;
ret = __dwc3_gadget_kick_transfer(dep, 0);
- if (ret && ret != -EBUSY)
- dwc3_trace(trace_dwc3_gadget,
- "%s: failed to kick transfers",
- dep->name);
if (ret == -EBUSY)
ret = 0;
@@ -1116,7 +1284,6 @@ static int __dwc3_gadget_ep_queue_zlp(struct dwc3 *dwc, struct dwc3_ep *dep)
struct usb_request *request;
struct usb_ep *ep = &dep->endpoint;
- dwc3_trace(trace_dwc3_gadget, "queueing ZLP");
request = dwc3_gadget_ep_alloc_request(ep, GFP_ATOMIC);
if (!request)
return -ENOMEM;
@@ -1188,6 +1355,68 @@ 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);
+
+ /*
+ * If request was already started, this means we had to
+ * stop the transfer. With that we also need to ignore
+ * all TRBs used by the request, however TRBs can only
+ * be modified after completion of END_TRANSFER
+ * command. So what we do here is that we wait for
+ * END_TRANSFER completion and only after that, we jump
+ * over TRBs by clearing HWO and incrementing dequeue
+ * pointer.
+ *
+ * Note that we have 2 possible types of transfers here:
+ *
+ * i) Linear buffer request
+ * ii) SG-list based request
+ *
+ * SG-list based requests will have r->num_pending_sgs
+ * set to a valid number (> 0). Linear requests,
+ * normally use a single TRB.
+ *
+ * For each of these two cases, if r->unaligned flag is
+ * set, one extra TRB has been used to align transfer
+ * size to wMaxPacketSize.
+ *
+ * All of these cases need to be taken into
+ * consideration so we don't mess up our TRB ring
+ * pointers.
+ */
+ wait_event_lock_irq(dep->wait_end_transfer,
+ !(dep->flags & DWC3_EP_END_TRANSFER_PENDING),
+ dwc->lock);
+
+ if (!r->trb)
+ goto out1;
+
+ if (r->num_pending_sgs) {
+ struct dwc3_trb *trb;
+ int i = 0;
+
+ for (i = 0; i < r->num_pending_sgs; i++) {
+ trb = r->trb + i;
+ trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
+ dwc3_ep_inc_deq(dep);
+ }
+
+ if (r->unaligned) {
+ trb = r->trb + r->num_pending_sgs + 1;
+ trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
+ dwc3_ep_inc_deq(dep);
+ }
+ } else {
+ struct dwc3_trb *trb = r->trb;
+
+ trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
+ dwc3_ep_inc_deq(dep);
+
+ if (r->unaligned) {
+ trb = r->trb + 1;
+ trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
+ dwc3_ep_inc_deq(dep);
+ }
+ }
goto out1;
}
dev_err(dwc->dev, "request %p was not queued to %s\n",
@@ -1198,6 +1427,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:
@@ -1225,6 +1455,9 @@ 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
@@ -1235,9 +1468,6 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
if (!protocol && ((dep->direction && transfer_in_flight) ||
(!dep->direction && started))) {
- dwc3_trace(trace_dwc3_gadget,
- "%s: pending request, cannot halt",
- dep->name);
return -EAGAIN;
}
@@ -1249,6 +1479,8 @@ 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)
@@ -1331,10 +1563,8 @@ static const struct usb_ep_ops dwc3_gadget_ep_ops = {
static int dwc3_gadget_get_frame(struct usb_gadget *g)
{
struct dwc3 *dwc = gadget_to_dwc(g);
- u32 reg;
- reg = dwc3_readl(dwc->regs, DWC3_DSTS);
- return DWC3_DSTS_SOFFN(reg);
+ return __dwc3_gadget_get_frame(dwc);
}
static int __dwc3_gadget_wakeup(struct dwc3 *dwc)
@@ -1357,10 +1587,8 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc)
speed = reg & DWC3_DSTS_CONNECTSPD;
if ((speed == DWC3_DSTS_SUPERSPEED) ||
- (speed == DWC3_DSTS_SUPERSPEED_PLUS)) {
- dwc3_trace(trace_dwc3_gadget, "no wakeup on SuperSpeed");
+ (speed == DWC3_DSTS_SUPERSPEED_PLUS))
return 0;
- }
link_state = DWC3_DSTS_USBLNKST(reg);
@@ -1369,9 +1597,6 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc)
case DWC3_LINK_STATE_U3: /* in HS, means SUSPEND */
break;
default:
- dwc3_trace(trace_dwc3_gadget,
- "can't wakeup from '%s'",
- dwc3_gadget_link_string(link_state));
return -EINVAL;
}
@@ -1476,11 +1701,6 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
if (!timeout)
return -ETIMEDOUT;
- dwc3_trace(trace_dwc3_gadget, "gadget %s data soft-%s",
- dwc->gadget_driver
- ? dwc->gadget_driver->function : "no-function",
- is_on ? "connect" : "disconnect");
-
return 0;
}
@@ -1492,6 +1712,21 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
is_on = !!is_on;
+ /*
+ * Per databook, when we want to stop the gadget, if a control transfer
+ * is still in process, complete it and get the core into setup phase.
+ */
+ if (!is_on && dwc->ep0state != EP0_SETUP_PHASE) {
+ reinit_completion(&dwc->ep0_in_setup);
+
+ ret = wait_for_completion_timeout(&dwc->ep0_in_setup,
+ msecs_to_jiffies(DWC3_PULL_UP_TIMEOUT));
+ if (ret == 0) {
+ dev_err(dwc->dev, "timed out waiting for SETUP phase\n");
+ return -ETIMEDOUT;
+ }
+ }
+
spin_lock_irqsave(&dwc->lock, flags);
ret = dwc3_gadget_run_stop(dwc, is_on, false);
spin_unlock_irqrestore(&dwc->lock, flags);
@@ -1509,11 +1744,13 @@ static void dwc3_gadget_enable_irq(struct dwc3 *dwc)
DWC3_DEVTEN_CMDCMPLTEN |
DWC3_DEVTEN_ERRTICERREN |
DWC3_DEVTEN_WKUPEVTEN |
- DWC3_DEVTEN_ULSTCNGEN |
DWC3_DEVTEN_CONNECTDONEEN |
DWC3_DEVTEN_USBRSTEN |
DWC3_DEVTEN_DISCONNEVTEN);
+ if (dwc->revision < DWC3_REVISION_250A)
+ reg |= DWC3_DEVTEN_ULSTCNGEN;
+
dwc3_writel(dwc->regs, DWC3_DEVTEN, reg);
}
@@ -1573,6 +1810,17 @@ static int __dwc3_gadget_start(struct dwc3 *dwc)
int ret = 0;
u32 reg;
+ /*
+ * Use IMOD if enabled via dwc->imod_interval. Otherwise, if
+ * the core supports IMOD, disable it.
+ */
+ if (dwc->imod_interval) {
+ dwc3_writel(dwc->regs, DWC3_DEV_IMOD(0), dwc->imod_interval);
+ dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), DWC3_GEVNTCOUNT_EHB);
+ } else if (dwc3_has_imod(dwc)) {
+ dwc3_writel(dwc->regs, DWC3_DEV_IMOD(0), 0);
+ }
+
reg = dwc3_readl(dwc->regs, DWC3_DCFG);
reg &= ~(DWC3_DCFG_SPEED_MASK);
@@ -1597,7 +1845,7 @@ static int __dwc3_gadget_start(struct dwc3 *dwc)
reg |= DWC3_DCFG_LOWSPEED;
break;
case USB_SPEED_FULL:
- reg |= DWC3_DCFG_FULLSPEED1;
+ reg |= DWC3_DCFG_FULLSPEED;
break;
case USB_SPEED_HIGH:
reg |= DWC3_DCFG_HIGHSPEED;
@@ -1633,16 +1881,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, &dwc3_gadget_ep0_desc, NULL, false,
- false);
+ ret = __dwc3_gadget_ep_enable(dep, false, false);
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, &dwc3_gadget_ep0_desc, NULL, false,
- false);
+ ret = __dwc3_gadget_ep_enable(dep, false, false);
if (ret) {
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
goto err1;
@@ -1708,9 +1954,6 @@ err0:
static void __dwc3_gadget_stop(struct dwc3 *dwc)
{
- if (pm_runtime_suspended(dwc->dev))
- return;
-
dwc3_gadget_disable_irq(dwc);
__dwc3_gadget_ep_disable(dwc->eps[0]);
__dwc3_gadget_ep_disable(dwc->eps[1]);
@@ -1720,9 +1963,30 @@ static int dwc3_gadget_stop(struct usb_gadget *g)
{
struct dwc3 *dwc = gadget_to_dwc(g);
unsigned long flags;
+ int epnum;
spin_lock_irqsave(&dwc->lock, flags);
+
+ if (pm_runtime_suspended(dwc->dev))
+ goto out;
+
__dwc3_gadget_stop(dwc);
+
+ for (epnum = 2; epnum < DWC3_ENDPOINTS_NUM; epnum++) {
+ struct dwc3_ep *dep = dwc->eps[epnum];
+
+ if (!dep)
+ continue;
+
+ if (!(dep->flags & DWC3_EP_END_TRANSFER_PENDING))
+ continue;
+
+ wait_event_lock_irq(dep->wait_end_transfer,
+ !(dep->flags & DWC3_EP_END_TRANSFER_PENDING),
+ dwc->lock);
+ }
+
+out:
dwc->gadget_driver = NULL;
spin_unlock_irqrestore(&dwc->lock, flags);
@@ -1765,9 +2029,13 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc,
(epnum & 1) ? "in" : "out");
dep->endpoint.name = dep->name;
- spin_lock_init(&dep->lock);
- dwc3_trace(trace_dwc3_gadget, "initializing %s", dep->name);
+ if (!(dep->number > 1)) {
+ dep->endpoint.desc = &dwc3_gadget_ep0_desc;
+ dep->endpoint.comp_desc = NULL;
+ }
+
+ spin_lock_init(&dep->lock);
if (epnum == 0 || epnum == 1) {
usb_ep_set_maxpacket_limit(&dep->endpoint, 512);
@@ -1775,6 +2043,44 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc,
dep->endpoint.ops = &dwc3_gadget_ep0_ops;
if (!epnum)
dwc->gadget.ep0 = &dep->endpoint;
+ } else if (direction) {
+ int mdwidth;
+ int size;
+ int ret;
+ int num;
+
+ 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(i));
+ size = DWC3_GTXFIFOSIZ_TXFDEF(size);
+
+ /* FIFO Depth is in MDWDITH bytes. Multiply */
+ size *= mdwidth;
+
+ num = size / 1024;
+ if (num == 0)
+ num = 1;
+
+ /*
+ * FIFO sizes account an extra MDWIDTH * (num + 1) bytes for
+ * internal overhead. We don't really know how these are used,
+ * but documentation say it exists.
+ */
+ size -= mdwidth * (num + 1);
+ size /= num;
+
+ usb_ep_set_maxpacket_limit(&dep->endpoint, size);
+
+ dep->endpoint.max_streams = 15;
+ dep->endpoint.ops = &dwc3_gadget_ep_ops;
+ list_add_tail(&dep->endpoint.ep_list,
+ &dwc->gadget.ep_list);
+
+ ret = dwc3_alloc_trb_pool(dep);
+ if (ret)
+ return ret;
} else {
int ret;
@@ -1815,15 +2121,13 @@ static int dwc3_gadget_init_endpoints(struct dwc3 *dwc)
ret = dwc3_gadget_init_hw_endpoints(dwc, dwc->num_out_eps, 0);
if (ret < 0) {
- dwc3_trace(trace_dwc3_gadget,
- "failed to allocate OUT endpoints");
+ dev_err(dwc->dev, "failed to initialize OUT endpoints\n");
return ret;
}
ret = dwc3_gadget_init_hw_endpoints(dwc, dwc->num_in_eps, 1);
if (ret < 0) {
- dwc3_trace(trace_dwc3_gadget,
- "failed to allocate IN endpoints");
+ dev_err(dwc->dev, "failed to initialize IN endpoints\n");
return ret;
}
@@ -1888,19 +2192,26 @@ static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep,
if (chain && (trb->ctrl & DWC3_TRB_CTRL_HWO))
trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
- if ((trb->ctrl & DWC3_TRB_CTRL_HWO) && status != -ESHUTDOWN)
+ /*
+ * If we're dealing with unaligned size OUT transfer, we will be left
+ * with one TRB pending in the ring. We need to manually clear HWO bit
+ * from that TRB.
+ */
+ if (req->unaligned && (trb->ctrl & DWC3_TRB_CTRL_HWO)) {
+ trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
return 1;
+ }
count = trb->size & DWC3_TRB_SIZE_MASK;
- req->request.actual += count;
+ req->remaining += count;
+
+ 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) {
- dwc3_trace(trace_dwc3_gadget,
- "%s: incomplete IN transfer",
- dep->name);
/*
* If missed isoc occurred and there is
* no request queued then issue END
@@ -1946,11 +2257,10 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
struct dwc3_request *req, *n;
struct dwc3_trb *trb;
bool ioc = false;
- int ret;
+ int ret = 0;
list_for_each_entry_safe(req, n, &dep->started_list, list) {
unsigned length;
- unsigned actual;
int chain;
length = req->request.length;
@@ -1964,6 +2274,9 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
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--;
@@ -1978,17 +2291,16 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
event, status, chain);
}
- /*
- * We assume here we will always receive the entire data block
- * which we should receive. Meaning, if we program RX to
- * receive 4K but we receive only 2K, we assume that's all we
- * should receive and we simply bounce the request back to the
- * gadget driver for further processing.
- */
- actual = length - req->request.actual;
- req->request.actual = actual;
+ if (req->unaligned) {
+ trb = &dep->trb_pool[dep->trb_dequeue];
+ ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb,
+ event, status, false);
+ req->unaligned = false;
+ }
+
+ req->request.actual = length - req->remaining;
- if (ret && chain && (actual < length) && req->num_pending_sgs)
+ if ((req->request.actual < length) && req->num_pending_sgs)
return __dwc3_gadget_kick_transfer(dep, 0);
dwc3_gadget_giveback(dep, req, status);
@@ -2096,11 +2408,18 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
{
struct dwc3_ep *dep;
u8 epnum = event->endpoint_number;
+ u8 cmd;
dep = dwc->eps[epnum];
- if (!(dep->flags & DWC3_EP_ENABLED))
- return;
+ if (!(dep->flags & DWC3_EP_ENABLED)) {
+ if (!(dep->flags & DWC3_EP_END_TRANSFER_PENDING))
+ return;
+
+ /* Handle only EPCMDCMPLT when EP disabled */
+ if (event->endpoint_event != DWC3_DEPEVT_EPCMDCMPLT)
+ return;
+ }
if (epnum == 0 || epnum == 1) {
dwc3_ep0_interrupt(dwc, event);
@@ -2112,9 +2431,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
dep->resource_index = 0;
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
- dwc3_trace(trace_dwc3_gadget,
- "%s is an Isochronous endpoint",
- dep->name);
+ dev_err(dwc->dev, "XferComplete for Isochronous endpoint\n");
return;
}
@@ -2127,22 +2444,11 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
dwc3_gadget_start_isoc(dwc, dep, event);
} else {
- int active;
int ret;
- active = event->status & DEPEVT_STATUS_TRANSFER_ACTIVE;
-
- dwc3_trace(trace_dwc3_gadget, "%s: reason %s",
- dep->name, active ? "Transfer Active"
- : "Transfer Not Active");
-
ret = __dwc3_gadget_kick_transfer(dep, 0);
if (!ret || ret == -EBUSY)
return;
-
- dwc3_trace(trace_dwc3_gadget,
- "%s: failed to kick transfers",
- dep->name);
}
break;
@@ -2152,26 +2458,16 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
dep->name);
return;
}
+ break;
+ case DWC3_DEPEVT_EPCMDCMPLT:
+ cmd = DEPEVT_PARAMETER_CMD(event->parameters);
- switch (event->status) {
- case DEPEVT_STREAMEVT_FOUND:
- dwc3_trace(trace_dwc3_gadget,
- "Stream %d found and started",
- event->parameters);
-
- break;
- case DEPEVT_STREAMEVT_NOTFOUND:
- /* FALLTHROUGH */
- default:
- dwc3_trace(trace_dwc3_gadget,
- "unable to find suitable stream");
+ if (cmd == DWC3_DEPCMD_ENDTRANSFER) {
+ dep->flags &= ~DWC3_EP_END_TRANSFER_PENDING;
+ wake_up(&dep->wait_end_transfer);
}
break;
case DWC3_DEPEVT_RXTXFIFOEVT:
- dwc3_trace(trace_dwc3_gadget, "%s FIFO Overrun", dep->name);
- break;
- case DWC3_DEPEVT_EPCMDCMPLT:
- dwc3_trace(trace_dwc3_gadget, "Endpoint Command Complete");
break;
}
}
@@ -2224,7 +2520,8 @@ static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force)
dep = dwc->eps[epnum];
- if (!dep->resource_index)
+ if ((dep->flags & DWC3_EP_END_TRANSFER_PENDING) ||
+ !dep->resource_index)
return;
/*
@@ -2268,25 +2565,9 @@ static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force)
dep->resource_index = 0;
dep->flags &= ~DWC3_EP_BUSY;
- if (dwc3_is_usb31(dwc) || dwc->revision < DWC3_REVISION_310A)
+ if (dwc3_is_usb31(dwc) || dwc->revision < DWC3_REVISION_310A) {
+ dep->flags |= DWC3_EP_END_TRANSFER_PENDING;
udelay(100);
-}
-
-static void dwc3_stop_active_transfers(struct dwc3 *dwc)
-{
- u32 epnum;
-
- for (epnum = 2; epnum < DWC3_ENDPOINTS_NUM; epnum++) {
- struct dwc3_ep *dep;
-
- dep = dwc->eps[epnum];
- if (!dep)
- continue;
-
- if (!(dep->flags & DWC3_EP_ENABLED))
- continue;
-
- dwc3_remove_requests(dwc, dep);
}
}
@@ -2375,8 +2656,6 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
reg &= ~DWC3_DCTL_TSTCTRL_MASK;
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
dwc->test_mode = false;
-
- dwc3_stop_active_transfers(dwc);
dwc3_clear_stall_all_ep(dwc);
/* Reset device address to zero */
@@ -2385,32 +2664,6 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
}
-static void dwc3_update_ram_clk_sel(struct dwc3 *dwc, u32 speed)
-{
- u32 reg;
- u32 usb30_clock = DWC3_GCTL_CLK_BUS;
-
- /*
- * We change the clock only at SS but I dunno why I would want to do
- * this. Maybe it becomes part of the power saving plan.
- */
-
- if ((speed != DWC3_DSTS_SUPERSPEED) &&
- (speed != DWC3_DSTS_SUPERSPEED_PLUS))
- return;
-
- /*
- * RAMClkSel is reset to 0 after USB reset, so it must be reprogrammed
- * each time on Connect Done.
- */
- if (!usb30_clock)
- return;
-
- reg = dwc3_readl(dwc->regs, DWC3_GCTL);
- reg |= DWC3_GCTL_RAMCLKSEL(usb30_clock);
- dwc3_writel(dwc->regs, DWC3_GCTL, reg);
-}
-
static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
{
struct dwc3_ep *dep;
@@ -2422,7 +2675,14 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
speed = reg & DWC3_DSTS_CONNECTSPD;
dwc->speed = speed;
- dwc3_update_ram_clk_sel(dwc, speed);
+ /*
+ * RAMClkSel is reset to 0 after USB reset, so it must be reprogrammed
+ * each time on Connect Done.
+ *
+ * Currently we always use the reset value. If any platform
+ * wants to set this to a different value, we need to add a
+ * setting and update GCTL.RAMCLKSEL here.
+ */
switch (speed) {
case DWC3_DSTS_SUPERSPEED_PLUS:
@@ -2456,8 +2716,7 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
dwc->gadget.ep0->maxpacket = 64;
dwc->gadget.speed = USB_SPEED_HIGH;
break;
- case DWC3_DSTS_FULLSPEED2:
- case DWC3_DSTS_FULLSPEED1:
+ case DWC3_DSTS_FULLSPEED:
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64);
dwc->gadget.ep0->maxpacket = 64;
dwc->gadget.speed = USB_SPEED_FULL;
@@ -2491,7 +2750,7 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
*/
WARN_ONCE(dwc->revision < DWC3_REVISION_240A
&& dwc->has_lpm_erratum,
- "LPM Erratum not available on dwc3 revisisions < 2.40a\n");
+ "LPM Erratum not available on dwc3 revisions < 2.40a\n");
if (dwc->has_lpm_erratum && dwc->revision >= DWC3_REVISION_240A)
reg |= DWC3_DCTL_LPM_ERRATA(dwc->lpm_nyet_threshold);
@@ -2504,16 +2763,14 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
}
dep = dwc->eps[0];
- ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, true,
- false);
+ ret = __dwc3_gadget_ep_enable(dep, true, false);
if (ret) {
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
return;
}
dep = dwc->eps[1];
- ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, true,
- false);
+ ret = __dwc3_gadget_ep_enable(dep, true, false);
if (ret) {
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
return;
@@ -2570,8 +2827,6 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
(pwropt != DWC3_GHWPARAMS1_EN_PWROPT_HIB)) {
if ((dwc->link_state == DWC3_LINK_STATE_U3) &&
(next == DWC3_LINK_STATE_RESUME)) {
- dwc3_trace(trace_dwc3_gadget,
- "ignoring transition U3 -> Resume");
return;
}
}
@@ -2705,11 +2960,7 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc,
break;
case DWC3_DEVICE_EVENT_EOPF:
/* It changed to be suspend event for version 2.30a and above */
- if (dwc->revision < DWC3_REVISION_230A) {
- dwc3_trace(trace_dwc3_gadget, "End of Periodic Frame");
- } else {
- dwc3_trace(trace_dwc3_gadget, "U3/L1-L2 Suspend Event");
-
+ if (dwc->revision >= DWC3_REVISION_230A) {
/*
* Ignore suspend event until the gadget enters into
* USB_STATE_CONFIGURED state.
@@ -2720,16 +2971,9 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc,
}
break;
case DWC3_DEVICE_EVENT_SOF:
- dwc3_trace(trace_dwc3_gadget, "Start of Periodic Frame");
- break;
case DWC3_DEVICE_EVENT_ERRATIC_ERROR:
- dwc3_trace(trace_dwc3_gadget, "Erratic Error");
- break;
case DWC3_DEVICE_EVENT_CMD_CMPL:
- dwc3_trace(trace_dwc3_gadget, "Command Complete");
- break;
case DWC3_DEVICE_EVENT_OVERFLOW:
- dwc3_trace(trace_dwc3_gadget, "Overflow");
break;
default:
dev_WARN(dwc->dev, "UNKNOWN IRQ %d\n", event->type);
@@ -2739,7 +2983,7 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc,
static void dwc3_process_event_entry(struct dwc3 *dwc,
const union dwc3_event *event)
{
- trace_dwc3_event(event->raw);
+ trace_dwc3_event(event->raw, dwc);
/* Endpoint IRQ, handle it and return early */
if (event->type.is_devspec == 0) {
@@ -2772,7 +3016,7 @@ static irqreturn_t dwc3_process_event_buf(struct dwc3_event_buffer *evt)
while (left > 0) {
union dwc3_event event;
- event.raw = *(u32 *) (evt->buf + evt->lpos);
+ event.raw = *(u32 *) (evt->cache + evt->lpos);
dwc3_process_event_entry(dwc, &event);
@@ -2785,10 +3029,8 @@ static irqreturn_t dwc3_process_event_buf(struct dwc3_event_buffer *evt)
* boundary so I worry about that once we try to handle
* that.
*/
- evt->lpos = (evt->lpos + 4) % DWC3_EVENT_BUFFERS_SIZE;
+ evt->lpos = (evt->lpos + 4) % evt->length;
left -= 4;
-
- dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), 4);
}
evt->count = 0;
@@ -2800,6 +3042,11 @@ static irqreturn_t dwc3_process_event_buf(struct dwc3_event_buffer *evt)
reg &= ~DWC3_GEVNTSIZ_INTMASK;
dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0), reg);
+ if (dwc->imod_interval) {
+ dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), DWC3_GEVNTCOUNT_EHB);
+ dwc3_writel(dwc->regs, DWC3_DEV_IMOD(0), dwc->imod_interval);
+ }
+
return ret;
}
@@ -2820,6 +3067,7 @@ static irqreturn_t dwc3_thread_interrupt(int irq, void *_evt)
static irqreturn_t dwc3_check_event_buf(struct dwc3_event_buffer *evt)
{
struct dwc3 *dwc = evt->dwc;
+ u32 amount;
u32 count;
u32 reg;
@@ -2843,6 +3091,14 @@ static irqreturn_t dwc3_check_event_buf(struct dwc3_event_buffer *evt)
reg |= DWC3_GEVNTSIZ_INTMASK;
dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0), reg);
+ amount = min(count, evt->length - evt->lpos);
+ memcpy(evt->cache + evt->lpos, evt->buf + evt->lpos, amount);
+
+ if (amount < count)
+ memcpy(evt->cache, evt->buf, count - amount);
+
+ dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), count);
+
return IRQ_WAKE_THREAD;
}
@@ -2853,6 +3109,39 @@ static irqreturn_t dwc3_interrupt(int irq, void *_evt)
return dwc3_check_event_buf(evt);
}
+static int dwc3_gadget_get_irq(struct dwc3 *dwc)
+{
+ struct platform_device *dwc3_pdev = to_platform_device(dwc->dev);
+ int irq;
+
+ irq = platform_get_irq_byname(dwc3_pdev, "peripheral");
+ if (irq > 0)
+ goto out;
+
+ if (irq == -EPROBE_DEFER)
+ goto out;
+
+ irq = platform_get_irq_byname(dwc3_pdev, "dwc_usb3");
+ if (irq > 0)
+ goto out;
+
+ if (irq == -EPROBE_DEFER)
+ goto out;
+
+ irq = platform_get_irq(dwc3_pdev, 0);
+ if (irq > 0)
+ goto out;
+
+ if (irq != -EPROBE_DEFER)
+ dev_err(dwc->dev, "missing peripheral IRQ\n");
+
+ if (!irq)
+ irq = -EINVAL;
+
+out:
+ return irq;
+}
+
/**
* dwc3_gadget_init - Initializes gadget related registers
* @dwc: pointer to our controller context structure
@@ -2861,35 +3150,18 @@ static irqreturn_t dwc3_interrupt(int irq, void *_evt)
*/
int dwc3_gadget_init(struct dwc3 *dwc)
{
- int ret, irq;
- struct platform_device *dwc3_pdev = to_platform_device(dwc->dev);
+ int ret;
+ int irq;
- irq = platform_get_irq_byname(dwc3_pdev, "peripheral");
- if (irq == -EPROBE_DEFER)
- return irq;
-
- if (irq <= 0) {
- irq = platform_get_irq_byname(dwc3_pdev, "dwc_usb3");
- if (irq == -EPROBE_DEFER)
- return irq;
-
- if (irq <= 0) {
- irq = platform_get_irq(dwc3_pdev, 0);
- if (irq <= 0) {
- if (irq != -EPROBE_DEFER) {
- dev_err(dwc->dev,
- "missing peripheral IRQ\n");
- }
- if (!irq)
- irq = -EINVAL;
- return irq;
- }
- }
+ irq = dwc3_gadget_get_irq(dwc);
+ if (irq < 0) {
+ ret = irq;
+ goto err0;
}
dwc->irq_gadget = irq;
- dwc->ctrl_req = dma_alloc_coherent(dwc->dev, sizeof(*dwc->ctrl_req),
+ dwc->ctrl_req = dma_alloc_coherent(dwc->sysdev, sizeof(*dwc->ctrl_req),
&dwc->ctrl_req_addr, GFP_KERNEL);
if (!dwc->ctrl_req) {
dev_err(dwc->dev, "failed to allocate ctrl request\n");
@@ -2897,8 +3169,9 @@ int dwc3_gadget_init(struct dwc3 *dwc)
goto err0;
}
- dwc->ep0_trb = dma_alloc_coherent(dwc->dev, sizeof(*dwc->ep0_trb) * 2,
- &dwc->ep0_trb_addr, GFP_KERNEL);
+ dwc->ep0_trb = dma_alloc_coherent(dwc->sysdev,
+ sizeof(*dwc->ep0_trb) * 2,
+ &dwc->ep0_trb_addr, GFP_KERNEL);
if (!dwc->ep0_trb) {
dev_err(dwc->dev, "failed to allocate ep0 trb\n");
ret = -ENOMEM;
@@ -2911,7 +3184,7 @@ int dwc3_gadget_init(struct dwc3 *dwc)
goto err2;
}
- dwc->ep0_bounce = dma_alloc_coherent(dwc->dev,
+ dwc->ep0_bounce = dma_alloc_coherent(dwc->sysdev,
DWC3_EP0_BOUNCE_SIZE, &dwc->ep0_bounce_addr,
GFP_KERNEL);
if (!dwc->ep0_bounce) {
@@ -2926,6 +3199,15 @@ int dwc3_gadget_init(struct dwc3 *dwc)
goto err4;
}
+ dwc->bounce = dma_alloc_coherent(dwc->sysdev, DWC3_BOUNCE_SIZE,
+ &dwc->bounce_addr, GFP_KERNEL);
+ if (!dwc->bounce) {
+ ret = -ENOMEM;
+ goto err5;
+ }
+
+ init_completion(&dwc->ep0_in_setup);
+
dwc->gadget.ops = &dwc3_gadget_ops;
dwc->gadget.speed = USB_SPEED_UNKNOWN;
dwc->gadget.sg_supported = true;
@@ -2949,52 +3231,48 @@ int dwc3_gadget_init(struct dwc3 *dwc)
* composite.c that we are USB 2.0 + LPM ECN.
*/
if (dwc->revision < DWC3_REVISION_220A)
- dwc3_trace(trace_dwc3_gadget,
- "Changing max_speed on rev %08x",
+ dev_info(dwc->dev, "changing max_speed on rev %08x\n",
dwc->revision);
dwc->gadget.max_speed = dwc->maximum_speed;
/*
- * Per databook, DWC3 needs buffer size to be aligned to MaxPacketSize
- * on ep out.
- */
- dwc->gadget.quirk_ep_out_aligned_size = true;
-
- /*
* REVISIT: Here we should clear all pending IRQs to be
* sure we're starting from a well known location.
*/
ret = dwc3_gadget_init_endpoints(dwc);
if (ret)
- goto err5;
+ goto err6;
ret = usb_add_gadget_udc(dwc->dev, &dwc->gadget);
if (ret) {
dev_err(dwc->dev, "failed to register udc\n");
- goto err5;
+ goto err6;
}
return 0;
+err6:
+ dma_free_coherent(dwc->sysdev, DWC3_BOUNCE_SIZE, dwc->bounce,
+ dwc->bounce_addr);
err5:
kfree(dwc->zlp_buf);
err4:
dwc3_gadget_free_endpoints(dwc);
- dma_free_coherent(dwc->dev, DWC3_EP0_BOUNCE_SIZE,
+ dma_free_coherent(dwc->sysdev, DWC3_EP0_BOUNCE_SIZE,
dwc->ep0_bounce, dwc->ep0_bounce_addr);
err3:
kfree(dwc->setup_buf);
err2:
- dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb) * 2,
+ dma_free_coherent(dwc->sysdev, sizeof(*dwc->ep0_trb) * 2,
dwc->ep0_trb, dwc->ep0_trb_addr);
err1:
- dma_free_coherent(dwc->dev, sizeof(*dwc->ctrl_req),
+ dma_free_coherent(dwc->sysdev, sizeof(*dwc->ctrl_req),
dwc->ctrl_req, dwc->ctrl_req_addr);
err0:
@@ -3009,30 +3287,27 @@ void dwc3_gadget_exit(struct dwc3 *dwc)
dwc3_gadget_free_endpoints(dwc);
- dma_free_coherent(dwc->dev, DWC3_EP0_BOUNCE_SIZE,
+ dma_free_coherent(dwc->sysdev, DWC3_BOUNCE_SIZE, dwc->bounce,
+ dwc->bounce_addr);
+ dma_free_coherent(dwc->sysdev, DWC3_EP0_BOUNCE_SIZE,
dwc->ep0_bounce, dwc->ep0_bounce_addr);
kfree(dwc->setup_buf);
kfree(dwc->zlp_buf);
- dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb) * 2,
+ dma_free_coherent(dwc->sysdev, sizeof(*dwc->ep0_trb) * 2,
dwc->ep0_trb, dwc->ep0_trb_addr);
- dma_free_coherent(dwc->dev, sizeof(*dwc->ctrl_req),
+ dma_free_coherent(dwc->sysdev, sizeof(*dwc->ctrl_req),
dwc->ctrl_req, dwc->ctrl_req_addr);
}
int dwc3_gadget_suspend(struct dwc3 *dwc)
{
- int ret;
-
if (!dwc->gadget_driver)
return 0;
- ret = dwc3_gadget_run_stop(dwc, false, false);
- if (ret < 0)
- return ret;
-
+ dwc3_gadget_run_stop(dwc, false, false);
dwc3_disconnect_gadget(dwc);
__dwc3_gadget_stop(dwc);
diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h
index e4a1d974a5ae..265e223ab645 100644
--- a/drivers/usb/dwc3/gadget.h
+++ b/drivers/usb/dwc3/gadget.h
@@ -28,23 +28,23 @@ struct dwc3;
#define gadget_to_dwc(g) (container_of(g, struct dwc3, gadget))
/* DEPCFG parameter 1 */
-#define DWC3_DEPCFG_INT_NUM(n) ((n) << 0)
+#define DWC3_DEPCFG_INT_NUM(n) (((n) & 0x1f) << 0)
#define DWC3_DEPCFG_XFER_COMPLETE_EN (1 << 8)
#define DWC3_DEPCFG_XFER_IN_PROGRESS_EN (1 << 9)
#define DWC3_DEPCFG_XFER_NOT_READY_EN (1 << 10)
#define DWC3_DEPCFG_FIFO_ERROR_EN (1 << 11)
#define DWC3_DEPCFG_STREAM_EVENT_EN (1 << 13)
-#define DWC3_DEPCFG_BINTERVAL_M1(n) ((n) << 16)
+#define DWC3_DEPCFG_BINTERVAL_M1(n) (((n) & 0xff) << 16)
#define DWC3_DEPCFG_STREAM_CAPABLE (1 << 24)
-#define DWC3_DEPCFG_EP_NUMBER(n) ((n) << 25)
+#define DWC3_DEPCFG_EP_NUMBER(n) (((n) & 0x1f) << 25)
#define DWC3_DEPCFG_BULK_BASED (1 << 30)
#define DWC3_DEPCFG_FIFO_BASED (1 << 31)
/* DEPCFG parameter 0 */
-#define DWC3_DEPCFG_EP_TYPE(n) ((n) << 1)
-#define DWC3_DEPCFG_MAX_PACKET_SIZE(n) ((n) << 3)
-#define DWC3_DEPCFG_FIFO_NUMBER(n) ((n) << 17)
-#define DWC3_DEPCFG_BURST_SIZE(n) ((n) << 22)
+#define DWC3_DEPCFG_EP_TYPE(n) (((n) & 0x3) << 1)
+#define DWC3_DEPCFG_MAX_PACKET_SIZE(n) (((n) & 0x7ff) << 3)
+#define DWC3_DEPCFG_FIFO_NUMBER(n) (((n) & 0x1f) << 17)
+#define DWC3_DEPCFG_BURST_SIZE(n) (((n) & 0xf) << 22)
#define DWC3_DEPCFG_DATA_SEQ_NUM(n) ((n) << 26)
/* This applies for core versions earlier than 1.94a */
#define DWC3_DEPCFG_IGN_SEQ_NUM (1 << 31)
@@ -62,10 +62,7 @@ struct dwc3;
static inline struct dwc3_request *next_request(struct list_head *list)
{
- if (list_empty(list))
- return NULL;
-
- return list_first_entry(list, struct dwc3_request, list);
+ return list_first_entry_or_null(list, struct dwc3_request, list);
}
static inline void dwc3_gadget_move_started_request(struct dwc3_request *req)
diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c
index f6533c68fed1..76f0b0df37c1 100644
--- a/drivers/usb/dwc3/host.c
+++ b/drivers/usb/dwc3/host.c
@@ -19,47 +19,60 @@
#include "core.h"
+static int dwc3_host_get_irq(struct dwc3 *dwc)
+{
+ struct platform_device *dwc3_pdev = to_platform_device(dwc->dev);
+ int irq;
+
+ irq = platform_get_irq_byname(dwc3_pdev, "host");
+ if (irq > 0)
+ goto out;
+
+ if (irq == -EPROBE_DEFER)
+ goto out;
+
+ irq = platform_get_irq_byname(dwc3_pdev, "dwc_usb3");
+ if (irq > 0)
+ goto out;
+
+ if (irq == -EPROBE_DEFER)
+ goto out;
+
+ irq = platform_get_irq(dwc3_pdev, 0);
+ if (irq > 0)
+ goto out;
+
+ if (irq != -EPROBE_DEFER)
+ dev_err(dwc->dev, "missing host IRQ\n");
+
+ if (!irq)
+ irq = -EINVAL;
+
+out:
+ return irq;
+}
+
int dwc3_host_init(struct dwc3 *dwc)
{
- struct property_entry props[2];
+ struct property_entry props[3];
struct platform_device *xhci;
int ret, irq;
struct resource *res;
struct platform_device *dwc3_pdev = to_platform_device(dwc->dev);
+ int prop_idx = 0;
- irq = platform_get_irq_byname(dwc3_pdev, "host");
- if (irq == -EPROBE_DEFER)
+ irq = dwc3_host_get_irq(dwc);
+ if (irq < 0)
return irq;
- if (irq <= 0) {
- irq = platform_get_irq_byname(dwc3_pdev, "dwc_usb3");
- if (irq == -EPROBE_DEFER)
- return irq;
-
- if (irq <= 0) {
- irq = platform_get_irq(dwc3_pdev, 0);
- if (irq <= 0) {
- if (irq != -EPROBE_DEFER) {
- dev_err(dwc->dev,
- "missing host IRQ\n");
- }
- if (!irq)
- irq = -EINVAL;
- return irq;
- } else {
- res = platform_get_resource(dwc3_pdev,
- IORESOURCE_IRQ, 0);
- }
- } else {
- res = platform_get_resource_byname(dwc3_pdev,
- IORESOURCE_IRQ,
- "dwc_usb3");
- }
-
- } else {
+ res = platform_get_resource_byname(dwc3_pdev, IORESOURCE_IRQ, "host");
+ if (!res)
res = platform_get_resource_byname(dwc3_pdev, IORESOURCE_IRQ,
- "host");
- }
+ "dwc_usb3");
+ if (!res)
+ res = platform_get_resource(dwc3_pdev, IORESOURCE_IRQ, 0);
+ if (!res)
+ return -ENOMEM;
dwc->xhci_resources[1].start = irq;
dwc->xhci_resources[1].end = irq;
@@ -72,11 +85,7 @@ int dwc3_host_init(struct dwc3 *dwc)
return -ENOMEM;
}
- dma_set_coherent_mask(&xhci->dev, dwc->dev->coherent_dma_mask);
-
xhci->dev.parent = dwc->dev;
- xhci->dev.dma_mask = dwc->dev->dma_mask;
- xhci->dev.dma_parms = dwc->dev->dma_parms;
dwc->xhci = xhci;
@@ -89,8 +98,22 @@ int dwc3_host_init(struct dwc3 *dwc)
memset(props, 0, sizeof(struct property_entry) * ARRAY_SIZE(props));
- if (dwc->usb3_lpm_capable) {
- props[0].name = "usb3-lpm-capable";
+ if (dwc->usb3_lpm_capable)
+ props[prop_idx++].name = "usb3-lpm-capable";
+
+ /**
+ * WORKAROUND: dwc3 revisions <=3.00a have a limitation
+ * where Port Disable command doesn't work.
+ *
+ * The suggested workaround is that we avoid Port Disable
+ * completely.
+ *
+ * This following flag tells XHCI to do just that.
+ */
+ if (dwc->revision <= DWC3_REVISION_300A)
+ props[prop_idx++].name = "quirk-broken-port-ped";
+
+ if (prop_idx) {
ret = platform_device_add_properties(xhci, props);
if (ret) {
dev_err(dwc->dev, "failed to add properties to xHCI\n");
@@ -99,9 +122,9 @@ int dwc3_host_init(struct dwc3 *dwc)
}
phy_create_lookup(dwc->usb2_generic_phy, "usb2-phy",
- dev_name(&xhci->dev));
+ dev_name(dwc->dev));
phy_create_lookup(dwc->usb3_generic_phy, "usb3-phy",
- dev_name(&xhci->dev));
+ dev_name(dwc->dev));
ret = platform_device_add(xhci);
if (ret) {
@@ -112,9 +135,9 @@ int dwc3_host_init(struct dwc3 *dwc)
return 0;
err2:
phy_remove_lookup(dwc->usb2_generic_phy, "usb2-phy",
- dev_name(&xhci->dev));
+ dev_name(dwc->dev));
phy_remove_lookup(dwc->usb3_generic_phy, "usb3-phy",
- dev_name(&xhci->dev));
+ dev_name(dwc->dev));
err1:
platform_device_put(xhci);
return ret;
@@ -123,8 +146,8 @@ err1:
void dwc3_host_exit(struct dwc3 *dwc)
{
phy_remove_lookup(dwc->usb2_generic_phy, "usb2-phy",
- dev_name(&dwc->xhci->dev));
+ dev_name(dwc->dev));
phy_remove_lookup(dwc->usb3_generic_phy, "usb3-phy",
- dev_name(&dwc->xhci->dev));
+ dev_name(dwc->dev));
platform_device_unregister(dwc->xhci);
}
diff --git a/drivers/usb/dwc3/io.h b/drivers/usb/dwc3/io.h
index a06f9a8fecc7..c69b06696824 100644
--- a/drivers/usb/dwc3/io.h
+++ b/drivers/usb/dwc3/io.h
@@ -40,8 +40,7 @@ static inline u32 dwc3_readl(void __iomem *base, u32 offset)
* documentation, so we revert it back to the proper addresses, the
* same way they are described on SNPS documentation
*/
- dwc3_trace(trace_dwc3_readl, "addr %p value %08x",
- base - DWC3_GLOBALS_REGS_START + offset, value);
+ trace_dwc3_readl(base - DWC3_GLOBALS_REGS_START, offset, value);
return value;
}
@@ -60,8 +59,7 @@ static inline void dwc3_writel(void __iomem *base, u32 offset, u32 value)
* documentation, so we revert it back to the proper addresses, the
* same way they are described on SNPS documentation
*/
- dwc3_trace(trace_dwc3_writel, "addr %p value %08x",
- base - DWC3_GLOBALS_REGS_START + offset, value);
+ trace_dwc3_writel(base - DWC3_GLOBALS_REGS_START, offset, value);
}
#endif /* __DRIVERS_USB_DWC3_IO_H */
diff --git a/drivers/usb/dwc3/trace.h b/drivers/usb/dwc3/trace.h
index d24cefd191b5..2b124f94d858 100644
--- a/drivers/usb/dwc3/trace.h
+++ b/drivers/usb/dwc3/trace.h
@@ -37,47 +37,66 @@ DECLARE_EVENT_CLASS(dwc3_log_msg,
TP_printk("%s", __get_str(msg))
);
-DEFINE_EVENT(dwc3_log_msg, dwc3_readl,
+DEFINE_EVENT(dwc3_log_msg, dwc3_gadget,
TP_PROTO(struct va_format *vaf),
TP_ARGS(vaf)
);
-DEFINE_EVENT(dwc3_log_msg, dwc3_writel,
+DEFINE_EVENT(dwc3_log_msg, dwc3_core,
TP_PROTO(struct va_format *vaf),
TP_ARGS(vaf)
);
-DEFINE_EVENT(dwc3_log_msg, dwc3_gadget,
+DEFINE_EVENT(dwc3_log_msg, dwc3_ep0,
TP_PROTO(struct va_format *vaf),
TP_ARGS(vaf)
);
-DEFINE_EVENT(dwc3_log_msg, dwc3_core,
- TP_PROTO(struct va_format *vaf),
- TP_ARGS(vaf)
+DECLARE_EVENT_CLASS(dwc3_log_io,
+ TP_PROTO(void *base, u32 offset, u32 value),
+ TP_ARGS(base, offset, value),
+ TP_STRUCT__entry(
+ __field(void *, base)
+ __field(u32, offset)
+ __field(u32, value)
+ ),
+ TP_fast_assign(
+ __entry->base = base;
+ __entry->offset = offset;
+ __entry->value = value;
+ ),
+ TP_printk("addr %p value %08x", __entry->base + __entry->offset,
+ __entry->value)
);
-DEFINE_EVENT(dwc3_log_msg, dwc3_ep0,
- TP_PROTO(struct va_format *vaf),
- TP_ARGS(vaf)
+DEFINE_EVENT(dwc3_log_io, dwc3_readl,
+ TP_PROTO(void *base, u32 offset, u32 value),
+ TP_ARGS(base, offset, value)
+);
+
+DEFINE_EVENT(dwc3_log_io, dwc3_writel,
+ TP_PROTO(void *base, u32 offset, u32 value),
+ TP_ARGS(base, offset, value)
);
DECLARE_EVENT_CLASS(dwc3_log_event,
- TP_PROTO(u32 event),
- TP_ARGS(event),
+ TP_PROTO(u32 event, struct dwc3 *dwc),
+ TP_ARGS(event, dwc),
TP_STRUCT__entry(
__field(u32, event)
+ __field(u32, ep0state)
),
TP_fast_assign(
__entry->event = event;
+ __entry->ep0state = dwc->ep0state;
),
TP_printk("event (%08x): %s", __entry->event,
- dwc3_decode_event(__entry->event))
+ dwc3_decode_event(__entry->event, __entry->ep0state))
);
DEFINE_EVENT(dwc3_log_event, dwc3_event,
- TP_PROTO(u32 event),
- TP_ARGS(event)
+ TP_PROTO(u32 event, struct dwc3 *dwc),
+ TP_ARGS(event, dwc)
);
DECLARE_EVENT_CLASS(dwc3_log_ctrl,
@@ -237,6 +256,7 @@ DECLARE_EVENT_CLASS(dwc3_log_trb,
__field(u32, bph)
__field(u32, size)
__field(u32, ctrl)
+ __field(u32, type)
),
TP_fast_assign(
snprintf(__get_str(name), DWC3_MSG_MAX, "%s", dep->name);
@@ -247,11 +267,31 @@ DECLARE_EVENT_CLASS(dwc3_log_trb,
__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 %d ctrl %08x (%c%c%c%c:%c%c:%s)",
+ 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,
- __entry->size, __entry->ctrl,
+ ({char *s;
+ int pcm = ((__entry->size >> 24) & 3) + 1;
+ switch (__entry->type) {
+ case USB_ENDPOINT_XFER_INT:
+ case USB_ENDPOINT_XFER_ISOC:
+ switch (pcm) {
+ case 1:
+ s = "1x ";
+ break;
+ case 2:
+ s = "2x ";
+ break;
+ case 3:
+ s = "3x ";
+ break;
+ }
+ default:
+ s = "";
+ } s; }),
+ DWC3_TRB_SIZE_LENGTH(__entry->size), __entry->ctrl,
__entry->ctrl & DWC3_TRB_CTRL_HWO ? 'H' : 'h',
__entry->ctrl & DWC3_TRB_CTRL_LST ? 'L' : 'l',
__entry->ctrl & DWC3_TRB_CTRL_CHN ? 'C' : 'c',
@@ -301,6 +341,57 @@ DEFINE_EVENT(dwc3_log_trb, dwc3_complete_trb,
TP_ARGS(dep, trb)
);
+DECLARE_EVENT_CLASS(dwc3_log_ep,
+ TP_PROTO(struct dwc3_ep *dep),
+ TP_ARGS(dep),
+ TP_STRUCT__entry(
+ __dynamic_array(char, name, DWC3_MSG_MAX)
+ __field(unsigned, maxpacket)
+ __field(unsigned, maxpacket_limit)
+ __field(unsigned, max_streams)
+ __field(unsigned, maxburst)
+ __field(unsigned, flags)
+ __field(unsigned, direction)
+ __field(u8, trb_enqueue)
+ __field(u8, trb_dequeue)
+ ),
+ TP_fast_assign(
+ snprintf(__get_str(name), DWC3_MSG_MAX, "%s", dep->name);
+ __entry->maxpacket = dep->endpoint.maxpacket;
+ __entry->maxpacket_limit = dep->endpoint.maxpacket_limit;
+ __entry->max_streams = dep->endpoint.max_streams;
+ __entry->maxburst = dep->endpoint.maxburst;
+ __entry->flags = dep->flags;
+ __entry->direction = dep->direction;
+ __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",
+ __get_str(name), __entry->maxpacket,
+ __entry->maxpacket_limit, __entry->max_streams,
+ __entry->maxburst, __entry->trb_enqueue,
+ __entry->trb_dequeue,
+ __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_PENDING_REQUEST ? 'P' : 'p',
+ __entry->flags & DWC3_EP_MISSED_ISOC ? 'M' : 'm',
+ __entry->flags & DWC3_EP_END_TRANSFER_PENDING ? 'E' : 'e',
+ __entry->direction ? '<' : '>'
+ )
+);
+
+DEFINE_EVENT(dwc3_log_ep, dwc3_gadget_ep_enable,
+ TP_PROTO(struct dwc3_ep *dep),
+ TP_ARGS(dep)
+);
+
+DEFINE_EVENT(dwc3_log_ep, dwc3_gadget_ep_disable,
+ TP_PROTO(struct dwc3_ep *dep),
+ TP_ARGS(dep)
+);
+
#endif /* __DWC3_TRACE_H */
/* this part has to be here */
diff --git a/drivers/usb/early/ehci-dbgp.c b/drivers/usb/early/ehci-dbgp.c
index ea73afb026d8..e2654443e8eb 100644
--- a/drivers/usb/early/ehci-dbgp.c
+++ b/drivers/usb/early/ehci-dbgp.c
@@ -580,7 +580,6 @@ try_again:
USB_DEBUG_DEVNUM);
goto err;
}
- devnum = USB_DEBUG_DEVNUM;
dbgp_printk("debug device renamed to 127\n");
}
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 32176f779861..49d685ad0da9 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -201,7 +201,12 @@ ep_found:
_ep->desc = chosen_desc;
_ep->comp_desc = NULL;
_ep->maxburst = 0;
- _ep->mult = 0;
+ _ep->mult = 1;
+
+ if (g->speed == USB_SPEED_HIGH && (usb_endpoint_xfer_isoc(_ep->desc) ||
+ usb_endpoint_xfer_int(_ep->desc)))
+ _ep->mult = usb_endpoint_maxp_mult(_ep->desc);
+
if (!want_comp_desc)
return 0;
@@ -218,7 +223,7 @@ ep_found:
switch (usb_endpoint_type(_ep->desc)) {
case USB_ENDPOINT_XFER_ISOC:
/* mult: bits 1:0 of bmAttributes */
- _ep->mult = comp_desc->bmAttributes & 0x3;
+ _ep->mult = (comp_desc->bmAttributes & 0x3) + 1;
case USB_ENDPOINT_XFER_BULK:
case USB_ENDPOINT_XFER_INT:
_ep->maxburst = comp_desc->bMaxBurst + 1;
@@ -1689,9 +1694,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
value = min(w_length, (u16) 1);
break;
- /* function drivers must handle get/set altsetting; if there's
- * no get() method, we know only altsetting zero works.
- */
+ /* function drivers must handle get/set altsetting */
case USB_REQ_SET_INTERFACE:
if (ctrl->bRequestType != USB_RECIP_INTERFACE)
goto unknown;
@@ -1700,7 +1703,13 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
f = cdev->config->interface[intf];
if (!f)
break;
- if (w_value && !f->set_alt)
+
+ /*
+ * If there's no get_alt() method, we know only altsetting zero
+ * works. There is no need to check if set_alt() is not NULL
+ * as we check this in usb_add_function().
+ */
+ if (w_value && !f->get_alt)
break;
value = f->set_alt(f, w_index, w_value);
if (value == USB_GADGET_DELAYED_STATUS) {
@@ -2138,7 +2147,7 @@ int composite_os_desc_req_prepare(struct usb_composite_dev *cdev,
cdev->os_desc_req->buf = kmalloc(4096, GFP_KERNEL);
if (!cdev->os_desc_req->buf) {
ret = -ENOMEM;
- kfree(cdev->os_desc_req);
+ usb_ep_free_request(ep0, cdev->os_desc_req);
goto end;
}
cdev->os_desc_req->context = cdev;
@@ -2382,18 +2391,8 @@ EXPORT_SYMBOL_GPL(usb_composite_setup_continue);
static char *composite_default_mfr(struct usb_gadget *gadget)
{
- char *mfr;
- int len;
-
- len = snprintf(NULL, 0, "%s %s with %s", init_utsname()->sysname,
- init_utsname()->release, gadget->name);
- len++;
- mfr = kmalloc(len, GFP_KERNEL);
- if (!mfr)
- return NULL;
- snprintf(mfr, len, "%s %s with %s", init_utsname()->sysname,
- init_utsname()->release, gadget->name);
- return mfr;
+ return kasprintf(GFP_KERNEL, "%s %s with %s", init_utsname()->sysname,
+ init_utsname()->release, gadget->name);
}
void usb_composite_overwrite_options(struct usb_composite_dev *cdev,
diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c
index 3984787f8e97..cbff3b02840d 100644
--- a/drivers/usb/gadget/configfs.c
+++ b/drivers/usb/gadget/configfs.c
@@ -269,6 +269,7 @@ static ssize_t gadget_dev_desc_UDC_store(struct config_item *item,
ret = unregister_gadget(gi);
if (ret)
goto err;
+ kfree(name);
} else {
if (gi->composite.gadget_driver.udc_name) {
ret = -EBUSY;
@@ -408,7 +409,7 @@ out:
return ret;
}
-static int config_usb_cfg_unlink(
+static void config_usb_cfg_unlink(
struct config_item *usb_cfg_ci,
struct config_item *usb_func_ci)
{
@@ -437,12 +438,11 @@ static int config_usb_cfg_unlink(
list_del(&f->list);
usb_put_function(f);
mutex_unlock(&gi->lock);
- return 0;
+ return;
}
}
mutex_unlock(&gi->lock);
WARN(1, "Unable to locate function to unbind\n");
- return 0;
}
static struct configfs_item_operations gadget_config_item_ops = {
@@ -865,7 +865,7 @@ out:
return ret;
}
-static int os_desc_unlink(struct config_item *os_desc_ci,
+static void os_desc_unlink(struct config_item *os_desc_ci,
struct config_item *usb_cfg_ci)
{
struct gadget_info *gi = container_of(to_config_group(os_desc_ci),
@@ -878,7 +878,6 @@ static int os_desc_unlink(struct config_item *os_desc_ci,
cdev->os_desc_config = NULL;
WARN_ON(gi->composite.gadget_driver.udc_name);
mutex_unlock(&gi->lock);
- return 0;
}
static struct configfs_item_operations os_desc_ops = {
diff --git a/drivers/usb/gadget/function/f_acm.c b/drivers/usb/gadget/function/f_acm.c
index a30766ca4226..5e3828d9dac7 100644
--- a/drivers/usb/gadget/function/f_acm.c
+++ b/drivers/usb/gadget/function/f_acm.c
@@ -535,13 +535,15 @@ static int acm_notify_serial_state(struct f_acm *acm)
{
struct usb_composite_dev *cdev = acm->port.func.config->cdev;
int status;
+ __le16 serial_state;
spin_lock(&acm->lock);
if (acm->notify_req) {
dev_dbg(&cdev->gadget->dev, "acm ttyGS%d serial state %04x\n",
acm->port_num, acm->serial_state);
+ serial_state = cpu_to_le16(acm->serial_state);
status = acm_cdc_notify(acm, USB_CDC_NOTIFY_SERIAL_STATE,
- 0, &acm->serial_state, sizeof(acm->serial_state));
+ 0, &serial_state, sizeof(acm->serial_state));
} else {
acm->pending = true;
status = 0;
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index 17989b72cdae..a0085571824d 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -23,6 +23,7 @@
#include <linux/export.h>
#include <linux/hid.h>
#include <linux/module.h>
+#include <linux/sched/signal.h>
#include <linux/uio.h>
#include <asm/unaligned.h>
@@ -266,7 +267,7 @@ static void ffs_ep0_complete(struct usb_ep *ep, struct usb_request *req)
{
struct ffs_data *ffs = req->context;
- complete_all(&ffs->ep0req_completion);
+ complete(&ffs->ep0req_completion);
}
static int __ffs_ep0_queue_wait(struct ffs_data *ffs, char *data, size_t len)
@@ -949,7 +950,7 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
goto error_mutex;
}
if (!io_data->read &&
- copy_from_iter(data, data_len, &io_data->data) != data_len) {
+ !copy_from_iter_full(data, data_len, &io_data->data)) {
ret = -EFAULT;
goto error_mutex;
}
@@ -1230,7 +1231,7 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code,
desc = epfile->ep->descs[desc_idx];
spin_unlock_irq(&epfile->ffs->eps_lock);
- ret = copy_to_user((void *)value, desc, sizeof(*desc));
+ ret = copy_to_user((void *)value, desc, desc->bLength);
if (ret)
ret = -EFAULT;
return ret;
@@ -1806,7 +1807,7 @@ static void ffs_func_eps_disable(struct ffs_function *func)
unsigned long flags;
spin_lock_irqsave(&func->ffs->eps_lock, flags);
- do {
+ while (count--) {
/* pending requests get nuked */
if (likely(ep->ep))
usb_ep_disable(ep->ep);
@@ -1817,7 +1818,7 @@ static void ffs_func_eps_disable(struct ffs_function *func)
__ffs_epfile_read_buffer_free(epfile);
++epfile;
}
- } while (--count);
+ }
spin_unlock_irqrestore(&func->ffs->eps_lock, flags);
}
@@ -1831,13 +1832,16 @@ static int ffs_func_eps_enable(struct ffs_function *func)
int ret = 0;
spin_lock_irqsave(&func->ffs->eps_lock, flags);
- do {
+ while(count--) {
struct usb_endpoint_descriptor *ds;
+ struct usb_ss_ep_comp_descriptor *comp_desc = NULL;
+ int needs_comp_desc = false;
int desc_idx;
- if (ffs->gadget->speed == USB_SPEED_SUPER)
+ if (ffs->gadget->speed == USB_SPEED_SUPER) {
desc_idx = 2;
- else if (ffs->gadget->speed == USB_SPEED_HIGH)
+ needs_comp_desc = true;
+ } else if (ffs->gadget->speed == USB_SPEED_HIGH)
desc_idx = 1;
else
desc_idx = 0;
@@ -1854,6 +1858,14 @@ static int ffs_func_eps_enable(struct ffs_function *func)
ep->ep->driver_data = ep;
ep->ep->desc = ds;
+
+ comp_desc = (struct usb_ss_ep_comp_descriptor *)(ds +
+ USB_DT_ENDPOINT_SIZE);
+ ep->ep->maxburst = comp_desc->bMaxBurst + 1;
+
+ if (needs_comp_desc)
+ ep->ep->comp_desc = comp_desc;
+
ret = usb_ep_enable(ep->ep);
if (likely(!ret)) {
epfile->ep = ep;
@@ -1867,7 +1879,7 @@ static int ffs_func_eps_enable(struct ffs_function *func)
++ep;
++epfile;
- } while (--count);
+ }
spin_unlock_irqrestore(&func->ffs->eps_lock, flags);
return ret;
@@ -2091,8 +2103,8 @@ static int __ffs_data_do_entity(enum ffs_entity_type type,
case FFS_STRING:
/*
- * Strings are indexed from 1 (0 is magic ;) reserved
- * for languages list or some such)
+ * Strings are indexed from 1 (0 is reserved
+ * for languages list)
*/
if (*valuep > helper->ffs->strings_count)
helper->ffs->strings_count = *valuep;
@@ -2101,7 +2113,7 @@ static int __ffs_data_do_entity(enum ffs_entity_type type,
case FFS_ENDPOINT:
d = (void *)desc;
helper->eps_count++;
- if (helper->eps_count >= 15)
+ if (helper->eps_count >= FFS_MAX_EPS_COUNT)
return -EINVAL;
/* Check if descriptors for any speed were already parsed */
if (!helper->ffs->eps_count && !helper->ffs->interfaces_count)
@@ -2269,6 +2281,8 @@ static int __ffs_data_do_os_desc(enum ffs_os_desc_type type,
if (len < sizeof(*d) || h->interface >= ffs->interfaces_count)
return -EINVAL;
length = le32_to_cpu(d->dwSize);
+ if (len < length)
+ return -EINVAL;
type = le32_to_cpu(d->dwPropertyDataType);
if (type < USB_EXT_PROP_UNICODE ||
type > USB_EXT_PROP_UNICODE_MULTI) {
@@ -2277,6 +2291,11 @@ static int __ffs_data_do_os_desc(enum ffs_os_desc_type type,
return -EINVAL;
}
pnl = le16_to_cpu(d->wPropertyNameLength);
+ if (length < 14 + pnl) {
+ pr_vdebug("invalid os descriptor length: %d pnl:%d (descriptor %d)\n",
+ length, pnl, type);
+ return -EINVAL;
+ }
pdl = le32_to_cpu(*(u32 *)((u8 *)data + 10 + pnl));
if (length != 14 + pnl + pdl) {
pr_vdebug("invalid os descriptor length: %d pnl:%d pdl:%d (descriptor %d)\n",
@@ -2363,6 +2382,9 @@ static int __ffs_data_got_descs(struct ffs_data *ffs,
}
}
if (flags & (1 << i)) {
+ if (len < 4) {
+ goto error;
+ }
os_descs_count = get_unaligned_le32(data);
data += 4;
len -= 4;
@@ -2435,7 +2457,8 @@ static int __ffs_data_got_strings(struct ffs_data *ffs,
ENTER();
- if (unlikely(get_unaligned_le32(data) != FUNCTIONFS_STRINGS_MAGIC ||
+ if (unlikely(len < 16 ||
+ get_unaligned_le32(data) != FUNCTIONFS_STRINGS_MAGIC ||
get_unaligned_le32(data + 4) != len))
goto error;
str_count = get_unaligned_le32(data + 8);
@@ -3448,12 +3471,12 @@ static void ffs_func_unbind(struct usb_configuration *c,
/* cleanup after autoconfig */
spin_lock_irqsave(&func->ffs->eps_lock, flags);
- do {
+ while (count--) {
if (ep->ep && ep->req)
usb_ep_free_request(ep->ep, ep->req);
ep->req = NULL;
++ep;
- } while (--count);
+ }
spin_unlock_irqrestore(&func->ffs->eps_lock, flags);
kfree(func->eps);
func->eps = NULL;
@@ -3666,6 +3689,7 @@ static void ffs_closed(struct ffs_data *ffs)
{
struct ffs_dev *ffs_obj;
struct f_fs_opts *opts;
+ struct config_item *ci;
ENTER();
ffs_dev_lock();
@@ -3686,11 +3710,14 @@ static void ffs_closed(struct ffs_data *ffs)
goto done;
if (opts->no_configfs || !opts->func_inst.group.cg_item.ci_parent
- || !atomic_read(&opts->func_inst.group.cg_item.ci_kref.refcount))
+ || !kref_read(&opts->func_inst.group.cg_item.ci_kref))
goto done;
- unregister_gadget_item(ffs_obj->opts->
- func_inst.group.cg_item.ci_parent->ci_parent);
+ ci = opts->func_inst.group.cg_item.ci_parent->ci_parent;
+ ffs_dev_unlock();
+
+ unregister_gadget_item(ci);
+ return;
done:
ffs_dev_unlock();
}
diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c
index e2966f87c860..5eea44823ca0 100644
--- a/drivers/usb/gadget/function/f_hid.c
+++ b/drivers/usb/gadget/function/f_hid.c
@@ -50,12 +50,12 @@ struct f_hidg {
/* recv report */
struct list_head completed_out_req;
- spinlock_t spinlock;
+ spinlock_t read_spinlock;
wait_queue_head_t read_queue;
unsigned int qlen;
/* send report */
- struct mutex lock;
+ spinlock_t write_spinlock;
bool write_pending;
wait_queue_head_t write_queue;
struct usb_request *req;
@@ -98,6 +98,60 @@ static struct hid_descriptor hidg_desc = {
/*.desc[0].wDescriptorLenght = DYNAMIC */
};
+/* Super-Speed Support */
+
+static struct usb_endpoint_descriptor hidg_ss_in_ep_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ /*.wMaxPacketSize = DYNAMIC */
+ .bInterval = 4, /* FIXME: Add this field in the
+ * HID gadget configuration?
+ * (struct hidg_func_descriptor)
+ */
+};
+
+static struct usb_ss_ep_comp_descriptor hidg_ss_in_comp_desc = {
+ .bLength = sizeof(hidg_ss_in_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+
+ /* .bMaxBurst = 0, */
+ /* .bmAttributes = 0, */
+ /* .wBytesPerInterval = DYNAMIC */
+};
+
+static struct usb_endpoint_descriptor hidg_ss_out_ep_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ /*.wMaxPacketSize = DYNAMIC */
+ .bInterval = 4, /* FIXME: Add this field in the
+ * HID gadget configuration?
+ * (struct hidg_func_descriptor)
+ */
+};
+
+static struct usb_ss_ep_comp_descriptor hidg_ss_out_comp_desc = {
+ .bLength = sizeof(hidg_ss_out_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+
+ /* .bMaxBurst = 0, */
+ /* .bmAttributes = 0, */
+ /* .wBytesPerInterval = DYNAMIC */
+};
+
+static struct usb_descriptor_header *hidg_ss_descriptors[] = {
+ (struct usb_descriptor_header *)&hidg_interface_desc,
+ (struct usb_descriptor_header *)&hidg_desc,
+ (struct usb_descriptor_header *)&hidg_ss_in_ep_desc,
+ (struct usb_descriptor_header *)&hidg_ss_in_comp_desc,
+ (struct usb_descriptor_header *)&hidg_ss_out_ep_desc,
+ (struct usb_descriptor_header *)&hidg_ss_out_comp_desc,
+ NULL,
+};
+
/* High-Speed Support */
static struct usb_endpoint_descriptor hidg_hs_in_ep_desc = {
@@ -204,28 +258,35 @@ static ssize_t f_hidg_read(struct file *file, char __user *buffer,
if (!access_ok(VERIFY_WRITE, buffer, count))
return -EFAULT;
- spin_lock_irqsave(&hidg->spinlock, flags);
+ spin_lock_irqsave(&hidg->read_spinlock, flags);
#define READ_COND (!list_empty(&hidg->completed_out_req))
/* wait for at least one buffer to complete */
while (!READ_COND) {
- spin_unlock_irqrestore(&hidg->spinlock, flags);
+ spin_unlock_irqrestore(&hidg->read_spinlock, flags);
if (file->f_flags & O_NONBLOCK)
return -EAGAIN;
if (wait_event_interruptible(hidg->read_queue, READ_COND))
return -ERESTARTSYS;
- spin_lock_irqsave(&hidg->spinlock, flags);
+ spin_lock_irqsave(&hidg->read_spinlock, flags);
}
/* pick the first one */
list = list_first_entry(&hidg->completed_out_req,
struct f_hidg_req_list, list);
+
+ /*
+ * Remove this from list to protect it from beign free()
+ * while host disables our function
+ */
+ list_del(&list->list);
+
req = list->req;
count = min_t(unsigned int, count, req->actual - list->pos);
- spin_unlock_irqrestore(&hidg->spinlock, flags);
+ spin_unlock_irqrestore(&hidg->read_spinlock, flags);
/* copy to user outside spinlock */
count -= copy_to_user(buffer, req->buf + list->pos, count);
@@ -238,15 +299,20 @@ static ssize_t f_hidg_read(struct file *file, char __user *buffer,
* call, taking into account its current read position.
*/
if (list->pos == req->actual) {
- spin_lock_irqsave(&hidg->spinlock, flags);
- list_del(&list->list);
kfree(list);
- spin_unlock_irqrestore(&hidg->spinlock, flags);
req->length = hidg->report_length;
ret = usb_ep_queue(hidg->out_ep, req, GFP_KERNEL);
- if (ret < 0)
+ if (ret < 0) {
+ free_ep_req(hidg->out_ep, req);
return ret;
+ }
+ } else {
+ spin_lock_irqsave(&hidg->read_spinlock, flags);
+ list_add(&list->list, &hidg->completed_out_req);
+ spin_unlock_irqrestore(&hidg->read_spinlock, flags);
+
+ wake_up(&hidg->read_queue);
}
return count;
@@ -255,13 +321,16 @@ static ssize_t f_hidg_read(struct file *file, char __user *buffer,
static void f_hidg_req_complete(struct usb_ep *ep, struct usb_request *req)
{
struct f_hidg *hidg = (struct f_hidg *)ep->driver_data;
+ unsigned long flags;
if (req->status != 0) {
ERROR(hidg->func.config->cdev,
"End Point Request ERROR: %d\n", req->status);
}
+ spin_lock_irqsave(&hidg->write_spinlock, flags);
hidg->write_pending = 0;
+ spin_unlock_irqrestore(&hidg->write_spinlock, flags);
wake_up(&hidg->write_queue);
}
@@ -269,18 +338,20 @@ static ssize_t f_hidg_write(struct file *file, const char __user *buffer,
size_t count, loff_t *offp)
{
struct f_hidg *hidg = file->private_data;
+ struct usb_request *req;
+ unsigned long flags;
ssize_t status = -ENOMEM;
if (!access_ok(VERIFY_READ, buffer, count))
return -EFAULT;
- mutex_lock(&hidg->lock);
+ spin_lock_irqsave(&hidg->write_spinlock, flags);
#define WRITE_COND (!hidg->write_pending)
-
+try_again:
/* write queue */
while (!WRITE_COND) {
- mutex_unlock(&hidg->lock);
+ spin_unlock_irqrestore(&hidg->write_spinlock, flags);
if (file->f_flags & O_NONBLOCK)
return -EAGAIN;
@@ -288,37 +359,59 @@ static ssize_t f_hidg_write(struct file *file, const char __user *buffer,
hidg->write_queue, WRITE_COND))
return -ERESTARTSYS;
- mutex_lock(&hidg->lock);
+ spin_lock_irqsave(&hidg->write_spinlock, flags);
}
+ hidg->write_pending = 1;
+ req = hidg->req;
count = min_t(unsigned, count, hidg->report_length);
- status = copy_from_user(hidg->req->buf, buffer, count);
+
+ spin_unlock_irqrestore(&hidg->write_spinlock, flags);
+ status = copy_from_user(req->buf, buffer, count);
if (status != 0) {
ERROR(hidg->func.config->cdev,
"copy_from_user error\n");
- mutex_unlock(&hidg->lock);
- return -EINVAL;
+ status = -EINVAL;
+ goto release_write_pending;
}
- hidg->req->status = 0;
- hidg->req->zero = 0;
- hidg->req->length = count;
- hidg->req->complete = f_hidg_req_complete;
- hidg->req->context = hidg;
- hidg->write_pending = 1;
+ spin_lock_irqsave(&hidg->write_spinlock, flags);
+
+ /* when our function has been disabled by host */
+ if (!hidg->req) {
+ free_ep_req(hidg->in_ep, req);
+ /*
+ * TODO
+ * Should we fail with error here?
+ */
+ goto try_again;
+ }
- status = usb_ep_queue(hidg->in_ep, hidg->req, GFP_ATOMIC);
+ req->status = 0;
+ req->zero = 0;
+ req->length = count;
+ req->complete = f_hidg_req_complete;
+ req->context = hidg;
+
+ status = usb_ep_queue(hidg->in_ep, req, GFP_ATOMIC);
if (status < 0) {
ERROR(hidg->func.config->cdev,
"usb_ep_queue error on int endpoint %zd\n", status);
- hidg->write_pending = 0;
- wake_up(&hidg->write_queue);
+ goto release_write_pending_unlocked;
} else {
status = count;
}
+ spin_unlock_irqrestore(&hidg->write_spinlock, flags);
+
+ return status;
+release_write_pending:
+ spin_lock_irqsave(&hidg->write_spinlock, flags);
+release_write_pending_unlocked:
+ hidg->write_pending = 0;
+ spin_unlock_irqrestore(&hidg->write_spinlock, flags);
- mutex_unlock(&hidg->lock);
+ wake_up(&hidg->write_queue);
return status;
}
@@ -371,20 +464,36 @@ static inline struct usb_request *hidg_alloc_ep_req(struct usb_ep *ep,
static void hidg_set_report_complete(struct usb_ep *ep, struct usb_request *req)
{
struct f_hidg *hidg = (struct f_hidg *) req->context;
+ struct usb_composite_dev *cdev = hidg->func.config->cdev;
struct f_hidg_req_list *req_list;
unsigned long flags;
- req_list = kzalloc(sizeof(*req_list), GFP_ATOMIC);
- if (!req_list)
- return;
+ switch (req->status) {
+ case 0:
+ req_list = kzalloc(sizeof(*req_list), GFP_ATOMIC);
+ if (!req_list) {
+ ERROR(cdev, "Unable to allocate mem for req_list\n");
+ goto free_req;
+ }
- req_list->req = req;
+ req_list->req = req;
- spin_lock_irqsave(&hidg->spinlock, flags);
- list_add_tail(&req_list->list, &hidg->completed_out_req);
- spin_unlock_irqrestore(&hidg->spinlock, flags);
+ spin_lock_irqsave(&hidg->read_spinlock, flags);
+ list_add_tail(&req_list->list, &hidg->completed_out_req);
+ spin_unlock_irqrestore(&hidg->read_spinlock, flags);
- wake_up(&hidg->read_queue);
+ wake_up(&hidg->read_queue);
+ break;
+ default:
+ ERROR(cdev, "Set report failed %d\n", req->status);
+ /* FALLTHROUGH */
+ case -ECONNABORTED: /* hardware forced ep reset */
+ case -ECONNRESET: /* request dequeued */
+ case -ESHUTDOWN: /* disconnect from host */
+free_req:
+ free_ep_req(ep, req);
+ return;
+ }
}
static int hidg_setup(struct usb_function *f,
@@ -490,20 +599,35 @@ static void hidg_disable(struct usb_function *f)
{
struct f_hidg *hidg = func_to_hidg(f);
struct f_hidg_req_list *list, *next;
+ unsigned long flags;
usb_ep_disable(hidg->in_ep);
usb_ep_disable(hidg->out_ep);
+ spin_lock_irqsave(&hidg->read_spinlock, flags);
list_for_each_entry_safe(list, next, &hidg->completed_out_req, list) {
+ free_ep_req(hidg->out_ep, list->req);
list_del(&list->list);
kfree(list);
}
+ spin_unlock_irqrestore(&hidg->read_spinlock, flags);
+
+ spin_lock_irqsave(&hidg->write_spinlock, flags);
+ if (!hidg->write_pending) {
+ free_ep_req(hidg->in_ep, hidg->req);
+ hidg->write_pending = 1;
+ }
+
+ hidg->req = NULL;
+ spin_unlock_irqrestore(&hidg->write_spinlock, flags);
}
static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
{
struct usb_composite_dev *cdev = f->config->cdev;
struct f_hidg *hidg = func_to_hidg(f);
+ struct usb_request *req_in = NULL;
+ unsigned long flags;
int i, status = 0;
VDBG(cdev, "hidg_set_alt intf:%d alt:%d\n", intf, alt);
@@ -524,6 +648,12 @@ static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
goto fail;
}
hidg->in_ep->driver_data = hidg;
+
+ req_in = hidg_alloc_ep_req(hidg->in_ep, hidg->report_length);
+ if (!req_in) {
+ status = -ENOMEM;
+ goto disable_ep_in;
+ }
}
@@ -535,12 +665,12 @@ static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
hidg->out_ep);
if (status) {
ERROR(cdev, "config_ep_by_speed FAILED!\n");
- goto fail;
+ goto free_req_in;
}
status = usb_ep_enable(hidg->out_ep);
if (status < 0) {
- ERROR(cdev, "Enable IN endpoint FAILED!\n");
- goto fail;
+ ERROR(cdev, "Enable OUT endpoint FAILED!\n");
+ goto free_req_in;
}
hidg->out_ep->driver_data = hidg;
@@ -556,17 +686,37 @@ static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
req->context = hidg;
status = usb_ep_queue(hidg->out_ep, req,
GFP_ATOMIC);
- if (status)
+ if (status) {
ERROR(cdev, "%s queue req --> %d\n",
hidg->out_ep->name, status);
+ free_ep_req(hidg->out_ep, req);
+ }
} else {
- usb_ep_disable(hidg->out_ep);
status = -ENOMEM;
- goto fail;
+ goto disable_out_ep;
}
}
}
+ if (hidg->in_ep != NULL) {
+ spin_lock_irqsave(&hidg->write_spinlock, flags);
+ hidg->req = req_in;
+ hidg->write_pending = 0;
+ spin_unlock_irqrestore(&hidg->write_spinlock, flags);
+
+ wake_up(&hidg->write_queue);
+ }
+ return 0;
+disable_out_ep:
+ usb_ep_disable(hidg->out_ep);
+free_req_in:
+ if (req_in)
+ free_ep_req(hidg->in_ep, req_in);
+
+disable_ep_in:
+ if (hidg->in_ep)
+ usb_ep_disable(hidg->in_ep);
+
fail:
return status;
}
@@ -615,17 +765,17 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f)
goto fail;
hidg->out_ep = ep;
- /* preallocate request and buffer */
- status = -ENOMEM;
- hidg->req = alloc_ep_req(hidg->in_ep, hidg->report_length);
- if (!hidg->req)
- goto fail;
-
/* set descriptor dynamic values */
hidg_interface_desc.bInterfaceSubClass = hidg->bInterfaceSubClass;
hidg_interface_desc.bInterfaceProtocol = hidg->bInterfaceProtocol;
+ hidg_ss_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
+ hidg_ss_in_comp_desc.wBytesPerInterval =
+ cpu_to_le16(hidg->report_length);
hidg_hs_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
hidg_fs_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
+ hidg_ss_out_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
+ hidg_ss_out_comp_desc.wBytesPerInterval =
+ cpu_to_le16(hidg->report_length);
hidg_hs_out_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
hidg_fs_out_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
/*
@@ -641,13 +791,20 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f)
hidg_hs_out_ep_desc.bEndpointAddress =
hidg_fs_out_ep_desc.bEndpointAddress;
+ hidg_ss_in_ep_desc.bEndpointAddress =
+ hidg_fs_in_ep_desc.bEndpointAddress;
+ hidg_ss_out_ep_desc.bEndpointAddress =
+ hidg_fs_out_ep_desc.bEndpointAddress;
+
status = usb_assign_descriptors(f, hidg_fs_descriptors,
- hidg_hs_descriptors, NULL, NULL);
+ hidg_hs_descriptors, hidg_ss_descriptors, NULL);
if (status)
goto fail;
- mutex_init(&hidg->lock);
- spin_lock_init(&hidg->spinlock);
+ spin_lock_init(&hidg->write_spinlock);
+ hidg->write_pending = 1;
+ hidg->req = NULL;
+ spin_lock_init(&hidg->read_spinlock);
init_waitqueue_head(&hidg->write_queue);
init_waitqueue_head(&hidg->read_queue);
INIT_LIST_HEAD(&hidg->completed_out_req);
@@ -840,7 +997,7 @@ static void hidg_free_inst(struct usb_function_instance *f)
mutex_lock(&hidg_ida_lock);
hidg_put_minor(opts->minor);
- if (idr_is_empty(&hidg_ida.idr))
+ if (ida_is_empty(&hidg_ida))
ghid_cleanup();
mutex_unlock(&hidg_ida_lock);
@@ -866,7 +1023,7 @@ static struct usb_function_instance *hidg_alloc_inst(void)
mutex_lock(&hidg_ida_lock);
- if (idr_is_empty(&hidg_ida.idr)) {
+ if (ida_is_empty(&hidg_ida)) {
status = ghid_setup(NULL, HIDG_MINORS);
if (status) {
ret = ERR_PTR(status);
@@ -879,7 +1036,7 @@ static struct usb_function_instance *hidg_alloc_inst(void)
if (opts->minor < 0) {
ret = ERR_PTR(opts->minor);
kfree(opts);
- if (idr_is_empty(&hidg_ida.idr))
+ if (ida_is_empty(&hidg_ida))
ghid_cleanup();
goto unlock;
}
@@ -911,10 +1068,6 @@ static void hidg_unbind(struct usb_configuration *c, struct usb_function *f)
device_destroy(hidg_class, MKDEV(major, hidg->minor));
cdev_del(&hidg->cdev);
- /* disable/free request and end point */
- usb_ep_disable(hidg->in_ep);
- free_ep_req(hidg->in_ep, hidg->req);
-
usb_free_all_descriptors(f);
}
diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c
index 8f3659b65f53..4c8aacc232c0 100644
--- a/drivers/usb/gadget/function/f_mass_storage.c
+++ b/drivers/usb/gadget/function/f_mass_storage.c
@@ -207,6 +207,7 @@
#include <linux/fs.h>
#include <linux/kref.h>
#include <linux/kthread.h>
+#include <linux/sched/signal.h>
#include <linux/limits.h>
#include <linux/rwsem.h>
#include <linux/slab.h>
diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c
index 639603722709..224717e63a53 100644
--- a/drivers/usb/gadget/function/f_ncm.c
+++ b/drivers/usb/gadget/function/f_ncm.c
@@ -998,7 +998,7 @@ static struct sk_buff *package_for_tx(struct f_ncm *ncm)
/* Merge the skbs */
swap(skb2, ncm->skb_tx_data);
if (ncm->skb_tx_data) {
- dev_kfree_skb_any(ncm->skb_tx_data);
+ dev_consume_skb_any(ncm->skb_tx_data);
ncm->skb_tx_data = NULL;
}
@@ -1009,7 +1009,7 @@ static struct sk_buff *package_for_tx(struct f_ncm *ncm)
/* Copy NTB across. */
ntb_iter = (void *) skb_put(skb2, ncm->skb_tx_ndp->len);
memcpy(ntb_iter, ncm->skb_tx_ndp->data, ncm->skb_tx_ndp->len);
- dev_kfree_skb_any(ncm->skb_tx_ndp);
+ dev_consume_skb_any(ncm->skb_tx_ndp);
ncm->skb_tx_ndp = NULL;
/* Insert zero'd datagram. */
@@ -1078,6 +1078,7 @@ static struct sk_buff *ncm_wrap_ntb(struct gether *port,
if (!ncm->skb_tx_data)
goto err;
+ ncm->skb_tx_data->dev = ncm->netdev;
ntb_data = (void *) skb_put(ncm->skb_tx_data, ncb_len);
memset(ntb_data, 0, ncb_len);
/* dwSignature */
@@ -1096,6 +1097,8 @@ static struct sk_buff *ncm_wrap_ntb(struct gether *port,
GFP_ATOMIC);
if (!ncm->skb_tx_ndp)
goto err;
+
+ ncm->skb_tx_ndp->dev = ncm->netdev;
ntb_ndp = (void *) skb_put(ncm->skb_tx_ndp,
opts->ndp_size);
memset(ntb_ndp, 0, ncb_len);
@@ -1110,8 +1113,7 @@ static struct sk_buff *ncm_wrap_ntb(struct gether *port,
}
/* Delay the timer. */
- hrtimer_start(&ncm->task_timer,
- ktime_set(0, TX_TIMEOUT_NSECS),
+ hrtimer_start(&ncm->task_timer, TX_TIMEOUT_NSECS,
HRTIMER_MODE_REL);
/* Add the datagram position entries */
@@ -1133,7 +1135,7 @@ static struct sk_buff *ncm_wrap_ntb(struct gether *port,
memset(ntb_data, 0, dgram_pad);
ntb_data = (void *) skb_put(ncm->skb_tx_data, skb->len);
memcpy(ntb_data, skb->data, skb->len);
- dev_kfree_skb_any(skb);
+ dev_consume_skb_any(skb);
skb = NULL;
} else if (ncm->skb_tx_data && ncm->timer_force_tx) {
@@ -1329,7 +1331,7 @@ static int ncm_unwrap_ntb(struct gether *port,
} while (ndp_len > 2 * (opts->dgram_item_len * 2));
} while (ndp_index);
- dev_kfree_skb_any(skb);
+ dev_consume_skb_any(skb);
VDBG(port->func.config->cdev,
"Parsed NTB with %d frames\n", dgram_counter);
diff --git a/drivers/usb/gadget/function/f_phonet.c b/drivers/usb/gadget/function/f_phonet.c
index 0473d619d5bf..b4058f0000e4 100644
--- a/drivers/usb/gadget/function/f_phonet.c
+++ b/drivers/usb/gadget/function/f_phonet.c
@@ -261,19 +261,10 @@ out:
return NETDEV_TX_OK;
}
-static int pn_net_mtu(struct net_device *dev, int new_mtu)
-{
- if ((new_mtu < PHONET_MIN_MTU) || (new_mtu > PHONET_MAX_MTU))
- return -EINVAL;
- dev->mtu = new_mtu;
- return 0;
-}
-
static const struct net_device_ops pn_netdev_ops = {
.ndo_open = pn_net_open,
.ndo_stop = pn_net_close,
.ndo_start_xmit = pn_net_xmit,
- .ndo_change_mtu = pn_net_mtu,
};
static void pn_net_setup(struct net_device *dev)
@@ -282,6 +273,8 @@ static void pn_net_setup(struct net_device *dev)
dev->type = ARPHRD_PHONET;
dev->flags = IFF_POINTOPOINT | IFF_NOARP;
dev->mtu = PHONET_DEV_MTU;
+ dev->min_mtu = PHONET_MIN_MTU;
+ dev->max_mtu = PHONET_MAX_MTU;
dev->hard_header_len = 1;
dev->dev_addr[0] = PN_MEDIA_USB;
dev->addr_len = 1;
diff --git a/drivers/usb/gadget/function/f_printer.c b/drivers/usb/gadget/function/f_printer.c
index 0de36cda6e41..8df244fc9d80 100644
--- a/drivers/usb/gadget/function/f_printer.c
+++ b/drivers/usb/gadget/function/f_printer.c
@@ -49,7 +49,6 @@
#include "u_printer.h"
-#define PNP_STRING_LEN 1024
#define PRINTER_MINORS 4
#define GET_DEVICE_ID 0
#define GET_PORT_STATUS 1
@@ -907,8 +906,7 @@ static bool gprinter_req_match(struct usb_function *f,
switch (ctrl->bRequest) {
case GET_DEVICE_ID:
w_index >>= 8;
- if (w_length <= PNP_STRING_LEN &&
- (USB_DIR_IN & ctrl->bRequestType))
+ if (USB_DIR_IN & ctrl->bRequestType)
break;
return false;
case GET_PORT_STATUS:
@@ -937,6 +935,7 @@ static int printer_func_setup(struct usb_function *f,
struct printer_dev *dev = func_to_printer(f);
struct usb_composite_dev *cdev = f->config->cdev;
struct usb_request *req = cdev->req;
+ u8 *buf = req->buf;
int value = -EOPNOTSUPP;
u16 wIndex = le16_to_cpu(ctrl->wIndex);
u16 wValue = le16_to_cpu(ctrl->wValue);
@@ -953,10 +952,16 @@ static int printer_func_setup(struct usb_function *f,
if ((wIndex>>8) != dev->interface)
break;
- value = (dev->pnp_string[0] << 8) | dev->pnp_string[1];
- memcpy(req->buf, dev->pnp_string, value);
+ if (!dev->pnp_string) {
+ value = 0;
+ break;
+ }
+ value = strlen(dev->pnp_string);
+ buf[0] = (value >> 8) & 0xFF;
+ buf[1] = value & 0xFF;
+ memcpy(buf + 2, dev->pnp_string, value);
DBG(dev, "1284 PNP String: %x %s\n", value,
- &dev->pnp_string[2]);
+ dev->pnp_string);
break;
case GET_PORT_STATUS: /* Get Port Status */
@@ -964,7 +969,7 @@ static int printer_func_setup(struct usb_function *f,
if (wIndex != dev->interface)
break;
- *(u8 *)req->buf = dev->printer_status;
+ buf[0] = dev->printer_status;
value = min_t(u16, wLength, 1);
break;
@@ -1157,10 +1162,21 @@ static ssize_t f_printer_opts_pnp_string_show(struct config_item *item,
char *page)
{
struct f_printer_opts *opts = to_f_printer_opts(item);
- int result;
+ int result = 0;
mutex_lock(&opts->lock);
- result = strlcpy(page, opts->pnp_string + 2, PNP_STRING_LEN - 2);
+ if (!opts->pnp_string)
+ goto unlock;
+
+ result = strlcpy(page, opts->pnp_string, PAGE_SIZE);
+ if (result >= PAGE_SIZE) {
+ result = PAGE_SIZE;
+ } else if (page[result - 1] != '\n' && result + 1 < PAGE_SIZE) {
+ page[result++] = '\n';
+ page[result] = '\0';
+ }
+
+unlock:
mutex_unlock(&opts->lock);
return result;
@@ -1170,13 +1186,24 @@ static ssize_t f_printer_opts_pnp_string_store(struct config_item *item,
const char *page, size_t len)
{
struct f_printer_opts *opts = to_f_printer_opts(item);
- int result, l;
+ char *new_pnp;
+ int result;
mutex_lock(&opts->lock);
- result = strlcpy(opts->pnp_string + 2, page, PNP_STRING_LEN - 2);
- l = strlen(opts->pnp_string + 2) + 2;
- opts->pnp_string[0] = (l >> 8) & 0xFF;
- opts->pnp_string[1] = l & 0xFF;
+
+ new_pnp = kstrndup(page, len, GFP_KERNEL);
+ if (!new_pnp) {
+ result = -ENOMEM;
+ goto unlock;
+ }
+
+ if (opts->pnp_string_allocated)
+ kfree(opts->pnp_string);
+
+ opts->pnp_string_allocated = true;
+ opts->pnp_string = new_pnp;
+ result = len;
+unlock:
mutex_unlock(&opts->lock);
return result;
@@ -1265,11 +1292,13 @@ static void gprinter_free_inst(struct usb_function_instance *f)
mutex_lock(&printer_ida_lock);
gprinter_put_minor(opts->minor);
- if (idr_is_empty(&printer_ida.idr))
+ if (ida_is_empty(&printer_ida))
gprinter_cleanup();
mutex_unlock(&printer_ida_lock);
+ if (opts->pnp_string_allocated)
+ kfree(opts->pnp_string);
kfree(opts);
}
@@ -1289,7 +1318,7 @@ static struct usb_function_instance *gprinter_alloc_inst(void)
mutex_lock(&printer_ida_lock);
- if (idr_is_empty(&printer_ida.idr)) {
+ if (ida_is_empty(&printer_ida)) {
status = gprinter_setup(PRINTER_MINORS);
if (status) {
ret = ERR_PTR(status);
@@ -1302,7 +1331,7 @@ static struct usb_function_instance *gprinter_alloc_inst(void)
if (opts->minor < 0) {
ret = ERR_PTR(opts->minor);
kfree(opts);
- if (idr_is_empty(&printer_ida.idr))
+ if (ida_is_empty(&printer_ida))
gprinter_cleanup();
goto unlock;
}
diff --git a/drivers/usb/gadget/function/f_tcm.c b/drivers/usb/gadget/function/f_tcm.c
index 197f73386fac..a82e2bd5ea34 100644
--- a/drivers/usb/gadget/function/f_tcm.c
+++ b/drivers/usb/gadget/function/f_tcm.c
@@ -373,7 +373,7 @@ static void bot_cleanup_old_alt(struct f_uas *fu)
usb_ep_free_request(fu->ep_in, fu->bot_req_in);
usb_ep_free_request(fu->ep_out, fu->bot_req_out);
usb_ep_free_request(fu->ep_out, fu->cmd.req);
- usb_ep_free_request(fu->ep_out, fu->bot_status.req);
+ usb_ep_free_request(fu->ep_in, fu->bot_status.req);
kfree(fu->cmd.buf);
@@ -1073,7 +1073,7 @@ static struct usbg_cmd *usbg_get_cmd(struct f_uas *fu,
struct usbg_cmd *cmd;
int tag;
- tag = percpu_ida_alloc(&se_sess->sess_tag_pool, GFP_ATOMIC);
+ tag = percpu_ida_alloc(&se_sess->sess_tag_pool, TASK_RUNNING);
if (tag < 0)
return ERR_PTR(-ENOMEM);
diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
index cd214ec8a601..f6a0d3a1311b 100644
--- a/drivers/usb/gadget/function/f_uac2.c
+++ b/drivers/usb/gadget/function/f_uac2.c
@@ -22,9 +22,6 @@
#include "u_uac2.h"
-/* Keep everyone on toes */
-#define USB_XFERS 2
-
/*
* The driver implements a simple UAC_2 topology.
* USB-OUT -> IT_1 -> OT_3 -> ALSA_Capture
@@ -78,7 +75,7 @@ struct uac2_rtd_params {
size_t period_size;
unsigned max_psize;
- struct uac2_req ureq[USB_XFERS];
+ struct uac2_req *ureq;
spinlock_t lock;
};
@@ -269,6 +266,8 @@ static int
uac2_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_uac2_chip *uac2 = snd_pcm_substream_chip(substream);
+ struct audio_dev *agdev = uac2_to_agdev(uac2);
+ struct f_uac2_opts *uac2_opts = agdev_to_uac2_opts(agdev);
struct uac2_rtd_params *prm;
unsigned long flags;
int err = 0;
@@ -300,7 +299,7 @@ uac2_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
/* Clear buffer after Play stops */
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && !prm->ss)
- memset(prm->rbuf, 0, prm->max_psize * USB_XFERS);
+ memset(prm->rbuf, 0, prm->max_psize * uac2_opts->req_number);
return err;
}
@@ -943,6 +942,8 @@ static inline void
free_ep(struct uac2_rtd_params *prm, struct usb_ep *ep)
{
struct snd_uac2_chip *uac2 = prm->uac2;
+ struct audio_dev *agdev = uac2_to_agdev(uac2);
+ struct f_uac2_opts *uac2_opts = agdev_to_uac2_opts(agdev);
int i;
if (!prm->ep_enabled)
@@ -950,7 +951,7 @@ free_ep(struct uac2_rtd_params *prm, struct usb_ep *ep)
prm->ep_enabled = false;
- for (i = 0; i < USB_XFERS; i++) {
+ for (i = 0; i < uac2_opts->req_number; i++) {
if (prm->ureq[i].req) {
usb_ep_dequeue(ep, prm->ureq[i].req);
usb_ep_free_request(ep, prm->ureq[i].req);
@@ -1067,13 +1068,13 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
agdev->out_ep = usb_ep_autoconfig(gadget, &fs_epout_desc);
if (!agdev->out_ep) {
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
- goto err;
+ return ret;
}
agdev->in_ep = usb_ep_autoconfig(gadget, &fs_epin_desc);
if (!agdev->in_ep) {
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
- goto err;
+ return ret;
}
uac2->p_prm.uac2 = uac2;
@@ -1091,35 +1092,51 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
ret = usb_assign_descriptors(fn, fs_audio_desc, hs_audio_desc, NULL,
NULL);
if (ret)
- goto err;
+ return ret;
prm = &agdev->uac2.c_prm;
prm->max_psize = hs_epout_desc.wMaxPacketSize;
- prm->rbuf = kzalloc(prm->max_psize * USB_XFERS, GFP_KERNEL);
+ prm->ureq = kcalloc(uac2_opts->req_number, sizeof(struct uac2_req),
+ GFP_KERNEL);
+ if (!prm->ureq) {
+ ret = -ENOMEM;
+ goto err_free_descs;
+ }
+ prm->rbuf = kcalloc(uac2_opts->req_number, prm->max_psize, GFP_KERNEL);
if (!prm->rbuf) {
prm->max_psize = 0;
+ ret = -ENOMEM;
goto err_free_descs;
}
prm = &agdev->uac2.p_prm;
prm->max_psize = hs_epin_desc.wMaxPacketSize;
- prm->rbuf = kzalloc(prm->max_psize * USB_XFERS, GFP_KERNEL);
+ prm->ureq = kcalloc(uac2_opts->req_number, sizeof(struct uac2_req),
+ GFP_KERNEL);
+ if (!prm->ureq) {
+ ret = -ENOMEM;
+ goto err_free_descs;
+ }
+ prm->rbuf = kcalloc(uac2_opts->req_number, prm->max_psize, GFP_KERNEL);
if (!prm->rbuf) {
prm->max_psize = 0;
- goto err_free_descs;
+ ret = -ENOMEM;
+ goto err_no_memory;
}
ret = alsa_uac2_init(agdev);
if (ret)
- goto err_free_descs;
+ goto err_no_memory;
return 0;
-err_free_descs:
- usb_free_all_descriptors(fn);
-err:
+err_no_memory:
+ kfree(agdev->uac2.p_prm.ureq);
+ kfree(agdev->uac2.c_prm.ureq);
kfree(agdev->uac2.p_prm.rbuf);
kfree(agdev->uac2.c_prm.rbuf);
- return -EINVAL;
+err_free_descs:
+ usb_free_all_descriptors(fn);
+ return ret;
}
static int
@@ -1127,6 +1144,7 @@ afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt)
{
struct usb_composite_dev *cdev = fn->config->cdev;
struct audio_dev *agdev = func_to_agdev(fn);
+ struct f_uac2_opts *opts = agdev_to_uac2_opts(agdev);
struct snd_uac2_chip *uac2 = &agdev->uac2;
struct usb_gadget *gadget = cdev->gadget;
struct device *dev = &uac2->pdev.dev;
@@ -1157,7 +1175,6 @@ afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt)
agdev->as_out_alt = alt;
req_len = prm->max_psize;
} else if (intf == agdev->as_in_intf) {
- struct f_uac2_opts *opts = agdev_to_uac2_opts(agdev);
unsigned int factor, rate;
struct usb_endpoint_descriptor *ep_desc;
@@ -1203,7 +1220,7 @@ afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt)
prm->ep_enabled = true;
usb_ep_enable(ep);
- for (i = 0; i < USB_XFERS; i++) {
+ for (i = 0; i < opts->req_number; i++) {
if (!prm->ureq[i].req) {
req = usb_ep_alloc_request(ep, GFP_ATOMIC);
if (req == NULL)
@@ -1487,6 +1504,7 @@ UAC2_ATTRIBUTE(p_ssize);
UAC2_ATTRIBUTE(c_chmask);
UAC2_ATTRIBUTE(c_srate);
UAC2_ATTRIBUTE(c_ssize);
+UAC2_ATTRIBUTE(req_number);
static struct configfs_attribute *f_uac2_attrs[] = {
&f_uac2_opts_attr_p_chmask,
@@ -1495,6 +1513,7 @@ static struct configfs_attribute *f_uac2_attrs[] = {
&f_uac2_opts_attr_c_chmask,
&f_uac2_opts_attr_c_srate,
&f_uac2_opts_attr_c_ssize,
+ &f_uac2_opts_attr_req_number,
NULL,
};
@@ -1532,6 +1551,7 @@ static struct usb_function_instance *afunc_alloc_inst(void)
opts->c_chmask = UAC2_DEF_CCHMASK;
opts->c_srate = UAC2_DEF_CSRATE;
opts->c_ssize = UAC2_DEF_CSSIZE;
+ opts->req_number = UAC2_DEF_REQ_NUM;
return &opts->func_inst;
}
@@ -1560,6 +1580,7 @@ static void afunc_unbind(struct usb_configuration *c, struct usb_function *f)
prm = &agdev->uac2.c_prm;
kfree(prm->rbuf);
+ kfree(prm->ureq);
usb_free_all_descriptors(f);
}
diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c
index 27ed51b5082f..f8a1881609a2 100644
--- a/drivers/usb/gadget/function/f_uvc.c
+++ b/drivers/usb/gadget/function/f_uvc.c
@@ -258,13 +258,6 @@ uvc_function_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
memcpy(&uvc_event->req, ctrl, sizeof(uvc_event->req));
v4l2_event_queue(&uvc->vdev, &v4l2_event);
- /* Pass additional setup data to userspace */
- if (uvc->event_setup_out && uvc->event_length) {
- uvc->control_req->length = uvc->event_length;
- return usb_ep_queue(uvc->func.config->cdev->gadget->ep0,
- uvc->control_req, GFP_ATOMIC);
- }
-
return 0;
}
@@ -601,6 +594,14 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
opts->streaming_maxpacket = clamp(opts->streaming_maxpacket, 1U, 3072U);
opts->streaming_maxburst = min(opts->streaming_maxburst, 15U);
+ /* For SS, wMaxPacketSize has to be 1024 if bMaxBurst is not 0 */
+ 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);
+ }
+
/* Fill in the FS/HS/SS Video Streaming specific descriptors from the
* module parameters.
*
@@ -632,7 +633,7 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
uvc_ss_streaming_comp.bMaxBurst = opts->streaming_maxburst;
uvc_ss_streaming_comp.wBytesPerInterval =
cpu_to_le16(max_packet_size * max_packet_mult *
- opts->streaming_maxburst);
+ (opts->streaming_maxburst + 1));
/* Allocate endpoints. */
ep = usb_ep_autoconfig(cdev->gadget, &uvc_control_ep);
diff --git a/drivers/usb/gadget/function/rndis.c b/drivers/usb/gadget/function/rndis.c
index ab6ac1b74ac0..a3b5e468b116 100644
--- a/drivers/usb/gadget/function/rndis.c
+++ b/drivers/usb/gadget/function/rndis.c
@@ -80,8 +80,7 @@ static const struct file_operations rndis_proc_fops;
#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
/* supported OIDs */
-static const u32 oid_supported_list[] =
-{
+static const u32 oid_supported_list[] = {
/* the general stuff */
RNDIS_OID_GEN_SUPPORTED_LIST,
RNDIS_OID_GEN_HARDWARE_STATUS,
@@ -474,8 +473,7 @@ static int gen_ndis_query_resp(struct rndis_params *params, u32 OID, u8 *buf,
break;
default:
- pr_warning("%s: query unknown OID 0x%08X\n",
- __func__, OID);
+ pr_warn("%s: query unknown OID 0x%08X\n", __func__, OID);
}
if (retval < 0)
length = 0;
@@ -546,8 +544,8 @@ static int gen_ndis_set_resp(struct rndis_params *params, u32 OID,
break;
default:
- pr_warning("%s: set unknown OID 0x%08X, size %d\n",
- __func__, OID, buf_len);
+ pr_warn("%s: set unknown OID 0x%08X, size %d\n",
+ __func__, OID, buf_len);
}
return retval;
@@ -854,7 +852,7 @@ int rndis_msg_parser(struct rndis_params *params, u8 *buf)
* In one case those messages seemed to relate to the host
* suspending itself.
*/
- pr_warning("%s: unknown RNDIS message 0x%08X len %d\n",
+ pr_warn("%s: unknown RNDIS message 0x%08X len %d\n",
__func__, MsgType, MsgLength);
print_hex_dump_bytes(__func__, DUMP_PREFIX_OFFSET,
buf, MsgLength);
diff --git a/drivers/usb/gadget/function/rndis.h b/drivers/usb/gadget/function/rndis.h
index ef92eb66d8ad..21e0430ffb98 100644
--- a/drivers/usb/gadget/function/rndis.h
+++ b/drivers/usb/gadget/function/rndis.h
@@ -22,8 +22,7 @@
#define RNDIS_MAXIMUM_FRAME_SIZE 1518
#define RNDIS_MAX_TOTAL_SIZE 1558
-typedef struct rndis_init_msg_type
-{
+typedef struct rndis_init_msg_type {
__le32 MessageType;
__le32 MessageLength;
__le32 RequestID;
@@ -32,8 +31,7 @@ typedef struct rndis_init_msg_type
__le32 MaxTransferSize;
} rndis_init_msg_type;
-typedef struct rndis_init_cmplt_type
-{
+typedef struct rndis_init_cmplt_type {
__le32 MessageType;
__le32 MessageLength;
__le32 RequestID;
@@ -49,15 +47,13 @@ typedef struct rndis_init_cmplt_type
__le32 AFListSize;
} rndis_init_cmplt_type;
-typedef struct rndis_halt_msg_type
-{
+typedef struct rndis_halt_msg_type {
__le32 MessageType;
__le32 MessageLength;
__le32 RequestID;
} rndis_halt_msg_type;
-typedef struct rndis_query_msg_type
-{
+typedef struct rndis_query_msg_type {
__le32 MessageType;
__le32 MessageLength;
__le32 RequestID;
@@ -67,8 +63,7 @@ typedef struct rndis_query_msg_type
__le32 DeviceVcHandle;
} rndis_query_msg_type;
-typedef struct rndis_query_cmplt_type
-{
+typedef struct rndis_query_cmplt_type {
__le32 MessageType;
__le32 MessageLength;
__le32 RequestID;
@@ -77,8 +72,7 @@ typedef struct rndis_query_cmplt_type
__le32 InformationBufferOffset;
} rndis_query_cmplt_type;
-typedef struct rndis_set_msg_type
-{
+typedef struct rndis_set_msg_type {
__le32 MessageType;
__le32 MessageLength;
__le32 RequestID;
@@ -88,31 +82,27 @@ typedef struct rndis_set_msg_type
__le32 DeviceVcHandle;
} rndis_set_msg_type;
-typedef struct rndis_set_cmplt_type
-{
+typedef struct rndis_set_cmplt_type {
__le32 MessageType;
__le32 MessageLength;
__le32 RequestID;
__le32 Status;
} rndis_set_cmplt_type;
-typedef struct rndis_reset_msg_type
-{
+typedef struct rndis_reset_msg_type {
__le32 MessageType;
__le32 MessageLength;
__le32 Reserved;
} rndis_reset_msg_type;
-typedef struct rndis_reset_cmplt_type
-{
+typedef struct rndis_reset_cmplt_type {
__le32 MessageType;
__le32 MessageLength;
__le32 Status;
__le32 AddressingReset;
} rndis_reset_cmplt_type;
-typedef struct rndis_indicate_status_msg_type
-{
+typedef struct rndis_indicate_status_msg_type {
__le32 MessageType;
__le32 MessageLength;
__le32 Status;
@@ -120,23 +110,20 @@ typedef struct rndis_indicate_status_msg_type
__le32 StatusBufferOffset;
} rndis_indicate_status_msg_type;
-typedef struct rndis_keepalive_msg_type
-{
+typedef struct rndis_keepalive_msg_type {
__le32 MessageType;
__le32 MessageLength;
__le32 RequestID;
} rndis_keepalive_msg_type;
-typedef struct rndis_keepalive_cmplt_type
-{
+typedef struct rndis_keepalive_cmplt_type {
__le32 MessageType;
__le32 MessageLength;
__le32 RequestID;
__le32 Status;
} rndis_keepalive_cmplt_type;
-struct rndis_packet_msg_type
-{
+struct rndis_packet_msg_type {
__le32 MessageType;
__le32 MessageLength;
__le32 DataOffset;
@@ -150,8 +137,7 @@ struct rndis_packet_msg_type
__le32 Reserved;
} __attribute__ ((packed));
-struct rndis_config_parameter
-{
+struct rndis_config_parameter {
__le32 ParameterNameOffset;
__le32 ParameterNameLength;
__le32 ParameterType;
@@ -160,23 +146,20 @@ struct rndis_config_parameter
};
/* implementation specific */
-enum rndis_state
-{
+enum rndis_state {
RNDIS_UNINITIALIZED,
RNDIS_INITIALIZED,
RNDIS_DATA_INITIALIZED,
};
-typedef struct rndis_resp_t
-{
+typedef struct rndis_resp_t {
struct list_head list;
u8 *buf;
u32 length;
int send;
} rndis_resp_t;
-typedef struct rndis_params
-{
+typedef struct rndis_params {
int confignr;
u8 used;
u16 saved_filter;
diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c
index 5d1bd13a56c1..c3cab77181d4 100644
--- a/drivers/usb/gadget/function/u_ether.c
+++ b/drivers/usb/gadget/function/u_ether.c
@@ -142,15 +142,6 @@ static inline int qlen(struct usb_gadget *gadget, unsigned qmult)
/* NETWORK DRIVER HOOKUP (to the layer above this driver) */
-static int ueth_change_mtu(struct net_device *net, int new_mtu)
-{
- if (new_mtu <= ETH_HLEN || new_mtu > GETHER_MAX_ETH_FRAME_LEN)
- return -ERANGE;
- net->mtu = new_mtu;
-
- return 0;
-}
-
static void eth_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *p)
{
struct eth_dev *dev = netdev_priv(net);
@@ -224,7 +215,7 @@ rx_submit(struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags)
if (dev->port_usb->is_fixed)
size = max_t(size_t, size, dev->port_usb->fixed_out_len);
- skb = alloc_skb(size + NET_IP_ALIGN, gfp_flags);
+ skb = __netdev_alloc_skb(dev->net, size + NET_IP_ALIGN, gfp_flags);
if (skb == NULL) {
DBG(dev, "no rx skb\n");
goto enomem;
@@ -455,16 +446,17 @@ static void tx_complete(struct usb_ep *ep, struct usb_request *req)
/* FALLTHROUGH */
case -ECONNRESET: /* unlink */
case -ESHUTDOWN: /* disconnect etc */
+ dev_kfree_skb_any(skb);
break;
case 0:
dev->net->stats.tx_bytes += skb->len;
+ dev_consume_skb_any(skb);
}
dev->net->stats.tx_packets++;
spin_lock(&dev->req_lock);
list_add(&req->list, &dev->tx_reqs);
spin_unlock(&dev->req_lock);
- dev_kfree_skb_any(skb);
atomic_dec(&dev->tx_qlen);
if (netif_carrier_ok(dev->net))
@@ -729,7 +721,6 @@ static const struct net_device_ops eth_netdev_ops = {
.ndo_open = eth_open,
.ndo_stop = eth_stop,
.ndo_start_xmit = eth_start_xmit,
- .ndo_change_mtu = ueth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
};
@@ -792,6 +783,10 @@ struct eth_dev *gether_setup_name(struct usb_gadget *g,
net->ethtool_ops = &ops;
+ /* MTU range: 14 - 15412 */
+ net->min_mtu = ETH_HLEN;
+ net->max_mtu = GETHER_MAX_ETH_FRAME_LEN;
+
dev->gadget = g;
SET_NETDEV_DEV(net, &g->dev);
SET_NETDEV_DEVTYPE(net, &gadget_type);
@@ -918,9 +913,16 @@ EXPORT_SYMBOL_GPL(gether_set_dev_addr);
int gether_get_dev_addr(struct net_device *net, char *dev_addr, int len)
{
struct eth_dev *dev;
+ int ret;
dev = netdev_priv(net);
- return get_ether_addr_str(dev->dev_mac, dev_addr, len);
+ ret = get_ether_addr_str(dev->dev_mac, dev_addr, len);
+ if (ret + 1 < len) {
+ dev_addr[ret++] = '\n';
+ dev_addr[ret] = '\0';
+ }
+
+ return ret;
}
EXPORT_SYMBOL_GPL(gether_get_dev_addr);
@@ -940,9 +942,16 @@ EXPORT_SYMBOL_GPL(gether_set_host_addr);
int gether_get_host_addr(struct net_device *net, char *host_addr, int len)
{
struct eth_dev *dev;
+ int ret;
dev = netdev_priv(net);
- return get_ether_addr_str(dev->host_mac, host_addr, len);
+ ret = get_ether_addr_str(dev->host_mac, host_addr, len);
+ if (ret + 1 < len) {
+ host_addr[ret++] = '\n';
+ host_addr[ret] = '\0';
+ }
+
+ return ret;
}
EXPORT_SYMBOL_GPL(gether_get_host_addr);
@@ -989,10 +998,12 @@ EXPORT_SYMBOL_GPL(gether_get_qmult);
int gether_get_ifname(struct net_device *net, char *name, int len)
{
+ int ret;
+
rtnl_lock();
- strlcpy(name, netdev_name(net), len);
+ ret = snprintf(name, len, "%s\n", netdev_name(net));
rtnl_unlock();
- return strlen(name);
+ return ret < len ? ret : len;
}
EXPORT_SYMBOL_GPL(gether_get_ifname);
diff --git a/drivers/usb/gadget/function/u_ether_configfs.h b/drivers/usb/gadget/function/u_ether_configfs.h
index 4f47289fcf7c..c71133de17e7 100644
--- a/drivers/usb/gadget/function/u_ether_configfs.h
+++ b/drivers/usb/gadget/function/u_ether_configfs.h
@@ -108,7 +108,7 @@
mutex_lock(&opts->lock); \
qmult = gether_get_qmult(opts->net); \
mutex_unlock(&opts->lock); \
- return sprintf(page, "%d", qmult); \
+ return sprintf(page, "%d\n", qmult); \
} \
\
static ssize_t _f_##_opts_qmult_store(struct config_item *item, \
diff --git a/drivers/usb/gadget/function/u_fs.h b/drivers/usb/gadget/function/u_fs.h
index 60139854e0b1..4b6969451cdc 100644
--- a/drivers/usb/gadget/function/u_fs.h
+++ b/drivers/usb/gadget/function/u_fs.h
@@ -247,7 +247,8 @@ struct ffs_data {
unsigned user_flags;
- u8 eps_addrmap[15];
+#define FFS_MAX_EPS_COUNT 31
+ u8 eps_addrmap[FFS_MAX_EPS_COUNT];
unsigned short strings_count;
unsigned short interfaces_count;
diff --git a/drivers/usb/gadget/function/u_printer.h b/drivers/usb/gadget/function/u_printer.h
index 0e2c49d4274e..8d30b7577f87 100644
--- a/drivers/usb/gadget/function/u_printer.h
+++ b/drivers/usb/gadget/function/u_printer.h
@@ -18,12 +18,11 @@
#include <linux/usb/composite.h>
-#define PNP_STRING_LEN 1024
-
struct f_printer_opts {
struct usb_function_instance func_inst;
int minor;
- char pnp_string[PNP_STRING_LEN];
+ char *pnp_string;
+ bool pnp_string_allocated;
unsigned q_len;
/*
diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c
index e0cd1e4c8892..000677c991b0 100644
--- a/drivers/usb/gadget/function/u_serial.c
+++ b/drivers/usb/gadget/function/u_serial.c
@@ -622,8 +622,8 @@ static void gs_write_complete(struct usb_ep *ep, struct usb_request *req)
switch (req->status) {
default:
/* presumably a transient fault */
- pr_warning("%s: unexpected %s status %d\n",
- __func__, ep->name, req->status);
+ pr_warn("%s: unexpected %s status %d\n",
+ __func__, ep->name, req->status);
/* FALL THROUGH */
case 0:
/* normal completion */
@@ -1256,7 +1256,8 @@ static void gserial_console_exit(void)
struct gscons_info *info = &gscons_info;
unregister_console(&gserial_cons);
- kthread_stop(info->console_thread);
+ if (info->console_thread != NULL)
+ kthread_stop(info->console_thread);
gs_buf_free(&info->con_buf);
}
diff --git a/drivers/usb/gadget/function/u_uac2.h b/drivers/usb/gadget/function/u_uac2.h
index 78dd37279bd4..19eeb83538a5 100644
--- a/drivers/usb/gadget/function/u_uac2.h
+++ b/drivers/usb/gadget/function/u_uac2.h
@@ -24,6 +24,7 @@
#define UAC2_DEF_CCHMASK 0x3
#define UAC2_DEF_CSRATE 64000
#define UAC2_DEF_CSSIZE 2
+#define UAC2_DEF_REQ_NUM 2
struct f_uac2_opts {
struct usb_function_instance func_inst;
@@ -33,6 +34,7 @@ struct f_uac2_opts {
int c_chmask;
int c_srate;
int c_ssize;
+ int req_number;
bool bound;
struct mutex lock;
diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h
index 7d3bb6272e06..11d70dead32b 100644
--- a/drivers/usb/gadget/function/uvc.h
+++ b/drivers/usb/gadget/function/uvc.h
@@ -26,14 +26,12 @@
#define UVC_EVENT_DATA (V4L2_EVENT_PRIVATE_START + 5)
#define UVC_EVENT_LAST (V4L2_EVENT_PRIVATE_START + 5)
-struct uvc_request_data
-{
+struct uvc_request_data {
__s32 length;
__u8 data[60];
};
-struct uvc_event
-{
+struct uvc_event {
union {
enum usb_device_speed speed;
struct usb_ctrlrequest req;
@@ -104,8 +102,7 @@ extern unsigned int uvc_gadget_trace_param;
* Structures
*/
-struct uvc_video
-{
+struct uvc_video {
struct usb_ep *ep;
/* Frame parameters */
@@ -134,15 +131,13 @@ struct uvc_video
unsigned int fid;
};
-enum uvc_state
-{
+enum uvc_state {
UVC_STATE_DISCONNECTED,
UVC_STATE_CONNECTED,
UVC_STATE_STREAMING,
};
-struct uvc_device
-{
+struct uvc_device {
struct video_device vdev;
struct v4l2_device v4l2_dev;
enum uvc_state state;
@@ -175,8 +170,7 @@ static inline struct uvc_device *to_uvc(struct usb_function *f)
return container_of(f, struct uvc_device, func);
}
-struct uvc_file_handle
-{
+struct uvc_file_handle {
struct v4l2_fh vfh;
struct uvc_video *device;
};
diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c
index 31125a4a2658..4e037d2a7a60 100644
--- a/drivers/usb/gadget/function/uvc_configfs.c
+++ b/drivers/usb/gadget/function/uvc_configfs.c
@@ -547,7 +547,7 @@ out:
return ret;
}
-static int uvcg_control_class_drop_link(struct config_item *src,
+static void uvcg_control_class_drop_link(struct config_item *src,
struct config_item *target)
{
struct config_item *control, *header;
@@ -555,7 +555,6 @@ static int uvcg_control_class_drop_link(struct config_item *src,
struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex;
struct uvc_descriptor_header **class_array;
struct uvcg_control_header *target_hdr;
- int ret = -EINVAL;
mutex_lock(su_mutex); /* for navigating configfs hierarchy */
@@ -569,23 +568,17 @@ static int uvcg_control_class_drop_link(struct config_item *src,
mutex_lock(&opts->lock);
class_array = uvcg_get_ctl_class_arr(src, opts);
- if (!class_array)
- goto unlock;
- if (opts->refcnt) {
- ret = -EBUSY;
+ if (!class_array || opts->refcnt)
goto unlock;
- }
target_hdr = to_uvcg_control_header(target);
--target_hdr->linked;
class_array[0] = NULL;
- ret = 0;
unlock:
mutex_unlock(&opts->lock);
out:
mutex_unlock(su_mutex);
- return ret;
}
static struct configfs_item_operations uvcg_control_class_item_ops = {
@@ -777,7 +770,7 @@ out:
return ret;
}
-static int uvcg_streaming_header_drop_link(struct config_item *src,
+static void uvcg_streaming_header_drop_link(struct config_item *src,
struct config_item *target)
{
struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex;
@@ -786,7 +779,6 @@ static int uvcg_streaming_header_drop_link(struct config_item *src,
struct uvcg_streaming_header *src_hdr;
struct uvcg_format *target_fmt = NULL;
struct uvcg_format_ptr *format_ptr, *tmp;
- int ret = -EINVAL;
src_hdr = to_uvcg_streaming_header(src);
mutex_lock(su_mutex); /* for navigating configfs hierarchy */
@@ -811,8 +803,6 @@ static int uvcg_streaming_header_drop_link(struct config_item *src,
out:
mutex_unlock(&opts->lock);
mutex_unlock(su_mutex);
- return ret;
-
}
static struct configfs_item_operations uvcg_streaming_header_item_ops = {
@@ -2051,7 +2041,7 @@ out:
return ret;
}
-static int uvcg_streaming_class_drop_link(struct config_item *src,
+static void uvcg_streaming_class_drop_link(struct config_item *src,
struct config_item *target)
{
struct config_item *streaming, *header;
@@ -2059,7 +2049,6 @@ static int uvcg_streaming_class_drop_link(struct config_item *src,
struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex;
struct uvc_descriptor_header ***class_array;
struct uvcg_streaming_header *target_hdr;
- int ret = -EINVAL;
mutex_lock(su_mutex); /* for navigating configfs hierarchy */
@@ -2076,23 +2065,19 @@ static int uvcg_streaming_class_drop_link(struct config_item *src,
if (!class_array || !*class_array)
goto unlock;
- if (opts->refcnt) {
- ret = -EBUSY;
+ if (opts->refcnt)
goto unlock;
- }
target_hdr = to_uvcg_streaming_header(target);
--target_hdr->linked;
kfree(**class_array);
kfree(*class_array);
*class_array = NULL;
- ret = 0;
unlock:
mutex_unlock(&opts->lock);
out:
mutex_unlock(su_mutex);
- return ret;
}
static struct configfs_item_operations uvcg_streaming_class_item_ops = {
diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c
index f4ccbd56f4d2..3e22b45687d3 100644
--- a/drivers/usb/gadget/function/uvc_v4l2.c
+++ b/drivers/usb/gadget/function/uvc_v4l2.c
@@ -53,8 +53,7 @@ uvc_send_response(struct uvc_device *uvc, struct uvc_request_data *data)
* V4L2 ioctls
*/
-struct uvc_format
-{
+struct uvc_format {
u8 bpp;
u32 fcc;
};
diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c
index 3d0d5d94a62f..0f01c04d7cbd 100644
--- a/drivers/usb/gadget/function/uvc_video.c
+++ b/drivers/usb/gadget/function/uvc_video.c
@@ -243,7 +243,7 @@ uvc_video_alloc_requests(struct uvc_video *video)
req_size = video->ep->maxpacket
* max_t(unsigned int, video->ep->maxburst, 1)
- * (video->ep->mult + 1);
+ * (video->ep->mult);
for (i = 0; i < UVC_NUM_REQUESTS; ++i) {
video->req_buffer[i] = kmalloc(req_size, GFP_KERNEL);
diff --git a/drivers/usb/gadget/legacy/audio.c b/drivers/usb/gadget/legacy/audio.c
index 5d7b3c6a422b..8a39f42a4d56 100644
--- a/drivers/usb/gadget/legacy/audio.c
+++ b/drivers/usb/gadget/legacy/audio.c
@@ -229,6 +229,7 @@ static int audio_bind(struct usb_composite_dev *cdev)
uac2_opts->c_chmask = c_chmask;
uac2_opts->c_srate = c_srate;
uac2_opts->c_ssize = c_ssize;
+ uac2_opts->req_number = UAC2_DEF_REQ_NUM;
#else
uac1_opts = container_of(fi_uac1, struct f_uac1_opts, func_inst);
uac1_opts->fn_play = fn_play;
diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c
index bd82dd12deff..a2c916869293 100644
--- a/drivers/usb/gadget/legacy/inode.c
+++ b/drivers/usb/gadget/legacy/inode.c
@@ -20,7 +20,7 @@
#include <linux/uts.h>
#include <linux/wait.h>
#include <linux/compiler.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/poll.h>
@@ -84,8 +84,7 @@ static int ep_open(struct inode *, struct file *);
/* /dev/gadget/$CHIP represents ep0 and the whole device */
enum ep0_state {
- /* DISBLED is the initial state.
- */
+ /* DISABLED is the initial state. */
STATE_DEV_DISABLED = 0,
/* Only one open() of /dev/gadget/$CHIP; only one file tracks
@@ -667,7 +666,7 @@ ep_write_iter(struct kiocb *iocb, struct iov_iter *from)
return -ENOMEM;
}
- if (unlikely(copy_from_iter(buf, len, from) != len)) {
+ if (unlikely(!copy_from_iter_full(buf, len, from))) {
value = -EFAULT;
goto out;
}
@@ -1126,7 +1125,7 @@ ep0_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
/* data and/or status stage for control request */
} else if (dev->state == STATE_DEV_SETUP) {
- /* IN DATA+STATUS caller makes len <= wLength */
+ len = min_t(size_t, len, dev->setup_wLength);
if (dev->setup_in) {
retval = setup_req (dev->gadget->ep0, dev->req, len);
if (retval == 0) {
@@ -1734,10 +1733,12 @@ static struct usb_gadget_driver gadgetfs_driver = {
* such as configuration notifications.
*/
-static int is_valid_config (struct usb_config_descriptor *config)
+static int is_valid_config(struct usb_config_descriptor *config,
+ unsigned int total)
{
return config->bDescriptorType == USB_DT_CONFIG
&& config->bLength == USB_DT_CONFIG_SIZE
+ && total >= USB_DT_CONFIG_SIZE
&& config->bConfigurationValue != 0
&& (config->bmAttributes & USB_CONFIG_ATT_ONE) != 0
&& (config->bmAttributes & USB_CONFIG_ATT_WAKEUP) == 0;
@@ -1762,7 +1763,8 @@ dev_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
}
spin_unlock_irq(&dev->lock);
- if (len < (USB_DT_CONFIG_SIZE + USB_DT_DEVICE_SIZE + 4))
+ if ((len < (USB_DT_CONFIG_SIZE + USB_DT_DEVICE_SIZE + 4)) ||
+ (len > PAGE_SIZE * 4))
return -EINVAL;
/* we might need to change message format someday */
@@ -1779,14 +1781,17 @@ dev_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
spin_lock_irq (&dev->lock);
value = -EINVAL;
- if (dev->buf)
+ if (dev->buf) {
+ kfree(kbuf);
goto fail;
+ }
dev->buf = kbuf;
/* full or low speed config */
dev->config = (void *) kbuf;
total = le16_to_cpu(dev->config->wTotalLength);
- if (!is_valid_config (dev->config) || total >= length)
+ if (!is_valid_config(dev->config, total) ||
+ total > length - USB_DT_DEVICE_SIZE)
goto fail;
kbuf += total;
length -= total;
@@ -1795,10 +1800,13 @@ dev_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
if (kbuf [1] == USB_DT_CONFIG) {
dev->hs_config = (void *) kbuf;
total = le16_to_cpu(dev->hs_config->wTotalLength);
- if (!is_valid_config (dev->hs_config) || total >= length)
+ if (!is_valid_config(dev->hs_config, total) ||
+ total > length - USB_DT_DEVICE_SIZE)
goto fail;
kbuf += total;
length -= total;
+ } else {
+ dev->hs_config = NULL;
}
/* could support multiple configs, using another encoding! */
@@ -1811,7 +1819,6 @@ dev_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
|| dev->dev->bDescriptorType != USB_DT_DEVICE
|| dev->dev->bNumConfigurations != 1)
goto fail;
- dev->dev->bNumConfigurations = 1;
dev->dev->bcdUSB = cpu_to_le16 (0x0200);
/* triggers gadgetfs_bind(); then we can enumerate. */
@@ -1842,7 +1849,7 @@ dev_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
fail:
spin_unlock_irq (&dev->lock);
- pr_debug ("%s: %s fail %Zd, %p\n", shortname, __func__, value, dev);
+ pr_debug ("%s: %s fail %zd, %p\n", shortname, __func__, value, dev);
kfree (dev->buf);
dev->buf = NULL;
return value;
diff --git a/drivers/usb/gadget/legacy/printer.c b/drivers/usb/gadget/legacy/printer.c
index 6f969a86175c..4c9cfff34a03 100644
--- a/drivers/usb/gadget/legacy/printer.c
+++ b/drivers/usb/gadget/legacy/printer.c
@@ -88,8 +88,8 @@ static const struct usb_descriptor_header *otg_desc[2];
static char product_desc [40] = DRIVER_DESC;
static char serial_num [40] = "1";
-static char pnp_string[PNP_STRING_LEN] =
- "XXMFG:linux;MDL:g_printer;CLS:PRINTER;SN:1;";
+static char *pnp_string =
+ "MFG:linux;MDL:g_printer;CLS:PRINTER;SN:1;";
/* static strings, in UTF-8 */
static struct usb_string strings [] = {
@@ -143,23 +143,29 @@ static int printer_do_config(struct usb_configuration *c)
static int printer_bind(struct usb_composite_dev *cdev)
{
struct f_printer_opts *opts;
- int ret, len;
+ int ret;
fi_printer = usb_get_function_instance("printer");
if (IS_ERR(fi_printer))
return PTR_ERR(fi_printer);
- if (iPNPstring)
- strlcpy(&pnp_string[2], iPNPstring, PNP_STRING_LEN - 2);
-
- len = strlen(pnp_string);
- pnp_string[0] = (len >> 8) & 0xFF;
- pnp_string[1] = len & 0xFF;
-
opts = container_of(fi_printer, struct f_printer_opts, func_inst);
opts->minor = 0;
- memcpy(opts->pnp_string, pnp_string, PNP_STRING_LEN);
opts->q_len = QLEN;
+ if (iPNPstring) {
+ opts->pnp_string = kstrdup(iPNPstring, GFP_KERNEL);
+ if (!opts->pnp_string) {
+ ret = -ENOMEM;
+ goto fail_put_func_inst;
+ }
+ opts->pnp_string_allocated = true;
+ /*
+ * we don't free this memory in case of error
+ * as printer cleanup func will do this for us
+ */
+ } else {
+ opts->pnp_string = pnp_string;
+ }
ret = usb_string_ids_tab(cdev, strings);
if (ret < 0)
diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig
index 658b8da60915..4b69f28a9af9 100644
--- a/drivers/usb/gadget/udc/Kconfig
+++ b/drivers/usb/gadget/udc/Kconfig
@@ -60,6 +60,20 @@ config USB_ATMEL_USBA
USBA is the integrated high-speed USB Device controller on
the AT32AP700x, some AT91SAM9 and AT91CAP9 processors from Atmel.
+ The fifo_mode parameter is used to select endpoint allocation mode.
+ fifo_mode = 0 is used to let the driver autoconfigure the endpoints.
+ In this case 2 banks are allocated for isochronous endpoints and
+ only one bank is allocated for the rest of the endpoints.
+
+ fifo_mode = 1 is a generic maximum fifo size (1024 bytes) configuration
+ allowing the usage of ep1 - ep6
+
+ fifo_mode = 2 is a generic performance maximum fifo size (1024 bytes)
+ configuration allowing the usage of ep1 - ep3
+
+ fifo_mode = 3 is a balanced performance configuration allowing the
+ the usage of ep1 - ep8
+
config USB_BCM63XX_UDC
tristate "Broadcom BCM63xx Peripheral Controller"
depends on BCM63XX
diff --git a/drivers/usb/gadget/udc/at91_udc.h b/drivers/usb/gadget/udc/at91_udc.h
index 0a433e6b346b..9bbe72764f31 100644
--- a/drivers/usb/gadget/udc/at91_udc.h
+++ b/drivers/usb/gadget/udc/at91_udc.h
@@ -175,7 +175,7 @@ struct at91_request {
#endif
#define ERR(stuff...) pr_err("udc: " stuff)
-#define WARNING(stuff...) pr_warning("udc: " stuff)
+#define WARNING(stuff...) pr_warn("udc: " stuff)
#define INFO(stuff...) pr_info("udc: " stuff)
#define DBG(stuff...) pr_debug("udc: " stuff)
diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c
index 45bc997d0711..2035906b8ced 100644
--- a/drivers/usb/gadget/udc/atmel_usba_udc.c
+++ b/drivers/usb/gadget/udc/atmel_usba_udc.c
@@ -20,6 +20,7 @@
#include <linux/mfd/syscon.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
+#include <linux/ctype.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/usb/atmel_usba_udc.h>
@@ -318,6 +319,91 @@ static inline void usba_cleanup_debugfs(struct usba_udc *udc)
}
#endif
+static ushort fifo_mode;
+
+/* "modprobe ... fifo_mode=1" etc */
+module_param(fifo_mode, ushort, 0x0);
+MODULE_PARM_DESC(fifo_mode, "Endpoint configuration mode");
+
+/* mode 0 - uses autoconfig */
+
+/* mode 1 - fits in 8KB, generic max fifo configuration */
+static struct usba_fifo_cfg mode_1_cfg[] = {
+{ .hw_ep_num = 0, .fifo_size = 64, .nr_banks = 1, },
+{ .hw_ep_num = 1, .fifo_size = 1024, .nr_banks = 2, },
+{ .hw_ep_num = 2, .fifo_size = 1024, .nr_banks = 1, },
+{ .hw_ep_num = 3, .fifo_size = 1024, .nr_banks = 1, },
+{ .hw_ep_num = 4, .fifo_size = 1024, .nr_banks = 1, },
+{ .hw_ep_num = 5, .fifo_size = 1024, .nr_banks = 1, },
+{ .hw_ep_num = 6, .fifo_size = 1024, .nr_banks = 1, },
+};
+
+/* mode 2 - fits in 8KB, performance max fifo configuration */
+static struct usba_fifo_cfg mode_2_cfg[] = {
+{ .hw_ep_num = 0, .fifo_size = 64, .nr_banks = 1, },
+{ .hw_ep_num = 1, .fifo_size = 1024, .nr_banks = 3, },
+{ .hw_ep_num = 2, .fifo_size = 1024, .nr_banks = 2, },
+{ .hw_ep_num = 3, .fifo_size = 1024, .nr_banks = 2, },
+};
+
+/* mode 3 - fits in 8KB, mixed fifo configuration */
+static struct usba_fifo_cfg mode_3_cfg[] = {
+{ .hw_ep_num = 0, .fifo_size = 64, .nr_banks = 1, },
+{ .hw_ep_num = 1, .fifo_size = 1024, .nr_banks = 2, },
+{ .hw_ep_num = 2, .fifo_size = 512, .nr_banks = 2, },
+{ .hw_ep_num = 3, .fifo_size = 512, .nr_banks = 2, },
+{ .hw_ep_num = 4, .fifo_size = 512, .nr_banks = 2, },
+{ .hw_ep_num = 5, .fifo_size = 512, .nr_banks = 2, },
+{ .hw_ep_num = 6, .fifo_size = 512, .nr_banks = 2, },
+};
+
+/* mode 4 - fits in 8KB, custom fifo configuration */
+static struct usba_fifo_cfg mode_4_cfg[] = {
+{ .hw_ep_num = 0, .fifo_size = 64, .nr_banks = 1, },
+{ .hw_ep_num = 1, .fifo_size = 512, .nr_banks = 2, },
+{ .hw_ep_num = 2, .fifo_size = 512, .nr_banks = 2, },
+{ .hw_ep_num = 3, .fifo_size = 8, .nr_banks = 2, },
+{ .hw_ep_num = 4, .fifo_size = 512, .nr_banks = 2, },
+{ .hw_ep_num = 5, .fifo_size = 512, .nr_banks = 2, },
+{ .hw_ep_num = 6, .fifo_size = 16, .nr_banks = 2, },
+{ .hw_ep_num = 7, .fifo_size = 8, .nr_banks = 2, },
+{ .hw_ep_num = 8, .fifo_size = 8, .nr_banks = 2, },
+};
+/* Add additional configurations here */
+
+int usba_config_fifo_table(struct usba_udc *udc)
+{
+ int n;
+
+ switch (fifo_mode) {
+ default:
+ fifo_mode = 0;
+ case 0:
+ udc->fifo_cfg = NULL;
+ n = 0;
+ break;
+ case 1:
+ udc->fifo_cfg = mode_1_cfg;
+ n = ARRAY_SIZE(mode_1_cfg);
+ break;
+ case 2:
+ udc->fifo_cfg = mode_2_cfg;
+ n = ARRAY_SIZE(mode_2_cfg);
+ break;
+ case 3:
+ udc->fifo_cfg = mode_3_cfg;
+ n = ARRAY_SIZE(mode_3_cfg);
+ break;
+ case 4:
+ udc->fifo_cfg = mode_4_cfg;
+ n = ARRAY_SIZE(mode_4_cfg);
+ break;
+ }
+ DBG(DBG_HW, "Setup fifo_mode %d\n", fifo_mode);
+
+ return n;
+}
+
static inline u32 usba_int_enb_get(struct usba_udc *udc)
{
return udc->int_enb_cache;
@@ -524,12 +610,12 @@ usba_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
{
struct usba_ep *ep = to_usba_ep(_ep);
struct usba_udc *udc = ep->udc;
- unsigned long flags, ept_cfg, maxpacket;
+ unsigned long flags, maxpacket;
unsigned int nr_trans;
DBG(DBG_GADGET, "%s: ep_enable: desc=%p\n", ep->ep.name, desc);
- maxpacket = usb_endpoint_maxp(desc) & 0x7ff;
+ maxpacket = usb_endpoint_maxp(desc);
if (((desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK) != ep->index)
|| ep->index == 0
@@ -543,24 +629,17 @@ usba_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
ep->is_isoc = 0;
ep->is_in = 0;
- if (maxpacket <= 8)
- ept_cfg = USBA_BF(EPT_SIZE, USBA_EPT_SIZE_8);
- else
- /* LSB is bit 1, not 0 */
- ept_cfg = USBA_BF(EPT_SIZE, fls(maxpacket - 1) - 3);
-
- DBG(DBG_HW, "%s: EPT_SIZE = %lu (maxpacket = %lu)\n",
- ep->ep.name, ept_cfg, maxpacket);
+ DBG(DBG_ERR, "%s: EPT_CFG = 0x%lx (maxpacket = %lu)\n",
+ ep->ep.name, ep->ept_cfg, maxpacket);
if (usb_endpoint_dir_in(desc)) {
ep->is_in = 1;
- ept_cfg |= USBA_EPT_DIR_IN;
+ ep->ept_cfg |= USBA_EPT_DIR_IN;
}
switch (usb_endpoint_type(desc)) {
case USB_ENDPOINT_XFER_CONTROL:
- ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_CONTROL);
- ept_cfg |= USBA_BF(BK_NUMBER, USBA_BK_NUMBER_ONE);
+ ep->ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_CONTROL);
break;
case USB_ENDPOINT_XFER_ISOC:
if (!ep->can_isoc) {
@@ -573,29 +652,20 @@ usba_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
* Bits 11:12 specify number of _additional_
* transactions per microframe.
*/
- nr_trans = ((usb_endpoint_maxp(desc) >> 11) & 3) + 1;
+ nr_trans = usb_endpoint_maxp_mult(desc);
if (nr_trans > 3)
return -EINVAL;
ep->is_isoc = 1;
- ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_ISO);
+ ep->ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_ISO);
+ ep->ept_cfg |= USBA_BF(NB_TRANS, nr_trans);
- /*
- * Do triple-buffering on high-bandwidth iso endpoints.
- */
- if (nr_trans > 1 && ep->nr_banks == 3)
- ept_cfg |= USBA_BF(BK_NUMBER, USBA_BK_NUMBER_TRIPLE);
- else
- ept_cfg |= USBA_BF(BK_NUMBER, USBA_BK_NUMBER_DOUBLE);
- ept_cfg |= USBA_BF(NB_TRANS, nr_trans);
break;
case USB_ENDPOINT_XFER_BULK:
- ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_BULK);
- ept_cfg |= USBA_BF(BK_NUMBER, USBA_BK_NUMBER_DOUBLE);
+ ep->ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_BULK);
break;
case USB_ENDPOINT_XFER_INT:
- ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_INT);
- ept_cfg |= USBA_BF(BK_NUMBER, USBA_BK_NUMBER_DOUBLE);
+ ep->ept_cfg |= USBA_BF(EPT_TYPE, USBA_EPT_TYPE_INT);
break;
}
@@ -604,7 +674,7 @@ usba_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
ep->ep.desc = desc;
ep->ep.maxpacket = maxpacket;
- usba_ep_writel(ep, CFG, ept_cfg);
+ usba_ep_writel(ep, CFG, ep->ept_cfg);
usba_ep_writel(ep, CTL_ENB, USBA_EPT_ENABLE);
if (ep->can_dma) {
@@ -1006,12 +1076,81 @@ static int atmel_usba_start(struct usb_gadget *gadget,
struct usb_gadget_driver *driver);
static int atmel_usba_stop(struct usb_gadget *gadget);
+static struct usb_ep *atmel_usba_match_ep(
+ struct usb_gadget *gadget,
+ struct usb_endpoint_descriptor *desc,
+ struct usb_ss_ep_comp_descriptor *ep_comp
+)
+{
+ struct usb_ep *_ep;
+ struct usba_ep *ep;
+
+ /* Look at endpoints until an unclaimed one looks usable */
+ list_for_each_entry(_ep, &gadget->ep_list, ep_list) {
+ if (usb_gadget_ep_match_desc(gadget, _ep, desc, ep_comp))
+ goto found_ep;
+ }
+ /* Fail */
+ return NULL;
+
+found_ep:
+
+ if (fifo_mode == 0) {
+ /* Optimize hw fifo size based on ep type and other info */
+ ep = to_usba_ep(_ep);
+
+ switch (usb_endpoint_type(desc)) {
+
+ case USB_ENDPOINT_XFER_CONTROL:
+ break;
+
+ case USB_ENDPOINT_XFER_ISOC:
+ ep->fifo_size = 1024;
+ ep->nr_banks = 2;
+ break;
+
+ case USB_ENDPOINT_XFER_BULK:
+ ep->fifo_size = 512;
+ ep->nr_banks = 1;
+ break;
+
+ case USB_ENDPOINT_XFER_INT:
+ if (desc->wMaxPacketSize == 0)
+ ep->fifo_size =
+ roundup_pow_of_two(_ep->maxpacket_limit);
+ else
+ ep->fifo_size =
+ roundup_pow_of_two(le16_to_cpu(desc->wMaxPacketSize));
+ ep->nr_banks = 1;
+ break;
+ }
+
+ /* It might be a little bit late to set this */
+ usb_ep_set_maxpacket_limit(&ep->ep, ep->fifo_size);
+
+ /* Generate ept_cfg basd on FIFO size and number of banks */
+ if (ep->fifo_size <= 8)
+ ep->ept_cfg = USBA_BF(EPT_SIZE, USBA_EPT_SIZE_8);
+ else
+ /* LSB is bit 1, not 0 */
+ ep->ept_cfg =
+ USBA_BF(EPT_SIZE, fls(ep->fifo_size - 1) - 3);
+
+ ep->ept_cfg |= USBA_BF(BK_NUMBER, ep->nr_banks);
+
+ ep->udc->configured_ep++;
+ }
+
+return _ep;
+}
+
static const struct usb_gadget_ops usba_udc_ops = {
.get_frame = usba_udc_get_frame,
.wakeup = usba_udc_wakeup,
.set_selfpowered = usba_udc_set_selfpowered,
.udc_start = atmel_usba_start,
.udc_stop = atmel_usba_stop,
+ .match_ep = atmel_usba_match_ep,
};
static struct usb_endpoint_descriptor usba_ep0_desc = {
@@ -1464,8 +1603,8 @@ restart:
pkt_len = USBA_BFEXT(BYTE_COUNT, usba_ep_readl(ep, STA));
DBG(DBG_HW, "Packet length: %u\n", pkt_len);
if (pkt_len != sizeof(crq)) {
- pr_warning("udc: Invalid packet length %u "
- "(expected %zu)\n", pkt_len, sizeof(crq));
+ pr_warn("udc: Invalid packet length %u (expected %zu)\n",
+ pkt_len, sizeof(crq));
set_protocol_stall(udc, ep);
return;
}
@@ -1678,7 +1817,8 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)
}
if (status & USBA_END_OF_RESET) {
- struct usba_ep *ep0;
+ struct usba_ep *ep0, *ep;
+ int i, n;
usba_writel(udc, INT_CLR, USBA_END_OF_RESET);
generate_bias_pulse(udc);
@@ -1717,6 +1857,16 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)
if (!(usba_ep_readl(ep0, CFG) & USBA_EPT_MAPPED))
dev_dbg(&udc->pdev->dev,
"ODD: EP0 configuration is invalid!\n");
+
+ /* Preallocate other endpoints */
+ n = fifo_mode ? udc->num_ep : udc->configured_ep;
+ for (i = 1; i < n; i++) {
+ ep = &udc->usba_ep[i];
+ usba_ep_writel(ep, CFG, ep->ept_cfg);
+ if (!(usba_ep_readl(ep, CFG) & USBA_EPT_MAPPED))
+ dev_dbg(&udc->pdev->dev,
+ "ODD: EP%d configuration is invalid!\n", i);
+ }
}
spin_unlock(&udc->lock);
@@ -1864,6 +2014,9 @@ static int atmel_usba_stop(struct usb_gadget *gadget)
if (gpio_is_valid(udc->vbus_pin))
disable_irq(gpio_to_irq(udc->vbus_pin));
+ if (fifo_mode == 0)
+ udc->configured_ep = 1;
+
usba_stop(udc);
udc->driver = NULL;
@@ -1931,9 +2084,13 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
&flags);
udc->vbus_pin_inverted = (flags & OF_GPIO_ACTIVE_LOW) ? 1 : 0;
- pp = NULL;
- while ((pp = of_get_next_child(np, pp)))
- udc->num_ep++;
+ if (fifo_mode == 0) {
+ pp = NULL;
+ while ((pp = of_get_next_child(np, pp)))
+ udc->num_ep++;
+ udc->configured_ep = 1;
+ } else
+ udc->num_ep = usba_config_fifo_table(udc);
eps = devm_kzalloc(&pdev->dev, sizeof(struct usba_ep) * udc->num_ep,
GFP_KERNEL);
@@ -1946,7 +2103,7 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
pp = NULL;
i = 0;
- while ((pp = of_get_next_child(np, pp))) {
+ while ((pp = of_get_next_child(np, pp)) && i < udc->num_ep) {
ep = &eps[i];
ret = of_property_read_u32(pp, "reg", &val);
@@ -1954,21 +2111,21 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
dev_err(&pdev->dev, "of_probe: reg error(%d)\n", ret);
goto err;
}
- ep->index = val;
+ ep->index = fifo_mode ? udc->fifo_cfg[i].hw_ep_num : val;
ret = of_property_read_u32(pp, "atmel,fifo-size", &val);
if (ret) {
dev_err(&pdev->dev, "of_probe: fifo-size error(%d)\n", ret);
goto err;
}
- ep->fifo_size = val;
+ ep->fifo_size = fifo_mode ? udc->fifo_cfg[i].fifo_size : val;
ret = of_property_read_u32(pp, "atmel,nb-banks", &val);
if (ret) {
dev_err(&pdev->dev, "of_probe: nb-banks error(%d)\n", ret);
goto err;
}
- ep->nr_banks = val;
+ ep->nr_banks = fifo_mode ? udc->fifo_cfg[i].nr_banks : val;
ep->can_dma = of_property_read_bool(pp, "atmel,can-dma");
ep->can_isoc = of_property_read_bool(pp, "atmel,can-isoc");
@@ -1978,7 +2135,8 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
dev_err(&pdev->dev, "of_probe: name error(%d)\n", ret);
goto err;
}
- ep->ep.name = kasprintf(GFP_KERNEL, "ep%d", ep->index);
+ sprintf(ep->name, "ep%d", ep->index);
+ ep->ep.name = ep->name;
ep->ep_regs = udc->regs + USBA_EPT_BASE(i);
ep->dma_regs = udc->regs + USBA_DMA_BASE(i);
@@ -1999,6 +2157,21 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
ep->ep.caps.dir_in = true;
ep->ep.caps.dir_out = true;
+ if (fifo_mode != 0) {
+ /*
+ * Generate ept_cfg based on FIFO size and
+ * banks number
+ */
+ if (ep->fifo_size <= 8)
+ ep->ept_cfg = USBA_BF(EPT_SIZE, USBA_EPT_SIZE_8);
+ else
+ /* LSB is bit 1, not 0 */
+ ep->ept_cfg =
+ USBA_BF(EPT_SIZE, fls(ep->fifo_size - 1) - 3);
+
+ ep->ept_cfg |= USBA_BF(BK_NUMBER, ep->nr_banks);
+ }
+
if (i)
list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list);
diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.h b/drivers/usb/gadget/udc/atmel_usba_udc.h
index 3e1c9d589dfa..9551b704bfd3 100644
--- a/drivers/usb/gadget/udc/atmel_usba_udc.h
+++ b/drivers/usb/gadget/udc/atmel_usba_udc.h
@@ -275,11 +275,18 @@ struct usba_dma_desc {
u32 ctrl;
};
+struct usba_fifo_cfg {
+ u8 hw_ep_num;
+ u16 fifo_size;
+ u8 nr_banks;
+};
+
struct usba_ep {
int state;
void __iomem *ep_regs;
void __iomem *dma_regs;
void __iomem *fifo;
+ char name[8];
struct usb_ep ep;
struct usba_udc *udc;
@@ -292,7 +299,7 @@ struct usba_ep {
unsigned int can_isoc:1;
unsigned int is_isoc:1;
unsigned int is_in:1;
-
+ unsigned long ept_cfg;
#ifdef CONFIG_USB_GADGET_DEBUG_FS
u32 last_dma_status;
struct dentry *debugfs_dir;
@@ -337,6 +344,8 @@ struct usba_udc {
int vbus_pin;
int vbus_pin_inverted;
int num_ep;
+ int configured_ep;
+ struct usba_fifo_cfg *fifo_cfg;
struct clk *pclk;
struct clk *hclk;
struct usba_ep *usba_ep;
diff --git a/drivers/usb/gadget/udc/bdc/bdc_cmd.c b/drivers/usb/gadget/udc/bdc/bdc_cmd.c
index 4d5e9188beae..6e920f1dce02 100644
--- a/drivers/usb/gadget/udc/bdc/bdc_cmd.c
+++ b/drivers/usb/gadget/udc/bdc/bdc_cmd.c
@@ -182,7 +182,7 @@ int bdc_config_ep(struct bdc *bdc, struct bdc_ep *ep)
usb_endpoint_xfer_int(desc)) {
param2 |= si;
- mbs = (usb_endpoint_maxp(desc) & 0x1800) >> 11;
+ mbs = usb_endpoint_maxp_mult(desc);
param2 |= mbs << MB_SHIFT;
}
break;
diff --git a/drivers/usb/gadget/udc/bdc/bdc_ep.c b/drivers/usb/gadget/udc/bdc/bdc_ep.c
index ccaa74ab6c0e..ff1ef24d1777 100644
--- a/drivers/usb/gadget/udc/bdc/bdc_ep.c
+++ b/drivers/usb/gadget/udc/bdc/bdc_ep.c
@@ -446,7 +446,7 @@ static int setup_bd_list_xfr(struct bdc *bdc, struct bdc_req *req, int num_bds)
bd_xfr->start_bdi = bd_list->eqp_bdi;
bd = bdi_to_bd(ep, bd_list->eqp_bdi);
req_len = req->usb_req.length;
- maxp = usb_endpoint_maxp(ep->desc) & 0x7ff;
+ maxp = usb_endpoint_maxp(ep->desc);
tfs = roundup(req->usb_req.length, maxp);
tfs = tfs/maxp;
dev_vdbg(bdc->dev, "%s ep:%s num_bds:%d tfs:%d r_len:%d bd:%p\n",
diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c
index 9483489080f6..d685d82dcf48 100644
--- a/drivers/usb/gadget/udc/core.c
+++ b/drivers/usb/gadget/udc/core.c
@@ -1080,6 +1080,24 @@ static void usb_udc_nop_release(struct device *dev)
dev_vdbg(dev, "%s\n", __func__);
}
+/* should be called with udc_lock held */
+static int check_pending_gadget_drivers(struct usb_udc *udc)
+{
+ struct usb_gadget_driver *driver;
+ int ret = 0;
+
+ list_for_each_entry(driver, &gadget_driver_pending_list, pending)
+ if (!driver->udc_name || strcmp(driver->udc_name,
+ dev_name(&udc->dev)) == 0) {
+ ret = udc_bind_to_driver(udc, driver);
+ if (ret != -EPROBE_DEFER)
+ list_del(&driver->pending);
+ break;
+ }
+
+ return ret;
+}
+
/**
* usb_add_gadget_udc_release - adds a new gadget to the udc class driver list
* @parent: the parent device to this udc. Usually the controller driver's
@@ -1093,7 +1111,6 @@ int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget,
void (*release)(struct device *dev))
{
struct usb_udc *udc;
- struct usb_gadget_driver *driver;
int ret = -ENOMEM;
udc = kzalloc(sizeof(*udc), GFP_KERNEL);
@@ -1136,17 +1153,9 @@ int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget,
udc->vbus = true;
/* pick up one of pending gadget drivers */
- list_for_each_entry(driver, &gadget_driver_pending_list, pending) {
- if (!driver->udc_name || strcmp(driver->udc_name,
- dev_name(&udc->dev)) == 0) {
- ret = udc_bind_to_driver(udc, driver);
- if (ret != -EPROBE_DEFER)
- list_del(&driver->pending);
- if (ret)
- goto err5;
- break;
- }
- }
+ ret = check_pending_gadget_drivers(udc);
+ if (ret)
+ goto err5;
mutex_unlock(&udc_lock);
@@ -1317,7 +1326,11 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver)
if (!ret)
break;
}
- if (!ret && !udc->driver)
+ if (ret)
+ ret = -ENODEV;
+ else if (udc->driver)
+ ret = -EBUSY;
+ else
goto found;
} else {
list_for_each_entry(udc, &udc_list, list) {
@@ -1352,14 +1365,22 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
return -EINVAL;
mutex_lock(&udc_lock);
- list_for_each_entry(udc, &udc_list, list)
+ list_for_each_entry(udc, &udc_list, list) {
if (udc->driver == driver) {
usb_gadget_remove_driver(udc);
usb_gadget_set_state(udc->gadget,
- USB_STATE_NOTATTACHED);
+ USB_STATE_NOTATTACHED);
+
+ /* Maybe there is someone waiting for this UDC? */
+ check_pending_gadget_drivers(udc);
+ /*
+ * For now we ignore bind errors as probably it's
+ * not a valid reason to fail other's gadget unbind
+ */
ret = 0;
break;
}
+ }
if (ret) {
list_del(&driver->pending);
diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c
index 77d07904f932..8cabc5944d5f 100644
--- a/drivers/usb/gadget/udc/dummy_hcd.c
+++ b/drivers/usb/gadget/udc/dummy_hcd.c
@@ -330,7 +330,7 @@ static void nuke(struct dummy *dum, struct dummy_ep *ep)
/* caller must hold lock */
static void stop_activity(struct dummy *dum)
{
- struct dummy_ep *ep;
+ int i;
/* prevent any more requests */
dum->address = 0;
@@ -338,8 +338,8 @@ static void stop_activity(struct dummy *dum)
/* The timer is left running so that outstanding URBs can fail */
/* nuke any pending requests first, so driver i/o is quiesced */
- list_for_each_entry(ep, &dum->gadget.ep_list, ep.ep_list)
- nuke(dum, ep);
+ for (i = 0; i < DUMMY_ENDPOINTS; ++i)
+ nuke(dum, &dum->ep[i]);
/* driver now does any non-usb quiescing necessary */
}
@@ -503,7 +503,7 @@ static int dummy_enable(struct usb_ep *_ep,
* maximum packet size.
* For SS devices the wMaxPacketSize is limited by 1024.
*/
- max = usb_endpoint_maxp(desc) & 0x7ff;
+ max = usb_endpoint_maxp(desc);
/* drivers must not request bad settings, since lower levels
* (hardware or its drivers) may not check. some endpoints
@@ -1031,6 +1031,8 @@ static int dummy_udc_probe(struct platform_device *pdev)
int rc;
dum = *((void **)dev_get_platdata(&pdev->dev));
+ /* Clear usb_gadget region for new registration to udc-core */
+ memzero_explicit(&dum->gadget, sizeof(struct usb_gadget));
dum->gadget.name = gadget_name;
dum->gadget.ops = &dummy_ops;
dum->gadget.max_speed = USB_SPEED_SUPER;
@@ -1483,8 +1485,7 @@ static int periodic_bytes(struct dummy *dum, struct dummy_ep *ep)
int tmp;
/* high bandwidth mode */
- tmp = usb_endpoint_maxp(ep->desc);
- tmp = (tmp >> 11) & 0x03;
+ tmp = usb_endpoint_maxp_mult(ep->desc);
tmp *= 8 /* applies to entire frame */;
limit += limit * tmp;
}
diff --git a/drivers/usb/gadget/udc/fotg210-udc.c b/drivers/usb/gadget/udc/fotg210-udc.c
index 6ba122cc7490..78d0204e3e20 100644
--- a/drivers/usb/gadget/udc/fotg210-udc.c
+++ b/drivers/usb/gadget/udc/fotg210-udc.c
@@ -527,7 +527,7 @@ static void fotg210_ep_fifo_flush(struct usb_ep *_ep)
{
}
-static struct usb_ep_ops fotg210_ep_ops = {
+static const struct usb_ep_ops fotg210_ep_ops = {
.enable = fotg210_ep_enable,
.disable = fotg210_ep_disable,
@@ -1058,7 +1058,7 @@ static int fotg210_udc_stop(struct usb_gadget *g)
return 0;
}
-static struct usb_gadget_ops fotg210_gadget_ops = {
+static const struct usb_gadget_ops fotg210_gadget_ops = {
.udc_start = fotg210_udc_start,
.udc_stop = fotg210_udc_stop,
};
diff --git a/drivers/usb/gadget/udc/fsl_qe_udc.c b/drivers/usb/gadget/udc/fsl_qe_udc.c
index 4fff51b8a18e..303328ce59ee 100644
--- a/drivers/usb/gadget/udc/fsl_qe_udc.c
+++ b/drivers/usb/gadget/udc/fsl_qe_udc.c
@@ -1847,7 +1847,7 @@ out:
return status;
}
-static struct usb_ep_ops qe_ep_ops = {
+static const struct usb_ep_ops qe_ep_ops = {
.enable = qe_ep_enable,
.disable = qe_ep_disable,
diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c
index aab5221d6c2e..b76fcdb763a0 100644
--- a/drivers/usb/gadget/udc/fsl_udc_core.c
+++ b/drivers/usb/gadget/udc/fsl_udc_core.c
@@ -520,7 +520,7 @@ static void struct_ep_qh_setup(struct fsl_udc *udc, unsigned char ep_num,
/* Setup qh structure and ep register for ep0. */
static void ep0_setup(struct fsl_udc *udc)
{
- /* the intialization of an ep includes: fields in QH, Regs,
+ /* the initialization of an ep includes: fields in QH, Regs,
* fsl_ep struct */
struct_ep_qh_setup(udc, 0, USB_RECV, USB_ENDPOINT_XFER_CONTROL,
USB_MAX_CTRL_PAYLOAD, 0, 0);
@@ -585,8 +585,7 @@ static int fsl_ep_enable(struct usb_ep *_ep,
break;
case USB_ENDPOINT_XFER_ISOC:
/* Calculate transactions needed for high bandwidth iso */
- mult = (unsigned char)(1 + ((max >> 11) & 0x03));
- max = max & 0x7ff; /* bit 0~10 */
+ mult = usb_endpoint_maxp_mult(desc);
/* 3 transactions at most */
if (mult > 3)
goto en_done;
@@ -1119,7 +1118,7 @@ static void fsl_ep_fifo_flush(struct usb_ep *_ep)
} while (fsl_readl(&dr_regs->endptstatus) & bits);
}
-static struct usb_ep_ops fsl_ep_ops = {
+static const struct usb_ep_ops fsl_ep_ops = {
.enable = fsl_ep_enable,
.disable = fsl_ep_disable,
@@ -1249,6 +1248,12 @@ static const struct usb_gadget_ops fsl_gadget_ops = {
.udc_stop = fsl_udc_stop,
};
+/*
+ * Empty complete function used by this driver to fill in the req->complete
+ * field when creating a request since the complete field is mandatory.
+ */
+static void fsl_noop_complete(struct usb_ep *ep, struct usb_request *req) { }
+
/* Set protocol stall on ep0, protocol stall will automatically be cleared
on new transaction */
static void ep0stall(struct fsl_udc *udc)
@@ -1283,7 +1288,7 @@ static int ep0_prime_status(struct fsl_udc *udc, int direction)
req->req.length = 0;
req->req.status = -EINPROGRESS;
req->req.actual = 0;
- req->req.complete = NULL;
+ req->req.complete = fsl_noop_complete;
req->dtd_count = 0;
ret = usb_gadget_map_request(&ep->udc->gadget, &req->req, ep_is_in(ep));
@@ -1366,7 +1371,7 @@ static void ch9getstatus(struct fsl_udc *udc, u8 request_type, u16 value,
req->req.length = 2;
req->req.status = -EINPROGRESS;
req->req.actual = 0;
- req->req.complete = NULL;
+ req->req.complete = fsl_noop_complete;
req->dtd_count = 0;
ret = usb_gadget_map_request(&ep->udc->gadget, &req->req, ep_is_in(ep));
@@ -2344,7 +2349,7 @@ static int struct_ep_setup(struct fsl_udc *udc, unsigned char index,
}
/* Driver probe function
- * all intialization operations implemented here except enabling usb_intr reg
+ * all initialization operations implemented here except enabling usb_intr reg
* board setup should have been done in the platform code
*/
static int fsl_udc_probe(struct platform_device *pdev)
diff --git a/drivers/usb/gadget/udc/fsl_usb2_udc.h b/drivers/usb/gadget/udc/fsl_usb2_udc.h
index 84715625b2b3..e92b8408b6f6 100644
--- a/drivers/usb/gadget/udc/fsl_usb2_udc.h
+++ b/drivers/usb/gadget/udc/fsl_usb2_udc.h
@@ -554,7 +554,7 @@ static void dump_msg(const char *label, const u8 * buf, unsigned int length)
#endif
#define ERR(stuff...) pr_err("udc: " stuff)
-#define WARNING(stuff...) pr_warning("udc: " stuff)
+#define WARNING(stuff...) pr_warn("udc: " stuff)
#define INFO(stuff...) pr_info("udc: " stuff)
/*-------------------------------------------------------------------------*/
diff --git a/drivers/usb/gadget/udc/fusb300_udc.c b/drivers/usb/gadget/udc/fusb300_udc.c
index 948845c90e47..e0c1b0099265 100644
--- a/drivers/usb/gadget/udc/fusb300_udc.c
+++ b/drivers/usb/gadget/udc/fusb300_udc.c
@@ -218,7 +218,7 @@ static int config_ep(struct fusb300_ep *ep,
(info.type == USB_ENDPOINT_XFER_ISOC)) {
info.interval = desc->bInterval;
if (info.type == USB_ENDPOINT_XFER_ISOC)
- info.bw_num = ((desc->wMaxPacketSize & 0x1800) >> 11);
+ info.bw_num = usb_endpoint_maxp_mult(desc);
}
ep_fifo_setting(fusb300, info);
@@ -518,7 +518,7 @@ static void fusb300_fifo_flush(struct usb_ep *_ep)
{
}
-static struct usb_ep_ops fusb300_ep_ops = {
+static const struct usb_ep_ops fusb300_ep_ops = {
.enable = fusb300_enable,
.disable = fusb300_disable,
diff --git a/drivers/usb/gadget/udc/goku_udc.c b/drivers/usb/gadget/udc/goku_udc.c
index 5107987bd353..8433c22900dc 100644
--- a/drivers/usb/gadget/udc/goku_udc.c
+++ b/drivers/usb/gadget/udc/goku_udc.c
@@ -968,7 +968,7 @@ static void goku_fifo_flush(struct usb_ep *_ep)
command(regs, COMMAND_FIFO_CLEAR, ep->num);
}
-static struct usb_ep_ops goku_ep_ops = {
+static const struct usb_ep_ops goku_ep_ops = {
.enable = goku_ep_enable,
.disable = goku_ep_disable,
diff --git a/drivers/usb/gadget/udc/gr_udc.c b/drivers/usb/gadget/udc/gr_udc.c
index 39b7136d31d9..1f9941145746 100644
--- a/drivers/usb/gadget/udc/gr_udc.c
+++ b/drivers/usb/gadget/udc/gr_udc.c
@@ -1539,7 +1539,7 @@ static int gr_ep_enable(struct usb_ep *_ep,
* additional transactions.
*/
max = 0x7ff & usb_endpoint_maxp(desc);
- nt = 0x3 & (usb_endpoint_maxp(desc) >> 11);
+ nt = usb_endpoint_maxp_mult(desc) - 1;
buffer_size = GR_BUFFER_SIZE(epctrl);
if (nt && (mode == 0 || mode == 2)) {
dev_err(dev->dev,
@@ -1841,7 +1841,7 @@ static void gr_fifo_flush(struct usb_ep *_ep)
spin_unlock(&ep->dev->lock);
}
-static struct usb_ep_ops gr_ep_ops = {
+static const struct usb_ep_ops gr_ep_ops = {
.enable = gr_ep_enable,
.disable = gr_ep_disable,
diff --git a/drivers/usb/gadget/udc/m66592-udc.c b/drivers/usb/gadget/udc/m66592-udc.c
index 6e977dc22570..46ce7bc15f2b 100644
--- a/drivers/usb/gadget/udc/m66592-udc.c
+++ b/drivers/usb/gadget/udc/m66592-udc.c
@@ -637,7 +637,7 @@ static void init_controller(struct m66592 *m66592)
clock = M66592_XTAL48;
break;
default:
- pr_warning("m66592-udc: xtal configuration error\n");
+ pr_warn("m66592-udc: xtal configuration error\n");
clock = 0;
}
@@ -649,7 +649,7 @@ static void init_controller(struct m66592 *m66592)
irq_sense = 0;
break;
default:
- pr_warning("m66592-udc: irq trigger config error\n");
+ pr_warn("m66592-udc: irq trigger config error\n");
irq_sense = 0;
}
@@ -1436,7 +1436,7 @@ static void m66592_fifo_flush(struct usb_ep *_ep)
spin_unlock_irqrestore(&ep->m66592->lock, flags);
}
-static struct usb_ep_ops m66592_ep_ops = {
+static const struct usb_ep_ops m66592_ep_ops = {
.enable = m66592_enable,
.disable = m66592_disable,
diff --git a/drivers/usb/gadget/udc/mv_u3d_core.c b/drivers/usb/gadget/udc/mv_u3d_core.c
index b9e19a591322..d365449a295a 100644
--- a/drivers/usb/gadget/udc/mv_u3d_core.c
+++ b/drivers/usb/gadget/udc/mv_u3d_core.c
@@ -462,6 +462,12 @@ static int mv_u3d_req_to_trb(struct mv_u3d_req *req)
req->trb_head->trb_hw,
trb_num * sizeof(*trb_hw),
DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(u3d->gadget.dev.parent,
+ req->trb_head->trb_dma)) {
+ kfree(req->trb_head->trb_hw);
+ kfree(req->trb_head);
+ return -EFAULT;
+ }
req->chain = 1;
}
@@ -487,30 +493,32 @@ mv_u3d_start_queue(struct mv_u3d_ep *ep)
ret = usb_gadget_map_request(&u3d->gadget, &req->req,
mv_u3d_ep_dir(ep));
if (ret)
- return ret;
+ goto break_processing;
req->req.status = -EINPROGRESS;
req->req.actual = 0;
req->trb_count = 0;
- /* build trbs and push them to device queue */
- if (!mv_u3d_req_to_trb(req)) {
- ret = mv_u3d_queue_trb(ep, req);
- if (ret) {
- ep->processing = 0;
- return ret;
- }
- } else {
- ep->processing = 0;
+ /* build trbs */
+ ret = mv_u3d_req_to_trb(req);
+ if (ret) {
dev_err(u3d->dev, "%s, mv_u3d_req_to_trb fail\n", __func__);
- return -ENOMEM;
+ goto break_processing;
}
+ /* and push them to device queue */
+ ret = mv_u3d_queue_trb(ep, req);
+ if (ret)
+ goto break_processing;
+
/* irq handler advances the queue */
- if (req)
- list_add_tail(&req->queue, &ep->queue);
+ list_add_tail(&req->queue, &ep->queue);
return 0;
+
+break_processing:
+ ep->processing = 0;
+ return ret;
}
static int mv_u3d_ep_enable(struct usb_ep *_ep,
@@ -987,7 +995,7 @@ static int mv_u3d_ep_set_wedge(struct usb_ep *_ep)
return mv_u3d_ep_set_halt_wedge(_ep, 1, 1);
}
-static struct usb_ep_ops mv_u3d_ep_ops = {
+static const struct usb_ep_ops mv_u3d_ep_ops = {
.enable = mv_u3d_ep_enable,
.disable = mv_u3d_ep_disable,
diff --git a/drivers/usb/gadget/udc/mv_udc_core.c b/drivers/usb/gadget/udc/mv_udc_core.c
index ce73b3552269..27ebb0d5449d 100644
--- a/drivers/usb/gadget/udc/mv_udc_core.c
+++ b/drivers/usb/gadget/udc/mv_udc_core.c
@@ -494,8 +494,7 @@ static int mv_ep_enable(struct usb_ep *_ep,
break;
case USB_ENDPOINT_XFER_ISOC:
/* Calculate transactions needed for high bandwidth iso */
- mult = (unsigned char)(1 + ((max >> 11) & 0x03));
- max = max & 0x7ff; /* bit 0~10 */
+ mult = usb_endpoint_maxp_mult(desc);
/* 3 transactions at most */
if (mult > 3)
goto en_done;
@@ -947,7 +946,7 @@ static int mv_ep_set_wedge(struct usb_ep *_ep)
return mv_ep_set_halt_wedge(_ep, 1, 1);
}
-static struct usb_ep_ops mv_ep_ops = {
+static const struct usb_ep_ops mv_ep_ops = {
.enable = mv_ep_enable,
.disable = mv_ep_disable,
diff --git a/drivers/usb/gadget/udc/net2272.c b/drivers/usb/gadget/udc/net2272.c
index 7c6113432093..7dc0102abdfe 100644
--- a/drivers/usb/gadget/udc/net2272.c
+++ b/drivers/usb/gadget/udc/net2272.c
@@ -181,7 +181,7 @@ static void net2272_dequeue_all(struct net2272_ep *);
static int net2272_kick_dma(struct net2272_ep *, struct net2272_request *);
static int net2272_fifo_status(struct usb_ep *);
-static struct usb_ep_ops net2272_ep_ops;
+static const struct usb_ep_ops net2272_ep_ops;
/*---------------------------------------------------------------------------*/
@@ -202,10 +202,10 @@ net2272_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
return -ESHUTDOWN;
- max = usb_endpoint_maxp(desc) & 0x1fff;
+ max = usb_endpoint_maxp(desc);
spin_lock_irqsave(&dev->lock, flags);
- _ep->maxpacket = max & 0x7fff;
+ _ep->maxpacket = max;
ep->desc = desc;
/* net2272_ep_reset() has already been called */
@@ -1067,7 +1067,7 @@ net2272_fifo_flush(struct usb_ep *_ep)
net2272_ep_write(ep, EP_STAT1, 1 << BUFFER_FLUSH);
}
-static struct usb_ep_ops net2272_ep_ops = {
+static const struct usb_ep_ops net2272_ep_ops = {
.enable = net2272_enable,
.disable = net2272_disable,
diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c
index 61c938c36d88..3828c2ec8623 100644
--- a/drivers/usb/gadget/udc/net2280.c
+++ b/drivers/usb/gadget/udc/net2280.c
@@ -224,14 +224,14 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
}
/* sanity check ep-e/ep-f since their fifos are small */
- max = usb_endpoint_maxp(desc) & 0x1fff;
+ max = usb_endpoint_maxp(desc);
if (ep->num > 4 && max > 64 && (dev->quirks & PLX_LEGACY)) {
ret = -ERANGE;
goto print_err;
}
spin_lock_irqsave(&dev->lock, flags);
- _ep->maxpacket = max & 0x7ff;
+ _ep->maxpacket = max;
ep->desc = desc;
/* ep_reset() has already been called */
@@ -1146,15 +1146,15 @@ static int scan_dma_completions(struct net2280_ep *ep)
*/
while (!list_empty(&ep->queue)) {
struct net2280_request *req;
- u32 tmp;
+ u32 req_dma_count;
req = list_entry(ep->queue.next,
struct net2280_request, queue);
if (!req->valid)
break;
rmb();
- tmp = le32_to_cpup(&req->td->dmacount);
- if ((tmp & BIT(VALID_BIT)) != 0)
+ req_dma_count = le32_to_cpup(&req->td->dmacount);
+ if ((req_dma_count & BIT(VALID_BIT)) != 0)
break;
/* SHORT_PACKET_TRANSFERRED_INTERRUPT handles "usb-short"
@@ -1163,40 +1163,41 @@ static int scan_dma_completions(struct net2280_ep *ep)
*/
if (unlikely(req->td->dmadesc == 0)) {
/* paranoia */
- tmp = readl(&ep->dma->dmacount);
- if (tmp & DMA_BYTE_COUNT_MASK)
+ u32 const ep_dmacount = readl(&ep->dma->dmacount);
+
+ if (ep_dmacount & DMA_BYTE_COUNT_MASK)
break;
/* single transfer mode */
- dma_done(ep, req, tmp, 0);
+ dma_done(ep, req, req_dma_count, 0);
num_completed++;
break;
} else if (!ep->is_in &&
(req->req.length % ep->ep.maxpacket) &&
!(ep->dev->quirks & PLX_PCIE)) {
- tmp = readl(&ep->regs->ep_stat);
+ u32 const ep_stat = readl(&ep->regs->ep_stat);
/* AVOID TROUBLE HERE by not issuing short reads from
* your gadget driver. That helps avoids errata 0121,
* 0122, and 0124; not all cases trigger the warning.
*/
- if ((tmp & BIT(NAK_OUT_PACKETS)) == 0) {
+ if ((ep_stat & BIT(NAK_OUT_PACKETS)) == 0) {
ep_warn(ep->dev, "%s lost packet sync!\n",
ep->ep.name);
req->req.status = -EOVERFLOW;
} else {
- tmp = readl(&ep->regs->ep_avail);
- if (tmp) {
+ u32 const ep_avail = readl(&ep->regs->ep_avail);
+ if (ep_avail) {
/* fifo gets flushed later */
ep->out_overflow = 1;
ep_dbg(ep->dev,
"%s dma, discard %d len %d\n",
- ep->ep.name, tmp,
+ ep->ep.name, ep_avail,
req->req.length);
req->req.status = -EOVERFLOW;
}
}
}
- dma_done(ep, req, tmp, 0);
+ dma_done(ep, req, req_dma_count, 0);
num_completed++;
}
@@ -1839,7 +1840,7 @@ static ssize_t queues_show(struct device *_dev, struct device_attribute *attr,
ep->ep.name, t & USB_ENDPOINT_NUMBER_MASK,
(t & USB_DIR_IN) ? "in" : "out",
type_string(d->bmAttributes),
- usb_endpoint_maxp(d) & 0x1fff,
+ usb_endpoint_maxp(d),
ep->dma ? "dma" : "pio", ep->fifo_size
);
} else /* ep0 should only have one transfer queued */
diff --git a/drivers/usb/gadget/udc/omap_udc.c b/drivers/usb/gadget/udc/omap_udc.c
index a8709f9e5648..f05ba6825bfe 100644
--- a/drivers/usb/gadget/udc/omap_udc.c
+++ b/drivers/usb/gadget/udc/omap_udc.c
@@ -1112,7 +1112,7 @@ done:
return status;
}
-static struct usb_ep_ops omap_ep_ops = {
+static const struct usb_ep_ops omap_ep_ops = {
.enable = omap_ep_enable,
.disable = omap_ep_disable,
diff --git a/drivers/usb/gadget/udc/omap_udc.h b/drivers/usb/gadget/udc/omap_udc.h
index cfadeb5fc5de..26974196cf44 100644
--- a/drivers/usb/gadget/udc/omap_udc.h
+++ b/drivers/usb/gadget/udc/omap_udc.h
@@ -187,7 +187,7 @@ struct omap_udc {
#endif
#define ERR(stuff...) pr_err("udc: " stuff)
-#define WARNING(stuff...) pr_warning("udc: " stuff)
+#define WARNING(stuff...) pr_warn("udc: " stuff)
#define INFO(stuff...) pr_info("udc: " stuff)
#define DBG(stuff...) pr_debug("udc: " stuff)
diff --git a/drivers/usb/gadget/udc/pch_udc.c b/drivers/usb/gadget/udc/pch_udc.c
index a97da645c1b9..8a365aad66fe 100644
--- a/drivers/usb/gadget/udc/pch_udc.c
+++ b/drivers/usb/gadget/udc/pch_udc.c
@@ -1523,7 +1523,6 @@ static void pch_udc_free_dma_chain(struct pch_udc_dev *dev,
td = phys_to_virt(addr);
addr2 = (dma_addr_t)td->next;
pci_pool_free(dev->data_requests, td, addr);
- td->next = 0x00;
addr = addr2;
}
req->chain_len = 1;
diff --git a/drivers/usb/gadget/udc/pxa25x_udc.h b/drivers/usb/gadget/udc/pxa25x_udc.h
index 4b8b72d7ab37..a458bec2536d 100644
--- a/drivers/usb/gadget/udc/pxa25x_udc.h
+++ b/drivers/usb/gadget/udc/pxa25x_udc.h
@@ -248,7 +248,7 @@ dump_state(struct pxa25x_udc *dev)
#define DBG(lvl, stuff...) do{if ((lvl) <= UDC_DEBUG) DMSG(stuff);}while(0)
#define ERR(stuff...) pr_err("udc: " stuff)
-#define WARNING(stuff...) pr_warning("udc: " stuff)
+#define WARNING(stuff...) pr_warn("udc: " stuff)
#define INFO(stuff...) pr_info("udc: " stuff)
diff --git a/drivers/usb/gadget/udc/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c
index 7fa60f5b7ae4..832c4fdbe985 100644
--- a/drivers/usb/gadget/udc/pxa27x_udc.c
+++ b/drivers/usb/gadget/udc/pxa27x_udc.c
@@ -1473,7 +1473,7 @@ static int pxa_ep_disable(struct usb_ep *_ep)
return 0;
}
-static struct usb_ep_ops pxa_ep_ops = {
+static const struct usb_ep_ops pxa_ep_ops = {
.enable = pxa_ep_enable,
.disable = pxa_ep_disable,
@@ -2534,9 +2534,10 @@ static int pxa_udc_remove(struct platform_device *_dev)
usb_del_gadget_udc(&udc->gadget);
pxa_cleanup_debugfs(udc);
- if (!IS_ERR_OR_NULL(udc->transceiver))
+ if (!IS_ERR_OR_NULL(udc->transceiver)) {
usb_unregister_notifier(udc->transceiver, &pxa27x_udc_phy);
- usb_put_phy(udc->transceiver);
+ usb_put_phy(udc->transceiver);
+ }
udc->transceiver = NULL;
the_controller = NULL;
diff --git a/drivers/usb/gadget/udc/r8a66597-udc.c b/drivers/usb/gadget/udc/r8a66597-udc.c
index f2c8862093a2..118ad70f1af0 100644
--- a/drivers/usb/gadget/udc/r8a66597-udc.c
+++ b/drivers/usb/gadget/udc/r8a66597-udc.c
@@ -1706,7 +1706,7 @@ static void r8a66597_fifo_flush(struct usb_ep *_ep)
spin_unlock_irqrestore(&ep->r8a66597->lock, flags);
}
-static struct usb_ep_ops r8a66597_ep_ops = {
+static const struct usb_ep_ops r8a66597_ep_ops = {
.enable = r8a66597_enable,
.disable = r8a66597_disable,
diff --git a/drivers/usb/gadget/udc/renesas_usb3.c b/drivers/usb/gadget/udc/renesas_usb3.c
index fb8fc34827ab..2218f91e92a6 100644
--- a/drivers/usb/gadget/udc/renesas_usb3.c
+++ b/drivers/usb/gadget/udc/renesas_usb3.c
@@ -1791,7 +1791,7 @@ static int renesas_usb3_init_ep(struct renesas_usb3 *usb3, struct device *dev,
dev_dbg(dev, "%s: num_usb3_eps = %d\n", __func__, usb3->num_usb3_eps);
/*
- * This driver prepares pipes as the followings:
+ * This driver prepares pipes as follows:
* - odd pipes = IN pipe
* - even pipes = OUT pipe (except pipe 0)
*/
@@ -1841,7 +1841,7 @@ static void renesas_usb3_init_ram(struct renesas_usb3 *usb3, struct device *dev,
memset(basead, 0, sizeof(basead));
/*
- * This driver prepares pipes as the followings:
+ * This driver prepares pipes as follows:
* - all pipes = the same size as "ramsize_per_pipe"
* Please refer to the "Method of Specifying RAM Mapping"
*/
diff --git a/drivers/usb/gadget/udc/s3c-hsudc.c b/drivers/usb/gadget/udc/s3c-hsudc.c
index 82a9e2a3bedc..42587b738a1f 100644
--- a/drivers/usb/gadget/udc/s3c-hsudc.c
+++ b/drivers/usb/gadget/udc/s3c-hsudc.c
@@ -954,7 +954,7 @@ static int s3c_hsudc_dequeue(struct usb_ep *_ep, struct usb_request *_req)
return 0;
}
-static struct usb_ep_ops s3c_hsudc_ep_ops = {
+static const struct usb_ep_ops s3c_hsudc_ep_ops = {
.enable = s3c_hsudc_ep_enable,
.disable = s3c_hsudc_ep_disable,
.alloc_request = s3c_hsudc_alloc_request,
diff --git a/drivers/usb/gadget/udc/s3c2410_udc.c b/drivers/usb/gadget/udc/s3c2410_udc.c
index eb3571ee59e3..4643a01262b4 100644
--- a/drivers/usb/gadget/udc/s3c2410_udc.c
+++ b/drivers/usb/gadget/udc/s3c2410_udc.c
@@ -1047,10 +1047,10 @@ static int s3c2410_udc_ep_enable(struct usb_ep *_ep,
if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
return -ESHUTDOWN;
- max = usb_endpoint_maxp(desc) & 0x1fff;
+ max = usb_endpoint_maxp(desc);
local_irq_save(flags);
- _ep->maxpacket = max & 0x7ff;
+ _ep->maxpacket = max;
ep->ep.desc = desc;
ep->halted = 0;
ep->bEndpointAddress = desc->bEndpointAddress;
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 0b80cee30da4..407d947b34ea 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -45,9 +45,9 @@ config USB_XHCI_PLATFORM
If unsure, say N.
config USB_XHCI_MTK
- tristate "xHCI support for Mediatek MT65xx"
+ tristate "xHCI support for Mediatek MT65xx/MT7621"
select MFD_SYSCON
- depends on ARCH_MEDIATEK || COMPILE_TEST
+ depends on (MIPS && SOC_MT7621) || ARCH_MEDIATEK || COMPILE_TEST
---help---
Say 'Y' to enable the support for the xHCI host controller
found in Mediatek MT65xx SoCs.
@@ -479,9 +479,10 @@ config USB_OHCI_HCD_OMAP3
OMAP3 and later chips.
config USB_OHCI_HCD_DAVINCI
- bool "OHCI support for TI DaVinci DA8xx"
+ tristate "OHCI support for TI DaVinci DA8xx"
depends on ARCH_DAVINCI_DA8XX
- depends on USB_OHCI_HCD=y
+ depends on USB_OHCI_HCD
+ select PHY_DA8XX_USB
default y
help
Enables support for the DaVinci DA8xx integrated OHCI
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index 6ef785b0ea8f..2644537b7bcf 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -61,6 +61,7 @@ obj-$(CONFIG_USB_OHCI_HCD_AT91) += ohci-at91.o
obj-$(CONFIG_USB_OHCI_HCD_S3C2410) += ohci-s3c2410.o
obj-$(CONFIG_USB_OHCI_HCD_LPC32XX) += ohci-nxp.o
obj-$(CONFIG_USB_OHCI_HCD_PXA27X) += ohci-pxa27x.o
+obj-$(CONFIG_USB_OHCI_HCD_DAVINCI) += ohci-da8xx.o
obj-$(CONFIG_USB_UHCI_HCD) += uhci-hcd.o
obj-$(CONFIG_USB_FHCI_HCD) += fhci.o
diff --git a/drivers/usb/host/ehci-exynos.c b/drivers/usb/host/ehci-exynos.c
index 42e5b66353ef..7a603f66a9bc 100644
--- a/drivers/usb/host/ehci-exynos.c
+++ b/drivers/usb/host/ehci-exynos.c
@@ -77,10 +77,12 @@ static int exynos_ehci_get_phy(struct device *dev,
if (IS_ERR(phy)) {
ret = PTR_ERR(phy);
if (ret == -EPROBE_DEFER) {
+ of_node_put(child);
return ret;
} else if (ret != -ENOSYS && ret != -ENODEV) {
dev_err(dev,
"Error retrieving usb2 phy: %d\n", ret);
+ of_node_put(child);
return ret;
}
}
diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c
index 9f5ffb629973..3733aab46efe 100644
--- a/drivers/usb/host/ehci-fsl.c
+++ b/drivers/usb/host/ehci-fsl.c
@@ -286,6 +286,9 @@ static int ehci_fsl_usb_setup(struct ehci_hcd *ehci)
if (pdata->has_fsl_erratum_a005275 == 1)
ehci->has_fsl_hs_errata = 1;
+ if (pdata->has_fsl_erratum_a005697 == 1)
+ ehci->has_fsl_susp_errata = 1;
+
if ((pdata->operating_mode == FSL_USB2_DR_HOST) ||
(pdata->operating_mode == FSL_USB2_DR_OTG))
if (ehci_fsl_setup_phy(hcd, pdata->phy_mode, 0))
@@ -597,7 +600,7 @@ static int ehci_fsl_drv_restore(struct device *dev)
return 0;
}
-static struct dev_pm_ops ehci_fsl_pm_ops = {
+static const struct dev_pm_ops ehci_fsl_pm_ops = {
.suspend = ehci_fsl_drv_suspend,
.resume = ehci_fsl_drv_resume,
.restore = ehci_fsl_drv_restore,
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 063064801ceb..ac2c4eab478d 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -1322,7 +1322,7 @@ static int __init ehci_hcd_init(void)
printk(KERN_WARNING "Warning! ehci_hcd should always be loaded"
" before uhci_hcd and ohci_hcd, not after\n");
- pr_debug("%s: block sizes: qh %Zd qtd %Zd itd %Zd sitd %Zd\n",
+ pr_debug("%s: block sizes: qh %zd qtd %zd itd %zd sitd %zd\n",
hcd_name,
sizeof(struct ehci_qh), sizeof(struct ehci_qtd),
sizeof(struct ehci_itd), sizeof(struct ehci_sitd));
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index 74f62d68f013..df169c8e7225 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -310,6 +310,14 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
}
spin_unlock_irq(&ehci->lock);
+ if (changed && ehci_has_fsl_susp_errata(ehci))
+ /*
+ * Wait for at least 10 millisecondes to ensure the controller
+ * enter the suspend status before initiating a port resume
+ * using the Force Port Resume bit (Not-EHCI compatible).
+ */
+ usleep_range(10000, 20000);
+
if ((changed && ehci->has_tdi_phy_lpm) || fs_idle_delay) {
/*
* Wait for HCD to enter low-power mode or for the bus
@@ -1200,6 +1208,12 @@ int ehci_hub_control(
wIndex, (temp1 & HOSTPC_PHCD) ?
"succeeded" : "failed");
}
+ if (ehci_has_fsl_susp_errata(ehci)) {
+ /* 10ms for HCD enter suspend */
+ spin_unlock_irqrestore(&ehci->lock, flags);
+ usleep_range(10000, 20000);
+ spin_lock_irqsave(&ehci->lock, flags);
+ }
set_bit(wIndex, &ehci->suspended_ports);
break;
case USB_PORT_FEAT_POWER:
diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c
index 3b3649d88c5f..93326974ff4b 100644
--- a/drivers/usb/host/ehci-pci.c
+++ b/drivers/usb/host/ehci-pci.c
@@ -258,9 +258,8 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
/* These workarounds need to be applied after ehci_setup() */
switch (pdev->vendor) {
case PCI_VENDOR_ID_NEC:
- ehci->need_io_watchdog = 0;
- break;
case PCI_VENDOR_ID_INTEL:
+ case PCI_VENDOR_ID_AMD:
ehci->need_io_watchdog = 0;
break;
case PCI_VENDOR_ID_NVIDIA:
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
index eca3710d8fc4..8f3f055c05fa 100644
--- a/drivers/usb/host/ehci-q.c
+++ b/drivers/usb/host/ehci-q.c
@@ -550,11 +550,6 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
/*-------------------------------------------------------------------------*/
-// high bandwidth multiplier, as encoded in highspeed endpoint descriptors
-#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03))
-// ... and packet size, for any kind of endpoint descriptor
-#define max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x07ff)
-
/*
* reverse of qh_urb_transaction: free a list of TDs.
* used for cleanup after errors, before HC sees an URB's TDs.
@@ -651,7 +646,7 @@ qh_urb_transaction (
token |= (1 /* "in" */ << 8);
/* else it's already initted to "out" pid (0 << 8) */
- maxpacket = max_packet(usb_maxpacket(urb->dev, urb->pipe, !is_input));
+ maxpacket = usb_maxpacket(urb->dev, urb->pipe, !is_input);
/*
* buffer gets wrapped in one or more qtds;
@@ -770,9 +765,11 @@ qh_make (
gfp_t flags
) {
struct ehci_qh *qh = ehci_qh_alloc (ehci, flags);
+ struct usb_host_endpoint *ep;
u32 info1 = 0, info2 = 0;
int is_input, type;
int maxp = 0;
+ int mult;
struct usb_tt *tt = urb->dev->tt;
struct ehci_qh_hw *hw;
@@ -787,13 +784,15 @@ qh_make (
is_input = usb_pipein (urb->pipe);
type = usb_pipetype (urb->pipe);
- maxp = usb_maxpacket (urb->dev, urb->pipe, !is_input);
+ ep = usb_pipe_endpoint (urb->dev, urb->pipe);
+ maxp = usb_endpoint_maxp (&ep->desc);
+ mult = usb_endpoint_maxp_mult (&ep->desc);
/* 1024 byte maxpacket is a hardware ceiling. High bandwidth
* acts like up to 3KB, but is built from smaller packets.
*/
- if (max_packet(maxp) > 1024) {
- ehci_dbg(ehci, "bogus qh maxpacket %d\n", max_packet(maxp));
+ if (maxp > 1024) {
+ ehci_dbg(ehci, "bogus qh maxpacket %d\n", maxp);
goto done;
}
@@ -809,8 +808,7 @@ qh_make (
unsigned tmp;
qh->ps.usecs = NS_TO_US(usb_calc_bus_time(USB_SPEED_HIGH,
- is_input, 0,
- hb_mult(maxp) * max_packet(maxp)));
+ is_input, 0, mult * maxp));
qh->ps.phase = NO_FRAME;
if (urb->dev->speed == USB_SPEED_HIGH) {
@@ -854,7 +852,7 @@ qh_make (
think_time = tt ? tt->think_time : 0;
qh->ps.tt_usecs = NS_TO_US(think_time +
usb_calc_bus_time (urb->dev->speed,
- is_input, 0, max_packet (maxp)));
+ is_input, 0, maxp));
if (urb->interval > ehci->periodic_size)
urb->interval = ehci->periodic_size;
qh->ps.period = urb->interval;
@@ -925,11 +923,11 @@ qh_make (
* to help them do so. So now people expect to use
* such nonconformant devices with Linux too; sigh.
*/
- info1 |= max_packet(maxp) << 16;
+ info1 |= maxp << 16;
info2 |= (EHCI_TUNE_MULT_HS << 30);
} else { /* PIPE_INTERRUPT */
- info1 |= max_packet (maxp) << 16;
- info2 |= hb_mult (maxp) << 30;
+ info1 |= maxp << 16;
+ info2 |= mult << 30;
}
break;
default:
@@ -1221,7 +1219,7 @@ static int submit_single_step_set_feature(
token |= (1 /* "in" */ << 8); /*This is IN stage*/
- maxpacket = max_packet(usb_maxpacket(urb->dev, urb->pipe, 0));
+ maxpacket = usb_maxpacket(urb->dev, urb->pipe, 0);
qtd_fill(ehci, qtd, buf, len, token, maxpacket);
diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c
index 1dfe54f14737..980a6b3b2da2 100644
--- a/drivers/usb/host/ehci-sched.c
+++ b/drivers/usb/host/ehci-sched.c
@@ -1064,11 +1064,10 @@ iso_stream_init(
/* knows about ITD vs SITD */
if (dev->speed == USB_SPEED_HIGH) {
- unsigned multi = hb_mult(maxp);
+ unsigned multi = usb_endpoint_maxp_mult(&urb->ep->desc);
stream->highspeed = 1;
- maxp = max_packet(maxp);
buf1 |= maxp;
maxp *= multi;
diff --git a/drivers/usb/host/ehci-timer.c b/drivers/usb/host/ehci-timer.c
index 69f50e6533a6..3893b5bafd87 100644
--- a/drivers/usb/host/ehci-timer.c
+++ b/drivers/usb/host/ehci-timer.c
@@ -88,8 +88,7 @@ static void ehci_enable_event(struct ehci_hcd *ehci, unsigned event,
ktime_t *timeout = &ehci->hr_timeouts[event];
if (resched)
- *timeout = ktime_add(ktime_get(),
- ktime_set(0, event_delays_ns[event]));
+ *timeout = ktime_add(ktime_get(), event_delays_ns[event]);
ehci->enabled_hrtimer_events |= (1 << event);
/* Track only the lowest-numbered pending event */
@@ -425,7 +424,7 @@ static enum hrtimer_restart ehci_hrtimer_func(struct hrtimer *t)
*/
now = ktime_get();
for_each_set_bit(e, &events, EHCI_HRTIMER_NUM_EVENTS) {
- if (now.tv64 >= ehci->hr_timeouts[e].tv64)
+ if (now >= ehci->hr_timeouts[e])
event_handlers[e](ehci);
else
ehci_enable_event(ehci, e, false);
diff --git a/drivers/usb/host/ehci-w90x900.c b/drivers/usb/host/ehci-w90x900.c
index e42a29e8e229..63b9d0c67963 100644
--- a/drivers/usb/host/ehci-w90x900.c
+++ b/drivers/usb/host/ehci-w90x900.c
@@ -33,8 +33,7 @@ static const char hcd_name[] = "ehci-w90x900 ";
static struct hc_driver __read_mostly ehci_w90x900_hc_driver;
-static int usb_w90x900_probe(const struct hc_driver *driver,
- struct platform_device *pdev)
+static int ehci_w90x900_probe(struct platform_device *pdev)
{
struct usb_hcd *hcd;
struct ehci_hcd *ehci;
@@ -42,7 +41,8 @@ static int usb_w90x900_probe(const struct hc_driver *driver,
int retval = 0, irq;
unsigned long val;
- hcd = usb_create_hcd(driver, &pdev->dev, "w90x900 EHCI");
+ hcd = usb_create_hcd(&ehci_w90x900_hc_driver,
+ &pdev->dev, "w90x900 EHCI");
if (!hcd) {
retval = -ENOMEM;
goto err1;
@@ -63,9 +63,9 @@ static int usb_w90x900_probe(const struct hc_driver *driver,
HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase));
/* enable PHY 0,1,the regs only apply to w90p910
- * 0xA4,0xA8 were offsets of PHY0 and PHY1 controller of
- * w90p910 IC relative to ehci->regs.
- */
+ * 0xA4,0xA8 were offsets of PHY0 and PHY1 controller of
+ * w90p910 IC relative to ehci->regs.
+ */
val = __raw_readl(ehci->regs+PHY0_CTR);
val |= ENPHY;
__raw_writel(val, ehci->regs+PHY0_CTR);
@@ -92,26 +92,12 @@ err1:
return retval;
}
-static void usb_w90x900_remove(struct usb_hcd *hcd,
- struct platform_device *pdev)
-{
- usb_remove_hcd(hcd);
- usb_put_hcd(hcd);
-}
-
-static int ehci_w90x900_probe(struct platform_device *pdev)
-{
- if (usb_disabled())
- return -ENODEV;
-
- return usb_w90x900_probe(&ehci_w90x900_hc_driver, pdev);
-}
-
static int ehci_w90x900_remove(struct platform_device *pdev)
{
struct usb_hcd *hcd = platform_get_drvdata(pdev);
- usb_w90x900_remove(hcd, pdev);
+ usb_remove_hcd(hcd);
+ usb_put_hcd(hcd);
return 0;
}
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index 3f3b74aeca97..a8e36170d8b8 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -219,6 +219,7 @@ struct ehci_hcd { /* one per controller */
unsigned no_selective_suspend:1;
unsigned has_fsl_port_bug:1; /* FreeScale */
unsigned has_fsl_hs_errata:1; /* Freescale HS quirk */
+ unsigned has_fsl_susp_errata:1; /* NXP SUSP quirk */
unsigned big_endian_mmio:1;
unsigned big_endian_desc:1;
unsigned big_endian_capbase:1;
@@ -710,6 +711,13 @@ ehci_port_speed(struct ehci_hcd *ehci, unsigned int portsc)
#endif
/*
+ * Some Freescale/NXP processors have an erratum (USB A-005697)
+ * in which we need to wait for 10ms for bus to enter suspend mode
+ * after setting SUSP bit.
+ */
+#define ehci_has_fsl_susp_errata(e) ((e)->has_fsl_susp_errata)
+
+/*
* While most USB host controllers implement their registers in
* little-endian format, a minority (celleb companion chip) implement
* them in big endian format.
diff --git a/drivers/usb/host/fotg210-hcd.c b/drivers/usb/host/fotg210-hcd.c
index 66efa9a67687..1c5b34b74860 100644
--- a/drivers/usb/host/fotg210-hcd.c
+++ b/drivers/usb/host/fotg210-hcd.c
@@ -1080,8 +1080,7 @@ static void fotg210_enable_event(struct fotg210_hcd *fotg210, unsigned event,
ktime_t *timeout = &fotg210->hr_timeouts[event];
if (resched)
- *timeout = ktime_add(ktime_get(),
- ktime_set(0, event_delays_ns[event]));
+ *timeout = ktime_add(ktime_get(), event_delays_ns[event]);
fotg210->enabled_hrtimer_events |= (1 << event);
/* Track only the lowest-numbered pending event */
@@ -1381,7 +1380,7 @@ static enum hrtimer_restart fotg210_hrtimer_func(struct hrtimer *t)
*/
now = ktime_get();
for_each_set_bit(e, &events, FOTG210_HRTIMER_NUM_EVENTS) {
- if (now.tv64 >= fotg210->hr_timeouts[e].tv64)
+ if (now >= fotg210->hr_timeouts[e])
event_handlers[e](fotg210);
else
fotg210_enable_event(fotg210, e, false);
@@ -5698,7 +5697,7 @@ static int __init fotg210_hcd_init(void)
test_bit(USB_OHCI_LOADED, &usb_hcds_loaded))
pr_warn("Warning! fotg210_hcd should always be loaded before uhci_hcd and ohci_hcd, not after\n");
- pr_debug("%s: block sizes: qh %Zd qtd %Zd itd %Zd\n",
+ pr_debug("%s: block sizes: qh %zd qtd %zd itd %zd\n",
hcd_name, sizeof(struct fotg210_qh),
sizeof(struct fotg210_qtd),
sizeof(struct fotg210_itd));
diff --git a/drivers/usb/host/fsl-mph-dr-of.c b/drivers/usb/host/fsl-mph-dr-of.c
index f07ccb25bc24..e90ddb530765 100644
--- a/drivers/usb/host/fsl-mph-dr-of.c
+++ b/drivers/usb/host/fsl-mph-dr-of.c
@@ -226,6 +226,8 @@ static int fsl_usb2_mph_dr_of_probe(struct platform_device *ofdev)
of_property_read_bool(np, "fsl,usb-erratum-a007792");
pdata->has_fsl_erratum_a005275 =
of_property_read_bool(np, "fsl,usb-erratum-a005275");
+ pdata->has_fsl_erratum_a005697 =
+ of_property_read_bool(np, "fsl,usb_erratum-a005697");
/*
* Determine whether phy_clk_valid needs to be checked
diff --git a/drivers/usb/host/isp1362-hcd.c b/drivers/usb/host/isp1362-hcd.c
index 6cf82ee460a6..0f2b4b358e1a 100644
--- a/drivers/usb/host/isp1362-hcd.c
+++ b/drivers/usb/host/isp1362-hcd.c
@@ -147,7 +147,7 @@ static inline struct isp1362_ep_queue *get_ptd_queue(struct isp1362_hcd *isp1362
if (epq)
DBG(1, "%s: PTD $%04x is on %s queue\n", __func__, offset, epq->name);
else
- pr_warning("%s: invalid PTD $%04x\n", __func__, offset);
+ pr_warn("%s: invalid PTD $%04x\n", __func__, offset);
return epq;
}
@@ -157,8 +157,9 @@ static inline int get_ptd_offset(struct isp1362_ep_queue *epq, u8 index)
int offset;
if (index * epq->blk_size > epq->buf_size) {
- pr_warning("%s: Bad %s index %d(%d)\n", __func__, epq->name, index,
- epq->buf_size / epq->blk_size);
+ pr_warn("%s: Bad %s index %d(%d)\n",
+ __func__, epq->name, index,
+ epq->buf_size / epq->blk_size);
return -EINVAL;
}
offset = epq->buf_start + index * epq->blk_size;
@@ -902,8 +903,8 @@ static void start_iso_transfers(struct isp1362_hcd *isp1362_hcd)
ptd_offset = next_ptd(epq, ep);
if (ptd_offset < 0) {
- pr_warning("%s: req %d No more %s PTD buffers available\n", __func__,
- ep->num_req, epq->name);
+ pr_warn("%s: req %d No more %s PTD buffers available\n",
+ __func__, ep->num_req, epq->name);
break;
}
}
@@ -973,8 +974,8 @@ static void finish_transfers(struct isp1362_hcd *isp1362_hcd, unsigned long done
break;
}
if (done_map)
- pr_warning("%s: done_map not clear: %08lx:%08lx\n", __func__, done_map,
- epq->skip_map);
+ pr_warn("%s: done_map not clear: %08lx:%08lx\n",
+ __func__, done_map, epq->skip_map);
atomic_dec(&epq->finishing);
}
@@ -1433,7 +1434,7 @@ static int isp1362_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
} else
DBG(1, "%s: urb %p active; wait4irq\n", __func__, urb);
} else {
- pr_warning("%s: No EP in URB %p\n", __func__, urb);
+ pr_warn("%s: No EP in URB %p\n", __func__, urb);
retval = -EINVAL;
}
done:
@@ -1748,10 +1749,10 @@ static int isp1362_bus_suspend(struct usb_hcd *hcd)
/* FALL THROUGH */
case OHCI_USB_RESET:
status = -EBUSY;
- pr_warning("%s: needs reinit!\n", __func__);
+ pr_warn("%s: needs reinit!\n", __func__);
goto done;
case OHCI_USB_SUSPEND:
- pr_warning("%s: already suspended?\n", __func__);
+ pr_warn("%s: already suspended?\n", __func__);
goto done;
}
DBG(0, "%s: suspend root hub\n", __func__);
@@ -1839,7 +1840,7 @@ static int isp1362_bus_resume(struct usb_hcd *hcd)
isp1362_hcd->hc_control = isp1362_read_reg32(isp1362_hcd, HCCONTROL);
pr_info("%s: HCCONTROL: %08x\n", __func__, isp1362_hcd->hc_control);
if (hcd->state == HC_STATE_RESUMING) {
- pr_warning("%s: duplicate resume\n", __func__);
+ pr_warn("%s: duplicate resume\n", __func__);
status = 0;
} else
switch (isp1362_hcd->hc_control & OHCI_CTRL_HCFS) {
@@ -2474,8 +2475,8 @@ static int isp1362_chip_test(struct isp1362_hcd *isp1362_hcd)
__func__, offset);
break;
}
- pr_warning("%s: memory check with offset %02x ok after second read\n",
- __func__, offset);
+ pr_warn("%s: memory check with offset %02x ok after second read\n",
+ __func__, offset);
}
}
kfree(ref);
diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c
index b38a228134df..5302f988e7e6 100644
--- a/drivers/usb/host/ohci-at91.c
+++ b/drivers/usb/host/ohci-at91.c
@@ -14,8 +14,8 @@
#include <linux/clk.h>
#include <linux/dma-mapping.h>
+#include <linux/gpio/consumer.h>
#include <linux/of_platform.h>
-#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/platform_data/atmel.h>
#include <linux/io.h>
@@ -39,11 +39,10 @@
#define AT91_MAX_USBH_PORTS 3
struct at91_usbh_data {
- int vbus_pin[AT91_MAX_USBH_PORTS]; /* port power-control pin */
- int overcurrent_pin[AT91_MAX_USBH_PORTS];
+ struct gpio_desc *vbus_pin[AT91_MAX_USBH_PORTS];
+ struct gpio_desc *overcurrent_pin[AT91_MAX_USBH_PORTS];
u8 ports; /* number of ports on root hub */
u8 overcurrent_supported;
- u8 vbus_pin_active_low[AT91_MAX_USBH_PORTS];
u8 overcurrent_status[AT91_MAX_USBH_PORTS];
u8 overcurrent_changed[AT91_MAX_USBH_PORTS];
};
@@ -68,8 +67,6 @@ static const struct ohci_driver_overrides ohci_at91_drv_overrides __initconst =
.extra_priv_size = sizeof(struct ohci_at91_priv),
};
-extern int usb_disabled(void);
-
/*-------------------------------------------------------------------------*/
static void at91_start_clock(struct ohci_at91_priv *ohci_at91)
@@ -268,11 +265,7 @@ static void ohci_at91_usb_set_power(struct at91_usbh_data *pdata, int port, int
if (!valid_port(port))
return;
- if (!gpio_is_valid(pdata->vbus_pin[port]))
- return;
-
- gpio_set_value(pdata->vbus_pin[port],
- pdata->vbus_pin_active_low[port] ^ enable);
+ gpiod_set_value(pdata->vbus_pin[port], enable);
}
static int ohci_at91_usb_get_power(struct at91_usbh_data *pdata, int port)
@@ -280,11 +273,7 @@ static int ohci_at91_usb_get_power(struct at91_usbh_data *pdata, int port)
if (!valid_port(port))
return -EINVAL;
- if (!gpio_is_valid(pdata->vbus_pin[port]))
- return -EINVAL;
-
- return gpio_get_value(pdata->vbus_pin[port]) ^
- pdata->vbus_pin_active_low[port];
+ return gpiod_get_value(pdata->vbus_pin[port]);
}
/*
@@ -361,7 +350,7 @@ static int ohci_at91_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
case USB_PORT_FEAT_SUSPEND:
dev_dbg(hcd->self.controller, "SetPortFeat: SUSPEND\n");
- if (valid_port(wIndex)) {
+ if (valid_port(wIndex) && ohci_at91->sfr_regmap) {
ohci_at91_port_suspend(ohci_at91->sfr_regmap,
1);
return 0;
@@ -404,7 +393,7 @@ static int ohci_at91_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
case USB_PORT_FEAT_SUSPEND:
dev_dbg(hcd->self.controller, "ClearPortFeature: SUSPEND\n");
- if (valid_port(wIndex)) {
+ if (valid_port(wIndex) && ohci_at91->sfr_regmap) {
ohci_at91_port_suspend(ohci_at91->sfr_regmap,
0);
return 0;
@@ -474,16 +463,13 @@ static irqreturn_t ohci_hcd_at91_overcurrent_irq(int irq, void *data)
{
struct platform_device *pdev = data;
struct at91_usbh_data *pdata = dev_get_platdata(&pdev->dev);
- int val, gpio, port;
+ int val, port;
/* From the GPIO notifying the over-current situation, find
* out the corresponding port */
at91_for_each_port(port) {
- if (gpio_is_valid(pdata->overcurrent_pin[port]) &&
- gpio_to_irq(pdata->overcurrent_pin[port]) == irq) {
- gpio = pdata->overcurrent_pin[port];
+ if (gpiod_to_irq(pdata->overcurrent_pin[port]) == irq)
break;
- }
}
if (port == AT91_MAX_USBH_PORTS) {
@@ -491,7 +477,7 @@ static irqreturn_t ohci_hcd_at91_overcurrent_irq(int irq, void *data)
return IRQ_HANDLED;
}
- val = gpio_get_value(gpio);
+ val = gpiod_get_value(pdata->overcurrent_pin[port]);
/* When notified of an over-current situation, disable power
on the corresponding port, and mark this port in
@@ -522,9 +508,8 @@ static int ohci_hcd_at91_drv_probe(struct platform_device *pdev)
struct device_node *np = pdev->dev.of_node;
struct at91_usbh_data *pdata;
int i;
- int gpio;
int ret;
- enum of_gpio_flags flags;
+ int err;
u32 ports;
/* Right now device-tree probed devices don't get dma_mask set.
@@ -545,40 +530,17 @@ static int ohci_hcd_at91_drv_probe(struct platform_device *pdev)
pdata->ports = ports;
at91_for_each_port(i) {
- /*
- * do not configure PIO if not in relation with
- * real USB port on board
- */
- if (i >= pdata->ports) {
- pdata->vbus_pin[i] = -EINVAL;
- pdata->overcurrent_pin[i] = -EINVAL;
- continue;
- }
-
- gpio = of_get_named_gpio_flags(np, "atmel,vbus-gpio", i,
- &flags);
- pdata->vbus_pin[i] = gpio;
- if (!gpio_is_valid(gpio))
- continue;
- pdata->vbus_pin_active_low[i] = flags & OF_GPIO_ACTIVE_LOW;
+ if (i >= pdata->ports)
+ break;
- ret = gpio_request(gpio, "ohci_vbus");
- if (ret) {
- dev_err(&pdev->dev,
- "can't request vbus gpio %d\n", gpio);
- continue;
- }
- ret = gpio_direction_output(gpio,
- !pdata->vbus_pin_active_low[i]);
- if (ret) {
- dev_err(&pdev->dev,
- "can't put vbus gpio %d as output %d\n",
- gpio, !pdata->vbus_pin_active_low[i]);
- gpio_free(gpio);
+ pdata->vbus_pin[i] =
+ devm_gpiod_get_index_optional(&pdev->dev, "atmel,vbus",
+ i, GPIOD_OUT_HIGH);
+ if (IS_ERR(pdata->vbus_pin[i])) {
+ err = PTR_ERR(pdata->vbus_pin[i]);
+ dev_err(&pdev->dev, "unable to claim gpio \"vbus\": %d\n", err);
continue;
}
-
- ohci_at91_usb_set_power(pdata, i, 1);
}
at91_for_each_port(i) {
@@ -586,37 +548,21 @@ static int ohci_hcd_at91_drv_probe(struct platform_device *pdev)
break;
pdata->overcurrent_pin[i] =
- of_get_named_gpio_flags(np, "atmel,oc-gpio", i, &flags);
-
- if (!gpio_is_valid(pdata->overcurrent_pin[i]))
- continue;
- gpio = pdata->overcurrent_pin[i];
-
- ret = gpio_request(gpio, "ohci_overcurrent");
- if (ret) {
- dev_err(&pdev->dev,
- "can't request overcurrent gpio %d\n",
- gpio);
+ devm_gpiod_get_index_optional(&pdev->dev, "atmel,oc",
+ i, GPIOD_IN);
+ 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);
continue;
}
- ret = gpio_direction_input(gpio);
- if (ret) {
- dev_err(&pdev->dev,
- "can't configure overcurrent gpio %d as input\n",
- gpio);
- gpio_free(gpio);
- continue;
- }
-
- ret = request_irq(gpio_to_irq(gpio),
- ohci_hcd_at91_overcurrent_irq,
- IRQF_SHARED, "ohci_overcurrent", pdev);
- if (ret) {
- gpio_free(gpio);
- dev_err(&pdev->dev,
- "can't get gpio IRQ for overcurrent\n");
- }
+ ret = devm_request_irq(&pdev->dev,
+ gpiod_to_irq(pdata->overcurrent_pin[i]),
+ ohci_hcd_at91_overcurrent_irq,
+ IRQF_SHARED,
+ "ohci_overcurrent", pdev);
+ if (ret)
+ dev_info(&pdev->dev, "failed to request gpio \"overcurrent\" IRQ\n");
}
device_init_wakeup(&pdev->dev, 1);
@@ -629,19 +575,8 @@ static int ohci_hcd_at91_drv_remove(struct platform_device *pdev)
int i;
if (pdata) {
- at91_for_each_port(i) {
- if (!gpio_is_valid(pdata->vbus_pin[i]))
- continue;
+ at91_for_each_port(i)
ohci_at91_usb_set_power(pdata, i, 0);
- gpio_free(pdata->vbus_pin[i]);
- }
-
- at91_for_each_port(i) {
- if (!gpio_is_valid(pdata->overcurrent_pin[i]))
- continue;
- free_irq(gpio_to_irq(pdata->overcurrent_pin[i]), pdev);
- gpio_free(pdata->overcurrent_pin[i]);
- }
}
device_init_wakeup(&pdev->dev, 0);
diff --git a/drivers/usb/host/ohci-da8xx.c b/drivers/usb/host/ohci-da8xx.c
index e5c33bc98ea4..05da2cb59612 100644
--- a/drivers/usb/host/ohci-da8xx.c
+++ b/drivers/usb/host/ohci-da8xx.c
@@ -11,62 +11,192 @@
* kind, whether express or implied.
*/
+#include <linux/clk.h>
+#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/platform_device.h>
-#include <linux/clk.h>
-
-#include <mach/da8xx.h>
+#include <linux/phy/phy.h>
#include <linux/platform_data/usb-davinci.h>
+#include <linux/regulator/consumer.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include <asm/unaligned.h>
-#ifndef CONFIG_ARCH_DAVINCI_DA8XX
-#error "This file is DA8xx bus glue. Define CONFIG_ARCH_DAVINCI_DA8XX."
-#endif
+#include "ohci.h"
-#define CFGCHIP2 DA8XX_SYSCFG0_VIRT(DA8XX_CFGCHIP2_REG)
+#define DRIVER_DESC "DA8XX"
+#define DRV_NAME "ohci-da8xx"
-static struct clk *usb11_clk;
-static struct clk *usb20_clk;
+static struct hc_driver __read_mostly ohci_da8xx_hc_driver;
+
+static int (*orig_ohci_hub_control)(struct usb_hcd *hcd, u16 typeReq,
+ u16 wValue, u16 wIndex, char *buf, u16 wLength);
+static int (*orig_ohci_hub_status_data)(struct usb_hcd *hcd, char *buf);
+
+struct da8xx_ohci_hcd {
+ struct usb_hcd *hcd;
+ struct clk *usb11_clk;
+ struct phy *usb11_phy;
+ struct regulator *vbus_reg;
+ struct notifier_block nb;
+ unsigned int reg_enabled;
+};
+
+#define to_da8xx_ohci(hcd) (struct da8xx_ohci_hcd *)(hcd_to_ohci(hcd)->priv)
/* Over-current indicator change bitmask */
static volatile u16 ocic_mask;
-static void ohci_da8xx_clock(int on)
+static int ohci_da8xx_enable(struct usb_hcd *hcd)
{
- u32 cfgchip2;
-
- cfgchip2 = __raw_readl(CFGCHIP2);
- if (on) {
- clk_enable(usb11_clk);
-
- /*
- * If USB 1.1 reference clock is sourced from USB 2.0 PHY, we
- * need to enable the USB 2.0 module clocking, start its PHY,
- * and not allow it to stop the clock during USB 2.0 suspend.
- */
- if (!(cfgchip2 & CFGCHIP2_USB1PHYCLKMUX)) {
- clk_enable(usb20_clk);
-
- cfgchip2 &= ~(CFGCHIP2_RESET | CFGCHIP2_PHYPWRDN);
- cfgchip2 |= CFGCHIP2_PHY_PLLON;
- __raw_writel(cfgchip2, CFGCHIP2);
-
- pr_info("Waiting for USB PHY clock good...\n");
- while (!(__raw_readl(CFGCHIP2) & CFGCHIP2_PHYCLKGD))
- cpu_relax();
- }
+ struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd);
+ int ret;
+
+ ret = clk_prepare_enable(da8xx_ohci->usb11_clk);
+ if (ret)
+ return ret;
+
+ ret = phy_init(da8xx_ohci->usb11_phy);
+ if (ret)
+ goto err_phy_init;
+
+ ret = phy_power_on(da8xx_ohci->usb11_phy);
+ if (ret)
+ goto err_phy_power_on;
+
+ return 0;
+
+err_phy_power_on:
+ phy_exit(da8xx_ohci->usb11_phy);
+err_phy_init:
+ clk_disable_unprepare(da8xx_ohci->usb11_clk);
+
+ return ret;
+}
+
+static void ohci_da8xx_disable(struct usb_hcd *hcd)
+{
+ struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd);
+
+ phy_power_off(da8xx_ohci->usb11_phy);
+ phy_exit(da8xx_ohci->usb11_phy);
+ clk_disable_unprepare(da8xx_ohci->usb11_clk);
+}
- /* Enable USB 1.1 PHY */
- cfgchip2 |= CFGCHIP2_USB1SUSPENDM;
- } else {
- clk_disable(usb11_clk);
- if (!(cfgchip2 & CFGCHIP2_USB1PHYCLKMUX))
- clk_disable(usb20_clk);
+static int ohci_da8xx_set_power(struct usb_hcd *hcd, int on)
+{
+ struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd);
+ struct device *dev = hcd->self.controller;
+ struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev);
+ int ret;
+
+ if (hub && hub->set_power)
+ return hub->set_power(1, on);
+
+ if (!da8xx_ohci->vbus_reg)
+ return 0;
- /* Disable USB 1.1 PHY */
- cfgchip2 &= ~CFGCHIP2_USB1SUSPENDM;
+ if (on && !da8xx_ohci->reg_enabled) {
+ ret = regulator_enable(da8xx_ohci->vbus_reg);
+ if (ret) {
+ dev_err(dev, "Failed to enable regulator: %d\n", ret);
+ return ret;
+ }
+ da8xx_ohci->reg_enabled = 1;
+
+ } else if (!on && da8xx_ohci->reg_enabled) {
+ ret = regulator_disable(da8xx_ohci->vbus_reg);
+ if (ret) {
+ dev_err(dev, "Failed to disable regulator: %d\n", ret);
+ return ret;
+ }
+ da8xx_ohci->reg_enabled = 0;
}
- __raw_writel(cfgchip2, CFGCHIP2);
+
+ return 0;
+}
+
+static int ohci_da8xx_get_power(struct usb_hcd *hcd)
+{
+ struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd);
+ struct device *dev = hcd->self.controller;
+ struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev);
+
+ if (hub && hub->get_power)
+ return hub->get_power(1);
+
+ if (da8xx_ohci->vbus_reg)
+ return regulator_is_enabled(da8xx_ohci->vbus_reg);
+
+ return 1;
+}
+
+static int ohci_da8xx_get_oci(struct usb_hcd *hcd)
+{
+ struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd);
+ struct device *dev = hcd->self.controller;
+ struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev);
+ unsigned int flags;
+ int ret;
+
+ if (hub && hub->get_oci)
+ return hub->get_oci(1);
+
+ if (!da8xx_ohci->vbus_reg)
+ return 0;
+
+ ret = regulator_get_error_flags(da8xx_ohci->vbus_reg, &flags);
+ if (ret)
+ return ret;
+
+ if (flags & REGULATOR_ERROR_OVER_CURRENT)
+ return 1;
+
+ return 0;
+}
+
+static int ohci_da8xx_has_set_power(struct usb_hcd *hcd)
+{
+ struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd);
+ struct device *dev = hcd->self.controller;
+ struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev);
+
+ if (hub && hub->set_power)
+ return 1;
+
+ if (da8xx_ohci->vbus_reg)
+ return 1;
+
+ return 0;
+}
+
+static int ohci_da8xx_has_oci(struct usb_hcd *hcd)
+{
+ struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd);
+ struct device *dev = hcd->self.controller;
+ struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev);
+
+ if (hub && hub->get_oci)
+ return 1;
+
+ if (da8xx_ohci->vbus_reg)
+ return 1;
+
+ return 0;
+}
+
+static int ohci_da8xx_has_potpgt(struct usb_hcd *hcd)
+{
+ struct device *dev = hcd->self.controller;
+ struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev);
+
+ if (hub && hub->potpgt)
+ return 1;
+
+ return 0;
}
/*
@@ -82,7 +212,51 @@ static void ohci_da8xx_ocic_handler(struct da8xx_ohci_root_hub *hub,
hub->set_power(port, 0);
}
-static int ohci_da8xx_init(struct usb_hcd *hcd)
+static int ohci_da8xx_regulator_event(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct da8xx_ohci_hcd *da8xx_ohci =
+ container_of(nb, struct da8xx_ohci_hcd, nb);
+
+ if (event & REGULATOR_EVENT_OVER_CURRENT) {
+ ocic_mask |= 1 << 1;
+ ohci_da8xx_set_power(da8xx_ohci->hcd, 0);
+ }
+
+ return 0;
+}
+
+static int ohci_da8xx_register_notify(struct usb_hcd *hcd)
+{
+ struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd);
+ struct device *dev = hcd->self.controller;
+ struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev);
+ int ret = 0;
+
+ if (hub && hub->ocic_notify) {
+ ret = hub->ocic_notify(ohci_da8xx_ocic_handler);
+ } else if (da8xx_ohci->vbus_reg) {
+ da8xx_ohci->nb.notifier_call = ohci_da8xx_regulator_event;
+ ret = devm_regulator_register_notifier(da8xx_ohci->vbus_reg,
+ &da8xx_ohci->nb);
+ }
+
+ if (ret)
+ dev_err(dev, "Failed to register notifier: %d\n", ret);
+
+ return ret;
+}
+
+static void ohci_da8xx_unregister_notify(struct usb_hcd *hcd)
+{
+ struct device *dev = hcd->self.controller;
+ struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev);
+
+ if (hub && hub->ocic_notify)
+ hub->ocic_notify(NULL);
+}
+
+static int ohci_da8xx_reset(struct usb_hcd *hcd)
{
struct device *dev = hcd->self.controller;
struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev);
@@ -92,7 +266,9 @@ static int ohci_da8xx_init(struct usb_hcd *hcd)
dev_dbg(dev, "starting USB controller\n");
- ohci_da8xx_clock(1);
+ result = ohci_da8xx_enable(hcd);
+ if (result < 0)
+ return result;
/*
* DA8xx only have 1 port connected to the pins but the HC root hub
@@ -100,9 +276,11 @@ static int ohci_da8xx_init(struct usb_hcd *hcd)
*/
ohci->num_ports = 1;
- result = ohci_init(ohci);
- if (result < 0)
+ result = ohci_setup(hcd);
+ if (result < 0) {
+ ohci_da8xx_disable(hcd);
return result;
+ }
/*
* Since we're providing a board-specific root hub port power control
@@ -111,45 +289,29 @@ static int ohci_da8xx_init(struct usb_hcd *hcd)
* the correct hub descriptor...
*/
rh_a = ohci_readl(ohci, &ohci->regs->roothub.a);
- if (hub->set_power) {
+ if (ohci_da8xx_has_set_power(hcd)) {
rh_a &= ~RH_A_NPS;
rh_a |= RH_A_PSM;
}
- if (hub->get_oci) {
+ if (ohci_da8xx_has_oci(hcd)) {
rh_a &= ~RH_A_NOCP;
rh_a |= RH_A_OCPM;
}
- rh_a &= ~RH_A_POTPGT;
- rh_a |= hub->potpgt << 24;
+ if (ohci_da8xx_has_potpgt(hcd)) {
+ rh_a &= ~RH_A_POTPGT;
+ rh_a |= hub->potpgt << 24;
+ }
ohci_writel(ohci, rh_a, &ohci->regs->roothub.a);
return result;
}
-static void ohci_da8xx_stop(struct usb_hcd *hcd)
-{
- ohci_stop(hcd);
- ohci_da8xx_clock(0);
-}
-
-static int ohci_da8xx_start(struct usb_hcd *hcd)
-{
- struct ohci_hcd *ohci = hcd_to_ohci(hcd);
- int result;
-
- result = ohci_run(ohci);
- if (result < 0)
- ohci_da8xx_stop(hcd);
-
- return result;
-}
-
/*
* Update the status data from the hub with the over-current indicator change.
*/
static int ohci_da8xx_hub_status_data(struct usb_hcd *hcd, char *buf)
{
- int length = ohci_hub_status_data(hcd, buf);
+ int length = orig_ohci_hub_status_data(hcd, buf);
/* See if we have OCIC bit set on port 1 */
if (ocic_mask & (1 << 1)) {
@@ -171,7 +333,6 @@ static int ohci_da8xx_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
u16 wIndex, char *buf, u16 wLength)
{
struct device *dev = hcd->self.controller;
- struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev);
int temp;
switch (typeReq) {
@@ -185,11 +346,11 @@ static int ohci_da8xx_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
temp = roothub_portstatus(hcd_to_ohci(hcd), wIndex - 1);
/* The port power status (PPS) bit defaults to 1 */
- if (hub->get_power && hub->get_power(wIndex) == 0)
+ if (!ohci_da8xx_get_power(hcd))
temp &= ~RH_PS_PPS;
/* The port over-current indicator (POCI) bit is always 0 */
- if (hub->get_oci && hub->get_oci(wIndex) > 0)
+ if (ohci_da8xx_get_oci(hcd) > 0)
temp |= RH_PS_POCI;
/* The over-current indicator change (OCIC) bit is 0 too */
@@ -214,10 +375,7 @@ check_port:
dev_dbg(dev, "%sPortFeature(%u): %s\n",
temp ? "Set" : "Clear", wIndex, "POWER");
- if (!hub->set_power)
- return -EPIPE;
-
- return hub->set_power(wIndex, temp) ? -EPIPE : 0;
+ return ohci_da8xx_set_power(hcd, temp) ? -EPIPE : 0;
case USB_PORT_FEAT_C_OVER_CURRENT:
dev_dbg(dev, "%sPortFeature(%u): %s\n",
temp ? "Set" : "Clear", wIndex,
@@ -231,86 +389,61 @@ check_port:
}
}
- return ohci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength);
+ return orig_ohci_hub_control(hcd, typeReq, wValue,
+ wIndex, buf, wLength);
}
-static const struct hc_driver ohci_da8xx_hc_driver = {
- .description = hcd_name,
- .product_desc = "DA8xx OHCI",
- .hcd_priv_size = sizeof(struct ohci_hcd),
-
- /*
- * generic hardware linkage
- */
- .irq = ohci_irq,
- .flags = HCD_USB11 | HCD_MEMORY,
-
- /*
- * basic lifecycle operations
- */
- .reset = ohci_da8xx_init,
- .start = ohci_da8xx_start,
- .stop = ohci_da8xx_stop,
- .shutdown = ohci_shutdown,
-
- /*
- * managing i/o requests and associated device resources
- */
- .urb_enqueue = ohci_urb_enqueue,
- .urb_dequeue = ohci_urb_dequeue,
- .endpoint_disable = ohci_endpoint_disable,
-
- /*
- * scheduling support
- */
- .get_frame_number = ohci_get_frame,
-
- /*
- * root hub support
- */
- .hub_status_data = ohci_da8xx_hub_status_data,
- .hub_control = ohci_da8xx_hub_control,
-
-#ifdef CONFIG_PM
- .bus_suspend = ohci_bus_suspend,
- .bus_resume = ohci_bus_resume,
-#endif
- .start_port_reset = ohci_start_port_reset,
-};
-
/*-------------------------------------------------------------------------*/
+#ifdef CONFIG_OF
+static const struct of_device_id da8xx_ohci_ids[] = {
+ { .compatible = "ti,da830-ohci" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, da8xx_ohci_ids);
+#endif
-
-/**
- * usb_hcd_da8xx_probe - initialize DA8xx-based HCDs
- * Context: !in_interrupt()
- *
- * Allocates basic resources for this USB host controller, and
- * then invokes the start() method for the HCD associated with it
- * through the hotplug entry's driver_data.
- */
-static int usb_hcd_da8xx_probe(const struct hc_driver *driver,
- struct platform_device *pdev)
+static int ohci_da8xx_probe(struct platform_device *pdev)
{
- struct da8xx_ohci_root_hub *hub = dev_get_platdata(&pdev->dev);
+ struct da8xx_ohci_hcd *da8xx_ohci;
struct usb_hcd *hcd;
struct resource *mem;
int error, irq;
+ hcd = usb_create_hcd(&ohci_da8xx_hc_driver, &pdev->dev,
+ dev_name(&pdev->dev));
+ if (!hcd)
+ return -ENOMEM;
- if (hub == NULL)
- return -ENODEV;
+ da8xx_ohci = to_da8xx_ohci(hcd);
+ da8xx_ohci->hcd = hcd;
- usb11_clk = devm_clk_get(&pdev->dev, "usb11");
- if (IS_ERR(usb11_clk))
- return PTR_ERR(usb11_clk);
+ da8xx_ohci->usb11_clk = devm_clk_get(&pdev->dev, "usb11");
+ if (IS_ERR(da8xx_ohci->usb11_clk)) {
+ error = PTR_ERR(da8xx_ohci->usb11_clk);
+ if (error != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "Failed to get clock.\n");
+ goto err;
+ }
- usb20_clk = devm_clk_get(&pdev->dev, "usb20");
- if (IS_ERR(usb20_clk))
- return PTR_ERR(usb20_clk);
+ da8xx_ohci->usb11_phy = devm_phy_get(&pdev->dev, "usb-phy");
+ if (IS_ERR(da8xx_ohci->usb11_phy)) {
+ error = PTR_ERR(da8xx_ohci->usb11_phy);
+ if (error != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "Failed to get phy.\n");
+ goto err;
+ }
- hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
- if (!hcd)
- return -ENOMEM;
+ da8xx_ohci->vbus_reg = devm_regulator_get_optional(&pdev->dev, "vbus");
+ if (IS_ERR(da8xx_ohci->vbus_reg)) {
+ error = PTR_ERR(da8xx_ohci->vbus_reg);
+ if (error == -ENODEV) {
+ da8xx_ohci->vbus_reg = NULL;
+ } else if (error == -EPROBE_DEFER) {
+ goto err;
+ } else {
+ dev_err(&pdev->dev, "Failed to get regulator\n");
+ goto err;
+ }
+ }
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
hcd->regs = devm_ioremap_resource(&pdev->dev, mem);
@@ -321,60 +454,38 @@ static int usb_hcd_da8xx_probe(const struct hc_driver *driver,
hcd->rsrc_start = mem->start;
hcd->rsrc_len = resource_size(mem);
- ohci_hcd_init(hcd_to_ohci(hcd));
-
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
error = -ENODEV;
goto err;
}
+
error = usb_add_hcd(hcd, irq, 0);
if (error)
goto err;
device_wakeup_enable(hcd->self.controller);
- if (hub->ocic_notify) {
- error = hub->ocic_notify(ohci_da8xx_ocic_handler);
- if (!error)
- return 0;
- }
+ error = ohci_da8xx_register_notify(hcd);
+ if (error)
+ goto err_remove_hcd;
+
+ return 0;
+err_remove_hcd:
usb_remove_hcd(hcd);
err:
usb_put_hcd(hcd);
return error;
}
-/**
- * usb_hcd_da8xx_remove - shutdown processing for DA8xx-based HCDs
- * @dev: USB Host Controller being removed
- * Context: !in_interrupt()
- *
- * Reverses the effect of usb_hcd_da8xx_probe(), first invoking
- * the HCD's stop() method. It is always called from a thread
- * context, normally "rmmod", "apmd", or something similar.
- */
-static inline void
-usb_hcd_da8xx_remove(struct usb_hcd *hcd, struct platform_device *pdev)
+static int ohci_da8xx_remove(struct platform_device *pdev)
{
- struct da8xx_ohci_root_hub *hub = dev_get_platdata(&pdev->dev);
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
- hub->ocic_notify(NULL);
+ ohci_da8xx_unregister_notify(hcd);
usb_remove_hcd(hcd);
usb_put_hcd(hcd);
-}
-
-static int ohci_hcd_da8xx_drv_probe(struct platform_device *dev)
-{
- return usb_hcd_da8xx_probe(&ohci_da8xx_hc_driver, dev);
-}
-
-static int ohci_hcd_da8xx_drv_remove(struct platform_device *dev)
-{
- struct usb_hcd *hcd = platform_get_drvdata(dev);
-
- usb_hcd_da8xx_remove(hcd, dev);
return 0;
}
@@ -397,7 +508,7 @@ static int ohci_da8xx_suspend(struct platform_device *pdev,
if (ret)
return ret;
- ohci_da8xx_clock(0);
+ ohci_da8xx_disable(hcd);
hcd->state = HC_STATE_SUSPENDED;
return ret;
@@ -407,32 +518,77 @@ static int ohci_da8xx_resume(struct platform_device *dev)
{
struct usb_hcd *hcd = platform_get_drvdata(dev);
struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+ int ret;
if (time_before(jiffies, ohci->next_statechange))
msleep(5);
ohci->next_statechange = jiffies;
- ohci_da8xx_clock(1);
- dev->dev.power.power_state = PMSG_ON;
- usb_hcd_resume_root_hub(hcd);
+ ret = ohci_da8xx_enable(hcd);
+ if (ret)
+ return ret;
+
+ ohci_resume(hcd, false);
+
return 0;
}
#endif
+static const struct ohci_driver_overrides da8xx_overrides __initconst = {
+ .reset = ohci_da8xx_reset,
+ .extra_priv_size = sizeof(struct da8xx_ohci_hcd),
+};
+
/*
* Driver definition to register with platform structure.
*/
static struct platform_driver ohci_hcd_da8xx_driver = {
- .probe = ohci_hcd_da8xx_drv_probe,
- .remove = ohci_hcd_da8xx_drv_remove,
+ .probe = ohci_da8xx_probe,
+ .remove = ohci_da8xx_remove,
.shutdown = usb_hcd_platform_shutdown,
#ifdef CONFIG_PM
.suspend = ohci_da8xx_suspend,
.resume = ohci_da8xx_resume,
#endif
.driver = {
- .name = "ohci",
+ .name = DRV_NAME,
+ .of_match_table = of_match_ptr(da8xx_ohci_ids),
},
};
-MODULE_ALIAS("platform:ohci");
+static int __init ohci_da8xx_init(void)
+{
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ pr_info("%s: " DRIVER_DESC "\n", DRV_NAME);
+ ohci_init_driver(&ohci_da8xx_hc_driver, &da8xx_overrides);
+
+ /*
+ * The Davinci da8xx HW has some unusual quirks, which require
+ * da8xx-specific workarounds. We override certain hc_driver
+ * functions here to achieve that. We explicitly do not enhance
+ * ohci_driver_overrides to allow this more easily, since this
+ * is an unusual case, and we don't want to encourage others to
+ * override these functions by making it too easy.
+ */
+
+ orig_ohci_hub_control = ohci_da8xx_hc_driver.hub_control;
+ orig_ohci_hub_status_data = ohci_da8xx_hc_driver.hub_status_data;
+
+ ohci_da8xx_hc_driver.hub_status_data = ohci_da8xx_hub_status_data;
+ ohci_da8xx_hc_driver.hub_control = ohci_da8xx_hub_control;
+
+ return platform_driver_register(&ohci_hcd_da8xx_driver);
+}
+module_init(ohci_da8xx_init);
+
+static void __exit ohci_da8xx_exit(void)
+{
+ platform_driver_unregister(&ohci_hcd_da8xx_driver);
+}
+module_exit(ohci_da8xx_exit);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/usb/host/ohci-exynos.c b/drivers/usb/host/ohci-exynos.c
index 2cd105be7319..6865b919403f 100644
--- a/drivers/usb/host/ohci-exynos.c
+++ b/drivers/usb/host/ohci-exynos.c
@@ -66,10 +66,12 @@ static int exynos_ohci_get_phy(struct device *dev,
if (IS_ERR(phy)) {
ret = PTR_ERR(phy);
if (ret == -EPROBE_DEFER) {
+ of_node_put(child);
return ret;
} else if (ret != -ENOSYS && ret != -ENODEV) {
dev_err(dev,
"Error retrieving usb2 phy: %d\n", ret);
+ of_node_put(child);
return ret;
}
}
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index 86612ac3fda2..b6daf2e69989 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -1219,11 +1219,6 @@ MODULE_LICENSE ("GPL");
#define SA1111_DRIVER ohci_hcd_sa1111_driver
#endif
-#ifdef CONFIG_USB_OHCI_HCD_DAVINCI
-#include "ohci-da8xx.c"
-#define DAVINCI_PLATFORM_DRIVER ohci_hcd_da8xx_driver
-#endif
-
#ifdef CONFIG_USB_OHCI_HCD_PPC_OF
#include "ohci-ppc-of.c"
#define OF_PLATFORM_DRIVER ohci_hcd_ppc_of_driver
@@ -1257,7 +1252,7 @@ static int __init ohci_hcd_mod_init(void)
return -ENODEV;
printk(KERN_INFO "%s: " DRIVER_DESC "\n", hcd_name);
- pr_debug ("%s: block sizes: ed %Zd td %Zd\n", hcd_name,
+ pr_debug ("%s: block sizes: ed %zd td %zd\n", hcd_name,
sizeof (struct ed), sizeof (struct td));
set_bit(USB_OHCI_LOADED, &usb_hcds_loaded);
@@ -1303,19 +1298,9 @@ static int __init ohci_hcd_mod_init(void)
goto error_tmio;
#endif
-#ifdef DAVINCI_PLATFORM_DRIVER
- retval = platform_driver_register(&DAVINCI_PLATFORM_DRIVER);
- if (retval < 0)
- goto error_davinci;
-#endif
-
return retval;
/* Error path */
-#ifdef DAVINCI_PLATFORM_DRIVER
- platform_driver_unregister(&DAVINCI_PLATFORM_DRIVER);
- error_davinci:
-#endif
#ifdef TMIO_OHCI_DRIVER
platform_driver_unregister(&TMIO_OHCI_DRIVER);
error_tmio:
@@ -1351,9 +1336,6 @@ module_init(ohci_hcd_mod_init);
static void __exit ohci_hcd_mod_exit(void)
{
-#ifdef DAVINCI_PLATFORM_DRIVER
- platform_driver_unregister(&DAVINCI_PLATFORM_DRIVER);
-#endif
#ifdef TMIO_OHCI_DRIVER
platform_driver_unregister(&TMIO_OHCI_DRIVER);
#endif
diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c
index ed678c17c4ea..248eb7702463 100644
--- a/drivers/usb/host/ohci-hub.c
+++ b/drivers/usb/host/ohci-hub.c
@@ -17,21 +17,21 @@
ohci_dbg (hc, \
"%s roothub.portstatus [%d] " \
"= 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s\n", \
- label, num, temp, \
- (temp & RH_PS_PRSC) ? " PRSC" : "", \
- (temp & RH_PS_OCIC) ? " OCIC" : "", \
- (temp & RH_PS_PSSC) ? " PSSC" : "", \
- (temp & RH_PS_PESC) ? " PESC" : "", \
- (temp & RH_PS_CSC) ? " CSC" : "", \
+ label, num, value, \
+ (value & RH_PS_PRSC) ? " PRSC" : "", \
+ (value & RH_PS_OCIC) ? " OCIC" : "", \
+ (value & RH_PS_PSSC) ? " PSSC" : "", \
+ (value & RH_PS_PESC) ? " PESC" : "", \
+ (value & RH_PS_CSC) ? " CSC" : "", \
\
- (temp & RH_PS_LSDA) ? " LSDA" : "", \
- (temp & RH_PS_PPS) ? " PPS" : "", \
- (temp & RH_PS_PRS) ? " PRS" : "", \
- (temp & RH_PS_POCI) ? " POCI" : "", \
- (temp & RH_PS_PSS) ? " PSS" : "", \
+ (value & RH_PS_LSDA) ? " LSDA" : "", \
+ (value & RH_PS_PPS) ? " PPS" : "", \
+ (value & RH_PS_PRS) ? " PRS" : "", \
+ (value & RH_PS_POCI) ? " POCI" : "", \
+ (value & RH_PS_PSS) ? " PSS" : "", \
\
- (temp & RH_PS_PES) ? " PES" : "", \
- (temp & RH_PS_CCS) ? " CCS" : "" \
+ (value & RH_PS_PES) ? " PES" : "", \
+ (value & RH_PS_CCS) ? " CCS" : "" \
);
/*-------------------------------------------------------------------------*/
diff --git a/drivers/usb/host/ohci-mem.c b/drivers/usb/host/ohci-mem.c
index c9e315c6808a..ed8a762b8670 100644
--- a/drivers/usb/host/ohci-mem.c
+++ b/drivers/usb/host/ohci-mem.c
@@ -88,10 +88,9 @@ td_alloc (struct ohci_hcd *hc, gfp_t mem_flags)
dma_addr_t dma;
struct td *td;
- td = dma_pool_alloc (hc->td_cache, mem_flags, &dma);
+ td = dma_pool_zalloc (hc->td_cache, mem_flags, &dma);
if (td) {
/* in case hc fetches it, make it look dead */
- memset (td, 0, sizeof *td);
td->hwNextTD = cpu_to_hc32 (hc, dma);
td->td_dma = dma;
/* hashed in td_fill */
@@ -122,9 +121,8 @@ ed_alloc (struct ohci_hcd *hc, gfp_t mem_flags)
dma_addr_t dma;
struct ed *ed;
- ed = dma_pool_alloc (hc->ed_cache, mem_flags, &dma);
+ ed = dma_pool_zalloc (hc->ed_cache, mem_flags, &dma);
if (ed) {
- memset (ed, 0, sizeof (*ed));
INIT_LIST_HEAD (&ed->td_list);
ed->dma = dma;
}
diff --git a/drivers/usb/host/ohci-nxp.c b/drivers/usb/host/ohci-nxp.c
index b7d4756232ae..6df8e2ed40fd 100644
--- a/drivers/usb/host/ohci-nxp.c
+++ b/drivers/usb/host/ohci-nxp.c
@@ -56,8 +56,6 @@ static struct hc_driver __read_mostly ohci_nxp_hc_driver;
static struct i2c_client *isp1301_i2c_client;
-extern int usb_disabled(void);
-
static struct clk *usb_host_clk;
static void isp1301_configure_lpc32xx(void)
@@ -127,6 +125,7 @@ static inline void isp1301_vbus_off(void)
static void ohci_nxp_start_hc(void)
{
unsigned long tmp = __raw_readl(USB_OTG_STAT_CONTROL) | HOST_EN;
+
__raw_writel(tmp, USB_OTG_STAT_CONTROL);
isp1301_vbus_on();
}
@@ -134,6 +133,7 @@ static void ohci_nxp_start_hc(void)
static void ohci_nxp_stop_hc(void)
{
unsigned long tmp;
+
isp1301_vbus_off();
tmp = __raw_readl(USB_OTG_STAT_CONTROL) & ~HOST_EN;
__raw_writel(tmp, USB_OTG_STAT_CONTROL);
@@ -155,9 +155,8 @@ static int ohci_hcd_nxp_probe(struct platform_device *pdev)
}
isp1301_i2c_client = isp1301_get_client(isp1301_node);
- if (!isp1301_i2c_client) {
+ if (!isp1301_i2c_client)
return -EPROBE_DEFER;
- }
ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
if (ret)
diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c
index 495c1454b9e8..a4d814b7f380 100644
--- a/drivers/usb/host/ohci-omap.c
+++ b/drivers/usb/host/ohci-omap.c
@@ -68,9 +68,6 @@ static inline int tps65010_set_gpio_out_value(unsigned gpio, unsigned value)
#endif
-extern int usb_disabled(void);
-extern int ocpi_enable(void);
-
static struct clk *usb_host_ck;
static struct clk *usb_dc_ck;
@@ -230,8 +227,7 @@ static int ohci_omap_reset(struct usb_hcd *hcd)
return status;
}
} else {
- dev_err(hcd->self.controller, "can't find phy\n");
- return -ENODEV;
+ return -EPROBE_DEFER;
}
ohci->start_hnp = start_hnp;
}
@@ -296,15 +292,14 @@ static int ohci_omap_reset(struct usb_hcd *hcd)
/*-------------------------------------------------------------------------*/
/**
- * usb_hcd_omap_probe - initialize OMAP-based HCDs
+ * ohci_hcd_omap_probe - initialize OMAP-based HCDs
* Context: !in_interrupt()
*
* Allocates basic resources for this USB host controller, and
* then invokes the start() method for the HCD associated with it
* through the hotplug entry's driver_data.
*/
-static int usb_hcd_omap_probe (const struct hc_driver *driver,
- struct platform_device *pdev)
+static int ohci_hcd_omap_probe(struct platform_device *pdev)
{
int retval, irq;
struct usb_hcd *hcd = 0;
@@ -336,7 +331,8 @@ static int usb_hcd_omap_probe (const struct hc_driver *driver,
}
- hcd = usb_create_hcd (driver, &pdev->dev, dev_name(&pdev->dev));
+ hcd = usb_create_hcd(&ohci_omap_hc_driver, &pdev->dev,
+ dev_name(&pdev->dev));
if (!hcd) {
retval = -ENOMEM;
goto err0;
@@ -384,17 +380,18 @@ err0:
/* may be called with controller, bus, and devices active */
/**
- * usb_hcd_omap_remove - shutdown processing for OMAP-based HCDs
+ * ohci_hcd_omap_remove - shutdown processing for OMAP-based HCDs
* @dev: USB Host Controller being removed
* Context: !in_interrupt()
*
- * Reverses the effect of usb_hcd_omap_probe(), first invoking
+ * Reverses the effect of ohci_hcd_omap_probe(), first invoking
* the HCD's stop() method. It is always called from a thread
* context, normally "rmmod", "apmd", or something similar.
*/
-static inline void
-usb_hcd_omap_remove (struct usb_hcd *hcd, struct platform_device *pdev)
+static int ohci_hcd_omap_remove(struct platform_device *pdev)
{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+
dev_dbg(hcd->self.controller, "stopping USB Controller\n");
usb_remove_hcd(hcd);
omap_ohci_clock_power(0);
@@ -409,21 +406,6 @@ usb_hcd_omap_remove (struct usb_hcd *hcd, struct platform_device *pdev)
usb_put_hcd(hcd);
clk_put(usb_dc_ck);
clk_put(usb_host_ck);
-}
-
-/*-------------------------------------------------------------------------*/
-
-static int ohci_hcd_omap_drv_probe(struct platform_device *dev)
-{
- return usb_hcd_omap_probe(&ohci_omap_hc_driver, dev);
-}
-
-static int ohci_hcd_omap_drv_remove(struct platform_device *dev)
-{
- struct usb_hcd *hcd = platform_get_drvdata(dev);
-
- usb_hcd_omap_remove(hcd, dev);
-
return 0;
}
@@ -472,8 +454,8 @@ static int ohci_omap_resume(struct platform_device *dev)
* Driver definition to register with the OMAP bus
*/
static struct platform_driver ohci_hcd_omap_driver = {
- .probe = ohci_hcd_omap_drv_probe,
- .remove = ohci_hcd_omap_drv_remove,
+ .probe = ohci_hcd_omap_probe,
+ .remove = ohci_hcd_omap_remove,
.shutdown = usb_hcd_platform_shutdown,
#ifdef CONFIG_PM
.suspend = ohci_omap_suspend,
diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c
index a667cf2d5788..79efde8f21e0 100644
--- a/drivers/usb/host/ohci-pxa27x.c
+++ b/drivers/usb/host/ohci-pxa27x.c
@@ -404,7 +404,7 @@ static int ohci_pxa_of_init(struct platform_device *pdev)
/**
- * usb_hcd_pxa27x_probe - initialize pxa27x-based HCDs
+ * ohci_hcd_pxa27x_probe - initialize pxa27x-based HCDs
* Context: !in_interrupt()
*
* Allocates basic resources for this USB host controller, and
@@ -412,7 +412,7 @@ static int ohci_pxa_of_init(struct platform_device *pdev)
* through the hotplug entry's driver_data.
*
*/
-int usb_hcd_pxa27x_probe (const struct hc_driver *driver, struct platform_device *pdev)
+static int ohci_hcd_pxa27x_probe(struct platform_device *pdev)
{
int retval, irq;
struct usb_hcd *hcd;
@@ -442,7 +442,7 @@ int usb_hcd_pxa27x_probe (const struct hc_driver *driver, struct platform_device
if (IS_ERR(usb_clk))
return PTR_ERR(usb_clk);
- hcd = usb_create_hcd (driver, &pdev->dev, "pxa27x");
+ hcd = usb_create_hcd(&ohci_pxa27x_hc_driver, &pdev->dev, "pxa27x");
if (!hcd)
return -ENOMEM;
@@ -503,17 +503,18 @@ int usb_hcd_pxa27x_probe (const struct hc_driver *driver, struct platform_device
/* may be called with controller, bus, and devices active */
/**
- * usb_hcd_pxa27x_remove - shutdown processing for pxa27x-based HCDs
+ * ohci_hcd_pxa27x_remove - shutdown processing for pxa27x-based HCDs
* @dev: USB Host Controller being removed
* Context: !in_interrupt()
*
- * Reverses the effect of usb_hcd_pxa27x_probe(), first invoking
+ * Reverses the effect of ohci_hcd_pxa27x_probe(), first invoking
* the HCD's stop() method. It is always called from a thread
* context, normally "rmmod", "apmd", or something similar.
*
*/
-void usb_hcd_pxa27x_remove (struct usb_hcd *hcd, struct platform_device *pdev)
+static int ohci_hcd_pxa27x_remove(struct platform_device *pdev)
{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
struct pxa27x_ohci *pxa_ohci = to_pxa27x_ohci(hcd);
unsigned int i;
@@ -524,28 +525,11 @@ void usb_hcd_pxa27x_remove (struct usb_hcd *hcd, struct platform_device *pdev)
pxa27x_ohci_set_vbus_power(pxa_ohci, i, false);
usb_put_hcd(hcd);
+ return 0;
}
/*-------------------------------------------------------------------------*/
-static int ohci_hcd_pxa27x_drv_probe(struct platform_device *pdev)
-{
- pr_debug ("In ohci_hcd_pxa27x_drv_probe");
-
- if (usb_disabled())
- return -ENODEV;
-
- return usb_hcd_pxa27x_probe(&ohci_pxa27x_hc_driver, pdev);
-}
-
-static int ohci_hcd_pxa27x_drv_remove(struct platform_device *pdev)
-{
- struct usb_hcd *hcd = platform_get_drvdata(pdev);
-
- usb_hcd_pxa27x_remove(hcd, pdev);
- return 0;
-}
-
#ifdef CONFIG_PM
static int ohci_hcd_pxa27x_drv_suspend(struct device *dev)
{
@@ -598,8 +582,8 @@ static const struct dev_pm_ops ohci_hcd_pxa27x_pm_ops = {
#endif
static struct platform_driver ohci_hcd_pxa27x_driver = {
- .probe = ohci_hcd_pxa27x_drv_probe,
- .remove = ohci_hcd_pxa27x_drv_remove,
+ .probe = ohci_hcd_pxa27x_probe,
+ .remove = ohci_hcd_pxa27x_remove,
.shutdown = usb_hcd_platform_shutdown,
.driver = {
.name = "pxa27x-ohci",
diff --git a/drivers/usb/host/ohci-s3c2410.c b/drivers/usb/host/ohci-s3c2410.c
index 7a1919ca543a..b006b93126f7 100644
--- a/drivers/usb/host/ohci-s3c2410.c
+++ b/drivers/usb/host/ohci-s3c2410.c
@@ -43,6 +43,8 @@ static const char hcd_name[] = "ohci-s3c2410";
static struct clk *clk;
static struct clk *usb_clk;
+static struct hc_driver __read_mostly ohci_s3c2410_hc_driver;
+
/* forward definitions */
static void s3c2410_hcd_oc(struct s3c2410_hcd_info *info, int port_oc);
@@ -321,26 +323,29 @@ static void s3c2410_hcd_oc(struct s3c2410_hcd_info *info, int port_oc)
/* may be called with controller, bus, and devices active */
/*
- * usb_hcd_s3c2410_remove - shutdown processing for HCD
+ * ohci_hcd_s3c2410_remove - shutdown processing for HCD
* @dev: USB Host Controller being removed
* Context: !in_interrupt()
*
- * Reverses the effect of usb_hcd_3c2410_probe(), first invoking
+ * Reverses the effect of ohci_hcd_3c2410_probe(), first invoking
* the HCD's stop() method. It is always called from a thread
* context, normally "rmmod", "apmd", or something similar.
*
*/
-static void
-usb_hcd_s3c2410_remove(struct usb_hcd *hcd, struct platform_device *dev)
+static int
+ohci_hcd_s3c2410_remove(struct platform_device *dev)
{
+ struct usb_hcd *hcd = platform_get_drvdata(dev);
+
usb_remove_hcd(hcd);
s3c2410_stop_hc(dev);
usb_put_hcd(hcd);
+ return 0;
}
/**
- * usb_hcd_s3c2410_probe - initialize S3C2410-based HCDs
+ * ohci_hcd_s3c2410_probe - initialize S3C2410-based HCDs
* Context: !in_interrupt()
*
* Allocates basic resources for this USB host controller, and
@@ -348,8 +353,7 @@ usb_hcd_s3c2410_remove(struct usb_hcd *hcd, struct platform_device *dev)
* through the hotplug entry's driver_data.
*
*/
-static int usb_hcd_s3c2410_probe(const struct hc_driver *driver,
- struct platform_device *dev)
+static int ohci_hcd_s3c2410_probe(struct platform_device *dev)
{
struct usb_hcd *hcd = NULL;
struct s3c2410_hcd_info *info = dev_get_platdata(&dev->dev);
@@ -358,7 +362,7 @@ static int usb_hcd_s3c2410_probe(const struct hc_driver *driver,
s3c2410_usb_set_power(info, 1, 1);
s3c2410_usb_set_power(info, 2, 1);
- hcd = usb_create_hcd(driver, &dev->dev, "s3c24xx");
+ hcd = usb_create_hcd(&ohci_s3c2410_hc_driver, &dev->dev, "s3c24xx");
if (hcd == NULL)
return -ENOMEM;
@@ -404,21 +408,6 @@ static int usb_hcd_s3c2410_probe(const struct hc_driver *driver,
/*-------------------------------------------------------------------------*/
-static struct hc_driver __read_mostly ohci_s3c2410_hc_driver;
-
-static int ohci_hcd_s3c2410_drv_probe(struct platform_device *pdev)
-{
- return usb_hcd_s3c2410_probe(&ohci_s3c2410_hc_driver, pdev);
-}
-
-static int ohci_hcd_s3c2410_drv_remove(struct platform_device *pdev)
-{
- struct usb_hcd *hcd = platform_get_drvdata(pdev);
-
- usb_hcd_s3c2410_remove(hcd, pdev);
- return 0;
-}
-
#ifdef CONFIG_PM
static int ohci_hcd_s3c2410_drv_suspend(struct device *dev)
{
@@ -457,13 +446,21 @@ static const struct dev_pm_ops ohci_hcd_s3c2410_pm_ops = {
.resume = ohci_hcd_s3c2410_drv_resume,
};
+static const struct of_device_id ohci_hcd_s3c2410_dt_ids[] = {
+ { .compatible = "samsung,s3c2410-ohci" },
+ { /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, ohci_hcd_s3c2410_dt_ids);
+
static struct platform_driver ohci_hcd_s3c2410_driver = {
- .probe = ohci_hcd_s3c2410_drv_probe,
- .remove = ohci_hcd_s3c2410_drv_remove,
+ .probe = ohci_hcd_s3c2410_probe,
+ .remove = ohci_hcd_s3c2410_remove,
.shutdown = usb_hcd_platform_shutdown,
.driver = {
.name = "s3c2410-ohci",
.pm = &ohci_hcd_s3c2410_pm_ops,
+ .of_match_table = ohci_hcd_s3c2410_dt_ids,
},
};
diff --git a/drivers/usb/host/oxu210hp-hcd.c b/drivers/usb/host/oxu210hp-hcd.c
index 4e4d601af35c..bcf531c44c70 100644
--- a/drivers/usb/host/oxu210hp-hcd.c
+++ b/drivers/usb/host/oxu210hp-hcd.c
@@ -2288,9 +2288,7 @@ restart:
while (q.ptr != NULL) {
union ehci_shadow temp;
- int live;
- live = HC_IS_RUNNING(oxu_to_hcd(oxu)->state);
switch (type) {
case Q_TYPE_QH:
/* handle any completions */
diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c
index 5d3d914ab4fb..683098afa93e 100644
--- a/drivers/usb/host/uhci-hcd.c
+++ b/drivers/usb/host/uhci-hcd.c
@@ -42,7 +42,7 @@
#include <linux/bitops.h>
#include <linux/dmi.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <asm/io.h>
#include <asm/irq.h>
diff --git a/drivers/usb/host/uhci-pci.c b/drivers/usb/host/uhci-pci.c
index 940304c33224..02260cfdedb1 100644
--- a/drivers/usb/host/uhci-pci.c
+++ b/drivers/usb/host/uhci-pci.c
@@ -129,6 +129,10 @@ static int uhci_pci_init(struct usb_hcd *hcd)
if (to_pci_dev(uhci_dev(uhci))->vendor == PCI_VENDOR_ID_HP)
uhci->wait_for_hp = 1;
+ /* Intel controllers use non-PME wakeup signalling */
+ if (to_pci_dev(uhci_dev(uhci))->vendor == PCI_VENDOR_ID_INTEL)
+ device_set_run_wake(uhci_dev(uhci), 1);
+
/* Set up pointers to PCI-specific functions */
uhci->reset_hc = uhci_pci_reset_hc;
uhci->check_and_reset_hc = uhci_pci_check_and_reset_hc;
diff --git a/drivers/usb/host/xhci-dbg.c b/drivers/usb/host/xhci-dbg.c
index 74c42f722678..2b4a00fa735d 100644
--- a/drivers/usb/host/xhci-dbg.c
+++ b/drivers/usb/host/xhci-dbg.c
@@ -37,10 +37,8 @@ void xhci_dbg_regs(struct xhci_hcd *xhci)
&xhci->cap_regs->hc_capbase, temp);
xhci_dbg(xhci, "// CAPLENGTH: 0x%x\n",
(unsigned int) HC_LENGTH(temp));
-#if 0
xhci_dbg(xhci, "// HCIVERSION: 0x%x\n",
(unsigned int) HC_VERSION(temp));
-#endif
xhci_dbg(xhci, "// xHCI operational registers at %p:\n", xhci->op_regs);
@@ -111,7 +109,7 @@ static void xhci_print_cap_regs(struct xhci_hcd *xhci)
xhci_dbg(xhci, "RTSOFF 0x%x:\n", temp & RTSOFF_MASK);
/* xhci 1.1 controllers have the HCCPARAMS2 register */
- if (hci_version > 100) {
+ if (hci_version > 0x100) {
temp = readl(&xhci->cap_regs->hcc_params2);
xhci_dbg(xhci, "HCC PARAMS2 0x%x:\n", (unsigned int) temp);
xhci_dbg(xhci, " HC %s Force save context capability",
@@ -177,7 +175,7 @@ static void xhci_print_ports(struct xhci_hcd *xhci)
ports = HCS_MAX_PORTS(xhci->hcs_params1);
addr = &xhci->op_regs->port_status_base;
for (i = 0; i < ports; i++) {
- for (j = 0; j < NUM_PORT_REGS; ++j) {
+ for (j = 0; j < NUM_PORT_REGS; j++) {
xhci_dbg(xhci, "%p port %s reg = 0x%x\n",
addr, names[j],
(unsigned int) readl(addr));
@@ -240,7 +238,7 @@ void xhci_print_run_regs(struct xhci_hcd *xhci)
xhci_dbg(xhci, " %p: Microframe index = 0x%x\n",
&xhci->run_regs->microframe_index,
(unsigned int) temp);
- for (i = 0; i < 7; ++i) {
+ for (i = 0; i < 7; i++) {
temp = readl(&xhci->run_regs->rsvd[i]);
if (temp != XHCI_INIT_VALUE)
xhci_dbg(xhci, " WARN: %p: Rsvd[%i] = 0x%x\n",
@@ -259,7 +257,7 @@ void xhci_print_registers(struct xhci_hcd *xhci)
void xhci_print_trb_offsets(struct xhci_hcd *xhci, union xhci_trb *trb)
{
int i;
- for (i = 0; i < 4; ++i)
+ for (i = 0; i < 4; i++)
xhci_dbg(xhci, "Offset 0x%x = 0x%x\n",
i*4, trb->generic.field[i]);
}
@@ -332,7 +330,7 @@ void xhci_debug_segment(struct xhci_hcd *xhci, struct xhci_segment *seg)
u64 addr = seg->dma;
union xhci_trb *trb = seg->trbs;
- for (i = 0; i < TRBS_PER_SEGMENT; ++i) {
+ for (i = 0; i < TRBS_PER_SEGMENT; i++) {
trb = &seg->trbs[i];
xhci_dbg(xhci, "@%016llx %08x %08x %08x %08x\n", addr,
lower_32_bits(le64_to_cpu(trb->link.segment_ptr)),
@@ -413,7 +411,7 @@ void xhci_dbg_erst(struct xhci_hcd *xhci, struct xhci_erst *erst)
int i;
struct xhci_erst_entry *entry;
- for (i = 0; i < erst->num_entries; ++i) {
+ for (i = 0; i < erst->num_entries; i++) {
entry = &erst->entries[i];
xhci_dbg(xhci, "@%016llx %08x %08x %08x %08x\n",
addr,
@@ -440,7 +438,7 @@ void xhci_dbg_cmd_ptrs(struct xhci_hcd *xhci)
static void dbg_rsvd64(struct xhci_hcd *xhci, u64 *ctx, dma_addr_t dma)
{
int i;
- for (i = 0; i < 4; ++i) {
+ for (i = 0; i < 4; i++) {
xhci_dbg(xhci, "@%p (virt) @%08llx "
"(dma) %#08llx - rsvd64[%d]\n",
&ctx[4 + i], (unsigned long long)dma,
@@ -496,7 +494,7 @@ static void xhci_dbg_slot_ctx(struct xhci_hcd *xhci, struct xhci_container_ctx *
&slot_ctx->dev_state,
(unsigned long long)dma, slot_ctx->dev_state);
dma += field_size;
- for (i = 0; i < 4; ++i) {
+ for (i = 0; i < 4; i++) {
xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - rsvd[%d]\n",
&slot_ctx->reserved[i], (unsigned long long)dma,
slot_ctx->reserved[i], i);
@@ -519,7 +517,7 @@ static void xhci_dbg_ep_ctx(struct xhci_hcd *xhci,
if (last_ep < 31)
last_ep_ctx = last_ep + 1;
- for (i = 0; i < last_ep_ctx; ++i) {
+ for (i = 0; i < last_ep_ctx; i++) {
unsigned int epaddr = xhci_get_endpoint_address(i);
struct xhci_ep_ctx *ep_ctx = xhci_get_ep_ctx(xhci, ctx, i);
dma_addr_t dma = ctx->dma +
@@ -544,7 +542,7 @@ static void xhci_dbg_ep_ctx(struct xhci_hcd *xhci,
&ep_ctx->tx_info,
(unsigned long long)dma, ep_ctx->tx_info);
dma += field_size;
- for (j = 0; j < 3; ++j) {
+ for (j = 0; j < 3; j++) {
xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - rsvd[%d]\n",
&ep_ctx->reserved[j],
(unsigned long long)dma,
@@ -583,7 +581,7 @@ void xhci_dbg_ctx(struct xhci_hcd *xhci,
&ctrl_ctx->add_flags, (unsigned long long)dma,
ctrl_ctx->add_flags);
dma += field_size;
- for (i = 0; i < 6; ++i) {
+ for (i = 0; i < 6; i++) {
xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - rsvd2[%d]\n",
&ctrl_ctx->rsvd2[i], (unsigned long long)dma,
ctrl_ctx->rsvd2[i], i);
diff --git a/drivers/usb/host/xhci-ext-caps.h b/drivers/usb/host/xhci-ext-caps.h
index e0244fb3903d..28deea584884 100644
--- a/drivers/usb/host/xhci-ext-caps.h
+++ b/drivers/usb/host/xhci-ext-caps.h
@@ -117,7 +117,7 @@ static inline int xhci_find_next_ext_cap(void __iomem *base, u32 start, int id)
offset = XHCI_HCC_EXT_CAPS(val) << 2;
if (!offset)
return 0;
- };
+ }
do {
val = readl(base + offset);
if (val == ~0)
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index 0ef16900efed..3bddeaa1e2d7 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -389,6 +389,8 @@ static int xhci_stop_device(struct xhci_hcd *xhci, int slot_id, int suspend)
if (!virt_dev)
return -ENODEV;
+ trace_xhci_stop_device(virt_dev);
+
cmd = xhci_alloc_command(xhci, false, true, GFP_NOIO);
if (!cmd) {
xhci_dbg(xhci, "Couldn't allocate command structure.\n");
@@ -418,7 +420,8 @@ static int xhci_stop_device(struct xhci_hcd *xhci, int slot_id, int suspend)
/* Wait for last stop endpoint command to finish */
wait_for_completion(cmd->completion);
- if (cmd->status == COMP_CMD_ABORT || cmd->status == COMP_CMD_STOP) {
+ if (cmd->status == COMP_COMMAND_ABORTED ||
+ cmd->status == COMP_STOPPED) {
xhci_warn(xhci, "Timeout while waiting for stop endpoint command\n");
ret = -ETIME;
}
@@ -458,6 +461,12 @@ static void xhci_disable_port(struct usb_hcd *hcd, struct xhci_hcd *xhci,
return;
}
+ if (xhci->quirks & XHCI_BROKEN_PORT_PED) {
+ xhci_dbg(xhci,
+ "Broken Port Enabled/Disabled, ignoring port disable request.\n");
+ return;
+ }
+
/* Write 1 to disable the port */
writel(port_status | PORT_PE, addr);
port_status = readl(addr);
@@ -990,8 +999,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
temp = readl(port_array[wIndex]);
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");
+ xhci_warn(xhci, "USB core suspending device not in U0/U1/U2.\n");
goto error;
}
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index 6afe32381209..ba1853f4e407 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -936,6 +936,9 @@ void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id)
return;
dev = xhci->devs[slot_id];
+
+ trace_xhci_free_virt_device(dev);
+
xhci->dcbaa->dev_context_ptrs[slot_id] = 0;
if (!dev)
return;
@@ -943,7 +946,7 @@ void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id)
if (dev->tt_info)
old_active_eps = dev->tt_info->active_eps;
- for (i = 0; i < 31; ++i) {
+ for (i = 0; i < 31; i++) {
if (dev->eps[i].ring)
xhci_ring_free(xhci, dev->eps[i].ring);
if (dev->eps[i].stream_info)
@@ -979,6 +982,40 @@ void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id)
xhci->devs[slot_id] = NULL;
}
+/*
+ * Free a virt_device structure.
+ * If the virt_device added a tt_info (a hub) and has children pointing to
+ * that tt_info, then free the child first. Recursive.
+ * We can't rely on udev at this point to find child-parent relationships.
+ */
+void xhci_free_virt_devices_depth_first(struct xhci_hcd *xhci, int slot_id)
+{
+ struct xhci_virt_device *vdev;
+ struct list_head *tt_list_head;
+ struct xhci_tt_bw_info *tt_info, *next;
+ int i;
+
+ vdev = xhci->devs[slot_id];
+ if (!vdev)
+ return;
+
+ tt_list_head = &(xhci->rh_bw[vdev->real_port - 1].tts);
+ list_for_each_entry_safe(tt_info, next, tt_list_head, tt_list) {
+ /* is this a hub device that added a tt_info to the tts list */
+ if (tt_info->slot_id == slot_id) {
+ /* are any devices using this tt_info? */
+ for (i = 1; i < HCS_MAX_SLOTS(xhci->hcs_params1); i++) {
+ vdev = xhci->devs[i];
+ if (vdev && (vdev->tt_info == tt_info))
+ xhci_free_virt_devices_depth_first(
+ xhci, i);
+ }
+ }
+ }
+ /* we are now at a leaf device */
+ xhci_free_virt_device(xhci, slot_id);
+}
+
int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id,
struct usb_device *udev, gfp_t flags)
{
@@ -1032,7 +1069,6 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id,
goto fail;
dev->num_rings_cached = 0;
- init_completion(&dev->cmd_completion);
dev->udev = udev;
/* Point to output device context in dcbaa. */
@@ -1042,6 +1078,8 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id,
&xhci->dcbaa->dev_context_ptrs[slot_id],
le64_to_cpu(xhci->dcbaa->dev_context_ptrs[slot_id]));
+ trace_xhci_alloc_virt_device(dev);
+
return 1;
fail:
xhci_free_virt_device(xhci, slot_id);
@@ -1216,6 +1254,8 @@ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *ud
ep0_ctx->deq = cpu_to_le64(dev->eps[0].ring->first_seg->dma |
dev->eps[0].ring->cycle_state);
+ trace_xhci_setup_addressable_virt_device(dev);
+
/* Steps 7 and 8 were done in xhci_alloc_virt_device() */
return 0;
@@ -1370,7 +1410,7 @@ static u32 xhci_get_endpoint_max_burst(struct usb_device *udev,
if (udev->speed == USB_SPEED_HIGH &&
(usb_endpoint_xfer_isoc(&ep->desc) ||
usb_endpoint_xfer_int(&ep->desc)))
- return (usb_endpoint_maxp(&ep->desc) & 0x1800) >> 11;
+ return usb_endpoint_maxp_mult(&ep->desc) - 1;
return 0;
}
@@ -1381,14 +1421,16 @@ static u32 xhci_get_endpoint_type(struct usb_host_endpoint *ep)
in = usb_endpoint_dir_in(&ep->desc);
- if (usb_endpoint_xfer_control(&ep->desc))
+ switch (usb_endpoint_type(&ep->desc)) {
+ case USB_ENDPOINT_XFER_CONTROL:
return CTRL_EP;
- if (usb_endpoint_xfer_bulk(&ep->desc))
+ case USB_ENDPOINT_XFER_BULK:
return in ? BULK_IN_EP : BULK_OUT_EP;
- if (usb_endpoint_xfer_isoc(&ep->desc))
+ case USB_ENDPOINT_XFER_ISOC:
return in ? ISOC_IN_EP : ISOC_OUT_EP;
- if (usb_endpoint_xfer_int(&ep->desc))
+ case USB_ENDPOINT_XFER_INT:
return in ? INT_IN_EP : INT_OUT_EP;
+ }
return 0;
}
@@ -1415,10 +1457,10 @@ static u32 xhci_get_max_esit_payload(struct usb_device *udev,
else if (udev->speed >= USB_SPEED_SUPER)
return le16_to_cpu(ep->ss_ep_comp.wBytesPerInterval);
- max_packet = GET_MAX_PACKET(usb_endpoint_maxp(&ep->desc));
- max_burst = (usb_endpoint_maxp(&ep->desc) & 0x1800) >> 11;
+ max_packet = usb_endpoint_maxp(&ep->desc);
+ max_burst = usb_endpoint_maxp_mult(&ep->desc);
/* A 0 in max burst means 1 transfer per ESIT */
- return max_packet * (max_burst + 1);
+ return max_packet * max_burst;
}
/* Set up an endpoint with one ring segment. Do not allocate stream rings.
@@ -1461,7 +1503,7 @@ int xhci_endpoint_init(struct xhci_hcd *xhci,
max_esit_payload = xhci_get_max_esit_payload(udev, ep);
interval = xhci_get_endpoint_interval(udev, ep);
mult = xhci_get_endpoint_mult(udev, ep);
- max_packet = GET_MAX_PACKET(usb_endpoint_maxp(&ep->desc));
+ max_packet = usb_endpoint_maxp(&ep->desc);
max_burst = xhci_get_endpoint_max_burst(udev, ep);
avg_trb_len = max_esit_payload;
@@ -1554,7 +1596,7 @@ void xhci_update_bw_info(struct xhci_hcd *xhci,
unsigned int ep_type;
int i;
- for (i = 1; i < 31; ++i) {
+ for (i = 1; i < 31; i++) {
bw_info = &virt_dev->eps[i].bw_info;
/* We can't tell what endpoint type is being dropped, but
@@ -1775,10 +1817,7 @@ struct xhci_command *xhci_alloc_command(struct xhci_hcd *xhci,
void xhci_urb_free_priv(struct urb_priv *urb_priv)
{
- if (urb_priv) {
- kfree(urb_priv->td[0]);
- kfree(urb_priv);
- }
+ kfree(urb_priv);
}
void xhci_free_command(struct xhci_hcd *xhci,
@@ -1796,7 +1835,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
int size;
int i, j, num_ports;
- del_timer_sync(&xhci->cmd_timer);
+ cancel_delayed_work_sync(&xhci->cmd_timer);
/* Free the Event Ring Segment Table and the actual Event Ring */
size = sizeof(struct xhci_erst_entry)*(xhci->erst.num_entries);
@@ -1829,8 +1868,8 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
}
}
- for (i = 1; i < MAX_HC_SLOTS; ++i)
- xhci_free_virt_device(xhci, i);
+ for (i = HCS_MAX_SLOTS(xhci->hcs_params1); i > 0; i--)
+ xhci_free_virt_devices_depth_first(xhci, i);
dma_pool_destroy(xhci->segment_pool);
xhci->segment_pool = NULL;
@@ -2343,9 +2382,9 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
INIT_LIST_HEAD(&xhci->cmd_list);
- /* init command timeout timer */
- setup_timer(&xhci->cmd_timer, xhci_handle_command_timeout,
- (unsigned long)xhci);
+ /* init command timeout work */
+ INIT_DELAYED_WORK(&xhci->cmd_timer, xhci_handle_command_timeout);
+ init_completion(&xhci->cmd_ring_stop_completion);
page_size = readl(&xhci->op_regs->page_size);
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
@@ -2384,7 +2423,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
* "physically contiguous and 64-byte (cache line) aligned".
*/
xhci->dcbaa = dma_alloc_coherent(dev, sizeof(*xhci->dcbaa), &dma,
- GFP_KERNEL);
+ flags);
if (!xhci->dcbaa)
goto fail;
memset(xhci->dcbaa, 0, sizeof *(xhci->dcbaa));
@@ -2480,7 +2519,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
xhci->erst.entries = dma_alloc_coherent(dev,
sizeof(struct xhci_erst_entry) * ERST_NUM_SEGS, &dma,
- GFP_KERNEL);
+ flags);
if (!xhci->erst.entries)
goto fail;
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
@@ -2536,10 +2575,9 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
* something other than the default (~1ms minimum between interrupts).
* See section 5.5.1.2.
*/
- init_completion(&xhci->addr_dev);
- for (i = 0; i < MAX_HC_SLOTS; ++i)
+ for (i = 0; i < MAX_HC_SLOTS; i++)
xhci->devs[i] = NULL;
- for (i = 0; i < USB_MAXCHILDREN; ++i) {
+ for (i = 0; i < USB_MAXCHILDREN; i++) {
xhci->bus_state[0].resume_done[i] = 0;
xhci->bus_state[1].resume_done[i] = 0;
/* Only the USB 2.0 completions will ever be used. */
diff --git a/drivers/usb/host/xhci-mtk-sch.c b/drivers/usb/host/xhci-mtk-sch.c
index 73f763c4f5f5..6e7ddf6cafae 100644
--- a/drivers/usb/host/xhci-mtk-sch.c
+++ b/drivers/usb/host/xhci-mtk-sch.c
@@ -337,7 +337,7 @@ int xhci_mtk_add_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev,
xhci_dbg(xhci, "%s() type:%d, speed:%d, mpkt:%d, dir:%d, ep:%p\n",
__func__, usb_endpoint_type(&ep->desc), udev->speed,
- GET_MAX_PACKET(usb_endpoint_maxp(&ep->desc)),
+ usb_endpoint_maxp(&ep->desc),
usb_endpoint_dir_in(&ep->desc), ep);
if (!need_bw_sch(ep, udev->speed, slot_ctx->tt_info & TT_SLOT)) {
@@ -403,7 +403,7 @@ void xhci_mtk_drop_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev,
xhci_dbg(xhci, "%s() type:%d, speed:%d, mpks:%d, dir:%d, ep:%p\n",
__func__, usb_endpoint_type(&ep->desc), udev->speed,
- GET_MAX_PACKET(usb_endpoint_maxp(&ep->desc)),
+ usb_endpoint_maxp(&ep->desc),
usb_endpoint_dir_in(&ep->desc), ep);
if (!need_bw_sch(ep, udev->speed, slot_ctx->tt_info & TT_SLOT))
diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c
index 79959f17c38c..67d5dc79b6b5 100644
--- a/drivers/usb/host/xhci-mtk.c
+++ b/drivers/usb/host/xhci-mtk.c
@@ -94,6 +94,9 @@ static int xhci_mtk_host_enable(struct xhci_hcd_mtk *mtk)
int ret;
int i;
+ if (!mtk->has_ippc)
+ return 0;
+
/* power on host ip */
value = readl(&ippc->ip_pw_ctr1);
value &= ~CTRL1_IP_HOST_PDN;
@@ -139,6 +142,9 @@ static int xhci_mtk_host_disable(struct xhci_hcd_mtk *mtk)
int ret;
int i;
+ if (!mtk->has_ippc)
+ return 0;
+
/* power down all u3 ports */
for (i = 0; i < mtk->num_u3_ports; i++) {
value = readl(&ippc->u3_ctrl_p[i]);
@@ -173,6 +179,9 @@ static int xhci_mtk_ssusb_config(struct xhci_hcd_mtk *mtk)
struct mu3c_ippc_regs __iomem *ippc = mtk->ippc_regs;
u32 value;
+ if (!mtk->has_ippc)
+ return 0;
+
/* reset whole ip */
value = readl(&ippc->ip_pw_ctr0);
value |= CTRL0_IP_SW_RST;
@@ -203,6 +212,12 @@ static int xhci_mtk_clks_enable(struct xhci_hcd_mtk *mtk)
{
int ret;
+ ret = clk_prepare_enable(mtk->ref_clk);
+ if (ret) {
+ dev_err(mtk->dev, "failed to enable ref_clk\n");
+ goto ref_clk_err;
+ }
+
ret = clk_prepare_enable(mtk->sys_clk);
if (ret) {
dev_err(mtk->dev, "failed to enable sys_clk\n");
@@ -229,6 +244,8 @@ usb_p1_err:
usb_p0_err:
clk_disable_unprepare(mtk->sys_clk);
sys_clk_err:
+ clk_disable_unprepare(mtk->ref_clk);
+ref_clk_err:
return -EINVAL;
}
@@ -239,6 +256,7 @@ static void xhci_mtk_clks_disable(struct xhci_hcd_mtk *mtk)
clk_disable_unprepare(mtk->wk_deb_p0);
}
clk_disable_unprepare(mtk->sys_clk);
+ clk_disable_unprepare(mtk->ref_clk);
}
/* only clocks can be turn off for ip-sleep wakeup mode */
@@ -364,7 +382,6 @@ static int usb_wakeup_of_property_parse(struct xhci_hcd_mtk *mtk,
static int xhci_mtk_setup(struct usb_hcd *hcd);
static const struct xhci_driver_overrides xhci_mtk_overrides __initconst = {
- .extra_priv_size = sizeof(struct xhci_hcd),
.reset = xhci_mtk_setup,
};
@@ -475,6 +492,7 @@ static void xhci_mtk_quirks(struct device *dev, struct xhci_hcd *xhci)
/* called during probe() after chip reset completes */
static int xhci_mtk_setup(struct usb_hcd *hcd)
{
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
struct xhci_hcd_mtk *mtk = hcd_to_mtk(hcd);
int ret;
@@ -482,12 +500,21 @@ static int xhci_mtk_setup(struct usb_hcd *hcd)
ret = xhci_mtk_ssusb_config(mtk);
if (ret)
return ret;
+ }
+
+ ret = xhci_gen_setup(hcd, xhci_mtk_quirks);
+ if (ret)
+ return ret;
+
+ if (usb_hcd_is_primary_hcd(hcd)) {
+ mtk->num_u3_ports = xhci->num_usb3_ports;
+ mtk->num_u2_ports = xhci->num_usb2_ports;
ret = xhci_mtk_sch_init(mtk);
if (ret)
return ret;
}
- return xhci_gen_setup(hcd, xhci_mtk_quirks);
+ return ret;
}
static int xhci_mtk_probe(struct platform_device *pdev)
@@ -531,6 +558,19 @@ static int xhci_mtk_probe(struct platform_device *pdev)
return PTR_ERR(mtk->sys_clk);
}
+ /*
+ * reference clock is usually a "fixed-clock", make it optional
+ * for backward compatibility and ignore the error if it does
+ * not exist.
+ */
+ mtk->ref_clk = devm_clk_get(dev, "ref_ck");
+ if (IS_ERR(mtk->ref_clk)) {
+ if (PTR_ERR(mtk->ref_clk) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ mtk->ref_clk = NULL;
+ }
+
mtk->lpm_support = of_property_read_bool(node, "usb3-lpm-capable");
ret = usb_wakeup_of_property_parse(mtk, node);
@@ -560,8 +600,10 @@ static int xhci_mtk_probe(struct platform_device *pdev)
goto disable_ldos;
irq = platform_get_irq(pdev, 0);
- if (irq < 0)
+ if (irq < 0) {
+ ret = irq;
goto disable_clk;
+ }
/* Initialize dma_mask and coherent_dma_mask to 32-bits */
ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
@@ -586,7 +628,7 @@ static int xhci_mtk_probe(struct platform_device *pdev)
mtk->hcd = platform_get_drvdata(pdev);
platform_set_drvdata(pdev, mtk);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mac");
hcd->regs = devm_ioremap_resource(dev, res);
if (IS_ERR(hcd->regs)) {
ret = PTR_ERR(hcd->regs);
@@ -595,11 +637,16 @@ static int xhci_mtk_probe(struct platform_device *pdev)
hcd->rsrc_start = res->start;
hcd->rsrc_len = resource_size(res);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- mtk->ippc_regs = devm_ioremap_resource(dev, res);
- if (IS_ERR(mtk->ippc_regs)) {
- ret = PTR_ERR(mtk->ippc_regs);
- goto put_usb2_hcd;
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ippc");
+ if (res) { /* ippc register is optional */
+ mtk->ippc_regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(mtk->ippc_regs)) {
+ ret = PTR_ERR(mtk->ippc_regs);
+ goto put_usb2_hcd;
+ }
+ mtk->has_ippc = true;
+ } else {
+ mtk->has_ippc = false;
}
for (phy_num = 0; phy_num < mtk->num_phys; phy_num++) {
@@ -630,13 +677,13 @@ static int xhci_mtk_probe(struct platform_device *pdev)
goto power_off_phys;
}
- if (HCC_MAX_PSA(xhci->hcc_params) >= 4)
- xhci->shared_hcd->can_do_streams = 1;
-
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;
diff --git a/drivers/usb/host/xhci-mtk.h b/drivers/usb/host/xhci-mtk.h
index 7da677c79ea8..3aa5e1d25064 100644
--- a/drivers/usb/host/xhci-mtk.h
+++ b/drivers/usb/host/xhci-mtk.h
@@ -118,11 +118,13 @@ struct xhci_hcd_mtk {
struct usb_hcd *hcd;
struct mu3h_sch_bw_info *sch_array;
struct mu3c_ippc_regs __iomem *ippc_regs;
+ bool has_ippc;
int num_u2_ports;
int num_u3_ports;
struct regulator *vusb33;
struct regulator *vbus;
struct clk *sys_clk; /* sys and mac clock */
+ struct clk *ref_clk;
struct clk *wk_deb_p0; /* port0's wakeup debounce clock */
struct clk *wk_deb_p1;
struct regmap *pericfg;
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index e96ae80d107e..fc99f51d12e1 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -165,7 +165,8 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
pdev->device == PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_XHCI ||
pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI ||
pdev->device == PCI_DEVICE_ID_INTEL_BROXTON_M_XHCI ||
- pdev->device == PCI_DEVICE_ID_INTEL_BROXTON_B_XHCI)) {
+ pdev->device == PCI_DEVICE_ID_INTEL_BROXTON_B_XHCI ||
+ pdev->device == PCI_DEVICE_ID_INTEL_APL_XHCI)) {
xhci->quirks |= XHCI_PME_STUCK_QUIRK;
}
if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
@@ -241,11 +242,7 @@ static int xhci_pci_setup(struct usb_hcd *hcd)
xhci_dbg(xhci, "Got SBRN %u\n", (unsigned int) xhci->sbrn);
/* Find any debug ports */
- retval = xhci_pci_reinit(xhci, pdev);
- if (!retval)
- return retval;
-
- return retval;
+ return xhci_pci_reinit(xhci, pdev);
}
/*
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index ed56bf9ed885..6ed468fa7d5e 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -100,6 +100,12 @@ static const struct xhci_plat_priv xhci_plat_renesas_rcar_gen3 = {
.plat_start = xhci_rcar_start,
};
+static const struct xhci_plat_priv xhci_plat_renesas_rcar_r8a7796 = {
+ .firmware_name = XHCI_RCAR_FIRMWARE_NAME_V3,
+ .init_quirk = xhci_rcar_init_quirk,
+ .plat_start = xhci_rcar_start,
+};
+
static const struct of_device_id usb_xhci_of_match[] = {
{
.compatible = "generic-xhci",
@@ -124,6 +130,9 @@ static const struct of_device_id usb_xhci_of_match[] = {
.compatible = "renesas,xhci-r8a7795",
.data = &xhci_plat_renesas_rcar_gen3,
}, {
+ .compatible = "renesas,xhci-r8a7796",
+ .data = &xhci_plat_renesas_rcar_r8a7796,
+ }, {
.compatible = "renesas,rcar-gen2-xhci",
.data = &xhci_plat_renesas_rcar_gen2,
}, {
@@ -156,7 +165,7 @@ static int xhci_plat_probe(struct platform_device *pdev)
return -ENODEV;
/* Try to set 64-bit DMA first */
- if (WARN_ON(!pdev->dev.dma_mask))
+ if (!pdev->dev.dma_mask)
/* Platform did not initialize dma_mask */
ret = dma_coerce_mask_and_coherent(&pdev->dev,
DMA_BIT_MASK(64));
@@ -223,8 +232,8 @@ static int xhci_plat_probe(struct platform_device *pdev)
if (device_property_read_bool(&pdev->dev, "usb3-lpm-capable"))
xhci->quirks |= XHCI_LPM_SUPPORT;
- if (HCC_MAX_PSA(xhci->hcc_params) >= 4)
- xhci->shared_hcd->can_do_streams = 1;
+ if (device_property_read_bool(&pdev->dev, "quirk-broken-port-ped"))
+ xhci->quirks |= XHCI_BROKEN_PORT_PED;
hcd->usb_phy = devm_usb_get_phy_by_phandle(&pdev->dev, "usb-phy", 0);
if (IS_ERR(hcd->usb_phy)) {
@@ -242,6 +251,9 @@ static int xhci_plat_probe(struct platform_device *pdev)
if (ret)
goto disable_usb_phy;
+ 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;
@@ -274,6 +286,8 @@ static int xhci_plat_remove(struct platform_device *dev)
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
struct clk *clk = xhci->clk;
+ xhci->xhc_state |= XHCI_STATE_REMOVING;
+
usb_remove_hcd(xhci->shared_hcd);
usb_phy_shutdown(hcd->usb_phy);
@@ -330,6 +344,7 @@ MODULE_DEVICE_TABLE(acpi, usb_xhci_acpi_match);
static struct platform_driver usb_xhci_driver = {
.probe = xhci_plat_probe,
.remove = xhci_plat_remove,
+ .shutdown = usb_hcd_platform_shutdown,
.driver = {
.name = "xhci-hcd",
.pm = DEV_PM_OPS,
diff --git a/drivers/usb/host/xhci-rcar.c b/drivers/usb/host/xhci-rcar.c
index 0e4535e632ec..d28df386e780 100644
--- a/drivers/usb/host/xhci-rcar.c
+++ b/drivers/usb/host/xhci-rcar.c
@@ -19,6 +19,8 @@
#include "xhci-rcar.h"
/*
+* - The V3 firmware is for r8a7796 (with good performance).
+* - The V2 firmware can be used on both r8a7795 (es1.x) and r8a7796.
* - 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.
@@ -26,6 +28,7 @@
*/
MODULE_FIRMWARE(XHCI_RCAR_FIRMWARE_NAME_V1);
MODULE_FIRMWARE(XHCI_RCAR_FIRMWARE_NAME_V2);
+MODULE_FIRMWARE(XHCI_RCAR_FIRMWARE_NAME_V3);
/*** Register Offset ***/
#define RCAR_USB3_INT_ENA 0x224 /* Interrupt Enable */
@@ -92,6 +95,7 @@ static int xhci_rcar_is_gen3(struct device *dev)
struct device_node *node = dev->of_node;
return of_device_is_compatible(node, "renesas,xhci-r8a7795") ||
+ of_device_is_compatible(node, "renesas,xhci-r8a7796") ||
of_device_is_compatible(node, "renesas,rcar-gen3-xhci");
}
diff --git a/drivers/usb/host/xhci-rcar.h b/drivers/usb/host/xhci-rcar.h
index 2941a25cfe98..d2ffe20401cf 100644
--- a/drivers/usb/host/xhci-rcar.h
+++ b/drivers/usb/host/xhci-rcar.h
@@ -13,6 +13,7 @@
#define XHCI_RCAR_FIRMWARE_NAME_V1 "r8a779x_usb3_v1.dlmem"
#define XHCI_RCAR_FIRMWARE_NAME_V2 "r8a779x_usb3_v2.dlmem"
+#define XHCI_RCAR_FIRMWARE_NAME_V3 "r8a779x_usb3_v3.dlmem"
#if IS_ENABLED(CONFIG_USB_XHCI_RCAR)
void xhci_rcar_start(struct usb_hcd *hcd);
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 797137e26549..a3309aa02993 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -89,6 +89,11 @@ dma_addr_t xhci_trb_virt_to_dma(struct xhci_segment *seg,
return seg->dma + (segment_offset * sizeof(*trb));
}
+static bool trb_is_noop(union xhci_trb *trb)
+{
+ return TRB_TYPE_NOOP_LE32(trb->generic.field[3]);
+}
+
static bool trb_is_link(union xhci_trb *trb)
{
return TRB_TYPE_LINK_LE32(trb->link.control);
@@ -110,6 +115,35 @@ static bool link_trb_toggles_cycle(union xhci_trb *trb)
return le32_to_cpu(trb->link.control) & LINK_TOGGLE;
}
+static bool last_td_in_urb(struct xhci_td *td)
+{
+ struct urb_priv *urb_priv = td->urb->hcpriv;
+
+ return urb_priv->num_tds_done == urb_priv->num_tds;
+}
+
+static void inc_td_cnt(struct urb *urb)
+{
+ struct urb_priv *urb_priv = urb->hcpriv;
+
+ urb_priv->num_tds_done++;
+}
+
+static void trb_to_noop(union xhci_trb *trb, u32 noop_type)
+{
+ if (trb_is_link(trb)) {
+ /* unchain chained link TRBs */
+ trb->link.control &= cpu_to_le32(~TRB_CHAIN);
+ } else {
+ trb->generic.field[0] = 0;
+ trb->generic.field[1] = 0;
+ trb->generic.field[2] = 0;
+ /* Preserve only the cycle bit of this TRB */
+ trb->generic.field[3] &= cpu_to_le32(TRB_CYCLE);
+ trb->generic.field[3] |= cpu_to_le32(TRB_TYPE(noop_type));
+ }
+}
+
/* Updates trb to point to the next TRB in the ring, and updates seg if the next
* TRB is in a new segment. This does not skip over link TRBs, and it does not
* effect the ring dequeue or enqueue pointers.
@@ -260,23 +294,68 @@ void xhci_ring_cmd_db(struct xhci_hcd *xhci)
readl(&xhci->dba->doorbell[0]);
}
-static int xhci_abort_cmd_ring(struct xhci_hcd *xhci)
+static bool xhci_mod_cmd_timer(struct xhci_hcd *xhci, unsigned long delay)
+{
+ return mod_delayed_work(system_wq, &xhci->cmd_timer, delay);
+}
+
+static struct xhci_command *xhci_next_queued_cmd(struct xhci_hcd *xhci)
+{
+ return list_first_entry_or_null(&xhci->cmd_list, struct xhci_command,
+ cmd_list);
+}
+
+/*
+ * Turn all commands on command ring with status set to "aborted" to no-op trbs.
+ * If there are other commands waiting then restart the ring and kick the timer.
+ * This must be called with command ring stopped and xhci->lock held.
+ */
+static void xhci_handle_stopped_cmd_ring(struct xhci_hcd *xhci,
+ struct xhci_command *cur_cmd)
+{
+ struct xhci_command *i_cmd;
+
+ /* Turn all aborted commands in list to no-ops, then restart */
+ list_for_each_entry(i_cmd, &xhci->cmd_list, cmd_list) {
+
+ if (i_cmd->status != COMP_COMMAND_ABORTED)
+ continue;
+
+ i_cmd->status = COMP_STOPPED;
+
+ xhci_dbg(xhci, "Turn aborted command %p to no-op\n",
+ i_cmd->command_trb);
+
+ trb_to_noop(i_cmd->command_trb, TRB_CMD_NOOP);
+
+ /*
+ * caller waiting for completion is called when command
+ * completion event is received for these no-op commands
+ */
+ }
+
+ xhci->cmd_ring_state = CMD_RING_STATE_RUNNING;
+
+ /* ring command ring doorbell to restart the command ring */
+ if ((xhci->cmd_ring->dequeue != xhci->cmd_ring->enqueue) &&
+ !(xhci->xhc_state & XHCI_STATE_DYING)) {
+ xhci->current_cmd = cur_cmd;
+ xhci_mod_cmd_timer(xhci, XHCI_CMD_DEFAULT_TIMEOUT);
+ xhci_ring_cmd_db(xhci);
+ }
+}
+
+/* Must be called with xhci->lock held, releases and aquires lock back */
+static int xhci_abort_cmd_ring(struct xhci_hcd *xhci, unsigned long flags)
{
u64 temp_64;
int ret;
xhci_dbg(xhci, "Abort command ring\n");
- temp_64 = xhci_read_64(xhci, &xhci->op_regs->cmd_ring);
- xhci->cmd_ring_state = CMD_RING_STATE_ABORTED;
+ reinit_completion(&xhci->cmd_ring_stop_completion);
- /*
- * Writing the CMD_RING_ABORT bit should cause a cmd completion event,
- * however on some host hw the CMD_RING_RUNNING bit is correctly cleared
- * but the completion event in never sent. Use the cmd timeout timer to
- * handle those cases. Use twice the time to cover the bit polling retry
- */
- mod_timer(&xhci->cmd_timer, jiffies + (2 * XHCI_CMD_DEFAULT_TIMEOUT));
+ temp_64 = xhci_read_64(xhci, &xhci->op_regs->cmd_ring);
xhci_write_64(xhci, temp_64 | CMD_RING_ABORT,
&xhci->op_regs->cmd_ring);
@@ -290,24 +369,28 @@ static int xhci_abort_cmd_ring(struct xhci_hcd *xhci)
ret = xhci_handshake(&xhci->op_regs->cmd_ring,
CMD_RING_RUNNING, 0, 5 * 1000 * 1000);
if (ret < 0) {
- /* we are about to kill xhci, give it one more chance */
- xhci_write_64(xhci, temp_64 | CMD_RING_ABORT,
- &xhci->op_regs->cmd_ring);
- udelay(1000);
- ret = xhci_handshake(&xhci->op_regs->cmd_ring,
- CMD_RING_RUNNING, 0, 3 * 1000 * 1000);
- if (ret == 0)
- return 0;
-
- xhci_err(xhci, "Stopped the command ring failed, "
- "maybe the host is dead\n");
- del_timer(&xhci->cmd_timer);
+ xhci_err(xhci,
+ "Stop command ring failed, maybe the host is dead\n");
xhci->xhc_state |= XHCI_STATE_DYING;
- xhci_quiesce(xhci);
xhci_halt(xhci);
return -ESHUTDOWN;
}
-
+ /*
+ * Writing the CMD_RING_ABORT bit should cause a cmd completion event,
+ * however on some host hw the CMD_RING_RUNNING bit is correctly cleared
+ * but the completion event in never sent. Wait 2 secs (arbitrary
+ * number) to handle those cases after negation of CMD_RING_RUNNING.
+ */
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ ret = wait_for_completion_timeout(&xhci->cmd_ring_stop_completion,
+ msecs_to_jiffies(2000));
+ spin_lock_irqsave(&xhci->lock, flags);
+ if (!ret) {
+ xhci_dbg(xhci, "No stop event for abort, ring start fail?\n");
+ xhci_cleanup_command_queue(xhci);
+ } else {
+ xhci_handle_stopped_cmd_ring(xhci, xhci_next_queued_cmd(xhci));
+ }
return 0;
}
@@ -326,7 +409,7 @@ void xhci_ring_ep_doorbell(struct xhci_hcd *xhci,
* pointer command pending because the device can choose to start any
* stream once the endpoint is on the HW schedule.
*/
- if ((ep_state & EP_HALT_PENDING) || (ep_state & SET_DEQ_PENDING) ||
+ if ((ep_state & EP_STOP_CMD_PENDING) || (ep_state & SET_DEQ_PENDING) ||
(ep_state & EP_HALTED))
return;
writel(DB_VALUE(ep_index, stream_id), db_addr);
@@ -473,9 +556,8 @@ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci,
if (new_deq == cur_td->last_trb)
td_last_trb_found = true;
- if (cycle_found &&
- TRB_TYPE_LINK_LE32(new_deq->generic.field[3]) &&
- new_deq->generic.field[3] & cpu_to_le32(LINK_TOGGLE))
+ if (cycle_found && trb_is_link(new_deq) &&
+ link_trb_toggles_cycle(new_deq))
state->new_cycle_state ^= 0x1;
next_trb(xhci, ep_ring, &new_seg, &new_deq);
@@ -511,108 +593,67 @@ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci,
* of this TD.) This is used to remove partially enqueued isoc TDs from a ring.
*/
static void td_to_noop(struct xhci_hcd *xhci, struct xhci_ring *ep_ring,
- struct xhci_td *cur_td, bool flip_cycle)
+ struct xhci_td *td, bool flip_cycle)
{
- struct xhci_segment *cur_seg;
- union xhci_trb *cur_trb;
-
- for (cur_seg = cur_td->start_seg, cur_trb = cur_td->first_trb;
- true;
- next_trb(xhci, ep_ring, &cur_seg, &cur_trb)) {
- if (TRB_TYPE_LINK_LE32(cur_trb->generic.field[3])) {
- /* Unchain any chained Link TRBs, but
- * leave the pointers intact.
- */
- cur_trb->generic.field[3] &= cpu_to_le32(~TRB_CHAIN);
- /* Flip the cycle bit (link TRBs can't be the first
- * or last TRB).
- */
- if (flip_cycle)
- cur_trb->generic.field[3] ^=
- cpu_to_le32(TRB_CYCLE);
- xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
- "Cancel (unchain) link TRB");
- xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
- "Address = %p (0x%llx dma); "
- "in seg %p (0x%llx dma)",
- cur_trb,
- (unsigned long long)xhci_trb_virt_to_dma(cur_seg, cur_trb),
- cur_seg,
- (unsigned long long)cur_seg->dma);
- } else {
- cur_trb->generic.field[0] = 0;
- cur_trb->generic.field[1] = 0;
- cur_trb->generic.field[2] = 0;
- /* Preserve only the cycle bit of this TRB */
- cur_trb->generic.field[3] &= cpu_to_le32(TRB_CYCLE);
- /* Flip the cycle bit except on the first or last TRB */
- if (flip_cycle && cur_trb != cur_td->first_trb &&
- cur_trb != cur_td->last_trb)
- cur_trb->generic.field[3] ^=
- cpu_to_le32(TRB_CYCLE);
- cur_trb->generic.field[3] |= cpu_to_le32(
- TRB_TYPE(TRB_TR_NOOP));
- xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
- "TRB to noop at offset 0x%llx",
- (unsigned long long)
- xhci_trb_virt_to_dma(cur_seg, cur_trb));
- }
- if (cur_trb == cur_td->last_trb)
+ struct xhci_segment *seg = td->start_seg;
+ union xhci_trb *trb = td->first_trb;
+
+ while (1) {
+ trb_to_noop(trb, TRB_TR_NOOP);
+
+ /* flip cycle if asked to */
+ if (flip_cycle && trb != td->first_trb && trb != td->last_trb)
+ trb->generic.field[3] ^= cpu_to_le32(TRB_CYCLE);
+
+ if (trb == td->last_trb)
break;
+
+ next_trb(xhci, ep_ring, &seg, &trb);
}
}
static void xhci_stop_watchdog_timer_in_irq(struct xhci_hcd *xhci,
struct xhci_virt_ep *ep)
{
- ep->ep_state &= ~EP_HALT_PENDING;
- /* Can't del_timer_sync in interrupt, so we attempt to cancel. If the
- * timer is running on another CPU, we don't decrement stop_cmds_pending
- * (since we didn't successfully stop the watchdog timer).
- */
- if (del_timer(&ep->stop_cmd_timer))
- ep->stop_cmds_pending--;
+ ep->ep_state &= ~EP_STOP_CMD_PENDING;
+ /* Can't del_timer_sync in interrupt */
+ del_timer(&ep->stop_cmd_timer);
}
-/* Must be called with xhci->lock held in interrupt context */
+/*
+ * Must be called with xhci->lock held in interrupt context,
+ * releases and re-acquires xhci->lock
+ */
static void xhci_giveback_urb_in_irq(struct xhci_hcd *xhci,
- struct xhci_td *cur_td, int status)
+ struct xhci_td *cur_td, int status)
{
- struct usb_hcd *hcd;
- struct urb *urb;
- struct urb_priv *urb_priv;
+ struct urb *urb = cur_td->urb;
+ struct urb_priv *urb_priv = urb->hcpriv;
+ struct usb_hcd *hcd = bus_to_hcd(urb->dev->bus);
- urb = cur_td->urb;
- urb_priv = urb->hcpriv;
- urb_priv->td_cnt++;
- hcd = bus_to_hcd(urb->dev->bus);
-
- /* Only giveback urb when this is the last td in urb */
- if (urb_priv->td_cnt == urb_priv->length) {
- if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
- xhci_to_hcd(xhci)->self.bandwidth_isoc_reqs--;
- if (xhci_to_hcd(xhci)->self.bandwidth_isoc_reqs == 0) {
- if (xhci->quirks & XHCI_AMD_PLL_FIX)
- usb_amd_quirk_pll_enable();
- }
+ if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+ xhci_to_hcd(xhci)->self.bandwidth_isoc_reqs--;
+ if (xhci_to_hcd(xhci)->self.bandwidth_isoc_reqs == 0) {
+ if (xhci->quirks & XHCI_AMD_PLL_FIX)
+ usb_amd_quirk_pll_enable();
}
- usb_hcd_unlink_urb_from_ep(hcd, urb);
-
- spin_unlock(&xhci->lock);
- usb_hcd_giveback_urb(hcd, urb, status);
- xhci_urb_free_priv(urb_priv);
- spin_lock(&xhci->lock);
}
+ xhci_urb_free_priv(urb_priv);
+ usb_hcd_unlink_urb_from_ep(hcd, urb);
+ spin_unlock(&xhci->lock);
+ usb_hcd_giveback_urb(hcd, urb, status);
+ trace_xhci_urb_giveback(urb);
+ spin_lock(&xhci->lock);
}
-void xhci_unmap_td_bounce_buffer(struct xhci_hcd *xhci, struct xhci_ring *ring,
- struct xhci_td *td)
+static void xhci_unmap_td_bounce_buffer(struct xhci_hcd *xhci,
+ struct xhci_ring *ring, struct xhci_td *td)
{
struct device *dev = xhci_to_hcd(xhci)->self.controller;
struct xhci_segment *seg = td->bounce_seg;
struct urb *urb = td->urb;
- if (!seg || !urb)
+ if (!ring || !seg || !urb)
return;
if (usb_urb_dir_out(urb)) {
@@ -646,7 +687,6 @@ static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, int slot_id,
unsigned int ep_index;
struct xhci_ring *ep_ring;
struct xhci_virt_ep *ep;
- struct list_head *entry;
struct xhci_td *cur_td = NULL;
struct xhci_td *last_unlinked_td;
@@ -663,6 +703,8 @@ static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, int slot_id,
memset(&deq_state, 0, sizeof(deq_state));
ep_index = TRB_TO_EP_INDEX(le32_to_cpu(trb->generic.field[3]));
ep = &xhci->devs[slot_id]->eps[ep_index];
+ last_unlinked_td = list_last_entry(&ep->cancelled_td_list,
+ struct xhci_td, cancelled_td_list);
if (list_empty(&ep->cancelled_td_list)) {
xhci_stop_watchdog_timer_in_irq(xhci, ep);
@@ -676,8 +718,7 @@ static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, int slot_id,
* it. We're also in the event handler, so we can't get re-interrupted
* if another Stop Endpoint command completes
*/
- list_for_each(entry, &ep->cancelled_td_list) {
- cur_td = list_entry(entry, struct xhci_td, cancelled_td_list);
+ list_for_each_entry(cur_td, &ep->cancelled_td_list, cancelled_td_list) {
xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
"Removing canceled TD starting at 0x%llx (dma).",
(unsigned long long)xhci_trb_virt_to_dma(
@@ -719,7 +760,7 @@ remove_finished_td:
*/
list_del_init(&cur_td->td_list);
}
- last_unlinked_td = cur_td;
+
xhci_stop_watchdog_timer_in_irq(xhci, ep);
/* If necessary, queue a Set Transfer Ring Dequeue Pointer command */
@@ -741,7 +782,7 @@ remove_finished_td:
* So stop when we've completed the URB for the last TD we unlinked.
*/
do {
- cur_td = list_entry(ep->cancelled_td_list.next,
+ cur_td = list_first_entry(&ep->cancelled_td_list,
struct xhci_td, cancelled_td_list);
list_del_init(&cur_td->cancelled_td_list);
@@ -750,9 +791,10 @@ remove_finished_td:
* just overwrite it (because the URB has been unlinked).
*/
ep_ring = xhci_urb_to_transfer_ring(xhci, cur_td->urb);
- if (ep_ring && cur_td->bounce_seg)
- xhci_unmap_td_bounce_buffer(xhci, ep_ring, cur_td);
- xhci_giveback_urb_in_irq(xhci, cur_td, 0);
+ xhci_unmap_td_bounce_buffer(xhci, ep_ring, cur_td);
+ inc_td_cnt(cur_td->urb);
+ if (last_td_in_urb(cur_td))
+ xhci_giveback_urb_in_irq(xhci, cur_td, 0);
/* Stop processing the cancelled list if the watchdog timer is
* running.
@@ -767,17 +809,19 @@ remove_finished_td:
static void xhci_kill_ring_urbs(struct xhci_hcd *xhci, struct xhci_ring *ring)
{
struct xhci_td *cur_td;
+ struct xhci_td *tmp;
- while (!list_empty(&ring->td_list)) {
- cur_td = list_first_entry(&ring->td_list,
- struct xhci_td, td_list);
+ list_for_each_entry_safe(cur_td, tmp, &ring->td_list, td_list) {
list_del_init(&cur_td->td_list);
+
if (!list_empty(&cur_td->cancelled_td_list))
list_del_init(&cur_td->cancelled_td_list);
- if (cur_td->bounce_seg)
- xhci_unmap_td_bounce_buffer(xhci, ring, cur_td);
- xhci_giveback_urb_in_irq(xhci, cur_td, -ESHUTDOWN);
+ xhci_unmap_td_bounce_buffer(xhci, ring, cur_td);
+
+ inc_td_cnt(cur_td->urb);
+ if (last_td_in_urb(cur_td))
+ xhci_giveback_urb_in_irq(xhci, cur_td, -ESHUTDOWN);
}
}
@@ -785,6 +829,7 @@ static void xhci_kill_endpoint_urbs(struct xhci_hcd *xhci,
int slot_id, int ep_index)
{
struct xhci_td *cur_td;
+ struct xhci_td *tmp;
struct xhci_virt_ep *ep;
struct xhci_ring *ring;
@@ -810,11 +855,14 @@ static void xhci_kill_endpoint_urbs(struct xhci_hcd *xhci,
slot_id, ep_index);
xhci_kill_ring_urbs(xhci, ring);
}
- while (!list_empty(&ep->cancelled_td_list)) {
- cur_td = list_first_entry(&ep->cancelled_td_list,
- struct xhci_td, cancelled_td_list);
+
+ list_for_each_entry_safe(cur_td, tmp, &ep->cancelled_td_list,
+ cancelled_td_list) {
list_del_init(&cur_td->cancelled_td_list);
- xhci_giveback_urb_in_irq(xhci, cur_td, -ESHUTDOWN);
+ inc_td_cnt(cur_td->urb);
+
+ if (last_td_in_urb(cur_td))
+ xhci_giveback_urb_in_irq(xhci, cur_td, -ESHUTDOWN);
}
}
@@ -832,10 +880,8 @@ static void xhci_kill_endpoint_urbs(struct xhci_hcd *xhci,
* simple flag to say whether there is a pending stop endpoint command for a
* particular endpoint.
*
- * Instead we use a combination of that flag and a counter for the number of
- * pending stop endpoint commands. If the timer is the tail end of the last
- * stop endpoint command, and the endpoint's command is still pending, we assume
- * the host is dying.
+ * Instead we use a combination of that flag and checking if a new timer is
+ * pending.
*/
void xhci_stop_endpoint_command_watchdog(unsigned long arg)
{
@@ -849,23 +895,11 @@ void xhci_stop_endpoint_command_watchdog(unsigned long arg)
spin_lock_irqsave(&xhci->lock, flags);
- ep->stop_cmds_pending--;
- if (xhci->xhc_state & XHCI_STATE_REMOVING) {
- spin_unlock_irqrestore(&xhci->lock, flags);
- return;
- }
- if (xhci->xhc_state & XHCI_STATE_DYING) {
- xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
- "Stop EP timer ran, but another timer marked "
- "xHCI as DYING, exiting.");
- spin_unlock_irqrestore(&xhci->lock, flags);
- return;
- }
- if (!(ep->stop_cmds_pending == 0 && (ep->ep_state & EP_HALT_PENDING))) {
- xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
- "Stop EP timer ran, but no command pending, "
- "exiting.");
+ /* bail out if cmd completed but raced with stop ep watchdog timer.*/
+ if (!(ep->ep_state & EP_STOP_CMD_PENDING) ||
+ timer_pending(&ep->stop_cmd_timer)) {
spin_unlock_irqrestore(&xhci->lock, flags);
+ xhci_dbg(xhci, "Stop EP timer raced with cmd completion, exit");
return;
}
@@ -874,7 +908,10 @@ void xhci_stop_endpoint_command_watchdog(unsigned long arg)
/* Oops, HC is dead or dying or at least not responding to the stop
* endpoint command.
*/
+
xhci->xhc_state |= XHCI_STATE_DYING;
+ ep->ep_state &= ~EP_STOP_CMD_PENDING;
+
/* Disable interrupts from the host controller and start halting it */
xhci_quiesce(xhci);
spin_unlock_irqrestore(&xhci->lock, flags);
@@ -998,20 +1035,19 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id,
unsigned int slot_state;
switch (cmd_comp_code) {
- case COMP_TRB_ERR:
+ case COMP_TRB_ERROR:
xhci_warn(xhci, "WARN Set TR Deq Ptr cmd invalid because of stream ID configuration\n");
break;
- case COMP_CTX_STATE:
+ case COMP_CONTEXT_STATE_ERROR:
xhci_warn(xhci, "WARN Set TR Deq Ptr cmd failed due to incorrect slot or ep state.\n");
- ep_state = le32_to_cpu(ep_ctx->ep_info);
- ep_state &= EP_STATE_MASK;
+ ep_state = GET_EP_CTX_STATE(ep_ctx);
slot_state = le32_to_cpu(slot_ctx->dev_state);
slot_state = GET_SLOT_STATE(slot_state);
xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
"Slot state = %u, EP state = %u",
slot_state, ep_state);
break;
- case COMP_EBADSLT:
+ case COMP_SLOT_NOT_ENABLED_ERROR:
xhci_warn(xhci, "WARN Set TR Deq Ptr cmd failed because slot %u was not enabled.\n",
slot_id);
break;
@@ -1096,12 +1132,12 @@ static void xhci_handle_cmd_reset_ep(struct xhci_hcd *xhci, int slot_id,
}
static void xhci_handle_cmd_enable_slot(struct xhci_hcd *xhci, int slot_id,
- u32 cmd_comp_code)
+ struct xhci_command *command, u32 cmd_comp_code)
{
if (cmd_comp_code == COMP_SUCCESS)
- xhci->slot_id = slot_id;
+ command->slot_id = slot_id;
else
- xhci->slot_id = 0;
+ command->slot_id = 0;
}
static void xhci_handle_cmd_disable_slot(struct xhci_hcd *xhci, int slot_id)
@@ -1183,7 +1219,7 @@ static void xhci_handle_cmd_nec_get_fw(struct xhci_hcd *xhci,
struct xhci_event_cmd *event)
{
if (!(xhci->quirks & XHCI_NEC_HOST)) {
- xhci->error_bitmask |= 1 << 6;
+ xhci_warn(xhci, "WARN NEC_GET_FW command on non-NEC host\n");
return;
}
xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
@@ -1208,104 +1244,65 @@ void xhci_cleanup_command_queue(struct xhci_hcd *xhci)
{
struct xhci_command *cur_cmd, *tmp_cmd;
list_for_each_entry_safe(cur_cmd, tmp_cmd, &xhci->cmd_list, cmd_list)
- xhci_complete_del_and_free_cmd(cur_cmd, COMP_CMD_ABORT);
-}
-
-/*
- * Turn all commands on command ring with status set to "aborted" to no-op trbs.
- * If there are other commands waiting then restart the ring and kick the timer.
- * This must be called with command ring stopped and xhci->lock held.
- */
-static void xhci_handle_stopped_cmd_ring(struct xhci_hcd *xhci,
- struct xhci_command *cur_cmd)
-{
- struct xhci_command *i_cmd, *tmp_cmd;
- u32 cycle_state;
-
- /* Turn all aborted commands in list to no-ops, then restart */
- list_for_each_entry_safe(i_cmd, tmp_cmd, &xhci->cmd_list,
- cmd_list) {
-
- if (i_cmd->status != COMP_CMD_ABORT)
- continue;
-
- i_cmd->status = COMP_CMD_STOP;
-
- xhci_dbg(xhci, "Turn aborted command %p to no-op\n",
- i_cmd->command_trb);
- /* get cycle state from the original cmd trb */
- cycle_state = le32_to_cpu(
- i_cmd->command_trb->generic.field[3]) & TRB_CYCLE;
- /* modify the command trb to no-op command */
- i_cmd->command_trb->generic.field[0] = 0;
- i_cmd->command_trb->generic.field[1] = 0;
- i_cmd->command_trb->generic.field[2] = 0;
- i_cmd->command_trb->generic.field[3] = cpu_to_le32(
- TRB_TYPE(TRB_CMD_NOOP) | cycle_state);
-
- /*
- * caller waiting for completion is called when command
- * completion event is received for these no-op commands
- */
- }
-
- xhci->cmd_ring_state = CMD_RING_STATE_RUNNING;
-
- /* ring command ring doorbell to restart the command ring */
- if ((xhci->cmd_ring->dequeue != xhci->cmd_ring->enqueue) &&
- !(xhci->xhc_state & XHCI_STATE_DYING)) {
- xhci->current_cmd = cur_cmd;
- mod_timer(&xhci->cmd_timer, jiffies + XHCI_CMD_DEFAULT_TIMEOUT);
- xhci_ring_cmd_db(xhci);
- }
- return;
+ xhci_complete_del_and_free_cmd(cur_cmd, COMP_COMMAND_ABORTED);
}
-
-void xhci_handle_command_timeout(unsigned long data)
+void xhci_handle_command_timeout(struct work_struct *work)
{
struct xhci_hcd *xhci;
int ret;
unsigned long flags;
u64 hw_ring_state;
- bool second_timeout = false;
- xhci = (struct xhci_hcd *) data;
- /* mark this command to be cancelled */
+ xhci = container_of(to_delayed_work(work), struct xhci_hcd, cmd_timer);
+
spin_lock_irqsave(&xhci->lock, flags);
- if (xhci->current_cmd) {
- if (xhci->current_cmd->status == COMP_CMD_ABORT)
- second_timeout = true;
- xhci->current_cmd->status = COMP_CMD_ABORT;
+
+ /*
+ * If timeout work is pending, or current_cmd is NULL, it means we
+ * raced with command completion. Command is handled so just return.
+ */
+ if (!xhci->current_cmd || delayed_work_pending(&xhci->cmd_timer)) {
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ return;
}
+ /* mark this command to be cancelled */
+ xhci->current_cmd->status = COMP_COMMAND_ABORTED;
/* Make sure command ring is running before aborting it */
hw_ring_state = xhci_read_64(xhci, &xhci->op_regs->cmd_ring);
if ((xhci->cmd_ring_state & CMD_RING_STATE_RUNNING) &&
(hw_ring_state & CMD_RING_RUNNING)) {
- spin_unlock_irqrestore(&xhci->lock, flags);
+ /* Prevent new doorbell, and start command abort */
+ xhci->cmd_ring_state = CMD_RING_STATE_ABORTED;
xhci_dbg(xhci, "Command timeout\n");
- ret = xhci_abort_cmd_ring(xhci);
+ ret = xhci_abort_cmd_ring(xhci, flags);
if (unlikely(ret == -ESHUTDOWN)) {
xhci_err(xhci, "Abort command ring failed\n");
xhci_cleanup_command_queue(xhci);
+ spin_unlock_irqrestore(&xhci->lock, flags);
usb_hc_died(xhci_to_hcd(xhci)->primary_hcd);
xhci_dbg(xhci, "xHCI host controller is dead.\n");
+
+ return;
}
- return;
+
+ goto time_out_completed;
}
- /* command ring failed to restart, or host removed. Bail out */
- if (second_timeout || xhci->xhc_state & XHCI_STATE_REMOVING) {
- spin_unlock_irqrestore(&xhci->lock, flags);
- xhci_dbg(xhci, "command timed out twice, ring start fail?\n");
+ /* host removed. Bail out */
+ if (xhci->xhc_state & XHCI_STATE_REMOVING) {
+ xhci_dbg(xhci, "host removed, ring start fail?\n");
xhci_cleanup_command_queue(xhci);
- return;
+
+ goto time_out_completed;
}
/* command timeout on stopped ring, ring can't be aborted */
xhci_dbg(xhci, "Command timeout on stopped ring\n");
xhci_handle_stopped_cmd_ring(xhci, xhci->current_cmd);
+
+time_out_completed:
spin_unlock_irqrestore(&xhci->lock, flags);
return;
}
@@ -1323,30 +1320,30 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
cmd_dma = le64_to_cpu(event->cmd_trb);
cmd_trb = xhci->cmd_ring->dequeue;
+
+ trace_xhci_handle_command(xhci->cmd_ring, &cmd_trb->generic);
+
cmd_dequeue_dma = xhci_trb_virt_to_dma(xhci->cmd_ring->deq_seg,
cmd_trb);
- /* Is the command ring deq ptr out of sync with the deq seg ptr? */
- if (cmd_dequeue_dma == 0) {
- xhci->error_bitmask |= 1 << 4;
- return;
- }
- /* Does the DMA address match our internal dequeue pointer address? */
- if (cmd_dma != (u64) cmd_dequeue_dma) {
- xhci->error_bitmask |= 1 << 5;
+ /*
+ * Check whether the completion event is for our internal kept
+ * command.
+ */
+ if (!cmd_dequeue_dma || cmd_dma != (u64)cmd_dequeue_dma) {
+ xhci_warn(xhci,
+ "ERROR mismatched command completion event\n");
return;
}
- cmd = list_entry(xhci->cmd_list.next, struct xhci_command, cmd_list);
+ cmd = list_first_entry(&xhci->cmd_list, struct xhci_command, cmd_list);
- del_timer(&xhci->cmd_timer);
-
- trace_xhci_cmd_completion(cmd_trb, (struct xhci_generic_trb *) event);
+ cancel_delayed_work(&xhci->cmd_timer);
cmd_comp_code = GET_COMP_CODE(le32_to_cpu(event->status));
/* If CMD ring stopped we own the trbs between enqueue and dequeue */
- if (cmd_comp_code == COMP_CMD_STOP) {
- xhci_handle_stopped_cmd_ring(xhci, cmd);
+ if (cmd_comp_code == COMP_STOPPED) {
+ complete_all(&xhci->cmd_ring_stop_completion);
return;
}
@@ -1362,16 +1359,19 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
* The command ring is stopped now, but the xHC will issue a Command
* Ring Stopped event which will cause us to restart it.
*/
- if (cmd_comp_code == COMP_CMD_ABORT) {
+ if (cmd_comp_code == COMP_COMMAND_ABORTED) {
xhci->cmd_ring_state = CMD_RING_STATE_STOPPED;
- if (cmd->status == COMP_CMD_ABORT)
+ if (cmd->status == COMP_COMMAND_ABORTED) {
+ if (xhci->current_cmd == cmd)
+ xhci->current_cmd = NULL;
goto event_handled;
+ }
}
cmd_type = TRB_FIELD_TO_TYPE(le32_to_cpu(cmd_trb->generic.field[3]));
switch (cmd_type) {
case TRB_ENABLE_SLOT:
- xhci_handle_cmd_enable_slot(xhci, slot_id, cmd_comp_code);
+ xhci_handle_cmd_enable_slot(xhci, slot_id, cmd, cmd_comp_code);
break;
case TRB_DISABLE_SLOT:
xhci_handle_cmd_disable_slot(xhci, slot_id);
@@ -1397,8 +1397,8 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
break;
case TRB_CMD_NOOP:
/* Is this an aborted command turned to NO-OP? */
- if (cmd->status == COMP_CMD_STOP)
- cmd_comp_code = COMP_CMD_STOP;
+ if (cmd->status == COMP_STOPPED)
+ cmd_comp_code = COMP_STOPPED;
break;
case TRB_RESET_EP:
WARN_ON(slot_id != TRB_TO_SLOT_ID(
@@ -1418,15 +1418,17 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
break;
default:
/* Skip over unknown commands on the event ring */
- xhci->error_bitmask |= 1 << 6;
+ xhci_info(xhci, "INFO unknown command type %d\n", cmd_type);
break;
}
/* restart timer if this wasn't the last command */
- if (cmd->cmd_list.next != &xhci->cmd_list) {
- xhci->current_cmd = list_entry(cmd->cmd_list.next,
- struct xhci_command, cmd_list);
- mod_timer(&xhci->cmd_timer, jiffies + XHCI_CMD_DEFAULT_TIMEOUT);
+ if (!list_is_singular(&xhci->cmd_list)) {
+ xhci->current_cmd = list_first_entry(&cmd->cmd_list,
+ struct xhci_command, cmd_list);
+ xhci_mod_cmd_timer(xhci, XHCI_CMD_DEFAULT_TIMEOUT);
+ } else if (xhci->current_cmd == cmd) {
+ xhci->current_cmd = NULL;
}
event_handled:
@@ -1519,10 +1521,10 @@ static void handle_port_status(struct xhci_hcd *xhci,
bool bogus_port_status = false;
/* Port status change events always have a successful completion code */
- if (GET_COMP_CODE(le32_to_cpu(event->generic.field[2])) != COMP_SUCCESS) {
- xhci_warn(xhci, "WARN: xHC returned failed port status event\n");
- xhci->error_bitmask |= 1 << 8;
- }
+ if (GET_COMP_CODE(le32_to_cpu(event->generic.field[2])) != COMP_SUCCESS)
+ xhci_warn(xhci,
+ "WARN: xHC returned failed port status event\n");
+
port_id = GET_PORT_ID(le32_to_cpu(event->generic.field[0]));
xhci_dbg(xhci, "Port Status Change Event for port %d\n", port_id);
@@ -1759,7 +1761,7 @@ struct xhci_segment *trb_in_td(struct xhci_hcd *xhci,
static void xhci_cleanup_halted_endpoint(struct xhci_hcd *xhci,
unsigned int slot_id, unsigned int ep_index,
unsigned int stream_id,
- struct xhci_td *td, union xhci_trb *event_trb)
+ struct xhci_td *td, union xhci_trb *ep_trb)
{
struct xhci_virt_ep *ep = &xhci->devs[slot_id]->eps[ep_index];
struct xhci_command *command;
@@ -1789,17 +1791,16 @@ static int xhci_requires_manual_halt_cleanup(struct xhci_hcd *xhci,
unsigned int trb_comp_code)
{
/* TRB completion codes that may require a manual halt cleanup */
- if (trb_comp_code == COMP_TX_ERR ||
- trb_comp_code == COMP_BABBLE ||
- trb_comp_code == COMP_SPLIT_ERR)
+ if (trb_comp_code == COMP_USB_TRANSACTION_ERROR ||
+ trb_comp_code == COMP_BABBLE_DETECTED_ERROR ||
+ trb_comp_code == COMP_SPLIT_TRANSACTION_ERROR)
/* The 0.95 spec says a babbling control endpoint
* is not halted. The 0.96 spec says it is. Some HW
* claims to be 0.95 compliant, but it halts the control
* endpoint anyway. Check if a babble halted the
* endpoint.
*/
- if ((ep_ctx->ep_info & cpu_to_le32(EP_STATE_MASK)) ==
- cpu_to_le32(EP_STATE_HALTED))
+ if (GET_EP_CTX_STATE(ep_ctx) == EP_STATE_HALTED)
return 1;
return 0;
@@ -1819,23 +1820,64 @@ int xhci_is_vendor_info_code(struct xhci_hcd *xhci, unsigned int trb_comp_code)
return 0;
}
-/*
- * Finish the td processing, remove the td from td list;
- * Return 1 if the urb can be given back.
- */
+static int xhci_td_cleanup(struct xhci_hcd *xhci, struct xhci_td *td,
+ struct xhci_ring *ep_ring, int *status)
+{
+ struct urb_priv *urb_priv;
+ struct urb *urb = NULL;
+
+ /* Clean up the endpoint's TD list */
+ urb = td->urb;
+ urb_priv = urb->hcpriv;
+
+ /* if a bounce buffer was used to align this td then unmap it */
+ xhci_unmap_td_bounce_buffer(xhci, ep_ring, td);
+
+ /* Do one last check of the actual transfer length.
+ * If the host controller said we transferred more data than the buffer
+ * length, urb->actual_length will be a very big number (since it's
+ * unsigned). Play it safe and say we didn't transfer anything.
+ */
+ if (urb->actual_length > urb->transfer_buffer_length) {
+ xhci_warn(xhci, "URB req %u and actual %u transfer length mismatch\n",
+ urb->transfer_buffer_length, urb->actual_length);
+ urb->actual_length = 0;
+ *status = 0;
+ }
+ list_del_init(&td->td_list);
+ /* Was this TD slated to be cancelled but completed anyway? */
+ if (!list_empty(&td->cancelled_td_list))
+ list_del_init(&td->cancelled_td_list);
+
+ inc_td_cnt(urb);
+ /* Giveback the urb when all the tds are completed */
+ if (last_td_in_urb(td)) {
+ if ((urb->actual_length != urb->transfer_buffer_length &&
+ (urb->transfer_flags & URB_SHORT_NOT_OK)) ||
+ (*status != 0 && !usb_endpoint_xfer_isoc(&urb->ep->desc)))
+ xhci_dbg(xhci, "Giveback URB %p, len = %d, expected = %d, status = %d\n",
+ urb, urb->actual_length,
+ urb->transfer_buffer_length, *status);
+
+ /* set isoc urb status to 0 just as EHCI, UHCI, and OHCI */
+ if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS)
+ *status = 0;
+ xhci_giveback_urb_in_irq(xhci, td, *status);
+ }
+
+ return 0;
+}
+
static int finish_td(struct xhci_hcd *xhci, struct xhci_td *td,
- union xhci_trb *event_trb, struct xhci_transfer_event *event,
+ union xhci_trb *ep_trb, struct xhci_transfer_event *event,
struct xhci_virt_ep *ep, int *status, bool skip)
{
struct xhci_virt_device *xdev;
+ struct xhci_ep_ctx *ep_ctx;
struct xhci_ring *ep_ring;
unsigned int slot_id;
- int ep_index;
- struct urb *urb = NULL;
- struct xhci_ep_ctx *ep_ctx;
- int ret = 0;
- struct urb_priv *urb_priv;
u32 trb_comp_code;
+ int ep_index;
slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags));
xdev = xhci->devs[slot_id];
@@ -1847,9 +1889,9 @@ static int finish_td(struct xhci_hcd *xhci, struct xhci_td *td,
if (skip)
goto td_cleanup;
- if (trb_comp_code == COMP_STOP_INVAL ||
- trb_comp_code == COMP_STOP ||
- trb_comp_code == COMP_STOP_SHORT) {
+ if (trb_comp_code == COMP_STOPPED_LENGTH_INVALID ||
+ trb_comp_code == COMP_STOPPED ||
+ trb_comp_code == COMP_STOPPED_SHORT_PACKET) {
/* The Endpoint Stop Command completion will take care of any
* stopped TDs. A stopped TD may be restarted, so don't update
* the ring dequeue pointer or take this TD off any lists yet.
@@ -1857,7 +1899,7 @@ static int finish_td(struct xhci_hcd *xhci, struct xhci_td *td,
ep->stopped_td = td;
return 0;
}
- if (trb_comp_code == COMP_STALL ||
+ if (trb_comp_code == COMP_STALL_ERROR ||
xhci_requires_manual_halt_cleanup(xhci, ep_ctx,
trb_comp_code)) {
/* Issue a reset endpoint command to clear the host side
@@ -1866,7 +1908,7 @@ static int finish_td(struct xhci_hcd *xhci, struct xhci_td *td,
* The class driver clears the device side halt later.
*/
xhci_cleanup_halted_endpoint(xhci, slot_id, ep_index,
- ep_ring->stream_id, td, event_trb);
+ ep_ring->stream_id, td, ep_trb);
} else {
/* Update ring dequeue pointer */
while (ep_ring->dequeue != td->last_trb)
@@ -1875,55 +1917,29 @@ static int finish_td(struct xhci_hcd *xhci, struct xhci_td *td,
}
td_cleanup:
- /* Clean up the endpoint's TD list */
- urb = td->urb;
- urb_priv = urb->hcpriv;
-
- /* if a bounce buffer was used to align this td then unmap it */
- if (td->bounce_seg)
- xhci_unmap_td_bounce_buffer(xhci, ep_ring, td);
+ return xhci_td_cleanup(xhci, td, ep_ring, status);
+}
- /* Do one last check of the actual transfer length.
- * If the host controller said we transferred more data than the buffer
- * length, urb->actual_length will be a very big number (since it's
- * unsigned). Play it safe and say we didn't transfer anything.
- */
- if (urb->actual_length > urb->transfer_buffer_length) {
- xhci_warn(xhci, "URB transfer length is wrong, xHC issue? req. len = %u, act. len = %u\n",
- urb->transfer_buffer_length,
- urb->actual_length);
- urb->actual_length = 0;
- if (td->urb->transfer_flags & URB_SHORT_NOT_OK)
- *status = -EREMOTEIO;
- else
- *status = 0;
- }
- list_del_init(&td->td_list);
- /* Was this TD slated to be cancelled but completed anyway? */
- if (!list_empty(&td->cancelled_td_list))
- list_del_init(&td->cancelled_td_list);
+/* sum trb lengths from ring dequeue up to stop_trb, _excluding_ stop_trb */
+static int sum_trb_lengths(struct xhci_hcd *xhci, struct xhci_ring *ring,
+ union xhci_trb *stop_trb)
+{
+ u32 sum;
+ union xhci_trb *trb = ring->dequeue;
+ struct xhci_segment *seg = ring->deq_seg;
- urb_priv->td_cnt++;
- /* Giveback the urb when all the tds are completed */
- if (urb_priv->td_cnt == urb_priv->length) {
- ret = 1;
- if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
- xhci_to_hcd(xhci)->self.bandwidth_isoc_reqs--;
- if (xhci_to_hcd(xhci)->self.bandwidth_isoc_reqs == 0) {
- if (xhci->quirks & XHCI_AMD_PLL_FIX)
- usb_amd_quirk_pll_enable();
- }
- }
+ for (sum = 0; trb != stop_trb; next_trb(xhci, ring, &seg, &trb)) {
+ if (!trb_is_noop(trb) && !trb_is_link(trb))
+ sum += TRB_LEN(le32_to_cpu(trb->generic.field[2]));
}
-
- return ret;
+ return sum;
}
/*
* Process control tds, update urb status and actual_length.
*/
static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td,
- union xhci_trb *event_trb, struct xhci_transfer_event *event,
+ union xhci_trb *ep_trb, struct xhci_transfer_event *event,
struct xhci_virt_ep *ep, int *status)
{
struct xhci_virt_device *xdev;
@@ -1932,202 +1948,182 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td,
int ep_index;
struct xhci_ep_ctx *ep_ctx;
u32 trb_comp_code;
+ u32 remaining, requested;
+ u32 trb_type;
+ trb_type = TRB_FIELD_TO_TYPE(le32_to_cpu(ep_trb->generic.field[3]));
slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags));
xdev = xhci->devs[slot_id];
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));
ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index);
trb_comp_code = GET_COMP_CODE(le32_to_cpu(event->transfer_len));
+ requested = td->urb->transfer_buffer_length;
+ remaining = EVENT_TRB_LEN(le32_to_cpu(event->transfer_len));
switch (trb_comp_code) {
case COMP_SUCCESS:
- if (event_trb == ep_ring->dequeue) {
- xhci_warn(xhci, "WARN: Success on ctrl setup TRB "
- "without IOC set??\n");
+ if (trb_type != TRB_STATUS) {
+ xhci_warn(xhci, "WARN: Success on ctrl %s TRB without IOC set?\n",
+ (trb_type == TRB_DATA) ? "data" : "setup");
*status = -ESHUTDOWN;
- } else if (event_trb != td->last_trb) {
- xhci_warn(xhci, "WARN: Success on ctrl data TRB "
- "without IOC set??\n");
- *status = -ESHUTDOWN;
- } else {
- *status = 0;
+ break;
}
+ *status = 0;
break;
- case COMP_SHORT_TX:
- if (td->urb->transfer_flags & URB_SHORT_NOT_OK)
- *status = -EREMOTEIO;
- else
- *status = 0;
+ case COMP_SHORT_PACKET:
+ *status = 0;
break;
- case COMP_STOP_SHORT:
- if (event_trb == ep_ring->dequeue || event_trb == td->last_trb)
- xhci_warn(xhci, "WARN: Stopped Short Packet on ctrl setup or status TRB\n");
+ case COMP_STOPPED_SHORT_PACKET:
+ if (trb_type == TRB_DATA || trb_type == TRB_NORMAL)
+ td->urb->actual_length = remaining;
else
- td->urb->actual_length =
- EVENT_TRB_LEN(le32_to_cpu(event->transfer_len));
-
- return finish_td(xhci, td, event_trb, event, ep, status, false);
- case COMP_STOP:
- /* Did we stop at data stage? */
- if (event_trb != ep_ring->dequeue && event_trb != td->last_trb)
- td->urb->actual_length =
- td->urb->transfer_buffer_length -
- EVENT_TRB_LEN(le32_to_cpu(event->transfer_len));
- /* fall through */
- case COMP_STOP_INVAL:
- return finish_td(xhci, td, event_trb, event, ep, status, false);
+ xhci_warn(xhci, "WARN: Stopped Short Packet on ctrl setup or status TRB\n");
+ goto finish_td;
+ case COMP_STOPPED:
+ switch (trb_type) {
+ case TRB_SETUP:
+ td->urb->actual_length = 0;
+ goto finish_td;
+ case TRB_DATA:
+ case TRB_NORMAL:
+ td->urb->actual_length = requested - remaining;
+ goto finish_td;
+ case TRB_STATUS:
+ td->urb->actual_length = requested;
+ goto finish_td;
+ default:
+ xhci_warn(xhci, "WARN: unexpected TRB Type %d\n",
+ trb_type);
+ goto finish_td;
+ }
+ case COMP_STOPPED_LENGTH_INVALID:
+ goto finish_td;
default:
if (!xhci_requires_manual_halt_cleanup(xhci,
- ep_ctx, trb_comp_code))
+ ep_ctx, trb_comp_code))
break;
- xhci_dbg(xhci, "TRB error code %u, "
- "halted endpoint index = %u\n",
- trb_comp_code, ep_index);
+ xhci_dbg(xhci, "TRB error %u, halted endpoint index = %u\n",
+ trb_comp_code, ep_index);
/* else fall through */
- case COMP_STALL:
+ case COMP_STALL_ERROR:
/* Did we transfer part of the data (middle) phase? */
- if (event_trb != ep_ring->dequeue &&
- event_trb != td->last_trb)
- td->urb->actual_length =
- td->urb->transfer_buffer_length -
- EVENT_TRB_LEN(le32_to_cpu(event->transfer_len));
+ if (trb_type == TRB_DATA || trb_type == TRB_NORMAL)
+ td->urb->actual_length = requested - remaining;
else if (!td->urb_length_set)
td->urb->actual_length = 0;
-
- return finish_td(xhci, td, event_trb, event, ep, status, false);
+ goto finish_td;
}
+
+ /* stopped at setup stage, no data transferred */
+ if (trb_type == TRB_SETUP)
+ goto finish_td;
+
/*
- * Did we transfer any data, despite the errors that might have
- * happened? I.e. did we get past the setup stage?
+ * if on data stage then update the actual_length of the URB and flag it
+ * as set, so it won't be overwritten in the event for the last TRB.
*/
- if (event_trb != ep_ring->dequeue) {
- /* The event was for the status stage */
- if (event_trb == td->last_trb) {
- if (td->urb_length_set) {
- /* Don't overwrite a previously set error code
- */
- if ((*status == -EINPROGRESS || *status == 0) &&
- (td->urb->transfer_flags
- & URB_SHORT_NOT_OK))
- /* Did we already see a short data
- * stage? */
- *status = -EREMOTEIO;
- } else {
- td->urb->actual_length =
- td->urb->transfer_buffer_length;
- }
- } else {
- /*
- * Maybe the event was for the data stage? If so, update
- * already the actual_length of the URB and flag it as
- * set, so that it is not overwritten in the event for
- * the last TRB.
- */
- td->urb_length_set = true;
- td->urb->actual_length =
- td->urb->transfer_buffer_length -
- EVENT_TRB_LEN(le32_to_cpu(event->transfer_len));
- xhci_dbg(xhci, "Waiting for status "
- "stage event\n");
- return 0;
- }
+ if (trb_type == TRB_DATA ||
+ trb_type == TRB_NORMAL) {
+ td->urb_length_set = true;
+ td->urb->actual_length = requested - remaining;
+ xhci_dbg(xhci, "Waiting for status stage event\n");
+ return 0;
}
- return finish_td(xhci, td, event_trb, event, ep, status, false);
+ /* at status stage */
+ if (!td->urb_length_set)
+ td->urb->actual_length = requested;
+
+finish_td:
+ return finish_td(xhci, td, ep_trb, event, ep, status, false);
}
/*
* Process isochronous tds, update urb packet status and actual_length.
*/
static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
- union xhci_trb *event_trb, struct xhci_transfer_event *event,
+ union xhci_trb *ep_trb, struct xhci_transfer_event *event,
struct xhci_virt_ep *ep, int *status)
{
struct xhci_ring *ep_ring;
struct urb_priv *urb_priv;
int idx;
- int len = 0;
- union xhci_trb *cur_trb;
- struct xhci_segment *cur_seg;
struct usb_iso_packet_descriptor *frame;
u32 trb_comp_code;
- bool skip_td = false;
+ bool sum_trbs_for_length = false;
+ u32 remaining, requested, ep_trb_len;
+ int short_framestatus;
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));
urb_priv = td->urb->hcpriv;
- idx = urb_priv->td_cnt;
+ idx = urb_priv->num_tds_done;
frame = &td->urb->iso_frame_desc[idx];
+ requested = frame->length;
+ remaining = EVENT_TRB_LEN(le32_to_cpu(event->transfer_len));
+ ep_trb_len = TRB_LEN(le32_to_cpu(ep_trb->generic.field[2]));
+ short_framestatus = td->urb->transfer_flags & URB_SHORT_NOT_OK ?
+ -EREMOTEIO : 0;
/* handle completion code */
switch (trb_comp_code) {
case COMP_SUCCESS:
- if (EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)) == 0) {
- frame->status = 0;
+ if (remaining) {
+ frame->status = short_framestatus;
+ if (xhci->quirks & XHCI_TRUST_TX_LENGTH)
+ sum_trbs_for_length = true;
break;
}
- if ((xhci->quirks & XHCI_TRUST_TX_LENGTH))
- trb_comp_code = COMP_SHORT_TX;
- /* fallthrough */
- case COMP_STOP_SHORT:
- case COMP_SHORT_TX:
- frame->status = td->urb->transfer_flags & URB_SHORT_NOT_OK ?
- -EREMOTEIO : 0;
+ frame->status = 0;
+ break;
+ case COMP_SHORT_PACKET:
+ frame->status = short_framestatus;
+ sum_trbs_for_length = true;
break;
- case COMP_BW_OVER:
+ case COMP_BANDWIDTH_OVERRUN_ERROR:
frame->status = -ECOMM;
- skip_td = true;
break;
- case COMP_BUFF_OVER:
- case COMP_BABBLE:
+ case COMP_ISOCH_BUFFER_OVERRUN:
+ case COMP_BABBLE_DETECTED_ERROR:
frame->status = -EOVERFLOW;
- skip_td = true;
break;
- case COMP_DEV_ERR:
- case COMP_STALL:
+ case COMP_INCOMPATIBLE_DEVICE_ERROR:
+ case COMP_STALL_ERROR:
frame->status = -EPROTO;
- skip_td = true;
break;
- case COMP_TX_ERR:
+ case COMP_USB_TRANSACTION_ERROR:
frame->status = -EPROTO;
- if (event_trb != td->last_trb)
+ if (ep_trb != td->last_trb)
return 0;
- skip_td = true;
break;
- case COMP_STOP:
- case COMP_STOP_INVAL:
+ case COMP_STOPPED:
+ sum_trbs_for_length = true;
+ break;
+ case COMP_STOPPED_SHORT_PACKET:
+ /* field normally containing residue now contains tranferred */
+ frame->status = short_framestatus;
+ requested = remaining;
+ break;
+ case COMP_STOPPED_LENGTH_INVALID:
+ requested = 0;
+ remaining = 0;
break;
default:
+ sum_trbs_for_length = true;
frame->status = -1;
break;
}
- if (trb_comp_code == COMP_SUCCESS || skip_td) {
- frame->actual_length = frame->length;
- td->urb->actual_length += frame->length;
- } else if (trb_comp_code == COMP_STOP_SHORT) {
- frame->actual_length =
- EVENT_TRB_LEN(le32_to_cpu(event->transfer_len));
- td->urb->actual_length += frame->actual_length;
- } else {
- for (cur_trb = ep_ring->dequeue,
- cur_seg = ep_ring->deq_seg; cur_trb != event_trb;
- next_trb(xhci, ep_ring, &cur_seg, &cur_trb)) {
- if (!TRB_TYPE_NOOP_LE32(cur_trb->generic.field[3]) &&
- !TRB_TYPE_LINK_LE32(cur_trb->generic.field[3]))
- len += TRB_LEN(le32_to_cpu(cur_trb->generic.field[2]));
- }
- len += TRB_LEN(le32_to_cpu(cur_trb->generic.field[2])) -
- EVENT_TRB_LEN(le32_to_cpu(event->transfer_len));
+ if (sum_trbs_for_length)
+ frame->actual_length = sum_trb_lengths(xhci, ep_ring, ep_trb) +
+ ep_trb_len - remaining;
+ else
+ frame->actual_length = requested;
- if (trb_comp_code != COMP_STOP_INVAL) {
- frame->actual_length = len;
- td->urb->actual_length += len;
- }
- }
+ td->urb->actual_length += frame->actual_length;
- return finish_td(xhci, td, event_trb, event, ep, status, false);
+ return finish_td(xhci, td, ep_trb, event, ep, status, false);
}
static int skip_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
@@ -2141,7 +2137,7 @@ static int skip_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
ep_ring = xhci_dma_to_transfer_ring(ep, le64_to_cpu(event->buffer));
urb_priv = td->urb->hcpriv;
- idx = urb_priv->td_cnt;
+ idx = urb_priv->num_tds_done;
frame = &td->urb->iso_frame_desc[idx];
/* The transfer is partly done. */
@@ -2162,119 +2158,62 @@ static int skip_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
* Process bulk and interrupt tds, update urb status and actual_length.
*/
static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td,
- union xhci_trb *event_trb, struct xhci_transfer_event *event,
+ union xhci_trb *ep_trb, struct xhci_transfer_event *event,
struct xhci_virt_ep *ep, int *status)
{
struct xhci_ring *ep_ring;
- union xhci_trb *cur_trb;
- struct xhci_segment *cur_seg;
u32 trb_comp_code;
+ u32 remaining, requested, ep_trb_len;
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));
+ ep_trb_len = TRB_LEN(le32_to_cpu(ep_trb->generic.field[2]));
+ requested = td->urb->transfer_buffer_length;
switch (trb_comp_code) {
case COMP_SUCCESS:
- /* Double check that the HW transferred everything. */
- if (event_trb != td->last_trb ||
- EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)) != 0) {
- xhci_warn(xhci, "WARN Successful completion "
- "on short TX\n");
- if (td->urb->transfer_flags & URB_SHORT_NOT_OK)
- *status = -EREMOTEIO;
- else
- *status = 0;
- if ((xhci->quirks & XHCI_TRUST_TX_LENGTH))
- trb_comp_code = COMP_SHORT_TX;
- } else {
- *status = 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");
+ xhci_dbg(xhci, "ep %#x - asked for %d bytes, %d bytes untransferred\n",
+ td->urb->ep->desc.bEndpointAddress,
+ requested, remaining);
}
+ *status = 0;
break;
- case COMP_STOP_SHORT:
- case COMP_SHORT_TX:
- if (td->urb->transfer_flags & URB_SHORT_NOT_OK)
- *status = -EREMOTEIO;
- else
- *status = 0;
+ case COMP_SHORT_PACKET:
+ xhci_dbg(xhci, "ep %#x - asked for %d bytes, %d bytes untransferred\n",
+ td->urb->ep->desc.bEndpointAddress,
+ requested, remaining);
+ *status = 0;
+ break;
+ case COMP_STOPPED_SHORT_PACKET:
+ td->urb->actual_length = remaining;
+ goto finish_td;
+ case COMP_STOPPED_LENGTH_INVALID:
+ /* stopped on ep trb with invalid length, exclude it */
+ ep_trb_len = 0;
+ remaining = 0;
break;
default:
- /* Others already handled above */
+ /* do nothing */
break;
}
- if (trb_comp_code == COMP_SHORT_TX)
- xhci_dbg(xhci, "ep %#x - asked for %d bytes, "
- "%d bytes untransferred\n",
- td->urb->ep->desc.bEndpointAddress,
- td->urb->transfer_buffer_length,
- EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)));
- /* Stopped - short packet completion */
- if (trb_comp_code == COMP_STOP_SHORT) {
- td->urb->actual_length =
- EVENT_TRB_LEN(le32_to_cpu(event->transfer_len));
- if (td->urb->transfer_buffer_length <
- td->urb->actual_length) {
- xhci_warn(xhci, "HC gave bad length of %d bytes txed\n",
- EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)));
- td->urb->actual_length = 0;
- /* status will be set by usb core for canceled urbs */
- }
- /* Fast path - was this the last TRB in the TD for this URB? */
- } else if (event_trb == td->last_trb) {
- if (EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)) != 0) {
- td->urb->actual_length =
- td->urb->transfer_buffer_length -
- EVENT_TRB_LEN(le32_to_cpu(event->transfer_len));
- if (td->urb->transfer_buffer_length <
- td->urb->actual_length) {
- xhci_warn(xhci, "HC gave bad length "
- "of %d bytes left\n",
- EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)));
- td->urb->actual_length = 0;
- if (td->urb->transfer_flags & URB_SHORT_NOT_OK)
- *status = -EREMOTEIO;
- else
- *status = 0;
- }
- /* Don't overwrite a previously set error code */
- if (*status == -EINPROGRESS) {
- if (td->urb->transfer_flags & URB_SHORT_NOT_OK)
- *status = -EREMOTEIO;
- else
- *status = 0;
- }
- } else {
- td->urb->actual_length =
- td->urb->transfer_buffer_length;
- /* Ignore a short packet completion if the
- * untransferred length was zero.
- */
- if (*status == -EREMOTEIO)
- *status = 0;
- }
- } else {
- /* Slow path - walk the list, starting from the dequeue
- * pointer, to get the actual length transferred.
- */
+ if (ep_trb == td->last_trb)
+ td->urb->actual_length = requested - remaining;
+ else
+ td->urb->actual_length =
+ sum_trb_lengths(xhci, ep_ring, ep_trb) +
+ ep_trb_len - remaining;
+finish_td:
+ if (remaining > requested) {
+ xhci_warn(xhci, "bad transfer trb length %d in event trb\n",
+ remaining);
td->urb->actual_length = 0;
- for (cur_trb = ep_ring->dequeue, cur_seg = ep_ring->deq_seg;
- cur_trb != event_trb;
- next_trb(xhci, ep_ring, &cur_seg, &cur_trb)) {
- if (!TRB_TYPE_NOOP_LE32(cur_trb->generic.field[3]) &&
- !TRB_TYPE_LINK_LE32(cur_trb->generic.field[3]))
- td->urb->actual_length +=
- TRB_LEN(le32_to_cpu(cur_trb->generic.field[2]));
- }
- /* If the ring didn't stop on a Link or No-op TRB, add
- * in the actual bytes transferred from the Normal TRB
- */
- if (trb_comp_code != COMP_STOP_INVAL)
- td->urb->actual_length +=
- TRB_LEN(le32_to_cpu(cur_trb->generic.field[2])) -
- EVENT_TRB_LEN(le32_to_cpu(event->transfer_len));
}
-
- return finish_td(xhci, td, event_trb, event, ep, status, false);
+ return finish_td(xhci, td, ep_trb, event, ep, status, false);
}
/*
@@ -2284,8 +2223,6 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td,
*/
static int handle_tx_event(struct xhci_hcd *xhci,
struct xhci_transfer_event *event)
- __releases(&xhci->lock)
- __acquires(&xhci->lock)
{
struct xhci_virt_device *xdev;
struct xhci_virt_ep *ep;
@@ -2293,16 +2230,13 @@ static int handle_tx_event(struct xhci_hcd *xhci,
unsigned int slot_id;
int ep_index;
struct xhci_td *td = NULL;
- dma_addr_t event_dma;
- struct xhci_segment *event_seg;
- union xhci_trb *event_trb;
- struct urb *urb = NULL;
+ dma_addr_t ep_trb_dma;
+ struct xhci_segment *ep_seg;
+ union xhci_trb *ep_trb;
int status = -EINPROGRESS;
- struct urb_priv *urb_priv;
struct xhci_ep_ctx *ep_ctx;
struct list_head *tmp;
u32 trb_comp_code;
- int ret = 0;
int td_num = 0;
bool handling_skipped_tds = false;
@@ -2328,9 +2262,7 @@ static int handle_tx_event(struct xhci_hcd *xhci,
ep = &xdev->eps[ep_index];
ep_ring = xhci_dma_to_transfer_ring(ep, le64_to_cpu(event->buffer));
ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index);
- if (!ep_ring ||
- (le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK) ==
- EP_STATE_DISABLED) {
+ if (!ep_ring || GET_EP_CTX_STATE(ep_ctx) == EP_STATE_DISABLED) {
xhci_err(xhci, "ERROR Transfer event for disabled endpoint "
"or incorrect stream ring\n");
xhci_err(xhci, "@%016llx %08x %08x %08x %08x\n",
@@ -2352,7 +2284,7 @@ static int handle_tx_event(struct xhci_hcd *xhci,
td_num++;
}
- event_dma = le64_to_cpu(event->buffer);
+ ep_trb_dma = le64_to_cpu(event->buffer);
trb_comp_code = GET_COMP_CODE(le32_to_cpu(event->transfer_len));
/* Look for common error cases */
switch (trb_comp_code) {
@@ -2363,50 +2295,50 @@ static int handle_tx_event(struct xhci_hcd *xhci,
if (EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)) == 0)
break;
if (xhci->quirks & XHCI_TRUST_TX_LENGTH)
- trb_comp_code = COMP_SHORT_TX;
+ trb_comp_code = COMP_SHORT_PACKET;
else
xhci_warn_ratelimited(xhci,
"WARN Successful completion on short TX: needs XHCI_TRUST_TX_LENGTH quirk?\n");
- case COMP_SHORT_TX:
+ case COMP_SHORT_PACKET:
break;
- case COMP_STOP:
+ case COMP_STOPPED:
xhci_dbg(xhci, "Stopped on Transfer TRB\n");
break;
- case COMP_STOP_INVAL:
+ case COMP_STOPPED_LENGTH_INVALID:
xhci_dbg(xhci, "Stopped on No-op or Link TRB\n");
break;
- case COMP_STOP_SHORT:
+ case COMP_STOPPED_SHORT_PACKET:
xhci_dbg(xhci, "Stopped with short packet transfer detected\n");
break;
- case COMP_STALL:
+ case COMP_STALL_ERROR:
xhci_dbg(xhci, "Stalled endpoint\n");
ep->ep_state |= EP_HALTED;
status = -EPIPE;
break;
- case COMP_TRB_ERR:
+ case COMP_TRB_ERROR:
xhci_warn(xhci, "WARN: TRB error on endpoint\n");
status = -EILSEQ;
break;
- case COMP_SPLIT_ERR:
- case COMP_TX_ERR:
+ case COMP_SPLIT_TRANSACTION_ERROR:
+ case COMP_USB_TRANSACTION_ERROR:
xhci_dbg(xhci, "Transfer error on endpoint\n");
status = -EPROTO;
break;
- case COMP_BABBLE:
+ case COMP_BABBLE_DETECTED_ERROR:
xhci_dbg(xhci, "Babble error on endpoint\n");
status = -EOVERFLOW;
break;
- case COMP_DB_ERR:
+ case COMP_DATA_BUFFER_ERROR:
xhci_warn(xhci, "WARN: HC couldn't access mem fast enough\n");
status = -ENOSR;
break;
- case COMP_BW_OVER:
+ case COMP_BANDWIDTH_OVERRUN_ERROR:
xhci_warn(xhci, "WARN: bandwidth overrun event on endpoint\n");
break;
- case COMP_BUFF_OVER:
+ case COMP_ISOCH_BUFFER_OVERRUN:
xhci_warn(xhci, "WARN: buffer overrun event on endpoint\n");
break;
- case COMP_UNDERRUN:
+ case COMP_RING_UNDERRUN:
/*
* When the Isoch ring is empty, the xHC will generate
* a Ring Overrun Event for IN Isoch endpoint or Ring
@@ -2419,7 +2351,7 @@ static int handle_tx_event(struct xhci_hcd *xhci,
TRB_TO_SLOT_ID(le32_to_cpu(event->flags)),
ep_index);
goto cleanup;
- case COMP_OVERRUN:
+ case COMP_RING_OVERRUN:
xhci_dbg(xhci, "overrun event on endpoint\n");
if (!list_empty(&ep_ring->td_list))
xhci_dbg(xhci, "Overrun Event for slot %d ep %d "
@@ -2427,11 +2359,11 @@ static int handle_tx_event(struct xhci_hcd *xhci,
TRB_TO_SLOT_ID(le32_to_cpu(event->flags)),
ep_index);
goto cleanup;
- case COMP_DEV_ERR:
+ case COMP_INCOMPATIBLE_DEVICE_ERROR:
xhci_warn(xhci, "WARN: detect an incompatible device");
status = -EPROTO;
break;
- case COMP_MISSED_INT:
+ case COMP_MISSED_SERVICE_ERROR:
/*
* When encounter missed service error, one or more isoc tds
* may be missed by xHC.
@@ -2441,7 +2373,7 @@ static int handle_tx_event(struct xhci_hcd *xhci,
ep->skip = true;
xhci_dbg(xhci, "Miss service interval error, set skip flag\n");
goto cleanup;
- case COMP_PING_ERR:
+ case COMP_NO_PING_RESPONSE_ERROR:
ep->skip = true;
xhci_dbg(xhci, "No Ping response error, Skip one Isoc TD\n");
goto cleanup;
@@ -2465,8 +2397,8 @@ static int handle_tx_event(struct xhci_hcd *xhci,
* event if the device was suspended. Don't print
* warnings.
*/
- if (!(trb_comp_code == COMP_STOP ||
- trb_comp_code == COMP_STOP_INVAL)) {
+ if (!(trb_comp_code == COMP_STOPPED ||
+ trb_comp_code == COMP_STOPPED_LENGTH_INVALID)) {
xhci_warn(xhci, "WARN Event TRB for slot %d ep %d with no TDs queued?\n",
TRB_TO_SLOT_ID(le32_to_cpu(event->flags)),
ep_index);
@@ -2480,7 +2412,6 @@ static int handle_tx_event(struct xhci_hcd *xhci,
xhci_dbg(xhci, "td_list is empty while skip "
"flag set. Clear skip flag.\n");
}
- ret = 0;
goto cleanup;
}
@@ -2489,17 +2420,17 @@ static int handle_tx_event(struct xhci_hcd *xhci,
ep->skip = false;
xhci_dbg(xhci, "All tds on the ep_ring skipped. "
"Clear skip flag.\n");
- ret = 0;
goto cleanup;
}
- td = list_entry(ep_ring->td_list.next, struct xhci_td, td_list);
+ td = list_first_entry(&ep_ring->td_list, struct xhci_td,
+ td_list);
if (ep->skip)
td_num--;
/* Is this a TRB in the currently executing TD? */
- event_seg = trb_in_td(xhci, ep_ring->deq_seg, ep_ring->dequeue,
- td->last_trb, event_dma, false);
+ ep_seg = trb_in_td(xhci, ep_ring->deq_seg, ep_ring->dequeue,
+ td->last_trb, ep_trb_dma, false);
/*
* Skip the Force Stopped Event. The event_trb(event_dma) of FSE
@@ -2509,13 +2440,12 @@ static int handle_tx_event(struct xhci_hcd *xhci,
* last TRB of the previous TD. The command completion handle
* will take care the rest.
*/
- if (!event_seg && (trb_comp_code == COMP_STOP ||
- trb_comp_code == COMP_STOP_INVAL)) {
- ret = 0;
+ if (!ep_seg && (trb_comp_code == COMP_STOPPED ||
+ trb_comp_code == COMP_STOPPED_LENGTH_INVALID)) {
goto cleanup;
}
- if (!event_seg) {
+ if (!ep_seg) {
if (!ep->skip ||
!usb_endpoint_xfer_isoc(&td->urb->ep->desc)) {
/* Some host controllers give a spurious
@@ -2525,7 +2455,6 @@ static int handle_tx_event(struct xhci_hcd *xhci,
if ((xhci->quirks & XHCI_SPURIOUS_SUCCESS) &&
ep_ring->last_td_was_short) {
ep_ring->last_td_was_short = false;
- ret = 0;
goto cleanup;
}
/* HC is busted, give up! */
@@ -2536,14 +2465,14 @@ static int handle_tx_event(struct xhci_hcd *xhci,
trb_comp_code);
trb_in_td(xhci, ep_ring->deq_seg,
ep_ring->dequeue, td->last_trb,
- event_dma, true);
+ ep_trb_dma, true);
return -ESHUTDOWN;
}
- ret = skip_isoc_td(xhci, td, event, ep, &status);
+ skip_isoc_td(xhci, td, event, ep, &status);
goto cleanup;
}
- if (trb_comp_code == COMP_SHORT_TX)
+ if (trb_comp_code == COMP_SHORT_PACKET)
ep_ring->last_td_was_short = true;
else
ep_ring->last_td_was_short = false;
@@ -2553,39 +2482,35 @@ static int handle_tx_event(struct xhci_hcd *xhci,
ep->skip = false;
}
- event_trb = &event_seg->trbs[(event_dma - event_seg->dma) /
- sizeof(*event_trb)];
+ ep_trb = &ep_seg->trbs[(ep_trb_dma - ep_seg->dma) /
+ sizeof(*ep_trb)];
+
+ trace_xhci_handle_transfer(ep_ring,
+ (struct xhci_generic_trb *) ep_trb);
+
/*
* No-op TRB should not trigger interrupts.
- * If event_trb is a no-op TRB, it means the
+ * If ep_trb is a no-op TRB, it means the
* corresponding TD has been cancelled. Just ignore
* the TD.
*/
- if (TRB_TYPE_NOOP_LE32(event_trb->generic.field[3])) {
- xhci_dbg(xhci,
- "event_trb is a no-op TRB. Skip it\n");
+ if (trb_is_noop(ep_trb)) {
+ xhci_dbg(xhci, "ep_trb is a no-op TRB. Skip it\n");
goto cleanup;
}
- /* Now update the urb's actual_length and give back to
- * the core
- */
+ /* update the urb's actual_length and give back to the core */
if (usb_endpoint_xfer_control(&td->urb->ep->desc))
- ret = process_ctrl_td(xhci, td, event_trb, event, ep,
- &status);
+ process_ctrl_td(xhci, td, ep_trb, event, ep, &status);
else if (usb_endpoint_xfer_isoc(&td->urb->ep->desc))
- ret = process_isoc_td(xhci, td, event_trb, event, ep,
- &status);
+ process_isoc_td(xhci, td, ep_trb, event, ep, &status);
else
- ret = process_bulk_intr_td(xhci, td, event_trb, event,
- ep, &status);
-
+ process_bulk_intr_td(xhci, td, ep_trb, event, ep,
+ &status);
cleanup:
-
-
handling_skipped_tds = ep->skip &&
- trb_comp_code != COMP_MISSED_INT &&
- trb_comp_code != COMP_PING_ERR;
+ trb_comp_code != COMP_MISSED_SERVICE_ERROR &&
+ trb_comp_code != COMP_NO_PING_RESPONSE_ERROR;
/*
* Do not update event ring dequeue pointer if we're in a loop
@@ -2594,33 +2519,6 @@ cleanup:
if (!handling_skipped_tds)
inc_deq(xhci, xhci->event_ring);
- if (ret) {
- urb = td->urb;
- urb_priv = urb->hcpriv;
-
- xhci_urb_free_priv(urb_priv);
-
- usb_hcd_unlink_urb_from_ep(bus_to_hcd(urb->dev->bus), urb);
- if ((urb->actual_length != urb->transfer_buffer_length &&
- (urb->transfer_flags &
- URB_SHORT_NOT_OK)) ||
- (status != 0 &&
- !usb_endpoint_xfer_isoc(&urb->ep->desc)))
- xhci_dbg(xhci, "Giveback URB %p, len = %d, "
- "expected = %d, status = %d\n",
- urb, urb->actual_length,
- urb->transfer_buffer_length,
- status);
- spin_unlock(&xhci->lock);
- /* EHCI, UHCI, and OHCI always unconditionally set the
- * urb->status of an isochronous endpoint to 0.
- */
- if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS)
- status = 0;
- usb_hcd_giveback_urb(bus_to_hcd(urb->dev->bus), urb, status);
- spin_lock(&xhci->lock);
- }
-
/*
* If ep->skip is set, it means there are missed tds on the
* endpoint ring need to take care of.
@@ -2644,18 +2542,19 @@ static int xhci_handle_event(struct xhci_hcd *xhci)
int update_ptrs = 1;
int ret;
+ /* Event ring hasn't been allocated yet. */
if (!xhci->event_ring || !xhci->event_ring->dequeue) {
- xhci->error_bitmask |= 1 << 1;
- return 0;
+ xhci_err(xhci, "ERROR event ring not ready\n");
+ return -ENOMEM;
}
event = xhci->event_ring->dequeue;
/* Does the HC or OS own the TRB? */
if ((le32_to_cpu(event->event_cmd.flags) & TRB_CYCLE) !=
- xhci->event_ring->cycle_state) {
- xhci->error_bitmask |= 1 << 2;
+ xhci->event_ring->cycle_state)
return 0;
- }
+
+ trace_xhci_handle_event(xhci->event_ring, &event->generic);
/*
* Barrier between reading the TRB_CYCLE (valid) flag above and any
@@ -2663,7 +2562,7 @@ static int xhci_handle_event(struct xhci_hcd *xhci)
*/
rmb();
/* FIXME: Handle more event types. */
- switch ((le32_to_cpu(event->event_cmd.flags) & TRB_TYPE_BITMASK)) {
+ switch (le32_to_cpu(event->event_cmd.flags) & TRB_TYPE_BITMASK) {
case TRB_TYPE(TRB_COMPLETION):
handle_cmd_completion(xhci, &event->event_cmd);
break;
@@ -2673,9 +2572,7 @@ static int xhci_handle_event(struct xhci_hcd *xhci)
break;
case TRB_TYPE(TRB_TRANSFER):
ret = handle_tx_event(xhci, &event->trans_event);
- if (ret < 0)
- xhci->error_bitmask |= 1 << 9;
- else
+ if (ret >= 0)
update_ptrs = 0;
break;
case TRB_TYPE(TRB_DEV_NOTE):
@@ -2686,7 +2583,9 @@ static int xhci_handle_event(struct xhci_hcd *xhci)
TRB_TYPE(48))
handle_vendor_event(xhci, event);
else
- xhci->error_bitmask |= 1 << 3;
+ xhci_warn(xhci, "ERROR unknown event type %d\n",
+ TRB_FIELD_TO_TYPE(
+ le32_to_cpu(event->event_cmd.flags)));
}
/* Any of the above functions may drop and re-acquire the lock, so check
* to make sure a watchdog timer didn't mark the host as non-responsive.
@@ -2715,27 +2614,28 @@ static int xhci_handle_event(struct xhci_hcd *xhci)
irqreturn_t xhci_irq(struct usb_hcd *hcd)
{
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
- u32 status;
- u64 temp_64;
union xhci_trb *event_ring_deq;
+ irqreturn_t ret = IRQ_NONE;
dma_addr_t deq;
+ u64 temp_64;
+ u32 status;
spin_lock(&xhci->lock);
/* Check if the xHC generated the interrupt, or the irq is shared */
status = readl(&xhci->op_regs->status);
- if (status == 0xffffffff)
- goto hw_died;
-
- if (!(status & STS_EINT)) {
- spin_unlock(&xhci->lock);
- return IRQ_NONE;
+ if (status == 0xffffffff) {
+ ret = IRQ_HANDLED;
+ goto out;
}
+
+ if (!(status & STS_EINT))
+ goto out;
+
if (status & STS_FATAL) {
xhci_warn(xhci, "WARNING: Host System Error\n");
xhci_halt(xhci);
-hw_died:
- spin_unlock(&xhci->lock);
- return IRQ_HANDLED;
+ ret = IRQ_HANDLED;
+ goto out;
}
/*
@@ -2766,9 +2666,8 @@ hw_died:
temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue);
xhci_write_64(xhci, temp_64 | ERST_EHB,
&xhci->ir_set->erst_dequeue);
- spin_unlock(&xhci->lock);
-
- return IRQ_HANDLED;
+ ret = IRQ_HANDLED;
+ goto out;
}
event_ring_deq = xhci->event_ring->dequeue;
@@ -2793,10 +2692,12 @@ hw_died:
/* Clear the event handler busy flag (RW1C); event ring is empty. */
temp_64 |= ERST_EHB;
xhci_write_64(xhci, temp_64, &xhci->ir_set->erst_dequeue);
+ ret = IRQ_HANDLED;
+out:
spin_unlock(&xhci->lock);
- return IRQ_HANDLED;
+ return ret;
}
irqreturn_t xhci_msi_irq(int irq, void *hcd)
@@ -2824,6 +2725,9 @@ static void queue_trb(struct xhci_hcd *xhci, struct xhci_ring *ring,
trb->field[1] = cpu_to_le32(field2);
trb->field[2] = cpu_to_le32(field3);
trb->field[3] = cpu_to_le32(field4);
+
+ trace_xhci_queue_trb(ring, trb);
+
inc_enq(xhci, ring, more_trbs_coming);
}
@@ -2931,14 +2835,13 @@ static int prepare_transfer(struct xhci_hcd *xhci,
return -EINVAL;
}
- ret = prepare_ring(xhci, ep_ring,
- le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK,
+ ret = prepare_ring(xhci, ep_ring, GET_EP_CTX_STATE(ep_ctx),
num_trbs, mem_flags);
if (ret)
return ret;
urb_priv = urb->hcpriv;
- td = urb_priv->td[td_index];
+ td = &urb_priv->td[td_index];
INIT_LIST_HEAD(&td->td_list);
INIT_LIST_HEAD(&td->cancelled_td_list);
@@ -2955,8 +2858,6 @@ static int prepare_transfer(struct xhci_hcd *xhci,
td->start_seg = ep_ring->enq_seg;
td->first_trb = ep_ring->enqueue;
- urb_priv->td[td_index] = td;
-
return 0;
}
@@ -3120,7 +3021,7 @@ static u32 xhci_td_remainder(struct xhci_hcd *xhci, int transferred,
if (xhci->quirks & XHCI_MTK_HOST)
trb_buff_len = 0;
- maxp = GET_MAX_PACKET(usb_endpoint_maxp(&urb->ep->desc));
+ maxp = usb_endpoint_maxp(&urb->ep->desc);
total_packet_count = DIV_ROUND_UP(td_total_len, maxp);
/* Queueing functions don't count the current TRB into transferred */
@@ -3136,7 +3037,7 @@ static int xhci_align_td(struct xhci_hcd *xhci, struct urb *urb, u32 enqd_len,
unsigned int max_pkt;
u32 new_buff_len;
- max_pkt = GET_MAX_PACKET(usb_endpoint_maxp(&urb->ep->desc));
+ max_pkt = usb_endpoint_maxp(&urb->ep->desc);
unalign = (enqd_len + *trb_buff_len) % max_pkt;
/* we got lucky, last normal TRB data on segment is packet aligned */
@@ -3233,10 +3134,10 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
urb_priv = urb->hcpriv;
/* Deal with URB_ZERO_PACKET - need one more td/trb */
- if (urb->transfer_flags & URB_ZERO_PACKET && urb_priv->length > 1)
+ if (urb->transfer_flags & URB_ZERO_PACKET && urb_priv->num_tds > 1)
need_zero_pkt = true;
- td = urb_priv->td[0];
+ td = &urb_priv->td[0];
/*
* Don't give the first TRB to the hardware (by toggling the cycle bit)
@@ -3329,7 +3230,7 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
ret = prepare_transfer(xhci, xhci->devs[slot_id],
ep_index, urb->stream_id,
1, urb, 1, mem_flags);
- urb_priv->td[1]->last_trb = ring->enqueue;
+ urb_priv->td[1].last_trb = ring->enqueue;
field = TRB_TYPE(TRB_NORMAL) | ring->cycle_state | TRB_IOC;
queue_trb(xhci, ring, 0, 0, 0, TRB_INTR_TARGET(0), field);
}
@@ -3350,7 +3251,7 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
struct usb_ctrlrequest *setup;
struct xhci_generic_trb *start_trb;
int start_cycle;
- u32 field, length_field, remainder;
+ u32 field;
struct urb_priv *urb_priv;
struct xhci_td *td;
@@ -3381,7 +3282,7 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
return ret;
urb_priv = urb->hcpriv;
- td = urb_priv->td[0];
+ td = &urb_priv->td[0];
/*
* Don't give the first TRB to the hardware (by toggling the cycle bit)
@@ -3423,16 +3324,16 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
else
field = TRB_TYPE(TRB_DATA);
- remainder = xhci_td_remainder(xhci, 0,
- urb->transfer_buffer_length,
- urb->transfer_buffer_length,
- urb, 1);
-
- length_field = TRB_LEN(urb->transfer_buffer_length) |
- TRB_TD_SIZE(remainder) |
- TRB_INTR_TARGET(0);
-
if (urb->transfer_buffer_length > 0) {
+ u32 length_field, remainder;
+
+ remainder = xhci_td_remainder(xhci, 0,
+ urb->transfer_buffer_length,
+ urb->transfer_buffer_length,
+ urb, 1);
+ length_field = TRB_LEN(urb->transfer_buffer_length) |
+ TRB_TD_SIZE(remainder) |
+ TRB_INTR_TARGET(0);
if (setup->bRequestType & USB_DIR_IN)
field |= TRB_DIR_IN;
queue_trb(xhci, ep_ring, true,
@@ -3650,7 +3551,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
addr = start_addr + urb->iso_frame_desc[i].offset;
td_len = urb->iso_frame_desc[i].length;
td_remain_len = td_len;
- max_pkt = GET_MAX_PACKET(usb_endpoint_maxp(&urb->ep->desc));
+ max_pkt = usb_endpoint_maxp(&urb->ep->desc);
total_pkt_count = DIV_ROUND_UP(td_len, max_pkt);
/* A zero-length transfer still involves at least one packet. */
@@ -3669,7 +3570,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
return ret;
goto cleanup;
}
- td = urb_priv->td[i];
+ td = &urb_priv->td[i];
/* use SIA as default, if frame id is used overwrite it */
sia_frame_id = TRB_SIA;
@@ -3776,20 +3677,20 @@ cleanup:
/* Clean up a partially enqueued isoc transfer. */
for (i--; i >= 0; i--)
- list_del_init(&urb_priv->td[i]->td_list);
+ list_del_init(&urb_priv->td[i].td_list);
/* Use the first TD as a temporary variable to turn the TDs we've queued
* into No-ops with a software-owned cycle bit. That way the hardware
* won't accidentally start executing bogus TDs when we partially
* overwrite them. td->first_trb and td->start_seg are already set.
*/
- urb_priv->td[0]->last_trb = ep_ring->enqueue;
+ urb_priv->td[0].last_trb = ep_ring->enqueue;
/* Every TRB except the first & last will have its cycle bit flipped. */
- td_to_noop(xhci, ep_ring, urb_priv->td[0], true);
+ td_to_noop(xhci, ep_ring, &urb_priv->td[0], true);
/* Reset the ring enqueue back to the first TRB and its cycle bit. */
- ep_ring->enqueue = urb_priv->td[0]->first_trb;
- ep_ring->enq_seg = urb_priv->td[0]->start_seg;
+ ep_ring->enqueue = urb_priv->td[0].first_trb;
+ ep_ring->enq_seg = urb_priv->td[0].start_seg;
ep_ring->cycle_state = start_cycle;
ep_ring->num_trbs_free = ep_ring->num_trbs_free_temp;
usb_hcd_unlink_urb_from_ep(bus_to_hcd(urb->dev->bus), urb);
@@ -3828,7 +3729,7 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags,
/* Check the ring to guarantee there is enough room for the whole urb.
* Do not insert any td of the urb to the ring if the check failed.
*/
- ret = prepare_ring(xhci, ep_ring, le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK,
+ ret = prepare_ring(xhci, ep_ring, GET_EP_CTX_STATE(ep_ctx),
num_trbs, mem_flags);
if (ret)
return ret;
@@ -3841,8 +3742,7 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags,
/* Calculate the start frame and put it in urb->start_frame. */
if (HCC_CFC(xhci->hcc_params) && !list_empty(&ep_ring->td_list)) {
- if ((le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK) ==
- EP_STATE_RUNNING) {
+ if (GET_EP_CTX_STATE(ep_ctx) == EP_STATE_RUNNING) {
urb->start_frame = xep->next_frame_id;
goto skip_start_over;
}
@@ -3916,15 +3816,15 @@ static int queue_command(struct xhci_hcd *xhci, struct xhci_command *cmd,
}
cmd->command_trb = xhci->cmd_ring->enqueue;
- list_add_tail(&cmd->cmd_list, &xhci->cmd_list);
/* if there are no other commands queued we start the timeout timer */
- if (xhci->cmd_list.next == &cmd->cmd_list &&
- !timer_pending(&xhci->cmd_timer)) {
+ if (list_empty(&xhci->cmd_list)) {
xhci->current_cmd = cmd;
- mod_timer(&xhci->cmd_timer, jiffies + XHCI_CMD_DEFAULT_TIMEOUT);
+ xhci_mod_cmd_timer(xhci, XHCI_CMD_DEFAULT_TIMEOUT);
}
+ list_add_tail(&cmd->cmd_list, &xhci->cmd_list);
+
queue_trb(xhci, xhci->cmd_ring, false, field1, field2, field3,
field4 | xhci->cmd_ring->cycle_state);
return 0;
diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c
index a59fafb4b329..74436f8ca538 100644
--- a/drivers/usb/host/xhci-tegra.c
+++ b/drivers/usb/host/xhci-tegra.c
@@ -1308,7 +1308,6 @@ static int tegra_xhci_setup(struct usb_hcd *hcd)
}
static const struct xhci_driver_overrides tegra_xhci_overrides __initconst = {
- .extra_priv_size = sizeof(struct xhci_hcd),
.reset = tegra_xhci_setup,
};
diff --git a/drivers/usb/host/xhci-trace.h b/drivers/usb/host/xhci-trace.h
index 59c05653b2ea..1ac2cdf8eece 100644
--- a/drivers/usb/host/xhci-trace.h
+++ b/drivers/usb/host/xhci-trace.h
@@ -103,7 +103,7 @@ DECLARE_EVENT_CLASS(xhci_log_ctx,
((HCC_64BYTE_CONTEXT(xhci->hcc_params) + 1) * 32) *
((ctx->type == XHCI_CTX_TYPE_INPUT) + ep_num + 1));
),
- TP_printk("\nctx_64=%d, ctx_type=%u, ctx_dma=@%llx, ctx_va=@%p",
+ TP_printk("ctx_64=%d, ctx_type=%u, ctx_dma=@%llx, ctx_va=@%p",
__entry->ctx_64, __entry->ctx_type,
(unsigned long long) __entry->ctx_dma, __entry->ctx_va
)
@@ -115,34 +115,174 @@ DEFINE_EVENT(xhci_log_ctx, xhci_address_ctx,
TP_ARGS(xhci, ctx, ep_num)
);
-DECLARE_EVENT_CLASS(xhci_log_event,
- TP_PROTO(void *trb_va, struct xhci_generic_trb *ev),
- TP_ARGS(trb_va, ev),
+DECLARE_EVENT_CLASS(xhci_log_trb,
+ TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb),
+ TP_ARGS(ring, trb),
TP_STRUCT__entry(
- __field(void *, va)
- __field(u64, dma)
- __field(u32, status)
- __field(u32, flags)
- __dynamic_array(u8, trb, sizeof(struct xhci_generic_trb))
+ __field(u32, type)
+ __field(u32, field0)
+ __field(u32, field1)
+ __field(u32, field2)
+ __field(u32, field3)
),
TP_fast_assign(
- __entry->va = trb_va;
- __entry->dma = ((u64)le32_to_cpu(ev->field[1])) << 32 |
- le32_to_cpu(ev->field[0]);
- __entry->status = le32_to_cpu(ev->field[2]);
- __entry->flags = le32_to_cpu(ev->field[3]);
- memcpy(__get_dynamic_array(trb), trb_va,
- sizeof(struct xhci_generic_trb));
+ __entry->type = ring->type;
+ __entry->field0 = le32_to_cpu(trb->field[0]);
+ __entry->field1 = le32_to_cpu(trb->field[1]);
+ __entry->field2 = le32_to_cpu(trb->field[2]);
+ __entry->field3 = le32_to_cpu(trb->field[3]);
),
- TP_printk("\ntrb_dma=@%llx, trb_va=@%p, status=%08x, flags=%08x",
- (unsigned long long) __entry->dma, __entry->va,
- __entry->status, __entry->flags
+ TP_printk("%s: %s", xhci_ring_type_string(__entry->type),
+ xhci_decode_trb(__entry->field0, __entry->field1,
+ __entry->field2, __entry->field3)
)
);
-DEFINE_EVENT(xhci_log_event, xhci_cmd_completion,
- TP_PROTO(void *trb_va, struct xhci_generic_trb *ev),
- TP_ARGS(trb_va, ev)
+DEFINE_EVENT(xhci_log_trb, xhci_handle_event,
+ TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb),
+ TP_ARGS(ring, trb)
+);
+
+DEFINE_EVENT(xhci_log_trb, xhci_handle_command,
+ TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb),
+ TP_ARGS(ring, trb)
+);
+
+DEFINE_EVENT(xhci_log_trb, xhci_handle_transfer,
+ TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb),
+ TP_ARGS(ring, trb)
+);
+
+DEFINE_EVENT(xhci_log_trb, xhci_queue_trb,
+ TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb),
+ TP_ARGS(ring, trb)
+);
+
+DECLARE_EVENT_CLASS(xhci_log_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(int, devnum)
+ __field(int, state)
+ __field(int, speed)
+ __field(u8, portnum)
+ __field(u8, level)
+ __field(int, slot_id)
+ ),
+ 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->devnum = vdev->udev->devnum;
+ __entry->state = vdev->udev->state;
+ __entry->speed = vdev->udev->speed;
+ __entry->portnum = vdev->udev->portnum;
+ __entry->level = vdev->udev->level;
+ __entry->slot_id = vdev->udev->slot_id;
+ ),
+ TP_printk("vdev %p ctx %llx | %llx num %d state %d speed %d port %d level %d slot %d",
+ __entry->vdev, __entry->in_ctx, __entry->out_ctx,
+ __entry->devnum, __entry->state, __entry->speed,
+ __entry->portnum, __entry->level, __entry->slot_id
+ )
+);
+
+DEFINE_EVENT(xhci_log_virt_dev, xhci_alloc_virt_device,
+ TP_PROTO(struct xhci_virt_device *vdev),
+ 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)
+);
+
+DEFINE_EVENT(xhci_log_virt_dev, xhci_setup_addressable_virt_device,
+ TP_PROTO(struct xhci_virt_device *vdev),
+ TP_ARGS(vdev)
+);
+
+DEFINE_EVENT(xhci_log_virt_dev, xhci_stop_device,
+ TP_PROTO(struct xhci_virt_device *vdev),
+ TP_ARGS(vdev)
+);
+
+DECLARE_EVENT_CLASS(xhci_log_urb,
+ TP_PROTO(struct urb *urb),
+ TP_ARGS(urb),
+ TP_STRUCT__entry(
+ __field(void *, urb)
+ __field(unsigned int, pipe)
+ __field(unsigned int, stream)
+ __field(int, status)
+ __field(unsigned int, flags)
+ __field(int, num_mapped_sgs)
+ __field(int, num_sgs)
+ __field(int, length)
+ __field(int, actual)
+ __field(int, epnum)
+ __field(int, dir_in)
+ __field(int, type)
+ ),
+ TP_fast_assign(
+ __entry->urb = urb;
+ __entry->pipe = urb->pipe;
+ __entry->stream = urb->stream_id;
+ __entry->status = urb->status;
+ __entry->flags = urb->transfer_flags;
+ __entry->num_mapped_sgs = urb->num_mapped_sgs;
+ __entry->num_sgs = urb->num_sgs;
+ __entry->length = urb->transfer_buffer_length;
+ __entry->actual = urb->actual_length;
+ __entry->epnum = usb_endpoint_num(&urb->ep->desc);
+ __entry->dir_in = usb_endpoint_dir_in(&urb->ep->desc);
+ __entry->type = usb_endpoint_type(&urb->ep->desc);
+ ),
+ TP_printk("ep%d%s-%s: urb %p pipe %u length %d/%d sgs %d/%d stream %d flags %08x",
+ __entry->epnum, __entry->dir_in ? "in" : "out",
+ ({ char *s;
+ switch (__entry->type) {
+ case USB_ENDPOINT_XFER_INT:
+ s = "intr";
+ break;
+ case USB_ENDPOINT_XFER_CONTROL:
+ s = "control";
+ break;
+ case USB_ENDPOINT_XFER_BULK:
+ s = "bulk";
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
+ s = "isoc";
+ break;
+ default:
+ s = "UNKNOWN";
+ } s; }), __entry->urb, __entry->pipe, __entry->actual,
+ __entry->length, __entry->num_mapped_sgs,
+ __entry->num_sgs, __entry->stream, __entry->flags
+ )
+);
+
+DEFINE_EVENT(xhci_log_urb, xhci_urb_enqueue,
+ TP_PROTO(struct urb *urb),
+ TP_ARGS(urb)
+);
+
+DEFINE_EVENT(xhci_log_urb, xhci_urb_giveback,
+ TP_PROTO(struct urb *urb),
+ TP_ARGS(urb)
+);
+
+DEFINE_EVENT(xhci_log_urb, xhci_urb_dequeue,
+ TP_PROTO(struct urb *urb),
+ TP_ARGS(urb)
);
#endif /* __XHCI_TRACE_H */
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 1a4ca02729c2..953fd8f62df0 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -113,12 +113,12 @@ int xhci_halt(struct xhci_hcd *xhci)
ret = xhci_handshake(&xhci->op_regs->status,
STS_HALT, STS_HALT, XHCI_MAX_HALT_USEC);
- if (!ret) {
- xhci->xhc_state |= XHCI_STATE_HALTED;
- xhci->cmd_ring_state = CMD_RING_STATE_STOPPED;
- } else
- xhci_warn(xhci, "Host not halted after %u microseconds.\n",
- XHCI_MAX_HALT_USEC);
+ if (ret) {
+ xhci_warn(xhci, "Host halt failed, %d\n", ret);
+ return ret;
+ }
+ xhci->xhc_state |= XHCI_STATE_HALTED;
+ xhci->cmd_ring_state = CMD_RING_STATE_STOPPED;
return ret;
}
@@ -167,6 +167,12 @@ int xhci_reset(struct xhci_hcd *xhci)
int ret, i;
state = readl(&xhci->op_regs->status);
+
+ if (state == ~(u32)0) {
+ xhci_warn(xhci, "Host not accessible, reset failed.\n");
+ return -ENODEV;
+ }
+
if ((state & STS_HALT) == 0) {
xhci_warn(xhci, "Host controller not halted, aborting reset.\n");
return 0;
@@ -201,7 +207,7 @@ int xhci_reset(struct xhci_hcd *xhci)
ret = xhci_handshake(&xhci->op_regs->status,
STS_CNR, 0, 10 * 1000 * 1000);
- for (i = 0; i < 2; ++i) {
+ for (i = 0; i < 2; i++) {
xhci->bus_state[i].port_c_suspend = 0;
xhci->bus_state[i].suspended_ports = 0;
xhci->bus_state[i].resuming_ports = 0;
@@ -690,7 +696,6 @@ void xhci_stop(struct usb_hcd *hcd)
xhci->cmd_ring_state = CMD_RING_STATE_STOPPED;
xhci_halt(xhci);
xhci_reset(xhci);
-
spin_unlock_irq(&xhci->lock);
}
@@ -863,7 +868,7 @@ static void xhci_disable_port_wake_on_bits(struct xhci_hcd *xhci)
spin_lock_irqsave(&xhci->lock, flags);
- /* disble usb3 ports Wake bits*/
+ /* disable usb3 ports Wake bits */
port_index = xhci->num_usb3_ports;
port_array = xhci->usb3_ports;
while (port_index--) {
@@ -874,7 +879,7 @@ static void xhci_disable_port_wake_on_bits(struct xhci_hcd *xhci)
writel(t2, port_array[port_index]);
}
- /* disble usb2 ports Wake bits*/
+ /* disable usb2 ports Wake bits */
port_index = xhci->num_usb2_ports;
port_array = xhci->usb2_ports;
while (port_index--) {
@@ -1327,12 +1332,11 @@ command_cleanup:
int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags)
{
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
- struct xhci_td *buffer;
unsigned long flags;
int ret = 0;
- unsigned int slot_id, ep_index;
+ unsigned int slot_id, ep_index, ep_state;
struct urb_priv *urb_priv;
- int size, i;
+ int num_tds;
if (!urb || xhci_check_args(hcd, urb->dev, urb->ep,
true, true, __func__) <= 0)
@@ -1344,40 +1348,30 @@ int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags)
if (!HCD_HW_ACCESSIBLE(hcd)) {
if (!in_interrupt())
xhci_dbg(xhci, "urb submitted during PCI suspend\n");
- ret = -ESHUTDOWN;
- goto exit;
+ return -ESHUTDOWN;
}
if (usb_endpoint_xfer_isoc(&urb->ep->desc))
- size = urb->number_of_packets;
+ num_tds = urb->number_of_packets;
else if (usb_endpoint_is_bulk_out(&urb->ep->desc) &&
urb->transfer_buffer_length > 0 &&
urb->transfer_flags & URB_ZERO_PACKET &&
!(urb->transfer_buffer_length % usb_endpoint_maxp(&urb->ep->desc)))
- size = 2;
+ num_tds = 2;
else
- size = 1;
+ num_tds = 1;
urb_priv = kzalloc(sizeof(struct urb_priv) +
- size * sizeof(struct xhci_td *), mem_flags);
+ num_tds * sizeof(struct xhci_td), mem_flags);
if (!urb_priv)
return -ENOMEM;
- buffer = kzalloc(size * sizeof(struct xhci_td), mem_flags);
- if (!buffer) {
- kfree(urb_priv);
- return -ENOMEM;
- }
-
- for (i = 0; i < size; i++) {
- urb_priv->td[i] = buffer;
- buffer++;
- }
-
- urb_priv->length = size;
- urb_priv->td_cnt = 0;
+ urb_priv->num_tds = num_tds;
+ urb_priv->num_tds_done = 0;
urb->hcpriv = urb_priv;
+ trace_xhci_urb_enqueue(urb);
+
if (usb_endpoint_xfer_control(&urb->ep->desc)) {
/* Check to see if the max packet size for the default control
* endpoint changed during FS device enumeration
@@ -1391,69 +1385,51 @@ int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags)
return ret;
}
}
+ }
- /* We have a spinlock and interrupts disabled, so we must pass
- * atomic context to this function, which may allocate memory.
- */
- spin_lock_irqsave(&xhci->lock, flags);
- if (xhci->xhc_state & XHCI_STATE_DYING)
- goto dying;
+ spin_lock_irqsave(&xhci->lock, flags);
+
+ if (xhci->xhc_state & XHCI_STATE_DYING) {
+ xhci_dbg(xhci, "Ep 0x%x: URB %p submitted for non-responsive xHCI host.\n",
+ urb->ep->desc.bEndpointAddress, urb);
+ ret = -ESHUTDOWN;
+ goto free_priv;
+ }
+
+ switch (usb_endpoint_type(&urb->ep->desc)) {
+
+ case USB_ENDPOINT_XFER_CONTROL:
ret = xhci_queue_ctrl_tx(xhci, GFP_ATOMIC, urb,
- slot_id, ep_index);
- if (ret)
- goto free_priv;
- spin_unlock_irqrestore(&xhci->lock, flags);
- } else if (usb_endpoint_xfer_bulk(&urb->ep->desc)) {
- spin_lock_irqsave(&xhci->lock, flags);
- if (xhci->xhc_state & XHCI_STATE_DYING)
- goto dying;
- if (xhci->devs[slot_id]->eps[ep_index].ep_state &
- EP_GETTING_STREAMS) {
- xhci_warn(xhci, "WARN: Can't enqueue URB while bulk ep "
- "is transitioning to using streams.\n");
- ret = -EINVAL;
- } else if (xhci->devs[slot_id]->eps[ep_index].ep_state &
- EP_GETTING_NO_STREAMS) {
- xhci_warn(xhci, "WARN: Can't enqueue URB while bulk ep "
- "is transitioning to "
- "not having streams.\n");
+ slot_id, ep_index);
+ break;
+ case USB_ENDPOINT_XFER_BULK:
+ ep_state = xhci->devs[slot_id]->eps[ep_index].ep_state;
+ if (ep_state & (EP_GETTING_STREAMS | EP_GETTING_NO_STREAMS)) {
+ xhci_warn(xhci, "WARN: Can't enqueue URB, ep in streams transition state %x\n",
+ ep_state);
ret = -EINVAL;
- } else {
- ret = xhci_queue_bulk_tx(xhci, GFP_ATOMIC, urb,
- slot_id, ep_index);
+ break;
}
- if (ret)
- goto free_priv;
- spin_unlock_irqrestore(&xhci->lock, flags);
- } else if (usb_endpoint_xfer_int(&urb->ep->desc)) {
- spin_lock_irqsave(&xhci->lock, flags);
- if (xhci->xhc_state & XHCI_STATE_DYING)
- goto dying;
+ ret = xhci_queue_bulk_tx(xhci, GFP_ATOMIC, urb,
+ slot_id, ep_index);
+ break;
+
+
+ case USB_ENDPOINT_XFER_INT:
ret = xhci_queue_intr_tx(xhci, GFP_ATOMIC, urb,
slot_id, ep_index);
- if (ret)
- goto free_priv;
- spin_unlock_irqrestore(&xhci->lock, flags);
- } else {
- spin_lock_irqsave(&xhci->lock, flags);
- if (xhci->xhc_state & XHCI_STATE_DYING)
- goto dying;
+ break;
+
+ case USB_ENDPOINT_XFER_ISOC:
ret = xhci_queue_isoc_tx_prepare(xhci, GFP_ATOMIC, urb,
slot_id, ep_index);
- if (ret)
- goto free_priv;
- spin_unlock_irqrestore(&xhci->lock, flags);
}
-exit:
- return ret;
-dying:
- xhci_dbg(xhci, "Ep 0x%x: URB %p submitted for "
- "non-responsive xHCI host.\n",
- urb->ep->desc.bEndpointAddress, urb);
- ret = -ESHUTDOWN;
+
+ if (ret) {
free_priv:
- xhci_urb_free_priv(urb_priv);
- urb->hcpriv = NULL;
+ xhci_urb_free_priv(urb_priv);
+ urb->hcpriv = NULL;
+ }
spin_unlock_irqrestore(&xhci->lock, flags);
return ret;
}
@@ -1501,84 +1477,72 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
struct xhci_ring *ep_ring;
struct xhci_virt_ep *ep;
struct xhci_command *command;
+ struct xhci_virt_device *vdev;
xhci = hcd_to_xhci(hcd);
spin_lock_irqsave(&xhci->lock, flags);
+
+ trace_xhci_urb_dequeue(urb);
+
/* Make sure the URB hasn't completed or been unlinked already */
ret = usb_hcd_check_unlink_urb(hcd, urb, status);
- if (ret || !urb->hcpriv)
+ if (ret)
goto done;
+
+ /* give back URB now if we can't queue it for cancel */
+ vdev = xhci->devs[urb->dev->slot_id];
+ urb_priv = urb->hcpriv;
+ if (!vdev || !urb_priv)
+ goto err_giveback;
+
+ ep_index = xhci_get_endpoint_index(&urb->ep->desc);
+ ep = &vdev->eps[ep_index];
+ ep_ring = xhci_urb_to_transfer_ring(xhci, urb);
+ if (!ep || !ep_ring)
+ goto err_giveback;
+
temp = readl(&xhci->op_regs->status);
if (temp == 0xffffffff || (xhci->xhc_state & XHCI_STATE_HALTED)) {
xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
"HW died, freeing TD.");
- urb_priv = urb->hcpriv;
- for (i = urb_priv->td_cnt;
- i < urb_priv->length && xhci->devs[urb->dev->slot_id];
+ for (i = urb_priv->num_tds_done;
+ i < urb_priv->num_tds;
i++) {
- td = urb_priv->td[i];
+ td = &urb_priv->td[i];
if (!list_empty(&td->td_list))
list_del_init(&td->td_list);
if (!list_empty(&td->cancelled_td_list))
list_del_init(&td->cancelled_td_list);
}
-
- usb_hcd_unlink_urb_from_ep(hcd, urb);
- spin_unlock_irqrestore(&xhci->lock, flags);
- usb_hcd_giveback_urb(hcd, urb, -ESHUTDOWN);
- xhci_urb_free_priv(urb_priv);
- return ret;
- }
- if ((xhci->xhc_state & XHCI_STATE_DYING) ||
- (xhci->xhc_state & XHCI_STATE_HALTED)) {
- xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
- "Ep 0x%x: URB %p to be canceled on "
- "non-responsive xHCI host.",
- urb->ep->desc.bEndpointAddress, urb);
- /* Let the stop endpoint command watchdog timer (which set this
- * state) finish cleaning up the endpoint TD lists. We must
- * have caught it in the middle of dropping a lock and giving
- * back an URB.
- */
- goto done;
+ goto err_giveback;
}
- ep_index = xhci_get_endpoint_index(&urb->ep->desc);
- ep = &xhci->devs[urb->dev->slot_id]->eps[ep_index];
- ep_ring = xhci_urb_to_transfer_ring(xhci, urb);
- if (!ep_ring) {
- ret = -EINVAL;
- goto done;
- }
-
- urb_priv = urb->hcpriv;
- i = urb_priv->td_cnt;
- if (i < urb_priv->length)
+ i = urb_priv->num_tds_done;
+ if (i < urb_priv->num_tds)
xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
"Cancel URB %p, dev %s, ep 0x%x, "
"starting at offset 0x%llx",
urb, urb->dev->devpath,
urb->ep->desc.bEndpointAddress,
(unsigned long long) xhci_trb_virt_to_dma(
- urb_priv->td[i]->start_seg,
- urb_priv->td[i]->first_trb));
+ urb_priv->td[i].start_seg,
+ urb_priv->td[i].first_trb));
- for (; i < urb_priv->length; i++) {
- td = urb_priv->td[i];
+ for (; i < urb_priv->num_tds; i++) {
+ td = &urb_priv->td[i];
list_add_tail(&td->cancelled_td_list, &ep->cancelled_td_list);
}
/* Queue a stop endpoint command, but only if this is
* the first cancellation to be handled.
*/
- if (!(ep->ep_state & EP_HALT_PENDING)) {
+ if (!(ep->ep_state & EP_STOP_CMD_PENDING)) {
command = xhci_alloc_command(xhci, false, false, GFP_ATOMIC);
if (!command) {
ret = -ENOMEM;
goto done;
}
- ep->ep_state |= EP_HALT_PENDING;
- ep->stop_cmds_pending++;
+ ep->ep_state |= EP_STOP_CMD_PENDING;
ep->stop_cmd_timer.expires = jiffies +
XHCI_STOP_EP_CMD_TIMEOUT * HZ;
add_timer(&ep->stop_cmd_timer);
@@ -1589,6 +1553,14 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
done:
spin_unlock_irqrestore(&xhci->lock, flags);
return ret;
+
+err_giveback:
+ if (urb_priv)
+ xhci_urb_free_priv(urb_priv);
+ usb_hcd_unlink_urb_from_ep(hcd, urb);
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ usb_hcd_giveback_urb(hcd, urb, -ESHUTDOWN);
+ return ret;
}
/* Drop an endpoint from a new bandwidth configuration for this device.
@@ -1645,8 +1617,7 @@ int xhci_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
/* If the HC already knows the endpoint is disabled,
* or the HCD has noted it is disabled, ignore this request
*/
- if (((ep_ctx->ep_info & cpu_to_le32(EP_STATE_MASK)) ==
- cpu_to_le32(EP_STATE_DISABLED)) ||
+ if ((GET_EP_CTX_STATE(ep_ctx) == EP_STATE_DISABLED) ||
le32_to_cpu(ctrl_ctx->drop_flags) &
xhci_get_endpoint_flag(&ep->desc)) {
/* Do not warn when called after a usb_device_reset */
@@ -1818,7 +1789,7 @@ static void xhci_zero_in_ctx(struct xhci_hcd *xhci, struct xhci_virt_device *vir
slot_ctx->dev_info &= cpu_to_le32(~LAST_CTX_MASK);
/* Endpoint 0 is always valid */
slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(1));
- for (i = 1; i < 31; ++i) {
+ for (i = 1; i < 31; i++) {
ep_ctx = xhci_get_ep_ctx(xhci, virt_dev->in_ctx, i);
ep_ctx->ep_info = 0;
ep_ctx->ep_info2 = 0;
@@ -1833,32 +1804,32 @@ static int xhci_configure_endpoint_result(struct xhci_hcd *xhci,
int ret;
switch (*cmd_status) {
- case COMP_CMD_ABORT:
- case COMP_CMD_STOP:
+ case COMP_COMMAND_ABORTED:
+ case COMP_STOPPED:
xhci_warn(xhci, "Timeout while waiting for configure endpoint command\n");
ret = -ETIME;
break;
- case COMP_ENOMEM:
+ case COMP_RESOURCE_ERROR:
dev_warn(&udev->dev,
"Not enough host controller resources for new device state.\n");
ret = -ENOMEM;
/* FIXME: can we allocate more resources for the HC? */
break;
- case COMP_BW_ERR:
- case COMP_2ND_BW_ERR:
+ case COMP_BANDWIDTH_ERROR:
+ case COMP_SECONDARY_BANDWIDTH_ERROR:
dev_warn(&udev->dev,
"Not enough bandwidth for new device state.\n");
ret = -ENOSPC;
/* FIXME: can we go back to the old state? */
break;
- case COMP_TRB_ERR:
+ case COMP_TRB_ERROR:
/* the HCD set up something wrong */
dev_warn(&udev->dev, "ERROR: Endpoint drop flag = 0, "
"add flag = 1, "
"and endpoint is not disabled.\n");
ret = -EINVAL;
break;
- case COMP_DEV_ERR:
+ case COMP_INCOMPATIBLE_DEVICE_ERROR:
dev_warn(&udev->dev,
"ERROR: Incompatible device for endpoint configure command.\n");
ret = -ENODEV;
@@ -1884,33 +1855,33 @@ static int xhci_evaluate_context_result(struct xhci_hcd *xhci,
struct xhci_virt_device *virt_dev = xhci->devs[udev->slot_id];
switch (*cmd_status) {
- case COMP_CMD_ABORT:
- case COMP_CMD_STOP:
+ case COMP_COMMAND_ABORTED:
+ case COMP_STOPPED:
xhci_warn(xhci, "Timeout while waiting for evaluate context command\n");
ret = -ETIME;
break;
- case COMP_EINVAL:
+ case COMP_PARAMETER_ERROR:
dev_warn(&udev->dev,
"WARN: xHCI driver setup invalid evaluate context command.\n");
ret = -EINVAL;
break;
- case COMP_EBADSLT:
+ case COMP_SLOT_NOT_ENABLED_ERROR:
dev_warn(&udev->dev,
"WARN: slot not enabled for evaluate context command.\n");
ret = -EINVAL;
break;
- case COMP_CTX_STATE:
+ case COMP_CONTEXT_STATE_ERROR:
dev_warn(&udev->dev,
"WARN: invalid context state for evaluate context command.\n");
xhci_dbg_ctx(xhci, virt_dev->out_ctx, 1);
ret = -EINVAL;
break;
- case COMP_DEV_ERR:
+ case COMP_INCOMPATIBLE_DEVICE_ERROR:
dev_warn(&udev->dev,
"ERROR: Incompatible device for evaluate context command.\n");
ret = -ENODEV;
break;
- case COMP_MEL_ERR:
+ case COMP_MAX_EXIT_LATENCY_TOO_LARGE_ERROR:
/* Max Exit Latency too large error */
dev_warn(&udev->dev, "WARN: Max Exit Latency too large\n");
ret = -EINVAL;
@@ -2790,7 +2761,7 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev)
LAST_CTX_TO_EP_NUM(le32_to_cpu(slot_ctx->dev_info)));
/* Free any rings that were dropped, but not changed. */
- for (i = 1; i < 31; ++i) {
+ for (i = 1; i < 31; i++) {
if ((le32_to_cpu(ctrl_ctx->drop_flags) & (1 << (i + 1))) &&
!(le32_to_cpu(ctrl_ctx->add_flags) & (1 << (i + 1)))) {
xhci_free_or_cache_endpoint_ring(xhci, virt_dev, i);
@@ -2802,7 +2773,7 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev)
* Install any rings for completely new endpoints or changed endpoints,
* and free or cache any old rings from changed endpoints.
*/
- for (i = 1; i < 31; ++i) {
+ for (i = 1; i < 31; i++) {
if (!virt_dev->eps[i].new_ring)
continue;
/* Only cache or free the old ring if it exists.
@@ -2836,7 +2807,7 @@ void xhci_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *udev)
xhci_dbg(xhci, "%s called for udev %p\n", __func__, udev);
virt_dev = xhci->devs[udev->slot_id];
/* Free any rings allocated for added endpoints */
- for (i = 0; i < 31; ++i) {
+ for (i = 0; i < 31; i++) {
if (virt_dev->eps[i].new_ring) {
xhci_ring_free(xhci, virt_dev->eps[i].new_ring);
virt_dev->eps[i].new_ring = NULL;
@@ -3209,7 +3180,7 @@ int xhci_alloc_streams(struct usb_hcd *hcd, struct usb_device *udev,
for (i = 0; i < num_eps; i++) {
ep_index = xhci_get_endpoint_index(&eps[i]->desc);
- max_packet = GET_MAX_PACKET(usb_endpoint_maxp(&eps[i]->desc));
+ max_packet = usb_endpoint_maxp(&eps[i]->desc);
vdev->eps[ep_index].stream_info = xhci_alloc_stream_info(xhci,
num_stream_ctxs,
num_streams,
@@ -3506,13 +3477,13 @@ int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev)
*/
ret = reset_device_cmd->status;
switch (ret) {
- case COMP_CMD_ABORT:
- case COMP_CMD_STOP:
+ case COMP_COMMAND_ABORTED:
+ case COMP_STOPPED:
xhci_warn(xhci, "Timeout waiting for reset device command\n");
ret = -ETIME;
goto command_cleanup;
- case COMP_EBADSLT: /* 0.95 completion code for bad slot ID */
- case COMP_CTX_STATE: /* 0.96 completion code for same thing */
+ case COMP_SLOT_NOT_ENABLED_ERROR: /* 0.95 completion for bad slot ID */
+ case COMP_CONTEXT_STATE_ERROR: /* 0.96 completion code for same thing */
xhci_dbg(xhci, "Can't reset device (slot ID %u) in %s state\n",
slot_id,
xhci_get_slot_state(xhci, virt_dev->out_ctx));
@@ -3542,7 +3513,7 @@ int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev)
/* Everything but endpoint 0 is disabled, so free or cache the rings. */
last_freed_endpoint = 1;
- for (i = 1; i < 31; ++i) {
+ for (i = 1; i < 31; i++) {
struct xhci_virt_ep *ep = &virt_dev->eps[i];
if (ep->ep_state & EP_HAS_STREAMS) {
@@ -3618,8 +3589,8 @@ void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev)
virt_dev = xhci->devs[udev->slot_id];
/* Stop any wayward timer functions (which may grab the lock) */
- for (i = 0; i < 31; ++i) {
- virt_dev->eps[i].ep_state &= ~EP_HALT_PENDING;
+ for (i = 0; i < 31; i++) {
+ virt_dev->eps[i].ep_state &= ~EP_STOP_CMD_PENDING;
del_timer_sync(&virt_dev->eps[i].stop_cmd_timer);
}
@@ -3683,27 +3654,26 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev)
int ret, slot_id;
struct xhci_command *command;
- command = xhci_alloc_command(xhci, false, false, GFP_KERNEL);
+ command = xhci_alloc_command(xhci, false, true, GFP_KERNEL);
if (!command)
return 0;
/* xhci->slot_id and xhci->addr_dev are not thread-safe */
mutex_lock(&xhci->mutex);
spin_lock_irqsave(&xhci->lock, flags);
- command->completion = &xhci->addr_dev;
ret = xhci_queue_slot_control(xhci, command, TRB_ENABLE_SLOT, 0);
if (ret) {
spin_unlock_irqrestore(&xhci->lock, flags);
mutex_unlock(&xhci->mutex);
xhci_dbg(xhci, "FIXME: allocate a command ring segment\n");
- kfree(command);
+ xhci_free_command(xhci, command);
return 0;
}
xhci_ring_cmd_db(xhci);
spin_unlock_irqrestore(&xhci->lock, flags);
wait_for_completion(command->completion);
- slot_id = xhci->slot_id;
+ slot_id = command->slot_id;
mutex_unlock(&xhci->mutex);
if (!slot_id || command->status != COMP_SUCCESS) {
@@ -3711,7 +3681,7 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev)
xhci_err(xhci, "Max number of devices this xHCI host supports is %u.\n",
HCS_MAX_SLOTS(
readl(&xhci->cap_regs->hcs_params1)));
- kfree(command);
+ xhci_free_command(xhci, command);
return 0;
}
@@ -3747,7 +3717,7 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev)
#endif
- kfree(command);
+ xhci_free_command(xhci, command);
/* Is this a LS or FS device under a HS hub? */
/* Hub or peripherial? */
return 1;
@@ -3755,6 +3725,7 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev)
disable_slot:
/* Disable slot, if we can do it without mem alloc */
spin_lock_irqsave(&xhci->lock, flags);
+ kfree(command->completion);
command->completion = NULL;
command->status = 0;
if (!xhci_queue_slot_control(xhci, command, TRB_DISABLE_SLOT,
@@ -3783,8 +3754,10 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
mutex_lock(&xhci->mutex);
- if (xhci->xhc_state) /* dying, removing or halted */
+ if (xhci->xhc_state) { /* dying, removing or halted */
+ ret = -ESHUTDOWN;
goto out;
+ }
if (!udev->slot_id) {
xhci_dbg_trace(xhci, trace_xhci_dbg_address,
@@ -3816,14 +3789,13 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
}
}
- command = xhci_alloc_command(xhci, false, false, GFP_KERNEL);
+ command = xhci_alloc_command(xhci, false, true, GFP_KERNEL);
if (!command) {
ret = -ENOMEM;
goto out;
}
command->in_ctx = virt_dev->in_ctx;
- command->completion = &xhci->addr_dev;
slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->in_ctx);
ctrl_ctx = xhci_get_input_control_ctx(virt_dev->in_ctx);
@@ -3852,6 +3824,7 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
le32_to_cpu(slot_ctx->dev_info) >> 27);
spin_lock_irqsave(&xhci->lock, flags);
+ trace_xhci_setup_device(virt_dev);
ret = xhci_queue_address_device(xhci, command, virt_dev->in_ctx->dma,
udev->slot_id, setup);
if (ret) {
@@ -3871,22 +3844,22 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
* command on a timeout.
*/
switch (command->status) {
- case COMP_CMD_ABORT:
- case COMP_CMD_STOP:
+ case COMP_COMMAND_ABORTED:
+ case COMP_STOPPED:
xhci_warn(xhci, "Timeout while waiting for setup device command\n");
ret = -ETIME;
break;
- case COMP_CTX_STATE:
- case COMP_EBADSLT:
+ case COMP_CONTEXT_STATE_ERROR:
+ case COMP_SLOT_NOT_ENABLED_ERROR:
xhci_err(xhci, "Setup ERROR: setup %s command for slot %d.\n",
act, udev->slot_id);
ret = -EINVAL;
break;
- case COMP_TX_ERR:
+ case COMP_USB_TRANSACTION_ERROR:
dev_warn(&udev->dev, "Device not responding to setup %s.\n", act);
ret = -EPROTO;
break;
- case COMP_DEV_ERR:
+ case COMP_INCOMPATIBLE_DEVICE_ERROR:
dev_warn(&udev->dev,
"ERROR: Incompatible device for setup %s command\n", act);
ret = -ENODEV;
@@ -3941,7 +3914,10 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
le32_to_cpu(slot_ctx->dev_state) & DEV_ADDR_MASK);
out:
mutex_unlock(&xhci->mutex);
- kfree(command);
+ if (command) {
+ kfree(command->completion);
+ kfree(command);
+ }
return ret;
}
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index f945380035d0..da3eb695fe54 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -709,6 +709,8 @@ struct xhci_ep_ctx {
#define EP_STATE_HALTED 2
#define EP_STATE_STOPPED 3
#define EP_STATE_ERROR 4
+#define GET_EP_CTX_STATE(ctx) (le32_to_cpu((ctx)->ep_info) & EP_STATE_MASK)
+
/* Mult - Max number of burtst within an interval, in EP companion desc. */
#define EP_MULT(p) (((p) & 0x3) << 8)
#define CTX_TO_EP_MULT(p) (((p) >> 8) & 0x3)
@@ -747,11 +749,6 @@ struct xhci_ep_ctx {
#define MAX_PACKET_MASK (0xffff << 16)
#define MAX_PACKET_DECODED(p) (((p) >> 16) & 0xffff)
-/* Get max packet size from ep desc. Bit 10..0 specify the max packet size.
- * USB2.0 spec 9.6.6.
- */
-#define GET_MAX_PACKET(p) ((p) & 0x7ff)
-
/* tx_info bitmasks */
#define EP_AVG_TRB_LENGTH(p) ((p) & 0xffff)
#define EP_MAX_ESIT_PAYLOAD_LO(p) (((p) & 0xffff) << 16)
@@ -789,6 +786,7 @@ struct xhci_command {
/* Input context for changing device state */
struct xhci_container_ctx *in_ctx;
u32 status;
+ int slot_id;
/* If completion is null, no one is waiting on this command
* and the structure can be freed after the command completes.
*/
@@ -914,7 +912,7 @@ struct xhci_virt_ep {
unsigned int ep_state;
#define SET_DEQ_PENDING (1 << 0)
#define EP_HALTED (1 << 1) /* For stall handling */
-#define EP_HALT_PENDING (1 << 2) /* For URB cancellation */
+#define EP_STOP_CMD_PENDING (1 << 2) /* For URB cancellation */
/* Transitioning the endpoint to using streams, don't enqueue URBs */
#define EP_GETTING_STREAMS (1 << 3)
#define EP_HAS_STREAMS (1 << 4)
@@ -926,7 +924,6 @@ struct xhci_virt_ep {
unsigned int stopped_stream;
/* Watchdog timer for stop endpoint command to cancel URBs */
struct timer_list stop_cmd_timer;
- int stop_cmds_pending;
struct xhci_hcd *xhci;
/* Dequeue pointer and dequeue segment for a submitted Set TR Dequeue
* command. We'll need to update the ring's dequeue segment and dequeue
@@ -997,7 +994,6 @@ struct xhci_virt_device {
int num_rings_cached;
#define XHCI_MAX_RINGS_CACHED 31
struct xhci_virt_ep eps[31];
- struct completion cmd_completion;
u8 fake_port;
u8 real_port;
struct xhci_interval_bw_table *bw_table;
@@ -1064,76 +1060,122 @@ struct xhci_transfer_event {
/* Completion Code - only applicable for some types of TRBs */
#define COMP_CODE_MASK (0xff << 24)
#define GET_COMP_CODE(p) (((p) & COMP_CODE_MASK) >> 24)
-#define COMP_SUCCESS 1
-/* Data Buffer Error */
-#define COMP_DB_ERR 2
-/* Babble Detected Error */
-#define COMP_BABBLE 3
-/* USB Transaction Error */
-#define COMP_TX_ERR 4
-/* TRB Error - some TRB field is invalid */
-#define COMP_TRB_ERR 5
-/* Stall Error - USB device is stalled */
-#define COMP_STALL 6
-/* Resource Error - HC doesn't have memory for that device configuration */
-#define COMP_ENOMEM 7
-/* Bandwidth Error - not enough room in schedule for this dev config */
-#define COMP_BW_ERR 8
-/* No Slots Available Error - HC ran out of device slots */
-#define COMP_ENOSLOTS 9
-/* Invalid Stream Type Error */
-#define COMP_STREAM_ERR 10
-/* Slot Not Enabled Error - doorbell rung for disabled device slot */
-#define COMP_EBADSLT 11
-/* Endpoint Not Enabled Error */
-#define COMP_EBADEP 12
-/* Short Packet */
-#define COMP_SHORT_TX 13
-/* Ring Underrun - doorbell rung for an empty isoc OUT ep ring */
-#define COMP_UNDERRUN 14
-/* Ring Overrun - isoc IN ep ring is empty when ep is scheduled to RX */
-#define COMP_OVERRUN 15
-/* Virtual Function Event Ring Full Error */
-#define COMP_VF_FULL 16
-/* Parameter Error - Context parameter is invalid */
-#define COMP_EINVAL 17
-/* Bandwidth Overrun Error - isoc ep exceeded its allocated bandwidth */
-#define COMP_BW_OVER 18
-/* Context State Error - illegal context state transition requested */
-#define COMP_CTX_STATE 19
-/* No Ping Response Error - HC didn't get PING_RESPONSE in time to TX */
-#define COMP_PING_ERR 20
-/* Event Ring is full */
-#define COMP_ER_FULL 21
-/* Incompatible Device Error */
-#define COMP_DEV_ERR 22
-/* Missed Service Error - HC couldn't service an isoc ep within interval */
-#define COMP_MISSED_INT 23
-/* Successfully stopped command ring */
-#define COMP_CMD_STOP 24
-/* Successfully aborted current command and stopped command ring */
-#define COMP_CMD_ABORT 25
-/* Stopped - transfer was terminated by a stop endpoint command */
-#define COMP_STOP 26
-/* Same as COMP_EP_STOPPED, but the transferred length in the event is invalid */
-#define COMP_STOP_INVAL 27
-/* Same as COMP_EP_STOPPED, but a short packet detected */
-#define COMP_STOP_SHORT 28
-/* Max Exit Latency Too Large Error */
-#define COMP_MEL_ERR 29
-/* TRB type 30 reserved */
-/* Isoc Buffer Overrun - an isoc IN ep sent more data than could fit in TD */
-#define COMP_BUFF_OVER 31
-/* Event Lost Error - xHC has an "internal event overrun condition" */
-#define COMP_ISSUES 32
-/* Undefined Error - reported when other error codes don't apply */
-#define COMP_UNKNOWN 33
-/* Invalid Stream ID Error */
-#define COMP_STRID_ERR 34
-/* Secondary Bandwidth Error - may be returned by a Configure Endpoint cmd */
-#define COMP_2ND_BW_ERR 35
-/* Split Transaction Error */
-#define COMP_SPLIT_ERR 36
+#define COMP_INVALID 0
+#define COMP_SUCCESS 1
+#define COMP_DATA_BUFFER_ERROR 2
+#define COMP_BABBLE_DETECTED_ERROR 3
+#define COMP_USB_TRANSACTION_ERROR 4
+#define COMP_TRB_ERROR 5
+#define COMP_STALL_ERROR 6
+#define COMP_RESOURCE_ERROR 7
+#define COMP_BANDWIDTH_ERROR 8
+#define COMP_NO_SLOTS_AVAILABLE_ERROR 9
+#define COMP_INVALID_STREAM_TYPE_ERROR 10
+#define COMP_SLOT_NOT_ENABLED_ERROR 11
+#define COMP_ENDPOINT_NOT_ENABLED_ERROR 12
+#define COMP_SHORT_PACKET 13
+#define COMP_RING_UNDERRUN 14
+#define COMP_RING_OVERRUN 15
+#define COMP_VF_EVENT_RING_FULL_ERROR 16
+#define COMP_PARAMETER_ERROR 17
+#define COMP_BANDWIDTH_OVERRUN_ERROR 18
+#define COMP_CONTEXT_STATE_ERROR 19
+#define COMP_NO_PING_RESPONSE_ERROR 20
+#define COMP_EVENT_RING_FULL_ERROR 21
+#define COMP_INCOMPATIBLE_DEVICE_ERROR 22
+#define COMP_MISSED_SERVICE_ERROR 23
+#define COMP_COMMAND_RING_STOPPED 24
+#define COMP_COMMAND_ABORTED 25
+#define COMP_STOPPED 26
+#define COMP_STOPPED_LENGTH_INVALID 27
+#define COMP_STOPPED_SHORT_PACKET 28
+#define COMP_MAX_EXIT_LATENCY_TOO_LARGE_ERROR 29
+#define COMP_ISOCH_BUFFER_OVERRUN 31
+#define COMP_EVENT_LOST_ERROR 32
+#define COMP_UNDEFINED_ERROR 33
+#define COMP_INVALID_STREAM_ID_ERROR 34
+#define COMP_SECONDARY_BANDWIDTH_ERROR 35
+#define COMP_SPLIT_TRANSACTION_ERROR 36
+
+static inline const char *xhci_trb_comp_code_string(u8 status)
+{
+ switch (status) {
+ case COMP_INVALID:
+ return "Invalid";
+ case COMP_SUCCESS:
+ return "Success";
+ case COMP_DATA_BUFFER_ERROR:
+ return "Data Buffer Error";
+ case COMP_BABBLE_DETECTED_ERROR:
+ return "Babble Detected";
+ case COMP_USB_TRANSACTION_ERROR:
+ return "USB Transaction Error";
+ case COMP_TRB_ERROR:
+ return "TRB Error";
+ case COMP_STALL_ERROR:
+ return "Stall Error";
+ case COMP_RESOURCE_ERROR:
+ return "Resource Error";
+ case COMP_BANDWIDTH_ERROR:
+ return "Bandwidth Error";
+ case COMP_NO_SLOTS_AVAILABLE_ERROR:
+ return "No Slots Available Error";
+ case COMP_INVALID_STREAM_TYPE_ERROR:
+ return "Invalid Stream Type Error";
+ case COMP_SLOT_NOT_ENABLED_ERROR:
+ return "Slot Not Enabled Error";
+ case COMP_ENDPOINT_NOT_ENABLED_ERROR:
+ return "Endpoint Not Enabled Error";
+ case COMP_SHORT_PACKET:
+ return "Short Packet";
+ case COMP_RING_UNDERRUN:
+ return "Ring Underrun";
+ case COMP_RING_OVERRUN:
+ return "Ring Overrun";
+ case COMP_VF_EVENT_RING_FULL_ERROR:
+ return "VF Event Ring Full Error";
+ case COMP_PARAMETER_ERROR:
+ return "Parameter Error";
+ case COMP_BANDWIDTH_OVERRUN_ERROR:
+ return "Bandwidth Overrun Error";
+ case COMP_CONTEXT_STATE_ERROR:
+ return "Context State Error";
+ case COMP_NO_PING_RESPONSE_ERROR:
+ return "No Ping Response Error";
+ case COMP_EVENT_RING_FULL_ERROR:
+ return "Event Ring Full Error";
+ case COMP_INCOMPATIBLE_DEVICE_ERROR:
+ return "Incompatible Device Error";
+ case COMP_MISSED_SERVICE_ERROR:
+ return "Missed Service Error";
+ case COMP_COMMAND_RING_STOPPED:
+ return "Command Ring Stopped";
+ case COMP_COMMAND_ABORTED:
+ return "Command Aborted";
+ case COMP_STOPPED:
+ return "Stopped";
+ case COMP_STOPPED_LENGTH_INVALID:
+ return "Stopped - Length Invalid";
+ case COMP_STOPPED_SHORT_PACKET:
+ return "Stopped - Short Packet";
+ case COMP_MAX_EXIT_LATENCY_TOO_LARGE_ERROR:
+ return "Max Exit Latency Too Large Error";
+ case COMP_ISOCH_BUFFER_OVERRUN:
+ return "Isoch Buffer Overrun";
+ case COMP_EVENT_LOST_ERROR:
+ return "Event Lost Error";
+ case COMP_UNDEFINED_ERROR:
+ return "Undefined Error";
+ case COMP_INVALID_STREAM_ID_ERROR:
+ return "Invalid Stream ID Error";
+ case COMP_SECONDARY_BANDWIDTH_ERROR:
+ return "Secondary Bandwidth Error";
+ case COMP_SPLIT_TRANSACTION_ERROR:
+ return "Split Transaction Error";
+ default:
+ return "Unknown!!";
+ }
+}
struct xhci_link_trb {
/* 64-bit segment pointer*/
@@ -1157,6 +1199,27 @@ struct xhci_event_cmd {
/* Address device - disable SetAddress */
#define TRB_BSR (1<<9)
+
+/* Configure Endpoint - Deconfigure */
+#define TRB_DC (1<<9)
+
+/* Stop Ring - Transfer State Preserve */
+#define TRB_TSP (1<<9)
+
+/* Force Event */
+#define TRB_TO_VF_INTR_TARGET(p) (((p) & (0x3ff << 22)) >> 22)
+#define TRB_TO_VF_ID(p) (((p) & (0xff << 16)) >> 16)
+
+/* Set Latency Tolerance Value */
+#define TRB_TO_BELT(p) (((p) & (0xfff << 16)) >> 16)
+
+/* Get Port Bandwidth */
+#define TRB_TO_DEV_SPEED(p) (((p) & (0xf << 16)) >> 16)
+
+/* Force Header */
+#define TRB_TO_PACKET_TYPE(p) ((p) & 0x1f)
+#define TRB_TO_ROOTHUB_PORT(p) (((p) & (0xff << 24)) >> 24)
+
enum xhci_setup_dev {
SETUP_CONTEXT_ONLY,
SETUP_CONTEXT_ADDRESS,
@@ -1180,16 +1243,21 @@ enum xhci_setup_dev {
#define STREAM_ID_FOR_TRB(p) ((((p)) & 0xffff) << 16)
#define SCT_FOR_TRB(p) (((p) << 1) & 0x7)
+/* Link TRB specific fields */
+#define TRB_TC (1<<1)
/* Port Status Change Event TRB fields */
/* Port ID - bits 31:24 */
#define GET_PORT_ID(p) (((p) & (0xff << 24)) >> 24)
+#define EVENT_DATA (1 << 2)
+
/* Normal TRB fields */
/* transfer_len bitmasks - bits 0:16 */
#define TRB_LEN(p) ((p) & 0x1ffff)
/* TD Size, packets remaining in this TD, bits 21:17 (5 bits, so max 31) */
#define TRB_TD_SIZE(p) (min((p), (u32)31) << 17)
+#define GET_TD_SIZE(p) (((p) & 0x3e0000) >> 17)
/* xhci 1.1 uses the TD_SIZE field for TBC if Extended TBC is enabled (ETE) */
#define TRB_TD_SIZE_TBC(p) (min((p), (u32)31) << 17)
/* Interrupter Target - which MSI-X vector to target the completion event at */
@@ -1317,6 +1385,80 @@ union xhci_trb {
/* Get NEC firmware revision. */
#define TRB_NEC_GET_FW 49
+static inline const char *xhci_trb_type_string(u8 type)
+{
+ switch (type) {
+ case TRB_NORMAL:
+ return "Normal";
+ case TRB_SETUP:
+ return "Setup Stage";
+ case TRB_DATA:
+ return "Data Stage";
+ case TRB_STATUS:
+ return "Status Stage";
+ case TRB_ISOC:
+ return "Isoch";
+ case TRB_LINK:
+ return "Link";
+ case TRB_EVENT_DATA:
+ return "Event Data";
+ case TRB_TR_NOOP:
+ return "No-Op";
+ case TRB_ENABLE_SLOT:
+ return "Enable Slot Command";
+ case TRB_DISABLE_SLOT:
+ return "Disable Slot Command";
+ case TRB_ADDR_DEV:
+ return "Address Device Command";
+ case TRB_CONFIG_EP:
+ return "Configure Endpoint Command";
+ case TRB_EVAL_CONTEXT:
+ return "Evaluate Context Command";
+ case TRB_RESET_EP:
+ return "Reset Endpoint Command";
+ case TRB_STOP_RING:
+ return "Stop Ring Command";
+ case TRB_SET_DEQ:
+ return "Set TR Dequeue Pointer Command";
+ case TRB_RESET_DEV:
+ return "Reset Device Command";
+ case TRB_FORCE_EVENT:
+ return "Force Event Command";
+ case TRB_NEG_BANDWIDTH:
+ return "Negotiate Bandwidth Command";
+ case TRB_SET_LT:
+ return "Set Latency Tolerance Value Command";
+ case TRB_GET_BW:
+ return "Get Port Bandwidth Command";
+ case TRB_FORCE_HEADER:
+ return "Force Header Command";
+ case TRB_CMD_NOOP:
+ return "No-Op Command";
+ case TRB_TRANSFER:
+ return "Transfer Event";
+ case TRB_COMPLETION:
+ return "Command Completion Event";
+ case TRB_PORT_STATUS:
+ return "Port Status Change Event";
+ case TRB_BANDWIDTH_EVENT:
+ return "Bandwidth Request Event";
+ case TRB_DOORBELL:
+ return "Doorbell Event";
+ case TRB_HC_EVENT:
+ return "Host Controller Event";
+ case TRB_DEV_NOTE:
+ return "Device Notification Event";
+ case TRB_MFINDEX_WRAP:
+ return "MFINDEX Wrap Event";
+ case TRB_NEC_CMD_COMP:
+ return "NEC Command Completion Event";
+ case TRB_NEC_GET_FW:
+ return "NET Get Firmware Revision Command";
+ default:
+ return "UNKNOWN";
+ }
+}
+
#define TRB_TYPE_LINK(x) (((x) & TRB_TYPE_BITMASK) == TRB_TYPE(TRB_LINK))
/* Above, but for __le32 types -- can avoid work by swapping constants: */
#define TRB_TYPE_LINK_LE32(x) (((x) & cpu_to_le32(TRB_TYPE_BITMASK)) == \
@@ -1393,6 +1535,28 @@ enum xhci_ring_type {
TYPE_EVENT,
};
+static inline const char *xhci_ring_type_string(enum xhci_ring_type type)
+{
+ switch (type) {
+ case TYPE_CTRL:
+ return "CTRL";
+ case TYPE_ISOC:
+ return "ISOC";
+ case TYPE_BULK:
+ return "BULK";
+ case TYPE_INTR:
+ return "INTR";
+ case TYPE_STREAM:
+ return "STREAM";
+ case TYPE_COMMAND:
+ return "CMD";
+ case TYPE_EVENT:
+ return "EVENT";
+ }
+
+ return "UNKNOWN";
+}
+
struct xhci_ring {
struct xhci_segment *first_seg;
struct xhci_segment *last_seg;
@@ -1444,9 +1608,9 @@ struct xhci_scratchpad {
};
struct urb_priv {
- int length;
- int td_cnt;
- struct xhci_td *td[0];
+ int num_tds;
+ int num_tds_done;
+ struct xhci_td td[0];
};
/*
@@ -1552,7 +1716,6 @@ struct xhci_hcd {
u8 max_ports;
u8 isoc_threshold;
int event_ring_max;
- int addr_64;
/* 4KB min, 128MB max */
int page_size;
/* Valid values are 12 to 20, inclusive */
@@ -1571,7 +1734,8 @@ struct xhci_hcd {
#define CMD_RING_STATE_STOPPED (1 << 2)
struct list_head cmd_list;
unsigned int cmd_ring_reserved_trbs;
- struct timer_list cmd_timer;
+ struct delayed_work cmd_timer;
+ struct completion cmd_ring_stop_completion;
struct xhci_command *current_cmd;
struct xhci_ring *event_ring;
struct xhci_erst erst;
@@ -1583,8 +1747,6 @@ struct xhci_hcd {
/* slot enabling and address device helpers */
/* these are not thread safe so use mutex */
struct mutex mutex;
- struct completion addr_dev;
- int slot_id;
/* For USB 3.0 LPM enable/disable. */
struct xhci_command *lpm_command;
/* Internal mirror of the HW's dcbaa */
@@ -1618,8 +1780,6 @@ struct xhci_hcd {
#define XHCI_STATE_DYING (1 << 0)
#define XHCI_STATE_HALTED (1 << 1)
#define XHCI_STATE_REMOVING (1 << 2)
- /* Statistics */
- int error_bitmask;
unsigned int quirks;
#define XHCI_LINK_TRB_QUIRK (1 << 0)
#define XHCI_RESET_EP_QUIRK (1 << 1)
@@ -1656,6 +1816,9 @@ struct xhci_hcd {
#define XHCI_SSIC_PORT_UNUSED (1 << 22)
#define XHCI_NO_64BIT_SUPPORT (1 << 23)
#define XHCI_MISSING_CAS (1 << 24)
+/* For controller with a broken Port Disable implementation */
+#define XHCI_BROKEN_PORT_PED (1 << 25)
+
unsigned int num_active_eps;
unsigned int limit_active_eps;
/* There are two roothubs to keep track of bus suspend info for */
@@ -1941,7 +2104,7 @@ void xhci_queue_config_ep_quirk(struct xhci_hcd *xhci,
unsigned int slot_id, unsigned int ep_index,
struct xhci_dequeue_state *deq_state);
void xhci_stop_endpoint_command_watchdog(unsigned long arg);
-void xhci_handle_command_timeout(unsigned long data);
+void xhci_handle_command_timeout(struct work_struct *work);
void xhci_ring_ep_doorbell(struct xhci_hcd *xhci, unsigned int slot_id,
unsigned int ep_index, unsigned int stream_id);
@@ -1990,4 +2153,211 @@ static inline struct xhci_ring *xhci_urb_to_transfer_ring(struct xhci_hcd *xhci,
urb->stream_id);
}
+static inline const char *xhci_decode_trb(u32 field0, u32 field1, u32 field2,
+ u32 field3)
+{
+ static char str[256];
+ int type = TRB_FIELD_TO_TYPE(field3);
+
+ switch (type) {
+ case TRB_LINK:
+ sprintf(str,
+ "TRB %08x%08x status '%s' len %d slot %d ep %d type '%s' flags %c:%c",
+ field1, field0,
+ xhci_trb_comp_code_string(GET_COMP_CODE(field2)),
+ EVENT_TRB_LEN(field2), TRB_TO_SLOT_ID(field3),
+ /* Macro decrements 1, maybe it shouldn't?!? */
+ TRB_TO_EP_INDEX(field3) + 1,
+ xhci_trb_type_string(TRB_FIELD_TO_TYPE(field3)),
+ field3 & EVENT_DATA ? 'E' : 'e',
+ field3 & TRB_CYCLE ? 'C' : 'c');
+ break;
+ case TRB_TRANSFER:
+ case TRB_COMPLETION:
+ case TRB_PORT_STATUS:
+ case TRB_BANDWIDTH_EVENT:
+ case TRB_DOORBELL:
+ case TRB_HC_EVENT:
+ case TRB_DEV_NOTE:
+ case TRB_MFINDEX_WRAP:
+ sprintf(str,
+ "TRB %08x%08x status '%s' len %d slot %d ep %d type '%s' flags %c:%c",
+ field1, field0,
+ xhci_trb_comp_code_string(GET_COMP_CODE(field2)),
+ EVENT_TRB_LEN(field2), TRB_TO_SLOT_ID(field3),
+ /* Macro decrements 1, maybe it shouldn't?!? */
+ TRB_TO_EP_INDEX(field3) + 1,
+ xhci_trb_type_string(TRB_FIELD_TO_TYPE(field3)),
+ field3 & EVENT_DATA ? 'E' : 'e',
+ field3 & TRB_CYCLE ? 'C' : 'c');
+
+ break;
+ case TRB_SETUP:
+ sprintf(str,
+ "bRequestType %02x bRequest %02x wValue %02x%02x wIndex %02x%02x wLength %d length %d TD size %d intr %d type '%s' flags %c:%c:%c:%c:%c:%c:%c:%c",
+ field0 & 0xff,
+ (field0 & 0xff00) >> 8,
+ (field0 & 0xff000000) >> 24,
+ (field0 & 0xff0000) >> 16,
+ (field1 & 0xff00) >> 8,
+ field1 & 0xff,
+ (field1 & 0xff000000) >> 16 |
+ (field1 & 0xff0000) >> 16,
+ TRB_LEN(field2), GET_TD_SIZE(field2),
+ GET_INTR_TARGET(field2),
+ xhci_trb_type_string(TRB_FIELD_TO_TYPE(field3)),
+ field3 & TRB_BEI ? 'B' : 'b',
+ field3 & TRB_IDT ? 'I' : 'i',
+ field3 & TRB_IOC ? 'I' : 'i',
+ field3 & TRB_CHAIN ? 'C' : 'c',
+ field3 & TRB_NO_SNOOP ? 'S' : 's',
+ field3 & TRB_ISP ? 'I' : 'i',
+ field3 & TRB_ENT ? 'E' : 'e',
+ field3 & TRB_CYCLE ? 'C' : 'c');
+ break;
+ case TRB_NORMAL:
+ case TRB_DATA:
+ case TRB_STATUS:
+ case TRB_ISOC:
+ case TRB_EVENT_DATA:
+ case TRB_TR_NOOP:
+ sprintf(str,
+ "Buffer %08x%08x length %d TD size %d intr %d type '%s' flags %c:%c:%c:%c:%c:%c:%c:%c",
+ field1, field0, TRB_LEN(field2), GET_TD_SIZE(field2),
+ GET_INTR_TARGET(field2),
+ xhci_trb_type_string(TRB_FIELD_TO_TYPE(field3)),
+ field3 & TRB_BEI ? 'B' : 'b',
+ field3 & TRB_IDT ? 'I' : 'i',
+ field3 & TRB_IOC ? 'I' : 'i',
+ field3 & TRB_CHAIN ? 'C' : 'c',
+ field3 & TRB_NO_SNOOP ? 'S' : 's',
+ field3 & TRB_ISP ? 'I' : 'i',
+ field3 & TRB_ENT ? 'E' : 'e',
+ field3 & TRB_CYCLE ? 'C' : 'c');
+ break;
+
+ case TRB_CMD_NOOP:
+ case TRB_ENABLE_SLOT:
+ sprintf(str,
+ "%s: flags %c",
+ xhci_trb_type_string(TRB_FIELD_TO_TYPE(field3)),
+ field3 & TRB_CYCLE ? 'C' : 'c');
+ break;
+ case TRB_DISABLE_SLOT:
+ case TRB_NEG_BANDWIDTH:
+ sprintf(str,
+ "%s: slot %d flags %c",
+ xhci_trb_type_string(TRB_FIELD_TO_TYPE(field3)),
+ TRB_TO_SLOT_ID(field3),
+ field3 & TRB_CYCLE ? 'C' : 'c');
+ break;
+ case TRB_ADDR_DEV:
+ sprintf(str,
+ "%s: ctx %08x%08x slot %d flags %c:%c",
+ xhci_trb_type_string(TRB_FIELD_TO_TYPE(field3)),
+ field1, field0,
+ TRB_TO_SLOT_ID(field3),
+ field3 & TRB_BSR ? 'B' : 'b',
+ field3 & TRB_CYCLE ? 'C' : 'c');
+ break;
+ case TRB_CONFIG_EP:
+ sprintf(str,
+ "%s: ctx %08x%08x slot %d flags %c:%c",
+ xhci_trb_type_string(TRB_FIELD_TO_TYPE(field3)),
+ field1, field0,
+ TRB_TO_SLOT_ID(field3),
+ field3 & TRB_DC ? 'D' : 'd',
+ field3 & TRB_CYCLE ? 'C' : 'c');
+ break;
+ case TRB_EVAL_CONTEXT:
+ sprintf(str,
+ "%s: ctx %08x%08x slot %d flags %c",
+ xhci_trb_type_string(TRB_FIELD_TO_TYPE(field3)),
+ field1, field0,
+ TRB_TO_SLOT_ID(field3),
+ field3 & TRB_CYCLE ? 'C' : 'c');
+ break;
+ case TRB_RESET_EP:
+ sprintf(str,
+ "%s: ctx %08x%08x slot %d ep %d flags %c",
+ xhci_trb_type_string(TRB_FIELD_TO_TYPE(field3)),
+ field1, field0,
+ TRB_TO_SLOT_ID(field3),
+ /* Macro decrements 1, maybe it shouldn't?!? */
+ TRB_TO_EP_INDEX(field3) + 1,
+ field3 & TRB_CYCLE ? 'C' : 'c');
+ break;
+ case TRB_STOP_RING:
+ sprintf(str,
+ "%s: slot %d sp %d ep %d flags %c",
+ xhci_trb_type_string(TRB_FIELD_TO_TYPE(field3)),
+ TRB_TO_SLOT_ID(field3),
+ TRB_TO_SUSPEND_PORT(field3),
+ /* Macro decrements 1, maybe it shouldn't?!? */
+ TRB_TO_EP_INDEX(field3) + 1,
+ field3 & TRB_CYCLE ? 'C' : 'c');
+ break;
+ case TRB_SET_DEQ:
+ sprintf(str,
+ "%s: deq %08x%08x stream %d slot %d ep %d flags %c",
+ xhci_trb_type_string(TRB_FIELD_TO_TYPE(field3)),
+ field1, field0,
+ TRB_TO_STREAM_ID(field2),
+ TRB_TO_SLOT_ID(field3),
+ /* Macro decrements 1, maybe it shouldn't?!? */
+ TRB_TO_EP_INDEX(field3) + 1,
+ field3 & TRB_CYCLE ? 'C' : 'c');
+ break;
+ case TRB_RESET_DEV:
+ sprintf(str,
+ "%s: slot %d flags %c",
+ xhci_trb_type_string(TRB_FIELD_TO_TYPE(field3)),
+ TRB_TO_SLOT_ID(field3),
+ field3 & TRB_CYCLE ? 'C' : 'c');
+ break;
+ case TRB_FORCE_EVENT:
+ sprintf(str,
+ "%s: event %08x%08x vf intr %d vf id %d flags %c",
+ xhci_trb_type_string(TRB_FIELD_TO_TYPE(field3)),
+ field1, field0,
+ TRB_TO_VF_INTR_TARGET(field2),
+ TRB_TO_VF_ID(field3),
+ field3 & TRB_CYCLE ? 'C' : 'c');
+ break;
+ case TRB_SET_LT:
+ sprintf(str,
+ "%s: belt %d flags %c",
+ xhci_trb_type_string(TRB_FIELD_TO_TYPE(field3)),
+ TRB_TO_BELT(field3),
+ field3 & TRB_CYCLE ? 'C' : 'c');
+ break;
+ case TRB_GET_BW:
+ sprintf(str,
+ "%s: ctx %08x%08x slot %d speed %d flags %c",
+ xhci_trb_type_string(TRB_FIELD_TO_TYPE(field3)),
+ field1, field0,
+ TRB_TO_SLOT_ID(field3),
+ TRB_TO_DEV_SPEED(field3),
+ field3 & TRB_CYCLE ? 'C' : 'c');
+ break;
+ case TRB_FORCE_HEADER:
+ sprintf(str,
+ "%s: info %08x%08x%08x pkt type %d roothub port %d flags %c",
+ xhci_trb_type_string(TRB_FIELD_TO_TYPE(field3)),
+ field2, field1, field0 & 0xffffffe0,
+ TRB_TO_PACKET_TYPE(field0),
+ TRB_TO_ROOTHUB_PORT(field3),
+ field3 & TRB_CYCLE ? 'C' : 'c');
+ break;
+ default:
+ sprintf(str,
+ "type '%s' -> raw %08x %08x %08x %08x",
+ xhci_trb_type_string(TRB_FIELD_TO_TYPE(field3)),
+ field0, field1, field2, field3);
+ }
+
+ return str;
+}
+
+
#endif /* __LINUX_XHCI_HCD_H */
diff --git a/drivers/usb/image/mdc800.c b/drivers/usb/image/mdc800.c
index 5cf2633cdb04..e92540a21b6b 100644
--- a/drivers/usb/image/mdc800.c
+++ b/drivers/usb/image/mdc800.c
@@ -85,7 +85,7 @@
* (20/10/1999)
*/
-#include <linux/sched.h>
+#include <linux/sched/signal.h>
#include <linux/signal.h>
#include <linux/spinlock.h>
#include <linux/errno.h>
diff --git a/drivers/usb/isp1760/isp1760-if.c b/drivers/usb/isp1760/isp1760-if.c
index 9535b2872183..79205b31e4a9 100644
--- a/drivers/usb/isp1760/isp1760-if.c
+++ b/drivers/usb/isp1760/isp1760-if.c
@@ -197,7 +197,7 @@ static int isp1760_plat_probe(struct platform_device *pdev)
irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!irq_res) {
- pr_warning("isp1760: IRQ resource not available\n");
+ pr_warn("isp1760: IRQ resource not available\n");
return -ENODEV;
}
irqflags = irq_res->flags & IRQF_TRIGGER_MASK;
diff --git a/drivers/usb/isp1760/isp1760-udc.c b/drivers/usb/isp1760/isp1760-udc.c
index 1c3d0fd658fa..69400f3da886 100644
--- a/drivers/usb/isp1760/isp1760-udc.c
+++ b/drivers/usb/isp1760/isp1760-udc.c
@@ -1250,7 +1250,7 @@ static int isp1760_udc_stop(struct usb_gadget *gadget)
return 0;
}
-static struct usb_gadget_ops isp1760_udc_ops = {
+static const struct usb_gadget_ops isp1760_udc_ops = {
.get_frame = isp1760_udc_get_frame,
.wakeup = isp1760_udc_wakeup,
.set_selfpowered = isp1760_udc_set_selfpowered,
diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig
index 47b357760afc..1d1d70d62a19 100644
--- a/drivers/usb/misc/Kconfig
+++ b/drivers/usb/misc/Kconfig
@@ -233,6 +233,15 @@ config USB_EZUSB_FX2
Say Y here if you need EZUSB device support.
(Cypress FX/FX2/FX2LP microcontrollers)
+config USB_HUB_USB251XB
+ tristate "USB251XB Hub Controller Configuration Driver"
+ depends on I2C
+ help
+ This option enables support for configuration via SMBus of the
+ Microchip USB251xB/xBi USB 2.0 Hub Controller series.
+ Configuration parameters may be set in devicetree or platform data.
+ Say Y or M here if you need to configure such a device via SMBus.
+
config USB_HSIC_USB3503
tristate "USB3503 HSIC to USB20 Driver"
depends on I2C
diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile
index 3d1992750da4..f6ac6c99a6e6 100644
--- a/drivers/usb/misc/Makefile
+++ b/drivers/usb/misc/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_USB_TRANCEVIBRATOR) += trancevibrator.o
obj-$(CONFIG_USB_USS720) += uss720.o
obj-$(CONFIG_USB_SEVSEG) += usbsevseg.o
obj-$(CONFIG_USB_YUREX) += yurex.o
+obj-$(CONFIG_USB_HUB_USB251XB) += usb251xb.o
obj-$(CONFIG_USB_HSIC_USB3503) += usb3503.o
obj-$(CONFIG_USB_HSIC_USB4604) += usb4604.o
obj-$(CONFIG_USB_CHAOSKEY) += chaoskey.o
diff --git a/drivers/usb/misc/adutux.c b/drivers/usb/misc/adutux.c
index 564268fca07a..db9a9e6ff6be 100644
--- a/drivers/usb/misc/adutux.c
+++ b/drivers/usb/misc/adutux.c
@@ -21,6 +21,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
+#include <linux/sched/signal.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/module.h>
@@ -389,10 +390,6 @@ static ssize_t adu_read(struct file *file, __user char *buffer, size_t count,
dev->secondary_head += (amount - i);
bytes_read += (amount - i);
bytes_to_read -= (amount - i);
- if (i) {
- retval = bytes_read ? bytes_read : -EFAULT;
- goto exit;
- }
} else {
/* we check the primary buffer */
spin_lock_irqsave (&dev->buflock, flags);
@@ -567,20 +564,20 @@ static ssize_t adu_write(struct file *file, const __user char *buffer,
}
dev_dbg(&dev->udev->dev,
- "%s : in progress, count = %Zd\n",
+ "%s : in progress, count = %zd\n",
__func__, count);
} else {
spin_unlock_irqrestore(&dev->buflock, flags);
set_current_state(TASK_RUNNING);
remove_wait_queue(&dev->write_wait, &waita);
- dev_dbg(&dev->udev->dev, "%s : sending, count = %Zd\n",
+ dev_dbg(&dev->udev->dev, "%s : sending, count = %zd\n",
__func__, count);
/* write the data into interrupt_out_buffer from userspace */
buffer_size = usb_endpoint_maxp(dev->interrupt_out_endpoint);
bytes_to_write = count > buffer_size ? buffer_size : count;
dev_dbg(&dev->udev->dev,
- "%s : buffer_size = %Zd, count = %Zd, bytes_to_write = %Zd\n",
+ "%s : buffer_size = %zd, count = %zd, bytes_to_write = %zd\n",
__func__, buffer_size, count, bytes_to_write);
if (copy_from_user(dev->interrupt_out_buffer, buffer, bytes_to_write) != 0) {
diff --git a/drivers/usb/misc/chaoskey.c b/drivers/usb/misc/chaoskey.c
index 6ddd08a32777..aa350dc9eb25 100644
--- a/drivers/usb/misc/chaoskey.c
+++ b/drivers/usb/misc/chaoskey.c
@@ -215,19 +215,7 @@ static int chaoskey_probe(struct usb_interface *interface,
dev->hwrng.name = dev->name ? dev->name : chaoskey_driver.name;
dev->hwrng.read = chaoskey_rng_read;
-
- /* Set the 'quality' metric. Quality is measured in units of
- * 1/1024's of a bit ("mills"). This should be set to 1024,
- * but there is a bug in the hwrng core which masks it with
- * 1023.
- *
- * The patch that has been merged to the crypto development
- * tree for that bug limits the value to 1024 at most, so by
- * setting this to 1024 + 1023, we get 1023 before the fix is
- * merged and 1024 afterwards. We'll patch this driver once
- * both bits of code are in the same tree.
- */
- dev->hwrng.quality = 1024 + 1023;
+ dev->hwrng.quality = 1024;
dev->hwrng_registered = (hwrng_register(&dev->hwrng) == 0);
if (!dev->hwrng_registered)
diff --git a/drivers/usb/misc/ftdi-elan.c b/drivers/usb/misc/ftdi-elan.c
index 9a82f8308ad7..01a9373b7e18 100644
--- a/drivers/usb/misc/ftdi-elan.c
+++ b/drivers/usb/misc/ftdi-elan.c
@@ -48,7 +48,7 @@
#include <linux/module.h>
#include <linux/kref.h>
#include <linux/mutex.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <linux/usb.h>
#include <linux/workqueue.h>
#include <linux/platform_device.h>
diff --git a/drivers/usb/misc/idmouse.c b/drivers/usb/misc/idmouse.c
index 2975e80b7a56..502bfe30a077 100644
--- a/drivers/usb/misc/idmouse.c
+++ b/drivers/usb/misc/idmouse.c
@@ -17,13 +17,14 @@
*/
#include <linux/kernel.h>
+#include <linux/sched/signal.h>
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/completion.h>
#include <linux/mutex.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <linux/usb.h>
/* image constants */
@@ -346,6 +347,9 @@ static int idmouse_probe(struct usb_interface *interface,
if (iface_desc->desc.bInterfaceClass != 0x0A)
return -ENODEV;
+ if (iface_desc->desc.bNumEndpoints < 1)
+ return -ENODEV;
+
/* allocate memory for our device state and initialize it */
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (dev == NULL)
diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c
index 095778ff984d..37c63cb39714 100644
--- a/drivers/usb/misc/iowarrior.c
+++ b/drivers/usb/misc/iowarrior.c
@@ -781,12 +781,6 @@ static int iowarrior_probe(struct usb_interface *interface,
iface_desc = interface->cur_altsetting;
dev->product_id = le16_to_cpu(udev->descriptor.idProduct);
- if (iface_desc->desc.bNumEndpoints < 1) {
- dev_err(&interface->dev, "Invalid number of endpoints\n");
- retval = -EINVAL;
- goto error;
- }
-
/* set up the endpoint information */
for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
endpoint = &iface_desc->endpoint[i].desc;
@@ -797,6 +791,21 @@ static int iowarrior_probe(struct usb_interface *interface,
/* this one will match for the IOWarrior56 only */
dev->int_out_endpoint = endpoint;
}
+
+ if (!dev->int_in_endpoint) {
+ dev_err(&interface->dev, "no interrupt-in endpoint found\n");
+ retval = -ENODEV;
+ goto error;
+ }
+
+ if (dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW56) {
+ if (!dev->int_out_endpoint) {
+ dev_err(&interface->dev, "no interrupt-out endpoint found\n");
+ retval = -ENODEV;
+ goto error;
+ }
+ }
+
/* we have to check the report_size often, so remember it in the endianness suitable for our machine */
dev->report_size = usb_endpoint_maxp(dev->int_in_endpoint);
if ((dev->interface->cur_altsetting->desc.bInterfaceNumber == 0) &&
diff --git a/drivers/usb/misc/ldusb.c b/drivers/usb/misc/ldusb.c
index 9ca595632f17..3bc5356832db 100644
--- a/drivers/usb/misc/ldusb.c
+++ b/drivers/usb/misc/ldusb.c
@@ -28,7 +28,7 @@
#include <linux/module.h>
#include <linux/mutex.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <linux/input.h>
#include <linux/usb.h>
#include <linux/poll.h>
diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c
index c8fbe7b739a0..322a042d6e59 100644
--- a/drivers/usb/misc/legousbtower.c
+++ b/drivers/usb/misc/legousbtower.c
@@ -83,7 +83,7 @@
#include <linux/module.h>
#include <linux/completion.h>
#include <linux/mutex.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <linux/usb.h>
#include <linux/poll.h>
@@ -673,7 +673,7 @@ static ssize_t tower_write (struct file *file, const char __user *buffer, size_t
/* write the data into interrupt_out_buffer from userspace */
bytes_to_write = min_t(int, count, write_buffer_size);
- dev_dbg(&dev->udev->dev, "%s: count = %Zd, bytes_to_write = %Zd\n",
+ dev_dbg(&dev->udev->dev, "%s: count = %zd, bytes_to_write = %zd\n",
__func__, count, bytes_to_write);
if (copy_from_user (dev->interrupt_out_buffer, buffer, bytes_to_write)) {
diff --git a/drivers/usb/misc/lvstest.c b/drivers/usb/misc/lvstest.c
index 77176511658f..d3d124753266 100644
--- a/drivers/usb/misc/lvstest.c
+++ b/drivers/usb/misc/lvstest.c
@@ -366,6 +366,10 @@ static int lvs_rh_probe(struct usb_interface *intf,
hdev = interface_to_usbdev(intf);
desc = intf->cur_altsetting;
+
+ if (desc->desc.bNumEndpoints < 1)
+ return -ENODEV;
+
endpoint = &desc->endpoint[0].desc;
/* valid only for SS root hub */
diff --git a/drivers/usb/misc/rio500.c b/drivers/usb/misc/rio500.c
index 13731d512624..b106ce76997b 100644
--- a/drivers/usb/misc/rio500.c
+++ b/drivers/usb/misc/rio500.c
@@ -31,7 +31,7 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/signal.h>
-#include <linux/sched.h>
+#include <linux/sched/signal.h>
#include <linux/mutex.h>
#include <linux/errno.h>
#include <linux/random.h>
@@ -421,7 +421,7 @@ read_rio(struct file *file, char __user *buffer, size_t count, loff_t * ppos)
} else if (result != -EREMOTEIO) {
mutex_unlock(&(rio->lock));
dev_err(&rio->rio_dev->dev,
- "Read Whoops - result:%u partial:%u this_read:%u\n",
+ "Read Whoops - result:%d partial:%u this_read:%u\n",
result, partial, this_read);
return -EIO;
} else {
diff --git a/drivers/usb/misc/sisusbvga/sisusb.c b/drivers/usb/misc/sisusbvga/sisusb.c
index 05bd39d62568..440d7fef58cc 100644
--- a/drivers/usb/misc/sisusbvga/sisusb.c
+++ b/drivers/usb/misc/sisusbvga/sisusb.c
@@ -1831,16 +1831,10 @@ static int sisusb_set_default_mode(struct sisusb_usb_data *sisusb,
SETIREGANDOR(SISCR, 0x09, 0x5f, ((crtcdata[16] & 0x01) << 5));
SETIREG(SISCR, 0x14, 0x4f);
du = (modex / 16) * (bpp * 2); /* offset/pitch */
- if (modex % 16)
- du += bpp;
-
SETIREGANDOR(SISSR, 0x0e, 0xf0, ((du >> 8) & 0x0f));
SETIREG(SISCR, 0x13, (du & 0xff));
du <<= 5;
tmp8 = du >> 8;
- if (du & 0xff)
- tmp8++;
-
SETIREG(SISSR, 0x10, tmp8);
SETIREG(SISSR, 0x31, 0x00); /* VCLK */
SETIREG(SISSR, 0x2b, 0x1b);
diff --git a/drivers/usb/misc/sisusbvga/sisusb_con.c b/drivers/usb/misc/sisusbvga/sisusb_con.c
index 460cebf322e3..4b5777ec1501 100644
--- a/drivers/usb/misc/sisusbvga/sisusb_con.c
+++ b/drivers/usb/misc/sisusbvga/sisusb_con.c
@@ -686,8 +686,6 @@ static void
sisusbcon_scrolldelta(struct vc_data *c, int lines)
{
struct sisusb_usb_data *sisusb;
- int margin = c->vc_size_row * 4;
- int ul, we, p, st;
sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num);
if (!sisusb)
@@ -700,39 +698,8 @@ sisusbcon_scrolldelta(struct vc_data *c, int lines)
return;
}
- if (!lines) /* Turn scrollback off */
- c->vc_visible_origin = c->vc_origin;
- else {
-
- if (sisusb->con_rolled_over >
- (c->vc_scr_end - sisusb->scrbuf) + margin) {
-
- ul = c->vc_scr_end - sisusb->scrbuf;
- we = sisusb->con_rolled_over + c->vc_size_row;
-
- } else {
-
- ul = 0;
- we = sisusb->scrbuf_size;
-
- }
-
- p = (c->vc_visible_origin - sisusb->scrbuf - ul + we) % we +
- lines * c->vc_size_row;
-
- st = (c->vc_origin - sisusb->scrbuf - ul + we) % we;
-
- if (st < 2 * margin)
- margin = 0;
-
- if (p < margin)
- p = 0;
-
- if (p > st - margin)
- p = st;
-
- c->vc_visible_origin = sisusb->scrbuf + (p + ul) % we;
- }
+ vc_scrolldelta_helper(c, lines, sisusb->con_rolled_over,
+ (void *)sisusb->scrbuf, sisusb->scrbuf_size);
sisusbcon_set_start_address(sisusb, c);
@@ -808,9 +775,10 @@ sisusbcon_cursor(struct vc_data *c, int mode)
mutex_unlock(&sisusb->lock);
}
-static int
+static bool
sisusbcon_scroll_area(struct vc_data *c, struct sisusb_usb_data *sisusb,
- int t, int b, int dir, int lines)
+ unsigned int t, unsigned int b, enum con_scroll dir,
+ unsigned int lines)
{
int cols = sisusb->sisusb_num_columns;
int length = ((b - t) * cols) * 2;
@@ -852,8 +820,9 @@ sisusbcon_scroll_area(struct vc_data *c, struct sisusb_usb_data *sisusb,
}
/* Interface routine */
-static int
-sisusbcon_scroll(struct vc_data *c, int t, int b, int dir, int lines)
+static bool
+sisusbcon_scroll(struct vc_data *c, unsigned int t, unsigned int b,
+ enum con_scroll dir, unsigned int lines)
{
struct sisusb_usb_data *sisusb;
u16 eattr = c->vc_video_erase_char;
@@ -870,17 +839,17 @@ sisusbcon_scroll(struct vc_data *c, int t, int b, int dir, int lines)
*/
if (!lines)
- return 1;
+ return true;
sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num);
if (!sisusb)
- return 0;
+ return false;
/* sisusb->lock is down */
if (sisusb_is_inactive(c, sisusb)) {
mutex_unlock(&sisusb->lock);
- return 0;
+ return false;
}
/* Special case */
@@ -971,7 +940,7 @@ sisusbcon_scroll(struct vc_data *c, int t, int b, int dir, int lines)
mutex_unlock(&sisusb->lock);
- return 1;
+ return true;
}
/* Interface routine */
diff --git a/drivers/usb/misc/usb251xb.c b/drivers/usb/misc/usb251xb.c
new file mode 100644
index 000000000000..91f66d68bcb7
--- /dev/null
+++ b/drivers/usb/misc/usb251xb.c
@@ -0,0 +1,594 @@
+/*
+ * Driver for Microchip USB251xB USB 2.0 Hi-Speed Hub Controller
+ * Configuration via SMBus.
+ *
+ * Copyright (c) 2017 SKIDATA AG
+ *
+ * This work is based on the USB3503 driver by Dongjin Kim and
+ * a not-accepted patch by Fabien Lahoudere, see:
+ * https://patchwork.kernel.org/patch/9257715/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/nls.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/slab.h>
+
+/* Internal Register Set Addresses & Default Values acc. to DS00001692C */
+#define USB251XB_ADDR_VENDOR_ID_LSB 0x00
+#define USB251XB_ADDR_VENDOR_ID_MSB 0x01
+#define USB251XB_DEF_VENDOR_ID 0x0424
+
+#define USB251XB_ADDR_PRODUCT_ID_LSB 0x02
+#define USB251XB_ADDR_PRODUCT_ID_MSB 0x03
+#define USB251XB_DEF_PRODUCT_ID_12 0x2512 /* USB2512B/12Bi */
+#define USB251XB_DEF_PRODUCT_ID_13 0x2513 /* USB2513B/13Bi */
+#define USB251XB_DEF_PRODUCT_ID_14 0x2514 /* USB2514B/14Bi */
+
+#define USB251XB_ADDR_DEVICE_ID_LSB 0x04
+#define USB251XB_ADDR_DEVICE_ID_MSB 0x05
+#define USB251XB_DEF_DEVICE_ID 0x0BB3
+
+#define USB251XB_ADDR_CONFIG_DATA_1 0x06
+#define USB251XB_DEF_CONFIG_DATA_1 0x9B
+#define USB251XB_ADDR_CONFIG_DATA_2 0x07
+#define USB251XB_DEF_CONFIG_DATA_2 0x20
+#define USB251XB_ADDR_CONFIG_DATA_3 0x08
+#define USB251XB_DEF_CONFIG_DATA_3 0x02
+
+#define USB251XB_ADDR_NON_REMOVABLE_DEVICES 0x09
+#define USB251XB_DEF_NON_REMOVABLE_DEVICES 0x00
+
+#define USB251XB_ADDR_PORT_DISABLE_SELF 0x0A
+#define USB251XB_DEF_PORT_DISABLE_SELF 0x00
+#define USB251XB_ADDR_PORT_DISABLE_BUS 0x0B
+#define USB251XB_DEF_PORT_DISABLE_BUS 0x00
+
+#define USB251XB_ADDR_MAX_POWER_SELF 0x0C
+#define USB251XB_DEF_MAX_POWER_SELF 0x01
+#define USB251XB_ADDR_MAX_POWER_BUS 0x0D
+#define USB251XB_DEF_MAX_POWER_BUS 0x32
+
+#define USB251XB_ADDR_MAX_CURRENT_SELF 0x0E
+#define USB251XB_DEF_MAX_CURRENT_SELF 0x01
+#define USB251XB_ADDR_MAX_CURRENT_BUS 0x0F
+#define USB251XB_DEF_MAX_CURRENT_BUS 0x32
+
+#define USB251XB_ADDR_POWER_ON_TIME 0x10
+#define USB251XB_DEF_POWER_ON_TIME 0x32
+
+#define USB251XB_ADDR_LANGUAGE_ID_HIGH 0x11
+#define USB251XB_ADDR_LANGUAGE_ID_LOW 0x12
+#define USB251XB_DEF_LANGUAGE_ID 0x0000
+
+#define USB251XB_STRING_BUFSIZE 62
+#define USB251XB_ADDR_MANUFACTURER_STRING_LEN 0x13
+#define USB251XB_ADDR_MANUFACTURER_STRING 0x16
+#define USB251XB_DEF_MANUFACTURER_STRING "Microchip"
+
+#define USB251XB_ADDR_PRODUCT_STRING_LEN 0x14
+#define USB251XB_ADDR_PRODUCT_STRING 0x54
+#define USB251XB_DEF_PRODUCT_STRING "USB251xB/xBi"
+
+#define USB251XB_ADDR_SERIAL_STRING_LEN 0x15
+#define USB251XB_ADDR_SERIAL_STRING 0x92
+#define USB251XB_DEF_SERIAL_STRING ""
+
+#define USB251XB_ADDR_BATTERY_CHARGING_ENABLE 0xD0
+#define USB251XB_DEF_BATTERY_CHARGING_ENABLE 0x00
+
+#define USB251XB_ADDR_BOOST_UP 0xF6
+#define USB251XB_DEF_BOOST_UP 0x00
+#define USB251XB_ADDR_BOOST_X 0xF8
+#define USB251XB_DEF_BOOST_X 0x00
+
+#define USB251XB_ADDR_PORT_SWAP 0xFA
+#define USB251XB_DEF_PORT_SWAP 0x00
+
+#define USB251XB_ADDR_PORT_MAP_12 0xFB
+#define USB251XB_DEF_PORT_MAP_12 0x00
+#define USB251XB_ADDR_PORT_MAP_34 0xFC
+#define USB251XB_DEF_PORT_MAP_34 0x00 /* USB2513B/i & USB2514B/i only */
+
+#define USB251XB_ADDR_STATUS_COMMAND 0xFF
+#define USB251XB_STATUS_COMMAND_SMBUS_DOWN 0x04
+#define USB251XB_STATUS_COMMAND_RESET 0x02
+#define USB251XB_STATUS_COMMAND_ATTACH 0x01
+
+#define USB251XB_I2C_REG_SZ 0x100
+#define USB251XB_I2C_WRITE_SZ 0x10
+
+#define DRIVER_NAME "usb251xb"
+#define DRIVER_DESC "Microchip USB 2.0 Hi-Speed Hub Controller"
+#define DRIVER_VERSION "1.0"
+
+struct usb251xb {
+ struct device *dev;
+ struct i2c_client *i2c;
+ u8 skip_config;
+ int gpio_reset;
+ u16 vendor_id;
+ u16 product_id;
+ u16 device_id;
+ u8 conf_data1;
+ u8 conf_data2;
+ u8 conf_data3;
+ u8 non_rem_dev;
+ u8 port_disable_sp;
+ u8 port_disable_bp;
+ u8 max_power_sp;
+ u8 max_power_bp;
+ u8 max_current_sp;
+ u8 max_current_bp;
+ u8 power_on_time;
+ u16 lang_id;
+ u8 manufacturer_len;
+ u8 product_len;
+ u8 serial_len;
+ char manufacturer[USB251XB_STRING_BUFSIZE];
+ char product[USB251XB_STRING_BUFSIZE];
+ char serial[USB251XB_STRING_BUFSIZE];
+ u8 bat_charge_en;
+ u8 boost_up;
+ u8 boost_x;
+ u8 port_swap;
+ u8 port_map12;
+ u8 port_map34;
+ u8 status;
+};
+
+struct usb251xb_data {
+ u16 product_id;
+ char product_str[USB251XB_STRING_BUFSIZE / 2]; /* ASCII string */
+};
+
+static const struct usb251xb_data usb2512b_data = {
+ .product_id = 0x2512,
+ .product_str = "USB2512B",
+};
+
+static const struct usb251xb_data usb2512bi_data = {
+ .product_id = 0x2512,
+ .product_str = "USB2512Bi",
+};
+
+static const struct usb251xb_data usb2513b_data = {
+ .product_id = 0x2513,
+ .product_str = "USB2513B",
+};
+
+static const struct usb251xb_data usb2513bi_data = {
+ .product_id = 0x2513,
+ .product_str = "USB2513Bi",
+};
+
+static const struct usb251xb_data usb2514b_data = {
+ .product_id = 0x2514,
+ .product_str = "USB2514B",
+};
+
+static const struct usb251xb_data usb2514bi_data = {
+ .product_id = 0x2514,
+ .product_str = "USB2514Bi",
+};
+
+static void usb251xb_reset(struct usb251xb *hub, int state)
+{
+ if (!gpio_is_valid(hub->gpio_reset))
+ return;
+
+ gpio_set_value_cansleep(hub->gpio_reset, state);
+
+ /* wait for hub recovery/stabilization */
+ if (state)
+ usleep_range(500, 750); /* >=500us at power on */
+ else
+ usleep_range(1, 10); /* >=1us at power down */
+}
+
+static int usb251xb_connect(struct usb251xb *hub)
+{
+ struct device *dev = hub->dev;
+ int err, i;
+ char i2c_wb[USB251XB_I2C_REG_SZ];
+
+ memset(i2c_wb, 0, USB251XB_I2C_REG_SZ);
+
+ if (hub->skip_config) {
+ dev_info(dev, "Skip hub configuration, only attach.\n");
+ i2c_wb[0] = 0x01;
+ i2c_wb[1] = USB251XB_STATUS_COMMAND_ATTACH;
+
+ usb251xb_reset(hub, 1);
+
+ err = i2c_smbus_write_i2c_block_data(hub->i2c,
+ USB251XB_ADDR_STATUS_COMMAND, 2, i2c_wb);
+ if (err) {
+ dev_err(dev, "attaching hub failed: %d\n", err);
+ return err;
+ }
+ return 0;
+ }
+
+ i2c_wb[USB251XB_ADDR_VENDOR_ID_MSB] = (hub->vendor_id >> 8) & 0xFF;
+ i2c_wb[USB251XB_ADDR_VENDOR_ID_LSB] = hub->vendor_id & 0xFF;
+ i2c_wb[USB251XB_ADDR_PRODUCT_ID_MSB] = (hub->product_id >> 8) & 0xFF;
+ i2c_wb[USB251XB_ADDR_PRODUCT_ID_LSB] = hub->product_id & 0xFF;
+ i2c_wb[USB251XB_ADDR_DEVICE_ID_MSB] = (hub->device_id >> 8) & 0xFF;
+ i2c_wb[USB251XB_ADDR_DEVICE_ID_LSB] = hub->device_id & 0xFF;
+ i2c_wb[USB251XB_ADDR_CONFIG_DATA_1] = hub->conf_data1;
+ i2c_wb[USB251XB_ADDR_CONFIG_DATA_2] = hub->conf_data2;
+ i2c_wb[USB251XB_ADDR_CONFIG_DATA_3] = hub->conf_data3;
+ i2c_wb[USB251XB_ADDR_NON_REMOVABLE_DEVICES] = hub->non_rem_dev;
+ i2c_wb[USB251XB_ADDR_PORT_DISABLE_SELF] = hub->port_disable_sp;
+ i2c_wb[USB251XB_ADDR_PORT_DISABLE_BUS] = hub->port_disable_bp;
+ i2c_wb[USB251XB_ADDR_MAX_POWER_SELF] = hub->max_power_sp;
+ i2c_wb[USB251XB_ADDR_MAX_POWER_BUS] = hub->max_power_bp;
+ i2c_wb[USB251XB_ADDR_MAX_CURRENT_SELF] = hub->max_current_sp;
+ i2c_wb[USB251XB_ADDR_MAX_CURRENT_BUS] = hub->max_current_bp;
+ i2c_wb[USB251XB_ADDR_POWER_ON_TIME] = hub->power_on_time;
+ i2c_wb[USB251XB_ADDR_LANGUAGE_ID_HIGH] = (hub->lang_id >> 8) & 0xFF;
+ i2c_wb[USB251XB_ADDR_LANGUAGE_ID_LOW] = hub->lang_id & 0xFF;
+ i2c_wb[USB251XB_ADDR_MANUFACTURER_STRING_LEN] = hub->manufacturer_len;
+ i2c_wb[USB251XB_ADDR_PRODUCT_STRING_LEN] = hub->product_len;
+ i2c_wb[USB251XB_ADDR_SERIAL_STRING_LEN] = hub->serial_len;
+ memcpy(&i2c_wb[USB251XB_ADDR_MANUFACTURER_STRING], hub->manufacturer,
+ USB251XB_STRING_BUFSIZE);
+ memcpy(&i2c_wb[USB251XB_ADDR_SERIAL_STRING], hub->serial,
+ USB251XB_STRING_BUFSIZE);
+ memcpy(&i2c_wb[USB251XB_ADDR_PRODUCT_STRING], hub->product,
+ USB251XB_STRING_BUFSIZE);
+ i2c_wb[USB251XB_ADDR_BATTERY_CHARGING_ENABLE] = hub->bat_charge_en;
+ i2c_wb[USB251XB_ADDR_BOOST_UP] = hub->boost_up;
+ i2c_wb[USB251XB_ADDR_BOOST_X] = hub->boost_x;
+ i2c_wb[USB251XB_ADDR_PORT_SWAP] = hub->port_swap;
+ i2c_wb[USB251XB_ADDR_PORT_MAP_12] = hub->port_map12;
+ i2c_wb[USB251XB_ADDR_PORT_MAP_34] = hub->port_map34;
+ i2c_wb[USB251XB_ADDR_STATUS_COMMAND] = USB251XB_STATUS_COMMAND_ATTACH;
+
+ usb251xb_reset(hub, 1);
+
+ /* write registers */
+ for (i = 0; i < (USB251XB_I2C_REG_SZ / USB251XB_I2C_WRITE_SZ); i++) {
+ int offset = i * USB251XB_I2C_WRITE_SZ;
+ char wbuf[USB251XB_I2C_WRITE_SZ + 1];
+
+ /* The first data byte transferred tells the hub how many data
+ * bytes will follow (byte count).
+ */
+ wbuf[0] = USB251XB_I2C_WRITE_SZ;
+ memcpy(&wbuf[1], &i2c_wb[offset], USB251XB_I2C_WRITE_SZ);
+
+ dev_dbg(dev, "writing %d byte block %d to 0x%02X\n",
+ USB251XB_I2C_WRITE_SZ, i, offset);
+
+ err = i2c_smbus_write_i2c_block_data(hub->i2c, offset,
+ USB251XB_I2C_WRITE_SZ + 1,
+ wbuf);
+ if (err)
+ goto out_err;
+ }
+
+ dev_info(dev, "Hub configuration was successful.\n");
+ return 0;
+
+out_err:
+ dev_err(dev, "configuring block %d failed: %d\n", i, err);
+ return err;
+}
+
+#ifdef CONFIG_OF
+static int usb251xb_get_ofdata(struct usb251xb *hub,
+ struct usb251xb_data *data)
+{
+ struct device *dev = hub->dev;
+ struct device_node *np = dev->of_node;
+ int len, err, i;
+ u32 *property_u32 = NULL;
+ const u32 *cproperty_u32;
+ const char *cproperty_char;
+ char str[USB251XB_STRING_BUFSIZE / 2];
+
+ if (!np) {
+ dev_err(dev, "failed to get ofdata\n");
+ return -ENODEV;
+ }
+
+ if (of_get_property(np, "skip-config", NULL))
+ hub->skip_config = 1;
+ else
+ hub->skip_config = 0;
+
+ hub->gpio_reset = of_get_named_gpio(np, "reset-gpios", 0);
+ if (hub->gpio_reset == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ if (gpio_is_valid(hub->gpio_reset)) {
+ err = devm_gpio_request_one(dev, hub->gpio_reset,
+ GPIOF_OUT_INIT_LOW,
+ "usb251xb reset");
+ if (err) {
+ dev_err(dev,
+ "unable to request GPIO %d as reset pin (%d)\n",
+ hub->gpio_reset, err);
+ return err;
+ }
+ }
+
+ if (of_property_read_u16_array(np, "vendor-id", &hub->vendor_id, 1))
+ hub->vendor_id = USB251XB_DEF_VENDOR_ID;
+
+ if (of_property_read_u16_array(np, "product-id",
+ &hub->product_id, 1))
+ hub->product_id = data->product_id;
+
+ if (of_property_read_u16_array(np, "device-id", &hub->device_id, 1))
+ hub->device_id = USB251XB_DEF_DEVICE_ID;
+
+ hub->conf_data1 = USB251XB_DEF_CONFIG_DATA_1;
+ if (of_get_property(np, "self-powered", NULL)) {
+ hub->conf_data1 |= BIT(7);
+
+ /* Configure Over-Current sens when self-powered */
+ hub->conf_data1 &= ~BIT(2);
+ if (of_get_property(np, "ganged-sensing", NULL))
+ hub->conf_data1 &= ~BIT(1);
+ else if (of_get_property(np, "individual-sensing", NULL))
+ hub->conf_data1 |= BIT(1);
+ } else if (of_get_property(np, "bus-powered", NULL)) {
+ hub->conf_data1 &= ~BIT(7);
+
+ /* Disable Over-Current sense when bus-powered */
+ hub->conf_data1 |= BIT(2);
+ }
+
+ if (of_get_property(np, "disable-hi-speed", NULL))
+ hub->conf_data1 |= BIT(5);
+
+ if (of_get_property(np, "multi-tt", NULL))
+ hub->conf_data1 |= BIT(4);
+ else if (of_get_property(np, "single-tt", NULL))
+ hub->conf_data1 &= ~BIT(4);
+
+ if (of_get_property(np, "disable-eop", NULL))
+ hub->conf_data1 |= BIT(3);
+
+ if (of_get_property(np, "individual-port-switching", NULL))
+ hub->conf_data1 |= BIT(0);
+ else if (of_get_property(np, "ganged-port-switching", NULL))
+ hub->conf_data1 &= ~BIT(0);
+
+ hub->conf_data2 = USB251XB_DEF_CONFIG_DATA_2;
+ if (of_get_property(np, "dynamic-power-switching", NULL))
+ hub->conf_data2 |= BIT(7);
+
+ if (!of_property_read_u32(np, "oc-delay-us", property_u32)) {
+ if (*property_u32 == 100) {
+ /* 100 us*/
+ hub->conf_data2 &= ~BIT(5);
+ hub->conf_data2 &= ~BIT(4);
+ } else if (*property_u32 == 4000) {
+ /* 4 ms */
+ hub->conf_data2 &= ~BIT(5);
+ hub->conf_data2 |= BIT(4);
+ } else if (*property_u32 == 16000) {
+ /* 16 ms */
+ hub->conf_data2 |= BIT(5);
+ hub->conf_data2 |= BIT(4);
+ } else {
+ /* 8 ms (DEFAULT) */
+ hub->conf_data2 |= BIT(5);
+ hub->conf_data2 &= ~BIT(4);
+ }
+ }
+
+ if (of_get_property(np, "compound-device", NULL))
+ hub->conf_data2 |= BIT(3);
+
+ hub->conf_data3 = USB251XB_DEF_CONFIG_DATA_3;
+ if (of_get_property(np, "port-mapping-mode", NULL))
+ hub->conf_data3 |= BIT(3);
+
+ if (of_get_property(np, "string-support", NULL))
+ hub->conf_data3 |= BIT(0);
+
+ hub->non_rem_dev = USB251XB_DEF_NON_REMOVABLE_DEVICES;
+ cproperty_u32 = of_get_property(np, "non-removable-ports", &len);
+ if (cproperty_u32 && (len / sizeof(u32)) > 0) {
+ for (i = 0; i < len / sizeof(u32); i++) {
+ u32 port = be32_to_cpu(cproperty_u32[i]);
+
+ if ((port >= 1) && (port <= 4))
+ hub->non_rem_dev |= BIT(port);
+ }
+ }
+
+ hub->port_disable_sp = USB251XB_DEF_PORT_DISABLE_SELF;
+ cproperty_u32 = of_get_property(np, "sp-disabled-ports", &len);
+ if (cproperty_u32 && (len / sizeof(u32)) > 0) {
+ for (i = 0; i < len / sizeof(u32); i++) {
+ u32 port = be32_to_cpu(cproperty_u32[i]);
+
+ if ((port >= 1) && (port <= 4))
+ hub->port_disable_sp |= BIT(port);
+ }
+ }
+
+ hub->port_disable_bp = USB251XB_DEF_PORT_DISABLE_BUS;
+ cproperty_u32 = of_get_property(np, "bp-disabled-ports", &len);
+ if (cproperty_u32 && (len / sizeof(u32)) > 0) {
+ for (i = 0; i < len / sizeof(u32); i++) {
+ u32 port = be32_to_cpu(cproperty_u32[i]);
+
+ if ((port >= 1) && (port <= 4))
+ hub->port_disable_bp |= BIT(port);
+ }
+ }
+
+ hub->power_on_time = USB251XB_DEF_POWER_ON_TIME;
+ if (!of_property_read_u32(np, "power-on-time-ms", property_u32))
+ hub->power_on_time = min_t(u8, *property_u32 / 2, 255);
+
+ if (of_property_read_u16_array(np, "language-id", &hub->lang_id, 1))
+ hub->lang_id = USB251XB_DEF_LANGUAGE_ID;
+
+ cproperty_char = of_get_property(np, "manufacturer", NULL);
+ strlcpy(str, cproperty_char ? : USB251XB_DEF_MANUFACTURER_STRING,
+ sizeof(str));
+ hub->manufacturer_len = strlen(str) & 0xFF;
+ memset(hub->manufacturer, 0, USB251XB_STRING_BUFSIZE);
+ len = min_t(size_t, USB251XB_STRING_BUFSIZE / 2, strlen(str));
+ len = utf8s_to_utf16s(str, len, UTF16_LITTLE_ENDIAN,
+ (wchar_t *)hub->manufacturer,
+ USB251XB_STRING_BUFSIZE);
+
+ cproperty_char = of_get_property(np, "product", NULL);
+ strlcpy(str, cproperty_char ? : data->product_str, sizeof(str));
+ hub->product_len = strlen(str) & 0xFF;
+ memset(hub->product, 0, USB251XB_STRING_BUFSIZE);
+ len = min_t(size_t, USB251XB_STRING_BUFSIZE / 2, strlen(str));
+ len = utf8s_to_utf16s(str, len, UTF16_LITTLE_ENDIAN,
+ (wchar_t *)hub->product,
+ USB251XB_STRING_BUFSIZE);
+
+ cproperty_char = of_get_property(np, "serial", NULL);
+ strlcpy(str, cproperty_char ? : USB251XB_DEF_SERIAL_STRING,
+ sizeof(str));
+ hub->serial_len = strlen(str) & 0xFF;
+ memset(hub->serial, 0, USB251XB_STRING_BUFSIZE);
+ len = min_t(size_t, USB251XB_STRING_BUFSIZE / 2, strlen(str));
+ len = utf8s_to_utf16s(str, len, UTF16_LITTLE_ENDIAN,
+ (wchar_t *)hub->serial,
+ USB251XB_STRING_BUFSIZE);
+
+ /* The following parameters are currently not exposed to devicetree, but
+ * may be as soon as needed.
+ */
+ hub->max_power_sp = USB251XB_DEF_MAX_POWER_SELF;
+ hub->max_power_bp = USB251XB_DEF_MAX_POWER_BUS;
+ hub->max_current_sp = USB251XB_DEF_MAX_CURRENT_SELF;
+ hub->max_current_bp = USB251XB_DEF_MAX_CURRENT_BUS;
+ hub->bat_charge_en = USB251XB_DEF_BATTERY_CHARGING_ENABLE;
+ hub->boost_up = USB251XB_DEF_BOOST_UP;
+ hub->boost_x = USB251XB_DEF_BOOST_X;
+ hub->port_swap = USB251XB_DEF_PORT_SWAP;
+ hub->port_map12 = USB251XB_DEF_PORT_MAP_12;
+ hub->port_map34 = USB251XB_DEF_PORT_MAP_34;
+
+ return 0;
+}
+
+static const struct of_device_id usb251xb_of_match[] = {
+ {
+ .compatible = "microchip,usb2512b",
+ .data = &usb2512b_data,
+ }, {
+ .compatible = "microchip,usb2512bi",
+ .data = &usb2512bi_data,
+ }, {
+ .compatible = "microchip,usb2513b",
+ .data = &usb2513b_data,
+ }, {
+ .compatible = "microchip,usb2513bi",
+ .data = &usb2513bi_data,
+ }, {
+ .compatible = "microchip,usb2514b",
+ .data = &usb2514b_data,
+ }, {
+ .compatible = "microchip,usb2514bi",
+ .data = &usb2514bi_data,
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, usb251xb_of_match);
+#else /* CONFIG_OF */
+static int usb251xb_get_ofdata(struct usb251xb *hub,
+ struct usb251xb_data *data)
+{
+ return 0;
+}
+#endif /* CONFIG_OF */
+
+static int usb251xb_probe(struct usb251xb *hub)
+{
+ struct device *dev = hub->dev;
+ struct device_node *np = dev->of_node;
+ const struct of_device_id *of_id = of_match_device(usb251xb_of_match,
+ dev);
+ int err;
+
+ if (np) {
+ err = usb251xb_get_ofdata(hub,
+ (struct usb251xb_data *)of_id->data);
+ if (err) {
+ dev_err(dev, "failed to get ofdata: %d\n", err);
+ return err;
+ }
+ }
+
+ err = usb251xb_connect(hub);
+ if (err) {
+ dev_err(dev, "Failed to connect hub (%d)\n", err);
+ return err;
+ }
+
+ dev_info(dev, "Hub probed successfully\n");
+
+ return 0;
+}
+
+static int usb251xb_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct usb251xb *hub;
+
+ hub = devm_kzalloc(&i2c->dev, sizeof(struct usb251xb), GFP_KERNEL);
+ if (!hub)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, hub);
+ hub->dev = &i2c->dev;
+ hub->i2c = i2c;
+
+ return usb251xb_probe(hub);
+}
+
+static const struct i2c_device_id usb251xb_id[] = {
+ { "usb2512b", 0 },
+ { "usb2512bi", 0 },
+ { "usb2513b", 0 },
+ { "usb2513bi", 0 },
+ { "usb2514b", 0 },
+ { "usb2514bi", 0 },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(i2c, usb251xb_id);
+
+static struct i2c_driver usb251xb_i2c_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .of_match_table = of_match_ptr(usb251xb_of_match),
+ },
+ .probe = usb251xb_i2c_probe,
+ .id_table = usb251xb_id,
+};
+
+module_i2c_driver(usb251xb_i2c_driver);
+
+MODULE_AUTHOR("Richard Leitner <richard.leitner@skidata.com>");
+MODULE_DESCRIPTION("USB251xB/xBi USB 2.0 Hub Controller Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c
index 5c8210dc6fd9..17c081068257 100644
--- a/drivers/usb/misc/usbtest.c
+++ b/drivers/usb/misc/usbtest.c
@@ -992,7 +992,7 @@ static int ch9_postconfig(struct usbtest_dev *dev)
dev_err(&iface->dev,
"hs dev qualifier --> %d\n",
retval);
- return (retval < 0) ? retval : -EDOM;
+ return retval;
}
/* usb2.0 but not high-speed capable; fine */
} else if (retval != sizeof(struct usb_qualifier_descriptor)) {
@@ -1915,7 +1915,7 @@ static struct urb *iso_alloc_urb(
if (bytes < 0 || !desc)
return NULL;
maxp = 0x7ff & usb_endpoint_maxp(desc);
- maxp *= 1 + (0x3 & (usb_endpoint_maxp(desc) >> 11));
+ maxp *= usb_endpoint_maxp_mult(desc);
packets = DIV_ROUND_UP(bytes, maxp);
urb = usb_alloc_urb(packets, GFP_KERNEL);
@@ -2001,8 +2001,8 @@ test_queue(struct usbtest_dev *dev, struct usbtest_param_32 *param,
"iso period %d %sframes, wMaxPacket %d, transactions: %d\n",
1 << (desc->bInterval - 1),
(udev->speed == USB_SPEED_HIGH) ? "micro" : "",
- usb_endpoint_maxp(desc) & 0x7ff,
- 1 + (0x3 & (usb_endpoint_maxp(desc) >> 11)));
+ usb_endpoint_maxp(desc),
+ usb_endpoint_maxp_mult(desc));
dev_info(&dev->intf->dev,
"total %lu msec (%lu packets)\n",
diff --git a/drivers/usb/misc/uss720.c b/drivers/usb/misc/uss720.c
index 356d312add57..07014cad6dbe 100644
--- a/drivers/usb/misc/uss720.c
+++ b/drivers/usb/misc/uss720.c
@@ -50,6 +50,7 @@
#include <linux/completion.h>
#include <linux/kref.h>
#include <linux/slab.h>
+#include <linux/sched/signal.h>
/*
* Version Information
@@ -526,7 +527,7 @@ static size_t parport_uss720_epp_write_data(struct parport *pp, const void *buf,
return 0;
i = usb_bulk_msg(usbdev, usb_sndbulkpipe(usbdev, 1), (void *)buf, length, &rlen, 20000);
if (i)
- printk(KERN_ERR "uss720: sendbulk ep 1 buf %p len %Zu rlen %u\n", buf, length, rlen);
+ printk(KERN_ERR "uss720: sendbulk ep 1 buf %p len %zu rlen %u\n", buf, length, rlen);
change_mode(pp, ECR_PS2);
return rlen;
#endif
@@ -587,7 +588,7 @@ static size_t parport_uss720_ecp_write_data(struct parport *pp, const void *buff
return 0;
i = usb_bulk_msg(usbdev, usb_sndbulkpipe(usbdev, 1), (void *)buffer, len, &rlen, 20000);
if (i)
- printk(KERN_ERR "uss720: sendbulk ep 1 buf %p len %Zu rlen %u\n", buffer, len, rlen);
+ printk(KERN_ERR "uss720: sendbulk ep 1 buf %p len %zu rlen %u\n", buffer, len, rlen);
change_mode(pp, ECR_PS2);
return rlen;
}
@@ -605,7 +606,7 @@ static size_t parport_uss720_ecp_read_data(struct parport *pp, void *buffer, siz
return 0;
i = usb_bulk_msg(usbdev, usb_rcvbulkpipe(usbdev, 2), buffer, len, &rlen, 20000);
if (i)
- printk(KERN_ERR "uss720: recvbulk ep 2 buf %p len %Zu rlen %u\n", buffer, len, rlen);
+ printk(KERN_ERR "uss720: recvbulk ep 2 buf %p len %zu rlen %u\n", buffer, len, rlen);
change_mode(pp, ECR_PS2);
return rlen;
}
@@ -638,7 +639,7 @@ static size_t parport_uss720_write_compat(struct parport *pp, const void *buffer
return 0;
i = usb_bulk_msg(usbdev, usb_sndbulkpipe(usbdev, 1), (void *)buffer, len, &rlen, 20000);
if (i)
- printk(KERN_ERR "uss720: sendbulk ep 1 buf %p len %Zu rlen %u\n", buffer, len, rlen);
+ printk(KERN_ERR "uss720: sendbulk ep 1 buf %p len %zu rlen %u\n", buffer, len, rlen);
change_mode(pp, ECR_PS2);
return rlen;
}
@@ -708,6 +709,11 @@ static int uss720_probe(struct usb_interface *intf,
interface = intf->cur_altsetting;
+ if (interface->desc.bNumEndpoints < 3) {
+ usb_put_dev(usbdev);
+ return -ENODEV;
+ }
+
/*
* Allocate parport interface
*/
diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c
index 1a874a1f3890..b6d8bf475c92 100644
--- a/drivers/usb/mon/mon_bin.c
+++ b/drivers/usb/mon/mon_bin.c
@@ -8,6 +8,7 @@
*/
#include <linux/kernel.h>
+#include <linux/sched/signal.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/cdev.h>
@@ -20,7 +21,7 @@
#include <linux/slab.h>
#include <linux/time64.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include "usb_mon.h"
@@ -1223,9 +1224,9 @@ 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_area_struct *vma, struct vm_fault *vmf)
+static int mon_bin_vma_fault(struct vm_fault *vmf)
{
- struct mon_reader_bin *rp = vma->vm_private_data;
+ struct mon_reader_bin *rp = vmf->vma->vm_private_data;
unsigned long offset, chunk_idx;
struct page *pageptr;
diff --git a/drivers/usb/mon/mon_main.c b/drivers/usb/mon/mon_main.c
index 33ff49c4cea4..46847340b819 100644
--- a/drivers/usb/mon/mon_main.c
+++ b/drivers/usb/mon/mon_main.c
@@ -409,7 +409,7 @@ static void __exit mon_exit(void)
printk(KERN_ERR TAG
": Outstanding opens (%d) on usb%d, leaking...\n",
mbus->nreaders, mbus->u_bus->busnum);
- atomic_set(&mbus->ref.refcount, 2); /* Force leak */
+ kref_get(&mbus->ref); /* Force leak */
}
mon_dissolve(mbus, mbus->u_bus);
diff --git a/drivers/usb/mon/mon_stat.c b/drivers/usb/mon/mon_stat.c
index 5388a339cfb8..5bdf73a57498 100644
--- a/drivers/usb/mon/mon_stat.c
+++ b/drivers/usb/mon/mon_stat.c
@@ -12,7 +12,7 @@
#include <linux/export.h>
#include <linux/usb.h>
#include <linux/fs.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include "usb_mon.h"
diff --git a/drivers/usb/mon/mon_text.c b/drivers/usb/mon/mon_text.c
index e59334b09c41..19c416d69eb9 100644
--- a/drivers/usb/mon/mon_text.c
+++ b/drivers/usb/mon/mon_text.c
@@ -8,13 +8,14 @@
#include <linux/list.h>
#include <linux/usb.h>
#include <linux/slab.h>
+#include <linux/sched/signal.h>
#include <linux/time.h>
#include <linux/ktime.h>
#include <linux/export.h>
#include <linux/mutex.h>
#include <linux/debugfs.h>
#include <linux/scatterlist.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include "usb_mon.h"
diff --git a/drivers/usb/mtu3/Kconfig b/drivers/usb/mtu3/Kconfig
new file mode 100644
index 000000000000..25cd61947bee
--- /dev/null
+++ b/drivers/usb/mtu3/Kconfig
@@ -0,0 +1,54 @@
+# For MTK USB3.0 IP
+
+config USB_MTU3
+ tristate "MediaTek USB3 Dual Role controller"
+ depends on EXTCON && (USB || USB_GADGET) && HAS_DMA
+ depends on ARCH_MEDIATEK || COMPILE_TEST
+ select USB_XHCI_MTK if USB_SUPPORT && USB_XHCI_HCD
+ help
+ Say Y or M here if your system runs on MediaTek SoCs with
+ Dual Role SuperSpeed USB controller. You can select usb
+ mode as peripheral role or host role, or both.
+
+ If you don't know what this is, please say N.
+
+ Choose M here to compile this driver as a module, and it
+ will be called mtu3.ko.
+
+
+if USB_MTU3
+choice
+ bool "MTU3 Mode Selection"
+ default USB_MTU3_DUAL_ROLE if (USB && USB_GADGET)
+ default USB_MTU3_HOST if (USB && !USB_GADGET)
+ default USB_MTU3_GADGET if (!USB && USB_GADGET)
+
+config USB_MTU3_HOST
+ bool "Host only mode"
+ depends on USB=y || USB=USB_MTU3
+ help
+ Select this when you want to use MTU3 in host mode only,
+ thereby the gadget feature will be regressed.
+
+config USB_MTU3_GADGET
+ bool "Gadget only mode"
+ depends on USB_GADGET=y || USB_GADGET=USB_MTU3
+ help
+ Select this when you want to use MTU3 in gadget mode only,
+ thereby the host feature will be regressed.
+
+config USB_MTU3_DUAL_ROLE
+ bool "Dual Role mode"
+ depends on ((USB=y || USB=USB_MTU3) && (USB_GADGET=y || USB_GADGET=USB_MTU3))
+ help
+ This is the default mode of working of MTU3 controller where
+ both host and gadget features are enabled.
+
+endchoice
+
+config USB_MTU3_DEBUG
+ bool "Enable Debugging Messages"
+ help
+ Say Y here to enable debugging messages in the MTU3 Driver.
+
+endif
diff --git a/drivers/usb/mtu3/Makefile b/drivers/usb/mtu3/Makefile
new file mode 100644
index 000000000000..60e0fff7a847
--- /dev/null
+++ b/drivers/usb/mtu3/Makefile
@@ -0,0 +1,18 @@
+
+ccflags-$(CONFIG_USB_MTU3_DEBUG) += -DDEBUG
+
+obj-$(CONFIG_USB_MTU3) += mtu3.o
+
+mtu3-y := mtu3_plat.o
+
+ifneq ($(filter y,$(CONFIG_USB_MTU3_HOST) $(CONFIG_USB_MTU3_DUAL_ROLE)),)
+ mtu3-y += mtu3_host.o
+endif
+
+ifneq ($(filter y,$(CONFIG_USB_MTU3_GADGET) $(CONFIG_USB_MTU3_DUAL_ROLE)),)
+ mtu3-y += mtu3_core.o mtu3_gadget_ep0.o mtu3_gadget.o mtu3_qmu.o
+endif
+
+ifneq ($(CONFIG_USB_MTU3_DUAL_ROLE),)
+ mtu3-y += mtu3_dr.o
+endif
diff --git a/drivers/usb/mtu3/mtu3.h b/drivers/usb/mtu3/mtu3.h
new file mode 100644
index 000000000000..aa6fd6a51221
--- /dev/null
+++ b/drivers/usb/mtu3/mtu3.h
@@ -0,0 +1,418 @@
+/*
+ * mtu3.h - MediaTek USB3 DRD header
+ *
+ * Copyright (C) 2016 MediaTek Inc.
+ *
+ * Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __MTU3_H__
+#define __MTU3_H__
+
+#include <linux/device.h>
+#include <linux/dmapool.h>
+#include <linux/extcon.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/phy/phy.h>
+#include <linux/regulator/consumer.h>
+#include <linux/usb.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/otg.h>
+
+struct mtu3;
+struct mtu3_ep;
+struct mtu3_request;
+
+#include "mtu3_hw_regs.h"
+#include "mtu3_qmu.h"
+
+#define MU3D_EP_TXCR0(epnum) (U3D_TX1CSR0 + (((epnum) - 1) * 0x10))
+#define MU3D_EP_TXCR1(epnum) (U3D_TX1CSR1 + (((epnum) - 1) * 0x10))
+#define MU3D_EP_TXCR2(epnum) (U3D_TX1CSR2 + (((epnum) - 1) * 0x10))
+
+#define MU3D_EP_RXCR0(epnum) (U3D_RX1CSR0 + (((epnum) - 1) * 0x10))
+#define MU3D_EP_RXCR1(epnum) (U3D_RX1CSR1 + (((epnum) - 1) * 0x10))
+#define MU3D_EP_RXCR2(epnum) (U3D_RX1CSR2 + (((epnum) - 1) * 0x10))
+
+#define USB_QMU_RQCSR(epnum) (U3D_RXQCSR1 + (((epnum) - 1) * 0x10))
+#define USB_QMU_RQSAR(epnum) (U3D_RXQSAR1 + (((epnum) - 1) * 0x10))
+#define USB_QMU_RQCPR(epnum) (U3D_RXQCPR1 + (((epnum) - 1) * 0x10))
+
+#define USB_QMU_TQCSR(epnum) (U3D_TXQCSR1 + (((epnum) - 1) * 0x10))
+#define USB_QMU_TQSAR(epnum) (U3D_TXQSAR1 + (((epnum) - 1) * 0x10))
+#define USB_QMU_TQCPR(epnum) (U3D_TXQCPR1 + (((epnum) - 1) * 0x10))
+
+#define SSUSB_U3_CTRL(p) (U3D_SSUSB_U3_CTRL_0P + ((p) * 0x08))
+#define SSUSB_U2_CTRL(p) (U3D_SSUSB_U2_CTRL_0P + ((p) * 0x08))
+
+#define MTU3_DRIVER_NAME "mtu3"
+#define DMA_ADDR_INVALID (~(dma_addr_t)0)
+
+#define MTU3_EP_ENABLED BIT(0)
+#define MTU3_EP_STALL BIT(1)
+#define MTU3_EP_WEDGE BIT(2)
+#define MTU3_EP_BUSY BIT(3)
+
+#define MTU3_U3_IP_SLOT_DEFAULT 2
+#define MTU3_U2_IP_SLOT_DEFAULT 1
+
+/**
+ * Normally the device works on HS or SS, to simplify fifo management,
+ * devide fifo into some 512B parts, use bitmap to manage it; And
+ * 128 bits size of bitmap is large enough, that means it can manage
+ * up to 64KB fifo size.
+ * NOTE: MTU3_EP_FIFO_UNIT should be power of two
+ */
+#define MTU3_EP_FIFO_UNIT (1 << 9)
+#define MTU3_FIFO_BIT_SIZE 128
+#define MTU3_U2_IP_EP0_FIFO_SIZE 64
+
+/**
+ * Maximum size of ep0 response buffer for ch9 requests,
+ * the SET_SEL request uses 6 so far, and GET_STATUS is 2
+ */
+#define EP0_RESPONSE_BUF 6
+
+/* device operated link and speed got from DEVICE_CONF register */
+enum mtu3_speed {
+ MTU3_SPEED_INACTIVE = 0,
+ MTU3_SPEED_FULL = 1,
+ MTU3_SPEED_HIGH = 3,
+ MTU3_SPEED_SUPER = 4,
+};
+
+/**
+ * @MU3D_EP0_STATE_SETUP: waits for SETUP or received a SETUP
+ * without data stage.
+ * @MU3D_EP0_STATE_TX: IN data stage
+ * @MU3D_EP0_STATE_RX: OUT data stage
+ * @MU3D_EP0_STATE_TX_END: the last IN data is transferred, and
+ * waits for its completion interrupt
+ * @MU3D_EP0_STATE_STALL: ep0 is in stall status, will be auto-cleared
+ * after receives a SETUP.
+ */
+enum mtu3_g_ep0_state {
+ MU3D_EP0_STATE_SETUP = 1,
+ MU3D_EP0_STATE_TX,
+ MU3D_EP0_STATE_RX,
+ MU3D_EP0_STATE_TX_END,
+ MU3D_EP0_STATE_STALL,
+};
+
+/**
+ * @base: the base address of fifo
+ * @limit: the bitmap size in bits
+ * @bitmap: fifo bitmap in unit of @MTU3_EP_FIFO_UNIT
+ */
+struct mtu3_fifo_info {
+ u32 base;
+ u32 limit;
+ DECLARE_BITMAP(bitmap, MTU3_FIFO_BIT_SIZE);
+};
+
+/**
+ * General Purpose Descriptor (GPD):
+ * The format of TX GPD is a little different from RX one.
+ * And the size of GPD is 16 bytes.
+ *
+ * @flag:
+ * bit0: Hardware Own (HWO)
+ * bit1: Buffer Descriptor Present (BDP), always 0, BD is not supported
+ * bit2: Bypass (BPS), 1: HW skips this GPD if HWO = 1
+ * bit7: Interrupt On Completion (IOC)
+ * @chksum: This is used to validate the contents of this GPD;
+ * If TXQ_CS_EN / RXQ_CS_EN bit is set, an interrupt is issued
+ * when checksum validation fails;
+ * Checksum value is calculated over the 16 bytes of the GPD by default;
+ * @data_buf_len (RX ONLY): This value indicates the length of
+ * the assigned data buffer
+ * @next_gpd: Physical address of the next GPD
+ * @buffer: Physical address of the data buffer
+ * @buf_len:
+ * (TX): This value indicates the length of the assigned data buffer
+ * (RX): The total length of data received
+ * @ext_len: reserved
+ * @ext_flag:
+ * bit5 (TX ONLY): Zero Length Packet (ZLP),
+ */
+struct qmu_gpd {
+ __u8 flag;
+ __u8 chksum;
+ __le16 data_buf_len;
+ __le32 next_gpd;
+ __le32 buffer;
+ __le16 buf_len;
+ __u8 ext_len;
+ __u8 ext_flag;
+} __packed;
+
+/**
+* dma: physical base address of GPD segment
+* start: virtual base address of GPD segment
+* end: the last GPD element
+* enqueue: the first empty GPD to use
+* dequeue: the first completed GPD serviced by ISR
+* NOTE: the size of GPD ring should be >= 2
+*/
+struct mtu3_gpd_ring {
+ dma_addr_t dma;
+ struct qmu_gpd *start;
+ struct qmu_gpd *end;
+ struct qmu_gpd *enqueue;
+ struct qmu_gpd *dequeue;
+};
+
+/**
+* @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.
+* @is_u3_drd: whether port0 supports usb3.0 dual-role device or not
+* @id_*: used to maually switch between host and device modes by idpin
+* @manual_drd_enabled: it's true when supports dual-role device by debugfs
+* to switch host/device modes depending on user input.
+*/
+struct otg_switch_mtk {
+ struct regulator *vbus;
+ struct extcon_dev *edev;
+ struct notifier_block vbus_nb;
+ struct notifier_block id_nb;
+ struct delayed_work extcon_reg_dwork;
+ bool is_u3_drd;
+ /* dual-role switch by debugfs */
+ struct pinctrl *id_pinctrl;
+ struct pinctrl_state *id_float;
+ struct pinctrl_state *id_ground;
+ bool manual_drd_enabled;
+};
+
+/**
+ * @mac_base: register base address of device MAC, exclude xHCI's
+ * @ippc_base: register base address of IP Power and Clock interface (IPPC)
+ * @vusb33: usb3.3V shared by device/host IP
+ * @sys_clk: system clock of mtu3, shared by device/host IP
+ * @dr_mode: works in which mode:
+ * host only, device only or dual-role mode
+ * @u2_ports: number of usb2.0 host ports
+ * @u3_ports: number of usb3.0 host ports
+ * @dbgfs_root: only used when supports manual dual-role switch via debugfs
+ * @wakeup_en: it's true when supports remote wakeup in host mode
+ * @wk_deb_p0: port0's wakeup debounce clock
+ * @wk_deb_p1: it's optional, and depends on port1 is supported or not
+ */
+struct ssusb_mtk {
+ struct device *dev;
+ struct mtu3 *u3d;
+ void __iomem *mac_base;
+ void __iomem *ippc_base;
+ struct phy **phys;
+ int num_phys;
+ /* common power & clock */
+ struct regulator *vusb33;
+ struct clk *sys_clk;
+ struct clk *ref_clk;
+ /* otg */
+ struct otg_switch_mtk otg_switch;
+ enum usb_dr_mode dr_mode;
+ bool is_host;
+ int u2_ports;
+ int u3_ports;
+ struct dentry *dbgfs_root;
+ /* usb wakeup for host mode */
+ bool wakeup_en;
+ struct clk *wk_deb_p0;
+ struct clk *wk_deb_p1;
+ struct regmap *pericfg;
+};
+
+/**
+ * @fifo_size: it is (@slot + 1) * @fifo_seg_size
+ * @fifo_seg_size: it is roundup_pow_of_two(@maxp)
+ */
+struct mtu3_ep {
+ struct usb_ep ep;
+ char name[12];
+ struct mtu3 *mtu;
+ u8 epnum;
+ u8 type;
+ u8 is_in;
+ u16 maxp;
+ int slot;
+ u32 fifo_size;
+ u32 fifo_addr;
+ u32 fifo_seg_size;
+ struct mtu3_fifo_info *fifo;
+
+ struct list_head req_list;
+ struct mtu3_gpd_ring gpd_ring;
+ const struct usb_ss_ep_comp_descriptor *comp_desc;
+ const struct usb_endpoint_descriptor *desc;
+
+ int flags;
+ u8 wedged;
+ u8 busy;
+};
+
+struct mtu3_request {
+ struct usb_request request;
+ struct list_head list;
+ struct mtu3_ep *mep;
+ struct mtu3 *mtu;
+ struct qmu_gpd *gpd;
+ int epnum;
+};
+
+static inline struct ssusb_mtk *dev_to_ssusb(struct device *dev)
+{
+ return dev_get_drvdata(dev);
+}
+
+/**
+ * struct mtu3 - device driver instance data.
+ * @slot: MTU3_U2_IP_SLOT_DEFAULT for U2 IP only,
+ * MTU3_U3_IP_SLOT_DEFAULT for U3 IP
+ * @may_wakeup: means device's remote wakeup is enabled
+ * @is_self_powered: is reported in device status and the config descriptor
+ * @ep0_req: dummy request used while handling standard USB requests
+ * for GET_STATUS and SET_SEL
+ * @setup_buf: ep0 response buffer for GET_STATUS and SET_SEL requests
+ */
+struct mtu3 {
+ spinlock_t lock;
+ struct ssusb_mtk *ssusb;
+ struct device *dev;
+ void __iomem *mac_base;
+ void __iomem *ippc_base;
+ int irq;
+
+ struct mtu3_fifo_info tx_fifo;
+ struct mtu3_fifo_info rx_fifo;
+
+ struct mtu3_ep *ep_array;
+ struct mtu3_ep *in_eps;
+ struct mtu3_ep *out_eps;
+ struct mtu3_ep *ep0;
+ int num_eps;
+ int slot;
+ int active_ep;
+
+ struct dma_pool *qmu_gpd_pool;
+ enum mtu3_g_ep0_state ep0_state;
+ struct usb_gadget g; /* the gadget */
+ struct usb_gadget_driver *gadget_driver;
+ struct mtu3_request ep0_req;
+ u8 setup_buf[EP0_RESPONSE_BUF];
+ u32 max_speed;
+
+ unsigned is_active:1;
+ unsigned may_wakeup:1;
+ unsigned is_self_powered:1;
+ unsigned test_mode:1;
+ unsigned softconnect:1;
+ unsigned u1_enable:1;
+ unsigned u2_enable:1;
+ unsigned is_u3_ip:1;
+
+ u8 address;
+ u8 test_mode_nr;
+ u32 hw_version;
+};
+
+static inline struct mtu3 *gadget_to_mtu3(struct usb_gadget *g)
+{
+ return container_of(g, struct mtu3, g);
+}
+
+static inline int is_first_entry(const struct list_head *list,
+ const struct list_head *head)
+{
+ return list_is_last(head, list);
+}
+
+static inline struct mtu3_request *to_mtu3_request(struct usb_request *req)
+{
+ return req ? container_of(req, struct mtu3_request, request) : NULL;
+}
+
+static inline struct mtu3_ep *to_mtu3_ep(struct usb_ep *ep)
+{
+ return ep ? container_of(ep, struct mtu3_ep, ep) : NULL;
+}
+
+static inline struct mtu3_request *next_request(struct mtu3_ep *mep)
+{
+ struct list_head *queue = &mep->req_list;
+
+ if (list_empty(queue))
+ return NULL;
+
+ return list_first_entry(queue, struct mtu3_request, list);
+}
+
+static inline void mtu3_writel(void __iomem *base, u32 offset, u32 data)
+{
+ writel(data, base + offset);
+}
+
+static inline u32 mtu3_readl(void __iomem *base, u32 offset)
+{
+ return readl(base + offset);
+}
+
+static inline void mtu3_setbits(void __iomem *base, u32 offset, u32 bits)
+{
+ void __iomem *addr = base + offset;
+ u32 tmp = readl(addr);
+
+ writel((tmp | (bits)), addr);
+}
+
+static inline void mtu3_clrbits(void __iomem *base, u32 offset, u32 bits)
+{
+ void __iomem *addr = base + offset;
+ u32 tmp = readl(addr);
+
+ writel((tmp & ~(bits)), addr);
+}
+
+int ssusb_check_clocks(struct ssusb_mtk *ssusb, u32 ex_clks);
+struct usb_request *mtu3_alloc_request(struct usb_ep *ep, gfp_t gfp_flags);
+void mtu3_free_request(struct usb_ep *ep, struct usb_request *req);
+void mtu3_req_complete(struct mtu3_ep *mep,
+ struct usb_request *req, int status);
+
+int mtu3_config_ep(struct mtu3 *mtu, struct mtu3_ep *mep,
+ int interval, int burst, int mult);
+void mtu3_deconfig_ep(struct mtu3 *mtu, struct mtu3_ep *mep);
+void mtu3_ep_stall_set(struct mtu3_ep *mep, bool set);
+void mtu3_ep0_setup(struct mtu3 *mtu);
+void mtu3_start(struct mtu3 *mtu);
+void mtu3_stop(struct mtu3 *mtu);
+void mtu3_dev_on_off(struct mtu3 *mtu, int is_on);
+
+int mtu3_gadget_setup(struct mtu3 *mtu);
+void mtu3_gadget_cleanup(struct mtu3 *mtu);
+void mtu3_gadget_reset(struct mtu3 *mtu);
+void mtu3_gadget_suspend(struct mtu3 *mtu);
+void mtu3_gadget_resume(struct mtu3 *mtu);
+void mtu3_gadget_disconnect(struct mtu3 *mtu);
+
+irqreturn_t mtu3_ep0_isr(struct mtu3 *mtu);
+extern const struct usb_ep_ops mtu3_ep0_ops;
+
+#endif
diff --git a/drivers/usb/mtu3/mtu3_core.c b/drivers/usb/mtu3/mtu3_core.c
new file mode 100644
index 000000000000..99c65b0788ff
--- /dev/null
+++ b/drivers/usb/mtu3/mtu3_core.c
@@ -0,0 +1,863 @@
+/*
+ * mtu3_core.c - hardware access layer and gadget init/exit of
+ * MediaTek usb3 Dual-Role Controller Driver
+ *
+ * Copyright (C) 2016 MediaTek Inc.
+ *
+ * Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+
+#include "mtu3.h"
+
+static int ep_fifo_alloc(struct mtu3_ep *mep, u32 seg_size)
+{
+ struct mtu3_fifo_info *fifo = mep->fifo;
+ u32 num_bits = DIV_ROUND_UP(seg_size, MTU3_EP_FIFO_UNIT);
+ u32 start_bit;
+
+ /* ensure that @mep->fifo_seg_size is power of two */
+ num_bits = roundup_pow_of_two(num_bits);
+ if (num_bits > fifo->limit)
+ return -EINVAL;
+
+ mep->fifo_seg_size = num_bits * MTU3_EP_FIFO_UNIT;
+ num_bits = num_bits * (mep->slot + 1);
+ start_bit = bitmap_find_next_zero_area(fifo->bitmap,
+ fifo->limit, 0, num_bits, 0);
+ if (start_bit >= fifo->limit)
+ return -EOVERFLOW;
+
+ bitmap_set(fifo->bitmap, start_bit, num_bits);
+ mep->fifo_size = num_bits * MTU3_EP_FIFO_UNIT;
+ mep->fifo_addr = fifo->base + MTU3_EP_FIFO_UNIT * start_bit;
+
+ dev_dbg(mep->mtu->dev, "%s fifo:%#x/%#x, start_bit: %d\n",
+ __func__, mep->fifo_seg_size, mep->fifo_size, start_bit);
+
+ return mep->fifo_addr;
+}
+
+static void ep_fifo_free(struct mtu3_ep *mep)
+{
+ struct mtu3_fifo_info *fifo = mep->fifo;
+ u32 addr = mep->fifo_addr;
+ u32 bits = mep->fifo_size / MTU3_EP_FIFO_UNIT;
+ u32 start_bit;
+
+ if (unlikely(addr < fifo->base || bits > fifo->limit))
+ return;
+
+ start_bit = (addr - fifo->base) / MTU3_EP_FIFO_UNIT;
+ bitmap_clear(fifo->bitmap, start_bit, bits);
+ mep->fifo_size = 0;
+ mep->fifo_seg_size = 0;
+
+ dev_dbg(mep->mtu->dev, "%s size:%#x/%#x, start_bit: %d\n",
+ __func__, mep->fifo_seg_size, mep->fifo_size, start_bit);
+}
+
+/* enable/disable U3D SS function */
+static inline void mtu3_ss_func_set(struct mtu3 *mtu, bool enable)
+{
+ /* If usb3_en==0, LTSSM will go to SS.Disable state */
+ if (enable)
+ mtu3_setbits(mtu->mac_base, U3D_USB3_CONFIG, USB3_EN);
+ else
+ mtu3_clrbits(mtu->mac_base, U3D_USB3_CONFIG, USB3_EN);
+
+ dev_dbg(mtu->dev, "USB3_EN = %d\n", !!enable);
+}
+
+/* set/clear U3D HS device soft connect */
+static inline void mtu3_hs_softconn_set(struct mtu3 *mtu, bool enable)
+{
+ if (enable) {
+ mtu3_setbits(mtu->mac_base, U3D_POWER_MANAGEMENT,
+ SOFT_CONN | SUSPENDM_ENABLE);
+ } else {
+ mtu3_clrbits(mtu->mac_base, U3D_POWER_MANAGEMENT,
+ SOFT_CONN | SUSPENDM_ENABLE);
+ }
+ dev_dbg(mtu->dev, "SOFTCONN = %d\n", !!enable);
+}
+
+/* only port0 of U2/U3 supports device mode */
+static int mtu3_device_enable(struct mtu3 *mtu)
+{
+ void __iomem *ibase = mtu->ippc_base;
+ u32 check_clk = 0;
+
+ mtu3_clrbits(ibase, U3D_SSUSB_IP_PW_CTRL2, SSUSB_IP_DEV_PDN);
+
+ if (mtu->is_u3_ip) {
+ check_clk = SSUSB_U3_MAC_RST_B_STS;
+ mtu3_clrbits(ibase, SSUSB_U3_CTRL(0),
+ (SSUSB_U3_PORT_DIS | SSUSB_U3_PORT_PDN |
+ SSUSB_U3_PORT_HOST_SEL));
+ }
+ mtu3_clrbits(ibase, SSUSB_U2_CTRL(0),
+ (SSUSB_U2_PORT_DIS | SSUSB_U2_PORT_PDN |
+ SSUSB_U2_PORT_HOST_SEL));
+ mtu3_setbits(ibase, SSUSB_U2_CTRL(0), SSUSB_U2_PORT_OTG_SEL);
+
+ return ssusb_check_clocks(mtu->ssusb, check_clk);
+}
+
+static void mtu3_device_disable(struct mtu3 *mtu)
+{
+ void __iomem *ibase = mtu->ippc_base;
+
+ if (mtu->is_u3_ip)
+ mtu3_setbits(ibase, SSUSB_U3_CTRL(0),
+ (SSUSB_U3_PORT_DIS | SSUSB_U3_PORT_PDN));
+
+ mtu3_setbits(ibase, SSUSB_U2_CTRL(0),
+ SSUSB_U2_PORT_DIS | SSUSB_U2_PORT_PDN);
+ mtu3_clrbits(ibase, SSUSB_U2_CTRL(0), SSUSB_U2_PORT_OTG_SEL);
+ mtu3_setbits(ibase, U3D_SSUSB_IP_PW_CTRL2, SSUSB_IP_DEV_PDN);
+}
+
+/* reset U3D's device module. */
+static void mtu3_device_reset(struct mtu3 *mtu)
+{
+ void __iomem *ibase = mtu->ippc_base;
+
+ mtu3_setbits(ibase, U3D_SSUSB_DEV_RST_CTRL, SSUSB_DEV_SW_RST);
+ udelay(1);
+ mtu3_clrbits(ibase, U3D_SSUSB_DEV_RST_CTRL, SSUSB_DEV_SW_RST);
+}
+
+/* disable all interrupts */
+static void mtu3_intr_disable(struct mtu3 *mtu)
+{
+ void __iomem *mbase = mtu->mac_base;
+
+ /* Disable level 1 interrupts */
+ mtu3_writel(mbase, U3D_LV1IECR, ~0x0);
+ /* Disable endpoint interrupts */
+ mtu3_writel(mbase, U3D_EPIECR, ~0x0);
+}
+
+static void mtu3_intr_status_clear(struct mtu3 *mtu)
+{
+ void __iomem *mbase = mtu->mac_base;
+
+ /* Clear EP0 and Tx/Rx EPn interrupts status */
+ mtu3_writel(mbase, U3D_EPISR, ~0x0);
+ /* Clear U2 USB common interrupts status */
+ mtu3_writel(mbase, U3D_COMMON_USB_INTR, ~0x0);
+ /* Clear U3 LTSSM interrupts status */
+ mtu3_writel(mbase, U3D_LTSSM_INTR, ~0x0);
+ /* Clear speed change interrupt status */
+ mtu3_writel(mbase, U3D_DEV_LINK_INTR, ~0x0);
+}
+
+/* enable system global interrupt */
+static void mtu3_intr_enable(struct mtu3 *mtu)
+{
+ void __iomem *mbase = mtu->mac_base;
+ u32 value;
+
+ /*Enable level 1 interrupts (BMU, QMU, MAC3, DMA, MAC2, EPCTL) */
+ value = BMU_INTR | QMU_INTR | MAC3_INTR | MAC2_INTR | EP_CTRL_INTR;
+ mtu3_writel(mbase, U3D_LV1IESR, value);
+
+ /* Enable U2 common USB interrupts */
+ value = SUSPEND_INTR | RESUME_INTR | RESET_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;
+ mtu3_writel(mbase, U3D_LTSSM_INTR_ENABLE, value);
+ }
+
+ /* Enable QMU interrupts. */
+ value = TXQ_CSERR_INT | TXQ_LENERR_INT | RXQ_CSERR_INT |
+ RXQ_LENERR_INT | RXQ_ZLPERR_INT;
+ mtu3_writel(mbase, U3D_QIESR1, value);
+
+ /* Enable speed change interrupt */
+ mtu3_writel(mbase, U3D_DEV_LINK_INTR_ENABLE, SSUSB_DEV_SPEED_CHG_INTR);
+}
+
+/* set/clear the stall and toggle bits for non-ep0 */
+void mtu3_ep_stall_set(struct mtu3_ep *mep, bool set)
+{
+ struct mtu3 *mtu = mep->mtu;
+ void __iomem *mbase = mtu->mac_base;
+ u8 epnum = mep->epnum;
+ u32 csr;
+
+ if (mep->is_in) { /* TX */
+ csr = mtu3_readl(mbase, MU3D_EP_TXCR0(epnum)) & TX_W1C_BITS;
+ if (set)
+ csr |= TX_SENDSTALL;
+ else
+ csr = (csr & (~TX_SENDSTALL)) | TX_SENTSTALL;
+ mtu3_writel(mbase, MU3D_EP_TXCR0(epnum), csr);
+ } else { /* RX */
+ csr = mtu3_readl(mbase, MU3D_EP_RXCR0(epnum)) & RX_W1C_BITS;
+ if (set)
+ csr |= RX_SENDSTALL;
+ else
+ csr = (csr & (~RX_SENDSTALL)) | RX_SENTSTALL;
+ mtu3_writel(mbase, MU3D_EP_RXCR0(epnum), csr);
+ }
+
+ 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));
+ mep->flags &= ~MTU3_EP_STALL;
+ } else {
+ mep->flags |= MTU3_EP_STALL;
+ }
+
+ dev_dbg(mtu->dev, "%s: %s\n", mep->name,
+ set ? "SEND STALL" : "CLEAR STALL, with EP RESET");
+}
+
+void mtu3_dev_on_off(struct mtu3 *mtu, int is_on)
+{
+ if (mtu->is_u3_ip && (mtu->max_speed == USB_SPEED_SUPER))
+ mtu3_ss_func_set(mtu, is_on);
+ else
+ mtu3_hs_softconn_set(mtu, is_on);
+
+ dev_info(mtu->dev, "gadget (%s) pullup D%s\n",
+ usb_speed_string(mtu->max_speed), is_on ? "+" : "-");
+}
+
+void mtu3_start(struct mtu3 *mtu)
+{
+ void __iomem *mbase = mtu->mac_base;
+
+ dev_dbg(mtu->dev, "%s devctl 0x%x\n", __func__,
+ mtu3_readl(mbase, U3D_DEVICE_CONTROL));
+
+ mtu3_clrbits(mtu->ippc_base, U3D_SSUSB_IP_PW_CTRL2, SSUSB_IP_DEV_PDN);
+
+ /*
+ * When disable U2 port, USB2_CSR's register will be reset to
+ * default value after re-enable it again(HS is enabled by default).
+ * So if force mac to work as FS, disable HS function.
+ */
+ if (mtu->max_speed == USB_SPEED_FULL)
+ mtu3_clrbits(mbase, U3D_POWER_MANAGEMENT, HS_ENABLE);
+
+ /* Initialize the default interrupts */
+ mtu3_intr_enable(mtu);
+ mtu->is_active = 1;
+
+ if (mtu->softconnect)
+ mtu3_dev_on_off(mtu, 1);
+}
+
+void mtu3_stop(struct mtu3 *mtu)
+{
+ dev_dbg(mtu->dev, "%s\n", __func__);
+
+ mtu3_intr_disable(mtu);
+ mtu3_intr_status_clear(mtu);
+
+ if (mtu->softconnect)
+ mtu3_dev_on_off(mtu, 0);
+
+ mtu->is_active = 0;
+ mtu3_setbits(mtu->ippc_base, U3D_SSUSB_IP_PW_CTRL2, SSUSB_IP_DEV_PDN);
+}
+
+/* for non-ep0 */
+int mtu3_config_ep(struct mtu3 *mtu, struct mtu3_ep *mep,
+ int interval, int burst, int mult)
+{
+ void __iomem *mbase = mtu->mac_base;
+ int epnum = mep->epnum;
+ u32 csr0, csr1, csr2;
+ int fifo_sgsz, fifo_addr;
+ int num_pkts;
+
+ fifo_addr = ep_fifo_alloc(mep, mep->maxp);
+ if (fifo_addr < 0) {
+ dev_err(mtu->dev, "alloc ep fifo failed(%d)\n", mep->maxp);
+ return -ENOMEM;
+ }
+ fifo_sgsz = ilog2(mep->fifo_seg_size);
+ dev_dbg(mtu->dev, "%s fifosz: %x(%x/%x)\n", __func__, fifo_sgsz,
+ mep->fifo_seg_size, mep->fifo_size);
+
+ if (mep->is_in) {
+ csr0 = TX_TXMAXPKTSZ(mep->maxp);
+ csr0 |= TX_DMAREQEN;
+
+ num_pkts = (burst + 1) * (mult + 1) - 1;
+ csr1 = TX_SS_BURST(burst) | TX_SLOT(mep->slot);
+ csr1 |= TX_MAX_PKT(num_pkts) | TX_MULT(mult);
+
+ csr2 = TX_FIFOADDR(fifo_addr >> 4);
+ csr2 |= TX_FIFOSEGSIZE(fifo_sgsz);
+
+ switch (mep->type) {
+ case USB_ENDPOINT_XFER_BULK:
+ csr1 |= TX_TYPE(TYPE_BULK);
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
+ csr1 |= TX_TYPE(TYPE_ISO);
+ csr2 |= TX_BINTERVAL(interval);
+ break;
+ case USB_ENDPOINT_XFER_INT:
+ csr1 |= TX_TYPE(TYPE_INT);
+ csr2 |= TX_BINTERVAL(interval);
+ break;
+ }
+
+ /* Enable QMU Done interrupt */
+ mtu3_setbits(mbase, U3D_QIESR0, QMU_TX_DONE_INT(epnum));
+
+ mtu3_writel(mbase, MU3D_EP_TXCR0(epnum), csr0);
+ mtu3_writel(mbase, MU3D_EP_TXCR1(epnum), csr1);
+ mtu3_writel(mbase, MU3D_EP_TXCR2(epnum), csr2);
+
+ dev_dbg(mtu->dev, "U3D_TX%d CSR0:%#x, CSR1:%#x, CSR2:%#x\n",
+ epnum, mtu3_readl(mbase, MU3D_EP_TXCR0(epnum)),
+ mtu3_readl(mbase, MU3D_EP_TXCR1(epnum)),
+ mtu3_readl(mbase, MU3D_EP_TXCR2(epnum)));
+ } else {
+ csr0 = RX_RXMAXPKTSZ(mep->maxp);
+ csr0 |= RX_DMAREQEN;
+
+ num_pkts = (burst + 1) * (mult + 1) - 1;
+ csr1 = RX_SS_BURST(burst) | RX_SLOT(mep->slot);
+ csr1 |= RX_MAX_PKT(num_pkts) | RX_MULT(mult);
+
+ csr2 = RX_FIFOADDR(fifo_addr >> 4);
+ csr2 |= RX_FIFOSEGSIZE(fifo_sgsz);
+
+ switch (mep->type) {
+ case USB_ENDPOINT_XFER_BULK:
+ csr1 |= RX_TYPE(TYPE_BULK);
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
+ csr1 |= RX_TYPE(TYPE_ISO);
+ csr2 |= RX_BINTERVAL(interval);
+ break;
+ case USB_ENDPOINT_XFER_INT:
+ csr1 |= RX_TYPE(TYPE_INT);
+ csr2 |= RX_BINTERVAL(interval);
+ break;
+ }
+
+ /*Enable QMU Done interrupt */
+ mtu3_setbits(mbase, U3D_QIESR0, QMU_RX_DONE_INT(epnum));
+
+ mtu3_writel(mbase, MU3D_EP_RXCR0(epnum), csr0);
+ mtu3_writel(mbase, MU3D_EP_RXCR1(epnum), csr1);
+ mtu3_writel(mbase, MU3D_EP_RXCR2(epnum), csr2);
+
+ dev_dbg(mtu->dev, "U3D_RX%d CSR0:%#x, CSR1:%#x, CSR2:%#x\n",
+ epnum, mtu3_readl(mbase, MU3D_EP_RXCR0(epnum)),
+ mtu3_readl(mbase, MU3D_EP_RXCR1(epnum)),
+ mtu3_readl(mbase, MU3D_EP_RXCR2(epnum)));
+ }
+
+ dev_dbg(mtu->dev, "csr0:%#x, csr1:%#x, csr2:%#x\n", csr0, csr1, csr2);
+ dev_dbg(mtu->dev, "%s: %s, fifo-addr:%#x, fifo-size:%#x(%#x/%#x)\n",
+ __func__, mep->name, mep->fifo_addr, mep->fifo_size,
+ fifo_sgsz, mep->fifo_seg_size);
+
+ return 0;
+}
+
+/* for non-ep0 */
+void mtu3_deconfig_ep(struct mtu3 *mtu, struct mtu3_ep *mep)
+{
+ void __iomem *mbase = mtu->mac_base;
+ int epnum = mep->epnum;
+
+ if (mep->is_in) {
+ mtu3_writel(mbase, MU3D_EP_TXCR0(epnum), 0);
+ mtu3_writel(mbase, MU3D_EP_TXCR1(epnum), 0);
+ mtu3_writel(mbase, MU3D_EP_TXCR2(epnum), 0);
+ mtu3_setbits(mbase, U3D_QIECR0, QMU_TX_DONE_INT(epnum));
+ } else {
+ mtu3_writel(mbase, MU3D_EP_RXCR0(epnum), 0);
+ mtu3_writel(mbase, MU3D_EP_RXCR1(epnum), 0);
+ mtu3_writel(mbase, MU3D_EP_RXCR2(epnum), 0);
+ mtu3_setbits(mbase, U3D_QIECR0, QMU_RX_DONE_INT(epnum));
+ }
+
+ ep_fifo_free(mep);
+
+ dev_dbg(mtu->dev, "%s: %s\n", __func__, mep->name);
+}
+
+/*
+ * Two scenarios:
+ * 1. when device IP supports SS, the fifo of EP0, TX EPs, RX EPs
+ * are separated;
+ * 2. when supports only HS, the fifo is shared for all EPs, and
+ * the capability registers of @EPNTXFFSZ or @EPNRXFFSZ indicate
+ * the total fifo size of non-ep0, and ep0's is fixed to 64B,
+ * so the total fifo size is 64B + @EPNTXFFSZ;
+ * Due to the first 64B should be reserved for EP0, non-ep0's fifo
+ * starts from offset 64 and are divided into two equal parts for
+ * TX or RX EPs for simplification.
+ */
+static void get_ep_fifo_config(struct mtu3 *mtu)
+{
+ struct mtu3_fifo_info *tx_fifo;
+ struct mtu3_fifo_info *rx_fifo;
+ u32 fifosize;
+
+ if (mtu->is_u3_ip) {
+ fifosize = mtu3_readl(mtu->mac_base, U3D_CAP_EPNTXFFSZ);
+ tx_fifo = &mtu->tx_fifo;
+ tx_fifo->base = 0;
+ tx_fifo->limit = fifosize / MTU3_EP_FIFO_UNIT;
+ bitmap_zero(tx_fifo->bitmap, MTU3_FIFO_BIT_SIZE);
+
+ fifosize = mtu3_readl(mtu->mac_base, U3D_CAP_EPNRXFFSZ);
+ rx_fifo = &mtu->rx_fifo;
+ rx_fifo->base = 0;
+ rx_fifo->limit = fifosize / MTU3_EP_FIFO_UNIT;
+ bitmap_zero(rx_fifo->bitmap, MTU3_FIFO_BIT_SIZE);
+ mtu->slot = MTU3_U3_IP_SLOT_DEFAULT;
+ } else {
+ fifosize = mtu3_readl(mtu->mac_base, U3D_CAP_EPNTXFFSZ);
+ tx_fifo = &mtu->tx_fifo;
+ tx_fifo->base = MTU3_U2_IP_EP0_FIFO_SIZE;
+ tx_fifo->limit = (fifosize / MTU3_EP_FIFO_UNIT) >> 1;
+ bitmap_zero(tx_fifo->bitmap, MTU3_FIFO_BIT_SIZE);
+
+ rx_fifo = &mtu->rx_fifo;
+ rx_fifo->base =
+ tx_fifo->base + tx_fifo->limit * MTU3_EP_FIFO_UNIT;
+ rx_fifo->limit = tx_fifo->limit;
+ bitmap_zero(rx_fifo->bitmap, MTU3_FIFO_BIT_SIZE);
+ mtu->slot = MTU3_U2_IP_SLOT_DEFAULT;
+ }
+
+ dev_dbg(mtu->dev, "%s, TX: base-%d, limit-%d; RX: base-%d, limit-%d\n",
+ __func__, tx_fifo->base, tx_fifo->limit,
+ rx_fifo->base, rx_fifo->limit);
+}
+
+void mtu3_ep0_setup(struct mtu3 *mtu)
+{
+ u32 maxpacket = mtu->g.ep0->maxpacket;
+ u32 csr;
+
+ dev_dbg(mtu->dev, "%s maxpacket: %d\n", __func__, maxpacket);
+
+ csr = mtu3_readl(mtu->mac_base, U3D_EP0CSR);
+ csr &= ~EP0_MAXPKTSZ_MSK;
+ csr |= EP0_MAXPKTSZ(maxpacket);
+ csr &= EP0_W1C_BITS;
+ mtu3_writel(mtu->mac_base, U3D_EP0CSR, csr);
+
+ /* Enable EP0 interrupt */
+ mtu3_writel(mtu->mac_base, U3D_EPIESR, EP0ISR);
+}
+
+static int mtu3_mem_alloc(struct mtu3 *mtu)
+{
+ void __iomem *mbase = mtu->mac_base;
+ struct mtu3_ep *ep_array;
+ int in_ep_num, out_ep_num;
+ u32 cap_epinfo;
+ int ret;
+ int i;
+
+ cap_epinfo = mtu3_readl(mbase, U3D_CAP_EPINFO);
+ in_ep_num = CAP_TX_EP_NUM(cap_epinfo);
+ out_ep_num = CAP_RX_EP_NUM(cap_epinfo);
+
+ dev_info(mtu->dev, "fifosz/epnum: Tx=%#x/%d, Rx=%#x/%d\n",
+ mtu3_readl(mbase, U3D_CAP_EPNTXFFSZ), in_ep_num,
+ mtu3_readl(mbase, U3D_CAP_EPNRXFFSZ), out_ep_num);
+
+ /* one for ep0, another is reserved */
+ mtu->num_eps = min(in_ep_num, out_ep_num) + 1;
+ ep_array = kcalloc(mtu->num_eps * 2, sizeof(*ep_array), GFP_KERNEL);
+ if (ep_array == NULL)
+ return -ENOMEM;
+
+ mtu->ep_array = ep_array;
+ mtu->in_eps = ep_array;
+ mtu->out_eps = &ep_array[mtu->num_eps];
+ /* ep0 uses in_eps[0], out_eps[0] is reserved */
+ mtu->ep0 = mtu->in_eps;
+ mtu->ep0->mtu = mtu;
+ mtu->ep0->epnum = 0;
+
+ for (i = 1; i < mtu->num_eps; i++) {
+ struct mtu3_ep *mep = mtu->in_eps + i;
+
+ mep->fifo = &mtu->tx_fifo;
+ mep = mtu->out_eps + i;
+ mep->fifo = &mtu->rx_fifo;
+ }
+
+ get_ep_fifo_config(mtu);
+
+ ret = mtu3_qmu_init(mtu);
+ if (ret)
+ kfree(mtu->ep_array);
+
+ return ret;
+}
+
+static void mtu3_mem_free(struct mtu3 *mtu)
+{
+ mtu3_qmu_exit(mtu);
+ kfree(mtu->ep_array);
+}
+
+static void mtu3_set_speed(struct mtu3 *mtu)
+{
+ void __iomem *mbase = mtu->mac_base;
+
+ if (!mtu->is_u3_ip && (mtu->max_speed > USB_SPEED_HIGH))
+ mtu->max_speed = USB_SPEED_HIGH;
+
+ if (mtu->max_speed == USB_SPEED_FULL) {
+ /* disable U3 SS function */
+ mtu3_clrbits(mbase, U3D_USB3_CONFIG, USB3_EN);
+ /* disable HS function */
+ mtu3_clrbits(mbase, U3D_POWER_MANAGEMENT, HS_ENABLE);
+ } else if (mtu->max_speed == USB_SPEED_HIGH) {
+ mtu3_clrbits(mbase, U3D_USB3_CONFIG, USB3_EN);
+ /* HS/FS detected by HW */
+ mtu3_setbits(mbase, U3D_POWER_MANAGEMENT, HS_ENABLE);
+ }
+
+ dev_info(mtu->dev, "max_speed: %s\n",
+ usb_speed_string(mtu->max_speed));
+}
+
+static void mtu3_regs_init(struct mtu3 *mtu)
+{
+
+ void __iomem *mbase = mtu->mac_base;
+
+ /* be sure interrupts are disabled before registration of ISR */
+ mtu3_intr_disable(mtu);
+ mtu3_intr_status_clear(mtu);
+
+ if (mtu->is_u3_ip) {
+ /* disable LGO_U1/U2 by default */
+ mtu3_clrbits(mbase, U3D_LINK_POWER_CONTROL,
+ SW_U1_ACCEPT_ENABLE | SW_U2_ACCEPT_ENABLE |
+ SW_U1_REQUEST_ENABLE | SW_U2_REQUEST_ENABLE);
+ /* device responses to u3_exit from host automatically */
+ mtu3_clrbits(mbase, U3D_LTSSM_CTRL, SOFT_U3_EXIT_EN);
+ /* automatically build U2 link when U3 detect fail */
+ mtu3_setbits(mbase, U3D_USB2_TEST_MODE, U2U3_AUTO_SWITCH);
+ }
+
+ mtu3_set_speed(mtu);
+
+ /* delay about 0.1us from detecting reset to send chirp-K */
+ mtu3_clrbits(mbase, U3D_LINK_RESET_INFO, WTCHRP_MSK);
+ /* U2/U3 detected by HW */
+ mtu3_writel(mbase, U3D_DEVICE_CONF, 0);
+ /* enable QMU 16B checksum */
+ mtu3_setbits(mbase, U3D_QCR0, QMU_CS16B_EN);
+ /* vbus detected by HW */
+ mtu3_clrbits(mbase, U3D_MISC_CTRL, VBUS_FRC_EN | VBUS_ON);
+}
+
+static irqreturn_t mtu3_link_isr(struct mtu3 *mtu)
+{
+ void __iomem *mbase = mtu->mac_base;
+ enum usb_device_speed udev_speed;
+ u32 maxpkt = 64;
+ u32 link;
+ u32 speed;
+
+ link = mtu3_readl(mbase, U3D_DEV_LINK_INTR);
+ link &= mtu3_readl(mbase, U3D_DEV_LINK_INTR_ENABLE);
+ mtu3_writel(mbase, U3D_DEV_LINK_INTR, link); /* W1C */
+ dev_dbg(mtu->dev, "=== LINK[%x] ===\n", link);
+
+ if (!(link & SSUSB_DEV_SPEED_CHG_INTR))
+ return IRQ_NONE;
+
+ speed = SSUSB_DEV_SPEED(mtu3_readl(mbase, U3D_DEVICE_CONF));
+
+ switch (speed) {
+ case MTU3_SPEED_FULL:
+ udev_speed = USB_SPEED_FULL;
+ /*BESLCK = 4 < BESLCK_U3 = 10 < BESLDCK = 15 */
+ mtu3_writel(mbase, U3D_USB20_LPM_PARAMETER, LPM_BESLDCK(0xf)
+ | LPM_BESLCK(4) | LPM_BESLCK_U3(0xa));
+ mtu3_setbits(mbase, U3D_POWER_MANAGEMENT,
+ LPM_BESL_STALL | LPM_BESLD_STALL);
+ break;
+ case MTU3_SPEED_HIGH:
+ udev_speed = USB_SPEED_HIGH;
+ /*BESLCK = 4 < BESLCK_U3 = 10 < BESLDCK = 15 */
+ mtu3_writel(mbase, U3D_USB20_LPM_PARAMETER, LPM_BESLDCK(0xf)
+ | LPM_BESLCK(4) | LPM_BESLCK_U3(0xa));
+ mtu3_setbits(mbase, U3D_POWER_MANAGEMENT,
+ LPM_BESL_STALL | LPM_BESLD_STALL);
+ break;
+ case MTU3_SPEED_SUPER:
+ udev_speed = USB_SPEED_SUPER;
+ maxpkt = 512;
+ break;
+ default:
+ udev_speed = USB_SPEED_UNKNOWN;
+ break;
+ }
+ dev_dbg(mtu->dev, "%s: %s\n", __func__, usb_speed_string(udev_speed));
+
+ mtu->g.speed = udev_speed;
+ mtu->g.ep0->maxpacket = maxpkt;
+ mtu->ep0_state = MU3D_EP0_STATE_SETUP;
+
+ if (udev_speed == USB_SPEED_UNKNOWN)
+ mtu3_gadget_disconnect(mtu);
+ else
+ mtu3_ep0_setup(mtu);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t mtu3_u3_ltssm_isr(struct mtu3 *mtu)
+{
+ void __iomem *mbase = mtu->mac_base;
+ u32 ltssm;
+
+ ltssm = mtu3_readl(mbase, U3D_LTSSM_INTR);
+ ltssm &= mtu3_readl(mbase, U3D_LTSSM_INTR_ENABLE);
+ mtu3_writel(mbase, U3D_LTSSM_INTR, ltssm); /* W1C */
+ dev_dbg(mtu->dev, "=== LTSSM[%x] ===\n", ltssm);
+
+ if (ltssm & (HOT_RST_INTR | WARM_RST_INTR))
+ mtu3_gadget_reset(mtu);
+
+ if (ltssm & VBUS_FALL_INTR)
+ mtu3_ss_func_set(mtu, false);
+
+ if (ltssm & VBUS_RISE_INTR)
+ mtu3_ss_func_set(mtu, true);
+
+ if (ltssm & EXIT_U3_INTR)
+ mtu3_gadget_resume(mtu);
+
+ if (ltssm & ENTER_U3_INTR)
+ mtu3_gadget_suspend(mtu);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t mtu3_u2_common_isr(struct mtu3 *mtu)
+{
+ void __iomem *mbase = mtu->mac_base;
+ u32 u2comm;
+
+ u2comm = mtu3_readl(mbase, U3D_COMMON_USB_INTR);
+ u2comm &= mtu3_readl(mbase, U3D_COMMON_USB_INTR_ENABLE);
+ mtu3_writel(mbase, U3D_COMMON_USB_INTR, u2comm); /* W1C */
+ dev_dbg(mtu->dev, "=== U2COMM[%x] ===\n", u2comm);
+
+ if (u2comm & SUSPEND_INTR)
+ mtu3_gadget_suspend(mtu);
+
+ if (u2comm & RESUME_INTR)
+ mtu3_gadget_resume(mtu);
+
+ if (u2comm & RESET_INTR)
+ mtu3_gadget_reset(mtu);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t mtu3_irq(int irq, void *data)
+{
+ struct mtu3 *mtu = (struct mtu3 *)data;
+ unsigned long flags;
+ u32 level1;
+
+ spin_lock_irqsave(&mtu->lock, flags);
+
+ /* U3D_LV1ISR is RU */
+ level1 = mtu3_readl(mtu->mac_base, U3D_LV1ISR);
+ level1 &= mtu3_readl(mtu->mac_base, U3D_LV1IER);
+
+ if (level1 & EP_CTRL_INTR)
+ mtu3_link_isr(mtu);
+
+ if (level1 & MAC2_INTR)
+ mtu3_u2_common_isr(mtu);
+
+ if (level1 & MAC3_INTR)
+ mtu3_u3_ltssm_isr(mtu);
+
+ if (level1 & BMU_INTR)
+ mtu3_ep0_isr(mtu);
+
+ if (level1 & QMU_INTR)
+ mtu3_qmu_isr(mtu);
+
+ spin_unlock_irqrestore(&mtu->lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+static int mtu3_hw_init(struct mtu3 *mtu)
+{
+ u32 cap_dev;
+ int ret;
+
+ mtu->hw_version = mtu3_readl(mtu->ippc_base, U3D_SSUSB_HW_ID);
+
+ cap_dev = mtu3_readl(mtu->ippc_base, U3D_SSUSB_IP_DEV_CAP);
+ mtu->is_u3_ip = !!SSUSB_IP_DEV_U3_PORT_NUM(cap_dev);
+
+ dev_info(mtu->dev, "IP version 0x%x(%s IP)\n", mtu->hw_version,
+ mtu->is_u3_ip ? "U3" : "U2");
+
+ mtu3_device_reset(mtu);
+
+ ret = mtu3_device_enable(mtu);
+ if (ret) {
+ dev_err(mtu->dev, "device enable failed %d\n", ret);
+ return ret;
+ }
+
+ ret = mtu3_mem_alloc(mtu);
+ if (ret)
+ return -ENOMEM;
+
+ mtu3_regs_init(mtu);
+
+ return 0;
+}
+
+static void mtu3_hw_exit(struct mtu3 *mtu)
+{
+ mtu3_device_disable(mtu);
+ mtu3_mem_free(mtu);
+}
+
+/*-------------------------------------------------------------------------*/
+
+int ssusb_gadget_init(struct ssusb_mtk *ssusb)
+{
+ struct device *dev = ssusb->dev;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mtu3 *mtu = NULL;
+ struct resource *res;
+ int ret = -ENOMEM;
+
+ mtu = devm_kzalloc(dev, sizeof(struct mtu3), GFP_KERNEL);
+ if (mtu == NULL)
+ return -ENOMEM;
+
+ mtu->irq = platform_get_irq(pdev, 0);
+ if (mtu->irq <= 0) {
+ dev_err(dev, "fail to get irq number\n");
+ return -ENODEV;
+ }
+ dev_info(dev, "irq %d\n", mtu->irq);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mac");
+ mtu->mac_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(mtu->mac_base)) {
+ dev_err(dev, "error mapping memory for dev mac\n");
+ return PTR_ERR(mtu->mac_base);
+ }
+
+ spin_lock_init(&mtu->lock);
+ mtu->dev = dev;
+ mtu->ippc_base = ssusb->ippc_base;
+ ssusb->mac_base = mtu->mac_base;
+ ssusb->u3d = mtu;
+ mtu->ssusb = ssusb;
+ mtu->max_speed = usb_get_maximum_speed(dev);
+
+ /* check the max_speed parameter */
+ switch (mtu->max_speed) {
+ case USB_SPEED_FULL:
+ case USB_SPEED_HIGH:
+ case USB_SPEED_SUPER:
+ break;
+ default:
+ dev_err(dev, "invalid max_speed: %s\n",
+ usb_speed_string(mtu->max_speed));
+ /* fall through */
+ case USB_SPEED_UNKNOWN:
+ /* default as SS */
+ mtu->max_speed = USB_SPEED_SUPER;
+ break;
+ }
+
+ dev_dbg(dev, "mac_base=0x%p, ippc_base=0x%p\n",
+ mtu->mac_base, mtu->ippc_base);
+
+ ret = mtu3_hw_init(mtu);
+ if (ret) {
+ dev_err(dev, "mtu3 hw init failed:%d\n", ret);
+ return ret;
+ }
+
+ ret = devm_request_irq(dev, mtu->irq, mtu3_irq, 0, dev_name(dev), mtu);
+ if (ret) {
+ dev_err(dev, "request irq %d failed!\n", mtu->irq);
+ goto irq_err;
+ }
+
+ device_init_wakeup(dev, true);
+
+ ret = mtu3_gadget_setup(mtu);
+ if (ret) {
+ dev_err(dev, "mtu3 gadget init failed:%d\n", ret);
+ goto gadget_err;
+ }
+
+ /* init as host mode, power down device IP for power saving */
+ if (mtu->ssusb->dr_mode == USB_DR_MODE_OTG)
+ mtu3_stop(mtu);
+
+ dev_dbg(dev, " %s() done...\n", __func__);
+
+ return 0;
+
+gadget_err:
+ device_init_wakeup(dev, false);
+
+irq_err:
+ mtu3_hw_exit(mtu);
+ ssusb->u3d = NULL;
+ dev_err(dev, " %s() fail...\n", __func__);
+
+ return ret;
+}
+
+void ssusb_gadget_exit(struct ssusb_mtk *ssusb)
+{
+ struct mtu3 *mtu = ssusb->u3d;
+
+ mtu3_gadget_cleanup(mtu);
+ device_init_wakeup(ssusb->dev, false);
+ mtu3_hw_exit(mtu);
+}
diff --git a/drivers/usb/mtu3/mtu3_dr.c b/drivers/usb/mtu3/mtu3_dr.c
new file mode 100644
index 000000000000..1a8987e7c5b0
--- /dev/null
+++ b/drivers/usb/mtu3/mtu3_dr.c
@@ -0,0 +1,379 @@
+/*
+ * mtu3_dr.c - dual role switch and host glue layer
+ *
+ * Copyright (C) 2016 MediaTek Inc.
+ *
+ * Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/debugfs.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/of_device.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+
+#include "mtu3.h"
+#include "mtu3_dr.h"
+
+#define USB2_PORT 2
+#define USB3_PORT 3
+
+enum mtu3_vbus_id_state {
+ MTU3_ID_FLOAT = 1,
+ MTU3_ID_GROUND,
+ MTU3_VBUS_OFF,
+ MTU3_VBUS_VALID,
+};
+
+static void toggle_opstate(struct ssusb_mtk *ssusb)
+{
+ if (!ssusb->otg_switch.is_u3_drd) {
+ mtu3_setbits(ssusb->mac_base, U3D_DEVICE_CONTROL, DC_SESSION);
+ mtu3_setbits(ssusb->mac_base, U3D_POWER_MANAGEMENT, SOFT_CONN);
+ }
+}
+
+/* only port0 supports dual-role mode */
+static int ssusb_port0_switch(struct ssusb_mtk *ssusb,
+ int version, bool tohost)
+{
+ void __iomem *ibase = ssusb->ippc_base;
+ u32 value;
+
+ dev_dbg(ssusb->dev, "%s (switch u%d port0 to %s)\n", __func__,
+ version, tohost ? "host" : "device");
+
+ if (version == USB2_PORT) {
+ /* 1. power off and disable u2 port0 */
+ value = mtu3_readl(ibase, SSUSB_U2_CTRL(0));
+ value |= SSUSB_U2_PORT_PDN | SSUSB_U2_PORT_DIS;
+ mtu3_writel(ibase, SSUSB_U2_CTRL(0), value);
+
+ /* 2. power on, enable u2 port0 and select its mode */
+ value = mtu3_readl(ibase, SSUSB_U2_CTRL(0));
+ value &= ~(SSUSB_U2_PORT_PDN | SSUSB_U2_PORT_DIS);
+ value = tohost ? (value | SSUSB_U2_PORT_HOST_SEL) :
+ (value & (~SSUSB_U2_PORT_HOST_SEL));
+ mtu3_writel(ibase, SSUSB_U2_CTRL(0), value);
+ } else {
+ /* 1. power off and disable u3 port0 */
+ value = mtu3_readl(ibase, SSUSB_U3_CTRL(0));
+ value |= SSUSB_U3_PORT_PDN | SSUSB_U3_PORT_DIS;
+ mtu3_writel(ibase, SSUSB_U3_CTRL(0), value);
+
+ /* 2. power on, enable u3 port0 and select its mode */
+ value = mtu3_readl(ibase, SSUSB_U3_CTRL(0));
+ value &= ~(SSUSB_U3_PORT_PDN | SSUSB_U3_PORT_DIS);
+ value = tohost ? (value | SSUSB_U3_PORT_HOST_SEL) :
+ (value & (~SSUSB_U3_PORT_HOST_SEL));
+ mtu3_writel(ibase, SSUSB_U3_CTRL(0), value);
+ }
+
+ return 0;
+}
+
+static void switch_port_to_host(struct ssusb_mtk *ssusb)
+{
+ u32 check_clk = 0;
+
+ dev_dbg(ssusb->dev, "%s\n", __func__);
+
+ ssusb_port0_switch(ssusb, USB2_PORT, true);
+
+ if (ssusb->otg_switch.is_u3_drd) {
+ ssusb_port0_switch(ssusb, USB3_PORT, true);
+ check_clk = SSUSB_U3_MAC_RST_B_STS;
+ }
+
+ ssusb_check_clocks(ssusb, check_clk);
+
+ /* after all clocks are stable */
+ toggle_opstate(ssusb);
+}
+
+static void switch_port_to_device(struct ssusb_mtk *ssusb)
+{
+ u32 check_clk = 0;
+
+ dev_dbg(ssusb->dev, "%s\n", __func__);
+
+ ssusb_port0_switch(ssusb, USB2_PORT, false);
+
+ if (ssusb->otg_switch.is_u3_drd) {
+ ssusb_port0_switch(ssusb, USB3_PORT, false);
+ check_clk = SSUSB_U3_MAC_RST_B_STS;
+ }
+
+ ssusb_check_clocks(ssusb, check_clk);
+}
+
+int ssusb_set_vbus(struct otg_switch_mtk *otg_sx, int is_on)
+{
+ struct ssusb_mtk *ssusb =
+ container_of(otg_sx, struct ssusb_mtk, otg_switch);
+ struct regulator *vbus = otg_sx->vbus;
+ int ret;
+
+ /* vbus is optional */
+ if (!vbus)
+ return 0;
+
+ dev_dbg(ssusb->dev, "%s: turn %s\n", __func__, is_on ? "on" : "off");
+
+ if (is_on) {
+ ret = regulator_enable(vbus);
+ if (ret) {
+ dev_err(ssusb->dev, "vbus regulator enable failed\n");
+ return ret;
+ }
+ } else {
+ regulator_disable(vbus);
+ }
+
+ return 0;
+}
+
+/*
+ * switch to host: -> MTU3_VBUS_OFF --> MTU3_ID_GROUND
+ * switch to device: -> MTU3_ID_FLOAT --> MTU3_VBUS_VALID
+ */
+static void ssusb_set_mailbox(struct otg_switch_mtk *otg_sx,
+ enum mtu3_vbus_id_state status)
+{
+ struct ssusb_mtk *ssusb =
+ container_of(otg_sx, struct ssusb_mtk, otg_switch);
+ struct mtu3 *mtu = ssusb->u3d;
+
+ dev_dbg(ssusb->dev, "mailbox state(%d)\n", status);
+
+ switch (status) {
+ case MTU3_ID_GROUND:
+ switch_port_to_host(ssusb);
+ ssusb_set_vbus(otg_sx, 1);
+ ssusb->is_host = true;
+ break;
+ case MTU3_ID_FLOAT:
+ ssusb->is_host = false;
+ ssusb_set_vbus(otg_sx, 0);
+ switch_port_to_device(ssusb);
+ break;
+ case MTU3_VBUS_OFF:
+ mtu3_stop(mtu);
+ pm_relax(ssusb->dev);
+ break;
+ case MTU3_VBUS_VALID:
+ /* avoid suspend when works as device */
+ pm_stay_awake(ssusb->dev);
+ mtu3_start(mtu);
+ break;
+ default:
+ dev_err(ssusb->dev, "invalid state\n");
+ }
+}
+
+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);
+
+ if (event)
+ ssusb_set_mailbox(otg_sx, MTU3_ID_GROUND);
+ else
+ ssusb_set_mailbox(otg_sx, MTU3_ID_FLOAT);
+
+ return NOTIFY_DONE;
+}
+
+static int ssusb_vbus_notifier(struct notifier_block *nb,
+ unsigned long event, void *ptr)
+{
+ 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);
+
+ return NOTIFY_DONE;
+}
+
+static int ssusb_extcon_register(struct otg_switch_mtk *otg_sx)
+{
+ struct ssusb_mtk *ssusb =
+ container_of(otg_sx, struct ssusb_mtk, otg_switch);
+ struct extcon_dev *edev = otg_sx->edev;
+ int ret;
+
+ /* extcon is optional */
+ if (!edev)
+ return 0;
+
+ otg_sx->vbus_nb.notifier_call = ssusb_vbus_notifier;
+ ret = extcon_register_notifier(edev, EXTCON_USB,
+ &otg_sx->vbus_nb);
+ if (ret < 0)
+ dev_err(ssusb->dev, "failed to register notifier for USB\n");
+
+ otg_sx->id_nb.notifier_call = ssusb_id_notifier;
+ ret = extcon_register_notifier(edev, EXTCON_USB_HOST,
+ &otg_sx->id_nb);
+ if (ret < 0)
+ dev_err(ssusb->dev, "failed to register notifier for USB-HOST\n");
+
+ dev_dbg(ssusb->dev, "EXTCON_USB: %d, EXTCON_USB_HOST: %d\n",
+ extcon_get_cable_state_(edev, EXTCON_USB),
+ extcon_get_cable_state_(edev, EXTCON_USB_HOST));
+
+ /* default as host, switch to device mode if needed */
+ if (extcon_get_cable_state_(edev, EXTCON_USB_HOST) == false)
+ ssusb_set_mailbox(otg_sx, MTU3_ID_FLOAT);
+ if (extcon_get_cable_state_(edev, EXTCON_USB) == true)
+ ssusb_set_mailbox(otg_sx, MTU3_VBUS_VALID);
+
+ 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.
+ * This is useful in special cases, such as uses TYPE-A receptacle but also
+ * wants to support dual-role mode.
+ * It generates cable state changes by pulling up/down IDPIN and
+ * notifies driver to switch mode by "extcon-usb-gpio".
+ * NOTE: when use MICRO receptacle, should not enable this interface.
+ */
+static void ssusb_mode_manual_switch(struct ssusb_mtk *ssusb, int to_host)
+{
+ struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
+
+ if (to_host)
+ pinctrl_select_state(otg_sx->id_pinctrl, otg_sx->id_ground);
+ else
+ pinctrl_select_state(otg_sx->id_pinctrl, otg_sx->id_float);
+}
+
+
+static int ssusb_mode_show(struct seq_file *sf, void *unused)
+{
+ struct ssusb_mtk *ssusb = sf->private;
+
+ seq_printf(sf, "current mode: %s(%s drd)\n(echo device/host)\n",
+ ssusb->is_host ? "host" : "device",
+ ssusb->otg_switch.manual_drd_enabled ? "manual" : "auto");
+
+ return 0;
+}
+
+static int ssusb_mode_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ssusb_mode_show, inode->i_private);
+}
+
+static ssize_t ssusb_mode_write(struct file *file,
+ const char __user *ubuf, size_t count, loff_t *ppos)
+{
+ struct seq_file *sf = file->private_data;
+ struct ssusb_mtk *ssusb = sf->private;
+ char buf[16];
+
+ if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+ return -EFAULT;
+
+ if (!strncmp(buf, "host", 4) && !ssusb->is_host) {
+ ssusb_mode_manual_switch(ssusb, 1);
+ } else if (!strncmp(buf, "device", 6) && ssusb->is_host) {
+ ssusb_mode_manual_switch(ssusb, 0);
+ } else {
+ dev_err(ssusb->dev, "wrong or duplicated setting\n");
+ return -EINVAL;
+ }
+
+ return count;
+}
+
+static const struct file_operations ssusb_mode_fops = {
+ .open = ssusb_mode_open,
+ .write = ssusb_mode_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static void ssusb_debugfs_init(struct ssusb_mtk *ssusb)
+{
+ struct dentry *root;
+ struct dentry *file;
+
+ root = debugfs_create_dir(dev_name(ssusb->dev), usb_debug_root);
+ if (IS_ERR_OR_NULL(root)) {
+ if (!root)
+ dev_err(ssusb->dev, "create debugfs root failed\n");
+ return;
+ }
+ ssusb->dbgfs_root = root;
+
+ file = debugfs_create_file("mode", S_IRUGO | S_IWUSR, root,
+ ssusb, &ssusb_mode_fops);
+ if (!file)
+ dev_dbg(ssusb->dev, "create debugfs mode failed\n");
+}
+
+static void ssusb_debugfs_exit(struct ssusb_mtk *ssusb)
+{
+ debugfs_remove_recursive(ssusb->dbgfs_root);
+}
+
+int ssusb_otg_switch_init(struct ssusb_mtk *ssusb)
+{
+ struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
+
+ INIT_DELAYED_WORK(&otg_sx->extcon_reg_dwork, extcon_register_dwork);
+
+ if (otg_sx->manual_drd_enabled)
+ ssusb_debugfs_init(ssusb);
+
+ /* It is enough to delay 1s for waiting for host initialization */
+ schedule_delayed_work(&otg_sx->extcon_reg_dwork, HZ);
+
+ return 0;
+}
+
+void ssusb_otg_switch_exit(struct ssusb_mtk *ssusb)
+{
+ struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
+
+ cancel_delayed_work(&otg_sx->extcon_reg_dwork);
+
+ if (otg_sx->edev) {
+ extcon_unregister_notifier(otg_sx->edev,
+ EXTCON_USB, &otg_sx->vbus_nb);
+ extcon_unregister_notifier(otg_sx->edev,
+ EXTCON_USB_HOST, &otg_sx->id_nb);
+ }
+
+ if (otg_sx->manual_drd_enabled)
+ ssusb_debugfs_exit(ssusb);
+}
diff --git a/drivers/usb/mtu3/mtu3_dr.h b/drivers/usb/mtu3/mtu3_dr.h
new file mode 100644
index 000000000000..9b228b5811b0
--- /dev/null
+++ b/drivers/usb/mtu3/mtu3_dr.h
@@ -0,0 +1,108 @@
+/*
+ * mtu3_dr.h - dual role switch and host glue layer header
+ *
+ * Copyright (C) 2016 MediaTek Inc.
+ *
+ * Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _MTU3_DR_H_
+#define _MTU3_DR_H_
+
+#if IS_ENABLED(CONFIG_USB_MTU3_HOST) || IS_ENABLED(CONFIG_USB_MTU3_DUAL_ROLE)
+
+int ssusb_host_init(struct ssusb_mtk *ssusb, struct device_node *parent_dn);
+void ssusb_host_exit(struct ssusb_mtk *ssusb);
+int ssusb_wakeup_of_property_parse(struct ssusb_mtk *ssusb,
+ struct device_node *dn);
+int ssusb_host_enable(struct ssusb_mtk *ssusb);
+int ssusb_host_disable(struct ssusb_mtk *ssusb, bool suspend);
+int ssusb_wakeup_enable(struct ssusb_mtk *ssusb);
+void ssusb_wakeup_disable(struct ssusb_mtk *ssusb);
+
+#else
+
+static inline int ssusb_host_init(struct ssusb_mtk *ssusb,
+
+ struct device_node *parent_dn)
+{
+ return 0;
+}
+
+static inline void ssusb_host_exit(struct ssusb_mtk *ssusb)
+{}
+
+static inline int ssusb_wakeup_of_property_parse(
+ struct ssusb_mtk *ssusb, struct device_node *dn)
+{
+ return 0;
+}
+
+static inline int ssusb_host_enable(struct ssusb_mtk *ssusb)
+{
+ return 0;
+}
+
+static inline int ssusb_host_disable(struct ssusb_mtk *ssusb, bool suspend)
+{
+ return 0;
+}
+
+static inline int ssusb_wakeup_enable(struct ssusb_mtk *ssusb)
+{
+ return 0;
+}
+
+static inline void ssusb_wakeup_disable(struct ssusb_mtk *ssusb)
+{}
+
+#endif
+
+
+#if IS_ENABLED(CONFIG_USB_MTU3_GADGET) || IS_ENABLED(CONFIG_USB_MTU3_DUAL_ROLE)
+int ssusb_gadget_init(struct ssusb_mtk *ssusb);
+void ssusb_gadget_exit(struct ssusb_mtk *ssusb);
+#else
+static inline int ssusb_gadget_init(struct ssusb_mtk *ssusb)
+{
+ return 0;
+}
+
+static inline void ssusb_gadget_exit(struct ssusb_mtk *ssusb)
+{}
+#endif
+
+
+#if IS_ENABLED(CONFIG_USB_MTU3_DUAL_ROLE)
+int ssusb_otg_switch_init(struct ssusb_mtk *ssusb);
+void ssusb_otg_switch_exit(struct ssusb_mtk *ssusb);
+int ssusb_set_vbus(struct otg_switch_mtk *otg_sx, int is_on);
+
+#else
+
+static inline int ssusb_otg_switch_init(struct ssusb_mtk *ssusb)
+{
+ return 0;
+}
+
+static inline void ssusb_otg_switch_exit(struct ssusb_mtk *ssusb)
+{}
+
+static inline int ssusb_set_vbus(struct otg_switch_mtk *otg_sx, int is_on)
+{
+ return 0;
+}
+
+#endif
+
+#endif /* _MTU3_DR_H_ */
diff --git a/drivers/usb/mtu3/mtu3_gadget.c b/drivers/usb/mtu3/mtu3_gadget.c
new file mode 100644
index 000000000000..9dd2441b4fa1
--- /dev/null
+++ b/drivers/usb/mtu3/mtu3_gadget.c
@@ -0,0 +1,730 @@
+/*
+ * mtu3_gadget.c - MediaTek usb3 DRD peripheral support
+ *
+ * Copyright (C) 2016 MediaTek Inc.
+ *
+ * Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "mtu3.h"
+
+void mtu3_req_complete(struct mtu3_ep *mep,
+ struct usb_request *req, int status)
+__releases(mep->mtu->lock)
+__acquires(mep->mtu->lock)
+{
+ struct mtu3_request *mreq;
+ struct mtu3 *mtu;
+ int busy = mep->busy;
+
+ mreq = to_mtu3_request(req);
+ list_del(&mreq->list);
+ if (mreq->request.status == -EINPROGRESS)
+ mreq->request.status = status;
+
+ mtu = mreq->mtu;
+ mep->busy = 1;
+ spin_unlock(&mtu->lock);
+
+ /* ep0 makes use of PIO, needn't unmap it */
+ if (mep->epnum)
+ usb_gadget_unmap_request(&mtu->g, req, mep->is_in);
+
+ dev_dbg(mtu->dev, "%s complete req: %p, sts %d, %d/%d\n", mep->name,
+ req, req->status, mreq->request.actual, mreq->request.length);
+
+ usb_gadget_giveback_request(&mep->ep, &mreq->request);
+
+ spin_lock(&mtu->lock);
+ mep->busy = busy;
+}
+
+static void nuke(struct mtu3_ep *mep, const int status)
+{
+ struct mtu3_request *mreq = NULL;
+
+ mep->busy = 1;
+ if (list_empty(&mep->req_list))
+ return;
+
+ dev_dbg(mep->mtu->dev, "abort %s's req: sts %d\n", mep->name, status);
+
+ /* exclude EP0 */
+ if (mep->epnum)
+ mtu3_qmu_flush(mep);
+
+ while (!list_empty(&mep->req_list)) {
+ mreq = list_first_entry(&mep->req_list,
+ struct mtu3_request, list);
+ mtu3_req_complete(mep, &mreq->request, status);
+ }
+}
+
+static int mtu3_ep_enable(struct mtu3_ep *mep)
+{
+ const struct usb_endpoint_descriptor *desc;
+ const struct usb_ss_ep_comp_descriptor *comp_desc;
+ struct mtu3 *mtu = mep->mtu;
+ u32 interval = 0;
+ u32 mult = 0;
+ u32 burst = 0;
+ int max_packet;
+ int ret;
+
+ desc = mep->desc;
+ comp_desc = mep->comp_desc;
+ mep->type = usb_endpoint_type(desc);
+ max_packet = usb_endpoint_maxp(desc);
+ mep->maxp = max_packet & GENMASK(10, 0);
+
+ switch (mtu->g.speed) {
+ case USB_SPEED_SUPER:
+ if (usb_endpoint_xfer_int(desc) ||
+ usb_endpoint_xfer_isoc(desc)) {
+ interval = desc->bInterval;
+ interval = clamp_val(interval, 1, 16) - 1;
+ if (usb_endpoint_xfer_isoc(desc) && comp_desc)
+ mult = comp_desc->bmAttributes;
+ }
+ if (comp_desc)
+ burst = comp_desc->bMaxBurst;
+
+ break;
+ case USB_SPEED_HIGH:
+ if (usb_endpoint_xfer_isoc(desc) ||
+ usb_endpoint_xfer_int(desc)) {
+ interval = desc->bInterval;
+ interval = clamp_val(interval, 1, 16) - 1;
+ burst = (max_packet & GENMASK(12, 11)) >> 11;
+ }
+ break;
+ default:
+ break; /*others are ignored */
+ }
+
+ dev_dbg(mtu->dev, "%s maxp:%d, interval:%d, burst:%d, mult:%d\n",
+ __func__, mep->maxp, interval, burst, mult);
+
+ mep->ep.maxpacket = mep->maxp;
+ mep->ep.desc = desc;
+ mep->ep.comp_desc = comp_desc;
+
+ /* slot mainly affects bulk/isoc transfer, so ignore int */
+ mep->slot = usb_endpoint_xfer_int(desc) ? 0 : mtu->slot;
+
+ ret = mtu3_config_ep(mtu, mep, interval, burst, mult);
+ if (ret < 0)
+ return ret;
+
+ ret = mtu3_gpd_ring_alloc(mep);
+ if (ret < 0) {
+ mtu3_deconfig_ep(mtu, mep);
+ return ret;
+ }
+
+ mtu3_qmu_start(mep);
+
+ return 0;
+}
+
+static int mtu3_ep_disable(struct mtu3_ep *mep)
+{
+ struct mtu3 *mtu = mep->mtu;
+
+ mtu3_qmu_stop(mep);
+
+ /* abort all pending requests */
+ nuke(mep, -ESHUTDOWN);
+ mtu3_deconfig_ep(mtu, mep);
+ mtu3_gpd_ring_free(mep);
+
+ mep->desc = NULL;
+ mep->ep.desc = NULL;
+ mep->comp_desc = NULL;
+ mep->type = 0;
+ mep->flags = 0;
+
+ return 0;
+}
+
+static int mtu3_gadget_ep_enable(struct usb_ep *ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ struct mtu3_ep *mep;
+ struct mtu3 *mtu;
+ unsigned long flags;
+ int ret = -EINVAL;
+
+ if (!ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT) {
+ pr_debug("%s invalid parameters\n", __func__);
+ return -EINVAL;
+ }
+
+ if (!desc->wMaxPacketSize) {
+ pr_debug("%s missing wMaxPacketSize\n", __func__);
+ return -EINVAL;
+ }
+ mep = to_mtu3_ep(ep);
+ mtu = mep->mtu;
+
+ /* check ep number and direction against endpoint */
+ if (usb_endpoint_num(desc) != mep->epnum)
+ return -EINVAL;
+
+ if (!!usb_endpoint_dir_in(desc) ^ !!mep->is_in)
+ return -EINVAL;
+
+ dev_dbg(mtu->dev, "%s %s\n", __func__, ep->name);
+
+ if (mep->flags & MTU3_EP_ENABLED) {
+ dev_WARN_ONCE(mtu->dev, true, "%s is already enabled\n",
+ mep->name);
+ return 0;
+ }
+
+ spin_lock_irqsave(&mtu->lock, flags);
+ mep->desc = desc;
+ mep->comp_desc = ep->comp_desc;
+
+ ret = mtu3_ep_enable(mep);
+ if (ret)
+ goto error;
+
+ mep->busy = 0;
+ mep->wedged = 0;
+ mep->flags |= MTU3_EP_ENABLED;
+ mtu->active_ep++;
+
+error:
+ spin_unlock_irqrestore(&mtu->lock, flags);
+
+ dev_dbg(mtu->dev, "%s active_ep=%d\n", __func__, mtu->active_ep);
+
+ return ret;
+}
+
+static int mtu3_gadget_ep_disable(struct usb_ep *ep)
+{
+ struct mtu3_ep *mep = to_mtu3_ep(ep);
+ struct mtu3 *mtu = mep->mtu;
+ unsigned long flags;
+
+ dev_dbg(mtu->dev, "%s %s\n", __func__, mep->name);
+
+ if (!(mep->flags & MTU3_EP_ENABLED)) {
+ dev_warn(mtu->dev, "%s is already disabled\n", mep->name);
+ return 0;
+ }
+
+ spin_lock_irqsave(&mtu->lock, flags);
+ mtu3_ep_disable(mep);
+ mep->flags &= ~MTU3_EP_ENABLED;
+ mtu->active_ep--;
+ spin_unlock_irqrestore(&(mtu->lock), flags);
+
+ dev_dbg(mtu->dev, "%s active_ep=%d, mtu3 is_active=%d\n",
+ __func__, mtu->active_ep, mtu->is_active);
+
+ return 0;
+}
+
+struct usb_request *mtu3_alloc_request(struct usb_ep *ep, gfp_t gfp_flags)
+{
+ struct mtu3_ep *mep = to_mtu3_ep(ep);
+ struct mtu3_request *mreq;
+
+ mreq = kzalloc(sizeof(*mreq), gfp_flags);
+ if (!mreq)
+ return NULL;
+
+ mreq->request.dma = DMA_ADDR_INVALID;
+ mreq->epnum = mep->epnum;
+ mreq->mep = mep;
+
+ return &mreq->request;
+}
+
+void mtu3_free_request(struct usb_ep *ep, struct usb_request *req)
+{
+ kfree(to_mtu3_request(req));
+}
+
+static int mtu3_gadget_queue(struct usb_ep *ep,
+ struct usb_request *req, gfp_t gfp_flags)
+{
+ struct mtu3_ep *mep;
+ struct mtu3_request *mreq;
+ struct mtu3 *mtu;
+ unsigned long flags;
+ int ret = 0;
+
+ if (!ep || !req)
+ return -EINVAL;
+
+ if (!req->buf)
+ return -ENODATA;
+
+ mep = to_mtu3_ep(ep);
+ mtu = mep->mtu;
+ mreq = to_mtu3_request(req);
+ mreq->mtu = mtu;
+
+ if (mreq->mep != mep)
+ return -EINVAL;
+
+ dev_dbg(mtu->dev, "%s %s EP%d(%s), req=%p, maxp=%d, len#%d\n",
+ __func__, mep->is_in ? "TX" : "RX", mreq->epnum, ep->name,
+ mreq, ep->maxpacket, mreq->request.length);
+
+ if (req->length > GPD_BUF_SIZE) {
+ dev_warn(mtu->dev,
+ "req length > supported MAX:%d requested:%d\n",
+ GPD_BUF_SIZE, req->length);
+ return -EOPNOTSUPP;
+ }
+
+ /* don't queue if the ep is down */
+ if (!mep->desc) {
+ dev_dbg(mtu->dev, "req=%p queued to %s while it's disabled\n",
+ req, ep->name);
+ return -ESHUTDOWN;
+ }
+
+ mreq->request.actual = 0;
+ mreq->request.status = -EINPROGRESS;
+
+ ret = usb_gadget_map_request(&mtu->g, req, mep->is_in);
+ if (ret) {
+ dev_err(mtu->dev, "dma mapping failed\n");
+ return ret;
+ }
+
+ spin_lock_irqsave(&mtu->lock, flags);
+
+ if (mtu3_prepare_transfer(mep)) {
+ ret = -EAGAIN;
+ goto error;
+ }
+
+ list_add_tail(&mreq->list, &mep->req_list);
+ mtu3_insert_gpd(mep, mreq);
+ mtu3_qmu_resume(mep);
+
+error:
+ spin_unlock_irqrestore(&mtu->lock, flags);
+
+ return ret;
+}
+
+static int mtu3_gadget_dequeue(struct usb_ep *ep, struct usb_request *req)
+{
+ struct mtu3_ep *mep = to_mtu3_ep(ep);
+ struct mtu3_request *mreq = to_mtu3_request(req);
+ struct mtu3_request *r;
+ unsigned long flags;
+ int ret = 0;
+ struct mtu3 *mtu = mep->mtu;
+
+ if (!ep || !req || mreq->mep != mep)
+ return -EINVAL;
+
+ dev_dbg(mtu->dev, "%s : req=%p\n", __func__, req);
+
+ spin_lock_irqsave(&mtu->lock, flags);
+
+ list_for_each_entry(r, &mep->req_list, list) {
+ if (r == mreq)
+ break;
+ }
+ if (r != mreq) {
+ dev_dbg(mtu->dev, "req=%p not queued to %s\n", req, ep->name);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ mtu3_qmu_flush(mep); /* REVISIT: set BPS ?? */
+ mtu3_req_complete(mep, req, -ECONNRESET);
+ mtu3_qmu_start(mep);
+
+done:
+ spin_unlock_irqrestore(&mtu->lock, flags);
+
+ return ret;
+}
+
+/*
+ * Set or clear the halt bit of an EP.
+ * A halted EP won't TX/RX any data but will queue requests.
+ */
+static int mtu3_gadget_ep_set_halt(struct usb_ep *ep, int value)
+{
+ struct mtu3_ep *mep = to_mtu3_ep(ep);
+ struct mtu3 *mtu = mep->mtu;
+ struct mtu3_request *mreq;
+ unsigned long flags;
+ int ret = 0;
+
+ if (!ep)
+ return -EINVAL;
+
+ dev_dbg(mtu->dev, "%s : %s...", __func__, ep->name);
+
+ spin_lock_irqsave(&mtu->lock, flags);
+
+ if (mep->type == USB_ENDPOINT_XFER_ISOC) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ mreq = next_request(mep);
+ if (value) {
+ /*
+ * If there is not request for TX-EP, QMU will not transfer
+ * data to TX-FIFO, so no need check whether TX-FIFO
+ * holds bytes or not here
+ */
+ if (mreq) {
+ dev_dbg(mtu->dev, "req in progress, cannot halt %s\n",
+ ep->name);
+ ret = -EAGAIN;
+ goto done;
+ }
+ } else {
+ mep->wedged = 0;
+ }
+
+ dev_dbg(mtu->dev, "%s %s stall\n", ep->name, value ? "set" : "clear");
+
+ mtu3_ep_stall_set(mep, value);
+
+done:
+ spin_unlock_irqrestore(&mtu->lock, flags);
+
+ return ret;
+}
+
+/* Sets the halt feature with the clear requests ignored */
+static int mtu3_gadget_ep_set_wedge(struct usb_ep *ep)
+{
+ struct mtu3_ep *mep = to_mtu3_ep(ep);
+
+ if (!ep)
+ return -EINVAL;
+
+ mep->wedged = 1;
+
+ return usb_ep_set_halt(ep);
+}
+
+static const struct usb_ep_ops mtu3_ep_ops = {
+ .enable = mtu3_gadget_ep_enable,
+ .disable = mtu3_gadget_ep_disable,
+ .alloc_request = mtu3_alloc_request,
+ .free_request = mtu3_free_request,
+ .queue = mtu3_gadget_queue,
+ .dequeue = mtu3_gadget_dequeue,
+ .set_halt = mtu3_gadget_ep_set_halt,
+ .set_wedge = mtu3_gadget_ep_set_wedge,
+};
+
+static int mtu3_gadget_get_frame(struct usb_gadget *gadget)
+{
+ struct mtu3 *mtu = gadget_to_mtu3(gadget);
+
+ return (int)mtu3_readl(mtu->mac_base, U3D_USB20_FRAME_NUM);
+}
+
+static int mtu3_gadget_wakeup(struct usb_gadget *gadget)
+{
+ struct mtu3 *mtu = gadget_to_mtu3(gadget);
+ unsigned long flags;
+
+ dev_dbg(mtu->dev, "%s\n", __func__);
+
+ /* remote wakeup feature is not enabled by host */
+ if (!mtu->may_wakeup)
+ return -EOPNOTSUPP;
+
+ spin_lock_irqsave(&mtu->lock, flags);
+ if (mtu->g.speed == USB_SPEED_SUPER) {
+ mtu3_setbits(mtu->mac_base, U3D_LINK_POWER_CONTROL, UX_EXIT);
+ } else {
+ mtu3_setbits(mtu->mac_base, U3D_POWER_MANAGEMENT, RESUME);
+ spin_unlock_irqrestore(&mtu->lock, flags);
+ usleep_range(10000, 11000);
+ spin_lock_irqsave(&mtu->lock, flags);
+ mtu3_clrbits(mtu->mac_base, U3D_POWER_MANAGEMENT, RESUME);
+ }
+ spin_unlock_irqrestore(&mtu->lock, flags);
+ return 0;
+}
+
+static int mtu3_gadget_set_self_powered(struct usb_gadget *gadget,
+ int is_selfpowered)
+{
+ struct mtu3 *mtu = gadget_to_mtu3(gadget);
+
+ mtu->is_self_powered = !!is_selfpowered;
+ return 0;
+}
+
+static int mtu3_gadget_pullup(struct usb_gadget *gadget, int is_on)
+{
+ struct mtu3 *mtu = gadget_to_mtu3(gadget);
+ unsigned long flags;
+
+ dev_dbg(mtu->dev, "%s (%s) for %sactive device\n", __func__,
+ is_on ? "on" : "off", mtu->is_active ? "" : "in");
+
+ /* we'd rather not pullup unless the device is active. */
+ spin_lock_irqsave(&mtu->lock, flags);
+
+ is_on = !!is_on;
+ if (!mtu->is_active) {
+ /* save it for mtu3_start() to process the request */
+ mtu->softconnect = is_on;
+ } else if (is_on != mtu->softconnect) {
+ mtu->softconnect = is_on;
+ mtu3_dev_on_off(mtu, is_on);
+ }
+
+ spin_unlock_irqrestore(&mtu->lock, flags);
+
+ return 0;
+}
+
+static int mtu3_gadget_start(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
+{
+ struct mtu3 *mtu = gadget_to_mtu3(gadget);
+ unsigned long flags;
+
+ if (mtu->gadget_driver) {
+ dev_err(mtu->dev, "%s is already bound to %s\n",
+ mtu->g.name, mtu->gadget_driver->driver.name);
+ return -EBUSY;
+ }
+
+ dev_dbg(mtu->dev, "bind driver %s\n", driver->function);
+
+ spin_lock_irqsave(&mtu->lock, flags);
+
+ mtu->softconnect = 0;
+ mtu->gadget_driver = driver;
+
+ if (mtu->ssusb->dr_mode == USB_DR_MODE_PERIPHERAL)
+ mtu3_start(mtu);
+
+ spin_unlock_irqrestore(&mtu->lock, flags);
+
+ return 0;
+}
+
+static void stop_activity(struct mtu3 *mtu)
+{
+ struct usb_gadget_driver *driver = mtu->gadget_driver;
+ int i;
+
+ /* don't disconnect if it's not connected */
+ if (mtu->g.speed == USB_SPEED_UNKNOWN)
+ driver = NULL;
+ else
+ mtu->g.speed = USB_SPEED_UNKNOWN;
+
+ /* deactivate the hardware */
+ if (mtu->softconnect) {
+ mtu->softconnect = 0;
+ mtu3_dev_on_off(mtu, 0);
+ }
+
+ /*
+ * killing any outstanding requests will quiesce the driver;
+ * then report disconnect
+ */
+ nuke(mtu->ep0, -ESHUTDOWN);
+ for (i = 1; i < mtu->num_eps; i++) {
+ nuke(mtu->in_eps + i, -ESHUTDOWN);
+ nuke(mtu->out_eps + i, -ESHUTDOWN);
+ }
+
+ if (driver) {
+ spin_unlock(&mtu->lock);
+ driver->disconnect(&mtu->g);
+ spin_lock(&mtu->lock);
+ }
+}
+
+static int mtu3_gadget_stop(struct usb_gadget *g)
+{
+ struct mtu3 *mtu = gadget_to_mtu3(g);
+ unsigned long flags;
+
+ dev_dbg(mtu->dev, "%s\n", __func__);
+
+ spin_lock_irqsave(&mtu->lock, flags);
+
+ stop_activity(mtu);
+ mtu->gadget_driver = NULL;
+
+ if (mtu->ssusb->dr_mode == USB_DR_MODE_PERIPHERAL)
+ mtu3_stop(mtu);
+
+ spin_unlock_irqrestore(&mtu->lock, flags);
+
+ return 0;
+}
+
+static const struct usb_gadget_ops mtu3_gadget_ops = {
+ .get_frame = mtu3_gadget_get_frame,
+ .wakeup = mtu3_gadget_wakeup,
+ .set_selfpowered = mtu3_gadget_set_self_powered,
+ .pullup = mtu3_gadget_pullup,
+ .udc_start = mtu3_gadget_start,
+ .udc_stop = mtu3_gadget_stop,
+};
+
+static void init_hw_ep(struct mtu3 *mtu, struct mtu3_ep *mep,
+ u32 epnum, u32 is_in)
+{
+ mep->epnum = epnum;
+ mep->mtu = mtu;
+ mep->is_in = is_in;
+
+ INIT_LIST_HEAD(&mep->req_list);
+
+ sprintf(mep->name, "ep%d%s", epnum,
+ !epnum ? "" : (is_in ? "in" : "out"));
+
+ mep->ep.name = mep->name;
+ INIT_LIST_HEAD(&mep->ep.ep_list);
+
+ /* initialize maxpacket as SS */
+ if (!epnum) {
+ usb_ep_set_maxpacket_limit(&mep->ep, 512);
+ mep->ep.caps.type_control = true;
+ mep->ep.ops = &mtu3_ep0_ops;
+ mtu->g.ep0 = &mep->ep;
+ } else {
+ usb_ep_set_maxpacket_limit(&mep->ep, 1024);
+ mep->ep.caps.type_iso = true;
+ mep->ep.caps.type_bulk = true;
+ mep->ep.caps.type_int = true;
+ mep->ep.ops = &mtu3_ep_ops;
+ list_add_tail(&mep->ep.ep_list, &mtu->g.ep_list);
+ }
+
+ dev_dbg(mtu->dev, "%s, name=%s, maxp=%d\n", __func__, mep->ep.name,
+ mep->ep.maxpacket);
+
+ if (!epnum) {
+ mep->ep.caps.dir_in = true;
+ mep->ep.caps.dir_out = true;
+ } else if (is_in) {
+ mep->ep.caps.dir_in = true;
+ } else {
+ mep->ep.caps.dir_out = true;
+ }
+}
+
+static void mtu3_gadget_init_eps(struct mtu3 *mtu)
+{
+ u8 epnum;
+
+ /* initialize endpoint list just once */
+ INIT_LIST_HEAD(&(mtu->g.ep_list));
+
+ dev_dbg(mtu->dev, "%s num_eps(1 for a pair of tx&rx ep)=%d\n",
+ __func__, mtu->num_eps);
+
+ init_hw_ep(mtu, mtu->ep0, 0, 0);
+ for (epnum = 1; epnum < mtu->num_eps; epnum++) {
+ init_hw_ep(mtu, mtu->in_eps + epnum, epnum, 1);
+ init_hw_ep(mtu, mtu->out_eps + epnum, epnum, 0);
+ }
+}
+
+int mtu3_gadget_setup(struct mtu3 *mtu)
+{
+ int ret;
+
+ mtu->g.ops = &mtu3_gadget_ops;
+ mtu->g.max_speed = mtu->max_speed;
+ mtu->g.speed = USB_SPEED_UNKNOWN;
+ mtu->g.sg_supported = 0;
+ mtu->g.name = MTU3_DRIVER_NAME;
+ mtu->is_active = 0;
+
+ mtu3_gadget_init_eps(mtu);
+
+ ret = usb_add_gadget_udc(mtu->dev, &mtu->g);
+ if (ret) {
+ dev_err(mtu->dev, "failed to register udc\n");
+ return ret;
+ }
+
+ usb_gadget_set_state(&mtu->g, USB_STATE_NOTATTACHED);
+
+ return 0;
+}
+
+void mtu3_gadget_cleanup(struct mtu3 *mtu)
+{
+ usb_del_gadget_udc(&mtu->g);
+}
+
+void mtu3_gadget_resume(struct mtu3 *mtu)
+{
+ dev_dbg(mtu->dev, "gadget RESUME\n");
+ if (mtu->gadget_driver && mtu->gadget_driver->resume) {
+ spin_unlock(&mtu->lock);
+ mtu->gadget_driver->resume(&mtu->g);
+ spin_lock(&mtu->lock);
+ }
+}
+
+/* called when SOF packets stop for 3+ msec or enters U3 */
+void mtu3_gadget_suspend(struct mtu3 *mtu)
+{
+ dev_dbg(mtu->dev, "gadget SUSPEND\n");
+ if (mtu->gadget_driver && mtu->gadget_driver->suspend) {
+ spin_unlock(&mtu->lock);
+ mtu->gadget_driver->suspend(&mtu->g);
+ spin_lock(&mtu->lock);
+ }
+}
+
+/* called when VBUS drops below session threshold, and in other cases */
+void mtu3_gadget_disconnect(struct mtu3 *mtu)
+{
+ dev_dbg(mtu->dev, "gadget DISCONNECT\n");
+ if (mtu->gadget_driver && mtu->gadget_driver->disconnect) {
+ spin_unlock(&mtu->lock);
+ mtu->gadget_driver->disconnect(&mtu->g);
+ spin_lock(&mtu->lock);
+ }
+
+ usb_gadget_set_state(&mtu->g, USB_STATE_NOTATTACHED);
+}
+
+void mtu3_gadget_reset(struct mtu3 *mtu)
+{
+ dev_dbg(mtu->dev, "gadget RESET\n");
+
+ /* 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;
+}
diff --git a/drivers/usb/mtu3/mtu3_gadget_ep0.c b/drivers/usb/mtu3/mtu3_gadget_ep0.c
new file mode 100644
index 000000000000..2d7427b48775
--- /dev/null
+++ b/drivers/usb/mtu3/mtu3_gadget_ep0.c
@@ -0,0 +1,881 @@
+/*
+ * mtu3_gadget_ep0.c - MediaTek USB3 DRD peripheral driver ep0 handling
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ *
+ * Author: Chunfeng.Yun <chunfeng.yun@mediatek.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "mtu3.h"
+
+/* ep0 is always mtu3->in_eps[0] */
+#define next_ep0_request(mtu) next_request((mtu)->ep0)
+
+/* for high speed test mode; see USB 2.0 spec 7.1.20 */
+static const u8 mtu3_test_packet[53] = {
+ /* implicit SYNC then DATA0 to start */
+
+ /* JKJKJKJK x9 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* JJKKJJKK x8 */
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ /* JJJJKKKK x8 */
+ 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee,
+ /* JJJJJJJKKKKKKK x8 */
+ 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ /* JJJJJJJK x8 */
+ 0x7f, 0xbf, 0xdf, 0xef, 0xf7, 0xfb, 0xfd,
+ /* JKKKKKKK x10, JK */
+ 0xfc, 0x7e, 0xbf, 0xdf, 0xef, 0xf7, 0xfb, 0xfd, 0x7e,
+ /* implicit CRC16 then EOP to end */
+};
+
+static char *decode_ep0_state(struct mtu3 *mtu)
+{
+ switch (mtu->ep0_state) {
+ case MU3D_EP0_STATE_SETUP:
+ return "SETUP";
+ case MU3D_EP0_STATE_TX:
+ return "IN";
+ case MU3D_EP0_STATE_RX:
+ return "OUT";
+ case MU3D_EP0_STATE_TX_END:
+ return "TX-END";
+ case MU3D_EP0_STATE_STALL:
+ return "STALL";
+ default:
+ return "??";
+ }
+}
+
+static void ep0_req_giveback(struct mtu3 *mtu, struct usb_request *req)
+{
+ mtu3_req_complete(mtu->ep0, req, 0);
+}
+
+static int
+forward_to_driver(struct mtu3 *mtu, const struct usb_ctrlrequest *setup)
+__releases(mtu->lock)
+__acquires(mtu->lock)
+{
+ int ret;
+
+ if (!mtu->gadget_driver)
+ return -EOPNOTSUPP;
+
+ spin_unlock(&mtu->lock);
+ ret = mtu->gadget_driver->setup(&mtu->g, setup);
+ spin_lock(&mtu->lock);
+
+ dev_dbg(mtu->dev, "%s ret %d\n", __func__, ret);
+ return ret;
+}
+
+static void ep0_write_fifo(struct mtu3_ep *mep, const u8 *src, u16 len)
+{
+ void __iomem *fifo = mep->mtu->mac_base + U3D_FIFO0;
+ u16 index = 0;
+
+ dev_dbg(mep->mtu->dev, "%s: ep%din, len=%d, buf=%p\n",
+ __func__, mep->epnum, len, src);
+
+ if (len >= 4) {
+ iowrite32_rep(fifo, src, len >> 2);
+ index = len & ~0x03;
+ }
+ if (len & 0x02) {
+ writew(*(u16 *)&src[index], fifo);
+ index += 2;
+ }
+ if (len & 0x01)
+ writeb(src[index], fifo);
+}
+
+static void ep0_read_fifo(struct mtu3_ep *mep, u8 *dst, u16 len)
+{
+ void __iomem *fifo = mep->mtu->mac_base + U3D_FIFO0;
+ u32 value;
+ u16 index = 0;
+
+ dev_dbg(mep->mtu->dev, "%s: ep%dout len=%d buf=%p\n",
+ __func__, mep->epnum, len, dst);
+
+ if (len >= 4) {
+ ioread32_rep(fifo, dst, len >> 2);
+ index = len & ~0x03;
+ }
+ if (len & 0x3) {
+ value = readl(fifo);
+ memcpy(&dst[index], &value, len & 0x3);
+ }
+
+}
+
+static void ep0_load_test_packet(struct mtu3 *mtu)
+{
+ /*
+ * because the length of test packet is less than max packet of HS ep0,
+ * write it into fifo directly.
+ */
+ ep0_write_fifo(mtu->ep0, mtu3_test_packet, sizeof(mtu3_test_packet));
+}
+
+/*
+ * A. send STALL for setup transfer without data stage:
+ * set SENDSTALL and SETUPPKTRDY at the same time;
+ * B. send STALL for other cases:
+ * set SENDSTALL only.
+ */
+static void ep0_stall_set(struct mtu3_ep *mep0, bool set, u32 pktrdy)
+{
+ struct mtu3 *mtu = mep0->mtu;
+ void __iomem *mbase = mtu->mac_base;
+ u32 csr;
+
+ /* EP0_SENTSTALL is W1C */
+ csr = mtu3_readl(mbase, U3D_EP0CSR) & EP0_W1C_BITS;
+ if (set)
+ csr |= EP0_SENDSTALL | pktrdy;
+ else
+ csr = (csr & ~EP0_SENDSTALL) | EP0_SENTSTALL;
+ mtu3_writel(mtu->mac_base, U3D_EP0CSR, csr);
+
+ mtu->ep0_state = MU3D_EP0_STATE_SETUP;
+
+ dev_dbg(mtu->dev, "ep0: %s STALL, ep0_state: %s\n",
+ set ? "SEND" : "CLEAR", decode_ep0_state(mtu));
+}
+
+static int ep0_queue(struct mtu3_ep *mep0, struct mtu3_request *mreq);
+
+static void ep0_dummy_complete(struct usb_ep *ep, struct usb_request *req)
+{}
+
+static void ep0_set_sel_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct mtu3_request *mreq;
+ struct mtu3 *mtu;
+ struct usb_set_sel_req sel;
+
+ memcpy(&sel, req->buf, sizeof(sel));
+
+ mreq = to_mtu3_request(req);
+ mtu = mreq->mtu;
+ dev_dbg(mtu->dev, "u1sel:%d, u1pel:%d, u2sel:%d, u2pel:%d\n",
+ sel.u1_sel, sel.u1_pel, sel.u2_sel, sel.u2_pel);
+}
+
+/* queue data stage to handle 6 byte SET_SEL request */
+static int ep0_set_sel(struct mtu3 *mtu, struct usb_ctrlrequest *setup)
+{
+ int ret;
+ u16 length = le16_to_cpu(setup->wLength);
+
+ if (unlikely(length != 6)) {
+ dev_err(mtu->dev, "%s wrong wLength:%d\n",
+ __func__, length);
+ return -EINVAL;
+ }
+
+ mtu->ep0_req.mep = mtu->ep0;
+ mtu->ep0_req.request.length = 6;
+ mtu->ep0_req.request.buf = mtu->setup_buf;
+ mtu->ep0_req.request.complete = ep0_set_sel_complete;
+ ret = ep0_queue(mtu->ep0, &mtu->ep0_req);
+
+ return ret < 0 ? ret : 1;
+}
+
+static int
+ep0_get_status(struct mtu3 *mtu, const struct usb_ctrlrequest *setup)
+{
+ struct mtu3_ep *mep = NULL;
+ int handled = 1;
+ u8 result[2] = {0, 0};
+ u8 epnum = 0;
+ int is_in;
+
+ switch (setup->bRequestType & USB_RECIP_MASK) {
+ case USB_RECIP_DEVICE:
+ result[0] = mtu->is_self_powered << USB_DEVICE_SELF_POWERED;
+ result[0] |= mtu->may_wakeup << USB_DEVICE_REMOTE_WAKEUP;
+ /* superspeed only */
+ if (mtu->g.speed == USB_SPEED_SUPER) {
+ result[0] |= mtu->u1_enable << USB_DEV_STAT_U1_ENABLED;
+ result[0] |= mtu->u2_enable << USB_DEV_STAT_U2_ENABLED;
+ }
+
+ dev_dbg(mtu->dev, "%s result=%x, U1=%x, U2=%x\n", __func__,
+ result[0], mtu->u1_enable, mtu->u2_enable);
+
+ break;
+ case USB_RECIP_INTERFACE:
+ break;
+ case USB_RECIP_ENDPOINT:
+ epnum = (u8) le16_to_cpu(setup->wIndex);
+ is_in = epnum & USB_DIR_IN;
+ epnum &= USB_ENDPOINT_NUMBER_MASK;
+
+ if (epnum >= mtu->num_eps) {
+ handled = -EINVAL;
+ break;
+ }
+ if (!epnum)
+ break;
+
+ mep = (is_in ? mtu->in_eps : mtu->out_eps) + epnum;
+ if (!mep->desc) {
+ handled = -EINVAL;
+ break;
+ }
+ if (mep->flags & MTU3_EP_STALL)
+ result[0] |= 1 << USB_ENDPOINT_HALT;
+
+ break;
+ default:
+ /* class, vendor, etc ... delegate */
+ handled = 0;
+ break;
+ }
+
+ if (handled > 0) {
+ int ret;
+
+ /* prepare a data stage for GET_STATUS */
+ dev_dbg(mtu->dev, "get_status=%x\n", *(u16 *)result);
+ memcpy(mtu->setup_buf, result, sizeof(result));
+ mtu->ep0_req.mep = mtu->ep0;
+ mtu->ep0_req.request.length = 2;
+ mtu->ep0_req.request.buf = &mtu->setup_buf;
+ mtu->ep0_req.request.complete = ep0_dummy_complete;
+ ret = ep0_queue(mtu->ep0, &mtu->ep0_req);
+ if (ret < 0)
+ handled = ret;
+ }
+ return handled;
+}
+
+static int handle_test_mode(struct mtu3 *mtu, struct usb_ctrlrequest *setup)
+{
+ void __iomem *mbase = mtu->mac_base;
+ int handled = 1;
+
+ switch (le16_to_cpu(setup->wIndex) >> 8) {
+ case TEST_J:
+ dev_dbg(mtu->dev, "TEST_J\n");
+ mtu->test_mode_nr = TEST_J_MODE;
+ break;
+ case TEST_K:
+ dev_dbg(mtu->dev, "TEST_K\n");
+ mtu->test_mode_nr = TEST_K_MODE;
+ break;
+ case TEST_SE0_NAK:
+ dev_dbg(mtu->dev, "TEST_SE0_NAK\n");
+ mtu->test_mode_nr = TEST_SE0_NAK_MODE;
+ break;
+ case TEST_PACKET:
+ dev_dbg(mtu->dev, "TEST_PACKET\n");
+ mtu->test_mode_nr = TEST_PACKET_MODE;
+ break;
+ default:
+ handled = -EINVAL;
+ goto out;
+ }
+
+ mtu->test_mode = true;
+
+ /* no TX completion interrupt, and need restart platform after test */
+ if (mtu->test_mode_nr == TEST_PACKET_MODE)
+ ep0_load_test_packet(mtu);
+
+ mtu3_writel(mbase, U3D_USB2_TEST_MODE, mtu->test_mode_nr);
+
+ mtu->ep0_state = MU3D_EP0_STATE_SETUP;
+
+out:
+ return handled;
+}
+
+static int ep0_handle_feature_dev(struct mtu3 *mtu,
+ struct usb_ctrlrequest *setup, bool set)
+{
+ void __iomem *mbase = mtu->mac_base;
+ int handled = -EINVAL;
+ u32 lpc;
+
+ switch (le16_to_cpu(setup->wValue)) {
+ case USB_DEVICE_REMOTE_WAKEUP:
+ mtu->may_wakeup = !!set;
+ handled = 1;
+ break;
+ case USB_DEVICE_TEST_MODE:
+ if (!set || (mtu->g.speed != USB_SPEED_HIGH) ||
+ (le16_to_cpu(setup->wIndex) & 0xff))
+ break;
+
+ handled = handle_test_mode(mtu, setup);
+ break;
+ case USB_DEVICE_U1_ENABLE:
+ if (mtu->g.speed != USB_SPEED_SUPER ||
+ mtu->g.state != USB_STATE_CONFIGURED)
+ break;
+
+ lpc = mtu3_readl(mbase, U3D_LINK_POWER_CONTROL);
+ if (set)
+ lpc |= SW_U1_ACCEPT_ENABLE;
+ else
+ lpc &= ~SW_U1_ACCEPT_ENABLE;
+ mtu3_writel(mbase, U3D_LINK_POWER_CONTROL, lpc);
+
+ mtu->u1_enable = !!set;
+ handled = 1;
+ break;
+ case USB_DEVICE_U2_ENABLE:
+ if (mtu->g.speed != USB_SPEED_SUPER ||
+ mtu->g.state != USB_STATE_CONFIGURED)
+ break;
+
+ lpc = mtu3_readl(mbase, U3D_LINK_POWER_CONTROL);
+ if (set)
+ lpc |= SW_U2_ACCEPT_ENABLE;
+ else
+ lpc &= ~SW_U2_ACCEPT_ENABLE;
+ mtu3_writel(mbase, U3D_LINK_POWER_CONTROL, lpc);
+
+ mtu->u2_enable = !!set;
+ handled = 1;
+ break;
+ default:
+ handled = -EINVAL;
+ break;
+ }
+ return handled;
+}
+
+static int ep0_handle_feature(struct mtu3 *mtu,
+ struct usb_ctrlrequest *setup, bool set)
+{
+ struct mtu3_ep *mep;
+ int handled = -EINVAL;
+ int is_in;
+ u16 value;
+ u16 index;
+ u8 epnum;
+
+ value = le16_to_cpu(setup->wValue);
+ index = le16_to_cpu(setup->wIndex);
+
+ switch (setup->bRequestType & USB_RECIP_MASK) {
+ case USB_RECIP_DEVICE:
+ handled = ep0_handle_feature_dev(mtu, setup, set);
+ break;
+ case USB_RECIP_INTERFACE:
+ /* superspeed only */
+ if ((value == USB_INTRF_FUNC_SUSPEND)
+ && (mtu->g.speed == USB_SPEED_SUPER)) {
+ /*
+ * forward the request because function drivers
+ * should handle it
+ */
+ handled = 0;
+ }
+ break;
+ case USB_RECIP_ENDPOINT:
+ epnum = index & USB_ENDPOINT_NUMBER_MASK;
+ if (epnum == 0 || epnum >= mtu->num_eps ||
+ value != USB_ENDPOINT_HALT)
+ break;
+
+ is_in = index & USB_DIR_IN;
+ mep = (is_in ? mtu->in_eps : mtu->out_eps) + epnum;
+ if (!mep->desc)
+ break;
+
+ handled = 1;
+ /* ignore request if endpoint is wedged */
+ if (mep->wedged)
+ break;
+
+ mtu3_ep_stall_set(mep, set);
+ break;
+ default:
+ /* class, vendor, etc ... delegate */
+ handled = 0;
+ break;
+ }
+ return handled;
+}
+
+/*
+ * handle all control requests can be handled
+ * returns:
+ * negative errno - error happened
+ * zero - need delegate SETUP to gadget driver
+ * positive - already handled
+ */
+static int handle_standard_request(struct mtu3 *mtu,
+ struct usb_ctrlrequest *setup)
+{
+ void __iomem *mbase = mtu->mac_base;
+ enum usb_device_state state = mtu->g.state;
+ int handled = -EINVAL;
+ u32 dev_conf;
+ u16 value;
+
+ value = le16_to_cpu(setup->wValue);
+
+ /* the gadget driver handles everything except what we must handle */
+ switch (setup->bRequest) {
+ case USB_REQ_SET_ADDRESS:
+ /* change it after the status stage */
+ mtu->address = (u8) (value & 0x7f);
+ dev_dbg(mtu->dev, "set address to 0x%x\n", mtu->address);
+
+ dev_conf = mtu3_readl(mbase, U3D_DEVICE_CONF);
+ dev_conf &= ~DEV_ADDR_MSK;
+ dev_conf |= DEV_ADDR(mtu->address);
+ mtu3_writel(mbase, U3D_DEVICE_CONF, dev_conf);
+
+ if (mtu->address)
+ usb_gadget_set_state(&mtu->g, USB_STATE_ADDRESS);
+ else
+ usb_gadget_set_state(&mtu->g, USB_STATE_DEFAULT);
+
+ handled = 1;
+ break;
+ case USB_REQ_SET_CONFIGURATION:
+ if (state == USB_STATE_ADDRESS) {
+ usb_gadget_set_state(&mtu->g,
+ USB_STATE_CONFIGURED);
+ } else if (state == USB_STATE_CONFIGURED) {
+ /*
+ * USB2 spec sec 9.4.7, if wValue is 0 then dev
+ * is moved to addressed state
+ */
+ if (!value)
+ usb_gadget_set_state(&mtu->g,
+ USB_STATE_ADDRESS);
+ }
+ handled = 0;
+ break;
+ case USB_REQ_CLEAR_FEATURE:
+ handled = ep0_handle_feature(mtu, setup, 0);
+ break;
+ case USB_REQ_SET_FEATURE:
+ handled = ep0_handle_feature(mtu, setup, 1);
+ break;
+ case USB_REQ_GET_STATUS:
+ handled = ep0_get_status(mtu, setup);
+ break;
+ case USB_REQ_SET_SEL:
+ handled = ep0_set_sel(mtu, setup);
+ break;
+ case USB_REQ_SET_ISOCH_DELAY:
+ handled = 1;
+ break;
+ default:
+ /* delegate SET_CONFIGURATION, etc */
+ handled = 0;
+ }
+
+ return handled;
+}
+
+/* receive an data packet (OUT) */
+static void ep0_rx_state(struct mtu3 *mtu)
+{
+ struct mtu3_request *mreq;
+ struct usb_request *req;
+ void __iomem *mbase = mtu->mac_base;
+ u32 maxp;
+ u32 csr;
+ u16 count = 0;
+
+ dev_dbg(mtu->dev, "%s\n", __func__);
+
+ csr = mtu3_readl(mbase, U3D_EP0CSR) & EP0_W1C_BITS;
+ mreq = next_ep0_request(mtu);
+ req = &mreq->request;
+
+ /* read packet and ack; or stall because of gadget driver bug */
+ if (req) {
+ void *buf = req->buf + req->actual;
+ unsigned int len = req->length - req->actual;
+
+ /* read the buffer */
+ count = mtu3_readl(mbase, U3D_RXCOUNT0);
+ if (count > len) {
+ req->status = -EOVERFLOW;
+ count = len;
+ }
+ ep0_read_fifo(mtu->ep0, buf, count);
+ req->actual += count;
+ csr |= EP0_RXPKTRDY;
+
+ maxp = mtu->g.ep0->maxpacket;
+ if (count < maxp || req->actual == req->length) {
+ mtu->ep0_state = MU3D_EP0_STATE_SETUP;
+ dev_dbg(mtu->dev, "ep0 state: %s\n",
+ decode_ep0_state(mtu));
+
+ csr |= EP0_DATAEND;
+ } else {
+ req = NULL;
+ }
+ } else {
+ csr |= EP0_RXPKTRDY | EP0_SENDSTALL;
+ dev_dbg(mtu->dev, "%s: SENDSTALL\n", __func__);
+ }
+
+ mtu3_writel(mbase, U3D_EP0CSR, csr);
+
+ /* give back the request if have received all data */
+ if (req)
+ ep0_req_giveback(mtu, req);
+
+}
+
+/* transmitting to the host (IN) */
+static void ep0_tx_state(struct mtu3 *mtu)
+{
+ struct mtu3_request *mreq = next_ep0_request(mtu);
+ struct usb_request *req;
+ u32 csr;
+ u8 *src;
+ u8 count;
+ u32 maxp;
+
+ dev_dbg(mtu->dev, "%s\n", __func__);
+
+ if (!mreq)
+ return;
+
+ maxp = mtu->g.ep0->maxpacket;
+ req = &mreq->request;
+
+ /* load the data */
+ src = (u8 *)req->buf + req->actual;
+ count = min(maxp, req->length - req->actual);
+ if (count)
+ ep0_write_fifo(mtu->ep0, src, count);
+
+ dev_dbg(mtu->dev, "%s act=%d, len=%d, cnt=%d, maxp=%d zero=%d\n",
+ __func__, req->actual, req->length, count, maxp, req->zero);
+
+ req->actual += count;
+
+ if ((count < maxp)
+ || ((req->actual == req->length) && !req->zero))
+ mtu->ep0_state = MU3D_EP0_STATE_TX_END;
+
+ /* send it out, triggering a "txpktrdy cleared" irq */
+ csr = mtu3_readl(mtu->mac_base, U3D_EP0CSR) & EP0_W1C_BITS;
+ mtu3_writel(mtu->mac_base, U3D_EP0CSR, csr | EP0_TXPKTRDY);
+
+ dev_dbg(mtu->dev, "%s ep0csr=0x%x\n", __func__,
+ mtu3_readl(mtu->mac_base, U3D_EP0CSR));
+}
+
+static void ep0_read_setup(struct mtu3 *mtu, struct usb_ctrlrequest *setup)
+{
+ struct mtu3_request *mreq;
+ u32 count;
+ u32 csr;
+
+ csr = mtu3_readl(mtu->mac_base, U3D_EP0CSR) & EP0_W1C_BITS;
+ count = mtu3_readl(mtu->mac_base, U3D_RXCOUNT0);
+
+ ep0_read_fifo(mtu->ep0, (u8 *)setup, count);
+
+ dev_dbg(mtu->dev, "SETUP req%02x.%02x v%04x i%04x l%04x\n",
+ setup->bRequestType, setup->bRequest,
+ le16_to_cpu(setup->wValue), le16_to_cpu(setup->wIndex),
+ le16_to_cpu(setup->wLength));
+
+ /* clean up any leftover transfers */
+ mreq = next_ep0_request(mtu);
+ if (mreq)
+ ep0_req_giveback(mtu, &mreq->request);
+
+ if (le16_to_cpu(setup->wLength) == 0) {
+ ; /* no data stage, nothing to do */
+ } else if (setup->bRequestType & USB_DIR_IN) {
+ mtu3_writel(mtu->mac_base, U3D_EP0CSR,
+ csr | EP0_SETUPPKTRDY | EP0_DPHTX);
+ mtu->ep0_state = MU3D_EP0_STATE_TX;
+ } else {
+ mtu3_writel(mtu->mac_base, U3D_EP0CSR,
+ (csr | EP0_SETUPPKTRDY) & (~EP0_DPHTX));
+ mtu->ep0_state = MU3D_EP0_STATE_RX;
+ }
+}
+
+static int ep0_handle_setup(struct mtu3 *mtu)
+__releases(mtu->lock)
+__acquires(mtu->lock)
+{
+ struct usb_ctrlrequest setup;
+ struct mtu3_request *mreq;
+ void __iomem *mbase = mtu->mac_base;
+ int handled = 0;
+
+ ep0_read_setup(mtu, &setup);
+
+ if ((setup.bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)
+ handled = handle_standard_request(mtu, &setup);
+
+ dev_dbg(mtu->dev, "handled %d, ep0_state: %s\n",
+ handled, decode_ep0_state(mtu));
+
+ if (handled < 0)
+ goto stall;
+ else if (handled > 0)
+ goto finish;
+
+ handled = forward_to_driver(mtu, &setup);
+ if (handled < 0) {
+stall:
+ dev_dbg(mtu->dev, "%s stall (%d)\n", __func__, handled);
+
+ ep0_stall_set(mtu->ep0, true,
+ le16_to_cpu(setup.wLength) ? 0 : EP0_SETUPPKTRDY);
+
+ return 0;
+ }
+
+finish:
+ if (mtu->test_mode) {
+ ; /* nothing to do */
+ } else if (le16_to_cpu(setup.wLength) == 0) { /* no data stage */
+
+ mtu3_writel(mbase, U3D_EP0CSR,
+ (mtu3_readl(mbase, U3D_EP0CSR) & EP0_W1C_BITS)
+ | EP0_SETUPPKTRDY | EP0_DATAEND);
+
+ /* complete zlp request directly */
+ mreq = next_ep0_request(mtu);
+ if (mreq && !mreq->request.length)
+ ep0_req_giveback(mtu, &mreq->request);
+ }
+
+ return 0;
+}
+
+irqreturn_t mtu3_ep0_isr(struct mtu3 *mtu)
+{
+ void __iomem *mbase = mtu->mac_base;
+ struct mtu3_request *mreq;
+ u32 int_status;
+ irqreturn_t ret = IRQ_NONE;
+ u32 csr;
+ u32 len;
+
+ int_status = mtu3_readl(mbase, U3D_EPISR);
+ int_status &= mtu3_readl(mbase, U3D_EPIER);
+ mtu3_writel(mbase, U3D_EPISR, int_status); /* W1C */
+
+ /* only handle ep0's */
+ if (!(int_status & EP0ISR))
+ return IRQ_NONE;
+
+ csr = mtu3_readl(mbase, U3D_EP0CSR);
+
+ dev_dbg(mtu->dev, "%s csr=0x%x\n", __func__, csr);
+
+ /* we sent a stall.. need to clear it now.. */
+ if (csr & EP0_SENTSTALL) {
+ ep0_stall_set(mtu->ep0, false, 0);
+ csr = mtu3_readl(mbase, U3D_EP0CSR);
+ ret = IRQ_HANDLED;
+ }
+ dev_dbg(mtu->dev, "ep0_state: %s\n", decode_ep0_state(mtu));
+
+ switch (mtu->ep0_state) {
+ case MU3D_EP0_STATE_TX:
+ /* irq on clearing txpktrdy */
+ if ((csr & EP0_FIFOFULL) == 0) {
+ ep0_tx_state(mtu);
+ ret = IRQ_HANDLED;
+ }
+ break;
+ case MU3D_EP0_STATE_RX:
+ /* irq on set rxpktrdy */
+ if (csr & EP0_RXPKTRDY) {
+ ep0_rx_state(mtu);
+ ret = IRQ_HANDLED;
+ }
+ break;
+ case MU3D_EP0_STATE_TX_END:
+ mtu3_writel(mbase, U3D_EP0CSR,
+ (csr & EP0_W1C_BITS) | EP0_DATAEND);
+
+ mreq = next_ep0_request(mtu);
+ if (mreq)
+ ep0_req_giveback(mtu, &mreq->request);
+
+ mtu->ep0_state = MU3D_EP0_STATE_SETUP;
+ ret = IRQ_HANDLED;
+ dev_dbg(mtu->dev, "ep0_state: %s\n", decode_ep0_state(mtu));
+ break;
+ case MU3D_EP0_STATE_SETUP:
+ if (!(csr & EP0_SETUPPKTRDY))
+ break;
+
+ len = mtu3_readl(mbase, U3D_RXCOUNT0);
+ if (len != 8) {
+ dev_err(mtu->dev, "SETUP packet len %d != 8 ?\n", len);
+ break;
+ }
+
+ ep0_handle_setup(mtu);
+ ret = IRQ_HANDLED;
+ break;
+ default:
+ /* can't happen */
+ ep0_stall_set(mtu->ep0, true, 0);
+ WARN_ON(1);
+ break;
+ }
+
+ return ret;
+}
+
+
+static int mtu3_ep0_enable(struct usb_ep *ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ /* always enabled */
+ return -EINVAL;
+}
+
+static int mtu3_ep0_disable(struct usb_ep *ep)
+{
+ /* always enabled */
+ return -EINVAL;
+}
+
+static int ep0_queue(struct mtu3_ep *mep, struct mtu3_request *mreq)
+{
+ struct mtu3 *mtu = mep->mtu;
+
+ mreq->mtu = mtu;
+ mreq->request.actual = 0;
+ mreq->request.status = -EINPROGRESS;
+
+ dev_dbg(mtu->dev, "%s %s (ep0_state: %s), len#%d\n", __func__,
+ mep->name, decode_ep0_state(mtu), mreq->request.length);
+
+ if (!list_empty(&mep->req_list))
+ return -EBUSY;
+
+ switch (mtu->ep0_state) {
+ case MU3D_EP0_STATE_SETUP:
+ case MU3D_EP0_STATE_RX: /* control-OUT data */
+ case MU3D_EP0_STATE_TX: /* control-IN data */
+ break;
+ default:
+ dev_err(mtu->dev, "%s, error in ep0 state %s\n", __func__,
+ decode_ep0_state(mtu));
+ return -EINVAL;
+ }
+
+ list_add_tail(&mreq->list, &mep->req_list);
+
+ /* sequence #1, IN ... start writing the data */
+ if (mtu->ep0_state == MU3D_EP0_STATE_TX)
+ ep0_tx_state(mtu);
+
+ return 0;
+}
+
+static int mtu3_ep0_queue(struct usb_ep *ep,
+ struct usb_request *req, gfp_t gfp)
+{
+ struct mtu3_ep *mep;
+ struct mtu3_request *mreq;
+ struct mtu3 *mtu;
+ unsigned long flags;
+ int ret = 0;
+
+ if (!ep || !req)
+ return -EINVAL;
+
+ mep = to_mtu3_ep(ep);
+ mtu = mep->mtu;
+ mreq = to_mtu3_request(req);
+
+ spin_lock_irqsave(&mtu->lock, flags);
+ ret = ep0_queue(mep, mreq);
+ spin_unlock_irqrestore(&mtu->lock, flags);
+ return ret;
+}
+
+static int mtu3_ep0_dequeue(struct usb_ep *ep, struct usb_request *req)
+{
+ /* we just won't support this */
+ return -EINVAL;
+}
+
+static int mtu3_ep0_halt(struct usb_ep *ep, int value)
+{
+ struct mtu3_ep *mep;
+ struct mtu3 *mtu;
+ unsigned long flags;
+ int ret = 0;
+
+ if (!ep || !value)
+ return -EINVAL;
+
+ mep = to_mtu3_ep(ep);
+ mtu = mep->mtu;
+
+ dev_dbg(mtu->dev, "%s\n", __func__);
+
+ spin_lock_irqsave(&mtu->lock, flags);
+
+ if (!list_empty(&mep->req_list)) {
+ ret = -EBUSY;
+ goto cleanup;
+ }
+
+ switch (mtu->ep0_state) {
+ /*
+ * stalls are usually issued after parsing SETUP packet, either
+ * directly in irq context from setup() or else later.
+ */
+ case MU3D_EP0_STATE_TX:
+ case MU3D_EP0_STATE_TX_END:
+ case MU3D_EP0_STATE_RX:
+ case MU3D_EP0_STATE_SETUP:
+ ep0_stall_set(mtu->ep0, true, 0);
+ break;
+ default:
+ dev_dbg(mtu->dev, "ep0 can't halt in state %s\n",
+ decode_ep0_state(mtu));
+ ret = -EINVAL;
+ }
+
+cleanup:
+ spin_unlock_irqrestore(&mtu->lock, flags);
+ return ret;
+}
+
+const struct usb_ep_ops mtu3_ep0_ops = {
+ .enable = mtu3_ep0_enable,
+ .disable = mtu3_ep0_disable,
+ .alloc_request = mtu3_alloc_request,
+ .free_request = mtu3_free_request,
+ .queue = mtu3_ep0_queue,
+ .dequeue = mtu3_ep0_dequeue,
+ .set_halt = mtu3_ep0_halt,
+};
diff --git a/drivers/usb/mtu3/mtu3_host.c b/drivers/usb/mtu3/mtu3_host.c
new file mode 100644
index 000000000000..cd4d01087855
--- /dev/null
+++ b/drivers/usb/mtu3/mtu3_host.c
@@ -0,0 +1,294 @@
+/*
+ * mtu3_dr.c - dual role switch and host glue layer
+ *
+ * Copyright (C) 2016 MediaTek Inc.
+ *
+ * Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/iopoll.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+
+#include "mtu3.h"
+#include "mtu3_dr.h"
+
+#define PERI_WK_CTRL1 0x404
+#define UWK_CTL1_IS_C(x) (((x) & 0xf) << 26)
+#define UWK_CTL1_IS_E BIT(25)
+#define UWK_CTL1_IDDIG_C(x) (((x) & 0xf) << 11) /* cycle debounce */
+#define UWK_CTL1_IDDIG_E BIT(10) /* enable debounce */
+#define UWK_CTL1_IDDIG_P BIT(9) /* polarity */
+#define UWK_CTL1_IS_P BIT(6) /* polarity for ip sleep */
+
+/*
+ * ip-sleep wakeup mode:
+ * all clocks can be turn off, but power domain should be kept on
+ */
+static void ssusb_wakeup_ip_sleep_en(struct ssusb_mtk *ssusb)
+{
+ u32 tmp;
+ struct regmap *pericfg = ssusb->pericfg;
+
+ regmap_read(pericfg, PERI_WK_CTRL1, &tmp);
+ tmp &= ~UWK_CTL1_IS_P;
+ tmp &= ~(UWK_CTL1_IS_C(0xf));
+ tmp |= UWK_CTL1_IS_C(0x8);
+ regmap_write(pericfg, PERI_WK_CTRL1, tmp);
+ regmap_write(pericfg, PERI_WK_CTRL1, tmp | UWK_CTL1_IS_E);
+
+ regmap_read(pericfg, PERI_WK_CTRL1, &tmp);
+ dev_dbg(ssusb->dev, "%s(): WK_CTRL1[P6,E25,C26:29]=%#x\n",
+ __func__, tmp);
+}
+
+static void ssusb_wakeup_ip_sleep_dis(struct ssusb_mtk *ssusb)
+{
+ u32 tmp;
+
+ regmap_read(ssusb->pericfg, PERI_WK_CTRL1, &tmp);
+ tmp &= ~UWK_CTL1_IS_E;
+ regmap_write(ssusb->pericfg, PERI_WK_CTRL1, tmp);
+}
+
+int ssusb_wakeup_of_property_parse(struct ssusb_mtk *ssusb,
+ struct device_node *dn)
+{
+ struct device *dev = ssusb->dev;
+
+ /*
+ * Wakeup function is optional, so it is not an error if this property
+ * does not exist, and in such case, no need to get relative
+ * properties anymore.
+ */
+ ssusb->wakeup_en = of_property_read_bool(dn, "mediatek,enable-wakeup");
+ if (!ssusb->wakeup_en)
+ return 0;
+
+ ssusb->wk_deb_p0 = devm_clk_get(dev, "wakeup_deb_p0");
+ if (IS_ERR(ssusb->wk_deb_p0)) {
+ dev_err(dev, "fail to get wakeup_deb_p0\n");
+ return PTR_ERR(ssusb->wk_deb_p0);
+ }
+
+ if (of_property_read_bool(dn, "wakeup_deb_p1")) {
+ ssusb->wk_deb_p1 = devm_clk_get(dev, "wakeup_deb_p1");
+ if (IS_ERR(ssusb->wk_deb_p1)) {
+ dev_err(dev, "fail to get wakeup_deb_p1\n");
+ return PTR_ERR(ssusb->wk_deb_p1);
+ }
+ }
+
+ ssusb->pericfg = syscon_regmap_lookup_by_phandle(dn,
+ "mediatek,syscon-wakeup");
+ if (IS_ERR(ssusb->pericfg)) {
+ dev_err(dev, "fail to get pericfg regs\n");
+ return PTR_ERR(ssusb->pericfg);
+ }
+
+ return 0;
+}
+
+static int ssusb_wakeup_clks_enable(struct ssusb_mtk *ssusb)
+{
+ int ret;
+
+ ret = clk_prepare_enable(ssusb->wk_deb_p0);
+ if (ret) {
+ dev_err(ssusb->dev, "failed to enable wk_deb_p0\n");
+ goto usb_p0_err;
+ }
+
+ ret = clk_prepare_enable(ssusb->wk_deb_p1);
+ if (ret) {
+ dev_err(ssusb->dev, "failed to enable wk_deb_p1\n");
+ goto usb_p1_err;
+ }
+
+ return 0;
+
+usb_p1_err:
+ clk_disable_unprepare(ssusb->wk_deb_p0);
+usb_p0_err:
+ return -EINVAL;
+}
+
+static void ssusb_wakeup_clks_disable(struct ssusb_mtk *ssusb)
+{
+ clk_disable_unprepare(ssusb->wk_deb_p1);
+ clk_disable_unprepare(ssusb->wk_deb_p0);
+}
+
+static void host_ports_num_get(struct ssusb_mtk *ssusb)
+{
+ u32 xhci_cap;
+
+ xhci_cap = mtu3_readl(ssusb->ippc_base, U3D_SSUSB_IP_XHCI_CAP);
+ ssusb->u2_ports = SSUSB_IP_XHCI_U2_PORT_NUM(xhci_cap);
+ ssusb->u3_ports = SSUSB_IP_XHCI_U3_PORT_NUM(xhci_cap);
+
+ dev_dbg(ssusb->dev, "host - u2_ports:%d, u3_ports:%d\n",
+ ssusb->u2_ports, ssusb->u3_ports);
+}
+
+/* only configure ports will be used later */
+int ssusb_host_enable(struct ssusb_mtk *ssusb)
+{
+ void __iomem *ibase = ssusb->ippc_base;
+ int num_u3p = ssusb->u3_ports;
+ int num_u2p = ssusb->u2_ports;
+ u32 check_clk;
+ u32 value;
+ int i;
+
+ /* power on host ip */
+ mtu3_clrbits(ibase, U3D_SSUSB_IP_PW_CTRL1, SSUSB_IP_HOST_PDN);
+
+ /* power on and enable all u3 ports */
+ for (i = 0; i < num_u3p; i++) {
+ value = mtu3_readl(ibase, SSUSB_U3_CTRL(i));
+ value &= ~(SSUSB_U3_PORT_PDN | SSUSB_U3_PORT_DIS);
+ value |= SSUSB_U3_PORT_HOST_SEL;
+ mtu3_writel(ibase, SSUSB_U3_CTRL(i), value);
+ }
+
+ /* power on and enable all u2 ports */
+ for (i = 0; i < num_u2p; i++) {
+ value = mtu3_readl(ibase, SSUSB_U2_CTRL(i));
+ value &= ~(SSUSB_U2_PORT_PDN | SSUSB_U2_PORT_DIS);
+ value |= SSUSB_U2_PORT_HOST_SEL;
+ mtu3_writel(ibase, SSUSB_U2_CTRL(i), value);
+ }
+
+ check_clk = SSUSB_XHCI_RST_B_STS;
+ if (num_u3p)
+ check_clk = SSUSB_U3_MAC_RST_B_STS;
+
+ return ssusb_check_clocks(ssusb, check_clk);
+}
+
+int ssusb_host_disable(struct ssusb_mtk *ssusb, bool suspend)
+{
+ void __iomem *ibase = ssusb->ippc_base;
+ int num_u3p = ssusb->u3_ports;
+ int num_u2p = ssusb->u2_ports;
+ u32 value;
+ int ret;
+ int i;
+
+ /* power down and disable all u3 ports */
+ for (i = 0; i < num_u3p; i++) {
+ value = mtu3_readl(ibase, SSUSB_U3_CTRL(i));
+ value |= SSUSB_U3_PORT_PDN;
+ value |= suspend ? 0 : SSUSB_U3_PORT_DIS;
+ mtu3_writel(ibase, SSUSB_U3_CTRL(i), value);
+ }
+
+ /* power down and disable all u2 ports */
+ for (i = 0; i < num_u2p; i++) {
+ value = mtu3_readl(ibase, SSUSB_U2_CTRL(i));
+ value |= SSUSB_U2_PORT_PDN;
+ value |= suspend ? 0 : SSUSB_U2_PORT_DIS;
+ mtu3_writel(ibase, SSUSB_U2_CTRL(i), value);
+ }
+
+ /* power down host ip */
+ mtu3_setbits(ibase, U3D_SSUSB_IP_PW_CTRL1, SSUSB_IP_HOST_PDN);
+
+ if (!suspend)
+ return 0;
+
+ /* wait for host ip to sleep */
+ ret = readl_poll_timeout(ibase + U3D_SSUSB_IP_PW_STS1, value,
+ (value & SSUSB_IP_SLEEP_STS), 100, 100000);
+ if (ret)
+ dev_err(ssusb->dev, "ip sleep failed!!!\n");
+
+ return ret;
+}
+
+static void ssusb_host_setup(struct ssusb_mtk *ssusb)
+{
+ host_ports_num_get(ssusb);
+
+ /*
+ * power on host and power on/enable all ports
+ * if support OTG, gadget driver will switch port0 to device mode
+ */
+ ssusb_host_enable(ssusb);
+
+ /* if port0 supports dual-role, works as host mode by default */
+ ssusb_set_vbus(&ssusb->otg_switch, 1);
+}
+
+static void ssusb_host_cleanup(struct ssusb_mtk *ssusb)
+{
+ if (ssusb->is_host)
+ ssusb_set_vbus(&ssusb->otg_switch, 0);
+
+ ssusb_host_disable(ssusb, false);
+}
+
+/*
+ * If host supports multiple ports, the VBUSes(5V) of ports except port0
+ * which supports OTG are better to be enabled by default in DTS.
+ * Because the host driver will keep link with devices attached when system
+ * enters suspend mode, so no need to control VBUSes after initialization.
+ */
+int ssusb_host_init(struct ssusb_mtk *ssusb, struct device_node *parent_dn)
+{
+ struct device *parent_dev = ssusb->dev;
+ int ret;
+
+ ssusb_host_setup(ssusb);
+
+ ret = of_platform_populate(parent_dn, NULL, NULL, parent_dev);
+ if (ret) {
+ dev_dbg(parent_dev, "failed to create child devices at %s\n",
+ parent_dn->full_name);
+ return ret;
+ }
+
+ dev_info(parent_dev, "xHCI platform device register success...\n");
+
+ return 0;
+}
+
+void ssusb_host_exit(struct ssusb_mtk *ssusb)
+{
+ of_platform_depopulate(ssusb->dev);
+ ssusb_host_cleanup(ssusb);
+}
+
+int ssusb_wakeup_enable(struct ssusb_mtk *ssusb)
+{
+ int ret = 0;
+
+ if (ssusb->wakeup_en) {
+ ret = ssusb_wakeup_clks_enable(ssusb);
+ ssusb_wakeup_ip_sleep_en(ssusb);
+ }
+ return ret;
+}
+
+void ssusb_wakeup_disable(struct ssusb_mtk *ssusb)
+{
+ if (ssusb->wakeup_en) {
+ ssusb_wakeup_ip_sleep_dis(ssusb);
+ ssusb_wakeup_clks_disable(ssusb);
+ }
+}
diff --git a/drivers/usb/mtu3/mtu3_hw_regs.h b/drivers/usb/mtu3/mtu3_hw_regs.h
new file mode 100644
index 000000000000..212367295276
--- /dev/null
+++ b/drivers/usb/mtu3/mtu3_hw_regs.h
@@ -0,0 +1,473 @@
+/*
+ * mtu3_hw_regs.h - MediaTek USB3 DRD register and field definitions
+ *
+ * Copyright (C) 2016 MediaTek Inc.
+ *
+ * Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _SSUSB_HW_REGS_H_
+#define _SSUSB_HW_REGS_H_
+
+/* segment offset of MAC register */
+#define SSUSB_DEV_BASE 0x0000
+#define SSUSB_EPCTL_CSR_BASE 0x0800
+#define SSUSB_USB3_MAC_CSR_BASE 0x1400
+#define SSUSB_USB3_SYS_CSR_BASE 0x1400
+#define SSUSB_USB2_CSR_BASE 0x2400
+
+/* IPPC register in Infra */
+#define SSUSB_SIFSLV_IPPC_BASE 0x0000
+
+/* --------------- SSUSB_DEV REGISTER DEFINITION --------------- */
+
+#define U3D_LV1ISR (SSUSB_DEV_BASE + 0x0000)
+#define U3D_LV1IER (SSUSB_DEV_BASE + 0x0004)
+#define U3D_LV1IESR (SSUSB_DEV_BASE + 0x0008)
+#define U3D_LV1IECR (SSUSB_DEV_BASE + 0x000C)
+
+#define U3D_EPISR (SSUSB_DEV_BASE + 0x0080)
+#define U3D_EPIER (SSUSB_DEV_BASE + 0x0084)
+#define U3D_EPIESR (SSUSB_DEV_BASE + 0x0088)
+#define U3D_EPIECR (SSUSB_DEV_BASE + 0x008C)
+
+#define U3D_EP0CSR (SSUSB_DEV_BASE + 0x0100)
+#define U3D_RXCOUNT0 (SSUSB_DEV_BASE + 0x0108)
+#define U3D_RESERVED (SSUSB_DEV_BASE + 0x010C)
+#define U3D_TX1CSR0 (SSUSB_DEV_BASE + 0x0110)
+#define U3D_TX1CSR1 (SSUSB_DEV_BASE + 0x0114)
+#define U3D_TX1CSR2 (SSUSB_DEV_BASE + 0x0118)
+
+#define U3D_RX1CSR0 (SSUSB_DEV_BASE + 0x0210)
+#define U3D_RX1CSR1 (SSUSB_DEV_BASE + 0x0214)
+#define U3D_RX1CSR2 (SSUSB_DEV_BASE + 0x0218)
+
+#define U3D_FIFO0 (SSUSB_DEV_BASE + 0x0300)
+
+#define U3D_QCR0 (SSUSB_DEV_BASE + 0x0400)
+#define U3D_QCR1 (SSUSB_DEV_BASE + 0x0404)
+#define U3D_QCR2 (SSUSB_DEV_BASE + 0x0408)
+#define U3D_QCR3 (SSUSB_DEV_BASE + 0x040C)
+
+#define U3D_TXQCSR1 (SSUSB_DEV_BASE + 0x0510)
+#define U3D_TXQSAR1 (SSUSB_DEV_BASE + 0x0514)
+#define U3D_TXQCPR1 (SSUSB_DEV_BASE + 0x0518)
+
+#define U3D_RXQCSR1 (SSUSB_DEV_BASE + 0x0610)
+#define U3D_RXQSAR1 (SSUSB_DEV_BASE + 0x0614)
+#define U3D_RXQCPR1 (SSUSB_DEV_BASE + 0x0618)
+#define U3D_RXQLDPR1 (SSUSB_DEV_BASE + 0x061C)
+
+#define U3D_QISAR0 (SSUSB_DEV_BASE + 0x0700)
+#define U3D_QIER0 (SSUSB_DEV_BASE + 0x0704)
+#define U3D_QIESR0 (SSUSB_DEV_BASE + 0x0708)
+#define U3D_QIECR0 (SSUSB_DEV_BASE + 0x070C)
+#define U3D_QISAR1 (SSUSB_DEV_BASE + 0x0710)
+#define U3D_QIER1 (SSUSB_DEV_BASE + 0x0714)
+#define U3D_QIESR1 (SSUSB_DEV_BASE + 0x0718)
+#define U3D_QIECR1 (SSUSB_DEV_BASE + 0x071C)
+
+#define U3D_TQERRIR0 (SSUSB_DEV_BASE + 0x0780)
+#define U3D_TQERRIER0 (SSUSB_DEV_BASE + 0x0784)
+#define U3D_TQERRIESR0 (SSUSB_DEV_BASE + 0x0788)
+#define U3D_TQERRIECR0 (SSUSB_DEV_BASE + 0x078C)
+#define U3D_RQERRIR0 (SSUSB_DEV_BASE + 0x07C0)
+#define U3D_RQERRIER0 (SSUSB_DEV_BASE + 0x07C4)
+#define U3D_RQERRIESR0 (SSUSB_DEV_BASE + 0x07C8)
+#define U3D_RQERRIECR0 (SSUSB_DEV_BASE + 0x07CC)
+#define U3D_RQERRIR1 (SSUSB_DEV_BASE + 0x07D0)
+#define U3D_RQERRIER1 (SSUSB_DEV_BASE + 0x07D4)
+#define U3D_RQERRIESR1 (SSUSB_DEV_BASE + 0x07D8)
+#define U3D_RQERRIECR1 (SSUSB_DEV_BASE + 0x07DC)
+
+#define U3D_CAP_EP0FFSZ (SSUSB_DEV_BASE + 0x0C04)
+#define U3D_CAP_EPNTXFFSZ (SSUSB_DEV_BASE + 0x0C08)
+#define U3D_CAP_EPNRXFFSZ (SSUSB_DEV_BASE + 0x0C0C)
+#define U3D_CAP_EPINFO (SSUSB_DEV_BASE + 0x0C10)
+#define U3D_MISC_CTRL (SSUSB_DEV_BASE + 0x0C84)
+
+/*---------------- SSUSB_DEV FIELD DEFINITION ---------------*/
+
+/* U3D_LV1ISR */
+#define EP_CTRL_INTR BIT(5)
+#define MAC2_INTR BIT(4)
+#define DMA_INTR BIT(3)
+#define MAC3_INTR BIT(2)
+#define QMU_INTR BIT(1)
+#define BMU_INTR BIT(0)
+
+/* U3D_LV1IECR */
+#define LV1IECR_MSK GENMASK(31, 0)
+
+/* U3D_EPISR */
+#define EPRISR(x) (BIT(16) << (x))
+#define EPTISR(x) (BIT(0) << (x))
+#define EP0ISR BIT(0)
+
+/* U3D_EP0CSR */
+#define EP0_SENDSTALL BIT(25)
+#define EP0_FIFOFULL BIT(23)
+#define EP0_SENTSTALL BIT(22)
+#define EP0_DPHTX BIT(20)
+#define EP0_DATAEND BIT(19)
+#define EP0_TXPKTRDY BIT(18)
+#define EP0_SETUPPKTRDY BIT(17)
+#define EP0_RXPKTRDY BIT(16)
+#define EP0_MAXPKTSZ_MSK GENMASK(9, 0)
+#define EP0_MAXPKTSZ(x) ((x) & EP0_MAXPKTSZ_MSK)
+#define EP0_W1C_BITS (~(EP0_RXPKTRDY | EP0_SETUPPKTRDY | EP0_SENTSTALL))
+
+/* U3D_TX1CSR0 */
+#define TX_DMAREQEN BIT(29)
+#define TX_FIFOFULL BIT(25)
+#define TX_FIFOEMPTY BIT(24)
+#define TX_SENTSTALL BIT(22)
+#define TX_SENDSTALL BIT(21)
+#define TX_TXPKTRDY BIT(16)
+#define TX_TXMAXPKTSZ_MSK GENMASK(10, 0)
+#define TX_TXMAXPKTSZ(x) ((x) & TX_TXMAXPKTSZ_MSK)
+#define TX_W1C_BITS (~(TX_SENTSTALL))
+
+/* U3D_TX1CSR1 */
+#define TX_MULT(x) (((x) & 0x3) << 22)
+#define TX_MAX_PKT(x) (((x) & 0x3f) << 16)
+#define TX_SLOT(x) (((x) & 0x3f) << 8)
+#define TX_TYPE(x) (((x) & 0x3) << 4)
+#define TX_SS_BURST(x) (((x) & 0xf) << 0)
+
+/* for TX_TYPE & RX_TYPE */
+#define TYPE_BULK (0x0)
+#define TYPE_INT (0x1)
+#define TYPE_ISO (0x2)
+#define TYPE_MASK (0x3)
+
+/* U3D_TX1CSR2 */
+#define TX_BINTERVAL(x) (((x) & 0xff) << 24)
+#define TX_FIFOSEGSIZE(x) (((x) & 0xf) << 16)
+#define TX_FIFOADDR(x) (((x) & 0x1fff) << 0)
+
+/* U3D_RX1CSR0 */
+#define RX_DMAREQEN BIT(29)
+#define RX_SENTSTALL BIT(22)
+#define RX_SENDSTALL BIT(21)
+#define RX_RXPKTRDY BIT(16)
+#define RX_RXMAXPKTSZ_MSK GENMASK(10, 0)
+#define RX_RXMAXPKTSZ(x) ((x) & RX_RXMAXPKTSZ_MSK)
+#define RX_W1C_BITS (~(RX_SENTSTALL | RX_RXPKTRDY))
+
+/* U3D_RX1CSR1 */
+#define RX_MULT(x) (((x) & 0x3) << 22)
+#define RX_MAX_PKT(x) (((x) & 0x3f) << 16)
+#define RX_SLOT(x) (((x) & 0x3f) << 8)
+#define RX_TYPE(x) (((x) & 0x3) << 4)
+#define RX_SS_BURST(x) (((x) & 0xf) << 0)
+
+/* U3D_RX1CSR2 */
+#define RX_BINTERVAL(x) (((x) & 0xff) << 24)
+#define RX_FIFOSEGSIZE(x) (((x) & 0xf) << 16)
+#define RX_FIFOADDR(x) (((x) & 0x1fff) << 0)
+
+/* U3D_QCR0 */
+#define QMU_RX_CS_EN(x) (BIT(16) << (x))
+#define QMU_TX_CS_EN(x) (BIT(0) << (x))
+#define QMU_CS16B_EN BIT(0)
+
+/* U3D_QCR1 */
+#define QMU_TX_ZLP(x) (BIT(0) << (x))
+
+/* U3D_QCR3 */
+#define QMU_RX_COZ(x) (BIT(16) << (x))
+#define QMU_RX_ZLP(x) (BIT(0) << (x))
+
+/* U3D_TXQCSR1 */
+/* U3D_RXQCSR1 */
+#define QMU_Q_ACTIVE BIT(15)
+#define QMU_Q_STOP BIT(2)
+#define QMU_Q_RESUME BIT(1)
+#define QMU_Q_START BIT(0)
+
+/* U3D_QISAR0, U3D_QIER0, U3D_QIESR0, U3D_QIECR0 */
+#define QMU_RX_DONE_INT(x) (BIT(16) << (x))
+#define QMU_TX_DONE_INT(x) (BIT(0) << (x))
+
+/* U3D_QISAR1, U3D_QIER1, U3D_QIESR1, U3D_QIECR1 */
+#define RXQ_ZLPERR_INT BIT(20)
+#define RXQ_LENERR_INT BIT(18)
+#define RXQ_CSERR_INT BIT(17)
+#define RXQ_EMPTY_INT BIT(16)
+#define TXQ_LENERR_INT BIT(2)
+#define TXQ_CSERR_INT BIT(1)
+#define TXQ_EMPTY_INT BIT(0)
+
+/* U3D_TQERRIR0, U3D_TQERRIER0, U3D_TQERRIESR0, U3D_TQERRIECR0 */
+#define QMU_TX_LEN_ERR(x) (BIT(16) << (x))
+#define QMU_TX_CS_ERR(x) (BIT(0) << (x))
+
+/* U3D_RQERRIR0, U3D_RQERRIER0, U3D_RQERRIESR0, U3D_RQERRIECR0 */
+#define QMU_RX_LEN_ERR(x) (BIT(16) << (x))
+#define QMU_RX_CS_ERR(x) (BIT(0) << (x))
+
+/* U3D_RQERRIR1, U3D_RQERRIER1, U3D_RQERRIESR1, U3D_RQERRIECR1 */
+#define QMU_RX_ZLP_ERR(n) (BIT(16) << (n))
+
+/* U3D_CAP_EPINFO */
+#define CAP_RX_EP_NUM(x) (((x) >> 8) & 0x1f)
+#define CAP_TX_EP_NUM(x) ((x) & 0x1f)
+
+/* U3D_MISC_CTRL */
+#define VBUS_ON BIT(1)
+#define VBUS_FRC_EN BIT(0)
+
+
+/*---------------- SSUSB_EPCTL_CSR REGISTER DEFINITION ----------------*/
+
+#define U3D_DEVICE_CONF (SSUSB_EPCTL_CSR_BASE + 0x0000)
+#define U3D_EP_RST (SSUSB_EPCTL_CSR_BASE + 0x0004)
+
+#define U3D_DEV_LINK_INTR_ENABLE (SSUSB_EPCTL_CSR_BASE + 0x0050)
+#define U3D_DEV_LINK_INTR (SSUSB_EPCTL_CSR_BASE + 0x0054)
+
+/*---------------- SSUSB_EPCTL_CSR FIELD DEFINITION ----------------*/
+
+/* U3D_DEVICE_CONF */
+#define DEV_ADDR_MSK GENMASK(30, 24)
+#define DEV_ADDR(x) ((0x7f & (x)) << 24)
+#define HW_USB2_3_SEL BIT(18)
+#define SW_USB2_3_SEL_EN BIT(17)
+#define SW_USB2_3_SEL BIT(16)
+#define SSUSB_DEV_SPEED(x) ((x) & 0x7)
+
+/* U3D_EP_RST */
+#define EP1_IN_RST BIT(17)
+#define EP1_OUT_RST BIT(1)
+#define EP_RST(is_in, epnum) (((is_in) ? BIT(16) : BIT(0)) << (epnum))
+#define EP0_RST BIT(0)
+
+/* U3D_DEV_LINK_INTR_ENABLE */
+/* U3D_DEV_LINK_INTR */
+#define SSUSB_DEV_SPEED_CHG_INTR BIT(0)
+
+
+/*---------------- SSUSB_USB3_MAC_CSR REGISTER DEFINITION ----------------*/
+
+#define U3D_LTSSM_CTRL (SSUSB_USB3_MAC_CSR_BASE + 0x0010)
+#define U3D_USB3_CONFIG (SSUSB_USB3_MAC_CSR_BASE + 0x001C)
+
+#define U3D_LTSSM_INTR_ENABLE (SSUSB_USB3_MAC_CSR_BASE + 0x013C)
+#define U3D_LTSSM_INTR (SSUSB_USB3_MAC_CSR_BASE + 0x0140)
+
+/*---------------- SSUSB_USB3_MAC_CSR FIELD DEFINITION ----------------*/
+
+/* U3D_LTSSM_CTRL */
+#define FORCE_POLLING_FAIL BIT(4)
+#define FORCE_RXDETECT_FAIL BIT(3)
+#define SOFT_U3_EXIT_EN BIT(2)
+#define COMPLIANCE_EN BIT(1)
+#define U1_GO_U2_EN BIT(0)
+
+/* U3D_USB3_CONFIG */
+#define USB3_EN BIT(0)
+
+/* U3D_LTSSM_INTR_ENABLE */
+/* U3D_LTSSM_INTR */
+#define U3_RESUME_INTR BIT(18)
+#define U3_LFPS_TMOUT_INTR BIT(17)
+#define VBUS_FALL_INTR BIT(16)
+#define VBUS_RISE_INTR BIT(15)
+#define RXDET_SUCCESS_INTR BIT(14)
+#define EXIT_U3_INTR BIT(13)
+#define EXIT_U2_INTR BIT(12)
+#define EXIT_U1_INTR BIT(11)
+#define ENTER_U3_INTR BIT(10)
+#define ENTER_U2_INTR BIT(9)
+#define ENTER_U1_INTR BIT(8)
+#define ENTER_U0_INTR BIT(7)
+#define RECOVERY_INTR BIT(6)
+#define WARM_RST_INTR BIT(5)
+#define HOT_RST_INTR BIT(4)
+#define LOOPBACK_INTR BIT(3)
+#define COMPLIANCE_INTR BIT(2)
+#define SS_DISABLE_INTR BIT(1)
+#define SS_INACTIVE_INTR BIT(0)
+
+/*---------------- SSUSB_USB3_SYS_CSR REGISTER DEFINITION ----------------*/
+
+#define U3D_LINK_UX_INACT_TIMER (SSUSB_USB3_SYS_CSR_BASE + 0x020C)
+#define U3D_LINK_POWER_CONTROL (SSUSB_USB3_SYS_CSR_BASE + 0x0210)
+#define U3D_LINK_ERR_COUNT (SSUSB_USB3_SYS_CSR_BASE + 0x0214)
+
+/*---------------- SSUSB_USB3_SYS_CSR FIELD DEFINITION ----------------*/
+
+/* U3D_LINK_UX_INACT_TIMER */
+#define DEV_U2_INACT_TIMEOUT_MSK GENMASK(23, 16)
+#define DEV_U2_INACT_TIMEOUT_VALUE(x) (((x) & 0xff) << 16)
+#define U2_INACT_TIMEOUT_MSK GENMASK(15, 8)
+#define U1_INACT_TIMEOUT_MSK GENMASK(7, 0)
+#define U1_INACT_TIMEOUT_VALUE(x) ((x) & 0xff)
+
+/* U3D_LINK_POWER_CONTROL */
+#define SW_U2_ACCEPT_ENABLE BIT(9)
+#define SW_U1_ACCEPT_ENABLE BIT(8)
+#define UX_EXIT BIT(5)
+#define LGO_U3 BIT(4)
+#define LGO_U2 BIT(3)
+#define LGO_U1 BIT(2)
+#define SW_U2_REQUEST_ENABLE BIT(1)
+#define SW_U1_REQUEST_ENABLE BIT(0)
+
+/* U3D_LINK_ERR_COUNT */
+#define CLR_LINK_ERR_CNT BIT(16)
+#define LINK_ERROR_COUNT GENMASK(15, 0)
+
+/*---------------- SSUSB_USB2_CSR REGISTER DEFINITION ----------------*/
+
+#define U3D_POWER_MANAGEMENT (SSUSB_USB2_CSR_BASE + 0x0004)
+#define U3D_DEVICE_CONTROL (SSUSB_USB2_CSR_BASE + 0x000C)
+#define U3D_USB2_TEST_MODE (SSUSB_USB2_CSR_BASE + 0x0014)
+#define U3D_COMMON_USB_INTR_ENABLE (SSUSB_USB2_CSR_BASE + 0x0018)
+#define U3D_COMMON_USB_INTR (SSUSB_USB2_CSR_BASE + 0x001C)
+#define U3D_LINK_RESET_INFO (SSUSB_USB2_CSR_BASE + 0x0024)
+#define U3D_USB20_FRAME_NUM (SSUSB_USB2_CSR_BASE + 0x003C)
+#define U3D_USB20_LPM_PARAMETER (SSUSB_USB2_CSR_BASE + 0x0044)
+#define U3D_USB20_MISC_CONTROL (SSUSB_USB2_CSR_BASE + 0x004C)
+
+/*---------------- SSUSB_USB2_CSR FIELD DEFINITION ----------------*/
+
+/* U3D_POWER_MANAGEMENT */
+#define LPM_BESL_STALL BIT(14)
+#define LPM_BESLD_STALL BIT(13)
+#define LPM_RWP BIT(11)
+#define LPM_HRWE BIT(10)
+#define LPM_MODE(x) (((x) & 0x3) << 8)
+#define ISO_UPDATE BIT(7)
+#define SOFT_CONN BIT(6)
+#define HS_ENABLE BIT(5)
+#define RESUME BIT(2)
+#define SUSPENDM_ENABLE BIT(0)
+
+/* U3D_DEVICE_CONTROL */
+#define DC_HOSTREQ BIT(1)
+#define DC_SESSION BIT(0)
+
+/* U3D_USB2_TEST_MODE */
+#define U2U3_AUTO_SWITCH BIT(10)
+#define LPM_FORCE_STALL BIT(8)
+#define FIFO_ACCESS BIT(6)
+#define FORCE_FS BIT(5)
+#define FORCE_HS BIT(4)
+#define TEST_PACKET_MODE BIT(3)
+#define TEST_K_MODE BIT(2)
+#define TEST_J_MODE BIT(1)
+#define TEST_SE0_NAK_MODE BIT(0)
+
+/* U3D_COMMON_USB_INTR_ENABLE */
+/* U3D_COMMON_USB_INTR */
+#define LPM_RESUME_INTR BIT(9)
+#define LPM_INTR BIT(8)
+#define DISCONN_INTR BIT(5)
+#define CONN_INTR BIT(4)
+#define SOF_INTR BIT(3)
+#define RESET_INTR BIT(2)
+#define RESUME_INTR BIT(1)
+#define SUSPEND_INTR BIT(0)
+
+/* U3D_LINK_RESET_INFO */
+#define WTCHRP_MSK GENMASK(19, 16)
+
+/* U3D_USB20_LPM_PARAMETER */
+#define LPM_BESLCK_U3(x) (((x) & 0xf) << 12)
+#define LPM_BESLCK(x) (((x) & 0xf) << 8)
+#define LPM_BESLDCK(x) (((x) & 0xf) << 4)
+#define LPM_BESL GENMASK(3, 0)
+
+/* U3D_USB20_MISC_CONTROL */
+#define LPM_U3_ACK_EN BIT(0)
+
+/*---------------- SSUSB_SIFSLV_IPPC REGISTER DEFINITION ----------------*/
+
+#define U3D_SSUSB_IP_PW_CTRL0 (SSUSB_SIFSLV_IPPC_BASE + 0x0000)
+#define U3D_SSUSB_IP_PW_CTRL1 (SSUSB_SIFSLV_IPPC_BASE + 0x0004)
+#define U3D_SSUSB_IP_PW_CTRL2 (SSUSB_SIFSLV_IPPC_BASE + 0x0008)
+#define U3D_SSUSB_IP_PW_CTRL3 (SSUSB_SIFSLV_IPPC_BASE + 0x000C)
+#define U3D_SSUSB_IP_PW_STS1 (SSUSB_SIFSLV_IPPC_BASE + 0x0010)
+#define U3D_SSUSB_IP_PW_STS2 (SSUSB_SIFSLV_IPPC_BASE + 0x0014)
+#define U3D_SSUSB_OTG_STS (SSUSB_SIFSLV_IPPC_BASE + 0x0018)
+#define U3D_SSUSB_OTG_STS_CLR (SSUSB_SIFSLV_IPPC_BASE + 0x001C)
+#define U3D_SSUSB_IP_XHCI_CAP (SSUSB_SIFSLV_IPPC_BASE + 0x0024)
+#define U3D_SSUSB_IP_DEV_CAP (SSUSB_SIFSLV_IPPC_BASE + 0x0028)
+#define U3D_SSUSB_OTG_INT_EN (SSUSB_SIFSLV_IPPC_BASE + 0x002C)
+#define U3D_SSUSB_U3_CTRL_0P (SSUSB_SIFSLV_IPPC_BASE + 0x0030)
+#define U3D_SSUSB_U2_CTRL_0P (SSUSB_SIFSLV_IPPC_BASE + 0x0050)
+#define U3D_SSUSB_REF_CK_CTRL (SSUSB_SIFSLV_IPPC_BASE + 0x008C)
+#define U3D_SSUSB_DEV_RST_CTRL (SSUSB_SIFSLV_IPPC_BASE + 0x0098)
+#define U3D_SSUSB_HW_ID (SSUSB_SIFSLV_IPPC_BASE + 0x00A0)
+#define U3D_SSUSB_HW_SUB_ID (SSUSB_SIFSLV_IPPC_BASE + 0x00A4)
+#define U3D_SSUSB_IP_SPARE0 (SSUSB_SIFSLV_IPPC_BASE + 0x00C8)
+
+/*---------------- SSUSB_SIFSLV_IPPC FIELD DEFINITION ----------------*/
+
+/* U3D_SSUSB_IP_PW_CTRL0 */
+#define SSUSB_IP_SW_RST BIT(0)
+
+/* U3D_SSUSB_IP_PW_CTRL1 */
+#define SSUSB_IP_HOST_PDN BIT(0)
+
+/* U3D_SSUSB_IP_PW_CTRL2 */
+#define SSUSB_IP_DEV_PDN BIT(0)
+
+/* U3D_SSUSB_IP_PW_CTRL3 */
+#define SSUSB_IP_PCIE_PDN BIT(0)
+
+/* U3D_SSUSB_IP_PW_STS1 */
+#define SSUSB_IP_SLEEP_STS BIT(30)
+#define SSUSB_U3_MAC_RST_B_STS BIT(16)
+#define SSUSB_XHCI_RST_B_STS BIT(11)
+#define SSUSB_SYS125_RST_B_STS BIT(10)
+#define SSUSB_REF_RST_B_STS BIT(8)
+#define SSUSB_SYSPLL_STABLE BIT(0)
+
+/* U3D_SSUSB_IP_PW_STS2 */
+#define SSUSB_U2_MAC_SYS_RST_B_STS BIT(0)
+
+/* U3D_SSUSB_OTG_STS */
+#define SSUSB_VBUS_VALID BIT(9)
+
+/* U3D_SSUSB_OTG_STS_CLR */
+#define SSUSB_VBUS_INTR_CLR BIT(6)
+
+/* U3D_SSUSB_IP_XHCI_CAP */
+#define SSUSB_IP_XHCI_U2_PORT_NUM(x) (((x) >> 8) & 0xff)
+#define SSUSB_IP_XHCI_U3_PORT_NUM(x) ((x) & 0xff)
+
+/* U3D_SSUSB_IP_DEV_CAP */
+#define SSUSB_IP_DEV_U3_PORT_NUM(x) ((x) & 0xff)
+
+/* U3D_SSUSB_OTG_INT_EN */
+#define SSUSB_VBUS_CHG_INT_A_EN BIT(7)
+#define SSUSB_VBUS_CHG_INT_B_EN BIT(6)
+
+/* U3D_SSUSB_U3_CTRL_0P */
+#define SSUSB_U3_PORT_HOST_SEL BIT(2)
+#define SSUSB_U3_PORT_PDN BIT(1)
+#define SSUSB_U3_PORT_DIS BIT(0)
+
+/* U3D_SSUSB_U2_CTRL_0P */
+#define SSUSB_U2_PORT_OTG_SEL BIT(7)
+#define SSUSB_U2_PORT_HOST_SEL BIT(2)
+#define SSUSB_U2_PORT_PDN BIT(1)
+#define SSUSB_U2_PORT_DIS BIT(0)
+
+/* U3D_SSUSB_DEV_RST_CTRL */
+#define SSUSB_DEV_SW_RST BIT(0)
+
+#endif /* _SSUSB_HW_REGS_H_ */
diff --git a/drivers/usb/mtu3/mtu3_plat.c b/drivers/usb/mtu3/mtu3_plat.c
new file mode 100644
index 000000000000..42550c7db3e7
--- /dev/null
+++ b/drivers/usb/mtu3/mtu3_plat.c
@@ -0,0 +1,506 @@
+/*
+ * Copyright (C) 2016 MediaTek Inc.
+ *
+ * Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+
+#include "mtu3.h"
+#include "mtu3_dr.h"
+
+/* u2-port0 should be powered on and enabled; */
+int ssusb_check_clocks(struct ssusb_mtk *ssusb, u32 ex_clks)
+{
+ void __iomem *ibase = ssusb->ippc_base;
+ u32 value, check_val;
+ int ret;
+
+ check_val = ex_clks | SSUSB_SYS125_RST_B_STS | SSUSB_SYSPLL_STABLE |
+ SSUSB_REF_RST_B_STS;
+
+ ret = readl_poll_timeout(ibase + U3D_SSUSB_IP_PW_STS1, value,
+ (check_val == (value & check_val)), 100, 20000);
+ if (ret) {
+ dev_err(ssusb->dev, "clks of sts1 are not stable!\n");
+ return ret;
+ }
+
+ ret = readl_poll_timeout(ibase + U3D_SSUSB_IP_PW_STS2, value,
+ (value & SSUSB_U2_MAC_SYS_RST_B_STS), 100, 10000);
+ if (ret) {
+ dev_err(ssusb->dev, "mac2 clock is not stable\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ssusb_phy_init(struct ssusb_mtk *ssusb)
+{
+ int i;
+ int ret;
+
+ for (i = 0; i < ssusb->num_phys; i++) {
+ ret = phy_init(ssusb->phys[i]);
+ if (ret)
+ goto exit_phy;
+ }
+ return 0;
+
+exit_phy:
+ for (; i > 0; i--)
+ phy_exit(ssusb->phys[i - 1]);
+
+ return ret;
+}
+
+static int ssusb_phy_exit(struct ssusb_mtk *ssusb)
+{
+ int i;
+
+ for (i = 0; i < ssusb->num_phys; i++)
+ phy_exit(ssusb->phys[i]);
+
+ return 0;
+}
+
+static int ssusb_phy_power_on(struct ssusb_mtk *ssusb)
+{
+ int i;
+ int ret;
+
+ for (i = 0; i < ssusb->num_phys; i++) {
+ ret = phy_power_on(ssusb->phys[i]);
+ if (ret)
+ goto power_off_phy;
+ }
+ return 0;
+
+power_off_phy:
+ for (; i > 0; i--)
+ phy_power_off(ssusb->phys[i - 1]);
+
+ return ret;
+}
+
+static void ssusb_phy_power_off(struct ssusb_mtk *ssusb)
+{
+ unsigned int i;
+
+ for (i = 0; i < ssusb->num_phys; i++)
+ phy_power_off(ssusb->phys[i]);
+}
+
+static int ssusb_rscs_init(struct ssusb_mtk *ssusb)
+{
+ int ret = 0;
+
+ ret = regulator_enable(ssusb->vusb33);
+ if (ret) {
+ dev_err(ssusb->dev, "failed to enable vusb33\n");
+ goto vusb33_err;
+ }
+
+ ret = clk_prepare_enable(ssusb->sys_clk);
+ if (ret) {
+ dev_err(ssusb->dev, "failed to enable sys_clk\n");
+ goto sys_clk_err;
+ }
+
+ ret = clk_prepare_enable(ssusb->ref_clk);
+ if (ret) {
+ dev_err(ssusb->dev, "failed to enable ref_clk\n");
+ goto ref_clk_err;
+ }
+
+ ret = ssusb_phy_init(ssusb);
+ if (ret) {
+ dev_err(ssusb->dev, "failed to init phy\n");
+ goto phy_init_err;
+ }
+
+ ret = ssusb_phy_power_on(ssusb);
+ if (ret) {
+ dev_err(ssusb->dev, "failed to power on phy\n");
+ goto phy_err;
+ }
+
+ return 0;
+
+phy_err:
+ ssusb_phy_exit(ssusb);
+phy_init_err:
+ clk_disable_unprepare(ssusb->ref_clk);
+ref_clk_err:
+ clk_disable_unprepare(ssusb->sys_clk);
+sys_clk_err:
+ regulator_disable(ssusb->vusb33);
+vusb33_err:
+
+ return ret;
+}
+
+static void ssusb_rscs_exit(struct ssusb_mtk *ssusb)
+{
+ clk_disable_unprepare(ssusb->sys_clk);
+ clk_disable_unprepare(ssusb->ref_clk);
+ regulator_disable(ssusb->vusb33);
+ ssusb_phy_power_off(ssusb);
+ ssusb_phy_exit(ssusb);
+}
+
+static void ssusb_ip_sw_reset(struct ssusb_mtk *ssusb)
+{
+ /* reset whole ip (xhci & u3d) */
+ mtu3_setbits(ssusb->ippc_base, U3D_SSUSB_IP_PW_CTRL0, SSUSB_IP_SW_RST);
+ udelay(1);
+ mtu3_clrbits(ssusb->ippc_base, U3D_SSUSB_IP_PW_CTRL0, SSUSB_IP_SW_RST);
+}
+
+static int get_iddig_pinctrl(struct ssusb_mtk *ssusb)
+{
+ struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
+
+ otg_sx->id_pinctrl = devm_pinctrl_get(ssusb->dev);
+ if (IS_ERR(otg_sx->id_pinctrl)) {
+ dev_err(ssusb->dev, "Cannot find id pinctrl!\n");
+ return PTR_ERR(otg_sx->id_pinctrl);
+ }
+
+ otg_sx->id_float =
+ pinctrl_lookup_state(otg_sx->id_pinctrl, "id_float");
+ if (IS_ERR(otg_sx->id_float)) {
+ dev_err(ssusb->dev, "Cannot find pinctrl id_float!\n");
+ return PTR_ERR(otg_sx->id_float);
+ }
+
+ otg_sx->id_ground =
+ pinctrl_lookup_state(otg_sx->id_pinctrl, "id_ground");
+ if (IS_ERR(otg_sx->id_ground)) {
+ dev_err(ssusb->dev, "Cannot find pinctrl id_ground!\n");
+ return PTR_ERR(otg_sx->id_ground);
+ }
+
+ return 0;
+}
+
+static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
+ struct device *dev = &pdev->dev;
+ struct regulator *vbus;
+ struct resource *res;
+ int i;
+ int ret;
+
+ ssusb->vusb33 = devm_regulator_get(&pdev->dev, "vusb33");
+ if (IS_ERR(ssusb->vusb33)) {
+ dev_err(dev, "failed to get vusb33\n");
+ return PTR_ERR(ssusb->vusb33);
+ }
+
+ ssusb->sys_clk = devm_clk_get(dev, "sys_ck");
+ if (IS_ERR(ssusb->sys_clk)) {
+ dev_err(dev, "failed to get sys clock\n");
+ return PTR_ERR(ssusb->sys_clk);
+ }
+
+ /*
+ * reference clock is usually a "fixed-clock", make it optional
+ * for backward compatibility and ignore the error if it does
+ * not exist.
+ */
+ ssusb->ref_clk = devm_clk_get(dev, "ref_ck");
+ if (IS_ERR(ssusb->ref_clk)) {
+ if (PTR_ERR(ssusb->ref_clk) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ ssusb->ref_clk = NULL;
+ }
+
+ ssusb->num_phys = of_count_phandle_with_args(node,
+ "phys", "#phy-cells");
+ if (ssusb->num_phys > 0) {
+ ssusb->phys = devm_kcalloc(dev, ssusb->num_phys,
+ sizeof(*ssusb->phys), GFP_KERNEL);
+ if (!ssusb->phys)
+ return -ENOMEM;
+ } else {
+ ssusb->num_phys = 0;
+ }
+
+ for (i = 0; i < ssusb->num_phys; i++) {
+ ssusb->phys[i] = devm_of_phy_get_by_index(dev, node, i);
+ if (IS_ERR(ssusb->phys[i])) {
+ dev_err(dev, "failed to get phy-%d\n", i);
+ return PTR_ERR(ssusb->phys[i]);
+ }
+ }
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ippc");
+ ssusb->ippc_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(ssusb->ippc_base))
+ return PTR_ERR(ssusb->ippc_base);
+
+ ssusb->dr_mode = usb_get_dr_mode(dev);
+ if (ssusb->dr_mode == USB_DR_MODE_UNKNOWN) {
+ dev_err(dev, "dr_mode is error\n");
+ return -EINVAL;
+ }
+
+ if (ssusb->dr_mode == USB_DR_MODE_PERIPHERAL)
+ return 0;
+
+ /* if host role is supported */
+ ret = ssusb_wakeup_of_property_parse(ssusb, node);
+ if (ret)
+ return ret;
+
+ if (ssusb->dr_mode != USB_DR_MODE_OTG)
+ return 0;
+
+ /* if dual-role mode is supported */
+ vbus = devm_regulator_get(&pdev->dev, "vbus");
+ if (IS_ERR(vbus)) {
+ dev_err(dev, "failed to get vbus\n");
+ return PTR_ERR(vbus);
+ }
+ otg_sx->vbus = vbus;
+
+ otg_sx->is_u3_drd = of_property_read_bool(node, "mediatek,usb3-drd");
+ otg_sx->manual_drd_enabled =
+ of_property_read_bool(node, "enable-manual-drd");
+
+ if (of_property_read_bool(node, "extcon")) {
+ otg_sx->edev = extcon_get_edev_by_phandle(ssusb->dev, 0);
+ if (IS_ERR(otg_sx->edev)) {
+ dev_err(ssusb->dev, "couldn't get extcon device\n");
+ return -EPROBE_DEFER;
+ }
+ if (otg_sx->manual_drd_enabled) {
+ ret = get_iddig_pinctrl(ssusb);
+ if (ret)
+ return ret;
+ }
+ }
+
+ dev_info(dev, "dr_mode: %d, is_u3_dr: %d\n",
+ ssusb->dr_mode, otg_sx->is_u3_drd);
+
+ return 0;
+}
+
+static int mtu3_probe(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
+ struct ssusb_mtk *ssusb;
+ int ret = -ENOMEM;
+
+ /* all elements are set to ZERO as default value */
+ ssusb = devm_kzalloc(dev, sizeof(*ssusb), GFP_KERNEL);
+ if (!ssusb)
+ return -ENOMEM;
+
+ ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
+ if (ret) {
+ dev_err(dev, "No suitable DMA config available\n");
+ return -ENOTSUPP;
+ }
+
+ platform_set_drvdata(pdev, ssusb);
+ ssusb->dev = dev;
+
+ ret = get_ssusb_rscs(pdev, ssusb);
+ if (ret)
+ return ret;
+
+ /* enable power domain */
+ pm_runtime_enable(dev);
+ pm_runtime_get_sync(dev);
+ device_enable_async_suspend(dev);
+
+ ret = ssusb_rscs_init(ssusb);
+ if (ret)
+ goto comm_init_err;
+
+ ssusb_ip_sw_reset(ssusb);
+
+ if (IS_ENABLED(CONFIG_USB_MTU3_HOST))
+ ssusb->dr_mode = USB_DR_MODE_HOST;
+ else if (IS_ENABLED(CONFIG_USB_MTU3_GADGET))
+ ssusb->dr_mode = USB_DR_MODE_PERIPHERAL;
+
+ /* default as host */
+ ssusb->is_host = !(ssusb->dr_mode == USB_DR_MODE_PERIPHERAL);
+
+ switch (ssusb->dr_mode) {
+ case USB_DR_MODE_PERIPHERAL:
+ ret = ssusb_gadget_init(ssusb);
+ if (ret) {
+ dev_err(dev, "failed to initialize gadget\n");
+ goto comm_exit;
+ }
+ break;
+ case USB_DR_MODE_HOST:
+ ret = ssusb_host_init(ssusb, node);
+ if (ret) {
+ dev_err(dev, "failed to initialize host\n");
+ goto comm_exit;
+ }
+ break;
+ case USB_DR_MODE_OTG:
+ ret = ssusb_gadget_init(ssusb);
+ if (ret) {
+ dev_err(dev, "failed to initialize gadget\n");
+ goto comm_exit;
+ }
+
+ ret = ssusb_host_init(ssusb, node);
+ if (ret) {
+ dev_err(dev, "failed to initialize host\n");
+ goto gadget_exit;
+ }
+
+ ssusb_otg_switch_init(ssusb);
+ break;
+ default:
+ dev_err(dev, "unsupported mode: %d\n", ssusb->dr_mode);
+ ret = -EINVAL;
+ goto comm_exit;
+ }
+
+ return 0;
+
+gadget_exit:
+ ssusb_gadget_exit(ssusb);
+comm_exit:
+ ssusb_rscs_exit(ssusb);
+comm_init_err:
+ pm_runtime_put_sync(dev);
+ pm_runtime_disable(dev);
+
+ return ret;
+}
+
+static int mtu3_remove(struct platform_device *pdev)
+{
+ struct ssusb_mtk *ssusb = platform_get_drvdata(pdev);
+
+ switch (ssusb->dr_mode) {
+ case USB_DR_MODE_PERIPHERAL:
+ ssusb_gadget_exit(ssusb);
+ break;
+ case USB_DR_MODE_HOST:
+ ssusb_host_exit(ssusb);
+ break;
+ case USB_DR_MODE_OTG:
+ ssusb_otg_switch_exit(ssusb);
+ ssusb_gadget_exit(ssusb);
+ ssusb_host_exit(ssusb);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ssusb_rscs_exit(ssusb);
+ pm_runtime_put_sync(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+
+ return 0;
+}
+
+/*
+ * when support dual-role mode, we reject suspend when
+ * it works as device mode;
+ */
+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);
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ /* REVISIT: disconnect it for only device mode? */
+ if (!ssusb->is_host)
+ return 0;
+
+ ssusb_host_disable(ssusb, true);
+ ssusb_phy_power_off(ssusb);
+ clk_disable_unprepare(ssusb->sys_clk);
+ clk_disable_unprepare(ssusb->ref_clk);
+ ssusb_wakeup_enable(ssusb);
+
+ return 0;
+}
+
+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);
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ if (!ssusb->is_host)
+ return 0;
+
+ ssusb_wakeup_disable(ssusb);
+ clk_prepare_enable(ssusb->sys_clk);
+ clk_prepare_enable(ssusb->ref_clk);
+ ssusb_phy_power_on(ssusb);
+ ssusb_host_enable(ssusb);
+
+ return 0;
+}
+
+static const struct dev_pm_ops mtu3_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(mtu3_suspend, mtu3_resume)
+};
+
+#define DEV_PM_OPS (IS_ENABLED(CONFIG_PM) ? &mtu3_pm_ops : NULL)
+
+#ifdef CONFIG_OF
+
+static const struct of_device_id mtu3_of_match[] = {
+ {.compatible = "mediatek,mt8173-mtu3",},
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, mtu3_of_match);
+
+#endif
+
+static struct platform_driver mtu3_driver = {
+ .probe = mtu3_probe,
+ .remove = mtu3_remove,
+ .driver = {
+ .name = MTU3_DRIVER_NAME,
+ .pm = DEV_PM_OPS,
+ .of_match_table = of_match_ptr(mtu3_of_match),
+ },
+};
+module_platform_driver(mtu3_driver);
+
+MODULE_AUTHOR("Chunfeng Yun <chunfeng.yun@mediatek.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MediaTek USB3 DRD Controller Driver");
diff --git a/drivers/usb/mtu3/mtu3_qmu.c b/drivers/usb/mtu3/mtu3_qmu.c
new file mode 100644
index 000000000000..7d9ba8a52368
--- /dev/null
+++ b/drivers/usb/mtu3/mtu3_qmu.c
@@ -0,0 +1,573 @@
+/*
+ * mtu3_qmu.c - Queue Management Unit driver for device controller
+ *
+ * Copyright (C) 2016 MediaTek Inc.
+ *
+ * Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/*
+ * Queue Management Unit (QMU) is designed to unload SW effort
+ * to serve DMA interrupts.
+ * By preparing General Purpose Descriptor (GPD) and Buffer Descriptor (BD),
+ * SW links data buffers and triggers QMU to send / receive data to
+ * host / from device at a time.
+ * And now only GPD is supported.
+ *
+ * For more detailed information, please refer to QMU Programming Guide
+ */
+
+#include <linux/dmapool.h>
+#include <linux/iopoll.h>
+
+#include "mtu3.h"
+
+#define QMU_CHECKSUM_LEN 16
+
+#define GPD_FLAGS_HWO BIT(0)
+#define GPD_FLAGS_BDP BIT(1)
+#define GPD_FLAGS_BPS BIT(2)
+#define GPD_FLAGS_IOC BIT(7)
+
+#define GPD_EXT_FLAG_ZLP BIT(5)
+
+
+static struct qmu_gpd *gpd_dma_to_virt(struct mtu3_gpd_ring *ring,
+ dma_addr_t dma_addr)
+{
+ dma_addr_t dma_base = ring->dma;
+ struct qmu_gpd *gpd_head = ring->start;
+ u32 offset = (dma_addr - dma_base) / sizeof(*gpd_head);
+
+ if (offset >= MAX_GPD_NUM)
+ return NULL;
+
+ return gpd_head + offset;
+}
+
+static dma_addr_t gpd_virt_to_dma(struct mtu3_gpd_ring *ring,
+ struct qmu_gpd *gpd)
+{
+ dma_addr_t dma_base = ring->dma;
+ struct qmu_gpd *gpd_head = ring->start;
+ u32 offset;
+
+ offset = gpd - gpd_head;
+ if (offset >= MAX_GPD_NUM)
+ return 0;
+
+ return dma_base + (offset * sizeof(*gpd));
+}
+
+static void gpd_ring_init(struct mtu3_gpd_ring *ring, struct qmu_gpd *gpd)
+{
+ ring->start = gpd;
+ ring->enqueue = gpd;
+ ring->dequeue = gpd;
+ ring->end = gpd + MAX_GPD_NUM - 1;
+}
+
+static void reset_gpd_list(struct mtu3_ep *mep)
+{
+ struct mtu3_gpd_ring *ring = &mep->gpd_ring;
+ struct qmu_gpd *gpd = ring->start;
+
+ if (gpd) {
+ gpd->flag &= ~GPD_FLAGS_HWO;
+ gpd_ring_init(ring, gpd);
+ }
+}
+
+int mtu3_gpd_ring_alloc(struct mtu3_ep *mep)
+{
+ struct qmu_gpd *gpd;
+ struct mtu3_gpd_ring *ring = &mep->gpd_ring;
+
+ /* software own all gpds as default */
+ gpd = dma_pool_zalloc(mep->mtu->qmu_gpd_pool, GFP_ATOMIC, &ring->dma);
+ if (gpd == NULL)
+ return -ENOMEM;
+
+ gpd_ring_init(ring, gpd);
+
+ return 0;
+}
+
+void mtu3_gpd_ring_free(struct mtu3_ep *mep)
+{
+ struct mtu3_gpd_ring *ring = &mep->gpd_ring;
+
+ dma_pool_free(mep->mtu->qmu_gpd_pool,
+ ring->start, ring->dma);
+ memset(ring, 0, sizeof(*ring));
+}
+
+/*
+ * calculate check sum of a gpd or bd
+ * add "noinline" and "mb" to prevent wrong calculation
+ */
+static noinline u8 qmu_calc_checksum(u8 *data)
+{
+ u8 chksum = 0;
+ int i;
+
+ data[1] = 0x0; /* set checksum to 0 */
+
+ mb(); /* ensure the gpd/bd is really up-to-date */
+ for (i = 0; i < QMU_CHECKSUM_LEN; i++)
+ chksum += data[i];
+
+ /* Default: HWO=1, @flag[bit0] */
+ chksum += 1;
+
+ return 0xFF - chksum;
+}
+
+void mtu3_qmu_resume(struct mtu3_ep *mep)
+{
+ struct mtu3 *mtu = mep->mtu;
+ void __iomem *mbase = mtu->mac_base;
+ int epnum = mep->epnum;
+ u32 offset;
+
+ offset = mep->is_in ? USB_QMU_TQCSR(epnum) : USB_QMU_RQCSR(epnum);
+
+ mtu3_writel(mbase, offset, QMU_Q_RESUME);
+ if (!(mtu3_readl(mbase, offset) & QMU_Q_ACTIVE))
+ mtu3_writel(mbase, offset, QMU_Q_RESUME);
+}
+
+static struct qmu_gpd *advance_enq_gpd(struct mtu3_gpd_ring *ring)
+{
+ if (ring->enqueue < ring->end)
+ ring->enqueue++;
+ else
+ ring->enqueue = ring->start;
+
+ return ring->enqueue;
+}
+
+static struct qmu_gpd *advance_deq_gpd(struct mtu3_gpd_ring *ring)
+{
+ if (ring->dequeue < ring->end)
+ ring->dequeue++;
+ else
+ ring->dequeue = ring->start;
+
+ return ring->dequeue;
+}
+
+/* check if a ring is emtpy */
+static int gpd_ring_empty(struct mtu3_gpd_ring *ring)
+{
+ struct qmu_gpd *enq = ring->enqueue;
+ struct qmu_gpd *next;
+
+ if (ring->enqueue < ring->end)
+ next = enq + 1;
+ else
+ next = ring->start;
+
+ /* one gpd is reserved to simplify gpd preparation */
+ return next == ring->dequeue;
+}
+
+int mtu3_prepare_transfer(struct mtu3_ep *mep)
+{
+ return gpd_ring_empty(&mep->gpd_ring);
+}
+
+static int mtu3_prepare_tx_gpd(struct mtu3_ep *mep, struct mtu3_request *mreq)
+{
+ struct qmu_gpd *enq;
+ struct mtu3_gpd_ring *ring = &mep->gpd_ring;
+ struct qmu_gpd *gpd = ring->enqueue;
+ struct usb_request *req = &mreq->request;
+
+ /* set all fields to zero as default value */
+ memset(gpd, 0, sizeof(*gpd));
+
+ gpd->buffer = cpu_to_le32((u32)req->dma);
+ gpd->buf_len = cpu_to_le16(req->length);
+ gpd->flag |= GPD_FLAGS_IOC;
+
+ /* get the next GPD */
+ enq = advance_enq_gpd(ring);
+ dev_dbg(mep->mtu->dev, "TX-EP%d queue gpd=%p, enq=%p\n",
+ mep->epnum, gpd, enq);
+
+ enq->flag &= ~GPD_FLAGS_HWO;
+ gpd->next_gpd = cpu_to_le32((u32)gpd_virt_to_dma(ring, enq));
+
+ if (req->zero)
+ gpd->ext_flag |= GPD_EXT_FLAG_ZLP;
+
+ gpd->chksum = qmu_calc_checksum((u8 *)gpd);
+ gpd->flag |= GPD_FLAGS_HWO;
+
+ mreq->gpd = gpd;
+
+ return 0;
+}
+
+static int mtu3_prepare_rx_gpd(struct mtu3_ep *mep, struct mtu3_request *mreq)
+{
+ struct qmu_gpd *enq;
+ struct mtu3_gpd_ring *ring = &mep->gpd_ring;
+ struct qmu_gpd *gpd = ring->enqueue;
+ struct usb_request *req = &mreq->request;
+
+ /* set all fields to zero as default value */
+ memset(gpd, 0, sizeof(*gpd));
+
+ gpd->buffer = cpu_to_le32((u32)req->dma);
+ gpd->data_buf_len = cpu_to_le16(req->length);
+ gpd->flag |= GPD_FLAGS_IOC;
+
+ /* get the next GPD */
+ enq = advance_enq_gpd(ring);
+ dev_dbg(mep->mtu->dev, "RX-EP%d queue gpd=%p, enq=%p\n",
+ mep->epnum, gpd, enq);
+
+ enq->flag &= ~GPD_FLAGS_HWO;
+ gpd->next_gpd = cpu_to_le32((u32)gpd_virt_to_dma(ring, enq));
+ gpd->chksum = qmu_calc_checksum((u8 *)gpd);
+ gpd->flag |= GPD_FLAGS_HWO;
+
+ mreq->gpd = gpd;
+
+ return 0;
+}
+
+void mtu3_insert_gpd(struct mtu3_ep *mep, struct mtu3_request *mreq)
+{
+
+ if (mep->is_in)
+ mtu3_prepare_tx_gpd(mep, mreq);
+ else
+ mtu3_prepare_rx_gpd(mep, mreq);
+}
+
+int mtu3_qmu_start(struct mtu3_ep *mep)
+{
+ struct mtu3 *mtu = mep->mtu;
+ void __iomem *mbase = mtu->mac_base;
+ struct mtu3_gpd_ring *ring = &mep->gpd_ring;
+ u8 epnum = mep->epnum;
+
+ if (mep->is_in) {
+ /* set QMU start address */
+ mtu3_writel(mbase, USB_QMU_TQSAR(mep->epnum), ring->dma);
+ mtu3_setbits(mbase, MU3D_EP_TXCR0(mep->epnum), TX_DMAREQEN);
+ mtu3_setbits(mbase, U3D_QCR0, QMU_TX_CS_EN(epnum));
+ /* send zero length packet according to ZLP flag in GPD */
+ mtu3_setbits(mbase, U3D_QCR1, QMU_TX_ZLP(epnum));
+ mtu3_writel(mbase, U3D_TQERRIESR0,
+ QMU_TX_LEN_ERR(epnum) | QMU_TX_CS_ERR(epnum));
+
+ if (mtu3_readl(mbase, USB_QMU_TQCSR(epnum)) & QMU_Q_ACTIVE) {
+ dev_warn(mtu->dev, "Tx %d Active Now!\n", epnum);
+ return 0;
+ }
+ mtu3_writel(mbase, USB_QMU_TQCSR(epnum), QMU_Q_START);
+
+ } else {
+ mtu3_writel(mbase, USB_QMU_RQSAR(mep->epnum), ring->dma);
+ mtu3_setbits(mbase, MU3D_EP_RXCR0(mep->epnum), RX_DMAREQEN);
+ mtu3_setbits(mbase, U3D_QCR0, QMU_RX_CS_EN(epnum));
+ /* don't expect ZLP */
+ mtu3_clrbits(mbase, U3D_QCR3, QMU_RX_ZLP(epnum));
+ /* move to next GPD when receive ZLP */
+ mtu3_setbits(mbase, U3D_QCR3, QMU_RX_COZ(epnum));
+ mtu3_writel(mbase, U3D_RQERRIESR0,
+ QMU_RX_LEN_ERR(epnum) | QMU_RX_CS_ERR(epnum));
+ mtu3_writel(mbase, U3D_RQERRIESR1, QMU_RX_ZLP_ERR(epnum));
+
+ if (mtu3_readl(mbase, USB_QMU_RQCSR(epnum)) & QMU_Q_ACTIVE) {
+ dev_warn(mtu->dev, "Rx %d Active Now!\n", epnum);
+ return 0;
+ }
+ mtu3_writel(mbase, USB_QMU_RQCSR(epnum), QMU_Q_START);
+ }
+
+ return 0;
+}
+
+/* may called in atomic context */
+void mtu3_qmu_stop(struct mtu3_ep *mep)
+{
+ struct mtu3 *mtu = mep->mtu;
+ void __iomem *mbase = mtu->mac_base;
+ int epnum = mep->epnum;
+ u32 value = 0;
+ u32 qcsr;
+ int ret;
+
+ qcsr = mep->is_in ? USB_QMU_TQCSR(epnum) : USB_QMU_RQCSR(epnum);
+
+ if (!(mtu3_readl(mbase, qcsr) & QMU_Q_ACTIVE)) {
+ dev_dbg(mtu->dev, "%s's qmu is inactive now!\n", mep->name);
+ return;
+ }
+ mtu3_writel(mbase, qcsr, QMU_Q_STOP);
+
+ ret = readl_poll_timeout_atomic(mbase + qcsr, value,
+ !(value & QMU_Q_ACTIVE), 1, 1000);
+ if (ret) {
+ dev_err(mtu->dev, "stop %s's qmu failed\n", mep->name);
+ return;
+ }
+
+ dev_dbg(mtu->dev, "%s's qmu stop now!\n", mep->name);
+}
+
+void mtu3_qmu_flush(struct mtu3_ep *mep)
+{
+
+ dev_dbg(mep->mtu->dev, "%s flush QMU %s\n", __func__,
+ ((mep->is_in) ? "TX" : "RX"));
+
+ /*Stop QMU */
+ mtu3_qmu_stop(mep);
+ reset_gpd_list(mep);
+}
+
+/*
+ * QMU can't transfer zero length packet directly (a hardware limit
+ * on old SoCs), so when needs to send ZLP, we intentionally trigger
+ * a length error interrupt, and in the ISR sends a ZLP by BMU.
+ */
+static void qmu_tx_zlp_error_handler(struct mtu3 *mtu, u8 epnum)
+{
+ struct mtu3_ep *mep = mtu->in_eps + epnum;
+ struct mtu3_gpd_ring *ring = &mep->gpd_ring;
+ void __iomem *mbase = mtu->mac_base;
+ struct qmu_gpd *gpd_current = NULL;
+ dma_addr_t gpd_dma = mtu3_readl(mbase, USB_QMU_TQCPR(epnum));
+ struct usb_request *req = NULL;
+ struct mtu3_request *mreq;
+ u32 txcsr = 0;
+ int ret;
+
+ mreq = next_request(mep);
+ if (mreq && mreq->request.length == 0)
+ req = &mreq->request;
+ else
+ return;
+
+ gpd_current = gpd_dma_to_virt(ring, gpd_dma);
+
+ if (le16_to_cpu(gpd_current->buf_len) != 0) {
+ dev_err(mtu->dev, "TX EP%d buffer length error(!=0)\n", epnum);
+ return;
+ }
+
+ dev_dbg(mtu->dev, "%s send ZLP for req=%p\n", __func__, mreq);
+
+ mtu3_clrbits(mbase, MU3D_EP_TXCR0(mep->epnum), TX_DMAREQEN);
+
+ ret = readl_poll_timeout_atomic(mbase + MU3D_EP_TXCR0(mep->epnum),
+ txcsr, !(txcsr & TX_FIFOFULL), 1, 1000);
+ if (ret) {
+ dev_err(mtu->dev, "%s wait for fifo empty fail\n", __func__);
+ return;
+ }
+ mtu3_setbits(mbase, MU3D_EP_TXCR0(mep->epnum), TX_TXPKTRDY);
+
+ /* by pass the current GDP */
+ gpd_current->flag |= GPD_FLAGS_BPS;
+ gpd_current->chksum = qmu_calc_checksum((u8 *)gpd_current);
+ gpd_current->flag |= GPD_FLAGS_HWO;
+
+ /*enable DMAREQEN, switch back to QMU mode */
+ mtu3_setbits(mbase, MU3D_EP_TXCR0(mep->epnum), TX_DMAREQEN);
+ mtu3_qmu_resume(mep);
+}
+
+/*
+ * NOTE: request list maybe is already empty as following case:
+ * queue_tx --> qmu_interrupt(clear interrupt pending, schedule tasklet)-->
+ * queue_tx --> process_tasklet(meanwhile, the second one is transferred,
+ * tasklet process both of them)-->qmu_interrupt for second one.
+ * To avoid upper case, put qmu_done_tx in ISR directly to process it.
+ */
+static void qmu_done_tx(struct mtu3 *mtu, u8 epnum)
+{
+ struct mtu3_ep *mep = mtu->in_eps + epnum;
+ struct mtu3_gpd_ring *ring = &mep->gpd_ring;
+ void __iomem *mbase = mtu->mac_base;
+ struct qmu_gpd *gpd = ring->dequeue;
+ struct qmu_gpd *gpd_current = NULL;
+ dma_addr_t gpd_dma = mtu3_readl(mbase, USB_QMU_TQCPR(epnum));
+ struct usb_request *request = NULL;
+ struct mtu3_request *mreq;
+
+ /*transfer phy address got from QMU register to virtual address */
+ gpd_current = gpd_dma_to_virt(ring, gpd_dma);
+
+ dev_dbg(mtu->dev, "%s EP%d, last=%p, current=%p, enq=%p\n",
+ __func__, epnum, gpd, gpd_current, ring->enqueue);
+
+ while (gpd != gpd_current && !(gpd->flag & GPD_FLAGS_HWO)) {
+
+ mreq = next_request(mep);
+
+ if (mreq == NULL || mreq->gpd != gpd) {
+ dev_err(mtu->dev, "no correct TX req is found\n");
+ break;
+ }
+
+ request = &mreq->request;
+ request->actual = le16_to_cpu(gpd->buf_len);
+ mtu3_req_complete(mep, request, 0);
+
+ gpd = advance_deq_gpd(ring);
+ }
+
+ dev_dbg(mtu->dev, "%s EP%d, deq=%p, enq=%p, complete\n",
+ __func__, epnum, ring->dequeue, ring->enqueue);
+
+}
+
+static void qmu_done_rx(struct mtu3 *mtu, u8 epnum)
+{
+ struct mtu3_ep *mep = mtu->out_eps + epnum;
+ struct mtu3_gpd_ring *ring = &mep->gpd_ring;
+ void __iomem *mbase = mtu->mac_base;
+ struct qmu_gpd *gpd = ring->dequeue;
+ struct qmu_gpd *gpd_current = NULL;
+ dma_addr_t gpd_dma = mtu3_readl(mbase, USB_QMU_RQCPR(epnum));
+ struct usb_request *req = NULL;
+ struct mtu3_request *mreq;
+
+ gpd_current = gpd_dma_to_virt(ring, gpd_dma);
+
+ dev_dbg(mtu->dev, "%s EP%d, last=%p, current=%p, enq=%p\n",
+ __func__, epnum, gpd, gpd_current, ring->enqueue);
+
+ while (gpd != gpd_current && !(gpd->flag & GPD_FLAGS_HWO)) {
+
+ mreq = next_request(mep);
+
+ if (mreq == NULL || mreq->gpd != gpd) {
+ dev_err(mtu->dev, "no correct RX req is found\n");
+ break;
+ }
+ req = &mreq->request;
+
+ req->actual = le16_to_cpu(gpd->buf_len);
+ mtu3_req_complete(mep, req, 0);
+
+ gpd = advance_deq_gpd(ring);
+ }
+
+ dev_dbg(mtu->dev, "%s EP%d, deq=%p, enq=%p, complete\n",
+ __func__, epnum, ring->dequeue, ring->enqueue);
+}
+
+static void qmu_done_isr(struct mtu3 *mtu, u32 done_status)
+{
+ int i;
+
+ for (i = 1; i < mtu->num_eps; i++) {
+ if (done_status & QMU_RX_DONE_INT(i))
+ qmu_done_rx(mtu, i);
+ if (done_status & QMU_TX_DONE_INT(i))
+ qmu_done_tx(mtu, i);
+ }
+}
+
+static void qmu_exception_isr(struct mtu3 *mtu, u32 qmu_status)
+{
+ void __iomem *mbase = mtu->mac_base;
+ u32 errval;
+ int i;
+
+ if ((qmu_status & RXQ_CSERR_INT) || (qmu_status & RXQ_LENERR_INT)) {
+ errval = mtu3_readl(mbase, U3D_RQERRIR0);
+ for (i = 1; i < mtu->num_eps; i++) {
+ if (errval & QMU_RX_CS_ERR(i))
+ dev_err(mtu->dev, "Rx %d CS error!\n", i);
+
+ if (errval & QMU_RX_LEN_ERR(i))
+ dev_err(mtu->dev, "RX %d Length error\n", i);
+ }
+ mtu3_writel(mbase, U3D_RQERRIR0, errval);
+ }
+
+ if (qmu_status & RXQ_ZLPERR_INT) {
+ errval = mtu3_readl(mbase, U3D_RQERRIR1);
+ for (i = 1; i < mtu->num_eps; i++) {
+ if (errval & QMU_RX_ZLP_ERR(i))
+ dev_dbg(mtu->dev, "RX EP%d Recv ZLP\n", i);
+ }
+ mtu3_writel(mbase, U3D_RQERRIR1, errval);
+ }
+
+ if ((qmu_status & TXQ_CSERR_INT) || (qmu_status & TXQ_LENERR_INT)) {
+ errval = mtu3_readl(mbase, U3D_TQERRIR0);
+ for (i = 1; i < mtu->num_eps; i++) {
+ if (errval & QMU_TX_CS_ERR(i))
+ dev_err(mtu->dev, "Tx %d checksum error!\n", i);
+
+ if (errval & QMU_TX_LEN_ERR(i))
+ qmu_tx_zlp_error_handler(mtu, i);
+ }
+ mtu3_writel(mbase, U3D_TQERRIR0, errval);
+ }
+}
+
+irqreturn_t mtu3_qmu_isr(struct mtu3 *mtu)
+{
+ void __iomem *mbase = mtu->mac_base;
+ u32 qmu_status;
+ u32 qmu_done_status;
+
+ /* U3D_QISAR1 is read update */
+ qmu_status = mtu3_readl(mbase, U3D_QISAR1);
+ qmu_status &= mtu3_readl(mbase, U3D_QIER1);
+
+ qmu_done_status = mtu3_readl(mbase, U3D_QISAR0);
+ qmu_done_status &= mtu3_readl(mbase, U3D_QIER0);
+ mtu3_writel(mbase, U3D_QISAR0, qmu_done_status); /* W1C */
+ dev_dbg(mtu->dev, "=== QMUdone[tx=%x, rx=%x] QMUexp[%x] ===\n",
+ (qmu_done_status & 0xFFFF), qmu_done_status >> 16,
+ qmu_status);
+
+ if (qmu_done_status)
+ qmu_done_isr(mtu, qmu_done_status);
+
+ if (qmu_status)
+ qmu_exception_isr(mtu, qmu_status);
+
+ return IRQ_HANDLED;
+}
+
+int mtu3_qmu_init(struct mtu3 *mtu)
+{
+
+ compiletime_assert(QMU_GPD_SIZE == 16, "QMU_GPD size SHOULD be 16B");
+
+ mtu->qmu_gpd_pool = dma_pool_create("QMU_GPD", mtu->dev,
+ QMU_GPD_RING_SIZE, QMU_GPD_SIZE, 0);
+
+ if (!mtu->qmu_gpd_pool)
+ return -ENOMEM;
+
+ return 0;
+}
+
+void mtu3_qmu_exit(struct mtu3 *mtu)
+{
+ dma_pool_destroy(mtu->qmu_gpd_pool);
+}
diff --git a/drivers/usb/mtu3/mtu3_qmu.h b/drivers/usb/mtu3/mtu3_qmu.h
new file mode 100644
index 000000000000..4dafa16bf120
--- /dev/null
+++ b/drivers/usb/mtu3/mtu3_qmu.h
@@ -0,0 +1,43 @@
+/*
+ * mtu3_qmu.h - Queue Management Unit driver header
+ *
+ * Copyright (C) 2016 MediaTek Inc.
+ *
+ * Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __MTK_QMU_H__
+#define __MTK_QMU_H__
+
+#define MAX_GPD_NUM 64
+#define QMU_GPD_SIZE (sizeof(struct qmu_gpd))
+#define QMU_GPD_RING_SIZE (MAX_GPD_NUM * QMU_GPD_SIZE)
+
+#define GPD_BUF_SIZE 65532
+
+void mtu3_qmu_stop(struct mtu3_ep *mep);
+int mtu3_qmu_start(struct mtu3_ep *mep);
+void mtu3_qmu_resume(struct mtu3_ep *mep);
+void mtu3_qmu_flush(struct mtu3_ep *mep);
+
+void mtu3_insert_gpd(struct mtu3_ep *mep, struct mtu3_request *mreq);
+int mtu3_prepare_transfer(struct mtu3_ep *mep);
+
+int mtu3_gpd_ring_alloc(struct mtu3_ep *mep);
+void mtu3_gpd_ring_free(struct mtu3_ep *mep);
+
+irqreturn_t mtu3_qmu_isr(struct mtu3 *mtu);
+int mtu3_qmu_init(struct mtu3 *mtu);
+void mtu3_qmu_exit(struct mtu3 *mtu);
+
+#endif
diff --git a/drivers/usb/musb/am35x.c b/drivers/usb/musb/am35x.c
index 50ca8052bc8e..02fbb4fe3745 100644
--- a/drivers/usb/musb/am35x.c
+++ b/drivers/usb/musb/am35x.c
@@ -121,7 +121,6 @@ static void am35x_musb_disable(struct musb *musb)
musb_writel(reg_base, CORE_INTR_MASK_CLEAR_REG, AM35X_INTR_USB_MASK);
musb_writel(reg_base, EP_INTR_MASK_CLEAR_REG,
AM35X_TX_INTR_MASK | AM35X_RX_INTR_MASK);
- musb_writeb(musb->mregs, MUSB_DEVCTL, 0);
musb_writel(reg_base, USB_END_OF_INTR_REG, 0);
}
diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c
index 310238c6b5cd..4418574a36a1 100644
--- a/drivers/usb/musb/blackfin.c
+++ b/drivers/usb/musb/blackfin.c
@@ -469,6 +469,7 @@ static const struct musb_platform_ops bfin_ops = {
.init = bfin_musb_init,
.exit = bfin_musb_exit,
+ .fifo_offset = bfin_fifo_offset,
.readb = bfin_readb,
.writeb = bfin_writeb,
.readw = bfin_readw,
@@ -580,8 +581,7 @@ static int bfin_remove(struct platform_device *pdev)
return 0;
}
-#ifdef CONFIG_PM
-static int bfin_suspend(struct device *dev)
+static int __maybe_unused bfin_suspend(struct device *dev)
{
struct bfin_glue *glue = dev_get_drvdata(dev);
struct musb *musb = glue_to_musb(glue);
@@ -598,7 +598,7 @@ static int bfin_suspend(struct device *dev)
return 0;
}
-static int bfin_resume(struct device *dev)
+static int __maybe_unused bfin_resume(struct device *dev)
{
struct bfin_glue *glue = dev_get_drvdata(dev);
struct musb *musb = glue_to_musb(glue);
@@ -607,7 +607,6 @@ static int bfin_resume(struct device *dev)
return 0;
}
-#endif
static SIMPLE_DEV_PM_OPS(bfin_pm_ops, bfin_suspend, bfin_resume);
diff --git a/drivers/usb/musb/cppi_dma.c b/drivers/usb/musb/cppi_dma.c
index 1ae48e64e975..c4fabe952ca6 100644
--- a/drivers/usb/musb/cppi_dma.c
+++ b/drivers/usb/musb/cppi_dma.c
@@ -224,7 +224,7 @@ static void cppi_controller_stop(struct cppi *controller)
int i;
struct musb *musb;
- musb = controller->musb;
+ musb = controller->controller.musb;
tibase = controller->tibase;
/* DISABLE INDIVIDUAL CHANNEL Interrupts */
@@ -288,7 +288,7 @@ cppi_channel_allocate(struct dma_controller *c,
controller = container_of(c, struct cppi, controller);
tibase = controller->tibase;
- musb = controller->musb;
+ musb = c->musb;
/* ep0 doesn't use DMA; remember cppi indices are 0..N-1 */
index = ep->epnum - 1;
@@ -336,7 +336,7 @@ static void cppi_channel_release(struct dma_channel *channel)
c = container_of(channel, struct cppi_channel, channel);
tibase = c->controller->tibase;
if (!c->hw_ep)
- musb_dbg(c->controller->musb,
+ musb_dbg(c->controller->controller.musb,
"releasing idle DMA channel %p", c);
else if (!c->transmit)
core_rxirq_enable(tibase, c->index + 1);
@@ -355,7 +355,7 @@ cppi_dump_rx(int level, struct cppi_channel *c, const char *tag)
musb_ep_select(base, c->index + 1);
- musb_dbg(c->controller->musb,
+ musb_dbg(c->controller->controller.musb,
"RX DMA%d%s: %d left, csr %04x, "
"%08x H%08x S%08x C%08x, "
"B%08x L%08x %08x .. %08x",
@@ -385,7 +385,7 @@ cppi_dump_tx(int level, struct cppi_channel *c, const char *tag)
musb_ep_select(base, c->index + 1);
- musb_dbg(c->controller->musb,
+ musb_dbg(c->controller->controller.musb,
"TX DMA%d%s: csr %04x, "
"H%08x S%08x C%08x %08x, "
"F%08x L%08x .. %08x",
@@ -954,7 +954,7 @@ static int cppi_channel_program(struct dma_channel *ch,
cppi_ch = container_of(ch, struct cppi_channel, channel);
controller = cppi_ch->controller;
- musb = controller->musb;
+ musb = controller->controller.musb;
switch (ch->status) {
case MUSB_DMA_STATUS_BUS_ABORT:
@@ -1009,7 +1009,7 @@ static bool cppi_rx_scan(struct cppi *cppi, unsigned ch)
int i;
dma_addr_t safe2ack;
void __iomem *regs = rx->hw_ep->regs;
- struct musb *musb = cppi->musb;
+ struct musb *musb = cppi->controller.musb;
cppi_dump_rx(6, rx, "/K");
@@ -1121,7 +1121,7 @@ static bool cppi_rx_scan(struct cppi *cppi, unsigned ch)
* setting it here "should" be racey, but seems to work
*/
csr = musb_readw(rx->hw_ep->regs, MUSB_RXCSR);
- if (is_host_active(cppi->musb)
+ if (is_host_active(cppi->controller.musb)
&& bd
&& !(csr & MUSB_RXCSR_H_REQPKT)) {
csr |= MUSB_RXCSR_H_REQPKT;
@@ -1311,7 +1311,7 @@ cppi_dma_controller_create(struct musb *musb, void __iomem *mregs)
controller->mregs = mregs;
controller->tibase = mregs - DAVINCI_BASE_OFFSET;
- controller->musb = musb;
+ controller->controller.musb = musb;
controller->controller.channel_alloc = cppi_channel_allocate;
controller->controller.channel_release = cppi_channel_release;
controller->controller.channel_program = cppi_channel_program;
@@ -1323,7 +1323,7 @@ cppi_dma_controller_create(struct musb *musb, void __iomem *mregs)
/* setup BufferPool */
controller->pool = dma_pool_create("cppi",
- controller->musb->controller,
+ controller->controller.musb->controller,
sizeof(struct cppi_descriptor),
CPPI_DESCRIPTOR_ALIGN, 0);
if (!controller->pool) {
@@ -1357,7 +1357,7 @@ void cppi_dma_controller_destroy(struct dma_controller *c)
cppi_controller_stop(cppi);
if (cppi->irq)
- free_irq(cppi->irq, cppi->musb);
+ free_irq(cppi->irq, cppi->controller.musb);
/* assert: caller stopped the controller first */
dma_pool_destroy(cppi->pool);
@@ -1469,7 +1469,7 @@ static int cppi_channel_abort(struct dma_channel *channel)
core_rxirq_disable(tibase, cppi_ch->index + 1);
/* for host, ensure ReqPkt is never set again */
- if (is_host_active(cppi_ch->controller->musb)) {
+ if (is_host_active(cppi_ch->controller->controller.musb)) {
value = musb_readl(tibase, DAVINCI_AUTOREQ_REG);
value &= ~((0x3) << (cppi_ch->index * 2));
musb_writel(tibase, DAVINCI_AUTOREQ_REG, value);
@@ -1478,7 +1478,7 @@ static int cppi_channel_abort(struct dma_channel *channel)
csr = musb_readw(regs, MUSB_RXCSR);
/* for host, clear (just) ReqPkt at end of current packet(s) */
- if (is_host_active(cppi_ch->controller->musb)) {
+ if (is_host_active(cppi_ch->controller->controller.musb)) {
csr |= MUSB_RXCSR_H_WZC_BITS;
csr &= ~MUSB_RXCSR_H_REQPKT;
} else
diff --git a/drivers/usb/musb/cppi_dma.h b/drivers/usb/musb/cppi_dma.h
index 7fdfb71a8f09..9bb7c5e45c85 100644
--- a/drivers/usb/musb/cppi_dma.h
+++ b/drivers/usb/musb/cppi_dma.h
@@ -107,7 +107,6 @@ struct cppi_channel {
/* CPPI DMA controller object */
struct cppi {
struct dma_controller controller;
- struct musb *musb;
void __iomem *mregs; /* Mentor regs */
void __iomem *tibase; /* TI/CPPI regs */
diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c
index 2440f88e07a3..d79c288ccbf2 100644
--- a/drivers/usb/musb/da8xx.c
+++ b/drivers/usb/musb/da8xx.c
@@ -6,6 +6,9 @@
* Based on the DaVinci "glue layer" code.
* Copyright (C) 2005-2006 by Texas Instruments
*
+ * DT support
+ * Copyright (c) 2016 Petr Kulhavy <petr@barix.com>
+ *
* This file is part of the Inventra Controller Driver for Linux.
*
* The Inventra Controller Driver for Linux is free software; you
@@ -122,7 +125,6 @@ static void da8xx_musb_disable(struct musb *musb)
musb_writel(reg_base, DA8XX_USB_INTR_MASK_CLEAR_REG,
DA8XX_INTR_USB_MASK |
DA8XX_INTR_TX_MASK | DA8XX_INTR_RX_MASK);
- musb_writeb(musb->mregs, MUSB_DEVCTL, 0);
musb_writel(reg_base, DA8XX_USB_END_OF_INTR_REG, 0);
}
@@ -340,6 +342,13 @@ static int da8xx_musb_set_mode(struct musb *musb, u8 musb_mode)
struct da8xx_glue *glue = dev_get_drvdata(musb->controller->parent);
enum phy_mode phy_mode;
+ /*
+ * The PHY has some issues when it is forced in device or host mode.
+ * Unless the user request another mode, configure the PHY in OTG mode.
+ */
+ if (!musb->is_initialized)
+ return phy_set_mode(glue->phy, PHY_MODE_USB_OTG);
+
switch (musb_mode) {
case MUSB_HOST: /* Force VBUS valid, ID = 0 */
phy_mode = PHY_MODE_USB_HOST;
@@ -366,6 +375,12 @@ static int da8xx_musb_init(struct musb *musb)
musb->mregs += DA8XX_MENTOR_CORE_OFFSET;
+ ret = clk_prepare_enable(glue->clk);
+ if (ret) {
+ dev_err(glue->dev, "failed to enable clock\n");
+ return ret;
+ }
+
/* Returns zero if e.g. not clocked */
rev = musb_readl(reg_base, DA8XX_USB_REVISION_REG);
if (!rev)
@@ -377,12 +392,6 @@ static int da8xx_musb_init(struct musb *musb)
goto fail;
}
- ret = clk_prepare_enable(glue->clk);
- if (ret) {
- dev_err(glue->dev, "failed to enable clock\n");
- goto fail;
- }
-
setup_timer(&otg_workaround, otg_timer, (unsigned long)musb);
/* Reset the controller */
@@ -392,7 +401,7 @@ static int da8xx_musb_init(struct musb *musb)
ret = phy_init(glue->phy);
if (ret) {
dev_err(glue->dev, "Failed to init phy.\n");
- goto err_phy_init;
+ goto fail;
}
ret = phy_power_on(glue->phy);
@@ -412,9 +421,8 @@ static int da8xx_musb_init(struct musb *musb)
err_phy_power_on:
phy_exit(glue->phy);
-err_phy_init:
- clk_disable_unprepare(glue->clk);
fail:
+ clk_disable_unprepare(glue->clk);
return ret;
}
@@ -433,16 +441,27 @@ static int da8xx_musb_exit(struct musb *musb)
return 0;
}
+static inline u8 get_vbus_power(struct device *dev)
+{
+ struct regulator *vbus_supply;
+ int current_uA;
+
+ vbus_supply = regulator_get_optional(dev, "vbus");
+ if (IS_ERR(vbus_supply))
+ return 255;
+ current_uA = regulator_get_current_limit(vbus_supply);
+ regulator_put(vbus_supply);
+ if (current_uA <= 0 || current_uA > 510000)
+ return 255;
+ return current_uA / 1000 / 2;
+}
+
static const struct musb_platform_ops da8xx_ops = {
- .quirks = MUSB_DMA_CPPI | MUSB_INDEXED_EP,
+ .quirks = MUSB_INDEXED_EP | MUSB_PRESERVE_SESSION,
.init = da8xx_musb_init,
.exit = da8xx_musb_exit,
.fifo_mode = 2,
-#ifdef CONFIG_USB_TI_CPPI_DMA
- .dma_init = cppi_dma_controller_create,
- .dma_exit = cppi_dma_controller_destroy,
-#endif
.enable = da8xx_musb_enable,
.disable = da8xx_musb_disable,
@@ -458,6 +477,12 @@ static const struct platform_device_info da8xx_dev_info = {
.dma_mask = DMA_BIT_MASK(32),
};
+static const struct musb_hdrc_config da8xx_config = {
+ .ram_bits = 10,
+ .num_eps = 5,
+ .multipoint = 1,
+};
+
static int da8xx_probe(struct platform_device *pdev)
{
struct resource musb_resources[2];
@@ -465,6 +490,7 @@ static int da8xx_probe(struct platform_device *pdev)
struct da8xx_glue *glue;
struct platform_device_info pinfo;
struct clk *clk;
+ struct device_node *np = pdev->dev.of_node;
int ret;
glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);
@@ -487,6 +513,16 @@ static int da8xx_probe(struct platform_device *pdev)
glue->dev = &pdev->dev;
glue->clk = clk;
+ if (IS_ENABLED(CONFIG_OF) && np) {
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ pdata->config = &da8xx_config;
+ pdata->mode = musb_get_mode(&pdev->dev);
+ pdata->power = get_vbus_power(&pdev->dev);
+ }
+
pdata->platform_ops = &da8xx_ops;
glue->usb_phy = usb_phy_generic_register();
@@ -537,11 +573,51 @@ static int da8xx_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_PM_SLEEP
+static int da8xx_suspend(struct device *dev)
+{
+ int ret;
+ struct da8xx_glue *glue = dev_get_drvdata(dev);
+
+ ret = phy_power_off(glue->phy);
+ if (ret)
+ return ret;
+ clk_disable_unprepare(glue->clk);
+
+ return 0;
+}
+
+static int da8xx_resume(struct device *dev)
+{
+ int ret;
+ struct da8xx_glue *glue = dev_get_drvdata(dev);
+
+ ret = clk_prepare_enable(glue->clk);
+ if (ret)
+ return ret;
+ return phy_power_on(glue->phy);
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(da8xx_pm_ops, da8xx_suspend, da8xx_resume);
+
+#ifdef CONFIG_OF
+static const struct of_device_id da8xx_id_table[] = {
+ {
+ .compatible = "ti,da830-musb",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, da8xx_id_table);
+#endif
+
static struct platform_driver da8xx_driver = {
.probe = da8xx_probe,
.remove = da8xx_remove,
.driver = {
.name = "musb-da8xx",
+ .pm = &da8xx_pm_ops,
+ .of_match_table = of_match_ptr(da8xx_id_table),
},
};
diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c
index cee61a51645e..52b491d3d5d8 100644
--- a/drivers/usb/musb/davinci.c
+++ b/drivers/usb/musb/davinci.c
@@ -133,7 +133,6 @@ static void davinci_musb_disable(struct musb *musb)
DAVINCI_USB_USBINT_MASK
| DAVINCI_USB_TXINT_MASK
| DAVINCI_USB_RXINT_MASK);
- musb_writeb(musb->mregs, MUSB_DEVCTL, 0);
musb_writel(musb->ctrl_base, DAVINCI_USB_EOI_REG, 0);
if (is_dma_capable() && !dma_off)
diff --git a/drivers/usb/musb/jz4740.c b/drivers/usb/musb/jz4740.c
index bc8889956d17..40c68c23d553 100644
--- a/drivers/usb/musb/jz4740.c
+++ b/drivers/usb/musb/jz4740.c
@@ -63,7 +63,7 @@ static struct musb_fifo_cfg jz4740_musb_fifo_cfg[] = {
{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 64, },
};
-static struct musb_hdrc_config jz4740_musb_config = {
+static const struct musb_hdrc_config jz4740_musb_config = {
/* Silicon does not implement USB OTG. */
.multipoint = 0,
/* Max EPs scanned, driver will decide which EP can be used. */
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
index c3e172e15ec3..0c3664ab705e 100644
--- a/drivers/usb/musb/musb_core.c
+++ b/drivers/usb/musb/musb_core.c
@@ -100,6 +100,7 @@
#include <linux/io.h>
#include <linux/dma-mapping.h>
#include <linux/usb.h>
+#include <linux/usb/of.h>
#include "musb_core.h"
#include "musb_trace.h"
@@ -130,6 +131,24 @@ static inline struct musb *dev_to_musb(struct device *dev)
return dev_get_drvdata(dev);
}
+enum musb_mode musb_get_mode(struct device *dev)
+{
+ enum usb_dr_mode mode;
+
+ mode = usb_get_dr_mode(dev);
+ switch (mode) {
+ case USB_DR_MODE_HOST:
+ return MUSB_HOST;
+ case USB_DR_MODE_PERIPHERAL:
+ return MUSB_PERIPHERAL;
+ case USB_DR_MODE_OTG:
+ case USB_DR_MODE_UNKNOWN:
+ default:
+ return MUSB_OTG;
+ }
+}
+EXPORT_SYMBOL_GPL(musb_get_mode);
+
/*-------------------------------------------------------------------------*/
#ifndef CONFIG_BLACKFIN
@@ -569,20 +588,17 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
if (devctl & MUSB_DEVCTL_HM) {
switch (musb->xceiv->otg->state) {
case OTG_STATE_A_SUSPEND:
- /* remote wakeup? later, GetPortStatus
- * will stop RESUME signaling
- */
-
+ /* 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->need_finish_resume = 1;
-
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;
@@ -1024,16 +1040,6 @@ static void musb_enable_interrupts(struct musb *musb)
}
-static void musb_generic_disable(struct musb *musb)
-{
- void __iomem *mbase = musb->mregs;
-
- musb_disable_interrupts(musb);
-
- /* off */
- musb_writeb(mbase, MUSB_DEVCTL, 0);
-}
-
/*
* Program the HDRC to start (enable interrupts, dma, etc.).
*/
@@ -1090,8 +1096,8 @@ void musb_stop(struct musb *musb)
{
/* stop IRQs, timers, ... */
musb_platform_disable(musb);
- musb_generic_disable(musb);
- musb_dbg(musb, "HDRC disabled");
+ musb_disable_interrupts(musb);
+ musb_writeb(musb->mregs, MUSB_DEVCTL, 0);
/* FIXME
* - mark host and/or peripheral drivers unusable/inactive
@@ -1863,6 +1869,7 @@ static void musb_pm_runtime_check_session(struct musb *musb)
return;
}
+ /* fall through */
case MUSB_QUIRK_A_DISCONNECT_19:
if (musb->quirk_retries--) {
musb_dbg(musb,
@@ -1909,6 +1916,14 @@ static void musb_pm_runtime_check_session(struct musb *musb)
static void musb_irq_work(struct work_struct *data)
{
struct musb *musb = container_of(data, struct musb, irq_work.work);
+ int error;
+
+ error = pm_runtime_get_sync(musb->controller);
+ if (error < 0) {
+ dev_err(musb->controller, "Could not enable: %i\n", error);
+
+ return;
+ }
musb_pm_runtime_check_session(musb);
@@ -1916,6 +1931,9 @@ static void musb_irq_work(struct work_struct *data)
musb->xceiv_old_state = musb->xceiv->otg->state;
sysfs_notify(&musb->controller->kobj, NULL, "mode");
}
+
+ pm_runtime_mark_last_busy(musb->controller);
+ pm_runtime_put_autosuspend(musb->controller);
}
static void musb_recover_from_babble(struct musb *musb)
@@ -2034,6 +2052,7 @@ struct musb_pending_work {
struct list_head node;
};
+#ifdef CONFIG_PM
/*
* Called from musb_runtime_resume(), musb_resume(), and
* musb_queue_resume_work(). Callers must take musb->lock.
@@ -2061,6 +2080,7 @@ static int musb_run_resume_work(struct musb *musb)
return error;
}
+#endif
/*
* Called to run work if device is active or else queue the work to happen
@@ -2294,7 +2314,8 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
/* be sure interrupts are disabled before connecting ISR */
musb_platform_disable(musb);
- musb_generic_disable(musb);
+ musb_disable_interrupts(musb);
+ musb_writeb(musb->mregs, MUSB_DEVCTL, 0);
/* Init IRQ workqueue before request_irq */
INIT_DELAYED_WORK(&musb->irq_work, musb_irq_work);
@@ -2414,8 +2435,9 @@ fail2:
musb_platform_exit(musb);
fail1:
- dev_err(musb->controller,
- "musb_init_controller failed with status %d\n", status);
+ if (status != -EPROBE_DEFER)
+ dev_err(musb->controller,
+ "%s failed with status %d\n", __func__, status);
musb_free(musb);
@@ -2467,11 +2489,13 @@ static int musb_remove(struct platform_device *pdev)
pm_runtime_get_sync(musb->controller);
musb_host_cleanup(musb);
musb_gadget_cleanup(musb);
- spin_lock_irqsave(&musb->lock, flags);
+
musb_platform_disable(musb);
- musb_generic_disable(musb);
- spin_unlock_irqrestore(&musb->lock, flags);
+ spin_lock_irqsave(&musb->lock, flags);
+ musb_disable_interrupts(musb);
musb_writeb(musb->mregs, MUSB_DEVCTL, 0);
+ spin_unlock_irqrestore(&musb->lock, flags);
+
pm_runtime_dont_use_autosuspend(musb->controller);
pm_runtime_put_sync(musb->controller);
pm_runtime_disable(musb->controller);
@@ -2646,7 +2670,9 @@ static int musb_suspend(struct device *dev)
unsigned long flags;
musb_platform_disable(musb);
- musb_generic_disable(musb);
+ musb_disable_interrupts(musb);
+ if (!(musb->io.quirks & MUSB_PRESERVE_SESSION))
+ musb_writeb(musb->mregs, MUSB_DEVCTL, 0);
WARN_ON(!list_empty(&musb->pending_list));
spin_lock_irqsave(&musb->lock, flags);
@@ -2691,11 +2717,6 @@ static int musb_resume(struct device *dev)
mask = MUSB_DEVCTL_BDEVICE | MUSB_DEVCTL_FSDEV | MUSB_DEVCTL_LSDEV;
if ((devctl & mask) != (musb->context.devctl & mask))
musb->port1_status = 0;
- if (musb->need_finish_resume) {
- musb->need_finish_resume = 0;
- schedule_delayed_work(&musb->finish_resume_work,
- msecs_to_jiffies(USB_RESUME_TIMEOUT));
- }
/*
* The USB HUB code expects the device to be in RPM_ACTIVE once it came
@@ -2747,12 +2768,6 @@ static int musb_runtime_resume(struct device *dev)
musb_restore_context(musb);
- if (musb->need_finish_resume) {
- musb->need_finish_resume = 0;
- schedule_delayed_work(&musb->finish_resume_work,
- msecs_to_jiffies(USB_RESUME_TIMEOUT));
- }
-
spin_lock_irqsave(&musb->lock, flags);
error = musb_run_resume_work(musb);
if (error)
diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h
index 91817d77d59c..5b708be6d1d1 100644
--- a/drivers/usb/musb/musb_core.h
+++ b/drivers/usb/musb/musb_core.h
@@ -172,6 +172,7 @@ struct musb_io;
*/
struct musb_platform_ops {
+#define MUSB_PRESERVE_SESSION BIT(7)
#define MUSB_DMA_UX500 BIT(6)
#define MUSB_DMA_CPPI41 BIT(5)
#define MUSB_DMA_CPPI BIT(4)
@@ -216,6 +217,7 @@ struct musb_platform_ops {
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);
+ void (*clear_ep_rxintr)(struct musb *musb, int epnum);
};
/*
@@ -409,7 +411,6 @@ struct musb {
/* is_suspended means USB B_PERIPHERAL suspend */
unsigned is_suspended:1;
- unsigned need_finish_resume :1;
/* may_wakeup means remote wakeup is enabled */
unsigned may_wakeup:1;
@@ -626,4 +627,16 @@ static inline void musb_platform_post_root_reset_end(struct musb *musb)
musb->ops->post_root_reset_end(musb);
}
+static inline void musb_platform_clear_ep_rxintr(struct musb *musb, int epnum)
+{
+ if (musb->ops->clear_ep_rxintr)
+ musb->ops->clear_ep_rxintr(musb, epnum);
+}
+
+/*
+ * gets the "dr_mode" property from DT and converts it into musb_mode
+ * if the property is not found or not recognized returns MUSB_OTG
+ */
+extern enum musb_mode musb_get_mode(struct device *dev);
+
#endif /* __MUSB_CORE_H__ */
diff --git a/drivers/usb/musb/musb_cppi41.c b/drivers/usb/musb/musb_cppi41.c
index d4d7c56b48c7..355655f8a3fb 100644
--- a/drivers/usb/musb/musb_cppi41.c
+++ b/drivers/usb/musb/musb_cppi41.c
@@ -30,7 +30,6 @@ struct cppi41_dma_controller {
struct dma_controller controller;
struct cppi41_dma_channel rx_channel[MUSB_DMA_NUM_CHANNELS];
struct cppi41_dma_channel tx_channel[MUSB_DMA_NUM_CHANNELS];
- struct musb *musb;
struct hrtimer early_tx;
struct list_head early_tx_list;
u32 rx_mode;
@@ -45,7 +44,7 @@ static void save_rx_toggle(struct cppi41_dma_channel *cppi41_channel)
if (cppi41_channel->is_tx)
return;
- if (!is_host_active(cppi41_channel->controller->musb))
+ if (!is_host_active(cppi41_channel->controller->controller.musb))
return;
csr = musb_readw(cppi41_channel->hw_ep->regs, MUSB_RXCSR);
@@ -78,8 +77,7 @@ static void update_rx_toggle(struct cppi41_dma_channel *cppi41_channel)
if (!toggle && toggle == cppi41_channel->usb_toggle) {
csr |= MUSB_RXCSR_H_DATATOGGLE | MUSB_RXCSR_H_WR_DATATOGGLE;
musb_writew(cppi41_channel->hw_ep->regs, MUSB_RXCSR, csr);
- musb_dbg(cppi41_channel->controller->musb,
- "Restoring DATA1 toggle.");
+ musb_dbg(musb, "Restoring DATA1 toggle.");
}
cppi41_channel->usb_toggle = toggle;
@@ -99,7 +97,8 @@ static bool musb_is_tx_fifo_empty(struct musb_hw_ep *hw_ep)
return true;
}
-static void cppi41_dma_callback(void *private_data);
+static void cppi41_dma_callback(void *private_data,
+ const struct dmaengine_result *result);
static void cppi41_trans_done(struct cppi41_dma_channel *cppi41_channel)
{
@@ -154,7 +153,7 @@ static void cppi41_trans_done(struct cppi41_dma_channel *cppi41_channel)
if (WARN_ON(!dma_desc))
return;
- dma_desc->callback = cppi41_dma_callback;
+ dma_desc->callback_result = cppi41_dma_callback;
dma_desc->callback_param = &cppi41_channel->channel;
cppi41_channel->cookie = dma_desc->tx_submit(dma_desc);
trace_musb_cppi41_cont(cppi41_channel);
@@ -179,7 +178,7 @@ static enum hrtimer_restart cppi41_recheck_tx_req(struct hrtimer *timer)
controller = container_of(timer, struct cppi41_dma_controller,
early_tx);
- musb = controller->musb;
+ musb = controller->controller.musb;
spin_lock_irqsave(&musb->lock, flags);
list_for_each_entry_safe(cppi41_channel, n, &controller->early_tx_list,
@@ -197,15 +196,15 @@ static enum hrtimer_restart cppi41_recheck_tx_req(struct hrtimer *timer)
if (!list_empty(&controller->early_tx_list) &&
!hrtimer_is_queued(&controller->early_tx)) {
ret = HRTIMER_RESTART;
- hrtimer_forward_now(&controller->early_tx,
- ktime_set(0, 20 * NSEC_PER_USEC));
+ hrtimer_forward_now(&controller->early_tx, 20 * NSEC_PER_USEC);
}
spin_unlock_irqrestore(&musb->lock, flags);
return ret;
}
-static void cppi41_dma_callback(void *private_data)
+static void cppi41_dma_callback(void *private_data,
+ const struct dmaengine_result *result)
{
struct dma_channel *channel = private_data;
struct cppi41_dma_channel *cppi41_channel = channel->private_data;
@@ -218,6 +217,13 @@ static void cppi41_dma_callback(void *private_data)
int is_hs = 0;
bool empty;
+ controller = cppi41_channel->controller;
+ if (controller->controller.dma_callback)
+ controller->controller.dma_callback(&controller->controller);
+
+ if (result->result == DMA_TRANS_ABORTED)
+ return;
+
spin_lock_irqsave(&musb->lock, flags);
dmaengine_tx_status(cppi41_channel->dc, cppi41_channel->cookie,
@@ -232,8 +238,27 @@ static void cppi41_dma_callback(void *private_data)
transferred < cppi41_channel->packet_sz)
cppi41_channel->prog_len = 0;
- if (cppi41_channel->is_tx)
- empty = musb_is_tx_fifo_empty(hw_ep);
+ if (cppi41_channel->is_tx) {
+ u8 type;
+
+ if (is_host_active(musb))
+ type = hw_ep->out_qh->type;
+ else
+ type = hw_ep->ep_in.type;
+
+ if (type == USB_ENDPOINT_XFER_ISOC)
+ /*
+ * Don't use the early-TX-interrupt workaround below
+ * for Isoch transfter. Since Isoch are periodic
+ * transfer, by the time the next transfer is
+ * scheduled, the current one should be done already.
+ *
+ * This avoids audio playback underrun issue.
+ */
+ empty = true;
+ else
+ empty = musb_is_tx_fifo_empty(hw_ep);
+ }
if (!cppi41_channel->is_tx || empty) {
cppi41_trans_done(cppi41_channel);
@@ -250,8 +275,6 @@ static void cppi41_dma_callback(void *private_data)
* We spin on HS (no longer than than 25us and setup a timer on
* FS to check for the bit and complete the transfer.
*/
- controller = cppi41_channel->controller;
-
if (is_host_active(musb)) {
if (musb->port1_status & USB_PORT_STAT_HIGH_SPEED)
is_hs = 1;
@@ -280,9 +303,9 @@ static void cppi41_dma_callback(void *private_data)
unsigned long usecs = cppi41_channel->total_len / 10;
hrtimer_start_range_ns(&controller->early_tx,
- ktime_set(0, usecs * NSEC_PER_USEC),
- 20 * NSEC_PER_USEC,
- HRTIMER_MODE_REL);
+ usecs * NSEC_PER_USEC,
+ 20 * NSEC_PER_USEC,
+ HRTIMER_MODE_REL);
}
out:
@@ -303,6 +326,7 @@ static void cppi41_set_dma_mode(struct cppi41_dma_channel *cppi41_channel,
unsigned mode)
{
struct cppi41_dma_controller *controller = cppi41_channel->controller;
+ struct musb *musb = controller->controller.musb;
u32 port;
u32 new_mode;
u32 old_mode;
@@ -318,12 +342,10 @@ static void cppi41_set_dma_mode(struct cppi41_dma_channel *cppi41_channel,
return;
if (cppi41_channel->is_tx) {
controller->tx_mode = new_mode;
- musb_writel(controller->musb->ctrl_base, USB_CTRL_TX_MODE,
- new_mode);
+ musb_writel(musb->ctrl_base, USB_CTRL_TX_MODE, new_mode);
} else {
controller->rx_mode = new_mode;
- musb_writel(controller->musb->ctrl_base, USB_CTRL_RX_MODE,
- new_mode);
+ musb_writel(musb->ctrl_base, USB_CTRL_RX_MODE, new_mode);
}
}
@@ -342,7 +364,8 @@ static void cppi41_set_autoreq_mode(struct cppi41_dma_channel *cppi41_channel,
if (new_mode == old_mode)
return;
controller->auto_req = new_mode;
- musb_writel(controller->musb->ctrl_base, USB_CTRL_AUTOREQ, new_mode);
+ musb_writel(controller->controller.musb->ctrl_base, USB_CTRL_AUTOREQ,
+ new_mode);
}
static bool cppi41_configure_channel(struct dma_channel *channel,
@@ -353,7 +376,7 @@ static bool cppi41_configure_channel(struct dma_channel *channel,
struct dma_chan *dc = cppi41_channel->dc;
struct dma_async_tx_descriptor *dma_desc;
enum dma_transfer_direction direction;
- struct musb *musb = cppi41_channel->controller->musb;
+ struct musb *musb = cppi41_channel->controller->controller.musb;
unsigned use_gen_rndis = 0;
cppi41_channel->buf_addr = dma_addr;
@@ -402,7 +425,7 @@ static bool cppi41_configure_channel(struct dma_channel *channel,
if (!dma_desc)
return false;
- dma_desc->callback = cppi41_dma_callback;
+ dma_desc->callback_result = cppi41_dma_callback;
dma_desc->callback_param = channel;
cppi41_channel->cookie = dma_desc->tx_submit(dma_desc);
cppi41_channel->channel.rx_packet_done = false;
@@ -466,7 +489,7 @@ static int cppi41_dma_channel_program(struct dma_channel *channel,
BUG_ON(channel->status == MUSB_DMA_STATUS_UNKNOWN ||
channel->status == MUSB_DMA_STATUS_BUSY);
- if (is_host_active(cppi41_channel->controller->musb)) {
+ if (is_host_active(cppi41_channel->controller->controller.musb)) {
if (cppi41_channel->is_tx)
hb_mult = cppi41_channel->hw_ep->out_qh->hb_mult;
else
@@ -491,7 +514,7 @@ static int cppi41_is_compatible(struct dma_channel *channel, u16 maxpacket,
{
struct cppi41_dma_channel *cppi41_channel = channel->private_data;
struct cppi41_dma_controller *controller = cppi41_channel->controller;
- struct musb *musb = controller->musb;
+ struct musb *musb = controller->controller.musb;
if (is_host_active(musb)) {
WARN_ON(1);
@@ -509,7 +532,7 @@ static int cppi41_dma_channel_abort(struct dma_channel *channel)
{
struct cppi41_dma_channel *cppi41_channel = channel->private_data;
struct cppi41_dma_controller *controller = cppi41_channel->controller;
- struct musb *musb = controller->musb;
+ struct musb *musb = controller->controller.musb;
void __iomem *epio = cppi41_channel->hw_ep->regs;
int tdbit;
int ret;
@@ -594,7 +617,7 @@ static void cppi41_dma_controller_stop(struct cppi41_dma_controller *controller)
static int cppi41_dma_controller_start(struct cppi41_dma_controller *controller)
{
- struct musb *musb = controller->musb;
+ struct musb *musb = controller->controller.musb;
struct device *dev = musb->controller;
struct device_node *np = dev->parent->of_node;
struct cppi41_dma_channel *cppi41_channel;
@@ -689,13 +712,13 @@ cppi41_dma_controller_create(struct musb *musb, void __iomem *base)
hrtimer_init(&controller->early_tx, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
controller->early_tx.function = cppi41_recheck_tx_req;
INIT_LIST_HEAD(&controller->early_tx_list);
- controller->musb = musb;
controller->controller.channel_alloc = cppi41_dma_channel_allocate;
controller->controller.channel_release = cppi41_dma_channel_release;
controller->controller.channel_program = cppi41_dma_channel_program;
controller->controller.channel_abort = cppi41_dma_channel_abort;
controller->controller.is_compatible = cppi41_is_compatible;
+ controller->controller.musb = musb;
ret = cppi41_dma_controller_start(controller);
if (ret)
diff --git a/drivers/usb/musb/musb_debugfs.c b/drivers/usb/musb/musb_debugfs.c
index 9b22d946c089..952733ceaac8 100644
--- a/drivers/usb/musb/musb_debugfs.c
+++ b/drivers/usb/musb/musb_debugfs.c
@@ -37,7 +37,7 @@
#include <linux/debugfs.h>
#include <linux/seq_file.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include "musb_core.h"
#include "musb_debug.h"
@@ -114,6 +114,7 @@ static int musb_regdump_show(struct seq_file *s, void *unused)
unsigned i;
seq_printf(s, "MUSB (M)HDRC Register Dump\n");
+ pm_runtime_get_sync(musb->controller);
for (i = 0; i < ARRAY_SIZE(musb_regmap); i++) {
switch (musb_regmap[i].size) {
@@ -132,6 +133,8 @@ static int musb_regdump_show(struct seq_file *s, void *unused)
}
}
+ pm_runtime_mark_last_busy(musb->controller);
+ pm_runtime_put_autosuspend(musb->controller);
return 0;
}
@@ -145,30 +148,39 @@ static int musb_test_mode_show(struct seq_file *s, void *unused)
struct musb *musb = s->private;
unsigned test;
+ pm_runtime_get_sync(musb->controller);
test = musb_readb(musb->mregs, MUSB_TESTMODE);
+ pm_runtime_mark_last_busy(musb->controller);
+ pm_runtime_put_autosuspend(musb->controller);
- if (test & MUSB_TEST_FORCE_HOST)
+ if (test == (MUSB_TEST_FORCE_HOST | MUSB_TEST_FORCE_FS))
+ seq_printf(s, "force host full-speed\n");
+
+ else if (test == (MUSB_TEST_FORCE_HOST | MUSB_TEST_FORCE_HS))
+ seq_printf(s, "force host high-speed\n");
+
+ else if (test == MUSB_TEST_FORCE_HOST)
seq_printf(s, "force host\n");
- if (test & MUSB_TEST_FIFO_ACCESS)
+ else if (test == MUSB_TEST_FIFO_ACCESS)
seq_printf(s, "fifo access\n");
- if (test & MUSB_TEST_FORCE_FS)
+ else if (test == MUSB_TEST_FORCE_FS)
seq_printf(s, "force full-speed\n");
- if (test & MUSB_TEST_FORCE_HS)
+ else if (test == MUSB_TEST_FORCE_HS)
seq_printf(s, "force high-speed\n");
- if (test & MUSB_TEST_PACKET)
+ else if (test == MUSB_TEST_PACKET)
seq_printf(s, "test packet\n");
- if (test & MUSB_TEST_K)
+ else if (test == MUSB_TEST_K)
seq_printf(s, "test K\n");
- if (test & MUSB_TEST_J)
+ else if (test == MUSB_TEST_J)
seq_printf(s, "test J\n");
- if (test & MUSB_TEST_SE0_NAK)
+ else if (test == MUSB_TEST_SE0_NAK)
seq_printf(s, "test SE0 NAK\n");
return 0;
@@ -192,13 +204,14 @@ static ssize_t musb_test_mode_write(struct file *file,
struct seq_file *s = file->private_data;
struct musb *musb = s->private;
u8 test;
- char buf[18];
+ char buf[24];
+ pm_runtime_get_sync(musb->controller);
test = musb_readb(musb->mregs, MUSB_TESTMODE);
if (test) {
dev_err(musb->controller, "Error: test mode is already set. "
"Please do USB Bus Reset to start a new test.\n");
- return count;
+ goto ret;
}
memset(buf, 0x00, sizeof(buf));
@@ -206,34 +219,43 @@ static ssize_t musb_test_mode_write(struct file *file,
if (copy_from_user(buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
return -EFAULT;
- if (strstarts(buf, "force host"))
+ if (strstarts(buf, "force host full-speed"))
+ test = MUSB_TEST_FORCE_HOST | MUSB_TEST_FORCE_FS;
+
+ else if (strstarts(buf, "force host high-speed"))
+ test = MUSB_TEST_FORCE_HOST | MUSB_TEST_FORCE_HS;
+
+ else if (strstarts(buf, "force host"))
test = MUSB_TEST_FORCE_HOST;
- if (strstarts(buf, "fifo access"))
+ else if (strstarts(buf, "fifo access"))
test = MUSB_TEST_FIFO_ACCESS;
- if (strstarts(buf, "force full-speed"))
+ else if (strstarts(buf, "force full-speed"))
test = MUSB_TEST_FORCE_FS;
- if (strstarts(buf, "force high-speed"))
+ else if (strstarts(buf, "force high-speed"))
test = MUSB_TEST_FORCE_HS;
- if (strstarts(buf, "test packet")) {
+ else if (strstarts(buf, "test packet")) {
test = MUSB_TEST_PACKET;
musb_load_testpacket(musb);
}
- if (strstarts(buf, "test K"))
+ else if (strstarts(buf, "test K"))
test = MUSB_TEST_K;
- if (strstarts(buf, "test J"))
+ else if (strstarts(buf, "test J"))
test = MUSB_TEST_J;
- if (strstarts(buf, "test SE0 NAK"))
+ else if (strstarts(buf, "test SE0 NAK"))
test = MUSB_TEST_SE0_NAK;
musb_writeb(musb->mregs, MUSB_TESTMODE, test);
+ret:
+ pm_runtime_mark_last_busy(musb->controller);
+ pm_runtime_put_autosuspend(musb->controller);
return count;
}
@@ -254,8 +276,13 @@ static int musb_softconnect_show(struct seq_file *s, void *unused)
switch (musb->xceiv->otg->state) {
case OTG_STATE_A_HOST:
case OTG_STATE_A_WAIT_BCON:
+ pm_runtime_get_sync(musb->controller);
+
reg = musb_readb(musb->mregs, MUSB_DEVCTL);
connect = reg & MUSB_DEVCTL_SESSION ? 1 : 0;
+
+ pm_runtime_mark_last_busy(musb->controller);
+ pm_runtime_put_autosuspend(musb->controller);
break;
default:
connect = -1;
@@ -284,6 +311,7 @@ static ssize_t musb_softconnect_write(struct file *file,
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
return -EFAULT;
+ pm_runtime_get_sync(musb->controller);
if (!strncmp(buf, "0", 1)) {
switch (musb->xceiv->otg->state) {
case OTG_STATE_A_HOST:
@@ -314,6 +342,8 @@ static ssize_t musb_softconnect_write(struct file *file,
}
}
+ pm_runtime_mark_last_busy(musb->controller);
+ pm_runtime_put_autosuspend(musb->controller);
return count;
}
diff --git a/drivers/usb/musb/musb_dma.h b/drivers/usb/musb/musb_dma.h
index 46357e183b4c..04c3bd86bd62 100644
--- a/drivers/usb/musb/musb_dma.h
+++ b/drivers/usb/musb/musb_dma.h
@@ -173,6 +173,7 @@ dma_channel_status(struct dma_channel *c)
/**
* struct dma_controller - A DMA Controller.
+ * @musb: the usb controller
* @start: call this to start a DMA controller;
* return 0 on success, else negative errno
* @stop: call this to stop a DMA controller
@@ -181,10 +182,13 @@ dma_channel_status(struct dma_channel *c)
* @channel_release: call this to release a DMA channel
* @channel_abort: call this to abort a pending DMA transaction,
* returning it to FREE (but allocated) state
+ * @dma_callback: invoked on DMA completion, useful to run platform
+ * code such IRQ acknowledgment.
*
* Controllers manage dma channels.
*/
struct dma_controller {
+ struct musb *musb;
struct dma_channel *(*channel_alloc)(struct dma_controller *,
struct musb_hw_ep *, u8 is_tx);
void (*channel_release)(struct dma_channel *);
@@ -196,6 +200,7 @@ struct dma_controller {
int (*is_compatible)(struct dma_channel *channel,
u16 maxpacket,
void *buf, u32 length);
+ void (*dma_callback)(struct dma_controller *);
};
/* called after channel_program(), may indicate a fault */
diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c
index feae1561b9ab..9c7ee26ef388 100644
--- a/drivers/usb/musb/musb_dsps.c
+++ b/drivers/usb/musb/musb_dsps.c
@@ -118,9 +118,11 @@ struct dsps_glue {
struct device *dev;
struct platform_device *musb; /* child musb pdev */
const struct dsps_musb_wrapper *wrp; /* wrapper register offsets */
+ int vbus_irq; /* optional vbus irq */
struct timer_list timer; /* otg_workaround timer */
unsigned long last_timer; /* last timer data for each instance */
bool sw_babble_enabled;
+ void __iomem *usbss_base;
struct dsps_context context;
struct debugfs_regset32 regset;
@@ -145,6 +147,36 @@ static const struct debugfs_reg32 dsps_musb_regs[] = {
{ "mode", 0xe8 },
};
+static void dsps_mod_timer(struct dsps_glue *glue, int wait_ms)
+{
+ int wait;
+
+ if (wait_ms < 0)
+ wait = msecs_to_jiffies(glue->wrp->poll_timeout);
+ else
+ wait = msecs_to_jiffies(wait_ms);
+
+ mod_timer(&glue->timer, jiffies + wait);
+}
+
+/*
+ * If no vbus irq from the PMIC is configured, we need to poll VBUS status.
+ */
+static void dsps_mod_timer_optional(struct dsps_glue *glue)
+{
+ if (glue->vbus_irq)
+ return;
+
+ dsps_mod_timer(glue, -1);
+}
+
+/* USBSS / USB AM335x */
+#define USBSS_IRQ_STATUS 0x28
+#define USBSS_IRQ_ENABLER 0x2c
+#define USBSS_IRQ_CLEARR 0x30
+
+#define USBSS_IRQ_PD_COMP (1 << 2)
+
/**
* dsps_musb_enable - enable interrupts
*/
@@ -167,8 +199,7 @@ static void dsps_musb_enable(struct musb *musb)
/* 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)
- mod_timer(&glue->timer, jiffies +
- msecs_to_jiffies(wrp->poll_timeout));
+ dsps_mod_timer(glue, -1);
}
/**
@@ -186,7 +217,6 @@ static void dsps_musb_disable(struct musb *musb)
musb_writel(reg_base, wrp->epintr_clear,
wrp->txep_bitmap | wrp->rxep_bitmap);
del_timer_sync(&glue->timer);
- musb_writeb(musb->mregs, MUSB_DEVCTL, 0);
}
/* Caller must take musb->lock */
@@ -199,6 +229,9 @@ static int dsps_check_status(struct musb *musb, void *unused)
u8 devctl;
int skip_session = 0;
+ if (glue->vbus_irq)
+ del_timer(&glue->timer);
+
/*
* We poll because DSPS IP's won't expose several OTG-critical
* status change events (from the transceiver) otherwise.
@@ -209,8 +242,7 @@ static int dsps_check_status(struct musb *musb, void *unused)
switch (musb->xceiv->otg->state) {
case OTG_STATE_A_WAIT_VRISE:
- mod_timer(&glue->timer, jiffies +
- msecs_to_jiffies(wrp->poll_timeout));
+ dsps_mod_timer_optional(glue);
break;
case OTG_STATE_A_WAIT_BCON:
musb_writeb(musb->mregs, MUSB_DEVCTL, 0);
@@ -219,17 +251,19 @@ static int dsps_check_status(struct musb *musb, void *unused)
case OTG_STATE_A_IDLE:
case OTG_STATE_B_IDLE:
- if (devctl & MUSB_DEVCTL_BDEVICE) {
- musb->xceiv->otg->state = OTG_STATE_B_IDLE;
- MUSB_DEV_MODE(musb);
- } else {
- musb->xceiv->otg->state = OTG_STATE_A_IDLE;
- MUSB_HST_MODE(musb);
+ if (!glue->vbus_irq) {
+ if (devctl & MUSB_DEVCTL_BDEVICE) {
+ musb->xceiv->otg->state = OTG_STATE_B_IDLE;
+ MUSB_DEV_MODE(musb);
+ } else {
+ musb->xceiv->otg->state = OTG_STATE_A_IDLE;
+ MUSB_HST_MODE(musb);
+ }
+ if (!(devctl & MUSB_DEVCTL_SESSION) && !skip_session)
+ musb_writeb(mregs, MUSB_DEVCTL,
+ MUSB_DEVCTL_SESSION);
}
- if (!(devctl & MUSB_DEVCTL_SESSION) && !skip_session)
- musb_writeb(mregs, MUSB_DEVCTL, MUSB_DEVCTL_SESSION);
- mod_timer(&glue->timer, jiffies +
- msecs_to_jiffies(wrp->poll_timeout));
+ dsps_mod_timer_optional(glue);
break;
case OTG_STATE_A_WAIT_VFALL:
musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE;
@@ -267,6 +301,17 @@ static void otg_timer(unsigned long _musb)
pm_runtime_put_autosuspend(dev);
}
+static void dsps_musb_clear_ep_rxintr(struct musb *musb, int epnum)
+{
+ u32 epintr;
+ struct dsps_glue *glue = dev_get_drvdata(musb->controller->parent);
+ const struct dsps_musb_wrapper *wrp = glue->wrp;
+
+ /* musb->lock might already been held */
+ epintr = (1 << epnum) << wrp->rxep_shift;
+ musb_writel(musb->ctrl_base, wrp->epintr_status, epintr);
+}
+
static irqreturn_t dsps_interrupt(int irq, void *hci)
{
struct musb *musb = hci;
@@ -321,15 +366,13 @@ static irqreturn_t dsps_interrupt(int irq, void *hci)
*/
musb->int_usb &= ~MUSB_INTR_VBUSERROR;
musb->xceiv->otg->state = OTG_STATE_A_WAIT_VFALL;
- mod_timer(&glue->timer, jiffies +
- msecs_to_jiffies(wrp->poll_timeout));
+ dsps_mod_timer_optional(glue);
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;
- mod_timer(&glue->timer, jiffies +
- msecs_to_jiffies(wrp->poll_timeout));
+ dsps_mod_timer_optional(glue);
} else {
musb->is_active = 0;
MUSB_DEV_MODE(musb);
@@ -353,8 +396,7 @@ static irqreturn_t dsps_interrupt(int irq, void *hci)
switch (musb->xceiv->otg->state) {
case OTG_STATE_B_IDLE:
case OTG_STATE_A_WAIT_BCON:
- mod_timer(&glue->timer, jiffies +
- msecs_to_jiffies(wrp->poll_timeout));
+ dsps_mod_timer_optional(glue);
break;
default:
break;
@@ -458,8 +500,7 @@ static int dsps_musb_init(struct musb *musb)
musb_writeb(musb->mregs, MUSB_BABBLE_CTL, val);
}
- mod_timer(&glue->timer, jiffies +
- msecs_to_jiffies(glue->wrp->poll_timeout));
+ dsps_mod_timer(glue, -1);
return dsps_musb_dbg_init(musb, glue);
}
@@ -608,20 +649,83 @@ static void dsps_read_fifo32(struct musb_hw_ep *hw_ep, u16 len, u8 *dst)
}
}
+#ifdef CONFIG_USB_TI_CPPI41_DMA
+static void dsps_dma_controller_callback(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;
+ u32 status;
+
+ status = musb_readl(usbss_base, USBSS_IRQ_STATUS);
+ if (status & USBSS_IRQ_PD_COMP)
+ musb_writel(usbss_base, USBSS_IRQ_STATUS, USBSS_IRQ_PD_COMP);
+}
+
+static struct dma_controller *
+dsps_dma_controller_create(struct musb *musb, void __iomem *base)
+{
+ struct dma_controller *controller;
+ struct dsps_glue *glue = dev_get_drvdata(musb->controller->parent);
+ void __iomem *usbss_base = glue->usbss_base;
+
+ controller = cppi41_dma_controller_create(musb, base);
+ if (IS_ERR_OR_NULL(controller))
+ return controller;
+
+ musb_writel(usbss_base, USBSS_IRQ_ENABLER, USBSS_IRQ_PD_COMP);
+ controller->dma_callback = dsps_dma_controller_callback;
+
+ 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)
+{
+ void __iomem *usbss_base = glue->usbss_base;
+
+ musb_writel(usbss_base, USBSS_IRQ_CLEARR, USBSS_IRQ_PD_COMP);
+}
+
+static void dsps_dma_controller_resume(struct dsps_glue *glue)
+{
+ void __iomem *usbss_base = glue->usbss_base;
+
+ musb_writel(usbss_base, USBSS_IRQ_ENABLER, USBSS_IRQ_PD_COMP);
+}
+#endif
+#else /* CONFIG_USB_TI_CPPI41_DMA */
+#ifdef CONFIG_PM_SLEEP
+static void dsps_dma_controller_suspend(struct dsps_glue *glue) {}
+static void dsps_dma_controller_resume(struct dsps_glue *glue) {}
+#endif
+#endif /* CONFIG_USB_TI_CPPI41_DMA */
+
static struct musb_platform_ops dsps_ops = {
.quirks = MUSB_DMA_CPPI41 | MUSB_INDEXED_EP,
.init = dsps_musb_init,
.exit = dsps_musb_exit,
#ifdef CONFIG_USB_TI_CPPI41_DMA
- .dma_init = cppi41_dma_controller_create,
- .dma_exit = cppi41_dma_controller_destroy,
+ .dma_init = dsps_dma_controller_create,
+ .dma_exit = dsps_dma_controller_destroy,
#endif
.enable = dsps_musb_enable,
.disable = dsps_musb_disable,
.set_mode = dsps_musb_set_mode,
.recover = dsps_musb_recover,
+ .clear_ep_rxintr = dsps_musb_clear_ep_rxintr,
};
static u64 musb_dmamask = DMA_BIT_MASK(32);
@@ -684,7 +788,8 @@ static int dsps_create_musb_pdev(struct dsps_glue *glue,
resources[1] = *res;
/* allocate the child platform device */
- musb = platform_device_alloc("musb-hdrc", PLATFORM_DEVID_AUTO);
+ musb = platform_device_alloc("musb-hdrc",
+ (resources[0].start & 0xFFF) == 0x400 ? 0 : 1);
if (!musb) {
dev_err(dev, "failed to allocate musb device\n");
return -ENOMEM;
@@ -753,6 +858,47 @@ err:
return ret;
}
+static irqreturn_t dsps_vbus_threaded_irq(int irq, void *priv)
+{
+ struct dsps_glue *glue = priv;
+ struct musb *musb = platform_get_drvdata(glue->musb);
+
+ if (!musb)
+ return IRQ_NONE;
+
+ dev_dbg(glue->dev, "VBUS interrupt\n");
+ dsps_mod_timer(glue, 0);
+
+ return IRQ_HANDLED;
+}
+
+static int dsps_setup_optional_vbus_irq(struct platform_device *pdev,
+ struct dsps_glue *glue)
+{
+ int error;
+
+ glue->vbus_irq = platform_get_irq_byname(pdev, "vbus");
+ if (glue->vbus_irq == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ if (glue->vbus_irq <= 0) {
+ glue->vbus_irq = 0;
+ return 0;
+ }
+
+ error = devm_request_threaded_irq(glue->dev, glue->vbus_irq,
+ NULL, dsps_vbus_threaded_irq,
+ IRQF_ONESHOT,
+ "vbus", glue);
+ if (error) {
+ glue->vbus_irq = 0;
+ return error;
+ }
+ dev_dbg(glue->dev, "VBUS irq %i configured\n", glue->vbus_irq);
+
+ return 0;
+}
+
static int dsps_probe(struct platform_device *pdev)
{
const struct of_device_id *match;
@@ -780,6 +926,15 @@ static int dsps_probe(struct platform_device *pdev)
glue->dev = &pdev->dev;
glue->wrp = wrp;
+ glue->usbss_base = of_iomap(pdev->dev.parent->of_node, 0);
+ if (!glue->usbss_base)
+ return -ENXIO;
+
+ if (usb_get_dr_mode(&pdev->dev) == USB_DR_MODE_PERIPHERAL) {
+ ret = dsps_setup_optional_vbus_irq(pdev, glue);
+ if (ret)
+ goto err_iounmap;
+ }
platform_set_drvdata(pdev, glue);
pm_runtime_enable(&pdev->dev);
@@ -791,6 +946,8 @@ static int dsps_probe(struct platform_device *pdev)
err:
pm_runtime_disable(&pdev->dev);
+err_iounmap:
+ iounmap(glue->usbss_base);
return ret;
}
@@ -801,6 +958,7 @@ static int dsps_remove(struct platform_device *pdev)
platform_device_unregister(glue->musb);
pm_runtime_disable(&pdev->dev);
+ iounmap(glue->usbss_base);
return 0;
}
@@ -868,6 +1026,8 @@ static int dsps_suspend(struct device *dev)
glue->context.tx_mode = musb_readl(mbase, wrp->tx_mode);
glue->context.rx_mode = musb_readl(mbase, wrp->rx_mode);
+ dsps_dma_controller_suspend(glue);
+
return 0;
}
@@ -881,6 +1041,8 @@ static int dsps_resume(struct device *dev)
if (!musb)
return 0;
+ dsps_dma_controller_resume(glue);
+
mbase = musb->ctrl_base;
musb_writel(mbase, wrp->control, glue->context.control);
musb_writel(mbase, wrp->epintr_set, glue->context.epintr);
@@ -891,8 +1053,7 @@ static int dsps_resume(struct device *dev)
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)
- mod_timer(&glue->timer, jiffies +
- msecs_to_jiffies(wrp->poll_timeout));
+ dsps_mod_timer(glue, -1);
return 0;
}
diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c
index a55173c9e564..1acc4864f9f6 100644
--- a/drivers/usb/musb/musb_gadget.c
+++ b/drivers/usb/musb/musb_gadget.c
@@ -974,8 +974,8 @@ static int musb_gadget_enable(struct usb_ep *ep,
goto fail;
/* REVISIT this rules out high bandwidth periodic transfers */
- tmp = usb_endpoint_maxp(desc);
- if (tmp & ~0x07ff) {
+ tmp = usb_endpoint_maxp_mult(desc) - 1;
+ if (tmp) {
int ok;
if (usb_endpoint_dir_in(desc))
@@ -987,12 +987,12 @@ static int musb_gadget_enable(struct usb_ep *ep,
musb_dbg(musb, "no support for high bandwidth ISO");
goto fail;
}
- musb_ep->hb_mult = (tmp >> 11) & 3;
+ musb_ep->hb_mult = tmp;
} else {
musb_ep->hb_mult = 0;
}
- musb_ep->packet_sz = tmp & 0x7ff;
+ musb_ep->packet_sz = usb_endpoint_maxp(desc);
tmp = musb_ep->packet_sz * (musb_ep->hb_mult + 1);
/* enable the interrupts for the endpoint, set the endpoint
diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
index 53bc4ceefe89..ac3a4952abb4 100644
--- a/drivers/usb/musb/musb_host.c
+++ b/drivers/usb/musb/musb_host.c
@@ -2237,7 +2237,7 @@ static int musb_urb_enqueue(
* Some musb cores don't support high bandwidth ISO transfers; and
* we don't (yet!) support high bandwidth interrupt transfers.
*/
- qh->hb_mult = 1 + ((qh->maxpacket >> 11) & 0x03);
+ qh->hb_mult = usb_endpoint_maxp_mult(epd);
if (qh->hb_mult > 1) {
int ok = (qh->type == USB_ENDPOINT_XFER_ISOC);
@@ -2374,12 +2374,11 @@ static int musb_cleanup_urb(struct urb *urb, struct musb_qh *qh)
int is_in = usb_pipein(urb->pipe);
int status = 0;
u16 csr;
+ struct dma_channel *dma = NULL;
musb_ep_select(regs, hw_end);
if (is_dma_capable()) {
- struct dma_channel *dma;
-
dma = is_in ? ep->rx_channel : ep->tx_channel;
if (dma) {
status = ep->musb->dma_controller->channel_abort(dma);
@@ -2395,10 +2394,9 @@ static int musb_cleanup_urb(struct urb *urb, struct musb_qh *qh)
/* giveback saves bulk toggle */
csr = musb_h_flush_rxfifo(ep, 0);
- /* REVISIT we still get an irq; should likely clear the
- * endpoint's irq status here to avoid bogus irqs.
- * clearing that status is platform-specific...
- */
+ /* clear the endpoint's irq status here to avoid bogus irqs */
+ if (is_dma_capable() && dma)
+ musb_platform_clear_ep_rxintr(musb, ep->epnum);
} else if (ep->epnum) {
musb_h_tx_flush_fifo(ep);
csr = musb_readw(epio, MUSB_TXCSR);
diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c
index 61b5f1c3c5bc..0b4595439d51 100644
--- a/drivers/usb/musb/musb_virthub.c
+++ b/drivers/usb/musb/musb_virthub.c
@@ -132,7 +132,6 @@ void musb_port_suspend(struct musb *musb, bool do_suspend)
musb_dbg(musb, "Root port resuming, power %02x", power);
- /* later, GetPortStatus will stop RESUME signaling */
musb->port1_status |= MUSB_PORT_STAT_RESUME;
schedule_delayed_work(&musb->finish_resume_work,
msecs_to_jiffies(USB_RESUME_TIMEOUT));
diff --git a/drivers/usb/musb/musbhsdma.h b/drivers/usb/musb/musbhsdma.h
index f7b13fd25257..a3dcbd55e436 100644
--- a/drivers/usb/musb/musbhsdma.h
+++ b/drivers/usb/musb/musbhsdma.h
@@ -157,5 +157,5 @@ struct musb_dma_controller {
void __iomem *base;
u8 channel_count;
u8 used_channels;
- u8 irq;
+ int irq;
};
diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c
index e8be8e39ab8f..456f3e6ecf03 100644
--- a/drivers/usb/musb/omap2430.c
+++ b/drivers/usb/musb/omap2430.c
@@ -277,12 +277,12 @@ static int omap2430_musb_init(struct musb *musb)
if (status == -ENXIO)
return status;
- pr_err("HS USB OTG: no transceiver configured\n");
+ dev_dbg(dev, "HS USB OTG: no transceiver configured\n");
return -EPROBE_DEFER;
}
if (IS_ERR(musb->phy)) {
- pr_err("HS USB OTG: no PHY configured\n");
+ dev_err(dev, "HS USB OTG: no PHY configured\n");
return PTR_ERR(musb->phy);
}
musb->isr = omap2430_musb_interrupt;
@@ -301,7 +301,7 @@ static int omap2430_musb_init(struct musb *musb)
musb_writel(musb->mregs, OTG_INTERFSEL, l);
- pr_debug("HS USB OTG: revision 0x%x, sysconfig 0x%02x, "
+ dev_dbg(dev, "HS USB OTG: revision 0x%x, sysconfig 0x%02x, "
"sysstatus 0x%x, intrfsel 0x%x, simenable 0x%x\n",
musb_readl(musb->mregs, OTG_REVISION),
musb_readl(musb->mregs, OTG_SYSCONFIG),
@@ -575,7 +575,7 @@ static int omap2430_runtime_resume(struct device *dev)
return 0;
}
-static struct dev_pm_ops omap2430_pm_ops = {
+static const struct dev_pm_ops omap2430_pm_ops = {
.runtime_suspend = omap2430_runtime_suspend,
.runtime_resume = omap2430_runtime_resume,
};
diff --git a/drivers/usb/musb/sunxi.c b/drivers/usb/musb/sunxi.c
index 1408245be18e..c9a09b5bb6e5 100644
--- a/drivers/usb/musb/sunxi.c
+++ b/drivers/usb/musb/sunxi.c
@@ -186,16 +186,6 @@ static irqreturn_t sunxi_musb_interrupt(int irq, void *__hci)
if (musb->int_usb)
writeb(musb->int_usb, musb->mregs + SUNXI_MUSB_INTRUSB);
- /*
- * sunxi musb often signals babble on low / full speed device
- * disconnect, without ever raising MUSB_INTR_DISCONNECT, since
- * normally babble never happens treat it as disconnect.
- */
- if ((musb->int_usb & MUSB_INTR_BABBLE) && is_host_active(musb)) {
- musb->int_usb &= ~MUSB_INTR_BABBLE;
- musb->int_usb |= MUSB_INTR_DISCONNECT;
- }
-
if ((musb->int_usb & MUSB_INTR_RESET) && !is_host_active(musb)) {
/* ep0 FADDR must be 0 when (re)entering peripheral mode */
musb_ep_select(musb->mregs, 0);
@@ -261,14 +251,14 @@ static int sunxi_musb_init(struct musb *musb)
writeb(SUNXI_MUSB_VEND0_PIO_MODE, musb->mregs + SUNXI_MUSB_VEND0);
/* Register notifier before calling phy_init() */
- ret = extcon_register_notifier(glue->extcon, EXTCON_USB_HOST,
- &glue->host_nb);
+ ret = devm_extcon_register_notifier(glue->dev, glue->extcon,
+ EXTCON_USB_HOST, &glue->host_nb);
if (ret)
goto error_reset_assert;
ret = phy_init(glue->phy);
if (ret)
- goto error_unregister_notifier;
+ goto error_reset_assert;
musb->isr = sunxi_musb_interrupt;
@@ -277,9 +267,6 @@ static int sunxi_musb_init(struct musb *musb)
return 0;
-error_unregister_notifier:
- extcon_unregister_notifier(glue->extcon, EXTCON_USB_HOST,
- &glue->host_nb);
error_reset_assert:
if (test_bit(SUNXI_MUSB_FL_HAS_RESET, &glue->flags))
reset_control_assert(glue->rst);
@@ -303,9 +290,6 @@ static int sunxi_musb_exit(struct musb *musb)
phy_exit(glue->phy);
- extcon_unregister_notifier(glue->extcon, EXTCON_USB_HOST,
- &glue->host_nb);
-
if (test_bit(SUNXI_MUSB_FL_HAS_RESET, &glue->flags))
reset_control_assert(glue->rst);
@@ -390,6 +374,20 @@ static int sunxi_musb_set_mode(struct musb *musb, u8 mode)
return 0;
}
+static int sunxi_musb_recover(struct musb *musb)
+{
+ struct sunxi_glue *glue = dev_get_drvdata(musb->controller->parent);
+
+ /*
+ * Schedule a phy_set_mode with the current glue->phy_mode value,
+ * this will force end the current session.
+ */
+ set_bit(SUNXI_MUSB_FL_PHY_MODE_PEND, &glue->flags);
+ schedule_work(&glue->work);
+
+ return 0;
+}
+
/*
* sunxi musb register layout
* 0x00 - 0x17 fifo regs, 1 long per fifo
@@ -618,6 +616,7 @@ static const struct musb_platform_ops sunxi_musb_ops = {
.dma_init = sunxi_musb_dma_controller_create,
.dma_exit = sunxi_musb_dma_controller_destroy,
.set_mode = sunxi_musb_set_mode,
+ .recover = sunxi_musb_recover,
.set_vbus = sunxi_musb_set_vbus,
.pre_root_reset_end = sunxi_musb_pre_root_reset_end,
.post_root_reset_end = sunxi_musb_post_root_reset_end,
@@ -640,7 +639,21 @@ static struct musb_fifo_cfg sunxi_musb_mode_cfg[] = {
MUSB_EP_FIFO_SINGLE(5, FIFO_RX, 512),
};
-static struct musb_hdrc_config sunxi_musb_hdrc_config = {
+/* H3/V3s OTG supports only 4 endpoints */
+#define SUNXI_MUSB_MAX_EP_NUM_H3 5
+
+static struct musb_fifo_cfg sunxi_musb_mode_cfg_h3[] = {
+ MUSB_EP_FIFO_SINGLE(1, FIFO_TX, 512),
+ MUSB_EP_FIFO_SINGLE(1, FIFO_RX, 512),
+ MUSB_EP_FIFO_SINGLE(2, FIFO_TX, 512),
+ MUSB_EP_FIFO_SINGLE(2, FIFO_RX, 512),
+ MUSB_EP_FIFO_SINGLE(3, FIFO_TX, 512),
+ MUSB_EP_FIFO_SINGLE(3, FIFO_RX, 512),
+ MUSB_EP_FIFO_SINGLE(4, FIFO_TX, 512),
+ MUSB_EP_FIFO_SINGLE(4, FIFO_RX, 512),
+};
+
+static const struct musb_hdrc_config sunxi_musb_hdrc_config = {
.fifo_cfg = sunxi_musb_mode_cfg,
.fifo_cfg_size = ARRAY_SIZE(sunxi_musb_mode_cfg),
.multipoint = true,
@@ -651,6 +664,18 @@ static struct musb_hdrc_config sunxi_musb_hdrc_config = {
.dma = 0,
};
+static struct musb_hdrc_config sunxi_musb_hdrc_config_h3 = {
+ .fifo_cfg = sunxi_musb_mode_cfg_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,
+};
+
+
static int sunxi_musb_probe(struct platform_device *pdev)
{
struct musb_hdrc_platform_data pdata;
@@ -693,7 +718,10 @@ static int sunxi_musb_probe(struct platform_device *pdev)
return -EINVAL;
}
pdata.platform_ops = &sunxi_musb_ops;
- pdata.config = &sunxi_musb_hdrc_config;
+ if (!of_device_is_compatible(np, "allwinner,sun8i-h3-musb"))
+ pdata.config = &sunxi_musb_hdrc_config;
+ else
+ pdata.config = &sunxi_musb_hdrc_config_h3;
glue->dev = &pdev->dev;
INIT_WORK(&glue->work, sunxi_musb_work);
@@ -705,7 +733,8 @@ static int sunxi_musb_probe(struct platform_device *pdev)
if (of_device_is_compatible(np, "allwinner,sun6i-a31-musb"))
set_bit(SUNXI_MUSB_FL_HAS_RESET, &glue->flags);
- if (of_device_is_compatible(np, "allwinner,sun8i-a33-musb")) {
+ if (of_device_is_compatible(np, "allwinner,sun8i-a33-musb") ||
+ of_device_is_compatible(np, "allwinner,sun8i-h3-musb")) {
set_bit(SUNXI_MUSB_FL_HAS_RESET, &glue->flags);
set_bit(SUNXI_MUSB_FL_NO_CONFIGDATA, &glue->flags);
}
@@ -799,6 +828,7 @@ static const struct of_device_id sunxi_musb_match[] = {
{ .compatible = "allwinner,sun4i-a10-musb", },
{ .compatible = "allwinner,sun6i-a31-musb", },
{ .compatible = "allwinner,sun8i-a33-musb", },
+ { .compatible = "allwinner,sun8i-h3-musb", },
{}
};
MODULE_DEVICE_TABLE(of, sunxi_musb_match);
diff --git a/drivers/usb/musb/tusb6010_omap.c b/drivers/usb/musb/tusb6010_omap.c
index e6959ccb4453..8b43c4b99f04 100644
--- a/drivers/usb/musb/tusb6010_omap.c
+++ b/drivers/usb/musb/tusb6010_omap.c
@@ -56,7 +56,6 @@ struct tusb_omap_dma_ch {
struct tusb_omap_dma {
struct dma_controller controller;
- struct musb *musb;
void __iomem *tbase;
int ch;
@@ -497,7 +496,7 @@ tusb_omap_dma_allocate(struct dma_controller *c,
u32 reg;
tusb_dma = container_of(c, struct tusb_omap_dma, controller);
- musb = tusb_dma->musb;
+ musb = tusb_dma->controller.musb;
tbase = musb->ctrl_base;
reg = musb_readl(tbase, TUSB_DMA_INT_MASK);
@@ -534,7 +533,7 @@ tusb_omap_dma_allocate(struct dma_controller *c,
dev_name = "TUSB receive";
}
- chdat->musb = tusb_dma->musb;
+ chdat->musb = tusb_dma->controller.musb;
chdat->tbase = tusb_dma->tbase;
chdat->hw_ep = hw_ep;
chdat->epnum = hw_ep->epnum;
@@ -667,7 +666,7 @@ tusb_dma_controller_create(struct musb *musb, void __iomem *base)
if (!tusb_dma)
goto out;
- tusb_dma->musb = musb;
+ tusb_dma->controller.musb = musb;
tusb_dma->tbase = musb->ctrl_base;
tusb_dma->ch = -1;
diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c
index 3eaa4ba6867d..5a572500c418 100644
--- a/drivers/usb/musb/ux500.c
+++ b/drivers/usb/musb/ux500.c
@@ -30,7 +30,7 @@
#include "musb_core.h"
-static struct musb_hdrc_config ux500_musb_hdrc_config = {
+static const struct musb_hdrc_config ux500_musb_hdrc_config = {
.multipoint = true,
.dyn_fifo = true,
.num_eps = 16,
diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig
index b9c409a18faa..61cef7511a50 100644
--- a/drivers/usb/phy/Kconfig
+++ b/drivers/usb/phy/Kconfig
@@ -84,6 +84,7 @@ config SAMSUNG_USBPHY
config TWL6030_USB
tristate "TWL6030 USB Transceiver Driver"
depends on TWL4030_CORE && OMAP_USB2 && USB_MUSB_OMAP2PLUS
+ depends on OF
help
Enable this to support the USB OTG transceiver on TWL6030
family chips. This TWL6030 transceiver has the VBUS and ID GND
diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c
index a03caf4b1327..61bf2285d5b1 100644
--- a/drivers/usb/phy/phy-ab8500-usb.c
+++ b/drivers/usb/phy/phy-ab8500-usb.c
@@ -1023,38 +1023,6 @@ static void ab8500_usb_vbus_turn_on_event_work(struct work_struct *work)
ab->enabled_charging_detection = true;
}
-static unsigned ab8500_eyediagram_workaroud(struct ab8500_usb *ab, unsigned mA)
-{
- /*
- * AB8500 V2 has eye diagram issues when drawing more than 100mA from
- * VBUS. Set charging current to 100mA in case of standard host
- */
- if (is_ab8500_2p0_or_earlier(ab->ab8500))
- if (mA > 100)
- mA = 100;
-
- return mA;
-}
-
-static int ab8500_usb_set_power(struct usb_phy *phy, unsigned mA)
-{
- struct ab8500_usb *ab;
-
- if (!phy)
- return -ENODEV;
-
- ab = phy_to_ab(phy);
-
- mA = ab8500_eyediagram_workaroud(ab, mA);
-
- ab->vbus_draw = mA;
-
- atomic_notifier_call_chain(&ab->phy.notifier,
- UX500_MUSB_VBUS, &ab->vbus_draw);
-
- return 0;
-}
-
static int ab8500_usb_set_suspend(struct usb_phy *x, int suspend)
{
/* TODO */
@@ -1392,7 +1360,6 @@ static int ab8500_usb_probe(struct platform_device *pdev)
ab->phy.otg = otg;
ab->phy.label = "ab8500";
ab->phy.set_suspend = ab8500_usb_set_suspend;
- ab->phy.set_power = ab8500_usb_set_power;
ab->phy.otg->state = OTG_STATE_UNDEFINED;
otg->usb_phy = &ab->phy;
diff --git a/drivers/usb/phy/phy-am335x-control.c b/drivers/usb/phy/phy-am335x-control.c
index 42a1afe36a90..5f5f19813fde 100644
--- a/drivers/usb/phy/phy-am335x-control.c
+++ b/drivers/usb/phy/phy-am335x-control.c
@@ -134,10 +134,12 @@ struct phy_control *am335x_get_phy_control(struct device *dev)
return NULL;
dev = bus_find_device(&platform_bus_type, NULL, node, match);
+ of_node_put(node);
if (!dev)
return NULL;
ctrl_usb = dev_get_drvdata(dev);
+ put_device(dev);
if (!ctrl_usb)
return NULL;
return &ctrl_usb->phy_ctrl;
diff --git a/drivers/usb/phy/phy-fsl-usb.c b/drivers/usb/phy/phy-fsl-usb.c
index 94eb2923afed..392ab422163c 100644
--- a/drivers/usb/phy/phy-fsl-usb.c
+++ b/drivers/usb/phy/phy-fsl-usb.c
@@ -642,17 +642,6 @@ static int fsl_otg_set_peripheral(struct usb_otg *otg,
return 0;
}
-/* Set OTG port power, only for B-device */
-static int fsl_otg_set_power(struct usb_phy *phy, unsigned mA)
-{
- if (!fsl_otg_dev)
- return -ENODEV;
- if (phy->otg->state == OTG_STATE_B_PERIPHERAL)
- pr_info("FSL OTG: Draw %d mA\n", mA);
-
- return 0;
-}
-
/*
* Delayed pin detect interrupt processing.
*
@@ -821,7 +810,6 @@ static int fsl_otg_conf(struct platform_device *pdev)
/* initialize the otg structure */
fsl_otg_tc->phy.label = DRIVER_DESC;
fsl_otg_tc->phy.dev = &pdev->dev;
- fsl_otg_tc->phy.set_power = fsl_otg_set_power;
fsl_otg_tc->phy.otg->usb_phy = &fsl_otg_tc->phy;
fsl_otg_tc->phy.otg->set_host = fsl_otg_set_host;
diff --git a/drivers/usb/phy/phy-generic.c b/drivers/usb/phy/phy-generic.c
index 8311ba2968cd..89d6e7a5fdb7 100644
--- a/drivers/usb/phy/phy-generic.c
+++ b/drivers/usb/phy/phy-generic.c
@@ -59,6 +59,15 @@ EXPORT_SYMBOL_GPL(usb_phy_generic_unregister);
static int nop_set_suspend(struct usb_phy *x, int suspend)
{
+ struct usb_phy_generic *nop = dev_get_drvdata(x->dev);
+
+ if (!IS_ERR(nop->clk)) {
+ if (suspend)
+ clk_disable_unprepare(nop->clk);
+ else
+ clk_prepare_enable(nop->clk);
+ }
+
return 0;
}
diff --git a/drivers/usb/phy/phy-isp1301-omap.c b/drivers/usb/phy/phy-isp1301-omap.c
index 8d111ec653e4..042c5a8fd423 100644
--- a/drivers/usb/phy/phy-isp1301-omap.c
+++ b/drivers/usb/phy/phy-isp1301-omap.c
@@ -94,7 +94,7 @@ struct isp1301 {
#if defined(CONFIG_MACH_OMAP_H2) || defined(CONFIG_MACH_OMAP_H3)
-#if defined(CONFIG_TPS65010) || (defined(CONFIG_TPS65010_MODULE) && defined(MODULE))
+#if IS_REACHABLE(CONFIG_TPS65010)
#include <linux/i2c/tps65010.h>
diff --git a/drivers/usb/phy/phy-isp1301.c b/drivers/usb/phy/phy-isp1301.c
index db68156568e6..f333024660b4 100644
--- a/drivers/usb/phy/phy-isp1301.c
+++ b/drivers/usb/phy/phy-isp1301.c
@@ -33,6 +33,12 @@ static const struct i2c_device_id isp1301_id[] = {
};
MODULE_DEVICE_TABLE(i2c, isp1301_id);
+static const struct of_device_id isp1301_of_match[] = {
+ {.compatible = "nxp,isp1301" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, isp1301_of_match);
+
static struct i2c_client *isp1301_i2c_client;
static int __isp1301_write(struct isp1301 *isp, u8 reg, u8 value, u8 clear)
@@ -130,6 +136,7 @@ static int isp1301_remove(struct i2c_client *client)
static struct i2c_driver isp1301_driver = {
.driver = {
.name = DRV_NAME,
+ .of_match_table = isp1301_of_match,
},
.probe = isp1301_probe,
.remove = isp1301_remove,
diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c
index 8a34759727bb..93d9aaad2994 100644
--- a/drivers/usb/phy/phy-msm-usb.c
+++ b/drivers/usb/phy/phy-msm-usb.c
@@ -842,23 +842,6 @@ static void msm_otg_notify_charger(struct msm_otg *motg, unsigned mA)
motg->cur_power = mA;
}
-static int msm_otg_set_power(struct usb_phy *phy, unsigned mA)
-{
- struct msm_otg *motg = container_of(phy, struct msm_otg, phy);
-
- /*
- * Gadget driver uses set_power method to notify about the
- * available current based on suspend/configured states.
- *
- * IDEV_CHG can be drawn irrespective of suspend/un-configured
- * states when CDP/ACA is connected.
- */
- if (motg->chg_type == USB_SDP_CHARGER)
- msm_otg_notify_charger(motg, mA);
-
- return 0;
-}
-
static void msm_otg_start_host(struct usb_phy *phy, int on)
{
struct msm_otg *motg = container_of(phy, struct msm_otg, phy);
@@ -1742,14 +1725,14 @@ static int msm_otg_read_dt(struct platform_device *pdev, struct msm_otg *motg)
if (!IS_ERR(ext_vbus)) {
motg->vbus.extcon = ext_vbus;
motg->vbus.nb.notifier_call = msm_otg_vbus_notifier;
- ret = extcon_register_notifier(ext_vbus, EXTCON_USB,
- &motg->vbus.nb);
+ ret = devm_extcon_register_notifier(&pdev->dev, ext_vbus,
+ EXTCON_USB, &motg->vbus.nb);
if (ret < 0) {
dev_err(&pdev->dev, "register VBUS notifier failed\n");
return ret;
}
- ret = extcon_get_cable_state_(ext_vbus, EXTCON_USB);
+ ret = extcon_get_state(ext_vbus, EXTCON_USB);
if (ret)
set_bit(B_SESS_VLD, &motg->inputs);
else
@@ -1759,16 +1742,14 @@ static int msm_otg_read_dt(struct platform_device *pdev, struct msm_otg *motg)
if (!IS_ERR(ext_id)) {
motg->id.extcon = ext_id;
motg->id.nb.notifier_call = msm_otg_id_notifier;
- ret = extcon_register_notifier(ext_id, EXTCON_USB_HOST,
- &motg->id.nb);
+ ret = devm_extcon_register_notifier(&pdev->dev, ext_id,
+ EXTCON_USB_HOST, &motg->id.nb);
if (ret < 0) {
dev_err(&pdev->dev, "register ID notifier failed\n");
- extcon_unregister_notifier(motg->vbus.extcon,
- EXTCON_USB, &motg->vbus.nb);
return ret;
}
- ret = extcon_get_cable_state_(ext_id, EXTCON_USB_HOST);
+ ret = extcon_get_state(ext_id, EXTCON_USB_HOST);
if (ret)
clear_bit(ID, &motg->inputs);
else
@@ -1883,10 +1864,9 @@ static int msm_otg_probe(struct platform_device *pdev)
*/
if (motg->phy_number) {
phy_select = devm_ioremap_nocache(&pdev->dev, USB2_PHY_SEL, 4);
- if (!phy_select) {
- ret = -ENOMEM;
- goto unregister_extcon;
- }
+ if (!phy_select)
+ return -ENOMEM;
+
/* Enable second PHY with the OTG port */
writel(0x1, phy_select);
}
@@ -1897,7 +1877,7 @@ static int msm_otg_probe(struct platform_device *pdev)
if (motg->irq < 0) {
dev_err(&pdev->dev, "platform_get_irq failed\n");
ret = motg->irq;
- goto unregister_extcon;
+ return motg->irq;
}
regs[0].supply = "vddcx";
@@ -1906,7 +1886,7 @@ static int msm_otg_probe(struct platform_device *pdev)
ret = devm_regulator_bulk_get(motg->phy.dev, ARRAY_SIZE(regs), regs);
if (ret)
- goto unregister_extcon;
+ return ret;
motg->vddcx = regs[0].consumer;
motg->v3p3 = regs[1].consumer;
@@ -1950,7 +1930,6 @@ static int msm_otg_probe(struct platform_device *pdev)
}
phy->init = msm_phy_init;
- phy->set_power = msm_otg_set_power;
phy->notify_disconnect = msm_phy_notify_disconnect;
phy->type = USB_PHY_TYPE_USB2;
@@ -2003,11 +1982,6 @@ disable_clks:
clk_disable_unprepare(motg->clk);
if (!IS_ERR(motg->core_clk))
clk_disable_unprepare(motg->core_clk);
-unregister_extcon:
- extcon_unregister_notifier(motg->id.extcon,
- EXTCON_USB_HOST, &motg->id.nb);
- extcon_unregister_notifier(motg->vbus.extcon,
- EXTCON_USB, &motg->vbus.nb);
return ret;
}
@@ -2029,9 +2003,6 @@ static int msm_otg_remove(struct platform_device *pdev)
*/
gpiod_set_value_cansleep(motg->switch_gpio, 0);
- extcon_unregister_notifier(motg->id.extcon, EXTCON_USB_HOST, &motg->id.nb);
- extcon_unregister_notifier(motg->vbus.extcon, EXTCON_USB, &motg->vbus.nb);
-
msm_otg_debugfs_cleanup();
cancel_delayed_work_sync(&motg->chg_work);
cancel_work_sync(&motg->sm_work);
diff --git a/drivers/usb/phy/phy-omap-otg.c b/drivers/usb/phy/phy-omap-otg.c
index 6523af4f8f93..800d1d90753d 100644
--- a/drivers/usb/phy/phy-omap-otg.c
+++ b/drivers/usb/phy/phy-omap-otg.c
@@ -118,19 +118,19 @@ static int omap_otg_probe(struct platform_device *pdev)
otg_dev->id_nb.notifier_call = omap_otg_id_notifier;
otg_dev->vbus_nb.notifier_call = omap_otg_vbus_notifier;
- ret = extcon_register_notifier(extcon, EXTCON_USB_HOST, &otg_dev->id_nb);
+ ret = devm_extcon_register_notifier(&pdev->dev, extcon,
+ EXTCON_USB_HOST, &otg_dev->id_nb);
if (ret)
return ret;
- ret = extcon_register_notifier(extcon, EXTCON_USB, &otg_dev->vbus_nb);
+ ret = devm_extcon_register_notifier(&pdev->dev, extcon,
+ EXTCON_USB, &otg_dev->vbus_nb);
if (ret) {
- extcon_unregister_notifier(extcon, EXTCON_USB_HOST,
- &otg_dev->id_nb);
return ret;
}
- otg_dev->id = extcon_get_cable_state_(extcon, EXTCON_USB_HOST);
- otg_dev->vbus = extcon_get_cable_state_(extcon, EXTCON_USB);
+ otg_dev->id = extcon_get_state(extcon, EXTCON_USB_HOST);
+ otg_dev->vbus = extcon_get_state(extcon, EXTCON_USB);
omap_otg_set_mode(otg_dev);
rev = readl(otg_dev->base);
@@ -145,20 +145,8 @@ static int omap_otg_probe(struct platform_device *pdev)
return 0;
}
-static int omap_otg_remove(struct platform_device *pdev)
-{
- struct otg_device *otg_dev = platform_get_drvdata(pdev);
- struct extcon_dev *edev = otg_dev->extcon;
-
- extcon_unregister_notifier(edev, EXTCON_USB_HOST, &otg_dev->id_nb);
- extcon_unregister_notifier(edev, EXTCON_USB, &otg_dev->vbus_nb);
-
- return 0;
-}
-
static struct platform_driver omap_otg_driver = {
.probe = omap_otg_probe,
- .remove = omap_otg_remove,
.driver = {
.name = "omap_otg",
},
diff --git a/drivers/usb/phy/phy-qcom-8x16-usb.c b/drivers/usb/phy/phy-qcom-8x16-usb.c
index d8593adb3621..fdf686398772 100644
--- a/drivers/usb/phy/phy-qcom-8x16-usb.c
+++ b/drivers/usb/phy/phy-qcom-8x16-usb.c
@@ -187,7 +187,7 @@ static int phy_8x16_init(struct usb_phy *phy)
val = ULPI_PWR_OTG_COMP_DISABLE;
usb_phy_io_write(phy, val, ULPI_SET(ULPI_PWR_CLK_MNG_REG));
- state = extcon_get_cable_state_(qphy->vbus_edev, EXTCON_USB);
+ state = extcon_get_state(qphy->vbus_edev, EXTCON_USB);
if (state)
phy_8x16_vbus_on(qphy);
else
@@ -316,23 +316,20 @@ static int phy_8x16_probe(struct platform_device *pdev)
goto off_clks;
qphy->vbus_notify.notifier_call = phy_8x16_vbus_notify;
- ret = extcon_register_notifier(qphy->vbus_edev, EXTCON_USB,
- &qphy->vbus_notify);
+ ret = devm_extcon_register_notifier(&pdev->dev, qphy->vbus_edev,
+ EXTCON_USB, &qphy->vbus_notify);
if (ret < 0)
goto off_power;
ret = usb_add_phy_dev(&qphy->phy);
if (ret)
- goto off_extcon;
+ goto off_power;
qphy->reboot_notify.notifier_call = phy_8x16_reboot_notify;
register_reboot_notifier(&qphy->reboot_notify);
return 0;
-off_extcon:
- extcon_unregister_notifier(qphy->vbus_edev, EXTCON_USB,
- &qphy->vbus_notify);
off_power:
regulator_bulk_disable(ARRAY_SIZE(qphy->regulator), qphy->regulator);
off_clks:
@@ -347,8 +344,6 @@ static int phy_8x16_remove(struct platform_device *pdev)
struct phy_8x16 *qphy = platform_get_drvdata(pdev);
unregister_reboot_notifier(&qphy->reboot_notify);
- extcon_unregister_notifier(qphy->vbus_edev, EXTCON_USB,
- &qphy->vbus_notify);
/*
* Ensure that D+/D- lines are routed to uB connector, so
diff --git a/drivers/usb/phy/phy-tahvo.c b/drivers/usb/phy/phy-tahvo.c
index ab5d364f6e8c..a31c8682e998 100644
--- a/drivers/usb/phy/phy-tahvo.c
+++ b/drivers/usb/phy/phy-tahvo.c
@@ -121,7 +121,7 @@ static void check_vbus_state(struct tahvo_usb *tu)
prev_state = tu->vbus_state;
tu->vbus_state = reg & TAHVO_STAT_VBUS;
if (prev_state != tu->vbus_state) {
- extcon_set_cable_state_(tu->extcon, EXTCON_USB, tu->vbus_state);
+ extcon_set_state_sync(tu->extcon, EXTCON_USB, tu->vbus_state);
sysfs_notify(&tu->pt_dev->dev.kobj, NULL, "vbus_state");
}
}
@@ -130,7 +130,7 @@ static void tahvo_usb_become_host(struct tahvo_usb *tu)
{
struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent);
- extcon_set_cable_state_(tu->extcon, EXTCON_USB_HOST, true);
+ extcon_set_state_sync(tu->extcon, EXTCON_USB_HOST, true);
/* Power up the transceiver in USB host mode */
retu_write(rdev, TAHVO_REG_USBR, USBR_REGOUT | USBR_NSUSPEND |
@@ -149,7 +149,7 @@ static void tahvo_usb_become_peripheral(struct tahvo_usb *tu)
{
struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent);
- extcon_set_cable_state_(tu->extcon, EXTCON_USB_HOST, false);
+ extcon_set_state_sync(tu->extcon, EXTCON_USB_HOST, false);
/* Power up transceiver and set it in USB peripheral mode */
retu_write(rdev, TAHVO_REG_USBR, USBR_SLAVE_CONTROL | USBR_REGOUT |
@@ -379,9 +379,9 @@ static int tahvo_usb_probe(struct platform_device *pdev)
}
/* Set the initial cable state. */
- extcon_set_cable_state_(tu->extcon, EXTCON_USB_HOST,
+ extcon_set_state_sync(tu->extcon, EXTCON_USB_HOST,
tu->tahvo_mode == TAHVO_MODE_HOST);
- extcon_set_cable_state_(tu->extcon, EXTCON_USB, tu->vbus_state);
+ extcon_set_state_sync(tu->extcon, EXTCON_USB, tu->vbus_state);
/* Create OTG interface */
tahvo_usb_power_off(tu);
diff --git a/drivers/usb/phy/phy-twl6030-usb.c b/drivers/usb/phy/phy-twl6030-usb.c
index a72e8d670adc..628b600b02b1 100644
--- a/drivers/usb/phy/phy-twl6030-usb.c
+++ b/drivers/usb/phy/phy-twl6030-usb.c
@@ -108,7 +108,6 @@ struct twl6030_usb {
enum musb_vbus_id_status linkstat;
u8 asleep;
bool vbus_enable;
- const char *regulator;
};
#define comparator_to_twl(x) container_of((x), struct twl6030_usb, comparator)
@@ -166,7 +165,7 @@ static int twl6030_usb_ldo_init(struct twl6030_usb *twl)
/* Program MISC2 register and set bit VUSB_IN_VBAT */
twl6030_writeb(twl, TWL6030_MODULE_ID0, 0x10, TWL6030_MISC2);
- twl->usb3v3 = regulator_get(twl->dev, twl->regulator);
+ twl->usb3v3 = regulator_get(twl->dev, "usb");
if (IS_ERR(twl->usb3v3))
return -ENODEV;
@@ -341,7 +340,11 @@ static int twl6030_usb_probe(struct platform_device *pdev)
int status, err;
struct device_node *np = pdev->dev.of_node;
struct device *dev = &pdev->dev;
- struct twl4030_usb_data *pdata = dev_get_platdata(dev);
+
+ if (!np) {
+ dev_err(dev, "no DT info\n");
+ return -EINVAL;
+ }
twl = devm_kzalloc(dev, sizeof(*twl), GFP_KERNEL);
if (!twl)
@@ -361,18 +364,6 @@ static int twl6030_usb_probe(struct platform_device *pdev)
return -EPROBE_DEFER;
}
- if (np) {
- twl->regulator = "usb";
- } else if (pdata) {
- if (pdata->features & TWL6032_SUBCLASS)
- twl->regulator = "ldousb";
- else
- twl->regulator = "vusb";
- } else {
- dev_err(&pdev->dev, "twl6030 initialized without pdata\n");
- return -EINVAL;
- }
-
/* init spinlock for workqueue */
spin_lock_init(&twl->lock);
@@ -436,13 +427,11 @@ static int twl6030_usb_remove(struct platform_device *pdev)
return 0;
}
-#ifdef CONFIG_OF
static const struct of_device_id twl6030_usb_id_table[] = {
{ .compatible = "ti,twl6030-usb" },
{}
};
MODULE_DEVICE_TABLE(of, twl6030_usb_id_table);
-#endif
static struct platform_driver twl6030_usb_driver = {
.probe = twl6030_usb_probe,
diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c
index 012a37aa3e0d..623c51300393 100644
--- a/drivers/usb/renesas_usbhs/common.c
+++ b/drivers/usb/renesas_usbhs/common.c
@@ -389,7 +389,7 @@ static void usbhsc_hotplug(struct usbhs_priv *priv)
if (enable && !mod) {
if (priv->edev) {
- cable = extcon_get_cable_state_(priv->edev, EXTCON_USB_HOST);
+ cable = extcon_get_state(priv->edev, EXTCON_USB_HOST);
if ((cable > 0 && id != USBHS_HOST) ||
(!cable && id != USBHS_GADGET)) {
dev_info(&pdev->dev,
diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c
index 857e78337324..d1af831f43eb 100644
--- a/drivers/usb/renesas_usbhs/fifo.c
+++ b/drivers/usb/renesas_usbhs/fifo.c
@@ -100,10 +100,7 @@ static void __usbhsf_pkt_del(struct usbhs_pkt *pkt)
static struct usbhs_pkt *__usbhsf_pkt_get(struct usbhs_pipe *pipe)
{
- if (list_empty(&pipe->list))
- return NULL;
-
- return list_first_entry(&pipe->list, struct usbhs_pkt, node);
+ return list_first_entry_or_null(&pipe->list, struct usbhs_pkt, node);
}
static void usbhsf_fifo_clear(struct usbhs_pipe *pipe,
diff --git a/drivers/usb/renesas_usbhs/mod_host.c b/drivers/usb/renesas_usbhs/mod_host.c
index 165e81bfd93a..dfb346e9bd0c 100644
--- a/drivers/usb/renesas_usbhs/mod_host.c
+++ b/drivers/usb/renesas_usbhs/mod_host.c
@@ -577,7 +577,7 @@ static struct usbhsh_device *usbhsh_device_attach(struct usbhsh_hpriv *hpriv,
upphub = usbhsh_device_number(hpriv, parent);
hubport = usbhsh_device_hubport(udev);
- dev_dbg(dev, "%s connecte to Hub [%d:%d](%p)\n", __func__,
+ dev_dbg(dev, "%s connected to Hub [%d:%d](%p)\n", __func__,
upphub, hubport, parent);
}
diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig
index 56ecb8b5115d..a8d5f2e4878d 100644
--- a/drivers/usb/serial/Kconfig
+++ b/drivers/usb/serial/Kconfig
@@ -255,6 +255,16 @@ config USB_SERIAL_F81232
To compile this driver as a module, choose M here: the
module will be called f81232.
+config USB_SERIAL_F8153X
+ tristate "USB Fintek F81532/534 Multi-Ports Serial Driver"
+ help
+ Say Y here if you want to use the Fintek F81532/534 Multi-Ports
+ USB to serial adapter.
+
+ To compile this driver as a module, choose M here: the
+ module will be called f81534.
+
+
config USB_SERIAL_GARMIN
tristate "USB Garmin GPS driver"
help
@@ -703,6 +713,15 @@ config USB_SERIAL_QT2
To compile this driver as a module, choose M here: the
module will be called quatech-serial.
+config USB_SERIAL_UPD78F0730
+ tristate "USB Renesas uPD78F0730 Single Port Serial Driver"
+ help
+ Say Y here if you want to use the Renesas uPD78F0730
+ serial driver.
+
+ To compile this driver as a module, choose M here: the
+ module will be called upd78f0730.
+
config USB_SERIAL_DEBUG
tristate "USB Debugging Device"
help
diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile
index 349d9df0895f..5a21a82390e1 100644
--- a/drivers/usb/serial/Makefile
+++ b/drivers/usb/serial/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_USB_SERIAL_EDGEPORT) += io_edgeport.o
obj-$(CONFIG_USB_SERIAL_EDGEPORT_TI) += io_ti.o
obj-$(CONFIG_USB_SERIAL_EMPEG) += empeg.o
obj-$(CONFIG_USB_SERIAL_F81232) += f81232.o
+obj-$(CONFIG_USB_SERIAL_F8153X) += f81534.o
obj-$(CONFIG_USB_SERIAL_FTDI_SIO) += ftdi_sio.o
obj-$(CONFIG_USB_SERIAL_GARMIN) += garmin_gps.o
obj-$(CONFIG_USB_SERIAL_IPAQ) += ipaq.o
@@ -55,6 +56,7 @@ obj-$(CONFIG_USB_SERIAL_SSU100) += ssu100.o
obj-$(CONFIG_USB_SERIAL_SYMBOL) += symbolserial.o
obj-$(CONFIG_USB_SERIAL_WWAN) += usb_wwan.o
obj-$(CONFIG_USB_SERIAL_TI) += ti_usb_3410_5052.o
+obj-$(CONFIG_USB_SERIAL_UPD78F0730) += upd78f0730.o
obj-$(CONFIG_USB_SERIAL_VISOR) += visor.o
obj-$(CONFIG_USB_SERIAL_WISHBONE) += wishbone-serial.o
obj-$(CONFIG_USB_SERIAL_WHITEHEAT) += whiteheat.o
diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c
index 1532cde8a437..2779e59c30f1 100644
--- a/drivers/usb/serial/ark3116.c
+++ b/drivers/usb/serial/ark3116.c
@@ -99,10 +99,17 @@ static int ark3116_read_reg(struct usb_serial *serial,
usb_rcvctrlpipe(serial->dev, 0),
0xfe, 0xc0, 0, reg,
buf, 1, ARK_TIMEOUT);
- if (result < 0)
+ if (result < 1) {
+ dev_err(&serial->interface->dev,
+ "failed to read register %u: %d\n",
+ reg, result);
+ if (result >= 0)
+ result = -EIO;
+
return result;
- else
- return buf[0];
+ }
+
+ return buf[0];
}
static inline int calc_divisor(int bps)
@@ -118,17 +125,11 @@ static inline int calc_divisor(int bps)
static int ark3116_attach(struct usb_serial *serial)
{
/* make sure we have our end-points */
- if ((serial->num_bulk_in == 0) ||
- (serial->num_bulk_out == 0) ||
- (serial->num_interrupt_in == 0)) {
- dev_err(&serial->dev->dev,
- "%s - missing endpoint - "
- "bulk in: %d, bulk out: %d, int in %d\n",
- KBUILD_MODNAME,
- serial->num_bulk_in,
- serial->num_bulk_out,
- serial->num_interrupt_in);
- return -EINVAL;
+ if (serial->num_bulk_in == 0 ||
+ serial->num_bulk_out == 0 ||
+ serial->num_interrupt_in == 0) {
+ dev_err(&serial->interface->dev, "missing endpoint\n");
+ return -ENODEV;
}
return 0;
@@ -186,10 +187,8 @@ static int ark3116_port_probe(struct usb_serial_port *port)
if (priv->irda)
ark3116_write_reg(serial, 0x9, 0);
- dev_info(&serial->dev->dev,
- "%s using %s mode\n",
- KBUILD_MODNAME,
- priv->irda ? "IrDA" : "RS232");
+ dev_info(&port->dev, "using %s mode\n", priv->irda ? "IrDA" : "RS232");
+
return 0;
}
@@ -325,9 +324,8 @@ static void ark3116_set_termios(struct tty_struct *tty,
/* check for software flow control */
if (I_IXOFF(tty) || I_IXON(tty)) {
- dev_warn(&serial->dev->dev,
- "%s: don't know how to do software flow control\n",
- KBUILD_MODNAME);
+ dev_warn(&port->dev,
+ "software flow control not implemented\n");
}
/* Don't rewrite B0 */
@@ -346,8 +344,8 @@ static void ark3116_close(struct usb_serial_port *port)
ark3116_write_reg(serial, UART_IER, 0);
usb_serial_generic_close(port);
- if (serial->num_interrupt_in)
- usb_kill_urb(port->interrupt_in_urb);
+
+ usb_kill_urb(port->interrupt_in_urb);
}
static int ark3116_open(struct tty_struct *tty, struct usb_serial_port *port)
@@ -366,23 +364,29 @@ static int ark3116_open(struct tty_struct *tty, struct usb_serial_port *port)
dev_dbg(&port->dev,
"%s - usb_serial_generic_open failed: %d\n",
__func__, result);
- goto err_out;
+ goto err_free;
}
/* remove any data still left: also clears error state */
ark3116_read_reg(serial, UART_RX, buf);
/* read modem status */
- priv->msr = ark3116_read_reg(serial, UART_MSR, buf);
+ result = ark3116_read_reg(serial, UART_MSR, buf);
+ if (result < 0)
+ goto err_close;
+ priv->msr = *buf;
+
/* read line status */
- priv->lsr = ark3116_read_reg(serial, UART_LSR, buf);
+ result = ark3116_read_reg(serial, UART_LSR, buf);
+ if (result < 0)
+ goto err_close;
+ priv->lsr = *buf;
result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
if (result) {
dev_err(&port->dev, "submit irq_in urb failed %d\n",
result);
- ark3116_close(port);
- goto err_out;
+ goto err_close;
}
/* activate interrupts */
@@ -395,8 +399,15 @@ static int ark3116_open(struct tty_struct *tty, struct usb_serial_port *port)
if (tty)
ark3116_set_termios(tty, port, NULL);
-err_out:
kfree(buf);
+
+ return 0;
+
+err_close:
+ usb_serial_generic_close(port);
+err_free:
+ kfree(buf);
+
return result;
}
@@ -602,9 +613,8 @@ static void ark3116_read_int_callback(struct urb *urb)
result = usb_submit_urb(urb, GFP_ATOMIC);
if (result)
- dev_err(&urb->dev->dev,
- "%s - Error %d submitting interrupt urb\n",
- __func__, result);
+ dev_err(&port->dev, "failed to resubmit interrupt urb: %d\n",
+ result);
}
diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c
index f139488d0816..351745aec0e1 100644
--- a/drivers/usb/serial/ch341.c
+++ b/drivers/usb/serial/ch341.c
@@ -61,13 +61,26 @@
* the Net/FreeBSD uchcom.c driver by Takanori Watanabe. Domo arigato.
*/
+#define CH341_REQ_READ_VERSION 0x5F
#define CH341_REQ_WRITE_REG 0x9A
#define CH341_REQ_READ_REG 0x95
-#define CH341_REG_BREAK1 0x05
-#define CH341_REG_BREAK2 0x18
-#define CH341_NBREAK_BITS_REG1 0x01
-#define CH341_NBREAK_BITS_REG2 0x40
-
+#define CH341_REQ_SERIAL_INIT 0xA1
+#define CH341_REQ_MODEM_CTRL 0xA4
+
+#define CH341_REG_BREAK 0x05
+#define CH341_REG_LCR 0x18
+#define CH341_NBREAK_BITS 0x01
+
+#define CH341_LCR_ENABLE_RX 0x80
+#define CH341_LCR_ENABLE_TX 0x40
+#define CH341_LCR_MARK_SPACE 0x20
+#define CH341_LCR_PAR_EVEN 0x10
+#define CH341_LCR_ENABLE_PAR 0x08
+#define CH341_LCR_STOP_BITS_2 0x04
+#define CH341_LCR_CS8 0x03
+#define CH341_LCR_CS7 0x02
+#define CH341_LCR_CS6 0x01
+#define CH341_LCR_CS5 0x00
static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x4348, 0x5523) },
@@ -80,8 +93,9 @@ MODULE_DEVICE_TABLE(usb, id_table);
struct ch341_private {
spinlock_t lock; /* access lock */
unsigned baud_rate; /* set baud rate */
- u8 line_control; /* set line control value RTS/DTR */
- u8 line_status; /* active status of modem control inputs */
+ u8 mcr;
+ u8 msr;
+ u8 lcr;
};
static void ch341_set_termios(struct tty_struct *tty,
@@ -93,12 +107,14 @@ static int ch341_control_out(struct usb_device *dev, u8 request,
{
int r;
- dev_dbg(&dev->dev, "ch341_control_out(%02x,%02x,%04x,%04x)\n",
- USB_DIR_OUT|0x40, (int)request, (int)value, (int)index);
+ dev_dbg(&dev->dev, "%s - (%02x,%04x,%04x)\n", __func__,
+ request, value, index);
r = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), request,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
value, index, NULL, 0, DEFAULT_TIMEOUT);
+ if (r < 0)
+ dev_err(&dev->dev, "failed to send control message: %d\n", r);
return r;
}
@@ -109,20 +125,32 @@ static int ch341_control_in(struct usb_device *dev,
{
int r;
- dev_dbg(&dev->dev, "ch341_control_in(%02x,%02x,%04x,%04x,%p,%u)\n",
- USB_DIR_IN|0x40, (int)request, (int)value, (int)index, buf,
- (int)bufsize);
+ dev_dbg(&dev->dev, "%s - (%02x,%04x,%04x,%u)\n", __func__,
+ request, value, index, bufsize);
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);
- return r;
+ if (r < bufsize) {
+ if (r >= 0) {
+ dev_err(&dev->dev,
+ "short control message received (%d < %u)\n",
+ r, bufsize);
+ r = -EIO;
+ }
+
+ dev_err(&dev->dev, "failed to receive control message: %d\n",
+ r);
+ return r;
+ }
+
+ return 0;
}
-static int ch341_set_baudrate(struct usb_device *dev,
- struct ch341_private *priv)
+static int ch341_set_baudrate_lcr(struct usb_device *dev,
+ struct ch341_private *priv, u8 lcr)
{
- short a, b;
+ short a;
int r;
unsigned long factor;
short divisor;
@@ -142,43 +170,47 @@ static int ch341_set_baudrate(struct usb_device *dev,
factor = 0x10000 - factor;
a = (factor & 0xff00) | divisor;
- b = factor & 0xff;
- r = ch341_control_out(dev, 0x9a, 0x1312, a);
- if (!r)
- r = ch341_control_out(dev, 0x9a, 0x0f2c, b);
+ /*
+ * CH341A buffers data until a full endpoint-size packet (32 bytes)
+ * has been received unless bit 7 is set.
+ */
+ a |= BIT(7);
+
+ r = ch341_control_out(dev, CH341_REQ_WRITE_REG, 0x1312, a);
+ if (r)
+ return r;
+
+ r = ch341_control_out(dev, CH341_REQ_WRITE_REG, 0x2518, lcr);
+ if (r)
+ return r;
return r;
}
static int ch341_set_handshake(struct usb_device *dev, u8 control)
{
- return ch341_control_out(dev, 0xa4, ~control, 0);
+ return ch341_control_out(dev, CH341_REQ_MODEM_CTRL, ~control, 0);
}
static int ch341_get_status(struct usb_device *dev, struct ch341_private *priv)
{
+ const unsigned int size = 2;
char *buffer;
int r;
- const unsigned size = 8;
unsigned long flags;
buffer = kmalloc(size, GFP_KERNEL);
if (!buffer)
return -ENOMEM;
- r = ch341_control_in(dev, 0x95, 0x0706, 0, buffer, size);
+ r = ch341_control_in(dev, CH341_REQ_READ_REG, 0x0706, 0, buffer, size);
if (r < 0)
goto out;
- /* setup the private status if available */
- if (r == 2) {
- r = 0;
- spin_lock_irqsave(&priv->lock, flags);
- priv->line_status = (~(*buffer)) & CH341_BITS_MODEM_STAT;
- spin_unlock_irqrestore(&priv->lock, flags);
- } else
- r = -EPROTO;
+ spin_lock_irqsave(&priv->lock, flags);
+ priv->msr = (~(*buffer)) & CH341_BITS_MODEM_STAT;
+ spin_unlock_irqrestore(&priv->lock, flags);
out: kfree(buffer);
return r;
@@ -188,55 +220,29 @@ out: kfree(buffer);
static int ch341_configure(struct usb_device *dev, struct ch341_private *priv)
{
+ const unsigned int size = 2;
char *buffer;
int r;
- const unsigned size = 8;
buffer = kmalloc(size, GFP_KERNEL);
if (!buffer)
return -ENOMEM;
/* expect two bytes 0x27 0x00 */
- r = ch341_control_in(dev, 0x5f, 0, 0, buffer, size);
+ r = ch341_control_in(dev, CH341_REQ_READ_VERSION, 0, 0, buffer, size);
if (r < 0)
goto out;
+ dev_dbg(&dev->dev, "Chip version: 0x%02x\n", buffer[0]);
- r = ch341_control_out(dev, 0xa1, 0, 0);
+ r = ch341_control_out(dev, CH341_REQ_SERIAL_INIT, 0, 0);
if (r < 0)
goto out;
- r = ch341_set_baudrate(dev, priv);
+ r = ch341_set_baudrate_lcr(dev, priv, priv->lcr);
if (r < 0)
goto out;
- /* expect two bytes 0x56 0x00 */
- r = ch341_control_in(dev, 0x95, 0x2518, 0, buffer, size);
- if (r < 0)
- goto out;
-
- r = ch341_control_out(dev, 0x9a, 0x2518, 0x0050);
- if (r < 0)
- goto out;
-
- /* expect 0xff 0xee */
- r = ch341_get_status(dev, priv);
- if (r < 0)
- goto out;
-
- r = ch341_control_out(dev, 0xa1, 0x501f, 0xd90a);
- if (r < 0)
- goto out;
-
- r = ch341_set_baudrate(dev, priv);
- if (r < 0)
- goto out;
-
- r = ch341_set_handshake(dev, priv->line_control);
- if (r < 0)
- goto out;
-
- /* expect 0x9f 0xee */
- r = ch341_get_status(dev, priv);
+ r = ch341_set_handshake(dev, priv->mcr);
out: kfree(buffer);
return r;
@@ -253,7 +259,11 @@ static int ch341_port_probe(struct usb_serial_port *port)
spin_lock_init(&priv->lock);
priv->baud_rate = DEFAULT_BAUD_RATE;
- priv->line_control = CH341_BIT_RTS | CH341_BIT_DTR;
+ /*
+ * Some CH340 devices appear unable to change the initial LCR
+ * settings, so set a sane 8N1 default.
+ */
+ priv->lcr = CH341_LCR_ENABLE_RX | CH341_LCR_ENABLE_TX | CH341_LCR_CS8;
r = ch341_configure(port->serial->dev, priv);
if (r < 0)
@@ -279,7 +289,7 @@ static int ch341_port_remove(struct usb_serial_port *port)
static int ch341_carrier_raised(struct usb_serial_port *port)
{
struct ch341_private *priv = usb_get_serial_port_data(port);
- if (priv->line_status & CH341_BIT_DCD)
+ if (priv->msr & CH341_BIT_DCD)
return 1;
return 0;
}
@@ -292,11 +302,11 @@ static void ch341_dtr_rts(struct usb_serial_port *port, int on)
/* drop DTR and RTS */
spin_lock_irqsave(&priv->lock, flags);
if (on)
- priv->line_control |= CH341_BIT_RTS | CH341_BIT_DTR;
+ priv->mcr |= CH341_BIT_RTS | CH341_BIT_DTR;
else
- priv->line_control &= ~(CH341_BIT_RTS | CH341_BIT_DTR);
+ priv->mcr &= ~(CH341_BIT_RTS | CH341_BIT_DTR);
spin_unlock_irqrestore(&priv->lock, flags);
- ch341_set_handshake(port->serial->dev, priv->line_control);
+ ch341_set_handshake(port->serial->dev, priv->mcr);
}
static void ch341_close(struct usb_serial_port *port)
@@ -309,14 +319,9 @@ static void ch341_close(struct usb_serial_port *port)
/* open this device, set default parameters */
static int ch341_open(struct tty_struct *tty, struct usb_serial_port *port)
{
- struct usb_serial *serial = port->serial;
struct ch341_private *priv = usb_get_serial_port_data(port);
int r;
- r = ch341_configure(serial->dev, priv);
- if (r)
- goto out;
-
if (tty)
ch341_set_termios(tty, port, NULL);
@@ -325,12 +330,25 @@ static int ch341_open(struct tty_struct *tty, struct usb_serial_port *port)
if (r) {
dev_err(&port->dev, "%s - failed to submit interrupt urb: %d\n",
__func__, r);
- goto out;
+ return r;
+ }
+
+ r = ch341_get_status(port->serial->dev, priv);
+ if (r < 0) {
+ dev_err(&port->dev, "failed to read modem status: %d\n", r);
+ goto err_kill_interrupt_urb;
}
r = usb_serial_generic_open(tty, port);
+ if (r)
+ goto err_kill_interrupt_urb;
+
+ return 0;
+
+err_kill_interrupt_urb:
+ usb_kill_urb(port->interrupt_in_urb);
-out: return r;
+ return r;
}
/* Old_termios contains the original termios settings and
@@ -342,35 +360,69 @@ static void ch341_set_termios(struct tty_struct *tty,
struct ch341_private *priv = usb_get_serial_port_data(port);
unsigned baud_rate;
unsigned long flags;
+ u8 lcr;
+ int r;
+
+ /* redundant changes may cause the chip to lose bytes */
+ if (old_termios && !tty_termios_hw_change(&tty->termios, old_termios))
+ return;
baud_rate = tty_get_baud_rate(tty);
- priv->baud_rate = baud_rate;
+ lcr = CH341_LCR_ENABLE_RX | CH341_LCR_ENABLE_TX;
+
+ switch (C_CSIZE(tty)) {
+ case CS5:
+ lcr |= CH341_LCR_CS5;
+ break;
+ case CS6:
+ lcr |= CH341_LCR_CS6;
+ break;
+ case CS7:
+ lcr |= CH341_LCR_CS7;
+ break;
+ case CS8:
+ lcr |= CH341_LCR_CS8;
+ break;
+ }
+
+ if (C_PARENB(tty)) {
+ lcr |= CH341_LCR_ENABLE_PAR;
+ if (C_PARODD(tty) == 0)
+ lcr |= CH341_LCR_PAR_EVEN;
+ if (C_CMSPAR(tty))
+ lcr |= CH341_LCR_MARK_SPACE;
+ }
+
+ if (C_CSTOPB(tty))
+ lcr |= CH341_LCR_STOP_BITS_2;
if (baud_rate) {
- spin_lock_irqsave(&priv->lock, flags);
- priv->line_control |= (CH341_BIT_DTR | CH341_BIT_RTS);
- spin_unlock_irqrestore(&priv->lock, flags);
- ch341_set_baudrate(port->serial->dev, priv);
- } else {
- spin_lock_irqsave(&priv->lock, flags);
- priv->line_control &= ~(CH341_BIT_DTR | CH341_BIT_RTS);
- spin_unlock_irqrestore(&priv->lock, flags);
+ priv->baud_rate = baud_rate;
+
+ r = ch341_set_baudrate_lcr(port->serial->dev, priv, lcr);
+ if (r < 0 && old_termios) {
+ priv->baud_rate = tty_termios_baud_rate(old_termios);
+ tty_termios_copy_hw(&tty->termios, old_termios);
+ } else if (r == 0) {
+ priv->lcr = lcr;
+ }
}
- ch341_set_handshake(port->serial->dev, priv->line_control);
+ spin_lock_irqsave(&priv->lock, flags);
+ if (C_BAUD(tty) == B0)
+ priv->mcr &= ~(CH341_BIT_DTR | CH341_BIT_RTS);
+ else if (old_termios && (old_termios->c_cflag & CBAUD) == B0)
+ priv->mcr |= (CH341_BIT_DTR | CH341_BIT_RTS);
+ spin_unlock_irqrestore(&priv->lock, flags);
- /* Unimplemented:
- * (cflag & CSIZE) : data bits [5, 8]
- * (cflag & PARENB) : parity {NONE, EVEN, ODD}
- * (cflag & CSTOPB) : stop bits [1, 2]
- */
+ ch341_set_handshake(port->serial->dev, priv->mcr);
}
static void ch341_break_ctl(struct tty_struct *tty, int break_state)
{
const uint16_t ch341_break_reg =
- ((uint16_t) CH341_REG_BREAK2 << 8) | CH341_REG_BREAK1;
+ ((uint16_t) CH341_REG_LCR << 8) | CH341_REG_BREAK;
struct usb_serial_port *port = tty->driver_data;
int r;
uint16_t reg_contents;
@@ -391,12 +443,12 @@ static void ch341_break_ctl(struct tty_struct *tty, int break_state)
__func__, break_reg[0], break_reg[1]);
if (break_state != 0) {
dev_dbg(&port->dev, "%s - Enter break state requested\n", __func__);
- break_reg[0] &= ~CH341_NBREAK_BITS_REG1;
- break_reg[1] &= ~CH341_NBREAK_BITS_REG2;
+ break_reg[0] &= ~CH341_NBREAK_BITS;
+ break_reg[1] &= ~CH341_LCR_ENABLE_TX;
} else {
dev_dbg(&port->dev, "%s - Leave break state requested\n", __func__);
- break_reg[0] |= CH341_NBREAK_BITS_REG1;
- break_reg[1] |= CH341_NBREAK_BITS_REG2;
+ break_reg[0] |= CH341_NBREAK_BITS;
+ break_reg[1] |= CH341_LCR_ENABLE_TX;
}
dev_dbg(&port->dev, "%s - New ch341 break register contents - reg1: %x, reg2: %x\n",
__func__, break_reg[0], break_reg[1]);
@@ -420,20 +472,20 @@ static int ch341_tiocmset(struct tty_struct *tty,
spin_lock_irqsave(&priv->lock, flags);
if (set & TIOCM_RTS)
- priv->line_control |= CH341_BIT_RTS;
+ priv->mcr |= CH341_BIT_RTS;
if (set & TIOCM_DTR)
- priv->line_control |= CH341_BIT_DTR;
+ priv->mcr |= CH341_BIT_DTR;
if (clear & TIOCM_RTS)
- priv->line_control &= ~CH341_BIT_RTS;
+ priv->mcr &= ~CH341_BIT_RTS;
if (clear & TIOCM_DTR)
- priv->line_control &= ~CH341_BIT_DTR;
- control = priv->line_control;
+ priv->mcr &= ~CH341_BIT_DTR;
+ control = priv->mcr;
spin_unlock_irqrestore(&priv->lock, flags);
return ch341_set_handshake(port->serial->dev, control);
}
-static void ch341_update_line_status(struct usb_serial_port *port,
+static void ch341_update_status(struct usb_serial_port *port,
unsigned char *data, size_t len)
{
struct ch341_private *priv = usb_get_serial_port_data(port);
@@ -448,8 +500,8 @@ static void ch341_update_line_status(struct usb_serial_port *port,
status = ~data[2] & CH341_BITS_MODEM_STAT;
spin_lock_irqsave(&priv->lock, flags);
- delta = status ^ priv->line_status;
- priv->line_status = status;
+ delta = status ^ priv->msr;
+ priv->msr = status;
spin_unlock_irqrestore(&priv->lock, flags);
if (data[1] & CH341_MULT_STAT)
@@ -502,7 +554,7 @@ static void ch341_read_int_callback(struct urb *urb)
}
usb_serial_debug_data(&port->dev, __func__, len, data);
- ch341_update_line_status(port, data, len);
+ ch341_update_status(port, data, len);
exit:
status = usb_submit_urb(urb, GFP_ATOMIC);
if (status) {
@@ -521,8 +573,8 @@ static int ch341_tiocmget(struct tty_struct *tty)
unsigned int result;
spin_lock_irqsave(&priv->lock, flags);
- mcr = priv->line_control;
- status = priv->line_status;
+ mcr = priv->mcr;
+ status = priv->msr;
spin_unlock_irqrestore(&priv->lock, flags);
result = ((mcr & CH341_BIT_DTR) ? TIOCM_DTR : 0)
@@ -539,14 +591,29 @@ static int ch341_tiocmget(struct tty_struct *tty)
static int ch341_reset_resume(struct usb_serial *serial)
{
- struct ch341_private *priv;
-
- priv = usb_get_serial_port_data(serial->port[0]);
+ struct usb_serial_port *port = serial->port[0];
+ struct ch341_private *priv = usb_get_serial_port_data(port);
+ int ret;
/* reconfigure ch341 serial port after bus-reset */
ch341_configure(serial->dev, priv);
- return 0;
+ if (tty_port_initialized(&port->port)) {
+ ret = usb_submit_urb(port->interrupt_in_urb, GFP_NOIO);
+ if (ret) {
+ dev_err(&port->dev, "failed to submit interrupt urb: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = ch341_get_status(port->serial->dev, priv);
+ if (ret < 0) {
+ dev_err(&port->dev, "failed to read modem status: %d\n",
+ ret);
+ }
+ }
+
+ return usb_serial_generic_resume(serial);
}
static struct usb_serial_driver ch341_device = {
diff --git a/drivers/usb/serial/console.c b/drivers/usb/serial/console.c
index 8967715fe6fc..fdf89800ebc3 100644
--- a/drivers/usb/serial/console.c
+++ b/drivers/usb/serial/console.c
@@ -143,6 +143,7 @@ static int usb_console_setup(struct console *co, char *options)
tty->driver = usb_serial_tty_driver;
tty->index = co->index;
init_ldsem(&tty->ldisc_sem);
+ spin_lock_init(&tty->files_lock);
INIT_LIST_HEAD(&tty->tty_files);
kref_get(&tty->driver->kref);
__module_get(tty->driver->owner);
@@ -264,8 +265,7 @@ static struct console usbcons = {
void usb_serial_console_disconnect(struct usb_serial *serial)
{
- if (serial && serial->port && serial->port[0]
- && serial->port[0] == usbcons_info.port) {
+ if (serial->port[0] == usbcons_info.port) {
usb_serial_console_exit();
usb_serial_put(serial);
}
diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c
index 243ac5ebe46a..0c55e7f64269 100644
--- a/drivers/usb/serial/cp210x.c
+++ b/drivers/usb/serial/cp210x.c
@@ -23,6 +23,9 @@
#include <linux/usb.h>
#include <linux/uaccess.h>
#include <linux/usb/serial.h>
+#include <linux/gpio/driver.h>
+#include <linux/bitops.h>
+#include <linux/mutex.h>
#define DRIVER_DESC "Silicon Labs CP210x RS232 serial adaptor driver"
@@ -33,7 +36,7 @@ static int cp210x_open(struct tty_struct *tty, struct usb_serial_port *);
static void cp210x_close(struct usb_serial_port *);
static void cp210x_get_termios(struct tty_struct *, struct usb_serial_port *);
static void cp210x_get_termios_port(struct usb_serial_port *port,
- unsigned int *cflagp, unsigned int *baudp);
+ tcflag_t *cflagp, unsigned int *baudp);
static void cp210x_change_speed(struct tty_struct *, struct usb_serial_port *,
struct ktermios *);
static void cp210x_set_termios(struct tty_struct *, struct usb_serial_port *,
@@ -44,6 +47,9 @@ static int cp210x_tiocmset(struct tty_struct *, unsigned int, unsigned int);
static int cp210x_tiocmset_port(struct usb_serial_port *port,
unsigned int, unsigned int);
static void cp210x_break_ctl(struct tty_struct *, int);
+static int cp210x_attach(struct usb_serial *);
+static void cp210x_disconnect(struct usb_serial *);
+static void cp210x_release(struct usb_serial *);
static int cp210x_port_probe(struct usb_serial_port *);
static int cp210x_port_remove(struct usb_serial_port *);
static void cp210x_dtr_rts(struct usb_serial_port *p, int on);
@@ -172,6 +178,8 @@ static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x1901, 0x0190) }, /* GE B850 CP2105 Recorder interface */
{ USB_DEVICE(0x1901, 0x0193) }, /* GE B650 CP2104 PMC interface */
{ USB_DEVICE(0x1901, 0x0194) }, /* GE Healthcare Remote Alarm Box */
+ { USB_DEVICE(0x1901, 0x0195) }, /* GE B850/B650/B450 CP2104 DP UART interface */
+ { USB_DEVICE(0x1901, 0x0196) }, /* GE B850 CP2105 DP UART interface */
{ USB_DEVICE(0x19CF, 0x3000) }, /* Parrot NMEA GPS Flight Recorder */
{ USB_DEVICE(0x1ADB, 0x0001) }, /* Schweitzer Engineering C662 Cable */
{ USB_DEVICE(0x1B1C, 0x1C00) }, /* Corsair USB Dongle */
@@ -209,6 +217,16 @@ static const struct usb_device_id id_table[] = {
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;
+#endif
+ u8 partnum;
+};
+
struct cp210x_port_private {
__u8 bInterfaceNumber;
bool has_swapped_line_ctl;
@@ -230,6 +248,9 @@ static struct usb_serial_driver cp210x_device = {
.tx_empty = cp210x_tx_empty,
.tiocmget = cp210x_tiocmget,
.tiocmset = cp210x_tiocmset,
+ .attach = cp210x_attach,
+ .disconnect = cp210x_disconnect,
+ .release = cp210x_release,
.port_probe = cp210x_port_probe,
.port_remove = cp210x_port_remove,
.dtr_rts = cp210x_dtr_rts
@@ -272,6 +293,7 @@ static struct usb_serial_driver * const serial_drivers[] = {
#define CP210X_SET_CHARS 0x19
#define CP210X_GET_BAUDRATE 0x1D
#define CP210X_SET_BAUDRATE 0x1E
+#define CP210X_VENDOR_SPECIFIC 0xFF
/* CP210X_IFC_ENABLE */
#define UART_ENABLE 0x0001
@@ -314,6 +336,21 @@ static struct usb_serial_driver * const serial_drivers[] = {
#define CONTROL_WRITE_DTR 0x0100
#define CONTROL_WRITE_RTS 0x0200
+/* CP210X_VENDOR_SPECIFIC values */
+#define CP210X_READ_LATCH 0x00C2
+#define CP210X_GET_PARTNUM 0x370B
+#define CP210X_GET_PORTCONFIG 0x370C
+#define CP210X_GET_DEVICEMODE 0x3711
+#define CP210X_WRITE_LATCH 0x37E1
+
+/* Part number definitions */
+#define CP210X_PARTNUM_CP2101 0x01
+#define CP210X_PARTNUM_CP2102 0x02
+#define CP210X_PARTNUM_CP2103 0x03
+#define CP210X_PARTNUM_CP2104 0x04
+#define CP210X_PARTNUM_CP2105 0x05
+#define CP210X_PARTNUM_CP2108 0x08
+
/* CP210X_GET_COMM_STATUS returns these 0x13 bytes */
struct cp210x_comm_status {
__le32 ulErrors;
@@ -369,6 +406,60 @@ struct cp210x_flow_ctl {
#define CP210X_SERIAL_RTS_ACTIVE 1
#define CP210X_SERIAL_RTS_FLOW_CTL 2
+/* CP210X_VENDOR_SPECIFIC, CP210X_GET_DEVICEMODE call reads these 0x2 bytes. */
+struct cp210x_pin_mode {
+ u8 eci;
+ u8 sci;
+} __packed;
+
+#define CP210X_PIN_MODE_MODEM 0
+#define CP210X_PIN_MODE_GPIO BIT(0)
+
+/*
+ * CP210X_VENDOR_SPECIFIC, CP210X_GET_PORTCONFIG call reads these 0xf bytes.
+ * Structure needs padding due to unused/unspecified bytes.
+ */
+struct cp210x_config {
+ __le16 gpio_mode;
+ u8 __pad0[2];
+ __le16 reset_state;
+ u8 __pad1[4];
+ __le16 suspend_state;
+ u8 sci_cfg;
+ u8 eci_cfg;
+ u8 device_cfg;
+} __packed;
+
+/* GPIO modes */
+#define CP210X_SCI_GPIO_MODE_OFFSET 9
+#define CP210X_SCI_GPIO_MODE_MASK GENMASK(11, 9)
+
+#define CP210X_ECI_GPIO_MODE_OFFSET 2
+#define CP210X_ECI_GPIO_MODE_MASK GENMASK(3, 2)
+
+/* CP2105 port configuration values */
+#define CP2105_GPIO0_TXLED_MODE BIT(0)
+#define CP2105_GPIO1_RXLED_MODE BIT(1)
+#define CP2105_GPIO1_RS485_MODE BIT(2)
+
+/* CP210X_VENDOR_SPECIFIC, CP210X_WRITE_LATCH call writes these 0x2 bytes. */
+struct cp210x_gpio_write {
+ u8 mask;
+ u8 state;
+} __packed;
+
+/*
+ * Helper to get interface number when we only have struct usb_serial.
+ */
+static u8 cp210x_interface_num(struct usb_serial *serial)
+{
+ struct usb_host_interface *cur_altsetting;
+
+ cur_altsetting = serial->interface->cur_altsetting;
+
+ return cur_altsetting->desc.bInterfaceNumber;
+}
+
/*
* Reads a variable-sized block of CP210X_ registers, identified by req.
* Returns data into buf in native USB byte order.
@@ -402,7 +493,7 @@ static int cp210x_read_reg_block(struct usb_serial_port *port, u8 req,
dev_err(&port->dev, "failed get req 0x%x size %d status: %d\n",
req, bufsize, result);
if (result >= 0)
- result = -EPROTO;
+ result = -EIO;
/*
* FIXME Some callers don't bother to check for error,
@@ -465,6 +556,40 @@ static int cp210x_read_u8_reg(struct usb_serial_port *port, u8 req, u8 *val)
}
/*
+ * Reads a variable-sized vendor block of CP210X_ registers, identified by val.
+ * Returns data into buf in native USB byte order.
+ */
+static int cp210x_read_vendor_block(struct usb_serial *serial, u8 type, u16 val,
+ void *buf, int bufsize)
+{
+ void *dmabuf;
+ int result;
+
+ dmabuf = kmalloc(bufsize, GFP_KERNEL);
+ if (!dmabuf)
+ return -ENOMEM;
+
+ result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+ CP210X_VENDOR_SPECIFIC, type, val,
+ cp210x_interface_num(serial), dmabuf, bufsize,
+ USB_CTRL_GET_TIMEOUT);
+ if (result == bufsize) {
+ memcpy(buf, dmabuf, bufsize);
+ result = 0;
+ } else {
+ dev_err(&serial->interface->dev,
+ "failed to get vendor val 0x%04x size %d: %d\n", val,
+ bufsize, result);
+ if (result >= 0)
+ result = -EIO;
+ }
+
+ kfree(dmabuf);
+
+ return result;
+}
+
+/*
* Writes any 16-bit CP210X_ register (req) whose value is passed
* entirely in the wValue field of the USB request.
*/
@@ -515,7 +640,7 @@ static int cp210x_write_reg_block(struct usb_serial_port *port, u8 req,
dev_err(&port->dev, "failed set req 0x%x size %d status: %d\n",
req, bufsize, result);
if (result >= 0)
- result = -EPROTO;
+ result = -EIO;
}
return result;
@@ -533,6 +658,42 @@ static int cp210x_write_u32_reg(struct usb_serial_port *port, u8 req, u32 val)
return cp210x_write_reg_block(port, req, &le32_val, sizeof(le32_val));
}
+#ifdef CONFIG_GPIOLIB
+/*
+ * Writes a variable-sized vendor block of CP210X_ registers, identified by val.
+ * Data in buf must be in native USB byte order.
+ */
+static int cp210x_write_vendor_block(struct usb_serial *serial, u8 type,
+ u16 val, void *buf, int bufsize)
+{
+ void *dmabuf;
+ int result;
+
+ dmabuf = kmemdup(buf, bufsize, GFP_KERNEL);
+ if (!dmabuf)
+ return -ENOMEM;
+
+ result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+ CP210X_VENDOR_SPECIFIC, type, val,
+ cp210x_interface_num(serial), dmabuf, bufsize,
+ USB_CTRL_SET_TIMEOUT);
+
+ kfree(dmabuf);
+
+ if (result == bufsize) {
+ result = 0;
+ } else {
+ dev_err(&serial->interface->dev,
+ "failed to set vendor val 0x%04x size %d: %d\n", val,
+ bufsize, result);
+ if (result >= 0)
+ result = -EIO;
+ }
+
+ return result;
+}
+#endif
+
/*
* Detect CP2108 GET_LINE_CTL bug and activate workaround.
* Write a known good value 0x800, read it back.
@@ -683,7 +844,7 @@ static int cp210x_get_tx_queue_byte_count(struct usb_serial_port *port,
} else {
dev_err(&port->dev, "failed to get comm status: %d\n", result);
if (result >= 0)
- result = -EPROTO;
+ result = -EIO;
}
kfree(sts);
@@ -719,7 +880,7 @@ static void cp210x_get_termios(struct tty_struct *tty,
&tty->termios.c_cflag, &baud);
tty_encode_baud_rate(tty, baud, baud);
} else {
- unsigned int cflag;
+ tcflag_t cflag;
cflag = 0;
cp210x_get_termios_port(port, &cflag, &baud);
}
@@ -730,10 +891,10 @@ static void cp210x_get_termios(struct tty_struct *tty,
* This is the heart of cp210x_get_termios which always uses a &usb_serial_port.
*/
static void cp210x_get_termios_port(struct usb_serial_port *port,
- unsigned int *cflagp, unsigned int *baudp)
+ tcflag_t *cflagp, unsigned int *baudp)
{
struct device *dev = &port->dev;
- unsigned int cflag;
+ tcflag_t cflag;
struct cp210x_flow_ctl flow_ctl;
u32 baud;
u16 bits;
@@ -930,16 +1091,9 @@ static void cp210x_set_termios(struct tty_struct *tty,
dev_dbg(dev, "%s - data bits = 7\n", __func__);
break;
case CS8:
- bits |= BITS_DATA_8;
- dev_dbg(dev, "%s - data bits = 8\n", __func__);
- break;
- /*case CS9:
- bits |= BITS_DATA_9;
- dev_dbg(dev, "%s - data bits = 9\n", __func__);
- break;*/
default:
- dev_dbg(dev, "cp210x driver does not support the number of bits requested, using 8 bit mode\n");
bits |= BITS_DATA_8;
+ dev_dbg(dev, "%s - data bits = 8\n", __func__);
break;
}
if (cp210x_write_u16_reg(port, CP210X_SET_LINE_CTL, bits))
@@ -1108,10 +1262,191 @@ static void cp210x_break_ctl(struct tty_struct *tty, int break_state)
cp210x_write_u16_reg(port, CP210X_SET_BREAK, state);
}
+#ifdef CONFIG_GPIOLIB
+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;
+ }
+
+ return 0;
+}
+
+static int cp210x_gpio_get(struct gpio_chip *gc, unsigned int gpio)
+{
+ struct usb_serial *serial = gpiochip_get_data(gc);
+ int result;
+ u8 buf;
+
+ result = cp210x_read_vendor_block(serial, REQTYPE_INTERFACE_TO_HOST,
+ CP210X_READ_LATCH, &buf, sizeof(buf));
+ if (result < 0)
+ return result;
+
+ return !!(buf & BIT(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_gpio_write buf;
+
+ if (value == 1)
+ buf.state = BIT(gpio);
+ else
+ buf.state = 0;
+
+ buf.mask = BIT(gpio);
+
+ cp210x_write_vendor_block(serial, REQTYPE_HOST_TO_INTERFACE,
+ CP210X_WRITE_LATCH, &buf, sizeof(buf));
+}
+
+static int cp210x_gpio_direction_get(struct gpio_chip *gc, unsigned int gpio)
+{
+ /* Hardware does not support an input mode */
+ return 0;
+}
+
+static int cp210x_gpio_direction_input(struct gpio_chip *gc, unsigned int gpio)
+{
+ /* Hardware does not support an input mode */
+ return -ENOTSUPP;
+}
+
+static int cp210x_gpio_direction_output(struct gpio_chip *gc, unsigned int gpio,
+ int value)
+{
+ return 0;
+}
+
+static int cp210x_gpio_set_config(struct gpio_chip *gc, unsigned int gpio,
+ unsigned long config)
+{
+ struct usb_serial *serial = gpiochip_get_data(gc);
+ struct cp210x_serial_private *priv = usb_get_serial_data(serial);
+ enum pin_config_param param = pinconf_to_config_param(config);
+
+ /* 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)))
+ return 0;
+
+ if ((param == PIN_CONFIG_DRIVE_OPEN_DRAIN) &&
+ !(priv->gpio_mode & BIT(gpio)))
+ return 0;
+
+ return -ENOTSUPP;
+}
+
+/*
+ * This function is for configuring GPIO using shared pins, where other signals
+ * are made unavailable by configuring the use of GPIO. This is believed to be
+ * only applicable to the cp2105 at this point, the other devices supported by
+ * 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)
+{
+ 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);
+ int result;
+
+ result = cp210x_read_vendor_block(serial, REQTYPE_DEVICE_TO_HOST,
+ CP210X_GET_DEVICEMODE, &mode,
+ sizeof(mode));
+ if (result < 0)
+ return result;
+
+ result = cp210x_read_vendor_block(serial, REQTYPE_DEVICE_TO_HOST,
+ CP210X_GET_PORTCONFIG, &config,
+ sizeof(config));
+ if (result < 0)
+ return result;
+
+ /* 2 banks of GPIO - One for the pins taken from each serial port */
+ if (intf_num == 0) {
+ if (mode.eci == CP210X_PIN_MODE_MODEM)
+ return 0;
+
+ priv->config = config.eci_cfg;
+ priv->gpio_mode = (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)
+ return 0;
+
+ priv->config = config.sci_cfg;
+ priv->gpio_mode = (u8)((le16_to_cpu(config.gpio_mode) &
+ CP210X_SCI_GPIO_MODE_MASK) >>
+ CP210X_SCI_GPIO_MODE_OFFSET);
+ priv->gc.ngpio = 3;
+ } else {
+ return -ENODEV;
+ }
+
+ priv->gc.label = "cp210x";
+ priv->gc.request = cp210x_gpio_request;
+ priv->gc.get_direction = cp210x_gpio_direction_get;
+ priv->gc.direction_input = cp210x_gpio_direction_input;
+ priv->gc.direction_output = cp210x_gpio_direction_output;
+ priv->gc.get = cp210x_gpio_get;
+ priv->gc.set = cp210x_gpio_set;
+ priv->gc.set_config = cp210x_gpio_set_config;
+ 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, serial);
+ if (!result)
+ priv->gpio_registered = true;
+
+ return result;
+}
+
+static void cp210x_gpio_remove(struct usb_serial *serial)
+{
+ struct cp210x_serial_private *priv = usb_get_serial_data(serial);
+
+ if (priv->gpio_registered) {
+ gpiochip_remove(&priv->gc);
+ priv->gpio_registered = false;
+ }
+}
+
+#else
+
+static int cp2105_shared_gpio_init(struct usb_serial *serial)
+{
+ return 0;
+}
+
+static void cp210x_gpio_remove(struct usb_serial *serial)
+{
+ /* Nothing to do */
+}
+
+#endif
+
static int cp210x_port_probe(struct usb_serial_port *port)
{
struct usb_serial *serial = port->serial;
- struct usb_host_interface *cur_altsetting;
struct cp210x_port_private *port_priv;
int ret;
@@ -1119,8 +1454,7 @@ static int cp210x_port_probe(struct usb_serial_port *port)
if (!port_priv)
return -ENOMEM;
- cur_altsetting = serial->interface->cur_altsetting;
- port_priv->bInterfaceNumber = cur_altsetting->desc.bInterfaceNumber;
+ port_priv->bInterfaceNumber = cp210x_interface_num(serial);
usb_set_serial_port_data(port, port_priv);
@@ -1143,6 +1477,52 @@ static int cp210x_port_remove(struct usb_serial_port *port)
return 0;
}
+static int cp210x_attach(struct usb_serial *serial)
+{
+ int result;
+ struct cp210x_serial_private *priv;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ result = cp210x_read_vendor_block(serial, REQTYPE_DEVICE_TO_HOST,
+ CP210X_GET_PARTNUM, &priv->partnum,
+ sizeof(priv->partnum));
+ if (result < 0)
+ goto err_free_priv;
+
+ 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");
+ }
+ }
+
+ return 0;
+err_free_priv:
+ kfree(priv);
+
+ return result;
+}
+
+static void cp210x_disconnect(struct usb_serial *serial)
+{
+ cp210x_gpio_remove(serial);
+}
+
+static void cp210x_release(struct usb_serial *serial)
+{
+ struct cp210x_serial_private *priv = usb_get_serial_data(serial);
+
+ cp210x_gpio_remove(serial);
+
+ kfree(priv);
+}
+
module_usb_serial_driver(serial_drivers, id_table);
MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c
index 5f17a3b9916d..80260b08398b 100644
--- a/drivers/usb/serial/cyberjack.c
+++ b/drivers/usb/serial/cyberjack.c
@@ -50,6 +50,7 @@
#define CYBERJACK_PRODUCT_ID 0x0100
/* Function prototypes */
+static int cyberjack_attach(struct usb_serial *serial);
static int cyberjack_port_probe(struct usb_serial_port *port);
static int cyberjack_port_remove(struct usb_serial_port *port);
static int cyberjack_open(struct tty_struct *tty,
@@ -77,6 +78,7 @@ static struct usb_serial_driver cyberjack_device = {
.description = "Reiner SCT Cyberjack USB card reader",
.id_table = id_table,
.num_ports = 1,
+ .attach = cyberjack_attach,
.port_probe = cyberjack_port_probe,
.port_remove = cyberjack_port_remove,
.open = cyberjack_open,
@@ -100,6 +102,14 @@ struct cyberjack_private {
short wrsent; /* Data already sent */
};
+static int cyberjack_attach(struct usb_serial *serial)
+{
+ if (serial->num_bulk_out < serial->num_ports)
+ return -ENODEV;
+
+ return 0;
+}
+
static int cyberjack_port_probe(struct usb_serial_port *port)
{
struct cyberjack_private *priv;
diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c
index bbeeb2bd55a8..90110de715e0 100644
--- a/drivers/usb/serial/cypress_m8.c
+++ b/drivers/usb/serial/cypress_m8.c
@@ -1069,7 +1069,6 @@ static void cypress_read_int_callback(struct urb *urb)
unsigned char *data = urb->transfer_buffer;
unsigned long flags;
char tty_flag = TTY_NORMAL;
- int havedata = 0;
int bytes = 0;
int result;
int i = 0;
@@ -1118,16 +1117,12 @@ static void cypress_read_int_callback(struct urb *urb)
priv->current_status = data[0] & 0xF8;
bytes = data[1] + 2;
i = 2;
- if (bytes > 2)
- havedata = 1;
break;
case packet_format_2:
/* This is for the CY7C63743... */
priv->current_status = data[0] & 0xF8;
bytes = (data[0] & 0x07) + 1;
i = 1;
- if (bytes > 1)
- havedata = 1;
break;
}
spin_unlock_irqrestore(&priv->lock, flags);
diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c
index 6a1df9e824ca..6537d3ca2797 100644
--- a/drivers/usb/serial/digi_acceleport.c
+++ b/drivers/usb/serial/digi_acceleport.c
@@ -27,6 +27,7 @@
#include <linux/uaccess.h>
#include <linux/usb.h>
#include <linux/wait.h>
+#include <linux/sched/signal.h>
#include <linux/usb/serial.h>
/* Defines */
@@ -1398,25 +1399,30 @@ 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);
- int opcode = ((unsigned char *)urb->transfer_buffer)[0];
- int len = ((unsigned char *)urb->transfer_buffer)[1];
- int port_status = ((unsigned char *)urb->transfer_buffer)[2];
- unsigned char *data = ((unsigned char *)urb->transfer_buffer) + 3;
+ unsigned char *buf = urb->transfer_buffer;
+ int opcode;
+ int len;
+ int port_status;
+ unsigned char *data;
int flag, throttled;
- int status = urb->status;
-
- /* do not process callbacks on closed ports */
- /* but do continue the read chain */
- if (urb->status == -ENOENT)
- return 0;
/* short/multiple packet check */
+ if (urb->actual_length < 2) {
+ dev_warn(&port->dev, "short packet received\n");
+ return -1;
+ }
+
+ opcode = buf[0];
+ len = buf[1];
+
if (urb->actual_length != len + 2) {
- dev_err(&port->dev, "%s: INCOMPLETE OR MULTIPLE PACKET, "
- "status=%d, port=%d, opcode=%d, len=%d, "
- "actual_length=%d, status=%d\n", __func__, status,
- priv->dp_port_num, opcode, len, urb->actual_length,
- port_status);
+ dev_err(&port->dev, "malformed packet received: port=%d, opcode=%d, len=%d, actual_length=%u\n",
+ priv->dp_port_num, opcode, len, urb->actual_length);
+ return -1;
+ }
+
+ if (opcode == DIGI_CMD_RECEIVE_DATA && len < 1) {
+ dev_err(&port->dev, "malformed data packet received\n");
return -1;
}
@@ -1430,6 +1436,9 @@ static int digi_read_inb_callback(struct urb *urb)
/* receive data */
if (opcode == DIGI_CMD_RECEIVE_DATA) {
+ port_status = buf[2];
+ data = &buf[3];
+
/* get flag from port_status */
flag = 0;
@@ -1482,16 +1491,20 @@ static int digi_read_oob_callback(struct urb *urb)
struct usb_serial *serial = port->serial;
struct tty_struct *tty;
struct digi_port *priv = usb_get_serial_port_data(port);
+ unsigned char *buf = urb->transfer_buffer;
int opcode, line, status, val;
int i;
unsigned int rts;
+ if (urb->actual_length < 4)
+ return -1;
+
/* handle each oob command */
- for (i = 0; i < urb->actual_length - 3;) {
- opcode = ((unsigned char *)urb->transfer_buffer)[i++];
- line = ((unsigned char *)urb->transfer_buffer)[i++];
- status = ((unsigned char *)urb->transfer_buffer)[i++];
- val = ((unsigned char *)urb->transfer_buffer)[i++];
+ for (i = 0; i < urb->actual_length - 3; i += 4) {
+ opcode = buf[i];
+ line = buf[i + 1];
+ status = buf[i + 2];
+ val = buf[i + 3];
dev_dbg(&port->dev, "digi_read_oob_callback: opcode=%d, line=%d, status=%d, val=%d\n",
opcode, line, status, val);
diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c
new file mode 100644
index 000000000000..22f23a429a95
--- /dev/null
+++ b/drivers/usb/serial/f81534.c
@@ -0,0 +1,1411 @@
+/*
+ * F81532/F81534 USB to Serial Ports Bridge
+ *
+ * F81532 => 2 Serial Ports
+ * F81534 => 4 Serial Ports
+ *
+ * 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.
+ *
+ * Copyright (C) 2016 Feature Integration Technology Inc., (Fintek)
+ * Copyright (C) 2016 Tom Tsai (Tom_Tsai@fintek.com.tw)
+ * Copyright (C) 2016 Peter Hong (Peter_Hong@fintek.com.tw)
+ *
+ * The F81532/F81534 had 1 control endpoint for setting, 1 endpoint bulk-out
+ * for all serial port TX and 1 endpoint bulk-in for all serial port read in
+ * (Read Data/MSR/LSR).
+ *
+ * Write URB is fixed with 512bytes, per serial port used 128Bytes.
+ * It can be described by f81534_prepare_write_buffer()
+ *
+ * Read URB is 512Bytes max, per serial port used 128Bytes.
+ * It can be described by f81534_process_read_urb() and maybe received with
+ * 128x1,2,3,4 bytes.
+ *
+ */
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+#include <linux/serial_reg.h>
+#include <linux/module.h>
+#include <linux/uaccess.h>
+
+/* Serial Port register Address */
+#define F81534_UART_BASE_ADDRESS 0x1200
+#define F81534_UART_OFFSET 0x10
+#define F81534_DIVISOR_LSB_REG (0x00 + F81534_UART_BASE_ADDRESS)
+#define F81534_DIVISOR_MSB_REG (0x01 + F81534_UART_BASE_ADDRESS)
+#define F81534_FIFO_CONTROL_REG (0x02 + F81534_UART_BASE_ADDRESS)
+#define F81534_LINE_CONTROL_REG (0x03 + F81534_UART_BASE_ADDRESS)
+#define F81534_MODEM_CONTROL_REG (0x04 + F81534_UART_BASE_ADDRESS)
+#define F81534_MODEM_STATUS_REG (0x06 + F81534_UART_BASE_ADDRESS)
+#define F81534_CONFIG1_REG (0x09 + F81534_UART_BASE_ADDRESS)
+
+#define F81534_DEF_CONF_ADDRESS_START 0x3000
+#define F81534_DEF_CONF_SIZE 8
+
+#define F81534_CUSTOM_ADDRESS_START 0x2f00
+#define F81534_CUSTOM_DATA_SIZE 0x10
+#define F81534_CUSTOM_NO_CUSTOM_DATA 0xff
+#define F81534_CUSTOM_VALID_TOKEN 0xf0
+#define F81534_CONF_OFFSET 1
+
+#define F81534_MAX_DATA_BLOCK 64
+#define F81534_MAX_BUS_RETRY 20
+
+/* Default URB timeout for USB operations */
+#define F81534_USB_MAX_RETRY 10
+#define F81534_USB_TIMEOUT 1000
+#define F81534_SET_GET_REGISTER 0xA0
+
+#define F81534_NUM_PORT 4
+#define F81534_UNUSED_PORT 0xff
+#define F81534_WRITE_BUFFER_SIZE 512
+
+#define DRIVER_DESC "Fintek F81532/F81534"
+#define FINTEK_VENDOR_ID_1 0x1934
+#define FINTEK_VENDOR_ID_2 0x2C42
+#define FINTEK_DEVICE_ID 0x1202
+#define F81534_MAX_TX_SIZE 124
+#define F81534_MAX_RX_SIZE 124
+#define F81534_RECEIVE_BLOCK_SIZE 128
+#define F81534_MAX_RECEIVE_BLOCK_SIZE 512
+
+#define F81534_TOKEN_RECEIVE 0x01
+#define F81534_TOKEN_WRITE 0x02
+#define F81534_TOKEN_TX_EMPTY 0x03
+#define F81534_TOKEN_MSR_CHANGE 0x04
+
+/*
+ * We used interal SPI bus to access FLASH section. We must wait the SPI bus to
+ * idle if we performed any command.
+ *
+ * SPI Bus status register: F81534_BUS_REG_STATUS
+ * Bit 0/1 : BUSY
+ * Bit 2 : IDLE
+ */
+#define F81534_BUS_BUSY (BIT(0) | BIT(1))
+#define F81534_BUS_IDLE BIT(2)
+#define F81534_BUS_READ_DATA 0x1004
+#define F81534_BUS_REG_STATUS 0x1003
+#define F81534_BUS_REG_START 0x1002
+#define F81534_BUS_REG_END 0x1001
+
+#define F81534_CMD_READ 0x03
+
+#define F81534_DEFAULT_BAUD_RATE 9600
+#define F81534_MAX_BAUDRATE 115200
+
+#define F81534_PORT_CONF_DISABLE_PORT BIT(3)
+#define F81534_PORT_CONF_NOT_EXIST_PORT BIT(7)
+#define F81534_PORT_UNAVAILABLE \
+ (F81534_PORT_CONF_DISABLE_PORT | F81534_PORT_CONF_NOT_EXIST_PORT)
+
+#define F81534_1X_RXTRIGGER 0xc3
+#define F81534_8X_RXTRIGGER 0xcf
+
+static const struct usb_device_id f81534_id_table[] = {
+ { USB_DEVICE(FINTEK_VENDOR_ID_1, FINTEK_DEVICE_ID) },
+ { USB_DEVICE(FINTEK_VENDOR_ID_2, FINTEK_DEVICE_ID) },
+ {} /* Terminating entry */
+};
+
+#define F81534_TX_EMPTY_BIT 0
+
+struct f81534_serial_private {
+ u8 conf_data[F81534_DEF_CONF_SIZE];
+ int tty_idx[F81534_NUM_PORT];
+ u8 setting_idx;
+ int opened_port;
+ struct mutex urb_mutex;
+};
+
+struct f81534_port_private {
+ struct mutex mcr_mutex;
+ unsigned long tx_empty;
+ spinlock_t msr_lock;
+ u8 shadow_mcr;
+ u8 shadow_msr;
+ u8 phy_num;
+};
+
+static int f81534_logic_to_phy_port(struct usb_serial *serial,
+ struct usb_serial_port *port)
+{
+ struct f81534_serial_private *serial_priv =
+ usb_get_serial_data(port->serial);
+ int count = 0;
+ int i;
+
+ for (i = 0; i < F81534_NUM_PORT; ++i) {
+ if (serial_priv->conf_data[i] & F81534_PORT_UNAVAILABLE)
+ continue;
+
+ if (port->port_number == count)
+ return i;
+
+ ++count;
+ }
+
+ return -ENODEV;
+}
+
+static int f81534_set_register(struct usb_serial *serial, u16 reg, u8 data)
+{
+ struct usb_interface *interface = serial->interface;
+ struct usb_device *dev = serial->dev;
+ size_t count = F81534_USB_MAX_RETRY;
+ int status;
+ u8 *tmp;
+
+ tmp = kmalloc(sizeof(u8), GFP_KERNEL);
+ if (!tmp)
+ return -ENOMEM;
+
+ *tmp = data;
+
+ /*
+ * Our device maybe not reply when heavily loading, We'll retry for
+ * F81534_USB_MAX_RETRY times.
+ */
+ while (count--) {
+ status = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ F81534_SET_GET_REGISTER,
+ USB_TYPE_VENDOR | USB_DIR_OUT,
+ reg, 0, tmp, sizeof(u8),
+ F81534_USB_TIMEOUT);
+ if (status > 0) {
+ status = 0;
+ break;
+ } else if (status == 0) {
+ status = -EIO;
+ }
+ }
+
+ if (status < 0) {
+ dev_err(&interface->dev, "%s: reg: %x data: %x failed: %d\n",
+ __func__, reg, data, status);
+ }
+
+ kfree(tmp);
+ return status;
+}
+
+static int f81534_get_register(struct usb_serial *serial, u16 reg, u8 *data)
+{
+ struct usb_interface *interface = serial->interface;
+ struct usb_device *dev = serial->dev;
+ size_t count = F81534_USB_MAX_RETRY;
+ int status;
+ u8 *tmp;
+
+ tmp = kmalloc(sizeof(u8), GFP_KERNEL);
+ if (!tmp)
+ return -ENOMEM;
+
+ /*
+ * Our device maybe not reply when heavily loading, We'll retry for
+ * F81534_USB_MAX_RETRY times.
+ */
+ while (count--) {
+ status = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ F81534_SET_GET_REGISTER,
+ USB_TYPE_VENDOR | USB_DIR_IN,
+ reg, 0, tmp, sizeof(u8),
+ F81534_USB_TIMEOUT);
+ if (status > 0) {
+ status = 0;
+ break;
+ } else if (status == 0) {
+ status = -EIO;
+ }
+ }
+
+ if (status < 0) {
+ dev_err(&interface->dev, "%s: reg: %x failed: %d\n", __func__,
+ reg, status);
+ goto end;
+ }
+
+ *data = *tmp;
+
+end:
+ kfree(tmp);
+ return status;
+}
+
+static int f81534_set_port_register(struct usb_serial_port *port, u16 reg,
+ u8 data)
+{
+ struct f81534_port_private *port_priv = usb_get_serial_port_data(port);
+
+ return f81534_set_register(port->serial,
+ reg + port_priv->phy_num * F81534_UART_OFFSET, data);
+}
+
+static int f81534_get_port_register(struct usb_serial_port *port, u16 reg,
+ u8 *data)
+{
+ struct f81534_port_private *port_priv = usb_get_serial_port_data(port);
+
+ return f81534_get_register(port->serial,
+ reg + port_priv->phy_num * F81534_UART_OFFSET, data);
+}
+
+/*
+ * If we try to access the internal flash via SPI bus, we should check the bus
+ * status for every command. e.g., F81534_BUS_REG_START/F81534_BUS_REG_END
+ */
+static int f81534_wait_for_spi_idle(struct usb_serial *serial)
+{
+ size_t count = F81534_MAX_BUS_RETRY;
+ u8 tmp;
+ int status;
+
+ do {
+ status = f81534_get_register(serial, F81534_BUS_REG_STATUS,
+ &tmp);
+ if (status)
+ return status;
+
+ if (tmp & F81534_BUS_BUSY)
+ continue;
+
+ if (tmp & F81534_BUS_IDLE)
+ break;
+
+ } while (--count);
+
+ if (!count) {
+ dev_err(&serial->interface->dev,
+ "%s: timed out waiting for idle SPI bus\n",
+ __func__);
+ return -EIO;
+ }
+
+ return f81534_set_register(serial, F81534_BUS_REG_STATUS,
+ tmp & ~F81534_BUS_IDLE);
+}
+
+static int f81534_get_spi_register(struct usb_serial *serial, u16 reg,
+ u8 *data)
+{
+ int status;
+
+ status = f81534_get_register(serial, reg, data);
+ if (status)
+ return status;
+
+ return f81534_wait_for_spi_idle(serial);
+}
+
+static int f81534_set_spi_register(struct usb_serial *serial, u16 reg, u8 data)
+{
+ int status;
+
+ status = f81534_set_register(serial, reg, data);
+ if (status)
+ return status;
+
+ return f81534_wait_for_spi_idle(serial);
+}
+
+static int f81534_read_flash(struct usb_serial *serial, u32 address,
+ size_t size, u8 *buf)
+{
+ u8 tmp_buf[F81534_MAX_DATA_BLOCK];
+ size_t block = 0;
+ size_t read_size;
+ size_t count;
+ int status;
+ int offset;
+ u16 reg_tmp;
+
+ status = f81534_set_spi_register(serial, F81534_BUS_REG_START,
+ F81534_CMD_READ);
+ if (status)
+ return status;
+
+ status = f81534_set_spi_register(serial, F81534_BUS_REG_START,
+ (address >> 16) & 0xff);
+ if (status)
+ return status;
+
+ status = f81534_set_spi_register(serial, F81534_BUS_REG_START,
+ (address >> 8) & 0xff);
+ if (status)
+ return status;
+
+ status = f81534_set_spi_register(serial, F81534_BUS_REG_START,
+ (address >> 0) & 0xff);
+ if (status)
+ return status;
+
+ /* Continuous read mode */
+ do {
+ read_size = min_t(size_t, F81534_MAX_DATA_BLOCK, size);
+
+ for (count = 0; count < read_size; ++count) {
+ /* To write F81534_BUS_REG_END when final byte */
+ if (size <= F81534_MAX_DATA_BLOCK &&
+ read_size == count + 1)
+ reg_tmp = F81534_BUS_REG_END;
+ else
+ reg_tmp = F81534_BUS_REG_START;
+
+ /*
+ * Dummy code, force IC to generate a read pulse, the
+ * set of value 0xf1 is dont care (any value is ok)
+ */
+ status = f81534_set_spi_register(serial, reg_tmp,
+ 0xf1);
+ if (status)
+ return status;
+
+ status = f81534_get_spi_register(serial,
+ F81534_BUS_READ_DATA,
+ &tmp_buf[count]);
+ if (status)
+ return status;
+
+ offset = count + block * F81534_MAX_DATA_BLOCK;
+ buf[offset] = tmp_buf[count];
+ }
+
+ size -= read_size;
+ ++block;
+ } while (size);
+
+ return 0;
+}
+
+static void f81534_prepare_write_buffer(struct usb_serial_port *port, u8 *buf)
+{
+ struct f81534_port_private *port_priv = usb_get_serial_port_data(port);
+ int phy_num = port_priv->phy_num;
+ u8 tx_len;
+ int i;
+
+ /*
+ * The block layout is fixed with 4x128 Bytes, per 128 Bytes a port.
+ * index 0: port phy idx (e.g., 0,1,2,3)
+ * index 1: only F81534_TOKEN_WRITE
+ * index 2: serial TX out length
+ * index 3: fix to 0
+ * index 4~127: serial out data block
+ */
+ for (i = 0; i < F81534_NUM_PORT; ++i) {
+ buf[i * F81534_RECEIVE_BLOCK_SIZE] = i;
+ buf[i * F81534_RECEIVE_BLOCK_SIZE + 1] = F81534_TOKEN_WRITE;
+ buf[i * F81534_RECEIVE_BLOCK_SIZE + 2] = 0;
+ buf[i * F81534_RECEIVE_BLOCK_SIZE + 3] = 0;
+ }
+
+ tx_len = kfifo_out_locked(&port->write_fifo,
+ &buf[phy_num * F81534_RECEIVE_BLOCK_SIZE + 4],
+ F81534_MAX_TX_SIZE, &port->lock);
+
+ buf[phy_num * F81534_RECEIVE_BLOCK_SIZE + 2] = tx_len;
+}
+
+static int f81534_submit_writer(struct usb_serial_port *port, gfp_t mem_flags)
+{
+ struct f81534_port_private *port_priv = usb_get_serial_port_data(port);
+ struct urb *urb;
+ unsigned long flags;
+ int result;
+
+ /* Check is any data in write_fifo */
+ spin_lock_irqsave(&port->lock, flags);
+
+ if (kfifo_is_empty(&port->write_fifo)) {
+ spin_unlock_irqrestore(&port->lock, flags);
+ return 0;
+ }
+
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ /* Check H/W is TXEMPTY */
+ if (!test_and_clear_bit(F81534_TX_EMPTY_BIT, &port_priv->tx_empty))
+ return 0;
+
+ urb = port->write_urbs[0];
+ f81534_prepare_write_buffer(port, port->bulk_out_buffers[0]);
+ urb->transfer_buffer_length = F81534_WRITE_BUFFER_SIZE;
+
+ result = usb_submit_urb(urb, mem_flags);
+ if (result) {
+ set_bit(F81534_TX_EMPTY_BIT, &port_priv->tx_empty);
+ dev_err(&port->dev, "%s: submit failed: %d\n", __func__,
+ result);
+ return result;
+ }
+
+ usb_serial_port_softint(port);
+ return 0;
+}
+
+static u32 f81534_calc_baud_divisor(u32 baudrate, u32 clockrate)
+{
+ if (!baudrate)
+ return 0;
+
+ /* Round to nearest divisor */
+ return DIV_ROUND_CLOSEST(clockrate, baudrate);
+}
+
+static int f81534_set_port_config(struct usb_serial_port *port, u32 baudrate,
+ u8 lcr)
+{
+ u32 divisor;
+ int status;
+ u8 value;
+
+ if (baudrate <= 1200)
+ value = F81534_1X_RXTRIGGER; /* 128 FIFO & TL: 1x */
+ else
+ value = F81534_8X_RXTRIGGER; /* 128 FIFO & TL: 8x */
+
+ status = f81534_set_port_register(port, F81534_CONFIG1_REG, value);
+ if (status) {
+ dev_err(&port->dev, "%s: CONFIG1 setting failed\n", __func__);
+ return status;
+ }
+
+ if (baudrate <= 1200)
+ value = UART_FCR_TRIGGER_1 | UART_FCR_ENABLE_FIFO; /* TL: 1 */
+ else
+ value = UART_FCR_R_TRIG_11 | UART_FCR_ENABLE_FIFO; /* TL: 14 */
+
+ status = f81534_set_port_register(port, F81534_FIFO_CONTROL_REG,
+ value);
+ if (status) {
+ dev_err(&port->dev, "%s: FCR setting failed\n", __func__);
+ return status;
+ }
+
+ divisor = f81534_calc_baud_divisor(baudrate, F81534_MAX_BAUDRATE);
+ value = UART_LCR_DLAB;
+ status = f81534_set_port_register(port, F81534_LINE_CONTROL_REG,
+ value);
+ if (status) {
+ dev_err(&port->dev, "%s: set LCR failed\n", __func__);
+ return status;
+ }
+
+ value = divisor & 0xff;
+ status = f81534_set_port_register(port, F81534_DIVISOR_LSB_REG, value);
+ if (status) {
+ dev_err(&port->dev, "%s: set DLAB LSB failed\n", __func__);
+ return status;
+ }
+
+ value = (divisor >> 8) & 0xff;
+ status = f81534_set_port_register(port, F81534_DIVISOR_MSB_REG, value);
+ if (status) {
+ dev_err(&port->dev, "%s: set DLAB MSB failed\n", __func__);
+ return status;
+ }
+
+ status = f81534_set_port_register(port, F81534_LINE_CONTROL_REG, lcr);
+ if (status) {
+ dev_err(&port->dev, "%s: set LCR failed\n", __func__);
+ return status;
+ }
+
+ return 0;
+}
+
+static int f81534_update_mctrl(struct usb_serial_port *port, unsigned int set,
+ unsigned int clear)
+{
+ struct f81534_port_private *port_priv = usb_get_serial_port_data(port);
+ int status;
+ u8 tmp;
+
+ if (((set | clear) & (TIOCM_DTR | TIOCM_RTS)) == 0)
+ return 0; /* no change */
+
+ mutex_lock(&port_priv->mcr_mutex);
+
+ /* 'Set' takes precedence over 'Clear' */
+ clear &= ~set;
+
+ /* Always enable UART_MCR_OUT2 */
+ tmp = UART_MCR_OUT2 | port_priv->shadow_mcr;
+
+ if (clear & TIOCM_DTR)
+ tmp &= ~UART_MCR_DTR;
+
+ if (clear & TIOCM_RTS)
+ tmp &= ~UART_MCR_RTS;
+
+ if (set & TIOCM_DTR)
+ tmp |= UART_MCR_DTR;
+
+ if (set & TIOCM_RTS)
+ tmp |= UART_MCR_RTS;
+
+ status = f81534_set_port_register(port, F81534_MODEM_CONTROL_REG, tmp);
+ if (status < 0) {
+ dev_err(&port->dev, "%s: MCR write failed\n", __func__);
+ mutex_unlock(&port_priv->mcr_mutex);
+ return status;
+ }
+
+ port_priv->shadow_mcr = tmp;
+ mutex_unlock(&port_priv->mcr_mutex);
+ return 0;
+}
+
+/*
+ * This function will search the data area with token F81534_CUSTOM_VALID_TOKEN
+ * for latest configuration index. If nothing found
+ * (*index = F81534_CUSTOM_NO_CUSTOM_DATA), We'll load default configure in
+ * F81534_DEF_CONF_ADDRESS_START section.
+ *
+ * Due to we only use block0 to save data, so *index should be 0 or
+ * F81534_CUSTOM_NO_CUSTOM_DATA.
+ */
+static int f81534_find_config_idx(struct usb_serial *serial, u8 *index)
+{
+ u8 tmp;
+ int status;
+
+ status = f81534_read_flash(serial, F81534_CUSTOM_ADDRESS_START, 1,
+ &tmp);
+ if (status) {
+ dev_err(&serial->interface->dev, "%s: read failed: %d\n",
+ __func__, status);
+ return status;
+ }
+
+ /* We'll use the custom data when the data is valid. */
+ if (tmp == F81534_CUSTOM_VALID_TOKEN)
+ *index = 0;
+ else
+ *index = F81534_CUSTOM_NO_CUSTOM_DATA;
+
+ return 0;
+}
+
+/*
+ * We had 2 generation of F81532/534 IC. All has an internal storage.
+ *
+ * 1st is pure USB-to-TTL RS232 IC and designed for 4 ports only, no any
+ * internal data will used. All mode and gpio control should manually set
+ * by AP or Driver and all storage space value are 0xff. The
+ * f81534_calc_num_ports() will run to final we marked as "oldest version"
+ * for this IC.
+ *
+ * 2rd is designed to more generic to use any transceiver and this is our
+ * mass production type. We'll save data in F81534_CUSTOM_ADDRESS_START
+ * (0x2f00) with 9bytes. The 1st byte is a indicater. If the token is
+ * F81534_CUSTOM_VALID_TOKEN(0xf0), the IC is 2nd gen type, the following
+ * 4bytes save port mode (0:RS232/1:RS485 Invert/2:RS485), and the last
+ * 4bytes save GPIO state(value from 0~7 to represent 3 GPIO output pin).
+ * The f81534_calc_num_ports() will run to "new style" with checking
+ * F81534_PORT_UNAVAILABLE section.
+ */
+static int f81534_calc_num_ports(struct usb_serial *serial)
+{
+ u8 setting[F81534_CUSTOM_DATA_SIZE];
+ u8 setting_idx;
+ u8 num_port = 0;
+ int status;
+ size_t i;
+
+ /* Check had custom setting */
+ status = f81534_find_config_idx(serial, &setting_idx);
+ if (status) {
+ dev_err(&serial->interface->dev, "%s: find idx failed: %d\n",
+ __func__, status);
+ return 0;
+ }
+
+ /*
+ * We'll read custom data only when data available, otherwise we'll
+ * read default value instead.
+ */
+ if (setting_idx != F81534_CUSTOM_NO_CUSTOM_DATA) {
+ status = f81534_read_flash(serial,
+ F81534_CUSTOM_ADDRESS_START +
+ F81534_CONF_OFFSET,
+ sizeof(setting), setting);
+ if (status) {
+ dev_err(&serial->interface->dev,
+ "%s: get custom data failed: %d\n",
+ __func__, status);
+ return 0;
+ }
+
+ dev_dbg(&serial->interface->dev,
+ "%s: read config from block: %d\n", __func__,
+ setting_idx);
+ } else {
+ /* Read default board setting */
+ status = f81534_read_flash(serial,
+ F81534_DEF_CONF_ADDRESS_START, F81534_NUM_PORT,
+ setting);
+
+ if (status) {
+ dev_err(&serial->interface->dev,
+ "%s: read failed: %d\n", __func__,
+ status);
+ return 0;
+ }
+
+ dev_dbg(&serial->interface->dev, "%s: read default config\n",
+ __func__);
+ }
+
+ /* New style, find all possible ports */
+ for (i = 0; i < F81534_NUM_PORT; ++i) {
+ if (setting[i] & F81534_PORT_UNAVAILABLE)
+ continue;
+
+ ++num_port;
+ }
+
+ if (num_port)
+ return num_port;
+
+ dev_warn(&serial->interface->dev, "%s: Read Failed. default 4 ports\n",
+ __func__);
+ return 4; /* Nothing found, oldest version IC */
+}
+
+static void f81534_set_termios(struct tty_struct *tty,
+ struct usb_serial_port *port,
+ struct ktermios *old_termios)
+{
+ u8 new_lcr = 0;
+ int status;
+ u32 baud;
+
+ if (C_BAUD(tty) == B0)
+ f81534_update_mctrl(port, 0, TIOCM_DTR | TIOCM_RTS);
+ else if (old_termios && (old_termios->c_cflag & CBAUD) == B0)
+ f81534_update_mctrl(port, TIOCM_DTR | TIOCM_RTS, 0);
+
+ if (C_PARENB(tty)) {
+ new_lcr |= UART_LCR_PARITY;
+
+ if (!C_PARODD(tty))
+ new_lcr |= UART_LCR_EPAR;
+
+ if (C_CMSPAR(tty))
+ new_lcr |= UART_LCR_SPAR;
+ }
+
+ if (C_CSTOPB(tty))
+ new_lcr |= UART_LCR_STOP;
+
+ switch (C_CSIZE(tty)) {
+ case CS5:
+ new_lcr |= UART_LCR_WLEN5;
+ break;
+ case CS6:
+ new_lcr |= UART_LCR_WLEN6;
+ break;
+ case CS7:
+ new_lcr |= UART_LCR_WLEN7;
+ break;
+ default:
+ case CS8:
+ new_lcr |= UART_LCR_WLEN8;
+ break;
+ }
+
+ baud = tty_get_baud_rate(tty);
+ if (!baud)
+ return;
+
+ if (baud > F81534_MAX_BAUDRATE) {
+ if (old_termios)
+ baud = tty_termios_baud_rate(old_termios);
+ else
+ baud = F81534_DEFAULT_BAUD_RATE;
+
+ tty_encode_baud_rate(tty, baud, baud);
+ }
+
+ dev_dbg(&port->dev, "%s: baud: %d\n", __func__, baud);
+
+ status = f81534_set_port_config(port, baud, new_lcr);
+ if (status < 0) {
+ dev_err(&port->dev, "%s: set port config failed: %d\n",
+ __func__, status);
+ }
+}
+
+static int f81534_submit_read_urb(struct usb_serial *serial, gfp_t flags)
+{
+ return usb_serial_generic_submit_read_urbs(serial->port[0], flags);
+}
+
+static void f81534_msr_changed(struct usb_serial_port *port, u8 msr)
+{
+ struct f81534_port_private *port_priv = usb_get_serial_port_data(port);
+ struct tty_struct *tty;
+ unsigned long flags;
+ u8 old_msr;
+
+ if (!(msr & UART_MSR_ANY_DELTA))
+ return;
+
+ spin_lock_irqsave(&port_priv->msr_lock, flags);
+ old_msr = port_priv->shadow_msr;
+ port_priv->shadow_msr = msr;
+ spin_unlock_irqrestore(&port_priv->msr_lock, flags);
+
+ dev_dbg(&port->dev, "%s: MSR from %02x to %02x\n", __func__, old_msr,
+ msr);
+
+ /* Update input line counters */
+ if (msr & UART_MSR_DCTS)
+ port->icount.cts++;
+ if (msr & UART_MSR_DDSR)
+ port->icount.dsr++;
+ if (msr & UART_MSR_DDCD)
+ port->icount.dcd++;
+ if (msr & UART_MSR_TERI)
+ port->icount.rng++;
+
+ wake_up_interruptible(&port->port.delta_msr_wait);
+
+ if (!(msr & UART_MSR_DDCD))
+ return;
+
+ dev_dbg(&port->dev, "%s: DCD Changed: phy_num: %d from %x to %x\n",
+ __func__, port_priv->phy_num, old_msr, msr);
+
+ tty = tty_port_tty_get(&port->port);
+ if (!tty)
+ return;
+
+ usb_serial_handle_dcd_change(port, tty, msr & UART_MSR_DCD);
+ tty_kref_put(tty);
+}
+
+static int f81534_read_msr(struct usb_serial_port *port)
+{
+ struct f81534_port_private *port_priv = usb_get_serial_port_data(port);
+ unsigned long flags;
+ int status;
+ u8 msr;
+
+ /* Get MSR initial value */
+ status = f81534_get_port_register(port, F81534_MODEM_STATUS_REG, &msr);
+ if (status)
+ return status;
+
+ /* Force update current state */
+ spin_lock_irqsave(&port_priv->msr_lock, flags);
+ port_priv->shadow_msr = msr;
+ spin_unlock_irqrestore(&port_priv->msr_lock, flags);
+
+ return 0;
+}
+
+static int f81534_open(struct tty_struct *tty, struct usb_serial_port *port)
+{
+ struct f81534_serial_private *serial_priv =
+ usb_get_serial_data(port->serial);
+ struct f81534_port_private *port_priv = usb_get_serial_port_data(port);
+ int status;
+
+ status = f81534_set_port_register(port,
+ F81534_FIFO_CONTROL_REG, UART_FCR_ENABLE_FIFO |
+ UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
+ if (status) {
+ dev_err(&port->dev, "%s: Clear FIFO failed: %d\n", __func__,
+ status);
+ return status;
+ }
+
+ if (tty)
+ f81534_set_termios(tty, port, NULL);
+
+ status = f81534_read_msr(port);
+ if (status)
+ return status;
+
+ mutex_lock(&serial_priv->urb_mutex);
+
+ /* Submit Read URBs for first port opened */
+ if (!serial_priv->opened_port) {
+ status = f81534_submit_read_urb(port->serial, GFP_KERNEL);
+ if (status)
+ goto exit;
+ }
+
+ serial_priv->opened_port++;
+
+exit:
+ mutex_unlock(&serial_priv->urb_mutex);
+
+ set_bit(F81534_TX_EMPTY_BIT, &port_priv->tx_empty);
+ return status;
+}
+
+static void f81534_close(struct usb_serial_port *port)
+{
+ struct f81534_serial_private *serial_priv =
+ usb_get_serial_data(port->serial);
+ struct usb_serial_port *port0 = port->serial->port[0];
+ unsigned long flags;
+ size_t i;
+
+ usb_kill_urb(port->write_urbs[0]);
+
+ spin_lock_irqsave(&port->lock, flags);
+ kfifo_reset_out(&port->write_fifo);
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ /* Kill Read URBs when final port closed */
+ mutex_lock(&serial_priv->urb_mutex);
+ serial_priv->opened_port--;
+
+ if (!serial_priv->opened_port) {
+ for (i = 0; i < ARRAY_SIZE(port0->read_urbs); ++i)
+ usb_kill_urb(port0->read_urbs[i]);
+ }
+
+ mutex_unlock(&serial_priv->urb_mutex);
+}
+
+static int f81534_get_serial_info(struct usb_serial_port *port,
+ struct serial_struct __user *retinfo)
+{
+ 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 = F81534_MAX_BAUDRATE;
+
+ if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
+ return -EFAULT;
+
+ 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)
+{
+ struct f81534_port_private *port_priv = usb_get_serial_port_data(port);
+ int phy_num = data[0];
+ size_t read_size = 0;
+ size_t i;
+ char tty_flag;
+ int status;
+ u8 lsr;
+
+ /*
+ * The block layout is 128 Bytes
+ * index 0: port phy idx (e.g., 0,1,2,3),
+ * index 1: It's could be
+ * F81534_TOKEN_RECEIVE
+ * F81534_TOKEN_TX_EMPTY
+ * F81534_TOKEN_MSR_CHANGE
+ * index 2: serial in size (data+lsr, must be even)
+ * meaningful for F81534_TOKEN_RECEIVE only
+ * index 3: current MSR with this device
+ * index 4~127: serial in data block (data+lsr, must be even)
+ */
+ switch (data[1]) {
+ case F81534_TOKEN_TX_EMPTY:
+ set_bit(F81534_TX_EMPTY_BIT, &port_priv->tx_empty);
+
+ /* Try to submit writer */
+ status = f81534_submit_writer(port, GFP_ATOMIC);
+ if (status)
+ dev_err(&port->dev, "%s: submit failed\n", __func__);
+ return;
+
+ case F81534_TOKEN_MSR_CHANGE:
+ f81534_msr_changed(port, data[3]);
+ return;
+
+ case F81534_TOKEN_RECEIVE:
+ read_size = data[2];
+ if (read_size > F81534_MAX_RX_SIZE) {
+ dev_err(&port->dev,
+ "%s: phy: %d read_size: %zu larger than: %d\n",
+ __func__, phy_num, read_size,
+ F81534_MAX_RX_SIZE);
+ return;
+ }
+
+ break;
+
+ default:
+ dev_warn(&port->dev, "%s: unknown token: %02x\n", __func__,
+ data[1]);
+ return;
+ }
+
+ for (i = 4; i < 4 + read_size; i += 2) {
+ tty_flag = TTY_NORMAL;
+ lsr = data[i + 1];
+
+ if (lsr & UART_LSR_BRK_ERROR_BITS) {
+ if (lsr & UART_LSR_BI) {
+ tty_flag = TTY_BREAK;
+ port->icount.brk++;
+ usb_serial_handle_break(port);
+ } else if (lsr & UART_LSR_PE) {
+ tty_flag = TTY_PARITY;
+ port->icount.parity++;
+ } else if (lsr & UART_LSR_FE) {
+ tty_flag = TTY_FRAME;
+ port->icount.frame++;
+ }
+
+ if (lsr & UART_LSR_OE) {
+ port->icount.overrun++;
+ tty_insert_flip_char(&port->port, 0,
+ TTY_OVERRUN);
+ }
+ }
+
+ if (port->port.console && port->sysrq) {
+ if (usb_serial_handle_sysrq_char(port, data[i]))
+ continue;
+ }
+
+ tty_insert_flip_char(&port->port, data[i], tty_flag);
+ }
+
+ tty_flip_buffer_push(&port->port);
+}
+
+static void f81534_process_read_urb(struct urb *urb)
+{
+ struct f81534_serial_private *serial_priv;
+ struct usb_serial_port *port;
+ struct usb_serial *serial;
+ u8 *buf;
+ int phy_port_num;
+ int tty_port_num;
+ size_t i;
+
+ if (!urb->actual_length ||
+ urb->actual_length % F81534_RECEIVE_BLOCK_SIZE) {
+ return;
+ }
+
+ port = urb->context;
+ serial = port->serial;
+ buf = urb->transfer_buffer;
+ serial_priv = usb_get_serial_data(serial);
+
+ for (i = 0; i < urb->actual_length; i += F81534_RECEIVE_BLOCK_SIZE) {
+ phy_port_num = buf[i];
+ if (phy_port_num >= F81534_NUM_PORT) {
+ dev_err(&port->dev,
+ "%s: phy_port_num: %d larger than: %d\n",
+ __func__, phy_port_num, F81534_NUM_PORT);
+ continue;
+ }
+
+ tty_port_num = serial_priv->tty_idx[phy_port_num];
+ port = serial->port[tty_port_num];
+
+ if (tty_port_initialized(&port->port))
+ f81534_process_per_serial_block(port, &buf[i]);
+ }
+}
+
+static void f81534_write_usb_callback(struct urb *urb)
+{
+ struct usb_serial_port *port = urb->context;
+
+ switch (urb->status) {
+ case 0:
+ break;
+ case -ENOENT:
+ case -ECONNRESET:
+ case -ESHUTDOWN:
+ dev_dbg(&port->dev, "%s - urb stopped: %d\n",
+ __func__, urb->status);
+ return;
+ case -EPIPE:
+ dev_err(&port->dev, "%s - urb stopped: %d\n",
+ __func__, urb->status);
+ return;
+ default:
+ dev_dbg(&port->dev, "%s - nonzero urb status: %d\n",
+ __func__, urb->status);
+ break;
+ }
+}
+
+static int f81534_setup_ports(struct usb_serial *serial)
+{
+ struct usb_serial_port *port;
+ u8 port0_out_address;
+ int buffer_size;
+ size_t i;
+
+ /*
+ * In our system architecture, we had 2 or 4 serial ports,
+ * but only get 1 set of bulk in/out endpoints.
+ *
+ * The usb-serial subsystem will generate port 0 data,
+ * but port 1/2/3 will not. It's will generate write URB and buffer
+ * by following code and use the port0 read URB for read operation.
+ */
+ for (i = 1; i < serial->num_ports; ++i) {
+ port0_out_address = serial->port[0]->bulk_out_endpointAddress;
+ buffer_size = serial->port[0]->bulk_out_size;
+ port = serial->port[i];
+
+ if (kfifo_alloc(&port->write_fifo, PAGE_SIZE, GFP_KERNEL))
+ return -ENOMEM;
+
+ port->bulk_out_size = buffer_size;
+ port->bulk_out_endpointAddress = port0_out_address;
+
+ port->write_urbs[0] = usb_alloc_urb(0, GFP_KERNEL);
+ if (!port->write_urbs[0])
+ return -ENOMEM;
+
+ port->bulk_out_buffers[0] = kzalloc(buffer_size, GFP_KERNEL);
+ if (!port->bulk_out_buffers[0])
+ return -ENOMEM;
+
+ usb_fill_bulk_urb(port->write_urbs[0], serial->dev,
+ usb_sndbulkpipe(serial->dev,
+ port0_out_address),
+ port->bulk_out_buffers[0], buffer_size,
+ serial->type->write_bulk_callback, port);
+
+ port->write_urb = port->write_urbs[0];
+ port->bulk_out_buffer = port->bulk_out_buffers[0];
+ }
+
+ return 0;
+}
+
+static int f81534_probe(struct usb_serial *serial,
+ const struct usb_device_id *id)
+{
+ struct usb_endpoint_descriptor *endpoint;
+ struct usb_host_interface *iface_desc;
+ struct device *dev;
+ int num_bulk_in = 0;
+ int num_bulk_out = 0;
+ int size_bulk_in = 0;
+ int size_bulk_out = 0;
+ int i;
+
+ dev = &serial->interface->dev;
+ iface_desc = serial->interface->cur_altsetting;
+
+ for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+ endpoint = &iface_desc->endpoint[i].desc;
+
+ if (usb_endpoint_is_bulk_in(endpoint)) {
+ ++num_bulk_in;
+ size_bulk_in = usb_endpoint_maxp(endpoint);
+ }
+
+ if (usb_endpoint_is_bulk_out(endpoint)) {
+ ++num_bulk_out;
+ size_bulk_out = usb_endpoint_maxp(endpoint);
+ }
+ }
+
+ if (num_bulk_in != 1 || num_bulk_out != 1) {
+ dev_err(dev, "expected endpoints not found\n");
+ return -ENODEV;
+ }
+
+ if (size_bulk_out != F81534_WRITE_BUFFER_SIZE ||
+ size_bulk_in != F81534_MAX_RECEIVE_BLOCK_SIZE) {
+ dev_err(dev, "unsupported endpoint max packet size\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int f81534_attach(struct usb_serial *serial)
+{
+ struct f81534_serial_private *serial_priv;
+ int index = 0;
+ int status;
+ int i;
+
+ serial_priv = devm_kzalloc(&serial->interface->dev,
+ sizeof(*serial_priv), GFP_KERNEL);
+ if (!serial_priv)
+ return -ENOMEM;
+
+ usb_set_serial_data(serial, serial_priv);
+
+ mutex_init(&serial_priv->urb_mutex);
+
+ status = f81534_setup_ports(serial);
+ if (status)
+ return status;
+
+ /* Check had custom setting */
+ status = f81534_find_config_idx(serial, &serial_priv->setting_idx);
+ if (status) {
+ dev_err(&serial->interface->dev, "%s: find idx failed: %d\n",
+ __func__, status);
+ return status;
+ }
+
+ /*
+ * We'll read custom data only when data available, otherwise we'll
+ * read default value instead.
+ */
+ if (serial_priv->setting_idx == F81534_CUSTOM_NO_CUSTOM_DATA) {
+ /*
+ * The default configuration layout:
+ * byte 0/1/2/3: uart setting
+ */
+ status = f81534_read_flash(serial,
+ F81534_DEF_CONF_ADDRESS_START,
+ F81534_DEF_CONF_SIZE,
+ serial_priv->conf_data);
+ if (status) {
+ dev_err(&serial->interface->dev,
+ "%s: read reserve data failed: %d\n",
+ __func__, status);
+ return status;
+ }
+ } else {
+ /* Only read 8 bytes for mode & GPIO */
+ status = f81534_read_flash(serial,
+ F81534_CUSTOM_ADDRESS_START +
+ F81534_CONF_OFFSET,
+ sizeof(serial_priv->conf_data),
+ serial_priv->conf_data);
+ if (status) {
+ dev_err(&serial->interface->dev,
+ "%s: idx: %d get data failed: %d\n",
+ __func__, serial_priv->setting_idx,
+ status);
+ return status;
+ }
+ }
+
+ /* Assign phy-to-logic mapping */
+ for (i = 0; i < F81534_NUM_PORT; ++i) {
+ if (serial_priv->conf_data[i] & F81534_PORT_UNAVAILABLE)
+ continue;
+
+ serial_priv->tty_idx[i] = index++;
+ dev_dbg(&serial->interface->dev,
+ "%s: phy_num: %d, tty_idx: %d\n", __func__, i,
+ serial_priv->tty_idx[i]);
+ }
+
+ return 0;
+}
+
+static int f81534_port_probe(struct usb_serial_port *port)
+{
+ struct f81534_port_private *port_priv;
+ int ret;
+
+ port_priv = devm_kzalloc(&port->dev, sizeof(*port_priv), GFP_KERNEL);
+ if (!port_priv)
+ return -ENOMEM;
+
+ spin_lock_init(&port_priv->msr_lock);
+ mutex_init(&port_priv->mcr_mutex);
+
+ /* Assign logic-to-phy mapping */
+ ret = f81534_logic_to_phy_port(port->serial, port);
+ if (ret < 0)
+ return ret;
+
+ port_priv->phy_num = ret;
+ usb_set_serial_port_data(port, port_priv);
+ dev_dbg(&port->dev, "%s: port_number: %d, phy_num: %d\n", __func__,
+ port->port_number, port_priv->phy_num);
+
+ return 0;
+}
+
+static int f81534_tiocmget(struct tty_struct *tty)
+{
+ struct usb_serial_port *port = tty->driver_data;
+ struct f81534_port_private *port_priv = usb_get_serial_port_data(port);
+ int status;
+ int r;
+ u8 msr;
+ u8 mcr;
+
+ /* Read current MSR from device */
+ status = f81534_get_port_register(port, F81534_MODEM_STATUS_REG, &msr);
+ if (status)
+ return status;
+
+ mutex_lock(&port_priv->mcr_mutex);
+ mcr = port_priv->shadow_mcr;
+ mutex_unlock(&port_priv->mcr_mutex);
+
+ r = (mcr & UART_MCR_DTR ? TIOCM_DTR : 0) |
+ (mcr & UART_MCR_RTS ? TIOCM_RTS : 0) |
+ (msr & UART_MSR_CTS ? TIOCM_CTS : 0) |
+ (msr & UART_MSR_DCD ? TIOCM_CAR : 0) |
+ (msr & UART_MSR_RI ? TIOCM_RI : 0) |
+ (msr & UART_MSR_DSR ? TIOCM_DSR : 0);
+
+ return r;
+}
+
+static int f81534_tiocmset(struct tty_struct *tty, unsigned int set,
+ unsigned int clear)
+{
+ struct usb_serial_port *port = tty->driver_data;
+
+ return f81534_update_mctrl(port, set, clear);
+}
+
+static void f81534_dtr_rts(struct usb_serial_port *port, int on)
+{
+ if (on)
+ f81534_update_mctrl(port, TIOCM_DTR | TIOCM_RTS, 0);
+ else
+ f81534_update_mctrl(port, 0, TIOCM_DTR | TIOCM_RTS);
+}
+
+static int f81534_write(struct tty_struct *tty, struct usb_serial_port *port,
+ const u8 *buf, int count)
+{
+ int bytes_out, status;
+
+ if (!count)
+ return 0;
+
+ bytes_out = kfifo_in_locked(&port->write_fifo, buf, count,
+ &port->lock);
+
+ status = f81534_submit_writer(port, GFP_ATOMIC);
+ if (status) {
+ dev_err(&port->dev, "%s: submit failed\n", __func__);
+ return status;
+ }
+
+ return bytes_out;
+}
+
+static bool f81534_tx_empty(struct usb_serial_port *port)
+{
+ struct f81534_port_private *port_priv = usb_get_serial_port_data(port);
+
+ return test_bit(F81534_TX_EMPTY_BIT, &port_priv->tx_empty);
+}
+
+static int f81534_resume(struct usb_serial *serial)
+{
+ struct f81534_serial_private *serial_priv =
+ usb_get_serial_data(serial);
+ struct usb_serial_port *port;
+ int error = 0;
+ int status;
+ size_t i;
+
+ /*
+ * We'll register port 0 bulkin when port had opened, It'll take all
+ * port received data, MSR register change and TX_EMPTY information.
+ */
+ mutex_lock(&serial_priv->urb_mutex);
+
+ if (serial_priv->opened_port) {
+ status = f81534_submit_read_urb(serial, GFP_NOIO);
+ if (status) {
+ mutex_unlock(&serial_priv->urb_mutex);
+ return status;
+ }
+ }
+
+ mutex_unlock(&serial_priv->urb_mutex);
+
+ for (i = 0; i < serial->num_ports; i++) {
+ port = serial->port[i];
+ if (!tty_port_initialized(&port->port))
+ continue;
+
+ status = f81534_submit_writer(port, GFP_NOIO);
+ if (status) {
+ dev_err(&port->dev, "%s: submit failed\n", __func__);
+ ++error;
+ }
+ }
+
+ if (error)
+ return -EIO;
+
+ return 0;
+}
+
+static struct usb_serial_driver f81534_device = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "f81534",
+ },
+ .description = DRIVER_DESC,
+ .id_table = f81534_id_table,
+ .open = f81534_open,
+ .close = f81534_close,
+ .write = f81534_write,
+ .tx_empty = f81534_tx_empty,
+ .calc_num_ports = f81534_calc_num_ports,
+ .probe = f81534_probe,
+ .attach = f81534_attach,
+ .port_probe = f81534_port_probe,
+ .dtr_rts = f81534_dtr_rts,
+ .process_read_urb = f81534_process_read_urb,
+ .ioctl = f81534_ioctl,
+ .tiocmget = f81534_tiocmget,
+ .tiocmset = f81534_tiocmset,
+ .write_bulk_callback = f81534_write_usb_callback,
+ .set_termios = f81534_set_termios,
+ .resume = f81534_resume,
+};
+
+static struct usb_serial_driver *const serial_drivers[] = {
+ &f81534_device, NULL
+};
+
+module_usb_serial_driver(serial_drivers, f81534_id_table);
+
+MODULE_DEVICE_TABLE(usb, f81534_id_table);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Peter Hong <Peter_Hong@fintek.com.tw>");
+MODULE_AUTHOR("Tom Tsai <Tom_Tsai@fintek.com.tw>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index 6e9fc8bcc285..c540de15aad2 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -1439,10 +1439,13 @@ static int read_latency_timer(struct usb_serial_port *port)
FTDI_SIO_GET_LATENCY_TIMER_REQUEST_TYPE,
0, priv->interface,
buf, 1, WDR_TIMEOUT);
- if (rv < 0)
+ if (rv < 1) {
dev_err(&port->dev, "Unable to read latency timer: %i\n", rv);
- else
+ if (rv >= 0)
+ rv = -EIO;
+ } else {
priv->latency = buf[0];
+ }
kfree(buf);
@@ -1455,8 +1458,6 @@ static int get_serial_info(struct usb_serial_port *port,
struct ftdi_private *priv = usb_get_serial_port_data(port);
struct serial_struct tmp;
- if (!retinfo)
- return -EFAULT;
memset(&tmp, 0, sizeof(tmp));
tmp.flags = priv->flags;
tmp.baud_base = priv->baud_base;
@@ -1533,14 +1534,11 @@ check_and_exit:
}
static int get_lsr_info(struct usb_serial_port *port,
- struct serial_struct __user *retinfo)
+ unsigned int __user *retinfo)
{
struct ftdi_private *priv = usb_get_serial_port_data(port);
unsigned int result = 0;
- if (!retinfo)
- return -EFAULT;
-
if (priv->transmit_empty)
result = TIOCSER_TEMT;
@@ -1807,8 +1805,6 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port)
mutex_init(&priv->cfg_lock);
- priv->flags = ASYNC_LOW_LATENCY;
-
if (quirk && quirk->port_probe)
quirk->port_probe(priv);
@@ -2072,6 +2068,20 @@ static int ftdi_process_packet(struct usb_serial_port *port,
priv->prev_status = status;
}
+ /* save if the transmitter is empty or not */
+ if (packet[1] & FTDI_RS_TEMT)
+ priv->transmit_empty = 1;
+ else
+ priv->transmit_empty = 0;
+
+ len -= 2;
+ if (!len)
+ return 0; /* status only */
+
+ /*
+ * Break and error status must only be processed for packets with
+ * data payload to avoid over-reporting.
+ */
flag = TTY_NORMAL;
if (packet[1] & FTDI_RS_ERR_MASK) {
/* Break takes precedence over parity, which takes precedence
@@ -2094,15 +2104,6 @@ static int ftdi_process_packet(struct usb_serial_port *port,
}
}
- /* save if the transmitter is empty or not */
- if (packet[1] & FTDI_RS_TEMT)
- priv->transmit_empty = 1;
- else
- priv->transmit_empty = 0;
-
- len -= 2;
- if (!len)
- return 0; /* status only */
port->icount.rx += len;
ch = packet + 2;
@@ -2433,8 +2434,12 @@ static int ftdi_get_modem_status(struct usb_serial_port *port,
FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE,
0, priv->interface,
buf, len, WDR_TIMEOUT);
- if (ret < 0) {
+
+ /* NOTE: We allow short responses and handle that below. */
+ if (ret < 1) {
dev_err(&port->dev, "failed to get modem status: %d\n", ret);
+ if (ret >= 0)
+ ret = -EIO;
ret = usb_translate_errors(ret);
goto out;
}
@@ -2485,20 +2490,15 @@ static int ftdi_ioctl(struct tty_struct *tty,
unsigned int cmd, unsigned long arg)
{
struct usb_serial_port *port = tty->driver_data;
+ void __user *argp = (void __user *)arg;
- /* Based on code from acm.c and others */
switch (cmd) {
-
- case TIOCGSERIAL: /* gets serial port data */
- return get_serial_info(port,
- (struct serial_struct __user *) arg);
-
- case TIOCSSERIAL: /* sets serial port data */
- return set_serial_info(tty, port,
- (struct serial_struct __user *) arg);
+ case TIOCGSERIAL:
+ return get_serial_info(port, argp);
+ case TIOCSSERIAL:
+ return set_serial_info(tty, port, argp);
case TIOCSERGETLSR:
- return get_lsr_info(port, (struct serial_struct __user *)arg);
- break;
+ return get_lsr_info(port, argp);
default:
break;
}
diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c
index 97cabf803c2f..b2f2e87aed94 100644
--- a/drivers/usb/serial/garmin_gps.c
+++ b/drivers/usb/serial/garmin_gps.c
@@ -1043,6 +1043,7 @@ static int garmin_write_bulk(struct usb_serial_port *port,
"%s - usb_submit_urb(write bulk) failed with status = %d\n",
__func__, status);
count = status;
+ kfree(buffer);
}
/* we are done with this urb, so let the host driver
diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c
index 944de657a07a..49ce2be90fa0 100644
--- a/drivers/usb/serial/generic.c
+++ b/drivers/usb/serial/generic.c
@@ -10,6 +10,7 @@
*/
#include <linux/kernel.h>
+#include <linux/sched/signal.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/sysrq.h>
diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c
index 11c05ce2f35f..bb7673e80a57 100644
--- a/drivers/usb/serial/io_edgeport.c
+++ b/drivers/usb/serial/io_edgeport.c
@@ -57,6 +57,88 @@
#define OPEN_TIMEOUT (5*HZ) /* 5 seconds */
+static const struct usb_device_id edgeport_2port_id_table[] = {
+ { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_2) },
+ { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_2I) },
+ { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_421) },
+ { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_21) },
+ { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_2_DIN) },
+ { }
+};
+
+static const struct usb_device_id edgeport_4port_id_table[] = {
+ { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_4) },
+ { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_RAPIDPORT_4) },
+ { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_4T) },
+ { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_MT4X56USB) },
+ { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_4I) },
+ { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8_DUAL_CPU) },
+ { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_4_DIN) },
+ { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_22I) },
+ { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_412_4) },
+ { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_COMPATIBLE) },
+ { }
+};
+
+static const struct usb_device_id edgeport_8port_id_table[] = {
+ { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8) },
+ { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_16_DUAL_CPU) },
+ { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8I) },
+ { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8R) },
+ { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8RR) },
+ { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_412_8) },
+ { }
+};
+
+static const struct usb_device_id Epic_port_id_table[] = {
+ { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0202) },
+ { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0203) },
+ { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0310) },
+ { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0311) },
+ { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0312) },
+ { USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A758) },
+ { USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A794) },
+ { USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A225) },
+ { }
+};
+
+/* Devices that this driver supports */
+static const struct usb_device_id id_table_combined[] = {
+ { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_4) },
+ { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_RAPIDPORT_4) },
+ { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_4T) },
+ { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_MT4X56USB) },
+ { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_2) },
+ { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_4I) },
+ { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_2I) },
+ { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_421) },
+ { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_21) },
+ { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8_DUAL_CPU) },
+ { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8) },
+ { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_2_DIN) },
+ { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_4_DIN) },
+ { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_16_DUAL_CPU) },
+ { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_22I) },
+ { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_412_4) },
+ { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_COMPATIBLE) },
+ { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8I) },
+ { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8R) },
+ { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8RR) },
+ { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_412_8) },
+ { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0202) },
+ { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0203) },
+ { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0310) },
+ { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0311) },
+ { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0312) },
+ { USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A758) },
+ { USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A794) },
+ { USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A225) },
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, id_table_combined);
+
+
/* receive port state */
enum RXSTATE {
EXPECT_HDR1 = 0, /* Expect header byte 1 */
@@ -217,8 +299,6 @@ static void edge_release(struct usb_serial *serial);
static int edge_port_probe(struct usb_serial_port *port);
static int edge_port_remove(struct usb_serial_port *port);
-#include "io_tables.h" /* all of the devices that this driver supports */
-
/* function prototypes for all of our local functions */
static void process_rcvd_data(struct edgeport_serial *edge_serial,
@@ -492,20 +572,24 @@ static int get_epic_descriptor(struct edgeport_serial *ep)
int result;
struct usb_serial *serial = ep->serial;
struct edgeport_product_info *product_info = &ep->product_info;
- struct edge_compatibility_descriptor *epic = &ep->epic_descriptor;
+ struct edge_compatibility_descriptor *epic;
struct edge_compatibility_bits *bits;
struct device *dev = &serial->dev->dev;
ep->is_epic = 0;
+
+ epic = kmalloc(sizeof(*epic), GFP_KERNEL);
+ if (!epic)
+ return -ENOMEM;
+
result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
USB_REQUEST_ION_GET_EPIC_DESC,
0xC0, 0x00, 0x00,
- &ep->epic_descriptor,
- sizeof(struct edge_compatibility_descriptor),
+ epic, sizeof(*epic),
300);
-
- if (result > 0) {
+ if (result == sizeof(*epic)) {
ep->is_epic = 1;
+ memcpy(&ep->epic_descriptor, epic, sizeof(*epic));
memset(product_info, 0, sizeof(struct edgeport_product_info));
product_info->NumPorts = epic->NumPorts;
@@ -534,8 +618,16 @@ static int get_epic_descriptor(struct edgeport_serial *ep)
dev_dbg(dev, " IOSPWriteLCR : %s\n", bits->IOSPWriteLCR ? "TRUE": "FALSE");
dev_dbg(dev, " IOSPSetBaudRate : %s\n", bits->IOSPSetBaudRate ? "TRUE": "FALSE");
dev_dbg(dev, " TrueEdgeport : %s\n", bits->TrueEdgeport ? "TRUE": "FALSE");
+
+ result = 0;
+ } else if (result >= 0) {
+ dev_warn(&serial->interface->dev, "short epic descriptor received: %d\n",
+ result);
+ result = -EIO;
}
+ kfree(epic);
+
return result;
}
@@ -1554,16 +1646,12 @@ static int get_serial_info(struct edgeport_port *edge_port,
{
struct serial_struct tmp;
- if (!retinfo)
- return -EFAULT;
-
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.flags = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ;
tmp.xmit_fifo_size = edge_port->maxTxCredits;
tmp.baud_base = 9600;
tmp.close_delay = 5*HZ;
@@ -2093,8 +2181,7 @@ static int rom_write(struct usb_serial *serial, __u16 extAddr, __u16 addr,
* rom_read
* reads a number of bytes from the Edgeport device starting at the given
* address.
- * If successful returns the number of bytes read, otherwise it returns
- * a negative error number of the problem.
+ * Returns zero on success or a negative error number.
****************************************************************************/
static int rom_read(struct usb_serial *serial, __u16 extAddr,
__u16 addr, __u16 length, __u8 *data)
@@ -2119,12 +2206,17 @@ static int rom_read(struct usb_serial *serial, __u16 extAddr,
USB_REQUEST_ION_READ_ROM,
0xC0, addr, extAddr, transfer_buffer,
current_length, 300);
- if (result < 0)
+ if (result < current_length) {
+ if (result >= 0)
+ result = -EIO;
break;
+ }
memcpy(data, transfer_buffer, current_length);
length -= current_length;
addr += current_length;
data += current_length;
+
+ result = 0;
}
kfree(transfer_buffer);
@@ -2578,9 +2670,10 @@ static void get_manufacturing_desc(struct edgeport_serial *edge_serial)
EDGE_MANUF_DESC_LEN,
(__u8 *)(&edge_serial->manuf_descriptor));
- if (response < 1)
- dev_err(dev, "error in getting manufacturer descriptor\n");
- else {
+ if (response < 0) {
+ dev_err(dev, "error in getting manufacturer descriptor: %d\n",
+ response);
+ } else {
char string[30];
dev_dbg(dev, "**Manufacturer Descriptor\n");
dev_dbg(dev, " RomSize: %dK\n",
@@ -2637,9 +2730,10 @@ static void get_boot_desc(struct edgeport_serial *edge_serial)
EDGE_BOOT_DESC_LEN,
(__u8 *)(&edge_serial->boot_descriptor));
- if (response < 1)
- dev_err(dev, "error in getting boot descriptor\n");
- else {
+ if (response < 0) {
+ dev_err(dev, "error in getting boot descriptor: %d\n",
+ response);
+ } else {
dev_dbg(dev, "**Boot Descriptor:\n");
dev_dbg(dev, " BootCodeLength: %d\n",
le16_to_cpu(edge_serial->boot_descriptor.BootCodeLength));
@@ -2754,6 +2848,11 @@ static int edge_startup(struct usb_serial *serial)
EDGE_COMPATIBILITY_MASK1,
EDGE_COMPATIBILITY_MASK2 };
+ if (serial->num_bulk_in < 1 || serial->num_interrupt_in < 1) {
+ dev_err(&serial->interface->dev, "missing endpoints\n");
+ return -ENODEV;
+ }
+
dev = serial->dev;
/* create our private serial structure */
@@ -2777,7 +2876,7 @@ static int edge_startup(struct usb_serial *serial)
dev_info(&serial->dev->dev, "%s detected\n", edge_serial->name);
/* Read the epic descriptor */
- if (get_epic_descriptor(edge_serial) <= 0) {
+ if (get_epic_descriptor(edge_serial) < 0) {
/* memcpy descriptor to Supports structures */
memcpy(&edge_serial->epic_descriptor.Supports, descriptor,
sizeof(struct edge_compatibility_bits));
@@ -3013,6 +3112,139 @@ static int edge_port_remove(struct usb_serial_port *port)
return 0;
}
+static struct usb_serial_driver edgeport_2port_device = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "edgeport_2",
+ },
+ .description = "Edgeport 2 port adapter",
+ .id_table = edgeport_2port_id_table,
+ .num_ports = 2,
+ .open = edge_open,
+ .close = edge_close,
+ .throttle = edge_throttle,
+ .unthrottle = edge_unthrottle,
+ .attach = edge_startup,
+ .disconnect = edge_disconnect,
+ .release = edge_release,
+ .port_probe = edge_port_probe,
+ .port_remove = edge_port_remove,
+ .ioctl = edge_ioctl,
+ .set_termios = edge_set_termios,
+ .tiocmget = edge_tiocmget,
+ .tiocmset = edge_tiocmset,
+ .tiocmiwait = usb_serial_generic_tiocmiwait,
+ .get_icount = usb_serial_generic_get_icount,
+ .write = edge_write,
+ .write_room = edge_write_room,
+ .chars_in_buffer = edge_chars_in_buffer,
+ .break_ctl = edge_break,
+ .read_int_callback = edge_interrupt_callback,
+ .read_bulk_callback = edge_bulk_in_callback,
+ .write_bulk_callback = edge_bulk_out_data_callback,
+};
+
+static struct usb_serial_driver edgeport_4port_device = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "edgeport_4",
+ },
+ .description = "Edgeport 4 port adapter",
+ .id_table = edgeport_4port_id_table,
+ .num_ports = 4,
+ .open = edge_open,
+ .close = edge_close,
+ .throttle = edge_throttle,
+ .unthrottle = edge_unthrottle,
+ .attach = edge_startup,
+ .disconnect = edge_disconnect,
+ .release = edge_release,
+ .port_probe = edge_port_probe,
+ .port_remove = edge_port_remove,
+ .ioctl = edge_ioctl,
+ .set_termios = edge_set_termios,
+ .tiocmget = edge_tiocmget,
+ .tiocmset = edge_tiocmset,
+ .tiocmiwait = usb_serial_generic_tiocmiwait,
+ .get_icount = usb_serial_generic_get_icount,
+ .write = edge_write,
+ .write_room = edge_write_room,
+ .chars_in_buffer = edge_chars_in_buffer,
+ .break_ctl = edge_break,
+ .read_int_callback = edge_interrupt_callback,
+ .read_bulk_callback = edge_bulk_in_callback,
+ .write_bulk_callback = edge_bulk_out_data_callback,
+};
+
+static struct usb_serial_driver edgeport_8port_device = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "edgeport_8",
+ },
+ .description = "Edgeport 8 port adapter",
+ .id_table = edgeport_8port_id_table,
+ .num_ports = 8,
+ .open = edge_open,
+ .close = edge_close,
+ .throttle = edge_throttle,
+ .unthrottle = edge_unthrottle,
+ .attach = edge_startup,
+ .disconnect = edge_disconnect,
+ .release = edge_release,
+ .port_probe = edge_port_probe,
+ .port_remove = edge_port_remove,
+ .ioctl = edge_ioctl,
+ .set_termios = edge_set_termios,
+ .tiocmget = edge_tiocmget,
+ .tiocmset = edge_tiocmset,
+ .tiocmiwait = usb_serial_generic_tiocmiwait,
+ .get_icount = usb_serial_generic_get_icount,
+ .write = edge_write,
+ .write_room = edge_write_room,
+ .chars_in_buffer = edge_chars_in_buffer,
+ .break_ctl = edge_break,
+ .read_int_callback = edge_interrupt_callback,
+ .read_bulk_callback = edge_bulk_in_callback,
+ .write_bulk_callback = edge_bulk_out_data_callback,
+};
+
+static struct usb_serial_driver epic_device = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "epic",
+ },
+ .description = "EPiC device",
+ .id_table = Epic_port_id_table,
+ .num_ports = 1,
+ .open = edge_open,
+ .close = edge_close,
+ .throttle = edge_throttle,
+ .unthrottle = edge_unthrottle,
+ .attach = edge_startup,
+ .disconnect = edge_disconnect,
+ .release = edge_release,
+ .port_probe = edge_port_probe,
+ .port_remove = edge_port_remove,
+ .ioctl = edge_ioctl,
+ .set_termios = edge_set_termios,
+ .tiocmget = edge_tiocmget,
+ .tiocmset = edge_tiocmset,
+ .tiocmiwait = usb_serial_generic_tiocmiwait,
+ .get_icount = usb_serial_generic_get_icount,
+ .write = edge_write,
+ .write_room = edge_write_room,
+ .chars_in_buffer = edge_chars_in_buffer,
+ .break_ctl = edge_break,
+ .read_int_callback = edge_interrupt_callback,
+ .read_bulk_callback = edge_bulk_in_callback,
+ .write_bulk_callback = edge_bulk_out_data_callback,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+ &edgeport_2port_device, &edgeport_4port_device,
+ &edgeport_8port_device, &epic_device, NULL
+};
+
module_usb_serial_driver(serial_drivers, id_table_combined);
MODULE_AUTHOR(DRIVER_AUTHOR);
diff --git a/drivers/usb/serial/io_tables.h b/drivers/usb/serial/io_tables.h
deleted file mode 100644
index ae5fac5656c9..000000000000
--- a/drivers/usb/serial/io_tables.h
+++ /dev/null
@@ -1,232 +0,0 @@
-/*
- * IO Edgeport Driver tables
- *
- * Copyright (C) 2001
- * Greg Kroah-Hartman (greg@kroah.com)
- *
- * 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.
- *
- */
-
-#ifndef IO_TABLES_H
-#define IO_TABLES_H
-
-static const struct usb_device_id edgeport_2port_id_table[] = {
- { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_2) },
- { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_2I) },
- { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_421) },
- { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_21) },
- { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_2_DIN) },
- { }
-};
-
-static const struct usb_device_id edgeport_4port_id_table[] = {
- { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_4) },
- { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_RAPIDPORT_4) },
- { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_4T) },
- { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_MT4X56USB) },
- { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_4I) },
- { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8_DUAL_CPU) },
- { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_4_DIN) },
- { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_22I) },
- { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_412_4) },
- { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_COMPATIBLE) },
- { }
-};
-
-static const struct usb_device_id edgeport_8port_id_table[] = {
- { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8) },
- { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_16_DUAL_CPU) },
- { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8I) },
- { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8R) },
- { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8RR) },
- { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_412_8) },
- { }
-};
-
-static const struct usb_device_id Epic_port_id_table[] = {
- { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0202) },
- { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0203) },
- { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0310) },
- { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0311) },
- { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0312) },
- { USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A758) },
- { USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A794) },
- { USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A225) },
- { }
-};
-
-/* Devices that this driver supports */
-static const struct usb_device_id id_table_combined[] = {
- { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_4) },
- { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_RAPIDPORT_4) },
- { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_4T) },
- { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_MT4X56USB) },
- { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_2) },
- { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_4I) },
- { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_2I) },
- { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_421) },
- { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_21) },
- { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8_DUAL_CPU) },
- { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8) },
- { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_2_DIN) },
- { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_4_DIN) },
- { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_16_DUAL_CPU) },
- { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_22I) },
- { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_412_4) },
- { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_COMPATIBLE) },
- { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8I) },
- { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8R) },
- { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8RR) },
- { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_412_8) },
- { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0202) },
- { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0203) },
- { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0310) },
- { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0311) },
- { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0312) },
- { USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A758) },
- { USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A794) },
- { USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A225) },
- { } /* Terminating entry */
-};
-
-MODULE_DEVICE_TABLE(usb, id_table_combined);
-
-static struct usb_serial_driver edgeport_2port_device = {
- .driver = {
- .owner = THIS_MODULE,
- .name = "edgeport_2",
- },
- .description = "Edgeport 2 port adapter",
- .id_table = edgeport_2port_id_table,
- .num_ports = 2,
- .open = edge_open,
- .close = edge_close,
- .throttle = edge_throttle,
- .unthrottle = edge_unthrottle,
- .attach = edge_startup,
- .disconnect = edge_disconnect,
- .release = edge_release,
- .port_probe = edge_port_probe,
- .port_remove = edge_port_remove,
- .ioctl = edge_ioctl,
- .set_termios = edge_set_termios,
- .tiocmget = edge_tiocmget,
- .tiocmset = edge_tiocmset,
- .tiocmiwait = usb_serial_generic_tiocmiwait,
- .get_icount = usb_serial_generic_get_icount,
- .write = edge_write,
- .write_room = edge_write_room,
- .chars_in_buffer = edge_chars_in_buffer,
- .break_ctl = edge_break,
- .read_int_callback = edge_interrupt_callback,
- .read_bulk_callback = edge_bulk_in_callback,
- .write_bulk_callback = edge_bulk_out_data_callback,
-};
-
-static struct usb_serial_driver edgeport_4port_device = {
- .driver = {
- .owner = THIS_MODULE,
- .name = "edgeport_4",
- },
- .description = "Edgeport 4 port adapter",
- .id_table = edgeport_4port_id_table,
- .num_ports = 4,
- .open = edge_open,
- .close = edge_close,
- .throttle = edge_throttle,
- .unthrottle = edge_unthrottle,
- .attach = edge_startup,
- .disconnect = edge_disconnect,
- .release = edge_release,
- .port_probe = edge_port_probe,
- .port_remove = edge_port_remove,
- .ioctl = edge_ioctl,
- .set_termios = edge_set_termios,
- .tiocmget = edge_tiocmget,
- .tiocmset = edge_tiocmset,
- .tiocmiwait = usb_serial_generic_tiocmiwait,
- .get_icount = usb_serial_generic_get_icount,
- .write = edge_write,
- .write_room = edge_write_room,
- .chars_in_buffer = edge_chars_in_buffer,
- .break_ctl = edge_break,
- .read_int_callback = edge_interrupt_callback,
- .read_bulk_callback = edge_bulk_in_callback,
- .write_bulk_callback = edge_bulk_out_data_callback,
-};
-
-static struct usb_serial_driver edgeport_8port_device = {
- .driver = {
- .owner = THIS_MODULE,
- .name = "edgeport_8",
- },
- .description = "Edgeport 8 port adapter",
- .id_table = edgeport_8port_id_table,
- .num_ports = 8,
- .open = edge_open,
- .close = edge_close,
- .throttle = edge_throttle,
- .unthrottle = edge_unthrottle,
- .attach = edge_startup,
- .disconnect = edge_disconnect,
- .release = edge_release,
- .port_probe = edge_port_probe,
- .port_remove = edge_port_remove,
- .ioctl = edge_ioctl,
- .set_termios = edge_set_termios,
- .tiocmget = edge_tiocmget,
- .tiocmset = edge_tiocmset,
- .tiocmiwait = usb_serial_generic_tiocmiwait,
- .get_icount = usb_serial_generic_get_icount,
- .write = edge_write,
- .write_room = edge_write_room,
- .chars_in_buffer = edge_chars_in_buffer,
- .break_ctl = edge_break,
- .read_int_callback = edge_interrupt_callback,
- .read_bulk_callback = edge_bulk_in_callback,
- .write_bulk_callback = edge_bulk_out_data_callback,
-};
-
-static struct usb_serial_driver epic_device = {
- .driver = {
- .owner = THIS_MODULE,
- .name = "epic",
- },
- .description = "EPiC device",
- .id_table = Epic_port_id_table,
- .num_ports = 1,
- .open = edge_open,
- .close = edge_close,
- .throttle = edge_throttle,
- .unthrottle = edge_unthrottle,
- .attach = edge_startup,
- .disconnect = edge_disconnect,
- .release = edge_release,
- .port_probe = edge_port_probe,
- .port_remove = edge_port_remove,
- .ioctl = edge_ioctl,
- .set_termios = edge_set_termios,
- .tiocmget = edge_tiocmget,
- .tiocmset = edge_tiocmset,
- .tiocmiwait = usb_serial_generic_tiocmiwait,
- .get_icount = usb_serial_generic_get_icount,
- .write = edge_write,
- .write_room = edge_write_room,
- .chars_in_buffer = edge_chars_in_buffer,
- .break_ctl = edge_break,
- .read_int_callback = edge_interrupt_callback,
- .read_bulk_callback = edge_bulk_in_callback,
- .write_bulk_callback = edge_bulk_out_data_callback,
-};
-
-static struct usb_serial_driver * const serial_drivers[] = {
- &edgeport_2port_device, &edgeport_4port_device,
- &edgeport_8port_device, &epic_device, NULL
-};
-
-#endif
-
diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c
index fce82fd79f77..a76b95d32157 100644
--- a/drivers/usb/serial/io_ti.c
+++ b/drivers/usb/serial/io_ti.c
@@ -1499,8 +1499,7 @@ static int do_boot_mode(struct edgeport_serial *serial,
dev_dbg(dev, "%s - Download successful -- Device rebooting...\n", __func__);
- /* return an error on purpose */
- return -ENODEV;
+ return 1;
}
stayinbootmode:
@@ -1508,7 +1507,7 @@ stayinbootmode:
dev_dbg(dev, "%s - STAYING IN BOOT MODE\n", __func__);
serial->product_info.TiMode = TI_MODE_BOOT;
- return 0;
+ return 1;
}
static int ti_do_config(struct edgeport_port *port, int feature, int on)
@@ -1675,6 +1674,12 @@ static void edge_interrupt_callback(struct urb *urb)
function = TIUMP_GET_FUNC_FROM_CODE(data[0]);
dev_dbg(dev, "%s - port_number %d, function %d, info 0x%x\n", __func__,
port_number, function, data[1]);
+
+ if (port_number >= edge_serial->serial->num_ports) {
+ dev_err(dev, "bad port number %d\n", port_number);
+ goto exit;
+ }
+
port = edge_serial->serial->port[port_number];
edge_port = usb_get_serial_port_data(port);
if (!edge_port) {
@@ -1756,7 +1761,7 @@ static void edge_bulk_in_callback(struct urb *urb)
port_number = edge_port->port->port_number;
- if (edge_port->lsr_event) {
+ if (urb->actual_length > 0 && edge_port->lsr_event) {
edge_port->lsr_event = 0;
dev_dbg(dev, "%s ===== Port %u LSR Status = %02x, Data = %02x ======\n",
__func__, port_number, edge_port->lsr_mask, *data);
@@ -2459,9 +2464,6 @@ static int get_serial_info(struct edgeport_port *edge_port,
struct serial_struct tmp;
unsigned cwait;
- if (!retinfo)
- return -EFAULT;
-
cwait = edge_port->port->port.closing_wait;
if (cwait != ASYNC_CLOSING_WAIT_NONE)
cwait = jiffies_to_msecs(cwait) / 10;
@@ -2472,7 +2474,6 @@ static int get_serial_info(struct edgeport_port *edge_port,
tmp.line = edge_port->port->minor;
tmp.port = edge_port->port->port_number;
tmp.irq = 0;
- tmp.flags = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ;
tmp.xmit_fifo_size = edge_port->port->bulk_out_size;
tmp.baud_base = 9600;
tmp.close_delay = 5*HZ;
@@ -2549,6 +2550,13 @@ static int edge_startup(struct usb_serial *serial)
int status;
u16 product_id;
+ /* Make sure we have the required endpoints when in download mode. */
+ if (serial->interface->cur_altsetting->desc.bNumEndpoints > 1) {
+ if (serial->num_bulk_in < serial->num_ports ||
+ serial->num_bulk_out < serial->num_ports)
+ return -ENODEV;
+ }
+
/* create our private serial structure */
edge_serial = kzalloc(sizeof(struct edgeport_serial), GFP_KERNEL);
if (!edge_serial)
@@ -2556,14 +2564,18 @@ static int edge_startup(struct usb_serial *serial)
mutex_init(&edge_serial->es_lock);
edge_serial->serial = serial;
+ INIT_DELAYED_WORK(&edge_serial->heartbeat_work, edge_heartbeat_work);
usb_set_serial_data(serial, edge_serial);
status = download_fw(edge_serial);
- if (status) {
+ if (status < 0) {
kfree(edge_serial);
return status;
}
+ if (status > 0)
+ return 1; /* bind but do not register any ports */
+
product_id = le16_to_cpu(
edge_serial->serial->dev->descriptor.idProduct);
@@ -2575,7 +2587,6 @@ static int edge_startup(struct usb_serial *serial)
}
}
- INIT_DELAYED_WORK(&edge_serial->heartbeat_work, edge_heartbeat_work);
edge_heartbeat_schedule(edge_serial);
return 0;
@@ -2583,6 +2594,9 @@ static int edge_startup(struct usb_serial *serial)
static void edge_disconnect(struct usb_serial *serial)
{
+ struct edgeport_serial *edge_serial = usb_get_serial_data(serial);
+
+ cancel_delayed_work_sync(&edge_serial->heartbeat_work);
}
static void edge_release(struct usb_serial *serial)
diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c
index 344b4eea4bd5..030390f37b0a 100644
--- a/drivers/usb/serial/iuu_phoenix.c
+++ b/drivers/usb/serial/iuu_phoenix.c
@@ -68,6 +68,16 @@ struct iuu_private {
u32 clk;
};
+static int iuu_attach(struct usb_serial *serial)
+{
+ unsigned char num_ports = serial->num_ports;
+
+ if (serial->num_bulk_in < num_ports || serial->num_bulk_out < num_ports)
+ return -ENODEV;
+
+ return 0;
+}
+
static int iuu_port_probe(struct usb_serial_port *port)
{
struct iuu_private *priv;
@@ -966,7 +976,6 @@ static int iuu_open(struct tty_struct *tty, struct usb_serial_port *port)
{
struct usb_serial *serial = port->serial;
struct device *dev = &port->dev;
- u8 *buf;
int result;
int baud;
u32 actual;
@@ -981,20 +990,8 @@ static int iuu_open(struct tty_struct *tty, struct usb_serial_port *port)
usb_clear_halt(serial->dev, port->write_urb->pipe);
usb_clear_halt(serial->dev, port->read_urb->pipe);
- buf = kmalloc(10, GFP_KERNEL);
- if (buf == NULL)
- return -ENOMEM;
-
priv->poll = 0;
- /* initialize writebuf */
-#define FISH(a, b, c, d) do { \
- result = usb_control_msg(port->serial->dev, \
- usb_rcvctrlpipe(port->serial->dev, 0), \
- b, a, c, d, buf, 1, 1000); \
- dev_dbg(dev, "0x%x:0x%x:0x%x:0x%x %d - %x\n", a, b, c, d, result, \
- buf[0]); } while (0);
-
#define SOUP(a, b, c, d) do { \
result = usb_control_msg(port->serial->dev, \
usb_sndctrlpipe(port->serial->dev, 0), \
@@ -1007,7 +1004,7 @@ static int iuu_open(struct tty_struct *tty, struct usb_serial_port *port)
/* sprintf(buf ,"%c%c%c%c",0x03,0x02,0x02,0x0); */
SOUP(0x03, 0x02, 0x02, 0x0);
- kfree(buf);
+
iuu_led(port, 0xF000, 0xF000, 0, 0xFF);
iuu_uart_on(port);
if (boost < 100)
@@ -1196,6 +1193,7 @@ static struct usb_serial_driver iuu_device = {
.tiocmset = iuu_tiocmset,
.set_termios = iuu_set_termios,
.init_termios = iuu_init_termios,
+ .attach = iuu_attach,
.port_probe = iuu_port_probe,
.port_remove = iuu_port_remove,
};
diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c
index 1f9414bdd649..5662d324edd2 100644
--- a/drivers/usb/serial/keyspan.c
+++ b/drivers/usb/serial/keyspan.c
@@ -41,11 +41,508 @@
#include <linux/usb.h>
#include <linux/usb/serial.h>
#include <linux/usb/ezusb.h>
-#include "keyspan.h"
#define DRIVER_AUTHOR "Hugh Blemings <hugh@misc.nu"
#define DRIVER_DESC "Keyspan USB to Serial Converter Driver"
+/* Function prototypes for Keyspan serial converter */
+static int keyspan_open(struct tty_struct *tty, struct usb_serial_port *port);
+static void keyspan_close(struct usb_serial_port *port);
+static void keyspan_dtr_rts(struct usb_serial_port *port, int on);
+static int keyspan_startup(struct usb_serial *serial);
+static void keyspan_disconnect(struct usb_serial *serial);
+static void keyspan_release(struct usb_serial *serial);
+static int keyspan_port_probe(struct usb_serial_port *port);
+static int keyspan_port_remove(struct usb_serial_port *port);
+static int keyspan_write_room(struct tty_struct *tty);
+static int keyspan_write(struct tty_struct *tty, struct usb_serial_port *port,
+ const unsigned char *buf, int count);
+static void keyspan_send_setup(struct usb_serial_port *port, int reset_port);
+static void keyspan_set_termios(struct tty_struct *tty,
+ struct usb_serial_port *port,
+ struct ktermios *old);
+static void keyspan_break_ctl(struct tty_struct *tty, int break_state);
+static int keyspan_tiocmget(struct tty_struct *tty);
+static int keyspan_tiocmset(struct tty_struct *tty, unsigned int set,
+ unsigned int clear);
+static int keyspan_fake_startup(struct usb_serial *serial);
+
+static int keyspan_usa19_calc_baud(struct usb_serial_port *port,
+ u32 baud_rate, u32 baudclk,
+ u8 *rate_hi, u8 *rate_low,
+ u8 *prescaler, int portnum);
+static int keyspan_usa19w_calc_baud(struct usb_serial_port *port,
+ u32 baud_rate, u32 baudclk,
+ u8 *rate_hi, u8 *rate_low,
+ u8 *prescaler, int portnum);
+static int keyspan_usa28_calc_baud(struct usb_serial_port *port,
+ u32 baud_rate, u32 baudclk,
+ u8 *rate_hi, u8 *rate_low,
+ u8 *prescaler, int portnum);
+static int keyspan_usa19hs_calc_baud(struct usb_serial_port *port,
+ u32 baud_rate, u32 baudclk,
+ u8 *rate_hi, u8 *rate_low,
+ u8 *prescaler, int portnum);
+
+static int keyspan_usa28_send_setup(struct usb_serial *serial,
+ struct usb_serial_port *port,
+ int reset_port);
+static int keyspan_usa26_send_setup(struct usb_serial *serial,
+ struct usb_serial_port *port,
+ int reset_port);
+static int keyspan_usa49_send_setup(struct usb_serial *serial,
+ struct usb_serial_port *port,
+ int reset_port);
+static int keyspan_usa90_send_setup(struct usb_serial *serial,
+ struct usb_serial_port *port,
+ int reset_port);
+static int keyspan_usa67_send_setup(struct usb_serial *serial,
+ struct usb_serial_port *port,
+ int reset_port);
+
+/* Values used for baud rate calculation - device specific */
+#define KEYSPAN_INVALID_BAUD_RATE (-1)
+#define KEYSPAN_BAUD_RATE_OK (0)
+#define KEYSPAN_USA18X_BAUDCLK (12000000L) /* a guess */
+#define KEYSPAN_USA19_BAUDCLK (12000000L)
+#define KEYSPAN_USA19W_BAUDCLK (24000000L)
+#define KEYSPAN_USA19HS_BAUDCLK (14769231L)
+#define KEYSPAN_USA28_BAUDCLK (1843200L)
+#define KEYSPAN_USA28X_BAUDCLK (12000000L)
+#define KEYSPAN_USA49W_BAUDCLK (48000000L)
+
+/* Some constants used to characterise each device. */
+#define KEYSPAN_MAX_NUM_PORTS (4)
+#define KEYSPAN_MAX_FLIPS (2)
+
+/*
+ * Device info for the Keyspan serial converter, used by the overall
+ * usb-serial probe function.
+ */
+#define KEYSPAN_VENDOR_ID (0x06cd)
+
+/* Product IDs for the products supported, pre-renumeration */
+#define keyspan_usa18x_pre_product_id 0x0105
+#define keyspan_usa19_pre_product_id 0x0103
+#define keyspan_usa19qi_pre_product_id 0x010b
+#define keyspan_mpr_pre_product_id 0x011b
+#define keyspan_usa19qw_pre_product_id 0x0118
+#define keyspan_usa19w_pre_product_id 0x0106
+#define keyspan_usa28_pre_product_id 0x0101
+#define keyspan_usa28x_pre_product_id 0x0102
+#define keyspan_usa28xa_pre_product_id 0x0114
+#define keyspan_usa28xb_pre_product_id 0x0113
+#define keyspan_usa49w_pre_product_id 0x0109
+#define keyspan_usa49wlc_pre_product_id 0x011a
+
+/*
+ * Product IDs post-renumeration. Note that the 28x and 28xb have the same
+ * id's post-renumeration but behave identically so it's not an issue. As
+ * such, the 28xb is not listed in any of the device tables.
+ */
+#define keyspan_usa18x_product_id 0x0112
+#define keyspan_usa19_product_id 0x0107
+#define keyspan_usa19qi_product_id 0x010c
+#define keyspan_usa19hs_product_id 0x0121
+#define keyspan_mpr_product_id 0x011c
+#define keyspan_usa19qw_product_id 0x0119
+#define keyspan_usa19w_product_id 0x0108
+#define keyspan_usa28_product_id 0x010f
+#define keyspan_usa28x_product_id 0x0110
+#define keyspan_usa28xa_product_id 0x0115
+#define keyspan_usa28xb_product_id 0x0110
+#define keyspan_usa28xg_product_id 0x0135
+#define keyspan_usa49w_product_id 0x010a
+#define keyspan_usa49wlc_product_id 0x012a
+#define keyspan_usa49wg_product_id 0x0131
+
+struct keyspan_device_details {
+ /* product ID value */
+ int product_id;
+
+ enum {msg_usa26, msg_usa28, msg_usa49, msg_usa90, msg_usa67} msg_format;
+
+ /* Number of physical ports */
+ int num_ports;
+
+ /* 1 if endpoint flipping used on input, 0 if not */
+ int indat_endp_flip;
+
+ /* 1 if endpoint flipping used on output, 0 if not */
+ int outdat_endp_flip;
+
+ /*
+ * Table mapping input data endpoint IDs to physical port
+ * number and flip if used
+ */
+ int indat_endpoints[KEYSPAN_MAX_NUM_PORTS];
+
+ /* Same for output endpoints */
+ int outdat_endpoints[KEYSPAN_MAX_NUM_PORTS];
+
+ /* Input acknowledge endpoints */
+ int inack_endpoints[KEYSPAN_MAX_NUM_PORTS];
+
+ /* Output control endpoints */
+ int outcont_endpoints[KEYSPAN_MAX_NUM_PORTS];
+
+ /* Endpoint used for input status */
+ int instat_endpoint;
+
+ /* Endpoint used for input data 49WG only */
+ int indat_endpoint;
+
+ /* Endpoint used for global control functions */
+ int glocont_endpoint;
+
+ int (*calculate_baud_rate)(struct usb_serial_port *port,
+ u32 baud_rate, u32 baudclk,
+ u8 *rate_hi, u8 *rate_low, u8 *prescaler,
+ int portnum);
+ u32 baudclk;
+};
+
+/*
+ * Now for each device type we setup the device detail structure with the
+ * appropriate information (provided in Keyspan's documentation)
+ */
+
+static const struct keyspan_device_details usa18x_device_details = {
+ .product_id = keyspan_usa18x_product_id,
+ .msg_format = msg_usa26,
+ .num_ports = 1,
+ .indat_endp_flip = 0,
+ .outdat_endp_flip = 1,
+ .indat_endpoints = {0x81},
+ .outdat_endpoints = {0x01},
+ .inack_endpoints = {0x85},
+ .outcont_endpoints = {0x05},
+ .instat_endpoint = 0x87,
+ .indat_endpoint = -1,
+ .glocont_endpoint = 0x07,
+ .calculate_baud_rate = keyspan_usa19w_calc_baud,
+ .baudclk = KEYSPAN_USA18X_BAUDCLK,
+};
+
+static const struct keyspan_device_details usa19_device_details = {
+ .product_id = keyspan_usa19_product_id,
+ .msg_format = msg_usa28,
+ .num_ports = 1,
+ .indat_endp_flip = 1,
+ .outdat_endp_flip = 1,
+ .indat_endpoints = {0x81},
+ .outdat_endpoints = {0x01},
+ .inack_endpoints = {0x83},
+ .outcont_endpoints = {0x03},
+ .instat_endpoint = 0x84,
+ .indat_endpoint = -1,
+ .glocont_endpoint = -1,
+ .calculate_baud_rate = keyspan_usa19_calc_baud,
+ .baudclk = KEYSPAN_USA19_BAUDCLK,
+};
+
+static const struct keyspan_device_details usa19qi_device_details = {
+ .product_id = keyspan_usa19qi_product_id,
+ .msg_format = msg_usa28,
+ .num_ports = 1,
+ .indat_endp_flip = 1,
+ .outdat_endp_flip = 1,
+ .indat_endpoints = {0x81},
+ .outdat_endpoints = {0x01},
+ .inack_endpoints = {0x83},
+ .outcont_endpoints = {0x03},
+ .instat_endpoint = 0x84,
+ .indat_endpoint = -1,
+ .glocont_endpoint = -1,
+ .calculate_baud_rate = keyspan_usa28_calc_baud,
+ .baudclk = KEYSPAN_USA19_BAUDCLK,
+};
+
+static const struct keyspan_device_details mpr_device_details = {
+ .product_id = keyspan_mpr_product_id,
+ .msg_format = msg_usa28,
+ .num_ports = 1,
+ .indat_endp_flip = 1,
+ .outdat_endp_flip = 1,
+ .indat_endpoints = {0x81},
+ .outdat_endpoints = {0x01},
+ .inack_endpoints = {0x83},
+ .outcont_endpoints = {0x03},
+ .instat_endpoint = 0x84,
+ .indat_endpoint = -1,
+ .glocont_endpoint = -1,
+ .calculate_baud_rate = keyspan_usa28_calc_baud,
+ .baudclk = KEYSPAN_USA19_BAUDCLK,
+};
+
+static const struct keyspan_device_details usa19qw_device_details = {
+ .product_id = keyspan_usa19qw_product_id,
+ .msg_format = msg_usa26,
+ .num_ports = 1,
+ .indat_endp_flip = 0,
+ .outdat_endp_flip = 1,
+ .indat_endpoints = {0x81},
+ .outdat_endpoints = {0x01},
+ .inack_endpoints = {0x85},
+ .outcont_endpoints = {0x05},
+ .instat_endpoint = 0x87,
+ .indat_endpoint = -1,
+ .glocont_endpoint = 0x07,
+ .calculate_baud_rate = keyspan_usa19w_calc_baud,
+ .baudclk = KEYSPAN_USA19W_BAUDCLK,
+};
+
+static const struct keyspan_device_details usa19w_device_details = {
+ .product_id = keyspan_usa19w_product_id,
+ .msg_format = msg_usa26,
+ .num_ports = 1,
+ .indat_endp_flip = 0,
+ .outdat_endp_flip = 1,
+ .indat_endpoints = {0x81},
+ .outdat_endpoints = {0x01},
+ .inack_endpoints = {0x85},
+ .outcont_endpoints = {0x05},
+ .instat_endpoint = 0x87,
+ .indat_endpoint = -1,
+ .glocont_endpoint = 0x07,
+ .calculate_baud_rate = keyspan_usa19w_calc_baud,
+ .baudclk = KEYSPAN_USA19W_BAUDCLK,
+};
+
+static const struct keyspan_device_details usa19hs_device_details = {
+ .product_id = keyspan_usa19hs_product_id,
+ .msg_format = msg_usa90,
+ .num_ports = 1,
+ .indat_endp_flip = 0,
+ .outdat_endp_flip = 0,
+ .indat_endpoints = {0x81},
+ .outdat_endpoints = {0x01},
+ .inack_endpoints = {-1},
+ .outcont_endpoints = {0x02},
+ .instat_endpoint = 0x82,
+ .indat_endpoint = -1,
+ .glocont_endpoint = -1,
+ .calculate_baud_rate = keyspan_usa19hs_calc_baud,
+ .baudclk = KEYSPAN_USA19HS_BAUDCLK,
+};
+
+static const struct keyspan_device_details usa28_device_details = {
+ .product_id = keyspan_usa28_product_id,
+ .msg_format = msg_usa28,
+ .num_ports = 2,
+ .indat_endp_flip = 1,
+ .outdat_endp_flip = 1,
+ .indat_endpoints = {0x81, 0x83},
+ .outdat_endpoints = {0x01, 0x03},
+ .inack_endpoints = {0x85, 0x86},
+ .outcont_endpoints = {0x05, 0x06},
+ .instat_endpoint = 0x87,
+ .indat_endpoint = -1,
+ .glocont_endpoint = 0x07,
+ .calculate_baud_rate = keyspan_usa28_calc_baud,
+ .baudclk = KEYSPAN_USA28_BAUDCLK,
+};
+
+static const struct keyspan_device_details usa28x_device_details = {
+ .product_id = keyspan_usa28x_product_id,
+ .msg_format = msg_usa26,
+ .num_ports = 2,
+ .indat_endp_flip = 0,
+ .outdat_endp_flip = 1,
+ .indat_endpoints = {0x81, 0x83},
+ .outdat_endpoints = {0x01, 0x03},
+ .inack_endpoints = {0x85, 0x86},
+ .outcont_endpoints = {0x05, 0x06},
+ .instat_endpoint = 0x87,
+ .indat_endpoint = -1,
+ .glocont_endpoint = 0x07,
+ .calculate_baud_rate = keyspan_usa19w_calc_baud,
+ .baudclk = KEYSPAN_USA28X_BAUDCLK,
+};
+
+static const struct keyspan_device_details usa28xa_device_details = {
+ .product_id = keyspan_usa28xa_product_id,
+ .msg_format = msg_usa26,
+ .num_ports = 2,
+ .indat_endp_flip = 0,
+ .outdat_endp_flip = 1,
+ .indat_endpoints = {0x81, 0x83},
+ .outdat_endpoints = {0x01, 0x03},
+ .inack_endpoints = {0x85, 0x86},
+ .outcont_endpoints = {0x05, 0x06},
+ .instat_endpoint = 0x87,
+ .indat_endpoint = -1,
+ .glocont_endpoint = 0x07,
+ .calculate_baud_rate = keyspan_usa19w_calc_baud,
+ .baudclk = KEYSPAN_USA28X_BAUDCLK,
+};
+
+static const struct keyspan_device_details usa28xg_device_details = {
+ .product_id = keyspan_usa28xg_product_id,
+ .msg_format = msg_usa67,
+ .num_ports = 2,
+ .indat_endp_flip = 0,
+ .outdat_endp_flip = 0,
+ .indat_endpoints = {0x84, 0x88},
+ .outdat_endpoints = {0x02, 0x06},
+ .inack_endpoints = {-1, -1},
+ .outcont_endpoints = {-1, -1},
+ .instat_endpoint = 0x81,
+ .indat_endpoint = -1,
+ .glocont_endpoint = 0x01,
+ .calculate_baud_rate = keyspan_usa19w_calc_baud,
+ .baudclk = KEYSPAN_USA28X_BAUDCLK,
+};
+/*
+ * We don't need a separate entry for the usa28xb as it appears as a 28x
+ * anyway.
+ */
+
+static const struct keyspan_device_details usa49w_device_details = {
+ .product_id = keyspan_usa49w_product_id,
+ .msg_format = msg_usa49,
+ .num_ports = 4,
+ .indat_endp_flip = 0,
+ .outdat_endp_flip = 0,
+ .indat_endpoints = {0x81, 0x82, 0x83, 0x84},
+ .outdat_endpoints = {0x01, 0x02, 0x03, 0x04},
+ .inack_endpoints = {-1, -1, -1, -1},
+ .outcont_endpoints = {-1, -1, -1, -1},
+ .instat_endpoint = 0x87,
+ .indat_endpoint = -1,
+ .glocont_endpoint = 0x07,
+ .calculate_baud_rate = keyspan_usa19w_calc_baud,
+ .baudclk = KEYSPAN_USA49W_BAUDCLK,
+};
+
+static const struct keyspan_device_details usa49wlc_device_details = {
+ .product_id = keyspan_usa49wlc_product_id,
+ .msg_format = msg_usa49,
+ .num_ports = 4,
+ .indat_endp_flip = 0,
+ .outdat_endp_flip = 0,
+ .indat_endpoints = {0x81, 0x82, 0x83, 0x84},
+ .outdat_endpoints = {0x01, 0x02, 0x03, 0x04},
+ .inack_endpoints = {-1, -1, -1, -1},
+ .outcont_endpoints = {-1, -1, -1, -1},
+ .instat_endpoint = 0x87,
+ .indat_endpoint = -1,
+ .glocont_endpoint = 0x07,
+ .calculate_baud_rate = keyspan_usa19w_calc_baud,
+ .baudclk = KEYSPAN_USA19W_BAUDCLK,
+};
+
+static const struct keyspan_device_details usa49wg_device_details = {
+ .product_id = keyspan_usa49wg_product_id,
+ .msg_format = msg_usa49,
+ .num_ports = 4,
+ .indat_endp_flip = 0,
+ .outdat_endp_flip = 0,
+ .indat_endpoints = {-1, -1, -1, -1}, /* single 'global' data in EP */
+ .outdat_endpoints = {0x01, 0x02, 0x04, 0x06},
+ .inack_endpoints = {-1, -1, -1, -1},
+ .outcont_endpoints = {-1, -1, -1, -1},
+ .instat_endpoint = 0x81,
+ .indat_endpoint = 0x88,
+ .glocont_endpoint = 0x00, /* uses control EP */
+ .calculate_baud_rate = keyspan_usa19w_calc_baud,
+ .baudclk = KEYSPAN_USA19W_BAUDCLK,
+};
+
+static const struct keyspan_device_details *keyspan_devices[] = {
+ &usa18x_device_details,
+ &usa19_device_details,
+ &usa19qi_device_details,
+ &mpr_device_details,
+ &usa19qw_device_details,
+ &usa19w_device_details,
+ &usa19hs_device_details,
+ &usa28_device_details,
+ &usa28x_device_details,
+ &usa28xa_device_details,
+ &usa28xg_device_details,
+ /* 28xb not required as it renumerates as a 28x */
+ &usa49w_device_details,
+ &usa49wlc_device_details,
+ &usa49wg_device_details,
+ NULL,
+};
+
+static const struct usb_device_id keyspan_ids_combined[] = {
+ { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa18x_pre_product_id) },
+ { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19_pre_product_id) },
+ { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19w_pre_product_id) },
+ { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19qi_pre_product_id) },
+ { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19qw_pre_product_id) },
+ { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_mpr_pre_product_id) },
+ { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28_pre_product_id) },
+ { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28x_pre_product_id) },
+ { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xa_pre_product_id) },
+ { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xb_pre_product_id) },
+ { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49w_pre_product_id) },
+ { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49wlc_pre_product_id) },
+ { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa18x_product_id) },
+ { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19_product_id) },
+ { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19w_product_id) },
+ { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19qi_product_id) },
+ { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19qw_product_id) },
+ { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19hs_product_id) },
+ { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_mpr_product_id) },
+ { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28_product_id) },
+ { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28x_product_id) },
+ { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xa_product_id) },
+ { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xg_product_id) },
+ { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49w_product_id)},
+ { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49wlc_product_id)},
+ { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49wg_product_id)},
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, keyspan_ids_combined);
+
+/* usb_device_id table for the pre-firmware download keyspan devices */
+static const struct usb_device_id keyspan_pre_ids[] = {
+ { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa18x_pre_product_id) },
+ { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19_pre_product_id) },
+ { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19qi_pre_product_id) },
+ { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19qw_pre_product_id) },
+ { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19w_pre_product_id) },
+ { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_mpr_pre_product_id) },
+ { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28_pre_product_id) },
+ { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28x_pre_product_id) },
+ { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xa_pre_product_id) },
+ { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xb_pre_product_id) },
+ { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49w_pre_product_id) },
+ { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49wlc_pre_product_id) },
+ { } /* Terminating entry */
+};
+
+static const struct usb_device_id keyspan_1port_ids[] = {
+ { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa18x_product_id) },
+ { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19_product_id) },
+ { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19qi_product_id) },
+ { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19qw_product_id) },
+ { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19w_product_id) },
+ { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19hs_product_id) },
+ { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_mpr_product_id) },
+ { } /* Terminating entry */
+};
+
+static const struct usb_device_id keyspan_2port_ids[] = {
+ { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28_product_id) },
+ { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28x_product_id) },
+ { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xa_product_id) },
+ { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xg_product_id) },
+ { } /* Terminating entry */
+};
+
+static const struct usb_device_id keyspan_4port_ids[] = {
+ { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49w_product_id) },
+ { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49wlc_product_id)},
+ { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49wg_product_id)},
+ { } /* Terminating entry */
+};
+
#define INSTAT_BUFLEN 32
#define GLOCONT_BUFLEN 64
#define INDAT49W_BUFLEN 512
@@ -126,8 +623,6 @@ struct keyspan_port_private {
#include "keyspan_usa67msg.h"
-module_usb_serial_driver(serial_drivers, keyspan_ids_combined);
-
static void keyspan_break_ctl(struct tty_struct *tty, int break_state)
{
struct usb_serial_port *port = tty->driver_data;
@@ -2523,6 +3018,97 @@ static int keyspan_port_remove(struct usb_serial_port *port)
return 0;
}
+/* Structs for the devices, pre and post renumeration. */
+static struct usb_serial_driver keyspan_pre_device = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "keyspan_no_firm",
+ },
+ .description = "Keyspan - (without firmware)",
+ .id_table = keyspan_pre_ids,
+ .num_ports = 1,
+ .attach = keyspan_fake_startup,
+};
+
+static struct usb_serial_driver keyspan_1port_device = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "keyspan_1",
+ },
+ .description = "Keyspan 1 port adapter",
+ .id_table = keyspan_1port_ids,
+ .num_ports = 1,
+ .open = keyspan_open,
+ .close = keyspan_close,
+ .dtr_rts = keyspan_dtr_rts,
+ .write = keyspan_write,
+ .write_room = keyspan_write_room,
+ .set_termios = keyspan_set_termios,
+ .break_ctl = keyspan_break_ctl,
+ .tiocmget = keyspan_tiocmget,
+ .tiocmset = keyspan_tiocmset,
+ .attach = keyspan_startup,
+ .disconnect = keyspan_disconnect,
+ .release = keyspan_release,
+ .port_probe = keyspan_port_probe,
+ .port_remove = keyspan_port_remove,
+};
+
+static struct usb_serial_driver keyspan_2port_device = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "keyspan_2",
+ },
+ .description = "Keyspan 2 port adapter",
+ .id_table = keyspan_2port_ids,
+ .num_ports = 2,
+ .open = keyspan_open,
+ .close = keyspan_close,
+ .dtr_rts = keyspan_dtr_rts,
+ .write = keyspan_write,
+ .write_room = keyspan_write_room,
+ .set_termios = keyspan_set_termios,
+ .break_ctl = keyspan_break_ctl,
+ .tiocmget = keyspan_tiocmget,
+ .tiocmset = keyspan_tiocmset,
+ .attach = keyspan_startup,
+ .disconnect = keyspan_disconnect,
+ .release = keyspan_release,
+ .port_probe = keyspan_port_probe,
+ .port_remove = keyspan_port_remove,
+};
+
+static struct usb_serial_driver keyspan_4port_device = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "keyspan_4",
+ },
+ .description = "Keyspan 4 port adapter",
+ .id_table = keyspan_4port_ids,
+ .num_ports = 4,
+ .open = keyspan_open,
+ .close = keyspan_close,
+ .dtr_rts = keyspan_dtr_rts,
+ .write = keyspan_write,
+ .write_room = keyspan_write_room,
+ .set_termios = keyspan_set_termios,
+ .break_ctl = keyspan_break_ctl,
+ .tiocmget = keyspan_tiocmget,
+ .tiocmset = keyspan_tiocmset,
+ .attach = keyspan_startup,
+ .disconnect = keyspan_disconnect,
+ .release = keyspan_release,
+ .port_probe = keyspan_port_probe,
+ .port_remove = keyspan_port_remove,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+ &keyspan_pre_device, &keyspan_1port_device,
+ &keyspan_2port_device, &keyspan_4port_device, NULL
+};
+
+module_usb_serial_driver(serial_drivers, keyspan_ids_combined);
+
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
diff --git a/drivers/usb/serial/keyspan.h b/drivers/usb/serial/keyspan.h
deleted file mode 100644
index 0273dda303a4..000000000000
--- a/drivers/usb/serial/keyspan.h
+++ /dev/null
@@ -1,629 +0,0 @@
-/*
- Keyspan USB to Serial Converter driver
-
- (C) Copyright (C) 2000-2001
- Hugh Blemings <hugh@blemings.org>
-
- 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.
-
- See http://blemings.org/hugh/keyspan.html for more information.
-
- Code in this driver inspired by and in a number of places taken
- from Brian Warner's original Keyspan-PDA driver.
-
- This driver has been put together with the support of Innosys, Inc.
- and Keyspan, Inc the manufacturers of the Keyspan USB-serial products.
- Thanks Guys :)
-
- Thanks to Paulus for miscellaneous tidy ups, some largish chunks
- of much nicer and/or completely new code and (perhaps most uniquely)
- having the patience to sit down and explain why and where he'd changed
- stuff.
-
- Tip 'o the hat to IBM (and previously Linuxcare :) for supporting
- staff in their work on open source projects.
-
- See keyspan.c for update history.
-
-*/
-
-#ifndef __LINUX_USB_SERIAL_KEYSPAN_H
-#define __LINUX_USB_SERIAL_KEYSPAN_H
-
-
-/* Function prototypes for Keyspan serial converter */
-static int keyspan_open (struct tty_struct *tty,
- struct usb_serial_port *port);
-static void keyspan_close (struct usb_serial_port *port);
-static void keyspan_dtr_rts (struct usb_serial_port *port, int on);
-static int keyspan_startup (struct usb_serial *serial);
-static void keyspan_disconnect (struct usb_serial *serial);
-static void keyspan_release (struct usb_serial *serial);
-static int keyspan_port_probe(struct usb_serial_port *port);
-static int keyspan_port_remove(struct usb_serial_port *port);
-static int keyspan_write_room (struct tty_struct *tty);
-
-static int keyspan_write (struct tty_struct *tty,
- struct usb_serial_port *port,
- const unsigned char *buf,
- int count);
-
-static void keyspan_send_setup (struct usb_serial_port *port,
- int reset_port);
-
-
-static void keyspan_set_termios (struct tty_struct *tty,
- struct usb_serial_port *port,
- struct ktermios *old);
-static void keyspan_break_ctl (struct tty_struct *tty,
- int break_state);
-static int keyspan_tiocmget (struct tty_struct *tty);
-static int keyspan_tiocmset (struct tty_struct *tty,
- unsigned int set,
- unsigned int clear);
-static int keyspan_fake_startup (struct usb_serial *serial);
-
-static int keyspan_usa19_calc_baud (struct usb_serial_port *port,
- u32 baud_rate, u32 baudclk,
- u8 *rate_hi, u8 *rate_low,
- u8 *prescaler, int portnum);
-
-static int keyspan_usa19w_calc_baud (struct usb_serial_port *port,
- u32 baud_rate, u32 baudclk,
- u8 *rate_hi, u8 *rate_low,
- u8 *prescaler, int portnum);
-
-static int keyspan_usa28_calc_baud (struct usb_serial_port *port,
- u32 baud_rate, u32 baudclk,
- u8 *rate_hi, u8 *rate_low,
- u8 *prescaler, int portnum);
-
-static int keyspan_usa19hs_calc_baud (struct usb_serial_port *port,
- u32 baud_rate, u32 baudclk,
- u8 *rate_hi, u8 *rate_low,
- u8 *prescaler, int portnum);
-
-static int keyspan_usa28_send_setup (struct usb_serial *serial,
- struct usb_serial_port *port,
- int reset_port);
-static int keyspan_usa26_send_setup (struct usb_serial *serial,
- struct usb_serial_port *port,
- int reset_port);
-static int keyspan_usa49_send_setup (struct usb_serial *serial,
- struct usb_serial_port *port,
- int reset_port);
-
-static int keyspan_usa90_send_setup (struct usb_serial *serial,
- struct usb_serial_port *port,
- int reset_port);
-
-static int keyspan_usa67_send_setup (struct usb_serial *serial,
- struct usb_serial_port *port,
- int reset_port);
-
-/* Values used for baud rate calculation - device specific */
-#define KEYSPAN_INVALID_BAUD_RATE (-1)
-#define KEYSPAN_BAUD_RATE_OK (0)
-#define KEYSPAN_USA18X_BAUDCLK (12000000L) /* a guess */
-#define KEYSPAN_USA19_BAUDCLK (12000000L)
-#define KEYSPAN_USA19W_BAUDCLK (24000000L)
-#define KEYSPAN_USA19HS_BAUDCLK (14769231L)
-#define KEYSPAN_USA28_BAUDCLK (1843200L)
-#define KEYSPAN_USA28X_BAUDCLK (12000000L)
-#define KEYSPAN_USA49W_BAUDCLK (48000000L)
-
-/* Some constants used to characterise each device. */
-#define KEYSPAN_MAX_NUM_PORTS (4)
-#define KEYSPAN_MAX_FLIPS (2)
-
-/* Device info for the Keyspan serial converter, used
- by the overall usb-serial probe function */
-#define KEYSPAN_VENDOR_ID (0x06cd)
-
-/* Product IDs for the products supported, pre-renumeration */
-#define keyspan_usa18x_pre_product_id 0x0105
-#define keyspan_usa19_pre_product_id 0x0103
-#define keyspan_usa19qi_pre_product_id 0x010b
-#define keyspan_mpr_pre_product_id 0x011b
-#define keyspan_usa19qw_pre_product_id 0x0118
-#define keyspan_usa19w_pre_product_id 0x0106
-#define keyspan_usa28_pre_product_id 0x0101
-#define keyspan_usa28x_pre_product_id 0x0102
-#define keyspan_usa28xa_pre_product_id 0x0114
-#define keyspan_usa28xb_pre_product_id 0x0113
-#define keyspan_usa49w_pre_product_id 0x0109
-#define keyspan_usa49wlc_pre_product_id 0x011a
-
-/* Product IDs post-renumeration. Note that the 28x and 28xb
- have the same id's post-renumeration but behave identically
- so it's not an issue. As such, the 28xb is not listed in any
- of the device tables. */
-#define keyspan_usa18x_product_id 0x0112
-#define keyspan_usa19_product_id 0x0107
-#define keyspan_usa19qi_product_id 0x010c
-#define keyspan_usa19hs_product_id 0x0121
-#define keyspan_mpr_product_id 0x011c
-#define keyspan_usa19qw_product_id 0x0119
-#define keyspan_usa19w_product_id 0x0108
-#define keyspan_usa28_product_id 0x010f
-#define keyspan_usa28x_product_id 0x0110
-#define keyspan_usa28xa_product_id 0x0115
-#define keyspan_usa28xb_product_id 0x0110
-#define keyspan_usa28xg_product_id 0x0135
-#define keyspan_usa49w_product_id 0x010a
-#define keyspan_usa49wlc_product_id 0x012a
-#define keyspan_usa49wg_product_id 0x0131
-
-struct keyspan_device_details {
- /* product ID value */
- int product_id;
-
- enum {msg_usa26, msg_usa28, msg_usa49, msg_usa90, msg_usa67} msg_format;
-
- /* Number of physical ports */
- int num_ports;
-
- /* 1 if endpoint flipping used on input, 0 if not */
- int indat_endp_flip;
-
- /* 1 if endpoint flipping used on output, 0 if not */
- int outdat_endp_flip;
-
- /* Table mapping input data endpoint IDs to physical
- port number and flip if used */
- int indat_endpoints[KEYSPAN_MAX_NUM_PORTS];
-
- /* Same for output endpoints */
- int outdat_endpoints[KEYSPAN_MAX_NUM_PORTS];
-
- /* Input acknowledge endpoints */
- int inack_endpoints[KEYSPAN_MAX_NUM_PORTS];
-
- /* Output control endpoints */
- int outcont_endpoints[KEYSPAN_MAX_NUM_PORTS];
-
- /* Endpoint used for input status */
- int instat_endpoint;
-
- /* Endpoint used for input data 49WG only */
- int indat_endpoint;
-
- /* Endpoint used for global control functions */
- int glocont_endpoint;
-
- int (*calculate_baud_rate) (struct usb_serial_port *port,
- u32 baud_rate, u32 baudclk,
- u8 *rate_hi, u8 *rate_low, u8 *prescaler, int portnum);
- u32 baudclk;
-};
-
-/* Now for each device type we setup the device detail
- structure with the appropriate information (provided
- in Keyspan's documentation) */
-
-static const struct keyspan_device_details usa18x_device_details = {
- .product_id = keyspan_usa18x_product_id,
- .msg_format = msg_usa26,
- .num_ports = 1,
- .indat_endp_flip = 0,
- .outdat_endp_flip = 1,
- .indat_endpoints = {0x81},
- .outdat_endpoints = {0x01},
- .inack_endpoints = {0x85},
- .outcont_endpoints = {0x05},
- .instat_endpoint = 0x87,
- .indat_endpoint = -1,
- .glocont_endpoint = 0x07,
- .calculate_baud_rate = keyspan_usa19w_calc_baud,
- .baudclk = KEYSPAN_USA18X_BAUDCLK,
-};
-
-static const struct keyspan_device_details usa19_device_details = {
- .product_id = keyspan_usa19_product_id,
- .msg_format = msg_usa28,
- .num_ports = 1,
- .indat_endp_flip = 1,
- .outdat_endp_flip = 1,
- .indat_endpoints = {0x81},
- .outdat_endpoints = {0x01},
- .inack_endpoints = {0x83},
- .outcont_endpoints = {0x03},
- .instat_endpoint = 0x84,
- .indat_endpoint = -1,
- .glocont_endpoint = -1,
- .calculate_baud_rate = keyspan_usa19_calc_baud,
- .baudclk = KEYSPAN_USA19_BAUDCLK,
-};
-
-static const struct keyspan_device_details usa19qi_device_details = {
- .product_id = keyspan_usa19qi_product_id,
- .msg_format = msg_usa28,
- .num_ports = 1,
- .indat_endp_flip = 1,
- .outdat_endp_flip = 1,
- .indat_endpoints = {0x81},
- .outdat_endpoints = {0x01},
- .inack_endpoints = {0x83},
- .outcont_endpoints = {0x03},
- .instat_endpoint = 0x84,
- .indat_endpoint = -1,
- .glocont_endpoint = -1,
- .calculate_baud_rate = keyspan_usa28_calc_baud,
- .baudclk = KEYSPAN_USA19_BAUDCLK,
-};
-
-static const struct keyspan_device_details mpr_device_details = {
- .product_id = keyspan_mpr_product_id,
- .msg_format = msg_usa28,
- .num_ports = 1,
- .indat_endp_flip = 1,
- .outdat_endp_flip = 1,
- .indat_endpoints = {0x81},
- .outdat_endpoints = {0x01},
- .inack_endpoints = {0x83},
- .outcont_endpoints = {0x03},
- .instat_endpoint = 0x84,
- .indat_endpoint = -1,
- .glocont_endpoint = -1,
- .calculate_baud_rate = keyspan_usa28_calc_baud,
- .baudclk = KEYSPAN_USA19_BAUDCLK,
-};
-
-static const struct keyspan_device_details usa19qw_device_details = {
- .product_id = keyspan_usa19qw_product_id,
- .msg_format = msg_usa26,
- .num_ports = 1,
- .indat_endp_flip = 0,
- .outdat_endp_flip = 1,
- .indat_endpoints = {0x81},
- .outdat_endpoints = {0x01},
- .inack_endpoints = {0x85},
- .outcont_endpoints = {0x05},
- .instat_endpoint = 0x87,
- .indat_endpoint = -1,
- .glocont_endpoint = 0x07,
- .calculate_baud_rate = keyspan_usa19w_calc_baud,
- .baudclk = KEYSPAN_USA19W_BAUDCLK,
-};
-
-static const struct keyspan_device_details usa19w_device_details = {
- .product_id = keyspan_usa19w_product_id,
- .msg_format = msg_usa26,
- .num_ports = 1,
- .indat_endp_flip = 0,
- .outdat_endp_flip = 1,
- .indat_endpoints = {0x81},
- .outdat_endpoints = {0x01},
- .inack_endpoints = {0x85},
- .outcont_endpoints = {0x05},
- .instat_endpoint = 0x87,
- .indat_endpoint = -1,
- .glocont_endpoint = 0x07,
- .calculate_baud_rate = keyspan_usa19w_calc_baud,
- .baudclk = KEYSPAN_USA19W_BAUDCLK,
-};
-
-static const struct keyspan_device_details usa19hs_device_details = {
- .product_id = keyspan_usa19hs_product_id,
- .msg_format = msg_usa90,
- .num_ports = 1,
- .indat_endp_flip = 0,
- .outdat_endp_flip = 0,
- .indat_endpoints = {0x81},
- .outdat_endpoints = {0x01},
- .inack_endpoints = {-1},
- .outcont_endpoints = {0x02},
- .instat_endpoint = 0x82,
- .indat_endpoint = -1,
- .glocont_endpoint = -1,
- .calculate_baud_rate = keyspan_usa19hs_calc_baud,
- .baudclk = KEYSPAN_USA19HS_BAUDCLK,
-};
-
-static const struct keyspan_device_details usa28_device_details = {
- .product_id = keyspan_usa28_product_id,
- .msg_format = msg_usa28,
- .num_ports = 2,
- .indat_endp_flip = 1,
- .outdat_endp_flip = 1,
- .indat_endpoints = {0x81, 0x83},
- .outdat_endpoints = {0x01, 0x03},
- .inack_endpoints = {0x85, 0x86},
- .outcont_endpoints = {0x05, 0x06},
- .instat_endpoint = 0x87,
- .indat_endpoint = -1,
- .glocont_endpoint = 0x07,
- .calculate_baud_rate = keyspan_usa28_calc_baud,
- .baudclk = KEYSPAN_USA28_BAUDCLK,
-};
-
-static const struct keyspan_device_details usa28x_device_details = {
- .product_id = keyspan_usa28x_product_id,
- .msg_format = msg_usa26,
- .num_ports = 2,
- .indat_endp_flip = 0,
- .outdat_endp_flip = 1,
- .indat_endpoints = {0x81, 0x83},
- .outdat_endpoints = {0x01, 0x03},
- .inack_endpoints = {0x85, 0x86},
- .outcont_endpoints = {0x05, 0x06},
- .instat_endpoint = 0x87,
- .indat_endpoint = -1,
- .glocont_endpoint = 0x07,
- .calculate_baud_rate = keyspan_usa19w_calc_baud,
- .baudclk = KEYSPAN_USA28X_BAUDCLK,
-};
-
-static const struct keyspan_device_details usa28xa_device_details = {
- .product_id = keyspan_usa28xa_product_id,
- .msg_format = msg_usa26,
- .num_ports = 2,
- .indat_endp_flip = 0,
- .outdat_endp_flip = 1,
- .indat_endpoints = {0x81, 0x83},
- .outdat_endpoints = {0x01, 0x03},
- .inack_endpoints = {0x85, 0x86},
- .outcont_endpoints = {0x05, 0x06},
- .instat_endpoint = 0x87,
- .indat_endpoint = -1,
- .glocont_endpoint = 0x07,
- .calculate_baud_rate = keyspan_usa19w_calc_baud,
- .baudclk = KEYSPAN_USA28X_BAUDCLK,
-};
-
-static const struct keyspan_device_details usa28xg_device_details = {
- .product_id = keyspan_usa28xg_product_id,
- .msg_format = msg_usa67,
- .num_ports = 2,
- .indat_endp_flip = 0,
- .outdat_endp_flip = 0,
- .indat_endpoints = {0x84, 0x88},
- .outdat_endpoints = {0x02, 0x06},
- .inack_endpoints = {-1, -1},
- .outcont_endpoints = {-1, -1},
- .instat_endpoint = 0x81,
- .indat_endpoint = -1,
- .glocont_endpoint = 0x01,
- .calculate_baud_rate = keyspan_usa19w_calc_baud,
- .baudclk = KEYSPAN_USA28X_BAUDCLK,
-};
-/* We don't need a separate entry for the usa28xb as it appears as a 28x anyway */
-
-static const struct keyspan_device_details usa49w_device_details = {
- .product_id = keyspan_usa49w_product_id,
- .msg_format = msg_usa49,
- .num_ports = 4,
- .indat_endp_flip = 0,
- .outdat_endp_flip = 0,
- .indat_endpoints = {0x81, 0x82, 0x83, 0x84},
- .outdat_endpoints = {0x01, 0x02, 0x03, 0x04},
- .inack_endpoints = {-1, -1, -1, -1},
- .outcont_endpoints = {-1, -1, -1, -1},
- .instat_endpoint = 0x87,
- .indat_endpoint = -1,
- .glocont_endpoint = 0x07,
- .calculate_baud_rate = keyspan_usa19w_calc_baud,
- .baudclk = KEYSPAN_USA49W_BAUDCLK,
-};
-
-static const struct keyspan_device_details usa49wlc_device_details = {
- .product_id = keyspan_usa49wlc_product_id,
- .msg_format = msg_usa49,
- .num_ports = 4,
- .indat_endp_flip = 0,
- .outdat_endp_flip = 0,
- .indat_endpoints = {0x81, 0x82, 0x83, 0x84},
- .outdat_endpoints = {0x01, 0x02, 0x03, 0x04},
- .inack_endpoints = {-1, -1, -1, -1},
- .outcont_endpoints = {-1, -1, -1, -1},
- .instat_endpoint = 0x87,
- .indat_endpoint = -1,
- .glocont_endpoint = 0x07,
- .calculate_baud_rate = keyspan_usa19w_calc_baud,
- .baudclk = KEYSPAN_USA19W_BAUDCLK,
-};
-
-static const struct keyspan_device_details usa49wg_device_details = {
- .product_id = keyspan_usa49wg_product_id,
- .msg_format = msg_usa49,
- .num_ports = 4,
- .indat_endp_flip = 0,
- .outdat_endp_flip = 0,
- .indat_endpoints = {-1, -1, -1, -1}, /* single 'global' data in EP */
- .outdat_endpoints = {0x01, 0x02, 0x04, 0x06},
- .inack_endpoints = {-1, -1, -1, -1},
- .outcont_endpoints = {-1, -1, -1, -1},
- .instat_endpoint = 0x81,
- .indat_endpoint = 0x88,
- .glocont_endpoint = 0x00, /* uses control EP */
- .calculate_baud_rate = keyspan_usa19w_calc_baud,
- .baudclk = KEYSPAN_USA19W_BAUDCLK,
-};
-
-static const struct keyspan_device_details *keyspan_devices[] = {
- &usa18x_device_details,
- &usa19_device_details,
- &usa19qi_device_details,
- &mpr_device_details,
- &usa19qw_device_details,
- &usa19w_device_details,
- &usa19hs_device_details,
- &usa28_device_details,
- &usa28x_device_details,
- &usa28xa_device_details,
- &usa28xg_device_details,
- /* 28xb not required as it renumerates as a 28x */
- &usa49w_device_details,
- &usa49wlc_device_details,
- &usa49wg_device_details,
- NULL,
-};
-
-static const struct usb_device_id keyspan_ids_combined[] = {
- { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa18x_pre_product_id) },
- { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19_pre_product_id) },
- { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19w_pre_product_id) },
- { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19qi_pre_product_id) },
- { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19qw_pre_product_id) },
- { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_mpr_pre_product_id) },
- { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28_pre_product_id) },
- { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28x_pre_product_id) },
- { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xa_pre_product_id) },
- { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xb_pre_product_id) },
- { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49w_pre_product_id) },
- { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49wlc_pre_product_id) },
- { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa18x_product_id) },
- { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19_product_id) },
- { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19w_product_id) },
- { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19qi_product_id) },
- { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19qw_product_id) },
- { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19hs_product_id) },
- { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_mpr_product_id) },
- { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28_product_id) },
- { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28x_product_id) },
- { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xa_product_id) },
- { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xg_product_id) },
- { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49w_product_id)},
- { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49wlc_product_id)},
- { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49wg_product_id)},
- { } /* Terminating entry */
-};
-
-MODULE_DEVICE_TABLE(usb, keyspan_ids_combined);
-
-/* usb_device_id table for the pre-firmware download keyspan devices */
-static const struct usb_device_id keyspan_pre_ids[] = {
- { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa18x_pre_product_id) },
- { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19_pre_product_id) },
- { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19qi_pre_product_id) },
- { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19qw_pre_product_id) },
- { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19w_pre_product_id) },
- { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_mpr_pre_product_id) },
- { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28_pre_product_id) },
- { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28x_pre_product_id) },
- { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xa_pre_product_id) },
- { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xb_pre_product_id) },
- { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49w_pre_product_id) },
- { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49wlc_pre_product_id) },
- { } /* Terminating entry */
-};
-
-static const struct usb_device_id keyspan_1port_ids[] = {
- { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa18x_product_id) },
- { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19_product_id) },
- { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19qi_product_id) },
- { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19qw_product_id) },
- { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19w_product_id) },
- { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19hs_product_id) },
- { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_mpr_product_id) },
- { } /* Terminating entry */
-};
-
-static const struct usb_device_id keyspan_2port_ids[] = {
- { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28_product_id) },
- { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28x_product_id) },
- { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xa_product_id) },
- { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xg_product_id) },
- { } /* Terminating entry */
-};
-
-static const struct usb_device_id keyspan_4port_ids[] = {
- { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49w_product_id) },
- { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49wlc_product_id)},
- { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49wg_product_id)},
- { } /* Terminating entry */
-};
-
-/* Structs for the devices, pre and post renumeration. */
-static struct usb_serial_driver keyspan_pre_device = {
- .driver = {
- .owner = THIS_MODULE,
- .name = "keyspan_no_firm",
- },
- .description = "Keyspan - (without firmware)",
- .id_table = keyspan_pre_ids,
- .num_ports = 1,
- .attach = keyspan_fake_startup,
-};
-
-static struct usb_serial_driver keyspan_1port_device = {
- .driver = {
- .owner = THIS_MODULE,
- .name = "keyspan_1",
- },
- .description = "Keyspan 1 port adapter",
- .id_table = keyspan_1port_ids,
- .num_ports = 1,
- .open = keyspan_open,
- .close = keyspan_close,
- .dtr_rts = keyspan_dtr_rts,
- .write = keyspan_write,
- .write_room = keyspan_write_room,
- .set_termios = keyspan_set_termios,
- .break_ctl = keyspan_break_ctl,
- .tiocmget = keyspan_tiocmget,
- .tiocmset = keyspan_tiocmset,
- .attach = keyspan_startup,
- .disconnect = keyspan_disconnect,
- .release = keyspan_release,
- .port_probe = keyspan_port_probe,
- .port_remove = keyspan_port_remove,
-};
-
-static struct usb_serial_driver keyspan_2port_device = {
- .driver = {
- .owner = THIS_MODULE,
- .name = "keyspan_2",
- },
- .description = "Keyspan 2 port adapter",
- .id_table = keyspan_2port_ids,
- .num_ports = 2,
- .open = keyspan_open,
- .close = keyspan_close,
- .dtr_rts = keyspan_dtr_rts,
- .write = keyspan_write,
- .write_room = keyspan_write_room,
- .set_termios = keyspan_set_termios,
- .break_ctl = keyspan_break_ctl,
- .tiocmget = keyspan_tiocmget,
- .tiocmset = keyspan_tiocmset,
- .attach = keyspan_startup,
- .disconnect = keyspan_disconnect,
- .release = keyspan_release,
- .port_probe = keyspan_port_probe,
- .port_remove = keyspan_port_remove,
-};
-
-static struct usb_serial_driver keyspan_4port_device = {
- .driver = {
- .owner = THIS_MODULE,
- .name = "keyspan_4",
- },
- .description = "Keyspan 4 port adapter",
- .id_table = keyspan_4port_ids,
- .num_ports = 4,
- .open = keyspan_open,
- .close = keyspan_close,
- .dtr_rts = keyspan_dtr_rts,
- .write = keyspan_write,
- .write_room = keyspan_write_room,
- .set_termios = keyspan_set_termios,
- .break_ctl = keyspan_break_ctl,
- .tiocmget = keyspan_tiocmget,
- .tiocmset = keyspan_tiocmset,
- .attach = keyspan_startup,
- .disconnect = keyspan_disconnect,
- .release = keyspan_release,
- .port_probe = keyspan_port_probe,
- .port_remove = keyspan_port_remove,
-};
-
-static struct usb_serial_driver * const serial_drivers[] = {
- &keyspan_pre_device, &keyspan_1port_device,
- &keyspan_2port_device, &keyspan_4port_device, NULL
-};
-
-#endif
diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c
index e49ad0c63ad8..d2dab2a341b8 100644
--- a/drivers/usb/serial/keyspan_pda.c
+++ b/drivers/usb/serial/keyspan_pda.c
@@ -139,6 +139,7 @@ static void keyspan_pda_rx_interrupt(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
unsigned char *data = urb->transfer_buffer;
+ unsigned int len = urb->actual_length;
int retval;
int status = urb->status;
struct keyspan_pda_private *priv;
@@ -159,18 +160,26 @@ static void keyspan_pda_rx_interrupt(struct urb *urb)
goto exit;
}
+ if (len < 1) {
+ dev_warn(&port->dev, "short message received\n");
+ goto exit;
+ }
+
/* see if the message is data or a status interrupt */
switch (data[0]) {
case 0:
/* rest of message is rx data */
- if (urb->actual_length) {
- tty_insert_flip_string(&port->port, data + 1,
- urb->actual_length - 1);
- tty_flip_buffer_push(&port->port);
- }
+ if (len < 2)
+ break;
+ tty_insert_flip_string(&port->port, data + 1, len - 1);
+ tty_flip_buffer_push(&port->port);
break;
case 1:
/* status interrupt */
+ if (len < 3) {
+ dev_warn(&port->dev, "short interrupt message received\n");
+ break;
+ }
dev_dbg(&port->dev, "rx int, d1=%d, d2=%d\n", data[1], data[2]);
switch (data[1]) {
case 1: /* modemline change */
@@ -699,6 +708,19 @@ MODULE_FIRMWARE("keyspan_pda/keyspan_pda.fw");
MODULE_FIRMWARE("keyspan_pda/xircom_pgs.fw");
#endif
+static int keyspan_pda_attach(struct usb_serial *serial)
+{
+ unsigned char num_ports = serial->num_ports;
+
+ if (serial->num_bulk_out < num_ports ||
+ serial->num_interrupt_in < num_ports) {
+ dev_err(&serial->interface->dev, "missing endpoints\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
static int keyspan_pda_port_probe(struct usb_serial_port *port)
{
@@ -776,6 +798,7 @@ static struct usb_serial_driver keyspan_pda_device = {
.break_ctl = keyspan_pda_break_ctl,
.tiocmget = keyspan_pda_tiocmget,
.tiocmset = keyspan_pda_tiocmset,
+ .attach = keyspan_pda_attach,
.port_probe = keyspan_pda_port_probe,
.port_remove = keyspan_pda_port_remove,
};
diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c
index fc5d3a791e08..595415e59d5d 100644
--- a/drivers/usb/serial/kl5kusb105.c
+++ b/drivers/usb/serial/kl5kusb105.c
@@ -89,7 +89,6 @@ static struct usb_serial_driver kl5kusb105d_device = {
.open = klsi_105_open,
.close = klsi_105_close,
.set_termios = klsi_105_set_termios,
- /*.break_ctl = klsi_105_break_ctl,*/
.tiocmget = klsi_105_tiocmget,
.port_probe = klsi_105_port_probe,
.port_remove = klsi_105_port_remove,
@@ -104,16 +103,15 @@ static struct usb_serial_driver * const serial_drivers[] = {
};
struct klsi_105_port_settings {
- __u8 pktlen; /* always 5, it seems */
- __u8 baudrate;
- __u8 databits;
- __u8 unknown1;
- __u8 unknown2;
-} __attribute__ ((packed));
+ u8 pktlen; /* always 5, it seems */
+ u8 baudrate;
+ u8 databits;
+ u8 unknown1;
+ u8 unknown2;
+};
struct klsi_105_private {
struct klsi_105_port_settings cfg;
- struct ktermios termios;
unsigned long line_state; /* modem line settings */
spinlock_t lock;
};
@@ -143,10 +141,12 @@ static int klsi_105_chg_port_settings(struct usb_serial_port *port,
if (rc < 0)
dev_err(&port->dev,
"Change port settings failed (error = %d)\n", rc);
- dev_info(&port->serial->dev->dev,
- "%d byte block, baudrate %x, databits %d, u1 %d, u2 %d\n",
- settings->pktlen, settings->baudrate, settings->databits,
- settings->unknown1, settings->unknown2);
+
+ dev_dbg(&port->dev,
+ "pktlen %u, baudrate 0x%02x, databits %u, u1 %u, u2 %u\n",
+ settings->pktlen, settings->baudrate, settings->databits,
+ settings->unknown1, settings->unknown2);
+
return rc;
}
@@ -175,8 +175,6 @@ static int klsi_105_get_line_state(struct usb_serial_port *port,
u8 *status_buf;
__u16 status;
- dev_info(&port->serial->dev->dev, "sending SIO Poll request\n");
-
status_buf = kmalloc(KLSI_STATUSBUF_LEN, GFP_KERNEL);
if (!status_buf)
return -ENOMEM;
@@ -192,14 +190,15 @@ static int klsi_105_get_line_state(struct usb_serial_port *port,
status_buf, KLSI_STATUSBUF_LEN,
10000
);
- if (rc < 0)
- dev_err(&port->dev, "Reading line status failed (error = %d)\n",
- rc);
- else {
+ if (rc != KLSI_STATUSBUF_LEN) {
+ dev_err(&port->dev, "reading line status failed: %d\n", rc);
+ if (rc >= 0)
+ rc = -EIO;
+ } else {
status = get_unaligned_le16(status_buf);
- dev_info(&port->serial->dev->dev, "read status %x %x\n",
- status_buf[0], status_buf[1]);
+ dev_dbg(&port->dev, "read status %02x %02x\n",
+ status_buf[0], status_buf[1]);
*line_state_p = klsi_105_status2linestate(status);
}
@@ -232,8 +231,6 @@ static int klsi_105_port_probe(struct usb_serial_port *port)
spin_lock_init(&priv->lock);
- /* priv->termios is left uninitialized until port opening */
-
usb_set_serial_port_data(port, priv);
return 0;
@@ -254,7 +251,6 @@ static int klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port)
struct klsi_105_private *priv = usb_get_serial_port_data(port);
int retval = 0;
int rc;
- int i;
unsigned long line_state;
struct klsi_105_port_settings *cfg;
unsigned long flags;
@@ -277,14 +273,7 @@ static int klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port)
cfg->unknown2 = 1;
klsi_105_chg_port_settings(port, cfg);
- /* set up termios structure */
spin_lock_irqsave(&priv->lock, flags);
- priv->termios.c_iflag = tty->termios.c_iflag;
- priv->termios.c_oflag = tty->termios.c_oflag;
- priv->termios.c_cflag = tty->termios.c_cflag;
- priv->termios.c_lflag = tty->termios.c_lflag;
- for (i = 0; i < NCCS; i++)
- priv->termios.c_cc[i] = tty->termios.c_cc[i];
priv->cfg.pktlen = cfg->pktlen;
priv->cfg.baudrate = cfg->baudrate;
priv->cfg.databits = cfg->databits;
@@ -296,7 +285,7 @@ static int klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port)
rc = usb_serial_generic_open(tty, port);
if (rc) {
retval = rc;
- goto exit;
+ goto err_free_cfg;
}
rc = usb_control_msg(port->serial->dev,
@@ -311,21 +300,38 @@ static int klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port)
if (rc < 0) {
dev_err(&port->dev, "Enabling read failed (error = %d)\n", rc);
retval = rc;
+ goto err_generic_close;
} else
dev_dbg(&port->dev, "%s - enabled reading\n", __func__);
rc = klsi_105_get_line_state(port, &line_state);
- if (rc >= 0) {
- spin_lock_irqsave(&priv->lock, flags);
- priv->line_state = line_state;
- spin_unlock_irqrestore(&priv->lock, flags);
- dev_dbg(&port->dev, "%s - read line state 0x%lx\n", __func__, line_state);
- retval = 0;
- } else
+ if (rc < 0) {
retval = rc;
+ goto err_disable_read;
+ }
-exit:
+ spin_lock_irqsave(&priv->lock, flags);
+ priv->line_state = line_state;
+ spin_unlock_irqrestore(&priv->lock, flags);
+ dev_dbg(&port->dev, "%s - read line state 0x%lx\n", __func__,
+ line_state);
+
+ return 0;
+
+err_disable_read:
+ usb_control_msg(port->serial->dev,
+ usb_sndctrlpipe(port->serial->dev, 0),
+ KL5KUSB105A_SIO_CONFIGURE,
+ USB_TYPE_VENDOR | USB_DIR_OUT,
+ KL5KUSB105A_SIO_CONFIGURE_READ_OFF,
+ 0, /* index */
+ NULL, 0,
+ KLSI_TIMEOUT);
+err_generic_close:
+ usb_serial_generic_close(port);
+err_free_cfg:
kfree(cfg);
+
return retval;
}
@@ -420,19 +426,6 @@ static void klsi_105_set_termios(struct tty_struct *tty,
*/
baud = tty_get_baud_rate(tty);
- if ((cflag & CBAUD) != (old_cflag & CBAUD)) {
- /* reassert DTR and (maybe) RTS on transition from B0 */
- if ((old_cflag & CBAUD) == B0) {
- dev_dbg(dev, "%s: baud was B0\n", __func__);
-#if 0
- priv->control_state |= TIOCM_DTR;
- /* don't set RTS if using hardware flow control */
- if (!(old_cflag & CRTSCTS))
- priv->control_state |= TIOCM_RTS;
- mct_u232_set_modem_ctrl(serial, priv->control_state);
-#endif
- }
- }
switch (baud) {
case 0: /* handled below */
break;
@@ -466,17 +459,14 @@ static void klsi_105_set_termios(struct tty_struct *tty,
baud = 9600;
break;
}
- if ((cflag & CBAUD) == B0) {
- dev_dbg(dev, "%s: baud is B0\n", __func__);
- /* Drop RTS and DTR */
- /* maybe this should be simulated by sending read
- * disable and read enable messages?
- */
-#if 0
- priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS);
- mct_u232_set_modem_ctrl(serial, priv->control_state);
-#endif
- }
+
+ /*
+ * FIXME: implement B0 handling
+ *
+ * Maybe this should be simulated by sending read disable and read
+ * enable messages?
+ */
+
tty_encode_baud_rate(tty, baud, baud);
if ((cflag & CSIZE) != (old_cflag & CSIZE)) {
@@ -510,22 +500,6 @@ static void klsi_105_set_termios(struct tty_struct *tty,
|| (cflag & CSTOPB) != (old_cflag & CSTOPB)) {
/* Not currently supported */
tty->termios.c_cflag &= ~(PARENB|PARODD|CSTOPB);
-#if 0
- priv->last_lcr = 0;
-
- /* set the parity */
- if (cflag & PARENB)
- priv->last_lcr |= (cflag & PARODD) ?
- MCT_U232_PARITY_ODD : MCT_U232_PARITY_EVEN;
- else
- priv->last_lcr |= MCT_U232_PARITY_NONE;
-
- /* set the number of stop bits */
- priv->last_lcr |= (cflag & CSTOPB) ?
- MCT_U232_STOP_BITS_2 : MCT_U232_STOP_BITS_1;
-
- mct_u232_set_line_ctrl(serial, priv->last_lcr);
-#endif
}
/*
* Set flow control: well, I do not really now how to handle DTR/RTS.
@@ -536,14 +510,6 @@ static void klsi_105_set_termios(struct tty_struct *tty,
|| (cflag & CRTSCTS) != (old_cflag & CRTSCTS)) {
/* Not currently supported */
tty->termios.c_cflag &= ~CRTSCTS;
- /* Drop DTR/RTS if no flow control otherwise assert */
-#if 0
- if ((iflag & IXOFF) || (iflag & IXON) || (cflag & CRTSCTS))
- priv->control_state |= TIOCM_DTR | TIOCM_RTS;
- else
- priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS);
- mct_u232_set_modem_ctrl(serial, priv->control_state);
-#endif
}
memcpy(cfg, &priv->cfg, sizeof(*cfg));
spin_unlock_irqrestore(&priv->lock, flags);
@@ -554,25 +520,6 @@ err:
kfree(cfg);
}
-#if 0
-static void mct_u232_break_ctl(struct tty_struct *tty, int break_state)
-{
- struct usb_serial_port *port = tty->driver_data;
- struct usb_serial *serial = port->serial;
- struct mct_u232_private *priv =
- (struct mct_u232_private *)port->private;
- unsigned char lcr = priv->last_lcr;
-
- dev_dbg(&port->dev, "%s - state=%d\n", __func__, break_state);
-
- /* LOCKING */
- if (break_state)
- lcr |= MCT_U232_SET_BREAK;
-
- mct_u232_set_line_ctrl(serial, lcr);
-}
-#endif
-
static int klsi_105_tiocmget(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c
index 2363654cafc9..813035f51fe7 100644
--- a/drivers/usb/serial/kobil_sct.c
+++ b/drivers/usb/serial/kobil_sct.c
@@ -51,6 +51,7 @@
/* Function prototypes */
+static int kobil_attach(struct usb_serial *serial);
static int kobil_port_probe(struct usb_serial_port *probe);
static int kobil_port_remove(struct usb_serial_port *probe);
static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port);
@@ -86,6 +87,7 @@ static struct usb_serial_driver kobil_device = {
.description = "KOBIL USB smart card terminal",
.id_table = id_table,
.num_ports = 1,
+ .attach = kobil_attach,
.port_probe = kobil_port_probe,
.port_remove = kobil_port_remove,
.ioctl = kobil_ioctl,
@@ -113,6 +115,16 @@ struct kobil_private {
};
+static int kobil_attach(struct usb_serial *serial)
+{
+ if (serial->num_interrupt_out < serial->num_ports) {
+ dev_err(&serial->interface->dev, "missing interrupt-out endpoint\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
static int kobil_port_probe(struct usb_serial_port *port)
{
struct usb_serial *serial = port->serial;
diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c
index 885655315de1..edbc81f205c2 100644
--- a/drivers/usb/serial/mct_u232.c
+++ b/drivers/usb/serial/mct_u232.c
@@ -322,8 +322,12 @@ static int mct_u232_get_modem_stat(struct usb_serial_port *port,
MCT_U232_GET_REQUEST_TYPE,
0, 0, buf, MCT_U232_GET_MODEM_STAT_SIZE,
WDR_TIMEOUT);
- if (rc < 0) {
+ if (rc < MCT_U232_GET_MODEM_STAT_SIZE) {
dev_err(&port->dev, "Get MODEM STATus failed (error = %d)\n", rc);
+
+ if (rc >= 0)
+ rc = -EIO;
+
*msr = 0;
} else {
*msr = buf[0];
diff --git a/drivers/usb/serial/metro-usb.c b/drivers/usb/serial/metro-usb.c
index 39e683096e94..cc84da8dbb84 100644
--- a/drivers/usb/serial/metro-usb.c
+++ b/drivers/usb/serial/metro-usb.c
@@ -135,23 +135,8 @@ static void metrousb_read_int_callback(struct urb *urb)
throttled = metro_priv->throttled;
spin_unlock_irqrestore(&metro_priv->lock, flags);
- /* Continue trying to read if set. */
- if (!throttled) {
- usb_fill_int_urb(port->interrupt_in_urb, port->serial->dev,
- usb_rcvintpipe(port->serial->dev, port->interrupt_in_endpointAddress),
- port->interrupt_in_urb->transfer_buffer,
- port->interrupt_in_urb->transfer_buffer_length,
- metrousb_read_int_callback, port, 1);
-
- result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
-
- if (result)
- dev_err(&port->dev,
- "%s - failed submitting interrupt in urb, error code=%d\n",
- __func__, result);
- }
- return;
-
+ if (throttled)
+ return;
exit:
/* Try to resubmit the urb. */
result = usb_submit_urb(urb, GFP_ATOMIC);
@@ -161,19 +146,8 @@ exit:
__func__, result);
}
-static void metrousb_write_int_callback(struct urb *urb)
-{
- struct usb_serial_port *port = urb->context;
-
- dev_warn(&port->dev, "%s not implemented yet.\n",
- __func__);
-}
-
static void metrousb_cleanup(struct usb_serial_port *port)
{
- dev_dbg(&port->dev, "%s\n", __func__);
-
- usb_unlink_urb(port->interrupt_in_urb);
usb_kill_urb(port->interrupt_in_urb);
metrousb_send_unidirectional_cmd(UNI_CMD_CLOSE, port);
@@ -186,8 +160,6 @@ static int metrousb_open(struct tty_struct *tty, struct usb_serial_port *port)
unsigned long flags = 0;
int result = 0;
- dev_dbg(&port->dev, "%s\n", __func__);
-
/* Make sure the urb is initialized. */
if (!port->interrupt_in_urb) {
dev_err(&port->dev, "%s - interrupt urb not initialized\n",
@@ -227,8 +199,6 @@ static int metrousb_open(struct tty_struct *tty, struct usb_serial_port *port)
__func__, result);
goto exit;
}
-
- dev_dbg(&port->dev, "%s - port open\n", __func__);
exit:
return result;
}
@@ -290,8 +260,6 @@ static void metrousb_throttle(struct tty_struct *tty)
struct metrousb_private *metro_priv = usb_get_serial_port_data(port);
unsigned long flags = 0;
- dev_dbg(tty->dev, "%s\n", __func__);
-
/* Set the private information for the port to stop reading data. */
spin_lock_irqsave(&metro_priv->lock, flags);
metro_priv->throttled = 1;
@@ -305,8 +273,6 @@ static int metrousb_tiocmget(struct tty_struct *tty)
struct metrousb_private *metro_priv = usb_get_serial_port_data(port);
unsigned long flags = 0;
- dev_dbg(tty->dev, "%s\n", __func__);
-
spin_lock_irqsave(&metro_priv->lock, flags);
control_state = metro_priv->control_state;
spin_unlock_irqrestore(&metro_priv->lock, flags);
@@ -350,15 +316,12 @@ static void metrousb_unthrottle(struct tty_struct *tty)
unsigned long flags = 0;
int result = 0;
- dev_dbg(tty->dev, "%s\n", __func__);
-
/* Set the private information for the port to resume reading data. */
spin_lock_irqsave(&metro_priv->lock, flags);
metro_priv->throttled = 0;
spin_unlock_irqrestore(&metro_priv->lock, flags);
/* Submit the urb to read from the port. */
- port->interrupt_in_urb->dev = port->serial->dev;
result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
if (result)
dev_err(tty->dev,
@@ -377,7 +340,6 @@ static struct usb_serial_driver metrousb_device = {
.open = metrousb_open,
.close = metrousb_cleanup,
.read_int_callback = metrousb_read_int_callback,
- .write_int_callback = metrousb_write_int_callback,
.port_probe = metrousb_port_probe,
.port_remove = metrousb_port_remove,
.throttle = metrousb_throttle,
diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c
index de9992b492b0..f075121c6e32 100644
--- a/drivers/usb/serial/mos7720.c
+++ b/drivers/usb/serial/mos7720.c
@@ -65,8 +65,6 @@ struct moschip_port {
struct urb *write_urb_pool[NUM_URBS];
};
-static struct usb_serial_driver moschip7720_2port_driver;
-
#define USB_VENDOR_ID_MOSCHIP 0x9710
#define MOSCHIP_DEVICE_ID_7720 0x7720
#define MOSCHIP_DEVICE_ID_7715 0x7715
@@ -236,11 +234,16 @@ static int read_mos_reg(struct usb_serial *serial, unsigned int serial_portnum,
status = usb_control_msg(usbdev, pipe, request, requesttype, value,
index, buf, 1, MOS_WDR_TIMEOUT);
- if (status == 1)
+ if (status == 1) {
*data = *buf;
- else if (status < 0)
+ } else {
dev_err(&usbdev->dev,
"mos7720: usb_control_msg() failed: %d\n", status);
+ if (status >= 0)
+ status = -EIO;
+ *data = 0;
+ }
+
kfree(buf);
return status;
@@ -970,25 +973,6 @@ static void mos7720_bulk_out_data_callback(struct urb *urb)
tty_port_tty_wakeup(&mos7720_port->port->port);
}
-/*
- * mos77xx_probe
- * this function installs the appropriate read interrupt endpoint callback
- * depending on whether the device is a 7720 or 7715, thus avoiding costly
- * run-time checks in the high-frequency callback routine itself.
- */
-static int mos77xx_probe(struct usb_serial *serial,
- const struct usb_device_id *id)
-{
- if (id->idProduct == MOSCHIP_DEVICE_ID_7715)
- moschip7720_2port_driver.read_int_callback =
- mos7715_interrupt_callback;
- else
- moschip7720_2port_driver.read_int_callback =
- mos7720_interrupt_callback;
-
- return 0;
-}
-
static int mos77xx_calc_num_ports(struct usb_serial *serial)
{
u16 product = le16_to_cpu(serial->dev->descriptor.idProduct);
@@ -1861,16 +1845,12 @@ static int get_serial_info(struct moschip_port *mos7720_port,
{
struct serial_struct tmp;
- if (!retinfo)
- return -EFAULT;
-
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.flags = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ;
tmp.xmit_fifo_size = NUM_URBS * URB_TRANSFER_BUFFER_SIZE;
tmp.baud_base = 9600;
tmp.close_delay = 5*HZ;
@@ -1920,6 +1900,11 @@ static int mos7720_startup(struct usb_serial *serial)
u16 product;
int ret_val;
+ if (serial->num_bulk_in < 2 || serial->num_bulk_out < 2) {
+ dev_err(&serial->interface->dev, "missing bulk endpoints\n");
+ return -ENODEV;
+ }
+
product = le16_to_cpu(serial->dev->descriptor.idProduct);
dev = serial->dev;
@@ -1944,19 +1929,18 @@ static int mos7720_startup(struct usb_serial *serial)
tmp->interrupt_in_endpointAddress;
serial->port[1]->interrupt_in_urb = NULL;
serial->port[1]->interrupt_in_buffer = NULL;
+
+ if (serial->port[0]->interrupt_in_urb) {
+ struct urb *urb = serial->port[0]->interrupt_in_urb;
+
+ urb->complete = mos7715_interrupt_callback;
+ }
}
/* setting configuration feature to one */
usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
(__u8)0x03, 0x00, 0x01, 0x00, NULL, 0x00, 5000);
- /* start the interrupt urb */
- ret_val = usb_submit_urb(serial->port[0]->interrupt_in_urb, GFP_KERNEL);
- if (ret_val)
- dev_err(&dev->dev,
- "%s - Error %d submitting control urb\n",
- __func__, ret_val);
-
#ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT
if (product == MOSCHIP_DEVICE_ID_7715) {
ret_val = mos7715_parport_init(serial);
@@ -1964,6 +1948,13 @@ static int mos7720_startup(struct usb_serial *serial)
return ret_val;
}
#endif
+ /* start the interrupt urb */
+ ret_val = usb_submit_urb(serial->port[0]->interrupt_in_urb, GFP_KERNEL);
+ if (ret_val) {
+ dev_err(&dev->dev, "failed to submit interrupt urb: %d\n",
+ ret_val);
+ }
+
/* LSR For Port 1 */
read_mos_reg(serial, 0, MOS7720_LSR, &data);
dev_dbg(&dev->dev, "LSR:%x\n", data);
@@ -1973,6 +1964,8 @@ static int mos7720_startup(struct usb_serial *serial)
static void mos7720_release(struct usb_serial *serial)
{
+ usb_kill_urb(serial->port[0]->interrupt_in_urb);
+
#ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT
/* close the parallel port */
@@ -2022,11 +2015,6 @@ static int mos7720_port_probe(struct usb_serial_port *port)
if (!mos7720_port)
return -ENOMEM;
- /* Initialize all port interrupt end point to port 0 int endpoint.
- * Our device has only one interrupt endpoint common to all ports.
- */
- port->interrupt_in_endpointAddress =
- port->serial->port[0]->interrupt_in_endpointAddress;
mos7720_port->port = port;
usb_set_serial_port_data(port, mos7720_port);
@@ -2056,7 +2044,6 @@ static struct usb_serial_driver moschip7720_2port_driver = {
.close = mos7720_close,
.throttle = mos7720_throttle,
.unthrottle = mos7720_unthrottle,
- .probe = mos77xx_probe,
.attach = mos7720_startup,
.release = mos7720_release,
.port_probe = mos7720_port_probe,
@@ -2070,7 +2057,7 @@ static struct usb_serial_driver moschip7720_2port_driver = {
.chars_in_buffer = mos7720_chars_in_buffer,
.break_ctl = mos7720_break,
.read_bulk_callback = mos7720_bulk_in_callback,
- .read_int_callback = NULL /* dynamically assigned in probe() */
+ .read_int_callback = mos7720_interrupt_callback,
};
static struct usb_serial_driver * const serial_drivers[] = {
diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c
index 57426d703a09..3821c53fcee9 100644
--- a/drivers/usb/serial/mos7840.c
+++ b/drivers/usb/serial/mos7840.c
@@ -214,7 +214,6 @@ MODULE_DEVICE_TABLE(usb, id_table);
struct moschip_port {
int port_num; /*Actual port number in the device(1,2,etc) */
- struct urb *write_urb; /* write URB for this port */
struct urb *read_urb; /* read URB for this port */
__u8 shadowLCR; /* last LCR value received */
__u8 shadowMCR; /* last MCR value received */
@@ -285,9 +284,15 @@ static int mos7840_get_reg_sync(struct usb_serial_port *port, __u16 reg,
ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), MCS_RDREQ,
MCS_RD_RTYPE, 0, reg, buf, VENDOR_READ_LENGTH,
MOS_WDR_TIMEOUT);
+ if (ret < VENDOR_READ_LENGTH) {
+ if (ret >= 0)
+ ret = -EIO;
+ goto out;
+ }
+
*val = buf[0];
dev_dbg(&port->dev, "%s offset is %x, return val %x\n", __func__, reg, *val);
-
+out:
kfree(buf);
return ret;
}
@@ -353,8 +358,13 @@ static int mos7840_get_uart_reg(struct usb_serial_port *port, __u16 reg,
ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), MCS_RDREQ,
MCS_RD_RTYPE, Wval, reg, buf, VENDOR_READ_LENGTH,
MOS_WDR_TIMEOUT);
+ if (ret < VENDOR_READ_LENGTH) {
+ if (ret >= 0)
+ ret = -EIO;
+ goto out;
+ }
*val = buf[0];
-
+out:
kfree(buf);
return ret;
}
@@ -1024,6 +1034,7 @@ static int mos7840_open(struct tty_struct *tty, struct usb_serial_port *port)
* (can't set it up in mos7840_startup as the structures *
* were not set up at that time.) */
if (port0->open_ports == 1) {
+ /* FIXME: Buffer never NULL, so URB is not submitted. */
if (serial->port[0]->interrupt_in_buffer == NULL) {
/* set up interrupt urb */
usb_fill_int_urb(serial->port[0]->interrupt_in_urb,
@@ -1037,9 +1048,7 @@ static int mos7840_open(struct tty_struct *tty, struct usb_serial_port *port)
serial,
serial->port[0]->interrupt_in_urb->interval);
- /* start interrupt read for mos7840 *
- * will continue as long as mos7840 is connected */
-
+ /* start interrupt read for mos7840 */
response =
usb_submit_urb(serial->port[0]->interrupt_in_urb,
GFP_KERNEL);
@@ -1186,7 +1195,6 @@ static void mos7840_close(struct usb_serial_port *port)
}
}
- usb_kill_urb(mos7840_port->write_urb);
usb_kill_urb(mos7840_port->read_urb);
mos7840_port->read_urb_busy = false;
@@ -1199,12 +1207,6 @@ static void mos7840_close(struct usb_serial_port *port)
}
}
- if (mos7840_port->write_urb) {
- /* if this urb had a transfer buffer already (old tx) free it */
- kfree(mos7840_port->write_urb->transfer_buffer);
- usb_free_urb(mos7840_port->write_urb);
- }
-
Data = 0x0;
mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data);
@@ -1489,10 +1491,10 @@ static int mos7840_tiocmget(struct tty_struct *tty)
return -ENODEV;
status = mos7840_get_uart_reg(port, MODEM_STATUS_REGISTER, &msr);
- if (status != 1)
+ if (status < 0)
return -EIO;
status = mos7840_get_uart_reg(port, MODEM_CONTROL_REGISTER, &mcr);
- if (status != 1)
+ if (status < 0)
return -EIO;
result = ((mcr & MCR_DTR) ? TIOCM_DTR : 0)
| ((mcr & MCR_RTS) ? TIOCM_RTS : 0)
@@ -1956,16 +1958,12 @@ static int mos7840_get_serial_info(struct moschip_port *mos7840_port,
if (mos7840_port == NULL)
return -1;
- if (!retinfo)
- return -EFAULT;
-
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.flags = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ;
tmp.xmit_fifo_size = NUM_URBS * URB_TRANSFER_BUFFER_SIZE;
tmp.baud_base = 9600;
tmp.close_delay = 5 * HZ;
@@ -2116,6 +2114,18 @@ static int mos7840_calc_num_ports(struct usb_serial *serial)
return mos7840_num_ports;
}
+static int mos7840_attach(struct usb_serial *serial)
+{
+ if (serial->num_bulk_in < serial->num_ports ||
+ serial->num_bulk_out < serial->num_ports ||
+ serial->num_interrupt_in < 1) {
+ dev_err(&serial->interface->dev, "missing endpoints\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
static int mos7840_port_probe(struct usb_serial_port *port)
{
struct usb_serial *serial = port->serial;
@@ -2391,6 +2401,7 @@ static struct usb_serial_driver moschip7840_4port_device = {
.tiocmset = mos7840_tiocmset,
.tiocmiwait = usb_serial_generic_tiocmiwait,
.get_icount = usb_serial_generic_get_icount,
+ .attach = mos7840_attach,
.port_probe = mos7840_port_probe,
.port_remove = mos7840_port_remove,
.read_bulk_callback = mos7840_bulk_in_callback,
diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c
index f6c6900bccf0..dd706953b466 100644
--- a/drivers/usb/serial/omninet.c
+++ b/drivers/usb/serial/omninet.c
@@ -31,13 +31,13 @@
#define BT_IGNITIONPRO_ID 0x2000
/* function prototypes */
-static int omninet_open(struct tty_struct *tty, struct usb_serial_port *port);
static void omninet_process_read_urb(struct urb *urb);
static void omninet_write_bulk_callback(struct urb *urb);
static int omninet_write(struct tty_struct *tty, struct usb_serial_port *port,
const unsigned char *buf, int count);
static int omninet_write_room(struct tty_struct *tty);
static void omninet_disconnect(struct usb_serial *serial);
+static int omninet_attach(struct usb_serial *serial);
static int omninet_port_probe(struct usb_serial_port *port);
static int omninet_port_remove(struct usb_serial_port *port);
@@ -56,9 +56,9 @@ static struct usb_serial_driver zyxel_omninet_device = {
.description = "ZyXEL - omni.net lcd plus usb",
.id_table = id_table,
.num_ports = 1,
+ .attach = omninet_attach,
.port_probe = omninet_port_probe,
.port_remove = omninet_port_remove,
- .open = omninet_open,
.write = omninet_write,
.write_room = omninet_write_room,
.write_bulk_callback = omninet_write_bulk_callback,
@@ -104,6 +104,17 @@ struct omninet_data {
__u8 od_outseq; /* Sequence number for bulk_out URBs */
};
+static int omninet_attach(struct usb_serial *serial)
+{
+ /* The second bulk-out endpoint is used for writing. */
+ if (serial->num_bulk_out < 2) {
+ dev_err(&serial->interface->dev, "missing endpoints\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
static int omninet_port_probe(struct usb_serial_port *port)
{
struct omninet_data *od;
@@ -127,17 +138,6 @@ static int omninet_port_remove(struct usb_serial_port *port)
return 0;
}
-static int omninet_open(struct tty_struct *tty, struct usb_serial_port *port)
-{
- struct usb_serial *serial = port->serial;
- struct usb_serial_port *wport;
-
- wport = serial->port[1];
- tty_port_tty_set(&wport->port, tty);
-
- return usb_serial_generic_open(tty, port);
-}
-
#define OMNINET_HEADERLEN 4
#define OMNINET_BULKOUTSIZE 64
#define OMNINET_PAYLOADSIZE (OMNINET_BULKOUTSIZE - OMNINET_HEADERLEN)
diff --git a/drivers/usb/serial/opticon.c b/drivers/usb/serial/opticon.c
index 4b7bfb394a32..3937b9c3cc69 100644
--- a/drivers/usb/serial/opticon.c
+++ b/drivers/usb/serial/opticon.c
@@ -142,7 +142,7 @@ static int opticon_open(struct tty_struct *tty, struct usb_serial_port *port)
usb_clear_halt(port->serial->dev, port->read_urb->pipe);
res = usb_serial_generic_open(tty, port);
- if (!res)
+ if (res)
return res;
/* Request CTS line state, sometimes during opening the current
@@ -336,9 +336,6 @@ static int get_serial_info(struct usb_serial_port *port,
{
struct serial_struct tmp;
- if (!serial)
- return -EFAULT;
-
memset(&tmp, 0x00, sizeof(tmp));
/* fake emulate a 16550 uart to make userspace code happy */
@@ -346,7 +343,6 @@ static int get_serial_info(struct usb_serial_port *port,
tmp.line = port->minor;
tmp.port = 0;
tmp.irq = 0;
- tmp.flags = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ;
tmp.xmit_fifo_size = 1024;
tmp.baud_base = 9600;
tmp.close_delay = 5*HZ;
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index 9894e341c6ac..af67a0de6b5d 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -233,6 +233,14 @@ static void option_instat_callback(struct urb *urb);
#define BANDRICH_PRODUCT_1012 0x1012
#define QUALCOMM_VENDOR_ID 0x05C6
+/* These Quectel products use Qualcomm's vendor ID */
+#define QUECTEL_PRODUCT_UC20 0x9003
+#define QUECTEL_PRODUCT_UC15 0x9090
+
+#define QUECTEL_VENDOR_ID 0x2c7c
+/* These Quectel products use Quectel's vendor ID */
+#define QUECTEL_PRODUCT_EC21 0x0121
+#define QUECTEL_PRODUCT_EC25 0x0125
#define CMOTECH_VENDOR_ID 0x16d8
#define CMOTECH_PRODUCT_6001 0x6001
@@ -268,6 +276,8 @@ static void option_instat_callback(struct urb *urb);
#define TELIT_PRODUCT_CC864_SINGLE 0x1006
#define TELIT_PRODUCT_DE910_DUAL 0x1010
#define TELIT_PRODUCT_UE910_V2 0x1012
+#define TELIT_PRODUCT_LE922_USBCFG1 0x1040
+#define TELIT_PRODUCT_LE922_USBCFG2 0x1041
#define TELIT_PRODUCT_LE922_USBCFG0 0x1042
#define TELIT_PRODUCT_LE922_USBCFG3 0x1043
#define TELIT_PRODUCT_LE922_USBCFG5 0x1045
@@ -1159,7 +1169,14 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE(QUALCOMM_VENDOR_ID, 0x6613)}, /* Onda H600/ZTE MF330 */
{ USB_DEVICE(QUALCOMM_VENDOR_ID, 0x0023)}, /* ONYX 3G device */
{ USB_DEVICE(QUALCOMM_VENDOR_ID, 0x9000)}, /* SIMCom SIM5218 */
- { USB_DEVICE(QUALCOMM_VENDOR_ID, 0x9003), /* Quectel UC20 */
+ /* Quectel products using Qualcomm vendor ID */
+ { USB_DEVICE(QUALCOMM_VENDOR_ID, QUECTEL_PRODUCT_UC15)},
+ { USB_DEVICE(QUALCOMM_VENDOR_ID, QUECTEL_PRODUCT_UC20),
+ .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ /* Quectel products using Quectel vendor ID */
+ { USB_DEVICE(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC21),
+ .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ { USB_DEVICE(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC25),
.driver_info = (kernel_ulong_t)&net_intf4_blacklist },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_6001) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CMU_300) },
@@ -1210,6 +1227,10 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_UE910_V2) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG0),
.driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg0 },
+ { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG1),
+ .driver_info = (kernel_ulong_t)&telit_le910_blacklist },
+ { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG2),
+ .driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg3 },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG3),
.driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg3 },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG5, 0xff),
@@ -1989,6 +2010,7 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x7d02, 0xff, 0x00, 0x00) },
{ USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x7d03, 0xff, 0x02, 0x01) },
{ USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x7d03, 0xff, 0x00, 0x00) },
+ { USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7d04, 0xff) }, /* D-Link DWM-158 */
{ USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7e19, 0xff), /* D-Link DWM-221 B1 */
.driver_info = (kernel_ulong_t)&net_intf4_blacklist },
{ USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x3e01, 0xff, 0xff, 0xff) }, /* D-Link DWM-152/C1 */
@@ -2000,6 +2022,7 @@ 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) */
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, option_ids);
diff --git a/drivers/usb/serial/oti6858.c b/drivers/usb/serial/oti6858.c
index a4b88bc038b6..b8bf52bf7a94 100644
--- a/drivers/usb/serial/oti6858.c
+++ b/drivers/usb/serial/oti6858.c
@@ -134,6 +134,7 @@ static int oti6858_chars_in_buffer(struct tty_struct *tty);
static int oti6858_tiocmget(struct tty_struct *tty);
static int oti6858_tiocmset(struct tty_struct *tty,
unsigned int set, unsigned int clear);
+static int oti6858_attach(struct usb_serial *serial);
static int oti6858_port_probe(struct usb_serial_port *port);
static int oti6858_port_remove(struct usb_serial_port *port);
@@ -158,6 +159,7 @@ static struct usb_serial_driver oti6858_device = {
.write_bulk_callback = oti6858_write_bulk_callback,
.write_room = oti6858_write_room,
.chars_in_buffer = oti6858_chars_in_buffer,
+ .attach = oti6858_attach,
.port_probe = oti6858_port_probe,
.port_remove = oti6858_port_remove,
};
@@ -324,6 +326,20 @@ static void send_data(struct work_struct *work)
usb_serial_port_softint(port);
}
+static int oti6858_attach(struct usb_serial *serial)
+{
+ unsigned char num_ports = serial->num_ports;
+
+ if (serial->num_bulk_in < num_ports ||
+ serial->num_bulk_out < num_ports ||
+ serial->num_interrupt_in < num_ports) {
+ dev_err(&serial->interface->dev, "missing endpoints\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
static int oti6858_port_probe(struct usb_serial_port *port)
{
struct oti6858_private *priv;
diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c
index ae682e4eeaef..ca69eb42071b 100644
--- a/drivers/usb/serial/pl2303.c
+++ b/drivers/usb/serial/pl2303.c
@@ -49,6 +49,7 @@ static const struct usb_device_id id_table[] = {
{ USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID) },
{ USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID_RSAQ5) },
{ USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_ID) },
+ { USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_ID2) },
{ USB_DEVICE(ATEN_VENDOR_ID2, ATEN_PRODUCT_ID) },
{ USB_DEVICE(ELCOM_VENDOR_ID, ELCOM_PRODUCT_ID) },
{ USB_DEVICE(ELCOM_VENDOR_ID, ELCOM_PRODUCT_ID_UCSGT) },
@@ -220,9 +221,17 @@ static int pl2303_probe(struct usb_serial *serial,
static int pl2303_startup(struct usb_serial *serial)
{
struct pl2303_serial_private *spriv;
+ unsigned char num_ports = serial->num_ports;
enum pl2303_type type = TYPE_01;
unsigned char *buf;
+ if (serial->num_bulk_in < num_ports ||
+ serial->num_bulk_out < num_ports ||
+ serial->num_interrupt_in < num_ports) {
+ dev_err(&serial->interface->dev, "missing endpoints\n");
+ return -ENODEV;
+ }
+
spriv = kzalloc(sizeof(*spriv), GFP_KERNEL);
if (!spriv)
return -ENOMEM;
@@ -441,7 +450,7 @@ static int pl2303_get_line_request(struct usb_serial_port *port,
if (ret != 7) {
dev_err(&port->dev, "%s - failed: %d\n", __func__, ret);
- if (ret > 0)
+ if (ret >= 0)
ret = -EIO;
return ret;
@@ -461,12 +470,8 @@ static int pl2303_set_line_request(struct usb_serial_port *port,
ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
SET_LINE_REQUEST, SET_LINE_REQUEST_TYPE,
0, 0, buf, 7, 100);
- if (ret != 7) {
+ if (ret < 0) {
dev_err(&port->dev, "%s - failed: %d\n", __func__, ret);
-
- if (ret > 0)
- ret = -EIO;
-
return ret;
}
diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h
index e3b7af8adfb7..09d9be88209e 100644
--- a/drivers/usb/serial/pl2303.h
+++ b/drivers/usb/serial/pl2303.h
@@ -27,6 +27,7 @@
#define ATEN_VENDOR_ID 0x0557
#define ATEN_VENDOR_ID2 0x0547
#define ATEN_PRODUCT_ID 0x2008
+#define ATEN_PRODUCT_ID2 0x2118
#define IODATA_VENDOR_ID 0x04bb
#define IODATA_PRODUCT_ID 0x0a03
diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c
index 1bc6089b9008..38b3f0d8cd58 100644
--- a/drivers/usb/serial/qcserial.c
+++ b/drivers/usb/serial/qcserial.c
@@ -124,6 +124,7 @@ static const struct usb_device_id id_table[] = {
{USB_DEVICE(0x1410, 0xa021)}, /* Novatel Gobi 3000 Composite */
{USB_DEVICE(0x413c, 0x8193)}, /* Dell Gobi 3000 QDL */
{USB_DEVICE(0x413c, 0x8194)}, /* Dell Gobi 3000 Composite */
+ {USB_DEVICE(0x413c, 0x81a6)}, /* Dell DW5570 QDL (MC8805) */
{USB_DEVICE(0x1199, 0x68a4)}, /* Sierra Wireless QDL */
{USB_DEVICE(0x1199, 0x68a5)}, /* Sierra Wireless Modem */
{USB_DEVICE(0x1199, 0x68a8)}, /* Sierra Wireless QDL */
@@ -168,6 +169,8 @@ static const struct usb_device_id id_table[] = {
{DEVICE_SWI(0x413c, 0x81a9)}, /* Dell Wireless 5808e Gobi(TM) 4G LTE Mobile Broadband Card */
{DEVICE_SWI(0x413c, 0x81b1)}, /* Dell Wireless 5809e Gobi(TM) 4G LTE Mobile Broadband Card */
{DEVICE_SWI(0x413c, 0x81b3)}, /* Dell Wireless 5809e Gobi(TM) 4G LTE Mobile Broadband Card (rev3) */
+ {DEVICE_SWI(0x413c, 0x81b5)}, /* Dell Wireless 5811e QDL */
+ {DEVICE_SWI(0x413c, 0x81b6)}, /* Dell Wireless 5811e QDL */
/* Huawei devices */
{DEVICE_HWI(0x03f0, 0x581d)}, /* HP lt4112 LTE/HSPA+ Gobi 4G Modem (Huawei me906e) */
diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c
index 85acb50a7ee2..fdbb904d153f 100644
--- a/drivers/usb/serial/quatech2.c
+++ b/drivers/usb/serial/quatech2.c
@@ -188,22 +188,22 @@ static inline int qt2_setdevice(struct usb_device *dev, u8 *data)
}
-static inline int qt2_getdevice(struct usb_device *dev, u8 *data)
-{
- return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
- QT_SET_GET_DEVICE, 0xc0, 0, 0,
- data, 3, QT2_USB_TIMEOUT);
-}
-
static inline int qt2_getregister(struct usb_device *dev,
u8 uart,
u8 reg,
u8 *data)
{
- return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
- QT_SET_GET_REGISTER, 0xc0, reg,
- uart, data, sizeof(*data), QT2_USB_TIMEOUT);
+ int ret;
+
+ 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 >= 0)
+ ret = -EIO;
+ }
+ return ret;
}
static inline int qt2_setregister(struct usb_device *dev,
@@ -372,9 +372,11 @@ static int qt2_open(struct tty_struct *tty, struct usb_serial_port *port)
0xc0, 0,
device_port, data, 2, QT2_USB_TIMEOUT);
- if (status < 0) {
+ if (status < 2) {
dev_err(&port->dev, "%s - open port failed %i\n", __func__,
status);
+ if (status >= 0)
+ status = -EIO;
kfree(data);
return status;
}
@@ -408,16 +410,12 @@ static void qt2_close(struct usb_serial_port *port)
{
struct usb_serial *serial;
struct qt2_port_private *port_priv;
- unsigned long flags;
int i;
serial = port->serial;
port_priv = usb_get_serial_port_data(port);
- spin_lock_irqsave(&port_priv->urb_lock, flags);
usb_kill_urb(port_priv->write_urb);
- port_priv->urb_in_use = false;
- spin_unlock_irqrestore(&port_priv->urb_lock, flags);
/* flush the port transmit buffer */
i = usb_control_msg(serial->dev,
@@ -463,14 +461,10 @@ static int get_serial_info(struct usb_serial_port *port,
{
struct serial_struct tmp;
- if (!retinfo)
- return -EFAULT;
-
memset(&tmp, 0, sizeof(tmp));
tmp.line = port->minor;
tmp.port = 0;
tmp.irq = 0;
- tmp.flags = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ;
tmp.xmit_fifo_size = port->bulk_out_size;
tmp.baud_base = 9600;
tmp.close_delay = 5*HZ;
diff --git a/drivers/usb/serial/safe_serial.c b/drivers/usb/serial/safe_serial.c
index 93c6c9b08daa..8a069aa154ed 100644
--- a/drivers/usb/serial/safe_serial.c
+++ b/drivers/usb/serial/safe_serial.c
@@ -200,6 +200,11 @@ static void safe_process_read_urb(struct urb *urb)
if (!safe)
goto out;
+ if (length < 2) {
+ dev_err(&port->dev, "malformed packet\n");
+ return;
+ }
+
fcs = fcs_compute10(data, length, CRC10_INITFCS);
if (fcs) {
dev_err(&port->dev, "%s - bad CRC %x\n", __func__, fcs);
diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c
index e1994e264cc0..465e851b2815 100644
--- a/drivers/usb/serial/sierra.c
+++ b/drivers/usb/serial/sierra.c
@@ -137,24 +137,9 @@ static int is_himemory(const u8 ifnum,
return 0;
}
-static int sierra_calc_interface(struct usb_serial *serial)
+static u8 sierra_interface_num(struct usb_serial *serial)
{
- int interface;
- struct usb_interface *p_interface;
- struct usb_host_interface *p_host_interface;
-
- /* Get the interface structure pointer from the serial struct */
- p_interface = serial->interface;
-
- /* Get a pointer to the host interface structure */
- p_host_interface = p_interface->cur_altsetting;
-
- /* read the interface descriptor for this active altsetting
- * to find out the interface number we are on
- */
- interface = p_host_interface->desc.bInterfaceNumber;
-
- return interface;
+ return serial->interface->cur_altsetting->desc.bInterfaceNumber;
}
static int sierra_probe(struct usb_serial *serial,
@@ -165,7 +150,7 @@ static int sierra_probe(struct usb_serial *serial,
u8 ifnum;
udev = serial->dev;
- ifnum = sierra_calc_interface(serial);
+ ifnum = sierra_interface_num(serial);
/*
* If this interface supports more than 1 alternate
@@ -178,9 +163,6 @@ static int sierra_probe(struct usb_serial *serial,
usb_set_interface(udev, ifnum, 1);
}
- /* ifnum could have changed - by calling usb_set_interface */
- ifnum = sierra_calc_interface(serial);
-
if (is_blacklisted(ifnum,
(struct sierra_iface_info *)id->driver_info)) {
dev_dbg(&serial->dev->dev,
@@ -342,7 +324,7 @@ static int sierra_send_setup(struct usb_serial_port *port)
/* If composite device then properly report interface */
if (serial->num_ports == 1) {
- interface = sierra_calc_interface(serial);
+ interface = sierra_interface_num(serial);
/* Control message is sent only to interfaces with
* interrupt_in endpoints
*/
@@ -916,7 +898,7 @@ static int sierra_port_probe(struct usb_serial_port *port)
/* Determine actual memory requirements */
if (serial->num_ports == 1) {
/* Get interface number for composite device */
- ifnum = sierra_calc_interface(serial);
+ ifnum = sierra_interface_num(serial);
himemoryp = &typeB_interface_list;
} else {
/* This is really the usb-serial port number of the interface
diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c
index ef0dbf0703c5..ddfd787c461c 100644
--- a/drivers/usb/serial/spcp8x5.c
+++ b/drivers/usb/serial/spcp8x5.c
@@ -154,6 +154,19 @@ static int spcp8x5_probe(struct usb_serial *serial,
return 0;
}
+static int spcp8x5_attach(struct usb_serial *serial)
+{
+ unsigned char num_ports = serial->num_ports;
+
+ if (serial->num_bulk_in < num_ports ||
+ serial->num_bulk_out < num_ports) {
+ dev_err(&serial->interface->dev, "missing endpoints\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
static int spcp8x5_port_probe(struct usb_serial_port *port)
{
const struct usb_device_id *id = usb_get_serial_data(port->serial);
@@ -219,11 +232,17 @@ static int spcp8x5_get_msr(struct usb_serial_port *port, u8 *status)
ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
GET_UART_STATUS, GET_UART_STATUS_TYPE,
0, GET_UART_STATUS_MSR, buf, 1, 100);
- if (ret < 0)
+ if (ret < 1) {
dev_err(&port->dev, "failed to get modem status: %d\n", ret);
+ if (ret >= 0)
+ ret = -EIO;
+ goto out;
+ }
dev_dbg(&port->dev, "0xc0:0x22:0:6 %d - 0x02%x\n", ret, *buf);
*status = *buf;
+ ret = 0;
+out:
kfree(buf);
return ret;
@@ -477,6 +496,7 @@ static struct usb_serial_driver spcp8x5_device = {
.tiocmget = spcp8x5_tiocmget,
.tiocmset = spcp8x5_tiocmset,
.probe = spcp8x5_probe,
+ .attach = spcp8x5_attach,
.port_probe = spcp8x5_port_probe,
.port_remove = spcp8x5_port_remove,
};
diff --git a/drivers/usb/serial/ssu100.c b/drivers/usb/serial/ssu100.c
index 70a098de429f..5aa7bbbeba3d 100644
--- a/drivers/usb/serial/ssu100.c
+++ b/drivers/usb/serial/ssu100.c
@@ -80,9 +80,17 @@ static inline int ssu100_setdevice(struct usb_device *dev, u8 *data)
static inline int ssu100_getdevice(struct usb_device *dev, u8 *data)
{
- return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
- QT_SET_GET_DEVICE, 0xc0, 0, 0,
- data, 3, 300);
+ int ret;
+
+ ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ QT_SET_GET_DEVICE, 0xc0, 0, 0,
+ data, 3, 300);
+ if (ret < 3) {
+ if (ret >= 0)
+ ret = -EIO;
+ }
+
+ return ret;
}
static inline int ssu100_getregister(struct usb_device *dev,
@@ -90,10 +98,17 @@ static inline int ssu100_getregister(struct usb_device *dev,
unsigned short reg,
u8 *data)
{
- return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
- QT_SET_GET_REGISTER, 0xc0, reg,
- uart, data, sizeof(*data), 300);
+ int ret;
+
+ 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 >= 0)
+ ret = -EIO;
+ }
+ return ret;
}
@@ -289,8 +304,10 @@ static int ssu100_open(struct tty_struct *tty, struct usb_serial_port *port)
QT_OPEN_CLOSE_CHANNEL,
QT_TRANSFER_IN, 0x01,
0, data, 2, 300);
- if (result < 0) {
+ if (result < 2) {
dev_dbg(&port->dev, "%s - open failed %i\n", __func__, result);
+ if (result >= 0)
+ result = -EIO;
kfree(data);
return result;
}
@@ -318,14 +335,10 @@ static int get_serial_info(struct usb_serial_port *port,
{
struct serial_struct tmp;
- if (!retinfo)
- return -EFAULT;
-
memset(&tmp, 0, sizeof(tmp));
tmp.line = port->minor;
tmp.port = 0;
tmp.irq = 0;
- tmp.flags = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ;
tmp.xmit_fifo_size = port->bulk_out_size;
tmp.baud_base = 9600;
tmp.close_delay = 5*HZ;
diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c
index a8b9bdba314f..3107bf5d1c96 100644
--- a/drivers/usb/serial/ti_usb_3410_5052.c
+++ b/drivers/usb/serial/ti_usb_3410_5052.c
@@ -579,6 +579,13 @@ static int ti_startup(struct usb_serial *serial)
goto free_tdev;
}
+ if (serial->num_bulk_in < serial->num_ports ||
+ serial->num_bulk_out < serial->num_ports) {
+ dev_err(&serial->interface->dev, "missing endpoints\n");
+ status = -ENODEV;
+ goto free_tdev;
+ }
+
return 0;
free_tdev:
@@ -1426,9 +1433,6 @@ static int ti_get_serial_info(struct ti_port *tport,
struct serial_struct ret_serial;
unsigned cwait;
- if (!ret_arg)
- return -EFAULT;
-
cwait = port->port.closing_wait;
if (cwait != ASYNC_CLOSING_WAIT_NONE)
cwait = jiffies_to_msecs(cwait) / 10;
@@ -1549,13 +1553,10 @@ static int ti_command_out_sync(struct ti_device *tdev, __u8 command,
(USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT),
value, moduleid, data, size, 1000);
- if (status == size)
- status = 0;
-
- if (status > 0)
- status = -ECOMM;
+ if (status < 0)
+ return status;
- return status;
+ return 0;
}
@@ -1571,8 +1572,7 @@ static int ti_command_in_sync(struct ti_device *tdev, __u8 command,
if (status == size)
status = 0;
-
- if (status > 0)
+ else if (status >= 0)
status = -ECOMM;
return status;
diff --git a/drivers/usb/serial/upd78f0730.c b/drivers/usb/serial/upd78f0730.c
new file mode 100644
index 000000000000..a028dd2310c9
--- /dev/null
+++ b/drivers/usb/serial/upd78f0730.c
@@ -0,0 +1,441 @@
+/*
+ * Renesas Electronics uPD78F0730 USB to serial converter driver
+ *
+ * Copyright (C) 2014,2016 Maksim Salau <maksim.salau@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * Protocol of the adaptor is described in the application note U19660EJ1V0AN00
+ * μPD78F0730 8-bit Single-Chip Microcontroller
+ * USB-to-Serial Conversion Software
+ * <https://www.renesas.com/en-eu/doc/DocumentServer/026/U19660EJ1V0AN00.pdf>
+ *
+ * The adaptor functionality is limited to the following:
+ * - data bits: 7 or 8
+ * - stop bits: 1 or 2
+ * - parity: even, odd or none
+ * - flow control: none
+ * - baud rates: 0, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 153600
+ * - signals: DTR, RTS and BREAK
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+
+#define DRIVER_DESC "Renesas uPD78F0730 USB to serial converter driver"
+
+#define DRIVER_AUTHOR "Maksim Salau <maksim.salau@gmail.com>"
+
+static const struct usb_device_id id_table[] = {
+ { USB_DEVICE(0x0409, 0x0063) }, /* V850ESJX3-STICK */
+ { USB_DEVICE(0x045B, 0x0212) }, /* YRPBRL78G13, YRPBRL78G14 */
+ { USB_DEVICE(0x064B, 0x7825) }, /* Analog Devices EVAL-ADXL362Z-DB */
+ {}
+};
+
+MODULE_DEVICE_TABLE(usb, id_table);
+
+/*
+ * Each adaptor is associated with a private structure, that holds the current
+ * state of control signals (DTR, RTS and BREAK).
+ */
+struct upd78f0730_port_private {
+ struct mutex lock; /* mutex to protect line_signals */
+ u8 line_signals;
+};
+
+/* Op-codes of control commands */
+#define UPD78F0730_CMD_LINE_CONTROL 0x00
+#define UPD78F0730_CMD_SET_DTR_RTS 0x01
+#define UPD78F0730_CMD_SET_XON_XOFF_CHR 0x02
+#define UPD78F0730_CMD_OPEN_CLOSE 0x03
+#define UPD78F0730_CMD_SET_ERR_CHR 0x04
+
+/* Data sizes in UPD78F0730_CMD_LINE_CONTROL command */
+#define UPD78F0730_DATA_SIZE_7_BITS 0x00
+#define UPD78F0730_DATA_SIZE_8_BITS 0x01
+#define UPD78F0730_DATA_SIZE_MASK 0x01
+
+/* Stop-bit modes in UPD78F0730_CMD_LINE_CONTROL command */
+#define UPD78F0730_STOP_BIT_1_BIT 0x00
+#define UPD78F0730_STOP_BIT_2_BIT 0x02
+#define UPD78F0730_STOP_BIT_MASK 0x02
+
+/* Parity modes in UPD78F0730_CMD_LINE_CONTROL command */
+#define UPD78F0730_PARITY_NONE 0x00
+#define UPD78F0730_PARITY_EVEN 0x04
+#define UPD78F0730_PARITY_ODD 0x08
+#define UPD78F0730_PARITY_MASK 0x0C
+
+/* Flow control modes in UPD78F0730_CMD_LINE_CONTROL command */
+#define UPD78F0730_FLOW_CONTROL_NONE 0x00
+#define UPD78F0730_FLOW_CONTROL_HW 0x10
+#define UPD78F0730_FLOW_CONTROL_SW 0x20
+#define UPD78F0730_FLOW_CONTROL_MASK 0x30
+
+/* Control signal bits in UPD78F0730_CMD_SET_DTR_RTS command */
+#define UPD78F0730_RTS 0x01
+#define UPD78F0730_DTR 0x02
+#define UPD78F0730_BREAK 0x04
+
+/* Port modes in UPD78F0730_CMD_OPEN_CLOSE command */
+#define UPD78F0730_PORT_CLOSE 0x00
+#define UPD78F0730_PORT_OPEN 0x01
+
+/* Error character substitution modes in UPD78F0730_CMD_SET_ERR_CHR command */
+#define UPD78F0730_ERR_CHR_DISABLED 0x00
+#define UPD78F0730_ERR_CHR_ENABLED 0x01
+
+/*
+ * Declaration of command structures
+ */
+
+/* UPD78F0730_CMD_LINE_CONTROL command */
+struct upd78f0730_line_control {
+ u8 opcode;
+ __le32 baud_rate;
+ u8 params;
+} __packed;
+
+/* UPD78F0730_CMD_SET_DTR_RTS command */
+struct upd78f0730_set_dtr_rts {
+ u8 opcode;
+ u8 params;
+};
+
+/* UPD78F0730_CMD_SET_XON_OFF_CHR command */
+struct upd78f0730_set_xon_xoff_chr {
+ u8 opcode;
+ u8 xon;
+ u8 xoff;
+};
+
+/* UPD78F0730_CMD_OPEN_CLOSE command */
+struct upd78f0730_open_close {
+ u8 opcode;
+ u8 state;
+};
+
+/* UPD78F0730_CMD_SET_ERR_CHR command */
+struct upd78f0730_set_err_chr {
+ u8 opcode;
+ u8 state;
+ u8 err_char;
+};
+
+static int upd78f0730_send_ctl(struct usb_serial_port *port,
+ const void *data, int size)
+{
+ struct usb_device *usbdev = port->serial->dev;
+ void *buf;
+ int res;
+
+ if (size <= 0 || !data)
+ return -EINVAL;
+
+ buf = kmemdup(data, size, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ res = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), 0x00,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
+ 0x0000, 0x0000, buf, size, USB_CTRL_SET_TIMEOUT);
+
+ kfree(buf);
+
+ if (res != size) {
+ struct device *dev = &port->dev;
+
+ dev_err(dev, "failed to send control request %02x: %d\n",
+ *(u8 *)data, res);
+ /* The maximum expected length of a transfer is 6 bytes */
+ if (res >= 0)
+ res = -EIO;
+
+ return res;
+ }
+
+ return 0;
+}
+
+static int upd78f0730_port_probe(struct usb_serial_port *port)
+{
+ struct upd78f0730_port_private *private;
+
+ private = kzalloc(sizeof(*private), GFP_KERNEL);
+ if (!private)
+ return -ENOMEM;
+
+ mutex_init(&private->lock);
+ usb_set_serial_port_data(port, private);
+
+ return 0;
+}
+
+static int upd78f0730_port_remove(struct usb_serial_port *port)
+{
+ struct upd78f0730_port_private *private;
+
+ private = usb_get_serial_port_data(port);
+ mutex_destroy(&private->lock);
+ kfree(private);
+
+ return 0;
+}
+
+static int upd78f0730_tiocmget(struct tty_struct *tty)
+{
+ struct device *dev = tty->dev;
+ struct upd78f0730_port_private *private;
+ struct usb_serial_port *port = tty->driver_data;
+ int signals;
+ int res;
+
+ private = usb_get_serial_port_data(port);
+
+ mutex_lock(&private->lock);
+ signals = private->line_signals;
+ mutex_unlock(&private->lock);
+
+ res = ((signals & UPD78F0730_DTR) ? TIOCM_DTR : 0) |
+ ((signals & UPD78F0730_RTS) ? TIOCM_RTS : 0);
+
+ dev_dbg(dev, "%s - res = %x\n", __func__, res);
+
+ return res;
+}
+
+static int upd78f0730_tiocmset(struct tty_struct *tty,
+ unsigned int set, unsigned int clear)
+{
+ struct device *dev = tty->dev;
+ struct usb_serial_port *port = tty->driver_data;
+ struct upd78f0730_port_private *private;
+ struct upd78f0730_set_dtr_rts request;
+ int res;
+
+ private = usb_get_serial_port_data(port);
+
+ mutex_lock(&private->lock);
+ if (set & TIOCM_DTR) {
+ private->line_signals |= UPD78F0730_DTR;
+ dev_dbg(dev, "%s - set DTR\n", __func__);
+ }
+ if (set & TIOCM_RTS) {
+ private->line_signals |= UPD78F0730_RTS;
+ dev_dbg(dev, "%s - set RTS\n", __func__);
+ }
+ if (clear & TIOCM_DTR) {
+ private->line_signals &= ~UPD78F0730_DTR;
+ dev_dbg(dev, "%s - clear DTR\n", __func__);
+ }
+ if (clear & TIOCM_RTS) {
+ private->line_signals &= ~UPD78F0730_RTS;
+ dev_dbg(dev, "%s - clear RTS\n", __func__);
+ }
+ request.opcode = UPD78F0730_CMD_SET_DTR_RTS;
+ request.params = private->line_signals;
+
+ res = upd78f0730_send_ctl(port, &request, sizeof(request));
+ mutex_unlock(&private->lock);
+
+ return res;
+}
+
+static void upd78f0730_break_ctl(struct tty_struct *tty, int break_state)
+{
+ struct device *dev = tty->dev;
+ struct upd78f0730_port_private *private;
+ struct usb_serial_port *port = tty->driver_data;
+ struct upd78f0730_set_dtr_rts request;
+
+ private = usb_get_serial_port_data(port);
+
+ mutex_lock(&private->lock);
+ if (break_state) {
+ private->line_signals |= UPD78F0730_BREAK;
+ dev_dbg(dev, "%s - set BREAK\n", __func__);
+ } else {
+ private->line_signals &= ~UPD78F0730_BREAK;
+ dev_dbg(dev, "%s - clear BREAK\n", __func__);
+ }
+ request.opcode = UPD78F0730_CMD_SET_DTR_RTS;
+ request.params = private->line_signals;
+
+ upd78f0730_send_ctl(port, &request, sizeof(request));
+ mutex_unlock(&private->lock);
+}
+
+static void upd78f0730_dtr_rts(struct usb_serial_port *port, int on)
+{
+ struct tty_struct *tty = port->port.tty;
+ unsigned int set = 0;
+ unsigned int clear = 0;
+
+ if (on)
+ set = TIOCM_DTR | TIOCM_RTS;
+ else
+ clear = TIOCM_DTR | TIOCM_RTS;
+
+ upd78f0730_tiocmset(tty, set, clear);
+}
+
+static speed_t upd78f0730_get_baud_rate(struct tty_struct *tty)
+{
+ const speed_t baud_rate = tty_get_baud_rate(tty);
+ const speed_t supported[] = {
+ 0, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 153600
+ };
+ int i;
+
+ for (i = ARRAY_SIZE(supported) - 1; i >= 0; i--) {
+ if (baud_rate == supported[i])
+ return baud_rate;
+ }
+
+ /* If the baud rate is not supported, switch to the default one */
+ tty_encode_baud_rate(tty, 9600, 9600);
+
+ return tty_get_baud_rate(tty);
+}
+
+static void upd78f0730_set_termios(struct tty_struct *tty,
+ struct usb_serial_port *port,
+ struct ktermios *old_termios)
+{
+ struct device *dev = &port->dev;
+ struct upd78f0730_line_control request;
+ speed_t baud_rate;
+
+ if (old_termios && !tty_termios_hw_change(&tty->termios, old_termios))
+ return;
+
+ if (C_BAUD(tty) == B0)
+ upd78f0730_dtr_rts(port, 0);
+ else if (old_termios && (old_termios->c_cflag & CBAUD) == B0)
+ upd78f0730_dtr_rts(port, 1);
+
+ baud_rate = upd78f0730_get_baud_rate(tty);
+ request.opcode = UPD78F0730_CMD_LINE_CONTROL;
+ request.baud_rate = cpu_to_le32(baud_rate);
+ request.params = 0;
+ dev_dbg(dev, "%s - baud rate = %d\n", __func__, baud_rate);
+
+ switch (C_CSIZE(tty)) {
+ case CS7:
+ request.params |= UPD78F0730_DATA_SIZE_7_BITS;
+ dev_dbg(dev, "%s - 7 data bits\n", __func__);
+ break;
+ default:
+ tty->termios.c_cflag &= ~CSIZE;
+ tty->termios.c_cflag |= CS8;
+ dev_warn(dev, "data size is not supported, using 8 bits\n");
+ /* fall through */
+ case CS8:
+ request.params |= UPD78F0730_DATA_SIZE_8_BITS;
+ dev_dbg(dev, "%s - 8 data bits\n", __func__);
+ break;
+ }
+
+ if (C_PARENB(tty)) {
+ if (C_PARODD(tty)) {
+ request.params |= UPD78F0730_PARITY_ODD;
+ dev_dbg(dev, "%s - odd parity\n", __func__);
+ } else {
+ request.params |= UPD78F0730_PARITY_EVEN;
+ dev_dbg(dev, "%s - even parity\n", __func__);
+ }
+
+ if (C_CMSPAR(tty)) {
+ tty->termios.c_cflag &= ~CMSPAR;
+ dev_warn(dev, "MARK/SPACE parity is not supported\n");
+ }
+ } else {
+ request.params |= UPD78F0730_PARITY_NONE;
+ dev_dbg(dev, "%s - no parity\n", __func__);
+ }
+
+ if (C_CSTOPB(tty)) {
+ request.params |= UPD78F0730_STOP_BIT_2_BIT;
+ dev_dbg(dev, "%s - 2 stop bits\n", __func__);
+ } else {
+ request.params |= UPD78F0730_STOP_BIT_1_BIT;
+ dev_dbg(dev, "%s - 1 stop bit\n", __func__);
+ }
+
+ if (C_CRTSCTS(tty)) {
+ tty->termios.c_cflag &= ~CRTSCTS;
+ dev_warn(dev, "RTSCTS flow control is not supported\n");
+ }
+ if (I_IXOFF(tty) || I_IXON(tty)) {
+ tty->termios.c_iflag &= ~(IXOFF | IXON);
+ dev_warn(dev, "XON/XOFF flow control is not supported\n");
+ }
+ request.params |= UPD78F0730_FLOW_CONTROL_NONE;
+ dev_dbg(dev, "%s - no flow control\n", __func__);
+
+ upd78f0730_send_ctl(port, &request, sizeof(request));
+}
+
+static int upd78f0730_open(struct tty_struct *tty, struct usb_serial_port *port)
+{
+ struct upd78f0730_open_close request = {
+ .opcode = UPD78F0730_CMD_OPEN_CLOSE,
+ .state = UPD78F0730_PORT_OPEN
+ };
+ int res;
+
+ res = upd78f0730_send_ctl(port, &request, sizeof(request));
+ if (res)
+ return res;
+
+ if (tty)
+ upd78f0730_set_termios(tty, port, NULL);
+
+ return usb_serial_generic_open(tty, port);
+}
+
+static void upd78f0730_close(struct usb_serial_port *port)
+{
+ struct upd78f0730_open_close request = {
+ .opcode = UPD78F0730_CMD_OPEN_CLOSE,
+ .state = UPD78F0730_PORT_CLOSE
+ };
+
+ usb_serial_generic_close(port);
+ upd78f0730_send_ctl(port, &request, sizeof(request));
+}
+
+static struct usb_serial_driver upd78f0730_device = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "upd78f0730",
+ },
+ .id_table = id_table,
+ .num_ports = 1,
+ .port_probe = upd78f0730_port_probe,
+ .port_remove = upd78f0730_port_remove,
+ .open = upd78f0730_open,
+ .close = upd78f0730_close,
+ .set_termios = upd78f0730_set_termios,
+ .tiocmget = upd78f0730_tiocmget,
+ .tiocmset = upd78f0730_tiocmset,
+ .dtr_rts = upd78f0730_dtr_rts,
+ .break_ctl = upd78f0730_break_ctl,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+ &upd78f0730_device,
+ NULL
+};
+
+module_usb_serial_driver(serial_drivers, id_table);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/serial/usb_wwan.c b/drivers/usb/serial/usb_wwan.c
index 3dfdfc81254b..59bfcb3da116 100644
--- a/drivers/usb/serial/usb_wwan.c
+++ b/drivers/usb/serial/usb_wwan.c
@@ -140,9 +140,6 @@ static int get_serial_info(struct usb_serial_port *port,
{
struct serial_struct tmp;
- if (!retinfo)
- return -EFAULT;
-
memset(&tmp, 0, sizeof(tmp));
tmp.line = port->minor;
tmp.port = port->port_number;
diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c
index d3ea90bef84d..5ab65eb1dacc 100644
--- a/drivers/usb/serial/whiteheat.c
+++ b/drivers/usb/serial/whiteheat.c
@@ -487,7 +487,6 @@ static int whiteheat_ioctl(struct tty_struct *tty,
serstruct.type = PORT_16654;
serstruct.line = port->minor;
serstruct.port = port->port_number;
- serstruct.flags = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ;
serstruct.xmit_fifo_size = kfifo_size(&port->write_fifo);
serstruct.custom_divisor = 0;
serstruct.baud_base = 460800;
diff --git a/drivers/usb/storage/ene_ub6250.c b/drivers/usb/storage/ene_ub6250.c
index 02bdaa912164..369f3c24815a 100644
--- a/drivers/usb/storage/ene_ub6250.c
+++ b/drivers/usb/storage/ene_ub6250.c
@@ -1364,7 +1364,6 @@ static int ms_lib_read_extra(struct us_data *us, u32 PhyBlock,
static int ms_libsearch_block_from_physical(struct us_data *us, u16 phyblk)
{
- u16 Newblk;
u16 blk;
struct ms_lib_type_extdat extdat; /* need check */
struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra;
@@ -1377,7 +1376,6 @@ static int ms_libsearch_block_from_physical(struct us_data *us, u16 phyblk)
if ((blk & MS_PHYSICAL_BLOCKS_PER_SEGMENT_MASK) == 0)
blk -= MS_PHYSICAL_BLOCKS_PER_SEGMENT;
- Newblk = info->MS_Lib.Phy2LogMap[blk];
if (info->MS_Lib.Phy2LogMap[blk] == MS_LB_NOT_USED_ERASED) {
return blk;
} else if (info->MS_Lib.Phy2LogMap[blk] == MS_LB_NOT_USED) {
diff --git a/drivers/usb/storage/sddr09.c b/drivers/usb/storage/sddr09.c
index 3aeaa536c44f..44f8ffccd031 100644
--- a/drivers/usb/storage/sddr09.c
+++ b/drivers/usb/storage/sddr09.c
@@ -870,13 +870,12 @@ sddr09_write_lba(struct us_data *us, unsigned int lba,
unsigned int pagelen;
unsigned char *bptr, *cptr, *xptr;
unsigned char ecc[3];
- int i, result, isnew;
+ int i, result;
lbap = ((lba % 1000) << 1) | 0x1000;
if (parity[MSB_of(lbap) ^ LSB_of(lbap)])
lbap ^= 1;
pba = info->lba_to_pba[lba];
- isnew = 0;
if (pba == UNDEF) {
pba = sddr09_find_unused_pba(info, lba);
@@ -887,7 +886,6 @@ sddr09_write_lba(struct us_data *us, unsigned int lba,
}
info->pba_to_lba[pba] = lba;
info->lba_to_pba[lba] = pba;
- isnew = 1;
}
if (pba == 1) {
diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h
index af3c7eecff91..9129f6cb8230 100644
--- a/drivers/usb/storage/unusual_devs.h
+++ b/drivers/usb/storage/unusual_devs.h
@@ -2071,6 +2071,20 @@ UNUSUAL_DEV( 0x1370, 0x6828, 0x0110, 0x0110,
USB_SC_DEVICE, USB_PR_DEVICE, NULL,
US_FL_IGNORE_RESIDUE ),
+/*
+ * Reported by Tobias Jakobi <tjakobi@math.uni-bielefeld.de>
+ * The INIC-3619 bridge is used in the StarTech SLSODDU33B
+ * SATA-USB enclosure for slimline optical drives.
+ *
+ * The quirk enables MakeMKV to properly exchange keys with
+ * an installed BD drive.
+ */
+UNUSUAL_DEV( 0x13fd, 0x3609, 0x0209, 0x0209,
+ "Initio Corporation",
+ "INIC-3619",
+ USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+ US_FL_IGNORE_RESIDUE ),
+
/* Reported by Qinglin Ye <yestyle@gmail.com> */
UNUSUAL_DEV( 0x13fe, 0x3600, 0x0100, 0x0100,
"Kingston",
@@ -2109,6 +2123,13 @@ UNUSUAL_DEV( 0x152d, 0x2566, 0x0114, 0x0114,
USB_SC_DEVICE, USB_PR_DEVICE, NULL,
US_FL_BROKEN_FUA ),
+/* Reported-by George Cherian <george.cherian@cavium.com> */
+UNUSUAL_DEV(0x152d, 0x9561, 0x0000, 0x9999,
+ "JMicron",
+ "JMS56x",
+ USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+ US_FL_NO_REPORT_OPCODES),
+
/*
* Entrega Technologies U1-SC25 (later Xircom PortGear PGSCSI)
* and Mac USB Dock USB-SCSI */
diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c
index 2cba13a532cd..615bea08ec0a 100644
--- a/drivers/usb/storage/usb.c
+++ b/drivers/usb/storage/usb.c
@@ -52,7 +52,6 @@
#include <linux/sched.h>
#include <linux/errno.h>
-#include <linux/freezer.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/kthread.h>
diff --git a/drivers/usb/usbip/usbip_common.c b/drivers/usb/usbip/usbip_common.c
index 8b232290be6b..cab2b71a80d0 100644
--- a/drivers/usb/usbip/usbip_common.c
+++ b/drivers/usb/usbip/usbip_common.c
@@ -327,13 +327,11 @@ EXPORT_SYMBOL_GPL(usbip_dump_header);
int usbip_recv(struct socket *sock, void *buf, int size)
{
int result;
- struct msghdr msg;
- struct kvec iov;
+ struct kvec iov = {.iov_base = buf, .iov_len = size};
+ struct msghdr msg = {.msg_flags = MSG_NOSIGNAL};
int total = 0;
- /* for blocks of if (usbip_dbg_flag_xmit) */
- char *bp = buf;
- int osize = size;
+ iov_iter_kvec(&msg.msg_iter, READ|ITER_KVEC, &iov, 1, size);
usbip_dbg_xmit("enter\n");
@@ -344,26 +342,18 @@ int usbip_recv(struct socket *sock, void *buf, int size)
}
do {
+ int sz = msg_data_left(&msg);
sock->sk->sk_allocation = GFP_NOIO;
- iov.iov_base = buf;
- iov.iov_len = size;
- msg.msg_name = NULL;
- msg.msg_namelen = 0;
- msg.msg_control = NULL;
- msg.msg_controllen = 0;
- msg.msg_flags = MSG_NOSIGNAL;
-
- result = kernel_recvmsg(sock, &msg, &iov, 1, size, MSG_WAITALL);
+
+ result = sock_recvmsg(sock, &msg, MSG_WAITALL);
if (result <= 0) {
pr_debug("receive sock %p buf %p size %u ret %d total %d\n",
- sock, buf, size, result, total);
+ sock, buf + total, sz, result, total);
goto err;
}
- size -= result;
- buf += result;
total += result;
- } while (size > 0);
+ } while (msg_data_left(&msg));
if (usbip_dbg_flag_xmit) {
if (!in_interrupt())
@@ -372,9 +362,9 @@ int usbip_recv(struct socket *sock, void *buf, int size)
pr_debug("interrupt :");
pr_debug("receiving....\n");
- usbip_dump_buffer(bp, osize);
- pr_debug("received, osize %d ret %d size %d total %d\n",
- osize, result, size, total);
+ usbip_dump_buffer(buf, size);
+ pr_debug("received, osize %d ret %d size %zd total %d\n",
+ size, result, msg_data_left(&msg), total);
}
return total;
@@ -707,7 +697,7 @@ void usbip_pad_iso(struct usbip_device *ud, struct urb *urb)
return;
/*
- * loop over all packets from last to first (to prevent overwritting
+ * loop over all packets from last to first (to prevent overwriting
* memory when padding) and move them into the proper place
*/
for (i = np-1; i > 0; i--) {
diff --git a/drivers/usb/usbip/usbip_common.h b/drivers/usb/usbip/usbip_common.h
index 9f490375ac92..f8573a52e41a 100644
--- a/drivers/usb/usbip/usbip_common.h
+++ b/drivers/usb/usbip/usbip_common.h
@@ -31,6 +31,7 @@
#include <linux/types.h>
#include <linux/usb.h>
#include <linux/wait.h>
+#include <linux/sched/task.h>
#include <uapi/linux/usbip.h>
#define USBIP_VERSION "1.0.0"
diff --git a/drivers/usb/usbip/vhci_hcd.c b/drivers/usb/usbip/vhci_hcd.c
index 03eccf29ace0..e4cb9f0625e8 100644
--- a/drivers/usb/usbip/vhci_hcd.c
+++ b/drivers/usb/usbip/vhci_hcd.c
@@ -313,6 +313,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
default:
break;
}
+ break;
default:
usbip_dbg_vhci_rh(" ClearPortFeature: default %x\n",
wValue);
@@ -460,13 +461,14 @@ static void vhci_tx_urb(struct urb *urb)
{
struct vhci_device *vdev = get_vdev(urb->dev);
struct vhci_priv *priv;
- struct vhci_hcd *vhci = vdev_to_vhci(vdev);
+ struct vhci_hcd *vhci;
unsigned long flags;
if (!vdev) {
pr_err("could not get virtual device");
return;
}
+ vhci = vdev_to_vhci(vdev);
priv = kzalloc(sizeof(struct vhci_priv), GFP_ATOMIC);
if (!priv) {
diff --git a/drivers/usb/usbip/vhci_sysfs.c b/drivers/usb/usbip/vhci_sysfs.c
index c404017c1b5a..b96e5b189269 100644
--- a/drivers/usb/usbip/vhci_sysfs.c
+++ b/drivers/usb/usbip/vhci_sysfs.c
@@ -361,6 +361,7 @@ static void set_status_attr(int id)
status->attr.attr.name = status->name;
status->attr.attr.mode = S_IRUGO;
status->attr.show = status_show;
+ sysfs_attr_init(&status->attr.attr);
}
static int init_status_attrs(void)
diff --git a/drivers/usb/usbip/vudc_dev.c b/drivers/usb/usbip/vudc_dev.c
index 7091848df6c8..968471b62cbc 100644
--- a/drivers/usb/usbip/vudc_dev.c
+++ b/drivers/usb/usbip/vudc_dev.c
@@ -242,10 +242,10 @@ static const struct usb_gadget_ops vgadget_ops = {
static int vep_enable(struct usb_ep *_ep,
const struct usb_endpoint_descriptor *desc)
{
- struct vep *ep;
- struct vudc *udc;
- unsigned maxp;
- unsigned long flags;
+ struct vep *ep;
+ struct vudc *udc;
+ unsigned int maxp;
+ unsigned long flags;
ep = to_vep(_ep);
udc = ep_to_vudc(ep);
@@ -259,7 +259,7 @@ static int vep_enable(struct usb_ep *_ep,
spin_lock_irqsave(&udc->lock, flags);
- maxp = usb_endpoint_maxp(desc) & 0x7ff;
+ maxp = usb_endpoint_maxp(desc);
_ep->maxpacket = maxp;
ep->desc = desc;
ep->type = usb_endpoint_type(desc);
@@ -549,30 +549,34 @@ static int init_vudc_hw(struct vudc *udc)
sprintf(ep->name, "ep%d%s", num,
i ? (is_out ? "out" : "in") : "");
ep->ep.name = ep->name;
+
+ ep->ep.ops = &vep_ops;
+
+ usb_ep_set_maxpacket_limit(&ep->ep, ~0);
+ ep->ep.max_streams = 16;
+ ep->gadget = &udc->gadget;
+ INIT_LIST_HEAD(&ep->req_queue);
+
if (i == 0) {
+ /* ep0 */
ep->ep.caps.type_control = true;
ep->ep.caps.dir_out = true;
ep->ep.caps.dir_in = true;
+
+ udc->gadget.ep0 = &ep->ep;
} else {
+ /* All other eps */
ep->ep.caps.type_iso = true;
ep->ep.caps.type_int = true;
ep->ep.caps.type_bulk = true;
- }
- if (is_out)
- ep->ep.caps.dir_out = true;
- else
- ep->ep.caps.dir_in = true;
+ if (is_out)
+ ep->ep.caps.dir_out = true;
+ else
+ ep->ep.caps.dir_in = true;
- ep->ep.ops = &vep_ops;
- list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list);
- ep->halted = ep->wedged = ep->already_seen =
- ep->setup_stage = 0;
- usb_ep_set_maxpacket_limit(&ep->ep, ~0);
- ep->ep.max_streams = 16;
- ep->gadget = &udc->gadget;
- ep->desc = NULL;
- INIT_LIST_HEAD(&ep->req_queue);
+ list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list);
+ }
}
spin_lock_init(&udc->lock);
@@ -589,9 +593,6 @@ static int init_vudc_hw(struct vudc *udc)
ud->eh_ops.reset = vudc_device_reset;
ud->eh_ops.unusable = vudc_device_unusable;
- udc->gadget.ep0 = &udc->ep[0].ep;
- list_del_init(&udc->ep[0].ep.ep_list);
-
v_init_timer(udc);
return 0;
diff --git a/drivers/usb/usbip/vudc_transfer.c b/drivers/usb/usbip/vudc_transfer.c
index aba6bd478045..4cfd475ee865 100644
--- a/drivers/usb/usbip/vudc_transfer.c
+++ b/drivers/usb/usbip/vudc_transfer.c
@@ -73,8 +73,8 @@ static int handle_control_request(struct vudc *udc, struct urb *urb,
{
struct vep *ep2;
int ret_val = 1;
- unsigned w_index;
- unsigned w_value;
+ unsigned int w_index;
+ unsigned int w_value;
w_index = le16_to_cpu(setup->wIndex);
w_value = le16_to_cpu(setup->wValue);
@@ -200,7 +200,7 @@ static int transfer(struct vudc *udc,
top:
/* if there's no request queued, the device is NAKing; return */
list_for_each_entry(req, &ep->req_queue, req_entry) {
- unsigned host_len, dev_len, len;
+ unsigned int host_len, dev_len, len;
void *ubuf_pos, *rbuf_pos;
int is_short, to_host;
int rescan = 0;
@@ -339,6 +339,8 @@ static void v_timer(unsigned long _vudc)
total = timer->frame_limit;
}
+ /* We have to clear ep0 flags separately as it's not on the list */
+ udc->ep[0].already_seen = 0;
list_for_each_entry(_ep, &udc->gadget.ep_list, ep_list) {
ep = to_vep(_ep);
ep->already_seen = 0;
diff --git a/drivers/usb/wusbcore/crypto.c b/drivers/usb/wusbcore/crypto.c
index 79451f7ef1b7..062c205f0046 100644
--- a/drivers/usb/wusbcore/crypto.c
+++ b/drivers/usb/wusbcore/crypto.c
@@ -216,7 +216,6 @@ static int wusb_ccm_mac(struct crypto_skcipher *tfm_cbc,
struct scatterlist sg[4], sg_dst;
void *dst_buf;
size_t dst_size;
- const u8 bzero[16] = { 0 };
u8 iv[crypto_skcipher_ivsize(tfm_cbc)];
size_t zero_padding;
@@ -261,7 +260,7 @@ static int wusb_ccm_mac(struct crypto_skcipher *tfm_cbc,
sg_set_buf(&sg[1], &scratch->b1, sizeof(scratch->b1));
sg_set_buf(&sg[2], b, blen);
/* 0 if well behaved :) */
- sg_set_buf(&sg[3], bzero, zero_padding);
+ 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);
diff --git a/drivers/usb/wusbcore/dev-sysfs.c b/drivers/usb/wusbcore/dev-sysfs.c
index 415b14002a61..d4de56b93d68 100644
--- a/drivers/usb/wusbcore/dev-sysfs.c
+++ b/drivers/usb/wusbcore/dev-sysfs.c
@@ -53,7 +53,7 @@ static ssize_t wusb_disconnect_store(struct device *dev,
wusbhc_put(wusbhc);
return size;
}
-static DEVICE_ATTR(wusb_disconnect, 0200, NULL, wusb_disconnect_store);
+static DEVICE_ATTR_WO(wusb_disconnect);
static ssize_t wusb_cdid_show(struct device *dev,
struct device_attribute *attr, char *buf)
@@ -69,7 +69,7 @@ static ssize_t wusb_cdid_show(struct device *dev,
wusb_dev_put(wusb_dev);
return result + 1;
}
-static DEVICE_ATTR(wusb_cdid, 0444, wusb_cdid_show, NULL);
+static DEVICE_ATTR_RO(wusb_cdid);
static ssize_t wusb_ck_store(struct device *dev,
struct device_attribute *attr,
@@ -105,7 +105,7 @@ static ssize_t wusb_ck_store(struct device *dev,
wusbhc_put(wusbhc);
return result < 0 ? result : size;
}
-static DEVICE_ATTR(wusb_ck, 0200, NULL, wusb_ck_store);
+static DEVICE_ATTR_WO(wusb_ck);
static struct attribute *wusb_dev_attrs[] = {
&dev_attr_wusb_disconnect.attr,
diff --git a/drivers/usb/wusbcore/security.c b/drivers/usb/wusbcore/security.c
index 8c9421b69da0..170f2c38de9b 100644
--- a/drivers/usb/wusbcore/security.c
+++ b/drivers/usb/wusbcore/security.c
@@ -240,6 +240,7 @@ int wusb_dev_sec_add(struct wusbhc *wusbhc,
if (new_secd == NULL) {
dev_err(dev,
"Can't allocate space for security descriptors\n");
+ result = -ENOMEM;
goto out;
}
secd = new_secd;
diff --git a/drivers/usb/wusbcore/wa-hc.c b/drivers/usb/wusbcore/wa-hc.c
index 252c7bd9218a..d01496fd27fe 100644
--- a/drivers/usb/wusbcore/wa-hc.c
+++ b/drivers/usb/wusbcore/wa-hc.c
@@ -39,6 +39,9 @@ int wa_create(struct wahc *wa, struct usb_interface *iface,
int result;
struct device *dev = &iface->dev;
+ if (iface->cur_altsetting->desc.bNumEndpoints < 3)
+ return -ENODEV;
+
result = wa_rpipes_create(wa);
if (result < 0)
goto error_rpipes_create;
diff --git a/drivers/usb/wusbcore/wa-nep.c b/drivers/usb/wusbcore/wa-nep.c
index ed4622279c63..e3819fc182b0 100644
--- a/drivers/usb/wusbcore/wa-nep.c
+++ b/drivers/usb/wusbcore/wa-nep.c
@@ -198,6 +198,7 @@ static int wa_nep_queue(struct wahc *wa, size_t size)
if (nw == NULL) {
if (printk_ratelimit())
dev_err(dev, "No memory to queue notification\n");
+ result = -ENOMEM;
goto out;
}
INIT_WORK(&nw->work, wa_notif_dispatch);
diff --git a/drivers/usb/wusbcore/wa-xfer.c b/drivers/usb/wusbcore/wa-xfer.c
index 167fcc71f5f6..e70322b1dd02 100644
--- a/drivers/usb/wusbcore/wa-xfer.c
+++ b/drivers/usb/wusbcore/wa-xfer.c
@@ -1203,6 +1203,7 @@ static int __wa_xfer_setup_segs(struct wa_xfer *xfer, size_t xfer_hdr_size)
sizeof(struct wa_xfer_packet_info_hwaiso) +
(seg_isoc_frame_count * sizeof(__le16));
}
+ result = -ENOMEM;
seg = xfer->seg[cnt] = kmalloc(alloc_size + iso_pkt_descr_size,
GFP_ATOMIC);
if (seg == NULL)
diff --git a/drivers/usb/wusbcore/wusbhc.c b/drivers/usb/wusbcore/wusbhc.c
index 94f401ab859f..a273a91cf667 100644
--- a/drivers/usb/wusbcore/wusbhc.c
+++ b/drivers/usb/wusbcore/wusbhc.c
@@ -84,8 +84,7 @@ static ssize_t wusb_trust_timeout_store(struct device *dev,
out:
return result < 0 ? result : size;
}
-static DEVICE_ATTR(wusb_trust_timeout, 0644, wusb_trust_timeout_show,
- wusb_trust_timeout_store);
+static DEVICE_ATTR_RW(wusb_trust_timeout);
/*
* Show the current WUSB CHID.
@@ -145,7 +144,7 @@ static ssize_t wusb_chid_store(struct device *dev,
result = wusbhc_chid_set(wusbhc, &chid);
return result < 0 ? result : size;
}
-static DEVICE_ATTR(wusb_chid, 0644, wusb_chid_show, wusb_chid_store);
+static DEVICE_ATTR_RW(wusb_chid);
static ssize_t wusb_phy_rate_show(struct device *dev,
@@ -174,8 +173,7 @@ static ssize_t wusb_phy_rate_store(struct device *dev,
wusbhc->phy_rate = phy_rate;
return size;
}
-static DEVICE_ATTR(wusb_phy_rate, 0644, wusb_phy_rate_show,
- wusb_phy_rate_store);
+static DEVICE_ATTR_RW(wusb_phy_rate);
static ssize_t wusb_dnts_show(struct device *dev,
struct device_attribute *attr,
@@ -205,7 +203,7 @@ static ssize_t wusb_dnts_store(struct device *dev,
return size;
}
-static DEVICE_ATTR(wusb_dnts, 0644, wusb_dnts_show, wusb_dnts_store);
+static DEVICE_ATTR_RW(wusb_dnts);
static ssize_t wusb_retry_count_show(struct device *dev,
struct device_attribute *attr,
@@ -234,8 +232,7 @@ static ssize_t wusb_retry_count_store(struct device *dev,
return size;
}
-static DEVICE_ATTR(wusb_retry_count, 0644, wusb_retry_count_show,
- wusb_retry_count_store);
+static DEVICE_ATTR_RW(wusb_retry_count);
/* Group all the WUSBHC attributes */
static struct attribute *wusbhc_attrs[] = {