diff options
Diffstat (limited to 'drivers/usb')
165 files changed, 1781 insertions, 1270 deletions
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 6f3c86149887..eecbd631fdab 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -14,8 +14,6 @@ obj-$(CONFIG_USB_DWC2) += dwc2/ obj-$(CONFIG_USB_ISP1760) += isp1760/ obj-$(CONFIG_USB_CDNS_SUPPORT) += cdns3/ -obj-$(CONFIG_USB_CDNS3) += cdns3/ -obj-$(CONFIG_USB_CDNSP_PCI) += cdns3/ obj-$(CONFIG_USB_FOTG210) += fotg210/ diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c index f3ae72feb5bf..4e71ed679a76 100644 --- a/drivers/usb/atm/ueagle-atm.c +++ b/drivers/usb/atm/ueagle-atm.c @@ -51,12 +51,6 @@ "[ueagle-atm vdbg] " format, ##args); \ } while (0) -#define uea_enters(usb_dev) \ - uea_vdbg(usb_dev, "entering %s\n" , __func__) - -#define uea_leaves(usb_dev) \ - uea_vdbg(usb_dev, "leaving %s\n" , __func__) - #define uea_err(usb_dev, format, args...) \ dev_err(&(usb_dev)->dev , "[UEAGLE-ATM] " format , ##args) @@ -600,13 +594,14 @@ static int uea_send_modem_cmd(struct usb_device *usb, static void uea_upload_pre_firmware(const struct firmware *fw_entry, void *context) { - struct usb_device *usb = context; + struct usb_interface *intf = context; + struct usb_device *usb = interface_to_usbdev(intf); + struct completion *fw_done = usb_get_intfdata(intf); const u8 *pfw; u8 value; u32 crc = 0; int ret, size; - uea_enters(usb); if (!fw_entry) { uea_err(usb, "firmware is not available\n"); goto err; @@ -670,18 +665,18 @@ err_fw_corrupted: uea_err(usb, "firmware is corrupted\n"); err: release_firmware(fw_entry); - uea_leaves(usb); + complete(fw_done); } /* * uea_load_firmware - Load usb firmware for pre-firmware devices. */ -static int uea_load_firmware(struct usb_device *usb, unsigned int ver) +static int uea_load_firmware(struct usb_interface *intf, unsigned int ver) { int ret; char *fw_name = EAGLE_FIRMWARE; + struct usb_device *usb = interface_to_usbdev(intf); - uea_enters(usb); uea_info(usb, "pre-firmware device, uploading firmware\n"); switch (ver) { @@ -703,14 +698,13 @@ static int uea_load_firmware(struct usb_device *usb, unsigned int ver) } ret = request_firmware_nowait(THIS_MODULE, 1, fw_name, &usb->dev, - GFP_KERNEL, usb, + GFP_KERNEL, intf, uea_upload_pre_firmware); if (ret) uea_err(usb, "firmware %s is not available\n", fw_name); else uea_info(usb, "loading firmware %s\n", fw_name); - uea_leaves(usb); return ret; } @@ -1137,7 +1131,6 @@ static int uea_cmv_e1(struct uea_softc *sc, struct cmv_e1 cmv; int ret; - uea_enters(INS_TO_USBDEV(sc)); uea_vdbg(INS_TO_USBDEV(sc), "Function : %d-%d, Address : %c%c%c%c, " "offset : 0x%04x, data : 0x%08x\n", E1_FUNCTION_TYPE(function), @@ -1164,9 +1157,8 @@ static int uea_cmv_e1(struct uea_softc *sc, sizeof(cmv), &cmv); if (ret < 0) return ret; - ret = wait_cmv_ack(sc); - uea_leaves(INS_TO_USBDEV(sc)); - return ret; + + return wait_cmv_ack(sc); } static int uea_cmv_e4(struct uea_softc *sc, @@ -1175,7 +1167,6 @@ static int uea_cmv_e4(struct uea_softc *sc, struct cmv_e4 cmv; int ret; - uea_enters(INS_TO_USBDEV(sc)); memset(&cmv, 0, sizeof(cmv)); uea_vdbg(INS_TO_USBDEV(sc), "Function : %d-%d, Group : 0x%04x, " @@ -1199,9 +1190,8 @@ static int uea_cmv_e4(struct uea_softc *sc, sizeof(cmv), &cmv); if (ret < 0) return ret; - ret = wait_cmv_ack(sc); - uea_leaves(INS_TO_USBDEV(sc)); - return ret; + + return wait_cmv_ack(sc); } static inline int uea_read_cmv_e1(struct uea_softc *sc, @@ -1295,7 +1285,6 @@ static int uea_stat_e1(struct uea_softc *sc) u32 data; int ret; - uea_enters(INS_TO_USBDEV(sc)); data = sc->stats.phy.state; ret = uea_read_cmv_e1(sc, E1_SA_STAT, 0, &sc->stats.phy.state); @@ -1438,7 +1427,6 @@ static int uea_stat_e4(struct uea_softc *sc) u32 tmp_arr[2]; int ret; - uea_enters(INS_TO_USBDEV(sc)); data = sc->stats.phy.state; /* XXX only need to be done before operationnal... */ @@ -1572,10 +1560,8 @@ static void cmvs_file_name(struct uea_softc *sc, char *const cmv_name, int ver) } else file = cmv_file[sc->modem_index]; - strcpy(cmv_name, FW_DIR); - strlcat(cmv_name, file, UEA_FW_NAME_MAX); - if (ver == 2) - strlcat(cmv_name, ".v2", UEA_FW_NAME_MAX); + snprintf(cmv_name, UEA_FW_NAME_MAX, FW_DIR "%s%s", + file, ver == 2 ? ".v2" : ""); kernel_param_unlock(THIS_MODULE); } @@ -1805,7 +1791,6 @@ static int uea_start_reset(struct uea_softc *sc) u16 zero = 0; /* ;-) */ int ret; - uea_enters(INS_TO_USBDEV(sc)); uea_info(INS_TO_USBDEV(sc), "(re)booting started\n"); /* mask interrupt */ @@ -1873,7 +1858,6 @@ static int uea_start_reset(struct uea_softc *sc) return ret; sc->reset = 0; - uea_leaves(INS_TO_USBDEV(sc)); return ret; } @@ -1889,7 +1873,6 @@ static int uea_kthread(void *data) int ret = -EAGAIN; set_freezable(); - uea_enters(INS_TO_USBDEV(sc)); while (!kthread_should_stop()) { if (ret < 0 || sc->reset) ret = uea_start_reset(sc); @@ -1898,7 +1881,7 @@ static int uea_kthread(void *data) if (ret != -EAGAIN) uea_wait(sc, 0, msecs_to_jiffies(1000)); } - uea_leaves(INS_TO_USBDEV(sc)); + return ret; } @@ -1911,8 +1894,6 @@ static int load_XILINX_firmware(struct uea_softc *sc) u8 value; char *fw_name = FPGA930_FIRMWARE; - uea_enters(INS_TO_USBDEV(sc)); - ret = request_firmware(&fw_entry, fw_name, &sc->usb_dev->dev); if (ret) { uea_err(INS_TO_USBDEV(sc), "firmware %s is not available\n", @@ -1956,7 +1937,6 @@ static int load_XILINX_firmware(struct uea_softc *sc) err1: release_firmware(fw_entry); err0: - uea_leaves(INS_TO_USBDEV(sc)); return ret; } @@ -1966,7 +1946,6 @@ static void uea_dispatch_cmv_e1(struct uea_softc *sc, struct intr_pkt *intr) struct cmv_dsc_e1 *dsc = &sc->cmv_dsc.e1; struct cmv_e1 *cmv = &intr->u.e1.s2.cmv; - uea_enters(INS_TO_USBDEV(sc)); if (le16_to_cpu(cmv->wPreamble) != E1_PREAMBLE) goto bad1; @@ -1990,7 +1969,6 @@ static void uea_dispatch_cmv_e1(struct uea_softc *sc, struct intr_pkt *intr) if (cmv->bFunction == E1_MAKEFUNCTION(E1_ADSLDIRECTIVE, E1_MODEMREADY)) { wake_up_cmv_ack(sc); - uea_leaves(INS_TO_USBDEV(sc)); return; } @@ -2004,7 +1982,6 @@ static void uea_dispatch_cmv_e1(struct uea_softc *sc, struct intr_pkt *intr) sc->data = sc->data << 16 | sc->data >> 16; wake_up_cmv_ack(sc); - uea_leaves(INS_TO_USBDEV(sc)); return; bad2: @@ -2012,14 +1989,12 @@ bad2: "Function : %d, Subfunction : %d\n", E1_FUNCTION_TYPE(cmv->bFunction), E1_FUNCTION_SUBTYPE(cmv->bFunction)); - uea_leaves(INS_TO_USBDEV(sc)); return; bad1: uea_err(INS_TO_USBDEV(sc), "invalid cmv received, " "wPreamble %d, bDirection %d\n", le16_to_cpu(cmv->wPreamble), cmv->bDirection); - uea_leaves(INS_TO_USBDEV(sc)); } /* The modem send us an ack. First with check if it right */ @@ -2028,7 +2003,6 @@ static void uea_dispatch_cmv_e4(struct uea_softc *sc, struct intr_pkt *intr) struct cmv_dsc_e4 *dsc = &sc->cmv_dsc.e4; struct cmv_e4 *cmv = &intr->u.e4.s2.cmv; - uea_enters(INS_TO_USBDEV(sc)); uea_dbg(INS_TO_USBDEV(sc), "cmv %x %x %x %x %x %x\n", be16_to_cpu(cmv->wGroup), be16_to_cpu(cmv->wFunction), be16_to_cpu(cmv->wOffset), be16_to_cpu(cmv->wAddress), @@ -2040,7 +2014,6 @@ static void uea_dispatch_cmv_e4(struct uea_softc *sc, struct intr_pkt *intr) if (be16_to_cpu(cmv->wFunction) == E4_MAKEFUNCTION(E4_ADSLDIRECTIVE, E4_MODEMREADY, 1)) { wake_up_cmv_ack(sc); - uea_leaves(INS_TO_USBDEV(sc)); return; } @@ -2053,7 +2026,6 @@ static void uea_dispatch_cmv_e4(struct uea_softc *sc, struct intr_pkt *intr) sc->data = be32_to_cpu(cmv->dwData[0]); sc->data1 = be32_to_cpu(cmv->dwData[1]); wake_up_cmv_ack(sc); - uea_leaves(INS_TO_USBDEV(sc)); return; bad2: @@ -2061,7 +2033,6 @@ bad2: "Function : %d, Subfunction : %d\n", E4_FUNCTION_TYPE(cmv->wFunction), E4_FUNCTION_SUBTYPE(cmv->wFunction)); - uea_leaves(INS_TO_USBDEV(sc)); return; } @@ -2089,8 +2060,6 @@ static void uea_intr(struct urb *urb) struct intr_pkt *intr = urb->transfer_buffer; int status = urb->status; - uea_enters(INS_TO_USBDEV(sc)); - if (unlikely(status < 0)) { uea_err(INS_TO_USBDEV(sc), "uea_intr() failed with %d\n", status); @@ -2130,8 +2099,6 @@ static int uea_boot(struct uea_softc *sc, struct usb_interface *intf) int ret = -ENOMEM; int size; - uea_enters(INS_TO_USBDEV(sc)); - if (UEA_CHIP_VERSION(sc) == EAGLE_IV) { size = E4_INTR_PKT_SIZE; sc->dispatch_cmv = uea_dispatch_cmv_e4; @@ -2188,7 +2155,6 @@ static int uea_boot(struct uea_softc *sc, struct usb_interface *intf) goto err2; } - uea_leaves(INS_TO_USBDEV(sc)); return 0; err2: @@ -2198,7 +2164,6 @@ err1: sc->urb_int = NULL; kfree(intr); err0: - uea_leaves(INS_TO_USBDEV(sc)); return ret; } @@ -2208,7 +2173,7 @@ err0: static void uea_stop(struct uea_softc *sc) { int ret; - uea_enters(INS_TO_USBDEV(sc)); + ret = kthread_stop(sc->kthread); uea_dbg(INS_TO_USBDEV(sc), "kthread finish with status %d\n", ret); @@ -2222,7 +2187,6 @@ static void uea_stop(struct uea_softc *sc) flush_work(&sc->task); release_firmware(sc->dsp_firm); - uea_leaves(INS_TO_USBDEV(sc)); } /* syfs interface */ @@ -2495,8 +2459,6 @@ static int uea_bind(struct usbatm_data *usbatm, struct usb_interface *intf, int ret, ifnum = intf->altsetting->desc.bInterfaceNumber; unsigned int alt; - uea_enters(usb); - /* interface 0 is for firmware/monitoring */ if (ifnum != UEA_INTR_IFACE_NO) return -ENODEV; @@ -2589,8 +2551,7 @@ static int uea_probe(struct usb_interface *intf, const struct usb_device_id *id) struct usb_device *usb = interface_to_usbdev(intf); int ret; - uea_enters(usb); - uea_info(usb, "ADSL device founded vid (%#X) pid (%#X) Rev (%#X): %s\n", + uea_dbg(usb, "ADSL device found with vid (%#X) pid (%#X) Rev (%#X): %s\n", le16_to_cpu(usb->descriptor.idVendor), le16_to_cpu(usb->descriptor.idProduct), le16_to_cpu(usb->descriptor.bcdDevice), @@ -2598,8 +2559,23 @@ static int uea_probe(struct usb_interface *intf, const struct usb_device_id *id) usb_reset_device(usb); - if (UEA_IS_PREFIRM(id)) - return uea_load_firmware(usb, UEA_CHIP_VERSION(id)); + if (UEA_IS_PREFIRM(id)) { + struct completion *fw_done; + + /* Wait for the firmware load to be done, in .disconnect() */ + fw_done = kzalloc_obj(*fw_done); + if (!fw_done) + return -ENOMEM; + + init_completion(fw_done); + usb_set_intfdata(intf, fw_done); + + ret = uea_load_firmware(intf, UEA_CHIP_VERSION(id)); + if (ret) + kfree(fw_done); + + return ret; + } ret = usbatm_usb_probe(intf, id, &uea_usbatm_driver); if (ret == 0) { @@ -2620,7 +2596,6 @@ static void uea_disconnect(struct usb_interface *intf) { struct usb_device *usb = interface_to_usbdev(intf); int ifnum = intf->altsetting->desc.bInterfaceNumber; - uea_enters(usb); /* ADI930 has 2 interfaces and eagle 3 interfaces. * Pre-firmware device has one interface @@ -2630,9 +2605,14 @@ static void uea_disconnect(struct usb_interface *intf) usbatm_usb_disconnect(intf); mutex_unlock(&uea_mutex); uea_info(usb, "ADSL device removed\n"); - } + } else if (usb->config->desc.bNumInterfaces == 1) { + struct completion *fw_done = usb_get_intfdata(intf); - uea_leaves(usb); + uea_dbg(usb, "pre-firmware device, waiting firmware upload\n"); + wait_for_completion(fw_done); + uea_dbg(usb, "pre-firmware device, finished waiting\n"); + kfree(fw_done); + } } /* diff --git a/drivers/usb/cdns3/Kconfig b/drivers/usb/cdns3/Kconfig index 0a514b591527..39ad23d1ada8 100644 --- a/drivers/usb/cdns3/Kconfig +++ b/drivers/usb/cdns3/Kconfig @@ -1,6 +1,9 @@ config USB_CDNS_SUPPORT tristate "Cadence USB Support" - depends on USB_SUPPORT && (USB || USB_GADGET) && HAS_DMA + depends on USB_SUPPORT && HAS_DMA + depends on USB || USB_GADGET + depends on USB if !USB_GADGET + depends on USB_GADGET if !USB select USB_XHCI_PLATFORM if USB_XHCI_HCD select USB_ROLE_SWITCH help @@ -8,44 +11,49 @@ config USB_CDNS_SUPPORT dual-role controller. It supports: dual-role switch, Host-only, and Peripheral-only. -config USB_CDNS_HOST - bool - if USB_CDNS_SUPPORT config USB_CDNS3 - tristate "Cadence USB3 Dual-Role Controller" + tristate "Cadence USB dual-role controller (USBSS and USBSSP)" depends on USB_CDNS_SUPPORT help - Say Y here if your system has a Cadence USB3 dual-role controller. - It supports: dual-role switch, Host-only, and Peripheral-only. + Say Y or M here if your system has an on-chip Cadence USB + dual-role controller. This covers both USBSS (USB 3.0) and + USBSSP (SuperSpeed Plus) IP; the driver detects the variant at + runtime. - If you choose to build this driver is a dynamically linked - as module, the module will be called cdns3.ko. -endif + The core driver (core, DRD, generic platform binding for the + "cdns,usb3" device tree compatible, optional host and gadget) + builds as one module named cdns.ko when built as a loadable + module. + + It supports: dual-role switch, Host-only, and Peripheral-only. if USB_CDNS3 -config USB_CDNS3_GADGET - bool "Cadence USB3 device controller" - depends on USB_GADGET=y || USB_GADGET=USB_CDNS3 +config USB_CDNS3_HOST + bool "Cadence USB host controller (xHCI)" + depends on USB=y || USB=USB_CDNS3 help - Say Y here to enable device controller functionality of the - Cadence USBSS-DEV driver. + Say Y here to enable host controller functionality for Cadence + USBSS and USBSSP dual-role controllers. - This controller supports FF, HS and SS mode. It doesn't support - LS and SSP mode. + The host controller is xHCI compliant and uses the standard + xHCI driver. -config USB_CDNS3_HOST - bool "Cadence USB3 host controller" - depends on USB=y || USB=USB_CDNS3 - select USB_CDNS_HOST +config USB_CDNS3_GADGET + bool "Cadence USB device controller (USBSS and USBSSP)" + depends on USB_GADGET=y || USB_GADGET=USB_CDNS3 help - Say Y here to enable host controller functionality of the - Cadence driver. + Say Y here to include Cadence USB device (gadget) support for + both USBSS (USB 3.0) and USBSSP (SuperSpeed Plus) IP in the + cdns.ko module. The implementation is selected at runtime from + the detected controller version. - Host controller is compliant with XHCI so it will use - standard XHCI driver. + USBSS gadget supports FF, HS and SS mode (not LS or SSP). + USBSSP gadget supports FF, HS, SS and SSP mode (not LS). + +comment "Platform glue driver support" config USB_CDNS3_PCI_WRAP tristate "Cadence USB3 support on PCIe-based platforms" @@ -58,6 +66,17 @@ config USB_CDNS3_PCI_WRAP If you choose to build this driver as module it will be dynamically linked and module will be called cdns3-pci.ko +config USB_CDNSP_PCI + tristate "Cadence USBSSP support on PCIe-based platforms" + depends on USB_PCI && ACPI + default USB_CDNS3 + help + If you're using the USBSSP Core IP with a PCIe, please say + 'Y' or 'M' here. + + If you choose to build this driver as module it will + be dynamically linked and module will be called cdnsp-pci.ko + config USB_CDNS3_TI tristate "Cadence USB3 support on TI platforms" depends on ARCH_K3 || COMPILE_TEST @@ -81,6 +100,7 @@ config USB_CDNS3_IMX config USB_CDNS3_STARFIVE tristate "Cadence USB3 support on StarFive SoC platforms" depends on ARCH_STARFIVE || COMPILE_TEST + default USB_CDNS3 help Say 'Y' or 'M' here if you are building for StarFive SoCs platforms that contain Cadence USB3 controller core. @@ -89,45 +109,7 @@ config USB_CDNS3_STARFIVE If you choose to build this driver as module it will be dynamically linked and module will be called cdns3-starfive.ko -endif - -if USB_CDNS_SUPPORT - -config USB_CDNSP_PCI - tristate "Cadence CDNSP Dual-Role Controller" - depends on USB_CDNS_SUPPORT && USB_PCI && ACPI - help - Say Y here if your system has a Cadence CDNSP dual-role controller. - It supports: dual-role switch Host-only, and Peripheral-only. - - If you choose to build this driver is a dynamically linked - module, the module will be called cdnsp.ko. -endif - -if USB_CDNSP_PCI - -config USB_CDNSP_GADGET - bool "Cadence CDNSP device controller" - depends on USB_GADGET=y || USB_GADGET=USB_CDNSP_PCI - help - Say Y here to enable device controller functionality of the - Cadence CDNSP-DEV driver. - - Cadence CDNSP Device Controller in device mode is - very similar to XHCI controller. Therefore some algorithms - used has been taken from host driver. - This controller supports FF, HS, SS and SSP mode. - It doesn't support LS. - -config USB_CDNSP_HOST - bool "Cadence CDNSP host controller" - depends on USB=y || USB=USB_CDNSP_PCI - select USB_CDNS_HOST - help - Say Y here to enable host controller functionality of the - Cadence driver. - Host controller is compliant with XHCI so it uses - standard XHCI driver. +endif # USB_CDNS3 -endif +endif # USB_CDNS_SUPPORT diff --git a/drivers/usb/cdns3/Makefile b/drivers/usb/cdns3/Makefile index 48dfae75b5aa..b2e4ba6a49a3 100644 --- a/drivers/usb/cdns3/Makefile +++ b/drivers/usb/cdns3/Makefile @@ -3,42 +3,28 @@ CFLAGS_cdns3-trace.o := -I$(src) CFLAGS_cdnsp-trace.o := -I$(src) -cdns-usb-common-y := core.o drd.o -cdns3-y := cdns3-plat.o +obj-$(CONFIG_USB_CDNS3) += cdns.o -ifeq ($(CONFIG_USB),m) -obj-m += cdns-usb-common.o -obj-m += cdns3.o -else -obj-$(CONFIG_USB_CDNS_SUPPORT) += cdns-usb-common.o -obj-$(CONFIG_USB_CDNS3) += cdns3.o -endif +cdns-y := core.o drd.o cdns3-plat.o +cdns-$(CONFIG_USB_CDNS3_HOST) += host.o -cdns-usb-common-$(CONFIG_USB_CDNS_HOST) += host.o -cdns3-$(CONFIG_USB_CDNS3_GADGET) += cdns3-gadget.o cdns3-ep0.o +ifneq ($(CONFIG_USB_CDNS3_GADGET),) +cdns-y += cdns3-gadget.o cdns3-ep0.o \ + cdnsp-ring.o cdnsp-gadget.o \ + cdnsp-mem.o cdnsp-ep0.o +endif +ifneq ($(CONFIG_TRACING),) ifneq ($(CONFIG_USB_CDNS3_GADGET),) -cdns3-$(CONFIG_TRACING) += cdns3-trace.o +cdns-y += cdns3-trace.o cdnsp-trace.o +endif endif +## +# Platform-specific glue layers (PCI wrappers, SoC integration) +## obj-$(CONFIG_USB_CDNS3_PCI_WRAP) += cdns3-pci-wrap.o +obj-$(CONFIG_USB_CDNSP_PCI) += cdnsp-pci.o obj-$(CONFIG_USB_CDNS3_TI) += cdns3-ti.o obj-$(CONFIG_USB_CDNS3_IMX) += cdns3-imx.o obj-$(CONFIG_USB_CDNS3_STARFIVE) += cdns3-starfive.o - -cdnsp-udc-pci-y := cdnsp-pci.o - -ifdef CONFIG_USB_CDNSP_PCI -ifeq ($(CONFIG_USB),m) -obj-m += cdnsp-udc-pci.o -else -obj-$(CONFIG_USB_CDNSP_PCI) += cdnsp-udc-pci.o -endif -endif - -cdnsp-udc-pci-$(CONFIG_USB_CDNSP_GADGET) += cdnsp-ring.o cdnsp-gadget.o \ - cdnsp-mem.o cdnsp-ep0.o - -ifneq ($(CONFIG_USB_CDNSP_GADGET),) -cdnsp-udc-pci-$(CONFIG_TRACING) += cdnsp-trace.o -endif diff --git a/drivers/usb/cdns3/cdns3-gadget.c b/drivers/usb/cdns3/cdns3-gadget.c index 1db8db1b7cc3..42311c1bfada 100644 --- a/drivers/usb/cdns3/cdns3-gadget.c +++ b/drivers/usb/cdns3/cdns3-gadget.c @@ -3522,3 +3522,4 @@ int cdns3_gadget_init(struct cdns *cdns) return 0; } +EXPORT_SYMBOL_GPL(cdns3_gadget_init); diff --git a/drivers/usb/cdns3/cdns3-plat.c b/drivers/usb/cdns3/cdns3-plat.c index 94e9706a1806..bb5405460035 100644 --- a/drivers/usb/cdns3/cdns3-plat.c +++ b/drivers/usb/cdns3/cdns3-plat.c @@ -21,6 +21,7 @@ #include "core.h" #include "gadget-export.h" +#include "host-export.h" #include "drd.h" static int set_phy_power_on(struct cdns *cdns) @@ -44,6 +45,19 @@ static void set_phy_power_off(struct cdns *cdns) phy_power_off(cdns->usb2_phy); } +static int cdns3_plat_gadget_init(struct cdns *cdns) +{ + if (cdns->version < CDNSP_CONTROLLER_V2) + return cdns3_gadget_init(cdns); + else + return cdnsp_gadget_init(cdns); +} + +static int cdns3_plat_host_init(struct cdns *cdns) +{ + return cdns_host_init(cdns); +} + /** * cdns3_plat_probe - probe for cdns3 core device * @pdev: Pointer to cdns3 core platform device @@ -64,6 +78,14 @@ static int cdns3_plat_probe(struct platform_device *pdev) cdns->dev = dev; cdns->pdata = dev_get_platdata(dev); + if (cdns->pdata && cdns->pdata->override_apb_timeout) + cdns->override_apb_timeout = cdns->pdata->override_apb_timeout; + + if (device_is_compatible(dev, "cdns,cdnsp")) { + cdns->no_drd = true; + cdns->version = CDNSP_CONTROLLER_V2; + dev_dbg(dev, "No DRD support\n"); + } platform_set_drvdata(pdev, cdns); @@ -97,21 +119,22 @@ static int cdns3_plat_probe(struct platform_device *pdev) cdns->dev_regs = regs; - cdns->otg_irq = platform_get_irq_byname(pdev, "otg"); - if (cdns->otg_irq < 0) - return dev_err_probe(dev, cdns->otg_irq, - "Failed to get otg IRQ\n"); - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "otg"); - if (!res) { - dev_err(dev, "couldn't get otg resource\n"); - return -ENXIO; + if (!cdns->no_drd) { + cdns->otg_irq = platform_get_irq_byname(pdev, "otg"); + if (cdns->otg_irq < 0) + return dev_err_probe(dev, cdns->otg_irq, + "Failed to get otg IRQ\n"); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "otg"); + if (!res) { + dev_err(dev, "couldn't get otg resource\n"); + return -ENXIO; + } + cdns->otg_res = *res; } cdns->phyrst_a_enable = device_property_read_bool(dev, "cdns,phyrst-a-enable"); - cdns->otg_res = *res; - cdns->wakeup_irq = platform_get_irq_byname_optional(pdev, "wakeup"); if (cdns->wakeup_irq == -EPROBE_DEFER) return cdns->wakeup_irq; @@ -143,12 +166,16 @@ static int cdns3_plat_probe(struct platform_device *pdev) if (ret) goto err_phy_power_on; - cdns->gadget_init = cdns3_gadget_init; - ret = cdns_init(cdns); if (ret) goto err_cdns_init; + cdns->gadget_init = cdns3_plat_gadget_init; + cdns->host_init = cdns3_plat_host_init; + ret = cdns_core_init_role(cdns); + if (ret) + goto err_cdns_init_role; + device_set_wakeup_capable(dev, true); pm_runtime_set_active(dev); pm_runtime_enable(dev); @@ -166,6 +193,9 @@ static int cdns3_plat_probe(struct platform_device *pdev) return 0; +err_cdns_init_role: + if (cdns->role_sw) + usb_role_switch_unregister(cdns->role_sw); err_cdns_init: set_phy_power_off(cdns); err_phy_power_on: @@ -321,6 +351,7 @@ static const struct dev_pm_ops cdns3_pm_ops = { #ifdef CONFIG_OF static const struct of_device_id of_cdns3_match[] = { { .compatible = "cdns,usb3" }, + { .compatible = "cdns,cdnsp" }, { }, }; MODULE_DEVICE_TABLE(of, of_cdns3_match); @@ -339,6 +370,3 @@ static struct platform_driver cdns3_driver = { module_platform_driver(cdns3_driver); MODULE_ALIAS("platform:cdns3"); -MODULE_AUTHOR("Pawel Laszczak <pawell@cadence.com>"); -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("Cadence USB3 DRD Controller Driver"); diff --git a/drivers/usb/cdns3/cdnsp-gadget.c b/drivers/usb/cdns3/cdnsp-gadget.c index 6b3815f8a6e5..a5275c2fb43b 100644 --- a/drivers/usb/cdns3/cdnsp-gadget.c +++ b/drivers/usb/cdns3/cdnsp-gadget.c @@ -124,20 +124,28 @@ void cdnsp_set_link_state(struct cdnsp_device *pdev, } static void cdnsp_disable_port(struct cdnsp_device *pdev, - __le32 __iomem *port_regs) + struct cdnsp_port *port) { - u32 temp = cdnsp_port_state_to_neutral(readl(port_regs)); + u32 temp; + + if (!port->exist) + return; - writel(temp | PORT_PED, port_regs); + temp = cdnsp_port_state_to_neutral(readl(&port->regs->portsc)); + writel(temp | PORT_PED, &port->regs->portsc); } static void cdnsp_clear_port_change_bit(struct cdnsp_device *pdev, - __le32 __iomem *port_regs) + struct cdnsp_port *port) { - u32 portsc = readl(port_regs); + u32 portsc; + + if (!port->exist) + return; + portsc = readl(&port->regs->portsc); writel(cdnsp_port_state_to_neutral(portsc) | - (portsc & PORT_CHANGE_BITS), port_regs); + (portsc & PORT_CHANGE_BITS), &port->regs->portsc); } static void cdnsp_set_apb_timeout_value(struct cdnsp_device *pdev) @@ -944,7 +952,7 @@ void cdnsp_set_usb2_hardware_lpm(struct cdnsp_device *pdev, struct usb_request *req, int enable) { - if (pdev->active_port != &pdev->usb2_port || !pdev->gadget.lpm_capable) + if (pdev->active_port == &pdev->usb3_port || !pdev->gadget.lpm_capable) return; trace_cdnsp_lpm(enable); @@ -1310,20 +1318,26 @@ static int cdnsp_run(struct cdnsp_device *pdev, break; } - if (speed >= USB_SPEED_SUPER) { + if (pdev->usb3_port.exist && speed >= USB_SPEED_SUPER) { writel(temp, &pdev->port3x_regs->mode_addr); cdnsp_set_link_state(pdev, &pdev->usb3_port.regs->portsc, XDEV_RXDETECT); } else { - cdnsp_disable_port(pdev, &pdev->usb3_port.regs->portsc); + cdnsp_disable_port(pdev, &pdev->usb3_port); } - cdnsp_set_link_state(pdev, &pdev->usb2_port.regs->portsc, - XDEV_RXDETECT); + if (pdev->usb2_port.exist) { + cdnsp_set_link_state(pdev, &pdev->usb2_port.regs->portsc, + XDEV_RXDETECT); + writel(PORT_REG6_L1_L0_HW_EN | fs_speed, &pdev->port20_regs->port_reg6); + } + + if (pdev->eusb_port.exist) + cdnsp_set_link_state(pdev, &pdev->eusb_port.regs->portsc, + XDEV_RXDETECT); cdnsp_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512); - writel(PORT_REG6_L1_L0_HW_EN | fs_speed, &pdev->port20_regs->port_reg6); ret = cdnsp_start(pdev); if (ret) { @@ -1469,8 +1483,10 @@ static void cdnsp_stop(struct cdnsp_device *pdev) cdnsp_ep_dequeue(&pdev->eps[0], req); } - cdnsp_disable_port(pdev, &pdev->usb2_port.regs->portsc); - cdnsp_disable_port(pdev, &pdev->usb3_port.regs->portsc); + cdnsp_disable_port(pdev, &pdev->usb2_port); + cdnsp_disable_port(pdev, &pdev->usb3_port); + cdnsp_disable_port(pdev, &pdev->eusb_port); + cdnsp_disable_slot(pdev); cdnsp_halt(pdev); @@ -1479,8 +1495,9 @@ static void cdnsp_stop(struct cdnsp_device *pdev) temp = readl(&pdev->ir_set->irq_pending); writel(IMAN_IE_CLEAR(temp), &pdev->ir_set->irq_pending); - cdnsp_clear_port_change_bit(pdev, &pdev->usb2_port.regs->portsc); - cdnsp_clear_port_change_bit(pdev, &pdev->usb3_port.regs->portsc); + cdnsp_clear_port_change_bit(pdev, &pdev->usb2_port); + cdnsp_clear_port_change_bit(pdev, &pdev->eusb_port); + cdnsp_clear_port_change_bit(pdev, &pdev->usb3_port); /* Clear interrupt line */ temp = readl(&pdev->ir_set->irq_pending); @@ -2075,3 +2092,4 @@ int cdnsp_gadget_init(struct cdns *cdns) return 0; } +EXPORT_SYMBOL_GPL(cdnsp_gadget_init); diff --git a/drivers/usb/cdns3/cdnsp-gadget.h b/drivers/usb/cdns3/cdnsp-gadget.h index a91cca509db0..c44bca348a41 100644 --- a/drivers/usb/cdns3/cdnsp-gadget.h +++ b/drivers/usb/cdns3/cdnsp-gadget.h @@ -1474,6 +1474,7 @@ struct cdnsp_device { unsigned int link_state; struct cdnsp_port usb2_port; + struct cdnsp_port eusb_port; struct cdnsp_port usb3_port; struct cdnsp_port *active_port; u16 test_mode; diff --git a/drivers/usb/cdns3/cdnsp-mem.c b/drivers/usb/cdns3/cdnsp-mem.c index a2a1b21f2ef8..83f3384b735d 100644 --- a/drivers/usb/cdns3/cdnsp-mem.c +++ b/drivers/usb/cdns3/cdnsp-mem.c @@ -631,6 +631,8 @@ cleanup_rings: } } + cdnsp_free_stream_ctx(pdev, pep); + cleanup_stream_rings: kfree(pep->stream_info.stream_rings); @@ -1088,11 +1090,9 @@ void cdnsp_mem_cleanup(struct cdnsp_device *pdev) pdev->dcbaa, pdev->dcbaa->dma); pdev->dcbaa = NULL; - - pdev->usb2_port.exist = 0; - pdev->usb3_port.exist = 0; - pdev->usb2_port.port_num = 0; - pdev->usb3_port.port_num = 0; + memset(&pdev->usb2_port, 0, sizeof(struct cdnsp_port)); + memset(&pdev->eusb_port, 0, sizeof(struct cdnsp_port)); + memset(&pdev->usb3_port, 0, sizeof(struct cdnsp_port)); pdev->active_port = NULL; } @@ -1133,6 +1133,18 @@ static void cdnsp_add_in_port(struct cdnsp_device *pdev, port_offset = CDNSP_EXT_PORT_OFF(temp); port_count = CDNSP_EXT_PORT_COUNT(temp); + if (port == &pdev->eusb_port) { + /* + * If controller has usb2 + eusb port then eusb is as + * second port + */ + if (port_count == 2) + port_offset++; + + if (port_count == 1 && pdev->usb2_port.exist) + return; + } + trace_cdnsp_port_info(addr, port_offset, port_count, port->maj_rev); port->port_num = port_offset; @@ -1152,13 +1164,10 @@ static int cdnsp_setup_port_arrays(struct cdnsp_device *pdev) base = &pdev->cap_regs->hc_capbase; offset = cdnsp_find_next_ext_cap(base, 0, EXT_CAP_CFG_DEV_20PORT_CAP_ID); - pdev->port20_regs = base + offset; - - offset = cdnsp_find_next_ext_cap(base, 0, D_XEC_CFG_3XPORT_CAP); - pdev->port3x_regs = base + offset; + if (offset) + pdev->port20_regs = base + offset; offset = 0; - base = &pdev->cap_regs->hc_capbase; /* Driver expects max 2 extended protocol capability. */ for (i = 0; i < 2; i++) { @@ -1173,26 +1182,46 @@ static int cdnsp_setup_port_arrays(struct cdnsp_device *pdev) cdnsp_add_in_port(pdev, &pdev->usb3_port, base + offset); - if (CDNSP_EXT_PORT_MAJOR(temp) == 0x02 && - !pdev->usb2_port.port_num) - cdnsp_add_in_port(pdev, &pdev->usb2_port, - base + offset); + if (CDNSP_EXT_PORT_MAJOR(temp) == 0x02) { + if (!pdev->usb2_port.port_num && pdev->port20_regs) + cdnsp_add_in_port(pdev, &pdev->usb2_port, + base + offset); + + if (!pdev->eusb_port.port_num) + cdnsp_add_in_port(pdev, &pdev->eusb_port, + base + offset); + } } - if (!pdev->usb2_port.exist || !pdev->usb3_port.exist) { - dev_err(pdev->dev, "Error: Only one port detected\n"); + if (!pdev->usb2_port.exist && !pdev->eusb_port.exist && + !pdev->usb3_port.exist) { + dev_err(pdev->dev, "Error: No port detected\n"); return -ENODEV; } - trace_cdnsp_init("Found USB 2.0 ports and USB 3.0 ports."); + if (pdev->usb2_port.exist) { + pdev->usb2_port.regs = (struct cdnsp_port_regs __iomem *) + (&pdev->op_regs->port_reg_base + NUM_PORT_REGS * + (pdev->usb2_port.port_num - 1)); + trace_cdnsp_init("Found USB 2.0 port."); + } + + if (pdev->eusb_port.exist) { + pdev->eusb_port.regs = (struct cdnsp_port_regs __iomem *) + (&pdev->op_regs->port_reg_base + NUM_PORT_REGS * + (pdev->eusb_port.port_num - 1)); + trace_cdnsp_init("Found eUSB 2.0 port."); + } - pdev->usb2_port.regs = (struct cdnsp_port_regs __iomem *) - (&pdev->op_regs->port_reg_base + NUM_PORT_REGS * - (pdev->usb2_port.port_num - 1)); + if (pdev->usb3_port.exist) { + offset = cdnsp_find_next_ext_cap(base, 0, D_XEC_CFG_3XPORT_CAP); + pdev->port3x_regs = base + offset; - pdev->usb3_port.regs = (struct cdnsp_port_regs __iomem *) - (&pdev->op_regs->port_reg_base + NUM_PORT_REGS * - (pdev->usb3_port.port_num - 1)); + pdev->usb3_port.regs = (struct cdnsp_port_regs __iomem *) + (&pdev->op_regs->port_reg_base + NUM_PORT_REGS * + (pdev->usb3_port.port_num - 1)); + trace_cdnsp_init("Found USB 3.x port."); + } return 0; } diff --git a/drivers/usb/cdns3/cdnsp-pci.c b/drivers/usb/cdns3/cdnsp-pci.c index 566d94e49102..c38a3dc7111a 100644 --- a/drivers/usb/cdns3/cdnsp-pci.c +++ b/drivers/usb/cdns3/cdnsp-pci.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Cadence PCI Glue driver. + * Cadence USBSSP PCI Glue driver. * * Copyright (C) 2019 Cadence. * @@ -16,26 +16,45 @@ #include <linux/pci.h> #include "core.h" -#include "gadget-export.h" +struct cdnsp_wrap { + struct platform_device *plat_dev; + struct property_entry prop[3]; + struct resource dev_res[6]; + int devfn; +}; + +#define RES_IRQ_HOST_ID 0 +#define RES_IRQ_PERIPHERAL_ID 1 +#define RES_IRQ_OTG_ID 2 +#define RES_HOST_ID 3 +#define RES_DEV_ID 4 +#define RES_DRD_ID 5 +/* DRD PCI configuration - 64-bit addressing */ +/* First PCI function */ #define PCI_BAR_HOST 0 -#define PCI_BAR_OTG 0 #define PCI_BAR_DEV 2 +/* Second PCI function */ +#define PCI_BAR_OTG 0 +/* Device only PCI configuration - 32-bit addressing */ +/* First PCI function */ +#define PCI_BAR_ONLY_DEV 1 #define PCI_DEV_FN_HOST_DEVICE 0 #define PCI_DEV_FN_OTG 1 #define PCI_DRIVER_NAME "cdns-pci-usbssp" -#define PLAT_DRIVER_NAME "cdns-usbssp" +#define PLAT_DRIVER_NAME "cdns-usb3" -#define CHICKEN_APB_TIMEOUT_VALUE 0x1C20 +#define PCI_DEVICE_ID_CDNS_UDC_USBSSP 0x0400 +#define CHICKEN_APB_TIMEOUT_VALUE 0x1C20 static struct pci_dev *cdnsp_get_second_fun(struct pci_dev *pdev) { /* * Gets the second function. - * Platform has two function. The fist keeps resources for - * Host/Device while the secon keeps resources for DRD/OTG. + * Platform has two function. The first keeps resources for + * Host/Device while the second keeps resources for DRD/OTG. */ if (pdev->device == PCI_DEVICE_ID_CDNS_USBSSP) return pci_get_device(pdev->vendor, PCI_DEVICE_ID_CDNS_USBSS, NULL); @@ -48,11 +67,13 @@ static struct pci_dev *cdnsp_get_second_fun(struct pci_dev *pdev) static int cdnsp_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { - struct device *dev = &pdev->dev; - struct pci_dev *func; + struct platform_device_info plat_info; + static struct cdns3_platform_data pdata; + struct cdnsp_wrap *wrap; struct resource *res; - struct cdns *cdnsp; - int ret; + struct pci_dev *func; + bool no_drd = false; + int ret = 0; /* * For GADGET/HOST PCI (devfn) function number is 0, @@ -62,11 +83,14 @@ static int cdnsp_pci_probe(struct pci_dev *pdev, pdev->devfn != PCI_DEV_FN_OTG)) return -EINVAL; + if (pdev->device == PCI_DEVICE_ID_CDNS_UDC_USBSSP) + no_drd = true; + func = cdnsp_get_second_fun(pdev); - if (!func) + if (!func && !no_drd) return -EINVAL; - if (func->class == PCI_CLASS_SERIAL_USB_XHCI || + if ((func && func->class == PCI_CLASS_SERIAL_USB_XHCI) || pdev->class == PCI_CLASS_SERIAL_USB_XHCI) { ret = -EINVAL; goto put_pci; @@ -79,147 +103,125 @@ static int cdnsp_pci_probe(struct pci_dev *pdev, } pci_set_master(pdev); - if (pci_is_enabled(func)) { - cdnsp = pci_get_drvdata(func); + + if (func && pci_is_enabled(func)) { + wrap = pci_get_drvdata(func); } else { - cdnsp = kzalloc_obj(*cdnsp); - if (!cdnsp) { + wrap = kzalloc_obj(*wrap); + if (!wrap) { ret = -ENOMEM; goto put_pci; } } - /* For GADGET device function number is 0. */ - if (pdev->devfn == 0) { - resource_size_t rsrc_start, rsrc_len; - - /* Function 0: host(BAR_0) + device(BAR_1).*/ - dev_dbg(dev, "Initialize resources\n"); - rsrc_start = pci_resource_start(pdev, PCI_BAR_DEV); - rsrc_len = pci_resource_len(pdev, PCI_BAR_DEV); - res = devm_request_mem_region(dev, rsrc_start, rsrc_len, "dev"); - if (!res) { - dev_dbg(dev, "controller already in use\n"); - ret = -EBUSY; - goto free_cdnsp; - } - - cdnsp->dev_regs = devm_ioremap(dev, rsrc_start, rsrc_len); - if (!cdnsp->dev_regs) { - dev_dbg(dev, "error mapping memory\n"); - ret = -EFAULT; - goto free_cdnsp; - } - - cdnsp->dev_irq = pdev->irq; - dev_dbg(dev, "USBSS-DEV physical base addr: %pa\n", - &rsrc_start); - - res = &cdnsp->xhci_res[0]; - res->start = pci_resource_start(pdev, PCI_BAR_HOST); - res->end = pci_resource_end(pdev, PCI_BAR_HOST); - res->name = "xhci"; - res->flags = IORESOURCE_MEM; - dev_dbg(dev, "USBSS-XHCI physical base addr: %pa\n", - &res->start); - - /* Interrupt for XHCI, */ - res = &cdnsp->xhci_res[1]; - res->start = pdev->irq; - res->name = "host"; - res->flags = IORESOURCE_IRQ; + res = wrap->dev_res; + + if (pdev->devfn == PCI_DEV_FN_HOST_DEVICE) { + int bar_dev = no_drd ? PCI_BAR_ONLY_DEV : PCI_BAR_DEV; + + /* Function 0: host(BAR_0) + device(BAR_2). */ + dev_dbg(&pdev->dev, "Initialize Device resources\n"); + res[RES_DEV_ID].start = pci_resource_start(pdev, bar_dev); + res[RES_DEV_ID].end = pci_resource_end(pdev, bar_dev); + res[RES_DEV_ID].name = "dev"; + res[RES_DEV_ID].flags = IORESOURCE_MEM; + dev_dbg(&pdev->dev, "USBSSP-DEV physical base addr: %pa\n", + &res[RES_DEV_ID].start); + + res[RES_HOST_ID].start = pci_resource_start(pdev, PCI_BAR_HOST); + res[RES_HOST_ID].end = pci_resource_end(pdev, PCI_BAR_HOST); + res[RES_HOST_ID].name = "xhci"; + res[RES_HOST_ID].flags = IORESOURCE_MEM; + dev_dbg(&pdev->dev, "USBSSP-XHCI physical base addr: %pa\n", + &res[RES_HOST_ID].start); + + /* Interrupt for XHCI */ + wrap->dev_res[RES_IRQ_HOST_ID].start = pdev->irq; + wrap->dev_res[RES_IRQ_HOST_ID].name = "host"; + wrap->dev_res[RES_IRQ_HOST_ID].flags = IORESOURCE_IRQ; + + /* Interrupt for device. It's the same as for HOST. */ + wrap->dev_res[RES_IRQ_PERIPHERAL_ID].start = pdev->irq; + wrap->dev_res[RES_IRQ_PERIPHERAL_ID].name = "peripheral"; + wrap->dev_res[RES_IRQ_PERIPHERAL_ID].flags = IORESOURCE_IRQ; } else { - res = &cdnsp->otg_res; - res->start = pci_resource_start(pdev, PCI_BAR_OTG); - res->end = pci_resource_end(pdev, PCI_BAR_OTG); - res->name = "otg"; - res->flags = IORESOURCE_MEM; - dev_dbg(dev, "CDNSP-DRD physical base addr: %pa\n", - &res->start); + res[RES_DRD_ID].start = pci_resource_start(pdev, PCI_BAR_OTG); + res[RES_DRD_ID].end = pci_resource_end(pdev, PCI_BAR_OTG); + res[RES_DRD_ID].name = "otg"; + res[RES_DRD_ID].flags = IORESOURCE_MEM; + dev_dbg(&pdev->dev, "CDNSP-DRD physical base addr: %pa\n", + &res[RES_DRD_ID].start); /* Interrupt for OTG/DRD. */ - cdnsp->otg_irq = pdev->irq; + wrap->dev_res[RES_IRQ_OTG_ID].start = pdev->irq; + wrap->dev_res[RES_IRQ_OTG_ID].name = "otg"; + wrap->dev_res[RES_IRQ_OTG_ID].flags = IORESOURCE_IRQ; } - /* - * Cadence PCI based platform require some longer timeout for APB - * to fixes domain clock synchronization issue after resuming - * controller from L1 state. - */ - cdnsp->override_apb_timeout = CHICKEN_APB_TIMEOUT_VALUE; - pci_set_drvdata(pdev, cdnsp); - - if (pci_is_enabled(func)) { - cdnsp->dev = dev; - cdnsp->gadget_init = cdnsp_gadget_init; + if (no_drd || pci_is_enabled(func)) { + u8 idx = 0; - ret = cdns_init(cdnsp); - if (ret) - goto free_cdnsp; - } - - device_wakeup_enable(&pdev->dev); - if (pci_dev_run_wake(pdev)) - pm_runtime_put_noidle(&pdev->dev); + /* set up platform device info */ + pdata.override_apb_timeout = CHICKEN_APB_TIMEOUT_VALUE; - return 0; + if (no_drd) { + wrap->prop[idx++] = PROPERTY_ENTRY_STRING("compatible", + "cdns,cdnsp"); + wrap->prop[idx++] = PROPERTY_ENTRY_STRING("dr_mode", "peripheral"); + } else { + wrap->prop[idx++] = PROPERTY_ENTRY_STRING("dr_mode", "otg"); + wrap->prop[idx++] = PROPERTY_ENTRY_BOOL("usb-role-switch"); + } -free_cdnsp: - if (!pci_is_enabled(func)) - kfree(cdnsp); + memset(&plat_info, 0, sizeof(plat_info)); + plat_info.parent = &pdev->dev; + plat_info.fwnode = pdev->dev.fwnode; + plat_info.name = PLAT_DRIVER_NAME; + plat_info.id = pdev->devfn; + plat_info.res = wrap->dev_res; + plat_info.num_res = ARRAY_SIZE(wrap->dev_res); + plat_info.dma_mask = pdev->dma_mask; + plat_info.data = &pdata; + plat_info.size_data = sizeof(pdata); + plat_info.properties = wrap->prop; + wrap->devfn = pdev->devfn; + /* register platform device */ + wrap->plat_dev = platform_device_register_full(&plat_info); + if (IS_ERR(wrap->plat_dev)) { + ret = PTR_ERR(wrap->plat_dev); + kfree(wrap); + goto put_pci; + } + } + pci_set_drvdata(pdev, wrap); put_pci: pci_dev_put(func); - return ret; } static void cdnsp_pci_remove(struct pci_dev *pdev) { - struct cdns *cdnsp; + struct cdnsp_wrap *wrap; struct pci_dev *func; func = cdnsp_get_second_fun(pdev); - cdnsp = (struct cdns *)pci_get_drvdata(pdev); + wrap = pci_get_drvdata(pdev); - if (pci_dev_run_wake(pdev)) - pm_runtime_get_noresume(&pdev->dev); + if (wrap->devfn == pdev->devfn) + platform_device_unregister(wrap->plat_dev); - if (pci_is_enabled(func)) { - cdns_remove(cdnsp); - } else { - kfree(cdnsp); - } + if (!func || !pci_is_enabled(func)) + kfree(wrap); pci_dev_put(func); } -static int __maybe_unused cdnsp_pci_suspend(struct device *dev) -{ - struct cdns *cdns = dev_get_drvdata(dev); - - return cdns_suspend(cdns); -} - -static int __maybe_unused cdnsp_pci_resume(struct device *dev) -{ - struct cdns *cdns = dev_get_drvdata(dev); - unsigned long flags; - int ret; - - spin_lock_irqsave(&cdns->lock, flags); - ret = cdns_resume(cdns); - spin_unlock_irqrestore(&cdns->lock, flags); - cdns_set_active(cdns, 1); - - return ret; -} - -static const struct dev_pm_ops cdnsp_pci_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(cdnsp_pci_suspend, cdnsp_pci_resume) -}; - static const struct pci_device_id cdnsp_pci_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_CDNS, PCI_DEVICE_ID_CDNS_UDC_USBSSP), + .class = PCI_CLASS_SERIAL_USB_DEVICE }, + { PCI_DEVICE(PCI_VENDOR_ID_CDNS, PCI_DEVICE_ID_CDNS_UDC_USBSSP), + .class = PCI_CLASS_SERIAL_USB_CDNS }, { PCI_DEVICE(PCI_VENDOR_ID_CDNS, PCI_DEVICE_ID_CDNS_USBSSP), .class = PCI_CLASS_SERIAL_USB_DEVICE }, { PCI_DEVICE(PCI_VENDOR_ID_CDNS, PCI_DEVICE_ID_CDNS_USBSSP), @@ -230,13 +232,10 @@ static const struct pci_device_id cdnsp_pci_ids[] = { }; static struct pci_driver cdnsp_pci_driver = { - .name = "cdnsp-pci", + .name = PCI_DRIVER_NAME, .id_table = cdnsp_pci_ids, .probe = cdnsp_pci_probe, .remove = cdnsp_pci_remove, - .driver = { - .pm = &cdnsp_pci_pm_ops, - } }; module_pci_driver(cdnsp_pci_driver); @@ -245,4 +244,4 @@ MODULE_DEVICE_TABLE(pci, cdnsp_pci_ids); MODULE_ALIAS("pci:cdnsp"); MODULE_AUTHOR("Pawel Laszczak <pawell@cadence.com>"); MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("Cadence CDNSP PCI driver"); +MODULE_DESCRIPTION("Cadence CDNSP PCI wrapper"); diff --git a/drivers/usb/cdns3/cdnsp-ring.c b/drivers/usb/cdns3/cdnsp-ring.c index 0758f171f73e..715658c981ff 100644 --- a/drivers/usb/cdns3/cdnsp-ring.c +++ b/drivers/usb/cdns3/cdnsp-ring.c @@ -259,7 +259,7 @@ static bool cdnsp_room_on_ring(struct cdnsp_device *pdev, */ static void cdnsp_force_l0_go(struct cdnsp_device *pdev) { - if (pdev->active_port == &pdev->usb2_port && pdev->gadget.lpm_capable) + if (pdev->active_port != &pdev->usb3_port && pdev->gadget.lpm_capable) cdnsp_set_link_state(pdev, &pdev->active_port->regs->portsc, XDEV_U0); } @@ -763,6 +763,8 @@ static int cdnsp_update_port_id(struct cdnsp_device *pdev, u32 port_id) if (port_id == pdev->usb2_port.port_num) { port = &pdev->usb2_port; + } else if (port_id == pdev->eusb_port.port_num) { + port = &pdev->eusb_port; } else if (port_id == pdev->usb3_port.port_num) { port = &pdev->usb3_port; } else { @@ -779,7 +781,8 @@ static int cdnsp_update_port_id(struct cdnsp_device *pdev, u32 port_id) cdnsp_enable_slot(pdev); } - if (port_id == pdev->usb2_port.port_num) + if ((pdev->usb2_port.exist && port_id == pdev->usb2_port.port_num) || + (pdev->eusb_port.exist && port_id == pdev->eusb_port.port_num)) cdnsp_set_usb2_hardware_lpm(pdev, NULL, 1); else writel(PORT_U1_TIMEOUT(1) | PORT_U2_TIMEOUT(1), @@ -808,7 +811,7 @@ static void cdnsp_handle_port_status(struct cdnsp_device *pdev, port_regs = pdev->active_port->regs; - if (port_id == pdev->usb2_port.port_num) + if (port_id == pdev->usb2_port.port_num || port_id == pdev->eusb_port.port_num) port2 = true; new_event: diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c index f0e32227c0b7..504bdf13ea80 100644 --- a/drivers/usb/cdns3/core.c +++ b/drivers/usb/cdns3/core.c @@ -21,7 +21,6 @@ #include <linux/pm_runtime.h> #include "core.h" -#include "host-export.h" #include "drd.h" static int cdns_idle_init(struct cdns *cdns); @@ -71,7 +70,8 @@ static void cdns_role_stop(struct cdns *cdns) static void cdns_exit_roles(struct cdns *cdns) { cdns_role_stop(cdns); - cdns_drd_exit(cdns); + if (!cdns->no_drd) + cdns_drd_exit(cdns); } /** @@ -80,7 +80,7 @@ static void cdns_exit_roles(struct cdns *cdns) * * Returns 0 on success otherwise negative errno */ -static int cdns_core_init_role(struct cdns *cdns) +int cdns_core_init_role(struct cdns *cdns) { struct device *dev = cdns->dev; enum usb_dr_mode best_dr_mode; @@ -96,23 +96,13 @@ static int cdns_core_init_role(struct cdns *cdns) * can be restricted later depending on strap pin configuration. */ if (dr_mode == USB_DR_MODE_UNKNOWN) { - if (cdns->version == CDNSP_CONTROLLER_V2) { - if (IS_ENABLED(CONFIG_USB_CDNSP_HOST) && - IS_ENABLED(CONFIG_USB_CDNSP_GADGET)) - dr_mode = USB_DR_MODE_OTG; - else if (IS_ENABLED(CONFIG_USB_CDNSP_HOST)) - dr_mode = USB_DR_MODE_HOST; - else if (IS_ENABLED(CONFIG_USB_CDNSP_GADGET)) - dr_mode = USB_DR_MODE_PERIPHERAL; - } else { - if (IS_ENABLED(CONFIG_USB_CDNS3_HOST) && - IS_ENABLED(CONFIG_USB_CDNS3_GADGET)) - dr_mode = USB_DR_MODE_OTG; - else if (IS_ENABLED(CONFIG_USB_CDNS3_HOST)) - dr_mode = USB_DR_MODE_HOST; - else if (IS_ENABLED(CONFIG_USB_CDNS3_GADGET)) - dr_mode = USB_DR_MODE_PERIPHERAL; - } + if (IS_ENABLED(CONFIG_USB_CDNS3_HOST) && + IS_ENABLED(CONFIG_USB_CDNS3_GADGET)) + dr_mode = USB_DR_MODE_OTG; + else if (IS_ENABLED(CONFIG_USB_CDNS3_HOST)) + dr_mode = USB_DR_MODE_HOST; + else if (IS_ENABLED(CONFIG_USB_CDNS3_GADGET)) + dr_mode = USB_DR_MODE_PERIPHERAL; } /* @@ -137,11 +127,8 @@ static int cdns_core_init_role(struct cdns *cdns) dr_mode = best_dr_mode; if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_HOST) { - if ((cdns->version == CDNSP_CONTROLLER_V2 && - IS_ENABLED(CONFIG_USB_CDNSP_HOST)) || - (cdns->version < CDNSP_CONTROLLER_V2 && - IS_ENABLED(CONFIG_USB_CDNS3_HOST))) - ret = cdns_host_init(cdns); + if (cdns->host_init) + ret = cdns->host_init(cdns); else ret = -ENXIO; @@ -197,11 +184,14 @@ static int cdns_core_init_role(struct cdns *cdns) goto err; } + dev_dbg(dev, "Cadence USB3 core: probe succeed\n"); + return 0; err: cdns_exit_roles(cdns); return ret; } +EXPORT_SYMBOL_GPL(cdns_core_init_role); /** * cdns_hw_role_state_machine - role switch state machine based on hw events. @@ -469,14 +459,8 @@ int cdns_init(struct cdns *cdns) if (ret) goto init_failed; - ret = cdns_core_init_role(cdns); - if (ret) - goto init_failed; - spin_lock_init(&cdns->lock); - dev_dbg(dev, "Cadence USB3 core: probe succeed\n"); - return 0; init_failed: cdns_drd_exit(cdns); @@ -576,5 +560,5 @@ EXPORT_SYMBOL_GPL(cdns_set_active); MODULE_AUTHOR("Peter Chen <peter.chen@nxp.com>"); MODULE_AUTHOR("Pawel Laszczak <pawell@cadence.com>"); MODULE_AUTHOR("Roger Quadros <rogerq@ti.com>"); -MODULE_DESCRIPTION("Cadence USBSS and USBSSP DRD Driver"); +MODULE_DESCRIPTION("Cadence USBSS/USBSSP DRD driver (core, DRD, platform, optional host/gadget)"); MODULE_LICENSE("GPL"); diff --git a/drivers/usb/cdns3/core.h b/drivers/usb/cdns3/core.h index 801be9e61340..8c492fda924c 100644 --- a/drivers/usb/cdns3/core.h +++ b/drivers/usb/cdns3/core.h @@ -45,6 +45,7 @@ struct cdns3_platform_data { unsigned long quirks; #define CDNS3_DEFAULT_PM_RUNTIME_ALLOW BIT(0) #define CDNS3_DRD_SUSPEND_RESIDENCY_ENABLE BIT(1) + u32 override_apb_timeout; /* 0 = use default (e.g. for PCI) */ }; /** @@ -82,6 +83,10 @@ struct cdns3_platform_data { * @override_apb_timeout: hold value of APB timeout. For value 0 the default * value in CHICKEN_BITS_3 will be preserved. * @gadget_init: pointer to gadget initialization function + * @host_init: pointer to host initialization function + * @no_drd: DRD register block is inaccessible. The controller is hardwired to + * single role (host or device) or the logic for role switching is + * missing. */ struct cdns { struct device *dev; @@ -120,13 +125,15 @@ struct cdns { spinlock_t lock; struct xhci_plat_priv *xhci_plat_data; u32 override_apb_timeout; - int (*gadget_init)(struct cdns *cdns); + int (*host_init)(struct cdns *cdns); + bool no_drd; }; int cdns_hw_role_switch(struct cdns *cdns); int cdns_init(struct cdns *cdns); int cdns_remove(struct cdns *cdns); +int cdns_core_init_role(struct cdns *cdns); #ifdef CONFIG_PM_SLEEP int cdns_resume(struct cdns *cdns); diff --git a/drivers/usb/cdns3/drd.c b/drivers/usb/cdns3/drd.c index 84fb38a5723a..d2bb682e4552 100644 --- a/drivers/usb/cdns3/drd.c +++ b/drivers/usb/cdns3/drd.c @@ -87,6 +87,9 @@ int cdns_get_id(struct cdns *cdns) { int id; + if (cdns->no_drd) + return 0; + id = readl(&cdns->otg_regs->sts) & OTGSTS_ID_VALUE; dev_dbg(cdns->dev, "OTG ID: %d", id); @@ -107,7 +110,7 @@ void cdns_clear_vbus(struct cdns *cdns) { u32 reg; - if (cdns->version != CDNSP_CONTROLLER_V2) + if (cdns->version != CDNSP_CONTROLLER_V2 || cdns->no_drd) return; reg = readl(&cdns->otg_cdnsp_regs->override); @@ -120,7 +123,7 @@ void cdns_set_vbus(struct cdns *cdns) { u32 reg; - if (cdns->version != CDNSP_CONTROLLER_V2) + if (cdns->version != CDNSP_CONTROLLER_V2 || cdns->no_drd) return; reg = readl(&cdns->otg_cdnsp_regs->override); @@ -179,7 +182,10 @@ static void cdns_otg_enable_irq(struct cdns *cdns) int cdns_drd_host_on(struct cdns *cdns) { u32 val, ready_bit; - int ret; + int ret = 0; + + if (cdns->no_drd) + goto phy_set; /* Enable host mode. */ writel(OTGCMD_HOST_BUS_REQ | OTGCMD_OTG_DIS, @@ -197,6 +203,7 @@ int cdns_drd_host_on(struct cdns *cdns) if (ret) dev_err(cdns->dev, "timeout waiting for xhci_ready\n"); +phy_set: phy_set_mode(cdns->usb2_phy, PHY_MODE_USB_HOST); phy_set_mode(cdns->usb3_phy, PHY_MODE_USB_HOST); return ret; @@ -210,6 +217,9 @@ void cdns_drd_host_off(struct cdns *cdns) { u32 val; + if (cdns->no_drd) + goto phy_set; + writel(OTGCMD_HOST_BUS_DROP | OTGCMD_DEV_BUS_DROP | OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF, &cdns->otg_regs->cmd); @@ -218,6 +228,8 @@ void cdns_drd_host_off(struct cdns *cdns) readl_poll_timeout_atomic(&cdns->otg_regs->state, val, !(val & OTGSTATE_HOST_STATE_MASK), 1, 2000000); + +phy_set: phy_set_mode(cdns->usb2_phy, PHY_MODE_INVALID); phy_set_mode(cdns->usb3_phy, PHY_MODE_INVALID); } @@ -234,6 +246,9 @@ int cdns_drd_gadget_on(struct cdns *cdns) u32 ready_bit; int ret, val; + if (cdns->no_drd) + goto phy_set; + /* switch OTG core */ writel(OTGCMD_DEV_BUS_REQ | reg, &cdns->otg_regs->cmd); @@ -251,6 +266,7 @@ int cdns_drd_gadget_on(struct cdns *cdns) return ret; } +phy_set: phy_set_mode(cdns->usb2_phy, PHY_MODE_USB_DEVICE); phy_set_mode(cdns->usb3_phy, PHY_MODE_USB_DEVICE); return 0; @@ -265,6 +281,9 @@ void cdns_drd_gadget_off(struct cdns *cdns) { u32 val; + if (cdns->no_drd) + goto phy_set; + /* * Driver should wait at least 10us after disabling Device * before turning-off Device (DEV_BUS_DROP). @@ -277,6 +296,8 @@ void cdns_drd_gadget_off(struct cdns *cdns) readl_poll_timeout_atomic(&cdns->otg_regs->state, val, !(val & OTGSTATE_DEV_STATE_MASK), 1, 2000000); + +phy_set: phy_set_mode(cdns->usb2_phy, PHY_MODE_INVALID); phy_set_mode(cdns->usb3_phy, PHY_MODE_INVALID); } @@ -392,6 +413,18 @@ int cdns_drd_init(struct cdns *cdns) u32 state, reg; int ret; + if (cdns->no_drd) { + cdns->dr_mode = usb_get_dr_mode(cdns->dev); + + if (cdns->dr_mode != USB_DR_MODE_HOST && + cdns->dr_mode != USB_DR_MODE_PERIPHERAL) { + dev_err(cdns->dev, "Incorrect dr_mode\n"); + return -EINVAL; + } + + return 0; + } + regs = devm_ioremap_resource(cdns->dev, &cdns->otg_res); if (IS_ERR(regs)) return PTR_ERR(regs); @@ -492,6 +525,9 @@ int cdns_drd_init(struct cdns *cdns) int cdns_drd_exit(struct cdns *cdns) { + if (cdns->no_drd) + return 0; + cdns_otg_disable_irq(cdns); return 0; @@ -500,6 +536,9 @@ int cdns_drd_exit(struct cdns *cdns) /* Indicate the cdns3 core was power lost before */ bool cdns_power_is_lost(struct cdns *cdns) { + if (cdns->no_drd) + return false; + if (cdns->version == CDNS3_CONTROLLER_V0) { if (!(readl(&cdns->otg_v0_regs->simulate) & BIT(0))) return true; diff --git a/drivers/usb/cdns3/gadget-export.h b/drivers/usb/cdns3/gadget-export.h index c37b6269b001..60c3177db62c 100644 --- a/drivers/usb/cdns3/gadget-export.h +++ b/drivers/usb/cdns3/gadget-export.h @@ -10,9 +10,10 @@ #ifndef __LINUX_CDNS3_GADGET_EXPORT #define __LINUX_CDNS3_GADGET_EXPORT -#if IS_ENABLED(CONFIG_USB_CDNSP_GADGET) +#if IS_ENABLED(CONFIG_USB_CDNS3_GADGET) int cdnsp_gadget_init(struct cdns *cdns); +int cdns3_gadget_init(struct cdns *cdns); #else static inline int cdnsp_gadget_init(struct cdns *cdns) @@ -20,13 +21,6 @@ static inline int cdnsp_gadget_init(struct cdns *cdns) return -ENXIO; } -#endif /* CONFIG_USB_CDNSP_GADGET */ - -#if IS_ENABLED(CONFIG_USB_CDNS3_GADGET) - -int cdns3_gadget_init(struct cdns *cdns); -#else - static inline int cdns3_gadget_init(struct cdns *cdns) { return -ENXIO; diff --git a/drivers/usb/cdns3/host-export.h b/drivers/usb/cdns3/host-export.h index cf92173ecf00..34fd1f1ad59d 100644 --- a/drivers/usb/cdns3/host-export.h +++ b/drivers/usb/cdns3/host-export.h @@ -9,7 +9,7 @@ #ifndef __LINUX_CDNS3_HOST_EXPORT #define __LINUX_CDNS3_HOST_EXPORT -#if IS_ENABLED(CONFIG_USB_CDNS_HOST) +#if IS_ENABLED(CONFIG_USB_CDNS3_HOST) int cdns_host_init(struct cdns *cdns); @@ -22,6 +22,6 @@ static inline int cdns_host_init(struct cdns *cdns) static inline void cdns_host_exit(struct cdns *cdns) { } -#endif /* USB_CDNS_HOST */ +#endif /* CONFIG_USB_CDNS3_HOST */ #endif /* __LINUX_CDNS3_HOST_EXPORT */ diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 2ab3db3c1015..07563be0013f 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -1187,19 +1187,16 @@ static int ci_hdrc_probe(struct platform_device *pdev) ci->role = ci_get_role(ci); if (!ci_otg_is_fsm_mode(ci)) { - /* only update vbus status for peripheral */ - if (ci->role == CI_ROLE_GADGET) { - /* Pull down DP for possible charger detection */ - hw_write(ci, OP_USBCMD, USBCMD_RS, 0); - ci_handle_vbus_change(ci); - } - ret = ci_role_start(ci, ci->role); if (ret) { dev_err(dev, "can't start %s role\n", ci_role(ci)->name); goto stop; } + + /* only update vbus status for peripheral */ + if (ci->role == CI_ROLE_GADGET) + ci_handle_vbus_change(ci); } ret = devm_request_irq(dev, ci->irq, ci_irq_handler, IRQF_SHARED, diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index f2de86d0ce40..d52f89489893 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -1835,6 +1835,20 @@ static const struct usb_ep_ops usb_ep_ops = { * GADGET block *****************************************************************************/ +static void ci_udc_enable_vbus_irq(struct ci_hdrc *ci, bool enable) +{ + u32 reg = OTGSC_BSVIS; + + if (!ci->is_otg) + return; + + if (enable) + reg |= OTGSC_BSVIE; + + /* Clear pending BSVIS and enable/disable BSVIE */ + hw_write_otgsc(ci, OTGSC_BSVIE | OTGSC_BSVIS, reg); +} + static int ci_udc_get_frame(struct usb_gadget *_gadget) { struct ci_hdrc *ci = container_of(_gadget, struct ci_hdrc, gadget); @@ -2030,6 +2044,8 @@ static int init_eps(struct ci_hdrc *ci) { int retval = 0, i, j; + memset(ci->ci_hw_ep, 0, sizeof(ci->ci_hw_ep)); + for (i = 0; i < ci->hw_ep_max/2; i++) for (j = RX; j <= TX; j++) { int k = i + j * ci->hw_ep_max/2; @@ -2275,6 +2291,8 @@ static int udc_start(struct ci_hdrc *ci) struct usb_otg_caps *otg_caps = &ci->platdata->ci_otg_caps; int retval = 0; + memset(&ci->gadget, 0, sizeof(ci->gadget)); + ci->gadget.ops = &usb_gadget_ops; ci->gadget.speed = USB_SPEED_UNKNOWN; ci->gadget.max_speed = USB_SPEED_HIGH; @@ -2313,10 +2331,15 @@ static int udc_start(struct ci_hdrc *ci) ci->gadget.ep0 = &ci->ep0in->ep; + if (ci->platdata->pins_device) + pinctrl_select_state(ci->platdata->pctl, + ci->platdata->pins_device); + retval = usb_add_gadget_udc(dev, &ci->gadget); if (retval) goto destroy_eps; + ci_udc_enable_vbus_irq(ci, true); return retval; destroy_eps: @@ -2328,48 +2351,20 @@ free_qh_pool: return retval; } -/* - * ci_hdrc_gadget_destroy: parent remove must call this to remove UDC - * - * No interrupts active, the IRQ has been released +/** + * udc_stop: deinitialize gadget role + * @ci: chipidea controller */ -void ci_hdrc_gadget_destroy(struct ci_hdrc *ci) +static void udc_stop(struct ci_hdrc *ci) { - if (!ci->roles[CI_ROLE_GADGET]) - return; - + ci_udc_enable_vbus_irq(ci, false); usb_del_gadget_udc(&ci->gadget); + ci->vbus_active = 0; destroy_eps(ci); dma_pool_destroy(ci->td_pool); dma_pool_destroy(ci->qh_pool); -} - -static int udc_id_switch_for_device(struct ci_hdrc *ci) -{ - if (ci->platdata->pins_device) - pinctrl_select_state(ci->platdata->pctl, - ci->platdata->pins_device); - - if (ci->is_otg) - /* Clear and enable BSV irq */ - hw_write_otgsc(ci, OTGSC_BSVIS | OTGSC_BSVIE, - OTGSC_BSVIS | OTGSC_BSVIE); - - return 0; -} - -static void udc_id_switch_for_host(struct ci_hdrc *ci) -{ - /* - * host doesn't care B_SESSION_VALID event - * so clear and disable BSV irq - */ - if (ci->is_otg) - hw_write_otgsc(ci, OTGSC_BSVIE | OTGSC_BSVIS, OTGSC_BSVIS); - - ci->vbus_active = 0; if (ci->platdata->pins_device && ci->platdata->pins_default) pinctrl_select_state(ci->platdata->pctl, @@ -2395,9 +2390,7 @@ static void udc_suspend(struct ci_hdrc *ci) static void udc_resume(struct ci_hdrc *ci, bool power_lost) { if (power_lost) { - if (ci->is_otg) - hw_write_otgsc(ci, OTGSC_BSVIS | OTGSC_BSVIE, - OTGSC_BSVIS | OTGSC_BSVIE); + ci_udc_enable_vbus_irq(ci, true); if (ci->vbus_active) usb_gadget_vbus_disconnect(&ci->gadget); } else if (ci->vbus_active && ci->driver && @@ -2420,7 +2413,6 @@ static void udc_resume(struct ci_hdrc *ci, bool power_lost) int ci_hdrc_gadget_init(struct ci_hdrc *ci) { struct ci_role_driver *rdrv; - int ret; if (!hw_read(ci, CAP_DCCPARAMS, DCCPARAMS_DC)) return -ENXIO; @@ -2429,8 +2421,8 @@ int ci_hdrc_gadget_init(struct ci_hdrc *ci) if (!rdrv) return -ENOMEM; - rdrv->start = udc_id_switch_for_device; - rdrv->stop = udc_id_switch_for_host; + rdrv->start = udc_start; + rdrv->stop = udc_stop; #ifdef CONFIG_PM_SLEEP rdrv->suspend = udc_suspend; rdrv->resume = udc_resume; @@ -2438,9 +2430,22 @@ int ci_hdrc_gadget_init(struct ci_hdrc *ci) rdrv->irq = udc_irq; rdrv->name = "gadget"; - ret = udc_start(ci); - if (!ret) - ci->roles[CI_ROLE_GADGET] = rdrv; + ci->roles[CI_ROLE_GADGET] = rdrv; - return ret; + /* Pull down DP for possible charger detection */ + hw_write(ci, OP_USBCMD, USBCMD_RS, 0); + return 0; +} + +/* + * ci_hdrc_gadget_destroy: parent remove must call this to remove UDC + * + * No interrupts active, the IRQ has been released + */ +void ci_hdrc_gadget_destroy(struct ci_hdrc *ci) +{ + struct device *dev = &ci->gadget.dev; + + if (ci->roles[CI_ROLE_GADGET] && device_is_registered(dev)) + udc_stop(ci); } diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index ddf0b5963859..7bc5329fa3ed 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -797,6 +797,9 @@ static void acm_port_shutdown(struct tty_port *port) "ctrl polling restart failed after port close\n"); /* port_shutdown() cleared DTR/RTS; restore them */ acm_set_control(acm, USB_CDC_CTRL_DTR | USB_CDC_CTRL_RTS); + if (acm_submit_read_urbs(acm, GFP_KERNEL)) + dev_dbg(&acm->control->dev, + "read urb restart failed after port close\n"); } } @@ -1564,6 +1567,9 @@ skip_countries: if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) dev_warn(&intf->dev, "failed to start persistent ctrl polling\n"); + if (acm_submit_read_urbs(acm, GFP_KERNEL)) + dev_warn(&intf->dev, + "failed to start persistent bulk read polling\n"); } return 0; @@ -1810,6 +1816,9 @@ static const struct usb_device_id acm_ids[] = { { USB_DEVICE(0x1901, 0x0006), /* GE Healthcare Patient Monitor UI Controller */ .driver_info = DISABLE_ECHO, /* DISABLE ECHO in termios flag */ }, + { USB_DEVICE(0x1965, 0x0017), /* Uniden BC125AT */ + .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ + }, { USB_DEVICE(0x1965, 0x0018), /* Uniden UBC125XLT */ .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ }, diff --git a/drivers/usb/common/ulpi.c b/drivers/usb/common/ulpi.c index 9b69148128e5..7e43429e996e 100644 --- a/drivers/usb/common/ulpi.c +++ b/drivers/usb/common/ulpi.c @@ -281,28 +281,24 @@ static int ulpi_register(struct device *dev, struct ulpi *ulpi) ulpi->dev.parent = dev; /* needed early for ops */ ulpi->dev.bus = &ulpi_bus; ulpi->dev.type = &ulpi_dev_type; + + device_initialize(&ulpi->dev); + dev_set_name(&ulpi->dev, "%s.ulpi", dev_name(dev)); ACPI_COMPANION_SET(&ulpi->dev, ACPI_COMPANION(dev)); ret = ulpi_of_register(ulpi); - if (ret) { - kfree(ulpi); + if (ret) return ret; - } ret = ulpi_read_id(ulpi); - if (ret) { - of_node_put(ulpi->dev.of_node); - kfree(ulpi); + if (ret) return ret; - } - ret = device_register(&ulpi->dev); - if (ret) { - put_device(&ulpi->dev); + ret = device_add(&ulpi->dev); + if (ret) return ret; - } root = debugfs_create_dir(dev_name(&ulpi->dev), ulpi_root); debugfs_create_file("regs", 0444, root, ulpi, &ulpi_regs_fops); @@ -334,9 +330,10 @@ struct ulpi *ulpi_register_interface(struct device *dev, ulpi->ops = ops; ret = ulpi_register(dev, ulpi); - if (ret) + if (ret) { + put_device(&ulpi->dev); return ERR_PTR(ret); - + } return ulpi; } diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index b181b43a35dc..8e8a5f59b319 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -446,7 +446,8 @@ rh_string(int id, struct usb_hcd const *hcd, u8 *data, unsigned len) /* Root hub control transfers execute synchronously */ -static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) +static int rh_call_control(struct usb_hcd *hcd, + struct urb *urb, gfp_t mem_flags) { struct usb_ctrlrequest *cmd; u16 typeReq, wValue, wIndex, wLength; @@ -481,8 +482,8 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) * tbuf should be at least as big as the * USB hub descriptor. */ - tbuf_size = max_t(u16, sizeof(struct usb_hub_descriptor), wLength); - tbuf = kzalloc(tbuf_size, GFP_KERNEL); + tbuf_size = max_t(u16, sizeof(struct usb_hub_descriptor), wLength); + tbuf = kzalloc(tbuf_size, mem_flags); if (!tbuf) { status = -ENOMEM; goto err_alloc; @@ -807,12 +808,13 @@ static int rh_queue_status (struct usb_hcd *hcd, struct urb *urb) return retval; } -static int rh_urb_enqueue (struct usb_hcd *hcd, struct urb *urb) +static int rh_urb_enqueue(struct usb_hcd *hcd, + struct urb *urb, gfp_t mem_flags) { if (usb_endpoint_xfer_int(&urb->ep->desc)) return rh_queue_status (hcd, urb); if (usb_endpoint_xfer_control(&urb->ep->desc)) - return rh_call_control (hcd, urb); + return rh_call_control(hcd, urb, mem_flags); return -EINVAL; } @@ -1533,7 +1535,7 @@ int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags) */ if (is_root_hub(urb->dev)) { - status = rh_urb_enqueue(hcd, urb); + status = rh_urb_enqueue(hcd, urb, mem_flags); } else { status = map_urb_for_dma(hcd, urb, mem_flags); if (likely(status == 0)) { diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 24960ba9caa9..5262e11c12cd 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -3148,7 +3148,7 @@ static int hub_port_reset(struct usb_hub *hub, int port1, delay = HUB_LONG_RESET_TIME; } - dev_err(&port_dev->dev, "Cannot enable. Maybe the USB cable is bad?\n"); + dev_err_ratelimited(&port_dev->dev, "Cannot enable. Maybe the USB cable is bad?\n"); done: if (status == 0) { diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 87810eff974e..87ee2d938bc0 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -296,6 +296,9 @@ static const struct usb_device_id usb_quirk_list[] = { /* CarrolTouch 4500U */ { USB_DEVICE(0x04e7, 0x0030), .driver_info = USB_QUIRK_RESET_RESUME }, + /* Samsung T5 EVO Portable SSD */ + { USB_DEVICE(0x04e8, 0x6200), .driver_info = USB_QUIRK_NO_LPM }, + /* Samsung Android phone modem - ID conflict with SPH-I500 */ { USB_DEVICE(0x04e8, 0x6601), .driver_info = USB_QUIRK_CONFIG_INTF_STRINGS }, @@ -576,6 +579,9 @@ static const struct usb_device_id usb_quirk_list[] = { /* VLI disk */ { USB_DEVICE(0x2109, 0x0711), .driver_info = USB_QUIRK_NO_LPM }, + /* VIA Labs, Inc. USB2.0 Hub */ + { USB_DEVICE(0x2109, 0x2817), .driver_info = USB_QUIRK_NO_LPM }, + /* Raydium Touchscreen */ { USB_DEVICE(0x2386, 0x3114), .driver_info = USB_QUIRK_NO_LPM }, diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 34127b890b2a..767251aa1aa3 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -9,6 +9,7 @@ #define __DWC2_CORE_H__ #include <linux/acpi.h> +#include <linux/device-id/pci.h> #include <linux/phy/phy.h> #include <linux/regulator/consumer.h> #include <linux/usb/gadget.h> diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c index 5c7538d498dd..f380275ac696 100644 --- a/drivers/usb/dwc2/hcd_intr.c +++ b/drivers/usb/dwc2/hcd_intr.c @@ -515,18 +515,20 @@ void dwc2_hcd_save_data_toggle(struct dwc2_hsotg *hsotg, u32 pid = (hctsiz & TSIZ_SC_MC_PID_MASK) >> TSIZ_SC_MC_PID_SHIFT; if (chan->ep_type != USB_ENDPOINT_XFER_CONTROL) { - if (WARN(!chan || !chan->qh, - "chan->qh must be specified for non-control eps\n")) + if (!chan->qh) { + dev_err(hsotg->dev, "chan->qh must be specified for non-control eps\n"); return; + } if (pid == TSIZ_SC_MC_PID_DATA0) chan->qh->data_toggle = DWC2_HC_PID_DATA0; else chan->qh->data_toggle = DWC2_HC_PID_DATA1; } else { - if (WARN(!qtd, - "qtd must be specified for control eps\n")) + if (!qtd) { + dev_err(hsotg->dev, "qtd must be specified for control eps\n"); return; + } if (pid == TSIZ_SC_MC_PID_DATA0) qtd->data_toggle = DWC2_HC_PID_DATA0; diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 65213896de99..ceb49f2f8004 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -425,8 +425,7 @@ static void dwc3_ref_clk_period(struct dwc3 *dwc) } reg = dwc3_readl(dwc, DWC3_GUCTL); - reg &= ~DWC3_GUCTL_REFCLKPER_MASK; - reg |= FIELD_PREP(DWC3_GUCTL_REFCLKPER_MASK, period); + FIELD_MODIFY(DWC3_GUCTL_REFCLKPER_MASK, ®, period); dwc3_writel(dwc, DWC3_GUCTL, reg); if (DWC3_VER_IS_PRIOR(DWC3, 250A)) @@ -456,12 +455,9 @@ static void dwc3_ref_clk_period(struct dwc3 *dwc) decr = 480000000 / rate; reg = dwc3_readl(dwc, DWC3_GFLADJ); - reg &= ~DWC3_GFLADJ_REFCLK_FLADJ_MASK - & ~DWC3_GFLADJ_240MHZDECR - & ~DWC3_GFLADJ_240MHZDECR_PLS1; - reg |= FIELD_PREP(DWC3_GFLADJ_REFCLK_FLADJ_MASK, fladj) - | FIELD_PREP(DWC3_GFLADJ_240MHZDECR, decr >> 1) - | FIELD_PREP(DWC3_GFLADJ_240MHZDECR_PLS1, decr & 1); + FIELD_MODIFY(DWC3_GFLADJ_REFCLK_FLADJ_MASK, ®, fladj); + FIELD_MODIFY(DWC3_GFLADJ_240MHZDECR, ®, decr >> 1); + FIELD_MODIFY(DWC3_GFLADJ_240MHZDECR_PLS1, ®, decr & 1); if (dwc->gfladj_refclk_lpm_sel) reg |= DWC3_GFLADJ_REFCLK_LPM_SEL; @@ -525,7 +521,7 @@ static void dwc3_free_event_buffers(struct dwc3 *dwc) } /** - * dwc3_alloc_event_buffers - Allocates @num event buffers of size @length + * dwc3_alloc_event_buffers - Allocate one event buffer of size @length * @dwc: pointer to our controller context structure * @length: size of event buffer * @@ -793,9 +789,9 @@ static void dwc3_ulpi_setup(struct dwc3 *dwc) if (dwc->enable_usb2_transceiver_delay) { for (index = 0; index < dwc->num_usb2_ports; index++) { - reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(index)); + reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(index)); reg |= DWC3_GUSB2PHYCFG_XCVRDLY; - dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(index), reg); + dwc3_writel(dwc, DWC3_GUSB2PHYCFG(index), reg); } } } diff --git a/drivers/usb/dwc3/dwc3-google.c b/drivers/usb/dwc3/dwc3-google.c index 4ca567ec01d0..60ee4cc99b28 100644 --- a/drivers/usb/dwc3/dwc3-google.c +++ b/drivers/usb/dwc3/dwc3-google.c @@ -104,9 +104,8 @@ static int dwc3_google_set_pmu_state(struct dwc3_google *google, int state) regmap_read(google->usb_cfg_regmap, google->host_cfg_offset + HOST_CFG1_OFFSET, ®); - reg &= ~HOST_CFG1_PM_POWER_STATE_REQUEST; - reg |= (FIELD_PREP(HOST_CFG1_PM_POWER_STATE_REQUEST, state) | - HOST_CFG1_PME_EN); + FIELD_MODIFY(HOST_CFG1_PM_POWER_STATE_REQUEST, ®, state); + reg |= HOST_CFG1_PME_EN; regmap_write(google->usb_cfg_regmap, google->host_cfg_offset + HOST_CFG1_OFFSET, reg); diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c index 55e144ba8cfc..4d611c08e8a4 100644 --- a/drivers/usb/dwc3/dwc3-meson-g12a.c +++ b/drivers/usb/dwc3/dwc3-meson-g12a.c @@ -907,35 +907,39 @@ static int __maybe_unused dwc3_meson_g12a_resume(struct device *dev) ret = priv->drvdata->usb_init(priv); if (ret) - return ret; + goto err_rearm; /* Init PHYs */ for (i = 0 ; i < PHY_COUNT ; ++i) { ret = phy_init(priv->phys[i]); if (ret) - return ret; + goto err_rearm; } /* Set PHY Power */ for (i = 0 ; i < PHY_COUNT ; ++i) { ret = phy_power_on(priv->phys[i]); if (ret) - return ret; + goto err_rearm; } if (priv->vbus && priv->otg_phy_mode == PHY_MODE_USB_HOST) { ret = regulator_enable(priv->vbus); if (ret) - return ret; + goto err_rearm; } if (priv->drvdata->usb_post_init) { ret = priv->drvdata->usb_post_init(priv); if (ret) - return ret; + goto err_rearm; } return 0; + +err_rearm: + reset_control_rearm(priv->reset); + return ret; } static const struct dev_pm_ops dwc3_meson_g12a_dev_pm_ops = { diff --git a/drivers/usb/dwc3/dwc3-octeon.c b/drivers/usb/dwc3/dwc3-octeon.c index 42bfc14ae0c4..2201f0f34abb 100644 --- a/drivers/usb/dwc3/dwc3-octeon.c +++ b/drivers/usb/dwc3/dwc3-octeon.c @@ -296,8 +296,7 @@ static int dwc3_octeon_setup(struct dwc3_octeon *octeon, return div; } val = dwc3_octeon_readq(uctl_ctl_reg); - val &= ~USBDRD_UCTL_CTL_H_CLKDIV_SEL; - val |= FIELD_PREP(USBDRD_UCTL_CTL_H_CLKDIV_SEL, div); + FIELD_MODIFY(USBDRD_UCTL_CTL_H_CLKDIV_SEL, &val, div); val |= USBDRD_UCTL_CTL_H_CLK_EN; dwc3_octeon_writeq(uctl_ctl_reg, val); val = dwc3_octeon_readq(uctl_ctl_reg); @@ -314,14 +313,11 @@ static int dwc3_octeon_setup(struct dwc3_octeon *octeon, /* Step 5a: Reference clock configuration. */ val = dwc3_octeon_readq(uctl_ctl_reg); val &= ~USBDRD_UCTL_CTL_REF_CLK_DIV2; - val &= ~USBDRD_UCTL_CTL_REF_CLK_SEL; - val |= FIELD_PREP(USBDRD_UCTL_CTL_REF_CLK_SEL, ref_clk_sel); + FIELD_MODIFY(USBDRD_UCTL_CTL_REF_CLK_SEL, &val, ref_clk_sel); - val &= ~USBDRD_UCTL_CTL_REF_CLK_FSEL; - val |= FIELD_PREP(USBDRD_UCTL_CTL_REF_CLK_FSEL, ref_clk_fsel); + FIELD_MODIFY(USBDRD_UCTL_CTL_REF_CLK_FSEL, &val, ref_clk_fsel); - val &= ~USBDRD_UCTL_CTL_MPLL_MULTIPLIER; - val |= FIELD_PREP(USBDRD_UCTL_CTL_MPLL_MULTIPLIER, mpll_mul); + FIELD_MODIFY(USBDRD_UCTL_CTL_MPLL_MULTIPLIER, &val, mpll_mul); /* Step 5b: Configure and enable spread-spectrum for SuperSpeed. */ val |= USBDRD_UCTL_CTL_SSC_EN; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 3d4ca68e584c..1082e9c9afaa 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -3934,15 +3934,48 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc, } } +static bool dwc3_prepare_disconnect_gadget(struct dwc3 *dwc, + struct usb_gadget_driver **driver, + struct usb_gadget **gadget) +{ + if (!dwc->async_callbacks || !dwc->gadget_driver || + !dwc->gadget_driver->disconnect) + return false; + + *driver = dwc->gadget_driver; + *gadget = dwc->gadget; + + return true; +} + static void dwc3_disconnect_gadget(struct dwc3 *dwc) { - if (dwc->async_callbacks && dwc->gadget_driver->disconnect) { + struct usb_gadget_driver *driver; + struct usb_gadget *gadget; + + if (dwc3_prepare_disconnect_gadget(dwc, &driver, &gadget)) { spin_unlock(&dwc->lock); - dwc->gadget_driver->disconnect(dwc->gadget); + driver->disconnect(gadget); spin_lock(&dwc->lock); } } +static void dwc3_disconnect_gadget_sleepable(struct dwc3 *dwc) +{ + struct usb_gadget_driver *driver; + struct usb_gadget *gadget; + unsigned long flags; + + spin_lock_irqsave(&dwc->lock, flags); + if (!dwc3_prepare_disconnect_gadget(dwc, &driver, &gadget)) { + spin_unlock_irqrestore(&dwc->lock, flags); + return; + } + + spin_unlock_irqrestore(&dwc->lock, flags); + driver->disconnect(gadget); +} + static void dwc3_suspend_gadget(struct dwc3 *dwc) { if (dwc->async_callbacks && dwc->gadget_driver->suspend) { @@ -4838,7 +4871,6 @@ EXPORT_SYMBOL_GPL(dwc3_gadget_exit); int dwc3_gadget_suspend(struct dwc3 *dwc) { - unsigned long flags; int ret; ret = dwc3_gadget_soft_disconnect(dwc); @@ -4852,10 +4884,7 @@ int dwc3_gadget_suspend(struct dwc3 *dwc) return -EAGAIN; } - spin_lock_irqsave(&dwc->lock, flags); - if (dwc->gadget_driver) - dwc3_disconnect_gadget(dwc); - spin_unlock_irqrestore(&dwc->lock, flags); + dwc3_disconnect_gadget_sleepable(dwc); return 0; } diff --git a/drivers/usb/fotg210/fotg210-hcd.c b/drivers/usb/fotg210/fotg210-hcd.c index 1a48329a4e08..956be5b56510 100644 --- a/drivers/usb/fotg210/fotg210-hcd.c +++ b/drivers/usb/fotg210/fotg210-hcd.c @@ -4267,8 +4267,6 @@ static int iso_stream_schedule(struct fotg210_hcd *fotg210, struct urb *urb, return 0; fail: - iso_sched_free(stream, sched); - urb->hcpriv = NULL; return status; } @@ -4562,6 +4560,10 @@ static int itd_submit(struct fotg210_hcd *fotg210, struct urb *urb, else usb_hcd_unlink_urb_from_ep(fotg210_to_hcd(fotg210), urb); done_not_linked: + if (status < 0) { + iso_sched_free(stream, urb->hcpriv); + urb->hcpriv = NULL; + } spin_unlock_irqrestore(&fotg210->lock, flags); done: return status; diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index dc3664374596..df39e3487c1f 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1863,9 +1863,10 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) if (cdev->config) config = cdev->config; else - config = list_first_entry( + config = list_first_entry_or_null( &cdev->configs, - struct usb_configuration, list); + struct usb_configuration, + list); if (!config) goto done; diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 75912ce6ab55..44218be1e676 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -288,6 +288,7 @@ static int ffs_acquire_dev(const char *dev_name, struct ffs_data *ffs_data); static void ffs_release_dev(struct ffs_dev *ffs_dev); static int ffs_ready(struct ffs_data *ffs); static void ffs_closed(struct ffs_data *ffs); +static void ffs_reset_work(struct work_struct *work); /* Misc helper functions ****************************************************/ @@ -1374,7 +1375,6 @@ ffs_epfile_release(struct inode *inode, struct file *file) mutex_unlock(&epfile->dmabufs_mutex); - __ffs_epfile_read_buffer_free(epfile); ffs_data_closed(epfile->ffs); return 0; @@ -1704,6 +1704,7 @@ static int ffs_dmabuf_transfer(struct file *file, resv_dir = epfile->in ? DMA_RESV_USAGE_READ : DMA_RESV_USAGE_WRITE; dma_resv_add_fence(dmabuf->resv, &fence->base, resv_dir); + dma_fence_put(&fence->base); dma_resv_unlock(dmabuf->resv); /* Now that the dma_fence is in place, queue the transfer. */ @@ -2221,6 +2222,7 @@ static struct ffs_data *ffs_data_new(const char *dev_name) init_waitqueue_head(&ffs->ev.waitq); init_waitqueue_head(&ffs->wait); init_completion(&ffs->ep0req_completion); + INIT_WORK(&ffs->reset_work, ffs_reset_work); /* XXX REVISIT need to update it in some places, or do we? */ ffs->ev.can_stall = 1; @@ -2364,6 +2366,7 @@ static int ffs_epfiles_create(struct ffs_data *ffs) sprintf(epfile->name, "ep%02x", ffs->eps_addrmap[i]); else sprintf(epfile->name, "ep%u", i); + epfile->in = (ffs->eps_addrmap[i] & USB_ENDPOINT_DIR_MASK) ? 1 : 0; err = ffs_sb_create_file(ffs->sb, epfile->name, epfile, &ffs_epfile_operations); if (err) { @@ -2389,6 +2392,7 @@ static void ffs_epfiles_destroy(struct super_block *sb, for (; count; --count, ++epfile) { BUG_ON(mutex_is_locked(&epfile->mutex)); + __ffs_epfile_read_buffer_free(epfile); simple_remove_by_name(root, epfile->name, clear_one); } @@ -2453,7 +2457,6 @@ static int ffs_func_eps_enable(struct ffs_function *func) ret = usb_ep_enable(ep->ep); if (!ret) { epfile->ep = ep; - epfile->in = usb_endpoint_dir_in(ep->ep->desc); epfile->isoc = usb_endpoint_xfer_isoc(ep->ep->desc); } else { break; @@ -3775,7 +3778,6 @@ static int ffs_func_set_alt(struct usb_function *f, if (ffs->state == FFS_DEACTIVATED) { ffs->state = FFS_CLOSING; spin_unlock_irqrestore(&ffs->eps_lock, flags); - INIT_WORK(&ffs->reset_work, ffs_reset_work); schedule_work(&ffs->reset_work); return -ENODEV; } @@ -3806,7 +3808,6 @@ static void ffs_func_disable(struct usb_function *f) if (ffs->state == FFS_DEACTIVATED) { ffs->state = FFS_CLOSING; spin_unlock_irqrestore(&ffs->eps_lock, flags); - INIT_WORK(&ffs->reset_work, ffs_reset_work); schedule_work(&ffs->reset_work); return; } diff --git a/drivers/usb/gadget/function/f_printer.c b/drivers/usb/gadget/function/f_printer.c index e4f7828ae75d..837f753d0cae 100644 --- a/drivers/usb/gadget/function/f_printer.c +++ b/drivers/usb/gadget/function/f_printer.c @@ -363,12 +363,11 @@ printer_open(struct inode *inode, struct file *fd) ret = 0; /* Change the printer status to show that it's on-line. */ dev->printer_status |= PRINTER_SELECTED; + kref_get(&dev->kref); } spin_unlock_irqrestore(&dev->lock, flags); - kref_get(&dev->kref); - return ret; } diff --git a/drivers/usb/gadget/function/rndis.c b/drivers/usb/gadget/function/rndis.c index 3da54a7d7aba..a2fd239b7ad3 100644 --- a/drivers/usb/gadget/function/rndis.c +++ b/drivers/usb/gadget/function/rndis.c @@ -591,6 +591,7 @@ static int rndis_init_response(struct rndis_params *params, static int rndis_query_response(struct rndis_params *params, rndis_query_msg_type *buf) { + u32 BufLength, BufOffset; rndis_query_cmplt_type *resp; rndis_resp_t *r; @@ -598,6 +599,13 @@ static int rndis_query_response(struct rndis_params *params, if (!params->dev) return -ENOTSUPP; + BufLength = le32_to_cpu(buf->InformationBufferLength); + BufOffset = le32_to_cpu(buf->InformationBufferOffset); + if ((BufLength > RNDIS_MAX_TOTAL_SIZE) || + (BufOffset > RNDIS_MAX_TOTAL_SIZE) || + (BufOffset + 8 >= RNDIS_MAX_TOTAL_SIZE)) + return -EINVAL; + /* * we need more memory: * gen_ndis_query_resp expects enough space for @@ -614,10 +622,8 @@ static int rndis_query_response(struct rndis_params *params, resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ if (gen_ndis_query_resp(params, le32_to_cpu(buf->OID), - le32_to_cpu(buf->InformationBufferOffset) - + 8 + (u8 *)buf, - le32_to_cpu(buf->InformationBufferLength), - r)) { + BufOffset + 8 + (u8 *)buf, + BufLength, r)) { /* OID not supported */ resp->Status = cpu_to_le32(RNDIS_STATUS_NOT_SUPPORTED); resp->MessageLength = cpu_to_le32(sizeof *resp); @@ -1074,6 +1080,12 @@ int rndis_rm_hdr(struct gether *port, /* tmp points to a struct rndis_packet_msg_type */ __le32 *tmp = (void *)skb->data; + /* Need at least MessageType, MessageLength, DataOffset, DataLength */ + if (skb->len < 16) { + dev_kfree_skb_any(skb); + return -EINVAL; + } + /* MessageType, MessageLength */ if (cpu_to_le32(RNDIS_MSG_PACKET) != get_unaligned(tmp++)) { diff --git a/drivers/usb/gadget/udc/aspeed-vhub/dev.c b/drivers/usb/gadget/udc/aspeed-vhub/dev.c index 2ecd049dacc2..8b9449d16324 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/dev.c +++ b/drivers/usb/gadget/udc/aspeed-vhub/dev.c @@ -593,7 +593,7 @@ int ast_vhub_init_dev(struct ast_vhub *vhub, unsigned int idx) d->gadget.max_speed = USB_SPEED_HIGH; d->gadget.speed = USB_SPEED_UNKNOWN; d->gadget.dev.of_node = vhub->pdev->dev.of_node; - d->gadget.dev.of_node_reused = true; + dev_set_of_node_reused(&d->gadget.dev); rc = usb_add_gadget_udc(d->port_dev, &d->gadget); if (rc != 0) diff --git a/drivers/usb/gadget/udc/aspeed_udc.c b/drivers/usb/gadget/udc/aspeed_udc.c index 7fc6696b7694..75f9c831b21a 100644 --- a/drivers/usb/gadget/udc/aspeed_udc.c +++ b/drivers/usb/gadget/udc/aspeed_udc.c @@ -694,7 +694,7 @@ static int ast_udc_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) struct ast_udc_dev *udc = ep->udc; struct ast_udc_request *req; unsigned long flags; - int rc = 0; + int rc = -EINVAL; spin_lock_irqsave(&udc->lock, flags); @@ -704,14 +704,11 @@ static int ast_udc_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) list_del_init(&req->queue); ast_udc_done(ep, req, -ESHUTDOWN); _req->status = -ECONNRESET; + rc = 0; break; } } - /* dequeue request not found */ - if (&req->req != _req) - rc = -EINVAL; - spin_unlock_irqrestore(&udc->lock, flags); return rc; diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c index e8861eaad907..f6da12b553a0 100644 --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -31,8 +31,9 @@ static const struct bus_type gadget_bus_type; /** * struct usb_udc - describes one usb device controller * @driver: the gadget driver pointer. For use by the class code - * @dev: the child device to the actual controller * @gadget: the gadget. For use by the class code + * @gadget_release: the gadget's release routine + * @dev: the child device to the actual controller * @list: for use by the udc class driver * @vbus: for udcs who care about vbus status, this value is real vbus status; * for udcs who do not care about vbus status, this value is always true @@ -53,6 +54,7 @@ static const struct bus_type gadget_bus_type; struct usb_udc { struct usb_gadget_driver *driver; struct usb_gadget *gadget; + void (*gadget_release)(struct device *dev); struct device dev; struct list_head list; bool vbus; @@ -712,6 +714,9 @@ static int usb_gadget_connect_locked(struct usb_gadget *gadget) goto out; } + if (gadget->connected) + goto out; + if (gadget->deactivated || !gadget->udc->allow_connect || !gadget->udc->started) { /* * If the gadget isn't usable (because it is deactivated, @@ -885,8 +890,10 @@ int usb_gadget_activate(struct usb_gadget *gadget) * If gadget has been connected before deactivation, or became connected * while it was being deactivated, we call usb_gadget_connect(). */ - if (gadget->connected) + if (gadget->connected) { + gadget->connected = false; ret = usb_gadget_connect_locked(gadget); + } unlock: mutex_unlock(&gadget->udc->connect_lock); @@ -1357,6 +1364,17 @@ static void usb_udc_nop_release(struct device *dev) dev_vdbg(dev, "%s\n", __func__); } +static void usb_gadget_release(struct device *dev) +{ + struct usb_gadget *gadget = dev_to_usb_gadget(dev); + struct usb_udc *udc = gadget->udc; + /* Cache the gadget's release routine to prevent UAF */ + void (*release)(struct device *dev) = udc->gadget_release; + + put_device(&udc->dev); + release(dev); +} + /** * usb_initialize_gadget - initialize a gadget and its embedded struct device * @parent: the parent device to this udc. Usually the controller driver's @@ -1413,6 +1431,14 @@ int usb_add_gadget(struct usb_gadget *gadget) mutex_init(&udc->connect_lock); udc->started = false; + /* + * Align decoupled lifecycles: take a UDC reference to ensure it + * remains allocated until the gadget is released, requiring an + * override of the gadget's release routine to drop it. + */ + udc->gadget_release = gadget->dev.release; + gadget->dev.release = usb_gadget_release; + get_device(&udc->dev); mutex_lock(&udc_lock); list_add_tail(&udc->list, &udc_list); @@ -1457,6 +1483,12 @@ int usb_add_gadget(struct usb_gadget *gadget) mutex_lock(&udc_lock); list_del(&udc->list); mutex_unlock(&udc_lock); + /* + * Revert the override and drop the UDC reference to prevent + * leaking the UDC if the gadget was statically allocated. + */ + gadget->dev.release = udc->gadget_release; + put_device(&udc->dev); err_put_udc: put_device(&udc->dev); diff --git a/drivers/usb/gadget/udc/goku_udc.c b/drivers/usb/gadget/udc/goku_udc.c index db42a5e3e805..ac2a984c2f87 100644 --- a/drivers/usb/gadget/udc/goku_udc.c +++ b/drivers/usb/gadget/udc/goku_udc.c @@ -1616,7 +1616,8 @@ pm_next: if (stat & INT_USBRESET) { /* hub reset done */ ACK(INT_USBRESET); INFO(dev, "USB reset done, gadget %s\n", - dev->driver->driver.name); + dev->driver ? dev->driver->driver.name : + "<not bound>"); } // and INT_ERR on some endpoint's crc/bitstuff/... problem } diff --git a/drivers/usb/gadget/udc/pxa25x_udc.c b/drivers/usb/gadget/udc/pxa25x_udc.c index b3d58d7c3a77..594d67193763 100644 --- a/drivers/usb/gadget/udc/pxa25x_udc.c +++ b/drivers/usb/gadget/udc/pxa25x_udc.c @@ -12,7 +12,7 @@ /* #define VERBOSE_DEBUG */ #include <linux/device.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/ioport.h> @@ -261,24 +261,12 @@ static void nuke (struct pxa25x_ep *, int status); /* one GPIO should control a D+ pullup, so host sees this device (or not) */ static void pullup_off(void) { - struct pxa2xx_udc_mach_info *mach = the_controller->mach; - int off_level = mach->gpio_pullup_inverted; - - if (gpio_is_valid(mach->gpio_pullup)) - gpio_set_value(mach->gpio_pullup, off_level); - else if (mach->udc_command) - mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT); + gpiod_set_value(the_controller->pullup_gpio, 0); } static void pullup_on(void) { - struct pxa2xx_udc_mach_info *mach = the_controller->mach; - int on_level = !mach->gpio_pullup_inverted; - - if (gpio_is_valid(mach->gpio_pullup)) - gpio_set_value(mach->gpio_pullup, on_level); - else if (mach->udc_command) - mach->udc_command(PXA2XX_UDC_CMD_CONNECT); + gpiod_set_value(the_controller->pullup_gpio, 1); } #if defined(CONFIG_CPU_BIG_ENDIAN) @@ -1190,8 +1178,7 @@ static int pxa25x_udc_pullup(struct usb_gadget *_gadget, int is_active) udc = container_of(_gadget, struct pxa25x_udc, gadget); - /* not all boards support pullup control */ - if (!gpio_is_valid(udc->mach->gpio_pullup) && !udc->mach->udc_command) + if (!udc->pullup_gpio) return -EOPNOTSUPP; udc->pullup = (is_active != 0); @@ -2343,19 +2330,17 @@ static int pxa25x_udc_probe(struct platform_device *pdev) /* other non-static parts of init */ dev->dev = &pdev->dev; - dev->mach = dev_get_platdata(&pdev->dev); dev->transceiver = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2); - if (gpio_is_valid(dev->mach->gpio_pullup)) { - retval = devm_gpio_request_one(&pdev->dev, dev->mach->gpio_pullup, - GPIOF_OUT_INIT_LOW, "pca25x_udc GPIO PULLUP"); - if (retval) { - dev_dbg(&pdev->dev, - "can't get pullup gpio %d, err: %d\n", - dev->mach->gpio_pullup, retval); - goto err; - } + dev->pullup_gpio = devm_gpiod_get_index_optional(&pdev->dev, "pullup", 0, + GPIOD_OUT_HIGH); + if (IS_ERR(dev->pullup_gpio)) { + dev_dbg(&pdev->dev, + "can't get pullup gpio err: %ld\n", + PTR_ERR(dev->pullup_gpio)); + retval = PTR_ERR(dev->pullup_gpio); + goto err; } timer_setup(&dev->timer, udc_watchdog, 0); @@ -2439,7 +2424,7 @@ static int pxa25x_udc_suspend(struct platform_device *dev, pm_message_t state) struct pxa25x_udc *udc = platform_get_drvdata(dev); unsigned long flags; - if (!gpio_is_valid(udc->mach->gpio_pullup) && !udc->mach->udc_command) + if (!udc->pullup_gpio) WARNING("USB host won't detect disconnect!\n"); udc->suspended = 1; diff --git a/drivers/usb/gadget/udc/pxa25x_udc.h b/drivers/usb/gadget/udc/pxa25x_udc.h index 6ab6047edc83..3452cf54286c 100644 --- a/drivers/usb/gadget/udc/pxa25x_udc.h +++ b/drivers/usb/gadget/udc/pxa25x_udc.h @@ -112,7 +112,7 @@ struct pxa25x_udc { struct device *dev; struct clk *clk; - struct pxa2xx_udc_mach_info *mach; + struct gpio_desc *pullup_gpio; struct usb_phy *transceiver; u64 dma_mask; struct pxa25x_ep ep [PXA_UDC_NUM_ENDPOINTS]; diff --git a/drivers/usb/gadget/udc/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c index 1abea0d48c35..640f81988c04 100644 --- a/drivers/usb/gadget/udc/pxa27x_udc.c +++ b/drivers/usb/gadget/udc/pxa27x_udc.c @@ -17,7 +17,6 @@ #include <linux/proc_fs.h> #include <linux/clk.h> #include <linux/irq.h> -#include <linux/gpio.h> #include <linux/gpio/consumer.h> #include <linux/slab.h> #include <linux/string_choices.h> @@ -1423,14 +1422,7 @@ static const struct usb_ep_ops pxa_ep_ops = { */ static void dplus_pullup(struct pxa_udc *udc, int on) { - if (udc->gpiod) { - gpiod_set_value(udc->gpiod, on); - } else if (udc->udc_command) { - if (on) - udc->udc_command(PXA2XX_UDC_CMD_CONNECT); - else - udc->udc_command(PXA2XX_UDC_CMD_DISCONNECT); - } + gpiod_set_value(udc->gpiod, on); udc->pullup_on = on; } @@ -1521,7 +1513,7 @@ static int pxa_udc_pullup(struct usb_gadget *_gadget, int is_active) struct pxa_udc *udc = to_gadget_udc(_gadget); int ret; - if (!udc->gpiod && !udc->udc_command) + if (!udc->gpiod) return -EOPNOTSUPP; dplus_pullup(udc, is_active); @@ -2380,26 +2372,11 @@ MODULE_DEVICE_TABLE(of, udc_pxa_dt_ids); static int pxa_udc_probe(struct platform_device *pdev) { struct pxa_udc *udc = &memory; - int retval = 0, gpio; - struct pxa2xx_udc_mach_info *mach = dev_get_platdata(&pdev->dev); - - if (mach) { - gpio = mach->gpio_pullup; - if (gpio_is_valid(gpio)) { - retval = devm_gpio_request_one(&pdev->dev, gpio, - GPIOF_OUT_INIT_LOW, - "USB D+ pullup"); - if (retval) - return retval; - udc->gpiod = gpio_to_desc(mach->gpio_pullup); - - if (mach->gpio_pullup_inverted ^ gpiod_is_active_low(udc->gpiod)) - gpiod_toggle_active_low(udc->gpiod); - } - udc->udc_command = mach->udc_command; - } else { - udc->gpiod = devm_gpiod_get(&pdev->dev, NULL, GPIOD_ASIS); - } + int retval = 0; + + udc->gpiod = devm_gpiod_get(&pdev->dev, NULL, GPIOD_ASIS); + if (IS_ERR(udc->gpiod)) + return PTR_ERR(udc->gpiod); udc->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(udc->regs)) diff --git a/drivers/usb/gadget/udc/pxa27x_udc.h b/drivers/usb/gadget/udc/pxa27x_udc.h index 31bf79ce931c..2c28b691010a 100644 --- a/drivers/usb/gadget/udc/pxa27x_udc.h +++ b/drivers/usb/gadget/udc/pxa27x_udc.h @@ -426,7 +426,6 @@ struct udc_stats { * @usb_gadget: udc gadget structure * @driver: bound gadget (zero, g_ether, g_mass_storage, ...) * @dev: device - * @udc_command: machine specific function to activate D+ pullup * @gpiod: gpio descriptor of gpio for D+ pullup (or NULL if none) * @transceiver: external transceiver to handle vbus sense and D+ pullup * @ep0state: control endpoint state machine state @@ -452,7 +451,6 @@ struct pxa_udc { struct usb_gadget gadget; struct usb_gadget_driver *driver; struct device *dev; - void (*udc_command)(int); struct gpio_desc *gpiod; struct usb_phy *transceiver; diff --git a/drivers/usb/gadget/udc/renesas_usbf.c b/drivers/usb/gadget/udc/renesas_usbf.c index 5d510665da1f..d67002ea049a 100644 --- a/drivers/usb/gadget/udc/renesas_usbf.c +++ b/drivers/usb/gadget/udc/renesas_usbf.c @@ -12,7 +12,6 @@ #include <linux/iopoll.h> #include <linux/kernel.h> #include <linux/kfifo.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 0a277a07cf70..b3b1ec696bf5 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -42,6 +42,12 @@ config USB_XHCI_PCI depends on USB_PCI default y +config USB_XHCI_PCI_PROM21 + tristate + depends on USB_XHCI_PCI + default USB_XHCI_PCI if SENSORS_PROM21_XHCI != n + select AUXILIARY_BUS + config USB_XHCI_PCI_RENESAS tristate "Support for additional Renesas xHCI controller with firmware" depends on USB_XHCI_PCI @@ -71,7 +77,7 @@ config USB_XHCI_HISTB config USB_XHCI_MTK tristate "xHCI support for MediaTek SoCs" select MFD_SYSCON - depends on (MIPS && SOC_MT7621) || ARCH_MEDIATEK || COMPILE_TEST + depends on (MIPS && SOC_MT7621) || ARCH_MEDIATEK || ARCH_AIROHA || COMPILE_TEST help Say 'Y' to enable the support for the xHCI host controller found in MediaTek SoCs. diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index a07e7ba9cd53..174580c1281a 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -71,6 +71,7 @@ obj-$(CONFIG_USB_UHCI_HCD) += uhci-hcd.o obj-$(CONFIG_USB_FHCI_HCD) += fhci.o obj-$(CONFIG_USB_XHCI_HCD) += xhci-hcd.o obj-$(CONFIG_USB_XHCI_PCI) += xhci-pci.o +obj-$(CONFIG_USB_XHCI_PCI_PROM21) += xhci-pci-prom21.o obj-$(CONFIG_USB_XHCI_PCI_RENESAS) += xhci-pci-renesas.o obj-$(CONFIG_USB_XHCI_PLATFORM) += xhci-plat-hcd.o obj-$(CONFIG_USB_XHCI_HISTB) += xhci-histb.o diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index a241337c9af8..57d07d1c2dfa 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -1623,6 +1623,7 @@ iso_stream_schedule( status = 1; /* and give it back immediately */ iso_sched_free(stream, sched); sched = NULL; + urb->hcpriv = NULL; } } urb->error_count = skip / period; @@ -1653,8 +1654,6 @@ iso_stream_schedule( return status; fail: - iso_sched_free(stream, sched); - urb->hcpriv = NULL; return status; } @@ -1966,6 +1965,10 @@ static int itd_submit(struct ehci_hcd *ehci, struct urb *urb, usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb); } done_not_linked: + if (status < 0) { + iso_sched_free(stream, urb->hcpriv); + urb->hcpriv = NULL; + } spin_unlock_irqrestore(&ehci->lock, flags); done: return status; @@ -2343,6 +2346,10 @@ static int sitd_submit(struct ehci_hcd *ehci, struct urb *urb, usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb); } done_not_linked: + if (status < 0) { + iso_sched_free(stream, urb->hcpriv); + urb->hcpriv = NULL; + } spin_unlock_irqrestore(&ehci->lock, flags); done: return status; diff --git a/drivers/usb/host/max3421-hcd.c b/drivers/usb/host/max3421-hcd.c index 0e17c988d36a..73e76d0e6973 100644 --- a/drivers/usb/host/max3421-hcd.c +++ b/drivers/usb/host/max3421-hcd.c @@ -1685,6 +1685,8 @@ max3421_hub_control(struct usb_hcd *hcd, u16 type_req, u16 value, u16 index, case ClearHubFeature: break; case ClearPortFeature: + if (index != 1) + goto error; switch (value) { case USB_PORT_FEAT_SUSPEND: break; @@ -1694,6 +1696,8 @@ max3421_hub_control(struct usb_hcd *hcd, u16 type_req, u16 value, u16 index, !pdata->vbus_active_level); fallthrough; default: + if (value >= 32) + goto error; max3421_hcd->port_status &= ~(1 << value); } break; @@ -1726,6 +1730,8 @@ max3421_hub_control(struct usb_hcd *hcd, u16 type_req, u16 value, u16 index, break; case SetPortFeature: + if (index != 1) + goto error; switch (value) { case USB_PORT_FEAT_LINK_STATE: case USB_PORT_FEAT_U1_TIMEOUT: @@ -1747,6 +1753,8 @@ max3421_hub_control(struct usb_hcd *hcd, u16 type_req, u16 value, u16 index, max3421_reset_port(hcd); fallthrough; default: + if (value >= 32) + goto error; if ((max3421_hcd->port_status & USB_PORT_STAT_POWER) != 0) max3421_hcd->port_status |= (1 << value); diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c index 4ae47edd4b8b..b044977f6f56 100644 --- a/drivers/usb/host/sl811-hcd.c +++ b/drivers/usb/host/sl811-hcd.c @@ -1591,6 +1591,7 @@ sl811h_remove(struct platform_device *dev) remove_debug_file(sl811); usb_remove_hcd(hcd); + device_wakeup_disable(hcd->self.controller); /* some platforms may use IORESOURCE_IO */ res = platform_get_resource(dev, IORESOURCE_MEM, 1); diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c index 7e6f7d72f03e..48ee6a4f9e1c 100644 --- a/drivers/usb/host/xhci-dbgcap.c +++ b/drivers/usb/host/xhci-dbgcap.c @@ -275,7 +275,6 @@ xhci_dbc_queue_trb(struct xhci_ring *ring, u32 field1, trace_xhci_dbc_gadget_ep_queue(ring, &trb->generic, xhci_trb_virt_to_dma(ring->enq_seg, ring->enqueue)); - ring->num_trbs_free--; next = ++(ring->enqueue); if (TRB_TYPE_LINK_LE32(next->link.control)) { next->link.control ^= cpu_to_le32(TRB_CYCLE); @@ -296,7 +295,7 @@ static int xhci_dbc_queue_bulk_tx(struct dbc_ep *dep, num_trbs = count_trbs(req->dma, req->length); WARN_ON(num_trbs != 1); - if (ring->num_trbs_free < num_trbs) + if (xhci_num_trbs_free(ring) <= num_trbs) return -EBUSY; addr = req->dma; @@ -629,18 +628,61 @@ static void xhci_dbc_mem_cleanup(struct xhci_dbc *dbc) dbc->ring_evt = NULL; } +static int xhci_dbc_enable_dce(struct xhci_dbc *dbc, bool enable) +{ + u32 done_state = 0; + u32 ctrl = 0; + + if (enable) { + ctrl = readl(&dbc->regs->control); + ctrl |= DBC_CTRL_DBC_ENABLE | DBC_CTRL_PORT_ENABLE; + done_state = DBC_CTRL_DBC_ENABLE; + } + + writel(ctrl, &dbc->regs->control); + return xhci_handshake(&dbc->regs->control, DBC_CTRL_DBC_ENABLE, + done_state, 1000); +} + +static void xhci_dbc_set_state(struct xhci_dbc *dbc, enum dbc_state new_state) +{ + if (dbc->state == new_state) + return; + + switch (new_state) { + case DS_ENABLED: + /* + * DbC pm usage is 1 here, both when moved from disconnect or + * configured states, or when setting initial DbC enable state. + * Just enable pending put + */ + dev_dbg(dbc->dev, "DbC set pending_rpm_put = 1\n"); + dbc->pending_rpm_put = 1; + break; + case DS_CONNECTED: + if (dbc->pending_rpm_put) + /* DbC pm usage still 1, just remove pending put */ + dbc->pending_rpm_put = 0; + else + /* DbC pm usage was put to 0, call get */ + pm_runtime_get(dbc->dev); + break; + default: + break; + } + + dbc->state_timestamp = jiffies; + dbc->state = new_state; +} + static int xhci_do_dbc_start(struct xhci_dbc *dbc) { int ret; - u32 ctrl; if (dbc->state != DS_DISABLED) return -EINVAL; - writel(0, &dbc->regs->control); - ret = xhci_handshake(&dbc->regs->control, - DBC_CTRL_DBC_ENABLE, - 0, 1000); + ret = xhci_dbc_enable_dce(dbc, false); if (ret) return ret; @@ -648,27 +690,11 @@ static int xhci_do_dbc_start(struct xhci_dbc *dbc) if (ret) return ret; - ctrl = readl(&dbc->regs->control); - writel(ctrl | DBC_CTRL_DBC_ENABLE | DBC_CTRL_PORT_ENABLE, - &dbc->regs->control); - ret = xhci_handshake(&dbc->regs->control, - DBC_CTRL_DBC_ENABLE, - DBC_CTRL_DBC_ENABLE, 1000); + ret = xhci_dbc_enable_dce(dbc, true); if (ret) return ret; - dbc->state = DS_ENABLED; - - return 0; -} - -static int xhci_do_dbc_stop(struct xhci_dbc *dbc) -{ - if (dbc->state == DS_DISABLED) - return -EINVAL; - - writel(0, &dbc->regs->control); - dbc->state = DS_DISABLED; + xhci_dbc_set_state(dbc, DS_ENABLED); return 0; } @@ -680,33 +706,42 @@ static int xhci_dbc_start(struct xhci_dbc *dbc) WARN_ON(!dbc); - pm_runtime_get_sync(dbc->dev); /* note this was self.controller */ + pm_runtime_get(dbc->dev); spin_lock_irqsave(&dbc->lock, flags); ret = xhci_do_dbc_start(dbc); + if (ret) + goto err_unlock; + spin_unlock_irqrestore(&dbc->lock, flags); - if (ret) { - pm_runtime_put(dbc->dev); /* note this was self.controller */ - return ret; - } + mod_delayed_work(system_percpu_wq, &dbc->event_work, + msecs_to_jiffies(dbc->poll_interval)); + + return 0; - return mod_delayed_work(system_percpu_wq, &dbc->event_work, - msecs_to_jiffies(dbc->poll_interval)); +err_unlock: + + spin_unlock_irqrestore(&dbc->lock, flags); + pm_runtime_put(dbc->dev); /* note this was self.controller */ + + return ret; } static void xhci_dbc_stop(struct xhci_dbc *dbc) { - int ret; unsigned long flags; + bool need_rpm_put = false; WARN_ON(!dbc); + spin_lock(&dbc->lock); + switch (dbc->state) { case DS_DISABLED: + spin_unlock(&dbc->lock); return; case DS_CONFIGURED: - spin_lock(&dbc->lock); xhci_dbc_flush_requests(dbc); spin_unlock(&dbc->lock); @@ -714,19 +749,28 @@ static void xhci_dbc_stop(struct xhci_dbc *dbc) dbc->driver->disconnect(dbc); break; default: + spin_unlock(&dbc->lock); break; } cancel_delayed_work_sync(&dbc->event_work); spin_lock_irqsave(&dbc->lock, flags); - ret = xhci_do_dbc_stop(dbc); + writel(0, &dbc->regs->control); + + if (dbc->state == DS_CONNECTED || dbc->state == DS_CONFIGURED || + dbc->pending_rpm_put) + need_rpm_put = true; + + dbc->pending_rpm_put = 0; + + xhci_dbc_set_state(dbc, DS_DISABLED); spin_unlock_irqrestore(&dbc->lock, flags); - if (ret) - return; xhci_dbc_mem_cleanup(dbc); - pm_runtime_put_sync(dbc->dev); /* note, was self.controller */ + + if (need_rpm_put) + pm_runtime_put(dbc->dev); } static void @@ -796,7 +840,6 @@ static void dbc_handle_xfer_event(struct xhci_dbc *dbc, union xhci_trb *event) } 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); } } @@ -861,7 +904,6 @@ static void dbc_handle_xfer_event(struct xhci_dbc *dbc, union xhci_trb *event) break; } - ring->num_trbs_free++; req->actual = req->length - remain_length; xhci_dbc_giveback(req, status); } @@ -893,23 +935,55 @@ static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc) return EVT_ERR; case DS_ENABLED: portsc = readl(&dbc->regs->portsc); + ctrl = readl(&dbc->regs->control); + if (portsc & DBC_PORTSC_CONN_STATUS) { - dbc->state = DS_CONNECTED; + xhci_dbc_set_state(dbc, DS_CONNECTED); dev_info(dbc->dev, "DbC connected\n"); + } else if (!(ctrl & DBC_CTRL_DBC_ENABLE)) { + dev_err(dbc->dev, "unexpected DbC disable, xHC reset?\n"); + } else if (dbc->pending_rpm_put && + time_is_before_jiffies(dbc->state_timestamp + + msecs_to_jiffies(DBC_AUTOSUSPEND_DELAY))) { + dbc->pending_rpm_put = 0; + dev_dbg(dbc->dev, "DbC Enabled state for 15 seconds, allow rpm suspend\n"); + pm_runtime_put(dbc->dev); } return EVT_DONE; case DS_CONNECTED: ctrl = readl(&dbc->regs->control); + portsc = readl(&dbc->regs->portsc); if (ctrl & DBC_CTRL_DBC_RUN) { - dbc->state = DS_CONFIGURED; + xhci_dbc_set_state(dbc, DS_CONFIGURED); dev_info(dbc->dev, "DbC configured\n"); - portsc = readl(&dbc->regs->portsc); writel(portsc, &dbc->regs->portsc); ret = EVT_GSER; break; } + /* Connection lost */ + if (!(portsc & DBC_PORTSC_CONN_STATUS)) { + /* covers DCE == 0 as it also sets CONN_STATUS to 0 */ + dev_warn(dbc->dev, "DbC connection lost mid enumeration\n"); + xhci_dbc_set_state(dbc, DS_ENABLED); + + return EVT_DONE; + } + + /* Enumeration timeout */ + if (time_is_before_jiffies(dbc->state_timestamp + + msecs_to_jiffies(DBC_ENUMERATION_TIMEOUT))) { + dev_err(dbc->dev, "DbC enumeration timeout, re-enabling DbC\n"); + dev_dbg(dbc->dev, "dcctrl %x, dcportsc %x\n", ctrl, portsc); + + /* Toggle DCE to retry enumeration */ + ret = xhci_dbc_enable_dce(dbc, false); + udelay(100); + ret = xhci_dbc_enable_dce(dbc, true); + xhci_dbc_set_state(dbc, DS_ENABLED); + } + return EVT_DONE; case DS_CONFIGURED: /* Handle cable unplug event: */ @@ -917,7 +991,7 @@ static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc) if (!(portsc & DBC_PORTSC_PORT_ENABLED) && !(portsc & DBC_PORTSC_CONN_STATUS)) { dev_info(dbc->dev, "DbC cable unplugged\n"); - dbc->state = DS_ENABLED; + xhci_dbc_set_state(dbc, DS_ENABLED); xhci_dbc_flush_requests(dbc); xhci_dbc_reinit_ep_rings(dbc); return EVT_DISC; @@ -927,7 +1001,7 @@ static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc) if (portsc & DBC_PORTSC_RESET_CHANGE) { dev_info(dbc->dev, "DbC port reset\n"); writel(portsc, &dbc->regs->portsc); - dbc->state = DS_ENABLED; + xhci_dbc_set_state(dbc, DS_ENABLED); xhci_dbc_flush_requests(dbc); xhci_dbc_reinit_ep_rings(dbc); return EVT_DISC; @@ -1062,6 +1136,9 @@ static ssize_t dbc_show(struct device *dev, if (dbc->state >= ARRAY_SIZE(dbc_state_strings)) return sysfs_emit(buf, "unknown\n"); + if (dbc->resume_required) + return sysfs_emit(buf, "suspended\n"); + return sysfs_emit(buf, "%s\n", dbc_state_strings[dbc->state]); } @@ -1075,12 +1152,30 @@ static ssize_t dbc_store(struct device *dev, xhci = hcd_to_xhci(dev_get_drvdata(dev)); dbc = xhci->dbc; - if (sysfs_streq(buf, "enable")) + if (sysfs_streq(buf, "enable")) { + pm_runtime_get_sync(dbc->dev); + + mutex_lock(&dbc->enable_mutex); + /* + * DbC may already be enabled here if xhci was suspended with + * dbc->resume_required set, and resumed by pm_runtime_get_sync() + * above. In this case we end up calling xhci_dbc_start() twice, + * second time returns an error but is harmless + */ xhci_dbc_start(dbc); - else if (sysfs_streq(buf, "disable")) + + mutex_unlock(&dbc->enable_mutex); + pm_runtime_put(dbc->dev); + } else if (sysfs_streq(buf, "disable")) { + mutex_lock(&dbc->enable_mutex); + + dbc->resume_required = 0; xhci_dbc_stop(dbc); - else + + mutex_unlock(&dbc->enable_mutex); + } else { return -EINVAL; + } return count; } @@ -1446,6 +1541,7 @@ xhci_alloc_dbc(struct device *dev, void __iomem *base, const struct dbc_driver * INIT_DELAYED_WORK(&dbc->event_work, xhci_dbc_handle_events); spin_lock_init(&dbc->lock); + mutex_init(&dbc->enable_mutex); ret = sysfs_create_groups(&dev->kobj, dbc_dev_groups); if (ret) @@ -1463,8 +1559,9 @@ void xhci_dbc_remove(struct xhci_dbc *dbc) if (!dbc) return; /* stop hw, stop wq and call dbc->ops->stop() */ + mutex_lock(&dbc->enable_mutex); xhci_dbc_stop(dbc); - + mutex_unlock(&dbc->enable_mutex); /* remove sysfs files */ sysfs_remove_groups(&dbc->dev->kobj, dbc_dev_groups); @@ -1517,6 +1614,8 @@ int xhci_dbc_suspend(struct xhci_hcd *xhci) if (!dbc) return 0; + mutex_lock(&dbc->enable_mutex); + switch (dbc->state) { case DS_ENABLED: case DS_CONNECTED: @@ -1528,6 +1627,7 @@ int xhci_dbc_suspend(struct xhci_hcd *xhci) } xhci_dbc_stop(dbc); + mutex_unlock(&dbc->enable_mutex); return 0; } @@ -1540,11 +1640,15 @@ int xhci_dbc_resume(struct xhci_hcd *xhci) if (!dbc) return 0; + mutex_lock(&dbc->enable_mutex); + if (dbc->resume_required) { dbc->resume_required = 0; xhci_dbc_start(dbc); } + mutex_unlock(&dbc->enable_mutex); + return ret; } #endif /* CONFIG_PM */ diff --git a/drivers/usb/host/xhci-dbgcap.h b/drivers/usb/host/xhci-dbgcap.h index 20ae4e7617f2..5b18efb2c1ea 100644 --- a/drivers/usb/host/xhci-dbgcap.h +++ b/drivers/usb/host/xhci-dbgcap.h @@ -113,6 +113,9 @@ struct dbc_ep { #define DBC_POLL_INTERVAL_DEFAULT 64 /* milliseconds */ #define DBC_POLL_INTERVAL_MAX 5000 /* milliseconds */ #define DBC_XFER_INACTIVITY_TIMEOUT 10 /* milliseconds */ +#define DBC_ENUMERATION_TIMEOUT 2000 /* milliseconds */ +#define DBC_AUTOSUSPEND_DELAY 15000 /* milliseconds */ + /* * Private structure for DbC hardware state: */ @@ -140,6 +143,7 @@ struct dbc_driver { struct xhci_dbc { spinlock_t lock; /* device access */ + struct mutex enable_mutex; struct device *dev; struct xhci_hcd *xhci; struct dbc_regs __iomem *regs; @@ -162,7 +166,9 @@ struct xhci_dbc { struct delayed_work event_work; unsigned int poll_interval; /* ms */ unsigned long xfer_timestamp; + unsigned long state_timestamp; unsigned resume_required:1; + unsigned pending_rpm_put:1; struct dbc_ep eps[2]; const struct dbc_driver *driver; diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index bacd0ddd0d09..b0264bd8577a 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -226,9 +226,8 @@ static int xhci_create_usb3x_bos_desc(struct xhci_hcd *xhci, char *buf, USB_SSP_SUBLINK_SPEED_ST_SYM_RX); ssp_cap->bmSublinkSpeedAttr[offset++] = cpu_to_le32(attr); - attr &= ~USB_SSP_SUBLINK_SPEED_ST; - attr |= FIELD_PREP(USB_SSP_SUBLINK_SPEED_ST, - USB_SSP_SUBLINK_SPEED_ST_SYM_TX); + FIELD_MODIFY(USB_SSP_SUBLINK_SPEED_ST, &attr, + USB_SSP_SUBLINK_SPEED_ST_SYM_TX); ssp_cap->bmSublinkSpeedAttr[offset++] = cpu_to_le32(attr); break; case PLT_ASYM_RX: @@ -639,7 +638,7 @@ struct xhci_hub *xhci_get_rhub(struct usb_hcd *hcd) /* * xhci_set_port_power() must be called with xhci->lock held. - * It will release and re-aquire the lock while calling ACPI + * It will release and re-acquire the lock while calling ACPI * method. */ static void xhci_set_port_power(struct xhci_hcd *xhci, struct xhci_port *port, diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 997fe90f54e5..a5e7f363922f 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -324,12 +324,6 @@ void xhci_initialize_ring_info(struct xhci_ring *ring) * handling ring expansion, set the cycle state equal to the old ring. */ ring->cycle_state = 1; - - /* - * Each segment has a link TRB, and leave an extra TRB for SW - * accounting purpose - */ - ring->num_trbs_free = ring->num_segs * (TRBS_PER_SEGMENT - 1) - 1; } EXPORT_SYMBOL_GPL(xhci_initialize_ring_info); @@ -883,8 +877,8 @@ void xhci_free_virt_device(struct xhci_hcd *xhci, struct xhci_virt_device *dev, /* If device ctx array still points to _this_ device, clear it */ if (dev->out_ctx && - xhci->dcbaa->dev_context_ptrs[slot_id] == cpu_to_le64(dev->out_ctx->dma)) - xhci->dcbaa->dev_context_ptrs[slot_id] = 0; + xhci->dcbaa.ctx_array[slot_id] == cpu_to_le64(dev->out_ctx->dma)) + xhci->dcbaa.ctx_array[slot_id] = 0; trace_xhci_free_virt_device(dev); @@ -1022,11 +1016,11 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id, dev->udev = udev; /* Point to output device context in dcbaa. */ - xhci->dcbaa->dev_context_ptrs[slot_id] = cpu_to_le64(dev->out_ctx->dma); + xhci->dcbaa.ctx_array[slot_id] = cpu_to_le64(dev->out_ctx->dma); xhci_dbg(xhci, "Set slot id %d dcbaa entry %p to 0x%llx\n", slot_id, - &xhci->dcbaa->dev_context_ptrs[slot_id], - le64_to_cpu(xhci->dcbaa->dev_context_ptrs[slot_id])); + &xhci->dcbaa.ctx_array[slot_id], + le64_to_cpu(xhci->dcbaa.ctx_array[slot_id])); trace_xhci_alloc_virt_device(dev); @@ -1677,7 +1671,7 @@ static int scratchpad_alloc(struct xhci_hcd *xhci, gfp_t flags) if (!xhci->scratchpad->sp_buffers) goto fail_sp3; - xhci->dcbaa->dev_context_ptrs[0] = cpu_to_le64(xhci->scratchpad->sp_dma); + xhci->dcbaa.ctx_array[0] = cpu_to_le64(xhci->scratchpad->sp_dma); for (i = 0; i < num_sp; i++) { dma_addr_t dma; void *buf = dma_alloc_coherent(dev, xhci->page_size, &dma, @@ -1933,6 +1927,7 @@ void xhci_rh_bw_cleanup(struct xhci_hcd *xhci) void xhci_mem_cleanup(struct xhci_hcd *xhci) { struct device *dev = xhci_to_hcd(xhci)->self.sysdev; + struct xhci_device_context_array *dcbaa; int i; cancel_delayed_work_sync(&xhci->cmd_timer); @@ -1952,8 +1947,11 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed command ring"); xhci_cleanup_command_queue(xhci); - for (i = xhci->max_slots; i > 0; i--) - xhci_free_virt_devices_depth_first(xhci, i); + if (xhci->devs) { + for (i = xhci->max_slots; i > 0; i--) + xhci_free_virt_devices_depth_first(xhci, i); + kfree(xhci->devs); + } dma_pool_destroy(xhci->segment_pool); xhci->segment_pool = NULL; @@ -1978,10 +1976,12 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed medium stream array pool"); - if (xhci->dcbaa) - dma_free_coherent(dev, sizeof(*xhci->dcbaa), - xhci->dcbaa, xhci->dcbaa->dma); - xhci->dcbaa = NULL; + dcbaa = &xhci->dcbaa; + if (dcbaa->ctx_array) { + dma_free_coherent(dev, array_size(sizeof(*dcbaa->ctx_array), xhci->max_slots + 1), + dcbaa->ctx_array, dcbaa->dma); + dcbaa->ctx_array = NULL; + } scratchpad_free(xhci); @@ -2008,6 +2008,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) xhci->rh_bw = NULL; xhci->port_caps = NULL; xhci->interrupters = NULL; + xhci->devs = NULL; xhci->usb2_rhub.bus_state.bus_suspended = 0; xhci->usb3_rhub.bus_state.bus_suspended = 0; @@ -2409,23 +2410,26 @@ EXPORT_SYMBOL_GPL(xhci_create_secondary_interrupter); int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) { - struct device *dev = xhci_to_hcd(xhci)->self.sysdev; - dma_addr_t dma; + struct device *dev = xhci_to_hcd(xhci)->self.sysdev; + struct xhci_device_context_array *dcbaa = &xhci->dcbaa; xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Starting %s", __func__); - /* - * xHCI section 5.4.6 - Device Context array must be - * "physically contiguous and 64-byte (cache line) aligned". - */ - xhci->dcbaa = dma_alloc_coherent(dev, sizeof(*xhci->dcbaa), &dma, flags); - if (!xhci->dcbaa) + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Allocating internal virtual device array"); + xhci->devs = kcalloc_node(xhci->max_slots + 1, sizeof(*xhci->devs), flags, + dev_to_node(dev)); + if (!xhci->devs) + goto fail; + + xhci->dcbaa.ctx_array = + dma_alloc_coherent(dev, array_size(sizeof(*dcbaa->ctx_array), xhci->max_slots + 1), + &dcbaa->dma, flags); + if (!dcbaa->ctx_array) goto fail; - xhci->dcbaa->dma = dma; xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Device context base array address = %pad (DMA), %p (virt)", - &xhci->dcbaa->dma, xhci->dcbaa); + &dcbaa->dma, dcbaa->ctx_array); /* * Initialize the ring segment pool. The ring must be a contiguous diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c index 06043c7c3100..d9b865546a67 100644 --- a/drivers/usb/host/xhci-mtk.c +++ b/drivers/usb/host/xhci-mtk.c @@ -185,9 +185,9 @@ static void xhci_mtk_rxfifo_depth_set(struct xhci_hcd_mtk *mtk) return; value = readl(hcd->regs + HSCH_CFG1); - value &= ~SCH3_RXFIFO_DEPTH_MASK; - value |= FIELD_PREP(SCH3_RXFIFO_DEPTH_MASK, - SCH_FIFO_TO_KB(mtk->rxfifo_depth) - 1); + FIELD_MODIFY(SCH3_RXFIFO_DEPTH_MASK, &value, + SCH_FIFO_TO_KB(mtk->rxfifo_depth) - 1); + writel(value, hcd->regs + HSCH_CFG1); } diff --git a/drivers/usb/host/xhci-pci-prom21.c b/drivers/usb/host/xhci-pci-prom21.c new file mode 100644 index 000000000000..6486f4a09345 --- /dev/null +++ b/drivers/usb/host/xhci-pci-prom21.c @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AMD Promontory 21 xHCI host controller PCI Bus Glue. + * + * This does not add any PROM21-specific USB or xHCI operation. It exists only + * to publish an auxiliary device for integrated temperature sensor support. + * + * Copyright (C) 2026 Jihong Min <hurryman2212@gmail.com> + */ + +#include <linux/auxiliary_bus.h> +#include <linux/device/devres.h> +#include <linux/errno.h> +#include <linux/idr.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/platform_data/usb-xhci-prom21.h> +#include <linux/usb.h> +#include <linux/usb/hcd.h> + +#include "xhci-pci.h" + +struct prom21_xhci_auxdev { + struct auxiliary_device *auxdev; + struct prom21_xhci_pdata pdata; + int id; +}; + +static DEFINE_IDA(prom21_xhci_auxdev_ida); + +static void prom21_xhci_auxdev_release(struct device *dev, void *res) +{ + struct prom21_xhci_auxdev *prom21_auxdev = res; + + auxiliary_device_destroy(prom21_auxdev->auxdev); + ida_free(&prom21_xhci_auxdev_ida, prom21_auxdev->id); +} + +static int prom21_xhci_create_auxdev(struct pci_dev *pdev) +{ + struct prom21_xhci_auxdev *prom21_auxdev; + struct usb_hcd *hcd = pci_get_drvdata(pdev); + int ret; + + prom21_auxdev = devres_alloc(prom21_xhci_auxdev_release, + sizeof(*prom21_auxdev), GFP_KERNEL); + if (!prom21_auxdev) + return -ENOMEM; + + prom21_auxdev->pdata.pdev = pdev; + prom21_auxdev->pdata.regs = hcd->regs; + prom21_auxdev->pdata.rsrc_len = hcd->rsrc_len; + + prom21_auxdev->id = ida_alloc(&prom21_xhci_auxdev_ida, GFP_KERNEL); + if (prom21_auxdev->id < 0) { + ret = prom21_auxdev->id; + goto err_free_devres; + } + + prom21_auxdev->auxdev = auxiliary_device_create(&pdev->dev, + KBUILD_MODNAME, "hwmon", + &prom21_auxdev->pdata, + prom21_auxdev->id); + if (!prom21_auxdev->auxdev) { + ret = -ENOMEM; + goto err_free_ida; + } + + devres_add(&pdev->dev, prom21_auxdev); + return 0; + +err_free_ida: + ida_free(&prom21_xhci_auxdev_ida, prom21_auxdev->id); +err_free_devres: + devres_free(prom21_auxdev); + return ret; +} + +static void prom21_xhci_destroy_auxdev(struct pci_dev *pdev) +{ + devres_release(&pdev->dev, prom21_xhci_auxdev_release, NULL, NULL); +} + +static int prom21_xhci_probe(struct pci_dev *dev, + const struct pci_device_id *id) +{ + int retval; + + retval = xhci_pci_common_probe(dev, id); + if (retval) + return retval; + + retval = prom21_xhci_create_auxdev(dev); + if (retval) { + /* + * The auxiliary device only provides optional temperature sensor + * support. Keep the xHCI controller usable if it fails. + */ + dev_err(&dev->dev, + "failed to create PROM21 hwmon auxiliary device: %d\n", + retval); + } + + return 0; +} + +static void prom21_xhci_remove(struct pci_dev *dev) +{ + prom21_xhci_destroy_auxdev(dev); + xhci_pci_remove(dev); +} + +static const struct pci_device_id pci_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_PROM21_XHCI_43FC) }, + { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_PROM21_XHCI_43FD) }, + { /* end: all zeroes */ } +}; +MODULE_DEVICE_TABLE(pci, pci_ids); + +static struct pci_driver prom21_xhci_driver = { + .name = "xhci-pci-prom21", + .id_table = pci_ids, + + .probe = prom21_xhci_probe, + .remove = prom21_xhci_remove, + + .shutdown = usb_hcd_pci_shutdown, + .driver = { + .pm = pm_ptr(&usb_hcd_pci_pm_ops), + }, +}; +module_pci_driver(prom21_xhci_driver); + +MODULE_AUTHOR("Jihong Min <hurryman2212@gmail.com>"); +MODULE_DESCRIPTION("AMD Promontory 21 xHCI PCI Host Controller Driver"); +MODULE_IMPORT_NS("xhci"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 585b2f3117b0..039c26b241d0 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -696,12 +696,23 @@ static const struct pci_device_id pci_ids_renesas[] = { { /* end: all zeroes */ } }; +/* handled by xhci-pci-prom21 if enabled */ +static const struct pci_device_id pci_ids_prom21[] = { + { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_PROM21_XHCI_43FC) }, + { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_PROM21_XHCI_43FD) }, + { /* end: all zeroes */ } +}; + static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) { if (IS_ENABLED(CONFIG_USB_XHCI_PCI_RENESAS) && pci_match_id(pci_ids_renesas, dev)) return -ENODEV; + if (IS_ENABLED(CONFIG_USB_XHCI_PCI_PROM21) && + pci_match_id(pci_ids_prom21, dev)) + return -ENODEV; + return xhci_pci_common_probe(dev, id); } diff --git a/drivers/usb/host/xhci-pci.h b/drivers/usb/host/xhci-pci.h index e87c7d9d76b8..11f435f94322 100644 --- a/drivers/usb/host/xhci-pci.h +++ b/drivers/usb/host/xhci-pci.h @@ -4,6 +4,9 @@ #ifndef XHCI_PCI_H #define XHCI_PCI_H +#define PCI_DEVICE_ID_AMD_PROM21_XHCI_43FC 0x43fc +#define PCI_DEVICE_ID_AMD_PROM21_XHCI_43FD 0x43fd + int xhci_pci_common_probe(struct pci_dev *dev, const struct pci_device_id *id); void xhci_pci_remove(struct pci_dev *dev); diff --git a/drivers/usb/host/xhci-rcar.c b/drivers/usb/host/xhci-rcar.c index 8a993ee21c87..d747c0905827 100644 --- a/drivers/usb/host/xhci-rcar.c +++ b/drivers/usb/host/xhci-rcar.c @@ -32,29 +32,6 @@ MODULE_FIRMWARE(XHCI_RCAR_FIRMWARE_NAME_V1); MODULE_FIRMWARE(XHCI_RCAR_FIRMWARE_NAME_V3); -static void xhci_rcar_start_gen2(struct usb_hcd *hcd) -{ - /* LCLK Select */ - writel(RCAR_USB3_LCLK_ENA_VAL, hcd->regs + RCAR_USB3_LCLK); - /* USB3.0 Configuration */ - writel(RCAR_USB3_CONF1_VAL, hcd->regs + RCAR_USB3_CONF1); - writel(RCAR_USB3_CONF2_VAL, hcd->regs + RCAR_USB3_CONF2); - writel(RCAR_USB3_CONF3_VAL, hcd->regs + RCAR_USB3_CONF3); - /* USB3.0 Polarity */ - writel(RCAR_USB3_RX_POL_VAL, hcd->regs + RCAR_USB3_RX_POL); - writel(RCAR_USB3_TX_POL_VAL, hcd->regs + RCAR_USB3_TX_POL); -} - -static int xhci_rcar_is_gen2(struct device *dev) -{ - struct device_node *node = dev->of_node; - - return of_device_is_compatible(node, "renesas,xhci-r8a7790") || - of_device_is_compatible(node, "renesas,xhci-r8a7791") || - of_device_is_compatible(node, "renesas,xhci-r8a7793") || - of_device_is_compatible(node, "renesas,rcar-gen2-xhci"); -} - static void xhci_rcar_start(struct usb_hcd *hcd) { u32 temp; @@ -64,8 +41,23 @@ static void xhci_rcar_start(struct usb_hcd *hcd) temp = readl(hcd->regs + RCAR_USB3_INT_ENA); temp |= RCAR_USB3_INT_ENA_VAL; writel(temp, hcd->regs + RCAR_USB3_INT_ENA); - if (xhci_rcar_is_gen2(hcd->self.controller)) - xhci_rcar_start_gen2(hcd); + } +} + +static void xhci_rcar_gen2_start(struct usb_hcd *hcd) +{ + if (hcd->regs != NULL) { + xhci_rcar_start(hcd); + + /* LCLK Select */ + writel(RCAR_USB3_LCLK_ENA_VAL, hcd->regs + RCAR_USB3_LCLK); + /* USB3.0 Configuration */ + writel(RCAR_USB3_CONF1_VAL, hcd->regs + RCAR_USB3_CONF1); + writel(RCAR_USB3_CONF2_VAL, hcd->regs + RCAR_USB3_CONF2); + writel(RCAR_USB3_CONF3_VAL, hcd->regs + RCAR_USB3_CONF3); + /* USB3.0 Polarity */ + writel(RCAR_USB3_RX_POL_VAL, hcd->regs + RCAR_USB3_RX_POL); + writel(RCAR_USB3_TX_POL_VAL, hcd->regs + RCAR_USB3_TX_POL); } } @@ -192,13 +184,16 @@ static int xhci_rcar_init_quirk(struct usb_hcd *hcd) static int xhci_rcar_resume_quirk(struct usb_hcd *hcd) { + struct xhci_plat_priv *priv; int ret; ret = xhci_rcar_download_firmware(hcd); - if (!ret) - xhci_rcar_start(hcd); + if (ret) + return ret; - return ret; + priv = hcd_to_xhci_priv(hcd); + priv->plat_start(hcd); + return 0; } /* @@ -213,19 +208,20 @@ static int xhci_rcar_resume_quirk(struct usb_hcd *hcd) * long delay for the handshake of STS_HALT is neeed in xhci_suspend() * by using the XHCI_SLOW_SUSPEND quirk. */ -#define SET_XHCI_PLAT_PRIV_FOR_RCAR(firmware) \ - .firmware_name = firmware, \ - .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, - static const struct xhci_plat_priv xhci_plat_renesas_rcar_gen2 = { - SET_XHCI_PLAT_PRIV_FOR_RCAR(XHCI_RCAR_FIRMWARE_NAME_V1) + .firmware_name = XHCI_RCAR_FIRMWARE_NAME_V1, + .quirks = XHCI_NO_64BIT_SUPPORT | XHCI_SLOW_SUSPEND, + .init_quirk = xhci_rcar_init_quirk, + .plat_start = xhci_rcar_gen2_start, + .resume_quirk = xhci_rcar_resume_quirk, }; static const struct xhci_plat_priv xhci_plat_renesas_rcar_gen3 = { - SET_XHCI_PLAT_PRIV_FOR_RCAR(XHCI_RCAR_FIRMWARE_NAME_V3) + .firmware_name = XHCI_RCAR_FIRMWARE_NAME_V3, + .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, }; static const struct xhci_plat_priv xhci_plat_renesas_rzv2m = { diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index e47e644b296e..4f98d8269625 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -340,7 +340,7 @@ static bool trb_in_td(struct xhci_td *td, dma_addr_t suspect_dma) * Only for transfer and command rings where driver is the producer, not for * event rings. */ -static unsigned int xhci_num_trbs_free(struct xhci_ring *ring) +unsigned int xhci_num_trbs_free(struct xhci_ring *ring) { struct xhci_segment *enq_seg = ring->enq_seg; union xhci_trb *enq = ring->enqueue; @@ -609,7 +609,7 @@ static struct xhci_virt_ep *xhci_get_virt_ep(struct xhci_hcd *xhci, unsigned int slot_id, unsigned int ep_index) { - if (slot_id == 0 || slot_id >= MAX_HC_SLOTS) { + if (slot_id == 0 || slot_id > xhci->max_slots) { xhci_warn(xhci, "Invalid slot_id %u\n", slot_id); return NULL; } @@ -1613,7 +1613,7 @@ static void xhci_handle_cmd_disable_slot(struct xhci_hcd *xhci, int slot_id, /* Delete default control endpoint resources */ xhci_free_device_endpoint_resources(xhci, virt_dev, true); if (cmd_comp_code == COMP_SUCCESS) { - xhci->dcbaa->dev_context_ptrs[slot_id] = 0; + xhci->dcbaa.ctx_array[slot_id] = 0; xhci->devs[slot_id] = NULL; } } @@ -1804,7 +1804,7 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, struct xhci_command *cmd; u32 cmd_type; - if (slot_id >= MAX_HC_SLOTS) { + if (slot_id > xhci->max_slots) { xhci_warn(xhci, "Invalid slot_id %u\n", slot_id); return; } @@ -2395,7 +2395,6 @@ static void process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, u32 trb_comp_code; bool sum_trbs_for_length = false; u32 remaining, requested, ep_trb_len; - int short_framestatus; trb_comp_code = GET_COMP_CODE(le32_to_cpu(event->transfer_len)); urb_priv = td->urb->hcpriv; @@ -2404,8 +2403,6 @@ static void process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, requested = frame->length; remaining = EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)); ep_trb_len = TRB_LEN(le32_to_cpu(ep_trb->generic.field[2])); - short_framestatus = td->urb->transfer_flags & URB_SHORT_NOT_OK ? - -EREMOTEIO : 0; /* handle completion code */ switch (trb_comp_code) { @@ -2413,15 +2410,12 @@ static void process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, /* Don't overwrite status if TD had an error, see xHCI 4.9.1 */ if (td->error_mid_td) break; - if (remaining) { - frame->status = short_framestatus; + if (remaining) sum_trbs_for_length = true; - break; - } frame->status = 0; break; case COMP_SHORT_PACKET: - frame->status = short_framestatus; + frame->status = 0; sum_trbs_for_length = true; break; case COMP_BANDWIDTH_OVERRUN_ERROR: @@ -2456,7 +2450,7 @@ static void process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, break; case COMP_STOPPED_SHORT_PACKET: /* field normally containing residue now contains transferred */ - frame->status = short_framestatus; + frame->status = 0; requested = remaining; break; case COMP_STOPPED_LENGTH_INVALID: @@ -2492,26 +2486,6 @@ finish_td: finish_td(xhci, ep, ep_ring, td, trb_comp_code); } -static void skip_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td, - struct xhci_virt_ep *ep, int status) -{ - struct urb_priv *urb_priv; - struct usb_iso_packet_descriptor *frame; - int idx; - - urb_priv = td->urb->hcpriv; - idx = urb_priv->num_tds_done; - frame = &td->urb->iso_frame_desc[idx]; - - /* The transfer is partly done. */ - frame->status = -EXDEV; - - /* calc actual length */ - frame->actual_length = 0; - - xhci_dequeue_td(xhci, td, ep->ring, status); -} - /* * Process bulk and interrupt tds, update urb status and actual_length. */ @@ -2542,6 +2516,7 @@ static void process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, td->status = 0; break; case COMP_SHORT_PACKET: + ep->err_count = 0; td->status = 0; break; case COMP_STOPPED_SHORT_PACKET: @@ -2854,7 +2829,11 @@ static int handle_tx_event(struct xhci_hcd *xhci, if (trb_comp_code == COMP_STOPPED_LENGTH_INVALID) return 0; - skip_isoc_td(xhci, td, ep, status); + /* + * TD was missed, skip it. Core already initialized frame->status + * to -EXDEV and frame->actual_length to 0, nothing more to do. + */ + xhci_dequeue_td(xhci, td, ep_ring, 0); if (!list_empty(&ep_ring->td_list)) { if (ring_xrun_event) { @@ -4353,6 +4332,7 @@ static int queue_command(struct xhci_hcd *xhci, struct xhci_command *cmd, u32 field3, u32 field4, bool command_must_succeed) { int reserved_trbs = xhci->cmd_ring_reserved_trbs; + struct usb_hcd *hcd = xhci_to_hcd(xhci); int ret; if ((xhci->xhc_state & XHCI_STATE_DYING) || @@ -4362,6 +4342,11 @@ static int queue_command(struct xhci_hcd *xhci, struct xhci_command *cmd, return -ESHUTDOWN; } + if (!HCD_HW_ACCESSIBLE(hcd)) { + xhci_warn(xhci, "Can't queue command, xHC not accessible\n"); + return -ESHUTDOWN; + } + if (!command_must_succeed) reserved_trbs++; diff --git a/drivers/usb/host/xhci-sideband.c b/drivers/usb/host/xhci-sideband.c index 23153e136d4b..a5deeee4d5dc 100644 --- a/drivers/usb/host/xhci-sideband.c +++ b/drivers/usb/host/xhci-sideband.c @@ -58,6 +58,8 @@ xhci_ring_to_sgtable(struct xhci_sideband *sb, struct xhci_ring *ring) if (sg_alloc_table_from_pages(sgt, pages, n_pages, 0, sz, GFP_KERNEL)) goto err; + kvfree(pages); + /* * Save first segment dma address to sg dma_address field for the sideband * client to have access to the IOVA of the ring. diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index d5637b376367..e7e6d569f1db 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -293,6 +293,7 @@ struct tegra_xusb { struct reset_control *host_rst; struct reset_control *ss_rst; + struct tegra_pmc *pmc; struct device *genpd_dev_host; struct device *genpd_dev_ss; bool use_genpd; @@ -1189,20 +1190,23 @@ static int tegra_xusb_unpowergate_partitions(struct tegra_xusb *tegra) return rc; } } else { - rc = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_XUSBA, - tegra->ss_clk, - tegra->ss_rst); + rc = tegra_pmc_powergate_sequence_power_up(tegra->pmc, + TEGRA_POWERGATE_XUSBA, + tegra->ss_clk, + tegra->ss_rst); if (rc < 0) { dev_err(dev, "failed to enable XUSB SS partition\n"); return rc; } - rc = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_XUSBC, - tegra->host_clk, - tegra->host_rst); + rc = tegra_pmc_powergate_sequence_power_up(tegra->pmc, + TEGRA_POWERGATE_XUSBC, + tegra->host_clk, + tegra->host_rst); if (rc < 0) { dev_err(dev, "failed to enable XUSB Host partition\n"); - tegra_powergate_power_off(TEGRA_POWERGATE_XUSBA); + tegra_pmc_powergate_power_off(tegra->pmc, + TEGRA_POWERGATE_XUSBA); return rc; } } @@ -1229,18 +1233,21 @@ static int tegra_xusb_powergate_partitions(struct tegra_xusb *tegra) return rc; } } else { - rc = tegra_powergate_power_off(TEGRA_POWERGATE_XUSBC); + rc = tegra_pmc_powergate_power_off(tegra->pmc, + TEGRA_POWERGATE_XUSBC); if (rc < 0) { dev_err(dev, "failed to disable XUSB Host partition\n"); return rc; } - rc = tegra_powergate_power_off(TEGRA_POWERGATE_XUSBA); + rc = tegra_pmc_powergate_power_off(tegra->pmc, + TEGRA_POWERGATE_XUSBA); if (rc < 0) { dev_err(dev, "failed to disable XUSB SS partition\n"); - tegra_powergate_sequence_power_up(TEGRA_POWERGATE_XUSBC, - tegra->host_clk, - tegra->host_rst); + tegra_pmc_powergate_sequence_power_up(tegra->pmc, + TEGRA_POWERGATE_XUSBC, + tegra->host_clk, + tegra->host_rst); return rc; } } @@ -1737,6 +1744,13 @@ static int tegra_xusb_probe(struct platform_device *pdev) err); goto put_padctl; } + + tegra->pmc = devm_tegra_pmc_get(&pdev->dev); + if (IS_ERR(tegra->pmc)) { + err = dev_err_probe(&pdev->dev, PTR_ERR(tegra->pmc), + "failed to get PMC\n"); + goto put_padctl; + } } else { err = tegra_xusb_powerdomain_init(&pdev->dev, tegra); if (err) diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index a54f5b57f205..f44ccee5fa07 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -102,17 +102,10 @@ int xhci_handshake(void __iomem *ptr, u32 mask, u32 done, u64 timeout_us) */ void xhci_quiesce(struct xhci_hcd *xhci) { - u32 halted; u32 cmd; - u32 mask; - - mask = ~(XHCI_IRQS); - halted = readl(&xhci->op_regs->status) & STS_HALT; - if (!halted) - mask &= ~CMD_RUN; cmd = readl(&xhci->op_regs->command); - cmd &= mask; + cmd &= ~(CMD_RUN | XHCI_IRQS); writel(cmd, &xhci->op_regs->command); } @@ -558,7 +551,7 @@ static void xhci_init(struct usb_hcd *hcd) xhci_set_cmd_ring_deq(xhci); /* Set Device Context Base Address Array pointer */ - xhci_write_64(xhci, xhci->dcbaa->dma, &xhci->op_regs->dcbaa_ptr); + xhci_write_64(xhci, xhci->dcbaa.dma, &xhci->op_regs->dcbaa_ptr); /* Set Doorbell array pointer */ xhci_set_doorbell_ptr(xhci); @@ -999,6 +992,10 @@ int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup) /* step 1: stop endpoint */ /* skipped assuming that port suspend has done */ + /* Check if command ring is empty */ + if (!list_empty(&xhci->cmd_list)) + xhci_warn(xhci, "Suspending and stopping xHC with pending command!\n"); + /* step 2: clear Run/Stop bit */ command = readl(&xhci->op_regs->command); command &= ~CMD_RUN; @@ -3788,6 +3785,7 @@ static int xhci_free_streams(struct usb_hcd *hcd, struct usb_device *udev, struct xhci_virt_device *vdev; struct xhci_command *command; struct xhci_input_control_ctx *ctrl_ctx; + struct xhci_stream_info *stream_info[EP_CTX_PER_DEV]; unsigned int ep_index; unsigned long flags; u32 changed_ep_bitmask; @@ -3848,10 +3846,15 @@ static int xhci_free_streams(struct usb_hcd *hcd, struct usb_device *udev, if (ret < 0) return ret; + /* + * dma_free_coherent() called by xhci_free_stream_info() may sleep, + * so save stream_info pointers and clear references under lock, + * then free the memory outside lock. + */ spin_lock_irqsave(&xhci->lock, flags); for (i = 0; i < num_eps; i++) { ep_index = xhci_get_endpoint_index(&eps[i]->desc); - xhci_free_stream_info(xhci, vdev->eps[ep_index].stream_info); + stream_info[i] = vdev->eps[ep_index].stream_info; vdev->eps[ep_index].stream_info = NULL; /* FIXME Unset maxPstreams in endpoint context and * update deq ptr to point to normal string ring. @@ -3861,6 +3864,9 @@ static int xhci_free_streams(struct usb_hcd *hcd, struct usb_device *udev, } spin_unlock_irqrestore(&xhci->lock, flags); + for (i = 0; i < num_eps; i++) + xhci_free_stream_info(xhci, stream_info[i]); + return 0; } @@ -4460,9 +4466,9 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev, xhci_dbg_trace(xhci, trace_xhci_dbg_address, "Slot ID %d dcbaa entry @%p = %#016llx", udev->slot_id, - &xhci->dcbaa->dev_context_ptrs[udev->slot_id], + &xhci->dcbaa.ctx_array[udev->slot_id], (unsigned long long) - le64_to_cpu(xhci->dcbaa->dev_context_ptrs[udev->slot_id])); + le64_to_cpu(xhci->dcbaa.ctx_array[udev->slot_id])); xhci_dbg_trace(xhci, trace_xhci_dbg_address, "Output Context DMA address = %#08llx", (unsigned long long)virt_dev->out_ctx->dma); @@ -5460,7 +5466,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) if (xhci->hci_version > 0x100) xhci->hcc_params2 = readl(&xhci->cap_regs->hcc_params2); - xhci->max_slots = HCS_MAX_SLOTS(hcs_params1); + xhci->max_slots = min(HCS_MAX_SLOTS(hcs_params1), MAX_HC_SLOTS); xhci->max_ports = min(HCS_MAX_PORTS(hcs_params1), MAX_HC_PORTS); /* xhci-plat or xhci-pci might have set max_interrupters already */ if (!xhci->max_interrupters) @@ -5533,8 +5539,6 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) init_completion(&xhci->cmd_ring_stop_completion); xhci_hcd_page_size(xhci); - memset(xhci->devs, 0, MAX_HC_SLOTS * sizeof(*xhci->devs)); - /* Allocate xHCI data structures */ retval = xhci_mem_init(xhci, GFP_KERNEL); if (retval) diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index aeecd301f207..d02046a573e4 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -33,8 +33,11 @@ /* xHCI PCI Configuration Registers */ #define XHCI_SBRN_OFFSET (0x60) -/* Max number of USB devices for any host controller - limit in section 6.1 */ -#define MAX_HC_SLOTS 256 +/* + * Max number of Devices Slots. xHCI specification section 5.3.3 + * Valid values are in the range of 1 to 255. + */ +#define MAX_HC_SLOTS 255 /* * Max Number of Ports. xHCI specification section 5.3.3 * Valid values are in the range of 1 to 255. @@ -792,18 +795,18 @@ struct xhci_tt_bw_info { /** * struct xhci_device_context_array - * @dev_context_ptr array of 64-bit DMA addresses for device contexts + * @ctx_array: Pointer to an array of addresses. The array size depends on Max + * Slots read from HCSPARAMS1. + * @dma: DMA address to @ctx_array + * + * Device Context Base Address Array (DCBAA) - Section 6.1. + * ctx_array[0]: Scratchpad Buffer Array Base Address + * ctx_array[1-MaxSlots]: Device Context Base Address */ struct xhci_device_context_array { - /* 64-bit device addresses; we only write 32-bit addresses */ - __le64 dev_context_ptrs[MAX_HC_SLOTS]; - /* private xHCD pointers */ + __le64 *ctx_array; dma_addr_t dma; }; -/* - * TODO: change this to be dynamically sized at HC mem init time since the HC - * might not be able to handle the maximum number of devices possible. - */ struct xhci_transfer_event { @@ -1376,7 +1379,6 @@ struct xhci_ring { u32 cycle_state; unsigned int stream_id; unsigned int num_segs; - unsigned int num_trbs_free; /* used only by xhci DbC */ unsigned int bounce_buf_len; enum xhci_ring_type type; u32 old_trb_comp_code; @@ -1532,7 +1534,7 @@ struct xhci_hcd { /* optional reset controller */ struct reset_control *reset; /* data structures */ - struct xhci_device_context_array *dcbaa; + struct xhci_device_context_array dcbaa; struct xhci_interrupter **interrupters; struct xhci_ring *cmd_ring; unsigned int cmd_ring_state; @@ -1552,7 +1554,7 @@ struct xhci_hcd { /* these are not thread safe so use mutex */ struct mutex mutex; /* Internal mirror of the HW's dcbaa */ - struct xhci_virt_device *devs[MAX_HC_SLOTS]; + struct xhci_virt_device **devs; /* For keeping track of bandwidth domains per roothub. */ struct xhci_root_port_bw_info *rh_bw; @@ -1955,6 +1957,7 @@ void xhci_ring_doorbell_for_active_rings(struct xhci_hcd *xhci, void xhci_cleanup_command_queue(struct xhci_hcd *xhci); void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring); unsigned int count_trbs(u64 addr, u64 len); +unsigned int xhci_num_trbs_free(struct xhci_ring *ring); int xhci_stop_endpoint_sync(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, int suspend, gfp_t gfp_flags); void xhci_process_cancelled_tds(struct xhci_virt_ep *ep); diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig index 0b56b773dbdf..91b62acb537b 100644 --- a/drivers/usb/misc/Kconfig +++ b/drivers/usb/misc/Kconfig @@ -145,7 +145,7 @@ config USB_APPLEDISPLAY Displays over USB. This driver provides a sysfs interface. config USB_QCOM_EUD - tristate "QCOM Embedded USB Debugger(EUD) Driver" + tristate "Qualcomm Embedded USB Debugger(EUD) Driver" depends on ARCH_QCOM || COMPILE_TEST select QCOM_SCM select USB_ROLE_SWITCH diff --git a/drivers/usb/misc/chaoskey.c b/drivers/usb/misc/chaoskey.c index d8016540953f..9c06f7775301 100644 --- a/drivers/usb/misc/chaoskey.c +++ b/drivers/usb/misc/chaoskey.c @@ -320,7 +320,6 @@ bail: mutex_unlock(&dev->lock); destruction: mutex_unlock(&chaoskey_list_lock); - usb_dbg(interface, "release success"); return rv; } diff --git a/drivers/usb/misc/idmouse.c b/drivers/usb/misc/idmouse.c index 0f6b3464c2d6..3e37adf2bb57 100644 --- a/drivers/usb/misc/idmouse.c +++ b/drivers/usb/misc/idmouse.c @@ -63,6 +63,7 @@ MODULE_DEVICE_TABLE(usb, idmouse_table); /* structure to hold all of our device specific stuff */ struct usb_idmouse { + struct kref kref; struct usb_device *udev; /* save off the usb device pointer */ struct usb_interface *interface; /* the interface for this device */ @@ -209,8 +210,10 @@ static int idmouse_resume(struct usb_interface *intf) return 0; } -static inline void idmouse_delete(struct usb_idmouse *dev) +static inline void idmouse_delete(struct kref *kref) { + struct usb_idmouse *dev = container_of(kref, struct usb_idmouse, kref); + kfree(dev->bulk_in_buffer); kfree(dev); } @@ -254,6 +257,8 @@ static int idmouse_open(struct inode *inode, struct file *file) /* increment our usage count for the driver */ ++dev->open; + kref_get(&dev->kref); + /* save our object in the file's private structure */ file->private_data = dev; @@ -277,16 +282,11 @@ static int idmouse_release(struct inode *inode, struct file *file) /* lock our device */ mutex_lock(&dev->lock); - --dev->open; + mutex_unlock(&dev->lock); + + kref_put(&dev->kref, idmouse_delete); - if (!dev->present) { - /* the device was unplugged before the file was released */ - mutex_unlock(&dev->lock); - idmouse_delete(dev); - } else { - mutex_unlock(&dev->lock); - } return 0; } @@ -334,6 +334,7 @@ static int idmouse_probe(struct usb_interface *interface, if (dev == NULL) return -ENOMEM; + kref_init(&dev->kref); mutex_init(&dev->lock); dev->udev = udev; dev->interface = interface; @@ -342,8 +343,7 @@ static int idmouse_probe(struct usb_interface *interface, result = usb_find_bulk_in_endpoint(iface_desc, &endpoint); if (result) { dev_err(&interface->dev, "Unable to find bulk-in endpoint.\n"); - idmouse_delete(dev); - return result; + goto err_put_kref; } dev->orig_bi_size = usb_endpoint_maxp(endpoint); @@ -351,8 +351,8 @@ static int idmouse_probe(struct usb_interface *interface, dev->bulk_in_endpointAddr = endpoint->bEndpointAddress; dev->bulk_in_buffer = kmalloc(IMGSIZE + dev->bulk_in_size, GFP_KERNEL); if (!dev->bulk_in_buffer) { - idmouse_delete(dev); - return -ENOMEM; + result = -ENOMEM; + goto err_put_kref; } /* allow device read, write and ioctl */ @@ -364,14 +364,18 @@ static int idmouse_probe(struct usb_interface *interface, if (result) { /* something prevented us from registering this device */ dev_err(&interface->dev, "Unable to allocate minor number.\n"); - idmouse_delete(dev); - return result; + goto err_put_kref; } /* be noisy */ dev_info(&interface->dev,"%s now attached\n",DRIVER_DESC); return 0; + +err_put_kref: + kref_put(&dev->kref, idmouse_delete); + + return result; } static void idmouse_disconnect(struct usb_interface *interface) @@ -387,14 +391,9 @@ static void idmouse_disconnect(struct usb_interface *interface) /* prevent device read, write and ioctl */ dev->present = 0; - /* if the device is opened, idmouse_release will clean this up */ - if (!dev->open) { - mutex_unlock(&dev->lock); - idmouse_delete(dev); - } else { - /* unlock */ - mutex_unlock(&dev->lock); - } + mutex_unlock(&dev->lock); + + kref_put(&dev->kref, idmouse_delete); dev_info(&interface->dev, "disconnected\n"); } diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c index 22504c0a2841..de2b236ef903 100644 --- a/drivers/usb/misc/iowarrior.c +++ b/drivers/usb/misc/iowarrior.c @@ -72,6 +72,7 @@ static struct usb_driver iowarrior_driver; /* Structure to hold all of our device specific stuff */ struct iowarrior { + struct kref kref; struct mutex mutex; /* locks this structure */ struct usb_device *udev; /* save off the usb device pointer */ struct usb_interface *interface; /* the interface for this device */ @@ -240,8 +241,10 @@ static void iowarrior_write_callback(struct urb *urb) /* * iowarrior_delete */ -static inline void iowarrior_delete(struct iowarrior *dev) +static inline void iowarrior_delete(struct kref *kref) { + struct iowarrior *dev = container_of(kref, struct iowarrior, kref); + kfree(dev->int_in_buffer); usb_free_urb(dev->int_in_urb); kfree(dev->read_queue); @@ -637,6 +640,9 @@ static int iowarrior_open(struct inode *inode, struct file *file) } /* increment our usage count for the driver */ ++dev->opened; + + kref_get(&dev->kref); + /* save our object in the file's private structure */ file->private_data = dev; retval = 0; @@ -652,7 +658,6 @@ out: static int iowarrior_release(struct inode *inode, struct file *file) { struct iowarrior *dev; - int retval = 0; dev = file->private_data; if (!dev) @@ -660,29 +665,18 @@ static int iowarrior_release(struct inode *inode, struct file *file) /* lock our device */ mutex_lock(&dev->mutex); + dev->opened = 0; /* we're closing now */ - if (dev->opened <= 0) { - retval = -ENODEV; /* close called more than once */ - mutex_unlock(&dev->mutex); - } else { - dev->opened = 0; /* we're closing now */ - retval = 0; - if (dev->present) { - /* - The device is still connected so we only shutdown - pending read-/write-ops. - */ - usb_kill_urb(dev->int_in_urb); - wake_up_interruptible(&dev->read_wait); - wake_up_interruptible(&dev->write_wait); - mutex_unlock(&dev->mutex); - } else { - /* The device was unplugged, cleanup resources */ - mutex_unlock(&dev->mutex); - iowarrior_delete(dev); - } + if (dev->present) { + usb_kill_urb(dev->int_in_urb); + wake_up_interruptible(&dev->read_wait); + wake_up_interruptible(&dev->write_wait); } - return retval; + mutex_unlock(&dev->mutex); + + kref_put(&dev->kref, iowarrior_delete); + + return 0; } static __poll_t iowarrior_poll(struct file *file, poll_table * wait) @@ -767,6 +761,7 @@ static int iowarrior_probe(struct usb_interface *interface, if (!dev) return retval; + kref_init(&dev->kref); mutex_init(&dev->mutex); atomic_set(&dev->intr_idx, 0); @@ -885,7 +880,8 @@ static int iowarrior_probe(struct usb_interface *interface, return retval; error: - iowarrior_delete(dev); + kref_put(&dev->kref, iowarrior_delete); + return retval; } @@ -905,21 +901,18 @@ static void iowarrior_disconnect(struct usb_interface *interface) /* prevent device read, write and ioctl */ dev->present = 0; + /* write urbs are not stopped on close() so kill unconditionally */ + usb_kill_anchored_urbs(&dev->submitted); + if (dev->opened) { - /* There is a process that holds a filedescriptor to the device , - so we only shutdown read-/write-ops going on. - Deleting the device is postponed until close() was called. - */ usb_kill_urb(dev->int_in_urb); - usb_kill_anchored_urbs(&dev->submitted); wake_up_interruptible(&dev->read_wait); wake_up_interruptible(&dev->write_wait); - mutex_unlock(&dev->mutex); - } else { - /* no process is using the device, cleanup now */ - mutex_unlock(&dev->mutex); - iowarrior_delete(dev); } + + mutex_unlock(&dev->mutex); + + kref_put(&dev->kref, iowarrior_delete); } /* usb specific object needed to register this driver with the usb subsystem */ diff --git a/drivers/usb/misc/ldusb.c b/drivers/usb/misc/ldusb.c index c74f142f6637..71132a15e771 100644 --- a/drivers/usb/misc/ldusb.c +++ b/drivers/usb/misc/ldusb.c @@ -150,6 +150,7 @@ MODULE_PARM_DESC(min_interrupt_out_interval, "Minimum interrupt out interval in /* Structure to hold all of our device specific stuff */ struct ld_usb { + struct kref kref; struct mutex mutex; /* locks this structure */ struct usb_interface *intf; /* save off the usb interface pointer */ unsigned long disconnected:1; @@ -201,8 +202,10 @@ static void ld_usb_abort_transfers(struct ld_usb *dev) /* * ld_usb_delete */ -static void ld_usb_delete(struct ld_usb *dev) +static void ld_usb_delete(struct kref *kref) { + struct ld_usb *dev = container_of(kref, struct ld_usb, kref); + /* free data structures */ usb_free_urb(dev->interrupt_in_urb); usb_free_urb(dev->interrupt_out_urb); @@ -355,6 +358,8 @@ static int ld_usb_open(struct inode *inode, struct file *file) goto unlock_exit; } + kref_get(&dev->kref); + /* save device in the file's private structure */ file->private_data = dev; @@ -381,17 +386,8 @@ static int ld_usb_release(struct inode *inode, struct file *file) mutex_lock(&dev->mutex); - if (dev->open_count != 1) { - retval = -ENODEV; + if (dev->disconnected) goto unlock_exit; - } - if (dev->disconnected) { - /* the device was unplugged before the file was released */ - mutex_unlock(&dev->mutex); - /* unlock here as ld_usb_delete frees dev */ - ld_usb_delete(dev); - goto exit; - } /* wait until write transfer is finished */ if (dev->interrupt_out_busy) @@ -401,7 +397,7 @@ static int ld_usb_release(struct inode *inode, struct file *file) unlock_exit: mutex_unlock(&dev->mutex); - + kref_put(&dev->kref, ld_usb_delete); exit: return retval; } @@ -659,6 +655,8 @@ static int ld_usb_probe(struct usb_interface *intf, const struct usb_device_id * dev = kzalloc_obj(*dev); if (!dev) goto exit; + + kref_init(&dev->kref); mutex_init(&dev->mutex); spin_lock_init(&dev->rbsl); dev->intf = intf; @@ -740,7 +738,7 @@ exit: return retval; error: - ld_usb_delete(dev); + kref_put(&dev->kref, ld_usb_delete); return retval; } @@ -768,18 +766,18 @@ static void ld_usb_disconnect(struct usb_interface *intf) mutex_lock(&dev->mutex); - /* if the device is not opened, then we clean up right now */ - if (!dev->open_count) { - mutex_unlock(&dev->mutex); - ld_usb_delete(dev); - } else { - dev->disconnected = 1; + dev->disconnected = 1; + + if (dev->open_count) { /* wake up pollers */ wake_up_interruptible_all(&dev->read_wait); wake_up_interruptible_all(&dev->write_wait); - mutex_unlock(&dev->mutex); } + mutex_unlock(&dev->mutex); + + kref_put(&dev->kref, ld_usb_delete); + dev_info(&intf->dev, "LD USB Device #%d now disconnected\n", (minor - USB_LD_MINOR_BASE)); } diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c index 052ffc2e71ee..18dd4115befb 100644 --- a/drivers/usb/misc/legousbtower.c +++ b/drivers/usb/misc/legousbtower.c @@ -185,6 +185,7 @@ MODULE_DEVICE_TABLE(usb, tower_table); /* Structure to hold all of our device specific stuff */ struct lego_usb_tower { + struct kref kref; struct mutex lock; /* locks this structure */ struct usb_device *udev; /* save off the usb device pointer */ unsigned char minor; /* the starting minor number for this device */ @@ -220,7 +221,6 @@ struct lego_usb_tower { /* local function prototypes */ static ssize_t tower_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos); static ssize_t tower_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos); -static inline void tower_delete(struct lego_usb_tower *dev); static int tower_open(struct inode *inode, struct file *file); static int tower_release(struct inode *inode, struct file *file); static __poll_t tower_poll(struct file *file, poll_table *wait); @@ -286,8 +286,10 @@ static inline void lego_usb_tower_debug_data(struct device *dev, /* * tower_delete */ -static inline void tower_delete(struct lego_usb_tower *dev) +static inline void tower_delete(struct kref *kref) { + struct lego_usb_tower *dev = container_of(kref, struct lego_usb_tower, kref); + /* free data structures */ usb_free_urb(dev->interrupt_in_urb); usb_free_urb(dev->interrupt_out_urb); @@ -381,6 +383,8 @@ static int tower_open(struct inode *inode, struct file *file) dev->open_count = 1; + kref_get(&dev->kref); + unlock_exit: mutex_unlock(&dev->lock); @@ -404,14 +408,8 @@ static int tower_release(struct inode *inode, struct file *file) mutex_lock(&dev->lock); - if (dev->disconnected) { - /* the device was unplugged before the file was released */ - - /* unlock here as tower_delete frees dev */ - mutex_unlock(&dev->lock); - tower_delete(dev); - goto exit; - } + if (dev->disconnected) + goto out_unlock; /* wait until write transfer is finished */ if (dev->interrupt_out_busy) { @@ -425,7 +423,9 @@ static int tower_release(struct inode *inode, struct file *file) dev->open_count = 0; +out_unlock: mutex_unlock(&dev->lock); + kref_put(&dev->kref, tower_delete); exit: return retval; } @@ -752,6 +752,7 @@ static int tower_probe(struct usb_interface *interface, const struct usb_device_ if (!dev) goto exit; + kref_init(&dev->kref); mutex_init(&dev->lock); dev->udev = usb_get_dev(udev); spin_lock_init(&dev->read_buffer_lock); @@ -828,7 +829,7 @@ exit: return retval; error: - tower_delete(dev); + kref_put(&dev->kref, tower_delete); return retval; } @@ -856,18 +857,18 @@ static void tower_disconnect(struct usb_interface *interface) mutex_lock(&dev->lock); - /* if the device is not opened, then we clean up right now */ - if (!dev->open_count) { - mutex_unlock(&dev->lock); - tower_delete(dev); - } else { - dev->disconnected = 1; + dev->disconnected = 1; + + if (dev->open_count) { /* wake up pollers */ wake_up_interruptible_all(&dev->read_wait); wake_up_interruptible_all(&dev->write_wait); - mutex_unlock(&dev->lock); } + mutex_unlock(&dev->lock); + + kref_put(&dev->kref, tower_delete); + dev_info(&interface->dev, "LEGO USB Tower #%d now disconnected\n", (minor - LEGO_USB_TOWER_MINOR_BASE)); } diff --git a/drivers/usb/misc/onboard_usb_dev.c b/drivers/usb/misc/onboard_usb_dev.c index 7cdbdfe07a76..11508ed4df25 100644 --- a/drivers/usb/misc/onboard_usb_dev.c +++ b/drivers/usb/misc/onboard_usb_dev.c @@ -665,6 +665,7 @@ static const struct usb_device_id onboard_dev_id_table[] = { { 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_GENESYS, 0x0625) }, /* Genesys Logic GL3590 USB 3.2 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 */ diff --git a/drivers/usb/misc/onboard_usb_dev.h b/drivers/usb/misc/onboard_usb_dev.h index ac1aa3e122ad..3523f8f8a149 100644 --- a/drivers/usb/misc/onboard_usb_dev.h +++ b/drivers/usb/misc/onboard_usb_dev.h @@ -101,6 +101,13 @@ static const struct onboard_dev_pdata genesys_gl852g_data = { .is_hub = true, }; +static const struct onboard_dev_pdata genesys_gl3590_data = { + .reset_us = 50, + .num_supplies = 2, + .supply_names = { "vdd", "vdd12" }, + .is_hub = true, +}; + static const struct onboard_dev_pdata usb_a_conn_data = { .num_supplies = 1, .supply_names = { "vbus" }, @@ -146,6 +153,7 @@ static const struct of_device_id onboard_dev_match[] = { { .compatible = "usb5e3,608", .data = &genesys_gl850g_data, }, { .compatible = "usb5e3,610", .data = &genesys_gl852g_data, }, { .compatible = "usb5e3,620", .data = &genesys_gl852g_data, }, + { .compatible = "usb5e3,625", .data = &genesys_gl3590_data, }, { .compatible = "usb5e3,626", .data = &genesys_gl852g_data, }, { .compatible = "usbbda,179", .data = &realtek_rtl8188etv_data, }, { .compatible = "usbbda,411", .data = &realtek_rts5411_data, }, diff --git a/drivers/usb/misc/usb-ljca.c b/drivers/usb/misc/usb-ljca.c index c60121faa3da..78e94dd89da5 100644 --- a/drivers/usb/misc/usb-ljca.c +++ b/drivers/usb/misc/usb-ljca.c @@ -9,7 +9,6 @@ #include <linux/auxiliary_bus.h> #include <linux/dev_printk.h> #include <linux/kernel.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/slab.h> diff --git a/drivers/usb/misc/usb251xb.c b/drivers/usb/misc/usb251xb.c index 7c0778631bea..fb0693742f01 100644 --- a/drivers/usb/misc/usb251xb.c +++ b/drivers/usb/misc/usb251xb.c @@ -746,15 +746,15 @@ static int usb251xb_i2c_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(usb251xb_i2c_pm_ops, usb251xb_i2c_suspend, usb251xb_i2c_resume); static const struct i2c_device_id usb251xb_id[] = { - { "usb2422" }, - { "usb2512b" }, - { "usb2512bi" }, - { "usb2513b" }, - { "usb2513bi" }, - { "usb2514b" }, - { "usb2514bi" }, - { "usb2517" }, - { "usb2517i" }, + { .name = "usb2422" }, + { .name = "usb2512b" }, + { .name = "usb2512bi" }, + { .name = "usb2513b" }, + { .name = "usb2513bi" }, + { .name = "usb2514b" }, + { .name = "usb2514bi" }, + { .name = "usb2517" }, + { .name = "usb2517i" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, usb251xb_id); diff --git a/drivers/usb/misc/usb3503.c b/drivers/usb/misc/usb3503.c index 322e59381b78..759770a13260 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 }, + { .name = USB3503_I2C_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, usb3503_id); diff --git a/drivers/usb/misc/usb4604.c b/drivers/usb/misc/usb4604.c index c9a2fb3518ae..2ae9656715e0 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" }, + { .name = "usb4604" }, { } }; MODULE_DEVICE_TABLE(i2c, usb4604_id); diff --git a/drivers/usb/misc/usbio.c b/drivers/usb/misc/usbio.c index 02d1e0760f0c..3c2474dca810 100644 --- a/drivers/usb/misc/usbio.c +++ b/drivers/usb/misc/usbio.c @@ -344,6 +344,10 @@ read: if (ibuf_len < bpkt_len) return -ENOSPC; + /* The device must not claim more payload than it actually sent. */ + if (bpkt_len > act - sizeof(*bpkt)) + return -EPROTO; + memcpy(ibuf, bpkt->data, bpkt_len); return bpkt_len; @@ -518,7 +522,7 @@ static int usbio_resume(struct usb_interface *intf) static void usbio_disconnect(struct usb_interface *intf) { struct usbio_device *usbio = usb_get_intfdata(intf); - struct usbio_client *client; + struct usbio_client *client, *next; /* Wakeup any clients waiting for a reply */ usbio->rxdat_len = 0; @@ -535,7 +539,7 @@ static void usbio_disconnect(struct usb_interface *intf) usb_kill_urb(usbio->urb); usb_free_urb(usbio->urb); - list_for_each_entry_reverse(client, &usbio->cli_list, link) { + list_for_each_entry_safe_reverse(client, next, &usbio->cli_list, link) { auxiliary_device_delete(&client->auxdev); auxiliary_device_uninit(&client->auxdev); } diff --git a/drivers/usb/misc/uss720.c b/drivers/usb/misc/uss720.c index b7d3c44b970e..1ce48f5832d7 100644 --- a/drivers/usb/misc/uss720.c +++ b/drivers/usb/misc/uss720.c @@ -732,8 +732,11 @@ static int uss720_probe(struct usb_interface *intf, * here. */ ret = get_1284_register(pp, 0, ®, GFP_KERNEL); dev_dbg(&intf->dev, "reg: %7ph\n", priv->reg); - if (ret < 0) + if (ret < 0) { + priv->pp = NULL; + parport_del_port(pp); goto probe_abort; + } ret = usb_find_last_int_in_endpoint(interface, &epd); if (!ret) { diff --git a/drivers/usb/mtu3/mtu3_gadget.c b/drivers/usb/mtu3/mtu3_gadget.c index da29f467943f..f224f2ee379a 100644 --- a/drivers/usb/mtu3/mtu3_gadget.c +++ b/drivers/usb/mtu3/mtu3_gadget.c @@ -305,6 +305,7 @@ static int mtu3_gadget_queue(struct usb_ep *ep, if (mtu3_prepare_transfer(mep)) { ret = -EAGAIN; + usb_gadget_unmap_request(&mtu->g, req, mep->is_in); goto error; } diff --git a/drivers/usb/phy/phy-isp1301.c b/drivers/usb/phy/phy-isp1301.c index 2940f0c84e1b..73cc70e958de 100644 --- a/drivers/usb/phy/phy-isp1301.c +++ b/drivers/usb/phy/phy-isp1301.c @@ -25,7 +25,7 @@ struct isp1301 { #define phy_to_isp(p) (container_of((p), struct isp1301, phy)) static const struct i2c_device_id isp1301_id[] = { - { "isp1301" }, + { .name = "isp1301" }, { } }; MODULE_DEVICE_TABLE(i2c, isp1301_id); diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c index d974da43fba3..3a574b6ee0c4 100644 --- a/drivers/usb/serial/ark3116.c +++ b/drivers/usb/serial/ark3116.c @@ -28,7 +28,6 @@ #include <linux/usb/serial.h> #include <linux/serial.h> #include <linux/serial_reg.h> -#include <linux/uaccess.h> #include <linux/mutex.h> #include <linux/spinlock.h> diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c index 7bbd9523d4e9..cfff292631aa 100644 --- a/drivers/usb/serial/belkin_sa.c +++ b/drivers/usb/serial/belkin_sa.c @@ -23,11 +23,9 @@ #include <linux/errno.h> #include <linux/slab.h> #include <linux/tty.h> -#include <linux/tty_driver.h> #include <linux/tty_flip.h> #include <linux/module.h> #include <linux/spinlock.h> -#include <linux/uaccess.h> #include <linux/usb.h> #include <linux/usb/serial.h> #include "belkin_sa.h" diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c index 4e8ceb23c27d..57e20eb02a46 100644 --- a/drivers/usb/serial/cyberjack.c +++ b/drivers/usb/serial/cyberjack.c @@ -28,11 +28,9 @@ #include <linux/errno.h> #include <linux/slab.h> #include <linux/tty.h> -#include <linux/tty_driver.h> #include <linux/tty_flip.h> #include <linux/module.h> #include <linux/spinlock.h> -#include <linux/uaccess.h> #include <linux/usb.h> #include <linux/usb/serial.h> diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index bcf302e88ca4..78fb1389b34c 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -25,7 +25,6 @@ #include <linux/errno.h> #include <linux/slab.h> #include <linux/tty.h> -#include <linux/tty_driver.h> #include <linux/tty_flip.h> #include <linux/module.h> #include <linux/moduleparam.h> @@ -35,7 +34,6 @@ #include <linux/serial.h> #include <linux/kfifo.h> #include <linux/delay.h> -#include <linux/uaccess.h> #include <linux/unaligned.h> #include "cypress_m8.h" diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index c481208255eb..dea039163661 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -15,11 +15,9 @@ #include <linux/errno.h> #include <linux/slab.h> #include <linux/tty.h> -#include <linux/tty_driver.h> #include <linux/tty_flip.h> #include <linux/module.h> #include <linux/spinlock.h> -#include <linux/uaccess.h> #include <linux/usb.h> #include <linux/wait.h> #include <linux/sched/signal.h> @@ -394,12 +392,14 @@ static int digi_write_oob_command(struct usb_serial_port *port, len &= ~3; memcpy(oob_port->write_urb->transfer_buffer, buf, len); oob_port->write_urb->transfer_buffer_length = len; + ret = usb_submit_urb(oob_port->write_urb, GFP_ATOMIC); - if (ret == 0) { - oob_priv->dp_write_urb_in_use = 1; - count -= len; - buf += len; - } + if (ret) + break; + + oob_priv->dp_write_urb_in_use = 1; + count -= len; + buf += len; } spin_unlock_irqrestore(&oob_priv->dp_port_lock, flags); if (ret) @@ -429,20 +429,22 @@ static int digi_write_inb_command(struct usb_serial_port *port, int len; struct digi_port *priv = usb_get_serial_port_data(port); unsigned char *data = port->write_urb->transfer_buffer; + unsigned long expire; unsigned long flags; dev_dbg(&port->dev, "digi_write_inb_command: TOP: port=%d, count=%d\n", priv->dp_port_num, count); if (timeout) - timeout += jiffies; - else - timeout = ULONG_MAX; + expire = jiffies + timeout; spin_lock_irqsave(&priv->dp_port_lock, flags); while (count > 0 && ret == 0) { - while (priv->dp_write_urb_in_use && - time_before(jiffies, timeout)) { + while (priv->dp_write_urb_in_use) { + if (timeout && time_after(jiffies, expire)) { + ret = -ETIMEDOUT; + break; + } cond_wait_interruptible_timeout_irqrestore( &priv->write_wait, DIGI_RETRY_TIMEOUT, &priv->dp_port_lock, flags); @@ -451,6 +453,9 @@ static int digi_write_inb_command(struct usb_serial_port *port, spin_lock_irqsave(&priv->dp_port_lock, flags); } + if (ret) + break; + /* len must be a multiple of 4 and small enough to */ /* guarantee the write will send buffered data first, */ /* so commands are in order with data and not split */ @@ -1071,6 +1076,7 @@ static int digi_open(struct tty_struct *tty, struct usb_serial_port *port) unsigned char buf[32]; struct digi_port *priv = usb_get_serial_port_data(port); struct ktermios not_termios; + int throttled; /* be sure the device is started up */ if (digi_startup_device(port->serial) != 0) @@ -1098,6 +1104,21 @@ static int digi_open(struct tty_struct *tty, struct usb_serial_port *port) not_termios.c_iflag = ~tty->termios.c_iflag; digi_set_termios(tty, port, ¬_termios); } + + spin_lock_irq(&priv->dp_port_lock); + throttled = priv->dp_throttle_restart; + priv->dp_throttled = 0; + priv->dp_throttle_restart = 0; + spin_unlock_irq(&priv->dp_port_lock); + + if (throttled) { + ret = usb_submit_urb(port->read_urb, GFP_KERNEL); + if (ret) { + dev_err(&port->dev, "failed to submit read urb: %d\n", ret); + return ret; + } + } + return 0; } diff --git a/drivers/usb/serial/empeg.c b/drivers/usb/serial/empeg.c index aedcf7ebd269..4937575caff1 100644 --- a/drivers/usb/serial/empeg.c +++ b/drivers/usb/serial/empeg.c @@ -16,11 +16,8 @@ #include <linux/errno.h> #include <linux/slab.h> #include <linux/tty.h> -#include <linux/tty_driver.h> -#include <linux/tty_flip.h> #include <linux/module.h> #include <linux/spinlock.h> -#include <linux/uaccess.h> #include <linux/usb.h> #include <linux/usb/serial.h> diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c index 9262a2ac97f5..dcceb95d7d99 100644 --- a/drivers/usb/serial/f81232.c +++ b/drivers/usb/serial/f81232.c @@ -11,13 +11,10 @@ #include <linux/errno.h> #include <linux/slab.h> #include <linux/tty.h> -#include <linux/tty_driver.h> #include <linux/tty_flip.h> #include <linux/serial.h> #include <linux/module.h> -#include <linux/moduleparam.h> #include <linux/mutex.h> -#include <linux/uaccess.h> #include <linux/usb.h> #include <linux/usb/serial.h> #include <linux/serial_reg.h> diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c index 685930ac8383..0037767e4bbf 100644 --- a/drivers/usb/serial/f81534.c +++ b/drivers/usb/serial/f81534.c @@ -28,7 +28,6 @@ #include <linux/usb/serial.h> #include <linux/serial_reg.h> #include <linux/module.h> -#include <linux/uaccess.h> /* Serial Port register Address */ #define F81534_UART_BASE_ADDRESS 0x1200 diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index af14548fa03d..88dd32da82c2 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -31,7 +31,6 @@ #include <linux/errno.h> #include <linux/slab.h> #include <linux/tty.h> -#include <linux/tty_driver.h> #include <linux/tty_flip.h> #include <linux/module.h> #include <linux/spinlock.h> diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c index 7205483a0115..8020149f5658 100644 --- a/drivers/usb/serial/garmin_gps.c +++ b/drivers/usb/serial/garmin_gps.c @@ -15,12 +15,9 @@ #include <linux/slab.h> #include <linux/timer.h> #include <linux/tty.h> -#include <linux/tty_driver.h> #include <linux/tty_flip.h> #include <linux/module.h> #include <linux/spinlock.h> -#include <linux/uaccess.h> -#include <linux/atomic.h> #include <linux/usb.h> #include <linux/usb/serial.h> diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index 757f0a586ddb..6eaf74930aa3 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -17,7 +17,6 @@ #include <linux/moduleparam.h> #include <linux/usb.h> #include <linux/usb/serial.h> -#include <linux/uaccess.h> #include <linux/kfifo.h> #include <linux/serial.h> diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index 3f5889145e51..34ccf7820537 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -25,12 +25,12 @@ * */ +#include <linux/atomic.h> #include <linux/kernel.h> #include <linux/jiffies.h> #include <linux/errno.h> #include <linux/slab.h> #include <linux/tty.h> -#include <linux/tty_driver.h> #include <linux/tty_flip.h> #include <linux/module.h> #include <linux/spinlock.h> diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index d48819d785ef..07c0eff3bef4 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -18,7 +18,6 @@ #include <linux/errno.h> #include <linux/slab.h> #include <linux/tty.h> -#include <linux/tty_driver.h> #include <linux/tty_flip.h> #include <linux/module.h> #include <linux/spinlock.h> @@ -28,7 +27,6 @@ #include <linux/kfifo.h> #include <linux/ioctl.h> #include <linux/firmware.h> -#include <linux/uaccess.h> #include <linux/usb.h> #include <linux/usb/serial.h> diff --git a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c index 3c6a9b9b9c2b..c01bdbcb3f55 100644 --- a/drivers/usb/serial/ipaq.c +++ b/drivers/usb/serial/ipaq.c @@ -10,11 +10,8 @@ #include <linux/errno.h> #include <linux/slab.h> #include <linux/tty.h> -#include <linux/tty_driver.h> -#include <linux/tty_flip.h> #include <linux/module.h> #include <linux/spinlock.h> -#include <linux/uaccess.h> #include <linux/usb.h> #include <linux/usb/serial.h> diff --git a/drivers/usb/serial/ipw.c b/drivers/usb/serial/ipw.c index 83709d678b3a..5be153f2c68d 100644 --- a/drivers/usb/serial/ipw.c +++ b/drivers/usb/serial/ipw.c @@ -36,12 +36,10 @@ #include <linux/errno.h> #include <linux/slab.h> #include <linux/tty.h> -#include <linux/tty_flip.h> #include <linux/module.h> #include <linux/spinlock.h> #include <linux/usb.h> #include <linux/usb/serial.h> -#include <linux/uaccess.h> #include "usb-wwan.h" #define DRIVER_AUTHOR "Roelf Diedericks" diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c index 12e928d25ba1..aac84539730d 100644 --- a/drivers/usb/serial/ir-usb.c +++ b/drivers/usb/serial/ir-usb.c @@ -25,11 +25,9 @@ #include <linux/init.h> #include <linux/slab.h> #include <linux/tty.h> -#include <linux/tty_driver.h> #include <linux/tty_flip.h> #include <linux/module.h> #include <linux/spinlock.h> -#include <linux/uaccess.h> #include <linux/usb.h> #include <linux/usb/serial.h> #include <linux/usb/irda.h> diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c index 0ca111b111c7..3b1f7546fb5c 100644 --- a/drivers/usb/serial/iuu_phoenix.c +++ b/drivers/usb/serial/iuu_phoenix.c @@ -14,13 +14,11 @@ #include <linux/errno.h> #include <linux/slab.h> #include <linux/tty.h> -#include <linux/tty_driver.h> #include <linux/tty_flip.h> #include <linux/serial.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/spinlock.h> -#include <linux/uaccess.h> #include <linux/usb.h> #include <linux/usb/serial.h> #include "iuu_phoenix.h" diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index 28b80607cebd..4d3746c7a94e 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -29,11 +29,9 @@ #include <linux/errno.h> #include <linux/slab.h> #include <linux/tty.h> -#include <linux/tty_driver.h> #include <linux/tty_flip.h> #include <linux/module.h> #include <linux/spinlock.h> -#include <linux/uaccess.h> #include <linux/usb.h> #include <linux/usb/serial.h> #include <linux/usb/ezusb.h> diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index 5036600dd334..f05bcce60600 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -15,12 +15,10 @@ #include <linux/errno.h> #include <linux/slab.h> #include <linux/tty.h> -#include <linux/tty_driver.h> #include <linux/tty_flip.h> #include <linux/module.h> #include <linux/spinlock.h> #include <linux/workqueue.h> -#include <linux/uaccess.h> #include <linux/usb.h> #include <linux/usb/serial.h> #include <linux/usb/ezusb.h> @@ -518,7 +516,7 @@ static int keyspan_pda_write_start(struct usb_serial_port *port) if (count == room) schedule_work(&priv->unthrottle_work); - return count; + return 0; } static void keyspan_pda_write_bulk_callback(struct urb *urb) diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index e72a0b45a707..b74c88fec03a 100644 --- a/drivers/usb/serial/kl5kusb105.c +++ b/drivers/usb/serial/kl5kusb105.c @@ -35,10 +35,8 @@ #include <linux/errno.h> #include <linux/slab.h> #include <linux/tty.h> -#include <linux/tty_driver.h> #include <linux/tty_flip.h> #include <linux/module.h> -#include <linux/uaccess.h> #include <linux/unaligned.h> #include <linux/usb.h> #include <linux/usb/serial.h> diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index 6126afd67a7b..4a622cf8e6ae 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -23,11 +23,9 @@ #include <linux/errno.h> #include <linux/slab.h> #include <linux/tty.h> -#include <linux/tty_driver.h> #include <linux/tty_flip.h> #include <linux/module.h> #include <linux/spinlock.h> -#include <linux/uaccess.h> #include <linux/usb.h> #include <linux/usb/serial.h> #include <linux/ioctl.h> diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index 163161881d2d..a6de18917ec9 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -21,11 +21,9 @@ #include <linux/errno.h> #include <linux/slab.h> #include <linux/tty.h> -#include <linux/tty_driver.h> #include <linux/tty_flip.h> #include <linux/module.h> #include <linux/spinlock.h> -#include <linux/uaccess.h> #include <linux/unaligned.h> #include <linux/usb.h> #include <linux/usb/serial.h> diff --git a/drivers/usb/serial/metro-usb.c b/drivers/usb/serial/metro-usb.c index bc834cc48550..35473544f1c8 100644 --- a/drivers/usb/serial/metro-usb.c +++ b/drivers/usb/serial/metro-usb.c @@ -13,11 +13,8 @@ #include <linux/usb.h> #include <linux/errno.h> #include <linux/slab.h> -#include <linux/tty_driver.h> #include <linux/tty_flip.h> -#include <linux/moduleparam.h> #include <linux/spinlock.h> -#include <linux/uaccess.h> #include <linux/usb/serial.h> #define DRIVER_DESC "Metrologic Instruments Inc. - USB-POS driver" diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index 94459408e7fb..104f43a8f4d8 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -17,11 +17,12 @@ * Copyright (C) 2000 Inside Out Networks, All rights reserved. * Copyright (C) 2001-2002 Greg Kroah-Hartman <greg@kroah.com> */ + +#include <linux/atomic.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/slab.h> #include <linux/tty.h> -#include <linux/tty_driver.h> #include <linux/tty_flip.h> #include <linux/module.h> #include <linux/spinlock.h> diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index c0d0c24b074b..ef803e74ab4b 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -13,7 +13,6 @@ #include <linux/errno.h> #include <linux/slab.h> #include <linux/tty.h> -#include <linux/tty_driver.h> #include <linux/tty_flip.h> #include <linux/module.h> #include <linux/serial.h> diff --git a/drivers/usb/serial/mxuport.c b/drivers/usb/serial/mxuport.c index c9b9928c473a..e3c5a1b97542 100644 --- a/drivers/usb/serial/mxuport.c +++ b/drivers/usb/serial/mxuport.c @@ -20,9 +20,7 @@ #include <linux/serial_reg.h> #include <linux/slab.h> #include <linux/tty.h> -#include <linux/tty_driver.h> #include <linux/tty_flip.h> -#include <linux/uaccess.h> #include <linux/usb.h> #include <linux/usb/serial.h> #include <linux/unaligned.h> @@ -146,11 +144,6 @@ #define MX_WAIT_FOR_LOW_WATER 0x0040 #define MX_WAIT_FOR_SEND_NEXT 0x0080 -#define MX_UPORT_2_PORT BIT(0) -#define MX_UPORT_4_PORT BIT(1) -#define MX_UPORT_8_PORT BIT(2) -#define MX_UPORT_16_PORT BIT(3) - /* This structure holds all of the local port information */ struct mxuport_port { u8 mcr_state; /* Last MCR state */ @@ -159,26 +152,31 @@ struct mxuport_port { spinlock_t spinlock; /* Protects msr_state */ }; +/* Encode number of ports (2..16 or undefined) */ +#define MX_PORTS_MASK GENMASK(3, 0) +#define MX_PORTS_OFFSET 1 +#define MX_PORTS(n) (((n) - MX_PORTS_OFFSET) & MX_PORTS_MASK) + /* Table of devices that work with this driver */ static const struct usb_device_id mxuport_idtable[] = { { USB_DEVICE(MX_USBSERIAL_VID, MX_UPORT1250_PID), - .driver_info = MX_UPORT_2_PORT }, + .driver_info = MX_PORTS(2) }, { USB_DEVICE(MX_USBSERIAL_VID, MX_UPORT1251_PID), - .driver_info = MX_UPORT_2_PORT }, + .driver_info = MX_PORTS(2) }, { USB_DEVICE(MX_USBSERIAL_VID, MX_UPORT1410_PID), - .driver_info = MX_UPORT_4_PORT }, + .driver_info = MX_PORTS(4) }, { USB_DEVICE(MX_USBSERIAL_VID, MX_UPORT1450_PID), - .driver_info = MX_UPORT_4_PORT }, + .driver_info = MX_PORTS(4) }, { USB_DEVICE(MX_USBSERIAL_VID, MX_UPORT1451_PID), - .driver_info = MX_UPORT_4_PORT }, + .driver_info = MX_PORTS(4) }, { USB_DEVICE(MX_USBSERIAL_VID, MX_UPORT1618_PID), - .driver_info = MX_UPORT_8_PORT }, + .driver_info = MX_PORTS(8) }, { USB_DEVICE(MX_USBSERIAL_VID, MX_UPORT1658_PID), - .driver_info = MX_UPORT_8_PORT }, + .driver_info = MX_PORTS(8) }, { USB_DEVICE(MX_USBSERIAL_VID, MX_UPORT1613_PID), - .driver_info = MX_UPORT_16_PORT }, + .driver_info = MX_PORTS(16) }, { USB_DEVICE(MX_USBSERIAL_VID, MX_UPORT1653_PID), - .driver_info = MX_UPORT_16_PORT }, + .driver_info = MX_PORTS(16) }, {} /* Terminating entry */ }; @@ -942,14 +940,8 @@ static int mxuport_calc_num_ports(struct usb_serial *serial, int num_ports; int i; - if (features & MX_UPORT_2_PORT) { - num_ports = 2; - } else if (features & MX_UPORT_4_PORT) { - num_ports = 4; - } else if (features & MX_UPORT_8_PORT) { - num_ports = 8; - } else if (features & MX_UPORT_16_PORT) { - num_ports = 16; + if (features & MX_PORTS_MASK) { + num_ports = (features & MX_PORTS_MASK) + MX_PORTS_OFFSET; } else { dev_warn(&serial->interface->dev, "unknown device, assuming two ports\n"); diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c index b59982ed8b25..363b8432cc61 100644 --- a/drivers/usb/serial/omninet.c +++ b/drivers/usb/serial/omninet.c @@ -14,10 +14,8 @@ #include <linux/errno.h> #include <linux/slab.h> #include <linux/tty.h> -#include <linux/tty_driver.h> #include <linux/tty_flip.h> #include <linux/module.h> -#include <linux/uaccess.h> #include <linux/usb.h> #include <linux/usb/serial.h> diff --git a/drivers/usb/serial/opticon.c b/drivers/usb/serial/opticon.c index e2bed477ad57..152dfc901cd8 100644 --- a/drivers/usb/serial/opticon.c +++ b/drivers/usb/serial/opticon.c @@ -10,14 +10,12 @@ #include <linux/kernel.h> #include <linux/tty.h> -#include <linux/tty_driver.h> #include <linux/slab.h> #include <linux/tty_flip.h> #include <linux/serial.h> #include <linux/module.h> #include <linux/usb.h> #include <linux/usb/serial.h> -#include <linux/uaccess.h> #define CONTROL_RTS 0x02 #define RESEND_CTS_STATE 0x03 diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index a34e79cfd5b6..7275f4e7f569 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -32,7 +32,6 @@ #include <linux/jiffies.h> #include <linux/errno.h> #include <linux/tty.h> -#include <linux/tty_flip.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/bitops.h> @@ -1326,6 +1325,22 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_CC864_SINGLE) }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_DE910_DUAL) }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_UE910_V2) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0990, 0xff, 0xff, 0x30), /* Telit FE990D50 (RNDIS) */ + .driver_info = NCTRL(6) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0990, 0xff, 0xff, 0x40) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0990, 0xff, 0xff, 0x60) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0991, 0xff, 0xff, 0x30), /* Telit FE990D50 (rmnet) */ + .driver_info = NCTRL(5) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0991, 0xff, 0xff, 0x40) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0991, 0xff, 0xff, 0x60) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0992, 0xff, 0xff, 0x30), /* Telit FE990D50 (MBIM) */ + .driver_info = NCTRL(6) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0992, 0xff, 0xff, 0x40) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0992, 0xff, 0xff, 0x60) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0993, 0xff, 0xff, 0x30), /* Telit FE990D50 (ECM) */ + .driver_info = NCTRL(6) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0993, 0xff, 0xff, 0x40) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0993, 0xff, 0xff, 0x60) }, { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1031, 0xff), /* Telit LE910C1-EUX */ .driver_info = NCTRL(0) | RSVD(3) }, { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1033, 0xff), /* Telit LE910C1-EUX (ECM) */ diff --git a/drivers/usb/serial/oti6858.c b/drivers/usb/serial/oti6858.c index 3ef5b5d8ce1a..c6364e8ff71f 100644 --- a/drivers/usb/serial/oti6858.c +++ b/drivers/usb/serial/oti6858.c @@ -38,15 +38,12 @@ #include <linux/errno.h> #include <linux/slab.h> #include <linux/tty.h> -#include <linux/tty_driver.h> #include <linux/tty_flip.h> #include <linux/serial.h> #include <linux/module.h> -#include <linux/moduleparam.h> #include <linux/spinlock.h> #include <linux/usb.h> #include <linux/usb/serial.h> -#include <linux/uaccess.h> #include <linux/kfifo.h> #include "oti6858.h" diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 50dc838e8115..0bcbdcea52af 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -15,13 +15,10 @@ #include <linux/errno.h> #include <linux/slab.h> #include <linux/tty.h> -#include <linux/tty_driver.h> #include <linux/tty_flip.h> #include <linux/serial.h> #include <linux/module.h> -#include <linux/moduleparam.h> #include <linux/spinlock.h> -#include <linux/uaccess.h> #include <linux/usb.h> #include <linux/usb/serial.h> #include <linux/unaligned.h> diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c index 1a930dc668e4..105c0a0eae86 100644 --- a/drivers/usb/serial/qcserial.c +++ b/drivers/usb/serial/qcserial.c @@ -8,7 +8,6 @@ */ #include <linux/tty.h> -#include <linux/tty_flip.h> #include <linux/module.h> #include <linux/usb.h> #include <linux/usb/serial.h> diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c index b05c655b3cc4..dc2b39810a26 100644 --- a/drivers/usb/serial/quatech2.c +++ b/drivers/usb/serial/quatech2.c @@ -13,14 +13,12 @@ #include <linux/errno.h> #include <linux/slab.h> #include <linux/tty.h> -#include <linux/tty_driver.h> #include <linux/tty_flip.h> #include <linux/module.h> #include <linux/serial.h> #include <linux/usb.h> #include <linux/usb/serial.h> #include <linux/serial_reg.h> -#include <linux/uaccess.h> /* default urb timeout for usb operations */ #define QT2_USB_TIMEOUT USB_CTRL_SET_TIMEOUT diff --git a/drivers/usb/serial/safe_serial.c b/drivers/usb/serial/safe_serial.c index d267a31dcccf..55e59e341732 100644 --- a/drivers/usb/serial/safe_serial.c +++ b/drivers/usb/serial/safe_serial.c @@ -64,11 +64,9 @@ #include <linux/errno.h> #include <linux/gfp.h> #include <linux/tty.h> -#include <linux/tty_driver.h> #include <linux/tty_flip.h> #include <linux/module.h> #include <linux/spinlock.h> -#include <linux/uaccess.h> #include <linux/usb.h> #include <linux/usb/serial.h> diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c index 2fbb48f19efd..c11d64bf08fb 100644 --- a/drivers/usb/serial/spcp8x5.c +++ b/drivers/usb/serial/spcp8x5.c @@ -14,8 +14,6 @@ #include <linux/errno.h> #include <linux/slab.h> #include <linux/tty.h> -#include <linux/tty_driver.h> -#include <linux/tty_flip.h> #include <linux/module.h> #include <linux/spinlock.h> #include <linux/usb.h> diff --git a/drivers/usb/serial/ssu100.c b/drivers/usb/serial/ssu100.c index da6316410b77..b0d51558b73c 100644 --- a/drivers/usb/serial/ssu100.c +++ b/drivers/usb/serial/ssu100.c @@ -9,14 +9,12 @@ #include <linux/errno.h> #include <linux/slab.h> #include <linux/tty.h> -#include <linux/tty_driver.h> #include <linux/tty_flip.h> #include <linux/module.h> #include <linux/serial.h> #include <linux/usb.h> #include <linux/usb/serial.h> #include <linux/serial_reg.h> -#include <linux/uaccess.h> #define QT_OPEN_CLOSE_CHANNEL 0xca #define QT_SET_GET_DEVICE 0xc2 diff --git a/drivers/usb/serial/symbolserial.c b/drivers/usb/serial/symbolserial.c index 7a5aa39172a8..8823671a9c7a 100644 --- a/drivers/usb/serial/symbolserial.c +++ b/drivers/usb/serial/symbolserial.c @@ -10,12 +10,10 @@ #include <linux/kernel.h> #include <linux/tty.h> #include <linux/slab.h> -#include <linux/tty_driver.h> #include <linux/tty_flip.h> #include <linux/module.h> #include <linux/usb.h> #include <linux/usb/serial.h> -#include <linux/uaccess.h> static const struct usb_device_id id_table[] = { { USB_DEVICE(0x05e0, 0x0600) }, diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index b3591d6d7645..b3f00e315815 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -18,7 +18,6 @@ #include <linux/firmware.h> #include <linux/slab.h> #include <linux/tty.h> -#include <linux/tty_driver.h> #include <linux/tty_flip.h> #include <linux/module.h> #include <linux/spinlock.h> @@ -26,7 +25,6 @@ #include <linux/serial.h> #include <linux/kfifo.h> #include <linux/mutex.h> -#include <linux/uaccess.h> #include <linux/usb.h> #include <linux/usb/serial.h> diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 0e072fd87c3d..17edc057a311 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -22,14 +22,11 @@ #include <linux/slab.h> #include <linux/tty.h> #include <linux/tty_driver.h> -#include <linux/tty_flip.h> #include <linux/module.h> -#include <linux/moduleparam.h> #include <linux/seq_file.h> #include <linux/spinlock.h> #include <linux/mutex.h> #include <linux/list.h> -#include <linux/uaccess.h> #include <linux/serial.h> #include <linux/usb.h> #include <linux/usb/serial.h> diff --git a/drivers/usb/serial/usb_wwan.c b/drivers/usb/serial/usb_wwan.c index e752ffa4dc62..a183cc4515ed 100644 --- a/drivers/usb/serial/usb_wwan.c +++ b/drivers/usb/serial/usb_wwan.c @@ -27,7 +27,6 @@ #include <linux/tty_flip.h> #include <linux/module.h> #include <linux/bitops.h> -#include <linux/uaccess.h> #include <linux/usb.h> #include <linux/usb/cdc.h> #include <linux/usb/serial.h> diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c index 062a38fe0c1c..5706c46c49c2 100644 --- a/drivers/usb/serial/visor.c +++ b/drivers/usb/serial/visor.c @@ -15,12 +15,8 @@ #include <linux/errno.h> #include <linux/slab.h> #include <linux/tty.h> -#include <linux/tty_driver.h> -#include <linux/tty_flip.h> #include <linux/module.h> -#include <linux/moduleparam.h> #include <linux/spinlock.h> -#include <linux/uaccess.h> #include <linux/usb.h> #include <linux/usb/serial.h> #include <linux/usb/cdc.h> diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index 6e2b52b2b5f8..a2d83f69e00e 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -16,13 +16,9 @@ #include <linux/errno.h> #include <linux/slab.h> #include <linux/tty.h> -#include <linux/tty_driver.h> -#include <linux/tty_flip.h> #include <linux/module.h> #include <linux/spinlock.h> #include <linux/mutex.h> -#include <linux/uaccess.h> -#include <asm/termbits.h> #include <linux/usb.h> #include <linux/serial_reg.h> #include <linux/serial.h> diff --git a/drivers/usb/serial/wishbone-serial.c b/drivers/usb/serial/wishbone-serial.c index 670d573f6b63..4a1fed4cb57b 100644 --- a/drivers/usb/serial/wishbone-serial.c +++ b/drivers/usb/serial/wishbone-serial.c @@ -11,7 +11,6 @@ #include <linux/module.h> #include <linux/usb.h> #include <linux/usb/serial.h> -#include <linux/uaccess.h> #define GSI_VENDOR_OPENCLOSE 0xB0 diff --git a/drivers/usb/serial/xr_serial.c b/drivers/usb/serial/xr_serial.c index 9fc4082de770..352c765d8803 100644 --- a/drivers/usb/serial/xr_serial.c +++ b/drivers/usb/serial/xr_serial.c @@ -16,6 +16,7 @@ #include <linux/module.h> #include <linux/slab.h> #include <linux/tty.h> +#include <linux/uaccess.h> #include <linux/usb.h> #include <linux/usb/cdc.h> #include <linux/usb/serial.h> diff --git a/drivers/usb/serial/xsens_mt.c b/drivers/usb/serial/xsens_mt.c index 382b3698c1d5..151a6e893ba2 100644 --- a/drivers/usb/serial/xsens_mt.c +++ b/drivers/usb/serial/xsens_mt.c @@ -10,7 +10,6 @@ #include <linux/module.h> #include <linux/usb.h> #include <linux/usb/serial.h> -#include <linux/uaccess.h> #define XSENS_VID 0x2639 diff --git a/drivers/usb/storage/ene_ub6250.c b/drivers/usb/storage/ene_ub6250.c index 8770de01a384..ed49a3bc859c 100644 --- a/drivers/usb/storage/ene_ub6250.c +++ b/drivers/usb/storage/ene_ub6250.c @@ -2305,7 +2305,8 @@ static int ene_transport(struct scsi_cmnd *srb, struct us_data *us) /*US_DEBUG(usb_stor_show_command(us, srb)); */ scsi_set_resid(srb, 0); - if (unlikely(!(info->SD_Status & SD_Ready) || (info->MS_Status & MS_Ready))) + if (unlikely(!(info->SD_Status & SD_Ready) && + !(info->MS_Status & MS_Ready))) result = ene_init(us); if (result == USB_STOR_XFER_GOOD) { result = USB_STOR_TRANSPORT_ERROR; diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index fa83fe0defe2..064c7fc8e368 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -570,7 +570,7 @@ void usb_stor_adjust_quirks(struct usb_device *udev, u64 *fflags) US_FL_INITIAL_READ10 | US_FL_WRITE_CACHE | US_FL_NO_ATA_1X | US_FL_NO_REPORT_OPCODES | US_FL_MAX_SECTORS_240 | US_FL_NO_REPORT_LUNS | - US_FL_ALWAYS_SYNC); + US_FL_ALWAYS_SYNC | US_FL_NO_SAME); p = quirks; while (*p) { diff --git a/drivers/usb/typec/anx7411.c b/drivers/usb/typec/anx7411.c index 2e8ae1d2faf9..41df115912b9 100644 --- a/drivers/usb/typec/anx7411.c +++ b/drivers/usb/typec/anx7411.c @@ -1537,7 +1537,9 @@ static int anx7411_i2c_probe(struct i2c_client *client) if (anx7411_typec_check_connection(plat)) dev_err(dev, "check status\n"); - pm_runtime_enable(dev); + ret = devm_pm_runtime_enable(dev); + if (ret) + goto free_wq; return 0; @@ -1577,8 +1579,8 @@ static void anx7411_i2c_remove(struct i2c_client *client) } static const struct i2c_device_id anx7411_id[] = { - { "anx7411" }, - {} + { .name = "anx7411" }, + { } }; MODULE_DEVICE_TABLE(i2c, anx7411_id); diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 0977581ad1b6..0595e8cb83aa 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -1619,6 +1619,7 @@ static ssize_t select_usb_power_delivery_store(struct device *dev, return -EINVAL; ret = port->ops->pd_set(port, pd); + put_device(&pd->dev); if (ret) return ret; diff --git a/drivers/usb/typec/mux.c b/drivers/usb/typec/mux.c index db5e4a4c0a99..9b908c46bd7d 100644 --- a/drivers/usb/typec/mux.c +++ b/drivers/usb/typec/mux.c @@ -275,9 +275,7 @@ static int mux_fwnode_match(struct device *dev, const void *fwnode) static void *typec_mux_match(const struct fwnode_handle *fwnode, const char *id, void *data) { - struct typec_mux_dev **mux_devs = data; struct device *dev; - int i; /* * Device graph (OF graph) does not give any means to identify the @@ -293,14 +291,6 @@ static void *typec_mux_match(const struct fwnode_handle *fwnode, dev = class_find_device(&typec_mux_class, NULL, fwnode, mux_fwnode_match); - /* Skip duplicates */ - for (i = 0; i < TYPEC_MUX_MAX_DEVS; i++) - if (to_typec_mux_dev(dev) == mux_devs[i]) { - put_device(dev); - return NULL; - } - - return dev ? to_typec_mux_dev(dev) : ERR_PTR(-EPROBE_DEFER); } @@ -326,8 +316,7 @@ struct typec_mux *fwnode_typec_mux_get(struct fwnode_handle *fwnode) return ERR_PTR(-ENOMEM); count = fwnode_connection_find_matches(fwnode, "mode-switch", - (void **)mux_devs, - typec_mux_match, + NULL, typec_mux_match, (void **)mux_devs, ARRAY_SIZE(mux_devs)); if (count <= 0) { diff --git a/drivers/usb/typec/mux/fsa4480.c b/drivers/usb/typec/mux/fsa4480.c index c54e42c7e6a1..bea0c1deec94 100644 --- a/drivers/usb/typec/mux/fsa4480.c +++ b/drivers/usb/typec/mux/fsa4480.c @@ -336,7 +336,7 @@ static void fsa4480_remove(struct i2c_client *client) } static const struct i2c_device_id fsa4480_table[] = { - { "fsa4480" }, + { .name = "fsa4480" }, { } }; MODULE_DEVICE_TABLE(i2c, fsa4480_table); diff --git a/drivers/usb/typec/mux/intel_pmc_mux.c b/drivers/usb/typec/mux/intel_pmc_mux.c index 1698428654ab..219a32da1348 100644 --- a/drivers/usb/typec/mux/intel_pmc_mux.c +++ b/drivers/usb/typec/mux/intel_pmc_mux.c @@ -151,13 +151,14 @@ struct pmc_usb { u8 num_ports; struct device *dev; struct intel_scu_ipc_dev *ipc; - struct pmc_usb_port *port; struct acpi_device *iom_adev; void __iomem *iom_base; u32 iom_port_status_offset; u8 iom_port_status_size; struct dentry *dentry; + + struct pmc_usb_port port[] __counted_by(num_ports); }; static struct dentry *pmc_mux_debugfs_root; @@ -731,27 +732,25 @@ static int pmc_usb_probe(struct platform_device *pdev) { struct fwnode_handle *fwnode = NULL; struct pmc_usb *pmc; + u8 num_ports = 0; int i = 0; int ret; - pmc = devm_kzalloc(&pdev->dev, sizeof(*pmc), GFP_KERNEL); - if (!pmc) - return -ENOMEM; - device_for_each_child_node(&pdev->dev, fwnode) - pmc->num_ports++; + num_ports++; /* The IOM microcontroller has a limitation of max 4 ports. */ - if (pmc->num_ports > 4) { + if (num_ports > 4) { dev_err(&pdev->dev, "driver limited to 4 ports\n"); return -ERANGE; } - pmc->port = devm_kcalloc(&pdev->dev, pmc->num_ports, - sizeof(struct pmc_usb_port), GFP_KERNEL); - if (!pmc->port) + pmc = devm_kzalloc(&pdev->dev, struct_size(pmc, port, num_ports), GFP_KERNEL); + if (!pmc) return -ENOMEM; + pmc->num_ports = num_ports; + pmc->ipc = devm_intel_scu_ipc_dev_get(&pdev->dev); if (!pmc->ipc) return -EPROBE_DEFER; diff --git a/drivers/usb/typec/mux/it5205.c b/drivers/usb/typec/mux/it5205.c index 4357cc67a867..5e1a120b2e3b 100644 --- a/drivers/usb/typec/mux/it5205.c +++ b/drivers/usb/typec/mux/it5205.c @@ -266,7 +266,7 @@ static void it5205_remove(struct i2c_client *client) } static const struct i2c_device_id it5205_table[] = { - { "it5205" }, + { .name = "it5205" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, it5205_table); diff --git a/drivers/usb/typec/mux/nb7vpq904m.c b/drivers/usb/typec/mux/nb7vpq904m.c index b57b6c9c40fe..d1fa26ff442c 100644 --- a/drivers/usb/typec/mux/nb7vpq904m.c +++ b/drivers/usb/typec/mux/nb7vpq904m.c @@ -499,7 +499,7 @@ static void nb7vpq904m_remove(struct i2c_client *client) } static const struct i2c_device_id nb7vpq904m_table[] = { - { "nb7vpq904m" }, + { .name = "nb7vpq904m" }, { } }; MODULE_DEVICE_TABLE(i2c, nb7vpq904m_table); diff --git a/drivers/usb/typec/mux/pi3usb30532.c b/drivers/usb/typec/mux/pi3usb30532.c index 8eeec135dcdb..985683fe49e9 100644 --- a/drivers/usb/typec/mux/pi3usb30532.c +++ b/drivers/usb/typec/mux/pi3usb30532.c @@ -169,7 +169,7 @@ static void pi3usb30532_remove(struct i2c_client *client) } static const struct i2c_device_id pi3usb30532_table[] = { - { "pi3usb30532" }, + { .name = "pi3usb30532" }, { } }; MODULE_DEVICE_TABLE(i2c, pi3usb30532_table); diff --git a/drivers/usb/typec/mux/ps883x.c b/drivers/usb/typec/mux/ps883x.c index 1256252eceed..64e0a61b776a 100644 --- a/drivers/usb/typec/mux/ps883x.c +++ b/drivers/usb/typec/mux/ps883x.c @@ -61,19 +61,110 @@ struct ps883x_retimer { struct mutex lock; /* protect non-concurrent retimer & switch */ enum typec_orientation orientation; - u8 cfg0; - u8 cfg1; - u8 cfg2; + bool in_reset; }; +static int ps883x_enable_vregs(struct ps883x_retimer *retimer) +{ + struct device *dev = &retimer->client->dev; + int ret; + + ret = regulator_enable(retimer->vdd33_supply); + if (ret) { + dev_err(dev, "cannot enable VDD 3.3V regulator: %d\n", ret); + return ret; + } + + ret = regulator_enable(retimer->vdd33_cap_supply); + if (ret) { + dev_err(dev, "cannot enable VDD 3.3V CAP regulator: %d\n", ret); + goto err_vdd33_disable; + } + + usleep_range(4000, 10000); + + ret = regulator_enable(retimer->vdd_supply); + if (ret) { + dev_err(dev, "cannot enable VDD regulator: %d\n", ret); + goto err_vdd33_cap_disable; + } + + ret = regulator_enable(retimer->vddar_supply); + if (ret) { + dev_err(dev, "cannot enable VDD AR regulator: %d\n", ret); + goto err_vdd_disable; + } + + ret = regulator_enable(retimer->vddat_supply); + if (ret) { + dev_err(dev, "cannot enable VDD AT regulator: %d\n", ret); + goto err_vddar_disable; + } + + ret = regulator_enable(retimer->vddio_supply); + if (ret) { + dev_err(dev, "cannot enable VDD IO regulator: %d\n", ret); + goto err_vddat_disable; + } + + return 0; + +err_vddat_disable: + regulator_disable(retimer->vddat_supply); +err_vddar_disable: + regulator_disable(retimer->vddar_supply); +err_vdd_disable: + regulator_disable(retimer->vdd_supply); +err_vdd33_cap_disable: + regulator_disable(retimer->vdd33_cap_supply); +err_vdd33_disable: + regulator_disable(retimer->vdd33_supply); + + return ret; +} + +static void ps883x_disable_vregs(struct ps883x_retimer *retimer) +{ + regulator_disable(retimer->vddio_supply); + regulator_disable(retimer->vddat_supply); + regulator_disable(retimer->vddar_supply); + regulator_disable(retimer->vdd_supply); + regulator_disable(retimer->vdd33_cap_supply); + regulator_disable(retimer->vdd33_supply); +} + +static void ps883x_reset(struct ps883x_retimer *retimer) +{ + if (retimer->in_reset) + return; + + gpiod_set_value(retimer->reset_gpio, 1); + ps883x_disable_vregs(retimer); + retimer->in_reset = true; +} + static int ps883x_configure(struct ps883x_retimer *retimer, int cfg0, - int cfg1, int cfg2) + int cfg1, int cfg2, bool reset) { struct device *dev = &retimer->client->dev; int ret; - if (retimer->cfg0 == cfg0 && retimer->cfg1 == cfg1 && retimer->cfg2 == cfg2) + if (reset) { + ps883x_reset(retimer); + return 0; + } else if (retimer->in_reset) { + ret = ps883x_enable_vregs(retimer); + if (ret) + return ret; + + gpiod_set_value(retimer->reset_gpio, 0); + + /* firmware initialization delay */ + msleep(60); + + retimer->in_reset = false; + } ret = regmap_write(retimer->regmap, REG_USB_PORT_CONN_STATUS_0, cfg0); if (ret) { @@ -93,10 +184,6 @@ static int ps883x_configure(struct ps883x_retimer *retimer, int cfg0, return ret; } - retimer->cfg0 = cfg0; - retimer->cfg1 = cfg1; - retimer->cfg2 = cfg2; - return 0; } @@ -107,6 +194,7 @@ static int ps883x_set(struct ps883x_retimer *retimer, struct typec_retimer_state int cfg0 = CONN_STATUS_0_CONNECTION_PRESENT; int cfg1 = 0x00; int cfg2 = 0x00; + bool reset = false; if (retimer->orientation == TYPEC_ORIENTATION_REVERSE) cfg0 |= CONN_STATUS_0_ORIENTATION_REVERSED; @@ -118,12 +206,12 @@ static int ps883x_set(struct ps883x_retimer *retimer, struct typec_retimer_state CONN_STATUS_1_DP_HPD_LEVEL; switch (state->mode) { + case TYPEC_DP_STATE_D: + cfg0 |= CONN_STATUS_0_USB_3_1_CONNECTED; + fallthrough; case TYPEC_DP_STATE_C: cfg1 |= CONN_STATUS_1_DP_SINK_REQUESTED | CONN_STATUS_1_DP_PIN_ASSIGNMENT_C_D; - fallthrough; - case TYPEC_DP_STATE_D: - cfg1 |= CONN_STATUS_0_USB_3_1_CONNECTED; break; default: /* MODE_E */ break; @@ -148,9 +236,13 @@ static int ps883x_set(struct ps883x_retimer *retimer, struct typec_retimer_state } } else { switch (state->mode) { + /* SAFE can be transient or point to an actual disconnect */ case TYPEC_STATE_SAFE: + reset = retimer->orientation == TYPEC_ORIENTATION_NONE; + break; /* USB2 pins don't even go through this chip */ case TYPEC_MODE_USB2: + reset = true; break; case TYPEC_STATE_USB: case TYPEC_MODE_USB3: @@ -171,7 +263,7 @@ static int ps883x_set(struct ps883x_retimer *retimer, struct typec_retimer_state } } - return ps883x_configure(retimer, cfg0, cfg1, cfg2); + return ps883x_configure(retimer, cfg0, cfg1, cfg2, reset); } static int ps883x_sw_set(struct typec_switch_dev *sw, @@ -184,11 +276,19 @@ static int ps883x_sw_set(struct typec_switch_dev *sw, if (ret) return ret; - mutex_lock(&retimer->lock); + guard(mutex)(&retimer->lock); if (retimer->orientation != orientation) { retimer->orientation = orientation; + /* + * Orientation notifications usually come prior to mode switch + * events. If the retimer is already in reset, we still want to + * cache the new orientation value for the subsequent ps883x_set(). + */ + if (retimer->in_reset) + return 0; + ret = regmap_assign_bits(retimer->regmap, REG_USB_PORT_CONN_STATUS_0, CONN_STATUS_0_ORIENTATION_REVERSED, orientation == TYPEC_ORIENTATION_REVERSE); @@ -196,8 +296,6 @@ static int ps883x_sw_set(struct typec_switch_dev *sw, dev_err(&retimer->client->dev, "failed to set orientation: %d\n", ret); } - mutex_unlock(&retimer->lock); - return ret; } @@ -222,75 +320,6 @@ static int ps883x_retimer_set(struct typec_retimer *rtmr, return typec_mux_set(retimer->typec_mux, &mux_state); } -static int ps883x_enable_vregs(struct ps883x_retimer *retimer) -{ - struct device *dev = &retimer->client->dev; - int ret; - - ret = regulator_enable(retimer->vdd33_supply); - if (ret) { - dev_err(dev, "cannot enable VDD 3.3V regulator: %d\n", ret); - return ret; - } - - ret = regulator_enable(retimer->vdd33_cap_supply); - if (ret) { - dev_err(dev, "cannot enable VDD 3.3V CAP regulator: %d\n", ret); - goto err_vdd33_disable; - } - - usleep_range(4000, 10000); - - ret = regulator_enable(retimer->vdd_supply); - if (ret) { - dev_err(dev, "cannot enable VDD regulator: %d\n", ret); - goto err_vdd33_cap_disable; - } - - ret = regulator_enable(retimer->vddar_supply); - if (ret) { - dev_err(dev, "cannot enable VDD AR regulator: %d\n", ret); - goto err_vdd_disable; - } - - ret = regulator_enable(retimer->vddat_supply); - if (ret) { - dev_err(dev, "cannot enable VDD AT regulator: %d\n", ret); - goto err_vddar_disable; - } - - ret = regulator_enable(retimer->vddio_supply); - if (ret) { - dev_err(dev, "cannot enable VDD IO regulator: %d\n", ret); - goto err_vddat_disable; - } - - return 0; - -err_vddat_disable: - regulator_disable(retimer->vddat_supply); -err_vddar_disable: - regulator_disable(retimer->vddar_supply); -err_vdd_disable: - regulator_disable(retimer->vdd_supply); -err_vdd33_cap_disable: - regulator_disable(retimer->vdd33_cap_supply); -err_vdd33_disable: - regulator_disable(retimer->vdd33_supply); - - return ret; -} - -static void ps883x_disable_vregs(struct ps883x_retimer *retimer) -{ - regulator_disable(retimer->vddio_supply); - regulator_disable(retimer->vddat_supply); - regulator_disable(retimer->vddar_supply); - regulator_disable(retimer->vdd_supply); - regulator_disable(retimer->vdd33_cap_supply); - regulator_disable(retimer->vdd33_supply); -} - static int ps883x_get_vregs(struct ps883x_retimer *retimer) { struct device *dev = &retimer->client->dev; @@ -422,6 +451,9 @@ static int ps883x_retimer_probe(struct i2c_client *client) } } + /* Keep the retimer in reset until a Type-C notification comes */ + ps883x_reset(retimer); + sw_desc.drvdata = retimer; sw_desc.fwnode = dev_fwnode(dev); sw_desc.set = ps883x_sw_set; diff --git a/drivers/usb/typec/mux/ptn36502.c b/drivers/usb/typec/mux/ptn36502.c index 129d9d24b932..afd16775dbaf 100644 --- a/drivers/usb/typec/mux/ptn36502.c +++ b/drivers/usb/typec/mux/ptn36502.c @@ -404,7 +404,7 @@ static void ptn36502_remove(struct i2c_client *client) } static const struct i2c_device_id ptn36502_table[] = { - { "ptn36502" }, + { .name = "ptn36502" }, { } }; MODULE_DEVICE_TABLE(i2c, ptn36502_table); diff --git a/drivers/usb/typec/mux/tusb1046.c b/drivers/usb/typec/mux/tusb1046.c index 3c1a4551c2fb..d6e1289a4945 100644 --- a/drivers/usb/typec/mux/tusb1046.c +++ b/drivers/usb/typec/mux/tusb1046.c @@ -11,7 +11,6 @@ #include <linux/usb/typec_dp.h> #include <linux/usb/typec_altmode.h> #include <linux/module.h> -#include <linux/mod_devicetable.h> #include <linux/err.h> #include <linux/of_device.h> #include <linux/device.h> diff --git a/drivers/usb/typec/mux/wcd939x-usbss.c b/drivers/usb/typec/mux/wcd939x-usbss.c index d46c353dfaf2..73db3aa3cec4 100644 --- a/drivers/usb/typec/mux/wcd939x-usbss.c +++ b/drivers/usb/typec/mux/wcd939x-usbss.c @@ -753,7 +753,7 @@ static void wcd939x_usbss_remove(struct i2c_client *client) } static const struct i2c_device_id wcd939x_usbss_table[] = { - { "wcd9390-usbss" }, + { .name = "wcd9390-usbss" }, { } }; MODULE_DEVICE_TABLE(i2c, wcd939x_usbss_table); diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c index 9ab1277b7ed1..3319f6a2b0c9 100644 --- a/drivers/usb/typec/tcpm/fusb302.c +++ b/drivers/usb/typec/tcpm/fusb302.c @@ -1841,8 +1841,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" }, - {} + { .name = "typec_fusb302" }, + { } }; MODULE_DEVICE_TABLE(i2c, fusb302_i2c_device_id); diff --git a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c index 3766790c1548..d770e58bc16c 100644 --- a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c +++ b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c @@ -4,19 +4,13 @@ */ #include <linux/err.h> -#include <linux/interrupt.h> #include <linux/kernel.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/of_graph.h> #include <linux/platform_device.h> #include <linux/regmap.h> -#include <linux/regulator/consumer.h> #include <linux/slab.h> -#include <linux/usb/role.h> #include <linux/usb/tcpm.h> -#include <linux/usb/typec_mux.h> #include <drm/bridge/aux-bridge.h> diff --git a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c index c8b1463e6e8b..e6b28648f440 100644 --- a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c +++ b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c @@ -6,7 +6,6 @@ #include <linux/err.h> #include <linux/interrupt.h> #include <linux/kernel.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/regmap.h> diff --git a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy_stub.c b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy_stub.c index 8fac171778da..01b310549c8c 100644 --- a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy_stub.c +++ b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy_stub.c @@ -6,7 +6,6 @@ #include <linux/err.h> #include <linux/interrupt.h> #include <linux/kernel.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/regmap.h> diff --git a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c index 8051eaa46991..bf985efe1cd6 100644 --- a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c +++ b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c @@ -7,8 +7,8 @@ #include <linux/err.h> #include <linux/interrupt.h> #include <linux/kernel.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> +#include <linux/of.h> #include <linux/platform_device.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> @@ -704,6 +704,7 @@ int qcom_pmic_typec_port_probe(struct platform_device *pdev, struct device *dev = &pdev->dev; struct pmic_typec_port_irq_data *irq_data; struct pmic_typec_port *pmic_typec_port; + struct fwnode_handle *connector; int i, ret, irq; pmic_typec_port = devm_kzalloc(dev, sizeof(*pmic_typec_port), GFP_KERNEL); @@ -720,7 +721,15 @@ int qcom_pmic_typec_port_probe(struct platform_device *pdev, mutex_init(&pmic_typec_port->vbus_lock); - pmic_typec_port->vdd_vbus = devm_regulator_get(dev, "vdd-vbus"); + connector = device_get_named_child_node(dev, "connector"); + if (!connector) + return -EINVAL; + + pmic_typec_port->vdd_vbus = devm_of_regulator_get_optional(dev, + to_of_node(connector), + "vbus"); + if (pmic_typec_port->vdd_vbus == ERR_PTR(-ENODEV)) + pmic_typec_port->vdd_vbus = devm_regulator_get(dev, "vdd-vbus"); if (IS_ERR(pmic_typec_port->vdd_vbus)) return PTR_ERR(pmic_typec_port->vdd_vbus); diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c index 0148b8f50412..7ac7000b2d13 100644 --- a/drivers/usb/typec/tcpm/tcpci.c +++ b/drivers/usb/typec/tcpm/tcpci.c @@ -141,13 +141,10 @@ static int tcpci_set_cc(struct tcpc_dev *tcpc, enum typec_cc_status cc) } if (vconn_pres) { - if (polarity == TYPEC_POLARITY_CC2) { - reg &= ~TCPC_ROLE_CTRL_CC1; - reg |= FIELD_PREP(TCPC_ROLE_CTRL_CC1, TCPC_ROLE_CTRL_CC_OPEN); - } else { - reg &= ~TCPC_ROLE_CTRL_CC2; - reg |= FIELD_PREP(TCPC_ROLE_CTRL_CC2, TCPC_ROLE_CTRL_CC_OPEN); - } + if (polarity == TYPEC_POLARITY_CC2) + FIELD_MODIFY(TCPC_ROLE_CTRL_CC1, ®, TCPC_ROLE_CTRL_CC_OPEN); + else + FIELD_MODIFY(TCPC_ROLE_CTRL_CC2, ®, TCPC_ROLE_CTRL_CC_OPEN); } ret = regmap_write(tcpci->regmap, TCPC_ROLE_CTRL, reg); @@ -1020,7 +1017,7 @@ static int tcpci_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(tcpci_pm_ops, tcpci_suspend, tcpci_resume); static const struct i2c_device_id tcpci_id[] = { - { "tcpci" }, + { .name = "tcpci" }, { } }; MODULE_DEVICE_TABLE(i2c, tcpci_id); diff --git a/drivers/usb/typec/tcpm/tcpci_maxim_core.c b/drivers/usb/typec/tcpm/tcpci_maxim_core.c index 7324139d51c8..998693a61839 100644 --- a/drivers/usb/typec/tcpm/tcpci_maxim_core.c +++ b/drivers/usb/typec/tcpm/tcpci_maxim_core.c @@ -579,7 +579,7 @@ static int max_tcpci_suspend(struct device *dev) static SIMPLE_DEV_PM_OPS(max_tcpci_pm_ops, max_tcpci_suspend, max_tcpci_resume); static const struct i2c_device_id max_tcpci_id[] = { - { "maxtcpc" }, + { .name = "maxtcpc" }, { } }; MODULE_DEVICE_TABLE(i2c, max_tcpci_id); diff --git a/drivers/usb/typec/tcpm/tcpci_mt6370.c b/drivers/usb/typec/tcpm/tcpci_mt6370.c index ed822f438a09..7d6c75c70985 100644 --- a/drivers/usb/typec/tcpm/tcpci_mt6370.c +++ b/drivers/usb/typec/tcpm/tcpci_mt6370.c @@ -8,7 +8,6 @@ #include <linux/bits.h> #include <linux/interrupt.h> #include <linux/kernel.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/pm_wakeirq.h> diff --git a/drivers/usb/typec/tcpm/tcpci_rt1711h.c b/drivers/usb/typec/tcpm/tcpci_rt1711h.c index 4b3e4e22a82e..af8356df6b98 100644 --- a/drivers/usb/typec/tcpm/tcpci_rt1711h.c +++ b/drivers/usb/typec/tcpm/tcpci_rt1711h.c @@ -8,7 +8,6 @@ #include <linux/bitfield.h> #include <linux/bits.h> #include <linux/kernel.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/i2c.h> #include <linux/interrupt.h> @@ -295,6 +294,8 @@ static int rt1711h_sw_reset(struct rt1711h_chip *chip) return 0; } +static void rt1711h_unregister_tcpci_port(void *tcpci); + static int rt1711h_probe(struct i2c_client *client) { int ret; @@ -340,6 +341,10 @@ static int rt1711h_probe(struct i2c_client *client) if (IS_ERR_OR_NULL(chip->tcpci)) return PTR_ERR(chip->tcpci); + ret = devm_add_action_or_reset(chip->dev, rt1711h_unregister_tcpci_port, chip->tcpci); + if (ret) + return ret; + ret = devm_request_threaded_irq(chip->dev, client->irq, NULL, rt1711h_irq, IRQF_ONESHOT | IRQF_TRIGGER_LOW, @@ -357,11 +362,9 @@ static int rt1711h_probe(struct i2c_client *client) return 0; } -static void rt1711h_remove(struct i2c_client *client) +static void rt1711h_unregister_tcpci_port(void *tcpci) { - struct rt1711h_chip *chip = i2c_get_clientdata(client); - - tcpci_unregister_port(chip->tcpci); + tcpci_unregister_port(tcpci); } static const struct rt1711h_chip_info rt1711h = { @@ -373,10 +376,10 @@ static const struct rt1711h_chip_info rt1715 = { }; static const struct i2c_device_id rt1711h_id[] = { - { "et7304", (kernel_ulong_t)&rt1715 }, - { "rt1711h", (kernel_ulong_t)&rt1711h }, - { "rt1715", (kernel_ulong_t)&rt1715 }, - {} + { .name = "et7304", .driver_data = (kernel_ulong_t)&rt1715 }, + { .name = "rt1711h", .driver_data = (kernel_ulong_t)&rt1711h }, + { .name = "rt1715", .driver_data = (kernel_ulong_t)&rt1715 }, + { } }; MODULE_DEVICE_TABLE(i2c, rt1711h_id); @@ -394,7 +397,6 @@ static struct i2c_driver rt1711h_i2c_driver = { .of_match_table = rt1711h_of_match, }, .probe = rt1711h_probe, - .remove = rt1711h_remove, .id_table = rt1711h_id, }; module_i2c_driver(rt1711h_i2c_driver); diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 7ef746a90a17..89eec20a2064 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -2000,6 +2000,11 @@ static void svdm_consume_modes(struct tcpm_port *port, const u32 *p, int cnt, return; } + if (pmdata->svid_index < 0 || pmdata->svid_index >= pmdata->nsvids) { + tcpm_log(port, "Invalid SVID index %d", pmdata->svid_index); + return; + } + for (i = 1; i < cnt; i++) { if (pmdata->altmodes >= ALTMODE_DISCOVERY_MAX) { /* Already logged in svdm_consume_svids() */ @@ -3088,7 +3093,7 @@ static int tcpm_altmode_enter(struct typec_altmode *altmode, u32 *vdo) if (svdm_version < 0) return svdm_version; - header = VDO(altmode->svid, vdo ? 2 : 1, svdm_version, CMD_ENTER_MODE); + header = VDO(altmode->svid, 1, svdm_version, CMD_ENTER_MODE); header |= VDO_OPOS(altmode->mode); return tcpm_queue_vdm_unlocked(port, header, vdo, vdo ? 1 : 0, TCPC_TX_SOP); @@ -3136,7 +3141,7 @@ static int tcpm_cable_altmode_enter(struct typec_altmode *altmode, enum typec_pl if (svdm_version < 0) return svdm_version; - header = VDO(altmode->svid, vdo ? 2 : 1, svdm_version, CMD_ENTER_MODE); + header = VDO(altmode->svid, 1, svdm_version, CMD_ENTER_MODE); header |= VDO_OPOS(altmode->mode); return tcpm_queue_vdm_unlocked(port, header, vdo, vdo ? 1 : 0, TCPC_TX_SOP_PRIME); diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c index d0b769333bd9..d5ee0af9058b 100644 --- a/drivers/usb/typec/tipd/core.c +++ b/drivers/usb/typec/tipd/core.c @@ -2028,7 +2028,7 @@ static const struct of_device_id tps6598x_of_match[] = { MODULE_DEVICE_TABLE(of, tps6598x_of_match); static const struct i2c_device_id tps6598x_id[] = { - { "tps6598x", (kernel_ulong_t)&tps6598x_data }, + { .name = "tps6598x", .driver_data = (kernel_ulong_t)&tps6598x_data }, { } }; MODULE_DEVICE_TABLE(i2c, tps6598x_id); diff --git a/drivers/usb/typec/ucsi/cros_ec_ucsi.c b/drivers/usb/typec/ucsi/cros_ec_ucsi.c index 251aa7251ce6..c38eb678d5fe 100644 --- a/drivers/usb/typec/ucsi/cros_ec_ucsi.c +++ b/drivers/usb/typec/ucsi/cros_ec_ucsi.c @@ -9,7 +9,6 @@ #include <linux/container_of.h> #include <linux/dev_printk.h> #include <linux/jiffies.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/of.h> #include <linux/platform_data/cros_ec_commands.h> @@ -114,12 +113,14 @@ static int cros_ucsi_async_control(struct ucsi *ucsi, u64 cmd) } static int cros_ucsi_sync_control(struct ucsi *ucsi, u64 cmd, u32 *cci, - void *data, size_t size) + void *data, size_t size, void *msg_out, + size_t msg_out_size) { struct cros_ucsi_data *udata = ucsi_get_drvdata(ucsi); int ret; - ret = ucsi_sync_control_common(ucsi, cmd, cci, data, size); + ret = ucsi_sync_control_common(ucsi, cmd, cci, data, size, msg_out, + msg_out_size); switch (ret) { case -EBUSY: /* EC may return -EBUSY if CCI.busy is set. diff --git a/drivers/usb/typec/ucsi/debugfs.c b/drivers/usb/typec/ucsi/debugfs.c index a4b9a6b51649..ff33a5e7c6b0 100644 --- a/drivers/usb/typec/ucsi/debugfs.c +++ b/drivers/usb/typec/ucsi/debugfs.c @@ -40,6 +40,11 @@ static int ucsi_cmd(void *data, u64 val) case UCSI_READ_POWER_LEVEL: ret = ucsi_send_command(ucsi, val, NULL, 0); break; + case UCSI_SET_PDOS: + ret = ucsi_write_message_out_command(ucsi, val, NULL, 0, + ucsi->debugfs->message_out, + UCSI_COMMAND_DATA_LEN(val)); + break; case UCSI_GET_CAPABILITY: case UCSI_GET_CONNECTOR_CAPABILITY: case UCSI_GET_ALTERNATE_MODES: @@ -110,6 +115,30 @@ static int ucsi_vbus_volt_show(struct seq_file *m, void *v) } DEFINE_SHOW_ATTRIBUTE(ucsi_vbus_volt); +static ssize_t ucsi_message_out_write(struct file *file, + const char __user *data, size_t count, loff_t *ppos) +{ + struct ucsi *ucsi = file->private_data; + int ret; + + char *buf __free(kfree) = memdup_user_nul(data, count); + if (IS_ERR(buf)) + return PTR_ERR(buf); + + ret = hex2bin(ucsi->debugfs->message_out, buf, + min(count / 2, sizeof(ucsi->debugfs->message_out))); + if (ret) + return ret; + + return count; +} + +static const struct file_operations ucsi_message_out_fops = { + .open = simple_open, + .write = ucsi_message_out_write, + .llseek = generic_file_llseek, +}; + void ucsi_debugfs_register(struct ucsi *ucsi) { ucsi->debugfs = kzalloc_obj(*ucsi->debugfs); @@ -122,6 +151,8 @@ void ucsi_debugfs_register(struct ucsi *ucsi) debugfs_create_file("peak_current", 0400, ucsi->debugfs->dentry, ucsi, &ucsi_peak_curr_fops); debugfs_create_file("avg_current", 0400, ucsi->debugfs->dentry, ucsi, &ucsi_avg_curr_fops); debugfs_create_file("vbus_voltage", 0400, ucsi->debugfs->dentry, ucsi, &ucsi_vbus_volt_fops); + debugfs_create_file("message_out", 0200, ucsi->debugfs->dentry, ucsi, + &ucsi_message_out_fops); } void ucsi_debugfs_unregister(struct ucsi *ucsi) diff --git a/drivers/usb/typec/ucsi/displayport.c b/drivers/usb/typec/ucsi/displayport.c index 67a0991a7b76..7067f2561b84 100644 --- a/drivers/usb/typec/ucsi/displayport.c +++ b/drivers/usb/typec/ucsi/displayport.c @@ -166,12 +166,12 @@ static int ucsi_displayport_status_update(struct ucsi_dp *dp) * that Multi-function is preferred. */ if (DP_CAP_CAPABILITY(cap) & DP_CAP_UFP_D) { - dp->data.status |= DP_STATUS_CON_UFP_D; + dp->data.status |= DP_STATUS_CON_DFP_D; if (DP_CAP_UFP_D_PIN_ASSIGN(cap) & BIT(DP_PIN_ASSIGN_D)) dp->data.status |= DP_STATUS_PREFER_MULTI_FUNC; } else { - dp->data.status |= DP_STATUS_CON_DFP_D; + dp->data.status |= DP_STATUS_CON_UFP_D; if (DP_CAP_DFP_D_PIN_ASSIGN(cap) & BIT(DP_PIN_ASSIGN_D)) dp->data.status |= DP_STATUS_PREFER_MULTI_FUNC; @@ -185,13 +185,12 @@ static int ucsi_displayport_status_update(struct ucsi_dp *dp) static int ucsi_displayport_configure(struct ucsi_dp *dp) { - u32 pins = DP_CONF_GET_PIN_ASSIGN(dp->data.conf); u64 command; if (!dp->override) return 0; - command = UCSI_CMD_SET_NEW_CAM(dp->con->num, 1, dp->offset, pins); + command = UCSI_CMD_SET_NEW_CAM(dp->con->num, 1, dp->offset, dp->data.conf); return ucsi_send_command(dp->con->ucsi, command, NULL, 0); } diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 61cb24ed820f..6a6723e8fb12 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -63,7 +63,8 @@ void ucsi_notify_common(struct ucsi *ucsi, u32 cci) EXPORT_SYMBOL_GPL(ucsi_notify_common); int ucsi_sync_control_common(struct ucsi *ucsi, u64 command, u32 *cci, - void *data, size_t size) + void *data, size_t size, void *msg_out, + size_t msg_out_size) { bool ack = UCSI_COMMAND(command) == UCSI_ACK_CC_CI; int ret; @@ -75,6 +76,17 @@ int ucsi_sync_control_common(struct ucsi *ucsi, u64 command, u32 *cci, reinit_completion(&ucsi->complete); + if (msg_out && msg_out_size) { + if (!ucsi->ops->write_message_out) { + ret = -EOPNOTSUPP; + goto out_clear_bit; + } + + ret = ucsi->ops->write_message_out(ucsi, msg_out, msg_out_size); + if (ret) + goto out_clear_bit; + } + ret = ucsi->ops->async_control(ucsi, command); if (ret) goto out_clear_bit; @@ -110,11 +122,12 @@ static int ucsi_acknowledge(struct ucsi *ucsi, bool conn_ack) ctrl |= UCSI_ACK_CONNECTOR_CHANGE; } - return ucsi->ops->sync_control(ucsi, ctrl, NULL, NULL, 0); + return ucsi->ops->sync_control(ucsi, ctrl, NULL, NULL, 0, NULL, 0); } static int ucsi_run_command(struct ucsi *ucsi, u64 command, u32 *cci, - void *data, size_t size, bool conn_ack) + void *data, size_t size, void *msg_out, + size_t msg_out_size, bool conn_ack) { int ret, err; @@ -123,10 +136,12 @@ static int ucsi_run_command(struct ucsi *ucsi, u64 command, u32 *cci, if (size > UCSI_MAX_DATA_LENGTH(ucsi)) return -EINVAL; - ret = ucsi->ops->sync_control(ucsi, command, cci, data, size); + ret = ucsi->ops->sync_control(ucsi, command, cci, data, size, + msg_out, msg_out_size); if (*cci & UCSI_CCI_BUSY) - return ucsi_run_command(ucsi, UCSI_CANCEL, cci, NULL, 0, false) ?: -EBUSY; + return ucsi_run_command(ucsi, UCSI_CANCEL, cci, + NULL, 0, NULL, 0, false) ?: -EBUSY; if (ret) return ret; @@ -158,7 +173,8 @@ static int ucsi_read_error(struct ucsi *ucsi, u8 connector_num) int ret; command = UCSI_GET_ERROR_STATUS | UCSI_CONNECTOR_NUMBER(connector_num); - ret = ucsi_run_command(ucsi, command, &cci, &error, sizeof(error), false); + ret = ucsi_run_command(ucsi, command, &cci, &error, + sizeof(error), NULL, 0, false); if (ret < 0) return ret; @@ -208,7 +224,8 @@ static int ucsi_read_error(struct ucsi *ucsi, u8 connector_num) } static int ucsi_send_command_common(struct ucsi *ucsi, u64 cmd, - void *data, size_t size, bool conn_ack) + void *data, size_t size, void *msg_out, + size_t msg_out_size, bool conn_ack) { u8 connector_num; u32 cci; @@ -236,7 +253,8 @@ static int ucsi_send_command_common(struct ucsi *ucsi, u64 cmd, mutex_lock(&ucsi->ppm_lock); - ret = ucsi_run_command(ucsi, cmd, &cci, data, size, conn_ack); + ret = ucsi_run_command(ucsi, cmd, &cci, data, size, + msg_out, msg_out_size, conn_ack); if (cci & UCSI_CCI_ERROR) ret = ucsi_read_error(ucsi, connector_num); @@ -250,10 +268,23 @@ static int ucsi_send_command_common(struct ucsi *ucsi, u64 cmd, int ucsi_send_command(struct ucsi *ucsi, u64 command, void *data, size_t size) { - return ucsi_send_command_common(ucsi, command, data, size, false); + return ucsi_send_command_common(ucsi, command, data, + size, NULL, 0, false); } EXPORT_SYMBOL_GPL(ucsi_send_command); +int ucsi_write_message_out_command(struct ucsi *ucsi, u64 command, + void *data, size_t size, void *msg_out, + size_t msg_out_size) +{ + if (msg_out_size > UCSI_MAX_MSG_OUT_DATA_LEN(ucsi)) + return -EINVAL; + + return ucsi_send_command_common(ucsi, command, data, + size, msg_out, msg_out_size, false); +} +EXPORT_SYMBOL_GPL(ucsi_write_message_out_command); + /* -------------------------------------------------------------------------- */ struct ucsi_work { @@ -681,7 +712,8 @@ static int ucsi_get_connector_status(struct ucsi_connector *con, bool conn_ack) UCSI_MAX_DATA_LENGTH(con->ucsi)); int ret; - ret = ucsi_send_command_common(con->ucsi, command, &con->status, size, conn_ack); + ret = ucsi_send_command_common(con->ucsi, command, &con->status, size, + NULL, 0, conn_ack); return ret < 0 ? ret : 0; } @@ -1658,6 +1690,7 @@ static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con) INIT_WORK(&con->work, ucsi_handle_connector_change); init_completion(&con->complete); mutex_init(&con->lock); + lockdep_set_class(&con->lock, &con->lock_key); INIT_LIST_HEAD(&con->partner_tasks); con->ucsi = ucsi; @@ -1903,6 +1936,9 @@ static int ucsi_init(struct ucsi *ucsi) goto err_reset; } + for (i = 0; i < ucsi->cap.num_connectors; i++) + lockdep_register_key(&connector[i].lock_key); + /* Register all connectors */ for (i = 0; i < ucsi->cap.num_connectors; i++) { connector[i].num = i + 1; @@ -1932,6 +1968,9 @@ static int ucsi_init(struct ucsi *ucsi) return 0; err_unregister: + for (i = 0; i < ucsi->cap.num_connectors; i++) + lockdep_unregister_key(&connector[i].lock_key); + for (con = connector; con->port; con++) { if (con->wq) destroy_workqueue(con->wq); @@ -1978,6 +2017,26 @@ static void ucsi_resume_work(struct work_struct *work) } } +int ucsi_suspend(struct ucsi *ucsi) +{ + int i; + + /* + * Cancel pending work so it cannot access the firmware after the ACPI + * EC is stopped for suspend; state is re-read on resume. + */ + cancel_delayed_work_sync(&ucsi->work); + + if (!ucsi->connector) + return 0; + + for (i = 0; i < ucsi->cap.num_connectors; i++) + cancel_work_sync(&ucsi->connector[i].work); + + return 0; +} +EXPORT_SYMBOL_GPL(ucsi_suspend); + int ucsi_resume(struct ucsi *ucsi) { if (ucsi->connector) @@ -2182,6 +2241,7 @@ void ucsi_unregister(struct ucsi *ucsi) usb_power_delivery_unregister(ucsi->connector[i].pd); ucsi->connector[i].pd = NULL; typec_unregister_port(ucsi->connector[i].port); + lockdep_unregister_key(&ucsi->connector[i].lock_key); } kfree(ucsi->connector); diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h index cff9ddc2ae21..6e1608d88ec3 100644 --- a/drivers/usb/typec/ucsi/ucsi.h +++ b/drivers/usb/typec/ucsi/ucsi.h @@ -65,6 +65,7 @@ struct dentry; * @read_cci: Read CCI register * @poll_cci: Read CCI register while polling with notifications disabled * @read_message_in: Read message data from UCSI + * @write_message_out: Write message data to UCSI * @sync_control: Blocking control operation * @async_control: Non-blocking control operation * @update_altmodes: Squashes duplicate DP altmodes @@ -82,8 +83,9 @@ struct ucsi_operations { int (*read_cci)(struct ucsi *ucsi, u32 *cci); int (*poll_cci)(struct ucsi *ucsi, u32 *cci); int (*read_message_in)(struct ucsi *ucsi, void *val, size_t val_len); + int (*write_message_out)(struct ucsi *ucsi, void *data, size_t data_len); int (*sync_control)(struct ucsi *ucsi, u64 command, u32 *cci, - void *data, size_t size); + void *data, size_t size, void *msg_out, size_t msg_out_size); int (*async_control)(struct ucsi *ucsi, u64 command); bool (*update_altmodes)(struct ucsi *ucsi, u8 recipient, struct ucsi_altmode *orig, @@ -136,6 +138,7 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num); #define UCSI_GET_PD_MESSAGE 0x15 #define UCSI_GET_CAM_CS 0x18 #define UCSI_SET_SINK_PATH 0x1c +#define UCSI_SET_PDOS 0x1d #define UCSI_READ_POWER_LEVEL 0x1e #define UCSI_SET_USB 0x21 #define UCSI_GET_LPM_PPM_INFO 0x22 @@ -213,6 +216,9 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num); #define UCSI_GET_PD_MESSAGE_TYPE_IDENTITY 4 #define UCSI_GET_PD_MESSAGE_TYPE_REVISION 5 +/* Data length bits */ +#define UCSI_COMMAND_DATA_LEN(_cmd_) (((_cmd_) >> 8) & GENMASK(7, 0)) + /* -------------------------------------------------------------------------- */ /* Error information returned by PPM in response to GET_ERROR_STATUS command. */ @@ -453,6 +459,8 @@ struct ucsi_bitfield { /* -------------------------------------------------------------------------- */ +#define MESSAGE_OUT_MAX_LEN 256 + struct ucsi_debugfs_entry { u64 command; struct ucsi_data { @@ -460,6 +468,7 @@ struct ucsi_debugfs_entry { u64 high; } response; int status; + u8 message_out[MESSAGE_OUT_MAX_LEN]; struct dentry *dentry; }; @@ -503,6 +512,9 @@ struct ucsi { }; #define UCSI_MAX_DATA_LENGTH(u) (((u)->version < UCSI_VERSION_2_0) ? 0x10 : 0xff) +#define UCSI_MAX_MSG_OUT_DATA_LEN(u) \ + (((u)->version >= UCSI_VERSION_3_0) ? 255 : \ + (((u)->version >= UCSI_VERSION_2_0) ? 256 : 16)) #define UCSI_MAX_SVID 5 #define UCSI_MAX_ALTMODES (UCSI_MAX_SVID * 6) @@ -517,6 +529,7 @@ struct ucsi_connector { struct ucsi *ucsi; struct mutex lock; /* port lock */ + struct lock_class_key lock_key; struct work_struct work; struct completion complete; struct workqueue_struct *wq; @@ -564,13 +577,18 @@ struct ucsi_connector { int ucsi_send_command(struct ucsi *ucsi, u64 command, void *retval, size_t size); +int ucsi_write_message_out_command(struct ucsi *ucsi, u64 command, + void *retval, size_t size, + void *msg_out, size_t msg_out_size); void ucsi_altmode_update_active(struct ucsi_connector *con); +int ucsi_suspend(struct ucsi *ucsi); 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, u32 *cci, - void *data, size_t size); + void *data, size_t size, void *msg_out, + size_t msg_out_size); #if IS_ENABLED(CONFIG_POWER_SUPPLY) int ucsi_register_port_psy(struct ucsi_connector *con); diff --git a/drivers/usb/typec/ucsi/ucsi_acpi.c b/drivers/usb/typec/ucsi/ucsi_acpi.c index 6b92f296e985..18286d3e9cc5 100644 --- a/drivers/usb/typec/ucsi/ucsi_acpi.c +++ b/drivers/usb/typec/ucsi/ucsi_acpi.c @@ -86,6 +86,21 @@ static int ucsi_acpi_read_message_in(struct ucsi *ucsi, void *val, size_t val_le return 0; } +static int ucsi_acpi_write_message_out(struct ucsi *ucsi, void *data, size_t data_len) +{ + struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi); + + if (!data || !data_len) + return -EINVAL; + + if (ucsi->version <= UCSI_VERSION_1_2) + memcpy(ua->base + UCSI_MESSAGE_OUT, data, data_len); + else + memcpy(ua->base + UCSIv2_MESSAGE_OUT, data, data_len); + + return 0; +} + static int ucsi_acpi_async_control(struct ucsi *ucsi, u64 command) { struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi); @@ -101,19 +116,22 @@ static const struct ucsi_operations ucsi_acpi_ops = { .read_cci = ucsi_acpi_read_cci, .poll_cci = ucsi_acpi_poll_cci, .read_message_in = ucsi_acpi_read_message_in, + .write_message_out = ucsi_acpi_write_message_out, .sync_control = ucsi_sync_control_common, .async_control = ucsi_acpi_async_control }; static int ucsi_gram_sync_control(struct ucsi *ucsi, u64 command, u32 *cci, - void *val, size_t len) + void *val, size_t len, void *msg_out, + size_t msg_out_size) { u16 bogus_change = UCSI_CONSTAT_POWER_LEVEL_CHANGE | UCSI_CONSTAT_PDOS_CHANGE; struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi); int ret; - ret = ucsi_sync_control_common(ucsi, command, cci, val, len); + ret = ucsi_sync_control_common(ucsi, command, cci, val, len, + msg_out, msg_out_size); if (ret < 0) return ret; @@ -245,6 +263,13 @@ static void ucsi_acpi_remove(struct platform_device *pdev) ucsi_acpi_notify); } +static int ucsi_acpi_suspend(struct device *dev) +{ + struct ucsi_acpi *ua = dev_get_drvdata(dev); + + return ucsi_suspend(ua->ucsi); +} + static int ucsi_acpi_resume(struct device *dev) { struct ucsi_acpi *ua = dev_get_drvdata(dev); @@ -252,7 +277,8 @@ static int ucsi_acpi_resume(struct device *dev) return ucsi_resume(ua->ucsi); } -static DEFINE_SIMPLE_DEV_PM_OPS(ucsi_acpi_pm_ops, NULL, ucsi_acpi_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(ucsi_acpi_pm_ops, ucsi_acpi_suspend, + ucsi_acpi_resume); static const struct acpi_device_id ucsi_acpi_match[] = { { "PNP0CA0", 0 }, diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c index 4463c1ae96bd..91c2958a708c 100644 --- a/drivers/usb/typec/ucsi/ucsi_ccg.c +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c @@ -608,7 +608,8 @@ static int ucsi_ccg_async_control(struct ucsi *ucsi, u64 command) } static int ucsi_ccg_sync_control(struct ucsi *ucsi, u64 command, u32 *cci, - void *data, size_t size) + void *data, size_t size, void *msg_out, + size_t msg_out_size) { struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi); struct ucsi_connector *con; @@ -630,7 +631,8 @@ static int ucsi_ccg_sync_control(struct ucsi *ucsi, u64 command, u32 *cci, ucsi_ccg_update_set_new_cam_cmd(uc, con, &command); } - ret = ucsi_sync_control_common(ucsi, command, cci, data, size); + ret = ucsi_sync_control_common(ucsi, command, cci, data, size, + msg_out, msg_out_size); switch (UCSI_COMMAND(command)) { case UCSI_GET_CURRENT_CAM: @@ -1519,8 +1521,8 @@ static void ucsi_ccg_remove(struct i2c_client *client) cancel_work_sync(&uc->work); pm_runtime_disable(uc->dev); ucsi_unregister(uc->ucsi); - ucsi_destroy(uc->ucsi); free_irq(uc->irq, uc); + ucsi_destroy(uc->ucsi); } static const struct of_device_id ucsi_ccg_of_match_table[] = { @@ -1530,8 +1532,8 @@ 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" }, - {} + { .name = "ccgx-ucsi" }, + { } }; MODULE_DEVICE_TABLE(i2c, ucsi_ccg_device_id); diff --git a/drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c b/drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c index ca749fde49bd..ca1b534cb183 100644 --- a/drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c +++ b/drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c @@ -18,6 +18,7 @@ #include <linux/usb/pd_vdo.h> #include <linux/usb/typec_altmode.h> #include <linux/usb/typec_dp.h> +#include <linux/usb/typec_mux.h> #include <linux/workqueue_types.h> #include "ucsi.h" @@ -82,6 +83,10 @@ struct gaokun_ucsi_port { struct gaokun_ucsi *ucsi; struct auxiliary_device *bridge; + struct typec_mux *typec_mux; + struct typec_mux_state state; + struct typec_altmode dp_alt; + int idx; enum gaokun_ucsi_ccx ccx; enum gaokun_ucsi_mux mux; @@ -226,19 +231,18 @@ static void gaokun_ucsi_port_update(struct gaokun_ucsi_port *port, port->hpd_state = FIELD_GET(GAOKUN_HPD_STATE_MASK, ddi); port->hpd_irq = FIELD_GET(GAOKUN_HPD_IRQ_MASK, ddi); - /* Mode and SVID are unused; keeping them to make things clearer */ switch (port->mode) { case USBC_DPAM_PAN_C: case USBC_DPAM_PAN_C_REVERSE: - port->mode = DP_PIN_ASSIGN_C; /* correct it for usb later */ + port->mode = TYPEC_DP_STATE_C; /* correct it for usb later */ break; case USBC_DPAM_PAN_D: case USBC_DPAM_PAN_D_REVERSE: - port->mode = DP_PIN_ASSIGN_D; + port->mode = TYPEC_DP_STATE_D; break; case USBC_DPAM_PAN_E: case USBC_DPAM_PAN_E_REVERSE: - port->mode = DP_PIN_ASSIGN_E; + port->mode = TYPEC_DP_STATE_E; break; case USBC_DPAM_PAN_NONE: port->mode = TYPEC_STATE_SAFE; @@ -287,18 +291,30 @@ static int gaokun_ucsi_refresh(struct gaokun_ucsi *uec) return idx; } -static void gaokun_ucsi_handle_altmode(struct gaokun_ucsi_port *port) +static void gaokun_ucsi_handle_usb_mode(struct gaokun_ucsi_port *port) { struct gaokun_ucsi *uec = port->ucsi; - int idx = port->idx; + int idx = port->idx, ret; + + /* + * For every typec port on this platform, the only mode-switch is + * controlled by its qmp combo phy which consumes svid and mode only. + */ + port->dp_alt.svid = port->svid; + port->state.mode = port->mode; + port->state.alt = &port->dp_alt; - if (idx >= uec->ucsi->cap.num_connectors) { + if (idx >= uec->num_ports) { dev_warn(uec->dev, "altmode port out of range: %d\n", idx); return; } + ret = typec_mux_set(port->typec_mux, &port->state); + if (ret) + dev_err(uec->dev, "failed to set mux %d\n", ret); + /* UCSI callback .connector_status() have set orientation */ - if (port->bridge) + if (port->bridge && port->svid == USB_TYPEC_DP_SID) drm_aux_hpd_bridge_notify(&port->bridge->dev, port->hpd_state ? connector_status_connected : @@ -307,7 +323,7 @@ static void gaokun_ucsi_handle_altmode(struct gaokun_ucsi_port *port) gaokun_ec_ucsi_pan_ack(uec->ec, port->idx); } -static void gaokun_ucsi_altmode_notify_ind(struct gaokun_ucsi *uec) +static void gaokun_ucsi_usb_notify_ind(struct gaokun_ucsi *uec) { int idx; @@ -320,7 +336,7 @@ static void gaokun_ucsi_altmode_notify_ind(struct gaokun_ucsi *uec) if (idx == GAOKUN_UCSI_NO_PORT_UPDATE) gaokun_ec_ucsi_pan_ack(uec->ec, idx); /* ack directly if no update */ else - gaokun_ucsi_handle_altmode(&uec->ports[idx]); + gaokun_ucsi_handle_usb_mode(&uec->ports[idx]); } /* @@ -352,7 +368,7 @@ static void gaokun_ucsi_handle_no_usb_event(struct gaokun_ucsi *uec, int idx) port = &uec->ports[idx]; if (!wait_for_completion_timeout(&port->usb_ack, 2 * HZ)) { dev_warn(uec->dev, "No USB EVENT, triggered by UCSI EVENT"); - gaokun_ucsi_altmode_notify_ind(uec); + gaokun_ucsi_usb_notify_ind(uec); } } @@ -366,7 +382,7 @@ static int gaokun_ucsi_notify(struct notifier_block *nb, switch (action) { case EC_EVENT_USB: gaokun_ucsi_complete_usb_ack(uec); - gaokun_ucsi_altmode_notify_ind(uec); + gaokun_ucsi_usb_notify_ind(uec); return NOTIFY_OK; case EC_EVENT_UCSI: @@ -429,8 +445,15 @@ static int gaokun_ucsi_ports_init(struct gaokun_ucsi *uec) fwnode_handle_put(fwnode); return PTR_ERR(ucsi_port->bridge); } - } + ucsi_port->typec_mux = fwnode_typec_mux_get(fwnode); + if (IS_ERR(ucsi_port->typec_mux)) { + fwnode_handle_put(fwnode); + return dev_err_probe(dev, PTR_ERR(ucsi_port->typec_mux), + "failed to acquire mode-switch for port: %d\n", + port); + } + } for (i = 0; i < num_ports; i++) { if (!uec->ports[i].bridge) continue; @@ -502,10 +525,14 @@ static int gaokun_ucsi_probe(struct auxiliary_device *adev, static void gaokun_ucsi_remove(struct auxiliary_device *adev) { struct gaokun_ucsi *uec = auxiliary_get_drvdata(adev); + int i; disable_delayed_work_sync(&uec->work); gaokun_ec_unregister_notify(uec->ec, &uec->nb); ucsi_unregister(uec->ucsi); + for (i = 0; i < uec->num_ports; ++i) + typec_mux_put(uec->ports[i].typec_mux); + ucsi_destroy(uec->ucsi); } diff --git a/drivers/usb/typec/ucsi/ucsi_stm32g0.c b/drivers/usb/typec/ucsi/ucsi_stm32g0.c index 838ac0185082..848ed459a6de 100644 --- a/drivers/usb/typec/ucsi/ucsi_stm32g0.c +++ b/drivers/usb/typec/ucsi/ucsi_stm32g0.c @@ -737,8 +737,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" }, - {} + { .name = "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 index 0187c1c4b21a..1be18d101842 100644 --- a/drivers/usb/typec/ucsi/ucsi_yoga_c630.c +++ b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c @@ -89,7 +89,8 @@ static int yoga_c630_ucsi_async_control(struct ucsi *ucsi, u64 command) static int yoga_c630_ucsi_sync_control(struct ucsi *ucsi, u64 command, u32 *cci, - void *data, size_t size) + void *data, size_t size, + void *msg_out, size_t msg_out_size) { int ret; @@ -126,7 +127,8 @@ static int yoga_c630_ucsi_sync_control(struct ucsi *ucsi, return 0; } - ret = ucsi_sync_control_common(ucsi, command, cci, data, size); + ret = ucsi_sync_control_common(ucsi, command, cci, + data, size, msg_out, msg_out_size); if (ret < 0) return ret; diff --git a/drivers/usb/usbip/Kconfig b/drivers/usb/usbip/Kconfig index b9f94e2e278d..50945b6fae1a 100644 --- a/drivers/usb/usbip/Kconfig +++ b/drivers/usb/usbip/Kconfig @@ -40,7 +40,7 @@ config USBIP_VHCI_HC_PORTS config USBIP_VHCI_NR_HCS int "Number of USB/IP virtual host controllers" - range 1 128 + range 1 32 default 1 depends on USBIP_VHCI_HCD help diff --git a/drivers/usb/usbip/usbip_common.h b/drivers/usb/usbip/usbip_common.h index 282efca64a01..be4c5e65a7f8 100644 --- a/drivers/usb/usbip/usbip_common.h +++ b/drivers/usb/usbip/usbip_common.h @@ -282,9 +282,7 @@ struct usbip_device { void (*unusable)(struct usbip_device *); } eh_ops; -#ifdef CONFIG_KCOV - u64 kcov_handle; -#endif + struct kcov_common_handle_id kcov_handle; }; #define kthread_get_run(threadfn, data, namefmt, ...) \ @@ -339,29 +337,4 @@ static inline int interface_to_devnum(struct usb_interface *interface) return udev->devnum; } -#ifdef CONFIG_KCOV - -static inline void usbip_kcov_handle_init(struct usbip_device *ud) -{ - ud->kcov_handle = kcov_common_handle(); -} - -static inline void usbip_kcov_remote_start(struct usbip_device *ud) -{ - kcov_remote_start_common(ud->kcov_handle); -} - -static inline void usbip_kcov_remote_stop(void) -{ - kcov_remote_stop(); -} - -#else /* CONFIG_KCOV */ - -static inline void usbip_kcov_handle_init(struct usbip_device *ud) { } -static inline void usbip_kcov_remote_start(struct usbip_device *ud) { } -static inline void usbip_kcov_remote_stop(void) { } - -#endif /* CONFIG_KCOV */ - #endif /* __USBIP_COMMON_H */ diff --git a/drivers/usb/usbip/vhci_rx.c b/drivers/usb/usbip/vhci_rx.c index a75f4a898a41..a678e7c89837 100644 --- a/drivers/usb/usbip/vhci_rx.c +++ b/drivers/usb/usbip/vhci_rx.c @@ -261,9 +261,9 @@ int vhci_rx_loop(void *data) if (usbip_event_happened(ud)) break; - usbip_kcov_remote_start(ud); + kcov_remote_start_common(ud->kcov_handle); vhci_rx_pdu(ud); - usbip_kcov_remote_stop(); + kcov_remote_stop(); } return 0; diff --git a/drivers/usb/usbip/vhci_sysfs.c b/drivers/usb/usbip/vhci_sysfs.c index 5bc8c47788d4..a7ede6fb3da9 100644 --- a/drivers/usb/usbip/vhci_sysfs.c +++ b/drivers/usb/usbip/vhci_sysfs.c @@ -59,6 +59,29 @@ static void port_show_vhci(char **out, int hub, int port, struct vhci_device *vd *out += sprintf(*out, "\n"); } +static ssize_t status_show_not_ready(int pdev_nr, char *out) +{ + char *s = out; + int i = 0; + + for (i = 0; i < VHCI_HC_PORTS; i++) { + out += sprintf(out, "hs %04u %03u ", + (pdev_nr * VHCI_PORTS) + i, + VDEV_ST_NOTASSIGNED); + out += sprintf(out, "000 00000000 0000000000000000 0-0"); + out += sprintf(out, "\n"); + } + + for (i = 0; i < VHCI_HC_PORTS; i++) { + out += sprintf(out, "ss %04u %03u ", + (pdev_nr * VHCI_PORTS) + VHCI_HC_PORTS + i, + VDEV_ST_NOTASSIGNED); + out += sprintf(out, "000 00000000 0000000000000000 0-0"); + out += sprintf(out, "\n"); + } + return out - s; +} + /* Sysfs entry to show port status */ static ssize_t status_show_vhci(int pdev_nr, char *out) { @@ -76,6 +99,12 @@ static ssize_t status_show_vhci(int pdev_nr, char *out) } hcd = platform_get_drvdata(pdev); + + if (!hcd) { + usbip_dbg_vhci_sysfs("show status error (hcd is NULL)\n"); + return status_show_not_ready(pdev_nr, out); + } + vhci_hcd = hcd_to_vhci_hcd(hcd); vhci = vhci_hcd->vhci; @@ -104,29 +133,6 @@ static ssize_t status_show_vhci(int pdev_nr, char *out) return out - s; } -static ssize_t status_show_not_ready(int pdev_nr, char *out) -{ - char *s = out; - int i = 0; - - for (i = 0; i < VHCI_HC_PORTS; i++) { - out += sprintf(out, "hs %04u %03u ", - (pdev_nr * VHCI_PORTS) + i, - VDEV_ST_NOTASSIGNED); - out += sprintf(out, "000 00000000 0000000000000000 0-0"); - out += sprintf(out, "\n"); - } - - for (i = 0; i < VHCI_HC_PORTS; i++) { - out += sprintf(out, "ss %04u %03u ", - (pdev_nr * VHCI_PORTS) + VHCI_HC_PORTS + i, - VDEV_ST_NOTASSIGNED); - out += sprintf(out, "000 00000000 0000000000000000 0-0"); - out += sprintf(out, "\n"); - } - return out - s; -} - static int status_name_to_id(const char *name) { char *c; @@ -425,7 +431,7 @@ static ssize_t attach_store(struct device *dev, struct device_attribute *attr, vdev->ud.tcp_rx = tcp_rx; vdev->ud.tcp_tx = tcp_tx; vdev->ud.status = VDEV_ST_NOTASSIGNED; - usbip_kcov_handle_init(&vdev->ud); + vdev->ud.kcov_handle = kcov_common_handle(); spin_unlock(&vdev->ud.lock); spin_unlock_irqrestore(&vhci->lock, flags); diff --git a/drivers/usb/usbip/vudc.h b/drivers/usb/usbip/vudc.h index faf61c9c6a98..5ef0e7d9b23a 100644 --- a/drivers/usb/usbip/vudc.h +++ b/drivers/usb/usbip/vudc.h @@ -38,7 +38,6 @@ struct vep { struct vrequest { struct usb_request req; - struct vudc *udc; struct list_head req_entry; /* Request queue */ }; diff --git a/drivers/usb/usbip/vudc_dev.c b/drivers/usb/usbip/vudc_dev.c index c5f079c5a1ea..5ef88117965d 100644 --- a/drivers/usb/usbip/vudc_dev.c +++ b/drivers/usb/usbip/vudc_dev.c @@ -333,7 +333,6 @@ static int vep_queue(struct usb_ep *_ep, struct usb_request *_req, static int vep_dequeue(struct usb_ep *_ep, struct usb_request *_req) { struct vep *ep; - struct vrequest *req; struct vudc *udc; struct vrequest *lst; unsigned long flags; @@ -343,8 +342,7 @@ static int vep_dequeue(struct usb_ep *_ep, struct usb_request *_req) return ret; ep = to_vep(_ep); - req = to_vrequest(_req); - udc = req->udc; + udc = ep_to_vudc(ep); if (!udc->driver) return -ESHUTDOWN; |
