summaryrefslogtreecommitdiff
path: root/drivers/usb
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/Makefile1
-rw-r--r--drivers/usb/atm/cxacru.c16
-rw-r--r--drivers/usb/atm/ueagle-atm.c2
-rw-r--r--drivers/usb/cdns3/cdns3-pci-wrap.c5
-rw-r--r--drivers/usb/cdns3/cdns3-ti.c15
-rw-r--r--drivers/usb/cdns3/cdns3-trace.h26
-rw-r--r--drivers/usb/cdns3/cdnsp-gadget.h3
-rw-r--r--drivers/usb/cdns3/cdnsp-pci.c31
-rw-r--r--drivers/usb/cdns3/cdnsp-ring.c36
-rw-r--r--drivers/usb/cdns3/cdnsp-trace.h10
-rw-r--r--drivers/usb/cdns3/core.h1
-rw-r--r--drivers/usb/cdns3/drd.c10
-rw-r--r--drivers/usb/cdns3/drd.h3
-rw-r--r--drivers/usb/cdns3/host.c4
-rw-r--r--drivers/usb/chipidea/ci_hdrc_imx.c23
-rw-r--r--drivers/usb/chipidea/ci_hdrc_msm.c1
-rw-r--r--drivers/usb/chipidea/ci_hdrc_npcm.c10
-rw-r--r--drivers/usb/chipidea/ci_hdrc_tegra.c9
-rw-r--r--drivers/usb/chipidea/trace.h4
-rw-r--r--drivers/usb/chipidea/udc.c8
-rw-r--r--drivers/usb/class/cdc-acm.c7
-rw-r--r--drivers/usb/class/cdc-wdm.c6
-rw-r--r--drivers/usb/class/usbtmc.c3
-rw-r--r--drivers/usb/common/common.c23
-rw-r--r--drivers/usb/common/ulpi.c2
-rw-r--r--drivers/usb/common/usb-otg-fsm.c1
-rw-r--r--drivers/usb/core/Makefile4
-rw-r--r--drivers/usb/core/config.c26
-rw-r--r--drivers/usb/core/driver.c16
-rw-r--r--drivers/usb/core/hcd-pci.c3
-rw-r--r--drivers/usb/core/hcd.c18
-rw-r--r--drivers/usb/core/hub.c17
-rw-r--r--drivers/usb/core/hub.h2
-rw-r--r--drivers/usb/core/of.c7
-rw-r--r--drivers/usb/core/quirks.c3
-rw-r--r--drivers/usb/core/sysfs.c1
-rw-r--r--drivers/usb/core/usb-acpi.c53
-rw-r--r--drivers/usb/core/usb.c1
-rw-r--r--drivers/usb/dwc2/core.c42
-rw-r--r--drivers/usb/dwc2/core.h8
-rw-r--r--drivers/usb/dwc2/core_intr.c26
-rw-r--r--drivers/usb/dwc2/debugfs.c2
-rw-r--r--drivers/usb/dwc2/drd.c9
-rw-r--r--drivers/usb/dwc2/gadget.c32
-rw-r--r--drivers/usb/dwc2/hcd.c10
-rw-r--r--drivers/usb/dwc2/hcd_queue.c52
-rw-r--r--drivers/usb/dwc2/hw.h14
-rw-r--r--drivers/usb/dwc2/params.c75
-rw-r--r--drivers/usb/dwc2/platform.c26
-rw-r--r--drivers/usb/dwc3/core.c435
-rw-r--r--drivers/usb/dwc3/core.h30
-rw-r--r--drivers/usb/dwc3/drd.c15
-rw-r--r--drivers/usb/dwc3/dwc3-exynos.c22
-rw-r--r--drivers/usb/dwc3/dwc3-imx8mp.c113
-rw-r--r--drivers/usb/dwc3/dwc3-octeon.c19
-rw-r--r--drivers/usb/dwc3/dwc3-omap.c4
-rw-r--r--drivers/usb/dwc3/dwc3-pci.c16
-rw-r--r--drivers/usb/dwc3/dwc3-qcom.c271
-rw-r--r--drivers/usb/dwc3/dwc3-rtk.c52
-rw-r--r--drivers/usb/dwc3/dwc3-st.c50
-rw-r--r--drivers/usb/dwc3/dwc3-xilinx.c43
-rw-r--r--drivers/usb/dwc3/ep0.c3
-rw-r--r--drivers/usb/dwc3/gadget.c45
-rw-r--r--drivers/usb/dwc3/host.c4
-rw-r--r--drivers/usb/dwc3/trace.h8
-rw-r--r--drivers/usb/fotg210/Makefile10
-rw-r--r--drivers/usb/fotg210/fotg210-core.c1
-rw-r--r--drivers/usb/fotg210/fotg210-hcd.c2
-rw-r--r--drivers/usb/gadget/composite.c3
-rw-r--r--drivers/usb/gadget/configfs.c15
-rw-r--r--drivers/usb/gadget/function/f_acm.c53
-rw-r--r--drivers/usb/gadget/function/f_ecm.c1
-rw-r--r--drivers/usb/gadget/function/f_eem.c1
-rw-r--r--drivers/usb/gadget/function/f_fs.c71
-rw-r--r--drivers/usb/gadget/function/f_hid.c282
-rw-r--r--drivers/usb/gadget/function/f_loopback.c3
-rw-r--r--drivers/usb/gadget/function/f_mass_storage.c6
-rw-r--r--drivers/usb/gadget/function/f_midi.c3
-rw-r--r--drivers/usb/gadget/function/f_midi2.c43
-rw-r--r--drivers/usb/gadget/function/f_ncm.c1
-rw-r--r--drivers/usb/gadget/function/f_obex.c1
-rw-r--r--drivers/usb/gadget/function/f_phonet.c1
-rw-r--r--drivers/usb/gadget/function/f_printer.c49
-rw-r--r--drivers/usb/gadget/function/f_rndis.c1
-rw-r--r--drivers/usb/gadget/function/f_serial.c1
-rw-r--r--drivers/usb/gadget/function/f_sourcesink.c3
-rw-r--r--drivers/usb/gadget/function/f_subset.c1
-rw-r--r--drivers/usb/gadget/function/f_tcm.c3
-rw-r--r--drivers/usb/gadget/function/f_uac1.c64
-rw-r--r--drivers/usb/gadget/function/f_uac1_legacy.c1
-rw-r--r--drivers/usb/gadget/function/f_uac2.c86
-rw-r--r--drivers/usb/gadget/function/f_uvc.c1
-rw-r--r--drivers/usb/gadget/function/rndis.c6
-rw-r--r--drivers/usb/gadget/function/storage_common.c1
-rw-r--r--drivers/usb/gadget/function/storage_common.h2
-rw-r--r--drivers/usb/gadget/function/u_audio.c84
-rw-r--r--drivers/usb/gadget/function/u_ether.c7
-rw-r--r--drivers/usb/gadget/function/u_serial.c24
-rw-r--r--drivers/usb/gadget/function/u_serial.h4
-rw-r--r--drivers/usb/gadget/function/u_uac1.h12
-rw-r--r--drivers/usb/gadget/function/u_uac2.h15
-rw-r--r--drivers/usb/gadget/function/uvc_configfs.c14
-rw-r--r--drivers/usb/gadget/function/uvc_v4l2.c34
-rw-r--r--drivers/usb/gadget/function/uvc_video.c3
-rw-r--r--drivers/usb/gadget/legacy/dbgp.c1
-rw-r--r--drivers/usb/gadget/legacy/gmidi.c1
-rw-r--r--drivers/usb/gadget/legacy/inode.c2
-rw-r--r--drivers/usb/gadget/legacy/raw_gadget.c1
-rw-r--r--drivers/usb/gadget/legacy/tcm_usb_gadget.c2
-rw-r--r--drivers/usb/gadget/legacy/zero.c1
-rw-r--r--drivers/usb/gadget/u_f.c2
-rw-r--r--drivers/usb/gadget/u_f.h86
-rw-r--r--drivers/usb/gadget/u_os_desc.h2
-rw-r--r--drivers/usb/gadget/udc/aspeed_udc.c6
-rw-r--r--drivers/usb/gadget/udc/atmel_usba_udc.c1
-rw-r--r--drivers/usb/gadget/udc/bdc/bdc.h2
-rw-r--r--drivers/usb/gadget/udc/bdc/bdc_core.c1
-rw-r--r--drivers/usb/gadget/udc/bdc/bdc_ep.c2
-rw-r--r--drivers/usb/gadget/udc/bdc/bdc_udc.c2
-rw-r--r--drivers/usb/gadget/udc/cdns2/cdns2-ep0.c2
-rw-r--r--drivers/usb/gadget/udc/cdns2/cdns2-gadget.c16
-rw-r--r--drivers/usb/gadget/udc/cdns2/cdns2-gadget.h9
-rw-r--r--drivers/usb/gadget/udc/cdns2/cdns2-pci.c9
-rw-r--r--drivers/usb/gadget/udc/cdns2/cdns2-trace.h22
-rw-r--r--drivers/usb/gadget/udc/core.c21
-rw-r--r--drivers/usb/gadget/udc/dummy_hcd.c41
-rw-r--r--drivers/usb/gadget/udc/fsl_udc_core.c2
-rw-r--r--drivers/usb/gadget/udc/goku_udc.c2
-rw-r--r--drivers/usb/gadget/udc/lpc32xx_udc.c67
-rw-r--r--drivers/usb/gadget/udc/mv_u3d_core.c4
-rw-r--r--drivers/usb/gadget/udc/mv_udc_core.c2
-rw-r--r--drivers/usb/gadget/udc/net2272.c2
-rw-r--r--drivers/usb/gadget/udc/net2280.c2
-rw-r--r--drivers/usb/gadget/udc/omap_udc.c12
-rw-r--r--drivers/usb/gadget/udc/pxa25x_udc.c2
-rw-r--r--drivers/usb/gadget/udc/snps_udc_core.c2
-rw-r--r--drivers/usb/gadget/udc/trace.h4
-rw-r--r--drivers/usb/gadget/udc/udc-xilinx.c2
-rw-r--r--drivers/usb/host/Kconfig2
-rw-r--r--drivers/usb/host/ehci-brcm.c1
-rw-r--r--drivers/usb/host/ehci-dbg.c10
-rw-r--r--drivers/usb/host/ehci-exynos.c36
-rw-r--r--drivers/usb/host/ehci-hcd.c2
-rw-r--r--drivers/usb/host/ehci-q.c20
-rw-r--r--drivers/usb/host/ehci.h8
-rw-r--r--drivers/usb/host/isp1362-hcd.c2
-rw-r--r--drivers/usb/host/ohci-da8xx.c2
-rw-r--r--drivers/usb/host/ohci-exynos.c37
-rw-r--r--drivers/usb/host/ohci-hcd.c2
-rw-r--r--drivers/usb/host/ohci-nxp.c18
-rw-r--r--drivers/usb/host/ohci-ppc-of.c4
-rw-r--r--drivers/usb/host/oxu210hp-hcd.c27
-rw-r--r--drivers/usb/host/r8a66597-hcd.c6
-rw-r--r--drivers/usb/host/sl811-hcd.c2
-rw-r--r--drivers/usb/host/xhci-dbgcap.c173
-rw-r--r--drivers/usb/host/xhci-dbgcap.h5
-rw-r--r--drivers/usb/host/xhci-dbgtty.c32
-rw-r--r--drivers/usb/host/xhci-ext-caps.h5
-rw-r--r--drivers/usb/host/xhci-hub.c38
-rw-r--r--drivers/usb/host/xhci-mem.c96
-rw-r--r--drivers/usb/host/xhci-pci-renesas.c49
-rw-r--r--drivers/usb/host/xhci-pci.c152
-rw-r--r--drivers/usb/host/xhci-pci.h19
-rw-r--r--drivers/usb/host/xhci-plat.c9
-rw-r--r--drivers/usb/host/xhci-rcar.c6
-rw-r--r--drivers/usb/host/xhci-ring.c524
-rw-r--r--drivers/usb/host/xhci-trace.h5
-rw-r--r--drivers/usb/host/xhci.c80
-rw-r--r--drivers/usb/host/xhci.h90
-rw-r--r--drivers/usb/image/microtek.c8
-rw-r--r--drivers/usb/isp1760/isp1760-hcd.c2
-rw-r--r--drivers/usb/misc/Kconfig16
-rw-r--r--drivers/usb/misc/Makefile2
-rw-r--r--drivers/usb/misc/appledisplay.c15
-rw-r--r--drivers/usb/misc/brcmstb-usb-pinmap.c1
-rw-r--r--drivers/usb/misc/cypress_cy7c63.c4
-rw-r--r--drivers/usb/misc/ezusb.c1
-rw-r--r--drivers/usb/misc/isight_firmware.c1
-rw-r--r--drivers/usb/misc/ldusb.c1
-rw-r--r--drivers/usb/misc/onboard_usb_dev.c631
-rw-r--r--drivers/usb/misc/onboard_usb_dev.h (renamed from drivers/usb/misc/onboard_usb_hub.h)64
-rw-r--r--drivers/usb/misc/onboard_usb_dev_pdevs.c (renamed from drivers/usb/misc/onboard_usb_hub_pdevs.c)47
-rw-r--r--drivers/usb/misc/onboard_usb_hub.c507
-rw-r--r--drivers/usb/misc/qcom_eud.c2
-rw-r--r--drivers/usb/misc/usb-ljca.c3
-rw-r--r--drivers/usb/misc/usb251xb.c22
-rw-r--r--drivers/usb/misc/usb3503.c2
-rw-r--r--drivers/usb/misc/usb4604.c2
-rw-r--r--drivers/usb/misc/uss720.c40
-rw-r--r--drivers/usb/misc/yurex.c11
-rw-r--r--drivers/usb/mon/mon_bin.c1
-rw-r--r--drivers/usb/mon/mon_main.c1
-rw-r--r--drivers/usb/mon/mon_stat.c1
-rw-r--r--drivers/usb/mon/mon_text.c2
-rw-r--r--drivers/usb/mtu3/mtu3_trace.h8
-rw-r--r--drivers/usb/musb/da8xx.c28
-rw-r--r--drivers/usb/musb/mediatek.c27
-rw-r--r--drivers/usb/musb/mpfs.c162
-rw-r--r--drivers/usb/musb/musb_gadget.c9
-rw-r--r--drivers/usb/musb/musb_trace.h12
-rw-r--r--drivers/usb/musb/musb_virthub.c2
-rw-r--r--drivers/usb/phy/phy-am335x-control.c1
-rw-r--r--drivers/usb/phy/phy-am335x.c1
-rw-r--r--drivers/usb/phy/phy-fsl-usb.c3
-rw-r--r--drivers/usb/phy/phy-generic.c1
-rw-r--r--drivers/usb/phy/phy-gpio-vbus-usb.c1
-rw-r--r--drivers/usb/phy/phy-mxs-usb.c132
-rw-r--r--drivers/usb/renesas_usbhs/common.c41
-rw-r--r--drivers/usb/renesas_usbhs/rza.h1
-rw-r--r--drivers/usb/renesas_usbhs/rza2.c13
-rw-r--r--drivers/usb/roles/class.c7
-rw-r--r--drivers/usb/serial/aircable.c3
-rw-r--r--drivers/usb/serial/ark3116.c1
-rw-r--r--drivers/usb/serial/belkin_sa.c1
-rw-r--r--drivers/usb/serial/bus.c2
-rw-r--r--drivers/usb/serial/ch341.c4
-rw-r--r--drivers/usb/serial/cp210x.c1
-rw-r--r--drivers/usb/serial/cyberjack.c1
-rw-r--r--drivers/usb/serial/cypress_m8.c5
-rw-r--r--drivers/usb/serial/digi_acceleport.c2
-rw-r--r--drivers/usb/serial/empeg.c1
-rw-r--r--drivers/usb/serial/f81232.c2
-rw-r--r--drivers/usb/serial/f81534.c1
-rw-r--r--drivers/usb/serial/ftdi_sio.c1
-rw-r--r--drivers/usb/serial/garmin_gps.c6
-rw-r--r--drivers/usb/serial/generic.c1
-rw-r--r--drivers/usb/serial/io_edgeport.c4
-rw-r--r--drivers/usb/serial/io_ti.c2
-rw-r--r--drivers/usb/serial/ipaq.c1
-rw-r--r--drivers/usb/serial/ipw.c1
-rw-r--r--drivers/usb/serial/ir-usb.c1
-rw-r--r--drivers/usb/serial/iuu_phoenix.c1
-rw-r--r--drivers/usb/serial/keyspan.c4
-rw-r--r--drivers/usb/serial/keyspan_pda.c2
-rw-r--r--drivers/usb/serial/kl5kusb105.c3
-rw-r--r--drivers/usb/serial/kobil_sct.c4
-rw-r--r--drivers/usb/serial/mct_u232.c3
-rw-r--r--drivers/usb/serial/metro-usb.c1
-rw-r--r--drivers/usb/serial/mos7720.c1
-rw-r--r--drivers/usb/serial/mos7840.c46
-rw-r--r--drivers/usb/serial/mxuport.c4
-rw-r--r--drivers/usb/serial/navman.c2
-rw-r--r--drivers/usb/serial/omninet.c1
-rw-r--r--drivers/usb/serial/opticon.c1
-rw-r--r--drivers/usb/serial/option.c44
-rw-r--r--drivers/usb/serial/oti6858.c1
-rw-r--r--drivers/usb/serial/pl2303.c4
-rw-r--r--drivers/usb/serial/pl2303.h4
-rw-r--r--drivers/usb/serial/qcaux.c2
-rw-r--r--drivers/usb/serial/qcserial.c1
-rw-r--r--drivers/usb/serial/quatech2.c3
-rw-r--r--drivers/usb/serial/safe_serial.c1
-rw-r--r--drivers/usb/serial/sierra.c1
-rw-r--r--drivers/usb/serial/spcp8x5.c11
-rw-r--r--drivers/usb/serial/ssu100.c1
-rw-r--r--drivers/usb/serial/symbolserial.c2
-rw-r--r--drivers/usb/serial/ti_usb_3410_5052.c2
-rw-r--r--drivers/usb/serial/upd78f0730.c1
-rw-r--r--drivers/usb/serial/usb-serial-simple.c2
-rw-r--r--drivers/usb/serial/usb-serial.c12
-rw-r--r--drivers/usb/serial/usb_debug.c10
-rw-r--r--drivers/usb/serial/visor.c3
-rw-r--r--drivers/usb/serial/whiteheat.c2
-rw-r--r--drivers/usb/serial/wishbone-serial.c1
-rw-r--r--drivers/usb/serial/xr_serial.c1
-rw-r--r--drivers/usb/serial/xsens_mt.c1
-rw-r--r--drivers/usb/storage/alauda.c13
-rw-r--r--drivers/usb/storage/cypress_atacb.c4
-rw-r--r--drivers/usb/storage/datafab.c4
-rw-r--r--drivers/usb/storage/ene_ub6250.c6
-rw-r--r--drivers/usb/storage/freecom.c4
-rw-r--r--drivers/usb/storage/isd200.c4
-rw-r--r--drivers/usb/storage/jumpshot.c4
-rw-r--r--drivers/usb/storage/karma.c4
-rw-r--r--drivers/usb/storage/onetouch.c4
-rw-r--r--drivers/usb/storage/scsiglue.c63
-rw-r--r--drivers/usb/storage/sddr09.c4
-rw-r--r--drivers/usb/storage/sddr55.c4
-rw-r--r--drivers/usb/storage/shuttle_usbat.c4
-rw-r--r--drivers/usb/storage/uas.c41
-rw-r--r--drivers/usb/storage/usb.c111
-rw-r--r--drivers/usb/typec/altmodes/displayport.c3
-rw-r--r--drivers/usb/typec/altmodes/nvidia.c1
-rw-r--r--drivers/usb/typec/anx7411.c19
-rw-r--r--drivers/usb/typec/bus.c2
-rw-r--r--drivers/usb/typec/class.c18
-rw-r--r--drivers/usb/typec/mux/Kconfig2
-rw-r--r--drivers/usb/typec/mux/fsa4480.c14
-rw-r--r--drivers/usb/typec/mux/gpio-sbu-mux.c14
-rw-r--r--drivers/usb/typec/mux/intel_pmc_mux.c3
-rw-r--r--drivers/usb/typec/mux/nb7vpq904m.c97
-rw-r--r--drivers/usb/typec/mux/ptn36502.c77
-rw-r--r--drivers/usb/typec/rt1719.c11
-rw-r--r--drivers/usb/typec/stusb160x.c2
-rw-r--r--drivers/usb/typec/tcpm/fusb302.c4
-rw-r--r--drivers/usb/typec/tcpm/maxim_contaminant.c53
-rw-r--r--drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c10
-rw-r--r--drivers/usb/typec/tcpm/tcpci.c178
-rw-r--r--drivers/usb/typec/tcpm/tcpci_maxim.h34
-rw-r--r--drivers/usb/typec/tcpm/tcpci_maxim_core.c84
-rw-r--r--drivers/usb/typec/tcpm/tcpci_rt1711h.c27
-rw-r--r--drivers/usb/typec/tcpm/tcpm.c56
-rw-r--r--drivers/usb/typec/tipd/core.c103
-rw-r--r--drivers/usb/typec/tipd/tps6598x.h11
-rw-r--r--drivers/usb/typec/ucsi/Kconfig9
-rw-r--r--drivers/usb/typec/ucsi/Makefile1
-rw-r--r--drivers/usb/typec/ucsi/displayport.c6
-rw-r--r--drivers/usb/typec/ucsi/psy.c43
-rw-r--r--drivers/usb/typec/ucsi/ucsi.c651
-rw-r--r--drivers/usb/typec/ucsi/ucsi.h125
-rw-r--r--drivers/usb/typec/ucsi/ucsi_acpi.c182
-rw-r--r--drivers/usb/typec/ucsi/ucsi_ccg.c107
-rw-r--r--drivers/usb/typec/ucsi/ucsi_glink.c190
-rw-r--r--drivers/usb/typec/ucsi/ucsi_stm32g0.c73
-rw-r--r--drivers/usb/typec/ucsi/ucsi_yoga_c630.c164
-rw-r--r--drivers/usb/usbip/stub_rx.c77
-rw-r--r--drivers/usb/usbip/vhci_hcd.c45
-rw-r--r--drivers/usb/usbip/vhci_sysfs.c3
318 files changed, 5776 insertions, 3604 deletions
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index 3a9a0dd4be70..949eca0adebe 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -35,6 +35,7 @@ obj-$(CONFIG_USB_R8A66597_HCD) += host/
obj-$(CONFIG_USB_FSL_USB2) += host/
obj-$(CONFIG_USB_FOTG210_HCD) += host/
obj-$(CONFIG_USB_MAX3421_HCD) += host/
+obj-$(CONFIG_USB_XEN_HCD) += host/
obj-$(CONFIG_USB_C67X00_HCD) += c67x00/
diff --git a/drivers/usb/atm/cxacru.c b/drivers/usb/atm/cxacru.c
index 4ce7cba2b48a..0dd85d2635b9 100644
--- a/drivers/usb/atm/cxacru.c
+++ b/drivers/usb/atm/cxacru.c
@@ -24,7 +24,7 @@
#include <linux/device.h>
#include <linux/firmware.h>
#include <linux/mutex.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include "usbatm.h"
@@ -1131,6 +1131,7 @@ static int cxacru_bind(struct usbatm_data *usbatm_instance,
struct cxacru_data *instance;
struct usb_device *usb_dev = interface_to_usbdev(intf);
struct usb_host_endpoint *cmd_ep = usb_dev->ep_in[CXACRU_EP_CMD];
+ struct usb_endpoint_descriptor *in, *out;
int ret;
/* instance init */
@@ -1177,6 +1178,19 @@ static int cxacru_bind(struct usbatm_data *usbatm_instance,
goto fail;
}
+ if (usb_endpoint_xfer_int(&cmd_ep->desc))
+ ret = usb_find_common_endpoints(intf->cur_altsetting,
+ NULL, NULL, &in, &out);
+ else
+ ret = usb_find_common_endpoints(intf->cur_altsetting,
+ &in, &out, NULL, NULL);
+
+ if (ret) {
+ usb_err(usbatm_instance, "cxacru_bind: interface has incorrect endpoints\n");
+ ret = -ENODEV;
+ goto fail;
+ }
+
if ((cmd_ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
== USB_ENDPOINT_XFER_INT) {
usb_fill_int_urb(instance->rcv_urb,
diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c
index 16703815be0c..e8e43c38aa1b 100644
--- a/drivers/usb/atm/ueagle-atm.c
+++ b/drivers/usb/atm/ueagle-atm.c
@@ -25,7 +25,7 @@
#include <linux/slab.h>
#include <linux/kernel.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include "usbatm.h"
diff --git a/drivers/usb/cdns3/cdns3-pci-wrap.c b/drivers/usb/cdns3/cdns3-pci-wrap.c
index 1f6320d98a76..591d149de8f3 100644
--- a/drivers/usb/cdns3/cdns3-pci-wrap.c
+++ b/drivers/usb/cdns3/cdns3-pci-wrap.c
@@ -37,8 +37,7 @@ struct cdns3_wrap {
#define PCI_DRIVER_NAME "cdns3-pci-usbss"
#define PLAT_DRIVER_NAME "cdns-usb3"
-#define CDNS_VENDOR_ID 0x17cd
-#define CDNS_DEVICE_ID 0x0100
+#define PCI_DEVICE_ID_CDNS_USB3 0x0100
static struct pci_dev *cdns3_get_second_fun(struct pci_dev *pdev)
{
@@ -190,7 +189,7 @@ static void cdns3_pci_remove(struct pci_dev *pdev)
}
static const struct pci_device_id cdns3_pci_ids[] = {
- { PCI_DEVICE(CDNS_VENDOR_ID, CDNS_DEVICE_ID), },
+ { PCI_VDEVICE(CDNS, PCI_DEVICE_ID_CDNS_USB3) },
{ 0, }
};
diff --git a/drivers/usb/cdns3/cdns3-ti.c b/drivers/usb/cdns3/cdns3-ti.c
index 5945c4b1e11f..cfabc12ee0e3 100644
--- a/drivers/usb/cdns3/cdns3-ti.c
+++ b/drivers/usb/cdns3/cdns3-ti.c
@@ -16,6 +16,7 @@
#include <linux/of_platform.h>
#include <linux/pm_runtime.h>
#include <linux/property.h>
+#include "core.h"
/* USB Wrapper register offsets */
#define USBSS_PID 0x0
@@ -85,6 +86,18 @@ static inline void cdns_ti_writel(struct cdns_ti *data, u32 offset, u32 value)
writel(value, data->usbss + offset);
}
+static struct cdns3_platform_data cdns_ti_pdata = {
+ .quirks = CDNS3_DRD_SUSPEND_RESIDENCY_ENABLE, /* Errata i2409 */
+};
+
+static const struct of_dev_auxdata cdns_ti_auxdata[] = {
+ {
+ .compatible = "cdns,usb3",
+ .platform_data = &cdns_ti_pdata,
+ },
+ {},
+};
+
static int cdns_ti_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -176,7 +189,7 @@ static int cdns_ti_probe(struct platform_device *pdev)
reg |= USBSS_W1_PWRUP_RST;
cdns_ti_writel(data, USBSS_W1, reg);
- error = of_platform_populate(node, NULL, NULL, dev);
+ error = of_platform_populate(node, NULL, cdns_ti_auxdata, dev);
if (error) {
dev_err(dev, "failed to create children: %d\n", error);
goto err;
diff --git a/drivers/usb/cdns3/cdns3-trace.h b/drivers/usb/cdns3/cdns3-trace.h
index 40db89e3333c..c4e542f1b9b7 100644
--- a/drivers/usb/cdns3/cdns3-trace.h
+++ b/drivers/usb/cdns3/cdns3-trace.h
@@ -33,7 +33,7 @@ TRACE_EVENT(cdns3_halt,
__field(u8, flush)
),
TP_fast_assign(
- __assign_str(name, ep_priv->name);
+ __assign_str(name);
__entry->halt = halt;
__entry->flush = flush;
),
@@ -49,8 +49,8 @@ TRACE_EVENT(cdns3_wa1,
__string(msg, msg)
),
TP_fast_assign(
- __assign_str(ep_name, ep_priv->name);
- __assign_str(msg, msg);
+ __assign_str(ep_name);
+ __assign_str(msg);
),
TP_printk("WA1: %s %s", __get_str(ep_name), __get_str(msg))
);
@@ -63,8 +63,8 @@ TRACE_EVENT(cdns3_wa2,
__string(msg, msg)
),
TP_fast_assign(
- __assign_str(ep_name, ep_priv->name);
- __assign_str(msg, msg);
+ __assign_str(ep_name);
+ __assign_str(msg);
),
TP_printk("WA2: %s %s", __get_str(ep_name), __get_str(msg))
);
@@ -77,7 +77,7 @@ DECLARE_EVENT_CLASS(cdns3_log_doorbell,
__field(u32, ep_trbaddr)
),
TP_fast_assign(
- __assign_str(name, ep_name);
+ __assign_str(name);
__entry->ep_trbaddr = ep_trbaddr;
),
TP_printk("%s, ep_trbaddr %08x", __get_str(name),
@@ -125,7 +125,7 @@ DECLARE_EVENT_CLASS(cdns3_log_epx_irq,
__field(u32, use_streams)
),
TP_fast_assign(
- __assign_str(ep_name, priv_ep->name);
+ __assign_str(ep_name);
__entry->ep_sts = readl(&priv_dev->regs->ep_sts);
__entry->ep_traddr = readl(&priv_dev->regs->ep_traddr);
__entry->ep_last_sid = priv_ep->last_stream_id;
@@ -214,7 +214,7 @@ DECLARE_EVENT_CLASS(cdns3_log_request,
__field(unsigned int, stream_id)
),
TP_fast_assign(
- __assign_str(name, req->priv_ep->name);
+ __assign_str(name);
__entry->req = req;
__entry->buf = req->request.buf;
__entry->actual = req->request.actual;
@@ -294,7 +294,7 @@ DECLARE_EVENT_CLASS(cdns3_stream_split_transfer_len,
__field(unsigned int, stream_id)
),
TP_fast_assign(
- __assign_str(name, req->priv_ep->name);
+ __assign_str(name);
__entry->req = req;
__entry->actual = req->request.length;
__entry->length = req->request.actual;
@@ -329,7 +329,7 @@ DECLARE_EVENT_CLASS(cdns3_log_aligned_request,
__field(u32, aligned_buf_size)
),
TP_fast_assign(
- __assign_str(name, priv_req->priv_ep->name);
+ __assign_str(name);
__entry->req = &priv_req->request;
__entry->buf = priv_req->request.buf;
__entry->dma = priv_req->request.dma;
@@ -364,7 +364,7 @@ DECLARE_EVENT_CLASS(cdns3_log_map_request,
__field(dma_addr_t, dma)
),
TP_fast_assign(
- __assign_str(name, priv_req->priv_ep->name);
+ __assign_str(name);
__entry->req = &priv_req->request;
__entry->buf = priv_req->request.buf;
__entry->dma = priv_req->request.dma;
@@ -395,7 +395,7 @@ DECLARE_EVENT_CLASS(cdns3_log_trb,
__field(unsigned int, last_stream_id)
),
TP_fast_assign(
- __assign_str(name, priv_ep->name);
+ __assign_str(name);
__entry->trb = trb;
__entry->buffer = le32_to_cpu(trb->buffer);
__entry->length = le32_to_cpu(trb->length);
@@ -467,7 +467,7 @@ DECLARE_EVENT_CLASS(cdns3_log_ep,
__field(u8, dequeue)
),
TP_fast_assign(
- __assign_str(name, priv_ep->name);
+ __assign_str(name);
__entry->maxpacket = priv_ep->endpoint.maxpacket;
__entry->maxpacket_limit = priv_ep->endpoint.maxpacket_limit;
__entry->max_streams = priv_ep->endpoint.max_streams;
diff --git a/drivers/usb/cdns3/cdnsp-gadget.h b/drivers/usb/cdns3/cdnsp-gadget.h
index dbee6f085277..84887dfea763 100644
--- a/drivers/usb/cdns3/cdnsp-gadget.h
+++ b/drivers/usb/cdns3/cdnsp-gadget.h
@@ -811,6 +811,7 @@ struct cdnsp_stream_info {
* generate Missed Service Error Event.
* Set skip flag when receive a Missed Service Error Event and
* process the missed tds on the endpoint ring.
+ * @wa1_nop_trb: hold pointer to NOP trb.
*/
struct cdnsp_ep {
struct usb_ep endpoint;
@@ -838,6 +839,8 @@ struct cdnsp_ep {
#define EP_UNCONFIGURED BIT(7)
bool skip;
+ union cdnsp_trb *wa1_nop_trb;
+
};
/**
diff --git a/drivers/usb/cdns3/cdnsp-pci.c b/drivers/usb/cdns3/cdnsp-pci.c
index 0725668ffea4..2d05368a6745 100644
--- a/drivers/usb/cdns3/cdnsp-pci.c
+++ b/drivers/usb/cdns3/cdnsp-pci.c
@@ -28,10 +28,11 @@
#define PCI_DRIVER_NAME "cdns-pci-usbssp"
#define PLAT_DRIVER_NAME "cdns-usbssp"
-#define CDNS_VENDOR_ID 0x17cd
-#define CDNS_DEVICE_ID 0x0200
-#define CDNS_DRD_ID 0x0100
-#define CDNS_DRD_IF (PCI_CLASS_SERIAL_USB << 8 | 0x80)
+#define PCI_DEVICE_ID_CDNS_USB3 0x0100
+#define PCI_DEVICE_ID_CDNS_UDC 0x0200
+
+#define PCI_CLASS_SERIAL_USB_CDNS_USB3 (PCI_CLASS_SERIAL_USB << 8 | 0x80)
+#define PCI_CLASS_SERIAL_USB_CDNS_UDC PCI_CLASS_SERIAL_USB_DEVICE
static struct pci_dev *cdnsp_get_second_fun(struct pci_dev *pdev)
{
@@ -40,10 +41,10 @@ static struct pci_dev *cdnsp_get_second_fun(struct pci_dev *pdev)
* Platform has two function. The fist keeps resources for
* Host/Device while the secon keeps resources for DRD/OTG.
*/
- if (pdev->device == CDNS_DEVICE_ID)
- return pci_get_device(pdev->vendor, CDNS_DRD_ID, NULL);
- else if (pdev->device == CDNS_DRD_ID)
- return pci_get_device(pdev->vendor, CDNS_DEVICE_ID, NULL);
+ if (pdev->device == PCI_DEVICE_ID_CDNS_UDC)
+ return pci_get_device(pdev->vendor, PCI_DEVICE_ID_CDNS_USB3, NULL);
+ if (pdev->device == PCI_DEVICE_ID_CDNS_USB3)
+ return pci_get_device(pdev->vendor, PCI_DEVICE_ID_CDNS_UDC, NULL);
return NULL;
}
@@ -220,18 +221,18 @@ static const struct dev_pm_ops cdnsp_pci_pm_ops = {
};
static const struct pci_device_id cdnsp_pci_ids[] = {
- { PCI_VENDOR_ID_CDNS, CDNS_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,
- PCI_CLASS_SERIAL_USB_DEVICE, PCI_ANY_ID },
- { PCI_VENDOR_ID_CDNS, CDNS_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,
- CDNS_DRD_IF, PCI_ANY_ID },
- { PCI_VENDOR_ID_CDNS, CDNS_DRD_ID, PCI_ANY_ID, PCI_ANY_ID,
- CDNS_DRD_IF, PCI_ANY_ID },
+ { PCI_DEVICE(PCI_VENDOR_ID_CDNS, PCI_DEVICE_ID_CDNS_UDC),
+ .class = PCI_CLASS_SERIAL_USB_CDNS_UDC },
+ { PCI_DEVICE(PCI_VENDOR_ID_CDNS, PCI_DEVICE_ID_CDNS_UDC),
+ .class = PCI_CLASS_SERIAL_USB_CDNS_USB3 },
+ { PCI_DEVICE(PCI_VENDOR_ID_CDNS, PCI_DEVICE_ID_CDNS_USB3),
+ .class = PCI_CLASS_SERIAL_USB_CDNS_USB3 },
{ 0, }
};
static struct pci_driver cdnsp_pci_driver = {
.name = "cdnsp-pci",
- .id_table = &cdnsp_pci_ids[0],
+ .id_table = cdnsp_pci_ids,
.probe = cdnsp_pci_probe,
.remove = cdnsp_pci_remove,
.driver = {
diff --git a/drivers/usb/cdns3/cdnsp-ring.c b/drivers/usb/cdns3/cdnsp-ring.c
index 02f297f5637d..46852529499d 100644
--- a/drivers/usb/cdns3/cdnsp-ring.c
+++ b/drivers/usb/cdns3/cdnsp-ring.c
@@ -402,7 +402,7 @@ static u64 cdnsp_get_hw_deq(struct cdnsp_device *pdev,
struct cdnsp_stream_ctx *st_ctx;
struct cdnsp_ep *pep;
- pep = &pdev->eps[stream_id];
+ pep = &pdev->eps[ep_index];
if (pep->ep_state & EP_HAS_STREAMS) {
st_ctx = &pep->stream_info.stream_ctx_array[stream_id];
@@ -718,7 +718,8 @@ int cdnsp_remove_request(struct cdnsp_device *pdev,
seg = cdnsp_trb_in_td(pdev, cur_td->start_seg, cur_td->first_trb,
cur_td->last_trb, hw_deq);
- if (seg && (pep->ep_state & EP_ENABLED))
+ if (seg && (pep->ep_state & EP_ENABLED) &&
+ !(pep->ep_state & EP_DIS_IN_RROGRESS))
cdnsp_find_new_dequeue_state(pdev, pep, preq->request.stream_id,
cur_td, &deq_state);
else
@@ -736,7 +737,8 @@ int cdnsp_remove_request(struct cdnsp_device *pdev,
* During disconnecting all endpoint will be disabled so we don't
* have to worry about updating dequeue pointer.
*/
- if (pdev->cdnsp_state & CDNSP_STATE_DISCONNECT_PENDING) {
+ if (pdev->cdnsp_state & CDNSP_STATE_DISCONNECT_PENDING ||
+ pep->ep_state & EP_DIS_IN_RROGRESS) {
status = -ESHUTDOWN;
ret = cdnsp_cmd_set_deq(pdev, pep, &deq_state);
}
@@ -1905,6 +1907,23 @@ int cdnsp_queue_bulk_tx(struct cdnsp_device *pdev, struct cdnsp_request *preq)
return ret;
/*
+ * workaround 1: STOP EP command on LINK TRB with TC bit set to 1
+ * causes that internal cycle bit can have incorrect state after
+ * command complete. In consequence empty transfer ring can be
+ * incorrectly detected when EP is resumed.
+ * NOP TRB before LINK TRB avoid such scenario. STOP EP command is
+ * then on NOP TRB and internal cycle bit is not changed and have
+ * correct value.
+ */
+ if (pep->wa1_nop_trb) {
+ field = le32_to_cpu(pep->wa1_nop_trb->trans_event.flags);
+ field ^= TRB_CYCLE;
+
+ pep->wa1_nop_trb->trans_event.flags = cpu_to_le32(field);
+ pep->wa1_nop_trb = NULL;
+ }
+
+ /*
* Don't give the first TRB to the hardware (by toggling the cycle bit)
* until we've finished creating all the other TRBs. The ring's cycle
* state may change as we enqueue the other TRBs, so save it too.
@@ -1999,6 +2018,17 @@ int cdnsp_queue_bulk_tx(struct cdnsp_device *pdev, struct cdnsp_request *preq)
send_addr = addr;
}
+ if (cdnsp_trb_is_link(ring->enqueue + 1)) {
+ field = TRB_TYPE(TRB_TR_NOOP) | TRB_IOC;
+ if (!ring->cycle_state)
+ field |= TRB_CYCLE;
+
+ pep->wa1_nop_trb = ring->enqueue;
+
+ cdnsp_queue_trb(pdev, ring, 0, 0x0, 0x0,
+ TRB_INTR_TARGET(0), field);
+ }
+
cdnsp_check_trb_math(preq, enqd_len);
ret = cdnsp_giveback_first_trb(pdev, pep, preq->request.stream_id,
start_cycle, start_trb);
diff --git a/drivers/usb/cdns3/cdnsp-trace.h b/drivers/usb/cdns3/cdnsp-trace.h
index 4b51011eb00b..f2bcf77a5d0a 100644
--- a/drivers/usb/cdns3/cdnsp-trace.h
+++ b/drivers/usb/cdns3/cdnsp-trace.h
@@ -48,7 +48,7 @@ DECLARE_EVENT_CLASS(cdnsp_log_ep,
__field(u8, drbls_count)
),
TP_fast_assign(
- __assign_str(name, pep->name);
+ __assign_str(name);
__entry->state = pep->ep_state;
__entry->stream_id = stream_id;
__entry->enabled = pep->ep_state & EP_HAS_STREAMS;
@@ -138,7 +138,7 @@ DECLARE_EVENT_CLASS(cdnsp_log_simple,
__string(text, msg)
),
TP_fast_assign(
- __assign_str(text, msg);
+ __assign_str(text);
),
TP_printk("%s", __get_str(text))
);
@@ -303,7 +303,7 @@ DECLARE_EVENT_CLASS(cdnsp_log_bounce,
__field(unsigned int, unalign)
),
TP_fast_assign(
- __assign_str(name, preq->pep->name);
+ __assign_str(name);
__entry->new_buf_len = new_buf_len;
__entry->offset = offset;
__entry->dma = dma;
@@ -470,7 +470,7 @@ DECLARE_EVENT_CLASS(cdnsp_log_request,
),
TP_fast_assign(
- __assign_str(name, req->pep->name);
+ __assign_str(name);
__entry->request = &req->request;
__entry->preq = req;
__entry->buf = req->request.buf;
@@ -674,7 +674,7 @@ DECLARE_EVENT_CLASS(cdnsp_log_td_info,
__field(dma_addr_t, trb_dma)
),
TP_fast_assign(
- __assign_str(name, preq->pep->name);
+ __assign_str(name);
__entry->request = &preq->request;
__entry->preq = preq;
__entry->first_trb = preq->td.first_trb;
diff --git a/drivers/usb/cdns3/core.h b/drivers/usb/cdns3/core.h
index 81a9c9d6be08..57d47348dc19 100644
--- a/drivers/usb/cdns3/core.h
+++ b/drivers/usb/cdns3/core.h
@@ -44,6 +44,7 @@ struct cdns3_platform_data {
bool suspend, bool wakeup);
unsigned long quirks;
#define CDNS3_DEFAULT_PM_RUNTIME_ALLOW BIT(0)
+#define CDNS3_DRD_SUSPEND_RESIDENCY_ENABLE BIT(1)
};
/**
diff --git a/drivers/usb/cdns3/drd.c b/drivers/usb/cdns3/drd.c
index 8b936a2e93a0..84fb38a5723a 100644
--- a/drivers/usb/cdns3/drd.c
+++ b/drivers/usb/cdns3/drd.c
@@ -389,7 +389,7 @@ static irqreturn_t cdns_drd_irq(int irq, void *data)
int cdns_drd_init(struct cdns *cdns)
{
void __iomem *regs;
- u32 state;
+ u32 state, reg;
int ret;
regs = devm_ioremap_resource(cdns->dev, &cdns->otg_res);
@@ -433,6 +433,14 @@ int cdns_drd_init(struct cdns *cdns)
cdns->otg_irq_regs = (struct cdns_otg_irq_regs __iomem *)
&cdns->otg_v1_regs->ien;
writel(1, &cdns->otg_v1_regs->simulate);
+
+ if (cdns->pdata &&
+ (cdns->pdata->quirks & CDNS3_DRD_SUSPEND_RESIDENCY_ENABLE)) {
+ reg = readl(&cdns->otg_v1_regs->susp_ctrl);
+ reg |= SUSP_CTRL_SUSPEND_RESIDENCY_ENABLE;
+ writel(reg, &cdns->otg_v1_regs->susp_ctrl);
+ }
+
cdns->version = CDNS3_CONTROLLER_V1;
} else {
dev_err(cdns->dev, "not supported DID=0x%08x\n", state);
diff --git a/drivers/usb/cdns3/drd.h b/drivers/usb/cdns3/drd.h
index d72370c321d3..1e2aee14d629 100644
--- a/drivers/usb/cdns3/drd.h
+++ b/drivers/usb/cdns3/drd.h
@@ -193,6 +193,9 @@ struct cdns_otg_irq_regs {
/* OTGREFCLK - bitmasks */
#define OTGREFCLK_STB_CLK_SWITCH_EN BIT(31)
+/* SUPS_CTRL - bitmasks */
+#define SUSP_CTRL_SUSPEND_RESIDENCY_ENABLE BIT(17)
+
/* OVERRIDE - bitmasks */
#define OVERRIDE_IDPULLUP BIT(0)
/* Only for CDNS3_CONTROLLER_V0 version */
diff --git a/drivers/usb/cdns3/host.c b/drivers/usb/cdns3/host.c
index ceca4d839dfd..7ba760ee62e3 100644
--- a/drivers/usb/cdns3/host.c
+++ b/drivers/usb/cdns3/host.c
@@ -62,7 +62,9 @@ static const struct xhci_plat_priv xhci_plat_cdns3_xhci = {
.resume_quirk = xhci_cdns3_resume_quirk,
};
-static const struct xhci_plat_priv xhci_plat_cdnsp_xhci;
+static const struct xhci_plat_priv xhci_plat_cdnsp_xhci = {
+ .quirks = XHCI_CDNS_SCTX_QUIRK,
+};
static int __cdns_host_init(struct cdns *cdns)
{
diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c
index ae9a6a17ec6e..c64ab0e07ea0 100644
--- a/drivers/usb/chipidea/ci_hdrc_imx.c
+++ b/drivers/usb/chipidea/ci_hdrc_imx.c
@@ -128,7 +128,7 @@ static struct imx_usbmisc_data *usbmisc_get_init_data(struct device *dev)
* In case the fsl,usbmisc property is not present this device doesn't
* need usbmisc. Return NULL (which is no error here)
*/
- if (!of_get_property(np, "fsl,usbmisc", NULL))
+ if (!of_property_present(np, "fsl,usbmisc"))
return NULL;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
@@ -212,7 +212,7 @@ static int imx_get_clks(struct device *dev)
/* Get wakeup clock. Not all of the platforms need to
* handle this clock. So make it optional.
*/
- data->clk_wakeup = devm_clk_get_optional(dev, "usb_wakeup_clk");
+ data->clk_wakeup = devm_clk_get_optional(dev, "usb_wakeup");
if (IS_ERR(data->clk_wakeup))
ret = dev_err_probe(dev, PTR_ERR(data->clk_wakeup),
"Failed to get wakeup clk\n");
@@ -557,7 +557,7 @@ static void ci_hdrc_imx_shutdown(struct platform_device *pdev)
ci_hdrc_imx_remove(pdev);
}
-static int __maybe_unused imx_controller_suspend(struct device *dev,
+static int imx_controller_suspend(struct device *dev,
pm_message_t msg)
{
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
@@ -582,7 +582,7 @@ static int __maybe_unused imx_controller_suspend(struct device *dev,
return 0;
}
-static int __maybe_unused imx_controller_resume(struct device *dev,
+static int imx_controller_resume(struct device *dev,
pm_message_t msg)
{
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
@@ -618,7 +618,7 @@ clk_disable:
return ret;
}
-static int __maybe_unused ci_hdrc_imx_suspend(struct device *dev)
+static int ci_hdrc_imx_suspend(struct device *dev)
{
int ret;
@@ -636,7 +636,7 @@ static int __maybe_unused ci_hdrc_imx_suspend(struct device *dev)
return ret;
}
-static int __maybe_unused ci_hdrc_imx_resume(struct device *dev)
+static int ci_hdrc_imx_resume(struct device *dev)
{
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
int ret;
@@ -652,7 +652,7 @@ static int __maybe_unused ci_hdrc_imx_resume(struct device *dev)
return ret;
}
-static int __maybe_unused ci_hdrc_imx_runtime_suspend(struct device *dev)
+static int ci_hdrc_imx_runtime_suspend(struct device *dev)
{
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
@@ -664,15 +664,14 @@ static int __maybe_unused ci_hdrc_imx_runtime_suspend(struct device *dev)
return imx_controller_suspend(dev, PMSG_AUTO_SUSPEND);
}
-static int __maybe_unused ci_hdrc_imx_runtime_resume(struct device *dev)
+static int ci_hdrc_imx_runtime_resume(struct device *dev)
{
return imx_controller_resume(dev, PMSG_AUTO_RESUME);
}
static const struct dev_pm_ops ci_hdrc_imx_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(ci_hdrc_imx_suspend, ci_hdrc_imx_resume)
- SET_RUNTIME_PM_OPS(ci_hdrc_imx_runtime_suspend,
- ci_hdrc_imx_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(ci_hdrc_imx_suspend, ci_hdrc_imx_resume)
+ RUNTIME_PM_OPS(ci_hdrc_imx_runtime_suspend, ci_hdrc_imx_runtime_resume, NULL)
};
static struct platform_driver ci_hdrc_imx_driver = {
.probe = ci_hdrc_imx_probe,
@@ -681,7 +680,7 @@ static struct platform_driver ci_hdrc_imx_driver = {
.driver = {
.name = "imx_usb",
.of_match_table = ci_hdrc_imx_dt_ids,
- .pm = &ci_hdrc_imx_pm_ops,
+ .pm = pm_ptr(&ci_hdrc_imx_pm_ops),
},
};
diff --git a/drivers/usb/chipidea/ci_hdrc_msm.c b/drivers/usb/chipidea/ci_hdrc_msm.c
index 7b5b47ce8a02..1661639cd2eb 100644
--- a/drivers/usb/chipidea/ci_hdrc_msm.c
+++ b/drivers/usb/chipidea/ci_hdrc_msm.c
@@ -303,4 +303,5 @@ module_platform_driver(ci_hdrc_msm_driver);
MODULE_ALIAS("platform:msm_hsusb");
MODULE_ALIAS("platform:ci13xxx_msm");
+MODULE_DESCRIPTION("ChipIdea Highspeed Dual Role Controller");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/chipidea/ci_hdrc_npcm.c b/drivers/usb/chipidea/ci_hdrc_npcm.c
index e4a191e02ceb..3e5e05dbda89 100644
--- a/drivers/usb/chipidea/ci_hdrc_npcm.c
+++ b/drivers/usb/chipidea/ci_hdrc_npcm.c
@@ -18,7 +18,7 @@ struct npcm_udc_data {
struct ci_hdrc_platform_data pdata;
};
-static int npcm_udc_notify_event(struct ci_hdrc *ci, unsigned event)
+static int npcm_udc_notify_event(struct ci_hdrc *ci, unsigned int event)
{
struct device *dev = ci->dev->parent;
@@ -28,7 +28,7 @@ static int npcm_udc_notify_event(struct ci_hdrc *ci, unsigned event)
hw_write(ci, OP_USBMODE, 0xffffffff, 0x0);
break;
default:
- dev_dbg(dev, "unknown ci_hdrc event (%d)\n",event);
+ dev_dbg(dev, "unknown ci_hdrc event (%d)\n", event);
break;
}
@@ -80,15 +80,13 @@ clk_err:
return ret;
}
-static int npcm_udc_remove(struct platform_device *pdev)
+static void npcm_udc_remove(struct platform_device *pdev)
{
struct npcm_udc_data *ci = platform_get_drvdata(pdev);
pm_runtime_disable(&pdev->dev);
ci_hdrc_remove_device(ci->ci);
clk_disable_unprepare(ci->core_clk);
-
- return 0;
}
static const struct of_device_id npcm_udc_dt_match[] = {
@@ -100,7 +98,7 @@ MODULE_DEVICE_TABLE(of, npcm_udc_dt_match);
static struct platform_driver npcm_udc_driver = {
.probe = npcm_udc_probe,
- .remove = npcm_udc_remove,
+ .remove_new = npcm_udc_remove,
.driver = {
.name = "npcm_udc",
.of_match_table = npcm_udc_dt_match,
diff --git a/drivers/usb/chipidea/ci_hdrc_tegra.c b/drivers/usb/chipidea/ci_hdrc_tegra.c
index 2cc305803217..9538d425f0a0 100644
--- a/drivers/usb/chipidea/ci_hdrc_tegra.c
+++ b/drivers/usb/chipidea/ci_hdrc_tegra.c
@@ -372,7 +372,7 @@ static void tegra_usb_remove(struct platform_device *pdev)
pm_runtime_force_suspend(&pdev->dev);
}
-static int __maybe_unused tegra_usb_runtime_resume(struct device *dev)
+static int tegra_usb_runtime_resume(struct device *dev)
{
struct tegra_usb *usb = dev_get_drvdata(dev);
int err;
@@ -386,7 +386,7 @@ static int __maybe_unused tegra_usb_runtime_resume(struct device *dev)
return 0;
}
-static int __maybe_unused tegra_usb_runtime_suspend(struct device *dev)
+static int tegra_usb_runtime_suspend(struct device *dev)
{
struct tegra_usb *usb = dev_get_drvdata(dev);
@@ -396,15 +396,14 @@ static int __maybe_unused tegra_usb_runtime_suspend(struct device *dev)
}
static const struct dev_pm_ops tegra_usb_pm = {
- SET_RUNTIME_PM_OPS(tegra_usb_runtime_suspend, tegra_usb_runtime_resume,
- NULL)
+ RUNTIME_PM_OPS(tegra_usb_runtime_suspend, tegra_usb_runtime_resume, NULL)
};
static struct platform_driver tegra_usb_driver = {
.driver = {
.name = "tegra-usb",
.of_match_table = tegra_usb_of_match,
- .pm = &tegra_usb_pm,
+ .pm = pm_ptr(&tegra_usb_pm),
},
.probe = tegra_usb_probe,
.remove_new = tegra_usb_remove,
diff --git a/drivers/usb/chipidea/trace.h b/drivers/usb/chipidea/trace.h
index ca0e65b48f0a..1875419cd17f 100644
--- a/drivers/usb/chipidea/trace.h
+++ b/drivers/usb/chipidea/trace.h
@@ -31,7 +31,7 @@ TRACE_EVENT(ci_log,
__vstring(msg, vaf->fmt, vaf->va)
),
TP_fast_assign(
- __assign_str(name, dev_name(ci->dev));
+ __assign_str(name);
__assign_vstr(msg, vaf->fmt, vaf->va);
),
TP_printk("%s: %s", __get_str(name), __get_str(msg))
@@ -51,7 +51,7 @@ DECLARE_EVENT_CLASS(ci_log_trb,
__field(u32, type)
),
TP_fast_assign(
- __assign_str(name, hwep->name);
+ __assign_str(name);
__entry->req = &hwreq->req;
__entry->td = td;
__entry->dma = td->dma;
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
index 2d7f616270c1..69ef3cd8d4f8 100644
--- a/drivers/usb/chipidea/udc.c
+++ b/drivers/usb/chipidea/udc.c
@@ -86,7 +86,7 @@ static int hw_device_state(struct ci_hdrc *ci, u32 dma)
hw_write(ci, OP_ENDPTLISTADDR, ~0, dma);
/* interrupt, error, port change, reset, sleep/suspend */
hw_write(ci, OP_USBINTR, ~0,
- USBi_UI|USBi_UEI|USBi_PCI|USBi_URI|USBi_SLI);
+ USBi_UI|USBi_UEI|USBi_PCI|USBi_URI);
} else {
hw_write(ci, OP_USBINTR, ~0, 0);
}
@@ -877,6 +877,7 @@ __releases(ci->lock)
__acquires(ci->lock)
{
int retval;
+ u32 intr;
spin_unlock(&ci->lock);
if (ci->gadget.speed != USB_SPEED_UNKNOWN)
@@ -890,6 +891,11 @@ __acquires(ci->lock)
if (retval)
goto done;
+ /* clear SLI */
+ hw_write(ci, OP_USBSTS, USBi_SLI, USBi_SLI);
+ intr = hw_read(ci, OP_USBINTR, ~0);
+ hw_write(ci, OP_USBINTR, ~0, intr | USBi_SLI);
+
ci->status = usb_ep_alloc_request(&ci->ep0in->ep, GFP_ATOMIC);
if (ci->status == NULL)
retval = -ENOMEM;
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index 0e7439dba8fe..6b37d1c47fce 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -35,7 +35,7 @@
#include <linux/usb.h>
#include <linux/usb/cdc.h>
#include <asm/byteorder.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <linux/idr.h>
#include <linux/list.h>
@@ -962,10 +962,12 @@ static int get_serial_info(struct tty_struct *tty, struct serial_struct *ss)
struct acm *acm = tty->driver_data;
ss->line = acm->minor;
+ mutex_lock(&acm->port.mutex);
ss->close_delay = jiffies_to_msecs(acm->port.close_delay) / 10;
ss->closing_wait = acm->port.closing_wait == ASYNC_CLOSING_WAIT_NONE ?
ASYNC_CLOSING_WAIT_NONE :
jiffies_to_msecs(acm->port.closing_wait) / 10;
+ mutex_unlock(&acm->port.mutex);
return 0;
}
@@ -1761,6 +1763,9 @@ static const struct usb_device_id acm_ids[] = {
{ USB_DEVICE(0x11ca, 0x0201), /* VeriFone Mx870 Gadget Serial */
.driver_info = SINGLE_RX_URB,
},
+ { USB_DEVICE(0x1901, 0x0006), /* GE Healthcare Patient Monitor UI Controller */
+ .driver_info = DISABLE_ECHO, /* DISABLE ECHO in termios flag */
+ },
{ USB_DEVICE(0x1965, 0x0018), /* Uniden UBC125XLT */
.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
},
diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c
index c553decb5461..86ee39db013f 100644
--- a/drivers/usb/class/cdc-wdm.c
+++ b/drivers/usb/class/cdc-wdm.c
@@ -26,7 +26,7 @@
#include <linux/usb/cdc.h>
#include <linux/wwan.h>
#include <asm/byteorder.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <linux/usb/cdc-wdm.h>
#define DRIVER_AUTHOR "Oliver Neukum"
@@ -266,14 +266,14 @@ static void wdm_int_callback(struct urb *urb)
dev_err(&desc->intf->dev, "Stall on int endpoint\n");
goto sw; /* halt is cleared in work */
default:
- dev_err(&desc->intf->dev,
+ dev_err_ratelimited(&desc->intf->dev,
"nonzero urb status received: %d\n", status);
break;
}
}
if (urb->actual_length < sizeof(struct usb_cdc_notification)) {
- dev_err(&desc->intf->dev, "wdm_int_callback - %d bytes\n",
+ dev_err_ratelimited(&desc->intf->dev, "wdm_int_callback - %d bytes\n",
urb->actual_length);
goto exit;
}
diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c
index 311007b1d904..34e46ef308ab 100644
--- a/drivers/usb/class/usbtmc.c
+++ b/drivers/usb/class/usbtmc.c
@@ -754,7 +754,7 @@ static struct urb *usbtmc_create_urb(void)
if (!urb)
return NULL;
- dmabuf = kmalloc(bufsize, GFP_KERNEL);
+ dmabuf = kzalloc(bufsize, GFP_KERNEL);
if (!dmabuf) {
usb_free_urb(urb);
return NULL;
@@ -2592,4 +2592,5 @@ static struct usb_driver usbtmc_driver = {
module_usb_driver(usbtmc_driver);
+MODULE_DESCRIPTION("USB Test & Measurement class driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/usb/common/common.c b/drivers/usb/common/common.c
index b84efae26e15..b7bea1015d7c 100644
--- a/drivers/usb/common/common.c
+++ b/drivers/usb/common/common.c
@@ -107,19 +107,18 @@ EXPORT_SYMBOL_GPL(usb_speed_string);
*/
enum usb_device_speed usb_get_maximum_speed(struct device *dev)
{
- const char *maximum_speed;
+ const char *p = "maximum-speed";
int ret;
- ret = device_property_read_string(dev, "maximum-speed", &maximum_speed);
- if (ret < 0)
- return USB_SPEED_UNKNOWN;
-
- ret = match_string(ssp_rate, ARRAY_SIZE(ssp_rate), maximum_speed);
+ ret = device_property_match_property_string(dev, p, ssp_rate, ARRAY_SIZE(ssp_rate));
if (ret > 0)
return USB_SPEED_SUPER_PLUS;
- ret = match_string(speed_names, ARRAY_SIZE(speed_names), maximum_speed);
- return (ret < 0) ? USB_SPEED_UNKNOWN : ret;
+ ret = device_property_match_property_string(dev, p, speed_names, ARRAY_SIZE(speed_names));
+ if (ret > 0)
+ return ret;
+
+ return USB_SPEED_UNKNOWN;
}
EXPORT_SYMBOL_GPL(usb_get_maximum_speed);
@@ -276,14 +275,13 @@ EXPORT_SYMBOL_GPL(usb_decode_interval);
*/
enum usb_dr_mode of_usb_get_dr_mode_by_phy(struct device_node *np, int arg0)
{
- struct device_node *controller = NULL;
+ struct device_node *controller;
struct of_phandle_args args;
const char *dr_mode;
int index;
int err;
- do {
- controller = of_find_node_with_property(controller, "phys");
+ for_each_node_with_property(controller, "phys") {
if (!of_device_is_available(controller))
continue;
index = 0;
@@ -306,7 +304,7 @@ enum usb_dr_mode of_usb_get_dr_mode_by_phy(struct device_node *np, int arg0)
goto finish;
index++;
} while (args.np);
- } while (controller);
+ }
finish:
err = of_property_read_string(controller, "dr_mode", &dr_mode);
@@ -433,4 +431,5 @@ static void __exit usb_common_exit(void)
subsys_initcall(usb_common_init);
module_exit(usb_common_exit);
+MODULE_DESCRIPTION("Common code for host and device side USB");
MODULE_LICENSE("GPL");
diff --git a/drivers/usb/common/ulpi.c b/drivers/usb/common/ulpi.c
index 0886b19d2e1c..4a2ee447b213 100644
--- a/drivers/usb/common/ulpi.c
+++ b/drivers/usb/common/ulpi.c
@@ -34,7 +34,7 @@ EXPORT_SYMBOL_GPL(ulpi_write);
/* -------------------------------------------------------------------------- */
-static int ulpi_match(struct device *dev, struct device_driver *driver)
+static int ulpi_match(struct device *dev, const struct device_driver *driver)
{
struct ulpi_driver *drv = to_ulpi_driver(driver);
struct ulpi *ulpi = to_ulpi_dev(dev);
diff --git a/drivers/usb/common/usb-otg-fsm.c b/drivers/usb/common/usb-otg-fsm.c
index 0697fde51d00..e11803225775 100644
--- a/drivers/usb/common/usb-otg-fsm.c
+++ b/drivers/usb/common/usb-otg-fsm.c
@@ -449,4 +449,5 @@ int otg_statemachine(struct otg_fsm *fsm)
return fsm->state_changed;
}
EXPORT_SYMBOL_GPL(otg_statemachine);
+MODULE_DESCRIPTION("OTG Finite State Machine");
MODULE_LICENSE("GPL");
diff --git a/drivers/usb/core/Makefile b/drivers/usb/core/Makefile
index 7d338e9c0657..ac006abd13b3 100644
--- a/drivers/usb/core/Makefile
+++ b/drivers/usb/core/Makefile
@@ -12,8 +12,8 @@ usbcore-$(CONFIG_OF) += of.o
usbcore-$(CONFIG_USB_PCI) += hcd-pci.o
usbcore-$(CONFIG_ACPI) += usb-acpi.o
-ifdef CONFIG_USB_ONBOARD_HUB
-usbcore-y += ../misc/onboard_usb_hub_pdevs.o
+ifdef CONFIG_USB_ONBOARD_DEV
+usbcore-y += ../misc/onboard_usb_dev_pdevs.o
endif
obj-$(CONFIG_USB) += usbcore.o
diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c
index 7f8d33f92ddb..880d52c0949d 100644
--- a/drivers/usb/core/config.c
+++ b/drivers/usb/core/config.c
@@ -279,11 +279,11 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno,
goto skip_to_next_endpoint_or_interface_descriptor;
}
- i = d->bEndpointAddress & ~USB_ENDPOINT_DIR_MASK;
- if (i >= 16 || i == 0) {
+ i = d->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+ if (i == 0) {
dev_notice(ddev, "config %d interface %d altsetting %d has an "
- "invalid endpoint with address 0x%X, skipping\n",
- cfgno, inum, asnum, d->bEndpointAddress);
+ "invalid descriptor for endpoint zero, skipping\n",
+ cfgno, inum, asnum);
goto skip_to_next_endpoint_or_interface_descriptor;
}
@@ -291,6 +291,20 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno,
if (ifp->desc.bNumEndpoints >= num_ep)
goto skip_to_next_endpoint_or_interface_descriptor;
+ /* Save a copy of the descriptor and use it instead of the original */
+ endpoint = &ifp->endpoint[ifp->desc.bNumEndpoints];
+ memcpy(&endpoint->desc, d, n);
+ d = &endpoint->desc;
+
+ /* Clear the reserved bits in bEndpointAddress */
+ i = d->bEndpointAddress &
+ (USB_ENDPOINT_DIR_MASK | USB_ENDPOINT_NUMBER_MASK);
+ if (i != d->bEndpointAddress) {
+ dev_notice(ddev, "config %d interface %d altsetting %d has an endpoint descriptor with address 0x%X, changing to 0x%X\n",
+ cfgno, inum, asnum, d->bEndpointAddress, i);
+ endpoint->desc.bEndpointAddress = i;
+ }
+
/* Check for duplicate endpoint addresses */
if (config_endpoint_is_duplicate(config, inum, asnum, d)) {
dev_notice(ddev, "config %d interface %d altsetting %d has a duplicate endpoint with address 0x%X, skipping\n",
@@ -308,10 +322,8 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno,
}
}
- endpoint = &ifp->endpoint[ifp->desc.bNumEndpoints];
+ /* Accept this endpoint */
++ifp->desc.bNumEndpoints;
-
- memcpy(&endpoint->desc, d, n);
INIT_LIST_HEAD(&endpoint->urb_list);
/*
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index e02ba15f6e34..0c3f12daac79 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -517,6 +517,19 @@ static int usb_unbind_interface(struct device *dev)
return 0;
}
+static void usb_shutdown_interface(struct device *dev)
+{
+ struct usb_interface *intf = to_usb_interface(dev);
+ struct usb_driver *driver;
+
+ if (!dev->driver)
+ return;
+
+ driver = to_usb_driver(dev->driver);
+ if (driver->shutdown)
+ driver->shutdown(intf);
+}
+
/**
* usb_driver_claim_interface - bind a driver to an interface
* @driver: the driver to be bound
@@ -855,7 +868,7 @@ bool usb_driver_applicable(struct usb_device *udev,
return false;
}
-static int usb_device_match(struct device *dev, struct device_driver *drv)
+static int usb_device_match(struct device *dev, const struct device_driver *drv)
{
/* devices and interfaces are handled separately */
if (is_usb_device(dev)) {
@@ -1059,6 +1072,7 @@ int usb_register_driver(struct usb_driver *new_driver, struct module *owner,
new_driver->driver.bus = &usb_bus_type;
new_driver->driver.probe = usb_probe_interface;
new_driver->driver.remove = usb_unbind_interface;
+ new_driver->driver.shutdown = usb_shutdown_interface;
new_driver->driver.owner = owner;
new_driver->driver.mod_name = mod_name;
new_driver->driver.dev_groups = new_driver->dev_groups;
diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c
index ee3156f49533..a08f3f228e6d 100644
--- a/drivers/usb/core/hcd-pci.c
+++ b/drivers/usb/core/hcd-pci.c
@@ -189,7 +189,8 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct hc_driver *driver)
* make sure irq setup is not touched for xhci in generic hcd code
*/
if ((driver->flags & HCD_MASK) < HCD_USB3) {
- retval = pci_alloc_irq_vectors(dev, 1, 1, PCI_IRQ_LEGACY | PCI_IRQ_MSI);
+ retval = pci_alloc_irq_vectors(dev, 1, 1,
+ PCI_IRQ_INTX | PCI_IRQ_MSI);
if (retval < 0) {
dev_err(&dev->dev,
"Found HC with no IRQ. Check BIOS/PCI %s setup!\n",
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index c0e005670d67..500dc35e6477 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -24,7 +24,7 @@
#include <linux/mutex.h>
#include <asm/irq.h>
#include <asm/byteorder.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <linux/platform_device.h>
#include <linux/workqueue.h>
#include <linux/pm_runtime.h>
@@ -866,7 +866,7 @@ static int usb_rh_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
*/
static void usb_bus_init (struct usb_bus *bus)
{
- memset (&bus->devmap, 0, sizeof(struct usb_devmap));
+ memset(&bus->devmap, 0, sizeof(bus->devmap));
bus->devnum_next = 1;
@@ -962,7 +962,7 @@ static int register_root_hub(struct usb_hcd *hcd)
usb_dev->devnum = devnum;
usb_dev->bus->devnum_next = devnum + 1;
- set_bit (devnum, usb_dev->bus->devmap.devicemap);
+ set_bit(devnum, usb_dev->bus->devmap);
usb_set_device_state(usb_dev, USB_STATE_ADDRESS);
mutex_lock(&usb_bus_idr_lock);
@@ -1623,6 +1623,7 @@ static void __usb_hcd_giveback_urb(struct urb *urb)
struct usb_hcd *hcd = bus_to_hcd(urb->dev->bus);
struct usb_anchor *anchor = urb->anchor;
int status = urb->unlinked;
+ unsigned long flags;
urb->hcpriv = NULL;
if (unlikely((urb->transfer_flags & URB_SHORT_NOT_OK) &&
@@ -1640,13 +1641,14 @@ static void __usb_hcd_giveback_urb(struct urb *urb)
/* pass ownership to the completion handler */
urb->status = status;
/*
- * This function can be called in task context inside another remote
- * coverage collection section, but kcov doesn't support that kind of
- * recursion yet. Only collect coverage in softirq context for now.
+ * Only collect coverage in the softirq context and disable interrupts
+ * to avoid scenarios with nested remote coverage collection sections
+ * that KCOV does not support.
+ * See the comment next to kcov_remote_start_usb_softirq() for details.
*/
- kcov_remote_start_usb_softirq((u64)urb->dev->bus->busnum);
+ flags = kcov_remote_start_usb_softirq((u64)urb->dev->bus->busnum);
urb->complete(urb);
- kcov_remote_stop_softirq();
+ kcov_remote_stop_softirq(flags);
usb_anchor_resume_wakeups(anchor);
atomic_dec(&urb->use_count);
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 008053039875..4b93c0bd1d4b 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -23,7 +23,7 @@
#include <linux/usb.h>
#include <linux/usbdevice_fs.h>
#include <linux/usb/hcd.h>
-#include <linux/usb/onboard_hub.h>
+#include <linux/usb/onboard_dev.h>
#include <linux/usb/otg.h>
#include <linux/usb/quirks.h>
#include <linux/workqueue.h>
@@ -1814,7 +1814,7 @@ static void hub_disconnect(struct usb_interface *intf)
if (hub->quirk_disable_autosuspend)
usb_autopm_put_interface(intf);
- onboard_hub_destroy_pdevs(&hub->onboard_hub_devs);
+ onboard_dev_destroy_pdevs(&hub->onboard_devs);
hub_put(hub);
}
@@ -1933,7 +1933,7 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
INIT_DELAYED_WORK(&hub->leds, led_work);
INIT_DELAYED_WORK(&hub->init_work, NULL);
INIT_WORK(&hub->events, hub_event);
- INIT_LIST_HEAD(&hub->onboard_hub_devs);
+ INIT_LIST_HEAD(&hub->onboard_devs);
spin_lock_init(&hub->irq_urb_lock);
timer_setup(&hub->irq_urb_retry, hub_retry_irq_urb, 0);
usb_get_intf(intf);
@@ -1963,7 +1963,7 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
}
if (hub_configure(hub, &desc->endpoint[0].desc) >= 0) {
- onboard_hub_create_pdevs(hdev, &hub->onboard_hub_devs);
+ onboard_dev_create_pdevs(hdev, &hub->onboard_devs);
return 0;
}
@@ -2207,13 +2207,12 @@ static void choose_devnum(struct usb_device *udev)
mutex_lock(&bus->devnum_next_mutex);
/* Try to allocate the next devnum beginning at bus->devnum_next. */
- devnum = find_next_zero_bit(bus->devmap.devicemap, 128,
- bus->devnum_next);
+ devnum = find_next_zero_bit(bus->devmap, 128, bus->devnum_next);
if (devnum >= 128)
- devnum = find_next_zero_bit(bus->devmap.devicemap, 128, 1);
+ devnum = find_next_zero_bit(bus->devmap, 128, 1);
bus->devnum_next = (devnum >= 127 ? 1 : devnum + 1);
if (devnum < 128) {
- set_bit(devnum, bus->devmap.devicemap);
+ set_bit(devnum, bus->devmap);
udev->devnum = devnum;
}
mutex_unlock(&bus->devnum_next_mutex);
@@ -2222,7 +2221,7 @@ static void choose_devnum(struct usb_device *udev)
static void release_devnum(struct usb_device *udev)
{
if (udev->devnum > 0) {
- clear_bit(udev->devnum, udev->bus->devmap.devicemap);
+ clear_bit(udev->devnum, udev->bus->devmap);
udev->devnum = -1;
}
}
diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h
index 183b69dc2955..e6ae73f8a95d 100644
--- a/drivers/usb/core/hub.h
+++ b/drivers/usb/core/hub.h
@@ -74,7 +74,7 @@ struct usb_hub {
spinlock_t irq_urb_lock;
struct timer_list irq_urb_retry;
struct usb_port **ports;
- struct list_head onboard_hub_devs;
+ struct list_head onboard_devs;
};
/**
diff --git a/drivers/usb/core/of.c b/drivers/usb/core/of.c
index f1a499ee482c..763e4122ed5b 100644
--- a/drivers/usb/core/of.c
+++ b/drivers/usb/core/of.c
@@ -84,9 +84,12 @@ static bool usb_of_has_devices_or_graph(const struct usb_device *hub)
if (of_graph_is_present(np))
return true;
- for_each_child_of_node(np, child)
- if (of_property_present(child, "reg"))
+ for_each_child_of_node(np, child) {
+ if (of_property_present(child, "reg")) {
+ of_node_put(child);
return true;
+ }
+ }
return false;
}
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index b4783574b8e6..13171454f959 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -506,6 +506,9 @@ static const struct usb_device_id usb_quirk_list[] = {
{ USB_DEVICE(0x1b1c, 0x1b38), .driver_info = USB_QUIRK_DELAY_INIT |
USB_QUIRK_DELAY_CTRL_MSG },
+ /* START BP-850k Printer */
+ { USB_DEVICE(0x1bc3, 0x0003), .driver_info = USB_QUIRK_NO_SET_INTF },
+
/* MIDI keyboard WORLDE MINI */
{ USB_DEVICE(0x1c75, 0x0204), .driver_info =
USB_QUIRK_CONFIG_INTF_STRINGS },
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c
index d83231d6736a..61b6d978892c 100644
--- a/drivers/usb/core/sysfs.c
+++ b/drivers/usb/core/sysfs.c
@@ -670,6 +670,7 @@ static int add_power_attributes(struct device *dev)
static void remove_power_attributes(struct device *dev)
{
+ sysfs_unmerge_group(&dev->kobj, &usb3_hardware_lpm_attr_group);
sysfs_unmerge_group(&dev->kobj, &usb2_hardware_lpm_attr_group);
sysfs_unmerge_group(&dev->kobj, &power_attr_group);
}
diff --git a/drivers/usb/core/usb-acpi.c b/drivers/usb/core/usb-acpi.c
index 7f8a912d4fe2..21585ed89ef8 100644
--- a/drivers/usb/core/usb-acpi.c
+++ b/drivers/usb/core/usb-acpi.c
@@ -142,6 +142,53 @@ int usb_acpi_set_power_state(struct usb_device *hdev, int index, bool enable)
}
EXPORT_SYMBOL_GPL(usb_acpi_set_power_state);
+/**
+ * usb_acpi_add_usb4_devlink - add device link to USB4 Host Interface for tunneled USB3 devices
+ *
+ * @udev: Tunneled USB3 device connected to a roothub.
+ *
+ * Adds a device link between a tunneled USB3 device and the USB4 Host Interface
+ * device to ensure correct runtime PM suspend and resume order. This function
+ * should only be called for tunneled USB3 devices.
+ * The USB4 Host Interface this tunneled device depends on is found from the roothub
+ * port ACPI device specific data _DSD entry.
+ *
+ * Return: negative error code on failure, 0 otherwise
+ */
+static int usb_acpi_add_usb4_devlink(struct usb_device *udev)
+{
+ const struct device_link *link;
+ struct usb_port *port_dev;
+ struct usb_hub *hub;
+
+ if (!udev->parent || udev->parent->parent)
+ return 0;
+
+ hub = usb_hub_to_struct_hub(udev->parent);
+ port_dev = hub->ports[udev->portnum - 1];
+
+ struct fwnode_handle *nhi_fwnode __free(fwnode_handle) =
+ fwnode_find_reference(dev_fwnode(&port_dev->dev), "usb4-host-interface", 0);
+
+ if (IS_ERR(nhi_fwnode))
+ return 0;
+
+ link = device_link_add(&port_dev->child->dev, nhi_fwnode->dev,
+ DL_FLAG_AUTOREMOVE_CONSUMER |
+ DL_FLAG_RPM_ACTIVE |
+ DL_FLAG_PM_RUNTIME);
+ if (!link) {
+ dev_err(&port_dev->dev, "Failed to created device link from %s to %s\n",
+ dev_name(&port_dev->child->dev), dev_name(nhi_fwnode->dev));
+ return -EINVAL;
+ }
+
+ dev_dbg(&port_dev->dev, "Created device link from %s to %s\n",
+ dev_name(&port_dev->child->dev), dev_name(nhi_fwnode->dev));
+
+ return 0;
+}
+
/*
* Private to usb-acpi, all the core needs to know is that
* port_dev->location is non-zero when it has been set by the firmware.
@@ -262,6 +309,12 @@ usb_acpi_find_companion_for_device(struct usb_device *udev)
if (!hub)
return NULL;
+
+ /* Tunneled USB3 devices depend on USB4 Host Interface, set device link to it */
+ if (udev->speed >= USB_SPEED_SUPER &&
+ udev->tunnel_mode != USB_LINK_NATIVE)
+ usb_acpi_add_usb4_devlink(udev);
+
/*
* This is an embedded USB device connected to a port and such
* devices share port's ACPI companion.
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index a0c432b14b20..0b4685aad2d5 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -1150,4 +1150,5 @@ static void __exit usb_exit(void)
subsys_initcall(usb_init);
module_exit(usb_exit);
+MODULE_DESCRIPTION("USB core host-side support");
MODULE_LICENSE("GPL");
diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c
index 5635e4d7ec88..9919ab725d54 100644
--- a/drivers/usb/dwc2/core.c
+++ b/drivers/usb/dwc2/core.c
@@ -249,6 +249,11 @@ void dwc2_hib_restore_common(struct dwc2_hsotg *hsotg, int rem_wakeup,
dwc2_writel(hsotg, gpwrdn, GPWRDN);
udelay(10);
+ /* Reset ULPI latch */
+ gpwrdn = dwc2_readl(hsotg, GPWRDN);
+ gpwrdn &= ~GPWRDN_ULPI_LATCH_EN_DURING_HIB_ENTRY;
+ dwc2_writel(hsotg, gpwrdn, GPWRDN);
+
/* Disable PMU interrupt */
gpwrdn = dwc2_readl(hsotg, GPWRDN);
gpwrdn &= ~GPWRDN_PMUINTSEL;
@@ -975,6 +980,41 @@ void dwc2_init_fs_ls_pclk_sel(struct dwc2_hsotg *hsotg)
dwc2_writel(hsotg, hcfg, HCFG);
}
+static void dwc2_set_clock_switch_timer(struct dwc2_hsotg *hsotg)
+{
+ u32 grstctl, gsnpsid, val = 0;
+
+ gsnpsid = dwc2_readl(hsotg, GSNPSID);
+
+ /*
+ * Applicable only to HSOTG core v5.00a or higher.
+ * Not applicable to HS/FS IOT devices.
+ */
+ if ((gsnpsid & ~DWC2_CORE_REV_MASK) != DWC2_OTG_ID ||
+ gsnpsid < DWC2_CORE_REV_5_00a)
+ return;
+
+ if ((hsotg->hw_params.hs_phy_type == GHWCFG2_HS_PHY_TYPE_UTMI &&
+ hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_NOT_SUPPORTED) ||
+ (hsotg->hw_params.hs_phy_type == GHWCFG2_HS_PHY_TYPE_ULPI &&
+ hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_NOT_SUPPORTED) ||
+ (hsotg->hw_params.hs_phy_type == GHWCFG2_HS_PHY_TYPE_NOT_SUPPORTED &&
+ hsotg->hw_params.fs_phy_type != GHWCFG2_FS_PHY_TYPE_NOT_SUPPORTED)) {
+ val = GRSTCTL_CLOCK_SWITH_TIMER_VALUE_DIS;
+ }
+
+ if (hsotg->params.speed == DWC2_SPEED_PARAM_LOW &&
+ hsotg->hw_params.hs_phy_type != GHWCFG2_HS_PHY_TYPE_NOT_SUPPORTED &&
+ hsotg->hw_params.fs_phy_type != GHWCFG2_FS_PHY_TYPE_NOT_SUPPORTED) {
+ val = GRSTCTL_CLOCK_SWITH_TIMER_VALUE_147;
+ }
+
+ grstctl = dwc2_readl(hsotg, GRSTCTL);
+ grstctl &= ~GRSTCTL_CLOCK_SWITH_TIMER_MASK;
+ grstctl |= GRSTCTL_CLOCK_SWITH_TIMER(val);
+ dwc2_writel(hsotg, grstctl, GRSTCTL);
+}
+
static int dwc2_fs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
{
u32 usbcfg, ggpio, i2cctl;
@@ -992,6 +1032,8 @@ static int dwc2_fs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
usbcfg |= GUSBCFG_PHYSEL;
dwc2_writel(hsotg, usbcfg, GUSBCFG);
+ dwc2_set_clock_switch_timer(hsotg);
+
/* Reset after a PHY select */
retval = dwc2_core_reset(hsotg, false);
diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h
index a141f83aba0c..2bd74f3033ed 100644
--- a/drivers/usb/dwc2/core.h
+++ b/drivers/usb/dwc2/core.h
@@ -288,6 +288,11 @@ enum dwc2_ep0_state {
* core has been configured to work at either data path
* width.
* 8 or 16 (default 16 if available)
+ * @eusb2_disc: Specifies whether eUSB2 PHY disconnect support flow
+ * applicable or no. Applicable in device mode of HSOTG
+ * and HS IOT cores v5.00 or higher.
+ * 0 - eUSB2 PHY disconnect support flow not applicable
+ * 1 - eUSB2 PHY disconnect support flow applicable
* @phy_ulpi_ddr: Specifies whether the ULPI operates at double or single
* data rate. This parameter is only applicable if phy_type
* is ULPI.
@@ -442,6 +447,7 @@ struct dwc2_core_params {
#define DWC2_SPEED_PARAM_LOW 2
u8 phy_utmi_width;
+ bool eusb2_disc;
bool phy_ulpi_ddr;
bool phy_ulpi_ext_vbus;
bool enable_dynamic_fifo;
@@ -1110,8 +1116,10 @@ struct dwc2_hsotg {
#define DWC2_CORE_REV_3_10a 0x4f54310a
#define DWC2_CORE_REV_4_00a 0x4f54400a
#define DWC2_CORE_REV_4_20a 0x4f54420a
+#define DWC2_CORE_REV_5_00a 0x4f54500a
#define DWC2_FS_IOT_REV_1_00a 0x5531100a
#define DWC2_HS_IOT_REV_1_00a 0x5532100a
+#define DWC2_HS_IOT_REV_5_00a 0x5532500a
#define DWC2_CORE_REV_MASK 0x0000ffff
/* DWC OTG HW Core ID */
diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c
index 26d752a4c3ca..7d3e641806f8 100644
--- a/drivers/usb/dwc2/core_intr.c
+++ b/drivers/usb/dwc2/core_intr.c
@@ -84,6 +84,7 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
u32 gotgint;
u32 gotgctl;
u32 gintmsk;
+ u32 pcgctl;
gotgint = dwc2_readl(hsotg, GOTGINT);
gotgctl = dwc2_readl(hsotg, GOTGCTL);
@@ -96,8 +97,22 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
dwc2_op_state_str(hsotg));
gotgctl = dwc2_readl(hsotg, GOTGCTL);
- if (dwc2_is_device_mode(hsotg))
+ if (dwc2_is_device_mode(hsotg)) {
+ if (hsotg->params.eusb2_disc) {
+ /* Clear the Gate hclk. */
+ pcgctl = dwc2_readl(hsotg, PCGCTL);
+ pcgctl &= ~PCGCTL_GATEHCLK;
+ dwc2_writel(hsotg, pcgctl, PCGCTL);
+ udelay(5);
+
+ /* Clear Phy Clock bit. */
+ pcgctl = dwc2_readl(hsotg, PCGCTL);
+ pcgctl &= ~PCGCTL_STOPPCLK;
+ dwc2_writel(hsotg, pcgctl, PCGCTL);
+ udelay(5);
+ }
dwc2_hsotg_disconnect(hsotg);
+ }
if (hsotg->op_state == OTG_STATE_B_HOST) {
hsotg->op_state = OTG_STATE_B_PERIPHERAL;
@@ -117,7 +132,7 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
* disconnected
*/
/* Reset to a clean state */
- hsotg->lx_state = DWC2_L0;
+ hsotg->lx_state = DWC2_L3;
}
gotgctl = dwc2_readl(hsotg, GOTGCTL);
@@ -286,7 +301,7 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg)
hsotg->lx_state);
if (dwc2_is_device_mode(hsotg)) {
- if (hsotg->lx_state == DWC2_L2) {
+ if (hsotg->lx_state != DWC2_L0) {
if (hsotg->in_ppd) {
ret = dwc2_exit_partial_power_down(hsotg, 0,
true);
@@ -714,6 +729,11 @@ static inline void dwc_handle_gpwrdn_disc_det(struct dwc2_hsotg *hsotg,
gpwrdn_tmp &= ~GPWRDN_PMUINTSEL;
dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN);
+ /* Reset ULPI latch */
+ gpwrdn = dwc2_readl(hsotg, GPWRDN);
+ gpwrdn &= ~GPWRDN_ULPI_LATCH_EN_DURING_HIB_ENTRY;
+ dwc2_writel(hsotg, gpwrdn, GPWRDN);
+
/* De-assert Wakeup Logic */
gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN);
gpwrdn_tmp &= ~GPWRDN_PMUACTV;
diff --git a/drivers/usb/dwc2/debugfs.c b/drivers/usb/dwc2/debugfs.c
index 1d72ece9cfe4..3116ac72747f 100644
--- a/drivers/usb/dwc2/debugfs.c
+++ b/drivers/usb/dwc2/debugfs.c
@@ -686,6 +686,7 @@ static int params_show(struct seq_file *seq, void *v)
print_param(seq, p, host_channels);
print_param(seq, p, phy_type);
print_param(seq, p, phy_utmi_width);
+ print_param(seq, p, eusb2_disc);
print_param(seq, p, phy_ulpi_ddr);
print_param(seq, p, phy_ulpi_ext_vbus);
print_param(seq, p, i2c_enable);
@@ -701,6 +702,7 @@ static int params_show(struct seq_file *seq, void *v)
print_param(seq, p, uframe_sched);
print_param(seq, p, external_id_pin_ctl);
print_param(seq, p, power_down);
+ print_param(seq, p, no_clock_gating);
print_param(seq, p, lpm);
print_param(seq, p, lpm_clock_gating);
print_param(seq, p, besl);
diff --git a/drivers/usb/dwc2/drd.c b/drivers/usb/dwc2/drd.c
index a8605b02115b..1ad8fa3f862a 100644
--- a/drivers/usb/dwc2/drd.c
+++ b/drivers/usb/dwc2/drd.c
@@ -127,6 +127,15 @@ static int dwc2_drd_role_sw_set(struct usb_role_switch *sw, enum usb_role role)
role = USB_ROLE_DEVICE;
}
+ if ((IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) ||
+ IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)) &&
+ dwc2_is_device_mode(hsotg) &&
+ hsotg->lx_state == DWC2_L2 &&
+ hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_NONE &&
+ hsotg->bus_suspended &&
+ !hsotg->params.no_clock_gating)
+ dwc2_gadget_exit_clock_gating(hsotg, 0);
+
if (role == USB_ROLE_HOST) {
already = dwc2_ovr_avalid(hsotg, true);
} else if (role == USB_ROLE_DEVICE) {
diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c
index b2f6da5b65cc..e7bf9cc635be 100644
--- a/drivers/usb/dwc2/gadget.c
+++ b/drivers/usb/dwc2/gadget.c
@@ -885,10 +885,10 @@ static void dwc2_gadget_config_nonisoc_xfer_ddma(struct dwc2_hsotg_ep *hs_ep,
}
/* DMA sg buffer */
- for_each_sg(ureq->sg, sg, ureq->num_sgs, i) {
+ for_each_sg(ureq->sg, sg, ureq->num_mapped_sgs, i) {
dwc2_gadget_fill_nonisoc_xfer_ddma_one(hs_ep, &desc,
sg_dma_address(sg) + sg->offset, sg_dma_len(sg),
- sg_is_last(sg));
+ (i == (ureq->num_mapped_sgs - 1)));
desc_count += hs_ep->desc_count;
}
@@ -3424,8 +3424,11 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg,
dwc2_hsotg_init_fifo(hsotg);
- if (!is_usb_reset)
+ if (!is_usb_reset) {
dwc2_set_bit(hsotg, DCTL, DCTL_SFTDISCON);
+ if (hsotg->params.eusb2_disc)
+ dwc2_set_bit(hsotg, GOTGCTL, GOTGCTL_EUSB2_DISC_SUPP);
+ }
dcfg |= DCFG_EPMISCNT(1);
@@ -5316,6 +5319,8 @@ void dwc2_gadget_program_ref_clk(struct dwc2_hsotg *hsotg)
int dwc2_gadget_enter_hibernation(struct dwc2_hsotg *hsotg)
{
u32 gpwrdn;
+ u32 gusbcfg;
+ u32 pcgcctl;
int ret = 0;
/* Change to L2(suspend) state */
@@ -5335,6 +5340,22 @@ int dwc2_gadget_enter_hibernation(struct dwc2_hsotg *hsotg)
}
gpwrdn = GPWRDN_PWRDNRSTN;
+ udelay(10);
+ gusbcfg = dwc2_readl(hsotg, GUSBCFG);
+ if (gusbcfg & GUSBCFG_ULPI_UTMI_SEL) {
+ /* ULPI interface */
+ gpwrdn |= GPWRDN_ULPI_LATCH_EN_DURING_HIB_ENTRY;
+ }
+ dwc2_writel(hsotg, gpwrdn, GPWRDN);
+ udelay(10);
+
+ /* Suspend the Phy Clock */
+ pcgcctl = dwc2_readl(hsotg, PCGCTL);
+ pcgcctl |= PCGCTL_STOPPCLK;
+ dwc2_writel(hsotg, pcgcctl, PCGCTL);
+ udelay(10);
+
+ gpwrdn = dwc2_readl(hsotg, GPWRDN);
gpwrdn |= GPWRDN_PMUACTV;
dwc2_writel(hsotg, gpwrdn, GPWRDN);
udelay(10);
@@ -5435,6 +5456,11 @@ int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg,
if (reset)
dwc2_clear_bit(hsotg, DCFG, DCFG_DEVADDR_MASK);
+ /* Reset ULPI latch */
+ gpwrdn = dwc2_readl(hsotg, GPWRDN);
+ gpwrdn &= ~GPWRDN_ULPI_LATCH_EN_DURING_HIB_ENTRY;
+ dwc2_writel(hsotg, gpwrdn, GPWRDN);
+
/* De-assert Wakeup Logic */
gpwrdn = dwc2_readl(hsotg, GPWRDN);
gpwrdn &= ~GPWRDN_PMUACTV;
diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index dd5b1c5691e1..cb54390e7de4 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -5525,6 +5525,11 @@ int dwc2_host_enter_hibernation(struct dwc2_hsotg *hsotg)
gusbcfg = dwc2_readl(hsotg, GUSBCFG);
if (gusbcfg & GUSBCFG_ULPI_UTMI_SEL) {
/* ULPI interface */
+ udelay(10);
+ gpwrdn = dwc2_readl(hsotg, GPWRDN);
+ gpwrdn |= GPWRDN_ULPI_LATCH_EN_DURING_HIB_ENTRY;
+ dwc2_writel(hsotg, gpwrdn, GPWRDN);
+ udelay(10);
/* Suspend the Phy Clock */
pcgcctl = dwc2_readl(hsotg, PCGCTL);
pcgcctl |= PCGCTL_STOPPCLK;
@@ -5631,6 +5636,11 @@ int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup,
dwc2_writel(hsotg, gr->gusbcfg, GUSBCFG);
dwc2_writel(hsotg, hr->hcfg, HCFG);
+ /* Reset ULPI latch */
+ gpwrdn = dwc2_readl(hsotg, GPWRDN);
+ gpwrdn &= ~GPWRDN_ULPI_LATCH_EN_DURING_HIB_ENTRY;
+ dwc2_writel(hsotg, gpwrdn, GPWRDN);
+
/* De-assert Wakeup Logic */
if (!(rem_wakeup && hsotg->hw_params.snpsid >= DWC2_CORE_REV_4_30a)) {
gpwrdn = dwc2_readl(hsotg, GPWRDN);
diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c
index 0d4495c6b9f7..238c6fd50e75 100644
--- a/drivers/usb/dwc2/hcd_queue.c
+++ b/drivers/usb/dwc2/hcd_queue.c
@@ -16,6 +16,7 @@
#include <linux/interrupt.h>
#include <linux/dma-mapping.h>
#include <linux/io.h>
+#include <linux/seq_buf.h>
#include <linux/slab.h>
#include <linux/usb.h>
@@ -360,41 +361,6 @@ static unsigned long *dwc2_get_ls_map(struct dwc2_hsotg *hsotg,
#ifdef DWC2_PRINT_SCHEDULE
/*
- * cat_printf() - A printf() + strcat() helper
- *
- * This is useful for concatenating a bunch of strings where each string is
- * constructed using printf.
- *
- * @buf: The destination buffer; will be updated to point after the printed
- * data.
- * @size: The number of bytes in the buffer (includes space for '\0').
- * @fmt: The format for printf.
- * @...: The args for printf.
- */
-static __printf(3, 4)
-void cat_printf(char **buf, size_t *size, const char *fmt, ...)
-{
- va_list args;
- int i;
-
- if (*size == 0)
- return;
-
- va_start(args, fmt);
- i = vsnprintf(*buf, *size, fmt, args);
- va_end(args);
-
- if (i >= *size) {
- (*buf)[*size - 1] = '\0';
- *buf += *size;
- *size = 0;
- } else {
- *buf += i;
- *size -= i;
- }
-}
-
-/*
* pmap_print() - Print the given periodic map
*
* Will attempt to print out the periodic schedule.
@@ -416,9 +382,7 @@ static void pmap_print(unsigned long *map, int bits_per_period,
int period;
for (period = 0; period < periods_in_map; period++) {
- char tmp[64];
- char *buf = tmp;
- size_t buf_size = sizeof(tmp);
+ DECLARE_SEQ_BUF(buf, 64);
int period_start = period * bits_per_period;
int period_end = period_start + bits_per_period;
int start = 0;
@@ -442,19 +406,19 @@ static void pmap_print(unsigned long *map, int bits_per_period,
continue;
if (!printed)
- cat_printf(&buf, &buf_size, "%s %d: ",
- period_name, period);
+ seq_buf_printf(&buf, "%s %d: ",
+ period_name, period);
else
- cat_printf(&buf, &buf_size, ", ");
+ seq_buf_puts(&buf, ", ");
printed = true;
- cat_printf(&buf, &buf_size, "%d %s -%3d %s", start,
- units, start + count - 1, units);
+ seq_buf_printf(&buf, "%d %s -%3d %s", start,
+ units, start + count - 1, units);
count = 0;
}
if (printed)
- print_fn(tmp, print_data);
+ print_fn(seq_buf_str(&buf), print_data);
}
}
diff --git a/drivers/usb/dwc2/hw.h b/drivers/usb/dwc2/hw.h
index 12f8c7f86dc9..fe8c87a2f8b5 100644
--- a/drivers/usb/dwc2/hw.h
+++ b/drivers/usb/dwc2/hw.h
@@ -11,6 +11,7 @@
#define HSOTG_REG(x) (x)
#define GOTGCTL HSOTG_REG(0x000)
+#define GOTGCTL_EUSB2_DISC_SUPP BIT(28)
#define GOTGCTL_CHIRPEN BIT(27)
#define GOTGCTL_MULT_VALID_BC_MASK (0x1f << 22)
#define GOTGCTL_MULT_VALID_BC_SHIFT 22
@@ -98,6 +99,17 @@
#define GRSTCTL_AHBIDLE BIT(31)
#define GRSTCTL_DMAREQ BIT(30)
#define GRSTCTL_CSFTRST_DONE BIT(29)
+#define GRSTCTL_CLOCK_SWITH_TIMER_MASK (0x7 << 11)
+#define GRSTCTL_CLOCK_SWITH_TIMER_SHIFT 11
+#define GRSTCTL_CLOCK_SWITH_TIMER_VALUE_19 0x0
+#define GRSTCTL_CLOCK_SWITH_TIMER_VALUE_15 0x1
+#define GRSTCTL_CLOCK_SWITH_TIMER_VALUE_147 0x2
+#define GRSTCTL_CLOCK_SWITH_TIMER_VALUE_50 0x3
+#define GRSTCTL_CLOCK_SWITH_TIMER_VALUE_100 0x4
+#define GRSTCTL_CLOCK_SWITH_TIMER_VALUE_125 0x5
+#define GRSTCTL_CLOCK_SWITH_TIMER_VALUE_200 0x6
+#define GRSTCTL_CLOCK_SWITH_TIMER_VALUE_DIS 0x7
+#define GRSTCTL_CLOCK_SWITH_TIMER(_x) ((_x) << 11)
#define GRSTCTL_TXFNUM_MASK (0x1f << 6)
#define GRSTCTL_TXFNUM_SHIFT 6
#define GRSTCTL_TXFNUM_LIMIT 0x1f
@@ -332,6 +344,8 @@
#define GLPMCFG_LPMCAP BIT(0)
#define GPWRDN HSOTG_REG(0x0058)
+
+#define GPWRDN_ULPI_LATCH_EN_DURING_HIB_ENTRY BIT(29)
#define GPWRDN_MULT_VAL_ID_BC_MASK (0x1f << 24)
#define GPWRDN_MULT_VAL_ID_BC_SHIFT 24
#define GPWRDN_ADP_INT BIT(23)
diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c
index eb677c3cfd0b..68226defdc60 100644
--- a/drivers/usb/dwc2/params.c
+++ b/drivers/usb/dwc2/params.c
@@ -23,6 +23,7 @@ static void dwc2_set_bcm_params(struct dwc2_hsotg *hsotg)
p->max_transfer_size = 65535;
p->max_packet_count = 511;
p->ahbcfg = 0x10;
+ p->no_clock_gating = true;
}
static void dwc2_set_his_params(struct dwc2_hsotg *hsotg)
@@ -133,7 +134,15 @@ static void dwc2_set_rk_params(struct dwc2_hsotg *hsotg)
p->no_clock_gating = true;
}
-static void dwc2_set_ltq_params(struct dwc2_hsotg *hsotg)
+static void dwc2_set_ltq_danube_params(struct dwc2_hsotg *hsotg)
+{
+ struct dwc2_core_params *p = &hsotg->params;
+
+ p->otg_caps.hnp_support = false;
+ p->otg_caps.srp_support = false;
+}
+
+static void dwc2_set_ltq_ase_params(struct dwc2_hsotg *hsotg)
{
struct dwc2_core_params *p = &hsotg->params;
@@ -142,12 +151,21 @@ static void dwc2_set_ltq_params(struct dwc2_hsotg *hsotg)
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_ltq_xrx200_params(struct dwc2_hsotg *hsotg)
+{
+ struct dwc2_core_params *p = &hsotg->params;
+
+ p->otg_caps.hnp_support = false;
+ p->otg_caps.srp_support = false;
+ p->host_rx_fifo_size = 288;
+ p->host_nperio_tx_fifo_size = 128;
+ p->host_perio_tx_fifo_size = 136;
+}
+
static void dwc2_set_amlogic_params(struct dwc2_hsotg *hsotg)
{
struct dwc2_core_params *p = &hsotg->params;
@@ -201,6 +219,25 @@ static void dwc2_set_amcc_params(struct dwc2_hsotg *hsotg)
p->ahbcfg = GAHBCFG_HBSTLEN_INCR16 << GAHBCFG_HBSTLEN_SHIFT;
}
+static void dwc2_set_cv1800_params(struct dwc2_hsotg *hsotg)
+{
+ struct dwc2_core_params *p = &hsotg->params;
+
+ p->otg_caps.hnp_support = false;
+ p->otg_caps.srp_support = false;
+ p->host_dma = false;
+ p->g_dma = false;
+ p->speed = DWC2_SPEED_PARAM_HIGH;
+ p->phy_type = DWC2_PHY_TYPE_PARAM_UTMI;
+ p->phy_utmi_width = 16;
+ p->ahbcfg = GAHBCFG_HBSTLEN_INCR16 << GAHBCFG_HBSTLEN_SHIFT;
+ p->lpm = false;
+ p->lpm_clock_gating = false;
+ p->besl = false;
+ p->hird_threshold_en = false;
+ p->power_down = DWC2_POWER_DOWN_PARAM_NONE;
+}
+
static void dwc2_set_stm32f4x9_fsotg_params(struct dwc2_hsotg *hsotg)
{
struct dwc2_core_params *p = &hsotg->params;
@@ -278,8 +315,11 @@ const struct of_device_id dwc2_of_match_table[] = {
{ .compatible = "ingenic,x1830-otg", .data = dwc2_set_x1600_params },
{ .compatible = "ingenic,x2000-otg", .data = dwc2_set_x2000_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 = "lantiq,danube-usb", .data = &dwc2_set_ltq_danube_params },
+ { .compatible = "lantiq,ase-usb", .data = &dwc2_set_ltq_ase_params },
+ { .compatible = "lantiq,arx100-usb", .data = &dwc2_set_ltq_ase_params },
+ { .compatible = "lantiq,xrx200-usb", .data = &dwc2_set_ltq_xrx200_params },
+ { .compatible = "lantiq,xrx300-usb", .data = &dwc2_set_ltq_xrx200_params },
{ .compatible = "snps,dwc2" },
{ .compatible = "samsung,s3c6400-hsotg",
.data = dwc2_set_s3c6400_params },
@@ -295,6 +335,8 @@ const struct of_device_id dwc2_of_match_table[] = {
.data = dwc2_set_amlogic_a1_params },
{ .compatible = "amcc,dwc-otg", .data = dwc2_set_amcc_params },
{ .compatible = "apm,apm82181-dwc-otg", .data = dwc2_set_amcc_params },
+ { .compatible = "sophgo,cv1800-usb",
+ .data = dwc2_set_cv1800_params },
{ .compatible = "st,stm32f4x9-fsotg",
.data = dwc2_set_stm32f4x9_fsotg_params },
{ .compatible = "st,stm32f4x9-hsotg" },
@@ -311,6 +353,7 @@ const struct of_device_id dwc2_of_match_table[] = {
MODULE_DEVICE_TABLE(of, dwc2_of_match_table);
const struct acpi_device_id dwc2_acpi_match[] = {
+ /* This ID refers to the same USB IP as of_device_id brcm,bcm2835-usb */
{ "BCM2848", (kernel_ulong_t)dwc2_set_bcm_params },
{ },
};
@@ -475,6 +518,7 @@ static void dwc2_set_default_params(struct dwc2_hsotg *hsotg)
dwc2_set_param_lpm(hsotg);
p->phy_ulpi_ddr = false;
p->phy_ulpi_ext_vbus = false;
+ p->eusb2_disc = false;
p->enable_dynamic_fifo = hw->enable_dynamic_fifo;
p->en_multiple_tx_fifo = hw->en_multiple_tx_fifo;
@@ -737,6 +781,25 @@ static void dwc2_check_param_tx_fifo_sizes(struct dwc2_hsotg *hsotg)
}
}
+static void dwc2_check_param_eusb2_disc(struct dwc2_hsotg *hsotg)
+{
+ u32 gsnpsid;
+
+ if (!hsotg->params.eusb2_disc)
+ return;
+ gsnpsid = dwc2_readl(hsotg, GSNPSID);
+ /*
+ * eusb2_disc not supported by FS IOT devices.
+ * For other cores, it supported starting from version 5.00a
+ */
+ if ((gsnpsid & ~DWC2_CORE_REV_MASK) == DWC2_FS_IOT_ID ||
+ (gsnpsid & DWC2_CORE_REV_MASK) <
+ (DWC2_CORE_REV_5_00a & DWC2_CORE_REV_MASK)) {
+ hsotg->params.eusb2_disc = false;
+ return;
+ }
+}
+
#define CHECK_RANGE(_param, _min, _max, _def) do { \
if ((int)(hsotg->params._param) < (_min) || \
(hsotg->params._param) > (_max)) { \
@@ -765,6 +828,8 @@ static void dwc2_check_params(struct dwc2_hsotg *hsotg)
dwc2_check_param_speed(hsotg);
dwc2_check_param_phy_utmi_width(hsotg);
dwc2_check_param_power_down(hsotg);
+ dwc2_check_param_eusb2_disc(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);
diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c
index 7b84416dfc2b..c1b7209b9483 100644
--- a/drivers/usb/dwc2/platform.c
+++ b/drivers/usb/dwc2/platform.c
@@ -469,18 +469,6 @@ static int dwc2_driver_probe(struct platform_device *dev)
spin_lock_init(&hsotg->lock);
- hsotg->irq = platform_get_irq(dev, 0);
- if (hsotg->irq < 0)
- return hsotg->irq;
-
- dev_dbg(hsotg->dev, "registering common handler for irq%d\n",
- hsotg->irq);
- retval = devm_request_irq(hsotg->dev, hsotg->irq,
- dwc2_handle_common_intr, IRQF_SHARED,
- dev_name(hsotg->dev), hsotg);
- if (retval)
- return retval;
-
hsotg->vbus_supply = devm_regulator_get_optional(hsotg->dev, "vbus");
if (IS_ERR(hsotg->vbus_supply)) {
retval = PTR_ERR(hsotg->vbus_supply);
@@ -524,6 +512,20 @@ static int dwc2_driver_probe(struct platform_device *dev)
if (retval)
goto error;
+ hsotg->irq = platform_get_irq(dev, 0);
+ if (hsotg->irq < 0) {
+ retval = hsotg->irq;
+ goto error;
+ }
+
+ dev_dbg(hsotg->dev, "registering common handler for irq%d\n",
+ hsotg->irq);
+ retval = devm_request_irq(hsotg->dev, hsotg->irq,
+ dwc2_handle_common_intr, IRQF_SHARED,
+ dev_name(hsotg->dev), hsotg);
+ if (retval)
+ goto error;
+
/*
* For OTG cores, set the force mode bits to reflect the value
* of dr_mode. Force mode bits should not be touched at any
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 100041320e8d..9eb085f359ce 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -39,6 +39,7 @@
#include "io.h"
#include "debug.h"
+#include "../host/xhci-ext-caps.h"
#define DWC3_DEFAULT_AUTOSUSPEND_DELAY 5000 /* ms */
@@ -107,22 +108,27 @@ static int dwc3_get_dr_mode(struct dwc3 *dwc)
void dwc3_enable_susphy(struct dwc3 *dwc, bool enable)
{
u32 reg;
+ int i;
- reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
- if (enable && !dwc->dis_u3_susphy_quirk)
- reg |= DWC3_GUSB3PIPECTL_SUSPHY;
- else
- reg &= ~DWC3_GUSB3PIPECTL_SUSPHY;
+ for (i = 0; i < dwc->num_usb3_ports; i++) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(i));
+ if (enable && !dwc->dis_u3_susphy_quirk)
+ reg |= DWC3_GUSB3PIPECTL_SUSPHY;
+ else
+ reg &= ~DWC3_GUSB3PIPECTL_SUSPHY;
- dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
+ dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(i), reg);
+ }
- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
- if (enable && !dwc->dis_u2_susphy_quirk)
- reg |= DWC3_GUSB2PHYCFG_SUSPHY;
- else
- reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
+ for (i = 0; i < dwc->num_usb2_ports; i++) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(i));
+ if (enable && !dwc->dis_u2_susphy_quirk)
+ reg |= DWC3_GUSB2PHYCFG_SUSPHY;
+ else
+ reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+ dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(i), reg);
+ }
}
void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode)
@@ -144,6 +150,7 @@ static void __dwc3_set_mode(struct work_struct *work)
int ret;
u32 reg;
u32 desired_dr_role;
+ int i;
mutex_lock(&dwc->mutex);
spin_lock_irqsave(&dwc->lock, flags);
@@ -221,8 +228,12 @@ static void __dwc3_set_mode(struct work_struct *work)
} else {
if (dwc->usb2_phy)
otg_set_vbus(dwc->usb2_phy->otg, true);
- phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_HOST);
- phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_HOST);
+
+ for (i = 0; i < dwc->num_usb2_ports; i++)
+ phy_set_mode(dwc->usb2_generic_phy[i], PHY_MODE_USB_HOST);
+ for (i = 0; i < dwc->num_usb3_ports; i++)
+ phy_set_mode(dwc->usb3_generic_phy[i], PHY_MODE_USB_HOST);
+
if (dwc->dis_split_quirk) {
reg = dwc3_readl(dwc->regs, DWC3_GUCTL3);
reg |= DWC3_GUCTL3_SPLITDISABLE;
@@ -237,8 +248,8 @@ static void __dwc3_set_mode(struct work_struct *work)
if (dwc->usb2_phy)
otg_set_vbus(dwc->usb2_phy->otg, false);
- phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_DEVICE);
- phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_DEVICE);
+ phy_set_mode(dwc->usb2_generic_phy[0], PHY_MODE_USB_DEVICE);
+ phy_set_mode(dwc->usb3_generic_phy[0], PHY_MODE_USB_DEVICE);
ret = dwc3_gadget_init(dwc);
if (ret)
@@ -506,6 +517,13 @@ static void dwc3_free_event_buffers(struct dwc3 *dwc)
static int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned int length)
{
struct dwc3_event_buffer *evt;
+ unsigned int hw_mode;
+
+ hw_mode = DWC3_GHWPARAMS0_MODE(dwc->hwparams.hwparams0);
+ if (hw_mode == DWC3_GHWPARAMS0_MODE_HOST) {
+ dwc->ev_buf = NULL;
+ return 0;
+ }
evt = dwc3_alloc_one_event_buffer(dwc, length);
if (IS_ERR(evt)) {
@@ -527,6 +545,9 @@ int dwc3_event_buffers_setup(struct dwc3 *dwc)
{
struct dwc3_event_buffer *evt;
+ if (!dwc->ev_buf)
+ return 0;
+
evt = dwc->ev_buf;
evt->lpos = 0;
dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(0),
@@ -543,6 +564,17 @@ int dwc3_event_buffers_setup(struct dwc3 *dwc)
void dwc3_event_buffers_cleanup(struct dwc3 *dwc)
{
struct dwc3_event_buffer *evt;
+ u32 reg;
+
+ if (!dwc->ev_buf)
+ return;
+ /*
+ * Exynos platforms may not be able to access event buffer if the
+ * controller failed to halt on dwc3_core_exit().
+ */
+ reg = dwc3_readl(dwc->regs, DWC3_DSTS);
+ if (!(reg & DWC3_DSTS_DEVCTRLHLT))
+ return;
evt = dwc->ev_buf;
@@ -580,6 +612,18 @@ static void dwc3_cache_hwparams(struct dwc3 *dwc)
parms->hwparams9 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS9);
}
+static void dwc3_config_soc_bus(struct dwc3 *dwc)
+{
+ if (dwc->gsbuscfg0_reqinfo != DWC3_GSBUSCFG0_REQINFO_UNSPECIFIED) {
+ u32 reg;
+
+ reg = dwc3_readl(dwc->regs, DWC3_GSBUSCFG0);
+ reg &= ~DWC3_GSBUSCFG0_REQINFO(~0);
+ reg |= DWC3_GSBUSCFG0_REQINFO(dwc->gsbuscfg0_reqinfo);
+ dwc3_writel(dwc->regs, DWC3_GSBUSCFG0, reg);
+ }
+}
+
static int dwc3_core_ulpi_init(struct dwc3 *dwc)
{
int intf;
@@ -596,19 +640,11 @@ static int dwc3_core_ulpi_init(struct dwc3 *dwc)
return ret;
}
-/**
- * dwc3_phy_setup - Configure USB PHY Interface of DWC3 Core
- * @dwc: Pointer to our controller context structure
- *
- * Returns 0 on success. The USB PHY interfaces are configured but not
- * initialized. The PHY interfaces and the PHYs get initialized together with
- * the core in dwc3_core_init.
- */
-static int dwc3_phy_setup(struct dwc3 *dwc)
+static int dwc3_ss_phy_setup(struct dwc3 *dwc, int index)
{
u32 reg;
- reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(index));
/*
* Make sure UX_EXIT_PX is cleared as that causes issues with some
@@ -655,9 +691,16 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
if (dwc->dis_del_phy_power_chg_quirk)
reg &= ~DWC3_GUSB3PIPECTL_DEPOCHANGE;
- dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
+ dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(index), reg);
- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
+ return 0;
+}
+
+static int dwc3_hs_phy_setup(struct dwc3 *dwc, int index)
+{
+ u32 reg;
+
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(index));
/* Select the HS PHY interface */
switch (DWC3_GHWPARAMS3_HSPHY_IFC(dwc->hwparams.hwparams3)) {
@@ -669,7 +712,7 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
} else if (dwc->hsphy_interface &&
!strncmp(dwc->hsphy_interface, "ulpi", 4)) {
reg |= DWC3_GUSB2PHYCFG_ULPI_UTMI;
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+ dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(index), reg);
} else {
/* Relying on default value. */
if (!(reg & DWC3_GUSB2PHYCFG_ULPI_UTMI))
@@ -727,7 +770,35 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
if (dwc->ulpi_ext_vbus_drv)
reg |= DWC3_GUSB2PHYCFG_ULPIEXTVBUSDRV;
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+ dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(index), reg);
+
+ return 0;
+}
+
+/**
+ * dwc3_phy_setup - Configure USB PHY Interface of DWC3 Core
+ * @dwc: Pointer to our controller context structure
+ *
+ * Returns 0 on success. The USB PHY interfaces are configured but not
+ * initialized. The PHY interfaces and the PHYs get initialized together with
+ * the core in dwc3_core_init.
+ */
+static int dwc3_phy_setup(struct dwc3 *dwc)
+{
+ int i;
+ int ret;
+
+ for (i = 0; i < dwc->num_usb3_ports; i++) {
+ ret = dwc3_ss_phy_setup(dwc, i);
+ if (ret)
+ return ret;
+ }
+
+ for (i = 0; i < dwc->num_usb2_ports; i++) {
+ ret = dwc3_hs_phy_setup(dwc, i);
+ if (ret)
+ return ret;
+ }
return 0;
}
@@ -735,23 +806,34 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
static int dwc3_phy_init(struct dwc3 *dwc)
{
int ret;
+ int i;
+ int j;
usb_phy_init(dwc->usb2_phy);
usb_phy_init(dwc->usb3_phy);
- ret = phy_init(dwc->usb2_generic_phy);
- if (ret < 0)
- goto err_shutdown_usb3_phy;
+ for (i = 0; i < dwc->num_usb2_ports; i++) {
+ ret = phy_init(dwc->usb2_generic_phy[i]);
+ if (ret < 0)
+ goto err_exit_usb2_phy;
+ }
- ret = phy_init(dwc->usb3_generic_phy);
- if (ret < 0)
- goto err_exit_usb2_phy;
+ for (j = 0; j < dwc->num_usb3_ports; j++) {
+ ret = phy_init(dwc->usb3_generic_phy[j]);
+ if (ret < 0)
+ goto err_exit_usb3_phy;
+ }
return 0;
+err_exit_usb3_phy:
+ while (--j >= 0)
+ phy_exit(dwc->usb3_generic_phy[j]);
+
err_exit_usb2_phy:
- phy_exit(dwc->usb2_generic_phy);
-err_shutdown_usb3_phy:
+ while (--i >= 0)
+ phy_exit(dwc->usb2_generic_phy[i]);
+
usb_phy_shutdown(dwc->usb3_phy);
usb_phy_shutdown(dwc->usb2_phy);
@@ -760,8 +842,13 @@ err_shutdown_usb3_phy:
static void dwc3_phy_exit(struct dwc3 *dwc)
{
- phy_exit(dwc->usb3_generic_phy);
- phy_exit(dwc->usb2_generic_phy);
+ int i;
+
+ for (i = 0; i < dwc->num_usb3_ports; i++)
+ phy_exit(dwc->usb3_generic_phy[i]);
+
+ for (i = 0; i < dwc->num_usb2_ports; i++)
+ phy_exit(dwc->usb2_generic_phy[i]);
usb_phy_shutdown(dwc->usb3_phy);
usb_phy_shutdown(dwc->usb2_phy);
@@ -770,23 +857,34 @@ static void dwc3_phy_exit(struct dwc3 *dwc)
static int dwc3_phy_power_on(struct dwc3 *dwc)
{
int ret;
+ int i;
+ int j;
usb_phy_set_suspend(dwc->usb2_phy, 0);
usb_phy_set_suspend(dwc->usb3_phy, 0);
- ret = phy_power_on(dwc->usb2_generic_phy);
- if (ret < 0)
- goto err_suspend_usb3_phy;
+ for (i = 0; i < dwc->num_usb2_ports; i++) {
+ ret = phy_power_on(dwc->usb2_generic_phy[i]);
+ if (ret < 0)
+ goto err_power_off_usb2_phy;
+ }
- ret = phy_power_on(dwc->usb3_generic_phy);
- if (ret < 0)
- goto err_power_off_usb2_phy;
+ for (j = 0; j < dwc->num_usb3_ports; j++) {
+ ret = phy_power_on(dwc->usb3_generic_phy[j]);
+ if (ret < 0)
+ goto err_power_off_usb3_phy;
+ }
return 0;
+err_power_off_usb3_phy:
+ while (--j >= 0)
+ phy_power_off(dwc->usb3_generic_phy[j]);
+
err_power_off_usb2_phy:
- phy_power_off(dwc->usb2_generic_phy);
-err_suspend_usb3_phy:
+ while (--i >= 0)
+ phy_power_off(dwc->usb2_generic_phy[i]);
+
usb_phy_set_suspend(dwc->usb3_phy, 1);
usb_phy_set_suspend(dwc->usb2_phy, 1);
@@ -795,8 +893,13 @@ err_suspend_usb3_phy:
static void dwc3_phy_power_off(struct dwc3 *dwc)
{
- phy_power_off(dwc->usb3_generic_phy);
- phy_power_off(dwc->usb2_generic_phy);
+ int i;
+
+ for (i = 0; i < dwc->num_usb3_ports; i++)
+ phy_power_off(dwc->usb3_generic_phy[i]);
+
+ for (i = 0; i < dwc->num_usb2_ports; i++)
+ phy_power_off(dwc->usb2_generic_phy[i]);
usb_phy_set_suspend(dwc->usb3_phy, 1);
usb_phy_set_suspend(dwc->usb2_phy, 1);
@@ -879,12 +982,16 @@ static bool dwc3_core_is_valid(struct dwc3 *dwc)
static void dwc3_core_setup_global_control(struct dwc3 *dwc)
{
+ unsigned int power_opt;
+ unsigned int hw_mode;
u32 reg;
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
reg &= ~DWC3_GCTL_SCALEDOWN_MASK;
+ hw_mode = DWC3_GHWPARAMS0_MODE(dwc->hwparams.hwparams0);
+ power_opt = DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1);
- switch (DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1)) {
+ switch (power_opt) {
case DWC3_GHWPARAMS1_EN_PWROPT_CLK:
/**
* WORKAROUND: DWC3 revisions between 2.10a and 2.50a have an
@@ -917,6 +1024,20 @@ static void dwc3_core_setup_global_control(struct dwc3 *dwc)
break;
}
+ /*
+ * This is a workaround for STAR#4846132, which only affects
+ * DWC_usb31 version2.00a operating in host mode.
+ *
+ * There is a problem in DWC_usb31 version 2.00a operating
+ * in host mode that would cause a CSR read timeout When CSR
+ * read coincides with RAM Clock Gating Entry. By disable
+ * Clock Gating, sacrificing power consumption for normal
+ * operation.
+ */
+ if (power_opt != DWC3_GHWPARAMS1_EN_PWROPT_NO &&
+ hw_mode != DWC3_GHWPARAMS0_MODE_GADGET && DWC3_VER_IS(DWC31, 200A))
+ reg |= DWC3_GCTL_DSBLCLKGTNG;
+
/* check if current dwc3 is on simulation board */
if (dwc->hwparams.hwparams6 & DWC3_GHWPARAMS6_EN_FPGA) {
dev_info(dwc->dev, "Running with FPGA optimizations\n");
@@ -1242,6 +1363,8 @@ static int dwc3_core_init(struct dwc3 *dwc)
dwc3_set_incr_burst_type(dwc);
+ dwc3_config_soc_bus(dwc);
+
ret = dwc3_phy_power_on(dwc);
if (ret)
goto err_exit_phy;
@@ -1264,6 +1387,21 @@ static int dwc3_core_init(struct dwc3 *dwc)
}
/*
+ * STAR 9001285599: This issue affects DWC_usb3 version 3.20a
+ * only. If the PM TIMER ECM is enabled through GUCTL2[19], the
+ * link compliance test (TD7.21) may fail. If the ECN is not
+ * enabled (GUCTL2[19] = 0), the controller will use the old timer
+ * value (5us), which is still acceptable for the link compliance
+ * test. Therefore, do not enable PM TIMER ECM in 3.20a by
+ * setting GUCTL2[19] by default; instead, use GUCTL2[19] = 0.
+ */
+ if (DWC3_VER_IS(DWC3, 320A)) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUCTL2);
+ reg &= ~DWC3_GUCTL2_LC_TIMER;
+ dwc3_writel(dwc->regs, DWC3_GUCTL2, reg);
+ }
+
+ /*
* When configured in HOST mode, after issuing U3/L2 exit controller
* fails to send proper CRC checksum in CRC5 feild. Because of this
* behaviour Transaction Error is generated, resulting in reset and
@@ -1306,10 +1444,13 @@ static int dwc3_core_init(struct dwc3 *dwc)
if (dwc->parkmode_disable_hs_quirk)
reg |= DWC3_GUCTL1_PARKMODE_DISABLE_HS;
- if (DWC3_VER_IS_WITHIN(DWC3, 290A, ANY) &&
- (dwc->maximum_speed == USB_SPEED_HIGH ||
- dwc->maximum_speed == USB_SPEED_FULL))
- reg |= DWC3_GUCTL1_DEV_FORCE_20_CLK_FOR_30_CLK;
+ if (DWC3_VER_IS_WITHIN(DWC3, 290A, ANY)) {
+ if (dwc->maximum_speed == USB_SPEED_FULL ||
+ dwc->maximum_speed == USB_SPEED_HIGH)
+ reg |= DWC3_GUCTL1_DEV_FORCE_20_CLK_FOR_30_CLK;
+ else
+ reg &= ~DWC3_GUCTL1_DEV_FORCE_20_CLK_FOR_30_CLK;
+ }
dwc3_writel(dwc->regs, DWC3_GUCTL1, reg);
}
@@ -1344,7 +1485,9 @@ static int dwc3_core_get_phy(struct dwc3 *dwc)
{
struct device *dev = dwc->dev;
struct device_node *node = dev->of_node;
+ char phy_name[9];
int ret;
+ u8 i;
if (node) {
dwc->usb2_phy = devm_usb_get_phy_by_phandle(dev, "usb-phy", 0);
@@ -1370,22 +1513,38 @@ static int dwc3_core_get_phy(struct dwc3 *dwc)
return dev_err_probe(dev, ret, "no usb3 phy configured\n");
}
- dwc->usb2_generic_phy = devm_phy_get(dev, "usb2-phy");
- if (IS_ERR(dwc->usb2_generic_phy)) {
- ret = PTR_ERR(dwc->usb2_generic_phy);
- if (ret == -ENOSYS || ret == -ENODEV)
- dwc->usb2_generic_phy = NULL;
+ for (i = 0; i < dwc->num_usb2_ports; i++) {
+ if (dwc->num_usb2_ports == 1)
+ snprintf(phy_name, sizeof(phy_name), "usb2-phy");
else
- return dev_err_probe(dev, ret, "no usb2 phy configured\n");
+ snprintf(phy_name, sizeof(phy_name), "usb2-%u", i);
+
+ dwc->usb2_generic_phy[i] = devm_phy_get(dev, phy_name);
+ if (IS_ERR(dwc->usb2_generic_phy[i])) {
+ ret = PTR_ERR(dwc->usb2_generic_phy[i]);
+ if (ret == -ENOSYS || ret == -ENODEV)
+ dwc->usb2_generic_phy[i] = NULL;
+ else
+ return dev_err_probe(dev, ret, "failed to lookup phy %s\n",
+ phy_name);
+ }
}
- dwc->usb3_generic_phy = devm_phy_get(dev, "usb3-phy");
- if (IS_ERR(dwc->usb3_generic_phy)) {
- ret = PTR_ERR(dwc->usb3_generic_phy);
- if (ret == -ENOSYS || ret == -ENODEV)
- dwc->usb3_generic_phy = NULL;
+ for (i = 0; i < dwc->num_usb3_ports; i++) {
+ if (dwc->num_usb3_ports == 1)
+ snprintf(phy_name, sizeof(phy_name), "usb3-phy");
else
- return dev_err_probe(dev, ret, "no usb3 phy configured\n");
+ snprintf(phy_name, sizeof(phy_name), "usb3-%u", i);
+
+ dwc->usb3_generic_phy[i] = devm_phy_get(dev, phy_name);
+ if (IS_ERR(dwc->usb3_generic_phy[i])) {
+ ret = PTR_ERR(dwc->usb3_generic_phy[i]);
+ if (ret == -ENOSYS || ret == -ENODEV)
+ dwc->usb3_generic_phy[i] = NULL;
+ else
+ return dev_err_probe(dev, ret, "failed to lookup phy %s\n",
+ phy_name);
+ }
}
return 0;
@@ -1395,6 +1554,7 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
{
struct device *dev = dwc->dev;
int ret;
+ int i;
switch (dwc->dr_mode) {
case USB_DR_MODE_PERIPHERAL:
@@ -1402,8 +1562,8 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
if (dwc->usb2_phy)
otg_set_vbus(dwc->usb2_phy->otg, false);
- phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_DEVICE);
- phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_DEVICE);
+ phy_set_mode(dwc->usb2_generic_phy[0], PHY_MODE_USB_DEVICE);
+ phy_set_mode(dwc->usb3_generic_phy[0], PHY_MODE_USB_DEVICE);
ret = dwc3_gadget_init(dwc);
if (ret)
@@ -1414,8 +1574,10 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
if (dwc->usb2_phy)
otg_set_vbus(dwc->usb2_phy->otg, true);
- phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_HOST);
- phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_HOST);
+ for (i = 0; i < dwc->num_usb2_ports; i++)
+ phy_set_mode(dwc->usb2_generic_phy[i], PHY_MODE_USB_HOST);
+ for (i = 0; i < dwc->num_usb3_ports; i++)
+ phy_set_mode(dwc->usb3_generic_phy[i], PHY_MODE_USB_HOST);
ret = dwc3_host_init(dwc);
if (ret)
@@ -1456,6 +1618,27 @@ static void dwc3_core_exit_mode(struct dwc3 *dwc)
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE);
}
+static void dwc3_get_software_properties(struct dwc3 *dwc)
+{
+ struct device *tmpdev;
+ u16 gsbuscfg0_reqinfo;
+ int ret;
+
+ dwc->gsbuscfg0_reqinfo = DWC3_GSBUSCFG0_REQINFO_UNSPECIFIED;
+
+ /*
+ * Iterate over all parent nodes for finding swnode properties
+ * and non-DT (non-ABI) properties.
+ */
+ for (tmpdev = dwc->dev; tmpdev; tmpdev = tmpdev->parent) {
+ ret = device_property_read_u16(tmpdev,
+ "snps,gsbuscfg0-reqinfo",
+ &gsbuscfg0_reqinfo);
+ if (!ret)
+ dwc->gsbuscfg0_reqinfo = gsbuscfg0_reqinfo;
+ }
+}
+
static void dwc3_get_properties(struct dwc3 *dwc)
{
struct device *dev = dwc->dev;
@@ -1867,10 +2050,60 @@ static int dwc3_get_clocks(struct dwc3 *dwc)
return 0;
}
+static int dwc3_get_num_ports(struct dwc3 *dwc)
+{
+ void __iomem *base;
+ u8 major_revision;
+ u32 offset;
+ u32 val;
+
+ /*
+ * Remap xHCI address space to access XHCI ext cap regs since it is
+ * needed to get information on number of ports present.
+ */
+ base = ioremap(dwc->xhci_resources[0].start,
+ resource_size(&dwc->xhci_resources[0]));
+ if (!base)
+ return -ENOMEM;
+
+ offset = 0;
+ do {
+ offset = xhci_find_next_ext_cap(base, offset,
+ XHCI_EXT_CAPS_PROTOCOL);
+ if (!offset)
+ break;
+
+ val = readl(base + offset);
+ major_revision = XHCI_EXT_PORT_MAJOR(val);
+
+ val = readl(base + offset + 0x08);
+ if (major_revision == 0x03) {
+ dwc->num_usb3_ports += XHCI_EXT_PORT_COUNT(val);
+ } else if (major_revision <= 0x02) {
+ dwc->num_usb2_ports += XHCI_EXT_PORT_COUNT(val);
+ } else {
+ dev_warn(dwc->dev, "unrecognized port major revision %d\n",
+ major_revision);
+ }
+ } while (1);
+
+ dev_dbg(dwc->dev, "hs-ports: %u ss-ports: %u\n",
+ dwc->num_usb2_ports, dwc->num_usb3_ports);
+
+ iounmap(base);
+
+ if (dwc->num_usb2_ports > DWC3_USB2_MAX_PORTS ||
+ dwc->num_usb3_ports > DWC3_USB3_MAX_PORTS)
+ return -EINVAL;
+
+ return 0;
+}
+
static int dwc3_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct resource *res, dwc_res;
+ unsigned int hw_mode;
void __iomem *regs;
struct dwc3 *dwc;
int ret;
@@ -1920,6 +2153,8 @@ static int dwc3_probe(struct platform_device *pdev)
dwc3_get_properties(dwc);
+ dwc3_get_software_properties(dwc);
+
dwc->reset = devm_reset_control_array_get_optional_shared(dev);
if (IS_ERR(dwc->reset)) {
ret = PTR_ERR(dwc->reset);
@@ -1954,6 +2189,20 @@ static int dwc3_probe(struct platform_device *pdev)
goto err_disable_clks;
}
+ /*
+ * Currently only DWC3 controllers that are host-only capable
+ * can have more than one port.
+ */
+ hw_mode = DWC3_GHWPARAMS0_MODE(dwc->hwparams.hwparams0);
+ if (hw_mode == DWC3_GHWPARAMS0_MODE_HOST) {
+ ret = dwc3_get_num_ports(dwc);
+ if (ret)
+ goto err_disable_clks;
+ } else {
+ dwc->num_usb2_ports = 1;
+ dwc->num_usb3_ports = 1;
+ }
+
spin_lock_init(&dwc->lock);
mutex_init(&dwc->mutex);
@@ -2084,8 +2333,8 @@ assert_reset:
static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
{
- unsigned long flags;
u32 reg;
+ int i;
switch (dwc->current_dr_role) {
case DWC3_GCTL_PRTCAP_DEVICE:
@@ -2104,17 +2353,21 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
/* Let controller to suspend HSPHY before PHY driver suspends */
if (dwc->dis_u2_susphy_quirk ||
dwc->dis_enblslpm_quirk) {
- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
- reg |= DWC3_GUSB2PHYCFG_ENBLSLPM |
- DWC3_GUSB2PHYCFG_SUSPHY;
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+ for (i = 0; i < dwc->num_usb2_ports; i++) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(i));
+ reg |= DWC3_GUSB2PHYCFG_ENBLSLPM |
+ DWC3_GUSB2PHYCFG_SUSPHY;
+ dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(i), reg);
+ }
/* Give some time for USB2 PHY to suspend */
usleep_range(5000, 6000);
}
- phy_pm_runtime_put_sync(dwc->usb2_generic_phy);
- phy_pm_runtime_put_sync(dwc->usb3_generic_phy);
+ for (i = 0; i < dwc->num_usb2_ports; i++)
+ phy_pm_runtime_put_sync(dwc->usb2_generic_phy[i]);
+ for (i = 0; i < dwc->num_usb3_ports; i++)
+ phy_pm_runtime_put_sync(dwc->usb3_generic_phy[i]);
break;
case DWC3_GCTL_PRTCAP_OTG:
/* do nothing during runtime_suspend */
@@ -2122,9 +2375,7 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
break;
if (dwc->current_otg_role == DWC3_OTG_ROLE_DEVICE) {
- spin_lock_irqsave(&dwc->lock, flags);
dwc3_gadget_suspend(dwc);
- spin_unlock_irqrestore(&dwc->lock, flags);
synchronize_irq(dwc->irq_gadget);
}
@@ -2141,9 +2392,9 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg)
{
- unsigned long flags;
int ret;
u32 reg;
+ int i;
switch (dwc->current_dr_role) {
case DWC3_GCTL_PRTCAP_DEVICE:
@@ -2163,17 +2414,21 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg)
break;
}
/* Restore GUSB2PHYCFG bits that were modified in suspend */
- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
- if (dwc->dis_u2_susphy_quirk)
- reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
+ for (i = 0; i < dwc->num_usb2_ports; i++) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(i));
+ if (dwc->dis_u2_susphy_quirk)
+ reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
- if (dwc->dis_enblslpm_quirk)
- reg &= ~DWC3_GUSB2PHYCFG_ENBLSLPM;
+ if (dwc->dis_enblslpm_quirk)
+ reg &= ~DWC3_GUSB2PHYCFG_ENBLSLPM;
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+ dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(i), reg);
+ }
- phy_pm_runtime_get_sync(dwc->usb2_generic_phy);
- phy_pm_runtime_get_sync(dwc->usb3_generic_phy);
+ for (i = 0; i < dwc->num_usb2_ports; i++)
+ phy_pm_runtime_get_sync(dwc->usb2_generic_phy[i]);
+ for (i = 0; i < dwc->num_usb3_ports; i++)
+ phy_pm_runtime_get_sync(dwc->usb3_generic_phy[i]);
break;
case DWC3_GCTL_PRTCAP_OTG:
/* nothing to do on runtime_resume */
@@ -2190,9 +2445,7 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg)
if (dwc->current_otg_role == DWC3_OTG_ROLE_HOST) {
dwc3_otg_host_init(dwc);
} else if (dwc->current_otg_role == DWC3_OTG_ROLE_DEVICE) {
- spin_lock_irqsave(&dwc->lock, flags);
dwc3_gadget_resume(dwc);
- spin_unlock_irqrestore(&dwc->lock, flags);
}
break;
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 180dd8d29287..c71240e8f7c7 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -33,6 +33,13 @@
#include <linux/power_supply.h>
+/*
+ * DWC3 Multiport controllers support up to 15 High-Speed PHYs
+ * and 4 SuperSpeed PHYs.
+ */
+#define DWC3_USB2_MAX_PORTS 15
+#define DWC3_USB3_MAX_PORTS 4
+
#define DWC3_MSG_MAX 500
/* Global constants */
@@ -187,6 +194,10 @@
#define DWC3_GSBUSCFG0_INCRBRSTENA (1 << 0) /* undefined length enable */
#define DWC3_GSBUSCFG0_INCRBRST_MASK 0xff
+/* Global SoC Bus Configuration Register: AHB-prot/AXI-cache/OCP-ReqInfo */
+#define DWC3_GSBUSCFG0_REQINFO(n) (((n) & 0xffff) << 16)
+#define DWC3_GSBUSCFG0_REQINFO_UNSPECIFIED 0xffffffff
+
/* Global Debug LSP MUX Select */
#define DWC3_GDBGLSPMUX_ENDBC BIT(15) /* Host only */
#define DWC3_GDBGLSPMUX_HOSTSELECT(n) ((n) & 0x3fff)
@@ -410,6 +421,7 @@
/* Global User Control Register 2 */
#define DWC3_GUCTL2_RST_ACTBITLATER BIT(14)
+#define DWC3_GUCTL2_LC_TIMER BIT(19)
/* Global User Control Register 3 */
#define DWC3_GUCTL3_SPLITDISABLE BIT(14)
@@ -1037,8 +1049,10 @@ struct dwc3_scratchpad_array {
* @usb_psy: pointer to power supply interface.
* @usb2_phy: pointer to USB2 PHY
* @usb3_phy: pointer to USB3 PHY
- * @usb2_generic_phy: pointer to USB2 PHY
- * @usb3_generic_phy: pointer to USB3 PHY
+ * @usb2_generic_phy: pointer to array of USB2 PHYs
+ * @usb3_generic_phy: pointer to array of USB3 PHYs
+ * @num_usb2_ports: number of USB2 ports
+ * @num_usb3_ports: number of USB3 ports
* @phys_ready: flag to indicate that PHYs are ready
* @ulpi: pointer to ulpi interface
* @ulpi_ready: flag to indicate that ULPI is initialized
@@ -1144,6 +1158,9 @@ struct dwc3_scratchpad_array {
* @num_ep_resized: carries the current number endpoints which have had its tx
* fifo resized.
* @debug_root: root debugfs directory for this device to put its files in.
+ * @gsbuscfg0_reqinfo: store GSBUSCFG0.DATRDREQINFO, DESRDREQINFO,
+ * DATWRREQINFO, and DESWRREQINFO value passed from
+ * glue driver.
*/
struct dwc3 {
struct work_struct drd_work;
@@ -1184,8 +1201,11 @@ struct dwc3 {
struct usb_phy *usb2_phy;
struct usb_phy *usb3_phy;
- struct phy *usb2_generic_phy;
- struct phy *usb3_generic_phy;
+ struct phy *usb2_generic_phy[DWC3_USB2_MAX_PORTS];
+ struct phy *usb3_generic_phy[DWC3_USB3_MAX_PORTS];
+
+ u8 num_usb2_ports;
+ u8 num_usb3_ports;
bool phys_ready;
@@ -1250,6 +1270,7 @@ struct dwc3 {
#define DWC3_REVISION_290A 0x5533290a
#define DWC3_REVISION_300A 0x5533300a
#define DWC3_REVISION_310A 0x5533310a
+#define DWC3_REVISION_320A 0x5533320a
#define DWC3_REVISION_330A 0x5533330a
#define DWC31_REVISION_ANY 0x0
@@ -1368,6 +1389,7 @@ struct dwc3 {
int last_fifo_depth;
int num_ep_resized;
struct dentry *debug_root;
+ u32 gsbuscfg0_reqinfo;
};
#define INCRX_BURST_MODE 0
diff --git a/drivers/usb/dwc3/drd.c b/drivers/usb/dwc3/drd.c
index 57ddd2e43022..d76ae676783c 100644
--- a/drivers/usb/dwc3/drd.c
+++ b/drivers/usb/dwc3/drd.c
@@ -331,6 +331,7 @@ void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus)
u32 reg;
int id;
unsigned long flags;
+ int i;
if (dwc->dr_mode != USB_DR_MODE_OTG)
return;
@@ -386,9 +387,12 @@ void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus)
} else {
if (dwc->usb2_phy)
otg_set_vbus(dwc->usb2_phy->otg, true);
- if (dwc->usb2_generic_phy)
- phy_set_mode(dwc->usb2_generic_phy,
- PHY_MODE_USB_HOST);
+ for (i = 0; i < dwc->num_usb2_ports; i++) {
+ if (dwc->usb2_generic_phy[i]) {
+ phy_set_mode(dwc->usb2_generic_phy[i],
+ PHY_MODE_USB_HOST);
+ }
+ }
}
break;
case DWC3_OTG_ROLE_DEVICE:
@@ -400,9 +404,8 @@ void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus)
if (dwc->usb2_phy)
otg_set_vbus(dwc->usb2_phy->otg, false);
- if (dwc->usb2_generic_phy)
- phy_set_mode(dwc->usb2_generic_phy,
- PHY_MODE_USB_DEVICE);
+ if (dwc->usb2_generic_phy[0])
+ phy_set_mode(dwc->usb2_generic_phy[0], PHY_MODE_USB_DEVICE);
ret = dwc3_gadget_init(dwc);
if (ret)
dev_err(dwc->dev, "failed to initialize peripheral\n");
diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c
index 5d365ca51771..9a6e988d165a 100644
--- a/drivers/usb/dwc3/dwc3-exynos.c
+++ b/drivers/usb/dwc3/dwc3-exynos.c
@@ -169,6 +169,12 @@ static const struct dwc3_exynos_driverdata exynos850_drvdata = {
.suspend_clk_idx = -1,
};
+static const struct dwc3_exynos_driverdata gs101_drvdata = {
+ .clk_names = { "bus_early", "susp_clk", "link_aclk", "link_pclk" },
+ .num_clks = 4,
+ .suspend_clk_idx = 1,
+};
+
static const struct of_device_id exynos_dwc3_match[] = {
{
.compatible = "samsung,exynos5250-dwusb3",
@@ -183,11 +189,13 @@ static const struct of_device_id exynos_dwc3_match[] = {
.compatible = "samsung,exynos850-dwusb3",
.data = &exynos850_drvdata,
}, {
+ .compatible = "google,gs101-dwusb3",
+ .data = &gs101_drvdata,
+ }, {
}
};
MODULE_DEVICE_TABLE(of, exynos_dwc3_match);
-#ifdef CONFIG_PM_SLEEP
static int dwc3_exynos_suspend(struct device *dev)
{
struct dwc3_exynos *exynos = dev_get_drvdata(dev);
@@ -230,14 +238,8 @@ static int dwc3_exynos_resume(struct device *dev)
return 0;
}
-static const struct dev_pm_ops dwc3_exynos_dev_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(dwc3_exynos_suspend, dwc3_exynos_resume)
-};
-
-#define DEV_PM_OPS (&dwc3_exynos_dev_pm_ops)
-#else
-#define DEV_PM_OPS NULL
-#endif /* CONFIG_PM_SLEEP */
+static DEFINE_SIMPLE_DEV_PM_OPS(dwc3_exynos_dev_pm_ops,
+ dwc3_exynos_suspend, dwc3_exynos_resume);
static struct platform_driver dwc3_exynos_driver = {
.probe = dwc3_exynos_probe,
@@ -245,7 +247,7 @@ static struct platform_driver dwc3_exynos_driver = {
.driver = {
.name = "exynos-dwc3",
.of_match_table = exynos_dwc3_match,
- .pm = DEV_PM_OPS,
+ .pm = pm_sleep_ptr(&dwc3_exynos_dev_pm_ops),
},
};
diff --git a/drivers/usb/dwc3/dwc3-imx8mp.c b/drivers/usb/dwc3/dwc3-imx8mp.c
index 8ee448068503..64c0cd1995aa 100644
--- a/drivers/usb/dwc3/dwc3-imx8mp.c
+++ b/drivers/usb/dwc3/dwc3-imx8mp.c
@@ -5,6 +5,7 @@
* Copyright (c) 2020 NXP.
*/
+#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/interrupt.h>
#include <linux/io.h>
@@ -96,7 +97,8 @@ static void imx8mp_configure_glue(struct dwc3_imx8mp *dwc3_imx)
writel(value, dwc3_imx->glue_base + USB_CTRL1);
}
-static void dwc3_imx8mp_wakeup_enable(struct dwc3_imx8mp *dwc3_imx)
+static void dwc3_imx8mp_wakeup_enable(struct dwc3_imx8mp *dwc3_imx,
+ pm_message_t msg)
{
struct dwc3 *dwc3 = platform_get_drvdata(dwc3_imx->dwc3);
u32 val;
@@ -106,12 +108,14 @@ static void dwc3_imx8mp_wakeup_enable(struct dwc3_imx8mp *dwc3_imx)
val = readl(dwc3_imx->hsio_blk_base + USB_WAKEUP_CTRL);
- if ((dwc3->current_dr_role == DWC3_GCTL_PRTCAP_HOST) && dwc3->xhci)
- val |= USB_WAKEUP_EN | USB_WAKEUP_SS_CONN |
- USB_WAKEUP_U3_EN | USB_WAKEUP_DPDM_EN;
- else if (dwc3->current_dr_role == DWC3_GCTL_PRTCAP_DEVICE)
+ if ((dwc3->current_dr_role == DWC3_GCTL_PRTCAP_HOST) && dwc3->xhci) {
+ val |= USB_WAKEUP_EN | USB_WAKEUP_DPDM_EN;
+ if (PMSG_IS_AUTO(msg))
+ val |= USB_WAKEUP_SS_CONN | USB_WAKEUP_U3_EN;
+ } else {
val |= USB_WAKEUP_EN | USB_WAKEUP_VBUS_EN |
USB_WAKEUP_VBUS_SRC_SESS_VAL;
+ }
writel(val, dwc3_imx->hsio_blk_base + USB_WAKEUP_CTRL);
}
@@ -144,10 +148,21 @@ static irqreturn_t dwc3_imx8mp_interrupt(int irq, void *_dwc3_imx)
return IRQ_HANDLED;
}
+static int dwc3_imx8mp_set_software_node(struct device *dev)
+{
+ struct property_entry props[3] = { 0 };
+ int prop_idx = 0;
+
+ props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-missing-cas-quirk");
+ props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-skip-phy-init-quirk");
+
+ return device_create_managed_software_node(dev, props, NULL);
+}
+
static int dwc3_imx8mp_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct device_node *dwc3_np, *node = dev->of_node;
+ struct device_node *node = dev->of_node;
struct dwc3_imx8mp *dwc3_imx;
struct resource *res;
int err, irq;
@@ -178,39 +193,26 @@ static int dwc3_imx8mp_probe(struct platform_device *pdev)
return PTR_ERR(dwc3_imx->glue_base);
}
- dwc3_imx->hsio_clk = devm_clk_get(dev, "hsio");
- if (IS_ERR(dwc3_imx->hsio_clk)) {
- err = PTR_ERR(dwc3_imx->hsio_clk);
- dev_err(dev, "Failed to get hsio clk, err=%d\n", err);
- return err;
- }
-
- err = clk_prepare_enable(dwc3_imx->hsio_clk);
- if (err) {
- dev_err(dev, "Failed to enable hsio clk, err=%d\n", err);
- return err;
- }
-
- dwc3_imx->suspend_clk = devm_clk_get(dev, "suspend");
- if (IS_ERR(dwc3_imx->suspend_clk)) {
- err = PTR_ERR(dwc3_imx->suspend_clk);
- dev_err(dev, "Failed to get suspend clk, err=%d\n", err);
- goto disable_hsio_clk;
- }
+ dwc3_imx->hsio_clk = devm_clk_get_enabled(dev, "hsio");
+ if (IS_ERR(dwc3_imx->hsio_clk))
+ return dev_err_probe(dev, PTR_ERR(dwc3_imx->hsio_clk),
+ "Failed to get hsio clk\n");
- err = clk_prepare_enable(dwc3_imx->suspend_clk);
- if (err) {
- dev_err(dev, "Failed to enable suspend clk, err=%d\n", err);
- goto disable_hsio_clk;
- }
+ dwc3_imx->suspend_clk = devm_clk_get_enabled(dev, "suspend");
+ if (IS_ERR(dwc3_imx->suspend_clk))
+ return dev_err_probe(dev, PTR_ERR(dwc3_imx->suspend_clk),
+ "Failed to get suspend clk\n");
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- err = irq;
- goto disable_clks;
- }
+ if (irq < 0)
+ return irq;
dwc3_imx->irq = irq;
+ struct device_node *dwc3_np __free(device_node) = of_get_compatible_child(node,
+ "snps,dwc3");
+ if (!dwc3_np)
+ return dev_err_probe(dev, -ENODEV, "failed to find dwc3 core child\n");
+
imx8mp_configure_glue(dwc3_imx);
pm_runtime_set_active(dev);
@@ -219,17 +221,17 @@ static int dwc3_imx8mp_probe(struct platform_device *pdev)
if (err < 0)
goto disable_rpm;
- dwc3_np = of_get_compatible_child(node, "snps,dwc3");
- if (!dwc3_np) {
+ err = dwc3_imx8mp_set_software_node(dev);
+ if (err) {
err = -ENODEV;
- dev_err(dev, "failed to find dwc3 core child\n");
+ dev_err(dev, "failed to create software node\n");
goto disable_rpm;
}
err = of_platform_populate(node, NULL, NULL, dev);
if (err) {
dev_err(&pdev->dev, "failed to create dwc3 core\n");
- goto err_node_put;
+ goto disable_rpm;
}
dwc3_imx->dwc3 = of_find_device_by_node(dwc3_np);
@@ -238,7 +240,6 @@ static int dwc3_imx8mp_probe(struct platform_device *pdev)
err = -ENODEV;
goto depopulate;
}
- of_node_put(dwc3_np);
err = devm_request_threaded_irq(dev, irq, NULL, dwc3_imx8mp_interrupt,
IRQF_ONESHOT, dev_name(dev), dwc3_imx);
@@ -254,51 +255,39 @@ static int dwc3_imx8mp_probe(struct platform_device *pdev)
depopulate:
of_platform_depopulate(dev);
-err_node_put:
- of_node_put(dwc3_np);
disable_rpm:
pm_runtime_disable(dev);
pm_runtime_put_noidle(dev);
-disable_clks:
- clk_disable_unprepare(dwc3_imx->suspend_clk);
-disable_hsio_clk:
- clk_disable_unprepare(dwc3_imx->hsio_clk);
return err;
}
static void dwc3_imx8mp_remove(struct platform_device *pdev)
{
- struct dwc3_imx8mp *dwc3_imx = platform_get_drvdata(pdev);
struct device *dev = &pdev->dev;
pm_runtime_get_sync(dev);
of_platform_depopulate(dev);
- clk_disable_unprepare(dwc3_imx->suspend_clk);
- clk_disable_unprepare(dwc3_imx->hsio_clk);
-
pm_runtime_disable(dev);
pm_runtime_put_noidle(dev);
}
-static int __maybe_unused dwc3_imx8mp_suspend(struct dwc3_imx8mp *dwc3_imx,
- pm_message_t msg)
+static int dwc3_imx8mp_suspend(struct dwc3_imx8mp *dwc3_imx, pm_message_t msg)
{
if (dwc3_imx->pm_suspended)
return 0;
/* Wakeup enable */
if (PMSG_IS_AUTO(msg) || device_may_wakeup(dwc3_imx->dev))
- dwc3_imx8mp_wakeup_enable(dwc3_imx);
+ dwc3_imx8mp_wakeup_enable(dwc3_imx, msg);
dwc3_imx->pm_suspended = true;
return 0;
}
-static int __maybe_unused dwc3_imx8mp_resume(struct dwc3_imx8mp *dwc3_imx,
- pm_message_t msg)
+static int dwc3_imx8mp_resume(struct dwc3_imx8mp *dwc3_imx, pm_message_t msg)
{
struct dwc3 *dwc = platform_get_drvdata(dwc3_imx->dwc3);
int ret = 0;
@@ -331,7 +320,7 @@ static int __maybe_unused dwc3_imx8mp_resume(struct dwc3_imx8mp *dwc3_imx,
return ret;
}
-static int __maybe_unused dwc3_imx8mp_pm_suspend(struct device *dev)
+static int dwc3_imx8mp_pm_suspend(struct device *dev)
{
struct dwc3_imx8mp *dwc3_imx = dev_get_drvdata(dev);
int ret;
@@ -349,7 +338,7 @@ static int __maybe_unused dwc3_imx8mp_pm_suspend(struct device *dev)
return ret;
}
-static int __maybe_unused dwc3_imx8mp_pm_resume(struct device *dev)
+static int dwc3_imx8mp_pm_resume(struct device *dev)
{
struct dwc3_imx8mp *dwc3_imx = dev_get_drvdata(dev);
int ret;
@@ -379,7 +368,7 @@ static int __maybe_unused dwc3_imx8mp_pm_resume(struct device *dev)
return ret;
}
-static int __maybe_unused dwc3_imx8mp_runtime_suspend(struct device *dev)
+static int dwc3_imx8mp_runtime_suspend(struct device *dev)
{
struct dwc3_imx8mp *dwc3_imx = dev_get_drvdata(dev);
@@ -388,7 +377,7 @@ static int __maybe_unused dwc3_imx8mp_runtime_suspend(struct device *dev)
return dwc3_imx8mp_suspend(dwc3_imx, PMSG_AUTO_SUSPEND);
}
-static int __maybe_unused dwc3_imx8mp_runtime_resume(struct device *dev)
+static int dwc3_imx8mp_runtime_resume(struct device *dev)
{
struct dwc3_imx8mp *dwc3_imx = dev_get_drvdata(dev);
@@ -398,9 +387,9 @@ static int __maybe_unused dwc3_imx8mp_runtime_resume(struct device *dev)
}
static const struct dev_pm_ops dwc3_imx8mp_dev_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(dwc3_imx8mp_pm_suspend, dwc3_imx8mp_pm_resume)
- SET_RUNTIME_PM_OPS(dwc3_imx8mp_runtime_suspend,
- dwc3_imx8mp_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(dwc3_imx8mp_pm_suspend, dwc3_imx8mp_pm_resume)
+ RUNTIME_PM_OPS(dwc3_imx8mp_runtime_suspend, dwc3_imx8mp_runtime_resume,
+ NULL)
};
static const struct of_device_id dwc3_imx8mp_of_match[] = {
@@ -414,7 +403,7 @@ static struct platform_driver dwc3_imx8mp_driver = {
.remove_new = dwc3_imx8mp_remove,
.driver = {
.name = "imx8mp-dwc3",
- .pm = &dwc3_imx8mp_dev_pm_ops,
+ .pm = pm_ptr(&dwc3_imx8mp_dev_pm_ops),
.of_match_table = dwc3_imx8mp_of_match,
},
};
diff --git a/drivers/usb/dwc3/dwc3-octeon.c b/drivers/usb/dwc3/dwc3-octeon.c
index 6010135e1acc..1a3b205367fd 100644
--- a/drivers/usb/dwc3/dwc3-octeon.c
+++ b/drivers/usb/dwc3/dwc3-octeon.c
@@ -419,7 +419,7 @@ static int dwc3_octeon_probe(struct platform_device *pdev)
int ref_clk_sel, ref_clk_fsel, mpll_mul;
int power_active_low, power_gpio;
int err, len;
- u32 clock_rate;
+ u32 clock_rate, gpio_pwr[3];
if (of_property_read_u32(node, "refclk-frequency", &clock_rate)) {
dev_err(dev, "No UCTL \"refclk-frequency\"\n");
@@ -476,21 +476,10 @@ static int dwc3_octeon_probe(struct platform_device *pdev)
power_gpio = DWC3_GPIO_POWER_NONE;
power_active_low = 0;
- if (of_find_property(node, "power", &len)) {
- u32 gpio_pwr[3];
-
- switch (len) {
- case 8:
- of_property_read_u32_array(node, "power", gpio_pwr, 2);
- break;
- case 12:
- of_property_read_u32_array(node, "power", gpio_pwr, 3);
+ len = of_property_read_variable_u32_array(node, "power", gpio_pwr, 2, 3);
+ if (len > 0) {
+ if (len == 3)
power_active_low = gpio_pwr[2] & 0x01;
- break;
- default:
- dev_err(dev, "invalid power configuration\n");
- return -EINVAL;
- }
power_gpio = gpio_pwr[1];
}
diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c
index d5c77db4daa9..2a11fc0ee84f 100644
--- a/drivers/usb/dwc3/dwc3-omap.c
+++ b/drivers/usb/dwc3/dwc3-omap.c
@@ -522,11 +522,13 @@ static int dwc3_omap_probe(struct platform_device *pdev)
if (ret) {
dev_err(dev, "failed to request IRQ #%d --> %d\n",
omap->irq, ret);
- goto err1;
+ goto err2;
}
dwc3_omap_enable_irqs(omap);
return 0;
+err2:
+ of_platform_depopulate(dev);
err1:
pm_runtime_put_sync(dev);
pm_runtime_disable(dev);
diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c
index 497deed38c0c..052852f80146 100644
--- a/drivers/usb/dwc3/dwc3-pci.c
+++ b/drivers/usb/dwc3/dwc3-pci.c
@@ -8,6 +8,7 @@
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
*/
+#include <linux/dmi.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
@@ -53,6 +54,10 @@
#define PCI_DEVICE_ID_INTEL_MTL 0x7e7e
#define PCI_DEVICE_ID_INTEL_ARLH_PCH 0x777e
#define PCI_DEVICE_ID_INTEL_TGL 0x9a15
+#define PCI_DEVICE_ID_INTEL_PTLH 0xe332
+#define PCI_DEVICE_ID_INTEL_PTLH_PCH 0xe37e
+#define PCI_DEVICE_ID_INTEL_PTLU 0xe432
+#define PCI_DEVICE_ID_INTEL_PTLU_PCH 0xe47e
#define PCI_DEVICE_ID_AMD_MR 0x163a
#define PCI_INTEL_BXT_DSM_GUID "732b85d5-b7a7-4a1b-9ba0-4bbd00ffd511"
@@ -220,6 +225,7 @@ static int dwc3_pci_quirks(struct dwc3_pci *dwc,
if (pdev->device == PCI_DEVICE_ID_INTEL_BYT) {
struct gpio_desc *gpio;
+ const char *bios_ver;
int ret;
/* On BYT the FW does not always enable the refclock */
@@ -277,8 +283,12 @@ static int dwc3_pci_quirks(struct dwc3_pci *dwc,
* detection. These can be identified by them _not_
* using the standard ACPI battery and ac drivers.
*/
+ bios_ver = dmi_get_system_info(DMI_BIOS_VERSION);
if (acpi_dev_present("INT33FD", "1", 2) &&
- acpi_quirk_skip_acpi_ac_and_battery()) {
+ acpi_quirk_skip_acpi_ac_and_battery() &&
+ /* Lenovo Yoga Tablet 2 Pro 1380 uses LC824206XA instead */
+ !(bios_ver &&
+ strstarts(bios_ver, "BLADE_21.X64.0005.R00.1504101516"))) {
dev_info(&pdev->dev, "Using TUSB1211 phy for charger detection\n");
swnode = &dwc3_pci_intel_phy_charger_detect_swnode;
}
@@ -424,6 +434,10 @@ static const struct pci_device_id dwc3_pci_id_table[] = {
{ PCI_DEVICE_DATA(INTEL, MTLS, &dwc3_pci_intel_swnode) },
{ PCI_DEVICE_DATA(INTEL, ARLH_PCH, &dwc3_pci_intel_swnode) },
{ PCI_DEVICE_DATA(INTEL, TGL, &dwc3_pci_intel_swnode) },
+ { PCI_DEVICE_DATA(INTEL, PTLH, &dwc3_pci_intel_swnode) },
+ { PCI_DEVICE_DATA(INTEL, PTLH_PCH, &dwc3_pci_intel_swnode) },
+ { PCI_DEVICE_DATA(INTEL, PTLU, &dwc3_pci_intel_swnode) },
+ { PCI_DEVICE_DATA(INTEL, PTLU_PCH, &dwc3_pci_intel_swnode) },
{ PCI_DEVICE_DATA(AMD, NL_USB, &dwc3_pci_amd_swnode) },
{ PCI_DEVICE_DATA(AMD, MR, &dwc3_pci_amd_mr_swnode) },
diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c
index f6b2fab49d5e..c1d4b52f25b0 100644
--- a/drivers/usb/dwc3/dwc3-qcom.c
+++ b/drivers/usb/dwc3/dwc3-qcom.c
@@ -4,6 +4,7 @@
* Inspired by dwc3-of-simple.c
*/
+#include <linux/cleanup.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/clk.h>
@@ -36,7 +37,6 @@
#define PIPE3_PHYSTATUS_SW BIT(3)
#define PIPE_UTMI_CLK_DIS BIT(8)
-#define PWR_EVNT_IRQ_STAT_REG 0x58
#define PWR_EVNT_LPM_IN_L2_MASK BIT(4)
#define PWR_EVNT_LPM_OUT_L2_MASK BIT(5)
@@ -52,6 +52,24 @@
#define APPS_USB_AVG_BW 0
#define APPS_USB_PEAK_BW MBps_to_icc(40)
+/* Qualcomm SoCs with multiport support has up to 4 ports */
+#define DWC3_QCOM_MAX_PORTS 4
+
+static const u32 pwr_evnt_irq_stat_reg[DWC3_QCOM_MAX_PORTS] = {
+ 0x58,
+ 0x1dc,
+ 0x228,
+ 0x238,
+};
+
+struct dwc3_qcom_port {
+ int qusb2_phy_irq;
+ int dp_hs_phy_irq;
+ int dm_hs_phy_irq;
+ int ss_phy_irq;
+ enum usb_device_speed usb2_speed;
+};
+
struct dwc3_qcom {
struct device *dev;
void __iomem *qscratch_base;
@@ -59,12 +77,8 @@ struct dwc3_qcom {
struct clk **clks;
int num_clocks;
struct reset_control *resets;
-
- int qusb2_phy_irq;
- int dp_hs_phy_irq;
- int dm_hs_phy_irq;
- int ss_phy_irq;
- enum usb_device_speed usb2_speed;
+ struct dwc3_qcom_port ports[DWC3_QCOM_MAX_PORTS];
+ u8 num_ports;
struct extcon_dev *edev;
struct extcon_dev *host_edev;
@@ -303,7 +317,7 @@ static bool dwc3_qcom_is_host(struct dwc3_qcom *qcom)
return dwc->xhci;
}
-static enum usb_device_speed dwc3_qcom_read_usb2_speed(struct dwc3_qcom *qcom)
+static enum usb_device_speed dwc3_qcom_read_usb2_speed(struct dwc3_qcom *qcom, int port_index)
{
struct dwc3 *dwc = platform_get_drvdata(qcom->dwc3);
struct usb_device *udev;
@@ -314,14 +328,8 @@ static enum usb_device_speed dwc3_qcom_read_usb2_speed(struct dwc3_qcom *qcom)
*/
hcd = platform_get_drvdata(dwc->xhci);
- /*
- * It is possible to query the speed of all children of
- * USB2.0 root hub via usb_hub_for_each_child(). DWC3 code
- * currently supports only 1 port per controller. So
- * this is sufficient.
- */
#ifdef CONFIG_USB
- udev = usb_hub_find_child(hcd->self.root_hub, 1);
+ udev = usb_hub_find_child(hcd->self.root_hub, port_index + 1);
#else
udev = NULL;
#endif
@@ -352,26 +360,26 @@ static void dwc3_qcom_disable_wakeup_irq(int irq)
disable_irq_nosync(irq);
}
-static void dwc3_qcom_disable_interrupts(struct dwc3_qcom *qcom)
+static void dwc3_qcom_disable_port_interrupts(struct dwc3_qcom_port *port)
{
- dwc3_qcom_disable_wakeup_irq(qcom->qusb2_phy_irq);
+ dwc3_qcom_disable_wakeup_irq(port->qusb2_phy_irq);
- if (qcom->usb2_speed == USB_SPEED_LOW) {
- dwc3_qcom_disable_wakeup_irq(qcom->dm_hs_phy_irq);
- } else if ((qcom->usb2_speed == USB_SPEED_HIGH) ||
- (qcom->usb2_speed == USB_SPEED_FULL)) {
- dwc3_qcom_disable_wakeup_irq(qcom->dp_hs_phy_irq);
+ if (port->usb2_speed == USB_SPEED_LOW) {
+ dwc3_qcom_disable_wakeup_irq(port->dm_hs_phy_irq);
+ } else if ((port->usb2_speed == USB_SPEED_HIGH) ||
+ (port->usb2_speed == USB_SPEED_FULL)) {
+ dwc3_qcom_disable_wakeup_irq(port->dp_hs_phy_irq);
} else {
- dwc3_qcom_disable_wakeup_irq(qcom->dp_hs_phy_irq);
- dwc3_qcom_disable_wakeup_irq(qcom->dm_hs_phy_irq);
+ dwc3_qcom_disable_wakeup_irq(port->dp_hs_phy_irq);
+ dwc3_qcom_disable_wakeup_irq(port->dm_hs_phy_irq);
}
- dwc3_qcom_disable_wakeup_irq(qcom->ss_phy_irq);
+ dwc3_qcom_disable_wakeup_irq(port->ss_phy_irq);
}
-static void dwc3_qcom_enable_interrupts(struct dwc3_qcom *qcom)
+static void dwc3_qcom_enable_port_interrupts(struct dwc3_qcom_port *port)
{
- dwc3_qcom_enable_wakeup_irq(qcom->qusb2_phy_irq, 0);
+ dwc3_qcom_enable_wakeup_irq(port->qusb2_phy_irq, 0);
/*
* Configure DP/DM line interrupts based on the USB2 device attached to
@@ -382,21 +390,37 @@ static void dwc3_qcom_enable_interrupts(struct dwc3_qcom *qcom)
* DP and DM lines as rising edge to detect HS/HS/LS device connect scenario.
*/
- if (qcom->usb2_speed == USB_SPEED_LOW) {
- dwc3_qcom_enable_wakeup_irq(qcom->dm_hs_phy_irq,
- IRQ_TYPE_EDGE_FALLING);
- } else if ((qcom->usb2_speed == USB_SPEED_HIGH) ||
- (qcom->usb2_speed == USB_SPEED_FULL)) {
- dwc3_qcom_enable_wakeup_irq(qcom->dp_hs_phy_irq,
- IRQ_TYPE_EDGE_FALLING);
+ if (port->usb2_speed == USB_SPEED_LOW) {
+ dwc3_qcom_enable_wakeup_irq(port->dm_hs_phy_irq,
+ IRQ_TYPE_EDGE_FALLING);
+ } else if ((port->usb2_speed == USB_SPEED_HIGH) ||
+ (port->usb2_speed == USB_SPEED_FULL)) {
+ dwc3_qcom_enable_wakeup_irq(port->dp_hs_phy_irq,
+ IRQ_TYPE_EDGE_FALLING);
} else {
- dwc3_qcom_enable_wakeup_irq(qcom->dp_hs_phy_irq,
- IRQ_TYPE_EDGE_RISING);
- dwc3_qcom_enable_wakeup_irq(qcom->dm_hs_phy_irq,
- IRQ_TYPE_EDGE_RISING);
+ dwc3_qcom_enable_wakeup_irq(port->dp_hs_phy_irq,
+ IRQ_TYPE_EDGE_RISING);
+ dwc3_qcom_enable_wakeup_irq(port->dm_hs_phy_irq,
+ IRQ_TYPE_EDGE_RISING);
}
- dwc3_qcom_enable_wakeup_irq(qcom->ss_phy_irq, 0);
+ dwc3_qcom_enable_wakeup_irq(port->ss_phy_irq, 0);
+}
+
+static void dwc3_qcom_disable_interrupts(struct dwc3_qcom *qcom)
+{
+ int i;
+
+ for (i = 0; i < qcom->num_ports; i++)
+ dwc3_qcom_disable_port_interrupts(&qcom->ports[i]);
+}
+
+static void dwc3_qcom_enable_interrupts(struct dwc3_qcom *qcom)
+{
+ int i;
+
+ for (i = 0; i < qcom->num_ports; i++)
+ dwc3_qcom_enable_port_interrupts(&qcom->ports[i]);
}
static int dwc3_qcom_suspend(struct dwc3_qcom *qcom, bool wakeup)
@@ -407,9 +431,11 @@ static int dwc3_qcom_suspend(struct dwc3_qcom *qcom, bool wakeup)
if (qcom->is_suspended)
return 0;
- val = readl(qcom->qscratch_base + PWR_EVNT_IRQ_STAT_REG);
- if (!(val & PWR_EVNT_LPM_IN_L2_MASK))
- dev_err(qcom->dev, "HS-PHY not in L2\n");
+ for (i = 0; i < qcom->num_ports; i++) {
+ val = readl(qcom->qscratch_base + pwr_evnt_irq_stat_reg[i]);
+ if (!(val & PWR_EVNT_LPM_IN_L2_MASK))
+ dev_err(qcom->dev, "port-%d HS-PHY not in L2\n", i + 1);
+ }
for (i = qcom->num_clocks - 1; i >= 0; i--)
clk_disable_unprepare(qcom->clks[i]);
@@ -423,7 +449,8 @@ static int dwc3_qcom_suspend(struct dwc3_qcom *qcom, bool wakeup)
* freezable workqueue.
*/
if (dwc3_qcom_is_host(qcom) && wakeup) {
- qcom->usb2_speed = dwc3_qcom_read_usb2_speed(qcom);
+ for (i = 0; i < qcom->num_ports; i++)
+ qcom->ports[i].usb2_speed = dwc3_qcom_read_usb2_speed(qcom, i);
dwc3_qcom_enable_interrupts(qcom);
}
@@ -457,8 +484,11 @@ static int dwc3_qcom_resume(struct dwc3_qcom *qcom, bool wakeup)
dev_warn(qcom->dev, "failed to enable interconnect: %d\n", ret);
/* Clear existing events from PHY related to L2 in/out */
- dwc3_qcom_setbits(qcom->qscratch_base, PWR_EVNT_IRQ_STAT_REG,
- PWR_EVNT_LPM_IN_L2_MASK | PWR_EVNT_LPM_OUT_L2_MASK);
+ for (i = 0; i < qcom->num_ports; i++) {
+ dwc3_qcom_setbits(qcom->qscratch_base,
+ pwr_evnt_irq_stat_reg[i],
+ PWR_EVNT_LPM_IN_L2_MASK | PWR_EVNT_LPM_OUT_L2_MASK);
+ }
qcom->is_suspended = false;
@@ -501,63 +531,123 @@ static void dwc3_qcom_select_utmi_clk(struct dwc3_qcom *qcom)
PIPE_UTMI_CLK_DIS);
}
-static int dwc3_qcom_setup_irq(struct platform_device *pdev)
+static int dwc3_qcom_request_irq(struct dwc3_qcom *qcom, int irq,
+ const char *name)
+{
+ int ret;
+
+ /* Keep wakeup interrupts disabled until suspend */
+ ret = devm_request_threaded_irq(qcom->dev, irq, NULL,
+ qcom_dwc3_resume_irq,
+ IRQF_ONESHOT | IRQF_NO_AUTOEN,
+ name, qcom);
+ if (ret)
+ dev_err(qcom->dev, "failed to request irq %s: %d\n", name, ret);
+
+ return ret;
+}
+
+static int dwc3_qcom_setup_port_irq(struct platform_device *pdev, int port_index, bool is_multiport)
{
struct dwc3_qcom *qcom = platform_get_drvdata(pdev);
+ const char *irq_name;
int irq;
int ret;
- irq = platform_get_irq_byname_optional(pdev, "qusb2_phy");
+ if (is_multiport)
+ irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "dp_hs_phy_%d", port_index + 1);
+ else
+ irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "dp_hs_phy_irq");
+ if (!irq_name)
+ return -ENOMEM;
+
+ irq = platform_get_irq_byname_optional(pdev, irq_name);
if (irq > 0) {
- /* Keep wakeup interrupts disabled until suspend */
- ret = devm_request_threaded_irq(qcom->dev, irq, NULL,
- qcom_dwc3_resume_irq,
- IRQF_ONESHOT | IRQF_NO_AUTOEN,
- "qcom_dwc3 QUSB2", qcom);
- if (ret) {
- dev_err(qcom->dev, "qusb2_phy_irq failed: %d\n", ret);
+ ret = dwc3_qcom_request_irq(qcom, irq, irq_name);
+ if (ret)
return ret;
- }
- qcom->qusb2_phy_irq = irq;
+ qcom->ports[port_index].dp_hs_phy_irq = irq;
}
- irq = platform_get_irq_byname_optional(pdev, "dp_hs_phy_irq");
+ if (is_multiport)
+ irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "dm_hs_phy_%d", port_index + 1);
+ else
+ irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "dm_hs_phy_irq");
+ if (!irq_name)
+ return -ENOMEM;
+
+ irq = platform_get_irq_byname_optional(pdev, irq_name);
if (irq > 0) {
- ret = devm_request_threaded_irq(qcom->dev, irq, NULL,
- qcom_dwc3_resume_irq,
- IRQF_ONESHOT | IRQF_NO_AUTOEN,
- "qcom_dwc3 DP_HS", qcom);
- if (ret) {
- dev_err(qcom->dev, "dp_hs_phy_irq failed: %d\n", ret);
+ ret = dwc3_qcom_request_irq(qcom, irq, irq_name);
+ if (ret)
return ret;
- }
- qcom->dp_hs_phy_irq = irq;
+ qcom->ports[port_index].dm_hs_phy_irq = irq;
}
- irq = platform_get_irq_byname_optional(pdev, "dm_hs_phy_irq");
+ if (is_multiport)
+ irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "ss_phy_%d", port_index + 1);
+ else
+ irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "ss_phy_irq");
+ if (!irq_name)
+ return -ENOMEM;
+
+ irq = platform_get_irq_byname_optional(pdev, irq_name);
if (irq > 0) {
- ret = devm_request_threaded_irq(qcom->dev, irq, NULL,
- qcom_dwc3_resume_irq,
- IRQF_ONESHOT | IRQF_NO_AUTOEN,
- "qcom_dwc3 DM_HS", qcom);
- if (ret) {
- dev_err(qcom->dev, "dm_hs_phy_irq failed: %d\n", ret);
+ ret = dwc3_qcom_request_irq(qcom, irq, irq_name);
+ if (ret)
return ret;
- }
- qcom->dm_hs_phy_irq = irq;
+ qcom->ports[port_index].ss_phy_irq = irq;
}
- irq = platform_get_irq_byname_optional(pdev, "ss_phy_irq");
+ if (is_multiport)
+ return 0;
+
+ irq = platform_get_irq_byname_optional(pdev, "qusb2_phy");
if (irq > 0) {
- ret = devm_request_threaded_irq(qcom->dev, irq, NULL,
- qcom_dwc3_resume_irq,
- IRQF_ONESHOT | IRQF_NO_AUTOEN,
- "qcom_dwc3 SS", qcom);
- if (ret) {
- dev_err(qcom->dev, "ss_phy_irq failed: %d\n", ret);
+ ret = dwc3_qcom_request_irq(qcom, irq, "qusb2_phy");
+ if (ret)
+ return ret;
+ qcom->ports[port_index].qusb2_phy_irq = irq;
+ }
+
+ return 0;
+}
+
+static int dwc3_qcom_find_num_ports(struct platform_device *pdev)
+{
+ char irq_name[14];
+ int port_num;
+ int irq;
+
+ irq = platform_get_irq_byname_optional(pdev, "dp_hs_phy_1");
+ if (irq <= 0)
+ return 1;
+
+ for (port_num = 2; port_num <= DWC3_QCOM_MAX_PORTS; port_num++) {
+ sprintf(irq_name, "dp_hs_phy_%d", port_num);
+
+ irq = platform_get_irq_byname_optional(pdev, irq_name);
+ if (irq <= 0)
+ return port_num - 1;
+ }
+
+ return DWC3_QCOM_MAX_PORTS;
+}
+
+static int dwc3_qcom_setup_irq(struct platform_device *pdev)
+{
+ struct dwc3_qcom *qcom = platform_get_drvdata(pdev);
+ bool is_multiport;
+ int ret;
+ int i;
+
+ qcom->num_ports = dwc3_qcom_find_num_ports(pdev);
+ is_multiport = (qcom->num_ports > 1);
+
+ for (i = 0; i < qcom->num_ports; i++) {
+ ret = dwc3_qcom_setup_port_irq(pdev, i, is_multiport);
+ if (ret)
return ret;
- }
- qcom->ss_phy_irq = irq;
}
return 0;
@@ -613,11 +703,12 @@ static int dwc3_qcom_clk_init(struct dwc3_qcom *qcom, int count)
static int dwc3_qcom_of_register_core(struct platform_device *pdev)
{
struct dwc3_qcom *qcom = platform_get_drvdata(pdev);
- struct device_node *np = pdev->dev.of_node, *dwc3_np;
+ struct device_node *np = pdev->dev.of_node;
struct device *dev = &pdev->dev;
int ret;
- dwc3_np = of_get_compatible_child(np, "snps,dwc3");
+ struct device_node *dwc3_np __free(device_node) = of_get_compatible_child(np,
+ "snps,dwc3");
if (!dwc3_np) {
dev_err(dev, "failed to find dwc3 core child\n");
return -ENODEV;
@@ -626,7 +717,7 @@ static int dwc3_qcom_of_register_core(struct platform_device *pdev)
ret = of_platform_populate(np, NULL, NULL, dev);
if (ret) {
dev_err(dev, "failed to register dwc3 core - %d\n", ret);
- goto node_put;
+ return ret;
}
qcom->dwc3 = of_find_device_by_node(dwc3_np);
@@ -636,9 +727,6 @@ static int dwc3_qcom_of_register_core(struct platform_device *pdev)
of_platform_depopulate(dev);
}
-node_put:
- of_node_put(dwc3_np);
-
return ret;
}
@@ -647,7 +735,6 @@ static int dwc3_qcom_probe(struct platform_device *pdev)
struct device_node *np = pdev->dev.of_node;
struct device *dev = &pdev->dev;
struct dwc3_qcom *qcom;
- struct resource *res;
int ret, i;
bool ignore_pipe_clk;
bool wakeup_source;
@@ -685,9 +772,7 @@ static int dwc3_qcom_probe(struct platform_device *pdev)
goto reset_assert;
}
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-
- qcom->qscratch_base = devm_ioremap_resource(dev, res);
+ qcom->qscratch_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(qcom->qscratch_base)) {
ret = PTR_ERR(qcom->qscratch_base);
goto clk_disable;
diff --git a/drivers/usb/dwc3/dwc3-rtk.c b/drivers/usb/dwc3/dwc3-rtk.c
index 3cd6b184551c..e9c8b032c72c 100644
--- a/drivers/usb/dwc3/dwc3-rtk.c
+++ b/drivers/usb/dwc3/dwc3-rtk.c
@@ -6,6 +6,7 @@
*
*/
+#include <linux/cleanup.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
@@ -173,23 +174,20 @@ static const char *const speed_names[] = {
static enum usb_device_speed __get_dwc3_maximum_speed(struct device_node *np)
{
- struct device_node *dwc3_np;
const char *maximum_speed;
int ret;
- dwc3_np = of_get_compatible_child(np, "snps,dwc3");
+ struct device_node *dwc3_np __free(device_node) = of_get_compatible_child(np,
+ "snps,dwc3");
if (!dwc3_np)
return USB_SPEED_UNKNOWN;
ret = of_property_read_string(dwc3_np, "maximum-speed", &maximum_speed);
if (ret < 0)
- goto out;
+ return USB_SPEED_UNKNOWN;
ret = match_string(speed_names, ARRAY_SIZE(speed_names), maximum_speed);
-out:
- of_node_put(dwc3_np);
-
return (ret < 0) ? USB_SPEED_UNKNOWN : ret;
}
@@ -276,7 +274,6 @@ static int dwc3_rtk_probe_dwc3_core(struct dwc3_rtk *rtk)
struct device_node *node = dev->of_node;
struct platform_device *dwc3_pdev;
struct device *dwc3_dev;
- struct device_node *dwc3_node;
enum usb_dr_mode dr_mode;
int ret = 0;
@@ -290,7 +287,8 @@ static int dwc3_rtk_probe_dwc3_core(struct dwc3_rtk *rtk)
return ret;
}
- dwc3_node = of_get_compatible_child(node, "snps,dwc3");
+ struct device_node *dwc3_node __free(device_node) = of_get_compatible_child(node,
+ "snps,dwc3");
if (!dwc3_node) {
dev_err(dev, "failed to find dwc3 core node\n");
ret = -ENODEV;
@@ -301,7 +299,7 @@ static int dwc3_rtk_probe_dwc3_core(struct dwc3_rtk *rtk)
if (!dwc3_pdev) {
dev_err(dev, "failed to find dwc3 core platform_device\n");
ret = -ENODEV;
- goto err_node_put;
+ goto depopulate;
}
dwc3_dev = &dwc3_pdev->dev;
@@ -343,14 +341,11 @@ static int dwc3_rtk_probe_dwc3_core(struct dwc3_rtk *rtk)
switch_usb2_role(rtk, rtk->cur_role);
platform_device_put(dwc3_pdev);
- of_node_put(dwc3_node);
return 0;
err_pdev_put:
platform_device_put(dwc3_pdev);
-err_node_put:
- of_node_put(dwc3_node);
depopulate:
of_platform_depopulate(dev);
@@ -363,30 +358,18 @@ static int dwc3_rtk_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct resource *res;
void __iomem *regs;
- int ret = 0;
rtk = devm_kzalloc(dev, sizeof(*rtk), GFP_KERNEL);
- if (!rtk) {
- ret = -ENOMEM;
- goto out;
- }
+ if (!rtk)
+ return -ENOMEM;
platform_set_drvdata(pdev, rtk);
rtk->dev = dev;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(dev, "missing memory resource\n");
- ret = -ENODEV;
- goto out;
- }
-
- regs = devm_ioremap_resource(dev, res);
- if (IS_ERR(regs)) {
- ret = PTR_ERR(regs);
- goto out;
- }
+ regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
rtk->regs = regs;
rtk->regs_size = resource_size(res);
@@ -394,16 +377,11 @@ static int dwc3_rtk_probe(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (res) {
rtk->pm_base = devm_ioremap_resource(dev, res);
- if (IS_ERR(rtk->pm_base)) {
- ret = PTR_ERR(rtk->pm_base);
- goto out;
- }
+ if (IS_ERR(rtk->pm_base))
+ return PTR_ERR(rtk->pm_base);
}
- ret = dwc3_rtk_probe_dwc3_core(rtk);
-
-out:
- return ret;
+ return dwc3_rtk_probe_dwc3_core(rtk);
}
static void dwc3_rtk_remove(struct platform_device *pdev)
diff --git a/drivers/usb/dwc3/dwc3-st.c b/drivers/usb/dwc3/dwc3-st.c
index 211360eee95a..2841021f3557 100644
--- a/drivers/usb/dwc3/dwc3-st.c
+++ b/drivers/usb/dwc3/dwc3-st.c
@@ -14,6 +14,7 @@
* Inspired by dwc3-omap.c and dwc3-exynos.c.
*/
+#include <linux/cleanup.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/io.h>
@@ -197,7 +198,7 @@ static int st_dwc3_probe(struct platform_device *pdev)
struct st_dwc3 *dwc3_data;
struct resource *res;
struct device *dev = &pdev->dev;
- struct device_node *node = dev->of_node, *child;
+ struct device_node *node = dev->of_node;
struct platform_device *child_pdev;
struct regmap *regmap;
int ret;
@@ -219,23 +220,26 @@ static int st_dwc3_probe(struct platform_device *pdev)
dwc3_data->regmap = regmap;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "syscfg-reg");
- if (!res) {
- ret = -ENXIO;
- goto undo_platform_dev_alloc;
- }
+ if (!res)
+ return -ENXIO;
dwc3_data->syscfg_reg_off = res->start;
- dev_vdbg(&pdev->dev, "glue-logic addr 0x%pK, syscfg-reg offset 0x%x\n",
+ dev_vdbg(dev, "glue-logic addr 0x%pK, syscfg-reg offset 0x%x\n",
dwc3_data->glue_base, dwc3_data->syscfg_reg_off);
+ struct device_node *child __free(device_node) = of_get_compatible_child(node,
+ "snps,dwc3");
+ if (!child) {
+ dev_err(dev, "failed to find dwc3 core node\n");
+ return -ENODEV;
+ }
+
dwc3_data->rstc_pwrdn =
devm_reset_control_get_exclusive(dev, "powerdown");
- if (IS_ERR(dwc3_data->rstc_pwrdn)) {
- dev_err(&pdev->dev, "could not get power controller\n");
- ret = PTR_ERR(dwc3_data->rstc_pwrdn);
- goto undo_platform_dev_alloc;
- }
+ if (IS_ERR(dwc3_data->rstc_pwrdn))
+ return dev_err_probe(dev, PTR_ERR(dwc3_data->rstc_pwrdn),
+ "could not get power controller\n");
/* Manage PowerDown */
reset_control_deassert(dwc3_data->rstc_pwrdn);
@@ -243,37 +247,29 @@ static int st_dwc3_probe(struct platform_device *pdev)
dwc3_data->rstc_rst =
devm_reset_control_get_shared(dev, "softreset");
if (IS_ERR(dwc3_data->rstc_rst)) {
- dev_err(&pdev->dev, "could not get reset controller\n");
- ret = PTR_ERR(dwc3_data->rstc_rst);
+ ret = dev_err_probe(dev, PTR_ERR(dwc3_data->rstc_rst),
+ "could not get reset controller\n");
goto undo_powerdown;
}
/* Manage SoftReset */
reset_control_deassert(dwc3_data->rstc_rst);
- child = of_get_compatible_child(node, "snps,dwc3");
- if (!child) {
- dev_err(&pdev->dev, "failed to find dwc3 core node\n");
- ret = -ENODEV;
- goto err_node_put;
- }
-
/* Allocate and initialize the core */
ret = of_platform_populate(node, NULL, NULL, dev);
if (ret) {
dev_err(dev, "failed to add dwc3 core\n");
- goto err_node_put;
+ goto undo_softreset;
}
child_pdev = of_find_device_by_node(child);
if (!child_pdev) {
dev_err(dev, "failed to find dwc3 core device\n");
ret = -ENODEV;
- goto err_node_put;
+ goto depopulate;
}
dwc3_data->dr_mode = usb_get_dr_mode(&child_pdev->dev);
- of_node_put(child);
platform_device_put(child_pdev);
/*
@@ -285,7 +281,7 @@ static int st_dwc3_probe(struct platform_device *pdev)
ret = st_dwc3_drd_init(dwc3_data);
if (ret) {
dev_err(dev, "drd initialisation failed\n");
- goto undo_softreset;
+ goto depopulate;
}
/* ST glue logic init */
@@ -294,14 +290,12 @@ static int st_dwc3_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, dwc3_data);
return 0;
-err_node_put:
- of_node_put(child);
+depopulate:
+ of_platform_depopulate(dev);
undo_softreset:
reset_control_assert(dwc3_data->rstc_rst);
undo_powerdown:
reset_control_assert(dwc3_data->rstc_pwrdn);
-undo_platform_dev_alloc:
- platform_device_put(pdev);
return ret;
}
diff --git a/drivers/usb/dwc3/dwc3-xilinx.c b/drivers/usb/dwc3/dwc3-xilinx.c
index 6095f4dee6ce..b5e5be424ce9 100644
--- a/drivers/usb/dwc3/dwc3-xilinx.c
+++ b/drivers/usb/dwc3/dwc3-xilinx.c
@@ -246,6 +246,31 @@ static const struct of_device_id dwc3_xlnx_of_match[] = {
};
MODULE_DEVICE_TABLE(of, dwc3_xlnx_of_match);
+static int dwc3_set_swnode(struct device *dev)
+{
+ struct device_node *np = dev->of_node, *dwc3_np;
+ struct property_entry props[2];
+ int prop_idx = 0, ret = 0;
+
+ dwc3_np = of_get_compatible_child(np, "snps,dwc3");
+ if (!dwc3_np) {
+ ret = -ENODEV;
+ dev_err(dev, "failed to find dwc3 core child\n");
+ return ret;
+ }
+
+ memset(props, 0, sizeof(struct property_entry) * ARRAY_SIZE(props));
+ if (of_dma_is_coherent(dwc3_np))
+ props[prop_idx++] = PROPERTY_ENTRY_U16("snps,gsbuscfg0-reqinfo",
+ 0xffff);
+ of_node_put(dwc3_np);
+
+ if (prop_idx)
+ ret = device_create_managed_software_node(dev, props, NULL);
+
+ return ret;
+}
+
static int dwc3_xlnx_probe(struct platform_device *pdev)
{
struct dwc3_xlnx *priv_data;
@@ -260,11 +285,8 @@ static int dwc3_xlnx_probe(struct platform_device *pdev)
return -ENOMEM;
regs = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(regs)) {
- ret = PTR_ERR(regs);
- dev_err_probe(dev, ret, "failed to map registers\n");
- return ret;
- }
+ if (IS_ERR(regs))
+ return dev_err_probe(dev, PTR_ERR(regs), "failed to map registers\n");
match = of_match_node(dwc3_xlnx_of_match, pdev->dev.of_node);
@@ -288,6 +310,10 @@ static int dwc3_xlnx_probe(struct platform_device *pdev)
if (ret)
goto err_clk_put;
+ ret = dwc3_set_swnode(dev);
+ if (ret)
+ goto err_clk_put;
+
ret = of_platform_populate(np, NULL, NULL, dev);
if (ret)
goto err_clk_put;
@@ -298,9 +324,14 @@ static int dwc3_xlnx_probe(struct platform_device *pdev)
goto err_pm_set_suspended;
pm_suspend_ignore_children(dev, false);
- return pm_runtime_resume_and_get(dev);
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0)
+ goto err_pm_set_suspended;
+
+ return 0;
err_pm_set_suspended:
+ of_platform_depopulate(dev);
pm_runtime_set_suspended(dev);
err_clk_put:
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index d96ffbe52039..c9533a99e47c 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -232,7 +232,8 @@ void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
/* stall is always issued on EP0 */
dep = dwc->eps[0];
__dwc3_gadget_ep_set_halt(dep, 1, false);
- dep->flags = DWC3_EP_ENABLED;
+ dep->flags &= DWC3_EP_RESOURCE_ALLOCATED;
+ dep->flags |= DWC3_EP_ENABLED;
dwc->delayed_status = false;
if (!list_empty(&dep->pending_list)) {
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index f94f68f1e7d2..291bc549935b 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -287,6 +287,23 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc, bool async);
*
* Caller should handle locking. This function will issue @cmd with given
* @params to @dep and wait for its completion.
+ *
+ * According to the programming guide, if the link state is in L1/L2/U3,
+ * then sending the Start Transfer command may not complete. The
+ * programming guide suggested to bring the link state back to ON/U0 by
+ * performing remote wakeup prior to sending the command. However, don't
+ * initiate remote wakeup when the user/function does not send wakeup
+ * request via wakeup ops. Send the command when it's allowed.
+ *
+ * Notes:
+ * For L1 link state, issuing a command requires the clearing of
+ * GUSB2PHYCFG.SUSPENDUSB2, which turns on the signal required to complete
+ * the given command (usually within 50us). This should happen within the
+ * command timeout set by driver. No additional step is needed.
+ *
+ * For L2 or U3 link state, the gadget is in USB suspend. Care should be
+ * taken when sending Start Transfer command to ensure that it's done after
+ * USB resume.
*/
int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
struct dwc3_gadget_ep_cmd_params *params)
@@ -327,30 +344,6 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
}
- if (DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_STARTTRANSFER) {
- int link_state;
-
- /*
- * Initiate remote wakeup if the link state is in U3 when
- * operating in SS/SSP or L1/L2 when operating in HS/FS. If the
- * link state is in U1/U2, no remote wakeup is needed. The Start
- * Transfer command will initiate the link recovery.
- */
- link_state = dwc3_gadget_get_link_state(dwc);
- switch (link_state) {
- case DWC3_LINK_STATE_U2:
- if (dwc->gadget->speed >= USB_SPEED_SUPER)
- break;
-
- fallthrough;
- case DWC3_LINK_STATE_U3:
- ret = __dwc3_gadget_wakeup(dwc, false);
- dev_WARN_ONCE(dwc->dev, ret, "wakeup failed --> %d\n",
- ret);
- break;
- }
- }
-
/*
* For some commands such as Update Transfer command, DEPCMDPARn
* registers are reserved. Since the driver often sends Update Transfer
@@ -1699,7 +1692,6 @@ static int __dwc3_gadget_get_frame(struct dwc3 *dwc)
*/
static int __dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool interrupt)
{
- struct dwc3 *dwc = dep->dwc;
struct dwc3_gadget_ep_cmd_params params;
u32 cmd;
int ret;
@@ -1724,8 +1716,7 @@ static int __dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool int
dep->resource_index = 0;
if (!interrupt) {
- if (!DWC3_IP_IS(DWC3) || DWC3_VER_IS_PRIOR(DWC3, 310A))
- mdelay(1);
+ mdelay(1);
dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
} else if (!ret) {
dep->flags |= DWC3_EP_END_TRANSFER_PENDING;
diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c
index a171b27a7845..e0533cee6870 100644
--- a/drivers/usb/dwc3/host.c
+++ b/drivers/usb/dwc3/host.c
@@ -126,7 +126,7 @@ out:
int dwc3_host_init(struct dwc3 *dwc)
{
- struct property_entry props[5];
+ struct property_entry props[6];
struct platform_device *xhci;
int ret, irq;
int prop_idx = 0;
@@ -162,6 +162,8 @@ int dwc3_host_init(struct dwc3 *dwc)
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-sg-trb-cache-size-quirk");
+ props[prop_idx++] = PROPERTY_ENTRY_BOOL("write-64-hi-lo-quirk");
+
if (dwc->usb3_lpm_capable)
props[prop_idx++] = PROPERTY_ENTRY_BOOL("usb3-lpm-capable");
diff --git a/drivers/usb/dwc3/trace.h b/drivers/usb/dwc3/trace.h
index d2997d17cfbe..bdeb1aaf65d8 100644
--- a/drivers/usb/dwc3/trace.h
+++ b/drivers/usb/dwc3/trace.h
@@ -112,7 +112,7 @@ DECLARE_EVENT_CLASS(dwc3_log_request,
__field(int, no_interrupt)
),
TP_fast_assign(
- __assign_str(name, req->dep->name);
+ __assign_str(name);
__entry->req = req;
__entry->actual = req->request.actual;
__entry->length = req->request.length;
@@ -193,7 +193,7 @@ DECLARE_EVENT_CLASS(dwc3_log_gadget_ep_cmd,
__field(int, cmd_status)
),
TP_fast_assign(
- __assign_str(name, dep->name);
+ __assign_str(name);
__entry->cmd = cmd;
__entry->param0 = params->param0;
__entry->param1 = params->param1;
@@ -229,7 +229,7 @@ DECLARE_EVENT_CLASS(dwc3_log_trb,
__field(u32, dequeue)
),
TP_fast_assign(
- __assign_str(name, dep->name);
+ __assign_str(name);
__entry->trb = trb;
__entry->bpl = trb->bpl;
__entry->bph = trb->bph;
@@ -301,7 +301,7 @@ DECLARE_EVENT_CLASS(dwc3_log_ep,
__field(u8, trb_dequeue)
),
TP_fast_assign(
- __assign_str(name, dep->name);
+ __assign_str(name);
__entry->maxpacket = dep->endpoint.maxpacket;
__entry->maxpacket_limit = dep->endpoint.maxpacket_limit;
__entry->max_streams = dep->endpoint.max_streams;
diff --git a/drivers/usb/fotg210/Makefile b/drivers/usb/fotg210/Makefile
index 5aecff21f24b..8f5b0fb9b988 100644
--- a/drivers/usb/fotg210/Makefile
+++ b/drivers/usb/fotg210/Makefile
@@ -1,10 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
-# This setup links the different object files into one single
-# module so we don't have to EXPORT() a lot of internal symbols
-# or create unnecessary submodules.
-fotg210-objs-y += fotg210-core.o
-fotg210-objs-$(CONFIG_USB_FOTG210_HCD) += fotg210-hcd.o
-fotg210-objs-$(CONFIG_USB_FOTG210_UDC) += fotg210-udc.o
-fotg210-objs := $(fotg210-objs-y)
obj-$(CONFIG_USB_FOTG210) += fotg210.o
+fotg210-y := fotg210-core.o
+fotg210-$(CONFIG_USB_FOTG210_HCD) += fotg210-hcd.o
+fotg210-$(CONFIG_USB_FOTG210_UDC) += fotg210-udc.o
diff --git a/drivers/usb/fotg210/fotg210-core.c b/drivers/usb/fotg210/fotg210-core.c
index 958fc40eae86..0655afe7f977 100644
--- a/drivers/usb/fotg210/fotg210-core.c
+++ b/drivers/usb/fotg210/fotg210-core.c
@@ -95,6 +95,7 @@ static int fotg210_gemini_init(struct fotg210 *fotg, struct resource *res,
/**
* fotg210_vbus() - Called by gadget driver to enable/disable VBUS
+ * @fotg: pointer to a private fotg210 object
* @enable: true to enable VBUS, false to disable VBUS
*/
void fotg210_vbus(struct fotg210 *fotg, bool enable)
diff --git a/drivers/usb/fotg210/fotg210-hcd.c b/drivers/usb/fotg210/fotg210-hcd.c
index 8c5aaf860635..3d404d19a205 100644
--- a/drivers/usb/fotg210/fotg210-hcd.c
+++ b/drivers/usb/fotg210/fotg210-hcd.c
@@ -36,7 +36,7 @@
#include <asm/byteorder.h>
#include <asm/irq.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include "fotg210.h"
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 0e151b54aae8..f25dd2cb5d03 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -19,7 +19,7 @@
#include <linux/usb/composite.h>
#include <linux/usb/otg.h>
#include <linux/usb/webusb.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include "u_os_desc.h"
@@ -2799,5 +2799,6 @@ void usb_composite_overwrite_options(struct usb_composite_dev *cdev,
}
EXPORT_SYMBOL_GPL(usb_composite_overwrite_options);
+MODULE_DESCRIPTION("infrastructure for Composite USB Gadgets");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("David Brownell");
diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c
index ce3cfa1f36f5..c82a6a0fba93 100644
--- a/drivers/usb/gadget/configfs.c
+++ b/drivers/usb/gadget/configfs.c
@@ -6,13 +6,13 @@
#include <linux/kstrtox.h>
#include <linux/nls.h>
#include <linux/usb/composite.h>
+#include <linux/usb/func_utils.h>
#include <linux/usb/gadget_configfs.h>
#include <linux/usb/webusb.h>
#include "configfs.h"
-#include "u_f.h"
#include "u_os_desc.h"
-int check_user_usb_string(const char *name,
+static int check_user_usb_string(const char *name,
struct usb_gadget_strings *stringtab_dev)
{
u16 num;
@@ -115,9 +115,12 @@ static int usb_string_copy(const char *s, char **s_copy)
int ret;
char *str;
char *copy = *s_copy;
+
ret = strlen(s);
if (ret > USB_MAX_STRING_LEN)
return -EOVERFLOW;
+ if (ret < 1)
+ return -EINVAL;
if (copy) {
str = copy;
@@ -899,7 +902,7 @@ static struct configfs_group_operations gadget_language_langid_group_ops = {
.drop_item = gadget_language_string_drop,
};
-static struct config_item_type gadget_language_type = {
+static const struct config_item_type gadget_language_type = {
.ct_item_ops = &gadget_language_langid_item_ops,
.ct_group_ops = &gadget_language_langid_group_ops,
.ct_attrs = gadget_language_langid_attrs,
@@ -958,7 +961,7 @@ static struct configfs_group_operations gadget_language_group_ops = {
.drop_item = &gadget_language_drop,
};
-static struct config_item_type gadget_language_strings_type = {
+static const struct config_item_type gadget_language_strings_type = {
.ct_group_ops = &gadget_language_group_ops,
.ct_owner = THIS_MODULE,
};
@@ -1103,7 +1106,7 @@ static struct configfs_attribute *webusb_attrs[] = {
NULL,
};
-static struct config_item_type webusb_type = {
+static const struct config_item_type webusb_type = {
.ct_attrs = webusb_attrs,
.ct_owner = THIS_MODULE,
};
@@ -1260,7 +1263,7 @@ static struct configfs_item_operations os_desc_ops = {
.drop_link = os_desc_unlink,
};
-static struct config_item_type os_desc_type = {
+static const struct config_item_type os_desc_type = {
.ct_item_ops = &os_desc_ops,
.ct_attrs = os_desc_attrs,
.ct_owner = THIS_MODULE,
diff --git a/drivers/usb/gadget/function/f_acm.c b/drivers/usb/gadget/function/f_acm.c
index f616059c5e1e..7061720b9732 100644
--- a/drivers/usb/gadget/function/f_acm.c
+++ b/drivers/usb/gadget/function/f_acm.c
@@ -41,6 +41,7 @@ struct f_acm {
struct gserial port;
u8 ctrl_id, data_id;
u8 port_num;
+ u8 bInterfaceProtocol;
u8 pending;
@@ -89,7 +90,7 @@ acm_iad_descriptor = {
.bInterfaceCount = 2, // control + data
.bFunctionClass = USB_CLASS_COMM,
.bFunctionSubClass = USB_CDC_SUBCLASS_ACM,
- .bFunctionProtocol = USB_CDC_ACM_PROTO_AT_V25TER,
+ /* .bFunctionProtocol = DYNAMIC */
/* .iFunction = DYNAMIC */
};
@@ -101,7 +102,7 @@ static struct usb_interface_descriptor acm_control_interface_desc = {
.bNumEndpoints = 1,
.bInterfaceClass = USB_CLASS_COMM,
.bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
- .bInterfaceProtocol = USB_CDC_ACM_PROTO_AT_V25TER,
+ /* .bInterfaceProtocol = DYNAMIC */
/* .iInterface = DYNAMIC */
};
@@ -663,6 +664,9 @@ acm_bind(struct usb_configuration *c, struct usb_function *f)
goto fail;
acm->notify = ep;
+ acm_iad_descriptor.bFunctionProtocol = acm->bInterfaceProtocol;
+ acm_control_interface_desc.bInterfaceProtocol = acm->bInterfaceProtocol;
+
/* allocate notification */
acm->notify_req = gs_alloc_req(ep,
sizeof(struct usb_cdc_notification) + 2,
@@ -719,8 +723,14 @@ static void acm_unbind(struct usb_configuration *c, struct usb_function *f)
static void acm_free_func(struct usb_function *f)
{
struct f_acm *acm = func_to_acm(f);
+ struct f_serial_opts *opts;
+
+ opts = container_of(f->fi, struct f_serial_opts, func_inst);
kfree(acm);
+ mutex_lock(&opts->lock);
+ opts->instances--;
+ mutex_unlock(&opts->lock);
}
static void acm_resume(struct usb_function *f)
@@ -761,7 +771,11 @@ static struct usb_function *acm_alloc_func(struct usb_function_instance *fi)
acm->port.func.disable = acm_disable;
opts = container_of(fi, struct f_serial_opts, func_inst);
+ mutex_lock(&opts->lock);
acm->port_num = opts->port_num;
+ acm->bInterfaceProtocol = opts->protocol;
+ opts->instances++;
+ mutex_unlock(&opts->lock);
acm->port.func.unbind = acm_unbind;
acm->port.func.free_func = acm_free_func;
acm->port.func.resume = acm_resume;
@@ -812,11 +826,42 @@ static ssize_t f_acm_port_num_show(struct config_item *item, char *page)
CONFIGFS_ATTR_RO(f_acm_, port_num);
+static ssize_t f_acm_protocol_show(struct config_item *item, char *page)
+{
+ return sprintf(page, "%u\n", to_f_serial_opts(item)->protocol);
+}
+
+static ssize_t f_acm_protocol_store(struct config_item *item,
+ const char *page, size_t count)
+{
+ struct f_serial_opts *opts = to_f_serial_opts(item);
+ int ret;
+
+ mutex_lock(&opts->lock);
+
+ if (opts->instances) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ ret = kstrtou8(page, 0, &opts->protocol);
+ if (ret)
+ goto out;
+ ret = count;
+
+out:
+ mutex_unlock(&opts->lock);
+ return ret;
+}
+
+CONFIGFS_ATTR(f_acm_, protocol);
+
static struct configfs_attribute *acm_attrs[] = {
#ifdef CONFIG_U_SERIAL_CONSOLE
&f_acm_attr_console,
#endif
&f_acm_attr_port_num,
+ &f_acm_attr_protocol,
NULL,
};
@@ -832,6 +877,7 @@ static void acm_free_instance(struct usb_function_instance *fi)
opts = container_of(fi, struct f_serial_opts, func_inst);
gserial_free_line(opts->port_num);
+ mutex_destroy(&opts->lock);
kfree(opts);
}
@@ -843,7 +889,9 @@ static struct usb_function_instance *acm_alloc_instance(void)
opts = kzalloc(sizeof(*opts), GFP_KERNEL);
if (!opts)
return ERR_PTR(-ENOMEM);
+ opts->protocol = USB_CDC_ACM_PROTO_AT_V25TER;
opts->func_inst.free_func_inst = acm_free_instance;
+ mutex_init(&opts->lock);
ret = gserial_alloc_line(&opts->port_num);
if (ret) {
kfree(opts);
@@ -854,4 +902,5 @@ static struct usb_function_instance *acm_alloc_instance(void)
return &opts->func_inst;
}
DECLARE_USB_FUNCTION_INIT(acm, acm_alloc_instance, acm_alloc_func);
+MODULE_DESCRIPTION("USB CDC serial (ACM) function driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/function/f_ecm.c b/drivers/usb/gadget/function/f_ecm.c
index f55f60639e42..6cb7771e8a69 100644
--- a/drivers/usb/gadget/function/f_ecm.c
+++ b/drivers/usb/gadget/function/f_ecm.c
@@ -966,5 +966,6 @@ static struct usb_function *ecm_alloc(struct usb_function_instance *fi)
}
DECLARE_USB_FUNCTION_INIT(ecm, ecm_alloc_inst, ecm_alloc);
+MODULE_DESCRIPTION("USB CDC Ethernet (ECM) link function driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("David Brownell");
diff --git a/drivers/usb/gadget/function/f_eem.c b/drivers/usb/gadget/function/f_eem.c
index 3b445bd88498..6de81ea17274 100644
--- a/drivers/usb/gadget/function/f_eem.c
+++ b/drivers/usb/gadget/function/f_eem.c
@@ -674,5 +674,6 @@ static struct usb_function *eem_alloc(struct usb_function_instance *fi)
}
DECLARE_USB_FUNCTION_INIT(eem, eem_alloc_inst, eem_alloc);
+MODULE_DESCRIPTION("USB CDC Ethernet (EEM) link function driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("David Brownell");
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index a057cbedf3c9..2920f8000bbd 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -28,11 +28,12 @@
#include <linux/sched/signal.h>
#include <linux/uio.h>
#include <linux/vmalloc.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <linux/usb/ccid.h>
#include <linux/usb/composite.h>
#include <linux/usb/functionfs.h>
+#include <linux/usb/func_utils.h>
#include <linux/aio.h>
#include <linux/kthread.h>
@@ -40,11 +41,11 @@
#include <linux/eventfd.h>
#include "u_fs.h"
-#include "u_f.h"
#include "u_os_desc.h"
#include "configfs.h"
#define FUNCTIONFS_MAGIC 0xa647361 /* Chosen by a honest dice roll ;) */
+#define MAX_ALT_SETTINGS 2 /* Allow up to 2 alt settings to be set. */
#define DMABUF_ENQUEUE_TIMEOUT_MS 5000
@@ -82,6 +83,7 @@ struct ffs_function {
short *interfaces_nums;
struct usb_function function;
+ int cur_alt[MAX_CONFIG_INTERFACES];
};
@@ -105,6 +107,7 @@ static int __must_check ffs_func_eps_enable(struct ffs_function *func);
static int ffs_func_bind(struct usb_configuration *,
struct usb_function *);
static int ffs_func_set_alt(struct usb_function *, unsigned, unsigned);
+static int ffs_func_get_alt(struct usb_function *f, unsigned int intf);
static void ffs_func_disable(struct usb_function *);
static int ffs_func_setup(struct usb_function *,
const struct usb_ctrlrequest *);
@@ -719,7 +722,6 @@ static __poll_t ffs_ep0_poll(struct file *file, poll_table *wait)
}
static const struct file_operations ffs_ep0_operations = {
- .llseek = no_llseek,
.open = ffs_ep0_open,
.write = ffs_ep0_write,
@@ -1827,7 +1829,6 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code,
}
static const struct file_operations ffs_epfile_operations = {
- .llseek = no_llseek,
.open = ffs_epfile_open,
.write_iter = ffs_epfile_write_iter,
@@ -2475,7 +2476,7 @@ typedef int (*ffs_os_desc_callback)(enum ffs_os_desc_type entity,
static int __must_check ffs_do_single_desc(char *data, unsigned len,
ffs_entity_callback entity,
- void *priv, int *current_class)
+ void *priv, int *current_class, int *current_subclass)
{
struct usb_descriptor_header *_ds = (void *)data;
u8 length;
@@ -2532,6 +2533,7 @@ static int __must_check ffs_do_single_desc(char *data, unsigned len,
if (ds->iInterface)
__entity(STRING, ds->iInterface);
*current_class = ds->bInterfaceClass;
+ *current_subclass = ds->bInterfaceSubClass;
}
break;
@@ -2556,6 +2558,12 @@ static int __must_check ffs_do_single_desc(char *data, unsigned len,
if (length != sizeof(struct ccid_descriptor))
goto inv_length;
break;
+ } else if (*current_class == USB_CLASS_APP_SPEC &&
+ *current_subclass == USB_SUBCLASS_DFU) {
+ pr_vdebug("dfu functional descriptor\n");
+ if (length != sizeof(struct usb_dfu_functional_descriptor))
+ goto inv_length;
+ break;
} else {
pr_vdebug("unknown descriptor: %d for class %d\n",
_ds->bDescriptorType, *current_class);
@@ -2618,6 +2626,7 @@ static int __must_check ffs_do_descs(unsigned count, char *data, unsigned len,
const unsigned _len = len;
unsigned long num = 0;
int current_class = -1;
+ int current_subclass = -1;
for (;;) {
int ret;
@@ -2637,7 +2646,7 @@ static int __must_check ffs_do_descs(unsigned count, char *data, unsigned len,
return _len - len;
ret = ffs_do_single_desc(data, len, entity, priv,
- &current_class);
+ &current_class, &current_subclass);
if (ret < 0) {
pr_debug("%s returns %d\n", __func__, ret);
return ret;
@@ -3712,6 +3721,15 @@ static void ffs_reset_work(struct work_struct *work)
ffs_data_reset(ffs);
}
+static int ffs_func_get_alt(struct usb_function *f,
+ unsigned int interface)
+{
+ struct ffs_function *func = ffs_func_from_usb(f);
+ int intf = ffs_func_revmap_intf(func, interface);
+
+ return (intf < 0) ? intf : func->cur_alt[interface];
+}
+
static int ffs_func_set_alt(struct usb_function *f,
unsigned interface, unsigned alt)
{
@@ -3719,11 +3737,12 @@ static int ffs_func_set_alt(struct usb_function *f,
struct ffs_data *ffs = func->ffs;
int ret = 0, intf;
- if (alt != (unsigned)-1) {
- intf = ffs_func_revmap_intf(func, interface);
- if (intf < 0)
- return intf;
- }
+ if (alt > MAX_ALT_SETTINGS)
+ return -EINVAL;
+
+ intf = ffs_func_revmap_intf(func, interface);
+ if (intf < 0)
+ return intf;
if (ffs->func)
ffs_func_eps_disable(ffs->func);
@@ -3738,22 +3757,34 @@ static int ffs_func_set_alt(struct usb_function *f,
if (ffs->state != FFS_ACTIVE)
return -ENODEV;
- if (alt == (unsigned)-1) {
- ffs->func = NULL;
- ffs_event_add(ffs, FUNCTIONFS_DISABLE);
- return 0;
- }
-
ffs->func = func;
ret = ffs_func_eps_enable(func);
- if (ret >= 0)
+ if (ret >= 0) {
ffs_event_add(ffs, FUNCTIONFS_ENABLE);
+ func->cur_alt[interface] = alt;
+ }
return ret;
}
static void ffs_func_disable(struct usb_function *f)
{
- ffs_func_set_alt(f, 0, (unsigned)-1);
+ struct ffs_function *func = ffs_func_from_usb(f);
+ struct ffs_data *ffs = func->ffs;
+
+ if (ffs->func)
+ ffs_func_eps_disable(ffs->func);
+
+ if (ffs->state == FFS_DEACTIVATED) {
+ ffs->state = FFS_CLOSING;
+ INIT_WORK(&ffs->reset_work, ffs_reset_work);
+ schedule_work(&ffs->reset_work);
+ return;
+ }
+
+ if (ffs->state == FFS_ACTIVE) {
+ ffs->func = NULL;
+ ffs_event_add(ffs, FUNCTIONFS_DISABLE);
+ }
}
static int ffs_func_setup(struct usb_function *f,
@@ -4074,6 +4105,7 @@ static struct usb_function *ffs_alloc(struct usb_function_instance *fi)
func->function.bind = ffs_func_bind;
func->function.unbind = ffs_func_unbind;
func->function.set_alt = ffs_func_set_alt;
+ func->function.get_alt = ffs_func_get_alt;
func->function.disable = ffs_func_disable;
func->function.setup = ffs_func_setup;
func->function.req_match = ffs_func_req_match;
@@ -4298,5 +4330,6 @@ static char *ffs_prepare_buffer(const char __user *buf, size_t len)
}
DECLARE_USB_FUNCTION_INIT(ffs, ffs_alloc_inst, ffs_alloc);
+MODULE_DESCRIPTION("user mode file system API for USB composite function controllers");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Michal Nazarewicz");
diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c
index 3c8a9dd585c0..740311c4fa24 100644
--- a/drivers/usb/gadget/function/f_hid.c
+++ b/drivers/usb/gadget/function/f_hid.c
@@ -15,13 +15,21 @@
#include <linux/uaccess.h>
#include <linux/wait.h>
#include <linux/sched.h>
+#include <linux/workqueue.h>
+#include <linux/usb/func_utils.h>
#include <linux/usb/g_hid.h>
+#include <uapi/linux/usb/g_hid.h>
-#include "u_f.h"
#include "u_hid.h"
#define HIDG_MINORS 4
+/*
+ * Most operating systems seem to allow for 5000ms timeout, we will allow
+ * userspace half that time to respond before we return an empty report.
+ */
+#define GET_REPORT_TIMEOUT_MS 2500
+
static int major, minors;
static const struct class hidg_class = {
@@ -31,6 +39,11 @@ static const struct class hidg_class = {
static DEFINE_IDA(hidg_ida);
static DEFINE_MUTEX(hidg_ida_lock); /* protects access to hidg_ida */
+struct report_entry {
+ struct usb_hidg_report report_data;
+ struct list_head node;
+};
+
/*-------------------------------------------------------------------------*/
/* HID gadget struct */
@@ -75,6 +88,19 @@ struct f_hidg {
wait_queue_head_t write_queue;
struct usb_request *req;
+ /* get report */
+ struct usb_request *get_req;
+ struct usb_hidg_report get_report;
+ bool get_report_returned;
+ int get_report_req_report_id;
+ int get_report_req_report_length;
+ spinlock_t get_report_spinlock;
+ wait_queue_head_t get_queue; /* Waiting for userspace response */
+ wait_queue_head_t get_id_queue; /* Get ID came in */
+ struct work_struct work;
+ struct workqueue_struct *workqueue;
+ struct list_head report_list;
+
struct device dev;
struct cdev cdev;
struct usb_function func;
@@ -524,6 +550,174 @@ release_write_pending:
return status;
}
+static struct report_entry *f_hidg_search_for_report(struct f_hidg *hidg, u8 report_id)
+{
+ struct list_head *ptr;
+ struct report_entry *entry;
+
+ list_for_each(ptr, &hidg->report_list) {
+ entry = list_entry(ptr, struct report_entry, node);
+ if (entry->report_data.report_id == report_id)
+ return entry;
+ }
+
+ return NULL;
+}
+
+static void get_report_workqueue_handler(struct work_struct *work)
+{
+ struct f_hidg *hidg = container_of(work, struct f_hidg, work);
+ struct usb_composite_dev *cdev = hidg->func.config->cdev;
+ struct usb_request *req;
+ struct report_entry *ptr;
+ unsigned long flags;
+
+ int status = 0;
+
+ spin_lock_irqsave(&hidg->get_report_spinlock, flags);
+ req = hidg->get_req;
+ if (!req) {
+ spin_unlock_irqrestore(&hidg->get_report_spinlock, flags);
+ return;
+ }
+
+ req->zero = 0;
+ req->length = min_t(unsigned int, min_t(unsigned int, hidg->get_report_req_report_length,
+ hidg->report_length),
+ MAX_REPORT_LENGTH);
+
+ /* Check if there is a response available for immediate response */
+ ptr = f_hidg_search_for_report(hidg, hidg->get_report_req_report_id);
+ if (ptr && !ptr->report_data.userspace_req) {
+ /* Report exists in list and it is to be used for immediate response */
+ req->buf = ptr->report_data.data;
+ status = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+ hidg->get_report_returned = true;
+ spin_unlock_irqrestore(&hidg->get_report_spinlock, flags);
+ } else {
+ /*
+ * Report does not exist in list or should not be immediately sent
+ * i.e. give userspace time to respond
+ */
+ hidg->get_report_returned = false;
+ spin_unlock_irqrestore(&hidg->get_report_spinlock, flags);
+ wake_up(&hidg->get_id_queue);
+#define GET_REPORT_COND (!hidg->get_report_returned)
+ /* Wait until userspace has responded or timeout */
+ status = wait_event_interruptible_timeout(hidg->get_queue, !GET_REPORT_COND,
+ msecs_to_jiffies(GET_REPORT_TIMEOUT_MS));
+ spin_lock_irqsave(&hidg->get_report_spinlock, flags);
+ req = hidg->get_req;
+ if (!req) {
+ spin_unlock_irqrestore(&hidg->get_report_spinlock, flags);
+ return;
+ }
+ if (status == 0 && !hidg->get_report_returned) {
+ /* GET_REPORT request was not serviced by userspace within timeout period */
+ VDBG(cdev, "get_report : userspace timeout.\n");
+ hidg->get_report_returned = true;
+ }
+
+ /* Search again for report ID in list and respond to GET_REPORT request */
+ ptr = f_hidg_search_for_report(hidg, hidg->get_report_req_report_id);
+ if (ptr) {
+ /*
+ * Either get an updated response just serviced by userspace
+ * or send the latest response in the list
+ */
+ req->buf = ptr->report_data.data;
+ } else {
+ /* If there are no prevoiusly sent reports send empty report */
+ req->buf = hidg->get_report.data;
+ memset(req->buf, 0x0, req->length);
+ }
+
+ status = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+ spin_unlock_irqrestore(&hidg->get_report_spinlock, flags);
+ }
+
+ if (status < 0)
+ VDBG(cdev, "usb_ep_queue error on ep0 responding to GET_REPORT\n");
+}
+
+static int f_hidg_get_report_id(struct file *file, __u8 __user *buffer)
+{
+ struct f_hidg *hidg = file->private_data;
+ int ret = 0;
+
+ ret = put_user(hidg->get_report_req_report_id, buffer);
+
+ return ret;
+}
+
+static int f_hidg_get_report(struct file *file, struct usb_hidg_report __user *buffer)
+{
+ struct f_hidg *hidg = file->private_data;
+ struct usb_composite_dev *cdev = hidg->func.config->cdev;
+ unsigned long flags;
+ struct report_entry *entry;
+ struct report_entry *ptr;
+ __u8 report_id;
+
+ entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry)
+ return -ENOMEM;
+
+ if (copy_from_user(&entry->report_data, buffer,
+ sizeof(struct usb_hidg_report))) {
+ ERROR(cdev, "copy_from_user error\n");
+ kfree(entry);
+ return -EINVAL;
+ }
+
+ report_id = entry->report_data.report_id;
+
+ spin_lock_irqsave(&hidg->get_report_spinlock, flags);
+ ptr = f_hidg_search_for_report(hidg, report_id);
+
+ if (ptr) {
+ /* Report already exists in list - update it */
+ if (copy_from_user(&ptr->report_data, buffer,
+ sizeof(struct usb_hidg_report))) {
+ spin_unlock_irqrestore(&hidg->get_report_spinlock, flags);
+ ERROR(cdev, "copy_from_user error\n");
+ kfree(entry);
+ return -EINVAL;
+ }
+ kfree(entry);
+ } else {
+ /* Report does not exist in list - add it */
+ list_add_tail(&entry->node, &hidg->report_list);
+ }
+
+ /* If there is no response pending then do nothing further */
+ if (hidg->get_report_returned) {
+ spin_unlock_irqrestore(&hidg->get_report_spinlock, flags);
+ return 0;
+ }
+
+ /* If this userspace response serves the current pending report */
+ if (hidg->get_report_req_report_id == report_id) {
+ hidg->get_report_returned = true;
+ wake_up(&hidg->get_queue);
+ }
+
+ spin_unlock_irqrestore(&hidg->get_report_spinlock, flags);
+ return 0;
+}
+
+static long f_hidg_ioctl(struct file *file, unsigned int code, unsigned long arg)
+{
+ switch (code) {
+ case GADGET_HID_READ_GET_REPORT_ID:
+ return f_hidg_get_report_id(file, (__u8 __user *)arg);
+ case GADGET_HID_WRITE_GET_REPORT:
+ return f_hidg_get_report(file, (struct usb_hidg_report __user *)arg);
+ default:
+ return -ENOTTY;
+ }
+}
+
static __poll_t f_hidg_poll(struct file *file, poll_table *wait)
{
struct f_hidg *hidg = file->private_data;
@@ -531,6 +725,8 @@ static __poll_t f_hidg_poll(struct file *file, poll_table *wait)
poll_wait(file, &hidg->read_queue, wait);
poll_wait(file, &hidg->write_queue, wait);
+ poll_wait(file, &hidg->get_queue, wait);
+ poll_wait(file, &hidg->get_id_queue, wait);
if (WRITE_COND)
ret |= EPOLLOUT | EPOLLWRNORM;
@@ -543,12 +739,16 @@ static __poll_t f_hidg_poll(struct file *file, poll_table *wait)
ret |= EPOLLIN | EPOLLRDNORM;
}
+ if (GET_REPORT_COND)
+ ret |= EPOLLPRI;
+
return ret;
}
#undef WRITE_COND
#undef READ_COND_SSREPORT
#undef READ_COND_INTOUT
+#undef GET_REPORT_COND
static int f_hidg_release(struct inode *inode, struct file *fd)
{
@@ -641,6 +841,10 @@ static void hidg_ssreport_complete(struct usb_ep *ep, struct usb_request *req)
wake_up(&hidg->read_queue);
}
+static void hidg_get_report_complete(struct usb_ep *ep, struct usb_request *req)
+{
+}
+
static int hidg_setup(struct usb_function *f,
const struct usb_ctrlrequest *ctrl)
{
@@ -649,6 +853,7 @@ static int hidg_setup(struct usb_function *f,
struct usb_request *req = cdev->req;
int status = 0;
__u16 value, length;
+ unsigned long flags;
value = __le16_to_cpu(ctrl->wValue);
length = __le16_to_cpu(ctrl->wLength);
@@ -660,14 +865,20 @@ static int hidg_setup(struct usb_function *f,
switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
| HID_REQ_GET_REPORT):
- VDBG(cdev, "get_report\n");
+ VDBG(cdev, "get_report | wLength=%d\n", ctrl->wLength);
- /* send an empty report */
- length = min_t(unsigned, length, hidg->report_length);
- memset(req->buf, 0x0, length);
+ /*
+ * Update GET_REPORT ID so that an ioctl can be used to determine what
+ * GET_REPORT the request was actually for.
+ */
+ spin_lock_irqsave(&hidg->get_report_spinlock, flags);
+ hidg->get_report_req_report_id = value & 0xff;
+ hidg->get_report_req_report_length = length;
+ spin_unlock_irqrestore(&hidg->get_report_spinlock, flags);
- goto respond;
- break;
+ queue_work(hidg->workqueue, &hidg->work);
+
+ return status;
case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
| HID_REQ_GET_PROTOCOL):
@@ -793,6 +1004,14 @@ static void hidg_disable(struct usb_function *f)
spin_unlock_irqrestore(&hidg->read_spinlock, flags);
}
+ spin_lock_irqsave(&hidg->get_report_spinlock, flags);
+ if (!hidg->get_report_returned) {
+ usb_ep_free_request(f->config->cdev->gadget->ep0, hidg->get_req);
+ hidg->get_req = NULL;
+ hidg->get_report_returned = true;
+ }
+ spin_unlock_irqrestore(&hidg->get_report_spinlock, flags);
+
spin_lock_irqsave(&hidg->write_spinlock, flags);
if (!hidg->write_pending) {
free_ep_req(hidg->in_ep, hidg->req);
@@ -902,6 +1121,14 @@ fail:
return status;
}
+#ifdef CONFIG_COMPAT
+static long f_hidg_compat_ioctl(struct file *file, unsigned int code,
+ unsigned long value)
+{
+ return f_hidg_ioctl(file, code, value);
+}
+#endif
+
static const struct file_operations f_hidg_fops = {
.owner = THIS_MODULE,
.open = f_hidg_open,
@@ -909,6 +1136,10 @@ static const struct file_operations f_hidg_fops = {
.write = f_hidg_write,
.read = f_hidg_read,
.poll = f_hidg_poll,
+ .unlocked_ioctl = f_hidg_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = f_hidg_compat_ioctl,
+#endif
.llseek = noop_llseek,
};
@@ -919,6 +1150,15 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f)
struct usb_string *us;
int status;
+ hidg->get_req = usb_ep_alloc_request(c->cdev->gadget->ep0, GFP_ATOMIC);
+ if (!hidg->get_req)
+ return -ENOMEM;
+
+ hidg->get_req->zero = 0;
+ hidg->get_req->complete = hidg_get_report_complete;
+ hidg->get_req->context = hidg;
+ hidg->get_report_returned = true;
+
/* maybe allocate device-global string IDs, and patch descriptors */
us = usb_gstrings_attach(c->cdev, ct_func_strings,
ARRAY_SIZE(ct_func_string_defs));
@@ -1004,9 +1244,24 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f)
hidg->write_pending = 1;
hidg->req = NULL;
spin_lock_init(&hidg->read_spinlock);
+ spin_lock_init(&hidg->get_report_spinlock);
init_waitqueue_head(&hidg->write_queue);
init_waitqueue_head(&hidg->read_queue);
+ init_waitqueue_head(&hidg->get_queue);
+ init_waitqueue_head(&hidg->get_id_queue);
INIT_LIST_HEAD(&hidg->completed_out_req);
+ INIT_LIST_HEAD(&hidg->report_list);
+
+ INIT_WORK(&hidg->work, get_report_workqueue_handler);
+ hidg->workqueue = alloc_workqueue("report_work",
+ WQ_FREEZABLE |
+ WQ_MEM_RECLAIM,
+ 1);
+
+ if (!hidg->workqueue) {
+ status = -ENOMEM;
+ goto fail;
+ }
/* create char device */
cdev_init(&hidg->cdev, &f_hidg_fops);
@@ -1016,12 +1271,16 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f)
return 0;
fail_free_descs:
+ destroy_workqueue(hidg->workqueue);
usb_free_all_descriptors(f);
fail:
ERROR(f->config->cdev, "hidg_bind FAILED\n");
if (hidg->req != NULL)
free_ep_req(hidg->in_ep, hidg->req);
+ usb_ep_free_request(c->cdev->gadget->ep0, hidg->get_req);
+ hidg->get_req = NULL;
+
return status;
}
@@ -1029,9 +1288,9 @@ static inline int hidg_get_minor(void)
{
int ret;
- ret = ida_simple_get(&hidg_ida, 0, 0, GFP_KERNEL);
+ ret = ida_alloc(&hidg_ida, GFP_KERNEL);
if (ret >= HIDG_MINORS) {
- ida_simple_remove(&hidg_ida, ret);
+ ida_free(&hidg_ida, ret);
ret = -ENODEV;
}
@@ -1176,7 +1435,7 @@ static const struct config_item_type hid_func_type = {
static inline void hidg_put_minor(int minor)
{
- ida_simple_remove(&hidg_ida, minor);
+ ida_free(&hidg_ida, minor);
}
static void hidg_free_inst(struct usb_function_instance *f)
@@ -1256,7 +1515,7 @@ static void hidg_unbind(struct usb_configuration *c, struct usb_function *f)
struct f_hidg *hidg = func_to_hidg(f);
cdev_device_del(&hidg->cdev, &hidg->dev);
-
+ destroy_workqueue(hidg->workqueue);
usb_free_all_descriptors(f);
}
@@ -1322,6 +1581,7 @@ err_unlock:
}
DECLARE_USB_FUNCTION_INIT(hid, hidg_alloc_inst, hidg_alloc);
+MODULE_DESCRIPTION("USB HID function driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Fabien Chouteau");
diff --git a/drivers/usb/gadget/function/f_loopback.c b/drivers/usb/gadget/function/f_loopback.c
index 17ac6ace0cff..49b009a7d5d7 100644
--- a/drivers/usb/gadget/function/f_loopback.c
+++ b/drivers/usb/gadget/function/f_loopback.c
@@ -14,9 +14,9 @@
#include <linux/module.h>
#include <linux/err.h>
#include <linux/usb/composite.h>
+#include <linux/usb/func_utils.h>
#include "g_zero.h"
-#include "u_f.h"
/*
* LOOPBACK FUNCTION ... a testing vehicle for USB peripherals,
@@ -593,4 +593,5 @@ void __exit lb_modexit(void)
usb_function_unregister(&Loopbackusb_func);
}
+MODULE_DESCRIPTION("USB peripheral loopback configuration driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c
index c265a1f62fc1..08e0d1c511e8 100644
--- a/drivers/usb/gadget/function/f_mass_storage.c
+++ b/drivers/usb/gadget/function/f_mass_storage.c
@@ -188,7 +188,7 @@
#include <linux/freezer.h>
#include <linux/module.h>
#include <linux/uaccess.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
@@ -3050,7 +3050,7 @@ static int fsg_bind(struct usb_configuration *c, struct usb_function *f)
if (!common->thread_task) {
common->state = FSG_STATE_NORMAL;
common->thread_task =
- kthread_create(fsg_main_thread, common, "file-storage");
+ kthread_run(fsg_main_thread, common, "file-storage");
if (IS_ERR(common->thread_task)) {
ret = PTR_ERR(common->thread_task);
common->thread_task = NULL;
@@ -3059,7 +3059,6 @@ static int fsg_bind(struct usb_configuration *c, struct usb_function *f)
}
DBG(common, "I/O thread pid: %d\n",
task_pid_nr(common->thread_task));
- wake_up_process(common->thread_task);
}
fsg->gadget = gadget;
@@ -3577,6 +3576,7 @@ static struct usb_function *fsg_alloc(struct usb_function_instance *fi)
}
DECLARE_USB_FUNCTION_INIT(mass_storage, fsg_alloc_inst, fsg_alloc);
+MODULE_DESCRIPTION("Mass Storage USB Composite Function");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Michal Nazarewicz");
diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c
index 20c6fbd94f32..1067847cc079 100644
--- a/drivers/usb/gadget/function/f_midi.c
+++ b/drivers/usb/gadget/function/f_midi.c
@@ -30,14 +30,15 @@
#include <sound/rawmidi.h>
#include <linux/usb/ch9.h>
+#include <linux/usb/func_utils.h>
#include <linux/usb/gadget.h>
#include <linux/usb/audio.h>
#include <linux/usb/midi.h>
-#include "u_f.h"
#include "u_midi.h"
MODULE_AUTHOR("Ben Williamson");
+MODULE_DESCRIPTION("USB MIDI class function driver");
MODULE_LICENSE("GPL v2");
static const char f_midi_shortname[] = "f_midi";
diff --git a/drivers/usb/gadget/function/f_midi2.c b/drivers/usb/gadget/function/f_midi2.c
index ec8cd7c7bbfc..8285df9ed6fd 100644
--- a/drivers/usb/gadget/function/f_midi2.c
+++ b/drivers/usb/gadget/function/f_midi2.c
@@ -15,11 +15,11 @@
#include <sound/ump_convert.h>
#include <linux/usb/ch9.h>
+#include <linux/usb/func_utils.h>
#include <linux/usb/gadget.h>
#include <linux/usb/audio.h>
#include <linux/usb/midi-v2.h>
-#include "u_f.h"
#include "u_midi2.h"
struct f_midi2;
@@ -150,6 +150,9 @@ struct f_midi2 {
#define func_to_midi2(f) container_of(f, struct f_midi2, func)
+/* convert from MIDI protocol number (1 or 2) to SNDRV_UMP_EP_INFO_PROTO_* */
+#define to_ump_protocol(v) (((v) & 3) << 8)
+
/* get EP name string */
static const char *ump_ep_name(const struct f_midi2_ep *ep)
{
@@ -564,8 +567,7 @@ static void reply_ump_stream_ep_config(struct f_midi2_ep *ep)
.status = UMP_STREAM_MSG_STATUS_STREAM_CFG,
};
- if ((ep->info.protocol & SNDRV_UMP_EP_INFO_PROTO_MIDI_MASK) ==
- SNDRV_UMP_EP_INFO_PROTO_MIDI2)
+ if (ep->info.protocol == 2)
rep.protocol = UMP_STREAM_MSG_EP_INFO_CAP_MIDI2 >> 8;
else
rep.protocol = UMP_STREAM_MSG_EP_INFO_CAP_MIDI1 >> 8;
@@ -627,25 +629,34 @@ static void process_ump_stream_msg(struct f_midi2_ep *ep, const u32 *data)
return;
case UMP_STREAM_MSG_STATUS_STREAM_CFG_REQUEST:
if (*data & UMP_STREAM_MSG_EP_INFO_CAP_MIDI2) {
- ep->info.protocol = SNDRV_UMP_EP_INFO_PROTO_MIDI2;
+ ep->info.protocol = 2;
DBG(midi2, "Switching Protocol to MIDI2\n");
} else {
- ep->info.protocol = SNDRV_UMP_EP_INFO_PROTO_MIDI1;
+ ep->info.protocol = 1;
DBG(midi2, "Switching Protocol to MIDI1\n");
}
- snd_ump_switch_protocol(ep->ump, ep->info.protocol);
+ snd_ump_switch_protocol(ep->ump, to_ump_protocol(ep->info.protocol));
reply_ump_stream_ep_config(ep);
return;
case UMP_STREAM_MSG_STATUS_FB_DISCOVERY:
if (format)
return; // invalid
blk = (*data >> 8) & 0xff;
- if (blk >= ep->num_blks)
- return;
- if (*data & UMP_STREAM_MSG_REQUEST_FB_INFO)
- reply_ump_stream_fb_info(ep, blk);
- if (*data & UMP_STREAM_MSG_REQUEST_FB_NAME)
- reply_ump_stream_fb_name(ep, blk);
+ if (blk == 0xff) {
+ /* inquiry for all blocks */
+ for (blk = 0; blk < ep->num_blks; blk++) {
+ if (*data & UMP_STREAM_MSG_REQUEST_FB_INFO)
+ reply_ump_stream_fb_info(ep, blk);
+ if (*data & UMP_STREAM_MSG_REQUEST_FB_NAME)
+ reply_ump_stream_fb_name(ep, blk);
+ }
+ } else if (blk < ep->num_blks) {
+ /* only the specified block */
+ if (*data & UMP_STREAM_MSG_REQUEST_FB_INFO)
+ reply_ump_stream_fb_info(ep, blk);
+ if (*data & UMP_STREAM_MSG_REQUEST_FB_NAME)
+ reply_ump_stream_fb_name(ep, blk);
+ }
return;
}
}
@@ -1065,7 +1076,8 @@ static void f_midi2_midi1_ep_out_complete(struct usb_ep *usb_ep,
group = midi2->out_cable_mapping[cable].group;
bytes = midi1_packet_bytes[*buf & 0x0f];
for (c = 0; c < bytes; c++) {
- snd_ump_convert_to_ump(cvt, group, ep->info.protocol,
+ snd_ump_convert_to_ump(cvt, group,
+ to_ump_protocol(ep->info.protocol),
buf[c + 1]);
if (cvt->ump_bytes) {
snd_ump_receive(ep->ump, cvt->ump,
@@ -1375,7 +1387,7 @@ static void assign_block_descriptors(struct f_midi2 *midi2,
desc->nNumGroupTrm = b->num_groups;
desc->iBlockItem = ep->blks[blk].string_id;
- if (ep->info.protocol & SNDRV_UMP_EP_INFO_PROTO_MIDI2)
+ if (ep->info.protocol == 2)
desc->bMIDIProtocol = USB_MS_MIDI_PROTO_2_0;
else
desc->bMIDIProtocol = USB_MS_MIDI_PROTO_1_0_128;
@@ -1552,7 +1564,7 @@ static int f_midi2_create_card(struct f_midi2 *midi2)
if (midi2->info.static_block)
ump->info.flags |= SNDRV_UMP_EP_INFO_STATIC_BLOCKS;
ump->info.protocol_caps = (ep->info.protocol_caps & 3) << 8;
- ump->info.protocol = (ep->info.protocol & 3) << 8;
+ ump->info.protocol = to_ump_protocol(ep->info.protocol);
ump->info.version = 0x0101;
ump->info.family_id = ep->info.family;
ump->info.model_id = ep->info.model;
@@ -2868,4 +2880,5 @@ static struct usb_function *f_midi2_alloc(struct usb_function_instance *fi)
DECLARE_USB_FUNCTION_INIT(midi2, f_midi2_alloc_inst, f_midi2_alloc);
+MODULE_DESCRIPTION("USB MIDI 2.0 class function driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c
index 0acc32ed9960..8e761249d672 100644
--- a/drivers/usb/gadget/function/f_ncm.c
+++ b/drivers/usb/gadget/function/f_ncm.c
@@ -1797,5 +1797,6 @@ static struct usb_function *ncm_alloc(struct usb_function_instance *fi)
}
DECLARE_USB_FUNCTION_INIT(ncm, ncm_alloc_inst, ncm_alloc);
+MODULE_DESCRIPTION("USB CDC Network (NCM) link function driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Yauheni Kaliuta");
diff --git a/drivers/usb/gadget/function/f_obex.c b/drivers/usb/gadget/function/f_obex.c
index dcb093210305..1305e2326cdf 100644
--- a/drivers/usb/gadget/function/f_obex.c
+++ b/drivers/usb/gadget/function/f_obex.c
@@ -487,4 +487,5 @@ static struct usb_function *obex_alloc(struct usb_function_instance *fi)
DECLARE_USB_FUNCTION_INIT(obex, obex_alloc_inst, obex_alloc);
MODULE_AUTHOR("Felipe Balbi");
+MODULE_DESCRIPTION("USB CDC OBEX function driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/function/f_phonet.c b/drivers/usb/gadget/function/f_phonet.c
index 0bebbdf3f213..0aa9e8224cae 100644
--- a/drivers/usb/gadget/function/f_phonet.c
+++ b/drivers/usb/gadget/function/f_phonet.c
@@ -729,4 +729,5 @@ void gphonet_cleanup(struct net_device *dev)
DECLARE_USB_FUNCTION_INIT(phonet, phonet_alloc_inst, phonet_alloc);
MODULE_AUTHOR("Rémi Denis-Courmont");
+MODULE_DESCRIPTION("USB CDC Phonet function");
MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/function/f_printer.c b/drivers/usb/gadget/function/f_printer.c
index 076dd4c1be96..d295ade8fa67 100644
--- a/drivers/usb/gadget/function/f_printer.c
+++ b/drivers/usb/gadget/function/f_printer.c
@@ -37,7 +37,7 @@
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/uaccess.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <linux/usb/ch9.h>
#include <linux/usb/composite.h>
@@ -213,6 +213,7 @@ static inline struct usb_endpoint_descriptor *ep_desc(struct usb_gadget *gadget,
struct usb_endpoint_descriptor *ss)
{
switch (gadget->speed) {
+ case USB_SPEED_SUPER_PLUS:
case USB_SPEED_SUPER:
return ss;
case USB_SPEED_HIGH:
@@ -449,11 +450,8 @@ printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr)
mutex_lock(&dev->lock_printer_io);
spin_lock_irqsave(&dev->lock, flags);
- if (dev->interface < 0) {
- spin_unlock_irqrestore(&dev->lock, flags);
- mutex_unlock(&dev->lock_printer_io);
- return -ENODEV;
- }
+ if (dev->interface < 0)
+ goto out_disabled;
/* We will use this flag later to check if a printer reset happened
* after we turn interrupts back on.
@@ -461,6 +459,9 @@ printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr)
dev->reset_printer = 0;
setup_rx_reqs(dev);
+ /* this dropped the lock - need to retest */
+ if (dev->interface < 0)
+ goto out_disabled;
bytes_copied = 0;
current_rx_req = dev->current_rx_req;
@@ -494,6 +495,8 @@ printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr)
wait_event_interruptible(dev->rx_wait,
(likely(!list_empty(&dev->rx_buffers))));
spin_lock_irqsave(&dev->lock, flags);
+ if (dev->interface < 0)
+ goto out_disabled;
}
/* We have data to return then copy it to the caller's buffer.*/
@@ -537,6 +540,9 @@ printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr)
return -EAGAIN;
}
+ if (dev->interface < 0)
+ goto out_disabled;
+
/* If we not returning all the data left in this RX request
* buffer then adjust the amount of data left in the buffer.
* Othewise if we are done with this RX request buffer then
@@ -566,6 +572,11 @@ printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr)
return bytes_copied;
else
return -EAGAIN;
+
+out_disabled:
+ spin_unlock_irqrestore(&dev->lock, flags);
+ mutex_unlock(&dev->lock_printer_io);
+ return -ENODEV;
}
static ssize_t
@@ -586,11 +597,8 @@ printer_write(struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
mutex_lock(&dev->lock_printer_io);
spin_lock_irqsave(&dev->lock, flags);
- if (dev->interface < 0) {
- spin_unlock_irqrestore(&dev->lock, flags);
- mutex_unlock(&dev->lock_printer_io);
- return -ENODEV;
- }
+ if (dev->interface < 0)
+ goto out_disabled;
/* Check if a printer reset happens while we have interrupts on */
dev->reset_printer = 0;
@@ -613,6 +621,8 @@ printer_write(struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
wait_event_interruptible(dev->tx_wait,
(likely(!list_empty(&dev->tx_reqs))));
spin_lock_irqsave(&dev->lock, flags);
+ if (dev->interface < 0)
+ goto out_disabled;
}
while (likely(!list_empty(&dev->tx_reqs)) && len) {
@@ -662,6 +672,9 @@ printer_write(struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
return -EAGAIN;
}
+ if (dev->interface < 0)
+ goto out_disabled;
+
list_add(&req->list, &dev->tx_reqs_active);
/* here, we unlock, and only unlock, to avoid deadlock. */
@@ -674,6 +687,8 @@ printer_write(struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
mutex_unlock(&dev->lock_printer_io);
return -EAGAIN;
}
+ if (dev->interface < 0)
+ goto out_disabled;
}
spin_unlock_irqrestore(&dev->lock, flags);
@@ -685,6 +700,11 @@ printer_write(struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
return bytes_copied;
else
return -EAGAIN;
+
+out_disabled:
+ spin_unlock_irqrestore(&dev->lock, flags);
+ mutex_unlock(&dev->lock_printer_io);
+ return -ENODEV;
}
static int
@@ -1312,9 +1332,9 @@ static inline int gprinter_get_minor(void)
{
int ret;
- ret = ida_simple_get(&printer_ida, 0, 0, GFP_KERNEL);
+ ret = ida_alloc(&printer_ida, GFP_KERNEL);
if (ret >= PRINTER_MINORS) {
- ida_simple_remove(&printer_ida, ret);
+ ida_free(&printer_ida, ret);
ret = -ENODEV;
}
@@ -1323,7 +1343,7 @@ static inline int gprinter_get_minor(void)
static inline void gprinter_put_minor(int minor)
{
- ida_simple_remove(&printer_ida, minor);
+ ida_free(&printer_ida, minor);
}
static int gprinter_setup(int);
@@ -1507,6 +1527,7 @@ static struct usb_function *gprinter_alloc(struct usb_function_instance *fi)
}
DECLARE_USB_FUNCTION_INIT(printer, gprinter_alloc_inst, gprinter_alloc);
+MODULE_DESCRIPTION("USB printer function driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Craig Nadler");
diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c
index b47f99d17ee9..7cec19d65fb5 100644
--- a/drivers/usb/gadget/function/f_rndis.c
+++ b/drivers/usb/gadget/function/f_rndis.c
@@ -1013,5 +1013,6 @@ static struct usb_function *rndis_alloc(struct usb_function_instance *fi)
}
DECLARE_USB_FUNCTION_INIT(rndis, rndis_alloc_inst, rndis_alloc);
+MODULE_DESCRIPTION("RNDIS link function driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("David Brownell");
diff --git a/drivers/usb/gadget/function/f_serial.c b/drivers/usb/gadget/function/f_serial.c
index 65c50092aea2..8f7e7a2b2ff2 100644
--- a/drivers/usb/gadget/function/f_serial.c
+++ b/drivers/usb/gadget/function/f_serial.c
@@ -392,6 +392,7 @@ static struct usb_function *gser_alloc(struct usb_function_instance *fi)
}
DECLARE_USB_FUNCTION_INIT(gser, gser_alloc_inst, gser_alloc);
+MODULE_DESCRIPTION("generic USB serial function driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Al Borchers");
MODULE_AUTHOR("David Brownell");
diff --git a/drivers/usb/gadget/function/f_sourcesink.c b/drivers/usb/gadget/function/f_sourcesink.c
index 2edbd9b510d6..ec5fd25020fd 100644
--- a/drivers/usb/gadget/function/f_sourcesink.c
+++ b/drivers/usb/gadget/function/f_sourcesink.c
@@ -13,10 +13,10 @@
#include <linux/device.h>
#include <linux/module.h>
#include <linux/usb/composite.h>
+#include <linux/usb/func_utils.h>
#include <linux/err.h>
#include "g_zero.h"
-#include "u_f.h"
/*
* SOURCE/SINK FUNCTION ... a primary testing vehicle for USB peripheral
@@ -1284,4 +1284,5 @@ static void __exit sslb_modexit(void)
module_init(sslb_modinit);
module_exit(sslb_modexit);
+MODULE_DESCRIPTION("USB peripheral source/sink configuration driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/function/f_subset.c b/drivers/usb/gadget/function/f_subset.c
index 8ae9689ef2a0..ea3fdd842462 100644
--- a/drivers/usb/gadget/function/f_subset.c
+++ b/drivers/usb/gadget/function/f_subset.c
@@ -500,5 +500,6 @@ static struct usb_function *geth_alloc(struct usb_function_instance *fi)
}
DECLARE_USB_FUNCTION_INIT(geth, geth_alloc_inst, geth_alloc);
+MODULE_DESCRIPTION("\"CDC Subset\" Ethernet link function driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("David Brownell");
diff --git a/drivers/usb/gadget/function/f_tcm.c b/drivers/usb/gadget/function/f_tcm.c
index 37befd6db001..15bb3aa12aa8 100644
--- a/drivers/usb/gadget/function/f_tcm.c
+++ b/drivers/usb/gadget/function/f_tcm.c
@@ -19,7 +19,7 @@
#include <scsi/scsi_tcq.h>
#include <target/target_core_base.h>
#include <target/target_core_fabric.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include "tcm.h"
#include "u_tcm.h"
@@ -2301,5 +2301,6 @@ static void __exit tcm_exit(void)
}
module_exit(tcm_exit);
+MODULE_DESCRIPTION("Target based USB-Gadget");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Sebastian Andrzej Siewior");
diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c
index 7de74a3dd392..c87e74afc881 100644
--- a/drivers/usb/gadget/function/f_uac1.c
+++ b/drivers/usb/gadget/function/f_uac1.c
@@ -377,24 +377,10 @@ enum {
STR_AS_OUT_IF_ALT1,
STR_AS_IN_IF_ALT0,
STR_AS_IN_IF_ALT1,
+ NUM_STR_DESCRIPTORS,
};
-static struct usb_string strings_uac1[] = {
- /* [STR_AC_IF].s = DYNAMIC, */
- [STR_USB_OUT_IT].s = "Playback Input terminal",
- [STR_USB_OUT_IT_CH_NAMES].s = "Playback Channels",
- [STR_IO_OUT_OT].s = "Playback Output terminal",
- [STR_IO_IN_IT].s = "Capture Input terminal",
- [STR_IO_IN_IT_CH_NAMES].s = "Capture Channels",
- [STR_USB_IN_OT].s = "Capture Output terminal",
- [STR_FU_IN].s = "Capture Volume",
- [STR_FU_OUT].s = "Playback Volume",
- [STR_AS_OUT_IF_ALT0].s = "Playback Inactive",
- [STR_AS_OUT_IF_ALT1].s = "Playback Active",
- [STR_AS_IN_IF_ALT0].s = "Capture Inactive",
- [STR_AS_IN_IF_ALT1].s = "Capture Active",
- { },
-};
+static struct usb_string strings_uac1[NUM_STR_DESCRIPTORS + 1] = {};
static struct usb_gadget_strings str_uac1 = {
.language = 0x0409, /* en-us */
@@ -1265,6 +1251,20 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
strings_uac1[STR_AC_IF].s = audio_opts->function_name;
+ strings_uac1[STR_USB_OUT_IT].s = audio_opts->c_it_name;
+ strings_uac1[STR_USB_OUT_IT_CH_NAMES].s = audio_opts->c_it_ch_name;
+ strings_uac1[STR_IO_OUT_OT].s = audio_opts->c_ot_name;
+ strings_uac1[STR_FU_OUT].s = audio_opts->c_fu_vol_name;
+ strings_uac1[STR_AS_OUT_IF_ALT0].s = "Playback Inactive";
+ strings_uac1[STR_AS_OUT_IF_ALT1].s = "Playback Active";
+
+ strings_uac1[STR_IO_IN_IT].s = audio_opts->p_it_name;
+ strings_uac1[STR_IO_IN_IT_CH_NAMES].s = audio_opts->p_it_ch_name;
+ strings_uac1[STR_USB_IN_OT].s = audio_opts->p_ot_name;
+ strings_uac1[STR_FU_IN].s = audio_opts->p_fu_vol_name;
+ strings_uac1[STR_AS_IN_IF_ALT0].s = "Capture Inactive";
+ strings_uac1[STR_AS_IN_IF_ALT1].s = "Capture Active";
+
us = usb_gstrings_attach(cdev, uac1_strings, ARRAY_SIZE(strings_uac1));
if (IS_ERR(us))
return PTR_ERR(us);
@@ -1681,8 +1681,19 @@ UAC1_ATTRIBUTE(bool, c_volume_present);
UAC1_ATTRIBUTE(s16, c_volume_min);
UAC1_ATTRIBUTE(s16, c_volume_max);
UAC1_ATTRIBUTE(s16, c_volume_res);
+
UAC1_ATTRIBUTE_STRING(function_name);
+UAC1_ATTRIBUTE_STRING(p_it_name);
+UAC1_ATTRIBUTE_STRING(p_it_ch_name);
+UAC1_ATTRIBUTE_STRING(p_ot_name);
+UAC1_ATTRIBUTE_STRING(p_fu_vol_name);
+
+UAC1_ATTRIBUTE_STRING(c_it_name);
+UAC1_ATTRIBUTE_STRING(c_it_ch_name);
+UAC1_ATTRIBUTE_STRING(c_ot_name);
+UAC1_ATTRIBUTE_STRING(c_fu_vol_name);
+
static struct configfs_attribute *f_uac1_attrs[] = {
&f_uac1_opts_attr_c_chmask,
&f_uac1_opts_attr_c_srate,
@@ -1706,6 +1717,16 @@ static struct configfs_attribute *f_uac1_attrs[] = {
&f_uac1_opts_attr_function_name,
+ &f_uac1_opts_attr_p_it_name,
+ &f_uac1_opts_attr_p_it_ch_name,
+ &f_uac1_opts_attr_p_ot_name,
+ &f_uac1_opts_attr_p_fu_vol_name,
+
+ &f_uac1_opts_attr_c_it_name,
+ &f_uac1_opts_attr_c_it_ch_name,
+ &f_uac1_opts_attr_c_ot_name,
+ &f_uac1_opts_attr_c_fu_vol_name,
+
NULL,
};
@@ -1760,6 +1781,16 @@ static struct usb_function_instance *f_audio_alloc_inst(void)
scnprintf(opts->function_name, sizeof(opts->function_name), "AC Interface");
+ scnprintf(opts->p_it_name, sizeof(opts->p_it_name), "Capture Input terminal");
+ scnprintf(opts->p_it_ch_name, sizeof(opts->p_it_ch_name), "Capture Channels");
+ scnprintf(opts->p_ot_name, sizeof(opts->p_ot_name), "Capture Output terminal");
+ scnprintf(opts->p_fu_vol_name, sizeof(opts->p_fu_vol_name), "Capture Volume");
+
+ scnprintf(opts->c_it_name, sizeof(opts->c_it_name), "Playback Input terminal");
+ scnprintf(opts->c_it_ch_name, sizeof(opts->c_it_ch_name), "Playback Channels");
+ scnprintf(opts->c_ot_name, sizeof(opts->c_ot_name), "Playback Output terminal");
+ scnprintf(opts->c_fu_vol_name, sizeof(opts->c_fu_vol_name), "Playback Volume");
+
return &opts->func_inst;
}
@@ -1823,5 +1854,6 @@ static struct usb_function *f_audio_alloc(struct usb_function_instance *fi)
}
DECLARE_USB_FUNCTION_INIT(uac1, f_audio_alloc_inst, f_audio_alloc);
+MODULE_DESCRIPTION("USB Audio Class 1.0 Function (using u_audio API)");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Ruslan Bilovol");
diff --git a/drivers/usb/gadget/function/f_uac1_legacy.c b/drivers/usb/gadget/function/f_uac1_legacy.c
index e2d7f69128a0..49cf5aae90ca 100644
--- a/drivers/usb/gadget/function/f_uac1_legacy.c
+++ b/drivers/usb/gadget/function/f_uac1_legacy.c
@@ -1014,5 +1014,6 @@ static struct usb_function *f_audio_alloc(struct usb_function_instance *fi)
}
DECLARE_USB_FUNCTION_INIT(uac1_legacy, f_audio_alloc_inst, f_audio_alloc);
+MODULE_DESCRIPTION("USB Audio class function driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Bryan Wu");
diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
index 383f6854cfec..1cdda44455b3 100644
--- a/drivers/usb/gadget/function/f_uac2.c
+++ b/drivers/usb/gadget/function/f_uac2.c
@@ -95,7 +95,9 @@ enum {
STR_CLKSRC_IN,
STR_CLKSRC_OUT,
STR_USB_IT,
+ STR_USB_IT_CH,
STR_IO_IT,
+ STR_IO_IT_CH,
STR_USB_OT,
STR_IO_OT,
STR_FU_IN,
@@ -104,25 +106,10 @@ enum {
STR_AS_OUT_ALT1,
STR_AS_IN_ALT0,
STR_AS_IN_ALT1,
+ NUM_STR_DESCRIPTORS,
};
-static struct usb_string strings_fn[] = {
- /* [STR_ASSOC].s = DYNAMIC, */
- [STR_IF_CTRL].s = "Topology Control",
- [STR_CLKSRC_IN].s = "Input Clock",
- [STR_CLKSRC_OUT].s = "Output Clock",
- [STR_USB_IT].s = "USBH Out",
- [STR_IO_IT].s = "USBD Out",
- [STR_USB_OT].s = "USBH In",
- [STR_IO_OT].s = "USBD In",
- [STR_FU_IN].s = "Capture Volume",
- [STR_FU_OUT].s = "Playback Volume",
- [STR_AS_OUT_ALT0].s = "Playback Inactive",
- [STR_AS_OUT_ALT1].s = "Playback Active",
- [STR_AS_IN_ALT0].s = "Capture Inactive",
- [STR_AS_IN_ALT1].s = "Capture Active",
- { },
-};
+static struct usb_string strings_fn[NUM_STR_DESCRIPTORS + 1] = {};
static const char *const speed_names[] = {
[USB_SPEED_UNKNOWN] = "UNKNOWN",
@@ -1049,6 +1036,23 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
return ret;
strings_fn[STR_ASSOC].s = uac2_opts->function_name;
+ strings_fn[STR_IF_CTRL].s = uac2_opts->if_ctrl_name;
+ strings_fn[STR_CLKSRC_IN].s = uac2_opts->clksrc_in_name;
+ strings_fn[STR_CLKSRC_OUT].s = uac2_opts->clksrc_out_name;
+
+ strings_fn[STR_USB_IT].s = uac2_opts->c_it_name;
+ strings_fn[STR_USB_IT_CH].s = uac2_opts->c_it_ch_name;
+ strings_fn[STR_IO_OT].s = uac2_opts->c_ot_name;
+ strings_fn[STR_FU_OUT].s = uac2_opts->c_fu_vol_name;
+ strings_fn[STR_AS_OUT_ALT0].s = "Playback Inactive";
+ strings_fn[STR_AS_OUT_ALT1].s = "Playback Active";
+
+ strings_fn[STR_IO_IT].s = uac2_opts->p_it_name;
+ strings_fn[STR_IO_IT_CH].s = uac2_opts->p_it_ch_name;
+ strings_fn[STR_USB_OT].s = uac2_opts->p_ot_name;
+ strings_fn[STR_FU_IN].s = uac2_opts->p_fu_vol_name;
+ strings_fn[STR_AS_IN_ALT0].s = "Capture Inactive";
+ strings_fn[STR_AS_IN_ALT1].s = "Capture Active";
us = usb_gstrings_attach(cdev, fn_strings, ARRAY_SIZE(strings_fn));
if (IS_ERR(us))
@@ -1072,7 +1076,9 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
in_clk_src_desc.iClockSource = us[STR_CLKSRC_IN].id;
out_clk_src_desc.iClockSource = us[STR_CLKSRC_OUT].id;
usb_out_it_desc.iTerminal = us[STR_USB_IT].id;
+ usb_out_it_desc.iChannelNames = us[STR_USB_IT_CH].id;
io_in_it_desc.iTerminal = us[STR_IO_IT].id;
+ io_in_it_desc.iChannelNames = us[STR_IO_IT_CH].id;
usb_in_ot_desc.iTerminal = us[STR_USB_OT].id;
io_out_ot_desc.iTerminal = us[STR_IO_OT].id;
std_as_out_if0_desc.iInterface = us[STR_AS_OUT_ALT0].id;
@@ -2063,7 +2069,10 @@ static ssize_t f_uac2_opts_##name##_store(struct config_item *item, \
goto end; \
} \
\
- ret = scnprintf(opts->name, min(sizeof(opts->name), len), \
+ if (len && page[len - 1] == '\n') \
+ len--; \
+ \
+ ret = scnprintf(opts->name, min(sizeof(opts->name), len + 1), \
"%s", page); \
\
end: \
@@ -2097,10 +2106,24 @@ UAC2_ATTRIBUTE(s16, c_volume_max);
UAC2_ATTRIBUTE(s16, c_volume_res);
UAC2_ATTRIBUTE(u32, fb_max);
UAC2_ATTRIBUTE_STRING(function_name);
+UAC2_ATTRIBUTE_STRING(if_ctrl_name);
+UAC2_ATTRIBUTE_STRING(clksrc_in_name);
+UAC2_ATTRIBUTE_STRING(clksrc_out_name);
+
+UAC2_ATTRIBUTE_STRING(p_it_name);
+UAC2_ATTRIBUTE_STRING(p_it_ch_name);
+UAC2_ATTRIBUTE_STRING(p_ot_name);
+UAC2_ATTRIBUTE_STRING(p_fu_vol_name);
+
+UAC2_ATTRIBUTE_STRING(c_it_name);
+UAC2_ATTRIBUTE_STRING(c_it_ch_name);
+UAC2_ATTRIBUTE_STRING(c_ot_name);
+UAC2_ATTRIBUTE_STRING(c_fu_vol_name);
UAC2_ATTRIBUTE(s16, p_terminal_type);
UAC2_ATTRIBUTE(s16, c_terminal_type);
+
static struct configfs_attribute *f_uac2_attrs[] = {
&f_uac2_opts_attr_p_chmask,
&f_uac2_opts_attr_p_srate,
@@ -2127,6 +2150,19 @@ static struct configfs_attribute *f_uac2_attrs[] = {
&f_uac2_opts_attr_c_volume_res,
&f_uac2_opts_attr_function_name,
+ &f_uac2_opts_attr_if_ctrl_name,
+ &f_uac2_opts_attr_clksrc_in_name,
+ &f_uac2_opts_attr_clksrc_out_name,
+
+ &f_uac2_opts_attr_p_it_name,
+ &f_uac2_opts_attr_p_it_ch_name,
+ &f_uac2_opts_attr_p_ot_name,
+ &f_uac2_opts_attr_p_fu_vol_name,
+
+ &f_uac2_opts_attr_c_it_name,
+ &f_uac2_opts_attr_c_it_ch_name,
+ &f_uac2_opts_attr_c_ot_name,
+ &f_uac2_opts_attr_c_fu_vol_name,
&f_uac2_opts_attr_p_terminal_type,
&f_uac2_opts_attr_c_terminal_type,
@@ -2188,6 +2224,19 @@ static struct usb_function_instance *afunc_alloc_inst(void)
opts->fb_max = FBACK_FAST_MAX;
scnprintf(opts->function_name, sizeof(opts->function_name), "Source/Sink");
+ scnprintf(opts->if_ctrl_name, sizeof(opts->if_ctrl_name), "Topology Control");
+ scnprintf(opts->clksrc_in_name, sizeof(opts->clksrc_in_name), "Input Clock");
+ scnprintf(opts->clksrc_out_name, sizeof(opts->clksrc_out_name), "Output Clock");
+
+ scnprintf(opts->p_it_name, sizeof(opts->p_it_name), "USBD Out");
+ scnprintf(opts->p_it_ch_name, sizeof(opts->p_it_ch_name), "Capture Channels");
+ scnprintf(opts->p_ot_name, sizeof(opts->p_ot_name), "USBH In");
+ scnprintf(opts->p_fu_vol_name, sizeof(opts->p_fu_vol_name), "Capture Volume");
+
+ scnprintf(opts->c_it_name, sizeof(opts->c_it_name), "USBH Out");
+ scnprintf(opts->c_it_ch_name, sizeof(opts->c_it_ch_name), "Playback Channels");
+ scnprintf(opts->c_ot_name, sizeof(opts->c_ot_name), "USBD In");
+ scnprintf(opts->c_fu_vol_name, sizeof(opts->c_fu_vol_name), "Playback Volume");
opts->p_terminal_type = UAC2_DEF_P_TERM_TYPE;
opts->c_terminal_type = UAC2_DEF_C_TERM_TYPE;
@@ -2251,6 +2300,7 @@ static struct usb_function *afunc_alloc(struct usb_function_instance *fi)
}
DECLARE_USB_FUNCTION_INIT(uac2, afunc_alloc_inst, afunc_alloc);
+MODULE_DESCRIPTION("USB Audio Class 2.0 Function");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Yadwinder Singh");
MODULE_AUTHOR("Jaswinder Singh");
diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c
index 929666805bd2..40187b7112e7 100644
--- a/drivers/usb/gadget/function/f_uvc.c
+++ b/drivers/usb/gadget/function/f_uvc.c
@@ -1118,5 +1118,6 @@ err_config:
}
DECLARE_USB_FUNCTION_INIT(uvc, uvc_alloc_inst, uvc_alloc);
+MODULE_DESCRIPTION("USB Video Class Gadget driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Laurent Pinchart");
diff --git a/drivers/usb/gadget/function/rndis.c b/drivers/usb/gadget/function/rndis.c
index 29bf8664bf58..afd75d72412c 100644
--- a/drivers/usb/gadget/function/rndis.c
+++ b/drivers/usb/gadget/function/rndis.c
@@ -31,7 +31,7 @@
#include <asm/io.h>
#include <asm/byteorder.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include "u_rndis.h"
@@ -869,12 +869,12 @@ EXPORT_SYMBOL_GPL(rndis_msg_parser);
static inline int rndis_get_nr(void)
{
- return ida_simple_get(&rndis_ida, 0, 1000, GFP_KERNEL);
+ return ida_alloc_max(&rndis_ida, 999, GFP_KERNEL);
}
static inline void rndis_put_nr(int nr)
{
- ida_simple_remove(&rndis_ida, nr);
+ ida_free(&rndis_ida, nr);
}
struct rndis_params *rndis_register(void (*resp_avail)(void *v), void *v)
diff --git a/drivers/usb/gadget/function/storage_common.c b/drivers/usb/gadget/function/storage_common.c
index 2a4163b0f6fe..75831f2c7abe 100644
--- a/drivers/usb/gadget/function/storage_common.c
+++ b/drivers/usb/gadget/function/storage_common.c
@@ -537,4 +537,5 @@ ssize_t fsg_store_forced_eject(struct fsg_lun *curlun, struct rw_semaphore *file
}
EXPORT_SYMBOL_GPL(fsg_store_forced_eject);
+MODULE_DESCRIPTION("Common definitions for mass storage functionality");
MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/function/storage_common.h b/drivers/usb/gadget/function/storage_common.h
index 0a544a82cbf8..ced5d2b09234 100644
--- a/drivers/usb/gadget/function/storage_common.h
+++ b/drivers/usb/gadget/function/storage_common.h
@@ -5,7 +5,7 @@
#include <linux/device.h>
#include <linux/usb/storage.h>
#include <scsi/scsi.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#ifndef DEBUG
#undef VERBOSE_DEBUG
diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c
index 4a42574b4a7f..ca8dbec65f73 100644
--- a/drivers/usb/gadget/function/u_audio.c
+++ b/drivers/usb/gadget/function/u_audio.c
@@ -57,13 +57,13 @@ struct uac_rtd_params {
/* Volume/Mute controls and their state */
int fu_id; /* Feature Unit ID */
- struct snd_kcontrol *snd_kctl_volume;
- struct snd_kcontrol *snd_kctl_mute;
+ struct snd_ctl_elem_id snd_kctl_volume_id;
+ struct snd_ctl_elem_id snd_kctl_mute_id;
s16 volume_min, volume_max, volume_res;
s16 volume;
int mute;
- struct snd_kcontrol *snd_kctl_rate; /* read-only current rate */
+ struct snd_ctl_elem_id snd_kctl_rate_id; /* read-only current rate */
int srate; /* selected samplerate */
int active; /* playback/capture running */
@@ -494,14 +494,13 @@ static inline void free_ep_fback(struct uac_rtd_params *prm, struct usb_ep *ep)
static void set_active(struct uac_rtd_params *prm, bool active)
{
// notifying through the Rate ctrl
- struct snd_kcontrol *kctl = prm->snd_kctl_rate;
unsigned long flags;
spin_lock_irqsave(&prm->lock, flags);
if (prm->active != active) {
prm->active = active;
snd_ctl_notify(prm->uac->card, SNDRV_CTL_EVENT_MASK_VALUE,
- &kctl->id);
+ &prm->snd_kctl_rate_id);
}
spin_unlock_irqrestore(&prm->lock, flags);
}
@@ -593,16 +592,25 @@ int u_audio_start_capture(struct g_audio *audio_dev)
struct usb_ep *ep, *ep_fback;
struct uac_rtd_params *prm;
struct uac_params *params = &audio_dev->params;
- int req_len, i;
+ int req_len, i, ret;
prm = &uac->c_prm;
dev_dbg(dev, "start capture with rate %d\n", prm->srate);
ep = audio_dev->out_ep;
- config_ep_by_speed(gadget, &audio_dev->func, ep);
+ ret = config_ep_by_speed(gadget, &audio_dev->func, ep);
+ if (ret < 0) {
+ dev_err(dev, "config_ep_by_speed for out_ep failed (%d)\n", ret);
+ return ret;
+ }
+
req_len = ep->maxpacket;
prm->ep_enabled = true;
- usb_ep_enable(ep);
+ ret = usb_ep_enable(ep);
+ if (ret < 0) {
+ dev_err(dev, "usb_ep_enable failed for out_ep (%d)\n", ret);
+ return ret;
+ }
for (i = 0; i < params->req_number; i++) {
if (!prm->reqs[i]) {
@@ -630,9 +638,18 @@ int u_audio_start_capture(struct g_audio *audio_dev)
return 0;
/* Setup feedback endpoint */
- config_ep_by_speed(gadget, &audio_dev->func, ep_fback);
+ ret = config_ep_by_speed(gadget, &audio_dev->func, ep_fback);
+ if (ret < 0) {
+ dev_err(dev, "config_ep_by_speed in_ep_fback failed (%d)\n", ret);
+ return ret; // TODO: Clean up out_ep
+ }
+
prm->fb_ep_enabled = true;
- usb_ep_enable(ep_fback);
+ ret = usb_ep_enable(ep_fback);
+ if (ret < 0) {
+ dev_err(dev, "usb_ep_enable failed for in_ep_fback (%d)\n", ret);
+ return ret; // TODO: Clean up out_ep
+ }
req_len = ep_fback->maxpacket;
req_fback = usb_ep_alloc_request(ep_fback, GFP_ATOMIC);
@@ -688,13 +705,17 @@ int u_audio_start_playback(struct g_audio *audio_dev)
struct uac_params *params = &audio_dev->params;
unsigned int factor;
const struct usb_endpoint_descriptor *ep_desc;
- int req_len, i;
+ int req_len, i, ret;
unsigned int p_pktsize;
prm = &uac->p_prm;
dev_dbg(dev, "start playback with rate %d\n", prm->srate);
ep = audio_dev->in_ep;
- config_ep_by_speed(gadget, &audio_dev->func, ep);
+ ret = config_ep_by_speed(gadget, &audio_dev->func, ep);
+ if (ret < 0) {
+ dev_err(dev, "config_ep_by_speed for in_ep failed (%d)\n", ret);
+ return ret;
+ }
ep_desc = ep->desc;
/*
@@ -721,7 +742,11 @@ int u_audio_start_playback(struct g_audio *audio_dev)
uac->p_residue_mil = 0;
prm->ep_enabled = true;
- usb_ep_enable(ep);
+ ret = usb_ep_enable(ep);
+ if (ret < 0) {
+ dev_err(dev, "usb_ep_enable failed for in_ep (%d)\n", ret);
+ return ret;
+ }
for (i = 0; i < params->req_number; i++) {
if (!prm->reqs[i]) {
@@ -807,7 +832,7 @@ int u_audio_set_volume(struct g_audio *audio_dev, int playback, s16 val)
if (change)
snd_ctl_notify(uac->card, SNDRV_CTL_EVENT_MASK_VALUE,
- &prm->snd_kctl_volume->id);
+ &prm->snd_kctl_volume_id);
return 0;
}
@@ -856,7 +881,7 @@ int u_audio_set_mute(struct g_audio *audio_dev, int playback, int val)
if (change)
snd_ctl_notify(uac->card, SNDRV_CTL_EVENT_MASK_VALUE,
- &prm->snd_kctl_mute->id);
+ &prm->snd_kctl_mute_id);
return 0;
}
@@ -1115,35 +1140,35 @@ static int u_audio_rate_get(struct snd_kcontrol *kcontrol,
}
static struct snd_kcontrol_new u_audio_controls[] = {
- [UAC_FBACK_CTRL] {
+ [UAC_FBACK_CTRL] = {
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "Capture Pitch 1000000",
.info = u_audio_pitch_info,
.get = u_audio_pitch_get,
.put = u_audio_pitch_put,
},
- [UAC_P_PITCH_CTRL] {
+ [UAC_P_PITCH_CTRL] = {
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "Playback Pitch 1000000",
.info = u_audio_pitch_info,
.get = u_audio_pitch_get,
.put = u_audio_pitch_put,
},
- [UAC_MUTE_CTRL] {
+ [UAC_MUTE_CTRL] = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "", /* will be filled later */
.info = u_audio_mute_info,
.get = u_audio_mute_get,
.put = u_audio_mute_put,
},
- [UAC_VOLUME_CTRL] {
+ [UAC_VOLUME_CTRL] = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "", /* will be filled later */
.info = u_audio_volume_info,
.get = u_audio_volume_get,
.put = u_audio_volume_put,
},
- [UAC_RATE_CTRL] {
+ [UAC_RATE_CTRL] = {
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "", /* will be filled later */
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
@@ -1243,7 +1268,7 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
if (err < 0)
goto snd_fail;
- strscpy(pcm->name, pcm_name, sizeof(pcm->name));
+ strscpy(pcm->name, pcm_name);
pcm->private_data = uac;
uac->pcm = pcm;
@@ -1257,7 +1282,7 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
if ((c_chmask && g_audio->in_ep_fback)
|| (p_chmask && params->p_fu.id)
|| (c_chmask && params->c_fu.id))
- strscpy(card->mixername, card_name, sizeof(card->driver));
+ strscpy(card->mixername, card_name);
if (c_chmask && g_audio->in_ep_fback) {
kctl = snd_ctl_new1(&u_audio_controls[UAC_FBACK_CTRL],
@@ -1331,7 +1356,7 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
err = snd_ctl_add(card, kctl);
if (err < 0)
goto snd_fail;
- prm->snd_kctl_mute = kctl;
+ prm->snd_kctl_mute_id = kctl->id;
prm->mute = 0;
}
@@ -1359,7 +1384,7 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
err = snd_ctl_add(card, kctl);
if (err < 0)
goto snd_fail;
- prm->snd_kctl_volume = kctl;
+ prm->snd_kctl_volume_id = kctl->id;
prm->volume = fu->volume_max;
prm->volume_max = fu->volume_max;
prm->volume_min = fu->volume_min;
@@ -1383,12 +1408,13 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
err = snd_ctl_add(card, kctl);
if (err < 0)
goto snd_fail;
- prm->snd_kctl_rate = kctl;
+ prm->snd_kctl_rate_id = kctl->id;
}
- strscpy(card->driver, card_name, sizeof(card->driver));
- strscpy(card->shortname, card_name, sizeof(card->shortname));
- sprintf(card->longname, "%s %i", card_name, card->dev->id);
+ strscpy(card->driver, card_name);
+ strscpy(card->shortname, card_name);
+ snprintf(card->longname, sizeof(card->longname), "%s %i",
+ card_name, card->dev->id);
snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
NULL, 0, BUFF_SIZE_MAX);
@@ -1420,6 +1446,8 @@ void g_audio_cleanup(struct g_audio *g_audio)
return;
uac = g_audio->uac;
+ g_audio->uac = NULL;
+
card = uac->card;
if (card)
snd_card_free_when_closed(card);
diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c
index 444212c0b5a9..09e2838917e2 100644
--- a/drivers/usb/gadget/function/u_ether.c
+++ b/drivers/usb/gadget/function/u_ether.c
@@ -1032,7 +1032,7 @@ int gether_set_ifname(struct net_device *net, const char *name, int len)
if (!p || p[1] != 'd' || strchr(p + 2, '%'))
return -EINVAL;
- strncpy(net->name, tmp, sizeof(net->name));
+ strscpy(net->name, tmp);
dev->ifname_set = true;
return 0;
@@ -1163,8 +1163,6 @@ struct net_device *gether_connect(struct gether *link)
if (netif_running(dev->net))
eth_start(dev, GFP_ATOMIC);
- netif_device_attach(dev->net);
-
/* on error, disable any endpoints */
} else {
(void) usb_ep_disable(link->out_ep);
@@ -1202,7 +1200,7 @@ void gether_disconnect(struct gether *link)
DBG(dev, "%s\n", __func__);
- netif_device_detach(dev->net);
+ netif_stop_queue(dev->net);
netif_carrier_off(dev->net);
/* disable endpoints, forcing (synchronous) completion
@@ -1247,5 +1245,6 @@ void gether_disconnect(struct gether *link)
}
EXPORT_SYMBOL_GPL(gether_disconnect);
+MODULE_DESCRIPTION("Ethernet-over-USB link layer utilities for Gadget stack");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("David Brownell");
diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c
index a92eb6d90976..0a8c05b2746b 100644
--- a/drivers/usb/gadget/function/u_serial.c
+++ b/drivers/usb/gadget/function/u_serial.c
@@ -28,6 +28,7 @@
#include <linux/kthread.h>
#include <linux/workqueue.h>
#include <linux/kfifo.h>
+#include <linux/serial.h>
#include "u_serial.h"
@@ -126,6 +127,7 @@ struct gs_port {
wait_queue_head_t close_wait;
bool suspended; /* port suspended */
bool start_delayed; /* delay start when suspended */
+ struct async_icount icount;
/* REVISIT this state ... */
struct usb_cdc_line_coding port_line_coding; /* 8-N-1 etc */
@@ -257,6 +259,7 @@ __acquires(&port->port_lock)
break;
}
do_tty_wake = true;
+ port->icount.tx += len;
req->length = len;
list_del(&req->list);
@@ -408,6 +411,7 @@ static void gs_rx_push(struct work_struct *work)
size -= n;
}
+ port->icount.rx += size;
count = tty_insert_flip_string(&port->port, packet,
size);
if (count)
@@ -851,6 +855,23 @@ static int gs_break_ctl(struct tty_struct *tty, int duration)
return status;
}
+static int gs_get_icount(struct tty_struct *tty,
+ struct serial_icounter_struct *icount)
+{
+ struct gs_port *port = tty->driver_data;
+ struct async_icount cnow;
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ cnow = port->icount;
+ spin_unlock_irqrestore(&port->port_lock, flags);
+
+ icount->rx = cnow.rx;
+ icount->tx = cnow.tx;
+
+ return 0;
+}
+
static const struct tty_operations gs_tty_ops = {
.open = gs_open,
.close = gs_close,
@@ -861,6 +882,7 @@ static const struct tty_operations gs_tty_ops = {
.chars_in_buffer = gs_chars_in_buffer,
.unthrottle = gs_unthrottle,
.break_ctl = gs_break_ctl,
+ .get_icount = gs_get_icount,
};
/*-------------------------------------------------------------------------*/
@@ -1441,6 +1463,7 @@ void gserial_suspend(struct gserial *gser)
spin_lock(&port->port_lock);
spin_unlock(&serial_port_lock);
port->suspended = true;
+ port->start_delayed = true;
spin_unlock_irqrestore(&port->port_lock, flags);
}
EXPORT_SYMBOL_GPL(gserial_suspend);
@@ -1536,4 +1559,5 @@ static void __exit userial_cleanup(void)
}
module_exit(userial_cleanup);
+MODULE_DESCRIPTION("utilities for USB gadget \"serial port\"/TTY support");
MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/function/u_serial.h b/drivers/usb/gadget/function/u_serial.h
index 901d99310bc4..e1274338ea61 100644
--- a/drivers/usb/gadget/function/u_serial.h
+++ b/drivers/usb/gadget/function/u_serial.h
@@ -17,6 +17,10 @@
struct f_serial_opts {
struct usb_function_instance func_inst;
u8 port_num;
+ u8 protocol;
+
+ struct mutex lock; /* protect instances */
+ int instances;
};
/*
diff --git a/drivers/usb/gadget/function/u_uac1.h b/drivers/usb/gadget/function/u_uac1.h
index f7a616760e31..feb6eb76462f 100644
--- a/drivers/usb/gadget/function/u_uac1.h
+++ b/drivers/usb/gadget/function/u_uac1.h
@@ -52,7 +52,17 @@ struct f_uac1_opts {
int req_number;
unsigned bound:1;
- char function_name[32];
+ char function_name[USB_MAX_STRING_LEN];
+
+ char p_it_name[USB_MAX_STRING_LEN];
+ char p_it_ch_name[USB_MAX_STRING_LEN];
+ char p_ot_name[USB_MAX_STRING_LEN];
+ char p_fu_vol_name[USB_MAX_STRING_LEN];
+
+ char c_it_name[USB_MAX_STRING_LEN];
+ char c_it_ch_name[USB_MAX_STRING_LEN];
+ char c_ot_name[USB_MAX_STRING_LEN];
+ char c_fu_vol_name[USB_MAX_STRING_LEN];
struct mutex lock;
int refcnt;
diff --git a/drivers/usb/gadget/function/u_uac2.h b/drivers/usb/gadget/function/u_uac2.h
index 5e81bdd6c5fb..0df808289ded 100644
--- a/drivers/usb/gadget/function/u_uac2.h
+++ b/drivers/usb/gadget/function/u_uac2.h
@@ -68,7 +68,20 @@ struct f_uac2_opts {
int fb_max;
bool bound;
- char function_name[32];
+ char function_name[USB_MAX_STRING_LEN];
+ char if_ctrl_name[USB_MAX_STRING_LEN];
+ char clksrc_in_name[USB_MAX_STRING_LEN];
+ char clksrc_out_name[USB_MAX_STRING_LEN];
+
+ char p_it_name[USB_MAX_STRING_LEN];
+ char p_it_ch_name[USB_MAX_STRING_LEN];
+ char p_ot_name[USB_MAX_STRING_LEN];
+ char p_fu_vol_name[USB_MAX_STRING_LEN];
+
+ char c_it_name[USB_MAX_STRING_LEN];
+ char c_it_ch_name[USB_MAX_STRING_LEN];
+ char c_ot_name[USB_MAX_STRING_LEN];
+ char c_fu_vol_name[USB_MAX_STRING_LEN];
s16 p_terminal_type;
s16 c_terminal_type;
diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c
index a4377df612f5..6fac696ea846 100644
--- a/drivers/usb/gadget/function/uvc_configfs.c
+++ b/drivers/usb/gadget/function/uvc_configfs.c
@@ -13,6 +13,7 @@
#include "uvc_configfs.h"
#include <linux/sort.h>
+#include <linux/usb/uvc.h>
#include <linux/usb/video.h>
/* -----------------------------------------------------------------------------
@@ -2260,6 +2261,8 @@ static ssize_t uvcg_uncompressed_guid_format_store(struct config_item *item,
struct f_uvc_opts *opts;
struct config_item *opts_item;
struct mutex *su_mutex = &ch->fmt.group.cg_subsys->su_mutex;
+ const struct uvc_format_desc *format;
+ u8 tmpguidFormat[sizeof(ch->desc.guidFormat)];
int ret;
mutex_lock(su_mutex); /* for navigating configfs hierarchy */
@@ -2273,7 +2276,16 @@ static ssize_t uvcg_uncompressed_guid_format_store(struct config_item *item,
goto end;
}
- memcpy(ch->desc.guidFormat, page,
+ memcpy(tmpguidFormat, page,
+ min(sizeof(tmpguidFormat), len));
+
+ format = uvc_format_by_guid(tmpguidFormat);
+ if (!format) {
+ ret = -EINVAL;
+ goto end;
+ }
+
+ memcpy(ch->desc.guidFormat, tmpguidFormat,
min(sizeof(ch->desc.guidFormat), len));
ret = sizeof(ch->desc.guidFormat);
diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c
index c7e5fa4f29e0..de1736f834e6 100644
--- a/drivers/usb/gadget/function/uvc_v4l2.c
+++ b/drivers/usb/gadget/function/uvc_v4l2.c
@@ -121,6 +121,9 @@ static struct uvcg_format *find_format_by_pix(struct uvc_device *uvc,
list_for_each_entry(format, &uvc->header->formats, entry) {
const struct uvc_format_desc *fmtdesc = to_uvc_format(format->fmt);
+ if (IS_ERR(fmtdesc))
+ continue;
+
if (fmtdesc->fcc == pixelformat) {
uformat = format->fmt;
break;
@@ -240,6 +243,7 @@ uvc_v4l2_try_format(struct file *file, void *fh, struct v4l2_format *fmt)
struct uvc_video *video = &uvc->video;
struct uvcg_format *uformat;
struct uvcg_frame *uframe;
+ const struct uvc_format_desc *fmtdesc;
u8 *fcc;
if (fmt->type != video->queue.queue.type)
@@ -260,12 +264,29 @@ uvc_v4l2_try_format(struct file *file, void *fh, struct v4l2_format *fmt)
if (!uframe)
return -EINVAL;
- fmt->fmt.pix.width = uframe->frame.w_width;
- fmt->fmt.pix.height = uframe->frame.w_height;
+ if (uformat->type == UVCG_UNCOMPRESSED) {
+ struct uvcg_uncompressed *u =
+ to_uvcg_uncompressed(&uformat->group.cg_item);
+ if (!u)
+ return 0;
+
+ v4l2_fill_pixfmt(&fmt->fmt.pix, fmt->fmt.pix.pixelformat,
+ uframe->frame.w_width, uframe->frame.w_height);
+
+ if (fmt->fmt.pix.sizeimage != (uvc_v4l2_get_bytesperline(uformat, uframe) *
+ uframe->frame.w_height))
+ return -EINVAL;
+ } else {
+ fmt->fmt.pix.width = uframe->frame.w_width;
+ fmt->fmt.pix.height = uframe->frame.w_height;
+ fmt->fmt.pix.bytesperline = uvc_v4l2_get_bytesperline(uformat, uframe);
+ fmt->fmt.pix.sizeimage = uvc_get_frame_size(uformat, uframe);
+ fmtdesc = to_uvc_format(uformat);
+ if (IS_ERR(fmtdesc))
+ return PTR_ERR(fmtdesc);
+ fmt->fmt.pix.pixelformat = fmtdesc->fcc;
+ }
fmt->fmt.pix.field = V4L2_FIELD_NONE;
- fmt->fmt.pix.bytesperline = uvc_v4l2_get_bytesperline(uformat, uframe);
- fmt->fmt.pix.sizeimage = uvc_get_frame_size(uformat, uframe);
- fmt->fmt.pix.pixelformat = to_uvc_format(uformat)->fcc;
fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
fmt->fmt.pix.priv = 0;
@@ -375,6 +396,9 @@ uvc_v4l2_enum_format(struct file *file, void *fh, struct v4l2_fmtdesc *f)
return -EINVAL;
fmtdesc = to_uvc_format(uformat);
+ if (IS_ERR(fmtdesc))
+ return PTR_ERR(fmtdesc);
+
f->pixelformat = fmtdesc->fcc;
return 0;
diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c
index d41f5f31dadd..57a851151225 100644
--- a/drivers/usb/gadget/function/uvc_video.c
+++ b/drivers/usb/gadget/function/uvc_video.c
@@ -12,7 +12,7 @@
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/usb/video.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <media/v4l2-dev.h>
@@ -753,6 +753,7 @@ int uvcg_video_enable(struct uvc_video *video)
video->req_int_count = 0;
uvc_video_ep_queue_initial_requests(video);
+ queue_work(video->async_wq, &video->pump);
return ret;
}
diff --git a/drivers/usb/gadget/legacy/dbgp.c b/drivers/usb/gadget/legacy/dbgp.c
index b62e45235e8e..d70fb5bc2357 100644
--- a/drivers/usb/gadget/legacy/dbgp.c
+++ b/drivers/usb/gadget/legacy/dbgp.c
@@ -434,6 +434,7 @@ static void __exit dbgp_exit(void)
}
MODULE_AUTHOR("Stephane Duverger");
+MODULE_DESCRIPTION("EHCI Debug Port device gadget");
MODULE_LICENSE("GPL");
module_init(dbgp_init);
module_exit(dbgp_exit);
diff --git a/drivers/usb/gadget/legacy/gmidi.c b/drivers/usb/gadget/legacy/gmidi.c
index 265c392810d7..e4a419b19f45 100644
--- a/drivers/usb/gadget/legacy/gmidi.c
+++ b/drivers/usb/gadget/legacy/gmidi.c
@@ -31,6 +31,7 @@
/*-------------------------------------------------------------------------*/
MODULE_AUTHOR("Ben Williamson");
+MODULE_DESCRIPTION("USB MIDI Gadget Driver");
MODULE_LICENSE("GPL v2");
static const char longname[] = "MIDI Gadget";
diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c
index 03179b1880fd..9c7381661016 100644
--- a/drivers/usb/gadget/legacy/inode.c
+++ b/drivers/usb/gadget/legacy/inode.c
@@ -705,7 +705,6 @@ static const struct file_operations ep_io_operations = {
.open = ep_open,
.release = ep_release,
- .llseek = no_llseek,
.unlocked_ioctl = ep_ioctl,
.read_iter = ep_read_iter,
.write_iter = ep_write_iter,
@@ -1939,7 +1938,6 @@ gadget_dev_open (struct inode *inode, struct file *fd)
}
static const struct file_operations ep0_operations = {
- .llseek = no_llseek,
.open = gadget_dev_open,
.read = ep0_read,
diff --git a/drivers/usb/gadget/legacy/raw_gadget.c b/drivers/usb/gadget/legacy/raw_gadget.c
index 399fca32a8ac..112fd18d8c99 100644
--- a/drivers/usb/gadget/legacy/raw_gadget.c
+++ b/drivers/usb/gadget/legacy/raw_gadget.c
@@ -1364,7 +1364,6 @@ static const struct file_operations raw_fops = {
.unlocked_ioctl = raw_ioctl,
.compat_ioctl = raw_ioctl,
.release = raw_release,
- .llseek = no_llseek,
};
static struct miscdevice raw_misc_device = {
diff --git a/drivers/usb/gadget/legacy/tcm_usb_gadget.c b/drivers/usb/gadget/legacy/tcm_usb_gadget.c
index 40870227999a..fc1e06246d9d 100644
--- a/drivers/usb/gadget/legacy/tcm_usb_gadget.c
+++ b/drivers/usb/gadget/legacy/tcm_usb_gadget.c
@@ -19,7 +19,7 @@
#include <scsi/scsi_tcq.h>
#include <target/target_core_base.h>
#include <target/target_core_fabric.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include "u_tcm.h"
diff --git a/drivers/usb/gadget/legacy/zero.c b/drivers/usb/gadget/legacy/zero.c
index 23312a07efb4..e25e0d8dd387 100644
--- a/drivers/usb/gadget/legacy/zero.c
+++ b/drivers/usb/gadget/legacy/zero.c
@@ -425,4 +425,5 @@ static struct usb_composite_driver zero_driver = {
module_usb_composite_driver(zero_driver);
MODULE_AUTHOR("David Brownell");
+MODULE_DESCRIPTION("Gadget Zero, for USB development");
MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/u_f.c b/drivers/usb/gadget/u_f.c
index 6aea1ecb3999..115d219c9c00 100644
--- a/drivers/usb/gadget/u_f.c
+++ b/drivers/usb/gadget/u_f.c
@@ -8,8 +8,8 @@
* Author: Andrzej Pietrasiewicz <andrzejtp2010@gmail.com>
*/
-#include "u_f.h"
#include <linux/usb/ch9.h>
+#include <linux/usb/func_utils.h>
struct usb_request *alloc_ep_req(struct usb_ep *ep, size_t len)
{
diff --git a/drivers/usb/gadget/u_f.h b/drivers/usb/gadget/u_f.h
deleted file mode 100644
index e313c3b8dcb1..000000000000
--- a/drivers/usb/gadget/u_f.h
+++ /dev/null
@@ -1,86 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * u_f.h
- *
- * Utility definitions for USB functions
- *
- * Copyright (c) 2013 Samsung Electronics Co., Ltd.
- * http://www.samsung.com
- *
- * Author: Andrzej Pietrasiewicz <andrzejtp2010@gmail.com>
- */
-
-#ifndef __U_F_H__
-#define __U_F_H__
-
-#include <linux/usb/gadget.h>
-#include <linux/overflow.h>
-
-/* Variable Length Array Macros **********************************************/
-#define vla_group(groupname) size_t groupname##__next = 0
-#define vla_group_size(groupname) groupname##__next
-
-#define vla_item(groupname, type, name, n) \
- size_t groupname##_##name##__offset = ({ \
- size_t offset = 0; \
- if (groupname##__next != SIZE_MAX) { \
- size_t align_mask = __alignof__(type) - 1; \
- size_t size = array_size(n, sizeof(type)); \
- offset = (groupname##__next + align_mask) & \
- ~align_mask; \
- if (check_add_overflow(offset, size, \
- &groupname##__next)) { \
- groupname##__next = SIZE_MAX; \
- offset = 0; \
- } \
- } \
- offset; \
- })
-
-#define vla_item_with_sz(groupname, type, name, n) \
- size_t groupname##_##name##__sz = array_size(n, sizeof(type)); \
- size_t groupname##_##name##__offset = ({ \
- size_t offset = 0; \
- if (groupname##__next != SIZE_MAX) { \
- size_t align_mask = __alignof__(type) - 1; \
- offset = (groupname##__next + align_mask) & \
- ~align_mask; \
- if (check_add_overflow(offset, groupname##_##name##__sz,\
- &groupname##__next)) { \
- groupname##__next = SIZE_MAX; \
- offset = 0; \
- } \
- } \
- offset; \
- })
-
-#define vla_ptr(ptr, groupname, name) \
- ((void *) ((char *)ptr + groupname##_##name##__offset))
-
-struct usb_ep;
-struct usb_request;
-
-/**
- * alloc_ep_req - returns a usb_request allocated by the gadget driver and
- * allocates the request's buffer.
- *
- * @ep: the endpoint to allocate a usb_request
- * @len: usb_requests's buffer suggested size
- *
- * In case @ep direction is OUT, the @len will be aligned to ep's
- * wMaxPacketSize. In order to avoid memory leaks or drops, *always* use
- * usb_requests's length (req->length) to refer to the allocated buffer size.
- * Requests allocated via alloc_ep_req() *must* be freed by free_ep_req().
- */
-struct usb_request *alloc_ep_req(struct usb_ep *ep, size_t len);
-
-/* Frees a usb_request previously allocated by alloc_ep_req() */
-static inline void free_ep_req(struct usb_ep *ep, struct usb_request *req)
-{
- WARN_ON(req->buf == NULL);
- kfree(req->buf);
- req->buf = NULL;
- usb_ep_free_request(ep, req);
-}
-
-#endif /* __U_F_H__ */
diff --git a/drivers/usb/gadget/u_os_desc.h b/drivers/usb/gadget/u_os_desc.h
index 5d7d35c8cc31..f8b9f0faa9b1 100644
--- a/drivers/usb/gadget/u_os_desc.h
+++ b/drivers/usb/gadget/u_os_desc.h
@@ -13,7 +13,7 @@
#ifndef __U_OS_DESC_H__
#define __U_OS_DESC_H__
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <linux/nls.h>
#define USB_EXT_PROP_DW_SIZE 0
diff --git a/drivers/usb/gadget/udc/aspeed_udc.c b/drivers/usb/gadget/udc/aspeed_udc.c
index 3916c8e2ba01..f4781e611aaa 100644
--- a/drivers/usb/gadget/udc/aspeed_udc.c
+++ b/drivers/usb/gadget/udc/aspeed_udc.c
@@ -66,8 +66,8 @@
#define USB_UPSTREAM_EN BIT(0)
/* Main config reg */
-#define UDC_CFG_SET_ADDR(x) ((x) & 0x3f)
-#define UDC_CFG_ADDR_MASK (0x3f)
+#define UDC_CFG_SET_ADDR(x) ((x) & UDC_CFG_ADDR_MASK)
+#define UDC_CFG_ADDR_MASK GENMASK(6, 0)
/* Interrupt ctrl & status reg */
#define UDC_IRQ_EP_POOL_NAK BIT(17)
@@ -1009,6 +1009,8 @@ static void ast_udc_getstatus(struct ast_udc_dev *udc)
break;
case USB_RECIP_ENDPOINT:
epnum = crq.wIndex & USB_ENDPOINT_NUMBER_MASK;
+ if (epnum >= AST_UDC_NUM_ENDPOINTS)
+ goto stall;
status = udc->ep[epnum].stopped;
break;
default:
diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c
index b76885d78e8a..4928eba19327 100644
--- a/drivers/usb/gadget/udc/atmel_usba_udc.c
+++ b/drivers/usb/gadget/udc/atmel_usba_udc.c
@@ -187,7 +187,6 @@ static int regs_dbg_release(struct inode *inode, struct file *file)
static const struct file_operations queue_dbg_fops = {
.owner = THIS_MODULE,
.open = queue_dbg_open,
- .llseek = no_llseek,
.read = queue_dbg_read,
.release = queue_dbg_release,
};
diff --git a/drivers/usb/gadget/udc/bdc/bdc.h b/drivers/usb/gadget/udc/bdc/bdc.h
index 8d00b1239f21..2f4abf6f8f77 100644
--- a/drivers/usb/gadget/udc/bdc/bdc.h
+++ b/drivers/usb/gadget/udc/bdc/bdc.h
@@ -20,7 +20,7 @@
#include <linux/debugfs.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#define BRCM_BDC_NAME "bdc"
#define BRCM_BDC_DESC "Broadcom USB Device Controller driver"
diff --git a/drivers/usb/gadget/udc/bdc/bdc_core.c b/drivers/usb/gadget/udc/bdc/bdc_core.c
index 35a652807fca..5149e2b7f050 100644
--- a/drivers/usb/gadget/udc/bdc/bdc_core.c
+++ b/drivers/usb/gadget/udc/bdc/bdc_core.c
@@ -639,6 +639,7 @@ static const struct of_device_id bdc_of_match[] = {
{ .compatible = "brcm,bdc" },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, bdc_of_match);
static struct platform_driver bdc_driver = {
.driver = {
diff --git a/drivers/usb/gadget/udc/bdc/bdc_ep.c b/drivers/usb/gadget/udc/bdc/bdc_ep.c
index fa88f210ecd5..f995cfa9b99e 100644
--- a/drivers/usb/gadget/udc/bdc/bdc_ep.c
+++ b/drivers/usb/gadget/udc/bdc/bdc_ep.c
@@ -30,7 +30,7 @@
#include <linux/pm.h>
#include <linux/io.h>
#include <linux/irq.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <linux/platform_device.h>
#include <linux/usb/composite.h>
diff --git a/drivers/usb/gadget/udc/bdc/bdc_udc.c b/drivers/usb/gadget/udc/bdc/bdc_udc.c
index 53ffaf4e2e37..23826fd7a8e6 100644
--- a/drivers/usb/gadget/udc/bdc/bdc_udc.c
+++ b/drivers/usb/gadget/udc/bdc/bdc_udc.c
@@ -29,7 +29,7 @@
#include <linux/pm.h>
#include <linux/io.h>
#include <linux/irq.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <linux/platform_device.h>
#include "bdc.h"
diff --git a/drivers/usb/gadget/udc/cdns2/cdns2-ep0.c b/drivers/usb/gadget/udc/cdns2/cdns2-ep0.c
index fa12a5d46f2e..a5a9d395fd0d 100644
--- a/drivers/usb/gadget/udc/cdns2/cdns2-ep0.c
+++ b/drivers/usb/gadget/udc/cdns2/cdns2-ep0.c
@@ -8,7 +8,7 @@
*/
#include <linux/usb/composite.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include "cdns2-gadget.h"
#include "cdns2-trace.h"
diff --git a/drivers/usb/gadget/udc/cdns2/cdns2-gadget.c b/drivers/usb/gadget/udc/cdns2/cdns2-gadget.c
index 0eed0e03842c..62fce42ef2da 100644
--- a/drivers/usb/gadget/udc/cdns2/cdns2-gadget.c
+++ b/drivers/usb/gadget/udc/cdns2/cdns2-gadget.c
@@ -2033,8 +2033,8 @@ static void cdns2_quiesce(struct cdns2_device *pdev)
set_reg_bit_8(&pdev->usb_regs->usbcs, USBCS_DISCON);
/* Disable interrupt. */
- writeb(0, &pdev->interrupt_regs->extien),
- writeb(0, &pdev->interrupt_regs->usbien),
+ writeb(0, &pdev->interrupt_regs->extien);
+ writeb(0, &pdev->interrupt_regs->usbien);
writew(0, &pdev->adma_regs->ep_ien);
/* Clear interrupt line. */
@@ -2251,7 +2251,6 @@ static int cdns2_gadget_start(struct cdns2_device *pdev)
{
u32 max_speed;
void *buf;
- int val;
int ret;
pdev->usb_regs = pdev->regs;
@@ -2261,14 +2260,9 @@ static int cdns2_gadget_start(struct cdns2_device *pdev)
pdev->adma_regs = pdev->regs + CDNS2_ADMA_REGS_OFFSET;
/* Reset controller. */
- set_reg_bit_8(&pdev->usb_regs->cpuctrl, CPUCTRL_SW_RST);
-
- ret = readl_poll_timeout_atomic(&pdev->usb_regs->cpuctrl, val,
- !(val & CPUCTRL_SW_RST), 1, 10000);
- if (ret) {
- dev_err(pdev->dev, "Error: reset controller timeout\n");
- return -EINVAL;
- }
+ writeb(CPUCTRL_SW_RST | CPUCTRL_UPCLK | CPUCTRL_WUEN,
+ &pdev->usb_regs->cpuctrl);
+ usleep_range(5, 10);
usb_initialize_gadget(pdev->dev, &pdev->gadget, NULL);
diff --git a/drivers/usb/gadget/udc/cdns2/cdns2-gadget.h b/drivers/usb/gadget/udc/cdns2/cdns2-gadget.h
index 71e2f62d653a..b5d5ec12e986 100644
--- a/drivers/usb/gadget/udc/cdns2/cdns2-gadget.h
+++ b/drivers/usb/gadget/udc/cdns2/cdns2-gadget.h
@@ -292,8 +292,17 @@ struct cdns2_usb_regs {
#define SPEEDCTRL_HSDISABLE BIT(7)
/* CPUCTRL- bitmasks. */
+/* UP clock enable */
+#define CPUCTRL_UPCLK BIT(0)
/* Controller reset bit. */
#define CPUCTRL_SW_RST BIT(1)
+/**
+ * If the wuen bit is ‘1’, the upclken is automatically set to ‘1’ after
+ * detecting rising edge of wuintereq interrupt. If the wuen bit is ‘0’,
+ * the wuintereq interrupt is ignored.
+ */
+#define CPUCTRL_WUEN BIT(7)
+
/**
* struct cdns2_adma_regs - ADMA controller registers.
diff --git a/drivers/usb/gadget/udc/cdns2/cdns2-pci.c b/drivers/usb/gadget/udc/cdns2/cdns2-pci.c
index 1691541c9413..b1a8f772467c 100644
--- a/drivers/usb/gadget/udc/cdns2/cdns2-pci.c
+++ b/drivers/usb/gadget/udc/cdns2/cdns2-pci.c
@@ -15,8 +15,7 @@
#include "cdns2-gadget.h"
#define PCI_DRIVER_NAME "cdns-pci-usbhs"
-#define CDNS_VENDOR_ID 0x17cd
-#define CDNS_DEVICE_ID 0x0120
+#define PCI_DEVICE_ID_CDNS_USB2 0x0120
#define PCI_BAR_DEV 0
#define PCI_DEV_FN_DEVICE 0
@@ -114,14 +113,14 @@ static const struct dev_pm_ops cdns2_pci_pm_ops = {
};
static const struct pci_device_id cdns2_pci_ids[] = {
- { PCI_VENDOR_ID_CDNS, CDNS_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,
- PCI_CLASS_SERIAL_USB_DEVICE, PCI_ANY_ID },
+ { PCI_DEVICE(PCI_VENDOR_ID_CDNS, PCI_DEVICE_ID_CDNS_USB2),
+ .class = PCI_CLASS_SERIAL_USB_DEVICE },
{ 0, }
};
static struct pci_driver cdns2_pci_driver = {
.name = "cdns2-pci",
- .id_table = &cdns2_pci_ids[0],
+ .id_table = cdns2_pci_ids,
.probe = cdns2_pci_probe,
.remove = cdns2_pci_remove,
.driver = {
diff --git a/drivers/usb/gadget/udc/cdns2/cdns2-trace.h b/drivers/usb/gadget/udc/cdns2/cdns2-trace.h
index 61f241634ea5..ade1752956b1 100644
--- a/drivers/usb/gadget/udc/cdns2/cdns2-trace.h
+++ b/drivers/usb/gadget/udc/cdns2/cdns2-trace.h
@@ -64,7 +64,7 @@ DECLARE_EVENT_CLASS(cdns2_log_simple,
__string(text, msg)
),
TP_fast_assign(
- __assign_str(text, msg);
+ __assign_str(text);
),
TP_printk("%s", __get_str(text))
);
@@ -103,7 +103,7 @@ TRACE_EVENT(cdns2_ep_halt,
__field(u8, flush)
),
TP_fast_assign(
- __assign_str(name, ep_priv->name);
+ __assign_str(name);
__entry->halt = halt;
__entry->flush = flush;
),
@@ -119,8 +119,8 @@ TRACE_EVENT(cdns2_wa1,
__string(msg, msg)
),
TP_fast_assign(
- __assign_str(ep_name, ep_priv->name);
- __assign_str(msg, msg);
+ __assign_str(ep_name);
+ __assign_str(msg);
),
TP_printk("WA1: %s %s", __get_str(ep_name), __get_str(msg))
);
@@ -134,7 +134,7 @@ DECLARE_EVENT_CLASS(cdns2_log_doorbell,
__field(u32, ep_trbaddr)
),
TP_fast_assign(
- __assign_str(name, pep->name);
+ __assign_str(name);
__entry->ep_trbaddr = ep_trbaddr;
),
TP_printk("%s, ep_trbaddr %08x", __get_str(name),
@@ -196,7 +196,7 @@ DECLARE_EVENT_CLASS(cdns2_log_epx_irq,
__field(u32, ep_traddr)
),
TP_fast_assign(
- __assign_str(ep_name, pep->name);
+ __assign_str(ep_name);
__entry->ep_sts = readl(&pdev->adma_regs->ep_sts);
__entry->ep_ists = readl(&pdev->adma_regs->ep_ists);
__entry->ep_traddr = readl(&pdev->adma_regs->ep_traddr);
@@ -288,7 +288,7 @@ DECLARE_EVENT_CLASS(cdns2_log_request,
__field(int, end_trb)
),
TP_fast_assign(
- __assign_str(name, preq->pep->name);
+ __assign_str(name);
__entry->request = &preq->request;
__entry->preq = preq;
__entry->buf = preq->request.buf;
@@ -380,7 +380,7 @@ DECLARE_EVENT_CLASS(cdns2_log_map_request,
__field(dma_addr_t, dma)
),
TP_fast_assign(
- __assign_str(name, priv_req->pep->name);
+ __assign_str(name);
__entry->req = &priv_req->request;
__entry->buf = priv_req->request.buf;
__entry->dma = priv_req->request.dma;
@@ -411,7 +411,7 @@ DECLARE_EVENT_CLASS(cdns2_log_trb,
__field(u32, type)
),
TP_fast_assign(
- __assign_str(name, pep->name);
+ __assign_str(name);
__entry->trb = trb;
__entry->buffer = le32_to_cpu(trb->buffer);
__entry->length = le32_to_cpu(trb->length);
@@ -476,7 +476,7 @@ DECLARE_EVENT_CLASS(cdns2_log_ep,
__field(u8, dequeue)
),
TP_fast_assign(
- __assign_str(name, pep->name);
+ __assign_str(name);
__entry->maxpacket = pep->endpoint.maxpacket;
__entry->maxpacket_limit = pep->endpoint.maxpacket_limit;
__entry->flags = pep->ep_state;
@@ -568,7 +568,7 @@ DECLARE_EVENT_CLASS(cdns2_log_epx_reg_config,
__field(u32, ep_cfg_reg)
),
TP_fast_assign(
- __assign_str(ep_name, pep->name);
+ __assign_str(ep_name);
__entry->burst_size = pep->trb_burst_size;
__entry->maxpack_reg = pep->dir ? readw(&pdev->epx_regs->txmaxpack[pep->num - 1]) :
readw(&pdev->epx_regs->rxmaxpack[pep->num - 1]);
diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c
index b3a9d18a8dcd..cf6478f97f4a 100644
--- a/drivers/usb/gadget/udc/core.c
+++ b/drivers/usb/gadget/udc/core.c
@@ -118,12 +118,10 @@ int usb_ep_enable(struct usb_ep *ep)
goto out;
/* UDC drivers can't handle endpoints with maxpacket size 0 */
- if (usb_endpoint_maxp(ep->desc) == 0) {
- /*
- * We should log an error message here, but we can't call
- * dev_err() because there's no way to find the gadget
- * given only ep.
- */
+ if (!ep->desc || usb_endpoint_maxp(ep->desc) == 0) {
+ WARN_ONCE(1, "%s: ep%d (%s) has %s\n", __func__, ep->address, ep->name,
+ (!ep->desc) ? "NULL descriptor" : "maxpacket 0");
+
ret = -EINVAL;
goto out;
}
@@ -1426,8 +1424,16 @@ int usb_add_gadget(struct usb_gadget *gadget)
if (ret)
goto err_free_id;
+ ret = sysfs_create_link(&udc->dev.kobj,
+ &gadget->dev.kobj, "gadget");
+ if (ret)
+ goto err_del_gadget;
+
return 0;
+ err_del_gadget:
+ device_del(&gadget->dev);
+
err_free_id:
ida_free(&gadget_id_numbers, gadget->id_number);
@@ -1536,6 +1542,7 @@ void usb_del_gadget(struct usb_gadget *gadget)
mutex_unlock(&udc_lock);
kobject_uevent(&udc->dev.kobj, KOBJ_REMOVE);
+ sysfs_remove_link(&udc->dev.kobj, "gadget");
flush_work(&gadget->work);
device_del(&gadget->dev);
ida_free(&gadget_id_numbers, gadget->id_number);
@@ -1559,7 +1566,7 @@ EXPORT_SYMBOL_GPL(usb_del_gadget_udc);
/* ------------------------------------------------------------------------- */
-static int gadget_match_driver(struct device *dev, struct device_driver *drv)
+static int gadget_match_driver(struct device *dev, const struct device_driver *drv)
{
struct usb_gadget *gadget = dev_to_usb_gadget(dev);
struct usb_udc *udc = gadget->udc;
diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c
index 0953e1b5c030..8820d9924448 100644
--- a/drivers/usb/gadget/udc/dummy_hcd.c
+++ b/drivers/usb/gadget/udc/dummy_hcd.c
@@ -30,7 +30,7 @@
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/init.h>
-#include <linux/timer.h>
+#include <linux/hrtimer.h>
#include <linux/list.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
@@ -42,7 +42,7 @@
#include <asm/byteorder.h>
#include <linux/io.h>
#include <asm/irq.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#define DRIVER_DESC "USB Host+Gadget Emulator"
#define DRIVER_VERSION "02 May 2005"
@@ -50,6 +50,8 @@
#define POWER_BUDGET 500 /* in mA; use 8 for low-power port testing */
#define POWER_BUDGET_3 900 /* in mA */
+#define DUMMY_TIMER_INT_NSECS 125000 /* 1 microframe */
+
static const char driver_name[] = "dummy_hcd";
static const char driver_desc[] = "USB Host+Gadget Emulator";
@@ -240,7 +242,7 @@ enum dummy_rh_state {
struct dummy_hcd {
struct dummy *dum;
enum dummy_rh_state rh_state;
- struct timer_list timer;
+ struct hrtimer timer;
u32 port_status;
u32 old_status;
unsigned long re_timeout;
@@ -1301,8 +1303,9 @@ static int dummy_urb_enqueue(
urb->error_count = 1; /* mark as a new urb */
/* kick the scheduler, it'll do the rest */
- if (!timer_pending(&dum_hcd->timer))
- mod_timer(&dum_hcd->timer, jiffies + 1);
+ if (!hrtimer_active(&dum_hcd->timer))
+ hrtimer_start(&dum_hcd->timer, ns_to_ktime(DUMMY_TIMER_INT_NSECS),
+ HRTIMER_MODE_REL_SOFT);
done:
spin_unlock_irqrestore(&dum_hcd->dum->lock, flags);
@@ -1323,7 +1326,7 @@ static int dummy_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
rc = usb_hcd_check_unlink_urb(hcd, urb, status);
if (!rc && dum_hcd->rh_state != DUMMY_RH_RUNNING &&
!list_empty(&dum_hcd->urbp_list))
- mod_timer(&dum_hcd->timer, jiffies);
+ hrtimer_start(&dum_hcd->timer, ns_to_ktime(0), HRTIMER_MODE_REL_SOFT);
spin_unlock_irqrestore(&dum_hcd->dum->lock, flags);
return rc;
@@ -1777,7 +1780,7 @@ static int handle_control_request(struct dummy_hcd *dum_hcd, struct urb *urb,
* drivers except that the callbacks are invoked from soft interrupt
* context.
*/
-static void dummy_timer(struct timer_list *t)
+static enum hrtimer_restart dummy_timer(struct hrtimer *t)
{
struct dummy_hcd *dum_hcd = from_timer(dum_hcd, t, timer);
struct dummy *dum = dum_hcd->dum;
@@ -1808,8 +1811,6 @@ static void dummy_timer(struct timer_list *t)
break;
}
- /* FIXME if HZ != 1000 this will probably misbehave ... */
-
/* look at each urb queued by the host side driver */
spin_lock_irqsave(&dum->lock, flags);
@@ -1817,7 +1818,7 @@ static void dummy_timer(struct timer_list *t)
dev_err(dummy_dev(dum_hcd),
"timer fired with no URBs pending?\n");
spin_unlock_irqrestore(&dum->lock, flags);
- return;
+ return HRTIMER_NORESTART;
}
dum_hcd->next_frame_urbp = NULL;
@@ -1995,10 +1996,13 @@ return_urb:
dum_hcd->udev = NULL;
} else if (dum_hcd->rh_state == DUMMY_RH_RUNNING) {
/* want a 1 msec delay here */
- mod_timer(&dum_hcd->timer, jiffies + msecs_to_jiffies(1));
+ hrtimer_start(&dum_hcd->timer, ns_to_ktime(DUMMY_TIMER_INT_NSECS),
+ HRTIMER_MODE_REL_SOFT);
}
spin_unlock_irqrestore(&dum->lock, flags);
+
+ return HRTIMER_NORESTART;
}
/*-------------------------------------------------------------------------*/
@@ -2387,7 +2391,7 @@ static int dummy_bus_resume(struct usb_hcd *hcd)
dum_hcd->rh_state = DUMMY_RH_RUNNING;
set_link_state(dum_hcd);
if (!list_empty(&dum_hcd->urbp_list))
- mod_timer(&dum_hcd->timer, jiffies);
+ hrtimer_start(&dum_hcd->timer, ns_to_ktime(0), HRTIMER_MODE_REL_SOFT);
hcd->state = HC_STATE_RUNNING;
}
spin_unlock_irq(&dum_hcd->dum->lock);
@@ -2465,7 +2469,8 @@ static DEVICE_ATTR_RO(urbs);
static int dummy_start_ss(struct dummy_hcd *dum_hcd)
{
- timer_setup(&dum_hcd->timer, dummy_timer, 0);
+ hrtimer_init(&dum_hcd->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
+ dum_hcd->timer.function = dummy_timer;
dum_hcd->rh_state = DUMMY_RH_RUNNING;
dum_hcd->stream_en_ep = 0;
INIT_LIST_HEAD(&dum_hcd->urbp_list);
@@ -2494,7 +2499,8 @@ static int dummy_start(struct usb_hcd *hcd)
return dummy_start_ss(dum_hcd);
spin_lock_init(&dum_hcd->dum->lock);
- timer_setup(&dum_hcd->timer, dummy_timer, 0);
+ hrtimer_init(&dum_hcd->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
+ dum_hcd->timer.function = dummy_timer;
dum_hcd->rh_state = DUMMY_RH_RUNNING;
INIT_LIST_HEAD(&dum_hcd->urbp_list);
@@ -2513,8 +2519,11 @@ static int dummy_start(struct usb_hcd *hcd)
static void dummy_stop(struct usb_hcd *hcd)
{
- device_remove_file(dummy_dev(hcd_to_dummy_hcd(hcd)), &dev_attr_urbs);
- dev_info(dummy_dev(hcd_to_dummy_hcd(hcd)), "stopped\n");
+ struct dummy_hcd *dum_hcd = hcd_to_dummy_hcd(hcd);
+
+ hrtimer_cancel(&dum_hcd->timer);
+ device_remove_file(dummy_dev(dum_hcd), &dev_attr_urbs);
+ dev_info(dummy_dev(dum_hcd), "stopped\n");
}
/*-------------------------------------------------------------------------*/
diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c
index 3432ebfae978..0cabd4eee6ac 100644
--- a/drivers/usb/gadget/udc/fsl_udc_core.c
+++ b/drivers/usb/gadget/udc/fsl_udc_core.c
@@ -39,7 +39,7 @@
#include <asm/byteorder.h>
#include <asm/io.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <asm/dma.h>
#include "fsl_usb2_udc.h"
diff --git a/drivers/usb/gadget/udc/goku_udc.c b/drivers/usb/gadget/udc/goku_udc.c
index 5ffb3d5c635b..b860c2e76449 100644
--- a/drivers/usb/gadget/udc/goku_udc.c
+++ b/drivers/usb/gadget/udc/goku_udc.c
@@ -40,7 +40,7 @@
#include <asm/byteorder.h>
#include <asm/io.h>
#include <asm/irq.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include "goku_udc.h"
diff --git a/drivers/usb/gadget/udc/lpc32xx_udc.c b/drivers/usb/gadget/udc/lpc32xx_udc.c
index d5f29f8fe481..3bfd889ed56a 100644
--- a/drivers/usb/gadget/udc/lpc32xx_udc.c
+++ b/drivers/usb/gadget/udc/lpc32xx_udc.c
@@ -1487,31 +1487,29 @@ static int udc_ep0_out_req(struct lpc32xx_udc *udc)
req = list_entry(ep0->queue.next, struct lpc32xx_request,
queue);
- if (req) {
- if (req->req.length == 0) {
- /* Just dequeue request */
- done(ep0, req, 0);
- udc->ep0state = WAIT_FOR_SETUP;
- return 1;
- }
+ if (req->req.length == 0) {
+ /* Just dequeue request */
+ done(ep0, req, 0);
+ udc->ep0state = WAIT_FOR_SETUP;
+ return 1;
+ }
- /* Get data from FIFO */
- bufferspace = req->req.length - req->req.actual;
- if (bufferspace > ep0->ep.maxpacket)
- bufferspace = ep0->ep.maxpacket;
+ /* Get data from FIFO */
+ bufferspace = req->req.length - req->req.actual;
+ if (bufferspace > ep0->ep.maxpacket)
+ bufferspace = ep0->ep.maxpacket;
- /* Copy data to buffer */
- prefetchw(req->req.buf + req->req.actual);
- tr = udc_read_hwep(udc, EP_OUT, req->req.buf + req->req.actual,
- bufferspace);
- req->req.actual += bufferspace;
+ /* Copy data to buffer */
+ prefetchw(req->req.buf + req->req.actual);
+ tr = udc_read_hwep(udc, EP_OUT, req->req.buf + req->req.actual,
+ bufferspace);
+ req->req.actual += bufferspace;
- if (tr < ep0->ep.maxpacket) {
- /* This is the last packet */
- done(ep0, req, 0);
- udc->ep0state = WAIT_FOR_SETUP;
- return 1;
- }
+ if (tr < ep0->ep.maxpacket) {
+ /* This is the last packet */
+ done(ep0, req, 0);
+ udc->ep0state = WAIT_FOR_SETUP;
+ return 1;
}
return 0;
@@ -1962,18 +1960,17 @@ static void udc_handle_eps(struct lpc32xx_udc *udc, struct lpc32xx_ep *ep)
/* If there isn't a request waiting, something went wrong */
req = list_entry(ep->queue.next, struct lpc32xx_request, queue);
- if (req) {
- done(ep, req, 0);
- /* Start another request if ready */
- if (!list_empty(&ep->queue)) {
- if (ep->is_in)
- udc_ep_in_req_dma(udc, ep);
- else
- udc_ep_out_req_dma(udc, ep);
- } else
- ep->req_pending = 0;
- }
+ done(ep, req, 0);
+
+ /* Start another request if ready */
+ if (!list_empty(&ep->queue)) {
+ if (ep->is_in)
+ udc_ep_in_req_dma(udc, ep);
+ else
+ udc_ep_out_req_dma(udc, ep);
+ } else
+ ep->req_pending = 0;
}
@@ -1989,10 +1986,6 @@ static void udc_handle_dma_ep(struct lpc32xx_udc *udc, struct lpc32xx_ep *ep)
#endif
req = list_entry(ep->queue.next, struct lpc32xx_request, queue);
- if (!req) {
- ep_err(ep, "DMA interrupt on no req!\n");
- return;
- }
dd = req->dd_desc_ptr;
/* DMA descriptor should always be retired for this call */
diff --git a/drivers/usb/gadget/udc/mv_u3d_core.c b/drivers/usb/gadget/udc/mv_u3d_core.c
index 2a421f0ff931..e1dd5cf25d08 100644
--- a/drivers/usb/gadget/udc/mv_u3d_core.c
+++ b/drivers/usb/gadget/udc/mv_u3d_core.c
@@ -1307,7 +1307,7 @@ static int mv_u3d_eps_init(struct mv_u3d *u3d)
/* initialize ep0, ep0 in/out use eps[1] */
ep = &u3d->eps[1];
ep->u3d = u3d;
- strncpy(ep->name, "ep0", sizeof(ep->name));
+ strscpy(ep->name, "ep0");
ep->ep.name = ep->name;
ep->ep.ops = &mv_u3d_ep_ops;
ep->wedge = 0;
@@ -1337,7 +1337,7 @@ static int mv_u3d_eps_init(struct mv_u3d *u3d)
ep->ep.caps.dir_out = true;
}
ep->u3d = u3d;
- strncpy(ep->name, name, sizeof(ep->name));
+ strscpy(ep->name, name);
ep->ep.name = ep->name;
ep->ep.caps.type_iso = true;
diff --git a/drivers/usb/gadget/udc/mv_udc_core.c b/drivers/usb/gadget/udc/mv_udc_core.c
index 78308b64955d..71012b282891 100644
--- a/drivers/usb/gadget/udc/mv_udc_core.c
+++ b/drivers/usb/gadget/udc/mv_udc_core.c
@@ -30,7 +30,7 @@
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/platform_data/mv_usb.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include "mv_udc.h"
diff --git a/drivers/usb/gadget/udc/net2272.c b/drivers/usb/gadget/udc/net2272.c
index 19bbc38f3d35..9230db57dab7 100644
--- a/drivers/usb/gadget/udc/net2272.c
+++ b/drivers/usb/gadget/udc/net2272.c
@@ -28,7 +28,7 @@
#include <linux/usb/gadget.h>
#include <asm/byteorder.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include "net2272.h"
diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c
index 1b929c519cd7..b2903e4bbf54 100644
--- a/drivers/usb/gadget/udc/net2280.c
+++ b/drivers/usb/gadget/udc/net2280.c
@@ -56,7 +56,7 @@
#include <asm/byteorder.h>
#include <asm/irq.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#define DRIVER_DESC "PLX NET228x/USB338x USB Peripheral Controller"
#define DRIVER_VERSION "2005 Sept 27/v3.0"
diff --git a/drivers/usb/gadget/udc/omap_udc.c b/drivers/usb/gadget/udc/omap_udc.c
index f90eeecf27de..61a45e4657d5 100644
--- a/drivers/usb/gadget/udc/omap_udc.c
+++ b/drivers/usb/gadget/udc/omap_udc.c
@@ -36,7 +36,7 @@
#include <asm/byteorder.h>
#include <asm/irq.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <asm/mach-types.h>
#include <linux/omap-dma.h>
@@ -56,7 +56,6 @@
/* ISO too */
#define USE_ISO
-#define DRIVER_DESC "OMAP UDC driver"
#define DRIVER_VERSION "4 October 2004"
#define OMAP_DMA_USB_W2FC_TX0 29
@@ -110,7 +109,6 @@ MODULE_PARM_DESC(use_dma, "enable/disable DMA");
static const char driver_name[] = "omap_udc";
-static const char driver_desc[] = DRIVER_DESC;
/*-------------------------------------------------------------------------*/
@@ -2299,13 +2297,11 @@ static int proc_udc_show(struct seq_file *s, void *_)
spin_lock_irqsave(&udc->lock, flags);
- seq_printf(s, "%s, version: " DRIVER_VERSION
+ seq_printf(s, "OMAP UDC driver, version: " DRIVER_VERSION
#ifdef USE_ISO
" (iso)"
#endif
- "%s\n",
- driver_desc,
- use_dma ? " (dma)" : "");
+ "%s\n", use_dma ? " (dma)" : "");
tmp = omap_readw(UDC_REV) & 0xff;
seq_printf(s,
@@ -2994,6 +2990,6 @@ static struct platform_driver udc_driver = {
module_platform_driver(udc_driver);
-MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_DESCRIPTION("OMAP UDC driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:omap_udc");
diff --git a/drivers/usb/gadget/udc/pxa25x_udc.c b/drivers/usb/gadget/udc/pxa25x_udc.c
index 1ac26cb49ecf..7c96fc9f680f 100644
--- a/drivers/usb/gadget/udc/pxa25x_udc.c
+++ b/drivers/usb/gadget/udc/pxa25x_udc.c
@@ -38,7 +38,7 @@
#include <asm/byteorder.h>
#include <asm/dma.h>
#include <asm/mach-types.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
diff --git a/drivers/usb/gadget/udc/snps_udc_core.c b/drivers/usb/gadget/udc/snps_udc_core.c
index 2fc5d4d277bc..cd89532adec2 100644
--- a/drivers/usb/gadget/udc/snps_udc_core.c
+++ b/drivers/usb/gadget/udc/snps_udc_core.c
@@ -33,7 +33,7 @@
#include <linux/prefetch.h>
#include <linux/moduleparam.h>
#include <asm/byteorder.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include "amd5536udc.h"
static void udc_setup_endpoints(struct udc *dev);
diff --git a/drivers/usb/gadget/udc/trace.h b/drivers/usb/gadget/udc/trace.h
index a5ed26fbc2da..4e334298b0e8 100644
--- a/drivers/usb/gadget/udc/trace.h
+++ b/drivers/usb/gadget/udc/trace.h
@@ -157,7 +157,7 @@ DECLARE_EVENT_CLASS(udc_log_ep,
__field(int, ret)
),
TP_fast_assign(
- __assign_str(name, ep->name);
+ __assign_str(name);
__entry->maxpacket = ep->maxpacket;
__entry->maxpacket_limit = ep->maxpacket_limit;
__entry->max_streams = ep->max_streams;
@@ -233,7 +233,7 @@ DECLARE_EVENT_CLASS(udc_log_req,
__field(struct usb_request *, req)
),
TP_fast_assign(
- __assign_str(name, ep->name);
+ __assign_str(name);
__entry->length = req->length;
__entry->actual = req->actual;
__entry->num_sgs = req->num_sgs;
diff --git a/drivers/usb/gadget/udc/udc-xilinx.c b/drivers/usb/gadget/udc/udc-xilinx.c
index 74590f93ea61..ebc45565c33e 100644
--- a/drivers/usb/gadget/udc/udc-xilinx.c
+++ b/drivers/usb/gadget/udc/udc-xilinx.c
@@ -947,7 +947,7 @@ static int xudc_ep_disable(struct usb_ep *_ep)
ep->desc = NULL;
ep->ep_usb.desc = NULL;
- dev_dbg(udc->dev, "USB Ep %d disable\n ", ep->epnumber);
+ dev_dbg(udc->dev, "USB Ep %d disable\n", ep->epnumber);
/* Disable the endpoint.*/
epcfg = udc->read_fn(udc->addr + ep->offset);
epcfg &= ~XUSB_EP_CFG_VALID_MASK;
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 4448d0ab06f0..d011d6c753ed 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -40,11 +40,11 @@ config USB_XHCI_DBGCAP
config USB_XHCI_PCI
tristate
depends on USB_PCI
- depends on USB_XHCI_PCI_RENESAS || !USB_XHCI_PCI_RENESAS
default y
config USB_XHCI_PCI_RENESAS
tristate "Support for additional Renesas xHCI controller with firmware"
+ depends on USB_XHCI_PCI
help
Say 'Y' to enable the support for the Renesas xHCI controller with
firmware. Make sure you have the firmware for the device and
diff --git a/drivers/usb/host/ehci-brcm.c b/drivers/usb/host/ehci-brcm.c
index 77e42c739c58..68cad0620f1a 100644
--- a/drivers/usb/host/ehci-brcm.c
+++ b/drivers/usb/host/ehci-brcm.c
@@ -246,6 +246,7 @@ static const struct of_device_id brcm_ehci_of_match[] = {
{ .compatible = "brcm,bcm7445-ehci", },
{}
};
+MODULE_DEVICE_TABLE(of, brcm_ehci_of_match);
static struct platform_driver ehci_brcm_driver = {
.probe = ehci_brcm_probe,
diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c
index c063fb042926..435001128221 100644
--- a/drivers/usb/host/ehci-dbg.c
+++ b/drivers/usb/host/ehci-dbg.c
@@ -430,13 +430,13 @@ static void qh_lines(struct ehci_hcd *ehci, struct ehci_qh *qh,
mark = '/';
}
switch ((scratch >> 8) & 0x03) {
- case 0:
+ case PID_CODE_OUT:
type = "out";
break;
- case 1:
+ case PID_CODE_IN:
type = "in";
break;
- case 2:
+ case PID_CODE_SETUP:
type = "setup";
break;
default:
@@ -602,10 +602,10 @@ static unsigned output_buf_tds_dir(char *buf, struct ehci_hcd *ehci,
list_for_each_entry(qtd, &qh->qtd_list, qtd_list) {
temp++;
switch ((hc32_to_cpu(ehci, qtd->hw_token) >> 8) & 0x03) {
- case 0:
+ case PID_CODE_OUT:
type = "out";
continue;
- case 1:
+ case PID_CODE_IN:
type = "in";
continue;
}
diff --git a/drivers/usb/host/ehci-exynos.c b/drivers/usb/host/ehci-exynos.c
index f644b131cc0b..e3a961d3f5fc 100644
--- a/drivers/usb/host/ehci-exynos.c
+++ b/drivers/usb/host/ehci-exynos.c
@@ -48,7 +48,6 @@ struct exynos_ehci_hcd {
static int exynos_ehci_get_phy(struct device *dev,
struct exynos_ehci_hcd *exynos_ehci)
{
- struct device_node *child;
struct phy *phy;
int phy_number, num_phys;
int ret;
@@ -66,26 +65,22 @@ static int exynos_ehci_get_phy(struct device *dev,
return 0;
/* Get PHYs using legacy bindings */
- for_each_available_child_of_node(dev->of_node, child) {
+ for_each_available_child_of_node_scoped(dev->of_node, child) {
ret = of_property_read_u32(child, "reg", &phy_number);
if (ret) {
dev_err(dev, "Failed to parse device tree\n");
- of_node_put(child);
return ret;
}
if (phy_number >= PHY_NUMBER) {
dev_err(dev, "Invalid number of PHYs\n");
- of_node_put(child);
return -EINVAL;
}
phy = devm_of_phy_optional_get(dev, child, NULL);
exynos_ehci->phy[phy_number] = phy;
- if (IS_ERR(phy)) {
- of_node_put(child);
+ if (IS_ERR(phy))
return PTR_ERR(phy);
- }
}
exynos_ehci->legacy_phy = true;
@@ -159,20 +154,16 @@ static int exynos_ehci_probe(struct platform_device *pdev)
err = exynos_ehci_get_phy(&pdev->dev, exynos_ehci);
if (err)
- goto fail_clk;
+ goto fail_io;
- exynos_ehci->clk = devm_clk_get(&pdev->dev, "usbhost");
+ exynos_ehci->clk = devm_clk_get_enabled(&pdev->dev, "usbhost");
if (IS_ERR(exynos_ehci->clk)) {
dev_err(&pdev->dev, "Failed to get usbhost clock\n");
err = PTR_ERR(exynos_ehci->clk);
- goto fail_clk;
+ goto fail_io;
}
- err = clk_prepare_enable(exynos_ehci->clk);
- if (err)
- goto fail_clk;
-
hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(hcd->regs)) {
err = PTR_ERR(hcd->regs);
@@ -223,8 +214,6 @@ fail_add_hcd:
exynos_ehci_phy_disable(&pdev->dev);
pdev->dev.of_node = exynos_ehci->of_node;
fail_io:
- clk_disable_unprepare(exynos_ehci->clk);
-fail_clk:
usb_put_hcd(hcd);
return err;
}
@@ -240,12 +229,9 @@ static void exynos_ehci_remove(struct platform_device *pdev)
exynos_ehci_phy_disable(&pdev->dev);
- clk_disable_unprepare(exynos_ehci->clk);
-
usb_put_hcd(hcd);
}
-#ifdef CONFIG_PM
static int exynos_ehci_suspend(struct device *dev)
{
struct usb_hcd *hcd = dev_get_drvdata(dev);
@@ -288,15 +274,9 @@ static int exynos_ehci_resume(struct device *dev)
ehci_resume(hcd, false);
return 0;
}
-#else
-#define exynos_ehci_suspend NULL
-#define exynos_ehci_resume NULL
-#endif
-static const struct dev_pm_ops exynos_ehci_pm_ops = {
- .suspend = exynos_ehci_suspend,
- .resume = exynos_ehci_resume,
-};
+static DEFINE_SIMPLE_DEV_PM_OPS(exynos_ehci_pm_ops,
+ exynos_ehci_suspend, exynos_ehci_resume);
#ifdef CONFIG_OF
static const struct of_device_id exynos_ehci_match[] = {
@@ -312,7 +292,7 @@ static struct platform_driver exynos_ehci_driver = {
.shutdown = usb_hcd_platform_shutdown,
.driver = {
.name = "exynos-ehci",
- .pm = &exynos_ehci_pm_ops,
+ .pm = pm_ptr(&exynos_ehci_pm_ops),
.of_match_table = of_match_ptr(exynos_ehci_match),
}
};
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 802bfafb1012..cbc0b86fcc36 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -32,7 +32,7 @@
#include <asm/byteorder.h>
#include <asm/io.h>
#include <asm/irq.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#if defined(CONFIG_PPC_PS3)
#include <asm/firmware.h>
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
index 666f5c4db25a..ba37a9fcab92 100644
--- a/drivers/usb/host/ehci-q.c
+++ b/drivers/usb/host/ehci-q.c
@@ -27,10 +27,6 @@
/*-------------------------------------------------------------------------*/
-/* PID Codes that are used here, from EHCI specification, Table 3-16. */
-#define PID_CODE_IN 1
-#define PID_CODE_SETUP 2
-
/* fill a qtd, returning how much of the buffer we were able to queue up */
static unsigned int
@@ -230,7 +226,7 @@ static int qtd_copy_status (
/* fs/ls interrupt xfer missed the complete-split */
status = -EPROTO;
} else if (token & QTD_STS_DBE) {
- status = (QTD_PID (token) == 1) /* IN ? */
+ status = (QTD_PID(token) == PID_CODE_IN) /* IN ? */
? -ENOSR /* hc couldn't read data */
: -ECOMM; /* hc couldn't write data */
} else if (token & QTD_STS_XACT) {
@@ -606,7 +602,7 @@ qh_urb_transaction (
/* SETUP pid */
qtd_fill(ehci, qtd, urb->setup_dma,
sizeof (struct usb_ctrlrequest),
- token | (2 /* "setup" */ << 8), 8);
+ token | (PID_CODE_SETUP << 8), 8);
/* ... and always at least one more pid */
token ^= QTD_TOGGLE;
@@ -620,7 +616,7 @@ qh_urb_transaction (
/* for zero length DATA stages, STATUS is always IN */
if (len == 0)
- token |= (1 /* "in" */ << 8);
+ token |= (PID_CODE_IN << 8);
}
/*
@@ -642,7 +638,7 @@ qh_urb_transaction (
}
if (is_input)
- token |= (1 /* "in" */ << 8);
+ token |= (PID_CODE_IN << 8);
/* else it's already initted to "out" pid (0 << 8) */
maxpacket = usb_endpoint_maxp(&urb->ep->desc);
@@ -709,7 +705,7 @@ qh_urb_transaction (
if (usb_pipecontrol (urb->pipe)) {
one_more = 1;
- token ^= 0x0100; /* "in" <--> "out" */
+ token ^= (PID_CODE_IN << 8); /* "in" <--> "out" */
token |= QTD_TOGGLE; /* force DATA1 */
} else if (usb_pipeout(urb->pipe)
&& (urb->transfer_flags & URB_ZERO_PACKET)
@@ -1203,7 +1199,7 @@ static int ehci_submit_single_step_set_feature(
/* SETUP pid, and interrupt after SETUP completion */
qtd_fill(ehci, qtd, urb->setup_dma,
sizeof(struct usb_ctrlrequest),
- QTD_IOC | token | (2 /* "setup" */ << 8), 8);
+ QTD_IOC | token | (PID_CODE_SETUP << 8), 8);
submit_async(ehci, urb, &qtd_list, GFP_ATOMIC);
return 0; /*Return now; we shall come back after 15 seconds*/
@@ -1216,7 +1212,7 @@ static int ehci_submit_single_step_set_feature(
token ^= QTD_TOGGLE; /*We need to start IN with DATA-1 Pid-sequence*/
buf = urb->transfer_dma;
- token |= (1 /* "in" */ << 8); /*This is IN stage*/
+ token |= (PID_CODE_IN << 8); /*This is IN stage*/
maxpacket = usb_endpoint_maxp(&urb->ep->desc);
@@ -1229,7 +1225,7 @@ static int ehci_submit_single_step_set_feature(
qtd->hw_alt_next = EHCI_LIST_END(ehci);
/* STATUS stage for GetDesc control request */
- token ^= 0x0100; /* "in" <--> "out" */
+ token ^= (PID_CODE_IN << 8); /* "in" <--> "out" */
token |= QTD_TOGGLE; /* force DATA1 */
qtd_prev = qtd;
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index 1441e3400796..d7a3c8d13f6b 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -321,10 +321,16 @@ struct ehci_qtd {
size_t length; /* length of buffer */
} __aligned(32);
+/* PID Codes that are used here, from EHCI specification, Table 3-16. */
+#define PID_CODE_OUT 0
+#define PID_CODE_IN 1
+#define PID_CODE_SETUP 2
+
/* mask NakCnt+T in qh->hw_alt_next */
#define QTD_MASK(ehci) cpu_to_hc32(ehci, ~0x1f)
-#define IS_SHORT_READ(token) (QTD_LENGTH(token) != 0 && QTD_PID(token) == 1)
+#define IS_SHORT_READ(token) (QTD_LENGTH(token) != 0 && \
+ QTD_PID(token) == PID_CODE_IN)
/*-------------------------------------------------------------------------*/
diff --git a/drivers/usb/host/isp1362-hcd.c b/drivers/usb/host/isp1362-hcd.c
index a52c3d858f3e..31059c8f94e6 100644
--- a/drivers/usb/host/isp1362-hcd.c
+++ b/drivers/usb/host/isp1362-hcd.c
@@ -83,7 +83,7 @@
#include <asm/irq.h>
#include <asm/byteorder.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
static int dbg_level;
#ifdef ISP1362_DEBUG
diff --git a/drivers/usb/host/ohci-da8xx.c b/drivers/usb/host/ohci-da8xx.c
index d9adae53466b..d2b67da76762 100644
--- a/drivers/usb/host/ohci-da8xx.c
+++ b/drivers/usb/host/ohci-da8xx.c
@@ -22,7 +22,7 @@
#include <linux/regulator/consumer.h>
#include <linux/usb.h>
#include <linux/usb/hcd.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include "ohci.h"
diff --git a/drivers/usb/host/ohci-exynos.c b/drivers/usb/host/ohci-exynos.c
index 20e26a474591..1379e03644b2 100644
--- a/drivers/usb/host/ohci-exynos.c
+++ b/drivers/usb/host/ohci-exynos.c
@@ -37,7 +37,6 @@ struct exynos_ohci_hcd {
static int exynos_ohci_get_phy(struct device *dev,
struct exynos_ohci_hcd *exynos_ohci)
{
- struct device_node *child;
struct phy *phy;
int phy_number, num_phys;
int ret;
@@ -55,26 +54,22 @@ static int exynos_ohci_get_phy(struct device *dev,
return 0;
/* Get PHYs using legacy bindings */
- for_each_available_child_of_node(dev->of_node, child) {
+ for_each_available_child_of_node_scoped(dev->of_node, child) {
ret = of_property_read_u32(child, "reg", &phy_number);
if (ret) {
dev_err(dev, "Failed to parse device tree\n");
- of_node_put(child);
return ret;
}
if (phy_number >= PHY_NUMBER) {
dev_err(dev, "Invalid number of PHYs\n");
- of_node_put(child);
return -EINVAL;
}
phy = devm_of_phy_optional_get(dev, child, NULL);
exynos_ohci->phy[phy_number] = phy;
- if (IS_ERR(phy)) {
- of_node_put(child);
+ if (IS_ERR(phy))
return PTR_ERR(phy);
- }
}
exynos_ohci->legacy_phy = true;
@@ -135,20 +130,16 @@ static int exynos_ohci_probe(struct platform_device *pdev)
err = exynos_ohci_get_phy(&pdev->dev, exynos_ohci);
if (err)
- goto fail_clk;
+ goto fail_io;
- exynos_ohci->clk = devm_clk_get(&pdev->dev, "usbhost");
+ exynos_ohci->clk = devm_clk_get_enabled(&pdev->dev, "usbhost");
if (IS_ERR(exynos_ohci->clk)) {
dev_err(&pdev->dev, "Failed to get usbhost clock\n");
err = PTR_ERR(exynos_ohci->clk);
- goto fail_clk;
+ goto fail_io;
}
- err = clk_prepare_enable(exynos_ohci->clk);
- if (err)
- goto fail_clk;
-
hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(hcd->regs)) {
err = PTR_ERR(hcd->regs);
@@ -191,8 +182,6 @@ fail_add_hcd:
exynos_ohci_phy_disable(&pdev->dev);
pdev->dev.of_node = exynos_ohci->of_node;
fail_io:
- clk_disable_unprepare(exynos_ohci->clk);
-fail_clk:
usb_put_hcd(hcd);
return err;
}
@@ -208,8 +197,6 @@ static void exynos_ohci_remove(struct platform_device *pdev)
exynos_ohci_phy_disable(&pdev->dev);
- clk_disable_unprepare(exynos_ohci->clk);
-
usb_put_hcd(hcd);
}
@@ -221,7 +208,6 @@ static void exynos_ohci_shutdown(struct platform_device *pdev)
hcd->driver->shutdown(hcd);
}
-#ifdef CONFIG_PM
static int exynos_ohci_suspend(struct device *dev)
{
struct usb_hcd *hcd = dev_get_drvdata(dev);
@@ -258,19 +244,13 @@ static int exynos_ohci_resume(struct device *dev)
return 0;
}
-#else
-#define exynos_ohci_suspend NULL
-#define exynos_ohci_resume NULL
-#endif
static const struct ohci_driver_overrides exynos_overrides __initconst = {
.extra_priv_size = sizeof(struct exynos_ohci_hcd),
};
-static const struct dev_pm_ops exynos_ohci_pm_ops = {
- .suspend = exynos_ohci_suspend,
- .resume = exynos_ohci_resume,
-};
+static DEFINE_SIMPLE_DEV_PM_OPS(exynos_ohci_pm_ops,
+ exynos_ohci_suspend, exynos_ohci_resume);
#ifdef CONFIG_OF
static const struct of_device_id exynos_ohci_match[] = {
@@ -286,7 +266,7 @@ static struct platform_driver exynos_ohci_driver = {
.shutdown = exynos_ohci_shutdown,
.driver = {
.name = "exynos-ohci",
- .pm = &exynos_ohci_pm_ops,
+ .pm = pm_ptr(&exynos_ohci_pm_ops),
.of_match_table = of_match_ptr(exynos_ohci_match),
}
};
@@ -308,4 +288,5 @@ module_exit(ohci_exynos_cleanup);
MODULE_ALIAS("platform:exynos-ohci");
MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>");
+MODULE_DESCRIPTION("OHCI support for Samsung S5P/Exynos SoC Series");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index 5cec7640e913..9b24181fee60 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -44,7 +44,7 @@
#include <asm/io.h>
#include <asm/irq.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <asm/byteorder.h>
diff --git a/drivers/usb/host/ohci-nxp.c b/drivers/usb/host/ohci-nxp.c
index 8264c454f6bd..5b775e1ea527 100644
--- a/drivers/usb/host/ohci-nxp.c
+++ b/drivers/usb/host/ohci-nxp.c
@@ -51,8 +51,6 @@ static struct hc_driver __read_mostly ohci_nxp_hc_driver;
static struct i2c_client *isp1301_i2c_client;
-static struct clk *usb_host_clk;
-
static void isp1301_configure_lpc32xx(void)
{
/* LPC32XX only supports DAT_SE0 USB mode */
@@ -155,6 +153,7 @@ static int ohci_hcd_nxp_probe(struct platform_device *pdev)
struct resource *res;
int ret = 0, irq;
struct device_node *isp1301_node;
+ struct clk *usb_host_clk;
if (pdev->dev.of_node) {
isp1301_node = of_parse_phandle(pdev->dev.of_node,
@@ -180,26 +179,20 @@ static int ohci_hcd_nxp_probe(struct platform_device *pdev)
}
/* Enable USB host clock */
- usb_host_clk = devm_clk_get(&pdev->dev, NULL);
+ usb_host_clk = devm_clk_get_enabled(&pdev->dev, NULL);
if (IS_ERR(usb_host_clk)) {
- dev_err(&pdev->dev, "failed to acquire USB OHCI clock\n");
+ dev_err(&pdev->dev, "failed to acquire and start USB OHCI clock\n");
ret = PTR_ERR(usb_host_clk);
goto fail_disable;
}
- ret = clk_prepare_enable(usb_host_clk);
- if (ret < 0) {
- dev_err(&pdev->dev, "failed to start USB OHCI clock\n");
- goto fail_disable;
- }
-
isp1301_configure();
hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
if (!hcd) {
dev_err(&pdev->dev, "Failed to allocate HC buffer\n");
ret = -ENOMEM;
- goto fail_hcd;
+ goto fail_disable;
}
hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
@@ -229,8 +222,6 @@ static int ohci_hcd_nxp_probe(struct platform_device *pdev)
ohci_nxp_stop_hc();
fail_resource:
usb_put_hcd(hcd);
-fail_hcd:
- clk_disable_unprepare(usb_host_clk);
fail_disable:
isp1301_i2c_client = NULL;
return ret;
@@ -243,7 +234,6 @@ static void ohci_hcd_nxp_remove(struct platform_device *pdev)
usb_remove_hcd(hcd);
ohci_nxp_stop_hc();
usb_put_hcd(hcd);
- clk_disable_unprepare(usb_host_clk);
isp1301_i2c_client = NULL;
}
diff --git a/drivers/usb/host/ohci-ppc-of.c b/drivers/usb/host/ohci-ppc-of.c
index f64bfe5f4d4d..a6be061f8653 100644
--- a/drivers/usb/host/ohci-ppc-of.c
+++ b/drivers/usb/host/ohci-ppc-of.c
@@ -204,10 +204,6 @@ static const struct of_device_id ohci_hcd_ppc_of_match[] = {
#ifdef CONFIG_USB_OHCI_HCD_PPC_OF_LE
{
.name = "usb",
- .compatible = "ohci-littledian",
- },
- {
- .name = "usb",
.compatible = "ohci-le",
},
#endif
diff --git a/drivers/usb/host/oxu210hp-hcd.c b/drivers/usb/host/oxu210hp-hcd.c
index d467472f9d3c..ca3859463ba1 100644
--- a/drivers/usb/host/oxu210hp-hcd.c
+++ b/drivers/usb/host/oxu210hp-hcd.c
@@ -27,7 +27,7 @@
#include <linux/iopoll.h>
#include <asm/irq.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <linux/irq.h>
#include <linux/platform_device.h>
@@ -196,31 +196,6 @@ struct ehci_regs {
#define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC)
} __packed;
-/* Appendix C, Debug port ... intended for use with special "debug devices"
- * that can help if there's no serial console. (nonstandard enumeration.)
- */
-struct ehci_dbg_port {
- u32 control;
-#define DBGP_OWNER (1<<30)
-#define DBGP_ENABLED (1<<28)
-#define DBGP_DONE (1<<16)
-#define DBGP_INUSE (1<<10)
-#define DBGP_ERRCODE(x) (((x)>>7)&0x07)
-# define DBGP_ERR_BAD 1
-# define DBGP_ERR_SIGNAL 2
-#define DBGP_ERROR (1<<6)
-#define DBGP_GO (1<<5)
-#define DBGP_OUT (1<<4)
-#define DBGP_LEN(x) (((x)>>0)&0x0f)
- u32 pids;
-#define DBGP_PID_GET(x) (((x)>>16)&0xff)
-#define DBGP_PID_SET(data, tok) (((data)<<8)|(tok))
- u32 data03;
- u32 data47;
- u32 address;
-#define DBGP_EPADDR(dev, ep) (((dev)<<8)|(ep))
-} __packed;
-
#define QTD_NEXT(dma) cpu_to_le32((u32)dma)
/*
diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c
index 9f4bf8c5f8a5..6576515a29cd 100644
--- a/drivers/usb/host/r8a66597-hcd.c
+++ b/drivers/usb/host/r8a66597-hcd.c
@@ -297,9 +297,9 @@ static void put_child_connect_map(struct r8a66597 *r8a66597, int address)
static void set_pipe_reg_addr(struct r8a66597_pipe *pipe, u8 dma_ch)
{
u16 pipenum = pipe->info.pipenum;
- const unsigned long fifoaddr[] = {D0FIFO, D1FIFO, CFIFO};
- const unsigned long fifosel[] = {D0FIFOSEL, D1FIFOSEL, CFIFOSEL};
- const unsigned long fifoctr[] = {D0FIFOCTR, D1FIFOCTR, CFIFOCTR};
+ static const unsigned long fifoaddr[] = {D0FIFO, D1FIFO, CFIFO};
+ static const unsigned long fifosel[] = {D0FIFOSEL, D1FIFOSEL, CFIFOSEL};
+ static const unsigned long fifoctr[] = {D0FIFOCTR, D1FIFOCTR, CFIFOCTR};
if (dma_ch > R8A66597_PIPE_NO_DMA) /* dma fifo not use? */
dma_ch = R8A66597_PIPE_NO_DMA;
diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c
index 2b871540bb50..92f2d1238448 100644
--- a/drivers/usb/host/sl811-hcd.c
+++ b/drivers/usb/host/sl811-hcd.c
@@ -54,7 +54,7 @@
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/byteorder.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include "sl811.h"
diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c
index 8a9869ef0db6..241d7aa1fbc2 100644
--- a/drivers/usb/host/xhci-dbgcap.c
+++ b/drivers/usb/host/xhci-dbgcap.c
@@ -173,16 +173,18 @@ static void xhci_dbc_giveback(struct dbc_request *req, int status)
spin_lock(&dbc->lock);
}
-static void xhci_dbc_flush_single_request(struct dbc_request *req)
+static void trb_to_noop(union xhci_trb *trb)
{
- union xhci_trb *trb = req->trb;
-
trb->generic.field[0] = 0;
trb->generic.field[1] = 0;
trb->generic.field[2] = 0;
trb->generic.field[3] &= cpu_to_le32(TRB_CYCLE);
trb->generic.field[3] |= cpu_to_le32(TRB_TYPE(TRB_TR_NOOP));
+}
+static void xhci_dbc_flush_single_request(struct dbc_request *req)
+{
+ trb_to_noop(req->trb);
xhci_dbc_giveback(req, -ESHUTDOWN);
}
@@ -516,7 +518,7 @@ static int xhci_dbc_mem_init(struct xhci_dbc *dbc, gfp_t flags)
goto string_fail;
/* Setup ERST register: */
- writel(dbc->erst.erst_size, &dbc->regs->ersts);
+ writel(dbc->erst.num_entries, &dbc->regs->ersts);
lo_hi_writeq(dbc->erst.erst_dma_addr, &dbc->regs->erstba);
deq = xhci_trb_virt_to_dma(dbc->ring_evt->deq_seg,
@@ -649,7 +651,6 @@ static void xhci_dbc_stop(struct xhci_dbc *dbc)
case DS_DISABLED:
return;
case DS_CONFIGURED:
- case DS_STALLED:
if (dbc->driver->disconnect)
dbc->driver->disconnect(dbc);
break;
@@ -670,6 +671,23 @@ static void xhci_dbc_stop(struct xhci_dbc *dbc)
}
static void
+handle_ep_halt_changes(struct xhci_dbc *dbc, struct dbc_ep *dep, bool halted)
+{
+ if (halted) {
+ dev_info(dbc->dev, "DbC Endpoint halted\n");
+ dep->halted = 1;
+
+ } else if (dep->halted) {
+ dev_info(dbc->dev, "DbC Endpoint halt cleared\n");
+ dep->halted = 0;
+
+ if (!list_empty(&dep->list_pending))
+ writel(DBC_DOOR_BELL_TARGET(dep->direction),
+ &dbc->regs->doorbell);
+ }
+}
+
+static void
dbc_handle_port_status(struct xhci_dbc *dbc, union xhci_trb *event)
{
u32 portsc;
@@ -697,6 +715,7 @@ static void dbc_handle_xfer_event(struct xhci_dbc *dbc, union xhci_trb *event)
struct xhci_ring *ring;
int ep_id;
int status;
+ struct xhci_ep_ctx *ep_ctx;
u32 comp_code;
size_t remain_length;
struct dbc_request *req = NULL, *r;
@@ -706,8 +725,30 @@ static void dbc_handle_xfer_event(struct xhci_dbc *dbc, union xhci_trb *event)
ep_id = TRB_TO_EP_ID(le32_to_cpu(event->generic.field[3]));
dep = (ep_id == EPID_OUT) ?
get_out_ep(dbc) : get_in_ep(dbc);
+ ep_ctx = (ep_id == EPID_OUT) ?
+ dbc_bulkout_ctx(dbc) : dbc_bulkin_ctx(dbc);
ring = dep->ring;
+ /* Match the pending request: */
+ list_for_each_entry(r, &dep->list_pending, list_pending) {
+ if (r->trb_dma == event->trans_event.buffer) {
+ req = r;
+ break;
+ }
+ if (r->status == -COMP_STALL_ERROR) {
+ dev_warn(dbc->dev, "Give back stale stalled req\n");
+ ring->num_trbs_free++;
+ xhci_dbc_giveback(r, 0);
+ }
+ }
+
+ if (!req) {
+ dev_warn(dbc->dev, "no matched request\n");
+ return;
+ }
+
+ trace_xhci_dbc_handle_transfer(ring, &req->trb->generic);
+
switch (comp_code) {
case COMP_SUCCESS:
remain_length = 0;
@@ -718,31 +759,49 @@ static void dbc_handle_xfer_event(struct xhci_dbc *dbc, union xhci_trb *event)
case COMP_TRB_ERROR:
case COMP_BABBLE_DETECTED_ERROR:
case COMP_USB_TRANSACTION_ERROR:
- case COMP_STALL_ERROR:
dev_warn(dbc->dev, "tx error %d detected\n", comp_code);
status = -comp_code;
break;
+ case COMP_STALL_ERROR:
+ dev_warn(dbc->dev, "Stall error at bulk TRB %llx, remaining %zu, ep deq %llx\n",
+ event->trans_event.buffer, remain_length, ep_ctx->deq);
+ status = 0;
+ dep->halted = 1;
+
+ /*
+ * xHC DbC may trigger a STALL bulk xfer event when host sends a
+ * ClearFeature(ENDPOINT_HALT) request even if there wasn't an
+ * active bulk transfer.
+ *
+ * Don't give back this transfer request as hardware will later
+ * start processing TRBs starting from this 'STALLED' TRB,
+ * causing TRBs and requests to be out of sync.
+ *
+ * If STALL event shows some bytes were transferred then assume
+ * it's an actual transfer issue and give back the request.
+ * In this case mark the TRB as No-Op to avoid hw from using the
+ * TRB again.
+ */
+
+ if ((ep_ctx->deq & ~TRB_CYCLE) == event->trans_event.buffer) {
+ dev_dbg(dbc->dev, "Ep stopped on Stalled TRB\n");
+ if (remain_length == req->length) {
+ dev_dbg(dbc->dev, "Spurious stall event, keep req\n");
+ req->status = -COMP_STALL_ERROR;
+ req->actual = 0;
+ return;
+ }
+ dev_dbg(dbc->dev, "Give back stalled req, but turn TRB to No-op\n");
+ trb_to_noop(req->trb);
+ }
+ break;
+
default:
dev_err(dbc->dev, "unknown tx error %d\n", comp_code);
status = -comp_code;
break;
}
- /* Match the pending request: */
- list_for_each_entry(r, &dep->list_pending, list_pending) {
- if (r->trb_dma == event->trans_event.buffer) {
- req = r;
- break;
- }
- }
-
- if (!req) {
- dev_warn(dbc->dev, "no matched request\n");
- return;
- }
-
- trace_xhci_dbc_handle_transfer(ring, &req->trb->generic);
-
ring->num_trbs_free++;
req->actual = req->length - remain_length;
xhci_dbc_giveback(req, status);
@@ -762,7 +821,6 @@ static void inc_evt_deq(struct xhci_ring *ring)
static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc)
{
dma_addr_t deq;
- struct dbc_ep *dep;
union xhci_trb *evt;
u32 ctrl, portsc;
bool update_erdp = false;
@@ -814,43 +872,17 @@ static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc)
return EVT_DISC;
}
- /* Handle endpoint stall event: */
+ /* Check and handle changes in endpoint halt status */
ctrl = readl(&dbc->regs->control);
- if ((ctrl & DBC_CTRL_HALT_IN_TR) ||
- (ctrl & DBC_CTRL_HALT_OUT_TR)) {
- dev_info(dbc->dev, "DbC Endpoint stall\n");
- dbc->state = DS_STALLED;
-
- if (ctrl & DBC_CTRL_HALT_IN_TR) {
- dep = get_in_ep(dbc);
- xhci_dbc_flush_endpoint_requests(dep);
- }
-
- if (ctrl & DBC_CTRL_HALT_OUT_TR) {
- dep = get_out_ep(dbc);
- xhci_dbc_flush_endpoint_requests(dep);
- }
-
- return EVT_DONE;
- }
+ handle_ep_halt_changes(dbc, get_in_ep(dbc), ctrl & DBC_CTRL_HALT_IN_TR);
+ handle_ep_halt_changes(dbc, get_out_ep(dbc), ctrl & DBC_CTRL_HALT_OUT_TR);
/* Clear DbC run change bit: */
if (ctrl & DBC_CTRL_DBC_RUN_CHANGE) {
writel(ctrl, &dbc->regs->control);
ctrl = readl(&dbc->regs->control);
}
-
break;
- case DS_STALLED:
- ctrl = readl(&dbc->regs->control);
- if (!(ctrl & DBC_CTRL_HALT_IN_TR) &&
- !(ctrl & DBC_CTRL_HALT_OUT_TR) &&
- (ctrl & DBC_CTRL_DBC_RUN)) {
- dbc->state = DS_CONFIGURED;
- break;
- }
-
- return EVT_DONE;
default:
dev_err(dbc->dev, "Unknown DbC state %d\n", dbc->state);
break;
@@ -939,7 +971,6 @@ static const char * const dbc_state_strings[DS_MAX] = {
[DS_ENABLED] = "enabled",
[DS_CONNECTED] = "connected",
[DS_CONFIGURED] = "configured",
- [DS_STALLED] = "stalled",
};
static ssize_t dbc_show(struct device *dev,
@@ -1150,11 +1181,48 @@ static ssize_t dbc_bInterfaceProtocol_store(struct device *dev,
return size;
}
+static ssize_t dbc_poll_interval_ms_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct xhci_dbc *dbc;
+ struct xhci_hcd *xhci;
+
+ xhci = hcd_to_xhci(dev_get_drvdata(dev));
+ dbc = xhci->dbc;
+
+ return sysfs_emit(buf, "%u\n", dbc->poll_interval);
+}
+
+static ssize_t dbc_poll_interval_ms_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct xhci_dbc *dbc;
+ struct xhci_hcd *xhci;
+ u32 value;
+ int ret;
+
+ ret = kstrtou32(buf, 0, &value);
+ if (ret || value > DBC_POLL_INTERVAL_MAX)
+ return -EINVAL;
+
+ xhci = hcd_to_xhci(dev_get_drvdata(dev));
+ dbc = xhci->dbc;
+
+ dbc->poll_interval = value;
+
+ mod_delayed_work(system_wq, &dbc->event_work, 0);
+
+ return size;
+}
+
static DEVICE_ATTR_RW(dbc);
static DEVICE_ATTR_RW(dbc_idVendor);
static DEVICE_ATTR_RW(dbc_idProduct);
static DEVICE_ATTR_RW(dbc_bcdDevice);
static DEVICE_ATTR_RW(dbc_bInterfaceProtocol);
+static DEVICE_ATTR_RW(dbc_poll_interval_ms);
static struct attribute *dbc_dev_attrs[] = {
&dev_attr_dbc.attr,
@@ -1162,6 +1230,7 @@ static struct attribute *dbc_dev_attrs[] = {
&dev_attr_dbc_idProduct.attr,
&dev_attr_dbc_bcdDevice.attr,
&dev_attr_dbc_bInterfaceProtocol.attr,
+ &dev_attr_dbc_poll_interval_ms.attr,
NULL
};
ATTRIBUTE_GROUPS(dbc_dev);
diff --git a/drivers/usb/host/xhci-dbgcap.h b/drivers/usb/host/xhci-dbgcap.h
index 92661b555c2a..8ec813b6e9fd 100644
--- a/drivers/usb/host/xhci-dbgcap.h
+++ b/drivers/usb/host/xhci-dbgcap.h
@@ -81,7 +81,6 @@ enum dbc_state {
DS_ENABLED,
DS_CONNECTED,
DS_CONFIGURED,
- DS_STALLED,
DS_MAX
};
@@ -90,12 +89,13 @@ struct dbc_ep {
struct list_head list_pending;
struct xhci_ring *ring;
unsigned int direction:1;
+ unsigned int halted:1;
};
#define DBC_QUEUE_SIZE 16
#define DBC_WRITE_BUF_SIZE 8192
#define DBC_POLL_INTERVAL_DEFAULT 64 /* milliseconds */
-
+#define DBC_POLL_INTERVAL_MAX 5000 /* milliseconds */
/*
* Private structure for DbC hardware state:
*/
@@ -110,7 +110,6 @@ struct dbc_port {
struct tasklet_struct push;
struct list_head write_pool;
- struct kfifo write_fifo;
bool registered;
};
diff --git a/drivers/usb/host/xhci-dbgtty.c b/drivers/usb/host/xhci-dbgtty.c
index b74e98e94393..b8e78867e25a 100644
--- a/drivers/usb/host/xhci-dbgtty.c
+++ b/drivers/usb/host/xhci-dbgtty.c
@@ -24,19 +24,6 @@ static inline struct dbc_port *dbc_to_port(struct xhci_dbc *dbc)
return dbc->priv;
}
-static unsigned int
-dbc_send_packet(struct dbc_port *port, char *packet, unsigned int size)
-{
- unsigned int len;
-
- len = kfifo_len(&port->write_fifo);
- if (len < size)
- size = len;
- if (size != 0)
- size = kfifo_out(&port->write_fifo, packet, size);
- return size;
-}
-
static int dbc_start_tx(struct dbc_port *port)
__releases(&port->port_lock)
__acquires(&port->port_lock)
@@ -49,7 +36,7 @@ static int dbc_start_tx(struct dbc_port *port)
while (!list_empty(pool)) {
req = list_entry(pool->next, struct dbc_request, list_pool);
- len = dbc_send_packet(port, req->buf, DBC_MAX_PACKET);
+ len = kfifo_out(&port->port.xmit_fifo, req->buf, DBC_MAX_PACKET);
if (len == 0)
break;
do_tty_wake = true;
@@ -216,7 +203,7 @@ static ssize_t dbc_tty_write(struct tty_struct *tty, const u8 *buf,
spin_lock_irqsave(&port->port_lock, flags);
if (count)
- count = kfifo_in(&port->write_fifo, buf, count);
+ count = kfifo_in(&port->port.xmit_fifo, buf, count);
dbc_start_tx(port);
spin_unlock_irqrestore(&port->port_lock, flags);
@@ -230,7 +217,7 @@ static int dbc_tty_put_char(struct tty_struct *tty, u8 ch)
int status;
spin_lock_irqsave(&port->port_lock, flags);
- status = kfifo_put(&port->write_fifo, ch);
+ status = kfifo_put(&port->port.xmit_fifo, ch);
spin_unlock_irqrestore(&port->port_lock, flags);
return status;
@@ -253,7 +240,7 @@ static unsigned int dbc_tty_write_room(struct tty_struct *tty)
unsigned int room;
spin_lock_irqsave(&port->port_lock, flags);
- room = kfifo_avail(&port->write_fifo);
+ room = kfifo_avail(&port->port.xmit_fifo);
spin_unlock_irqrestore(&port->port_lock, flags);
return room;
@@ -266,7 +253,7 @@ static unsigned int dbc_tty_chars_in_buffer(struct tty_struct *tty)
unsigned int chars;
spin_lock_irqsave(&port->port_lock, flags);
- chars = kfifo_len(&port->write_fifo);
+ chars = kfifo_len(&port->port.xmit_fifo);
spin_unlock_irqrestore(&port->port_lock, flags);
return chars;
@@ -346,7 +333,7 @@ static void dbc_rx_push(struct tasklet_struct *t)
port->n_read = 0;
}
- list_move(&req->list_pool, &port->read_pool);
+ list_move_tail(&req->list_pool, &port->read_pool);
}
if (do_push)
@@ -424,7 +411,8 @@ static int xhci_dbc_tty_register_device(struct xhci_dbc *dbc)
goto err_idr;
}
- ret = kfifo_alloc(&port->write_fifo, DBC_WRITE_BUF_SIZE, GFP_KERNEL);
+ ret = kfifo_alloc(&port->port.xmit_fifo, DBC_WRITE_BUF_SIZE,
+ GFP_KERNEL);
if (ret)
goto err_exit_port;
@@ -453,7 +441,7 @@ err_free_requests:
xhci_dbc_free_requests(&port->read_pool);
xhci_dbc_free_requests(&port->write_pool);
err_free_fifo:
- kfifo_free(&port->write_fifo);
+ kfifo_free(&port->port.xmit_fifo);
err_exit_port:
idr_remove(&dbc_tty_minors, port->minor);
err_idr:
@@ -478,7 +466,7 @@ static void xhci_dbc_tty_unregister_device(struct xhci_dbc *dbc)
idr_remove(&dbc_tty_minors, port->minor);
mutex_unlock(&dbc_tty_minors_lock);
- kfifo_free(&port->write_fifo);
+ kfifo_free(&port->port.xmit_fifo);
xhci_dbc_free_requests(&port->read_pool);
xhci_dbc_free_requests(&port->read_queue);
xhci_dbc_free_requests(&port->write_pool);
diff --git a/drivers/usb/host/xhci-ext-caps.h b/drivers/usb/host/xhci-ext-caps.h
index 96eb36a58738..67ecf7320c62 100644
--- a/drivers/usb/host/xhci-ext-caps.h
+++ b/drivers/usb/host/xhci-ext-caps.h
@@ -42,6 +42,7 @@
#define XHCI_EXT_CAPS_DEBUG 10
/* Vendor caps */
#define XHCI_EXT_CAPS_VENDOR_INTEL 192
+#define XHCI_EXT_CAPS_INTEL_SPR_SHADOW 206
/* USB Legacy Support Capability - section 7.1.1 */
#define XHCI_HC_BIOS_OWNED (1 << 16)
#define XHCI_HC_OS_OWNED (1 << 24)
@@ -64,6 +65,10 @@
#define XHCI_HLC (1 << 19)
#define XHCI_BLC (1 << 20)
+/* Intel SPR shadow capability */
+#define XHCI_INTEL_SPR_ESS_PORT_OFFSET 0x8ac4 /* SuperSpeed port control */
+#define XHCI_INTEL_SPR_TUNEN BIT(4) /* Tunnel mode enabled */
+
/* command register values to disable interrupts and halt the HC */
/* start/stop HC execution - do not write unless HC is halted*/
#define XHCI_CMD_RUN (1 << 0)
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index 61f083de6e19..8d774f19271e 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -10,7 +10,7 @@
#include <linux/slab.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <linux/bitfield.h>
#include "xhci.h"
@@ -752,6 +752,42 @@ static int xhci_exit_test_mode(struct xhci_hcd *xhci)
return xhci_reset(xhci, XHCI_RESET_SHORT_USEC);
}
+/**
+ * xhci_port_is_tunneled() - Check if USB3 connection is tunneled over USB4
+ * @xhci: xhci host controller
+ * @port: USB3 port to be checked.
+ *
+ * Some hosts can detect if a USB3 connection is native USB3 or tunneled over
+ * USB4. Intel hosts expose this via vendor specific extended capability 206
+ * eSS PORT registers TUNEN (tunnel enabled) bit.
+ *
+ * A USB3 device must be connected to the port to detect the tunnel.
+ *
+ * Return: link tunnel mode enum, USB_LINK_UNKNOWN if host is incapable of
+ * detecting USB3 over USB4 tunnels. USB_LINK_NATIVE or USB_LINK_TUNNELED
+ * otherwise.
+ */
+enum usb_link_tunnel_mode xhci_port_is_tunneled(struct xhci_hcd *xhci,
+ struct xhci_port *port)
+{
+ void __iomem *base;
+ u32 offset;
+
+ base = &xhci->cap_regs->hc_capbase;
+ offset = xhci_find_next_ext_cap(base, 0, XHCI_EXT_CAPS_INTEL_SPR_SHADOW);
+
+ if (offset && offset <= XHCI_INTEL_SPR_ESS_PORT_OFFSET) {
+ offset = XHCI_INTEL_SPR_ESS_PORT_OFFSET + port->hcd_portnum * 0x20;
+
+ if (readl(base + offset) & XHCI_INTEL_SPR_TUNEN)
+ return USB_LINK_TUNNELED;
+ else
+ return USB_LINK_NATIVE;
+ }
+
+ return USB_LINK_UNKNOWN;
+}
+
void xhci_set_link_state(struct xhci_hcd *xhci, struct xhci_port *port,
u32 link_state)
{
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index 69dd86669883..d2900197a49e 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -136,10 +136,7 @@ static void xhci_link_rings(struct xhci_hcd *xhci, struct xhci_ring *ring,
if (!ring || !first || !last)
return;
- /* Set chain bit for 0.95 hosts, and for isoc rings on AMD 0.96 host */
- chain_links = !!(xhci_link_trb_quirk(xhci) ||
- (ring->type == TYPE_ISOC &&
- (xhci->quirks & XHCI_AMD_0x96_HOST)));
+ chain_links = xhci_link_chain_quirk(xhci, ring->type);
next = ring->enq_seg->next;
xhci_link_segments(ring->enq_seg, first, ring->type, chain_links);
@@ -156,7 +153,7 @@ static void xhci_link_rings(struct xhci_hcd *xhci, struct xhci_ring *ring,
ring->last_seg = last;
}
- for (seg = last; seg != ring->last_seg; seg = seg->next)
+ for (seg = ring->enq_seg; seg != ring->last_seg; seg = seg->next)
seg->next->num = seg->num + 1;
}
@@ -327,18 +324,19 @@ EXPORT_SYMBOL_GPL(xhci_initialize_ring_info);
/* Allocate segments and link them for a ring */
static int xhci_alloc_segments_for_ring(struct xhci_hcd *xhci,
- struct xhci_segment **first, struct xhci_segment **last,
- unsigned int num_segs, unsigned int num,
- unsigned int cycle_state, enum xhci_ring_type type,
- unsigned int max_packet, gfp_t flags)
+ struct xhci_segment **first,
+ struct xhci_segment **last,
+ unsigned int num_segs,
+ unsigned int cycle_state,
+ enum xhci_ring_type type,
+ unsigned int max_packet,
+ gfp_t flags)
{
struct xhci_segment *prev;
+ unsigned int num = 0;
bool chain_links;
- /* Set chain bit for 0.95 hosts, and for isoc rings on AMD 0.96 host */
- chain_links = !!(xhci_link_trb_quirk(xhci) ||
- (type == TYPE_ISOC &&
- (xhci->quirks & XHCI_AMD_0x96_HOST)));
+ chain_links = xhci_link_chain_quirk(xhci, type);
prev = xhci_segment_alloc(xhci, cycle_state, max_packet, num, flags);
if (!prev)
@@ -394,9 +392,8 @@ struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci,
if (num_segs == 0)
return ring;
- ret = xhci_alloc_segments_for_ring(xhci, &ring->first_seg,
- &ring->last_seg, num_segs, 0, cycle_state, type,
- max_packet, flags);
+ ret = xhci_alloc_segments_for_ring(xhci, &ring->first_seg, &ring->last_seg, num_segs,
+ cycle_state, type, max_packet, flags);
if (ret)
goto fail;
@@ -434,10 +431,8 @@ int xhci_ring_expansion(struct xhci_hcd *xhci, struct xhci_ring *ring,
struct xhci_segment *last;
int ret;
- ret = xhci_alloc_segments_for_ring(xhci, &first, &last,
- num_new_segs, ring->enq_seg->num + 1,
- ring->cycle_state, ring->type,
- ring->bounce_buf_len, flags);
+ ret = xhci_alloc_segments_for_ring(xhci, &first, &last, num_new_segs, ring->cycle_state,
+ ring->type, ring->bounce_buf_len, flags);
if (ret)
return -ENOMEM;
@@ -536,7 +531,7 @@ static void xhci_free_stream_ctx(struct xhci_hcd *xhci,
struct xhci_stream_ctx *stream_ctx, dma_addr_t dma)
{
struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
- size_t size = sizeof(struct xhci_stream_ctx) * num_stream_ctxs;
+ size_t size = array_size(sizeof(struct xhci_stream_ctx), num_stream_ctxs);
if (size > MEDIUM_STREAM_ARRAY_SIZE)
dma_free_coherent(dev, size, stream_ctx, dma);
@@ -561,7 +556,7 @@ static struct xhci_stream_ctx *xhci_alloc_stream_ctx(struct xhci_hcd *xhci,
gfp_t mem_flags)
{
struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
- size_t size = size_mul(sizeof(struct xhci_stream_ctx), num_stream_ctxs);
+ size_t size = array_size(sizeof(struct xhci_stream_ctx), num_stream_ctxs);
if (size > MEDIUM_STREAM_ARRAY_SIZE)
return dma_alloc_coherent(dev, size, dma, mem_flags);
@@ -1638,7 +1633,7 @@ static int scratchpad_alloc(struct xhci_hcd *xhci, gfp_t flags)
goto fail_sp;
xhci->scratchpad->sp_array = dma_alloc_coherent(dev,
- size_mul(sizeof(u64), num_sp),
+ array_size(sizeof(u64), num_sp),
&xhci->scratchpad->sp_dma, flags);
if (!xhci->scratchpad->sp_array)
goto fail_sp2;
@@ -1671,7 +1666,7 @@ static int scratchpad_alloc(struct xhci_hcd *xhci, gfp_t flags)
kfree(xhci->scratchpad->sp_buffers);
fail_sp3:
- dma_free_coherent(dev, num_sp * sizeof(u64),
+ dma_free_coherent(dev, array_size(sizeof(u64), num_sp),
xhci->scratchpad->sp_array,
xhci->scratchpad->sp_dma);
@@ -1700,7 +1695,7 @@ static void scratchpad_free(struct xhci_hcd *xhci)
xhci->scratchpad->sp_array[i]);
}
kfree(xhci->scratchpad->sp_buffers);
- dma_free_coherent(dev, num_sp * sizeof(u64),
+ dma_free_coherent(dev, array_size(sizeof(u64), num_sp),
xhci->scratchpad->sp_array,
xhci->scratchpad->sp_dma);
kfree(xhci->scratchpad);
@@ -1778,7 +1773,7 @@ static int xhci_alloc_erst(struct xhci_hcd *xhci,
struct xhci_segment *seg;
struct xhci_erst_entry *entry;
- size = size_mul(sizeof(struct xhci_erst_entry), evt_ring->num_segs);
+ size = array_size(sizeof(struct xhci_erst_entry), evt_ring->num_segs);
erst->entries = dma_alloc_coherent(xhci_to_hcd(xhci)->self.sysdev,
size, &erst->erst_dma_addr, flags);
if (!erst->entries)
@@ -1829,7 +1824,7 @@ xhci_free_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir)
if (!ir)
return;
- erst_size = sizeof(struct xhci_erst_entry) * ir->erst.num_entries;
+ erst_size = array_size(sizeof(struct xhci_erst_entry), ir->erst.num_entries);
if (ir->erst.entries)
dma_free_coherent(dev, erst_size,
ir->erst.entries,
@@ -1877,7 +1872,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
cancel_delayed_work_sync(&xhci->cmd_timer);
- for (i = 0; i < xhci->max_interrupters; i++) {
+ for (i = 0; xhci->interrupters && i < xhci->max_interrupters; i++) {
if (xhci->interrupters[i]) {
xhci_remove_interrupter(xhci, xhci->interrupters[i]);
xhci_free_interrupter(xhci, xhci->interrupters[i]);
@@ -1950,7 +1945,6 @@ no_bw:
kfree(xhci->usb3_rhub.ports);
kfree(xhci->hw_ports);
kfree(xhci->rh_bw);
- kfree(xhci->ext_caps);
for (i = 0; i < xhci->num_port_caps; i++)
kfree(xhci->port_caps[i].psi);
kfree(xhci->port_caps);
@@ -1961,7 +1955,6 @@ no_bw:
xhci->usb3_rhub.ports = NULL;
xhci->hw_ports = NULL;
xhci->rh_bw = NULL;
- xhci->ext_caps = NULL;
xhci->port_caps = NULL;
xhci->interrupters = NULL;
@@ -2089,10 +2082,7 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports,
port_cap->maj_rev = major_revision;
port_cap->min_rev = minor_revision;
-
- /* cache usb2 port capabilities */
- if (major_revision < 0x03 && xhci->num_ext_caps < max_caps)
- xhci->ext_caps[xhci->num_ext_caps++] = temp;
+ port_cap->protocol_caps = temp;
if ((xhci->hci_version >= 0x100) && (major_revision != 0x03) &&
(temp & XHCI_HLC)) {
@@ -2212,11 +2202,6 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags)
XHCI_EXT_CAPS_PROTOCOL);
}
- xhci->ext_caps = kcalloc_node(cap_count, sizeof(*xhci->ext_caps),
- flags, dev_to_node(dev));
- if (!xhci->ext_caps)
- return -ENOMEM;
-
xhci->port_caps = kcalloc_node(cap_count, sizeof(*xhci->port_caps),
flags, dev_to_node(dev));
if (!xhci->port_caps)
@@ -2269,24 +2254,24 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags)
}
static struct xhci_interrupter *
-xhci_alloc_interrupter(struct xhci_hcd *xhci, int segs, gfp_t flags)
+xhci_alloc_interrupter(struct xhci_hcd *xhci, unsigned int segs, gfp_t flags)
{
struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
struct xhci_interrupter *ir;
- unsigned int num_segs = segs;
+ unsigned int max_segs;
int ret;
+ if (!segs)
+ segs = ERST_DEFAULT_SEGS;
+
+ max_segs = BIT(HCS_ERST_MAX(xhci->hcs_params2));
+ segs = min(segs, max_segs);
+
ir = kzalloc_node(sizeof(*ir), flags, dev_to_node(dev));
if (!ir)
return NULL;
- /* number of ring segments should be greater than 0 */
- if (segs <= 0)
- num_segs = min_t(unsigned int, 1 << HCS_ERST_MAX(xhci->hcs_params2),
- ERST_MAX_SEGS);
-
- ir->event_ring = xhci_ring_alloc(xhci, num_segs, 1, TYPE_EVENT, 0,
- flags);
+ ir->event_ring = xhci_ring_alloc(xhci, segs, 1, TYPE_EVENT, 0, flags);
if (!ir->event_ring) {
xhci_warn(xhci, "Failed to allocate interrupter event ring\n");
kfree(ir);
@@ -2335,7 +2320,10 @@ xhci_add_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir,
erst_base = xhci_read_64(xhci, &ir->ir_set->erst_base);
erst_base &= ERST_BASE_RSVDP;
erst_base |= ir->erst.erst_dma_addr & ~ERST_BASE_RSVDP;
- xhci_write_64(xhci, erst_base, &ir->ir_set->erst_base);
+ if (xhci->quirks & XHCI_WRITE_64_HI_LO)
+ hi_lo_writeq(erst_base, &ir->ir_set->erst_base);
+ else
+ xhci_write_64(xhci, erst_base, &ir->ir_set->erst_base);
/* Set the event ring dequeue address of this interrupter */
xhci_set_hc_event_deq(xhci, ir);
@@ -2344,7 +2332,8 @@ xhci_add_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir,
}
struct xhci_interrupter *
-xhci_create_secondary_interrupter(struct usb_hcd *hcd, int num_seg)
+xhci_create_secondary_interrupter(struct usb_hcd *hcd, unsigned int segs,
+ u32 imod_interval)
{
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
struct xhci_interrupter *ir;
@@ -2354,7 +2343,7 @@ xhci_create_secondary_interrupter(struct usb_hcd *hcd, int num_seg)
if (!xhci->interrupters || xhci->max_interrupters <= 1)
return NULL;
- ir = xhci_alloc_interrupter(xhci, num_seg, GFP_KERNEL);
+ ir = xhci_alloc_interrupter(xhci, segs, GFP_KERNEL);
if (!ir)
return NULL;
@@ -2377,6 +2366,11 @@ xhci_create_secondary_interrupter(struct usb_hcd *hcd, int num_seg)
return NULL;
}
+ err = xhci_set_interrupter_moderation(ir, imod_interval);
+ if (err)
+ xhci_warn(xhci, "Failed to set interrupter %d moderation to %uns\n",
+ i, imod_interval);
+
xhci_dbg(xhci, "Add secondary interrupter %d, max interrupters %d\n",
i, xhci->max_interrupters);
diff --git a/drivers/usb/host/xhci-pci-renesas.c b/drivers/usb/host/xhci-pci-renesas.c
index 93f8b355bc70..65fc9319d5e7 100644
--- a/drivers/usb/host/xhci-pci-renesas.c
+++ b/drivers/usb/host/xhci-pci-renesas.c
@@ -6,7 +6,7 @@
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/slab.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include "xhci.h"
#include "xhci-trace.h"
@@ -50,6 +50,8 @@
#define RENESAS_RETRY 10000
#define RENESAS_DELAY 10
+#define RENESAS_FW_NAME "renesas_usb_fw.mem"
+
static int renesas_fw_download_image(struct pci_dev *dev,
const u32 *fw, size_t step, bool rom)
{
@@ -573,12 +575,10 @@ exit:
return err;
}
-int renesas_xhci_check_request_fw(struct pci_dev *pdev,
- const struct pci_device_id *id)
+static int renesas_xhci_check_request_fw(struct pci_dev *pdev,
+ const struct pci_device_id *id)
{
- struct xhci_driver_data *driver_data =
- (struct xhci_driver_data *)id->driver_data;
- const char *fw_name = driver_data->firmware;
+ const char fw_name[] = RENESAS_FW_NAME;
const struct firmware *fw;
bool has_rom;
int err;
@@ -625,6 +625,41 @@ exit:
release_firmware(fw);
return err;
}
-EXPORT_SYMBOL_GPL(renesas_xhci_check_request_fw);
+static int
+xhci_pci_renesas_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+ int retval;
+
+ retval = renesas_xhci_check_request_fw(dev, id);
+ if (retval)
+ return retval;
+
+ return xhci_pci_common_probe(dev, id);
+}
+
+static const struct pci_device_id pci_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, 0x0014) },
+ { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, 0x0015) },
+ { /* end: all zeroes */ }
+};
+MODULE_DEVICE_TABLE(pci, pci_ids);
+
+static struct pci_driver xhci_renesas_pci_driver = {
+ .name = "xhci-pci-renesas",
+ .id_table = pci_ids,
+
+ .probe = xhci_pci_renesas_probe,
+ .remove = xhci_pci_remove,
+
+ .shutdown = usb_hcd_pci_shutdown,
+ .driver = {
+ .pm = pm_ptr(&usb_hcd_pci_pm_ops),
+ },
+};
+module_pci_driver(xhci_renesas_pci_driver);
+
+MODULE_DESCRIPTION("Renesas xHCI PCI Host Controller Driver");
+MODULE_FIRMWARE(RENESAS_FW_NAME);
+MODULE_IMPORT_NS(xhci);
MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index 93b697648018..91dccd25a551 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -36,6 +36,7 @@
#define PCI_VENDOR_ID_ETRON 0x1b6f
#define PCI_DEVICE_ID_EJ168 0x7023
+#define PCI_DEVICE_ID_EJ188 0x7052
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_XHCI 0x8c31
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31
@@ -45,8 +46,20 @@
#define PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_XHCI 0x9d2f
#define PCI_DEVICE_ID_INTEL_BROXTON_M_XHCI 0x0aa8
#define PCI_DEVICE_ID_INTEL_BROXTON_B_XHCI 0x1aa8
-#define PCI_DEVICE_ID_INTEL_APL_XHCI 0x5aa8
-#define PCI_DEVICE_ID_INTEL_DNV_XHCI 0x19d0
+#define PCI_DEVICE_ID_INTEL_APOLLO_LAKE_XHCI 0x5aa8
+#define PCI_DEVICE_ID_INTEL_DENVERTON_XHCI 0x19d0
+#define PCI_DEVICE_ID_INTEL_ICE_LAKE_XHCI 0x8a13
+#define PCI_DEVICE_ID_INTEL_TIGER_LAKE_XHCI 0x9a13
+#define PCI_DEVICE_ID_INTEL_TIGER_LAKE_PCH_XHCI 0xa0ed
+#define PCI_DEVICE_ID_INTEL_COMET_LAKE_XHCI 0xa3af
+#define PCI_DEVICE_ID_INTEL_ALDER_LAKE_PCH_XHCI 0x51ed
+#define PCI_DEVICE_ID_INTEL_ALDER_LAKE_N_PCH_XHCI 0x54ed
+
+#define PCI_VENDOR_ID_PHYTIUM 0x1db7
+#define PCI_DEVICE_ID_PHYTIUM_XHCI 0xdc27
+
+/* Thunderbolt */
+#define PCI_DEVICE_ID_INTEL_MAPLE_RIDGE_XHCI 0x1138
#define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_2C_XHCI 0x15b5
#define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_4C_XHCI 0x15b6
#define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_LP_XHCI 0x15c1
@@ -55,12 +68,6 @@
#define PCI_DEVICE_ID_INTEL_TITAN_RIDGE_2C_XHCI 0x15e9
#define PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_XHCI 0x15ec
#define PCI_DEVICE_ID_INTEL_TITAN_RIDGE_DD_XHCI 0x15f0
-#define PCI_DEVICE_ID_INTEL_ICE_LAKE_XHCI 0x8a13
-#define PCI_DEVICE_ID_INTEL_CML_XHCI 0xa3af
-#define PCI_DEVICE_ID_INTEL_TIGER_LAKE_XHCI 0x9a13
-#define PCI_DEVICE_ID_INTEL_MAPLE_RIDGE_XHCI 0x1138
-#define PCI_DEVICE_ID_INTEL_ALDER_LAKE_PCH_XHCI 0x51ed
-#define PCI_DEVICE_ID_INTEL_ALDER_LAKE_N_PCH_XHCI 0x54ed
#define PCI_DEVICE_ID_AMD_RENOIR_XHCI 0x1639
#define PCI_DEVICE_ID_AMD_PROMONTORYA_4 0x43b9
@@ -74,6 +81,9 @@
#define PCI_DEVICE_ID_ASMEDIA_2142_XHCI 0x2142
#define PCI_DEVICE_ID_ASMEDIA_3242_XHCI 0x3242
+#define PCI_DEVICE_ID_CADENCE 0x17CD
+#define PCI_DEVICE_ID_CADENCE_SSP 0x0200
+
static const char hcd_name[] = "xhci_hcd";
static struct hc_driver __read_mostly xhci_pci_hc_driver;
@@ -89,6 +99,10 @@ static const struct xhci_driver_overrides xhci_pci_overrides __initconst = {
.update_hub_device = xhci_pci_update_hub_device,
};
+/*
+ * Primary Legacy and MSI IRQ are synced in suspend_common().
+ * All MSI-X IRQs and secondary MSI IRQs should be synced here.
+ */
static void xhci_msix_sync_irqs(struct xhci_hcd *xhci)
{
struct usb_hcd *hcd = xhci_to_hcd(xhci);
@@ -101,13 +115,12 @@ static void xhci_msix_sync_irqs(struct xhci_hcd *xhci)
}
}
-/* Free any IRQs and disable MSI-X */
+/* Legacy IRQ is freed by usb_remove_hcd() or usb_hcd_pci_shutdown() */
static void xhci_cleanup_msix(struct xhci_hcd *xhci)
{
struct usb_hcd *hcd = xhci_to_hcd(xhci);
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
- /* return if using legacy interrupt */
if (hcd->irq > 0)
return;
@@ -231,15 +244,6 @@ static int xhci_pci_reinit(struct xhci_hcd *xhci, struct pci_dev *pdev)
static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
{
struct pci_dev *pdev = to_pci_dev(dev);
- struct xhci_driver_data *driver_data;
- const struct pci_device_id *id;
-
- id = pci_match_id(to_pci_driver(pdev->dev.driver)->id_table, pdev);
-
- if (id && id->driver_data) {
- driver_data = (struct xhci_driver_data *)id->driver_data;
- xhci->quirks |= driver_data->quirks;
- }
/* Look for vendor-specific quirks */
if (pdev->vendor == PCI_VENDOR_ID_FRESCO_LOGIC &&
@@ -270,17 +274,12 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
"QUIRK: Fresco Logic revision %u "
"has broken MSI implementation",
pdev->revision);
- xhci->quirks |= XHCI_TRUST_TX_LENGTH;
}
if (pdev->vendor == PCI_VENDOR_ID_FRESCO_LOGIC &&
pdev->device == PCI_DEVICE_ID_FRESCO_LOGIC_FL1009)
xhci->quirks |= XHCI_BROKEN_STREAMS;
- if (pdev->vendor == PCI_VENDOR_ID_FRESCO_LOGIC &&
- pdev->device == PCI_DEVICE_ID_FRESCO_LOGIC_FL1100)
- xhci->quirks |= XHCI_TRUST_TX_LENGTH;
-
if (pdev->vendor == PCI_VENDOR_ID_NEC)
xhci->quirks |= XHCI_NEC_HOST;
@@ -307,11 +306,8 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
xhci->quirks |= XHCI_RESET_ON_RESUME;
}
- if (pdev->vendor == PCI_VENDOR_ID_AMD) {
- xhci->quirks |= XHCI_TRUST_TX_LENGTH;
- if (pdev->device == 0x43f7)
- xhci->quirks |= XHCI_DEFAULT_PM_RUNTIME_ALLOW;
- }
+ if (pdev->vendor == PCI_VENDOR_ID_AMD && pdev->device == 0x43f7)
+ xhci->quirks |= XHCI_DEFAULT_PM_RUNTIME_ALLOW;
if ((pdev->vendor == PCI_VENDOR_ID_AMD) &&
((pdev->device == PCI_DEVICE_ID_AMD_PROMONTORYA_4) ||
@@ -356,9 +352,9 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *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_APL_XHCI ||
- pdev->device == PCI_DEVICE_ID_INTEL_DNV_XHCI ||
- pdev->device == PCI_DEVICE_ID_INTEL_CML_XHCI)) {
+ pdev->device == PCI_DEVICE_ID_INTEL_APOLLO_LAKE_XHCI ||
+ pdev->device == PCI_DEVICE_ID_INTEL_DENVERTON_XHCI ||
+ pdev->device == PCI_DEVICE_ID_INTEL_COMET_LAKE_XHCI)) {
xhci->quirks |= XHCI_PME_STUCK_QUIRK;
}
if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
@@ -367,18 +363,19 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
(pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI ||
pdev->device == PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_XHCI ||
- pdev->device == PCI_DEVICE_ID_INTEL_APL_XHCI))
+ pdev->device == PCI_DEVICE_ID_INTEL_APOLLO_LAKE_XHCI))
xhci->quirks |= XHCI_INTEL_USB_ROLE_SW;
if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
(pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI ||
pdev->device == PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_XHCI ||
pdev->device == PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_XHCI ||
- pdev->device == PCI_DEVICE_ID_INTEL_APL_XHCI ||
- pdev->device == PCI_DEVICE_ID_INTEL_DNV_XHCI))
+ pdev->device == PCI_DEVICE_ID_INTEL_APOLLO_LAKE_XHCI ||
+ pdev->device == PCI_DEVICE_ID_INTEL_DENVERTON_XHCI))
xhci->quirks |= XHCI_MISSING_CAS;
if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
- (pdev->device == PCI_DEVICE_ID_INTEL_ALDER_LAKE_PCH_XHCI ||
+ (pdev->device == PCI_DEVICE_ID_INTEL_TIGER_LAKE_PCH_XHCI ||
+ pdev->device == PCI_DEVICE_ID_INTEL_ALDER_LAKE_PCH_XHCI ||
pdev->device == PCI_DEVICE_ID_INTEL_ALDER_LAKE_N_PCH_XHCI))
xhci->quirks |= XHCI_RESET_TO_DEFAULT;
@@ -399,12 +396,16 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
if (pdev->vendor == PCI_VENDOR_ID_ETRON &&
pdev->device == PCI_DEVICE_ID_EJ168) {
xhci->quirks |= XHCI_RESET_ON_RESUME;
- xhci->quirks |= XHCI_TRUST_TX_LENGTH;
xhci->quirks |= XHCI_BROKEN_STREAMS;
}
+ if (pdev->vendor == PCI_VENDOR_ID_ETRON &&
+ pdev->device == PCI_DEVICE_ID_EJ188) {
+ xhci->quirks |= XHCI_RESET_ON_RESUME;
+ xhci->quirks |= XHCI_BROKEN_STREAMS;
+ }
+
if (pdev->vendor == PCI_VENDOR_ID_RENESAS &&
pdev->device == 0x0014) {
- xhci->quirks |= XHCI_TRUST_TX_LENGTH;
xhci->quirks |= XHCI_ZERO_64B_REGS;
}
if (pdev->vendor == PCI_VENDOR_ID_RENESAS &&
@@ -415,6 +416,10 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
if (pdev->vendor == PCI_VENDOR_ID_VIA)
xhci->quirks |= XHCI_RESET_ON_RESUME;
+ if (pdev->vendor == PCI_VENDOR_ID_PHYTIUM &&
+ pdev->device == PCI_DEVICE_ID_PHYTIUM_XHCI)
+ xhci->quirks |= XHCI_RESET_ON_RESUME;
+
/* See https://bugzilla.kernel.org/show_bug.cgi?id=79511 */
if (pdev->vendor == PCI_VENDOR_ID_VIA &&
pdev->device == 0x3432)
@@ -434,7 +439,6 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
}
if (pdev->vendor == PCI_VENDOR_ID_ASMEDIA &&
pdev->device == PCI_DEVICE_ID_ASMEDIA_1042A_XHCI) {
- xhci->quirks |= XHCI_TRUST_TX_LENGTH;
xhci->quirks |= XHCI_NO_64BIT_SUPPORT;
}
if (pdev->vendor == PCI_VENDOR_ID_ASMEDIA &&
@@ -473,6 +477,10 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
xhci->quirks |= XHCI_ZHAOXIN_TRB_FETCH;
}
+ if (pdev->vendor == PCI_DEVICE_ID_CADENCE &&
+ pdev->device == PCI_DEVICE_ID_CADENCE_SSP)
+ xhci->quirks |= XHCI_CDNS_SCTX_QUIRK;
+
/* xHC spec requires PCI devices to support D3hot and D3cold */
if (xhci->hci_version >= 0x120)
xhci->quirks |= XHCI_DEFAULT_PM_RUNTIME_ALLOW;
@@ -534,10 +542,9 @@ static int xhci_pci_setup(struct usb_hcd *hcd)
struct xhci_hcd *xhci;
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
int retval;
+ u8 sbrn;
xhci = hcd_to_xhci(hcd);
- if (!xhci->sbrn)
- pci_read_config_byte(pdev, XHCI_SBRN_OFFSET, &xhci->sbrn);
/* imod_interval is the interrupt moderation value in nanoseconds. */
xhci->imod_interval = 40000;
@@ -552,7 +559,8 @@ static int xhci_pci_setup(struct usb_hcd *hcd)
if (xhci->quirks & XHCI_PME_STUCK_QUIRK)
xhci_pme_acpi_rtd3_enable(pdev);
- xhci_dbg(xhci, "Got SBRN %u\n", (unsigned int) xhci->sbrn);
+ pci_read_config_byte(pdev, XHCI_SBRN_OFFSET, &sbrn);
+ xhci_dbg(xhci, "Got SBRN %u\n", (unsigned int)sbrn);
/* Find any debug ports */
return xhci_pci_reinit(xhci, pdev);
@@ -572,21 +580,13 @@ static int xhci_pci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hd
* We need to register our own PCI probe function (instead of the USB core's
* function) in order to create a second roothub under xHCI.
*/
-static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
+int xhci_pci_common_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
int retval;
struct xhci_hcd *xhci;
struct usb_hcd *hcd;
- struct xhci_driver_data *driver_data;
struct reset_control *reset;
- driver_data = (struct xhci_driver_data *)id->driver_data;
- if (driver_data && driver_data->quirks & XHCI_RENESAS_FW_QUIRK) {
- retval = renesas_xhci_check_request_fw(dev, id);
- if (retval)
- return retval;
- }
-
reset = devm_reset_control_get_optional_exclusive(&dev->dev, NULL);
if (IS_ERR(reset))
return PTR_ERR(reset);
@@ -651,12 +651,30 @@ put_runtime_pm:
pm_runtime_put_noidle(&dev->dev);
return retval;
}
+EXPORT_SYMBOL_NS_GPL(xhci_pci_common_probe, xhci);
+
+static const struct pci_device_id pci_ids_reject[] = {
+ /* handled by xhci-pci-renesas */
+ { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, 0x0014) },
+ { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, 0x0015) },
+ { /* end: all zeroes */ }
+};
+
+static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+ if (pci_match_id(pci_ids_reject, dev))
+ return -ENODEV;
+
+ return xhci_pci_common_probe(dev, id);
+}
-static void xhci_pci_remove(struct pci_dev *dev)
+void xhci_pci_remove(struct pci_dev *dev)
{
struct xhci_hcd *xhci;
+ bool set_power_d3;
xhci = hcd_to_xhci(pci_get_drvdata(dev));
+ set_power_d3 = xhci->quirks & XHCI_SPURIOUS_WAKEUP;
xhci->xhc_state |= XHCI_STATE_REMOVING;
@@ -669,12 +687,13 @@ static void xhci_pci_remove(struct pci_dev *dev)
xhci->shared_hcd = NULL;
}
+ usb_hcd_pci_remove(dev);
+
/* Workaround for spurious wakeups at shutdown with HSW */
- if (xhci->quirks & XHCI_SPURIOUS_WAKEUP)
+ if (set_power_d3)
pci_set_power_state(dev, PCI_D3hot);
-
- usb_hcd_pci_remove(dev);
}
+EXPORT_SYMBOL_NS_GPL(xhci_pci_remove, xhci);
/*
* In some Intel xHCI controllers, in order to get D3 working,
@@ -783,7 +802,6 @@ static int xhci_pci_resume(struct usb_hcd *hcd, pm_message_t msg)
{
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
- int retval = 0;
reset_control_reset(xhci->reset);
@@ -814,8 +832,7 @@ static int xhci_pci_resume(struct usb_hcd *hcd, pm_message_t msg)
if (xhci->quirks & XHCI_PME_STUCK_QUIRK)
xhci_pme_quirk(hcd);
- retval = xhci_resume(xhci, msg);
- return retval;
+ return xhci_resume(xhci, msg);
}
static int xhci_pci_poweroff_late(struct usb_hcd *hcd, bool do_wakeup)
@@ -882,19 +899,8 @@ static void xhci_pci_shutdown(struct usb_hcd *hcd)
/*-------------------------------------------------------------------------*/
-static const struct xhci_driver_data reneses_data = {
- .quirks = XHCI_RENESAS_FW_QUIRK,
- .firmware = "renesas_usb_fw.mem",
-};
-
/* PCI driver selection metadata; PCI hotplugging uses this */
static const struct pci_device_id pci_ids[] = {
- { PCI_DEVICE(0x1912, 0x0014),
- .driver_data = (unsigned long)&reneses_data,
- },
- { PCI_DEVICE(0x1912, 0x0015),
- .driver_data = (unsigned long)&reneses_data,
- },
/* handle any USB 3.0 xHCI controller */
{ PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_XHCI, ~0),
},
@@ -902,14 +908,6 @@ static const struct pci_device_id pci_ids[] = {
};
MODULE_DEVICE_TABLE(pci, pci_ids);
-/*
- * Without CONFIG_USB_XHCI_PCI_RENESAS renesas_xhci_check_request_fw() won't
- * load firmware, so don't encumber the xhci-pci driver with it.
- */
-#if IS_ENABLED(CONFIG_USB_XHCI_PCI_RENESAS)
-MODULE_FIRMWARE("renesas_usb_fw.mem");
-#endif
-
/* pci driver glue; this is a "new style" PCI driver module */
static struct pci_driver xhci_pci_driver = {
.name = hcd_name,
diff --git a/drivers/usb/host/xhci-pci.h b/drivers/usb/host/xhci-pci.h
index cb9a8f331a44..e87c7d9d76b8 100644
--- a/drivers/usb/host/xhci-pci.h
+++ b/drivers/usb/host/xhci-pci.h
@@ -4,22 +4,7 @@
#ifndef XHCI_PCI_H
#define XHCI_PCI_H
-#if IS_ENABLED(CONFIG_USB_XHCI_PCI_RENESAS)
-int renesas_xhci_check_request_fw(struct pci_dev *dev,
- const struct pci_device_id *id);
-
-#else
-static int renesas_xhci_check_request_fw(struct pci_dev *dev,
- const struct pci_device_id *id)
-{
- return 0;
-}
-
-#endif
-
-struct xhci_driver_data {
- u64 quirks;
- const char *firmware;
-};
+int xhci_pci_common_probe(struct pci_dev *dev, const struct pci_device_id *id);
+void xhci_pci_remove(struct pci_dev *dev);
#endif
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index 3d071b875308..ecaa75718e59 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -256,6 +256,15 @@ int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, const s
if (device_property_read_bool(tmpdev, "xhci-sg-trb-cache-size-quirk"))
xhci->quirks |= XHCI_SG_TRB_CACHE_SIZE_QUIRK;
+ if (device_property_read_bool(tmpdev, "write-64-hi-lo-quirk"))
+ xhci->quirks |= XHCI_WRITE_64_HI_LO;
+
+ if (device_property_read_bool(tmpdev, "xhci-missing-cas-quirk"))
+ xhci->quirks |= XHCI_MISSING_CAS;
+
+ if (device_property_read_bool(tmpdev, "xhci-skip-phy-init-quirk"))
+ xhci->quirks |= XHCI_SKIP_PHY_INIT;
+
device_property_read_u32(tmpdev, "imod-interval-ns",
&xhci->imod_interval);
}
diff --git a/drivers/usb/host/xhci-rcar.c b/drivers/usb/host/xhci-rcar.c
index ab9c5969e462..8b357647728c 100644
--- a/drivers/usb/host/xhci-rcar.c
+++ b/drivers/usb/host/xhci-rcar.c
@@ -214,8 +214,7 @@ static int xhci_rcar_resume_quirk(struct usb_hcd *hcd)
*/
#define SET_XHCI_PLAT_PRIV_FOR_RCAR(firmware) \
.firmware_name = firmware, \
- .quirks = XHCI_NO_64BIT_SUPPORT | XHCI_TRUST_TX_LENGTH | \
- XHCI_SLOW_SUSPEND, \
+ .quirks = XHCI_NO_64BIT_SUPPORT | XHCI_SLOW_SUSPEND, \
.init_quirk = xhci_rcar_init_quirk, \
.plat_start = xhci_rcar_start, \
.resume_quirk = xhci_rcar_resume_quirk,
@@ -229,8 +228,7 @@ static const struct xhci_plat_priv xhci_plat_renesas_rcar_gen3 = {
};
static const struct xhci_plat_priv xhci_plat_renesas_rzv2m = {
- .quirks = XHCI_NO_64BIT_SUPPORT | XHCI_TRUST_TX_LENGTH |
- XHCI_SLOW_SUSPEND,
+ .quirks = XHCI_NO_64BIT_SUPPORT | XHCI_SLOW_SUSPEND,
.init_quirk = xhci_rzv2m_init_quirk,
.plat_start = xhci_rzv2m_start,
};
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 575f0fd9c9f1..4d664ba53fe9 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -250,9 +250,7 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring,
* AMD 0.96 host, carry over the chain bit of the previous TRB
* (which may mean the chain bit is cleared).
*/
- if (!(ring->type == TYPE_ISOC &&
- (xhci->quirks & XHCI_AMD_0x96_HOST)) &&
- !xhci_link_trb_quirk(xhci)) {
+ if (!xhci_link_chain_quirk(xhci, ring->type)) {
next->link.control &= cpu_to_le32(~TRB_CHAIN);
next->link.control |= cpu_to_le32(chain);
}
@@ -283,7 +281,7 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring,
* Only for transfer and command rings where driver is the producer, not for
* event rings.
*/
-static unsigned int xhci_num_trbs_free(struct xhci_hcd *xhci, struct xhci_ring *ring)
+static unsigned int xhci_num_trbs_free(struct xhci_ring *ring)
{
struct xhci_segment *enq_seg = ring->enq_seg;
union xhci_trb *enq = ring->enqueue;
@@ -308,7 +306,7 @@ static unsigned int xhci_num_trbs_free(struct xhci_hcd *xhci, struct xhci_ring *
free += last_on_seg - enq;
enq_seg = enq_seg->next;
enq = enq_seg->trbs;
- } while (i++ <= ring->num_segs);
+ } while (i++ < ring->num_segs);
return free;
}
@@ -351,10 +349,8 @@ static unsigned int xhci_ring_expansion_needed(struct xhci_hcd *xhci, struct xhc
while (new_segs > 0) {
seg = seg->next;
if (seg == ring->deq_seg) {
- xhci_dbg(xhci, "Ring expansion by %d segments needed\n",
- new_segs);
- xhci_dbg(xhci, "Adding %d trbs moves enq %d trbs into deq seg\n",
- num_trbs, trbs_past_seg % TRBS_PER_SEGMENT);
+ xhci_dbg(xhci, "Adding %d trbs requires expanding ring by %d segments\n",
+ num_trbs, new_segs);
return new_segs;
}
new_segs--;
@@ -656,25 +652,6 @@ static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci,
stream_id);
return -ENODEV;
}
- /*
- * A cancelled TD can complete with a stall if HW cached the trb.
- * In this case driver can't find td, but if the ring is empty we
- * can move the dequeue pointer to the current enqueue position.
- * We shouldn't hit this anymore as cached cancelled TRBs are given back
- * after clearing the cache, but be on the safe side and keep it anyway
- */
- if (!td) {
- if (list_empty(&ep_ring->td_list)) {
- new_seg = ep_ring->enq_seg;
- new_deq = ep_ring->enqueue;
- new_cycle = ep_ring->cycle_state;
- xhci_dbg(xhci, "ep ring empty, Set new dequeue = enqueue");
- goto deq_found;
- } else {
- xhci_warn(xhci, "Can't find new dequeue state, missing td\n");
- return -EINVAL;
- }
- }
hw_dequeue = xhci_get_hw_deq(xhci, dev, ep_index, stream_id);
new_seg = ep_ring->deq_seg;
@@ -711,8 +688,6 @@ static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci,
} while (!cycle_found || !td_last_trb_found);
-deq_found:
-
/* Don't update the ring cycle state for the producer (us). */
addr = xhci_trb_virt_to_dma(new_seg, new_deq);
if (addr == 0) {
@@ -740,7 +715,7 @@ deq_found:
lower_32_bits(addr) | trb_sct | new_cycle,
upper_32_bits(addr),
STREAM_ID_FOR_TRB(stream_id), SLOT_ID_FOR_TRB(slot_id) |
- EP_ID_FOR_TRB(ep_index) | TRB_TYPE(TRB_SET_DEQ), false);
+ EP_INDEX_FOR_TRB(ep_index) | TRB_TYPE(TRB_SET_DEQ), false);
if (ret < 0) {
xhci_free_command(xhci, cmd);
return ret;
@@ -785,10 +760,6 @@ static void td_to_noop(struct xhci_hcd *xhci, struct xhci_ring *ep_ring,
}
}
-/*
- * 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)
{
@@ -1026,21 +997,34 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep)
td->urb->stream_id);
hw_deq &= ~0xf;
- if (td->cancel_status == TD_HALTED ||
- trb_in_td(xhci, td->start_seg, td->first_trb, td->last_trb, hw_deq, false)) {
+ if (td->cancel_status == TD_HALTED || trb_in_td(xhci, td, hw_deq, false)) {
switch (td->cancel_status) {
case TD_CLEARED: /* TD is already no-op */
case TD_CLEARING_CACHE: /* set TR deq command already queued */
break;
case TD_DIRTY: /* TD is cached, clear it */
case TD_HALTED:
+ case TD_CLEARING_CACHE_DEFERRED:
+ if (cached_td) {
+ if (cached_td->urb->stream_id != td->urb->stream_id) {
+ /* Multiple streams case, defer move dq */
+ xhci_dbg(xhci,
+ "Move dq deferred: stream %u URB %p\n",
+ td->urb->stream_id, td->urb);
+ td->cancel_status = TD_CLEARING_CACHE_DEFERRED;
+ break;
+ }
+
+ /* Should never happen, but clear the TD if it does */
+ xhci_warn(xhci,
+ "Found multiple active URBs %p and %p in stream %u?\n",
+ td->urb, cached_td->urb,
+ td->urb->stream_id);
+ td_to_noop(xhci, ring, cached_td, false);
+ cached_td->cancel_status = TD_CLEARED;
+ }
+
td->cancel_status = TD_CLEARING_CACHE;
- if (cached_td)
- /* FIXME stream case, several stopped rings */
- xhci_dbg(xhci,
- "Move dq past stream %u URB %p instead of stream %u URB %p\n",
- td->urb->stream_id, td->urb,
- cached_td->urb->stream_id, cached_td->urb);
cached_td = td;
break;
}
@@ -1060,10 +1044,16 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep)
if (err) {
/* Failed to move past cached td, just set cached TDs to no-op */
list_for_each_entry_safe(td, tmp_td, &ep->cancelled_td_list, cancelled_td_list) {
- if (td->cancel_status != TD_CLEARING_CACHE)
+ /*
+ * Deferred TDs need to have the deq pointer set after the above command
+ * completes, so if that failed we just give up on all of them (and
+ * complain loudly since this could cause issues due to caching).
+ */
+ if (td->cancel_status != TD_CLEARING_CACHE &&
+ td->cancel_status != TD_CLEARING_CACHE_DEFERRED)
continue;
- xhci_dbg(xhci, "Failed to clear cancelled cached URB %p, mark clear anyway\n",
- td->urb);
+ xhci_warn(xhci, "Failed to clear cancelled cached URB %p, mark clear anyway\n",
+ td->urb);
td_to_noop(xhci, ring, td, false);
td->cancel_status = TD_CLEARED;
}
@@ -1084,8 +1074,7 @@ static struct xhci_td *find_halted_td(struct xhci_virt_ep *ep)
hw_deq = xhci_get_hw_deq(ep->xhci, ep->vdev, ep->ep_index, 0);
hw_deq &= ~0xf;
td = list_first_entry(&ep->ring->td_list, struct xhci_td, td_list);
- if (trb_in_td(ep->xhci, td->start_seg, td->first_trb,
- td->last_trb, hw_deq, false))
+ if (trb_in_td(ep->xhci, td, hw_deq, false))
return td;
}
return NULL;
@@ -1350,6 +1339,7 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id,
struct xhci_ep_ctx *ep_ctx;
struct xhci_slot_ctx *slot_ctx;
struct xhci_td *td, *tmp_td;
+ bool deferred = false;
ep_index = TRB_TO_EP_INDEX(le32_to_cpu(trb->generic.field[3]));
stream_id = TRB_TO_STREAM_ID(le32_to_cpu(trb->generic.field[2]));
@@ -1409,6 +1399,20 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id,
struct xhci_stream_ctx *ctx =
&ep->stream_info->stream_ctx_array[stream_id];
deq = le64_to_cpu(ctx->stream_ring) & SCTX_DEQ_MASK;
+
+ /*
+ * Cadence xHCI controllers store some endpoint state
+ * information within Rsvd0 fields of Stream Endpoint
+ * context. This field is not cleared during Set TR
+ * Dequeue Pointer command which causes XDMA to skip
+ * over transfer ring and leads to data loss on stream
+ * pipe.
+ * To fix this issue driver must clear Rsvd0 field.
+ */
+ if (xhci->quirks & XHCI_CDNS_SCTX_QUIRK) {
+ ctx->reserved[0] = 0;
+ ctx->reserved[1] = 0;
+ }
} else {
deq = le64_to_cpu(ep_ctx->deq) & ~EP_CTX_CYCLE_MASK;
}
@@ -1436,6 +1440,8 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id,
xhci_dbg(ep->xhci, "%s: Giveback cancelled URB %p TD\n",
__func__, td->urb);
xhci_td_cleanup(ep->xhci, td, ep_ring, td->status);
+ } else if (td->cancel_status == TD_CLEARING_CACHE_DEFERRED) {
+ deferred = true;
} else {
xhci_dbg(ep->xhci, "%s: Keep cancelled URB %p TD as cancel_status is %d\n",
__func__, td->urb, td->cancel_status);
@@ -1445,8 +1451,17 @@ cleanup:
ep->ep_state &= ~SET_DEQ_PENDING;
ep->queued_deq_seg = NULL;
ep->queued_deq_ptr = NULL;
- /* Restart any rings with pending URBs */
- ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
+
+ if (deferred) {
+ /* We have more streams to clear */
+ xhci_dbg(ep->xhci, "%s: Pending TDs to clear, continuing with invalidation\n",
+ __func__);
+ xhci_invalidate_cancelled_tds(ep);
+ } else {
+ /* Restart any rings with pending URBs */
+ xhci_dbg(ep->xhci, "%s: All TDs cleared, ring doorbell\n", __func__);
+ ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
+ }
}
static void xhci_handle_cmd_reset_ep(struct xhci_hcd *xhci, int slot_id,
@@ -1483,8 +1498,8 @@ static void xhci_handle_cmd_reset_ep(struct xhci_hcd *xhci, int slot_id,
ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
}
-static void xhci_handle_cmd_enable_slot(struct xhci_hcd *xhci, int slot_id,
- struct xhci_command *command, u32 cmd_comp_code)
+static void xhci_handle_cmd_enable_slot(int slot_id, struct xhci_command *command,
+ u32 cmd_comp_code)
{
if (cmd_comp_code == COMP_SUCCESS)
command->slot_id = slot_id;
@@ -1509,8 +1524,7 @@ static void xhci_handle_cmd_disable_slot(struct xhci_hcd *xhci, int slot_id)
xhci_free_device_endpoint_resources(xhci, virt_dev, true);
}
-static void xhci_handle_cmd_config_ep(struct xhci_hcd *xhci, int slot_id,
- u32 cmd_comp_code)
+static void xhci_handle_cmd_config_ep(struct xhci_hcd *xhci, int slot_id)
{
struct xhci_virt_device *virt_dev;
struct xhci_input_control_ctx *ctrl_ctx;
@@ -1752,14 +1766,14 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
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, cmd_comp_code);
+ xhci_handle_cmd_enable_slot(slot_id, cmd, cmd_comp_code);
break;
case TRB_DISABLE_SLOT:
xhci_handle_cmd_disable_slot(xhci, slot_id);
break;
case TRB_CONFIG_EP:
if (!cmd->completion)
- xhci_handle_cmd_config_ep(xhci, slot_id, cmd_comp_code);
+ xhci_handle_cmd_config_ep(xhci, slot_id);
break;
case TRB_EVAL_CONTEXT:
break;
@@ -1877,9 +1891,7 @@ static void xhci_cavium_reset_phy_quirk(struct xhci_hcd *xhci)
} while (!(pll_lock_check & 0x1) && --retry_count);
}
-static void handle_port_status(struct xhci_hcd *xhci,
- struct xhci_interrupter *ir,
- union xhci_trb *event)
+static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event)
{
struct usb_hcd *hcd;
u32 port_id;
@@ -2051,25 +2063,19 @@ cleanup:
}
/*
- * This TD is defined by the TRBs starting at start_trb in start_seg and ending
- * at end_trb, which may be in another segment. If the suspect DMA address is a
- * TRB in this TD, this function returns that TRB's segment. Otherwise it
- * returns 0.
+ * If the suspect DMA address is a TRB in this TD, this function returns that
+ * TRB's segment. Otherwise it returns 0.
*/
-struct xhci_segment *trb_in_td(struct xhci_hcd *xhci,
- struct xhci_segment *start_seg,
- union xhci_trb *start_trb,
- union xhci_trb *end_trb,
- dma_addr_t suspect_dma,
- bool debug)
+struct xhci_segment *trb_in_td(struct xhci_hcd *xhci, struct xhci_td *td, dma_addr_t suspect_dma,
+ bool debug)
{
dma_addr_t start_dma;
dma_addr_t end_seg_dma;
dma_addr_t end_trb_dma;
struct xhci_segment *cur_seg;
- start_dma = xhci_trb_virt_to_dma(start_seg, start_trb);
- cur_seg = start_seg;
+ start_dma = xhci_trb_virt_to_dma(td->start_seg, td->first_trb);
+ cur_seg = td->start_seg;
do {
if (start_dma == 0)
@@ -2078,7 +2084,7 @@ struct xhci_segment *trb_in_td(struct xhci_hcd *xhci,
end_seg_dma = xhci_trb_virt_to_dma(cur_seg,
&cur_seg->trbs[TRBS_PER_SEGMENT - 1]);
/* If the end TRB isn't in this segment, this is set to 0 */
- end_trb_dma = xhci_trb_virt_to_dma(cur_seg, end_trb);
+ end_trb_dma = xhci_trb_virt_to_dma(cur_seg, td->last_trb);
if (debug)
xhci_warn(xhci,
@@ -2112,7 +2118,7 @@ struct xhci_segment *trb_in_td(struct xhci_hcd *xhci,
}
cur_seg = cur_seg->next;
start_dma = xhci_trb_virt_to_dma(cur_seg, &cur_seg->trbs[0]);
- } while (cur_seg != start_seg);
+ } while (cur_seg != td->start_seg);
return NULL;
}
@@ -2134,30 +2140,34 @@ static void xhci_clear_hub_tt_buffer(struct xhci_hcd *xhci, struct xhci_td *td,
}
}
-/* Check if an error has halted the endpoint ring. The class driver will
- * cleanup the halt for a non-default control endpoint if we indicate a stall.
- * However, a babble and other errors also halt the endpoint ring, and the class
- * driver won't clear the halt in that case, so we need to issue a Set Transfer
- * Ring Dequeue Pointer command manually.
+/*
+ * Check if xhci internal endpoint state has gone to a "halt" state due to an
+ * error or stall, including default control pipe protocol stall.
+ * The internal halt needs to be cleared with a reset endpoint command.
+ *
+ * External device side is also halted in functional stall cases. Class driver
+ * will clear the device halt with a CLEAR_FEATURE(ENDPOINT_HALT) request later.
*/
-static int xhci_requires_manual_halt_cleanup(struct xhci_hcd *xhci,
- struct xhci_ep_ctx *ep_ctx,
- unsigned int trb_comp_code)
+static bool xhci_halted_host_endpoint(struct xhci_ep_ctx *ep_ctx, unsigned int comp_code)
{
- /* TRB completion codes that may require a manual halt cleanup */
- 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.
+ /* Stall halts both internal and device side endpoint */
+ if (comp_code == COMP_STALL_ERROR)
+ return true;
+
+ /* TRB completion codes that may require internal halt cleanup */
+ if (comp_code == COMP_USB_TRANSACTION_ERROR ||
+ comp_code == COMP_BABBLE_DETECTED_ERROR ||
+ comp_code == COMP_SPLIT_TRANSACTION_ERROR)
+ /*
+ * The 0.95 spec says a babbling control endpoint is not halted.
+ * The 0.96 spec says it is. Some HW claims to be 0.95
+ * compliant, but it halts the control endpoint anyway.
+ * Check endpoint context if endpoint is halted.
*/
if (GET_EP_CTX_STATE(ep_ctx) == EP_STATE_HALTED)
- return 1;
+ return true;
- return 0;
+ return false;
}
int xhci_is_vendor_info_code(struct xhci_hcd *xhci, unsigned int trb_comp_code)
@@ -2327,8 +2337,7 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
case COMP_STOPPED_LENGTH_INVALID:
goto finish_td;
default:
- if (!xhci_requires_manual_halt_cleanup(xhci,
- ep_ctx, trb_comp_code))
+ if (!xhci_halted_host_endpoint(ep_ctx, trb_comp_code))
break;
xhci_dbg(xhci, "TRB error %u, halted endpoint index = %u\n",
trb_comp_code, ep->ep_index);
@@ -2399,8 +2408,7 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
break;
if (remaining) {
frame->status = short_framestatus;
- if (xhci->quirks & XHCI_TRUST_TX_LENGTH)
- sum_trbs_for_length = true;
+ sum_trbs_for_length = true;
break;
}
frame->status = 0;
@@ -2439,7 +2447,9 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
requested = remaining;
break;
case COMP_STOPPED_LENGTH_INVALID:
- requested = 0;
+ /* exclude stopped trb with invalid length from length sum */
+ sum_trbs_for_length = true;
+ ep_trb_len = 0;
remaining = 0;
break;
default:
@@ -2525,9 +2535,6 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
td->status = 0;
break;
case COMP_SHORT_PACKET:
- xhci_dbg(xhci, "ep %#x - asked for %d bytes, %d bytes untransferred\n",
- td->urb->ep->desc.bEndpointAddress,
- requested, remaining);
td->status = 0;
break;
case COMP_STOPPED_SHORT_PACKET:
@@ -2535,9 +2542,8 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
goto finish_td;
case COMP_STOPPED_LENGTH_INVALID:
/* stopped on ep trb with invalid length, exclude it */
- ep_trb_len = 0;
- remaining = 0;
- break;
+ td->urb->actual_length = sum_trb_lengths(xhci, ep_ring, ep_trb);
+ goto finish_td;
case COMP_USB_TRANSACTION_ERROR:
if (xhci->quirks & XHCI_NO_SOFT_RETRY ||
(ep->err_count++ > MAX_SOFT_RETRY) ||
@@ -2569,6 +2575,33 @@ finish_td:
return finish_td(xhci, ep, ep_ring, td, trb_comp_code);
}
+/* Transfer events which don't point to a transfer TRB, see xhci 4.17.4 */
+static int handle_transferless_tx_event(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
+ u32 trb_comp_code)
+{
+ switch (trb_comp_code) {
+ case COMP_STALL_ERROR:
+ case COMP_USB_TRANSACTION_ERROR:
+ case COMP_INVALID_STREAM_TYPE_ERROR:
+ case COMP_INVALID_STREAM_ID_ERROR:
+ xhci_dbg(xhci, "Stream transaction error ep %u no id\n", ep->ep_index);
+ if (ep->err_count++ > MAX_SOFT_RETRY)
+ xhci_handle_halted_endpoint(xhci, ep, NULL, EP_HARD_RESET);
+ else
+ xhci_handle_halted_endpoint(xhci, ep, NULL, EP_SOFT_RESET);
+ break;
+ case COMP_RING_UNDERRUN:
+ case COMP_RING_OVERRUN:
+ case COMP_STOPPED_LENGTH_INVALID:
+ break;
+ default:
+ xhci_err(xhci, "Transfer event %u for unknown stream ring slot %u ep %u\n",
+ trb_comp_code, ep->vdev->slot_id, ep->ep_index);
+ return -ENODEV;
+ }
+ return 0;
+}
+
/*
* If this function returns an error condition, it means it got a Transfer
* event with a corrupted Slot ID, Endpoint ID, or TRB DMA address.
@@ -2589,8 +2622,6 @@ static int handle_tx_event(struct xhci_hcd *xhci,
int status = -EINPROGRESS;
struct xhci_ep_ctx *ep_ctx;
u32 trb_comp_code;
- int td_num = 0;
- bool handling_skipped_tds = false;
slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags));
ep_index = TRB_TO_EP_ID(le32_to_cpu(event->flags)) - 1;
@@ -2613,36 +2644,8 @@ static int handle_tx_event(struct xhci_hcd *xhci,
goto err_out;
}
- /* Some transfer events don't always point to a trb, see xhci 4.17.4 */
- if (!ep_ring) {
- switch (trb_comp_code) {
- case COMP_STALL_ERROR:
- case COMP_USB_TRANSACTION_ERROR:
- case COMP_INVALID_STREAM_TYPE_ERROR:
- case COMP_INVALID_STREAM_ID_ERROR:
- xhci_dbg(xhci, "Stream transaction error ep %u no id\n",
- ep_index);
- if (ep->err_count++ > MAX_SOFT_RETRY)
- xhci_handle_halted_endpoint(xhci, ep, NULL,
- EP_HARD_RESET);
- else
- xhci_handle_halted_endpoint(xhci, ep, NULL,
- EP_SOFT_RESET);
- goto cleanup;
- case COMP_RING_UNDERRUN:
- case COMP_RING_OVERRUN:
- case COMP_STOPPED_LENGTH_INVALID:
- goto cleanup;
- default:
- xhci_err(xhci, "ERROR Transfer event for unknown stream ring slot %u ep %u\n",
- slot_id, ep_index);
- goto err_out;
- }
- }
-
- /* Count current td numbers if ep->skip is set */
- if (ep->skip)
- td_num += list_count_nodes(&ep_ring->td_list);
+ if (!ep_ring)
+ return handle_transferless_tx_event(xhci, ep, trb_comp_code);
/* Look for common error cases */
switch (trb_comp_code) {
@@ -2650,15 +2653,11 @@ static int handle_tx_event(struct xhci_hcd *xhci,
* transfer type
*/
case COMP_SUCCESS:
- if (EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)) == 0)
- break;
- if (xhci->quirks & XHCI_TRUST_TX_LENGTH ||
- ep_ring->last_td_was_short)
+ if (EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)) != 0) {
trb_comp_code = COMP_SHORT_PACKET;
- else
- xhci_warn_ratelimited(xhci,
- "WARN Successful completion on short TX for slot %u ep %u: needs XHCI_TRUST_TX_LENGTH quirk?\n",
- slot_id, ep_index);
+ xhci_dbg(xhci, "Successful completion on short TX for slot %u ep %u with last td short %d\n",
+ slot_id, ep_index, ep_ring->last_td_was_short);
+ }
break;
case COMP_SHORT_PACKET:
break;
@@ -2728,21 +2727,15 @@ static int handle_tx_event(struct xhci_hcd *xhci,
* a Ring Overrun Event for IN Isoch endpoint or Ring
* Underrun Event for OUT Isoch endpoint.
*/
- xhci_dbg(xhci, "underrun event on endpoint\n");
- if (!list_empty(&ep_ring->td_list))
- xhci_dbg(xhci, "Underrun Event for slot %d ep %d "
- "still with TDs queued?\n",
- TRB_TO_SLOT_ID(le32_to_cpu(event->flags)),
- ep_index);
- goto cleanup;
+ xhci_dbg(xhci, "Underrun event on slot %u ep %u\n", slot_id, ep_index);
+ if (ep->skip)
+ break;
+ return 0;
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 "
- "still with TDs queued?\n",
- TRB_TO_SLOT_ID(le32_to_cpu(event->flags)),
- ep_index);
- goto cleanup;
+ xhci_dbg(xhci, "Overrun event on slot %u ep %u\n", slot_id, ep_index);
+ if (ep->skip)
+ break;
+ return 0;
case COMP_MISSED_SERVICE_ERROR:
/*
* When encounter missed service error, one or more isoc tds
@@ -2754,13 +2747,13 @@ static int handle_tx_event(struct xhci_hcd *xhci,
xhci_dbg(xhci,
"Miss service interval error for slot %u ep %u, set skip flag\n",
slot_id, ep_index);
- goto cleanup;
+ return 0;
case COMP_NO_PING_RESPONSE_ERROR:
ep->skip = true;
xhci_dbg(xhci,
"No Ping response error for slot %u ep %u, Skip one Isoc TD\n",
slot_id, ep_index);
- goto cleanup;
+ return 0;
case COMP_INCOMPATIBLE_DEVICE_ERROR:
/* needs disable slot command to recover */
@@ -2777,78 +2770,60 @@ static int handle_tx_event(struct xhci_hcd *xhci,
xhci_warn(xhci,
"ERROR Unknown event condition %u for slot %u ep %u , HC probably busted\n",
trb_comp_code, slot_id, ep_index);
- goto cleanup;
+ if (ep->skip)
+ break;
+ return 0;
}
- do {
- /* This TRB should be in the TD at the head of this ring's
- * TD list.
+ if (list_empty(&ep_ring->td_list)) {
+ /*
+ * Don't print wanings if ring is empty due to a stopped endpoint generating an
+ * extra completion event if the device was suspended. Or, a event for the last TRB
+ * of a short TD we already got a short event for. The short TD is already removed
+ * from the TD list.
*/
- if (list_empty(&ep_ring->td_list)) {
- /*
- * Don't print wanings if it's due to a stopped endpoint
- * generating an extra completion event if the device
- * was suspended. Or, a event for the last TRB of a
- * short TD we already got a short event for.
- * The short TD is already removed from the TD list.
- */
-
- if (!(trb_comp_code == COMP_STOPPED ||
- trb_comp_code == COMP_STOPPED_LENGTH_INVALID ||
- ep_ring->last_td_was_short)) {
- 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);
- }
- if (ep->skip) {
- ep->skip = false;
- xhci_dbg(xhci, "td_list is empty while skip flag set. Clear skip flag for slot %u ep %u.\n",
- slot_id, ep_index);
- }
- if (trb_comp_code == COMP_STALL_ERROR ||
- xhci_requires_manual_halt_cleanup(xhci, ep_ctx,
- trb_comp_code)) {
- xhci_handle_halted_endpoint(xhci, ep, NULL,
- EP_HARD_RESET);
- }
- goto cleanup;
+ if (trb_comp_code != COMP_STOPPED &&
+ trb_comp_code != COMP_STOPPED_LENGTH_INVALID &&
+ !ep_ring->last_td_was_short) {
+ xhci_warn(xhci, "Event TRB for slot %u ep %u with no TDs queued\n",
+ slot_id, ep_index);
}
- /* We've skipped all the TDs on the ep ring when ep->skip set */
- if (ep->skip && td_num == 0) {
- ep->skip = false;
- xhci_dbg(xhci, "All tds on the ep_ring skipped. Clear skip flag for slot %u ep %u.\n",
- slot_id, ep_index);
- goto cleanup;
- }
+ ep->skip = false;
+ goto check_endpoint_halted;
+ }
+ do {
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? */
- ep_seg = trb_in_td(xhci, td->start_seg, td->first_trb,
- td->last_trb, ep_trb_dma, false);
-
- /*
- * Skip the Force Stopped Event. The event_trb(event_dma) of FSE
- * is not in the current TD pointed by ep_ring->dequeue because
- * that the hardware dequeue pointer still at the previous TRB
- * of the current TD. The previous TRB maybe a Link TD or the
- * last TRB of the previous TD. The command completion handle
- * will take care the rest.
- */
- if (!ep_seg && (trb_comp_code == COMP_STOPPED ||
- trb_comp_code == COMP_STOPPED_LENGTH_INVALID)) {
- goto cleanup;
- }
+ ep_seg = trb_in_td(xhci, td, ep_trb_dma, false);
if (!ep_seg) {
if (ep->skip && usb_endpoint_xfer_isoc(&td->urb->ep->desc)) {
skip_isoc_td(xhci, td, ep, status);
- goto cleanup;
+ if (!list_empty(&ep_ring->td_list))
+ continue;
+
+ xhci_dbg(xhci, "All TDs skipped for slot %u ep %u. Clear skip flag.\n",
+ slot_id, ep_index);
+ ep->skip = false;
+ td = NULL;
+ goto check_endpoint_halted;
+ }
+
+ /*
+ * Skip the Force Stopped Event. The 'ep_trb' of FSE is not in the current
+ * TD pointed by 'ep_ring->dequeue' because that the hardware dequeue
+ * pointer still at the previous TRB of the current TD. The previous TRB
+ * maybe a Link TD or the last TRB of the previous TD. The command
+ * completion handle will take care the rest.
+ */
+ if (trb_comp_code == COMP_STOPPED ||
+ trb_comp_code == COMP_STOPPED_LENGTH_INVALID) {
+ return 0;
}
/*
@@ -2858,7 +2833,7 @@ 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;
- goto cleanup;
+ return 0;
}
/*
@@ -2876,8 +2851,7 @@ static int handle_tx_event(struct xhci_hcd *xhci,
!list_is_last(&td->td_list, &ep_ring->td_list)) {
struct xhci_td *td_next = list_next_entry(td, td_list);
- ep_seg = trb_in_td(xhci, td_next->start_seg, td_next->first_trb,
- td_next->last_trb, ep_trb_dma, false);
+ ep_seg = trb_in_td(xhci, td_next, ep_trb_dma, false);
if (ep_seg) {
/* give back previous TD, start handling new */
xhci_dbg(xhci, "Missing TD completion event after mid TD error\n");
@@ -2896,15 +2870,11 @@ static int handle_tx_event(struct xhci_hcd *xhci,
"part of current TD ep_index %d "
"comp_code %u\n", ep_index,
trb_comp_code);
- trb_in_td(xhci, td->start_seg, td->first_trb,
- td->last_trb, ep_trb_dma, true);
+ trb_in_td(xhci, td, ep_trb_dma, true);
+
return -ESHUTDOWN;
}
}
- if (trb_comp_code == COMP_SHORT_PACKET)
- ep_ring->last_td_was_short = true;
- else
- ep_ring->last_td_was_short = false;
if (ep->skip) {
xhci_dbg(xhci,
@@ -2913,50 +2883,46 @@ static int handle_tx_event(struct xhci_hcd *xhci,
ep->skip = false;
}
- 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 could trigger interrupts in a case where
- * a URB was killed and a STALL_ERROR happens right
- * after the endpoint ring stopped. Reset the halted
- * endpoint. Otherwise, the endpoint remains stalled
- * indefinitely.
- */
-
- if (trb_is_noop(ep_trb)) {
- if (trb_comp_code == COMP_STALL_ERROR ||
- xhci_requires_manual_halt_cleanup(xhci, ep_ctx,
- trb_comp_code))
- xhci_handle_halted_endpoint(xhci, ep, td,
- EP_HARD_RESET);
- goto cleanup;
- }
-
- td->status = status;
-
- /* update the urb's actual_length and give back to the core */
- if (usb_endpoint_xfer_control(&td->urb->ep->desc))
- process_ctrl_td(xhci, ep, ep_ring, td, ep_trb, event);
- else if (usb_endpoint_xfer_isoc(&td->urb->ep->desc))
- process_isoc_td(xhci, ep, ep_ring, td, ep_trb, event);
- else
- process_bulk_intr_td(xhci, ep, ep_ring, td, ep_trb, event);
-cleanup:
- handling_skipped_tds = ep->skip &&
- trb_comp_code != COMP_MISSED_SERVICE_ERROR &&
- trb_comp_code != COMP_NO_PING_RESPONSE_ERROR;
-
/*
* If ep->skip is set, it means there are missed tds on the
* endpoint ring need to take care of.
* Process them as short transfer until reach the td pointed by
* the event.
*/
- } while (handling_skipped_tds);
+ } while (ep->skip);
+
+ if (trb_comp_code == COMP_SHORT_PACKET)
+ ep_ring->last_td_was_short = true;
+ else
+ ep_ring->last_td_was_short = false;
+
+ ep_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 could trigger interrupts in a case where a URB was killed
+ * and a STALL_ERROR happens right after the endpoint ring stopped.
+ * Reset the halted endpoint. Otherwise, the endpoint remains stalled
+ * indefinitely.
+ */
+
+ if (trb_is_noop(ep_trb))
+ goto check_endpoint_halted;
+
+ td->status = status;
+
+ /* update the urb's actual_length and give back to the core */
+ if (usb_endpoint_xfer_control(&td->urb->ep->desc))
+ process_ctrl_td(xhci, ep, ep_ring, td, ep_trb, event);
+ else if (usb_endpoint_xfer_isoc(&td->urb->ep->desc))
+ process_isoc_td(xhci, ep, ep_ring, td, ep_trb, event);
+ else
+ process_bulk_intr_td(xhci, ep, ep_ring, td, ep_trb, event);
+ return 0;
+
+check_endpoint_halted:
+ if (xhci_halted_host_endpoint(ep_ctx, trb_comp_code))
+ xhci_handle_halted_endpoint(xhci, ep, td, EP_HARD_RESET);
return 0;
@@ -2996,7 +2962,7 @@ static int xhci_handle_event_trb(struct xhci_hcd *xhci, struct xhci_interrupter
handle_cmd_completion(xhci, &event->event_cmd);
break;
case TRB_PORT_STATUS:
- handle_port_status(xhci, ir, event);
+ handle_port_status(xhci, event);
break;
case TRB_TRANSFER:
handle_tx_event(xhci, ir, &event->trans_event);
@@ -3056,8 +3022,7 @@ static void xhci_update_erst_dequeue(struct xhci_hcd *xhci,
}
/* Clear the interrupt pending bit for a specific interrupter. */
-static void xhci_clear_interrupt_pending(struct xhci_hcd *xhci,
- struct xhci_interrupter *ir)
+static void xhci_clear_interrupt_pending(struct xhci_interrupter *ir)
{
if (!ir->ip_autoclear) {
u32 irq_pending;
@@ -3078,7 +3043,7 @@ static int xhci_handle_events(struct xhci_hcd *xhci, struct xhci_interrupter *ir
int err;
u64 temp;
- xhci_clear_interrupt_pending(xhci, ir);
+ xhci_clear_interrupt_pending(ir);
/* Event ring hasn't been allocated yet. */
if (!ir->event_ring || !ir->event_ring->dequeue) {
@@ -3251,7 +3216,7 @@ static int prepare_ring(struct xhci_hcd *xhci, struct xhci_ring *ep_ring,
if (ep_ring != xhci->cmd_ring) {
new_segs = xhci_ring_expansion_needed(xhci, ep_ring, num_trbs);
- } else if (xhci_num_trbs_free(xhci, ep_ring) <= num_trbs) {
+ } else if (xhci_num_trbs_free(ep_ring) <= num_trbs) {
xhci_err(xhci, "Do not support expand command ring\n");
return -ENOMEM;
}
@@ -3269,9 +3234,7 @@ static int prepare_ring(struct xhci_hcd *xhci, struct xhci_ring *ep_ring,
/* If we're not dealing with 0.95 hardware or isoc rings
* on AMD 0.96 host, clear the chain bit.
*/
- if (!xhci_link_trb_quirk(xhci) &&
- !(ep_ring->type == TYPE_ISOC &&
- (xhci->quirks & XHCI_AMD_0x96_HOST)))
+ if (!xhci_link_chain_quirk(xhci, ep_ring->type))
ep_ring->enqueue->link.control &=
cpu_to_le32(~TRB_CHAIN);
else
@@ -3426,8 +3389,7 @@ static void giveback_first_trb(struct xhci_hcd *xhci, int slot_id,
xhci_ring_ep_doorbell(xhci, slot_id, ep_index, stream_id);
}
-static void check_interval(struct xhci_hcd *xhci, struct urb *urb,
- struct xhci_ep_ctx *ep_ctx)
+static void check_interval(struct urb *urb, struct xhci_ep_ctx *ep_ctx)
{
int xhci_interval;
int ep_interval;
@@ -3468,7 +3430,7 @@ int xhci_queue_intr_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
struct xhci_ep_ctx *ep_ctx;
ep_ctx = xhci_get_ep_ctx(xhci, xhci->devs[slot_id]->out_ctx, ep_index);
- check_interval(xhci, urb, ep_ctx);
+ check_interval(urb, ep_ctx);
return xhci_queue_bulk_tx(xhci, mem_flags, urb, slot_id, ep_index);
}
@@ -3714,7 +3676,6 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
upper_32_bits(send_addr),
length_field,
field);
- td->num_trbs++;
addr += trb_buff_len;
sent_len = trb_buff_len;
@@ -3741,7 +3702,6 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
urb_priv->td[1].last_trb_seg = ring->enq_seg;
field = TRB_TYPE(TRB_NORMAL) | ring->cycle_state | TRB_IOC;
queue_trb(xhci, ring, 0, 0, 0, TRB_INTR_TARGET(0), field);
- urb_priv->td[1].num_trbs++;
}
check_trb_math(urb, enqd_len);
@@ -3792,7 +3752,6 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
urb_priv = urb->hcpriv;
td = &urb_priv->td[0];
- td->num_trbs = num_trbs;
/*
* Don't give the first TRB to the hardware (by toggling the cycle bit)
@@ -3990,10 +3949,6 @@ static int xhci_get_isoc_frame_id(struct xhci_hcd *xhci,
start_frame_id = (start_frame_id >> 3) & 0x7ff;
end_frame_id = (end_frame_id >> 3) & 0x7ff;
- xhci_dbg(xhci, "%s: index %d, reg 0x%x start_frame_id 0x%x, end_frame_id 0x%x, start_frame 0x%x\n",
- __func__, index, readl(&xhci->run_regs->microframe_index),
- start_frame_id, end_frame_id, start_frame);
-
if (start_frame_id < end_frame_id) {
if (start_frame > end_frame_id ||
start_frame < start_frame_id)
@@ -4113,7 +4068,6 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
goto cleanup;
}
td = &urb_priv->td[i];
- td->num_trbs = trbs_per_td;
/* use SIA as default, if frame id is used overwrite it */
sia_frame_id = TRB_SIA;
if (!(urb->transfer_flags & URB_ISO_ASAP) &&
@@ -4277,7 +4231,7 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags,
* Check interval value. This should be done before we start to
* calculate the start frame value.
*/
- check_interval(xhci, urb, ep_ctx);
+ check_interval(urb, ep_ctx);
/* Calculate the start frame and put it in urb->start_frame. */
if (HCC_CFC(xhci->hcc_params) && !list_empty(&ep_ring->td_list)) {
@@ -4430,7 +4384,7 @@ int xhci_queue_stop_endpoint(struct xhci_hcd *xhci, struct xhci_command *cmd,
int slot_id, unsigned int ep_index, int suspend)
{
u32 trb_slot_id = SLOT_ID_FOR_TRB(slot_id);
- u32 trb_ep_index = EP_ID_FOR_TRB(ep_index);
+ u32 trb_ep_index = EP_INDEX_FOR_TRB(ep_index);
u32 type = TRB_TYPE(TRB_STOP_RING);
u32 trb_suspend = SUSPEND_PORT_FOR_TRB(suspend);
@@ -4443,7 +4397,7 @@ int xhci_queue_reset_ep(struct xhci_hcd *xhci, struct xhci_command *cmd,
enum xhci_ep_reset_type reset_type)
{
u32 trb_slot_id = SLOT_ID_FOR_TRB(slot_id);
- u32 trb_ep_index = EP_ID_FOR_TRB(ep_index);
+ u32 trb_ep_index = EP_INDEX_FOR_TRB(ep_index);
u32 type = TRB_TYPE(TRB_RESET_EP);
if (reset_type == EP_SOFT_RESET)
diff --git a/drivers/usb/host/xhci-trace.h b/drivers/usb/host/xhci-trace.h
index 5762564b9d73..24405315ffe6 100644
--- a/drivers/usb/host/xhci-trace.h
+++ b/drivers/usb/host/xhci-trace.h
@@ -250,6 +250,7 @@ DECLARE_EVENT_CLASS(xhci_log_urb,
TP_PROTO(struct urb *urb),
TP_ARGS(urb),
TP_STRUCT__entry(
+ __string(devname, dev_name(&urb->dev->dev))
__field(void *, urb)
__field(unsigned int, pipe)
__field(unsigned int, stream)
@@ -265,6 +266,7 @@ DECLARE_EVENT_CLASS(xhci_log_urb,
__field(int, slot_id)
),
TP_fast_assign(
+ __assign_str(devname);
__entry->urb = urb;
__entry->pipe = urb->pipe;
__entry->stream = urb->stream_id;
@@ -279,7 +281,8 @@ DECLARE_EVENT_CLASS(xhci_log_urb,
__entry->type = usb_endpoint_type(&urb->ep->desc);
__entry->slot_id = urb->dev->slot_id;
),
- TP_printk("ep%d%s-%s: urb %p pipe %u slot %d length %d/%d sgs %d/%d stream %d flags %08x",
+ TP_printk("%s ep%d%s-%s: urb %p pipe %u slot %d length %d/%d sgs %d/%d stream %d flags %08x",
+ __get_str(devname),
__entry->epnum, __entry->dir_in ? "in" : "out",
__print_symbolic(__entry->type,
{ USB_ENDPOINT_XFER_INT, "intr" },
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 8579603edaff..899c0effb5d3 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -347,8 +347,8 @@ static int xhci_disable_interrupter(struct xhci_interrupter *ir)
}
/* interrupt moderation interval imod_interval in nanoseconds */
-static int xhci_set_interrupter_moderation(struct xhci_interrupter *ir,
- u32 imod_interval)
+int xhci_set_interrupter_moderation(struct xhci_interrupter *ir,
+ u32 imod_interval)
{
u32 imod;
@@ -1125,10 +1125,20 @@ int xhci_resume(struct xhci_hcd *xhci, pm_message_t msg)
xhci_dbg(xhci, "Start the secondary HCD\n");
retval = xhci_run(xhci->shared_hcd);
}
-
+ if (retval)
+ return retval;
+ /*
+ * Resume roothubs unconditionally as PORTSC change bits are not
+ * immediately visible after xHC reset
+ */
hcd->state = HC_STATE_SUSPENDED;
- if (xhci->shared_hcd)
+
+ if (xhci->shared_hcd) {
xhci->shared_hcd->state = HC_STATE_SUSPENDED;
+ usb_hcd_resume_root_hub(xhci->shared_hcd);
+ }
+ usb_hcd_resume_root_hub(hcd);
+
goto done;
}
@@ -1152,7 +1162,6 @@ int xhci_resume(struct xhci_hcd *xhci, pm_message_t msg)
xhci_dbc_resume(xhci);
- done:
if (retval == 0) {
/*
* Resume roothubs only if there are pending events.
@@ -1178,6 +1187,7 @@ int xhci_resume(struct xhci_hcd *xhci, pm_message_t msg)
usb_hcd_resume_root_hub(hcd);
}
}
+done:
/*
* If system is subject to the Quirk, Compliance Mode Timer needs to
* be re-initialized Always after a system resume. Ports are subject
@@ -2827,7 +2837,7 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci,
xhci->num_active_eps);
return -ENOMEM;
}
- if ((xhci->quirks & XHCI_SW_BW_CHECKING) &&
+ if ((xhci->quirks & XHCI_SW_BW_CHECKING) && !ctx_change &&
xhci_reserve_bandwidth(xhci, virt_dev, command->in_ctx)) {
if ((xhci->quirks & XHCI_EP_LIMIT_QUIRK))
xhci_free_host_resources(xhci, ctrl_ctx);
@@ -4190,8 +4200,10 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
mutex_unlock(&xhci->mutex);
ret = xhci_disable_slot(xhci, udev->slot_id);
xhci_free_virt_device(xhci, udev->slot_id);
- if (!ret)
- xhci_alloc_dev(hcd, udev);
+ if (!ret) {
+ if (xhci_alloc_dev(hcd, udev) == 1)
+ xhci_setup_addressable_virt_dev(xhci, udev);
+ }
kfree(command->completion);
kfree(command);
return -EPROTO;
@@ -4507,35 +4519,27 @@ static int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd,
return 0;
}
-/* check if a usb2 port supports a given extened capability protocol
- * only USB2 ports extended protocol capability values are cached.
- * Return 1 if capability is supported
- */
-static int xhci_check_usb2_port_capability(struct xhci_hcd *xhci, int port,
- unsigned capability)
-{
- u32 port_offset, port_count;
- int i;
-
- for (i = 0; i < xhci->num_ext_caps; i++) {
- if (xhci->ext_caps[i] & capability) {
- /* port offsets starts at 1 */
- port_offset = XHCI_EXT_PORT_OFF(xhci->ext_caps[i]) - 1;
- port_count = XHCI_EXT_PORT_COUNT(xhci->ext_caps[i]);
- if (port >= port_offset &&
- port < port_offset + port_count)
- return 1;
- }
- }
- return 0;
-}
-
static int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
{
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
- int portnum = udev->portnum - 1;
+ struct xhci_port *port;
+ u32 capability;
- if (hcd->speed >= HCD_USB3 || !udev->lpm_capable)
+ /* Check if USB3 device at root port is tunneled over USB4 */
+ if (hcd->speed >= HCD_USB3 && !udev->parent->parent) {
+ port = xhci->usb3_rhub.ports[udev->portnum - 1];
+
+ udev->tunnel_mode = xhci_port_is_tunneled(xhci, port);
+ if (udev->tunnel_mode == USB_LINK_UNKNOWN)
+ dev_dbg(&udev->dev, "link tunnel state unknown\n");
+ else if (udev->tunnel_mode == USB_LINK_TUNNELED)
+ dev_dbg(&udev->dev, "tunneled over USB4 link\n");
+ else if (udev->tunnel_mode == USB_LINK_NATIVE)
+ dev_dbg(&udev->dev, "native USB 3.x link\n");
+ return 0;
+ }
+
+ if (hcd->speed >= HCD_USB3 || !udev->lpm_capable || !xhci->hw_lpm_support)
return 0;
/* we only support lpm for non-hub device connected to root hub yet */
@@ -4543,14 +4547,14 @@ static int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
udev->descriptor.bDeviceClass == USB_CLASS_HUB)
return 0;
- if (xhci->hw_lpm_support == 1 &&
- xhci_check_usb2_port_capability(
- xhci, portnum, XHCI_HLC)) {
+ port = xhci->usb2_rhub.ports[udev->portnum - 1];
+ capability = port->port_cap->protocol_caps;
+
+ if (capability & XHCI_HLC) {
udev->usb2_hw_lpm_capable = 1;
udev->l1_params.timeout = XHCI_L1_TIMEOUT;
udev->l1_params.besl = XHCI_DEFAULT_BESL;
- if (xhci_check_usb2_port_capability(xhci, portnum,
- XHCI_BLC))
+ if (capability & XHCI_BLC)
udev->usb2_hw_lpm_besl_capable = 1;
}
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 6f4bf98a6282..620502de971a 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -17,6 +17,7 @@
#include <linux/kernel.h>
#include <linux/usb/hcd.h>
#include <linux/io-64-nonatomic-lo-hi.h>
+#include <linux/io-64-nonatomic-hi-lo.h>
/* Code sharing between pci-quirks and xhci hcd */
#include "xhci-ext-caps.h"
@@ -805,13 +806,19 @@ struct xhci_transfer_event {
__le32 flags;
};
+/* Transfer event flags bitfield, also for select command completion events */
+#define TRB_TO_SLOT_ID(p) (((p) >> 24) & 0xff)
+#define SLOT_ID_FOR_TRB(p) (((p) & 0xff) << 24)
+
+#define TRB_TO_EP_ID(p) (((p) >> 16) & 0x1f) /* Endpoint ID 1 - 31 */
+#define EP_ID_FOR_TRB(p) (((p) & 0x1f) << 16)
+
+#define TRB_TO_EP_INDEX(p) (TRB_TO_EP_ID(p) - 1) /* Endpoint index 0 - 30 */
+#define EP_INDEX_FOR_TRB(p) ((((p) + 1) & 0x1f) << 16)
+
/* Transfer event TRB length bit mask */
-/* bits 0:23 */
#define EVENT_TRB_LEN(p) ((p) & 0xffffff)
-/** Transfer Event bit fields **/
-#define TRB_TO_EP_ID(p) (((p) >> 16) & 0x1f)
-
/* 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)
@@ -950,8 +957,6 @@ struct xhci_event_cmd {
__le32 flags;
};
-/* flags bitmasks */
-
/* Address device - disable SetAddress */
#define TRB_BSR (1<<9)
@@ -987,13 +992,8 @@ enum xhci_setup_dev {
/* bits 16:23 are the virtual function ID */
/* bits 24:31 are the slot ID */
-#define TRB_TO_SLOT_ID(p) (((p) & (0xff<<24)) >> 24)
-#define SLOT_ID_FOR_TRB(p) (((p) & 0xff) << 24)
/* Stop Endpoint TRB - ep_index to endpoint ID for this TRB */
-#define TRB_TO_EP_INDEX(p) ((((p) & (0x1f << 16)) >> 16) - 1)
-#define EP_ID_FOR_TRB(p) ((((p) + 1) & 0x1f) << 16)
-
#define SUSPEND_PORT_FOR_TRB(p) (((p) & 1) << 23)
#define TRB_TO_SUSPEND_PORT(p) (((p) & (1 << 23)) >> 23)
#define LAST_EP_INDEX 30
@@ -1276,6 +1276,7 @@ enum xhci_cancelled_td_status {
TD_DIRTY = 0,
TD_HALTED,
TD_CLEARING_CACHE,
+ TD_CLEARING_CACHE_DEFERRED,
TD_CLEARED,
};
@@ -1293,7 +1294,6 @@ struct xhci_td {
/* actual_length of the URB has already been set */
bool urb_length_set;
bool error_mid_td;
- unsigned int num_trbs;
};
/*
@@ -1376,8 +1376,6 @@ struct xhci_erst {
unsigned int num_entries;
/* xhci->event_ring keeps track of segment dma addresses */
dma_addr_t erst_dma_addr;
- /* Num entries the ERST can contain */
- unsigned int erst_size;
};
struct xhci_scratchpad {
@@ -1392,8 +1390,8 @@ struct urb_priv {
struct xhci_td td[] __counted_by(num_tds);
};
-/* Reasonable limit for number of Event Ring segments (spec allows 32k) */
-#define ERST_MAX_SEGS 2
+/* Number of Event Ring segments to allocate, when amount is not specified. (spec allows 32k) */
+#define ERST_DEFAULT_SEGS 2
/* Poll every 60 seconds */
#define POLL_TIMEOUT 60
/* Stop endpoint command timeout (secs) for URB cancellation watchdog timer */
@@ -1451,6 +1449,7 @@ struct xhci_port_cap {
u8 psi_uid_count;
u8 maj_rev;
u8 min_rev;
+ u32 protocol_caps;
};
struct xhci_port {
@@ -1499,15 +1498,10 @@ struct xhci_hcd {
spinlock_t lock;
/* packed release number */
- u8 sbrn;
u16 hci_version;
- u8 max_slots;
u16 max_interrupters;
- u8 max_ports;
- u8 isoc_threshold;
/* imod_interval in ns (I * 250ns) */
u32 imod_interval;
- int event_ring_max;
/* 4KB min, 128MB max */
int page_size;
/* Valid values are 12 to 20, inclusive */
@@ -1589,7 +1583,7 @@ struct xhci_hcd {
#define XHCI_RESET_ON_RESUME BIT_ULL(7)
#define XHCI_SW_BW_CHECKING BIT_ULL(8)
#define XHCI_AMD_0x96_HOST BIT_ULL(9)
-#define XHCI_TRUST_TX_LENGTH BIT_ULL(10)
+#define XHCI_TRUST_TX_LENGTH BIT_ULL(10) /* Deprecated */
#define XHCI_LPM_SUPPORT BIT_ULL(11)
#define XHCI_INTEL_HOST BIT_ULL(12)
#define XHCI_SPURIOUS_REBOOT BIT_ULL(13)
@@ -1617,7 +1611,7 @@ struct xhci_hcd {
#define XHCI_DEFAULT_PM_RUNTIME_ALLOW BIT_ULL(33)
#define XHCI_RESET_PLL_ON_DISCONNECT BIT_ULL(34)
#define XHCI_SNPS_BROKEN_SUSPEND BIT_ULL(35)
-#define XHCI_RENESAS_FW_QUIRK BIT_ULL(36)
+/* Reserved. It was XHCI_RENESAS_FW_QUIRK */
#define XHCI_SKIP_PHY_INIT BIT_ULL(37)
#define XHCI_DISABLE_SPARSE BIT_ULL(38)
#define XHCI_SG_TRB_CACHE_SIZE_QUIRK BIT_ULL(39)
@@ -1628,6 +1622,8 @@ struct xhci_hcd {
#define XHCI_RESET_TO_DEFAULT BIT_ULL(44)
#define XHCI_ZHAOXIN_TRB_FETCH BIT_ULL(45)
#define XHCI_ZHAOXIN_HOST BIT_ULL(46)
+#define XHCI_WRITE_64_HI_LO BIT_ULL(47)
+#define XHCI_CDNS_SCTX_QUIRK BIT_ULL(48)
unsigned int num_active_eps;
unsigned int limit_active_eps;
@@ -1640,9 +1636,6 @@ struct xhci_hcd {
unsigned broken_suspend:1;
/* Indicates that omitting hcd is supported if root hub has no ports */
unsigned allow_single_roothub:1;
- /* cached usb2 extened protocol capabilites */
- u32 *ext_caps;
- unsigned int num_ext_caps;
/* cached extended protocol port capabilities */
struct xhci_port_cap *port_caps;
unsigned int num_port_caps;
@@ -1729,8 +1722,6 @@ static inline bool xhci_has_one_roothub(struct xhci_hcd *xhci)
dev_err(xhci_to_hcd(xhci)->self.controller , fmt , ## args)
#define xhci_warn(xhci, fmt, args...) \
dev_warn(xhci_to_hcd(xhci)->self.controller , fmt , ## args)
-#define xhci_warn_ratelimited(xhci, fmt, args...) \
- dev_warn_ratelimited(xhci_to_hcd(xhci)->self.controller , fmt , ## args)
#define xhci_info(xhci, fmt, args...) \
dev_info(xhci_to_hcd(xhci)->self.controller , fmt , ## args)
@@ -1754,9 +1745,12 @@ static inline void xhci_write_64(struct xhci_hcd *xhci,
lo_hi_writeq(val, regs);
}
-static inline int xhci_link_trb_quirk(struct xhci_hcd *xhci)
+
+/* Link TRB chain should always be set on 0.95 hosts, and AMD 0.96 ISOC rings */
+static inline bool xhci_link_chain_quirk(struct xhci_hcd *xhci, enum xhci_ring_type type)
{
- return xhci->quirks & XHCI_LINK_TRB_QUIRK;
+ return (xhci->quirks & XHCI_LINK_TRB_QUIRK) ||
+ (type == TYPE_ISOC && (xhci->quirks & XHCI_AMD_0x96_HOST));
}
/* xHCI debugging */
@@ -1833,7 +1827,8 @@ struct xhci_container_ctx *xhci_alloc_container_ctx(struct xhci_hcd *xhci,
void xhci_free_container_ctx(struct xhci_hcd *xhci,
struct xhci_container_ctx *ctx);
struct xhci_interrupter *
-xhci_create_secondary_interrupter(struct usb_hcd *hcd, int num_seg);
+xhci_create_secondary_interrupter(struct usb_hcd *hcd, unsigned int segs,
+ u32 imod_interval);
void xhci_remove_secondary_interrupter(struct usb_hcd
*hcd, struct xhci_interrupter *ir);
@@ -1873,12 +1868,13 @@ int xhci_alloc_tt_info(struct xhci_hcd *xhci,
struct xhci_virt_device *virt_dev,
struct usb_device *hdev,
struct usb_tt *tt, gfp_t mem_flags);
+int xhci_set_interrupter_moderation(struct xhci_interrupter *ir,
+ u32 imod_interval);
/* xHCI ring, segment, TRB, and TD functions */
dma_addr_t xhci_trb_virt_to_dma(struct xhci_segment *seg, union xhci_trb *trb);
-struct xhci_segment *trb_in_td(struct xhci_hcd *xhci,
- struct xhci_segment *start_seg, union xhci_trb *start_trb,
- union xhci_trb *end_trb, dma_addr_t suspect_dma, bool debug);
+struct xhci_segment *trb_in_td(struct xhci_hcd *xhci, struct xhci_td *td,
+ dma_addr_t suspect_dma, bool debug);
int xhci_is_vendor_info_code(struct xhci_hcd *xhci, unsigned int trb_comp_code);
void xhci_ring_cmd_db(struct xhci_hcd *xhci);
int xhci_queue_slot_control(struct xhci_hcd *xhci, struct xhci_command *cmd,
@@ -1907,10 +1903,6 @@ int xhci_queue_reset_ep(struct xhci_hcd *xhci, struct xhci_command *cmd,
enum xhci_ep_reset_type reset_type);
int xhci_queue_reset_device(struct xhci_hcd *xhci, struct xhci_command *cmd,
u32 slot_id);
-void xhci_cleanup_stalled_ring(struct xhci_hcd *xhci, unsigned int slot_id,
- unsigned int ep_index, unsigned int stream_id,
- struct xhci_td *td);
-void xhci_stop_endpoint_command_watchdog(struct timer_list *t);
void xhci_handle_command_timeout(struct work_struct *work);
void xhci_ring_ep_doorbell(struct xhci_hcd *xhci, unsigned int slot_id,
@@ -1932,7 +1924,8 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex,
int xhci_hub_status_data(struct usb_hcd *hcd, char *buf);
int xhci_find_raw_port_number(struct usb_hcd *hcd, int port1);
struct xhci_hub *xhci_get_rhub(struct usb_hcd *hcd);
-
+enum usb_link_tunnel_mode xhci_port_is_tunneled(struct xhci_hcd *xhci,
+ struct xhci_port *port);
void xhci_hc_died(struct xhci_hcd *xhci);
#ifdef CONFIG_PM
@@ -2027,8 +2020,7 @@ static inline const char *xhci_decode_trb(char *str, size_t size,
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,
+ TRB_TO_EP_ID(field3),
xhci_trb_type_string(type),
field3 & EVENT_DATA ? 'E' : 'e',
field3 & TRB_CYCLE ? 'C' : 'c');
@@ -2143,8 +2135,7 @@ static inline const char *xhci_decode_trb(char *str, size_t size,
xhci_trb_type_string(type),
field1, field0,
TRB_TO_SLOT_ID(field3),
- /* Macro decrements 1, maybe it shouldn't?!? */
- TRB_TO_EP_INDEX(field3) + 1,
+ TRB_TO_EP_ID(field3),
field3 & TRB_TSP ? 'T' : 't',
field3 & TRB_CYCLE ? 'C' : 'c');
break;
@@ -2154,8 +2145,7 @@ static inline const char *xhci_decode_trb(char *str, size_t size,
xhci_trb_type_string(type),
TRB_TO_SLOT_ID(field3),
TRB_TO_SUSPEND_PORT(field3),
- /* Macro decrements 1, maybe it shouldn't?!? */
- TRB_TO_EP_INDEX(field3) + 1,
+ TRB_TO_EP_ID(field3),
field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_SET_DEQ:
@@ -2165,8 +2155,7 @@ static inline const char *xhci_decode_trb(char *str, size_t size,
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,
+ TRB_TO_EP_ID(field3),
field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_RESET_DEV:
@@ -2340,7 +2329,12 @@ static inline const char *xhci_decode_portsc(char *str, u32 portsc)
{
int ret;
- ret = sprintf(str, "%s %s %s Link:%s PortSpeed:%d ",
+ ret = sprintf(str, "0x%08x ", portsc);
+
+ if (portsc == ~(u32)0)
+ return str;
+
+ ret += sprintf(str + ret, "%s %s %s Link:%s PortSpeed:%d ",
portsc & PORT_POWER ? "Powered" : "Powered-off",
portsc & PORT_CONNECT ? "Connected" : "Not-connected",
portsc & PORT_PE ? "Enabled" : "Disabled",
diff --git a/drivers/usb/image/microtek.c b/drivers/usb/image/microtek.c
index 8c8fa71c69c4..9f758241d9d3 100644
--- a/drivers/usb/image/microtek.c
+++ b/drivers/usb/image/microtek.c
@@ -328,12 +328,6 @@ static int mts_slave_alloc (struct scsi_device *s)
return 0;
}
-static int mts_slave_configure (struct scsi_device *s)
-{
- blk_queue_dma_alignment(s->request_queue, (512 - 1));
- return 0;
-}
-
static int mts_scsi_abort(struct scsi_cmnd *srb)
{
struct mts_desc* desc = (struct mts_desc*)(srb->device->host->hostdata[0]);
@@ -631,8 +625,8 @@ static const struct scsi_host_template mts_scsi_host_template = {
.can_queue = 1,
.this_id = -1,
.emulated = 1,
+ .dma_alignment = 511,
.slave_alloc = mts_slave_alloc,
- .slave_configure = mts_slave_configure,
.max_sectors= 256, /* 128 K */
};
diff --git a/drivers/usb/isp1760/isp1760-hcd.c b/drivers/usb/isp1760/isp1760-hcd.c
index 0e5e4cb74c87..add2d2e3b61b 100644
--- a/drivers/usb/isp1760/isp1760-hcd.c
+++ b/drivers/usb/isp1760/isp1760-hcd.c
@@ -27,7 +27,7 @@
#include <linux/iopoll.h>
#include <linux/mm.h>
#include <linux/timer.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <asm/cacheflush.h>
#include "isp1760-core.h"
diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig
index c510af7baa0d..50b86d531701 100644
--- a/drivers/usb/misc/Kconfig
+++ b/drivers/usb/misc/Kconfig
@@ -316,18 +316,18 @@ config BRCM_USB_PINMAP
signals, which are typically on dedicated pins on the chip,
to any gpio.
-config USB_ONBOARD_HUB
- tristate "Onboard USB hub support"
+config USB_ONBOARD_DEV
+ tristate "Onboard USB device support"
depends on OF
help
- Say Y here if you want to support discrete onboard USB hubs that
- don't require an additional control bus for initialization, but
- need some non-trivial form of initialization, such as enabling a
- power regulator. An example for such a hub is the Realtek
- RTS5411.
+ Say Y here if you want to support discrete onboard USB devices
+ that don't require an additional control bus for initialization,
+ but need some non-trivial form of initialization, such as
+ enabling a power regulator. An example for such device is the
+ Realtek RTS5411 hub.
This driver can be used as a module but its state (module vs
builtin) must match the state of the USB subsystem. Enabling
this config will enable the driver and it will automatically
match the state of the USB subsystem. If this driver is a
- module it will be called onboard_usb_hub.
+ module it will be called onboard_usb_dev.
diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile
index 0bc732bcb162..0cd5bc8f52fe 100644
--- a/drivers/usb/misc/Makefile
+++ b/drivers/usb/misc/Makefile
@@ -33,4 +33,4 @@ obj-$(CONFIG_USB_CHAOSKEY) += chaoskey.o
obj-$(CONFIG_USB_SISUSBVGA) += sisusbvga/
obj-$(CONFIG_USB_LINK_LAYER_TEST) += lvstest.o
obj-$(CONFIG_BRCM_USB_PINMAP) += brcmstb-usb-pinmap.o
-obj-$(CONFIG_USB_ONBOARD_HUB) += onboard_usb_hub.o
+obj-$(CONFIG_USB_ONBOARD_DEV) += onboard_usb_dev.o
diff --git a/drivers/usb/misc/appledisplay.c b/drivers/usb/misc/appledisplay.c
index c8098e9b432e..62b5a30edc42 100644
--- a/drivers/usb/misc/appledisplay.c
+++ b/drivers/usb/misc/appledisplay.c
@@ -107,7 +107,12 @@ static void appledisplay_complete(struct urb *urb)
case ACD_BTN_BRIGHT_UP:
case ACD_BTN_BRIGHT_DOWN:
pdata->button_pressed = 1;
- schedule_delayed_work(&pdata->work, 0);
+ /*
+ * there is a window during which no device
+ * is registered
+ */
+ if (pdata->bd )
+ schedule_delayed_work(&pdata->work, 0);
break;
case ACD_BTN_NONE:
default:
@@ -202,6 +207,7 @@ static int appledisplay_probe(struct usb_interface *iface,
const struct usb_device_id *id)
{
struct backlight_properties props;
+ struct backlight_device *backlight;
struct appledisplay *pdata;
struct usb_device *udev = interface_to_usbdev(iface);
struct usb_endpoint_descriptor *endpoint;
@@ -272,13 +278,14 @@ static int appledisplay_probe(struct usb_interface *iface,
memset(&props, 0, sizeof(struct backlight_properties));
props.type = BACKLIGHT_RAW;
props.max_brightness = 0xff;
- pdata->bd = backlight_device_register(bl_name, NULL, pdata,
+ backlight = backlight_device_register(bl_name, NULL, pdata,
&appledisplay_bl_data, &props);
- if (IS_ERR(pdata->bd)) {
+ if (IS_ERR(backlight)) {
dev_err(&iface->dev, "Backlight registration failed\n");
- retval = PTR_ERR(pdata->bd);
+ retval = PTR_ERR(backlight);
goto error;
}
+ pdata->bd = backlight;
/* Try to get brightness */
brightness = appledisplay_bl_get_brightness(pdata->bd);
diff --git a/drivers/usb/misc/brcmstb-usb-pinmap.c b/drivers/usb/misc/brcmstb-usb-pinmap.c
index 2b2019c19cde..1ce885e4184c 100644
--- a/drivers/usb/misc/brcmstb-usb-pinmap.c
+++ b/drivers/usb/misc/brcmstb-usb-pinmap.c
@@ -335,6 +335,7 @@ static const struct of_device_id brcmstb_usb_pinmap_of_match[] = {
{ .compatible = "brcm,usb-pinmap" },
{ },
};
+MODULE_DEVICE_TABLE(of, brcmstb_usb_pinmap_of_match);
static struct platform_driver brcmstb_usb_pinmap_driver = {
.driver = {
diff --git a/drivers/usb/misc/cypress_cy7c63.c b/drivers/usb/misc/cypress_cy7c63.c
index cecd7693b741..75f5a740cba3 100644
--- a/drivers/usb/misc/cypress_cy7c63.c
+++ b/drivers/usb/misc/cypress_cy7c63.c
@@ -88,6 +88,9 @@ static int vendor_command(struct cypress *dev, unsigned char request,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_OTHER,
address, data, iobuf, CYPRESS_MAX_REQSIZE,
USB_CTRL_GET_TIMEOUT);
+ /* we must not process garbage */
+ if (retval < 2)
+ goto err_buf;
/* store returned data (more READs to be added) */
switch (request) {
@@ -107,6 +110,7 @@ static int vendor_command(struct cypress *dev, unsigned char request,
break;
}
+err_buf:
kfree(iobuf);
error:
return retval;
diff --git a/drivers/usb/misc/ezusb.c b/drivers/usb/misc/ezusb.c
index 78aaee56c2b7..7ab4856aba0e 100644
--- a/drivers/usb/misc/ezusb.c
+++ b/drivers/usb/misc/ezusb.c
@@ -148,4 +148,5 @@ int ezusb_fx2_ihex_firmware_download(struct usb_device *dev,
EXPORT_SYMBOL_GPL(ezusb_fx2_ihex_firmware_download);
#endif
+MODULE_DESCRIPTION("EZUSB device support");
MODULE_LICENSE("GPL");
diff --git a/drivers/usb/misc/isight_firmware.c b/drivers/usb/misc/isight_firmware.c
index 4d30095d6ad2..2d9af74a62c1 100644
--- a/drivers/usb/misc/isight_firmware.c
+++ b/drivers/usb/misc/isight_firmware.c
@@ -127,5 +127,6 @@ static struct usb_driver isight_firmware_driver = {
module_usb_driver(isight_firmware_driver);
+MODULE_DESCRIPTION("iSight firmware loading support");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
diff --git a/drivers/usb/misc/ldusb.c b/drivers/usb/misc/ldusb.c
index 7cbef74dfc9a..f392d6f84df9 100644
--- a/drivers/usb/misc/ldusb.c
+++ b/drivers/usb/misc/ldusb.c
@@ -627,7 +627,6 @@ static const struct file_operations ld_usb_fops = {
.open = ld_usb_open,
.release = ld_usb_release,
.poll = ld_usb_poll,
- .llseek = no_llseek,
};
/*
diff --git a/drivers/usb/misc/onboard_usb_dev.c b/drivers/usb/misc/onboard_usb_dev.c
new file mode 100644
index 000000000000..560591e02d6a
--- /dev/null
+++ b/drivers/usb/misc/onboard_usb_dev.c
@@ -0,0 +1,631 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for onboard USB devices
+ *
+ * Copyright (c) 2022, Google LLC
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/export.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/suspend.h>
+#include <linux/sysfs.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include <linux/usb/onboard_dev.h>
+#include <linux/workqueue.h>
+
+#include "onboard_usb_dev.h"
+
+/* USB5744 register offset and mask */
+#define USB5744_CMD_ATTACH 0xAA
+#define USB5744_CMD_ATTACH_LSB 0x56
+#define USB5744_CMD_CREG_ACCESS 0x99
+#define USB5744_CMD_CREG_ACCESS_LSB 0x37
+#define USB5744_CREG_MEM_ADDR 0x00
+#define USB5744_CREG_WRITE 0x00
+#define USB5744_CREG_RUNTIMEFLAGS2 0x41
+#define USB5744_CREG_RUNTIMEFLAGS2_LSB 0x1D
+#define USB5744_CREG_BYPASS_UDC_SUSPEND BIT(3)
+
+static void onboard_dev_attach_usb_driver(struct work_struct *work);
+
+static struct usb_device_driver onboard_dev_usbdev_driver;
+static DECLARE_WORK(attach_usb_driver_work, onboard_dev_attach_usb_driver);
+
+/************************** Platform driver **************************/
+
+struct usbdev_node {
+ struct usb_device *udev;
+ struct list_head list;
+};
+
+struct onboard_dev {
+ struct regulator_bulk_data supplies[MAX_SUPPLIES];
+ struct device *dev;
+ const struct onboard_dev_pdata *pdata;
+ struct gpio_desc *reset_gpio;
+ bool always_powered_in_suspend;
+ bool is_powered_on;
+ bool going_away;
+ struct list_head udev_list;
+ struct mutex lock;
+ struct clk *clk;
+};
+
+static int onboard_dev_get_regulators(struct onboard_dev *onboard_dev)
+{
+ const char * const *supply_names = onboard_dev->pdata->supply_names;
+ unsigned int num_supplies = onboard_dev->pdata->num_supplies;
+ struct device *dev = onboard_dev->dev;
+ unsigned int i;
+ int err;
+
+ if (num_supplies > MAX_SUPPLIES)
+ return dev_err_probe(dev, -EINVAL, "max %d supplies supported!\n",
+ MAX_SUPPLIES);
+
+ for (i = 0; i < num_supplies; i++)
+ onboard_dev->supplies[i].supply = supply_names[i];
+
+ err = devm_regulator_bulk_get(dev, num_supplies, onboard_dev->supplies);
+ if (err)
+ dev_err(dev, "Failed to get regulator supplies: %pe\n",
+ ERR_PTR(err));
+
+ return err;
+}
+
+static int onboard_dev_power_on(struct onboard_dev *onboard_dev)
+{
+ int err;
+
+ err = clk_prepare_enable(onboard_dev->clk);
+ if (err) {
+ dev_err(onboard_dev->dev, "failed to enable clock: %pe\n",
+ ERR_PTR(err));
+ return err;
+ }
+
+ err = regulator_bulk_enable(onboard_dev->pdata->num_supplies,
+ onboard_dev->supplies);
+ if (err) {
+ dev_err(onboard_dev->dev, "failed to enable supplies: %pe\n",
+ ERR_PTR(err));
+ goto disable_clk;
+ }
+
+ fsleep(onboard_dev->pdata->reset_us);
+ gpiod_set_value_cansleep(onboard_dev->reset_gpio, 0);
+ fsleep(onboard_dev->pdata->power_on_delay_us);
+
+ onboard_dev->is_powered_on = true;
+
+ return 0;
+
+disable_clk:
+ clk_disable_unprepare(onboard_dev->clk);
+ return err;
+}
+
+static int onboard_dev_power_off(struct onboard_dev *onboard_dev)
+{
+ int err;
+
+ gpiod_set_value_cansleep(onboard_dev->reset_gpio, 1);
+
+ err = regulator_bulk_disable(onboard_dev->pdata->num_supplies,
+ onboard_dev->supplies);
+ if (err) {
+ dev_err(onboard_dev->dev, "failed to disable supplies: %pe\n",
+ ERR_PTR(err));
+ return err;
+ }
+
+ clk_disable_unprepare(onboard_dev->clk);
+
+ onboard_dev->is_powered_on = false;
+
+ return 0;
+}
+
+static int __maybe_unused onboard_dev_suspend(struct device *dev)
+{
+ struct onboard_dev *onboard_dev = dev_get_drvdata(dev);
+ struct usbdev_node *node;
+ bool power_off = true;
+
+ if (onboard_dev->always_powered_in_suspend)
+ return 0;
+
+ mutex_lock(&onboard_dev->lock);
+
+ list_for_each_entry(node, &onboard_dev->udev_list, list) {
+ if (!device_may_wakeup(node->udev->bus->controller))
+ continue;
+
+ if (usb_wakeup_enabled_descendants(node->udev)) {
+ power_off = false;
+ break;
+ }
+ }
+
+ mutex_unlock(&onboard_dev->lock);
+
+ if (!power_off)
+ return 0;
+
+ return onboard_dev_power_off(onboard_dev);
+}
+
+static int __maybe_unused onboard_dev_resume(struct device *dev)
+{
+ struct onboard_dev *onboard_dev = dev_get_drvdata(dev);
+
+ if (onboard_dev->is_powered_on)
+ return 0;
+
+ return onboard_dev_power_on(onboard_dev);
+}
+
+static inline void get_udev_link_name(const struct usb_device *udev, char *buf,
+ size_t size)
+{
+ snprintf(buf, size, "usb_dev.%s", dev_name(&udev->dev));
+}
+
+static int onboard_dev_add_usbdev(struct onboard_dev *onboard_dev,
+ struct usb_device *udev)
+{
+ struct usbdev_node *node;
+ char link_name[64];
+ int err;
+
+ mutex_lock(&onboard_dev->lock);
+
+ if (onboard_dev->going_away) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ node = kzalloc(sizeof(*node), GFP_KERNEL);
+ if (!node) {
+ err = -ENOMEM;
+ goto error;
+ }
+
+ node->udev = udev;
+
+ list_add(&node->list, &onboard_dev->udev_list);
+
+ mutex_unlock(&onboard_dev->lock);
+
+ get_udev_link_name(udev, link_name, sizeof(link_name));
+ WARN_ON(sysfs_create_link(&onboard_dev->dev->kobj, &udev->dev.kobj,
+ link_name));
+
+ return 0;
+
+error:
+ mutex_unlock(&onboard_dev->lock);
+
+ return err;
+}
+
+static void onboard_dev_remove_usbdev(struct onboard_dev *onboard_dev,
+ const struct usb_device *udev)
+{
+ struct usbdev_node *node;
+ char link_name[64];
+
+ get_udev_link_name(udev, link_name, sizeof(link_name));
+ sysfs_remove_link(&onboard_dev->dev->kobj, link_name);
+
+ mutex_lock(&onboard_dev->lock);
+
+ list_for_each_entry(node, &onboard_dev->udev_list, list) {
+ if (node->udev == udev) {
+ list_del(&node->list);
+ kfree(node);
+ break;
+ }
+ }
+
+ mutex_unlock(&onboard_dev->lock);
+}
+
+static ssize_t always_powered_in_suspend_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ const struct onboard_dev *onboard_dev = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%d\n", onboard_dev->always_powered_in_suspend);
+}
+
+static ssize_t always_powered_in_suspend_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct onboard_dev *onboard_dev = dev_get_drvdata(dev);
+ bool val;
+ int ret;
+
+ ret = kstrtobool(buf, &val);
+ if (ret < 0)
+ return ret;
+
+ onboard_dev->always_powered_in_suspend = val;
+
+ return count;
+}
+static DEVICE_ATTR_RW(always_powered_in_suspend);
+
+static struct attribute *onboard_dev_attrs[] = {
+ &dev_attr_always_powered_in_suspend.attr,
+ NULL,
+};
+
+static umode_t onboard_dev_attrs_are_visible(struct kobject *kobj,
+ struct attribute *attr,
+ int n)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct onboard_dev *onboard_dev = dev_get_drvdata(dev);
+
+ if (attr == &dev_attr_always_powered_in_suspend.attr &&
+ !onboard_dev->pdata->is_hub)
+ return 0;
+
+ return attr->mode;
+}
+
+static const struct attribute_group onboard_dev_group = {
+ .is_visible = onboard_dev_attrs_are_visible,
+ .attrs = onboard_dev_attrs,
+};
+__ATTRIBUTE_GROUPS(onboard_dev);
+
+
+static void onboard_dev_attach_usb_driver(struct work_struct *work)
+{
+ int err;
+
+ err = driver_attach(&onboard_dev_usbdev_driver.driver);
+ if (err)
+ pr_err("Failed to attach USB driver: %pe\n", ERR_PTR(err));
+}
+
+static int onboard_dev_5744_i2c_init(struct i2c_client *client)
+{
+#if IS_ENABLED(CONFIG_I2C)
+ struct device *dev = &client->dev;
+ int ret;
+
+ /*
+ * Set BYPASS_UDC_SUSPEND bit to ensure MCU is always enabled
+ * and ready to respond to SMBus runtime commands.
+ * The command writes 5 bytes to memory and single data byte in
+ * configuration register.
+ */
+ char wr_buf[7] = {USB5744_CREG_MEM_ADDR, 5,
+ USB5744_CREG_WRITE, 1,
+ USB5744_CREG_RUNTIMEFLAGS2,
+ USB5744_CREG_RUNTIMEFLAGS2_LSB,
+ USB5744_CREG_BYPASS_UDC_SUSPEND};
+
+ ret = i2c_smbus_write_block_data(client, 0, sizeof(wr_buf), wr_buf);
+ if (ret)
+ return dev_err_probe(dev, ret, "BYPASS_UDC_SUSPEND bit configuration failed\n");
+
+ ret = i2c_smbus_write_word_data(client, USB5744_CMD_CREG_ACCESS,
+ USB5744_CMD_CREG_ACCESS_LSB);
+ if (ret)
+ return dev_err_probe(dev, ret, "Configuration Register Access Command failed\n");
+
+ /* Send SMBus command to boot hub. */
+ ret = i2c_smbus_write_word_data(client, USB5744_CMD_ATTACH,
+ USB5744_CMD_ATTACH_LSB);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "USB Attach with SMBus command failed\n");
+
+ return ret;
+#else
+ return -ENODEV;
+#endif
+}
+
+static int onboard_dev_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct onboard_dev *onboard_dev;
+ struct device_node *i2c_node;
+ int err;
+
+ onboard_dev = devm_kzalloc(dev, sizeof(*onboard_dev), GFP_KERNEL);
+ if (!onboard_dev)
+ return -ENOMEM;
+
+ onboard_dev->pdata = device_get_match_data(dev);
+ if (!onboard_dev->pdata)
+ return -EINVAL;
+
+ if (!onboard_dev->pdata->is_hub)
+ onboard_dev->always_powered_in_suspend = true;
+
+ onboard_dev->dev = dev;
+
+ err = onboard_dev_get_regulators(onboard_dev);
+ if (err)
+ return err;
+
+ onboard_dev->clk = devm_clk_get_optional(dev, NULL);
+ if (IS_ERR(onboard_dev->clk))
+ return dev_err_probe(dev, PTR_ERR(onboard_dev->clk),
+ "failed to get clock\n");
+
+ onboard_dev->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(onboard_dev->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(onboard_dev->reset_gpio),
+ "failed to get reset GPIO\n");
+
+ mutex_init(&onboard_dev->lock);
+ INIT_LIST_HEAD(&onboard_dev->udev_list);
+
+ dev_set_drvdata(dev, onboard_dev);
+
+ err = onboard_dev_power_on(onboard_dev);
+ if (err)
+ return err;
+
+ i2c_node = of_parse_phandle(pdev->dev.of_node, "i2c-bus", 0);
+ if (i2c_node) {
+ struct i2c_client *client;
+
+ client = of_find_i2c_device_by_node(i2c_node);
+ of_node_put(i2c_node);
+
+ if (!client) {
+ err = -EPROBE_DEFER;
+ goto err_power_off;
+ }
+
+ if (of_device_is_compatible(pdev->dev.of_node, "usb424,2744") ||
+ of_device_is_compatible(pdev->dev.of_node, "usb424,5744"))
+ err = onboard_dev_5744_i2c_init(client);
+
+ put_device(&client->dev);
+ if (err < 0)
+ goto err_power_off;
+ }
+
+ /*
+ * The USB driver might have been detached from the USB devices by
+ * onboard_dev_remove() (e.g. through an 'unbind' by userspace),
+ * make sure to re-attach it if needed.
+ *
+ * This needs to be done deferred to avoid self-deadlocks on systems
+ * with nested onboard hubs.
+ */
+ schedule_work(&attach_usb_driver_work);
+
+ return 0;
+
+err_power_off:
+ onboard_dev_power_off(onboard_dev);
+ return err;
+}
+
+static void onboard_dev_remove(struct platform_device *pdev)
+{
+ struct onboard_dev *onboard_dev = dev_get_drvdata(&pdev->dev);
+ struct usbdev_node *node;
+ struct usb_device *udev;
+
+ onboard_dev->going_away = true;
+
+ mutex_lock(&onboard_dev->lock);
+
+ /* unbind the USB devices to avoid dangling references to this device */
+ while (!list_empty(&onboard_dev->udev_list)) {
+ node = list_first_entry(&onboard_dev->udev_list,
+ struct usbdev_node, list);
+ udev = node->udev;
+
+ /*
+ * Unbinding the driver will call onboard_dev_remove_usbdev(),
+ * which acquires onboard_dev->lock. We must release the lock
+ * first.
+ */
+ get_device(&udev->dev);
+ mutex_unlock(&onboard_dev->lock);
+ device_release_driver(&udev->dev);
+ put_device(&udev->dev);
+ mutex_lock(&onboard_dev->lock);
+ }
+
+ mutex_unlock(&onboard_dev->lock);
+
+ onboard_dev_power_off(onboard_dev);
+}
+
+MODULE_DEVICE_TABLE(of, onboard_dev_match);
+
+static const struct dev_pm_ops __maybe_unused onboard_dev_pm_ops = {
+ SET_LATE_SYSTEM_SLEEP_PM_OPS(onboard_dev_suspend, onboard_dev_resume)
+};
+
+static struct platform_driver onboard_dev_driver = {
+ .probe = onboard_dev_probe,
+ .remove_new = onboard_dev_remove,
+
+ .driver = {
+ .name = "onboard-usb-dev",
+ .of_match_table = onboard_dev_match,
+ .pm = pm_ptr(&onboard_dev_pm_ops),
+ .dev_groups = onboard_dev_groups,
+ },
+};
+
+/************************** USB driver **************************/
+
+#define VENDOR_ID_CYPRESS 0x04b4
+#define VENDOR_ID_GENESYS 0x05e3
+#define VENDOR_ID_MICROCHIP 0x0424
+#define VENDOR_ID_REALTEK 0x0bda
+#define VENDOR_ID_TI 0x0451
+#define VENDOR_ID_VIA 0x2109
+#define VENDOR_ID_XMOS 0x20B1
+
+/*
+ * Returns the onboard_dev platform device that is associated with the USB
+ * device passed as parameter.
+ */
+static struct onboard_dev *_find_onboard_dev(struct device *dev)
+{
+ struct platform_device *pdev;
+ struct device_node *np;
+ struct onboard_dev *onboard_dev;
+
+ pdev = of_find_device_by_node(dev->of_node);
+ if (!pdev) {
+ np = of_parse_phandle(dev->of_node, "peer-hub", 0);
+ if (!np) {
+ dev_err(dev, "failed to find device node for peer hub\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ pdev = of_find_device_by_node(np);
+ of_node_put(np);
+
+ if (!pdev)
+ return ERR_PTR(-ENODEV);
+ }
+
+ onboard_dev = dev_get_drvdata(&pdev->dev);
+ put_device(&pdev->dev);
+
+ /*
+ * The presence of drvdata indicates that the platform driver finished
+ * probing. This handles the case where (conceivably) we could be
+ * running at the exact same time as the platform driver's probe. If
+ * we detect the race we request probe deferral and we'll come back and
+ * try again.
+ */
+ if (!onboard_dev)
+ return ERR_PTR(-EPROBE_DEFER);
+
+ return onboard_dev;
+}
+
+static bool onboard_dev_usbdev_match(struct usb_device *udev)
+{
+ /* Onboard devices using this driver must have a device tree node */
+ return !!udev->dev.of_node;
+}
+
+static int onboard_dev_usbdev_probe(struct usb_device *udev)
+{
+ struct device *dev = &udev->dev;
+ struct onboard_dev *onboard_dev;
+ int err;
+
+ onboard_dev = _find_onboard_dev(dev);
+ if (IS_ERR(onboard_dev))
+ return PTR_ERR(onboard_dev);
+
+ dev_set_drvdata(dev, onboard_dev);
+
+ err = onboard_dev_add_usbdev(onboard_dev, udev);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static void onboard_dev_usbdev_disconnect(struct usb_device *udev)
+{
+ struct onboard_dev *onboard_dev = dev_get_drvdata(&udev->dev);
+
+ onboard_dev_remove_usbdev(onboard_dev, udev);
+}
+
+static const struct usb_device_id onboard_dev_id_table[] = {
+ { USB_DEVICE(VENDOR_ID_CYPRESS, 0x6504) }, /* CYUSB33{0,1,2}x/CYUSB230x 3.0 HUB */
+ { USB_DEVICE(VENDOR_ID_CYPRESS, 0x6506) }, /* CYUSB33{0,1,2}x/CYUSB230x 2.0 HUB */
+ { USB_DEVICE(VENDOR_ID_CYPRESS, 0x6570) }, /* CY7C6563x 2.0 HUB */
+ { USB_DEVICE(VENDOR_ID_GENESYS, 0x0608) }, /* Genesys Logic GL850G USB 2.0 HUB */
+ { USB_DEVICE(VENDOR_ID_GENESYS, 0x0610) }, /* Genesys Logic GL852G USB 2.0 HUB */
+ { USB_DEVICE(VENDOR_ID_GENESYS, 0x0620) }, /* Genesys Logic GL3523 USB 3.1 HUB */
+ { USB_DEVICE(VENDOR_ID_MICROCHIP, 0x2412) }, /* USB2412 USB 2.0 HUB */
+ { USB_DEVICE(VENDOR_ID_MICROCHIP, 0x2514) }, /* USB2514B USB 2.0 HUB */
+ { USB_DEVICE(VENDOR_ID_MICROCHIP, 0x2517) }, /* USB2517 USB 2.0 HUB */
+ { USB_DEVICE(VENDOR_ID_MICROCHIP, 0x2744) }, /* USB5744 USB 2.0 HUB */
+ { USB_DEVICE(VENDOR_ID_MICROCHIP, 0x5744) }, /* USB5744 USB 3.0 HUB */
+ { USB_DEVICE(VENDOR_ID_REALTEK, 0x0411) }, /* RTS5411 USB 3.1 HUB */
+ { USB_DEVICE(VENDOR_ID_REALTEK, 0x5411) }, /* RTS5411 USB 2.1 HUB */
+ { USB_DEVICE(VENDOR_ID_REALTEK, 0x0414) }, /* RTS5414 USB 3.2 HUB */
+ { USB_DEVICE(VENDOR_ID_REALTEK, 0x5414) }, /* RTS5414 USB 2.1 HUB */
+ { USB_DEVICE(VENDOR_ID_TI, 0x8025) }, /* TI USB8020B 3.0 HUB */
+ { USB_DEVICE(VENDOR_ID_TI, 0x8027) }, /* TI USB8020B 2.0 HUB */
+ { USB_DEVICE(VENDOR_ID_TI, 0x8140) }, /* TI USB8041 3.0 HUB */
+ { USB_DEVICE(VENDOR_ID_TI, 0x8142) }, /* TI USB8041 2.0 HUB */
+ { USB_DEVICE(VENDOR_ID_VIA, 0x0817) }, /* VIA VL817 3.1 HUB */
+ { USB_DEVICE(VENDOR_ID_VIA, 0x2817) }, /* VIA VL817 2.0 HUB */
+ { USB_DEVICE(VENDOR_ID_XMOS, 0x0013) }, /* XMOS XVF3500 Voice Processor */
+ {}
+};
+MODULE_DEVICE_TABLE(usb, onboard_dev_id_table);
+
+static struct usb_device_driver onboard_dev_usbdev_driver = {
+ .name = "onboard-usb-dev",
+ .match = onboard_dev_usbdev_match,
+ .probe = onboard_dev_usbdev_probe,
+ .disconnect = onboard_dev_usbdev_disconnect,
+ .generic_subclass = 1,
+ .supports_autosuspend = 1,
+ .id_table = onboard_dev_id_table,
+};
+
+static int __init onboard_dev_init(void)
+{
+ int ret;
+
+ ret = usb_register_device_driver(&onboard_dev_usbdev_driver, THIS_MODULE);
+ if (ret)
+ return ret;
+
+ ret = platform_driver_register(&onboard_dev_driver);
+ if (ret)
+ usb_deregister_device_driver(&onboard_dev_usbdev_driver);
+
+ return ret;
+}
+module_init(onboard_dev_init);
+
+static void __exit onboard_dev_exit(void)
+{
+ usb_deregister_device_driver(&onboard_dev_usbdev_driver);
+ platform_driver_unregister(&onboard_dev_driver);
+
+ cancel_work_sync(&attach_usb_driver_work);
+}
+module_exit(onboard_dev_exit);
+
+MODULE_AUTHOR("Matthias Kaehlcke <mka@chromium.org>");
+MODULE_DESCRIPTION("Driver for discrete onboard USB devices");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/misc/onboard_usb_hub.h b/drivers/usb/misc/onboard_usb_dev.h
index b4b15d45f84d..317b3eb99c02 100644
--- a/drivers/usb/misc/onboard_usb_hub.h
+++ b/drivers/usb/misc/onboard_usb_dev.h
@@ -3,65 +3,98 @@
* Copyright (c) 2022, Google LLC
*/
-#ifndef _USB_MISC_ONBOARD_USB_HUB_H
-#define _USB_MISC_ONBOARD_USB_HUB_H
+#ifndef _USB_MISC_ONBOARD_USB_DEV_H
+#define _USB_MISC_ONBOARD_USB_DEV_H
-struct onboard_hub_pdata {
+#define MAX_SUPPLIES 2
+
+struct onboard_dev_pdata {
unsigned long reset_us; /* reset pulse width in us */
+ unsigned long power_on_delay_us; /* power on delay in us */
unsigned int num_supplies; /* number of supplies */
+ const char * const supply_names[MAX_SUPPLIES];
+ bool is_hub;
};
-static const struct onboard_hub_pdata microchip_usb424_data = {
+static const struct onboard_dev_pdata microchip_usb424_data = {
.reset_us = 1,
.num_supplies = 1,
+ .supply_names = { "vdd" },
+ .is_hub = true,
};
-static const struct onboard_hub_pdata microchip_usb5744_data = {
+static const struct onboard_dev_pdata microchip_usb5744_data = {
.reset_us = 0,
+ .power_on_delay_us = 10000,
.num_supplies = 2,
+ .supply_names = { "vdd", "vdd2" },
+ .is_hub = true,
};
-static const struct onboard_hub_pdata realtek_rts5411_data = {
+static const struct onboard_dev_pdata realtek_rts5411_data = {
.reset_us = 0,
.num_supplies = 1,
+ .supply_names = { "vdd" },
+ .is_hub = true,
};
-static const struct onboard_hub_pdata ti_tusb8020b_data = {
+static const struct onboard_dev_pdata ti_tusb8020b_data = {
.reset_us = 3000,
.num_supplies = 1,
+ .supply_names = { "vdd" },
+ .is_hub = true,
};
-static const struct onboard_hub_pdata ti_tusb8041_data = {
+static const struct onboard_dev_pdata ti_tusb8041_data = {
.reset_us = 3000,
.num_supplies = 1,
+ .supply_names = { "vdd" },
+ .is_hub = true,
};
-static const struct onboard_hub_pdata cypress_hx3_data = {
+static const struct onboard_dev_pdata cypress_hx3_data = {
.reset_us = 10000,
.num_supplies = 2,
+ .supply_names = { "vdd", "vdd2" },
+ .is_hub = true,
};
-static const struct onboard_hub_pdata cypress_hx2vl_data = {
+static const struct onboard_dev_pdata cypress_hx2vl_data = {
.reset_us = 1,
.num_supplies = 1,
+ .supply_names = { "vdd" },
+ .is_hub = true,
};
-static const struct onboard_hub_pdata genesys_gl850g_data = {
+static const struct onboard_dev_pdata genesys_gl850g_data = {
.reset_us = 3,
.num_supplies = 1,
+ .supply_names = { "vdd" },
+ .is_hub = true,
};
-static const struct onboard_hub_pdata genesys_gl852g_data = {
+static const struct onboard_dev_pdata genesys_gl852g_data = {
.reset_us = 50,
.num_supplies = 1,
+ .supply_names = { "vdd" },
+ .is_hub = true,
};
-static const struct onboard_hub_pdata vialab_vl817_data = {
+static const struct onboard_dev_pdata vialab_vl817_data = {
.reset_us = 10,
.num_supplies = 1,
+ .supply_names = { "vdd" },
+ .is_hub = true,
+};
+
+static const struct onboard_dev_pdata xmos_xvf3500_data = {
+ .reset_us = 1,
+ .num_supplies = 2,
+ .supply_names = { "vdd", "vddio" },
+ .is_hub = false,
};
-static const struct of_device_id onboard_hub_match[] = {
+static const struct of_device_id onboard_dev_match[] = {
{ .compatible = "usb424,2412", .data = &microchip_usb424_data, },
{ .compatible = "usb424,2514", .data = &microchip_usb424_data, },
{ .compatible = "usb424,2517", .data = &microchip_usb424_data, },
@@ -84,7 +117,8 @@ static const struct of_device_id onboard_hub_match[] = {
{ .compatible = "usbbda,5414", .data = &realtek_rts5411_data, },
{ .compatible = "usb2109,817", .data = &vialab_vl817_data, },
{ .compatible = "usb2109,2817", .data = &vialab_vl817_data, },
+ { .compatible = "usb20b1,0013", .data = &xmos_xvf3500_data, },
{}
};
-#endif /* _USB_MISC_ONBOARD_USB_HUB_H */
+#endif /* _USB_MISC_ONBOARD_USB_DEV_H */
diff --git a/drivers/usb/misc/onboard_usb_hub_pdevs.c b/drivers/usb/misc/onboard_usb_dev_pdevs.c
index ed22a18f4ab7..722504752ce3 100644
--- a/drivers/usb/misc/onboard_usb_hub_pdevs.c
+++ b/drivers/usb/misc/onboard_usb_dev_pdevs.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * API for creating and destroying USB onboard hub platform devices
+ * API for creating and destroying USB onboard platform devices
*
* Copyright (c) 2022, Google LLC
*/
@@ -15,29 +15,30 @@
#include <linux/usb.h>
#include <linux/usb/hcd.h>
#include <linux/usb/of.h>
-#include <linux/usb/onboard_hub.h>
+#include <linux/usb/onboard_dev.h>
-#include "onboard_usb_hub.h"
+#include "onboard_usb_dev.h"
struct pdev_list_entry {
struct platform_device *pdev;
struct list_head node;
};
-static bool of_is_onboard_usb_hub(const struct device_node *np)
+static bool of_is_onboard_usb_dev(struct device_node *np)
{
- return !!of_match_node(onboard_hub_match, np);
+ return !!of_match_node(onboard_dev_match, np);
}
/**
- * onboard_hub_create_pdevs -- create platform devices for onboard USB hubs
- * @parent_hub : parent hub to scan for connected onboard hubs
- * @pdev_list : list of onboard hub platform devices owned by the parent hub
+ * onboard_dev_create_pdevs -- create platform devices for onboard USB devices
+ * @parent_hub : parent hub to scan for connected onboard devices
+ * @pdev_list : list of onboard platform devices owned by the parent hub
*
- * Creates a platform device for each supported onboard hub that is connected to
- * the given parent hub. The platform device is in charge of initializing the
- * hub (enable regulators, take the hub out of reset, ...) and can optionally
- * control whether the hub remains powered during system suspend or not.
+ * Creates a platform device for each supported onboard device that is connected
+ * to the given parent hub. The platform device is in charge of initializing the
+ * device (enable regulators, take the device out of reset, ...). For onboard
+ * hubs, it can optionally control whether the device remains powered during
+ * system suspend or not.
*
* To keep track of the platform devices they are added to a list that is owned
* by the parent hub.
@@ -50,9 +51,9 @@ static bool of_is_onboard_usb_hub(const struct device_node *np)
* node. That means the root hubs of the primary and secondary HCD share the
* same device tree node (the HCD node). As a result this function can be called
* twice with the same DT node for root hubs. We only want to create a single
- * platform device for each physical onboard hub, hence for root hubs the loop
- * is only executed for the root hub of the primary HCD. Since the function
- * scans through all child nodes it still creates pdevs for onboard hubs
+ * platform device for each physical onboard device, hence for root hubs the
+ * loop is only executed for the root hub of the primary HCD. Since the function
+ * scans through all child nodes it still creates pdevs for onboard devices
* connected to the root hub of the secondary HCD if needed.
*
* Further there must be only one platform device for onboard hubs with a peer
@@ -63,7 +64,7 @@ static bool of_is_onboard_usb_hub(const struct device_node *np)
* the function processes the nodes of both peers. A platform device is only
* created if the peer hub doesn't have one already.
*/
-void onboard_hub_create_pdevs(struct usb_device *parent_hub, struct list_head *pdev_list)
+void onboard_dev_create_pdevs(struct usb_device *parent_hub, struct list_head *pdev_list)
{
int i;
struct usb_hcd *hcd = bus_to_hcd(parent_hub->bus);
@@ -82,7 +83,7 @@ void onboard_hub_create_pdevs(struct usb_device *parent_hub, struct list_head *p
if (!np)
continue;
- if (!of_is_onboard_usb_hub(np))
+ if (!of_is_onboard_usb_dev(np))
goto node_put;
npc = of_parse_phandle(np, "peer-hub", 0);
@@ -104,7 +105,7 @@ void onboard_hub_create_pdevs(struct usb_device *parent_hub, struct list_head *p
pdev = of_platform_device_create(np, NULL, &parent_hub->dev);
if (!pdev) {
dev_err(&parent_hub->dev,
- "failed to create platform device for onboard hub '%pOF'\n", np);
+ "failed to create platform device for onboard dev '%pOF'\n", np);
goto node_put;
}
@@ -121,16 +122,16 @@ node_put:
of_node_put(np);
}
}
-EXPORT_SYMBOL_GPL(onboard_hub_create_pdevs);
+EXPORT_SYMBOL_GPL(onboard_dev_create_pdevs);
/**
- * onboard_hub_destroy_pdevs -- free resources of onboard hub platform devices
- * @pdev_list : list of onboard hub platform devices
+ * onboard_dev_destroy_pdevs -- free resources of onboard platform devices
+ * @pdev_list : list of onboard platform devices
*
* Destroys the platform devices in the given list and frees the memory associated
* with the list entry.
*/
-void onboard_hub_destroy_pdevs(struct list_head *pdev_list)
+void onboard_dev_destroy_pdevs(struct list_head *pdev_list)
{
struct pdev_list_entry *pdle, *tmp;
@@ -140,4 +141,4 @@ void onboard_hub_destroy_pdevs(struct list_head *pdev_list)
kfree(pdle);
}
}
-EXPORT_SYMBOL_GPL(onboard_hub_destroy_pdevs);
+EXPORT_SYMBOL_GPL(onboard_dev_destroy_pdevs);
diff --git a/drivers/usb/misc/onboard_usb_hub.c b/drivers/usb/misc/onboard_usb_hub.c
deleted file mode 100644
index d8049275a023..000000000000
--- a/drivers/usb/misc/onboard_usb_hub.c
+++ /dev/null
@@ -1,507 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Driver for onboard USB hubs
- *
- * Copyright (c) 2022, Google LLC
- */
-
-#include <linux/clk.h>
-#include <linux/device.h>
-#include <linux/export.h>
-#include <linux/err.h>
-#include <linux/gpio/consumer.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/module.h>
-#include <linux/mutex.h>
-#include <linux/of.h>
-#include <linux/of_platform.h>
-#include <linux/platform_device.h>
-#include <linux/regulator/consumer.h>
-#include <linux/slab.h>
-#include <linux/suspend.h>
-#include <linux/sysfs.h>
-#include <linux/usb.h>
-#include <linux/usb/hcd.h>
-#include <linux/usb/onboard_hub.h>
-#include <linux/workqueue.h>
-
-#include "onboard_usb_hub.h"
-
-/*
- * Use generic names, as the actual names might differ between hubs. If a new
- * hub requires more than the currently supported supplies, add a new one here.
- */
-static const char * const supply_names[] = {
- "vdd",
- "vdd2",
-};
-
-#define MAX_SUPPLIES ARRAY_SIZE(supply_names)
-
-static void onboard_hub_attach_usb_driver(struct work_struct *work);
-
-static struct usb_device_driver onboard_hub_usbdev_driver;
-static DECLARE_WORK(attach_usb_driver_work, onboard_hub_attach_usb_driver);
-
-/************************** Platform driver **************************/
-
-struct usbdev_node {
- struct usb_device *udev;
- struct list_head list;
-};
-
-struct onboard_hub {
- struct regulator_bulk_data supplies[MAX_SUPPLIES];
- struct device *dev;
- const struct onboard_hub_pdata *pdata;
- struct gpio_desc *reset_gpio;
- bool always_powered_in_suspend;
- bool is_powered_on;
- bool going_away;
- struct list_head udev_list;
- struct mutex lock;
- struct clk *clk;
-};
-
-static int onboard_hub_power_on(struct onboard_hub *hub)
-{
- int err;
-
- err = clk_prepare_enable(hub->clk);
- if (err) {
- dev_err(hub->dev, "failed to enable clock: %pe\n", ERR_PTR(err));
- return err;
- }
-
- err = regulator_bulk_enable(hub->pdata->num_supplies, hub->supplies);
- if (err) {
- dev_err(hub->dev, "failed to enable supplies: %pe\n", ERR_PTR(err));
- goto disable_clk;
- }
-
- fsleep(hub->pdata->reset_us);
- gpiod_set_value_cansleep(hub->reset_gpio, 0);
-
- hub->is_powered_on = true;
-
- return 0;
-
-disable_clk:
- clk_disable_unprepare(hub->clk);
- return err;
-}
-
-static int onboard_hub_power_off(struct onboard_hub *hub)
-{
- int err;
-
- gpiod_set_value_cansleep(hub->reset_gpio, 1);
-
- err = regulator_bulk_disable(hub->pdata->num_supplies, hub->supplies);
- if (err) {
- dev_err(hub->dev, "failed to disable supplies: %pe\n", ERR_PTR(err));
- return err;
- }
-
- clk_disable_unprepare(hub->clk);
-
- hub->is_powered_on = false;
-
- return 0;
-}
-
-static int __maybe_unused onboard_hub_suspend(struct device *dev)
-{
- struct onboard_hub *hub = dev_get_drvdata(dev);
- struct usbdev_node *node;
- bool power_off = true;
-
- if (hub->always_powered_in_suspend)
- return 0;
-
- mutex_lock(&hub->lock);
-
- list_for_each_entry(node, &hub->udev_list, list) {
- if (!device_may_wakeup(node->udev->bus->controller))
- continue;
-
- if (usb_wakeup_enabled_descendants(node->udev)) {
- power_off = false;
- break;
- }
- }
-
- mutex_unlock(&hub->lock);
-
- if (!power_off)
- return 0;
-
- return onboard_hub_power_off(hub);
-}
-
-static int __maybe_unused onboard_hub_resume(struct device *dev)
-{
- struct onboard_hub *hub = dev_get_drvdata(dev);
-
- if (hub->is_powered_on)
- return 0;
-
- return onboard_hub_power_on(hub);
-}
-
-static inline void get_udev_link_name(const struct usb_device *udev, char *buf, size_t size)
-{
- snprintf(buf, size, "usb_dev.%s", dev_name(&udev->dev));
-}
-
-static int onboard_hub_add_usbdev(struct onboard_hub *hub, struct usb_device *udev)
-{
- struct usbdev_node *node;
- char link_name[64];
- int err;
-
- mutex_lock(&hub->lock);
-
- if (hub->going_away) {
- err = -EINVAL;
- goto error;
- }
-
- node = kzalloc(sizeof(*node), GFP_KERNEL);
- if (!node) {
- err = -ENOMEM;
- goto error;
- }
-
- node->udev = udev;
-
- list_add(&node->list, &hub->udev_list);
-
- mutex_unlock(&hub->lock);
-
- get_udev_link_name(udev, link_name, sizeof(link_name));
- WARN_ON(sysfs_create_link(&hub->dev->kobj, &udev->dev.kobj, link_name));
-
- return 0;
-
-error:
- mutex_unlock(&hub->lock);
-
- return err;
-}
-
-static void onboard_hub_remove_usbdev(struct onboard_hub *hub, const struct usb_device *udev)
-{
- struct usbdev_node *node;
- char link_name[64];
-
- get_udev_link_name(udev, link_name, sizeof(link_name));
- sysfs_remove_link(&hub->dev->kobj, link_name);
-
- mutex_lock(&hub->lock);
-
- list_for_each_entry(node, &hub->udev_list, list) {
- if (node->udev == udev) {
- list_del(&node->list);
- kfree(node);
- break;
- }
- }
-
- mutex_unlock(&hub->lock);
-}
-
-static ssize_t always_powered_in_suspend_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- const struct onboard_hub *hub = dev_get_drvdata(dev);
-
- return sysfs_emit(buf, "%d\n", hub->always_powered_in_suspend);
-}
-
-static ssize_t always_powered_in_suspend_store(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct onboard_hub *hub = dev_get_drvdata(dev);
- bool val;
- int ret;
-
- ret = kstrtobool(buf, &val);
- if (ret < 0)
- return ret;
-
- hub->always_powered_in_suspend = val;
-
- return count;
-}
-static DEVICE_ATTR_RW(always_powered_in_suspend);
-
-static struct attribute *onboard_hub_attrs[] = {
- &dev_attr_always_powered_in_suspend.attr,
- NULL,
-};
-ATTRIBUTE_GROUPS(onboard_hub);
-
-static void onboard_hub_attach_usb_driver(struct work_struct *work)
-{
- int err;
-
- err = driver_attach(&onboard_hub_usbdev_driver.driver);
- if (err)
- pr_err("Failed to attach USB driver: %pe\n", ERR_PTR(err));
-}
-
-static int onboard_hub_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct onboard_hub *hub;
- unsigned int i;
- int err;
-
- hub = devm_kzalloc(dev, sizeof(*hub), GFP_KERNEL);
- if (!hub)
- return -ENOMEM;
-
- hub->pdata = device_get_match_data(dev);
- if (!hub->pdata)
- return -EINVAL;
-
- if (hub->pdata->num_supplies > MAX_SUPPLIES)
- return dev_err_probe(dev, -EINVAL, "max %zu supplies supported!\n",
- MAX_SUPPLIES);
-
- for (i = 0; i < hub->pdata->num_supplies; i++)
- hub->supplies[i].supply = supply_names[i];
-
- err = devm_regulator_bulk_get(dev, hub->pdata->num_supplies, hub->supplies);
- if (err) {
- dev_err(dev, "Failed to get regulator supplies: %pe\n", ERR_PTR(err));
- return err;
- }
-
- hub->clk = devm_clk_get_optional(dev, NULL);
- if (IS_ERR(hub->clk))
- return dev_err_probe(dev, PTR_ERR(hub->clk), "failed to get clock\n");
-
- hub->reset_gpio = devm_gpiod_get_optional(dev, "reset",
- GPIOD_OUT_HIGH);
- if (IS_ERR(hub->reset_gpio))
- return dev_err_probe(dev, PTR_ERR(hub->reset_gpio), "failed to get reset GPIO\n");
-
- hub->dev = dev;
- mutex_init(&hub->lock);
- INIT_LIST_HEAD(&hub->udev_list);
-
- dev_set_drvdata(dev, hub);
-
- err = onboard_hub_power_on(hub);
- if (err)
- return err;
-
- /*
- * The USB driver might have been detached from the USB devices by
- * onboard_hub_remove() (e.g. through an 'unbind' by userspace),
- * make sure to re-attach it if needed.
- *
- * This needs to be done deferred to avoid self-deadlocks on systems
- * with nested onboard hubs.
- */
- schedule_work(&attach_usb_driver_work);
-
- return 0;
-}
-
-static void onboard_hub_remove(struct platform_device *pdev)
-{
- struct onboard_hub *hub = dev_get_drvdata(&pdev->dev);
- struct usbdev_node *node;
- struct usb_device *udev;
-
- hub->going_away = true;
-
- mutex_lock(&hub->lock);
-
- /* unbind the USB devices to avoid dangling references to this device */
- while (!list_empty(&hub->udev_list)) {
- node = list_first_entry(&hub->udev_list, struct usbdev_node, list);
- udev = node->udev;
-
- /*
- * Unbinding the driver will call onboard_hub_remove_usbdev(),
- * which acquires hub->lock. We must release the lock first.
- */
- get_device(&udev->dev);
- mutex_unlock(&hub->lock);
- device_release_driver(&udev->dev);
- put_device(&udev->dev);
- mutex_lock(&hub->lock);
- }
-
- mutex_unlock(&hub->lock);
-
- onboard_hub_power_off(hub);
-}
-
-MODULE_DEVICE_TABLE(of, onboard_hub_match);
-
-static const struct dev_pm_ops __maybe_unused onboard_hub_pm_ops = {
- SET_LATE_SYSTEM_SLEEP_PM_OPS(onboard_hub_suspend, onboard_hub_resume)
-};
-
-static struct platform_driver onboard_hub_driver = {
- .probe = onboard_hub_probe,
- .remove_new = onboard_hub_remove,
-
- .driver = {
- .name = "onboard-usb-hub",
- .of_match_table = onboard_hub_match,
- .pm = pm_ptr(&onboard_hub_pm_ops),
- .dev_groups = onboard_hub_groups,
- },
-};
-
-/************************** USB driver **************************/
-
-#define VENDOR_ID_CYPRESS 0x04b4
-#define VENDOR_ID_GENESYS 0x05e3
-#define VENDOR_ID_MICROCHIP 0x0424
-#define VENDOR_ID_REALTEK 0x0bda
-#define VENDOR_ID_TI 0x0451
-#define VENDOR_ID_VIA 0x2109
-
-/*
- * Returns the onboard_hub platform device that is associated with the USB
- * device passed as parameter.
- */
-static struct onboard_hub *_find_onboard_hub(struct device *dev)
-{
- struct platform_device *pdev;
- struct device_node *np;
- struct onboard_hub *hub;
-
- pdev = of_find_device_by_node(dev->of_node);
- if (!pdev) {
- np = of_parse_phandle(dev->of_node, "peer-hub", 0);
- if (!np) {
- dev_err(dev, "failed to find device node for peer hub\n");
- return ERR_PTR(-EINVAL);
- }
-
- pdev = of_find_device_by_node(np);
- of_node_put(np);
-
- if (!pdev)
- return ERR_PTR(-ENODEV);
- }
-
- hub = dev_get_drvdata(&pdev->dev);
- put_device(&pdev->dev);
-
- /*
- * The presence of drvdata ('hub') indicates that the platform driver
- * finished probing. This handles the case where (conceivably) we could
- * be running at the exact same time as the platform driver's probe. If
- * we detect the race we request probe deferral and we'll come back and
- * try again.
- */
- if (!hub)
- return ERR_PTR(-EPROBE_DEFER);
-
- return hub;
-}
-
-static int onboard_hub_usbdev_probe(struct usb_device *udev)
-{
- struct device *dev = &udev->dev;
- struct onboard_hub *hub;
- int err;
-
- /* ignore supported hubs without device tree node */
- if (!dev->of_node)
- return -ENODEV;
-
- hub = _find_onboard_hub(dev);
- if (IS_ERR(hub))
- return PTR_ERR(hub);
-
- dev_set_drvdata(dev, hub);
-
- err = onboard_hub_add_usbdev(hub, udev);
- if (err)
- return err;
-
- return 0;
-}
-
-static void onboard_hub_usbdev_disconnect(struct usb_device *udev)
-{
- struct onboard_hub *hub = dev_get_drvdata(&udev->dev);
-
- onboard_hub_remove_usbdev(hub, udev);
-}
-
-static const struct usb_device_id onboard_hub_id_table[] = {
- { USB_DEVICE(VENDOR_ID_CYPRESS, 0x6504) }, /* CYUSB33{0,1,2}x/CYUSB230x 3.0 */
- { USB_DEVICE(VENDOR_ID_CYPRESS, 0x6506) }, /* CYUSB33{0,1,2}x/CYUSB230x 2.0 */
- { USB_DEVICE(VENDOR_ID_CYPRESS, 0x6570) }, /* CY7C6563x 2.0 */
- { USB_DEVICE(VENDOR_ID_GENESYS, 0x0608) }, /* Genesys Logic GL850G USB 2.0 */
- { USB_DEVICE(VENDOR_ID_GENESYS, 0x0610) }, /* Genesys Logic GL852G USB 2.0 */
- { USB_DEVICE(VENDOR_ID_GENESYS, 0x0620) }, /* Genesys Logic GL3523 USB 3.1 */
- { USB_DEVICE(VENDOR_ID_MICROCHIP, 0x2412) }, /* USB2412 USB 2.0 */
- { USB_DEVICE(VENDOR_ID_MICROCHIP, 0x2514) }, /* USB2514B USB 2.0 */
- { USB_DEVICE(VENDOR_ID_MICROCHIP, 0x2517) }, /* USB2517 USB 2.0 */
- { USB_DEVICE(VENDOR_ID_MICROCHIP, 0x2744) }, /* USB5744 USB 2.0 */
- { USB_DEVICE(VENDOR_ID_MICROCHIP, 0x5744) }, /* USB5744 USB 3.0 */
- { USB_DEVICE(VENDOR_ID_REALTEK, 0x0411) }, /* RTS5411 USB 3.1 */
- { USB_DEVICE(VENDOR_ID_REALTEK, 0x5411) }, /* RTS5411 USB 2.1 */
- { USB_DEVICE(VENDOR_ID_REALTEK, 0x0414) }, /* RTS5414 USB 3.2 */
- { USB_DEVICE(VENDOR_ID_REALTEK, 0x5414) }, /* RTS5414 USB 2.1 */
- { USB_DEVICE(VENDOR_ID_TI, 0x8025) }, /* TI USB8020B 3.0 */
- { USB_DEVICE(VENDOR_ID_TI, 0x8027) }, /* TI USB8020B 2.0 */
- { USB_DEVICE(VENDOR_ID_TI, 0x8140) }, /* TI USB8041 3.0 */
- { USB_DEVICE(VENDOR_ID_TI, 0x8142) }, /* TI USB8041 2.0 */
- { USB_DEVICE(VENDOR_ID_VIA, 0x0817) }, /* VIA VL817 3.1 */
- { USB_DEVICE(VENDOR_ID_VIA, 0x2817) }, /* VIA VL817 2.0 */
- {}
-};
-MODULE_DEVICE_TABLE(usb, onboard_hub_id_table);
-
-static struct usb_device_driver onboard_hub_usbdev_driver = {
- .name = "onboard-usb-hub",
- .probe = onboard_hub_usbdev_probe,
- .disconnect = onboard_hub_usbdev_disconnect,
- .generic_subclass = 1,
- .supports_autosuspend = 1,
- .id_table = onboard_hub_id_table,
-};
-
-static int __init onboard_hub_init(void)
-{
- int ret;
-
- ret = usb_register_device_driver(&onboard_hub_usbdev_driver, THIS_MODULE);
- if (ret)
- return ret;
-
- ret = platform_driver_register(&onboard_hub_driver);
- if (ret)
- usb_deregister_device_driver(&onboard_hub_usbdev_driver);
-
- return ret;
-}
-module_init(onboard_hub_init);
-
-static void __exit onboard_hub_exit(void)
-{
- usb_deregister_device_driver(&onboard_hub_usbdev_driver);
- platform_driver_unregister(&onboard_hub_driver);
-
- cancel_work_sync(&attach_usb_driver_work);
-}
-module_exit(onboard_hub_exit);
-
-MODULE_AUTHOR("Matthias Kaehlcke <mka@chromium.org>");
-MODULE_DESCRIPTION("Driver for discrete onboard USB hubs");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/misc/qcom_eud.c b/drivers/usb/misc/qcom_eud.c
index 26e9b8749d8a..19906301a4eb 100644
--- a/drivers/usb/misc/qcom_eud.c
+++ b/drivers/usb/misc/qcom_eud.c
@@ -232,7 +232,7 @@ static void eud_remove(struct platform_device *pdev)
}
static const struct of_device_id eud_dt_match[] = {
- { .compatible = "qcom,sc7280-eud" },
+ { .compatible = "qcom,eud" },
{ }
};
MODULE_DEVICE_TABLE(of, eud_dt_match);
diff --git a/drivers/usb/misc/usb-ljca.c b/drivers/usb/misc/usb-ljca.c
index 2d30fc1be306..01ceafc4ab78 100644
--- a/drivers/usb/misc/usb-ljca.c
+++ b/drivers/usb/misc/usb-ljca.c
@@ -18,7 +18,7 @@
#include <linux/usb.h>
#include <linux/usb/ljca.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
/* command flags */
#define LJCA_ACK_FLAG BIT(0)
@@ -169,6 +169,7 @@ static const struct acpi_device_id ljca_gpio_hids[] = {
{ "INTC1096" },
{ "INTC100B" },
{ "INTC10D1" },
+ { "INTC10B5" },
{},
};
diff --git a/drivers/usb/misc/usb251xb.c b/drivers/usb/misc/usb251xb.c
index 7da404f55a6d..e24cdb667307 100644
--- a/drivers/usb/misc/usb251xb.c
+++ b/drivers/usb/misc/usb251xb.c
@@ -382,11 +382,9 @@ static void usb251xb_get_ports_field(struct usb251xb *hub,
bool ds_only, u8 *fld)
{
struct device *dev = hub->dev;
- struct property *prop;
- const __be32 *p;
u32 port;
- of_property_for_each_u32(dev->of_node, prop_name, prop, p, port) {
+ of_property_for_each_u32(dev->of_node, prop_name, port) {
if ((port >= ds_only ? 1 : 0) && (port <= port_cnt))
*fld |= BIT(port);
else
@@ -726,15 +724,15 @@ static int __maybe_unused usb251xb_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(usb251xb_pm_ops, usb251xb_suspend, usb251xb_resume);
static const struct i2c_device_id usb251xb_id[] = {
- { "usb2422", 0 },
- { "usb2512b", 0 },
- { "usb2512bi", 0 },
- { "usb2513b", 0 },
- { "usb2513bi", 0 },
- { "usb2514b", 0 },
- { "usb2514bi", 0 },
- { "usb2517", 0 },
- { "usb2517i", 0 },
+ { "usb2422" },
+ { "usb2512b" },
+ { "usb2512bi" },
+ { "usb2513b" },
+ { "usb2513bi" },
+ { "usb2514b" },
+ { "usb2514bi" },
+ { "usb2517" },
+ { "usb2517i" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(i2c, usb251xb_id);
diff --git a/drivers/usb/misc/usb3503.c b/drivers/usb/misc/usb3503.c
index 72765077932c..3b33e4878c60 100644
--- a/drivers/usb/misc/usb3503.c
+++ b/drivers/usb/misc/usb3503.c
@@ -390,7 +390,7 @@ static SIMPLE_DEV_PM_OPS(usb3503_platform_pm_ops, usb3503_platform_suspend,
usb3503_platform_resume);
static const struct i2c_device_id usb3503_id[] = {
- { USB3503_I2C_NAME, 0 },
+ { USB3503_I2C_NAME },
{ }
};
MODULE_DEVICE_TABLE(i2c, usb3503_id);
diff --git a/drivers/usb/misc/usb4604.c b/drivers/usb/misc/usb4604.c
index 065e269ba4e3..c9a2fb3518ae 100644
--- a/drivers/usb/misc/usb4604.c
+++ b/drivers/usb/misc/usb4604.c
@@ -135,7 +135,7 @@ static SIMPLE_DEV_PM_OPS(usb4604_i2c_pm_ops, usb4604_i2c_suspend,
usb4604_i2c_resume);
static const struct i2c_device_id usb4604_id[] = {
- { "usb4604", 0 },
+ { "usb4604" },
{ }
};
MODULE_DEVICE_TABLE(i2c, usb4604_id);
diff --git a/drivers/usb/misc/uss720.c b/drivers/usb/misc/uss720.c
index b00d92db5dfd..b26c1d382d59 100644
--- a/drivers/usb/misc/uss720.c
+++ b/drivers/usb/misc/uss720.c
@@ -677,7 +677,7 @@ static int uss720_probe(struct usb_interface *intf,
struct parport_uss720_private *priv;
struct parport *pp;
unsigned char reg;
- int i;
+ int ret;
dev_dbg(&intf->dev, "probe: vendor id 0x%x, device id 0x%x\n",
le16_to_cpu(usbdev->descriptor.idVendor),
@@ -688,12 +688,12 @@ static int uss720_probe(struct usb_interface *intf,
usb_put_dev(usbdev);
return -ENODEV;
}
- i = usb_set_interface(usbdev, intf->altsetting->desc.bInterfaceNumber, 2);
- dev_dbg(&intf->dev, "set interface result %d\n", i);
+ ret = usb_set_interface(usbdev, intf->altsetting->desc.bInterfaceNumber, 2);
+ dev_dbg(&intf->dev, "set interface result %d\n", ret);
interface = intf->cur_altsetting;
- if (interface->desc.bNumEndpoints < 3) {
+ if (interface->desc.bNumEndpoints < 2) {
usb_put_dev(usbdev);
return -ENODEV;
}
@@ -719,18 +719,27 @@ static int uss720_probe(struct usb_interface *intf,
priv->pp = pp;
pp->private_data = priv;
- pp->modes = PARPORT_MODE_PCSPP | PARPORT_MODE_TRISTATE | PARPORT_MODE_EPP | PARPORT_MODE_ECP | PARPORT_MODE_COMPAT;
+ pp->modes = PARPORT_MODE_PCSPP | PARPORT_MODE_TRISTATE | PARPORT_MODE_EPP | PARPORT_MODE_COMPAT;
+ if (interface->desc.bNumEndpoints >= 3)
+ pp->modes |= PARPORT_MODE_ECP;
+ pp->dev = &usbdev->dev;
/* set the USS720 control register to manual mode, no ECP compression, enable all ints */
set_1284_register(pp, 7, 0x00, GFP_KERNEL);
set_1284_register(pp, 6, 0x30, GFP_KERNEL); /* PS/2 mode */
set_1284_register(pp, 2, 0x0c, GFP_KERNEL);
- /* debugging */
- get_1284_register(pp, 0, &reg, GFP_KERNEL);
+
+ /* The Belkin F5U002 Rev 2 P80453-B USB parallel port adapter shares the
+ * device ID 050d:0002 with some other device that works with this
+ * driver, but it itself does not. Detect and handle the bad cable
+ * here. */
+ ret = get_1284_register(pp, 0, &reg, GFP_KERNEL);
dev_dbg(&intf->dev, "reg: %7ph\n", priv->reg);
+ if (ret < 0)
+ return ret;
- i = usb_find_last_int_in_endpoint(interface, &epd);
- if (!i) {
+ ret = usb_find_last_int_in_endpoint(interface, &epd);
+ if (!ret) {
dev_dbg(&intf->dev, "epaddr %d interval %d\n",
epd->bEndpointAddress, epd->bInterval);
}
@@ -766,14 +775,15 @@ static void uss720_disconnect(struct usb_interface *intf)
/* table of cables that work through this driver */
static const struct usb_device_id uss720_table[] = {
- { USB_DEVICE(0x047e, 0x1001) },
- { USB_DEVICE(0x04b8, 0x0002) },
- { USB_DEVICE(0x04b8, 0x0003) },
+ { USB_DEVICE(0x047e, 0x1001) }, /* Infowave 901-0030 */
+ { USB_DEVICE(0x04b8, 0x0002) }, /* Epson CAEUL0002 ISD-103 */
+ { USB_DEVICE(0x04b8, 0x0003) }, /* Epson ISD-101 */
{ USB_DEVICE(0x050d, 0x0002) },
- { USB_DEVICE(0x050d, 0x1202) },
+ { USB_DEVICE(0x050d, 0x1202) }, /* Belkin F5U120-PC */
{ USB_DEVICE(0x0557, 0x2001) },
- { USB_DEVICE(0x05ab, 0x0002) },
- { USB_DEVICE(0x06c6, 0x0100) },
+ { USB_DEVICE(0x05ab, 0x0002) }, /* Belkin F5U002 ISD-101 */
+ { USB_DEVICE(0x05ab, 0x1001) }, /* Belkin F5U002 P80453-A */
+ { USB_DEVICE(0x06c6, 0x0100) }, /* Infowave ISD-103 */
{ USB_DEVICE(0x0729, 0x1284) },
{ USB_DEVICE(0x1293, 0x0002) },
{ } /* Terminating entry */
diff --git a/drivers/usb/misc/yurex.c b/drivers/usb/misc/yurex.c
index 9a0649d23693..4a9859e03f6b 100644
--- a/drivers/usb/misc/yurex.c
+++ b/drivers/usb/misc/yurex.c
@@ -404,7 +404,6 @@ static ssize_t yurex_read(struct file *file, char __user *buffer, size_t count,
struct usb_yurex *dev;
int len = 0;
char in_buffer[MAX_S64_STRLEN];
- unsigned long flags;
dev = file->private_data;
@@ -419,9 +418,9 @@ static ssize_t yurex_read(struct file *file, char __user *buffer, size_t count,
return -EIO;
}
- spin_lock_irqsave(&dev->lock, flags);
+ spin_lock_irq(&dev->lock);
scnprintf(in_buffer, MAX_S64_STRLEN, "%lld\n", dev->bbu);
- spin_unlock_irqrestore(&dev->lock, flags);
+ spin_unlock_irq(&dev->lock);
mutex_unlock(&dev->io_mutex);
return simple_read_from_buffer(buffer, count, ppos, in_buffer, len);
@@ -511,8 +510,11 @@ static ssize_t yurex_write(struct file *file, const char __user *user_buffer,
__func__, retval);
goto error;
}
- if (set && timeout)
+ if (set && timeout) {
+ spin_lock_irq(&dev->lock);
dev->bbu = c2;
+ spin_unlock_irq(&dev->lock);
+ }
return timeout ? count : -EIO;
error:
@@ -531,4 +533,5 @@ static const struct file_operations yurex_fops = {
module_usb_driver(yurex_driver);
+MODULE_DESCRIPTION("USB YUREX driver support");
MODULE_LICENSE("GPL");
diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c
index 4e30de4db1c0..afb71c18415d 100644
--- a/drivers/usb/mon/mon_bin.c
+++ b/drivers/usb/mon/mon_bin.c
@@ -1289,7 +1289,6 @@ static int mon_bin_mmap(struct file *filp, struct vm_area_struct *vma)
static const struct file_operations mon_fops_binary = {
.owner = THIS_MODULE,
.open = mon_bin_open,
- .llseek = no_llseek,
.read = mon_bin_read,
/* .write = mon_text_write, */
.poll = mon_bin_poll,
diff --git a/drivers/usb/mon/mon_main.c b/drivers/usb/mon/mon_main.c
index 824904abe76f..af852d53aac6 100644
--- a/drivers/usb/mon/mon_main.c
+++ b/drivers/usb/mon/mon_main.c
@@ -419,4 +419,5 @@ static void __exit mon_exit(void)
module_init(mon_init);
module_exit(mon_exit);
+MODULE_DESCRIPTION("USB Monitor");
MODULE_LICENSE("GPL");
diff --git a/drivers/usb/mon/mon_stat.c b/drivers/usb/mon/mon_stat.c
index 3c23805ab1a4..398e02af6a2b 100644
--- a/drivers/usb/mon/mon_stat.c
+++ b/drivers/usb/mon/mon_stat.c
@@ -62,7 +62,6 @@ static int mon_stat_release(struct inode *inode, struct file *file)
const struct file_operations mon_fops_stat = {
.owner = THIS_MODULE,
.open = mon_stat_open,
- .llseek = no_llseek,
.read = mon_stat_read,
/* .write = mon_stat_write, */
/* .poll = mon_stat_poll, */
diff --git a/drivers/usb/mon/mon_text.c b/drivers/usb/mon/mon_text.c
index 2fe9b95bac1d..68b9b2b41189 100644
--- a/drivers/usb/mon/mon_text.c
+++ b/drivers/usb/mon/mon_text.c
@@ -685,7 +685,6 @@ static int mon_text_release(struct inode *inode, struct file *file)
static const struct file_operations mon_fops_text_t = {
.owner = THIS_MODULE,
.open = mon_text_open,
- .llseek = no_llseek,
.read = mon_text_read_t,
.release = mon_text_release,
};
@@ -693,7 +692,6 @@ static const struct file_operations mon_fops_text_t = {
static const struct file_operations mon_fops_text_u = {
.owner = THIS_MODULE,
.open = mon_text_open,
- .llseek = no_llseek,
.read = mon_text_read_u,
.release = mon_text_release,
};
diff --git a/drivers/usb/mtu3/mtu3_trace.h b/drivers/usb/mtu3/mtu3_trace.h
index 03d2a9bac27e..89870175d635 100644
--- a/drivers/usb/mtu3/mtu3_trace.h
+++ b/drivers/usb/mtu3/mtu3_trace.h
@@ -26,7 +26,7 @@ TRACE_EVENT(mtu3_log,
__vstring(msg, vaf->fmt, vaf->va)
),
TP_fast_assign(
- __assign_str(name, dev_name(dev));
+ __assign_str(name);
__assign_vstr(msg, vaf->fmt, vaf->va);
),
TP_printk("%s: %s", __get_str(name), __get_str(msg))
@@ -127,7 +127,7 @@ DECLARE_EVENT_CLASS(mtu3_log_request,
__field(int, no_interrupt)
),
TP_fast_assign(
- __assign_str(name, mreq->mep->name);
+ __assign_str(name);
__entry->mreq = mreq;
__entry->gpd = mreq->gpd;
__entry->actual = mreq->request.actual;
@@ -182,7 +182,7 @@ DECLARE_EVENT_CLASS(mtu3_log_gpd,
__field(u32, dw3)
),
TP_fast_assign(
- __assign_str(name, mep->name);
+ __assign_str(name);
__entry->gpd = gpd;
__entry->dw0 = le32_to_cpu(gpd->dw0_info);
__entry->dw1 = le32_to_cpu(gpd->next_gpd);
@@ -226,7 +226,7 @@ DECLARE_EVENT_CLASS(mtu3_log_ep,
__field(struct mtu3_gpd_ring *, gpd_ring)
),
TP_fast_assign(
- __assign_str(name, mep->name);
+ __assign_str(name);
__entry->type = mep->type;
__entry->slot = mep->slot;
__entry->maxp = mep->ep.maxpacket;
diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c
index 8abf3a567e30..953094c1930c 100644
--- a/drivers/usb/musb/da8xx.c
+++ b/drivers/usb/musb/da8xx.c
@@ -191,7 +191,7 @@ static void otg_timer(struct timer_list *t)
spin_unlock_irqrestore(&musb->lock, flags);
}
-static void da8xx_musb_try_idle(struct musb *musb, unsigned long timeout)
+static void __maybe_unused da8xx_musb_try_idle(struct musb *musb, unsigned long timeout)
{
static unsigned long last_timer;
@@ -220,6 +220,13 @@ static void da8xx_musb_try_idle(struct musb *musb, unsigned long timeout)
mod_timer(&musb->dev_timer, timeout);
}
+static int da8xx_babble_recover(struct musb *musb)
+{
+ dev_dbg(musb->controller, "resetting controller to recover from babble\n");
+ musb_writel(musb->ctrl_base, DA8XX_USB_CTRL_REG, DA8XX_SOFT_RESET_MASK);
+ return 0;
+}
+
static irqreturn_t da8xx_musb_interrupt(int irq, void *hci)
{
struct musb *musb = hci;
@@ -328,13 +335,6 @@ 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;
@@ -483,7 +483,11 @@ static const struct musb_platform_ops da8xx_ops = {
.disable = da8xx_musb_disable,
.set_mode = da8xx_musb_set_mode,
+
+#ifndef CONFIG_USB_MUSB_HOST
.try_idle = da8xx_musb_try_idle,
+#endif
+ .recover = da8xx_babble_recover,
.set_vbus = da8xx_musb_set_vbus,
};
@@ -556,7 +560,7 @@ static int da8xx_probe(struct platform_device *pdev)
ret = of_platform_populate(pdev->dev.of_node, NULL,
da8xx_auxdata_lookup, &pdev->dev);
if (ret)
- return ret;
+ goto err_unregister_phy;
pinfo = da8xx_dev_info;
pinfo.parent = &pdev->dev;
@@ -571,9 +575,13 @@ static int da8xx_probe(struct platform_device *pdev)
ret = PTR_ERR_OR_ZERO(glue->musb);
if (ret) {
dev_err(&pdev->dev, "failed to register musb device: %d\n", ret);
- usb_phy_generic_unregister(glue->usb_phy);
+ goto err_unregister_phy;
}
+ return 0;
+
+err_unregister_phy:
+ usb_phy_generic_unregister(glue->usb_phy);
return ret;
}
diff --git a/drivers/usb/musb/mediatek.c b/drivers/usb/musb/mediatek.c
index 0a35aab3ab81..63c86c046b98 100644
--- a/drivers/usb/musb/mediatek.c
+++ b/drivers/usb/musb/mediatek.c
@@ -416,10 +416,9 @@ static int mtk_musb_probe(struct platform_device *pdev)
return -ENOMEM;
ret = of_platform_populate(np, NULL, NULL, dev);
- if (ret) {
- dev_err(dev, "failed to create child devices at %p\n", np);
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to create child devices at %p\n", np);
ret = mtk_musb_clks_get(glue);
if (ret)
@@ -448,23 +447,19 @@ static int mtk_musb_probe(struct platform_device *pdev)
glue->role = USB_ROLE_NONE;
break;
default:
- dev_err(&pdev->dev, "Error 'dr_mode' property\n");
- return -EINVAL;
+ return dev_err_probe(&pdev->dev, -EINVAL,
+ "Error 'dr_mode' property\n");
}
glue->phy = devm_of_phy_get_by_index(dev, np, 0);
- if (IS_ERR(glue->phy)) {
- dev_err(dev, "fail to getting phy %ld\n",
- PTR_ERR(glue->phy));
- return PTR_ERR(glue->phy);
- }
+ if (IS_ERR(glue->phy))
+ return dev_err_probe(dev, PTR_ERR(glue->phy),
+ "fail to getting phy\n");
glue->usb_phy = usb_phy_generic_register();
- if (IS_ERR(glue->usb_phy)) {
- dev_err(dev, "fail to registering usb-phy %ld\n",
- PTR_ERR(glue->usb_phy));
- return PTR_ERR(glue->usb_phy);
- }
+ if (IS_ERR(glue->usb_phy))
+ return dev_err_probe(dev, PTR_ERR(glue->usb_phy),
+ "fail to registering usb-phy\n");
glue->xceiv = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
if (IS_ERR(glue->xceiv)) {
diff --git a/drivers/usb/musb/mpfs.c b/drivers/usb/musb/mpfs.c
index f0f56df38835..00e13214aa76 100644
--- a/drivers/usb/musb/mpfs.c
+++ b/drivers/usb/musb/mpfs.c
@@ -49,30 +49,6 @@ static const struct musb_hdrc_config mpfs_musb_hdrc_config = {
.ram_bits = MPFS_MUSB_RAM_BITS,
};
-static irqreturn_t mpfs_musb_interrupt(int irq, void *__hci)
-{
- unsigned long flags;
- irqreturn_t ret = IRQ_NONE;
- struct musb *musb = __hci;
-
- spin_lock_irqsave(&musb->lock, flags);
-
- musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB);
- musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX);
- musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX);
-
- if (musb->int_usb || musb->int_tx || musb->int_rx) {
- musb_writeb(musb->mregs, MUSB_INTRUSB, musb->int_usb);
- musb_writew(musb->mregs, MUSB_INTRTX, musb->int_tx);
- musb_writew(musb->mregs, MUSB_INTRRX, musb->int_rx);
- ret = musb_interrupt(musb);
- }
-
- spin_unlock_irqrestore(&musb->lock, flags);
-
- return ret;
-}
-
static void mpfs_musb_set_vbus(struct musb *musb, int is_on)
{
u8 devctl;
@@ -111,6 +87,129 @@ static void mpfs_musb_set_vbus(struct musb *musb, int is_on)
musb_readb(musb->mregs, MUSB_DEVCTL));
}
+#define POLL_SECONDS 2
+
+static void otg_timer(struct timer_list *t)
+{
+ struct musb *musb = from_timer(musb, t, dev_timer);
+ void __iomem *mregs = musb->mregs;
+ u8 devctl;
+ unsigned long flags;
+
+ /*
+ * We poll because PolarFire SoC won't expose several OTG-critical
+ * status change events (from the transceiver) otherwise.
+ */
+ devctl = musb_readb(mregs, MUSB_DEVCTL);
+ dev_dbg(musb->controller, "Poll devctl %02x (%s)\n", devctl,
+ usb_otg_state_string(musb->xceiv->otg->state));
+
+ spin_lock_irqsave(&musb->lock, flags);
+ switch (musb->xceiv->otg->state) {
+ case OTG_STATE_A_WAIT_BCON:
+ devctl &= ~MUSB_DEVCTL_SESSION;
+ musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
+
+ devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
+ if (devctl & MUSB_DEVCTL_BDEVICE) {
+ musb->xceiv->otg->state = OTG_STATE_B_IDLE;
+ MUSB_DEV_MODE(musb);
+ mod_timer(&musb->dev_timer, jiffies + POLL_SECONDS * HZ);
+ } else {
+ musb->xceiv->otg->state = OTG_STATE_A_IDLE;
+ MUSB_HST_MODE(musb);
+ }
+ break;
+ case OTG_STATE_A_WAIT_VFALL:
+ if (devctl & MUSB_DEVCTL_VBUS) {
+ mod_timer(&musb->dev_timer, jiffies + POLL_SECONDS * HZ);
+ break;
+ }
+ musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE;
+ break;
+ case OTG_STATE_B_IDLE:
+ /*
+ * There's no ID-changed IRQ, so we have no good way to tell
+ * when to switch to the A-Default state machine (by setting
+ * the DEVCTL.Session bit).
+ *
+ * Workaround: whenever we're in B_IDLE, try setting the
+ * session flag every few seconds. If it works, ID was
+ * grounded and we're now in the A-Default state machine.
+ *
+ * NOTE: setting the session flag is _supposed_ to trigger
+ * SRP but clearly it doesn't.
+ */
+ musb_writeb(mregs, MUSB_DEVCTL, devctl | MUSB_DEVCTL_SESSION);
+ devctl = musb_readb(mregs, MUSB_DEVCTL);
+ if (devctl & MUSB_DEVCTL_BDEVICE)
+ mod_timer(&musb->dev_timer, jiffies + POLL_SECONDS * HZ);
+ else
+ musb->xceiv->otg->state = OTG_STATE_A_IDLE;
+ break;
+ default:
+ break;
+ }
+ spin_unlock_irqrestore(&musb->lock, flags);
+}
+
+static void __maybe_unused mpfs_musb_try_idle(struct musb *musb, unsigned long timeout)
+{
+ static unsigned long last_timer;
+
+ if (timeout == 0)
+ timeout = jiffies + msecs_to_jiffies(3);
+
+ /* Never idle if active, or when VBUS timeout is not set as host */
+ if (musb->is_active || (musb->a_wait_bcon == 0 &&
+ musb->xceiv->otg->state == OTG_STATE_A_WAIT_BCON)) {
+ dev_dbg(musb->controller, "%s active, deleting timer\n",
+ usb_otg_state_string(musb->xceiv->otg->state));
+ del_timer(&musb->dev_timer);
+ last_timer = jiffies;
+ return;
+ }
+
+ if (time_after(last_timer, timeout) && timer_pending(&musb->dev_timer)) {
+ dev_dbg(musb->controller, "Longer idle timer already pending, ignoring...\n");
+ return;
+ }
+ last_timer = timeout;
+
+ dev_dbg(musb->controller, "%s inactive, starting idle timer for %u ms\n",
+ usb_otg_state_string(musb->xceiv->otg->state),
+ jiffies_to_msecs(timeout - jiffies));
+ mod_timer(&musb->dev_timer, timeout);
+}
+
+static irqreturn_t mpfs_musb_interrupt(int irq, void *__hci)
+{
+ unsigned long flags;
+ irqreturn_t ret = IRQ_NONE;
+ struct musb *musb = __hci;
+
+ spin_lock_irqsave(&musb->lock, flags);
+
+ musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB);
+ musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX);
+ musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX);
+
+ if (musb->int_usb || musb->int_tx || musb->int_rx) {
+ musb_writeb(musb->mregs, MUSB_INTRUSB, musb->int_usb);
+ musb_writew(musb->mregs, MUSB_INTRTX, musb->int_tx);
+ musb_writew(musb->mregs, MUSB_INTRRX, musb->int_rx);
+ ret = musb_interrupt(musb);
+ }
+
+ /* Poll for ID change */
+ if (musb->xceiv->otg->state == OTG_STATE_B_IDLE)
+ mod_timer(&musb->dev_timer, jiffies + POLL_SECONDS * HZ);
+
+ spin_unlock_irqrestore(&musb->lock, flags);
+
+ return ret;
+}
+
static int mpfs_musb_init(struct musb *musb)
{
struct device *dev = musb->controller;
@@ -121,6 +220,8 @@ static int mpfs_musb_init(struct musb *musb)
return PTR_ERR(musb->xceiv);
}
+ timer_setup(&musb->dev_timer, otg_timer, 0);
+
musb->dyn_fifo = true;
musb->isr = mpfs_musb_interrupt;
@@ -129,14 +230,25 @@ static int mpfs_musb_init(struct musb *musb)
return 0;
}
+static int mpfs_musb_exit(struct musb *musb)
+{
+ del_timer_sync(&musb->dev_timer);
+
+ return 0;
+}
+
static const struct musb_platform_ops mpfs_ops = {
.quirks = MUSB_DMA_INVENTRA,
.init = mpfs_musb_init,
+ .exit = mpfs_musb_exit,
.fifo_mode = 2,
#ifdef CONFIG_USB_INVENTRA_DMA
.dma_init = musbhs_dma_controller_create,
.dma_exit = musbhs_dma_controller_destroy,
#endif
+#ifndef CONFIG_USB_MUSB_HOST
+ .try_idle = mpfs_musb_try_idle,
+#endif
.set_vbus = mpfs_musb_set_vbus
};
@@ -190,6 +302,8 @@ static int mpfs_probe(struct platform_device *pdev)
pdata->config = &mpfs_musb_hdrc_config;
pdata->platform_ops = &mpfs_ops;
+ pdata->extvbus = device_property_read_bool(dev, "microchip,ext-vbus-drv");
+
pdata->mode = usb_get_dr_mode(dev);
if (pdata->mode == USB_DR_MODE_UNKNOWN) {
dev_info(dev, "No dr_mode property found, defaulting to otg\n");
diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c
index 55df0ee413d8..bdf13911a1e5 100644
--- a/drivers/usb/musb/musb_gadget.c
+++ b/drivers/usb/musb/musb_gadget.c
@@ -1156,15 +1156,6 @@ void musb_free_request(struct usb_ep *ep, struct usb_request *req)
kfree(request);
}
-static LIST_HEAD(buffers);
-
-struct free_record {
- struct list_head list;
- struct device *dev;
- unsigned bytes;
- dma_addr_t dma;
-};
-
/*
* Context: controller locked, IRQs blocked.
*/
diff --git a/drivers/usb/musb/musb_trace.h b/drivers/usb/musb/musb_trace.h
index f246b14394c4..726e6697d475 100644
--- a/drivers/usb/musb/musb_trace.h
+++ b/drivers/usb/musb/musb_trace.h
@@ -31,7 +31,7 @@ TRACE_EVENT(musb_log,
__vstring(msg, vaf->fmt, vaf->va)
),
TP_fast_assign(
- __assign_str(name, dev_name(musb->controller));
+ __assign_str(name);
__assign_vstr(msg, vaf->fmt, vaf->va);
),
TP_printk("%s: %s", __get_str(name), __get_str(msg))
@@ -46,9 +46,9 @@ TRACE_EVENT(musb_state,
__string(desc, desc)
),
TP_fast_assign(
- __assign_str(name, dev_name(musb->controller));
+ __assign_str(name);
__entry->devctl = devctl;
- __assign_str(desc, desc);
+ __assign_str(desc);
),
TP_printk("%s: devctl: %02x %s", __get_str(name), __entry->devctl,
__get_str(desc))
@@ -160,7 +160,7 @@ TRACE_EVENT(musb_isr,
__field(u16, int_rx)
),
TP_fast_assign(
- __assign_str(name, dev_name(musb->controller));
+ __assign_str(name);
__entry->int_usb = musb->int_usb;
__entry->int_tx = musb->int_tx;
__entry->int_rx = musb->int_rx;
@@ -184,7 +184,7 @@ DECLARE_EVENT_CLASS(musb_urb,
__field(u32, actual_len)
),
TP_fast_assign(
- __assign_str(name, dev_name(musb->controller));
+ __assign_str(name);
__entry->urb = urb;
__entry->pipe = urb->pipe;
__entry->status = urb->status;
@@ -325,7 +325,7 @@ DECLARE_EVENT_CLASS(musb_cppi41,
),
TP_fast_assign(
__entry->ch = ch;
- __assign_str(name, dev_name(ch->hw_ep->musb->controller));
+ __assign_str(name);
__entry->hwep = ch->hw_ep->epnum;
__entry->port = ch->port_num;
__entry->is_tx = ch->is_tx;
diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c
index 2b2164e028b3..ce6f25a9650b 100644
--- a/drivers/usb/musb/musb_virthub.c
+++ b/drivers/usb/musb/musb_virthub.c
@@ -14,7 +14,7 @@
#include <linux/time.h>
#include <linux/timer.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include "musb_core.h"
diff --git a/drivers/usb/phy/phy-am335x-control.c b/drivers/usb/phy/phy-am335x-control.c
index 97e6603c7149..ada508be090a 100644
--- a/drivers/usb/phy/phy-am335x-control.c
+++ b/drivers/usb/phy/phy-am335x-control.c
@@ -189,4 +189,5 @@ static struct platform_driver am335x_control_driver = {
};
module_platform_driver(am335x_control_driver);
+MODULE_DESCRIPTION("AM335x USB PHY Control Driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/phy/phy-am335x.c b/drivers/usb/phy/phy-am335x.c
index e39665cf4b4a..6db88e00f127 100644
--- a/drivers/usb/phy/phy-am335x.c
+++ b/drivers/usb/phy/phy-am335x.c
@@ -142,4 +142,5 @@ static struct platform_driver am335x_phy_driver = {
};
module_platform_driver(am335x_phy_driver);
+MODULE_DESCRIPTION("AM335x USB PHY Driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/phy/phy-fsl-usb.c b/drivers/usb/phy/phy-fsl-usb.c
index 79617bb0a70e..c5c6b818998e 100644
--- a/drivers/usb/phy/phy-fsl-usb.c
+++ b/drivers/usb/phy/phy-fsl-usb.c
@@ -27,7 +27,7 @@
#include <linux/platform_device.h>
#include <linux/uaccess.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include "phy-fsl-usb.h"
@@ -1005,7 +1005,6 @@ struct platform_driver fsl_otg_driver = {
.remove_new = fsl_otg_remove,
.driver = {
.name = driver_name,
- .owner = THIS_MODULE,
},
};
diff --git a/drivers/usb/phy/phy-generic.c b/drivers/usb/phy/phy-generic.c
index fdcffebf415c..e7d50e0a1612 100644
--- a/drivers/usb/phy/phy-generic.c
+++ b/drivers/usb/phy/phy-generic.c
@@ -71,6 +71,7 @@ static void nop_reset(struct usb_phy_generic *nop)
gpiod_set_value_cansleep(nop->gpiod_reset, 1);
usleep_range(10000, 20000);
gpiod_set_value_cansleep(nop->gpiod_reset, 0);
+ usleep_range(10000, 30000);
}
/* interface to regulator framework */
diff --git a/drivers/usb/phy/phy-gpio-vbus-usb.c b/drivers/usb/phy/phy-gpio-vbus-usb.c
index 817c242a76ca..5428b2b67de1 100644
--- a/drivers/usb/phy/phy-gpio-vbus-usb.c
+++ b/drivers/usb/phy/phy-gpio-vbus-usb.c
@@ -374,6 +374,7 @@ static const struct of_device_id gpio_vbus_of_match[] = {
},
{},
};
+MODULE_DEVICE_TABLE(of, gpio_vbus_of_match);
static struct platform_driver gpio_vbus_driver = {
.driver = {
diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c
index 920a32cd094d..cc4156c1b148 100644
--- a/drivers/usb/phy/phy-mxs-usb.c
+++ b/drivers/usb/phy/phy-mxs-usb.c
@@ -18,6 +18,7 @@
#include <linux/regmap.h>
#include <linux/mfd/syscon.h>
#include <linux/iopoll.h>
+#include <linux/regulator/consumer.h>
#define DRIVER_NAME "mxs_phy"
@@ -70,6 +71,9 @@
#define BM_USBPHY_PLL_EN_USB_CLKS BIT(6)
/* Anatop Registers */
+#define ANADIG_REG_1P1_SET 0x114
+#define ANADIG_REG_1P1_CLR 0x118
+
#define ANADIG_ANA_MISC0 0x150
#define ANADIG_ANA_MISC0_SET 0x154
#define ANADIG_ANA_MISC0_CLR 0x158
@@ -117,6 +121,14 @@
#define BM_ANADIG_USB2_MISC_RX_VPIN_FS BIT(29)
#define BM_ANADIG_USB2_MISC_RX_VMIN_FS BIT(28)
+/* System Integration Module (SIM) Registers */
+#define SIM_GPR1 0x30
+
+#define USB_PHY_VLLS_WAKEUP_EN BIT(0)
+
+#define BM_ANADIG_REG_1P1_ENABLE_WEAK_LINREG BIT(18)
+#define BM_ANADIG_REG_1P1_TRACK_VDD_SOC_CAP BIT(19)
+
#define to_mxs_phy(p) container_of((p), struct mxs_phy, phy)
/* Do disconnection between PHY and controller without vbus */
@@ -149,6 +161,15 @@
#define MXS_PHY_TX_D_CAL_MIN 79
#define MXS_PHY_TX_D_CAL_MAX 119
+/*
+ * At imx6q/6sl/6sx, the PHY2's clock is controlled by hardware directly,
+ * eg, according to PHY's suspend status. In these PHYs, we only need to
+ * open the clock at the initialization and close it at its shutdown routine.
+ * These PHYs can send resume signal without software interfere if not
+ * gate clock.
+ */
+#define MXS_PHY_HARDWARE_CONTROL_PHY2_CLK BIT(4)
+
struct mxs_phy_data {
unsigned int flags;
};
@@ -160,12 +181,14 @@ static const struct mxs_phy_data imx23_phy_data = {
static const struct mxs_phy_data imx6q_phy_data = {
.flags = MXS_PHY_SENDING_SOF_TOO_FAST |
MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS |
- MXS_PHY_NEED_IP_FIX,
+ MXS_PHY_NEED_IP_FIX |
+ MXS_PHY_HARDWARE_CONTROL_PHY2_CLK,
};
static const struct mxs_phy_data imx6sl_phy_data = {
.flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS |
- MXS_PHY_NEED_IP_FIX,
+ MXS_PHY_NEED_IP_FIX |
+ MXS_PHY_HARDWARE_CONTROL_PHY2_CLK,
};
static const struct mxs_phy_data vf610_phy_data = {
@@ -174,11 +197,13 @@ static const struct mxs_phy_data vf610_phy_data = {
};
static const struct mxs_phy_data imx6sx_phy_data = {
- .flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS,
+ .flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS |
+ MXS_PHY_HARDWARE_CONTROL_PHY2_CLK,
};
static const struct mxs_phy_data imx6ul_phy_data = {
- .flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS,
+ .flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS |
+ MXS_PHY_HARDWARE_CONTROL_PHY2_CLK,
};
static const struct mxs_phy_data imx7ulp_phy_data = {
@@ -201,9 +226,11 @@ struct mxs_phy {
struct clk *clk;
const struct mxs_phy_data *data;
struct regmap *regmap_anatop;
+ struct regmap *regmap_sim;
int port_id;
u32 tx_reg_set;
u32 tx_reg_mask;
+ struct regulator *phy_3p0;
};
static inline bool is_imx6q_phy(struct mxs_phy *mxs_phy)
@@ -221,6 +248,11 @@ static inline bool is_imx7ulp_phy(struct mxs_phy *mxs_phy)
return mxs_phy->data == &imx7ulp_phy_data;
}
+static inline bool is_imx6ul_phy(struct mxs_phy *mxs_phy)
+{
+ return mxs_phy->data == &imx6ul_phy_data;
+}
+
/*
* PHY needs some 32K cycles to switch from 32K clock to
* bus (such as AHB/AXI, etc) clock.
@@ -288,6 +320,16 @@ static int mxs_phy_hw_init(struct mxs_phy *mxs_phy)
if (ret)
goto disable_pll;
+ if (mxs_phy->phy_3p0) {
+ ret = regulator_enable(mxs_phy->phy_3p0);
+ if (ret) {
+ dev_err(mxs_phy->phy.dev,
+ "Failed to enable 3p0 regulator, ret=%d\n",
+ ret);
+ return ret;
+ }
+ }
+
/* Power up the PHY */
writel(0, base + HW_USBPHY_PWD);
@@ -448,6 +490,9 @@ static void mxs_phy_shutdown(struct usb_phy *phy)
if (is_imx7ulp_phy(mxs_phy))
mxs_phy_pll_enable(phy->io_priv, false);
+ if (mxs_phy->phy_3p0)
+ regulator_disable(mxs_phy->phy_3p0);
+
clk_disable_unprepare(mxs_phy->clk);
}
@@ -503,12 +548,19 @@ static int mxs_phy_suspend(struct usb_phy *x, int suspend)
}
writel(BM_USBPHY_CTRL_CLKGATE,
x->io_priv + HW_USBPHY_CTRL_SET);
- clk_disable_unprepare(mxs_phy->clk);
+ if (!(mxs_phy->port_id == 1 &&
+ (mxs_phy->data->flags &
+ MXS_PHY_HARDWARE_CONTROL_PHY2_CLK)))
+ clk_disable_unprepare(mxs_phy->clk);
} else {
mxs_phy_clock_switch_delay();
- ret = clk_prepare_enable(mxs_phy->clk);
- if (ret)
- return ret;
+ if (!(mxs_phy->port_id == 1 &&
+ (mxs_phy->data->flags &
+ MXS_PHY_HARDWARE_CONTROL_PHY2_CLK))) {
+ ret = clk_prepare_enable(mxs_phy->clk);
+ if (ret)
+ return ret;
+ }
writel(BM_USBPHY_CTRL_CLKGATE,
x->io_priv + HW_USBPHY_CTRL_CLR);
writel(0, x->io_priv + HW_USBPHY_PWD);
@@ -738,6 +790,17 @@ static int mxs_phy_probe(struct platform_device *pdev)
}
}
+ /* Currently, only imx7ulp has SIM module */
+ if (of_get_property(np, "nxp,sim", NULL)) {
+ mxs_phy->regmap_sim = syscon_regmap_lookup_by_phandle
+ (np, "nxp,sim");
+ if (IS_ERR(mxs_phy->regmap_sim)) {
+ dev_dbg(&pdev->dev,
+ "failed to find regmap for sim\n");
+ return PTR_ERR(mxs_phy->regmap_sim);
+ }
+ }
+
/* Precompute which bits of the TX register are to be updated, if any */
if (!of_property_read_u32(np, "fsl,tx-cal-45-dn-ohms", &val) &&
val >= MXS_PHY_TX_CAL45_MIN && val <= MXS_PHY_TX_CAL45_MAX) {
@@ -789,6 +852,17 @@ static int mxs_phy_probe(struct platform_device *pdev)
mxs_phy->clk = clk;
mxs_phy->data = of_device_get_match_data(&pdev->dev);
+ mxs_phy->phy_3p0 = devm_regulator_get(&pdev->dev, "phy-3p0");
+ if (PTR_ERR(mxs_phy->phy_3p0) == -ENODEV)
+ /* not exist */
+ mxs_phy->phy_3p0 = NULL;
+ else if (IS_ERR(mxs_phy->phy_3p0))
+ return dev_err_probe(&pdev->dev, PTR_ERR(mxs_phy->phy_3p0),
+ "Getting regulator error\n");
+
+ if (mxs_phy->phy_3p0)
+ regulator_set_voltage(mxs_phy->phy_3p0, 3200000, 3200000);
+
platform_set_drvdata(pdev, mxs_phy);
device_set_wakeup_capable(&pdev->dev, true);
@@ -804,28 +878,58 @@ static void mxs_phy_remove(struct platform_device *pdev)
}
#ifdef CONFIG_PM_SLEEP
+static void mxs_phy_wakeup_enable(struct mxs_phy *mxs_phy, bool on)
+{
+ u32 mask = USB_PHY_VLLS_WAKEUP_EN;
+
+ /* If the SoCs don't have SIM, quit */
+ if (!mxs_phy->regmap_sim)
+ return;
+
+ if (on) {
+ regmap_update_bits(mxs_phy->regmap_sim, SIM_GPR1, mask, mask);
+ udelay(500);
+ } else {
+ regmap_update_bits(mxs_phy->regmap_sim, SIM_GPR1, mask, 0);
+ }
+}
+
static void mxs_phy_enable_ldo_in_suspend(struct mxs_phy *mxs_phy, bool on)
{
- unsigned int reg = on ? ANADIG_ANA_MISC0_SET : ANADIG_ANA_MISC0_CLR;
+ unsigned int reg;
+ u32 value;
/* If the SoCs don't have anatop, quit */
if (!mxs_phy->regmap_anatop)
return;
- if (is_imx6q_phy(mxs_phy))
+ if (is_imx6q_phy(mxs_phy)) {
+ reg = on ? ANADIG_ANA_MISC0_SET : ANADIG_ANA_MISC0_CLR;
regmap_write(mxs_phy->regmap_anatop, reg,
BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG);
- else if (is_imx6sl_phy(mxs_phy))
+ } else if (is_imx6sl_phy(mxs_phy)) {
+ reg = on ? ANADIG_ANA_MISC0_SET : ANADIG_ANA_MISC0_CLR;
regmap_write(mxs_phy->regmap_anatop,
reg, BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG_SL);
+ } else if (is_imx6ul_phy(mxs_phy)) {
+ reg = on ? ANADIG_REG_1P1_SET : ANADIG_REG_1P1_CLR;
+ value = BM_ANADIG_REG_1P1_ENABLE_WEAK_LINREG |
+ BM_ANADIG_REG_1P1_TRACK_VDD_SOC_CAP;
+ if (mxs_phy_get_vbus_status(mxs_phy) && on)
+ regmap_write(mxs_phy->regmap_anatop, reg, value);
+ else if (!on)
+ regmap_write(mxs_phy->regmap_anatop, reg, value);
+ }
}
static int mxs_phy_system_suspend(struct device *dev)
{
struct mxs_phy *mxs_phy = dev_get_drvdata(dev);
- if (device_may_wakeup(dev))
+ if (device_may_wakeup(dev)) {
mxs_phy_enable_ldo_in_suspend(mxs_phy, true);
+ mxs_phy_wakeup_enable(mxs_phy, true);
+ }
return 0;
}
@@ -834,8 +938,10 @@ static int mxs_phy_system_resume(struct device *dev)
{
struct mxs_phy *mxs_phy = dev_get_drvdata(dev);
- if (device_may_wakeup(dev))
+ if (device_may_wakeup(dev)) {
mxs_phy_enable_ldo_in_suspend(mxs_phy, false);
+ mxs_phy_wakeup_enable(mxs_phy, false);
+ }
return 0;
}
diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c
index dd1c17542439..edc43f169d49 100644
--- a/drivers/usb/renesas_usbhs/common.c
+++ b/drivers/usb/renesas_usbhs/common.c
@@ -363,14 +363,14 @@ static void usbhsc_clk_disable_unprepare(struct usbhs_priv *priv)
* platform default param
*/
-/* commonly used on old SH-Mobile SoCs */
+/* commonly used on old SH-Mobile and RZ/G2L family SoCs */
static struct renesas_usbhs_driver_pipe_config usbhsc_default_pipe[] = {
RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_CONTROL, 64, 0x00, false),
- RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_ISOC, 1024, 0x08, false),
- RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_ISOC, 1024, 0x18, false),
- RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x28, true),
- RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x38, true),
+ RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_ISOC, 1024, 0x08, true),
+ RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_ISOC, 1024, 0x28, true),
RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x48, true),
+ RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x58, true),
+ RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x68, true),
RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_INT, 64, 0x04, false),
RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_INT, 64, 0x05, false),
RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_INT, 64, 0x06, false),
@@ -566,6 +566,18 @@ static const struct of_device_id usbhs_of_match[] = {
.data = &usbhs_rcar_gen3_with_pll_plat_info,
},
{
+ .compatible = "renesas,usbhs-r9a07g043",
+ .data = &usbhs_rzg2l_plat_info,
+ },
+ {
+ .compatible = "renesas,usbhs-r9a07g044",
+ .data = &usbhs_rzg2l_plat_info,
+ },
+ {
+ .compatible = "renesas,usbhs-r9a07g054",
+ .data = &usbhs_rzg2l_plat_info,
+ },
+ {
.compatible = "renesas,rcar-gen2-usbhs",
.data = &usbhs_rcar_gen2_plat_info,
},
@@ -581,7 +593,11 @@ static const struct of_device_id usbhs_of_match[] = {
.compatible = "renesas,rza2-usbhs",
.data = &usbhs_rza2_plat_info,
},
- { },
+ {
+ .compatible = "renesas,rzg2l-usbhs",
+ .data = &usbhs_rzg2l_plat_info,
+ },
+ { }
};
MODULE_DEVICE_TABLE(of, usbhs_of_match);
@@ -595,16 +611,11 @@ static int usbhs_probe(struct platform_device *pdev)
u32 tmp;
int irq;
- /* check device node */
- if (dev_of_node(dev))
- info = of_device_get_match_data(dev);
- else
- info = renesas_usbhs_get_info(pdev);
-
- /* check platform information */
+ info = of_device_get_match_data(dev);
if (!info) {
- dev_err(dev, "no platform information\n");
- return -EINVAL;
+ info = dev_get_platdata(dev);
+ if (!info)
+ return dev_err_probe(dev, -EINVAL, "no platform info\n");
}
/* platform data */
diff --git a/drivers/usb/renesas_usbhs/rza.h b/drivers/usb/renesas_usbhs/rza.h
index a29b75fef057..8b879aa34a20 100644
--- a/drivers/usb/renesas_usbhs/rza.h
+++ b/drivers/usb/renesas_usbhs/rza.h
@@ -3,3 +3,4 @@
extern const struct renesas_usbhs_platform_info usbhs_rza1_plat_info;
extern const struct renesas_usbhs_platform_info usbhs_rza2_plat_info;
+extern const struct renesas_usbhs_platform_info usbhs_rzg2l_plat_info;
diff --git a/drivers/usb/renesas_usbhs/rza2.c b/drivers/usb/renesas_usbhs/rza2.c
index f079817250bb..b83699eab373 100644
--- a/drivers/usb/renesas_usbhs/rza2.c
+++ b/drivers/usb/renesas_usbhs/rza2.c
@@ -71,3 +71,16 @@ const struct renesas_usbhs_platform_info usbhs_rza2_plat_info = {
.has_new_pipe_configs = 1,
},
};
+
+const struct renesas_usbhs_platform_info usbhs_rzg2l_plat_info = {
+ .platform_callback = {
+ .hardware_init = usbhs_rza2_hardware_init,
+ .hardware_exit = usbhs_rza2_hardware_exit,
+ .power_ctrl = usbhs_rza2_power_ctrl,
+ .get_id = usbhs_get_id_as_gadget,
+ },
+ .driver_param = {
+ .has_cnen = 1,
+ .cfifo_byte_addr = 1,
+ },
+};
diff --git a/drivers/usb/roles/class.c b/drivers/usb/roles/class.c
index d7aa913ceb8a..c58a12c147f4 100644
--- a/drivers/usb/roles/class.c
+++ b/drivers/usb/roles/class.c
@@ -11,6 +11,7 @@
#include <linux/usb/role.h>
#include <linux/property.h>
#include <linux/device.h>
+#include <linux/lockdep.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/slab.h>
@@ -21,6 +22,7 @@ static const struct class role_class = {
struct usb_role_switch {
struct device dev;
+ struct lock_class_key key;
struct mutex lock; /* device lock*/
struct module *module; /* the module this device depends on */
enum usb_role role;
@@ -326,6 +328,8 @@ static void usb_role_switch_release(struct device *dev)
{
struct usb_role_switch *sw = to_role_switch(dev);
+ mutex_destroy(&sw->lock);
+ lockdep_unregister_key(&sw->key);
kfree(sw);
}
@@ -364,7 +368,8 @@ usb_role_switch_register(struct device *parent,
if (!sw)
return ERR_PTR(-ENOMEM);
- mutex_init(&sw->lock);
+ lockdep_register_key(&sw->key);
+ mutex_init_with_key(&sw->lock, &sw->key);
sw->allow_userspace_control = desc->allow_userspace_control;
sw->usb2_port = desc->usb2_port;
diff --git a/drivers/usb/serial/aircable.c b/drivers/usb/serial/aircable.c
index a1df686c3066..2a76f1f0ee4f 100644
--- a/drivers/usb/serial/aircable.c
+++ b/drivers/usb/serial/aircable.c
@@ -35,7 +35,7 @@
*
*/
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <linux/tty.h>
#include <linux/slab.h>
#include <linux/module.h>
@@ -138,7 +138,6 @@ static void aircable_process_read_urb(struct urb *urb)
static struct usb_serial_driver aircable_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "aircable",
},
.id_table = id_table,
diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c
index 67a07cc007f0..800b04fe37fa 100644
--- a/drivers/usb/serial/ark3116.c
+++ b/drivers/usb/serial/ark3116.c
@@ -599,7 +599,6 @@ static void ark3116_process_read_urb(struct urb *urb)
static struct usb_serial_driver ark3116_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "ark3116",
},
.id_table = id_table,
diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c
index cf47ee4ae5d3..44f5b58beec9 100644
--- a/drivers/usb/serial/belkin_sa.c
+++ b/drivers/usb/serial/belkin_sa.c
@@ -66,7 +66,6 @@ MODULE_DEVICE_TABLE(usb, id_table);
/* All of the device info needed for the serial converters */
static struct usb_serial_driver belkin_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "belkin",
},
.description = "Belkin / Peracom / GoHubs USB Serial Adapter",
diff --git a/drivers/usb/serial/bus.c b/drivers/usb/serial/bus.c
index 6c812d01b37d..d200e2c29a8f 100644
--- a/drivers/usb/serial/bus.c
+++ b/drivers/usb/serial/bus.c
@@ -14,7 +14,7 @@
#include <linux/usb/serial.h>
static int usb_serial_device_match(struct device *dev,
- struct device_driver *drv)
+ const struct device_driver *drv)
{
const struct usb_serial_port *port = to_usb_serial_port(dev);
struct usb_serial_driver *driver = to_usb_serial_driver(drv);
diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c
index 612bea504d7a..d10e4c4848a0 100644
--- a/drivers/usb/serial/ch341.c
+++ b/drivers/usb/serial/ch341.c
@@ -19,7 +19,7 @@
#include <linux/usb.h>
#include <linux/usb/serial.h>
#include <linux/serial.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#define DEFAULT_BAUD_RATE 9600
#define DEFAULT_TIMEOUT 1000
@@ -837,7 +837,6 @@ static int ch341_reset_resume(struct usb_serial *serial)
static struct usb_serial_driver ch341_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "ch341-uart",
},
.id_table = id_table,
@@ -863,4 +862,5 @@ static struct usb_serial_driver * const serial_drivers[] = {
module_usb_serial_driver(serial_drivers, id_table);
+MODULE_DESCRIPTION("Winchiphead CH341 USB Serial driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c
index 21fd26609252..c24101f0a07a 100644
--- a/drivers/usb/serial/cp210x.c
+++ b/drivers/usb/serial/cp210x.c
@@ -299,7 +299,6 @@ struct cp210x_port_private {
static struct usb_serial_driver cp210x_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "cp210x",
},
.id_table = id_table,
diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c
index 51e5aac3bf4c..76dd8e7453b5 100644
--- a/drivers/usb/serial/cyberjack.c
+++ b/drivers/usb/serial/cyberjack.c
@@ -67,7 +67,6 @@ MODULE_DEVICE_TABLE(usb, id_table);
static struct usb_serial_driver cyberjack_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "cyberjack",
},
.description = "Reiner SCT Cyberjack USB card reader",
diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c
index 1e0c028c5ec9..e29569d65991 100644
--- a/drivers/usb/serial/cypress_m8.c
+++ b/drivers/usb/serial/cypress_m8.c
@@ -36,7 +36,7 @@
#include <linux/kfifo.h>
#include <linux/delay.h>
#include <linux/uaccess.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include "cypress_m8.h"
@@ -139,7 +139,6 @@ static void cypress_write_int_callback(struct urb *urb);
static struct usb_serial_driver cypress_earthmate_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "earthmate",
},
.description = "DeLorme Earthmate USB",
@@ -166,7 +165,6 @@ static struct usb_serial_driver cypress_earthmate_device = {
static struct usb_serial_driver cypress_hidcom_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "cyphidcom",
},
.description = "HID->COM RS232 Adapter",
@@ -192,7 +190,6 @@ static struct usb_serial_driver cypress_hidcom_device = {
static struct usb_serial_driver cypress_ca42v2_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "nokiaca42v2",
},
.description = "Nokia CA-42 V2 Adapter",
diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c
index d1dea3850576..a06485965412 100644
--- a/drivers/usb/serial/digi_acceleport.c
+++ b/drivers/usb/serial/digi_acceleport.c
@@ -262,7 +262,6 @@ MODULE_DEVICE_TABLE(usb, id_table_combined);
static struct usb_serial_driver digi_acceleport_2_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "digi_2",
},
.description = "Digi 2 port USB adapter",
@@ -293,7 +292,6 @@ static struct usb_serial_driver digi_acceleport_2_device = {
static struct usb_serial_driver digi_acceleport_4_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "digi_4",
},
.description = "Digi 4 port USB adapter",
diff --git a/drivers/usb/serial/empeg.c b/drivers/usb/serial/empeg.c
index 405e835e93dd..aedcf7ebd269 100644
--- a/drivers/usb/serial/empeg.c
+++ b/drivers/usb/serial/empeg.c
@@ -43,7 +43,6 @@ MODULE_DEVICE_TABLE(usb, id_table);
static struct usb_serial_driver empeg_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "empeg",
},
.id_table = id_table,
diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c
index 5f7a46bcace6..530b77fc2f78 100644
--- a/drivers/usb/serial/f81232.c
+++ b/drivers/usb/serial/f81232.c
@@ -967,7 +967,6 @@ static int f81232_resume(struct usb_serial *serial)
static struct usb_serial_driver f81232_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "f81232",
},
.id_table = f81232_id_table,
@@ -994,7 +993,6 @@ static struct usb_serial_driver f81232_device = {
static struct usb_serial_driver f81534a_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "f81534a",
},
.id_table = f81534a_id_table,
diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c
index ef126cd3d94f..685930ac8383 100644
--- a/drivers/usb/serial/f81534.c
+++ b/drivers/usb/serial/f81534.c
@@ -1538,7 +1538,6 @@ static int f81534_resume(struct usb_serial *serial)
static struct usb_serial_driver f81534_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "f81534",
},
.description = DRIVER_DESC,
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index 76a04ab41100..c6f17d732b95 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -2871,7 +2871,6 @@ static int ftdi_ioctl(struct tty_struct *tty,
static struct usb_serial_driver ftdi_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "ftdi_sio",
.dev_groups = ftdi_groups,
},
diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c
index 670e942fdaaa..b97ba8ed6801 100644
--- a/drivers/usb/serial/garmin_gps.c
+++ b/drivers/usb/serial/garmin_gps.c
@@ -104,7 +104,7 @@ struct garmin_packet {
int seq;
/* the real size of the data array, always > 0 */
int size;
- __u8 data[];
+ __u8 data[] __counted_by(size);
};
/* structure used to keep the current state of the driver */
@@ -267,8 +267,7 @@ static int pkt_add(struct garmin_data *garmin_data_p,
/* process only packets containing data ... */
if (data_length) {
- pkt = kmalloc(sizeof(struct garmin_packet)+data_length,
- GFP_ATOMIC);
+ pkt = kmalloc(struct_size(pkt, data, data_length), GFP_ATOMIC);
if (!pkt)
return 0;
@@ -1413,7 +1412,6 @@ static void garmin_port_remove(struct usb_serial_port *port)
/* All of the device info needed */
static struct usb_serial_driver garmin_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "garmin_gps",
},
.description = "Garmin GPS usb/tty",
diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c
index 15b6dee3a8e5..757f0a586ddb 100644
--- a/drivers/usb/serial/generic.c
+++ b/drivers/usb/serial/generic.c
@@ -63,7 +63,6 @@ static int usb_serial_generic_calc_num_ports(struct usb_serial *serial,
static struct usb_serial_driver usb_serial_generic_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "generic",
},
.id_table = generic_device_ids,
diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c
index abe4bbb0ac65..c7d6b5e3f898 100644
--- a/drivers/usb/serial/io_edgeport.c
+++ b/drivers/usb/serial/io_edgeport.c
@@ -2978,7 +2978,6 @@ static void edge_port_remove(struct usb_serial_port *port)
static struct usb_serial_driver edgeport_2port_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "edgeport_2",
},
.description = "Edgeport 2 port adapter",
@@ -3013,7 +3012,6 @@ static struct usb_serial_driver edgeport_2port_device = {
static struct usb_serial_driver edgeport_4port_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "edgeport_4",
},
.description = "Edgeport 4 port adapter",
@@ -3048,7 +3046,6 @@ static struct usb_serial_driver edgeport_4port_device = {
static struct usb_serial_driver edgeport_8port_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "edgeport_8",
},
.description = "Edgeport 8 port adapter",
@@ -3083,7 +3080,6 @@ static struct usb_serial_driver edgeport_8port_device = {
static struct usb_serial_driver epic_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "epic",
},
.description = "EPiC device",
diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c
index 7a3a6e539456..7d0584b2a234 100644
--- a/drivers/usb/serial/io_ti.c
+++ b/drivers/usb/serial/io_ti.c
@@ -2670,7 +2670,6 @@ static int edge_resume(struct usb_serial *serial)
static struct usb_serial_driver edgeport_1port_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "edgeport_ti_1",
},
.description = "Edgeport TI 1 port adapter",
@@ -2708,7 +2707,6 @@ static struct usb_serial_driver edgeport_1port_device = {
static struct usb_serial_driver edgeport_2port_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "edgeport_ti_2",
},
.description = "Edgeport TI 2 port adapter",
diff --git a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c
index e11441bac44f..3c6a9b9b9c2b 100644
--- a/drivers/usb/serial/ipaq.c
+++ b/drivers/usb/serial/ipaq.c
@@ -496,7 +496,6 @@ MODULE_DEVICE_TABLE(usb, ipaq_id_table);
/* All of the device info needed for the Compaq iPAQ */
static struct usb_serial_driver ipaq_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "ipaq",
},
.description = "PocketPC PDA",
diff --git a/drivers/usb/serial/ipw.c b/drivers/usb/serial/ipw.c
index d04c7cc5c1c2..b1e672c2f423 100644
--- a/drivers/usb/serial/ipw.c
+++ b/drivers/usb/serial/ipw.c
@@ -285,7 +285,6 @@ static void ipw_close(struct usb_serial_port *port)
static struct usb_serial_driver ipw_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "ipw",
},
.description = "IPWireless converter",
diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c
index 82f108134e6f..a106b71e698f 100644
--- a/drivers/usb/serial/ir-usb.c
+++ b/drivers/usb/serial/ir-usb.c
@@ -71,7 +71,6 @@ MODULE_DEVICE_TABLE(usb, ir_id_table);
static struct usb_serial_driver ir_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "ir-usb",
},
.description = "IR Dongle",
diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c
index 77cba71bcccb..c21dcc9b6f05 100644
--- a/drivers/usb/serial/iuu_phoenix.c
+++ b/drivers/usb/serial/iuu_phoenix.c
@@ -1157,7 +1157,6 @@ static int iuu_remove_sysfs_attrs(struct usb_serial_port *port)
static struct usb_serial_driver iuu_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "iuu_phoenix",
},
.id_table = id_table,
diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c
index 0a783985197c..9129e0282c24 100644
--- a/drivers/usb/serial/keyspan.c
+++ b/drivers/usb/serial/keyspan.c
@@ -3001,7 +3001,6 @@ static void keyspan_port_remove(struct usb_serial_port *port)
/* 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)",
@@ -3012,7 +3011,6 @@ static struct usb_serial_driver keyspan_pre_device = {
static struct usb_serial_driver keyspan_1port_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "keyspan_1",
},
.description = "Keyspan 1 port adapter",
@@ -3036,7 +3034,6 @@ static struct usb_serial_driver keyspan_1port_device = {
static struct usb_serial_driver keyspan_2port_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "keyspan_2",
},
.description = "Keyspan 2 port adapter",
@@ -3060,7 +3057,6 @@ static struct usb_serial_driver keyspan_2port_device = {
static struct usb_serial_driver keyspan_4port_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "keyspan_4",
},
.description = "Keyspan 4 port adapter",
diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c
index 0eef358b314a..e98b479593d3 100644
--- a/drivers/usb/serial/keyspan_pda.c
+++ b/drivers/usb/serial/keyspan_pda.c
@@ -676,7 +676,6 @@ static void keyspan_pda_port_remove(struct usb_serial_port *port)
static struct usb_serial_driver keyspan_pda_fake_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "keyspan_pda_pre",
},
.description = "Keyspan PDA - (prerenumeration)",
@@ -687,7 +686,6 @@ static struct usb_serial_driver keyspan_pda_fake_device = {
static struct usb_serial_driver keyspan_pda_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "keyspan_pda",
},
.description = "Keyspan PDA",
diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c
index 394b3189e003..d36155b6d2bf 100644
--- a/drivers/usb/serial/kl5kusb105.c
+++ b/drivers/usb/serial/kl5kusb105.c
@@ -39,7 +39,7 @@
#include <linux/tty_flip.h>
#include <linux/module.h>
#include <linux/uaccess.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
#include "kl5kusb105.h"
@@ -75,7 +75,6 @@ MODULE_DEVICE_TABLE(usb, id_table);
static struct usb_serial_driver kl5kusb105d_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "kl5kusb105d",
},
.description = "KL5KUSB105D / PalmConnect",
diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c
index 5e775f68fcb8..464433be2034 100644
--- a/drivers/usb/serial/kobil_sct.c
+++ b/drivers/usb/serial/kobil_sct.c
@@ -77,7 +77,6 @@ MODULE_DEVICE_TABLE(usb, id_table);
static struct usb_serial_driver kobil_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "kobil",
},
.description = "KOBIL USB smart card terminal",
@@ -156,8 +155,7 @@ static void kobil_init_termios(struct tty_struct *tty)
{
/* Default to echo off and other sane device settings */
tty->termios.c_lflag = 0;
- tty->termios.c_iflag &= ~(ISIG | ICANON | ECHO | IEXTEN | XCASE);
- tty->termios.c_iflag |= IGNBRK | IGNPAR | IXOFF;
+ tty->termios.c_iflag = IGNBRK | IGNPAR | IXOFF;
/* do NOT translate CR to CR-NL (0x0A -> 0x0A 0x0D) */
tty->termios.c_oflag &= ~ONLCR;
}
diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c
index 6570c8817a80..2bce8cc03aca 100644
--- a/drivers/usb/serial/mct_u232.c
+++ b/drivers/usb/serial/mct_u232.c
@@ -26,7 +26,7 @@
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/uaccess.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
#include <linux/serial.h>
@@ -69,7 +69,6 @@ MODULE_DEVICE_TABLE(usb, id_table);
static struct usb_serial_driver mct_u232_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "mct_u232",
},
.description = "MCT U232",
diff --git a/drivers/usb/serial/metro-usb.c b/drivers/usb/serial/metro-usb.c
index 30ab565e0738..028878292901 100644
--- a/drivers/usb/serial/metro-usb.c
+++ b/drivers/usb/serial/metro-usb.c
@@ -341,7 +341,6 @@ static void metrousb_unthrottle(struct tty_struct *tty)
static struct usb_serial_driver metrousb_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "metro-usb",
},
.description = "Metrologic USB to Serial",
diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c
index 23544074eb1c..e59bfa7c8030 100644
--- a/drivers/usb/serial/mos7720.c
+++ b/drivers/usb/serial/mos7720.c
@@ -1724,7 +1724,6 @@ static void mos7720_port_remove(struct usb_serial_port *port)
static struct usb_serial_driver moschip7720_2port_driver = {
.driver = {
- .owner = THIS_MODULE,
.name = "moschip7720",
},
.description = "Moschip 2 port adapter",
diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c
index 8b0308d84270..ca3da79afd23 100644
--- a/drivers/usb/serial/mos7840.c
+++ b/drivers/usb/serial/mos7840.c
@@ -1737,9 +1737,51 @@ static void mos7840_port_remove(struct usb_serial_port *port)
kfree(mos7840_port);
}
+static int mos7840_suspend(struct usb_serial *serial, pm_message_t message)
+{
+ struct moschip_port *mos7840_port;
+ struct usb_serial_port *port;
+ int i;
+
+ for (i = 0; i < serial->num_ports; ++i) {
+ port = serial->port[i];
+ if (!tty_port_initialized(&port->port))
+ continue;
+
+ mos7840_port = usb_get_serial_port_data(port);
+
+ usb_kill_urb(mos7840_port->read_urb);
+ mos7840_port->read_urb_busy = false;
+ }
+
+ return 0;
+}
+
+static int mos7840_resume(struct usb_serial *serial)
+{
+ struct moschip_port *mos7840_port;
+ struct usb_serial_port *port;
+ int res;
+ int i;
+
+ for (i = 0; i < serial->num_ports; ++i) {
+ port = serial->port[i];
+ if (!tty_port_initialized(&port->port))
+ continue;
+
+ mos7840_port = usb_get_serial_port_data(port);
+
+ mos7840_port->read_urb_busy = true;
+ res = usb_submit_urb(mos7840_port->read_urb, GFP_NOIO);
+ if (res)
+ mos7840_port->read_urb_busy = false;
+ }
+
+ return 0;
+}
+
static struct usb_serial_driver moschip7840_4port_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "mos7840",
},
.description = DRIVER_DESC,
@@ -1764,6 +1806,8 @@ static struct usb_serial_driver moschip7840_4port_device = {
.port_probe = mos7840_port_probe,
.port_remove = mos7840_port_remove,
.read_bulk_callback = mos7840_bulk_in_callback,
+ .suspend = mos7840_suspend,
+ .resume = mos7840_resume,
};
static struct usb_serial_driver * const serial_drivers[] = {
diff --git a/drivers/usb/serial/mxuport.c b/drivers/usb/serial/mxuport.c
index 1f7bb3e4fcf2..ad5fdf55a02e 100644
--- a/drivers/usb/serial/mxuport.c
+++ b/drivers/usb/serial/mxuport.c
@@ -25,7 +25,7 @@
#include <linux/uaccess.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
/* Definitions for the vendor ID and device ID */
#define MX_USBSERIAL_VID 0x110A
@@ -1278,7 +1278,6 @@ static int mxuport_resume(struct usb_serial *serial)
static struct usb_serial_driver mxuport_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "mxuport",
},
.description = "MOXA UPort",
@@ -1315,4 +1314,5 @@ module_usb_serial_driver(serial_drivers, mxuport_idtable);
MODULE_AUTHOR("Andrew Lunn <andrew@lunn.ch>");
MODULE_AUTHOR("<support@moxa.com>");
+MODULE_DESCRIPTION("Moxa UPORT USB Serial driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/usb/serial/navman.c b/drivers/usb/serial/navman.c
index 20277c52dded..4d57a5902292 100644
--- a/drivers/usb/serial/navman.c
+++ b/drivers/usb/serial/navman.c
@@ -95,7 +95,6 @@ static int navman_write(struct tty_struct *tty, struct usb_serial_port *port,
static struct usb_serial_driver navman_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "navman",
},
.id_table = id_table,
@@ -112,4 +111,5 @@ static struct usb_serial_driver * const serial_drivers[] = {
module_usb_serial_driver(serial_drivers, id_table);
+MODULE_DESCRIPTION("Navman USB Serial driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c
index 41f1b872d277..397ebd5a3e74 100644
--- a/drivers/usb/serial/omninet.c
+++ b/drivers/usb/serial/omninet.c
@@ -49,7 +49,6 @@ MODULE_DEVICE_TABLE(usb, id_table);
static struct usb_serial_driver zyxel_omninet_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "omninet",
},
.description = "ZyXEL - omni.net usb",
diff --git a/drivers/usb/serial/opticon.c b/drivers/usb/serial/opticon.c
index e31a6d77da3a..1ee84ccc4bbd 100644
--- a/drivers/usb/serial/opticon.c
+++ b/drivers/usb/serial/opticon.c
@@ -375,7 +375,6 @@ static void opticon_port_remove(struct usb_serial_port *port)
static struct usb_serial_driver opticon_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "opticon",
},
.id_table = id_table,
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index 8a5846d4adf6..eb0731992ca9 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -619,6 +619,8 @@ static void option_instat_callback(struct urb *urb);
/* MeiG Smart Technology products */
#define MEIGSMART_VENDOR_ID 0x2dee
+/* MeiG Smart SRM825L based on Qualcomm 315 */
+#define MEIGSMART_PRODUCT_SRM825L 0x4d22
/* MeiG Smart SLM320 based on UNISOC UIS8910 */
#define MEIGSMART_PRODUCT_SLM320 0x4d41
@@ -1425,6 +1427,10 @@ static const struct usb_device_id option_ids[] = {
.driver_info = NCTRL(0) | RSVD(1) },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1901, 0xff), /* Telit LN940 (MBIM) */
.driver_info = NCTRL(0) },
+ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x3000, 0xff), /* Telit FN912 */
+ .driver_info = RSVD(0) | NCTRL(3) },
+ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x3001, 0xff), /* Telit FN912 */
+ .driver_info = RSVD(0) | NCTRL(2) | RSVD(3) | RSVD(4) },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x7010, 0xff), /* Telit LE910-S1 (RNDIS) */
.driver_info = NCTRL(2) },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x7011, 0xff), /* Telit LE910-S1 (ECM) */
@@ -1433,6 +1439,8 @@ static const struct usb_device_id option_ids[] = {
.driver_info = NCTRL(2) },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x701b, 0xff), /* Telit LE910R1 (ECM) */
.driver_info = NCTRL(2) },
+ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x9000, 0xff), /* Telit generic core-dump device */
+ .driver_info = NCTRL(0) },
{ USB_DEVICE(TELIT_VENDOR_ID, 0x9010), /* Telit SBL FN980 flashing device */
.driver_info = NCTRL(0) | ZLP },
{ USB_DEVICE(TELIT_VENDOR_ID, 0x9200), /* Telit LE910S1 flashing device */
@@ -2224,6 +2232,10 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_7106_2COM, 0x02, 0x02, 0x01) },
{ USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_DC_4COM2, 0xff, 0x02, 0x01) },
{ USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_DC_4COM2, 0xff, 0x00, 0x00) },
+ { USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, 0x7126, 0xff, 0x00, 0x00),
+ .driver_info = NCTRL(2) },
+ { USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, 0x7127, 0xff, 0x00, 0x00),
+ .driver_info = NCTRL(2) | NCTRL(3) | NCTRL(4) },
{ USB_DEVICE(CELLIENT_VENDOR_ID, CELLIENT_PRODUCT_MEN200) },
{ USB_DEVICE(CELLIENT_VENDOR_ID, CELLIENT_PRODUCT_MPL200),
.driver_info = RSVD(1) | RSVD(4) },
@@ -2284,6 +2296,8 @@ static const struct usb_device_id option_ids[] = {
.driver_info = RSVD(3) },
{ USB_DEVICE_INTERFACE_CLASS(0x0489, 0xe0f0, 0xff), /* Foxconn T99W373 MBIM */
.driver_info = RSVD(3) },
+ { USB_DEVICE_INTERFACE_CLASS(0x0489, 0xe145, 0xff), /* Foxconn T99W651 RNDIS */
+ .driver_info = RSVD(5) | RSVD(6) },
{ USB_DEVICE(0x1508, 0x1001), /* Fibocom NL668 (IOT version) */
.driver_info = RSVD(4) | RSVD(5) | RSVD(6) },
{ USB_DEVICE(0x1782, 0x4d10) }, /* Fibocom L610 (AT mode) */
@@ -2321,6 +2335,32 @@ static const struct usb_device_id option_ids[] = {
.driver_info = RSVD(4) },
{ USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x0115, 0xff), /* Rolling RW135-GL (laptop MBIM) */
.driver_info = RSVD(5) },
+ { USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x0802, 0xff), /* Rolling RW350-GL (laptop MBIM) */
+ .driver_info = RSVD(5) },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0100, 0xff, 0xff, 0x30) }, /* NetPrisma LCUK54-WWD for Global */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0100, 0xff, 0x00, 0x40) },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0100, 0xff, 0xff, 0x40) },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0101, 0xff, 0xff, 0x30) }, /* NetPrisma LCUK54-WRD for Global SKU */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0101, 0xff, 0x00, 0x40) },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0101, 0xff, 0xff, 0x40) },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0106, 0xff, 0xff, 0x30) }, /* NetPrisma LCUK54-WRD for China SKU */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0106, 0xff, 0x00, 0x40) },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0106, 0xff, 0xff, 0x40) },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0111, 0xff, 0xff, 0x30) }, /* NetPrisma LCUK54-WWD for SA */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0111, 0xff, 0x00, 0x40) },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0111, 0xff, 0xff, 0x40) },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0112, 0xff, 0xff, 0x30) }, /* NetPrisma LCUK54-WWD for EU */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0112, 0xff, 0x00, 0x40) },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0112, 0xff, 0xff, 0x40) },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0113, 0xff, 0xff, 0x30) }, /* NetPrisma LCUK54-WWD for NA */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0113, 0xff, 0x00, 0x40) },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0113, 0xff, 0xff, 0x40) },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0115, 0xff, 0xff, 0x30) }, /* NetPrisma LCUK54-WWD for China EDU */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0115, 0xff, 0x00, 0x40) },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0115, 0xff, 0xff, 0x40) },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0116, 0xff, 0xff, 0x30) }, /* NetPrisma LCUK54-WWD for Golbal EDU */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0116, 0xff, 0x00, 0x40) },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0116, 0xff, 0xff, 0x40) },
{ USB_DEVICE_AND_INTERFACE_INFO(OPPO_VENDOR_ID, OPPO_PRODUCT_R11, 0xff, 0xff, 0x30) },
{ USB_DEVICE_AND_INTERFACE_INFO(SIERRA_VENDOR_ID, SIERRA_PRODUCT_EM9191, 0xff, 0xff, 0x30) },
{ USB_DEVICE_AND_INTERFACE_INFO(SIERRA_VENDOR_ID, SIERRA_PRODUCT_EM9191, 0xff, 0xff, 0x40) },
@@ -2328,6 +2368,9 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(UNISOC_VENDOR_ID, TOZED_PRODUCT_LT70C, 0xff, 0, 0) },
{ USB_DEVICE_AND_INTERFACE_INFO(UNISOC_VENDOR_ID, LUAT_PRODUCT_AIR720U, 0xff, 0, 0) },
{ USB_DEVICE_AND_INTERFACE_INFO(MEIGSMART_VENDOR_ID, MEIGSMART_PRODUCT_SLM320, 0xff, 0, 0) },
+ { USB_DEVICE_AND_INTERFACE_INFO(MEIGSMART_VENDOR_ID, MEIGSMART_PRODUCT_SRM825L, 0xff, 0xff, 0x30) },
+ { USB_DEVICE_AND_INTERFACE_INFO(MEIGSMART_VENDOR_ID, MEIGSMART_PRODUCT_SRM825L, 0xff, 0xff, 0x40) },
+ { USB_DEVICE_AND_INTERFACE_INFO(MEIGSMART_VENDOR_ID, MEIGSMART_PRODUCT_SRM825L, 0xff, 0xff, 0x60) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, option_ids);
@@ -2338,7 +2381,6 @@ MODULE_DEVICE_TABLE(usb, option_ids);
static struct usb_serial_driver option_1port_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "option1",
},
.description = "GSM modem (1-port)",
diff --git a/drivers/usb/serial/oti6858.c b/drivers/usb/serial/oti6858.c
index fa07f6ff9ecc..24068368892c 100644
--- a/drivers/usb/serial/oti6858.c
+++ b/drivers/usb/serial/oti6858.c
@@ -138,7 +138,6 @@ static void oti6858_port_remove(struct usb_serial_port *port);
/* device info */
static struct usb_serial_driver oti6858_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "oti6858",
},
.id_table = id_table,
diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c
index d93f5d584557..ad41363e3cea 100644
--- a/drivers/usb/serial/pl2303.c
+++ b/drivers/usb/serial/pl2303.c
@@ -24,7 +24,7 @@
#include <linux/uaccess.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include "pl2303.h"
@@ -118,6 +118,7 @@ static const struct usb_device_id id_table[] = {
{ USB_DEVICE(SMART_VENDOR_ID, SMART_PRODUCT_ID) },
{ USB_DEVICE(AT_VENDOR_ID, AT_VTKIT3_PRODUCT_ID) },
{ USB_DEVICE(IBM_VENDOR_ID, IBM_PRODUCT_ID) },
+ { USB_DEVICE(MACROSILICON_VENDOR_ID, MACROSILICON_MS3020_PRODUCT_ID) },
{ } /* Terminating entry */
};
@@ -1234,7 +1235,6 @@ static void pl2303_process_read_urb(struct urb *urb)
static struct usb_serial_driver pl2303_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "pl2303",
},
.id_table = id_table,
diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h
index 732f9b13ad5d..d60eda7f6eda 100644
--- a/drivers/usb/serial/pl2303.h
+++ b/drivers/usb/serial/pl2303.h
@@ -171,3 +171,7 @@
/* Allied Telesis VT-Kit3 */
#define AT_VENDOR_ID 0x0caa
#define AT_VTKIT3_PRODUCT_ID 0x3001
+
+/* Macrosilicon MS3020 */
+#define MACROSILICON_VENDOR_ID 0x345f
+#define MACROSILICON_MS3020_PRODUCT_ID 0x3020
diff --git a/drivers/usb/serial/qcaux.c b/drivers/usb/serial/qcaux.c
index 929ffba663f2..485ec5b07122 100644
--- a/drivers/usb/serial/qcaux.c
+++ b/drivers/usb/serial/qcaux.c
@@ -72,7 +72,6 @@ MODULE_DEVICE_TABLE(usb, id_table);
static struct usb_serial_driver qcaux_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "qcaux",
},
.id_table = id_table,
@@ -84,4 +83,5 @@ static struct usb_serial_driver * const serial_drivers[] = {
};
module_usb_serial_driver(serial_drivers, id_table);
+MODULE_DESCRIPTION("Qualcomm USB Auxiliary Serial Port driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c
index 703a9c563557..c7de9585feb2 100644
--- a/drivers/usb/serial/qcserial.c
+++ b/drivers/usb/serial/qcserial.c
@@ -454,7 +454,6 @@ static void qc_release(struct usb_serial *serial)
static struct usb_serial_driver qcdevice = {
.driver = {
- .owner = THIS_MODULE,
.name = "qcserial",
},
.description = "Qualcomm USB modem",
diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c
index 821f25e52ec2..a317bdbd00ad 100644
--- a/drivers/usb/serial/quatech2.c
+++ b/drivers/usb/serial/quatech2.c
@@ -9,7 +9,7 @@
*
*/
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/tty.h>
@@ -924,7 +924,6 @@ write_out:
static struct usb_serial_driver qt2_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "quatech-serial",
},
.description = DRIVER_DESC,
diff --git a/drivers/usb/serial/safe_serial.c b/drivers/usb/serial/safe_serial.c
index 6accbecb6318..238b54993446 100644
--- a/drivers/usb/serial/safe_serial.c
+++ b/drivers/usb/serial/safe_serial.c
@@ -284,7 +284,6 @@ static int safe_startup(struct usb_serial *serial)
static struct usb_serial_driver safe_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "safe_serial",
},
.id_table = id_table,
diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c
index 353b2549eaa8..64a2e0bb5723 100644
--- a/drivers/usb/serial/sierra.c
+++ b/drivers/usb/serial/sierra.c
@@ -1021,7 +1021,6 @@ static int sierra_resume(struct usb_serial *serial)
static struct usb_serial_driver sierra_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "sierra",
},
.description = "Sierra USB modem",
diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c
index 09a972a838ee..11077beb7232 100644
--- a/drivers/usb/serial/spcp8x5.c
+++ b/drivers/usb/serial/spcp8x5.c
@@ -49,16 +49,6 @@ static const struct usb_device_id id_table[] = {
};
MODULE_DEVICE_TABLE(usb, id_table);
-struct spcp8x5_usb_ctrl_arg {
- u8 type;
- u8 cmd;
- u8 cmd_type;
- u16 value;
- u16 index;
- u16 length;
-};
-
-
/* spcp8x5 spec register define */
#define MCR_CONTROL_LINE_RTS 0x02
#define MCR_CONTROL_LINE_DTR 0x01
@@ -462,7 +452,6 @@ static int spcp8x5_tiocmget(struct tty_struct *tty)
static struct usb_serial_driver spcp8x5_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "SPCP8x5",
},
.id_table = id_table,
diff --git a/drivers/usb/serial/ssu100.c b/drivers/usb/serial/ssu100.c
index 1e1888b66305..df21009bdf42 100644
--- a/drivers/usb/serial/ssu100.c
+++ b/drivers/usb/serial/ssu100.c
@@ -500,7 +500,6 @@ static void ssu100_process_read_urb(struct urb *urb)
static struct usb_serial_driver ssu100_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "ssu100",
},
.description = DRIVER_DESC,
diff --git a/drivers/usb/serial/symbolserial.c b/drivers/usb/serial/symbolserial.c
index d7f73ad6e778..58962bcbf9ba 100644
--- a/drivers/usb/serial/symbolserial.c
+++ b/drivers/usb/serial/symbolserial.c
@@ -169,7 +169,6 @@ static void symbol_port_remove(struct usb_serial_port *port)
static struct usb_serial_driver symbol_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "symbol",
},
.id_table = id_table,
@@ -190,4 +189,5 @@ static struct usb_serial_driver * const serial_drivers[] = {
module_usb_serial_driver(serial_drivers, id_table);
+MODULE_DESCRIPTION("Symbol USB barcode to serial driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c
index 0fba25abf671..a0c244bc77c0 100644
--- a/drivers/usb/serial/ti_usb_3410_5052.c
+++ b/drivers/usb/serial/ti_usb_3410_5052.c
@@ -417,7 +417,6 @@ static const struct usb_device_id ti_id_table_combined[] = {
static struct usb_serial_driver ti_1port_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "ti_usb_3410_5052_1",
},
.description = "TI USB 3410 1 port adapter",
@@ -450,7 +449,6 @@ static struct usb_serial_driver ti_1port_device = {
static struct usb_serial_driver ti_2port_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "ti_usb_3410_5052_2",
},
.description = "TI USB 5052 2 port adapter",
diff --git a/drivers/usb/serial/upd78f0730.c b/drivers/usb/serial/upd78f0730.c
index 46952182e04f..15a17bf111f1 100644
--- a/drivers/usb/serial/upd78f0730.c
+++ b/drivers/usb/serial/upd78f0730.c
@@ -407,7 +407,6 @@ static void upd78f0730_close(struct usb_serial_port *port)
static struct usb_serial_driver upd78f0730_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "upd78f0730",
},
.id_table = id_table,
diff --git a/drivers/usb/serial/usb-serial-simple.c b/drivers/usb/serial/usb-serial-simple.c
index 24b8772a345e..2c12449ff60c 100644
--- a/drivers/usb/serial/usb-serial-simple.c
+++ b/drivers/usb/serial/usb-serial-simple.c
@@ -24,7 +24,6 @@ static const struct usb_device_id vendor##_id_table[] = { \
}; \
static struct usb_serial_driver vendor##_device = { \
.driver = { \
- .owner = THIS_MODULE, \
.name = #vendor, \
}, \
.id_table = vendor##_id_table, \
@@ -163,4 +162,5 @@ static const struct usb_device_id id_table[] = {
MODULE_DEVICE_TABLE(usb, id_table);
module_usb_serial_driver(serial_drivers, id_table);
+MODULE_DESCRIPTION("USB Serial 'Simple' driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c
index f1e91eb7f8a4..df6a2ae0bf42 100644
--- a/drivers/usb/serial/usb-serial.c
+++ b/drivers/usb/serial/usb-serial.c
@@ -1459,17 +1459,18 @@ static void usb_serial_deregister(struct usb_serial_driver *device)
}
/**
- * usb_serial_register_drivers - register drivers for a usb-serial module
+ * __usb_serial_register_drivers - register drivers for a usb-serial module
* @serial_drivers: NULL-terminated array of pointers to drivers to be registered
+ * @owner: owning module
* @name: name of the usb_driver for this set of @serial_drivers
* @id_table: list of all devices this @serial_drivers set binds to
*
* Registers all the drivers in the @serial_drivers array, and dynamically
* creates a struct usb_driver with the name @name and id_table of @id_table.
*/
-int usb_serial_register_drivers(struct usb_serial_driver *const serial_drivers[],
- const char *name,
- const struct usb_device_id *id_table)
+int __usb_serial_register_drivers(struct usb_serial_driver *const serial_drivers[],
+ struct module *owner, const char *name,
+ const struct usb_device_id *id_table)
{
int rc;
struct usb_driver *udriver;
@@ -1514,6 +1515,7 @@ int usb_serial_register_drivers(struct usb_serial_driver *const serial_drivers[]
for (sd = serial_drivers; *sd; ++sd) {
(*sd)->usb_driver = udriver;
+ (*sd)->driver.owner = owner;
rc = usb_serial_register(*sd);
if (rc)
goto err_deregister_drivers;
@@ -1532,7 +1534,7 @@ err_free_driver:
kfree(udriver);
return rc;
}
-EXPORT_SYMBOL_GPL(usb_serial_register_drivers);
+EXPORT_SYMBOL_GPL(__usb_serial_register_drivers);
/**
* usb_serial_deregister_drivers - deregister drivers for a usb-serial module
diff --git a/drivers/usb/serial/usb_debug.c b/drivers/usb/serial/usb_debug.c
index 6934970f180d..ec9fff794b36 100644
--- a/drivers/usb/serial/usb_debug.c
+++ b/drivers/usb/serial/usb_debug.c
@@ -76,26 +76,31 @@ static void usb_debug_process_read_urb(struct urb *urb)
usb_serial_generic_process_read_urb(urb);
}
+static void usb_debug_init_termios(struct tty_struct *tty)
+{
+ tty->termios.c_lflag &= ~(ECHO | ECHONL);
+}
+
static struct usb_serial_driver debug_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "debug",
},
.id_table = id_table,
.num_ports = 1,
.bulk_out_size = USB_DEBUG_MAX_PACKET_SIZE,
.break_ctl = usb_debug_break_ctl,
+ .init_termios = usb_debug_init_termios,
.process_read_urb = usb_debug_process_read_urb,
};
static struct usb_serial_driver dbc_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "xhci_dbc",
},
.id_table = dbc_id_table,
.num_ports = 1,
.break_ctl = usb_debug_break_ctl,
+ .init_termios = usb_debug_init_termios,
.process_read_urb = usb_debug_process_read_urb,
};
@@ -104,4 +109,5 @@ static struct usb_serial_driver * const serial_drivers[] = {
};
module_usb_serial_driver(serial_drivers, id_table_combined);
+MODULE_DESCRIPTION("USB Debug cable driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c
index 4412834db21c..062a38fe0c1c 100644
--- a/drivers/usb/serial/visor.c
+++ b/drivers/usb/serial/visor.c
@@ -161,7 +161,6 @@ MODULE_DEVICE_TABLE(usb, id_table_combined);
and Palm 4.0 devices */
static struct usb_serial_driver handspring_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "visor",
},
.description = "Handspring Visor / Palm OS",
@@ -180,7 +179,6 @@ static struct usb_serial_driver handspring_device = {
/* All of the device info needed for the Clie UX50, TH55 Palm 5.0 devices */
static struct usb_serial_driver clie_5_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "clie_5",
},
.description = "Sony Clie 5.0",
@@ -200,7 +198,6 @@ static struct usb_serial_driver clie_5_device = {
/* device info for the Sony Clie OS version 3.5 */
static struct usb_serial_driver clie_3_5_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "clie_3.5",
},
.description = "Sony Clie 3.5",
diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c
index ca48e90a8e81..009faeb2ef55 100644
--- a/drivers/usb/serial/whiteheat.c
+++ b/drivers/usb/serial/whiteheat.c
@@ -91,7 +91,6 @@ static int whiteheat_break_ctl(struct tty_struct *tty, int break_state);
static struct usb_serial_driver whiteheat_fake_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "whiteheatnofirm",
},
.description = "Connect Tech - WhiteHEAT - (prerenumeration)",
@@ -103,7 +102,6 @@ static struct usb_serial_driver whiteheat_fake_device = {
static struct usb_serial_driver whiteheat_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "whiteheat",
},
.description = "Connect Tech - WhiteHEAT",
diff --git a/drivers/usb/serial/wishbone-serial.c b/drivers/usb/serial/wishbone-serial.c
index ff4092f9b33c..670d573f6b63 100644
--- a/drivers/usb/serial/wishbone-serial.c
+++ b/drivers/usb/serial/wishbone-serial.c
@@ -70,7 +70,6 @@ static void wishbone_serial_close(struct usb_serial_port *port)
static struct usb_serial_driver wishbone_serial_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "wishbone_serial",
},
.id_table = id_table,
diff --git a/drivers/usb/serial/xr_serial.c b/drivers/usb/serial/xr_serial.c
index 1d9a12628f81..4186e791b384 100644
--- a/drivers/usb/serial/xr_serial.c
+++ b/drivers/usb/serial/xr_serial.c
@@ -1082,7 +1082,6 @@ MODULE_DEVICE_TABLE(usb, id_table);
static struct usb_serial_driver xr_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "xr_serial",
},
.id_table = id_table,
diff --git a/drivers/usb/serial/xsens_mt.c b/drivers/usb/serial/xsens_mt.c
index cf262c9a9638..382b3698c1d5 100644
--- a/drivers/usb/serial/xsens_mt.c
+++ b/drivers/usb/serial/xsens_mt.c
@@ -49,7 +49,6 @@ static int xsens_mt_probe(struct usb_serial *serial,
static struct usb_serial_driver xsens_mt_device = {
.driver = {
- .owner = THIS_MODULE,
.name = "xsens_mt",
},
.id_table = id_table,
diff --git a/drivers/usb/storage/alauda.c b/drivers/usb/storage/alauda.c
index 115f05a6201a..a9d3c58ce7d9 100644
--- a/drivers/usb/storage/alauda.c
+++ b/drivers/usb/storage/alauda.c
@@ -105,6 +105,8 @@ struct alauda_info {
unsigned char sense_key;
unsigned long sense_asc; /* additional sense code */
unsigned long sense_ascq; /* additional sense code qualifier */
+
+ bool media_initialized;
};
#define short_pack(lsb,msb) ( ((u16)(lsb)) | ( ((u16)(msb))<<8 ) )
@@ -130,7 +132,7 @@ static int init_alauda(struct us_data *us);
{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \
.driver_info = (flags) }
-static struct usb_device_id alauda_usb_ids[] = {
+static const struct usb_device_id alauda_usb_ids[] = {
# include "unusual_alauda.h"
{ } /* Terminating entry */
};
@@ -152,7 +154,7 @@ MODULE_DEVICE_TABLE(usb, alauda_usb_ids);
.initFunction = init_function, \
}
-static struct us_unusual_dev alauda_unusual_dev_list[] = {
+static const struct us_unusual_dev alauda_unusual_dev_list[] = {
# include "unusual_alauda.h"
{ } /* Terminating entry */
};
@@ -476,11 +478,12 @@ static int alauda_check_media(struct us_data *us)
}
/* Check for media change */
- if (status[0] & 0x08) {
+ if (status[0] & 0x08 || !info->media_initialized) {
usb_stor_dbg(us, "Media change detected\n");
alauda_free_maps(&MEDIA_INFO(us));
- alauda_init_media(us);
-
+ rc = alauda_init_media(us);
+ if (rc == USB_STOR_TRANSPORT_GOOD)
+ info->media_initialized = true;
info->sense_key = UNIT_ATTENTION;
info->sense_asc = 0x28;
info->sense_ascq = 0x00;
diff --git a/drivers/usb/storage/cypress_atacb.c b/drivers/usb/storage/cypress_atacb.c
index 98b3ec352a13..30dfd0082474 100644
--- a/drivers/usb/storage/cypress_atacb.c
+++ b/drivers/usb/storage/cypress_atacb.c
@@ -33,7 +33,7 @@ MODULE_IMPORT_NS(USB_STORAGE);
{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \
.driver_info = (flags) }
-static struct usb_device_id cypress_usb_ids[] = {
+static const struct usb_device_id cypress_usb_ids[] = {
# include "unusual_cypress.h"
{ } /* Terminating entry */
};
@@ -55,7 +55,7 @@ MODULE_DEVICE_TABLE(usb, cypress_usb_ids);
.initFunction = init_function, \
}
-static struct us_unusual_dev cypress_unusual_dev_list[] = {
+static const struct us_unusual_dev cypress_unusual_dev_list[] = {
# include "unusual_cypress.h"
{ } /* Terminating entry */
};
diff --git a/drivers/usb/storage/datafab.c b/drivers/usb/storage/datafab.c
index bcc4a2fad863..3ea5601d16b8 100644
--- a/drivers/usb/storage/datafab.c
+++ b/drivers/usb/storage/datafab.c
@@ -80,7 +80,7 @@ static int datafab_determine_lun(struct us_data *us,
{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \
.driver_info = (flags) }
-static struct usb_device_id datafab_usb_ids[] = {
+static const struct usb_device_id datafab_usb_ids[] = {
# include "unusual_datafab.h"
{ } /* Terminating entry */
};
@@ -102,7 +102,7 @@ MODULE_DEVICE_TABLE(usb, datafab_usb_ids);
.initFunction = init_function, \
}
-static struct us_unusual_dev datafab_unusual_dev_list[] = {
+static const struct us_unusual_dev datafab_unusual_dev_list[] = {
# include "unusual_datafab.h"
{ } /* Terminating entry */
};
diff --git a/drivers/usb/storage/ene_ub6250.c b/drivers/usb/storage/ene_ub6250.c
index 97c66c0d91f4..a4bfbecbf16c 100644
--- a/drivers/usb/storage/ene_ub6250.c
+++ b/drivers/usb/storage/ene_ub6250.c
@@ -43,7 +43,7 @@ MODULE_FIRMWARE(MS_RW_FIRMWARE);
{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \
.driver_info = (flags)}
-static struct usb_device_id ene_ub6250_usb_ids[] = {
+static const struct usb_device_id ene_ub6250_usb_ids[] = {
# include "unusual_ene_ub6250.h"
{ } /* Terminating entry */
};
@@ -65,7 +65,7 @@ MODULE_DEVICE_TABLE(usb, ene_ub6250_usb_ids);
.initFunction = init_function, \
}
-static struct us_unusual_dev ene_ub6250_unusual_dev_list[] = {
+static const struct us_unusual_dev ene_ub6250_unusual_dev_list[] = {
# include "unusual_ene_ub6250.h"
{ } /* Terminating entry */
};
@@ -1484,7 +1484,7 @@ static int ms_scsi_mode_sense(struct us_data *us, struct scsi_cmnd *srb)
static int ms_scsi_read_capacity(struct us_data *us, struct scsi_cmnd *srb)
{
u32 bl_num;
- u16 bl_len;
+ u32 bl_len;
unsigned int offset = 0;
unsigned char buf[8];
struct scatterlist *sg = NULL;
diff --git a/drivers/usb/storage/freecom.c b/drivers/usb/storage/freecom.c
index c3ce51c2dabd..cab27ba7a32a 100644
--- a/drivers/usb/storage/freecom.c
+++ b/drivers/usb/storage/freecom.c
@@ -119,7 +119,7 @@ static int init_freecom(struct us_data *us);
{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \
.driver_info = (flags) }
-static struct usb_device_id freecom_usb_ids[] = {
+static const struct usb_device_id freecom_usb_ids[] = {
# include "unusual_freecom.h"
{ } /* Terminating entry */
};
@@ -141,7 +141,7 @@ MODULE_DEVICE_TABLE(usb, freecom_usb_ids);
.initFunction = init_function, \
}
-static struct us_unusual_dev freecom_unusual_dev_list[] = {
+static const struct us_unusual_dev freecom_unusual_dev_list[] = {
# include "unusual_freecom.h"
{ } /* Terminating entry */
};
diff --git a/drivers/usb/storage/isd200.c b/drivers/usb/storage/isd200.c
index 300aeef160e7..f2254eb3c0d7 100644
--- a/drivers/usb/storage/isd200.c
+++ b/drivers/usb/storage/isd200.c
@@ -67,7 +67,7 @@ static int isd200_Initialization(struct us_data *us);
{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \
.driver_info = (flags) }
-static struct usb_device_id isd200_usb_ids[] = {
+static const struct usb_device_id isd200_usb_ids[] = {
# include "unusual_isd200.h"
{ } /* Terminating entry */
};
@@ -89,7 +89,7 @@ MODULE_DEVICE_TABLE(usb, isd200_usb_ids);
.initFunction = init_function, \
}
-static struct us_unusual_dev isd200_unusual_dev_list[] = {
+static const struct us_unusual_dev isd200_unusual_dev_list[] = {
# include "unusual_isd200.h"
{ } /* Terminating entry */
};
diff --git a/drivers/usb/storage/jumpshot.c b/drivers/usb/storage/jumpshot.c
index 229bf0c1afc9..0e71a8f33c2b 100644
--- a/drivers/usb/storage/jumpshot.c
+++ b/drivers/usb/storage/jumpshot.c
@@ -62,7 +62,7 @@ MODULE_IMPORT_NS(USB_STORAGE);
{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \
.driver_info = (flags) }
-static struct usb_device_id jumpshot_usb_ids[] = {
+static const struct usb_device_id jumpshot_usb_ids[] = {
# include "unusual_jumpshot.h"
{ } /* Terminating entry */
};
@@ -84,7 +84,7 @@ MODULE_DEVICE_TABLE(usb, jumpshot_usb_ids);
.initFunction = init_function, \
}
-static struct us_unusual_dev jumpshot_unusual_dev_list[] = {
+static const struct us_unusual_dev jumpshot_unusual_dev_list[] = {
# include "unusual_jumpshot.h"
{ } /* Terminating entry */
};
diff --git a/drivers/usb/storage/karma.c b/drivers/usb/storage/karma.c
index 38ddfedef629..d6a5e54f2ca8 100644
--- a/drivers/usb/storage/karma.c
+++ b/drivers/usb/storage/karma.c
@@ -51,7 +51,7 @@ static int rio_karma_init(struct us_data *us);
{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \
.driver_info = (flags) }
-static struct usb_device_id karma_usb_ids[] = {
+static const struct usb_device_id karma_usb_ids[] = {
# include "unusual_karma.h"
{ } /* Terminating entry */
};
@@ -73,7 +73,7 @@ MODULE_DEVICE_TABLE(usb, karma_usb_ids);
.initFunction = init_function, \
}
-static struct us_unusual_dev karma_unusual_dev_list[] = {
+static const struct us_unusual_dev karma_unusual_dev_list[] = {
# include "unusual_karma.h"
{ } /* Terminating entry */
};
diff --git a/drivers/usb/storage/onetouch.c b/drivers/usb/storage/onetouch.c
index 01f3c2779ccf..f97cf6cadb8e 100644
--- a/drivers/usb/storage/onetouch.c
+++ b/drivers/usb/storage/onetouch.c
@@ -55,7 +55,7 @@ struct usb_onetouch {
{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \
.driver_info = (flags) }
-static struct usb_device_id onetouch_usb_ids[] = {
+static const struct usb_device_id onetouch_usb_ids[] = {
# include "unusual_onetouch.h"
{ } /* Terminating entry */
};
@@ -77,7 +77,7 @@ MODULE_DEVICE_TABLE(usb, onetouch_usb_ids);
.initFunction = init_function, \
}
-static struct us_unusual_dev onetouch_unusual_dev_list[] = {
+static const struct us_unusual_dev onetouch_unusual_dev_list[] = {
# include "unusual_onetouch.h"
{ } /* Terminating entry */
};
diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c
index 12cf9940e5b6..8c8b5e6041cc 100644
--- a/drivers/usb/storage/scsiglue.c
+++ b/drivers/usb/storage/scsiglue.c
@@ -40,7 +40,6 @@
#include <scsi/scsi_eh.h>
#include "usb.h"
-#include <linux/usb/hcd.h>
#include "scsiglue.h"
#include "debug.h"
#include "transport.h"
@@ -76,20 +75,20 @@ static int slave_alloc (struct scsi_device *sdev)
*/
sdev->inquiry_len = 36;
- /*
- * Some host controllers may have alignment requirements.
- * We'll play it safe by requiring 512-byte alignment always.
- */
- blk_queue_update_dma_alignment(sdev->request_queue, (512 - 1));
-
/* Tell the SCSI layer if we know there is more than one LUN */
if (us->protocol == USB_PR_BULK && us->max_lun > 0)
sdev->sdev_bflags |= BLIST_FORCELUN;
+ /*
+ * Some USB storage devices reset if the IO advice hints grouping mode
+ * page is queried. Hence skip that mode page.
+ */
+ sdev->sdev_bflags |= BLIST_SKIP_IO_HINTS;
+
return 0;
}
-static int slave_configure(struct scsi_device *sdev)
+static int device_configure(struct scsi_device *sdev, struct queue_limits *lim)
{
struct us_data *us = host_to_us(sdev->host);
struct device *dev = us->pusb_dev->bus->sysdev;
@@ -104,40 +103,28 @@ static int slave_configure(struct scsi_device *sdev)
if (us->fflags & US_FL_MAX_SECTORS_MIN)
max_sectors = PAGE_SIZE >> 9;
- if (queue_max_hw_sectors(sdev->request_queue) > max_sectors)
- blk_queue_max_hw_sectors(sdev->request_queue,
- max_sectors);
+ lim->max_hw_sectors = min(lim->max_hw_sectors, max_sectors);
} else if (sdev->type == TYPE_TAPE) {
/*
* Tapes need much higher max_sector limits, so just
* raise it to the maximum possible (4 GB / 512) and
* let the queue segment size sort out the real limit.
*/
- blk_queue_max_hw_sectors(sdev->request_queue, 0x7FFFFF);
+ lim->max_hw_sectors = 0x7FFFFF;
} else if (us->pusb_dev->speed >= USB_SPEED_SUPER) {
/*
* USB3 devices will be limited to 2048 sectors. This gives us
* better throughput on most devices.
*/
- blk_queue_max_hw_sectors(sdev->request_queue, 2048);
+ lim->max_hw_sectors = 2048;
}
/*
* The max_hw_sectors should be up to maximum size of a mapping for
* the device. Otherwise, a DMA API might fail on swiotlb environment.
*/
- blk_queue_max_hw_sectors(sdev->request_queue,
- min_t(size_t, queue_max_hw_sectors(sdev->request_queue),
- dma_max_mapping_size(dev) >> SECTOR_SHIFT));
-
- /*
- * Some USB host controllers can't do DMA; they have to use PIO.
- * For such controllers we need to make sure the block layer sets
- * up bounce buffers in addressable memory.
- */
- if (!hcd_uses_dma(bus_to_hcd(us->pusb_dev->bus)) ||
- (bus_to_hcd(us->pusb_dev->bus)->localmem_pool != NULL))
- blk_queue_bounce_limit(sdev->request_queue, BLK_BOUNCE_HIGH);
+ lim->max_hw_sectors = min_t(size_t,
+ lim->max_hw_sectors, dma_max_mapping_size(dev) >> SECTOR_SHIFT);
/*
* We can't put these settings in slave_alloc() because that gets
@@ -598,13 +585,22 @@ static ssize_t max_sectors_store(struct device *dev, struct device_attribute *at
size_t count)
{
struct scsi_device *sdev = to_scsi_device(dev);
+ struct queue_limits lim;
unsigned short ms;
+ int ret;
- if (sscanf(buf, "%hu", &ms) > 0) {
- blk_queue_max_hw_sectors(sdev->request_queue, ms);
- return count;
- }
- return -EINVAL;
+ if (sscanf(buf, "%hu", &ms) <= 0)
+ return -EINVAL;
+
+ blk_mq_freeze_queue(sdev->request_queue);
+ lim = queue_limits_start_update(sdev->request_queue);
+ lim.max_hw_sectors = ms;
+ ret = queue_limits_commit_update(sdev->request_queue, &lim);
+ blk_mq_unfreeze_queue(sdev->request_queue);
+
+ if (ret)
+ return ret;
+ return count;
}
static DEVICE_ATTR_RW(max_sectors);
@@ -642,12 +638,17 @@ static const struct scsi_host_template usb_stor_host_template = {
.this_id = -1,
.slave_alloc = slave_alloc,
- .slave_configure = slave_configure,
+ .device_configure = device_configure,
.target_alloc = target_alloc,
/* lots of sg segments can be handled */
.sg_tablesize = SG_MAX_SEGMENTS,
+ /*
+ * Some host controllers may have alignment requirements.
+ * We'll play it safe by requiring 512-byte alignment always.
+ */
+ .dma_alignment = 511,
/*
* Limit the total size of a transfer to 120 KB.
diff --git a/drivers/usb/storage/sddr09.c b/drivers/usb/storage/sddr09.c
index 51bcd4a43690..03d1b9c69ea1 100644
--- a/drivers/usb/storage/sddr09.c
+++ b/drivers/usb/storage/sddr09.c
@@ -63,7 +63,7 @@ static int usb_stor_sddr09_init(struct us_data *us);
{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \
.driver_info = (flags) }
-static struct usb_device_id sddr09_usb_ids[] = {
+static const struct usb_device_id sddr09_usb_ids[] = {
# include "unusual_sddr09.h"
{ } /* Terminating entry */
};
@@ -85,7 +85,7 @@ MODULE_DEVICE_TABLE(usb, sddr09_usb_ids);
.initFunction = init_function, \
}
-static struct us_unusual_dev sddr09_unusual_dev_list[] = {
+static const struct us_unusual_dev sddr09_unusual_dev_list[] = {
# include "unusual_sddr09.h"
{ } /* Terminating entry */
};
diff --git a/drivers/usb/storage/sddr55.c b/drivers/usb/storage/sddr55.c
index 0aa079405d23..b8227478a7ad 100644
--- a/drivers/usb/storage/sddr55.c
+++ b/drivers/usb/storage/sddr55.c
@@ -40,7 +40,7 @@ MODULE_IMPORT_NS(USB_STORAGE);
{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \
.driver_info = (flags) }
-static struct usb_device_id sddr55_usb_ids[] = {
+static const struct usb_device_id sddr55_usb_ids[] = {
# include "unusual_sddr55.h"
{ } /* Terminating entry */
};
@@ -62,7 +62,7 @@ MODULE_DEVICE_TABLE(usb, sddr55_usb_ids);
.initFunction = init_function, \
}
-static struct us_unusual_dev sddr55_unusual_dev_list[] = {
+static const struct us_unusual_dev sddr55_unusual_dev_list[] = {
# include "unusual_sddr55.h"
{ } /* Terminating entry */
};
diff --git a/drivers/usb/storage/shuttle_usbat.c b/drivers/usb/storage/shuttle_usbat.c
index f0d0ca37163d..e7c224b7c464 100644
--- a/drivers/usb/storage/shuttle_usbat.c
+++ b/drivers/usb/storage/shuttle_usbat.c
@@ -162,7 +162,7 @@ static int init_usbat_flash(struct us_data *us);
{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \
.driver_info = (flags) }
-static struct usb_device_id usbat_usb_ids[] = {
+static const struct usb_device_id usbat_usb_ids[] = {
# include "unusual_usbat.h"
{ } /* Terminating entry */
};
@@ -184,7 +184,7 @@ MODULE_DEVICE_TABLE(usb, usbat_usb_ids);
.initFunction = init_function, \
}
-static struct us_unusual_dev usbat_unusual_dev_list[] = {
+static const struct us_unusual_dev usbat_unusual_dev_list[] = {
# include "unusual_usbat.h"
{ } /* Terminating entry */
};
diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c
index 08953f0d4532..03043d567fa1 100644
--- a/drivers/usb/storage/uas.c
+++ b/drivers/usb/storage/uas.c
@@ -21,6 +21,7 @@
#include <scsi/scsi.h>
#include <scsi/scsi_eh.h>
#include <scsi/scsi_dbg.h>
+#include <scsi/scsi_devinfo.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
@@ -422,6 +423,7 @@ static void uas_data_cmplt(struct urb *urb)
uas_log_cmd_state(cmnd, "data cmplt err", status);
/* error: no data transfered */
scsi_set_resid(cmnd, sdb->length);
+ set_host_byte(cmnd, DID_ERROR);
} else {
scsi_set_resid(cmnd, sdb->length - urb->actual_length);
}
@@ -820,27 +822,26 @@ static int uas_slave_alloc(struct scsi_device *sdev)
struct uas_dev_info *devinfo =
(struct uas_dev_info *)sdev->host->hostdata;
- sdev->hostdata = devinfo;
-
/*
- * The protocol has no requirements on alignment in the strict sense.
- * Controllers may or may not have alignment restrictions.
- * As this is not exported, we use an extremely conservative guess.
+ * Some USB storage devices reset if the IO advice hints grouping mode
+ * page is queried. Hence skip that mode page.
*/
- blk_queue_update_dma_alignment(sdev->request_queue, (512 - 1));
-
- if (devinfo->flags & US_FL_MAX_SECTORS_64)
- blk_queue_max_hw_sectors(sdev->request_queue, 64);
- else if (devinfo->flags & US_FL_MAX_SECTORS_240)
- blk_queue_max_hw_sectors(sdev->request_queue, 240);
+ sdev->sdev_bflags |= BLIST_SKIP_IO_HINTS;
+ sdev->hostdata = devinfo;
return 0;
}
-static int uas_slave_configure(struct scsi_device *sdev)
+static int uas_device_configure(struct scsi_device *sdev,
+ struct queue_limits *lim)
{
struct uas_dev_info *devinfo = sdev->hostdata;
+ if (devinfo->flags & US_FL_MAX_SECTORS_64)
+ lim->max_hw_sectors = 64;
+ else if (devinfo->flags & US_FL_MAX_SECTORS_240)
+ lim->max_hw_sectors = 240;
+
if (devinfo->flags & US_FL_NO_REPORT_OPCODES)
sdev->no_report_opcodes = 1;
@@ -905,11 +906,17 @@ static const struct scsi_host_template uas_host_template = {
.queuecommand = uas_queuecommand,
.target_alloc = uas_target_alloc,
.slave_alloc = uas_slave_alloc,
- .slave_configure = uas_slave_configure,
+ .device_configure = uas_device_configure,
.eh_abort_handler = uas_eh_abort_handler,
.eh_device_reset_handler = uas_eh_device_reset_handler,
.this_id = -1,
.skip_settle_delay = 1,
+ /*
+ * The protocol has no requirements on alignment in the strict sense.
+ * Controllers may or may not have alignment restrictions.
+ * As this is not exported, we use an extremely conservative guess.
+ */
+ .dma_alignment = 511,
.dma_boundary = PAGE_SIZE - 1,
.cmd_size = sizeof(struct uas_cmd_info),
};
@@ -920,7 +927,7 @@ static const struct scsi_host_template uas_host_template = {
{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \
.driver_info = (flags) }
-static struct usb_device_id uas_usb_ids[] = {
+static const struct usb_device_id uas_usb_ids[] = {
# include "unusual_uas.h"
{ USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, USB_SC_SCSI, USB_PR_BULK) },
{ USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, USB_SC_SCSI, USB_PR_UAS) },
@@ -1226,9 +1233,8 @@ static void uas_disconnect(struct usb_interface *intf)
* hang on reboot when the device is still in uas mode. Note the reset is
* necessary as some devices won't revert to usb-storage mode without it.
*/
-static void uas_shutdown(struct device *dev)
+static void uas_shutdown(struct usb_interface *intf)
{
- struct usb_interface *intf = to_usb_interface(dev);
struct usb_device *udev = interface_to_usbdev(intf);
struct Scsi_Host *shost = usb_get_intfdata(intf);
struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata;
@@ -1251,7 +1257,7 @@ static struct usb_driver uas_driver = {
.suspend = uas_suspend,
.resume = uas_resume,
.reset_resume = uas_reset_resume,
- .driver.shutdown = uas_shutdown,
+ .shutdown = uas_shutdown,
.id_table = uas_usb_ids,
};
@@ -1281,6 +1287,7 @@ static void __exit uas_exit(void)
module_init(uas_init);
module_exit(uas_exit);
+MODULE_DESCRIPTION("USB Attached SCSI driver");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS(USB_STORAGE);
MODULE_AUTHOR(
diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c
index d1ad6a2509ab..d36f3b6992bb 100644
--- a/drivers/usb/storage/usb.c
+++ b/drivers/usb/storage/usb.c
@@ -47,6 +47,7 @@
#include <scsi/scsi_device.h>
#include "usb.h"
+#include <linux/usb/hcd.h>
#include "scsiglue.h"
#include "transport.h"
#include "protocol.h"
@@ -67,9 +68,102 @@ MODULE_AUTHOR("Matthew Dharm <mdharm-usb@one-eyed-alien.net>");
MODULE_DESCRIPTION("USB Mass Storage driver for Linux");
MODULE_LICENSE("GPL");
-static unsigned int delay_use = 1;
-module_param(delay_use, uint, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(delay_use, "seconds to delay before using a new device");
+static unsigned int delay_use = 1 * MSEC_PER_SEC;
+
+/**
+ * parse_delay_str - parse an unsigned decimal integer delay
+ * @str: String to parse.
+ * @ndecimals: Number of decimal to scale up.
+ * @suffix: Suffix string to parse.
+ * @val: Where to store the parsed value.
+ *
+ * Parse an unsigned decimal value in @str, optionally end with @suffix.
+ * Stores the parsed value in @val just as it is if @str ends with @suffix.
+ * Otherwise store the value scale up by 10^(@ndecimal).
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ */
+static int parse_delay_str(const char *str, int ndecimals, const char *suffix,
+ unsigned int *val)
+{
+ int n, n2, l;
+ char buf[16];
+
+ l = strlen(suffix);
+ n = strlen(str);
+ if (n > 0 && str[n - 1] == '\n')
+ --n;
+ if (n >= l && !strncmp(&str[n - l], suffix, l)) {
+ n -= l;
+ n2 = 0;
+ } else
+ n2 = ndecimals;
+
+ if (n + n2 > sizeof(buf) - 1)
+ return -EINVAL;
+
+ memcpy(buf, str, n);
+ while (n2-- > 0)
+ buf[n++] = '0';
+ buf[n] = 0;
+
+ return kstrtouint(buf, 10, val);
+}
+
+/**
+ * format_delay_ms - format an integer value into a delay string
+ * @val: The integer value to format, scaled by 10^(@ndecimals).
+ * @ndecimals: Number of decimal to scale down.
+ * @suffix: Suffix string to format.
+ * @str: Where to store the formatted string.
+ * @size: The size of buffer for @str.
+ *
+ * Format an integer value in @val scale down by 10^(@ndecimals) without @suffix
+ * if @val is divisible by 10^(@ndecimals).
+ * Otherwise format a value in @val just as it is with @suffix
+ *
+ * Returns the number of characters written into @str.
+ */
+static int format_delay_ms(unsigned int val, int ndecimals, const char *suffix,
+ char *str, int size)
+{
+ u64 delay_ms = val;
+ unsigned int rem = do_div(delay_ms, int_pow(10, ndecimals));
+ int ret;
+
+ if (rem)
+ ret = scnprintf(str, size, "%u%s\n", val, suffix);
+ else
+ ret = scnprintf(str, size, "%u\n", (unsigned int)delay_ms);
+ return ret;
+}
+
+static int delay_use_set(const char *s, const struct kernel_param *kp)
+{
+ unsigned int delay_ms;
+ int ret;
+
+ ret = parse_delay_str(skip_spaces(s), 3, "ms", &delay_ms);
+ if (ret < 0)
+ return ret;
+
+ *((unsigned int *)kp->arg) = delay_ms;
+ return 0;
+}
+
+static int delay_use_get(char *s, const struct kernel_param *kp)
+{
+ unsigned int delay_ms = *((unsigned int *)kp->arg);
+
+ return format_delay_ms(delay_ms, 3, "ms", s, PAGE_SIZE);
+}
+
+static const struct kernel_param_ops delay_use_ops = {
+ .set = delay_use_set,
+ .get = delay_use_get,
+};
+module_param_cb(delay_use, &delay_use_ops, &delay_use, 0644);
+MODULE_PARM_DESC(delay_use, "time to delay before using a new device");
static char quirks[128];
module_param_string(quirks, quirks, sizeof(quirks), S_IRUGO | S_IWUSR);
@@ -961,6 +1055,15 @@ int usb_stor_probe1(struct us_data **pus,
if (result)
goto BadDevice;
+ /*
+ * Some USB host controllers can't do DMA; they have to use PIO.
+ * For such controllers we need to make sure the block layer sets
+ * up bounce buffers in addressable memory.
+ */
+ if (!hcd_uses_dma(bus_to_hcd(us->pusb_dev->bus)) ||
+ bus_to_hcd(us->pusb_dev->bus)->localmem_pool)
+ host->no_highmem = true;
+
/* Get the unusual_devs entries and the descriptors */
result = get_device_info(us, id, unusual_dev);
if (result)
@@ -1054,7 +1157,7 @@ int usb_stor_probe2(struct us_data *us)
if (delay_use > 0)
dev_dbg(dev, "waiting for device to settle before scanning\n");
queue_delayed_work(system_freezable_wq, &us->scan_dwork,
- delay_use * HZ);
+ msecs_to_jiffies(delay_use));
return 0;
/* We come here if there are any problems */
diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c
index 038dc51f429d..92cc1b136120 100644
--- a/drivers/usb/typec/altmodes/displayport.c
+++ b/drivers/usb/typec/altmodes/displayport.c
@@ -746,7 +746,7 @@ int dp_altmode_probe(struct typec_altmode *alt)
dp->alt = alt;
alt->desc = "DisplayPort";
- alt->ops = &dp_altmode_ops;
+ typec_altmode_set_ops(alt, &dp_altmode_ops);
if (plug) {
plug->desc = "Displayport";
@@ -802,7 +802,6 @@ static struct typec_altmode_driver dp_altmode_driver = {
.remove = dp_altmode_remove,
.driver = {
.name = "typec_displayport",
- .owner = THIS_MODULE,
.dev_groups = displayport_groups,
},
};
diff --git a/drivers/usb/typec/altmodes/nvidia.c b/drivers/usb/typec/altmodes/nvidia.c
index c36769736405..fe70b36f078f 100644
--- a/drivers/usb/typec/altmodes/nvidia.c
+++ b/drivers/usb/typec/altmodes/nvidia.c
@@ -35,7 +35,6 @@ static struct typec_altmode_driver nvidia_altmode_driver = {
.remove = nvidia_altmode_remove,
.driver = {
.name = "typec_nvidia",
- .owner = THIS_MODULE,
},
};
module_typec_altmode_driver(nvidia_altmode_driver);
diff --git a/drivers/usb/typec/anx7411.c b/drivers/usb/typec/anx7411.c
index b12a07edc71b..d1e7c487ddfb 100644
--- a/drivers/usb/typec/anx7411.c
+++ b/drivers/usb/typec/anx7411.c
@@ -6,6 +6,7 @@
* Copyright(c) 2022, Analogix Semiconductor. All rights reserved.
*
*/
+#include <linux/bitfield.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
@@ -884,8 +885,8 @@ static void anx7411_chip_standby(struct anx7411_data *ctx)
OCM_RESET);
ret |= anx7411_reg_write(ctx->tcpc_client, ANALOG_CTRL_10, 0x80);
/* Set TCPC to RD and DRP enable */
- cc1 = TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC1_SHIFT;
- cc2 = TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC2_SHIFT;
+ cc1 = FIELD_PREP(TCPC_ROLE_CTRL_CC1, TCPC_ROLE_CTRL_CC_RD);
+ cc2 = FIELD_PREP(TCPC_ROLE_CTRL_CC2, TCPC_ROLE_CTRL_CC_RD);
ret |= anx7411_reg_write(ctx->tcpc_client, TCPC_ROLE_CTRL,
TCPC_ROLE_CTRL_DRP | cc1 | cc2);
@@ -1339,12 +1340,6 @@ static void anx7411_get_gpio_irq(struct anx7411_data *ctx)
dev_err(dev, "failed to get GPIO IRQ\n");
}
-static enum power_supply_usb_type anx7411_psy_usb_types[] = {
- POWER_SUPPLY_USB_TYPE_C,
- POWER_SUPPLY_USB_TYPE_PD,
- POWER_SUPPLY_USB_TYPE_PD_PPS,
-};
-
static enum power_supply_property anx7411_psy_props[] = {
POWER_SUPPLY_PROP_USB_TYPE,
POWER_SUPPLY_PROP_ONLINE,
@@ -1422,8 +1417,9 @@ static int anx7411_psy_register(struct anx7411_data *ctx)
psy_desc->name = psy_name;
psy_desc->type = POWER_SUPPLY_TYPE_USB;
- psy_desc->usb_types = anx7411_psy_usb_types;
- psy_desc->num_usb_types = ARRAY_SIZE(anx7411_psy_usb_types);
+ psy_desc->usb_types = BIT(POWER_SUPPLY_USB_TYPE_C) |
+ BIT(POWER_SUPPLY_USB_TYPE_PD) |
+ BIT(POWER_SUPPLY_USB_TYPE_PD_PPS);
psy_desc->properties = anx7411_psy_props;
psy_desc->num_properties = ARRAY_SIZE(anx7411_psy_props);
@@ -1566,7 +1562,7 @@ static void anx7411_i2c_remove(struct i2c_client *client)
}
static const struct i2c_device_id anx7411_id[] = {
- {"anx7411", 0},
+ { "anx7411" },
{}
};
@@ -1576,6 +1572,7 @@ static const struct of_device_id anx_match_table[] = {
{.compatible = "analogix,anx7411",},
{},
};
+MODULE_DEVICE_TABLE(of, anx_match_table);
static struct i2c_driver anx7411_driver = {
.driver = {
diff --git a/drivers/usb/typec/bus.c b/drivers/usb/typec/bus.c
index 6ea103e1abae..aa879253d3b8 100644
--- a/drivers/usb/typec/bus.c
+++ b/drivers/usb/typec/bus.c
@@ -447,7 +447,7 @@ static struct attribute *typec_attrs[] = {
};
ATTRIBUTE_GROUPS(typec);
-static int typec_match(struct device *dev, struct device_driver *driver)
+static int typec_match(struct device *dev, const struct device_driver *driver)
{
struct typec_altmode_driver *drv = to_altmode_driver(driver);
struct typec_altmode *altmode = to_typec_altmode(dev);
diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c
index 9610e647a8d4..9262fcd4144f 100644
--- a/drivers/usb/typec/class.c
+++ b/drivers/usb/typec/class.c
@@ -467,6 +467,22 @@ static const struct attribute_group *typec_altmode_groups[] = {
NULL
};
+/**
+ * typec_altmode_set_ops - Set ops for altmode
+ * @adev: Handle to the alternate mode
+ * @ops: Ops for the alternate mode
+ *
+ * After setting ops, attribute visiblity needs to be refreshed if the alternate
+ * mode can be activated.
+ */
+void typec_altmode_set_ops(struct typec_altmode *adev,
+ const struct typec_altmode_ops *ops)
+{
+ adev->ops = ops;
+ sysfs_update_group(&adev->dev.kobj, &typec_altmode_group);
+}
+EXPORT_SYMBOL_GPL(typec_altmode_set_ops);
+
static int altmode_id_get(struct device *dev)
{
struct ida *ids;
@@ -2317,7 +2333,7 @@ void typec_port_register_altmodes(struct typec_port *port,
continue;
}
- alt->ops = ops;
+ typec_altmode_set_ops(alt, ops);
typec_altmode_set_drvdata(alt, drvdata);
altmodes[index] = alt;
index++;
diff --git a/drivers/usb/typec/mux/Kconfig b/drivers/usb/typec/mux/Kconfig
index 399c7b0983df..ce7db6ad3057 100644
--- a/drivers/usb/typec/mux/Kconfig
+++ b/drivers/usb/typec/mux/Kconfig
@@ -60,7 +60,7 @@ config TYPEC_MUX_PTN36502
tristate "NXP PTN36502 Type-C redriver driver"
depends on I2C
depends on DRM || DRM=n
- select DRM_PANEL_BRIDGE if DRM
+ select DRM_AUX_BRIDGE if DRM_BRIDGE && OF
select REGMAP_I2C
help
Say Y or M if your system has a NXP PTN36502 Type-C redriver chip
diff --git a/drivers/usb/typec/mux/fsa4480.c b/drivers/usb/typec/mux/fsa4480.c
index cb7cdf90cb0a..f71dba8bf07c 100644
--- a/drivers/usb/typec/mux/fsa4480.c
+++ b/drivers/usb/typec/mux/fsa4480.c
@@ -13,6 +13,10 @@
#include <linux/usb/typec_dp.h>
#include <linux/usb/typec_mux.h>
+#define FSA4480_DEVICE_ID 0x00
+ #define FSA4480_DEVICE_ID_VENDOR_ID GENMASK(7, 6)
+ #define FSA4480_DEVICE_ID_VERSION_ID GENMASK(5, 3)
+ #define FSA4480_DEVICE_ID_REV_ID GENMASK(2, 0)
#define FSA4480_SWITCH_ENABLE 0x04
#define FSA4480_SWITCH_SELECT 0x05
#define FSA4480_SWITCH_STATUS1 0x07
@@ -251,6 +255,7 @@ static int fsa4480_probe(struct i2c_client *client)
struct typec_switch_desc sw_desc = { };
struct typec_mux_desc mux_desc = { };
struct fsa4480 *fsa;
+ int val = 0;
int ret;
fsa = devm_kzalloc(dev, sizeof(*fsa), GFP_KERNEL);
@@ -268,6 +273,15 @@ static int fsa4480_probe(struct i2c_client *client)
if (IS_ERR(fsa->regmap))
return dev_err_probe(dev, PTR_ERR(fsa->regmap), "failed to initialize regmap\n");
+ ret = regmap_read(fsa->regmap, FSA4480_DEVICE_ID, &val);
+ if (ret)
+ return dev_err_probe(dev, -ENODEV, "FSA4480 not found\n");
+
+ dev_dbg(dev, "Found FSA4480 v%lu.%lu (Vendor ID = %lu)\n",
+ FIELD_GET(FSA4480_DEVICE_ID_VERSION_ID, val),
+ FIELD_GET(FSA4480_DEVICE_ID_REV_ID, val),
+ FIELD_GET(FSA4480_DEVICE_ID_VENDOR_ID, val));
+
/* Safe mode */
fsa->cur_enable = FSA4480_ENABLE_DEVICE | FSA4480_ENABLE_USB;
fsa->mode = TYPEC_STATE_SAFE;
diff --git a/drivers/usb/typec/mux/gpio-sbu-mux.c b/drivers/usb/typec/mux/gpio-sbu-mux.c
index ad60fd4e8431..8902102c05a8 100644
--- a/drivers/usb/typec/mux/gpio-sbu-mux.c
+++ b/drivers/usb/typec/mux/gpio-sbu-mux.c
@@ -48,10 +48,10 @@ static int gpio_sbu_switch_set(struct typec_switch_dev *sw,
}
if (enabled != sbu_mux->enabled)
- gpiod_set_value(sbu_mux->enable_gpio, enabled);
+ gpiod_set_value_cansleep(sbu_mux->enable_gpio, enabled);
if (swapped != sbu_mux->swapped)
- gpiod_set_value(sbu_mux->select_gpio, swapped);
+ gpiod_set_value_cansleep(sbu_mux->select_gpio, swapped);
sbu_mux->enabled = enabled;
sbu_mux->swapped = swapped;
@@ -66,6 +66,9 @@ static int gpio_sbu_mux_set(struct typec_mux_dev *mux,
{
struct gpio_sbu_mux *sbu_mux = typec_mux_get_drvdata(mux);
+ if (!sbu_mux->enable_gpio)
+ return -EOPNOTSUPP;
+
mutex_lock(&sbu_mux->lock);
switch (state->mode) {
@@ -82,7 +85,7 @@ static int gpio_sbu_mux_set(struct typec_mux_dev *mux,
break;
}
- gpiod_set_value(sbu_mux->enable_gpio, sbu_mux->enabled);
+ gpiod_set_value_cansleep(sbu_mux->enable_gpio, sbu_mux->enabled);
mutex_unlock(&sbu_mux->lock);
@@ -102,7 +105,8 @@ static int gpio_sbu_mux_probe(struct platform_device *pdev)
mutex_init(&sbu_mux->lock);
- sbu_mux->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
+ sbu_mux->enable_gpio = devm_gpiod_get_optional(dev, "enable",
+ GPIOD_OUT_LOW);
if (IS_ERR(sbu_mux->enable_gpio))
return dev_err_probe(dev, PTR_ERR(sbu_mux->enable_gpio),
"unable to acquire enable gpio\n");
@@ -141,7 +145,7 @@ static void gpio_sbu_mux_remove(struct platform_device *pdev)
{
struct gpio_sbu_mux *sbu_mux = platform_get_drvdata(pdev);
- gpiod_set_value(sbu_mux->enable_gpio, 0);
+ gpiod_set_value_cansleep(sbu_mux->enable_gpio, 0);
typec_mux_unregister(sbu_mux->mux);
typec_switch_unregister(sbu_mux->sw);
diff --git a/drivers/usb/typec/mux/intel_pmc_mux.c b/drivers/usb/typec/mux/intel_pmc_mux.c
index 56989a0d0f43..46b4c5c3a6be 100644
--- a/drivers/usb/typec/mux/intel_pmc_mux.c
+++ b/drivers/usb/typec/mux/intel_pmc_mux.c
@@ -8,6 +8,7 @@
#include <linux/acpi.h>
#include <linux/module.h>
+#include <linux/platform_data/x86/intel_scu_ipc.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/usb/pd.h>
@@ -18,8 +19,6 @@
#include <linux/debugfs.h>
#include <linux/usb.h>
-#include <asm/intel_scu_ipc.h>
-
#define PMC_USBC_CMD 0xa7
/* Response status bits */
diff --git a/drivers/usb/typec/mux/nb7vpq904m.c b/drivers/usb/typec/mux/nb7vpq904m.c
index b17826713753..b57b6c9c40fe 100644
--- a/drivers/usb/typec/mux/nb7vpq904m.c
+++ b/drivers/usb/typec/mux/nb7vpq904m.c
@@ -69,6 +69,7 @@ struct nb7vpq904m {
bool swap_data_lanes;
struct typec_switch *typec_switch;
+ struct typec_mux *typec_mux;
struct mutex lock; /* protect non-concurrent retimer & switch */
@@ -275,6 +276,7 @@ static int nb7vpq904m_sw_set(struct typec_switch_dev *sw, enum typec_orientation
static int nb7vpq904m_retimer_set(struct typec_retimer *retimer, struct typec_retimer_state *state)
{
struct nb7vpq904m *nb7 = typec_retimer_get_drvdata(retimer);
+ struct typec_mux_state mux_state;
int ret = 0;
mutex_lock(&nb7->lock);
@@ -292,7 +294,14 @@ static int nb7vpq904m_retimer_set(struct typec_retimer *retimer, struct typec_re
mutex_unlock(&nb7->lock);
- return ret;
+ if (ret)
+ return ret;
+
+ mux_state.alt = state->alt;
+ mux_state.data = state->data;
+ mux_state.mode = state->mode;
+
+ return typec_mux_set(nb7->typec_mux, &mux_state);
}
static const struct regmap_config nb7_regmap = {
@@ -321,46 +330,48 @@ static int nb7vpq904m_parse_data_lanes_mapping(struct nb7vpq904m *nb7)
ep = of_graph_get_endpoint_by_regs(nb7->client->dev.of_node, 1, 0);
- if (ep) {
- ret = of_property_count_u32_elems(ep, "data-lanes");
- if (ret == -EINVAL)
- /* Property isn't here, consider default mapping */
- goto out_done;
- if (ret < 0)
- goto out_error;
-
- if (ret != DATA_LANES_COUNT) {
- dev_err(&nb7->client->dev, "expected 4 data lanes\n");
- ret = -EINVAL;
- goto out_error;
- }
+ if (!ep)
+ return 0;
+
+
+ ret = of_property_count_u32_elems(ep, "data-lanes");
+ if (ret == -EINVAL)
+ /* Property isn't here, consider default mapping */
+ goto out_done;
+ if (ret < 0)
+ goto out_error;
- ret = of_property_read_u32_array(ep, "data-lanes", data_lanes, DATA_LANES_COUNT);
- if (ret)
- goto out_error;
+ if (ret != DATA_LANES_COUNT) {
+ dev_err(&nb7->client->dev, "expected 4 data lanes\n");
+ ret = -EINVAL;
+ goto out_error;
+ }
- for (i = 0; i < ARRAY_SIZE(supported_data_lane_mapping); i++) {
- for (j = 0; j < DATA_LANES_COUNT; j++) {
- if (data_lanes[j] != supported_data_lane_mapping[i][j])
- break;
- }
+ ret = of_property_read_u32_array(ep, "data-lanes", data_lanes, DATA_LANES_COUNT);
+ if (ret)
+ goto out_error;
- if (j == DATA_LANES_COUNT)
+ for (i = 0; i < ARRAY_SIZE(supported_data_lane_mapping); i++) {
+ for (j = 0; j < DATA_LANES_COUNT; j++) {
+ if (data_lanes[j] != supported_data_lane_mapping[i][j])
break;
}
- switch (i) {
- case NORMAL_LANE_MAPPING:
- break;
- case INVERT_LANE_MAPPING:
- nb7->swap_data_lanes = true;
- dev_info(&nb7->client->dev, "using inverted data lanes mapping\n");
+ if (j == DATA_LANES_COUNT)
break;
- default:
- dev_err(&nb7->client->dev, "invalid data lanes mapping\n");
- ret = -EINVAL;
- goto out_error;
- }
+ }
+
+ switch (i) {
+ case NORMAL_LANE_MAPPING:
+ break;
+ case INVERT_LANE_MAPPING:
+ nb7->swap_data_lanes = true;
+ dev_info(&nb7->client->dev, "using inverted data lanes mapping\n");
+ break;
+ default:
+ dev_err(&nb7->client->dev, "invalid data lanes mapping\n");
+ ret = -EINVAL;
+ goto out_error;
}
out_done:
@@ -411,9 +422,16 @@ static int nb7vpq904m_probe(struct i2c_client *client)
return dev_err_probe(dev, PTR_ERR(nb7->typec_switch),
"failed to acquire orientation-switch\n");
+ nb7->typec_mux = fwnode_typec_mux_get(dev->fwnode);
+ if (IS_ERR(nb7->typec_mux)) {
+ ret = dev_err_probe(dev, PTR_ERR(nb7->typec_mux),
+ "Failed to acquire mode-switch\n");
+ goto err_switch_put;
+ }
+
ret = nb7vpq904m_parse_data_lanes_mapping(nb7);
if (ret)
- return ret;
+ goto err_mux_put;
ret = regulator_enable(nb7->vcc_supply);
if (ret)
@@ -456,6 +474,12 @@ err_disable_gpio:
gpiod_set_value(nb7->enable_gpio, 0);
regulator_disable(nb7->vcc_supply);
+err_mux_put:
+ typec_mux_put(nb7->typec_mux);
+
+err_switch_put:
+ typec_switch_put(nb7->typec_switch);
+
return ret;
}
@@ -469,6 +493,9 @@ static void nb7vpq904m_remove(struct i2c_client *client)
gpiod_set_value(nb7->enable_gpio, 0);
regulator_disable(nb7->vcc_supply);
+
+ typec_mux_put(nb7->typec_mux);
+ typec_switch_put(nb7->typec_switch);
}
static const struct i2c_device_id nb7vpq904m_table[] = {
diff --git a/drivers/usb/typec/mux/ptn36502.c b/drivers/usb/typec/mux/ptn36502.c
index 72ae38a1b2be..129d9d24b932 100644
--- a/drivers/usb/typec/mux/ptn36502.c
+++ b/drivers/usb/typec/mux/ptn36502.c
@@ -8,7 +8,7 @@
* Copyright (C) 2023 Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
*/
-#include <drm/drm_bridge.h>
+#include <drm/bridge/aux-bridge.h>
#include <linux/bitfield.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
@@ -67,8 +67,7 @@ struct ptn36502 {
struct typec_retimer *retimer;
struct typec_switch *typec_switch;
-
- struct drm_bridge bridge;
+ struct typec_mux *typec_mux;
struct mutex lock; /* protect non-concurrent retimer & switch */
@@ -237,6 +236,7 @@ static int ptn36502_sw_set(struct typec_switch_dev *sw, enum typec_orientation o
static int ptn36502_retimer_set(struct typec_retimer *retimer, struct typec_retimer_state *state)
{
struct ptn36502 *ptn = typec_retimer_get_drvdata(retimer);
+ struct typec_mux_state mux_state;
int ret = 0;
mutex_lock(&ptn->lock);
@@ -254,7 +254,14 @@ static int ptn36502_retimer_set(struct typec_retimer *retimer, struct typec_reti
mutex_unlock(&ptn->lock);
- return ret;
+ if (ret)
+ return ret;
+
+ mux_state.alt = state->alt;
+ mux_state.data = state->data;
+ mux_state.mode = state->mode;
+
+ return typec_mux_set(ptn->typec_mux, &mux_state);
}
static int ptn36502_detect(struct ptn36502 *ptn)
@@ -283,44 +290,6 @@ static int ptn36502_detect(struct ptn36502 *ptn)
return 0;
}
-#if IS_ENABLED(CONFIG_OF) && IS_ENABLED(CONFIG_DRM_PANEL_BRIDGE)
-static int ptn36502_bridge_attach(struct drm_bridge *bridge,
- enum drm_bridge_attach_flags flags)
-{
- struct ptn36502 *ptn = container_of(bridge, struct ptn36502, bridge);
- struct drm_bridge *next_bridge;
-
- if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR))
- return -EINVAL;
-
- next_bridge = devm_drm_of_get_bridge(&ptn->client->dev, ptn->client->dev.of_node, 0, 0);
- if (IS_ERR(next_bridge)) {
- dev_err(&ptn->client->dev, "failed to acquire drm_bridge: %pe\n", next_bridge);
- return PTR_ERR(next_bridge);
- }
-
- return drm_bridge_attach(bridge->encoder, next_bridge, bridge,
- DRM_BRIDGE_ATTACH_NO_CONNECTOR);
-}
-
-static const struct drm_bridge_funcs ptn36502_bridge_funcs = {
- .attach = ptn36502_bridge_attach,
-};
-
-static int ptn36502_register_bridge(struct ptn36502 *ptn)
-{
- ptn->bridge.funcs = &ptn36502_bridge_funcs;
- ptn->bridge.of_node = ptn->client->dev.of_node;
-
- return devm_drm_bridge_add(&ptn->client->dev, &ptn->bridge);
-}
-#else
-static int ptn36502_register_bridge(struct ptn36502 *ptn)
-{
- return 0;
-}
-#endif
-
static const struct regmap_config ptn36502_regmap = {
.max_register = 0x0d,
.reg_bits = 8,
@@ -361,15 +330,24 @@ static int ptn36502_probe(struct i2c_client *client)
return dev_err_probe(dev, PTR_ERR(ptn->typec_switch),
"Failed to acquire orientation-switch\n");
+ ptn->typec_mux = fwnode_typec_mux_get(dev->fwnode);
+ if (IS_ERR(ptn->typec_mux)) {
+ ret = dev_err_probe(dev, PTR_ERR(ptn->typec_mux),
+ "Failed to acquire mode-switch\n");
+ goto err_switch_put;
+ }
+
ret = regulator_enable(ptn->vdd18_supply);
- if (ret)
- return dev_err_probe(dev, ret, "Failed to enable vdd18\n");
+ if (ret) {
+ ret = dev_err_probe(dev, ret, "Failed to enable vdd18\n");
+ goto err_mux_put;
+ }
ret = ptn36502_detect(ptn);
if (ret)
goto err_disable_regulator;
- ret = ptn36502_register_bridge(ptn);
+ ret = drm_aux_bridge_register(dev);
if (ret)
goto err_disable_regulator;
@@ -403,6 +381,12 @@ err_switch_unregister:
err_disable_regulator:
regulator_disable(ptn->vdd18_supply);
+err_mux_put:
+ typec_mux_put(ptn->typec_mux);
+
+err_switch_put:
+ typec_switch_put(ptn->typec_switch);
+
return ret;
}
@@ -414,6 +398,9 @@ static void ptn36502_remove(struct i2c_client *client)
typec_switch_unregister(ptn->sw);
regulator_disable(ptn->vdd18_supply);
+
+ typec_mux_put(ptn->typec_mux);
+ typec_switch_put(ptn->typec_switch);
}
static const struct i2c_device_id ptn36502_table[] = {
diff --git a/drivers/usb/typec/rt1719.c b/drivers/usb/typec/rt1719.c
index be02d420920e..0b0c23a0b014 100644
--- a/drivers/usb/typec/rt1719.c
+++ b/drivers/usb/typec/rt1719.c
@@ -109,12 +109,6 @@ struct rt1719_data {
u16 conn_stat;
};
-static const enum power_supply_usb_type rt1719_psy_usb_types[] = {
- POWER_SUPPLY_USB_TYPE_C,
- POWER_SUPPLY_USB_TYPE_PD,
- POWER_SUPPLY_USB_TYPE_PD_PPS
-};
-
static const enum power_supply_property rt1719_psy_properties[] = {
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_USB_TYPE,
@@ -572,8 +566,9 @@ static int devm_rt1719_psy_register(struct rt1719_data *data)
data->psy_desc.name = psy_name;
data->psy_desc.type = POWER_SUPPLY_TYPE_USB;
- data->psy_desc.usb_types = rt1719_psy_usb_types;
- data->psy_desc.num_usb_types = ARRAY_SIZE(rt1719_psy_usb_types);
+ data->psy_desc.usb_types = BIT(POWER_SUPPLY_USB_TYPE_C) |
+ BIT(POWER_SUPPLY_USB_TYPE_PD) |
+ BIT(POWER_SUPPLY_USB_TYPE_PD_PPS);
data->psy_desc.properties = rt1719_psy_properties;
data->psy_desc.num_properties = ARRAY_SIZE(rt1719_psy_properties);
data->psy_desc.get_property = rt1719_psy_get_property;
diff --git a/drivers/usb/typec/stusb160x.c b/drivers/usb/typec/stusb160x.c
index 3ab118df1bd4..f3140fc04c12 100644
--- a/drivers/usb/typec/stusb160x.c
+++ b/drivers/usb/typec/stusb160x.c
@@ -234,7 +234,7 @@ static const struct regmap_config stusb1600_regmap_config = {
.readable_reg = stusb160x_reg_readable,
.volatile_reg = stusb160x_reg_volatile,
.precious_reg = stusb160x_reg_precious,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static bool stusb160x_get_vconn(struct stusb160x *chip)
diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c
index ef18a448b740..e2fe479e16ad 100644
--- a/drivers/usb/typec/tcpm/fusb302.c
+++ b/drivers/usb/typec/tcpm/fusb302.c
@@ -1820,8 +1820,8 @@ static const struct of_device_id fusb302_dt_match[] __maybe_unused = {
MODULE_DEVICE_TABLE(of, fusb302_dt_match);
static const struct i2c_device_id fusb302_i2c_device_id[] = {
- {"typec_fusb302", 0},
- {},
+ { "typec_fusb302" },
+ {}
};
MODULE_DEVICE_TABLE(i2c, fusb302_i2c_device_id);
diff --git a/drivers/usb/typec/tcpm/maxim_contaminant.c b/drivers/usb/typec/tcpm/maxim_contaminant.c
index f8504a90da26..22163d8f9eb0 100644
--- a/drivers/usb/typec/tcpm/maxim_contaminant.c
+++ b/drivers/usb/typec/tcpm/maxim_contaminant.c
@@ -5,6 +5,7 @@
* USB-C module to reduce wakeups due to contaminants.
*/
+#include <linux/bitfield.h>
#include <linux/device.h>
#include <linux/irqreturn.h>
#include <linux/module.h>
@@ -45,14 +46,9 @@ enum fladc_select {
#define READ1_SLEEP_MS 10
#define READ2_SLEEP_MS 5
-#define STATUS_CHECK(reg, mask, val) (((reg) & (mask)) == (val))
-
#define IS_CC_OPEN(cc_status) \
- (STATUS_CHECK((cc_status), TCPC_CC_STATUS_CC1_MASK << TCPC_CC_STATUS_CC1_SHIFT, \
- TCPC_CC_STATE_SRC_OPEN) && STATUS_CHECK((cc_status), \
- TCPC_CC_STATUS_CC2_MASK << \
- TCPC_CC_STATUS_CC2_SHIFT, \
- TCPC_CC_STATE_SRC_OPEN))
+ (FIELD_GET(TCPC_CC_STATUS_CC1, cc_status) == TCPC_CC_STATE_SRC_OPEN \
+ && FIELD_GET(TCPC_CC_STATUS_CC2, cc_status) == TCPC_CC_STATE_SRC_OPEN)
static int max_contaminant_adc_to_mv(struct max_tcpci_chip *chip, enum fladc_select channel,
bool ua_src, u8 fladc)
@@ -80,8 +76,8 @@ static int max_contaminant_read_adc_mv(struct max_tcpci_chip *chip, enum fladc_s
int ret;
/* Channel & scale select */
- ret = regmap_update_bits(regmap, TCPC_VENDOR_ADC_CTRL1, ADCINSEL_MASK,
- channel << ADC_CHANNEL_OFFSET);
+ ret = regmap_update_bits(regmap, TCPC_VENDOR_ADC_CTRL1, ADCINSEL,
+ FIELD_PREP(ADCINSEL, channel));
if (ret < 0)
return ret;
@@ -100,7 +96,8 @@ static int max_contaminant_read_adc_mv(struct max_tcpci_chip *chip, enum fladc_s
if (ret < 0)
return ret;
- ret = regmap_update_bits(regmap, TCPC_VENDOR_ADC_CTRL1, ADCINSEL_MASK, 0);
+ ret = regmap_update_bits(regmap, TCPC_VENDOR_ADC_CTRL1, ADCINSEL,
+ FIELD_PREP(ADCINSEL, 0));
if (ret < 0)
return ret;
@@ -120,13 +117,14 @@ static int max_contaminant_read_resistance_kohm(struct max_tcpci_chip *chip,
if (channel == CC1_SCALE1 || channel == CC2_SCALE1 || channel == CC1_SCALE2 ||
channel == CC2_SCALE2) {
/* Enable 1uA current source */
- ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, CCLPMODESEL_MASK,
- ULTRA_LOW_POWER_MODE);
+ ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, CCLPMODESEL,
+ FIELD_PREP(CCLPMODESEL, ULTRA_LOW_POWER_MODE));
if (ret < 0)
return ret;
/* Enable 1uA current source */
- ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, CCRPCTRL_MASK, UA_1_SRC);
+ ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, CCRPCTRL,
+ FIELD_PREP(CCRPCTRL, UA_1_SRC));
if (ret < 0)
return ret;
@@ -180,7 +178,8 @@ static int max_contaminant_read_comparators(struct max_tcpci_chip *chip, u8 *ven
int ret;
/* Enable 80uA source */
- ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, CCRPCTRL_MASK, UA_80_SRC);
+ ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, CCRPCTRL,
+ FIELD_PREP(CCRPCTRL, UA_80_SRC));
if (ret < 0)
return ret;
@@ -213,7 +212,8 @@ static int max_contaminant_read_comparators(struct max_tcpci_chip *chip, u8 *ven
if (ret < 0)
return ret;
- ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, CCRPCTRL_MASK, 0);
+ ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, CCRPCTRL,
+ FIELD_PREP(CCRPCTRL, 0));
if (ret < 0)
return ret;
@@ -284,10 +284,11 @@ static int max_contaminant_enable_dry_detection(struct max_tcpci_chip *chip)
u8 temp;
int ret;
- ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL3, CCWTRDEB_MASK | CCWTRSEL_MASK
- | WTRCYCLE_MASK, CCWTRDEB_1MS << CCWTRDEB_SHIFT |
- CCWTRSEL_1V << CCWTRSEL_SHIFT | WTRCYCLE_4_8_S <<
- WTRCYCLE_SHIFT);
+ ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL3,
+ CCWTRDEB | CCWTRSEL | WTRCYCLE,
+ FIELD_PREP(CCWTRDEB, CCWTRDEB_1MS)
+ | FIELD_PREP(CCWTRSEL, CCWTRSEL_1V)
+ | FIELD_PREP(WTRCYCLE, WTRCYCLE_4_8_S));
if (ret < 0)
return ret;
@@ -302,8 +303,9 @@ static int max_contaminant_enable_dry_detection(struct max_tcpci_chip *chip)
if (ret < 0)
return ret;
- ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, CCLPMODESEL_MASK,
- ULTRA_LOW_POWER_MODE);
+ ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, CCLPMODESEL,
+ FIELD_PREP(CCLPMODESEL,
+ ULTRA_LOW_POWER_MODE));
if (ret < 0)
return ret;
ret = max_tcpci_read8(chip, TCPC_VENDOR_CC_CTRL2, &temp);
@@ -322,11 +324,14 @@ static int max_contaminant_enable_dry_detection(struct max_tcpci_chip *chip)
return 0;
}
-bool max_contaminant_is_contaminant(struct max_tcpci_chip *chip, bool disconnect_while_debounce)
+bool max_contaminant_is_contaminant(struct max_tcpci_chip *chip, bool disconnect_while_debounce,
+ bool *cc_handled)
{
u8 cc_status, pwr_cntl;
int ret;
+ *cc_handled = true;
+
ret = max_tcpci_read8(chip, TCPC_CC_STATUS, &cc_status);
if (ret < 0)
return false;
@@ -368,9 +373,8 @@ bool max_contaminant_is_contaminant(struct max_tcpci_chip *chip, bool disconnect
return true;
}
}
- return false;
} else if (chip->contaminant_state == DETECTED) {
- if (STATUS_CHECK(cc_status, TCPC_CC_STATUS_TOGGLING, 0)) {
+ if (!(cc_status & TCPC_CC_STATUS_TOGGLING)) {
chip->contaminant_state = max_contaminant_detect_contaminant(chip);
if (chip->contaminant_state == DETECTED) {
max_contaminant_enable_dry_detection(chip);
@@ -379,6 +383,7 @@ bool max_contaminant_is_contaminant(struct max_tcpci_chip *chip, bool disconnect
}
}
+ *cc_handled = false;
return false;
}
diff --git a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c
index d3958c061a97..501eddb294e4 100644
--- a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c
+++ b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c
@@ -41,7 +41,7 @@ static int qcom_pmic_typec_probe(struct platform_device *pdev)
struct device_node *np = dev->of_node;
const struct pmic_typec_resources *res;
struct regmap *regmap;
- struct device *bridge_dev;
+ struct auxiliary_device *bridge_dev;
u32 base;
int ret;
@@ -92,7 +92,7 @@ static int qcom_pmic_typec_probe(struct platform_device *pdev)
if (!tcpm->tcpc.fwnode)
return -EINVAL;
- bridge_dev = drm_dp_hpd_bridge_register(tcpm->dev, to_of_node(tcpm->tcpc.fwnode));
+ bridge_dev = devm_drm_dp_hpd_bridge_alloc(tcpm->dev, to_of_node(tcpm->tcpc.fwnode));
if (IS_ERR(bridge_dev))
return PTR_ERR(bridge_dev);
@@ -110,8 +110,14 @@ static int qcom_pmic_typec_probe(struct platform_device *pdev)
if (ret)
goto port_stop;
+ ret = devm_drm_dp_hpd_bridge_add(tcpm->dev, bridge_dev);
+ if (ret)
+ goto pdphy_stop;
+
return 0;
+pdphy_stop:
+ tcpm->pdphy_stop(tcpm);
port_stop:
tcpm->port_stop(tcpm);
port_unregister:
diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c
index c962014bba4e..ed32583829be 100644
--- a/drivers/usb/typec/tcpm/tcpci.c
+++ b/drivers/usb/typec/tcpm/tcpci.c
@@ -5,6 +5,7 @@
* USB Type-C Port Controller Interface.
*/
+#include <linux/bitfield.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -67,6 +68,18 @@ static int tcpci_write16(struct tcpci *tcpci, unsigned int reg, u16 val)
return regmap_raw_write(tcpci->regmap, reg, &val, sizeof(u16));
}
+static int tcpci_check_std_output_cap(struct regmap *regmap, u8 mask)
+{
+ unsigned int reg;
+ int ret;
+
+ ret = regmap_read(regmap, TCPC_STD_OUTPUT_CAP, &reg);
+ if (ret < 0)
+ return ret;
+
+ return (reg & mask) == mask;
+}
+
static int tcpci_set_cc(struct tcpc_dev *tcpc, enum typec_cc_status cc)
{
struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
@@ -91,45 +104,45 @@ static int tcpci_set_cc(struct tcpc_dev *tcpc, enum typec_cc_status cc)
switch (cc) {
case TYPEC_CC_RA:
- reg = (TCPC_ROLE_CTRL_CC_RA << TCPC_ROLE_CTRL_CC1_SHIFT) |
- (TCPC_ROLE_CTRL_CC_RA << TCPC_ROLE_CTRL_CC2_SHIFT);
+ reg = (FIELD_PREP(TCPC_ROLE_CTRL_CC1, TCPC_ROLE_CTRL_CC_RA)
+ | FIELD_PREP(TCPC_ROLE_CTRL_CC2, TCPC_ROLE_CTRL_CC_RA));
break;
case TYPEC_CC_RD:
- reg = (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC1_SHIFT) |
- (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC2_SHIFT);
+ reg = (FIELD_PREP(TCPC_ROLE_CTRL_CC1, TCPC_ROLE_CTRL_CC_RD)
+ | FIELD_PREP(TCPC_ROLE_CTRL_CC2, TCPC_ROLE_CTRL_CC_RD));
break;
case TYPEC_CC_RP_DEF:
- reg = (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT) |
- (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT) |
- (TCPC_ROLE_CTRL_RP_VAL_DEF <<
- TCPC_ROLE_CTRL_RP_VAL_SHIFT);
+ reg = (FIELD_PREP(TCPC_ROLE_CTRL_CC1, TCPC_ROLE_CTRL_CC_RP)
+ | FIELD_PREP(TCPC_ROLE_CTRL_CC2, TCPC_ROLE_CTRL_CC_RP)
+ | FIELD_PREP(TCPC_ROLE_CTRL_RP_VAL,
+ TCPC_ROLE_CTRL_RP_VAL_DEF));
break;
case TYPEC_CC_RP_1_5:
- reg = (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT) |
- (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT) |
- (TCPC_ROLE_CTRL_RP_VAL_1_5 <<
- TCPC_ROLE_CTRL_RP_VAL_SHIFT);
+ reg = (FIELD_PREP(TCPC_ROLE_CTRL_CC1, TCPC_ROLE_CTRL_CC_RP)
+ | FIELD_PREP(TCPC_ROLE_CTRL_CC2, TCPC_ROLE_CTRL_CC_RP)
+ | FIELD_PREP(TCPC_ROLE_CTRL_RP_VAL,
+ TCPC_ROLE_CTRL_RP_VAL_1_5));
break;
case TYPEC_CC_RP_3_0:
- reg = (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT) |
- (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT) |
- (TCPC_ROLE_CTRL_RP_VAL_3_0 <<
- TCPC_ROLE_CTRL_RP_VAL_SHIFT);
+ reg = (FIELD_PREP(TCPC_ROLE_CTRL_CC1, TCPC_ROLE_CTRL_CC_RP)
+ | FIELD_PREP(TCPC_ROLE_CTRL_CC2, TCPC_ROLE_CTRL_CC_RP)
+ | FIELD_PREP(TCPC_ROLE_CTRL_RP_VAL,
+ TCPC_ROLE_CTRL_RP_VAL_3_0));
break;
case TYPEC_CC_OPEN:
default:
- reg = (TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC1_SHIFT) |
- (TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC2_SHIFT);
+ reg = (FIELD_PREP(TCPC_ROLE_CTRL_CC1, TCPC_ROLE_CTRL_CC_OPEN)
+ | FIELD_PREP(TCPC_ROLE_CTRL_CC2, TCPC_ROLE_CTRL_CC_OPEN));
break;
}
if (vconn_pres) {
if (polarity == TYPEC_POLARITY_CC2) {
- reg &= ~(TCPC_ROLE_CTRL_CC1_MASK << TCPC_ROLE_CTRL_CC1_SHIFT);
- reg |= (TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC1_SHIFT);
+ reg &= ~TCPC_ROLE_CTRL_CC1;
+ reg |= FIELD_PREP(TCPC_ROLE_CTRL_CC1, TCPC_ROLE_CTRL_CC_OPEN);
} else {
- reg &= ~(TCPC_ROLE_CTRL_CC2_MASK << TCPC_ROLE_CTRL_CC2_SHIFT);
- reg |= (TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC2_SHIFT);
+ reg &= ~TCPC_ROLE_CTRL_CC2;
+ reg |= FIELD_PREP(TCPC_ROLE_CTRL_CC2, TCPC_ROLE_CTRL_CC_OPEN);
}
}
@@ -155,15 +168,11 @@ static int tcpci_apply_rc(struct tcpc_dev *tcpc, enum typec_cc_status cc,
* APPLY_RC state is when ROLE_CONTROL.CC1 != ROLE_CONTROL.CC2 and vbus autodischarge on
* disconnect is disabled. Bail out when ROLE_CONTROL.CC1 != ROLE_CONTROL.CC2.
*/
- if (((reg & (TCPC_ROLE_CTRL_CC2_MASK << TCPC_ROLE_CTRL_CC2_SHIFT)) >>
- TCPC_ROLE_CTRL_CC2_SHIFT) !=
- ((reg & (TCPC_ROLE_CTRL_CC1_MASK << TCPC_ROLE_CTRL_CC1_SHIFT)) >>
- TCPC_ROLE_CTRL_CC1_SHIFT))
+ if (FIELD_GET(TCPC_ROLE_CTRL_CC2, reg) != FIELD_GET(TCPC_ROLE_CTRL_CC1, reg))
return 0;
return regmap_update_bits(tcpci->regmap, TCPC_ROLE_CTRL, polarity == TYPEC_POLARITY_CC1 ?
- TCPC_ROLE_CTRL_CC2_MASK << TCPC_ROLE_CTRL_CC2_SHIFT :
- TCPC_ROLE_CTRL_CC1_MASK << TCPC_ROLE_CTRL_CC1_SHIFT,
+ TCPC_ROLE_CTRL_CC2 : TCPC_ROLE_CTRL_CC1,
TCPC_ROLE_CTRL_CC_OPEN);
}
@@ -188,25 +197,25 @@ static int tcpci_start_toggling(struct tcpc_dev *tcpc,
switch (cc) {
default:
case TYPEC_CC_RP_DEF:
- reg |= (TCPC_ROLE_CTRL_RP_VAL_DEF <<
- TCPC_ROLE_CTRL_RP_VAL_SHIFT);
+ reg |= FIELD_PREP(TCPC_ROLE_CTRL_RP_VAL,
+ TCPC_ROLE_CTRL_RP_VAL_DEF);
break;
case TYPEC_CC_RP_1_5:
- reg |= (TCPC_ROLE_CTRL_RP_VAL_1_5 <<
- TCPC_ROLE_CTRL_RP_VAL_SHIFT);
+ reg |= FIELD_PREP(TCPC_ROLE_CTRL_RP_VAL,
+ TCPC_ROLE_CTRL_RP_VAL_1_5);
break;
case TYPEC_CC_RP_3_0:
- reg |= (TCPC_ROLE_CTRL_RP_VAL_3_0 <<
- TCPC_ROLE_CTRL_RP_VAL_SHIFT);
+ reg |= FIELD_PREP(TCPC_ROLE_CTRL_RP_VAL,
+ TCPC_ROLE_CTRL_RP_VAL_3_0);
break;
}
if (cc == TYPEC_CC_RD)
- reg |= (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC1_SHIFT) |
- (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC2_SHIFT);
+ reg |= (FIELD_PREP(TCPC_ROLE_CTRL_CC1, TCPC_ROLE_CTRL_CC_RD)
+ | FIELD_PREP(TCPC_ROLE_CTRL_CC2, TCPC_ROLE_CTRL_CC_RD));
else
- reg |= (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT) |
- (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT);
+ reg |= (FIELD_PREP(TCPC_ROLE_CTRL_CC1, TCPC_ROLE_CTRL_CC_RP)
+ | FIELD_PREP(TCPC_ROLE_CTRL_CC2, TCPC_ROLE_CTRL_CC_RP));
ret = regmap_write(tcpci->regmap, TCPC_ROLE_CTRL, reg);
if (ret < 0)
return ret;
@@ -229,12 +238,10 @@ static int tcpci_get_cc(struct tcpc_dev *tcpc,
if (ret < 0)
return ret;
- *cc1 = tcpci_to_typec_cc((reg >> TCPC_CC_STATUS_CC1_SHIFT) &
- TCPC_CC_STATUS_CC1_MASK,
+ *cc1 = tcpci_to_typec_cc(FIELD_GET(TCPC_CC_STATUS_CC1, reg),
reg & TCPC_CC_STATUS_TERM ||
tcpc_presenting_rd(role_control, CC1));
- *cc2 = tcpci_to_typec_cc((reg >> TCPC_CC_STATUS_CC2_SHIFT) &
- TCPC_CC_STATUS_CC2_MASK,
+ *cc2 = tcpci_to_typec_cc(FIELD_GET(TCPC_CC_STATUS_CC2, reg),
reg & TCPC_CC_STATUS_TERM ||
tcpc_presenting_rd(role_control, CC2));
@@ -270,28 +277,28 @@ static int tcpci_set_polarity(struct tcpc_dev *tcpc,
reg = reg & ~TCPC_ROLE_CTRL_DRP;
if (polarity == TYPEC_POLARITY_CC2) {
- reg &= ~(TCPC_ROLE_CTRL_CC2_MASK << TCPC_ROLE_CTRL_CC2_SHIFT);
+ reg &= ~TCPC_ROLE_CTRL_CC2;
/* Local port is source */
if (cc2 == TYPEC_CC_RD)
/* Role control would have the Rp setting when DRP was enabled */
- reg |= TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT;
+ reg |= FIELD_PREP(TCPC_ROLE_CTRL_CC2, TCPC_ROLE_CTRL_CC_RP);
else
- reg |= TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC2_SHIFT;
+ reg |= FIELD_PREP(TCPC_ROLE_CTRL_CC2, TCPC_ROLE_CTRL_CC_RD);
} else {
- reg &= ~(TCPC_ROLE_CTRL_CC1_MASK << TCPC_ROLE_CTRL_CC1_SHIFT);
+ reg &= ~TCPC_ROLE_CTRL_CC1;
/* Local port is source */
if (cc1 == TYPEC_CC_RD)
/* Role control would have the Rp setting when DRP was enabled */
- reg |= TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT;
+ reg |= FIELD_PREP(TCPC_ROLE_CTRL_CC1, TCPC_ROLE_CTRL_CC_RP);
else
- reg |= TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC1_SHIFT;
+ reg |= FIELD_PREP(TCPC_ROLE_CTRL_CC1, TCPC_ROLE_CTRL_CC_RD);
}
}
if (polarity == TYPEC_POLARITY_CC2)
- reg |= TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC1_SHIFT;
+ reg |= FIELD_PREP(TCPC_ROLE_CTRL_CC1, TCPC_ROLE_CTRL_CC_OPEN);
else
- reg |= TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC2_SHIFT;
+ reg |= FIELD_PREP(TCPC_ROLE_CTRL_CC2, TCPC_ROLE_CTRL_CC_OPEN);
ret = regmap_write(tcpci->regmap, TCPC_ROLE_CTRL, reg);
if (ret < 0)
return ret;
@@ -301,6 +308,28 @@ static int tcpci_set_polarity(struct tcpc_dev *tcpc,
TCPC_TCPC_CTRL_ORIENTATION : 0);
}
+static int tcpci_set_orientation(struct tcpc_dev *tcpc,
+ enum typec_orientation orientation)
+{
+ struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
+ unsigned int reg;
+
+ switch (orientation) {
+ case TYPEC_ORIENTATION_NONE:
+ /* We can't put a single output into high impedance */
+ fallthrough;
+ case TYPEC_ORIENTATION_NORMAL:
+ reg = TCPC_CONFIG_STD_OUTPUT_ORIENTATION_NORMAL;
+ break;
+ case TYPEC_ORIENTATION_REVERSE:
+ reg = TCPC_CONFIG_STD_OUTPUT_ORIENTATION_FLIPPED;
+ break;
+ }
+
+ return regmap_update_bits(tcpci->regmap, TCPC_CONFIG_STD_OUTPUT,
+ TCPC_CONFIG_STD_OUTPUT_ORIENTATION_MASK, reg);
+}
+
static void tcpci_set_partner_usb_comm_capable(struct tcpc_dev *tcpc, bool capable)
{
struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
@@ -427,7 +456,7 @@ static int tcpci_set_roles(struct tcpc_dev *tcpc, bool attached,
unsigned int reg;
int ret;
- reg = PD_REV20 << TCPC_MSG_HDR_INFO_REV_SHIFT;
+ reg = FIELD_PREP(TCPC_MSG_HDR_INFO_REV, PD_REV20);
if (role == TYPEC_SOURCE)
reg |= TCPC_MSG_HDR_INFO_PWR_ROLE;
if (data == TYPEC_HOST)
@@ -578,8 +607,11 @@ static int tcpci_pd_transmit(struct tcpc_dev *tcpc, enum tcpm_transmit_type type
}
/* nRetryCount is 3 in PD2.0 spec where 2 in PD3.0 spec */
- reg = ((negotiated_rev > PD_REV20 ? PD_RETRY_COUNT_3_0_OR_HIGHER : PD_RETRY_COUNT_DEFAULT)
- << TCPC_TRANSMIT_RETRY_SHIFT) | (type << TCPC_TRANSMIT_TYPE_SHIFT);
+ reg = FIELD_PREP(TCPC_TRANSMIT_RETRY,
+ (negotiated_rev > PD_REV20
+ ? PD_RETRY_COUNT_3_0_OR_HIGHER
+ : PD_RETRY_COUNT_DEFAULT));
+ reg |= FIELD_PREP(TCPC_TRANSMIT_TYPE, type);
ret = regmap_write(tcpci->regmap, TCPC_TRANSMIT, reg);
if (ret < 0)
return ret;
@@ -675,10 +707,13 @@ irqreturn_t tcpci_irq(struct tcpci *tcpci)
{
u16 status;
int ret;
+ int irq_ret;
unsigned int raw;
tcpci_read16(tcpci, TCPC_ALERT, &status);
+ irq_ret = status & tcpci->alert_mask;
+process_status:
/*
* Clear alert status for everything except RX_STATUS, which shouldn't
* be cleared until we have successfully retrieved message.
@@ -751,7 +786,12 @@ irqreturn_t tcpci_irq(struct tcpci *tcpci)
else if (status & TCPC_ALERT_TX_FAILED)
tcpm_pd_transmit_complete(tcpci->port, TCPC_TX_FAILED);
- return IRQ_RETVAL(status & tcpci->alert_mask);
+ tcpci_read16(tcpci, TCPC_ALERT, &status);
+
+ if (status & tcpci->alert_mask)
+ goto process_status;
+
+ return IRQ_RETVAL(irq_ret);
}
EXPORT_SYMBOL_GPL(tcpci_irq);
@@ -830,6 +870,9 @@ struct tcpci *tcpci_register_port(struct device *dev, struct tcpci_data *data)
if (tcpci->data->vbus_vsafe0v)
tcpci->tcpc.is_vbus_vsafe0v = tcpci_is_vbus_vsafe0v;
+ if (tcpci->data->set_orientation)
+ tcpci->tcpc.set_orientation = tcpci_set_orientation;
+
err = tcpci_parse_config(tcpci);
if (err < 0)
return ERR_PTR(err);
@@ -873,20 +916,29 @@ static int tcpci_probe(struct i2c_client *client)
if (err < 0)
return err;
- chip->tcpci = tcpci_register_port(&client->dev, &chip->data);
- if (IS_ERR(chip->tcpci))
- return PTR_ERR(chip->tcpci);
+ err = tcpci_check_std_output_cap(chip->data.regmap,
+ TCPC_STD_OUTPUT_CAP_ORIENTATION);
+ if (err < 0)
+ return err;
+
+ chip->data.set_orientation = err;
err = devm_request_threaded_irq(&client->dev, client->irq, NULL,
_tcpci_irq,
- IRQF_SHARED | IRQF_ONESHOT | IRQF_TRIGGER_LOW,
+ IRQF_SHARED | IRQF_ONESHOT,
dev_name(&client->dev), chip);
- if (err < 0) {
- tcpci_unregister_port(chip->tcpci);
+ if (err < 0)
return err;
- }
- return 0;
+ /*
+ * Disable irq while registering port. If irq is configured as an edge
+ * irq this allow to keep track and process the irq as soon as it is enabled.
+ */
+ disable_irq(client->irq);
+ chip->tcpci = tcpci_register_port(&client->dev, &chip->data);
+ enable_irq(client->irq);
+
+ return PTR_ERR_OR_ZERO(chip->tcpci);
}
static void tcpci_remove(struct i2c_client *client)
@@ -903,7 +955,7 @@ static void tcpci_remove(struct i2c_client *client)
}
static const struct i2c_device_id tcpci_id[] = {
- { "tcpci", 0 },
+ { "tcpci" },
{ }
};
MODULE_DEVICE_TABLE(i2c, tcpci_id);
diff --git a/drivers/usb/typec/tcpm/tcpci_maxim.h b/drivers/usb/typec/tcpm/tcpci_maxim.h
index 78ff3b73ee7e..76270d5c2838 100644
--- a/drivers/usb/typec/tcpm/tcpci_maxim.h
+++ b/drivers/usb/typec/tcpm/tcpci_maxim.h
@@ -20,28 +20,24 @@
#define SBUOVPDIS BIT(7)
#define CCOVPDIS BIT(6)
#define SBURPCTRL BIT(5)
-#define CCLPMODESEL_MASK GENMASK(4, 3)
-#define ULTRA_LOW_POWER_MODE BIT(3)
-#define CCRPCTRL_MASK GENMASK(2, 0)
+#define CCLPMODESEL GENMASK(4, 3)
+#define ULTRA_LOW_POWER_MODE 1
+#define CCRPCTRL GENMASK(2, 0)
#define UA_1_SRC 1
#define UA_80_SRC 3
#define TCPC_VENDOR_CC_CTRL3 0x8e
-#define CCWTRDEB_MASK GENMASK(7, 6)
-#define CCWTRDEB_SHIFT 6
+#define CCWTRDEB GENMASK(7, 6)
#define CCWTRDEB_1MS 1
-#define CCWTRSEL_MASK GENMASK(5, 3)
-#define CCWTRSEL_SHIFT 3
+#define CCWTRSEL GENMASK(5, 3)
#define CCWTRSEL_1V 0x4
#define CCLADDERDIS BIT(2)
-#define WTRCYCLE_MASK BIT(0)
-#define WTRCYCLE_SHIFT 0
+#define WTRCYCLE GENMASK(0, 0)
#define WTRCYCLE_2_4_S 0
#define WTRCYCLE_4_8_S 1
#define TCPC_VENDOR_ADC_CTRL1 0x91
-#define ADCINSEL_MASK GENMASK(7, 5)
-#define ADC_CHANNEL_OFFSET 5
+#define ADCINSEL GENMASK(7, 5)
#define ADCEN BIT(0)
enum contamiant_state {
@@ -85,6 +81,20 @@ static inline int max_tcpci_write8(struct max_tcpci_chip *chip, unsigned int reg
return regmap_raw_write(chip->data.regmap, reg, &val, sizeof(u8));
}
-bool max_contaminant_is_contaminant(struct max_tcpci_chip *chip, bool disconnect_while_debounce);
+/**
+ * max_contaminant_is_contaminant - Test if CC was toggled due to contaminant
+ *
+ * @chip: Handle to a struct max_tcpci_chip
+ * @disconnect_while_debounce: Whether the disconnect was detected when CC
+ * pins were debouncing
+ * @cc_handled: Returns whether or not update to CC status was handled here
+ *
+ * Determine if a contaminant was detected.
+ *
+ * Returns: true if a contaminant was detected, false otherwise. cc_handled
+ * is updated to reflect whether or not further CC handling is required.
+ */
+bool max_contaminant_is_contaminant(struct max_tcpci_chip *chip, bool disconnect_while_debounce,
+ bool *cc_handled);
#endif // TCPCI_MAXIM_H_
diff --git a/drivers/usb/typec/tcpm/tcpci_maxim_core.c b/drivers/usb/typec/tcpm/tcpci_maxim_core.c
index eec3bcec119c..fd1b80593367 100644
--- a/drivers/usb/typec/tcpm/tcpci_maxim_core.c
+++ b/drivers/usb/typec/tcpm/tcpci_maxim_core.c
@@ -97,11 +97,13 @@ static void max_tcpci_init_regs(struct max_tcpci_chip *chip)
if (ret < 0)
return;
- alert_mask = TCPC_ALERT_TX_SUCCESS | TCPC_ALERT_TX_DISCARDED | TCPC_ALERT_TX_FAILED |
- TCPC_ALERT_RX_HARD_RST | TCPC_ALERT_RX_STATUS | TCPC_ALERT_CC_STATUS |
- TCPC_ALERT_VBUS_DISCNCT | TCPC_ALERT_RX_BUF_OVF | TCPC_ALERT_POWER_STATUS |
- /* Enable Extended alert for detecting Fast Role Swap Signal */
- TCPC_ALERT_EXTND | TCPC_ALERT_EXTENDED_STATUS | TCPC_ALERT_FAULT;
+ alert_mask = (TCPC_ALERT_TX_SUCCESS | TCPC_ALERT_TX_DISCARDED |
+ TCPC_ALERT_TX_FAILED | TCPC_ALERT_RX_HARD_RST |
+ TCPC_ALERT_RX_STATUS | TCPC_ALERT_POWER_STATUS |
+ TCPC_ALERT_CC_STATUS |
+ TCPC_ALERT_EXTND | TCPC_ALERT_EXTENDED_STATUS |
+ TCPC_ALERT_VBUS_DISCNCT | TCPC_ALERT_RX_BUF_OVF |
+ TCPC_ALERT_FAULT);
ret = max_tcpci_write16(chip, TCPC_ALERT_MASK, alert_mask);
if (ret < 0) {
@@ -191,9 +193,8 @@ static void process_rx(struct max_tcpci_chip *chip, u16 status)
* Read complete, clear RX status alert bit.
* Clear overflow as well if set.
*/
- ret = max_tcpci_write16(chip, TCPC_ALERT, status & TCPC_ALERT_RX_BUF_OVF ?
- TCPC_ALERT_RX_STATUS | TCPC_ALERT_RX_BUF_OVF :
- TCPC_ALERT_RX_STATUS);
+ ret = max_tcpci_write16(chip, TCPC_ALERT,
+ TCPC_ALERT_RX_STATUS | (status & TCPC_ALERT_RX_BUF_OVF));
if (ret < 0)
return;
@@ -295,9 +296,8 @@ static irqreturn_t _max_tcpci_irq(struct max_tcpci_chip *chip, u16 status)
* be cleared until we have successfully retrieved message.
*/
if (status & ~TCPC_ALERT_RX_STATUS) {
- mask = status & TCPC_ALERT_RX_BUF_OVF ?
- status & ~(TCPC_ALERT_RX_STATUS | TCPC_ALERT_RX_BUF_OVF) :
- status & ~TCPC_ALERT_RX_STATUS;
+ mask = status & ~(TCPC_ALERT_RX_STATUS
+ | (status & TCPC_ALERT_RX_BUF_OVF));
ret = max_tcpci_write16(chip, TCPC_ALERT, mask);
if (ret < 0) {
dev_err(chip->dev, "ALERT clear failed\n");
@@ -357,12 +357,14 @@ static irqreturn_t _max_tcpci_irq(struct max_tcpci_chip *chip, u16 status)
tcpm_vbus_change(chip->port);
if (status & TCPC_ALERT_CC_STATUS) {
+ bool cc_handled = false;
+
if (chip->contaminant_state == DETECTED || tcpm_port_is_toggling(chip->port)) {
- if (!max_contaminant_is_contaminant(chip, false))
+ if (!max_contaminant_is_contaminant(chip, false, &cc_handled))
tcpm_port_clean(chip->port);
- } else {
- tcpm_cc_change(chip->port);
}
+ if (!cc_handled)
+ tcpm_cc_change(chip->port);
}
if (status & TCPC_ALERT_POWER_STATUS)
@@ -397,7 +399,7 @@ static irqreturn_t max_tcpci_irq(int irq, void *dev_id)
}
while (status) {
irq_return = _max_tcpci_irq(chip, status);
- /* Do not return if the ALERT is already set. */
+ /* Do not return if a (new) ALERT is set (again). */
ret = max_tcpci_read16(chip, TCPC_ALERT, &status);
if (ret < 0)
break;
@@ -455,8 +457,9 @@ static int tcpci_init(struct tcpci *tcpci, struct tcpci_data *data)
static void max_tcpci_check_contaminant(struct tcpci *tcpci, struct tcpci_data *tdata)
{
struct max_tcpci_chip *chip = tdata_to_max_tcpci(tdata);
+ bool cc_handled;
- if (!max_contaminant_is_contaminant(chip, true))
+ if (!max_contaminant_is_contaminant(chip, true, &cc_handled))
tcpm_port_clean(chip->port);
}
@@ -472,6 +475,11 @@ static bool max_tcpci_attempt_vconn_swap_discovery(struct tcpci *tcpci, struct t
return true;
}
+static void max_tcpci_unregister_tcpci_port(void *tcpci)
+{
+ tcpci_unregister_port(tcpci);
+}
+
static int max_tcpci_probe(struct i2c_client *client)
{
int ret;
@@ -484,17 +492,17 @@ static int max_tcpci_probe(struct i2c_client *client)
chip->client = client;
chip->data.regmap = devm_regmap_init_i2c(client, &max_tcpci_regmap_config);
- if (IS_ERR(chip->data.regmap)) {
- dev_err(&client->dev, "Regmap init failed\n");
- return PTR_ERR(chip->data.regmap);
- }
+ if (IS_ERR(chip->data.regmap))
+ return dev_err_probe(&client->dev, PTR_ERR(chip->data.regmap),
+ "Regmap init failed\n");
chip->dev = &client->dev;
i2c_set_clientdata(client, chip);
ret = max_tcpci_read8(chip, TCPC_POWER_STATUS, &power_status);
if (ret < 0)
- return ret;
+ return dev_err_probe(&client->dev, ret,
+ "Failed to read TCPC_POWER_STATUS\n");
/* Chip level tcpci callbacks */
chip->data.set_vbus = max_tcpci_set_vbus;
@@ -511,34 +519,29 @@ static int max_tcpci_probe(struct i2c_client *client)
max_tcpci_init_regs(chip);
chip->tcpci = tcpci_register_port(chip->dev, &chip->data);
- if (IS_ERR(chip->tcpci)) {
- dev_err(&client->dev, "TCPCI port registration failed\n");
- return PTR_ERR(chip->tcpci);
- }
+ if (IS_ERR(chip->tcpci))
+ return dev_err_probe(&client->dev, PTR_ERR(chip->tcpci),
+ "TCPCI port registration failed\n");
+
+ ret = devm_add_action_or_reset(&client->dev,
+ max_tcpci_unregister_tcpci_port,
+ chip->tcpci);
+ if (ret)
+ return ret;
+
chip->port = tcpci_get_tcpm_port(chip->tcpci);
+
ret = max_tcpci_init_alert(chip, client);
if (ret < 0)
- goto unreg_port;
+ return dev_err_probe(&client->dev, ret,
+ "IRQ initialization failed\n");
device_init_wakeup(chip->dev, true);
return 0;
-
-unreg_port:
- tcpci_unregister_port(chip->tcpci);
-
- return ret;
-}
-
-static void max_tcpci_remove(struct i2c_client *client)
-{
- struct max_tcpci_chip *chip = i2c_get_clientdata(client);
-
- if (!IS_ERR_OR_NULL(chip->tcpci))
- tcpci_unregister_port(chip->tcpci);
}
static const struct i2c_device_id max_tcpci_id[] = {
- { "maxtcpc", 0 },
+ { "maxtcpc" },
{ }
};
MODULE_DEVICE_TABLE(i2c, max_tcpci_id);
@@ -557,7 +560,6 @@ static struct i2c_driver max_tcpci_i2c_driver = {
.of_match_table = of_match_ptr(max_tcpci_of_match),
},
.probe = max_tcpci_probe,
- .remove = max_tcpci_remove,
.id_table = max_tcpci_id,
};
module_i2c_driver(max_tcpci_i2c_driver);
diff --git a/drivers/usb/typec/tcpm/tcpci_rt1711h.c b/drivers/usb/typec/tcpm/tcpci_rt1711h.c
index 67422d45eb54..64f6dd0dc660 100644
--- a/drivers/usb/typec/tcpm/tcpci_rt1711h.c
+++ b/drivers/usb/typec/tcpm/tcpci_rt1711h.c
@@ -5,6 +5,7 @@
* Richtek RT1711H Type-C Chip Driver
*/
+#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/kernel.h>
#include <linux/mod_devicetable.h>
@@ -195,12 +196,10 @@ static inline int rt1711h_init_cc_params(struct rt1711h_chip *chip, u8 status)
if (ret < 0)
return ret;
- cc1 = tcpci_to_typec_cc((status >> TCPC_CC_STATUS_CC1_SHIFT) &
- TCPC_CC_STATUS_CC1_MASK,
+ cc1 = tcpci_to_typec_cc(FIELD_GET(TCPC_CC_STATUS_CC1, status),
status & TCPC_CC_STATUS_TERM ||
tcpc_presenting_rd(role, CC1));
- cc2 = tcpci_to_typec_cc((status >> TCPC_CC_STATUS_CC2_SHIFT) &
- TCPC_CC_STATUS_CC2_MASK,
+ cc2 = tcpci_to_typec_cc(FIELD_GET(TCPC_CC_STATUS_CC2, status),
status & TCPC_CC_STATUS_TERM ||
tcpc_presenting_rd(role, CC2));
@@ -233,25 +232,25 @@ static int rt1711h_start_drp_toggling(struct tcpci *tcpci,
switch (cc) {
default:
case TYPEC_CC_RP_DEF:
- reg |= (TCPC_ROLE_CTRL_RP_VAL_DEF <<
- TCPC_ROLE_CTRL_RP_VAL_SHIFT);
+ reg |= FIELD_PREP(TCPC_ROLE_CTRL_RP_VAL,
+ TCPC_ROLE_CTRL_RP_VAL_DEF);
break;
case TYPEC_CC_RP_1_5:
- reg |= (TCPC_ROLE_CTRL_RP_VAL_1_5 <<
- TCPC_ROLE_CTRL_RP_VAL_SHIFT);
+ reg |= FIELD_PREP(TCPC_ROLE_CTRL_RP_VAL,
+ TCPC_ROLE_CTRL_RP_VAL_1_5);
break;
case TYPEC_CC_RP_3_0:
- reg |= (TCPC_ROLE_CTRL_RP_VAL_3_0 <<
- TCPC_ROLE_CTRL_RP_VAL_SHIFT);
+ reg |= FIELD_PREP(TCPC_ROLE_CTRL_RP_VAL,
+ TCPC_ROLE_CTRL_RP_VAL_3_0);
break;
}
if (cc == TYPEC_CC_RD)
- reg |= (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC1_SHIFT) |
- (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC2_SHIFT);
+ reg |= (FIELD_PREP(TCPC_ROLE_CTRL_CC1, TCPC_ROLE_CTRL_CC_RD)
+ | FIELD_PREP(TCPC_ROLE_CTRL_CC2, TCPC_ROLE_CTRL_CC_RD));
else
- reg |= (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT) |
- (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT);
+ reg |= (FIELD_PREP(TCPC_ROLE_CTRL_CC1, TCPC_ROLE_CTRL_CC_RP)
+ | FIELD_PREP(TCPC_ROLE_CTRL_CC2, TCPC_ROLE_CTRL_CC_RP));
ret = rt1711h_write8(chip, TCPC_ROLE_CTRL, reg);
if (ret < 0)
diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
index 8a1af08f71b6..fc619478200f 100644
--- a/drivers/usb/typec/tcpm/tcpm.c
+++ b/drivers/usb/typec/tcpm/tcpm.c
@@ -57,6 +57,7 @@
S(SNK_DISCOVERY_DEBOUNCE), \
S(SNK_DISCOVERY_DEBOUNCE_DONE), \
S(SNK_WAIT_CAPABILITIES), \
+ S(SNK_WAIT_CAPABILITIES_TIMEOUT), \
S(SNK_NEGOTIATE_CAPABILITIES), \
S(SNK_NEGOTIATE_PPS_CAPABILITIES), \
S(SNK_TRANSITION_SINK), \
@@ -943,7 +944,7 @@ static int tcpm_pd_transmit(struct tcpm_port *port,
enum tcpm_transmit_type tx_sop_type,
const struct pd_message *msg)
{
- unsigned long timeout;
+ unsigned long time_left;
int ret;
unsigned int negotiated_rev;
@@ -968,10 +969,10 @@ static int tcpm_pd_transmit(struct tcpm_port *port,
return ret;
mutex_unlock(&port->lock);
- timeout = wait_for_completion_timeout(&port->tx_complete,
- msecs_to_jiffies(PD_T_TCPC_TX_TIMEOUT));
+ time_left = wait_for_completion_timeout(&port->tx_complete,
+ msecs_to_jiffies(PD_T_TCPC_TX_TIMEOUT));
mutex_lock(&port->lock);
- if (!timeout)
+ if (!time_left)
return -ETIMEDOUT;
switch (port->tx_status) {
@@ -3014,8 +3015,10 @@ static int tcpm_register_source_caps(struct tcpm_port *port)
memcpy(caps.pdo, port->source_caps, sizeof(u32) * port->nr_source_caps);
caps.role = TYPEC_SOURCE;
- if (cap)
+ if (cap) {
usb_power_delivery_unregister_capabilities(cap);
+ port->partner_source_caps = NULL;
+ }
cap = usb_power_delivery_register_capabilities(port->partner_pd, &caps);
if (IS_ERR(cap))
@@ -3108,7 +3111,8 @@ static void tcpm_pd_data_request(struct tcpm_port *port,
PD_MSG_CTRL_REJECT :
PD_MSG_CTRL_NOT_SUPP,
NONE_AMS);
- } else if (port->state == SNK_WAIT_CAPABILITIES) {
+ } else if (port->state == SNK_WAIT_CAPABILITIES ||
+ port->state == SNK_WAIT_CAPABILITIES_TIMEOUT) {
/*
* This message may be received even if VBUS is not
* present. This is quite unexpected; see USB PD
@@ -4511,7 +4515,7 @@ static inline enum tcpm_state hard_reset_state(struct tcpm_port *port)
return ERROR_RECOVERY;
if (port->pwr_role == TYPEC_SOURCE)
return SRC_UNATTACHED;
- if (port->state == SNK_WAIT_CAPABILITIES)
+ if (port->state == SNK_WAIT_CAPABILITIES_TIMEOUT)
return SNK_READY;
return SNK_UNATTACHED;
}
@@ -5039,10 +5043,31 @@ static void run_state_machine(struct tcpm_port *port)
tcpm_set_state(port, SNK_SOFT_RESET,
PD_T_SINK_WAIT_CAP);
} else {
- tcpm_set_state(port, hard_reset_state(port),
+ tcpm_set_state(port, SNK_WAIT_CAPABILITIES_TIMEOUT,
PD_T_SINK_WAIT_CAP);
}
break;
+ case SNK_WAIT_CAPABILITIES_TIMEOUT:
+ /*
+ * There are some USB PD sources in the field, which do not
+ * properly implement the specification and fail to start
+ * sending Source Capability messages after a soft reset. The
+ * specification suggests to do a hard reset when no Source
+ * capability message is received within PD_T_SINK_WAIT_CAP,
+ * but that might effectively kil the machine's power source.
+ *
+ * This slightly diverges from the specification and tries to
+ * recover from this by explicitly asking for the capabilities
+ * using the Get_Source_Cap control message before falling back
+ * to a hard reset. The control message should also be supported
+ * and handled by all USB PD source and dual role devices
+ * according to the specification.
+ */
+ if (tcpm_pd_send_control(port, PD_CTRL_GET_SOURCE_CAP, TCPC_TX_SOP))
+ tcpm_set_state_cond(port, hard_reset_state(port), 0);
+ else
+ tcpm_set_state(port, hard_reset_state(port), PD_T_SINK_WAIT_CAP);
+ break;
case SNK_NEGOTIATE_CAPABILITIES:
port->pd_capable = true;
tcpm_set_partner_usb_comm_capable(port,
@@ -5185,6 +5210,8 @@ static void run_state_machine(struct tcpm_port *port)
case HARD_RESET_SEND:
if (port->ams != NONE_AMS)
tcpm_ams_finish(port);
+ if (!port->self_powered && port->port_type == TYPEC_PORT_SNK)
+ dev_err(port->dev, "Initiating hard-reset, which might result in machine power-loss.\n");
/*
* State machine will be directed to HARD_RESET_START,
* thus set upcoming_state to INVALID_STATE.
@@ -5628,7 +5655,6 @@ static void run_state_machine(struct tcpm_port *port)
break;
case PORT_RESET:
tcpm_reset_port(port);
- port->pd_events = 0;
if (port->self_powered)
tcpm_set_cc(port, TYPEC_CC_OPEN);
else
@@ -6172,6 +6198,7 @@ static void _tcpm_pd_hard_reset(struct tcpm_port *port)
port->tcpc->set_bist_data(port->tcpc, false);
switch (port->state) {
+ case TOGGLING:
case ERROR_RECOVERY:
case PORT_RESET:
case PORT_RESET_WAIT_OFF:
@@ -7456,12 +7483,6 @@ static int tcpm_psy_prop_writeable(struct power_supply *psy,
}
}
-static enum power_supply_usb_type tcpm_psy_usb_types[] = {
- POWER_SUPPLY_USB_TYPE_C,
- POWER_SUPPLY_USB_TYPE_PD,
- POWER_SUPPLY_USB_TYPE_PD_PPS,
-};
-
static const char *tcpm_psy_name_prefix = "tcpm-source-psy-";
static int devm_tcpm_psy_register(struct tcpm_port *port)
@@ -7482,8 +7503,9 @@ static int devm_tcpm_psy_register(struct tcpm_port *port)
port_dev_name);
port->psy_desc.name = psy_name;
port->psy_desc.type = POWER_SUPPLY_TYPE_USB;
- port->psy_desc.usb_types = tcpm_psy_usb_types;
- port->psy_desc.num_usb_types = ARRAY_SIZE(tcpm_psy_usb_types);
+ port->psy_desc.usb_types = BIT(POWER_SUPPLY_USB_TYPE_C) |
+ BIT(POWER_SUPPLY_USB_TYPE_PD) |
+ BIT(POWER_SUPPLY_USB_TYPE_PD_PPS);
port->psy_desc.properties = tcpm_psy_props;
port->psy_desc.num_properties = ARRAY_SIZE(tcpm_psy_props);
port->psy_desc.get_property = tcpm_psy_get_prop;
diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c
index 0717cfcd9f8c..7ee721a877c1 100644
--- a/drivers/usb/typec/tipd/core.c
+++ b/drivers/usb/typec/tipd/core.c
@@ -28,6 +28,7 @@
#define TPS_REG_MODE 0x03
#define TPS_REG_CMD1 0x08
#define TPS_REG_DATA1 0x09
+#define TPS_REG_VERSION 0x0F
#define TPS_REG_INT_EVENT1 0x14
#define TPS_REG_INT_EVENT2 0x15
#define TPS_REG_INT_MASK1 0x16
@@ -149,11 +150,6 @@ static enum power_supply_property tps6598x_psy_props[] = {
POWER_SUPPLY_PROP_ONLINE,
};
-static enum power_supply_usb_type tps6598x_psy_usb_types[] = {
- POWER_SUPPLY_USB_TYPE_C,
- POWER_SUPPLY_USB_TYPE_PD,
-};
-
static const char *tps6598x_psy_name_prefix = "tps6598x-source-psy-";
/*
@@ -604,11 +600,11 @@ static irqreturn_t tps25750_interrupt(int irq, void *data)
if (!tps6598x_read_status(tps, &status))
goto err_clear_ints;
- if ((event[0] | event[1]) & TPS_REG_INT_POWER_STATUS_UPDATE)
+ if (event[0] & TPS_REG_INT_POWER_STATUS_UPDATE)
if (!tps6598x_read_power_status(tps))
goto err_clear_ints;
- if ((event[0] | event[1]) & TPS_REG_INT_DATA_STATUS_UPDATE)
+ if (event[0] & TPS_REG_INT_DATA_STATUS_UPDATE)
if (!tps6598x_read_data_status(tps))
goto err_clear_ints;
@@ -617,7 +613,7 @@ static irqreturn_t tps25750_interrupt(int irq, void *data)
* a plug event. Therefore, we need to check
* for pr/dr status change to set TypeC dr/pr accordingly.
*/
- if ((event[0] | event[1]) & TPS_REG_INT_PLUG_EVENT ||
+ if (event[0] & TPS_REG_INT_PLUG_EVENT ||
tps6598x_has_role_changed(tps, status))
tps6598x_handle_plug_event(tps, status);
@@ -636,49 +632,67 @@ err_unlock:
static irqreturn_t tps6598x_interrupt(int irq, void *data)
{
+ int intev_len = TPS_65981_2_6_INTEVENT_LEN;
struct tps6598x *tps = data;
- u64 event1 = 0;
- u64 event2 = 0;
+ u64 event1[2] = { };
+ u64 event2[2] = { };
+ u32 version;
u32 status;
int ret;
mutex_lock(&tps->lock);
- ret = tps6598x_read64(tps, TPS_REG_INT_EVENT1, &event1);
- ret |= tps6598x_read64(tps, TPS_REG_INT_EVENT2, &event2);
+ ret = tps6598x_read32(tps, TPS_REG_VERSION, &version);
+ if (ret)
+ dev_warn(tps->dev, "%s: failed to read version (%d)\n",
+ __func__, ret);
+
+ if (TPS_VERSION_HW_VERSION(version) == TPS_VERSION_HW_65987_8_DH ||
+ TPS_VERSION_HW_VERSION(version) == TPS_VERSION_HW_65987_8_DK)
+ intev_len = TPS_65987_8_INTEVENT_LEN;
+
+ ret = tps6598x_block_read(tps, TPS_REG_INT_EVENT1, event1, intev_len);
+
+ ret = tps6598x_block_read(tps, TPS_REG_INT_EVENT1, event1, intev_len);
if (ret) {
- dev_err(tps->dev, "%s: failed to read events\n", __func__);
+ dev_err(tps->dev, "%s: failed to read event1\n", __func__);
goto err_unlock;
}
- trace_tps6598x_irq(event1, event2);
+ ret = tps6598x_block_read(tps, TPS_REG_INT_EVENT2, event2, intev_len);
+ if (ret) {
+ dev_err(tps->dev, "%s: failed to read event2\n", __func__);
+ goto err_unlock;
+ }
+ trace_tps6598x_irq(event1[0], event2[0]);
- if (!(event1 | event2))
+ if (!(event1[0] | event1[1] | event2[0] | event2[1]))
goto err_unlock;
if (!tps6598x_read_status(tps, &status))
goto err_clear_ints;
- if ((event1 | event2) & TPS_REG_INT_POWER_STATUS_UPDATE)
+ if ((event1[0] | event2[0]) & TPS_REG_INT_POWER_STATUS_UPDATE)
if (!tps6598x_read_power_status(tps))
goto err_clear_ints;
- if ((event1 | event2) & TPS_REG_INT_DATA_STATUS_UPDATE)
+ if ((event1[0] | event2[0]) & TPS_REG_INT_DATA_STATUS_UPDATE)
if (!tps6598x_read_data_status(tps))
goto err_clear_ints;
/* Handle plug insert or removal */
- if ((event1 | event2) & TPS_REG_INT_PLUG_EVENT)
+ if ((event1[0] | event2[0]) & TPS_REG_INT_PLUG_EVENT)
tps6598x_handle_plug_event(tps, status);
err_clear_ints:
- tps6598x_write64(tps, TPS_REG_INT_CLEAR1, event1);
- tps6598x_write64(tps, TPS_REG_INT_CLEAR2, event2);
+ tps6598x_block_write(tps, TPS_REG_INT_CLEAR1, event1, intev_len);
+ tps6598x_block_write(tps, TPS_REG_INT_CLEAR2, event2, intev_len);
err_unlock:
mutex_unlock(&tps->lock);
- if (event1 | event2)
+ if (event1[0] | event1[1] | event2[0] | event2[1])
return IRQ_HANDLED;
+
return IRQ_NONE;
}
@@ -808,8 +822,8 @@ static int devm_tps6598_psy_register(struct tps6598x *tps)
tps->psy_desc.name = psy_name;
tps->psy_desc.type = POWER_SUPPLY_TYPE_USB;
- tps->psy_desc.usb_types = tps6598x_psy_usb_types;
- tps->psy_desc.num_usb_types = ARRAY_SIZE(tps6598x_psy_usb_types);
+ tps->psy_desc.usb_types = BIT(POWER_SUPPLY_USB_TYPE_C) |
+ BIT(POWER_SUPPLY_USB_TYPE_PD);
tps->psy_desc.properties = tps6598x_psy_props;
tps->psy_desc.num_properties = ARRAY_SIZE(tps6598x_psy_props);
tps->psy_desc.get_property = tps6598x_psy_get_prop;
@@ -873,19 +887,19 @@ tps6598x_register_port(struct tps6598x *tps, struct fwnode_handle *fwnode)
return 0;
}
-static int tps_request_firmware(struct tps6598x *tps, const struct firmware **fw)
+static int tps_request_firmware(struct tps6598x *tps, const struct firmware **fw,
+ const char **firmware_name)
{
- const char *firmware_name;
int ret;
ret = device_property_read_string(tps->dev, "firmware-name",
- &firmware_name);
+ firmware_name);
if (ret)
return ret;
- ret = request_firmware(fw, firmware_name, tps->dev);
+ ret = request_firmware(fw, *firmware_name, tps->dev);
if (ret) {
- dev_err(tps->dev, "failed to retrieve \"%s\"\n", firmware_name);
+ dev_err(tps->dev, "failed to retrieve \"%s\"\n", *firmware_name);
return ret;
}
@@ -980,12 +994,7 @@ static int tps25750_start_patch_burst_mode(struct tps6598x *tps)
u32 addr;
struct device_node *np = tps->dev->of_node;
- ret = device_property_read_string(tps->dev, "firmware-name",
- &firmware_name);
- if (ret)
- return ret;
-
- ret = tps_request_firmware(tps, &fw);
+ ret = tps_request_firmware(tps, &fw, &firmware_name);
if (ret)
return ret;
@@ -1136,12 +1145,7 @@ static int tps6598x_apply_patch(struct tps6598x *tps)
const char *firmware_name;
int ret;
- ret = device_property_read_string(tps->dev, "firmware-name",
- &firmware_name);
- if (ret)
- return ret;
-
- ret = tps_request_firmware(tps, &fw);
+ ret = tps_request_firmware(tps, &fw, &firmware_name);
if (ret)
return ret;
@@ -1156,10 +1160,7 @@ static int tps6598x_apply_patch(struct tps6598x *tps)
bytes_left = fw->size;
while (bytes_left) {
- if (bytes_left < TPS_MAX_LEN)
- in_len = bytes_left;
- else
- in_len = TPS_MAX_LEN;
+ in_len = min(bytes_left, TPS_MAX_LEN);
ret = tps6598x_exec_cmd(tps, "PTCd", in_len,
fw->data + copied_bytes,
TPS_PTCD_OUT_BYTES, out);
@@ -1185,10 +1186,14 @@ static int tps6598x_apply_patch(struct tps6598x *tps)
dev_info(tps->dev, "Firmware update succeeded\n");
release_fw:
+ if (ret) {
+ dev_err(tps->dev, "Failed to write patch %s of %zu bytes\n",
+ firmware_name, fw->size);
+ }
release_firmware(fw);
return ret;
-};
+}
static int cd321x_init(struct tps6598x *tps)
{
@@ -1346,10 +1351,7 @@ static int tps6598x_probe(struct i2c_client *client)
TPS_REG_INT_PLUG_EVENT;
}
- if (dev_fwnode(tps->dev))
- tps->data = device_get_match_data(tps->dev);
- else
- tps->data = i2c_get_match_data(client);
+ tps->data = i2c_get_match_data(client);
if (!tps->data)
return -EINVAL;
@@ -1458,8 +1460,9 @@ static void tps6598x_remove(struct i2c_client *client)
if (!client->irq)
cancel_delayed_work_sync(&tps->wq_poll);
+ else
+ devm_free_irq(tps->dev, client->irq, tps);
- devm_free_irq(tps->dev, client->irq, tps);
tps6598x_disconnect(tps, 0);
typec_unregister_port(tps->port);
usb_role_switch_put(tps->role_sw);
diff --git a/drivers/usb/typec/tipd/tps6598x.h b/drivers/usb/typec/tipd/tps6598x.h
index 89b24519463a..9b23e9017452 100644
--- a/drivers/usb/typec/tipd/tps6598x.h
+++ b/drivers/usb/typec/tipd/tps6598x.h
@@ -253,4 +253,15 @@
#define TPS_PTCC_DEV 2
#define TPS_PTCC_APP 3
+/* Version Register */
+#define TPS_VERSION_HW_VERSION_MASK GENMASK(31, 24)
+#define TPS_VERSION_HW_VERSION(x) TPS_FIELD_GET(TPS_VERSION_HW_VERSION_MASK, (x))
+#define TPS_VERSION_HW_65981_2_6 0x00
+#define TPS_VERSION_HW_65987_8_DH 0xF7
+#define TPS_VERSION_HW_65987_8_DK 0xF9
+
+/* Int Event Register length */
+#define TPS_65981_2_6_INTEVENT_LEN 8
+#define TPS_65987_8_INTEVENT_LEN 11
+
#endif /* __TPS6598X_H__ */
diff --git a/drivers/usb/typec/ucsi/Kconfig b/drivers/usb/typec/ucsi/Kconfig
index bdcb1764cfae..680e1b87b152 100644
--- a/drivers/usb/typec/ucsi/Kconfig
+++ b/drivers/usb/typec/ucsi/Kconfig
@@ -69,4 +69,13 @@ config UCSI_PMIC_GLINK
To compile the driver as a module, choose M here: the module will be
called ucsi_glink.
+config UCSI_LENOVO_YOGA_C630
+ tristate "UCSI Interface Driver for Lenovo Yoga C630"
+ depends on EC_LENOVO_YOGA_C630
+ help
+ This driver enables UCSI support on the Lenovo Yoga C630 laptop.
+
+ To compile the driver as a module, choose M here: the module will be
+ called ucsi_yoga_c630.
+
endif
diff --git a/drivers/usb/typec/ucsi/Makefile b/drivers/usb/typec/ucsi/Makefile
index b4679f94696b..aed41d23887b 100644
--- a/drivers/usb/typec/ucsi/Makefile
+++ b/drivers/usb/typec/ucsi/Makefile
@@ -21,3 +21,4 @@ obj-$(CONFIG_UCSI_ACPI) += ucsi_acpi.o
obj-$(CONFIG_UCSI_CCG) += ucsi_ccg.o
obj-$(CONFIG_UCSI_STM32G0) += ucsi_stm32g0.o
obj-$(CONFIG_UCSI_PMIC_GLINK) += ucsi_glink.o
+obj-$(CONFIG_UCSI_LENOVO_YOGA_C630) += ucsi_yoga_c630.o
diff --git a/drivers/usb/typec/ucsi/displayport.c b/drivers/usb/typec/ucsi/displayport.c
index d9d3c91125ca..420af5139c70 100644
--- a/drivers/usb/typec/ucsi/displayport.c
+++ b/drivers/usb/typec/ucsi/displayport.c
@@ -275,8 +275,6 @@ static void ucsi_displayport_work(struct work_struct *work)
struct ucsi_dp *dp = container_of(work, struct ucsi_dp, work);
int ret;
- mutex_lock(&dp->con->lock);
-
ret = typec_altmode_vdm(dp->alt, dp->header,
dp->vdo_data, dp->vdo_size);
if (ret)
@@ -285,8 +283,6 @@ static void ucsi_displayport_work(struct work_struct *work)
dp->vdo_data = NULL;
dp->vdo_size = 0;
dp->header = 0;
-
- mutex_unlock(&dp->con->lock);
}
void ucsi_displayport_remove_partner(struct typec_altmode *alt)
@@ -337,7 +333,7 @@ struct typec_altmode *ucsi_register_displayport(struct ucsi_connector *con,
dp->con = con;
dp->alt = alt;
- alt->ops = &ucsi_displayport_ops;
+ typec_altmode_set_ops(alt, &ucsi_displayport_ops);
typec_altmode_set_drvdata(alt, dp);
return alt;
diff --git a/drivers/usb/typec/ucsi/psy.c b/drivers/usb/typec/ucsi/psy.c
index b35c6e07911e..1c631c7855a9 100644
--- a/drivers/usb/typec/ucsi/psy.c
+++ b/drivers/usb/typec/ucsi/psy.c
@@ -20,6 +20,7 @@ enum ucsi_psy_online_states {
};
static enum power_supply_property ucsi_psy_props[] = {
+ POWER_SUPPLY_PROP_CHARGE_TYPE,
POWER_SUPPLY_PROP_USB_TYPE,
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_VOLTAGE_MIN,
@@ -194,6 +195,35 @@ static int ucsi_psy_get_usb_type(struct ucsi_connector *con,
return 0;
}
+static int ucsi_psy_get_charge_type(struct ucsi_connector *con, union power_supply_propval *val)
+{
+ if (!(con->status.flags & UCSI_CONSTAT_CONNECTED)) {
+ val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
+ return 0;
+ }
+
+ /* The Battery Charging Cabability Status field is only valid in sink role. */
+ if ((con->status.flags & UCSI_CONSTAT_PWR_DIR) != TYPEC_SINK) {
+ val->intval = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
+ return 0;
+ }
+
+ switch (UCSI_CONSTAT_BC_STATUS(con->status.pwr_status)) {
+ case UCSI_CONSTAT_BC_NOMINAL_CHARGING:
+ val->intval = POWER_SUPPLY_CHARGE_TYPE_STANDARD;
+ break;
+ case UCSI_CONSTAT_BC_SLOW_CHARGING:
+ case UCSI_CONSTAT_BC_TRICKLE_CHARGING:
+ val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+ break;
+ default:
+ val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
+ break;
+ }
+
+ return 0;
+}
+
static int ucsi_psy_get_prop(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
@@ -201,6 +231,8 @@ static int ucsi_psy_get_prop(struct power_supply *psy,
struct ucsi_connector *con = power_supply_get_drvdata(psy);
switch (psp) {
+ case POWER_SUPPLY_PROP_CHARGE_TYPE:
+ return ucsi_psy_get_charge_type(con, val);
case POWER_SUPPLY_PROP_USB_TYPE:
return ucsi_psy_get_usb_type(con, val);
case POWER_SUPPLY_PROP_ONLINE:
@@ -222,12 +254,6 @@ static int ucsi_psy_get_prop(struct power_supply *psy,
}
}
-static enum power_supply_usb_type ucsi_psy_usb_types[] = {
- POWER_SUPPLY_USB_TYPE_C,
- POWER_SUPPLY_USB_TYPE_PD,
- POWER_SUPPLY_USB_TYPE_PD_PPS,
-};
-
int ucsi_register_port_psy(struct ucsi_connector *con)
{
struct power_supply_config psy_cfg = {};
@@ -244,8 +270,9 @@ int ucsi_register_port_psy(struct ucsi_connector *con)
con->psy_desc.name = psy_name;
con->psy_desc.type = POWER_SUPPLY_TYPE_USB;
- con->psy_desc.usb_types = ucsi_psy_usb_types;
- con->psy_desc.num_usb_types = ARRAY_SIZE(ucsi_psy_usb_types);
+ con->psy_desc.usb_types = BIT(POWER_SUPPLY_USB_TYPE_C) |
+ BIT(POWER_SUPPLY_USB_TYPE_PD) |
+ BIT(POWER_SUPPLY_USB_TYPE_PD_PPS);
con->psy_desc.properties = ucsi_psy_props;
con->psy_desc.num_properties = ARRAY_SIZE(ucsi_psy_props);
con->psy_desc.get_property = ucsi_psy_get_prop;
diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c
index bd6ae92aa39e..e0f3925e401b 100644
--- a/drivers/usb/typec/ucsi/ucsi.c
+++ b/drivers/usb/typec/ucsi/ucsi.c
@@ -36,61 +36,118 @@
*/
#define UCSI_SWAP_TIMEOUT_MS 5000
-static int ucsi_read_message_in(struct ucsi *ucsi, void *buf,
- size_t buf_size)
+void ucsi_notify_common(struct ucsi *ucsi, u32 cci)
{
- /*
- * Below UCSI 2.0, MESSAGE_IN was limited to 16 bytes. Truncate the
- * reads here.
- */
- if (ucsi->version <= UCSI_VERSION_1_2)
- buf_size = clamp(buf_size, 0, 16);
+ /* Ignore bogus data in CCI if busy indicator is set. */
+ if (cci & UCSI_CCI_BUSY)
+ return;
+
+ if (UCSI_CCI_CONNECTOR(cci))
+ ucsi_connector_change(ucsi, UCSI_CCI_CONNECTOR(cci));
+
+ if (cci & UCSI_CCI_ACK_COMPLETE &&
+ test_bit(ACK_PENDING, &ucsi->flags))
+ complete(&ucsi->complete);
- return ucsi->ops->read(ucsi, UCSI_MESSAGE_IN, buf, buf_size);
+ if (cci & UCSI_CCI_COMMAND_COMPLETE &&
+ test_bit(COMMAND_PENDING, &ucsi->flags))
+ complete(&ucsi->complete);
}
+EXPORT_SYMBOL_GPL(ucsi_notify_common);
-static int ucsi_acknowledge_command(struct ucsi *ucsi)
+int ucsi_sync_control_common(struct ucsi *ucsi, u64 command)
{
- u64 ctrl;
+ bool ack = UCSI_COMMAND(command) == UCSI_ACK_CC_CI;
+ int ret;
- ctrl = UCSI_ACK_CC_CI;
- ctrl |= UCSI_ACK_COMMAND_COMPLETE;
+ if (ack)
+ set_bit(ACK_PENDING, &ucsi->flags);
+ else
+ set_bit(COMMAND_PENDING, &ucsi->flags);
+
+ ret = ucsi->ops->async_control(ucsi, command);
+ if (ret)
+ goto out_clear_bit;
+
+ if (!wait_for_completion_timeout(&ucsi->complete, 5 * HZ))
+ ret = -ETIMEDOUT;
+
+out_clear_bit:
+ if (ack)
+ clear_bit(ACK_PENDING, &ucsi->flags);
+ else
+ clear_bit(COMMAND_PENDING, &ucsi->flags);
- return ucsi->ops->sync_write(ucsi, UCSI_CONTROL, &ctrl, sizeof(ctrl));
+ return ret;
}
+EXPORT_SYMBOL_GPL(ucsi_sync_control_common);
-static int ucsi_acknowledge_connector_change(struct ucsi *ucsi)
+static int ucsi_acknowledge(struct ucsi *ucsi, bool conn_ack)
{
u64 ctrl;
ctrl = UCSI_ACK_CC_CI;
- ctrl |= UCSI_ACK_CONNECTOR_CHANGE;
+ ctrl |= UCSI_ACK_COMMAND_COMPLETE;
+ if (conn_ack) {
+ clear_bit(EVENT_PENDING, &ucsi->flags);
+ ctrl |= UCSI_ACK_CONNECTOR_CHANGE;
+ }
- return ucsi->ops->sync_write(ucsi, UCSI_CONTROL, &ctrl, sizeof(ctrl));
+ return ucsi->ops->sync_control(ucsi, ctrl);
}
-static int ucsi_exec_command(struct ucsi *ucsi, u64 command);
-
-static int ucsi_read_error(struct ucsi *ucsi)
+static int ucsi_run_command(struct ucsi *ucsi, u64 command, u32 *cci,
+ void *data, size_t size, bool conn_ack)
{
- u16 error;
- int ret;
+ int ret, err;
+
+ *cci = 0;
+
+ if (size > UCSI_MAX_DATA_LENGTH(ucsi))
+ return -EINVAL;
+
+ ret = ucsi->ops->sync_control(ucsi, command);
+ if (ucsi->ops->read_cci(ucsi, cci))
+ return -EIO;
- /* Acknowledge the command that failed */
- ret = ucsi_acknowledge_command(ucsi);
+ if (*cci & UCSI_CCI_BUSY)
+ return ucsi_run_command(ucsi, UCSI_CANCEL, cci, NULL, 0, false) ?: -EBUSY;
if (ret)
return ret;
- ret = ucsi_exec_command(ucsi, UCSI_GET_ERROR_STATUS);
- if (ret < 0)
- return ret;
+ if (!(*cci & UCSI_CCI_COMMAND_COMPLETE))
+ return -EIO;
+
+ if (*cci & UCSI_CCI_NOT_SUPPORTED)
+ err = -EOPNOTSUPP;
+ else if (*cci & UCSI_CCI_ERROR)
+ err = -EIO;
+ else
+ err = 0;
+
+ if (!err && data && UCSI_CCI_LENGTH(*cci))
+ err = ucsi->ops->read_message_in(ucsi, data, size);
- ret = ucsi_read_message_in(ucsi, &error, sizeof(error));
+ /*
+ * Don't ACK connection change if there was an error.
+ */
+ ret = ucsi_acknowledge(ucsi, err ? false : conn_ack);
if (ret)
return ret;
- ret = ucsi_acknowledge_command(ucsi);
- if (ret)
+ return err ?: UCSI_CCI_LENGTH(*cci);
+}
+
+static int ucsi_read_error(struct ucsi *ucsi, u8 connector_num)
+{
+ u64 command;
+ u16 error;
+ u32 cci;
+ int ret;
+
+ command = UCSI_GET_ERROR_STATUS | UCSI_CONNECTOR_NUMBER(connector_num);
+ ret = ucsi_run_command(ucsi, command, &cci, &error, sizeof(error), false);
+ if (ret < 0)
return ret;
switch (error) {
@@ -123,6 +180,12 @@ static int ucsi_read_error(struct ucsi *ucsi)
case UCSI_ERROR_SWAP_REJECTED:
dev_warn(ucsi->dev, "Swap rejected\n");
break;
+ case UCSI_ERROR_REVERSE_CURRENT_PROTECTION:
+ dev_warn(ucsi->dev, "Reverse Current Protection detected\n");
+ break;
+ case UCSI_ERROR_SET_SINK_PATH_REJECTED:
+ dev_warn(ucsi->dev, "Set Sink Path rejected\n");
+ break;
case UCSI_ERROR_UNDEFINED:
default:
dev_err(ucsi->dev, "unknown error %u\n", error);
@@ -132,74 +195,48 @@ static int ucsi_read_error(struct ucsi *ucsi)
return -EIO;
}
-static int ucsi_exec_command(struct ucsi *ucsi, u64 cmd)
+static int ucsi_send_command_common(struct ucsi *ucsi, u64 cmd,
+ void *data, size_t size, bool conn_ack)
{
+ u8 connector_num;
u32 cci;
int ret;
- ret = ucsi->ops->sync_write(ucsi, UCSI_CONTROL, &cmd, sizeof(cmd));
- if (ret)
- return ret;
-
- ret = ucsi->ops->read(ucsi, UCSI_CCI, &cci, sizeof(cci));
- if (ret)
- return ret;
-
- if (cmd != UCSI_CANCEL && cci & UCSI_CCI_BUSY)
- return ucsi_exec_command(ucsi, UCSI_CANCEL);
-
- if (!(cci & UCSI_CCI_COMMAND_COMPLETE))
- return -EIO;
-
- if (cci & UCSI_CCI_NOT_SUPPORTED) {
- if (ucsi_acknowledge_command(ucsi) < 0)
- dev_err(ucsi->dev,
- "ACK of unsupported command failed\n");
- return -EOPNOTSUPP;
+ if (ucsi->version > UCSI_VERSION_1_2) {
+ switch (UCSI_COMMAND(cmd)) {
+ case UCSI_GET_ALTERNATE_MODES:
+ connector_num = UCSI_GET_ALTMODE_GET_CONNECTOR_NUMBER(cmd);
+ break;
+ case UCSI_PPM_RESET:
+ case UCSI_CANCEL:
+ case UCSI_ACK_CC_CI:
+ case UCSI_SET_NOTIFICATION_ENABLE:
+ case UCSI_GET_CAPABILITY:
+ connector_num = 0;
+ break;
+ default:
+ connector_num = UCSI_DEFAULT_GET_CONNECTOR_NUMBER(cmd);
+ break;
+ }
+ } else {
+ connector_num = 0;
}
- if (cci & UCSI_CCI_ERROR) {
- if (cmd == UCSI_GET_ERROR_STATUS)
- return -EIO;
- return ucsi_read_error(ucsi);
- }
+ mutex_lock(&ucsi->ppm_lock);
- if (cmd == UCSI_CANCEL && cci & UCSI_CCI_CANCEL_COMPLETE) {
- ret = ucsi_acknowledge_command(ucsi);
- return ret ? ret : -EBUSY;
- }
+ ret = ucsi_run_command(ucsi, cmd, &cci, data, size, conn_ack);
+
+ if (cci & UCSI_CCI_ERROR)
+ ret = ucsi_read_error(ucsi, connector_num);
- return UCSI_CCI_LENGTH(cci);
+ mutex_unlock(&ucsi->ppm_lock);
+ return ret;
}
int ucsi_send_command(struct ucsi *ucsi, u64 command,
void *data, size_t size)
{
- u8 length;
- int ret;
-
- mutex_lock(&ucsi->ppm_lock);
-
- ret = ucsi_exec_command(ucsi, command);
- if (ret < 0)
- goto out;
-
- length = ret;
-
- if (data) {
- ret = ucsi_read_message_in(ucsi, data, size);
- if (ret)
- goto out;
- }
-
- ret = ucsi_acknowledge_command(ucsi);
- if (ret)
- goto out;
-
- ret = length;
-out:
- mutex_unlock(&ucsi->ppm_lock);
- return ret;
+ return ucsi_send_command_common(ucsi, command, data, size, false);
}
EXPORT_SYMBOL_GPL(ucsi_send_command);
@@ -619,7 +656,10 @@ static int ucsi_read_pdos(struct ucsi_connector *con,
u64 command;
int ret;
- if (ucsi->quirks & UCSI_NO_PARTNER_PDOS)
+ if (is_partner &&
+ ucsi->quirks & UCSI_NO_PARTNER_PDOS &&
+ ((con->status.flags & UCSI_CONSTAT_PWR_DIR) ||
+ !is_source(role)))
return 0;
command = UCSI_COMMAND(UCSI_GET_PDOS) | UCSI_CONNECTOR_NUMBER(con->num);
@@ -638,9 +678,13 @@ static int ucsi_read_pdos(struct ucsi_connector *con,
static int ucsi_get_pdos(struct ucsi_connector *con, enum typec_role role,
int is_partner, u32 *pdos)
{
+ struct ucsi *ucsi = con->ucsi;
u8 num_pdos;
int ret;
+ if (!(ucsi->cap.features & UCSI_CAP_PDO_DETAILS))
+ return 0;
+
/* UCSI max payload means only getting at most 4 PDOs at a time */
ret = ucsi_read_pdos(con, role, is_partner, pdos, 0, UCSI_MAX_PDOS);
if (ret < 0)
@@ -674,104 +718,86 @@ static int ucsi_get_src_pdos(struct ucsi_connector *con)
return ret;
}
-static int ucsi_read_identity(struct ucsi_connector *con, u8 recipient,
- u8 offset, u8 bytes, void *resp)
+static struct usb_power_delivery_capabilities *ucsi_get_pd_caps(struct ucsi_connector *con,
+ enum typec_role role,
+ bool is_partner)
{
- struct ucsi *ucsi = con->ucsi;
- u64 command;
+ struct usb_power_delivery_capabilities_desc pd_caps;
int ret;
- command = UCSI_COMMAND(UCSI_GET_PD_MESSAGE) |
- UCSI_CONNECTOR_NUMBER(con->num);
- command |= UCSI_GET_PD_MESSAGE_RECIPIENT(recipient);
- command |= UCSI_GET_PD_MESSAGE_OFFSET(offset);
- command |= UCSI_GET_PD_MESSAGE_BYTES(bytes);
- command |= UCSI_GET_PD_MESSAGE_TYPE(UCSI_GET_PD_MESSAGE_TYPE_IDENTITY);
+ ret = ucsi_get_pdos(con, role, is_partner, pd_caps.pdo);
+ if (ret <= 0)
+ return ERR_PTR(ret);
- ret = ucsi_send_command(ucsi, command, resp, bytes);
- if (ret < 0)
- dev_err(ucsi->dev, "UCSI_GET_PD_MESSAGE failed (%d)\n", ret);
+ if (ret < PDO_MAX_OBJECTS)
+ pd_caps.pdo[ret] = 0;
- return ret;
+ pd_caps.role = role;
+
+ return usb_power_delivery_register_capabilities(is_partner ? con->partner_pd : con->pd,
+ &pd_caps);
}
-static int ucsi_get_identity(struct ucsi_connector *con, u8 recipient,
- struct usb_pd_identity *id)
+static int ucsi_get_pd_message(struct ucsi_connector *con, u8 recipient,
+ size_t bytes, void *data, u8 type)
{
- struct ucsi *ucsi = con->ucsi;
- struct ucsi_pd_message_disc_id resp = {};
+ size_t len = min(bytes, UCSI_MAX_DATA_LENGTH(con->ucsi));
+ u64 command;
+ u8 offset;
int ret;
- if (ucsi->version < UCSI_VERSION_2_0) {
- /*
- * Before UCSI v2.0, MESSAGE_IN is 16 bytes which cannot fit
- * the 28 byte identity response including the VDM header.
- * First request the VDM header, ID Header VDO, Cert Stat VDO
- * and Product VDO.
- */
- ret = ucsi_read_identity(con, recipient, 0, 0x10, &resp);
- if (ret < 0)
- return ret;
-
+ for (offset = 0; offset < bytes; offset += len) {
+ len = min(len, bytes - offset);
- /* Then request Product Type VDO1 through Product Type VDO3. */
- ret = ucsi_read_identity(con, recipient, 0x10, 0xc,
- &resp.vdo[0]);
- if (ret < 0)
- return ret;
+ command = UCSI_COMMAND(UCSI_GET_PD_MESSAGE) | UCSI_CONNECTOR_NUMBER(con->num);
+ command |= UCSI_GET_PD_MESSAGE_RECIPIENT(recipient);
+ command |= UCSI_GET_PD_MESSAGE_OFFSET(offset);
+ command |= UCSI_GET_PD_MESSAGE_BYTES(len);
+ command |= UCSI_GET_PD_MESSAGE_TYPE(type);
- } else {
- /*
- * In UCSI v2.0 and after, MESSAGE_IN is large enough to request
- * the large enough to request the full Discover Identity
- * response at once.
- */
- ret = ucsi_read_identity(con, recipient, 0x0, 0x1c, &resp);
+ ret = ucsi_send_command(con->ucsi, command, data + offset, len);
if (ret < 0)
return ret;
}
- id->id_header = resp.id_header;
- id->cert_stat = resp.cert_stat;
- id->product = resp.product;
- id->vdo[0] = resp.vdo[0];
- id->vdo[1] = resp.vdo[1];
- id->vdo[2] = resp.vdo[2];
return 0;
}
static int ucsi_get_partner_identity(struct ucsi_connector *con)
{
+ u32 vdo[7] = {};
int ret;
- ret = ucsi_get_identity(con, UCSI_RECIPIENT_SOP,
- &con->partner_identity);
+ ret = ucsi_get_pd_message(con, UCSI_RECIPIENT_SOP, sizeof(vdo), vdo,
+ UCSI_GET_PD_MESSAGE_TYPE_IDENTITY);
if (ret < 0)
return ret;
+ /* VDM Header is not part of struct usb_pd_identity, so dropping it. */
+ con->partner_identity = *(struct usb_pd_identity *)&vdo[1];
+
ret = typec_partner_set_identity(con->partner);
- if (ret < 0) {
- dev_err(con->ucsi->dev, "Failed to set partner identity (%d)\n",
- ret);
- }
+ if (ret < 0)
+ dev_err(con->ucsi->dev, "Failed to set partner identity (%d)\n", ret);
return ret;
}
static int ucsi_get_cable_identity(struct ucsi_connector *con)
{
+ u32 vdo[7] = {};
int ret;
- ret = ucsi_get_identity(con, UCSI_RECIPIENT_SOP_P,
- &con->cable_identity);
+ ret = ucsi_get_pd_message(con, UCSI_RECIPIENT_SOP_P, sizeof(vdo), vdo,
+ UCSI_GET_PD_MESSAGE_TYPE_IDENTITY);
if (ret < 0)
return ret;
+ con->cable_identity = *(struct usb_pd_identity *)&vdo[1];
+
ret = typec_cable_set_identity(con->cable);
- if (ret < 0) {
- dev_err(con->ucsi->dev, "Failed to set cable identity (%d)\n",
- ret);
- }
+ if (ret < 0)
+ dev_err(con->ucsi->dev, "Failed to set cable identity (%d)\n", ret);
return ret;
}
@@ -789,69 +815,63 @@ static int ucsi_check_altmodes(struct ucsi_connector *con)
/* Ignoring the errors in this case. */
if (con->partner_altmode[0]) {
num_partner_am = ucsi_get_num_altmode(con->partner_altmode);
- if (num_partner_am > 0)
- typec_partner_set_num_altmodes(con->partner, num_partner_am);
+ typec_partner_set_num_altmodes(con->partner, num_partner_am);
ucsi_altmode_update_active(con);
return 0;
+ } else {
+ typec_partner_set_num_altmodes(con->partner, 0);
}
return ret;
}
+static void ucsi_register_device_pdos(struct ucsi_connector *con)
+{
+ struct ucsi *ucsi = con->ucsi;
+ struct usb_power_delivery_desc desc = { ucsi->cap.pd_version };
+ struct usb_power_delivery_capabilities *pd_cap;
+
+ if (con->pd)
+ return;
+
+ con->pd = usb_power_delivery_register(ucsi->dev, &desc);
+
+ pd_cap = ucsi_get_pd_caps(con, TYPEC_SOURCE, false);
+ if (!IS_ERR(pd_cap))
+ con->port_source_caps = pd_cap;
+
+ pd_cap = ucsi_get_pd_caps(con, TYPEC_SINK, false);
+ if (!IS_ERR(pd_cap))
+ con->port_sink_caps = pd_cap;
+
+ typec_port_set_usb_power_delivery(con->port, con->pd);
+}
+
static int ucsi_register_partner_pdos(struct ucsi_connector *con)
{
struct usb_power_delivery_desc desc = { con->ucsi->cap.pd_version };
- struct usb_power_delivery_capabilities_desc caps;
struct usb_power_delivery_capabilities *cap;
- int ret;
if (con->partner_pd)
return 0;
- con->partner_pd = usb_power_delivery_register(NULL, &desc);
+ con->partner_pd = typec_partner_usb_power_delivery_register(con->partner, &desc);
if (IS_ERR(con->partner_pd))
return PTR_ERR(con->partner_pd);
- ret = ucsi_get_pdos(con, TYPEC_SOURCE, 1, caps.pdo);
- if (ret > 0) {
- if (ret < PDO_MAX_OBJECTS)
- caps.pdo[ret] = 0;
-
- caps.role = TYPEC_SOURCE;
- cap = usb_power_delivery_register_capabilities(con->partner_pd, &caps);
- if (IS_ERR(cap))
- return PTR_ERR(cap);
-
- con->partner_source_caps = cap;
-
- ret = typec_partner_set_usb_power_delivery(con->partner, con->partner_pd);
- if (ret) {
- usb_power_delivery_unregister_capabilities(con->partner_source_caps);
- return ret;
- }
- }
-
- ret = ucsi_get_pdos(con, TYPEC_SINK, 1, caps.pdo);
- if (ret > 0) {
- if (ret < PDO_MAX_OBJECTS)
- caps.pdo[ret] = 0;
-
- caps.role = TYPEC_SINK;
+ cap = ucsi_get_pd_caps(con, TYPEC_SOURCE, true);
+ if (IS_ERR(cap))
+ return PTR_ERR(cap);
- cap = usb_power_delivery_register_capabilities(con->partner_pd, &caps);
- if (IS_ERR(cap))
- return PTR_ERR(cap);
+ con->partner_source_caps = cap;
- con->partner_sink_caps = cap;
+ cap = ucsi_get_pd_caps(con, TYPEC_SINK, true);
+ if (IS_ERR(cap))
+ return PTR_ERR(cap);
- ret = typec_partner_set_usb_power_delivery(con->partner, con->partner_pd);
- if (ret) {
- usb_power_delivery_unregister_capabilities(con->partner_sink_caps);
- return ret;
- }
- }
+ con->partner_sink_caps = cap;
- return 0;
+ return typec_partner_set_usb_power_delivery(con->partner, con->partner_pd);
}
static void ucsi_unregister_partner_pdos(struct ucsi_connector *con)
@@ -893,10 +913,20 @@ static void ucsi_unregister_plug(struct ucsi_connector *con)
static int ucsi_register_cable(struct ucsi_connector *con)
{
+ struct ucsi_cable_property cable_prop;
struct typec_cable *cable;
struct typec_cable_desc desc = {};
+ u64 command;
+ int ret;
+
+ command = UCSI_GET_CABLE_PROPERTY | UCSI_CONNECTOR_NUMBER(con->num);
+ ret = ucsi_send_command(con->ucsi, command, &cable_prop, sizeof(cable_prop));
+ if (ret < 0) {
+ dev_err(con->ucsi->dev, "GET_CABLE_PROPERTY failed (%d)\n", ret);
+ return ret;
+ }
- switch (UCSI_CABLE_PROP_FLAG_PLUG_TYPE(con->cable_prop.flags)) {
+ switch (UCSI_CABLE_PROP_FLAG_PLUG_TYPE(cable_prop.flags)) {
case UCSI_CABLE_PROPERTY_PLUG_TYPE_A:
desc.type = USB_PLUG_TYPE_A;
break;
@@ -911,11 +941,12 @@ static int ucsi_register_cable(struct ucsi_connector *con)
break;
}
- desc.identity = &con->cable_identity;
- desc.active = !!(UCSI_CABLE_PROP_FLAG_ACTIVE_CABLE &
- con->cable_prop.flags);
- desc.pd_revision = UCSI_CABLE_PROP_FLAG_PD_MAJOR_REV_AS_BCD(
- con->cable_prop.flags);
+ if (con->ucsi->cap.features & UCSI_CAP_GET_PD_MESSAGE)
+ desc.identity = &con->cable_identity;
+ desc.active = !!(UCSI_CABLE_PROP_FLAG_ACTIVE_CABLE & cable_prop.flags);
+
+ if (con->ucsi->version >= UCSI_VERSION_2_1)
+ desc.pd_revision = UCSI_CABLE_PROP_FLAG_PD_MAJOR_REV_AS_BCD(cable_prop.flags);
cable = typec_register_cable(con->port, &desc);
if (IS_ERR(cable)) {
@@ -940,6 +971,27 @@ static void ucsi_unregister_cable(struct ucsi_connector *con)
con->cable = NULL;
}
+static int ucsi_check_connector_capability(struct ucsi_connector *con)
+{
+ u64 command;
+ int ret;
+
+ if (!con->partner || con->ucsi->version < UCSI_VERSION_2_1)
+ return 0;
+
+ command = UCSI_GET_CONNECTOR_CAPABILITY | UCSI_CONNECTOR_NUMBER(con->num);
+ ret = ucsi_send_command(con->ucsi, command, &con->cap, sizeof(con->cap));
+ if (ret < 0) {
+ dev_err(con->ucsi->dev, "GET_CONNECTOR_CAPABILITY failed (%d)\n", ret);
+ return ret;
+ }
+
+ typec_partner_set_pd_revision(con->partner,
+ UCSI_CONCAP_FLAG_PARTNER_PD_MAJOR_REV_AS_BCD(con->cap.flags));
+
+ return ret;
+}
+
static void ucsi_pwr_opmode_change(struct ucsi_connector *con)
{
switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) {
@@ -947,8 +999,9 @@ static void ucsi_pwr_opmode_change(struct ucsi_connector *con)
con->rdo = con->status.request_data_obj;
typec_set_pwr_opmode(con->port, TYPEC_PWR_MODE_PD);
ucsi_partner_task(con, ucsi_get_src_pdos, 30, 0);
- ucsi_partner_task(con, ucsi_check_altmodes, 30, 0);
+ ucsi_partner_task(con, ucsi_check_altmodes, 30, HZ);
ucsi_partner_task(con, ucsi_register_partner_pdos, 1, HZ);
+ ucsi_partner_task(con, ucsi_check_connector_capability, 1, HZ);
break;
case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
con->rdo = 0;
@@ -987,9 +1040,12 @@ static int ucsi_register_partner(struct ucsi_connector *con)
break;
}
- desc.identity = &con->partner_identity;
+ if (pwr_opmode == UCSI_CONSTAT_PWR_OPMODE_PD)
+ ucsi_register_device_pdos(con);
+
+ if (con->ucsi->cap.features & UCSI_CAP_GET_PD_MESSAGE)
+ desc.identity = &con->partner_identity;
desc.usb_pd = pwr_opmode == UCSI_CONSTAT_PWR_OPMODE_PD;
- desc.pd_revision = UCSI_CONCAP_FLAG_PARTNER_PD_MAJOR_REV_AS_BCD(con->cap.flags);
partner = typec_register_partner(con->port, &desc);
if (IS_ERR(partner)) {
@@ -1066,27 +1122,6 @@ static void ucsi_partner_change(struct ucsi_connector *con)
con->num, u_role);
}
-static int ucsi_check_connector_capability(struct ucsi_connector *con)
-{
- u64 command;
- int ret;
-
- if (!con->partner || con->ucsi->version < UCSI_VERSION_2_0)
- return 0;
-
- command = UCSI_GET_CONNECTOR_CAPABILITY | UCSI_CONNECTOR_NUMBER(con->num);
- ret = ucsi_send_command(con->ucsi, command, &con->cap, sizeof(con->cap));
- if (ret < 0) {
- dev_err(con->ucsi->dev, "GET_CONNECTOR_CAPABILITY failed (%d)\n", ret);
- return ret;
- }
-
- typec_partner_set_pd_revision(con->partner,
- UCSI_CONCAP_FLAG_PARTNER_PD_MAJOR_REV_AS_BCD(con->cap.flags));
-
- return ret;
-}
-
static int ucsi_check_connection(struct ucsi_connector *con)
{
u8 prev_flags = con->status.flags;
@@ -1118,21 +1153,11 @@ static int ucsi_check_connection(struct ucsi_connector *con)
static int ucsi_check_cable(struct ucsi_connector *con)
{
- u64 command;
- int ret;
+ int ret, num_plug_am;
if (con->cable)
return 0;
- command = UCSI_GET_CABLE_PROPERTY | UCSI_CONNECTOR_NUMBER(con->num);
- ret = ucsi_send_command(con->ucsi, command, &con->cable_prop,
- sizeof(con->cable_prop));
- if (ret < 0) {
- dev_err(con->ucsi->dev, "GET_CABLE_PROPERTY failed (%d)\n",
- ret);
- return ret;
- }
-
ret = ucsi_register_cable(con);
if (ret < 0)
return ret;
@@ -1151,6 +1176,13 @@ static int ucsi_check_cable(struct ucsi_connector *con)
ret = ucsi_register_altmodes(con, UCSI_RECIPIENT_SOP_P);
if (ret < 0)
return ret;
+
+ if (con->plug_altmode[0]) {
+ num_plug_am = ucsi_get_num_altmode(con->plug_altmode);
+ typec_plug_set_num_altmodes(con->plug, num_plug_am);
+ } else {
+ typec_plug_set_num_altmodes(con->plug, 0);
+ }
}
return 0;
@@ -1167,8 +1199,14 @@ static void ucsi_handle_connector_change(struct work_struct *work)
mutex_lock(&con->lock);
+ if (!test_and_set_bit(EVENT_PENDING, &ucsi->flags))
+ dev_err_once(ucsi->dev, "%s entered without EVENT_PENDING\n",
+ __func__);
+
command = UCSI_GET_CONNECTOR_STATUS | UCSI_CONNECTOR_NUMBER(con->num);
- ret = ucsi_send_command(ucsi, command, &con->status, sizeof(con->status));
+
+ ret = ucsi_send_command_common(ucsi, command, &con->status,
+ sizeof(con->status), true);
if (ret < 0) {
dev_err(ucsi->dev, "%s: GET_CONNECTOR_STATUS failed (%d)\n",
__func__, ret);
@@ -1178,6 +1216,9 @@ static void ucsi_handle_connector_change(struct work_struct *work)
trace_ucsi_connector_change(con->num, &con->status);
+ if (ucsi->ops->connector_status)
+ ucsi->ops->connector_status(con);
+
role = !!(con->status.flags & UCSI_CONSTAT_PWR_DIR);
if (con->status.change & UCSI_CONSTAT_POWER_DIR_CHANGE) {
@@ -1196,15 +1237,16 @@ static void ucsi_handle_connector_change(struct work_struct *work)
if (con->status.flags & UCSI_CONSTAT_CONNECTED) {
ucsi_register_partner(con);
ucsi_partner_task(con, ucsi_check_connection, 1, HZ);
- ucsi_partner_task(con, ucsi_check_connector_capability, 1, HZ);
if (con->ucsi->cap.features & UCSI_CAP_GET_PD_MESSAGE)
ucsi_partner_task(con, ucsi_get_partner_identity, 1, HZ);
if (con->ucsi->cap.features & UCSI_CAP_CABLE_DETAILS)
ucsi_partner_task(con, ucsi_check_cable, 1, HZ);
if (UCSI_CONSTAT_PWR_OPMODE(con->status.flags) ==
- UCSI_CONSTAT_PWR_OPMODE_PD)
+ UCSI_CONSTAT_PWR_OPMODE_PD) {
ucsi_partner_task(con, ucsi_register_partner_pdos, 1, HZ);
+ ucsi_partner_task(con, ucsi_check_connector_capability, 1, HZ);
+ }
} else {
ucsi_unregister_partner(con);
}
@@ -1223,15 +1265,10 @@ static void ucsi_handle_connector_change(struct work_struct *work)
}
if (con->status.change & UCSI_CONSTAT_CAM_CHANGE)
- ucsi_partner_task(con, ucsi_check_altmodes, 1, 0);
-
- mutex_lock(&ucsi->ppm_lock);
- clear_bit(EVENT_PENDING, &con->ucsi->flags);
- ret = ucsi_acknowledge_connector_change(ucsi);
- mutex_unlock(&ucsi->ppm_lock);
+ ucsi_partner_task(con, ucsi_check_altmodes, 1, HZ);
- if (ret)
- dev_err(ucsi->dev, "%s: ACK failed (%d)", __func__, ret);
+ if (con->status.change & UCSI_CONSTAT_BC_CHANGE)
+ ucsi_port_psy_changed(con);
out_unlock:
mutex_unlock(&con->lock);
@@ -1258,12 +1295,26 @@ EXPORT_SYMBOL_GPL(ucsi_connector_change);
/* -------------------------------------------------------------------------- */
+/*
+ * Hard Reset bit field was defined with value 1 in UCSI spec version 1.0.
+ * Starting with spec version 1.1, Hard Reset bit field was removed from the
+ * CONNECTOR_RESET command, until spec 2.0 reintroduced it with value 0, so, in effect,
+ * the value to pass in to the command for a Hard Reset is different depending
+ * on the supported UCSI version by the LPM.
+ *
+ * For performing a Data Reset on LPMs supporting version 2.0 and greater,
+ * this function needs to be called with the second argument set to 0.
+ */
static int ucsi_reset_connector(struct ucsi_connector *con, bool hard)
{
u64 command;
command = UCSI_CONNECTOR_RESET | UCSI_CONNECTOR_NUMBER(con->num);
- command |= hard ? UCSI_CONNECTOR_RESET_HARD : 0;
+
+ if (con->ucsi->version < UCSI_VERSION_1_1)
+ command |= hard ? UCSI_CONNECTOR_RESET_HARD_VER_1_0 : 0;
+ else if (con->ucsi->version >= UCSI_VERSION_2_0)
+ command |= hard ? 0 : UCSI_CONNECTOR_RESET_DATA_VER_2_0;
return ucsi_send_command(con->ucsi, command, NULL, 0);
}
@@ -1277,7 +1328,7 @@ static int ucsi_reset_ppm(struct ucsi *ucsi)
mutex_lock(&ucsi->ppm_lock);
- ret = ucsi->ops->read(ucsi, UCSI_CCI, &cci, sizeof(cci));
+ ret = ucsi->ops->read_cci(ucsi, &cci);
if (ret < 0)
goto out;
@@ -1289,15 +1340,13 @@ static int ucsi_reset_ppm(struct ucsi *ucsi)
*/
if (cci & UCSI_CCI_RESET_COMPLETE) {
command = UCSI_SET_NOTIFICATION_ENABLE;
- ret = ucsi->ops->async_write(ucsi, UCSI_CONTROL, &command,
- sizeof(command));
+ ret = ucsi->ops->async_control(ucsi, command);
if (ret < 0)
goto out;
tmo = jiffies + msecs_to_jiffies(UCSI_TIMEOUT_MS);
do {
- ret = ucsi->ops->read(ucsi, UCSI_CCI,
- &cci, sizeof(cci));
+ ret = ucsi->ops->read_cci(ucsi, &cci);
if (ret < 0)
goto out;
if (cci & UCSI_CCI_COMMAND_COMPLETE)
@@ -1311,8 +1360,7 @@ static int ucsi_reset_ppm(struct ucsi *ucsi)
}
command = UCSI_PPM_RESET;
- ret = ucsi->ops->async_write(ucsi, UCSI_CONTROL, &command,
- sizeof(command));
+ ret = ucsi->ops->async_control(ucsi, command);
if (ret < 0)
goto out;
@@ -1324,20 +1372,20 @@ static int ucsi_reset_ppm(struct ucsi *ucsi)
goto out;
}
- ret = ucsi->ops->read(ucsi, UCSI_CCI, &cci, sizeof(cci));
+ /* Give the PPM time to process a reset before reading CCI */
+ msleep(20);
+
+ ret = ucsi->ops->read_cci(ucsi, &cci);
if (ret)
goto out;
/* If the PPM is still doing something else, reset it again. */
if (cci & ~UCSI_CCI_RESET_COMPLETE) {
- ret = ucsi->ops->async_write(ucsi, UCSI_CONTROL,
- &command,
- sizeof(command));
+ ret = ucsi->ops->async_control(ucsi, command);
if (ret < 0)
goto out;
}
- msleep(20);
} while (!(cci & UCSI_CCI_RESET_COMPLETE));
out:
@@ -1477,9 +1525,6 @@ static struct fwnode_handle *ucsi_find_fwnode(struct ucsi_connector *con)
static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con)
{
- struct usb_power_delivery_desc desc = { ucsi->cap.pd_version};
- struct usb_power_delivery_capabilities_desc pd_caps;
- struct usb_power_delivery_capabilities *pd_cap;
struct typec_capability *cap = &con->typec_cap;
enum typec_accessory *accessory = cap->accessory;
enum usb_role u_role = USB_ROLE_NONE;
@@ -1546,6 +1591,9 @@ static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con)
cap->driver_data = con;
cap->ops = &ucsi_ops;
+ if (ucsi->ops->update_connector)
+ ucsi->ops->update_connector(con);
+
ret = ucsi_register_port_psy(con);
if (ret)
goto out;
@@ -1557,40 +1605,8 @@ static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con)
goto out;
}
- con->pd = usb_power_delivery_register(ucsi->dev, &desc);
-
- ret = ucsi_get_pdos(con, TYPEC_SOURCE, 0, pd_caps.pdo);
- if (ret > 0) {
- if (ret < PDO_MAX_OBJECTS)
- pd_caps.pdo[ret] = 0;
-
- pd_caps.role = TYPEC_SOURCE;
- pd_cap = usb_power_delivery_register_capabilities(con->pd, &pd_caps);
- if (IS_ERR(pd_cap)) {
- ret = PTR_ERR(pd_cap);
- goto out;
- }
-
- con->port_source_caps = pd_cap;
- typec_port_set_usb_power_delivery(con->port, con->pd);
- }
-
- memset(&pd_caps, 0, sizeof(pd_caps));
- ret = ucsi_get_pdos(con, TYPEC_SINK, 0, pd_caps.pdo);
- if (ret > 0) {
- if (ret < PDO_MAX_OBJECTS)
- pd_caps.pdo[ret] = 0;
-
- pd_caps.role = TYPEC_SINK;
- pd_cap = usb_power_delivery_register_capabilities(con->pd, &pd_caps);
- if (IS_ERR(pd_cap)) {
- ret = PTR_ERR(pd_cap);
- goto out;
- }
-
- con->port_sink_caps = pd_cap;
- typec_port_set_usb_power_delivery(con->port, con->pd);
- }
+ if (!(ucsi->quirks & UCSI_DELAY_DEVICE_PDOS))
+ ucsi_register_device_pdos(con);
/* Alternate modes */
ret = ucsi_register_altmodes(con, UCSI_RECIPIENT_CON);
@@ -1610,6 +1626,9 @@ static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con)
}
ret = 0; /* ucsi_send_command() returns length on success */
+ if (ucsi->ops->connector_status)
+ ucsi->ops->connector_status(con);
+
switch (UCSI_CONSTAT_PARTNER_TYPE(con->status.flags)) {
case UCSI_CONSTAT_PARTNER_TYPE_UFP:
case UCSI_CONSTAT_PARTNER_TYPE_CABLE_AND_UFP:
@@ -1653,8 +1672,10 @@ static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con)
if (con->partner &&
UCSI_CONSTAT_PWR_OPMODE(con->status.flags) ==
UCSI_CONSTAT_PWR_OPMODE_PD) {
+ ucsi_register_device_pdos(con);
ucsi_get_src_pdos(con);
ucsi_check_altmodes(con);
+ ucsi_check_connector_capability(con);
}
trace_ucsi_register_port(con->num, &con->status);
@@ -1672,6 +1693,44 @@ out_unlock:
return ret;
}
+static u64 ucsi_get_supported_notifications(struct ucsi *ucsi)
+{
+ u16 features = ucsi->cap.features;
+ u64 ntfy = UCSI_ENABLE_NTFY_ALL;
+
+ if (!(features & UCSI_CAP_ALT_MODE_DETAILS))
+ ntfy &= ~UCSI_ENABLE_NTFY_CAM_CHANGE;
+
+ if (!(features & UCSI_CAP_PDO_DETAILS))
+ ntfy &= ~(UCSI_ENABLE_NTFY_PWR_LEVEL_CHANGE |
+ UCSI_ENABLE_NTFY_CAP_CHANGE);
+
+ if (!(features & UCSI_CAP_EXT_SUPPLY_NOTIFICATIONS))
+ ntfy &= ~UCSI_ENABLE_NTFY_EXT_PWR_SRC_CHANGE;
+
+ if (!(features & UCSI_CAP_PD_RESET))
+ ntfy &= ~UCSI_ENABLE_NTFY_PD_RESET_COMPLETE;
+
+ if (ucsi->version <= UCSI_VERSION_1_2)
+ return ntfy;
+
+ ntfy |= UCSI_ENABLE_NTFY_SINK_PATH_STS_CHANGE;
+
+ if (features & UCSI_CAP_GET_ATTENTION_VDO)
+ ntfy |= UCSI_ENABLE_NTFY_ATTENTION;
+
+ if (features & UCSI_CAP_FW_UPDATE_REQUEST)
+ ntfy |= UCSI_ENABLE_NTFY_LPM_FW_UPDATE_REQ;
+
+ if (features & UCSI_CAP_SECURITY_REQUEST)
+ ntfy |= UCSI_ENABLE_NTFY_SECURITY_REQ_PARTNER;
+
+ if (features & UCSI_CAP_SET_RETIMER_MODE)
+ ntfy |= UCSI_ENABLE_NTFY_SET_RETIMER_MODE;
+
+ return ntfy;
+}
+
/**
* ucsi_init - Initialize UCSI interface
* @ucsi: UCSI to be initialized
@@ -1726,8 +1785,8 @@ static int ucsi_init(struct ucsi *ucsi)
goto err_unregister;
}
- /* Enable all notifications */
- ntfy = UCSI_ENABLE_NTFY_ALL;
+ /* Enable all supported notifications */
+ ntfy = ucsi_get_supported_notifications(ucsi);
command = UCSI_SET_NOTIFICATION_ENABLE | ntfy;
ret = ucsi_send_command(ucsi, command, NULL, 0);
if (ret < 0)
@@ -1737,7 +1796,7 @@ static int ucsi_init(struct ucsi *ucsi)
ucsi->ntfy = ntfy;
mutex_lock(&ucsi->ppm_lock);
- ret = ucsi->ops->read(ucsi, UCSI_CCI, &cci, sizeof(cci));
+ ret = ucsi->ops->read_cci(ucsi, &cci);
mutex_unlock(&ucsi->ppm_lock);
if (ret)
return ret;
@@ -1851,7 +1910,9 @@ struct ucsi *ucsi_create(struct device *dev, const struct ucsi_operations *ops)
{
struct ucsi *ucsi;
- if (!ops || !ops->read || !ops->sync_write || !ops->async_write)
+ if (!ops ||
+ !ops->read_version || !ops->read_cci || !ops->read_message_in ||
+ !ops->sync_control || !ops->async_control)
return ERR_PTR(-EINVAL);
ucsi = kzalloc(sizeof(*ucsi), GFP_KERNEL);
@@ -1861,6 +1922,7 @@ struct ucsi *ucsi_create(struct device *dev, const struct ucsi_operations *ops)
INIT_WORK(&ucsi->resume_work, ucsi_resume_work);
INIT_DELAYED_WORK(&ucsi->work, ucsi_init_work);
mutex_init(&ucsi->ppm_lock);
+ init_completion(&ucsi->complete);
ucsi->dev = dev;
ucsi->ops = ops;
@@ -1887,8 +1949,7 @@ int ucsi_register(struct ucsi *ucsi)
{
int ret;
- ret = ucsi->ops->read(ucsi, UCSI_VERSION, &ucsi->version,
- sizeof(ucsi->version));
+ ret = ucsi->ops->read_version(ucsi, &ucsi->version);
if (ret)
return ret;
@@ -1927,7 +1988,7 @@ void ucsi_unregister(struct ucsi *ucsi)
cancel_work_sync(&ucsi->resume_work);
/* Disable notifications */
- ucsi->ops->async_write(ucsi, UCSI_CONTROL, &cmd, sizeof(cmd));
+ ucsi->ops->async_control(ucsi, cmd);
if (!ucsi->connector)
return;
diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h
index 0e7c92eb1b22..1cf5aad4c23a 100644
--- a/drivers/usb/typec/ucsi/ucsi.h
+++ b/drivers/usb/typec/ucsi/ucsi.h
@@ -4,18 +4,20 @@
#define __DRIVER_USB_TYPEC_UCSI_H
#include <linux/bitops.h>
+#include <linux/completion.h>
#include <linux/device.h>
#include <linux/power_supply.h>
#include <linux/types.h>
#include <linux/usb/typec.h>
#include <linux/usb/pd.h>
#include <linux/usb/role.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
/* -------------------------------------------------------------------------- */
struct ucsi;
struct ucsi_altmode;
+struct ucsi_connector;
struct dentry;
/* UCSI offsets (Bytes) */
@@ -27,6 +29,7 @@ struct dentry;
#define UCSIv2_MESSAGE_OUT 272
/* UCSI versions */
+#define UCSI_VERSION_1_1 0x0110
#define UCSI_VERSION_1_2 0x0120
#define UCSI_VERSION_2_0 0x0200
#define UCSI_VERSION_2_1 0x0210
@@ -55,24 +58,29 @@ struct dentry;
/**
* struct ucsi_operations - UCSI I/O operations
- * @read: Read operation
- * @sync_write: Blocking write operation
- * @async_write: Non-blocking write operation
+ * @read_version: Read implemented UCSI version
+ * @read_cci: Read CCI register
+ * @read_message_in: Read message data from UCSI
+ * @sync_control: Blocking control operation
+ * @async_control: Non-blocking control operation
* @update_altmodes: Squashes duplicate DP altmodes
+ * @update_connector: Update connector capabilities before registering
+ * @connector_status: Updates connector status, called holding connector lock
*
* Read and write routines for UCSI interface. @sync_write must wait for the
* Command Completion Event from the PPM before returning, and @async_write must
* return immediately after sending the data to the PPM.
*/
struct ucsi_operations {
- int (*read)(struct ucsi *ucsi, unsigned int offset,
- void *val, size_t val_len);
- int (*sync_write)(struct ucsi *ucsi, unsigned int offset,
- const void *val, size_t val_len);
- int (*async_write)(struct ucsi *ucsi, unsigned int offset,
- const void *val, size_t val_len);
+ int (*read_version)(struct ucsi *ucsi, u16 *version);
+ int (*read_cci)(struct ucsi *ucsi, u32 *cci);
+ int (*read_message_in)(struct ucsi *ucsi, void *val, size_t val_len);
+ int (*sync_control)(struct ucsi *ucsi, u64 command);
+ int (*async_control)(struct ucsi *ucsi, u64 command);
bool (*update_altmodes)(struct ucsi *ucsi, struct ucsi_altmode *orig,
struct ucsi_altmode *updated);
+ void (*update_connector)(struct ucsi_connector *con);
+ void (*connector_status)(struct ucsi_connector *con);
};
struct ucsi *ucsi_create(struct device *dev, const struct ucsi_operations *ops);
@@ -111,26 +119,36 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num);
#define UCSI_CONNECTOR_NUMBER(_num_) ((u64)(_num_) << 16)
#define UCSI_COMMAND(_cmd_) ((_cmd_) & 0xff)
+#define UCSI_GET_ALTMODE_GET_CONNECTOR_NUMBER(_cmd_) (((_cmd_) >> 24) & GENMASK(6, 0))
+#define UCSI_DEFAULT_GET_CONNECTOR_NUMBER(_cmd_) (((_cmd_) >> 16) & GENMASK(6, 0))
+
/* CONNECTOR_RESET command bits */
-#define UCSI_CONNECTOR_RESET_HARD BIT(23) /* Deprecated in v1.1 */
+#define UCSI_CONNECTOR_RESET_HARD_VER_1_0 BIT(23) /* Deprecated in v1.1 */
+#define UCSI_CONNECTOR_RESET_DATA_VER_2_0 BIT(23) /* Redefined in v2.0 */
+
/* ACK_CC_CI bits */
#define UCSI_ACK_CONNECTOR_CHANGE BIT(16)
#define UCSI_ACK_COMMAND_COMPLETE BIT(17)
/* SET_NOTIFICATION_ENABLE command bits */
-#define UCSI_ENABLE_NTFY_CMD_COMPLETE BIT(16)
-#define UCSI_ENABLE_NTFY_EXT_PWR_SRC_CHANGE BIT(17)
-#define UCSI_ENABLE_NTFY_PWR_OPMODE_CHANGE BIT(18)
-#define UCSI_ENABLE_NTFY_CAP_CHANGE BIT(21)
-#define UCSI_ENABLE_NTFY_PWR_LEVEL_CHANGE BIT(22)
-#define UCSI_ENABLE_NTFY_PD_RESET_COMPLETE BIT(23)
-#define UCSI_ENABLE_NTFY_CAM_CHANGE BIT(24)
-#define UCSI_ENABLE_NTFY_BAT_STATUS_CHANGE BIT(25)
-#define UCSI_ENABLE_NTFY_PARTNER_CHANGE BIT(27)
-#define UCSI_ENABLE_NTFY_PWR_DIR_CHANGE BIT(28)
-#define UCSI_ENABLE_NTFY_CONNECTOR_CHANGE BIT(30)
-#define UCSI_ENABLE_NTFY_ERROR BIT(31)
+#define UCSI_ENABLE_NTFY_CMD_COMPLETE BIT_ULL(16)
+#define UCSI_ENABLE_NTFY_EXT_PWR_SRC_CHANGE BIT_ULL(17)
+#define UCSI_ENABLE_NTFY_PWR_OPMODE_CHANGE BIT_ULL(18)
+#define UCSI_ENABLE_NTFY_ATTENTION BIT_ULL(19)
+#define UCSI_ENABLE_NTFY_LPM_FW_UPDATE_REQ BIT_ULL(20)
+#define UCSI_ENABLE_NTFY_CAP_CHANGE BIT_ULL(21)
+#define UCSI_ENABLE_NTFY_PWR_LEVEL_CHANGE BIT_ULL(22)
+#define UCSI_ENABLE_NTFY_PD_RESET_COMPLETE BIT_ULL(23)
+#define UCSI_ENABLE_NTFY_CAM_CHANGE BIT_ULL(24)
+#define UCSI_ENABLE_NTFY_BAT_STATUS_CHANGE BIT_ULL(25)
+#define UCSI_ENABLE_NTFY_SECURITY_REQ_PARTNER BIT_ULL(26)
+#define UCSI_ENABLE_NTFY_PARTNER_CHANGE BIT_ULL(27)
+#define UCSI_ENABLE_NTFY_PWR_DIR_CHANGE BIT_ULL(28)
+#define UCSI_ENABLE_NTFY_SET_RETIMER_MODE BIT_ULL(29)
+#define UCSI_ENABLE_NTFY_CONNECTOR_CHANGE BIT_ULL(30)
+#define UCSI_ENABLE_NTFY_ERROR BIT_ULL(31)
+#define UCSI_ENABLE_NTFY_SINK_PATH_STS_CHANGE BIT_ULL(32)
#define UCSI_ENABLE_NTFY_ALL 0xdbe70000
/* SET_UOR command bits */
@@ -188,6 +206,8 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num);
#define UCSI_ERROR_HARD_RESET BIT(10)
#define UCSI_ERROR_PPM_POLICY_CONFLICT BIT(11)
#define UCSI_ERROR_SWAP_REJECTED BIT(12)
+#define UCSI_ERROR_REVERSE_CURRENT_PROTECTION BIT(13)
+#define UCSI_ERROR_SET_SINK_PATH_REJECTED BIT(14)
#define UCSI_SET_NEW_CAM_ENTER(x) (((x) >> 23) & 0x1)
#define UCSI_SET_NEW_CAM_GET_AM(x) (((x) >> 24) & 0xff)
@@ -215,7 +235,13 @@ struct ucsi_capability {
#define UCSI_CAP_CABLE_DETAILS BIT(5)
#define UCSI_CAP_EXT_SUPPLY_NOTIFICATIONS BIT(6)
#define UCSI_CAP_PD_RESET BIT(7)
-#define UCSI_CAP_GET_PD_MESSAGE BIT(8)
+#define UCSI_CAP_GET_PD_MESSAGE BIT(8)
+#define UCSI_CAP_GET_ATTENTION_VDO BIT(9)
+#define UCSI_CAP_FW_UPDATE_REQUEST BIT(10)
+#define UCSI_CAP_NEGOTIATED_PWR_LEVEL_CHANGE BIT(11)
+#define UCSI_CAP_SECURITY_REQUEST BIT(12)
+#define UCSI_CAP_SET_RETIMER_MODE BIT(13)
+#define UCSI_CAP_CHUNKING_SUPPORT BIT(14)
u8 reserved_1;
u8 num_alt_modes;
u8 reserved_2;
@@ -321,47 +347,12 @@ struct ucsi_connector_status {
#define UCSI_CONSTAT_PARTNER_TYPE_AUDIO 6
u32 request_data_obj;
- u8 pwr_status[3];
-#define UCSI_CONSTAT_BC_STATUS(_p_) ((_p_[0]) & GENMASK(1, 0))
+ u8 pwr_status;
+#define UCSI_CONSTAT_BC_STATUS(_p_) ((_p_) & GENMASK(1, 0))
#define UCSI_CONSTAT_BC_NOT_CHARGING 0
#define UCSI_CONSTAT_BC_NOMINAL_CHARGING 1
#define UCSI_CONSTAT_BC_SLOW_CHARGING 2
#define UCSI_CONSTAT_BC_TRICKLE_CHARGING 3
-#define UCSI_CONSTAT_PROVIDER_CAP_LIMIT(_p_) (((_p_[0]) & GENMASK(5, 2)) >> 2)
-#define UCSI_CONSTAT_CAP_PWR_LOWERED 0
-#define UCSI_CONSTAT_CAP_PWR_BUDGET_LIMIT 1
-#define UCSI_CONSTAT_PROVIDER_PD_VERSION_OPER_MODE(_p_) \
- ((get_unaligned_le32(_p_) & GENMASK(21, 6)) >> 6)
-#define UCSI_CONSTAT_ORIENTATION(_p_) (((_p_[2]) & GENMASK(6, 6)) >> 6)
-#define UCSI_CONSTAT_ORIENTATION_DIRECT 0
-#define UCSI_CONSTAT_ORIENTATION_FLIPPED 1
-#define UCSI_CONSTAT_SINK_PATH_STATUS(_p_) (((_p_[2]) & GENMASK(7, 7)) >> 7)
-#define UCSI_CONSTAT_SINK_PATH_DISABLED 0
-#define UCSI_CONSTAT_SINK_PATH_ENABLED 1
- u8 pwr_readings[9];
-#define UCSI_CONSTAT_REV_CURR_PROT_STATUS(_p_) ((_p_[0]) & 0x1)
-#define UCSI_CONSTAT_PWR_READING_VALID(_p_) (((_p_[0]) & GENMASK(1, 1)) >> 1)
-#define UCSI_CONSTAT_CURRENT_SCALE(_p_) (((_p_[0]) & GENMASK(4, 2)) >> 2)
-#define UCSI_CONSTAT_PEAK_CURRENT(_p_) \
- ((get_unaligned_le32(_p_) & GENMASK(20, 5)) >> 5)
-#define UCSI_CONSTAT_AVG_CURRENT(_p_) \
- ((get_unaligned_le32(&(_p_)[2]) & GENMASK(20, 5)) >> 5)
-#define UCSI_CONSTAT_VOLTAGE_SCALE(_p_) \
- ((get_unaligned_le16(&(_p_)[4]) & GENMASK(8, 5)) >> 5)
-#define UCSI_CONSTAT_VOLTAGE_READING(_p_) \
- ((get_unaligned_le32(&(_p_)[5]) & GENMASK(16, 1)) >> 1)
-} __packed;
-
-/*
- * Data structure filled by PPM in response to GET_PD_MESSAGE command with the
- * Response Message Type set to Discover Identity Response.
- */
-struct ucsi_pd_message_disc_id {
- u32 vdm_header;
- u32 id_header;
- u32 cert_stat;
- u32 product;
- u32 vdo[3];
} __packed;
/* -------------------------------------------------------------------------- */
@@ -379,7 +370,7 @@ struct ucsi_debugfs_entry {
struct ucsi {
u16 version;
struct device *dev;
- struct driver_data *driver_data;
+ void *driver_data;
const struct ucsi_operations *ops;
@@ -405,11 +396,15 @@ struct ucsi {
#define EVENT_PENDING 0
#define COMMAND_PENDING 1
#define ACK_PENDING 2
+ struct completion complete;
unsigned long quirks;
#define UCSI_NO_PARTNER_PDOS BIT(0) /* Don't read partner's PDOs */
+#define UCSI_DELAY_DEVICE_PDOS BIT(1) /* Reading PDOs fails until the parter is in PD mode */
};
+#define UCSI_MAX_DATA_LENGTH(u) (((u)->version < UCSI_VERSION_2_0) ? 0x10 : 0xff)
+
#define UCSI_MAX_SVID 5
#define UCSI_MAX_ALTMODES (UCSI_MAX_SVID * 6)
@@ -440,7 +435,6 @@ struct ucsi_connector {
struct ucsi_connector_status status;
struct ucsi_connector_capability cap;
- struct ucsi_cable_property cable_prop;
struct power_supply *psy;
struct power_supply_desc psy_desc;
u32 rdo;
@@ -468,6 +462,9 @@ int ucsi_send_command(struct ucsi *ucsi, u64 command,
void ucsi_altmode_update_active(struct ucsi_connector *con);
int ucsi_resume(struct ucsi *ucsi);
+void ucsi_notify_common(struct ucsi *ucsi, u32 cci);
+int ucsi_sync_control_common(struct ucsi *ucsi, u64 command);
+
#if IS_ENABLED(CONFIG_POWER_SUPPLY)
int ucsi_register_port_psy(struct ucsi_connector *con);
void ucsi_unregister_port_psy(struct ucsi_connector *con);
@@ -492,7 +489,7 @@ ucsi_register_displayport(struct ucsi_connector *con,
bool override, int offset,
struct typec_altmode_desc *desc)
{
- return NULL;
+ return typec_port_register_altmode(con->port, desc);
}
static inline void
diff --git a/drivers/usb/typec/ucsi/ucsi_acpi.c b/drivers/usb/typec/ucsi/ucsi_acpi.c
index 7b3ac133ef86..7a5dff8d9cc6 100644
--- a/drivers/usb/typec/ucsi/ucsi_acpi.c
+++ b/drivers/usb/typec/ucsi/ucsi_acpi.c
@@ -21,11 +21,7 @@ struct ucsi_acpi {
struct device *dev;
struct ucsi *ucsi;
void *base;
- struct completion complete;
- unsigned long flags;
-#define UCSI_ACPI_SUPPRESS_EVENT 0
-#define UCSI_ACPI_COMMAND_PENDING 1
-#define UCSI_ACPI_ACK_PENDING 2
+ bool check_bogus_event;
guid_t guid;
u64 cmd;
};
@@ -46,8 +42,7 @@ static int ucsi_acpi_dsm(struct ucsi_acpi *ua, int func)
return 0;
}
-static int ucsi_acpi_read(struct ucsi *ucsi, unsigned int offset,
- void *val, size_t val_len)
+static int ucsi_acpi_read_version(struct ucsi *ucsi, u16 *version)
{
struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
int ret;
@@ -56,120 +51,142 @@ static int ucsi_acpi_read(struct ucsi *ucsi, unsigned int offset,
if (ret)
return ret;
- memcpy(val, ua->base + offset, val_len);
+ memcpy(version, ua->base + UCSI_VERSION, sizeof(*version));
return 0;
}
-static int ucsi_acpi_async_write(struct ucsi *ucsi, unsigned int offset,
- const void *val, size_t val_len)
+static int ucsi_acpi_read_cci(struct ucsi *ucsi, u32 *cci)
{
struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
+ int ret;
+
+ ret = ucsi_acpi_dsm(ua, UCSI_DSM_FUNC_READ);
+ if (ret)
+ return ret;
- memcpy(ua->base + offset, val, val_len);
- ua->cmd = *(u64 *)val;
+ memcpy(cci, ua->base + UCSI_CCI, sizeof(*cci));
- return ucsi_acpi_dsm(ua, UCSI_DSM_FUNC_WRITE);
+ return 0;
}
-static int ucsi_acpi_sync_write(struct ucsi *ucsi, unsigned int offset,
- const void *val, size_t val_len)
+static int ucsi_acpi_read_message_in(struct ucsi *ucsi, void *val, size_t val_len)
{
struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
- bool ack = UCSI_COMMAND(*(u64 *)val) == UCSI_ACK_CC_CI;
int ret;
- if (ack)
- set_bit(UCSI_ACPI_ACK_PENDING, &ua->flags);
- else
- set_bit(UCSI_ACPI_COMMAND_PENDING, &ua->flags);
-
- ret = ucsi_acpi_async_write(ucsi, offset, val, val_len);
+ ret = ucsi_acpi_dsm(ua, UCSI_DSM_FUNC_READ);
if (ret)
- goto out_clear_bit;
+ return ret;
- if (!wait_for_completion_timeout(&ua->complete, 5 * HZ))
- ret = -ETIMEDOUT;
+ memcpy(val, ua->base + UCSI_MESSAGE_IN, val_len);
-out_clear_bit:
- if (ack)
- clear_bit(UCSI_ACPI_ACK_PENDING, &ua->flags);
- else
- clear_bit(UCSI_ACPI_COMMAND_PENDING, &ua->flags);
+ return 0;
+}
- return ret;
+static int ucsi_acpi_async_control(struct ucsi *ucsi, u64 command)
+{
+ struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
+
+ memcpy(ua->base + UCSI_CONTROL, &command, sizeof(command));
+ ua->cmd = command;
+
+ return ucsi_acpi_dsm(ua, UCSI_DSM_FUNC_WRITE);
}
static const struct ucsi_operations ucsi_acpi_ops = {
- .read = ucsi_acpi_read,
- .sync_write = ucsi_acpi_sync_write,
- .async_write = ucsi_acpi_async_write
+ .read_version = ucsi_acpi_read_version,
+ .read_cci = ucsi_acpi_read_cci,
+ .read_message_in = ucsi_acpi_read_message_in,
+ .sync_control = ucsi_sync_control_common,
+ .async_control = ucsi_acpi_async_control
};
static int
-ucsi_zenbook_read(struct ucsi *ucsi, unsigned int offset, void *val, size_t val_len)
+ucsi_zenbook_read_cci(struct ucsi *ucsi, u32 *cci)
{
struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
int ret;
- if (offset == UCSI_VERSION || UCSI_COMMAND(ua->cmd) == UCSI_PPM_RESET) {
+ if (UCSI_COMMAND(ua->cmd) == UCSI_PPM_RESET) {
ret = ucsi_acpi_dsm(ua, UCSI_DSM_FUNC_READ);
if (ret)
return ret;
}
- memcpy(val, ua->base + offset, val_len);
+ memcpy(cci, ua->base + UCSI_CCI, sizeof(*cci));
+
+ return 0;
+}
+
+static int
+ucsi_zenbook_read_message_in(struct ucsi *ucsi, void *val, size_t val_len)
+{
+ struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
+
+ /* UCSI_MESSAGE_IN is never read for PPM_RESET, return stored data */
+ memcpy(val, ua->base + UCSI_MESSAGE_IN, val_len);
return 0;
}
static const struct ucsi_operations ucsi_zenbook_ops = {
- .read = ucsi_zenbook_read,
- .sync_write = ucsi_acpi_sync_write,
- .async_write = ucsi_acpi_async_write
+ .read_version = ucsi_acpi_read_version,
+ .read_cci = ucsi_zenbook_read_cci,
+ .read_message_in = ucsi_zenbook_read_message_in,
+ .sync_control = ucsi_sync_control_common,
+ .async_control = ucsi_acpi_async_control
};
-/*
- * Some Dell laptops don't like ACK commands with the
- * UCSI_ACK_CONNECTOR_CHANGE but not the UCSI_ACK_COMMAND_COMPLETE
- * bit set. To work around this send a dummy command and bundle the
- * UCSI_ACK_CONNECTOR_CHANGE with the UCSI_ACK_COMMAND_COMPLETE
- * for the dummy command.
- */
-static int
-ucsi_dell_sync_write(struct ucsi *ucsi, unsigned int offset,
- const void *val, size_t val_len)
+static int ucsi_gram_read_message_in(struct ucsi *ucsi, void *val, size_t val_len)
{
+ u16 bogus_change = UCSI_CONSTAT_POWER_LEVEL_CHANGE |
+ UCSI_CONSTAT_PDOS_CHANGE;
struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
- u64 cmd = *(u64 *)val;
- u64 dummycmd = UCSI_GET_CAPABILITY;
+ struct ucsi_connector_status *status;
int ret;
- if (cmd == (UCSI_ACK_CC_CI | UCSI_ACK_CONNECTOR_CHANGE)) {
- cmd |= UCSI_ACK_COMMAND_COMPLETE;
-
- /*
- * The UCSI core thinks it is sending a connector change ack
- * and will accept new connector change events. We don't want
- * this to happen for the dummy command as its response will
- * still report the very event that the core is trying to clear.
- */
- set_bit(UCSI_ACPI_SUPPRESS_EVENT, &ua->flags);
- ret = ucsi_acpi_sync_write(ucsi, UCSI_CONTROL, &dummycmd,
- sizeof(dummycmd));
- clear_bit(UCSI_ACPI_SUPPRESS_EVENT, &ua->flags);
-
- if (ret < 0)
- return ret;
+ ret = ucsi_acpi_read_message_in(ucsi, val, val_len);
+ if (ret < 0)
+ return ret;
+
+ if (UCSI_COMMAND(ua->cmd) == UCSI_GET_CONNECTOR_STATUS &&
+ ua->check_bogus_event) {
+ status = (struct ucsi_connector_status *)val;
+
+ /* Clear the bogus change */
+ if (status->change == bogus_change)
+ status->change = 0;
+
+ ua->check_bogus_event = false;
}
- return ucsi_acpi_sync_write(ucsi, UCSI_CONTROL, &cmd, sizeof(cmd));
+ return ret;
}
-static const struct ucsi_operations ucsi_dell_ops = {
- .read = ucsi_acpi_read,
- .sync_write = ucsi_dell_sync_write,
- .async_write = ucsi_acpi_async_write
+static int ucsi_gram_sync_control(struct ucsi *ucsi, u64 command)
+{
+ struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
+ int ret;
+
+ ret = ucsi_sync_control_common(ucsi, command);
+ if (ret < 0)
+ return ret;
+
+ if (UCSI_COMMAND(ua->cmd) == UCSI_GET_PDOS &&
+ ua->cmd & UCSI_GET_PDOS_PARTNER_PDO(1) &&
+ ua->cmd & UCSI_GET_PDOS_SRC_PDOS)
+ ua->check_bogus_event = true;
+
+ return ret;
+}
+
+static const struct ucsi_operations ucsi_gram_ops = {
+ .read_version = ucsi_acpi_read_version,
+ .read_cci = ucsi_acpi_read_cci,
+ .read_message_in = ucsi_gram_read_message_in,
+ .sync_control = ucsi_gram_sync_control,
+ .async_control = ucsi_acpi_async_control
};
static const struct dmi_system_id ucsi_acpi_quirks[] = {
@@ -182,9 +199,11 @@ static const struct dmi_system_id ucsi_acpi_quirks[] = {
},
{
.matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_SYS_VENDOR, "LG Electronics"),
+ DMI_MATCH(DMI_PRODUCT_FAMILY, "LG gram PC"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "90Q"),
},
- .driver_data = (void *)&ucsi_dell_ops,
+ .driver_data = (void *)&ucsi_gram_ops,
},
{ }
};
@@ -195,19 +214,11 @@ static void ucsi_acpi_notify(acpi_handle handle, u32 event, void *data)
u32 cci;
int ret;
- ret = ua->ucsi->ops->read(ua->ucsi, UCSI_CCI, &cci, sizeof(cci));
+ ret = ua->ucsi->ops->read_cci(ua->ucsi, &cci);
if (ret)
return;
- if (UCSI_CCI_CONNECTOR(cci) &&
- !test_bit(UCSI_ACPI_SUPPRESS_EVENT, &ua->flags))
- ucsi_connector_change(ua->ucsi, UCSI_CCI_CONNECTOR(cci));
-
- if (cci & UCSI_CCI_ACK_COMPLETE && test_bit(ACK_PENDING, &ua->flags))
- complete(&ua->complete);
- if (cci & UCSI_CCI_COMMAND_COMPLETE &&
- test_bit(UCSI_ACPI_COMMAND_PENDING, &ua->flags))
- complete(&ua->complete);
+ ucsi_notify_common(ua->ucsi, cci);
}
static int ucsi_acpi_probe(struct platform_device *pdev)
@@ -241,7 +252,6 @@ static int ucsi_acpi_probe(struct platform_device *pdev)
if (ret)
return ret;
- init_completion(&ua->complete);
ua->dev = &pdev->dev;
id = dmi_first_match(ucsi_acpi_quirks);
diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c
index dda7c7c94e08..ba58d11907bc 100644
--- a/drivers/usb/typec/ucsi/ucsi_ccg.c
+++ b/drivers/usb/typec/ucsi/ucsi_ccg.c
@@ -18,7 +18,7 @@
#include <linux/pm_runtime.h>
#include <linux/usb/typec_dp.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include "ucsi.h"
enum enum_fw_mode {
@@ -222,8 +222,6 @@ struct ucsi_ccg {
u16 fw_build;
struct work_struct pm_work;
- struct completion complete;
-
u64 last_cmd_sent;
bool has_multiple_dp;
struct ucsi_ccg_altmode orig[UCSI_MAX_ALTMODES];
@@ -556,32 +554,34 @@ static void ucsi_ccg_nvidia_altmode(struct ucsi_ccg *uc,
}
}
-static int ucsi_ccg_read(struct ucsi *ucsi, unsigned int offset,
- void *val, size_t val_len)
+static int ucsi_ccg_read_version(struct ucsi *ucsi, u16 *version)
{
struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi);
- u16 reg = CCGX_RAB_UCSI_DATA_BLOCK(offset);
- struct ucsi_capability *cap;
- struct ucsi_altmode *alt;
- int ret = 0;
+ u16 reg = CCGX_RAB_UCSI_DATA_BLOCK(UCSI_VERSION);
- if (offset == UCSI_CCI) {
- spin_lock(&uc->op_lock);
- memcpy(val, &(uc->op_data).cci, val_len);
- spin_unlock(&uc->op_lock);
- } else if (offset == UCSI_MESSAGE_IN) {
- spin_lock(&uc->op_lock);
- memcpy(val, &(uc->op_data).message_in, val_len);
- spin_unlock(&uc->op_lock);
- } else {
- ret = ccg_read(uc, reg, val, val_len);
- }
+ return ccg_read(uc, reg, (u8 *)version, sizeof(*version));
+}
- if (ret)
- return ret;
+static int ucsi_ccg_read_cci(struct ucsi *ucsi, u32 *cci)
+{
+ struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi);
- if (offset != UCSI_MESSAGE_IN)
- return ret;
+ spin_lock(&uc->op_lock);
+ *cci = uc->op_data.cci;
+ spin_unlock(&uc->op_lock);
+
+ return 0;
+}
+
+static int ucsi_ccg_read_message_in(struct ucsi *ucsi, void *val, size_t val_len)
+{
+ struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi);
+ struct ucsi_capability *cap;
+ struct ucsi_altmode *alt;
+
+ spin_lock(&uc->op_lock);
+ memcpy(val, uc->op_data.message_in, val_len);
+ spin_unlock(&uc->op_lock);
switch (UCSI_COMMAND(uc->last_cmd_sent)) {
case UCSI_GET_CURRENT_CAM:
@@ -607,28 +607,26 @@ static int ucsi_ccg_read(struct ucsi *ucsi, unsigned int offset,
}
uc->last_cmd_sent = 0;
- return ret;
+ return 0;
}
-static int ucsi_ccg_async_write(struct ucsi *ucsi, unsigned int offset,
- const void *val, size_t val_len)
+static int ucsi_ccg_async_control(struct ucsi *ucsi, u64 command)
{
struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi);
- u16 reg = CCGX_RAB_UCSI_DATA_BLOCK(offset);
+ u16 reg = CCGX_RAB_UCSI_DATA_BLOCK(UCSI_CONTROL);
/*
- * UCSI may read CCI instantly after async_write,
+ * UCSI may read CCI instantly after async_control,
* clear CCI to avoid caller getting wrong data before we get CCI from ISR
*/
spin_lock(&uc->op_lock);
uc->op_data.cci = 0;
spin_unlock(&uc->op_lock);
- return ccg_write(uc, reg, val, val_len);
+ return ccg_write(uc, reg, (u8 *)&command, sizeof(command));
}
-static int ucsi_ccg_sync_write(struct ucsi *ucsi, unsigned int offset,
- const void *val, size_t val_len)
+static int ucsi_ccg_sync_control(struct ucsi *ucsi, u64 command)
{
struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi);
struct ucsi_connector *con;
@@ -637,29 +635,19 @@ static int ucsi_ccg_sync_write(struct ucsi *ucsi, unsigned int offset,
mutex_lock(&uc->lock);
pm_runtime_get_sync(uc->dev);
- set_bit(DEV_CMD_PENDING, &uc->flags);
- if (offset == UCSI_CONTROL && val_len == sizeof(uc->last_cmd_sent)) {
- uc->last_cmd_sent = *(u64 *)val;
+ uc->last_cmd_sent = command;
- if (UCSI_COMMAND(uc->last_cmd_sent) == UCSI_SET_NEW_CAM &&
- uc->has_multiple_dp) {
- con_index = (uc->last_cmd_sent >> 16) &
- UCSI_CMD_CONNECTOR_MASK;
- con = &uc->ucsi->connector[con_index - 1];
- ucsi_ccg_update_set_new_cam_cmd(uc, con, (u64 *)val);
- }
+ if (UCSI_COMMAND(uc->last_cmd_sent) == UCSI_SET_NEW_CAM &&
+ uc->has_multiple_dp) {
+ con_index = (uc->last_cmd_sent >> 16) &
+ UCSI_CMD_CONNECTOR_MASK;
+ con = &uc->ucsi->connector[con_index - 1];
+ ucsi_ccg_update_set_new_cam_cmd(uc, con, &command);
}
- ret = ucsi_ccg_async_write(ucsi, offset, val, val_len);
- if (ret)
- goto err_clear_bit;
-
- if (!wait_for_completion_timeout(&uc->complete, msecs_to_jiffies(5000)))
- ret = -ETIMEDOUT;
+ ret = ucsi_sync_control_common(ucsi, command);
-err_clear_bit:
- clear_bit(DEV_CMD_PENDING, &uc->flags);
pm_runtime_put_sync(uc->dev);
mutex_unlock(&uc->lock);
@@ -667,9 +655,11 @@ err_clear_bit:
}
static const struct ucsi_operations ucsi_ccg_ops = {
- .read = ucsi_ccg_read,
- .sync_write = ucsi_ccg_sync_write,
- .async_write = ucsi_ccg_async_write,
+ .read_version = ucsi_ccg_read_version,
+ .read_cci = ucsi_ccg_read_cci,
+ .read_message_in = ucsi_ccg_read_message_in,
+ .sync_control = ucsi_ccg_sync_control,
+ .async_control = ucsi_ccg_async_control,
.update_altmodes = ucsi_ccg_update_altmodes
};
@@ -694,9 +684,6 @@ static irqreturn_t ccg_irq_handler(int irq, void *data)
if (ret)
goto err_clear_irq;
- if (UCSI_CCI_CONNECTOR(cci))
- ucsi_connector_change(uc->ucsi, UCSI_CCI_CONNECTOR(cci));
-
/*
* As per CCGx UCSI interface guide, copy CCI and MESSAGE_IN
* to the OpRegion before clear the UCSI interrupt
@@ -708,9 +695,8 @@ static irqreturn_t ccg_irq_handler(int irq, void *data)
err_clear_irq:
ccg_write(uc, CCGX_RAB_INTR_REG, &intr_reg, sizeof(intr_reg));
- if (!ret && test_bit(DEV_CMD_PENDING, &uc->flags) &&
- cci & (UCSI_CCI_ACK_COMPLETE | UCSI_CCI_COMMAND_COMPLETE))
- complete(&uc->complete);
+ if (!ret)
+ ucsi_notify_common(uc->ucsi, cci);
return IRQ_HANDLED;
}
@@ -1429,7 +1415,6 @@ static int ucsi_ccg_probe(struct i2c_client *client)
uc->client = client;
uc->irq = client->irq;
mutex_init(&uc->lock);
- init_completion(&uc->complete);
INIT_WORK(&uc->work, ccg_update_firmware);
INIT_WORK(&uc->pm_work, ccg_pm_workaround_work);
@@ -1516,7 +1501,7 @@ static const struct of_device_id ucsi_ccg_of_match_table[] = {
MODULE_DEVICE_TABLE(of, ucsi_ccg_of_match_table);
static const struct i2c_device_id ucsi_ccg_device_id[] = {
- {"ccgx-ucsi", 0},
+ { "ccgx-ucsi" },
{}
};
MODULE_DEVICE_TABLE(i2c, ucsi_ccg_device_id);
diff --git a/drivers/usb/typec/ucsi/ucsi_glink.c b/drivers/usb/typec/ucsi/ucsi_glink.c
index ce08eb33e5be..03c0fa8edc8d 100644
--- a/drivers/usb/typec/ucsi/ucsi_glink.c
+++ b/drivers/usb/typec/ucsi/ucsi_glink.c
@@ -14,7 +14,7 @@
#include <linux/soc/qcom/pmic_glink.h>
#include "ucsi.h"
-#define PMIC_GLINK_MAX_PORTS 2
+#define PMIC_GLINK_MAX_PORTS 3
#define UCSI_BUF_SIZE 48
@@ -58,21 +58,19 @@ struct pmic_glink_ucsi {
struct device *dev;
struct gpio_desc *port_orientation[PMIC_GLINK_MAX_PORTS];
- struct typec_switch *port_switch[PMIC_GLINK_MAX_PORTS];
struct pmic_glink_client *client;
struct ucsi *ucsi;
struct completion read_ack;
struct completion write_ack;
- struct completion sync_ack;
- bool sync_pending;
struct mutex lock; /* protects concurrent access to PMIC Glink interface */
- int sync_val;
-
struct work_struct notify_work;
struct work_struct register_work;
+ spinlock_t state_lock;
+ bool ucsi_registered;
+ bool pd_running;
u8 read_buf[UCSI_BUF_SIZE];
};
@@ -115,6 +113,21 @@ out_unlock:
return ret;
}
+static int pmic_glink_ucsi_read_version(struct ucsi *ucsi, u16 *version)
+{
+ return pmic_glink_ucsi_read(ucsi, UCSI_VERSION, version, sizeof(*version));
+}
+
+static int pmic_glink_ucsi_read_cci(struct ucsi *ucsi, u32 *cci)
+{
+ return pmic_glink_ucsi_read(ucsi, UCSI_CCI, cci, sizeof(*cci));
+}
+
+static int pmic_glink_ucsi_read_message_in(struct ucsi *ucsi, void *val, size_t val_len)
+{
+ return pmic_glink_ucsi_read(ucsi, UCSI_MESSAGE_IN, val, val_len);
+}
+
static int pmic_glink_ucsi_locked_write(struct pmic_glink_ucsi *ucsi, unsigned int offset,
const void *val, size_t val_len)
{
@@ -144,52 +157,55 @@ static int pmic_glink_ucsi_locked_write(struct pmic_glink_ucsi *ucsi, unsigned i
return 0;
}
-static int pmic_glink_ucsi_async_write(struct ucsi *__ucsi, unsigned int offset,
- const void *val, size_t val_len)
+static int pmic_glink_ucsi_async_control(struct ucsi *__ucsi, u64 command)
{
struct pmic_glink_ucsi *ucsi = ucsi_get_drvdata(__ucsi);
int ret;
mutex_lock(&ucsi->lock);
- ret = pmic_glink_ucsi_locked_write(ucsi, offset, val, val_len);
+ ret = pmic_glink_ucsi_locked_write(ucsi, UCSI_CONTROL, &command, sizeof(command));
mutex_unlock(&ucsi->lock);
return ret;
}
-static int pmic_glink_ucsi_sync_write(struct ucsi *__ucsi, unsigned int offset,
- const void *val, size_t val_len)
+static void pmic_glink_ucsi_update_connector(struct ucsi_connector *con)
{
- struct pmic_glink_ucsi *ucsi = ucsi_get_drvdata(__ucsi);
- unsigned long left;
- int ret;
-
- /* TOFIX: Downstream forces recipient to CON when UCSI_GET_ALTERNATE_MODES command */
-
- mutex_lock(&ucsi->lock);
- ucsi->sync_val = 0;
- reinit_completion(&ucsi->sync_ack);
- ucsi->sync_pending = true;
- ret = pmic_glink_ucsi_locked_write(ucsi, offset, val, val_len);
- mutex_unlock(&ucsi->lock);
+ struct pmic_glink_ucsi *ucsi = ucsi_get_drvdata(con->ucsi);
+ int i;
- left = wait_for_completion_timeout(&ucsi->sync_ack, 5 * HZ);
- if (!left) {
- dev_err(ucsi->dev, "timeout waiting for UCSI sync write response\n");
- ret = -ETIMEDOUT;
- } else if (ucsi->sync_val) {
- dev_err(ucsi->dev, "sync write returned: %d\n", ucsi->sync_val);
+ for (i = 0; i < PMIC_GLINK_MAX_PORTS; i++) {
+ if (ucsi->port_orientation[i])
+ con->typec_cap.orientation_aware = true;
}
+}
+
+static void pmic_glink_ucsi_connector_status(struct ucsi_connector *con)
+{
+ struct pmic_glink_ucsi *ucsi = ucsi_get_drvdata(con->ucsi);
+ int orientation;
- ucsi->sync_pending = false;
+ if (con->num >= PMIC_GLINK_MAX_PORTS ||
+ !ucsi->port_orientation[con->num - 1])
+ return;
- return ret;
+ orientation = gpiod_get_value(ucsi->port_orientation[con->num - 1]);
+ if (orientation >= 0) {
+ typec_set_orientation(con->port,
+ orientation ?
+ TYPEC_ORIENTATION_REVERSE :
+ TYPEC_ORIENTATION_NORMAL);
+ }
}
static const struct ucsi_operations pmic_glink_ucsi_ops = {
- .read = pmic_glink_ucsi_read,
- .sync_write = pmic_glink_ucsi_sync_write,
- .async_write = pmic_glink_ucsi_async_write
+ .read_version = pmic_glink_ucsi_read_version,
+ .read_cci = pmic_glink_ucsi_read_cci,
+ .read_message_in = pmic_glink_ucsi_read_message_in,
+ .sync_control = ucsi_sync_control_common,
+ .async_control = pmic_glink_ucsi_async_control,
+ .update_connector = pmic_glink_ucsi_update_connector,
+ .connector_status = pmic_glink_ucsi_connector_status,
};
static void pmic_glink_ucsi_read_ack(struct pmic_glink_ucsi *ucsi, const void *data, int len)
@@ -210,14 +226,12 @@ static void pmic_glink_ucsi_write_ack(struct pmic_glink_ucsi *ucsi, const void *
if (resp->ret_code)
return;
- ucsi->sync_val = resp->ret_code;
complete(&ucsi->write_ack);
}
static void pmic_glink_ucsi_notify(struct work_struct *work)
{
struct pmic_glink_ucsi *ucsi = container_of(work, struct pmic_glink_ucsi, notify_work);
- unsigned int con_num;
u32 cci;
int ret;
@@ -227,50 +241,26 @@ static void pmic_glink_ucsi_notify(struct work_struct *work)
return;
}
- con_num = UCSI_CCI_CONNECTOR(cci);
- if (con_num) {
- if (con_num <= PMIC_GLINK_MAX_PORTS &&
- ucsi->port_orientation[con_num - 1]) {
- int orientation = gpiod_get_value(ucsi->port_orientation[con_num - 1]);
-
- if (orientation >= 0) {
- typec_switch_set(ucsi->port_switch[con_num - 1],
- orientation ? TYPEC_ORIENTATION_REVERSE
- : TYPEC_ORIENTATION_NORMAL);
- }
- }
-
- ucsi_connector_change(ucsi->ucsi, con_num);
- }
-
- if (ucsi->sync_pending && cci & UCSI_CCI_BUSY) {
- ucsi->sync_val = -EBUSY;
- complete(&ucsi->sync_ack);
- } else if (ucsi->sync_pending &&
- (cci & (UCSI_CCI_ACK_COMPLETE | UCSI_CCI_COMMAND_COMPLETE))) {
- complete(&ucsi->sync_ack);
- }
+ ucsi_notify_common(ucsi->ucsi, cci);
}
static void pmic_glink_ucsi_register(struct work_struct *work)
{
struct pmic_glink_ucsi *ucsi = container_of(work, struct pmic_glink_ucsi, register_work);
- int orientation;
- int i;
+ unsigned long flags;
+ bool pd_running;
- for (i = 0; i < PMIC_GLINK_MAX_PORTS; i++) {
- if (!ucsi->port_orientation[i])
- continue;
- orientation = gpiod_get_value(ucsi->port_orientation[i]);
+ spin_lock_irqsave(&ucsi->state_lock, flags);
+ pd_running = ucsi->pd_running;
+ spin_unlock_irqrestore(&ucsi->state_lock, flags);
- if (orientation >= 0) {
- typec_switch_set(ucsi->port_switch[i],
- orientation ? TYPEC_ORIENTATION_REVERSE
- : TYPEC_ORIENTATION_NORMAL);
- }
+ if (!ucsi->ucsi_registered && pd_running) {
+ ucsi_register(ucsi->ucsi);
+ ucsi->ucsi_registered = true;
+ } else if (ucsi->ucsi_registered && !pd_running) {
+ ucsi_unregister(ucsi->ucsi);
+ ucsi->ucsi_registered = false;
}
-
- ucsi_register(ucsi->ucsi);
}
static void pmic_glink_ucsi_callback(const void *data, size_t len, void *priv)
@@ -288,17 +278,18 @@ static void pmic_glink_ucsi_callback(const void *data, size_t len, void *priv)
case UC_UCSI_USBC_NOTIFY_IND:
schedule_work(&ucsi->notify_work);
break;
- };
+ }
}
static void pmic_glink_ucsi_pdr_notify(void *priv, int state)
{
struct pmic_glink_ucsi *ucsi = priv;
+ unsigned long flags;
- if (state == SERVREG_SERVICE_STATE_UP)
- schedule_work(&ucsi->register_work);
- else if (state == SERVREG_SERVICE_STATE_DOWN)
- ucsi_unregister(ucsi->ucsi);
+ spin_lock_irqsave(&ucsi->state_lock, flags);
+ ucsi->pd_running = (state == SERVREG_SERVICE_STATE_UP);
+ spin_unlock_irqrestore(&ucsi->state_lock, flags);
+ schedule_work(&ucsi->register_work);
}
static void pmic_glink_ucsi_destroy(void *data)
@@ -311,12 +302,17 @@ static void pmic_glink_ucsi_destroy(void *data)
mutex_unlock(&ucsi->lock);
}
+static unsigned long quirk_sc8180x = UCSI_NO_PARTNER_PDOS;
+static unsigned long quirk_sc8280xp = UCSI_NO_PARTNER_PDOS | UCSI_DELAY_DEVICE_PDOS;
+static unsigned long quirk_sm8450 = UCSI_DELAY_DEVICE_PDOS;
+
static const struct of_device_id pmic_glink_ucsi_of_quirks[] = {
- { .compatible = "qcom,qcm6490-pmic-glink", .data = (void *)UCSI_NO_PARTNER_PDOS, },
- { .compatible = "qcom,sc8180x-pmic-glink", .data = (void *)UCSI_NO_PARTNER_PDOS, },
- { .compatible = "qcom,sc8280xp-pmic-glink", .data = (void *)UCSI_NO_PARTNER_PDOS, },
- { .compatible = "qcom,sm8350-pmic-glink", .data = (void *)UCSI_NO_PARTNER_PDOS, },
- { .compatible = "qcom,sm8550-pmic-glink", .data = (void *)UCSI_NO_PARTNER_PDOS, },
+ { .compatible = "qcom,qcm6490-pmic-glink", .data = &quirk_sc8280xp, },
+ { .compatible = "qcom,sc8180x-pmic-glink", .data = &quirk_sc8180x, },
+ { .compatible = "qcom,sc8280xp-pmic-glink", .data = &quirk_sc8280xp, },
+ { .compatible = "qcom,sm8350-pmic-glink", .data = &quirk_sc8180x, },
+ { .compatible = "qcom,sm8450-pmic-glink", .data = &quirk_sm8450, },
+ { .compatible = "qcom,sm8550-pmic-glink", .data = &quirk_sm8450, },
{}
};
@@ -340,7 +336,7 @@ static int pmic_glink_ucsi_probe(struct auxiliary_device *adev,
INIT_WORK(&ucsi->register_work, pmic_glink_ucsi_register);
init_completion(&ucsi->read_ack);
init_completion(&ucsi->write_ack);
- init_completion(&ucsi->sync_ack);
+ spin_lock_init(&ucsi->state_lock);
mutex_init(&ucsi->lock);
ucsi->ucsi = ucsi_create(dev, &pmic_glink_ucsi_ops);
@@ -354,7 +350,7 @@ static int pmic_glink_ucsi_probe(struct auxiliary_device *adev,
match = of_match_device(pmic_glink_ucsi_of_quirks, dev->parent);
if (match)
- ucsi->ucsi->quirks = (unsigned long)match->data;
+ ucsi->ucsi->quirks = *(unsigned long *)match->data;
ucsi_set_drvdata(ucsi->ucsi, ucsi);
@@ -365,6 +361,7 @@ static int pmic_glink_ucsi_probe(struct auxiliary_device *adev,
ret = fwnode_property_read_u32(fwnode, "reg", &port);
if (ret < 0) {
dev_err(dev, "missing reg property of %pOFn\n", fwnode);
+ fwnode_handle_put(fwnode);
return ret;
}
@@ -379,23 +376,24 @@ static int pmic_glink_ucsi_probe(struct auxiliary_device *adev,
if (!desc)
continue;
- if (IS_ERR(desc))
+ if (IS_ERR(desc)) {
+ fwnode_handle_put(fwnode);
return dev_err_probe(dev, PTR_ERR(desc),
"unable to acquire orientation gpio\n");
+ }
ucsi->port_orientation[port] = desc;
-
- ucsi->port_switch[port] = fwnode_typec_switch_get(fwnode);
- if (IS_ERR(ucsi->port_switch[port]))
- return dev_err_probe(dev, PTR_ERR(ucsi->port_switch[port]),
- "failed to acquire orientation-switch\n");
}
- ucsi->client = devm_pmic_glink_register_client(dev,
- PMIC_GLINK_OWNER_USBC,
- pmic_glink_ucsi_callback,
- pmic_glink_ucsi_pdr_notify,
- ucsi);
- return PTR_ERR_OR_ZERO(ucsi->client);
+ ucsi->client = devm_pmic_glink_client_alloc(dev, PMIC_GLINK_OWNER_USBC,
+ pmic_glink_ucsi_callback,
+ pmic_glink_ucsi_pdr_notify,
+ ucsi);
+ if (IS_ERR(ucsi->client))
+ return PTR_ERR(ucsi->client);
+
+ pmic_glink_client_register(ucsi->client);
+
+ return 0;
}
static void pmic_glink_ucsi_remove(struct auxiliary_device *adev)
diff --git a/drivers/usb/typec/ucsi/ucsi_stm32g0.c b/drivers/usb/typec/ucsi/ucsi_stm32g0.c
index 93d7806681cf..6923fad31d79 100644
--- a/drivers/usb/typec/ucsi/ucsi_stm32g0.c
+++ b/drivers/usb/typec/ucsi/ucsi_stm32g0.c
@@ -12,7 +12,7 @@
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/platform_device.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include "ucsi.h"
@@ -61,9 +61,7 @@ struct ucsi_stm32g0 {
struct i2c_client *i2c_bl;
bool in_bootloader;
u8 bl_version;
- struct completion complete;
struct device *dev;
- unsigned long flags;
const char *fw_name;
struct ucsi *ucsi;
bool suspended;
@@ -357,8 +355,22 @@ static int ucsi_stm32g0_read(struct ucsi *ucsi, unsigned int offset, void *val,
return 0;
}
-static int ucsi_stm32g0_async_write(struct ucsi *ucsi, unsigned int offset, const void *val,
- size_t len)
+static int ucsi_stm32g0_read_version(struct ucsi *ucsi, u16 *version)
+{
+ return ucsi_stm32g0_read(ucsi, UCSI_VERSION, version, sizeof(*version));
+}
+
+static int ucsi_stm32g0_read_cci(struct ucsi *ucsi, u32 *cci)
+{
+ return ucsi_stm32g0_read(ucsi, UCSI_CCI, cci, sizeof(*cci));
+}
+
+static int ucsi_stm32g0_read_message_in(struct ucsi *ucsi, void *val, size_t len)
+{
+ return ucsi_stm32g0_read(ucsi, UCSI_MESSAGE_IN, val, len);
+}
+
+static int ucsi_stm32g0_async_control(struct ucsi *ucsi, u64 command)
{
struct ucsi_stm32g0 *g0 = ucsi_get_drvdata(ucsi);
struct i2c_client *client = g0->client;
@@ -371,19 +383,19 @@ static int ucsi_stm32g0_async_write(struct ucsi *ucsi, unsigned int offset, cons
unsigned char *buf;
int ret;
- buf = kmalloc(len + 1, GFP_KERNEL);
+ buf = kmalloc(sizeof(command) + 1, GFP_KERNEL);
if (!buf)
return -ENOMEM;
- buf[0] = offset;
- memcpy(&buf[1], val, len);
- msg[0].len = len + 1;
+ buf[0] = UCSI_CONTROL;
+ memcpy(&buf[1], &command, sizeof(command));
+ msg[0].len = sizeof(command) + 1;
msg[0].buf = buf;
ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
kfree(buf);
if (ret != ARRAY_SIZE(msg)) {
- dev_err(g0->dev, "i2c write %02x, %02x error: %d\n", client->addr, offset, ret);
+ dev_err(g0->dev, "i2c write %02x, %02x error: %d\n", client->addr, UCSI_CONTROL, ret);
return ret < 0 ? ret : -EIO;
}
@@ -391,27 +403,6 @@ static int ucsi_stm32g0_async_write(struct ucsi *ucsi, unsigned int offset, cons
return 0;
}
-static int ucsi_stm32g0_sync_write(struct ucsi *ucsi, unsigned int offset, const void *val,
- size_t len)
-{
- struct ucsi_stm32g0 *g0 = ucsi_get_drvdata(ucsi);
- int ret;
-
- set_bit(COMMAND_PENDING, &g0->flags);
-
- ret = ucsi_stm32g0_async_write(ucsi, offset, val, len);
- if (ret)
- goto out_clear_bit;
-
- if (!wait_for_completion_timeout(&g0->complete, msecs_to_jiffies(5000)))
- ret = -ETIMEDOUT;
-
-out_clear_bit:
- clear_bit(COMMAND_PENDING, &g0->flags);
-
- return ret;
-}
-
static irqreturn_t ucsi_stm32g0_irq_handler(int irq, void *data)
{
struct ucsi_stm32g0 *g0 = data;
@@ -425,20 +416,17 @@ static irqreturn_t ucsi_stm32g0_irq_handler(int irq, void *data)
if (ret)
return IRQ_NONE;
- if (UCSI_CCI_CONNECTOR(cci))
- ucsi_connector_change(g0->ucsi, UCSI_CCI_CONNECTOR(cci));
-
- if (test_bit(COMMAND_PENDING, &g0->flags) &&
- cci & (UCSI_CCI_ACK_COMPLETE | UCSI_CCI_COMMAND_COMPLETE))
- complete(&g0->complete);
+ ucsi_notify_common(g0->ucsi, cci);
return IRQ_HANDLED;
}
static const struct ucsi_operations ucsi_stm32g0_ops = {
- .read = ucsi_stm32g0_read,
- .sync_write = ucsi_stm32g0_sync_write,
- .async_write = ucsi_stm32g0_async_write,
+ .read_version = ucsi_stm32g0_read_version,
+ .read_cci = ucsi_stm32g0_read_cci,
+ .read_message_in = ucsi_stm32g0_read_message_in,
+ .sync_control = ucsi_sync_control_common,
+ .async_control = ucsi_stm32g0_async_control,
};
static int ucsi_stm32g0_register(struct ucsi *ucsi)
@@ -638,7 +626,6 @@ static int ucsi_stm32g0_probe(struct i2c_client *client)
g0->dev = dev;
g0->client = client;
- init_completion(&g0->complete);
i2c_set_clientdata(client, g0);
g0->ucsi = ucsi_create(dev, &ucsi_stm32g0_ops);
@@ -752,8 +739,8 @@ static const struct of_device_id __maybe_unused ucsi_stm32g0_typec_of_match[] =
MODULE_DEVICE_TABLE(of, ucsi_stm32g0_typec_of_match);
static const struct i2c_device_id ucsi_stm32g0_typec_i2c_devid[] = {
- {"stm32g0-typec", 0},
- {},
+ { "stm32g0-typec" },
+ {}
};
MODULE_DEVICE_TABLE(i2c, ucsi_stm32g0_typec_i2c_devid);
diff --git a/drivers/usb/typec/ucsi/ucsi_yoga_c630.c b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
new file mode 100644
index 000000000000..f3a5e24ea84d
--- /dev/null
+++ b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
@@ -0,0 +1,164 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024, Linaro Ltd
+ * Authors:
+ * Bjorn Andersson
+ * Dmitry Baryshkov
+ */
+#include <linux/auxiliary_bus.h>
+#include <linux/bitops.h>
+#include <linux/completion.h>
+#include <linux/container_of.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/string.h>
+#include <linux/platform_data/lenovo-yoga-c630.h>
+
+#include "ucsi.h"
+
+struct yoga_c630_ucsi {
+ struct yoga_c630_ec *ec;
+ struct ucsi *ucsi;
+ struct notifier_block nb;
+ u16 version;
+};
+
+static int yoga_c630_ucsi_read_version(struct ucsi *ucsi, u16 *version)
+{
+ struct yoga_c630_ucsi *uec = ucsi_get_drvdata(ucsi);
+
+ *version = uec->version;
+
+ return 0;
+}
+
+static int yoga_c630_ucsi_read_cci(struct ucsi *ucsi, u32 *cci)
+{
+ struct yoga_c630_ucsi *uec = ucsi_get_drvdata(ucsi);
+ u8 buf[YOGA_C630_UCSI_READ_SIZE];
+ int ret;
+
+ ret = yoga_c630_ec_ucsi_read(uec->ec, buf);
+ if (ret)
+ return ret;
+
+ memcpy(cci, buf, sizeof(*cci));
+
+ return 0;
+}
+
+static int yoga_c630_ucsi_read_message_in(struct ucsi *ucsi,
+ void *val, size_t val_len)
+{
+ struct yoga_c630_ucsi *uec = ucsi_get_drvdata(ucsi);
+ u8 buf[YOGA_C630_UCSI_READ_SIZE];
+ int ret;
+
+ ret = yoga_c630_ec_ucsi_read(uec->ec, buf);
+ if (ret)
+ return ret;
+
+ memcpy(val, buf + YOGA_C630_UCSI_CCI_SIZE,
+ min(val_len, YOGA_C630_UCSI_DATA_SIZE));
+
+ return 0;
+}
+
+static int yoga_c630_ucsi_async_control(struct ucsi *ucsi, u64 command)
+{
+ struct yoga_c630_ucsi *uec = ucsi_get_drvdata(ucsi);
+
+ return yoga_c630_ec_ucsi_write(uec->ec, (u8*)&command);
+}
+
+const struct ucsi_operations yoga_c630_ucsi_ops = {
+ .read_version = yoga_c630_ucsi_read_version,
+ .read_cci = yoga_c630_ucsi_read_cci,
+ .read_message_in = yoga_c630_ucsi_read_message_in,
+ .sync_control = ucsi_sync_control_common,
+ .async_control = yoga_c630_ucsi_async_control,
+};
+
+static int yoga_c630_ucsi_notify(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct yoga_c630_ucsi *uec = container_of(nb, struct yoga_c630_ucsi, nb);
+ u32 cci;
+ int ret;
+
+ switch (action) {
+ case LENOVO_EC_EVENT_USB:
+ case LENOVO_EC_EVENT_HPD:
+ ucsi_connector_change(uec->ucsi, 1);
+ return NOTIFY_OK;
+
+ case LENOVO_EC_EVENT_UCSI:
+ ret = uec->ucsi->ops->read_cci(uec->ucsi, &cci);
+ if (ret)
+ return NOTIFY_DONE;
+
+ ucsi_notify_common(uec->ucsi, cci);
+
+ return NOTIFY_OK;
+
+ default:
+ return NOTIFY_DONE;
+ }
+}
+
+static int yoga_c630_ucsi_probe(struct auxiliary_device *adev,
+ const struct auxiliary_device_id *id)
+{
+ struct yoga_c630_ec *ec = adev->dev.platform_data;
+ struct yoga_c630_ucsi *uec;
+ int ret;
+
+ uec = devm_kzalloc(&adev->dev, sizeof(*uec), GFP_KERNEL);
+ if (!uec)
+ return -ENOMEM;
+
+ uec->ec = ec;
+ uec->nb.notifier_call = yoga_c630_ucsi_notify;
+
+ uec->ucsi = ucsi_create(&adev->dev, &yoga_c630_ucsi_ops);
+ if (IS_ERR(uec->ucsi))
+ return PTR_ERR(uec->ucsi);
+
+ ucsi_set_drvdata(uec->ucsi, uec);
+
+ uec->version = yoga_c630_ec_ucsi_get_version(uec->ec);
+
+ auxiliary_set_drvdata(adev, uec);
+
+ ret = yoga_c630_ec_register_notify(ec, &uec->nb);
+ if (ret)
+ return ret;
+
+ return ucsi_register(uec->ucsi);
+}
+
+static void yoga_c630_ucsi_remove(struct auxiliary_device *adev)
+{
+ struct yoga_c630_ucsi *uec = auxiliary_get_drvdata(adev);
+
+ yoga_c630_ec_unregister_notify(uec->ec, &uec->nb);
+ ucsi_unregister(uec->ucsi);
+}
+
+static const struct auxiliary_device_id yoga_c630_ucsi_id_table[] = {
+ { .name = YOGA_C630_MOD_NAME "." YOGA_C630_DEV_UCSI, },
+ {}
+};
+MODULE_DEVICE_TABLE(auxiliary, yoga_c630_ucsi_id_table);
+
+static struct auxiliary_driver yoga_c630_ucsi_driver = {
+ .name = YOGA_C630_DEV_UCSI,
+ .id_table = yoga_c630_ucsi_id_table,
+ .probe = yoga_c630_ucsi_probe,
+ .remove = yoga_c630_ucsi_remove,
+};
+
+module_auxiliary_driver(yoga_c630_ucsi_driver);
+
+MODULE_DESCRIPTION("Lenovo Yoga C630 UCSI");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/usbip/stub_rx.c b/drivers/usb/usbip/stub_rx.c
index fc01b31bbb87..6338d818bc8b 100644
--- a/drivers/usb/usbip/stub_rx.c
+++ b/drivers/usb/usbip/stub_rx.c
@@ -144,53 +144,62 @@ static int tweak_set_configuration_cmd(struct urb *urb)
if (err && err != -ENODEV)
dev_err(&sdev->udev->dev, "can't set config #%d, error %d\n",
config, err);
- return 0;
+ return err;
}
static int tweak_reset_device_cmd(struct urb *urb)
{
struct stub_priv *priv = (struct stub_priv *) urb->context;
struct stub_device *sdev = priv->sdev;
+ int err;
dev_info(&urb->dev->dev, "usb_queue_reset_device\n");
- if (usb_lock_device_for_reset(sdev->udev, NULL) < 0) {
+ err = usb_lock_device_for_reset(sdev->udev, NULL);
+ if (err < 0) {
dev_err(&urb->dev->dev, "could not obtain lock to reset device\n");
- return 0;
+ return err;
}
- usb_reset_device(sdev->udev);
+ err = usb_reset_device(sdev->udev);
usb_unlock_device(sdev->udev);
- return 0;
+ return err;
}
/*
* clear_halt, set_interface, and set_configuration require special tricks.
+ * Returns 1 if request was tweaked, 0 otherwise.
*/
-static void tweak_special_requests(struct urb *urb)
+static int tweak_special_requests(struct urb *urb)
{
+ int err;
+
if (!urb || !urb->setup_packet)
- return;
+ return 0;
if (usb_pipetype(urb->pipe) != PIPE_CONTROL)
- return;
+ return 0;
if (is_clear_halt_cmd(urb))
/* tweak clear_halt */
- tweak_clear_halt_cmd(urb);
+ err = tweak_clear_halt_cmd(urb);
else if (is_set_interface_cmd(urb))
/* tweak set_interface */
- tweak_set_interface_cmd(urb);
+ err = tweak_set_interface_cmd(urb);
else if (is_set_configuration_cmd(urb))
/* tweak set_configuration */
- tweak_set_configuration_cmd(urb);
+ err = tweak_set_configuration_cmd(urb);
else if (is_reset_device_cmd(urb))
- tweak_reset_device_cmd(urb);
- else
+ err = tweak_reset_device_cmd(urb);
+ else {
usbip_dbg_stub_rx("no need to tweak\n");
+ return 0;
+ }
+
+ return !err;
}
/*
@@ -468,6 +477,7 @@ static void stub_recv_cmd_submit(struct stub_device *sdev,
int support_sg = 1;
int np = 0;
int ret, i;
+ int is_tweaked;
if (pipe == -1)
return;
@@ -580,8 +590,11 @@ static void stub_recv_cmd_submit(struct stub_device *sdev,
priv->urbs[i]->pipe = pipe;
priv->urbs[i]->complete = stub_complete;
- /* no need to submit an intercepted request, but harmless? */
- tweak_special_requests(priv->urbs[i]);
+ /*
+ * all URBs belong to a single PDU, so a global is_tweaked flag is
+ * enough
+ */
+ is_tweaked = tweak_special_requests(priv->urbs[i]);
masking_bogus_flags(priv->urbs[i]);
}
@@ -594,22 +607,32 @@ static void stub_recv_cmd_submit(struct stub_device *sdev,
/* urb is now ready to submit */
for (i = 0; i < priv->num_urbs; i++) {
- ret = usb_submit_urb(priv->urbs[i], GFP_KERNEL);
+ if (!is_tweaked) {
+ ret = usb_submit_urb(priv->urbs[i], GFP_KERNEL);
- if (ret == 0)
- usbip_dbg_stub_rx("submit urb ok, seqnum %u\n",
- pdu->base.seqnum);
- else {
- dev_err(&udev->dev, "submit_urb error, %d\n", ret);
- usbip_dump_header(pdu);
- usbip_dump_urb(priv->urbs[i]);
+ if (ret == 0)
+ usbip_dbg_stub_rx("submit urb ok, seqnum %u\n",
+ pdu->base.seqnum);
+ else {
+ dev_err(&udev->dev, "submit_urb error, %d\n", ret);
+ usbip_dump_header(pdu);
+ usbip_dump_urb(priv->urbs[i]);
+ /*
+ * Pessimistic.
+ * This connection will be discarded.
+ */
+ usbip_event_add(ud, SDEV_EVENT_ERROR_SUBMIT);
+ break;
+ }
+ } else {
/*
- * Pessimistic.
- * This connection will be discarded.
+ * An identical URB was already submitted in
+ * tweak_special_requests(). Skip submitting this URB to not
+ * duplicate the request.
*/
- usbip_event_add(ud, SDEV_EVENT_ERROR_SUBMIT);
- break;
+ priv->urbs[i]->status = 0;
+ stub_complete(priv->urbs[i]);
}
}
diff --git a/drivers/usb/usbip/vhci_hcd.c b/drivers/usb/usbip/vhci_hcd.c
index 82650c11e451..8dac1edc74d4 100644
--- a/drivers/usb/usbip/vhci_hcd.c
+++ b/drivers/usb/usbip/vhci_hcd.c
@@ -372,7 +372,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
}
switch (wValue) {
case USB_PORT_FEAT_SUSPEND:
- if (hcd->speed == HCD_USB3) {
+ if (hcd->speed >= HCD_USB3) {
pr_err(" ClearPortFeature: USB_PORT_FEAT_SUSPEND req not "
"supported for USB 3.0 roothub\n");
goto error;
@@ -388,7 +388,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
case USB_PORT_FEAT_POWER:
usbip_dbg_vhci_rh(
" ClearPortFeature: USB_PORT_FEAT_POWER\n");
- if (hcd->speed == HCD_USB3)
+ if (hcd->speed >= HCD_USB3)
vhci_hcd->port_status[rhport] &= ~USB_SS_PORT_STAT_POWER;
else
vhci_hcd->port_status[rhport] &= ~USB_PORT_STAT_POWER;
@@ -404,19 +404,19 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
break;
case GetHubDescriptor:
usbip_dbg_vhci_rh(" GetHubDescriptor\n");
- if (hcd->speed == HCD_USB3 &&
+ if (hcd->speed >= HCD_USB3 &&
(wLength < USB_DT_SS_HUB_SIZE ||
wValue != (USB_DT_SS_HUB << 8))) {
pr_err("Wrong hub descriptor type for USB 3.0 roothub.\n");
goto error;
}
- if (hcd->speed == HCD_USB3)
+ if (hcd->speed >= HCD_USB3)
ss_hub_descriptor((struct usb_hub_descriptor *) buf);
else
hub_descriptor((struct usb_hub_descriptor *) buf);
break;
case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
- if (hcd->speed != HCD_USB3)
+ if (hcd->speed < HCD_USB3)
goto error;
if ((wValue >> 8) != USB_DT_BOS)
@@ -503,7 +503,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
case USB_PORT_FEAT_LINK_STATE:
usbip_dbg_vhci_rh(
" SetPortFeature: USB_PORT_FEAT_LINK_STATE\n");
- if (hcd->speed != HCD_USB3) {
+ if (hcd->speed < HCD_USB3) {
pr_err("USB_PORT_FEAT_LINK_STATE req not "
"supported for USB 2.0 roothub\n");
goto error;
@@ -521,7 +521,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
usbip_dbg_vhci_rh(
" SetPortFeature: USB_PORT_FEAT_U2_TIMEOUT\n");
/* TODO: add suspend/resume support! */
- if (hcd->speed != HCD_USB3) {
+ if (hcd->speed < HCD_USB3) {
pr_err("USB_PORT_FEAT_U1/2_TIMEOUT req not "
"supported for USB 2.0 roothub\n");
goto error;
@@ -531,7 +531,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
usbip_dbg_vhci_rh(
" SetPortFeature: USB_PORT_FEAT_SUSPEND\n");
/* Applicable only for USB2.0 hub */
- if (hcd->speed == HCD_USB3) {
+ if (hcd->speed >= HCD_USB3) {
pr_err("USB_PORT_FEAT_SUSPEND req not "
"supported for USB 3.0 roothub\n");
goto error;
@@ -551,7 +551,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
pr_err("invalid port number %d\n", wIndex);
goto error;
}
- if (hcd->speed == HCD_USB3)
+ if (hcd->speed >= HCD_USB3)
vhci_hcd->port_status[rhport] |= USB_SS_PORT_STAT_POWER;
else
vhci_hcd->port_status[rhport] |= USB_PORT_STAT_POWER;
@@ -564,7 +564,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
goto error;
}
/* Applicable only for USB3.0 hub */
- if (hcd->speed != HCD_USB3) {
+ if (hcd->speed < HCD_USB3) {
pr_err("USB_PORT_FEAT_BH_PORT_RESET req not "
"supported for USB 2.0 roothub\n");
goto error;
@@ -578,7 +578,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
goto error;
}
/* if it's already enabled, disable */
- if (hcd->speed == HCD_USB3) {
+ if (hcd->speed >= HCD_USB3) {
vhci_hcd->port_status[rhport] = 0;
vhci_hcd->port_status[rhport] =
(USB_SS_PORT_STAT_POWER |
@@ -602,7 +602,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
}
if (wValue >= 32)
goto error;
- if (hcd->speed == HCD_USB3) {
+ if (hcd->speed >= HCD_USB3) {
if ((vhci_hcd->port_status[rhport] &
USB_SS_PORT_STAT_POWER) != 0) {
vhci_hcd->port_status[rhport] |= (1 << wValue);
@@ -616,7 +616,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
break;
case GetPortErrorCount:
usbip_dbg_vhci_rh(" GetPortErrorCount\n");
- if (hcd->speed != HCD_USB3) {
+ if (hcd->speed < HCD_USB3) {
pr_err("GetPortErrorCount req not "
"supported for USB 2.0 roothub\n");
goto error;
@@ -626,7 +626,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
break;
case SetHubDepth:
usbip_dbg_vhci_rh(" SetHubDepth\n");
- if (hcd->speed != HCD_USB3) {
+ if (hcd->speed < HCD_USB3) {
pr_err("SetHubDepth req not supported for "
"USB 2.0 roothub\n");
goto error;
@@ -646,7 +646,7 @@ error:
if (!invalid_rhport) {
dump_port_status_diff(prev_port_status[rhport],
vhci_hcd->port_status[rhport],
- hcd->speed == HCD_USB3);
+ hcd->speed >= HCD_USB3);
}
}
usbip_dbg_vhci_rh(" bye\n");
@@ -745,6 +745,7 @@ static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag
*
*/
if (usb_pipedevice(urb->pipe) == 0) {
+ struct usb_device *old;
__u8 type = usb_pipetype(urb->pipe);
struct usb_ctrlrequest *ctrlreq =
(struct usb_ctrlrequest *) urb->setup_packet;
@@ -755,14 +756,15 @@ static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag
goto no_need_xmit;
}
+ old = vdev->udev;
switch (ctrlreq->bRequest) {
case USB_REQ_SET_ADDRESS:
/* set_address may come when a device is reset */
dev_info(dev, "SetAddress Request (%d) to port %d\n",
ctrlreq->wValue, vdev->rhport);
- usb_put_dev(vdev->udev);
vdev->udev = usb_get_dev(urb->dev);
+ usb_put_dev(old);
spin_lock(&vdev->ud.lock);
vdev->ud.status = VDEV_ST_USED;
@@ -781,8 +783,8 @@ static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag
usbip_dbg_vhci_hc(
"Not yet?:Get_Descriptor to device 0 (get max pipe size)\n");
- usb_put_dev(vdev->udev);
vdev->udev = usb_get_dev(urb->dev);
+ usb_put_dev(old);
goto out;
default:
@@ -1067,6 +1069,7 @@ static void vhci_shutdown_connection(struct usbip_device *ud)
static void vhci_device_reset(struct usbip_device *ud)
{
struct vhci_device *vdev = container_of(ud, struct vhci_device, ud);
+ struct usb_device *old = vdev->udev;
unsigned long flags;
spin_lock_irqsave(&ud->lock, flags);
@@ -1074,8 +1077,8 @@ static void vhci_device_reset(struct usbip_device *ud)
vdev->speed = 0;
vdev->devid = 0;
- usb_put_dev(vdev->udev);
vdev->udev = NULL;
+ usb_put_dev(old);
if (ud->tcp_socket) {
sockfd_put(ud->tcp_socket);
@@ -1154,8 +1157,8 @@ static int vhci_setup(struct usb_hcd *hcd)
} else {
vhci->vhci_hcd_ss = hcd_to_vhci_hcd(hcd);
vhci->vhci_hcd_ss->vhci = vhci;
- hcd->speed = HCD_USB3;
- hcd->self.root_hub->speed = USB_SPEED_SUPER;
+ hcd->speed = HCD_USB31;
+ hcd->self.root_hub->speed = USB_SPEED_SUPER_PLUS;
}
/*
@@ -1316,7 +1319,7 @@ static const struct hc_driver vhci_hc_driver = {
.product_desc = driver_desc,
.hcd_priv_size = sizeof(struct vhci_hcd),
- .flags = HCD_USB3 | HCD_SHARED,
+ .flags = HCD_USB31 | HCD_SHARED,
.reset = vhci_setup,
.start = vhci_start,
diff --git a/drivers/usb/usbip/vhci_sysfs.c b/drivers/usb/usbip/vhci_sysfs.c
index e2847cd3e6e3..d5865460e82d 100644
--- a/drivers/usb/usbip/vhci_sysfs.c
+++ b/drivers/usb/usbip/vhci_sysfs.c
@@ -283,6 +283,7 @@ static int valid_args(__u32 *pdev_nr, __u32 *rhport,
case USB_SPEED_HIGH:
case USB_SPEED_WIRELESS:
case USB_SPEED_SUPER:
+ case USB_SPEED_SUPER_PLUS:
break;
default:
pr_err("Failed attach request for unsupported USB speed: %s\n",
@@ -349,7 +350,7 @@ static ssize_t attach_store(struct device *dev, struct device_attribute *attr,
vhci_hcd = hcd_to_vhci_hcd(hcd);
vhci = vhci_hcd->vhci;
- if (speed == USB_SPEED_SUPER)
+ if (speed >= USB_SPEED_SUPER)
vdev = &vhci->vhci_hcd_ss->vdev[rhport];
else
vdev = &vhci->vhci_hcd_hs->vdev[rhport];